Hello,
I ran into a small obstacle while working with the Warrior spell "Charge" (Spell ID 100). Initially, I was reading s_spellHistory externally and running logic against the data to identify a spell's cooldown. When I ran Charge against this framework, it failed to give the correct cooldown as Charge has a recharge timer and can take multiple charges.
To overcome this obstacle, I went internal and called Spell_C_GetSpellCooldown. As an Arms Warrior, this was sufficient for checking when Charge would be ready to cast again. When switching to Protection; however, the Charge spell changes. It's ID remains 100; however, it gains one extra charge and the recharge rate changes to 15 seconds. Using Spell_C_GetSpellCooldown is no longer sufficient as it returns a duration of 0 when the spell has 1 or more charges available. This is correct, because the spell is ready to be cast again but if you require logic to save a charge for later; then this collapses. It's possible to store casts as a member in my script object but I'd rather not keep track of information that I can simply query from the client.
Given that, I moved my attention to Spell_C_GetSpellCharges. Unfortunately, this is no different than my first attempt--externally reading s_spellHistory. It returns a duration of 1.5 seconds and 0 charges when the first charge is used--which is not correct.
You can see the method being called here:
Code:
Offsets are shifted by 10000 because IDA places information at the top of the IDB.
signed __int64 __usercall sub_11DD8C0@<rax>(__int64 a1@<rcx>, __int64 a2@<xmm0>, __int128 *a3@<xmm1>, __m128i *a4@<xmm6>, __m256i *a5@<ymm1>)
{
__int64 v5; // rdi@1
bool v6; // dl@3
signed int v7; // eax@5
__int64 v8; // rdx@5
__int64 v9; // rdx@6
__int64 v10; // rdx@6
__int64 v11; // rdx@6
__int64 v12; // rdx@6
signed __int64 result; // rax@6
int v14; // [sp+30h] [bp-18h]@5
int v15; // [sp+34h] [bp-14h]@5
int v16; // [sp+58h] [bp+10h]@1
int v17; // [sp+60h] [bp+18h]@5
unsigned int v18; // [sp+68h] [bp+20h]@5
v16 = 0;
v5 = a1;
if ( (unsigned int)sub_11EFB30(a1, &v16, 0i64, 0i64, a2, a4, a5, 1)
&& ((signed int)sub_11ED320(v16, 1) >= 0 ? (v6 = 1) : (v6 = sub_14342B0(v16) != 0i64),
v18 = xmmword_20CD27C,
v7 = sub_B9B210((unsigned int)v16, v6, &v17, &v14, a3, a4, a5, &v15, &v18), <<<< Here
v17 > 0) )
{
sub_1E0C30(v5, v8, COERCE__INT64((double)v7));
sub_1E0C30(v5, v9, COERCE__INT64((double)v17));
sub_1E0C30(v5, v10, COERCE__INT64((double)v14 * 0.001));
sub_1E0C30(v5, v11, COERCE__INT64((double)v15 * 0.001));
sub_1E0C30(v5, v12, *(_OWORD *)&_mm_cvtps_pd((__m128)v18));
result = 5i64;
}
else
{
result = 0i64;
}
return result;
}
It appears that the function returns the current number of charges (v7), v17 should be the max number of charges, v14 and v15 should be duration and start time (respectively), and v18 should be the mod rate.
Here is the function's definition:
Code:
__int64 __usercall sub_B9B210@<rax>(__int64 a1@<rcx>, unsigned __int8 a2@<dl>, _DWORD *a3@<r8>, int *a4@<r9>, __int128 *xmm1_8_0@<xmm1>, __m128i *xmm6_8_0@<xmm6>, __m256i *a7@<ymm1>, _DWORD *a5, _DWORD *a6);
If I use the first charge, Spell_C_GetSpellCharges returns a duration of 1500, and current charges as 0. After 1500 milliseconds, the method returns default full-charge values--even when the spell has one charge on cooldown. If I use the second charge, Spell_C_GetSpellCharges returns a duration of 15000 and current charges as 0. This information can not be used to sufficiently calculate the number of charges left when used against the Warrior spell "Charge".
This leaves me back where I started. I'm sure many of you have already cracked this, is there anyone here who could point me in the right direction?