hotfix: windows build, fresh docs

This commit is contained in:
John McCardle 2025-07-10 16:34:38 -04:00
parent 4144cdf067
commit 96857a41c6
6 changed files with 1785 additions and 412 deletions

File diff suppressed because it is too large Load Diff

View File

@ -183,7 +183,7 @@
<div class="container">
<h1>McRogueFace API Reference - Complete Documentation</h1>
<p><em>Generated on 2025-07-08 11:53:54</em></p>
<p><em>Generated on 2025-07-10 01:04:50</em></p>
<div class="toc">
<h2>Table of Contents</h2>
<ul>
@ -632,13 +632,6 @@ mcrfpy.setScale(2.0) # Double the window size
</div>
<h4>Methods:</h4>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">get_current_value()</code></h5>
<p>Get the current interpolated value of the animation.</p>
<div style="margin-left: 20px; color: #28a745;">
<strong>Returns:</strong> float: Current animation value between start and end
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">update(delta_time)</code></h5>
<p>Update the animation by the given time delta.</p>
<div style="margin-left: 20px;">
@ -662,6 +655,13 @@ The UI element to animate
<strong>Note:</strong> The target must have the property specified in the animation constructor.
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">get_current_value()</code></h5>
<p>Get the current interpolated value of the animation.</p>
<div style="margin-left: 20px; color: #28a745;">
<strong>Returns:</strong> float: Current animation value between start and end
</div>
</div>
</div>
<div class="method-section">
<h3><span class="class-name">Caption</span></h3>
@ -701,23 +701,6 @@ Attributes:
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">resize(width, height)</code></h5>
<p>Resize the element to new dimensions.</p>
<div style="margin-left: 20px;">
<span class="arg-name">width</span>
<span class="arg-type">(float)</span>:
New width in pixels
</div>
<div style="margin-left: 20px;">
<span class="arg-name">height</span>
<span class="arg-type">(float)</span>:
New height in pixels
</div>
<div style="margin-left: 20px; color: #856404;">
<strong>Note:</strong> For Caption and Sprite, this may not change actual size if determined by content.
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">move(dx, dy)</code></h5>
<p>Move the element by a relative offset.</p>
<div style="margin-left: 20px;">
@ -734,12 +717,47 @@ Vertical offset in pixels
<strong>Note:</strong> This modifies the x and y position properties by the given amounts.
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">resize(width, height)</code></h5>
<p>Resize the element to new dimensions.</p>
<div style="margin-left: 20px;">
<span class="arg-name">width</span>
<span class="arg-type">(float)</span>:
New width in pixels
</div>
<div style="margin-left: 20px;">
<span class="arg-name">height</span>
<span class="arg-type">(float)</span>:
New height in pixels
</div>
<div style="margin-left: 20px; color: #856404;">
<strong>Note:</strong> For Caption and Sprite, this may not change actual size if determined by content.
</div>
</div>
</div>
<div class="method-section">
<h3><span class="class-name">Color</span></h3>
<p>SFML Color Object</p>
<h4>Methods:</h4>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">from_hex(hex_string)</code></h5>
<p>Create a Color from a hexadecimal color string.</p>
<div style="margin-left: 20px;">
<span class="arg-name">hex_string</span>
<span class="arg-type">(str)</span>:
Hex color string (e.g., &quot;#FF0000&quot; or &quot;FF0000&quot;)
</div>
<div style="margin-left: 20px; color: #28a745;">
<strong>Returns:</strong> Color: New Color object from hex string
</div>
<div style="margin-left: 20px;">
<strong>Example:</strong>
<pre><code>
red = Color.from_hex(&quot;#FF0000&quot;)
</code></pre>
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">lerp(other, t)</code></h5>
<p>Linearly interpolate between this color and another.</p>
<div style="margin-left: 20px;">
@ -775,24 +793,6 @@ hex_str = color.to_hex() # Returns &quot;#FF0000&quot;
</code></pre>
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">from_hex(hex_string)</code></h5>
<p>Create a Color from a hexadecimal color string.</p>
<div style="margin-left: 20px;">
<span class="arg-name">hex_string</span>
<span class="arg-type">(str)</span>:
Hex color string (e.g., &quot;#FF0000&quot; or &quot;FF0000&quot;)
</div>
<div style="margin-left: 20px; color: #28a745;">
<strong>Returns:</strong> Color: New Color object from hex string
</div>
<div style="margin-left: 20px;">
<strong>Example:</strong>
<pre><code>
red = Color.from_hex(&quot;#FF0000&quot;)
</code></pre>
</div>
</div>
</div>
<div class="method-section">
<h3><span class="class-name">Drawable</span></h3>
@ -809,6 +809,23 @@ red = Color.from_hex(&quot;#FF0000&quot;)
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">move(dx, dy)</code></h5>
<p>Move the element by a relative offset.</p>
<div style="margin-left: 20px;">
<span class="arg-name">dx</span>
<span class="arg-type">(float)</span>:
Horizontal offset in pixels
</div>
<div style="margin-left: 20px;">
<span class="arg-name">dy</span>
<span class="arg-type">(float)</span>:
Vertical offset in pixels
</div>
<div style="margin-left: 20px; color: #856404;">
<strong>Note:</strong> This modifies the x and y position properties by the given amounts.
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">resize(width, height)</code></h5>
<p>Resize the element to new dimensions.</p>
<div style="margin-left: 20px;">
@ -825,39 +842,12 @@ New height in pixels
<strong>Note:</strong> For Caption and Sprite, this may not change actual size if determined by content.
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">move(dx, dy)</code></h5>
<p>Move the element by a relative offset.</p>
<div style="margin-left: 20px;">
<span class="arg-name">dx</span>
<span class="arg-type">(float)</span>:
Horizontal offset in pixels
</div>
<div style="margin-left: 20px;">
<span class="arg-name">dy</span>
<span class="arg-type">(float)</span>:
Vertical offset in pixels
</div>
<div style="margin-left: 20px; color: #856404;">
<strong>Note:</strong> This modifies the x and y position properties by the given amounts.
</div>
</div>
</div>
<div class="method-section">
<h3><span class="class-name">Entity</span></h3>
<p>UIEntity objects</p>
<h4>Methods:</h4>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">get_bounds()</code></h5>
<p>Get the bounding rectangle of this drawable element.</p>
<div style="margin-left: 20px; color: #28a745;">
<strong>Returns:</strong> tuple: (x, y, width, height) representing the element&#x27;s bounds
</div>
<div style="margin-left: 20px; color: #856404;">
<strong>Note:</strong> The bounds are in screen coordinates and account for current position and size.
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">move(dx, dy)</code></h5>
<p>Move the element by a relative offset.</p>
<div style="margin-left: 20px;">
@ -875,6 +865,36 @@ Vertical offset in pixels
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">resize(width, height)</code></h5>
<p>Resize the element to new dimensions.</p>
<div style="margin-left: 20px;">
<span class="arg-name">width</span>
<span class="arg-type">(float)</span>:
New width in pixels
</div>
<div style="margin-left: 20px;">
<span class="arg-name">height</span>
<span class="arg-type">(float)</span>:
New height in pixels
</div>
<div style="margin-left: 20px; color: #856404;">
<strong>Note:</strong> For Caption and Sprite, this may not change actual size if determined by content.
</div>
</div>
<div class="arg-item">
<span class="method">update_visibility(...)</span>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">index()</code></h5>
<p>Get the index of this entity in its parent grid&#x27;s entity list.</p>
<div style="margin-left: 20px; color: #28a745;">
<strong>Returns:</strong> int: Index position, or -1 if not in a grid
</div>
</div>
<div class="arg-item">
<span class="method">path_to(...)</span>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">at(x, y)</code></h5>
<p>Check if this entity is at the specified grid coordinates.</p>
<div style="margin-left: 20px;">
@ -892,27 +912,13 @@ Grid y coordinate to check
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">resize(width, height)</code></h5>
<p>Resize the element to new dimensions.</p>
<div style="margin-left: 20px;">
<span class="arg-name">width</span>
<span class="arg-type">(float)</span>:
New width in pixels
</div>
<div style="margin-left: 20px;">
<span class="arg-name">height</span>
<span class="arg-type">(float)</span>:
New height in pixels
<h5><code class="method">get_bounds()</code></h5>
<p>Get the bounding rectangle of this drawable element.</p>
<div style="margin-left: 20px; color: #28a745;">
<strong>Returns:</strong> tuple: (x, y, width, height) representing the element&#x27;s bounds
</div>
<div style="margin-left: 20px; color: #856404;">
<strong>Note:</strong> For Caption and Sprite, this may not change actual size if determined by content.
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">index()</code></h5>
<p>Get the index of this entity in its parent grid&#x27;s entity list.</p>
<div style="margin-left: 20px; color: #28a745;">
<strong>Returns:</strong> int: Index position, or -1 if not in a grid
<strong>Note:</strong> The bounds are in screen coordinates and account for current position and size.
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
@ -937,21 +943,15 @@ The entity to remove
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">extend(iterable)</code></h5>
<p>Add all entities from an iterable to the collection.</p>
<div style="margin-left: 20px;">
<span class="arg-name">iterable</span>
<span class="arg-type">(Iterable[Entity])</span>:
Entities to add
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">append(entity)</code></h5>
<p>Add an entity to the end of the collection.</p>
<h5><code class="method">count(entity)</code></h5>
<p>Count the number of occurrences of an entity in the collection.</p>
<div style="margin-left: 20px;">
<span class="arg-name">entity</span>
<span class="arg-type">(Entity)</span>:
The entity to add
The entity to count
</div>
<div style="margin-left: 20px; color: #28a745;">
<strong>Returns:</strong> int: Number of times entity appears in collection
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
@ -967,15 +967,21 @@ The entity to find
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">count(entity)</code></h5>
<p>Count the number of occurrences of an entity in the collection.</p>
<h5><code class="method">extend(iterable)</code></h5>
<p>Add all entities from an iterable to the collection.</p>
<div style="margin-left: 20px;">
<span class="arg-name">iterable</span>
<span class="arg-type">(Iterable[Entity])</span>:
Entities to add
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">append(entity)</code></h5>
<p>Add an entity to the end of the collection.</p>
<div style="margin-left: 20px;">
<span class="arg-name">entity</span>
<span class="arg-type">(Entity)</span>:
The entity to count
</div>
<div style="margin-left: 20px; color: #28a745;">
<strong>Returns:</strong> int: Number of times entity appears in collection
The entity to add
</div>
</div>
</div>
@ -1022,23 +1028,6 @@ Attributes:
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">resize(width, height)</code></h5>
<p>Resize the element to new dimensions.</p>
<div style="margin-left: 20px;">
<span class="arg-name">width</span>
<span class="arg-type">(float)</span>:
New width in pixels
</div>
<div style="margin-left: 20px;">
<span class="arg-name">height</span>
<span class="arg-type">(float)</span>:
New height in pixels
</div>
<div style="margin-left: 20px; color: #856404;">
<strong>Note:</strong> For Caption and Sprite, this may not change actual size if determined by content.
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">move(dx, dy)</code></h5>
<p>Move the element by a relative offset.</p>
<div style="margin-left: 20px;">
@ -1055,6 +1044,23 @@ Vertical offset in pixels
<strong>Note:</strong> This modifies the x and y position properties by the given amounts.
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">resize(width, height)</code></h5>
<p>Resize the element to new dimensions.</p>
<div style="margin-left: 20px;">
<span class="arg-name">width</span>
<span class="arg-type">(float)</span>:
New width in pixels
</div>
<div style="margin-left: 20px;">
<span class="arg-name">height</span>
<span class="arg-type">(float)</span>:
New height in pixels
</div>
<div style="margin-left: 20px; color: #856404;">
<strong>Note:</strong> For Caption and Sprite, this may not change actual size if determined by content.
</div>
</div>
</div>
<div class="method-section">
<h3><span class="class-name">Grid</span></h3>
@ -1086,31 +1092,24 @@ Attributes:
z_index (int): Rendering order</p>
<h4>Methods:</h4>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">get_bounds()</code></h5>
<p>Get the bounding rectangle of this drawable element.</p>
<div style="margin-left: 20px; color: #28a745;">
<strong>Returns:</strong> tuple: (x, y, width, height) representing the element&#x27;s bounds
<h5><code class="method">move(dx, dy)</code></h5>
<p>Move the element by a relative offset.</p>
<div style="margin-left: 20px;">
<span class="arg-name">dx</span>
<span class="arg-type">(float)</span>:
Horizontal offset in pixels
</div>
<div style="margin-left: 20px;">
<span class="arg-name">dy</span>
<span class="arg-type">(float)</span>:
Vertical offset in pixels
</div>
<div style="margin-left: 20px; color: #856404;">
<strong>Note:</strong> The bounds are in screen coordinates and account for current position and size.
<strong>Note:</strong> This modifies the x and y position properties by the given amounts.
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">at(x, y)</code></h5>
<p>Get the GridPoint at the specified grid coordinates.</p>
<div style="margin-left: 20px;">
<span class="arg-name">x</span>
<span class="arg-type">(int)</span>:
Grid x coordinate
</div>
<div style="margin-left: 20px;">
<span class="arg-name">y</span>
<span class="arg-type">(int)</span>:
Grid y coordinate
</div>
<div style="margin-left: 20px; color: #28a745;">
<strong>Returns:</strong> GridPoint or None: The grid point at (x, y), or None if out of bounds
</div>
<div class="arg-item">
<span class="method">compute_fov(...)</span>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">resize(width, height)</code></h5>
@ -1129,21 +1128,49 @@ New height in pixels
<strong>Note:</strong> For Caption and Sprite, this may not change actual size if determined by content.
</div>
</div>
<div class="arg-item">
<span class="method">compute_dijkstra(...)</span>
</div>
<div class="arg-item">
<span class="method">get_dijkstra_path(...)</span>
</div>
<div class="arg-item">
<span class="method">is_in_fov(...)</span>
</div>
<div class="arg-item">
<span class="method">find_path(...)</span>
</div>
<div class="arg-item">
<span class="method">compute_astar_path(...)</span>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">move(dx, dy)</code></h5>
<p>Move the element by a relative offset.</p>
<h5><code class="method">at(x, y)</code></h5>
<p>Get the GridPoint at the specified grid coordinates.</p>
<div style="margin-left: 20px;">
<span class="arg-name">dx</span>
<span class="arg-type">(float)</span>:
Horizontal offset in pixels
<span class="arg-name">x</span>
<span class="arg-type">(int)</span>:
Grid x coordinate
</div>
<div style="margin-left: 20px;">
<span class="arg-name">dy</span>
<span class="arg-type">(float)</span>:
Vertical offset in pixels
<span class="arg-name">y</span>
<span class="arg-type">(int)</span>:
Grid y coordinate
</div>
<div style="margin-left: 20px; color: #28a745;">
<strong>Returns:</strong> GridPoint or None: The grid point at (x, y), or None if out of bounds
</div>
</div>
<div class="arg-item">
<span class="method">get_dijkstra_distance(...)</span>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">get_bounds()</code></h5>
<p>Get the bounding rectangle of this drawable element.</p>
<div style="margin-left: 20px; color: #28a745;">
<strong>Returns:</strong> tuple: (x, y, width, height) representing the element&#x27;s bounds
</div>
<div style="margin-left: 20px; color: #856404;">
<strong>Note:</strong> This modifies the x and y position properties by the given amounts.
<strong>Note:</strong> The bounds are in screen coordinates and account for current position and size.
</div>
</div>
</div>
@ -1273,23 +1300,6 @@ Attributes:
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">resize(width, height)</code></h5>
<p>Resize the element to new dimensions.</p>
<div style="margin-left: 20px;">
<span class="arg-name">width</span>
<span class="arg-type">(float)</span>:
New width in pixels
</div>
<div style="margin-left: 20px;">
<span class="arg-name">height</span>
<span class="arg-type">(float)</span>:
New height in pixels
</div>
<div style="margin-left: 20px; color: #856404;">
<strong>Note:</strong> For Caption and Sprite, this may not change actual size if determined by content.
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">move(dx, dy)</code></h5>
<p>Move the element by a relative offset.</p>
<div style="margin-left: 20px;">
@ -1306,6 +1316,23 @@ Vertical offset in pixels
<strong>Note:</strong> This modifies the x and y position properties by the given amounts.
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">resize(width, height)</code></h5>
<p>Resize the element to new dimensions.</p>
<div style="margin-left: 20px;">
<span class="arg-name">width</span>
<span class="arg-type">(float)</span>:
New width in pixels
</div>
<div style="margin-left: 20px;">
<span class="arg-name">height</span>
<span class="arg-type">(float)</span>:
New height in pixels
</div>
<div style="margin-left: 20px; color: #856404;">
<strong>Note:</strong> For Caption and Sprite, this may not change actual size if determined by content.
</div>
</div>
</div>
<div class="method-section">
<h3><span class="class-name">Texture</span></h3>
@ -1323,6 +1350,13 @@ Vertical offset in pixels
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">restart()</code></h5>
<p>Restart the timer from the beginning.</p>
<div style="margin-left: 20px; color: #856404;">
<strong>Note:</strong> Resets the timer&#x27;s internal clock to zero.
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">pause()</code></h5>
<p>Pause the timer, stopping its callback execution.</p>
<div style="margin-left: 20px; color: #856404;">
@ -1336,13 +1370,6 @@ Vertical offset in pixels
<strong>Note:</strong> After cancelling, the timer object cannot be reused.
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">restart()</code></h5>
<p>Restart the timer from the beginning.</p>
<div style="margin-left: 20px; color: #856404;">
<strong>Note:</strong> Resets the timer&#x27;s internal clock to zero.
</div>
</div>
</div>
<div class="method-section">
<h3><span class="class-name">UICollection</span></h3>
@ -1358,6 +1385,30 @@ The drawable to remove
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">count(drawable)</code></h5>
<p>Count the number of occurrences of a drawable in the collection.</p>
<div style="margin-left: 20px;">
<span class="arg-name">drawable</span>
<span class="arg-type">(UIDrawable)</span>:
The drawable to count
</div>
<div style="margin-left: 20px; color: #28a745;">
<strong>Returns:</strong> int: Number of times drawable appears in collection
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">index(drawable)</code></h5>
<p>Find the index of the first occurrence of a drawable.</p>
<div style="margin-left: 20px;">
<span class="arg-name">drawable</span>
<span class="arg-type">(UIDrawable)</span>:
The drawable to find
</div>
<div style="margin-left: 20px; color: #28a745;">
<strong>Returns:</strong> int: Index of drawable in collection
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">extend(iterable)</code></h5>
<p>Add all drawables from an iterable to the collection.</p>
<div style="margin-left: 20px;">
@ -1375,30 +1426,6 @@ Drawables to add
The drawable element to add
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">index(drawable)</code></h5>
<p>Find the index of the first occurrence of a drawable.</p>
<div style="margin-left: 20px;">
<span class="arg-name">drawable</span>
<span class="arg-type">(UIDrawable)</span>:
The drawable to find
</div>
<div style="margin-left: 20px; color: #28a745;">
<strong>Returns:</strong> int: Index of drawable in collection
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">count(drawable)</code></h5>
<p>Count the number of occurrences of a drawable in the collection.</p>
<div style="margin-left: 20px;">
<span class="arg-name">drawable</span>
<span class="arg-type">(UIDrawable)</span>:
The drawable to count
</div>
<div style="margin-left: 20px; color: #28a745;">
<strong>Returns:</strong> int: Number of times drawable appears in collection
</div>
</div>
</div>
<div class="method-section">
<h3><span class="class-name">UICollectionIter</span></h3>
@ -1413,28 +1440,10 @@ The drawable to count
<p>SFML Vector Object</p>
<h4>Methods:</h4>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">magnitude()</code></h5>
<p>Calculate the length/magnitude of this vector.</p>
<h5><code class="method">copy()</code></h5>
<p>Create a copy of this vector.</p>
<div style="margin-left: 20px; color: #28a745;">
<strong>Returns:</strong> float: The magnitude of the vector
</div>
<div style="margin-left: 20px;">
<strong>Example:</strong>
<pre><code>
length = vector.magnitude()
</code></pre>
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">distance_to(other)</code></h5>
<p>Calculate the distance to another vector.</p>
<div style="margin-left: 20px;">
<span class="arg-name">other</span>
<span class="arg-type">(Vector)</span>:
The other vector
</div>
<div style="margin-left: 20px; color: #28a745;">
<strong>Returns:</strong> float: Distance between the two vectors
<strong>Returns:</strong> Vector: New Vector object with same x and y values
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
@ -1457,6 +1466,19 @@ The other vector
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">magnitude()</code></h5>
<p>Calculate the length/magnitude of this vector.</p>
<div style="margin-left: 20px; color: #28a745;">
<strong>Returns:</strong> float: The magnitude of the vector
</div>
<div style="margin-left: 20px;">
<strong>Example:</strong>
<pre><code>
length = vector.magnitude()
</code></pre>
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">normalize()</code></h5>
<p>Return a unit vector in the same direction.</p>
<div style="margin-left: 20px; color: #28a745;">
@ -1474,10 +1496,15 @@ The other vector
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">copy()</code></h5>
<p>Create a copy of this vector.</p>
<h5><code class="method">distance_to(other)</code></h5>
<p>Calculate the distance to another vector.</p>
<div style="margin-left: 20px;">
<span class="arg-name">other</span>
<span class="arg-type">(Vector)</span>:
The other vector
</div>
<div style="margin-left: 20px; color: #28a745;">
<strong>Returns:</strong> Vector: New Vector object with same x and y values
<strong>Returns:</strong> float: Distance between the two vectors
</div>
</div>
</div>
@ -1486,6 +1513,16 @@ The other vector
<p>Window singleton for accessing and modifying the game window properties</p>
<h4>Methods:</h4>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">get()</code></h5>
<p>Get the Window singleton instance.</p>
<div style="margin-left: 20px; color: #28a745;">
<strong>Returns:</strong> Window: The singleton window object
</div>
<div style="margin-left: 20px; color: #856404;">
<strong>Note:</strong> This is a static method that returns the same instance every time.
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">screenshot(filename)</code></h5>
<p>Take a screenshot and save it to a file.</p>
<div style="margin-left: 20px;">
@ -1504,16 +1541,6 @@ Path where to save the screenshot
<strong>Note:</strong> Only works if the window is not fullscreen.
</div>
</div>
<div style="margin-left: 20px; margin-bottom: 15px;">
<h5><code class="method">get()</code></h5>
<p>Get the Window singleton instance.</p>
<div style="margin-left: 20px; color: #28a745;">
<strong>Returns:</strong> Window: The singleton window object
</div>
<div style="margin-left: 20px; color: #856404;">
<strong>Note:</strong> This is a static method that returns the same instance every time.
</div>
</div>
</div>
<h2 id="automation">Automation Module</h2>
<p>The <code>mcrfpy.automation</code> module provides testing and automation capabilities.</p>

View File

@ -35,7 +35,7 @@ int PyTimer::init(PyTimerObject* self, PyObject* args, PyObject* kwds) {
PyObject* callback = nullptr;
int interval = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "sOi", kwlist,
if (!PyArg_ParseTupleAndKeywords(args, kwds, "sOi", const_cast<char**>(kwlist),
&name, &callback, &interval)) {
return -1;
}

View File

@ -508,8 +508,22 @@ PyMethodDef UIEntity::methods[] = {
{"at", (PyCFunction)UIEntity::at, METH_O},
{"index", (PyCFunction)UIEntity::index, METH_NOARGS, "Return the index of this entity in its grid's entity collection"},
{"die", (PyCFunction)UIEntity::die, METH_NOARGS, "Remove this entity from its grid"},
{"path_to", (PyCFunction)UIEntity::path_to, METH_VARARGS | METH_KEYWORDS, "Find path from entity to target position using Dijkstra pathfinding"},
{"update_visibility", (PyCFunction)UIEntity::update_visibility, METH_NOARGS, "Update entity's visibility state based on current FOV"},
{"path_to", (PyCFunction)UIEntity::path_to, METH_VARARGS | METH_KEYWORDS,
"path_to(x: int, y: int) -> bool\n\n"
"Find and follow path to target position using A* pathfinding.\n\n"
"Args:\n"
" x: Target X coordinate\n"
" y: Target Y coordinate\n\n"
"Returns:\n"
" True if a path was found and the entity started moving, False otherwise\n\n"
"The entity will automatically move along the path over multiple frames.\n"
"Call this again to change the target or repath."},
{"update_visibility", (PyCFunction)UIEntity::update_visibility, METH_NOARGS,
"update_visibility() -> None\n\n"
"Update entity's visibility state based on current FOV.\n\n"
"Recomputes which cells are visible from the entity's position and updates\n"
"the entity's gridstate to track explored areas. This is called automatically\n"
"when the entity moves if it has a grid with perspective set."},
{NULL, NULL, 0, NULL}
};
@ -522,8 +536,22 @@ PyMethodDef UIEntity_all_methods[] = {
{"at", (PyCFunction)UIEntity::at, METH_O},
{"index", (PyCFunction)UIEntity::index, METH_NOARGS, "Return the index of this entity in its grid's entity collection"},
{"die", (PyCFunction)UIEntity::die, METH_NOARGS, "Remove this entity from its grid"},
{"path_to", (PyCFunction)UIEntity::path_to, METH_VARARGS | METH_KEYWORDS, "Find path from entity to target position using Dijkstra pathfinding"},
{"update_visibility", (PyCFunction)UIEntity::update_visibility, METH_NOARGS, "Update entity's visibility state based on current FOV"},
{"path_to", (PyCFunction)UIEntity::path_to, METH_VARARGS | METH_KEYWORDS,
"path_to(x: int, y: int) -> bool\n\n"
"Find and follow path to target position using A* pathfinding.\n\n"
"Args:\n"
" x: Target X coordinate\n"
" y: Target Y coordinate\n\n"
"Returns:\n"
" True if a path was found and the entity started moving, False otherwise\n\n"
"The entity will automatically move along the path over multiple frames.\n"
"Call this again to change the target or repath."},
{"update_visibility", (PyCFunction)UIEntity::update_visibility, METH_NOARGS,
"update_visibility() -> None\n\n"
"Update entity's visibility state based on current FOV.\n\n"
"Recomputes which cells are visible from the entity's position and updates\n"
"the entity's gridstate to track explored areas. This is called automatically\n"
"when the entity moves if it has a grid with perspective set."},
{NULL} // Sentinel
};

View File

@ -972,7 +972,7 @@ PyObject* UIGrid::py_compute_fov(PyUIGridObject* self, PyObject* args, PyObject*
int light_walls = 1;
int algorithm = FOV_BASIC;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "ii|ipi", kwlist,
if (!PyArg_ParseTupleAndKeywords(args, kwds, "ii|ipi", const_cast<char**>(kwlist),
&x, &y, &radius, &light_walls, &algorithm)) {
return NULL;
}
@ -998,7 +998,7 @@ PyObject* UIGrid::py_find_path(PyUIGridObject* self, PyObject* args, PyObject* k
int x1, y1, x2, y2;
float diagonal_cost = 1.41f;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "iiii|f", kwlist,
if (!PyArg_ParseTupleAndKeywords(args, kwds, "iiii|f", const_cast<char**>(kwlist),
&x1, &y1, &x2, &y2, &diagonal_cost)) {
return NULL;
}
@ -1026,7 +1026,7 @@ PyObject* UIGrid::py_compute_dijkstra(PyUIGridObject* self, PyObject* args, PyOb
int root_x, root_y;
float diagonal_cost = 1.41f;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "ii|f", kwlist,
if (!PyArg_ParseTupleAndKeywords(args, kwds, "ii|f", const_cast<char**>(kwlist),
&root_x, &root_y, &diagonal_cost)) {
return NULL;
}
@ -1075,7 +1075,7 @@ PyObject* UIGrid::py_compute_astar_path(PyUIGridObject* self, PyObject* args, Py
static const char* kwlist[] = {"x1", "y1", "x2", "y2", "diagonal_cost", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwds, "iiii|f", kwlist,
if (!PyArg_ParseTupleAndKeywords(args, kwds, "iiii|f", const_cast<char**>(kwlist),
&x1, &y1, &x2, &y2, &diagonal_cost)) {
return NULL;
}
@ -1096,19 +1096,77 @@ PyObject* UIGrid::py_compute_astar_path(PyUIGridObject* self, PyObject* args, Py
PyMethodDef UIGrid::methods[] = {
{"at", (PyCFunction)UIGrid::py_at, METH_VARARGS | METH_KEYWORDS},
{"compute_fov", (PyCFunction)UIGrid::py_compute_fov, METH_VARARGS | METH_KEYWORDS,
"Compute field of view from a position. Args: x, y, radius=0, light_walls=True, algorithm=FOV_BASIC"},
"compute_fov(x: int, y: int, radius: int = 0, light_walls: bool = True, algorithm: int = FOV_BASIC) -> None\n\n"
"Compute field of view from a position.\n\n"
"Args:\n"
" x: X coordinate of the viewer\n"
" y: Y coordinate of the viewer\n"
" radius: Maximum view distance (0 = unlimited)\n"
" light_walls: Whether walls are lit when visible\n"
" algorithm: FOV algorithm to use (FOV_BASIC, FOV_DIAMOND, FOV_SHADOW, FOV_PERMISSIVE_0-8)\n\n"
"Updates the internal FOV state. Use is_in_fov() to check visibility after calling this.\n"
"When perspective is set, this also updates visibility overlays automatically."},
{"is_in_fov", (PyCFunction)UIGrid::py_is_in_fov, METH_VARARGS,
"Check if a cell is in the field of view. Args: x, y"},
"is_in_fov(x: int, y: int) -> bool\n\n"
"Check if a cell is in the field of view.\n\n"
"Args:\n"
" x: X coordinate to check\n"
" y: Y coordinate to check\n\n"
"Returns:\n"
" True if the cell is visible, False otherwise\n\n"
"Must call compute_fov() first to calculate visibility."},
{"find_path", (PyCFunction)UIGrid::py_find_path, METH_VARARGS | METH_KEYWORDS,
"Find A* path between two points. Args: x1, y1, x2, y2, diagonal_cost=1.41"},
"find_path(x1: int, y1: int, x2: int, y2: int, diagonal_cost: float = 1.41) -> List[Tuple[int, int]]\n\n"
"Find A* path between two points.\n\n"
"Args:\n"
" x1: Starting X coordinate\n"
" y1: Starting Y coordinate\n"
" x2: Target X coordinate\n"
" y2: Target Y coordinate\n"
" diagonal_cost: Cost of diagonal movement (default: 1.41)\n\n"
"Returns:\n"
" List of (x, y) tuples representing the path, empty list if no path exists\n\n"
"Uses A* algorithm with walkability from grid cells."},
{"compute_dijkstra", (PyCFunction)UIGrid::py_compute_dijkstra, METH_VARARGS | METH_KEYWORDS,
"Compute Dijkstra map from root position. Args: root_x, root_y, diagonal_cost=1.41"},
"compute_dijkstra(root_x: int, root_y: int, diagonal_cost: float = 1.41) -> None\n\n"
"Compute Dijkstra map from root position.\n\n"
"Args:\n"
" root_x: X coordinate of the root/target\n"
" root_y: Y coordinate of the root/target\n"
" diagonal_cost: Cost of diagonal movement (default: 1.41)\n\n"
"Precomputes distances from all reachable cells to the root.\n"
"Use get_dijkstra_distance() and get_dijkstra_path() to query results.\n"
"Useful for multiple entities pathfinding to the same target."},
{"get_dijkstra_distance", (PyCFunction)UIGrid::py_get_dijkstra_distance, METH_VARARGS,
"Get distance from Dijkstra root to position. Args: x, y. Returns float or None if invalid."},
"get_dijkstra_distance(x: int, y: int) -> Optional[float]\n\n"
"Get distance from Dijkstra root to position.\n\n"
"Args:\n"
" x: X coordinate to query\n"
" y: Y coordinate to query\n\n"
"Returns:\n"
" Distance as float, or None if position is unreachable or invalid\n\n"
"Must call compute_dijkstra() first."},
{"get_dijkstra_path", (PyCFunction)UIGrid::py_get_dijkstra_path, METH_VARARGS,
"Get path from position to Dijkstra root. Args: x, y. Returns list of (x,y) tuples."},
"get_dijkstra_path(x: int, y: int) -> List[Tuple[int, int]]\n\n"
"Get path from position to Dijkstra root.\n\n"
"Args:\n"
" x: Starting X coordinate\n"
" y: Starting Y coordinate\n\n"
"Returns:\n"
" List of (x, y) tuples representing path to root, empty if unreachable\n\n"
"Must call compute_dijkstra() first. Path includes start but not root position."},
{"compute_astar_path", (PyCFunction)UIGrid::py_compute_astar_path, METH_VARARGS | METH_KEYWORDS,
"Compute A* path between two points. Args: x1, y1, x2, y2, diagonal_cost=1.41. Returns list of (x,y) tuples. Note: diagonal_cost is currently ignored (uses default 1.41)."},
"compute_astar_path(x1: int, y1: int, x2: int, y2: int, diagonal_cost: float = 1.41) -> List[Tuple[int, int]]\n\n"
"Compute A* path between two points.\n\n"
"Args:\n"
" x1: Starting X coordinate\n"
" y1: Starting Y coordinate\n"
" x2: Target X coordinate\n"
" y2: Target Y coordinate\n"
" diagonal_cost: Cost of diagonal movement (default: 1.41)\n\n"
"Returns:\n"
" List of (x, y) tuples representing the path, empty list if no path exists\n\n"
"Alternative A* implementation. Prefer find_path() for consistency."},
{NULL, NULL, 0, NULL}
};
@ -1120,19 +1178,77 @@ PyMethodDef UIGrid_all_methods[] = {
UIDRAWABLE_METHODS,
{"at", (PyCFunction)UIGrid::py_at, METH_VARARGS | METH_KEYWORDS},
{"compute_fov", (PyCFunction)UIGrid::py_compute_fov, METH_VARARGS | METH_KEYWORDS,
"Compute field of view from a position. Args: x, y, radius=0, light_walls=True, algorithm=FOV_BASIC"},
"compute_fov(x: int, y: int, radius: int = 0, light_walls: bool = True, algorithm: int = FOV_BASIC) -> None\n\n"
"Compute field of view from a position.\n\n"
"Args:\n"
" x: X coordinate of the viewer\n"
" y: Y coordinate of the viewer\n"
" radius: Maximum view distance (0 = unlimited)\n"
" light_walls: Whether walls are lit when visible\n"
" algorithm: FOV algorithm to use (FOV_BASIC, FOV_DIAMOND, FOV_SHADOW, FOV_PERMISSIVE_0-8)\n\n"
"Updates the internal FOV state. Use is_in_fov() to check visibility after calling this.\n"
"When perspective is set, this also updates visibility overlays automatically."},
{"is_in_fov", (PyCFunction)UIGrid::py_is_in_fov, METH_VARARGS,
"Check if a cell is in the field of view. Args: x, y"},
"is_in_fov(x: int, y: int) -> bool\n\n"
"Check if a cell is in the field of view.\n\n"
"Args:\n"
" x: X coordinate to check\n"
" y: Y coordinate to check\n\n"
"Returns:\n"
" True if the cell is visible, False otherwise\n\n"
"Must call compute_fov() first to calculate visibility."},
{"find_path", (PyCFunction)UIGrid::py_find_path, METH_VARARGS | METH_KEYWORDS,
"Find A* path between two points. Args: x1, y1, x2, y2, diagonal_cost=1.41"},
"find_path(x1: int, y1: int, x2: int, y2: int, diagonal_cost: float = 1.41) -> List[Tuple[int, int]]\n\n"
"Find A* path between two points.\n\n"
"Args:\n"
" x1: Starting X coordinate\n"
" y1: Starting Y coordinate\n"
" x2: Target X coordinate\n"
" y2: Target Y coordinate\n"
" diagonal_cost: Cost of diagonal movement (default: 1.41)\n\n"
"Returns:\n"
" List of (x, y) tuples representing the path, empty list if no path exists\n\n"
"Uses A* algorithm with walkability from grid cells."},
{"compute_dijkstra", (PyCFunction)UIGrid::py_compute_dijkstra, METH_VARARGS | METH_KEYWORDS,
"Compute Dijkstra map from root position. Args: root_x, root_y, diagonal_cost=1.41"},
"compute_dijkstra(root_x: int, root_y: int, diagonal_cost: float = 1.41) -> None\n\n"
"Compute Dijkstra map from root position.\n\n"
"Args:\n"
" root_x: X coordinate of the root/target\n"
" root_y: Y coordinate of the root/target\n"
" diagonal_cost: Cost of diagonal movement (default: 1.41)\n\n"
"Precomputes distances from all reachable cells to the root.\n"
"Use get_dijkstra_distance() and get_dijkstra_path() to query results.\n"
"Useful for multiple entities pathfinding to the same target."},
{"get_dijkstra_distance", (PyCFunction)UIGrid::py_get_dijkstra_distance, METH_VARARGS,
"Get distance from Dijkstra root to position. Args: x, y. Returns float or None if invalid."},
"get_dijkstra_distance(x: int, y: int) -> Optional[float]\n\n"
"Get distance from Dijkstra root to position.\n\n"
"Args:\n"
" x: X coordinate to query\n"
" y: Y coordinate to query\n\n"
"Returns:\n"
" Distance as float, or None if position is unreachable or invalid\n\n"
"Must call compute_dijkstra() first."},
{"get_dijkstra_path", (PyCFunction)UIGrid::py_get_dijkstra_path, METH_VARARGS,
"Get path from position to Dijkstra root. Args: x, y. Returns list of (x,y) tuples."},
"get_dijkstra_path(x: int, y: int) -> List[Tuple[int, int]]\n\n"
"Get path from position to Dijkstra root.\n\n"
"Args:\n"
" x: Starting X coordinate\n"
" y: Starting Y coordinate\n\n"
"Returns:\n"
" List of (x, y) tuples representing path to root, empty if unreachable\n\n"
"Must call compute_dijkstra() first. Path includes start but not root position."},
{"compute_astar_path", (PyCFunction)UIGrid::py_compute_astar_path, METH_VARARGS | METH_KEYWORDS,
"Compute A* path between two points. Args: x1, y1, x2, y2, diagonal_cost=1.41. Returns list of (x,y) tuples. Note: diagonal_cost is currently ignored (uses default 1.41)."},
"compute_astar_path(x1: int, y1: int, x2: int, y2: int, diagonal_cost: float = 1.41) -> List[Tuple[int, int]]\n\n"
"Compute A* path between two points.\n\n"
"Args:\n"
" x1: Starting X coordinate\n"
" y1: Starting Y coordinate\n"
" x2: Target X coordinate\n"
" y2: Target Y coordinate\n"
" diagonal_cost: Cost of diagonal movement (default: 1.41)\n\n"
"Returns:\n"
" List of (x, y) tuples representing the path, empty list if no path exists\n\n"
"Alternative A* implementation. Prefer find_path() for consistency."},
{NULL} // Sentinel
};
@ -1161,7 +1277,10 @@ PyGetSetDef UIGrid::getsetters[] = {
{"texture", (getter)UIGrid::get_texture, NULL, "Texture of the grid", NULL}, //TODO 7DRL-day2-item5
{"fill_color", (getter)UIGrid::get_fill_color, (setter)UIGrid::set_fill_color, "Background fill color of the grid", NULL},
{"perspective", (getter)UIGrid::get_perspective, (setter)UIGrid::set_perspective, "Entity perspective index (-1 for omniscient view)", NULL},
{"perspective", (getter)UIGrid::get_perspective, (setter)UIGrid::set_perspective,
"Entity perspective index for FOV rendering (-1 for omniscient view, 0+ for entity index). "
"When set to an entity index, only cells visible to that entity are rendered normally; "
"explored but not visible cells are darkened, and unexplored cells are black.", NULL},
{"z_index", (getter)UIDrawable::get_int, (setter)UIDrawable::set_int, "Z-order for rendering (lower values rendered first)", (void*)PyObjectsEnum::UIGRID},
{"name", (getter)UIDrawable::get_name, (setter)UIDrawable::set_name, "Name for finding elements", (void*)PyObjectsEnum::UIGRID},
UIDRAWABLE_GETSETTERS,

View File

@ -1,165 +1,208 @@
#!/usr/bin/env python3
"""Animation System Demo - Shows all animation capabilities"""
"""
Animation Demo: Grid Center & Entity Movement
=============================================
Demonstrates:
- Animated grid centering following entity
- Smooth entity movement along paths
- Perspective shifts with zoom transitions
- Field of view updates
"""
import mcrfpy
import math
import sys
# Create main scene
mcrfpy.createScene("animation_demo")
ui = mcrfpy.sceneUI("animation_demo")
mcrfpy.setScene("animation_demo")
# Setup scene
mcrfpy.createScene("anim_demo")
# Title
title = mcrfpy.Caption((400, 30), "McRogueFace Animation System Demo", mcrfpy.default_font)
title.size = 24
title.fill_color = (255, 255, 255)
# Note: centered property doesn't exist for Caption
# Create grid
grid = mcrfpy.Grid(grid_x=30, grid_y=20)
grid.fill_color = mcrfpy.Color(20, 20, 30)
# Simple map
for y in range(20):
for x in range(30):
cell = grid.at(x, y)
# Create walls around edges and some obstacles
if x == 0 or x == 29 or y == 0 or y == 19:
cell.walkable = False
cell.transparent = False
cell.color = mcrfpy.Color(40, 30, 30)
elif (x == 10 and 5 <= y <= 15) or (y == 10 and 5 <= x <= 25):
cell.walkable = False
cell.transparent = False
cell.color = mcrfpy.Color(60, 40, 40)
else:
cell.walkable = True
cell.transparent = True
cell.color = mcrfpy.Color(80, 80, 100)
# Create entities
player = mcrfpy.Entity(5, 5, grid=grid)
player.sprite_index = 64 # @
enemy = mcrfpy.Entity(25, 15, grid=grid)
enemy.sprite_index = 69 # E
# Update visibility
player.update_visibility()
enemy.update_visibility()
# UI setup
ui = mcrfpy.sceneUI("anim_demo")
ui.append(grid)
grid.position = (100, 100)
grid.size = (600, 400)
title = mcrfpy.Caption("Animation Demo - Grid Center & Entity Movement", 200, 20)
title.fill_color = mcrfpy.Color(255, 255, 255)
ui.append(title)
# 1. Position Animation Demo
pos_frame = mcrfpy.Frame(50, 100, 80, 80)
pos_frame.fill_color = (255, 100, 100)
pos_frame.outline = 2
ui.append(pos_frame)
status = mcrfpy.Caption("Press 1: Move Player | 2: Move Enemy | 3: Perspective Shift | Q: Quit", 100, 50)
status.fill_color = mcrfpy.Color(200, 200, 200)
ui.append(status)
pos_label = mcrfpy.Caption((50, 80), "Position Animation", mcrfpy.default_font)
pos_label.fill_color = (200, 200, 200)
ui.append(pos_label)
# 2. Size Animation Demo
size_frame = mcrfpy.Frame(200, 100, 50, 50)
size_frame.fill_color = (100, 255, 100)
size_frame.outline = 2
ui.append(size_frame)
size_label = mcrfpy.Caption((200, 80), "Size Animation", mcrfpy.default_font)
size_label.fill_color = (200, 200, 200)
ui.append(size_label)
# 3. Color Animation Demo
color_frame = mcrfpy.Frame(350, 100, 80, 80)
color_frame.fill_color = (255, 0, 0)
ui.append(color_frame)
color_label = mcrfpy.Caption((350, 80), "Color Animation", mcrfpy.default_font)
color_label.fill_color = (200, 200, 200)
ui.append(color_label)
# 4. Easing Functions Demo
easing_y = 250
easing_frames = []
easings = ["linear", "easeIn", "easeOut", "easeInOut", "easeInElastic", "easeOutBounce"]
for i, easing in enumerate(easings):
x = 50 + i * 120
frame = mcrfpy.Frame(x, easing_y, 20, 20)
frame.fill_color = (100, 150, 255)
ui.append(frame)
easing_frames.append((frame, easing))
label = mcrfpy.Caption((x, easing_y - 20), easing, mcrfpy.default_font)
label.size = 12
label.fill_color = (200, 200, 200)
ui.append(label)
# 5. Complex Animation Demo
complex_frame = mcrfpy.Frame(300, 350, 100, 100)
complex_frame.fill_color = (128, 128, 255)
complex_frame.outline = 3
ui.append(complex_frame)
complex_label = mcrfpy.Caption((300, 330), "Complex Multi-Property", mcrfpy.default_font)
complex_label.fill_color = (200, 200, 200)
ui.append(complex_label)
# Start animations
def start_animations(runtime):
# 1. Position animation - back and forth
x_anim = mcrfpy.Animation("x", 500.0, 3.0, "easeInOut")
x_anim.start(pos_frame)
# 2. Size animation - pulsing
w_anim = mcrfpy.Animation("w", 150.0, 2.0, "easeInOut")
h_anim = mcrfpy.Animation("h", 150.0, 2.0, "easeInOut")
w_anim.start(size_frame)
h_anim.start(size_frame)
# 3. Color animation - rainbow cycle
color_anim = mcrfpy.Animation("fill_color", (0, 255, 255, 255), 2.0, "linear")
color_anim.start(color_frame)
# 4. Easing demos - all move up with different easings
for frame, easing in easing_frames:
y_anim = mcrfpy.Animation("y", 150.0, 2.0, easing)
y_anim.start(frame)
# 5. Complex animation - multiple properties
cx_anim = mcrfpy.Animation("x", 500.0, 4.0, "easeInOut")
cy_anim = mcrfpy.Animation("y", 400.0, 4.0, "easeOut")
cw_anim = mcrfpy.Animation("w", 150.0, 4.0, "easeInElastic")
ch_anim = mcrfpy.Animation("h", 150.0, 4.0, "easeInElastic")
outline_anim = mcrfpy.Animation("outline", 10.0, 4.0, "linear")
cx_anim.start(complex_frame)
cy_anim.start(complex_frame)
cw_anim.start(complex_frame)
ch_anim.start(complex_frame)
outline_anim.start(complex_frame)
# Individual color component animations
r_anim = mcrfpy.Animation("fill_color.r", 255.0, 4.0, "easeInOut")
g_anim = mcrfpy.Animation("fill_color.g", 100.0, 4.0, "easeInOut")
b_anim = mcrfpy.Animation("fill_color.b", 50.0, 4.0, "easeInOut")
r_anim.start(complex_frame)
g_anim.start(complex_frame)
b_anim.start(complex_frame)
print("All animations started!")
# Reverse some animations
def reverse_animations(runtime):
# Position back
x_anim = mcrfpy.Animation("x", 50.0, 3.0, "easeInOut")
x_anim.start(pos_frame)
# Size back
w_anim = mcrfpy.Animation("w", 50.0, 2.0, "easeInOut")
h_anim = mcrfpy.Animation("h", 50.0, 2.0, "easeInOut")
w_anim.start(size_frame)
h_anim.start(size_frame)
# Color cycle continues
color_anim = mcrfpy.Animation("fill_color", (255, 0, 255, 255), 2.0, "linear")
color_anim.start(color_frame)
# Easing frames back down
for frame, easing in easing_frames:
y_anim = mcrfpy.Animation("y", 250.0, 2.0, easing)
y_anim.start(frame)
# Continue color cycle
def cycle_colors(runtime):
color_anim = mcrfpy.Animation("fill_color", (255, 255, 0, 255), 2.0, "linear")
color_anim.start(color_frame)
# Info text
info = mcrfpy.Caption((400, 550), "Watch as different properties animate with various easing functions!", mcrfpy.default_font)
info.fill_color = (255, 255, 200)
# Note: centered property doesn't exist for Caption
info = mcrfpy.Caption("Perspective: Player", 500, 70)
info.fill_color = mcrfpy.Color(100, 255, 100)
ui.append(info)
# Schedule animations
mcrfpy.setTimer("start", start_animations, 500)
mcrfpy.setTimer("reverse", reverse_animations, 4000)
mcrfpy.setTimer("cycle", cycle_colors, 2500)
# Movement functions
def move_player_demo():
"""Demo player movement with camera follow"""
# Calculate path to a destination
path = player.path_to(20, 10)
if not path:
status.text = "No path available!"
return
status.text = f"Moving player along {len(path)} steps..."
# Animate along path
for i, (x, y) in enumerate(path[:5]): # First 5 steps
delay = i * 500 # 500ms between steps
# Schedule movement
def move_step(dt, px=x, py=y):
# Animate entity position
anim_x = mcrfpy.Animation("x", float(px), 0.4, "easeInOut")
anim_y = mcrfpy.Animation("y", float(py), 0.4, "easeInOut")
anim_x.start(player)
anim_y.start(player)
# Update visibility
player.update_visibility()
# Animate camera to follow
center_x = px * 16 # Assuming 16x16 tiles
center_y = py * 16
cam_anim = mcrfpy.Animation("center", (center_x, center_y), 0.4, "easeOut")
cam_anim.start(grid)
mcrfpy.setTimer(f"player_move_{i}", move_step, delay)
# Exit handler
def on_key(key):
if key == "Escape":
mcrfpy.exit()
def move_enemy_demo():
"""Demo enemy movement"""
# Calculate path
path = enemy.path_to(10, 5)
if not path:
status.text = "Enemy has no path!"
return
status.text = f"Moving enemy along {len(path)} steps..."
# Animate along path
for i, (x, y) in enumerate(path[:5]): # First 5 steps
delay = i * 500
def move_step(dt, ex=x, ey=y):
anim_x = mcrfpy.Animation("x", float(ex), 0.4, "easeInOut")
anim_y = mcrfpy.Animation("y", float(ey), 0.4, "easeInOut")
anim_x.start(enemy)
anim_y.start(enemy)
enemy.update_visibility()
# If following enemy, update camera
if grid.perspective == 1:
center_x = ex * 16
center_y = ey * 16
cam_anim = mcrfpy.Animation("center", (center_x, center_y), 0.4, "easeOut")
cam_anim.start(grid)
mcrfpy.setTimer(f"enemy_move_{i}", move_step, delay)
mcrfpy.keypressScene(on_key)
def perspective_shift_demo():
"""Demo dramatic perspective shift"""
status.text = "Perspective shift in progress..."
# Phase 1: Zoom out
zoom_out = mcrfpy.Animation("zoom", 0.5, 1.5, "easeInExpo")
zoom_out.start(grid)
# Phase 2: Switch perspective at peak
def switch_perspective(dt):
if grid.perspective == 0:
grid.perspective = 1
info.text = "Perspective: Enemy"
info.fill_color = mcrfpy.Color(255, 100, 100)
target = enemy
else:
grid.perspective = 0
info.text = "Perspective: Player"
info.fill_color = mcrfpy.Color(100, 255, 100)
target = player
# Update camera to new target
center_x = target.x * 16
center_y = target.y * 16
cam_anim = mcrfpy.Animation("center", (center_x, center_y), 0.5, "linear")
cam_anim.start(grid)
mcrfpy.setTimer("switch_persp", switch_perspective, 1600)
# Phase 3: Zoom back in
def zoom_in(dt):
zoom_in_anim = mcrfpy.Animation("zoom", 1.0, 1.5, "easeOutExpo")
zoom_in_anim.start(grid)
status.text = "Perspective shift complete!"
mcrfpy.setTimer("zoom_in", zoom_in, 2100)
print("Animation demo started! Press Escape to exit.")
# Input handler
def handle_input(key, state):
if state != "start":
return
if key == "q":
print("Exiting demo...")
sys.exit(0)
elif key == "1":
move_player_demo()
elif key == "2":
move_enemy_demo()
elif key == "3":
perspective_shift_demo()
# Set scene
mcrfpy.setScene("anim_demo")
mcrfpy.keypressScene(handle_input)
# Initial setup
grid.perspective = 0
grid.zoom = 1.0
# Center on player initially
center_x = player.x * 16
center_y = player.y * 16
initial_cam = mcrfpy.Animation("center", (center_x, center_y), 0.5, "easeOut")
initial_cam.start(grid)
print("Animation Demo Started!")
print("======================")
print("Press 1: Animate player movement with camera follow")
print("Press 2: Animate enemy movement")
print("Press 3: Dramatic perspective shift with zoom")
print("Press Q: Quit")
print()
print("Watch how the grid center smoothly follows entities")
print("and how perspective shifts create cinematic effects!")