diff --git a/common/spdat.h b/common/spdat.h index 058fe64b5..575c8293d 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -1013,7 +1013,7 @@ typedef enum { //#define SE_Appraisal 307 // *not implemented Rogue AA - This ability allows you to estimate the selling price of an item you are holding on your cursor. #define SE_ZoneSuspendMinion 308 // implemented, @Pet, allow suspended pets to be resummoned upon zoning, base: 1, limit: none, max: none, Calc: Bool #define SE_GateCastersBindpoint 309 // implemented - Gate to casters bind point -#define SE_ReduceReuseTimer 310 // implemented, @Fc, On Caster, disc reuse time mod, base: milliseconds +#define SE_ReduceReuseTimer 310 // implemented, @Fc, On Caster, spell and disc reuse time mod by amount, base: milliseconds #define SE_LimitCombatSkills 311 // implemented, @Ff, Include or exclude combat skills or procs (non-memorizable spells) from being focused, base1: 0=Exclude if proc 1=Allow only if proc #define SE_Sanctuary 312 // implemented - Places caster at bottom hate list, effect fades if cast cast spell on targets other than self. #define SE_ForageAdditionalItems 313 // implemented[AA] - chance to forage additional items diff --git a/zone/client.cpp b/zone/client.cpp index f09bcefcd..e7d799d0a 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -2658,17 +2658,17 @@ bool Client::CheckAccess(int16 iDBLevel, int16 iDefaultLevel) { return false; } -void Client::MemorizeSpell(uint32 slot,uint32 spellid,uint32 scribing){ +void Client::MemorizeSpell(uint32 slot,uint32 spellid,uint32 scribing, uint32 reduction){ if (slot < 0 || slot >= EQ::spells::DynamicLookup(ClientVersion(), GetGM())->SpellbookSize) return; if ((spellid < 3 || spellid > EQ::spells::DynamicLookup(ClientVersion(), GetGM())->SpellIdMax) && spellid != 0xFFFFFFFF) return; - auto outapp = new EQApplicationPacket(OP_MemorizeSpell, sizeof(MemorizeSpell_Struct)); MemorizeSpell_Struct* mss=(MemorizeSpell_Struct*)outapp->pBuffer; mss->scribing=scribing; mss->slot=slot; mss->spell_id=spellid; + mss->reduction = reduction; outapp->priority = 5; QueuePacket(outapp); safe_delete(outapp); diff --git a/zone/client.h b/zone/client.h index 2f0120331..5dea68d2b 100644 --- a/zone/client.h +++ b/zone/client.h @@ -906,7 +906,7 @@ public: int32 CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id); void SetAATitle(const char *Title); void SetTitleSuffix(const char *txt); - void MemorizeSpell(uint32 slot, uint32 spellid, uint32 scribing); + void MemorizeSpell(uint32 slot, uint32 spellid, uint32 scribing, uint32 reduction = 0); // Item methods void EVENT_ITEM_ScriptStopReturn(); diff --git a/zone/mob.cpp b/zone/mob.cpp index 30cc77e45..9273137f9 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -333,6 +333,7 @@ Mob::Mob( casting_spell_timer_duration = 0; casting_spell_inventory_slot = 0; casting_spell_aa_id = 0; + casting_spell_recast_adjust = 0; target = 0; ActiveProjectileATK = false; diff --git a/zone/mob.h b/zone/mob.h index e3ae9c7f6..47dfae24b 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -1529,6 +1529,7 @@ protected: uint32 casting_spell_type; int16 casting_spell_resist_adjust; uint32 casting_spell_aa_id; + uint32 casting_spell_recast_adjust; bool casting_spell_checks; uint16 bardsong; EQ::spells::CastingSlot bardsong_slot; diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index a141c6a53..8ac522594 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -2546,6 +2546,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove if (CalcFocusEffect(focusFcTimerRefresh, spell_id, CastToClient()->m_pp.mem_spells[i])){ CastToClient()->m_pp.spellSlotRefresh[i] = 1; CastToClient()->GetPTimers().Clear(&database, (pTimerSpellStart + CastToClient()->m_pp.mem_spells[i])); + if (!CastToClient()->IsLinkedSpellReuseTimerReady(spells[CastToClient()->m_pp.mem_spells[i]].timer_id)) { + CastToClient()->GetPTimers().Clear(&database, (pTimerLinkedSpellReuseStart + spells[CastToClient()->m_pp.mem_spells[i]].timer_id)); + } } } } @@ -6323,7 +6326,7 @@ uint16 Client::GetSympatheticFocusEffect(focusType type, uint16 spell_id) { int32 Client::GetFocusEffect(focusType type, uint16 spell_id) { - if (IsBardSong(spell_id) && type != focusFcBaseEffects && type != focusSpellDuration) + if (IsBardSong(spell_id) && type != focusFcBaseEffects && type != focusSpellDuration && type != focusReduceRecastTime) return 0; int32 realTotal = 0; diff --git a/zone/spells.cpp b/zone/spells.cpp index ef4c9e910..0c2534b71 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -882,6 +882,7 @@ void Mob::ZeroCastingVars() casting_spell_resist_adjust = 0; casting_spell_checks = false; casting_spell_aa_id = 0; + casting_spell_recast_adjust = 0; delaytimer = false; } @@ -1502,10 +1503,12 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo if((IsFromItem && RuleB(Character, SkillUpFromItems)) || !IsFromItem) { c->CheckSongSkillIncrease(spell_id); } - if (spells[spell_id].timer_id > 0 && slot < CastingSlot::MaxGems) - c->SetLinkedSpellReuseTimer(spells[spell_id].timer_id, spells[spell_id].recast_time / 1000); - if(RuleB(Spells, EnableBardMelody)) - c->MemorizeSpell(static_cast(slot), spell_id, memSpellSpellbar); + if (spells[spell_id].timer_id > 0 && slot < CastingSlot::MaxGems) { + c->SetLinkedSpellReuseTimer(spells[spell_id].timer_id, (spells[spell_id].recast_time / 1000) - (casting_spell_recast_adjust / 1000)); + } + if (RuleB(Spells, EnableBardMelody)) { + c->MemorizeSpell(static_cast(slot), spell_id, memSpellSpellbar, casting_spell_recast_adjust); + } } LogSpells("Bard song [{}] should be started", spell_id); } @@ -1517,9 +1520,11 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo SendSpellBarEnable(spell_id); // this causes the delayed refresh of the spell bar gems - if (spells[spell_id].timer_id > 0 && slot < CastingSlot::MaxGems) - c->SetLinkedSpellReuseTimer(spells[spell_id].timer_id, spells[spell_id].recast_time / 1000); - c->MemorizeSpell(static_cast(slot), spell_id, memSpellSpellbar); + if (spells[spell_id].timer_id > 0 && slot < CastingSlot::MaxGems) { + c->SetLinkedSpellReuseTimer(spells[spell_id].timer_id, (spells[spell_id].recast_time / 1000) - (casting_spell_recast_adjust / 1000)); + } + + c->MemorizeSpell(static_cast(slot), spell_id, memSpellSpellbar, casting_spell_recast_adjust); // this tells the client that casting may happen again SetMana(GetMana()); @@ -2575,13 +2580,23 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui { recast -= GetAA(aaTouchoftheWicked) * 420; } - int reduction = CastToClient()->GetFocusEffect(focusReduceRecastTime, spell_id);//Client only - if(reduction) + int reduction = CastToClient()->GetFocusEffect(focusReduceRecastTime, spell_id); + + if (reduction) { recast -= reduction; - + casting_spell_recast_adjust = reduction * 1000; //used later to adjust on client with memorizespell_struct + if (recast < 0) { + casting_spell_recast_adjust = spells[spell_id].recast_time; + } + recast = std::max(recast, 0); + } + LogSpells("Spell [{}]: Setting long reuse timer to [{}] s (orig [{}])", spell_id, recast, spells[spell_id].recast_time); - CastToClient()->GetPTimers().Start(pTimerSpellStart + spell_id, recast); + + if (recast > 0) { + CastToClient()->GetPTimers().Start(pTimerSpellStart + spell_id, recast); + } } }