[Tool] AutoHotkey scripts

Better autocasting of multiple skills (frequencies, delays)

I’ve written such stuff for Diablo 3 but the code was very complicated (the loop was external always working in the background, different skills had different frequencies, you could pause them, toggle some of the skills etc) and I used the modulo (it’s clean and great for minimizing number of threads). It doesn’t need to be complicated though

Let’s assume the step for Modulo is 100 ms (instead of 1000 ms) so that we can set the frequencies to 1300 ms, 2700 ms etc instead of just 1, 2, 3, 4 s (can be changed to bigger 200, 500, 1000 ms if there are some performance issues)

Now we want choose the big number we are going to divide to check whether it’s time to use our skills. You want your skills to be cast 1) every 40 x 100 ms 2) every 300 x 100 ms. We want to choose a number that is divisible buy both 40 and 300. Let’s take the Lowest Common Multiple

LCM(40, 300) = 600
If we choose for example 3 skills with 1.3 s, 2.5 s and 10 s (13 x 100ms , 25 x 100 ms, 100 x 100 ms)
we would take
LCM(13, 25, 100) = 1300

Testing code for you to test

; my settings, highly recommend them for your scripts, they make a difference
SendMode Input
#NoEnv
#SingleInstance Force
#MaxHotkeysPerInterval 1000 
#MaxThreadsPerHotkey 1
#KeyHistory 50
SetWorkingDir %A_ScriptDir%

~*LButton::       ; * makes it work with Shift and other modifiers
    KeyWait, LButton, T0.2
    if ErrorLevel
    {
        counter := 0 ;0 for the first skill to be casted ASAP
                     ;1 for the first skill to be casted later
                     
        while GetKeyState("LButton", "P")
        {
            if !Mod(counter, 30) ;every 30 x 100 ms
                send {1}
            
            if !Mod(counter - 5, 30) ;every 30 x 100 ms, -5 for the 2th skill to be cast 0.5s later
                send {2}         ; to avoid skill conflict when you just started holding The Left Click
                
            if GetKeyState("Shift", "P") and !Mod(counter - 10, 30) ;combining with Shift for fun
                send {3}              ;every 30 x 100 ms, -10 for the 3th skill to be cast 1s later 
                                     ; to avoid conflict when you just started holding Left Click
                
            Sleep, 100 ; 100 ms instead of 1s for more freedom
            counter := Mod(counter + 1, 6000) ; in this example it could be 30 because
                                       ;  LCM(30, 30, 30) = 30 or any multiple of 30 like 6000
        }
    }
Return

The code doing exactly what you wanted (worse for testing, only changed fragment)

if !Mod(counter, 40) ;every 40 x 100 ms
                    send {1}
                
                if !Mod(counter - 5, 300) ;every 300 x 100 ms, -5 for the 2th skill to be cast 0.5s later
                    send {2}         ; to avoid skill conflict when you just started holding The Left Click
                    
                if !Mod(counter - 10, 300) 
                    send {3}              ;every 300 x 100 ms, -10 for the 3th skill to be cast 1s later 
                                         ; to avoid conflict when you just started holding Left Click
                    
                Sleep, 100 ; 100 ms instead of 1s for more freedom
                counter := Mod(counter + 1, 600) ; LCM(40, 300, 300) = 600
                                                          ; or any multiple of 600 i.e 40 x 300 x 300

The problem is in Grim Dawn when you want to cast 2-3 skills at the same time sometimes not all of them fire (the skills interrupt each other). You know, when you have your skill to be casted every 30s and it for some reason it’s not casted and you have to wait another 30s? You have a problem. My workaround for that has been trying to cast skills more often than needed (for example I would try to cast Totem/Wind Devil every 1s not every 5s) but the workaround has to be always tailored to the specific case. In this code I added time shifts for 2th, 3rd skills (counter -5, counter - 10) to prevent this when you start holding the Left Click. This could be combined with higher frequencies to be sure, for example trying to cast skills every 15 seconds instead of 30 seconds. You could probably improve the code so that there’s never conflict between skills but the code complication would be far greater than the benefits. You could make the counter global, to change the behavior a little bit. You could make the buffs to fire only once etc.

Another little problem is these times probably won’t be precise. What I mean is for example when the loop Sleep is 100 ms, the loop step will probably take longer than 100 ms (? I don’t know for sure though)

There’s also a possibility to have external loop working constantly in the background and you only “toggling” it with a hotkey, not having to hold a button if needed.

Tweak the code to your liking, it has lots of freedom and many ways of adjusting autocasting so that it plays very well. Almost anything can be achieved in Autohotkey but sometimes it takes me weeks to figure out some bugs/behaviors. I have lots of fun writing and testing these scripts so write if you have any problems. And as it’s always the case I’ve learnt something and got the code I’ll use myself.

Video of Testing Code (at first we don’t hold shift so Devouring Swarm doesn’t fire)

PS Some new stuff

dual mode Left Click Hold
(cast some skills when you just hold Left Click and some more skills when you additionaly hold Shift)

~*LButton:: ; ~ means Left Click still clicks, * means it works with Shift, Ctrl etc
    KeyWait, LButton, T0.2
    if ErrorLevel
    {
        while GetKeyState("LButton", "P")
        {
            Send {7} ;key for Wind Devil
            
            if GetKeyState("Shift", "P") ;When Shift pressed do something else as well
                Send {8} ;key responsible for War Cry/Wendigo Totem which we only 
                
            Sleep, 500 ; sleep time low to cast skills with very little delay
        }
    }
Return

suspending only selected hotkeys with a hotkey (not all of them with Suspend function):

q::
    Hotkey, ~*LButton, toggle
    Hotkey, ~*RButton, toggle
Return