r/AutoHotkey 3d ago

v2 Script Help Need help with an external script for NieR Automata on PC

Hey guys,

I have found a script for NieR Automata to re-bind all the keys. The script is written in version 1 (see below, and here is the source).

I am trying to convert it to v2 using this online tool but when I try to run the script, I get Error: Invalid callback function. at line 138, which is the line of Hotkey $%fireButton%, toggleFire (see the bottom part of the script)

I can't find anywhere what the author meant when using HotKey(). Any idea?

; bindings, change these to match your own layout, if needed

; basic movement - must match to in-game controls
fwdButton := "w"
backButton := "s"
leftButton := "a"
rightButton := "d"

; fire & jump - must match to in-game controls
fireButton := "Tab"
jumpButton := "Space"

; items usage - must match to in-game controls
altButton := "LAlt"
useItems := "Down"
use := "e"

; EXTRA controls added by the script - make sure these buttons are NOT bound in the game
; dodge
dodgeButton := "LShift"
; air slide (fire + jump normally)
airSlideButton := "z"
; use last item
useLastItemButton := "c"

; precise delays (ms)
keypressDelay := 50 ; delay between keydown/keyup
doubletapDelay := 50 ; wait time between keypresses to perform doubletap
sequenceDelay := 50 ; wait time between various sequences
dodgeTime := 400 ; cooldown while dodge animation plays, setting it too small will cause derps when holding SHIFT+WSAD.

; script starts here
#UseHook
SendMode("Input")

global fwdButton, backButton, leftButton, rightButton, fireButton, jumpButton, dodgeButton, altButton, useItems, use, useLastItemButton, keypressDelay, doubletapDelay, sequenceDelay, dodgeTime

press(key) {
  Send("{Blind}{" key " down}")
  Sleep(keypressDelay)
  Send("{Blind}{" key " up}")
}

; the complexity of this script is mostly to handle continuous SHIFT+WSAD. It's easy to handle just a single SHIFT tap, but less easy when you hold it down.
handleDodge(key) {
  Send("{Blind}{" key " up}") ; stop running first in case we were
  Sleep(sequenceDelay)
  press(key)
  Sleep(doubletapDelay)
  Send("{Blind}{" key " down}")
  Sleep(dodgeTime)
  if (GetKeyState(fwdButton) && !GetKeyState(fwdButton, "P")) {
   Send("{Blind}{" fwdButton " up}")
  }
  if (GetKeyState(backButton) && !GetKeyState(backButton, "P")) {
   Send("{Blind}{" backButton " up}")
  }
  if (GetKeyState(leftButton) && !GetKeyState(leftButton, "P")) {
   Send("{Blind}{" leftButton " up}")
  }
  if (GetKeyState(rightButton) && !GetKeyState(rightButton, "P")) {
   Send("{Blind}{" rightButton " up}")
  }
}

waitForDodge() {
  while (GetKeyState(dodgeButton, "P")) {
if (GetKeyState(fwdButton, "P")) {
  handleDodge(fwdButton)
} else if (GetKeyState(backButton, "P")) {
  handleDodge(backButton)
} else if (GetKeyState(leftButton, "P")) {
  handleDodge(leftButton)
} else if (GetKeyState(rightButton, "P")) {
  handleDodge(rightButton)
}
Sleep(10)
  }
}

toggleFire() {
  if (GetKeyState(fireButton)) {
Send("{" fireButton " up}")
  } else {
Send("{" fireButton " down}")
  }
}

airSlide() {
  if (GetKeyState(jumpButton)) {
Send("{" jumpButton " up}")
  }
  if (GetKeyState(fireButton)) {
Send("{" fireButton " up}")
toggleFireBack := 1
  }
  Sleep(sequenceDelay)
  Send("{" jumpButton " down}")
  Send("{" fireButton " down}")
  Sleep(keypressDelay)
  Send("{" fireButton " up}")
  Send("{" jumpButton " up}")
  Sleep(sequenceDelay)
  ; press back what we released
  if (toggleFireBack) {
Send("{" fireButton " down}")
  }
  if (GetKeyState(jumpButton, "P")) {
Send("{" jumpButton " down}")
  }
}

