Skip to content

Commit

Permalink
[14085] Creature: Implement base stat usage for npcs
Browse files Browse the repository at this point in the history
Works retroactively with old data since that one was correct for what we had
  • Loading branch information
killerwife committed Apr 21, 2024
1 parent 16c1724 commit 13aa87c
Show file tree
Hide file tree
Showing 10 changed files with 152 additions and 60 deletions.
7 changes: 6 additions & 1 deletion sql/base/mangos.sql
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ CREATE TABLE `db_version` (
`version` varchar(120) DEFAULT NULL,
`creature_ai_version` varchar(120) DEFAULT NULL,
`cache_id` int(10) DEFAULT '0',
`required_14084_01_mangos_charmed_spell_list` bit(1) DEFAULT NULL
`required_14085_01_mangos_creature_cls_stats` bit(1) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='Used DB version notes';

--
Expand Down Expand Up @@ -1540,6 +1540,11 @@ CREATE TABLE `creature_template` (
`DamageVariance` float NOT NULL DEFAULT '1',
`ArmorMultiplier` float NOT NULL DEFAULT '1',
`ExperienceMultiplier` float NOT NULL DEFAULT '1',
`StrengthMultiplier` float NOT NULL DEFAULT '1',
`AgilityMultiplier` float NOT NULL DEFAULT '1',
`StaminaMultiplier` float NOT NULL DEFAULT '1',
`IntellectMultiplier` float NOT NULL DEFAULT '1',
`SpiritMultiplier` float NOT NULL DEFAULT '1',
`MinLevelHealth` int(10) unsigned NOT NULL DEFAULT '0',
`MaxLevelHealth` int(10) unsigned NOT NULL DEFAULT '0',
`MinLevelMana` int(10) unsigned NOT NULL DEFAULT '0',
Expand Down
15 changes: 15 additions & 0 deletions sql/updates/mangos/14085_01_mangos_creature_cls_stats.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
ALTER TABLE db_version CHANGE COLUMN required_14084_01_mangos_charmed_spell_list required_14085_01_mangos_creature_cls_stats bit;

ALTER TABLE creature_template_classlevelstats ADD `Strength` int(11) NOT NULL DEFAULT '0';
ALTER TABLE creature_template_classlevelstats ADD `Agility` int(11) NOT NULL DEFAULT '0';
ALTER TABLE creature_template_classlevelstats ADD `Stamina` int(11) NOT NULL DEFAULT '0';
ALTER TABLE creature_template_classlevelstats ADD `Intellect` int(11) NOT NULL DEFAULT '0';
ALTER TABLE creature_template_classlevelstats ADD `Spirit` int(11) NOT NULL DEFAULT '0';

ALTER TABLE creature_template ADD `StrengthMultiplier` FLOAT NOT NULL DEFAULT 1 AFTER `ExperienceMultiplier`;
ALTER TABLE creature_template ADD `AgilityMultiplier` FLOAT NOT NULL DEFAULT 1 AFTER `StrengthMultiplier`;
ALTER TABLE creature_template ADD `StaminaMultiplier` FLOAT NOT NULL DEFAULT 1 AFTER `AgilityMultiplier`;
ALTER TABLE creature_template ADD `IntellectMultiplier` FLOAT NOT NULL DEFAULT 1 AFTER `StaminaMultiplier`;
ALTER TABLE creature_template ADD `SpiritMultiplier` FLOAT NOT NULL DEFAULT 1 AFTER `IntellectMultiplier`;


44 changes: 40 additions & 4 deletions src/game/Entities/Creature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1330,6 +1330,15 @@ void Creature::SelectLevel(uint32 forcedLevel /*= USE_DEFAULT_DATABASE_LEVEL*/)
float meleeAttackPwr = 0.f;
float rangedAttackPwr = 0.f;

float healthMultiplier = 1.f;
float manaMultiplier = 1.f;

float strength = 0.f;
float agility = 0.f;
float stamina = 0.f;
float intellect = 0.f;
float spirit = 0.f;

float damageMod = _GetDamageMod(rank);
float damageMulti = cinfo->DamageMultiplier * damageMod;
bool usedDamageMulti = false;
Expand All @@ -1338,13 +1347,17 @@ void Creature::SelectLevel(uint32 forcedLevel /*= USE_DEFAULT_DATABASE_LEVEL*/)
{
// Use Creature Stats to calculate stat values

// health
if (cinfo->HealthMultiplier >= 0)
health = std::round(cCLS->BaseHealth * cinfo->HealthMultiplier);
health = cCLS->BaseHealth;
// health
if (cinfo->HealthMultiplier > 0)
healthMultiplier = cinfo->HealthMultiplier;

// mana
if (cinfo->PowerMultiplier >= 0)
mana = std::round(cCLS->BaseMana * cinfo->PowerMultiplier);
mana = cCLS->BaseMana;
// mana
if (cinfo->PowerMultiplier > 0)
manaMultiplier = cinfo->PowerMultiplier;

// armor
if (cinfo->ArmorMultiplier >= 0)
Expand All @@ -1365,6 +1378,18 @@ void Creature::SelectLevel(uint32 forcedLevel /*= USE_DEFAULT_DATABASE_LEVEL*/)
meleeAttackPwr = cCLS->BaseMeleeAttackPower;
rangedAttackPwr = cCLS->BaseRangedAttackPower;
}

// attributes
if (cinfo->StrengthMultiplier >= 0)
strength = cCLS->Strength * cinfo->StrengthMultiplier;
if (cinfo->AgilityMultiplier >= 0)
agility = cCLS->Agility * cinfo->AgilityMultiplier;
if (cinfo->StaminaMultiplier >= 0)
stamina = cCLS->Stamina * cinfo->StaminaMultiplier;
if (cinfo->IntellectMultiplier >= 0)
intellect = cCLS->Intellect * cinfo->IntellectMultiplier;
if (cinfo->SpiritMultiplier >= 0)
spirit = cCLS->Spirit * cinfo->SpiritMultiplier;
}

if (!usedDamageMulti || health == -1 || mana == -1 || armor == -1.f) // some field needs to default to old db fields
Expand Down Expand Up @@ -1479,6 +1504,17 @@ void Creature::SelectLevel(uint32 forcedLevel /*= USE_DEFAULT_DATABASE_LEVEL*/)
SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, meleeAttackPwr * damageMod);
SetModifierValue(UNIT_MOD_ATTACK_POWER_RANGED, BASE_VALUE, rangedAttackPwr * damageMod);

