• 🏆 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!

Making an accurate "get mouse screen position"

Level 14
Joined
Jan 16, 2009
Messages
716
tldr: The goal of this post is to find an accurate and robust get_mouse_screen_position() for the community.

Big thanks to @Unryze for helping me find stuff in the 1.26 game.dll!



So for a few patches now, there is a way to get the mouse world coordinates. However there is no way to get the mouse screen coordinates directly.
This shouldn’t be an issue at all since all the data required to transform world coordinates to screen coordinates should be here.

The world camera’s view and projection matrices are needed for that.
Using the camera’s eye, target, field of view, near_z and far_z and the screen’s aspect ratio the 2 transforms are computed (view transform, projection transform)

However, one might notice that the camera’s target is not in the center of the screen but slightly
higher. In fact it’s centered on (0.355/0.6) * screen_height.
This is known because by digging in the 1.26 game.dll code, it can be found that the world frame starts vertically at 0.13 and ends at 0.58 (in UI coordinates). For reforged they have kept this centering but expanded the frame in some way.

Still, while using this to correct the aspect ratio to 0.8/(0.58 - 0.13), the values outputted by the projection transform are not what is expected.
For instance when the mouse is at the 0.13 ui height, the projection doesn’t output -1 for the y-axis. In the same way, when the mouse is at the 0.8 ui width, the projection doesn’t output 1 for the x-axis.

Now if one digs more into the 1.26 game.dll code, the projection matrix used by warcraft 3 is quite different.
Instead of having 1 / tan(0.5 * field_of_view_y) for the [2][2] field, they actually have 1 / tan(0.5 * field_of_view_y/square_root(aspect_ratio * aspect_ratio + 1)).
Now if this projection matrix is used, the projection output starts to get really close to what is expected. However it doesn't match quite perfectly (by 1% pretty much).
Moreover, as soon as the fov or the world frame’s aspect ratio is modified, the deviation starts to increase to the point of this not being usable robustly for a get_mouse_screen_position(). Even using the mouse scroll zoom in fully seems to skew the results.

At this point I believe I am missing some step that was either added in reforged or that was not found in the 1.26 game.dll or maybe something else entirely.
So please share any ideas or findings you might have on this matter so we can finally have an accurate and robust get_mouse_screen_position().

Here is my pseudocode to get an okayish get_mouse_screen_position() with the default camera (fov 70 and non altered world frame).
Code:
position // The mouse position.

// View transform
world_up = (0,0,1)
eye      = get_camera_eye()
target   = get_camera_target()
forward  = get_normalized(eye-at); // in RH coord system, z goes backward, hence 'eye-at' instead of 'at-eye'.
right    = get_normalized(get_cross_product(world_up, forward));
up       = get_cross_product(forward, right);

view_11 = right.x
view_12 = up.x
view_13 = forward.x

view_21 = right.y
view_22 = up.y
view_23 = forward.y

view_31 = right.z
view_32 = up.z
view_33 = forward.z

view_41 = -get_dot_product(right,   eye)
view_42 = -get_dot_product(up,      eye)
view_43 = -get_dot_product(forward, eye)

view_position = (position.x * view_11 + position.y * view_21 + position.z * view_31 + view_41,
                 position.x * view_12 + position.y * view_22 + position.z * view_32 + view_42,
                 position.x * view_13 + position.y * view_23 + position.z * view_33 + view_43)

// Projection transform
fov                = get_camera_fov()
near               = get_camera_near()
far                = get_camera_far()

world_frame_top    = 0.58
world_frame_bottom = 0.13
world_frame_left   = 0.
world_frame_right  = 0.8
world_frame_aspect_ratio = (world_frame_right - world_frame_left) / (world_frame_top - world_frame_bottom)

projection_22 = 1.0 / tan(0.5 * fov / square_root(world_frame_aspect_ratio * world_frame_aspect_ratio + 1.0))
projection_11 = projection_22 / world_frame_aspect_ratio

