System ArrowKey

Bribe

vJass errors are legion
Reaction score
67
If you want to centralize all the arrow key events in your map and keep your workload as short and sweet as possible, I have designed this library for you.

JASS:
library ArrowKeyEvent /*
    =========================================================================
    ArrowKeyEvent version 1.0.0.0
    =========================================================================
    Credits:
    -------------------------------------------------------------------------
    -   Written by Bribe.
    -   Earth-Fury for providing a lot of inspiration for the development of
        this system (especially documentation) via his KeyAction resource.
    -   tooltiperror & Sgqvur for providing very helpful, constructive advice.
    =========================================================================
    Introduction:
    -------------------------------------------------------------------------
    Easy to use, efficient system for handling all arrow key events. It uses
    arrays and GetHandleId lookups to avoid the trap of many cloned functions
    that so many arrow key systems suffer from.
    =========================================================================
    API Guide:
    -------------------------------------------------------------------------
    To help centralize and make everything understandable, I originally used
    the constants appointed by Blizzard (bj_KEYEVENTKEY_LEFT/RIGHT/etc). But
    there was a lot of criticism over their ugliness so I made the following
    constants to correspond accordingly. They have the same values as the BJ
    constants, so you can use whichever is more appealing for you.

    Their purpose is to be passed as arguments so you are able to query such
    things as "is this key pressed" or simply to help make sense of what key
    was pressed from an event response and interpret it as an integer.

        constant integer ARROW_KEY_LEFT  = 0
        constant integer ARROW_KEY_RIGHT = 1
        constant integer ARROW_KEY_DOWN  = 2
        constant integer ARROW_KEY_UP    = 3

    -------------------------------------------------------------------------
    As I was developing this resource, is was mostly written in vanilla JASS.
    I had since converted it to OOP syntax but it has been requested to bring
    it back. I have made it available once again.

    function IsArrowKeyPressed
        takes player whichPlayer, integer arrowKey
            returns boolean

        To find out if the arrow key was pressed, you need to first ask which
        player is holding down the key, and then pass an ARROW_KEY constant
        to represent the key you are querying for.

    function RegisterArrowKeyEvent
        takes code onEvent
            returns nothing

        Instead of making up to 8 different functions for 8 different events,
        you can use this instead. Just specify a function that you want to be
        called whenever any key is pressed. That function must "return false".
        For event responses, you reference one of the following 3 functions:

    function GetEventArrowKeyPlayerId
        takes nothing
            returns integer

        This is more of an optimization benefit than not. GetTriggerPlayer()
        will get you the player who pressed the key, but most of the time you
        just need to know the player ID of that person.

    function GetEventArrowKey
        takes nothing
            returns integer

        Call this function to find out which key was pressed. It will return
        one of the four ARROW_KEY constants, of course.

    function IsEventArrowKeyPressed
        takes nothing
            returns boolean

        This is also here more for optimization's benefit. You can certainly
        use "IsArrowKeyPressed(GetEventArrowKey(GetEventArrowKeyPlayerId()))"
        but this is much more convenient I must say.

    -------------------------------------------------------------------------
    "implement ArrowKey"

    ArrowKey API is accessible as if it were part of the implementing struct
    itself. As a bonus, you can create a method "onArrowKeyEvent" that gets
    called automatically when any key is pressed. The method musn't be static,
    because the member "this" is a player ID (to mirror the ArrowKey struct's
    own API). It must take an integer as its first argument to represent the
    pressed or released key. As a final argument, it must take a boolean to
    determine if the key was pressed (true) or released (false).

    The member "this" taken by the method is a player ID.
*/
    //=======================================================================
    //
    // System Code
    //
    //=======================================================================

    globals
        //-------------------------------------------------------------------
        // Yo dawg, I herd you like constant variables so I gave you some new
        // constant variables so you can have some constant variables to go
        // with Blizzard's constant variables.
        //
        constant integer ARROW_KEY_LEFT  = bj_KEYEVENTKEY_LEFT
        constant integer ARROW_KEY_RIGHT = bj_KEYEVENTKEY_RIGHT
        constant integer ARROW_KEY_DOWN  = bj_KEYEVENTKEY_DOWN
        constant integer ARROW_KEY_UP    = bj_KEYEVENTKEY_UP
    endglobals

    //=======================================================================
    // Ugly modules are needed to prevent initialization bugs.
    //
    private module Init
        private static method onInit takes nothing returns nothing
            local player p
            local integer i = 12
            local trigger t = .trig
            loop
                set i = i - 1
                set p = Player(i)
                if GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING and GetPlayerController(p) == MAP_CONTROL_USER then

                    //Register arrow key events for playing players
                    call TriggerRegisterPlayerEvent(t, p, EVENT_PLAYER_ARROW_LEFT_DOWN)
                    call TriggerRegisterPlayerEvent(t, p, EVENT_PLAYER_ARROW_LEFT_UP)
                    call TriggerRegisterPlayerEvent(t, p, EVENT_PLAYER_ARROW_RIGHT_DOWN)
                    call TriggerRegisterPlayerEvent(t, p, EVENT_PLAYER_ARROW_RIGHT_UP)
                    call TriggerRegisterPlayerEvent(t, p, EVENT_PLAYER_ARROW_DOWN_DOWN)
                    call TriggerRegisterPlayerEvent(t, p, EVENT_PLAYER_ARROW_DOWN_UP)
                    call TriggerRegisterPlayerEvent(t, p, EVENT_PLAYER_ARROW_UP_DOWN)
                    call TriggerRegisterPlayerEvent(t, p, EVENT_PLAYER_ARROW_UP_UP)

                    //Run if library ArrowKey is found in the map.
                    //! runtextmacro optional INIT_ARROW_KEY_AA()
                endif
                exitwhen i == 0
            endloop

            //Run if library ArrowKey is found in the map.
            //! runtextmacro optional INIT_ARROW_KEY_CALL_LINK()

            call TriggerAddCondition(t, Filter(function ArrowKey.actions))
            set p = null
            set t = null
        endmethod
    endmodule

    //=======================================================================
    // A central struct to handle all ArrowKey mechanics. This has its uses,
    // giving you slightly more control over the inner system functionality.
    //
    struct ArrowKey extends array

        //-------------------------------------------------------------------
        // Event responses
        //
        readonly static integer  eventKey        = 0     //Arrow key that triggered the event.
        readonly static boolean  eventKeyPressed = false //Was the arrow key pressed?
        readonly static ArrowKey eventPlayerId   = 0     //The id of the player who pressed the key.

        //Run if library ArrowKey is found in the map.
        //! runtextmacro optional ARROW_KEY_DECLARE_ARRAYS()

        //-------------------------------------------------------------------
        // System variables
        //
        private static trigger trig = CreateTrigger()   //Handles all events.
        private static boolean array press              //Is key pressed?

        //===================================================================
        // User-friendly typecasting.
        //
        static method operator [] takes player who returns ArrowKey
            return GetPlayerId(who)
        endmethod

        //===================================================================
        // Great for simplifying arrow key events - this function runs when
        // any player presses or releases an arrow key. The code passed is
        // expected to "return false".
        //
        static method registerEvent takes code onEvent returns nothing
            call TriggerAddCondition(trig, Filter(onEvent))
        endmethod

        //===================================================================
        // Returns true if the key is pressed, false if it is released.
        //
        method isPressed takes integer arrow returns boolean
            return press[this + arrow * 12]
        endmethod

        //Run if library ArrowKey is found in the map.
        //! runtextmacro optional ARROW_KEY_AXIS_METHODS()

        //===================================================================
        // If you are running debug tests, this might come in handy.
        //
        static if DEBUG_MODE then
            static method getKeyName takes integer arrow returns string
                if arrow == ARROW_KEY_LEFT then
                    return "LEFT"
                elseif arrow == ARROW_KEY_RIGHT then
                    return "RIGHT"
                elseif arrow == ARROW_KEY_DOWN then
                    return "DOWN"
                elseif arrow == ARROW_KEY_UP then
                    return "UP"
                endif
                return "--"
            endmethod
        endif

        //===================================================================
        //
        // Private Components
        //
        //===================================================================

        //===================================================================
        // Key event handler.
        //
        private static method actions takes nothing returns boolean
            local integer id = GetHandleId(GetTriggerEventId()) - 261
            set .eventPlayerId = GetPlayerId(GetTriggerPlayer())

            //If id is an even number, the key was pressed.
            set .eventKey = id / 2
            set .eventKeyPressed = .eventKey * 2 == id
            set .press[.eventPlayerId + .eventKey * 12] = .eventKeyPressed

            //Run if library ArrowKey is found in the map.
            //! runtextmacro optional ARROW_KEY_SETUP()

            return false
        endmethod

        //Run if library ArrowKey is found in the map.
        //! runtextmacro optional INIT_ARROW_KEY_LINK()

        //Initialize the system via module
        implement Init

    endstruct

    //=======================================================================
    //
    // Event Responses
    //
    //=======================================================================

    //=======================================================================
    function IsArrowKeyPressed takes player whichPlayer, integer arrowKey returns boolean
        return ArrowKey[whichPlayer].isPressed(arrowKey)
    endfunction

    //=======================================================================
    function RegisterArrowKeyEvent takes code onEvent returns nothing
        call ArrowKey.registerEvent(onEvent)
    endfunction

    //=======================================================================
    function GetEventArrowKeyPlayerId takes nothing returns integer
        return ArrowKey.eventPlayerId
    endfunction

    //=======================================================================
    function GetEventArrowKey takes nothing returns integer
        return ArrowKey.eventKey
    endfunction

    //=======================================================================
    function IsEventArrowKeyPressed takes nothing returns boolean
        return ArrowKey.eventKeyPressed
    endfunction

    //=======================================================================
    // Implementation of this module allows you to make your struct "extend"
    // the ArrowKey struct. As a bonus feature, you can create a method named
    // "onArrowKeyEvent" that is called whenever a key is pressed or released.
    // The method needs "takes integer arrow, boolean pressed", and must be
    // positioned *above* the "implement ArrowKey" statement (this way it is
    // detected by the static if and doesn't compile to a function interface).
    //
    module ArrowKey

        //Delegates are fun, you should try them out.
        private delegate ArrowKey AK

        //===================================================================
        // Please call this method from *below the module implement statement
        // if you know what's good for you.
        //
        static method operator [] takes player who returns thistype
            return GetPlayerId(who)
        endmethod

        static if thistype.onArrowKeyEvent.exists then
            private static method eventProxy takes nothing returns boolean
                call thistype(.eventPlayerId).onArrowKeyEvent(.eventKey, .eventKeyPressed)
                return false
            endmethod
        endif

        private static method onInit takes nothing returns nothing
            local thistype i = 12
            loop
                set i = i - 1
                set i.AK = i    //Delegates require some delegation of course.
                exitwhen i == 0
            endloop
            static if thistype.onArrowKeyEvent.exists then
                call ArrowKey.registerEvent(function thistype.eventProxy)
            endif
        endmethod

    endmodule