// primary attributes
SetCreateStat(STAT_STRENGTH, strength);
SetCreateStat(STAT_AGILITY, agility);
SetCreateStat(STAT_STAMINA, stamina);
SetCreateStat(STAT_INTELLECT, intellect);
SetCreateStat(STAT_SPIRIT, spirit);

// multipliers
SetModifierValue(UNIT_MOD_HEALTH, TOTAL_PCT, healthMultiplier);
SetModifierValue(UNIT_MOD_MANA, TOTAL_PCT, manaMultiplier);

UpdateAllStats();
}

Expand Down
12 changes: 10 additions & 2 deletions src/game/Entities/Creature.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,11 @@ struct CreatureInfo
float DamageVariance;
float ArmorMultiplier;
float ExperienceMultiplier;
float StrengthMultiplier;
float AgilityMultiplier;
float StaminaMultiplier;
float IntellectMultiplier;
float SpiritMultiplier;
uint32 MinLevelHealth;
uint32 MaxLevelHealth;
uint32 MinLevelMana;
Expand Down Expand Up @@ -308,6 +313,11 @@ struct CreatureClassLvlStats
float BaseMeleeAttackPower;
float BaseRangedAttackPower;
uint32 BaseArmor;
uint32 Strength;
uint32 Agility;
uint32 Stamina;
uint32 Intellect;
uint32 Spirit;
};

struct CreatureModelInfo
Expand Down Expand Up @@ -668,8 +678,6 @@ class Creature : public Unit
bool UpdateAllStats() override;
void UpdateResistances(uint32 school) override;
void UpdateArmor() override;
void UpdateMaxHealth() override;
void UpdateMaxPower(Powers power) override;
void UpdateAttackPowerAndDamage(bool ranged = false) override;
void UpdateDamagePhysical(WeaponAttackType attType) override;
uint32 GetCurrentEquipmentId() const { return m_equipmentId; }
Expand Down
5 changes: 0 additions & 5 deletions src/game/Entities/Player.h
Original file line number Diff line number Diff line change
Expand Up @@ -1880,15 +1880,10 @@ class Player : public Unit
bool UpdateGatherSkill(uint32 SkillId, uint32 SkillValue, uint32 RedLevel, uint32 Multiplicator = 1);
bool UpdateFishingSkill();

