Requires Autohotkey_L to run
http://l.autohotkey.net/AutoHotkey_L_Install.exeAutohotkey_L installed should be exactly Unicode x32 version(you get to choose and on Win x64 it defaults to x64 version, i think that may mean calling 64-bit dllcalls which would screw this script up).
Uses passive memory reading & keypress sending.
It reads basic flasks properties like flask type/current charges/charges per use/recovery duration to try and optimally use your Mana/Health/Hybrid flasks(well for now it'll just use them from left to right but won't spam/waste them, will use hp flasks if you need hp, mana flasks if you need mana, hybrid - for either)
It does not read out prefixes/suffixes(it actually really only cares for ones that affect recovery speed) but allows you to easily specify such differences in by-character-name Flaskconfig.
Also allows you to specify min life/nshield % to auto-quit on hardcore.
Currently most use from this can be gained if used for multiboxing, it'll manage all your open PoE windows.
Default toggle hotkey - F1
In case of a new client update you can try commenting out this line to let it try to find new offset
baseMgrPtr:=
comment it out like this
; baseMgrPtr:=
Update:
Fixed multiple drinking/spamming potions.
Default requested process rights changed to VM_READ only since it's been reported to fix script not working for some.
Default autoQuit method changed to quit to main menu, resolution independent, should succeed in case of lags too.
Some additional checks and fixes to make sure it doesn't auto-quit forever if auto-quit is enabled.
Hybrid flasks now considered for both regen queues regardless of which resource type(hp/mana) they're initially used for
Fixed bug with not reading energy shield.
0.10.1f bptr updated, built-in AoB successfully scans for it too but wouldn't use it right away unless you followed it's advice and saved it to script.
Fixed incorrect reading of reserved mana amount(read reserved hp instead)
Further fixes to reservation cases : turns out flat value auras and % reserved are stored separately
Multiple fixes and additions:
BasePointer scan immediate use of found value was still bugged.
In multiboxing environment 1 dead/non-ingame toon could prevent script from checking out other windows since it used break on client windows iteration loop instead of continue.
Further attempt at fixing excessive quitting(closing game window when already at login menu/trying to log back in).
Quit mechanism reworked to also send "Space" - close all panels key, will get you out in case for example some mob sneaks up on you while you're looking at passive skilltree or whatever
Added tolerance time for soft-quit to mainmenu method "autoQuitSoftToleranceBeforeKill", in case it doesn't get out to mainmenu within tolerance time for whatever reason it'll resort to ending process.
Basic granite flask support added with configurable life/nshield tresholds. Since NShield regen kicks in after quite a while it's likely to waste some charges on all granite CI toons by drinking some of that flasks even after mobs are dead, but it should work well on otherwise hp-flask using toons(either for hp regen or nshield regen through zealot's oath).
Some vars/arrays renamed to better reflect their purpose, a bit of streamlining/cleanup done.
Since it seems to be easy enough on resources for this to decrease performance in any noticeable way and this greatly increases chances of you getting saved from death, reduced default watchdog period to 100 ms from 1000ms.
Now defaults to "On" state when run since it seems to adequately handle distinguishing between menu/ingame states & alive/dead toons at this point.
Some basic error messages for critical error cases instead of silent failures, will also auto-quit script in that cases:
Running script on AutoHotkey 64-bit
Failure to obtain moduleBase, moduleSize
Failure to obtain valid process handle for client
(AoBScan failure produced notification, will auto-quit now in addition to make it clear script can't work in that case)
Fixed a bug that would lead to script cpu-hogging while in mainmenu(which in turn exposed whole design flaw with sleeping between drinking potions related to multiboxing, since that "sleep" accidentally wound up in main loop that basically meant sequential only & not parallel drinking of potions...)
Flask timers are now reset in case you get to 100% of respective resource.
As usual test autoquit to menu by pressing F4
PoE_AutoFlasks.ahk
(!empty($user->lang['CODE'])) ? $user->lang['CODE'] : ucwords(strtolower(str_replace('_', ' ', 'CODE'))):
SetBatchLines, -1
DetectHiddenWindows, On
cliname=Path of Exile
cliexe=Client.exe
trayNotifications:=true ;display tray notifications about script actions : drinking potions, autoquitting
autoPotionsWatchdogPeriod:=100 ;milliseconds, decrease this value to have script recheck life/mana/flasks availability more often/increase chances of getting saved from death in time
lagCompensation:=50
autoQuitMode:=1 ; default autoQuit method : 0 =winKill, 1 = exit to login screen
autoQuitPauseBeforeClick:=50
autoQuitSoftToleranceBeforeKill:=2000 ; try to quit to loginscreen at most milliseconds before killing game window(in case we can't quit by clicking menu option for some reason)
PlayerConfig:={}
PlayerConfig["Default"]:={minLifeRatioToDrink: 0.45, minManaRatioToDrink: 0.55, minManaToDrink: 40, minLifeRatioToPopGranite: 0.65, minLifeRatioToQuit: 0.35} ; disableAutoPotions:true, minLifeRatioToQuit:, minNShieldRatioToQuit: , HasZealotsOath: false, }
PlayerConfig["Default"].FlaskConfig:=[]
PlayerConfig["Default"].FlaskConfig[1]:={Hotkey:"{1}"} ; ,OverrideFlaskDuration:70, instantRecoveryOnLowLife: true, } ; if flask has special faster/slower recovery affixes/suffixes, specify override recovery time in deciseconds, e.g. 7 seconds = 70
PlayerConfig["Default"].FlaskConfig[2]:={Hotkey:"{2}"}
PlayerConfig["Default"].FlaskConfig[3]:={Hotkey:"{3}"}
PlayerConfig["Default"].FlaskConfig[4]:={Hotkey:"{4}"}
PlayerConfig["Default"].FlaskConfig[5]:={Hotkey:"{5}"}
PlayerConfig["YourHardcorePlayerName"]:={minLifeRatioToDrink: 0.7, minManaRatioToDrink: 0.35, minManaToDrink: 70, minLifeRatioToQuit: 0.4}
PlayerConfig["YourHardcorePlayerName"].FlaskConfig:=[]
PlayerConfig["YourHardcorePlayerName"].FlaskConfig[1]:={Hotkey:"{1}"}
PlayerConfig["YourHardcorePlayerName"].FlaskConfig[2]:={Hotkey:"{2}"}
PlayerConfig["YourHardcorePlayerName"].FlaskConfig[3]:={Hotkey:"{3}"}
PlayerConfig["YourHardcorePlayerName"].FlaskConfig[4]:={Hotkey:"{4}"}
PlayerConfig["YourHardcorePlayerName"].FlaskConfig[5]:={Hotkey:"{5}"}
autoPotionsState:=true
WindowQueuedFlaskEffects:=[] ;keyed by "%hwnd%%CurrPid%", hpQueueEndtime, manaQueueEndtime
baseMgrPtr:=0x68ce7C ;0.10.4, delete for script to try to auto-scan for it in newer versions, scan takes 4-5 seconds
basePtrAoBArray:=[0x6A,0xFF,0x68,"?","?","?","?",0x50,0x64,"?","?","?","?","?","?",0xA1,"?","?","?","?",0x81,0xEC,"?","?","?","?",0x53,0x55,0x56,0x57,0x33,0xFF,0x3B,0xC7]
basePtrAobOffset:=0x10
WindowBasicsCache:=[] ; keyed by "%hwnd%%CurrPid%", entries are objects with properties processHandle, moduleBase, moduleSize, baseFramePtr
#Include AutoHotkeyMemoryLib.ahk
Loop
{
AutoPotions()
}
GetWindowBasics(hwnd, byref mB="", byref pH="", byref mS="")
{
global WindowBasicsCache
global cliexe
WinGet, CurrPid, PID, ahk_id %hwnd%
k="%hwnd%%CurrPid%"
mB:=WindowBasicsCache[k].mBase
mS:=WindowBasicsCache[k].mSize
if mB=
{
WindowBasicsCache[k]:=Object()
GetModuleInfo(cliexe, CurrPid, mB, mS)
if (mB="" || mS="")
{
MsgBox, Failed to obtain moduleBase or moduleSize for PID %CurrPid%, script will now terminate
ExitApp
}
WindowBasicsCache[k].mBase:=mB
WindowBasicsCache[k].mSize:=mS
}
pH:=WindowBasicsCache[k].ProcessHandle
if pH=
{
pH:=GetProcessHandle(CurrPid)
if (pH="" || pH=-1)
{
MsgBox, Invalid process handle obtained for PID %CurrPid%, script will now terminate
ExitApp
}
WindowBasicsCache[k].ProcessHandle:=pH
}
}
ScanBaseMgrPtr(mBase,pH,moduleSize)
{
global basePtrAoBArray
global basePtrAobOffset
global baseMgrPtr
aobResult:=AobScan(pH,mBase,moduleSize,basePtrAoBArray)
if aobResult
{
SetFormat, IntegerFast, hex
baseMgrPtr:=ReadMemUInt(pH,mBase+aobResult+basePtrAobOffset)-mBase
MsgBox, PoE Base ptr found with AoB Scan baseMgrPtr = %baseMgrPtr%, save this value to script for quick startup
SetFormat, IntegerFast, dec
}
else
{
MsgBox, baseMgrPtr not found with AoBScan, script will now terminate
ExitApp
}
}
GetFrameBase(hwnd)
{
global baseMgrPtr
global WindowBasicsCache
WinGet, CurrPid, PID, ahk_id %hwnd%
k="%hwnd%%CurrPid%"
fB:=WindowBasicsCache[k].fBase
if fB=
{
GetWindowBasics(hwnd, mBase, pH, mSize)
if baseMgrPtr=
{
ScanBaseMgrPtr(mBase, pH, mSize)
}
fB:=GetMultilevelPointer(pH,[mBase+baseMgrPtr,4,0x7C,0x94])
WindowBasicsCache[k].fBase:=fB
}
return fB
}
GetUiBase(hwnd)
{
global baseMgrPtr
GetWindowBasics(hwnd, mBase, pH, mSize)
if baseMgrPtr=
{
ScanBaseMgrPtr(mBase, pH, mSize)
}
return GetMultilevelPointer(pH,[mBase+baseMgrPtr,4,0xFC,0x94,0x50])
}
ReadClientResolution(hwnd, ByRef w, ByRef h)
{
GetWindowBasics(hwnd,mBase,pH)
if (mBase!=0 && pH && pH!=-1)
{
FrameBase:=GetFrameBase(hwnd)
w:=ReadMemUInt(pH,FrameBase+0x1368)
h:=ReadMemUInt(pH,FrameBase+0x136C)
return true
}
}
ReadPlayerStats(hwnd, byRef PlayerStats)
{
GetWindowBasics(hwnd, mBase, pH)
fBase:=GetFrameBase(hwnd)
PlayerBase:=GetMultilevelPointer(pH,[fBase+0xb4,0x51C])
PlayerMain:=ReadMemUInt(pH,PlayerBase+4)
PlayerStatsOffset:=ReadMemUInt(pH,PlayerMain+0xC)
PlayerStats.MaxHP:=ReadMemUInt(pH,PlayerStatsOffset+0x24)
PlayerStats.CurrHP:=ReadMemUInt(pH,PlayerStatsOffset+0x28)
PlayerStats.ReservedHPFlat:=ReadMemUInt(pH,PlayerStatsOffset+0x30)
PlayerStats.ReservedHPPercent:=ReadMemUInt(pH,PlayerStatsOffset+0x34)
PlayerStats.MaxMana:=ReadMemUInt(pH,PlayerStatsOffset+0x48)
PlayerStats.ReservedManaFlat:=ReadMemUInt(pH,PlayerStatsOffset+0x54)
PlayerStats.ReservedManaPercent:=ReadMemUInt(pH,PlayerStatsOffset+0x58)
PlayerStats.CurrMana:=ReadMemUInt(pH,PlayerStatsOffset+0x4C)
PlayerStats.MaxNShield:=ReadMemUInt(pH,PlayerStatsOffset+0x6C)
PlayerStats.CurrNShield:=ReadMemUInt(pH,PlayerStatsOffset+0x70)
if (ReadMemUInt(pH, ReadMemUInt(pH,PlayerMain+0x14)+0x24)<8) ;names shorter than 7 chars are stored immediately in component
PlayerStats.Name:=ReadMemStr(pH, ReadMemUint(pH,PlayerMain+0x14)+0x10,30,"UTF-16") ;immediate name in component
else
PlayerStats.Name:=ReadMemStr(pH, GetMultilevelPointer(pH,[PlayerMain+0x14,0x10]),30,"UTF-16") ; otherwise pointer to name is stored
}
ReadFlasksData(hwnd, byRef FlasksData)
{
GetWindowBasics(hwnd, mBase, pH)
UiBase:=GetUiBase(hwnd)
if (!UiBase) ;not InGame
return
FlaskInvBase:=GetMultilevelPointer(pH,[UiBase+0x8d0,0x8e8,0x28])
Loop, 5
{
currFlaskPtr:=ReadMemUInt(pH,FlaskInvBase+(A_Index-1)*4)
if (currFlaskPtr!=0) ; there's a flask in said slot
{
FlasksData[A_Index]:={}
FlaskChargesPtr:=GetMultilevelPointer(ph,[currFlaskPtr,4,0x1C,4,4,0xC])
FlasksData[A_Index].ChargesCurrent:=ReadMemUInt(pH,FlaskChargesPtr+0xC)
FlasksData[A_Index].ChargesPerUse:=ReadMemUInt(pH,ReadMemUInt(pH,FlaskChargesPtr+8)+0xC)
if (FlasksData[A_Index].ChargesCurrent < FlasksData[A_Index].ChargesPerUse) ; not enough charges in this flask to use it, don't bother
continue
FlaskMetadataPtr:=GetMultilevelPointer(ph,[currFlaskPtr,0,8])
FlaskMetadataStr:=ReadMemStr(ph,FlaskMetadataPtr,70,"UTF-16")
FlaskTypeStr:=SubStr(FlaskMetadataStr,23)
FlasksData[A_Index].type:=FlaskTypeStr
FlaskLocalstatsPtr:=GetMultilevelPointer(ph,[currFlaskPtr,4,0x18,0x20,0xC])
if InStr(FlaskTypeStr, "Life")
{
FlasksData[A_Index].HPRegAmount:=ReadMemUInt(pH,FlaskLocalstatsPtr+4)
FlasksData[A_Index].EffectDuration:=ReadMemUInt(pH,FlaskLocalstatsPtr+0xC)
}
if InStr(FlaskTypeStr, "Mana")
{
FlasksData[A_Index].ManaRegAmount:=ReadMemUInt(pH,FlaskLocalstatsPtr+4)
FlasksData[A_Index].EffectDuration:=ReadMemUInt(pH,FlaskLocalstatsPtr+0xC)
}
if InStr(FlaskTypeStr, "Hybrid")
{
FlasksData[A_Index].HPRegAmount:=ReadMemUInt(pH,FlaskLocalstatsPtr+4)
FlasksData[A_Index].ManaRegAmount:=ReadMemUInt(pH,FlaskLocalstatsPtr+0xC)
FlasksData[A_Index].EffectDuration:=ReadMemUInt(pH,FlaskLocalstatsPtr+0x14)
}
if InStr(FlaskTypeStr, "FlaskUtility")
{
FlasksData[A_Index].EffectDuration:=ReadMemUInt(pH,FlaskLocalstatsPtr+0x4)
}
}
}
}
IsInGame(hwnd)
{
ubase:=GetUiBase(hwnd)
if ((ubase="") || ubase=0)
return false
else
return true
}
ReadCursorScreenPosition(hwnd,ByRef cX, ByRef cY)
{
GetWindowBasics(hwnd,mBase,pH)
if (mBase!=0 && pH && pH!=-1)
{
FrameBase:=GetFrameBase(hwnd)
cX:=ReadMemSInt(pH,FrameBase+0x157C)
cY:=ReadMemSInt(pH,FrameBase+0x1580)
return true
}
}
ScreenToClient(hwnd, ByRef x, ByRef y)
{
VarSetCapacity(pt, 8)
NumPut(x, pt, 0)
NumPut(y, pt, 4)
DllCall("ScreenToClient", "uint", hwnd, "uint", &pt)
x := NumGet(pt, 0, "int")
y := NumGet(pt, 4, "int")
VarSetCapacity(pt, 0)
}
GetClientCoords(byRef mx, byRef my)
{
hwnd:=WinActive("A")
CoordMode, Mouse, Screen
MouseGetPos, mx, my
ScreenToClient(hwnd,mx,my) ;get mouse pos relative to window client rect
}
GetFractionalCoords(ByRef fX, ByRef fY)
{
hwnd:=WinActive("A")
if (!IsInGame(hwnd))
GetClientCoords(mx,my)
else
ReadCursorScreenPosition(hwnd,mx,my)
ReadClientResolution(hwnd,w,h)
fX:=mx/w
fY:=my/h
}
GetClientCoordsFromFractional(hwnd, fX,fY, ByRef cX, ByRef cY)
{
ReadClientResolution(hwnd,w,h)
cX:=fX*w
cY:=fY*h
}
QuitToLoginScreen(hwnd)
{
global autoQuitPauseBeforeClick
global autoQuitSoftToleranceBeforeKill
if (!IsInGame(hwnd))
{
return
}
TrayTip, PoE quit to loginscreen, %A_Space% , 2
ReadClientResolution(hwnd,w,h)
cX:=0.5*w
cY:=0.4*h
p := cY << 16 | (cX & 0xffff)
quitStartTC:=A_TickCount
while (IsInGame(hwnd))
{
if ((A_TickCount-quitStartTC)>=autoQuitSoftToleranceBeforeKill)
WinKill, % "ahk_id" hwnd
ControlSend,,{Space}, ahk_id %hwnd%
ControlSend,,{Esc}, ahk_id %hwnd%
sleep, autoQuitPauseBeforeClick
PostMessage, 0x201, 1 , p, , ahk_id %hwnd%
PostMessage, 0x202, 0 , p, , ahk_id %hwnd%
sleep, autoQuitPauseBeforeClick
}
}
AutoPotions()
{
global autoPotionsWatchdogPeriod
global lagCompensation
global PlayerConfig
global WindowQueuedFlaskEffects
global cliname
global cliexe
global trayNotifications
global autoQuitMode
if (autoPotionsState!=true)
return
WinGet, WinID, List, %cliname%
Loop, %WinID%
{
WinGet, ProcModuleName, ProcessName, % "ahk_id" WinID%A_Index%
If(ProcModuleName!=cliexe) ; got a window with title "Path of Exile" but exe is not Client.exe, perhaps we have browser window open with PoE site, ignore it
continue
if (!IsInGame(WinID%A_Index%)) ;not ingame
continue
PlayerStats:={}
ReadPlayerStats(WinID%A_Index%, PlayerStats)
if (PlayerStats.MaxHP<1 || PlayerStats.CurrHP=0)
continue
if (PlayerConfig.HasKey(PlayerStats.Name))
CurrentConfig:=PlayerConfig[PlayerStats.Name]
else
CurrentConfig:=PlayerConfig["Default"]
if PlayerStats.MaxNShield>0
{
currNShieldRatio:=PlayerStats.CurrNShield/PlayerStats.MaxNShield
}
if (PlayerStats.MaxHP>1)
{
currLifeRatio:=PlayerStats.CurrHP/(PlayerStats.MaxHP-PlayerStats.ReservedHPFlat-PlayerStats.MaxHP*PlayerStats.ReservedHPPercent/100)
}
if CurrentConfig.HasZealotsOath
{
currLifeRatio:=currNShieldRatio
}
if (PlayerStats.MaxMana>0)
{
currManaRatio:=PlayerStats.CurrMana/(PlayerStats.MaxMana-PlayerStats.ReservedManaFlat-PlayerStats.MaxMana*PlayerStats.ReservedManaPercent/100)
}
if (currLifeRatio<CurrentConfig.minLifeRatioToQuit || currNShieldRatio<CurrentConfig.minNShieldRatioToQuit)
{
if (autoQuitMode=0)
{
TrayTip, PoE autoPotions AutoQuit by closing window, specified min life reached, %A_Space% , 2
WinKill, % "ahk_id" WinID%A_Index%
}
else if (autoQuitMode=1)
{
QuitToLoginScreen(WinID%A_Index%)
}
continue
}
if (CurrentConfig.disableAutoPotions)
continue
FlasksData:=[]
ReadFlasksData(WinID%A_Index%,FlasksData)
WinGet, CurrPID, PID, % "ahk_id" WinID%A_Index%
hwnd:=WinID%A_Index%
k="%hwnd%%CurrPid%"
if (!WindowQueuedFlaskEffects.HasKey(k))
{
WindowQueuedFlaskEffects[k]:={}
}
if (currLifeRatio<CurrentConfig.minLifeRatioToPopGranite || currNShieldRatio<CurrentConfig.minNShieldRatioToPopGranite)
if ((!WindowQueuedFlaskEffects[k].HasKey("graniteQueueEndtime")) || (A_TickCount>=(WindowQueuedFlaskEffects[k].graniteQueueEndtime-lagCompensation)))
Loop, 5
if (FlasksData[A_Index].type="FlaskUtility5") ; granite flask
{
if CurrentConfig.FlaskConfig[A_Index].HasKey("OverrideFlaskDuration")
EffectDuration:=CurrentConfig.FlaskConfig[A_Index].OverrideFlaskDuration
else
EffectDuration:=FlasksData[A_Index].EffectDuration
WindowQueuedFlaskEffects[k].graniteQueueEndtime:=A_TickCount+EffectDuration*100
if (trayNotifications)
{
pname:=PlayerStats.Name
TrayTip, PoE autoPotions popping Granite flask %A_Index% on %pname%, %A_Space% , 2
}
hKey:=CurrentConfig.FlaskConfig[A_Index].Hotkey
ControlSend,,%hkey%, % "ahk_id" hwnd
break
}
if (currLifeRatio=1)
WindowQueuedFlaskEffects[k].hpQueueEndtime:=A_TickCount
if (currManaRatio=1)
WindowQueuedFlaskEffects[k].ManaQueueEndtime:=A_TickCount
if (currLifeRatio<CurrentConfig.minLifeRatioToDrink || (PlayerStats.MaxHP>1 && PlayerStats.CurrHP<CurrentConfig.minLifeToDrink))
if ((!WindowQueuedFlaskEffects[k].HasKey("hpQueueEndtime")) || (A_TickCount>=(WindowQueuedFlaskEffects[k].hpQueueEndtime-lagCompensation)))
Loop, 5
if (FlasksData[A_Index].HasKey("HPRegAmount"))
{
if CurrentConfig.FlaskConfig[A_Index].HasKey("OverrideFlaskDuration")
EffectDuration:=CurrentConfig.FlaskConfig[A_Index].OverrideFlaskDuration
else
EffectDuration:=FlasksData[A_Index].EffectDuration
if ((CurrentConfig.FlaskConfig[A_Index].instantRecoveryOnLowLife) && ((PlayerStats.CurrHP/PlayerStats.MaxHP)<=0.35)) ; "Low life" can be caused by auras hp reservation from blood magic
EffectDuration:=lagCompensation
WindowQueuedFlaskEffects[k].hpQueueEndtime:=A_TickCount+EffectDuration*100
if (FlasksData[A_Index].HasKey("ManaRegAmount")) ; hybrid flask
WindowQueuedFlaskEffects[k].ManaQueueEndtime:=A_TickCount+EffectDuration*100
if (trayNotifications)
{
pname:=PlayerStats.Name
TrayTip, PoE autoPotions sipping HP flask %A_Index% on %pname%, %A_Space% , 2
}
hKey:=CurrentConfig.FlaskConfig[A_Index].Hotkey
ControlSend,,%hkey%, % "ahk_id" hwnd
break
}
if (PlayerStats.MaxMana>0 && (currManaRatio<CurrentConfig.minManaRatioToDrink || PlayerStats.CurrMana<CurrentConfig.minManaToDrink))
if ((!WindowQueuedFlaskEffects[k].HasKey("ManaQueueEndtime")) || (A_TickCount>=(WindowQueuedFlaskEffects[k].ManaQueueEndtime-lagCompensation)))
Loop, 5
if (FlasksData[A_Index].HasKey("ManaRegAmount"))
{
if CurrentConfig.FlaskConfig[A_Index].HasKey("OverrideFlaskDuration")
{
EffectDuration:=CurrentConfig.FlaskConfig[A_Index].OverrideFlaskDuration
}
else
EffectDuration:=FlasksData[A_Index].EffectDuration
if ((CurrentConfig.FlaskConfig[A_Index].instantRecoveryOnLowLife) && ((PlayerStats.CurrHP/PlayerStats.MaxHP)<=0.35))
EffectDuration:=lagCompensation
WindowQueuedFlaskEffects[k].ManaQueueEndtime:=A_TickCount+EffectDuration*100
if (FlasksData[A_Index].HasKey("HPRegAmount")) ; hybrid flask
WindowQueuedFlaskEffects[k].hpQueueEndtime:=A_TickCount+EffectDuration*100
hKey:=CurrentConfig.FlaskConfig[A_Index].Hotkey
if (trayNotifications)
{
pname:=PlayerStats.Name
TrayTip, PoE autoPotions sipping mana flask %A_Index% on %pname%, %A_Space% , 2
}
ControlSend,,%hkey%, % "ahk_id" hwnd
break
}
}
Sleep, %autoPotionsWatchdogPeriod%
}
F1::
global autoPotionsState
global trayNotifications
autoPotionsState:=not autoPotionsState
if (trayNotifications)
{
if (autoPotionsState=true)
TrayTip, PoE autoPotions is on, %A_Space% , 2
else
TrayTip, PoE autoPotions is off, %A_Space% , 2
}
return
F2::
GetClientCoords(mx,my)
GetFractionalCoords(fx,fy)
msgbox, mx=%mx% my=%my% fx=%fx% fy=%fy%
return
F4::
QuitToLoginScreen(WinActive("A"))
return
AutoHotkeyMemoryLib.ahk
(!empty($user->lang['CODE'])) ? $user->lang['CODE'] : ucwords(strtolower(str_replace('_', ' ', 'CODE'))):
if (A_PtrSize != 4)
{
MsgBox, You are not running 32-bit version of Autohotkey L, reinstall correct version. Script will now terminate.
ExitApp
}
GetModuleInfo(ModuleName, PID, byRef mBase="", byRef mSize="")
{
TH32CS_SNAPMODULE := 0x00000008
INVALID_HANDLE_VALUE = -1
VarSetCapacity(me32, 548, 0)
NumPut(548, me32)
snapMod := DllCall("CreateToolhelp32Snapshot", "Uint", TH32CS_SNAPMODULE, "Uint", PID)
If (snapMod = INVALID_HANDLE_VALUE) {
Return 0
}
If (DllCall("Module32First", "Uint", snapMod, "Uint", &me32)){
If StrGet(&me32 + 32, "cp0")=ModuleName
{
mBase:=NumGet(&me32 + 20)
mSize:=NumGet(&me32 + 24)
DllCall("CloseHandle", "UInt", snapMod)
Return
}
while(DllCall("Module32Next", "Uint", snapMod, "UInt", &me32))
{
If StrGet(&me32 + 32, "cp0")=ModuleName
{
mBase:=NumGet(&me32 + 20)
mSize:=NumGet(&me32 + 24)
DllCall("CloseHandle", "UInt", snapMod)
Return
}
}
}
DllCall("CloseHandle", "Uint", snapMod)
}
GetProcessHandle(pid)
{
return DllCall("OpenProcess", "UInt", 0x10, "UInt", 0, "UInt", pid, "UInt")
}
ReadMemFloat(ProcessHandle, MADDRESS)
{
if DllCall("ReadProcessMemory","UInt",ProcessHandle,"UInt",MADDRESS,"Float*",MVALUE,"UInt",4,"UInt*",0)!=0
{
return MVALUE
}
}
ReadMemUInt(ProcessHandle, MADDRESS)
{
if DllCall("ReadProcessMemory","UInt",ProcessHandle,"UInt",MADDRESS,"UInt*",MVALUE,"UInt",4,"UInt*",0)!=0
{
return MVALUE
}
}
ReadMemSInt(ProcessHandle, MADDRESS)
{
if DllCall("ReadProcessMemory","UInt",ProcessHandle,"UInt",MADDRESS,"Int*",MVALUE,"UInt",4,"UInt*",0)!=0
{
return MVALUE
}
}
WriteMemUInt(ProcessHandle, MADDRESS, val)
{
DllCall("WriteProcessMemory","UInt",ProcessHandle,"UInt",MADDRESS,"UInt*",val,"UInt",4,"UInt*",0)!=0
}
WriteMemSInt(ProcessHandle, MADDRESS, val)
{
DllCall("WriteProcessMemory","UInt",ProcessHandle,"UInt",MADDRESS,"Int*",val,"UInt",4,"UInt*",0)!=0
}
WriteMemFloat(ProcessHandle, MADDRESS, val)
{
DllCall("WriteProcessMemory","UInt",ProcessHandle,"UInt",MADDRESS,"Float*",val,"UInt",4,"UInt*",0)!=0
}
ReadMemStr(ProcessHandle, MADDRESS, maxlen=255, cp="cp0")
{
VarSetCapacity(MVALUE,maxlen)
if DllCall("ReadProcessMemory","UInt",ProcessHandle,"UInt",MADDRESS,"PTR",&MVALUE,"UInt",maxlen,"UInt*",bytesread)!=0
{
Str:=StrGet(&MVALUE,cp)
VarSetCapacity(MVALUE,0)
return Str
}
VarSetCapacity(MVALUE,0)
}
GetMultilevelPointer(ProcessHandle, PARRAY)
{
if PARRAY._MaxIndex()<2
return
if (DllCall("ReadProcessMemory","UInt",ProcessHandle,"UInt",PARRAY[1],"UInt*",currOffset,"UInt",4,"UInt*",0)!=0)
{
i:=2
while (i<=PARRAY._MaxIndex() && DllCall("ReadProcessMemory","UInt",ProcessHandle,"UInt",currOffset+PARRAY[i],"UInt*",currOffset,"UInt",4,"UInt*",0)!=0)
{
i:=i+1
}
if (i>PARRAY._MaxIndex())
{
return currOffset
}
}
}
AobScan(ProcessHandle,mBase,mSize, ByRef patternArray)
{
if (patternArray._MaxIndex()>mSize)
{
MsgBox, aobscan fail : pattern array is larger than module size
return
}
VarSetCapacity(ClientCodeSegment,mSize)
if (DllCall("ReadProcessMemory","UInt",ProcessHandle,"UInt",mBase,"PTR",&ClientCodeSegment,"UInt",mSize,"UInt*",bytesread)!=0)
{
pLen:=patternArray._MaxIndex()
if (bytesread<>mSize)
{
VarSetCapacity(ClientCodeSegment,0)
MsgBox, aobscan fail : mSize=%mSize% bytesread=%bytesread%
return
}
i:=0
while (i<=mSize-pLen-1)
{
j:=1
while (j<=pLen)
{
if (patternArray[j]="?" || NumGet(ClientCodeSegment, i+j-1, "UChar")=patternArray[j])
{
j:=j+1
}
else
{
break
}
}
if (j>pLen)
{
VarSetCapacity(ClientCodeSegment,0)
return i
}
i:=i+1
}
}
MsgBox, aobscan fail : pattern not found
VarSetCapacity(ClientCodeSegment,0)