Call a weaponswap toggle once

Ask gaming related questions
Elkad
Posts: 11
Joined: 27 Mar 2016, 20:11

Call a weaponswap toggle once

26 Dec 2023, 09:12

Attacks on keys 1234wryu
Weaponswap toggle = T

I want 1234 to all be made with weapon0
wryu to be made with weapon1
So T needs to precede the key only when changing from number attacks to letter attacks.
Example wrote: current Weapon=0
I press 3 4 3 w r 4 r r 2
Game should receive
3 4 3 Tw r T4 Tr r T2

This doesn't work, it both inserts T before every number, and never does for letters
Not sure what I'm doing wrong, but it's not flipping weapon:= ?
And is there a better way to handle all the keys I want than an individual section for each?

Code: Select all

#MaxThreadsPerHotkey 1
#UseHook 1
Global weapon:=""


W::
{
global weapon
	{
	If weapon:="0" 
	Send "{T}"
	weapon:="1"
	}
Send "{W}"
Return
}

R::
{
global weapon
	{
	If weapon:="0" 
	Send "{T}"
	weapon:="1"
	}
Send "{R}"
Return
}

4::
{
global weapon
	{
	If weapon:="1" 
	Send "{T}"
	weapon:="0"	
	}
Send "{4}"
Return
}

3::
{
global weapon
	{
	If weapon:="1" 
	Send "{T}"
	weapon:="0"	
	}
Send "{3}"
Return
}
niCode
Posts: 316
Joined: 17 Oct 2022, 22:09

Re: Call a weaponswap toggle once

26 Dec 2023, 15:18

There are some things here that are incorrect. For example:

Code: Select all

{
If weapon:="0" 
Send "{T}"
weapon:="1"
}
The opening brace isn't in the right place. When an if statement doesn't have braces, it will only consider running the next line. So in this example, the if statement would only send t if weapon was 0 but it would always set weapon to 1. The opening brace needs to be after the if line, whether at the end of the line or on the next line:

Code: Select all

if (expression) {
    ; code
}

; or

if (expression)
{
    ; code
}
Also, your if statements aren't checking the expression correctly. You used := which is used for assignment so you keep checking if they have a value and assigning them that value in one fell swoop. = is used to check equality.

I would also avoid using capital letters. AHK interprets them as the shifted variant. e.g. R would be seen as Shift+r. This could cause unexpected behavior if not intended.

As for getting you the script you want, because you didn't provide code for the Weaponswap toggle = T, I'm going to assume this is a hotkey built into the game, and not a toggle variable you coded but forgot to include.

How close does this get you to where you wanted?

Code: Select all

*w::
*r::
*y::
*u::
*1::
*2::
*3::
*4::Attack()


Attack()
{
    static savedState := false
    key := SubStr(A_ThisHotkey, -1)
    state := IsAlpha(key) ? 'letter' : 'number'

    if state != savedState {
        SendEvent('t')
        savedState := state
    }
    SendEvent(key)
}
Elkad
Posts: 11
Joined: 27 Mar 2016, 20:11

Re: Call a weaponswap toggle once

26 Dec 2023, 22:48

Thanks.
As you suspected, T is handled ingame.

First, I fixed mine thanks to your guidance. It works, but it's not reliable. T is often late, giving a "wrong weapon" error from the game.

Yours is 100% reliable, tested a couple hundred times.

However, my example didn't fit my actual realworld usage.
You used IsAlpha to sort, and that won't always work for me. I have numbers and letters and F-keys intermixed with no respect for weapontype.

So I need a way to define keys as weapon0 or weapon1 manually.

I also planned to integrate it with existing macros.
Currently I hit t to swap weapons (handled ingame) and then hit r, which AHK handles as

Code: Select all

$r:: Send "{h}{a}{r}{l}{9}{0}" 
Note that r is not first.

I'd like to integrate t into the function as needed.

Another example

Code: Select all

F11:: ; not bound ingame, it's just bound to mouse6
Send "{g}{7}{6}{F3}{F4}"
The T swap always comes first, if needed.
niCode
Posts: 316
Joined: 17 Oct 2022, 22:09

