diff --git a/sql/updates/world/4.3.4/2025_06_18_00_world.sql b/sql/updates/world/4.3.4/2025_06_18_00_world.sql new file mode 100644 index 0000000000..80b3f0f5d7 --- /dev/null +++ b/sql/updates/world/4.3.4/2025_06_18_00_world.sql @@ -0,0 +1,2 @@ +ALTER TABLE `creature_template` + CHANGE `exp` `HealthScalingExpansion` INT NOT NULL DEFAULT 0; diff --git a/src/server/database/Database/Implementation/WorldDatabase.cpp b/src/server/database/Database/Implementation/WorldDatabase.cpp index 19c05152fd..660fff047f 100644 --- a/src/server/database/Database/Implementation/WorldDatabase.cpp +++ b/src/server/database/Database/Implementation/WorldDatabase.cpp @@ -76,7 +76,7 @@ void WorldDatabaseConnection::DoPrepareStatements() PrepareStatement(WORLD_SEL_WAYPOINT_SCRIPT_ID_BY_GUID, "SELECT id FROM waypoint_scripts WHERE guid = ?", CONNECTION_SYNCH); PrepareStatement(WORLD_DEL_CREATURE, "DELETE FROM creature WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(WORLD_SEL_COMMANDS, "SELECT name, permission, help FROM command", CONNECTION_SYNCH); - PrepareStatement(WORLD_SEL_CREATURE_TEMPLATE, "SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, modelid1, modelid2, modelid3, modelid4, name, femaleName, subname, IconName, gossip_menu_id, minlevel, maxlevel, exp, exp_unk, faction, npcflag, speed_walk, speed_run, scale, `rank`, dmgschool, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, family, trainer_class, type, type_flags, type_flags2, lootid, pickpocketloot, skinloot, resistance1, resistance2, resistance3, resistance4, resistance5, resistance6, spell1, spell2, spell3, spell4, spell5, spell6, spell7, spell8, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, ctm.HoverInitiallyEnabled, ctm.Random, ctm.InteractionPauseTimer, HoverHeight, HealthModifier, HealthModifierExtra, ManaModifier, ManaModifierExtra, ArmorModifier, DamageModifier, ExperienceModifier, RacialLeader, movementId, RegenHealth, mechanic_immune_mask, spell_school_immune_mask, flags_extra, StaticFlags, StaticFlags2, StaticFlags3, StaticFlags4, StaticFlags5, ScriptName FROM creature_template ct LEFT JOIN creature_template_movement ctm ON ct.entry = ctm.CreatureId WHERE entry = ?", CONNECTION_SYNCH); + PrepareStatement(WORLD_SEL_CREATURE_TEMPLATE, "SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, modelid1, modelid2, modelid3, modelid4, name, femaleName, subname, IconName, gossip_menu_id, minlevel, maxlevel, HealthScalingExpansion, exp_unk, faction, npcflag, speed_walk, speed_run, scale, `rank`, dmgschool, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, family, trainer_class, type, type_flags, type_flags2, lootid, pickpocketloot, skinloot, resistance1, resistance2, resistance3, resistance4, resistance5, resistance6, spell1, spell2, spell3, spell4, spell5, spell6, spell7, spell8, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, ctm.HoverInitiallyEnabled, ctm.Random, ctm.InteractionPauseTimer, HoverHeight, HealthModifier, HealthModifierExtra, ManaModifier, ManaModifierExtra, ArmorModifier, DamageModifier, ExperienceModifier, RacialLeader, movementId, RegenHealth, mechanic_immune_mask, spell_school_immune_mask, flags_extra, StaticFlags, StaticFlags2, StaticFlags3, StaticFlags4, StaticFlags5, ScriptName FROM creature_template ct LEFT JOIN creature_template_movement ctm ON ct.entry = ctm.CreatureId WHERE entry = ?", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_WAYPOINT_SCRIPT_BY_ID, "SELECT guid, delay, command, datalong, datalong2, dataint, x, y, z, o FROM waypoint_scripts WHERE id = ?", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_CREATURE_BY_ID, "SELECT guid FROM creature WHERE id = ?", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_GAMEOBJECT_NEAREST, "SELECT guid, id, position_x, position_y, position_z, map, (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) AS order_ FROM gameobject WHERE map = ? AND (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) <= ? ORDER BY order_", CONNECTION_SYNCH); diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 16711c959c..b1c3f3c17c 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -1875,7 +1875,7 @@ float Creature::GetAttackDistance(Unit const* player) const float maxRadius = (45.0f * sWorld->getRate(RATE_CREATURE_AGGRO)); float minRadius = (5.0f * sWorld->getRate(RATE_CREATURE_AGGRO)); float aggroRate = sWorld->getRate(RATE_CREATURE_AGGRO); - uint8 expansionMaxLevel = uint8(DBCManager::GetMaxLevelForExpansion(GetCreatureTemplate()->expansion)); + uint8 expansionMaxLevel = uint8(DBCManager::GetMaxLevelForExpansion(GetCreatureTemplate()->GetHealthScalingExpansion())); uint32 playerLevel = player->getLevel(); uint32 creatureLevel = getLevel(); diff --git a/src/server/game/Entities/Creature/CreatureData.h b/src/server/game/Entities/Creature/CreatureData.h index 950f763bc0..6a27da8fb4 100644 --- a/src/server/game/Entities/Creature/CreatureData.h +++ b/src/server/game/Entities/Creature/CreatureData.h @@ -366,7 +366,7 @@ struct TC_GAME_API CreatureTemplate uint32 GossipMenuId; uint8 minlevel; uint8 maxlevel; - uint32 expansion; + int32 HealthScalingExpansion; uint32 expansionUnknown; // either 0 or 3, sent to the client / wdb uint32 faction; uint32 npcflag; @@ -450,6 +450,12 @@ struct TC_GAME_API CreatureTemplate return canTameExotic || !IsExotic(); } + // Helpers + int32 GetHealthScalingExpansion() const + { + return HealthScalingExpansion == EXPANSION_LEVEL_CURRENT ? CURRENT_EXPANSION : HealthScalingExpansion; + } + void InitializeQueryData(); WorldPacket BuildQueryData(LocaleConstant loc) const; }; @@ -467,10 +473,9 @@ struct TC_GAME_API CreatureBaseStats float BaseDamage[MAX_EXPANSIONS]; // Helpers - uint32 GenerateHealth(CreatureTemplate const* info) const { - return uint32(ceil(BaseHealth[info->expansion] * info->ModHealth * info->ModHealthExtra)); + return uint32(ceil(BaseHealth[info->GetHealthScalingExpansion()] * info->ModHealth * info->ModHealthExtra)); } uint32 GenerateArmor(CreatureTemplate const* info) const @@ -480,7 +485,7 @@ struct TC_GAME_API CreatureBaseStats float GenerateBaseDamage(CreatureTemplate const* info) const { - return BaseDamage[info->expansion]; + return BaseDamage[info->GetHealthScalingExpansion()]; } static CreatureBaseStats const* GetBaseStats(uint8 level, uint8 unitClass); diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 68019c7086..b22477b376 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -426,8 +426,8 @@ void ObjectMgr::LoadCreatureTemplates() // 0 1 2 3 4 5 6 7 8 QueryResult result = WorldDatabase.Query("SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, modelid1, modelid2, modelid3, " - // 9 10 11 12 13 14 15 16 17 18 19 20 21 22 - "modelid4, name, femaleName, subname, IconName, gossip_menu_id, minlevel, maxlevel, exp, exp_unk, faction, npcflag, speed_walk, speed_run, " + // 9 10 11 12 13 14 15 16 17 18 19 20 21 22 + "modelid4, name, femaleName, subname, IconName, gossip_menu_id, minlevel, maxlevel, HealthScalingExpansion, exp_unk, faction, npcflag, speed_walk, speed_run, " // 23 24 25 26 27 28 29 30 31 32 "scale, `rank`, dmgschool, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, " // 33 34 35 36 37 38 39 40 41 42 43 44 45 46 @@ -490,7 +490,7 @@ void ObjectMgr::LoadCreatureTemplate(Field* fields) creatureTemplate.GossipMenuId = fields[14].GetUInt32(); creatureTemplate.minlevel = fields[15].GetUInt8(); creatureTemplate.maxlevel = fields[16].GetUInt8(); - creatureTemplate.expansion = uint32(fields[17].GetInt16()); + creatureTemplate.HealthScalingExpansion = fields[17].GetInt32(); creatureTemplate.expansionUnknown = uint32(fields[18].GetUInt16()); creatureTemplate.faction = uint32(fields[19].GetUInt16()); creatureTemplate.npcflag = fields[20].GetUInt32(); @@ -838,10 +838,10 @@ void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo) if (!ok2) continue; - if (cInfo->expansion > difficultyInfo->expansion) + if (cInfo->HealthScalingExpansion >= 0 && cInfo->HealthScalingExpansion > difficultyInfo->HealthScalingExpansion) { - TC_LOG_ERROR("sql.sql", "Creature (Entry: %u, exp: %u) has different `exp` in difficulty %u mode (Entry: %u, exp: %u).", - cInfo->Entry, cInfo->expansion, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->expansion); + TC_LOG_ERROR("sql.sql", "Creature (Entry: %u, exp: %u) has different `HealthScalingExpansion` in difficulty %u mode (Entry: %u, exp: %u).", + cInfo->Entry, cInfo->HealthScalingExpansion, diff + 1, cInfo->DifficultyEntry[diff], difficultyInfo->HealthScalingExpansion); } if (cInfo->minlevel > difficultyInfo->minlevel) @@ -1097,10 +1097,10 @@ void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo) const_cast(cInfo)->MovementType = IDLE_MOTION_TYPE; } - if (cInfo->expansion > (MAX_EXPANSIONS - 1)) + if (cInfo->HealthScalingExpansion > (MAX_EXPANSIONS - 1)) { - TC_LOG_ERROR("sql.sql", "Table `creature_template` lists creature (Entry: %u) with expansion %u. Ignored and set to 0.", cInfo->Entry, cInfo->expansion); - const_cast(cInfo)->expansion = 0; + TC_LOG_ERROR("sql.sql", "Table `creature_template` lists creature (Entry: %u) with `HealthScalingExpansion` %u. Ignored and set to 0.", cInfo->Entry, cInfo->HealthScalingExpansion); + const_cast(cInfo)->HealthScalingExpansion = 0; } if (cInfo->expansionUnknown > MAX_EXPANSIONS) diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index 694eae0af1..ae663592a4 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -54,6 +54,7 @@ enum LootModes enum Expansions { + EXPANSION_LEVEL_CURRENT = -1, EXPANSION_CLASSIC = 0, EXPANSION_THE_BURNING_CRUSADE = 1, EXPANSION_WRATH_OF_THE_LICH_KING = 2, @@ -61,6 +62,8 @@ enum Expansions MAX_EXPANSIONS = 4 }; +constexpr uint8 CURRENT_EXPANSION = EXPANSION_CATACLYSM; + enum Gender { GENDER_MALE = 0, diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 5de26c1a99..41cd7b7dd5 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -581,7 +581,7 @@ int32 SpellEffectInfo::CalcBaseValue(WorldObject const* caster, Unit const* targ if (Creature const* creatureCaster = Object::ToCreature(caster)) { uint8 casterClass = creatureCaster->getClass(); - uint8 expansion = creatureCaster->GetCreatureTemplate()->expansion; + uint8 expansion = creatureCaster->GetCreatureTemplate()->GetHealthScalingExpansion(); float spellDamage = GetGameTableColumnForClass(sNpcDamageByClassGameTable[expansion].GetRow(_spellInfo->SpellLevel), casterClass); float creatureDamage = GetGameTableColumnForClass(sNpcDamageByClassGameTable[expansion].GetRow(creatureCaster->getLevel()), casterClass);