Skip to content

Commit

Permalink
Utilize ClassLevelStats for pets
Browse files Browse the repository at this point in the history
Fix low damage, attempt.... one too many.
Non-controllable pets should pull as much info from there as possible;
hunters pets need to use pet_levelstats, and so do summon_pets
(not all of the info, though)
If expansion is not set for the pet it will fall back
to the other calculation and output a DB error.

Sources:
Calculation:
https://github.com/cmangos/issues/wiki/Creature_template#DamageMultiplier
Player's Guardian Pets should most likely not scale; (need more research):
http://wowwiki.wikia.com/wiki/Mechanical_Dragonling (look at the patch notes between vanilla and TBC)
  • Loading branch information
Phatcat authored and Fabian committed Aug 29, 2016
1 parent 2538d40 commit 396954d
Showing 1 changed file with 161 additions and 80 deletions.
241 changes: 161 additions & 80 deletions src/game/Pet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -822,6 +822,8 @@ void Pet::InitStatsForLevel(uint32 petlevel)
for (int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
SetModifierValue(UnitMods(UNIT_MOD_RESISTANCE_START + i), BASE_VALUE, float(createResistance[i]));

float health, mana, armor, minDmg;

switch (getPetType())
{
case HUNTER_PET:
Expand All @@ -842,24 +844,31 @@ void Pet::InitStatsForLevel(uint32 petlevel)
UpdateModelData();
}

uint32 maxlevel = sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL);

if (petlevel < maxlevel)
// Max level
if (petlevel < sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL))
SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, sObjectMgr.GetXPForPetLevel(petlevel));

else
{
SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, 0);
SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, 1000);
}

// Info found in pet_levelstats
if (PetLevelInfo const* pInfo = sObjectMgr.GetPetLevelInfo(1, petlevel))
{
for (int i = STAT_STRENGTH; i < MAX_STATS;++i)
SetCreateStat(Stats(i), float(pInfo->stats[i]));

SetCreateHealth(pInfo->health);
SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, pInfo->armor);
health = pInfo->health;
mana = 0;
armor = pInfo->armor;

// First we divide attack time by standard attack time, and then multipy by level and damage mod.
uint32 mDmg = (GetAttackTime(BASE_ATTACK) * petlevel) / 2000;

// Set damage
SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(mDmg - mDmg / 4));
SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float((mDmg - mDmg / 4) * 1.5));
}
else
{
Expand All @@ -868,124 +877,163 @@ void Pet::InitStatsForLevel(uint32 petlevel)
for (int i = STAT_STRENGTH; i < MAX_STATS;++i)
SetCreateStat(Stats(i), 1.0f);

SetCreateHealth(1);
SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, 1);
}
health = 1;
mana = 0;
armor = 0;

// First we divide attack time by standard attack time, and then multipy by level and damage mod.
uint32 mDmg = (GetAttackTime(BASE_ATTACK) * petlevel) / 2000;
SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, (mDmg - mDmg / 5));
SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, (mDmg + mDmg / 5));
// damage is increased afterwards as strength and pet scaling modify attack power
// Set damage
SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, 1);
SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, 1);
}

break;
}
case SUMMON_PET:
{
switch (owner->getClass())
if (owner)
{
case CLASS_WARLOCK:
switch (owner->getClass())
{
// the damage bonus used for pets is either fire or shadow damage, whatever is higher
uint32 fire = owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_FIRE);
uint32 shadow = owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_SHADOW);
uint32 val = (fire > shadow) ? fire : shadow;
case CLASS_WARLOCK:
{
// the damage bonus used for pets is either fire or shadow damage, whatever is higher
uint32 fire = owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_FIRE);
uint32 shadow = owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_SHADOW);
uint32 val = (fire > shadow) ? fire : shadow;

SetBonusDamage(int32(val * 0.15f));
// bonusAP += val * 0.57;
break;
}
case CLASS_MAGE:
{
// 40% damage bonus of mage's frost damage
float val = owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_FROST) * 0.4f;
if (val < 0)
val = 0;
SetBonusDamage(int32(val));
break;
SetBonusDamage(int32(val * 0.15f));
// bonusAP += val * 0.57;
break;
}
case CLASS_MAGE:
{
// 40% damage bonus of mage's frost damage
float val = owner->GetUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS + SPELL_SCHOOL_FROST) * 0.4f;
if (val < 0)
val = 0;
SetBonusDamage(int32(val));
break;
}
default:
break;
}
default:
break;
}
else
sLog.outError("Pet::InitStatsForLevel> No owner for creature pet %s !", GetGuidStr().c_str());

SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, 0);
SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, 1000);

float dMaxLevel = cInfo->MaxMeleeDmg / cInfo->MaxLevel;
float dMinLevel = cInfo->MinMeleeDmg / cInfo->MinLevel;
float mDmg = (dMaxLevel - ((dMaxLevel - dMinLevel) / 2)) * petlevel;

SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, (mDmg - mDmg / 5));
SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, (mDmg + mDmg / 5));

// Info found in pet_levelstats
if (PetLevelInfo const* pInfo = sObjectMgr.GetPetLevelInfo(cInfo->Entry, petlevel))
{
for (int i = STAT_STRENGTH; i < MAX_STATS;++i)
SetCreateStat(Stats(i), float(pInfo->stats[i]));

SetCreateHealth(pInfo->health);
SetCreateMana(pInfo->mana);
SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, pInfo->armor);
health = pInfo->health;
mana = pInfo->mana;
armor = pInfo->armor;

// Info found in ClassLevelStats
if (CreatureClassLvlStats const* cCLS = sObjectMgr.GetCreatureClassLvlStats(petlevel, cInfo->UnitClass, cInfo->Expansion))
{
minDmg = (cCLS->BaseDamage * cInfo->DamageVariance + (cCLS->BaseMeleeAttackPower / 14) * (cInfo->MeleeBaseAttackTime/1000)) * cInfo->DamageMultiplier;

// Apply custom damage setting (from config)
minDmg *= _GetDamageMod(cInfo->Rank);

SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(minDmg));
SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(minDmg * 1.5));
}
else
{
sLog.outErrorDb("SUMMON_PET creature_template not finished (expansion field = -1) on creature %s! (entry: %u)", GetGuidStr().c_str(), cInfo->Entry);

float dMinLevel = cInfo->MinMeleeDmg / cInfo->MinLevel;
float dMaxLevel = cInfo->MaxMeleeDmg / cInfo->MaxLevel;
float mDmg = (dMaxLevel - ((dMaxLevel - dMinLevel) / 2)) * petlevel;

// Set damage
SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(mDmg - mDmg / 4));
SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float((mDmg - mDmg / 4) * 1.5));
}
}
else
{
sLog.outErrorDb("WARLOCK PET levelstats missing in DB! 'Weakifying' pet");
sLog.outErrorDb("SUMMON_PET levelstats missing in DB! 'Weakifying' pet and giving it mana to make it obvious");

for (int i = STAT_STRENGTH; i < MAX_STATS;++i)
SetCreateStat(Stats(i), 1.0f);

SetCreateHealth(1);
SetCreateMana(1);
SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, 1);
health = 1;
mana = 1;
armor = 1;

// Set damage
SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, 1);
SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, 1);
}

break;
}
case PROTECTOR_PET:
case GUARDIAN_PET:
{
SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(cInfo->MinMeleeDmg));
SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(cInfo->MaxMeleeDmg));
if (CreatureClassLvlStats const* cCLS = sObjectMgr.GetCreatureClassLvlStats(petlevel, cInfo->UnitClass, cInfo->Expansion))
{
health = cCLS->BaseHealth;
mana = cCLS->BaseMana;
armor = cCLS->BaseArmor;

SetBaseWeaponDamage(RANGED_ATTACK, MINDAMAGE, float(cInfo->MinRangedDmg));
SetBaseWeaponDamage(RANGED_ATTACK, MAXDAMAGE, float(cInfo->MaxRangedDmg));
// Melee
minDmg = (cCLS->BaseDamage * cInfo->DamageVariance + (cCLS->BaseMeleeAttackPower / 14) * (cInfo->MeleeBaseAttackTime/1000)) * cInfo->DamageMultiplier;

SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, float(((cInfo->Armor * petlevel) / cInfo->MaxLevel) * cInfo->ArmorMultiplier));
// Get custom setting
minDmg *= _GetDamageMod(cInfo->Rank);

if (petlevel == cInfo->MaxLevel)
{
SetCreateHealth(cInfo->MaxLevelHealth);
SetCreateMana(cInfo->MaxLevelMana);
}
// If the damage value is not passed on as float it will result in damage = 1; but only for guardian type pets, though...
SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(minDmg));
SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(minDmg * 1.5));

else if (petlevel == cInfo->MinLevel)
{
SetCreateHealth(cInfo->MinLevelHealth);
SetCreateMana(cInfo->MinLevelMana);
}
// Ranged
minDmg = (cCLS->BaseDamage * cInfo->DamageVariance + (cCLS->BaseRangedAttackPower / 14) * (cInfo->RangedBaseAttackTime/1000)) * cInfo->DamageMultiplier;

