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

[vJASS] ScreenMouse

  • Determine direction of mousemovement on screen
  • Get the mouse position on screen, relative to center
  • Get mouse button up/down state
  • Note that coordinates go from right to left (X) and bottom to top (Y)
  • LIMITATIONS:
    • In most cases direction of mousemovement should be reasonably accurate despite these
    • Cursor must be on map geometry
    • Results are distorted on non-flat terrain
    • Relative position is distorted by Field of View (x values are larger at the top of the screen than at the bottom)
JASS:
//===========================================================================
//
//  Screen Mouse v1.1.2
//  by loktar
//  -------------------------------------------------------------------------
// * Determine direction of mousemovement on screen
// * Get the mouse position on screen, relative to center
// * Get mouse button up/down state
// * Note that coordinates go from right to left (X) and bottom to top (Y)
// * LIMITATIONS:
// * * Cursor must be on map geometry
// * * Results are distorted on non-flat terrain
// * * Relative position is distorted by Field of View (x values are smaller at the top of the screen than at the bottom)
//  -------------------------------------------------------------------------
//
//    -------
//    * API *
//    -------
//  *    boolean SMRegisterPlayerMove(trigger moveTrigger, player plr)
//          - Register mouse move functionality
//          - Disables moveTrigger
//          - Native trigger functions can be used in actions/conditions added to moveTrigger
//          - Returns false if trigger/player combination is already registered
//
//  *    boolean SMEnablePlayerMove(boolean enable, trigger moveTrigger, integer playerId)
//          - Enable/disable ScreenMouse move functionality
//          - If enable == false, prevents all moveTrigger actions from executing
//          - If enable == false, disables moveTrigger
//          - Returns false if trigger/player combination doesn't exist
//
//  *    boolean SMRegisterPlayerButton(trigger buttonTrigger, player plr)
//          - Register mouse button functionality
//          - Native trigger functions can be used in actions/conditions added to buttonTrigger
//          - Returns false if trigger/player combination is already registered
//
//  *    boolean SMEnablePlayerButton(boolean enable, trigger buttonTrigger, integer playerId)
//          - Enable/disable ScreenMouse button functionality
//          - If enable == false, prevents all buttonTrigger actions from executing
//          - Enables/disables buttonTrigger
//          - Returns false if trigger/player combination doesn't exist
//
//  *    boolean SMRegisterPlayerDrag(trigger buttonTrigger, trigger moveTrigger, player plr, boolean left, boolean right, boolean both)
//          - Register mouse drag functionality: enable/disable moveTrigger while holding down mouse button(s)
//          - Register for left button, right button and/or both together
//          - Calls SMRegisterPlayerMove() and SMRegisterPlayerButton()
//          - Returns false if buttonTrigger/player combination already has a moveTrigger associated
//
//  *    boolean SMEnablePlayerDrag(boolean left, boolean right, boolean both, trigger buttonTrigger, integer playerId)
//          - Enable/disable ScreenMouse drag functionality
//          - If any flag == true, enables buttonTrigger
//          - If all flags == false, disables buttonTrigger and associated moveTrigger
//          - Returns false if buttonTrigger/player combination doesn't exist or has no associated moveTrigger
//
//  *    boolean SMIsLeftDown(integer playerId), SMIsRightDown(integer playerId)
//          - Mouse button up/down state
//          - Set with buttonTriggers
//
//  *    real SMGetDifX(integer playerId), SMGetDifY(integer playerId)
//          - Difference with previous mouse position on screen
//          - Set with moveTriggers
//
//  *    real SMGetRelX(integer playerId), SMGetRelY(integer playerId)
//          - Position of the mouse relative to the center of the screen
//          - Set with buttonTriggers
//
//  *    real SMGetDifXs(integer playerId), SMGetDifYs(integer playerId)
//          - Difference with previous mouse position on map, compensated for Target Distance and Field of View
//          - Set with moveTriggers
//
//  *    real SMGetX(integer playerId), SMGetY(integer playerId)
//          - Difference with previous mouse position on screen
//          - Set with moveTriggers
//
//  *    real SM_minX, SM_maxX, SM_minY, SM_maxY
//          - Mouse position bounds
//          - Default: Camera Bounds
//
//  *    real SM_distMpl
//          - Multiplier for Target Distance compensation
//          - Only applied if larger than current Target Distance
//          - Difference/Distance*SM_distMpl
//          - Default: 1650
//
//  *    real SM_fovMpl
//          - Multiplier for Field of View compensation (in Radians!)
//          - Only applied if larger than current Field of View
//          - Difference/FoV*SM_fovMpl
//          - Default: Deg2Rad(70)
//
//===========================================================================
library ScreenMouse initializer InitScreenMouse
    globals
        private constant real R90 = Deg2Rad(90)
     
        private gamecache gcSM = InitGameCache("ScreenMouse.w3v")
        private constant string DIFX   = "difx"
        private constant string DIFY   = "dify"
        private constant string DIFX_S = "difxs"
        private constant string DIFY_S = "difys"
        private constant string RELX   = "relx"
        private constant string RELY   = "rely"
     
        private hashtable htbSM = InitHashtable()
        private constant key X
        private constant key Y
        private constant key LEFT_DOWN
        private constant key RIGHT_DOWN
        private constant key DO_LEFT
        private constant key DO_RIGHT
        private constant key DO_BOTH
        private constant key TRG_MOVE
        real SM_minX // GetCameraBound cannot be called at init
        real SM_maxX
        real SM_minY
        real SM_maxY
        real SM_distMpl = 1650
        real SM_fovMpl  = Deg2Rad(70)
    endglobals