endlibrary


Optional functionality add-on. If you want to make use of it, make sure your library requires ArrowKey.

JASS:
library ArrowKey requires ArrowKeyEvent /*
    =========================================================================
    ArrowKey version 1.0.0.0
    =========================================================================
    Credits:
    -------------------------------------------------------------------------
    Same as credits for ArrowKeyEvent
    =========================================================================
    API Guide:
    =========================================================================
    ArrowKey provides more than just event responses, it expects you to have
    some other purposes for those values. If you have an arrow-key movement
    system, for example, you might want to have certain values to know how to
    offset the unit's coordinates. The following constants provide those for
    you:

        constant integer ARROW_KEY_NEG_VALUE = -1
        constant integer ARROW_KEY_NIL_VALUE =  0
        constant integer ARROW_KEY_POS_VALUE =  1

    How you get those values in the first place is first you must query one
    of the X or Y axis, because both axis have positive and negative values
    of their own of course. You can reference these constant variables for
    that purpose:

        constant integer ARROW_KEY_X_AXIS = 0
        constant integer ARROW_KEY_Y_AXIS = 1

    I imagine you must be wondering what this is all good for. Here are the
    functions you would pass these constants into for arguments or receive as
    return values:

    function GetArrowKeyAxis
        takes integer arrowKey
            returns integer

        Pass an ARROW_KEY_LEFT/RIGHT/etc. constant to this function to return
        the axial constant they correspond to. LEFT & RIGHT belong to the X
        axis, DOWN/UP to the Y axis of course. This is useful if you're work-
        ing with the values dynamically.

    function GetArrowKeyValue
        takes integer arrowKey
            returns integer

        I am sure you are aware that along any decent graph you have negative
        values on the left/lower axis and positive values on the right/upper
        axis. This is why this function exists. It will return -1 for down or
        left, 1 for up or right. This is very useful if you use coordinates &
        want to offset by the direction of an arrow key.

    function GetOpposingArrowKey
        takes integer arrowKey
            returns integer

        This is mostly used by the system to avoid cloned functions, but if
        you want to use it or want to know if you should use it, this simply
        returns whatever key is opposite the key you passed. LEFT for RIGHT,
        for example.

    function GetAxisArrowKey
        takes player whichPlayer, integer axis
            returns integer

        This is a really good one. First, of course you must pass a player
        argument to let the system know which player's key you want info on.
        Second, you pass it the X or Y axis value. If you want to know if the
        player is holding down the LEFT or RIGHT key, suppose they are both
        pressed? This will let you know which key was pressed LAST. If the
        last key pressed is released, but the other is still held down, this
        function intelligently knows to go back to that other key.

        If there is no key being held down, it returns the following constant:

            constant integer ARROW_KEY_NONE = -1

    function GetAxisArrowKeyValue
        takes player whichPlayer, integer axis
            returns integer

        This is a wrapper of sorts for the above function, however it filters
        out the ARROW_KEY_NONE value and returns ARROW_KEY_NIL_VALUE in that
        case. This is useful because if you are doing math you can multiply
        by 1 to go up/right, -1 to go down/left, or 0 to remain stationary on
        that axis.
*/
    //=======================================================================
    //
    // System Code
    //
    //=======================================================================

    globals
        //-------------------------------------------------------------------
        // Axis are important for a parameter in GetAxisArrowKey, a function
        // that you determine which axis key the user is focusing on.
        //
        // If no key is pressed, the value ARROW_KEY_NONE will be returned.
        //
        constant integer ARROW_KEY_X_AXIS = 0
        constant integer ARROW_KEY_Y_AXIS = 1
        constant integer ARROW_KEY_NONE   = -1

        //-------------------------------------------------------------------
        // These help to interpret arrow key values returned by the system.
        // They are very useful for coordinate-based math.
        //
        constant integer ARROW_KEY_NEG_VALUE = -1
        constant integer ARROW_KEY_NIL_VALUE =  0
        constant integer ARROW_KEY_POS_VALUE =  1
    endglobals

    function GetOpposingArrowKey takes integer arrowKey returns integer
        return ArrowKey.oppositeKey[arrowKey]
    endfunction

    function GetArrowKeyAxis takes integer arrowKey returns integer
        return ArrowKey.keyAxis[arrowKey]
    endfunction

    function GetArrowKeyValue takes integer arrowKey returns integer
        return ArrowKey.keyValue[arrowKey]
    endfunction

    function GetAxisArrowKey takes player whichPlayer, integer axis returns integer
        return ArrowKey[whichPlayer].getAxisKey(axis)
    endfunction

    function GetAxisArrowKeyValue takes player whichPlayer, integer axis returns integer
        return ArrowKey[whichPlayer].getAxisKeyValue(axis)
    endfunction

    //! textmacro ARROW_KEY_DECLARE_ARRAYS
        //-------------------------------------------------------------------
        // Useful reference variables. Pass one of the arrow key constants
        // ARROW_KEY_UP, ARROW_KEY_DOWN, ARROW_KEY_LEFT or ARROW_KEY_RIGHT.
        //
        readonly static integer array oppositeKey //Axis-opposite of key (.LEFT<->.RIGHT, .UP<->.DOWN)
        readonly static integer array keyAxis     //Axis of key (.X_AXIS or .Y_AXIS)
        readonly static integer array keyValue    //Key value (.NEG_VALUE or .POS_VALUE)

        //Needed for storing the active axis key
        private static integer array aaKey
    //! endtextmacro

    //! textmacro ARROW_KEY_AXIS_METHODS
        //===================================================================
        // Returns ARROW_KEY_NONE if there is no key pressed for that axis.
        // The return value is whatever key the user focuses on. If a player
        // holds down an arrow key, that is the active key. But if the player
        // presses the opposite key, that will be the new active key. If that
        // key is released before the other key is released, then the other
        // key will be the return value as that one is still being held down.
        //
        method getAxisKey takes integer axis returns integer
            return .aaKey[this * 2 + axis]
        endmethod

        //===================================================================
        // This method is more safe than the one above because it filters out
        // the unsafe ARROW_KEY_NONE return. It returns ARROW_KEY_NIL_VALUE
        // if there is no active key for the queried axis. I recommend this
        // method for something like an arrow key movement system because you
        // can use for seamless math.
        //
        // Example use:
        //
        //  local ArrowKey ak = ArrowKey[GetTriggerPlayer()]
        //  local real x = GetUnitX(hero[ak]) + ak.getAxisKeyValue(ArrowKey.X_AXIS) * polarOffset
        //  local real y = GetUnitX(hero[ak]) + ak.getAxisKeyValue(ArrowKey.Y_AXIS) * polarOffset
        //  call IssuePointOrder(hero[ak], "move", x, y)
        //
        method getAxisKeyValue takes integer axis returns integer
            local integer i = this.getAxisKey(axis)
            if i == ARROW_KEY_NONE then
                return ARROW_KEY_NIL_VALUE
            endif
            return .keyValue<i>
        endmethod
    //! endtextmacro

    //! textmacro ARROW_KEY_SETUP
        if .eventKeyPressed then
            //The active axis key is the one that was just pressed.
            set id = .eventKey
        elseif .eventPlayerId.isPressed(.oppositeKey[.eventKey]) then
            //The active axis key is the opposite of this released one.
            set id = .oppositeKey[.eventKey]
        else
            //There is no active axis key.
            set id = ARROW_KEY_NONE
        endif

        //Assign the active arrow key to the player&#039;s relevant key axis.
        set .aaKey[.eventPlayerId * 2 + .keyAxis[.eventKey]] = id
    //! endtextmacro

    //! textmacro INIT_ARROW_KEY_LINK
        private static method linkKeys takes integer a, integer b, integer axis returns nothing
            set .keyAxis[a] = axis
            set .keyAxis<b> = axis
            set .keyValue[a] = ARROW_KEY_NEG_VALUE
            set .keyValue<b> = ARROW_KEY_POS_VALUE
            set .oppositeKey[a] = b
            set .oppositeKey<b> = a
        endmethod
    //! endtextmacro

    //! textmacro INIT_ARROW_KEY_AA
        //Default active axis keys to ARROW_KEY_NONE
        set .aaKey<i> = ARROW_KEY_NONE
        set .aaKey[i + 1] = ARROW_KEY_NONE
    //! endtextmacro

    //! textmacro INIT_ARROW_KEY_CALL_LINK
        call .linkKeys(ARROW_KEY_LEFT, ARROW_KEY_RIGHT, ARROW_KEY_X_AXIS)
        call .linkKeys(ARROW_KEY_DOWN, ARROW_KEY_UP, ARROW_KEY_Y_AXIS)
    //! endtextmacro

