Core/Units: Power handling improvements

* Don't include creature_template power multipliers in CreateMana updatefield
* Allow NPCs to have power types other than mana
* Add missing Essence config regeneration rate
* Fixed demon hunter powers not decaying immediately after combat
* Fixed some powers decaying immediately out of combat after energizing (for example holy power)
* Replace hardcoded list of powers to set to full on levelup with a db2 flag check
* Updated Creature::GetPowerIndex for 10.1 new power types
This commit is contained in:
Shauren
2023-05-09 23:16:30 +02:00
parent 8828af2a5e
commit 1325e0c4b2
17 changed files with 131 additions and 76 deletions

View File

@@ -180,6 +180,7 @@ TC_GAME_API extern DB2Storage<ParagonReputationEntry> sParagonRepu
TC_GAME_API extern DB2Storage<PhaseEntry> sPhaseStore;
TC_GAME_API extern DB2Storage<PlayerConditionEntry> sPlayerConditionStore;
TC_GAME_API extern DB2Storage<PowerDisplayEntry> sPowerDisplayStore;
TC_GAME_API extern DB2Storage<PowerTypeEntry> sPowerTypeStore;
TC_GAME_API extern DB2Storage<PvpTalentEntry> sPvpTalentStore;
TC_GAME_API extern DB2Storage<PvpTalentCategoryEntry> sPvpTalentCategoryStore;
TC_GAME_API extern DB2Storage<PvpTalentSlotUnlockEntry> sPvpTalentSlotUnlockStore;

View File

@@ -1666,18 +1666,18 @@ enum class PlayerInteractionType : int32
enum class PowerTypeFlags : int16
{
StopRegenWhileCasting = 0x0001,
StopRegenWhileCasting = 0x0001, // NYI
UseRegenInterrupt = 0x0002,
FillFractionalPowerOnEnergize = 0x0008,
NoClientPrediction = 0x0010,
FillFractionalPowerOnEnergize = 0x0008, // NYI
NoClientPrediction = 0x0010, // NYI
UnitsUseDefaultPowerOnInit = 0x0020,
NotSetToDefaultOnResurrect = 0x0040,
NotSetToDefaultOnResurrect = 0x0040, // NYI
IsUsedByNPCs = 0x0080,
ContinueRegenWhileFatigued = 0x0200,
RegenAffectedByHaste = 0x0400,
ContinueRegenWhileFatigued = 0x0200, // NYI
RegenAffectedByHaste = 0x0400, // NYI
SetToMaxOnLevelUp = 0x1000,
SetToMaxLevelOnInitialLogIn = 0x2000,
AllowCostModsForPlayers = 0x4000
SetToMaxLevelOnInitialLogIn = 0x2000, // NYI
AllowCostModsForPlayers = 0x4000 // NYI
};
DEFINE_ENUM_FLAG(PowerTypeFlags);

View File

@@ -1563,23 +1563,22 @@ void Creature::UpdateLevelDependantStats()
SetHealth(health);
ResetPlayerDamageReq();
// mana
uint32 mana = stats->GenerateMana(cInfo);
SetCreateMana(mana);
switch (GetClass())
{
case CLASS_PALADIN:
case CLASS_MAGE:
SetMaxPower(POWER_MANA, mana);
SetFullPower(POWER_MANA);
break;
default: // We don't set max power here, 0 makes power bar hidden
break;
}
SetStatFlatModifier(UNIT_MOD_HEALTH, BASE_VALUE, (float)health);
// mana
Powers powerType = CalculateDisplayPowerType();
SetCreateMana(stats->BaseMana);
SetStatPctModifier(UnitMods(UNIT_MOD_POWER_START + AsUnderlyingType(powerType)), BASE_PCT, cInfo->ModMana * cInfo->ModManaExtra);
SetPowerType(powerType);
if (PowerTypeEntry const* powerTypeEntry = sDB2Manager.GetPowerTypeEntry(powerType))
{
if (powerTypeEntry->GetFlags().HasFlag(PowerTypeFlags::UnitsUseDefaultPowerOnInit))
SetPower(powerType, powerTypeEntry->DefaultPower);
else
SetFullPower(powerType);
}
// damage
float basedamage = GetBaseDamageForLevel(level);
@@ -1598,7 +1597,7 @@ void Creature::UpdateLevelDependantStats()
SetStatFlatModifier(UNIT_MOD_ATTACK_POWER, BASE_VALUE, stats->AttackPower);
SetStatFlatModifier(UNIT_MOD_ATTACK_POWER_RANGED, BASE_VALUE, stats->RangedAttackPower);
float armor = GetBaseArmorForLevel(level); /// @todo Why is this treated as uint32 when it's a float?
float armor = GetBaseArmorForLevel(level);
SetStatFlatModifier(UNIT_MOD_ARMOR, BASE_VALUE, armor);
}