//===============================================================================
//===============================================================================
//===============================================================================
//==== MOUSE FUNCS ==============================================================
//===============================================================================
    //==== Compensate angles and save ====
    //==== ! This function should only be called locally ! ====
    private function SaveScreenDif takes real difX, real difY, string keyP, string keyX, string keyY returns nothing
        local real field
        local real tmpX
        // Compensate Rotation
        set field = GetCameraField(CAMERA_FIELD_ROTATION)
        set tmpX = difX // Save original newX for newY calculation
        set difX = Cos(field-R90)*(difX) + Sin(field-R90)*(difY)
        set difY = Cos(field+R90)*(-difY) - Sin(field+R90)*(-tmpX)
        // Compensate Roll
        set field = GetCameraField(CAMERA_FIELD_ROLL)
        set tmpX = difX // Save original newX for newY calculation
        set difX = Sin(field-R90)*(-difX) + Cos(field-R90)*(-difY)
        set difY = Sin(field+R90)*(difY) - Cos(field+R90)*(tmpX)
        // Compensate AoA
        set difY = Sin(-GetCameraField(CAMERA_FIELD_ANGLE_OF_ATTACK))*difY
        call StoreReal(gcSM, keyP, keyX, difX)
        call StoreReal(gcSM, keyP, keyY, difY)
        call SyncStoredReal(gcSM, keyP, keyX)
        call SyncStoredReal(gcSM, keyP, keyY)
    endfunction
    //========
    //==== Check for valid mouse position ====
    private function IsValidPosition takes real x, real y returns boolean
        return (x != 0 or y != 0) and x >= SM_minX and x <= SM_maxX and y >= SM_minY and y <= SM_maxY // Mouse on UI gives (0, 0)
    endfunction
    //========
    //==== Mouse Move ====
    private function MouseMoveCndAcn takes nothing returns boolean
        local real newX
        local real newY
        local real realTmp
        local real realTmp2
        local player plr = GetTriggerPlayer()
        local integer pId = GetPlayerId(plr)
        local trigger trg = GetTriggeringTrigger()
        local integer hIdMove = GetHandleId(trg)
        local boolean success = false
        local string keyP
        if HaveSavedBoolean(htbSM, pId, hIdMove) and LoadBoolean(htbSM, pId, hIdMove) then
            call DisableTrigger(trg)
   
            set newX = BlzGetTriggerPlayerMouseX()
            set newY = BlzGetTriggerPlayerMouseY()
         
            if IsValidPosition(newX, newY) then
                if not HaveSavedReal(htbSM, pId, X) or not HaveSavedReal(htbSM, pId, Y) then
                    call SaveReal(htbSM, pId, X, newX)
                    call SaveReal(htbSM, pId, Y, newY)
                else
                    set realTmp = LoadReal(htbSM, pId, X)
                    set realTmp2 = LoadReal(htbSM, pId, Y)
                    call SaveReal(htbSM, pId, X, newX)
                    call SaveReal(htbSM, pId, Y, newY)
                 
                    if IsValidPosition(realTmp, realTmp2) and plr == GetLocalPlayer() then
                        set newX = realTmp-newX
                        set newY = realTmp2-newY
       
                        // Compensate Distance
                        set realTmp = GetCameraField(CAMERA_FIELD_TARGET_DISTANCE)
                        if realTmp > SM_distMpl then
                            set newX = newX/realTmp*SM_distMpl
                            set newY = newY/realTmp*SM_distMpl
                        endif
               
                        // Compensate FoV
                        set realTmp = GetCameraField(CAMERA_FIELD_FIELD_OF_VIEW)
                        if realTmp > SM_fovMpl then
                            set newX = newX/realTmp*SM_fovMpl
                            set newY = newY/realTmp*SM_fovMpl
                        endif
               
                        set keyP = I2S(pId)
                        call StoreReal(gcSM, keyP, DIFX_S, newX)
                        call StoreReal(gcSM, keyP, DIFY_S, newY)
                        call SyncStoredReal(gcSM, keyP, DIFX_S)
                        call SyncStoredReal(gcSM, keyP, DIFY_S)
                        call SaveScreenDif(newX, newY, keyP, DIFX, DIFY)
                     
                        set success = true
                    endif // saved valid & local player
                endif // have saved
            endif // new valid
         
            call EnableTrigger(trg)
        endif // move enabled for player
        if not success then
            set keyP = I2S(pId)
            call StoreReal(gcSM, keyP, DIFX, 0)
            call StoreReal(gcSM, keyP, DIFY, 0)
            call StoreReal(gcSM, keyP, DIFX_S, 0)
            call StoreReal(gcSM, keyP, DIFY_S, 0)
        endif
     
        set plr = null
        set trg = null
        return success // trigger/player combo is enabled and local player?
    endfunction
    //========
    //==== Mouse Button ====
    private function MouseBtnCndAcn takes nothing returns boolean
        local player plr = GetTriggerPlayer()
        local integer pId = GetPlayerId(plr)
        local integer hIdBtn = GetHandleId(GetTriggeringTrigger())
        local mousebuttontype mouseBtn
        local real mouseX
        local real mouseY
        local integer p_hBtnId
        local trigger moveTrg = null
        local integer hIdMove
        local boolean enable
     
        if HaveSavedBoolean(htbSM, pId, hIdBtn) and LoadBoolean(htbSM, pId, hIdBtn) then
            set mouseBtn = BlzGetTriggerPlayerMouseButton()
            set mouseX = BlzGetTriggerPlayerMouseX()
            set mouseY = BlzGetTriggerPlayerMouseY()
   
            // Mouse Down
            if GetTriggerEventId() == EVENT_PLAYER_MOUSE_DOWN then
                // MOUSE_BUTTON_TYPE_MIDDLE does not fire this event as of 1.30.4
                if mouseBtn == MOUSE_BUTTON_TYPE_LEFT then
                    call SaveBoolean(htbSM, pId, LEFT_DOWN, true)
                elseif mouseBtn == MOUSE_BUTTON_TYPE_RIGHT then
                    call SaveBoolean(htbSM, pId, RIGHT_DOWN, true)
                endif
       
                // Set relative position
                if IsValidPosition(mouseX, mouseY) and plr == GetLocalPlayer() then
                    call SaveScreenDif(GetCameraTargetPositionX()-mouseX, GetCameraTargetPositionY()-mouseY, I2S(pId), RELX, RELY)
                endif
       
            // Mouse Up
            elseif mouseBtn == MOUSE_BUTTON_TYPE_LEFT then
                call SaveBoolean(htbSM, pId, LEFT_DOWN, false)
            elseif mouseBtn == MOUSE_BUTTON_TYPE_RIGHT then
                call SaveBoolean(htbSM, pId, RIGHT_DOWN, false)
            endif
   
            // Enable/Disable Drag
            set p_hBtnId = pId+hIdBtn*100
   
            if HaveSavedHandle(htbSM, p_hBtnId, TRG_MOVE) then
                set moveTrg = LoadTriggerHandle(htbSM, p_hBtnId, TRG_MOVE)
                set hIdMove = GetHandleId(moveTrg)
       
                if HaveSavedBoolean(htbSM, pId, hIdMove) and LoadBoolean(htbSM, pId, hIdMove) then
                    set enable = LoadBoolean(htbSM, pId, RIGHT_DOWN)
                    if LoadBoolean(htbSM, pId, LEFT_DOWN) then
                        set enable = (enable and LoadBoolean(htbSM, p_hBtnId, DO_BOTH)) or (not enable and LoadBoolean(htbSM, p_hBtnId, DO_LEFT))
                    else
                        set enable = enable and LoadBoolean(htbSM, p_hBtnId, DO_RIGHT)
                    endif
           
                    if enable then
                        // Enable Move trigger
                        if not IsTriggerEnabled(moveTrg) then
                            call SaveReal(htbSM, pId, X, mouseX)
                            call SaveReal(htbSM, pId, Y, mouseY)
                            call EnableTrigger(moveTrg)
                        endif
                    else
                        call DisableTrigger(moveTrg)
                    endif
                endif
       
                set moveTrg = null
            endif
   
            set plr = null
            return true
        endif
        set plr = null
        return false // trigger/player combo is disabled
    endfunction
    //===========================================================================
    //===========================================================================
//===============================================================================
//===============================================================================
//===============================================================================
//==== API FUNCS ================================================================
//===============================================================================
    //==== Register MOVE to trigger/player ====
    function SMRegisterPlayerMove takes trigger moveTrg, player plr returns boolean
        local integer hIdMove = GetHandleId(moveTrg)
        local integer pId = GetPlayerId(plr)
        if not HaveSavedBoolean(htbSM, pId, hIdMove) then
            call SaveBoolean(htbSM, pId, hIdMove, true)
   
            call DisableTrigger(moveTrg)
            call TriggerRegisterPlayerEvent(moveTrg, plr, EVENT_PLAYER_MOUSE_MOVE)
            call TriggerAddCondition(moveTrg, function MouseMoveCndAcn)
   
            return true
        endif
        return false // trigger/player combo already registered
    endfunction
    //========
    //==== Enable/Disable MOVE for trigger/player ====
    function SMEnablePlayerMove takes boolean enable, trigger moveTrg, integer pId returns boolean
        local integer hIdMove = GetHandleId(moveTrg)
        if HaveSavedBoolean(htbSM, pId, hIdMove) then
            call SaveBoolean(htbSM, pId, hIdMove, enable)
   
            if not enable then
                call DisableTrigger(moveTrg)
            endif
   
            return true
        endif
        return false // not found
    endfunction
    //========
    //==== Register BUTTON to trigger/player ====
    function SMRegisterPlayerButton takes trigger btnTrg, player plr returns boolean
        local integer hIdBtn = GetHandleId(btnTrg)
        local integer pId = GetPlayerId(plr)
        if not HaveSavedBoolean(htbSM, pId, hIdBtn) then
            call SaveBoolean(htbSM, pId, hIdBtn, true)
            if not HaveSavedBoolean(htbSM, pId, LEFT_DOWN) then
                call SaveBoolean(htbSM, pId, LEFT_DOWN, false)
                call SaveBoolean(htbSM, pId, RIGHT_DOWN, false)
            endif
   
            call TriggerRegisterPlayerEvent(btnTrg, plr, EVENT_PLAYER_MOUSE_DOWN)
            call TriggerRegisterPlayerEvent(btnTrg, plr, EVENT_PLAYER_MOUSE_UP)
            call TriggerAddCondition(btnTrg, function MouseBtnCndAcn)
   
            return true
        endif
        return false // trigger/player combo already registered
    endfunction
    //========
    //==== Enable/Disable MOVE for trigger/player ====
    function SMEnablePlayerButton takes boolean enable, trigger btnTrg, integer pId returns boolean
        local integer hIdBtn = GetHandleId(btnTrg)
        if HaveSavedBoolean(htbSM, pId, hIdBtn) then
            call SaveBoolean(htbSM, pId, hIdBtn, enable)
   
            if enable then
                call EnableTrigger(btnTrg)
            else
                call DisableTrigger(btnTrg)
            endif
   
            return true
        endif
        return false // not found
    endfunction
    //========
    //==== Register DRAG to triggers/player ====
    function SMRegisterPlayerDrag takes trigger btnTrg, trigger moveTrg, player plr, boolean left, boolean right, boolean both returns boolean
        local integer hIdBtn = GetHandleId(btnTrg)
        local integer pId = GetPlayerId(plr)
        local integer p_hBtnId = pId+hIdBtn*100
        if not HaveSavedHandle(htbSM, p_hBtnId, TRG_MOVE) then
            call SaveTriggerHandle(htbSM, p_hBtnId, TRG_MOVE, moveTrg)
            call SaveBoolean(htbSM, p_hBtnId, DO_LEFT, left)
            call SaveBoolean(htbSM, p_hBtnId, DO_RIGHT, right)
            call SaveBoolean(htbSM, p_hBtnId, DO_BOTH, both)
            call SMRegisterPlayerButton(btnTrg, plr)
            call SMRegisterPlayerMove(moveTrg, plr)
   
            return true
        endif
        return false // player/btn trg combo already registered
    endfunction
    //========
    //==== Enable/Disable DRAG for trigger/player ====
    function SMEnablePlayerDrag takes boolean left, boolean right, boolean both, trigger btnTrg, integer pId returns boolean
        local integer id = pId+GetHandleId(btnTrg)*100
        if HaveSavedHandle(htbSM, id, TRG_MOVE) then
            call SaveBoolean(htbSM, id, DO_LEFT, left)
            call SaveBoolean(htbSM, id, DO_RIGHT, right)
            call SaveBoolean(htbSM, id, DO_BOTH, both)
   
            if left or right or both then
                call EnableTrigger(btnTrg)
            else
                call DisableTrigger(btnTrg)
                call DisableTrigger(LoadTriggerHandle(htbSM, id, TRG_MOVE))
            endif
   
            return true
        endif
        return false // not found
    endfunction
    //========
    //==== Get Left Down ====
    function SMIsLeftDown takes integer playerId returns boolean
        return HaveSavedBoolean(htbSM, playerId, LEFT_DOWN) and LoadBoolean(htbSM, playerId, LEFT_DOWN)
    endfunction
    //========
    //==== Get Right Down ====
    function SMIsRightDown takes integer playerId returns boolean
        return HaveSavedBoolean(htbSM, playerId, RIGHT_DOWN) and LoadBoolean(htbSM, playerId, RIGHT_DOWN)
    endfunction
    //========
    // ==== Get difX ====
    function SMGetDifX takes integer playerId returns real
        local string keyP = I2S(playerId)
        if HaveStoredReal(gcSM, keyP, DIFX) then
            return GetStoredReal(gcSM, keyP, DIFX)
        endif
        return 0.0
    endfunction
    //========
    // ==== Get difY ====
    function SMGetDifY takes integer playerId returns real
        local string keyP = I2S(playerId)
        if HaveStoredReal(gcSM, keyP, DIFY) then
            return GetStoredReal(gcSM, keyP, DIFY)
        endif
        return 0.0
    endfunction
    //========
    // ==== Get difXs ====
    function SMGetDifXs takes integer playerId returns real
        local string keyP = I2S(playerId)
        if HaveStoredReal(gcSM, keyP, DIFX_S) then
            return GetStoredReal(gcSM, keyP, DIFX_S)
        endif
        return 0.0
    endfunction
    //========
    // ==== Get difYs ====
    function SMGetDifYs takes integer playerId returns real
        local string keyP = I2S(playerId)
        if HaveStoredReal(gcSM, keyP, DIFY_S) then
            return GetStoredReal(gcSM, keyP, DIFY_S)
        endif
        return 0.0
    endfunction
    //========
    // ==== Get X ====
    function SMGetX takes integer playerId returns real
        if HaveSavedReal(htbSM, playerId, X) then
            return LoadReal(htbSM, playerId, X)
        endif
        return 0.0
    endfunction
    //========
    // ==== Get Y ====
    function SMGetY takes integer playerId returns real
        if HaveSavedReal(htbSM, playerId, Y) then
            return LoadReal(htbSM, playerId, Y)
        endif
        return 0.0
    endfunction
    //========
    // ==== Get Relative X ====
    function SMGetRelX takes integer playerId returns real
        local string keyP = I2S(playerId)
        if HaveStoredReal(gcSM, keyP, RELX) then
            return GetStoredReal(gcSM, keyP, RELX)
        endif
        return 0.0
    endfunction
    //========
    // ==== Get Relative Y ====
    function SMGetRelY takes integer playerId returns real
        local string keyP = I2S(playerId)
        if HaveStoredReal(gcSM, keyP, RELY) then
            return GetStoredReal(gcSM, keyP, RELY)
        endif
        return 0.0
    endfunction
    //===========================================================================
    //===========================================================================
