Fix #161: Update Grid, GridPoint, GridPointState stubs to match current API

- Grid: Update constructor (pos, size, grid_size, texture, ...) and add all
  current properties (zoom, center, layers, FOV, cell events, etc.)
- Grid: Add all methods (find_path, compute_fov, add_layer, entities_in_radius, etc.)
- GridPoint: Replace incorrect properties (texture_index, solid, color) with
  actual API (walkable, transparent, entities, grid_pos)
- GridPointState: Replace incorrect properties with actual API (visible, discovered, point)
- Add missing types: ColorLayer, TileLayer, FOV, AStarPath, DijkstraMap,
  HeightMap, NoiseSource, BSP

Closes #161

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
John McCardle 2026-01-21 21:47:26 -05:00
commit 4be2502a10
3 changed files with 1120 additions and 111 deletions

View file

@ -6,7 +6,7 @@ Core game engine interface for creating roguelike games with Python.
from typing import Any, List, Dict, Tuple, Optional, Callable, Union, overload from typing import Any, List, Dict, Tuple, Optional, Callable, Union, overload
# Type aliases # Type aliases
UIElement = Union['Frame', 'Caption', 'Sprite', 'Grid'] UIElement = Union['Frame', 'Caption', 'Sprite', 'Grid', 'Line', 'Circle', 'Arc']
Transition = Union[str, None] Transition = Union[str, None]
# Classes # Classes
@ -83,6 +83,15 @@ class Drawable:
name: str name: str
pos: Vector pos: Vector
# Mouse event callbacks (#140, #141)
on_click: Optional[Callable[[float, float, int, str], None]]
on_enter: Optional[Callable[[float, float, int, str], None]]
on_exit: Optional[Callable[[float, float, int, str], None]]
on_move: Optional[Callable[[float, float, int, str], None]]
# Read-only hover state (#140)
hovered: bool
def get_bounds(self) -> Tuple[float, float, float, float]: def get_bounds(self) -> Tuple[float, float, float, float]:
"""Get bounding box as (x, y, width, height).""" """Get bounding box as (x, y, width, height)."""
... ...
@ -96,7 +105,7 @@ class Drawable:
... ...
class Frame(Drawable): class Frame(Drawable):
"""Frame(x=0, y=0, w=0, h=0, fill_color=None, outline_color=None, outline=0, click=None, children=None) """Frame(x=0, y=0, w=0, h=0, fill_color=None, outline_color=None, outline=0, on_click=None, children=None)
A rectangular frame UI element that can contain other drawable elements. A rectangular frame UI element that can contain other drawable elements.
""" """
@ -106,7 +115,7 @@ class Frame(Drawable):
@overload @overload
def __init__(self, x: float = 0, y: float = 0, w: float = 0, h: float = 0, def __init__(self, x: float = 0, y: float = 0, w: float = 0, h: float = 0,
fill_color: Optional[Color] = None, outline_color: Optional[Color] = None, fill_color: Optional[Color] = None, outline_color: Optional[Color] = None,
outline: float = 0, click: Optional[Callable] = None, outline: float = 0, on_click: Optional[Callable] = None,
children: Optional[List[UIElement]] = None) -> None: ... children: Optional[List[UIElement]] = None) -> None: ...
w: float w: float
@ -114,12 +123,12 @@ class Frame(Drawable):
fill_color: Color fill_color: Color
outline_color: Color outline_color: Color
outline: float outline: float
click: Optional[Callable[[float, float, int], None]] on_click: Optional[Callable[[float, float, int], None]]
children: 'UICollection' children: 'UICollection'
clip_children: bool clip_children: bool
class Caption(Drawable): class Caption(Drawable):
"""Caption(text='', x=0, y=0, font=None, fill_color=None, outline_color=None, outline=0, click=None) """Caption(text='', x=0, y=0, font=None, fill_color=None, outline_color=None, outline=0, on_click=None)
A text display UI element with customizable font and styling. A text display UI element with customizable font and styling.
""" """
@ -130,19 +139,19 @@ class Caption(Drawable):
def __init__(self, text: str = '', x: float = 0, y: float = 0, def __init__(self, text: str = '', x: float = 0, y: float = 0,
font: Optional[Font] = None, fill_color: Optional[Color] = None, font: Optional[Font] = None, fill_color: Optional[Color] = None,
outline_color: Optional[Color] = None, outline: float = 0, outline_color: Optional[Color] = None, outline: float = 0,
click: Optional[Callable] = None) -> None: ... on_click: Optional[Callable] = None) -> None: ...
text: str text: str
font: Font font: Font
fill_color: Color fill_color: Color
outline_color: Color outline_color: Color
outline: float outline: float
click: Optional[Callable[[float, float, int], None]] on_click: Optional[Callable[[float, float, int], None]]
w: float # Read-only, computed from text w: float # Read-only, computed from text
h: float # Read-only, computed from text h: float # Read-only, computed from text
class Sprite(Drawable): class Sprite(Drawable):
"""Sprite(x=0, y=0, texture=None, sprite_index=0, scale=1.0, click=None) """Sprite(x=0, y=0, texture=None, sprite_index=0, scale=1.0, on_click=None)
A sprite UI element that displays a texture or portion of a texture atlas. A sprite UI element that displays a texture or portion of a texture atlas.
""" """
@ -152,54 +161,428 @@ class Sprite(Drawable):
@overload @overload
def __init__(self, x: float = 0, y: float = 0, texture: Optional[Texture] = None, def __init__(self, x: float = 0, y: float = 0, texture: Optional[Texture] = None,
sprite_index: int = 0, scale: float = 1.0, sprite_index: int = 0, scale: float = 1.0,
click: Optional[Callable] = None) -> None: ... on_click: Optional[Callable] = None) -> None: ...
texture: Texture texture: Texture
sprite_index: int sprite_index: int
scale: float scale: float
click: Optional[Callable[[float, float, int], None]] on_click: Optional[Callable[[float, float, int], None]]
w: float # Read-only, computed from texture w: float # Read-only, computed from texture
h: float # Read-only, computed from texture h: float # Read-only, computed from texture
class Grid(Drawable): class Grid(Drawable):
"""Grid(x=0, y=0, grid_size=(20, 20), texture=None, tile_width=16, tile_height=16, scale=1.0, click=None) """Grid(pos=(0,0), size=(0,0), grid_size=(2,2), texture=None, ...)
A grid-based tilemap UI element for rendering tile-based levels and game worlds. A grid-based tilemap UI element for rendering tile-based levels and game worlds.
Supports layers, FOV, pathfinding, and entity management.
""" """
@overload @overload
def __init__(self) -> None: ... def __init__(self) -> None: ...
@overload @overload
def __init__(self, x: float = 0, y: float = 0, grid_size: Tuple[int, int] = (20, 20), def __init__(self, pos: Tuple[float, float] = (0, 0),
texture: Optional[Texture] = None, tile_width: int = 16, tile_height: int = 16, size: Tuple[float, float] = (0, 0),
scale: float = 1.0, click: Optional[Callable] = None) -> None: ... grid_size: Tuple[int, int] = (2, 2),
texture: Optional[Texture] = None,
fill_color: Optional[Color] = None,
on_click: Optional[Callable] = None,
center_x: float = 0, center_y: float = 0, zoom: float = 1.0,
visible: bool = True, opacity: float = 1.0,
z_index: int = 0, name: str = '') -> None: ...
grid_size: Tuple[int, int] # Dimensions
tile_width: int grid_size: Tuple[int, int] # Read-only (grid_w, grid_h)
tile_height: int grid_w: int # Read-only
texture: Texture grid_h: int # Read-only
scale: float
points: List[List['GridPoint']] # Position and size
entities: 'EntityCollection' position: Tuple[float, float]
background_color: Color size: Vector
click: Optional[Callable[[int, int, int], None]] w: float
h: float
# Camera/viewport
center: Vector # Viewport center point (pan position)
center_x: float
center_y: float
zoom: float # Scale factor for rendering
# Collections
entities: 'EntityCollection' # Entities on this grid
children: 'UICollection' # UI overlays (speech bubbles, effects)
layers: List[Union['ColorLayer', 'TileLayer']] # Grid layers sorted by z_index
# Appearance
texture: Texture # Read-only
fill_color: Color # Background fill color
# Perspective/FOV
perspective: Optional['Entity'] # Entity for FOV rendering (None = omniscient)
perspective_enabled: bool # Whether to use perspective-based FOV
fov: 'FOV' # FOV algorithm enum
fov_radius: int # Default FOV radius
# Cell-level mouse events
on_cell_enter: Optional[Callable[['Vector'], None]]
on_cell_exit: Optional[Callable[['Vector'], None]]
on_cell_click: Optional[Callable[['Vector'], None]]
hovered_cell: Optional[Tuple[int, int]] # Read-only
def at(self, x: int, y: int) -> 'GridPoint': def at(self, x: int, y: int) -> 'GridPoint':
"""Get grid point at tile coordinates.""" """Get grid point at tile coordinates."""
... ...
class GridPoint: def center_camera(self, pos: Optional[Tuple[float, float]] = None) -> None:
"""Grid point representing a single tile.""" """Center the camera on a tile coordinate."""
...
texture_index: int # FOV methods
solid: bool def compute_fov(self, pos: Tuple[int, int], radius: int = 0,
light_walls: bool = True, algorithm: Optional['FOV'] = None) -> None:
"""Compute field of view from a position."""
...
def is_in_fov(self, pos: Tuple[int, int]) -> bool:
"""Check if a cell is in the field of view."""
...
# Pathfinding methods
def find_path(self, start: Union[Tuple[int, int], 'Vector', 'Entity'],
end: Union[Tuple[int, int], 'Vector', 'Entity'],
diagonal_cost: float = 1.41) -> Optional['AStarPath']:
"""Compute A* path between two points."""
...
def get_dijkstra_map(self, root: Union[Tuple[int, int], 'Vector', 'Entity'],
diagonal_cost: float = 1.41) -> 'DijkstraMap':
"""Get or create a Dijkstra distance map for a root position."""
...
def clear_dijkstra_maps(self) -> None:
"""Clear all cached Dijkstra maps."""
...
# Layer methods
def add_layer(self, type: str, z_index: int = -1,
texture: Optional[Texture] = None) -> Union['ColorLayer', 'TileLayer']:
"""Add a new layer to the grid."""
...
def remove_layer(self, layer: Union['ColorLayer', 'TileLayer']) -> None:
"""Remove a layer from the grid."""
...
def layer(self, z_index: int) -> Optional[Union['ColorLayer', 'TileLayer']]:
"""Get layer by z_index."""
...
# Spatial queries
def entities_in_radius(self, pos: Union[Tuple[float, float], 'Vector'],
radius: float) -> List['Entity']:
"""Query entities within radius using spatial hash."""
...
# HeightMap application
def apply_threshold(self, source: 'HeightMap', range: Tuple[float, float],
walkable: Optional[bool] = None,
transparent: Optional[bool] = None) -> 'Grid':
"""Apply walkable/transparent properties where heightmap values are in range."""
...
def apply_ranges(self, source: 'HeightMap',
ranges: List[Tuple[Tuple[float, float], Dict[str, bool]]]) -> 'Grid':
"""Apply multiple thresholds in a single pass."""
...
class Line(Drawable):
"""Line(start=None, end=None, thickness=1.0, color=None, on_click=None, **kwargs)
A line UI element for drawing straight lines between two points.
"""
@overload
def __init__(self) -> None: ...
@overload
def __init__(self, start: Optional[Tuple[float, float]] = None,
end: Optional[Tuple[float, float]] = None,
thickness: float = 1.0, color: Optional[Color] = None,
on_click: Optional[Callable] = None) -> None: ...
start: Vector
end: Vector
thickness: float
color: Color color: Color
on_click: Optional[Callable[[float, float, int], None]]
class Circle(Drawable):
"""Circle(radius=0, center=None, fill_color=None, outline_color=None, outline=0, on_click=None, **kwargs)
A circle UI element for drawing filled or outlined circles.
"""
@overload
def __init__(self) -> None: ...
@overload
def __init__(self, radius: float = 0, center: Optional[Tuple[float, float]] = None,
fill_color: Optional[Color] = None, outline_color: Optional[Color] = None,
outline: float = 0, on_click: Optional[Callable] = None) -> None: ...
radius: float
center: Vector
fill_color: Color
outline_color: Color
outline: float
on_click: Optional[Callable[[float, float, int], None]]
class Arc(Drawable):
"""Arc(center=None, radius=0, start_angle=0, end_angle=90, color=None, thickness=1, on_click=None, **kwargs)
An arc UI element for drawing curved line segments.
"""
@overload
def __init__(self) -> None: ...
@overload
def __init__(self, center: Optional[Tuple[float, float]] = None, radius: float = 0,
start_angle: float = 0, end_angle: float = 90,
color: Optional[Color] = None, thickness: float = 1.0,
on_click: Optional[Callable] = None) -> None: ...
center: Vector
radius: float
start_angle: float
end_angle: float
color: Color
thickness: float
on_click: Optional[Callable[[float, float, int], None]]
class GridPoint:
"""Grid point representing a single tile's properties.
Accessed via Grid.at(x, y). Controls walkability and transparency
for pathfinding and FOV calculations.
"""
walkable: bool # Whether entities can walk through this cell
transparent: bool # Whether light/sight passes through this cell
entities: List['Entity'] # Read-only list of entities at this cell
grid_pos: Tuple[int, int] # Read-only (x, y) position in grid
class GridPointState: class GridPointState:
"""State information for a grid point.""" """Per-entity visibility state for a grid cell.
texture_index: int Tracks what an entity has seen/discovered. Accessed via entity perspective system.
color: Color """
visible: bool # Currently visible in FOV
discovered: bool # Has been seen at least once
point: Optional['GridPoint'] # The GridPoint at this position (None if not discovered)
class ColorLayer:
"""A color overlay layer for Grid.
Provides per-cell color values for tinting, fog of war, etc.
"""
z_index: int
grid: 'Grid' # Read-only parent grid
def fill(self, color: Color) -> None:
"""Fill entire layer with a single color."""
...
def set_color(self, pos: Tuple[int, int], color: Color) -> None:
"""Set color at a specific cell."""
...
def get_color(self, pos: Tuple[int, int]) -> Color:
"""Get color at a specific cell."""
...
class TileLayer:
"""A tile sprite layer for Grid.
Provides per-cell tile indices for multi-layer tile rendering.
"""
z_index: int
grid: 'Grid' # Read-only parent grid
texture: Optional[Texture]
def fill(self, tile_index: int) -> None:
"""Fill entire layer with a single tile index."""
...
def set_tile(self, pos: Tuple[int, int], tile_index: int) -> None:
"""Set tile index at a specific cell."""
...
def get_tile(self, pos: Tuple[int, int]) -> int:
"""Get tile index at a specific cell."""
...
class FOV:
"""Field of view algorithm enum.
Available algorithms:
- FOV.BASIC: Simple raycasting
- FOV.DIAMOND: Diamond-shaped FOV
- FOV.SHADOW: Shadow casting (recommended)
- FOV.PERMISSIVE_0 through FOV.PERMISSIVE_8: Permissive algorithms
- FOV.RESTRICTIVE: Restrictive precise angle shadowcasting
"""
BASIC: 'FOV'
DIAMOND: 'FOV'
SHADOW: 'FOV'
PERMISSIVE_0: 'FOV'
PERMISSIVE_1: 'FOV'
PERMISSIVE_2: 'FOV'
PERMISSIVE_3: 'FOV'
PERMISSIVE_4: 'FOV'
PERMISSIVE_5: 'FOV'
PERMISSIVE_6: 'FOV'
PERMISSIVE_7: 'FOV'
PERMISSIVE_8: 'FOV'
RESTRICTIVE: 'FOV'
class AStarPath:
"""A* pathfinding result.
Returned by Grid.find_path(). Can be iterated or walked step-by-step.
"""
def __iter__(self) -> Any: ...
def __len__(self) -> int: ...
def walk(self) -> Optional[Tuple[int, int]]:
"""Get next step in path, or None if complete."""
...
def reverse(self) -> 'AStarPath':
"""Return a reversed copy of the path."""
...
class DijkstraMap:
"""Dijkstra distance map for pathfinding.
Created by Grid.get_dijkstra_map(). Provides distance queries
and path finding from the root position.
"""
root: Tuple[int, int] # Read-only root position
def get_distance(self, pos: Tuple[int, int]) -> float:
"""Get distance from root to position (-1 if unreachable)."""
...
def get_path(self, pos: Tuple[int, int]) -> Optional[List[Tuple[int, int]]]:
"""Get path from position to root."""
...
class HeightMap:
"""2D height field for terrain generation.
Used for procedural generation and applying terrain to grids.
"""
width: int # Read-only
height: int # Read-only
def __init__(self, width: int, height: int) -> None: ...
def get(self, x: int, y: int) -> float:
"""Get height value at position."""
...
def set(self, x: int, y: int, value: float) -> None:
"""Set height value at position."""
...
def fill(self, value: float) -> 'HeightMap':
"""Fill entire heightmap with a value."""
...
def clear(self) -> 'HeightMap':
"""Clear heightmap to 0."""
...
def normalize(self, min_val: float = 0.0, max_val: float = 1.0) -> 'HeightMap':
"""Normalize values to range."""
...
def add_hill(self, center: Tuple[float, float], radius: float, height: float) -> 'HeightMap':
"""Add a hill at position."""
...
def add_fbm(self, noise: 'NoiseSource', mulx: float = 1.0, muly: float = 1.0,
addx: float = 0.0, addy: float = 0.0, octaves: int = 4,
delta: float = 1.0, scale: float = 1.0) -> 'HeightMap':
"""Add fractal Brownian motion noise."""
...
def scale(self, factor: float) -> 'HeightMap':
"""Scale all values by factor."""
...
def clamp(self, min_val: float, max_val: float) -> 'HeightMap':
"""Clamp values to range."""
...
class NoiseSource:
"""Coherent noise generator for procedural generation.
Supports various noise types: PERLIN, SIMPLEX, WAVELET, etc.
"""
def __init__(self, type: str = 'SIMPLEX', seed: Optional[int] = None) -> None: ...
def get(self, x: float, y: float, z: float = 0.0) -> float:
"""Get noise value at position."""
...
class BSP:
"""Binary space partitioning for dungeon generation.
Recursively subdivides a rectangle into rooms.
"""
x: int
y: int
width: int
height: int
level: int
horizontal: bool
position: int
def __init__(self, x: int, y: int, width: int, height: int) -> None: ...
def split_recursive(self, randomizer: Optional[Any] = None, nb: int = 8,
minHSize: int = 4, minVSize: int = 4,
maxHRatio: float = 1.5, maxVRatio: float = 1.5) -> None:
"""Recursively split the BSP tree."""
...
def traverse(self, callback: Callable[['BSP'], bool],
order: str = 'PRE_ORDER') -> None:
"""Traverse BSP tree calling callback for each node."""
...
def is_leaf(self) -> bool:
"""Check if this is a leaf node (no children)."""
...
def contains(self, x: int, y: int) -> bool:
"""Check if point is within this node's bounds."""
...
def get_left(self) -> Optional['BSP']:
"""Get left child node."""
...
def get_right(self) -> Optional['BSP']:
"""Get right child node."""
...
class Entity(Drawable): class Entity(Drawable):
"""Entity(grid_x=0, grid_y=0, texture=None, sprite_index=0, name='') """Entity(grid_x=0, grid_y=0, texture=None, sprite_index=0, name='')
@ -232,7 +615,7 @@ class Entity(Drawable):
... ...
class UICollection: class UICollection:
"""Collection of UI drawable elements (Frame, Caption, Sprite, Grid).""" """Collection of UI drawable elements (Frame, Caption, Sprite, Grid, Line, Circle, Arc)."""
def __len__(self) -> int: ... def __len__(self) -> int: ...
def __getitem__(self, index: int) -> UIElement: ... def __getitem__(self, index: int) -> UIElement: ...
@ -271,6 +654,8 @@ class Scene:
"""Base class for object-oriented scenes.""" """Base class for object-oriented scenes."""
name: str name: str
children: UICollection # #151: UI elements collection (read-only alias for get_ui())
on_key: Optional[Callable[[str, str], None]] # Keyboard handler (key, action)
def __init__(self, name: str) -> None: ... def __init__(self, name: str) -> None: ...
@ -287,27 +672,27 @@ class Scene:
... ...
def on_keypress(self, key: str, pressed: bool) -> None: def on_keypress(self, key: str, pressed: bool) -> None:
"""Handle keyboard events.""" """Handle keyboard events (override in subclass)."""
... ...
def on_click(self, x: float, y: float, button: int) -> None: def on_click(self, x: float, y: float, button: int) -> None:
"""Handle mouse clicks.""" """Handle mouse clicks (override in subclass)."""
... ...
def on_enter(self) -> None: def on_enter(self) -> None:
"""Called when entering the scene.""" """Called when entering the scene (override in subclass)."""
... ...
def on_exit(self) -> None: def on_exit(self) -> None:
"""Called when leaving the scene.""" """Called when leaving the scene (override in subclass)."""
... ...
def on_resize(self, width: int, height: int) -> None: def on_resize(self, width: int, height: int) -> None:
"""Handle window resize events.""" """Handle window resize events (override in subclass)."""
... ...
def update(self, dt: float) -> None: def update(self, dt: float) -> None:
"""Update scene logic.""" """Update scene logic (override in subclass)."""
... ...
class Timer: class Timer:

