コード例 #1
0
        public static void HandleDropMesos(Character chr, int amount)
        {
            //30 E8 03 00 00
            if (chr.AssertForHack(amount < 10, "Trying to drop less than 10 mesos") ||
                chr.AssertForHack(amount > 50000, "Trying to drop more than 50k mesos") ||
                chr.AssertForHack(amount > chr.Inventory.Mesos, "Trying to drop more mesos than he's got") ||
                chr.AssertForHack(chr.Room != null, "Trying to drop mesos while in a 'room'"))
            {
                InventoryPacket.NoChange(chr);
                return;
            }

            if (chr.IsGM && !chr.IsAdmin)
            {
                MessagePacket.SendNotice("You cannot drop mesos.", chr);
                InventoryPacket.NoChange(chr);
                return;
            }

            chr.AddMesos(-amount, true);
            Common.Tracking.MesosTransfer.PlayerDropMesos(chr.ID, amount, chr.MapID.ToString());

            chr.Field.DropPool.Create(Reward.Create(amount), chr.ID, 0, DropType.Normal, chr.ID, new Pos(chr.Position), chr.Position.X, 0, false, 0, false, true);
            // This shouldn't be required
            InventoryPacket.NoChange(chr);
        }
コード例 #2
0
ファイル: MapPacket.cs プロジェクト: zhuomingliang/WvsGlobal
        public static void HandleNPCChat(Character chr, Packet packet)
        {
            int npcId = packet.ReadInt();
            var Npc   = chr.Field.GetNPC(npcId);

            if (chr.AssertForHack(!chr.CanAttachAdditionalProcess, "Tried to chat to npc while not able to attach additional process"))
            {
                InventoryPacket.NoChange(chr);
                return;
            }

            // Npc doesnt exist
            if (Npc == null)
            {
                InventoryPacket.NoChange(chr);
                return;
            }

            int RealID = Npc.ID;

            if (!DataProvider.NPCs.TryGetValue(RealID, out NPCData npc))
            {
                return;
            }

            if (npc.Shop.Count > 0)
            {
                // It's a shop!
                chr.ShopNPCID = RealID;
                NpcPacket.SendShowNPCShop(chr, RealID);
            }
            else if (npc.Trunk > 0)
            {
                chr.TrunkNPCID = RealID;
                StoragePacket.SendShowStorage(chr, chr.TrunkNPCID);
            }
            else
            {
                Action <string> errorHandlerFnc = null;
                if (chr.IsGM)
                {
                    errorHandlerFnc = (script) =>
                    {
                        MessagePacket.SendNotice("Error compiling script '" + script + "'!", chr);
                    };
                }

                INpcScript NPC = null;
                if (NPC == null && npc.Quest != null)
                {
                    NPC = Server.Instance.TryGetOrCompileScript(npc.Quest, errorHandlerFnc);
                }
                if (NPC == null)
                {
                    NPC = Server.Instance.TryGetOrCompileScript(npc.ID.ToString(), errorHandlerFnc);
                }

                NpcChatSession.Start(RealID, NPC, chr);
            }
        }
コード例 #3
0
        public static void HandleSpawnPet(Character chr, short slot)
        {
            if (!(chr.Inventory.GetItem(5, slot) is PetItem petItem))
            {
                InventoryPacket.NoChange(chr);
                return;
            }


            if (chr.PetCashId != 0)
            {
                // Already spawned a pet
                SendRemovePet(chr);

                if (chr.PetCashId == petItem.CashId)
                {
                    // Spawned the same mob
                    chr.PetCashId = 0;
                    InventoryPacket.NoChange(chr);
                    return;
                }
            }

            chr.PetCashId = petItem.CashId;
            DoPetSpawn(chr);
            InventoryPacket.NoChange(chr);
        }
コード例 #4
0
ファイル: MapPacket.cs プロジェクト: zhuomingliang/WvsGlobal
        public static void HandleSitChair(Character chr, Packet packet)
        {
            short chair = packet.ReadShort();

            if (chair == -1)
            {
                if (chr.MapChair != -1)
                {
                    chr.Field.UsedSeats.Remove(chr.MapChair);
                    chr.MapChair = -1;
                    SendCharacterSit(chr, -1);
                }
                else
                {
                    InventoryPacket.NoChange(chr);
                }
            }
            else
            {
                if (chr.Field != null && chr.Field.Seats.ContainsKey(chair) && !chr.Field.UsedSeats.Contains(chair))
                {
                    chr.Field.UsedSeats.Add(chair);
                    chr.MapChair = chair;
                    SendCharacterSit(chr, chair);
                }
                else
                {
                    InventoryPacket.NoChange(chr);
                }
            }
        }
コード例 #5
0
ファイル: MapPacket.cs プロジェクト: zhuomingliang/WvsGlobal
        public static void HandleDoorUse(Character chr, Packet packet)
        {
            int charid = packet.ReadInt();

            Program.MainForm.LogDebug("cid: " + charid);
            bool enterFromTown = packet.ReadBool();

            if (enterFromTown)
            {
                // When you enter from town and go to a training map
                // Resulting map is _not_ a town
                if (chr.Field.DoorPool.DoorsLeadingHere.TryGetValue(charid, out var door) && door.CanEnterDoor(chr))
                {
                    chr.ChangeMap(door.FieldId, PartyData.GetMemberIdx(charid) ?? 0, door);
                    return;
                }
            }
            else
            {
                // When you enter from a training map
                // Resulting map _is_ a town
                if (chr.Field.DoorPool.TryGetDoor(charid, out var door) && door.CanEnterDoor(chr))
                {
                    chr.ChangeMap(chr.Field.ReturnMap, PartyData.GetMemberIdx(charid) ?? 0, door);
                    return;
                }
            }

            InventoryPacket.NoChange(chr);
        }