//===============================================================================
//===============================================================================
//===============================================================================
//==== INITIALIZER ==============================================================
//===============================================================================
    private function InitScreenMouse takes nothing returns nothing
        call TriggerSleepAction(0) // For GetCameraBound
        // Get map bounds
        set SM_minX = GetCameraBoundMinX()
        set SM_maxX = GetCameraBoundMaxX()
        set SM_minY = GetCameraBoundMinY()
        set SM_maxY = GetCameraBoundMaxY()
    endfunction
//===============================================================================
//===============================================================================
endlibrary

Show direction of mousemovement
JASS:
function ShowMovement takes nothing returns boolean
    local string strDif = ""
    local integer pid = GetPlayerId(GetTriggerPlayer())
   
    if SMGetDifX(pid) > 0 then
        set strDif = "Left"
    elseif SMGetDifX(pid) < 0 then
        set strDif = "Right"
    endif

    if SMGetDifY(pid) < 0 then
        set strDif = strDif+" Up"
    elseif SMGetDifY(pid) > 0 then
        set strDif = strDif+" Down"
    endif

    if SMIsLeftDown(pid) then
        if SMIsRightDown(pid) then
            set strDif = "|cffff0000"+strDif+"|r"
        else
            set strDif = "|cff00ff00"+strDif+"|r"
        endif
    elseif SMIsRightDown(pid) then
        set strDif = "|cff0000ff"+strDif+"|r"
    endif

    if strDif == "" then
        call BJDebugMsg("none")
    else
        call BJDebugMsg(strDif)
    endif

    return false
