[Tool] AutoHotkey scripts

My current code is different with 3 skills each with different behaviour

  • Wind Devil - cast as soon as cooldown ends when you hold Left Click (key sent every 200 ms)

  • War Cry - cast as soon as cooldown ends when you hold both Left Click and Shift (key sent every 200ms)

  • Wendigo Totem - cast only once as soon as cooldown ends when you hold both Left Click and Shift and then it waits for 14 seconds to cast it again unless you release, click and hold Shift again, then you can cast it sooner (as soon as cooldown ends if you click and release Shift frequently, i.e. if moving from pack to pack when you want Totems more often) [this is how I play - I hold Left Click permanently, hold Shift when fighting one pack, Right click for movement skill]

There’s a mechanism to ensure than Wendigo Totem is definitely cast and not interrupted by Primal Strike from Left Click or other skills

~*LButton::
    KeyWait, LButton, T0.2
    if ErrorLevel
    {    
        counter := 0
        while GetKeyState("LButton", "P")
        {
            if GetKeyState("Shift", "P")
            {
                if counter < 10 ;we try casting Totem 10 times (for 10 x 200 ms = 2s) for 100% chance
                {
                    Send {7} ; Wendigo Totem
                }
                Send {6} ; War Cry
                counter := Mod(counter + 1, 70) ;70 means 70 x 200 ms = 14s because Totem has 15s duration
            }
            else
                counter := 0
            Send {8} ; Wind Devil        
            Sleep, 200
        }
    }
Return

Hide All Items (Loot Filter) during fight

  1. Short version if you fight with Left Click and have a simple script

    ~*LButton::
     Send {LAlt}
    Return
    
    ~*LButton UP::
     Send {LAlt}
    Return
    
  2. Longer version example (when you move with Left Click & fight with Left Click + Shift)

    ~*LButton::
     KeyWait, LButton, T0.2
     if ErrorLevel
     {    
         items_hidden := false ;ITEMS SHOWN BEFORE THE FIGHT
       
       while GetKeyState("LButton", "P")
     {   
         ;SKILLS CAST WHILE WALKING
         
         if GetKeyState("Shift", "P") ;FIGHT MODE
         {
             if !items_hidden ;HIDING ITEMS IF SHIFT IS HELD
             {
                 Send {LAlt}
                 items_hidden := true
             }
             
             ;SKILLS CAST WHILE FIGHTING
         }
         else
         {
             if items_hidden ; SHOWING ITEMS IF SHIFT IS RELEASED FIRST
             {
                 items_hidden := false
                 Send {LAlt}
             }
         }
         Sleep, 200
     }
     }
    Return
    
     ~*LButton UP:: ;SHOWING ITEMS IF LEFT CLICK IS RELEASED FIRST
     if items_hidden
     {
         items_hidden := false
         Send {LAlt}
     }
    Return

Again, what a nice idea!

Thanks, ideas keep coming naturally when you are too lazy to practice execution/click many buttons/master piano builds :grin: and I really don’t like focusing on cooldowns while fighting

; first I've done it with external main loop and Sleep but
; setTimer function is actually simpler and more powerful

SendMode Input
#NoEnv
#SingleInstance Force
#MaxHotkeysPerInterval 1000
#MaxThreadsPerHotkey 1
#KeyHistory 50
SetWorkingDir %A_ScriptDir%

doom_bolt_allowed := true

AllowDoomBolt()
{
    global doom_bolt_allowed
    doom_bolt_allowed := true
}

~*RButton::
    KeyWait, RButton, T0.2
    if ErrorLevel
    {
        counter := 0

        while GetKeyState("RButton", "P")
        {
            if doom_bolt_allowed
            {
                send {0}
                doom_bolt_allowed := false
                SetTimer, AllowDoomBolt, -13000
                /*
                    '-13000' above makes AllowDoomBolt run only once after 13s
                    whereas with '13000' the function would run every 13s
                */                
            }
            
            if !Mod(counter-5, 40) ;blood of dreeg
                send {9}
                
            if !Mod(counter-20, 100) ;seal of consumption
                send {8}
                
            Sleep, 100
            counter := Mod(counter + 1, 2600)                
        }
    }