コード例 #6
0
        public static void HandleAddSkillLevel(Character chr, Packet packet)
        {
            var SkillID = packet.ReadInt(); // Todo, add check.

            if (chr.PrimaryStats.SP <= 0)
            {
                // No SP left...
                InventoryPacket.NoChange(chr);
                return;
            }

            if (!DataProvider.Skills.TryGetValue(SkillID, out var sd))
            {
                Program.MainForm.LogAppend("Character {0} tried to put points in a skill ({1}) that doesnt exist.", chr.ID, SkillID);
                return;
            }

            if (chr.Skills.Skills.TryGetValue(SkillID, out var skillLevel) &&
                skillLevel >= sd.MaxLevel)
            {
                // Reached max points, stop
                InventoryPacket.NoChange(chr);
                return;
            }

            if (sd.RequiredSkills != null)
            {
                foreach (var sdRequiredSkill in sd.RequiredSkills)
                {
                    if (chr.Skills.GetSkillLevel(sdRequiredSkill.Key) < sdRequiredSkill.Value)
                    {
                        Program.MainForm.LogAppend(
                            "Character {0} tried to put points in a skill ({1}) without having enough points in {2} (req {3})",
                            chr.ID, SkillID, sdRequiredSkill.Key, sdRequiredSkill.Value);
                        InventoryPacket.NoChange(chr);
                        return;
                    }
                }
            }

            var jobOfSkill      = Constants.getSkillJob(SkillID);
            var jobTrackOfSkill = Constants.getJobTrack(jobOfSkill);

            // Check if the user tried to get a skill from a different job
            if (
                Constants.getJobTrack(chr.PrimaryStats.Job) != jobTrackOfSkill ||
                jobOfSkill > chr.PrimaryStats.Job
                )
            {
                Program.MainForm.LogAppend("Character {0} tried to put points in a skill ({1}) for the wrong job.", chr.ID, SkillID);
                // More hax
                return;
            }

            chr.Skills.AddSkillPoint(SkillID);
            chr.AddSP(-1);
        }
コード例 #7
0
        /// <summary>
        /// Set the MaxSlots for <param name="inventory"/> to <param name="slots" />.
        /// If the Items array is already initialized, it will either expand the array,
        /// or, when <param name="slots" /> is less, will remove items and shrink it.
        /// </summary>
        /// <param name="inventory">Inventory ID, 0-5</param>
        /// <param name="slots">Amount of slots</param>
        public override void SetInventorySlots(byte inventory, byte slots, bool sendPacket = true)
        {
            base.SetInventorySlots(inventory, slots, sendPacket);

            if (sendPacket)
            {
                InventoryPacket.IncreaseSlots(Character, inventory, slots);
            }
        }
コード例 #8
0
ファイル: PlayerShop.cs プロジェクト: zhuomingliang/WvsGlobal
        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); //-.-
        }
コード例 #9
0
ファイル: MapPacket.cs プロジェクト: zhuomingliang/WvsGlobal
        public static void SendPlayerInfo(Character chr, Packet packet)
        {
            int       id     = packet.ReadInt();
            Character victim = chr.Field.GetPlayer(id);

            if (victim == null)
            {
                InventoryPacket.NoChange(chr);
                return;
            }

            Packet pw = new Packet(ServerMessages.CHARACTER_INFO); // Idk why this is in mappacket, it's part of CWvsContext

            pw.WriteInt(victim.ID);
            pw.WriteByte(victim.PrimaryStats.Level);
            pw.WriteShort(victim.PrimaryStats.Job);
            pw.WriteShort(victim.PrimaryStats.Fame);

            if (chr.IsGM && !victim.IsGM)
            {
                pw.WriteString("" + id + ":" + victim.UserID);
            }
            else if (victim.IsGM && !victim.Undercover)
            {
                pw.WriteString("Administrator");
            }
            else
            {
                pw.WriteString("");
            }

            var petItem = victim.GetSpawnedPet();

            pw.WriteBool(petItem != null);
            if (petItem != null)
            {
                pw.WriteInt(petItem.ItemID);
                pw.WriteString(petItem.Name);
                pw.WriteByte(petItem.Level);
                pw.WriteShort(petItem.Closeness);
                pw.WriteByte(petItem.Fullness);
                pw.WriteInt(victim.Inventory.GetEquippedItemId((short)Constants.EquipSlots.Slots.PetEquip1, true)); // Pet equip.
            }

            pw.WriteByte((byte)victim.Wishlist.Count);
            victim.Wishlist.ForEach(pw.WriteInt);

            //todo : rings
            pw.WriteLong(0);

            chr.SendPacket(pw);
        }
コード例 #10
0
        public BaseItem TakeItemAmountFromSlot(byte inventory, short slot, short amount, bool takeStars)
        {
            var item = GetItem(inventory, slot);

            if (item == null)
            {
                return(null);
            }

            if (!takeStars)
            {
                if (item.Amount - amount < 0)
                {
                    return(null);
                }
            }

            bool     removeItem = false;
            BaseItem newItem;

            if (takeStars && Constants.isStar(item.ItemID))
            {
                // Take the whole item
                newItem    = item;
                removeItem = true;
            }
            else
            {
                newItem    = item.SplitInTwo(amount);
                removeItem = item.Amount == 0 && Constants.isStar(item.ItemID) == false;
            }

            if (removeItem)
            {
                SetItem(inventory, slot, null);
                TryRemoveCashItem(item);
                InventoryPacket.SwitchSlots(Character, slot, 0, inventory);
            }
            else
            {
                // Update item
                InventoryPacket.AddItem(Character, inventory, item, false);
            }

            return(newItem);
        }
コード例 #11
0
ファイル: PlayerShop.cs プロジェクト: zhuomingliang/WvsGlobal
 public void HandleMoveItemBack(Character pCharacter, byte slot)
 {
     if (Items.TryGetValue(slot, out PlayerShopItem pst))
     {
         pCharacter.Inventory.AddItem2(pst.sItem);
         ItemTransfer.PersonalShopGetBackItem(pCharacter.ID, pst.sItem.ItemID, pst.sItem.Amount, _transaction, pst.sItem);
         var x = pst.sItem.Amount;
         if (x == 1)
         {
             PlayerShopPackets.MoveItemToInventory(pCharacter, 0, slot);
             // Remove? but that would reorder it!!!
         }
         else
         {
             byte left = (byte)(x - 1); //amount left
             PlayerShopPackets.MoveItemToInventory(pCharacter, left, slot);
         }
     }
     InventoryPacket.NoChange(pCharacter);
 }
コード例 #12
0
        /// <summary>
        /// Try to remove <paramref name="amount"/> amount of itemid <paramref name="itemid"/>.
        /// Does not 'remove' stacks, keeps them as-is (with 0 items).
        /// </summary>
        /// <param name="itemid">The Item ID</param>
        /// <param name="amount">Amount</param>
        /// <returns>Amount of items that were _not_ taken away</returns>
        public int TakeItem(int itemid, int amount)
        {
            if (amount == 0)
            {
                return(0);
            }

            int  initialAmount  = amount;
            var  isRechargeable = Constants.isRechargeable(itemid);
            byte inventory      = Constants.getInventory(itemid);

            for (short i = 1; i <= MaxSlots[inventory - 1]; i++)
            {
                BaseItem item = GetItem(inventory, i);
                if (item == null || item.ItemID != itemid)
                {
                    continue;
                }

                var maxRemove = Math.Min(item.Amount, amount);
                item.Amount -= (short)maxRemove;
                if (item.Amount == 0 && !isRechargeable)
                {
                    // Your item. Gone.
                    SetItem(inventory, i, null);
                    TryRemoveCashItem(item);
                    InventoryPacket.SwitchSlots(Character, i, 0, inventory);
                }
                else
                {
                    // Update item with new amount
                    InventoryPacket.AddItem(Character, inventory, item, false);
                }
                amount -= maxRemove;
            }

            return(initialAmount - amount);
        }
