* Added support for using a lookup table for creature mana / health

* As long as creature level, class, and the proper expansion they are from are
* set in creature_template, you will have 100% accurate health and mana.
* Research and base stats table done by Malcrom -- THANK YOU!
* Original patch by Kudlaty -- THANK YOU!
* Updated and enhanced by Machiavelli - THANK YOU!
* Optimizations by w12x, MrSmite, and XTZGZoReX -- THANK YOU!
* Final code updating for current rev by XTZGZoReX -- THANK YOU!

--HG--
branch : trunk
This commit is contained in:
Brian
2009-12-20 20:23:18 -07:00
parent c4ac6323b0
commit 5300226fee
11 changed files with 1107 additions and 33 deletions
@@ -1585,8 +1585,9 @@ struct TRINITY_DLL_DECL npc_snake_trap_serpentsAI : public ScriptedAI
IsViper = false;
//We have to reload the states from db for summoned guardians
m_creature->SetMaxHealth(Info->maxhealth);
m_creature->SetHealth(Info->maxhealth);
BaseHealthManaPair pair = m_creature->GenerateHealthMana();
m_creature->SetMaxHealth(pair.first);
m_creature->SetHealth(pair.second);
m_creature->SetStatFloatValue(UNIT_FIELD_MINDAMAGE, Info->mindmg);
m_creature->SetStatFloatValue(UNIT_FIELD_MAXDAMAGE, Info->maxdmg);
+9 -7
View File
@@ -1033,14 +1033,13 @@ void Creature::SelectLevel(const CreatureInfo *cinfo)
uint8 level = minlevel == maxlevel ? minlevel : urand(minlevel, maxlevel);
SetLevel(level);
float rellevel = maxlevel == minlevel ? 0 : (float(level - minlevel))/(maxlevel - minlevel);
BaseHealthManaPair pair = objmgr.GenerateCreatureStats(level, cinfo);
// health
float healthmod = _GetHealthMod(rank);
uint32 minhealth = std::min(cinfo->maxhealth, cinfo->minhealth);
uint32 maxhealth = std::max(cinfo->maxhealth, cinfo->minhealth);
uint32 health = uint32(healthmod * (minhealth + uint32(rellevel*(maxhealth - minhealth))));
uint32 basehp = pair.first;
uint32 health = uint32(basehp * healthmod);
SetCreateHealth(health);
SetMaxHealth(health);
@@ -1048,9 +1047,7 @@ void Creature::SelectLevel(const CreatureInfo *cinfo)
ResetPlayerDamageReq();
// mana
uint32 minmana = std::min(cinfo->maxmana, cinfo->minmana);
uint32 maxmana = std::max(cinfo->maxmana, cinfo->minmana);
uint32 mana = minmana + uint32(rellevel*(maxmana - minmana));
uint32 mana = pair.second;
SetCreateMana(mana);
SetMaxPower(POWER_MANA, mana); //MAX Mana
@@ -2320,3 +2317,8 @@ time_t Creature::GetLinkedCreatureRespawnTime() const
return 0;
}
BaseHealthManaPair Creature::GenerateHealthMana()
{
return objmgr.GenerateCreatureStats(getLevel(), GetCreatureInfo());
}
+35 -6
View File
@@ -81,10 +81,7 @@ struct CreatureInfo
uint32 GossipMenuId;
uint32 minlevel;
uint32 maxlevel;
uint32 minhealth;
uint32 maxhealth;
uint32 minmana;
uint32 maxmana;
uint32 expansion;
uint32 armor;
uint32 faction_A;
uint32 faction_H;
@@ -129,8 +126,8 @@ struct CreatureInfo
char const* AIName;
uint32 MovementType;
uint32 InhabitType;
float unk16;
float unk17;
float ModHealth;
float ModMana;
bool RacialLeader;
uint32 questItems[6];
uint32 movementId;
@@ -165,6 +162,35 @@ struct CreatureInfo
}
};
// Defines base stats for creatures (used to calculate HP/mana).
struct CreatureBaseStats
{
uint8 Expansion;
uint8 Class;
uint32 Level;
uint32 BaseHealth;
uint32 BaseMana;
// Helpers
uint32 GenerateHealth(uint32 level, CreatureInfo const* info) const
{
return uint32((BaseHealth * info->ModHealth) + 0.5f);
}
uint32 GenerateMana(uint32 level, CreatureInfo const* info) const
{
// Mana can be 0.
if (!BaseMana)
return 0;
return uint32((BaseMana * info->ModMana) + 0.5f);
}
};
typedef std::list<CreatureBaseStats> CreatureBaseStatsList;
typedef std::pair<uint32, uint32> BaseHealthManaPair;
struct CreatureLocale
{
std::vector<std::string> Name;
@@ -625,6 +651,9 @@ class TRINITY_DLL_SPEC Creature : public Unit
void SetOriginalEntry(uint32 entry) { m_originalEntry = entry; }
// Provided for script access.
BaseHealthManaPair GenerateHealthMana();
static float _GetDamageMod(int32 Rank);
float m_SightDistance, m_CombatDistance;
+91 -14
View File
@@ -1296,24 +1296,12 @@ void ObjectMgr::LoadCreatures()
}
}
if(cInfo->RegenHealth && data.curhealth < cInfo->minhealth)
{
sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `creature_template`.`RegenHealth`=1 and low current health (%u), `creature_template`.`minhealth`=%u.",guid,data.id,data.curhealth, cInfo->minhealth );
data.curhealth = cInfo->minhealth;
}
if(cInfo->flags_extra & CREATURE_FLAG_EXTRA_INSTANCE_BIND)
{
if(!mapEntry || !mapEntry->IsDungeon())
sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `creature_template`.`flags_extra` including CREATURE_FLAG_EXTRA_INSTANCE_BIND but creature are not in instance.",guid,data.id);
}
if(data.curmana < cInfo->minmana)
{
sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with low current mana (%u), `creature_template`.`minmana`=%u.",guid,data.id,data.curmana, cInfo->minmana );
data.curmana = cInfo->minmana;
}
if(data.spawndist < 0.0f)
{
sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `spawndist`< 0, set to 0.",guid,data.id );
@@ -1494,6 +1482,9 @@ uint32 ObjectMgr::AddCreData(uint32 entry, uint32 team, uint32 mapId, float x, f
if(!cInfo)
return 0;
uint32 level = cInfo->minlevel == cInfo->maxlevel ? cInfo->minlevel : urand(cInfo->minlevel, cInfo->maxlevel); // Only used for extracting creature base stats
BaseHealthManaPair pair = objmgr.GenerateCreatureStats(level, cInfo);
uint32 guid = GenerateLowGuid(HIGHGUID_UNIT);
CreatureData& data = NewOrExistCreatureData(guid);
data.id = entry;
@@ -1507,8 +1498,8 @@ uint32 ObjectMgr::AddCreData(uint32 entry, uint32 team, uint32 mapId, float x, f
data.spawntimesecs = spawntimedelay;
data.spawndist = 0;
data.currentwaypoint = 0;
data.curhealth = cInfo->maxhealth;
data.curmana = cInfo->maxmana;
data.curhealth = pair.first;
data.curmana = pair.second;
data.is_dead = false;
data.movementType = cInfo->MovementType;
data.spawnMask = 1;
@@ -8936,6 +8927,92 @@ void ObjectMgr::RemoveGMTicket(uint64 ticketGuid, int64 source, bool permanently
RemoveGMTicket(ticket, source, permanently);
}
CreatureBaseStats const* ObjectMgr::GetCreatureBaseStats(uint8 expansion, uint8 unitClass, uint32 level)
{
for (CreatureBaseStatsList::const_iterator it = m_creatureBaseStatsList.begin(); it != m_creatureBaseStatsList.end(); ++it)
{
CreatureBaseStats const& stats = (*it);
if (stats.Expansion == expansion && stats.Class == unitClass && stats.Level == level)
return &stats;
}
return NULL;
}
BaseHealthManaPair ObjectMgr::GenerateCreatureStats(uint32 level, CreatureInfo const* info)
{
uint32 health = 1;
uint32 mana = 1;
CreatureBaseStats const* stats = GetCreatureBaseStats(info->expansion, info->unit_class, level);
if (!stats)
{
sLog.outError("Could not find base stats for creature entry %u (base stats level %u)", info->Entry, level);
}
else
{
health = stats->GenerateHealth(level, info);
mana = stats->GenerateMana(level, info);
}
return BaseHealthManaPair(health, mana);
}
void ObjectMgr::LoadCreatureClassLevelStats()
{
QueryResult *result = WorldDatabase.Query("SELECT exp, class, level, basehp, basemana FROM creature_classlevelstats");
if (!result)
{
barGoLink bar(1);
bar.step();
sLog.outString();
sLog.outString(">> Loaded 0 creature base stats. DB table `creature_classlevelstats` is empty.");
return;
}
barGoLink bar(result->GetRowCount());
uint32 counter = 0;
do
{
Field *fields = result->Fetch();
CreatureBaseStats stats = CreatureBaseStats();
stats.Expansion = fields[0].GetUInt8();
stats.Class = fields[1].GetUInt8();
stats.Level = fields[2].GetUInt32();
stats.BaseHealth = fields[3].GetUInt32();
stats.BaseMana = fields[4].GetUInt32();
if (stats.Level > MAX_LEVEL)
{
sLog.outErrorDb("Creature base stats for expansion %u, class %u has invalid level %u (max is %u) - set to %u",
stats.Expansion, stats.Class, stats.Level, MAX_LEVEL, DEFAULT_MAX_LEVEL);
stats.Level = DEFAULT_MAX_LEVEL;
}
if (!stats.Class || ((1 << (stats.Class-1)) & CLASSMASK_ALL_CREATURES) == 0)
sLog.outErrorDb("Creature base stats for expansion %u, level %u has invalid class %u",
stats.Expansion, stats.Level, stats.Class);
if (stats.BaseHealth < 1)
{
sLog.outErrorDb("Creature base stats for expansion %u, class %u, level %u has invalid zero base HP - set to 1",
stats.Expansion, stats.Class, stats.Level);
stats.BaseHealth = 1;
}
m_creatureBaseStatsList.push_back(stats);
bar.step();
++counter;
}
while (result->NextRow());
sLog.outString();
sLog.outString( ">> Loaded %u creature base stats.", counter);
}
bool ObjectMgr::CheckDB() const
{
CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(28511);
+6
View File
@@ -576,6 +576,7 @@ class ObjectMgr
bool LoadTrinityStrings(DatabaseType& db, char const* table, int32 min_value, int32 max_value);
bool LoadTrinityStrings() { return LoadTrinityStrings(WorldDatabase,"trinity_string",MIN_TRINITY_STRING_ID,MAX_TRINITY_STRING_ID); }
void LoadDbScriptStrings();
void LoadCreatureClassLevelStats();
void LoadCreatureLocales();
void LoadCreatureTemplates();
void LoadCreatures();
@@ -649,6 +650,9 @@ class ObjectMgr
void ReturnOrDeleteOldMails(bool serverUp);
CreatureBaseStats const* GetCreatureBaseStats(uint8 expansion, uint8 unitClass, uint32 level);
BaseHealthManaPair GenerateCreatureStats(uint32 level, CreatureInfo const* info);
void SetHighestGuids();
uint32 GenerateLowGuid(HighGuid guidhigh);
uint32 GenerateArenaTeamId();
@@ -1009,6 +1013,8 @@ class ObjectMgr
MailLevelRewardMap m_mailLevelRewardMap;
CreatureBaseStatsList m_creatureBaseStatsList;
typedef std::map<uint32,PetLevelInfo*> PetLevelInfoMap;
// PetLevelInfoMap[creature_id][level]
PetLevelInfoMap petInfo; // [creature_id][level]
+2 -2
View File
@@ -193,8 +193,8 @@ void WorldSession::HandleCreatureQueryOpcode( WorldPacket & recv_data )
data << uint32(ci->Modelid2); // Modelid2
data << uint32(ci->Modelid3); // Modelid3
data << uint32(ci->Modelid4); // Modelid4
data << float(ci->unk16); // unk
data << float(ci->unk17); // unk
data << float(ci->ModHealth); // dmg/hp modifier
data << float(ci->ModMana); // dmg/mana modifier
data << uint8(ci->RacialLeader);
for (uint32 i = 0; i < 6; ++i)
data << uint32(ci->questItems[i]); // itemId[6], quest drop
+3
View File
@@ -1375,6 +1375,9 @@ void World::SetInitialWorldSettings()
sLog.outString("Loading Points Of Interest Data...");
objmgr.LoadPointsOfInterest();
sLog.outString("Loading Creature Base Stats...");
objmgr.LoadCreatureClassLevelStats();
sLog.outString("Loading Creature Data...");
objmgr.LoadCreatures();
+2 -2
View File
@@ -27,8 +27,8 @@ extern DatabasePostgre WorldDatabase;
extern DatabaseMysql WorldDatabase;
#endif
const char CreatureInfosrcfmt[]="iiiiiiiiiisssiiiiiiiiiiiffiffiifiiiiiiiiiiffiiiiiiiiiiiiiiiiiiiiiiiisiiffliiiiiiiliiis";
const char CreatureInfodstfmt[]="iiiiiiiiiisssiiiiiiiiiiiffiffiifiiiiiiiiiiffiiiiiiiiiiiiiiiiiiiiiiiisiiffliiiiiiiliiii";
const char CreatureInfosrcfmt[]="iiiiiiiiiisssiiiiiiiiffiffiifiiiiiiiiiiffiiiiiiiiiiiiiiiiiiiiiiiisiiffliiiiiiiliiis";
const char CreatureInfodstfmt[]="iiiiiiiiiisssiiiiiiiiffiffiifiiiiiiiiiiffiiiiiiiiiiiiiiiiiiiiiiiisiiffliiiiiiiliiii";
const char CreatureDataAddonInfofmt[]="iiiiiiis";
const char CreatureModelfmt[]="iffbi";
const char CreatureInfoAddonInfofmt[]="iiiiiiis";