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); }
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); } }
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); }
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); } } }
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); }
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); }
/// <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); } }
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); //-.- }
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); }
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); }
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); }
/// <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); }
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 }
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())); }); }
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); }
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); } }
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; } } }
public static void UpdatePet(Character chr, PetItem petItem) { InventoryPacket.AddItem(chr, Constants.getInventory(petItem.ItemID), petItem, false); }
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; } } }
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; } } }
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); } }
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(); } }
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); }
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); } }