/* * This file is part of the AzerothCore 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 AZEROTHCORE_GAMEOBJECT_H #define AZEROTHCORE_GAMEOBJECT_H #include "Common.h" #include "G3D/Quat.h" #include "GameObjectData.h" #include "LootMgr.h" #include "Object.h" #include "SharedDefines.h" #include "Unit.h" class GameObjectAI; class Transport; class StaticTransport; class MotionTransport; class OPvPCapturePoint; class Unit; class GameObjectModel; struct TransportAnimation; typedef void(*goEventFlag)(Player*, GameObject*, Battleground*); // Benchmarked: Faster than std::map (insert/find) typedef std::unordered_map GameObjectTemplateContainer; typedef std::unordered_map GameObjectTemplateAddonContainer; typedef std::unordered_map GameObjectAddonContainer; typedef std::vector GameObjectQuestItemList; typedef std::unordered_map GameObjectQuestItemMap; union GameObjectValue { //11 GAMEOBJECT_TYPE_TRANSPORT struct { uint32 PathProgress; TransportAnimation const* AnimationInfo; } Transport; //25 GAMEOBJECT_TYPE_FISHINGHOLE struct { uint32 MaxOpens; } FishingHole; //29 GAMEOBJECT_TYPE_CAPTURE_POINT struct { OPvPCapturePoint* OPvPObj; } CapturePoint; //33 GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING struct { uint32 Health; uint32 MaxHealth; } Building; }; enum class GameObjectActions : uint32 { // Name from client executable // Comments None, // -NONE- AnimateCustom0, // Animate Custom0 AnimateCustom1, // Animate Custom1 AnimateCustom2, // Animate Custom2 AnimateCustom3, // Animate Custom3 Disturb, // Disturb // Triggers trap Unlock, // Unlock // Resets GO_FLAG_LOCKED Lock, // Lock // Sets GO_FLAG_LOCKED Open, // Open // Sets GO_STATE_ACTIVE OpenAndUnlock, // Open + Unlock // Sets GO_STATE_ACTIVE and resets GO_FLAG_LOCKED Close, // Close // Sets GO_STATE_READY ToggleOpen, // Toggle Open Destroy, // Destroy // Sets GO_STATE_DESTROYED Rebuild, // Rebuild // Resets from GO_STATE_DESTROYED Creation, // Creation Despawn, // Despawn MakeInert, // Make Inert // Disables interactions MakeActive, // Make Active // Enables interactions CloseAndLock, // Close + Lock // Sets GO_STATE_READY and sets GO_FLAG_LOCKED UseArtKit0, // Use ArtKit0 // 46904: 121 UseArtKit1, // Use ArtKit1 // 36639: 81, 46903: 122 UseArtKit2, // Use ArtKit2 UseArtKit3, // Use ArtKit3 SetTapList, // Set Tap List }; // 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, 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 GameObject : public WorldObject, public GridObject, public MovableMapObject, public UpdatableMapObject { public: explicit GameObject(); ~GameObject() override; void BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* target) override; void AddToWorld() override; void RemoveFromWorld() override; void CleanupsBeforeDelete(bool finalCleanup = true) override; uint32 GetDynamicFlags() const override { return GetUInt32Value(GAMEOBJECT_DYNAMIC); } void ReplaceAllDynamicFlags(uint32 flag) override { SetUInt32Value(GAMEOBJECT_DYNAMIC, flag); } virtual bool Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, uint32 phaseMask, float x, float y, float z, float ang, G3D::Quat const& rotation, uint32 animprogress, GOState go_state, uint32 artKit = 0); void Update(uint32 p_time) override; [[nodiscard]] GameObjectTemplate const* GetGOInfo() const { return m_goInfo; } [[nodiscard]] GameObjectTemplateAddon const* GetTemplateAddon() const; [[nodiscard]] GameObjectData const* GetGameObjectData() const { return m_goData; } [[nodiscard]] GameObjectValue const* GetGOValue() const { return &m_goValue; } [[nodiscard]] bool IsTransport() const; [[nodiscard]] bool IsDestructibleBuilding() const; [[nodiscard]] 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(G3D::Quat const& rot); void SetTransportPathRotation(float qx, float qy, float qz, float qw); [[nodiscard]] G3D::Quat const& GetLocalRotation() const { return m_localRotation; } [[nodiscard]] int64 GetPackedLocalRotation() const { return m_packedRotation; } [[nodiscard]] G3D::Quat GetWorldRotation() const; // overwrite WorldObject function for proper name localization [[nodiscard]] std::string const& GetNameForLocaleIdx(LocaleConstant locale_idx) const override; void SaveToDB(bool saveAddon = false); void SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask, bool saveAddon = false); virtual bool LoadFromDB(ObjectGuid::LowType guid, Map* map) { return LoadGameObjectFromDB(guid, map, false); } virtual bool LoadGameObjectFromDB(ObjectGuid::LowType guid, Map* map, bool addToMap = true); void DeleteFromDB(); void SetOwnerGUID(ObjectGuid owner) { // Owner already found and different than expected owner - remove object from old owner if (owner && GetOwnerGUID() && GetOwnerGUID() != owner) { ABORT(); } m_spawnedByDefault = false; // all object with owner is despawned after delay SetGuidValue(OBJECT_FIELD_CREATED_BY, owner); } [[nodiscard]] ObjectGuid GetOwnerGUID() const { return GetGuidValue(OBJECT_FIELD_CREATED_BY); } [[nodiscard]] Unit* GetOwner() const; void SetSpellId(uint32 id) { m_spawnedByDefault = false; // all summoned object is despawned after delay m_spellId = id; } [[nodiscard]] uint32 GetSpellId() const { return m_spellId;} [[nodiscard]] time_t GetRespawnTime() const { return m_respawnTime; } [[nodiscard]] time_t GetRespawnTimeEx() const; void SetRespawnTime(int32 respawn); void SetRespawnDelay(int32 respawn); void Respawn(); [[nodiscard]] bool isSpawned() const { return m_respawnDelayTime == 0 || (m_respawnTime > 0 && !m_spawnedByDefault) || (m_respawnTime == 0 && m_spawnedByDefault); } [[nodiscard]] bool isSpawnedByDefault() const { return m_spawnedByDefault; } void SetSpawnedByDefault(bool b) { m_spawnedByDefault = b; } [[nodiscard]] uint32 GetRespawnDelay() const { return m_respawnDelayTime; } void Refresh(); void DespawnOrUnsummon(Milliseconds delay = 0ms, Seconds forcedRespawnTime = 0s); void Delete(); void GetFishLoot(Loot* fishLoot, Player* lootOwner, bool junk = false); [[nodiscard]] GameobjectTypes GetGoType() const { return GameobjectTypes(GetByteValue(GAMEOBJECT_BYTES_1, 1)); } void SetGoType(GameobjectTypes type) { SetByteValue(GAMEOBJECT_BYTES_1, 1, type); } [[nodiscard]] GOState GetGoState() const { return GOState(GetByteValue(GAMEOBJECT_BYTES_1, 0)); } void SetGoState(GOState state); [[nodiscard]] uint8 GetGoArtKit() const { return GetByteValue(GAMEOBJECT_BYTES_1, 2); } void SetGoArtKit(uint8 artkit); [[nodiscard]] uint8 GetGoAnimProgress() const { return GetByteValue(GAMEOBJECT_BYTES_1, 3); } void SetGoAnimProgress(uint8 animprogress) { SetByteValue(GAMEOBJECT_BYTES_1, 3, animprogress); } static void SetGoArtKit(uint8 artkit, GameObject* go, ObjectGuid::LowType lowguid = 0); void SetPhaseMask(uint32 newPhaseMask, bool update) override; void EnableCollision(bool enable); GameObjectFlags GetGameObjectFlags() const { return GameObjectFlags(GetUInt32Value(GAMEOBJECT_FLAGS)); } bool HasGameObjectFlag(GameObjectFlags flags) const { return HasFlag(GAMEOBJECT_FLAGS, flags) != 0; } void SetGameObjectFlag(GameObjectFlags flags) { SetFlag(GAMEOBJECT_FLAGS, flags); } void RemoveGameObjectFlag(GameObjectFlags flags) { RemoveFlag(GAMEOBJECT_FLAGS, flags); } void ReplaceAllGameObjectFlags(GameObjectFlags flags) { SetUInt32Value(GAMEOBJECT_FLAGS, flags); } void Use(Unit* user); [[nodiscard]] LootState getLootState() const { return m_lootState; } // Note: unit is only used when s = GO_ACTIVATED void SetLootState(LootState s, Unit* unit = nullptr); [[nodiscard]] uint16 GetLootMode() const { return m_LootMode; } [[nodiscard]] bool HasLootMode(uint16 lootMode) const { return m_LootMode & lootMode; } 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 AddToSkillupList(ObjectGuid const& playerGuid); [[nodiscard]] bool IsInSkillupList(ObjectGuid const& playerGuid) const; void AddUniqueUse(Player* player); void AddUse() { ++m_usetimes; } [[nodiscard]] uint32 GetUseCount() const { return m_usetimes; } [[nodiscard]] uint32 GetUniqueUseCount() const { return m_unique_users.size(); } void SaveRespawnTime() override { SaveRespawnTime(0); } void SaveRespawnTime(uint32 forceDelay); Loot loot; [[nodiscard]] Player* GetLootRecipient() const; [[nodiscard]] Group* GetLootRecipientGroup() const; void SetLootRecipient(Creature* creature); void SetLootRecipient(Map* map); bool IsLootAllowedFor(Player const* player) const; [[nodiscard]] bool HasLootRecipient() const { return m_lootRecipient || m_lootRecipientGroup; } uint32 m_groupLootTimer; // (msecs)timer used for group loot uint32 lootingGroupLowGUID; // used to find group which is looting void SetLootGenerationTime(); [[nodiscard]] uint32 GetLootGenerationTime() const { return m_lootGenerationTime; } [[nodiscard]] GameObject* GetLinkedTrap(); void SetLinkedTrap(GameObject* linkedTrap) { m_linkedTrap = linkedTrap->GetGUID(); } [[nodiscard]] bool hasQuest(uint32 quest_id) const override; [[nodiscard]] bool hasInvolvedQuest(uint32 quest_id) const override; bool ActivateToQuest(Player* target) const; void UseDoorOrButton(uint32 time_to_restore = 0, bool alternative = false, Unit* user = nullptr); // 0 = use `gameobject`.`spawntimesecs` void ResetDoorOrButton(); void TriggeringLinkedGameObject(uint32 trapEntry, Unit* target); [[nodiscard]] bool IsNeverVisible() const override; bool IsAlwaysVisibleFor(WorldObject const* seer) const override; [[nodiscard]] bool IsInvisibleDueToDespawn() const override; uint8 getLevelForTarget(WorldObject const* target) const override { if (Unit* owner = GetOwner()) return owner->getLevelForTarget(target); return 1; } GameObject* LookupFishingHoleAround(float range); void CastSpell(Unit* target, uint32 spell); void SendCustomAnim(uint32 anim); [[nodiscard]] bool IsInRange(float x, float y, float z, float radius) const; void ModifyHealth(int32 change, Unit* attackerOrHealer = nullptr, uint32 spellId = 0); void SetDestructibleBuildingModifyState(bool allow) { m_allowModifyDestructibleBuilding = allow; } // sets GameObject type 33 destruction flags and optionally default health for that state void SetDestructibleState(GameObjectDestructibleState state, Player* eventInvoker = nullptr, bool setHealth = false); [[nodiscard]] GameObjectDestructibleState GetDestructibleState() const { if (HasGameObjectFlag(GO_FLAG_DESTROYED)) return GO_DESTRUCTIBLE_DESTROYED; if (HasGameObjectFlag(GO_FLAG_DAMAGED)) return GO_DESTRUCTIBLE_DAMAGED; return GO_DESTRUCTIBLE_INTACT; } void EventInform(uint32 eventId); [[nodiscard]] virtual uint32 GetScriptId() const; [[nodiscard]] GameObjectAI* AI() const { return m_AI; } [[nodiscard]] std::string const& GetAIName() const; void SetDisplayId(uint32 displayid); [[nodiscard]] uint32 GetDisplayId() const { return GetUInt32Value(GAMEOBJECT_DISPLAYID); } GameObjectModel* m_model; void GetRespawnPosition(float& x, float& y, float& z, float* ori = nullptr) const; void SetPosition(float x, float y, float z, float o); void SetPosition(const Position& pos) { SetPosition(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation()); } [[nodiscard]] bool IsStaticTransport() const { return GetGOInfo()->type == GAMEOBJECT_TYPE_TRANSPORT; } [[nodiscard]] bool IsMotionTransport() const { return GetGOInfo()->type == GAMEOBJECT_TYPE_MO_TRANSPORT; } Transport* ToTransport() { if (GetGOInfo()->type == GAMEOBJECT_TYPE_MO_TRANSPORT || GetGOInfo()->type == GAMEOBJECT_TYPE_TRANSPORT) return reinterpret_cast(this); else return nullptr; } [[nodiscard]] Transport const* ToTransport() const { if (GetGOInfo()->type == GAMEOBJECT_TYPE_MO_TRANSPORT || GetGOInfo()->type == GAMEOBJECT_TYPE_TRANSPORT) return reinterpret_cast(this); else return nullptr; } StaticTransport* ToStaticTransport() { if (GetGOInfo()->type == GAMEOBJECT_TYPE_TRANSPORT) return reinterpret_cast(this); else return nullptr; } [[nodiscard]] StaticTransport const* ToStaticTransport() const { if (GetGOInfo()->type == GAMEOBJECT_TYPE_TRANSPORT) return reinterpret_cast(this); else return nullptr; } MotionTransport* ToMotionTransport() { if (GetGOInfo()->type == GAMEOBJECT_TYPE_MO_TRANSPORT) return reinterpret_cast(this); else return nullptr; } [[nodiscard]] MotionTransport const* ToMotionTransport() const { if (GetGOInfo()->type == GAMEOBJECT_TYPE_MO_TRANSPORT) return reinterpret_cast(this); else return nullptr; } [[nodiscard]] float GetStationaryX() const override { if (GetGOInfo()->type != GAMEOBJECT_TYPE_MO_TRANSPORT) return m_stationaryPosition.GetPositionX(); return GetPositionX(); } [[nodiscard]] float GetStationaryY() const override { if (GetGOInfo()->type != GAMEOBJECT_TYPE_MO_TRANSPORT) return m_stationaryPosition.GetPositionY(); return GetPositionY(); } [[nodiscard]] float GetStationaryZ() const override { if (GetGOInfo()->type != GAMEOBJECT_TYPE_MO_TRANSPORT) return m_stationaryPosition.GetPositionZ(); return GetPositionZ(); } [[nodiscard]] float GetStationaryO() const override { if (GetGOInfo()->type != GAMEOBJECT_TYPE_MO_TRANSPORT) return m_stationaryPosition.GetOrientation(); return GetOrientation(); } [[nodiscard]] float GetInteractionDistance() const; void UpdateModelPosition(); [[nodiscard]] bool IsAtInteractDistance(Position const& pos, float radius) const; [[nodiscard]] bool IsAtInteractDistance(Player const* player, SpellInfo const* spell = nullptr) const; [[nodiscard]] bool IsWithinDistInMap(Player const* player) const; using WorldObject::IsWithinDistInMap; [[nodiscard]] SpellInfo const* GetSpellForLock(Player const* player) const; static std::unordered_map gameObjectToEventFlag; // Gameobject -> event flag [[nodiscard]] bool ValidateGameobjectType() const; [[nodiscard]] bool IsInstanceGameobject() const; [[nodiscard]] uint8 GameobjectStateToInt(GOState* state) const; /* A check to verify if this object is available to be saved on the DB when * a state change occurs */ [[nodiscard]] bool IsAllowedToSaveToDB() const { return m_saveStateOnDb; }; /* Enable or Disable the ability to save on the database this gameobject's state * whenever it changes */ void AllowSaveToDB(bool enable) { m_saveStateOnDb = enable; }; void SaveStateToDB(); std::string GetDebugInfo() const override; bool IsUpdateNeeded() override; protected: bool AIM_Initialize(); GameObjectModel* 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 Seconds m_restockTime; LootState m_lootState; bool m_spawnedByDefault; uint32 m_cooldownTime; // used as internal reaction delay time store (not state change reaction). // For traps this: spell casting cooldown, for doors/buttons: reset time. std::unordered_map m_SkillupList; ObjectGuid m_ritualOwnerGUID; // used for GAMEOBJECT_TYPE_SUMMONING_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; GameObjectData const* m_goData; GameObjectValue m_goValue; bool m_allowModifyDestructibleBuilding; int64 m_packedRotation; G3D::Quat m_localRotation; Position m_stationaryPosition; ObjectGuid m_lootRecipient; ObjectGuid::LowType m_lootRecipientGroup; uint16 m_LootMode; // bitmask, default LOOT_MODE_DEFAULT, determines what loot will be lootable uint32 m_lootGenerationTime; ObjectGuid m_linkedTrap; ObjectGuid _lootStateUnitGUID; private: void CheckRitualList(); void ClearRitualList(); 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 = true*/, bool /*incTargetRadius = true*/) const override { //! Following check does check 3d distance dist2compare += obj->GetObjectSize(); return IsInRange(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), dist2compare); } GameObjectAI* m_AI; bool m_saveStateOnDb = false; }; #endif