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
This commit is contained in:
Michael Cook (mackal) 2016-08-14 23:32:27 -04:00
parent ed5715ccd9
commit e894e96404
12 changed files with 64 additions and 3 deletions

View File

@ -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

View File

@ -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),

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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<uint32>(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<uint32>(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<uint32>(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);
}