Beispiel #1
0
        public static void HandleShopUpdateItem(Character pCharacter, byte inv, short invslot, short bundle, short bundleamount, int price)
        {
            MiniRoomBase mrb = pCharacter.Room;

            if (pCharacter.AssertForHack(mrb.Users[0] != pCharacter,
                                         "PlayerShop hack: Tried to update shop item while not owner."))
            {
                return;
            }

            BaseItem tehItem = pCharacter.Inventory.GetItem(inv, invslot);

            if (tehItem == null)
            {
                //Doesn't have item in inventory
                ReportManager.FileNewReport("Tried adding an item into player shop without having it.", pCharacter.ID, 0);
                InventoryPacket.NoChange(pCharacter);
                return;
            }

            var newItem = tehItem.Duplicate();

            newItem.InventorySlot = invslot;

            if (newItem.Amount < bundle || bundle <= 0 || bundle > 100)
            {
                //Packet edits
                ReportManager.FileNewReport("Tried adding an item into player shop with an incorrect amount/incorrect itemid.", pCharacter.ID, 0);
                InventoryPacket.NoChange(pCharacter);
                return;
            }

            PlayerShopItem pst = new PlayerShopItem(newItem)
            {
                Price = price
            };

            pst.sItem.Amount = (short)(bundle * bundleamount);
            pst.Bundles      = bundle;
            pst.BundleAmount = bundleamount;
            mrb.AddItemToShop(pCharacter, pst);
            if (Constants.isStackable(pst.sItem.ItemID))
            {
                pCharacter.Inventory.TakeItemAmountFromSlot(inv, invslot, (short)(bundle * bundleamount), false);
            }
            else
            {
                pCharacter.Inventory.TakeItem(pst.sItem.ItemID, bundle);
            }

            ItemTransfer.PersonalShopPutUpItem(pCharacter.ID, pst.sItem.ItemID, pst.sItem.Amount, mrb.TransactionID, pst.sItem);

            InventoryPacket.NoChange(pCharacter); //-.-
        }
Beispiel #2
0
        private static void EnterMiniRoom(Character chr, Packet packet)
        {
            if (chr.Room != null)
            {
                miniroomLog.Info($"{chr.Name} cannot enter miniroom: already in one.");
                return; // Already in a Mini Room
            }


            //MessagePacket.SendNotice("PACKET: " + packet.ToString(), chr);
            int roomId = packet.ReadInt();

            if (!MiniRoomBase.MiniRooms.TryGetValue(roomId, out var mrb))
            {
                ReportManager.FileNewReport("Tried entering a trade room without a proper ID.", chr.ID, 0);
                return; // Invalid Room ID
            }

            if (mrb.EnteredUsers == 0)
            {
                return;
            }

            if (mrb.IsFull())
            {
                miniroomLog.Info($"{chr.Name} cannot enter miniroom: already full.");
                return; // Error msg if full?
            }

            if (mrb.Users.ToList().Exists(u => u != null && u.MapID != chr.MapID))
            {
                InviteResult(chr, 1); // must be on same map. Show "not found" msg
                return;
            }

            chr.Room = mrb;
            byte nType = (byte)chr.Room.Type;

            switch (nType)
            {
            case 1:     // Omok
            {
                bool usePassword = packet.ReadBool();
                Omok omok        = MiniRoomBase.Omoks[chr.Room.ID];

                if (usePassword)
                {
                    string password = packet.ReadString();
                    if (password != omok.Password)
                    {
                        miniroomLog.Info($"{chr.Name} cannot enter omok: invalid password");
                        MiniGamePacket.ErrorMessage(chr, MiniGamePacket.MiniGameError.IncorrectPassword);
                        chr.Room = null;
                        break;
                    }
                }
                if (chr.Inventory.Mesos >= 100)
                {
                    omok.AddPlayer(chr);
                    MiniGamePacket.AddVisitor(chr, mrb);
                    MiniGamePacket.ShowWindow(chr, mrb, omok.OmokType);
                    chr.AddMesos(-100);
                    miniroomLog.Info($"{chr.Name} entered omok");
                }
                else
                {
                    miniroomLog.Info($"{chr.Name} cannot enter omok: not enough mesos");
                    MiniGamePacket.ErrorMessage(chr, MiniGamePacket.MiniGameError.NotEnoughMesos);
                }
                break;
            }

            case 3:     // Trade
            {
                miniroomLog.Info($"{chr.Name} entered trade");
                mrb.AddPlayer(chr);
                MiniRoomPacket.ShowJoin(mrb, chr);
                MiniRoomPacket.ShowWindow(mrb, chr);
                break;
            }

            case 4:     // Player Shop
            {
                miniroomLog.Info($"{chr.Name} entered playershop");
                PlayerShop shop = MiniRoomBase.PlayerShops[roomId];
                for (int i = 0; i < shop.EnteredUsers; i++)
                {
                    Character shopUser = mrb.Users[i];
                    if (shopUser != null && shopUser != chr)
                    {
                        shop.AddPlayer(chr);
                        PlayerShopPackets.AddPlayer(chr, shopUser);
                        PlayerShopPackets.OpenPlayerShop(chr, mrb);
                        PlayerShopPackets.PersonalShopRefresh(chr, shop);         //Show items
                    }
                }
                break;
            }
            }
        }