View File

@@ -192,6 +192,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
bool UpdateEntry(uint32 entry, CreatureData const* data = nullptr, bool updateLevel = true);
int32 GetCreatePowerValue(Powers power) const override;
bool UpdateStats(Stats stat) override;
bool UpdateAllStats() override;
void UpdateArmor() override;

View File

@@ -616,15 +616,6 @@ struct TC_GAME_API CreatureBaseStats
uint32 RangedAttackPower;
// Helpers
uint32 GenerateMana(CreatureTemplate const* info) const
{
// Mana can be 0.
if (!BaseMana)
return 0;
return uint32(ceil(BaseMana * info->ModMana * info->ModManaExtra));
}
static CreatureBaseStats const* GetBaseStats(uint8 level, uint8 unitClass);
};

View File

@@ -896,6 +896,8 @@ bool Guardian::InitStatsForLevel(uint8 petlevel)
for (uint8 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
SetStatFlatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + i), BASE_VALUE, float(cinfo->resistance[i]));
Powers powerType = CalculateDisplayPowerType();
// Health, Mana or Power, Armor
PetLevelInfo const* pInfo = sObjectMgr->GetPetLevelInfo(creature_ID, petlevel);
if (pInfo) // exist in DB
@@ -903,6 +905,8 @@ bool Guardian::InitStatsForLevel(uint8 petlevel)
SetCreateHealth(pInfo->health);
SetCreateMana(pInfo->mana);
SetStatPctModifier(UnitMods(UNIT_MOD_POWER_START + AsUnderlyingType(powerType)), BASE_PCT, 1.0f);
if (pInfo->armor > 0)
SetStatFlatModifier(UNIT_MOD_ARMOR, BASE_VALUE, float(pInfo->armor));
@@ -916,7 +920,7 @@ bool Guardian::InitStatsForLevel(uint8 petlevel)
ApplyLevelScaling();
SetCreateHealth(sDB2Manager.EvaluateExpectedStat(ExpectedStatType::CreatureHealth, petlevel, cinfo->GetHealthScalingExpansion(), m_unitData->ContentTuningID, Classes(cinfo->unit_class)) * cinfo->ModHealth * cinfo->ModHealthExtra * _GetHealthMod(cinfo->rank));
SetCreateMana(stats->GenerateMana(cinfo));
SetCreateMana(stats->BaseMana);
SetCreateStat(STAT_STRENGTH, 22);
SetCreateStat(STAT_AGILITY, 22);
SetCreateStat(STAT_STAMINA, 25);
@@ -924,17 +928,7 @@ bool Guardian::InitStatsForLevel(uint8 petlevel)
}
// Power
if (petType == HUNTER_PET) // Hunter pets have focus
SetPowerType(POWER_FOCUS);
else if (IsPetGhoul() || IsPetAbomination()) // DK pets have energy
{
SetPowerType(POWER_ENERGY);
SetFullPower(POWER_ENERGY);
}
else if (IsWarlockPet()) // Warlock pets have energy (since 5.x)
SetPowerType(POWER_ENERGY);
else
SetPowerType(POWER_MANA);
SetPowerType(powerType);
// Damage
SetBonusDamage(0);

View File

