McRogueFace/docs/API_REFERENCE_COMPLETE.md
John McCardle f4343e1e82 Squashed commit of the following: [alpha_presentable]
Author: John McCardle <mccardle.john@gmail.com>
Co-Authored-By: Claude <noreply@anthropic.com>

commit dc47f2474c7b2642d368f9772894aed857527807
    the UIEntity rant

commit 673ca8e1b089ea670257fc04ae1a676ed95a40ed
    I forget when these tests were written, but I want them in the squash merge

commit 70c71565c684fa96e222179271ecb13a156d80ad
    Fix UI object segfault by switching from managed to manual weakref management

    The UI types (Frame, Caption, Sprite, Grid, Entity) were using
    Py_TPFLAGS_MANAGED_WEAKREF while also trying to manually create weakrefs
    for the PythonObjectCache. This is fundamentally incompatible - when
    Python manages weakrefs internally, PyWeakref_NewRef() cannot access the
    weakref list properly, causing segfaults.

    Changed all UI types to use manual weakref management (like PyTimer):
    - Restored weakreflist field in all UI type structures
    - Removed Py_TPFLAGS_MANAGED_WEAKREF from all UI type flags
    - Added tp_weaklistoffset for all UI types in module initialization
    - Initialize weakreflist=NULL in tp_new and init methods
    - Call PyObject_ClearWeakRefs() in dealloc functions

    This allows the PythonObjectCache to continue working correctly,
    maintaining Python object identity for C++ objects across the boundary.

    Fixes segfault when creating UI objects (e.g., Caption, Grid) that was
    preventing tutorial scripts from running.

This is the bulk of the required behavior for Issue #126.
that issure isn't ready for closure yet; several other sub-issues left.
    closes #110
    mention issue #109 - resolves some __init__ related nuisances

commit 3dce3ec539ae99e32d869007bf3f49d03e4e2f89
    Refactor timer system for cleaner architecture and enhanced functionality

    Major improvements to the timer system:
    - Unified all timer logic in the Timer class (C++)
    - Removed PyTimerCallable subclass, now using PyCallable directly
    - Timer objects are now passed to callbacks as first argument
    - Added 'once' parameter for one-shot timers that auto-stop
    - Implemented proper PythonObjectCache integration with weakref support

    API enhancements:
    - New callback signature: callback(timer, runtime) instead of just (runtime)
    - Timer objects expose: name, interval, remaining, paused, active, once properties
    - Methods: pause(), resume(), cancel(), restart()
    - Comprehensive documentation with examples
    - Enhanced repr showing timer state (active/paused/once/remaining time)

    This cleanup follows the UIEntity/PyUIEntity pattern and makes the timer
    system more Pythonic while maintaining backward compatibility through
    the legacy setTimer/delTimer API.

    closes #121

commit 145834cfc31b8dabc4cb3591b9cb4ed99fc8b964
    Implement Python object cache to preserve derived types in collections

    Add a global cache system that maintains weak references to Python objects,
    ensuring that derived Python classes maintain their identity when stored in
    and retrieved from C++ collections.

    Key changes:
    - Add PythonObjectCache singleton with serial number system
    - Each cacheable object (UIDrawable, UIEntity, Timer, Animation) gets unique ID
    - Cache stores weak references to prevent circular reference memory leaks
    - Update all UI type definitions to support weak references (Py_TPFLAGS_MANAGED_WEAKREF)
    - Enable subclassing for all UI types (Py_TPFLAGS_BASETYPE)
    - Collections check cache before creating new Python wrappers
    - Register objects in cache during __init__ methods
    - Clean up cache entries in C++ destructors

    This ensures that Python code like:
    ```python
    class MyFrame(mcrfpy.Frame):
        def __init__(self):
            super().__init__()
            self.custom_data = "preserved"

    frame = MyFrame()
    scene.ui.append(frame)
    retrieved = scene.ui[0]  # Same MyFrame instance with custom_data intact
    ```

    Works correctly, with retrieved maintaining the derived type and custom attributes.

    Closes #112

