From e7ba484fcf4b1e2126722186c2dd220280e679a6 Mon Sep 17 00:00:00 2001 From: Ovahlord Date: Mon, 27 Apr 2020 14:20:33 +0200 Subject: [PATCH] Core/Units: reworked creature power regeneration handling * players and creatures now share the same power regeneration regeneration function * UpdatePowerRegeneration is now virtual and handles creature and player specific formulas individually * handle update field flag UF_FLAG_UNIT_ALL to handle UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER and UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER for creatures as well --- .../game/Entities/Creature/Creature.cpp | 71 +++--------- src/server/game/Entities/Creature/Creature.h | 8 +- src/server/game/Entities/Object/Object.cpp | 3 + .../Object/Updates/UpdateFieldFlags.cpp | 20 ++-- .../Object/Updates/UpdateFieldFlags.h | 2 +- src/server/game/Entities/Pet/Pet.cpp | 43 ------- src/server/game/Entities/Pet/Pet.h | 3 - src/server/game/Entities/Player/Player.cpp | 107 ------------------ src/server/game/Entities/Player/Player.h | 5 +- src/server/game/Entities/Unit/StatSystem.cpp | 40 ++++++- src/server/game/Entities/Unit/Unit.cpp | 104 +++++++++++++++++ src/server/game/Entities/Unit/Unit.h | 10 ++ src/server/game/Miscellaneous/SharedDefines.h | 6 +- .../game/Spells/Auras/SpellAuraEffects.cpp | 7 +- 14 files changed, 188 insertions(+), 241 deletions(-) diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 2d560d82b4..ea6c0f70fc 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -205,7 +205,6 @@ m_AlreadySearchedAssistance(false), m_regenHealth(true), m_cannotReachTarget(fal m_originalEntry(0), m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), _waypointPathId(0), _currentWaypointNodeInfo(0, 0), m_formation(nullptr), m_triggerJustAppeared(true), m_respawnCompatibilityMode(false) { - m_regenTimer = REGENERATION_INTERVAL; m_valuesCount = UNIT_END; for (uint8 i = 0; i < MAX_CREATURE_SPELLS; ++i) @@ -213,6 +212,7 @@ m_formation(nullptr), m_triggerJustAppeared(true), m_respawnCompatibilityMode(fa DisableReputationGain = false; + _healthRegenerationTimer = UNIT_HEALTH_REGENERATION_INTERVAL; m_SightDistance = sWorld->getFloatConfig(CONFIG_SIGHT_MONSTER); m_CombatDistance = 0;//MELEE_RANGE; @@ -248,6 +248,8 @@ void Creature::AddToWorld() if (GetZoneScript()) GetZoneScript()->OnCreatureCreate(this); + + UpdatePowerRegeneration(GetPowerType()); } } @@ -771,33 +773,22 @@ void Creature::Update(uint32 diff) if (!IsAlive()) break; - if (m_regenTimer > 0) + _healthRegenerationTimer -= diff; + if (_healthRegenerationTimer <= 0) { - if (diff >= m_regenTimer) - m_regenTimer = 0; - else - m_regenTimer -= diff; - } + bool isInCombat = IsInCombat() && (!GetVictim() || // if IsInCombat() is true and this has no victim + !EnsureVictim()->GetCharmerOrOwnerPlayerOrPlayerItself() || // or the victim/owner/charmer is not a player + !EnsureVictim()->GetCharmerOrOwnerPlayerOrPlayerItself()->IsGameMaster()); // or the victim/owner/charmer is not a GameMaster - if (m_regenTimer == 0) - { - bool bInCombat = IsInCombat() && (!GetVictim() || // if IsInCombat() is true and this has no victim - !EnsureVictim()->GetCharmerOrOwnerPlayerOrPlayerItself() || // or the victim/owner/charmer is not a player - !EnsureVictim()->GetCharmerOrOwnerPlayerOrPlayerItself()->IsGameMaster()); // or the victim/owner/charmer is not a GameMaster - - if (!IsInEvadeMode() && (!bInCombat || IsPolymorphed() || CanNotReachTarget())) // regenerate health if not in combat or if polymorphed + if (!IsInEvadeMode() && (!isInCombat || IsPolymorphed() || CanNotReachTarget())) // regenerate health if not in combat or if polymorphed RegenerateHealth(); - if (HasFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_REGENERATE_POWER)) - { - if (GetPowerType() == POWER_ENERGY) - Regenerate(POWER_ENERGY); - else - RegenerateMana(); - } - m_regenTimer = REGENERATION_INTERVAL; + _healthRegenerationTimer += UNIT_HEALTH_REGENERATION_INTERVAL; } + if (HasFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_REGENERATE_POWER)) + Regenerate(GetPowerType(), diff); + // Update serverside orientation of channeled spells that are suposed to track the channel target if (Spell const* spell = m_currentSpells[CURRENT_CHANNELED_SPELL]) if (spell->GetSpellInfo()->HasAttribute(SPELL_ATTR1_CHANNEL_TRACK_TARGET)) @@ -819,38 +810,6 @@ void Creature::Update(uint32 diff) } } -void Creature::RegenerateMana() -{ - uint32 curValue = GetPower(POWER_MANA); - uint32 maxValue = GetMaxPower(POWER_MANA); - - if (curValue >= maxValue) - return; - - uint32 addvalue = 0; - - // Combat and any controlled creature - if (IsInCombat() || GetCharmerOrOwnerGUID()) - { - float ManaIncreaseRate = sWorld->getRate(RATE_POWER_MANA); - float Spirit = GetStat(STAT_SPIRIT); - - addvalue = uint32((Spirit / 5.0f + 17.0f) * ManaIncreaseRate); - } - else - addvalue = maxValue / 3; - - // Apply modifiers (if any). - AuraEffectList const& ModPowerRegenPCTAuras = GetAuraEffectsByType(SPELL_AURA_MOD_POWER_REGEN_PERCENT); - for (AuraEffectList::const_iterator i = ModPowerRegenPCTAuras.begin(); i != ModPowerRegenPCTAuras.end(); ++i) - if ((*i)->GetMiscValue() == POWER_MANA) - AddPct(addvalue, (*i)->GetAmount()); - - addvalue += GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN, POWER_MANA) * REGENERATION_INTERVAL / (5 * IN_MILLISECONDS); - - ModifyPower(POWER_MANA, addvalue); -} - void Creature::RegenerateHealth() { if (!isRegeneratingHealth()) @@ -876,14 +835,14 @@ void Creature::RegenerateHealth() addvalue = uint32(Spirit * 0.80 * HealthIncreaseRate); } else - addvalue = maxValue/3; + addvalue = maxValue / 3; // Apply modifiers (if any). AuraEffectList const& ModPowerRegenPCTAuras = GetAuraEffectsByType(SPELL_AURA_MOD_HEALTH_REGEN_PERCENT); for (AuraEffectList::const_iterator i = ModPowerRegenPCTAuras.begin(); i != ModPowerRegenPCTAuras.end(); ++i) AddPct(addvalue, (*i)->GetAmount()); - addvalue += GetTotalAuraModifier(SPELL_AURA_MOD_REGEN) * REGENERATION_INTERVAL / (5 * IN_MILLISECONDS); + addvalue += GetTotalAuraModifier(SPELL_AURA_MOD_REGEN) * UNIT_HEALTH_REGENERATION_INTERVAL / (5 * IN_MILLISECONDS); ModifyHealth(addvalue); } diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 48a98ef4f4..b95f3c0b2c 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -146,6 +146,7 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma uint32 GetPowerIndex(Powers power) const override; void UpdateAttackPowerAndDamage(bool ranged = false) override; void CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bool addTotalPct, float& minDamage, float& maxDamage) override; + void UpdatePowerRegeneration(Powers powerType) override; void SetCanDualWield(bool value) override; int8 GetOriginalEquipmentId() const { return m_originalEquipmentId; } @@ -319,8 +320,9 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma static float _GetDamageMod(int32 Rank); - uint32 m_regenTimer; - float m_SightDistance, m_CombatDistance; + int32 _healthRegenerationTimer; + float m_SightDistance; + float m_CombatDistance; bool m_isTempWorldObject; //true when possessed @@ -369,9 +371,7 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma uint32 m_combatPulseDelay; // (secs) how often the creature puts the entire zone in combat (only works in dungeons) ReactStates m_reactState; // for AI, not charmInfo - void RegenerateMana(); void RegenerateHealth(); - void Regenerate(Powers power); MovementGeneratorType m_defaultMovementType; ObjectGuid::LowType m_spawnId; ///< For new or temporary creatures is 0 for saved it is lowguid uint8 m_equipmentId; diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index 3cf2c0a0f8..892e215904 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -748,6 +748,9 @@ uint32 Object::GetUpdateFieldData(Player const* target, uint32*& flags) const if (plr && plr->IsInSameRaidWith(target)) visibleFlag |= UF_FLAG_PARTY_MEMBER; + + if (IsCreature()) + visibleFlag |= UF_FLAG_UNIT_ALL; break; } case TYPEID_GAMEOBJECT: diff --git a/src/server/game/Entities/Object/Updates/UpdateFieldFlags.cpp b/src/server/game/Entities/Object/Updates/UpdateFieldFlags.cpp index 462702a0c5..4f0dcbdd5e 100644 --- a/src/server/game/Entities/Object/Updates/UpdateFieldFlags.cpp +++ b/src/server/game/Entities/Object/Updates/UpdateFieldFlags.cpp @@ -209,16 +209,16 @@ uint32 UnitUpdateFieldFlags[PLAYER_END] = UF_FLAG_PUBLIC, // UNIT_FIELD_MAXPOWER3 UF_FLAG_PUBLIC, // UNIT_FIELD_MAXPOWER4 UF_FLAG_PUBLIC, // UNIT_FIELD_MAXPOWER5 - UF_FLAG_PRIVATE | UF_FLAG_OWNER | UF_FLAG_UNUSED2, // UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER - UF_FLAG_PRIVATE | UF_FLAG_OWNER | UF_FLAG_UNUSED2, // UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER+1 - UF_FLAG_PRIVATE | UF_FLAG_OWNER | UF_FLAG_UNUSED2, // UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER+2 - UF_FLAG_PRIVATE | UF_FLAG_OWNER | UF_FLAG_UNUSED2, // UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER+3 - UF_FLAG_PRIVATE | UF_FLAG_OWNER | UF_FLAG_UNUSED2, // UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER+4 - UF_FLAG_PRIVATE | UF_FLAG_OWNER | UF_FLAG_UNUSED2, // UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER - UF_FLAG_PRIVATE | UF_FLAG_OWNER | UF_FLAG_UNUSED2, // UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER+1 - UF_FLAG_PRIVATE | UF_FLAG_OWNER | UF_FLAG_UNUSED2, // UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER+2 - UF_FLAG_PRIVATE | UF_FLAG_OWNER | UF_FLAG_UNUSED2, // UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER+3 - UF_FLAG_PRIVATE | UF_FLAG_OWNER | UF_FLAG_UNUSED2, // UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER+4 + UF_FLAG_PRIVATE | UF_FLAG_OWNER | UF_FLAG_UNIT_ALL, // UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER + UF_FLAG_PRIVATE | UF_FLAG_OWNER | UF_FLAG_UNIT_ALL, // UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER+1 + UF_FLAG_PRIVATE | UF_FLAG_OWNER | UF_FLAG_UNIT_ALL, // UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER+2 + UF_FLAG_PRIVATE | UF_FLAG_OWNER | UF_FLAG_UNIT_ALL, // UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER+3 + UF_FLAG_PRIVATE | UF_FLAG_OWNER | UF_FLAG_UNIT_ALL, // UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER+4 + UF_FLAG_PRIVATE | UF_FLAG_OWNER | UF_FLAG_UNIT_ALL, // UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER + UF_FLAG_PRIVATE | UF_FLAG_OWNER | UF_FLAG_UNIT_ALL, // UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER+1 + UF_FLAG_PRIVATE | UF_FLAG_OWNER | UF_FLAG_UNIT_ALL, // UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER+2 + UF_FLAG_PRIVATE | UF_FLAG_OWNER | UF_FLAG_UNIT_ALL, // UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER+3 + UF_FLAG_PRIVATE | UF_FLAG_OWNER | UF_FLAG_UNIT_ALL, // UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER+4 UF_FLAG_PUBLIC, // UNIT_FIELD_LEVEL UF_FLAG_PUBLIC, // UNIT_FIELD_FACTIONTEMPLATE UF_FLAG_PUBLIC, // UNIT_VIRTUAL_ITEM_SLOT_ID diff --git a/src/server/game/Entities/Object/Updates/UpdateFieldFlags.h b/src/server/game/Entities/Object/Updates/UpdateFieldFlags.h index 95e5983239..d69765bfbd 100644 --- a/src/server/game/Entities/Object/Updates/UpdateFieldFlags.h +++ b/src/server/game/Entities/Object/Updates/UpdateFieldFlags.h @@ -31,7 +31,7 @@ enum UpdatefieldFlags UF_FLAG_ITEM_OWNER = 0x010, UF_FLAG_SPECIAL_INFO = 0x020, UF_FLAG_PARTY_MEMBER = 0x040, - UF_FLAG_UNUSED2 = 0x080, + UF_FLAG_UNIT_ALL = 0x080, UF_FLAG_DYNAMIC = 0x100 }; diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index d021e983c2..d82a2a6336 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -54,7 +54,6 @@ Pet::Pet(Player* owner, PetType type) : } m_name = "Pet"; - m_petFocusRegenTimer = PetFocusRegenInterval; } Pet::~Pet() @@ -594,16 +593,6 @@ void Pet::Update(uint32 diff) } } - // Regenerate Focus - m_petFocusRegenTimer -= diff; - if (m_petFocusRegenTimer <= 0) - { - if (GetPowerType() == POWER_FOCUS) - Regenerate(POWER_FOCUS); - - m_petFocusRegenTimer += PetFocusRegenInterval; - } - break; } default: @@ -612,38 +601,6 @@ void Pet::Update(uint32 diff) Creature::Update(diff); } -void Creature::Regenerate(Powers power) -{ - uint32 curValue = GetPower(power); - uint32 maxValue = GetMaxPower(power); - - if (curValue >= maxValue) - return; - - float addvalue = 0.0f; - - switch (power) - { - case POWER_FOCUS: - addvalue = 5 * sWorld->getRate(RATE_POWER_FOCUS); - break; - case POWER_ENERGY: - addvalue = 20; - // For Death Knight Ghouls - if (GetEntry() == 26125) - if (Unit* owner = GetOwner()) - AddPct(addvalue, (1.0f / owner->ToPlayer()->GetFloatValue(PLAYER_FIELD_MOD_HASTE_REGEN) - 1.0f) * 100.0f); - break; - default: - return; - } - - // Apply modifiers (if any). - AddPct(addvalue, GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN_PERCENT, power)); - addvalue += GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN, power) * (IsHunterPet() ? PetFocusRegenInterval : REGENERATION_INTERVAL) / (5 * IN_MILLISECONDS); - ModifyPower(power, int32(addvalue)); -} - void Pet::Remove(PetSaveMode mode, bool returnreagent) { GetOwner()->RemovePet(this, mode, returnreagent); diff --git a/src/server/game/Entities/Pet/Pet.h b/src/server/game/Entities/Pet/Pet.h index 8b9e0b4a4c..297bfe78c2 100644 --- a/src/server/game/Entities/Pet/Pet.h +++ b/src/server/game/Entities/Pet/Pet.h @@ -21,8 +21,6 @@ #include "PetDefines.h" #include "TemporarySummon.h" -static constexpr uint32 const PetFocusRegenInterval = 1 * IN_MILLISECONDS; - enum StableResultCode { STABLE_ERR_MONEY = 0x01, // "you don't have enough money" @@ -174,7 +172,6 @@ class TC_GAME_API Pet : public Guardian int32 m_duration; // time until unsummon (used mostly for summoned guardians and not used for controlled pets) uint64 m_auraRaidUpdateMask; bool m_loading; - int32 m_petFocusRegenTimer; DeclinedName* m_declinedname; uint32 m_petSlot; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index a9334ff05a..2d59108b27 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -159,7 +159,6 @@ Player::Player(WorldSession* session): Unit(true) m_comboPoints = 0; - _regenerationTimer = 0; m_foodEmoteTimerCount = 0; m_weaponChangeTimer = 0; @@ -339,9 +338,6 @@ Player::Player(WorldSession* session): Unit(true) m_ChampioningFaction = 0; - for (uint8 i = 0; i < MAX_POWERS_PER_CLASS; ++i) - m_powerFraction[i] = 0; - isDebugAreaTriggers = false; m_WeeklyQuestChanged = false; @@ -1822,7 +1818,6 @@ bool Player::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Un void Player::RegenerateAll(uint32 diff) { - _regenerationTimer += diff; m_foodEmoteTimerCount += diff; Regenerate(POWER_ENERGY, diff); @@ -1854,19 +1849,6 @@ void Player::RegenerateAll(uint32 diff) } } - if (_regenerationTimer >= REGENERATION_INTERVAL) - { - // Not in combat or they have regeneration - if (!IsInCombat() || IsPolymorphed() || m_baseHealthRegen || - HasAuraType(SPELL_AURA_MOD_REGEN_DURING_COMBAT) || - HasAuraType(SPELL_AURA_MOD_HEALTH_REGEN_IN_COMBAT)) - { - RegenerateHealth(); - } - - _regenerationTimer -= REGENERATION_INTERVAL; - } - // Handles the emotes for drinking and eating. // According to sniffs there is a background timer going on that repeats independed from the time window where the aura applies. // That's why we dont need to reset the timer on apply. In sniffs I have seen that the first call for the spell visual is totally random, then after @@ -1899,95 +1881,6 @@ void Player::RegenerateAll(uint32 diff) } } -void Player::Regenerate(Powers power, uint32 diff) -{ - uint32 maxValue = GetMaxPower(power); - if (!maxValue) - return; - - uint32 curValue = GetPower(power); - - // Block power regeneration - if (HasAuraTypeWithValue(SPELL_AURA_PREVENT_REGENERATE_POWER, power) || HasAuraType(SPELL_AURA_INTERRUPT_REGEN)) - return; - - // Skip regeneration for power type we cannot have - uint32 powerIndex = GetPowerIndex(power); - if (powerIndex == MAX_POWERS) - return; - - float addValue = GetPowerRegen(power, IsInCombat()) * (0.001f * diff); - - // Apply config values - switch (power) - { - case POWER_MANA: - addValue *= sWorld->getRate(RATE_POWER_MANA); - break; - case POWER_RAGE: - addValue *= sWorld->getRate(RATE_POWER_RAGE_LOSS); - break; - case POWER_RUNIC_POWER: - addValue*= sWorld->getRate(RATE_POWER_RUNICPOWER_LOSS); - break; - case POWER_FOCUS: - addValue *= sWorld->getRate(RATE_POWER_FOCUS); - break; - case POWER_ENERGY: - addValue *= sWorld->getRate(RATE_POWER_ENERGY); - break; - default: - break; - } - - if (addValue < 0.0f) - { - if (curValue == 0) - return; - } - else if (addValue > 0.0f) - { - if (curValue == maxValue) - return; - } - else - return; - - addValue += m_powerFraction[powerIndex]; - uint32 integerValue = uint32(std::fabs(addValue)); - - if (addValue < 0.0f) - { - if (curValue > integerValue) - { - curValue -= integerValue; - m_powerFraction[powerIndex] = addValue + integerValue; - } - else - { - curValue = 0; - m_powerFraction[powerIndex] = 0; - } - } - else - { - curValue += integerValue; - - if (curValue > maxValue) - { - curValue = maxValue; - m_powerFraction[powerIndex] = 0; - } - else - m_powerFraction[powerIndex] = addValue - integerValue; - } - - if (_regenerationTimer >= REGENERATION_INTERVAL) - SetPower(power, curValue); - else - UpdateUInt32Value(UNIT_FIELD_POWER1 + powerIndex, curValue); -} - void Player::RegenerateHealth() { uint32 curValue = GetHealth(); diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 7ae5bb900f..d534dac7bf 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1515,7 +1515,6 @@ class TC_GAME_API Player : public Unit, public GridObject void SendTalentWipeConfirm(ObjectGuid guid) const; void ResetPetTalents(); void RegenerateAll(uint32 diff); - void Regenerate(Powers power, uint32 diff); void RegenerateHealth(); void SetRegenerationTimer(uint32 time) { _regenerationTimer = time;} void setWeaponChangeTimer(uint32 time) { m_weaponChangeTimer = time;} @@ -1837,7 +1836,7 @@ class TC_GAME_API Player : public Unit, public GridObject void UpdateExpertise(WeaponAttackType attType); void ApplyManaRegenBonus(int32 amount, bool apply); void ApplyHealthRegenBonus(int32 amount, bool apply); - void UpdatePowerRegeneration(Powers powerType); + void UpdatePowerRegeneration(Powers powerType) override; uint32 GetRuneTimer(uint8 index) const { return m_runeGraceCooldown[index]; } void SetRuneTimer(uint8 index, uint32 timer) { m_runeGraceCooldown[index] = timer; } uint32 GetLastRuneGraceTimer(uint8 index) const { return m_lastRuneGraceTimers[index]; } @@ -2397,9 +2396,7 @@ class TC_GAME_API Player : public Unit, public GridObject protected: // Gamemaster whisper whitelist GuidList WhisperList; - uint32 _regenerationTimer; uint32 m_foodEmoteTimerCount; - float m_powerFraction[MAX_POWERS_PER_CLASS]; uint32 m_contestedPvPTimer; /*********************************************************/ diff --git a/src/server/game/Entities/Unit/StatSystem.cpp b/src/server/game/Entities/Unit/StatSystem.cpp index 1f50a8acd8..6ca5949b30 100644 --- a/src/server/game/Entities/Unit/StatSystem.cpp +++ b/src/server/game/Entities/Unit/StatSystem.cpp @@ -762,7 +762,7 @@ void Player::UpdatePowerRegeneration(Powers powerType) if (powerIndex == MAX_POWERS && powerType != POWER_RUNE) return; - float powerRegenMod = GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN, powerType); + float powerRegenMod = GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN, powerType) / 5.f; float powerRegenModPct = GetTotalAuraMultiplierByMiscValue(SPELL_AURA_MOD_POWER_REGEN_PERCENT, powerType); switch (powerType) @@ -778,20 +778,20 @@ void Player::UpdatePowerRegeneration(Powers powerType) if (GetStat(STAT_INTELLECT) > 0.0f) spirit_regen *= std::sqrt(GetStat(STAT_INTELLECT)); - // CombatRegen = 5% of Base Mana - float base_regen = GetCreateMana() * 0.01f + powerRegenMod / 5.0f; + // CombatRegen = 5% of Base Mana per five seconds + float base_regen = CalculatePct(GetCreateMana(), 1) + powerRegenMod; // Set regen rate in cast state apply only on spirit based regen int32 modManaRegenInterrupt = GetTotalAuraModifier(SPELL_AURA_MOD_MANA_REGEN_INTERRUPT); SetStatFloatValue(UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER + powerIndex, base_regen + CalculatePct(spirit_regen, modManaRegenInterrupt)); - SetStatFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER + powerIndex, 0.001f + spirit_regen + base_regen); + SetStatFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER + powerIndex, spirit_regen + base_regen); break; } case POWER_RUNIC_POWER: case POWER_RAGE: // Butchery and Anger Management - SetStatFloatValue(UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER + powerIndex, powerRegenMod / 5.0f); + SetStatFloatValue(UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER + powerIndex, powerRegenMod); break; case POWER_RUNE: { @@ -913,6 +913,36 @@ void Creature::UpdateMaxPower(Powers power) SetMaxPower(power, uint32(std::lroundf(value))); } +void Creature::UpdatePowerRegeneration(Powers powerType) +{ + float powerRegenMod = GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN, powerType) / 5.f; + float powerRegenModPct = GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_POWER_REGEN_PERCENT, powerType); + + switch (powerType) + { + case POWER_MANA: + // Regeneration for controlled units + if (GetCharmerGUID() || GetOwnerGUID()) + { + float spiritRegen = (GetStat(STAT_SPIRIT) / 5.f + 17.f) * sWorld->getRate(RATE_POWER_MANA); + SetFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER, spiritRegen + powerRegenMod); + SetFloatValue(UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER, powerRegenMod); + } + else // Default creature regeneration value. + SetFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER, 8.714059f + powerRegenMod); // Most common value in sniffs. Todo: research + break; + case POWER_ENERGY: + { + float regenerationRate = CalculatePct(10, powerRegenModPct) + powerRegenMod; + SetFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER, regenerationRate); + SetFloatValue(UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER, regenerationRate); + break; + } + default: + break; + } +} + void Creature::UpdateAttackPowerAndDamage(bool ranged) { UnitMods unitMod = ranged ? UNIT_MOD_ATTACK_POWER_RANGED : UNIT_MOD_ATTACK_POWER; diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index ab6231777d..82d59c45c9 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -413,6 +413,11 @@ Unit::Unit(bool isWorldObject) : _powerBarId = 0; + for (uint8 i = 0; i < MAX_POWERS_PER_CLASS; ++i) + _powerFraction[i] = 0; + + _regenerationTimer = 0; + _oldFactionId = 0; _isWalkingBeforeCharm = false; } @@ -543,6 +548,12 @@ void Unit::Update(uint32 p_time) if (ObjectGuid guid = GetChannelObjectGuid()) if (WorldObject* target = ObjectAccessor::GetWorldObject(*this, guid)) SetOrientation(GetAngle(target)); + + // Update Power regeneration interval + if (_regenerationTimer >= int32(GetRegenerationInterval())) + _regenerationTimer -= (GetRegenerationInterval() - p_time); + else + _regenerationTimer += p_time; } bool Unit::haveOffhandWeapon() const @@ -10265,6 +10276,96 @@ int32 Unit::GetCreatePowers(Powers power) const return 0; } +void Unit::Regenerate(Powers powerType, uint32 diff) +{ + uint32 maxValue = GetMaxPower(powerType); + if (!maxValue) + return; + + uint32 curValue = GetPower(powerType); + + // Block power regeneration + if (HasAuraTypeWithValue(SPELL_AURA_PREVENT_REGENERATE_POWER, powerType) || HasAuraType(SPELL_AURA_INTERRUPT_REGEN)) + return; + + // Skip regeneration for power type we cannot have + uint32 powerIndex = GetPowerIndex(powerType); + if (powerIndex == MAX_POWERS) + return; + + float addValue = GetPowerRegen(powerType, IsInCombat()) * (diff / float(IN_MILLISECONDS)); + + // Apply config values + switch (powerType) + { + case POWER_MANA: + addValue *= sWorld->getRate(RATE_POWER_MANA); + break; + case POWER_RAGE: + addValue *= sWorld->getRate(RATE_POWER_RAGE_LOSS); + break; + case POWER_RUNIC_POWER: + addValue*= sWorld->getRate(RATE_POWER_RUNICPOWER_LOSS); + break; + case POWER_FOCUS: + addValue *= sWorld->getRate(RATE_POWER_FOCUS); + break; + case POWER_ENERGY: + addValue *= sWorld->getRate(RATE_POWER_ENERGY); + break; + default: + break; + } + + if (addValue < 0.0f) + { + if (curValue == 0) + return; + } + else if (addValue > 0.0f) + { + if (curValue == maxValue) + return; + } + else + return; + + addValue += _powerFraction[powerIndex]; + uint32 integerValue = uint32(std::fabs(addValue)); + + if (addValue < 0.0f) + { + if (curValue > integerValue) + { + curValue -= integerValue; + _powerFraction[powerIndex] = addValue + integerValue; + } + else + { + curValue = 0; + _powerFraction[powerIndex] = 0; + } + } + else + { + curValue += integerValue; + + if (curValue > maxValue) + { + curValue = maxValue; + _powerFraction[powerIndex] = 0; + } + else + _powerFraction[powerIndex] = addValue - integerValue; + } + + // Players update their powers in 2 seconds intervals, creatures in 1 second ones + if (_regenerationTimer >= int32(GetRegenerationInterval())) + SetPower(powerType, curValue); + else + UpdateUInt32Value(UNIT_FIELD_POWER1 + powerIndex, curValue); +} + void Unit::RemoveFromWorld() { // cleanup @@ -14500,6 +14601,9 @@ void Unit::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* target) if (plr && plr->IsInSameRaidWith(target)) visibleFlag |= UF_FLAG_PARTY_MEMBER; + if (IsCreature()) + visibleFlag |= UF_FLAG_UNIT_ALL; + Creature const* creature = ToCreature(); for (uint16 index = 0; index < valCount; ++index) { diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 6f43be6e1f..90a886f206 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1057,6 +1057,8 @@ class TC_GAME_API Unit : public WorldObject void ClearUnitState(uint32 f) { m_state &= ~f; } bool CanFreeMove() const; + virtual void UpdatePowerRegeneration(Powers powerType) { } + uint32 HasUnitTypeMask(uint32 mask) const { return mask & m_unitTypeMask; } void AddUnitTypeMask(uint32 mask) { m_unitTypeMask |= mask; } bool IsSummon() const { return (m_unitTypeMask & UNIT_MASK_SUMMON) != 0; } @@ -1113,6 +1115,8 @@ class TC_GAME_API Unit : public WorldObject void SetPower(Powers power, int32 val); void SetMaxPower(Powers power, int32 val); void SetPowerBarID(uint32 id) { _powerBarId = id; } + void Regenerate(Powers powerType, uint32 diff); + inline void SetFullPower(Powers power) { SetPower(power, GetMaxPower(power)); } // returns the change in power @@ -1927,6 +1931,7 @@ class TC_GAME_API Unit : public WorldObject virtual void Whisper(uint32 textId, Player* target, bool isBossWhisper = false); float GetCollisionHeight() const override; + protected: explicit Unit (bool isWorldObject); @@ -1999,6 +2004,8 @@ class TC_GAME_API Unit : public WorldObject uint32 m_unitTypeMask; LiquidTypeEntry const* _lastLiquid; uint32 _powerBarId; + float _powerFraction[MAX_POWERS_PER_CLASS]; + int32 _regenerationTimer; bool IsAlwaysVisibleFor(WorldObject const* seer) const override; bool IsAlwaysDetectableFor(WorldObject const* seer) const override; @@ -2009,6 +2016,7 @@ class TC_GAME_API Unit : public WorldObject virtual void ProcessTerrainStatusUpdate(ZLiquidStatus status, Optional const& liquidData); void InterruptMovementBasedAuras(); + private: void UpdateSplineMovement(uint32 t_diff); @@ -2020,6 +2028,8 @@ class TC_GAME_API Unit : public WorldObject void ProcSkillsAndReactives(bool isVictim, Unit* procTarget, uint32 typeMask, uint32 hitMask, WeaponAttackType attType); + uint32 GetRegenerationInterval() const { return IsPlayer() ? PLAYER_REGENERATION_INTERVAL : UNIT_REGENERATION_INTERVAL; } + protected: void SetFeared(bool apply); void SetConfused(bool apply); diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index 39a760ce5e..e0a626cf68 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -245,8 +245,10 @@ enum Powers POWER_HEALTH = 0xFFFFFFFE // (-2 as signed value) }; -#define MAX_POWERS_PER_CLASS 5 -#define REGENERATION_INTERVAL 2 * IN_MILLISECONDS +#define MAX_POWERS_PER_CLASS 5 +#define PLAYER_REGENERATION_INTERVAL 2 * IN_MILLISECONDS +#define UNIT_REGENERATION_INTERVAL 1 * IN_MILLISECONDS +#define UNIT_HEALTH_REGENERATION_INTERVAL 2 * IN_MILLISECONDS enum SpellSchools { diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index bfef6d78cb..4819390b6b 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -3644,12 +3644,8 @@ void AuraEffect::HandleModPowerRegen(AuraApplication const* aurApp, uint8 mode, if (!(mode & (AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK | AURA_EFFECT_HANDLE_STAT))) return; - Player* target = aurApp->GetTarget()->ToPlayer(); - if (!target) - return; - // Update power regen values - target->UpdatePowerRegeneration(Powers(GetMiscValue())); + aurApp->GetTarget()->UpdatePowerRegeneration(Powers(GetMiscValue())); } void AuraEffect::HandleModPowerRegenPCT(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -3659,7 +3655,6 @@ void AuraEffect::HandleModPowerRegenPCT(AuraApplication const* aurApp, uint8 mod if (target->GetTypeId() == TYPEID_PLAYER) target->ApplyPercentModFloatValue(PLAYER_FIELD_MOD_HASTE_REGEN, GetAmount(), !apply); - HandleModPowerRegen(aurApp, mode, apply); }