private void AddItems(Character chr) { // Note: Exchange logic, so A gets B and B gets A stuff for (int i = 0; i < 2; i++) { if (i == chr.RoomSlotId) { continue; } var charFrom = Users[i]; for (int j = 0; j < 10; j++) { TradeItem ti = ItemList[i][j]; if (ti?.OriginalItem != null) { chr.Inventory.AddItem2(ti.OriginalItem); ItemTransfer.PlayerTradeExchange(charFrom.ID, chr.ID, ti.OriginalItem.ItemID, ti.OriginalItem.Amount, _transaction, ti.OriginalItem); ti.OriginalItem = null; } } var mesos = Mesos[i]; if (mesos != 0) { chr.AddMesos(mesos); MesosTransfer.PlayerTradeExchange(charFrom.ID, chr.ID, mesos, _transaction); } } }
public override void RemovePlayer(Character pCharacter, byte pReason) { // Give items back RevertItems(); var mesos = Mesos[pCharacter.RoomSlotId]; if (mesos != 0) { pCharacter.AddMesos(mesos); MesosTransfer.PlayerTradeReverted(pCharacter.ID, mesos, _transaction); Mesos[pCharacter.RoomSlotId] = 0; } base.RemovePlayer(pCharacter, pReason); }
public static void HandleStorage(Character chr, Packet pr) { if (chr.TrunkNPCID == 0) { return; } byte opcode = pr.ReadByte(); switch ((StorageAction)opcode) { case StorageAction.Withdraw: // Remove { byte inventory = pr.ReadByte(); byte slot = pr.ReadByte(); BaseItem item = chr.Storage.GetItem(inventory, slot); if (item == null) { return; } short amount = item.Amount; if (!Constants.isStackable(item.ItemID)) { amount = 1; // 1 'set' } if (chr.Inventory.HasSlotsFreeForItem(item.ItemID, amount, inventory != 1)) { // AddItem2 will distribute stackable items chr.Inventory.AddItem2(item); chr.Storage.TakeItemOut(inventory, slot); ItemTransfer.PlayerStorageWithdraw(chr.ID, chr.TrunkNPCID, item.ItemID, item.Amount, null, item); EncodeStorage(chr, StorageEncode.EncodeWithdraw, GetEncodeFlagForInventory(Constants.getInventory(item.ItemID))); } else { SendError(chr, StorageErrors.InventoryFullOrNot); } break; } case StorageAction.Deposit: // Add { byte slot = (byte)pr.ReadShort(); int itemid = pr.ReadInt(); short amount = pr.ReadShort(); NPCData data = DataProvider.NPCs[chr.TrunkNPCID]; var storageCost = data.Trunk; if (chr.Inventory.Mesos < storageCost) { SendError(chr, StorageErrors.NotEnoughMesos); return; } byte inventory = Constants.getInventory(itemid); BaseItem item = chr.Inventory.GetItem(inventory, slot); if (item == null || item.ItemID != itemid || item.CashId != 0) { // hax return; } if (!chr.Storage.SlotsAvailable()) { SendError(chr, StorageErrors.StorageIsFull); return; } var isRechargable = Constants.isRechargeable(item.ItemID); if (isRechargable) { amount = item.Amount; } var possibleNewItem = chr.Inventory.TakeItemAmountFromSlot(inventory, slot, amount, true); if (chr.AssertForHack(possibleNewItem == null, "Storage hack (amount > item.amount)")) { return; } chr.Storage.AddItem(possibleNewItem); ItemTransfer.PlayerStorageStore(chr.ID, chr.TrunkNPCID, item.ItemID, item.Amount, "" + item.GetHashCode() + " " + possibleNewItem.GetHashCode(), possibleNewItem); EncodeStorage(chr, StorageEncode.EncodeDeposit, GetEncodeFlagForInventory(Constants.getInventory(item.ItemID))); chr.AddMesos(-storageCost); //why did you forget this diamondo :P MesosTransfer.PlayerGaveToNPC(chr.ID, chr.TrunkNPCID, storageCost, "" + item.GetHashCode()); break; } case StorageAction.StoreMesos: { int mesos = pr.ReadInt(); if (mesos < 0) { // Store if (chr.AssertForHack(Math.Abs(mesos) > chr.Inventory.Mesos, "Trying to store more mesos than he has") == false) { Common.Tracking.MesosTransfer.PlayerStoreMesos(chr.ID, Math.Abs(mesos)); chr.AddMesos(mesos); chr.Storage.ChangeMesos(mesos); } } else { // Withdraw if (chr.AssertForHack(Math.Abs(mesos) > chr.Storage.Mesos, "Trying to withdraw more mesos than he has") == false) { Common.Tracking.MesosTransfer.PlayerRetrieveMesos(chr.ID, Math.Abs(mesos)); chr.AddMesos(mesos); chr.Storage.ChangeMesos(mesos); } } break; } case StorageAction.Exit: { chr.TrunkNPCID = 0; break; } default: { Program.MainForm.LogAppend("Unknown Storage action: {0}", pr); break; } } }
public void BuyItem(Character pCharacter, byte slot, short quantity) { //This may seem confusing, but the client calculates the amount left itself. //The formula is bundles * bundleamount, so if you have 2 bundles with 25 in each, it will obviously show 50. If you have 100 items in 1 bundle, it will show you 100 PlayerShopItem pst = Items[slot]; PlayerShop ps = MiniRoomBase.PlayerShops[pCharacter.Room.ID]; if (pst == null) { return; } if (pCharacter.AssertForHack( quantity < 0, $"PlayerShop hack: trying to buy negative amount. Requested {quantity}" )) { return; } if (pst.sItem.Amount <= 0) { // TODO: Get packet? return; } var cost = quantity * pst.Price; var realAmount = (short)(quantity * pst.BundleAmount); if (cost > pCharacter.Inventory.Mesos) { // TODO: Get packet? return; } if (pCharacter.AssertForHack( pst.Bundles < quantity, $"PlayerShop hack: buying more than there's in store. Bundle: {pst.Bundles}, requested {quantity}" )) { return; } if (pCharacter.AssertForHack( pst.sItem.Amount < realAmount, $"PlayerShop hack: buying more than there's in store. Item amount: {pst.sItem.Amount}, requested {realAmount}" )) { return; } pCharacter.Inventory.AddNewItem(pst.sItem.ItemID, realAmount); pCharacter.AddMesos(-cost); Owner.AddMesos(cost); MesosTransfer.PlayerBuysFromPersonalShop(pCharacter.ID, Owner.ID, cost, _transaction); ItemTransfer.PersonalShopBoughtItem(Owner.ID, pCharacter.ID, pst.sItem.ItemID, realAmount, TransactionID, pst.sItem); pst.Bundles -= quantity; pst.sItem.Amount -= realAmount; PlayerShopPackets.PersonalShopRefresh(pCharacter, ps); PlayerShopPackets.SoldItemResult(Owner, pCharacter, slot, quantity); }
public void DoSkillCost(int skillid, byte level) { var data = GetSkillLevelData(skillid, level); if (data == null) { return; } if (skillid == Constants.DragonKnight.Skills.DragonRoar) { var lefthp = (int)(Character.PrimaryStats.MaxHP * (data.XValue / 100.0d)); Character.DamageHP((short)lefthp); } var mp = data.MPUsage; var hp = data.HPUsage; var cash = data.MesosUsage; var item = data.ItemIDUsage; if (mp > 0) { mp = GetElemAmpInc(skillid, mp); if (Character.AssertForHack(Character.PrimaryStats.MP < mp, "MP Hack (no MP left)")) { return; } Character.ModifyMP((short)-mp, true); } if (hp > 0) { if (Character.AssertForHack(Character.PrimaryStats.HP < hp, "HP Hack (no HP left)")) { return; } Character.ModifyHP((short)-hp, true); } if (item > 0) { var slotsAvailable = Character.Inventory.ItemAmountAvailable(item); if (Character.AssertForHack(slotsAvailable < data.ItemAmountUsage, "Player tried to use skill with item consumption. Not enough items to consume.")) { return; } Character.Inventory.TakeItem(item, data.ItemAmountUsage); ItemTransfer.PlayerUsedSkill(Character.ID, skillid, item, data.ItemAmountUsage); } if (cash > 0 && false) { var min = (short)(cash - (80 + level * 5)); var max = (short)(cash + (80 + level * 5)); var realAmount = (short)Character.CalcDamageRandomizer.ValueBetween(min, max); if (Character.AssertForHack(((long)Character.Inventory.Mesos - realAmount) < 0, "Player tried to use skill with meso consumption. Not enough mesos.")) { return; } Character.AddMesos(-realAmount); MesosTransfer.PlayerUsedSkill(Character.ID, realAmount, skillid); } }
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 HandleCharacterDamage(Character chr, Packet pr) { //1A FF 03 00 00 00 00 00 00 00 00 04 87 01 00 00 00 sbyte attack = pr.ReadSByte(); int damage = pr.ReadInt(); int reducedDamage = damage; int actualHPEffect = -damage; int actualMPEffect = 0; int healSkillId = 0; Mob mob = null; if (chr.AssertForHack(damage < -1, "Less than -1 (" + damage + ") damage in HandleCharacterDamage")) { return; } if (chr.PrimaryStats.HP == 0) { return; } byte mobSkillId = 0, mobSkillLevel = 0; if (attack <= -2) { mobSkillLevel = pr.ReadByte(); mobSkillId = pr.ReadByte(); // (short >> 8) Trace.WriteLine($"Got a hit with {attack} attack, mobSkillLevel {mobSkillLevel}, mobSkillId {mobSkillId}"); } else { int magicAttackElement = 0; if (pr.ReadBool()) { magicAttackElement = pr.ReadInt(); // 0 = no element (Grendel the Really Old, 9001001) // 1 = Ice (Celion? blue, 5120003) // 2 = Lightning (Regular big Sentinel, 3000000) // 3 = Fire (Fire sentinel, 5200002) } var mobMapId = pr.ReadInt(); var mobId = pr.ReadInt(); mob = chr.Field.GetMob(mobMapId); if (mob == null || mobId != mob.MobID) { return; } // Newer ver: int nCalcDamageMobStatIndex var stance = pr.ReadByte(); var isReflected = pr.ReadBool(); byte reflectHitAction = 0; short reflectX = 0, reflectY = 0; if (isReflected) { reflectHitAction = pr.ReadByte(); reflectX = pr.ReadShort(); reflectY = pr.ReadShort(); } if (chr.PrimaryStats.BuffMagicGuard.HasReferenceId(Constants.Magician.Skills.MagicGuard) && chr.PrimaryStats.MP > 0) { // Absorbs X amount of damage. :) var skillId = chr.PrimaryStats.BuffMagicGuard.R; byte skillLevel; var sld = chr.Skills.GetSkillLevelData(skillId, out skillLevel); int damageEaten = (int)Math.Round((damage * (sld.XValue / 100.0d))); // MagicGuard doesn't show reduced damage. Trace.WriteLine($"Reducing damage by MG. Reflected {damageEaten}"); //Program.MainForm.LogAppend("MG Damage before change: " + actualHPEffect); actualHPEffect += damageEaten; //Program.MainForm.LogAppend("MG Damage after change: " + actualHPEffect); actualMPEffect = -damageEaten; healSkillId = skillId; } if (chr.PrimaryStats.BuffPowerGuard.HasReferenceId(Constants.Fighter.Skills.PowerGuard) || chr.PrimaryStats.BuffPowerGuard.HasReferenceId(Constants.Page.Skills.PowerGuard)) { var skillId = chr.PrimaryStats.BuffPowerGuard.R; byte skillLevel; var sld = chr.Skills.GetSkillLevelData(skillId, out skillLevel); int damageReflectedBack = (int)(damage * (sld.XValue / 100.0d)); if (damageReflectedBack > mob.MaxHP) { damageReflectedBack = (int)(mob.MaxHP * 0.1); } if (mob.IsBoss) { damageReflectedBack /= 2; } mob.GiveDamage(chr, damageReflectedBack); MobPacket.SendMobDamageOrHeal(chr, mobId, damageReflectedBack, false, false); mob.CheckDead(mob.Position); Trace.WriteLine($"Reducing damage by PG. Reflected {damageReflectedBack}"); actualHPEffect += damageReflectedBack; // Buff 'damaged' hp, so its less healSkillId = skillId; } if (chr.PrimaryStats.BuffMesoGuard.IsSet()) { var skillId = Constants.ChiefBandit.Skills.MesoGuard; var sld = chr.Skills.GetSkillLevelData( skillId, out var skillLevel ); if (sld != null) { var percentage = sld.XValue; var damageReduction = reducedDamage / 2; var mesoLoss = damageReduction * percentage / 100; if (damageReduction != 0) { var playerMesos = chr.Inventory.Mesos; var maxMesosUsable = Math.Min(chr.PrimaryStats.BuffMesoGuard.MesosLeft, playerMesos); if (mesoLoss > maxMesosUsable) { // New calculation. in our version it should actually 'save' the // mesos for a bit. damageReduction = 100 * maxMesosUsable / percentage; mesoLoss = maxMesosUsable; } if (mesoLoss > 0) { chr.PrimaryStats.BuffMesoGuard.MesosLeft -= mesoLoss; MesosTransfer.PlayerUsedSkill(chr.ID, mesoLoss, skillId); chr.AddMesos(-(mesoLoss), false); Trace.WriteLine($"Reducing damage by mesos. Mesos: {mesoLoss}, maxMesos {maxMesosUsable}, reduction {damageReduction}"); actualHPEffect += damageReduction; reducedDamage -= reducedDamage; } if (chr.PrimaryStats.BuffMesoGuard.MesosLeft <= 0) { // Debuff when out of mesos chr.PrimaryStats.RemoveByReference(skillId); } } } } SendCharacterDamageByMob( chr, attack, damage, reducedDamage, healSkillId, mobMapId, mobId, stance, isReflected, reflectHitAction, reflectX, reflectY ); } Trace.WriteLine($"Showing damage: {reducedDamage}, {damage}"); Trace.WriteLine($"Applying damage: HP {actualHPEffect}, MP: {actualMPEffect}"); if (actualHPEffect < 0) { chr.ModifyHP((short)actualHPEffect); } if (actualMPEffect < 0) { chr.ModifyMP((short)actualMPEffect); } if (mobSkillLevel != 0 && mobSkillId != 0) { // Check if the skill exists and has any extra effect. if (!DataProvider.MobSkills.TryGetValue(mobSkillId, out var skillLevels)) { return; } // Still going strong if (!skillLevels.TryGetValue(mobSkillLevel, out var msld)) { return; } OnStatChangeByMobSkill(chr, msld); } else if (mob != null) { // CUser::OnStatChangeByMobAttack if (mob.Data.Attacks == null || !mob.Data.Attacks.TryGetValue((byte)attack, out var mad)) { return; } // Okay, we've got an attack... if (mad.Disease <= 0) { return; } // Shit's poisonous! // Hmm... We could actually make snails give buffs... hurr if (!DataProvider.MobSkills.TryGetValue(mad.Disease, out var skillLevels)) { return; } // Still going strong if (!skillLevels.TryGetValue(mad.SkillLevel, out var msld)) { return; } OnStatChangeByMobSkill(chr, msld); } }