projection_33 = (near + far)       / (far - near)
projection_43 = (-2. * near * far) / (far - near) // This was also found in the 1.26 dll.

projection_34 = -1

w         = view_position.z * projection_34 // homogeneous coordinates allow for the projection to actually be doable with a matrix transform.
inverse_w = 1.0 / w

projection_xy = (inverse_w * (view_position.x * projection_11), 
                 inverse_w * (view_position.y * projection_22))
ui_position   = (world_frame_left   + (world_frame_right - world_frame_left) * (0.5 + 0.5 * projection_xy.x),
                 world_frame_bottom + (world_frame_top - world_frame_bottom) * (0.5 + 0.5 * projection_xy.y))

print("ui_position = "+to_string(ui_position))
 
Level 18
Joined
Oct 17, 2012
Messages
821
So, I tried out your test map and here is my result:
MouseScreenPosition.gif

JASS:
library MouseScreenPosition
    globals
      private integer vallocNext = -1
      private real array vx
      private real array vy
      private real array vz
    
      private integer vvallocNext = -1
      private real array vvx
      private real array vvy
      private real array vvz
      private real array vvw
    
      private integer mallocNext = -1
      private real array mElems

      private location gLoc = Location(0.0, 0.0)
      real getMouseScreenXyResultX = 0.0
      real getMouseScreenXyResultY = 0.0
    endglobals

    private function vAlloc takes nothing returns integer
      set vallocNext = vallocNext + 1
      if 1024 == vallocNext then
        set vallocNext = 0
      endif
      return vallocNext
    endfunction

    private function vVec takes real x, real y, real z returns integer
      local integer v = vAlloc()
      set vx[v] = x
      set vy[v] = y
      set vz[v] = z
      return v
    endfunction

    private function vSub takes integer a, integer b returns integer
      return vVec(vx[a] - vx[b], vy[a] - vy[b], vz[a] - vz[b])
    endfunction

    private function vNorm takes integer v returns integer
      local real l = 1.0 / SquareRoot(vx[v] * vx[v] + vy[v] * vy[v] + vz[v] * vz[v])
      return vVec(l * vx[v], l * vy[v], l * vz[v])
    endfunction

    private function vDot takes integer a, integer b returns real
      return vx[a] * vx[b] + vy[a] * vy[b] + vz[a] * vz[b]
    endfunction

    private function vCross takes integer a, integer b returns integer
      return vVec(vy[a] * vz[b] - vz[a] * vy[b], vz[a] * vx[b] - vx[a] * vz[b], vx[a] * vy[b] - vy[a] * vx[b])
    endfunction

    private function vNearlyParallel takes integer a, integer b, real epsilon returns boolean
      local real s0 = vx[a] * vx[b] + vy[a] * vy[b] + vz[a] * vz[b]
      local real s1 = vx[a] * vx[a] + vy[a] * vy[a] + vz[a] * vz[a]
      local real s2 = vx[b] * vx[b] + vy[b] * vy[b] + vz[b] * vz[b]
      local real d = s0 * s0 - s1 * s2
      if d < 0 then
        set d = -d
      endif
      return d < epsilon
    endfunction

    private function vvAlloc takes nothing returns integer
      set vvallocNext = vvallocNext + 1
      if 1024 == vvallocNext then
        set vvallocNext = 0
      endif
      return vvallocNext
    endfunction

    private function vvVec takes real x, real y, real z, real w returns integer
      local integer vv = vvAlloc()
      set vvx[vv] = x
      set vvy[vv] = y
      set vvz[vv] = z
      set vvw[vv] = w
      return vv
    endfunction

    private function mAlloc takes nothing returns integer
      set mallocNext = mallocNext + 1
      if 1024 == mallocNext then
        set mallocNext = 0
      endif
      return mallocNext
    endfunction

    private function mZero takes nothing returns integer
      local integer m = mAlloc()
      local integer a = 0 - 1
      loop
        set a = a + 1
        exitwhen 16 == a
        set mElems[16 * m + a] = 0.0
      endloop
      return m
    endfunction

    // column matrix
    // [Xx Yx Zx Px]
    // [Xy Yy Zy Py]
    // [Xz Yz Zz Pz]
    // [ 0  0  0  1]
    private function mSet takes integer m, integer c, integer r, real v returns nothing
      set mElems[16 * m + 4 * r + c] = v
    endfunction

    private function mGet takes integer m, integer c, integer r returns real
      return mElems[16 * m + 4 * r + c]
    endfunction

    private function mLookAt1 takes integer f, integer s, integer u, integer eye returns integer
      local integer m = mAlloc()

      call mSet(m, 0, 0, vx[s])
      call mSet(m, 0, 1, vx[u])
      call mSet(m, 0, 2, -vx[f])
      call mSet(m, 0, 3, 0.0)

      call mSet(m, 1, 0, vy[s])
      call mSet(m, 1, 1, vy[u])
      call mSet(m, 1, 2, -vy[f])
      call mSet(m, 1, 3, 0.0)

      call mSet(m, 2, 0, vz[s])
      call mSet(m, 2, 1, vz[u])
      call mSet(m, 2, 2, -vz[f])
      call mSet(m, 2, 3, 0.0)

      call mSet(m, 3, 0, -vDot(s, eye))
      call mSet(m, 3, 1, -vDot(u, eye))
      call mSet(m, 3, 2, vDot(f, eye))
      call mSet(m, 3, 3, 1.0)

      return m
    endfunction

    private function mLookAt_RH takes integer eye, integer target, integer up returns integer
      local integer f = vNorm(vSub(target, eye))
      local integer s = vNorm(vCross(f, up))
      local integer u = vCross(s, f)
      return mLookAt1(f, s, u, eye)
    endfunction

    private function getCotangent takes real fov, real aspect returns real
      // return 1.0 / Tan(0.5 * fov)
      return 1.0 / Tan(0.5 * fov / SquareRoot(aspect*aspect + 1.0))
    endfunction

    private function mPerspective_RH_NO takes real fov, real aspect, real near, real far returns integer
      local integer m = mZero()
      local real cotangent = getCotangent(fov, aspect)
      call mSet(m, 0, 0, cotangent / aspect)
      call mSet(m, 1, 1, cotangent)
      call mSet(m, 2, 3, -1.0)
      call mSet(m, 2, 2, (near + far) / (near - far))
      call mSet(m, 3, 2, (2.0 * near * far) / (near - far))
      return m
    endfunction

    private function mMulM takes integer a, integer b returns integer
      local integer r = mAlloc()
      call mSet(r, 0, 0, mGet(a, 0, 0) * mGet(b, 0, 0) + mGet(a, 1, 0) * mGet(b, 0, 1) + mGet(a, 2, 0) * mGet(b, 0, 2) + mGet(a, 3, 0) * mGet(b, 0, 3))
      call mSet(r, 1, 0, mGet(a, 0, 0) * mGet(b, 1, 0) + mGet(a, 1, 0) * mGet(b, 1, 1) + mGet(a, 2, 0) * mGet(b, 1, 2) + mGet(a, 3, 0) * mGet(b, 1, 3))
      call mSet(r, 2, 0, mGet(a, 0, 0) * mGet(b, 2, 0) + mGet(a, 1, 0) * mGet(b, 2, 1) + mGet(a, 2, 0) * mGet(b, 2, 2) + mGet(a, 3, 0) * mGet(b, 2, 3))
      call mSet(r, 3, 0, mGet(a, 0, 0) * mGet(b, 3, 0) + mGet(a, 1, 0) * mGet(b, 3, 1) + mGet(a, 2, 0) * mGet(b, 3, 2) + mGet(a, 3, 0) * mGet(b, 3, 3))
      call mSet(r, 0, 1, mGet(a, 0, 1) * mGet(b, 0, 0) + mGet(a, 1, 1) * mGet(b, 0, 1) + mGet(a, 2, 1) * mGet(b, 0, 2) + mGet(a, 3, 1) * mGet(b, 0, 3))
      call mSet(r, 1, 1, mGet(a, 0, 1) * mGet(b, 1, 0) + mGet(a, 1, 1) * mGet(b, 1, 1) + mGet(a, 2, 1) * mGet(b, 1, 2) + mGet(a, 3, 1) * mGet(b, 1, 3))
      call mSet(r, 2, 1, mGet(a, 0, 1) * mGet(b, 2, 0) + mGet(a, 1, 1) * mGet(b, 2, 1) + mGet(a, 2, 1) * mGet(b, 2, 2) + mGet(a, 3, 1) * mGet(b, 2, 3))
      call mSet(r, 3, 1, mGet(a, 0, 1) * mGet(b, 3, 0) + mGet(a, 1, 1) * mGet(b, 3, 1) + mGet(a, 2, 1) * mGet(b, 3, 2) + mGet(a, 3, 1) * mGet(b, 3, 3))
      call mSet(r, 0, 2, mGet(a, 0, 2) * mGet(b, 0, 0) + mGet(a, 1, 2) * mGet(b, 0, 1) + mGet(a, 2, 2) * mGet(b, 0, 2) + mGet(a, 3, 2) * mGet(b, 0, 3))
      call mSet(r, 1, 2, mGet(a, 0, 2) * mGet(b, 1, 0) + mGet(a, 1, 2) * mGet(b, 1, 1) + mGet(a, 2, 2) * mGet(b, 1, 2) + mGet(a, 3, 2) * mGet(b, 1, 3))
      call mSet(r, 2, 2, mGet(a, 0, 2) * mGet(b, 2, 0) + mGet(a, 1, 2) * mGet(b, 2, 1) + mGet(a, 2, 2) * mGet(b, 2, 2) + mGet(a, 3, 2) * mGet(b, 2, 3))
      call mSet(r, 3, 2, mGet(a, 0, 2) * mGet(b, 3, 0) + mGet(a, 1, 2) * mGet(b, 3, 1) + mGet(a, 2, 2) * mGet(b, 3, 2) + mGet(a, 3, 2) * mGet(b, 3, 3))
      call mSet(r, 0, 3, mGet(a, 0, 3) * mGet(b, 0, 0) + mGet(a, 1, 3) * mGet(b, 0, 1) + mGet(a, 2, 3) * mGet(b, 0, 2) + mGet(a, 3, 3) * mGet(b, 0, 3))
      call mSet(r, 1, 3, mGet(a, 0, 3) * mGet(b, 1, 0) + mGet(a, 1, 3) * mGet(b, 1, 1) + mGet(a, 2, 3) * mGet(b, 1, 2) + mGet(a, 3, 3) * mGet(b, 1, 3))
      call mSet(r, 2, 3, mGet(a, 0, 3) * mGet(b, 2, 0) + mGet(a, 1, 3) * mGet(b, 2, 1) + mGet(a, 2, 3) * mGet(b, 2, 2) + mGet(a, 3, 3) * mGet(b, 2, 3))
      call mSet(r, 3, 3, mGet(a, 0, 3) * mGet(b, 3, 0) + mGet(a, 1, 3) * mGet(b, 3, 1) + mGet(a, 2, 3) * mGet(b, 3, 2) + mGet(a, 3, 3) * mGet(b, 3, 3))
      return r
    endfunction

    private function mMulVv takes integer m, integer vv returns integer
      local real x
      local real y
      local real z
      local real w

      set x = vvx[vv] * mGet(m, 0, 0)
      set y = vvx[vv] * mGet(m, 0, 1)
      set z = vvx[vv] * mGet(m, 0, 2)
      set w = vvx[vv] * mGet(m, 0, 3)

      set x = x + vvy[vv] * mGet(m, 1, 0)
      set y = y + vvy[vv] * mGet(m, 1, 1)
      set z = z + vvy[vv] * mGet(m, 1, 2)
      set w = w + vvy[vv] * mGet(m, 1, 3)

      set x = x + vvz[vv] * mGet(m, 2, 0)
      set y = y + vvz[vv] * mGet(m, 2, 1)
      set z = z + vvz[vv] * mGet(m, 2, 2)
      set w = w + vvz[vv] * mGet(m, 2, 3)

      set x = x + vvw[vv] * mGet(m, 3, 0)
      set y = y + vvw[vv] * mGet(m, 3, 1)
      set z = z + vvw[vv] * mGet(m, 3, 2)
      set w = w + vvw[vv] * mGet(m, 3, 3)

      return vvVec(x, y, z, w)
    endfunction

    private function getTerrainZ takes real x, real y returns real
      call MoveLocation(gLoc, x, y)
      return GetLocationZ(gLoc)
    endfunction

    private function getCameraEye takes nothing returns integer
      local real x = GetCameraEyePositionX()
      local real y = GetCameraEyePositionY()
      local real z = GetCameraEyePositionZ() // + getTerrainZ(x, y)
      return vVec(x, y, z)
    endfunction

    private function getCameraTarget takes nothing returns integer
      local real x = GetCameraTargetPositionX()
      local real y = GetCameraTargetPositionY()
      local real z = GetCameraTargetPositionZ() // + getTerrainZ(x, y)
      return vVec(x, y, z)
    endfunction

    private function absf takes real r returns real
      if r < 0 then
        return -r
      endif
      return r
    endfunction

    function GetMouseScreenXY takes real mwx, real mwy returns nothing
      local real mwz = getTerrainZ(mwx, mwy)
      local real fov = GetCameraField(CAMERA_FIELD_FIELD_OF_VIEW) // in radians
      local real aspect = 0.8 / (0.58 - 0.13)
      local real near = GetCameraField(CAMERA_FIELD_NEARZ) // default 16
      local real far = GetCameraField(CAMERA_FIELD_FARZ) // default 5000
      local integer ce
      local integer ct
      local integer up
      local integer m0
      local integer m1
      local integer vv
      local real pitch

      set ce = getCameraEye()
      set ct = getCameraTarget()
      set up = vVec(0.0, 0.0, 1.0)
      if vNearlyParallel(vSub(ct, ce), up, 1000.0) then
        set up = vVec(0.0, 1.0, 0.0)
      endif

      set m0 = mLookAt_RH(ce, ct, up)
      set m1 = mPerspective_RH_NO(fov, aspect, near, far)
      set vv = mMulVv(mMulM(m1, m0), vvVec(mwx, mwy, mwz, 1.0))
      set getMouseScreenXyResultX = vvx[vv] / vvw[vv]
      set getMouseScreenXyResultY = vvy[vv] / vvw[vv]

      set pitch = bj_RADTODEG * GetCameraField(CAMERA_FIELD_ANGLE_OF_ATTACK)
      if 89 < pitch and pitch < 269 then
        set getMouseScreenXyResultX = -getMouseScreenXyResultX
      endif
    endfunction
