mirror of
https://github.com/araxiaonline/TrinityCore.git
synced 2026-06-17 05:29:43 -04:00
power regen update
This commit is contained in:
@@ -319,8 +319,6 @@ Creature::Creature(bool isWorldObject) : Unit(isWorldObject), MapObject(), m_Pla
|
||||
m_formation(nullptr), m_triggerJustAppeared(true), m_respawnCompatibilityMode(false), _lastDamagedTime(0),
|
||||
_regenerateHealth(true), _creatureImmunitiesId(0), _gossipMenuId(0), _sparringHealthPct(0)
|
||||
{
|
||||
m_regenTimer = CREATURE_REGEN_INTERVAL;
|
||||
|
||||
for (uint8 i = 0; i < MAX_CREATURE_SPELLS; ++i)
|
||||
m_spells[i] = 0;
|
||||
|
||||
@@ -854,43 +852,6 @@ void Creature::Update(uint32 diff)
|
||||
if (!IsAlive())
|
||||
break;
|
||||
|
||||
if (m_regenTimer > 0)
|
||||
{
|
||||
if (diff >= m_regenTimer)
|
||||
m_regenTimer = 0;
|
||||
else
|
||||
m_regenTimer -= diff;
|
||||
}
|
||||
|
||||
if (m_regenTimer == 0)
|
||||
{
|
||||
if (!IsInEvadeMode())
|
||||
{
|
||||
// regenerate health if not in combat or if polymorphed)
|
||||
if (!IsEngaged() || IsPolymorphed())
|
||||
RegenerateHealth();
|
||||
else if (CanNotReachTarget())
|
||||
{
|
||||
// regenerate health if cannot reach the target and the setting is set to do so.
|
||||
// this allows to disable the health regen of raid bosses if pathfinding has issues for whatever reason
|
||||
if (sWorld->getBoolConfig(CONFIG_REGEN_HP_CANNOT_REACH_TARGET_IN_RAID) || !GetMap()->IsRaid())
|
||||
{
|
||||
RegenerateHealth();
|
||||
TC_LOG_DEBUG("entities.unit.chase", "RegenerateHealth() enabled because Creature cannot reach the target. Detail: {}", GetDebugInfo());
|
||||
}
|
||||
else
|
||||
TC_LOG_DEBUG("entities.unit.chase", "RegenerateHealth() disabled even if the Creature cannot reach the target. Detail: {}", GetDebugInfo());
|
||||
}
|
||||
}
|
||||
|
||||
if (GetPowerType() == POWER_ENERGY)
|
||||
Regenerate(POWER_ENERGY);
|
||||
else
|
||||
Regenerate(POWER_MANA);
|
||||
|
||||
m_regenTimer = CREATURE_REGEN_INTERVAL;
|
||||
}
|
||||
|
||||
if (CanNotReachTarget() && !IsInEvadeMode() && !GetMap()->IsRaid())
|
||||
{
|
||||
m_cannotReachTimer += diff;
|
||||
@@ -913,59 +874,6 @@ void Creature::Heartbeat()
|
||||
ForcePartyMembersIntoCombat();
|
||||
}
|
||||
|
||||
void Creature::Regenerate(Powers power)
|
||||
{
|
||||
uint32 curValue = GetPower(power);
|
||||
uint32 maxValue = GetMaxPower(power);
|
||||
|
||||
if (!HasUnitFlag2(UNIT_FLAG2_REGENERATE_POWER))
|
||||
return;
|
||||
|
||||
if (curValue >= maxValue)
|
||||
return;
|
||||
|
||||
float addvalue = 0.0f;
|
||||
|
||||
switch (power)
|
||||
{
|
||||
case POWER_FOCUS:
|
||||
{
|
||||
// For hunter pets.
|
||||
addvalue = 24 * sWorld->getRate(RATE_POWER_FOCUS);
|
||||
break;
|
||||
}
|
||||
case POWER_ENERGY:
|
||||
{
|
||||
// For deathknight's ghoul.
|
||||
addvalue = 20;
|
||||
break;
|
||||
}
|
||||
case POWER_MANA:
|
||||
{
|
||||
// Combat and any controlled creature
|
||||
if (IsInCombat() || GetCharmerOrOwnerGUID().IsEmpty())
|
||||
{
|
||||
float ManaIncreaseRate = sWorld->getRate(RATE_POWER_MANA);
|
||||
|
||||
addvalue = uint32((27.0f / 5.0f + 17.0f) * ManaIncreaseRate);
|
||||
}
|
||||
else
|
||||
addvalue = maxValue / 3;
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply modifiers (if any).
|
||||
addvalue *= GetTotalAuraMultiplierByMiscValue(SPELL_AURA_MOD_POWER_REGEN_PERCENT, power);
|
||||
|
||||
addvalue += GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN, power) * (IsHunterPet() ? PET_FOCUS_REGEN_INTERVAL : CREATURE_REGEN_INTERVAL) / (5 * IN_MILLISECONDS);
|
||||
|
||||
ModifyPower(power, int32(addvalue));
|
||||
}
|
||||
|
||||
void Creature::RegenerateHealth()
|
||||
{
|
||||
if (!CanRegenerateHealth())
|
||||
@@ -992,7 +900,7 @@ void Creature::RegenerateHealth()
|
||||
// Apply modifiers (if any).
|
||||
addvalue *= GetTotalAuraMultiplier(SPELL_AURA_MOD_HEALTH_REGEN_PERCENT);
|
||||
|
||||
addvalue += GetTotalAuraModifier(SPELL_AURA_MOD_REGEN) * CREATURE_REGEN_INTERVAL / (5 * IN_MILLISECONDS);
|
||||
addvalue += GetTotalAuraModifier(SPELL_AURA_MOD_REGEN) * HEALTH_REGENERATION_INTERVAL / (5 * IN_MILLISECONDS);
|
||||
|
||||
ModifyHealth(addvalue);
|
||||
}
|
||||
|
||||
@@ -480,8 +480,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
|
||||
uint32 m_boundaryCheckTime; // (msecs) remaining time for next evade boundary check
|
||||
|
||||
ReactStates m_reactState; // for AI, not charmInfo
|
||||
void RegenerateHealth();
|
||||
void Regenerate(Powers power);
|
||||
void RegenerateHealth() override;
|
||||
MovementGeneratorType m_defaultMovementType;
|
||||
ObjectGuid::LowType m_spawnId; ///< For new or temporary creatures is 0 for saved it is lowguid
|
||||
uint8 m_equipmentId;
|
||||
|
||||
@@ -418,8 +418,6 @@ struct TC_GAME_API CreatureMovementData
|
||||
std::string ToString() const;
|
||||
};
|
||||
|
||||
const uint32 CREATURE_REGEN_INTERVAL = 2 * IN_MILLISECONDS;
|
||||
const uint32 PET_FOCUS_REGEN_INTERVAL = 4 * IN_MILLISECONDS;
|
||||
const uint32 CREATURE_NOPATH_EVADE_TIME = 5 * IN_MILLISECONDS;
|
||||
|
||||
const uint8 MAX_KILL_CREDIT = 2;
|
||||
|
||||
@@ -177,9 +177,6 @@ Player::Player(WorldSession* session) : Unit(true), m_sceneMgr(this)
|
||||
if (!GetSession()->HasPermission(rbac::RBAC_PERM_CAN_FILTER_WHISPERS))
|
||||
SetAcceptWhispers(true);
|
||||
|
||||
m_regenInterruptTimestamp = GameTime::Now();
|
||||
m_regenTimer = 0;
|
||||
m_regenTimerCount = 0;
|
||||
m_weaponChangeTimer = 0;
|
||||
|
||||
m_zoneUpdateId = uint32(-1);
|
||||
@@ -318,8 +315,6 @@ Player::Player(WorldSession* session) : Unit(true), m_sceneMgr(this)
|
||||
|
||||
m_ChampioningFaction = 0;
|
||||
|
||||
m_powerFraction.fill(0.0f);
|
||||
|
||||
isDebugAreaTriggers = false;
|
||||
|
||||
m_WeeklyQuestChanged = false;
|
||||
@@ -1045,12 +1040,6 @@ void Player::Update(uint32 p_time)
|
||||
m_zoneUpdateTimer -= p_time;
|
||||
}
|
||||
|
||||
if (IsAlive())
|
||||
{
|
||||
m_regenTimer += p_time;
|
||||
RegenerateAll();
|
||||
}
|
||||
|
||||
if (m_deathState == JUST_DIED)
|
||||
KillPlayer();
|
||||
|
||||
@@ -1663,159 +1652,6 @@ void Player::RegenerateAll()
|
||||
m_regenTimer = 0;
|
||||
}
|
||||
|
||||
void Player::Regenerate(Powers power)
|
||||
{
|
||||
// Skip regeneration for power type we cannot have
|
||||
uint32 powerIndex = GetPowerIndex(power);
|
||||
if (powerIndex == MAX_POWERS || powerIndex >= MAX_POWERS_PER_CLASS)
|
||||
return;
|
||||
|
||||
/// @todo possible use of miscvalueb instead of amount
|
||||
if (HasAuraTypeWithValue(SPELL_AURA_PREVENT_REGENERATE_POWER, power) || HasAuraType(SPELL_AURA_INTERRUPT_REGEN))
|
||||
return;
|
||||
|
||||
int32 curValue = GetPower(power);
|
||||
|
||||
PowerTypeEntry const* powerType = sDB2Manager.GetPowerTypeEntry(power);
|
||||
if (!powerType)
|
||||
return;
|
||||
|
||||
float addvalue = 0.0f;
|
||||
if (!IsInCombat())
|
||||
{
|
||||
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;
|
||||
}
|
||||
else
|
||||
addvalue = (powerType->RegenCombat + m_unitData->PowerRegenInterruptedFlatModifier[powerIndex]) * 0.001f * m_regenTimer;
|
||||
|
||||
static Rates const RatesForPower[MAX_POWERS] =
|
||||
{
|
||||
RATE_POWER_MANA,
|
||||
RATE_POWER_RAGE_LOSS,
|
||||
RATE_POWER_FOCUS,
|
||||
RATE_POWER_ENERGY,
|
||||
RATE_POWER_COMBO_POINTS_LOSS,
|
||||
MAX_RATES, // runes
|
||||
RATE_POWER_RUNIC_POWER_LOSS,
|
||||
RATE_POWER_SOUL_SHARDS,
|
||||
RATE_POWER_LUNAR_POWER,
|
||||
RATE_POWER_HOLY_POWER,
|
||||
MAX_RATES, // alternate
|
||||
RATE_POWER_MAELSTROM,
|
||||
RATE_POWER_CHI,
|
||||
RATE_POWER_INSANITY,
|
||||
MAX_RATES, // burning embers, unused
|
||||
MAX_RATES, // demonic fury, unused
|
||||
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)
|
||||
addvalue *= sWorld->getRate(RatesForPower[power]);
|
||||
|
||||
int32 minPower = powerType->MinPower;
|
||||
int32 maxPower = GetMaxPower(power);
|
||||
|
||||
if (powerType->CenterPower)
|
||||
{
|
||||
if (curValue > powerType->CenterPower)
|
||||
{
|
||||
addvalue = -std::abs(addvalue);
|
||||
minPower = powerType->CenterPower;
|
||||
}
|
||||
else if (curValue < powerType->CenterPower)
|
||||
{
|
||||
addvalue = std::abs(addvalue);
|
||||
maxPower = powerType->CenterPower;
|
||||
}
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
addvalue += m_powerFraction[powerIndex];
|
||||
int32 integerValue = int32(std::fabs(addvalue));
|
||||
|
||||
if (addvalue < 0.0f)
|
||||
{
|
||||
if (curValue <= minPower)
|
||||
return;
|
||||
}
|
||||
else if (addvalue > 0.0f)
|
||||
{
|
||||
if (curValue >= maxPower)
|
||||
return;
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
bool forcesSetPower = false;
|
||||
if (addvalue < 0.0f)
|
||||
{
|
||||
if (curValue > minPower + integerValue)
|
||||
{
|
||||
curValue -= integerValue;
|
||||
m_powerFraction[powerIndex] = addvalue + integerValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
curValue = minPower;
|
||||
m_powerFraction[powerIndex] = 0;
|
||||
forcesSetPower = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (curValue + integerValue <= maxPower)
|
||||
{
|
||||
curValue += integerValue;
|
||||
m_powerFraction[powerIndex] = addvalue - integerValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
curValue = maxPower;
|
||||
m_powerFraction[powerIndex] = 0;
|
||||
forcesSetPower = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (GetCommandStatus(CHEAT_POWER))
|
||||
curValue = maxPower;
|
||||
|
||||
if (m_regenTimerCount >= 2000 || forcesSetPower)
|
||||
SetPower(power, curValue);
|
||||
else
|
||||
{
|
||||
// throttle packet sending
|
||||
DoWithSuppressingObjectUpdates([&]()
|
||||
{
|
||||
SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::Power, powerIndex), curValue);
|
||||
const_cast<UF::UnitData&>(*m_unitData).ClearChanged(&UF::UnitData::Power, powerIndex);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
@@ -1755,10 +1755,7 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player>
|
||||
|
||||
void SetBindPoint(ObjectGuid guid) const;
|
||||
void SendRespecWipeConfirm(ObjectGuid const& guid, uint32 cost, SpecResetType respecType) const;
|
||||
void RegenerateAll();
|
||||
void Regenerate(Powers power);
|
||||
void InterruptPowerRegen(Powers power);
|
||||
void RegenerateHealth();
|
||||
void RegenerateHealth() override;
|
||||
void setRegenTimerCount(uint32 time) {m_regenTimerCount = time;}
|
||||
void setWeaponChangeTimer(uint32 time) {m_weaponChangeTimer = time;}
|
||||
|
||||
@@ -2101,7 +2098,6 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player>
|
||||
void UpdateExpertise(WeaponAttackType attType);
|
||||
void ApplyManaRegenBonus(int32 amount, bool apply);
|
||||
void ApplyHealthRegenBonus(int32 amount, bool apply);
|
||||
void UpdatePowerRegen(Powers powerType);
|
||||
|
||||
void SetPetSpellPower(uint32 spellPower) { SetUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::PetSpellPower), spellPower); }
|
||||
|
||||
@@ -2880,9 +2876,6 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player>
|
||||
protected:
|
||||
// Gamemaster whisper whitelist
|
||||
GuidList WhisperList;
|
||||
TimePoint m_regenInterruptTimestamp;
|
||||
uint32 m_regenTimerCount;
|
||||
std::array<float, MAX_POWERS_PER_CLASS> m_powerFraction;
|
||||
uint32 m_contestedPvPTimer;
|
||||
|
||||
/*********************************************************/
|
||||
|
||||
@@ -97,6 +97,89 @@ int32 Unit::GetCreatePowerValue(Powers power) const
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Unit::UpdatePowerRegen(Powers powerType)
|
||||
{
|
||||
uint32 powerIndex = GetPowerIndex(powerType);
|
||||
if (powerIndex == MAX_POWERS)
|
||||
return;
|
||||
|
||||
float powerRegenMod = GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN, powerType) / 5.f;
|
||||
float powerRegenModPct = GetTotalAuraMultiplierByMiscValue(SPELL_AURA_MOD_POWER_REGEN_PERCENT, powerType);
|
||||
|
||||
switch (powerType)
|
||||
{
|
||||
case POWER_MANA:
|
||||
{
|
||||
// Get base of Mana Pool in sBaseMPGameTable
|
||||
uint32 basemana = 0;
|
||||
|
||||
if (IsPlayer())
|
||||
sObjectMgr->GetPlayerClassLevelInfo(GetClass(), GetLevel(), basemana);
|
||||
else
|
||||
basemana = GetCreateMana();
|
||||
|
||||
float manaRegenModPct = GetTotalAuraMultiplierByMiscValue(SPELL_AURA_MOD_MANA_REGEN_PCT, POWER_MANA);
|
||||
|
||||
// BaseRegen = 5% of Base Mana per five seconds
|
||||
float baseRegen = basemana / 100.f;
|
||||
// SPELL_AURA_MOD_POWER_REGEN flat bonus
|
||||
baseRegen += powerRegenMod;
|
||||
|
||||
// SpiritRegen = Spirit * GTRegenMpPerSpt * Sqrt(INT) * 5
|
||||
float spiritRegen = GetStat(STAT_SPIRIT) * GetGameTableColumnForClass(sRegenMpPerSptTable.GetRow(GetLevel()), GetClass()) * 5.0f;
|
||||
if (GetStat(STAT_INTELLECT) > 0.0f)
|
||||
spiritRegen *= std::sqrt(GetStat(STAT_INTELLECT));
|
||||
|
||||
// SPELL_AURA_MOD_POWER_REGEN_PERCENT pct bonus
|
||||
baseRegen *= powerRegenModPct;
|
||||
spiritRegen *= powerRegenModPct;
|
||||
|
||||
// SPELL_AURA_MOD_MANA_REGEN_PCT pct bonus
|
||||
baseRegen *= manaRegenModPct;
|
||||
spiritRegen *= manaRegenModPct;
|
||||
|
||||
// SPELL_AURA_MOD_MANA_REGEN_INTERRUPT allow some of the spirit regeneration to bypass the combat restriction
|
||||
int32 modManaRegenInterrupt = GetTotalAuraModifier(SPELL_AURA_MOD_MANA_REGEN_INTERRUPT);
|
||||
|
||||
SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::PowerRegenFlatModifier, powerIndex), baseRegen + spiritRegen);
|
||||
SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::PowerRegenInterruptedFlatModifier, powerIndex), baseRegen + CalculatePct(spiritRegen, modManaRegenInterrupt));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
// Classic Only - Death Knight Runes use the flags of the POWER_RUNES
|
||||
if (powerType == POWER_RUNE_BLOOD || powerType == POWER_RUNE_FROST || powerType == POWER_RUNE_UNHOLY)
|
||||
powerType = POWER_RUNES;
|
||||
|
||||
PowerTypeEntry const* powerTypeEntry = sDB2Manager.GetPowerTypeEntry(powerType);
|
||||
// Base Regen
|
||||
float peaceRegen = powerTypeEntry->RegenPeace;
|
||||
float combatRegen = powerTypeEntry->RegenCombat;
|
||||
|
||||
// Haste Regen
|
||||
if (powerTypeEntry->GetFlags().HasFlag(PowerTypeFlags::RegenAffectedByHaste) && G3D::fuzzyNe(m_unitData->ModHaste, 0.0f))
|
||||
{
|
||||
peaceRegen /= m_unitData->ModHaste;
|
||||
combatRegen /= m_unitData->ModHaste;
|
||||
}
|
||||
|
||||
peaceRegen *= powerRegenModPct;
|
||||
combatRegen *= powerRegenModPct;
|
||||
|
||||
// Subtract the base value to get the proper offset
|
||||
peaceRegen -= powerTypeEntry->RegenPeace;
|
||||
combatRegen -= powerTypeEntry->RegenCombat;
|
||||
|
||||
peaceRegen += powerRegenMod;
|
||||
combatRegen += powerRegenMod;
|
||||
|
||||
SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::PowerRegenFlatModifier, powerIndex), peaceRegen);
|
||||
SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::PowerRegenInterruptedFlatModifier, powerIndex), combatRegen);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*#######################################
|
||||
######## ########
|
||||
######## PLAYERS STAT SYSTEM ########
|
||||
@@ -795,85 +878,6 @@ void Player::ApplyHealthRegenBonus(int32 amount, bool apply)
|
||||
_ModifyUInt32(apply, m_baseHealthRegen, amount);
|
||||
}
|
||||
|
||||
void Player::UpdatePowerRegen(Powers powerType)
|
||||
{
|
||||
uint32 powerIndex = GetPowerIndex(powerType);
|
||||
if (powerIndex == MAX_POWERS)
|
||||
return;
|
||||
|
||||
float powerRegenMod = GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN, powerType) / 5.f;
|
||||
float powerRegenModPct = GetTotalAuraMultiplierByMiscValue(SPELL_AURA_MOD_POWER_REGEN_PERCENT, powerType);
|
||||
|
||||
switch (powerType)
|
||||
{
|
||||
case POWER_MANA:
|
||||
{
|
||||
// Get base of Mana Pool in sBaseMPGameTable
|
||||
uint32 basemana = 0;
|
||||
sObjectMgr->GetPlayerClassLevelInfo(GetClass(), GetLevel(), basemana);
|
||||
|
||||
float manaRegenModPct = GetTotalAuraMultiplierByMiscValue(SPELL_AURA_MOD_MANA_REGEN_PCT, POWER_MANA);
|
||||
|
||||
// BaseRegen = 5% of Base Mana per five seconds
|
||||
float baseRegen = basemana / 100.f;
|
||||
// SPELL_AURA_MOD_POWER_REGEN flat bonus
|
||||
baseRegen += powerRegenMod;
|
||||
|
||||
// SpiritRegen = Spirit * GTRegenMpPerSpt * Sqrt(INT) * 5
|
||||
float spiritRegen = GetStat(STAT_SPIRIT) * GetGameTableColumnForClass(sRegenMpPerSptTable.GetRow(GetLevel()), GetClass()) * 5.0f;
|
||||
if (GetStat(STAT_INTELLECT) > 0.0f)
|
||||
spiritRegen *= std::sqrt(GetStat(STAT_INTELLECT));
|
||||
|
||||
// SPELL_AURA_MOD_POWER_REGEN_PERCENT pct bonus
|
||||
baseRegen *= powerRegenModPct;
|
||||
spiritRegen *= powerRegenModPct;
|
||||
|
||||
// SPELL_AURA_MOD_MANA_REGEN_PCT pct bonus
|
||||
baseRegen *= manaRegenModPct;
|
||||
spiritRegen *= manaRegenModPct;
|
||||
|
||||
// SPELL_AURA_MOD_MANA_REGEN_INTERRUPT allow some of the spirit regeneration to bypass the combat restriction
|
||||
int32 modManaRegenInterrupt = GetTotalAuraModifier(SPELL_AURA_MOD_MANA_REGEN_INTERRUPT);
|
||||
|
||||
SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::PowerRegenFlatModifier, powerIndex), baseRegen + spiritRegen);
|
||||
SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::PowerRegenInterruptedFlatModifier, powerIndex), baseRegen + CalculatePct(spiritRegen, modManaRegenInterrupt));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
// Classic Only - Death Knight Runes use the flags of the POWER_RUNES
|
||||
if (powerType == POWER_RUNE_BLOOD || powerType == POWER_RUNE_FROST || powerType == POWER_RUNE_UNHOLY)
|
||||
powerType = POWER_RUNES;
|
||||
|
||||
PowerTypeEntry const* powerTypeEntry = sDB2Manager.GetPowerTypeEntry(powerType);
|
||||
// Base Regen
|
||||
float peaceRegen = powerTypeEntry->RegenPeace;
|
||||
float combatRegen = powerTypeEntry->RegenCombat;
|
||||
|
||||
// Haste Regen
|
||||
if (powerTypeEntry->GetFlags().HasFlag(PowerTypeFlags::RegenAffectedByHaste) && G3D::fuzzyNe(m_unitData->ModHaste, 0.0f))
|
||||
{
|
||||
peaceRegen /= m_unitData->ModHaste;
|
||||
combatRegen /= m_unitData->ModHaste;
|
||||
}
|
||||
|
||||
peaceRegen *= powerRegenModPct;
|
||||
combatRegen *= powerRegenModPct;
|
||||
|
||||
// Subtract the base value to get the proper offset
|
||||
peaceRegen -= powerTypeEntry->RegenPeace;
|
||||
combatRegen -= powerTypeEntry->RegenCombat;
|
||||
|
||||
peaceRegen += powerRegenMod;
|
||||
combatRegen += powerRegenMod;
|
||||
|
||||
SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::PowerRegenFlatModifier, powerIndex), peaceRegen);
|
||||
SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::PowerRegenInterruptedFlatModifier, powerIndex), combatRegen);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Player::_ApplyAllStatBonuses()
|
||||
{
|
||||
SetCanModifyStats(false);
|
||||
|
||||
@@ -310,7 +310,7 @@ Unit::Unit(bool isWorldObject) :
|
||||
m_ControlledByPlayer(false), m_procDeep(0), m_procChainLength(0), m_transformSpell(0),
|
||||
m_removedAurasCount(0), m_interruptMask(SpellAuraInterruptFlags::None), m_interruptMask2(SpellAuraInterruptFlags2::None),
|
||||
m_unitMovedByMe(nullptr), m_playerMovingMe(nullptr), m_charmer(nullptr), m_charmed(nullptr),
|
||||
i_motionMaster(std::make_unique<MotionMaster>(this)), m_regenTimer(0), m_vehicle(nullptr),
|
||||
i_motionMaster(std::make_unique<MotionMaster>(this)), m_vehicle(nullptr),
|
||||
m_unitTypeMask(UNIT_MASK_NONE), m_Diminishing(), m_combatManager(this),
|
||||
m_threatManager(this), m_aiLocked(false), _playHoverAnim(false), _aiAnimKitId(0), _movementAnimKitId(0), _meleeAnimKitId(0),
|
||||
_spellHistory(std::make_unique<SpellHistory>(this))
|
||||
@@ -470,6 +470,8 @@ void Unit::Update(uint32 p_time)
|
||||
|
||||
if (IsAlive())
|
||||
{
|
||||
RegenerateAll(p_time);
|
||||
|
||||
ModifyAuraState(AURA_STATE_WOUNDED_20_PERCENT, HealthBelowPct(20));
|
||||
ModifyAuraState(AURA_STATE_WOUNDED_25_PERCENT, HealthBelowPct(25));
|
||||
ModifyAuraState(AURA_STATE_WOUNDED_35_PERCENT, HealthBelowPct(35));
|
||||
@@ -6644,10 +6646,9 @@ 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);
|
||||
if (PowerTypeEntry const* powerTypeEntry = sDB2Manager.GetPowerTypeEntry(powerType))
|
||||
if (powerTypeEntry->GetFlags().HasFlag(PowerTypeFlags::UseRegenInterrupt))
|
||||
InterruptPowerRegen(powerType);
|
||||
|
||||
int32 gain = 0;
|
||||
int32 overEnergize = 0;
|
||||
@@ -10509,28 +10510,25 @@ void Unit::ApplyHasteRegenMod(float val, bool apply)
|
||||
else
|
||||
ApplyPercentModUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::ModHasteRegen), -val, apply);
|
||||
|
||||
if (IsPlayer())
|
||||
for (uint8 powerType = POWER_MANA; powerType != MAX_POWERS; ++powerType)
|
||||
{
|
||||
for (uint8 powerType = POWER_MANA; powerType != MAX_POWERS; ++powerType)
|
||||
{
|
||||
uint32 powerIndex = GetPowerIndex(static_cast<Powers>(powerType));
|
||||
if (powerIndex == MAX_POWERS)
|
||||
continue;
|
||||
uint32 powerIndex = GetPowerIndex(static_cast<Powers>(powerType));
|
||||
if (powerIndex == MAX_POWERS)
|
||||
continue;
|
||||
|
||||
PowerTypeEntry const* powerTypeEntry = sDB2Manager.GetPowerTypeEntry(static_cast<Powers>(powerType));
|
||||
if (!powerTypeEntry)
|
||||
continue;
|
||||
PowerTypeEntry const* powerTypeEntry = sDB2Manager.GetPowerTypeEntry(static_cast<Powers>(powerType));
|
||||
if (!powerTypeEntry)
|
||||
continue;
|
||||
|
||||
bool regenAffectedByHaste = powerTypeEntry->GetFlags().HasFlag(PowerTypeFlags::RegenAffectedByHaste);
|
||||
bool regenAffectedByHaste = powerTypeEntry->GetFlags().HasFlag(PowerTypeFlags::RegenAffectedByHaste);
|
||||
|
||||
// Classic Only - Death Knight Runes use the flags of the POWER_RUNES
|
||||
if (powerType == POWER_RUNE_BLOOD || powerType == POWER_RUNE_FROST || powerType == POWER_RUNE_UNHOLY)
|
||||
if (PowerTypeEntry const* powerTypeEntry = sDB2Manager.GetPowerTypeEntry(POWER_RUNES))
|
||||
regenAffectedByHaste = powerTypeEntry->GetFlags().HasFlag(PowerTypeFlags::RegenAffectedByHaste);
|
||||
// Classic Only - Death Knight Runes use the flags of the POWER_RUNES
|
||||
if (powerType == POWER_RUNE_BLOOD || powerType == POWER_RUNE_FROST || powerType == POWER_RUNE_UNHOLY)
|
||||
if (PowerTypeEntry const* powerTypeEntry = sDB2Manager.GetPowerTypeEntry(POWER_RUNES))
|
||||
regenAffectedByHaste = powerTypeEntry->GetFlags().HasFlag(PowerTypeFlags::RegenAffectedByHaste);
|
||||
|
||||
if (regenAffectedByHaste)
|
||||
ToPlayer()->UpdatePowerRegen(static_cast<Powers>(powerType));
|
||||
}
|
||||
if (regenAffectedByHaste)
|
||||
UpdatePowerRegen(static_cast<Powers>(powerType));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11943,6 +11941,188 @@ bool Unit::CanApplyResilience() const
|
||||
*damage -= target->GetDamageReduction(*damage);
|
||||
}
|
||||
|
||||
// Players update their powers in 2 seconds intervals, creatures in 1 second ones
|
||||
constexpr uint32 CREATURE_POWER_REGEN_UPDATE_INTERVAL = 1 * IN_MILLISECONDS;
|
||||
constexpr uint32 PLAYER_POWER_REGEN_UPDATE_INTERVAL = 2 * IN_MILLISECONDS;
|
||||
|
||||
void Unit::RegenerateAll(uint32 diff)
|
||||
{
|
||||
_powerRegenUpdateTimer += diff;
|
||||
_healthRegenerationTimer += diff;
|
||||
|
||||
for (Powers power = POWER_MANA; power < MAX_POWERS; power = Powers(power + 1))
|
||||
if (power != POWER_RUNE_BLOOD && power != POWER_RUNE_FROST && power != POWER_RUNE_UNHOLY)
|
||||
Regenerate(power, diff);
|
||||
|
||||
uint32 powerRegenUpdateInterval = IsPlayer() ? PLAYER_POWER_REGEN_UPDATE_INTERVAL : CREATURE_POWER_REGEN_UPDATE_INTERVAL;
|
||||
if (_powerRegenUpdateTimer >= powerRegenUpdateInterval)
|
||||
_powerRegenUpdateTimer = 0;
|
||||
|
||||
if (_healthRegenerationTimer >= HEALTH_REGENERATION_INTERVAL)
|
||||
{
|
||||
RegenerateHealth();
|
||||
_healthRegenerationTimer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::Regenerate(Powers power, uint32 diff)
|
||||
{
|
||||
if (!HasUnitFlag2(UNIT_FLAG2_REGENERATE_POWER))
|
||||
return;
|
||||
|
||||
// Skip regeneration for power type we cannot have
|
||||
uint32 powerIndex = GetPowerIndex(power);
|
||||
if (powerIndex == MAX_POWERS || powerIndex >= MAX_POWERS_PER_CLASS)
|
||||
return;
|
||||
|
||||
/// @todo possible use of miscvalueb instead of amount
|
||||
if (HasAuraTypeWithValue(SPELL_AURA_PREVENT_REGENERATE_POWER, power) || HasAuraType(SPELL_AURA_INTERRUPT_REGEN))
|
||||
return;
|
||||
|
||||
int32 curValue = GetPower(power);
|
||||
|
||||
PowerTypeEntry const* powerType = sDB2Manager.GetPowerTypeEntry(power);
|
||||
if (!powerType)
|
||||
return;
|
||||
|
||||
float addvalue = 0.0f;
|
||||
if (!IsInCombat())
|
||||
{
|
||||
if (powerType->GetFlags().HasFlag(PowerTypeFlags::UseRegenInterrupt) && _regenInterruptTimestamp + Milliseconds(powerType->RegenInterruptTimeMS) >= GameTime::Now())
|
||||
return;
|
||||
|
||||
addvalue = (powerType->RegenPeace + m_unitData->PowerRegenFlatModifier[powerIndex]) * 0.001f * diff;
|
||||
}
|
||||
else
|
||||
addvalue = (powerType->RegenCombat + m_unitData->PowerRegenInterruptedFlatModifier[powerIndex]) * 0.001f * diff;
|
||||
|
||||
static Rates const RatesForPower[MAX_POWERS] =
|
||||
{
|
||||
RATE_POWER_MANA,
|
||||
RATE_POWER_RAGE_LOSS,
|
||||
RATE_POWER_FOCUS,
|
||||
RATE_POWER_ENERGY,
|
||||
RATE_POWER_COMBO_POINTS_LOSS,
|
||||
MAX_RATES, // runes
|
||||
RATE_POWER_RUNIC_POWER_LOSS,
|
||||
RATE_POWER_SOUL_SHARDS,
|
||||
RATE_POWER_LUNAR_POWER,
|
||||
RATE_POWER_HOLY_POWER,
|
||||
MAX_RATES, // alternate
|
||||
RATE_POWER_MAELSTROM,
|
||||
RATE_POWER_CHI,
|
||||
RATE_POWER_INSANITY,
|
||||
MAX_RATES, // burning embers, unused
|
||||
MAX_RATES, // demonic fury, unused
|
||||
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)
|
||||
addvalue *= sWorld->getRate(RatesForPower[power]);
|
||||
|
||||
int32 minPower = powerType->MinPower;
|
||||
int32 maxPower = GetMaxPower(power);
|
||||
|
||||
if (powerType->CenterPower)
|
||||
{
|
||||
if (curValue > powerType->CenterPower)
|
||||
{
|
||||
addvalue = -std::abs(addvalue);
|
||||
minPower = powerType->CenterPower;
|
||||
}
|
||||
else if (curValue < powerType->CenterPower)
|
||||
{
|
||||
addvalue = std::abs(addvalue);
|
||||
maxPower = powerType->CenterPower;
|
||||
}
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
addvalue += _powerFraction[powerIndex];
|
||||
int32 integerValue = int32(std::fabs(addvalue));
|
||||
|
||||
if (addvalue < 0.0f)
|
||||
{
|
||||
if (curValue <= minPower)
|
||||
return;
|
||||
}
|
||||
else if (addvalue > 0.0f)
|
||||
{
|
||||
if (curValue >= maxPower)
|
||||
return;
|
||||
}
|
||||
else
|
||||
return;
|
||||
|
||||
bool forcesSetPower = false;
|
||||
if (addvalue < 0.0f)
|
||||
{
|
||||
if (curValue > minPower + integerValue)
|
||||
{
|
||||
curValue -= integerValue;
|
||||
_powerFraction[powerIndex] = addvalue + integerValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
curValue = minPower;
|
||||
_powerFraction[powerIndex] = 0;
|
||||
forcesSetPower = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (curValue + integerValue <= maxPower)
|
||||
{
|
||||
curValue += integerValue;
|
||||
_powerFraction[powerIndex] = addvalue - integerValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
curValue = maxPower;
|
||||
_powerFraction[powerIndex] = 0;
|
||||
forcesSetPower = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsPlayer() && ToPlayer()->GetCommandStatus(CHEAT_POWER))
|
||||
curValue = maxPower;
|
||||
|
||||
if (_powerRegenUpdateTimer >= (IsPlayer() ? PLAYER_POWER_REGEN_UPDATE_INTERVAL : CREATURE_POWER_REGEN_UPDATE_INTERVAL) || forcesSetPower)
|
||||
SetPower(power, curValue);
|
||||
else
|
||||
{
|
||||
// throttle packet sending
|
||||
DoWithSuppressingObjectUpdates([&]()
|
||||
{
|
||||
SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::Power, powerIndex), curValue);
|
||||
const_cast<UF::UnitData&>(*m_unitData).ClearChanged(&UF::UnitData::Power, powerIndex);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::InterruptPowerRegen(Powers power)
|
||||
{
|
||||
uint32 powerIndex = GetPowerIndex(power);
|
||||
if (powerIndex == MAX_POWERS || powerIndex >= MAX_POWERS_PER_CLASS)
|
||||
return;
|
||||
|
||||
_regenInterruptTimestamp = GameTime::Now();
|
||||
_powerFraction[powerIndex] = 0.0f;
|
||||
|
||||
if (IsPlayer())
|
||||
ToPlayer()->SendDirectMessage(WorldPackets::Combat::InterruptPowerRegen(power).Write());
|
||||
}
|
||||
|
||||
int32 Unit::CalculateAOEAvoidance(int32 damage, uint32 schoolMask, bool npcCaster) const
|
||||
{
|
||||
damage = int32(float(damage) * GetTotalAuraMultiplierByMiscMask(SPELL_AURA_MOD_AOE_DAMAGE_AVOIDANCE, schoolMask));
|
||||
|
||||
@@ -625,6 +625,8 @@ struct PositionUpdateInfo
|
||||
#define ATTACK_DISPLAY_DELAY 200
|
||||
#define MAX_PLAYER_STEALTH_DETECT_RANGE 30.0f // max distance for detection targets by player
|
||||
|
||||
constexpr uint32 HEALTH_REGENERATION_INTERVAL = 2 * IN_MILLISECONDS;
|
||||
|
||||
class TC_GAME_API Unit : public WorldObject
|
||||
{
|
||||
public:
|
||||
@@ -950,6 +952,13 @@ class TC_GAME_API Unit : public WorldObject
|
||||
virtual bool CanApplyResilience() const;
|
||||
static void ApplyResilience(Unit const* victim, int32* damage);
|
||||
|
||||
// Regeneration handling
|
||||
void RegenerateAll(uint32 diff);
|
||||
void Regenerate(Powers powerType, uint32 diff);
|
||||
void InterruptPowerRegen(Powers power);
|
||||
void UpdatePowerRegen(Powers powerType);
|
||||
virtual void RegenerateHealth() = 0;
|
||||
|
||||
int32 CalculateAOEAvoidance(int32 damage, uint32 schoolMask, bool npcCaster) const;
|
||||
|
||||
float MeleeSpellMissChance(Unit const* victim, WeaponAttackType attType, SpellInfo const* spellInfo) const override;
|
||||
@@ -1893,7 +1902,6 @@ class TC_GAME_API Unit : public WorldObject
|
||||
std::unique_ptr<MotionMaster> i_motionMaster;
|
||||
|
||||
std::array<uint32, MAX_REACTIVE> m_reactiveTimer;
|
||||
uint32 m_regenTimer;
|
||||
|
||||
Vehicle* m_vehicle;
|
||||
Trinity::unique_trackable_ptr<Vehicle> m_vehicleKit;
|
||||
@@ -1987,6 +1995,11 @@ class TC_GAME_API Unit : public WorldObject
|
||||
PositionUpdateInfo _positionUpdateInfo;
|
||||
|
||||
bool _isCombatDisallowed;
|
||||
|
||||
TimePoint _regenInterruptTimestamp;
|
||||
uint32 _powerRegenUpdateTimer; // Controls the interval in which the regenerated power is being sent in update object packets
|
||||
uint32 _healthRegenerationTimer;
|
||||
std::array<float, MAX_POWERS_PER_CLASS> _powerFraction;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user