From 4be2502a10035ea7442a2c49a2fb0ca8b791018e Mon Sep 17 00:00:00 2001 From: John McCardle Date: Wed, 21 Jan 2026 21:47:26 -0500 Subject: [PATCH] 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 --- docs/stubs/mcrfpy.pyi | 519 ++++++++++++++++++++++++++++++++----- stubs/mcrfpy.pyi | 356 +++++++++++++++++++++++-- tools/generate_stubs_v2.py | 356 +++++++++++++++++++++++-- 3 files changed, 1120 insertions(+), 111 deletions(-) diff --git a/docs/stubs/mcrfpy.pyi b/docs/stubs/mcrfpy.pyi index 919794b..b9d4b4e 100644 --- a/docs/stubs/mcrfpy.pyi +++ b/docs/stubs/mcrfpy.pyi @@ -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 # Type aliases -UIElement = Union['Frame', 'Caption', 'Sprite', 'Grid'] +UIElement = Union['Frame', 'Caption', 'Sprite', 'Grid', 'Line', 'Circle', 'Arc'] Transition = Union[str, None] # Classes @@ -75,131 +75,514 @@ class Font: class Drawable: """Base class for all drawable UI elements.""" - + x: float y: float visible: bool z_index: int name: str 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]: """Get bounding box as (x, y, width, height).""" ... - + def move(self, dx: float, dy: float) -> None: """Move by relative offset (dx, dy).""" ... - + def resize(self, width: float, height: float) -> None: """Resize to new dimensions (width, height).""" ... 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. """ - + @overload def __init__(self) -> None: ... @overload 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, - outline: float = 0, click: Optional[Callable] = None, + outline: float = 0, on_click: Optional[Callable] = None, children: Optional[List[UIElement]] = None) -> None: ... - + w: float h: float fill_color: Color outline_color: Color outline: float - click: Optional[Callable[[float, float, int], None]] + on_click: Optional[Callable[[float, float, int], None]] children: 'UICollection' clip_children: bool 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. """ - + @overload def __init__(self) -> None: ... @overload def __init__(self, text: str = '', x: float = 0, y: float = 0, font: Optional[Font] = None, fill_color: Optional[Color] = None, outline_color: Optional[Color] = None, outline: float = 0, - click: Optional[Callable] = None) -> None: ... - + on_click: Optional[Callable] = None) -> None: ... + text: str font: Font fill_color: Color outline_color: Color outline: float - click: Optional[Callable[[float, float, int], None]] + on_click: Optional[Callable[[float, float, int], None]] w: float # Read-only, computed from text h: float # Read-only, computed from text 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. """ - + @overload def __init__(self) -> None: ... @overload def __init__(self, x: float = 0, y: float = 0, texture: Optional[Texture] = None, sprite_index: int = 0, scale: float = 1.0, - click: Optional[Callable] = None) -> None: ... - + on_click: Optional[Callable] = None) -> None: ... + texture: Texture sprite_index: int scale: float - click: Optional[Callable[[float, float, int], None]] + on_click: Optional[Callable[[float, float, int], None]] w: float # Read-only, computed from texture h: float # Read-only, computed from texture 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. + Supports layers, FOV, pathfinding, and entity management. """ - + @overload def __init__(self) -> None: ... @overload - def __init__(self, x: float = 0, y: float = 0, grid_size: Tuple[int, int] = (20, 20), - texture: Optional[Texture] = None, tile_width: int = 16, tile_height: int = 16, - scale: float = 1.0, click: Optional[Callable] = None) -> None: ... - - grid_size: Tuple[int, int] - tile_width: int - tile_height: int - texture: Texture - scale: float - points: List[List['GridPoint']] - entities: 'EntityCollection' - background_color: Color - click: Optional[Callable[[int, int, int], None]] - + def __init__(self, pos: Tuple[float, float] = (0, 0), + size: Tuple[float, float] = (0, 0), + 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: ... + + # Dimensions + grid_size: Tuple[int, int] # Read-only (grid_w, grid_h) + grid_w: int # Read-only + grid_h: int # Read-only + + # Position and size + position: Tuple[float, float] + size: Vector + 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': """Get grid point at tile coordinates.""" ... -class GridPoint: - """Grid point representing a single tile.""" - - texture_index: int - solid: bool + 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): + """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 + 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: - """State information for a grid point.""" - - texture_index: int - color: Color + """Per-entity visibility state for a grid cell. + + Tracks what an entity has seen/discovered. Accessed via entity perspective system. + """ + + 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): """Entity(grid_x=0, grid_y=0, texture=None, sprite_index=0, name='') @@ -232,7 +615,7 @@ class Entity(Drawable): ... 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 __getitem__(self, index: int) -> UIElement: ... @@ -269,45 +652,47 @@ class EntityCollection: class Scene: """Base class for object-oriented scenes.""" - + 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 activate(self) -> None: """Called when scene becomes active.""" ... - + def deactivate(self) -> None: """Called when scene becomes inactive.""" ... - + def get_ui(self) -> UICollection: """Get UI elements collection.""" ... - + 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: - """Handle mouse clicks.""" + """Handle mouse clicks (override in subclass).""" ... - + def on_enter(self) -> None: - """Called when entering the scene.""" + """Called when entering the scene (override in subclass).""" ... - + 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: - """Handle window resize events.""" + """Handle window resize events (override in subclass).""" ... - + def update(self, dt: float) -> None: - """Update scene logic.""" + """Update scene logic (override in subclass).""" ... class Timer: diff --git a/stubs/mcrfpy.pyi b/stubs/mcrfpy.pyi index bc0c00f..b9d4b4e 100644 --- a/stubs/mcrfpy.pyi +++ b/stubs/mcrfpy.pyi @@ -171,32 +171,129 @@ class Sprite(Drawable): h: float # Read-only, computed from texture 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. + Supports layers, FOV, pathfinding, and entity management. """ @overload def __init__(self) -> None: ... @overload - def __init__(self, x: float = 0, y: float = 0, grid_size: Tuple[int, int] = (20, 20), - texture: Optional[Texture] = None, tile_width: int = 16, tile_height: int = 16, - scale: float = 1.0, on_click: Optional[Callable] = None) -> None: ... + def __init__(self, pos: Tuple[float, float] = (0, 0), + size: Tuple[float, float] = (0, 0), + 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] - tile_width: int - tile_height: int - texture: Texture - scale: float - points: List[List['GridPoint']] - entities: 'EntityCollection' - background_color: Color - on_click: Optional[Callable[[int, int, int], None]] + # Dimensions + grid_size: Tuple[int, int] # Read-only (grid_w, grid_h) + grid_w: int # Read-only + grid_h: int # Read-only + + # Position and size + position: Tuple[float, float] + size: Vector + 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': """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): """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]] class GridPoint: - """Grid point representing a single tile.""" - - texture_index: int - solid: bool - color: Color + """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: - """State information for a grid point.""" - - texture_index: int - color: Color + """Per-entity visibility state for a grid cell. + + Tracks what an entity has seen/discovered. Accessed via entity perspective system. + """ + + 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): """Entity(grid_x=0, grid_y=0, texture=None, sprite_index=0, name='') diff --git a/tools/generate_stubs_v2.py b/tools/generate_stubs_v2.py index 3de0ad4..648435b 100644 --- a/tools/generate_stubs_v2.py +++ b/tools/generate_stubs_v2.py @@ -183,32 +183,129 @@ class Sprite(Drawable): h: float # Read-only, computed from texture 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. + Supports layers, FOV, pathfinding, and entity management. """ @overload def __init__(self) -> None: ... @overload - def __init__(self, x: float = 0, y: float = 0, grid_size: Tuple[int, int] = (20, 20), - texture: Optional[Texture] = None, tile_width: int = 16, tile_height: int = 16, - scale: float = 1.0, on_click: Optional[Callable] = None) -> None: ... + def __init__(self, pos: Tuple[float, float] = (0, 0), + size: Tuple[float, float] = (0, 0), + 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] - tile_width: int - tile_height: int - texture: Texture - scale: float - points: List[List['GridPoint']] - entities: 'EntityCollection' - background_color: Color - on_click: Optional[Callable[[int, int, int], None]] + # Dimensions + grid_size: Tuple[int, int] # Read-only (grid_w, grid_h) + grid_w: int # Read-only + grid_h: int # Read-only + + # Position and size + position: Tuple[float, float] + size: Vector + 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': """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): """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]] class GridPoint: - """Grid point representing a single tile.""" - - texture_index: int - solid: bool - color: Color + """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: - """State information for a grid point.""" - - texture_index: int - color: Color + """Per-entity visibility state for a grid cell. + + Tracks what an entity has seen/discovered. Accessed via entity perspective system. + """ + + 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): """Entity(grid_x=0, grid_y=0, texture=None, sprite_index=0, name='')