Trigger action by tapping different keys in rapid succession

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
london1888
Posts: 4
Joined: 22 Sep 2018, 09:40

Trigger action by tapping different keys in rapid succession

22 Sep 2018, 09:54

I'm trying set up a script that does the following:

Double tap 1::action 1
Quickly tap 1 and 2::action 2
Quickly tap 1 and 3::action 3
Quickly tap 1 and 4::action 4
...

Is there a way to do this? I found RapidHotkey() but it seems to only work when tapping the same key multiple times.
buttshark
Posts: 62
Joined: 22 Apr 2017, 20:57

Re: Trigger action by tapping different keys in rapid succession

23 Sep 2018, 03:20

I would do this by setting '1' as an individual hotkey, and then look for another key press in the near future via getkeystate().

Code: Select all

~1::
t := A_TickCount + 200 ;adjust number to alter how quickly double tapping should occur
while(getkeystate("1", "p"))
	{}
while(A_TickCount < t)
{
	Loop 10
	{
		key := A_Index - 1
		if(getkeystate(key, "p"))
			goto, lbl%key%
	}
}
return

lbl0:
	MsgBox you tapped 1 then 0
return
lbl1:
	MsgBox you tapped 1 then 1
return
lbl2:
	MsgBox you tapped 1 then 2
return
lbl3:
	MsgBox you tapped 1 then 3
return
lbl4:
	MsgBox you tapped 1 then 4
return
lbl5:
	MsgBox you tapped 1 then 5
return
lbl6:
	MsgBox you tapped 1 then 6
return
lbl7:
	MsgBox you tapped 1 then 7
return
lbl8:
	MsgBox you tapped 1 then 8
return
lbl9:
	MsgBox you tapped 1 then 9
return
I have no idea what I'm doing.
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: Trigger action by tapping different keys in rapid succession

23 Sep 2018, 04:47

different solution:

Code: Select all

1::
	ToolTip Press next key now!, A_ScreenWidth/2, A_ScreenHeight/2

	SetTimer TriggerTimeout, -200
	waitingForNextKey := true
return

TriggerTimeout:
	ToolTip

	waitingForNextKey := false
return

#If waitingForNextKey
1::
	MsgBox 11
	waitingForNextKey := false
return

2::
	MsgBox 12
	waitingForNextKey := false
return

3::
	MsgBox 13
	waitingForNextKey := false
return

4::
	MsgBox 14
	waitingForNextKey := false
return
london1888
Posts: 4
Joined: 22 Sep 2018, 09:40

Re: Trigger action by tapping different keys in rapid succession

23 Sep 2018, 10:03

Thank you both for your reply. However, I apologize for not stating my needs more clearly.
I am actually trying to achieve 16 hotkeys through 2-key combinations of 1, 2, 3, 4. In other words, I need 11, 12, 13, 14, 21, 22, 23, 24, ..., 41, 42, 43, 44.
I tried modifying both of your codes but I couldn't get either to work how I want.

First I tried to modify buttshark's code into a function.

Code: Select all

*$1::Shortcut("{F1}1", "{F1}2", "{F1}3", "{F1}4")
*$2::Shortcut("{F1}5", "{F1}6", "{F1}7", "{F1}8")
*$3::Shortcut("{F2}1", "{F2}2", "{F2}3", "{F2}4")
*$4::Shortcut("{F2}5", "{F2}6", "{F2}7", "{F2}8")

Shortcut(ks1, ks2, ks3, ks4, t = 200) {
	CurrentKey := RegExReplace(A_ThisHotKey,"[\*\~\$\#\+\!\^]")
	t := A_TickCount + t
	while(getkeystate(CurrentKey, "p"))
	{}
	while(A_TickCount < t)
	{
		Loop 4
		{
			key := A_Index
			if(getkeystate(key, "p"))
				goto, lbl%key%
		}
	}
	return
	
	lbl1:
		;Send %ks1%
		MsgBox %CurrentKey% %key% lbl1
	return
	lbl2:
		;Send %ks2%
		MsgBox %CurrentKey% %key% lbl2
	return
	lbl3:
		;Send %ks3%
		MsgBox %CurrentKey% %key% lbl3
	return
	lbl4:
		;Send %ks4%
		MsgBox %CurrentKey% %key% lbl4
	return
}
When running this script, only 11, 22, 33, 44 worked.

Next I tried to write out the script manually without using function.

Code: Select all

*$1::
t := A_TickCount + 200
while(getkeystate("1", "p"))
	{}
while(A_TickCount < t)
{
	Loop 4
	{
		key := A_Index
		if(getkeystate(key, "p"))
			goto, lbl1_%key%
	}
}
return

