Quote:
Originally Posted by Thr!ce
Ok I feel kinda stupid... I rereversed something already reversed:
|
Thanks for completing it anyway
I made some research about skills cooldown to stop spamming skills stupidly. Even if spamming works well, from a behaviour point of view it is better to only cast one appropriate skill per frame.
Anyway here is what I found (a bit more complex that what we found before) :
The cooldown handling is actually a simple list where a minimalist structure containing the skill id and the current cooldown counter (and few other things) is dynamically allocated and added in the list through a pointer. When the cooldown counter reaches 0, the structure is deallocated and removed from the list.
So basically each time a skill is launched, a new cooldown structure is pushed in the list and lives for the whole duration of the cooldown. The list is apparently preserving initial order (not sorted by cooldown value for example).
Here is the cooldown structure :
Code:
struct SkillCooldownData
{
DWORD unk1;
DWORD skillID;// 0x0000XXXX
DWORD unk2;
DWORD unk3;
float cooldownTimer;//0x10
DWORD unk4;
DWORD unk5;
DWORD unk6;
};
Here is a function I made too read any current cooldown of any skill from its skill id :
(addresses are the french client's ones and even not updated)
Code:
float GetSkillRemainingTime( DWORD lpSkillId, ULONG lpBase/* = 0x017BD130*/)
{
SkillCooldownData* scd = NULL;
size_t* addr = (size_t*)lpBase;
if(addr)
addr = ThreadSafeReadAddress(addr, 0);
if(addr)
addr = ThreadSafeReadAddress(addr, 0x30);
size_t* startAddr = ThreadSafeReadAddress(addr, 0x8);
size_t* endAddr = ThreadSafeReadAddress(addr, 0xC);
size_t nbElts = (endAddr - startAddr);
if(nbElts == 0)
return 0.0f;
for(size_t i = 0; i < nbElts; ++i)
{
scd = (SkillCooldownData*)ThreadSafeReadAddress(startAdd r, 0x4*i);
if(scd && scd->skillID == lpSkillId)
return scd->cooldownTimer;
}
return 0.0f;
}
Don't question yourself too much about
addr = ThreadSafeReadAddress(addr, 0x30);
It can sum up to addr = *(addr + 0x30);
I modified a little the previous coded functions from ntKid To return the real skill id and not the pointer containing it :
Code:
DWORD GetSkillIdFromSlotBar( DWORD lpSlot, ULONG lpBase/* = 0x017BB230*/ )
{
DWORD dwDelta = ( lpSlot - 1 ) * 4, dwRes = NULL;
size_t* addr = (size_t*)lpBase;
if(addr)
addr = ThreadSafeReadAddress(addr, 0);
if(addr)
addr = ThreadSafeReadAddress(addr, 0x4);
if(addr)
addr = ThreadSafeReadAddress(addr, 0xC);
if(addr)
addr = ThreadSafeReadAddress(addr, 0x604);
if(addr)
addr = ThreadSafeReadAddress(addr, 0x4);
if(addr)
addr = ThreadSafeReadAddress(addr, dwDelta);
if(addr)
addr = ThreadSafeReadAddress(addr, 0x8);
return ((((DWORD)addr)>>0xC)&0x0000FFFF);
}
VOID SendSkillID( DWORD lpSkillId, ULONG lpBase/* = 0x00F551E4*/, ULONG lpFunction/* = 0x006F1870*/ )
{
size_t* addr = (size_t*)lpBase;
if(addr)
addr = ThreadSafeReadAddress(addr, 0);
if(addr)
addr = ThreadSafeReadAddress(addr, 0x14);
if(addr)
addr = ThreadSafeReadAddress(addr, 0x64);
if(addr)
addr = ThreadSafeReadAddress(addr, 0x10);
if(addr)
{
__asm
{
mov edi, addr;
mov eax, 0x00000000;
push eax;// @ 8D162A
push eax;
mov eax, lpSkillId;
push eax;
mov ecx, edi;
call lpFunction;//6F1870
}
}
}
And finally here is an example of a smart use of this :
Code:
bool SendSkill( DWORD slot )
{
DWORD skillID = GetSkillIdFromSlotBar(slot);
float cooldown = GetSkillRemainingTime(skillID);
if(cooldown == 0.0f)
{
SendSkillID(skillID);
return true;
}
return false;
}
//And finally the skill casting behaviour executed from the main loop of the game :
if(elapsed > 0.7f)// 0.7 is the common cooldown between 2 skill cast in the game
{
bool hasSentSkill = false;
if(data.currentHP < (data.HPmax/2))// regen/aoe attack healing
hasSentSkill = SendSkill(5);
if(!hasSentSkill)
{
int tab[8] = {1,2,3,4, 9,10,11,12};// should be ordered by highest cooldown to smallest one
for(int i = 0; i < 8; ++i)
{
hasSentSkill = SendSkill( tab[i] );
if(hasSentSkill)
break;
}
if(!hasSentSkill)// regen/aoe attack healing
hasSentSkill = SendSkill(5);
}
if(hasSentSkill)
elapsed -= 0.7f;
}