endfunction

function ShowMovement_Actions takes nothing returns nothing
    local trigger trgMoveP1 = CreateTrigger()
    local trigger trgMoveP2 = CreateTrigger()

    call SMRegisterPlayerDrag(CreateTrigger(), trgMoveP1, Player(0), true, true, true)
    call TriggerAddCondition(trgMoveP1, function ShowMovement)
    call SMRegisterPlayerDrag(CreateTrigger(), trgMoveP2, Player(1), true, true, true)
    call TriggerAddCondition(trgMoveP2, function ShowMovement)
   
    set trgMoveP1 = null
    set trgMoveP2 = null
endfunction

//===========================================================================
function InitTrig_ShowMovement takes nothing returns nothing
    set gg_trg_ShowMovement = CreateTrigger()

    call TriggerAddAction(gg_trg_ShowMovement, function ShowMovement_Actions)
endfunction

Show relative mouse position
JASS:
function ShowPosition takes nothing returns boolean
    call BJDebugMsg("x: "+R2S(SMGetRelX(0))+", y: "+R2S(SMGetRelY(0)))
    return false
endfunction

function Trig_ScreenMouse_Example_Actions takes nothing returns nothing
    call SMRegisterPlayerButton(gg_trg_ScreenMouse_Example, Player(0))
    call TriggerAddCondition(gg_trg_ScreenMouse_Example, function ShowPosition)