Beispiel #3
0
        public override void OnPacket(Character pCharacter, byte pOpcode, Packet pPacket)
        {
            switch (pOpcode)
            {
            case 13:
            {
                byte charslot = pCharacter.RoomSlotId;
                // Put Item
                if (!IsFull())
                {
                    // You can't put items while the second char isn't there yet
                    InventoryPacket.NoChange(pCharacter);
                    return;
                }

                byte  inventory = pPacket.ReadByte();
                short slot      = pPacket.ReadShort();
                short amount    = pPacket.ReadShort();
                byte  toslot    = pPacket.ReadByte();

                var demItem = pCharacter.Inventory.GetItem(inventory, slot);

                if (demItem == null || toslot < 1 || toslot > 9)         // Todo: trade check
                {
                    // HAX
                    var msg = $"Player tried to add an item in trade with to an incorrect slot. Item = null? {demItem == null}; toSlot {toslot}";
                    Program.MainForm.LogAppend(msg);
                    ReportManager.FileNewReport(msg, pCharacter.ID, 0);
                    InventoryPacket.NoChange(pCharacter);
                    return;
                }

                BaseItem tehItem = pCharacter.Inventory.TakeItemAmountFromSlot(inventory, slot, amount, Constants.isRechargeable(demItem.ItemID));

                if (ItemList[charslot][toslot] == null)
                {
                    ItemList[charslot][toslot] = new TradeItem()
                    {
                        OriginalItem = tehItem
                    };
                }

                var pTradeItem = ItemList[charslot][toslot].OriginalItem;

                ItemTransfer.PlayerTradePutUp(pCharacter.ID, demItem.ItemID, slot, amount, _transaction, demItem);

                bool isUser0 = pCharacter.Name == Users[0].Name;

                TradePacket.AddItem(Users[0], toslot, pTradeItem, (byte)(isUser0 ? 0 : 1));
                TradePacket.AddItem(Users[1], toslot, pTradeItem, (byte)(isUser0 ? 1 : 0));

                InventoryPacket.NoChange(pCharacter);         // -.-
                break;
            }

            case 14:     // Put mesos
            {
                //MessagePacket.SendNotice("PUTMESO PACKET: " + pPacket.ToString(), pCharacter);
                int amount = pPacket.ReadInt();

                if (amount < 0 || pCharacter.Inventory.Mesos < amount)
                {
                    // HAX
                    var msg = "Player tried putting an incorrect meso amount in trade. Amount: " + amount;
                    Program.MainForm.LogAppend(msg);
                    ReportManager.FileNewReport(msg, pCharacter.ID, 0);
                    return;
                }

                pCharacter.AddMesos(-amount, true);
                MesosTransfer.PlayerTradePutUp(pCharacter.ID, amount, _transaction);
                Mesos[pCharacter.RoomSlotId] += amount;


                bool isUser0 = pCharacter.Name == Users[0].Name;

                TradePacket.PutCash(Users[0], Mesos[pCharacter.RoomSlotId], (byte)(isUser0 ? 0 : 1));
                TradePacket.PutCash(Users[1], Mesos[pCharacter.RoomSlotId], (byte)(isUser0 ? 1 : 0));

                break;
            }

            // Accept trade
            case 0xF:
            {
                byte charslot = pCharacter.RoomSlotId;
                Locked[charslot] = true;

                for (int i = 0; i < 2; i++)
                {
                    Character chr = Users[i];

                    if (chr != pCharacter)
                    {
                        TradePacket.SelectTrade(chr);
                    }
                }

                if (Locked[0] == true && Locked[1] == true)
                {
                    Character chr  = Users[0];
                    Character chr2 = Users[1];
                    if (ContinueTrade())
                    {
                        CompleteTrade();

                        TradePacket.TradeSuccessful(chr);
                        TradePacket.TradeSuccessful(chr2);
                    }
                    else
                    {
                        // Unsuccessful error
                        RemovePlayer(chr, 6);
                        RemovePlayer(chr2, 6);
                    }
                }
                break;
            }
            }
        }