endlibrary</i></b></b></b></i>


Example code:
Run it in debug mode.

JASS:
struct Tester extends array

    method onArrowKeyEvent takes integer arrow, boolean pressed returns nothing
        static if LIBRARY_ArrowKey then
            call BJDebugMsg(&quot;Axis Focus: X: &quot; + .getKeyName(thistype(0).getAxisKey(ARROW_KEY_X_AXIS)) + &quot;, Y: &quot; + .getKeyName(thistype(0).getAxisKey(ARROW_KEY_Y_AXIS)))
        else
            if pressed then
                call BJDebugMsg(&quot;Key pressed: &quot; + .getKeyName(arrow))
            else
                call BJDebugMsg(&quot;Key released: &quot; + .getKeyName(arrow))
            endif
        endif
    endmethod

    implement ArrowKey
endstruct
 

Sgqvur

FullOfUltimateTruthsAndEt ernalPrinciples, i.e shi
Reaction score
62
This is a good idea but I don't like a few things:


1. The library is called ArrowKeyEvent but the event resposes are backwards:

-> means "I think should be / It seams better to me if its called"

GetEventArrowKeyPlayerId -> GetTriggerPlayerId() or GetArrowKeyEventPlayerId()
GeEventArrowKey -> KeyId() or ArrowKeyId() or GetArrowKeyEventKeyId()