@@ -167,7 +167,7 @@ Player::Player(WorldSession* session) : Unit(true), m_sceneMgr(this)
if (!GetSession()->HasPermission(rbac::RBAC_PERM_CAN_FILTER_WHISPERS))
SetAcceptWhispers(true);
m_combatExitTime = 0;
m_regenInterruptTimestamp = GameTime::Now();
m_regenTimer = 0;
m_regenTimerCount = 0;
m_foodEmoteTimerCount = 0;
@@ -1780,7 +1780,7 @@ void Player::Regenerate(Powers power)
float addvalue = 0.0f;
if (!IsInCombat())
{
if (powerType->RegenInterruptTimeMS && GetMSTimeDiffToNow(m_combatExitTime) < uint32(powerType->RegenInterruptTimeMS))
if (powerType->GetFlags().HasFlag(PowerTypeFlags::UseRegenInterrupt) && m_regenInterruptTimestamp + Milliseconds(powerType->RegenInterruptTimeMS) < GameTime::Now())
return;
addvalue = (powerType->RegenPeace + m_unitData->PowerRegenFlatModifier[powerIndex]) * 0.001f * m_regenTimer;
@@ -1809,6 +1809,13 @@ void Player::Regenerate(Powers power)
RATE_POWER_ARCANE_CHARGES,
RATE_POWER_FURY,
RATE_POWER_PAIN,
RATE_POWER_ESSENCE,
MAX_RATES, // runes
MAX_RATES, // runes
MAX_RATES, // runes
MAX_RATES, // alternate
MAX_RATES, // alternate
MAX_RATES, // alternate
};
if (RatesForPower[power] != MAX_RATES)
@@ -1903,6 +1910,17 @@ void Player::Regenerate(Powers power)
}
}
void Player::InterruptPowerRegen(Powers power)
{
uint32 powerIndex = GetPowerIndex(power);
if (powerIndex == MAX_POWERS || powerIndex >= MAX_POWERS_PER_CLASS)
return;
m_regenInterruptTimestamp = GameTime::Now();
m_powerFraction[powerIndex] = 0.0f;
SendDirectMessage(WorldPackets::Combat::InterruptPowerRegen(power).Write());
}
void Player::RegenerateHealth()
{
uint32 curValue = GetHealth();
@@ -2382,7 +2400,9 @@ void Player::GiveLevel(uint8 level)
// Only health and mana are set to maximum.
SetFullHealth();
SetFullPower(POWER_MANA);
for (PowerTypeEntry const* powerType : sPowerTypeStore)
if (powerType->GetFlags().HasFlag(PowerTypeFlags::SetToMaxOnLevelUp))
SetFullPower(Powers(powerType->PowerTypeEnum));
// update level to hunter/summon pet
if (Pet* pet = GetPet())
@@ -25523,7 +25543,7 @@ void Player::AtExitCombat()
{
Unit::AtExitCombat();
UpdatePotionCooldown();
m_combatExitTime = getMSTime();
m_regenInterruptTimestamp = GameTime::Now();
}
float Player::GetBlockPercent(uint8 attackerLevel) const

View File

@@ -1747,6 +1747,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void SendRespecWipeConfirm(ObjectGuid const& guid, uint32 cost, SpecResetType respecType) const;
void RegenerateAll();
void Regenerate(Powers power);
void InterruptPowerRegen(Powers power);
void RegenerateHealth();
void setRegenTimerCount(uint32 time) {m_regenTimerCount = time;}
void setWeaponChangeTimer(uint32 time) {m_weaponChangeTimer = time;}
@@ -2875,7 +2876,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
protected:
// Gamemaster whisper whitelist
GuidList WhisperList;
uint32 m_combatExitTime;
TimePoint m_regenInterruptTimestamp;
uint32 m_regenTimerCount;
uint32 m_foodEmoteTimerCount;
float m_powerFraction[MAX_POWERS_PER_CLASS];

View File

@@ -86,6 +86,17 @@ void Unit::UpdateDamagePhysical(WeaponAttackType attType)
}
}
int32 Unit::GetCreatePowerValue(Powers power) const
{
if (power == POWER_MANA)
return GetCreateMana();
if (PowerTypeEntry const* powerType = sDB2Manager.GetPowerTypeEntry(power))
return powerType->MaxBasePower;
return 0;
}
/*#######################################
######## ########
######## PLAYERS STAT SYSTEM ########
@@ -874,6 +885,15 @@ void Player::_RemoveAllStatBonuses()
######## ########
#######################################*/
int32 Creature::GetCreatePowerValue(Powers power) const
{
if (PowerTypeEntry const* powerType = sDB2Manager.GetPowerTypeEntry(power))
if (!powerType->GetFlags().HasFlag(PowerTypeFlags::IsUsedByNPCs))
return 0;
return Unit::GetCreatePowerValue(power);
}
bool Creature::UpdateStats(Stats /*stat*/)
{
return true;
@@ -910,10 +930,21 @@ uint32 Creature::GetPowerIndex(Powers power) const
{
if (power == GetPowerType())
return 0;
if (power == POWER_ALTERNATE_POWER)
return 1;
if (power == POWER_COMBO_POINTS)
return 2;
switch (power)
{
case POWER_COMBO_POINTS:
return 2;
case POWER_ALTERNATE_POWER:
return 1;
case POWER_ALTERNATE_QUEST:
return 3;
case POWER_ALTERNATE_ENCOUNTER:
return 4;
case POWER_ALTERNATE_MOUNT:
return 5;
default:
break;
}
return MAX_POWERS;
}

View File

@@ -5472,7 +5472,7 @@ void Unit::SetPowerType(Powers new_powertype, bool sendUpdate/* = true*/)
}
}
void Unit::UpdateDisplayPower()
Powers Unit::CalculateDisplayPowerType() const
{
Powers displayPower = POWER_MANA;
switch (GetShapeshiftForm())
@@ -5496,22 +5496,18 @@ void Unit::UpdateDisplayPower()
AuraEffect const* powerTypeAura = powerTypeAuras.front();
displayPower = Powers(powerTypeAura->GetMiscValue());
}
else if (GetTypeId() == TYPEID_PLAYER)
else
{
ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(GetClass());
if (cEntry && cEntry->DisplayPower < MAX_POWERS)
displayPower = Powers(cEntry->DisplayPower);
}
else if (GetTypeId() == TYPEID_UNIT)
{
if (Vehicle* vehicle = GetVehicleKit())
{
if (PowerDisplayEntry const* powerDisplay = sPowerDisplayStore.LookupEntry(vehicle->GetVehicleInfo()->PowerDisplayID[0]))
displayPower = Powers(powerDisplay->ActualType);
else if (GetClass() == CLASS_ROGUE)
displayPower = POWER_ENERGY;
}
else if (Pet* pet = ToPet())
else if (Pet const* pet = ToPet())
{
if (pet->getPetType() == HUNTER_PET) // Hunter pets have focus
displayPower = POWER_FOCUS;
@@ -5523,7 +5519,12 @@ void Unit::UpdateDisplayPower()
}
}
SetPowerType(displayPower);
return displayPower;
}
void Unit::UpdateDisplayPower()
{
SetPowerType(CalculateDisplayPowerType());
}
void Unit::SetSheath(SheathState sheathed)
@@ -6496,6 +6497,11 @@ void Unit::SendEnergizeSpellLog(Unit* victim, uint32 spellID, int32 damage, int3
void Unit::EnergizeBySpell(Unit* victim, SpellInfo const* spellInfo, int32 damage, Powers powerType)
{
if (Player* player = victim->ToPlayer())
if (PowerTypeEntry const* powerTypeEntry = sDB2Manager.GetPowerTypeEntry(powerType))
if (powerTypeEntry->GetFlags().HasFlag(PowerTypeFlags::UseRegenInterrupt))
player->InterruptPowerRegen(powerType);
int32 gain = victim->ModifyPower(powerType, damage, false);
int32 overEnergize = damage - gain;
victim->GetThreatManager().ForwardThreatForAssistingMe(this, float(damage) / 2, spellInfo, true);
@@ -9310,17 +9316,6 @@ void Unit::TriggerOnPowerChangeAuras(Powers power, int32 oldVal, int32 newVal)
}
}
int32 Unit::GetCreatePowerValue(Powers power) const
{
if (power == POWER_MANA)
return GetCreateMana();
if (PowerTypeEntry const* powerType = sDB2Manager.GetPowerTypeEntry(power))
return powerType->MaxBasePower;
return 0;
}
void Unit::AIUpdateTick(uint32 diff)
{
if (UnitAI* ai = GetAI())

View File

@@ -915,6 +915,7 @@ class TC_GAME_API Unit : public WorldObject
Powers GetPowerType() const { return Powers(*m_unitData->DisplayPower); }
void SetPowerType(Powers power, bool sendUpdate = true);
void SetOverrideDisplayPowerId(uint32 powerDisplayId) { SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::OverrideDisplayPowerID), powerDisplayId); }
Powers CalculateDisplayPowerType() const;
void UpdateDisplayPower();
int32 GetPower(Powers power) const;
int32 GetMinPower(Powers power) const { return power == POWER_LUNAR_POWER ? -100 : 0; }
@@ -1490,7 +1491,7 @@ class TC_GAME_API Unit : public WorldObject
uint32 GetCreateHealth() const { return m_unitData->BaseHealth; }
void SetCreateMana(uint32 val) { SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::BaseMana), val); }
uint32 GetCreateMana() const { return m_unitData->BaseMana; }
int32 GetCreatePowerValue(Powers power) const;
virtual int32 GetCreatePowerValue(Powers power) const;
float GetPosStat(Stats stat) const { return m_unitData->StatPosBuff[stat]; }
float GetNegStat(Stats stat) const { return m_unitData->StatNegBuff[stat]; }
float GetCreateStat(Stats stat) const { return m_createStats[stat]; }