endlibrary
JASS:
scope Test initializer Init
    globals
        framehandle frame
    endglobals

    function OnMouseMoveOverTerrain takes nothing returns nothing
      call GetMouseScreenXY(BlzGetTriggerPlayerMouseX(), BlzGetTriggerPlayerMouseY())
      call BlzFrameSetAbsPoint(frame, FRAMEPOINT_CENTER, getMouseScreenXyResultX, getMouseScreenXyResultY)
      //call print("(" + R2SW(msx, 1, 2) + ", " + R2SW(msy, 1, 2) + ")")
    endfunction

    function Init takes nothing returns nothing
      local trigger t = CreateTrigger()
      call TriggerRegisterPlayerEvent(t, Player(0), EVENT_PLAYER_MOUSE_MOVE)
      call TriggerAddAction(t, function OnMouseMoveOverTerrain)
    
      set frame = BlzCreateFrameByType("BACKDROP", "", BlzGetFrameByName("ConsoleUIBackdrop", 0), "", 0)
      call BlzFrameSetSize(frame, 0.03, 0.03)
      call BlzFrameSetTexture(frame, "ReplaceableTextures\\CommandButtons\\BTNPeasant.blp", 0, true)
    endfunction
endscope
The custom frame does move along with the movement of the mouse, but the position of the frame is a bit off. I only tried this on flat terrain.
 