Re: Call a weaponswap toggle once

27 Dec 2023, 06:18

So I changed it (at the bottom) so it should be easy to add the hotkey under the section where you want your weapon0 and weapon1 keys to go. Obviously change the hotkeys/swap the 0 and 1 if they are backwards for what you consider weapon0 and weapon1.

You stated:
I'd like to integrate t into the function as needed.
Not 100% sure what you mean here. I took it to mean: when you press t, it will inverse the state; so that you can manually press t and the function still tracks the correct state. I've added that. Let me know if I interpreted that incorrectly.


I'm also not sure what you mean by the rest. I'm going to use the following example you gave and explain what I think you mean.

Code: Select all

$r:: Send "{h}{a}{r}{l}{9}{0}" 
I think what you want, is when you press r, if any (or all) of the keys being sent are hotkeys you set up, you also want those hotkeys to fire the code associated with them. If so, this can easily be achieve with the #InputLevel directive. I'll give a simple example to show how it works:

Code: Select all

#InputLevel 1       ; set input level to 1, all hotkeys underneath this directive have the ability to trigger other hotkeys with a smaller input level
*F1::Send('{F2}')

#InputLevel 0       ; set input level back to default value so hotkeys underneath don't accidentally fire other hotkeys
*F2::MsgBox('This is a message')

Anyway, here is the updated code so far.

Code: Select all

; all weapon0 keys here
*w::
*r::
*y::
*F2::
*u::Attack(0)

; all weapon1 keys here
*1::
*2::
*3::
*F1::
*4::Attack(1)

~*t::Attack('toggle')   ; ~ prefix allows 

Attack(weapon)
{
    static weaponState := false

    if weapon = 'toggle' {
        weaponState := !weaponState
        return
    }

    if weaponState != weapon {
        SendEvent('t')
        weaponState := weapon
    }
    SendEvent('{' SubStr(A_ThisHotkey, 2) '}')
}
Elkad
Posts: 11
Joined: 27 Mar 2016, 20:11

Re: Call a weaponswap toggle once

27 Dec 2023, 15:53

niCode wrote:
27 Dec 2023, 06:18
So I changed it (at the bottom) so it should be easy to add the hotkey under the section where you want your weapon0 and weapon1 keys to go. Obviously change the hotkeys/swap the 0 and 1 if they are backwards for what you consider weapon0 and weapon1.
Perfect
Not 100% sure what you mean here. I took it to mean: when you press t, it will inverse the state; so that you can manually press t and the function still tracks the correct state. I've added that. Let me know if I interpreted that incorrectly.
Good thought, but actually, I specifically don't want that. If I get out of of phase and am trying to cast a spell holding my axe instead of my wand, I want T to still operate independently (ignored by AHK) so I can fix it.
That's pretty likely to happen.
I'm also not sure what you mean by the rest. I'm going to use the following example you gave and explain what I think you mean.

Code: Select all

$r:: Send "{h}{a}{r}{l}{9}{0}" 
I think what you want, is when you press r, if any (or all) of the keys being sent are hotkeys you set up, you also want those hotkeys to fire the code associated with them. If so, this can easily be achieve with the #InputLevel directive. I'll give a simple example to show how it works:
I just wanted the rest of the keys sent to the game, they aren't calling another function.
Of course they may need to call another function to do that...

t (if needed by current incorrect weapon state) just needs to go in front of

Code: Select all

$r:: Send "{h}{a}{r}{l}{9}{0}" ;  
2 cases.
Wrong weapon equipped.
I hit r..
AHK sends "tharl90" to the game

correct weapon equipped
I hit r
AHK sends "harl90" to the game

So I need a way to define the followup key(s), and then recall & send them after checking and sending (or not sending) "t"

Added some comments

Code: Select all

; all weapon0 keys here
*w:: ; Full list of keys to game are "h a w p"
*r:: ; Full list of keys to game are "h a r l 9 0"
*y:: ; 
*F2::
*u::Attack(0)