コード例 #13
0
        public override void AC_OnPacketInbound(Packet packet)
        {
            ClientMessages header = 0;

            try
            {
                header = (ClientMessages)packet.ReadByte();

                if (!Loaded || Player?.Character == null)
                {
                    switch (header)
                    {
                    case ClientMessages.MIGRATE_IN:
                        OnPlayerLoad(packet);
                        break;     //updated
                    }
                }
                // Block packets as we are migrating
                else if (Server.Instance.InMigration == false || Server.Instance.IsNewServerInMigration)
                {
                    var character = Player.Character;

                    if (logPackets.Contains(header))
                    {
                        PacketLog.ReceivedPacket(packet, (byte)header, Server.Instance.Name, IP);
                    }

                    switch (header)
                    {
                    case ClientMessages.ENTER_PORTAL:
                        MapPacket.OnEnterPortal(packet, character);
                        break;

                    case ClientMessages.CHANGE_CHANNEL:
                        OnChangeChannel(character, packet);
                        break;

                    case ClientMessages.ENTER_CASH_SHOP:
                        OnEnterCashShop(character);
                        break;

                    case ClientMessages.MOVE_PLAYER:
                        MapPacket.HandleMove(character, packet);
                        break;

                    case ClientMessages.SIT_REQUEST:
                        MapPacket.HandleSitChair(character, packet);
                        break;

                    case ClientMessages.ENTER_TOWN_PORTAL:
                        MapPacket.HandleDoorUse(character, packet);
                        break;

                    case ClientMessages.CLOSE_RANGE_ATTACK:
                        AttackPacket.HandleMeleeAttack(character, packet);
                        break;

                    case ClientMessages.RANGED_ATTACK:
                        AttackPacket.HandleRangedAttack(character, packet);
                        break;

                    case ClientMessages.MAGIC_ATTACK:
                        AttackPacket.HandleMagicAttack(character, packet);
                        break;

                    case ClientMessages.TAKE_DAMAGE:
                        CharacterStatsPacket.HandleCharacterDamage(character, packet);
                        break;

                    case ClientMessages.CHAT:
                        MessagePacket.HandleChat(character, packet);
                        break;

                    case ClientMessages.GROUP_MESSAGE:
                        MessagePacket.HandleSpecialChat(character, packet);
                        break;

                    case ClientMessages.WHISPER:
                        MessagePacket.HandleCommand(character, packet);
                        break;

                    case ClientMessages.EMOTE:
                        MapPacket.SendEmotion(character, packet.ReadInt());
                        break;

                    case ClientMessages.NPC_TALK:
                        MapPacket.HandleNPCChat(character, packet);
                        break;

                    case ClientMessages.NPC_TALK_MORE:
                        NpcPacket.HandleNPCChat(character, packet);
                        break;

                    case ClientMessages.SHOP_ACTION:
                        NpcPacket.HandleNPCShop(character, packet);
                        break;

                    case ClientMessages.STORAGE_ACTION:
                        StoragePacket.HandleStorage(character, packet);
                        break;

                    case ClientMessages.ITEM_MOVE:
                        InventoryPacket.HandleInventoryPacket(character, packet);
                        break;

                    case ClientMessages.ITEM_USE:
                        InventoryPacket.HandleUseItemPacket(character, packet);
                        break;

                    case ClientMessages.SUMMON_BAG_USE:
                        InventoryPacket.HandleUseSummonSack(character, packet);
                        break;

                    case ClientMessages.CASH_ITEM_USE:
                        CashPacket.HandleCashItem(character, packet);
                        break;

                    case ClientMessages.RETURN_SCROLL_USE:
                        InventoryPacket.HandleUseReturnScroll(character, packet);
                        break;

                    case ClientMessages.SCROLL_USE:
                        InventoryPacket.HandleScrollItem(character, packet);
                        break;

                    case ClientMessages.DISTRIBUTE_AP:
                        CharacterStatsPacket.HandleStats(character, packet);
                        break;

                    case ClientMessages.HEAL_OVER_TIME:
                        CharacterStatsPacket.HandleHeal(character, packet);
                        break;

                    case ClientMessages.DISTRIBUTE_SP:
                        SkillPacket.HandleAddSkillLevel(character, packet);
                        break;

                    case ClientMessages.PREPARE_SKILL:
                        SkillPacket.HandlePrepareSkill(character, packet);
                        break;

                    case ClientMessages.GIVE_BUFF:
                        SkillPacket.HandleUseSkill(character, packet);
                        break;

                    case ClientMessages.CANCEL_BUFF:
                        SkillPacket.HandleStopSkill(character, packet);
                        break;

                    case ClientMessages.DROP_MESOS:
                        DropPacket.HandleDropMesos(character, packet.ReadInt());
                        break;

                    case ClientMessages.GIVE_FAME:
                        FamePacket.HandleFame(character, packet);
                        break;

                    case ClientMessages.CHAR_INFO_REQUEST:
                        MapPacket.SendPlayerInfo(character, packet);
                        break;

                    case ClientMessages.SPAWN_PET:
                        PetsPacket.HandleSpawnPet(character, packet.ReadShort());
                        break;

                    case ClientMessages.SUMMON_MOVE:
                        MapPacket.HandleSummonMove(character, packet);
                        break;

                    case ClientMessages.SUMMON_ATTACK:
                        AttackPacket.HandleSummonAttack(character, packet);
                        break;

                    case ClientMessages.SUMMON_DAMAGED:
                        MapPacket.HandleSummonDamage(character, packet);
                        break;

                    case ClientMessages.MOB_MOVE:
                        MobPacket.HandleMobControl(character, packet);
                        break;

                    case ClientMessages.NPC_ANIMATE:
                        MapPacket.HandleNPCAnimation(character, packet);
                        break;

                    case ClientMessages.PET_MOVE: PetsPacket.HandleMovePet(character, packet); break;

                    case ClientMessages.PET_INTERACTION: PetsPacket.HandleInteraction(character, packet); break;

                    case ClientMessages.PET_ACTION: PetsPacket.HandlePetAction(character, packet); break;

                    case ClientMessages.FIELD_CONTIMOVE_STATE:
                        MapPacket.OnContiMoveState(character, packet);
                        break;

                    case ClientMessages.DROP_PICK_UP:
                        DropPacket.HandlePickupDrop(character, packet);
                        break;

                    case ClientMessages.MESSENGER:
                        MessengerHandler.HandleMessenger(character, packet);
                        break;

                    case ClientMessages.MINI_ROOM_OPERATION:
                        MiniRoomPacket.HandlePacket(character, packet);
                        break;

                    case ClientMessages.FRIEND_OPERATION:
                        BuddyHandler.HandleBuddy(character, packet);
                        break;

                    case ClientMessages.PARTY_OPERATION:
                        PartyHandler.HandleParty(character, packet);
                        break;

                    case ClientMessages.DENY_PARTY_REQUEST:
                        PartyHandler.HandleDecline(character, packet);
                        break;

                    case ClientMessages.REACTOR_HIT:
                        ReactorPacket.HandleReactorHit(character, packet);
                        break;

                    case ClientMessages.REPORT_USER:
                        MiscPacket.ReportPlayer(character, packet);
                        break;


                    //this is a garbage opcode that i use when doing janky client packet workarounds. This is where packets go to die.
                    case ClientMessages.JUNK:
                        Program.MainForm.LogDebug("received junk packet");
                        break;

                    // eh.. ignore?
                    // Happens when one of the following buffs are set:
                    // Stun, Poison, Seal, Darkness, Weakness, Curse
                    // Maybe patch out of the client
                    case ClientMessages.CHARACTER_IS_DEBUFFED: break;

                    // TODO: Implement???
                    case ClientMessages.MOB_APPLY_CONTROL: break;

                    case ClientMessages.CLIENT_HASH: break;

                    case ClientMessages.PONG:
                        // Make sure we update the player online thing
                        RedisBackend.Instance.SetPlayerOnline(
                            character.UserID,
                            Server.Instance.GetOnlineId()
                            );

                        // Cleanup expired items
                        character.Inventory.CheckExpired();
                        break;

                    default:
                        if (character.Field.HandlePacket(character, packet, header) == false)
                        {
                            Program.MainForm.LogAppend(
                                "[{0}] Unknown packet received! " + packet,
                                header
                                );
                        }

                        break;
                    }
                }
            }
            catch (Exception ex)
            {
                Program.MainForm.LogAppend($"---- ERROR ----\r\n{ex}");
                Program.MainForm.LogAppend($"Packet: {packet}");
                FileWriter.WriteLine(@"etclog\ExceptionCatcher.log", "[Game Server " + Server.Instance.ID + "][" + DateTime.Now + "] Exception caught: " + ex, true);
                //Disconnect();
            }


#if DEBUG
            if (packet.Length != packet.Position)
            {
                var packetStr = packet.ToString();
                packetStr = packetStr.Substring(0, packet.Position * 3 - 1) + "-x-" + packetStr.Substring(packet.Position * 3);

                log.Debug($"Did not read full message in packet: {header} {packetStr}");
            }
#endif
        }