Level 18
Joined
Oct 17, 2012
Messages
821
After some testing, I was able to find a way to convert the given mouse coordinates to frame coordinates. The custom frame follows the mouse better in game than shown in the GIF. Changing the angle of attack does distort the movement a little though. Using a periodic timer with a low timeout to move the frame would also be better than doing so on a mouse move event.
MouseScreenPosition.gif
JASS:
scope Test initializer Init
    globals
        private framehandle frame
    endglobals
    
    private function screenPosX takes real x returns real
        return ((x + 0.99)*0.8) / (0.98 + 0.99)
    endfunction

    private function screenPosY takes real y returns real
        return ((y + 1.54)*(0.58))/(0.96 + 1.54)
    endfunction
 
    private function print takes string msg returns nothing
        call DisplayTimedTextToPlayer(Player(0), 0, 0, 0, msg)
    endfunction

    private function OnMouseMoveOverTerrain takes nothing returns nothing
      //local real screenX = screenPosX(BlzGetTriggerPlayerMouseX())
      //local real screenY = screenPosY(BlzGetTriggerPlayerMouseY())
      call GetMouseScreenXY(BlzGetTriggerPlayerMouseX(), BlzGetTriggerPlayerMouseY())
  
      call BlzFrameSetAbsPoint(frame, FRAMEPOINT_CENTER, screenPosX(getMouseScreenXyResultX), screenPosY(getMouseScreenXyResultY))
      //call print("(" + R2SW(getMouseScreenXyResultX, 1, 2) + ", " + R2SW(getMouseScreenXyResultY, 1, 2) + ")")
      //call print("(" + R2S(screenX) + "," + R2S(screenY) + ")")
    endfunction

    private function Init takes nothing returns nothing
      local trigger t = CreateTrigger()
      call TriggerRegisterPlayerEvent(t, Player(0), EVENT_PLAYER_MOUSE_MOVE)
      call TriggerAddAction(t, function OnMouseMoveOverTerrain)
  
      set frame = BlzCreateFrameByType("BACKDROP", "", BlzGetFrameByName("ConsoleUIBackdrop", 0), "", 0)
      call BlzFrameSetSize(frame, 0.01, 0.01)
      call BlzFrameSetTexture(frame, "ReplaceableTextures\\CommandButtons\\BTNPeasant.blp", 0, true)
      call BlzFrameSetAbsPoint(frame, FRAMEPOINT_CENTER, 0.4, 0.3)
  
      set frame = BlzCreateFrameByType("BACKDROP", "", BlzGetFrameByName("ConsoleUIBackdrop", 0), "", 0)
      call BlzFrameSetSize(frame, 0.01, 0.01)
      call BlzFrameSetTexture(frame, "ReplaceableTextures\\CommandButtons\\BTNPeasant.blp", 0, true)
      call BlzFrameSetAbsPoint(frame, FRAMEPOINT_CENTER, 0.4, 0.0)
  
      set frame = BlzCreateFrameByType("BACKDROP", "", BlzGetFrameByName("ConsoleUIBackdrop", 0), "", 0)
      call BlzFrameSetSize(frame, 0.01, 0.01)
      call BlzFrameSetTexture(frame, "ReplaceableTextures\\CommandButtons\\BTNPeasant.blp", 0, true)
      call BlzFrameSetAbsPoint(frame, FRAMEPOINT_CENTER, 0.4, 0.58)
  
      set frame = BlzCreateFrameByType("BACKDROP", "", BlzGetFrameByName("ConsoleUIBackdrop", 0), "", 0)
      call BlzFrameSetSize(frame, 0.01, 0.01)
      call BlzFrameSetTexture(frame, "ReplaceableTextures\\CommandButtons\\BTNPeasant.blp", 0, true)
      call BlzFrameSetAbsPoint(frame, FRAMEPOINT_CENTER, 0.8, 0.3)
  
      set frame = BlzCreateFrameByType("BACKDROP", "", BlzGetFrameByName("ConsoleUIBackdrop", 0), "", 0)
      call BlzFrameSetSize(frame, 0.01, 0.01)
      call BlzFrameSetTexture(frame, "ReplaceableTextures\\CommandButtons\\BTNPeasant.blp", 0, true)
      call BlzFrameSetAbsPoint(frame, FRAMEPOINT_CENTER, 0.0, 0.3)
  
      set frame = BlzCreateFrameByType("BACKDROP", "", BlzGetFrameByName("ConsoleUIBackdrop", 0), "", 0)
      call BlzFrameSetSize(frame, 0.03, 0.03)
      call BlzFrameSetTexture(frame, "ReplaceableTextures\\CommandButtons\\BTNPeasant.blp", 0, true)
      call BlzFrameSetAbsPoint(frame, FRAMEPOINT_CENTER, 0.4, 0.5)
  
      call BlzHideOriginFrames(true)
    endfunction
