| 11-10-2009, 09:51 AM | #1 | |
Introduction KeyAction is written in vJass and requires the NewGen editor, or Jass Helper in some other configuration. KeyAction requires the latest version of Jass Helper.Credits
The KeyAction Library Requirements: KeyAction:library KeyAction initializer OnInit //============================================================================== // KeyAction v0.1.0 //============================================================================== // Credits: //------------------------------------------------------------------------------ // Written By: // Earth-Fury //------------------------------------------------------------------------------ // I don't care if you credit me or not if you use this. However, you must not // misrepresent the source of this library in any way. This includes, but is not // limited to claiming yourself to be the sole contributor to a map which uses // this library. // //============================================================================== // Introduction: //------------------------------------------------------------------------------ // KeyAction is a library that provides a nicer, simpler API for dealing with // arrow key events. There is a speed penalty associated with using this // library as opposed to the native trigger events. However, this speed loss is // tiny, and only has an effect when a user actively presses or releases any of // the arrow keys. // // Note that arrow keys have a noticeable delay in single player, and rather // large delays in multiplayer. This is the fault of the game, not a fault in // this library. // // For the sake of simplicity and sanity, it is only possible to register events // for any arrow key click. If you are only interested in some combination of // keys and pressed/unpressed events, you have to filter out the undesired ones // in the callback. // //============================================================================== // API Guide //------------------------------------------------------------------------------ // This library uses integers to represent keys. The following constants are // defined by this library to make referring to keys simpler: // // KEY_DOWN = 0 // KEY_UP = 1 // KEY_LEFT = 2 // KEY_RIGHT = 3 // //------------------------------------------------------------------------------ // This library provides mechanisms for knowing if a key is currently pressed // or not, as well as knowing the general direction a pair of opposing keys is // representing. The API for both is simple function calls: // // function GetPlayerKeyState // takes player who, integer key // returns boolean // // Returns true if player "who" has the arrow key "key" currently pressed. // // function GetPlayerKeyAxisState // takes player who, integer whatAxis // returns integer // // Returns -1 for left/down, 0 for middle/none, and 1 for right/up. // The "whatAxis" parameter must be one of the two constants: // KEY_AXIS_HORIZONTAL or KEY_AXIS_VERTICAL // //------------------------------------------------------------------------------ // This library allows you to register functions that will be called when any // player presses or releases any arrow key. The functions must conform to the // KeyEventCallback function interface: // // function interface KeyEventCallback // takes player who, integer whatKey, boolean pressed // returns nothing // // The player parameter is the one who pressed/released the key. // The whatKey integer is the key that was pressed/released. See the key // constants defined above. // The boolean parameter tells you if it was a key press, or a key release that // generated the event. // // function RegisterKeyAction // takes KeyEventCallback callback // returns nothing // // Registers the given KeyEventCallback function to be called when any // player presses any arrow key. Functions can not be unregistered. // //------------------------------------------------------------------------------ // This library also provides a module which will call a predefined method on // all instances of an implementing struct. // // To implement the module in your struct: // implement OnKeyAction // // Your struct must have a method named "onKeyAction" with the same parameter // list and return type of the KeyEventCallback function interface. // // The module also adds an operator for enabling and disabling key events for // single instances of an implementing type. // NOTE!!: Currently, you _must_ enable key events on creation of your struct, // and disable them on destruction. This will become automatic in a future // version. Please, to be forwards-compatible, enable key events in your // struct's .create() method, and disable them in your struct's .destroy() or // .onDestroy() method. // // The operator is: // set myStructInst.keyActionEnabled = true // //============================================================================== // Configuration // ============================================================================= globals // The following constants determines the behaviour of vertical // and horizontal key axis states in the event two opposing // keys are pressed at once. // // If set to true, pressing two opposing keys will cause that // axis' state to be 0. If either key is released, the state of // the axis will represent the key that remains held down. // // If set to false, pressing two opposing keys will cause that // axis' sate to represent the last key pressed. If the key // being represented is released, the state of the axis will // switch to the key that is still held down. private constant boolean ZERO_ON_OPPOSITION_VERTICAL = true private constant boolean ZERO_ON_OPPOSITION_HORIZONTAL = false endglobals //============================================================================== // End of Configuration // ============================================================================= // ======================================== // Public constants // ======================================== globals constant integer KEY_DOWN = 0 constant integer KEY_UP = 1 constant integer KEY_LEFT = 2 constant integer KEY_RIGHT = 3 constant integer KEY_AXIS_HORIZONTAL = 0 constant integer KEY_AXIS_VERTICAL = 1 endglobals // ======================================== // Private variables/constants // ======================================== globals // Key event triggers: private trigger key_UP_DOWN = CreateTrigger() private trigger key_UP_UP = CreateTrigger() private trigger key_DOWN_DOWN = CreateTrigger() private trigger key_DOWN_UP = CreateTrigger() private trigger key_LEFT_DOWN = CreateTrigger() private trigger key_LEFT_UP = CreateTrigger() private trigger key_RIGHT_DOWN = CreateTrigger() private trigger key_RIGHT_UP = CreateTrigger() // Key value constants: private constant integer KEY_VALUE_UP = 1 private constant integer KEY_VALUE_DOWN = -1 private constant integer KEY_VALUE_LEFT = -1 private constant integer KEY_VALUE_RIGHT = 1 // Opposition values: private constant integer KEY_OPPOSITE_VALUE_UP = -KEY_VALUE_UP private constant integer KEY_OPPOSITE_VALUE_DOWN = -KEY_VALUE_DOWN private constant integer KEY_OPPOSITE_VALUE_LEFT = -KEY_VALUE_LEFT private constant integer KEY_OPPOSITE_VALUE_RIGHT = -KEY_VALUE_RIGHT // Opposition constants: private constant integer KEY_OPPOSITE_UP = KEY_DOWN private constant integer KEY_OPPOSITE_DOWN = KEY_UP private constant integer KEY_OPPOSITE_LEFT = KEY_RIGHT private constant integer KEY_OPPOSITE_RIGHT = KEY_LEFT // Key-to-axis constants: private constant integer KEY_AXIS_UP = KEY_AXIS_VERTICAL private constant integer KEY_AXIS_DOWN = KEY_AXIS_VERTICAL private constant integer KEY_AXIS_LEFT = KEY_AXIS_HORIZONTAL private constant integer KEY_AXIS_RIGHT = KEY_AXIS_HORIZONTAL // Key axis behaviour constants: private constant boolean KEY_AXIS_ZERO_ON_OP_UP = ZERO_ON_OPPOSITION_VERTICAL private constant boolean KEY_AXIS_ZERO_ON_OP_DOWN = ZERO_ON_OPPOSITION_VERTICAL private constant boolean KEY_AXIS_ZERO_ON_OP_LEFT = ZERO_ON_OPPOSITION_HORIZONTAL private constant boolean KEY_AXIS_ZERO_ON_OP_RIGHT = ZERO_ON_OPPOSITION_HORIZONTAL endglobals // ======================================== // Key state getters // ======================================== // Individual keys globals private boolean array playerKeyStates endglobals function GetPlayerKeyState takes player who, integer key returns boolean return playerKeyStates[GetPlayerId(who) + key * 15] endfunction // Left/Right and Up/Down globals private integer array playerKeyAxisStates endglobals function GetPlayerKeyAxisState takes player who, integer whatAxis returns integer return playerKeyAxisStates[GetPlayerId(who) + whatAxis * 15] endfunction // ======================================== // Function events // ======================================== function interface KeyEventCallback takes player who, integer whatKey, boolean pressed returns nothing globals private KeyEventCallback array callbacks private integer callbackCount = 0 endglobals function RegisterKeyAction takes KeyEventCallback callback returns nothing set callbacks[callbackCount] = callback set callbackCount = callbackCount + 1 endfunction //! textmacro KeyAction_HandleFuncEvent takes KEY, DIR, PRESSED private function HandleKeyEventFunc_$KEY$_$DIR$ takes nothing returns boolean local player p = GetTriggerPlayer() local integer pid = GetPlayerId(p) local integer i // Set individual key states set playerKeyStates[KEY_$KEY$ * 15 + pid] = $PRESSED$ // Set axis key states static if $PRESSED$ then static if KEY_AXIS_ZERO_ON_OP_$KEY$ then if playerKeyStates[pid + KEY_OPPOSITE_$KEY$ * 15] then set playerKeyAxisStates[pid + KEY_AXIS_$KEY$ * 15] = 0 else set playerKeyAxisStates[pid + KEY_AXIS_$KEY$ * 15] = KEY_VALUE_$KEY$ endif else set playerKeyAxisStates[pid + KEY_AXIS_$KEY$ * 15] = KEY_VALUE_$KEY$ endif else if playerKeyStates[pid + KEY_OPPOSITE_$KEY$ * 15] then set playerKeyAxisStates[pid + KEY_AXIS_$KEY$ * 15] = KEY_OPPOSITE_VALUE_$KEY$ else set playerKeyAxisStates[pid + KEY_AXIS_$KEY$ * 15] = 0 endif endif // Evaluate registered function callbacks set i = 0 loop exitwhen i == callbackCount call callbacks[i].evaluate(p, KEY_$KEY$, $PRESSED$) set i = i + 1 endloop return false endfunction //! endtextmacro //! runtextmacro KeyAction_HandleFuncEvent("UP", "DOWN", "true") //! runtextmacro KeyAction_HandleFuncEvent("UP", "UP", "false") //! runtextmacro KeyAction_HandleFuncEvent("DOWN", "DOWN", "true") //! runtextmacro KeyAction_HandleFuncEvent("DOWN", "UP", "false") //! runtextmacro KeyAction_HandleFuncEvent("LEFT", "DOWN", "true") //! runtextmacro KeyAction_HandleFuncEvent("LEFT", "UP", "false") //! runtextmacro KeyAction_HandleFuncEvent("RIGHT", "DOWN", "true") //! runtextmacro KeyAction_HandleFuncEvent("RIGHT", "UP", "false") // ======================================== // Module events // ======================================== module OnKeyAction private static thistype array all private static integer count = 0 private integer id = 0 method operator keyActionEnabled= takes boolean b returns nothing if b and id == 0 then set all[count] = this set id = count set count = count + 1 elseif id != 0 then set all[id] = all[count] set id = 0 set count = count - 1 endif endmethod //! textmacro KeyAction_HandleEvent takes KEY, DIR, PRESSED private static method handleKeyEvent_$KEY$_$DIR$ takes nothing returns boolean local integer i = 0 loop exitwhen i == count call all[i].onKeyAction(GetTriggerPlayer(), KEY_$KEY$, $PRESSED$) set i = i + 1 endloop return false endmethod //! endtextmacro //! runtextmacro KeyAction_HandleEvent("UP", "DOWN", "true") //! runtextmacro KeyAction_HandleEvent("UP", "UP", "false") //! runtextmacro KeyAction_HandleEvent("DOWN", "DOWN", "true") //! runtextmacro KeyAction_HandleEvent("DOWN", "UP", "false") //! runtextmacro KeyAction_HandleEvent("LEFT", "DOWN", "true") //! runtextmacro KeyAction_HandleEvent("LEFT", "UP", "false") //! runtextmacro KeyAction_HandleEvent("RIGHT", "DOWN", "true") //! runtextmacro KeyAction_HandleEvent("RIGHT", "UP", "false") private static method onInit takes nothing returns nothing //! textmacro KeyAction_AddCondition takes KEY, DIR call TriggerAddCondition(key_$KEY$_$DIR$, Condition(function thistype.handleKeyEvent_$KEY$_$DIR$)) //! endtextmacro //! runtextmacro KeyAction_AddCondition("UP", "DOWN") //! runtextmacro KeyAction_AddCondition("UP", "UP") //! runtextmacro KeyAction_AddCondition("DOWN", "DOWN") //! runtextmacro KeyAction_AddCondition("DOWN", "UP") //! runtextmacro KeyAction_AddCondition("LEFT", "DOWN") //! runtextmacro KeyAction_AddCondition("LEFT", "UP") //! runtextmacro KeyAction_AddCondition("RIGHT", "DOWN") //! runtextmacro KeyAction_AddCondition("RIGHT", "UP") endmethod endmodule // ======================================== // Initialization // ======================================== private function OnInit takes nothing returns nothing local integer i //! textmacro KeyAction_RegisterEvents takes KEY, DIR set i = 0 loop exitwhen i > 11 // The actual event call TriggerRegisterPlayerEvent(key_$KEY$_$DIR$, Player(i), EVENT_PLAYER_ARROW_$KEY$_$DIR$) // For the functions call TriggerAddCondition(key_$KEY$_$DIR$, Condition(function HandleKeyEventFunc_$KEY$_$DIR$)) set i = i + 1 endloop //! endtextmacro //! runtextmacro KeyAction_RegisterEvents("UP", "DOWN") //! runtextmacro KeyAction_RegisterEvents("UP", "UP") //! runtextmacro KeyAction_RegisterEvents("DOWN", "DOWN") //! runtextmacro KeyAction_RegisterEvents("DOWN", "UP") //! runtextmacro KeyAction_RegisterEvents("LEFT", "DOWN") //! runtextmacro KeyAction_RegisterEvents("LEFT", "UP") //! runtextmacro KeyAction_RegisterEvents("RIGHT", "DOWN") //! runtextmacro KeyAction_RegisterEvents("RIGHT", "UP") endfunction endlibrary Change Log
|
| 11-10-2009, 10:54 AM | #2 |
Yes very nice,... But I guess there is nothing that removes the delay of arrow keys or? Anyway, I see very much use in this. For an hero arrow selection system, for example. Still don't get why you use a trigger per arrow and for every arrowstate, instead sharing one and checking the gameevent. JASS:function eventHandler takes nothing returns nothing if EVENT_PLAYER_ARROW_DOWN_UP == GetTriggerEventId() then //Arrow_up_stuff endif endfunction |
| 11-10-2009, 11:16 AM | #3 | ||
Quote:
Using a program like DelayReducer to reduce Battle.net lag will help on battle.net. There is still, however, an intrinsic delay in arrow keys registering events for some unknown reason. Quote:
|
| 11-10-2009, 11:26 AM | #4 | |
Quote:
But it doesn't matter at all. Its good, and since I have my own bnet I don't have any delay. |
| 11-10-2009, 01:09 PM | #5 |
Hey, nice. Made this a while ago: JASS:library ArrowkeyController initializer Init globals // A problem with the arrow keys are, that they always have some delay. // Normally it's 0.25 (it's a constant) private constant real ARROW_KEY_REACTION_TIME = 0.25 // The bigger the smoother the 'back camera movement' but the less // the accurarry private constant real CAMERA_SMOOTHING = 1.00 endglobals globals private trigger onArrowKey = CreateTrigger() private trigger releaseArrowKey = CreateTrigger() private boolean array Allowed private boolean array IsLocked private real array CamX private real array CamY endglobals private function unlockCamera takes nothing returns nothing local integer ID = GetPlayerId(GetTriggerPlayer()) local real x local real y if Allowed[ID] then if GetLocalPlayer() == GetTriggerPlayer() then call PanCameraToTimed(CamX[ID], CamY[ID], 0.5) endif set IsLocked[ID] = false endif endfunction private function lockCamera takes nothing returns nothing local integer ID = GetPlayerId(GetTriggerPlayer()) local real x local real y if Allowed[ID] then set IsLocked[ID] = true if GetLocalPlayer() == GetTriggerPlayer() then call PanCameraToTimed(CamX[ID], CamY[ID], ARROW_KEY_REACTION_TIME*CAMERA_SMOOTHING) endif endif endfunction private function SaveCamCoords takes nothing returns nothing local integer i = 0 loop exitwhen i == 12 if IsLocked[i] == false then if GetLocalPlayer() == Player(i) then set CamX[i] = GetCameraTargetPositionX() set CamY[i] = GetCameraTargetPositionY() endif endif set i = i + 1 endloop endfunction private function Init takes nothing returns nothing local integer i = 0 loop exitwhen i == 12 set Allowed[i] = true if GetPlayerSlotState(Player(i)) == PLAYER_SLOT_STATE_PLAYING and GetPlayerController(Player(i)) == MAP_CONTROL_USER then call TriggerRegisterPlayerEvent(onArrowKey, Player(i), EVENT_PLAYER_ARROW_UP_DOWN) call TriggerRegisterPlayerEvent(onArrowKey, Player(i), EVENT_PLAYER_ARROW_DOWN_DOWN) call TriggerRegisterPlayerEvent(onArrowKey, Player(i), EVENT_PLAYER_ARROW_RIGHT_DOWN) call TriggerRegisterPlayerEvent(onArrowKey, Player(i), EVENT_PLAYER_ARROW_LEFT_DOWN) call TriggerRegisterPlayerEvent(releaseArrowKey, Player(i), EVENT_PLAYER_ARROW_UP_UP) call TriggerRegisterPlayerEvent(releaseArrowKey, Player(i), EVENT_PLAYER_ARROW_DOWN_UP) call TriggerRegisterPlayerEvent(releaseArrowKey, Player(i), EVENT_PLAYER_ARROW_RIGHT_UP) call TriggerRegisterPlayerEvent(releaseArrowKey, Player(i), EVENT_PLAYER_ARROW_LEFT_UP) endif set i = i + 1 endloop call TriggerAddAction(onArrowKey,function lockCamera) call TriggerAddAction(releaseArrowKey,function unlockCamera) call TimerStart(CreateTimer(),ARROW_KEY_REACTION_TIME,true,function SaveCamCoords) endfunction public function Allow takes boolean flag returns nothing local integer i = 0 if flag == true then call DisableTrigger(onArrowKey) call DisableTrigger(releaseArrowKey) else loop exitwhen i == 12 call PanCameraToTimed(CamX[i], CamY[i], 0.5) set i = i + 1 endloop call EnableTrigger(onArrowKey) call EnableTrigger(releaseArrowKey) endif endfunction public function AllowFor takes player p, boolean flag returns nothing set Allowed[GetPlayerId(p)] = flag endfunction endlibrary You should add sth. like that to your library. Making movement via arrow keys not possible. But I'm not sure if that fits into a library like that. Niceldy done. |
| 11-10-2009, 05:34 PM | #6 | |
Quote:
That's the kind of thing that should require a library like this. It would shorten and simplify the code. |
| 11-10-2009, 06:23 PM | #7 |
| 04-16-2010, 03:56 PM | #8 |
Remind me why I would ever use this over Anitarf's ArrowKeys library in the database, again? |
| 04-19-2010, 05:43 PM | #9 | |
Quote:
I like my API better. Also the ability to register multiple callbacks directly to the library. (Limited utility, but useful on rare occasions.) I don't feel like putting work in to improving this much any time soon, so do as you see fit. Let it rot, approve it, graveyard it. (I can always resubmit it in the future) |
| 12-07-2010, 02:41 PM | #10 |
I guess I've spent enough time trying to figure out what to do with this. It is certainly coded with more rigour than ArrowKeys and should probably replace it. However, I don't see anything here that would easily replicate the "quickpress" functionality of ArrowKeys, which is a bit ugly but a very functional hack. On top of that, I suppose some people might prefer a more lightweight library so I guess the two of them can coexist in the resource section, even if that means the number of libraries handling arrow keys will approach the number of people who actually have a use for them. Approved. |
| 09-26-2011, 11:57 AM | #11 |
I found a couple of bugs in the OnKeyAction module. Original code:method operator keyActionEnabled= takes boolean b returns nothing if b and id == 0 then set all[count] = this set id = count set count = count + 1 elseif id != 0 then set all[id] = all[count] set id = 0 set count = count - 1 endif endmethod Fixed code:private integer id = -1 method operator keyActionEnabled= takes boolean b returns nothing if b and id == -1 then set all[count] = this set id = count set count = count + 1 elseif not b and id != -1 then set count = count - 1 set all[id] = all[count] set id = -1 endif endmethod Edit: there can also be trouble if struct instances are destroyed in the OnKeyAction callback. Not sure what would be the best way to avoid issues here. |
| 10-15-2014, 11:20 AM | #12 |
this bug doesnt break anything but definitely a bad bug for how easy the fix is... JASS:private function OnInit takes nothing returns nothing local integer i //! textmacro KeyAction_RegisterEvents takes KEY, DIR set i = 0 loop exitwhen i > 11 // The actual event call TriggerRegisterPlayerEvent(key_$KEY$_$DIR$, Player(i), EVENT_PLAYER_ARROW_$KEY$_$DIR$) // For the functions call TriggerAddCondition(key_$KEY$_$DIR$, Condition(function HandleKeyEventFunc_$KEY$_$DIR$)) set i = i + 1 endloop //! endtextmacro //! runtextmacro KeyAction_RegisterEvents("UP", "DOWN") //! runtextmacro KeyAction_RegisterEvents("UP", "UP") //! runtextmacro KeyAction_RegisterEvents("DOWN", "DOWN") //! runtextmacro KeyAction_RegisterEvents("DOWN", "UP") //! runtextmacro KeyAction_RegisterEvents("LEFT", "DOWN") //! runtextmacro KeyAction_RegisterEvents("LEFT", "UP") //! runtextmacro KeyAction_RegisterEvents("RIGHT", "DOWN") //! runtextmacro KeyAction_RegisterEvents("RIGHT", "UP") endfunction should be JASS:private function OnInit takes nothing returns nothing local integer i //! textmacro KeyAction_RegisterEvents takes KEY, DIR set i = 0 loop exitwhen i > 11 // The actual event call TriggerRegisterPlayerEvent(key_$KEY$_$DIR$, Player(i), EVENT_PLAYER_ARROW_$KEY$_$DIR$) set i = i + 1 endloop // For the functions call TriggerAddCondition(key_$KEY$_$DIR$, Condition(function HandleKeyEventFunc_$KEY$_$DIR$)) //! endtextmacro //! runtextmacro KeyAction_RegisterEvents("UP", "DOWN") //! runtextmacro KeyAction_RegisterEvents("UP", "UP") //! runtextmacro KeyAction_RegisterEvents("DOWN", "DOWN") //! runtextmacro KeyAction_RegisterEvents("DOWN", "UP") //! runtextmacro KeyAction_RegisterEvents("LEFT", "DOWN") //! runtextmacro KeyAction_RegisterEvents("LEFT", "UP") //! runtextmacro KeyAction_RegisterEvents("RIGHT", "DOWN") //! runtextmacro KeyAction_RegisterEvents("RIGHT", "UP") endfunction oh yeah it also means that the initial event (where it sets the key down and all that internal magic) happens 12 times, each time the exact same things being set lol also i thought id share my findings after using this. the system works great and allows easy access / general use. id definitely recommend this as a prototyping thing ("hmm... is this actually a good idea?") but if you need to react to quick key presses youll need to either make your own system or butcher out the "general" components of this one. by quickpress, i mean >1 second reaction time AND frequent key presses. you should just use this system barring that |