*$2::
t := A_TickCount + 200
while(getkeystate("2", "p"))
	{}
while(A_TickCount < t)
{
	Loop 4
	{
		key := A_Index
		if(getkeystate(key, "p"))
			goto, lbl2_%key%
	}
}
return


lbl1_1:
	MsgBox 11
return
lbl1_2:
	MsgBox 12
return
lbl1_3:
	MsgBox 13
return
lbl1_4:
	MsgBox 14
return
lbl2_1:
	MsgBox 21
return
lbl2_2:
	MsgBox 22
return
lbl2_3:
	MsgBox 23
return
lbl2_4:
	MsgBox 24
return
I only had 11 through 24, and like before 12 and 21 doesn't work. 13, 14, 23, 24 works but I suspect if I wrote in hotkeys for 3 and 4 they will stop working as well.

Then I tried modifying swagfag's code.

Code: Select all

*$1::
	SetTimer 1Timeout, -200
	1Next := true
return

*$2::
	SetTimer 2Timeout, -200
	2Next := true
return

1Timeout:
	1Next := false
return

2Timeout:
	2Next := false
return

#If 1Next
*$1::
	MsgBox 11
	1Next := false
return

*$2::
	MsgBox 12
	1Next := false
return

*$3::
	MsgBox 13
	1Next := false
return

*$4::
	MsgBox 14
	1Next := false
return

#If 2Next
*$1::
	MsgBox 21
	2Next := false
return

*$2::
	MsgBox 22
	2Next := false
return

*$3::
	MsgBox 23
	2Next := false
return

*$4::
	MsgBox 24
	2Next := false
return
This actually works. But I want to use those as shortcuts in a game so I tried to add this in the beginning.

Code: Select all

#IfWinActive ahk_exe MonsterHunterWorld.exe
And suddenly 11, 12, 21, 22 won't work anymore. Same as before 13, 14, 23, 24 still works but if I added the hotkeys for 3 and 4 then everything probably would stop working.

What baffles me is that, if I comment out the #IfWinActive, then the script works perfectly in game. When I replaced the MsgBox with actual game commands, they trigger the in-game actions flawlessly.
swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: Trigger action by tapping different keys in rapid succession

23 Sep 2018, 11:26

thats because #If directives overwrite each other

try this instead:

Code: Select all

; these directives need to exist somewhere in the script for Hotkey, If, CustomExpression to work
#If waitForNextKey && WinActive("ahk_exe MonsterHunterWorld.exe")
#If

Keymap := [["{F1}1", "{F1}2", "{F1}3", "{F1}4"]
		 , ["{F1}5", "{F1}6", "{F1}7", "{F1}8"]
		 , ["{F2}1", "{F2}2", "{F2}3", "{F2}4"]
		 , ["{F2}5", "{F2}6", "{F2}7", "{F2}8"]]

for key in Keymap
{
	; define keys that initiate the standby phase
	Hotkey IfWinActive, ahk_exe MonsterHunterWorld.exe
	Hotkey % "*" key, SetTimeout
	Hotkey If
	
	; define keys that send and end the standby phase
	Hotkey If, waitForNextKey && WinActive("ahk_exe MonsterHunterWorld.exe")
	Hotkey % "*" key, SendKey
	Hotkey If
}

SetTimeout:
	ToolTip Press next key now!, A_ScreenWidth/2, A_ScreenHeight/2 ; debug only

	SetTimer TriggerTimeout, -200 ; adjust timeout duration here
	firstKey := stripWildcard(A_ThisHotkey) ; this is to keep track of which key initiated the sending sequence
	waitForNextKey := true ; set the flag and enable the second key layer
Return

TriggerTimeout:
	ToolTip ; debug only
	waitForNextKey := false ; unset the flag and disable the second key layer
	firstKey := "" ; blank out just in case
Return

SendKey:
	Send % Keymap[firstKey, getSecondKey(A_ThisHotkey)] ; pick the send command from the 2d array
	waitForNextKey := false ; unset the flag
Return

stripWildcard(key) {
	return StrReplace(key, "*")
}

getSecondKey(key) {
	return stripWildcard(key)
}
london1888
Posts: 4
Joined: 22 Sep 2018, 09:40

Re: Trigger action by tapping different keys in rapid succession

23 Sep 2018, 11:45

swagfag wrote:thats because #If directives overwrite each other

try this instead:

Code: Select all

; these directives need to exist somewhere in the script for Hotkey, If, CustomExpression to work
#If waitForNextKey && WinActive("ahk_exe MonsterHunterWorld.exe")
#If

Keymap := [["{F1}1", "{F1}2", "{F1}3", "{F1}4"]
		 , ["{F1}5", "{F1}6", "{F1}7", "{F1}8"]
		 , ["{F2}1", "{F2}2", "{F2}3", "{F2}4"]
		 , ["{F2}5", "{F2}6", "{F2}7", "{F2}8"]]