2. The Blizzard.j constants have rather bad names, I think it would be better if the library exports better alternatives:

bj_KEYEVENTKEY_LEFT -> ARROW_KEY_LEFT
bj_KEYEVENTKEY_RIGHT -> ARROW_KEY_RIGHT
bj_KEYEVENTKEY_DOWN -> ARROW_KEY_DOWN
bj_KEYEVENTKEY_UP -> ARROW_KEY_UP

3. RegisterAnyArrowKeyEvent should have access to a boolean which determens if the key was pressed or released:
something like KeyUp() or KeyDown()

So all in all it becomes something like:

JASS:
function f takes nothing returns boolean
    local integer p     = GetTriggerPlayerId()
    local integer key   = KeyId()
    local boolean is_up = KeyUp() // or KeyDown()

    if key == ARROW_KEY_LEFT then
        if is_up then
            call DestroyPlayer(p)
        else
            call CopyPlayer(p)
        endif
    else if key == ARROW_KEY_RIGHT then
        if is_up then
            // etc.
        end
    // etc.
    endif

    return false
endfunction

function onInit takes nothing returns nothing 
    call RegisterAnyArrowKeyEvent(function f)
endfunction


PS: I don't see a resason besides library name missmatch if you add the ESC key as an option as well.
 

Bribe

