From e78c71784cff5d990009c5fd76eb16d90cb4a8d3 Mon Sep 17 00:00:00 2001 From: hondacrx Date: Tue, 18 Oct 2022 14:22:16 -0400 Subject: [PATCH] Core/Loot: Initial support for personal loot in gameobjects (non-instanced content) Port From (https://github.com/TrinityCore/TrinityCore/commit/641390dca2329041a6ef513c2c9f7b28d42d2762) --- .../Game/Chat/Commands/GameObjectCommands.cs | 2 + Source/Game/Entities/GameObject/GameObject.cs | 93 +++++++++++++++---- Source/Game/Globals/ObjectManager.cs | 10 +- Source/Game/Handlers/LootHandler.cs | 4 +- Source/Game/Loot/Loot.cs | 2 +- Source/Game/Loot/LootManager.cs | 24 +++-- 6 files changed, 103 insertions(+), 32 deletions(-) diff --git a/Source/Game/Chat/Commands/GameObjectCommands.cs b/Source/Game/Chat/Commands/GameObjectCommands.cs index ccc1d150c..77896b5f9 100644 --- a/Source/Game/Chat/Commands/GameObjectCommands.cs +++ b/Source/Game/Chat/Commands/GameObjectCommands.cs @@ -151,6 +151,8 @@ namespace Game.Chat uint displayId = gameObjectInfo.displayId; string name = gameObjectInfo.name; uint lootId = gameObjectInfo.GetLootId(); + if (type == GameObjectTypes.Chest && lootId == 0) + lootId = gameObjectInfo.Chest.chestPersonalLoot; // If we have a real object, send some info about it if (thisGO != null) diff --git a/Source/Game/Entities/GameObject/GameObject.cs b/Source/Game/Entities/GameObject/GameObject.cs index d5549aa2d..455851f16 100644 --- a/Source/Game/Entities/GameObject/GameObject.cs +++ b/Source/Game/Entities/GameObject/GameObject.cs @@ -482,6 +482,8 @@ namespace Game.Entities m_lootState = LootState.Ready; loot = null; m_personalLoot.Clear(); + m_unique_users.Clear(); + m_usetimes = 0; AddToObjectUpdateIfNeeded(); break; default: @@ -711,6 +713,8 @@ namespace Game.Entities m_lootState = LootState.Ready; loot = null; m_personalLoot.Clear(); + m_unique_users.Clear(); + m_usetimes = 0; AddToObjectUpdateIfNeeded(); } break; @@ -797,6 +801,8 @@ namespace Game.Entities loot = null; m_personalLoot.Clear(); + m_unique_users.Clear(); + m_usetimes = 0; // Do not delete chests or goobers that are not consumed on loot, while still allowing them to despawn when they expire if summoned bool isSummonedAndExpired = (GetOwner() != null || GetSpellId() != 0) && m_respawnTime == 0; @@ -1346,7 +1352,10 @@ namespace Game.Entities return false; // scan GO chest with loot including quest items - if (target.GetQuestStatus(GetGoInfo().Chest.questID) == QuestStatus.Incomplete || LootStorage.Gameobject.HaveQuestLootForPlayer(GetGoInfo().GetLootId(), target)) + if (target.GetQuestStatus(GetGoInfo().Chest.questID) == QuestStatus.Incomplete + || LootStorage.Gameobject.HaveQuestLootForPlayer(GetGoInfo().Chest.chestLoot, target) + || LootStorage.Gameobject.HaveQuestLootForPlayer(GetGoInfo().Chest.chestPersonalLoot, target) + || LootStorage.Gameobject.HaveQuestLootForPlayer(GetGoInfo().Chest.chestPushLoot, target)) { Battleground bg = target.GetBattleground(); if (bg) @@ -1657,19 +1666,16 @@ namespace Game.Entities return; GameObjectTemplate info = GetGoInfo(); - if (GetLootState() == LootState.Ready) + if (loot == null && info.GetLootId() != 0) { - uint lootId = info.GetLootId(); - if (lootId != 0) + if (info.GetLootId() != 0) { - SetLootGenerationTime(); - Group group = player.GetGroup(); bool groupRules = group != null && info.Chest.usegrouplootrules != 0; loot = new Loot(GetMap(), GetGUID(), LootType.Chest, groupRules ? group : null); loot.SetDungeonEncounterId(info.Chest.DungeonEncounter); - loot.FillLoot(lootId, LootStorage.Gameobject, player, !groupRules, false, GetLootMode(), GetMap().GetDifficultyLootItemContext()); + loot.FillLoot(info.GetLootId(), LootStorage.Gameobject, player, !groupRules, false, GetLootMode(), GetMap().GetDifficultyLootItemContext()); if (GetLootMode() > 0) { @@ -1681,24 +1687,59 @@ namespace Game.Entities /// @todo possible must be moved to loot release (in different from linked triggering) if (info.Chest.triggeredEvent != 0) + GameEvents.Trigger(info.Chest.triggeredEvent, player, this); + + // triggering linked GO + uint trapEntry = info.Chest.linkedTrap; + if (trapEntry != 0) + TriggeringLinkedGameObject(trapEntry, player); + } + else if (!m_personalLoot.ContainsKey(player.GetGUID())) + { + if (info.Chest.chestPersonalLoot != 0) { - Log.outDebug(LogFilter.Spells, $"Chest ScriptStart id {info.Chest.triggeredEvent} for GO {GetSpawnId()}"); - GameEvents.Trigger(info.Chest.triggeredEvent, user, this); + Loot personalLoot = new(GetMap(), GetGUID(), LootType.Chest, null); + m_personalLoot[player.GetGUID()] = personalLoot; + + personalLoot.SetDungeonEncounterId(info.Chest.DungeonEncounter); + personalLoot.FillLoot(info.Chest.chestPersonalLoot, LootStorage.Gameobject, player, true, false, GetLootMode(), GetMap().GetDifficultyLootItemContext()); + + if (GetLootMode() > 0) + { + GameObjectTemplateAddon addon = GetTemplateAddon(); + if (addon != null) + personalLoot.GenerateMoneyLoot(addon.Mingold, addon.Maxgold); + } } + } + + if (!m_unique_users.Contains(player.GetGUID()) && info.GetLootId() == 0) + { + if (info.Chest.chestPushLoot != 0) + { + Loot pushLoot = new(GetMap(), GetGUID(), LootType.Chest, null); + pushLoot.FillLoot(info.Chest.chestPushLoot, LootStorage.Gameobject, player, true, false, GetLootMode(), GetMap().GetDifficultyLootItemContext()); + pushLoot.AutoStore(player, ItemConst.NullBag, ItemConst.NullSlot); + } + + if (info.Chest.triggeredEvent != 0) + GameEvents.Trigger(info.Chest.triggeredEvent, player, this); // triggering linked GO uint trapEntry = info.Chest.linkedTrap; if (trapEntry != 0) TriggeringLinkedGameObject(trapEntry, player); - SetLootState(LootState.Activated, player); + AddUniqueUse(player); } - else - loot = GetLootForPlayer(player); + + if (GetLootState() != LootState.Activated) + SetLootState(LootState.Activated, player); // Send loot - if (loot != null) - player.SendLoot(loot); + Loot playerLoot = GetLootForPlayer(player); + if (playerLoot != null) + player.SendLoot(playerLoot); break; } case GameObjectTypes.Trap: //6 @@ -2743,6 +2784,18 @@ namespace Game.Entities } } + public bool IsFullyLooted() + { + if (loot != null && !loot.IsLooted()) + return false; + + foreach (var (_, loot) in m_personalLoot) + if (!loot.IsLooted()) + return false; + + return true; + } + public void SetGoState(GameObjectState state) { GameObjectState oldState = GetGoState(); @@ -2880,6 +2933,15 @@ namespace Game.Entities public bool IsLootAllowedFor(Player player) { + Loot loot = GetLootForPlayer(player); + if (loot != null) // check only if loot was already generated + { + if (loot.IsLooted()) // nothing to loot or everything looted. + return false; + if (!loot.HasAllowedLooter(GetGUID()) || (!loot.HasItemForAll() && !loot.HasItemFor(player))) // no loot in chest for this player + return false; + } + if (m_lootRecipient.IsEmpty() && m_lootRecipientGroup.IsEmpty()) return true; @@ -3365,8 +3427,6 @@ namespace Game.Entities void AddLootMode(LootModes lootMode) { m_LootMode |= lootMode; } void RemoveLootMode(LootModes lootMode) { m_LootMode &= ~lootMode; } void ResetLootMode() { m_LootMode = LootModes.Default; } - public void SetLootGenerationTime() { m_lootGenerationTime = (uint)GameTime.GetGameTime(); } - public uint GetLootGenerationTime() { return m_lootGenerationTime; } public void AddToSkillupList(ObjectGuid PlayerGuid) { m_SkillupList.Add(PlayerGuid); } public bool IsInSkillupList(ObjectGuid PlayerGuid) @@ -3495,7 +3555,6 @@ namespace Game.Entities ObjectGuid m_lootRecipient; ObjectGuid m_lootRecipientGroup; LootModes m_LootMode; // bitmask, default LOOT_MODE_DEFAULT, determines what loot will be lootable - uint m_lootGenerationTime; long m_packedRotation; Quaternion m_localRotation; public Position StationaryPosition { get; set; } diff --git a/Source/Game/Globals/ObjectManager.cs b/Source/Game/Globals/ObjectManager.cs index 160928048..02cba3051 100644 --- a/Source/Game/Globals/ObjectManager.cs +++ b/Source/Game/Globals/ObjectManager.cs @@ -18,11 +18,11 @@ using Framework.Collections; using Framework.Constants; using Framework.Database; -using Framework.Dynamic; using Framework.IO; using Game.Conditions; using Game.DataStorage; using Game.Entities; +using Game.Loots; using Game.Mails; using Game.Maps; using Game.Misc; @@ -30,7 +30,6 @@ using Game.Movement; using Game.Scripting; using Game.Spells; using System; -using System.Collections; using System.Collections.Generic; using System.Linq; using System.Numerics; @@ -4687,10 +4686,11 @@ namespace Game case GameObjectTypes.Chest: { // scan GO chest with loot including quest items - uint lootId = pair.Value.GetLootId(); - // find quest loot for GO - if (pair.Value.Chest.questID != 0 || Loots.LootStorage.Gameobject.HaveQuestLootFor(lootId)) + if (pair.Value.Chest.questID != 0 + || LootStorage.Gameobject.HaveQuestLootFor(pair.Value.Chest.chestLoot) + || LootStorage.Gameobject.HaveQuestLootFor(pair.Value.Chest.chestPersonalLoot) + || LootStorage.Gameobject.HaveQuestLootFor(pair.Value.Chest.chestPushLoot)) break; continue; diff --git a/Source/Game/Handlers/LootHandler.cs b/Source/Game/Handlers/LootHandler.cs index 588c85b95..be9ec33f1 100644 --- a/Source/Game/Handlers/LootHandler.cs +++ b/Source/Game/Handlers/LootHandler.cs @@ -296,10 +296,8 @@ namespace Game else go.SetLootState(LootState.Ready); } - else + else if (go.IsFullyLooted()) go.SetLootState(LootState.JustDeactivated); - - loot.Clear(); } else { diff --git a/Source/Game/Loot/Loot.cs b/Source/Game/Loot/Loot.cs index 31f6dee34..8bfca59cc 100644 --- a/Source/Game/Loot/Loot.cs +++ b/Source/Game/Loot/Loot.cs @@ -694,7 +694,7 @@ namespace Game.Loots } } - public bool AutoStore(Player player, byte bag, byte slot, bool broadcast, bool createdByPlayer = false) + public bool AutoStore(Player player, byte bag, byte slot, bool broadcast = false, bool createdByPlayer = false) { bool allLooted = true; for (uint i = 0; i < items.Count; ++i) diff --git a/Source/Game/Loot/LootManager.cs b/Source/Game/Loot/LootManager.cs index af7a1ac8e..f6cb5143f 100644 --- a/Source/Game/Loot/LootManager.cs +++ b/Source/Game/Loot/LootManager.cs @@ -165,17 +165,29 @@ namespace Game.Loots List lootIdSet, lootIdSetUsed = new(); uint count = Gameobject.LoadAndCollectLootIds(out lootIdSet); + void checkLootId(uint lootId, uint gameObjectId) + { + if (!lootIdSet.Contains(lootId)) + Gameobject.ReportNonExistingId(lootId, gameObjectId); + else + lootIdSetUsed.Add(lootId); + } + // remove real entries and check existence loot var gotc = Global.ObjectMgr.GetGameObjectTemplates(); - foreach (var go in gotc) + foreach (var (gameObjectId, gameObjectTemplate) in gotc) { - uint lootid = go.Value.GetLootId(); + uint lootid = gameObjectTemplate.GetLootId(); if (lootid != 0) + checkLootId(lootid, gameObjectId); + + if (gameObjectTemplate.type == GameObjectTypes.Chest) { - if (!lootIdSet.Contains(lootid)) - Gameobject.ReportNonExistingId(lootid, go.Value.entry); - else - lootIdSetUsed.Add(lootid); + if (gameObjectTemplate.Chest.chestPersonalLoot != 0) + checkLootId(gameObjectTemplate.Chest.chestPersonalLoot, gameObjectId); + + if (gameObjectTemplate.Chest.chestPushLoot != 0) + checkLootId(gameObjectTemplate.Chest.chestPushLoot, gameObjectId); } }