diff --git a/zone/aa.cpp b/zone/aa.cpp index b5a85f5e6..df46bbb8f 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -1239,10 +1239,6 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) { 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)) { return; @@ -1257,13 +1253,11 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) { 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)) { @@ -1280,28 +1274,32 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) { Message(Chat::Red, "You can use this ability again in %u minute(s) %u seconds", aaremain_min, aaremain_sec); } + return; } - if (!IsCastWhileInvis(rank->spell)) { - CommonBreakInvisible(); + //calculate cooldown + int cooldown = rank->recast_time - GetAlternateAdvancementCooldownReduction(rank); + if (cooldown < 0) { + cooldown = 0; } + if (!IsCastWhileInvis(rank->spell)) + CommonBreakInvisible(); + if (spells[rank->spell].sneak && (!hidden || (hidden && (Timer::GetCurrentTime() - tmHidden) < 4000))) { MessageString(Chat::SpellFailure, SNEAK_RESTRICT); return; } // // 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); @@ -1318,34 +1316,20 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) { if (!DoCastingChecksOnCaster(rank->spell)) { return; } - 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); + 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); } - //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 { - CastSpell(rank->spell, target_id, EQ::spells::CastingSlot::AltAbility, -1, -1, 0, -1, 0xFFFFFFFF, 0, nullptr, rank->id); + if (!CastSpell(rank->spell, target_id, EQ::spells::CastingSlot::AltAbility, -1, -1, 0, -1, rank->spell_type + pTimerAAStart, cooldown, nullptr, rank->id)) { + return; + } } } -} -void Client::SetAARecastTimer(AA::Rank *rank_in, int32 spell_id) { - - if (!rank_in) { - return; - } - - int timer_duration = rank_in->recast_time; - - if (timer_duration) { - 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); + CastToClient()->GetPTimers().Start(rank->spell_type + pTimerAAStart, cooldown); + SendAlternateAdvancementTimer(rank->spell_type, 0, 0); } int Mob::GetAlternateAdvancementCooldownReduction(AA::Rank *rank_in) { @@ -1379,7 +1363,6 @@ 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 5cbc203ac..6b8c1e1a1 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1495,8 +1495,6 @@ 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 cfb9dbdf9..aa41f35a0 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -800,18 +800,50 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) { return false; } - 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); + 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 { - if (!CastSpell(spell_id, target, EQ::spells::CastingSlot::Discipline)) { - LogSpells("Discipline [{}] failed at cast spell.", spell_id); - return false; + 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)) { + 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); + } + } + else { + 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 (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); + } + } return(true); } diff --git a/zone/mob.h b/zone/mob.h index c2c28296f..d0b3988ea 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, bool from_casted_spell = false, uint32 aa_id = 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); 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 1bfde6a53..226670cf4 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1601,7 +1601,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, true)) + if(!SpellFinished(spell_id, spell_target, slot, mana_used, inventory_slot, resist_adjust, false,-1, 0xFFFFFFFF, 0, 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 @@ -2221,7 +2221,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, - bool from_casted_spell, uint32 aa_id) + uint32 timer, uint32 timer_duration, bool from_casted_spell) { Mob *ae_center = nullptr; @@ -2595,54 +2595,32 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui if (mgb) { SetMGB(false); } - - //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) + /* + Set Recast Timer on spells. + */ + if(IsClient() && !isproc) { - //Set Item or Augment Click Recast Timer - if (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt) { - CastToClient()->SetItemRecastTimer(spell_id, inventory_slot); + //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 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); - CastToClient()->SetAARecastTimer(rank, spell_id); + if(casting_spell_aa_id) { + AA::Rank *rank = zone->GetAlternateAdvancementRank(casting_spell_aa_id); - if (rank && rank->base_ability) { + if(rank && rank->base_ability) { ExpendAlternateAdvancementCharge(rank->base_ability->id); } } - //Set Custom Recast Timer - else if (spell_id == casting_spell_id && casting_spell_timer != 0xFFFFFFFF) + 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); } - //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; + 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; @@ -2662,14 +2640,20 @@ 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)); @@ -2730,8 +2714,6 @@ 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; } /////////////////////////////////////////////////////////////////////////////// @@ -6229,40 +6211,6 @@ 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) { if (!distance) { return; }