float GetHealthBonusFromStamina() const;
float GetManaBonusFromIntellect() const;

bool UpdateStats(Stats stat) override;
bool UpdateAllStats() override;
void UpdateResistances(uint32 school) override;
void UpdateArmor() override;
void UpdateMaxHealth() override;
void UpdateMaxPower(Powers power) override;
void ApplyFeralAPBonus(int32 amount, bool apply);
void UpdateAttackPowerAndDamage(bool ranged = false) override;
void UpdateShieldBlockValue();
Expand Down
86 changes: 47 additions & 39 deletions src/game/Entities/StatSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -200,27 +200,37 @@ void Player::UpdateArmor()
UpdateAttackPowerAndDamage(); // armor dependent auras update for SPELL_AURA_MOD_ATTACK_POWER_OF_ARMOR
}

float Player::GetHealthBonusFromStamina() const
float Unit::GetHealthBonusFromStamina(float stamina)
{
float stamina = GetStat(STAT_STAMINA);

float baseStam = stamina < 20 ? stamina : 20;
float moreStam = stamina - baseStam;

return baseStam + (moreStam * 10.0f);
}

float Player::GetManaBonusFromIntellect() const
float Unit::GetHealthBonusFromStamina() const
{
float intellect = GetStat(STAT_INTELLECT);
float stamina = GetStat(STAT_STAMINA);

return Unit::GetHealthBonusFromStamina(stamina);
}

float Unit::GetManaBonusFromIntellect(float intellect)
{
float baseInt = intellect < 20 ? intellect : 20;
float moreInt = intellect - baseInt;

return baseInt + (moreInt * 15.0f);
}

void Player::UpdateMaxHealth()
float Unit::GetManaBonusFromIntellect() const
{
float intellect = GetStat(STAT_INTELLECT);

return Unit::GetManaBonusFromIntellect(intellect);
}

void Unit::UpdateMaxHealth()
{
UnitMods unitMod = UNIT_MOD_HEALTH;

Expand All @@ -232,7 +242,7 @@ void Player::UpdateMaxHealth()
SetMaxHealth((uint32)value);
}

void Player::UpdateMaxPower(Powers power)
void Unit::UpdateMaxPower(Powers power)
{
UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + power);

Expand Down Expand Up @@ -846,6 +856,13 @@ bool Creature::UpdateStats(Stats /*stat*/)

bool Creature::UpdateAllStats()
{
for (int i = STAT_STRENGTH; i < MAX_STATS; ++i)
{
float value = GetTotalStatValue(Stats(i));
SetStat(Stats(i), (int32)value);
}

UpdateArmor();
UpdateMaxHealth();
UpdateAttackPowerAndDamage();
UpdateAttackPowerAndDamage(true);
Expand All @@ -872,36 +889,20 @@ void Creature::UpdateResistances(uint32 school)

void Creature::UpdateArmor()
{
float dynamic = (GetStat(STAT_AGILITY) * 2.0f);

m_auraModifiersGroup[UNIT_MOD_ARMOR][TOTAL_VALUE] += dynamic;
int32 value = GetTotalResistanceValue(SPELL_SCHOOL_NORMAL);
int32 oldValue = GetArmor();
SetArmor(value);
}

void Creature::UpdateMaxHealth()
{
UnitMods unitMod = UNIT_MOD_HEALTH;

float value = GetModifierValue(unitMod, BASE_VALUE) + GetCreateHealth();
value *= GetModifierValue(unitMod, BASE_PCT);
value += GetModifierValue(unitMod, TOTAL_VALUE);
value *= GetModifierValue(unitMod, TOTAL_PCT);

SetMaxHealth((uint32)value);
}

void Creature::UpdateMaxPower(Powers power)
{
UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + power);

float value = GetModifierValue(unitMod, BASE_VALUE) + GetCreatePowers(power);
value *= GetModifierValue(unitMod, BASE_PCT);
value += GetModifierValue(unitMod, TOTAL_VALUE);
value *= GetModifierValue(unitMod, TOTAL_PCT);

SetMaxPower(power, uint32(value));
m_auraModifiersGroup[UNIT_MOD_ARMOR][TOTAL_VALUE] -= dynamic;
}