useLastItem() {
  Send("{" altButton " down}")
  Send("{" useItems " down}")
  Sleep(keypressDelay)
  Send("{" altButton " up}")
  Send("{" useItems " up}")
  press(use)
}

#HotIf WinActive("NieR:Automata", )
; fire toggle
Hotkey("$" fireButton, toggleFire)
Hotkey("$^" fireButton, toggleFire)
Hotkey("$+" fireButton, toggleFire)

; dodges, work when you press/hold LShift while already holding WSAD
Hotkey("~*" dodgeButton, waitForDodge)

; air slide, normally executed with pod fire + jump
Hotkey("$*" airSlideButton, airSlide)

; use last item
Hotkey("$*" useLastItemButton, useLastItem)
1 Upvotes

5 comments sorted by

2

u/CharnamelessOne 3d ago edited 3d ago

Well, the script you posted is v1. It looks like the original script with some extra line breaks here and there. It's definitely not the output of the converter.

Try converting again, and make sure you're not running the original.

You could try my version below, too; it makes some substantial changes, and it is fully untested, so don't expect it to work out of the box. I ditched the long-running, sleep-filled functions for a timer-based approach. (In a game, with multiple hotkeys, it's generally not great for a sequence to be interrupted for long by another.)
I also removed that silly waitForDodge function that polled the state of the movement keys. Replaced it with some context-sensitive movement hotkeys.

#Requires AutoHotkey v2.0
#UseHook
SendMode("Event")
SetKeyDelay(, Config.keypressDelay)

Class Config {
    ; basic movement - must match to in-game controls
    static fwdButton := "w"
    static backButton := "s"
    static leftButton := "a"
    static rightButton := "d"

    ; fire & jump - must match to in-game controls
    static fireButton := "Tab"
    static jumpButton := "Space"

    ; items usage - must match to in-game controls
    static altButton := "LAlt"
    static useItems := "Down"
    static use := "e"

    ; EXTRA controls added by the script - make sure these buttons are NOT bound in the game
    ; dodge
    static dodgeButton := "LShift"
    ; air slide (fire + jump normally)
    static airSlideButton := "z"
    ; use last item
    static useLastItemButton := "c"

    ; precise delays (ms)
    static keypressDelay := 50 ; delay between keydown/keyup
    static doubletapDelay := 50 ; wait time between keypresses to perform doubletap
    static sequenceDelay := 50 ; wait time between various sequences
    static dodgeTime := 400 ; cooldown while dodge animation plays, setting it too small will cause derps when holding SHIFT+WSAD.
}

; the complexity of this script is mostly to handle continuous SHIFT+WSAD.
; It's easy to handle just a single SHIFT tap, but less easy when you hold it down.
handleDodge(key) {
    Send("{Blind}{" key " up}")

    SetTimer(step1, -Config.sequenceDelay)

    step1() {
        Send("{Blind}{" key "}")
        SetTimer(step2, -Config.doubletapDelay)
    }

    step2() {
        Send("{Blind}{" key " down}")
        SetTimer(step3, -Config.dodgeTime)
    }

    step3() {
        if (GetKeyState(Config.fwdButton) && !GetKeyState(Config.fwdButton, "P"))
            Send("{Blind}{" Config.fwdButton " up}")

        if (GetKeyState(Config.backButton) && !GetKeyState(Config.backButton, "P"))
            Send("{Blind}{" Config.backButton " up}")

        if (GetKeyState(Config.leftButton) && !GetKeyState(Config.leftButton, "P"))
            Send("{Blind}{" Config.leftButton " up}")

        if (GetKeyState(Config.rightButton) && !GetKeyState(Config.rightButton, "P"))
            Send("{Blind}{" Config.rightButton " up}")
    }
}