commit c5e7e8e298
    Update test demos for new Python API and entity system

    - Update all text input demos to use new Entity constructor signature
    - Fix pathfinding showcase to work with new entity position handling
    - Remove entity_waypoints tracking in favor of simplified movement
    - Delete obsolete exhaustive_api_demo.py (superseded by newer demos)
    - Adjust entity creation calls to match Entity((x, y), texture, sprite_index) pattern

commit 6d29652ae7
    Update animation demo suite with crash fixes and improvements

    - Add warnings about AnimationManager segfault bug in sizzle_reel_final.py
    - Create sizzle_reel_final_fixed.py that works around the crash by hiding objects instead of removing them
    - Increase font sizes for better visibility in demos
    - Extend demo durations for better showcase of animations
    - Remove debug prints from animation_sizzle_reel_working.py
    - Minor cleanup and improvements to all animation demos

commit a010e5fa96
    Update game scripts for new Python API

    - Convert entity position access from tuple to x/y properties
    - Update caption size property to font_size
    - Fix grid boundary checks to use grid_size instead of exceptions
    - Clean up demo timer on menu exit to prevent callbacks

    These changes adapt the game scripts to work with the new standardized
    Python API constructors and property names.

commit 9c8d6c4591
    Fix click event z-order handling in PyScene

    Changed click detection to properly respect z-index by:
    - Sorting ui_elements in-place when needed (same as render order)
    - Using reverse iterators to check highest z-index elements first
    - This ensures top-most elements receive clicks before lower ones

commit dcd1b0ca33
    Add roguelike tutorial implementation files

    Implement Parts 0-2 of the classic roguelike tutorial adapted for McRogueFace:
    - Part 0: Basic grid setup and tile rendering
    - Part 1: Drawing '@' symbol and basic movement
    - Part 1b: Variant with sprite-based player
    - Part 2: Entity system and NPC implementation with three movement variants:
      - part_2.py: Standard implementation
      - part_2-naive.py: Naive movement approach
      - part_2-onemovequeued.py: Queued movement system

    Includes tutorial assets:
    - tutorial2.png: Tileset for dungeon tiles
    - tutorial_hero.png: Player sprite sheet

commit 6813fb5129
    Standardize Python API constructors and remove PyArgHelpers

    - Remove PyArgHelpers.h and all macro-based argument parsing
    - Convert all UI class constructors to use PyArg_ParseTupleAndKeywords
    - Standardize constructor signatures across UICaption, UIEntity, UIFrame, UIGrid, and UISprite
    - Replace PYARGHELPER_SINGLE/MULTI macros with explicit argument parsing
    - Improve error messages and argument validation
    - Maintain backward compatibility with existing Python code

    This change improves code maintainability and consistency across the Python API.