endfunction

//===========================================================================
function InitTrig_ScreenMouse_Example takes nothing returns nothing
    set gg_trg_ScreenMouse_Example = CreateTrigger()
    call TriggerAddAction(gg_trg_ScreenMouse_Example, function Trig_ScreenMouse_Example_Actions)
endfunction

### v1.1.2
- Data is now synced (there should not be any desyncs)

### v1.1.1
- Now setting DifX/Y and DifX/Y_s to 0 when move trigger is run with invalid mouse position or when disabled with SMEnablePlayerMove

### v1.1.0
- Added separate buttonTrigger registration
- SMRegisterPlayer renamed to SMRegisterPlayerDrag
- Added Enable/Disable function for each registration function
- DifX/Y, DifX/Y_s and RelX/Y now only updated when TriggerPlayer == LocalPlayer
- Getting player with GetTriggerPlayer() instead of saving trigger/player associations
- Registration limits removed, except:
- - each trigger/player combination can only be registered once
- - each buttonTrigger/player combination can have only one associated moveTrigger
- Misc improvements/changes

### v1.0.3
- Renamed ScreenMouseRegisterPlayer to SMRegisterPlayer
- Added SMRegisterPlayerMove
- Move trigger no longer disables itself if no mouse buttons are pressed down
- Added SMGetX(), SMGetY(), SMGetRelX() and SMGetRelY()
- Added checks if saved values exist to getter functions