vJass errors are legion
Reaction score
67
Thanks for the feedback. I've added "IsEventArrowKeyPressed".

I like keeping the BJ constants because syntax highlighting covers them, making it more intuitive as well. Why re-create the thing just to give it slightly different name?

The event naming follows normal convention but I can't make it short stuff like "GetTriggerPlayerId" because there could be another system that wants that same name.

Same reason goes for why I won't include the Esc key registry. Yes it adds more functionality but this system doesn't claim any rights to it.
 

Sgqvur

FullOfUltimateTruthsAndEt ernalPrinciples, i.e shi
Reaction score
62
Oh lol it already does what I thought it didn't =):

JASS:
    function f takes nothing returns boolean
        local integer p     = GetEventArrowKeyPlayerId()
        local integer key   = GetEventArrowKey()
        local boolean is_up = not IsArrowKeyPressed(p, key)

        if key == bj_KEYEVENTKEY_UP then
            if is_up then
                call BJDebugMsg(&quot;released up key&quot;)
            else
                call BJDebugMsg(&quot;pressed up key&quot;)
            endif
        elseif key == bj_KEYEVENTKEY_DOWN then
            if is_up then
                call BJDebugMsg(&quot;released down key&quot;)
            else
                call BJDebugMsg(&quot;pressed down key&quot;)
            endif
        endif

        return false
    endfunction

    function onInit takes nothing returns nothing 
        call RegisterAnyArrowKeyEvent(function f)
    endfunction