コード例 #14
0
        public void CheckExpired()
        {
            var currentTime = MasterThread.CurrentDate.ToFileTimeUtc();

            _cashItems.GetExpiredItems(currentTime, expiredItems =>
            {
                var dict = new Dictionary <byte, List <short> >();
                expiredItems.ForEach(x =>
                {
                    InventoryPacket.SendCashItemExpired(Character, x.ItemId);
                    var inventory = Constants.getInventory(x.ItemId);
                    var baseItem  = GetItemByCashID(x.CashId, inventory);

                    if (baseItem != null)
                    {
                        if (dict.TryGetValue(inventory, out var curList))
                        {
                            curList.Add(baseItem.InventorySlot);
                        }
                        else
                        {
                            dict[inventory] = new List <short> {
                                baseItem.InventorySlot
                            };
                        }
                    }
                    RemoveLockerItem(x, baseItem, true);
                });

                dict.ForEach(x => InventoryPacket.MultiDelete(Character, x.Key, x.Value.ToArray()));
            });

            GetExpiredItems(currentTime, expiredItems =>
            {
                var dict    = new Dictionary <byte, List <short> >();
                var itemIds = new List <int>();
                expiredItems.ForEach(x =>
                {
                    var inventory = Constants.getInventory(x.ItemID);
                    if (x.CashId != 0)
                    {
                        var baseItem = GetItemByCashID(x.CashId, inventory);
                        if (dict.TryGetValue(inventory, out var curList))
                        {
                            curList.Add(baseItem.InventorySlot);
                        }
                        else
                        {
                            dict[inventory] = new List <short> {
                                baseItem.InventorySlot
                            };
                        }
                        TryRemoveCashItem(x);
                    }
                    SetItem(inventory, x.InventorySlot, null);
                    itemIds.Add(x.ItemID);
                });

                InventoryPacket.SendItemsExpired(Character, itemIds);
                dict.ForEach(x => InventoryPacket.MultiDelete(Character, x.Key, x.Value.ToArray()));
            });
        }
