Core/DataStores: ported NpcTotalHp game tables from Cata Classic and use them to calculate npc health values

This commit is contained in:
Ovahlord committed 2025-06-07 15:53:41 +02:00
1 parent 94f1ac8498
commit 77efec36ee
8 files changed
+685 -51

No files matched your search

-31
View File
@@ -1,31 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef __GAMETABLE_H
#define __GAMETABLE_H
#include "Define.h"
enum UnitClass : uint8;
class GameTable
{
public:
static float GetSpellScalingValue(uint8 level, UnitClass unitClass);
};
#endif // __GAMETABLE_H
@@ -15,11 +15,13 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "GameTable.h"
#include "GameTables.h"
#include "DBCStores.h"
#include "DBCStructure.h"
/*static*/ float GameTable::GetSpellScalingValue(uint8 level, UnitClass unitClass)
GameTable<GtNpcTotalHpEntry> sNpcTotalHpGameTable[MAX_EXPANSIONS];
/*static*/ float GameTables::GetSpellScalingValue(uint8 level, UnitClass unitClass)
{
if (level > GT_MAX_LEVEL)
return 0.0f;
@@ -30,3 +32,11 @@
return gt->value;
}
/*float*/ float GameTables::GetNpcTotalHpValue(uint8 level, UnitClass unitClass, Expansions expansion)
{
if (level > GT_MAX_LEVEL || expansion >= MAX_EXPANSIONS)
return 0.0f;
return GetGameTableColumnForClass(sNpcTotalHpGameTable[expansion].GetRow(level), unitClass);
}
+104
View File
@@ -0,0 +1,104 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifndef __GAMETABLES_H
#define __GAMETABLES_H
#include "Define.h"
enum UnitClass : uint8;
struct GtNpcTotalHpEntry
{
float Warrior = 0.0f;
float Paladin = 0.0f;
float Hunter = 0.0f;
float Rogue = 0.0f;
float Priest = 0.0f;
float DeathKnight = 0.0f;
float Shaman = 0.0f;
float Mage = 0.0f;
float Warlock = 0.0f;
float Monk = 0.0f;
float Druid = 0.0f;
};
template<class T>
class GameTable
{
public:
T const* GetRow(uint32 row) const
{
if (row >= _data.size())
return nullptr;
return &_data[row];
}
std::size_t GetTableRowCount() const { return _data.size(); }
void SetData(std::vector<T> data) { _data = std::move(data); }
private:
std::vector<T> _data;
};
class GameTables
{
public:
// Helper Methods
static float GetSpellScalingValue(uint8 level, UnitClass unitClass);
static float GetNpcTotalHpValue(uint8 level, UnitClass unitClass, Expansions expansion);
};
TC_GAME_API extern GameTable<GtNpcTotalHpEntry> sNpcTotalHpGameTable[MAX_EXPANSIONS];
template<class T>
inline float GetGameTableColumnForClass(T const* row, int32 class_)
{
switch (class_)
{
case CLASS_WARRIOR:
return row->Warrior;
case CLASS_PALADIN:
return row->Paladin;
case CLASS_HUNTER:
return row->Hunter;
case CLASS_ROGUE:
return row->Rogue;
case CLASS_PRIEST:
return row->Priest;
case CLASS_DEATH_KNIGHT:
return row->DeathKnight;
case CLASS_SHAMAN:
return row->Shaman;
case CLASS_MAGE:
return row->Mage;
case CLASS_WARLOCK:
return row->Warlock;
//case CLASS_MONK:
// return row->Monk;
case CLASS_DRUID:
return row->Druid;
default:
break;
}
return 0.0f;
}
#endif // __GAMETABLES_H
+84 -14
View File
@@ -27,6 +27,7 @@
#include "DB2Structure.h"
#include "DB2Stores.h"
#include "DisableMgr.h"
#include "GameTables.h"
#include "GameTime.h"
#include "GameObject.h"
#include "GameObjectAIFactory.h"
@@ -9554,8 +9555,8 @@ CreatureBaseStats const* ObjectMgr::GetCreatureBaseStats(uint8 level, uint8 unit
void ObjectMgr::LoadCreatureClassLevelStats()
{
uint32 oldMSTime = getMSTime();
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13
QueryResult result = WorldDatabase.Query("SELECT level, class, basehp0, basehp1, basehp2, basehp3, basemana, basearmor, attackpower, rangedattackpower, damage_base, damage_exp1, damage_exp2, damage_exp3 FROM creature_classlevelstats");
// 0 1 2 3 4 5 6 7 8 9
QueryResult result = WorldDatabase.Query("SELECT level, class, basemana, basearmor, attackpower, rangedattackpower, damage_base, damage_exp1, damage_exp2, damage_exp3 FROM creature_classlevelstats");
if (!result)
{
@@ -9578,15 +9579,9 @@ void ObjectMgr::LoadCreatureClassLevelStats()
for (uint8 i = 0; i < MAX_EXPANSIONS; ++i)
{
stats.BaseHealth[i] = fields[2 + i].GetUInt32();
stats.BaseHealth[i] = GetGameTableColumnForClass(sNpcTotalHpGameTable[i].GetRow(Level), Class);
if (stats.BaseHealth[i] == 0)
{
TC_LOG_ERROR("sql.sql", "Creature base stats for class %u, level %u has invalid zero base HP[%u] - set to 1", Class, Level, i);
stats.BaseHealth[i] = 1;
}
stats.BaseDamage[i] = fields[10 + i].GetFloat();
stats.BaseDamage[i] = fields[6 + i].GetFloat();
if (stats.BaseDamage[i] < 0.0f)
{
TC_LOG_ERROR("sql.sql", "Creature base stats for class %u, level %u has invalid negative base damage[%u] - set to 0.0", Class, Level, i);
@@ -9594,11 +9589,11 @@ void ObjectMgr::LoadCreatureClassLevelStats()
}
}
stats.BaseMana = fields[6].GetUInt16();
stats.BaseArmor = fields[7].GetUInt16();
stats.BaseMana = fields[2].GetUInt16();
stats.BaseArmor = fields[3].GetUInt16();
stats.AttackPower = fields[8].GetUInt16();
stats.RangedAttackPower = fields[9].GetUInt16();
stats.AttackPower = fields[4].GetUInt16();
stats.RangedAttackPower = fields[5].GetUInt16();
_creatureBaseStatsStore[MAKE_PAIR16(Level, Class)] = stats;
@@ -10098,6 +10093,81 @@ CreatureStaticFlagsOverride const* ObjectMgr::GetCreatureStaticFlagsOverride(Obj
return Trinity::Containers::MapGetValuePtr(_creatureStaticFlagsOverrideStore, std::make_pair(spawnId, difficultyId));
}
void ObjectMgr::LoadDatabaseGameTables()
{
LoadNpcTotalHpGameTables();
}
void ObjectMgr::LoadNpcTotalHpGameTables()
{
uint32 oldMSTime = getMSTime();
QueryResult result = WorldDatabase.Query("SELECT exp0.Level, exp0.Warrior, exp1.Warrior, exp2.Warrior, exp3.Warrior, "
"exp0.Paladin, exp1.Paladin, exp2.Paladin, exp3.Paladin, "
"exp0.Hunter, exp1.Hunter, exp2.Hunter, exp3.Hunter, "
"exp0.Rogue, exp1.Rogue, exp2.Rogue, exp3.Rogue, "
"exp0.Priest, exp1.Priest, exp2.Priest, exp3.Priest, "
"exp0.DeathKnight, exp1.DeathKnight, exp2.DeathKnight, exp3.DeathKnight, "
"exp0.Shaman, exp1.Shaman, exp2.Shaman, exp3.Shaman, "
"exp0.Mage, exp1.Mage, exp2.Mage, exp3.Mage, "
"exp0.Warlock, exp1.Warlock, exp2.Warlock, exp3.Warlock, "
"exp0.Monk, exp1.Monk, exp2.Monk, exp3.Monk, "
"exp0.Druid, exp1.Druid, exp2.Druid, exp3.Druid "
"FROM gt_npc_total_hp AS exp0 "
"INNER JOIN gt_npc_total_hp_exp1 AS exp1 ON exp0.Level = exp1.Level "
"INNER JOIN gt_npc_total_hp_exp2 AS exp2 ON exp0.Level = exp2.Level "
"INNER JOIN gt_npc_total_hp_exp3 AS exp3 ON exp0.Level = exp3.Level "
"ORDER BY Level ASC");
if (!result)
{
TC_LOG_INFO("server.loading", ">> Loaded 0 npc total health game table entries. DB table `gt_npc_total_hp` is empty.");
return;
}
uint32 count = 0;
// Initialize the storages with one extra entry for level 0
std::vector<GtNpcTotalHpEntry> npcTotalHpExp0(result->GetRowCount() + 1);
std::vector<GtNpcTotalHpEntry> npcTotalHpExp1(result->GetRowCount() + 1);
std::vector<GtNpcTotalHpEntry> npcTotalHpExp2(result->GetRowCount() + 1);
std::vector<GtNpcTotalHpEntry> npcTotalHpExp3(result->GetRowCount() + 1);
do
{
Field* fields = result->Fetch();
uint8 level = fields[0].GetUInt8();
uint8 expansion = 0;
for (std::vector<GtNpcTotalHpEntry>* storage : { &npcTotalHpExp0, &npcTotalHpExp1, &npcTotalHpExp2, &npcTotalHpExp3 })
{
std::vector<GtNpcTotalHpEntry>& npcTotalHp = *storage;
npcTotalHp[level].Warrior = fields[1 + expansion].GetFloat();
npcTotalHp[level].Paladin = fields[5 + expansion].GetFloat();
npcTotalHp[level].Hunter = fields[9 + expansion].GetFloat();
npcTotalHp[level].Rogue = fields[13 + expansion].GetFloat();
npcTotalHp[level].Priest = fields[17 + expansion].GetFloat();
npcTotalHp[level].DeathKnight = fields[21 + expansion].GetFloat();
npcTotalHp[level].Shaman = fields[25 + expansion].GetFloat();
npcTotalHp[level].Mage = fields[29 + expansion].GetFloat();
npcTotalHp[level].Warlock = fields[33 + expansion].GetFloat();
npcTotalHp[level].Monk = fields[37 + expansion].GetFloat();
npcTotalHp[level].Druid = fields[41 + expansion].GetFloat();
++expansion;
}
}
while (result->NextRow());
sNpcTotalHpGameTable[EXPANSION_CLASSIC].SetData(npcTotalHpExp0);
sNpcTotalHpGameTable[EXPANSION_THE_BURNING_CRUSADE].SetData(npcTotalHpExp1);
sNpcTotalHpGameTable[EXPANSION_WRATH_OF_THE_LICH_KING].SetData(npcTotalHpExp2);
sNpcTotalHpGameTable[EXPANSION_CATACLYSM].SetData(npcTotalHpExp3);
TC_LOG_INFO("server.loading", ">> Loaded %u npc total health game table entries in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
}
void ObjectMgr::LoadGameObjectQuestItems()
{
uint32 oldMSTime = getMSTime();
+3
View File
@@ -1611,6 +1611,9 @@ class TC_GAME_API ObjectMgr
CreatureStaticFlagsOverride const* GetCreatureStaticFlagsOverride(ObjectGuid::LowType spawnId, Difficulty difficultyId) const;
void LoadDatabaseGameTables();
void LoadNpcTotalHpGameTables();
private:
// first free id for selected id type
uint32 _auctionId;
+3 -3
View File
@@ -21,7 +21,7 @@
#include "Creature.h"
#include "DBCStores.h"
#include "GameClient.h"
#include "GameTable.h"
#include "GameTables.h"
#include "InstanceScript.h"
#include "Item.h"
#include "ItemTemplate.h"
@@ -518,7 +518,7 @@ int32 SpellEffectInfo::CalcBaseValue(WorldObject const* caster, Unit const* targ
if (!_spellInfo->Scaling.Class)
return 0;
value = GameTable::GetSpellScalingValue(level, static_cast<UnitClass>(_spellInfo->Scaling.Class > 0 ? _spellInfo->Scaling.Class: (MAX_CLASSES - 1 /*last class*/) - _spellInfo->Scaling.Class));
value = GameTables::GetSpellScalingValue(level, static_cast<UnitClass>(_spellInfo->Scaling.Class > 0 ? _spellInfo->Scaling.Class: (MAX_CLASSES - 1 /*last class*/) - _spellInfo->Scaling.Class));
value *= _spellInfo->GetSpellScalingMultiplier(level, false);
}
@@ -550,7 +550,7 @@ double SpellEffectInfo::CalcPointsPerResource(WorldObject const* caster) const
if (scalingClass < 0)
scalingClass = (MAX_CLASSES - 1 /*last class*/) - scalingClass;
float scaledAmount = GameTable::GetSpellScalingValue(caster->ToUnit()->getLevel(), static_cast<UnitClass>(scalingClass));
float scaledAmount = GameTables::GetSpellScalingValue(caster->ToUnit()->getLevel(), static_cast<UnitClass>(scalingClass));
scaledAmount *= _spellInfo->GetSpellScalingMultiplier(caster->ToUnit()->getLevel(), false) * Scaling.ComboPointsCoefficient;
value = scaledAmount;
}
+4 -1
View File
@@ -1901,7 +1901,10 @@ void World::SetInitialWorldSettings()
TC_LOG_INFO("server.loading", "Loading Points Of Interest Data...");
sObjectMgr->LoadPointsOfInterest();
TC_LOG_INFO("server.loading", "Loading Creature Base Stats...");
TC_LOG_INFO("server.loading", "Loading Database Game Tables...");
sObjectMgr->LoadDatabaseGameTables();
TC_LOG_INFO("server.loading", "Loading Creature Base Stats..."); // must be after LoadDatabaseGameTables
sObjectMgr->LoadCreatureClassLevelStats();
TC_LOG_INFO("server.loading", "Loading Spawn Group Templates...");