Skip to content

Instantly share code, notes, and snippets.

@KarlRamstedt
Last active April 3, 2024 14:26
Show Gist options
  • Star 16 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • 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
}
@headasstommy
Copy link

@KarlRamstedt
Works good!
But I have seen people achieve faster but i dont know how..... hoping maybe you can help me

IDK.1.mp4

@GitHubAbuser123
Copy link

Hello again.
I have a quick question:
Do you think it would be possible to somehow make spam macros activate about a second after the initial button press?

For example:
I want to use the "E spam" macro for melee because I don't really want to get trigger finger or carpal tunnel, but sometimes (for example when typing) I want to be able to press the E button just once instead of sending someone fifteen E's in a message.

Would it be possible to make it so when I initially press the E button the macro sends just one E input but if i keep holding it down for another second or two THEN it starts spamming?

@GitHubAbuser123
Copy link

@KarlRamstedt Works good! But I have seen people achieve faster but i dont know how..... hoping maybe you can help me

IDK.1.mp4

People that do it faster change the delay values to lower ones and remove the delay after the melee attack in the code (requires high FPS).
Also some of them aim the gun right after contagion is fired to "animation cancel" and fall of the ground faster.

//just find the "contagion" section in the code and change values from 40 to something lower and lower. when it stops working you need to go a bit higher again untill you find your sweet spot

@KarlRamstedt
Copy link
Author

KarlRamstedt commented Apr 3, 2022

make spam macros activate about a second after the initial button press?
I want to use the "E spam" macro for melee[...] but sometimes I want to be able to press the E button just once instead of sending someone fifteen E's in a message.

Well, the first solution would be to tap the button really quickly to avoid having it re-trigger.

Another solution is toggling the spam functionality off when you wanna type. That's why it's a toggle in the first place :P

But if you really want to code for this niche use-case, here's a quick solution that might work:

Spam(key){
	Send, % "{Blind}{" . key . " Down}" ; Required because ~ can't be used with KeyWait for blocking Auto-Repeat
	if (spam){
		Sleep, 999
		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
	}
}

(basically just add a sleep delay before starting the timer)

@foxertin
Copy link

hello! im having a problem of the script not pressing the E key, and the spam key function seemingly doesn't work for any of the keys.... thoughts on what im doing wrong?

@KarlRamstedt
Copy link
Author

KarlRamstedt commented May 5, 2022

@foxertin not sure about the E-pressing, but note that you gotta press Ctrl+L to activate the spam. There should be a tooltip pop-up in the middle of the screen confirming with Spam On when you've pressed it.

@Turboameeba
Copy link

Could you help make a Contagion heavy throw macro even when you have guns equipped please? This current macro does just the normal throw as far as what I have tested

@KarlRamstedt
Copy link
Author

KarlRamstedt commented Oct 16, 2022

@Turboameeba I'm not too knowledgeable on Heavy throws, they're just Crouch+Heavy Attack instead of normal attack?
Try this quick edit(and change Heavy-attack key if needed ofc):

*!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}{LCtrl Down}
	Sleep, 40 ; Too short delay = aim-glide gets stuck
	Send, {Blind}{MButton Down} ; Heavy-attack key
	Sleep, 40 ; ONLY NEEDED AS CLIENT. Too short delay = throw fizzles
	Send, {Blind}{RButton Up}{LCtrl Up}
return

@TheRealDjElite
Copy link

@KarlRamstedt I appreciate your work!

Is it possible to bind the Slide-Attack to "XButton2" instead of Shift+LeftAlt (I changed my Melee default back to "e") and add a Bullet-Jump script to "XButton1"?

🙏🏽

@KarlRamstedt
Copy link
Author

KarlRamstedt commented Dec 29, 2022

@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

As for a Bullet-Jump hotkey, try this:

*!XButton1:: ; Mouse 4 to bullet-jump
	Send, {Blind}{Ctrl}{Space}
return

If the above doesn't work, try this:

*!XButton1:: ; Mouse 4 to bullet-jump
	Send, {Blind}{Ctrl Down}
	Send, {Blind}{Space}
	Send, {Blind}{Ctrl Up}
return

@TheRealDjElite
Copy link

TheRealDjElite commented Jan 5, 2023

@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

As for a Bullet-Jump hotkey, try this:

*!XButton1:: ; Mouse 4 to bullet-jump
	Send, {Blind}{Ctrl}{Space}
return

If the above doesn't work, try this:

*!XButton1:: ; Mouse 4 to bullet-jump
	Send, {Blind}{Ctrl Down}
	Send, {Blind}{Space}
	Send, {Blind}{Ctrl Up}
return

@KarlRamstedt:

Thanks for the Bullet-Jump. Both actually work after I removed the "*!" from the *!XButton1 to get XButton1.

I got to bind the Slide-Attack to "Shift+Xbutton2" instead of Shift+LeftAlt.

Thanks for teaching me how to fish. :-)

@TheRealDjElite
Copy link

@KarlRamstedt:

Any advice for when SPAM attack buttons get stuck? It seems to go crazy even if I click outside of WF.

@KarlRamstedt
Copy link
Author

@TheRealDjElite just tap the button. Should work even outside of Warframe.

@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