コード例 #15
0
        public static void HandleStats(Character chr, Packet packet)
        {
            uint flag = packet.ReadUInt();

            if (chr.AssertForHack(chr.PrimaryStats.AP <= 0, "Trying to use AP, but nothing left."))
            {
                InventoryPacket.NoChange(chr);
                return;
            }

            short jobTrack = Constants.getJobTrack(chr.PrimaryStats.Job);

            switch ((StatFlags)flag)
            {
            case StatFlags.Str:
            {
                if (chr.PrimaryStats.Str >= Constants.MaxStat)
                {
                    InventoryPacket.NoChange(chr);
                    return;
                }
                chr.AddStr(1);
                break;
            }

            case StatFlags.Dex:
            {
                if (chr.PrimaryStats.Dex >= Constants.MaxStat)
                {
                    InventoryPacket.NoChange(chr);
                    return;
                }
                chr.AddDex(1);
                break;
            }

            case StatFlags.Int:
            {
                if (chr.PrimaryStats.Int >= Constants.MaxStat)
                {
                    InventoryPacket.NoChange(chr);
                    return;
                }
                chr.AddInt(1);
                break;
            }

            case StatFlags.Luk:
            {
                if (chr.PrimaryStats.Luk >= Constants.MaxStat)
                {
                    InventoryPacket.NoChange(chr);
                    return;
                }
                chr.AddLuk(1);
                break;
            }

            case StatFlags.MaxHp:
            {
                if (chr.PrimaryStats.MaxHP >= Constants.MaxMaxHp)
                {
                    InventoryPacket.NoChange(chr);
                    return;
                }
                short hpGain = 0;

                hpGain += RNG.Range.generate(
                    Constants.HpMpFormulaArguments[jobTrack, 1, (int)Constants.HpMpFormulaFields.HPMin],
                    Constants.HpMpFormulaArguments[jobTrack, 1, (int)Constants.HpMpFormulaFields.HPMax],
                    true
                    );

                byte improvedMaxHpIncreaseLvl = chr.Skills.GetSkillLevel(Constants.Swordsman.Skills.ImprovedMaxHpIncrease);
                if (improvedMaxHpIncreaseLvl > 0)
                {
                    hpGain += CharacterSkills.GetSkillLevelData(Constants.Swordsman.Skills.ImprovedMaxHpIncrease, improvedMaxHpIncreaseLvl).XValue;
                }

                chr.ModifyMaxHP(hpGain);
                break;
            }

            case StatFlags.MaxMp:
            {
                if (chr.PrimaryStats.MaxMP >= Constants.MaxMaxMp)
                {
                    InventoryPacket.NoChange(chr);
                    return;
                }
                short mpGain = 0;
                short intt   = chr.PrimaryStats.GetIntAddition(true);

                mpGain += RNG.Range.generate(
                    Constants.HpMpFormulaArguments[jobTrack, 1, (int)Constants.HpMpFormulaFields.MPMin],
                    Constants.HpMpFormulaArguments[jobTrack, 1, (int)Constants.HpMpFormulaFields.MPMax],
                    true
                    );

                // Additional buffing through INT stats
                mpGain += (short)(
                    intt *
                    Constants.HpMpFormulaArguments[jobTrack, 1, (int)Constants.HpMpFormulaFields.MPIntStatMultiplier] /
                    200
                    );

                byte improvedMaxMpIncreaseLvl = chr.Skills.GetSkillLevel(Constants.Magician.Skills.ImprovedMaxMpIncrease);
                if (improvedMaxMpIncreaseLvl > 0)
                {
                    mpGain += CharacterSkills.GetSkillLevelData(Constants.Magician.Skills.ImprovedMaxMpIncrease, improvedMaxMpIncreaseLvl).XValue;
                }

                chr.ModifyMaxMP(mpGain);
                break;
            }

            default:
            {
                Program.MainForm.LogAppend("Unknown type {0:X4}", flag);
                break;
            }
            }

            chr.AddAP(-1, true);
            chr.PrimaryStats.CalculateAdditions(false, false);
        }
コード例 #16
0
        public short AddItem2(BaseItem item, bool sendpacket = true)
        {
            byte  inventory = Constants.getInventory(item.ItemID);
            short slot      = 0;
            // see if there's a free slot
            BaseItem temp     = null;
            short    maxSlots = 1;

            if (DataProvider.Items.TryGetValue(item.ItemID, out ItemData itemData))
            {
                maxSlots = (short)itemData.MaxSlot;
                if (maxSlots == 0)
                {
                    // 1, 100 or specified
                    maxSlots = 100;
                }
            }
            for (short i = 1; i <= MaxSlots[inventory - 1]; i++)
            { // Slot 1 - 24, not 0 - 23
                temp = GetItem(inventory, i);
                if (temp != null)
                {
                    if (Constants.isStackable(item.ItemID) && item.ItemID == temp.ItemID && temp.Amount < maxSlots)
                    {
                        if (item.Amount + temp.Amount > maxSlots)
                        {
                            short amount = (short)(maxSlots - temp.Amount);
                            item.Amount -= amount;
                            temp.Amount  = maxSlots;
                            if (sendpacket)
                            {
                                InventoryPacket.AddItem(Character, inventory, temp, false);
                            }
                        }
                        else
                        {
                            item.Amount += temp.Amount;
                            // Removing the item looks a bit odd to me?
                            SetItem(inventory, i, null);
                            AddItem(inventory, i, item, false);
                            if (sendpacket)
                            {
                                InventoryPacket.AddItem(Character, inventory, item, false);
                            }
                            return(0);
                        }
                    }
                }
                else if (slot == 0)
                {
                    slot = i;
                    if (!Constants.isStackable(item.ItemID))
                    {
                        break;
                    }
                }
            }
            if (slot != 0)
            {
                SetItem(inventory, slot, item);
                if (sendpacket)
                {
                    InventoryPacket.AddItem(Character, inventory, item, true);
                }
                return(0);
            }
            else
            {
                return(item.Amount);
            }
        }
コード例 #17
0
ファイル: NpcPacket.cs プロジェクト: zhuomingliang/WvsGlobal
        public static void HandleNPCShop(Character chr, Packet packet)
        {
            if (chr.ShopNPCID == 0)
            {
                return;
            }

            var shopInfo   = DataProvider.NPCs[chr.ShopNPCID].Shop;
            var transferId = "" + chr.ID + "-" + chr.ShopNPCID + "-" + RNG.Range.generate(0, long.MaxValue).ToString();

            byte type = packet.ReadByte();

            switch ((ShopReq)type)
            {
            case ShopReq.Buy:
            {
                short slot   = packet.ReadShort();
                int   itemid = packet.ReadInt();
                short amount = packet.ReadShort();

                if (amount < 1 ||
                    (Constants.isEquip(itemid) && amount != 1))
                {
                    Program.MainForm.LogAppend("Disconnecting player: trying to buy a negative amount of items OR multiple equips. " + packet);
                    chr.Player.Socket.Disconnect();
                    return;
                }

                if (slot < 0 || slot >= shopInfo.Count)
                {
                    SendShopResult(chr, ShopRes.BuyUnknown);
                    return;
                }

                ShopItemData sid   = shopInfo[slot];
                int          costs = amount * sid.Price;
                if (false && sid.Stock == 0)
                {
                    SendShopResult(chr, ShopRes.BuyNoStock);
                    return;
                }
                if (sid.ID != itemid)
                {
                    SendShopResult(chr, ShopRes.BuyUnknown);
                    return;
                }
                if (costs > chr.Inventory.Mesos)
                {
                    SendShopResult(chr, ShopRes.BuyNoMoney);
                    return;
                }

                if (Constants.isRechargeable(itemid))
                {
                    costs = amount * sid.Price;
                    if (amount > DataProvider.Items[itemid].MaxSlot)         // You can't but multiple sets at once
                    {
                        SendShopResult(chr, ShopRes.BuyUnknown);
                        return;
                    }
                }


                if (!chr.Inventory.HasSlotsFreeForItem(itemid, amount, true))
                {
                    SendShopResult(chr, ShopRes.BuyUnknown);
                    return;
                }

                Common.Tracking.MesosTransfer.PlayerBuysFromShop(chr.ID, chr.ShopNPCID, costs,
                                                                 transferId);
                Common.Tracking.ItemTransfer.PlayerBuysFromShop(chr.ID, chr.ShopNPCID, itemid, amount,
                                                                transferId, null);

                chr.Inventory.AddNewItem(itemid, amount);
                SendShopResult(chr, ShopRes.BuySuccess);
                sid.Stock -= amount;
                chr.AddMesos(-costs);

                break;
            }

            case ShopReq.Sell:
            {
                short itemslot = packet.ReadShort();
                int   itemid   = packet.ReadInt();
                short amount   = packet.ReadShort();
                byte  inv      = Constants.getInventory(itemid);

                BaseItem item = chr.Inventory.GetItem(inv, itemslot);

                if (item == null ||
                    item.ItemID != itemid ||
                    amount < 1 ||
                    // Do not trigger this when selling stars and such.
                    (!Constants.isRechargeable(itemid) && amount > item.Amount) ||
                    (Constants.isEquip(itemid)
                            ? DataProvider.Equips.ContainsKey(itemid) == false
                            : DataProvider.Items.ContainsKey(itemid) == false) ||
                    item.CashId != 0)
                {
                    Program.MainForm.LogAppend("Disconnecting player: invalid trade packet: " + packet);
                    chr.Player.Socket.Disconnect();
                    return;
                }


                int sellPrice = 0;
                if (Constants.isEquip(itemid))
                {
                    var ed = DataProvider.Equips[itemid];
                    sellPrice = ed.Price;
                }
                else
                {
                    var id = DataProvider.Items[itemid];
                    sellPrice = id.Price * amount;
                }

                if (sellPrice < 0)
                {
                    SendShopResult(chr, ShopRes.SellIncorrectRequest);
                    return;
                }

                // Change amount here (rechargeables are sold as 1)
                if (Constants.isRechargeable(item.ItemID))
                {
                    amount = item.Amount;
                }

                Common.Tracking.MesosTransfer.PlayerSellsToShop(chr.ID, chr.ShopNPCID, sellPrice, transferId);
                Common.Tracking.ItemTransfer.PlayerSellsToShop(chr.ID, chr.ShopNPCID, item.ItemID, amount, transferId, item);

                if (amount == item.Amount)
                {
                    chr.Inventory.SetItem(inv, itemslot, null);
                    chr.Inventory.TryRemoveCashItem(item);
                    InventoryPacket.SwitchSlots(chr, itemslot, 0, inv);
                }
                else
                {
                    item.Amount -= amount;
                    InventoryPacket.AddItem(chr, inv, item, false);
                }
                chr.AddMesos(sellPrice);

                SendShopResult(chr, ShopRes.SellSuccess);
                break;
            }

            case ShopReq.Recharge:
            {
                short itemslot = packet.ReadShort();

                byte     inv  = 2;
                BaseItem item = chr.Inventory.GetItem(inv, itemslot);
                if (item == null ||
                    !Constants.isRechargeable(item.ItemID))
                {
                    Program.MainForm.LogAppend("Disconnecting player: invalid trade packet: " + packet);
                    chr.Player.Socket.Disconnect();
                    return;
                }

                ShopItemData sid = shopInfo.FirstOrDefault((a) => a.ID == item.ItemID);
                if (sid == null)
                {
                    Program.MainForm.LogAppend("Disconnecting player: Item not found in shop; not rechargeable?");
                    chr.Player.Socket.Disconnect();
                    return;
                }

                if (sid.UnitRechargeRate <= 0.0)
                {
                    SendShopResult(chr, ShopRes.RechargeIncorrectRequest);
                    return;
                }

                ItemData data    = DataProvider.Items[item.ItemID];
                short    maxslot = (short)(data.MaxSlot + chr.Skills.GetRechargeableBonus());
                short    toFill  = (short)(maxslot - item.Amount);

                int sellPrice = (int)Math.Ceiling(-1.0 * sid.UnitRechargeRate * toFill);
                sellPrice = Math.Max(sellPrice, 1);
                if (chr.Inventory.Mesos > -sellPrice)
                {
                    Common.Tracking.MesosTransfer.PlayerBuysFromShop(chr.ID, chr.ShopNPCID, -sellPrice,
                                                                     transferId);
                    Common.Tracking.ItemTransfer.PlayerBuysFromShop(chr.ID, chr.ShopNPCID, item.ItemID,
                                                                    (short)(maxslot - item.Amount), transferId, item);

                    item.Amount = maxslot;

                    chr.AddMesos(sellPrice);
                    InventoryPacket.AddItem(chr, inv, item, false);
                    SendShopResult(chr, ShopRes.RechargeSuccess);
                }
                else
                {
                    SendShopResult(chr, ShopRes.RechargeNoMoney);         // no muney? hier! suk a kok!
                }
                break;
            }

            case ShopReq.Close: chr.ShopNPCID = 0; chr.NpcSession = null; break;

            default:
            {
                Program.MainForm.LogAppend("Unknown NPC shop action: " + packet);
                break;
            }
            }
        }
コード例 #18
0
ファイル: Pet.cs プロジェクト: zhuomingliang/WvsGlobal
 public static void UpdatePet(Character chr, PetItem petItem)
 {
     InventoryPacket.AddItem(chr, Constants.getInventory(petItem.ItemID), petItem, false);
 }
コード例 #19
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;
            }
            }
        }
コード例 #20
0
ファイル: MapPacket.cs プロジェクト: zhuomingliang/WvsGlobal
        public static void OnEnterPortal(Packet packet, Character chr)
        {
            if (packet.ReadByte() != chr.PortalCount)
            {
                InventoryPacket.NoChange(chr);
                return;
            }

            int    opcode     = packet.ReadInt();
            string portalname = packet.ReadString();

            if (portalname.Length > 0)
            {
                new Pos(packet);
            }
            packet.ReadByte(); // Related to teleporting to party member? Always 0
            packet.ReadByte(); // unk

            switch (opcode)
            {
            case 0:
            {
                if (chr.PrimaryStats.HP == 0)
                {
                    chr.HandleDeath();
                }
                else if (!chr.IsGM)
                {
                    Program.MainForm.LogAppend($"Not handling death of {chr.ID}, because user is not dead. Killing him again. HP: " + chr.PrimaryStats.HP);
                    // Kill him anyway
                    chr.DamageHP(30000);
                }
                else
                {
                    // Admin /map 0
                    chr.ChangeMap(opcode);
                }
                break;
            }

            case -1:
            {
                if (chr.Field.Portals.TryGetValue(portalname, out Portal portal) &&
                    DataProvider.Maps.TryGetValue(portal.ToMapID, out Map toMap) &&
                    toMap.Portals.TryGetValue(portal.ToName, out Portal to))
                {
                    var pos  = new Pos(portal.X, portal.Y);
                    var dist = chr.Position - pos;
                    if (chr.AssertForHack(dist > 300, "Portal distance hack (" + dist + ")", dist > 600))
                    {
                        InventoryPacket.NoChange(chr);
                        return;
                    }

                    if (portal.Enabled == false)
                    {
                        Program.MainForm.LogDebug(chr.Name + " tried to enter a disabled portal.");
                        BlockedMessage(chr, PortalBlockedMessage.ClosedForNow);
                        InventoryPacket.NoChange(chr);
                        return;
                    }

                    if (chr.Field.PortalsOpen == false)
                    {
                        Program.MainForm.LogDebug(chr.Name + " tried to enter a disabled portal.");
                        BlockedMessage(chr, PortalBlockedMessage.ClosedForNow);
                        InventoryPacket.NoChange(chr);
                    }
                    else if (chr.Field.PQPortalOpen)
                    {
                        chr.ChangeMap(portal.ToMapID, to);
                    }
                    else
                    {
                        BlockedMessage(chr, PortalBlockedMessage.CannotGoToThatPlace);
                    }
                }
                else
                {
                    Program.MainForm.LogDebug(chr.Name + " tried to enter unknown portal??? " + portalname + ", " + chr.Field.ID);
                    BlockedMessage(chr, PortalBlockedMessage.ClosedForNow);
                }


                break;
            }

            default:
            {
                if (chr.IsGM)
                {
                    chr.ChangeMap(opcode);
                }
                break;
            }
            }
        }