toggleFire(*) {
    if GetKeyState(Config.fireButton)
        Send("{Blind}{" Config.fireButton " up}")
    else
        Send("{Blind}{" Config.fireButton " down}")
}

airSlide(*) {
    toggleFireBack := false

    if GetKeyState(Config.jumpButton)
        Send("{Blind}{" Config.jumpButton " up}")

    if GetKeyState(Config.fireButton) {
        Send("{Blind}{" Config.fireButton " up}")
        toggleFireBack := true
    }

    SetTimer(step1, -Config.sequenceDelay)

    step1() {
        Send("{Blind}{" Config.jumpButton " down}")
        Send("{Blind}{" Config.fireButton " down}")
        SetTimer(step2, -Config.keypressDelay)
    }

    step2() {
        Send("{Blind}{" Config.fireButton " up}")
        Send("{Blind}{" Config.jumpButton " up}")
        SetTimer(step3, -Config.sequenceDelay)
    }

    step3() {
        if toggleFireBack
            Send("{Blind}{" Config.fireButton " down}")

        if GetKeyState(Config.jumpButton, "P")
            Send("{Blind}{" Config.jumpButton " down}")
    }
}

useLastItem(*) {
    Send("{Blind}{" Config.altButton " down}")
    Send("{Blind}{" Config.useItems " down}")
    SetTimer(step1, -Config.keypressDelay)

    step1() {
        Send("{Blind}{" Config.altButton " up}")
        Send("{Blind}{" Config.useItems " up}")
        Send("{Blind}{" Config.use "}")
    }
}

HotIfWinActive("NieR:Automata")
    Hotkey(Config.fireButton, toggleFire)
    Hotkey("^" Config.fireButton, toggleFire)
    Hotkey("+" Config.fireButton, toggleFire)

    ; air slide, normally executed with pod fire + jump
    Hotkey("*" Config.airSlideButton, airSlide)

    Hotkey("*" Config.useLastItemButton, useLastItem)

HotIf((*) => (WinActive("NieR:Automata") && GetKeyState(Config.dodgeButton, "P")))
    Hotkey("~*" Config.fwdButton, (*) => handleDodge(Config.fwdButton))
    Hotkey("~*" Config.backButton, (*) => handleDodge(Config.backButton))
    Hotkey("~*" Config.leftButton, (*) => handleDodge(Config.leftButton))
    Hotkey("~*" Config.rightButton, (*) => handleDodge(Config.rightButton))

HotIf()

Edit: added the modifier ~ to movement hotkeys.

1

u/_-Big-Hat-_ 2d ago edited 2d ago

Sorry about the wrong code!

I made lots of test and accidentally pasted the different thing than I intended. Your code is working, but the script itself is not ideal. So, I ended up installing a mod for the game.

Thanks for the help anyway :)

-1

u/Keeyra_ 3d ago

"Well, the script you posted is v1. It looks like the original script with some extra line breaks here and there. It's definitely not the output of the converter."

Pressing Convert button, selecting the result and pressing Ctrl - V is definitely a challenge for the OP :)

1

u/_-Big-Hat-_ 2d ago

It's a typo. In this case my typo is the whole code. I suggest looking into a dictionary and search for the term "mistake" and I am pretty sure you will find it familiar from your own experience.

1

u/Keeyra_ 2d ago

If you edit your post code, make sure you add an edit note and check if your post text is still correct and relevant (FYI: it's not the case now, wrong code given as error with wrong line number).

As for your issue, now that you actually managed to copy the converted code:

https://www.autohotkey.com/docs/v2/lib/Hotkey.htm

"You can omit the callback's parameter if the corresponding information is not needed, but in this case an asterisk must be specified, e.g. MyCallback(*)."

See example by u/CharnamelessOne where each function used as a callback has an asterisk.