Skip to content

Commit 1010431

Browse files
committed
Detect and warn about uninitialized tile attributes on events.
1 parent 5c39d6d commit 1010431

File tree

2 files changed

+63
-17
lines changed

2 files changed

+63
-17
lines changed

CHANGELOG.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ Unreleased
1010
------------------
1111
Changed
1212
- Now bundles SDL 2.0.14 for MacOS.
13+
- `tcod.event` can now detect and will warn about uninitialized tile
14+
attributes on mouse events.
1315

1416
Removed
1517
- Python 3.5 is no longer supported.

tcod/event.py

Lines changed: 61 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -77,17 +77,35 @@ def _describe_bitmask(
7777
return "|".join(result)
7878

7979

80-
def _pixel_to_tile(x: float, y: float) -> Tuple[float, float]:
80+
def _pixel_to_tile(x: float, y: float) -> Optional[Tuple[float, float]]:
8181
"""Convert pixel coordinates to tile coordinates."""
8282
if not lib.TCOD_ctx.engine:
83-
return 0, 0
83+
return None
8484
xy = ffi.new("double[2]", (x, y))
8585
lib.TCOD_sys_pixel_to_tile(xy, xy + 1)
8686
return xy[0], xy[1]
8787

8888

8989
Point = NamedTuple("Point", [("x", int), ("y", int)])
9090

91+
92+
def _verify_tile_coordinates(xy: Optional[Point]) -> Point:
93+
"""Check if an events tile coordinate is initialized and warn if not.
94+
95+
Always returns a valid Point object for backwards compatibility.
96+
"""
97+
if xy is not None:
98+
return xy
99+
warnings.warn(
100+
"This events tile coordinates are uninitialized!"
101+
"\nYou MUST pass this event to `Context.convert_event` before you can"
102+
" read its tile attributes.",
103+
RuntimeWarning,
104+
stacklevel=3, # Called within other functions, never directly.
105+
)
106+
return Point(0, 0)
107+
108+
91109
# manually define names for SDL macros
92110
BUTTON_LEFT = 1
93111
BUTTON_MIDDLE = 2
@@ -314,14 +332,22 @@ class MouseState(Event):
314332
def __init__(
315333
self,
316334
pixel: Tuple[int, int] = (0, 0),
317-
tile: Tuple[int, int] = (0, 0),
335+
tile: Optional[Tuple[int, int]] = (0, 0),
318336
state: int = 0,
319337
):
320338
super().__init__()
321339
self.pixel = Point(*pixel)
322-
self.tile = Point(*tile)
340+
self.__tile = Point(*tile) if tile is not None else None
323341
self.state = state
324342

343+
@property
344+
def tile(self) -> Point:
345+
return _verify_tile_coordinates(self.__tile)
346+
347+
@tile.setter
348+
def tile(self, xy: Tuple[int, int]) -> None:
349+
self.__tile = Point(*xy)
350+
325351
def __repr__(self) -> str:
326352
return ("tcod.event.%s(pixel=%r, tile=%r, state=%s)") % (
327353
self.__class__.__name__,
@@ -362,13 +388,23 @@ def __init__(
362388
self,
363389
pixel: Tuple[int, int] = (0, 0),
364390
pixel_motion: Tuple[int, int] = (0, 0),
365-
tile: Tuple[int, int] = (0, 0),
366-
tile_motion: Tuple[int, int] = (0, 0),
391+
tile: Optional[Tuple[int, int]] = (0, 0),
392+
tile_motion: Optional[Tuple[int, int]] = (0, 0),
367393
state: int = 0,
368394
):
369395
super().__init__(pixel, tile, state)
370396
self.pixel_motion = Point(*pixel_motion)
371-
self.tile_motion = Point(*tile_motion)
397+
self.__tile_motion = (
398+
Point(*tile_motion) if tile_motion is not None else None
399+
)
400+
401+
@property
402+
def tile_motion(self) -> Point:
403+
return _verify_tile_coordinates(self.__tile_motion)
404+
405+
@tile_motion.setter
406+
def tile_motion(self, xy: Tuple[int, int]) -> None:
407+
self.__tile_motion = Point(*xy)
372408

373409
@classmethod
374410
def from_sdl_event(cls, sdl_event: Any) -> "MouseMotion":
@@ -377,12 +413,15 @@ def from_sdl_event(cls, sdl_event: Any) -> "MouseMotion":
377413
pixel = motion.x, motion.y
378414
pixel_motion = motion.xrel, motion.yrel
379415
subtile = _pixel_to_tile(*pixel)
380-
tile = int(subtile[0]), int(subtile[1])
381-
prev_pixel = pixel[0] - pixel_motion[0], pixel[1] - pixel_motion[1]
382-
prev_subtile = _pixel_to_tile(*prev_pixel)
383-
prev_tile = int(prev_subtile[0]), int(prev_subtile[1])
384-
tile_motion = tile[0] - prev_tile[0], tile[1] - prev_tile[1]
385-
self = cls(pixel, pixel_motion, tile, tile_motion, motion.state)
416+
if subtile is None:
417+
self = cls(pixel, pixel_motion, None, None, motion.state)
418+
else:
419+
tile = int(subtile[0]), int(subtile[1])
420+
prev_pixel = pixel[0] - pixel_motion[0], pixel[1] - pixel_motion[1]
421+
prev_subtile = _pixel_to_tile(*prev_pixel) or (0, 0)
422+
prev_tile = int(prev_subtile[0]), int(prev_subtile[1])
423+
tile_motion = tile[0] - prev_tile[0], tile[1] - prev_tile[1]
424+
self = cls(pixel, pixel_motion, tile, tile_motion, motion.state)
386425
self.sdl_event = sdl_event
387426
return self
388427

@@ -430,7 +469,7 @@ class MouseButtonEvent(MouseState):
430469
def __init__(
431470
self,
432471
pixel: Tuple[int, int] = (0, 0),
433-
tile: Tuple[int, int] = (0, 0),
472+
tile: Optional[Tuple[int, int]] = (0, 0),
434473
button: int = 0,
435474
):
436475
super().__init__(pixel, tile, button)
@@ -448,7 +487,10 @@ def from_sdl_event(cls, sdl_event: Any) -> Any:
448487
button = sdl_event.button
449488
pixel = button.x, button.y
450489
subtile = _pixel_to_tile(*pixel)
451-
tile = int(subtile[0]), int(subtile[1])
490+
if subtile is None:
491+
tile: Optional[Tuple[int, int]] = None
492+
else:
493+
tile = int(subtile[0]), int(subtile[1])
452494
self = cls(pixel, tile, button.button)
453495
self.sdl_event = sdl_event
454496
return self
@@ -993,8 +1035,10 @@ def get_mouse_state() -> MouseState:
9931035
"""
9941036
xy = ffi.new("int[2]")
9951037
buttons = lib.SDL_GetMouseState(xy, xy + 1)
996-
x, y = _pixel_to_tile(*xy)
997-
return MouseState((xy[0], xy[1]), (int(x), int(y)), buttons)
1038+
tile = _pixel_to_tile(*xy)
1039+
if tile is None:
1040+
return MouseState((xy[0], xy[1]), None, buttons)
1041+
return MouseState((xy[0], xy[1]), (int(tile[0]), int(tile[1])), buttons)
9981042

9991043

10001044
@ffi.def_extern() # type: ignore

0 commit comments

Comments
 (0)