• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[vJASS] Arrow Key Detection System

This is my Arrow Key Detection system I have created as part of an arrow key movement system. I thought other people may have a use for it, so here it is!
It basically extends on Blizzard's arrow key down and up events, allowing you to query if a player is pressing an arrow key. A known bug is that it can't detect when a key is lifted while in any menu, but I can't see any way around this so it'll have to do :cry:

This system works around one function: function IsKeyDown takes integer key, player id returns boolean, and I have made constant variables to support it:

JASS:
constant integer ARROW_LEFT = 0
constant integer ARROW_RIGHT = 1
constant integer ARROW_DOWN = 2
constant integer ARROW_UP = 3

I don't believe there are any leaks as I use no local handles and my only Global variable is an array whose elements are only ever set once.
v1.0.1

This version actually works.


Anyway, the code:

JASS:
library KeyDetection initializer Init

globals
    private trigger array ArrowDetectors
    private boolean array KeyDown
    private constant integer MAX_NUMBER_OF_PLAYERS = 4
    
    constant integer ARROW_LEFT = 0
    constant integer ARROW_RIGHT = 1
    constant integer ARROW_DOWN = 2
    constant integer ARROW_UP = 3
endglobals

function IsKeyDown takes integer key, player id returns boolean
    return KeyDown[MAX_NUMBER_OF_PLAYERS*key+GetPlayerId(id)]
endfunction

private function GetKeyEventDown takes integer i returns playerevent
    if i == 0 then
        return EVENT_PLAYER_ARROW_LEFT_DOWN
    elseif i == 1 then
        return EVENT_PLAYER_ARROW_RIGHT_DOWN
    elseif i == 2 then
        return EVENT_PLAYER_ARROW_DOWN_DOWN
    elseif i == 3 then
        return EVENT_PLAYER_ARROW_UP_DOWN
    endif
    return null
endfunction

private function GetKeyEventUp takes integer i returns playerevent
    if i == 0 then
        return EVENT_PLAYER_ARROW_LEFT_UP
    elseif i == 1 then
        return EVENT_PLAYER_ARROW_RIGHT_UP
    elseif i == 2 then
        return EVENT_PLAYER_ARROW_DOWN_UP
    elseif i == 3 then
        return EVENT_PLAYER_ARROW_UP_UP
    endif
    return null
endfunction

private function LeftDown takes nothing returns nothing
    set KeyDown[MAX_NUMBER_OF_PLAYERS*0+GetPlayerId(GetTriggerPlayer())] = true
endfunction

private function LeftUp takes nothing returns nothing
    set KeyDown[MAX_NUMBER_OF_PLAYERS*0+GetPlayerId(GetTriggerPlayer())] = false
endfunction

private function RightDown takes nothing returns nothing
    set KeyDown[MAX_NUMBER_OF_PLAYERS*1+GetPlayerId(GetTriggerPlayer())] = true
endfunction

private function RightUp takes nothing returns nothing
    set KeyDown[MAX_NUMBER_OF_PLAYERS*1+GetPlayerId(GetTriggerPlayer())] = false
endfunction

private function DownDown takes nothing returns nothing
    set KeyDown[MAX_NUMBER_OF_PLAYERS*2+GetPlayerId(GetTriggerPlayer())] = true
endfunction

private function DownUp takes nothing returns nothing
    set KeyDown[MAX_NUMBER_OF_PLAYERS*2+GetPlayerId(GetTriggerPlayer())] = false
endfunction

private function UpDown takes nothing returns nothing
    set KeyDown[MAX_NUMBER_OF_PLAYERS*3+GetPlayerId(GetTriggerPlayer())] = true
endfunction

private function UpUp takes nothing returns nothing
    set KeyDown[MAX_NUMBER_OF_PLAYERS*3+GetPlayerId(GetTriggerPlayer())] = false
endfunction

private function GetActionFunc takes integer i returns code
    if i == 0 then
        return function LeftDown
    elseif i == 1 then
        return function RightDown
    elseif i == 2 then
        return function DownDown
    elseif i == 3 then
        return function UpDown
    elseif i == 4 then
        return function LeftUp
    elseif i == 5 then
        return function RightUp
    elseif i == 6 then
        return function DownUp
    elseif i == 7 then
        return function UpUp
    endif
    return null
endfunction