View file

@ -171,32 +171,129 @@ class Sprite(Drawable):
h: float # Read-only, computed from texture h: float # Read-only, computed from texture
class Grid(Drawable): class Grid(Drawable):
"""Grid(x=0, y=0, grid_size=(20, 20), texture=None, tile_width=16, tile_height=16, scale=1.0, on_click=None) """Grid(pos=(0,0), size=(0,0), grid_size=(2,2), texture=None, ...)
A grid-based tilemap UI element for rendering tile-based levels and game worlds. A grid-based tilemap UI element for rendering tile-based levels and game worlds.
Supports layers, FOV, pathfinding, and entity management.
""" """
@overload @overload
def __init__(self) -> None: ... def __init__(self) -> None: ...
@overload @overload
def __init__(self, x: float = 0, y: float = 0, grid_size: Tuple[int, int] = (20, 20), def __init__(self, pos: Tuple[float, float] = (0, 0),
texture: Optional[Texture] = None, tile_width: int = 16, tile_height: int = 16, size: Tuple[float, float] = (0, 0),
scale: float = 1.0, on_click: Optional[Callable] = None) -> None: ... grid_size: Tuple[int, int] = (2, 2),
texture: Optional[Texture] = None,
fill_color: Optional[Color] = None,
on_click: Optional[Callable] = None,
center_x: float = 0, center_y: float = 0, zoom: float = 1.0,
visible: bool = True, opacity: float = 1.0,
z_index: int = 0, name: str = '') -> None: ...
grid_size: Tuple[int, int] # Dimensions
tile_width: int grid_size: Tuple[int, int] # Read-only (grid_w, grid_h)
tile_height: int grid_w: int # Read-only
texture: Texture grid_h: int # Read-only
scale: float
points: List[List['GridPoint']] # Position and size
entities: 'EntityCollection' position: Tuple[float, float]
background_color: Color size: Vector
on_click: Optional[Callable[[int, int, int], None]] w: float
h: float
# Camera/viewport
center: Vector # Viewport center point (pan position)
center_x: float
center_y: float
zoom: float # Scale factor for rendering
# Collections
entities: 'EntityCollection' # Entities on this grid
children: 'UICollection' # UI overlays (speech bubbles, effects)
layers: List[Union['ColorLayer', 'TileLayer']] # Grid layers sorted by z_index
# Appearance
texture: Texture # Read-only
fill_color: Color # Background fill color
# Perspective/FOV
perspective: Optional['Entity'] # Entity for FOV rendering (None = omniscient)
perspective_enabled: bool # Whether to use perspective-based FOV
fov: 'FOV' # FOV algorithm enum
fov_radius: int # Default FOV radius
# Cell-level mouse events
on_cell_enter: Optional[Callable[['Vector'], None]]
on_cell_exit: Optional[Callable[['Vector'], None]]
on_cell_click: Optional[Callable[['Vector'], None]]
hovered_cell: Optional[Tuple[int, int]] # Read-only
def at(self, x: int, y: int) -> 'GridPoint': def at(self, x: int, y: int) -> 'GridPoint':
"""Get grid point at tile coordinates.""" """Get grid point at tile coordinates."""
... ...
def center_camera(self, pos: Optional[Tuple[float, float]] = None) -> None:
"""Center the camera on a tile coordinate."""
...
# FOV methods
def compute_fov(self, pos: Tuple[int, int], radius: int = 0,
light_walls: bool = True, algorithm: Optional['FOV'] = None) -> None:
"""Compute field of view from a position."""
...
def is_in_fov(self, pos: Tuple[int, int]) -> bool:
"""Check if a cell is in the field of view."""
...
# Pathfinding methods
def find_path(self, start: Union[Tuple[int, int], 'Vector', 'Entity'],
end: Union[Tuple[int, int], 'Vector', 'Entity'],
diagonal_cost: float = 1.41) -> Optional['AStarPath']:
"""Compute A* path between two points."""
...
def get_dijkstra_map(self, root: Union[Tuple[int, int], 'Vector', 'Entity'],
diagonal_cost: float = 1.41) -> 'DijkstraMap':
"""Get or create a Dijkstra distance map for a root position."""
...
def clear_dijkstra_maps(self) -> None:
"""Clear all cached Dijkstra maps."""
...
# Layer methods
def add_layer(self, type: str, z_index: int = -1,
texture: Optional[Texture] = None) -> Union['ColorLayer', 'TileLayer']:
"""Add a new layer to the grid."""
...
def remove_layer(self, layer: Union['ColorLayer', 'TileLayer']) -> None:
"""Remove a layer from the grid."""
...
def layer(self, z_index: int) -> Optional[Union['ColorLayer', 'TileLayer']]:
"""Get layer by z_index."""
...
# Spatial queries
def entities_in_radius(self, pos: Union[Tuple[float, float], 'Vector'],
radius: float) -> List['Entity']:
"""Query entities within radius using spatial hash."""
...
# HeightMap application
def apply_threshold(self, source: 'HeightMap', range: Tuple[float, float],
walkable: Optional[bool] = None,
transparent: Optional[bool] = None) -> 'Grid':
"""Apply walkable/transparent properties where heightmap values are in range."""
...
def apply_ranges(self, source: 'HeightMap',
ranges: List[Tuple[Tuple[float, float], Dict[str, bool]]]) -> 'Grid':
"""Apply multiple thresholds in a single pass."""
...
class Line(Drawable): class Line(Drawable):
"""Line(start=None, end=None, thickness=1.0, color=None, on_click=None, **kwargs) """Line(start=None, end=None, thickness=1.0, color=None, on_click=None, **kwargs)
@ -260,17 +357,232 @@ class Arc(Drawable):
on_click: Optional[Callable[[float, float, int], None]] on_click: Optional[Callable[[float, float, int], None]]
class GridPoint: class GridPoint:
"""Grid point representing a single tile.""" """Grid point representing a single tile's properties.
texture_index: int Accessed via Grid.at(x, y). Controls walkability and transparency
solid: bool for pathfinding and FOV calculations.
color: Color """
walkable: bool # Whether entities can walk through this cell
transparent: bool # Whether light/sight passes through this cell
entities: List['Entity'] # Read-only list of entities at this cell
grid_pos: Tuple[int, int] # Read-only (x, y) position in grid
class GridPointState: class GridPointState:
"""State information for a grid point.""" """Per-entity visibility state for a grid cell.
texture_index: int Tracks what an entity has seen/discovered. Accessed via entity perspective system.
color: Color """
visible: bool # Currently visible in FOV
discovered: bool # Has been seen at least once
point: Optional['GridPoint'] # The GridPoint at this position (None if not discovered)
class ColorLayer:
"""A color overlay layer for Grid.
Provides per-cell color values for tinting, fog of war, etc.
"""
z_index: int
grid: 'Grid' # Read-only parent grid
def fill(self, color: Color) -> None:
"""Fill entire layer with a single color."""
...
def set_color(self, pos: Tuple[int, int], color: Color) -> None:
"""Set color at a specific cell."""
...
def get_color(self, pos: Tuple[int, int]) -> Color:
"""Get color at a specific cell."""
...
class TileLayer:
"""A tile sprite layer for Grid.
Provides per-cell tile indices for multi-layer tile rendering.
"""
z_index: int
grid: 'Grid' # Read-only parent grid
texture: Optional[Texture]
def fill(self, tile_index: int) -> None:
"""Fill entire layer with a single tile index."""
...
def set_tile(self, pos: Tuple[int, int], tile_index: int) -> None:
"""Set tile index at a specific cell."""
...
def get_tile(self, pos: Tuple[int, int]) -> int:
"""Get tile index at a specific cell."""
...
class FOV:
"""Field of view algorithm enum.
Available algorithms:
- FOV.BASIC: Simple raycasting
- FOV.DIAMOND: Diamond-shaped FOV
- FOV.SHADOW: Shadow casting (recommended)
- FOV.PERMISSIVE_0 through FOV.PERMISSIVE_8: Permissive algorithms
- FOV.RESTRICTIVE: Restrictive precise angle shadowcasting
"""
BASIC: 'FOV'
DIAMOND: 'FOV'
SHADOW: 'FOV'
PERMISSIVE_0: 'FOV'
PERMISSIVE_1: 'FOV'
PERMISSIVE_2: 'FOV'
PERMISSIVE_3: 'FOV'
PERMISSIVE_4: 'FOV'
PERMISSIVE_5: 'FOV'
PERMISSIVE_6: 'FOV'
PERMISSIVE_7: 'FOV'
PERMISSIVE_8: 'FOV'
RESTRICTIVE: 'FOV'
class AStarPath:
"""A* pathfinding result.
Returned by Grid.find_path(). Can be iterated or walked step-by-step.
"""
def __iter__(self) -> Any: ...
def __len__(self) -> int: ...
def walk(self) -> Optional[Tuple[int, int]]:
"""Get next step in path, or None if complete."""
...
def reverse(self) -> 'AStarPath':
"""Return a reversed copy of the path."""
...
class DijkstraMap:
"""Dijkstra distance map for pathfinding.
Created by Grid.get_dijkstra_map(). Provides distance queries
and path finding from the root position.
"""
root: Tuple[int, int] # Read-only root position
def get_distance(self, pos: Tuple[int, int]) -> float:
"""Get distance from root to position (-1 if unreachable)."""
...
def get_path(self, pos: Tuple[int, int]) -> Optional[List[Tuple[int, int]]]:
"""Get path from position to root."""
...
class HeightMap:
"""2D height field for terrain generation.
Used for procedural generation and applying terrain to grids.
"""
width: int # Read-only
height: int # Read-only
def __init__(self, width: int, height: int) -> None: ...
def get(self, x: int, y: int) -> float:
"""Get height value at position."""
...
def set(self, x: int, y: int, value: float) -> None:
"""Set height value at position."""
...
def fill(self, value: float) -> 'HeightMap':
"""Fill entire heightmap with a value."""
...
def clear(self) -> 'HeightMap':
"""Clear heightmap to 0."""
...
def normalize(self, min_val: float = 0.0, max_val: float = 1.0) -> 'HeightMap':
"""Normalize values to range."""
...
def add_hill(self, center: Tuple[float, float], radius: float, height: float) -> 'HeightMap':
"""Add a hill at position."""
...
def add_fbm(self, noise: 'NoiseSource', mulx: float = 1.0, muly: float = 1.0,
addx: float = 0.0, addy: float = 0.0, octaves: int = 4,
delta: float = 1.0, scale: float = 1.0) -> 'HeightMap':
"""Add fractal Brownian motion noise."""
...
def scale(self, factor: float) -> 'HeightMap':
"""Scale all values by factor."""
...
def clamp(self, min_val: float, max_val: float) -> 'HeightMap':
"""Clamp values to range."""
...
class NoiseSource:
"""Coherent noise generator for procedural generation.
Supports various noise types: PERLIN, SIMPLEX, WAVELET, etc.
"""
def __init__(self, type: str = 'SIMPLEX', seed: Optional[int] = None) -> None: ...
def get(self, x: float, y: float, z: float = 0.0) -> float:
"""Get noise value at position."""
...
class BSP:
"""Binary space partitioning for dungeon generation.
Recursively subdivides a rectangle into rooms.
"""
x: int
y: int
width: int
height: int
level: int
horizontal: bool
position: int
def __init__(self, x: int, y: int, width: int, height: int) -> None: ...
def split_recursive(self, randomizer: Optional[Any] = None, nb: int = 8,
minHSize: int = 4, minVSize: int = 4,
maxHRatio: float = 1.5, maxVRatio: float = 1.5) -> None:
"""Recursively split the BSP tree."""
...
def traverse(self, callback: Callable[['BSP'], bool],
order: str = 'PRE_ORDER') -> None:
"""Traverse BSP tree calling callback for each node."""
...
def is_leaf(self) -> bool:
"""Check if this is a leaf node (no children)."""
...
def contains(self, x: int, y: int) -> bool:
"""Check if point is within this node's bounds."""
...
def get_left(self) -> Optional['BSP']:
"""Get left child node."""
...
def get_right(self) -> Optional['BSP']:
"""Get right child node."""
...
class Entity(Drawable): class Entity(Drawable):
"""Entity(grid_x=0, grid_y=0, texture=None, sprite_index=0, name='') """Entity(grid_x=0, grid_y=0, texture=None, sprite_index=0, name='')

