diff --git a/sql/updates/world/4.3.4/2025_03_11_02_world.sql b/sql/updates/world/4.3.4/2025_03_11_02_world.sql new file mode 100644 index 0000000000..cf21b82a3d --- /dev/null +++ b/sql/updates/world/4.3.4/2025_03_11_02_world.sql @@ -0,0 +1,11 @@ +DROP TABLE IF EXISTS `creature_static_flags_override`; +CREATE TABLE `creature_static_flags_override` ( + `SpawnId` INT UNSIGNED NOT NULL, + `DifficultyId` TINYINT UNSIGNED NOT NULL DEFAULT 0, + `StaticFlags1` INT UNSIGNED, + `StaticFlags2` INT UNSIGNED, + `StaticFlags3` INT UNSIGNED, + `StaticFlags4` INT UNSIGNED, + `StaticFlags5` INT UNSIGNED, + PRIMARY KEY (`SpawnId`, `DifficultyId`) +); diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 170eb5dd30..3a41d0e74e 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -552,7 +552,8 @@ bool Creature::InitEntry(uint32 entry, CreatureData const* data /*= nullptr*/) for (uint8 i = 0; i < MAX_CREATURE_SPELLS; ++i) m_spells[i] = GetCreatureTemplate()->spells[i]; - ApplyAllStaticFlags(cinfo->StaticFlags); + CreatureStaticFlagsHolder staticFlags = GenerateStaticFlags(cinfo, GetSpawnId(), GetMap()->GetDifficulty()); + ApplyAllStaticFlags(staticFlags); _staticFlags.ApplyFlag(CREATURE_STATIC_FLAG_NO_XP, cinfo->type == CREATURE_TYPE_CRITTER || IsPet() @@ -667,6 +668,20 @@ bool Creature::UpdateEntry(uint32 entry, CreatureData const* data /*= nullptr*/, return true; } +CreatureStaticFlagsHolder Creature::GenerateStaticFlags(CreatureTemplate const* cInfo, ObjectGuid::LowType spawnId, Difficulty difficultyId) const +{ + CreatureStaticFlagsOverride const* staticFlagsOverride = sObjectMgr->GetCreatureStaticFlagsOverride(spawnId, difficultyId); + if (!staticFlagsOverride) + return cInfo->StaticFlags; + + return CreatureStaticFlagsHolder( + staticFlagsOverride->StaticFlags1.value_or(cInfo->StaticFlags.GetFlags()), + staticFlagsOverride->StaticFlags2.value_or(cInfo->StaticFlags.GetFlags2()), + staticFlagsOverride->StaticFlags3.value_or(cInfo->StaticFlags.GetFlags3()), + staticFlagsOverride->StaticFlags4.value_or(cInfo->StaticFlags.GetFlags4()), + staticFlagsOverride->StaticFlags5.value_or(cInfo->StaticFlags.GetFlags5())); +} + void Creature::ApplyAllStaticFlags(CreatureStaticFlagsHolder const& flags) { _staticFlags = flags; diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index f71d7ca2c0..de68d8bdff 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -485,6 +485,8 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma time_t _lastDamagedTime; // Part of Evade mechanics CreatureTextRepeatGroup m_textRepeat; + // Draws data from m_creatureDifficulty and spawn/difficulty based override data and returns a CreatureStaticFlagsHolder value which contains the data of both + CreatureStaticFlagsHolder GenerateStaticFlags(CreatureTemplate const* cInfo, ObjectGuid::LowType spawnId, Difficulty difficultyId) const; void ApplyAllStaticFlags(CreatureStaticFlagsHolder const& flags); CreatureStaticFlagsHolder _staticFlags; diff --git a/src/server/game/Entities/Creature/CreatureData.h b/src/server/game/Entities/Creature/CreatureData.h index 3498946629..5aacc95a35 100644 --- a/src/server/game/Entities/Creature/CreatureData.h +++ b/src/server/game/Entities/Creature/CreatureData.h @@ -288,6 +288,12 @@ public: void ApplyFlag(CreatureStaticFlags4 flag, bool apply) { if (apply) _flags4 |= flag; else _flags4 &= ~flag; } void ApplyFlag(CreatureStaticFlags5 flag, bool apply) { if (apply) _flags5 |= flag; else _flags5 &= ~flag; } + EnumFlag GetFlags() const { return _flags; } + EnumFlag GetFlags2() const { return _flags2; } + EnumFlag GetFlags3() const { return _flags3; } + EnumFlag GetFlags4() const { return _flags4; } + EnumFlag GetFlags5() const { return _flags5; } + private: EnumFlag _flags; EnumFlag _flags2; @@ -550,6 +556,16 @@ struct CreatureAddon VisibilityDistanceType visibilityDistanceType; }; +// `creature_static_flags_override` table +struct CreatureStaticFlagsOverride +{ + Optional StaticFlags1; + Optional StaticFlags2; + Optional StaticFlags3; + Optional StaticFlags4; + Optional StaticFlags5; +}; + // Vendors struct VendorItem { diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index cd8934bf08..f7fbca32bf 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -10095,6 +10095,11 @@ PlayerInfo const* ObjectMgr::GetPlayerInfo(uint32 race, uint32 class_) const return info; } +CreatureStaticFlagsOverride const* ObjectMgr::GetCreatureStaticFlagsOverride(ObjectGuid::LowType spawnId, Difficulty difficultyId) const +{ + return Trinity::Containers::MapGetValuePtr(_creatureStaticFlagsOverrideStore, std::make_pair(spawnId, difficultyId)); +} + void ObjectMgr::LoadGameObjectQuestItems() { uint32 oldMSTime = getMSTime(); @@ -10223,6 +10228,61 @@ void ObjectMgr::LoadTaxiNodeLevelData() TC_LOG_INFO("server.loading", ">> Loaded %u taxi node level definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); } +void ObjectMgr::LoadCreatureStaticFlagsOverride() +{ + // reload case + _creatureStaticFlagsOverrideStore.clear(); + + uint32 oldMSTime = getMSTime(); + + // 0 1 2 3 4 5 6 + QueryResult result = WorldDatabase.Query("SELECT SpawnId, DifficultyId, StaticFlags1, StaticFlags2, StaticFlags3, StaticFlags4, StaticFlags5 FROM creature_static_flags_override"); + + if (!result) + { + TC_LOG_INFO("server.loading", ">> Loaded 0 creature static flag overrides. DB table `creature_static_flags_override` is empty."); + return; + } + + uint32 count = 0; + do + { + Field* fields = result->Fetch(); + + ObjectGuid::LowType spawnId = fields[0].GetUInt32(); + Difficulty difficultyId = static_cast(fields[1].GetUInt8()); + + CreatureData const* creatureData = GetCreatureData(spawnId); + if (!creatureData) + { + TC_LOG_ERROR("sql.sql", "Table `creature_static_flags_override` has data for nonexistent creature (SpawnId: %u), skipped", spawnId); + continue; + } + + if (!(creatureData->spawnMask & (1 << difficultyId))) + { + TC_LOG_ERROR("sql.sql", "Table `creature_static_flags_override` has data for a creature that is not available for the specified DifficultyId (SpawnId: %u, DifficultyId: %u), skipped", spawnId, difficultyId); + continue; + } + + CreatureStaticFlagsOverride& staticFlagsOverride = _creatureStaticFlagsOverrideStore[std::make_pair(spawnId, difficultyId)]; + if (!fields[2].IsNull()) + staticFlagsOverride.StaticFlags1 = static_cast(fields[2].GetUInt32()); + if (!fields[3].IsNull()) + staticFlagsOverride.StaticFlags2 = static_cast(fields[3].GetUInt32()); + if (!fields[4].IsNull()) + staticFlagsOverride.StaticFlags3 = static_cast(fields[4].GetUInt32()); + if (!fields[5].IsNull()) + staticFlagsOverride.StaticFlags4 = static_cast(fields[5].GetUInt32()); + if (!fields[6].IsNull()) + staticFlagsOverride.StaticFlags5 = static_cast(fields[6].GetUInt32()); + + ++count; + } while (result->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded %u creature static flag overrides in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); +} + void ObjectMgr::InitializeQueriesData(QueryDataGroup mask) { uint32 oldMSTime = getMSTime(); diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index d3ed75f9d9..c5a279be54 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -572,6 +572,7 @@ typedef std::unordered_map PageTextLocaleContainer; typedef std::unordered_map TaxiNodeLevelDataContainer; typedef std::unordered_map VehicleSeatAddonContainer; typedef std::unordered_map CreatureMovementInfoOverrideContainer; +typedef std::unordered_map, CreatureStaticFlagsOverride> CreatureStaticFlagsOverrideMap; struct GossipMenuItemsLocale { @@ -1190,6 +1191,7 @@ class TC_GAME_API ObjectMgr void CheckCreatureMovement(char const* table, uint64 id, CreatureMovementData& creatureMovement); void LoadGameObjectQuestItems(); void LoadCreatureQuestItems(); + void LoadCreatureStaticFlagsOverride(); void LoadTempSummons(); void LoadCreatures(); void LoadLinkedRespawn(); @@ -1607,6 +1609,8 @@ class TC_GAME_API ObjectMgr uint8 const* GetSummonPropertiesParameter(uint32 summonPropertiesRecID) const; + CreatureStaticFlagsOverride const* GetCreatureStaticFlagsOverride(ObjectGuid::LowType spawnId, Difficulty difficultyId) const; + private: // first free id for selected id type uint32 _auctionId; @@ -1742,6 +1746,7 @@ class TC_GAME_API ObjectMgr GameObjectAddonContainer _gameObjectAddonStore; GameObjectQuestItemMap _gameObjectQuestItemStore; CreatureQuestItemMap _creatureQuestItemStore; + CreatureStaticFlagsOverrideMap _creatureStaticFlagsOverrideStore; EquipmentInfoContainer _equipmentInfoStore; LinkedRespawnContainer _linkedRespawnStore; CreatureLocaleContainer _creatureLocaleStore; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 5527781660..e55c960817 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -2215,6 +2215,9 @@ void World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Loading Creature Text Locales..."); sCreatureTextMgr->LoadCreatureTextLocales(); + TC_LOG_INFO("server.loading", "Loading creature StaticFlags overrides..."); + sObjectMgr->LoadCreatureStaticFlagsOverride(); // must be after LoadCreatures + TC_LOG_INFO("server.loading", "Loading Taxi node level definitions..."); sObjectMgr->LoadTaxiNodeLevelData();