; all weapon1 keys here
*1::
*2::
*3:: 
*F11:: ; Full list is g 7 6 F3 F4"
*4::Attack(1)

;~*t::Attack('toggle')   ; don't need this

Attack(weapon)
{
    static weaponState := false

    if weapon = 'toggle' {
        weaponState := !weaponState
        return
    }

    if weaponState != weapon {
        SendEvent('t')
        weaponState := weapon
    }
;    SendEvent('{' SubStr(A_ThisHotkey, 2) '}') ;  The key I pressed can't come immediately after "t" - it has to recall the specific stored list for that key and send them in order, which may not even include the key pressed.
}
Top
niCode
Posts: 316
Joined: 17 Oct 2022, 22:09

Re: Call a weaponswap toggle once

28 Dec 2023, 05:22

Okay, so I removed the t toggle portion. Hopefully I'm starting to understand your intentions here. In this new version, instead of putting the keys you want to send into a Send function (e.g. Send('harl90')), put them into the Weapon0 or Weapon1 function as you'll see in the code. If you don't pass any keys, then it'll act like before where it sends the key that pressed it.

Code: Select all

; all weapon0 keys here
*w::Weapon0('hawp')
*r::Weapon0('harl90')


; all weapon1 keys here
*1::
*2::
*3::
*4::Weapon1()
*F11::Weapon1('g76{F3}{F4}')

Weapon0(keys := '{' SubStr(A_ThisHotkey, 2) '}') => Attack(keys, 0)
Weapon1(keys := '{' SubStr(A_ThisHotkey, 2) '}') => Attack(keys, 1)

Attack(keys, weapon)
{
    static weaponState := false

    if weaponState != weapon {
        SendEvent('t')
        weaponState := weapon
    }
    SendEvent(keys)
}
Last edited by niCode on 28 Dec 2023, 14:31, edited 1 time in total.
Elkad
Posts: 11
Joined: 27 Mar 2016, 20:11

Re: Call a weaponswap toggle once

28 Dec 2023, 10:17

Stuck at work right now so can't test (other than notepad), but that looks perfect.
Getting my intentions across feels like the hardest part - from my end anyway.

Thanks


Question about A_ThisHotkey

If some other portion of my script is using A_ThisHotkey, and I trigger both at once (or very close together on a human scale, say <100ms), do both scripts lose their minds?
I don't have any delay or sleep set anywhere, and MaxThreadsPerHotkey=1

Specifically this - which I've been using the prior version of for ~15 years on v1, and @andymbody just helped me move to v2 here

When I press F12, it sends only the F3-F8 keys I have previously selected. If I tap one of those keys, it's added/removed from subsequent F12 presses. And F8 only fires every other time in any case.

I assume F3 and F4 being in both will also be a problem, I'll have to restructure that.

Code: Select all

s_F3:=s_F4:=s_F5:=s_F6:=s_F7:=s_F8:=""	; global keyword not required in main section, will be global automatically
skipF8 := false							; separated because this is used as a boolean

F12::
{
	global	; "Assume-global" mode - only required because of skipF8 modification
	Send "{blind}{" s_F3 "}{" s_F4 "}{" s_F5 "}{" s_F6 "}{" s_F7 "}"	; v2 requires quotes and no %
	if (!skipF8)
	Send "{blind}{" s_F8 "}"
	skipF8 :=!skipF8
	return
}

#UseHook 1
F8::
F7::
F6::
F5::
F4::
F3::
{
	global 		; can use "Assume-global" mode here to cover all vars
	s_%A_ThisHotkey%  ?  s_%A_ThisHotkey% := ""  :  s_%A_ThisHotkey% := SubStr(A_ThisHotkey,2)
	return
}
}
niCode
Posts: 316
Joined: 17 Oct 2022, 22:09

Re: Call a weaponswap toggle once

28 Dec 2023, 14:46