View file

@ -183,32 +183,129 @@ class Sprite(Drawable):
h: float # Read-only, computed from texture h: float # Read-only, computed from texture
class Grid(Drawable): class Grid(Drawable):
"""Grid(x=0, y=0, grid_size=(20, 20), texture=None, tile_width=16, tile_height=16, scale=1.0, on_click=None) """Grid(pos=(0,0), size=(0,0), grid_size=(2,2), texture=None, ...)
A grid-based tilemap UI element for rendering tile-based levels and game worlds. A grid-based tilemap UI element for rendering tile-based levels and game worlds.
Supports layers, FOV, pathfinding, and entity management.
""" """
@overload @overload
def __init__(self) -> None: ... def __init__(self) -> None: ...
@overload @overload
def __init__(self, x: float = 0, y: float = 0, grid_size: Tuple[int, int] = (20, 20), def __init__(self, pos: Tuple[float, float] = (0, 0),
texture: Optional[Texture] = None, tile_width: int = 16, tile_height: int = 16, size: Tuple[float, float] = (0, 0),
scale: float = 1.0, on_click: Optional[Callable] = None) -> None: ... grid_size: Tuple[int, int] = (2, 2),
texture: Optional[Texture] = None,
fill_color: Optional[Color] = None,
on_click: Optional[Callable] = None,
center_x: float = 0, center_y: float = 0, zoom: float = 1.0,
visible: bool = True, opacity: float = 1.0,
z_index: int = 0, name: str = '') -> None: ...
grid_size: Tuple[int, int] # Dimensions
tile_width: int grid_size: Tuple[int, int] # Read-only (grid_w, grid_h)
tile_height: int grid_w: int # Read-only
texture: Texture grid_h: int # Read-only
scale: float
points: List[List['GridPoint']] # Position and size
entities: 'EntityCollection' position: Tuple[float, float]
background_color: Color size: Vector
on_click: Optional[Callable[[int, int, int], None]] w: float
h: float
# Camera/viewport
center: Vector # Viewport center point (pan position)
center_x: float
center_y: float
zoom: float # Scale factor for rendering
# Collections
entities: 'EntityCollection' # Entities on this grid
children: 'UICollection' # UI overlays (speech bubbles, effects)
layers: List[Union['ColorLayer', 'TileLayer']] # Grid layers sorted by z_index
# Appearance
texture: Texture # Read-only
fill_color: Color # Background fill color
# Perspective/FOV
perspective: Optional['Entity'] # Entity for FOV rendering (None = omniscient)
perspective_enabled: bool # Whether to use perspective-based FOV
fov: 'FOV' # FOV algorithm enum
fov_radius: int # Default FOV radius
# Cell-level mouse events
on_cell_enter: Optional[Callable[['Vector'], None]]
on_cell_exit: Optional[Callable[['Vector'], None]]
on_cell_click: Optional[Callable[['Vector'], None]]
hovered_cell: Optional[Tuple[int, int]] # Read-only
def at(self, x: int, y: int) -> 'GridPoint': def at(self, x: int, y: int) -> 'GridPoint':
"""Get grid point at tile coordinates.""" """Get grid point at tile coordinates."""
... ...
def center_camera(self, pos: Optional[Tuple[float, float]] = None) -> None:
"""Center the camera on a tile coordinate."""
...
# FOV methods
def compute_fov(self, pos: Tuple[int, int], radius: int = 0,
light_walls: bool = True, algorithm: Optional['FOV'] = None) -> None:
"""Compute field of view from a position."""
...
def is_in_fov(self, pos: Tuple[int, int]) -> bool:
"""Check if a cell is in the field of view."""
...
# Pathfinding methods
def find_path(self, start: Union[Tuple[int, int], 'Vector', 'Entity'],
end: Union[Tuple[int, int], 'Vector', 'Entity'],
diagonal_cost: float = 1.41) -> Optional['AStarPath']:
"""Compute A* path between two points."""
...
def get_dijkstra_map(self, root: Union[Tuple[int, int], 'Vector', 'Entity'],
diagonal_cost: float = 1.41) -> 'DijkstraMap':
"""Get or create a Dijkstra distance map for a root position."""
...
def clear_dijkstra_maps(self) -> None:
"""Clear all cached Dijkstra maps."""
...
# Layer methods
def add_layer(self, type: str, z_index: int = -1,
texture: Optional[Texture] = None) -> Union['ColorLayer', 'TileLayer']:
"""Add a new layer to the grid."""
...
def remove_layer(self, layer: Union['ColorLayer', 'TileLayer']) -> None:
"""Remove a layer from the grid."""
...
def layer(self, z_index: int) -> Optional[Union['ColorLayer', 'TileLayer']]:
"""Get layer by z_index."""
...
# Spatial queries
def entities_in_radius(self, pos: Union[Tuple[float, float], 'Vector'],
radius: float) -> List['Entity']:
"""Query entities within radius using spatial hash."""
...
# HeightMap application
def apply_threshold(self, source: 'HeightMap', range: Tuple[float, float],
walkable: Optional[bool] = None,
transparent: Optional[bool] = None) -> 'Grid':
"""Apply walkable/transparent properties where heightmap values are in range."""
...
def apply_ranges(self, source: 'HeightMap',
ranges: List[Tuple[Tuple[float, float], Dict[str, bool]]]) -> 'Grid':
"""Apply multiple thresholds in a single pass."""
...
class Line(Drawable): class Line(Drawable):
"""Line(start=None, end=None, thickness=1.0, color=None, on_click=None, **kwargs) """Line(start=None, end=None, thickness=1.0, color=None, on_click=None, **kwargs)
@ -272,17 +369,232 @@ class Arc(Drawable):
on_click: Optional[Callable[[float, float, int], None]] on_click: Optional[Callable[[float, float, int], None]]
class GridPoint: class GridPoint:
"""Grid point representing a single tile.""" """Grid point representing a single tile's properties.
texture_index: int Accessed via Grid.at(x, y). Controls walkability and transparency
solid: bool for pathfinding and FOV calculations.
color: Color """
walkable: bool # Whether entities can walk through this cell
transparent: bool # Whether light/sight passes through this cell
entities: List['Entity'] # Read-only list of entities at this cell
grid_pos: Tuple[int, int] # Read-only (x, y) position in grid
class GridPointState: class GridPointState:
"""State information for a grid point.""" """Per-entity visibility state for a grid cell.
texture_index: int Tracks what an entity has seen/discovered. Accessed via entity perspective system.
color: Color """
visible: bool # Currently visible in FOV
discovered: bool # Has been seen at least once
point: Optional['GridPoint'] # The GridPoint at this position (None if not discovered)
class ColorLayer:
"""A color overlay layer for Grid.
Provides per-cell color values for tinting, fog of war, etc.
"""
z_index: int
grid: 'Grid' # Read-only parent grid
def fill(self, color: Color) -> None:
"""Fill entire layer with a single color."""
...
def set_color(self, pos: Tuple[int, int], color: Color) -> None:
"""Set color at a specific cell."""
...
def get_color(self, pos: Tuple[int, int]) -> Color:
"""Get color at a specific cell."""
...
class TileLayer:
"""A tile sprite layer for Grid.
Provides per-cell tile indices for multi-layer tile rendering.
"""
z_index: int
grid: 'Grid' # Read-only parent grid
texture: Optional[Texture]
def fill(self, tile_index: int) -> None:
"""Fill entire layer with a single tile index."""
...
def set_tile(self, pos: Tuple[int, int], tile_index: int) -> None:
"""Set tile index at a specific cell."""
...
def get_tile(self, pos: Tuple[int, int]) -> int:
"""Get tile index at a specific cell."""
...
class FOV:
"""Field of view algorithm enum.
Available algorithms:
- FOV.BASIC: Simple raycasting
- FOV.DIAMOND: Diamond-shaped FOV
- FOV.SHADOW: Shadow casting (recommended)
- FOV.PERMISSIVE_0 through FOV.PERMISSIVE_8: Permissive algorithms
- FOV.RESTRICTIVE: Restrictive precise angle shadowcasting
"""
BASIC: 'FOV'
DIAMOND: 'FOV'
SHADOW: 'FOV'
PERMISSIVE_0: 'FOV'
PERMISSIVE_1: 'FOV'
PERMISSIVE_2: 'FOV'
PERMISSIVE_3: 'FOV'
PERMISSIVE_4: 'FOV'
PERMISSIVE_5: 'FOV'
PERMISSIVE_6: 'FOV'
PERMISSIVE_7: 'FOV'
PERMISSIVE_8: 'FOV'
RESTRICTIVE: 'FOV'
class AStarPath:
"""A* pathfinding result.
Returned by Grid.find_path(). Can be iterated or walked step-by-step.
"""
def __iter__(self) -> Any: ...
def __len__(self) -> int: ...
def walk(self) -> Optional[Tuple[int, int]]:
"""Get next step in path, or None if complete."""
...
def reverse(self) -> 'AStarPath':
"""Return a reversed copy of the path."""
...
class DijkstraMap:
"""Dijkstra distance map for pathfinding.
Created by Grid.get_dijkstra_map(). Provides distance queries
and path finding from the root position.
"""
root: Tuple[int, int] # Read-only root position
def get_distance(self, pos: Tuple[int, int]) -> float:
"""Get distance from root to position (-1 if unreachable)."""
...
def get_path(self, pos: Tuple[int, int]) -> Optional[List[Tuple[int, int]]]:
"""Get path from position to root."""
...
class HeightMap:
"""2D height field for terrain generation.
Used for procedural generation and applying terrain to grids.
"""
width: int # Read-only
height: int # Read-only
def __init__(self, width: int, height: int) -> None: ...
def get(self, x: int, y: int) -> float:
"""Get height value at position."""
...
def set(self, x: int, y: int, value: float) -> None:
"""Set height value at position."""
...
def fill(self, value: float) -> 'HeightMap':
"""Fill entire heightmap with a value."""
...
def clear(self) -> 'HeightMap':
"""Clear heightmap to 0."""
...
def normalize(self, min_val: float = 0.0, max_val: float = 1.0) -> 'HeightMap':
"""Normalize values to range."""
...
def add_hill(self, center: Tuple[float, float], radius: float, height: float) -> 'HeightMap':
"""Add a hill at position."""
...
def add_fbm(self, noise: 'NoiseSource', mulx: float = 1.0, muly: float = 1.0,
addx: float = 0.0, addy: float = 0.0, octaves: int = 4,
delta: float = 1.0, scale: float = 1.0) -> 'HeightMap':
"""Add fractal Brownian motion noise."""
...
def scale(self, factor: float) -> 'HeightMap':
"""Scale all values by factor."""
...
def clamp(self, min_val: float, max_val: float) -> 'HeightMap':
"""Clamp values to range."""
...
class NoiseSource:
"""Coherent noise generator for procedural generation.
Supports various noise types: PERLIN, SIMPLEX, WAVELET, etc.
"""
def __init__(self, type: str = 'SIMPLEX', seed: Optional[int] = None) -> None: ...
def get(self, x: float, y: float, z: float = 0.0) -> float:
"""Get noise value at position."""
...
class BSP:
"""Binary space partitioning for dungeon generation.
Recursively subdivides a rectangle into rooms.
"""
x: int
y: int
width: int
height: int
level: int
horizontal: bool
position: int
def __init__(self, x: int, y: int, width: int, height: int) -> None: ...
def split_recursive(self, randomizer: Optional[Any] = None, nb: int = 8,
minHSize: int = 4, minVSize: int = 4,
maxHRatio: float = 1.5, maxVRatio: float = 1.5) -> None:
"""Recursively split the BSP tree."""
...
def traverse(self, callback: Callable[['BSP'], bool],
order: str = 'PRE_ORDER') -> None:
"""Traverse BSP tree calling callback for each node."""
...
def is_leaf(self) -> bool:
"""Check if this is a leaf node (no children)."""
...
def contains(self, x: int, y: int) -> bool:
"""Check if point is within this node's bounds."""
...
def get_left(self) -> Optional['BSP']:
"""Get left child node."""
...
def get_right(self) -> Optional['BSP']:
"""Get right child node."""
...
class Entity(Drawable): class Entity(Drawable):
"""Entity(grid_x=0, grid_y=0, texture=None, sprite_index=0, name='') """Entity(grid_x=0, grid_y=0, texture=None, sprite_index=0, name='')