Compare commits

..

No commits in common. "b1b377368067c8adca8286c1ce8625e4fe3f4dab" and "f2f8d6422fca11dcab0159daae763fb893ca29f5" have entirely different histories.

10 changed files with 85 additions and 1602 deletions

View file

@ -17,7 +17,6 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
2. **Always Check Gitea First** 2. **Always Check Gitea First**
- Before starting work: Check open issues for related tasks or blockers - Before starting work: Check open issues for related tasks or blockers
- Before implementing: Read relevant wiki pages per the [Development Workflow](https://gamedev.ffwf.net/gitea/john/McRogueFace/wiki/Development-Workflow) consultation table
- When using `/roadmap` command: Query Gitea for up-to-date issue status - When using `/roadmap` command: Query Gitea for up-to-date issue status
- When researching a feature: Search Gitea wiki and issues before grepping codebase - When researching a feature: Search Gitea wiki and issues before grepping codebase
- When encountering a bug: Check if an issue already exists - When encountering a bug: Check if an issue already exists
@ -30,10 +29,9 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
4. **Document as You Go** 4. **Document as You Go**
- When work on one issue interacts with another system: Add notes to related issues - When work on one issue interacts with another system: Add notes to related issues
- When discovering undocumented behavior: Note it for wiki update - When discovering undocumented behavior: Create task to document it
- When documentation misleads you: Note it for wiki correction - When documentation misleads you: Create task to correct or expand it
- After committing code changes: Update relevant wiki pages (with user permission) - When implementing a feature: Update the Gitea wiki if appropriate
- Follow the [Development Workflow](https://gamedev.ffwf.net/gitea/john/McRogueFace/wiki/Development-Workflow) for wiki update procedures
5. **Cross-Reference Everything** 5. **Cross-Reference Everything**
- Commit messages should reference issue numbers (e.g., "Fixes #104", "Addresses #125") - Commit messages should reference issue numbers (e.g., "Fixes #104", "Addresses #125")

View file

@ -108,7 +108,7 @@
<body> <body>
<div class="container"> <div class="container">
<h1>McRogueFace API Reference</h1> <h1>McRogueFace API Reference</h1>
<p><em>Generated on 2025-11-29 10:12:05</em></p> <p><em>Generated on 2025-10-30 21:14:43</em></p>
<p><em>This documentation was dynamically generated from the compiled module.</em></p> <p><em>This documentation was dynamically generated from the compiled module.</em></p>
<div class="toc"> <div class="toc">
@ -118,11 +118,8 @@
<li><a href="#classes">Classes</a> <li><a href="#classes">Classes</a>
<ul> <ul>
<li><a href="#Animation">Animation</a></li> <li><a href="#Animation">Animation</a></li>
<li><a href="#Arc">Arc</a></li>
<li><a href="#Caption">Caption</a></li> <li><a href="#Caption">Caption</a></li>
<li><a href="#Circle">Circle</a></li>
<li><a href="#Color">Color</a></li> <li><a href="#Color">Color</a></li>
<li><a href="#ColorLayer">ColorLayer</a></li>
<li><a href="#Drawable">Drawable</a></li> <li><a href="#Drawable">Drawable</a></li>
<li><a href="#Entity">Entity</a></li> <li><a href="#Entity">Entity</a></li>
<li><a href="#EntityCollection">EntityCollection</a></li> <li><a href="#EntityCollection">EntityCollection</a></li>
@ -131,11 +128,9 @@
<li><a href="#Grid">Grid</a></li> <li><a href="#Grid">Grid</a></li>
<li><a href="#GridPoint">GridPoint</a></li> <li><a href="#GridPoint">GridPoint</a></li>
<li><a href="#GridPointState">GridPointState</a></li> <li><a href="#GridPointState">GridPointState</a></li>
<li><a href="#Line">Line</a></li>
<li><a href="#Scene">Scene</a></li> <li><a href="#Scene">Scene</a></li>
<li><a href="#Sprite">Sprite</a></li> <li><a href="#Sprite">Sprite</a></li>
<li><a href="#Texture">Texture</a></li> <li><a href="#Texture">Texture</a></li>
<li><a href="#TileLayer">TileLayer</a></li>
<li><a href="#Timer">Timer</a></li> <li><a href="#Timer">Timer</a></li>
<li><a href="#UICollection">UICollection</a></li> <li><a href="#UICollection">UICollection</a></li>
<li><a href="#UICollectionIter">UICollectionIter</a></li> <li><a href="#UICollectionIter">UICollectionIter</a></li>
@ -192,15 +187,6 @@ Note:</p>
<p><span class='returns'>Returns:</span> None No error is raised if the timer doesn&#x27;t exist.</p> <p><span class='returns'>Returns:</span> None No error is raised if the timer doesn&#x27;t exist.</p>
</div> </div>
<div class="method-section">
<h3><code class="function-signature">end_benchmark() -> str</code></h3>
<p>Stop benchmark capture and write data to JSON file.
Note:</p>
<p><span class='returns'>Returns:</span> str: The filename of the written benchmark data</p>
<p><span class='raises'>Raises:</span> RuntimeError: If no benchmark is currently running Returns the auto-generated filename (e.g., &#x27;benchmark_12345_20250528_143022.json&#x27;)</p>
</div>
<div class="method-section"> <div class="method-section">
<h3><code class="function-signature">exit() -> None</code></h3> <h3><code class="function-signature">exit() -> None</code></h3>
<p>Cleanly shut down the game engine and exit the application. <p>Cleanly shut down the game engine and exit the application.
@ -277,19 +263,6 @@ Note:</p>
<p><span class='returns'>Returns:</span> None Only one music track can play at a time. Loading new music stops the current track.</p> <p><span class='returns'>Returns:</span> None Only one music track can play at a time. Loading new music stops the current track.</p>
</div> </div>
<div class="method-section">
<h3><code class="function-signature">log_benchmark(message: str) -> None</code></h3>
<p>Add a log message to the current benchmark frame.
Note:</p>
<h4>Arguments:</h4>
<ul>
<li><span class='arg-name'>message</span>: Text to associate with the current frame</li>
</ul>
<p><span class='returns'>Returns:</span> None</p>
<p><span class='raises'>Raises:</span> RuntimeError: If no benchmark is currently running Messages appear in the &#x27;logs&#x27; array of each frame in the output JSON.</p>
</div>
<div class="method-section"> <div class="method-section">
<h3><code class="function-signature">playSound(buffer_id: int) -> None</code></h3> <h3><code class="function-signature">playSound(buffer_id: int) -> None</code></h3>
<p>Play a sound effect using a previously loaded buffer.</p> <p>Play a sound effect using a previously loaded buffer.</p>
@ -312,18 +285,6 @@ Note:</p>
<p><span class='raises'>Raises:</span> KeyError: If the specified scene doesn&#x27;t exist</p> <p><span class='raises'>Raises:</span> KeyError: If the specified scene doesn&#x27;t exist</p>
</div> </div>
<div class="method-section">
<h3><code class="function-signature">setDevConsole(enabled: bool) -> None</code></h3>
<p>Enable or disable the developer console overlay.
Note:</p>
<h4>Arguments:</h4>
<ul>
<li><span class='arg-name'>enabled</span>: True to enable the console (default), False to disable</li>
</ul>
<p><span class='returns'>Returns:</span> None When disabled, the grave/tilde key will not open the console. Use this to ship games without debug features.</p>
</div>
<div class="method-section"> <div class="method-section">
<h3><code class="function-signature">setMusicVolume(volume: int) -> None</code></h3> <h3><code class="function-signature">setMusicVolume(volume: int) -> None</code></h3>
<p>Set the global music volume.</p> <p>Set the global music volume.</p>
@ -383,15 +344,6 @@ Note:</p>
<p><span class='returns'>Returns:</span> None If a timer with this name exists, it will be replaced. The handler receives the total runtime in seconds as its argument.</p> <p><span class='returns'>Returns:</span> None If a timer with this name exists, it will be replaced. The handler receives the total runtime in seconds as its argument.</p>
</div> </div>
<div class="method-section">
<h3><code class="function-signature">start_benchmark() -> None</code></h3>
<p>Start capturing benchmark data to a file.
Note:</p>
<p><span class='returns'>Returns:</span> None</p>
<p><span class='raises'>Raises:</span> RuntimeError: If a benchmark is already running Benchmark filename is auto-generated from PID and timestamp. Use end_benchmark() to stop and get filename.</p>
</div>
<h2 id='classes'>Classes</h2> <h2 id='classes'>Classes</h2>
<div class="method-section"> <div class="method-section">
@ -446,73 +398,6 @@ Note:</p>
</div> </div>
</div> </div>
<div class="method-section">
<h3 id="Arc"><span class="class-name">Arc</span></h3>
<p><em>Inherits from: Drawable</em></p>
<p>Arc(center=None, radius=0, start_angle=0, end_angle=90, color=None, thickness=1, **kwargs)
An arc UI element for drawing curved line segments.
Args:
center (tuple, optional): Center position as (x, y). Default: (0, 0)
radius (float, optional): Arc radius in pixels. Default: 0
start_angle (float, optional): Starting angle in degrees. Default: 0
end_angle (float, optional): Ending angle in degrees. Default: 90
color (Color, optional): Arc color. Default: White
thickness (float, optional): Line thickness. Default: 1.0
Keyword Args:
click (callable): Click 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
Attributes:
center (Vector): Center position
radius (float): Arc radius
start_angle (float): Starting angle in degrees
end_angle (float): Ending angle in degrees
color (Color): Arc color
thickness (float): Line thickness
visible (bool): Visibility state
opacity (float): Opacity value
z_index (int): Rendering order
name (str): Element name
</p>
<h4>Methods:</h4>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">get_bounds() -> tuple</code></h5>
<p>Get the bounding rectangle of this drawable element.
Note:</p>
<p style='margin-left: 20px;'><span class='returns'>Returns:</span> tuple: (x, y, width, height) representing the element&#x27;s bounds The bounds are in screen coordinates and account for current position and size.</p>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">move(dx: float, dy: float) -> None</code></h5>
<p>Move the element by a relative offset.
Note:</p>
<div style='margin-left: 20px;'>
<div><span class='arg-name'>dx</span>: Horizontal offset in pixels</div>
<div><span class='arg-name'>dy</span>: Vertical offset in pixels</div>
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">resize(width: float, height: float) -> None</code></h5>
<p>Resize the element to new dimensions.
Note:</p>
<div style='margin-left: 20px;'>
<div><span class='arg-name'>width</span>: New width in pixels</div>
<div><span class='arg-name'>height</span>: New height in pixels</div>
</div>
</div>
</div>
<div class="method-section"> <div class="method-section">
<h3 id="Caption"><span class="class-name">Caption</span></h3> <h3 id="Caption"><span class="class-name">Caption</span></h3>
<p><em>Inherits from: Drawable</em></p> <p><em>Inherits from: Drawable</em></p>
@ -577,71 +462,6 @@ Note:</p>
<h5><code class="method-name">resize(width: float, height: float) -> None</code></h5> <h5><code class="method-name">resize(width: float, height: float) -> None</code></h5>
<p>Resize the element to new dimensions. <p>Resize the element to new dimensions.
Note:</p>
<div style='margin-left: 20px;'>
<div><span class='arg-name'>width</span>: New width in pixels</div>
<div><span class='arg-name'>height</span>: New height in pixels</div>
</div>
</div>
</div>
<div class="method-section">
<h3 id="Circle"><span class="class-name">Circle</span></h3>
<p><em>Inherits from: Drawable</em></p>
<p>Circle(radius=0, center=None, fill_color=None, outline_color=None, outline=0, **kwargs)
A circle UI element for drawing filled or outlined circles.
Args:
radius (float, optional): Circle radius in pixels. Default: 0
center (tuple, optional): Center position as (x, y). Default: (0, 0)
fill_color (Color, optional): Fill color. Default: White
outline_color (Color, optional): Outline color. Default: Transparent
outline (float, optional): Outline thickness. Default: 0 (no outline)
Keyword Args:
click (callable): Click 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
Attributes:
radius (float): Circle radius
center (Vector): Center position
fill_color (Color): Fill color
outline_color (Color): Outline color
outline (float): Outline thickness
visible (bool): Visibility state
opacity (float): Opacity value
z_index (int): Rendering order
name (str): Element name
</p>
<h4>Methods:</h4>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">get_bounds() -> tuple</code></h5>
<p>Get the bounding rectangle of this drawable element.
Note:</p>
<p style='margin-left: 20px;'><span class='returns'>Returns:</span> tuple: (x, y, width, height) representing the element&#x27;s bounds The bounds are in screen coordinates and account for current position and size.</p>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">move(dx: float, dy: float) -> None</code></h5>
<p>Move the element by a relative offset.
Note:</p>
<div style='margin-left: 20px;'>
<div><span class='arg-name'>dx</span>: Horizontal offset in pixels</div>
<div><span class='arg-name'>dy</span>: Vertical offset in pixels</div>
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">resize(width: float, height: float) -> None</code></h5>
<p>Resize the element to new dimensions.
Note:</p> Note:</p>
<div style='margin-left: 20px;'> <div style='margin-left: 20px;'>
<div><span class='arg-name'>width</span>: New width in pixels</div> <div><span class='arg-name'>width</span>: New width in pixels</div>
@ -688,43 +508,6 @@ Note:</p>
</div> </div>
</div> </div>
<div class="method-section">
<h3 id="ColorLayer"><span class="class-name">ColorLayer</span></h3>
<p>ColorLayer(z_index=-1, grid_size=None)
A grid layer that stores RGBA colors per cell.
Args:
z_index (int): Render order. Negative = below entities. Default: -1
grid_size (tuple): Dimensions as (width, height). Default: parent grid size
Attributes:
z_index (int): Layer z-order relative to entities
visible (bool): Whether layer is rendered
grid_size (tuple): Layer dimensions (read-only)
Methods:
at(x, y): Get color at cell position
set(x, y, color): Set color at cell position
fill(color): Fill entire layer with color</p>
<h4>Methods:</h4>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">at(x, y) -> Color</code></h5>
<p>Get the color at cell position (x, y).</p>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">fill(color)</code></h5>
<p>Fill the entire layer with the specified color.</p>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">set(x, y, color)</code></h5>
<p>Set the color at cell position (x, y).</p>
</div>
</div>
<div class="method-section"> <div class="method-section">
<h3 id="Drawable"><span class="class-name">Drawable</span></h3> <h3 id="Drawable"><span class="class-name">Drawable</span></h3>
<p>Base class for all drawable UI elements</p> <p>Base class for all drawable UI elements</p>
@ -860,44 +643,23 @@ when the entity moves if it has a grid with perspective set.</p>
<h4>Methods:</h4> <h4>Methods:</h4>
<div style="margin-left: 20px; margin-bottom: 15px;"> <div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">append(entity)</code></h5> <h5><code class="method-name">append(...)</code></h5>
<p>Add an entity to the end of the collection.</p>
</div> </div>
<div style="margin-left: 20px; margin-bottom: 15px;"> <div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">count(entity) -> int</code></h5> <h5><code class="method-name">count(...)</code></h5>
<p>Count occurrences of entity in the collection.</p>
</div> </div>
<div style="margin-left: 20px; margin-bottom: 15px;"> <div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">extend(iterable)</code></h5> <h5><code class="method-name">extend(...)</code></h5>
<p>Add all entities from an iterable to the collection.</p>
</div> </div>
<div style="margin-left: 20px; margin-bottom: 15px;"> <div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">find(name) -> entity or list</code></h5> <h5><code class="method-name">index(...)</code></h5>
<p>Find entities by name.</p>
<p style='margin-left: 20px;'><span class='returns'>Returns:</span> Single entity if exact match, list if wildcard, None if not found.</p>
</div> </div>
<div style="margin-left: 20px; margin-bottom: 15px;"> <div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">index(entity) -> int</code></h5> <h5><code class="method-name">remove(...)</code></h5>
<p>Return index of first occurrence of entity. Raises ValueError if not found.</p>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">insert(index, entity)</code></h5>
<p>Insert entity at index. Like list.insert(), indices past the end append.</p>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">pop([index]) -> entity</code></h5>
<p>Remove and return entity at index (default: last entity).</p>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">remove(entity)</code></h5>
<p>Remove first occurrence of entity. Raises ValueError if not found.</p>
</div> </div>
</div> </div>
@ -933,7 +695,6 @@ Keyword Args:
w (float): Width override. Default: 0 w (float): Width override. Default: 0
h (float): Height override. Default: 0 h (float): Height override. Default: 0
clip_children (bool): Whether to clip children to frame bounds. Default: False clip_children (bool): Whether to clip children to frame bounds. Default: False
cache_subtree (bool): Cache rendering to texture for performance. Default: False
Attributes: Attributes:
x, y (float): Position in pixels x, y (float): Position in pixels
@ -947,8 +708,7 @@ Attributes:
opacity (float): Opacity value opacity (float): Opacity value
z_index (int): Rendering order z_index (int): Rendering order
name (str): Element name name (str): Element name
clip_children (bool): Whether to clip children to frame bounds clip_children (bool): Whether to clip children to frame bounds</p>
cache_subtree (bool): Cache subtree rendering to texture</p>
<h4>Methods:</h4> <h4>Methods:</h4>
<div style="margin-left: 20px; margin-bottom: 15px;"> <div style="margin-left: 20px; margin-bottom: 15px;">
@ -1034,17 +794,6 @@ Attributes:
name (str): Element name</p> name (str): Element name</p>
<h4>Methods:</h4> <h4>Methods:</h4>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">add_layer(type: str, z_index: int = -1, texture: Texture = None) -> ColorLayer | TileLayer</code></h5>
<p>Add a new layer to the grid.</p>
<div style='margin-left: 20px;'>
<div><span class='arg-name'>type</span>: Layer type (&#x27;color&#x27; or &#x27;tile&#x27;)</div>
<div><span class='arg-name'>z_index</span>: Render order. Negative = below entities, &gt;= 0 = above entities. Default: -1</div>
<div><span class='arg-name'>texture</span>: Texture for tile layers. Required for &#x27;tile&#x27; type.</div>
</div>
<p style='margin-left: 20px;'><span class='returns'>Returns:</span> The created ColorLayer or TileLayer object.</p>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;"> <div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">at(...)</code></h5> <h5><code class="method-name">at(...)</code></h5>
</div> </div>
@ -1073,8 +822,8 @@ Attributes:
</div> </div>
<div style="margin-left: 20px; margin-bottom: 15px;"> <div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">compute_fov(x: int, y: int, radius: int = 0, light_walls: bool = True, algorithm: int = FOV_BASIC) -> None</code></h5> <h5><code class="method-name">compute_fov(x: int, y: int, radius: int = 0, light_walls: bool = True, algorithm: int = FOV_BASIC) -> List[Tuple[int, int, bool, bool]]</code></h5>
<p>Compute field of view from a position.</p> <p>Compute field of view from a position and return visible cells.</p>
<div style='margin-left: 20px;'> <div style='margin-left: 20px;'>
<div><span class='arg-name'>x</span>: X coordinate of the viewer</div> <div><span class='arg-name'>x</span>: X coordinate of the viewer</div>
<div><span class='arg-name'>y</span>: Y coordinate of the viewer</div> <div><span class='arg-name'>y</span>: Y coordinate of the viewer</div>
@ -1082,6 +831,7 @@ Attributes:
<div><span class='arg-name'>light_walls</span>: Whether walls are lit when visible</div> <div><span class='arg-name'>light_walls</span>: Whether walls are lit when visible</div>
<div><span class='arg-name'>algorithm</span>: FOV algorithm to use (FOV_BASIC, FOV_DIAMOND, FOV_SHADOW, FOV_PERMISSIVE_0-8)</div> <div><span class='arg-name'>algorithm</span>: FOV algorithm to use (FOV_BASIC, FOV_DIAMOND, FOV_SHADOW, FOV_PERMISSIVE_0-8)</div>
</div> </div>
<p style='margin-left: 20px;'><span class='returns'>Returns:</span> 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().</p>
</div> </div>
<div style="margin-left: 20px; margin-bottom: 15px;"> <div style="margin-left: 20px; margin-bottom: 15px;">
@ -1135,15 +885,6 @@ Note:</p>
<p style='margin-left: 20px;'><span class='returns'>Returns:</span> True if the cell is visible, False otherwise Must call compute_fov() first to calculate visibility.</p> <p style='margin-left: 20px;'><span class='returns'>Returns:</span> True if the cell is visible, False otherwise Must call compute_fov() first to calculate visibility.</p>
</div> </div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">layer(z_index: int) -> ColorLayer | TileLayer | None</code></h5>
<p>Get a layer by its z_index.</p>
<div style='margin-left: 20px;'>
<div><span class='arg-name'>z_index</span>: The z_index of the layer to find.</div>
</div>
<p style='margin-left: 20px;'><span class='returns'>Returns:</span> The layer with the specified z_index, or None if not found.</p>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;"> <div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">move(dx: float, dy: float) -> None</code></h5> <h5><code class="method-name">move(dx: float, dy: float) -> None</code></h5>
<p>Move the element by a relative offset. <p>Move the element by a relative offset.
@ -1155,14 +896,6 @@ Note:</p>
</div> </div>
</div> </div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">remove_layer(layer: ColorLayer | TileLayer) -> None</code></h5>
<p>Remove a layer from the grid.</p>
<div style='margin-left: 20px;'>
<div><span class='arg-name'>layer</span>: The layer to remove.</div>
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;"> <div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">resize(width: float, height: float) -> None</code></h5> <h5><code class="method-name">resize(width: float, height: float) -> None</code></h5>
<p>Resize the element to new dimensions. <p>Resize the element to new dimensions.
@ -1187,69 +920,6 @@ Note:</p>
<h4>Methods:</h4> <h4>Methods:</h4>
</div> </div>
<div class="method-section">
<h3 id="Line"><span class="class-name">Line</span></h3>
<p><em>Inherits from: Drawable</em></p>
<p>Line(start=None, end=None, thickness=1.0, color=None, **kwargs)
A line UI element for drawing straight lines between two points.
Args:
start (tuple, optional): Starting point as (x, y). Default: (0, 0)
end (tuple, optional): Ending point as (x, y). Default: (0, 0)
thickness (float, optional): Line thickness in pixels. Default: 1.0
color (Color, optional): Line color. Default: White
Keyword Args:
click (callable): Click 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
Attributes:
start (Vector): Starting point
end (Vector): Ending point
thickness (float): Line thickness
color (Color): Line color
visible (bool): Visibility state
opacity (float): Opacity value
z_index (int): Rendering order
name (str): Element name
</p>
<h4>Methods:</h4>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">get_bounds() -> tuple</code></h5>
<p>Get the bounding rectangle of this drawable element.
Note:</p>
<p style='margin-left: 20px;'><span class='returns'>Returns:</span> tuple: (x, y, width, height) representing the element&#x27;s bounds The bounds are in screen coordinates and account for current position and size.</p>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">move(dx: float, dy: float) -> None</code></h5>
<p>Move the element by a relative offset.
Note:</p>
<div style='margin-left: 20px;'>
<div><span class='arg-name'>dx</span>: Horizontal offset in pixels</div>
<div><span class='arg-name'>dy</span>: Vertical offset in pixels</div>
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">resize(width: float, height: float) -> None</code></h5>
<p>Resize the element to new dimensions.
Note:</p>
<div style='margin-left: 20px;'>
<div><span class='arg-name'>width</span>: New width in pixels</div>
<div><span class='arg-name'>height</span>: New height in pixels</div>
</div>
</div>
</div>
<div class="method-section"> <div class="method-section">
<h3 id="Scene"><span class="class-name">Scene</span></h3> <h3 id="Scene"><span class="class-name">Scene</span></h3>
<p>Base class for object-oriented scenes</p> <p>Base class for object-oriented scenes</p>
@ -1359,45 +1029,6 @@ Note:</p>
<h4>Methods:</h4> <h4>Methods:</h4>
</div> </div>
<div class="method-section">
<h3 id="TileLayer"><span class="class-name">TileLayer</span></h3>
<p>TileLayer(z_index=-1, texture=None, grid_size=None)
A grid layer that stores sprite indices per cell.
Args:
z_index (int): Render order. Negative = below entities. Default: -1
texture (Texture): Sprite atlas for tile rendering. Default: None
grid_size (tuple): Dimensions as (width, height). Default: parent grid size
Attributes:
z_index (int): Layer z-order relative to entities
visible (bool): Whether layer is rendered
texture (Texture): Tile sprite atlas
grid_size (tuple): Layer dimensions (read-only)
Methods:
at(x, y): Get tile index at cell position
set(x, y, index): Set tile index at cell position
fill(index): Fill entire layer with tile index</p>
<h4>Methods:</h4>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">at(x, y) -> int</code></h5>
<p>Get the tile index at cell position (x, y). Returns -1 if no tile.</p>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">fill(index)</code></h5>
<p>Fill the entire layer with the specified tile index.</p>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">set(x, y, index)</code></h5>
<p>Set the tile index at cell position (x, y). Use -1 for no tile.</p>
</div>
</div>
<div class="method-section"> <div class="method-section">
<h3 id="Timer"><span class="class-name">Timer</span></h3> <h3 id="Timer"><span class="class-name">Timer</span></h3>
<p>Timer(name, callback, interval, once=False) <p>Timer(name, callback, interval, once=False)
@ -1475,50 +1106,23 @@ Note:</p>
<h4>Methods:</h4> <h4>Methods:</h4>
<div style="margin-left: 20px; margin-bottom: 15px;"> <div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">append(element)</code></h5> <h5><code class="method-name">append(...)</code></h5>
<p>Add an element to the end of the collection.</p>
</div> </div>
<div style="margin-left: 20px; margin-bottom: 15px;"> <div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">count(element) -> int</code></h5> <h5><code class="method-name">count(...)</code></h5>
<p>Count occurrences of element in the collection.</p>
</div> </div>
<div style="margin-left: 20px; margin-bottom: 15px;"> <div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">extend(iterable)</code></h5> <h5><code class="method-name">extend(...)</code></h5>
<p>Add all elements from an iterable to the collection.</p>
</div> </div>
<div style="margin-left: 20px; margin-bottom: 15px;"> <div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">find(name, recursive=False) -> element or list</code></h5> <h5><code class="method-name">index(...)</code></h5>
<p>Find elements by name.</p>
<p style='margin-left: 20px;'><span class='returns'>Returns:</span> Single element if exact match, list if wildcard, None if not found.</p>
</div> </div>
<div style="margin-left: 20px; margin-bottom: 15px;"> <div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">index(element) -> int</code></h5> <h5><code class="method-name">remove(...)</code></h5>
<p>Return index of first occurrence of element. Raises ValueError if not found.</p>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">insert(index, element)</code></h5>
<p>Insert element at index. Like list.insert(), indices past the end append.
Note: If using z_index for sorting, insertion order may not persist after
the next render. Use name-based .find() for stable element access.</p>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">pop([index]) -> element</code></h5>
<p>Remove and return element at index (default: last element).
Note: If using z_index for sorting, indices may shift after render.
Use name-based .find() for stable element access.</p>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">remove(element)</code></h5>
<p>Remove first occurrence of element. Raises ValueError if not found.</p>
</div> </div>
</div> </div>
@ -1569,14 +1173,6 @@ Use name-based .find() for stable element access.</p>
<p style='margin-left: 20px;'><span class='returns'>Returns:</span> float: Dot product of the two vectors</p> <p style='margin-left: 20px;'><span class='returns'>Returns:</span> float: Dot product of the two vectors</p>
</div> </div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">floor() -> Vector</code></h5>
<p>Return a new vector with floored (integer) coordinates.
Note:</p>
<p style='margin-left: 20px;'><span class='returns'>Returns:</span> Vector: New Vector with floor(x) and floor(y) Useful for grid-based positioning. For a hashable tuple, use the .int property instead.</p>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;"> <div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method-name">magnitude() -> float</code></h5> <h5><code class="method-name">magnitude() -> float</code></h5>
<p>Calculate the length/magnitude of this vector.</p> <p>Calculate the length/magnitude of this vector.</p>

View file

@ -14,11 +14,11 @@
. ftr VB CB . ftr VB CB
. ftr VBI CBI . ftr VBI CBI
.\} .\}
.TH "MCRFPY" "3" "2025-11-29" "McRogueFace dev" "" .TH "MCRFPY" "3" "2025-10-30" "McRogueFace dev" ""
.hy .hy
.SH McRogueFace API Reference .SH McRogueFace API Reference
.PP .PP
\f[I]Generated on 2025-11-29 10:12:05\f[R] \f[I]Generated on 2025-10-30 20:49:34\f[R]
.PP .PP
\f[I]This documentation was dynamically generated from the compiled \f[I]This documentation was dynamically generated from the compiled
module.\f[R] module.\f[R]
@ -31,16 +31,10 @@ Classes
.IP \[bu] 2 .IP \[bu] 2
Animation Animation
.IP \[bu] 2 .IP \[bu] 2
Arc
.IP \[bu] 2
Caption Caption
.IP \[bu] 2 .IP \[bu] 2
Circle
.IP \[bu] 2
Color Color
.IP \[bu] 2 .IP \[bu] 2
ColorLayer
.IP \[bu] 2
Drawable Drawable
.IP \[bu] 2 .IP \[bu] 2
Entity Entity
@ -57,16 +51,12 @@ GridPoint
.IP \[bu] 2 .IP \[bu] 2
GridPointState GridPointState
.IP \[bu] 2 .IP \[bu] 2
Line
.IP \[bu] 2
Scene Scene
.IP \[bu] 2 .IP \[bu] 2
Sprite Sprite
.IP \[bu] 2 .IP \[bu] 2
Texture Texture
.IP \[bu] 2 .IP \[bu] 2
TileLayer
.IP \[bu] 2
Timer Timer
.IP \[bu] 2 .IP \[bu] 2
UICollection UICollection
@ -120,17 +110,6 @@ Note:
.PP .PP
\f[B]Returns:\f[R] None No error is raised if the timer doesn\[cq]t \f[B]Returns:\f[R] None No error is raised if the timer doesn\[cq]t
exist. exist.
.SS \f[V]end_benchmark() -> str\f[R]
.PP
Stop benchmark capture and write data to JSON file.
.PP
Note:
.PP
\f[B]Returns:\f[R] str: The filename of the written benchmark data
.PP
\f[B]Raises:\f[R] RuntimeError: If no benchmark is currently running
Returns the auto-generated filename (e.g.,
`benchmark_12345_20250528_143022.json')
.SS \f[V]exit() -> None\f[R] .SS \f[V]exit() -> None\f[R]
.PP .PP
Cleanly shut down the game engine and exit the application. Cleanly shut down the game engine and exit the application.
@ -201,19 +180,6 @@ OGG, FLAC)
.PP .PP
\f[B]Returns:\f[R] None Only one music track can play at a time. \f[B]Returns:\f[R] None Only one music track can play at a time.
Loading new music stops the current track. Loading new music stops the current track.
.SS \f[V]log_benchmark(message: str) -> None\f[R]
.PP
Add a log message to the current benchmark frame.
.PP
Note:
.PP
\f[B]Arguments:\f[R] - \f[V]message\f[R]: Text to associate with the
current frame
.PP
\f[B]Returns:\f[R] None
.PP
\f[B]Raises:\f[R] RuntimeError: If no benchmark is currently running
Messages appear in the `logs' array of each frame in the output JSON.
.SS \f[V]playSound(buffer_id: int) -> None\f[R] .SS \f[V]playSound(buffer_id: int) -> None\f[R]
.PP .PP
Play a sound effect using a previously loaded buffer. Play a sound effect using a previously loaded buffer.
@ -235,18 +201,6 @@ If None, uses current scene
in the scene in the scene
.PP .PP
\f[B]Raises:\f[R] KeyError: If the specified scene doesn\[cq]t exist \f[B]Raises:\f[R] KeyError: If the specified scene doesn\[cq]t exist
.SS \f[V]setDevConsole(enabled: bool) -> None\f[R]
.PP
Enable or disable the developer console overlay.
.PP
Note:
.PP
\f[B]Arguments:\f[R] - \f[V]enabled\f[R]: True to enable the console
(default), False to disable
.PP
\f[B]Returns:\f[R] None When disabled, the grave/tilde key will not open
the console.
Use this to ship games without debug features.
.SS \f[V]setMusicVolume(volume: int) -> None\f[R] .SS \f[V]setMusicVolume(volume: int) -> None\f[R]
.PP .PP
Set the global music volume. Set the global music volume.
@ -301,17 +255,6 @@ Note:
\f[B]Returns:\f[R] None If a timer with this name exists, it will be \f[B]Returns:\f[R] None If a timer with this name exists, it will be
replaced. replaced.
The handler receives the total runtime in seconds as its argument. The handler receives the total runtime in seconds as its argument.
.SS \f[V]start_benchmark() -> None\f[R]
.PP
Start capturing benchmark data to a file.
.PP
Note:
.PP
\f[B]Returns:\f[R] None
.PP
\f[B]Raises:\f[R] RuntimeError: If a benchmark is already running
Benchmark filename is auto-generated from PID and timestamp.
Use end_benchmark() to stop and get filename.
.SS Classes .SS Classes
.SS Animation .SS Animation
.PP .PP
@ -373,62 +316,6 @@ update in seconds
\f[B]Returns:\f[R] bool: True if animation is still running, False if \f[B]Returns:\f[R] bool: True if animation is still running, False if
complete Typically called by AnimationManager automatically. complete Typically called by AnimationManager automatically.
Manual calls only needed for custom animation control. Manual calls only needed for custom animation control.
.SS Arc
.PP
\f[I]Inherits from: Drawable\f[R]
.PP
Arc(center=None, radius=0, start_angle=0, end_angle=90, color=None,
thickness=1, **kwargs)
.PP
An arc UI element for drawing curved line segments.
.PP
Args: center (tuple, optional): Center position as (x, y).
Default: (0, 0) radius (float, optional): Arc radius in pixels.
Default: 0 start_angle (float, optional): Starting angle in degrees.
Default: 0 end_angle (float, optional): Ending angle in degrees.
Default: 90 color (Color, optional): Arc color.
Default: White thickness (float, optional): Line thickness.
Default: 1.0
.PP
Keyword Args: click (callable): Click 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
.PP
Attributes: center (Vector): Center position radius (float): Arc radius
start_angle (float): Starting angle in degrees end_angle (float): Ending
angle in degrees color (Color): Arc color thickness (float): Line
thickness 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]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 Caption .SS Caption
.PP .PP
\f[I]Inherits from: Drawable\f[R] \f[I]Inherits from: Drawable\f[R]
@ -491,61 +378,6 @@ Note:
.PP .PP
\f[B]Arguments:\f[R] - \f[V]width\f[R]: New width in pixels - \f[B]Arguments:\f[R] - \f[V]width\f[R]: New width in pixels -
\f[V]height\f[R]: New height in pixels \f[V]height\f[R]: New height in pixels
.SS Circle
.PP
\f[I]Inherits from: Drawable\f[R]
.PP
Circle(radius=0, center=None, fill_color=None, outline_color=None,
outline=0, **kwargs)
.PP
A circle UI element for drawing filled or outlined circles.
.PP
Args: radius (float, optional): Circle radius in pixels.
Default: 0 center (tuple, optional): Center position as (x, y).
Default: (0, 0) fill_color (Color, optional): Fill color.
Default: White outline_color (Color, optional): Outline color.
Default: Transparent outline (float, optional): Outline thickness.
Default: 0 (no outline)
.PP
Keyword Args: click (callable): Click 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
.PP
Attributes: radius (float): Circle radius center (Vector): Center
position fill_color (Color): Fill color outline_color (Color): Outline
color outline (float): Outline thickness 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]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 .SS Color
.PP .PP
SFML Color Object SFML Color Object
@ -587,34 +419,6 @@ Note:
\f[B]Returns:\f[R] str: Hex string in format `#RRGGBB' or `#RRGGBBAA' \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 (< (if alpha < 255) Alpha component is only included if not fully opaque (<
255) 255)
.SS ColorLayer
.PP
ColorLayer(z_index=-1, grid_size=None)
.PP
A grid layer that stores RGBA colors per cell.
.PP
Args: z_index (int): Render order.
Negative = below entities.
Default: -1 grid_size (tuple): Dimensions as (width, height).
Default: parent grid size
.PP
Attributes: z_index (int): Layer z-order relative to entities visible
(bool): Whether layer is rendered grid_size (tuple): Layer dimensions
(read-only)
.PP
Methods: at(x, y): Get color at cell position set(x, y, color): Set
color at cell position fill(color): Fill entire layer with color
.PP
\f[B]Methods:\f[R]
.SS \f[V]at(x, y) -> Color\f[R]
.PP
Get the color at cell position (x, y).
.SS \f[V]fill(color)\f[R]
.PP
Fill the entire layer with the specified color.
.SS \f[V]set(x, y, color)\f[R]
.PP
Set the color at cell position (x, y).
.SS Drawable .SS Drawable
.PP .PP
Base class for all drawable UI elements Base class for all drawable UI elements
@ -727,36 +531,11 @@ perspective set.
Iterable, indexable collection of Entities Iterable, indexable collection of Entities
.PP .PP
\f[B]Methods:\f[R] \f[B]Methods:\f[R]
.SS \f[V]append(entity)\f[R] .SS \f[V]append(...)\f[R]
.PP .SS \f[V]count(...)\f[R]
Add an entity to the end of the collection. .SS \f[V]extend(...)\f[R]
.SS \f[V]count(entity) -> int\f[R] .SS \f[V]index(...)\f[R]
.PP .SS \f[V]remove(...)\f[R]
Count occurrences of entity in the collection.
.SS \f[V]extend(iterable)\f[R]
.PP
Add all entities from an iterable to the collection.
.SS \f[V]find(name) -> entity or list\f[R]
.PP
Find entities by name.
.PP
\f[B]Returns:\f[R] Single entity if exact match, list if wildcard, None
if not found.
.SS \f[V]index(entity) -> int\f[R]
.PP
Return index of first occurrence of entity.
Raises ValueError if not found.
.SS \f[V]insert(index, entity)\f[R]
.PP
Insert entity at index.
Like list.insert(), indices past the end append.
.SS \f[V]pop([index]) -> entity\f[R]
.PP
Remove and return entity at index (default: last entity).
.SS \f[V]remove(entity)\f[R]
.PP
Remove first occurrence of entity.
Raises ValueError if not found.
.SS Font .SS Font
.PP .PP
SFML Font Object SFML Font Object
@ -789,8 +568,6 @@ Default: 0 w (float): Width override.
Default: 0 h (float): Height override. Default: 0 h (float): Height override.
Default: 0 clip_children (bool): Whether to clip children to frame Default: 0 clip_children (bool): Whether to clip children to frame
bounds. bounds.
Default: False cache_subtree (bool): Cache rendering to texture for
performance.
Default: False Default: False
.PP .PP
Attributes: x, y (float): Position in pixels w, h (float): Size in Attributes: x, y (float): Position in pixels w, h (float): Size in
@ -800,7 +577,7 @@ thickness click (callable): Click event handler children (list):
Collection of child drawable elements visible (bool): Visibility state Collection of child drawable elements visible (bool): Visibility state
opacity (float): Opacity value z_index (int): Rendering order name opacity (float): Opacity value z_index (int): Rendering order name
(str): Element name clip_children (bool): Whether to clip children to (str): Element name clip_children (bool): Whether to clip children to
frame bounds cache_subtree (bool): Cache subtree rendering to texture frame bounds
.PP .PP
\f[B]Methods:\f[R] \f[B]Methods:\f[R]
.SS \f[V]get_bounds() -> tuple\f[R] .SS \f[V]get_bounds() -> tuple\f[R]
@ -876,17 +653,6 @@ visible (bool): Visibility state opacity (float): Opacity value z_index
(int): Rendering order name (str): Element name (int): Rendering order name (str): Element name
.PP .PP
\f[B]Methods:\f[R] \f[B]Methods:\f[R]
.SS \f[V]add_layer(type: str, z_index: int = -1, texture: Texture = None) -> ColorLayer | TileLayer\f[R]
.PP
Add a new layer to the grid.
.PP
\f[B]Arguments:\f[R] - \f[V]type\f[R]: Layer type (`color' or `tile') -
\f[V]z_index\f[R]: Render order.
Negative = below entities, >= 0 = above entities.
Default: -1 - \f[V]texture\f[R]: Texture for tile layers.
Required for `tile' type.
.PP
\f[B]Returns:\f[R] The created ColorLayer or TileLayer object.
.SS \f[V]at(...)\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] .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 .PP
@ -907,15 +673,20 @@ Compute Dijkstra map from root position.
\f[B]Arguments:\f[R] - \f[V]root_x\f[R]: X coordinate of the root/target \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]root_y\f[R]: Y coordinate of the root/target -
\f[V]diagonal_cost\f[R]: Cost of diagonal movement (default: 1.41) \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) -> None\f[R] .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 .PP
Compute field of view from a position. Compute field of view from a position and return visible cells.
.PP .PP
\f[B]Arguments:\f[R] - \f[V]x\f[R]: X coordinate of the viewer - \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 \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 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, when visible - \f[V]algorithm\f[R]: FOV algorithm to use (FOV_BASIC,
FOV_DIAMOND, FOV_SHADOW, FOV_PERMISSIVE_0-8) 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] .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 .PP
Find A* path between two points. Find A* path between two points.
@ -965,15 +736,6 @@ Y coordinate to check
.PP .PP
\f[B]Returns:\f[R] True if the cell is visible, False otherwise Must \f[B]Returns:\f[R] True if the cell is visible, False otherwise Must
call compute_fov() first to calculate visibility. call compute_fov() first to calculate visibility.
.SS \f[V]layer(z_index: int) -> ColorLayer | TileLayer | None\f[R]
.PP
Get a layer by its z_index.
.PP
\f[B]Arguments:\f[R] - \f[V]z_index\f[R]: The z_index of the layer to
find.
.PP
\f[B]Returns:\f[R] The layer with the specified z_index, or None if not
found.
.SS \f[V]move(dx: float, dy: float) -> None\f[R] .SS \f[V]move(dx: float, dy: float) -> None\f[R]
.PP .PP
Move the element by a relative offset. Move the element by a relative offset.
@ -982,11 +744,6 @@ Note:
.PP .PP
\f[B]Arguments:\f[R] - \f[V]dx\f[R]: Horizontal offset in pixels - \f[B]Arguments:\f[R] - \f[V]dx\f[R]: Horizontal offset in pixels -
\f[V]dy\f[R]: Vertical offset in pixels \f[V]dy\f[R]: Vertical offset in pixels
.SS \f[V]remove_layer(layer: ColorLayer | TileLayer) -> None\f[R]
.PP
Remove a layer from the grid.
.PP
\f[B]Arguments:\f[R] - \f[V]layer\f[R]: The layer to remove.
.SS \f[V]resize(width: float, height: float) -> None\f[R] .SS \f[V]resize(width: float, height: float) -> None\f[R]
.PP .PP
Resize the element to new dimensions. Resize the element to new dimensions.
@ -1005,58 +762,6 @@ UIGridPoint object
UIGridPointState object UIGridPointState object
.PP .PP
\f[B]Methods:\f[R] \f[B]Methods:\f[R]
.SS Line
.PP
\f[I]Inherits from: Drawable\f[R]
.PP
Line(start=None, end=None, thickness=1.0, color=None, **kwargs)
.PP
A line UI element for drawing straight lines between two points.
.PP
Args: start (tuple, optional): Starting point as (x, y).
Default: (0, 0) end (tuple, optional): Ending point as (x, y).
Default: (0, 0) thickness (float, optional): Line thickness in pixels.
Default: 1.0 color (Color, optional): Line color.
Default: White
.PP
Keyword Args: click (callable): Click 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
.PP
Attributes: start (Vector): Starting point end (Vector): Ending point
thickness (float): Line thickness color (Color): Line color 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]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 Scene .SS Scene
.PP .PP
Base class for object-oriented scenes Base class for object-oriented scenes
@ -1159,38 +864,6 @@ Note:
SFML Texture Object SFML Texture Object
.PP .PP
\f[B]Methods:\f[R] \f[B]Methods:\f[R]
.SS TileLayer
.PP
TileLayer(z_index=-1, texture=None, grid_size=None)
.PP
A grid layer that stores sprite indices per cell.
.PP
Args: z_index (int): Render order.
Negative = below entities.
Default: -1 texture (Texture): Sprite atlas for tile rendering.
Default: None grid_size (tuple): Dimensions as (width, height).
Default: parent grid size
.PP
Attributes: z_index (int): Layer z-order relative to entities visible
(bool): Whether layer is rendered texture (Texture): Tile sprite atlas
grid_size (tuple): Layer dimensions (read-only)
.PP
Methods: at(x, y): Get tile index at cell position set(x, y, index): Set
tile index at cell position fill(index): Fill entire layer with tile
index
.PP
\f[B]Methods:\f[R]
.SS \f[V]at(x, y) -> int\f[R]
.PP
Get the tile index at cell position (x, y).
Returns -1 if no tile.
.SS \f[V]fill(index)\f[R]
.PP
Fill the entire layer with the specified tile index.
.SS \f[V]set(x, y, index)\f[R]
.PP
Set the tile index at cell position (x, y).
Use -1 for no tile.
.SS Timer .SS Timer
.PP .PP
Timer(name, callback, interval, once=False) Timer(name, callback, interval, once=False)
@ -1264,43 +937,11 @@ Timer will fire after the remaining time elapses.
Iterable, indexable collection of UI objects Iterable, indexable collection of UI objects
.PP .PP
\f[B]Methods:\f[R] \f[B]Methods:\f[R]
.SS \f[V]append(element)\f[R] .SS \f[V]append(...)\f[R]
.PP .SS \f[V]count(...)\f[R]
Add an element to the end of the collection. .SS \f[V]extend(...)\f[R]
.SS \f[V]count(element) -> int\f[R] .SS \f[V]index(...)\f[R]
.PP .SS \f[V]remove(...)\f[R]
Count occurrences of element in the collection.
.SS \f[V]extend(iterable)\f[R]
.PP
Add all elements from an iterable to the collection.
.SS \f[V]find(name, recursive=False) -> element or list\f[R]
.PP
Find elements by name.
.PP
\f[B]Returns:\f[R] Single element if exact match, list if wildcard, None
if not found.
.SS \f[V]index(element) -> int\f[R]
.PP
Return index of first occurrence of element.
Raises ValueError if not found.
.SS \f[V]insert(index, element)\f[R]
.PP
Insert element at index.
Like list.insert(), indices past the end append.
.PP
Note: If using z_index for sorting, insertion order may not persist
after the next render.
Use name-based .find() for stable element access.
.SS \f[V]pop([index]) -> element\f[R]
.PP
Remove and return element at index (default: last element).
.PP
Note: If using z_index for sorting, indices may shift after render.
Use name-based .find() for stable element access.
.SS \f[V]remove(element)\f[R]
.PP
Remove first occurrence of element.
Raises ValueError if not found.
.SS UICollectionIter .SS UICollectionIter
.PP .PP
Iterator for a collection of UI objects Iterator for a collection of UI objects
@ -1340,15 +981,6 @@ Calculate the dot product with another vector.
\f[B]Arguments:\f[R] - \f[V]other\f[R]: The other vector \f[B]Arguments:\f[R] - \f[V]other\f[R]: The other vector
.PP .PP
\f[B]Returns:\f[R] float: Dot product of the two vectors \f[B]Returns:\f[R] float: Dot product of the two vectors
.SS \f[V]floor() -> Vector\f[R]
.PP
Return a new vector with floored (integer) coordinates.
.PP
Note:
.PP
\f[B]Returns:\f[R] Vector: New Vector with floor(x) and floor(y) Useful
for grid-based positioning.
For a hashable tuple, use the .int property instead.
.SS \f[V]magnitude() -> float\f[R] .SS \f[V]magnitude() -> float\f[R]
.PP .PP
Calculate the length/magnitude of this vector. Calculate the length/magnitude of this vector.

View file

@ -61,7 +61,7 @@ void PyScene::do_mouse_input(std::string button, std::string type)
void PyScene::doAction(std::string name, std::string type) void PyScene::doAction(std::string name, std::string type)
{ {
if (name.compare("left") == 0 || name.compare("right") == 0 || name.compare("wheel_up") == 0 || name.compare("wheel_down") == 0) { if (name.compare("left") == 0 || name.compare("rclick") == 0 || name.compare("wheel_up") == 0 || name.compare("wheel_down") == 0) {
do_mouse_input(name, type); do_mouse_input(name, type);
} }
else if ACTIONONCE("debug_menu") { else if ACTIONONCE("debug_menu") {

View file

@ -96,7 +96,7 @@ class Drawable:
... ...
class Frame(Drawable): class Frame(Drawable):
"""Frame(x=0, y=0, w=0, h=0, fill_color=None, outline_color=None, outline=0, on_click=None, children=None) """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. A rectangular frame UI element that can contain other drawable elements.
""" """
@ -106,7 +106,7 @@ class Frame(Drawable):
@overload @overload
def __init__(self, x: float = 0, y: float = 0, w: float = 0, h: float = 0, def __init__(self, x: float = 0, y: float = 0, w: float = 0, h: float = 0,
fill_color: Optional[Color] = None, outline_color: Optional[Color] = None, fill_color: Optional[Color] = None, outline_color: Optional[Color] = None,
outline: float = 0, on_click: Optional[Callable] = None, outline: float = 0, click: Optional[Callable] = None,
children: Optional[List[UIElement]] = None) -> None: ... children: Optional[List[UIElement]] = None) -> None: ...
w: float w: float
@ -114,15 +114,12 @@ class Frame(Drawable):
fill_color: Color fill_color: Color
outline_color: Color outline_color: Color
outline: float outline: float
on_click: Optional[Callable[[float, float, int], None]] click: Optional[Callable[[float, float, int], None]]
on_enter: Optional[Callable[[], None]]
on_exit: Optional[Callable[[], None]]
on_move: Optional[Callable[[float, float], None]]
children: 'UICollection' children: 'UICollection'
clip_children: bool clip_children: bool
class Caption(Drawable): class Caption(Drawable):
"""Caption(text='', x=0, y=0, font=None, fill_color=None, outline_color=None, outline=0, on_click=None) """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. A text display UI element with customizable font and styling.
""" """
@ -133,22 +130,19 @@ class Caption(Drawable):
def __init__(self, text: str = '', x: float = 0, y: float = 0, def __init__(self, text: str = '', x: float = 0, y: float = 0,
font: Optional[Font] = None, fill_color: Optional[Color] = None, font: Optional[Font] = None, fill_color: Optional[Color] = None,
outline_color: Optional[Color] = None, outline: float = 0, outline_color: Optional[Color] = None, outline: float = 0,
on_click: Optional[Callable] = None) -> None: ... click: Optional[Callable] = None) -> None: ...
text: str text: str
font: Font font: Font
fill_color: Color fill_color: Color
outline_color: Color outline_color: Color
outline: float outline: float
on_click: Optional[Callable[[float, float, int], None]] click: Optional[Callable[[float, float, int], None]]
on_enter: Optional[Callable[[], None]]
on_exit: Optional[Callable[[], None]]
on_move: Optional[Callable[[float, float], None]]
w: float # Read-only, computed from text w: float # Read-only, computed from text
h: float # Read-only, computed from text h: float # Read-only, computed from text
class Sprite(Drawable): class Sprite(Drawable):
"""Sprite(x=0, y=0, texture=None, sprite_index=0, scale=1.0, on_click=None) """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. A sprite UI element that displays a texture or portion of a texture atlas.
""" """
@ -158,97 +152,42 @@ class Sprite(Drawable):
@overload @overload
def __init__(self, x: float = 0, y: float = 0, texture: Optional[Texture] = None, def __init__(self, x: float = 0, y: float = 0, texture: Optional[Texture] = None,
sprite_index: int = 0, scale: float = 1.0, sprite_index: int = 0, scale: float = 1.0,
on_click: Optional[Callable] = None) -> None: ... click: Optional[Callable] = None) -> None: ...
texture: Texture texture: Texture
sprite_index: int sprite_index: int
scale: float scale: float
on_click: Optional[Callable[[float, float, int], None]] click: Optional[Callable[[float, float, int], None]]
on_enter: Optional[Callable[[], None]]
on_exit: Optional[Callable[[], None]]
on_move: Optional[Callable[[float, float], None]]
w: float # Read-only, computed from texture w: float # Read-only, computed from texture
h: float # Read-only, computed from texture h: float # Read-only, computed from texture
class Grid(Drawable): class Grid(Drawable):
"""Grid(pos=None, size=None, grid_size=(20, 20), texture=None, on_click=None, layers=None) """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. A grid-based tilemap UI element for rendering tile-based levels and game worlds.
Supports multiple rendering layers (ColorLayer, TileLayer) and entity management.
""" """
@overload @overload
def __init__(self) -> None: ... def __init__(self) -> None: ...
@overload @overload
def __init__(self, pos: Optional[Tuple[float, float]] = None, def __init__(self, x: float = 0, y: float = 0, grid_size: Tuple[int, int] = (20, 20),
size: Optional[Tuple[float, float]] = None, texture: Optional[Texture] = None, tile_width: int = 16, tile_height: int = 16,
grid_size: Tuple[int, int] = (20, 20), scale: float = 1.0, click: Optional[Callable] = None) -> None: ...
texture: Optional[Texture] = None,
on_click: Optional[Callable] = None,
layers: Optional[Dict[str, str]] = None) -> None: ...
grid_size: Tuple[int, int] grid_size: Tuple[int, int]
grid_x: int # Read-only grid width tile_width: int
grid_y: int # Read-only grid height tile_height: int
texture: Texture texture: Texture
zoom: float scale: float
center: Tuple[float, float] points: List[List['GridPoint']]
center_x: float
center_y: float
fill_color: Color
entities: 'EntityCollection' entities: 'EntityCollection'
children: 'UICollection' background_color: Color
layers: List[Union['ColorLayer', 'TileLayer']] click: Optional[Callable[[int, int, int], None]]
hovered_cell: Optional[Tuple[int, int]]
# Mouse event handlers
on_click: Optional[Callable[[float, float, int], None]]
on_enter: Optional[Callable[[], None]]
on_exit: Optional[Callable[[], None]]
on_move: Optional[Callable[[float, float], None]]
# Grid cell event handlers
on_cell_click: Optional[Callable[[int, int], None]]
on_cell_enter: Optional[Callable[[int, int], None]]
on_cell_exit: Optional[Callable[[int, int], None]]
def at(self, x: int, y: int) -> 'GridPoint': def at(self, x: int, y: int) -> 'GridPoint':
"""Get grid point at tile coordinates.""" """Get grid point at tile coordinates."""
... ...
def add_layer(self, type: str, z_index: int = -1,
texture: Optional[Texture] = None) -> Union['ColorLayer', 'TileLayer']:
"""Add a rendering layer. type='color' or 'tile'. z_index<0 = below entities."""
...
def remove_layer(self, layer: Union['ColorLayer', 'TileLayer']) -> None:
"""Remove a layer from the grid."""
...
def compute_fov(self, x: int, y: int, radius: int) -> None:
"""Compute field of view from a position."""
...
def is_in_fov(self, x: int, y: int) -> bool:
"""Check if a cell is visible in the current FOV."""
...
def compute_dijkstra(self, sources: List[Tuple[int, int]], max_cost: float = -1) -> None:
"""Compute Dijkstra distance map from source points."""
...
def get_dijkstra_distance(self, x: int, y: int) -> float:
"""Get distance to nearest source for a cell."""
...
def get_dijkstra_path(self, x: int, y: int) -> List[Tuple[int, int]]:
"""Get path from cell to nearest source."""
...
def find_path(self, x1: int, y1: int, x2: int, y2: int) -> List[Tuple[int, int]]:
"""Find A* path between two cells."""
...
class GridPoint: class GridPoint:
"""Grid point representing a single tile.""" """Grid point representing a single tile."""
@ -262,45 +201,6 @@ class GridPointState:
texture_index: int texture_index: int
color: Color color: Color
class ColorLayer:
"""Grid layer that renders solid colors per cell."""
z_index: int
visible: bool
grid_size: Tuple[int, int]
def fill(self, color: Color) -> None:
"""Fill all cells with a color."""
...
def set(self, x: int, y: int, color: Color) -> None:
"""Set color at a specific cell."""
...
def at(self, x: int, y: int) -> Color:
"""Get color at a specific cell."""
...
class TileLayer:
"""Grid layer that renders texture tiles per cell."""
z_index: int
visible: bool
texture: Texture
grid_size: Tuple[int, int]
def fill(self, sprite_index: int) -> None:
"""Fill all cells with a sprite index."""
...
def set(self, x: int, y: int, sprite_index: int) -> None:
"""Set tile sprite at a specific cell."""
...
def at(self, x: int, y: int) -> int:
"""Get tile sprite index at a specific cell."""
...
class Entity(Drawable): class Entity(Drawable):
"""Entity(grid_x=0, grid_y=0, texture=None, sprite_index=0, name='') """Entity(grid_x=0, grid_y=0, texture=None, sprite_index=0, name='')

View file

@ -8,33 +8,29 @@ class GridDemo(DemoScreen):
def setup(self): def setup(self):
self.add_title("Grid System") self.add_title("Grid System")
self.add_description("Multi-layer rendering with camera, zoom, and children support") self.add_description("Tile-based rendering with camera, zoom, and children support")
# Create a grid with no default layers # Create a grid
grid = mcrfpy.Grid(grid_size=(15, 10), pos=(50, 120), size=(400, 280), layers={}) grid = mcrfpy.Grid(grid_size=(15, 10), pos=(50, 120), size=(400, 280))
grid.fill_color = mcrfpy.Color(20, 20, 40) grid.fill_color = mcrfpy.Color(20, 20, 40)
# Add a color layer for the checkerboard pattern (z_index=-1 = below entities)
color_layer = grid.add_layer("color", z_index=-1)
# Center camera on middle of grid (in pixel coordinates: cells * cell_size / 2) # Center camera on middle of grid (in pixel coordinates: cells * cell_size / 2)
# For 15x10 grid with 16x16 cells: center = (15*16/2, 10*16/2) = (120, 80) # For 15x10 grid with 16x16 cells: center = (15*16/2, 10*16/2) = (120, 80)
grid.center = (120, 80) grid.center = (120, 80)
self.ui.append(grid) self.ui.append(grid)
# Set tile colors via the color layer to create a pattern # Set some tile colors to create a pattern
for x in range(15): for x in range(15):
for y in range(10): for y in range(10):
point = grid.at(x, y) point = grid.at(x, y)
# Checkerboard pattern # Checkerboard pattern
if (x + y) % 2 == 0: if (x + y) % 2 == 0:
color_layer.set(x, y, mcrfpy.Color(40, 40, 60)) point.color = mcrfpy.Color(40, 40, 60)
else: else:
color_layer.set(x, y, mcrfpy.Color(30, 30, 50)) point.color = mcrfpy.Color(30, 30, 50)
# Border # Border
if x == 0 or x == 14 or y == 0 or y == 9: if x == 0 or x == 14 or y == 0 or y == 9:
color_layer.set(x, y, mcrfpy.Color(80, 60, 40)) point.color = mcrfpy.Color(80, 60, 40)
point.walkable = False point.walkable = False
# Add some children to the grid # Add some children to the grid
@ -57,12 +53,13 @@ class GridDemo(DemoScreen):
props = [ props = [
"grid_size: (15, 10)", "grid_size: (15, 10)",
"layers: [ColorLayer]", "zoom: 1.0",
"center: (120, 80)", "center: (120, 80)",
"fill_color: dark blue",
"", "",
"Features:", "Features:",
"- Multi-layer rendering",
"- Camera pan/zoom", "- Camera pan/zoom",
"- Tile colors",
"- Children collection", "- Children collection",
"- FOV/pathfinding", "- FOV/pathfinding",
] ]
@ -72,9 +69,8 @@ class GridDemo(DemoScreen):
info.children.append(cap) info.children.append(cap)
# Code example # Code example
code = """# Grid with layers code = """# Grid with children
grid = mcrfpy.Grid(grid_size=(20, 15), pos=(50, 50), size=(320, 240), layers={}) grid = mcrfpy.Grid(grid_size=(20, 15), pos=(50, 50), size=(320, 240))
layer = grid.add_layer("color", z_index=-1) # Below entities grid.at(5, 5).color = mcrfpy.Color(255, 0, 0) # Red tile
layer.set(5, 5, mcrfpy.Color(255, 0, 0)) # Red tile
grid.children.append(mcrfpy.Caption("Label", pos=(80, 48)))""" grid.children.append(mcrfpy.Caption("Label", pos=(80, 48)))"""
self.add_code_example(code, y=420) self.add_code_example(code, y=420)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Before After
Before After

View file

@ -1,293 +0,0 @@
#!/usr/bin/env python3
"""
VLLM Integration Demo for McRogueFace
=====================================
Demonstrates using a local Vision-Language Model (Gemma 3) with
McRogueFace headless rendering to create an AI-driven agent.
Requirements:
- Local VLLM running at http://192.168.1.100:8100
- McRogueFace built with headless mode support
This is a research-grade demo for issue #156.
"""
import mcrfpy
from mcrfpy import automation
import sys
import requests
import base64
import os
import random
# VLLM configuration
VLLM_URL = "http://192.168.1.100:8100/v1/chat/completions"
SCREENSHOT_PATH = "/tmp/vllm_demo_screenshot.png"
# Sprite constants from Crypt of Sokoban tileset
FLOOR_COMMON = 0 # 95% of floors
FLOOR_SPECKLE1 = 12 # 4% of floors
FLOOR_SPECKLE2 = 24 # 1% of floors
WALL_TILE = 40 # Wall sprite
PLAYER_SPRITE = 84 # Player character
RAT_SPRITE = 123 # Enemy/rat creature
def file_to_base64(file_path):
"""Convert any image file to base64 string."""
with open(file_path, 'rb') as f:
return base64.b64encode(f.read()).decode('utf-8')
def llm_chat_completion(messages: list):
"""Chat completion endpoint of local LLM"""
try:
response = requests.post(VLLM_URL, json={'messages': messages}, timeout=60)
return response.json()
except requests.exceptions.RequestException as e:
return {"error": str(e)}
def message_with_image(text, image_path):
"""Create a message with an embedded image for vision models."""
image_data = file_to_base64(image_path)
return {
"role": "user",
"content": [
{"type": "text", "text": text},
{"type": "image_url", "image_url": {"url": "data:image/png;base64," + image_data}}
]
}
def get_floor_tile():
"""Return a floor tile sprite with realistic distribution."""
roll = random.random()
if roll < 0.95:
return FLOOR_COMMON
elif roll < 0.99:
return FLOOR_SPECKLE1
else:
return FLOOR_SPECKLE2
def setup_scene():
"""Create a dungeon scene with player agent and NPC rat."""
print("Setting up scene...")
# Create and set scene
mcrfpy.createScene("vllm_demo")
mcrfpy.setScene("vllm_demo")
ui = mcrfpy.sceneUI("vllm_demo")
# Load the game texture (16x16 tiles from Crypt of Sokoban)
texture = mcrfpy.Texture("assets/kenney_TD_MR_IP.png", 16, 16)
# Create grid: 1014px wide at position (5,5)
# Using 20x15 grid for a reasonable dungeon size
grid = mcrfpy.Grid(
grid_size=(20, 15),
texture=texture,
pos=(5, 5),
size=(1014, 700)
)
grid.fill_color = mcrfpy.Color(20, 20, 30)
# Set zoom factor to 2.0 for better visibility
grid.zoom = 2.0
ui.append(grid)
# Set up floor tiles and walls with proper sprite distribution
for x in range(20):
for y in range(15):
point = grid.at(x, y)
# Create walls around the edges
if x == 0 or x == 19 or y == 0 or y == 14:
point.tilesprite = WALL_TILE
point.walkable = False
point.transparent = False # Walls block FOV
else:
# Floor inside with varied sprites
point.tilesprite = get_floor_tile()
point.walkable = True
point.transparent = True # Floors don't block FOV
# Add some interior walls for interest - a room divider
for y in range(5, 10):
point = grid.at(10, y)
point.tilesprite = WALL_TILE
point.walkable = False
point.transparent = False
# Door opening
door = grid.at(10, 7)
door.tilesprite = get_floor_tile()
door.walkable = True
door.transparent = True
# Create a ColorLayer for fog of war (z_index=10 to render on top)
fov_layer = grid.add_layer('color', z_index=10)
fov_layer.fill(mcrfpy.Color(0, 0, 0, 255)) # Start all black (unknown)
# Create the player entity ("The Agent")
player = mcrfpy.Entity(grid_pos=(5, 7), texture=texture, sprite_index=PLAYER_SPRITE)
grid.entities.append(player)
# Create an NPC rat entity (closer so it's visible in FOV)
rat = mcrfpy.Entity(grid_pos=(10, 7), texture=texture, sprite_index=RAT_SPRITE)
grid.entities.append(rat)
# Bind the fog layer to player's perspective
# visible = transparent, discovered = dim, unknown = black
fov_layer.apply_perspective(
entity=player,
visible=mcrfpy.Color(0, 0, 0, 0), # Transparent when visible
discovered=mcrfpy.Color(40, 40, 60, 180), # Dark overlay when discovered but not visible
unknown=mcrfpy.Color(0, 0, 0, 255) # Black when never seen
)
# Update visibility from player's position
player.update_visibility()
# Center the camera on the agent entity
px, py = int(player.pos[0]), int(player.pos[1])
grid.center = (px * 16 + 8, py * 16 + 8)
return grid, player, rat
def check_entity_visible(grid, entity):
"""Check if an entity is within the current FOV."""
ex, ey = int(entity.pos[0]), int(entity.pos[1])
return grid.is_in_fov(ex, ey)
def build_grounded_prompt(grid, player, rat):
"""Build a text prompt with visually grounded information."""
observations = []
# Check what the agent can see
if check_entity_visible(grid, rat):
observations.append("You see a rat to the east.")
# Could add more observations here:
# - walls blocking path
# - items on ground
# - doors/exits
if not observations:
observations.append("The area appears clear.")
return " ".join(observations)
def run_demo():
"""Main demo function."""
print("=" * 60)
print("VLLM Integration Demo (Research Mode)")
print("=" * 60)
print()
# Setup the scene
grid, player, rat = setup_scene()
# Advance simulation to ensure scene is ready
mcrfpy.step(0.016)
# Take screenshot
print(f"Taking screenshot: {SCREENSHOT_PATH}")
result = automation.screenshot(SCREENSHOT_PATH)
if not result:
print("ERROR: Failed to take screenshot")
return False
file_size = os.path.getsize(SCREENSHOT_PATH)
print(f"Screenshot saved: {file_size} bytes")
print()
# Build grounded observations
grounded_text = build_grounded_prompt(grid, player, rat)
print(f"Grounded observations: {grounded_text}")
print()
# Query 1: Ask VLLM to describe what it sees
print("-" * 40)
print("Query 1: Describe what you see")
print("-" * 40)
system_prompt = """You are an AI agent in a roguelike dungeon game. You can see the game world through screenshots.
The view shows a top-down grid-based dungeon with tiles, walls, and creatures.
Your character is the humanoid figure. The dark areas are outside your field of vision.
Other creatures may be enemies or NPCs. Describe what you observe concisely."""
user_prompt = f"""Look at this game screenshot. {grounded_text}
Describe what you see in the dungeon from your character's perspective.
Be specific about:
- Your position in the room
- Any creatures you can see
- The layout of walls and passages
- Areas obscured by fog of war (darkness)"""
messages = [
{"role": "system", "content": system_prompt},
message_with_image(user_prompt, SCREENSHOT_PATH)
]
resp = llm_chat_completion(messages)
if "error" in resp:
print(f"VLLM Error: {resp['error']}")
print("\nNote: The VLLM server may not be running or accessible.")
print("Screenshot is saved for manual inspection.")
description = "I can see a dungeon scene."
else:
description = resp.get('choices', [{}])[0].get('message', {}).get('content', 'No response')
print(f"\nVLLM Response:\n{description}")
print()
# Query 2: Ask what action the agent would like to take
print("-" * 40)
print("Query 2: What would you like to do?")
print("-" * 40)
messages.append({"role": "assistant", "content": description})
messages.append({
"role": "user",
"content": f"""Based on what you see, what action would you like to take?
Available actions:
- GO NORTH / SOUTH / EAST / WEST - move in that direction
- WAIT - stay in place and observe
- LOOK - examine your surroundings more carefully
{grounded_text}
State your reasoning briefly, then declare your action clearly (e.g., "Action: GO EAST")."""
})
resp = llm_chat_completion(messages)
if "error" in resp:
print(f"VLLM Error: {resp['error']}")
else:
action = resp.get('choices', [{}])[0].get('message', {}).get('content', 'No response')
print(f"\nVLLM Response:\n{action}")
print()
print("=" * 60)
print("Demo Complete")
print("=" * 60)
print(f"\nScreenshot preserved at: {SCREENSHOT_PATH}")
print("Grid settings: zoom=2.0, FOV radius=8, perspective rendering enabled")
return True
# Main execution
if __name__ == "__main__":
try:
success = run_demo()
if success:
print("\nPASS")
sys.exit(0)
else:
print("\nFAIL")
sys.exit(1)
except Exception as e:
print(f"\nError: {e}")
import traceback
traceback.print_exc()
sys.exit(1)

View file

@ -1,346 +0,0 @@
#!/usr/bin/env python3
"""
Multi-Agent VLLM Demo for McRogueFace
=====================================
Demonstrates cycling through multiple agent perspectives,
each with their own FOV and grounded observations.
Three agents:
- Wizard (left side) - can see the rat but not the other agents
- Blacksmith (right side) - can see the knight, rat, and the wall
- Knight (right side) - can see the blacksmith, rat, and the wall
Each agent gets their own screenshot and VLLM query.
"""
import mcrfpy
from mcrfpy import automation
import sys
import requests
import base64
import os
import random
# VLLM configuration
VLLM_URL = "http://192.168.1.100:8100/v1/chat/completions"
SCREENSHOT_DIR = "/tmp/vllm_multi_agent"
# Sprite constants
FLOOR_COMMON = 0
FLOOR_SPECKLE1 = 12
FLOOR_SPECKLE2 = 24
WALL_TILE = 40
# Agent sprites
WIZARD_SPRITE = 84
BLACKSMITH_SPRITE = 86
KNIGHT_SPRITE = 96
RAT_SPRITE = 123
def file_to_base64(file_path):
"""Convert any image file to base64 string."""
with open(file_path, 'rb') as f:
return base64.b64encode(f.read()).decode('utf-8')
def llm_chat_completion(messages: list):
"""Chat completion endpoint of local LLM"""
try:
response = requests.post(VLLM_URL, json={'messages': messages}, timeout=60)
return response.json()
except requests.exceptions.RequestException as e:
return {"error": str(e)}
def message_with_image(text, image_path):
"""Create a message with an embedded image for vision models."""
image_data = file_to_base64(image_path)
return {
"role": "user",
"content": [
{"type": "text", "text": text},
{"type": "image_url", "image_url": {"url": "data:image/png;base64," + image_data}}
]
}
def get_floor_tile():
"""Return a floor tile sprite with realistic distribution."""
roll = random.random()
if roll < 0.95:
return FLOOR_COMMON
elif roll < 0.99:
return FLOOR_SPECKLE1
else:
return FLOOR_SPECKLE2
class Agent:
"""Wrapper for an agent entity with metadata."""
def __init__(self, name, entity, description):
self.name = name
self.entity = entity
self.description = description # e.g., "a wizard", "a blacksmith"
@property
def pos(self):
return (int(self.entity.pos[0]), int(self.entity.pos[1]))
def setup_scene():
"""Create a dungeon scene with multiple agents."""
print("Setting up multi-agent scene...")
# Create and set scene
mcrfpy.createScene("multi_agent_demo")
mcrfpy.setScene("multi_agent_demo")
ui = mcrfpy.sceneUI("multi_agent_demo")
# Load the game texture
texture = mcrfpy.Texture("assets/kenney_TD_MR_IP.png", 16, 16)
# Create grid
grid = mcrfpy.Grid(
grid_size=(25, 15),
texture=texture,
pos=(5, 5),
size=(1014, 700)
)
grid.fill_color = mcrfpy.Color(20, 20, 30)
grid.zoom = 2.0
ui.append(grid)
# Set up floor tiles and walls
for x in range(25):
for y in range(15):
point = grid.at(x, y)
if x == 0 or x == 24 or y == 0 or y == 14:
point.tilesprite = WALL_TILE
point.walkable = False
point.transparent = False
else:
point.tilesprite = get_floor_tile()
point.walkable = True
point.transparent = True
# Add a wall divider in the middle (blocks wizard's view of right side)
for y in range(3, 12):
point = grid.at(10, y)
point.tilesprite = WALL_TILE
point.walkable = False
point.transparent = False
# Door opening in the wall
door = grid.at(10, 7)
door.tilesprite = get_floor_tile()
door.walkable = True
door.transparent = True
# Create FOV layer for fog of war
fov_layer = grid.add_layer('color', z_index=10)
fov_layer.fill(mcrfpy.Color(0, 0, 0, 255))
# Create agents
agents = []
# Wizard on the left side
wizard_entity = mcrfpy.Entity(grid_pos=(4, 7), texture=texture, sprite_index=WIZARD_SPRITE)
grid.entities.append(wizard_entity)
agents.append(Agent("Wizard", wizard_entity, "a wizard"))
# Blacksmith on the right side (upper)
blacksmith_entity = mcrfpy.Entity(grid_pos=(18, 5), texture=texture, sprite_index=BLACKSMITH_SPRITE)
grid.entities.append(blacksmith_entity)
agents.append(Agent("Blacksmith", blacksmith_entity, "a blacksmith"))
# Knight on the right side (lower)
knight_entity = mcrfpy.Entity(grid_pos=(18, 10), texture=texture, sprite_index=KNIGHT_SPRITE)
grid.entities.append(knight_entity)
agents.append(Agent("Knight", knight_entity, "a knight"))
# Rat in the middle-right area (visible to blacksmith and knight, maybe wizard through door)
rat_entity = mcrfpy.Entity(grid_pos=(14, 7), texture=texture, sprite_index=RAT_SPRITE)
grid.entities.append(rat_entity)
return grid, fov_layer, agents, rat_entity
def switch_perspective(grid, fov_layer, agent):
"""Switch the grid view to an agent's perspective."""
# Reset fog layer to all unknown (black) before switching
# This prevents discovered tiles from one agent carrying over to another
fov_layer.fill(mcrfpy.Color(0, 0, 0, 255))
# Apply this agent's perspective
fov_layer.apply_perspective(
entity=agent.entity,
visible=mcrfpy.Color(0, 0, 0, 0),
discovered=mcrfpy.Color(40, 40, 60, 180),
unknown=mcrfpy.Color(0, 0, 0, 255)
)
# Update visibility from agent's position
agent.entity.update_visibility()
# Center camera on this agent
px, py = agent.pos
grid.center = (px * 16 + 8, py * 16 + 8)
def get_visible_entities(grid, observer, all_agents, rat):
"""Get list of entities visible to the observer."""
visible = []
ox, oy = observer.pos
# Check rat visibility
rx, ry = int(rat.pos[0]), int(rat.pos[1])
if grid.is_in_fov(rx, ry):
# Determine direction
direction = get_direction(ox, oy, rx, ry)
visible.append(f"a rat to the {direction}")
# Check other agents
for agent in all_agents:
if agent.name == observer.name:
continue
ax, ay = agent.pos
if grid.is_in_fov(ax, ay):
direction = get_direction(ox, oy, ax, ay)
visible.append(f"{agent.description} to the {direction}")
return visible
def get_direction(from_x, from_y, to_x, to_y):
"""Get cardinal direction from one point to another."""
dx = to_x - from_x
dy = to_y - from_y
# Primary direction
if abs(dx) > abs(dy):
return "east" if dx > 0 else "west"
elif abs(dy) > abs(dx):
return "south" if dy > 0 else "north"
else:
# Diagonal - pick one
ns = "south" if dy > 0 else "north"
ew = "east" if dx > 0 else "west"
return f"{ns}{ew}"
def build_grounded_prompt(visible_entities):
"""Build grounded text from visible entities."""
if not visible_entities:
return "The area appears clear."
if len(visible_entities) == 1:
return f"You see {visible_entities[0]}."
else:
items = ", ".join(visible_entities[:-1]) + f" and {visible_entities[-1]}"
return f"You see {items}."
def query_agent(agent, screenshot_path, grounded_text):
"""Query VLLM for a single agent's perspective."""
system_prompt = f"""You are {agent.description} in a roguelike dungeon game. You can see the game world through screenshots.
The view shows a top-down grid-based dungeon. Your character is centered in the view.
The dark areas are outside your field of vision. Other figures may be allies, enemies, or NPCs.
Describe what you observe concisely and decide on an action."""
user_prompt = f"""Look at this game screenshot from your perspective as {agent.description}. {grounded_text}
Describe what you see briefly, then choose an action:
- GO NORTH / SOUTH / EAST / WEST
- WAIT
- LOOK
State your reasoning in 1-2 sentences, then declare: "Action: <YOUR_ACTION>" """
messages = [
{"role": "system", "content": system_prompt},
message_with_image(user_prompt, screenshot_path)
]
resp = llm_chat_completion(messages)
if "error" in resp:
return f"VLLM Error: {resp['error']}"
else:
return resp.get('choices', [{}])[0].get('message', {}).get('content', 'No response')
def run_demo():
"""Main demo function."""
print("=" * 70)
print("Multi-Agent VLLM Demo")
print("=" * 70)
print()
# Create screenshot directory
os.makedirs(SCREENSHOT_DIR, exist_ok=True)
# Setup scene
grid, fov_layer, agents, rat = setup_scene()
# Cycle through each agent's perspective
for i, agent in enumerate(agents):
print(f"\n{'='*70}")
print(f"Agent {i+1}/3: {agent.name} ({agent.description})")
print(f"Position: {agent.pos}")
print("=" * 70)
# Switch to this agent's perspective
switch_perspective(grid, fov_layer, agent)
# Advance simulation
mcrfpy.step(0.016)
# Take screenshot
screenshot_path = os.path.join(SCREENSHOT_DIR, f"{i}_{agent.name.lower()}_view.png")
result = automation.screenshot(screenshot_path)
if not result:
print(f"ERROR: Failed to take screenshot for {agent.name}")
continue
file_size = os.path.getsize(screenshot_path)
print(f"Screenshot: {screenshot_path} ({file_size} bytes)")
# Get visible entities for this agent
visible = get_visible_entities(grid, agent, agents, rat)
grounded_text = build_grounded_prompt(visible)
print(f"Grounded observations: {grounded_text}")
# Query VLLM
print(f"\nQuerying VLLM for {agent.name}...")
print("-" * 50)
response = query_agent(agent, screenshot_path, grounded_text)
print(f"\n{agent.name}'s Response:\n{response}")
print()
print("\n" + "=" * 70)
print("Multi-Agent Demo Complete")
print("=" * 70)
print(f"\nScreenshots saved to: {SCREENSHOT_DIR}/")
for i, agent in enumerate(agents):
print(f" - {i}_{agent.name.lower()}_view.png")
return True
# Main execution
if __name__ == "__main__":
try:
success = run_demo()
if success:
print("\nPASS")
sys.exit(0)
else:
print("\nFAIL")
sys.exit(1)
except Exception as e:
print(f"\nError: {e}")
import traceback
traceback.print_exc()
sys.exit(1)