for key in Keymap
{
	; define keys that initiate the standby phase
	Hotkey IfWinActive, ahk_exe MonsterHunterWorld.exe
	Hotkey % "*" key, SetTimeout
	Hotkey If
	
	; define keys that send and end the standby phase
	Hotkey If, waitForNextKey && WinActive("ahk_exe MonsterHunterWorld.exe")
	Hotkey % "*" key, SendKey
	Hotkey If
}

SetTimeout:
	ToolTip Press next key now!, A_ScreenWidth/2, A_ScreenHeight/2 ; debug only

	SetTimer TriggerTimeout, -200 ; adjust timeout duration here
	firstKey := stripWildcard(A_ThisHotkey) ; this is to keep track of which key initiated the sending sequence
	waitForNextKey := true ; set the flag and enable the second key layer
Return

TriggerTimeout:
	ToolTip ; debug only
	waitForNextKey := false ; unset the flag and disable the second key layer
	firstKey := "" ; blank out just in case
Return

SendKey:
	Send % Keymap[firstKey, getSecondKey(A_ThisHotkey)] ; pick the send command from the 2d array
	waitForNextKey := false ; unset the flag
Return

stripWildcard(key) {
	return StrReplace(key, "*")
}

getSecondKey(key) {
	return stripWildcard(key)
}
Thank you. I tried it but only the the tooltip shows up when I press a key combo. The actual command doesn't seem to trigger.
london1888
Posts: 4
Joined: 22 Sep 2018, 09:40

Re: Trigger action by tapping different keys in rapid succession

23 Sep 2018, 13:23

swagfag wrote:thats because #If directives overwrite each other

try this instead:

Code: Select all

; these directives need to exist somewhere in the script for Hotkey, If, CustomExpression to work
#If waitForNextKey && WinActive("ahk_exe MonsterHunterWorld.exe")
#If

Keymap := [["{F1}1", "{F1}2", "{F1}3", "{F1}4"]
		 , ["{F1}5", "{F1}6", "{F1}7", "{F1}8"]
		 , ["{F2}1", "{F2}2", "{F2}3", "{F2}4"]
		 , ["{F2}5", "{F2}6", "{F2}7", "{F2}8"]]

for key in Keymap
{
	; define keys that initiate the standby phase
	Hotkey IfWinActive, ahk_exe MonsterHunterWorld.exe
	Hotkey % "*" key, SetTimeout
	Hotkey If
	
	; define keys that send and end the standby phase
	Hotkey If, waitForNextKey && WinActive("ahk_exe MonsterHunterWorld.exe")
	Hotkey % "*" key, SendKey
	Hotkey If
}

SetTimeout:
	ToolTip Press next key now!, A_ScreenWidth/2, A_ScreenHeight/2 ; debug only

	SetTimer TriggerTimeout, -200 ; adjust timeout duration here
	firstKey := stripWildcard(A_ThisHotkey) ; this is to keep track of which key initiated the sending sequence
	waitForNextKey := true ; set the flag and enable the second key layer
Return

TriggerTimeout:
	ToolTip ; debug only
	waitForNextKey := false ; unset the flag and disable the second key layer
	firstKey := "" ; blank out just in case
Return

SendKey:
	Send % Keymap[firstKey, getSecondKey(A_ThisHotkey)] ; pick the send command from the 2d array
	waitForNextKey := false ; unset the flag
Return

stripWildcard(key) {
	return StrReplace(key, "*")
}

getSecondKey(key) {
	return stripWildcard(key)
}
I found the problem. Due to this

Code: Select all

; define keys that initiate the standby phase
	Hotkey IfWinActive, ahk_exe MonsterHunterWorld.exe
	Hotkey % "*" key, SetTimeout
	Hotkey If
as long as the window is active, any input will always be treated as the first key regardless of the value of waitForNextKey. I fixed it simply by swapping the position of the two Hotkey If segments so that it becomes

Code: Select all

for key in Keymap
{
	; define keys that send and end the standby phase
	Hotkey If, waitForNextKey && WinActive("ahk_exe MonsterHunterWorld.exe")
	Hotkey % "*" key, SendKey
	Hotkey If

	; define keys that initiate the standby phase
	Hotkey IfWinActive, ahk_exe MonsterHunterWorld.exe
	Hotkey % "*" key, SetTimeout
	Hotkey If
}
This way it checks if waitForNextKey is true, and if not, then it treats the input as the first key.

This now works perfectly. Thank you so much!

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: Descolada, lmstearn, roeleboele, ulysim and 383 guests