endscope
 
Level 3
Joined
Jun 5, 2021
Messages
12
If you use MemHackAPI( Unryze ), then it has functions to obtain the cursor coordinates (GetCursorX and GetCursorY), and then through a simple algorithm (width and height of the screen), the approximate position can be calculated. Note that GetCursorXY obtains the pixel coordinates (integer) of the screen, such as 1920*1080, the upper left corner returns 0,0, and the lower right corner returns 1920,1080.
Of course, there is a simpler and more direct method, which is to obtain the screen coordinates (UI coordinates) of the mouse from memory.
The following code allows you to simply obtain coordinates from memory:
If you are familiar with MemHackAPI, this should not be unfamiliar to you.

JASS:
function ExGetMouseScreenX takes nothing returns real
        local integer pData = ReadRealMemory( pGameUI + 0x2C )
        if pData == 0 then
            return 0.
        endif
        return ReadRealFloat( pData + 0xC0 )
endfunction
function ExGetMouseScreenY takes nothing returns real
        local integer pData = ReadRealMemory( pGameUI + 0x2C )
        if pData == 0 then
            return 0.
        endif
        return ReadRealFloat( pData + 0xC4 )
endfunction

It should look like this:

2023-11-03 04-04-39.gif
 
Last edited:
Hi,

If you use MemHackAPI( @Unryze ), then it has functions to obtain the cursor coordinates (GetCursorX and GetCursorY), and then through a simple algorithm (width and height of the screen), the approximate position can be calculated. Note that GetCursorXY obtains the pixel coordinates (integer) of the screen, such as 1920*1080, the upper left corner returns 0,0, and the lower right corner returns 1920,1080.
Of course, there is a simpler and more direct method, which is to obtain the screen coordinates (UI coordinates) of the mouse from memory.
The following code allows you to simply obtain coordinates from memory:
If you are familiar with MemHackAPI, this should not be unfamiliar to you.

JASS:
function ExGetMouseScreenX takes nothing returns real
local integer pData = ReadRealMemory( pGameUI + 0x2C )
if pData == 0 then
return 0.
endif
return ReadRealFloat( pData + 0xC0 )
endfunction
function ExGetMouseScreenY takes nothing returns real
local integer pData = ReadRealMemory( pGameUI + 0x2C )
if pData == 0 then
return 0.
endif
return ReadRealFloat( pData + 0xC4 )
endfunction
It should look like this:
Can you post your map with the system? Is it working on reforged?

Thanks
 
Top