### v1.0.2
- Players can now be registered more than once
- Improved moveTrigger enable/disable behaviour in buttonTrigger

### v1.0.1 (small update)
- Register function now checks if player and/or trigger(s) are already registered

### v1.0.1
- Values now stored per player/trigger in hashtable
- Get values with getter functions
- Choose which buttons to use (left, right, both together)
- Updated example trigger

### v1.0.0 (quick update)
- Updated to use triggers passed by user instead of internal triggers
- Nulled trg in MouseMoveCndAcn()
- Some small changes
- Added SM_difXs/SM_difYs; multipliers now only applied if larger than current field values
 
Last edited:

Jampion

Code Reviewer
Level 15
Joined
Mar 25, 2016
Messages
1,327
Can you give an example where this system is useful?
There only seems to be a way to get differences in mouse positions. Can you detect if a player clicks on the bottom right corner of the screen for instance?
 
I've just updated my model viewer map which uses this (now as a separate library) for controlling the camera or model with the mouse. You can move, rotate and zoom/scale the camera/model.

I think detecting where the player clicks should be possible, but I'm not 100% sure. It's probably a matter of comparing the click position to the camera target.
I'll try adding that in the future.

edit: oops, looks like I deleted the example trigger, I'll add it back in.
edit: done
 
Last edited:

Jampion

Code Reviewer
Level 15
Joined
Mar 25, 2016
Messages
1,327
So this system is about handling mouse drag events?
In that case you should improve the documentation of what this system does. I initially thought it would allow me to get the mouse position on the screen. For example if you have a custom user interface and need to detect when some buttons on the screen are clicked. You should also add, that the move trigger only runs as long as the specified button is pressed.
 
Ah, right, didn't realize it was unclear. I'll update the documentation when I have some time.

Maybe I should also add a function that activates the move trigger regardless of mouse buttons being pressed?
Edit: well, this can actually almost already be done if the user manually enables the move trigger, but atm it's automatically disabled when no mouse buttons are pressed. I can probably just remove that, as the button trigger takes care of that anyway.

As for getting the exact position on screen (as opposed to the relative position), I don't think that's possible due to differences in resolution and maybe some other factors, but I may be wrong on that.

Edit: I've updated the script. You can now get the position of the mouse on screen after left or right mouse button is pushed. The position is relative to center (so center of screen is 0, 0). You can also now register for the move event separately.
Hopefully the description is clearer now too, let me know if it isn't :)
 
Last edited:
You can just use the native "get triggering x" functions related to mouse events inside any action or condition you've added to either of the triggers.
On the other hand, triggers can only be registered once, to a specific player, so the triggers will only fire for that specific player.

Edit: this just made me realize it's unnecessary for me to store trigger-player associations as I'm doing now. I can just use GetTriggeringPlayer instead, should've thought of that before. Still need to store move-button trigger associations so the correct move trigger can be enabled/disabled by the button trigger.

Edit: I also just realized that the camera functions get the field data for the local player, so the trigger events should only be registered when player == local player, right?
Hmm...

Edit: script has been updated to address these points, and registration is now split into move/button/drag.
 