Return

Code can be probably rewritten with Timers only instead of the counter.

OUTDATED there’s a better version without loop and sleeps using timers
Automatic camera (following your character)


b - rotate left
n - rotate right
~*LButton::
    KeyWait, LButton, T0.2 ;need to hold the button for 200ms to follow
    if ErrorLevel
    {         
        while GetKeyState("LButton", "P")
        {
            WinGetActiveStats, Title, Width, Height, X, Y
            MouseGetPos, xpos, ypos 
            xpos := xpos - Width/2 ;vector from the middle of the screen to the cursor
            ypos := Height/2 - ypos
            
            if xpos*xpos + ypos*ypos < 40000 ;<200 pix from center -> no rotation
                continue

            if (ypos > 0) and ( Abs(ATan(xpos / ypos)) * 57.29578 < 20) 
                continue ; 20° degrees from 12 o'clock -> 40° no rotation sector
            
            if xpos > 0 ;right half of screen -> rotate right
            {
                Send {n down}
                Sleep, 100 ; haven't tested other values here
                Send {n up}
            }
            else ;left half of screen -> rotate left
            {
                Send {b down}
                Sleep, 100
                Send {b up}
            }
            
            Sleep, 20
        }
    }
Return

[edit] added 200 pixel protection (no rotation if cursor is closer than 200 pixels to the middle of the screen) so that there’s no rotation during melee fights

[edit2] changing sector of no rotation near 12 o’clock from 10 x 2 = 20° to 20 x 2 = 40° degrees so that there’s less rotation and direction changes when moving upwards and changing direction only slightly; set this parameter to your liking

1 Like

this is a-freakin-mazing!

1 Like

If I just copy your text now AHK open an error message which says it doesn’t accept ;

Fixed. The ‘;’ shouldn’t be right after the code, there should be ’ ’ (space) in between

BTW another thing that could be added to the script is blocking the rotation for some time after you press buttons responsible for using skills

For example when you use a Movement Skill, the rotation stops (because it’s a little bit weird when the screen is rotatin when you are in the middle of animation)

You can also check this script Faster camera rotation using keyboard or scroll wheel I’ve been using it and now I may switch to this (need to test it longer)

The biggest problem of the script is that if I minimize GD and want to use my mouse without typing b or n I have to close the script everytime :sweat_smile:

Can I configure a global pause key for AHK?

Here’s a fuller version of script with suspension of hotkeys when Grim Dawn is minimized (the initial setting are recommended as well)

SendMode Input
#NoEnv
#SingleInstance Force
#MaxHotkeysPerInterval 1000 
#MaxThreadsPerHotkey 1
#KeyHistory 50
SetWorkingDir %A_ScriptDir%

hotkeys_turned_on := true

Loop
{
    Sleep, 1000
    if WinActive("Grim Dawn", , "Grim Dawn ")
    {
        if hotkeys_turned_on
            continue
    }
    else if !hotkeys_turned_on
        continue
    
    hotkeys_turned_on ^= true
    Suspend, Toggle
}

~*LButton::
    KeyWait, LButton, T0.2 ;need to hold the button for 200ms to follow
    if ErrorLevel
    {         
        while GetKeyState("LButton", "P")
        {
            WinGetActiveStats, Title, Width, Height, X, Y
            MouseGetPos, xpos, ypos 
            xpos := xpos - Width/2 ;vector from the middle of the screen to the cursor
            ypos := Height/2 - ypos
            
            if xpos*xpos + ypos*ypos < 40000 ;<200 pix from center -> no rotation
                continue

            if (ypos > 0) and (Abs(ATan(xpos / ypos)) * 57.29578 < 20) 
                continue ; 20° degrees from 12 o'clock -> 40° no rotation sector
            
            if xpos > 0 ;right half of screen -> rotate right
            {
                Send {n down}
                Sleep, 100 ; haven't tested other values here
                Send {n up}
            }
            else ;left half of screen -> rotate left
            {
                Send {b down}
                Sleep, 100
                Send {b up}
            }
            
            Sleep, 20
        }
    }