else
{
float hMaxLevel = cInfo->MaxLevelHealth / cInfo->MaxLevel;
float hMinLevel = cInfo->MinLevelHealth / cInfo->MinLevel;
float mMaxLevel = cInfo->MaxLevelMana / cInfo->MaxLevel;
float mMinLevel = cInfo->MinLevelMana / cInfo->MinLevel;
// Get custom setting
minDmg *= _GetDamageMod(cInfo->Rank);

if (petlevel > cInfo->MaxLevel)
SetBaseWeaponDamage(RANGED_ATTACK, MINDAMAGE, float(minDmg));
SetBaseWeaponDamage(RANGED_ATTACK, MAXDAMAGE, float(minDmg * 1.5));
}
else // TODO: Remove fallback to creature_template data when DB is ready
{
if (petlevel >= cInfo->MaxLevel)
{
SetCreateHealth(hMaxLevel * petlevel);
SetCreateMana(mMaxLevel * petlevel);
health = cInfo->MaxLevelHealth;
mana = cInfo->MaxLevelMana;
}
else if (petlevel < cInfo->MinLevel)
else if (petlevel <= cInfo->MinLevel)
{
SetCreateHealth(hMinLevel * petlevel);
SetCreateMana(mMinLevel * petlevel);
health = cInfo->MinLevelHealth;
mana = cInfo->MinLevelMana;
}
else
{
SetCreateHealth((hMaxLevel - ((hMaxLevel - hMinLevel) / 2)) * petlevel);
SetCreateMana((mMaxLevel - ((mMaxLevel - mMinLevel) / 2)) * petlevel);
float hMinLevel = cInfo->MinLevelHealth / cInfo->MinLevel;
float hMaxLevel = cInfo->MaxLevelHealth / cInfo->MaxLevel;
float mMinLevel = cInfo->MinLevelMana / cInfo->MinLevel;
float mMaxLevel = cInfo->MaxLevelMana / cInfo->MaxLevel;

health = (hMaxLevel - ((hMaxLevel - hMinLevel) / 2)) * petlevel;
mana = (mMaxLevel - ((mMaxLevel - mMinLevel) / 2)) * petlevel;
}

sLog.outErrorDb("Pet::InitStatsForLevel> Error trying to set stats for creature %s (entry: %u) using ClassLevelStats; not enough data to do it!", GetGuidStr().c_str(), cInfo->Entry);

SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, float(cInfo->MinMeleeDmg));
SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, float(cInfo->MaxMeleeDmg));

SetBaseWeaponDamage(RANGED_ATTACK, MINDAMAGE, float(cInfo->MinRangedDmg));
SetBaseWeaponDamage(RANGED_ATTACK, MAXDAMAGE, float(cInfo->MaxRangedDmg));
}

break;
Expand All @@ -994,13 +1042,46 @@ void Pet::InitStatsForLevel(uint32 petlevel)
sLog.outError("Pet have incorrect type (%u) for level handling.", getPetType());
}

// Hunter's pets' should NOT use creature's original modifiers/multipliers
if (getPetType() != HUNTER_PET)
{
health *= cInfo->HealthMultiplier;

if (mana > 0)
mana *= cInfo->PowerMultiplier;

armor *= cInfo->ArmorMultiplier;
}

// Apply custom health setting (from config)
health *= _GetHealthMod(cInfo->Rank);

// Need to update stats before setting health and power or it will bug out in-game displaying it as the mob missing about 2/3
UpdateAllStats();

SetHealth(GetMaxHealth());
SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
// A pet cannot not have health
if (health < 1)
health = 1;

// Remove rage bar from pets
// Set health
SetCreateHealth(health);
SetMaxHealth(health);
SetHealth(health);
SetModifierValue(UNIT_MOD_HEALTH, BASE_VALUE, health);

// Set mana
SetCreateMana(mana);
SetMaxPower(POWER_MANA, mana);
SetPower(POWER_MANA, mana);
SetModifierValue(UNIT_MOD_MANA, BASE_VALUE, mana);

// Remove rage bar from pets (By setting rage = 0, and ensuring it stays that way by setting max rage = 0 as well)
SetMaxPower(POWER_RAGE, 0);
SetPower(POWER_RAGE, 0);
SetModifierValue(UNIT_MOD_RAGE, BASE_VALUE, 0);

// Set armor
SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, armor);

return;
}
Expand Down

0 comments on commit 396954d

Please sign in to comment.