Bribe:
1. Same reason goes for why I won't include the Esc key registry. Yes it adds more functionality but this system doesn't claim any rights to it.

1. It could be at least optional? No? Some static if voodoo why not...

But anyway. If you ask me the RegisterAnyArrowKeyEvent renders the RegisterArrowKeyEvent kinda useless but I guess it's a style choice:
a) one monolithic function or b) four not so monolithic functions. I'll always pick the first because it seams to lead to a better structured script.
 

Bribe

vJass errors are legion
Reaction score
67
The "ArrowKeyEvent" would be useful if you only want to register when the up key and down key or left key and right key are pressed, like switching through a menu.
 

Sgqvur

FullOfUltimateTruthsAndEt ernalPrinciples, i.e shi
Reaction score
62
Bribe:
1. The "ArrowKeyEvent" would be useful if you only want to register when the up key and down key or left key and right key are pressed, like switching through a menu.

1. Well this simply means that nothing will happen for those keys in the "monolithic" function:

Only up and down responses:

JASS:
    function f takes nothing returns boolean
        local integer p     = GetEventArrowKeyPlayerId()
        local integer key   = GetEventArrowKey()

        if     key == bj_KEYEVENTKEY_UP then
            call MenuGoUp(p)
        elseif key == bj_KEYEVENTKEY_DOWN then
            call MenuGoDown(p)

        elseif key == bj_KEYEVENTKEY_LEFT then
            call DoNothing
        elseif key == bj_KEYEVENTKEY_RIGHT then
            call DoNothing
        endif

        return false
    endfunction

    function onInit takes nothing returns nothing 
        call RegisterAnyArrowKeyEvent(function f)
    endfunction


Edit: But I think it should be:
JASS:
-
        local integer p          = GetEventArrowKeyPlayerId()
        local integer key        = GetEventArrowKey()
        lcaol integer is_pressed = IsArrowKeyPressed(p, key)

        if     key == bj_KEYEVENTKEY_UP and is_pressed then
            call MenuGoUp(p)
        elseif key == bj_KEYEVENTKEY_DOWN and is_pressed then
            call MenuGoDown(p)


else the menu will move two (on press and on release) items at a time:
 

Bribe

vJass errors are legion
Reaction score
67
That might be better actually. I mean, this obviously isn't an event that fires quite a lot so having 10 evaluations instead of 5 isn't really going to make a difference.

I'd like to hear more opinions on it though.
 

Sgqvur

FullOfUltimateTruthsAndEt ernalPrinciples, i.e shi
Reaction score
62
Bribe:
1. If you want to centralize all the arrow key events in your map and keep your workload as short and sweet as possible, I have designed this library for you.
2. I'd like to hear more opinions on it though.