コード例 #21
0
        public static void HandleCashItem(Character chr, Packet packet)
        {
            short slot   = packet.ReadShort();
            int   itemid = packet.ReadInt();

            BaseItem item = chr.Inventory.GetItem(2, slot);

            if (chr.AssertForHack(item == null, "HandleCashItem with null item") ||
                chr.AssertForHack(item.ItemID != itemid, "HandleCashItem with itemid inconsistency") ||
                chr.AssertForHack(!DataProvider.Items.TryGetValue(itemid, out var data), "HandleCashItem with unknown item") ||
                chr.AssertForHack(!data.Cash, "HandleCashItem with non-cash item"))
            {
                return;
            }

            var itemType = (Constants.Items.Types.ItemTypes)Constants.getItemType(itemid);

            bool used = false;

            switch (itemType)
            {
            case Constants.Items.Types.ItemTypes.ItemWeather:
                used = chr.Field.MakeWeatherEffect(itemid, packet.ReadString(), new TimeSpan(0, 0, 30));
                break;

            case Constants.Items.Types.ItemTypes.ItemJukebox:
                used = chr.Field.MakeJukeboxEffect(itemid, chr.Name, packet.ReadInt());
                break;

            case Constants.Items.Types.ItemTypes.ItemPetTag:
            {
                var name    = packet.ReadString();
                var petItem = chr.GetSpawnedPet();
                if (petItem != null &&
                    !chr.IsInvalidTextInput("Pet name tag", name, Constants.MaxPetName, Constants.MinPetName))
                {
                    petItem.Name = name;
                    PetsPacket.SendPetNamechange(chr, petItem.Name);
                    used = true;
                }
            }

            break;

            case Constants.Items.Types.ItemTypes.ItemMegaPhone:
            {
                var text = packet.ReadString();
                if (!chr.IsInvalidTextInput("Megaphone item", text, Constants.MaxSpeakerTextLength))
                {
                    switch (itemid)
                    {
                    case 2081000:             // Super Megaphone (channel)
                        MessagePacket.SendMegaphoneMessage(chr.Name + " : " + text);
                        used = true;
                        break;

                    case 2082000:             // Super Megaphone
                        Server.Instance.CenterConnection.PlayerSuperMegaphone(
                            chr.Name + " : " + text,
                            packet.ReadBool()
                            );
                        used = true;
                        break;
                    }
                }
            }
            break;

            case Constants.Items.Types.ItemTypes.ItemKite:
                if (chr.Field.Kites.Count > 0)
                {
                    //Todo : check for character positions..?
                    MapPacket.KiteMessage(chr);
                }
                else
                {
                    string message = packet.ReadString();
                    Kite   pKite   = new Kite(chr, chr.ID, itemid, message, chr.Field);

                    used = true;
                }
                break;

            case Constants.Items.Types.ItemTypes.ItemMesoSack:
                if (data.Mesos > 0)
                {
                    int amountGot = chr.AddMesos(data.Mesos);

                    MiscPacket.SendGotMesosFromLucksack(chr, amountGot);
                    used = true;
                }
                break;

            case Constants.Items.Types.ItemTypes.ItemTeleportRock:
            {
                byte mode = packet.ReadByte();
                int  map  = -1;
                if (mode == 1)
                {
                    string    name   = packet.ReadString();
                    Character target = Server.Instance.GetCharacter(name);
                    if (target != null && target != chr)
                    {
                        map  = target.MapID;
                        used = true;
                    }
                    else
                    {
                        SendRockError(chr, RockErrors.DifficultToLocate);
                    }
                }
                else
                {
                    map = packet.ReadInt();
                    if (!chr.Inventory.HasRockLocation(map))
                    {
                        map = -1;
                    }
                }

                if (map != -1)
                {
                    //I don't think it's even possible for you to be in a map that doesn't exist and use a Teleport rock?
                    Map from = chr.Field;
                    Map to   = DataProvider.Maps.ContainsKey(map) ? DataProvider.Maps[map] : null;

                    if (to == from)
                    {
                        SendRockError(chr, RockErrors.AlreadyThere);
                    }
                    else if (from.Limitations.HasFlag(FieldLimit.TeleportItemLimit))
                    {
                        SendRockError(chr, RockErrors.CannotGo);
                    }
                    else if (chr.AssertForHack(chr.PrimaryStats.Level < 7, "Using telerock while not lvl 8 or higher."))
                    {
                        // Hacks.
                    }
                    else
                    {
                        chr.ChangeMap(map);
                        used = true;
                    }
                }

                break;
            }

            default:
                Program.MainForm.LogAppend("Unknown cashitem used: {0} {1} {2}", itemType, itemid, packet.ToString());
                break;
            }

            if (used)
            {
                ItemTransfer.ItemUsed(chr.ID, item.ItemID, 1, "");
                chr.Inventory.TakeItem(item.ItemID, 1);
            }
            else
            {
                InventoryPacket.NoChange(chr);
            }
        }