//===========================================================================
private function Init takes nothing returns nothing
    local integer i = 0
    local integer i2 = 0
    
    loop
        exitwhen i >= 4
        set ArrowDetectors[i] = CreateTrigger()
        set ArrowDetectors[i+4] = CreateTrigger()
        loop
            exitwhen i2 >= MAX_NUMBER_OF_PLAYERS
            call TriggerRegisterPlayerEvent(ArrowDetectors[i], Player(i2), GetKeyEventDown(i))
            call TriggerRegisterPlayerEvent(ArrowDetectors[i+4], Player(i2), GetKeyEventUp(i))
            set i2 = i2 + 1
        endloop
        set i2 = 0
        call TriggerAddAction(ArrowDetectors[i], GetActionFunc(i))
        call TriggerAddAction(ArrowDetectors[i+4], GetActionFunc(i+4))
        set i = i + 1
    endloop
    
endfunction

endlibrary
PS: I will convert it to normal JASS if you want, but I figured using a library with private functions was easier and most systems and spells which implement this system will probably use vJASS anyway.
 
Last edited:
Level 6
Joined
Apr 16, 2007
Messages
177
JASS:
private function LeftUp takes nothing returns nothing
    set KeyDown[MAX_NUMBER_OF_PLAYERS*0+GetPlayerId(GetTriggerPlayer())] = false
endfunction

2D arrays would be appreciated. You use VJass anyway ; )

JASS:
    local integer i2 = 0

j would be nice instead of i2.

JASS:
        endloop
        set i2 = 0

the set i2 = 0 should be infront of the loop, not behind it ; )


MAX_NUMBER_OF_PLAYERS is not counting players but keys; Also, you should use it everywhere where you can ( instead of adding +4 here and counting to 4 there )

This is what I modified your stuff into:

JASS:
library KeyDetector
globals                                                   
    private constant integer MAX_USER_SLOT = 12 // I don't think AI would press keys ;D
    private constant integer KEYS = 4
    private trigger array ArrowDetectors[KEYS][2]
    private boolean array KeyDown[KEYS][MAX_USER_SLOT]

    constant integer ARROW_LEFT = 0
    constant integer ARROW_RIGHT = 1
    constant integer ARROW_DOWN = 2
    constant integer ARROW_UP = 3
endglobals

function IsKeyDown takes integer key, player id returns boolean
    return KeyDown[key][ GetPlayerId(id) ]
endfunction

private function GetKeyEventDown takes integer i returns playerevent
    if i == 0 then
        return EVENT_PLAYER_ARROW_LEFT_DOWN
    elseif i == 1 then
        return EVENT_PLAYER_ARROW_RIGHT_DOWN
    elseif i == 2 then
        return EVENT_PLAYER_ARROW_DOWN_DOWN
    elseif i == 3 then
        return EVENT_PLAYER_ARROW_UP_DOWN
    endif
    return null
endfunction

private function GetKeyEventUp takes integer i returns playerevent
    if i == 0 then
        return EVENT_PLAYER_ARROW_LEFT_UP
    elseif i == 1 then
        return EVENT_PLAYER_ARROW_RIGHT_UP
    elseif i == 2 then
        return EVENT_PLAYER_ARROW_DOWN_UP
    elseif i == 3 then
        return EVENT_PLAYER_ARROW_UP_UP
    endif
    return null
endfunction

private function ArrowDown takes nothing returns nothing
    local integer i = 0
    local trigger trigTrigger = GetTriggeringTrigger()
    local integer trigId = GetPlayerId( GetTriggerPlayer() )
    
    loop
        exitwhen ArrowDetectors[i][0] == trigTrigger
        set i = i + 1
    endloop
    set KeyDown[i][trigId] = true    
    set trigTrigger = null
endfunction

private function ArrowUp takes nothing returns nothing
    local integer i = 0
    local trigger trigTrigger = GetTriggeringTrigger()
    local integer trigId = GetPlayerId( GetTriggerPlayer() )
    
    loop
        exitwhen ArrowDetectors[i][1] == trigTrigger
        set i = i + 1
    endloop
    set KeyDown[i][trigId] = false
    set trigTrigger = null
endfunction

