SC2Mapster Forums

Development > Galaxy Scripting

Input, a script to capture key and mouse input

  • 14 posts
    #1 Mar 10, 2011 at 04:54 UTC - 0 likes
    //=====================================================
    // Input, a script to capture key and mouse input
    //
    // This script will catch and store player input
    // from the keyboard and mouse in a struct array.
    // Keys and buttons are records as bool states,
    // while mouse clicks and movement are kept as
    // int and fixed values for the UI and World
    // respectively.
    //
    // To get the state of the X key, you would use:
    // playerInput[p].key.state[c_keyX]
    // To get the point of the cursor, you could:
    // Point(playerInput[p].mouse.x,playerInput[p].mouse.y)
    //
    // The user may configure the DETECT bool
    // constants to set which triggers will be
    // created on initialization of the script.
    //=====================================================
    // CONFIGURABLE CONSTANTS
    const bool DETECT_KEYDOWN = true;
    const bool DETECT_KEYUP = true;
    const bool DETECT_BUTTONDOWN = true;
    const bool DETECT_BUTTONUP = true;
    const bool DETECT_MOVEMENT = true;
    const int INPUT_BUTTONS = 6;
    const int INPUT_KEYS = 99;
    const int INPUT_MAX_PLAYERS = c_maxPlayers;
    // STRUCTS
    struct Button { bool[INPUT_BUTTONS] state; };
    struct Key { bool[INPUT_KEYS] state; };
    struct Mouse {
        int uix;
        int uiy;
        fixed x;
        fixed y;
        fixed z;
    };
    struct Input {
        Key key;
        Button button;
        Mouse mouse;
    };
    // GLOBALS
    Input[INPUT_MAX_PLAYERS] playerInput;
    // FUNCTIONS
    bool Input_KeyOn (bool testConds, bool runActions) {
        playerInput[EventPlayer()].key.state[EventKeyPressed()] = true;
        return true;
    }
    bool Input_KeyOff (bool testConds, bool runActions) {
        playerInput[EventPlayer()].key.state[EventKeyPressed()] = false;
        return true;
    }
    bool Input_ButtonOn (bool testConds, bool runActions) {
        int p = EventPlayer();
        playerInput[p].button.state[EventMouseClickedButton()] = true;
        playerInput[p].mouse.uix = EventMouseClickedPosXUI();
        playerInput[p].mouse.uiy = EventMouseClickedPosYUI();
        playerInput[p].mouse.x = EventMouseClickedPosXWorld();
        playerInput[p].mouse.y = EventMouseClickedPosYWorld();
        playerInput[p].mouse.z = EventMouseClickedPosZWorld();
        return true;
    }
    bool Input_ButtonOff (bool testConds, bool runActions) {
        int p = EventPlayer();
        playerInput[p].button.state[EventMouseClickedButton()] = false;
        playerInput[p].mouse.uix = EventMouseClickedPosXUI();
        playerInput[p].mouse.uiy = EventMouseClickedPosYUI();
        playerInput[p].mouse.x = EventMouseClickedPosXWorld();
        playerInput[p].mouse.y = EventMouseClickedPosYWorld();
        playerInput[p].mouse.z = EventMouseClickedPosZWorld();
        return true;
    }
    bool Input_Movement (bool testConds, bool runActions) {
        int p = EventPlayer();
        playerInput[p].mouse.uix = EventMouseMovedPosXUI();
        playerInput[p].mouse.uiy = EventMouseMovedPosYUI();
        playerInput[p].mouse.x = EventMouseMovedPosXWorld();
        playerInput[p].mouse.y = EventMouseMovedPosYWorld();
        playerInput[p].mouse.z = EventMouseMovedPosZWorld();
        return true;
    }
    void Input_Init () {
        if (DETECT_KEYDOWN) {
            TriggerAddEventKeyPressed(TriggerCreate("Input_KeyOn"),c_playerAny,c_keyNone,true,0,0,0);
        }
        if (DETECT_KEYUP) {
            TriggerAddEventKeyPressed(TriggerCreate("Input_KeyOff"),c_playerAny,c_keyNone,false,0,0,0);
        }
        if (DETECT_BUTTONDOWN) {
            TriggerAddEventMouseClicked(TriggerCreate("Input_ButtonOn"),c_playerAny,c_mouseButtonNone,true);
        }
        if (DETECT_BUTTONUP) {
            TriggerAddEventMouseClicked(TriggerCreate("Input_ButtonOff"),c_playerAny,c_mouseButtonNone,false);
        }
        if (DETECT_MOVEMENT) {
            TriggerAddEventMouseMoved(TriggerCreate("Input_Movement"), c_playerAny);
        }
    }
    
    Last edited Jun 06, 2011 by JademusSreg
    #2 Mar 10, 2011 at 15:13 UTC - 0 likes

    Neat! I'll attach a map with the GUI version of this script.

    Name Size MD5
    jademus_key_capture.SC2Map 11.1 KiB 9a04acb62bb7...
    #3 Mar 10, 2011 at 16:00 UTC - 0 likes

    Any improvement compare to my 2 month old script? Which activate and deactivate itself for certain interval for every action and every active player?

    include "TriggerLibs/NativeLib"
    trigger gt_WASD_test;
    bool WASD_w_down(bool testConds, bool runActions) {
        gv_trigger_stats[EventPlayer()][0]=1;
        if (gv_c_debug == true){
            UIDisplayMessage(PlayerGroupSingle(EventPlayer()), c_messageAreaChat, StringToText("w down"));
        }
        TriggerEnable(TriggerGetCurrent(), false);
        Wait(0.1, c_timeGame);
        TriggerEnable(TriggerGetCurrent(), true);
        return true;
    }
    bool WASD_a_down(bool testConds, bool runActions) {
        gv_trigger_stats[EventPlayer()][1]=1;
        TriggerEnable(TriggerGetCurrent(), false);
        if (gv_c_debug){
            UIDisplayMessage(PlayerGroupSingle(EventPlayer()), c_messageAreaChat, StringToText("a down"));
        }
        TriggerEnable(TriggerGetCurrent(), true);
        return true;
    }
    bool WASD_s_down(bool testConds, bool runActions) {
        gv_trigger_stats[EventPlayer()][2]=1;
        if (gv_c_debug){
            UIDisplayMessage(PlayerGroupSingle(EventPlayer()), c_messageAreaChat, StringToText("s down"));
        }
        TriggerEnable(TriggerGetCurrent(), false);
        Wait(0.1, c_timeGame);
        TriggerEnable(TriggerGetCurrent(), true);
        return true;
    }
    bool WASD_d_down(bool testConds, bool runActions) {
        gv_trigger_stats[EventPlayer()][3]=1;
        if (gv_c_debug){
            UIDisplayMessage(PlayerGroupSingle(EventPlayer()), c_messageAreaChat, StringToText("d down"));
        }
        TriggerEnable(TriggerGetCurrent(), false);
        Wait(0.1, c_timeGame);
        TriggerEnable(TriggerGetCurrent(), true);
        return true;
    }
    bool WASD_jump_down(bool testConds, bool runActions) {
        gv_trigger_stats[EventPlayer()][4]=1;
        TriggerEnable(TriggerGetCurrent(), false);
        gf_WASD_jump(EventPlayer());
        Wait(gv_c_jump_period, c_timeGame);
        TriggerEnable(TriggerGetCurrent(), true);
        gv_trigger_stats[EventPlayer()][4]=0;
        return true;
    }
    bool WASD_shoot_down(bool testConds, bool runActions) {
        gv_trigger_stats[EventPlayer()][5]=1;
        TriggerEnable(TriggerGetCurrent(), false);
        
        while ((gv_trigger_stats[EventPlayer()][5] == 1)) {
            gf_WASD_shoot(EventPlayer());
            Wait(0.8, c_timeGame);
        }
        TriggerEnable(TriggerGetCurrent(), true);
        return true;
    }
    bool WASD_shoot2_down(bool testConds, bool runActions) {
        gv_trigger_stats[EventPlayer()][6]=1;
        TriggerEnable(TriggerGetCurrent(), false);
        gf_WASD_shoot2(EventPlayer());
        Wait(15, c_timeGame);
        TriggerEnable(TriggerGetCurrent(), true);
        return true;
    }
    bool WASD_w_up(bool testConds, bool runActions) {
        gv_trigger_stats[EventPlayer()][0]=0;
        if (gv_c_debug){
            UIDisplayMessage(PlayerGroupSingle(EventPlayer()), c_messageAreaChat, StringToText("w up"));
        }
        TriggerEnable(TriggerGetCurrent(), false);
        Wait(0.1, c_timeGame);
        TriggerEnable(TriggerGetCurrent(), true);
        return true;
    }
    bool WASD_a_up(bool testConds, bool runActions) {
        gv_trigger_stats[EventPlayer()][1]=0;
        if (gv_c_debug){
        UIDisplayMessage(PlayerGroupSingle(EventPlayer()), c_messageAreaChat, StringToText("a up"));
        }
        TriggerEnable(TriggerGetCurrent(), false);
        Wait(0.1, c_timeGame);
        TriggerEnable(TriggerGetCurrent(), true);
        return true;
    }
    bool WASD_s_up(bool testConds, bool runActions) {
        gv_trigger_stats[EventPlayer()][2]=0;
        if (gv_c_debug){
            UIDisplayMessage(PlayerGroupSingle(EventPlayer()), c_messageAreaChat, StringToText("s up"));
        }
        TriggerEnable(TriggerGetCurrent(), false);
        Wait(0.1, c_timeGame);
        TriggerEnable(TriggerGetCurrent(), true);
        return true;
    }
    bool WASD_d_up(bool testConds, bool runActions) {
        gv_trigger_stats[EventPlayer()][3]=0;
        if (gv_c_debug){
            UIDisplayMessage(PlayerGroupSingle(EventPlayer()), c_messageAreaChat, StringToText("d up"));
        }
        TriggerEnable(TriggerGetCurrent(), false);
        Wait(0.1, c_timeGame);
        TriggerEnable(TriggerGetCurrent(), true);
        return true;
    }
    bool WASD_jump_up(bool testConds, bool runActions) {
        gv_trigger_stats[EventPlayer()][4]=0;
        TriggerEnable(TriggerGetCurrent(), false);
        gf_WASD_jump(EventPlayer());
        Wait(2.0, c_timeGame);
        TriggerEnable(TriggerGetCurrent(), true);
        return true;
    }
    bool WASD_shoot_up(bool testConds, bool runActions) {
        gv_trigger_stats[EventPlayer()][5]=0;
        TriggerEnable(TriggerGetCurrent(), false);
        Wait(0.1,c_timeGame);
        TriggerEnable(TriggerGetCurrent(), true);
        return true;
    }
    bool WASD_shoot2_up(bool testConds, bool runActions) {
        gv_trigger_stats[EventPlayer()][6]=0;
        TriggerEnable(TriggerGetCurrent(), false);
        
        return true;
    }
    bool WASD_test_Func (bool testConds, bool runActions) {
        // Variable Declarations
        int lv_player;
        // Variable Initialization
        lv_player = 0;
        return true;
    }
    void WASD_InitTriggers () {
        playergroup lv_pg_active = PlayerGroupActive();
        int lv_p = 1;
        int lv_player ;
        while ( lv_p <= PlayerGroupCount(lv_pg_active)){
            lv_player = PlayerGroupPlayer(lv_pg_active, lv_p);
            gv_trigger_array [lv_player][0]= TriggerCreate("WASD_w_down");
            TriggerAddEventKeyPressed(gv_trigger_array [lv_player][0], lv_player, c_keyW, true, c_keyModifierStateIgnore, c_keyModifierStateIgnore, c_keyModifierStateIgnore);
            gv_trigger_array [lv_player][1]= TriggerCreate("WASD_w_up");
            TriggerAddEventKeyPressed(gv_trigger_array [lv_player][1], lv_player, c_keyW, false, c_keyModifierStateIgnore, c_keyModifierStateIgnore, c_keyModifierStateIgnore);
            //TriggerEnable(gv_trigger_array [lv_player][1], false);
            gv_trigger_array [lv_player][2]= TriggerCreate("WASD_a_down");
            TriggerAddEventKeyPressed(gv_trigger_array [lv_player][2], lv_player, c_keyA, true, c_keyModifierStateIgnore, c_keyModifierStateIgnore, c_keyModifierStateIgnore);
            gv_trigger_array [lv_player][3]= TriggerCreate("WASD_a_up");
            TriggerAddEventKeyPressed(gv_trigger_array [lv_player][3], lv_player, c_keyA, false, c_keyModifierStateIgnore, c_keyModifierStateIgnore, c_keyModifierStateIgnore);
            //TriggerEnable(gv_trigger_array [lv_player][3], false);
            gv_trigger_array [lv_player][4]= TriggerCreate("WASD_s_down");
            TriggerAddEventKeyPressed(gv_trigger_array [lv_player][4], lv_player, c_keyS, true, c_keyModifierStateIgnore, c_keyModifierStateIgnore, c_keyModifierStateIgnore);
            gv_trigger_array [lv_player][5]= TriggerCreate("WASD_s_up");
            TriggerAddEventKeyPressed(gv_trigger_array [lv_player][5], lv_player, c_keyS, false, c_keyModifierStateIgnore, c_keyModifierStateIgnore, c_keyModifierStateIgnore);
            //TriggerEnable(gv_trigger_array [lv_player][5], false);
            gv_trigger_array [lv_player][6]= TriggerCreate("WASD_d_down");
            TriggerAddEventKeyPressed(gv_trigger_array [lv_player][6], lv_player, c_keyD, true, c_keyModifierStateIgnore, c_keyModifierStateIgnore, c_keyModifierStateIgnore);
            gv_trigger_array [lv_player][7]= TriggerCreate("WASD_d_up");
            TriggerAddEventKeyPressed(gv_trigger_array [lv_player][7], lv_player, c_keyD, false, c_keyModifierStateIgnore, c_keyModifierStateIgnore, c_keyModifierStateIgnore);
            //TriggerEnable(gv_trigger_array [lv_player][7], false);
            gv_trigger_array [lv_player][8]= TriggerCreate("WASD_jump_down");
            TriggerAddEventKeyPressed(gv_trigger_array [lv_player][8], lv_player, c_keySpace, true, c_keyModifierStateIgnore, c_keyModifierStateIgnore, c_keyModifierStateIgnore);
            //gv_trigger_array [lv_player][9]= TriggerCreate("WASD_jump_up");
            //TriggerAddEventKeyPressed(gv_trigger_array [lv_player][9], lv_player, c_keySpace, false, c_keyModifierStateIgnore, c_keyModifierStateIgnore, c_keyModifierStateIgnore);
            //TriggerEnable(gv_trigger_array [lv_player][9], false);
            gv_trigger_array [lv_player][10]= TriggerCreate("WASD_shoot_down");
            TriggerAddEventMouseClicked(gv_trigger_array [lv_player][10], lv_player, c_mouseButtonLeft, true);
            gv_trigger_array [lv_player][11]= TriggerCreate("WASD_shoot_up");
            TriggerAddEventMouseClicked(gv_trigger_array [lv_player][11], lv_player, c_mouseButtonLeft, false);
            //TriggerEnable(gv_trigger_array [lv_player][11], false);
            gv_trigger_array [lv_player][12]= TriggerCreate("WASD_shoot2_down");
            TriggerAddEventMouseClicked(gv_trigger_array [lv_player][12], lv_player, c_mouseButtonRight, true);
            gv_trigger_array [lv_player][13]= TriggerCreate("WASD_shoot2_up");
            TriggerAddEventMouseClicked(gv_trigger_array [lv_player][13], lv_player, c_mouseButtonRight, false);
            TriggerEnable(gv_trigger_array [lv_player][13], false);
            lv_p+=1;
        }
    }
    
    Last edited Mar 10, 2011 by avogatro
    #4 Mar 10, 2011 at 17:16 UTC - 0 likes

    In my experience, even disabled triggers to detect player key, button, and movement input generate network traffic. This is why using as few triggers as possible is preferable, as it is the only thing approximating a throttle we have on this issue.

    Since the engine cannot perform simultaneous parallel processing of its threads, using multiple triggers when they will just fire in sequence anyway is needless overhead.

    My script is intended to capture all player input from keys, mouse buttons, and mouse movement, without any specific implementation. Call me a romantic, but I have a soft spot for modularity. I'd like people to be able to do whatever they want with it, stopping just long enough to configure the constants that regulate the creation triggers, in case they don't need to detect key up or mouse movement.

    So it's not an improvement, per se, but rather a script with some similar features but a different and more general purpose, which may also happen to be more conservative toward net traffic.

    The only (minor) issue one may have with my script is the overhead cost of the variable memory which, assuming a 32-bit bool implementation, roughly exceeds 6 kb.

    #5 Mar 10, 2011 at 21:20 UTC - 0 likes

    @JademusSreg: Go

    While I have a few issues with Jademus' script (consts, man, consts! 6, 16, 99, magic numbers make me sad), I'm inclined to agree with him in regards to the disabled triggers part.

    CortexRP.com Creator

    #6 Mar 10, 2011 at 22:13 UTC - 0 likes

    My attempts at using constants to define array sizes have been met with syntax errors in the past. Also, they are not magic, so much as they should correspond to the highest constant ints for the relevant input types; keys go up to c_keyF12 = 98, for example. I have no other option there. It becomes the user's responsibility to modify them according to its needs.

    EDIT: I tried using constants again and was pleasantly surprised. Script updated. Thanks Motive. =D

    Last edited Mar 10, 2011 by JademusSreg
    #7 Mar 10, 2011 at 22:18 UTC - 0 likes

    Yea, Jademus' script is much cleaner than yours, avo :)

    Though a const int for the player number would be appreciated:

    const int MAX_PLAYERS = 16;
    
    ...
    
    Input[MAX_PLAYERS] playerInput;
    
    #8 Mar 10, 2011 at 22:35 UTC - 0 likes

    Yes. =)

    #9 Mar 11, 2011 at 02:07 UTC - 0 likes
    • Beautiful is better than ugly.
    • Explicit is better than implicit.
    • Simple is better than complex.
    • Complex is better than complicated.
    • Flat is better than nested.
    • Sparse is better than dense.
    • Readability counts.
    • Special cases aren't special enough to break the rules.
      • Although practicality beats purity.
    • Errors should never pass silently.
      • Unless explicitly silenced.
    • In the face of ambiguity, refuse the temptation to guess.
    • There should be one and preferably only oneobvious way to do it.
      • Although that way may not be obvious at first unless you're Dutch.
    • Now is better than never.
      • Although never is often better than right now.
    • If the implementation is hard to explain, it's a bad idea.
    • If the implementation is easy to explain, it may be a good idea.
    • NameSpaces are one honking great idea. let's do more of those!

    I don't know, if yours faster or not. But most other author use your logic, and their maps are not very fast.

    I think, my system generate less traffic, but it is hard to proof.

    We need a test with a lot of player. 2 system but same map.

    Last edited Mar 11, 2011 by avogatro
    #10 Mar 11, 2011 at 05:30 UTC - 0 likes

    My script almost certainly generates less network traffic, a total of 5 input catching triggers versus 14n where n is the number of active players, but it would be positively unscientific for one to be averse to testing such things. Be certain to implement each system in separate maps when conducting the tests, since toggling between the two in the same map via the disabling of triggers is not effective at throttling the network traffic generated by the input detection triggers, in my experience.

    Also, your demonstrable wealth of platitudes has awed and humbled me.

    #11 Mar 11, 2011 at 16:56 UTC - 0 likes

    Event's will probably be queued and sent in packages from one player to another.
    Then they'll arrive at the other player who can then use them like the events have been generated on his local pc.
    Thus it'll probably not matter very much in terms of net traffic.

    Two triggers which both use the same trigger event don't necessarily cause a game to transfere the same event twice.

    See my awesome drawing skills for what I mean:

    http://simplest-image-hosting.net/png-0-unbenannt63

    @avogatro: Go

    I like your creed there. Filled with truth :)

    Last edited Mar 11, 2011 by s3rius
    #12 Mar 12, 2011 at 05:53 UTC - 0 likes

    @s3rius: Go

    Only now do i realize i can use constants to declare array sizes.. it doesnt work if its not a constant right? Are there any cases in which this does not work?

    Works
    AoS2
    Coder
    Boss Bars+
    Custom boss bars
    GXML
    Dialog XML parser
    Project W
    Coder
    RandNAligning dialog labels

    #13 Mar 12, 2011 at 11:21 UTC - 0 likes

    @FuzzYD: Go

    I guess it only does not work if you declare the constant after the array. But that's normal.

    Generally this always works. It needs to be a constant though, yes.

    Last edited Mar 12, 2011 by s3rius
    #14 Jun 06, 2011 at 20:58 UTC - 0 likes

    Trivial update; MAX_PLAYERS updated to INPUT_MAX_PLAYERS = c_maxPlayers to avoid namespace conflicts.

  • 14 posts

You must login to post a comment. Don't have an account? Register to get one!