/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see .
*/
#ifndef TRINITYCORE_GAMEOBJECT_H
#define TRINITYCORE_GAMEOBJECT_H
#include "Object.h"
#include "GridObject.h"
#include "GameObjectData.h"
#include "MapObject.h"
#include "SharedDefines.h"
class GameObject;
class GameObjectAI;
class GameObjectModel;
class OPvPCapturePoint;
class Transport;
class TransportBase;
class Unit;
struct Loot;
struct TransportAnimation;
enum TriggerCastFlags : uint32;
namespace Vignettes
{
struct VignetteData;
}
// enum for GAMEOBJECT_TYPE_NEW_FLAG
// values taken from world state
enum class FlagState : uint8
{
InBase = 1,
Taken,
Dropped,
Respawning
};
namespace WorldPackets
{
namespace Battleground
{
enum class BattlegroundCapturePointState : uint8;
}
}
// Base class for GameObject type specific implementations
class GameObjectTypeBase
{
public:
class TC_GAME_API CustomCommand
{
public:
virtual ~CustomCommand();
virtual void Execute(GameObjectTypeBase& type) const = 0;
};
explicit GameObjectTypeBase(GameObject& owner) : _owner(owner) { }
virtual ~GameObjectTypeBase() = default;
virtual void Update([[maybe_unused]] uint32 diff) { }
virtual void OnStateChanged([[maybe_unused]] GOState oldState, [[maybe_unused]] GOState newState) { }
virtual void OnRelocated() { }
virtual bool IsNeverVisibleFor([[maybe_unused]] WorldObject const* seer, [[maybe_unused]] bool allowServersideObjects) const { return false; }
virtual void ActivateObject([[maybe_unused]] GameObjectActions action, [[maybe_unused]] int32 param, [[maybe_unused]] WorldObject* spellCaster = nullptr, [[maybe_unused]] uint32 spellId = 0, [[maybe_unused]] int32 effectIndex = -1) { }
protected:
GameObject& _owner;
};
namespace GameObjectType
{
class TC_GAME_API SetTransportAutoCycleBetweenStopFrames : public GameObjectTypeBase::CustomCommand
{
public:
explicit SetTransportAutoCycleBetweenStopFrames(bool on);
void Execute(GameObjectTypeBase& type) const override;
private:
bool _on;
};
class TC_GAME_API SetNewFlagState : public GameObjectTypeBase::CustomCommand
{
public:
explicit SetNewFlagState(FlagState state, Player* player);
void Execute(GameObjectTypeBase& type) const override;
private:
FlagState _state;
Player* _player;
};
class TC_GAME_API SetControlZoneValue : public GameObjectTypeBase::CustomCommand
{
public:
explicit SetControlZoneValue(Optional value = { });
void Execute(GameObjectTypeBase& type) const override;
private:
Optional _value;
};
}
union GameObjectValue
{
//25 GAMEOBJECT_TYPE_FISHINGHOLE
struct
{
uint32 MaxOpens;
} FishingHole;
//29 GAMEOBJECT_TYPE_CONTROL_ZONE
struct
{
OPvPCapturePoint *OPvPObj;
} ControlZone;
//33 GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING
struct
{
uint32 Health;
::DestructibleHitpoint const* DestructibleHitpoint;
} Building;
//42 GAMEOBJECT_TYPE_CAPTURE_POINT
struct
{
TeamId LastTeamCapture;
WorldPackets::Battleground::BattlegroundCapturePointState State;
uint32 AssaultTimer;
} CapturePoint;
};
// For containers: [GO_NOT_READY]->GO_READY (close)->GO_ACTIVATED (open) ->GO_JUST_DEACTIVATED->GO_READY -> ...
// For bobber: GO_NOT_READY ->GO_READY (close)->GO_ACTIVATED (open) ->GO_JUST_DEACTIVATED->
// For door(closed):[GO_NOT_READY]->GO_READY (close)->GO_ACTIVATED (open) ->GO_JUST_DEACTIVATED->GO_READY(close) -> ...
// For door(open): [GO_NOT_READY]->GO_READY (open) ->GO_ACTIVATED (close)->GO_JUST_DEACTIVATED->GO_READY(open) -> ...
enum LootState
{
GO_NOT_READY = 0,
GO_READY, // can be ready but despawned, and then not possible activate until spawn
GO_ACTIVATED,
GO_JUST_DEACTIVATED
};
// 5 sec for bobber catch
#define FISHING_BOBBER_READY_TIME 5
class TC_GAME_API GameObject : public WorldObject, public GridObject, public MapObject
{
public:
explicit GameObject();
~GameObject();
protected:
void BuildValuesCreate(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const override;
void BuildValuesUpdate(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const override;
void ClearUpdateMask(bool remove) override;
public:
void BuildValuesUpdateForPlayerWithMask(UpdateData* data, UF::ObjectData::Mask const& requestedObjectMask,
UF::GameObjectData::Mask const& requestedGameObjectMask, Player const* target) const;
struct ValuesUpdateForPlayerWithMaskSender // sender compatible with MessageDistDeliverer
{
explicit ValuesUpdateForPlayerWithMaskSender(GameObject const* owner) : Owner(owner) { }
GameObject const* Owner;
UF::ObjectData::Base ObjectMask;
UF::GameObjectData::Base GameObjectMask;
void operator()(Player const* player) const;
};
void AddToWorld() override;
void RemoveFromWorld() override;
void CleanupsBeforeDelete(bool finalCleanup = true) override;
private:
bool Create(uint32 entry, Map* map, Position const& pos, QuaternionData const& rotation, uint32 animProgress, GOState goState, uint32 artKit, bool dynamic, ObjectGuid::LowType spawnid);
public:
static GameObject* CreateGameObject(uint32 entry, Map* map, Position const& pos, QuaternionData const& rotation, uint32 animProgress, GOState goState, uint32 artKit = 0);
static GameObject* CreateGameObjectFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap = true);
void Update(uint32 p_time) override;
GameObjectTemplate const* GetGOInfo() const { return m_goInfo; }
GameObjectTemplateAddon const* GetTemplateAddon() const { return m_goTemplateAddon; }
GameObjectOverride const* GetGameObjectOverride() const;
GameObjectData const* GetGameObjectData() const { return m_goData; }
GameObjectValue const* GetGOValue() const { return &m_goValue; }
bool IsTransport() const;
bool IsDestructibleBuilding() const;
ObjectGuid::LowType GetSpawnId() const { return m_spawnId; }
// z_rot, y_rot, x_rot - rotation angles around z, y and x axes
void SetLocalRotationAngles(float z_rot, float y_rot, float x_rot);
void SetLocalRotation(float qx, float qy, float qz, float qw);
void SetParentRotation(QuaternionData const& rotation); // transforms(rotates) transport's path
QuaternionData const& GetLocalRotation() const { return m_localRotation; }
int64 GetPackedLocalRotation() const { return m_packedRotation; }
QuaternionData GetWorldRotation() const;
// overwrite WorldObject function for proper name localization
std::string GetNameForLocaleIdx(LocaleConstant locale) const override;
bool HasLabel(int32 gameobjectLabel) const;
std::span GetLabels() const;
void SaveToDB();
void SaveToDB(uint32 mapid, std::vector const& spawnDifficulties);
bool LoadFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap, bool = true); // arg4 is unused, only present to match the signature on Creature
static bool DeleteFromDB(ObjectGuid::LowType spawnId);
ObjectGuid GetCreatorGUID() const override { return m_gameObjectData->CreatedBy; }
void SetOwnerGUID(ObjectGuid owner)
{
// Owner already found and different than expected owner - remove object from old owner
if (!owner.IsEmpty() && !GetOwnerGUID().IsEmpty() && GetOwnerGUID() != owner)
{
ABORT();
}
m_spawnedByDefault = false; // all object with owner is despawned after delay
SetUpdateFieldValue(m_values.ModifyValue(&GameObject::m_gameObjectData).ModifyValue(&UF::GameObjectData::CreatedBy), owner);
}
ObjectGuid GetOwnerGUID() const override { return m_gameObjectData->CreatedBy; }
void SetSpellId(uint32 id)
{
m_spawnedByDefault = false; // all summoned object is despawned after delay
m_spellId = id;
}
uint32 GetSpellId() const { return m_spellId;}
time_t GetRespawnTime() const { return m_respawnTime; }
time_t GetRespawnTimeEx() const;
void SetRespawnTime(int32 respawn);
void Respawn();
bool isSpawned() const
{
return m_respawnDelayTime == 0 ||
(m_respawnTime > 0 && !m_spawnedByDefault) ||
(m_respawnTime == 0 && m_spawnedByDefault);
}
bool isSpawnedByDefault() const { return m_spawnedByDefault; }
void SetSpawnedByDefault(bool b) { m_spawnedByDefault = b; }
uint32 GetRespawnDelay() const { return m_respawnDelayTime; }
void Refresh();
void DespawnOrUnsummon(Milliseconds delay = 0ms, Seconds forceRespawnTime = 0s);
void DespawnForPlayer(Player* seer, Seconds respawnTime);
void Delete();
void SendGameObjectDespawn();
Loot* GetFishLoot(Player* lootOwner);
Loot* GetFishLootJunk(Player* lootOwner);
bool HasFlag(GameObjectFlags flags) const { return (*m_gameObjectData->Flags & flags) != 0; }
void SetFlag(GameObjectFlags flags) { SetUpdateFieldFlagValue(m_values.ModifyValue(&GameObject::m_gameObjectData).ModifyValue(&UF::GameObjectData::Flags), flags); }
void RemoveFlag(GameObjectFlags flags) { RemoveUpdateFieldFlagValue(m_values.ModifyValue(&GameObject::m_gameObjectData).ModifyValue(&UF::GameObjectData::Flags), flags); }
void ReplaceAllFlags(GameObjectFlags flags) { SetUpdateFieldValue(m_values.ModifyValue(&GameObject::m_gameObjectData).ModifyValue(&UF::GameObjectData::Flags), flags); }
void SetLevel(uint32 level) { SetUpdateFieldValue(m_values.ModifyValue(&GameObject::m_gameObjectData).ModifyValue(&UF::GameObjectData::Level), level); }
GameobjectTypes GetGoType() const { return GameobjectTypes(*m_gameObjectData->TypeID); }
void SetGoType(GameobjectTypes type) { SetUpdateFieldValue(m_values.ModifyValue(&GameObject::m_gameObjectData).ModifyValue(&UF::GameObjectData::TypeID), type); }
GOState GetGoState() const { return GOState(*m_gameObjectData->State); }
void SetGoState(GOState state);
GOState GetGoStateFor(ObjectGuid const& viewer) const;
void SetGoStateFor(GOState state, Player const* viewer);
uint32 GetGoArtKit() const { return m_gameObjectData->ArtKit; }
void SetGoArtKit(uint32 artkit);
uint8 GetGoAnimProgress() const { return m_gameObjectData->PercentHealth; }
void SetGoAnimProgress(uint8 animprogress) { SetUpdateFieldValue(m_values.ModifyValue(&GameObject::m_gameObjectData).ModifyValue(&UF::GameObjectData::PercentHealth), animprogress); }
static void SetGoArtKit(uint32 artkit, GameObject* go, ObjectGuid::LowType lowguid = UI64LIT(0));
std::vector const* GetPauseTimes() const;
Optional GetPathProgressForClient() const { return m_transportPathProgress; }
void SetPathProgressForClient(float progress);
void EnableCollision(bool enable);
void Use(Unit* user, bool ignoreCastInProgress = false);
LootState getLootState() const { return m_lootState; }
// Note: unit is only used when s = GO_ACTIVATED
void SetLootState(LootState s, Unit* unit = nullptr);
uint16 GetLootMode() const { return m_LootMode; }
bool HasLootMode(uint16 lootMode) const { return (m_LootMode & lootMode) != 0; }
void SetLootMode(uint16 lootMode) { m_LootMode = lootMode; }
void AddLootMode(uint16 lootMode) { m_LootMode |= lootMode; }
void RemoveLootMode(uint16 lootMode) { m_LootMode &= ~lootMode; }
void ResetLootMode() { m_LootMode = LOOT_MODE_DEFAULT; }
void ClearLoot();
bool IsFullyLooted() const;
void OnLootRelease(Player* looter);
void AddToSkillupList(ObjectGuid const& PlayerGuidLow) { m_SkillupList.insert(PlayerGuidLow); }
bool IsInSkillupList(ObjectGuid const& playerGuid) const
{
return m_SkillupList.count(playerGuid) > 0;
}
void ClearSkillupList() { m_SkillupList.clear(); }
void AddUniqueUse(Player* player);
void AddUse() { ++m_usetimes; }
uint32 GetUseCount() const { return m_usetimes; }
uint32 GetUniqueUseCount() const { return uint32(m_unique_users.size()); }
void SaveRespawnTime(uint32 forceDelay = 0);
std::unique_ptr m_loot;
std::unordered_map> m_personalLoot;
GuidUnorderedSet const& GetTapList() const { return m_tapList; }
void SetTapList(GuidUnorderedSet tapList) { m_tapList = std::move(tapList); }
bool IsLootAllowedFor(Player const* player) const;
bool HasLootRecipient() const { return !m_tapList.empty(); }
Loot* GetLootForPlayer(Player const* /*player*/) const override;
GameObject* GetLinkedTrap();
void SetLinkedTrap(GameObject* linkedTrap) { m_linkedTrap = linkedTrap->GetGUID(); }
bool hasQuest(uint32 quest_id) const override;
bool hasInvolvedQuest(uint32 quest_id) const override;
bool HasConditionalInteraction() const;
bool CanActivateForPlayer(Player const* target) const;
bool ActivateToQuest(Player const* target) const;
void UseDoorOrButton(uint32 time_to_restore = 0, bool alternative = false, Unit* user = nullptr);
// 0 = use `gameobject`.`spawntimesecs`
void ResetDoorOrButton();
void ActivateObject(GameObjectActions action, int32 param, WorldObject* spellCaster = nullptr, uint32 spellId = 0, int32 effectIndex = -1);
void TriggeringLinkedGameObject(uint32 trapEntry, Unit* target);
bool IsNeverVisibleFor(WorldObject const* seer, bool allowServersideObjects = false) const override;
bool IsAlwaysVisibleFor(WorldObject const* seer) const override;
bool IsInvisibleDueToDespawn(WorldObject const* seer) const override;
uint8 GetLevelForTarget(WorldObject const* target) const override;
GameObject* LookupFishingHoleAround(float range);
void SendCustomAnim(uint32 anim);
bool IsInRange(float x, float y, float z, float radius) const;
void ModifyHealth(int32 change, WorldObject* attackerOrHealer = nullptr, uint32 spellId = 0);
// sets GameObject type 33 destruction flags and optionally default health for that state
void SetDestructibleState(GameObjectDestructibleState state, WorldObject* attackerOrHealer = nullptr, bool setHealth = false);
GameObjectDestructibleState GetDestructibleState() const
{
if ((*m_gameObjectData->Flags & GO_FLAG_DESTROYED))
return GO_DESTRUCTIBLE_DESTROYED;
if ((*m_gameObjectData->Flags & GO_FLAG_DAMAGED))
return GO_DESTRUCTIBLE_DAMAGED;
return GO_DESTRUCTIBLE_INTACT;
}
// There's many places not ready for dynamic spawns. This allows them to live on for now.
void SetRespawnCompatibilityMode(bool mode = true) { m_respawnCompatibilityMode = mode; }
bool GetRespawnCompatibilityMode() {return m_respawnCompatibilityMode; }
std::string const& GetAIName() const;
uint32 GetScriptId() const;
GameObjectAI* AI() const { return m_AI; }
void InheritStringIds(GameObject const* parent);
bool HasStringId(std::string_view id) const;
void SetScriptStringId(std::string id);
std::string_view GetStringId(StringIdType type) const { return m_stringIds[size_t(type)] ? std::string_view(*m_stringIds[size_t(type)]) : std::string_view(); }
SpawnTrackingStateData const* GetSpawnTrackingStateDataForPlayer(Player const* player) const override;
void SetDisplayId(uint32 displayid);
uint32 GetDisplayId() const { return m_gameObjectData->DisplayID; }
uint8 GetNameSetId() const;
uint32 GetFaction() const override { return m_gameObjectData->FactionTemplate; }
void SetFaction(uint32 faction) override { SetUpdateFieldValue(m_values.ModifyValue(&GameObject::m_gameObjectData).ModifyValue(&UF::GameObjectData::FactionTemplate), faction); }
GameObjectModel* m_model;
void GetRespawnPosition(float &x, float &y, float &z, float* ori = nullptr) const;
TransportBase* ToTransportBase() { return const_cast(const_cast(this)->ToTransportBase()); }
TransportBase const* ToTransportBase() const;
Transport* ToTransport() { if (GetGOInfo()->type == GAMEOBJECT_TYPE_MAP_OBJ_TRANSPORT) return reinterpret_cast(this); else return nullptr; }
Transport const* ToTransport() const { if (GetGOInfo()->type == GAMEOBJECT_TYPE_MAP_OBJ_TRANSPORT) return reinterpret_cast(this); else return nullptr; }
Position const& GetStationaryPosition() const override { return m_stationaryPosition; }
void RelocateStationaryPosition(float x, float y, float z, float o) { m_stationaryPosition.Relocate(x, y, z, o); }
void RelocateStationaryPosition(Position const& pos) { m_stationaryPosition.Relocate(pos); }
void AfterRelocation();
float GetInteractionDistance() const;
void UpdateModelPosition();
bool IsAtInteractDistance(Position const& pos, float radius) const;
bool IsAtInteractDistance(Player const* player, SpellInfo const* spell = nullptr) const;
bool IsWithinDistInMap(Player const* player) const;
using WorldObject::IsWithinDistInMap;
SpellInfo const* GetSpellForLock(Player const* player) const;
uint16 GetAIAnimKitId() const override { return _animKitId; }
void SetAnimKitId(uint16 animKitId, bool oneshot);
uint32 GetWorldEffectID() const { return _worldEffectID; }
void SetWorldEffectID(uint32 worldEffectID) { _worldEffectID = worldEffectID; }
Vignettes::VignetteData const* GetVignette() const { return m_vignette.get(); }
void SetVignette(uint32 vignetteId);
void SetSpellVisualId(int32 spellVisualId, ObjectGuid activatorGuid = ObjectGuid::Empty);
void AssaultCapturePoint(Player* player);
void UpdateCapturePoint();
bool CanInteractWithCapturePoint(Player const* target) const;
FlagState GetFlagState() const;
ObjectGuid const& GetFlagCarrierGUID() const;
time_t GetFlagTakenFromBaseTime() const;
GuidUnorderedSet const* GetInsidePlayers() const;
bool MeetsInteractCondition(Player const* user) const;
void AIM_Destroy();
bool AIM_Initialize();
std::string GetDebugInfo() const override;
void UpdateDynamicFlagsForNearbyPlayers();
void HandleCustomTypeCommand(GameObjectTypeBase::CustomCommand const& command) const;
UF::UpdateField m_gameObjectData;
TeamId GetControllingTeam() const;
protected:
void CreateModel();
void UpdateModel(); // updates model in case displayId were changed
uint32 m_spellId;
time_t m_respawnTime; // (secs) time of next respawn (or despawn if GO have owner()),
uint32 m_respawnDelayTime; // (secs) if 0 then current GO state no dependent from timer
uint32 m_despawnDelay;
Seconds m_despawnRespawnTime; // override respawn time after delayed despawn
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
GuidSet m_SkillupList;
ObjectGuid m_ritualOwnerGUID; // used for GAMEOBJECT_TYPE_RITUAL where GO is not summoned (no owner)
GuidSet m_unique_users;
uint32 m_usetimes;
typedef std::map ChairSlotAndUser;
ChairSlotAndUser ChairListSlots;
ObjectGuid::LowType m_spawnId; ///< For new or temporary gameobjects is 0 for saved it is lowguid
GameObjectTemplate const* m_goInfo;
GameObjectTemplateAddon const* m_goTemplateAddon;
GameObjectData const* m_goData;
std::unique_ptr m_goTypeImpl;
GameObjectValue m_goValue; // TODO: replace with m_goTypeImpl
std::array m_stringIds;
Optional m_scriptStringId;
int64 m_packedRotation;
QuaternionData m_localRotation;
Position m_stationaryPosition;
GuidUnorderedSet m_tapList;
uint16 m_LootMode; // bitmask, default LOOT_MODE_DEFAULT, determines what loot will be lootable
ObjectGuid m_linkedTrap;
private:
void RemoveFromOwner();
void SwitchDoorOrButton(bool activate, bool alternative = false);
void UpdatePackedRotation();
//! Object distance/size - overridden from Object::_IsWithinDist. Needs to take in account proper GO size.
bool _IsWithinDist(WorldObject const* obj, float dist2compare, bool /*is3D*/, bool /*incOwnRadius*/, bool /*incTargetRadius*/) const override
{
//! Following check does check 3d distance
return IsInRange(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), dist2compare);
}
GameObjectAI* m_AI;
bool m_respawnCompatibilityMode;
uint16 _animKitId;
uint32 _worldEffectID;
Optional m_transportPathProgress;
std::unique_ptr m_vignette;
struct PerPlayerState
{
SystemTimePoint ValidUntil = SystemTimePoint::min();
Optional State;
bool Despawned = false;
};
std::unique_ptr> m_perPlayerState;
std::unordered_map& GetOrCreatePerPlayerStates();
};
#endif