Regions the mouse cannot be tracked:
1. The map background, which is that black (by default) void that you see, where there are no icons to be drawn e.g. past the edge of the world or by opacity.
2. Everywhere outside of the map control.
There is a decent workaround for untrackable region #1, which is used by Forum_account's Mouse Position library as well as my own Mouse and InputHandler libraries: catch the mouse with a screen object.
/* Catches the mouse over the map if nothing else does.
*/
obj/InputHandler_mouse_catcher
mouse_opacity = 2
icon = null
name = ""
plane = -100
layer = BACKGROUND_LAYER
screen_loc = "SOUTHWEST to NORTHEAST"
However, even this is not a perfect solution, because of SEE_PIXELS (which seems pretty useful otherwise):
DM Reference, "sight var (mob)":
SEE_PIXELS draws everything and then covers hidden turfs with blackness.
By "covering hidden turfs with blackness", it also "covers up" the mouse catcher, forcing the mouse to be ignored over that space.
There is one workaround that I know of for untrackable region #2, which is used by HDK Mouse And Keys: use JavaScript to track the mouse everywhere, even outside the game window. IIRC that was a security exploit and isn't actually supposed to work.
I wrote my own JavaScript script that uses edge mode for features requiring at least Internet Explorer 9.
What it does
When the mouse position over the browser changes, client.Topic() is called and provided with the position of the mouse relative to the screen and to the browser element, flipping the y-coordinate to be upward-positive and having an origin of (1, 1) in the screen/window by tradition.
It works except for a couple significant issues:
* It requires a browser control to cover the entire window, so you can't even see the map. Probably not a problem in the web client, which wouldn't require an extra browser control.
* It's in units of screen pixels instead of map pixels, but this can probably be easily accounted for.
The bare minimum for this feature
Essentially, the exact same mouse position information, without requiring the developer to create a skin with a window-filling, all-element-covering browser control.
Extra goodies that would be really nice to have
- Coordinates relative to the client's camera. This would be calculated by mapping window coordinates to scaled map coordinates, etc.
- Coordinates relative to the bottom-left of the world. I already compute this in my library by adding camera-relative coordinates to client.bound_x/y.
- A client variable that enables mouse tracking, for the bandwidth-conscious, for mouse tracking to be disabled by default.
- A client proc that is called when the mouse moves, only if mouse tracking is enabled. It should provide the coordinates as arguments; possibly along with the other MouseMove() arguments.
Please do not put it in params, forcing us to use text2num() or some regex() to parse it.
This would also allow for mouse-enter/exit events on individual elements.
The code itself:
Demo with source: https://puu.sh/qujer/2b4303d317.zip
client
var const
TOPIC_SRC = "src"
TOPIC_ACTION = "action"
TOPIC_ACTION_TRACK_MOUSE = "trackMouse"
TOPIC_MOUSE_SCREEN_X = "screenX"
TOPIC_MOUSE_SCREEN_Y = "screenY"
TOPIC_MOUSE_WINDOW_X = "windowX"
TOPIC_MOUSE_WINDOW_Y = "windowY"
var tmp
MousePosition/mouse_position
/MousePosition
var screen_x, screen_y, window_x, window_y
New()
..()
MousePosition_Initialize()
Topic(Href, HrefList[])
..()
if(HrefList[TOPIC_ACTION] == TOPIC_ACTION_TRACK_MOUSE)
mouse_position.screen_x = text2num(HrefList[TOPIC_MOUSE_SCREEN_X])
mouse_position.screen_y = text2num(HrefList[TOPIC_MOUSE_SCREEN_Y])
mouse_position.window_x = text2num(HrefList[TOPIC_MOUSE_WINDOW_X])
mouse_position.window_y = text2num(HrefList[TOPIC_MOUSE_WINDOW_Y])
src << output({"<tt>Mouse Position:<ul>
<li>Screen X: [mouse_position.screen_x]</li>
<li>Screen Y: [mouse_position.screen_y]</li>
<li>Window X: [mouse_position.window_x]</li>
<li>Window Y: [mouse_position.window_y]</li></ul></tt>"}, "mouse_tracker:output")
proc
MousePosition_Initialize()
mouse_position = new /MousePosition
src << output({"\
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
</head>
<body>
<div id=output></div>
<script>
var lastX, lastY;
addEventListener("mousemove", function(e) {
if(e.screenX != lastX || e.screenY != lastY) {
lastX = e.screenX;
lastY = e.screenY;
window.location = "byond://?\
[TOPIC_SRC]=\ref[src];\
[TOPIC_ACTION]=[TOPIC_ACTION_TRACK_MOUSE];\
[TOPIC_MOUSE_SCREEN_X]="+(1 + e.screenX)+";\
[TOPIC_MOUSE_SCREEN_Y]="+(window.screen.height - e.screenY)+";\
[TOPIC_MOUSE_WINDOW_X]="+(1 + e.clientX)+";\
[TOPIC_MOUSE_WINDOW_Y]="+(window.innerHeight - e.clientY)+";\
";
}
});
function output(html) {
document.getElementById('output').innerHTML = html;
}
</script>
</body>
</html>
"}, "mouse_tracker")
e.g.
1. double-click and hold, don't move the mouse
2. move the camera
3. move the mouse; no mouse events (i.e. MouseDrag) will be called until buttons are released.
I'm pretty sure I reported this bug already, though.
In general, it'd be great if we could check whether a certain button (keyboard button, mouse button, gamepad button) is being pressed, like what my InputHandler tries to provide but can't due to that bug.