commit 6f67fbb51e
    Fix animation callback crashes from iterator invalidation (#119)

    Resolved segfaults caused by creating new animations from within
    animation callbacks. The issue was iterator invalidation in
    AnimationManager::update() when callbacks modified the active
    animations vector.

    Changes:
    - Add deferred animation queue to AnimationManager
    - New animations created during update are queued and added after
    - Set isUpdating flag to track when in update loop
    - Properly handle Animation destructor during callback execution
    - Add clearCallback() method for safe cleanup scenarios

    This fixes the "free(): invalid pointer" and "malloc(): unaligned
    fastbin chunk detected" errors that occurred with rapid animation
    creation in callbacks.

commit eb88c7b3aa
    Add animation completion callbacks (#119)

    Implement callbacks that fire when animations complete, enabling direct
    causality between animation end and game state changes. This eliminates
    race conditions from parallel timer workarounds.

    - Add optional callback parameter to Animation constructor
    - Callbacks execute synchronously when animation completes
    - Proper Python reference counting with GIL safety
    - Callbacks receive (anim, target) parameters (currently None)
    - Exception handling prevents crashes from Python errors

    Example usage:
    ```python
    def on_complete(anim, target):
        player_moving = False

    anim = mcrfpy.Animation("x", 300.0, 1.0, "easeOut", callback=on_complete)
    anim.start(player)
    ```

    closes #119

commit 9fb428dd01
    Update ROADMAP with GitHub issue numbers (#111-#125)

    Added issue numbers from GitHub tracker to roadmap items:
    - #111: Grid Click Events Broken in Headless
    - #112: Object Splitting Bug (Python type preservation)
    - #113: Batch Operations for Grid
    - #114: CellView API
    - #115: SpatialHash Implementation
    - #116: Dirty Flag System
    - #117: Memory Pool for Entities
    - #118: Scene as Drawable
    - #119: Animation Completion Callbacks
    - #120: Animation Property Locking
    - #121: Timer Object System
    - #122: Parent-Child UI System
    - #123: Grid Subgrid System
    - #124: Grid Point Animation
    - #125: GitHub Issues Automation

    Also updated existing references:
    - #101/#110: Constructor standardization
    - #109: Vector class indexing

    Note: Tutorial-specific items and Python-implementable features
    (input queue, collision reservation) are not tracked as engine issues.

commit 062e4dadc4
    Fix animation segfaults with RAII weak_ptr implementation

    Resolved two critical segmentation faults in AnimationManager:
    1. Race condition when creating multiple animations in timer callbacks
    2. Exit crash when animations outlive their target objects

    Changes:
    - Replace raw pointers with std::weak_ptr for automatic target invalidation
    - Add Animation::complete() to jump animations to final value
    - Add Animation::hasValidTarget() to check if target still exists
    - Update AnimationManager to auto-remove invalid animations
    - Add AnimationManager::clear() call to GameEngine::cleanup()
    - Update Python bindings to pass shared_ptr instead of raw pointers

    This ensures animations can never reference destroyed objects, following
    proper RAII principles. Tested with sizzle_reel_final.py and stress
    tests creating/destroying hundreds of animated objects.

commit 98fc49a978
    Directory structure cleanup and organization overhaul
2025-07-15 21:30:49 -04:00

1158 lines
23 KiB
Markdown

# McRogueFace API Reference
*Generated on 2025-07-15 21:28:42*
## Overview
McRogueFace Python API
Core game engine interface for creating roguelike games with Python.
This module provides:
- Scene management (createScene, setScene, currentScene)
- UI components (Frame, Caption, Sprite, Grid)
- Entity system for game objects
- Audio playback (sound effects and music)
- Timer system for scheduled events
- Input handling
- Performance metrics
Example:
import mcrfpy
# Create a new scene
mcrfpy.createScene('game')
mcrfpy.setScene('game')
# Add UI elements
frame = mcrfpy.Frame(10, 10, 200, 100)
caption = mcrfpy.Caption('Hello World', 50, 50)
mcrfpy.sceneUI().extend([frame, caption])
## Table of Contents
- [Functions](#functions)
- [Scene Management](#scene-management)
- [Audio](#audio)
- [UI Utilities](#ui-utilities)
- [System](#system)
- [Classes](#classes)
- [UI Components](#ui-components)
- [Collections](#collections)
- [System Types](#system-types)
- [Other Classes](#other-classes)
- [Automation Module](#automation-module)
## Functions
### Scene Management
### `createScene(name: str) -> None`
Create a new empty scene with the given name.
**Arguments:**
- `name` (*str*): Unique name for the new scene
**Raises:** ValueError: If a scene with this name already exists
**Note:** The scene is created but not made active. Use setScene() to switch to it.
**Example:**
```python
mcrfpy.createScene("game_over")
```
---
### `setScene(scene: str, transition: str = None, duration: float = 0.0) -> None`
Switch to a different scene with optional transition effect.
**Arguments:**
- `scene` (*str*): Name of the scene to switch to
- `transition` (*str*): Transition type: "fade", "slide_left", "slide_right", "slide_up", "slide_down"
- `duration` (*float*): Transition duration in seconds (default: 0.0 for instant)
**Raises:** KeyError: If the scene doesn't exist
**Example:**
```python
mcrfpy.setScene("game", "fade", 0.5)
```
---
### `currentScene() -> str`
Get the name of the currently active scene.
**Returns:** str: Name of the current scene
**Example:**
```python
scene_name = mcrfpy.currentScene()
```
---
### `sceneUI(scene: str = None) -> UICollection`
Get all UI elements for a scene.
**Arguments:**
- `scene` (*str*): Scene name. If None, uses current scene
**Returns:** UICollection: All UI elements in the scene
**Raises:** KeyError: If the specified scene doesn't exist
**Example:**
```python
ui_elements = mcrfpy.sceneUI("game")
```
---
### `keypressScene(handler: callable) -> None`
Set the keyboard event handler for the current scene.
**Arguments:**
- `handler` (*callable*): Function that receives (key_name: str, is_pressed: bool)
**Example:**
```python
def on_key(key, pressed):
if key == "SPACE" and pressed:
player.jump()
mcrfpy.keypressScene(on_key)
```
---
### Audio
### `createSoundBuffer(filename: str) -> int`
Load a sound effect from a file and return its buffer ID.
**Arguments:**
- `filename` (*str*): Path to the sound file (WAV, OGG, FLAC)
**Returns:** int: Buffer ID for use with playSound()
**Raises:** RuntimeError: If the file cannot be loaded
**Example:**
```python
jump_sound = mcrfpy.createSoundBuffer("assets/jump.wav")
```
---
### `loadMusic(filename: str, loop: bool = True) -> None`
Load and immediately play background music from a file.
**Arguments:**
- `filename` (*str*): Path to the music file (WAV, OGG, FLAC)
- `loop` (*bool*): Whether to loop the music (default: True)
**Note:** Only one music track can play at a time. Loading new music stops the current track.
**Example:**
```python
mcrfpy.loadMusic("assets/background.ogg", True)
```
---
### `playSound(buffer_id: int) -> None`
Play a sound effect using a previously loaded buffer.
**Arguments:**
- `buffer_id` (*int*): Sound buffer ID returned by createSoundBuffer()
**Raises:** RuntimeError: If the buffer ID is invalid
**Example:**
```python
mcrfpy.playSound(jump_sound)
```
---
### `getMusicVolume() -> int`
Get the current music volume level.
**Returns:** int: Current volume (0-100)
**Example:**
```python
current_volume = mcrfpy.getMusicVolume()
```
---
### `getSoundVolume() -> int`
Get the current sound effects volume level.
**Returns:** int: Current volume (0-100)
**Example:**
```python
current_volume = mcrfpy.getSoundVolume()
```
---
### `setMusicVolume(volume: int) -> None`
Set the global music volume.
**Arguments:**
- `volume` (*int*): Volume level from 0 (silent) to 100 (full volume)
**Example:**
```python
mcrfpy.setMusicVolume(50) # Set to 50% volume
```
---
### `setSoundVolume(volume: int) -> None`
Set the global sound effects volume.
**Arguments:**
- `volume` (*int*): Volume level from 0 (silent) to 100 (full volume)
**Example:**
```python
mcrfpy.setSoundVolume(75) # Set to 75% volume
```
---
### UI Utilities
### `find(name: str, scene: str = None) -> UIDrawable | None`
Find the first UI element with the specified name.
**Arguments:**
- `name` (*str*): Exact name to search for
- `scene` (*str*): Scene to search in (default: current scene)
**Returns:** UIDrawable or None: The found element, or None if not found
**Note:** Searches scene UI elements and entities within grids.
**Example:**
```python
button = mcrfpy.find("start_button")
```
---
### `findAll(pattern: str, scene: str = None) -> list`
Find all UI elements matching a name pattern.
**Arguments:**
- `pattern` (*str*): Name pattern with optional wildcards (* matches any characters)
- `scene` (*str*): Scene to search in (default: current scene)
**Returns:** list: All matching UI elements and entities
**Example:**
```python
enemies = mcrfpy.findAll("enemy_*")
```
---
### System
### `exit() -> None`
Cleanly shut down the game engine and exit the application.
**Note:** This immediately closes the window and terminates the program.
**Example:**
```python
mcrfpy.exit()
```
---
### `getMetrics() -> dict`
Get current performance metrics.
**Returns:** dict: Performance data with keys:
- frame_time: Last frame duration in seconds
- avg_frame_time: Average frame time
- fps: Frames per second
- draw_calls: Number of draw calls
- ui_elements: Total UI element count
- visible_elements: Visible element count
- current_frame: Frame counter
- runtime: Total runtime in seconds
**Example:**
```python
metrics = mcrfpy.getMetrics()
```
---
### `setTimer(name: str, handler: callable, interval: int) -> None`
Create or update a recurring timer.
**Arguments:**
- `name` (*str*): Unique identifier for the timer
- `handler` (*callable*): Function called with (runtime: float) parameter
- `interval` (*int*): Time between calls in milliseconds
**Note:** If a timer with this name exists, it will be replaced.
**Example:**
```python
def update_score(runtime):
score += 1
mcrfpy.setTimer("score_update", update_score, 1000)
```
---
### `delTimer(name: str) -> None`
Stop and remove a timer.
**Arguments:**
- `name` (*str*): Timer identifier to remove
**Note:** No error is raised if the timer doesn't exist.
**Example:**
```python
mcrfpy.delTimer("score_update")
```
---
### `setScale(multiplier: float) -> None`
Scale the game window size.
**Arguments:**
- `multiplier` (*float*): Scale factor (e.g., 2.0 for double size)
**Note:** The internal resolution remains 1024x768, but the window is scaled.
**Example:**
```python
mcrfpy.setScale(2.0) # Double the window size
```
---
## Classes
### UI Components
### class `Frame`
A rectangular frame UI element that can contain other drawable elements.
#### Methods
#### `resize(width, height)`
Resize the element to new dimensions.
**Arguments:**
- `width` (*float*): New width in pixels
- `height` (*float*): New height in pixels
**Note:** For Caption and Sprite, this may not change actual size if determined by content.
#### `move(dx, dy)`
Move the element by a relative offset.
**Arguments:**
- `dx` (*float*): Horizontal offset in pixels
- `dy` (*float*): Vertical offset in pixels
**Note:** This modifies the x and y position properties by the given amounts.
#### `get_bounds()`
Get the bounding rectangle of this drawable element.
**Returns:** tuple: (x, y, width, height) representing the element's bounds
**Note:** The bounds are in screen coordinates and account for current position and size.
---
### class `Caption`
A text display UI element with customizable font and styling.
#### Methods
#### `resize(width, height)`
Resize the element to new dimensions.
**Arguments:**
- `width` (*float*): New width in pixels
- `height` (*float*): New height in pixels
**Note:** For Caption and Sprite, this may not change actual size if determined by content.
#### `move(dx, dy)`
Move the element by a relative offset.
**Arguments:**
- `dx` (*float*): Horizontal offset in pixels
- `dy` (*float*): Vertical offset in pixels
**Note:** This modifies the x and y position properties by the given amounts.
#### `get_bounds()`
Get the bounding rectangle of this drawable element.
**Returns:** tuple: (x, y, width, height) representing the element's bounds
**Note:** The bounds are in screen coordinates and account for current position and size.
---
### class `Sprite`
A sprite UI element that displays a texture or portion of a texture atlas.
#### Methods
#### `resize(width, height)`
Resize the element to new dimensions.
**Arguments:**
- `width` (*float*): New width in pixels
- `height` (*float*): New height in pixels
**Note:** For Caption and Sprite, this may not change actual size if determined by content.
#### `move(dx, dy)`
Move the element by a relative offset.
**Arguments:**
- `dx` (*float*): Horizontal offset in pixels
- `dy` (*float*): Vertical offset in pixels
**Note:** This modifies the x and y position properties by the given amounts.
#### `get_bounds()`
Get the bounding rectangle of this drawable element.
**Returns:** tuple: (x, y, width, height) representing the element's bounds
**Note:** The bounds are in screen coordinates and account for current position and size.
---
### class `Grid`
A grid-based tilemap UI element for rendering tile-based levels and game worlds.
#### Methods
#### `resize(width, height)`
Resize the element to new dimensions.
**Arguments:**
- `width` (*float*): New width in pixels
- `height` (*float*): New height in pixels
**Note:** For Caption and Sprite, this may not change actual size if determined by content.
#### `at(x, y)`
Get the GridPoint at the specified grid coordinates.
**Arguments:**
- `x` (*int*): Grid x coordinate
- `y` (*int*): Grid y coordinate
**Returns:** GridPoint or None: The grid point at (x, y), or None if out of bounds
#### `move(dx, dy)`
Move the element by a relative offset.
**Arguments:**
- `dx` (*float*): Horizontal offset in pixels
- `dy` (*float*): Vertical offset in pixels
**Note:** This modifies the x and y position properties by the given amounts.
#### `get_bounds()`
Get the bounding rectangle of this drawable element.
**Returns:** tuple: (x, y, width, height) representing the element's bounds
**Note:** The bounds are in screen coordinates and account for current position and size.
---
### class `Entity`
Game entity that can be placed in a Grid.
#### Methods
#### `move(dx, dy)`
Move the element by a relative offset.
**Arguments:**
- `dx` (*float*): Horizontal offset in pixels
- `dy` (*float*): Vertical offset in pixels
**Note:** This modifies the x and y position properties by the given amounts.
#### `at(x, y)`
Check if this entity is at the specified grid coordinates.
**Arguments:**
- `x` (*int*): Grid x coordinate to check
- `y` (*int*): Grid y coordinate to check
**Returns:** bool: True if entity is at position (x, y), False otherwise
#### `get_bounds()`
Get the bounding rectangle of this drawable element.
**Returns:** tuple: (x, y, width, height) representing the element's bounds
**Note:** The bounds are in screen coordinates and account for current position and size.
#### `die()`
Remove this entity from its parent grid.
**Note:** The entity object remains valid but is no longer rendered or updated.
#### `resize(width, height)`
Resize the element to new dimensions.
**Arguments:**
- `width` (*float*): New width in pixels
- `height` (*float*): New height in pixels
**Note:** For Caption and Sprite, this may not change actual size if determined by content.
#### `index()`
Get the index of this entity in its parent grid's entity list.
**Returns:** int: Index position, or -1 if not in a grid
---
### Collections
### class `EntityCollection`
Container for Entity objects in a Grid. Supports iteration and indexing.
#### Methods
#### `remove(entity)`
Remove the first occurrence of an entity from the collection.
**Arguments:**
- `entity` (*Entity*): The entity to remove
**Raises:** ValueError: If entity is not in collection
#### `extend(iterable)`
Add all entities from an iterable to the collection.
**Arguments:**
- `iterable` (*Iterable[Entity]*): Entities to add
#### `count(entity)`
Count the number of occurrences of an entity in the collection.
**Arguments:**
- `entity` (*Entity*): The entity to count
**Returns:** int: Number of times entity appears in collection
#### `index(entity)`
Find the index of the first occurrence of an entity.
**Arguments:**
- `entity` (*Entity*): The entity to find
**Returns:** int: Index of entity in collection
**Raises:** ValueError: If entity is not in collection
#### `append(entity)`
Add an entity to the end of the collection.
**Arguments:**
- `entity` (*Entity*): The entity to add
---
### class `UICollection`
Container for UI drawable elements. Supports iteration and indexing.
#### Methods
#### `remove(drawable)`
Remove the first occurrence of a drawable from the collection.
**Arguments:**
- `drawable` (*UIDrawable*): The drawable to remove
**Raises:** ValueError: If drawable is not in collection
#### `extend(iterable)`
Add all drawables from an iterable to the collection.
**Arguments:**
- `iterable` (*Iterable[UIDrawable]*): Drawables to add
#### `count(drawable)`
Count the number of occurrences of a drawable in the collection.
**Arguments:**
- `drawable` (*UIDrawable*): The drawable to count
**Returns:** int: Number of times drawable appears in collection
#### `index(drawable)`
Find the index of the first occurrence of a drawable.
**Arguments:**
- `drawable` (*UIDrawable*): The drawable to find
**Returns:** int: Index of drawable in collection
**Raises:** ValueError: If drawable is not in collection
#### `append(drawable)`
Add a drawable element to the end of the collection.
**Arguments:**
- `drawable` (*UIDrawable*): The drawable element to add
---
### class `UICollectionIter`
Iterator for UICollection. Automatically created when iterating over a UICollection.
---
### class `UIEntityCollectionIter`
Iterator for EntityCollection. Automatically created when iterating over an EntityCollection.
---
### System Types
### class `Color`
RGBA color representation.
#### Methods
#### `to_hex()`
Convert this Color to a hexadecimal string.
**Returns:** str: Hex color string in format "#RRGGBB"
**Example:**
```python
hex_str = color.to_hex() # Returns "#FF0000"
```
#### `from_hex(hex_string)`
Create a Color from a hexadecimal color string.
**Arguments:**
- `hex_string` (*str*): Hex color string (e.g., "#FF0000" or "FF0000")
**Returns:** Color: New Color object from hex string
**Example:**
```python
red = Color.from_hex("#FF0000")
```
#### `lerp(other, t)`
Linearly interpolate between this color and another.
**Arguments:**
- `other` (*Color*): The color to interpolate towards
- `t` (*float*): Interpolation factor from 0.0 to 1.0
**Returns:** Color: New interpolated Color object
**Example:**
```python
mixed = red.lerp(blue, 0.5) # 50% between red and blue
```
---
### class `Vector`
2D vector for positions and directions.
#### Methods
#### `magnitude()`
Calculate the length/magnitude of this vector.
**Returns:** float: The magnitude of the vector
#### `normalize()`
Return a unit vector in the same direction.
**Returns:** Vector: New normalized vector with magnitude 1.0
**Raises:** ValueError: If vector has zero magnitude
#### `dot(other)`
Calculate the dot product with another vector.
**Arguments:**
- `other` (*Vector*): The other vector
**Returns:** float: Dot product of the two vectors
#### `distance_to(other)`
Calculate the distance to another vector.
**Arguments:**
- `other` (*Vector*): The other vector
**Returns:** float: Distance between the two vectors
#### `copy()`
Create a copy of this vector.
**Returns:** Vector: New Vector object with same x and y values
#### `angle()`
Get the angle of this vector in radians.
**Returns:** float: Angle in radians from positive x-axis
#### `magnitude_squared()`
Calculate the squared magnitude of this vector.
**Returns:** float: The squared magnitude (faster than magnitude())
**Note:** Use this for comparisons to avoid expensive square root calculation.
---
### class `Texture`
Texture object for image data.
---
### class `Font`
Font object for text rendering.
---
### Other Classes
### class `Animation`
Animate UI element properties over time.
#### Properties
- **`property`**: str: Name of the property being animated (e.g., "x", "y", "scale")
- **`duration`**: float: Total duration of the animation in seconds
- **`elapsed_time`**: float: Time elapsed since animation started (read-only)
- **`current_value`**: float: Current interpolated value of the animation (read-only)
- **`is_running`**: bool: True if animation is currently running (read-only)
- **`is_finished`**: bool: True if animation has completed (read-only)
#### Methods
#### `get_current_value()`
Get the current interpolated value of the animation.
**Returns:** float: Current animation value between start and end
#### `update(delta_time)`
Update the animation by the given time delta.
**Arguments:**
- `delta_time` (*float*): Time elapsed since last update in seconds
**Returns:** bool: True if animation is still running, False if finished
#### `start(target)`
Start the animation on a target UI element.
**Arguments:**
- `target` (*UIDrawable*): The UI element to animate
**Note:** The target must have the property specified in the animation constructor.
---
### class `Drawable`
Base class for all drawable UI elements.
#### Methods
#### `resize(width, height)`
Resize the element to new dimensions.
**Arguments:**
- `width` (*float*): New width in pixels
- `height` (*float*): New height in pixels
**Note:** For Caption and Sprite, this may not change actual size if determined by content.
#### `move(dx, dy)`
Move the element by a relative offset.
**Arguments:**
- `dx` (*float*): Horizontal offset in pixels
- `dy` (*float*): Vertical offset in pixels
**Note:** This modifies the x and y position properties by the given amounts.
#### `get_bounds()`
Get the bounding rectangle of this drawable element.
**Returns:** tuple: (x, y, width, height) representing the element's bounds
**Note:** The bounds are in screen coordinates and account for current position and size.
---
### class `GridPoint`
Represents a single tile in a Grid.
#### Properties
- **`x`**: int: Grid x coordinate of this point
- **`y`**: int: Grid y coordinate of this point
- **`texture_index`**: int: Index of the texture/sprite to display at this point
- **`solid`**: bool: Whether this point blocks movement
- **`transparent`**: bool: Whether this point allows light/vision through
- **`color`**: Color: Color tint applied to the texture at this point
---
### class `GridPointState`
State information for a GridPoint.
#### Properties
- **`visible`**: bool: Whether this point is currently visible to the player
- **`discovered`**: bool: Whether this point has been discovered/explored
- **`custom_flags`**: int: Bitfield for custom game-specific flags
---
### class `Scene`
Base class for object-oriented scenes.
#### Methods
#### `register_keyboard(callable)`
Register a keyboard event handler function for the scene.
**Arguments:**
- `callable` (*callable*): Function that takes (key: str, action: str) parameters
**Note:** Alternative to overriding the on_keypress method when subclassing Scene objects.
**Example:**
```python
def handle_keyboard(key, action):
print(f"Key '{key}' was {action}")
scene.register_keyboard(handle_keyboard)
```
#### `get_ui()`
Get the UI element collection for this scene.
**Returns:** UICollection: Collection of all UI elements in this scene
#### `activate()`
Make this scene the active scene.
**Note:** Equivalent to calling setScene() with this scene's name.
#### `keypress(handler)`
Register a keyboard handler function for this scene.
**Arguments:**
- `handler` (*callable*): Function that takes (key_name: str, is_pressed: bool)
**Note:** Alternative to overriding the on_keypress method.
---
### class `Timer`
Timer object for scheduled callbacks.
#### Methods
#### `pause()`
Pause the timer, stopping its callback execution.
**Note:** Use resume() to continue the timer from where it was paused.
#### `resume()`
Resume a paused timer.
**Note:** Has no effect if timer is not paused.
#### `restart()`
Restart the timer from the beginning.
**Note:** Resets the timer's internal clock to zero.
#### `cancel()`
Cancel the timer and remove it from the system.
**Note:** After cancelling, the timer object cannot be reused.
---
### class `Window`
Window singleton for accessing and modifying the game window properties.
#### Methods
#### `screenshot(filename)`
Take a screenshot and save it to a file.
**Arguments:**
- `filename` (*str*): Path where to save the screenshot
**Note:** Supports PNG, JPG, and BMP formats based on file extension.
#### `get()`
Get the Window singleton instance.
**Returns:** Window: The singleton window object
**Note:** This is a static method that returns the same instance every time.
#### `center()`
Center the window on the screen.
**Note:** Only works if the window is not fullscreen.
---
## Automation Module
The `mcrfpy.automation` module provides testing and automation capabilities.
### `automation.click`
Click at position
---
### `automation.doubleClick`
Double click at position
---
### `automation.dragRel`
Drag mouse relative to current position
---
### `automation.dragTo`
Drag mouse to position
---
### `automation.hotkey`
Press a hotkey combination (e.g., hotkey('ctrl', 'c'))
---
### `automation.keyDown`
Press and hold a key
---
### `automation.keyUp`
Release a key
---
### `automation.middleClick`
Middle click at position
---
### `automation.mouseDown`
Press mouse button
---
### `automation.mouseUp`
Release mouse button
---
### `automation.moveRel`
Move mouse relative to current position
---
### `automation.moveTo`
Move mouse to absolute position
---
### `automation.onScreen`
Check if coordinates are within screen bounds
---
### `automation.position`
Get current mouse position as (x, y) tuple
---
### `automation.rightClick`
Right click at position
---
### `automation.screenshot`
Save a screenshot to the specified file
---
### `automation.scroll`
Scroll wheel at position
---
### `automation.size`
Get screen size as (width, height) tuple
---
### `automation.tripleClick`
Triple click at position
---
### `automation.typewrite`
Type text with optional interval between keystrokes
---