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 ReportPlayer(Character chr, Packet packet) { var characterId = packet.ReadInt(); var reported = Server.Instance.GetCharacter(characterId); var reason = packet.ReadByte(); string textReason = "Invalid reason (" + reason + ")"; switch (reason) { case 0: textReason = "hacking"; break; case 1: textReason = "botting"; break; case 2: textReason = "scamming"; break; case 3: textReason = "fAKE gm"; break; case 4: textReason = "harassment"; break; case 5: textReason = "advertising"; break; } if (reported != null) { SendSueResult(reported, SueResults.YouHaveBeenSnitched); } else { // Not sending this as we have enough info anyway // SendSueResult(chr, SueResults.UnableToLocateTheUser); } SendSueResult(chr, SueResults.SuccessfullyReported); // Store the reasons somewhere... var report = new AbuseReport( chr.Name, chr.ID, chr.UserID, reported == null ? "null" : reported.Name, reported == null ? -1 : reported.ID, reported == null ? -1 : reported.UserID, chr.MapID, reason, textReason, MasterThread.CurrentDate); Server.Instance.ServerTraceDiscordReporter.Enqueue(report.ToString()); MessagePacket.SendNoticeGMs(report.ToString(), MessagePacket.MessageTypes.Notice); ReportManager.AddAbuseReport(report); }
private static void EnterMiniRoom(Character chr, Packet packet) { if (chr.Room != null) { miniroomLog.Info($"{chr.Name} cannot enter miniroom: already in one."); return; // Already in a Mini Room } //MessagePacket.SendNotice("PACKET: " + packet.ToString(), chr); int roomId = packet.ReadInt(); if (!MiniRoomBase.MiniRooms.TryGetValue(roomId, out var mrb)) { ReportManager.FileNewReport("Tried entering a trade room without a proper ID.", chr.ID, 0); return; // Invalid Room ID } if (mrb.EnteredUsers == 0) { return; } if (mrb.IsFull()) { miniroomLog.Info($"{chr.Name} cannot enter miniroom: already full."); return; // Error msg if full? } if (mrb.Users.ToList().Exists(u => u != null && u.MapID != chr.MapID)) { InviteResult(chr, 1); // must be on same map. Show "not found" msg return; } chr.Room = mrb; byte nType = (byte)chr.Room.Type; switch (nType) { case 1: // Omok { bool usePassword = packet.ReadBool(); Omok omok = MiniRoomBase.Omoks[chr.Room.ID]; if (usePassword) { string password = packet.ReadString(); if (password != omok.Password) { miniroomLog.Info($"{chr.Name} cannot enter omok: invalid password"); MiniGamePacket.ErrorMessage(chr, MiniGamePacket.MiniGameError.IncorrectPassword); chr.Room = null; break; } } if (chr.Inventory.Mesos >= 100) { omok.AddPlayer(chr); MiniGamePacket.AddVisitor(chr, mrb); MiniGamePacket.ShowWindow(chr, mrb, omok.OmokType); chr.AddMesos(-100); miniroomLog.Info($"{chr.Name} entered omok"); } else { miniroomLog.Info($"{chr.Name} cannot enter omok: not enough mesos"); MiniGamePacket.ErrorMessage(chr, MiniGamePacket.MiniGameError.NotEnoughMesos); } break; } case 3: // Trade { miniroomLog.Info($"{chr.Name} entered trade"); mrb.AddPlayer(chr); MiniRoomPacket.ShowJoin(mrb, chr); MiniRoomPacket.ShowWindow(mrb, chr); break; } case 4: // Player Shop { miniroomLog.Info($"{chr.Name} entered playershop"); PlayerShop shop = MiniRoomBase.PlayerShops[roomId]; for (int i = 0; i < shop.EnteredUsers; i++) { Character shopUser = mrb.Users[i]; if (shopUser != null && shopUser != chr) { shop.AddPlayer(chr); PlayerShopPackets.AddPlayer(chr, shopUser); PlayerShopPackets.OpenPlayerShop(chr, mrb); PlayerShopPackets.PersonalShopRefresh(chr, shop); //Show items } } break; } } }
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 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); } }