Beispiel #4
0
        public static void HandleUseSkill(Character chr, Packet packet)
        {
            if (chr.PrimaryStats.HP == 0)
            {
                // We don't like zombies
                InventoryPacket.NoChange(chr);
                return;
            }

            var field = chr.Field;

            var SkillID    = packet.ReadInt();
            var SkillLevel = packet.ReadByte();


            if (SkillID == Constants.Priest.Skills.MysticDoor && MasterThread.CurrentTime - chr.tLastDoor < 3000)
            {
                //hack fix for door dc bug
                InventoryPacket.NoChange(chr);
                return;
            }

            if (!chr.Skills.Skills.ContainsKey(SkillID) ||
                SkillLevel < 1 ||
                SkillLevel > chr.Skills.Skills[SkillID])
            {
                Program.MainForm.LogAppend("Player {0} tried to use a skill without having it.", chr.ID);
                ReportManager.FileNewReport("Player {0} tried to use a skill without having it.", chr.ID, 0);
                chr.Player.Socket.Disconnect();
                return;
            }

            var isGMHideSkill = SkillID == Constants.Gm.Skills.Hide;

            // Prevent sending the hide enable/disable packet to others
            if (isGMHideSkill)
            {
                chr.SetHide(chr.GMHideEnabled == false, false);
                if (chr.GMHideEnabled == false)
                {
                    StopSkill(chr, SkillID);
                }
            }
            else if (SkillID != Constants.Cleric.Skills.Heal ||
                     chr.Inventory.GetEquippedItemId((short)Constants.EquipSlots.Slots.Weapon, false) == 0)
            {
                // If you are using Heal, and are not using a wand/weapon, it won't show anything.

                MapPacket.SendPlayerSkillAnim(chr, SkillID, SkillLevel);
            }

            var sld = DataProvider.Skills[SkillID].Levels[SkillLevel];


            if (SkillID == (int)Constants.Spearman.Skills.HyperBody && !chr.PrimaryStats.HasBuff((int)Constants.Spearman.Skills.HyperBody)) // Buff already exists, do not execute bonus again. Allow multiple casts for duration refresh
            {
                var hpmpBonus = (short)((double)chr.PrimaryStats.MaxHP * ((double)sld.XValue / 100.0d));
                chr.PrimaryStats.BuffBonuses.MaxHP = hpmpBonus;
                hpmpBonus = (short)((double)chr.PrimaryStats.MaxMP * ((double)sld.YValue / 100.0d));
                chr.PrimaryStats.BuffBonuses.MaxMP = hpmpBonus;
            }

            short skillDelay = 0;

            IEnumerable <Character> getCharactersForPartyBuff(byte Flags, bool deadPlayersToo = false)
            {
                if (chr.PartyID == 0)
                {
                    yield break;
                }

                if (PartyData.Parties.TryGetValue(chr.PartyID, out var party))
                {
                    for (var i = 5; i >= 0; i--)
                    {
                        if ((Flags >> (5 - i) & 1) == 0)
                        {
                            continue;
                        }

                        var charid = party.Members[i];
                        if (charid == 0)
                        {
                            continue;
                        }

                        var affected = Server.Instance.GetCharacter(charid);

                        if (affected != null && chr.MapID == affected.MapID &&
                            (deadPlayersToo || affected.PrimaryStats.HP > 0))
                        {
                            yield return(affected);
                        }
                    }
                }
            }

            void handlePartyEffects(byte Flags, short Delay, bool deadPlayersToo = false, Action <Character> additionalEffects = null)
            {
                handlePartyEffectsWithPlayers(getCharactersForPartyBuff(Flags, deadPlayersToo), Delay, additionalEffects);
            }

            void handlePartyEffectsWithPlayers(IEnumerable <Character> characters, short Delay, Action <Character> additionalEffects = null)
            {
                foreach (var character in characters)
                {
                    if (character == chr)
                    {
                        continue;
                    }
                    if (!computerSaysYes())
                    {
                        continue;
                    }

                    MapPacket.SendPlayerSkillAnimThirdParty(character, SkillID, SkillLevel, true, true);
                    MapPacket.SendPlayerSkillAnimThirdParty(character, SkillID, SkillLevel, true, false);
                    additionalEffects?.Invoke(character);
                }
            }

            // Runs func() for each mob inside the packet that:
            // - is not a boss (when isBossable is false)
            // - effect chance was a success
            void handleMobStatEffects(bool isBossable, Action <Mob, short> func)
            {
                var mobCount = packet.ReadByte();
                var mobs     = new List <Mob>(mobCount);

                for (var i = 0; i < mobCount; i++)
                {
                    var mobId = packet.ReadInt();
                    var mob   = field.GetMob(mobId);
                    if (mob == null)
                    {
                        continue;
                    }

                    if (chr.AssertForHack(mob.IsBoss && !isBossable, "Tried hitting boss with non-boss skill", false))
                    {
                        continue;
                    }

                    if (computerSaysYes())
                    {
                        mobs.Add(mob);
                    }
                }

                var delay = packet.ReadShort();

                mobs.ForEach(x => func(x, delay));
            }

            IEnumerable <Character> getFullMapPlayersForGMSkill()
            {
                return(field.Characters.Where(victim =>
                {
                    if (victim == chr)
                    {
                        return false;
                    }
                    if (chr.GMHideEnabled && victim.GMHideEnabled == false)
                    {
                        return false;
                    }

                    // Only Admins can buff other regular people
                    if (chr.IsGM && !chr.IsAdmin && !victim.IsGM)
                    {
                        return false;
                    }

                    return true;
                }));
            }

            bool computerSaysYes()
            {
                var chance = sld.Property;

                if (chance == 0)
                {
                    chance = 100;
                }
                return(!(Rand32.Next() % 100 >= chance));
            }

            //TODO refactor copy-pasted "nearest 6 mobs" logic
            switch (SkillID)
            {
            case Constants.Assassin.Skills.Haste:
            case Constants.Bandit.Skills.Haste:
            case Constants.Cleric.Skills.Bless:
            case Constants.Spearman.Skills.IronWill:
            case Constants.Fighter.Skills.Rage:
            case Constants.FPWizard.Skills.Meditation:
            case Constants.ILWizard.Skills.Meditation:
            case Constants.Hermit.Skills.MesoUp:
            {
                var Flags = packet.ReadByte();
                var Delay = packet.ReadShort();

                handlePartyEffects(Flags, Delay, false, victim =>
                    {
                        victim.Buffs.AddBuff(SkillID, SkillLevel);
                    });
                break;
            }

            case Constants.Spearman.Skills.HyperBody:
            {
                var Flags = packet.ReadByte();
                var Delay = packet.ReadShort();

                handlePartyEffects(Flags, Delay, false, victim =>
                    {
                        if (!victim.PrimaryStats.HasBuff((int)Constants.Spearman.Skills.HyperBody))     // Buff already exists, do not execute bonus again. Allow multiple casts for duration refresh
                        {
                            var hpmpBonus = (short)((double)victim.PrimaryStats.MaxHP * ((double)sld.XValue / 100.0d));
                            victim.PrimaryStats.BuffBonuses.MaxHP = hpmpBonus;
                            hpmpBonus = (short)((double)victim.PrimaryStats.MaxMP * ((double)sld.YValue / 100.0d));
                            victim.PrimaryStats.BuffBonuses.MaxMP = hpmpBonus;
                        }
                        victim.Buffs.AddBuff(SkillID, SkillLevel);
                    });


                break;
            }

            case Constants.Cleric.Skills.Heal:
            {
                var Flags   = packet.ReadByte();
                var Delay   = packet.ReadShort();
                var members = getCharactersForPartyBuff(Flags, false);
                var count   = members.Count();

                double healRate = 0;
                if (sld.HPProperty > 0)
                {
                    int chrInt = chr.PrimaryStats.getTotalInt();
                    int chrLuk = chr.PrimaryStats.getTotalLuk();
                    var rate   = (chrInt * 0.8) + Rand32.Next() % (chrInt * 0.2);
                    healRate = (((rate * 1.5 + chrLuk) * (chr.PrimaryStats.getTotalMagicAttack() + chr.PrimaryStats.BuffMagicAttack.N) * 0.01) * (count * 0.3 + 1.0) * sld.HPProperty * 0.01);
                }

                var bigHeal = Math.Min(((long)healRate / (count == 0 ? 1 : count)), short.MaxValue);         //prevent integer overflow caused by high stats. Set count to 1 when not in party
                var heal    = (short)bigHeal;
                chr.ModifyHP((short)Math.Min(heal, (chr.PrimaryStats.GetMaxHP() - chr.PrimaryStats.HP)));

                handlePartyEffectsWithPlayers(members, Delay, victim =>
                    {
                        int oldHP = victim.PrimaryStats.HP;
                        victim.ModifyHP((short)Math.Min(heal, (victim.PrimaryStats.GetMaxHP() - victim.PrimaryStats.HP)));
                        chr.AddEXP(20 * ((victim.PrimaryStats.HP - oldHP) / (8 * victim.Level + 190)), true);
                    });

                break;
            }

            case Constants.Priest.Skills.Dispel:
            {
                var Flags = packet.ReadByte();
                var Delay = packet.ReadShort();

                if (computerSaysYes())
                {
                    chr.Buffs.Dispell();
                }

                handlePartyEffects(Flags, Delay, false, victim =>
                    {
                        victim.Buffs.Dispell();
                    });

                handleMobStatEffects(false, (mob, delay) =>
                    {
                        MobStatus.MobStatValue Flag = 0;
                        Flag |= mob.Status.BuffPowerUp.Reset();
                        Flag |= mob.Status.BuffMagicUp.Reset();
                        Flag |= mob.Status.BuffMagicGuardUp.Reset();
                        Flag |= mob.Status.BuffPowerGuardUp.Reset();
                        Flag |= mob.Status.BuffHardSkin.Reset();
                        MobPacket.SendMobStatsTempReset(mob, Flag);
                    });

                break;
            }

            case Constants.Priest.Skills.HolySymbol:
            {
                var Flags = packet.ReadByte();
                var Delay = packet.ReadShort();

                handlePartyEffects(Flags, Delay, false, victim =>
                    {
                        victim.Buffs.AddBuff(SkillID, SkillLevel);
                    });

                break;
            }

            // DOOR
            case Constants.Priest.Skills.MysticDoor:
            {
                var x = packet.ReadShort();
                var y = packet.ReadShort();

                if (chr.DoorMapId != Constants.InvalidMap)
                {
                    DataProvider.Maps[chr.DoorMapId].DoorPool.TryRemoveDoor(chr.ID);
                }

                chr.DoorMapId = chr.MapID;
                field.DoorPool.CreateDoor(chr, x, y, MasterThread.CurrentTime + sld.BuffTime * 1000);
                chr.tLastDoor = MasterThread.CurrentTime;
                break;
            }

            // GM SKILLS
            case Constants.Gm.Skills.Haste:
            case Constants.Gm.Skills.HolySymbol:
            case Constants.Gm.Skills.Bless:
            {
                getFullMapPlayersForGMSkill().ForEach(victim =>
                    {
                        MapPacket.SendPlayerSkillAnimThirdParty(victim, SkillID, SkillLevel, true, true);
                        MapPacket.SendPlayerSkillAnimThirdParty(victim, SkillID, SkillLevel, true, false);
                        victim.Buffs.AddBuff(SkillID, SkillLevel);
                    });
                break;
            }

            case Constants.Gm.Skills.HealPlusDispell:
            {
                getFullMapPlayersForGMSkill().ForEach(victim =>
                    {
                        MapPacket.SendPlayerSkillAnimThirdParty(victim, SkillID, SkillLevel, true, true);
                        MapPacket.SendPlayerSkillAnimThirdParty(victim, SkillID, SkillLevel, true, false);
                        victim.ModifyHP(victim.PrimaryStats.GetMaxMP(false), true);
                        victim.ModifyMP(victim.PrimaryStats.GetMaxMP(false), true);
                        victim.Buffs.Dispell();
                    });
                chr.ModifyHP(chr.PrimaryStats.GetMaxMP(false), true);
                chr.ModifyMP(chr.PrimaryStats.GetMaxMP(false), true);
                chr.Buffs.Dispell();
                break;
            }

            case Constants.Gm.Skills.Resurrection:
            {
                getFullMapPlayersForGMSkill().ForEach(victim =>
                    {
                        if (victim.PrimaryStats.HP <= 0)
                        {
                            MapPacket.SendPlayerSkillAnimThirdParty(victim, SkillID, SkillLevel, true, true);
                            MapPacket.SendPlayerSkillAnimThirdParty(victim, SkillID, SkillLevel, true, false);
                            victim.ModifyHP(victim.PrimaryStats.GetMaxHP(false), true);
                        }
                    });
                break;
            }

            // MOB SKILLS
            case Constants.Page.Skills.Threaten:
            {
                var buffTime = MasterThread.CurrentTime + (sld.BuffTime * 1000);
                handleMobStatEffects(false, (mob, delay) =>
                    {
                        var stat = mob.Status.BuffPhysicalDamage.Set(
                            SkillID,
                            (short)-((mob.Data.PAD * SkillLevel) / 100),
                            buffTime + delay
                            );

                        stat |= mob.Status.BuffPhysicalDefense.Set(
                            SkillID,
                            (short)-((mob.Data.PDD * SkillLevel) / 100),
                            buffTime + delay
                            );

                        MobPacket.SendMobStatsTempSet(mob, delay, stat);
                    });
                break;
            }

            case Constants.FPWizard.Skills.Slow:
            case Constants.ILWizard.Skills.Slow:
            {
                var buffNValue = sld.XValue;
                var buffTime   = MasterThread.CurrentTime + (sld.BuffTime * 1000);

                handleMobStatEffects(false, (mob, delay) =>
                    {
                        MobPacket.SendMobStatsTempSet(mob, delay, mob.Status.BuffSpeed.Set(SkillID, buffNValue, buffTime + delay));
                    });
                break;
            }

            case Constants.Gm.Skills.ItemExplosion:
            {
                field.DropPool.Clear(RewardLeaveType.Explode);
                // TODO: Explode people and such
                break;
            }

            case Constants.WhiteKnight.Skills.MagicCrash:
            {
                handleMobStatEffects(false, (mob, delay) =>
                    {
                        MobPacket.SendMobStatsTempReset(mob, mob.Status.BuffMagicGuardUp.Reset());
                    });
                break;
            }

            case Constants.DragonKnight.Skills.PowerCrash:
            {
                handleMobStatEffects(false, (mob, delay) =>
                    {
                        MobPacket.SendMobStatsTempReset(mob, mob.Status.BuffPowerUp.Reset());
                    });
                break;
            }

            case Constants.Crusader.Skills.ArmorCrash:
            {
                handleMobStatEffects(false, (mob, delay) =>
                    {
                        MobPacket.SendMobStatsTempReset(mob, mob.Status.BuffPowerGuardUp.Reset());
                    });
                break;
            }

            case Constants.ILMage.Skills.Seal:
            case Constants.FPMage.Skills.Seal:
            {
                var buffTime = MasterThread.CurrentTime + (sld.BuffTime * 1000);
                handleMobStatEffects(false, (mob, delay) =>
                    {
                        MobPacket.SendMobStatsTempSet(mob, delay, mob.Status.BuffSeal.Set(SkillID, 1, buffTime + delay));
                    });

                break;
            }

            case Constants.Hermit.Skills.ShadowWeb:
            {
                var buffTime = MasterThread.CurrentTime + (sld.BuffTime * 1000);

                handleMobStatEffects(false, (mob, delay) =>
                    {
                        var stat = mob.Status.BuffWeb.Set(
                            SkillID,
                            (short)(mob.MaxHP / (50 - SkillLevel)),
                            buffTime + delay
                            );
                        MobPacket.SendMobStatsTempSet(mob, delay, stat);
                    });
                break;
            }

            case Constants.Priest.Skills.Doom:
            {
                var buffTime = MasterThread.CurrentTime + (sld.BuffTime * 1000);

                handleMobStatEffects(false, (mob, delay) =>
                    {
                        MobPacket.SendMobStatsTempSet(mob, delay, mob.Status.BuffDoom.Set(SkillID, 1, buffTime + delay));
                    });
                break;
            }

            // SUMMONS
            case Constants.Priest.Skills.SummonDragon:
            case Constants.Ranger.Skills.SilverHawk:
            case Constants.Sniper.Skills.GoldenEagle:
            {
                var X = packet.ReadShort();
                var Y = packet.ReadShort();

                var    fh   = field.GetFootholdUnderneath(X, Y, out var MaxY);
                ushort fhid = 0;
                fhid = fh?.ID ?? (ushort)chr.Foothold;
                var bird = new Summon(chr, SkillID, SkillLevel, X, Y, true, fhid, MasterThread.CurrentTime + sld.BuffTime * 1000);
                chr.Summons.SetSummon(bird);

                break;
            }

            case Constants.Ranger.Skills.Puppet:
            case Constants.Sniper.Skills.Puppet:
            {
                Program.MainForm.LogDebug(packet.ToString());
                var    X     = packet.ReadShort();
                var    Y     = packet.ReadShort();
                var    fh    = field.GetFootholdUnderneath(X, Y, out var MaxY);
                var    floor = field.FindFloor(new Pos(X, Y));
                ushort fhid  = 0;
                fhid = fh?.ID ?? (ushort)chr.Foothold;
                var puppet = new Puppet(chr, SkillID, SkillLevel, floor.X, floor.Y, false, fhid, MasterThread.CurrentTime + sld.BuffTime * 1000, sld.XValue);
                chr.Summons.SetSummon(puppet);
                break;
            }
            }

            if (packet.Length == packet.Position + 2)
            {
                // Read the delay...
                skillDelay = packet.ReadShort();
            }

            // Handle regular skill stuff
            if (!isGMHideSkill || chr.GMHideEnabled)
            {
                chr.Buffs.AddBuff(SkillID, SkillLevel, skillDelay);
            }

            InventoryPacket.NoChange(chr);
            chr.Skills.DoSkillCost(SkillID, SkillLevel);

            if (sld.Speed > 0)
            {
                MapPacket.SendAvatarModified(chr, MapPacket.AvatarModFlag.Speed);
            }
        }