@@ -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
8989Point = 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+ "\n You 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
92110BUTTON_LEFT = 1
93111BUTTON_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