Return

Sometimes people use something similar to

#IfWinActive ahk_class Grim Dawn

to make script only work in a particular application but such lines don’t work perfectly for me

Adding a specific hotkey to pause a script also work I think
Tab::Suspend

1 Like

Perfect, thanks.

Because you mentioned it in the other thread.
If people get dizzy it is mostly because they play at a low framerate (30 for me) and have motion blur disabled.
I play with nearly stable 144 fps and adaptive sync enabled but I think even 60 fps should be enough.
GD does not have the motion blur option so I guess a high framerate is the only solution.

You may be on to something but I personally just never get dizzy (in any real life situation) so I think that the genetic factor plays a role as well. In Grim Dawn I have 40 - 50 FPS

Also try playing Grim Dawn with a controller. It’s great. And you can use controller for fighting and mouse for shopping and the game dynamically switches between them. Rotating the camera with the right stick is great and you can configure everything in Steam Big Picture (for example I set my analog to activate Wind Devil whenever I touch it so it’s cast kinda automatically when I move my character with this analog stick)

I unfortunately don’t know how to use AutoHotkey with gamepads, it’s definitely not straightforward.

Didn’t know you can play GD with controller just that well. Will definitly try that out.

Have you tried to remap the xinput inputs to other keys which work with AHK?
Just an idea.
Also depends on controller for sure. For the steam controller that shouldn’t be a problem in general.

It should work but the syntaxis must be

#IfWinActive, Grim Dawn

all the text below this switch will be working only if Grim Dawn window is active.

I’m 90% sure I tested it before but it was not working 100% of the time or as expected and I moved to this ugly Loop as a consequence.

[edit] Ok, I’ve tested it and it works now. I’m keeping it for further testing.

@slippycheeze

Test failed. This makes script also work when you are using Grim Tools website -> because the tab in web browser start with “Grim Dawn”. However you can fix it with

SetTitleMatchMode, 3
#IfWinActive, Grim Dawn

The first line forces the window title to exactly match IfWinActive argument (not start with it)
That may have been the reason I ditched IfWinActive before

Haven’t tried anything yet but when I was browsing the internet for some code it seem like too much work/no guarantee it will work in the end.

However you can kinda use AHK scripts if you bind some keyboard key to gamepad button (in Steam Controller Configuration Legacy Keys submenu). Then it works like this

  1. you press some gamepad button
  2. it makes chosen keyboard key to be pressed
  3. Grim Dawn switches automatically from Gamepad mode to Mouse & Keyboard mode (wish these two modes could work together)
  4. And know you have to move the analog stick for Grim Dawn to go back to Gamepad mode again unless you don’t want your controller to work

That’s how Inquisitor Seal cast under your feet when using controller works

The easiest way to use M&K actions without modifying xinput and staying in gamepad mode would be a hardware converter like CronusMAX.
But surely you have to buy it first.

a) From my experience, event mode is way better for games (esp on slower pcs) than input.
Input is ofc faster, but its TOO fast (since it has no inherit delay, + it buffers).

using my script(s) in input mode, the game(s) often fails to recognize an action or modifier states get fubar.
eventmode increased stability of scripts alot plus you can set a global delay and presstime

b) in gd at least, you can send the keys in blind mode what reduces strain on the games i/o. that reduces the chances for stalling shift modifier eg when you use mouse + shift combo for hold position casting. (gd doesnt care when you use modifiers on action keys) eg shift-5 fires slot5 as 5 does.

c) mind to explain the reasons for your autoexec choices?
i often play with them and im curious about why you chose those.

#NoEnv
#SingleInstance Force
*#MaxHotkeysPerInterval 1000 *
#MaxThreadsPerHotkey 1
#KeyHistory 50

p.s
Setkeydelay, delay, pressduration
is crucial in messy situations (and individual dependent on machine specs)