1. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  2. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still havn't received your rank award? Then please contact the administration.
    Dismiss Notice
  3. The 18th Icon Contest is ON! Choose any ingame unit and give him/her Hero abilities. Good luck to all.
    Dismiss Notice
  4. The Secrets of Warcraft 3 have revealed interesting works. The RESULTS for Abelhawk's Mini-Mapping Contest #15 have come out!
    Dismiss Notice
  5. Contestants are to create a scene set in the Stone Age. Come and see what you can come up with. We wish you the best of luck!
    Dismiss Notice
  6. Colour outside the lines! Techtree Contest #13 is a go. The contest is optionally paired.
    Dismiss Notice
  7. Night Rider gained several songs for his journey. The poll for the 12th Music Contest has started. Check it out!
    Dismiss Notice
  8. Greetings cerebrates, our Swarm needs new spawners that will have numerous children. Join the HIVE's 31st Modeling Contest - Spawners and Spawned! The contest is optionally paired.
    Dismiss Notice
  9. Join other hivers in a friendly concept-art contest. The contestants have to create a genie coming out of its container. We wish you the best of luck!
    Dismiss Notice
  10. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

[vJASS] [Snippet] Mouse Utility

Discussion in 'JASS Resources' started by MyPad, Nov 28, 2018.

  1. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,270
    Resources:
    6
    Models:
    1
    Icons:
    1
    Spells:
    3
    JASS:
    1
    Resources:
    6
    This is a snippet that makes mouse coordinate detection easier for the user. It allows the user to determine the mouse's map coordinates in runtime without having to ever depend on events that the user would usually have to listen to. This also has the added bonus of returning an individual instance.

    Code (vJASS):

    library MouseUtils
    /*
        -------------------
            MouseUtils
             - MyPad
             
             1.0.2.2
        -------------------
       
        ----------------------------------------------------------------------------
            A simple snippet that allows one to
            conveniently use the mouse natives
            as they were meant to be...
           
         -------------------
        |    API            |
         -------------------
       
            struct UserMouse extends array
                static method operator [] (player p) -> thistype
                    - Returns the player's id + 1
                   
                static method getCurEventType() -> integer
                    - Returns the custom event that got executed.
                   
                method operator player -> player
                    - Returns Player(this - 1)
                   
                readonly real mouseX
                readonly real mouseY
                    - Returns the current mouse coordinates.
                   
                readonly method operator isMouseClicked -> boolean
                    - Determines whether any mouse key has been clicked,
                      and will return true on the first mouse key.
                     
                method isMouseButtonClicked(mousebuttontype mouseButton)
                    - Returns true if the mouse button hasn't been
                      released yet.
                method setMousePos(real x, y) (introduced in 1.0.2.2)
                    - Sets the mouse position for a given player.
                     
                static method registerCode(code c, integer ev) -> triggercondition
                    - Lets code run upon the execution of a certain event.
                    - Returns a triggercondition that can be removed later.
                   
                static method unregisterCallback(triggercondition trgHndl, integer ev)
                    - Removes a generated triggercondition from the trigger.
                   
            functions:
                GetPlayerMouseX(player p) -> real
                GetPlayerMouseY(player p) -> real
                    - Returns the coordinates of the mouse of the player.
                   
                OnMouseEvent(code func, integer eventId) -> triggercondition
                    - See UserMouse.registerCode
                   
                GetMouseEventType() -> integer
                    - See UserMouse.getCurEventType
                   
                UnregisterMouseCallback(triggercondition t, integer eventId)
                    - See UserMouse.unregisterCallback
                SetUserMousePos(player p, real x, real y)
                    - See UserMouse.setMousePos
       
      Unique Global Constants:
       IMPL_LOCK (Introduced in v.1.0.2.2)
        - Enables or disables the lock option
         -------------------
        |    Credits        |
         -------------------
         
            -   Pyrogasm for pointing out a comparison logic flaw
                in operator isMouseClicked.
               
            -   Illidan(Evil)X for the useful enum handles that
                grant more functionality to this snippet.
           
            -   TriggerHappy for the suggestion to include
                associated events and callbacks to this snippet.
               
            -   Quilnez for pointing out a bug related to the
                method isMouseButtonClicked not working as intended
                in certain situations.
               
        ----------------------------------------------------------------------------
    */

    //  Arbitrary constants
    globals
        constant integer EVENT_MOUSE_UP     = 1024
        constant integer EVENT_MOUSE_DOWN   = 2048
     constant integer EVENT_MOUSE_MOVE   = 3072
     
     private constant boolean IMPL_LOCK = false
    endglobals
    private module Init
        private static method onInit takes nothing returns nothing
            call thistype.init()
        endmethod
    endmodule
    struct UserMouse extends array
     static if IMPL_LOCK then
      //  Determines the minimum interval that a mouse move event detector
      //  will be deactivated. (Globally-based)
      //  You can configure it to any amount you like.
      private static constant real INTERVAL           = 0.031250000
     
      //  Determines how many times a mouse move event detector can fire
      //  before being deactivated. (locally-based)
      //  You can configure this to any integer value. (Preferably positive)
      private static constant integer MOUSE_COUNT_MAX  = 16
     
      // Determines the amount to be deducted from mouseEventCount
      // per INTERVAL. Runs independently of resetTimer
      private static constant integer MOUSE_COUNT_LOSS = 8
      private static constant boolean IS_INSTANT   = INTERVAL <= 0.
     endif
        private static integer currentEventType         = 0
        private static integer updateCount               = 0
        private static trigger stateDetector             = null
     static if IMPL_LOCK and not IS_INSTANT then
      private static timer resetTimer                 = null
     endif
        private static trigger array evTrigger
       
        private static integer array mouseButtonStack
     
     static if IMPL_LOCK and not IS_INSTANT then
      private integer  mouseEventCount
      private timer mouseEventReductor
     endif
        private thistype next
        private thistype prev
       
        private thistype resetNext
        private thistype resetPrev
        private trigger posDetector
     private integer mouseClickCount
       
        readonly real mouseX
        readonly real mouseY
       
        //  Converts the enum type mousebuttontype into an integer
        private static method toIndex takes mousebuttontype mouseButton returns integer
            return GetHandleId(mouseButton)
        endmethod
       
        static method getCurEventType takes nothing returns integer
            return currentEventType
        endmethod
       
        static method operator [] takes player p returns thistype
            if thistype(GetPlayerId(p) + 1).posDetector != null then
                return GetPlayerId(p) + 1
            endif
            return 0
        endmethod
           
        method operator player takes nothing returns player
            return Player(this - 1)
        endmethod
        method operator isMouseClicked takes nothing returns boolean
            return .mouseClickCount > 0
        endmethod
        method isMouseButtonClicked takes mousebuttontype mouseButton returns boolean
            return UserMouse.mouseButtonStack[(this - 1)*3 + UserMouse.toIndex(mouseButton)] > 0
     endmethod
        method setMousePos takes integer x, integer y returns nothing
            if GetLocalPlayer() == this.player then
                call BlzSetMousePos(x, y)
            endif
        endmethod
     static if IMPL_LOCK then
     private static method getMouseEventReductor takes timer t returns thistype
      local thistype this = thistype(0).next
      loop
       exitwhen this.mouseEventReductor == t or this == 0
       set this = this.next
      endloop
      return this
     endmethod
        private static method onMouseUpdateListener takes nothing returns nothing
            local thistype this = thistype(0).resetNext
            set updateCount     = 0
           
            loop
                exitwhen this == 0
                set updateCount = updateCount + 1
                           
                set this.mouseEventCount = 0
                call EnableTrigger(this.posDetector)
               
                set this.resetNext.resetPrev = this.resetPrev
                set this.resetPrev.resetNext = this.resetNext
               
                set this                     = this.resetNext
      endloop
     
      if updateCount > 0 then
       static if not IS_INSTANT then
        call TimerStart(resetTimer, INTERVAL, false, function thistype.onMouseUpdateListener)
       else
        call onMouseUpdateListener()
       endif
      else
       static if not IS_INSTANT then
        call TimerStart(resetTimer, 0.00, false, null)
        call PauseTimer(resetTimer)
       endif
            endif
     endmethod
     
     private static method onMouseReductListener takes nothing returns nothing
      local thistype this  = getMouseEventReductor(GetExpiredTimer())
      if this.mouseEventCount <= 0 then
       call PauseTimer(this.mouseEventReductor)
      else
       set this.mouseEventCount = IMaxBJ(this.mouseEventCount - MOUSE_COUNT_LOSS, 0)
       call TimerStart(this.mouseEventReductor, INTERVAL, false, function thistype.onMouseReductListener)
      endif
     endmethod
     endif
        private static method onMouseUpOrDown takes nothing returns nothing
            local thistype this = thistype[GetTriggerPlayer()]
            local integer index = (this - 1)*3 + UserMouse.toIndex(BlzGetTriggerPlayerMouseButton())
            local boolean releaseFlag   = false
           
            if GetTriggerEventId() == EVENT_PLAYER_MOUSE_DOWN then
                set this.mouseClickCount    = IMinBJ(this.mouseClickCount + 1, 3)
                set releaseFlag          = UserMouse.mouseButtonStack[index] <= 0
                set UserMouse.mouseButtonStack[index]  = IMinBJ(UserMouse.mouseButtonStack[index] + 1, 1)
               
                if releaseFlag then
                    set currentEventType = EVENT_MOUSE_DOWN
                    call TriggerEvaluate(evTrigger[EVENT_MOUSE_DOWN])
                endif
            else
                set this.mouseClickCount = IMaxBJ(this.mouseClickCount - 1, 0)
                set releaseFlag          = UserMouse.mouseButtonStack[index] > 0
                set UserMouse.mouseButtonStack[index]  = IMaxBJ(UserMouse.mouseButtonStack[index] - 1, 0)
               
                if releaseFlag then
                    set currentEventType = EVENT_MOUSE_UP
                    call TriggerEvaluate(evTrigger[EVENT_MOUSE_UP])
                endif
            endif
        endmethod
       
        private static method onMouseMove takes nothing returns nothing
      local thistype this   = thistype[GetTriggerPlayer()]
      local boolean started  = false
                   
            set this.mouseX      = BlzGetTriggerPlayerMouseX()
            set this.mouseY      = BlzGetTriggerPlayerMouseY()
     
      static if IMPL_LOCK then
       set this.mouseEventCount  = this.mouseEventCount + 1
       if this.mouseEventCount <= 1 then
        call TimerStart(this.mouseEventReductor, INTERVAL, false, function thistype.onMouseReductListener)
       endif
      endif
      set currentEventType   = EVENT_MOUSE_MOVE
      call TriggerEvaluate(evTrigger[EVENT_MOUSE_MOVE])  
      static if IMPL_LOCK then
       if this.mouseEventCount >= thistype.MOUSE_COUNT_MAX then
        call DisableTrigger(this.posDetector)                  
       
        if thistype(0).resetNext == 0 then
         static if not IS_INSTANT then
          call TimerStart(resetTimer, INTERVAL, false, function thistype.onMouseUpdateListener)
          // Mouse event reductor should be paused
         else
          set started  = true
         endif
         call PauseTimer(this.mouseEventReductor)
        endif
       
        set this.resetNext              = 0
        set this.resetPrev              = this.resetNext.resetPrev
        set this.resetPrev.resetNext    = this
        set this.resetNext.resetPrev    = this  
         
        if started then
         call onMouseUpdateListener()
        endif
       endif
      endif
        endmethod
           
        private static method init takes nothing returns nothing
            local thistype this = 1
            local player p      = this.player
     
      static if IMPL_LOCK and not IS_INSTANT then
       set resetTimer  = CreateTimer()
      endif
            set stateDetector   = CreateTrigger()
           
            set evTrigger[EVENT_MOUSE_UP]   = CreateTrigger()
            set evTrigger[EVENT_MOUSE_DOWN] = CreateTrigger()
            set evTrigger[EVENT_MOUSE_MOVE] = CreateTrigger()
           
            call TriggerAddCondition( stateDetector, Condition(function thistype.onMouseUpOrDown))
            loop
                exitwhen integer(this) > bj_MAX_PLAYER_SLOTS
               
                if GetPlayerController(p) == MAP_CONTROL_USER and GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING then
                    set this.next             = 0
                    set this.prev             = thistype(0).prev
                    set thistype(0).prev.next = this
                    set thistype(0).prev      = this
                   
                    set this.posDetector         = CreateTrigger()
                    static if IMPL_LOCK and not IS_INSTANT then
                        set this.mouseEventReductor  = CreateTimer()
                    endif
                    call TriggerRegisterPlayerEvent( this.posDetector, p, EVENT_PLAYER_MOUSE_MOVE )
                    call TriggerAddCondition( this.posDetector, Condition(function thistype.onMouseMove))                
                   
                    call TriggerRegisterPlayerEvent( stateDetector, p, EVENT_PLAYER_MOUSE_UP )
                    call TriggerRegisterPlayerEvent( stateDetector, p, EVENT_PLAYER_MOUSE_DOWN )
                endif
               
                set this = this + 1
                set p    = this.player
            endloop
        endmethod
       
        static method registerCode takes code handlerFunc, integer eventId returns triggercondition
            return TriggerAddCondition(evTrigger[eventId], Condition(handlerFunc))
        endmethod
       
        static method unregisterCallback takes triggercondition whichHandler, integer eventId returns nothing
            call TriggerRemoveCondition(evTrigger[eventId], whichHandler)
        endmethod
       
        implement Init
    endstruct
    function GetPlayerMouseX takes player p returns real
        return UserMouse[p].mouseX
    endfunction
    function GetPlayerMouseY takes player p returns real
        return UserMouse[p].mouseY
    endfunction
    function OnMouseEvent takes code func, integer eventId returns triggercondition
        return UserMouse.registerCode(func, eventId)
    endfunction
    function GetMouseEventType takes nothing returns integer
        return UserMouse.getCurEventType()
    endfunction
    function UnregisterMouseCallback takes triggercondition whichHandler, integer eventId returns nothing
        call UserMouse.unregisterCallback(whichHandler, eventId)
    endfunction
    function SetUserMousePos takes player p, integer x, integer y returns nothing
        call UserMouse[p].setMousePos(x, y)
    endfunction
    endlibrary
     
     
    Last edited: Jul 20, 2019
  2. Pyrogasm

    Pyrogasm

    Joined:
    Feb 27, 2007
    Messages:
    2,664
    Resources:
    1
    Spells:
    1
    Resources:
    1
    Cool snippet. There are some choices I don't understand, though:
    • Is the mouse move event really that inefficient that even with a relatively small amount of code to run (onMouseMove) you want to disable it after 4 move events in rapid succession? Why did you feel this was necessary? Why is 4 events the number? Why resume all instances simultaneously with a global timer? Why is the lockout 0.03? I'd just like to know more about this in general.

    • You're using the 0th instance as kind of a 'data holding' instance, right? I bumbled through the parts that use resetNext/Prev and eventually found this to be really confusing:
      Code (vJASS):
      set this.resetNext              = 0
      set this.resetPrev              = this.resetNext.resetPrev
      set this.resetPrev.resetNext    = this
      set this.resetNext.resetPrev    = this

      //would be clearer as
      set this.resetNext              = thistype(0) //I thought initially this 0 was a 'nothing' 0 not a 'zeroth instance', which made the next line seem useless
      set this.resetPrev              = thistype(0).resetPrev
      set thistype(0).resetNext       = this
      set thistype(0).resetPrev       = this


    • Your mouse click counting logic seems backward based on the output of isMouseClicked. And for that matter what exactly is that method supposed to tell you? It seems to me it would return true when a player is holding down the button but has not released it to 'finish' the click.
      Code (vJASS):
      if GetTriggerEventId() == EVENT_PLAYER_MOUSE_UP then
           set this.mouseClickCount = this.mouseClickCount + 1  //goes up by 1 when button is released
      else
           set this.mouseClickCount = this.mouseClickCount - 1  //goes down by 1 when button is pressed
      endif

      method operator isMouseClicked takes nothing returns boolean
          return .mouseClickCount > 0 //as per above logic the value of this variable is always 0 .. -1 .. 0 .. -1
      endmethod
     
  3. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,270
    Resources:
    6
    Models:
    1
    Icons:
    1
    Spells:
    3
    JASS:
    1
    Resources:
    6
    When I made the snippet, I was using BJDebugMsg("") in it. Having to see a lot of messages caused the game to suffer, so I thought that it might become troublesome/resource intensive at any point, and chose to implement it such that at most 4 mouse move events can fire per a given interval, which is 0.03 by default.

    The reason why all instances that have reached the global cap are resumed simultaneously, is to mimic the deterministic behavior of the game, making sure that all instances are synced at the same time.

    I did not really test out the snippet when I uploaded it, but looking back at it now, I would say that the mouse move event is not really inefficient, just that DisplayTextToPlayers clutter up the game and the log and slow it down upon every time the mouse is moved. I will clarify with an update on this that the lockout and the number of move events allowed per interval are configurable, but set to safe values by default.

    I might have missed out a question, so I may reply in another message.

    Oops. That was a nice logical catch. I suppose I can change the conditional expression to be more reasonable. Before all else, I interpret clicking as the moment a mouse button has been depressed or EVENT_PLAYER_MOUSE_DOWN. Since one cannot know exactly which mouse button was clicked, I had to go with the method a'la counter. When the counter is greater than 1, the snippet will tell you that at least one button has been clicked and not released yet.
     
  4. Illidan(Evil)X

    Illidan(Evil)X

    Joined:
    Oct 24, 2004
    Messages:
    646
    Resources:
    150
    Models:
    109
    Icons:
    27
    Skins:
    2
    Maps:
    12
    Resources:
    150
    You very much can.
    Code (vJASS):
    type mousebuttontype    extends     handle

    constant native ConvertMouseButtonType      takes integer i returns mousebuttontype

    constant mousebuttontype    MOUSE_BUTTON_TYPE_LEFT          = ConvertMouseButtonType(1)
    constant mousebuttontype    MOUSE_BUTTON_TYPE_MIDDLE        = ConvertMouseButtonType(2)
    constant mousebuttontype    MOUSE_BUTTON_TYPE_RIGHT         = ConvertMouseButtonType(3)

    native BlzGetTriggerPlayerMouseButton              takes nothing returns mousebuttontype
     
  5. Pyrogasm

    Pyrogasm

    Joined:
    Feb 27, 2007
    Messages:
    2,664
    Resources:
    1
    Spells:
    1
    Resources:
    1
    Oh okay cool I get where you were coming from with the lockout now. You addressed everything I was wondering about, will have to mess with it now. Glad to see I even made the credits list with that catch :]


    Something else I forgot to ask above: why write your onInit in a module like that? Habit from larger projects? If it were complex or involved some other part of the code in the init process I’d get it but you actually typed more letters to do the same thing. If you had just named your struct’s Init method onInit instead you wouldn’t have needed to write the module and implement it since all onInit does is call Init!

    <insert blah blah about extra code overhead from JASSHelper that nobody functionally has to worrying about here>
     
  6. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,270
    Resources:
    6
    Models:
    1
    Icons:
    1
    Spells:
    3
    JASS:
    1
    Resources:
    6
    Well, isn't that quite a catch? Syntax highlighter keeps hiding those stuff which could really have proven useful here.
    Looks like I have some more work to do.

    In writing up libraries, I find it more convenient to have an initialization module that calls a specific function named init. That way, all I have to do is just implement the module below the init function, and now I have a module initialization function. This has become my habit with certain structs as of late.
     
  7. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,654
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    If you are trying to make an API which allows more convenient usage of the mouse natives I would suggest the following:

    Code (vJASS):

    // These should return a cached value
    function GetPlayerMouseX takes player p returns real
    function GetPlayerMouseY takes player p returns real

    // Simply add the boolexpr to a global trigger and return the result of TriggerAddCondition
    function OnMouseMove takes boolexpr func returns triggercondition
    function OnMouseClick takes boolexpr func, boolean up returns triggercondition

    // Ability to remove callbacks (TriggerRemoveCondition)
    function RemoveMouseEventCallback takes triggercondition callback returns nothing
     


    Keeping your struct syntax would be fine but I'd really like normal JASS API too.

    I also don't think there should be any need for the lockout. It might be useful as an optional feature though.
     
  8. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,270
    Resources:
    6
    Models:
    1
    Icons:
    1
    Spells:
    3
    JASS:
    1
    Resources:
    6
    Was too busy last week, so I only got around to update it now.
    The lockout behavior is going to be kept, just in case anyone used the previous version.

    The attached map demonstrates a sample of what the snippet can allow one to perform.
    • JASS Syntax has been added to the next version, along with suggested functions.
    • New integer constants have been introduced for callback-related functions, EVENT_MOUSE_UP, EVENT_MOUSE_DOWN, and EVENT_MOUSE_MOVE.
     

    Attached Files:

  9. TriggerHappy

    TriggerHappy

    Code Moderator

    Joined:
    Jun 23, 2007
    Messages:
    3,654
    Resources:
    22
    Spells:
    11
    Tutorials:
    2
    JASS:
    9
    Resources:
    22
    Tested and works properly. This is a very convenient way to start using the mouse natives without creating any boilerplate yourself.

    Approved.
     
  10. Quilnez

    Quilnez

    Joined:
    Oct 12, 2011
    Messages:
    3,243
    Resources:
    37
    Icons:
    2
    Tools:
    1
    Maps:
    7
    Spells:
    21
    Tutorials:
    2
    JASS:
    4
    Resources:
    37
    Found a bug:

    Press and hold mouse button (left/right click) => pause the game (press F10/minimize/anything that pauses the game) => release mouse button => unpause the game =>
    isMouseButtonClicked
    will get stuck at
    true
     
  11. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,270
    Resources:
    6
    Models:
    1
    Icons:
    1
    Spells:
    3
    JASS:
    1
    Resources:
    6
    Hmm, the mouse event should work even when the game is paused. I'll see if I can reproduce the error.
     
  12. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,270
    Resources:
    6
    Models:
    1
    Icons:
    1
    Spells:
    3
    JASS:
    1
    Resources:
    6
    @Quilnez

    Anyway, the new release of the snippet now has some protective measures which will prevent some 'quirks' associated with the events themselves, running once per instant. Hopefully, this will resolve a reported bug with
    isMouseButtonClicked
     
  13. Pyrogasm

    Pyrogasm

    Joined:
    Feb 27, 2007
    Messages:
    2,664
    Resources:
    1
    Spells:
    1
    Resources:
    1
    Okay I've been messing with this for a bit and I have some... gripes/confusion about the lockout you implemented.
    • The lockout is based on sheer number of inputs, not inputs in a given time. This means that if I move for 15 inputs (one less than the default lockout number) and then stop moving my mouse, the next time I move it I'm instantly locked out for the duration after one move event even though I'm clearly not overloading the system with input. A method where inputs with a frequency higher than X would be ignored seems to make more logical sense.

    • Setting the lockout time to 0 doesn't instantly re-enable the mouse tracking. I do not understand why this should be since the 0.00 timer callback is a frequent method method to run new threads and it definitely does not delay anything in those situations. I tested this by destroying/creating an effect on the mouse position and then whipping my mouse around the screen at a relatively constant rate. There are visible gaps in the pattern of created effects (not because I went faster there) that appear regularly as they should after the appropriate number of move events to fire the lockout. I tested by commenting out the lockout if block entirely and the resulting effects did not have any visible gaps. I'm really confused about this.

    • Having a static if block to enable/disable the lockout entirely seems a good addition. Is this code really so taxing that having it on constantly could be an issue? I am aware I can effectively create this by setting the lockout count to something ridiculously high.

    Additional comment: Moving the camera field with the arrow keys does not trigger mouse move events even though the coordinates of the mouse are changing. I know there's nothing you can do about this but it seems like an issue Blizzard should fix with the mouse move natives. Scrolling the camera field also doesn't fire mouse move. This also sounds like a pain in the butt to solve and involves hooking into all the camera functions.
     
  14. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,270
    Resources:
    6
    Models:
    1
    Icons:
    1
    Spells:
    3
    JASS:
    1
    Resources:
    6
    Released v.1.0.2.2 (Apologies for the messed up indenting.)

    The new version addresses this with a timer per player, which decreases the likelihood of locking within a given amount of time by decrementing the total count with a new MOUSE_COUNT_LOSS variable.

    This will hopefully be addressed in the new version.



    I had gripes on having to introduce static ifs to this, but got around to it. The new version now has
    IMPL_LOCK
    , shorthand for implement lock (or implicit lock).



    Yep, kinda tricky to implement. There is this native which could prove to be of use to others (BlzSetMousePos). The new version includes a method that uses that function.