Skip to content

Instantly share code, notes, and snippets.

@KarlRamstedt
Last active September 9, 2024 05:29
Show Gist options
  • Save KarlRamstedt/758553272a042c4a94bef02ab5bdec2c to your computer and use it in GitHub Desktop.
Save KarlRamstedt/758553272a042c4a94bef02ab5bdec2c to your computer and use it in GitHub Desktop.
Spam macros for Slide-Attack, Ability, Primary-fire and Melee in Warframe. Including non-spam Contagion macro.
#NoEnv ; For performance and compatibility with future AutoHotkey releases
SendMode Input ; For speed and reliability
SetBatchLines -1 ; No script sleep, for more consistent timing behavior. Default behavior is 10ms execution then 10ms sleep
ListLines Off ; Increase performance by a few percent by not logging the lines of code that are executed
; Modifiers: [+ = Shift] [^ = Ctrl] [# = Win] [! = Alt] [* = Ignores unspecified modifiers] [~ = Doesn't block normal function] [$ = Forces hook, preventing hotkey self-trigger] More info here: https://www.autohotkey.com/docs/KeyList.htm
; Time values are in ms(MilliSeconds), 1/1000 of a second. 1000/delay = activationsPerSecond. I.e: 50ms delay -> 1000/50 = 20 per sec
; Time values are typically rounded up to a multiple of 10ms by the Windows time-keeping system. So there's no point to Timer/Sleep values that aren't multiples of 10. Higher precision may be achieved via Loop+DllCall, but is rarely relevant and certainly doesn't matter for this script
global slideDelay := 110 ; 110 achieves max movement speed with Balla (with extremely high attack speed). Use higher delay for slower attack speeds. Too low a delay can yield normal, non-slide attacks. To find out the sweetspot for your weapon, see if you execute any non-slide attacks, then increase delay incrementally until they stop
global crouchDelay := 250 ; 125ms between notes for Octavia tracks. I recommend putting a purple "Melody" note in every column for best sync
global abilitySpam := [false, false, false, false]
global abilityDelay := [500, 9000, 15000, 2000] ; Delay between activations for ability 1, 2, 3 and 4, respectively
global delayToModify := 4
global incrementIncrement := 50
global abilityDelayIncrement := 500
global abilityKeys := ["1", "2", "3", "4"] ; Keybind for each respective ability key. Change these to be the same as your ingame keybinds
#IfWinActive ahk_exe Warframe.x64.exe ; Only trigger hotkeys when Warframe is the active window
Hotkey, IfWinActive, ahk_exe Warframe.x64.exe ; Same, but for dynamically created hotkeys
global BoundFuncCache := {} ; A collection of bound functions for use in Timer stopping. Func(f).Bind(k) seems to create an object and return a reference to it, without caching the result, so manual caching is required to reference the same object
for i, key in abilityKeys {
toggleAbilityBF := Func("ToggleAbilitySpam").Bind(key)
Hotkey, % "$*<^>!" . key, % toggleAbilityBF ; AltGr+AbilityKey to toggle spam for that ability
selectBF := Func("SelectAbilityToModify").Bind(key)
Hotkey, % "#" . key, % selectBF ; Win+AbilityKey to select ability for modification of delay
BoundFuncCache[key] := Func("CastAbility").Bind(key)
}
SelectAbilityToModify(key){
delayToModify := IndexOf(key, abilityKeys)
DisplayAbilityDelay()
}
ToggleAbilitySpam(key){
i := IndexOf(key, abilityKeys)
abilitySpam[i] := !abilitySpam[i]
if (abilitySpam[i]){
CenteredToolTip("Ability " . i . " Spam On (Delay: " . abilityDelay[i] . "ms)") ; Periods concatenate strings
Send, % "{Blind}{" . key . "}"
tmp := BoundFuncCache[key] ; SetTimer doesn't support function references in expression mode, requiring a temporary variable and regular variable dereferencing
SetTimer, %tmp%, % abilityDelay[i]
} else {
CenteredToolTip("Ability " . i . " Spam Off")
tmp := BoundFuncCache[key]
SetTimer, %tmp%, Off
}
}
CastAbility(key){
if (WinActive("ahk_exe Warframe.x64.exe"))
Send, % "{Blind}{" . key . "}" ; {Blind} fixes issues with a combined Sprint+Roll key; Without {Blind}, holding down Shift during a Send command will send {Shift Up}, then the key and then {Shift Down}, causing unintentional rolling
}
global spam := false
spamHotkeys := ["LButton", "XButton2", "MButton"] ; Hold one of these to spam that key. Just add a key to the array to automatically make it a new spam hotkey (same goes for removing)
for i, key in spamHotkeys { ; Creates hotkeys for each key in the array above
BoundFuncCache[key] := Func("SendBlind").Bind(key)
Hotkey, IfWinActive ; Make StopSpam work outside of Warframe to avoid button getting stuck when clicking on a different window
stopSpamBF := Func("StopSpam").Bind(BoundFuncCache[key])
Hotkey, % "~*" . key . " Up", % stopSpamBF
Hotkey, IfWinActive, ahk_exe Warframe.x64.exe ; Re-enable condition
spamBF := Func("Spam").Bind(key) ; Bind(BoundFunc) the Key to the Spam function to use it as input for the Hotkey Command
Hotkey, % "$*" . key, % spamBF ; $ to ensure Hotkeys can't trigger themselves
}
Spam(key){
Send, % "{Blind}{" . key . " Down}" ; Required because ~ can't be used with KeyWait for blocking Auto-Repeat
if (spam){
tmp := BoundFuncCache[key] ; SetTimer doesn't support function references in expression mode, requiring a temporary variable and regular variable dereferencing
SetTimer, %tmp%, 50 ; Delay between activations in ms. 50ms = 20 times per second. Should be good for most use-cases
KeyWait, % key
}
}
StopSpam(boundFunc){
SetTimer, %boundFunc%, Off
}
SendBlind(key){ ; Function-wrapper for the Send Command
Send, % "{Blind}{" . key . "}"
}
^L:: ; Ctrl+L toggles Spam On/Off
spam := !spam
if (spam)
CenteredToolTip("Spam On")
else {
CenteredToolTip("Spam Off")
for i, func in BoundFuncCache
SetTimer, %func%, Off
}
return
MeleeAttack(){
Send, {Blind}{XButton2} ; Melee key. Replace with the key you use
}
*!Q:: ; Alt+Q to throw Contagion. Most of the delays(Sleep) need to be longer than 2 frames, which at 60FPS is 2/60 = 33.33ms, i.e: 40 delay. Increase delay to 70 if you can't keep FPS above 60
Send, {Blind}{Space}
Sleep, 40 ; Too short delay = throw fizzles (no throw)
Send, {Blind}{Space}
Sleep, 40 ; Only needed when melee isn't the active weapon. Too short delay = throw fizzles. Works with <2 frames delay, but not 100% reliably
Send, {Blind}{RButton Down}
Sleep, 40 ; Too short delay = aim-glide gets stuck
MeleeAttack()
Sleep, 40 ; ONLY NEEDED AS CLIENT. Too short delay = throw fizzles
Send, {Blind}{RButton Up}
return
; Shift+PageUp/Down to adjust delay between Slide-Attacks. Warframe uses a queue system for melee inputs; too low a delay can yield normal, non-slide attacks
+PgUp::
slideDelay := slideDelay+10
CenteredToolTip("SlideDelay: " . slideDelay . "ms")
return
+PgDn::
if (slideDelay > 10) ; Avoid 0 and negative values
slideDelay := slideDelay-10
CenteredToolTip("SlideDelay: " . slideDelay . "ms")
return
*+LAlt:: ; Triggers with Shift+LeftAlt, then keeps attacking while LeftAlt is held down
SlideAttack()
SetTimer, SlideAttack, %slideDelay%
KeyWait, LAlt
return
~*LAlt Up::
SetTimer, SlideAttack, Off
return
SlideAttack(){
Send, {LCtrl Down} ; Crouch key. {Blind} is not used because it causes Alt to sometimes get stuck logically down
MeleeAttack()
Send, {LCtrl Up} ; No delay needed between Crouch down and up. Has the bonus of removing the "shake" of spam-crouching
}
; Alt+PageUp/Down to adjust delay between crouches
!PgUp::
crouchDelay := crouchDelay+10
CenteredToolTip("CrouchDelay: " . crouchDelay . "ms")
return
!PgDn::
if (crouchDelay > 10)
crouchDelay := crouchDelay-10
CenteredToolTip("CrouchDelay: " . crouchDelay . "ms")
return
*!C:: ; Alt+C to spam crouch
Send, {Blind}{LCtrl Down}
SetTimer, CrouchSpam, %crouchDelay%
KeyWait, C
return
~*C Up::
Send, {Blind}{LCtrl Up}
SetTimer, CrouchSpam, Off
return
CrouchSpam(){
Send, {Blind}{LCtrl Up}
Sleep, 1
Send, {Blind}{LCtrl Down}
}
DisplayAbilityDelay(){
CenteredToolTip("Ability " . delayToModify . " Delay: " . abilityDelay[delayToModify] . "ms")
}
; AltGr+PageUp/Down to adjust delay between ability activations
<^>!PgUp::
abilityDelay[delayToModify] := abilityDelay[delayToModify]+abilityDelayIncrement
DisplayAbilityDelay()
if (abilitySpam[delayToModify]){
tmp := BoundFuncCache[abilityKeys[delayToModify]]
SetTimer, %tmp%, % abilityDelay[delayToModify] ; Update running timers
}
return
<^>!PgDn::
if (abilityDelay[delayToModify] > abilityDelayIncrement) ; Avoid 0 and negative values
abilityDelay[delayToModify] := abilityDelay[delayToModify]-abilityDelayIncrement
DisplayAbilityDelay()
if (abilitySpam[delayToModify]){
tmp := BoundFuncCache[abilityKeys[delayToModify]]
SetTimer, %tmp%, % abilityDelay[delayToModify]
}
return
; Win+PageUp/Down to adjust increment for adjusting delay between ability activations
#PgUp::
abilityDelayIncrement := abilityDelayIncrement+incrementIncrement
CenteredToolTip("Ability Delay Increment: " . abilityDelayIncrement . "ms")
return
#PgDn::
if (abilityDelayIncrement > incrementIncrement)
abilityDelayIncrement := abilityDelayIncrement-incrementIncrement
CenteredToolTip("Ability Delay Increment: " . abilityDelayIncrement . "ms")
return
!X:: ; Alt+X to skip transmission dialogue (AKA "Nightwave skip"). Made for 16:9 aspect ratios, change the MouseMove if your screen isn't 16:9 so the cursor lands on the Nightwave banner
Send, {Esc}
Sleep, 444 ; Wait for transition animation
MouseMove, % A_ScreenWidth*0.85, % A_ScreenHeight*0.87, 0 ; Use location relative to screen resolution so that it works with any 16:9 resolution. NOTE: Mouse Movement doesn't work in fullscreen mode
Sleep, 1 ; Needs delay between movement and Click
Click, down
Sleep, 1 ; Needs separate up and down events, otherwise the UI doesn't register the click
Click, up ; Essentially: Send, {Blind}{LButton Up}
Sleep, 444 ; Wait for transition animation
Send, {Esc}
return
CapsLock::5 ; Remaps CapsLock to 5. I'm using CapsLock as Transference key
#IfWinActive ; The next hotkeys work outside of Warframe too
^P::Suspend ; Ctrl+P toggles hotkeys On/Off
*#P::Pause ; Win+P toggles execution Pause
<^>!R::Reload ; AltGr+R reloads script
CenteredToolTip(text, duration = 999){ ; Duration in ms (MilliSeconds). Default value can be overridden
ToolTip, %text%, A_ScreenWidth/2, A_ScreenHeight/2
SetTimer, RemoveToolTip, -%duration% ; Negative to only trigger once
}
RemoveToolTip(){
ToolTip
}
IndexOf(item, array){ ; Returns the index of the first item matching the input item
for i in array
if (array[i] == item)
return i
}
@KarlRamstedt
Copy link
Author

@claymen1 Try increasing the delays. You look like you have a low framerate.

@claymen1
Copy link

@KarlRamstedt fixed it alread long ago, and the fps drop i get only from the recording tho ^^

@PsykhoEX
Copy link

I cannot change the delay for hability, only the 4

Alt + PGUP / PGDN

@KarlRamstedt
Copy link
Author

@PsykhoEX have you tried Win(WindowsKey)+1 to select ability one?

@TheRealDjElite
Copy link

@TheRealDjElite Changing slide attack trigger is easy enough for you to do on your own, so not gonna bother with the details on that one :P

@KarlRamstedt Previously, I had figured out how to assign the XButton2 to be the Slide Attack Trigger. However, I completely wiped my desktop and lost my AHK file for it. I've been trying to figure out what to edit but it's not working.

It's in this part of the macro, right?

*+LAlt:: ; Triggers with Shift+LeftAlt, then keeps attacking while LeftAlt is held down
	SlideAttack()
	SetTimer, SlideAttack, %slideDelay%
	KeyWait, LAlt
return
~*LAlt Up::
	SetTimer, SlideAttack, Off
return
SlideAttack(){
	Send, {LCtrl Down} ; Crouch key. {Blind} is not used because it causes Alt to sometimes get stuck logically down
	MeleeAttack()
	Send, {LCtrl Up} ; No delay needed between Crouch down and up. Has the bonus of removing the "shake" of spam-crouching
}

Could you help me out, please? I'd really appreciate it.

@KarlRamstedt
Copy link
Author

@TheRealDjElite yeah, it's in that part of the script. If you just want it to be XButton2 then you just replace *+LAlt:: with *XButton2::

@TheRealDjElite
Copy link

@KarlRamstedt Does this look right?

*XButton2:: ; Triggers with Shift+LeftAlt, then keeps attacking while LeftAlt is held down
	SlideAttack()
	SetTimer, SlideAttack, %slideDelay%
	KeyWait, LAlt
return
~*LAlt Up::
	SetTimer, SlideAttack, Off
return
SlideAttack(){
	Send, {LCtrl Down} ; Crouch key. {Blind} is not used because it causes Alt to sometimes get stuck logically down
	MeleeAttack()
	Send, {LCtrl Up} ; No delay needed between Crouch down and up. Has the bonus of removing the "shake" of spam-crouching
}

@KarlRamstedt
Copy link
Author

@TheRealDjElite you gotta change the "Up" button as well. So make that ~*XButton2 Up::

@TheRealDjElite
Copy link

TheRealDjElite commented Apr 4, 2023 via email

@KarlRamstedt
Copy link
Author

Well, that doesn't make sense to me. Pretty sure it should work. Try with a different button to verify? Could be an issue with your mouse.

@Turboameeba
Copy link

Turboameeba commented Apr 18, 2023

The Exodia Contagion heavy throw you have to first have your setting for for Switch Weapon enabled.. When you hold down that button, it turns on melee mode.. which means you can press aim button without actually taking a gun in your hand. Then you have to jump in the air.. Do aim glide and press crouch button to do a Jump aim glide SLIDE... Slide in the air.. And when you then press your heavy attack button, your Cntagion will actually throw a heavy Contagion, which will get that 2x crit chance buff etc, like you can see from this video. Sorry for bad quality. It was recoded with some website screen capture
Exodia Contagion Heavy.webm

@KarlRamstedt
Copy link
Author

@Turboameeba That sounds like what I wrote, no?
The code I gave you before should do that, but you may ofc have to tweak timings and/or possibly add additional delays between the crouches and other buttons.
You'll have to do some trial and error testing :)

@Turboameeba
Copy link

Turboameeba commented Apr 20, 2023

So far the code I tested was just a normal jump throw. Not the heavy attack throw in the video. The haavy attack throw produces orange and red crits every single time. You cannot even do the heavy throw without first turning the melee mode on by holding the button down so you see the small blink red light effect. Just having the melee in your hand is not enough to do it. I dont use macros tho, was just curious in past.. The heavy throw is not also possible without the air slide during aim glide.
Blink n Slide.webm

@headasstommy
Copy link

hey i cant get the exodia one to work without for some reason dashing

2023-05-30.07-01-09.mp4

@KarlRamstedt
Copy link
Author

KarlRamstedt commented Jun 2, 2023

@headasstommy could be a number of things causing this. Make sure all the keybinds ingame correspond to the buttons pressed by the script. If that doesn't fix it, try increasing the delays.
Also, looks like there's crouching happening in the script. Are you trying to do the heavy contagion throw?

@whatislifeZA
Copy link

been having this problem with the exodia contagion stand alone macro, only does ground slams no matter what delay i use, any idea what the problem is?
https://user-images.githubusercontent.com/131336927/267317079-1286647b-daf2-43da-add5-478745499e8c.mp4

@whatislifeZA
Copy link

nvm im stupid, forgot that xbutton2 was bound to heavy attack :/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment