diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index b89ad3e9aff..29f171de074 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -69,6 +69,8 @@ GameObject::GameObject() : WorldObject(false), MapObject(), m_respawnTime = 0; m_respawnDelayTime = 300; m_despawnDelay = 0; + m_despawnRespawnTime = 0s; + m_restockTime = 0; m_lootState = GO_NOT_READY; m_spawnedByDefault = true; m_usetimes = 0; @@ -417,6 +419,14 @@ void GameObject::Update(uint32 diff) } return; } + case GAMEOBJECT_TYPE_CHEST: + if (m_restockTime > GameTime::GetGameTime()) + return; + // If there is no restock timer, or if the restock timer passed, the chest becomes ready to loot + m_restockTime = 0; + m_lootState = GO_READY; + AddToObjectUpdateIfNeeded(); + break; default: m_lootState = GO_READY; // for other GOis same switched without delay to GO_READY break; @@ -604,6 +614,14 @@ void GameObject::Update(uint32 diff) } else m_groupLootTimer -= diff; } + + // Gameobject was partially looted and restock time passed, restock all loot now + if (!GetGOInfo()->chest.consumable && GameTime::GetGameTime() >= m_restockTime) + { + m_restockTime = 0; + m_lootState = GO_READY; + AddToObjectUpdateIfNeeded(); + } break; case GAMEOBJECT_TYPE_TRAP: { @@ -675,21 +693,26 @@ void GameObject::Update(uint32 diff) loot.clear(); - //! If this is summoned by a spell with ie. SPELL_EFFECT_SUMMON_OBJECT_WILD, with or without owner, we check respawn criteria based on spell - //! The GetOwnerGUID() check is mostly for compatibility with hacky scripts - 99% of the time summoning should be done trough spells. - if (GetSpellId() || GetOwnerGUID()) + // 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() || GetSpellId()) && m_respawnTime == 0; + if ((GetGoType() == GAMEOBJECT_TYPE_CHEST || GetGoType() == GAMEOBJECT_TYPE_GOOBER) && !GetGOInfo()->IsDespawnAtAction() && !isSummonedAndExpired) { - //Don't delete spell spawned chests, which are not consumed on loot - if (m_respawnTime > 0 && GetGoType() == GAMEOBJECT_TYPE_CHEST && !GetGOInfo()->IsDespawnAtAction()) + if (GetGoType() == GAMEOBJECT_TYPE_CHEST && GetGOInfo()->chest.chestRestockTime > 0) { - UpdateObjectVisibility(); - SetLootState(GO_READY); + // Start restock timer when the chest is fully looted + m_restockTime = GameTime::GetGameTime() + GetGOInfo()->chest.chestRestockTime; + SetLootState(GO_NOT_READY); + AddToObjectUpdateIfNeeded(); } else - { - SetRespawnTime(0); - Delete(); - } + SetLootState(GO_READY); + UpdateObjectVisibility(); + return; + } + else if (GetOwnerGUID() || GetSpellId()) + { + SetRespawnTime(0); + Delete(); return; } @@ -1199,6 +1222,10 @@ bool GameObject::ActivateToQuest(Player* target) const } case GAMEOBJECT_TYPE_CHEST: { + // Chests become inactive while not ready to be looted + if (getLootState() == GO_NOT_READY) + return false; + // scan GO chest with loot including quest items if (LootTemplates_Gameobject.HaveQuestLootForPlayer(GetGOInfo()->GetLootId(), target)) { @@ -2278,6 +2305,10 @@ void GameObject::SetLootState(LootState state, Unit* unit) AI()->OnLootStateChanged(state, unit); + // Start restock timer if the chest is partially looted or not looted at all + if (GetGoType() == GAMEOBJECT_TYPE_CHEST && state == GO_ACTIVATED && GetGOInfo()->chest.chestRestockTime > 0 && m_restockTime == 0) + m_restockTime = GameTime::GetGameTime() + GetGOInfo()->chest.chestRestockTime; + if (GetGoType() == GAMEOBJECT_TYPE_DOOR) // only set collision for doors on SetGoState return; diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h index 5b21824558b..42ca1bd8aac 100644 --- a/src/server/game/Entities/GameObject/GameObject.h +++ b/src/server/game/Entities/GameObject/GameObject.h @@ -313,6 +313,7 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject LootState m_lootState; ObjectGuid m_lootStateUnitGUID; // GUID of the unit passed with SetLootState(LootState, Unit*) bool m_spawnedByDefault; + time_t m_restockTime; time_t m_cooldownTime; // used as internal reaction delay time store (not state change reaction). // For traps this: spell casting cooldown, for doors/buttons: reset time. GOState m_prevGoState; // What state to set whenever resetting