View File

@@ -114,6 +114,13 @@ WorldPacket const* WorldPackets::Combat::PowerUpdate::Write()
return &_worldPacket;
}
WorldPacket const* WorldPackets::Combat::InterruptPowerRegen::Write()
{
_worldPacket << int32(PowerType);
return &_worldPacket;
}
void WorldPackets::Combat::SetSheathed::Read()
{
_worldPacket >> CurrentSheathState;

View File

@@ -22,6 +22,7 @@
#include "ObjectGuid.h"
class Unit;
enum Powers : int8;
namespace WorldPackets
{
@@ -166,6 +167,16 @@ namespace WorldPackets
std::vector<PowerUpdatePower> Powers;
};
class InterruptPowerRegen final : public ServerPacket
{
public:
explicit InterruptPowerRegen(Powers powerType) : ServerPacket(SMSG_INTERRUPT_POWER_REGEN, 4), PowerType(powerType) { }
WorldPacket const* Write() override;
Powers PowerType;
};
class SetSheathed final : public ClientPacket
{
public:

View File

@@ -1528,7 +1528,7 @@ void OpcodeTable::Initialize()
DEFINE_SERVER_OPCODE_HANDLER(SMSG_INSTANCE_RESET, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_INSTANCE_RESET_FAILED, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_INSTANCE_SAVE_CREATED, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_INTERRUPT_POWER_REGEN, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_INTERRUPT_POWER_REGEN, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_INVALIDATE_PAGE_TEXT, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_INVALIDATE_PLAYER, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_INVALID_PROMOTION_CODE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);

View File

@@ -631,6 +631,7 @@ void World::LoadConfigSettings(bool reload)
setRegenRate(RATE_POWER_ARCANE_CHARGES, "Rate.ArcaneCharges.Loss");
setRegenRate(RATE_POWER_FURY, "Rate.Fury.Loss");
setRegenRate(RATE_POWER_PAIN, "Rate.Pain.Loss");
setRegenRate(RATE_POWER_ESSENCE, "Rate.Essence.Loss");
rate_values[RATE_SKILL_DISCOVERY] = sConfigMgr->GetFloatDefault("Rate.Skill.Discovery", 1.0f);
rate_values[RATE_DROP_ITEM_POOR] = sConfigMgr->GetFloatDefault("Rate.Drop.Item.Poor", 1.0f);

View File

@@ -463,6 +463,7 @@ enum Rates
RATE_POWER_ARCANE_CHARGES,
RATE_POWER_FURY,
RATE_POWER_PAIN,
RATE_POWER_ESSENCE,
RATE_SKILL_DISCOVERY,
RATE_DROP_ITEM_POOR,
RATE_DROP_ITEM_NORMAL,

View File

@@ -2414,6 +2414,7 @@ Rate.Insanity.Loss = 1
Rate.ArcaneCharges.Loss= 1
Rate.Fury.Loss = 1
Rate.Pain.Loss = 1
Rate.Essence.Loss = 1
#
# Rate.Skill.Discovery