コード例 #22
0
ファイル: NpcPacket.cs プロジェクト: zhuomingliang/WvsGlobal
        public static void HandleNPCChat(Character chr, Packet packet)
        {
            if (chr.NpcSession == null)
            {
                return;
            }

            NpcChatSession session = chr.NpcSession;
            byte           state   = packet.ReadByte();

            if (state != session.mLastSentType)
            {
                InventoryPacket.NoChange(chr);
                return;
            }

            if (!session.WaitingForResponse)
            {
                InventoryPacket.NoChange(chr);
                return;
            }
            session.WaitingForResponse = false;

            Trace.WriteLine(packet.ToString());

            byte option = packet.ReadByte();

            try
            {
                switch (state)
                {
                case 0:
                    switch (option)
                    {
                    case 0:         // Back button...
                        session.SendPreviousMessage();
                        break;

                    case 1:         // Next button...
                        session.SendNextMessage();
                        break;

                    default:
                        session.Stop();
                        break;
                    }
                    break;

                case 1:
                    switch (option)
                    {
                    case 0:         // No.
                        session.HandleThing(session.mRealState, 0, "", 0);
                        break;

                    case 1:         // Yes.
                        session.HandleThing(session.mRealState, 1, "", 0);
                        break;

                    default:
                        session.Stop();
                        break;
                    }
                    break;

                case 2:
                    switch (option)
                    {
                    case 0:         // No text :(
                        session.Stop();
                        break;

                    case 1:         // Oh yea, text
                        session.HandleThing(session.mRealState, 1, packet.ReadString(), 0);
                        break;

                    default:
                        session.Stop();
                        break;
                    }
                    break;

                case 3:
                    switch (option)
                    {
                    case 0:         // No int :(
                        session.Stop();
                        break;

                    case 1:         // Oh yea, int
                        session.HandleThing(session.mRealState, 1, "", packet.ReadShort());
                        break;

                    default:
                        session.Stop();
                        break;
                    }
                    break;

                case 4:
                case 5:
                    switch (option)
                    {
                    case 0:         // Stopping.
                        session.Stop();
                        break;

                    case 1:         // Got answer
                        var val = packet.ReadByte();
                        if (val == 255)
                        {
                            val = 0;                     // Menus do not correctly work when holding enter key
                        }
                        session.HandleThing(session.mRealState, val, "", 0);
                        break;

                    default:
                        session.Stop();
                        break;
                    }
                    break;

                default:
                    session.Stop();
                    Program.MainForm.LogAppend("Unknown NPC chat action: " + packet);
                    break;
                }
            }
            catch (Exception ex)
            {
                Program.MainForm.LogAppend($"Exception while handling NPC {session.mID} {session.mRealState}. Packet: " + packet + ". Exception: " + ex);
                InventoryPacket.NoChange(chr);
                session?.Stop();
            }
        }
コード例 #23
0
        public static void HandlePickupDrop(Character chr, Packet packet)
        {
            // 5F 18 FF 12 01 00 00 00 00
            packet.Skip(4); // pos?

            int dropid = packet.ReadInt();

            if (chr.AssertForHack(chr.Room != null, "Trying to loot a drop while in a 'room'") ||
                !chr.Field.DropPool.Drops.TryGetValue(dropid, out Drop drop) ||
                !drop.CanTakeDrop(chr))
            {
                InventoryPacket.NoChange(chr);
                return;
            }

            var dropLootRange = drop.Pt2 - chr.Position;

            chr.AssertForHack(dropLootRange > 200, "Possible drop VAC! Distance: " + dropLootRange, dropLootRange > 250);

            bool   SentDropNotice          = false;
            Reward reward                  = drop.Reward;
            int    dropNoticeItemIdOrMesos = reward.Drop;
            short  pickupAmount            = reward.Amount;

            if (reward.Mesos)
            {
                // Party meso distribution
                if (drop.SourceID != 0 &&
                    chr.PartyID != 0 &&
                    drop.OwnPartyID == chr.PartyID)
                {
                    var PartyData = chr.Field.GetInParty(chr.PartyID);
                    var Count     = PartyData.Count();

                    if (Count > 1)
                    {
                        SentDropNotice = true;
                        var Base = drop.Reward.Drop * 0.8 / Count + 0.5;
                        Base = Math.Floor(Base);
                        if (Base <= 0.0)
                        {
                            Base = 0.0;
                        }

                        var Bonus = Convert.ToInt32(drop.Reward.Drop - Count * Base);
                        if (Bonus < 0)
                        {
                            Bonus = 0;
                        }

                        drop.Reward.Drop = Convert.ToInt32(Base);

                        foreach (var BonusUser in PartyData)
                        {
                            int mesosGiven = reward.Drop;
                            if (chr.ID == BonusUser.ID)
                            {
                                mesosGiven += Bonus;
                            }
                            // Now figure out what we really gave the user
                            mesosGiven = BonusUser.AddMesos(mesosGiven, true);

                            Common.Tracking.MesosTransfer.PlayerLootMesos(drop.SourceID, chr.ID, mesosGiven, "Party " + chr.PartyID + ", " + chr.MapID + ", " + drop.GetHashCode());

                            CharacterStatsPacket.SendGainDrop(BonusUser, true, mesosGiven, 0);
                        }
                    }
                }

                if (!SentDropNotice)
                {
                    dropNoticeItemIdOrMesos = chr.AddMesos(reward.Drop, true);
                    Common.Tracking.MesosTransfer.PlayerLootMesos(
                        drop.SourceID,
                        chr.ID,
                        dropNoticeItemIdOrMesos,
                        chr.MapID + ", " + drop.GetHashCode()
                        );
                }
            }
            else if (Constants.isStar(reward.ItemID))
            {
                if (!chr.Inventory.HasSlotsFreeForItem(reward.ItemID, reward.Amount, Constants.isStackable(reward.ItemID)))
                {
                    CannotLoot(chr, -1);
                    InventoryPacket.NoChange(chr);
                    return;
                }
                var rewardItem = drop.Reward.GetData();
                chr.Inventory.AddItem2(rewardItem);
                ItemTransfer.ItemPickedUp(chr.ID, chr.MapID, reward.ItemID, reward.Amount, chr.MapID + ", " + drop.GetHashCode(), rewardItem);
            }
            else if (chr.Inventory.AddItem2(drop.Reward.GetData()) == drop.Reward.Amount)
            {
                CannotLoot(chr, -1);
                InventoryPacket.NoChange(chr); // ._. stupid nexon
                return;
            }
            else
            {
                if (Constants.isEquip(drop.Reward.ItemID))
                {
                    ItemTransfer.ItemPickedUp(chr.ID, chr.MapID, reward.ItemID, reward.Amount, chr.MapID + ", " + drop.GetHashCode(), drop.Reward.GetData());
                }
            }
            if (!SentDropNotice)
            {
                CharacterStatsPacket.SendGainDrop(chr, reward.Mesos, dropNoticeItemIdOrMesos, pickupAmount);
            }
            chr.Field.DropPool.RemoveDrop(drop, RewardLeaveType.FreeForAll, chr.ID);
        }
コード例 #24
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);
            }
        }