This is good enough to convince me =):
JASS:
function f takes nothing returns boolean
    local integer p            = GetEventArrowKeyPlayerId()
    local integer key          = GetEventArrowKey()
    lcaol integer is_pressed   = IsArrowKeyPressed(p, key)
    local integer is_released  = false

    if menu_mode[p] then
        if     key == bj_KEYEVENTKEY_UP and is_pressed then
            call MenuGoUp(p)
        elseif key == bj_KEYEVENTKEY_DOWN and is_pressed then
            call MenuGoDown(p)
        endif
    elseif character_movement_mode[p] then
        set is_released = not is_pressed

        if     key == bj_KEYEVENTKEY_LEFT and is_pressed then
            call StartMovingPlayerCharacterWest(p)
        elseif key == bj_KEYEVENTKEY_LEFT and is_released then
            call StopMovingPlayerCharaacterWest(p)

        elseif key == bj_KEYEVENTKEY_RIGHT and is_pressed then
            call StartMovingPlayerCharacterEast(p)
        elseif key == bj_KEYEVENTKEY_RIGHT and is_released then
            call StopMovingPlayerCharacterWest(p)

        elseif key == bj_KEYEVENTKEY_UP and is_pressed then
            call StartMovingPlayerCharacterNorth(p)
        elseif key == bj_KEYEVENTKEY_UP and is_relesed then
            call StopMovingPlayerCharacterNorth(p)

        elseif key == bj_KEYEVENTKEY_DOWN and is_pressed then
            call StartMovingPlayerCharacterSouth(p)
        elseif key == bj_KEYEVENTKEY_DOWN and is_released then
            call StopMovingPlayerCharacterSouth(p)
        endif
    endif

    return false
endfunction

function onInit takes nothing returns nothing 
    call RegisterAnyArrowKeyEvent(function f)
endfunction
 

Bribe

vJass errors are legion
Reaction score
67
Completely reworked the API and added some good new features.
 

Sgqvur

FullOfUltimateTruthsAndEt ernalPrinciples, i.e shi
Reaction score
62
1. Where is the equivalent for the GetEventArrowKey() function?
Which method ArrowKey.<????>

2. I mean wasn't the point of the library to "centralize all the arrow key events in your map",
but what it's doing now is to encourage and allowing users to implement it for different structs aka "decentralizing".

3. I don't think that users should have the choice of aether to user ArrowKey.registerEvent or implementing the ArrowKey and using the onEvent. "There should be one – and preferably only one – obvious way to do it" right?

4. I don't think the "good new features" are really useful.


PS: Glad you got rid of the bj_KEYEVENTKEY_LEFT for the ArrowKey.LEFT though.

Edit: Oh ArrowKey.eventKey static readonly right... was searching for a method =)

Edit2: Hm

Bribe:
JASS:
        //  local ArrowKey ak = ArrowKey[GetTriggerPlayer()]
        //  local real x = GetUnitX(hero[ak]) + ak.getAxisKeyValue(ArrowKey.X_AXIS) * polarOffset
        //  local real y = GetUnitX(hero[ak]) + ak.getAxisKeyValue(ArrowKey.Y_AXIS) * polarOffset


I think the above is now unintuitive and instead the first line should be local player p = ArrowKey[GetTriggerPlayer()] or ArrowKey.playerid or GetPlayerId(GetTriggerPlayer()) because we are accessing the hero of player p (hero[p]) not the hero of the ArrowKey (hero[ak]).
But if used this way the p.isPressed(ArrowKey.eventKey) method wouldn't work (has to be ak.isPressed(ArrowKey.eventKey)
but ak is unintuitive as stated above so yeah this seams to be a design problem =).
 

Bribe

vJass errors are legion
Reaction score
67
AIDS also has two ways to register the events, either by using the textmacro or by using the AIDS_RegisterOnAllocate/Deallocate methods. UnitIndexer on Hive also has two ways to register. The Event library also has 2 ways to register, either by struct API or by the normal JASS API.

Why would you think the new features aren't useful? Instead of a long chain of if-statements for an arrow key movement system, for example, you could use the method "getAxisKeyValue" to generate a multiplier for your x- and y- offsets.
 

Sgqvur

FullOfUltimateTruthsAndEt ernalPrinciples, i.e shi
Reaction score
62
Bribe:
1. AIDS also has two ways to register the events, either by using the textmacro or by using the AIDS_RegisterOnAllocate/Deallocate methods. UnitIndexer on Hive also has two ways to register. The Event library also has 2 ways to register, either by struct API or by the normal JASS API.

2. Why would you think the new features aren't useful? Instead of a long chain of if-statements for an arrow key movement system, for example, you could use the method "getAxisKeyValue" to generate a multiplier for your x- and y- offsets.

1. Well tnx for informing me but I think you have to ask yourself as the designer of the library
which on is better for the user? I take the side of implementing the module because you get the variables
as parameters instead of calling unnecessary event response functions, but that's just me. And I think
you should rename the onEvent method because it's too generic for a name and it should take a third parameter the id of the player =).

2. Because the purpose of the library is to just detect the arrow keys for all players and in a monolithic function to just delegate them
to specific actions. The axes methods are too specific.
 

Bribe

