From 8a48473dbc3c8a0dd108fe637347b5d047ede0f0 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Tue, 8 Feb 2022 07:35:47 -0500 Subject: [PATCH] [Spells] Fix for AA and Discipline recast timers being set on spell casting failure. (#1971) * recast timer updates * reworked * removed unneeded param * fix expendible AA * fixed * Update spells.cpp * [Spells] Fix for AA and Discipline recast timers being set on spell casting failure. don't check recasts from triggered spells. --- zone/aa.cpp | 78 ++++++++++++++++++++++---------------- zone/client.h | 2 + zone/effects.cpp | 48 +++-------------------- zone/mob.h | 2 +- zone/spells.cpp | 99 ++++++++++++++++++++++++++++++++++++------------ 5 files changed, 130 insertions(+), 99 deletions(-) diff --git a/zone/aa.cpp b/zone/aa.cpp index f455bfd53..a05f9dc47 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -1227,65 +1227,65 @@ void Client::IncrementAlternateAdvancementRank(int rank_id) { void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) { AA::Rank *rank = zone->GetAlternateAdvancementRank(rank_id); - if(!rank) { + if (!rank) { return; } AA::Ability *ability = rank->base_ability; - if(!ability) { + if (!ability) { return; } - if(!IsValidSpell(rank->spell)) { + if (!IsValidSpell(rank->spell)) { + return; + } + //do not allow AA to cast if your actively casting another AA. + if (rank->spell == casting_spell_id && rank->id == casting_spell_aa_id) { return; } - if(!CanUseAlternateAdvancementRank(rank)) { + if (!CanUseAlternateAdvancementRank(rank)) { return; } - + bool use_toggle_passive_hotkey = UseTogglePassiveHotkey(*rank); //make sure it is not a passive - if(!rank->effects.empty() && !use_toggle_passive_hotkey) { + if (!rank->effects.empty() && !use_toggle_passive_hotkey) { return; } uint32 charges = 0; // We don't have the AA - if (!GetAA(rank_id, &charges)) + if (!GetAA(rank_id, &charges)) { return; + } //if expendable make sure we have charges - if(ability->charges > 0 && charges < 1) + if (ability->charges > 0 && charges < 1) { return; + } //check cooldown - if(!p_timers.Expired(&database, rank->spell_type + pTimerAAStart, false)) { + if (!p_timers.Expired(&database, rank->spell_type + pTimerAAStart, false)) { uint32 aaremain = p_timers.GetRemainingTime(rank->spell_type + pTimerAAStart); uint32 aaremain_hr = aaremain / (60 * 60); uint32 aaremain_min = (aaremain / 60) % 60; uint32 aaremain_sec = aaremain % 60; - if(aaremain_hr >= 1) { + if (aaremain_hr >= 1) { Message(Chat::Red, "You can use this ability again in %u hour(s) %u minute(s) %u seconds", - aaremain_hr, aaremain_min, aaremain_sec); + aaremain_hr, aaremain_min, aaremain_sec); } else { Message(Chat::Red, "You can use this ability again in %u minute(s) %u seconds", - aaremain_min, aaremain_sec); + aaremain_min, aaremain_sec); } - return; } - //calculate cooldown - int cooldown = rank->recast_time - GetAlternateAdvancementCooldownReduction(rank); - if(cooldown < 0) { - cooldown = 0; - } - - if (!IsCastWhileInvis(rank->spell)) + if (!IsCastWhileInvis(rank->spell)) { CommonBreakInvisible(); + } if (spells[rank->spell].sneak && (!hidden || (hidden && (Timer::GetCurrentTime() - tmHidden) < 4000))) { MessageString(Chat::SpellFailure, SNEAK_RESTRICT); @@ -1293,13 +1293,15 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) { } // // Modern clients don't require pet targeted for AA casts that are ST_Pet - if (spells[rank->spell].target_type == ST_Pet || spells[rank->spell].target_type == ST_SummonedPet) + if (spells[rank->spell].target_type == ST_Pet || spells[rank->spell].target_type == ST_SummonedPet) { target_id = GetPetID(); + } // extra handling for cast_not_standing spells if (!IgnoreCastingRestriction(rank->spell)) { - if (GetAppearance() == eaSitting) // we need to stand! + if (GetAppearance() == eaSitting) { // we need to stand! SetAppearance(eaStanding, false); + } if (GetAppearance() != eaStanding) { MessageString(Chat::SpellFailure, STAND_TO_CAST); @@ -1312,24 +1314,35 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) { } else { // Bards can cast instant cast AAs while they are casting or channeling item cast. - if (GetClass() == BARD && IsCasting() && spells[rank->spell].cast_time == 0) { + if (GetClass() == BARD && IsCasting() && spells[rank->spell].cast_time == 0) { if (!DoCastingChecksOnCaster(rank->spell)) { return; } - if (!SpellFinished(rank->spell, entity_list.GetMob(target_id), EQ::spells::CastingSlot::AltAbility, spells[rank->spell].mana, -1, spells[rank->spell].resist_difficulty, false)) { - return; - } - ExpendAlternateAdvancementCharge(ability->id); + SpellFinished(rank->spell, entity_list.GetMob(target_id), EQ::spells::CastingSlot::AltAbility, spells[rank->spell].mana, -1, spells[rank->spell].resist_difficulty, false, -1, false, rank->id); } + //Known issue: If you attempt to give a Bard an AA with a cast time, the cast timer will not display on the client (no live bard AA have cast time). else { - if (!CastSpell(rank->spell, target_id, EQ::spells::CastingSlot::AltAbility, -1, -1, 0, -1, rank->spell_type + pTimerAAStart, cooldown, nullptr, rank->id)) { - return; - } + CastSpell(rank->spell, target_id, EQ::spells::CastingSlot::AltAbility, -1, -1, 0, -1, 0xFFFFFFFF, 0, nullptr, rank->id); } } +} - CastToClient()->GetPTimers().Start(rank->spell_type + pTimerAAStart, cooldown); - SendAlternateAdvancementTimer(rank->spell_type, 0, 0); +void Client::SetAARecastTimer(AA::Rank *rank_in, int32 spell_id) { + + if (!rank_in) { + return; + } + + //calculate AA cooldown + int timer_duration = rank_in->recast_time - GetAlternateAdvancementCooldownReduction(rank_in); + + if (timer_duration <= 0) { + return; + } + + CastToClient()->GetPTimers().Start(rank_in->spell_type + pTimerAAStart, timer_duration); + CastToClient()->SendAlternateAdvancementTimer(rank_in->spell_type, 0, 0); + LogSpells("Spell [{}]: Setting AA reuse timer [{}] to [{}]", spell_id, rank_in->spell_type + pTimerAAStart, timer_duration); } int Mob::GetAlternateAdvancementCooldownReduction(AA::Rank *rank_in) { @@ -1363,6 +1376,7 @@ int Mob::GetAlternateAdvancementCooldownReduction(AA::Rank *rank_in) { } void Mob::ExpendAlternateAdvancementCharge(uint32 aa_id) { + for (auto &iter : aa_ranks) { AA::Ability *ability = zone->GetAlternateAdvancementAbility(iter.first); if (ability && aa_id == ability->id) { diff --git a/zone/client.h b/zone/client.h index f30f9add7..3b3b0ac84 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1495,6 +1495,8 @@ public: void SendItemRecastTimer(int32 recast_type, uint32 recast_delay = 0); void SetItemRecastTimer(int32 spell_id, uint32 inventory_slot); bool HasItemRecastTimer(int32 spell_id, uint32 inventory_slot); + void SetDisciplineRecastTimer(int32 spell_id); + void SetAARecastTimer(AA::Rank *rank_in, int32 spell_id); inline bool AggroMeterAvailable() const { return ((m_ClientVersionBit & EQ::versions::maskRoF2AndLater)) && RuleB(Character, EnableAggroMeter); } // RoF untested inline void SetAggroMeterLock(int in) { m_aggrometer.set_lock_id(in); } diff --git a/zone/effects.cpp b/zone/effects.cpp index 24f9afd52..98ebb32dd 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -800,51 +800,15 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) { return false; } - bool instant_recast = true; - - if(spell.recast_time > 0) { - uint32 reduced_recast = spell.recast_time / 1000; - auto focus = GetFocusEffect(focusReduceRecastTime, spell_id); - // do stupid stuff because custom servers. - // we really should be able to just do the -= focus but since custom servers could have shorter reuse timers - // we have to make sure we don't underflow the uint32 ... - // and yes, the focus effect can be used to increase the durations (spell 38944) - if (focus > reduced_recast) { - reduced_recast = 0; - if (GetPTimers().Enabled((uint32)DiscTimer)) - GetPTimers().Clear(&database, (uint32)DiscTimer); - } else { - reduced_recast -= focus; - } - - if (reduced_recast > 0){ - instant_recast = false; - - if (GetClass() == BARD && IsCasting() && spells[spell_id].cast_time == 0) { - if (DoCastingChecksOnCaster(spell_id)) { - if (SpellFinished(spell_id, entity_list.GetMob(target), EQ::spells::CastingSlot::Discipline, 0, -1, spells[spell_id].resist_difficulty, false, -1, (uint32)DiscTimer, reduced_recast, false)) { - SendDisciplineTimer(spells[spell_id].timer_id, reduced_recast); - } - } - } - else { - if (CastSpell(spell_id, target, EQ::spells::CastingSlot::Discipline, -1, -1, 0, -1, (uint32)DiscTimer, reduced_recast)) { - SendDisciplineTimer(spells[spell_id].timer_id, reduced_recast); - } - } + if (GetClass() == BARD && IsCasting() && spells[spell_id].cast_time == 0) { + if (DoCastingChecksOnCaster(spell_id)) { + SpellFinished(spell_id, entity_list.GetMob(target), EQ::spells::CastingSlot::Discipline); } } - - if (instant_recast) { - if (GetClass() == BARD && IsCasting() && spells[spell_id].cast_time == 0) { - if (DoCastingChecksOnCaster(spell_id)) { - SpellFinished(spell_id, entity_list.GetMob(target), EQ::spells::CastingSlot::Discipline, 0, -1, spells[spell_id].resist_difficulty, false, -1, 0xFFFFFFFF, 0, false); - } - } - else { - CastSpell(spell_id, target, EQ::spells::CastingSlot::Discipline); - } + else { + CastSpell(spell_id, target, EQ::spells::CastingSlot::Discipline); } + return(true); } diff --git a/zone/mob.h b/zone/mob.h index f0af8f8b9..702031616 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -326,7 +326,7 @@ public: void CastedSpellFinished(uint16 spell_id, uint32 target_id, EQ::spells::CastingSlot slot, uint16 mana_used, uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0); bool SpellFinished(uint16 spell_id, Mob *target, EQ::spells::CastingSlot slot = EQ::spells::CastingSlot::Item, uint16 mana_used = 0, - uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0, bool isproc = false, int level_override = -1, uint32 timer = 0xFFFFFFFF, uint32 timer_duration = 0, bool from_casted_spell = false); + uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0, bool isproc = false, int level_override = -1, bool from_casted_spell = false, uint32 aa_id = 0); void SendBeginCast(uint16 spell_id, uint32 casttime); virtual bool SpellOnTarget(uint16 spell_id, Mob* spelltar, int reflect_effectiveness = 0, bool use_resist_adjust = false, int16 resist_adjust = 0, bool isproc = false, int level_override = -1, int32 duration_override = 0); diff --git a/zone/spells.cpp b/zone/spells.cpp index e4decc0f6..6d4acc291 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1681,7 +1681,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo } // we're done casting, now try to apply the spell - if(!SpellFinished(spell_id, spell_target, slot, mana_used, inventory_slot, resist_adjust, false,-1, 0xFFFFFFFF, 0, true)) + if(!SpellFinished(spell_id, spell_target, slot, mana_used, inventory_slot, resist_adjust, false,-1, true)) { LogSpells("Casting of [{}] canceled: SpellFinished returned false", spell_id); // most of the cases we return false have a message already or are logic errors that shouldn't happen @@ -2300,7 +2300,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce // if you need to abort the casting, return false bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, uint16 mana_used, uint32 inventory_slot, int16 resist_adjust, bool isproc, int level_override, - uint32 timer, uint32 timer_duration, bool from_casted_spell) + bool from_casted_spell, uint32 aa_id) { Mob *ae_center = nullptr; @@ -2674,32 +2674,54 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui if (mgb) { SetMGB(false); } - /* - Set Recast Timer on spells. - */ - if(IsClient() && !isproc) + + //all spell triggers use Item slot, but don't have an item associated. We don't need to check recast timers on these. + bool is_triggered_spell = false; + if (slot == CastingSlot::Item && inventory_slot == 0xFFFFFFFF) { + is_triggered_spell = true; + } + + if (IsClient() && !isproc && !is_triggered_spell) { - //Support for bards to get disc recast timers while singing - if (GetClass() == BARD && spell_id != casting_spell_id && timer != 0xFFFFFFFF) { - CastToClient()->GetPTimers().Start(timer, timer_duration); - LogSpells("Spell [{}]: Setting bard disciple reuse timer from spell finished [{}] to [{}]", spell_id, timer, timer_duration); + //Set Item or Augment Click Recast Timer + if (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt) { + CastToClient()->SetItemRecastTimer(spell_id, inventory_slot); } + //Set Discipline Recast Timer + else if (slot == CastingSlot::Discipline) { + if (spell_id == casting_spell_id || (GetClass() == BARD && spells[spell_id].cast_time == 0 && spell_id != casting_spell_id)) { + CastToClient()->SetDisciplineRecastTimer(spell_id); + } + } + //Set AA Recast Timer. + else if (slot == CastingSlot::AltAbility){ + uint32 active_aa_id = 0; + //aa_id is only passed directly into spellfinished when a bard is using AA while casting, this supports casting an AA while clicking an instant AA. + if (GetClass() == BARD && spells[spell_id].cast_time == 0 && aa_id) { + active_aa_id = aa_id; + } + else { + active_aa_id = casting_spell_aa_id; + } + + AA::Rank *rank = zone->GetAlternateAdvancementRank(active_aa_id); - if(casting_spell_aa_id) { - AA::Rank *rank = zone->GetAlternateAdvancementRank(casting_spell_aa_id); + CastToClient()->SetAARecastTimer(rank, spell_id); - if(rank && rank->base_ability) { + if (rank && rank->base_ability) { ExpendAlternateAdvancementCharge(rank->base_ability->id); } } - else if(spell_id == casting_spell_id && casting_spell_timer != 0xFFFFFFFF) + //Set Custom Recast Timer + else if (spell_id == casting_spell_id && casting_spell_timer != 0xFFFFFFFF) { //aa new todo: aa expendable charges here CastToClient()->GetPTimers().Start(casting_spell_timer, casting_spell_timer_duration); LogSpells("Spell [{}]: Setting custom reuse timer [{}] to [{}]", spell_id, casting_spell_timer, casting_spell_timer_duration); } - else if(spells[spell_id].recast_time > 1000 && !spells[spell_id].is_discipline) { - int recast = spells[spell_id].recast_time/1000; + //Set Spell Recast Timer + else if (spells[spell_id].recast_time > 1000 && !spells[spell_id].is_discipline) { + int recast = spells[spell_id].recast_time / 1000; if (spell_id == SPELL_LAY_ON_HANDS) //lay on hands { recast -= GetAA(aaFervrentBlessing) * 420; @@ -2719,20 +2741,14 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui } recast = std::max(recast, 0); } - + LogSpells("Spell [{}]: Setting long reuse timer to [{}] s (orig [{}])", spell_id, recast, spells[spell_id].recast_time); - + if (recast > 0) { CastToClient()->GetPTimers().Start(pTimerSpellStart + spell_id, recast); } } } - /* - Set Recast Timer on item clicks, including augmenets. - */ - if(IsClient() && (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt)){ - CastToClient()->SetItemRecastTimer(spell_id, inventory_slot); - } if (IsNPC()) { CastToNPC()->AI_Event_SpellCastFinished(true, static_cast(slot)); @@ -2793,6 +2809,8 @@ bool Mob::ApplyBardPulse(int32 spell_id, Mob *spell_target, CastingSlot slot) { if (!SpellFinished(spell_id, spell_target, slot, spells[spell_id].mana, 0xFFFFFFFF, spells[spell_id].resist_difficulty)) { return false; } + + return true; } /////////////////////////////////////////////////////////////////////////////// @@ -6283,6 +6301,39 @@ bool Client::HasItemRecastTimer(int32 spell_id, uint32 inventory_slot) return false; } +void Client::SetDisciplineRecastTimer(int32 spell_id) { + + if (!IsValidSpell(spell_id)) { + return; + } + + if (spells[spell_id].recast_time == 0) { + return; + } + + pTimerType DiscTimer = pTimerDisciplineReuseStart + spells[spell_id].timer_id; + uint32 timer_duration = spells[spell_id].recast_time / 1000; + auto focus = GetFocusEffect(focusReduceRecastTime, spell_id); + + if (focus > timer_duration) { + timer_duration = 0; + if (GetPTimers().Enabled((uint32)DiscTimer)) { + GetPTimers().Clear(&database, (uint32)DiscTimer); + } + else { + timer_duration -= focus; + } + } + + if (timer_duration <= 0) { + return; + } + + CastToClient()->GetPTimers().Start((uint32)DiscTimer, timer_duration); + CastToClient()->SendDisciplineTimer(spells[spell_id].timer_id, timer_duration); + LogSpells("Spell [{}]: Setting disciple reuse timer [{}] to [{}]", spell_id, spells[spell_id].timer_id, timer_duration); +} + void Mob::CalcDestFromHeading(float heading, float distance, float MaxZDiff, float StartX, float StartY, float &dX, float &dY, float &dZ) {