[note: written last]
hm... this may be simpler but I didn't think of it immediately
Alternative to using a thread you may be able to store the result of GetTickCount somewhere and then subtract the stored value from the current value and if the result is greater than 1500 (if ticks are in milliseconds, otherwise you'd need to do some conversion) then you'd do the regen and store the current tick count to reset the time. something like:
Code: Select all
...
call kernel32.GetTickCount
push eax // save current if needed later
sub eax, [StoredTime]
cmp eax, 1500 // adjust if not in millseconds
pop eax // restore current
jl exit // don't forget to popad
// otherwise greater so 1.5 seconds has passed so regen
mov [StoredTime], eax // set stored time to current so time resets
mov [edi+118], (float)200 // regen
...
FinishTime:
dd 0
StoredTime:
dd 0
...
That way the code isn't sitting there looping until the time changes but moving on to let other code run and just checking to see how much time has passed since it last ran. If you were regening a set amount (like 10) rather than full health then a better way (for lag) would be to divide the time difference by 1500 and multiply the regen rate by that so that if you have a lag spike that causes a 3 second delay instead of just a 1.5 second delay then the code would properly add 2* the regen rate instead of just 1, but that's a bit more complicated
[thread info written first]
You'd need to create another thread so that you don't freeze the main game thread. You can do that with createThread, also there's the sleep function that lets you wait a certain amount of time in milliseconds. With a pointer you could also use lua quite easily
Code: Select all
{$lua}
[ENABLE]
healthRegenTimer = createTimer()
healthRegenTimer.Interval = 1500 -- 1.5 seconds
local healthPointer = '[[game.exe+baseOffset]+offset1]+offset2' -- infinite offsets, or none if static (no []s)
local maxHealth = 200
local healthRegen = 3
healthRegenTimer.OnTimer = function()
local curHealth = readFloat(healthPointer)
if curHealth and curHealth < maxHealth then -- if successfully read and not full
writeFloat(healthPointer, curHealth+healthRegen) -- increment health by 1
end
end
[DISABLE]
healthRegenTimer.destroy()
note that timers don't seem to work in asynchronous scripts for some reason, you could reimplement it using "createThread(function() while true sleep(1500) ... end end)" if you really wanted it to be marked as asynchronous or wanted to avoid someone accidentally breaking the script because they right clicked and then clicked randomly on the screen to remove the menu and accidentally enabled that.
For asm createthead you have to allocate memory and register it as a symbol for the code to run, globalalloc can do both for you
Code: Select all
globalalloc(healthRegenThreadMem, 2048)
label(loop)
label(maxHealth)
label(regenRate)
healthRegenThreadMem:
loop:
// different calling conventions in x86 vs x64, use lua to put the correct one in the script
{$lua}
if targetIs64Bit() then return [[
mov ecx, 1500
sub rsp, 20 // shadowspace
call sleep
add rsp, 20]]
else return [[
push 1500
call sleep]]
end
{$asm}
// CE will fix rax to be eax in x86 targets
mov rax, [game.exe+baseoffset]
lea rax, [rax+offset1]
lea rax, [rax+offset2]
// ... infinite offsets
fld [rax] // load health
fld [maxHealth]
// compare based on https://stackoverflow.com/questions/7057501/x86-assembler-floating-point-compare
// so may be wrong, I don't do this often enough to know it off the top of my head :(
fcomip
fstp st(0)
jb loop
fadd [regenRate]
fstp [rax]
jmp loop
maxHealth:
dd (float)200
regenRate:
dd (float)10
if you don't have a pointer then you can combine a thread and hooking to get the address
Code: Select all
globalalloc(playerptr,4) // 8 for pointers in x64
newmem:
mov [playerptr], edi
code:
...
globalalloc(healthRegenThreadMem, 2048)
...
// CE will fix rax to be eax in x86 targets
mov rax, [playerptr]
test rax,rax
jz loop // if not set yet (hook hasn't ran) don't do anything because we don't have a valid pointer to health
fld [rax+118] // load health
...
I'm sure there's a couple other methods as well (like using win32 code to create the thread in the hook lol)