//===========================================================================
private function Init takes nothing returns nothing
    local integer i = 0
    local integer j = 0

    loop
        exitwhen i >= KEYS
        set ArrowDetectors[i][0] = CreateTrigger()
        set ArrowDetectors[i][1] = CreateTrigger()
        set j = 0
        loop
            exitwhen j >= MAX_USER_SLOT
            call TriggerRegisterPlayerEvent(ArrowDetectors[i][0], Player(j), GetKeyEventDown( i ) )
            call TriggerRegisterPlayerEvent(ArrowDetectors[i][1], Player(j), GetKeyEventUp( i ) )
            set KeyDown[i][j] = false
            set j = j + 1
        endloop
        call TriggerAddAction(ArrowDetectors[i][0], function ArrowDown )
        call TriggerAddAction(ArrowDetectors[i][1], function ArrowUp )
        set i = i + 1
    endloop            
endfunction
endlibrary



Interesting system nevertheless,
Davey
 
JASS:
private function LeftUp takes nothing returns nothing
    set KeyDown[MAX_NUMBER_OF_PLAYERS*0+GetPlayerId(GetTriggerPlayer())] = false
endfunction


2D arrays would be appreciated. You use VJass anyway ; )
Never even heard of 2D arrays in JASS before, that's why I didn't use them.
JASS:
local integer i2 = 0

j would be nice instead of i2.
"i" stands for index. So "i2" is index 2... you don't just carry on with the alpabet...

JASS:
        endloop
        set i2 = 0

the set i2 = 0 should be infront of the loop, not behind it ; )
Does it matter where you put it? Not really.

MAX_NUMBER_OF_PLAYERS is not counting players but keys; Also, you should use it everywhere where you can ( instead of adding +4 here and counting to 4 there )
No it's definitely players. What's the point in setting a variable for the max number of keys anyway? It's always going to stay 4...
 
Level 6
Joined
Apr 16, 2007
Messages
177
"i" stands for index. So "i2" is index 2... you don't just carry on with the alpabet...
It's bad style.
Also, it is not index 2 but the inner index, which is normally called "j".

Does it matter where you put it? Not really.
It's bad style.
No it's definitely players.
There's definitely more players than 4, or 5.

What's the point in setting a variable for the max number of keys anyway? It's always going to stay 4...
It's bad style.
 
It's bad style.
Also, it is not index 2 but the inner index, which is normally called "j".
different people have different opinions on "bad style"
It's bad style.
as above...
There's definitely more players than 4, or 5.
I know. I'll set that to 12 if you like, but it's there for people to edit. Some people might want maximum efficiency and only have it detect keys down for a few players...
It's bad style.
It's not at all. Constants are there to make it easy to change elements of the script. The game can't detect more than the 4 arrow keys anyway, so what's the point in making it changeable in my script when it won't actually do anything?
 
Level 6
Joined
Apr 16, 2007
Messages
177
different people have different opinions on "bad style"
Still there are some conventions.

It's not at all. Constants are there to make it easy to change elements of the script. The game can't detect more than the 4 arrow keys anyway, so what's the point in making it changeable in my script when it won't actually do anything?
Have you ever heard the words "maintainability" and "readability"?
I bet an advanced programmer ( like you ) has, so you should be able to answer your question all on your own.

It is still changeable in your code. I could just go arround and change all your fours to threes, for example.

Constants are there to easily modify your code and to make it more readable. According to your argumentation: Why on earth would somebody name a constant Pi, if he can just aswell write 3.1415926..... ? It is not meant to change anyway!

JASS:
local integer angle = 0
local unit trigUnit = GetTriggerUnit()

loop
    exitwhen angle > 3.1415962
    call SetUnitX( trigUnit, 10 * Cos( angle ) )
    call SetUnitY( trigUnit, 10 * Sin( angle ) )
    set angle = angle + 0.31415962
endloop

:thumbs_up:

VS

JASS:
local integer angle = 0
local unit trigUnit = GetTriggerUnit()

loop
    exitwhen angle > PI
    call SetUnitX( trigUnit, RADIUS * Cos( angle ) )
    call SetUnitY( trigUnit, RADIUS * Sin( angle ) )
    set angle = angle + PI/10
endloop

So, what is the point of making 3.1415926 a constant PI, that is changeable with a few clicks?

PS:Yourspacingisquite wierd.
JASS:
    set KeyDown[MAX_NUMBER_OF_PLAYERS*1+GetPlayerId(GetTriggerPlayer())] = false
 
Top