From aa88c5bb2f133cbe75c2a65f349d4d3560967921 Mon Sep 17 00:00:00 2001 From: hondacrx Date: Sun, 18 Sep 2022 20:32:52 -0400 Subject: [PATCH] Core/Loot: Simplify loot containers Port From (https://github.com/TrinityCore/TrinityCore/commit/c00e2e4851498aa04ac1d8ff13a0759b245df72b) --- Source/Framework/Constants/LootConst.cs | 11 - Source/Framework/Constants/SharedConst.cs | 1 - Source/Game/Chat/Commands/NPCCommands.cs | 19 +- Source/Game/Entities/Player/Player.Items.cs | 150 +---- Source/Game/Entities/Player/Player.cs | 4 +- Source/Game/Handlers/LootHandler.cs | 13 +- Source/Game/Loot/Loot.cs | 671 +++++--------------- Source/Game/Loot/LootManager.cs | 14 +- Source/Game/Mails/MailDraft.cs | 3 +- 9 files changed, 194 insertions(+), 692 deletions(-) diff --git a/Source/Framework/Constants/LootConst.cs b/Source/Framework/Constants/LootConst.cs index 82b4b1345..e7405ff52 100644 --- a/Source/Framework/Constants/LootConst.cs +++ b/Source/Framework/Constants/LootConst.cs @@ -58,17 +58,6 @@ namespace Framework.Constants JunkFish = 0x8000 } - public enum PermissionTypes - { - All = 0, - Group = 1, - Master = 2, - Restricted = 3, - RoundRobin = 4, - Owner = 5, - None = 6 - } - public enum LootType { None = 0, diff --git a/Source/Framework/Constants/SharedConst.cs b/Source/Framework/Constants/SharedConst.cs index 8aac74687..63c544fd0 100644 --- a/Source/Framework/Constants/SharedConst.cs +++ b/Source/Framework/Constants/SharedConst.cs @@ -79,7 +79,6 @@ namespace Framework.Constants /// Loot Const /// public const int MaxNRLootItems = 18; - public const int MaxNRQuestItems = 32; public const int PlayerCorpseLootEntry = 1; /// diff --git a/Source/Game/Chat/Commands/NPCCommands.cs b/Source/Game/Chat/Commands/NPCCommands.cs index 1c87d6ca4..e4eddc230 100644 --- a/Source/Game/Chat/Commands/NPCCommands.cs +++ b/Source/Game/Chat/Commands/NPCCommands.cs @@ -340,11 +340,6 @@ namespace Game.Chat foreach (LootItem item in loot.items) if (!item.is_looted) _ShowLootEntry(handler, item.itemid, item.count); - - handler.SendSysMessage(CypherStrings.CommandNpcShowlootLabel, "Quest items", loot.quest_items.Count); - foreach (LootItem item in loot.quest_items) - if (!item.is_looted) - _ShowLootEntry(handler, item.itemid, item.count); } else { @@ -353,23 +348,11 @@ namespace Game.Chat if (!item.is_looted && !item.freeforall && item.conditions.Empty()) _ShowLootEntry(handler, item.itemid, item.count); - if (!loot.GetPlayerQuestItems().Empty()) - { - handler.SendSysMessage(CypherStrings.CommandNpcShowlootLabel2, "Per-player quest items"); - _IterateNotNormalLootMap(handler, loot.GetPlayerQuestItems(), loot.quest_items); - } - if (!loot.GetPlayerFFAItems().Empty()) { handler.SendSysMessage(CypherStrings.CommandNpcShowlootLabel2, "FFA items per allowed player"); _IterateNotNormalLootMap(handler, loot.GetPlayerFFAItems(), loot.items); } - - if (!loot.GetPlayerNonQuestNonFFAConditionalItems().Empty()) - { - handler.SendSysMessage(CypherStrings.CommandNpcShowlootLabel2, "Per-player conditional items"); - _IterateNotNormalLootMap(handler, loot.GetPlayerNonQuestNonFFAConditionalItems(), loot.items); - } } return true; @@ -565,7 +548,7 @@ namespace Game.Chat foreach (var it in list) { - LootItem item = items[it.index]; + LootItem item = items[it.LootListId]; if (!it.is_looted && !item.is_looted) _ShowLootEntry(handler, item.itemid, item.count, true); } diff --git a/Source/Game/Entities/Player/Player.Items.cs b/Source/Game/Entities/Player/Player.Items.cs index bdf5ef77c..254136815 100644 --- a/Source/Game/Entities/Player/Player.Items.cs +++ b/Source/Game/Entities/Player/Player.Items.cs @@ -477,7 +477,7 @@ namespace Game.Entities DurabilityRepair(item.GetPos(), false, 0.0f); } } - + public void DurabilityRepair(ushort pos, bool takeCost, float discountMod) { Item item = GetItemByPos(pos); @@ -4107,14 +4107,14 @@ namespace Game.Entities { return m_lootRolls.Find(roll => roll.IsLootItem(lootObjectGuid, lootListId)); } - + public void AddLootRoll(LootRoll roll) { m_lootRolls.Add(roll); } public void RemoveLootRoll(LootRoll roll) { m_lootRolls.Remove(roll); } - + //Inventory public bool IsInventoryPos(ushort pos) { @@ -5956,7 +5956,7 @@ namespace Game.Entities public void SetLootGUID(ObjectGuid guid) { SetUpdateFieldValue(m_values.ModifyValue(m_playerData).ModifyValue(m_playerData.LootTargetGUID), guid); } public void StoreLootItem(ObjectGuid lootWorldObjectGuid, byte lootSlot, Loot loot, AELootResult aeResult = null) { - LootItem item = loot.LootItemInSlot(lootSlot, this, out NotNormalLootItem qitem, out NotNormalLootItem ffaitem, out NotNormalLootItem conditem); + LootItem item = loot.LootItemInSlot(lootSlot, this, out NotNormalLootItem ffaItem); if (item == null || item.is_looted) { SendEquipError(InventoryResult.LootGone); @@ -5969,8 +5969,7 @@ namespace Game.Entities return; } - // questitems use the blocked field for other purposes - if (qitem == null && item.is_blocked) + if (item.is_blocked) { SendLootReleaseAll(); return; @@ -5988,31 +5987,14 @@ namespace Game.Entities if (msg == InventoryResult.Ok) { Item newitem = StoreNewItem(dest, item.itemid, true, item.randomBonusListId, item.GetAllowedLooters(), item.context, item.BonusListIDs); - if (qitem != null) + if (ffaItem != null) { - qitem.is_looted = true; - //freeforall is 1 if everyone's supposed to get the quest item. - if (item.freeforall || loot.GetPlayerQuestItems().Count == 1) - SendNotifyLootItemRemoved(loot.GetGUID(), loot.GetOwnerGUID(), lootSlot); - else - loot.NotifyQuestItemRemoved(qitem.index, GetMap()); - } - else - { - if (ffaitem != null) - { - //freeforall case, notify only one player of the removal - ffaitem.is_looted = true; - SendNotifyLootItemRemoved(loot.GetGUID(), loot.GetOwnerGUID(), lootSlot); - } - else - { - //not freeforall, notify everyone - if (conditem != null) - conditem.is_looted = true; - loot.NotifyItemRemoved(lootSlot, GetMap()); - } + //freeforall case, notify only one player of the removal + ffaItem.is_looted = true; + SendNotifyLootItemRemoved(loot.GetGUID(), loot.GetOwnerGUID(), lootSlot); } + else //not freeforall, notify everyone + loot.NotifyItemRemoved(lootSlot, GetMap()); //if only one person is supposed to loot the item, then set it to looted if (!item.freeforall) @@ -6122,7 +6104,6 @@ namespace Game.Entities Session.DoLootReleaseAll(); Loot loot; - PermissionTypes permission = PermissionTypes.All; Log.outDebug(LogFilter.Loot, "Player.SendLoot"); if (guid.IsGameObject()) @@ -6217,31 +6198,6 @@ namespace Game.Entities go.SetLootState(LootState.Activated, this); } - - if (go.GetLootState() == LootState.Activated) - { - Group group = GetGroup(); - if (group) - { - switch (loot.GetLootMethod()) - { - case LootMethod.MasterLoot: - permission = group.GetMasterLooterGuid() == GetGUID() ? PermissionTypes.Master : PermissionTypes.Restricted; - break; - case LootMethod.RoundRobin: - permission = PermissionTypes.RoundRobin; - break; - case LootMethod.FreeForAll: - permission = PermissionTypes.All; - break; - default: - permission = PermissionTypes.Group; - break; - } - } - else - permission = PermissionTypes.All; - } } else if (guid.IsItem()) { @@ -6253,8 +6209,6 @@ namespace Game.Entities return; } - permission = PermissionTypes.Owner; - loot = item.GetLootForPlayer(this); // If item doesn't already have loot, attempt to load it. If that @@ -6300,11 +6254,6 @@ namespace Game.Entities } loot = bones.GetLootForPlayer(this); - - if (bones.lootRecipient != null && bones.lootRecipient != this) - permission = PermissionTypes.None; - else - permission = PermissionTypes.Owner; } else { @@ -6343,7 +6292,6 @@ namespace Game.Entities uint a = RandomHelper.URand(0, creature.GetLevel() / 2); uint b = RandomHelper.URand(0, GetLevel() / 2); loot.gold = (uint)(10 * (a + b) * WorldConfig.GetFloatValue(WorldCfg.RateDropMoney)); - permission = PermissionTypes.Owner; } else { @@ -6373,76 +6321,38 @@ namespace Game.Entities if (loot.loot_type == LootType.Skinning) { loot_type = LootType.Skinning; - permission = creature.GetLootRecipientGUID() == GetGUID() ? PermissionTypes.Owner : PermissionTypes.None; } else if (loot_type == LootType.Skinning) { loot.Clear(); loot.FillLoot(creature.GetCreatureTemplate().SkinLootId, LootStorage.Skinning, this, true); - permission = PermissionTypes.Owner; // Set new loot recipient creature.SetLootRecipient(this, false); } - // set group rights only for loot_type != LOOT_SKINNING - else - { - if (creature.GetLootRecipientGroup()) - { - Group group = GetGroup(); - if (group == creature.GetLootRecipientGroup()) - { - switch (loot.GetLootMethod()) - { - case LootMethod.MasterLoot: - permission = PermissionTypes.Master; - break; - case LootMethod.FreeForAll: - permission = PermissionTypes.All; - break; - case LootMethod.RoundRobin: - permission = PermissionTypes.RoundRobin; - break; - default: - permission = PermissionTypes.Group; - break; - } - } - else - permission = PermissionTypes.None; - } - else if (creature.GetLootRecipient() == this) - permission = PermissionTypes.Owner; - else - permission = PermissionTypes.None; - } } } - if (permission != PermissionTypes.None) - { - if (!guid.IsItem() && !aeLooting) - SetLootGUID(guid); - LootResponse packet = new(); - packet.Owner = guid; - packet.LootObj = loot.GetGUID(); - packet.LootMethod = loot.GetLootMethod(); - packet.AcquireReason = (byte)SharedConst.GetLootTypeForClient(loot_type); - packet.Acquired = true; // false == No Loot (this too^^) - packet.AELooting = aeLooting; - loot.BuildLootResponse(packet, this, permission); - SendPacket(packet); + if (!guid.IsItem() && !aeLooting) + SetLootGUID(guid); - // add 'this' player as one of the players that are looting 'loot' - loot.OnLootOpened(GetMap(), GetGUID()); - m_AELootView[loot.GetGUID()] = loot; + LootResponse packet = new(); + packet.Owner = guid; + packet.LootObj = loot.GetGUID(); + packet.LootMethod = loot.GetLootMethod(); + packet.AcquireReason = (byte)SharedConst.GetLootTypeForClient(loot_type); + packet.Acquired = true; // false == No Loot (this too^^) + packet.AELooting = aeLooting; + loot.BuildLootResponse(packet, this); + SendPacket(packet); - if (loot_type == LootType.Corpse && !guid.IsItem()) - SetUnitFlag(UnitFlags.Looting); - } - else - SendLootError(loot != null ? loot.GetGUID() : ObjectGuid.Empty, guid, LootError.DidntKill); + // add 'this' player as one of the players that are looting 'loot' + loot.OnLootOpened(GetMap(), GetGUID()); + m_AELootView[loot.GetGUID()] = loot; + + if (loot_type == LootType.Corpse && !guid.IsItem()) + SetUnitFlag(UnitFlags.Looting); } public void SendLootError(ObjectGuid lootObj, ObjectGuid owner, LootError error) @@ -6462,12 +6372,12 @@ namespace Game.Entities SendPacket(packet); } - public void SendNotifyLootItemRemoved(ObjectGuid lootObj, ObjectGuid owner, byte lootSlot) + public void SendNotifyLootItemRemoved(ObjectGuid lootObj, ObjectGuid owner, byte lootListId) { LootRemoved packet = new(); packet.LootObj = lootObj; packet.Owner = owner; - packet.LootListID = (byte)(lootSlot + 1); + packet.LootListID = lootListId; SendPacket(packet); } diff --git a/Source/Game/Entities/Player/Player.cs b/Source/Game/Entities/Player/Player.cs index 742740ed4..721ebf9c6 100644 --- a/Source/Game/Entities/Player/Player.cs +++ b/Source/Game/Entities/Player/Player.cs @@ -3138,7 +3138,6 @@ namespace Game.Entities { case LootMethod.PersonalLoot:// @todo implement personal loot (http://wow.gamepedia.com/Loot#Personal_Loot) return false; - case LootMethod.MasterLoot: case LootMethod.FreeForAll: return true; case LootMethod.RoundRobin: @@ -3148,10 +3147,11 @@ namespace Game.Entities return true; return loot.HasItemFor(this); + case LootMethod.MasterLoot: case LootMethod.GroupLoot: case LootMethod.NeedBeforeGreed: // may only loot if the player is the loot roundrobin player - // or item over threshold (so roll(s) can be launched) + // or item over threshold (so roll(s) can be launched or to preview master looted items) // or if there are free/quest/conditional item for the player if (loot.roundRobinPlayer.IsEmpty() || loot.roundRobinPlayer == GetGUID()) return true; diff --git a/Source/Game/Handlers/LootHandler.cs b/Source/Game/Handlers/LootHandler.cs index 503717d04..8436d660f 100644 --- a/Source/Game/Handlers/LootHandler.cs +++ b/Source/Game/Handlers/LootHandler.cs @@ -75,7 +75,7 @@ namespace Game } } - player.StoreLootItem(lguid, (byte)(req.LootListID - 1), loot, aeResult); + player.StoreLootItem(lguid, req.LootListID, loot, aeResult); // If player is removing the last LootItem, delete the empty container. if (loot.IsLooted() && lguid.IsItem()) @@ -412,14 +412,13 @@ namespace Game if (loot == null || loot.GetLootMethod() != LootMethod.MasterLoot) return; - byte slotid = (byte)(req.LootListID - 1); - if (slotid >= loot.items.Count + loot.quest_items.Count) + if (req.LootListID >= loot.items.Count) { - Log.outDebug(LogFilter.Loot, $"MasterLootItem: Player {GetPlayer().GetName()} might be using a hack! (slot {slotid}, size {loot.items.Count})"); + Log.outDebug(LogFilter.Loot, $"MasterLootItem: Player {GetPlayer().GetName()} might be using a hack! (slot {req.LootListID}, size {loot.items.Count})"); return; } - LootItem item = slotid >= loot.items.Count ? loot.quest_items[slotid - loot.items.Count] : loot.items[slotid]; + LootItem item = loot.items[req.LootListID]; List dest = new(); InventoryResult msg = target.CanStoreNewItem(ItemConst.NullBag, ItemConst.NullSlot, dest, item.itemid, item.count); @@ -445,7 +444,7 @@ namespace Game item.count = 0; item.is_looted = true; - loot.NotifyItemRemoved(slotid, GetPlayer().GetMap()); + loot.NotifyItemRemoved(req.LootListID, GetPlayer().GetMap()); --loot.unlootedCount; } @@ -461,7 +460,7 @@ namespace Game [WorldPacketHandler(ClientOpcodes.LootRoll)] void HandleLootRoll(LootRollPacket packet) { - LootRoll lootRoll = GetPlayer().GetLootRoll(packet.LootObj, (byte)(packet.LootListID - 1)); + LootRoll lootRoll = GetPlayer().GetLootRoll(packet.LootObj, packet.LootListID); if (lootRoll == null) return; diff --git a/Source/Game/Loot/Loot.cs b/Source/Game/Loot/Loot.cs index c5d1d41f0..32a5ac73f 100644 --- a/Source/Game/Loot/Loot.cs +++ b/Source/Game/Loot/Loot.cs @@ -108,6 +108,73 @@ namespace Game.Loots allowedGUIDs.Add(player.GetGUID()); } + public LootSlotType? GetUiTypeForPlayer(Player player, Loot loot) + { + if (is_looted) + return null; + + if (!allowedGUIDs.Contains(player.GetGUID())) + return null; + + if (freeforall) + { + var ffaItems = loot.GetPlayerFFAItems().LookupByKey(player.GetGUID()); + if (ffaItems != null) + { + var ffaItemItr = ffaItems.Find(ffaItem => ffaItem.LootListId == LootListId); + if (ffaItemItr != null && !ffaItemItr.is_looted) + return loot.GetLootMethod() == LootMethod.FreeForAll ? LootSlotType.Owner : LootSlotType.AllowLoot; + } + return null; + } + + if (needs_quest && !follow_loot_rules) + return loot.GetLootMethod() == LootMethod.FreeForAll ? LootSlotType.Owner : LootSlotType.AllowLoot; + + switch (loot.GetLootMethod()) + { + case LootMethod.FreeForAll: + return LootSlotType.Owner; + case LootMethod.RoundRobin: + if (!loot.roundRobinPlayer.IsEmpty() && loot.roundRobinPlayer != player.GetGUID()) + return null; + + return LootSlotType.AllowLoot; + case LootMethod.MasterLoot: + if (is_underthreshold) + { + if (!loot.roundRobinPlayer.IsEmpty() && loot.roundRobinPlayer != player.GetGUID()) + return null; + + return LootSlotType.AllowLoot; + } + + return loot.GetLootMasterGUID() == player.GetGUID() ? LootSlotType.Master : LootSlotType.Locked; + case LootMethod.GroupLoot: + case LootMethod.NeedBeforeGreed: + if (is_underthreshold) + if (!loot.roundRobinPlayer.IsEmpty() && loot.roundRobinPlayer != player.GetGUID()) + return null; + + if (is_blocked) + return LootSlotType.RollOngoing; + + if (rollWinnerGUID.IsEmpty()) // all passed + return LootSlotType.AllowLoot; + + if (rollWinnerGUID == player.GetGUID()) + return LootSlotType.Owner; + + return null; + case LootMethod.PersonalLoot: + return LootSlotType.Owner; + default: + break; + } + + return null; + } + public List GetAllowedLooters() { return allowedGUIDs; } public uint itemid; @@ -130,18 +197,18 @@ namespace Game.Loots public class NotNormalLootItem { - public byte index; // position in quest_items or items; + public byte LootListId; // position in quest_items or items; public bool is_looted; public NotNormalLootItem() { - index = 0; + LootListId = 0; is_looted = false; } public NotNormalLootItem(byte _index, bool _islooted = false) { - index = _index; + LootListId = _index; is_looted = _islooted; } } @@ -167,7 +234,6 @@ namespace Game.Loots bool m_isStarted; LootItem m_lootItem; Loot m_loot; - uint m_lootListId; RollMask m_voteMask; DateTime m_endTime = DateTime.MinValue; @@ -347,7 +413,7 @@ namespace Game.Loots void FillPacket(LootItemData lootItem) { lootItem.Quantity = m_lootItem.count; - lootItem.LootListID = (byte)(m_lootListId + 1); + lootItem.LootListID = (byte)m_lootItem.LootListId; lootItem.CanTradeToTapList = m_lootItem.allowedGUIDs.Count > 1; lootItem.Loot = new(m_lootItem); } @@ -367,7 +433,6 @@ namespace Game.Loots m_lootItem = loot.items[(int)lootListId]; m_loot = loot; - m_lootListId = lootListId; m_lootItem.is_blocked = true; // block the item while rolling uint playerCount = 0; @@ -469,7 +534,7 @@ namespace Game.Loots public bool IsLootItem(ObjectGuid lootObject, uint lootListId) { - return m_loot.GetGUID() == lootObject && m_lootListId == lootListId; + return m_loot.GetGUID() == lootObject && m_lootItem.LootListId == lootListId; } /** @@ -560,8 +625,7 @@ namespace Game.Loots loot.FillLoot(disenchant.Id, LootStorage.Disenchant, player, true, false, LootModes.Default, ItemContext.None); if (!loot.AutoStore(player, ItemConst.NullBag, ItemConst.NullSlot, true)) { - uint maxSlot = loot.GetMaxSlotInLootFor(player); - for (uint i = 0; i < maxSlot; ++i) + for (uint i = 0; i < loot.items.Count; ++i) { LootItem disenchantLoot = loot.LootItemInSlot(i, player); if (disenchantLoot != null) @@ -572,7 +636,7 @@ namespace Game.Loots m_loot.NotifyItemRemoved((byte)m_lootItem.LootListId, m_map); } else - player.StoreLootItem(m_loot.GetOwnerGUID(), (byte)m_lootListId, m_loot); + player.StoreLootItem(m_loot.GetOwnerGUID(), (byte)m_lootItem.LootListId, m_loot); } } m_isStarted = false; @@ -593,7 +657,7 @@ namespace Game.Loots } // Inserts the item into the loot (called by LootTemplate processors) - public void AddItem(LootStoreItem item, Player player) + public void AddItem(LootStoreItem item) { ItemTemplate proto = Global.ObjectMgr.GetItemTemplate(item.itemid); if (proto == null) @@ -602,68 +666,36 @@ namespace Game.Loots uint count = RandomHelper.URand(item.mincount, item.maxcount); uint stacks = (uint)(count / proto.GetMaxStackSize() + (Convert.ToBoolean(count % proto.GetMaxStackSize()) ? 1 : 0)); - List lootItems = item.needs_quest ? quest_items : items; - uint limit = (uint)(item.needs_quest ? SharedConst.MaxNRQuestItems : SharedConst.MaxNRLootItems); - - for (uint i = 0; i < stacks && lootItems.Count < limit; ++i) + for (uint i = 0; i < stacks && items.Count < SharedConst.MaxNRLootItems; ++i) { LootItem generatedLoot = new(item); generatedLoot.context = _itemContext; generatedLoot.count = (byte)Math.Min(count, proto.GetMaxStackSize()); - generatedLoot.LootListId = (uint)lootItems.Count; + generatedLoot.LootListId = (uint)items.Count; if (_itemContext != 0) { List bonusListIDs = Global.DB2Mgr.GetDefaultItemBonusTree(generatedLoot.itemid, _itemContext); generatedLoot.BonusListIDs.AddRange(bonusListIDs); } - lootItems.Add(generatedLoot); + + items.Add(generatedLoot); count -= proto.GetMaxStackSize(); - - // In some cases, a dropped item should be visible/lootable only for some players in group - bool canSeeItemInLootWindow = false; - Group group = player.GetGroup(); - if (group != null) - { - for (GroupReference itr = group.GetFirstMember(); itr != null; itr = itr.Next()) - { - Player member = itr.GetSource(); - if (member != null) - if (generatedLoot.AllowedForPlayer(member)) - canSeeItemInLootWindow = true; - } - } - else if (generatedLoot.AllowedForPlayer(player)) - canSeeItemInLootWindow = true; - - if (!canSeeItemInLootWindow) - continue; - - // non-conditional one-player only items are counted here, - // free for all items are counted in FillFFALoot(), - // non-ffa conditionals are counted in FillNonQuestNonFFAConditionalLoot() - if (!item.needs_quest && item.conditions.Empty() && !proto.HasFlag(ItemFlags.MultiDrop)) - ++unlootedCount; } } public bool AutoStore(Player player, byte bag, byte slot, bool broadcast, bool createdByPlayer = false) { bool allLooted = true; - uint max_slot = GetMaxSlotInLootFor(player); - for (uint i = 0; i < max_slot; ++i) + for (uint i = 0; i < items.Count; ++i) { - NotNormalLootItem qitem = null; - NotNormalLootItem ffaitem = null; - NotNormalLootItem conditem = null; - - LootItem lootItem = LootItemInSlot(i, player, out qitem, out ffaitem, out conditem); + LootItem lootItem = LootItemInSlot(i, player, out NotNormalLootItem ffaitem); if (lootItem == null || lootItem.is_looted) continue; if (!lootItem.AllowedForPlayer(player)) continue; - if (qitem == null && lootItem.is_blocked) + if (lootItem.is_blocked) continue; // dont allow protected item to be looted by someone else @@ -683,12 +715,8 @@ namespace Game.Loots continue; } - if (qitem != null) - qitem.is_looted = true; - else if (ffaitem != null) + if (ffaitem != null) ffaitem.is_looted = true; - else if (conditem != null) - conditem.is_looted = true; if (!lootItem.freeforall) lootItem.is_looted = true; @@ -703,14 +731,10 @@ namespace Game.Loots return allLooted; } - public LootItem GetItemInSlot(uint lootSlot) + public LootItem GetItemInSlot(uint lootListId) { - if (lootSlot < items.Count) - return items[(int)lootSlot]; - - lootSlot -= (uint)items.Count; - if (lootSlot < quest_items.Count) - return quest_items[(int)lootSlot]; + if (lootListId < items.Count) + return items[(int)lootListId]; return null; } @@ -732,7 +756,7 @@ namespace Game.Loots _itemContext = context; - tab.Process(this, store.IsRatesAllowed(), (byte)lootMode, 0, lootOwner); // Processing is done there, callback via Loot.AddItem() + tab.Process(this, store.IsRatesAllowed(), (byte)lootMode, 0); // Processing is done there, callback via Loot.AddItem() // Setting access rights for group loot case Group group = lootOwner.GetGroup(); @@ -748,8 +772,11 @@ namespace Game.Loots FillNotNormalLootFor(player); } - void processLootItem(LootItem item) + foreach (LootItem item in items) { + if (!item.follow_loot_rules || item.freeforall) + continue; + ItemTemplate proto = Global.ObjectMgr.GetItemTemplate(item.itemid); if (proto != null) { @@ -771,22 +798,6 @@ namespace Game.Loots } } } - }; - - foreach (LootItem item in items) - { - if (item.freeforall) - continue; - - processLootItem(item); - } - - foreach (LootItem item in quest_items) - { - if (!item.follow_loot_rules) - continue; - - processLootItem(item); } } // ... for personal loot @@ -808,142 +819,43 @@ namespace Game.Loots ObjectGuid plguid = player.GetGUID(); _allowedLooters.Add(plguid); - var questItemList = PlayerQuestItems.LookupByKey(plguid); - if (questItemList.Empty()) - FillQuestLoot(player); + List ffaItems = new(); - questItemList = PlayerFFAItems.LookupByKey(plguid); - if (questItemList.Empty()) - FillFFALoot(player); - - questItemList = PlayerNonQuestNonFFAConditionalItems.LookupByKey(plguid); - if (questItemList.Empty()) - FillNonQuestNonFFAConditionalLoot(player); - } - - List FillFFALoot(Player player) - { - List ql = new(); - - for (byte i = 0; i < items.Count; ++i) + foreach (LootItem item in items) { - LootItem item = items[i]; - if (!item.is_looted && item.freeforall && item.AllowedForPlayer(player)) + if (!item.AllowedForPlayer(player)) + continue; + + if (item.freeforall) { - ql.Add(new NotNormalLootItem(i)); + ffaItems.Add(new NotNormalLootItem((byte)item.LootListId)); + ++unlootedCount; + } + + else if (!item.is_counted) + { + item.is_counted = true; ++unlootedCount; } } - if (ql.Empty()) - return null; - - PlayerFFAItems[player.GetGUID()] = ql; - return ql; + if (!ffaItems.Empty()) + PlayerFFAItems[player.GetGUID()] = ffaItems; } - List FillQuestLoot(Player player) - { - if (items.Count == SharedConst.MaxNRLootItems) - return null; - - List ql = new(); - - for (byte i = 0; i < quest_items.Count; ++i) - { - LootItem item = quest_items[i]; - - if (!item.is_looted && (item.AllowedForPlayer(player) || (item.follow_loot_rules && player.GetGroup() && ((GetLootMethod() == LootMethod.MasterLoot && player.GetGroup().GetMasterLooterGuid() == player.GetGUID()) || GetLootMethod() != LootMethod.MasterLoot)))) - { - item.AddAllowedLooter(player); - - ql.Add(new NotNormalLootItem(i)); - - // quest items get blocked when they first appear in a - // player's quest vector - // - // increase once if one looter only, looter-times if free for all - if (item.freeforall || !item.is_blocked) - ++unlootedCount; - if (!player.GetGroup() || GetLootMethod() != LootMethod.GroupLoot && GetLootMethod() != LootMethod.RoundRobin) - item.is_blocked = true; - - if (items.Count + ql.Count == SharedConst.MaxNRLootItems) - break; - } - } - if (ql.Empty()) - return null; - - PlayerQuestItems[player.GetGUID()] = ql; - return ql; - } - - List FillNonQuestNonFFAConditionalLoot(Player player) - { - List ql = new(); - - for (byte i = 0; i < items.Count; ++i) - { - LootItem item = items[i]; - if (!item.is_looted && !item.freeforall && item.AllowedForPlayer(player)) - { - item.AddAllowedLooter(player); - if (!item.conditions.Empty()) - { - ql.Add(new NotNormalLootItem(i)); - if (!item.is_counted) - { - ++unlootedCount; - item.is_counted = true; - } - } - } - } - if (ql.Empty()) - return null; - - PlayerNonQuestNonFFAConditionalItems[player.GetGUID()] = ql; - return ql; - } - - public void NotifyItemRemoved(byte lootIndex, Map map) + public void NotifyItemRemoved(byte lootListId, Map map) { // notify all players that are looting this that the item was removed // convert the index to the slot the player sees for (int i = 0; i < PlayersLooting.Count; ++i) { - Player player = Global.ObjAccessor.GetPlayer(map, PlayersLooting[i]); - if (player != null) - player.SendNotifyLootItemRemoved(GetGUID(), GetOwnerGUID(), lootIndex); - else - PlayersLooting.RemoveAt(i); - } - } + LootItem item = items[lootListId]; + if (!item.GetAllowedLooters().Contains(PlayersLooting[i])) + continue; - public void NotifyQuestItemRemoved(byte questIndex, Map map) - { - // when a free for all questitem is looted - // all players will get notified of it being removed - // (other questitems can be looted by each group member) - // bit inefficient but isn't called often - for (var i = 0; i < PlayersLooting.Count; ++i) - { Player player = Global.ObjAccessor.GetPlayer(map, PlayersLooting[i]); if (player) - { - var pql = PlayerQuestItems.LookupByKey(player.GetGUID()); - if (!pql.Empty()) - { - byte j; - for (j = 0; j < pql.Count; ++j) - if (pql[j].index == questIndex) - break; - - if (j < pql.Count) - player.SendNotifyLootItemRemoved(GetGUID(), GetOwnerGUID(), (byte)(items.Count + j)); - } - } + player.SendNotifyLootItemRemoved(GetGUID(), GetOwnerGUID(), lootListId); else PlayersLooting.RemoveAt(i); } @@ -979,8 +891,7 @@ namespace Game.Loots maxEnchantingSkill = Math.Max(maxEnchantingSkill, allowedLooter.GetSkillValue(SkillType.Enchanting)); } - uint lootListId = 0; - for (; lootListId < items.Count; ++lootListId) + for (uint lootListId = 0; lootListId < items.Count; ++lootListId) { LootItem item = items[(int)lootListId]; if (!item.is_blocked) @@ -991,18 +902,6 @@ namespace Game.Loots if (!lootRoll.TryToStart(map, this, lootListId, maxEnchantingSkill)) _rolls.Remove(lootListId); } - - for (; lootListId - items.Count < quest_items.Count; ++lootListId) - { - LootItem item = quest_items[(int)lootListId - items.Count]; - if (!item.is_blocked) - continue; - - LootRoll lootRoll = new(); - var inserted = _rolls.TryAdd(lootListId, lootRoll); - if (!lootRoll.TryToStart(map, this, lootListId, maxEnchantingSkill)) - _rolls.Remove(lootListId); - } } else if (_lootMethod == LootMethod.MasterLoot) { @@ -1036,66 +935,27 @@ namespace Game.Loots public LootItem LootItemInSlot(uint lootSlot, Player player) { - return LootItemInSlot(lootSlot, player, out _, out _, out _); + return LootItemInSlot(lootSlot, player, out _); } - public LootItem LootItemInSlot(uint lootSlot, Player player, out NotNormalLootItem qitem, out NotNormalLootItem ffaitem, out NotNormalLootItem conditem) + public LootItem LootItemInSlot(uint lootListId, Player player, out NotNormalLootItem ffaItem) { - qitem = null; - ffaitem = null; - conditem = null; + ffaItem = null; - LootItem item = null; - bool is_looted = true; - if (lootSlot >= items.Count) + LootItem item = items[(int)lootListId]; + bool is_looted = item.is_looted; + + if (item.freeforall) { - int questSlot = (int)(lootSlot - items.Count); - var questItems = PlayerQuestItems.LookupByKey(player.GetGUID()); - if (!questItems.Empty()) + var itemList = PlayerFFAItems.LookupByKey(player.GetGUID()); + if (itemList != null) { - NotNormalLootItem qitem2 = questItems[questSlot]; - if (qitem2 != null) + foreach (NotNormalLootItem notNormalLootItem in itemList) { - qitem = qitem2; - item = quest_items[qitem2.index]; - is_looted = qitem2.is_looted; - } - } - } - else - { - item = items[(int)lootSlot]; - is_looted = item.is_looted; - if (item.freeforall) - { - var questItemList = PlayerFFAItems.LookupByKey(player.GetGUID()); - if (!questItemList.Empty()) - { - foreach (var c in questItemList) + if (notNormalLootItem.LootListId == lootListId) { - if (c.index == lootSlot) - { - NotNormalLootItem ffaitem2 = c; - ffaitem = ffaitem2; - is_looted = ffaitem2.is_looted; - break; - } - } - } - } - else if (!item.conditions.Empty()) - { - var questItemList = PlayerNonQuestNonFFAConditionalItems.LookupByKey(player.GetGUID()); - if (!questItemList.Empty()) - { - foreach (var iter in questItemList) - { - if (iter.index == lootSlot) - { - NotNormalLootItem conditem2 = iter; - conditem = conditem2; - is_looted = conditem2.is_looted; - break; - } + is_looted = notNormalLootItem.is_looted; + ffaItem = notNormalLootItem; + break; } } } @@ -1107,12 +967,6 @@ namespace Game.Loots return item; } - public uint GetMaxSlotInLootFor(Player player) - { - var questItemList = PlayerQuestItems.LookupByKey(player.GetGUID()); - return (uint)(items.Count + questItemList.Count); - } - // return true if there is any item that is lootable for any player (not quest item, FFA or conditional) public bool HasItemForAll() { @@ -1121,7 +975,7 @@ namespace Game.Loots return true; foreach (LootItem item in items) - if (!item.is_looted && !item.freeforall && item.conditions.Empty()) + if (!item.is_looted && item.follow_loot_rules && !item.freeforall && item.conditions.Empty()) return true; return false; @@ -1130,40 +984,17 @@ namespace Game.Loots // return true if there is any FFA, quest or conditional item for the player. public bool HasItemFor(Player player) { - var lootPlayerQuestItems = GetPlayerQuestItems(); - var q_list = lootPlayerQuestItems.LookupByKey(player.GetGUID()); - if (!q_list.Empty()) - { - foreach (var qi in q_list) - { - LootItem item = quest_items[qi.index]; - if (!qi.is_looted && !item.is_looted) - return true; - } - } + // quest items + foreach (LootItem lootItem in items) + if (!lootItem.is_looted && !lootItem.follow_loot_rules && lootItem.GetAllowedLooters().Contains(player.GetGUID())) + return true; - var lootPlayerFFAItems = GetPlayerFFAItems(); - var ffa_list = lootPlayerFFAItems.LookupByKey(player.GetGUID()); - if (!ffa_list.Empty()) + var ffaItems = GetPlayerFFAItems().LookupByKey(player.GetGUID()); + if (ffaItems != null) { - foreach (var fi in ffa_list) - { - LootItem item = items[fi.index]; - if (!fi.is_looted && !item.is_looted) - return true; - } - } - - var lootPlayerNonQuestNonFFAConditionalItems = GetPlayerNonQuestNonFFAConditionalItems(); - var conditional_list = lootPlayerNonQuestNonFFAConditionalItems.LookupByKey(player.GetGUID()); - if (!conditional_list.Empty()) - { - foreach (var ci in conditional_list) - { - LootItem item = items[ci.index]; - if (!ci.is_looted && !item.is_looted) - return true; - } + bool hasFfaItem = ffaItems.Any(ffaItem => !ffaItem.is_looted); + if (hasFfaItem) + return true; } return false; @@ -1181,231 +1012,29 @@ namespace Game.Loots return false; } - public void BuildLootResponse(LootResponse packet, Player viewer, PermissionTypes permission) + public void BuildLootResponse(LootResponse packet, Player viewer) { - if (permission == PermissionTypes.None) - return; - packet.Coins = gold; - switch (permission) + foreach (LootItem item in items) { - case PermissionTypes.Group: - case PermissionTypes.Master: - case PermissionTypes.Restricted: - { - // if you are not the round-robin group looter, you can only see - // blocked rolled items and quest items, and !ffa items - for (byte i = 0; i < items.Count; ++i) - { - if (!items[i].is_looted && !items[i].freeforall && items[i].conditions.Empty() && items[i].AllowedForPlayer(viewer)) - { - LootSlotType slot_type; + var uiType = item.GetUiTypeForPlayer(viewer, this); + if (!uiType.HasValue) + continue; - if (items[i].is_blocked) // for ML & restricted is_blocked = !is_underthreshold - { - switch (permission) - { - case PermissionTypes.Group: - slot_type = LootSlotType.RollOngoing; - break; - case PermissionTypes.Master: - { - if (viewer.GetGroup() && viewer.GetGroup().GetMasterLooterGuid() == viewer.GetGUID()) - slot_type = LootSlotType.Master; - else - slot_type = LootSlotType.Locked; - break; - } - case PermissionTypes.Restricted: - slot_type = LootSlotType.Locked; - break; - default: - continue; - } - } - else if (roundRobinPlayer.IsEmpty() || viewer.GetGUID() == roundRobinPlayer || !items[i].is_underthreshold) - { - // no round robin owner or he has released the loot - // or it IS the round robin group owner - // => item is lootable - slot_type = LootSlotType.AllowLoot; - } - else if (!items[i].rollWinnerGUID.IsEmpty()) - { - if (items[i].rollWinnerGUID == viewer.GetGUID()) - slot_type = LootSlotType.Owner; - else - continue; - } - else - // item shall not be displayed. - continue; - - LootItemData lootItem = new(); - lootItem.LootListID = (byte)(i + 1); - lootItem.UIType = slot_type; - lootItem.Quantity = items[i].count; - lootItem.Loot = new ItemInstance(items[i]); - packet.Items.Add(lootItem); - } - } - break; - } - case PermissionTypes.RoundRobin: - { - for (var i = 0; i < items.Count; ++i) - { - if (!items[i].is_looted && !items[i].freeforall && items[i].conditions.Empty() && items[i].AllowedForPlayer(viewer)) - { - if (!roundRobinPlayer.IsEmpty() && viewer.GetGUID() != roundRobinPlayer) - // item shall not be displayed. - continue; - - LootItemData lootItem = new(); - lootItem.LootListID = (byte)(i + 1); - lootItem.UIType = LootSlotType.AllowLoot; - lootItem.Quantity = items[i].count; - lootItem.Loot = new(items[i]); - packet.Items.Add(lootItem); - } - } - break; - } - case PermissionTypes.All: - case PermissionTypes.Owner: - { - for (byte i = 0; i < items.Count; ++i) - { - if (!items[i].is_looted && !items[i].freeforall && items[i].conditions.Empty() && items[i].AllowedForPlayer(viewer)) - { - LootItemData lootItem = new(); - lootItem.LootListID = (byte)(i + 1); - lootItem.UIType = (permission == PermissionTypes.Owner ? LootSlotType.Owner : LootSlotType.AllowLoot); - lootItem.Quantity = items[i].count; - lootItem.Loot = new ItemInstance(items[i]); - packet.Items.Add(lootItem); - } - } - break; - } - default: - return; - } - - LootSlotType slotType = permission == PermissionTypes.Owner ? LootSlotType.Owner : LootSlotType.AllowLoot; - var lootPlayerQuestItems = GetPlayerQuestItems(); - var q_list = lootPlayerQuestItems.LookupByKey(viewer.GetGUID()); - if (!q_list.Empty()) - { - for (var i = 0; i < q_list.Count; ++i) - { - NotNormalLootItem qi = q_list[i]; - LootItem item = quest_items[qi.index]; - if (!qi.is_looted && !item.is_looted) - { - LootItemData lootItem = new(); - lootItem.LootListID = (byte)(items.Count + i + 1); - lootItem.Quantity = item.count; - lootItem.Loot = new ItemInstance(item); - - switch (permission) - { - case PermissionTypes.Master: - lootItem.UIType = LootSlotType.Master; - break; - case PermissionTypes.Restricted: - lootItem.UIType = item.is_blocked ? LootSlotType.Locked : LootSlotType.AllowLoot; - break; - case PermissionTypes.Group: - case PermissionTypes.RoundRobin: - if (!item.is_blocked) - lootItem.UIType = LootSlotType.AllowLoot; - else - lootItem.UIType = LootSlotType.RollOngoing; - break; - default: - lootItem.UIType = slotType; - break; - } - - packet.Items.Add(lootItem); - } - } - } - - var lootPlayerFFAItems = GetPlayerFFAItems(); - var ffa_list = lootPlayerFFAItems.LookupByKey(viewer.GetGUID()); - if (!ffa_list.Empty()) - { - foreach (var fi in ffa_list) - { - LootItem item = items[fi.index]; - if (!fi.is_looted && !item.is_looted) - { - LootItemData lootItem = new(); - lootItem.LootListID = (byte)(fi.index + 1); - lootItem.UIType = slotType; - lootItem.Quantity = item.count; - lootItem.Loot = new ItemInstance(item); - packet.Items.Add(lootItem); - } - } - } - - var lootPlayerNonQuestNonFFAConditionalItems = GetPlayerNonQuestNonFFAConditionalItems(); - var conditional_list = lootPlayerNonQuestNonFFAConditionalItems.LookupByKey(viewer.GetGUID()); - if (!conditional_list.Empty()) - { - foreach (var ci in conditional_list) - { - LootItem item = items[ci.index]; - if (!ci.is_looted && !item.is_looted) - { - LootItemData lootItem = new(); - lootItem.LootListID = (byte)(ci.index + 1); - lootItem.Quantity = item.count; - lootItem.Loot = new ItemInstance(item); - - if (item.follow_loot_rules) - { - switch (permission) - { - case PermissionTypes.Master: - lootItem.UIType = LootSlotType.Master; - break; - case PermissionTypes.Restricted: - lootItem.UIType = item.is_blocked ? LootSlotType.Locked : LootSlotType.AllowLoot; - break; - case PermissionTypes.Group: - case PermissionTypes.RoundRobin: - if (!item.is_blocked) - lootItem.UIType = LootSlotType.AllowLoot; - else - lootItem.UIType = LootSlotType.RollOngoing; - break; - default: - lootItem.UIType = slotType; - break; - } - } - else - lootItem.UIType = slotType; - - packet.Items.Add(lootItem); - } - } + LootItemData lootItem = new(); + lootItem.LootListID = (byte)item.LootListId; + lootItem.UIType = uiType.Value; + lootItem.Quantity = item.count; + lootItem.Loot = new(item); + packet.Items.Add(lootItem); } } public void Clear() { - PlayerQuestItems.Clear(); - PlayerFFAItems.Clear(); - PlayerNonQuestNonFFAConditionalItems.Clear(); - foreach (ObjectGuid playerGuid in PlayersLooting) { Player player = Global.ObjAccessor.FindConnectedPlayer(playerGuid); @@ -1415,7 +1044,6 @@ namespace Game.Loots PlayersLooting.Clear(); items.Clear(); - quest_items.Clear(); gold = 0; unlootedCount = 0; roundRobinPlayer = ObjectGuid.Empty; @@ -1458,12 +1086,9 @@ namespace Game.Loots public ObjectGuid GetLootMasterGUID() { return _lootMaster; } - public MultiMap GetPlayerQuestItems() { return PlayerQuestItems; } public MultiMap GetPlayerFFAItems() { return PlayerFFAItems; } - public MultiMap GetPlayerNonQuestNonFFAConditionalItems() { return PlayerNonQuestNonFFAConditionalItems; } public List items = new(); - public List quest_items = new(); public uint gold; public byte unlootedCount; public ObjectGuid roundRobinPlayer; // GUID of the player having the Round-Robin ownership for the loot. If 0, round robin owner has released. @@ -1471,9 +1096,7 @@ namespace Game.Loots public byte maxDuplicates; // Max amount of items with the same entry that can drop (default is 1; on 25 man raid mode 3) List PlayersLooting = new(); - MultiMap PlayerQuestItems = new(); MultiMap PlayerFFAItems = new(); - MultiMap PlayerNonQuestNonFFAConditionalItems = new(); // Loot GUID ObjectGuid _guid; diff --git a/Source/Game/Loot/LootManager.cs b/Source/Game/Loot/LootManager.cs index 02dee4d74..af7a1ac8e 100644 --- a/Source/Game/Loot/LootManager.cs +++ b/Source/Game/Loot/LootManager.cs @@ -707,7 +707,7 @@ namespace Game.Loots Entries.Add(item); } - public void Process(Loot loot, bool rate, ushort lootMode, byte groupId, Player player) + public void Process(Loot loot, bool rate, ushort lootMode, byte groupId) { if (groupId != 0) // Group reference uses own processing of the group { @@ -717,7 +717,7 @@ namespace Game.Loots if (Groups[groupId - 1] == null) return; - Groups[groupId - 1].Process(loot, lootMode, player); + Groups[groupId - 1].Process(loot, lootMode); return; } @@ -738,17 +738,17 @@ namespace Game.Loots uint maxcount = (uint)(item.maxcount * WorldConfig.GetFloatValue(WorldCfg.RateDropItemReferencedAmount)); for (uint loop = 0; loop < maxcount; ++loop) // Ref multiplicator - Referenced.Process(loot, rate, lootMode, item.groupid, player); + Referenced.Process(loot, rate, lootMode, item.groupid); } else // Plain entries (not a reference, not grouped) - loot.AddItem(item, player); // Chance is already checked, just add + loot.AddItem(item); // Chance is already checked, just add } // Now processing groups foreach (var group in Groups.Values) { if (group != null) - group.Process(loot, lootMode, player); + group.Process(loot, lootMode); } } public void CopyConditions(List conditions) @@ -971,11 +971,11 @@ namespace Game.Loots return false; } - public void Process(Loot loot, ushort lootMode, Player player) + public void Process(Loot loot, ushort lootMode) { LootStoreItem item = Roll(loot, lootMode); if (item != null) - loot.AddItem(item, player); + loot.AddItem(item); } float RawTotalChance() { diff --git a/Source/Game/Mails/MailDraft.cs b/Source/Game/Mails/MailDraft.cs index adc354f0e..de7b49295 100644 --- a/Source/Game/Mails/MailDraft.cs +++ b/Source/Game/Mails/MailDraft.cs @@ -65,8 +65,7 @@ namespace Game.Mails // can be empty mailLoot.FillLoot(m_mailTemplateId, LootStorage.Mail, receiver, true, true, LootModes.Default, ItemContext.None); - uint max_slot = mailLoot.GetMaxSlotInLootFor(receiver); - for (uint i = 0; m_items.Count < SharedConst.MaxMailItems && i < max_slot; ++i) + for (uint i = 0; m_items.Count < SharedConst.MaxMailItems && i < mailLoot.items.Count; ++i) { LootItem lootitem = mailLoot.LootItemInSlot(i, receiver); if (lootitem != null)