-
resizeresize(width: float, height: float) -> None
+
resize(width: float, height: float) -> None
Resize the element to new dimensions.
-
Note:
width: New width in pixels
@@ -934,17 +926,30 @@ Note:
Methods:
-
activate(...)
-
Make this the active scene
+
activate() -> None
+
Make this the active scene.
+
+Note:
+
Returns: None Deactivates the current scene and activates this one. Scene transitions and lifecycle callbacks are triggered.
-
get_ui(...)
-
Get the UI element collection for this scene
+
get_ui() -> UICollection
+
Get the UI element collection for this scene.
+
+Note:
+
Returns: UICollection: Collection of UI elements (Frames, Captions, Sprites, Grids) in this scene Use to add, remove, or iterate over UI elements. Changes are reflected immediately.
-
register_keyboardRegister a keyboard handler function (alternative to overriding on_keypress)
+
register_keyboard(callback: callable) -> None
+
Register a keyboard event handler function.
+
+Note:
+
+
callback: Function that receives (key: str, pressed: bool) when keyboard events occur
+
+
Returns: None Alternative to overriding on_keypress() method. Handler is called for both key press and release events.
@@ -988,20 +993,17 @@ Attributes:
Methods:
-
get_boundsget_bounds() -> tuple
+
get_bounds() -> tuple
Get the bounding rectangle of this drawable element.
-
-
Note:
-
Returns: tuple: (x, y, width, height) representing the element's bounds The bounds are in screen coordinates and account for current position and size.
+
Returns: tuple: (x, y, width, height) representing the element's bounds The bounds are in screen coordinates and account for current position and size.
-
movemove(dx: float, dy: float) -> None
+
move(dx: float, dy: float) -> None
Move the element by a relative offset.
-
Note:
dx: Horizontal offset in pixels
@@ -1010,10 +1012,9 @@ Note:
-
resizeresize(width: float, height: float) -> None
+
resize(width: float, height: float) -> None
Resize the element to new dimensions.
-
Note:
width: New width in pixels
@@ -1067,43 +1068,35 @@ Example:
Methods:
-
cancelcancel() -> None
+
cancel() -> None
Cancel the timer and remove it from the timer system.
-
-
Note:
-
Returns: None The timer will no longer fire and cannot be restarted. The callback will not be called again.
+
Returns: None The timer will no longer fire and cannot be restarted. The callback will not be called again.
-
pausepause() -> None
+
pause() -> None
Pause the timer, preserving the time remaining until next trigger.
-
-
Note:
-
Returns: None The timer can be resumed later with resume(). Time spent paused does not count toward the interval.
+
Returns: None The timer can be resumed later with resume(). Time spent paused does not count toward the interval.
-
restartrestart() -> None
+
restart() -> None
Restart the timer from the beginning.
-
-
Note:
-
Returns: None Resets the timer to fire after a full interval from now, regardless of remaining time.
+
Returns: None Resets the timer to fire after a full interval from now, regardless of remaining time.
-
resumeresume() -> None
+
resume() -> None
Resume a paused timer from where it left off.
-
-
Note:
-
Returns: None Has no effect if the timer is not paused. Timer will fire after the remaining time elapses.
+
Returns: None Has no effect if the timer is not paused. Timer will fire after the remaining time elapses.
@@ -1151,59 +1144,55 @@ Note:
Methods:
-
angleangle() -> float
+
angle() -> float
Get the angle of this vector in radians.
-
Returns: float: Angle in radians from positive x-axis
+
Returns: float: Angle in radians from positive x-axis
-
copycopy() -> Vector
+
copy() -> Vector
Create a copy of this vector.
-
Returns: Vector: New Vector object with same x and y values
+
Returns: Vector: New Vector object with same x and y values
-
distance_todistance_to(other: Vector) -> float
+
distance_to(other: Vector) -> float
Calculate the distance to another vector.
-
Returns: float: Distance between the two vectors
+
Returns: float: Distance between the two vectors
-
dotdot(other: Vector) -> float
+
dot(other: Vector) -> float
Calculate the dot product with another vector.
-
Returns: float: Dot product of the two vectors
+
Returns: float: Dot product of the two vectors
-
magnitudemagnitude() -> float
+
magnitude() -> float
Calculate the length/magnitude of this vector.
-
Returns: float: The magnitude of the vector
+
Returns: float: The magnitude of the vector
-
magnitude_squaredmagnitude_squared() -> float
+
magnitude_squared() -> float
Calculate the squared magnitude of this vector.
-
-
Note:
-
Returns: float: The squared magnitude (faster than magnitude()) Use this for comparisons to avoid expensive square root calculation.
+
Returns: float: The squared magnitude (faster than magnitude()) Use this for comparisons to avoid expensive square root calculation.
-
normalizenormalize() -> Vector
+
normalize() -> Vector
Return a unit vector in the same direction.
-
-
Note:
-
Returns: Vector: New normalized vector with magnitude 1.0 For zero vectors (magnitude 0.0), returns a zero vector rather than raising an exception
+
Returns: Vector: New normalized vector with magnitude 1.0 For zero vectors (magnitude 0.0), returns a zero vector rather than raising an exception
@@ -1213,18 +1202,30 @@ Note:
Methods:
-
center(...)
-
Center the window on the screen
+
center() -> None
+
Center the window on the screen.
+
+Note:
+
Returns: None Only works in windowed mode. Has no effect when fullscreen or in headless mode.
-
get(...)
-
Get the Window singleton instance
+
get() -> Window
+
Get the Window singleton instance.
+
+Note:
+
Returns: Window: The global window object This is a class method. Call as Window.get(). There is only one window instance per application.
-
screenshot(...)
-
Take a screenshot. Pass filename to save to file, or get raw bytes if no filename.
+
screenshot(filename: str = None) -> bytes | None
+
Take a screenshot of the current window contents.
+
+Note:
+
+
filename: Optional path to save screenshot. If omitted, returns raw RGBA bytes.
+
+
Returns: bytes | None: Raw RGBA pixel data if no filename given, otherwise None after saving Screenshot is taken at the actual window resolution. Use after render loop update for current frame.
diff --git a/docs/mcrfpy.3 b/docs/mcrfpy.3
new file mode 100644
index 0000000..5c46cba
--- /dev/null
+++ b/docs/mcrfpy.3
@@ -0,0 +1,1070 @@
+.\" Automatically generated by Pandoc 2.17.1.1
+.\"
+.\" Define V font for inline verbatim, using C font in formats
+.\" that render this, and otherwise B font.
+.ie "\f[CB]x\f[]"x" \{\
+. ftr V B
+. ftr VI BI
+. ftr VB B
+. ftr VBI BI
+.\}
+.el \{\
+. ftr V CR
+. ftr VI CI
+. ftr VB CB
+. ftr VBI CBI
+.\}
+.TH "MCRFPY" "3" "2025-10-30" "McRogueFace dev" ""
+.hy
+.SH McRogueFace API Reference
+.PP
+\f[I]Generated on 2025-10-30 20:49:34\f[R]
+.PP
+\f[I]This documentation was dynamically generated from the compiled
+module.\f[R]
+.SS Table of Contents
+.IP \[bu] 2
+Functions
+.IP \[bu] 2
+Classes
+.RS 2
+.IP \[bu] 2
+Animation
+.IP \[bu] 2
+Caption
+.IP \[bu] 2
+Color
+.IP \[bu] 2
+Drawable
+.IP \[bu] 2
+Entity
+.IP \[bu] 2
+EntityCollection
+.IP \[bu] 2
+Font
+.IP \[bu] 2
+Frame
+.IP \[bu] 2
+Grid
+.IP \[bu] 2
+GridPoint
+.IP \[bu] 2
+GridPointState
+.IP \[bu] 2
+Scene
+.IP \[bu] 2
+Sprite
+.IP \[bu] 2
+Texture
+.IP \[bu] 2
+Timer
+.IP \[bu] 2
+UICollection
+.IP \[bu] 2
+UICollectionIter
+.IP \[bu] 2
+UIEntityCollectionIter
+.IP \[bu] 2
+Vector
+.IP \[bu] 2
+Window
+.RE
+.IP \[bu] 2
+Constants
+.SS Functions
+.SS \f[V]createScene(name: str) -> None\f[R]
+.PP
+Create a new empty scene.
+.PP
+Note:
+.PP
+\f[B]Arguments:\f[R] - \f[V]name\f[R]: Unique name for the new scene
+.PP
+\f[B]Returns:\f[R] None
+.PP
+\f[B]Raises:\f[R] ValueError: If a scene with this name already exists
+The scene is created but not made active.
+Use setScene() to switch to it.
+.SS \f[V]createSoundBuffer(filename: str) -> int\f[R]
+.PP
+Load a sound effect from a file and return its buffer ID.
+.PP
+\f[B]Arguments:\f[R] - \f[V]filename\f[R]: Path to the sound file (WAV,
+OGG, FLAC)
+.PP
+\f[B]Returns:\f[R] int: Buffer ID for use with playSound()
+.PP
+\f[B]Raises:\f[R] RuntimeError: If the file cannot be loaded
+.SS \f[V]currentScene() -> str\f[R]
+.PP
+Get the name of the currently active scene.
+.PP
+\f[B]Returns:\f[R] str: Name of the current scene
+.SS \f[V]delTimer(name: str) -> None\f[R]
+.PP
+Stop and remove a timer.
+.PP
+Note:
+.PP
+\f[B]Arguments:\f[R] - \f[V]name\f[R]: Timer identifier to remove
+.PP
+\f[B]Returns:\f[R] None No error is raised if the timer doesn\[cq]t
+exist.
+.SS \f[V]exit() -> None\f[R]
+.PP
+Cleanly shut down the game engine and exit the application.
+.PP
+Note:
+.PP
+\f[B]Returns:\f[R] None This immediately closes the window and
+terminates the program.
+.SS \f[V]find(name: str, scene: str = None) -> UIDrawable | None\f[R]
+.PP
+Find the first UI element with the specified name.
+.PP
+Note:
+.PP
+\f[B]Arguments:\f[R] - \f[V]name\f[R]: Exact name to search for -
+\f[V]scene\f[R]: Scene to search in (default: current scene)
+.PP
+\f[B]Returns:\f[R] Frame, Caption, Sprite, Grid, or Entity if found;
+None otherwise Searches scene UI elements and entities within grids.
+.SS \f[V]findAll(pattern: str, scene: str = None) -> list\f[R]
+.PP
+Find all UI elements matching a name pattern.
+.PP
+Note:
+.PP
+\f[B]Arguments:\f[R] - \f[V]pattern\f[R]: Name pattern with optional
+wildcards (* matches any characters) - \f[V]scene\f[R]: Scene to search
+in (default: current scene)
+.PP
+\f[B]Returns:\f[R] list: All matching UI elements and entities
+.SS \f[V]getMetrics() -> dict\f[R]
+.PP
+Get current performance metrics.
+.PP
+\f[B]Returns:\f[R] dict: Performance data with keys: frame_time (last
+frame duration in seconds), avg_frame_time (average frame time), fps
+(frames per second), draw_calls (number of draw calls), ui_elements
+(total UI element count), visible_elements (visible element count),
+current_frame (frame counter), runtime (total runtime in seconds)
+.SS \f[V]getMusicVolume() -> int\f[R]
+.PP
+Get the current music volume level.
+.PP
+\f[B]Returns:\f[R] int: Current volume (0-100)
+.SS \f[V]getSoundVolume() -> int\f[R]
+.PP
+Get the current sound effects volume level.
+.PP
+\f[B]Returns:\f[R] int: Current volume (0-100)
+.SS \f[V]keypressScene(handler: callable) -> None\f[R]
+.PP
+Set the keyboard event handler for the current scene.
+.PP
+Note:
+.PP
+\f[B]Arguments:\f[R] - \f[V]handler\f[R]: Callable that receives
+(key_name: str, is_pressed: bool)
+.PP
+\f[B]Returns:\f[R] None
+.SS \f[V]loadMusic(filename: str) -> None\f[R]
+.PP
+Load and immediately play background music from a file.
+.PP
+Note:
+.PP
+\f[B]Arguments:\f[R] - \f[V]filename\f[R]: Path to the music file (WAV,
+OGG, FLAC)
+.PP
+\f[B]Returns:\f[R] None Only one music track can play at a time.
+Loading new music stops the current track.
+.SS \f[V]playSound(buffer_id: int) -> None\f[R]
+.PP
+Play a sound effect using a previously loaded buffer.
+.PP
+\f[B]Arguments:\f[R] - \f[V]buffer_id\f[R]: Sound buffer ID returned by
+createSoundBuffer()
+.PP
+\f[B]Returns:\f[R] None
+.PP
+\f[B]Raises:\f[R] RuntimeError: If the buffer ID is invalid
+.SS \f[V]sceneUI(scene: str = None) -> list\f[R]
+.PP
+Get all UI elements for a scene.
+.PP
+\f[B]Arguments:\f[R] - \f[V]scene\f[R]: Scene name.
+If None, uses current scene
+.PP
+\f[B]Returns:\f[R] list: All UI elements (Frame, Caption, Sprite, Grid)
+in the scene
+.PP
+\f[B]Raises:\f[R] KeyError: If the specified scene doesn\[cq]t exist
+.SS \f[V]setMusicVolume(volume: int) -> None\f[R]
+.PP
+Set the global music volume.
+.PP
+\f[B]Arguments:\f[R] - \f[V]volume\f[R]: Volume level from 0 (silent) to
+100 (full volume)
+.PP
+\f[B]Returns:\f[R] None
+.SS \f[V]setScale(multiplier: float) -> None\f[R]
+.PP
+Scale the game window size.
+.PP
+Note:
+.PP
+\f[B]Arguments:\f[R] - \f[V]multiplier\f[R]: Scale factor (e.g., 2.0 for
+double size)
+.PP
+\f[B]Returns:\f[R] None The internal resolution remains 1024x768, but
+the window is scaled.
+This is deprecated - use Window.resolution instead.
+.SS \f[V]setScene(scene: str, transition: str = None, duration: float = 0.0) -> None\f[R]
+.PP
+Switch to a different scene with optional transition effect.
+.PP
+\f[B]Arguments:\f[R] - \f[V]scene\f[R]: Name of the scene to switch to -
+\f[V]transition\f[R]: Transition type (`fade', `slide_left',
+`slide_right', `slide_up', `slide_down') - \f[V]duration\f[R]:
+Transition duration in seconds (default: 0.0 for instant)
+.PP
+\f[B]Returns:\f[R] None
+.PP
+\f[B]Raises:\f[R] KeyError: If the scene doesn\[cq]t exist ValueError:
+If the transition type is invalid
+.SS \f[V]setSoundVolume(volume: int) -> None\f[R]
+.PP
+Set the global sound effects volume.
+.PP
+\f[B]Arguments:\f[R] - \f[V]volume\f[R]: Volume level from 0 (silent) to
+100 (full volume)
+.PP
+\f[B]Returns:\f[R] None
+.SS \f[V]setTimer(name: str, handler: callable, interval: int) -> None\f[R]
+.PP
+Create or update a recurring timer.
+.PP
+Note:
+.PP
+\f[B]Arguments:\f[R] - \f[V]name\f[R]: Unique identifier for the timer -
+\f[V]handler\f[R]: Function called with (runtime: float) parameter -
+\f[V]interval\f[R]: Time between calls in milliseconds
+.PP
+\f[B]Returns:\f[R] None If a timer with this name exists, it will be
+replaced.
+The handler receives the total runtime in seconds as its argument.
+.SS Classes
+.SS Animation
+.PP
+Animation object for animating UI properties
+.PP
+\f[B]Methods:\f[R]
+.SS \f[V]complete() -> None\f[R]
+.PP
+Complete the animation immediately by jumping to the final value.
+.PP
+Note:
+.PP
+\f[B]Returns:\f[R] None Sets elapsed = duration and applies target value
+immediately.
+Completion callback will be called if set.
+.SS \f[V]get_current_value() -> Any\f[R]
+.PP
+Get the current interpolated value of the animation.
+.PP
+Note:
+.PP
+\f[B]Returns:\f[R] Any: Current value (type depends on property: float,
+int, Color tuple, Vector tuple, or str) Return type matches the target
+property type.
+For sprite_index returns int, for pos returns (x, y), for fill_color
+returns (r, g, b, a).
+.SS \f[V]hasValidTarget() -> bool\f[R]
+.PP
+Check if the animation still has a valid target.
+.PP
+Note:
+.PP
+\f[B]Returns:\f[R] bool: True if the target still exists, False if it
+was destroyed Animations automatically clean up when targets are
+destroyed.
+Use this to check if manual cleanup is needed.
+.SS \f[V]start(target: UIDrawable) -> None\f[R]
+.PP
+Start the animation on a target UI element.
+.PP
+Note:
+.PP
+\f[B]Arguments:\f[R] - \f[V]target\f[R]: The UI element to animate
+(Frame, Caption, Sprite, Grid, or Entity)
+.PP
+\f[B]Returns:\f[R] None The animation will automatically stop if the
+target is destroyed.
+Call AnimationManager.update(delta_time) each frame to progress
+animations.
+.SS \f[V]update(delta_time: float) -> bool\f[R]
+.PP
+Update the animation by the given time delta.
+.PP
+Note:
+.PP
+\f[B]Arguments:\f[R] - \f[V]delta_time\f[R]: Time elapsed since last
+update in seconds
+.PP
+\f[B]Returns:\f[R] bool: True if animation is still running, False if
+complete Typically called by AnimationManager automatically.
+Manual calls only needed for custom animation control.
+.SS Caption
+.PP
+\f[I]Inherits from: Drawable\f[R]
+.PP
+Caption(pos=None, font=None, text=\[cq]\[cq], **kwargs)
+.PP
+A text display UI element with customizable font and styling.
+.PP
+Args: pos (tuple, optional): Position as (x, y) tuple.
+Default: (0, 0) font (Font, optional): Font object for text rendering.
+Default: engine default font text (str, optional): The text content to
+display.
+Default: \[cq]\[cq]
+.PP
+Keyword Args: fill_color (Color): Text fill color.
+Default: (255, 255, 255, 255) outline_color (Color): Text outline color.
+Default: (0, 0, 0, 255) outline (float): Text outline thickness.
+Default: 0 font_size (float): Font size in points.
+Default: 16 click (callable): Click event handler.
+Default: None visible (bool): Visibility state.
+Default: True opacity (float): Opacity (0.0-1.0).
+Default: 1.0 z_index (int): Rendering order.
+Default: 0 name (str): Element name for finding.
+Default: None x (float): X position override.
+Default: 0 y (float): Y position override.
+Default: 0
+.PP
+Attributes: text (str): The displayed text content x, y (float):
+Position in pixels pos (Vector): Position as a Vector object font
+(Font): Font used for rendering font_size (float): Font size in points
+fill_color, outline_color (Color): Text appearance outline (float):
+Outline thickness click (callable): Click event handler visible (bool):
+Visibility state opacity (float): Opacity value z_index (int): Rendering
+order name (str): Element name w, h (float): Read-only computed size
+based on text and font
+.PP
+\f[B]Methods:\f[R]
+.SS \f[V]get_bounds() -> tuple\f[R]
+.PP
+Get the bounding rectangle of this drawable element.
+.PP
+Note:
+.PP
+\f[B]Returns:\f[R] tuple: (x, y, width, height) representing the
+element\[cq]s bounds The bounds are in screen coordinates and account
+for current position and size.
+.SS \f[V]move(dx: float, dy: float) -> None\f[R]
+.PP
+Move the element by a relative offset.
+.PP
+Note:
+.PP
+\f[B]Arguments:\f[R] - \f[V]dx\f[R]: Horizontal offset in pixels -
+\f[V]dy\f[R]: Vertical offset in pixels
+.SS \f[V]resize(width: float, height: float) -> None\f[R]
+.PP
+Resize the element to new dimensions.
+.PP
+Note:
+.PP
+\f[B]Arguments:\f[R] - \f[V]width\f[R]: New width in pixels -
+\f[V]height\f[R]: New height in pixels
+.SS Color
+.PP
+SFML Color Object
+.PP
+\f[B]Methods:\f[R]
+.SS \f[V]from_hex(hex_string: str) -> Color\f[R]
+.PP
+Create a Color from a hexadecimal string.
+.PP
+Note:
+.PP
+\f[B]Arguments:\f[R] - \f[V]hex_string\f[R]: Hex color string (e.g.,
+`#FF0000', `FF0000', `#AABBCCDD' for RGBA)
+.PP
+\f[B]Returns:\f[R] Color: New Color object with values from hex string
+.PP
+\f[B]Raises:\f[R] ValueError: If hex string is not 6 or 8 characters
+(RGB or RGBA) This is a class method.
+Call as Color.from_hex(`#FF0000')
+.SS \f[V]lerp(other: Color, t: float) -> Color\f[R]
+.PP
+Linearly interpolate between this color and another.
+.PP
+Note:
+.PP
+\f[B]Arguments:\f[R] - \f[V]other\f[R]: The target Color to interpolate
+towards - \f[V]t\f[R]: Interpolation factor (0.0 = this color, 1.0 =
+other color).
+Automatically clamped to [0.0, 1.0]
+.PP
+\f[B]Returns:\f[R] Color: New Color representing the interpolated value
+All components (r, g, b, a) are interpolated independently
+.SS \f[V]to_hex() -> str\f[R]
+.PP
+Convert this Color to a hexadecimal string.
+.PP
+Note:
+.PP
+\f[B]Returns:\f[R] str: Hex string in format `#RRGGBB' or `#RRGGBBAA'
+(if alpha < 255) Alpha component is only included if not fully opaque (<
+255)
+.SS Drawable
+.PP
+Base class for all drawable UI elements
+.PP
+\f[B]Methods:\f[R]
+.SS \f[V]get_bounds() -> tuple\f[R]
+.PP
+Get the bounding rectangle of this drawable element.
+.PP
+Note:
+.PP
+\f[B]Returns:\f[R] tuple: (x, y, width, height) representing the
+element\[cq]s bounds The bounds are in screen coordinates and account
+for current position and size.
+.SS \f[V]move(dx: float, dy: float) -> None\f[R]
+.PP
+Move the element by a relative offset.
+.PP
+Note:
+.PP
+\f[B]Arguments:\f[R] - \f[V]dx\f[R]: Horizontal offset in pixels -
+\f[V]dy\f[R]: Vertical offset in pixels
+.SS \f[V]resize(width: float, height: float) -> None\f[R]
+.PP
+Resize the element to new dimensions.
+.PP
+Note:
+.PP
+\f[B]Arguments:\f[R] - \f[V]width\f[R]: New width in pixels -
+\f[V]height\f[R]: New height in pixels
+.SS Entity
+.PP
+Entity(grid_pos=None, texture=None, sprite_index=0, **kwargs)
+.PP
+A game entity that exists on a grid with sprite rendering.
+.PP
+Args: grid_pos (tuple, optional): Grid position as (x, y) tuple.
+Default: (0, 0) texture (Texture, optional): Texture object for sprite.
+Default: default texture sprite_index (int, optional): Index into
+texture atlas.
+Default: 0
+.PP
+Keyword Args: grid (Grid): Grid to attach entity to.
+Default: None visible (bool): Visibility state.
+Default: True opacity (float): Opacity (0.0-1.0).
+Default: 1.0 name (str): Element name for finding.
+Default: None x (float): X grid position override.
+Default: 0 y (float): Y grid position override.
+Default: 0
+.PP
+Attributes: pos (tuple): Grid position as (x, y) tuple x, y (float):
+Grid position coordinates draw_pos (tuple): Pixel position for rendering
+gridstate (GridPointState): Visibility state for grid points
+sprite_index (int): Current sprite index visible (bool): Visibility
+state opacity (float): Opacity value name (str): Element name
+.PP
+\f[B]Methods:\f[R]
+.SS \f[V]at(...)\f[R]
+.SS \f[V]die(...)\f[R]
+.PP
+Remove this entity from its grid
+.SS \f[V]get_bounds() -> tuple\f[R]
+.PP
+Get the bounding rectangle of this drawable element.
+.PP
+Note:
+.PP
+\f[B]Returns:\f[R] tuple: (x, y, width, height) representing the
+element\[cq]s bounds The bounds are in screen coordinates and account
+for current position and size.
+.SS \f[V]index(...)\f[R]
+.PP
+Return the index of this entity in its grid\[cq]s entity collection
+.SS \f[V]move(dx: float, dy: float) -> None\f[R]
+.PP
+Move the element by a relative offset.
+.PP
+Note:
+.PP
+\f[B]Arguments:\f[R] - \f[V]dx\f[R]: Horizontal offset in pixels -
+\f[V]dy\f[R]: Vertical offset in pixels
+.SS \f[V]path_to(x: int, y: int) -> bool\f[R]
+.PP
+Find and follow path to target position using A* pathfinding.
+.PP
+\f[B]Arguments:\f[R] - \f[V]x\f[R]: Target X coordinate - \f[V]y\f[R]:
+Target Y coordinate
+.PP
+\f[B]Returns:\f[R] True if a path was found and the entity started
+moving, False otherwise The entity will automatically move along the
+path over multiple frames.
+Call this again to change the target or repath.
+.SS \f[V]resize(width: float, height: float) -> None\f[R]
+.PP
+Resize the element to new dimensions.
+.PP
+Note:
+.PP
+\f[B]Arguments:\f[R] - \f[V]width\f[R]: New width in pixels -
+\f[V]height\f[R]: New height in pixels
+.SS \f[V]update_visibility() -> None\f[R]
+.PP
+Update entity\[cq]s visibility state based on current FOV.
+Recomputes which cells are visible from the entity\[cq]s position and
+updates the entity\[cq]s gridstate to track explored areas.
+This is called automatically when the entity moves if it has a grid with
+perspective set.
+.SS EntityCollection
+.PP
+Iterable, indexable collection of Entities
+.PP
+\f[B]Methods:\f[R]
+.SS \f[V]append(...)\f[R]
+.SS \f[V]count(...)\f[R]
+.SS \f[V]extend(...)\f[R]
+.SS \f[V]index(...)\f[R]
+.SS \f[V]remove(...)\f[R]
+.SS Font
+.PP
+SFML Font Object
+.PP
+\f[B]Methods:\f[R]
+.SS Frame
+.PP
+\f[I]Inherits from: Drawable\f[R]
+.PP
+Frame(pos=None, size=None, **kwargs)
+.PP
+A rectangular frame UI element that can contain other drawable elements.
+.PP
+Args: pos (tuple, optional): Position as (x, y) tuple.
+Default: (0, 0) size (tuple, optional): Size as (width, height) tuple.
+Default: (0, 0)
+.PP
+Keyword Args: fill_color (Color): Background fill color.
+Default: (0, 0, 0, 128) outline_color (Color): Border outline color.
+Default: (255, 255, 255, 255) outline (float): Border outline thickness.
+Default: 0 click (callable): Click event handler.
+Default: None children (list): Initial list of child drawable elements.
+Default: None visible (bool): Visibility state.
+Default: True opacity (float): Opacity (0.0-1.0).
+Default: 1.0 z_index (int): Rendering order.
+Default: 0 name (str): Element name for finding.
+Default: None x (float): X position override.
+Default: 0 y (float): Y position override.
+Default: 0 w (float): Width override.
+Default: 0 h (float): Height override.
+Default: 0 clip_children (bool): Whether to clip children to frame
+bounds.
+Default: False
+.PP
+Attributes: x, y (float): Position in pixels w, h (float): Size in
+pixels pos (Vector): Position as a Vector object fill_color,
+outline_color (Color): Visual appearance outline (float): Border
+thickness click (callable): Click event handler children (list):
+Collection of child drawable elements visible (bool): Visibility state
+opacity (float): Opacity value z_index (int): Rendering order name
+(str): Element name clip_children (bool): Whether to clip children to
+frame bounds
+.PP
+\f[B]Methods:\f[R]
+.SS \f[V]get_bounds() -> tuple\f[R]
+.PP
+Get the bounding rectangle of this drawable element.
+.PP
+Note:
+.PP
+\f[B]Returns:\f[R] tuple: (x, y, width, height) representing the
+element\[cq]s bounds The bounds are in screen coordinates and account
+for current position and size.
+.SS \f[V]move(dx: float, dy: float) -> None\f[R]
+.PP
+Move the element by a relative offset.
+.PP
+Note:
+.PP
+\f[B]Arguments:\f[R] - \f[V]dx\f[R]: Horizontal offset in pixels -
+\f[V]dy\f[R]: Vertical offset in pixels
+.SS \f[V]resize(width: float, height: float) -> None\f[R]
+.PP
+Resize the element to new dimensions.
+.PP
+Note:
+.PP
+\f[B]Arguments:\f[R] - \f[V]width\f[R]: New width in pixels -
+\f[V]height\f[R]: New height in pixels
+.SS Grid
+.PP
+\f[I]Inherits from: Drawable\f[R]
+.PP
+Grid(pos=None, size=None, grid_size=None, texture=None, **kwargs)
+.PP
+A grid-based UI element for tile-based rendering and entity management.
+.PP
+Args: pos (tuple, optional): Position as (x, y) tuple.
+Default: (0, 0) size (tuple, optional): Size as (width, height) tuple.
+Default: auto-calculated from grid_size grid_size (tuple, optional):
+Grid dimensions as (grid_x, grid_y) tuple.
+Default: (2, 2) texture (Texture, optional): Texture containing tile
+sprites.
+Default: default texture
+.PP
+Keyword Args: fill_color (Color): Background fill color.
+Default: None click (callable): Click event handler.
+Default: None center_x (float): X coordinate of center point.
+Default: 0 center_y (float): Y coordinate of center point.
+Default: 0 zoom (float): Zoom level for rendering.
+Default: 1.0 perspective (int): Entity perspective index (-1 for
+omniscient).
+Default: -1 visible (bool): Visibility state.
+Default: True opacity (float): Opacity (0.0-1.0).
+Default: 1.0 z_index (int): Rendering order.
+Default: 0 name (str): Element name for finding.
+Default: None x (float): X position override.
+Default: 0 y (float): Y position override.
+Default: 0 w (float): Width override.
+Default: auto-calculated h (float): Height override.
+Default: auto-calculated grid_x (int): Grid width override.
+Default: 2 grid_y (int): Grid height override.
+Default: 2
+.PP
+Attributes: x, y (float): Position in pixels w, h (float): Size in
+pixels pos (Vector): Position as a Vector object size (tuple): Size as
+(width, height) tuple center (tuple): Center point as (x, y) tuple
+center_x, center_y (float): Center point coordinates zoom (float): Zoom
+level for rendering grid_size (tuple): Grid dimensions (width, height)
+in tiles grid_x, grid_y (int): Grid dimensions texture (Texture): Tile
+texture atlas fill_color (Color): Background color entities
+(EntityCollection): Collection of entities in the grid perspective
+(int): Entity perspective index click (callable): Click event handler
+visible (bool): Visibility state opacity (float): Opacity value z_index
+(int): Rendering order name (str): Element name
+.PP
+\f[B]Methods:\f[R]
+.SS \f[V]at(...)\f[R]
+.SS \f[V]compute_astar_path(x1: int, y1: int, x2: int, y2: int, diagonal_cost: float = 1.41) -> List[Tuple[int, int]]\f[R]
+.PP
+Compute A* path between two points.
+.PP
+\f[B]Arguments:\f[R] - \f[V]x1\f[R]: Starting X coordinate -
+\f[V]y1\f[R]: Starting Y coordinate - \f[V]x2\f[R]: Target X coordinate
+- \f[V]y2\f[R]: Target Y coordinate - \f[V]diagonal_cost\f[R]: Cost of
+diagonal movement (default: 1.41)
+.PP
+\f[B]Returns:\f[R] List of (x, y) tuples representing the path, empty
+list if no path exists Alternative A* implementation.
+Prefer find_path() for consistency.
+.SS \f[V]compute_dijkstra(root_x: int, root_y: int, diagonal_cost: float = 1.41) -> None\f[R]
+.PP
+Compute Dijkstra map from root position.
+.PP
+\f[B]Arguments:\f[R] - \f[V]root_x\f[R]: X coordinate of the root/target
+- \f[V]root_y\f[R]: Y coordinate of the root/target -
+\f[V]diagonal_cost\f[R]: Cost of diagonal movement (default: 1.41)
+.SS \f[V]compute_fov(x: int, y: int, radius: int = 0, light_walls: bool = True, algorithm: int = FOV_BASIC) -> List[Tuple[int, int, bool, bool]]\f[R]
+.PP
+Compute field of view from a position and return visible cells.
+.PP
+\f[B]Arguments:\f[R] - \f[V]x\f[R]: X coordinate of the viewer -
+\f[V]y\f[R]: Y coordinate of the viewer - \f[V]radius\f[R]: Maximum view
+distance (0 = unlimited) - \f[V]light_walls\f[R]: Whether walls are lit
+when visible - \f[V]algorithm\f[R]: FOV algorithm to use (FOV_BASIC,
+FOV_DIAMOND, FOV_SHADOW, FOV_PERMISSIVE_0-8)
+.PP
+\f[B]Returns:\f[R] List of tuples (x, y, visible, discovered) for all
+visible cells: - x, y: Grid coordinates - visible: True (all returned
+cells are visible) - discovered: True (FOV implies discovery) Also
+updates the internal FOV state for use with is_in_fov().
+.SS \f[V]find_path(x1: int, y1: int, x2: int, y2: int, diagonal_cost: float = 1.41) -> List[Tuple[int, int]]\f[R]
+.PP
+Find A* path between two points.
+.PP
+\f[B]Arguments:\f[R] - \f[V]x1\f[R]: Starting X coordinate -
+\f[V]y1\f[R]: Starting Y coordinate - \f[V]x2\f[R]: Target X coordinate
+- \f[V]y2\f[R]: Target Y coordinate - \f[V]diagonal_cost\f[R]: Cost of
+diagonal movement (default: 1.41)
+.PP
+\f[B]Returns:\f[R] List of (x, y) tuples representing the path, empty
+list if no path exists Uses A* algorithm with walkability from grid
+cells.
+.SS \f[V]get_bounds() -> tuple\f[R]
+.PP
+Get the bounding rectangle of this drawable element.
+.PP
+Note:
+.PP
+\f[B]Returns:\f[R] tuple: (x, y, width, height) representing the
+element\[cq]s bounds The bounds are in screen coordinates and account
+for current position and size.
+.SS \f[V]get_dijkstra_distance(x: int, y: int) -> Optional[float]\f[R]
+.PP
+Get distance from Dijkstra root to position.
+.PP
+\f[B]Arguments:\f[R] - \f[V]x\f[R]: X coordinate to query - \f[V]y\f[R]:
+Y coordinate to query
+.PP
+\f[B]Returns:\f[R] Distance as float, or None if position is unreachable
+or invalid Must call compute_dijkstra() first.
+.SS \f[V]get_dijkstra_path(x: int, y: int) -> List[Tuple[int, int]]\f[R]
+.PP
+Get path from position to Dijkstra root.
+.PP
+\f[B]Arguments:\f[R] - \f[V]x\f[R]: Starting X coordinate - \f[V]y\f[R]:
+Starting Y coordinate
+.PP
+\f[B]Returns:\f[R] List of (x, y) tuples representing path to root,
+empty if unreachable Must call compute_dijkstra() first.
+Path includes start but not root position.
+.SS \f[V]is_in_fov(x: int, y: int) -> bool\f[R]
+.PP
+Check if a cell is in the field of view.
+.PP
+\f[B]Arguments:\f[R] - \f[V]x\f[R]: X coordinate to check - \f[V]y\f[R]:
+Y coordinate to check
+.PP
+\f[B]Returns:\f[R] True if the cell is visible, False otherwise Must
+call compute_fov() first to calculate visibility.
+.SS \f[V]move(dx: float, dy: float) -> None\f[R]
+.PP
+Move the element by a relative offset.
+.PP
+Note:
+.PP
+\f[B]Arguments:\f[R] - \f[V]dx\f[R]: Horizontal offset in pixels -
+\f[V]dy\f[R]: Vertical offset in pixels
+.SS \f[V]resize(width: float, height: float) -> None\f[R]
+.PP
+Resize the element to new dimensions.
+.PP
+Note:
+.PP
+\f[B]Arguments:\f[R] - \f[V]width\f[R]: New width in pixels -
+\f[V]height\f[R]: New height in pixels
+.SS GridPoint
+.PP
+UIGridPoint object
+.PP
+\f[B]Methods:\f[R]
+.SS GridPointState
+.PP
+UIGridPointState object
+.PP
+\f[B]Methods:\f[R]
+.SS Scene
+.PP
+Base class for object-oriented scenes
+.PP
+\f[B]Methods:\f[R]
+.SS \f[V]activate() -> None\f[R]
+.PP
+Make this the active scene.
+.PP
+Note:
+.PP
+\f[B]Returns:\f[R] None Deactivates the current scene and activates this
+one.
+Scene transitions and lifecycle callbacks are triggered.
+.SS \f[V]get_ui() -> UICollection\f[R]
+.PP
+Get the UI element collection for this scene.
+.PP
+Note:
+.PP
+\f[B]Returns:\f[R] UICollection: Collection of UI elements (Frames,
+Captions, Sprites, Grids) in this scene Use to add, remove, or iterate
+over UI elements.
+Changes are reflected immediately.
+.SS \f[V]register_keyboard(callback: callable) -> None\f[R]
+.PP
+Register a keyboard event handler function.
+.PP
+Note:
+.PP
+\f[B]Arguments:\f[R] - \f[V]callback\f[R]: Function that receives (key:
+str, pressed: bool) when keyboard events occur
+.PP
+\f[B]Returns:\f[R] None Alternative to overriding on_keypress() method.
+Handler is called for both key press and release events.
+.SS Sprite
+.PP
+\f[I]Inherits from: Drawable\f[R]
+.PP
+Sprite(pos=None, texture=None, sprite_index=0, **kwargs)
+.PP
+A sprite UI element that displays a texture or portion of a texture
+atlas.
+.PP
+Args: pos (tuple, optional): Position as (x, y) tuple.
+Default: (0, 0) texture (Texture, optional): Texture object to display.
+Default: default texture sprite_index (int, optional): Index into
+texture atlas.
+Default: 0
+.PP
+Keyword Args: scale (float): Uniform scale factor.
+Default: 1.0 scale_x (float): Horizontal scale factor.
+Default: 1.0 scale_y (float): Vertical scale factor.
+Default: 1.0 click (callable): Click event handler.
+Default: None visible (bool): Visibility state.
+Default: True opacity (float): Opacity (0.0-1.0).
+Default: 1.0 z_index (int): Rendering order.
+Default: 0 name (str): Element name for finding.
+Default: None x (float): X position override.
+Default: 0 y (float): Y position override.
+Default: 0
+.PP
+Attributes: x, y (float): Position in pixels pos (Vector): Position as a
+Vector object texture (Texture): The texture being displayed
+sprite_index (int): Current sprite index in texture atlas scale (float):
+Uniform scale factor scale_x, scale_y (float): Individual scale factors
+click (callable): Click event handler visible (bool): Visibility state
+opacity (float): Opacity value z_index (int): Rendering order name
+(str): Element name w, h (float): Read-only computed size based on
+texture and scale
+.PP
+\f[B]Methods:\f[R]
+.SS \f[V]get_bounds() -> tuple\f[R]
+.PP
+Get the bounding rectangle of this drawable element.
+.PP
+Note:
+.PP
+\f[B]Returns:\f[R] tuple: (x, y, width, height) representing the
+element\[cq]s bounds The bounds are in screen coordinates and account
+for current position and size.
+.SS \f[V]move(dx: float, dy: float) -> None\f[R]
+.PP
+Move the element by a relative offset.
+.PP
+Note:
+.PP
+\f[B]Arguments:\f[R] - \f[V]dx\f[R]: Horizontal offset in pixels -
+\f[V]dy\f[R]: Vertical offset in pixels
+.SS \f[V]resize(width: float, height: float) -> None\f[R]
+.PP
+Resize the element to new dimensions.
+.PP
+Note:
+.PP
+\f[B]Arguments:\f[R] - \f[V]width\f[R]: New width in pixels -
+\f[V]height\f[R]: New height in pixels
+.SS Texture
+.PP
+SFML Texture Object
+.PP
+\f[B]Methods:\f[R]
+.SS Timer
+.PP
+Timer(name, callback, interval, once=False)
+.PP
+Create a timer that calls a function at regular intervals.
+.PP
+Args: name (str): Unique identifier for the timer callback (callable):
+Function to call - receives (timer, runtime) args interval (int): Time
+between calls in milliseconds once (bool): If True, timer stops after
+first call.
+Default: False
+.PP
+Attributes: interval (int): Time between calls in milliseconds remaining
+(int): Time until next call in milliseconds (read-only) paused (bool):
+Whether timer is paused (read-only) active (bool): Whether timer is
+active and not paused (read-only) callback (callable): The callback
+function once (bool): Whether timer stops after firing once
+.PP
+Methods: pause(): Pause the timer, preserving time remaining resume():
+Resume a paused timer cancel(): Stop and remove the timer restart():
+Reset timer to start from beginning
+.PP
+Example: def on_timer(timer, runtime): print(f\[cq]Timer {timer} fired
+at {runtime}ms\[cq]) if runtime > 5000: timer.cancel()
+.IP
+.nf
+\f[C]
+timer = mcrfpy.Timer(\[aq]my_timer\[aq], on_timer, 1000)
+timer.pause() # Pause timer
+timer.resume() # Resume timer
+timer.once = True # Make it one-shot
+\f[R]
+.fi
+.PP
+\f[B]Methods:\f[R]
+.SS \f[V]cancel() -> None\f[R]
+.PP
+Cancel the timer and remove it from the timer system.
+.PP
+Note:
+.PP
+\f[B]Returns:\f[R] None The timer will no longer fire and cannot be
+restarted.
+The callback will not be called again.
+.SS \f[V]pause() -> None\f[R]
+.PP
+Pause the timer, preserving the time remaining until next trigger.
+.PP
+Note:
+.PP
+\f[B]Returns:\f[R] None The timer can be resumed later with resume().
+Time spent paused does not count toward the interval.
+.SS \f[V]restart() -> None\f[R]
+.PP
+Restart the timer from the beginning.
+.PP
+Note:
+.PP
+\f[B]Returns:\f[R] None Resets the timer to fire after a full interval
+from now, regardless of remaining time.
+.SS \f[V]resume() -> None\f[R]
+.PP
+Resume a paused timer from where it left off.
+.PP
+Note:
+.PP
+\f[B]Returns:\f[R] None Has no effect if the timer is not paused.
+Timer will fire after the remaining time elapses.
+.SS UICollection
+.PP
+Iterable, indexable collection of UI objects
+.PP
+\f[B]Methods:\f[R]
+.SS \f[V]append(...)\f[R]
+.SS \f[V]count(...)\f[R]
+.SS \f[V]extend(...)\f[R]
+.SS \f[V]index(...)\f[R]
+.SS \f[V]remove(...)\f[R]
+.SS UICollectionIter
+.PP
+Iterator for a collection of UI objects
+.PP
+\f[B]Methods:\f[R]
+.SS UIEntityCollectionIter
+.PP
+Iterator for a collection of UI objects
+.PP
+\f[B]Methods:\f[R]
+.SS Vector
+.PP
+SFML Vector Object
+.PP
+\f[B]Methods:\f[R]
+.SS \f[V]angle() -> float\f[R]
+.PP
+Get the angle of this vector in radians.
+.PP
+\f[B]Returns:\f[R] float: Angle in radians from positive x-axis
+.SS \f[V]copy() -> Vector\f[R]
+.PP
+Create a copy of this vector.
+.PP
+\f[B]Returns:\f[R] Vector: New Vector object with same x and y values
+.SS \f[V]distance_to(other: Vector) -> float\f[R]
+.PP
+Calculate the distance to another vector.
+.PP
+\f[B]Arguments:\f[R] - \f[V]other\f[R]: The other vector
+.PP
+\f[B]Returns:\f[R] float: Distance between the two vectors
+.SS \f[V]dot(other: Vector) -> float\f[R]
+.PP
+Calculate the dot product with another vector.
+.PP
+\f[B]Arguments:\f[R] - \f[V]other\f[R]: The other vector
+.PP
+\f[B]Returns:\f[R] float: Dot product of the two vectors
+.SS \f[V]magnitude() -> float\f[R]
+.PP
+Calculate the length/magnitude of this vector.
+.PP
+\f[B]Returns:\f[R] float: The magnitude of the vector
+.SS \f[V]magnitude_squared() -> float\f[R]
+.PP
+Calculate the squared magnitude of this vector.
+.PP
+Note:
+.PP
+\f[B]Returns:\f[R] float: The squared magnitude (faster than
+magnitude()) Use this for comparisons to avoid expensive square root
+calculation.
+.SS \f[V]normalize() -> Vector\f[R]
+.PP
+Return a unit vector in the same direction.
+.PP
+Note:
+.PP
+\f[B]Returns:\f[R] Vector: New normalized vector with magnitude 1.0 For
+zero vectors (magnitude 0.0), returns a zero vector rather than raising
+an exception
+.SS Window
+.PP
+Window singleton for accessing and modifying the game window properties
+.PP
+\f[B]Methods:\f[R]
+.SS \f[V]center() -> None\f[R]
+.PP
+Center the window on the screen.
+.PP
+Note:
+.PP
+\f[B]Returns:\f[R] None Only works in windowed mode.
+Has no effect when fullscreen or in headless mode.
+.SS \f[V]get() -> Window\f[R]
+.PP
+Get the Window singleton instance.
+.PP
+Note:
+.PP
+\f[B]Returns:\f[R] Window: The global window object This is a class
+method.
+Call as Window.get().
+There is only one window instance per application.
+.SS \f[V]screenshot(filename: str = None) -> bytes | None\f[R]
+.PP
+Take a screenshot of the current window contents.
+.PP
+Note:
+.PP
+\f[B]Arguments:\f[R] - \f[V]filename\f[R]: Optional path to save
+screenshot.
+If omitted, returns raw RGBA bytes.
+.PP
+\f[B]Returns:\f[R] bytes | None: Raw RGBA pixel data if no filename
+given, otherwise None after saving Screenshot is taken at the actual
+window resolution.
+Use after render loop update for current frame.
+.SS Constants
+.IP \[bu] 2
+\f[V]FOV_BASIC\f[R] (int): 0
+.IP \[bu] 2
+\f[V]FOV_DIAMOND\f[R] (int): 1
+.IP \[bu] 2
+\f[V]FOV_PERMISSIVE_0\f[R] (int): 3
+.IP \[bu] 2
+\f[V]FOV_PERMISSIVE_1\f[R] (int): 4
+.IP \[bu] 2
+\f[V]FOV_PERMISSIVE_2\f[R] (int): 5
+.IP \[bu] 2
+\f[V]FOV_PERMISSIVE_3\f[R] (int): 6
+.IP \[bu] 2
+\f[V]FOV_PERMISSIVE_4\f[R] (int): 7
+.IP \[bu] 2
+\f[V]FOV_PERMISSIVE_5\f[R] (int): 8
+.IP \[bu] 2
+\f[V]FOV_PERMISSIVE_6\f[R] (int): 9
+.IP \[bu] 2
+\f[V]FOV_PERMISSIVE_7\f[R] (int): 10
+.IP \[bu] 2
+\f[V]FOV_PERMISSIVE_8\f[R] (int): 11
+.IP \[bu] 2
+\f[V]FOV_RESTRICTIVE\f[R] (int): 12
+.IP \[bu] 2
+\f[V]FOV_SHADOW\f[R] (int): 2
diff --git a/stubs/mcrfpy.pyi b/stubs/mcrfpy.pyi
new file mode 100644
index 0000000..919794b
--- /dev/null
+++ b/stubs/mcrfpy.pyi
@@ -0,0 +1,532 @@
+"""Type stubs for McRogueFace Python API.
+
+Core game engine interface for creating roguelike games with Python.
+"""
+
+from typing import Any, List, Dict, Tuple, Optional, Callable, Union, overload
+
+# Type aliases
+UIElement = Union['Frame', 'Caption', 'Sprite', 'Grid']
+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
+
+ def get_bounds(self) -> Tuple[float, float, float, float]:
+ """Get bounding box as (x, y, width, height)."""
+ ...
+
+ def move(self, dx: float, dy: float) -> None:
+ """Move by relative offset (dx, dy)."""
+ ...
+
+ def resize(self, width: float, height: float) -> None:
+ """Resize to new dimensions (width, height)."""
+ ...
+
+class Frame(Drawable):
+ """Frame(x=0, y=0, w=0, h=0, fill_color=None, outline_color=None, outline=0, click=None, children=None)
+
+ A rectangular frame UI element that can contain other drawable elements.
+ """
+
+ @overload
+ def __init__(self) -> None: ...
+ @overload
+ def __init__(self, x: float = 0, y: float = 0, w: float = 0, h: float = 0,
+ fill_color: Optional[Color] = None, outline_color: Optional[Color] = None,
+ outline: float = 0, click: Optional[Callable] = None,
+ children: Optional[List[UIElement]] = None) -> None: ...
+
+ w: float
+ h: float
+ fill_color: Color
+ outline_color: Color
+ outline: float
+ click: Optional[Callable[[float, float, int], None]]
+ children: 'UICollection'
+ clip_children: bool
+
+class Caption(Drawable):
+ """Caption(text='', x=0, y=0, font=None, fill_color=None, outline_color=None, outline=0, click=None)
+
+ A text display UI element with customizable font and styling.
+ """
+
+ @overload
+ def __init__(self) -> None: ...
+ @overload
+ def __init__(self, text: str = '', x: float = 0, y: float = 0,
+ font: Optional[Font] = None, fill_color: Optional[Color] = None,
+ outline_color: Optional[Color] = None, outline: float = 0,
+ click: Optional[Callable] = None) -> None: ...
+
+ text: str
+ font: Font
+ fill_color: Color
+ outline_color: Color
+ outline: float
+ click: Optional[Callable[[float, float, int], None]]
+ w: float # Read-only, computed from text
+ h: float # Read-only, computed from text
+
+class Sprite(Drawable):
+ """Sprite(x=0, y=0, texture=None, sprite_index=0, scale=1.0, click=None)
+
+ A sprite UI element that displays a texture or portion of a texture atlas.
+ """
+
+ @overload
+ def __init__(self) -> None: ...
+ @overload
+ def __init__(self, x: float = 0, y: float = 0, texture: Optional[Texture] = None,
+ sprite_index: int = 0, scale: float = 1.0,
+ click: Optional[Callable] = None) -> None: ...
+
+ texture: Texture
+ sprite_index: int
+ scale: float
+ click: Optional[Callable[[float, float, int], None]]
+ w: float # Read-only, computed from texture
+ h: float # Read-only, computed from texture
+
+class Grid(Drawable):
+ """Grid(x=0, y=0, grid_size=(20, 20), texture=None, tile_width=16, tile_height=16, scale=1.0, click=None)
+
+ A grid-based tilemap UI element for rendering tile-based levels and game worlds.
+ """
+
+ @overload
+ def __init__(self) -> None: ...
+ @overload
+ def __init__(self, x: float = 0, y: float = 0, grid_size: Tuple[int, int] = (20, 20),
+ texture: Optional[Texture] = None, tile_width: int = 16, tile_height: int = 16,
+ scale: float = 1.0, click: Optional[Callable] = None) -> None: ...
+
+ grid_size: Tuple[int, int]
+ tile_width: int
+ tile_height: int
+ texture: Texture
+ scale: float
+ points: List[List['GridPoint']]
+ entities: 'EntityCollection'
+ background_color: Color
+ click: Optional[Callable[[int, int, int], None]]
+
+ def at(self, x: int, y: int) -> 'GridPoint':
+ """Get grid point at tile coordinates."""
+ ...
+
+class GridPoint:
+ """Grid point representing a single tile."""
+
+ texture_index: int
+ solid: bool
+ color: Color
+
+class GridPointState:
+ """State information for a grid point."""
+
+ texture_index: int
+ color: Color
+
+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)."""
+
+ 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
+
+ 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."""
+ ...
+
+ def on_click(self, x: float, y: float, button: int) -> None:
+ """Handle mouse clicks."""
+ ...
+
+ def on_enter(self) -> None:
+ """Called when entering the scene."""
+ ...
+
+ def on_exit(self) -> None:
+ """Called when leaving the scene."""
+ ...
+
+ def on_resize(self, width: int, height: int) -> None:
+ """Handle window resize events."""
+ ...
+
+ def update(self, dt: float) -> None:
+ """Update scene logic."""
+ ...
+
+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."""
+ ...
diff --git a/stubs/mcrfpy/__init__.pyi b/stubs/mcrfpy/__init__.pyi
new file mode 100644
index 0000000..5e7b5ac
--- /dev/null
+++ b/stubs/mcrfpy/__init__.pyi
@@ -0,0 +1,213 @@
+"""Type stubs for McRogueFace Python API.
+
+Auto-generated - do not edit directly.
+"""
+
+from typing import Any, List, Dict, Tuple, Optional, Callable, Union
+
+# Module documentation
+# McRogueFace Python API
+#
+# Core game engine interface for creating roguelike games with Python.
+
+# Classes
+
+class Animation:
+ """Animation object for animating UI properties"""
+ def __init__(selftype(self)) -> None: ...
+
+ def complete(self) -> None: ...
+ def get_current_value(self) -> Any: ...
+ def hasValidTarget(self) -> bool: ...
+ def start(selftarget: UIDrawable) -> None: ...
+ def update(selfdelta_time: float) -> bool: ...
+
+class Caption:
+ """Caption(pos=None, font=None, text='', **kwargs)"""
+ def __init__(selftype(self)) -> None: ...
+
+ def get_bounds(self) -> tuple: ...
+ def move(selfdx: float, dy: float) -> None: ...
+ def resize(selfwidth: float, height: float) -> None: ...
+
+class Color:
+ """SFML Color Object"""
+ def __init__(selftype(self)) -> None: ...
+
+ def from_hex(selfhex_string: str) -> Color: ...
+ def lerp(selfother: Color, t: float) -> Color: ...
+ def to_hex(self) -> str: ...
+
+class Drawable:
+ """Base class for all drawable UI elements"""
+ def __init__(selftype(self)) -> None: ...
+
+ def get_bounds(self) -> tuple: ...
+ def move(selfdx: float, dy: float) -> None: ...
+ def resize(selfwidth: float, height: float) -> None: ...
+
+class Entity:
+ """Entity(grid_pos=None, texture=None, sprite_index=0, **kwargs)"""
+ def __init__(selftype(self)) -> None: ...
+
+ def at(self, *args, **kwargs) -> Any: ...
+ def die(self, *args, **kwargs) -> Any: ...
+ def get_bounds(self) -> tuple: ...
+ def index(self, *args, **kwargs) -> Any: ...
+ def move(selfdx: float, dy: float) -> None: ...
+ def path_to(selfx: int, y: int) -> bool: ...
+ def resize(selfwidth: float, height: float) -> None: ...
+ def update_visibility(self) -> None: ...
+
+class EntityCollection:
+ """Iterable, indexable collection of Entities"""
+ def __init__(selftype(self)) -> None: ...
+
+ def append(self, *args, **kwargs) -> Any: ...
+ def count(self, *args, **kwargs) -> Any: ...
+ def extend(self, *args, **kwargs) -> Any: ...
+ def index(self, *args, **kwargs) -> Any: ...
+ def remove(self, *args, **kwargs) -> Any: ...
+
+class Font:
+ """SFML Font Object"""
+ def __init__(selftype(self)) -> None: ...
+
+class Frame:
+ """Frame(pos=None, size=None, **kwargs)"""
+ def __init__(selftype(self)) -> None: ...
+
+ def get_bounds(self) -> tuple: ...
+ def move(selfdx: float, dy: float) -> None: ...
+ def resize(selfwidth: float, height: float) -> None: ...
+
+class Grid:
+ """Grid(pos=None, size=None, grid_size=None, texture=None, **kwargs)"""
+ def __init__(selftype(self)) -> None: ...
+
+ def at(self, *args, **kwargs) -> Any: ...
+ def compute_astar_path(selfx1: int, y1: int, x2: int, y2: int, diagonal_cost: float = 1.41) -> List[Tuple[int, int]]: ...
+ def compute_dijkstra(selfroot_x: int, root_y: int, diagonal_cost: float = 1.41) -> None: ...
+ def compute_fov(selfx: int, y: int, radius: int = 0, light_walls: bool = True, algorithm: int = FOV_BASIC) -> List[Tuple[int, int, bool, bool]]: ...
+ def find_path(selfx1: int, y1: int, x2: int, y2: int, diagonal_cost: float = 1.41) -> List[Tuple[int, int]]: ...
+ def get_bounds(self) -> tuple: ...
+ def get_dijkstra_distance(selfx: int, y: int) -> Optional[float]: ...
+ def get_dijkstra_path(selfx: int, y: int) -> List[Tuple[int, int]]: ...
+ def is_in_fov(selfx: int, y: int) -> bool: ...
+ def move(selfdx: float, dy: float) -> None: ...
+ def resize(selfwidth: float, height: float) -> None: ...
+
+class GridPoint:
+ """UIGridPoint object"""
+ def __init__(selftype(self)) -> None: ...
+
+class GridPointState:
+ """UIGridPointState object"""
+ def __init__(selftype(self)) -> None: ...
+
+class Scene:
+ """Base class for object-oriented scenes"""
+ def __init__(selftype(self)) -> None: ...
+
+ def activate(self) -> None: ...
+ def get_ui(self) -> UICollection: ...
+ def register_keyboard(selfcallback: callable) -> None: ...
+
+class Sprite:
+ """Sprite(pos=None, texture=None, sprite_index=0, **kwargs)"""
+ def __init__(selftype(self)) -> None: ...
+
+ def get_bounds(self) -> tuple: ...
+ def move(selfdx: float, dy: float) -> None: ...
+ def resize(selfwidth: float, height: float) -> None: ...
+
+class Texture:
+ """SFML Texture Object"""
+ def __init__(selftype(self)) -> None: ...
+
+class Timer:
+ """Timer(name, callback, interval, once=False)"""
+ def __init__(selftype(self)) -> None: ...
+
+ def cancel(self) -> None: ...
+ def pause(self) -> None: ...
+ def restart(self) -> None: ...
+ def resume(self) -> None: ...
+
+class UICollection:
+ """Iterable, indexable collection of UI objects"""
+ def __init__(selftype(self)) -> None: ...
+
+ def append(self, *args, **kwargs) -> Any: ...
+ def count(self, *args, **kwargs) -> Any: ...
+ def extend(self, *args, **kwargs) -> Any: ...
+ def index(self, *args, **kwargs) -> Any: ...
+ def remove(self, *args, **kwargs) -> Any: ...
+
+class UICollectionIter:
+ """Iterator for a collection of UI objects"""
+ def __init__(selftype(self)) -> None: ...
+
+class UIEntityCollectionIter:
+ """Iterator for a collection of UI objects"""
+ def __init__(selftype(self)) -> None: ...
+
+class Vector:
+ """SFML Vector Object"""
+ def __init__(selftype(self)) -> None: ...
+
+ def angle(self) -> float: ...
+ def copy(self) -> Vector: ...
+ def distance_to(selfother: Vector) -> float: ...
+ def dot(selfother: Vector) -> float: ...
+ def magnitude(self) -> float: ...
+ def magnitude_squared(self) -> float: ...
+ def normalize(self) -> Vector: ...
+
+class Window:
+ """Window singleton for accessing and modifying the game window properties"""
+ def __init__(selftype(self)) -> None: ...
+
+ def center(self) -> None: ...
+ def get(self) -> Window: ...
+ def screenshot(selffilename: str = None) -> bytes | None: ...
+
+# Functions
+
+def createScene(name: str) -> None: ...
+def createSoundBuffer(filename: str) -> int: ...
+def currentScene() -> str: ...
+def delTimer(name: str) -> None: ...
+def exit() -> None: ...
+def find(name: str, scene: str = None) -> UIDrawable | None: ...
+def findAll(pattern: str, scene: str = None) -> list: ...
+def getMetrics() -> dict: ...
+def getMusicVolume() -> int: ...
+def getSoundVolume() -> int: ...
+def keypressScene(handler: callable) -> None: ...
+def loadMusic(filename: str) -> None: ...
+def playSound(buffer_id: int) -> None: ...
+def sceneUI(scene: str = None) -> list: ...
+def setMusicVolume(volume: int) -> None: ...
+def setScale(multiplier: float) -> None: ...
+def setScene(scene: str, transition: str = None, duration: float = 0.0) -> None: ...
+def setSoundVolume(volume: int) -> None: ...
+def setTimer(name: str, handler: callable, interval: int) -> None: ...
+
+# Constants
+
+FOV_BASIC: int
+FOV_DIAMOND: int
+FOV_PERMISSIVE_0: int
+FOV_PERMISSIVE_1: int
+FOV_PERMISSIVE_2: int
+FOV_PERMISSIVE_3: int
+FOV_PERMISSIVE_4: int
+FOV_PERMISSIVE_5: int
+FOV_PERMISSIVE_6: int
+FOV_PERMISSIVE_7: int
+FOV_PERMISSIVE_8: int
+FOV_RESTRICTIVE: int
+FOV_SHADOW: int
+default_font: Any
+default_texture: Any
\ No newline at end of file
diff --git a/stubs/mcrfpy/automation.pyi b/stubs/mcrfpy/automation.pyi
new file mode 100644
index 0000000..57ed71a
--- /dev/null
+++ b/stubs/mcrfpy/automation.pyi
@@ -0,0 +1,24 @@
+"""Type stubs for McRogueFace automation API."""
+
+from typing import Optional, Tuple
+
+def click(x=None, y=None, clicks=1, interval=0.0, button='left') -> Any: ...
+def doubleClick(x=None, y=None) -> Any: ...
+def dragRel(xOffset, yOffset, duration=0.0, button='left') -> Any: ...
+def dragTo(x, y, duration=0.0, button='left') -> Any: ...
+def hotkey(*keys) - Press a hotkey combination (e.g., hotkey('ctrl', 'c')) -> Any: ...
+def keyDown(key) -> Any: ...
+def keyUp(key) -> Any: ...
+def middleClick(x=None, y=None) -> Any: ...
+def mouseDown(x=None, y=None, button='left') -> Any: ...
+def mouseUp(x=None, y=None, button='left') -> Any: ...
+def moveRel(xOffset, yOffset, duration=0.0) -> Any: ...
+def moveTo(x, y, duration=0.0) -> Any: ...
+def onScreen(x, y) -> Any: ...
+def position() - Get current mouse position as (x, y) -> Any: ...
+def rightClick(x=None, y=None) -> Any: ...
+def screenshot(filename) -> Any: ...
+def scroll(clicks, x=None, y=None) -> Any: ...
+def size() - Get screen size as (width, height) -> Any: ...
+def tripleClick(x=None, y=None) -> Any: ...
+def typewrite(message, interval=0.0) -> Any: ...
\ No newline at end of file
diff --git a/stubs/py.typed b/stubs/py.typed
new file mode 100644
index 0000000..e69de29
diff --git a/tools/generate_all_docs.sh b/tools/generate_all_docs.sh
new file mode 100755
index 0000000..7c0234d
--- /dev/null
+++ b/tools/generate_all_docs.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+set -e # Exit on any error
+
+echo "=== McRogueFace Documentation Generation ==="
+
+# Verify build exists
+if [ ! -f "./build/mcrogueface" ]; then
+ echo "ERROR: build/mcrogueface not found. Run 'make' first."
+ exit 1
+fi
+
+# Generate API docs (HTML + Markdown)
+echo "Generating API documentation..."
+./build/mcrogueface --headless --exec tools/generate_dynamic_docs.py
+
+# Generate type stubs (using v2 - manually maintained high-quality stubs)
+echo "Generating type stubs..."
+./build/mcrogueface --headless --exec tools/generate_stubs_v2.py
+
+# Generate man page
+echo "Generating man page..."
+./tools/generate_man_page.sh
+
+echo "=== Documentation generation complete ==="
+echo " HTML: docs/api_reference_dynamic.html"
+echo " Markdown: docs/API_REFERENCE_DYNAMIC.md"
+echo " Man page: docs/mcrfpy.3"
+echo " Stubs: stubs/mcrfpy.pyi"
diff --git a/tools/generate_dynamic_docs.py b/tools/generate_dynamic_docs.py
index 426fdcd..c6e4d4d 100644
--- a/tools/generate_dynamic_docs.py
+++ b/tools/generate_dynamic_docs.py
@@ -82,67 +82,90 @@ except ImportError:
sys.exit(1)
def parse_docstring(docstring):
- """Parse a docstring to extract signature, description, args, and returns."""
+ """Parse a docstring to extract signature, description, args, returns, and raises."""
if not docstring:
- return {"signature": "", "description": "", "args": [], "returns": "", "example": ""}
-
+ return {"signature": "", "description": "", "args": [], "returns": "", "raises": "", "example": ""}
+
lines = docstring.strip().split('\n')
result = {
"signature": "",
"description": "",
"args": [],
"returns": "",
+ "raises": "",
"example": ""
}
-
+
# First line often contains the signature
if lines and '(' in lines[0] and ')' in lines[0]:
result["signature"] = lines[0].strip()
lines = lines[1:] if len(lines) > 1 else []
-
+
# Parse the rest
current_section = "description"
description_lines = []
+ returns_lines = []
+ raises_lines = []
example_lines = []
- in_example = False
-
+
for line in lines:
line_lower = line.strip().lower()
-
+
+ # Detect section headers
if line_lower.startswith("args:") or line_lower.startswith("arguments:"):
current_section = "args"
continue
elif line_lower.startswith("returns:") or line_lower.startswith("return:"):
current_section = "returns"
- result["returns"] = line[line.find(':')+1:].strip()
+ # Capture any text on the same line as "Returns:"
+ content_after_colon = line[line.find(':')+1:].strip()
+ if content_after_colon:
+ returns_lines.append(content_after_colon)
+ continue
+ elif line_lower.startswith("raises:") or line_lower.startswith("raise:"):
+ current_section = "raises"
+ # Capture any text on the same line as "Raises:"
+ content_after_colon = line[line.find(':')+1:].strip()
+ if content_after_colon:
+ raises_lines.append(content_after_colon)
continue
elif line_lower.startswith("example:") or line_lower.startswith("examples:"):
- in_example = True
+ current_section = "example"
continue
elif line_lower.startswith("note:"):
+ # Notes go into description
if description_lines:
description_lines.append("")
description_lines.append(line)
continue
-
- if in_example:
- example_lines.append(line)
- elif current_section == "description" and not line.startswith(" "):
+
+ # Skip blank lines unless we're in example section
+ if not line.strip() and current_section != "example":
+ continue
+
+ # Add content to appropriate section
+ if current_section == "description":
description_lines.append(line)
elif current_section == "args" and line.strip():
- # Parse argument lines like " x: X coordinate"
+ # Parse argument lines like " filename: Path to file"
match = re.match(r'\s+(\w+):\s*(.+)', line)
if match:
result["args"].append({
"name": match.group(1),
"description": match.group(2).strip()
})
- elif current_section == "returns" and line.strip() and line.startswith(" "):
- result["returns"] += " " + line.strip()
-
+ elif current_section == "returns" and line.strip():
+ returns_lines.append(line.strip())
+ elif current_section == "raises" and line.strip():
+ raises_lines.append(line.strip())
+ elif current_section == "example":
+ example_lines.append(line)
+
result["description"] = '\n'.join(description_lines).strip()
+ result["returns"] = ' '.join(returns_lines).strip()
+ result["raises"] = ' '.join(raises_lines).strip()
result["example"] = '\n'.join(example_lines).strip()
-
+
return result
def get_all_functions():
@@ -361,27 +384,32 @@ def generate_html_docs():
for func_name in sorted(functions.keys()):
func_info = functions[func_name]
parsed = func_info["parsed"]
-
+
+ # Use signature if available (already includes name), otherwise use just name
+ heading = parsed['signature'] if parsed['signature'] else f"{func_name}(...)"
html_content += f"""
-
{func_name}{parsed['signature'] if parsed['signature'] else '(...)'}
+
{heading}
"""
if parsed['description']:
description = transform_doc_links(parsed['description'], format='html')
html_content += f"
{description}
\n"
-
+
if parsed['args']:
html_content += "
Arguments:
\n
\n"
for arg in parsed['args']:
html_content += f" - {arg['name']}: {html.escape(arg['description'])}
\n"
html_content += "
\n"
-
+
if parsed['returns']:
html_content += f"
Returns: {html.escape(parsed['returns'])}
\n"
-
+
+ if parsed['raises']:
+ html_content += f"
Raises: {html.escape(parsed['raises'])}
\n"
+
if parsed['example']:
html_content += f"
Example:
\n
{html.escape(parsed['example'])}
\n"
-
+
html_content += "
\n"
# Generate class documentation
@@ -419,25 +447,30 @@ def generate_html_docs():
if method_name == '__init__':
continue
parsed = method_info['parsed']
-
+
+ # Use signature if available (already includes name), otherwise use just name
+ heading = parsed['signature'] if parsed['signature'] else f"{method_name}(...)"
html_content += f"""
-
{method_name}{parsed['signature'] if parsed['signature'] else '(...)'}
+
{heading}
"""
if parsed['description']:
description = transform_doc_links(parsed['description'], format='html')
html_content += f"
{description}
\n"
-
+
if parsed['args']:
html_content += "
\n"
for arg in parsed['args']:
html_content += f"
{arg['name']}: {html.escape(arg['description'])}
\n"
html_content += "
\n"
-
+
if parsed['returns']:
html_content += f"
Returns: {html.escape(parsed['returns'])}
\n"
-
+
+ if parsed['raises']:
+ html_content += f"
Raises: {html.escape(parsed['raises'])}
\n"
+
html_content += "
\n"
html_content += "
\n"
@@ -491,22 +524,27 @@ def generate_markdown_docs():
for func_name in sorted(functions.keys()):
func_info = functions[func_name]
parsed = func_info["parsed"]
-
- md_content += f"### `{func_name}{parsed['signature'] if parsed['signature'] else '(...)'}`\n\n"
+
+ # Use signature if available (already includes name), otherwise use just name
+ heading = parsed['signature'] if parsed['signature'] else f"{func_name}(...)"
+ md_content += f"### `{heading}`\n\n"
if parsed['description']:
description = transform_doc_links(parsed['description'], format='markdown')
md_content += f"{description}\n\n"
-
+
if parsed['args']:
md_content += "**Arguments:**\n"
for arg in parsed['args']:
md_content += f"- `{arg['name']}`: {arg['description']}\n"
md_content += "\n"
-
+
if parsed['returns']:
md_content += f"**Returns:** {parsed['returns']}\n\n"
-
+
+ if parsed['raises']:
+ md_content += f"**Raises:** {parsed['raises']}\n\n"
+
if parsed['example']:
md_content += f"**Example:**\n```python\n{parsed['example']}\n```\n\n"
@@ -542,21 +580,26 @@ def generate_markdown_docs():
if method_name == '__init__':
continue
parsed = method_info['parsed']
-
- md_content += f"#### `{method_name}{parsed['signature'] if parsed['signature'] else '(...)'}`\n\n"
+
+ # Use signature if available (already includes name), otherwise use just name
+ heading = parsed['signature'] if parsed['signature'] else f"{method_name}(...)"
+ md_content += f"#### `{heading}`\n\n"
if parsed['description']:
description = transform_doc_links(parsed['description'], format='markdown')
md_content += f"{description}\n\n"
-
+
if parsed['args']:
md_content += "**Arguments:**\n"
for arg in parsed['args']:
md_content += f"- `{arg['name']}`: {arg['description']}\n"
md_content += "\n"
-
+
if parsed['returns']:
md_content += f"**Returns:** {parsed['returns']}\n\n"
+
+ if parsed['raises']:
+ md_content += f"**Raises:** {parsed['raises']}\n\n"
# Constants
md_content += "## Constants\n\n"
diff --git a/tools/generate_man_page.sh b/tools/generate_man_page.sh
new file mode 100755
index 0000000..45a160d
--- /dev/null
+++ b/tools/generate_man_page.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+# Convert markdown docs to man page format
+
+pandoc docs/API_REFERENCE_DYNAMIC.md \
+ -s -t man \
+ --metadata title="MCRFPY" \
+ --metadata section=3 \
+ --metadata date="$(date +%Y-%m-%d)" \
+ --metadata footer="McRogueFace $(git describe --tags 2>/dev/null || echo 'dev')" \
+ -o docs/mcrfpy.3
+
+echo "Generated docs/mcrfpy.3"