Elkad wrote:
28 Dec 2023, 10:17
Question about A_ThisHotkey
If some other portion of my script is using A_ThisHotkey, and I trigger both at once (or very close together on a human scale, say <100ms), do both scripts lose their minds?
None of the code you provided should take close to 100ms. If you pressed them at the same time, it's possible they could conflict.
Elkad wrote:
28 Dec 2023, 10:17
I assume F3 and F4 being in both will also be a problem, I'll have to restructure that.
You can easily have hotkeys only trigger in specific applications. For example:

Code: Select all

#HotIf WinActive('ahk_exe Code.exe')    ; hotkeys here only work in VSCode
F1::MsgBox('VSCode is active')

#HotIf WinActive('ahk_exe vivaldi.exe') ; hotkeys here only work in my browser
F1::MsgBox('vivaldi browser is active')

#HotIf  ; turns off context-sensitivity so any hotkeys underneath aren't affected by the previous #HotIf expression (e.g. my browser being active)
F1 in the example will show a different message box in my coding editor and my browser, but will perform like a normal F1 key anywhere else.

And with your code, just replace GameExeName with the correct exe name for your game.

Code: Select all

#HotIf WinActive('ahk_exe GameExeName.exe)
; all weapon0 keys here
*w::Weapon0('hawp')
*r::Weapon0('harl90')


; all weapon1 keys here
*1::
*2::
*3::
*4::Weapon1()
*F11::Weapon1('g76{F3}{F4}')
#HotIf
niCode
Posts: 316
Joined: 17 Oct 2022, 22:09

Re: Call a weaponswap toggle once

28 Dec 2023, 15:32

Another thing I just remembered about is ThisHotkey. It's very similar but only works in the scope of a hotkey press. So in my example, it's worthless because all the code it executes leaves the hotkey scope into the functions (unless I added more parameters to accept the hotkey pressed). But if you feel there could be a confliction, you could use it in your other code by just changing A_ThisHotkey to ThisHotkey.

Code: Select all

s_F3:=s_F4:=s_F5:=s_F6:=s_F7:=s_F8:=""	; global keyword not required in main section, will be global automatically
skipF8 := false							; separated because this is used as a boolean

F12::
{
	global	; "Assume-global" mode - only required because of skipF8 modification
	Send "{blind}{" s_F3 "}{" s_F4 "}{" s_F5 "}{" s_F6 "}{" s_F7 "}"	; v2 requires quotes and no %
	if (!skipF8)
	Send "{blind}{" s_F8 "}"
	skipF8 :=!skipF8
	return
}

#UseHook 1
F8::
F7::
F6::
F5::
F4::
F3::
{
	global 		; can use "Assume-global" mode here to cover all vars
	s_%ThisHotkey%  ?  s_%ThisHotkey% := ""  :  s_%ThisHotkey% := SubStr(ThisHotkey,2)
	return
}
Elkad
Posts: 11
Joined: 27 Mar 2016, 20:11

Re: Call a weaponswap toggle once

28 Dec 2023, 22:31

First, the report.

It's working great!

Your followups.
niCode wrote:
28 Dec 2023, 14:46

You can easily have hotkeys only trigger in specific applications. For example:
Same keys in same application, so not useful in that respect.

And with your code, just replace GameExeName with the correct exe name for your game.

Code: Select all

#HotIf WinActive('ahk_exe GameExeName.exe)

Yeah, I've already got WinActive setup to pause the whole thing if it loses focus. Took me a bit to get the syntax change from v1 to v2 figured out for that.
I've also got every key that opens chat ingame setup to suspend, so I can chat normally ingame, and it unsuspends when I hit enter or esc to leave chatmode.
Plus a manual suspend key, and a reload key to set it all back to defaults.

Similar to not tracking a manual t, I removed the * from your examples. I don't want them to fire if I have a modifier held. I've carefully arranged stuff so I have no reason to ever hit a modifier in combat, and have non-combat stuff mapped with modifiers on re-used keys. Like I for Cure Disease and Alt+Ifor Inventory.




And currently-unnecessary feature creep is setting in now... Feel free to ignore. As a matter of fact, definitely ignore, I want to take a shot at it myself.
Spoiler

Return to “Gaming”

Who is online

Users browsing this forum: No registered users and 10 guests