void Creature::UpdateAttackPowerAndDamage(bool ranged)
{
float val2 = 0.0f;
float level = float(GetLevel());

UnitMods unitMod = ranged ? UNIT_MOD_ATTACK_POWER_RANGED : UNIT_MOD_ATTACK_POWER;

uint16 index = UNIT_FIELD_ATTACK_POWER;
Expand All @@ -913,25 +914,32 @@ void Creature::UpdateAttackPowerAndDamage(bool ranged)
index = UNIT_FIELD_RANGED_ATTACK_POWER;
index_mod = UNIT_FIELD_RANGED_ATTACK_POWER_MODS;
index_mult = UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER;

val2 = GetStat(STAT_AGILITY) - 10.0f;
}
else
{
val2 = (GetStat(STAT_STRENGTH) - 10.0f) * 2.f;
}

float base_attPower = GetModifierValue(unitMod, BASE_VALUE) * GetModifierValue(unitMod, BASE_PCT);
SetModifierValue(unitMod, BASE_VALUE, val2);
float base_attPower = GetModifierValue(unitMod, BASE_VALUE) * GetModifierValue(unitMod, BASE_PCT);
float attPowerMod = GetModifierValue(unitMod, TOTAL_VALUE);

float attPowerMultiplier = GetModifierValue(unitMod, TOTAL_PCT) - 1.0f;

SetInt32Value(index, (uint32)base_attPower); // UNIT_FIELD_(RANGED)_ATTACK_POWER field
SetInt32Value(index_mod, (uint32)attPowerMod); // UNIT_FIELD_(RANGED)_ATTACK_POWER_MODS field
SetFloatValue(index_mult, attPowerMultiplier); // UNIT_FIELD_(RANGED)_ATTACK_POWER_MULTIPLIER field

if (ranged)
// automatically update weapon damage after attack power modification
if (!ranged)
{
UpdateDamagePhysical(RANGED_ATTACK);
return;
UpdateDamagePhysical(BASE_ATTACK);
UpdateDamagePhysical(OFF_ATTACK);
}

// automatically update weapon damage after attack power modification
UpdateDamagePhysical(BASE_ATTACK);
UpdateDamagePhysical(OFF_ATTACK);
else
UpdateDamagePhysical(RANGED_ATTACK);
}

void Creature::UpdateDamagePhysical(WeaponAttackType attType)
Expand Down
10 changes: 8 additions & 2 deletions src/game/Entities/Unit.h
Original file line number Diff line number Diff line change
Expand Up @@ -2131,12 +2131,18 @@ class Unit : public WorldObject
Powers GetPowerTypeByAuraGroup(UnitMods unitMod) const;
bool CanModifyStats() const { return m_canModifyStats; }
void SetCanModifyStats(bool modifyStats) { m_canModifyStats = modifyStats; }

static float GetHealthBonusFromStamina(float stamina);
float GetHealthBonusFromStamina() const;
static float GetManaBonusFromIntellect(float intellect);
float GetManaBonusFromIntellect() const;

virtual bool UpdateStats(Stats stat) = 0;
virtual bool UpdateAllStats() = 0;
virtual void UpdateResistances(uint32 school) = 0;
virtual void UpdateArmor() = 0;
virtual void UpdateMaxHealth() = 0;
virtual void UpdateMaxPower(Powers power) = 0;
virtual void UpdateMaxHealth();
virtual void UpdateMaxPower(Powers power);
virtual void UpdateAttackPowerAndDamage(bool ranged = false) = 0;
virtual void UpdateDamagePhysical(WeaponAttackType attType) = 0;
float GetTotalAttackPowerValue(WeaponAttackType attType) const;
Expand Down
27 changes: 23 additions & 4 deletions src/game/Globals/ObjectMgr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -907,7 +907,7 @@ void ObjectMgr::LoadCreatureClassLvlStats()
// initialize data array
memset(&m_creatureClassLvlStats, 0, sizeof(m_creatureClassLvlStats));

std::string queryStr = "SELECT Class, Level, BaseMana, BaseMeleeAttackPower, BaseRangedAttackPower, BaseArmor";
std::string queryStr = "SELECT Class, Level, BaseMana, BaseMeleeAttackPower, BaseRangedAttackPower, BaseArmor, Strength, Agility, Stamina, Intellect, Spirit";

