From 13aa87cce5f419ce583a9a59efde8f172d87434e Mon Sep 17 00:00:00 2001 From: killerwife Date: Sun, 21 Apr 2024 18:21:27 +0200 Subject: [PATCH] [14085] Creature: Implement base stat usage for npcs Works retroactively with old data since that one was correct for what we had --- sql/base/mangos.sql | 7 +- .../14085_01_mangos_creature_cls_stats.sql | 15 ++++ src/game/Entities/Creature.cpp | 44 +++++++++- src/game/Entities/Creature.h | 12 ++- src/game/Entities/Player.h | 5 -- src/game/Entities/StatSystem.cpp | 86 ++++++++++--------- src/game/Entities/Unit.h | 10 ++- src/game/Globals/ObjectMgr.cpp | 27 +++++- src/game/Server/SQLStorages.cpp | 4 +- src/shared/revision_sql.h | 2 +- 10 files changed, 152 insertions(+), 60 deletions(-) create mode 100644 sql/updates/mangos/14085_01_mangos_creature_cls_stats.sql diff --git a/sql/base/mangos.sql b/sql/base/mangos.sql index d4d7aefde53..ce06ddf330e 100644 --- a/sql/base/mangos.sql +++ b/sql/base/mangos.sql @@ -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'; -- @@ -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', diff --git a/sql/updates/mangos/14085_01_mangos_creature_cls_stats.sql b/sql/updates/mangos/14085_01_mangos_creature_cls_stats.sql new file mode 100644 index 00000000000..d576b960e79 --- /dev/null +++ b/sql/updates/mangos/14085_01_mangos_creature_cls_stats.sql @@ -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`; + + diff --git a/src/game/Entities/Creature.cpp b/src/game/Entities/Creature.cpp index 65f959cc2f4..9df6b3592d0 100644 --- a/src/game/Entities/Creature.cpp +++ b/src/game/Entities/Creature.cpp @@ -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; @@ -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) @@ -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 @@ -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(); } diff --git a/src/game/Entities/Creature.h b/src/game/Entities/Creature.h index b0a2de52069..6f7cd17d40f 100644 --- a/src/game/Entities/Creature.h +++ b/src/game/Entities/Creature.h @@ -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; @@ -308,6 +313,11 @@ struct CreatureClassLvlStats float BaseMeleeAttackPower; float BaseRangedAttackPower; uint32 BaseArmor; + uint32 Strength; + uint32 Agility; + uint32 Stamina; + uint32 Intellect; + uint32 Spirit; }; struct CreatureModelInfo @@ -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; } diff --git a/src/game/Entities/Player.h b/src/game/Entities/Player.h index bd3fe9e1ed4..1c1446758aa 100644 --- a/src/game/Entities/Player.h +++ b/src/game/Entities/Player.h @@ -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(); diff --git a/src/game/Entities/StatSystem.cpp b/src/game/Entities/StatSystem.cpp index 1b24115093e..f348aae2784 100644 --- a/src/game/Entities/StatSystem.cpp +++ b/src/game/Entities/StatSystem.cpp @@ -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; @@ -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); @@ -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); @@ -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; @@ -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) diff --git a/src/game/Entities/Unit.h b/src/game/Entities/Unit.h index 3f7a3091f05..f282a12237f 100644 --- a/src/game/Entities/Unit.h +++ b/src/game/Entities/Unit.h @@ -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; diff --git a/src/game/Globals/ObjectMgr.cpp b/src/game/Globals/ObjectMgr.cpp index 92ea98684f2..75641282fd4 100644 --- a/src/game/Globals/ObjectMgr.cpp +++ b/src/game/Globals/ObjectMgr.cpp @@ -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) @@ -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) { @@ -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(); + + // 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; } diff --git a/src/game/Server/SQLStorages.cpp b/src/game/Server/SQLStorages.cpp index fbf9e88ed22..3252322072b 100644 --- a/src/game/Server/SQLStorages.cpp +++ b/src/game/Server/SQLStorages.cpp @@ -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"; diff --git a/src/shared/revision_sql.h b/src/shared/revision_sql.h index 14da67736de..df065681c96 100644 --- a/src/shared/revision_sql.h +++ b/src/shared/revision_sql.h @@ -3,5 +3,5 @@ #define REVISION_DB_REALMD "required_14083_01_realmd_joindate_datetime" #define REVISION_DB_LOGS "required_14039_01_logs_anticheat" #define REVISION_DB_CHARACTERS "required_14061_01_characters_fishingSteps" - #define REVISION_DB_MANGOS "required_14084_01_mangos_charmed_spell_list" + #define REVISION_DB_MANGOS "required_14085_01_mangos_creature_cls_stats" #endif // __REVISION_SQL_H__