vJass errors are legion
Reaction score
67
I have made an update where you can control whether or not the extra features get added. I agree that they are quite specific though they are one of the main reasons to register key events in the first place, and using them removes the need to delegate 8 different functions for handling each type of key process.

Sgqvur, if I were to get rid of one of the two ways of registering key events I would get rid of the module way, as the module requires more overhead.

However, I have taken your advice and changed "onEvent" to "onKeyEvent".

The method "onKeyEvent" is not static, so the variable represented by "this" is the player ID. I think you didn't spot that in the documentation.
 

Sgqvur

FullOfUltimateTruthsAndEt ernalPrinciples, i.e shi
Reaction score
62
Bribe:
1. I would get rid of the module way, as the module requires more overhead.
2. The method "onKeyEvent" is not static, so the variable represented by "this" is the player ID. I think you didn't spot that in the documentation.

1. Didn't we agree on that one? Worst case scenario: 12 players intentionally tapping the keys as fast as humanly possible, what is the overhead? Pretty neglect-able.
2. Yeah but it wouldn't hurt if it was an argument as well:
method onKeyEvent takes integer p, integer key, boolean pressed returns boolean // "perfect" =).
 

Bribe

vJass errors are legion
Reaction score
67
Why should I pass the argument twice? That doesn't make sense - the ArrowKey struct itself uses a player ID for each of its methods.

And by overhead I didn't mean in terms of efficiency, I meant requiring the user to only be able to use the event from within structs that implement the module.
 

Sgqvur

FullOfUltimateTruthsAndEt ernalPrinciples, i.e shi
Reaction score
62
Bribe:
1. I meant requiring the user to only be able to use the event from within structs that implement the module.
2. Why should I pass the argument twice? That doesn't make sense - the ArrowKey struct itself uses a player ID for each of its methods.

1. Well I wouldn't consider this an overhead but really a motivation for a better practice.
2. It seams to me that way is more user-friendly.

JASS:
method onKeyEvent takes integer key, boolean press returns boolean
    call MoveHero(hero[this])

    return false

or

    local integer p = this
    call MoveHero(hero[p])

    return false
endmethod

vs 

method onKeyEvent takes player p, integer key, boolean press returns boolean
    call MoveHero(hero[p])

    return false
endmethod
 

Bribe

vJass errors are legion
Reaction score
67
First of all you can't use a player as an array value, hence why I use the player ID instead. In fact you'd pretty much never need to get the player handle.

The module also creates an integer array and some extra lines of code, because that is needed to fully delegate the ArrowKey struct. I don't want to have to dump that requirement on users.
 

Sgqvur

FullOfUltimateTruthsAndEt ernalPrinciples, i.e shi
Reaction score
62
Bribe:
1. First of all you can't use a player as an array value

2. The module also creates an integer array and some extra lines of code, because that is needed to fully delegate the ArrowKey struct. I don't want to have to dump that requirement on users.

1. A silly mistake that the pjass would've cought =). But you get my point.

2. Well simple make the module do all the work and get rid of the other things, so that the user should only write the onKeyEvent method.
I was thinking of a library that exposes a oneshot module that can be registered only one onKeyEvent equavalent method, so that the user would be forced to centralize his arrow key movement things in to a single struct! But that's just me again. =)
Also even more user-friendly would be to use a function interface so that the user could name his method anything he wants not just onKeyEvent or whatever.
 

Bribe

vJass errors are legion
Reaction score
67
I don't agree with any of those suggestions. If the user just wants the event, without the struct delegation, he/she can use ArrowKey.registerEvent(function WhateverNameYouWant) which also solves any distaste for the onKeyEvent naming convention.

Having a player as an argument is pointless. Why would anyone want the player handle value from a response like that? The user could call GetTriggerPlayer() if he/she wants it.
 

Sgqvur

FullOfUltimateTruthsAndEt ernalPrinciples, i.e shi
Reaction score
62
Bribe:
1. Having a player as an argument is pointless. Why would anyone want the player handle value from a response like that? The user could call GetTriggerPlayer() if he/she wants it.

2. I don't agree with any of those suggestions. If the user just wants the event, without the struct delegation, he/she can use ArrowKey.registerEvent(function WhateverNameYouWant) which also solves any distaste for the onKeyEvent naming convention.

1. Uf..
JASS:
method onKeyEvent takes integer p, integer key, boolean press returns boolean
    call MoveHero(hero[p])

    return false
endmethod


Happy now?

2. I meant: force the user to use a struct with a oneshot module that allows a registration of an arrow handler method that the user picks the name.
But if the user decides to implement the module to another struct and tries to register another arrow handler method it wouldn't work, hence the name oneshot module =).
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Staff online

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top