std::string expData;
for (int i = 0; i <= MAX_EXPANSION; ++i)
Expand Down Expand Up @@ -957,6 +957,11 @@ void ObjectMgr::LoadCreatureClassLvlStats()
float baseMeleeAttackPower = fields[3].GetFloat();
float baseRangedAttackPower = fields[4].GetFloat();
uint32 baseArmor = fields[5].GetUInt32();
uint32 strength = fields[6].GetUInt32();
uint32 agility = fields[7].GetUInt32();
uint32 stamina = fields[8].GetUInt32();
uint32 intellect = fields[9].GetUInt32();
uint32 spirit = fields[10].GetUInt32();

for (int i = 0; i <= MAX_EXPANSION; ++i)
{
Expand All @@ -966,9 +971,23 @@ void ObjectMgr::LoadCreatureClassLvlStats()
cCLS.BaseMeleeAttackPower = baseMeleeAttackPower;
cCLS.BaseRangedAttackPower = baseRangedAttackPower;
cCLS.BaseArmor = baseArmor;

cCLS.BaseHealth = fields[6 + (i * 2)].GetUInt32();
cCLS.BaseDamage = fields[7 + (i * 2)].GetFloat();
cCLS.Strength = strength;
cCLS.Agility = agility;
cCLS.Stamina = stamina;
cCLS.Intellect = intellect;
cCLS.Spirit = spirit;

cCLS.BaseHealth = fields[11 + (i * 2)].GetUInt32();
cCLS.BaseDamage = fields[11 + (i * 2)].GetFloat();

This comment has been minimized.

Copy link
@Exxenoz

Exxenoz Apr 25, 2024

Member

Shouldn't this be 12 + (i * 2)?

This comment has been minimized.

Copy link
@killerwife

killerwife Apr 26, 2024

Author Contributor

y


// should ensure old data does not need change (not wanting to recalculate to avoid losing data)
// if any mistake is made, it will be in these formulae that make asumptions about the new calculations
// AP, RAP, HP, Mana and armor should stay the same pre-change and post-change when using multipliers == 1
cCLS.BaseHealth -= std::min(cCLS.BaseHealth, std::max(0u, (uint32)Unit::GetHealthBonusFromStamina(cCLS.Stamina)));
cCLS.BaseMana -= std::min(cCLS.BaseMana, std::max(0u, (uint32)Unit::GetManaBonusFromIntellect(cCLS.Intellect)));
cCLS.BaseMeleeAttackPower -= std::min(cCLS.BaseMeleeAttackPower, std::max(0.f, float(cCLS.Strength >= 10 ? (cCLS.Strength - 10) * 2 : 0)));
cCLS.BaseRangedAttackPower -= std::min(cCLS.BaseRangedAttackPower, std::max(0.f, float(cCLS.Agility >= 10 ? (cCLS.Agility - 10) : 0)));
cCLS.BaseArmor -= std::min(cCLS.BaseArmor, std::max(0u, cCLS.Agility * 2));
}
++storedRow;
}
Expand Down
4 changes: 2 additions & 2 deletions src/game/Server/SQLStorages.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@

#include "Server/SQLStorages.h"

const char CreatureInfosrcfmt[] = "isssiiiiiiiiiifiiiiliiiiiiiiiifffiiiiiiiiffffffiiiiffffiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiss";
const char CreatureInfodstfmt[] = "isssiiiiiiiiiifiiiiliiiiiiiiiifffiiiiiiiiffffffiiiiffffiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiisi";
const char CreatureInfosrcfmt[] = "isssiiiiiiiiiifiiiiliiiiiiiiiifffiiiiiiiifffffffffffiiiiffffiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiss";
const char CreatureInfodstfmt[] = "isssiiiiiiiiiifiiiiliiiiiiiiiifffiiiiiiiifffffffffffiiiiffffiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiisi";
const char CreatureDataAddonInfofmt[] = "iibbbiis";
const char CreatureConditionalSpawnSrcFmt[] = "iiix";
const char CreatureConditionalSpawnDstFmt[] = "iii";
Expand Down
Loading

2 comments on commit 13aa87c

@killerwife
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@X-Savior just pinging you so you see the final piece of the puzzle added.

@al3xc1985
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This @killerwife is indeed a masterpiece. It will increase the pve gameplay more than significally. Ty

Please sign in to comment.