Last edited:

Jampion

Code Reviewer
Level 15
Joined
Mar 25, 2016
Messages
1,327
I don't know how I feel about this system. The calculation of the screen position is probably as good as one can do it, but it doesn't work well with uneven terrain, which limits its use. You can use it for a menu like you did in the model viewer, where you can guarantee, that the map layout is flat. But in actual gameplay you can get inaccurate results.

Have you tested this system in multiplayer? You store mouse positions in the hashtable locally. When those values are used later to move a unit for instance you would probably get different results for different clients, so it would desync.
 
doesn't work well with uneven terrain
Have you tested this? I haven't myself, but I don't really see a reason why this would be the case, as map coordinates (afaik) stay the same on uneven terrain?
But results will definitely be inaccurate when the mouse is outside map bounds, or underneath terrain, or on the sky.

Have you tested this system in multiplayer?
I haven't. I honestly don't remember why I added the local player stuff, I should probably just get rid of it. If I just store everything globally there should be no risk of desyncs, right?


I'm focusing on something else right now, but I'm gonna be working on the model viewer map again in a while, and I'll update this script then too.
 
Last edited:

Jampion

Code Reviewer
Level 15
Joined
Mar 25, 2016
Messages
1,327
Have you tested this? I haven't myself, but I don't really see a reason why this would be the case, as map coordinates (afaik) stay the same on uneven terrain?

I used this code to print screen coordinates:
JASS:
function ShowPosition takes nothing returns boolean
    call BJDebugMsg("x: "+R2S(SMGetRelX(0))+", y: "+R2S(SMGetRelY(0)))
    return false
endfunction

function Trig_ScreenMouse_Example_Actions_1 takes nothing returns nothing
    call SMRegisterPlayerButton(gg_trg_ScreenMouse_Example_1, Player(0))
    call TriggerAddCondition(gg_trg_ScreenMouse_Example_1, function ShowPosition)
endfunction
I tried to click at the center, so y = 0. On normal terrain this works fine, but on the hill I got higher values.

upload_2019-5-25_23-13-7-png.323845

There also is a problem with x coordinates regardless of terrain. When I clicked at the top of the screen, I got x values from 1200 to -1200, but at the bottom it is only around 900 to -900.


I haven't. I honestly don't remember why I added the local player stuff, I should probably just get rid of it. If I just store everything globally there should be no risk of desyncs, right?

Camera is local, so you need local code to get the camera information from the specific player. But when you save the values in the hashtables, you need to make sure it's the same for every client. At some point you would need to synchronize your data.

Check out the Sync library [vJASS] - Sync (Game Cache). It has a documentation with a little bit of information about the problem:
Sync is a library that allows you to synchronize otherwise asynchronous data such as camera position or the contents of a local file. This is important because the game is likely to split up, or disconnect (desync) the players if you try to use data that differs from each player.
 
I tried to click at the center, so y = 0. On normal terrain this works fine, but on the hill I got higher values.
Oh, that's disappointing. I should have done some testing on non-flat terrain.
Hopefully I can figure out exactly what's going on and fix it, but my first thought is that there's no probably no way to account for this, so this probably will only be useful on flat terrain then :(

There also is a problem with x coordinates regardless of terrain. When I clicked at the top of the screen, I got x values from 1200 to -1200, but at the bottom it is only around 900 to -900.
Hmm, that's weird, I don't remember seeing this problem. I'll have to do some testing.

Camera is local, so you need local code to get the camera information from the specific player. But when you save the values in the hashtables, you need to make sure it's the same for every client. At some point you would need to synchronize your data.
Ah right, I made it local because of the camera, now I remember. Thanks for the link, I'll check it out. Hopefully it can be achieved without adding too much complexity, I'd like to keep this script fairly simple and without requirements (especially since it looks like it's a lot less useful than I thought if it's only accurate on flat terrain).
 
Data is now synced. I asked a question about this here: [vJASS] - Syncing Data - it's hard for me to test it myself.

Unfortunately I don't think it's possible to solve the other issues :( The discrepancy in x values I believe is due to the the field of view angle. I would need to know for a given target distance how many units of distance are visible on screen in order to resolve this.
 
Top