diff --git a/docs/API_REFERENCE_DYNAMIC.md b/docs/API_REFERENCE_DYNAMIC.md index 70973d9..9c225ec 100644 --- a/docs/API_REFERENCE_DYNAMIC.md +++ b/docs/API_REFERENCE_DYNAMIC.md @@ -1,6 +1,6 @@ # McRogueFace API Reference -*Generated on 2026-04-17 21:43:43* +*Generated on 2026-04-18 07:28:57* *This documentation was dynamically generated from the compiled module.* diff --git a/docs/api_reference_dynamic.html b/docs/api_reference_dynamic.html index 7e0da42..48b4abd 100644 --- a/docs/api_reference_dynamic.html +++ b/docs/api_reference_dynamic.html @@ -108,7 +108,7 @@

McRogueFace API Reference

-

Generated on 2026-04-17 21:43:43

+

Generated on 2026-04-18 07:28:57

This documentation was dynamically generated from the compiled module.

diff --git a/docs/mcrfpy.3 b/docs/mcrfpy.3 index 1bbffe3..a9f78ab 100644 --- a/docs/mcrfpy.3 +++ b/docs/mcrfpy.3 @@ -14,11 +14,11 @@ . ftr VB CB . ftr VBI CBI .\} -.TH "MCRFPY" "3" "2026-04-17" "McRogueFace 0.2.7-prerelease-7drl2026-76-g417fc43" "" +.TH "MCRFPY" "3" "2026-04-18" "McRogueFace 0.2.7-prerelease-7drl2026-79-g59e7221" "" .hy .SH McRogueFace API Reference .PP -\f[I]Generated on 2026-04-17 21:43:43\f[R] +\f[I]Generated on 2026-04-18 07:28:57\f[R] .PP \f[I]This documentation was dynamically generated from the compiled module.\f[R] diff --git a/stubs/mcrfpy.pyi b/stubs/mcrfpy.pyi index f77be4f..9852545 100644 --- a/stubs/mcrfpy.pyi +++ b/stubs/mcrfpy.pyi @@ -1,921 +1,2069 @@ """Type stubs for McRogueFace Python API. -Core game engine interface for creating roguelike games with Python. +Auto-generated by tools/generate_stubs_v2.py via runtime introspection. +Do not edit by hand -- regenerate after API changes: + + make && ./tools/generate_all_docs.sh """ -from typing import Any, List, Dict, Tuple, Optional, Callable, Union, overload +from enum import IntEnum +from typing import Any, Callable, Dict, List, Optional, Tuple, Union, overload -# Type aliases -UIElement = Union['Frame', 'Caption', 'Sprite', 'Grid', 'Line', 'Circle', 'Arc'] -Transition = Union[str, None] +# --- Enums -------------------------------------------------------------- +class Alignment(IntEnum): + """Alignment enum for positioning UI elements relative to parent bounds.""" + BOTTOM_CENTER: int + BOTTOM_LEFT: int + BOTTOM_RIGHT: int + CENTER: int + CENTER_LEFT: int + CENTER_RIGHT: int + TOP_CENTER: int + TOP_LEFT: int + TOP_RIGHT: int -# Classes +class Behavior(IntEnum): + """Enum representing entity behavior types for grid.step() turn management.""" + CUSTOM: int + FLEE: int + IDLE: int + LOOP: int + NOISE4: int + NOISE8: int + PATH: int + PATROL: int + SEEK: int + SLEEP: int + WAYPOINT: int -class Color: - """SFML Color Object for RGBA colors.""" - - r: int - g: int - b: int - a: int - - @overload - def __init__(self) -> None: ... - @overload - def __init__(self, r: int, g: int, b: int, a: int = 255) -> None: ... - - def from_hex(self, hex_string: str) -> 'Color': - """Create color from hex string (e.g., '#FF0000' or 'FF0000').""" - ... - - def to_hex(self) -> str: - """Convert color to hex string format.""" - ... - - def lerp(self, other: 'Color', t: float) -> 'Color': - """Linear interpolation between two colors.""" - ... +class Easing(IntEnum): + """Easing type.""" + EASE_IN: int + EASE_IN_BACK: int + EASE_IN_BOUNCE: int + EASE_IN_CIRC: int + EASE_IN_CUBIC: int + EASE_IN_ELASTIC: int + EASE_IN_EXPO: int + EASE_IN_OUT: int + EASE_IN_OUT_BACK: int + EASE_IN_OUT_BOUNCE: int + EASE_IN_OUT_CIRC: int + EASE_IN_OUT_CUBIC: int + EASE_IN_OUT_ELASTIC: int + EASE_IN_OUT_EXPO: int + EASE_IN_OUT_QUAD: int + EASE_IN_OUT_QUART: int + EASE_IN_OUT_SINE: int + EASE_IN_QUAD: int + EASE_IN_QUART: int + EASE_IN_SINE: int + EASE_OUT: int + EASE_OUT_BACK: int + EASE_OUT_BOUNCE: int + EASE_OUT_CIRC: int + EASE_OUT_CUBIC: int + EASE_OUT_ELASTIC: int + EASE_OUT_EXPO: int + EASE_OUT_QUAD: int + EASE_OUT_QUART: int + EASE_OUT_SINE: int + LINEAR: int + PING_PONG: int + PING_PONG_EASE_IN: int + PING_PONG_EASE_IN_OUT: int + PING_PONG_EASE_OUT: int + PING_PONG_SMOOTH: int -class Vector: - """SFML Vector Object for 2D coordinates.""" - - x: float - y: float - - @overload - def __init__(self) -> None: ... - @overload - def __init__(self, x: float, y: float) -> None: ... - - def add(self, other: 'Vector') -> 'Vector': ... - def subtract(self, other: 'Vector') -> 'Vector': ... - def multiply(self, scalar: float) -> 'Vector': ... - def divide(self, scalar: float) -> 'Vector': ... - def distance(self, other: 'Vector') -> float: ... - def normalize(self) -> 'Vector': ... - def dot(self, other: 'Vector') -> float: ... +class FOV(IntEnum): + """FOV type.""" + BASIC: int + DIAMOND: int + PERMISSIVE_0: int + PERMISSIVE_1: int + PERMISSIVE_2: int + PERMISSIVE_3: int + PERMISSIVE_4: int + PERMISSIVE_5: int + PERMISSIVE_6: int + PERMISSIVE_7: int + PERMISSIVE_8: int + RESTRICTIVE: int + SHADOW: int + SYMMETRIC_SHADOWCAST: int -class Texture: - """SFML Texture Object for images.""" - - def __init__(self, filename: str) -> None: ... - - filename: str - width: int - height: int - sprite_count: int +class InputState(IntEnum): + """Enum representing input event states (pressed/released).""" + PRESSED: int + RELEASED: int -class Font: - """SFML Font Object for text rendering.""" - - def __init__(self, filename: str) -> None: ... - - filename: str - family: str +class Key(IntEnum): + """Enum representing keyboard keys.""" + A: int + ADD: int + APOSTROPHE: int + B: int + BACKSLASH: int + BACKSPACE: int + C: int + COMMA: int + D: int + DELETE: int + DIVIDE: int + DOWN: int + E: int + END: int + ENTER: int + EQUAL: int + ESCAPE: int + F: int + F1: int + F10: int + F11: int + F12: int + F13: int + F14: int + F15: int + F2: int + F3: int + F4: int + F5: int + F6: int + F7: int + F8: int + F9: int + G: int + GRAVE: int + H: int + HOME: int + HYPHEN: int + I: int + INSERT: int + J: int + K: int + L: int + LEFT: int + LEFT_ALT: int + LEFT_BRACKET: int + LEFT_CONTROL: int + LEFT_SHIFT: int + LEFT_SYSTEM: int + M: int + MENU: int + MULTIPLY: int + N: int + NUMPAD_0: int + NUMPAD_1: int + NUMPAD_2: int + NUMPAD_3: int + NUMPAD_4: int + NUMPAD_5: int + NUMPAD_6: int + NUMPAD_7: int + NUMPAD_8: int + NUMPAD_9: int + NUM_0: int + NUM_1: int + NUM_2: int + NUM_3: int + NUM_4: int + NUM_5: int + NUM_6: int + NUM_7: int + NUM_8: int + NUM_9: int + O: int + P: int + PAGE_DOWN: int + PAGE_UP: int + PAUSE: int + PERIOD: int + Q: int + R: int + RIGHT: int + RIGHT_ALT: int + RIGHT_BRACKET: int + RIGHT_CONTROL: int + RIGHT_SHIFT: int + RIGHT_SYSTEM: int + S: int + SEMICOLON: int + SLASH: int + SPACE: int + SUBTRACT: int + T: int + TAB: int + U: int + UNKNOWN: int + UP: int + V: int + W: int + X: int + Y: int + Z: int -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, 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, on_click: Optional[Callable] = None, - children: Optional[List[UIElement]] = None) -> None: ... - - w: float - h: float - fill_color: Color - outline_color: Color - outline: float - 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, 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, - on_click: Optional[Callable] = None) -> None: ... - - text: str - font: Font - fill_color: Color - outline_color: Color - outline: float - 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, 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, - on_click: Optional[Callable] = None) -> None: ... - - texture: Texture - sprite_index: int - scale: float - 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(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, 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.""" - ... - - 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 MouseButton(IntEnum): + """Enum representing mouse buttons and scroll wheel.""" + LEFT: int + MIDDLE: int + RIGHT: int + SCROLL_DOWN: int + SCROLL_UP: int + X1: int + X2: int class Perspective(IntEnum): - """An entity's knowledge state for a grid cell (#294). + """Enum representing an entity's knowledge of a cell.""" + DISCOVERED: int + UNKNOWN: int + VISIBLE: int - Values of entity.perspective_map. Satisfies the invariant visible is a - subset of discovered: VISIBLE cells are always at least DISCOVERED. - """ +class Transition(IntEnum): + """Transition type.""" + FADE: int + NONE: int + SLIDE_DOWN: int + SLIDE_LEFT: int + SLIDE_RIGHT: int + SLIDE_UP: int - UNKNOWN = 0 # Never seen - DISCOVERED = 1 # Seen before, not currently visible - VISIBLE = 2 # In current FOV +class Traversal(IntEnum): + """Traversal type.""" + INVERTED_LEVEL_ORDER: int + IN_ORDER: int + LEVEL_ORDER: int + POST_ORDER: int + PRE_ORDER: int -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 Trigger(IntEnum): + """Enum representing trigger types passed to entity step() callbacks.""" + BLOCKED: int + DONE: int + TARGET: int +# --- Classes ------------------------------------------------------------ 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.""" + """A computed A* path result, consumed step by step.""" + def __init__(self, *args, **kwargs) -> None: ... + destination: Vector # Ending position of the path (Vector, read-only). + origin: Vector # Starting position of the path (Vector, read-only). + remaining: int # Number of steps remaining in the path (int, read-only). + def peek(self) -> Vector: + """See next step without consuming it.""" ... - - 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='') - - Game entity that lives within a Grid. - """ - - @overload - def __init__(self) -> None: ... - @overload - def __init__(self, grid_x: float = 0, grid_y: float = 0, texture: Optional[Texture] = None, - sprite_index: int = 0, name: str = '') -> None: ... - - grid_x: float - grid_y: float - texture: Texture - sprite_index: int - grid: Optional[Grid] - perspective_map: Optional['DiscreteMap'] # 3-state FOV memory (#294); None if no grid - - def at(self, x: int, y: int) -> Optional['GridPoint']: - """Return grid.at(x, y) if the cell is currently VISIBLE to this entity - (perspective_map[x, y] == Perspective.VISIBLE), otherwise None. - """ - ... - - def die(self) -> None: - """Remove entity from its grid.""" - ... - - def index(self) -> int: - """Get index in parent grid's entity collection.""" - ... - -class UICollection: - """Collection of UI drawable elements (Frame, Caption, Sprite, Grid, Line, Circle, Arc).""" - - def __len__(self) -> int: ... - def __getitem__(self, index: int) -> UIElement: ... - def __setitem__(self, index: int, value: UIElement) -> None: ... - def __delitem__(self, index: int) -> None: ... - def __contains__(self, item: UIElement) -> bool: ... - def __iter__(self) -> Any: ... - def __add__(self, other: 'UICollection') -> 'UICollection': ... - def __iadd__(self, other: 'UICollection') -> 'UICollection': ... - - def append(self, item: UIElement) -> None: ... - def extend(self, items: List[UIElement]) -> None: ... - def remove(self, item: UIElement) -> None: ... - def index(self, item: UIElement) -> int: ... - def count(self, item: UIElement) -> int: ... - -class EntityCollection: - """Collection of Entity objects.""" - - def __len__(self) -> int: ... - def __getitem__(self, index: int) -> Entity: ... - def __setitem__(self, index: int, value: Entity) -> None: ... - def __delitem__(self, index: int) -> None: ... - def __contains__(self, item: Entity) -> bool: ... - def __iter__(self) -> Any: ... - def __add__(self, other: 'EntityCollection') -> 'EntityCollection': ... - def __iadd__(self, other: 'EntityCollection') -> 'EntityCollection': ... - - def append(self, item: Entity) -> None: ... - def extend(self, items: List[Entity]) -> None: ... - def remove(self, item: Entity) -> None: ... - def index(self, item: Entity) -> int: ... - def count(self, item: Entity) -> int: ... - -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 (override in subclass).""" - ... - - def on_click(self, x: float, y: float, button: int) -> None: - """Handle mouse clicks (override in subclass).""" - ... - - def on_enter(self) -> None: - """Called when entering the scene (override in subclass).""" - ... - - def on_exit(self) -> None: - """Called when leaving the scene (override in subclass).""" - ... - - def on_resize(self, width: int, height: int) -> None: - """Handle window resize events (override in subclass).""" - ... - - def update(self, dt: float) -> None: - """Update scene logic (override in subclass).""" - ... - -class Timer: - """Timer object for scheduled callbacks.""" - - name: str - interval: int - active: bool - - def __init__(self, name: str, callback: Callable[[float], None], interval: int) -> None: ... - - def pause(self) -> None: - """Pause the timer.""" - ... - - def resume(self) -> None: - """Resume the timer.""" - ... - - def cancel(self) -> None: - """Cancel and remove the timer.""" - ... - -class Window: - """Window singleton for managing the game window.""" - - resolution: Tuple[int, int] - fullscreen: bool - vsync: bool - title: str - fps_limit: int - game_resolution: Tuple[int, int] - scaling_mode: str - - @staticmethod - def get() -> 'Window': - """Get the window singleton instance.""" + def walk(self) -> Vector: + """Get and consume next step in the path.""" ... class Animation: - """Animation object for animating UI properties.""" - - target: Any - property: str - duration: float - easing: str - loop: bool - on_complete: Optional[Callable] - - def __init__(self, target: Any, property: str, start_value: Any, end_value: Any, - duration: float, easing: str = 'linear', loop: bool = False, - on_complete: Optional[Callable] = None) -> None: ... - - def start(self) -> None: - """Start the animation.""" + """Create an animation that interpolates a property value over time.""" + def __init__(self, property: str, target: Any, duration: float, easing: str = 'linear', delta: bool = False, loop: bool = False, callback: Callable = None) -> None: ... + duration: float # Animation duration in seconds (float, read-only). Total time for the animation to complete. + elapsed: float # Elapsed time in seconds (float, read-only). Time since the animation started. + is_complete: bool # Whether animation is complete (bool, read-only). True when elapsed >= duration or complete() was called. + is_delta: bool # Whether animation uses delta mode (bool, read-only). In delta mode, the target value is added to the starting value. + is_looping: bool # Whether animation loops (bool, read-only). Looping animations repeat from the start when they reach the end. + property: str # Target property name (str, read-only). The property being animated (e.g., 'pos', 'opacity', 'sprite_index'). + def complete(self) -> None: + """Complete the animation immediately by jumping to the final value.""" ... - - def update(self, dt: float) -> bool: - """Update animation, returns True if still running.""" - ... - def get_current_value(self) -> Any: - """Get the current interpolated value.""" + """Get the current interpolated value of the animation.""" + ... + def hasValidTarget(self) -> bool: + """Check if the animation still has a valid target.""" + ... + def start(self, target: UIDrawable, conflict_mode: str = 'replace') -> None: + """Start the animation on a target UI element.""" + ... + def stop(self) -> None: + """Stop the animation without completing it.""" + ... + def update(self, delta_time: float) -> bool: + """Update the animation by the given time delta.""" ... -# Module functions +class Arc: + """An arc UI element for drawing curved line segments.""" + def __init__(self, center=None, radius=0, start_angle=0, end_angle=90, color=None, thickness=1, **kwargs) -> None: ... + align: Any # Alignment relative to parent bounds (Alignment enum or None). When set, position is automatically calculated when parent is assigned or resized. Set to None ... + bounds: Any # Bounding box as (pos, size) tuple of Vectors. Returns (Vector(x, y), Vector(width, height)). + center: Any # Center position of the arc + color: Any # Arc color + end_angle: Any # Ending angle in degrees + global_bounds: Any # Bounding box as (pos, size) tuple of Vectors in screen coordinates. Returns (Vector(x, y), Vector(width, height)). + global_position: Any # Global screen position (read-only). Calculates absolute position by walking up the parent chain. + grid_pos: Any # Position in grid tile coordinates (only when parent is Grid) + grid_size: Any # Size in grid tile coordinates (only when parent is Grid) + horiz_margin: float # Horizontal margin override (float, 0 = use general margin). Invalid for vertically-centered alignments (TOP_CENTER, BOTTOM_CENTER, CENTER). + hovered: Any # Whether mouse is currently over this element (read-only). Updated automatically by the engine during mouse movement. + margin: float # General margin from edge when aligned (float). Applied to both horizontal and vertical edges unless overridden. Invalid for CENTER alignment (raises ValueErr... + name: Any # Name for finding this element. + on_click: Any # Callable executed when arc is clicked. Function receives (pos: Vector, button: str, action: str). + on_enter: Any # Callback for mouse enter events. Called with (pos: Vector, button: str, action: str) when mouse enters this element's bounds. + on_exit: Any # Callback for mouse exit events. Called with (pos: Vector, button: str, action: str) when mouse leaves this element's bounds. + on_move: Any # Callback for mouse movement within bounds. Called with (pos: Vector, button: str, action: str) for each mouse movement while inside. Performance note: Called... + opacity: Any # Opacity level (0.0 = transparent, 1.0 = opaque). Automatically clamped to valid range [0.0, 1.0]. + origin: Any # Transform origin as Vector (pivot point for rotation). Default (0,0) is top-left; set to (w/2, h/2) to rotate around center. + parent: Any # Parent drawable. Get: Returns the parent Frame/Grid if nested, or None if at scene level. Set: Assign a Frame/Grid to reparent, or None to remove from parent. + pos: Any # Position as a Vector (same as center). + radius: Any # Arc radius in pixels + rotate_with_camera: bool # Whether to rotate visually with parent Grid's camera_rotation (bool). False (default): stay screen-aligned. True: tilt with camera. Only affects children of ... + rotation: Any # Rotation angle in degrees (clockwise around origin). Animatable property. + start_angle: Any # Starting angle in degrees + thickness: Any # Line thickness + vert_margin: float # Vertical margin override (float, 0 = use general margin). Invalid for horizontally-centered alignments (CENTER_LEFT, CENTER_RIGHT, CENTER). + visible: bool # Whether the object is visible (bool). Invisible objects are not rendered or clickable. + z_index: Any # Z-order for rendering (lower values rendered first). + def animate(self, property: str, target: Any, duration: float, easing=None, delta=False, loop=False, callback=None, conflict_mode='replace') -> Animation: + """Create and start an animation on this drawable's property.""" + ... + def move(self, *args, **kwargs) -> Any: + """move(dx, dy) or (delta) -> None""" + ... + def realign(self) -> None: + """Reapply alignment relative to parent, useful for responsive layouts.""" + ... + def resize(self, *args, **kwargs) -> Any: + """resize(width, height) or (size) -> None""" + ... -def createSoundBuffer(filename: str) -> int: - """Load a sound effect from a file and return its buffer ID.""" +class AutoRuleSet: + """AutoRuleSet - LDtk auto-tile rule set for pattern-based terrain rendering.""" + def __init__(self, *args, **kwargs) -> None: ... + grid_size: int # Cell size in pixels (int, read-only). + group_count: int # Number of rule groups (int, read-only). + name: str # Rule set name / layer identifier (str, read-only). + rule_count: int # Total number of rules across all groups (int, read-only). + value_count: int # Number of IntGrid terrain values (int, read-only). + values: Any # List of IntGrid value dicts with value and name (read-only). + def apply(self, discrete_map: DiscreteMap, tile_layer: TileLayer, seed: int = 0) -> None: + """Resolve auto-rules and write tile indices directly into a TileLayer.""" + ... + def resolve(self, discrete_map: DiscreteMap, seed: int = 0) -> list[int]: + """Resolve IntGrid data to tile indices using LDtk auto-rules.""" + ... + def terrain_enum(self) -> IntEnum: + """Generate a Python IntEnum from this rule set's IntGrid values.""" + ... + +class BSP: + """Binary Space Partitioning tree for procedural dungeon generation.""" + def __init__(self, pos: tuple[int, int], size: tuple[int, int]) -> None: ... + adjacency: Any # Leaf adjacency graph. adjacency[i] returns tuple of neighbor indices. Read-only. + bounds: Any # Root node bounds as ((x, y), (w, h)). Read-only. + pos: Any # Top-left position (x, y). Read-only. + root: Any # Reference to the root BSPNode. Read-only. + size: Any # Dimensions (width, height). Read-only. + def clear(self) -> BSP: + """Remove all children, keeping only the root node with original bounds. WARNING: Invalidates all existing BSPNode references from this tree.""" + ... + def find(self, pos: tuple[int, int]) -> BSPNode | None: + """Find the smallest (deepest) node containing the position.""" + ... + def get_leaf(self, index: int) -> BSPNode: + """Get a leaf node by its index (0 to len(bsp)-1). This is useful when working with adjacency data, which returns leaf indices.""" + ... + def leaves(self) -> Iterator[BSPNode]: + """Iterate all leaf nodes (the actual rooms). Same as iterating the BSP directly.""" + ... + def split_once(self, horizontal: bool, position: int) -> BSP: + """Split the root node once at the specified position. horizontal=True creates a horizontal divider, producing top/bottom rooms. horizontal=False creates a vert...""" + ... + def split_recursive(self, depth: int, min_size: tuple[int, int], max_ratio: float = 1.5, seed: int = None) -> BSP: + """Recursively split to the specified depth. WARNING: Invalidates all existing BSPNode references from this tree.""" + ... + def to_heightmap(self, size: tuple[int, int] = None, select: str = 'leaves', shrink: int = 0, value: float = 1.0) -> HeightMap: + """Convert BSP node selection to a HeightMap.""" + ... + def traverse(self, order: Traversal = Traversal.LEVEL_ORDER) -> Iterator[BSPNode]: + """Iterate all nodes in the specified order.""" + ... + +class Billboard: + """A camera-facing 3D sprite for trees, items, particles, etc.""" + def __init__(self, texture=None, sprite_index=0, pos=(0,0,0), scale=1.0, facing='camera_y') -> None: ... + facing: str # Facing mode: 'camera', 'camera_y', or 'fixed' (str) + opacity: float # Opacity from 0.0 (transparent) to 1.0 (opaque) (float) + phi: float # Vertical tilt for 'fixed' mode in radians (float) + pos: Any # World position as (x, y, z) tuple + scale: float # Uniform scale factor (float) + sprite_index: int # Index into sprite sheet (int) + texture: Any # Sprite sheet texture (Texture or None) + theta: float # Horizontal rotation for 'fixed' mode in radians (float) + visible: bool # Visibility state (bool) + +class CallableBinding: + """A binding that calls a Python function to get its value.""" + def __init__(self, callable: Callable[[], float]) -> None: ... + callable: Any # The Python callable (read-only). + is_valid: bool # True if the callable is still valid (bool, read-only). + value: float # Current value from calling the callable (float, read-only). Returns None on error. + +class Caption: + """A text display UI element with customizable font and styling.""" + def __init__(self, pos=None, font=None, text='', **kwargs) -> None: ... + align: Any # Alignment relative to parent bounds (Alignment enum or None). When set, position is automatically calculated when parent is assigned or resized. Set to None ... + bounds: Any # Bounding box as (pos, size) tuple of Vectors. Returns (Vector(x, y), Vector(width, height)). + fill_color: Any # Fill color of the text. Returns a copy; modifying components requires reassignment. For animation, use 'fill_color.r', 'fill_color.g', etc. + font_size: Any # Font size (integer) in points + global_bounds: Any # Bounding box as (pos, size) tuple of Vectors in screen coordinates. Returns (Vector(x, y), Vector(width, height)). + global_position: Any # Global screen position (read-only). Calculates absolute position by walking up the parent chain. + grid_pos: Any # Position in grid tile coordinates (only when parent is Grid) + grid_size: Any # Size in grid tile coordinates (only when parent is Grid) + h: Any # Text height in pixels (read-only) + horiz_margin: float # Horizontal margin override (float, 0 = use general margin). Invalid for vertically-centered alignments (TOP_CENTER, BOTTOM_CENTER, CENTER). + hovered: Any # Whether mouse is currently over this element (read-only). Updated automatically by the engine during mouse movement. + margin: float # General margin from edge when aligned (float). Applied to both horizontal and vertical edges unless overridden. Invalid for CENTER alignment (raises ValueErr... + name: Any # Name for finding elements + on_click: Any # Callable executed when object is clicked. Function receives (pos: Vector, button: str, action: str). + on_enter: Any # Callback for mouse enter events. Called with (pos: Vector, button: str, action: str) when mouse enters this element's bounds. + on_exit: Any # Callback for mouse exit events. Called with (pos: Vector, button: str, action: str) when mouse leaves this element's bounds. + on_move: Any # Callback for mouse movement within bounds. Called with (pos: Vector, button: str, action: str) for each mouse movement while inside. Performance note: Called... + opacity: Any # Opacity level (0.0 = transparent, 1.0 = opaque). Automatically clamped to valid range [0.0, 1.0]. + origin: Any # Transform origin as Vector (pivot point for rotation). Default (0,0) is top-left; set to (w/2, h/2) to rotate around center. + outline: Any # Thickness of the border + outline_color: Any # Outline color of the text. Returns a copy; modifying components requires reassignment. For animation, use 'outline_color.r', 'outline_color.g', etc. + parent: Any # Parent drawable. Get: Returns the parent Frame/Grid if nested, or None if at scene level. Set: Assign a Frame/Grid to reparent, or None to remove from parent. + pos: Any # (x, y) vector + rotate_with_camera: bool # Whether to rotate visually with parent Grid's camera_rotation (bool). False (default): stay screen-aligned. True: tilt with camera. Only affects children of ... + rotation: Any # Rotation angle in degrees (clockwise around origin). Animatable property. + shader: Any # Shader for GPU visual effects (Shader or None). When set, the drawable is rendered through the shader program. Set to None to disable shader effects. + size: Any # Text dimensions as Vector (read-only) + text: Any # The text displayed + uniforms: Any # Collection of shader uniforms (read-only access to collection). Set uniforms via dict-like syntax: drawable.uniforms['name'] = value. Supports float, vec2/3/... + vert_margin: float # Vertical margin override (float, 0 = use general margin). Invalid for horizontally-centered alignments (CENTER_LEFT, CENTER_RIGHT, CENTER). + visible: bool # Whether the object is visible (bool). Invisible objects are not rendered or clickable. + w: Any # Text width in pixels (read-only) + x: Any # X coordinate of top-left corner + y: Any # Y coordinate of top-left corner + z_index: Any # Z-order for rendering (lower values rendered first). Automatically triggers scene resort when changed. + def animate(self, property: str, target: Any, duration: float, easing=None, delta=False, loop=False, callback=None, conflict_mode='replace') -> Animation: + """Create and start an animation on this drawable's property.""" + ... + def move(self, *args, **kwargs) -> Any: + """move(dx, dy) or (delta) -> None""" + ... + def realign(self) -> None: + """Reapply alignment relative to parent, useful for responsive layouts.""" + ... + def resize(self, *args, **kwargs) -> Any: + """resize(width, height) or (size) -> None""" + ... + +class Circle: + """A circle UI element for drawing filled or outlined circles.""" + def __init__(self, radius=0, center=None, fill_color=None, outline_color=None, outline=0, **kwargs) -> None: ... + align: Any # Alignment relative to parent bounds (Alignment enum or None). When set, position is automatically calculated when parent is assigned or resized. Set to None ... + bounds: Any # Bounding box as (pos, size) tuple of Vectors. Returns (Vector(x, y), Vector(width, height)). + center: Any # Center position of the circle + fill_color: Any # Fill color of the circle + global_bounds: Any # Bounding box as (pos, size) tuple of Vectors in screen coordinates. Returns (Vector(x, y), Vector(width, height)). + global_position: Any # Global screen position (read-only). Calculates absolute position by walking up the parent chain. + grid_pos: Any # Position in grid tile coordinates (only when parent is Grid) + grid_size: Any # Size in grid tile coordinates (only when parent is Grid) + horiz_margin: float # Horizontal margin override (float, 0 = use general margin). Invalid for vertically-centered alignments (TOP_CENTER, BOTTOM_CENTER, CENTER). + hovered: Any # Whether mouse is currently over this element (read-only). Updated automatically by the engine during mouse movement. + margin: float # General margin from edge when aligned (float). Applied to both horizontal and vertical edges unless overridden. Invalid for CENTER alignment (raises ValueErr... + name: Any # Name for finding this element. + on_click: Any # Callable executed when circle is clicked. Function receives (pos: Vector, button: str, action: str). + on_enter: Any # Callback for mouse enter events. Called with (pos: Vector, button: str, action: str) when mouse enters this element's bounds. + on_exit: Any # Callback for mouse exit events. Called with (pos: Vector, button: str, action: str) when mouse leaves this element's bounds. + on_move: Any # Callback for mouse movement within bounds. Called with (pos: Vector, button: str, action: str) for each mouse movement while inside. Performance note: Called... + opacity: Any # Opacity level (0.0 = transparent, 1.0 = opaque). Automatically clamped to valid range [0.0, 1.0]. + origin: Any # Transform origin as Vector (pivot point for rotation). Default (0,0) is top-left; set to (w/2, h/2) to rotate around center. + outline: Any # Outline thickness (0 for no outline) + outline_color: Any # Outline color of the circle + parent: Any # Parent drawable. Get: Returns the parent Frame/Grid if nested, or None if at scene level. Set: Assign a Frame/Grid to reparent, or None to remove from parent. + pos: Any # Position as a Vector (same as center). + radius: Any # Circle radius in pixels + rotate_with_camera: bool # Whether to rotate visually with parent Grid's camera_rotation (bool). False (default): stay screen-aligned. True: tilt with camera. Only affects children of ... + rotation: Any # Rotation angle in degrees (clockwise around origin). Animatable property. + vert_margin: float # Vertical margin override (float, 0 = use general margin). Invalid for horizontally-centered alignments (CENTER_LEFT, CENTER_RIGHT, CENTER). + visible: bool # Whether the object is visible (bool). Invisible objects are not rendered or clickable. + z_index: Any # Z-order for rendering (lower values rendered first). + def animate(self, property: str, target: Any, duration: float, easing=None, delta=False, loop=False, callback=None, conflict_mode='replace') -> Animation: + """Create and start an animation on this drawable's property.""" + ... + def move(self, *args, **kwargs) -> Any: + """move(dx, dy) or (delta) -> None""" + ... + def realign(self) -> None: + """Reapply alignment relative to parent, useful for responsive layouts.""" + ... + def resize(self, *args, **kwargs) -> Any: + """resize(width, height) or (size) -> None""" + ... + +class Color: + """RGBA color representation.""" + def __init__(self, r: int = 0, g: int = 0, b: int = 0, a: int = 255) -> None: ... + a: Any # Alpha component (0-255, where 0=transparent, 255=opaque). Automatically clamped to valid range. + b: Any # Blue component (0-255). Automatically clamped to valid range. + g: Any # Green component (0-255). Automatically clamped to valid range. + r: Any # Red component (0-255). Automatically clamped to valid range. + def from_hex(self, hex_string: str) -> Color: + """Create a Color from a hexadecimal string.""" + ... + def lerp(self, other: Color, t: float) -> Color: + """Linearly interpolate between this color and another.""" + ... + def to_hex(self) -> str: + """Convert this Color to a hexadecimal string.""" + ... + +class ColorLayer: + """A grid layer that stores RGBA colors per cell for background/overlay effects.""" + def __init__(self, z_index=-1, name=None, grid_size=None) -> None: ... + grid: Any # Parent Grid or None. Setting manages layer association and handles lazy allocation. + grid_size: Any # Layer dimensions as (width, height) tuple. + name: str # Layer name (str, read-only). Used for Grid.layer(name) lookup. + visible: Any # Whether the layer is rendered. + z_index: Any # Layer z-order. Negative values render below entities. + def apply_gradient(self, source, range, color_low, color_high) -> ColorLayer: + """Interpolate between colors based on HeightMap value within range.""" + ... + def apply_perspective(self, entity, visible=None, discovered=None, unknown=None) -> Any: + """Bind this layer to an entity for automatic FOV updates.""" + ... + def apply_ranges(self, source, ranges) -> ColorLayer: + """Apply multiple color assignments in a single pass.""" + ... + def apply_threshold(self, source, range, color) -> ColorLayer: + """Set fixed color for cells where HeightMap value is within range.""" + ... + def at(self, pos) -> Color: + """at(x, y) -> Color""" + ... + def clear_perspective(self) -> Any: + """Remove the perspective binding from this layer.""" + ... + def draw_fov(self, source, radius=None, fov=None, visible=None, discovered=None, unknown=None) -> Any: + """Paint cells based on field-of-view visibility from source position.""" + ... + def fill(self, color) -> Any: + """Fill the entire layer with the specified color.""" + ... + def fill_rect(self, pos, size, color) -> Any: + """Fill a rectangular region with a color.""" + ... + def set(self, pos, color) -> Any: + """Set the color at cell position.""" + ... + def update_perspective(self) -> Any: + """Redraw FOV based on the bound entity's current position.""" + ... + +class DijkstraMap: + """A Dijkstra distance map from a fixed root position.""" + def __init__(self, *args, **kwargs) -> None: ... + root: Vector # Root position that distances are measured from (Vector, read-only). + def distance(self, pos) -> float | None: + """Get distance from position to root.""" + ... + def path_from(self, pos) -> AStarPath: + """Get full path from position to root.""" + ... + def step_from(self, pos) -> Vector | None: + """Get single step from position toward root.""" + ... + def to_heightmap(self, size=None, unreachable=-1.0) -> HeightMap: + """Convert distance field to a HeightMap.""" + ... + +class DiscreteMap: + """A 2D grid of uint8 values (0-255) for discrete/categorical data.""" + def __init__(self, size: tuple[int, int], fill: int = 0, enum: type[IntEnum] = None) -> None: ... + enum_type: Any # Optional IntEnum class for value interpretation. + size: Any # Dimensions (width, height) of the map. Read-only. + def add(self, other: DiscreteMap | int, *, pos=None, source_pos=None, size=None) -> DiscreteMap: + """Add values from another map or a scalar, with saturation to 0-255.""" + ... + def bitwise_and(self, other: DiscreteMap, *, pos=None, source_pos=None, size=None) -> DiscreteMap: + """Bitwise AND with another DiscreteMap.""" + ... + def bitwise_or(self, other: DiscreteMap, *, pos=None, source_pos=None, size=None) -> DiscreteMap: + """Bitwise OR with another DiscreteMap.""" + ... + def bitwise_xor(self, other: DiscreteMap, *, pos=None, source_pos=None, size=None) -> DiscreteMap: + """Bitwise XOR with another DiscreteMap.""" + ... + def bool(self, condition: int | set | callable) -> DiscreteMap: + """Create binary mask from condition. Returns NEW DiscreteMap.""" + ... + def clear(self) -> DiscreteMap: + """Set all cells to 0. Equivalent to fill(0).""" + ... + def copy_from(self, other: DiscreteMap, *, pos=None, source_pos=None, size=None) -> DiscreteMap: + """Copy values from another DiscreteMap into the specified region.""" + ... + def count(self, value: int) -> int: + """Count cells with the specified value.""" + ... + def count_range(self, min_val: int, max_val: int) -> int: + """Count cells with values in the specified range (inclusive).""" + ... + def fill(self, value: int, *, pos=None, size=None) -> DiscreteMap: + """Set cells in region to the specified value.""" + ... + def from_bytes(self, data: bytes, size: tuple[int, int], *, enum: type = None) -> DiscreteMap: + """Create a DiscreteMap from raw byte data.""" + ... + def from_heightmap(self, hmap: HeightMap, mapping: list[tuple[tuple[float, float], int]], *, enum=None) -> DiscreteMap: + """Create DiscreteMap from HeightMap using range-to-value mapping.""" + ... + def get(self, *args, **kwargs) -> Any: + """get(x, y) or (pos) -> int | Enum""" + ... + def histogram(self) -> dict[int, int]: + """Get a histogram of value counts.""" + ... + def invert(self) -> DiscreteMap: + """Return NEW DiscreteMap with (255 - value) for each cell.""" + ... + def mask(self) -> memoryview: + """Get raw uint8_t data as memoryview for libtcod compatibility.""" + ... + def max(self, other: DiscreteMap, *, pos=None, source_pos=None, size=None) -> DiscreteMap: + """Set each cell to the maximum of this and another DiscreteMap.""" + ... + def min(self, other: DiscreteMap, *, pos=None, source_pos=None, size=None) -> DiscreteMap: + """Set each cell to the minimum of this and another DiscreteMap.""" + ... + def min_max(self) -> tuple[int, int]: + """Get the minimum and maximum values in the map.""" + ... + def multiply(self, factor: float, *, pos=None, size=None) -> DiscreteMap: + """Multiply values by a scalar factor, with saturation to 0-255.""" + ... + def set(self, x: int, y: int, value: int) -> None: + """Set the value at integer coordinates.""" + ... + def subtract(self, other: DiscreteMap | int, *, pos=None, source_pos=None, size=None) -> DiscreteMap: + """Subtract values from another map or a scalar, with saturation to 0-255.""" + ... + def to_bytes(self) -> bytes: + """Serialize map data to bytes (row-major, one byte per cell).""" + ... + def to_heightmap(self, mapping: dict[int, float] = None) -> HeightMap: + """Convert to HeightMap, optionally mapping values to floats.""" + ... + +class Drawable: + """Base class for all drawable UI elements""" + def __init__(self, *args, **kwargs) -> None: ... + on_click: Any # Callable executed when object is clicked. Function receives (pos: Vector, button: str, action: str). + opacity: Any # Opacity level (0.0 = transparent, 1.0 = opaque). Automatically clamped to valid range [0.0, 1.0]. + visible: bool # Whether the object is visible (bool). Invisible objects are not rendered or clickable. + z_index: Any # Z-order for rendering (lower values rendered first). Automatically triggers scene resort when changed. + def move(self, *args, **kwargs) -> Any: + """move(dx, dy) or (delta) -> None""" + ... + def resize(self, *args, **kwargs) -> Any: + """resize(width, height) or (size) -> None""" + ... + +class Entity: + """A game entity that exists on a grid with sprite rendering.""" + def __init__(self, grid_pos=None, texture=None, sprite_index=0, **kwargs) -> None: ... + behavior_type: int # Current behavior type (int, read-only). Use set_behavior() to change. + cell_pos: Vector # Integer logical cell position (Vector). Decoupled from draw_pos. Determines which cell this entity logically occupies for collision, pathfinding, etc. + cell_x: Any # Integer X cell coordinate. + cell_y: Any # Integer Y cell coordinate. + default_behavior: int # Default behavior type (int, maps to Behavior enum). Entity reverts to this after DONE trigger. Default: 0 (IDLE). + draw_pos: Vector # Fractional tile position for rendering (Vector). Use for smooth animation between grid cells. + grid: Any # Grid this entity belongs to. Get: Returns the Grid or None. Set: Assign a Grid to move entity, or None to remove from grid. + grid_pos: Vector # Grid position as integer cell coordinates (Vector). Alias for cell_pos. + grid_x: Any # Grid X position as integer cell coordinate. Alias for cell_x. + grid_y: Any # Grid Y position as integer cell coordinate. Alias for cell_y. + labels: frozenset # Set of string labels for collision/targeting (frozenset). Assign any iterable of strings to replace all labels. + move_speed: float # Animation duration for behavior movement in seconds (float). 0 = instant. Default: 0.15. + name: Any # Name for finding elements + opacity: Any # Opacity (0.0 = transparent, 1.0 = opaque) + perspective_map: DiscreteMap # Per-entity FOV memory (DiscreteMap, read-write). 3-state values per cell: 0 = unknown (never seen), 1 = discovered (seen before, not currently visible), 2 = ... + pos: Vector # Pixel position relative to grid (Vector). Computed as draw_pos * tile_size. Requires entity to be attached to a grid. + shader: Any # Shader for GPU visual effects (Shader or None). When set, the entity is rendered through the shader program. Set to None to disable shader effects. + sight_radius: int # FOV radius for TARGET trigger (int). Default: 10. + sprite_grid: Any # Per-tile sprite indices for composite multi-tile entities (list of lists or None). Row-major, dimensions must match tile_width x tile_height. Use -1 for empt... + sprite_index: Any # Sprite index on the texture on the display + sprite_offset: Vector # Pixel offset for oversized sprites (Vector). Applied pre-zoom during grid rendering. + sprite_offset_x: Any # X component of sprite pixel offset. + sprite_offset_y: Any # Y component of sprite pixel offset. + step: Any # Step callback for grid.step() turn management. Called with (trigger, data) when behavior triggers fire. Set to None to clear. + target_label: Any # Label to search for with TARGET trigger (str or None). Default: None. + tile_height: int # Entity height in tiles (int). Must be >= 1. Default 1. + tile_size: Any # Entity size in tiles as (width, height) Vector. Default (1, 1). + tile_width: int # Entity width in tiles (int). Must be >= 1. Default 1. + turn_order: int # Turn order for grid.step() (int). 0 = skip, higher values go later. Default: 1. + uniforms: Any # Collection of shader uniforms (read-only access to collection). Set uniforms via dict-like syntax: entity.uniforms['name'] = value. Supports float, vec2/3/4 ... + visible: Any # Visibility flag + x: Any # Pixel X position relative to grid. Requires entity to be attached to a grid. + y: Any # Pixel Y position relative to grid. Requires entity to be attached to a grid. + def add_label(self, label: str) -> None: + """Add a label to this entity. Idempotent (adding same label twice is safe).""" + ... + def animate(self, property: str, target: Any, duration: float, easing=None, delta=False, loop=False, callback=None, conflict_mode='replace') -> Animation: + """Create and start an animation on this entity's property.""" + ... + def at(self, *args, **kwargs) -> Any: + """at(x, y) or at(pos) -> GridPoint | None""" + ... + def die(self, *args, **kwargs) -> Any: + """Remove this entity from its grid.""" + ... + def find_path(self, target, diagonal_cost=1.41, collide=None) -> AStarPath | None: + """Find a path from this entity to the target position.""" + ... + def has_label(self, label: str) -> bool: + """Check if this entity has the given label.""" + ... + def index(self, *args, **kwargs) -> Any: + """Return the index of this entity in its grid's entity collection""" + ... + def move(self, *args, **kwargs) -> Any: + """move(dx, dy) or (delta) -> None""" + ... + def path_to(self, *args, **kwargs) -> Any: + """path_to(x, y) or path_to(target) -> list""" + ... + def remove_label(self, label: str) -> None: + """Remove a label from this entity. No-op if label not present.""" + ... + def resize(self, *args, **kwargs) -> Any: + """resize(width, height) or (size) -> None""" + ... + def set_behavior(self, type, waypoints=None, turns=0, path=None) -> None: + """Configure this entity's behavior for grid.step() turn management.""" + ... + def update_visibility(self) -> None: + """Update entity's visibility state based on current FOV.""" + ... + def visible_entities(self, fov=None, radius=None) -> list[Entity]: + """Get list of other entities visible from this entity's position.""" + ... + +class Entity3D: + """A 3D game entity that exists on a Viewport3D's navigation grid.""" + def __init__(self, pos=None, **kwargs) -> None: ... + anim_clip: Any # Current animation clip name. Set to play an animation. + anim_frame: Any # Current animation frame number (read-only, approximate at 30fps). + anim_loop: Any # Whether animation loops when it reaches the end. + anim_paused: Any # Whether animation playback is paused. + anim_speed: Any # Animation playback speed multiplier. 1.0 = normal speed. + anim_time: Any # Current time position in animation (seconds). + auto_animate: Any # Enable auto-play of walk/idle clips based on movement. + color: Any # Entity render color. + grid_pos: Any # Grid position (x, z). Same as pos. + idle_clip: Any # Animation clip to play when entity is stationary. + is_moving: Any # Whether entity is currently moving (read-only). + model: Model3D # 3D model (Model3D). If None, uses placeholder cube. + name: str # Entity name (str). Used for find() lookup. + on_anim_complete: Any # Callback(entity, clip_name) when non-looping animation ends. + pos: Any # Grid position (x, z). Setting triggers smooth movement. + rotation: Any # Y-axis rotation in degrees. + scale: Any # Uniform scale factor. Can also set as (x, y, z) tuple. + viewport: Any # Owning Viewport3D (read-only). + visible: Any # Visibility state. + walk_clip: Any # Animation clip to play when entity is moving. + world_pos: Any # Current world position (x, y, z) (read-only). Includes animation interpolation. + def animate(self, property, target, duration, easing=None, delta=False, callback=None, conflict_mode=None) -> Any: + """Animate a property over time.""" + ... + def at(self, x, z) -> dict: + """Get visibility state for a cell from this entity's perspective.""" + ... + def clear_path(self) -> Any: + """Clear the movement queue and stop at current position.""" + ... + def follow_path(self, path) -> Any: + """Queue path positions for smooth movement.""" + ... + def path_to(self, *args, **kwargs) -> Any: + """path_to(x, z) or path_to(pos=(x, z)) -> list""" + ... + def teleport(self, *args, **kwargs) -> Any: + """teleport(x, z) or teleport(pos=(x, z))""" + ... + def update_visibility(self) -> Any: + """Recompute field of view from current position.""" + ... + +class EntityCollection3D: + """Collection of Entity3D objects belonging to a Viewport3D.""" + def __init__(self, *args, **kwargs) -> None: ... + def append(self, entity) -> Any: + """Add an Entity3D to the collection.""" + ... + def clear(self) -> Any: + """Remove all entities from the collection.""" + ... + def extend(self, iterable) -> Any: + """Add all Entity3D objects from iterable to the collection.""" + ... + def find(self, name) -> Entity3D or None: + """Find an Entity3D by name. Returns None if not found.""" + ... + def pop(self, index=-1) -> Entity3D: + """Remove and return Entity3D at index (default: last).""" + ... + def remove(self, entity) -> Any: + """Remove an Entity3D from the collection.""" + ... + +class EntityCollection3DIter: + """Iterator for EntityCollection3D""" + def __init__(self, *args, **kwargs) -> None: ... + +class Font: + """A font resource for rendering text in Caption elements.""" + def __init__(self, filename: str) -> None: ... + family: str # Font family name (str, read-only). Retrieved from font metadata. + source: str # Source filename path (str, read-only). The path used to load this font. + +class Frame: + """A rectangular frame UI element that can contain other drawable elements.""" + def __init__(self, pos=None, size=None, **kwargs) -> None: ... + align: Any # Alignment relative to parent bounds (Alignment enum or None). When set, position is automatically calculated when parent is assigned or resized. Set to None ... + bounds: Any # Bounding box as (pos, size) tuple of Vectors. Returns (Vector(x, y), Vector(width, height)). + cache_subtree: Any # #144: Cache subtree rendering to texture for performance + children: Any # UICollection of objects on top of this one + clip_children: Any # Whether to clip children to frame bounds + fill_color: Any # Fill color of the rectangle. Returns a copy; modifying components requires reassignment. For animation, use 'fill_color.r', 'fill_color.g', etc. + global_bounds: Any # Bounding box as (pos, size) tuple of Vectors in screen coordinates. Returns (Vector(x, y), Vector(width, height)). + global_position: Any # Global screen position (read-only). Calculates absolute position by walking up the parent chain. + grid_pos: Any # Position in grid tile coordinates (only when parent is Grid) + grid_size: Any # Size in grid tile coordinates (only when parent is Grid) + h: Any # height of the rectangle + horiz_margin: float # Horizontal margin override (float, 0 = use general margin). Invalid for vertically-centered alignments (TOP_CENTER, BOTTOM_CENTER, CENTER). + hovered: Any # Whether mouse is currently over this element (read-only). Updated automatically by the engine during mouse movement. + margin: float # General margin from edge when aligned (float). Applied to both horizontal and vertical edges unless overridden. Invalid for CENTER alignment (raises ValueErr... + name: Any # Name for finding elements + on_click: Any # Callable executed when object is clicked. Function receives (pos: Vector, button: str, action: str). + on_enter: Any # Callback for mouse enter events. Called with (pos: Vector, button: str, action: str) when mouse enters this element's bounds. + on_exit: Any # Callback for mouse exit events. Called with (pos: Vector, button: str, action: str) when mouse leaves this element's bounds. + on_move: Any # Callback for mouse movement within bounds. Called with (pos: Vector, button: str, action: str) for each mouse movement while inside. Performance note: Called... + opacity: Any # Opacity level (0.0 = transparent, 1.0 = opaque). Automatically clamped to valid range [0.0, 1.0]. + origin: Any # Transform origin as Vector (pivot point for rotation). Default (0,0) is top-left; set to (w/2, h/2) to rotate around center. + outline: Any # Thickness of the border + outline_color: Any # Outline color of the rectangle. Returns a copy; modifying components requires reassignment. For animation, use 'outline_color.r', 'outline_color.g', etc. + parent: Any # Parent drawable. Get: Returns the parent Frame/Grid if nested, or None if at scene level. Set: Assign a Frame/Grid to reparent, or None to remove from parent. + pos: Any # Position as a Vector + rotate_with_camera: bool # Whether to rotate visually with parent Grid's camera_rotation (bool). False (default): stay screen-aligned. True: tilt with camera. Only affects children of ... + rotation: Any # Rotation angle in degrees (clockwise around origin). Animatable property. + shader: Any # Shader for GPU visual effects (Shader or None). When set, the drawable is rendered through the shader program. Set to None to disable shader effects. + uniforms: Any # Collection of shader uniforms (read-only access to collection). Set uniforms via dict-like syntax: drawable.uniforms['name'] = value. Supports float, vec2/3/... + vert_margin: float # Vertical margin override (float, 0 = use general margin). Invalid for horizontally-centered alignments (CENTER_LEFT, CENTER_RIGHT, CENTER). + visible: bool # Whether the object is visible (bool). Invisible objects are not rendered or clickable. + w: Any # width of the rectangle + x: Any # X coordinate of top-left corner + y: Any # Y coordinate of top-left corner + z_index: Any # Z-order for rendering (lower values rendered first). Automatically triggers scene resort when changed. + def animate(self, property: str, target: Any, duration: float, easing=None, delta=False, loop=False, callback=None, conflict_mode='replace') -> Animation: + """Create and start an animation on this drawable's property.""" + ... + def move(self, *args, **kwargs) -> Any: + """move(dx, dy) or (delta) -> None""" + ... + def realign(self) -> None: + """Reapply alignment relative to parent, useful for responsive layouts.""" + ... + def resize(self, *args, **kwargs) -> Any: + """resize(width, height) or (size) -> None""" + ... + +class Grid: + """A grid-based UI element for tile-based rendering and entity management.""" + def __init__(self, grid_size=None, pos=None, size=None, texture=None, **kwargs) -> None: ... + align: Any # Alignment relative to parent bounds (Alignment enum or None). When set, position is automatically calculated when parent is assigned or resized. Set to None ... + bounds: Any # Bounding box as (pos, size) tuple of Vectors. Returns (Vector(x, y), Vector(width, height)). + camera_rotation: Any # Rotation of grid contents around camera center (degrees). + center: Any # Camera center point in pixel coordinates. + center_x: Any # center of the view X-coordinate + center_y: Any # center of the view Y-coordinate + children: Any # UICollection of UIDrawable children (speech bubbles, effects, overlays) + entities: Any # EntityCollection of entities on this grid + fill_color: Any # Background fill color. + fov: Any # FOV algorithm for this grid (mcrfpy.FOV enum). Used by entity.updateVisibility() and layer methods when fov=None. + fov_radius: Any # Default FOV radius for this grid. Used when radius not specified. + global_bounds: Any # Bounding box as (pos, size) tuple of Vectors in screen coordinates. Returns (Vector(x, y), Vector(width, height)). + global_position: Any # Global screen position (read-only). Calculates absolute position by walking up the parent chain. + grid_data: Any # The underlying grid data object (for multi-view scenarios). + grid_h: Any # Grid height in cells + grid_pos: Any # Position in parent grid's tile coordinates (only when parent is Grid) + grid_size: Any # Grid dimensions (grid_w, grid_h) + grid_w: Any # Grid width in cells + h: Any # visible widget height + horiz_margin: float # Horizontal margin override (float, 0 = use general margin). Invalid for vertically-centered alignments (TOP_CENTER, BOTTOM_CENTER, CENTER). + hovered: Any # Whether mouse is currently over this element (read-only). Updated automatically by the engine during mouse movement. + hovered_cell: Any # Currently hovered cell as (x, y) tuple, or None if not hovering. + layers: ColorLayer # List of grid layers (ColorLayer, TileLayer) sorted by z_index + margin: float # General margin from edge when aligned (float). Applied to both horizontal and vertical edges unless overridden. Invalid for CENTER alignment (raises ValueErr... + name: Any # Name for finding elements + on_cell_click: Any # Callback when a grid cell is clicked. Called with (cell_pos: Vector). + on_cell_enter: Any # Callback when mouse enters a grid cell. Called with (cell_pos: Vector). + on_cell_exit: Any # Callback when mouse exits a grid cell. Called with (cell_pos: Vector). + on_click: Any # Callable executed when object is clicked. + on_enter: Any # Callback for mouse enter events. Called with (pos: Vector, button: str, action: str) when mouse enters this element's bounds. + on_exit: Any # Callback for mouse exit events. Called with (pos: Vector, button: str, action: str) when mouse leaves this element's bounds. + on_move: Any # Callback for mouse movement within bounds. Called with (pos: Vector, button: str, action: str) for each mouse movement while inside. Performance note: Called... + opacity: Any # Opacity level (0.0 = transparent, 1.0 = opaque). Automatically clamped to valid range [0.0, 1.0]. + origin: Any # Transform origin as Vector (pivot point for rotation). Default (0,0) is top-left; set to (w/2, h/2) to rotate around center. + parent: Any # Parent drawable. Get: Returns the parent Frame/Grid if nested, or None if at scene level. Set: Assign a Frame/Grid to reparent, or None to remove from parent. + perspective: Any # Entity whose perspective to use for FOV rendering (None for omniscient view). Setting an entity automatically enables perspective mode. + perspective_enabled: Any # Whether to use perspective-based FOV rendering. When True with no valid entity, all cells appear undiscovered. + pos: Any # Position of the grid as Vector + rotate_with_camera: bool # Whether to rotate visually with parent Grid's camera_rotation (bool). False (default): stay screen-aligned. True: tilt with camera. Only affects children of ... + rotation: Any # Rotation angle in degrees (clockwise around origin). Animatable property. + shader: Any # Shader for GPU visual effects (Shader or None). When set, the drawable is rendered through the shader program. Set to None to disable shader effects. + size: Any # Size of the grid as Vector (width, height) + texture: Any # Texture used for tile rendering (read-only). + uniforms: Any # Collection of shader uniforms (read-only access to collection). Set uniforms via dict-like syntax: drawable.uniforms['name'] = value. Supports float, vec2/3/... + vert_margin: float # Vertical margin override (float, 0 = use general margin). Invalid for horizontally-centered alignments (CENTER_LEFT, CENTER_RIGHT, CENTER). + view: Any # Auto-created GridView for rendering (read-only). When Grid is appended to a scene, this view is what actually renders. + visible: bool # Whether the object is visible (bool). Invisible objects are not rendered or clickable. + w: Any # visible widget width + x: Any # top-left corner X-coordinate + y: Any # top-left corner Y-coordinate + z_index: Any # Z-order for rendering (lower values rendered first). + zoom: Any # Zoom level for rendering. + def add_layer(self, layer: ColorLayer | TileLayer) -> ColorLayer | TileLayer: + """Add a layer to the grid.""" + ... + def animate(self, property: str, target: Any, duration: float, easing=None, delta=False, loop=False, callback=None, conflict_mode='replace') -> Animation: + """Create and start an animation on this drawable's property.""" + ... + def apply_ranges(self, source: HeightMap, ranges: list) -> Grid: + """Apply multiple thresholds in a single pass.""" + ... + def apply_threshold(self, source: HeightMap, range: tuple, walkable: bool = None, transparent: bool = None) -> Grid: + """Apply walkable/transparent properties where heightmap values are in range.""" + ... + def at(self, *args, **kwargs) -> Any: ... + def center_camera(self, pos: tuple = None) -> None: + """Center the camera on a tile coordinate.""" + ... + def clear_dijkstra_maps(self) -> None: + """Clear all cached Dijkstra maps.""" + ... + def compute_fov(self, pos, radius: int = 0, light_walls: bool = True, algorithm: int = FOV_BASIC) -> None: + """Compute field of view from a position.""" + ... + def entities_in_radius(self, pos: tuple|Vector, radius: float) -> list[Entity]: + """Query entities within radius using spatial hash (O(k) where k = nearby entities).""" + ... + def find_path(self, start, end, diagonal_cost=1.41, collide=None) -> AStarPath | None: + """Compute A* path between two points.""" + ... + def get_dijkstra_map(self, root, diagonal_cost=1.41, collide=None) -> DijkstraMap: + """Get or create a Dijkstra distance map for a root position.""" + ... + def is_in_fov(self, pos) -> bool: + """Check if a cell is in the field of view.""" + ... + def layer(self, name: str) -> ColorLayer | TileLayer | None: + """Get a layer by its name.""" + ... + def move(self, *args, **kwargs) -> Any: + """move(dx, dy) or (delta) -> None""" + ... + def realign(self) -> None: + """Reapply alignment relative to parent, useful for responsive layouts.""" + ... + def remove_layer(self, name_or_layer: str | ColorLayer | TileLayer) -> None: + """Remove a layer from the grid.""" + ... + def resize(self, *args, **kwargs) -> Any: + """resize(width, height) or (size) -> None""" + ... + def step(self, n=1, turn_order=None) -> None: + """Execute n rounds of turn-based entity behavior.""" + ... + +class GridView: + """A grid-based UI element for tile-based rendering and entity management.""" + def __init__(self, *args, **kwargs) -> None: ... + align: Any # Alignment relative to parent bounds (Alignment enum or None). When set, position is automatically calculated when parent is assigned or resized. Set to None ... + bounds: Any # Bounding box as (pos, size) tuple of Vectors. Returns (Vector(x, y), Vector(width, height)). + camera_rotation: Any # Rotation of grid contents around camera center (degrees). + center: Any # Camera center point in pixel coordinates. + center_x: Any # center of the view X-coordinate + center_y: Any # center of the view Y-coordinate + children: Any # UICollection of UIDrawable children (speech bubbles, effects, overlays) + entities: Any # EntityCollection of entities on this grid + fill_color: Any # Background fill color. + fov: Any # FOV algorithm for this grid (mcrfpy.FOV enum). Used by entity.updateVisibility() and layer methods when fov=None. + fov_radius: Any # Default FOV radius for this grid. Used when radius not specified. + global_bounds: Any # Bounding box as (pos, size) tuple of Vectors in screen coordinates. Returns (Vector(x, y), Vector(width, height)). + global_position: Any # Global screen position (read-only). Calculates absolute position by walking up the parent chain. + grid_data: Any # The underlying grid data object (for multi-view scenarios). + grid_h: Any # Grid height in cells + grid_pos: Any # Position in parent grid's tile coordinates (only when parent is Grid) + grid_size: Any # Grid dimensions (grid_w, grid_h) + grid_w: Any # Grid width in cells + h: Any # visible widget height + horiz_margin: float # Horizontal margin override (float, 0 = use general margin). Invalid for vertically-centered alignments (TOP_CENTER, BOTTOM_CENTER, CENTER). + hovered: Any # Whether mouse is currently over this element (read-only). Updated automatically by the engine during mouse movement. + hovered_cell: Any # Currently hovered cell as (x, y) tuple, or None if not hovering. + layers: ColorLayer # List of grid layers (ColorLayer, TileLayer) sorted by z_index + margin: float # General margin from edge when aligned (float). Applied to both horizontal and vertical edges unless overridden. Invalid for CENTER alignment (raises ValueErr... + name: Any # Name for finding elements + on_cell_click: Any # Callback when a grid cell is clicked. Called with (cell_pos: Vector). + on_cell_enter: Any # Callback when mouse enters a grid cell. Called with (cell_pos: Vector). + on_cell_exit: Any # Callback when mouse exits a grid cell. Called with (cell_pos: Vector). + on_click: Any # Callable executed when object is clicked. + on_enter: Any # Callback for mouse enter events. Called with (pos: Vector, button: str, action: str) when mouse enters this element's bounds. + on_exit: Any # Callback for mouse exit events. Called with (pos: Vector, button: str, action: str) when mouse leaves this element's bounds. + on_move: Any # Callback for mouse movement within bounds. Called with (pos: Vector, button: str, action: str) for each mouse movement while inside. Performance note: Called... + opacity: Any # Opacity level (0.0 = transparent, 1.0 = opaque). Automatically clamped to valid range [0.0, 1.0]. + origin: Any # Transform origin as Vector (pivot point for rotation). Default (0,0) is top-left; set to (w/2, h/2) to rotate around center. + parent: Any # Parent drawable. Get: Returns the parent Frame/Grid if nested, or None if at scene level. Set: Assign a Frame/Grid to reparent, or None to remove from parent. + perspective: Any # Entity whose perspective to use for FOV rendering (None for omniscient view). Setting an entity automatically enables perspective mode. + perspective_enabled: Any # Whether to use perspective-based FOV rendering. When True with no valid entity, all cells appear undiscovered. + pos: Any # Position of the grid as Vector + rotate_with_camera: bool # Whether to rotate visually with parent Grid's camera_rotation (bool). False (default): stay screen-aligned. True: tilt with camera. Only affects children of ... + rotation: Any # Rotation angle in degrees (clockwise around origin). Animatable property. + shader: Any # Shader for GPU visual effects (Shader or None). When set, the drawable is rendered through the shader program. Set to None to disable shader effects. + size: Any # Size of the grid as Vector (width, height) + texture: Any # Texture used for tile rendering (read-only). + uniforms: Any # Collection of shader uniforms (read-only access to collection). Set uniforms via dict-like syntax: drawable.uniforms['name'] = value. Supports float, vec2/3/... + vert_margin: float # Vertical margin override (float, 0 = use general margin). Invalid for horizontally-centered alignments (CENTER_LEFT, CENTER_RIGHT, CENTER). + view: Any # Auto-created GridView for rendering (read-only). When Grid is appended to a scene, this view is what actually renders. + visible: bool # Whether the object is visible (bool). Invisible objects are not rendered or clickable. + w: Any # visible widget width + x: Any # top-left corner X-coordinate + y: Any # top-left corner Y-coordinate + z_index: Any # Z-order for rendering (lower values rendered first). + zoom: Any # Zoom level for rendering. + def add_layer(self, layer: ColorLayer | TileLayer) -> ColorLayer | TileLayer: + """Add a layer to the grid.""" + ... + def animate(self, property: str, target: Any, duration: float, easing=None, delta=False, loop=False, callback=None, conflict_mode='replace') -> Animation: + """Create and start an animation on this drawable's property.""" + ... + def apply_ranges(self, source: HeightMap, ranges: list) -> Grid: + """Apply multiple thresholds in a single pass.""" + ... + def apply_threshold(self, source: HeightMap, range: tuple, walkable: bool = None, transparent: bool = None) -> Grid: + """Apply walkable/transparent properties where heightmap values are in range.""" + ... + def at(self, *args, **kwargs) -> Any: ... + def center_camera(self, pos: tuple = None) -> None: + """Center the camera on a tile coordinate.""" + ... + def clear_dijkstra_maps(self) -> None: + """Clear all cached Dijkstra maps.""" + ... + def compute_fov(self, pos, radius: int = 0, light_walls: bool = True, algorithm: int = FOV_BASIC) -> None: + """Compute field of view from a position.""" + ... + def entities_in_radius(self, pos: tuple|Vector, radius: float) -> list[Entity]: + """Query entities within radius using spatial hash (O(k) where k = nearby entities).""" + ... + def find_path(self, start, end, diagonal_cost=1.41, collide=None) -> AStarPath | None: + """Compute A* path between two points.""" + ... + def get_dijkstra_map(self, root, diagonal_cost=1.41, collide=None) -> DijkstraMap: + """Get or create a Dijkstra distance map for a root position.""" + ... + def is_in_fov(self, pos) -> bool: + """Check if a cell is in the field of view.""" + ... + def layer(self, name: str) -> ColorLayer | TileLayer | None: + """Get a layer by its name.""" + ... + def move(self, *args, **kwargs) -> Any: + """move(dx, dy) or (delta) -> None""" + ... + def realign(self) -> None: + """Reapply alignment relative to parent, useful for responsive layouts.""" + ... + def remove_layer(self, name_or_layer: str | ColorLayer | TileLayer) -> None: + """Remove a layer from the grid.""" + ... + def resize(self, *args, **kwargs) -> Any: + """resize(width, height) or (size) -> None""" + ... + def step(self, n=1, turn_order=None) -> None: + """Execute n rounds of turn-based entity behavior.""" + ... + +class HeightMap: + """A 2D grid of float values for procedural generation.""" + def __init__(self, size: tuple[int, int], fill: float = 0.0) -> None: ... + size: Any # Dimensions (width, height) of the heightmap. Read-only. + def add(self, other: HeightMap, *, pos=None, source_pos=None, size=None) -> HeightMap: + """Add another heightmap's values to this one in the specified region.""" + ... + def add_bsp(self, bsp: BSP, *, pos=None, select: str = 'leaves', nodes: list = None, shrink: int = 0, value: float = 1.0) -> HeightMap: + """Add BSP node regions to heightmap. More efficient than creating intermediate HeightMap.""" + ... + def add_constant(self, value: float, *, pos=None, size=None) -> HeightMap: + """Add a constant value to cells in region.""" + ... + def add_hill(self, center, radius: float, height: float) -> HeightMap: + """Add a smooth hill at the specified position.""" + ... + def add_noise(self, source: NoiseSource, world_origin: tuple = (0.0, 0.0), world_size: tuple = None, mode: str = 'fbm', octaves: int = 4, scale: float = 1.0) -> HeightMap: + """Sample noise and add to current values. More efficient than creating intermediate HeightMap.""" + ... + def add_voronoi(self, num_points: int, coefficients: tuple = (1.0, -0.5), seed: int = None) -> HeightMap: + """Add Voronoi-based terrain features.""" + ... + def clamp(self, min: float = 0.0, max: float = 1.0, *, pos=None, size=None) -> HeightMap: + """Clamp values in region to the specified range.""" + ... + def clear(self) -> HeightMap: + """Set all cells to 0.0. Equivalent to fill(0.0).""" + ... + def copy_from(self, other: HeightMap, *, pos=None, source_pos=None, size=None) -> HeightMap: + """Copy values from another heightmap into the specified region.""" + ... + def count_in_range(self, range: tuple[float, float]) -> int: + """Count cells with values in the specified range (inclusive).""" + ... + def dig_bezier(self, points: tuple, start_radius: float, end_radius: float, start_height: float, end_height: float) -> HeightMap: + """Construct a canal along a cubic Bezier curve with specified heights.""" + ... + def dig_hill(self, center, radius: float, target_height: float) -> HeightMap: + """Construct a pit or crater with the specified center height.""" + ... + def fill(self, value: float, *, pos=None, size=None) -> HeightMap: + """Set cells in region to the specified value.""" + ... + def get(self, *args, **kwargs) -> Any: + """get(x, y) or (pos) -> float""" + ... + def get_interpolated(self, *args, **kwargs) -> Any: + """get_interpolated(x, y) or (pos) -> float""" + ... + def get_normal(self, *args, **kwargs) -> Any: + """get_normal(x, y, water_level=0.0) or (pos, water_level=0.0) -> tuple[float, float, float]""" + ... + def get_slope(self, *args, **kwargs) -> Any: + """get_slope(x, y) or (pos) -> float""" + ... + def inverse(self) -> HeightMap: + """Return NEW HeightMap with (1.0 - value) for each cell.""" + ... + def lerp(self, other: HeightMap, t: float, *, pos=None, source_pos=None, size=None) -> HeightMap: + """Linear interpolation between this and another heightmap in the specified region.""" + ... + def max(self, other: HeightMap, *, pos=None, source_pos=None, size=None) -> HeightMap: + """Set each cell in region to the maximum of this and another heightmap.""" + ... + def mid_point_displacement(self, roughness: float = 0.5, seed: int = None) -> HeightMap: + """Generate terrain using midpoint displacement algorithm (diamond-square).""" + ... + def min(self, other: HeightMap, *, pos=None, source_pos=None, size=None) -> HeightMap: + """Set each cell in region to the minimum of this and another heightmap.""" + ... + def min_max(self) -> tuple[float, float]: + """Get the minimum and maximum height values in the map.""" + ... + def multiply(self, other: HeightMap, *, pos=None, source_pos=None, size=None) -> HeightMap: + """Multiply this heightmap by another in the specified region (useful for masking).""" + ... + def multiply_bsp(self, bsp: BSP, *, pos=None, select: str = 'leaves', nodes: list = None, shrink: int = 0, value: float = 1.0) -> HeightMap: + """Multiply by BSP regions. Effectively masks the heightmap to node interiors.""" + ... + def multiply_noise(self, source: NoiseSource, world_origin: tuple = (0.0, 0.0), world_size: tuple = None, mode: str = 'fbm', octaves: int = 4, scale: float = 1.0) -> HeightMap: + """Sample noise and multiply with current values. Useful for applying noise-based masks.""" + ... + def normalize(self, min: float = 0.0, max: float = 1.0, *, pos=None, size=None) -> HeightMap: + """Linearly rescale values in region. Current min becomes new min, current max becomes new max.""" + ... + def rain_erosion(self, drops: int, erosion: float = 0.1, sedimentation: float = 0.05, seed: int = None) -> HeightMap: + """Simulate rain erosion on the terrain.""" + ... + def scale(self, factor: float, *, pos=None, size=None) -> HeightMap: + """Multiply cells in region by a factor.""" + ... + def smooth(self, iterations: int = 1) -> HeightMap: + """Smooth the heightmap by averaging neighboring cells.""" + ... + def sparse_kernel(self, weights: dict[tuple[int, int], float]) -> HeightMap: + """Apply sparse convolution kernel, returning a NEW HeightMap with results.""" + ... + def sparse_kernel_from(self, source: HeightMap, weights: dict[tuple[int, int], float]) -> None: + """Apply sparse convolution from source heightmap into self (for reusing destination buffers).""" + ... + def subtract(self, other: HeightMap, *, pos=None, source_pos=None, size=None) -> HeightMap: + """Subtract another heightmap's values from this one in the specified region.""" + ... + def threshold(self, range: tuple[float, float]) -> HeightMap: + """Return NEW HeightMap with original values where in range, 0.0 elsewhere.""" + ... + def threshold_binary(self, range: tuple[float, float], value: float = 1.0) -> HeightMap: + """Return NEW HeightMap with uniform value where in range, 0.0 elsewhere.""" + ... + +class Keyboard: + """Keyboard state singleton for checking modifier keys""" + def __init__(self, *args, **kwargs) -> None: ... + alt: Any # True if either Alt key is currently pressed (read-only). + ctrl: Any # True if either Control key is currently pressed (read-only). + shift: Any # True if either Shift key is currently pressed (read-only). + system: Any # True if either System key (Win/Cmd) is currently pressed (read-only). + +class LdtkProject: + """Load an LDtk project file (.ldtk).""" + def __init__(self, path: str) -> None: ... + enums: Any # Enum definitions from the project as a list of dicts (read-only). + level_names: Any # List of level identifier names (list[str], read-only). + ruleset_names: Any # List of rule set / layer names (list[str], read-only). + tileset_names: Any # List of tileset identifier names (list[str], read-only). + version: str # LDtk JSON format version string (str, read-only). + def level(self, name: str) -> dict: + """Get level data by name.""" + ... + def ruleset(self, name: str) -> AutoRuleSet: + """Get an auto-rule set by layer name.""" + ... + def tileset(self, name: str) -> TileSetFile: + """Get a tileset by name.""" + ... + +class Line: + """A line UI element for drawing straight lines between two points.""" + def __init__(self, start=None, end=None, thickness=1.0, color=None, **kwargs) -> None: ... + align: Any # Alignment relative to parent bounds (Alignment enum or None). When set, position is automatically calculated when parent is assigned or resized. Set to None ... + bounds: Any # Bounding box as (pos, size) tuple of Vectors. Returns (Vector(x, y), Vector(width, height)). + color: Any # Line color as a Color object. + end: Any # Ending point of the line as a Vector. + global_bounds: Any # Bounding box as (pos, size) tuple of Vectors in screen coordinates. Returns (Vector(x, y), Vector(width, height)). + global_position: Any # Global screen position (read-only). Calculates absolute position by walking up the parent chain. + grid_pos: Any # Position in grid tile coordinates (only when parent is Grid) + grid_size: Any # Size in grid tile coordinates (only when parent is Grid) + horiz_margin: float # Horizontal margin override (float, 0 = use general margin). Invalid for vertically-centered alignments (TOP_CENTER, BOTTOM_CENTER, CENTER). + hovered: Any # Whether mouse is currently over this element (read-only). Updated automatically by the engine during mouse movement. + margin: float # General margin from edge when aligned (float). Applied to both horizontal and vertical edges unless overridden. Invalid for CENTER alignment (raises ValueErr... + name: Any # Name for finding this element. + on_click: Any # Callable executed when line is clicked. Function receives (pos: Vector, button: str, action: str). + on_enter: Any # Callback for mouse enter events. Called with (pos: Vector, button: str, action: str) when mouse enters this element's bounds. + on_exit: Any # Callback for mouse exit events. Called with (pos: Vector, button: str, action: str) when mouse leaves this element's bounds. + on_move: Any # Callback for mouse movement within bounds. Called with (pos: Vector, button: str, action: str) for each mouse movement while inside. Performance note: Called... + opacity: Any # Opacity level (0.0 = transparent, 1.0 = opaque). Automatically clamped to valid range [0.0, 1.0]. + origin: Any # Transform origin as Vector (pivot point for rotation). Default (0,0) is top-left; set to (w/2, h/2) to rotate around center. + parent: Any # Parent drawable. Get: Returns the parent Frame/Grid if nested, or None if at scene level. Set: Assign a Frame/Grid to reparent, or None to remove from parent. + pos: Any # Position as a Vector (midpoint of line). + rotate_with_camera: bool # Whether to rotate visually with parent Grid's camera_rotation (bool). False (default): stay screen-aligned. True: tilt with camera. Only affects children of ... + rotation: Any # Rotation angle in degrees (clockwise around origin). Animatable property. + start: Any # Starting point of the line as a Vector. + thickness: Any # Line thickness in pixels. + vert_margin: float # Vertical margin override (float, 0 = use general margin). Invalid for horizontally-centered alignments (CENTER_LEFT, CENTER_RIGHT, CENTER). + visible: bool # Whether the object is visible (bool). Invisible objects are not rendered or clickable. + z_index: Any # Z-order for rendering (lower values rendered first). + def animate(self, property: str, target: Any, duration: float, easing=None, delta=False, loop=False, callback=None, conflict_mode='replace') -> Animation: + """Create and start an animation on this drawable's property.""" + ... + def move(self, *args, **kwargs) -> Any: + """move(dx, dy) or (delta) -> None""" + ... + def realign(self) -> None: + """Reapply alignment relative to parent, useful for responsive layouts.""" + ... + def resize(self, *args, **kwargs) -> Any: + """resize(width, height) or (size) -> None""" + ... + +class Model3D: + """A 3D model resource that can be rendered by Entity3D.""" + def __init__(self, path=None) -> None: ... + animation_clips: Any # List of animation clip names (read-only) + bone_count: Any # Number of bones in skeleton (read-only) + bounds: Any # AABB as ((min_x, min_y, min_z), (max_x, max_y, max_z)) (read-only) + has_skeleton: Any # Whether model has skeletal animation data (read-only) + mesh_count: Any # Number of submeshes (read-only) + name: Any # Model name (read-only) + triangle_count: Any # Total triangle count across all meshes (read-only) + vertex_count: Any # Total vertex count across all meshes (read-only) + def cube(self, size=1.0) -> Model3D: + """Create a unit cube centered at origin.""" + ... + def plane(self, width=1.0, depth=1.0, segments=1) -> Model3D: + """Create a flat plane.""" + ... + def sphere(self, radius=0.5, segments=16, rings=12) -> Model3D: + """Create a UV sphere.""" + ... + +class Mouse: + """Mouse state singleton for reading button/position state and controlling cursor visibility""" + def __init__(self, *args, **kwargs) -> None: ... + grabbed: Any # Whether the mouse cursor is confined to the window (default: False). + left: Any # True if left mouse button is currently pressed (read-only). + middle: Any # True if middle mouse button is currently pressed (read-only). + pos: Any # Current mouse position as Vector (read-only). + right: Any # True if right mouse button is currently pressed (read-only). + visible: Any # Whether the mouse cursor is visible (default: True). + x: Any # Current mouse X position in window coordinates (read-only). + y: Any # Current mouse Y position in window coordinates (read-only). + +class Music: + """Streaming music object for longer audio tracks""" + def __init__(self, *args, **kwargs) -> None: ... + duration: Any # Total duration of the music in seconds (read-only). + loop: Any # Whether the music loops when it reaches the end. + playing: Any # True if the music is currently playing (read-only). + position: Any # Current playback position in seconds. Can be set to seek. + source: Any # Filename path used to load this music (read-only). + volume: Any # Volume level from 0 (silent) to 100 (full volume). + def pause(self) -> None: + """Pause the music. Use play() to resume from the paused position.""" + ... + def play(self) -> None: + """Start or resume playing the music.""" + ... + def stop(self) -> None: + """Stop playing and reset to the beginning.""" + ... + +class NoiseSource: + """A configured noise generator for procedural generation.""" + def __init__(self, dimensions: int = 2, algorithm: str = 'simplex', hurst: float = 0.5, lacunarity: float = 2.0, seed: int = None) -> None: ... + algorithm: Any # Noise algorithm name ('simplex', 'perlin', or 'wavelet'). Read-only. + dimensions: Any # Number of input dimensions (1-4). Read-only. + hurst: Any # Hurst exponent for fbm/turbulence. Read-only. + lacunarity: Any # Frequency multiplier between octaves. Read-only. + seed: Any # Random seed used (even if originally None). Read-only. + def fbm(self, pos: tuple[float, ...], octaves: int = 4) -> float: + """Get fractal brownian motion value at coordinates.""" + ... + def get(self, pos: tuple[float, ...]) -> float: + """Get flat noise value at coordinates.""" + ... + def sample(self, size: tuple[int, int], world_origin: tuple[float, float] = (0.0, 0.0), world_size: tuple[float, float] = None, mode: str = 'fbm', octaves: int = 4) -> HeightMap: + """Sample noise into a HeightMap for batch processing.""" + ... + def turbulence(self, pos: tuple[float, ...], octaves: int = 4) -> float: + """Get turbulence (absolute fbm) value at coordinates.""" + ... + +class PropertyBinding: + """A binding that reads a property value from a UI drawable.""" + def __init__(self, target: UIDrawable, property: str) -> None: ... + is_valid: bool # True if the binding target still exists and property is valid (bool, read-only). + property: str # The property name being read (str, read-only). + target: Any # The drawable this binding reads from (read-only). + value: float # Current value of the binding (float, read-only). Returns None if invalid. + +class Scene: + """Object-oriented scene management with lifecycle callbacks.""" + def __init__(self, name: str) -> None: ... + active: bool # Whether this scene is currently active (bool, read-only). Only one scene can be active at a time. + children: UICollection # UI element collection for this scene (UICollection, read-only). Use to add, remove, or iterate over UI elements. Changes are reflected immediately. + name: str # Scene name (str, read-only). Unique identifier for this scene. + on_key: Any # Keyboard event handler (callable or None). Function receives (key: Key, action: InputState) for keyboard events. Set to None to remove the handler. + opacity: Any # Scene opacity (0.0-1.0). Applied to all UI elements during rendering. + pos: Vector # Scene position offset (Vector). Applied to all UI elements during rendering. + registered: bool # Whether this scene is registered with the game engine (bool, read-only). Unregistered scenes still exist but won't receive lifecycle callbacks. + visible: bool # Scene visibility (bool). If False, scene is not rendered. + def activate(self, transition: Transition = None, duration: float = None) -> None: + """Make this the active scene with optional transition effect.""" + ... + def realign(self) -> None: + """Recalculate alignment for all children with alignment set.""" + ... + def register(self) -> None: + """Register this scene with the game engine.""" + ... + def unregister(self) -> None: + """Unregister this scene from the game engine.""" + ... + +class Shader: + """A GPU shader program for visual effects.""" + def __init__(self, fragment_source: str, dynamic: bool = False) -> None: ... + dynamic: bool # Whether this shader uses time-varying effects (bool). Dynamic shaders invalidate parent caches each frame. + is_valid: bool # True if the shader compiled successfully (bool, read-only). + source: str # The GLSL fragment shader source code (str, read-only). + def set_uniform(self, name: str, value: float|tuple) -> None: + """Set a custom uniform value on this shader.""" + ... + +class Sound: + """Sound effect object for short audio clips.""" + def __init__(self, source) -> None: ... + buffer: Any # The SoundBuffer if created from one, else None (read-only). + duration: Any # Total duration of the sound in seconds (read-only). + loop: Any # Whether the sound loops when it reaches the end. + pitch: Any # Playback pitch multiplier (1.0 = normal, >1 = higher, <1 = lower). + playing: Any # True if the sound is currently playing (read-only). + source: Any # Filename path used to load this sound (read-only). + volume: Any # Volume level from 0 (silent) to 100 (full volume). + def pause(self) -> None: + """Pause the sound. Use play() to resume from the paused position.""" + ... + def play(self) -> None: + """Start or resume playing the sound.""" + ... + def play_varied(self, pitch_range: float = 0.1, volume_range: float = 3.0) -> None: + """Play with randomized pitch and volume for natural variation.""" + ... + def stop(self) -> None: + """Stop playing and reset to the beginning.""" + ... + +class SoundBuffer: + """SoundBuffer.from_samples(data: bytes, channels: int, sample_rate: int)""" + def __init__(self, filename: str) -> None: ... + channels: Any # Number of audio channels (read-only). + duration: Any # Total duration in seconds (read-only). + sample_count: Any # Total number of samples (read-only). + sample_rate: Any # Sample rate in Hz (read-only). + sfxr_params: Any # Dict of sfxr parameters if sfxr-generated, else None (read-only). + def bit_crush(self, bits: int, rate_divisor: int) -> SoundBuffer: + """Reduce bit depth and sample rate for lo-fi effect.""" + ... + def concat(self, buffers: list[SoundBuffer], overlap: float = 0.0) -> SoundBuffer: + """Concatenate multiple SoundBuffers with optional crossfade overlap.""" + ... + def distortion(self, drive: float) -> SoundBuffer: + """Apply tanh soft clipping distortion.""" + ... + def echo(self, delay_ms: float, feedback: float, wet: float) -> SoundBuffer: + """Apply echo effect with delay, feedback, and wet/dry mix.""" + ... + def from_samples(self, data: bytes, channels: int, sample_rate: int) -> SoundBuffer: + """Create a SoundBuffer from raw int16 PCM sample data.""" + ... + def gain(self, factor: float) -> SoundBuffer: + """Multiply all samples by a scalar factor. Use for volume/amplitude control before mixing.""" + ... + def high_pass(self, cutoff_hz: float) -> SoundBuffer: + """Apply single-pole IIR high-pass filter.""" + ... + def low_pass(self, cutoff_hz: float) -> SoundBuffer: + """Apply single-pole IIR low-pass filter.""" + ... + def mix(self, buffers: list[SoundBuffer]) -> SoundBuffer: + """Mix multiple SoundBuffers together (additive, clamped).""" + ... + def normalize(self) -> SoundBuffer: + """Scale samples to 95%% of int16 max.""" + ... + def pitch_shift(self, factor: float) -> SoundBuffer: + """Resample to shift pitch. factor>1 = higher+shorter.""" + ... + def reverb(self, room_size: float, damping: float, wet: float) -> SoundBuffer: + """Apply simplified Freeverb-style reverb.""" + ... + def reverse(self) -> SoundBuffer: + """Reverse the sample order.""" + ... + def sfxr(self, preset: str = None, seed: int = None, **params) -> SoundBuffer: + """Generate retro sound effects using sfxr synthesis.""" + ... + def sfxr_mutate(self, amount: float = 0.05, seed: int = None) -> SoundBuffer: + """Jitter sfxr params and re-synthesize. Only works on sfxr-generated buffers.""" + ... + def slice(self, start: float, end: float) -> SoundBuffer: + """Extract a time range in seconds.""" + ... + def tone(self, frequency: float, duration: float, waveform: str = 'sine', **kwargs) -> SoundBuffer: + """Generate a tone with optional ADSR envelope.""" + ... + +class Sprite: + """A sprite UI element that displays a texture or portion of a texture atlas.""" + def __init__(self, pos=None, texture=None, sprite_index=0, **kwargs) -> None: ... + align: Any # Alignment relative to parent bounds (Alignment enum or None). When set, position is automatically calculated when parent is assigned or resized. Set to None ... + bounds: Any # Bounding box as (pos, size) tuple of Vectors. Returns (Vector(x, y), Vector(width, height)). + global_bounds: Any # Bounding box as (pos, size) tuple of Vectors in screen coordinates. Returns (Vector(x, y), Vector(width, height)). + global_position: Any # Global screen position (read-only). Calculates absolute position by walking up the parent chain. + grid_pos: Any # Position in grid tile coordinates (only when parent is Grid) + grid_size: Any # Size in grid tile coordinates (only when parent is Grid) + horiz_margin: float # Horizontal margin override (float, 0 = use general margin). Invalid for vertically-centered alignments (TOP_CENTER, BOTTOM_CENTER, CENTER). + hovered: Any # Whether mouse is currently over this element (read-only). Updated automatically by the engine during mouse movement. + margin: float # General margin from edge when aligned (float). Applied to both horizontal and vertical edges unless overridden. Invalid for CENTER alignment (raises ValueErr... + name: Any # Name for finding elements + on_click: Any # Callable executed when object is clicked. Function receives (pos: Vector, button: str, action: str). + on_enter: Any # Callback for mouse enter events. Called with (pos: Vector, button: str, action: str) when mouse enters this element's bounds. + on_exit: Any # Callback for mouse exit events. Called with (pos: Vector, button: str, action: str) when mouse leaves this element's bounds. + on_move: Any # Callback for mouse movement within bounds. Called with (pos: Vector, button: str, action: str) for each mouse movement while inside. Performance note: Called... + opacity: Any # Opacity level (0.0 = transparent, 1.0 = opaque). Automatically clamped to valid range [0.0, 1.0]. + origin: Any # Transform origin as Vector (pivot point for rotation). Default (0,0) is top-left; set to (w/2, h/2) to rotate around center. + parent: Any # Parent drawable. Get: Returns the parent Frame/Grid if nested, or None if at scene level. Set: Assign a Frame/Grid to reparent, or None to remove from parent. + pos: Any # Position as a Vector + rotate_with_camera: bool # Whether to rotate visually with parent Grid's camera_rotation (bool). False (default): stay screen-aligned. True: tilt with camera. Only affects children of ... + rotation: Any # Rotation angle in degrees (clockwise around origin). Animatable property. + scale: Any # Uniform size factor + scale_x: Any # Horizontal scale factor + scale_y: Any # Vertical scale factor + shader: Any # Shader for GPU visual effects (Shader or None). When set, the drawable is rendered through the shader program. Set to None to disable shader effects. + sprite_index: Any # Which sprite on the texture is shown + texture: Any # Texture object + uniforms: Any # Collection of shader uniforms (read-only access to collection). Set uniforms via dict-like syntax: drawable.uniforms['name'] = value. Supports float, vec2/3/... + vert_margin: float # Vertical margin override (float, 0 = use general margin). Invalid for horizontally-centered alignments (CENTER_LEFT, CENTER_RIGHT, CENTER). + visible: bool # Whether the object is visible (bool). Invisible objects are not rendered or clickable. + x: Any # X coordinate of top-left corner + y: Any # Y coordinate of top-left corner + z_index: Any # Z-order for rendering (lower values rendered first). Automatically triggers scene resort when changed. + def animate(self, property: str, target: Any, duration: float, easing=None, delta=False, loop=False, callback=None, conflict_mode='replace') -> Animation: + """Create and start an animation on this drawable's property.""" + ... + def move(self, *args, **kwargs) -> Any: + """move(dx, dy) or (delta) -> None""" + ... + def realign(self) -> None: + """Reapply alignment relative to parent, useful for responsive layouts.""" + ... + def resize(self, *args, **kwargs) -> Any: + """resize(width, height) or (size) -> None""" + ... + +class Texture: + """A texture atlas for sprites and tiles.""" + def __init__(self, filename: str, sprite_width: int = 0, sprite_height: int = 0, display_size: tuple = None, display_origin: tuple = None) -> None: ... + display_height: int # Display height of sprite content within each cell (int, read-only). Defaults to sprite_height. + display_offset_x: int # X offset of sprite content within each cell (int, read-only). Default 0. + display_offset_y: int # Y offset of sprite content within each cell (int, read-only). Default 0. + display_width: int # Display width of sprite content within each cell (int, read-only). Defaults to sprite_width. + sheet_height: int # Number of sprite rows in the texture sheet (int, read-only). Calculated as texture_height / sprite_height. + sheet_width: int # Number of sprite columns in the texture sheet (int, read-only). Calculated as texture_width / sprite_width. + source: str # Source filename path (str, read-only). The path used to load this texture. + sprite_count: int # Total number of sprites in the texture sheet (int, read-only). Equals sheet_width * sheet_height. + sprite_height: int # Height of each sprite in pixels (int, read-only). Specified during texture initialization. + sprite_width: int # Width of each sprite in pixels (int, read-only). Specified during texture initialization. + def composite(self, layers: list[Texture], sprite_width: int, sprite_height: int, name: str = '') -> Texture: + """Alpha-composite multiple texture layers into a single texture.""" + ... + def from_bytes(self, data: bytes, width: int, height: int, sprite_width: int, sprite_height: int, name: str = '') -> Texture: + """Create a Texture from raw RGBA pixel data.""" + ... + def hsl_shift(self, hue_shift: float, sat_shift: float = 0.0, lit_shift: float = 0.0) -> Texture: + """Create a new texture with HSL color adjustments applied.""" + ... + +class TileLayer: + """A grid layer that stores sprite indices per cell for tile-based rendering.""" + def __init__(self, z_index=-1, name=None, texture=None, grid_size=None) -> None: ... + grid: Any # Parent Grid or None. Setting manages layer association and handles lazy allocation. + grid_size: Any # Layer dimensions as (width, height) tuple. + name: str # Layer name (str, read-only). Used for Grid.layer(name) lookup. + texture: Any # Texture atlas for tile sprites. + visible: Any # Whether the layer is rendered. + z_index: Any # Layer z-order. Negative values render below entities. + def apply_ranges(self, source, ranges) -> TileLayer: + """Apply multiple tile assignments in a single pass.""" + ... + def apply_threshold(self, source, range, tile) -> TileLayer: + """Set tile index for cells where HeightMap value is within range.""" + ... + def at(self, pos) -> int: + """at(x, y) -> int""" + ... + def fill(self, index) -> Any: + """Fill the entire layer with the specified tile index.""" + ... + def fill_rect(self, pos, size, index) -> Any: + """Fill a rectangular region with a tile index.""" + ... + def set(self, pos, index) -> Any: + """Set the tile index at cell position. Use -1 for no tile.""" + ... + +class TileMapFile: + """Load a Tiled map file (.tmx or .tmj).""" + def __init__(self, path: str) -> None: ... + height: int # Map height in tiles (int, read-only). + object_layer_names: Any # List of object layer names (read-only). + orientation: str # Map orientation, e.g. 'orthogonal' (str, read-only). + properties: Any # Custom map properties as a dict (read-only). + tile_height: int # Tile height in pixels (int, read-only). + tile_layer_names: Any # List of tile layer names (read-only). + tile_width: int # Tile width in pixels (int, read-only). + tileset_count: int # Number of referenced tilesets (int, read-only). + width: int # Map width in tiles (int, read-only). + def apply_to_tile_layer(self, tile_layer: TileLayer, layer_name: str, tileset_index: int = 0) -> None: + """Resolve GIDs and write sprite indices into a TileLayer.""" + ... + def object_layer(self, name: str) -> list[dict]: + """Get objects from an object layer as Python dicts.""" + ... + def resolve_gid(self, gid: int) -> tuple[int, int]: + """Resolve a global tile ID to tileset index and local tile ID.""" + ... + def tile_layer_data(self, name: str) -> list[int]: + """Get raw global GID data for a tile layer.""" + ... + def tileset(self, index: int) -> tuple[int, TileSetFile]: + """Get a referenced tileset by index.""" + ... + +class TileSetFile: + """Load a Tiled tileset file (.tsx or .tsj).""" + def __init__(self, path: str) -> None: ... + columns: int # Number of columns in tileset image (int, read-only). + image_source: str # Resolved path to the tileset image file (str, read-only). + margin: int # Margin around tiles in pixels (int, read-only). + name: str # Tileset name (str, read-only). + properties: Any # Custom tileset properties as a dict (read-only). + spacing: int # Spacing between tiles in pixels (int, read-only). + tile_count: int # Total number of tiles (int, read-only). + tile_height: int # Height of each tile in pixels (int, read-only). + tile_width: int # Width of each tile in pixels (int, read-only). + wang_sets: Any # List of WangSet objects from this tileset (read-only). + def tile_info(self, tile_id: int) -> dict | None: + """Get metadata for a specific tile.""" + ... + def to_texture(self) -> Texture: + """Create a Texture from the tileset image.""" + ... + def wang_set(self, name: str) -> WangSet: + """Look up a WangSet by name.""" + ... + +class Timer: + """Create a timer that calls a function at regular intervals.""" + def __init__(self, name, callback, interval, once=False, start=True) -> None: ... + active: bool # Running state (bool, read-write). True if running (not paused, not stopped). Set True to start/resume, False to pause. + callback: Callable # The callback function (callable). Preserved when stopped, allowing timer restart. + interval: int # Timer interval in milliseconds (int). Must be positive. Can be changed while timer is running. + name: str # Timer name (str, read-only). Unique identifier for this timer. + once: bool # Whether the timer stops after firing once (bool). One-shot timers can be restarted. + paused: bool # Whether the timer is paused (bool, read-only). Paused timers preserve their remaining time. + remaining: int # Time remaining until next trigger in milliseconds (int, read-only). Full interval when stopped. + stopped: bool # Whether the timer is stopped (bool, read-only). Stopped timers are not in the engine tick loop but preserve their callback. + def pause(self) -> None: + """Pause the timer, preserving the time remaining until next trigger.""" + ... + def restart(self) -> None: + """Restart the timer from the beginning and ensure it's running.""" + ... + def resume(self) -> None: + """Resume a paused timer from where it left off.""" + ... + def start(self) -> None: + """Start the timer, adding it to the engine tick loop.""" + ... + def stop(self) -> None: + """Stop the timer and remove it from the engine tick loop.""" + ... + +class Vector: + """2D vector for positions, sizes, and directions.""" + def __init__(self, x: float = 0, y: float = 0) -> None: ... + int: Any # Integer tuple (floor of x and y) for use as dict keys. Read-only. + x: float # X coordinate of the vector (float) + y: float # Y coordinate of the vector (float) + def angle(self) -> float: + """Get the angle of this vector in radians.""" + ... + def copy(self) -> Vector: + """Create a copy of this vector.""" + ... + def distance_to(self, other: Vector) -> float: + """Calculate the distance to another vector.""" + ... + def dot(self, other: Vector) -> float: + """Calculate the dot product with another vector.""" + ... + def floor(self) -> Vector: + """Return a new vector with floored (integer) coordinates.""" + ... + def magnitude(self) -> float: + """Calculate the length/magnitude of this vector.""" + ... + def magnitude_squared(self) -> float: + """Calculate the squared magnitude of this vector.""" + ... + def normalize(self) -> Vector: + """Return a unit vector in the same direction.""" + ... + +class Viewport3D: + """A 3D rendering viewport that displays a 3D scene as a UI element.""" + def __init__(self, pos=None, size=None, **kwargs) -> None: ... + bg_color: Any # Background clear color. + bounds: Any # Bounding box as (pos, size) tuple of Vectors. Returns (Vector(x, y), Vector(width, height)). + camera_pos: Any # Camera position as (x, y, z) tuple. + camera_target: Any # Camera look-at target as (x, y, z) tuple. + cell_size: Any # World units per navigation grid cell. + enable_affine: Any # Enable PS1-style affine texture mapping (warped textures). + enable_dither: Any # Enable PS1-style color dithering. + enable_fog: Any # Enable distance fog. + enable_vertex_snap: Any # Enable PS1-style vertex snapping (jittery vertices). + entities: Any # Collection of Entity3D objects (read-only). Use append/remove to modify. + fog_color: Any # Fog color. + fog_far: Any # Fog end distance. + fog_near: Any # Fog start distance. + fov: Any # Camera field of view in degrees. + global_bounds: Any # Bounding box as (pos, size) tuple of Vectors in screen coordinates. Returns (Vector(x, y), Vector(width, height)). + global_position: Any # Global screen position (read-only). Calculates absolute position by walking up the parent chain. + grid_size: Any # Navigation grid dimensions as (width, depth) tuple. + h: Any # Display height in pixels. + hovered: Any # Whether mouse is currently over this element (read-only). Updated automatically by the engine during mouse movement. + on_click: Any # Callable executed when object is clicked. Function receives (pos: Vector, button: str, action: str). + on_enter: Any # Callback for mouse enter events. Called with (pos: Vector, button: str, action: str) when mouse enters this element's bounds. + on_exit: Any # Callback for mouse exit events. Called with (pos: Vector, button: str, action: str) when mouse leaves this element's bounds. + on_move: Any # Callback for mouse movement within bounds. Called with (pos: Vector, button: str, action: str) for each mouse movement while inside. Performance note: Called... + opacity: Any # Opacity level (0.0 = transparent, 1.0 = opaque). Automatically clamped to valid range [0.0, 1.0]. + parent: Any # Parent drawable. Get: Returns the parent Frame/Grid if nested, or None if at scene level. Set: Assign a Frame/Grid to reparent, or None to remove from parent. + pos: Any # Position as Vector (x, y). + render_resolution: Any # Internal render resolution (width, height). Lower values for PS1 effect. + visible: bool # Whether the object is visible (bool). Invisible objects are not rendered or clickable. + w: Any # Display width in pixels. + x: Any # X position in pixels. + y: Any # Y position in pixels. + z_index: Any # Z-order for rendering (lower values rendered first). Automatically triggers scene resort when changed. + def add_billboard(self, billboard) -> Any: + """Add a Billboard to the viewport.""" + ... + def add_layer(self, name, z_index=0) -> dict: + """Add a new mesh layer to the viewport.""" + ... + def add_mesh(self, layer_name, model, pos, rotation=0, scale=1.0) -> int: + """Add a Model3D instance to a layer at the specified position.""" + ... + def add_voxel_layer(self, voxel_grid, z_index=0) -> Any: + """Add a VoxelGrid as a renderable layer.""" + ... + def animate(self, property: str, target: Any, duration: float, easing=None, delta=False, loop=False, callback=None, conflict_mode='replace') -> Animation: + """Create and start an animation on this drawable's property.""" + ... + def apply_heightmap(self, heightmap, y_scale=1.0) -> Any: + """Set cell heights from HeightMap.""" + ... + def apply_terrain_colors(self, layer_name, r_map, g_map, b_map) -> Any: + """Apply per-vertex colors to terrain from RGB HeightMaps.""" + ... + def apply_threshold(self, heightmap, min_height, max_height, walkable=True) -> Any: + """Set cell walkability based on height thresholds.""" + ... + def at(self, x, z) -> VoxelPoint: + """Get VoxelPoint at grid coordinates.""" + ... + def billboard_count(self) -> int: + """Get the number of billboards.""" + ... + def build_terrain(self, layer_name, heightmap, y_scale=1.0, cell_size=1.0) -> int: + """Build terrain mesh from HeightMap on specified layer.""" + ... + def clear_billboards(self) -> Any: + """Remove all billboards from the viewport.""" + ... + def clear_meshes(self, layer_name) -> Any: + """Clear all mesh instances from a layer.""" + ... + def clear_voxel_nav_region(self, voxel_grid) -> Any: + """Clear navigation cells in a voxel grid's footprint.""" + ... + def compute_fov(self, origin, radius=10) -> list: + """Compute field of view from a position.""" + ... + def find_path(self, start, end) -> list: + """Find A* path between two points.""" + ... + def follow(self, entity, distance=10, height=5, smoothing=1.0) -> Any: + """Position camera to follow an entity.""" + ... + def get_billboard(self, index) -> Billboard: + """Get a Billboard by index.""" + ... + def get_layer(self, name) -> dict or None: + """Get a layer by name.""" + ... + def is_in_fov(self, x, z) -> bool: + """Check if a cell is in the current FOV (after compute_fov).""" + ... + def layer_count(self) -> int: + """Get the number of mesh layers.""" + ... + def move(self, *args, **kwargs) -> Any: + """move(dx, dy) or (delta) -> None""" + ... + def orbit_camera(self, angle=0, distance=10, height=5) -> Any: + """Position camera to orbit around origin.""" + ... + def place_blocking(self, grid_pos, footprint, walkable=False, transparent=False) -> Any: + """Mark grid cells as blocking for pathfinding and FOV.""" + ... + def project_all_voxels_to_nav(self, headroom=2) -> Any: + """Project all voxel layers to the navigation grid.""" + ... + def project_voxel_to_nav(self, voxel_grid, headroom=2) -> Any: + """Project a VoxelGrid to the navigation grid.""" + ... + def realign(self) -> None: + """Reapply alignment relative to parent, useful for responsive layouts.""" + ... + def remove_billboard(self, billboard) -> Any: + """Remove a Billboard from the viewport.""" + ... + def remove_layer(self, name) -> bool: + """Remove a layer by name. Returns True if found and removed.""" + ... + def remove_voxel_layer(self, voxel_grid) -> bool: + """Remove a VoxelGrid layer from the viewport.""" + ... + def resize(self, *args, **kwargs) -> Any: + """resize(width, height) or (size) -> None""" + ... + def screen_to_world(self, x, y, y_plane=0.0) -> tuple or None: + """Convert screen coordinates to world position via ray casting.""" + ... + def set_grid_size(self, width, depth) -> Any: + """Initialize navigation grid with specified dimensions.""" + ... + def set_slope_cost(self, max_slope=0.5, cost_multiplier=1.0) -> Any: + """Calculate slope costs and mark steep cells unwalkable.""" + ... + def voxel_layer_count(self) -> int: + """Get the number of voxel layers.""" + ... + +class VoxelGrid: + """A dense 3D grid of voxel material IDs with a material palette.""" + def __init__(self, size: tuple[int, int, int], cell_size: float = 1.0) -> None: ... + cell_size: Any # World units per voxel. Read-only. + depth: Any # Grid depth (Z dimension). Read-only. + greedy_meshing: Any # Enable greedy meshing optimization (reduces vertex count for uniform regions). + height: Any # Grid height (Y dimension). Read-only. + material_count: Any # Number of materials in the palette. Read-only. + offset: Any # World-space position (x, y, z) of the grid origin. + rotation: Any # Y-axis rotation in degrees. + size: Any # Dimensions (width, height, depth) of the grid. Read-only. + vertex_count: Any # Number of vertices after mesh generation. Read-only. + visible: Any # Show or hide this voxel grid in rendering. + width: Any # Grid width (X dimension). Read-only. + def add_material(self, name, color=Color(255, 255, 255), sprite_index=-1, transparent=False, path_cost=1.0) -> int: + """Add a new material to the palette. Returns the material ID (1-indexed).""" + ... + def clear(self) -> None: + """Clear the grid (fill with air, material 0).""" + ... + def copy_region(self, min_coord, max_coord) -> VoxelRegion: + """Copy a rectangular region to a VoxelRegion prefab.""" + ... + def count_material(self, material) -> int: + """Count the number of voxels with the specified material ID.""" + ... + def count_non_air(self) -> int: + """Count the number of non-air voxels in the grid.""" + ... + def fill(self, material) -> None: + """Fill the entire grid with the specified material ID.""" + ... + def fill_box(self, min_coord, max_coord, material) -> None: + """Fill a rectangular region with the specified material.""" + ... + def fill_box_hollow(self, min_coord, max_coord, material, thickness=1) -> None: + """Create a hollow rectangular room (walls only, hollow inside).""" + ... + def fill_cylinder(self, base_pos, radius, height, material) -> None: + """Fill a vertical cylinder (Y-axis aligned).""" + ... + def fill_noise(self, min_coord, max_coord, material, threshold=0.5, scale=0.1, seed=0) -> None: + """Fill region with 3D noise-based pattern (caves, clouds).""" + ... + def fill_sphere(self, center, radius, material) -> None: + """Fill a spherical region.""" + ... + def from_bytes(self, data) -> bool: + """Load voxel data from a bytes object.""" + ... + def get(self, x, y, z) -> int: + """Get the material ID at integer coordinates.""" + ... + def get_material(self, id) -> dict: + """Get material properties by ID.""" + ... + def load(self, path) -> bool: + """Load voxel data from a binary file.""" + ... + def paste_region(self, region, position, skip_air=True) -> None: + """Paste a VoxelRegion prefab at the specified position.""" + ... + def project_column(self, x, z, headroom=2) -> dict: + """Project a single column to navigation info.""" + ... + def rebuild_mesh(self) -> None: + """Force immediate mesh rebuild for rendering.""" + ... + def save(self, path) -> bool: + """Save the voxel grid to a binary file.""" + ... + def set(self, x, y, z, material) -> None: + """Set the material ID at integer coordinates.""" + ... + def to_bytes(self) -> bytes: + """Serialize the voxel grid to a bytes object.""" + ... + +class VoxelRegion: + """VoxelRegion - Portable voxel data for copy/paste operations.""" + def __init__(self, *args, **kwargs) -> None: ... + depth: Any # Region depth. Read-only. + height: Any # Region height. Read-only. + size: Any # Dimensions (width, height, depth) of the region. Read-only. + width: Any # Region width. Read-only. + +class WangSet: + """WangSet - Wang terrain auto-tile set from a Tiled tileset.""" + def __init__(self, *args, **kwargs) -> None: ... + color_count: int # Number of terrain colors (int, read-only). + colors: Any # List of color dicts with name, index, tile_id, probability (read-only). + name: str # Wang set name (str, read-only). + type: str # Wang set type: 'corner', 'edge', or 'mixed' (str, read-only). + def apply(self, discrete_map: DiscreteMap, tile_layer: TileLayer) -> None: + """Resolve terrain and write tile indices directly into a TileLayer.""" + ... + def resolve(self, discrete_map: DiscreteMap) -> list[int]: + """Resolve terrain data to tile indices using Wang tile rules.""" + ... + def terrain_enum(self) -> IntEnum: + """Generate a Python IntEnum from this WangSet's terrain colors.""" + ... + +class Window: + """Window singleton for accessing and modifying the game window properties""" + def __init__(self, *args, **kwargs) -> None: ... + framerate_limit: int # Frame rate limit in FPS (int, 0 for unlimited). Caps maximum frame rate. + fullscreen: bool # Window fullscreen state (bool). Setting this recreates the window. + game_resolution: Any # Fixed game resolution as (width, height) tuple. Enables resolution-independent rendering with scaling. + resolution: Any # Window resolution as (width, height) tuple. Setting this recreates the window. + scaling_mode: str # Viewport scaling mode (str): 'center' (no scaling), 'stretch' (fill window), or 'fit' (maintain aspect ratio). + title: str # Window title string (str). Displayed in the window title bar. + visible: bool # Window visibility state (bool). Hidden windows still process events. + vsync: bool # Vertical sync enabled state (bool). Prevents screen tearing but may limit framerate. + def center(self) -> None: + """Center the window on the screen.""" + ... + def get(self) -> Window: + """Get the Window singleton instance.""" + ... + def screenshot(self, filename: str = None) -> bytes | None: + """Take a screenshot of the current window contents.""" + ... + +# --- Submodules --------------------------------------------------------- +class _automation_module: + """Stub for mcrfpy.automation submodule.""" + @staticmethod + def click(self, *args, **kwargs) -> Any: + """click(pos=None, clicks=1, interval=0.0, button='left') - Click at position. Accepts (x,y) tuple, [x,y] list, Vector, or None for current position.""" + ... + @staticmethod + def doubleClick(self, *args, **kwargs) -> Any: + """doubleClick(pos=None) - Double click at position. Accepts (x,y) tuple, [x,y] list, Vector, or None for current position.""" + ... + @staticmethod + def dragRel(self, *args, **kwargs) -> Any: + """dragRel(offset, duration=0.0, button='left') - Drag mouse relative to current position. Accepts (x,y) tuple, [x,y] list, or Vector.""" + ... + @staticmethod + def dragTo(self, *args, **kwargs) -> Any: + """dragTo(pos, duration=0.0, button='left') - Drag mouse to position. Accepts (x,y) tuple, [x,y] list, or Vector.""" + ... + @staticmethod + def hotkey(self, *args, **kwargs) -> Any: + """hotkey(*keys) - Press a hotkey combination (e.g., hotkey('ctrl', 'c'))""" + ... + @staticmethod + def keyDown(self, *args, **kwargs) -> Any: + """keyDown(key) - Press and hold a key""" + ... + @staticmethod + def keyUp(self, *args, **kwargs) -> Any: + """keyUp(key) - Release a key""" + ... + @staticmethod + def middleClick(self, *args, **kwargs) -> Any: + """middleClick(pos=None) - Middle click at position. Accepts (x,y) tuple, [x,y] list, Vector, or None for current position.""" + ... + @staticmethod + def mouseDown(self, *args, **kwargs) -> Any: + """mouseDown(pos=None, button='left') - Press mouse button at position. Accepts (x,y) tuple, [x,y] list, Vector, or None for current position.""" + ... + @staticmethod + def mouseUp(self, *args, **kwargs) -> Any: + """mouseUp(pos=None, button='left') - Release mouse button at position. Accepts (x,y) tuple, [x,y] list, Vector, or None for current position.""" + ... + @staticmethod + def moveRel(self, *args, **kwargs) -> Any: + """moveRel(offset, duration=0.0) - Move mouse relative to current position. Accepts (x,y) tuple, [x,y] list, or Vector.""" + ... + @staticmethod + def moveTo(self, *args, **kwargs) -> Any: + """moveTo(pos, duration=0.0) - Move mouse to position. Accepts (x,y) tuple, [x,y] list, or Vector.""" + ... + @staticmethod + def onScreen(self, *args, **kwargs) -> Any: + """onScreen(pos) - Check if position is within screen bounds. Accepts (x,y) tuple, [x,y] list, or Vector.""" + ... + @staticmethod + def position(self, *args, **kwargs) -> Any: + """position() - Get current mouse position as Vector""" + ... + @staticmethod + def rightClick(self, *args, **kwargs) -> Any: + """rightClick(pos=None) - Right click at position. Accepts (x,y) tuple, [x,y] list, Vector, or None for current position.""" + ... + @staticmethod + def screenshot(self, *args, **kwargs) -> Any: + """screenshot(filename) - Save a screenshot to the specified file""" + ... + @staticmethod + def scroll(self, *args, **kwargs) -> Any: + """scroll(clicks, pos=None) - Scroll wheel at position. Accepts (x,y) tuple, [x,y] list, Vector, or None for current position.""" + ... + @staticmethod + def size(self, *args, **kwargs) -> Any: + """size() - Get screen size as Vector""" + ... + @staticmethod + def tripleClick(self, *args, **kwargs) -> Any: + """tripleClick(pos=None) - Triple click at position. Accepts (x,y) tuple, [x,y] list, Vector, or None for current position.""" + ... + @staticmethod + def typewrite(self, *args, **kwargs) -> Any: + """typewrite(message, interval=0.0) - Type text with optional interval between keystrokes""" + ... +automation: _automation_module + +# --- Module-level functions --------------------------------------------- +def bresenham(start, end, *, include_start=True, include_end=True) -> list[tuple[int, int]]: + """Compute grid cells along a line using Bresenham's algorithm.""" ... - -def loadMusic(filename: str) -> None: - """Load and immediately play background music from a file.""" +def end_benchmark() -> str: + """Stop benchmark capture and write data to JSON file.""" ... - -def setMusicVolume(volume: int) -> None: - """Set the global music volume (0-100).""" - ... - -def setSoundVolume(volume: int) -> None: - """Set the global sound effects volume (0-100).""" - ... - -def playSound(buffer_id: int) -> None: - """Play a sound effect using a previously loaded buffer.""" - ... - -def getMusicVolume() -> int: - """Get the current music volume level (0-100).""" - ... - -def getSoundVolume() -> int: - """Get the current sound effects volume level (0-100).""" - ... - -def sceneUI(scene: Optional[str] = None) -> UICollection: - """Get all UI elements for a scene.""" - ... - -def currentScene() -> str: - """Get the name of the currently active scene.""" - ... - -def setScene(scene: str, transition: Optional[str] = None, duration: float = 0.0) -> None: - """Switch to a different scene with optional transition effect.""" - ... - -def createScene(name: str) -> None: - """Create a new empty scene.""" - ... - -def keypressScene(handler: Callable[[str, bool], None]) -> None: - """Set the keyboard event handler for the current scene.""" - ... - -def setTimer(name: str, handler: Callable[[float], None], interval: int) -> None: - """Create or update a recurring timer.""" - ... - -def delTimer(name: str) -> None: - """Stop and remove a timer.""" - ... - def exit() -> None: """Cleanly shut down the game engine and exit the application.""" ... - -def setScale(multiplier: float) -> None: - """Scale the game window size (deprecated - use Window.resolution).""" - ... - -def find(name: str, scene: Optional[str] = None) -> Optional[UIElement]: +def find(name: str, scene: str = None) -> UIDrawable | None: """Find the first UI element with the specified name.""" ... - -def findAll(pattern: str, scene: Optional[str] = None) -> List[UIElement]: - """Find all UI elements matching a name pattern (supports * wildcards).""" +def find_all(pattern: str, scene: str = None) -> list: + """Find all UI elements matching a name pattern.""" ... - -def getMetrics() -> Dict[str, Union[int, float]]: +def get_metrics() -> dict: """Get current performance metrics.""" ... +def lock() -> _LockContext: + """Get a context manager for thread-safe UI updates from background threads.""" + ... +def log_benchmark(message: str) -> None: + """Add a log message to the current benchmark frame.""" + ... +def set_dev_console(enabled: bool) -> None: + """Enable or disable the developer console overlay.""" + ... +def set_scale(multiplier: float) -> None: + """Deprecated: use Window.resolution instead. Scale the game window size.""" + ... +def start_benchmark() -> None: + """Start capturing benchmark data to a file.""" + ... +def step(dt: float = None) -> float: + """Advance simulation time (headless mode only).""" + ... -# Submodule -class automation: - """Automation API for testing and scripting.""" - - @staticmethod - def screenshot(filename: str) -> bool: - """Save a screenshot to the specified file.""" - ... - - @staticmethod - def position() -> Tuple[int, int]: - """Get current mouse position as (x, y) tuple.""" - ... - - @staticmethod - def size() -> Tuple[int, int]: - """Get screen size as (width, height) tuple.""" - ... - - @staticmethod - def onScreen(x: int, y: int) -> bool: - """Check if coordinates are within screen bounds.""" - ... - - @staticmethod - def moveTo(x: int, y: int, duration: float = 0.0) -> None: - """Move mouse to absolute position.""" - ... - - @staticmethod - def moveRel(xOffset: int, yOffset: int, duration: float = 0.0) -> None: - """Move mouse relative to current position.""" - ... - - @staticmethod - def dragTo(x: int, y: int, duration: float = 0.0, button: str = 'left') -> None: - """Drag mouse to position.""" - ... - - @staticmethod - def dragRel(xOffset: int, yOffset: int, duration: float = 0.0, button: str = 'left') -> None: - """Drag mouse relative to current position.""" - ... - - @staticmethod - def click(x: Optional[int] = None, y: Optional[int] = None, clicks: int = 1, - interval: float = 0.0, button: str = 'left') -> None: - """Click mouse at position.""" - ... - - @staticmethod - def mouseDown(x: Optional[int] = None, y: Optional[int] = None, button: str = 'left') -> None: - """Press mouse button down.""" - ... - - @staticmethod - def mouseUp(x: Optional[int] = None, y: Optional[int] = None, button: str = 'left') -> None: - """Release mouse button.""" - ... - - @staticmethod - def keyDown(key: str) -> None: - """Press key down.""" - ... - - @staticmethod - def keyUp(key: str) -> None: - """Release key.""" - ... - - @staticmethod - def press(key: str) -> None: - """Press and release a key.""" - ... - - @staticmethod - def typewrite(text: str, interval: float = 0.0) -> None: - """Type text with optional interval between characters.""" - ... +# --- Module-level constants --------------------------------------------- +default_font: Font +default_fov: FOV +default_texture: Texture +keyboard: Keyboard +mouse: Mouse +window: Window diff --git a/tools/generate_stubs_v2.py b/tools/generate_stubs_v2.py index 648435b..bf7d0fe 100644 --- a/tools/generate_stubs_v2.py +++ b/tools/generate_stubs_v2.py @@ -1,959 +1,512 @@ #!/usr/bin/env python3 -"""Generate .pyi type stub files for McRogueFace Python API - Version 2. +"""Generate .pyi type stub files for McRogueFace Python API. -This script creates properly formatted type stubs by manually defining -the API based on the documentation we've created. +Uses runtime introspection of the compiled mcrfpy module. Signatures are +extracted from the first line of each docstring when present, with a +fallback to (*args, **kwargs) when a class or callable has no signature +declared. + +Run via McRogueFace itself so the mcrfpy module is importable: + + ./build/mcrogueface --headless --exec tools/generate_stubs_v2.py """ import os -import mcrfpy +import re +import sys +import types +import inspect +from pathlib import Path -def generate_mcrfpy_stub(): - """Generate the main mcrfpy.pyi stub file.""" - return '''"""Type stubs for McRogueFace Python API. +try: + import mcrfpy +except ImportError: + print("Error: this script must be run under McRogueFace (needs mcrfpy)") + sys.exit(1) -Core game engine interface for creating roguelike games with Python. + +# ---------- signature extraction ---------- + +_SIG_NAME_RE = re.compile(r"^\s*(\w+)\s*\(") +_RET_RE = re.compile(r"^\s*->\s*(.+?)\s*$") + + +def _parse_balanced_signature(line): + """Parse 'name(...) -> ret' with proper paren/bracket depth tracking. + + Returns (name, params_text, return_text) or None if not a signature. + Rejects multi-form signatures like 'foo(x) or foo(y)' by requiring the + content after the matched closing paren to be empty or '-> X'. + """ + m = _SIG_NAME_RE.match(line) + if not m: + return None + name = m.group(1) + i = m.end() # position right after the opening '(' + depth = 1 + params_start = i + while i < len(line) and depth: + c = line[i] + if c in "([{": + depth += 1 + elif c in ")]}": + depth -= 1 + if depth == 0: + break + i += 1 + if depth != 0: + return None + params = line[params_start:i] + tail = line[i + 1:].strip() + if not tail: + return name, params, None + rm = _RET_RE.match(tail) + if not rm: + return None # trailing 'or foo(...)' or similar, bail out + return name, params, rm.group(1).strip() + + +def first_line(doc): + if not doc: + return "" + return doc.strip().split("\n", 1)[0].strip() + + +def extract_signature(name, doc): + """Parse 'name(args) -> ret' from the first docstring line. + + Returns (params_str, return_str) or (None, None) if no signature present + or if the signature is multi-form (e.g. 'foo(x) or foo(y)'). + """ + line = first_line(doc) + if not line: + return None, None + parsed = _parse_balanced_signature(line) + if parsed is None: + return None, None + sig_name, params, ret = parsed + if sig_name != name: + return None, None + return params.strip(), ret + + +def first_description_paragraph(doc): + """Return the first non-empty line after a signature line, if any.""" + if not doc: + return "" + lines = doc.strip().split("\n") + start = 1 if lines and _parse_balanced_signature(lines[0].strip()) else 0 + for line in lines[start:]: + s = line.strip() + if s: + return s + return "" + + +# ---------- classification ---------- + +def is_enum_like(cls): + """True if this class looks like an IntEnum: int subclass with uppercase members.""" + if not issubclass(cls, int): + return False + for name, value in cls.__dict__.items(): + if name.isupper() and isinstance(value, cls): + return True + return False + + +def enum_members(cls): + """Yield (name, int_value) pairs for enum-like classes.""" + for name, value in sorted(cls.__dict__.items()): + if name.isupper() and isinstance(value, cls): + yield name, int(value) + + +def is_method_like(attr): + return isinstance(attr, (types.BuiltinFunctionType, + types.BuiltinMethodType, + types.MethodType, + types.FunctionType, + types.MethodDescriptorType, + types.WrapperDescriptorType, + types.ClassMethodDescriptorType)) or callable(attr) and not inspect.isclass(attr) + + +def is_property_descriptor(attr): + return isinstance(attr, (types.GetSetDescriptorType, types.MemberDescriptorType, property)) + + +# ---------- emitters ---------- + +def indent(text, n=4): + pad = " " * n + return "\n".join(pad + ln if ln else ln for ln in text.split("\n")) + + +def _sanitize_params(params): + """Rewrite param forms that are not valid Python syntax. + + - Bare `...` (used in docs to mean "and more kwargs") becomes `**kwargs`. + - `**kwargs` already present is kept. + """ + if not params: + return params + # Replace a trailing ", ..." or lone "..." with **kwargs + if params.strip() == "...": + return "**kwargs" + # If "..." appears as a token, replace with **kwargs + tokens = [t.strip() for t in params.split(",")] + fixed = [] + saw_kwargs = False + for tok in tokens: + if tok == "...": + if not saw_kwargs: + fixed.append("**kwargs") + saw_kwargs = True + else: + if tok.startswith("**"): + saw_kwargs = True + fixed.append(tok) + return ", ".join(fixed) + + +def emit_function(name, doc, is_method=False, is_static=False): + """Emit a def line for a free function or method. Always returns a str + ending with `: ...` plus an optional one-line docstring.""" + params, ret = extract_signature(name, doc) + if params is None: + if is_method: + params = "self, *args, **kwargs" + else: + params = "*args, **kwargs" + else: + params = _sanitize_params(params) + if is_method and not is_static: + params = "self" + (", " + params if params else "") + ret = ret or "Any" + + decorator = ("@staticmethod\n" if is_static and is_method else "") + summary = first_description_paragraph(doc) or first_line(doc) + # If the signature itself is the only line, there's nothing useful to + # restate. Strip a summary that exactly matches the signature. + sig_line = f"{name}({params.replace('self, ', '').replace('self', '')})" + body = f'"""{escape_docstring(summary)}"""' if summary else "..." + # Never add duplicate bodies + if body == "...": + return f'{decorator}def {name}({params}) -> {ret}: ...' + return f'{decorator}def {name}({params}) -> {ret}:\n {body}\n ...' + + +def escape_docstring(text): + # Collapse whitespace and escape triple quotes + t = " ".join(text.split()) + t = t.replace('"""', "'''") + if len(t) > 160: + t = t[:157] + "..." + return t + + +# Recognized type words the property parser will accept. +# Lowercase maps directly to a Python typing name. Anything else must +# look like a class name (CapitalCase). +_TYPE_MAPPING = { + "int": "int", + "uint": "int", + "float": "float", + "bool": "bool", + "str": "str", + "string": "str", + "tuple": "tuple", + "list": "list", + "dict": "dict", + "set": "set", + "frozenset": "frozenset", + "bytes": "bytes", + "any": "Any", + "callable": "Callable", + "none": "None", + "object": "Any", +} + + +def property_type_hint(doc): + """Best-effort type inference from a property docstring. + + Accepts the FIRST parenthesized group whose first word is either a + recognized primitive type or a CapitalCase class name. Skips groups + like "(width, height)" or "(trigger, data)" that are argument lists + rather than type declarations. + """ + if not doc: + return "Any" + for m in re.finditer(r"\(([^()]+)\)", doc): + text = m.group(1).strip() + first = text.split(",")[0].strip() + lower = first.lower() + if lower in _TYPE_MAPPING: + return _TYPE_MAPPING[lower] + # Class-like name (starts with capital): accept + if re.match(r"^[A-Z][A-Za-z0-9_]*$", first): + return first + # Union-like text "int | None" etc. + if "|" in first and all( + tok.strip().lower() in _TYPE_MAPPING or re.match(r"^[A-Z]", tok.strip()) + for tok in first.split("|") + ): + return first + return "Any" + + +def property_is_readonly(doc): + if not doc: + return False + return "read-only" in doc.lower() or "readonly" in doc.lower() + + +def emit_property(name, doc): + t = property_type_hint(doc) + summary = first_description_paragraph(doc) or first_line(doc) + if summary: + return f'{name}: {t} # {escape_docstring(summary)}' + return f'{name}: {t}' + + +# ---------- class emitter ---------- + +# Methods inherited from object that we should never emit +_OBJECT_ATTRS = set(dir(object)) + + +def iter_class_members(cls): + """Yield (name, attr) from cls __dict__ plus inherited members, skipping dunders + except __init__, and skipping plain object inheritance.""" + seen = set() + # Walk mro excluding object + for klass in cls.__mro__: + if klass is object: + break + for name, attr in klass.__dict__.items(): + if name in seen: + continue + if name.startswith("__") and name != "__init__": + continue + seen.add(name) + yield name, attr + + +def emit_class(name, cls): + """Emit a `class Name:` block for a regular class or IntEnum.""" + bases = [] + if is_enum_like(cls): + bases.append("IntEnum") + + header = f'class {name}({", ".join(bases)}):' if bases else f'class {name}:' + doc = cls.__doc__ or "" + summary = first_description_paragraph(doc) or first_line(doc) or f"{name} type." + body_lines = [f'"""{escape_docstring(summary)}"""'] + + if is_enum_like(cls): + for m_name, m_val in enum_members(cls): + body_lines.append(f"{m_name}: int") + # Enums rarely have other members we need to expose in stubs + return header + "\n" + indent("\n".join(body_lines)) + + # Regular class: emit __init__, methods, properties + methods = [] + properties = [] + seen_names = set() + + def add_from(klass): + for attr_name, attr in iter_class_members(klass): + if attr_name == "__init__" or attr_name in seen_names: + continue + if is_property_descriptor(attr): + pdoc = attr.__doc__ if not isinstance(attr, property) else (attr.fget.__doc__ if attr.fget else "") + properties.append((attr_name, pdoc or "")) + seen_names.add(attr_name) + elif callable(attr): + if attr_name in _OBJECT_ATTRS: + continue + mdoc = attr.__doc__ or "" + is_static = isinstance(attr, (types.BuiltinFunctionType, + types.BuiltinMethodType)) and not hasattr(attr, "__self__") + methods.append((attr_name, mdoc, is_static)) + seen_names.add(attr_name) + + add_from(cls) + + # Merge delegated methods/properties from any inner data class + delegate = discover_delegate(name, cls) + if delegate is not None: + add_from(delegate) + + # __init__: take signature from class doc when possible + init_params, _ = extract_signature(name, doc) + if init_params is None: + init_body = "def __init__(self, *args, **kwargs) -> None: ..." + else: + init_body = f"def __init__(self, {init_params}) -> None: ..." if init_params else \ + "def __init__(self) -> None: ..." + body_lines.append(init_body) + + for pname, pdoc in sorted(properties): + body_lines.append(emit_property(pname, pdoc)) + + for mname, mdoc, is_static in sorted(methods): + # Skip dunders we don't have a good signature for + if mname.startswith("__") and mname != "__init__": + continue + body_lines.append(emit_function(mname, mdoc, is_method=True, is_static=is_static)) + + if len(body_lines) == 1: + body_lines.append("...") + return header + "\n" + indent("\n".join(body_lines)) + + +# ---------- delegation discovery ---------- + +# Some C types expose methods on an inner "_Data" helper that dir(cls) does +# not see because __getattr__ forwards at the instance level. We probe known +# method names on a live instance to discover the delegate type and merge its +# surface into the stub. +_DELEGATION_PROBES = ( + "center_camera", # Grid/GridView -> _GridData + "camera_rotation", + "add_collision_label", + "entities", + "compute_fov", + "add_layer", + "apply_threshold", +) + +_DELEGATE_CLASS_FACTORIES = { + # class_name: callable returning a live instance, or None to skip + "Grid": lambda: mcrfpy.Grid(grid_size=(2, 2)), + "GridView": lambda: mcrfpy.GridView(grid=mcrfpy.Grid(grid_size=(2, 2))), +} + + +def discover_delegate(cls_name, cls): + """Return a delegate type if `cls` forwards known method names to it, else None.""" + factory = _DELEGATE_CLASS_FACTORIES.get(cls_name) + if factory is None: + return None + try: + inst = factory() + except Exception: + return None + for probe in _DELEGATION_PROBES: + try: + m = getattr(inst, probe, None) + except Exception: + continue + if callable(m) and hasattr(m, "__self__"): + s_type = type(m.__self__) + if s_type is not cls: + return s_type + return None + + +# ---------- submodule (automation) ---------- + +def emit_submodule(mod_name, mod): + lines = [f"class _{mod_name}_module:"] + body = [f'"""Stub for mcrfpy.{mod_name} submodule."""'] + for name in sorted(dir(mod)): + if name.startswith("_"): + continue + attr = getattr(mod, name) + if callable(attr): + body.append(emit_function(name, attr.__doc__ or "", is_method=True, is_static=True)) + if len(body) == 1: + body.append("...") + lines.append(indent("\n".join(body))) + lines.append(f"{mod_name}: _{mod_name}_module") + return "\n".join(lines) + + +# ---------- main entry ---------- + +HEADER = '''"""Type stubs for McRogueFace Python API. + +Auto-generated by tools/generate_stubs_v2.py via runtime introspection. +Do not edit by hand -- regenerate after API changes: + + make && ./tools/generate_all_docs.sh """ -from typing import Any, List, Dict, Tuple, Optional, Callable, Union, overload - -# Type aliases -UIElement = Union['Frame', 'Caption', 'Sprite', 'Grid', 'Line', 'Circle', 'Arc'] -Transition = Union[str, None] - -# Classes - -class Color: - """SFML Color Object for RGBA colors.""" - - r: int - g: int - b: int - a: int - - @overload - def __init__(self) -> None: ... - @overload - def __init__(self, r: int, g: int, b: int, a: int = 255) -> None: ... - - def from_hex(self, hex_string: str) -> 'Color': - """Create color from hex string (e.g., '#FF0000' or 'FF0000').""" - ... - - def to_hex(self) -> str: - """Convert color to hex string format.""" - ... - - def lerp(self, other: 'Color', t: float) -> 'Color': - """Linear interpolation between two colors.""" - ... - -class Vector: - """SFML Vector Object for 2D coordinates.""" - - x: float - y: float - - @overload - def __init__(self) -> None: ... - @overload - def __init__(self, x: float, y: float) -> None: ... - - def add(self, other: 'Vector') -> 'Vector': ... - def subtract(self, other: 'Vector') -> 'Vector': ... - def multiply(self, scalar: float) -> 'Vector': ... - def divide(self, scalar: float) -> 'Vector': ... - def distance(self, other: 'Vector') -> float: ... - def normalize(self) -> 'Vector': ... - def dot(self, other: 'Vector') -> float: ... - -class Texture: - """SFML Texture Object for images.""" - - def __init__(self, filename: str) -> None: ... - - filename: str - width: int - height: int - sprite_count: int - -class Font: - """SFML Font Object for text rendering.""" - - def __init__(self, filename: str) -> None: ... - - filename: str - family: str - -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, 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, on_click: Optional[Callable] = None, - children: Optional[List[UIElement]] = None) -> None: ... - - w: float - h: float - fill_color: Color - outline_color: Color - outline: float - 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, 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, - on_click: Optional[Callable] = None) -> None: ... - - text: str - font: Font - fill_color: Color - outline_color: Color - outline: float - 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, 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, - on_click: Optional[Callable] = None) -> None: ... - - texture: Texture - sprite_index: int - scale: float - 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(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, 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.""" - ... - - 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: - """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='') - - Game entity that lives within a Grid. - """ - - @overload - def __init__(self) -> None: ... - @overload - def __init__(self, grid_x: float = 0, grid_y: float = 0, texture: Optional[Texture] = None, - sprite_index: int = 0, name: str = '') -> None: ... - - grid_x: float - grid_y: float - texture: Texture - sprite_index: int - grid: Optional[Grid] - - def at(self, grid_x: float, grid_y: float) -> None: - """Move entity to grid position.""" - ... - - def die(self) -> None: - """Remove entity from its grid.""" - ... - - def index(self) -> int: - """Get index in parent grid's entity collection.""" - ... - -class UICollection: - """Collection of UI drawable elements (Frame, Caption, Sprite, Grid, Line, Circle, Arc).""" - - def __len__(self) -> int: ... - def __getitem__(self, index: int) -> UIElement: ... - def __setitem__(self, index: int, value: UIElement) -> None: ... - def __delitem__(self, index: int) -> None: ... - def __contains__(self, item: UIElement) -> bool: ... - def __iter__(self) -> Any: ... - def __add__(self, other: 'UICollection') -> 'UICollection': ... - def __iadd__(self, other: 'UICollection') -> 'UICollection': ... - - def append(self, item: UIElement) -> None: ... - def extend(self, items: List[UIElement]) -> None: ... - def remove(self, item: UIElement) -> None: ... - def index(self, item: UIElement) -> int: ... - def count(self, item: UIElement) -> int: ... - -class EntityCollection: - """Collection of Entity objects.""" - - def __len__(self) -> int: ... - def __getitem__(self, index: int) -> Entity: ... - def __setitem__(self, index: int, value: Entity) -> None: ... - def __delitem__(self, index: int) -> None: ... - def __contains__(self, item: Entity) -> bool: ... - def __iter__(self) -> Any: ... - def __add__(self, other: 'EntityCollection') -> 'EntityCollection': ... - def __iadd__(self, other: 'EntityCollection') -> 'EntityCollection': ... - - def append(self, item: Entity) -> None: ... - def extend(self, items: List[Entity]) -> None: ... - def remove(self, item: Entity) -> None: ... - def index(self, item: Entity) -> int: ... - def count(self, item: Entity) -> int: ... - -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 (override in subclass).""" - ... - - def on_click(self, x: float, y: float, button: int) -> None: - """Handle mouse clicks (override in subclass).""" - ... - - def on_enter(self) -> None: - """Called when entering the scene (override in subclass).""" - ... - - def on_exit(self) -> None: - """Called when leaving the scene (override in subclass).""" - ... - - def on_resize(self, width: int, height: int) -> None: - """Handle window resize events (override in subclass).""" - ... - - def update(self, dt: float) -> None: - """Update scene logic (override in subclass).""" - ... - -class Timer: - """Timer object for scheduled callbacks.""" - - name: str - interval: int - active: bool - - def __init__(self, name: str, callback: Callable[[float], None], interval: int) -> None: ... - - def pause(self) -> None: - """Pause the timer.""" - ... - - def resume(self) -> None: - """Resume the timer.""" - ... - - def cancel(self) -> None: - """Cancel and remove the timer.""" - ... - -class Window: - """Window singleton for managing the game window.""" - - resolution: Tuple[int, int] - fullscreen: bool - vsync: bool - title: str - fps_limit: int - game_resolution: Tuple[int, int] - scaling_mode: str - - @staticmethod - def get() -> 'Window': - """Get the window singleton instance.""" - ... - -class Animation: - """Animation object for animating UI properties.""" - - target: Any - property: str - duration: float - easing: str - loop: bool - on_complete: Optional[Callable] - - def __init__(self, target: Any, property: str, start_value: Any, end_value: Any, - duration: float, easing: str = 'linear', loop: bool = False, - on_complete: Optional[Callable] = None) -> None: ... - - def start(self) -> None: - """Start the animation.""" - ... - - def update(self, dt: float) -> bool: - """Update animation, returns True if still running.""" - ... - - def get_current_value(self) -> Any: - """Get the current interpolated value.""" - ... - -# Module functions - -def createSoundBuffer(filename: str) -> int: - """Load a sound effect from a file and return its buffer ID.""" - ... - -def loadMusic(filename: str) -> None: - """Load and immediately play background music from a file.""" - ... - -def setMusicVolume(volume: int) -> None: - """Set the global music volume (0-100).""" - ... - -def setSoundVolume(volume: int) -> None: - """Set the global sound effects volume (0-100).""" - ... - -def playSound(buffer_id: int) -> None: - """Play a sound effect using a previously loaded buffer.""" - ... - -def getMusicVolume() -> int: - """Get the current music volume level (0-100).""" - ... - -def getSoundVolume() -> int: - """Get the current sound effects volume level (0-100).""" - ... - -def sceneUI(scene: Optional[str] = None) -> UICollection: - """Get all UI elements for a scene.""" - ... - -def currentScene() -> str: - """Get the name of the currently active scene.""" - ... - -def setScene(scene: str, transition: Optional[str] = None, duration: float = 0.0) -> None: - """Switch to a different scene with optional transition effect.""" - ... - -def createScene(name: str) -> None: - """Create a new empty scene.""" - ... - -def keypressScene(handler: Callable[[str, bool], None]) -> None: - """Set the keyboard event handler for the current scene.""" - ... - -def setTimer(name: str, handler: Callable[[float], None], interval: int) -> None: - """Create or update a recurring timer.""" - ... - -def delTimer(name: str) -> None: - """Stop and remove a timer.""" - ... - -def exit() -> None: - """Cleanly shut down the game engine and exit the application.""" - ... - -def setScale(multiplier: float) -> None: - """Scale the game window size (deprecated - use Window.resolution).""" - ... - -def find(name: str, scene: Optional[str] = None) -> Optional[UIElement]: - """Find the first UI element with the specified name.""" - ... - -def findAll(pattern: str, scene: Optional[str] = None) -> List[UIElement]: - """Find all UI elements matching a name pattern (supports * wildcards).""" - ... - -def getMetrics() -> Dict[str, Union[int, float]]: - """Get current performance metrics.""" - ... - -# Submodule -class automation: - """Automation API for testing and scripting.""" - - @staticmethod - def screenshot(filename: str) -> bool: - """Save a screenshot to the specified file.""" - ... - - @staticmethod - def position() -> Tuple[int, int]: - """Get current mouse position as (x, y) tuple.""" - ... - - @staticmethod - def size() -> Tuple[int, int]: - """Get screen size as (width, height) tuple.""" - ... - - @staticmethod - def onScreen(x: int, y: int) -> bool: - """Check if coordinates are within screen bounds.""" - ... - - @staticmethod - def moveTo(x: int, y: int, duration: float = 0.0) -> None: - """Move mouse to absolute position.""" - ... - - @staticmethod - def moveRel(xOffset: int, yOffset: int, duration: float = 0.0) -> None: - """Move mouse relative to current position.""" - ... - - @staticmethod - def dragTo(x: int, y: int, duration: float = 0.0, button: str = 'left') -> None: - """Drag mouse to position.""" - ... - - @staticmethod - def dragRel(xOffset: int, yOffset: int, duration: float = 0.0, button: str = 'left') -> None: - """Drag mouse relative to current position.""" - ... - - @staticmethod - def click(x: Optional[int] = None, y: Optional[int] = None, clicks: int = 1, - interval: float = 0.0, button: str = 'left') -> None: - """Click mouse at position.""" - ... - - @staticmethod - def mouseDown(x: Optional[int] = None, y: Optional[int] = None, button: str = 'left') -> None: - """Press mouse button down.""" - ... - - @staticmethod - def mouseUp(x: Optional[int] = None, y: Optional[int] = None, button: str = 'left') -> None: - """Release mouse button.""" - ... - - @staticmethod - def keyDown(key: str) -> None: - """Press key down.""" - ... - - @staticmethod - def keyUp(key: str) -> None: - """Release key.""" - ... - - @staticmethod - def press(key: str) -> None: - """Press and release a key.""" - ... - - @staticmethod - def typewrite(text: str, interval: float = 0.0) -> None: - """Type text with optional interval between characters.""" - ... +from enum import IntEnum +from typing import Any, Callable, Dict, List, Optional, Tuple, Union, overload ''' -def main(): - """Generate type stubs.""" - print("Generating comprehensive type stubs for McRogueFace...") - - # Create stubs directory - os.makedirs('stubs', exist_ok=True) - - # Write main stub file - with open('stubs/mcrfpy.pyi', 'w') as f: - f.write(generate_mcrfpy_stub()) - - print("Generated stubs/mcrfpy.pyi") - - # Create py.typed marker - with open('stubs/py.typed', 'w') as f: - f.write('') - - print("Created py.typed marker") - - print("\nType stubs generated successfully!") - print("\nTo use in your IDE:") - print("1. Add the 'stubs' directory to your project") - print("2. Most IDEs will automatically detect the .pyi files") - print("3. For VS Code: add to python.analysis.extraPaths in settings.json") - print("4. For PyCharm: mark 'stubs' directory as Sources Root") -if __name__ == '__main__': - main() \ No newline at end of file +def classify_module(): + classes = {} + functions = {} + constants = {} + submodules = {} + for name in sorted(dir(mcrfpy)): + if name.startswith("_"): + continue + attr = getattr(mcrfpy, name) + if isinstance(attr, types.ModuleType): + submodules[name] = attr + elif inspect.isclass(attr): + classes[name] = attr + elif callable(attr): + functions[name] = attr + else: + constants[name] = attr + return classes, functions, constants, submodules + + +def main(): + classes, functions, constants, submodules = classify_module() + + out_lines = [HEADER] + + # Emit classes (enums first so forward-reference order is sensible) + enum_names = [n for n, c in classes.items() if is_enum_like(c)] + other_names = [n for n in classes if n not in enum_names] + + out_lines.append("# --- Enums --------------------------------------------------------------") + for n in sorted(enum_names): + out_lines.append(emit_class(n, classes[n])) + out_lines.append("") + + out_lines.append("# --- Classes ------------------------------------------------------------") + for n in sorted(other_names): + out_lines.append(emit_class(n, classes[n])) + out_lines.append("") + + out_lines.append("# --- Submodules ---------------------------------------------------------") + for n in sorted(submodules): + out_lines.append(emit_submodule(n, submodules[n])) + out_lines.append("") + + out_lines.append("# --- Module-level functions ---------------------------------------------") + for n in sorted(functions): + fn = functions[n] + out_lines.append(emit_function(n, fn.__doc__ or "")) + + out_lines.append("") + out_lines.append("# --- Module-level constants ---------------------------------------------") + for n in sorted(constants): + v = constants[n] + t = type(v).__name__ + out_lines.append(f"{n}: {t}") + + out_text = "\n".join(out_lines).rstrip() + "\n" + + stubs_dir = Path("stubs") + stubs_dir.mkdir(exist_ok=True) + (stubs_dir / "mcrfpy.pyi").write_text(out_text) + (stubs_dir / "py.typed").write_text("") + + print(f"Wrote stubs/mcrfpy.pyi ({len(out_text)} bytes, " + f"{len(classes)} classes, {len(functions)} functions, " + f"{len(constants)} constants, {len(submodules)} submodules)") + + +if __name__ == "__main__": + main()