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 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 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 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 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 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); }
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 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); }
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 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 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 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 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 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 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); } }