From e894e96404181af7cf715ed18028688d252723de Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 14 Aug 2016 23:32:27 -0400 Subject: [PATCH] Implement Linked Spell Reuse Timers They started linked spells at OoW launch (I think) At least canni was linked then. This is rather user unfriendly, but that's live like. Ex. the spells aren't actually put on cool down so you can attempt to cast them still but you will be interrupted. Titanium is particularly unfriendly with large differences in reuse times --- changelog.txt | 6 ++++++ common/emu_oplist.h | 1 + common/eq_packet_structs.h | 12 +++++++++++ common/ptimer.h | 4 +++- utils/patches/patch_RoF.conf | 1 + utils/patches/patch_RoF2.conf | 1 + utils/patches/patch_SoD.conf | 1 + utils/patches/patch_SoF.conf | 1 + utils/patches/patch_Titanium.conf | 1 + utils/patches/patch_UF.conf | 1 + zone/client.h | 3 +++ zone/spells.cpp | 35 +++++++++++++++++++++++++++++-- 12 files changed, 64 insertions(+), 3 deletions(-) diff --git a/changelog.txt b/changelog.txt index 4f71af6d5..7647ea8ec 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,11 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 08/14/2016 == +mackal: Implement Linked Spell Reuse Timers + - For whatever reason this is a bit unfriendly, but that's how it is on live. + - Titanium is especially unfriendly with large differences in reuse times (ex higher canni and the first 4) + - Unsure when this went live for spells, but canni was at least linked at OoW launch + == 08/13/2016 == Kinglykrab: Implemented optional avoidance cap rules. - Serves to eliminate God-like characters on custom servers with high item stats diff --git a/common/emu_oplist.h b/common/emu_oplist.h index 8b913095b..6b7c58841 100644 --- a/common/emu_oplist.h +++ b/common/emu_oplist.h @@ -289,6 +289,7 @@ N(OP_LFGuild), N(OP_LFPCommand), N(OP_LFPGetMatchesRequest), N(OP_LFPGetMatchesResponse), +N(OP_LinkedReuse), N(OP_LoadSpellSet), N(OP_LocInfo), N(OP_LockoutTimerInfo), diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index fc1c9f9e7..12a08fa36 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -390,6 +390,18 @@ uint32 scribing; // 1 if memorizing a spell, set to 0 if scribing to book, 2 if uint32 reduction; // lower reuse }; +/* +** Linked Spell Reuse Timer +** Length: 12 +** Comes before the OP_Memorize +** Live (maybe TDS steam) has an extra DWORD after timer_id +*/ +struct LinkedSpellReuseTimer_Struct { + uint32 timer_id; // Timer ID of the spell + uint32 end_time; // timestamp of when it will be ready + uint32 start_time; // timestamp of when it started +}; + /* ** Make Charmed Pet ** Length: 12 Bytes diff --git a/common/ptimer.h b/common/ptimer.h index 7194ab49b..d7398bea3 100644 --- a/common/ptimer.h +++ b/common/ptimer.h @@ -37,10 +37,12 @@ enum { //values for pTimerType pTimerSenseTraps = 12, pTimerDisarmTraps = 13, pTimerDisciplineReuseStart = 14, - pTimerDisciplineReuseEnd = 24, + pTimerDisciplineReuseEnd = 24, // client actually has 20 ids, but still no disc go that high even on live pTimerCombatAbility = 25, pTimerCombatAbility2 = 26, // RoF2+ Tiger Claw is unlinked from other monk skills, generic in case other classes ever need it pTimerBeggingPickPocket = 27, + pTimerLinkedSpellReuseStart = 28, + pTimerLinkedSpellReuseEnd = 48, pTimerLayHands = 87, //these IDs are used by client too pTimerHarmTouch = 89, //so dont change them diff --git a/utils/patches/patch_RoF.conf b/utils/patches/patch_RoF.conf index 00682d57f..aefcd8dbc 100644 --- a/utils/patches/patch_RoF.conf +++ b/utils/patches/patch_RoF.conf @@ -172,6 +172,7 @@ OP_BeginCast=0x17ff OP_ColoredText=0x41cb OP_ConsentResponse=0x183d OP_MemorizeSpell=0x2fac +OP_LinkedReuse=0x3ac0 OP_SwapSpell=0x4736 OP_CastSpell=0x1cb5 OP_Consider=0x4d8d diff --git a/utils/patches/patch_RoF2.conf b/utils/patches/patch_RoF2.conf index 9237a0238..19ef8d842 100644 --- a/utils/patches/patch_RoF2.conf +++ b/utils/patches/patch_RoF2.conf @@ -171,6 +171,7 @@ OP_BeginCast=0x318f OP_ColoredText=0x43af OP_ConsentResponse=0x384a OP_MemorizeSpell=0x217c +OP_LinkedReuse=0x1619 OP_SwapSpell=0x0efa OP_CastSpell=0x1287 OP_Consider=0x742b diff --git a/utils/patches/patch_SoD.conf b/utils/patches/patch_SoD.conf index 4944b0ef3..08e072d33 100644 --- a/utils/patches/patch_SoD.conf +++ b/utils/patches/patch_SoD.conf @@ -171,6 +171,7 @@ OP_BeginCast=0x0d5a # C OP_ColoredText=0x569a # C OP_ConsentResponse=0x6e47 # C OP_MemorizeSpell=0x8543 # C +OP_LinkedReuse=0x6ef9 OP_SwapSpell=0x3fd2 # C OP_CastSpell=0x3582 # C OP_Consider=0x6024 # C diff --git a/utils/patches/patch_SoF.conf b/utils/patches/patch_SoF.conf index 113038e49..d758daea9 100644 --- a/utils/patches/patch_SoF.conf +++ b/utils/patches/patch_SoF.conf @@ -169,6 +169,7 @@ OP_BeginCast=0x5A50 #SEQ 12/04/08 OP_ColoredText=0x3BC7 #SEQ 12/04/08 OP_ConsentResponse=0x4D30 #SEQ 12/04/08 OP_MemorizeSpell=0x6A93 #SEQ 12/04/08 +OP_LinkedReuse=0x2c26 OP_SwapSpell=0x1418 #SEQ 12/04/08 OP_CastSpell=0x7F5D #SEQ 12/04/08 OP_Consider=0x32E1 #SEQ 12/04/08 diff --git a/utils/patches/patch_Titanium.conf b/utils/patches/patch_Titanium.conf index a48de92b5..23f07109f 100644 --- a/utils/patches/patch_Titanium.conf +++ b/utils/patches/patch_Titanium.conf @@ -170,6 +170,7 @@ OP_Save=0x736b # ShowEQ 10/27/05 OP_Camp=0x78c1 # ShowEQ 10/27/05 OP_EndLootRequest=0x2316 # ShowEQ 10/27/05 OP_MemorizeSpell=0x308e # ShowEQ 10/27/05 +OP_LinkedReuse=0x6a00 OP_SwapSpell=0x2126 # ShowEQ 10/27/05 OP_CastSpell=0x304b # ShowEQ 10/27/05 OP_DeleteSpell=0x4f37 diff --git a/utils/patches/patch_UF.conf b/utils/patches/patch_UF.conf index 4b650176f..f41b62740 100644 --- a/utils/patches/patch_UF.conf +++ b/utils/patches/patch_UF.conf @@ -173,6 +173,7 @@ OP_BeginCast=0x0d5a # C OP_ColoredText=0x71bf # C OP_ConsentResponse=0x0e87 # C OP_MemorizeSpell=0x3887 # C +OP_LinkedReuse=0x1b26 OP_SwapSpell=0x5805 # C OP_CastSpell=0x50c2 # C OP_Consider=0x3c2d # C diff --git a/zone/client.h b/zone/client.h index dc04fbc1e..df7c1fce3 100644 --- a/zone/client.h +++ b/zone/client.h @@ -894,6 +894,9 @@ public: void SendDisciplineTimer(uint32 timer_id, uint32 duration); bool UseDiscipline(uint32 spell_id, uint32 target); + void SetLinkedSpellReuseTimer(uint32 timer_id, uint32 duration); + bool IsLinkedSpellReuseTimerReady(uint32 timer_id); + bool CheckTitle(int titleset); void EnableTitle(int titleset); void RemoveTitle(int titleset); diff --git a/zone/spells.cpp b/zone/spells.cpp index ea8067b8c..d310589b1 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -552,6 +552,10 @@ bool Mob::DoCastingChecks() } } + if (IsClient() && spells[spell_id].EndurTimerIndex > 0 && casting_spell_slot < CastingSlot::MaxGems) + if (!CastToClient()->IsLinkedSpellReuseTimerReady(spells[spell_id].EndurTimerIndex)) + return false; + casting_spell_checks = true; return true; } @@ -1308,8 +1312,11 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo { if(IsClient()) { - this->CastToClient()->CheckSongSkillIncrease(spell_id); - this->CastToClient()->MemorizeSpell(static_cast(slot), spell_id, memSpellSpellbar); + Client *c = CastToClient(); + c->CheckSongSkillIncrease(spell_id); + if (spells[spell_id].EndurTimerIndex > 0 && slot < CastingSlot::MaxGems) + c->SetLinkedSpellReuseTimer(spells[spell_id].EndurTimerIndex, spells[spell_id].recast_time / 1000); + c->MemorizeSpell(static_cast(slot), spell_id, memSpellSpellbar); } Log.Out(Logs::Detail, Logs::Spells, "Bard song %d should be started", spell_id); } @@ -1321,6 +1328,8 @@ 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].EndurTimerIndex > 0 && slot < CastingSlot::MaxGems) + c->SetLinkedSpellReuseTimer(spells[spell_id].EndurTimerIndex, spells[spell_id].recast_time / 1000); c->MemorizeSpell(static_cast(slot), spell_id, memSpellSpellbar); // this tells the client that casting may happen again @@ -5680,3 +5689,25 @@ void Mob::ConeDirectional(uint16 spell_id, int16 resist_adjust) ++iter; } } + +// duration in seconds +void Client::SetLinkedSpellReuseTimer(uint32 timer_id, uint32 duration) +{ + if (timer_id > 19) + return; + GetPTimers().Start(pTimerLinkedSpellReuseStart + timer_id, duration); + auto outapp = new EQApplicationPacket(OP_LinkedReuse, sizeof(LinkedSpellReuseTimer_Struct)); + auto lr = (LinkedSpellReuseTimer_Struct *)outapp->pBuffer; + lr->timer_id = timer_id; + lr->start_time = Timer::GetCurrentTime() / 1000; + lr->end_time = lr->start_time + duration; + FastQueuePacket(&outapp); +} + +bool Client::IsLinkedSpellReuseTimerReady(uint32 timer_id) +{ + if (timer_id > 19) + return true; + return GetPTimers().Expired(&database, pTimerLinkedSpellReuseStart + timer_id); +} +