diff --git a/tools/generate_api_docs.py b/tools/generate_api_docs.py
deleted file mode 100644
index d1e100f..0000000
--- a/tools/generate_api_docs.py
+++ /dev/null
@@ -1,482 +0,0 @@
-#!/usr/bin/env python3
-"""Generate API reference documentation for McRogueFace.
-
-This script generates comprehensive API documentation in multiple formats:
-- Markdown for GitHub/documentation sites
-- HTML for local browsing
-- RST for Sphinx integration (future)
-"""
-
-import os
-import sys
-import inspect
-import datetime
-from typing import Dict, List, Any, Optional
-from pathlib import Path
-
-# We need to run this with McRogueFace as the interpreter
-# so mcrfpy is available
-import mcrfpy
-
-def escape_markdown(text: str) -> str:
- """Escape special markdown characters."""
- if not text:
- return ""
- # Escape backticks in inline code
- return text.replace("`", "\\`")
-
-def format_signature(name: str, doc: str) -> str:
- """Extract and format function signature from docstring."""
- if not doc:
- return f"{name}(...)"
-
- lines = doc.strip().split('\n')
- if lines and '(' in lines[0]:
- # First line contains signature
- return lines[0].split('->')[0].strip()
-
- return f"{name}(...)"
-
-def get_class_info(cls: type) -> Dict[str, Any]:
- """Extract comprehensive information about a class."""
- info = {
- 'name': cls.__name__,
- 'doc': cls.__doc__ or "",
- 'methods': [],
- 'properties': [],
- 'bases': [base.__name__ for base in cls.__bases__ if base.__name__ != 'object'],
- }
-
- # Get all attributes
- for attr_name in sorted(dir(cls)):
- if attr_name.startswith('_') and not attr_name.startswith('__'):
- continue
-
- try:
- attr = getattr(cls, attr_name)
-
- if isinstance(attr, property):
- prop_info = {
- 'name': attr_name,
- 'doc': (attr.fget.__doc__ if attr.fget else "") or "",
- 'readonly': attr.fset is None
- }
- info['properties'].append(prop_info)
- elif callable(attr) and not attr_name.startswith('__'):
- method_info = {
- 'name': attr_name,
- 'doc': attr.__doc__ or "",
- 'signature': format_signature(attr_name, attr.__doc__)
- }
- info['methods'].append(method_info)
- except:
- pass
-
- return info
-
-def get_function_info(func: Any, name: str) -> Dict[str, Any]:
- """Extract information about a function."""
- return {
- 'name': name,
- 'doc': func.__doc__ or "",
- 'signature': format_signature(name, func.__doc__)
- }
-
-def generate_markdown_class(cls_info: Dict[str, Any]) -> List[str]:
- """Generate markdown documentation for a class."""
- lines = []
-
- # Class header
- lines.append(f"### class `{cls_info['name']}`")
- if cls_info['bases']:
- lines.append(f"*Inherits from: {', '.join(cls_info['bases'])}*")
- lines.append("")
-
- # Class description
- if cls_info['doc']:
- doc_lines = cls_info['doc'].strip().split('\n')
- # First line is usually the constructor signature
- if doc_lines and '(' in doc_lines[0]:
- lines.append(f"```python")
- lines.append(doc_lines[0])
- lines.append("```")
- lines.append("")
- # Rest is description
- if len(doc_lines) > 2:
- lines.extend(doc_lines[2:])
- lines.append("")
- else:
- lines.extend(doc_lines)
- lines.append("")
-
- # Properties
- if cls_info['properties']:
- lines.append("#### Properties")
- lines.append("")
- for prop in cls_info['properties']:
- readonly = " *(readonly)*" if prop['readonly'] else ""
- lines.append(f"- **`{prop['name']}`**{readonly}")
- if prop['doc']:
- lines.append(f" - {prop['doc'].strip()}")
- lines.append("")
-
- # Methods
- if cls_info['methods']:
- lines.append("#### Methods")
- lines.append("")
- for method in cls_info['methods']:
- lines.append(f"##### `{method['signature']}`")
- if method['doc']:
- # Parse docstring for better formatting
- doc_lines = method['doc'].strip().split('\n')
- # Skip the signature line if it's repeated
- start = 1 if doc_lines and method['name'] in doc_lines[0] else 0
- for line in doc_lines[start:]:
- lines.append(line)
- lines.append("")
-
- lines.append("---")
- lines.append("")
- return lines
-
-def generate_markdown_function(func_info: Dict[str, Any]) -> List[str]:
- """Generate markdown documentation for a function."""
- lines = []
-
- lines.append(f"### `{func_info['signature']}`")
- lines.append("")
-
- if func_info['doc']:
- doc_lines = func_info['doc'].strip().split('\n')
- # Skip signature line if present
- start = 1 if doc_lines and func_info['name'] in doc_lines[0] else 0
-
- # Process documentation sections
- in_section = None
- for line in doc_lines[start:]:
- if line.strip() in ['Args:', 'Returns:', 'Raises:', 'Note:', 'Example:']:
- in_section = line.strip()
- lines.append(f"**{in_section}**")
- elif in_section and line.strip():
- # Indent content under sections
- lines.append(f"{line}")
- else:
- lines.append(line)
- lines.append("")
-
- lines.append("---")
- lines.append("")
- return lines
-
-def generate_markdown_docs() -> str:
- """Generate complete markdown API documentation."""
- lines = []
-
- # Header
- lines.append("# McRogueFace API Reference")
- lines.append("")
- lines.append(f"*Generated on {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}*")
- lines.append("")
-
- # Module description
- if mcrfpy.__doc__:
- lines.append("## Overview")
- lines.append("")
- lines.extend(mcrfpy.__doc__.strip().split('\n'))
- lines.append("")
-
- # Table of contents
- lines.append("## Table of Contents")
- lines.append("")
- lines.append("- [Classes](#classes)")
- lines.append("- [Functions](#functions)")
- lines.append("- [Automation Module](#automation-module)")
- lines.append("")
-
- # Collect all components
- classes = []
- functions = []
- constants = []
-
- for name in sorted(dir(mcrfpy)):
- if name.startswith('_'):
- continue
-
- obj = getattr(mcrfpy, name)
-
- if isinstance(obj, type):
- classes.append((name, obj))
- elif callable(obj):
- functions.append((name, obj))
- elif not inspect.ismodule(obj):
- constants.append((name, obj))
-
- # Document classes
- lines.append("## Classes")
- lines.append("")
-
- # Group classes by category
- ui_classes = []
- collection_classes = []
- system_classes = []
- other_classes = []
-
- for name, cls in classes:
- if name in ['Frame', 'Caption', 'Sprite', 'Grid', 'Entity']:
- ui_classes.append((name, cls))
- elif 'Collection' in name:
- collection_classes.append((name, cls))
- elif name in ['Color', 'Vector', 'Texture', 'Font']:
- system_classes.append((name, cls))
- else:
- other_classes.append((name, cls))
-
- # UI Classes
- if ui_classes:
- lines.append("### UI Components")
- lines.append("")
- for name, cls in ui_classes:
- lines.extend(generate_markdown_class(get_class_info(cls)))
-
- # Collections
- if collection_classes:
- lines.append("### Collections")
- lines.append("")
- for name, cls in collection_classes:
- lines.extend(generate_markdown_class(get_class_info(cls)))
-
- # System Classes
- if system_classes:
- lines.append("### System Types")
- lines.append("")
- for name, cls in system_classes:
- lines.extend(generate_markdown_class(get_class_info(cls)))
-
- # Other Classes
- if other_classes:
- lines.append("### Other Classes")
- lines.append("")
- for name, cls in other_classes:
- lines.extend(generate_markdown_class(get_class_info(cls)))
-
- # Document functions
- lines.append("## Functions")
- lines.append("")
-
- # Group functions by category
- scene_funcs = []
- audio_funcs = []
- ui_funcs = []
- system_funcs = []
-
- for name, func in functions:
- if 'scene' in name.lower() or name in ['createScene', 'setScene']:
- scene_funcs.append((name, func))
- elif any(x in name.lower() for x in ['sound', 'music', 'volume']):
- audio_funcs.append((name, func))
- elif name in ['find', 'findAll']:
- ui_funcs.append((name, func))
- else:
- system_funcs.append((name, func))
-
- # Scene Management
- if scene_funcs:
- lines.append("### Scene Management")
- lines.append("")
- for name, func in scene_funcs:
- lines.extend(generate_markdown_function(get_function_info(func, name)))
-
- # Audio
- if audio_funcs:
- lines.append("### Audio")
- lines.append("")
- for name, func in audio_funcs:
- lines.extend(generate_markdown_function(get_function_info(func, name)))
-
- # UI Utilities
- if ui_funcs:
- lines.append("### UI Utilities")
- lines.append("")
- for name, func in ui_funcs:
- lines.extend(generate_markdown_function(get_function_info(func, name)))
-
- # System
- if system_funcs:
- lines.append("### System")
- lines.append("")
- for name, func in system_funcs:
- lines.extend(generate_markdown_function(get_function_info(func, name)))
-
- # Automation module
- if hasattr(mcrfpy, 'automation'):
- lines.append("## Automation Module")
- lines.append("")
- lines.append("The `mcrfpy.automation` module provides testing and automation capabilities.")
- lines.append("")
-
- automation = mcrfpy.automation
- auto_funcs = []
-
- for name in sorted(dir(automation)):
- if not name.startswith('_'):
- obj = getattr(automation, name)
- if callable(obj):
- auto_funcs.append((name, obj))
-
- for name, func in auto_funcs:
- # Format as static method
- func_info = get_function_info(func, name)
- lines.append(f"### `automation.{func_info['signature']}`")
- lines.append("")
- if func_info['doc']:
- lines.append(func_info['doc'])
- lines.append("")
- lines.append("---")
- lines.append("")
-
- return '\n'.join(lines)
-
-def generate_html_docs(markdown_content: str) -> str:
- """Convert markdown to HTML."""
- # Simple conversion - in production use a proper markdown parser
- html = ['']
- html.append('
')
- html.append('')
- html.append('McRogueFace API Reference')
- html.append('')
- html.append('')
-
- # Very basic markdown to HTML conversion
- lines = markdown_content.split('\n')
- in_code_block = False
- in_list = False
-
- for line in lines:
- stripped = line.strip()
-
- if stripped.startswith('```'):
- if in_code_block:
- html.append('')
- in_code_block = False
- else:
- lang = stripped[3:] or 'python'
- html.append(f'')
- in_code_block = True
- continue
-
- if in_code_block:
- html.append(line)
- continue
-
- # Headers
- if stripped.startswith('#'):
- level = len(stripped.split()[0])
- text = stripped[level:].strip()
- html.append(f'{text}')
- # Lists
- elif stripped.startswith('- '):
- if not in_list:
- html.append('')
- in_list = True
- html.append(f'- {stripped[2:]}
')
- # Horizontal rule
- elif stripped == '---':
- if in_list:
- html.append('
')
- in_list = False
- html.append('
')
- # Emphasis
- elif stripped.startswith('*') and stripped.endswith('*') and len(stripped) > 2:
- html.append(f'{stripped[1:-1]}')
- # Bold
- elif stripped.startswith('**') and stripped.endswith('**'):
- html.append(f'{stripped[2:-2]}')
- # Regular paragraph
- elif stripped:
- if in_list:
- html.append('')
- in_list = False
- # Convert inline code
- text = stripped
- if '`' in text:
- import re
- text = re.sub(r'`([^`]+)`', r'\1', text)
- html.append(f'{text}
')
- else:
- if in_list:
- html.append('')
- in_list = False
- # Empty line
- html.append('')
-
- if in_list:
- html.append('')
- if in_code_block:
- html.append('
')
-
- html.append('')
- return '\n'.join(html)
-
-def main():
- """Generate API documentation in multiple formats."""
- print("Generating McRogueFace API Documentation...")
-
- # Create docs directory
- docs_dir = Path("docs")
- docs_dir.mkdir(exist_ok=True)
-
- # Generate markdown documentation
- print("- Generating Markdown documentation...")
- markdown_content = generate_markdown_docs()
-
- # Write markdown
- md_path = docs_dir / "API_REFERENCE.md"
- with open(md_path, 'w') as f:
- f.write(markdown_content)
- print(f" ✓ Written to {md_path}")
-
- # Generate HTML
- print("- Generating HTML documentation...")
- html_content = generate_html_docs(markdown_content)
-
- # Write HTML
- html_path = docs_dir / "api_reference.html"
- with open(html_path, 'w') as f:
- f.write(html_content)
- print(f" ✓ Written to {html_path}")
-
- # Summary statistics
- lines = markdown_content.split('\n')
- class_count = markdown_content.count('### class')
- func_count = len([l for l in lines if l.strip().startswith('### `') and 'class' not in l])
-
- print("\nDocumentation Statistics:")
- print(f"- Classes documented: {class_count}")
- print(f"- Functions documented: {func_count}")
- print(f"- Total lines: {len(lines)}")
- print(f"- File size: {len(markdown_content):,} bytes")
-
- print("\nAPI documentation generated successfully!")
-
-if __name__ == '__main__':
- main()
\ No newline at end of file
diff --git a/tools/generate_api_docs_html.py b/tools/generate_api_docs_html.py
deleted file mode 100644
index fe3cf08..0000000
--- a/tools/generate_api_docs_html.py
+++ /dev/null
@@ -1,1602 +0,0 @@
-#!/usr/bin/env python3
-"""Generate high-quality HTML API reference documentation for McRogueFace."""
-
-import os
-import sys
-import datetime
-import html
-from pathlib import Path
-import mcrfpy
-
-def escape_html(text: str) -> str:
- """Escape HTML special characters."""
- return html.escape(text) if text else ""
-
-def format_docstring_as_html(docstring: str) -> str:
- """Convert docstring to properly formatted HTML."""
- if not docstring:
- return ""
-
- # Split and process lines
- lines = docstring.strip().split('\n')
- result = []
- in_code_block = False
-
- for line in lines:
- # Convert \n to actual newlines
- line = line.replace('\\n', '\n')
-
- # Handle code blocks
- if line.strip().startswith('```'):
- if in_code_block:
- result.append('')
- in_code_block = False
- else:
- result.append('')
- in_code_block = True
- continue
-
- # Convert markdown-style code to HTML
- if '`' in line and not in_code_block:
- import re
- line = re.sub(r'`([^`]+)`', r'\1', line)
-
- if in_code_block:
- result.append(escape_html(line))
- else:
- result.append(escape_html(line) + '
')
-
- if in_code_block:
- result.append('
')
-
- return '\n'.join(result)
-
-def get_class_details(cls):
- """Get detailed information about a class."""
- info = {
- 'name': cls.__name__,
- 'doc': cls.__doc__ or "",
- 'methods': {},
- 'properties': {},
- 'bases': []
- }
-
- # Get real base classes (excluding object)
- for base in cls.__bases__:
- if base.__name__ != 'object':
- info['bases'].append(base.__name__)
-
- # Special handling for Entity which doesn't inherit from Drawable
- if cls.__name__ == 'Entity' and 'Drawable' in info['bases']:
- info['bases'].remove('Drawable')
-
- # Get methods and properties
- for attr_name in dir(cls):
- if attr_name.startswith('__') and attr_name != '__init__':
- continue
-
- try:
- attr = getattr(cls, attr_name)
-
- if isinstance(attr, property):
- info['properties'][attr_name] = {
- 'doc': (attr.fget.__doc__ if attr.fget else "") or "",
- 'readonly': attr.fset is None
- }
- elif callable(attr) and not attr_name.startswith('_'):
- info['methods'][attr_name] = attr.__doc__ or ""
- except:
- pass
-
- return info
-
-def generate_class_init_docs(class_name):
- """Generate initialization documentation for specific classes."""
- init_docs = {
- 'Entity': {
- 'signature': 'Entity(x=0, y=0, sprite_id=0)',
- 'description': 'Game entity that can be placed in a Grid.',
- 'args': [
- ('x', 'int', 'Grid x coordinate. Default: 0'),
- ('y', 'int', 'Grid y coordinate. Default: 0'),
- ('sprite_id', 'int', 'Sprite index for rendering. Default: 0')
- ],
- 'example': '''entity = mcrfpy.Entity(5, 10, 42)
-entity.move(1, 0) # Move right one tile'''
- },
- 'Color': {
- 'signature': 'Color(r=255, g=255, b=255, a=255)',
- 'description': 'RGBA color representation.',
- 'args': [
- ('r', 'int', 'Red component (0-255). Default: 255'),
- ('g', 'int', 'Green component (0-255). Default: 255'),
- ('b', 'int', 'Blue component (0-255). Default: 255'),
- ('a', 'int', 'Alpha component (0-255). Default: 255')
- ],
- 'example': 'red = mcrfpy.Color(255, 0, 0)'
- },
- 'Font': {
- 'signature': 'Font(filename)',
- 'description': 'Load a font from file.',
- 'args': [
- ('filename', 'str', 'Path to font file (TTF/OTF)')
- ]
- },
- 'Texture': {
- 'signature': 'Texture(filename)',
- 'description': 'Load a texture from file.',
- 'args': [
- ('filename', 'str', 'Path to image file (PNG/JPG/BMP)')
- ]
- },
- 'Vector': {
- 'signature': 'Vector(x=0.0, y=0.0)',
- 'description': '2D vector for positions and directions.',
- 'args': [
- ('x', 'float', 'X component. Default: 0.0'),
- ('y', 'float', 'Y component. Default: 0.0')
- ]
- },
- 'Animation': {
- 'signature': 'Animation(property_name, start_value, end_value, duration, transition="linear", loop=False)',
- 'description': 'Animate UI element properties over time.',
- 'args': [
- ('property_name', 'str', 'Property to animate (e.g., "x", "y", "scale")'),
- ('start_value', 'float', 'Starting value'),
- ('end_value', 'float', 'Ending value'),
- ('duration', 'float', 'Duration in seconds'),
- ('transition', 'str', 'Easing function. Default: "linear"'),
- ('loop', 'bool', 'Whether to loop. Default: False')
- ],
- 'properties': ['current_value', 'elapsed_time', 'is_running', 'is_finished']
- },
- 'GridPoint': {
- 'description': 'Represents a single tile in a Grid.',
- 'properties': ['x', 'y', 'texture_index', 'solid', 'transparent', 'color']
- },
- 'GridPointState': {
- 'description': 'State information for a GridPoint.',
- 'properties': ['visible', 'discovered', 'custom_flags']
- },
- 'Timer': {
- 'signature': 'Timer(name, callback, interval_ms)',
- 'description': 'Create a recurring timer.',
- 'args': [
- ('name', 'str', 'Unique timer identifier'),
- ('callback', 'callable', 'Function to call'),
- ('interval_ms', 'int', 'Interval in milliseconds')
- ]
- }
- }
-
- return init_docs.get(class_name, {})
-
-def generate_method_docs(method_name, class_name):
- """Generate documentation for specific methods."""
- method_docs = {
- # Base Drawable methods (inherited by all UI elements)
- 'Drawable': {
- 'get_bounds': {
- 'signature': 'get_bounds()',
- 'description': '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.'
- },
- 'move': {
- 'signature': 'move(dx, dy)',
- 'description': 'Move the element by a relative offset.',
- 'args': [
- ('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.'
- },
- 'resize': {
- 'signature': 'resize(width, height)',
- 'description': 'Resize the element to new dimensions.',
- 'args': [
- ('width', 'float', 'New width in pixels'),
- ('height', 'float', 'New height in pixels')
- ],
- 'note': 'Behavior varies by element type. Some elements may ignore or constrain dimensions.'
- }
- },
-
- # Caption-specific methods
- 'Caption': {
- 'get_bounds': {
- 'signature': 'get_bounds()',
- 'description': 'Get the bounding rectangle of the text.',
- 'returns': 'tuple: (x, y, width, height) based on text content and font size',
- 'note': 'Bounds are automatically calculated from the rendered text dimensions.'
- },
- 'move': {
- 'signature': 'move(dx, dy)',
- 'description': 'Move the caption by a relative offset.',
- 'args': [
- ('dx', 'float', 'Horizontal offset in pixels'),
- ('dy', 'float', 'Vertical offset in pixels')
- ]
- },
- 'resize': {
- 'signature': 'resize(width, height)',
- 'description': 'Set text wrapping bounds (limited support).',
- 'args': [
- ('width', 'float', 'Maximum width for text wrapping'),
- ('height', 'float', 'Currently unused')
- ],
- 'note': 'Full text wrapping is not yet implemented. This prepares for future multiline support.'
- }
- },
-
- # Entity-specific methods
- 'Entity': {
- 'at': {
- 'signature': 'at(x, y)',
- 'description': 'Get the GridPointState at the specified grid coordinates relative to this entity.',
- 'args': [
- ('x', 'int', 'Grid x offset from entity position'),
- ('y', 'int', 'Grid y offset from entity position')
- ],
- 'returns': 'GridPointState: State of the grid point at the specified position',
- 'note': 'Requires entity to be associated with a grid. Raises ValueError if not.'
- },
- 'die': {
- 'signature': 'die()',
- 'description': 'Remove this entity from its parent grid.',
- 'returns': 'None',
- 'note': 'The entity object remains valid but is no longer rendered or updated.'
- },
- 'index': {
- 'signature': 'index()',
- 'description': 'Get the index of this entity in its grid\'s entity collection.',
- 'returns': 'int: Zero-based index in the parent grid\'s entity list',
- 'note': 'Raises RuntimeError if not associated with a grid, ValueError if not found.'
- },
- 'get_bounds': {
- 'signature': 'get_bounds()',
- 'description': 'Get the bounding rectangle of the entity\'s sprite.',
- 'returns': 'tuple: (x, y, width, height) of the sprite bounds',
- 'note': 'Delegates to the internal sprite\'s get_bounds method.'
- },
- 'move': {
- 'signature': 'move(dx, dy)',
- 'description': 'Move the entity by a relative offset in pixels.',
- 'args': [
- ('dx', 'float', 'Horizontal offset in pixels'),
- ('dy', 'float', 'Vertical offset in pixels')
- ],
- 'note': 'Updates both sprite position and entity grid position.'
- },
- 'resize': {
- 'signature': 'resize(width, height)',
- 'description': 'Entities do not support direct resizing.',
- 'args': [
- ('width', 'float', 'Ignored'),
- ('height', 'float', 'Ignored')
- ],
- 'note': 'This method exists for interface compatibility but has no effect.'
- }
- },
-
- # Frame-specific methods
- 'Frame': {
- 'get_bounds': {
- 'signature': 'get_bounds()',
- 'description': 'Get the bounding rectangle of the frame.',
- 'returns': 'tuple: (x, y, width, height) representing the frame bounds'
- },
- 'move': {
- 'signature': 'move(dx, dy)',
- 'description': 'Move the frame and all its children by a relative offset.',
- 'args': [
- ('dx', 'float', 'Horizontal offset in pixels'),
- ('dy', 'float', 'Vertical offset in pixels')
- ],
- 'note': 'Child elements maintain their relative positions within the frame.'
- },
- 'resize': {
- 'signature': 'resize(width, height)',
- 'description': 'Resize the frame to new dimensions.',
- 'args': [
- ('width', 'float', 'New width in pixels'),
- ('height', 'float', 'New height in pixels')
- ],
- 'note': 'Does not automatically resize children. Set clip_children=True to clip overflow.'
- }
- },
-
- # Grid-specific methods
- 'Grid': {
- 'at': {
- 'signature': 'at(x, y) or at((x, y))',
- 'description': 'Get the GridPoint at the specified grid coordinates.',
- 'args': [
- ('x', 'int', 'Grid x coordinate (0-based)'),
- ('y', 'int', 'Grid y coordinate (0-based)')
- ],
- 'returns': 'GridPoint: The grid point at (x, y)',
- 'note': 'Raises IndexError if coordinates are out of range. Accepts either two arguments or a tuple.',
- 'example': 'point = grid.at(5, 3) # or grid.at((5, 3))'
- },
- 'get_bounds': {
- 'signature': 'get_bounds()',
- 'description': 'Get the bounding rectangle of the entire grid.',
- 'returns': 'tuple: (x, y, width, height) of the grid\'s display area'
- },
- 'move': {
- 'signature': 'move(dx, dy)',
- 'description': 'Move the grid display by a relative offset.',
- 'args': [
- ('dx', 'float', 'Horizontal offset in pixels'),
- ('dy', 'float', 'Vertical offset in pixels')
- ],
- 'note': 'Moves the entire grid viewport. Use center property to pan within the grid.'
- },
- 'resize': {
- 'signature': 'resize(width, height)',
- 'description': 'Resize the grid\'s display viewport.',
- 'args': [
- ('width', 'float', 'New viewport width in pixels'),
- ('height', 'float', 'New viewport height in pixels')
- ],
- 'note': 'Changes the visible area, not the grid dimensions. Use zoom to scale content.'
- }
- },
-
- # Sprite-specific methods
- 'Sprite': {
- 'get_bounds': {
- 'signature': 'get_bounds()',
- 'description': 'Get the bounding rectangle of the sprite.',
- 'returns': 'tuple: (x, y, width, height) based on texture size and scale',
- 'note': 'Bounds account for current scale. Returns (x, y, 0, 0) if no texture.'
- },
- 'move': {
- 'signature': 'move(dx, dy)',
- 'description': 'Move the sprite by a relative offset.',
- 'args': [
- ('dx', 'float', 'Horizontal offset in pixels'),
- ('dy', 'float', 'Vertical offset in pixels')
- ]
- },
- 'resize': {
- 'signature': 'resize(width, height)',
- 'description': 'Resize the sprite by adjusting its scale.',
- 'args': [
- ('width', 'float', 'Target width in pixels'),
- ('height', 'float', 'Target height in pixels')
- ],
- 'note': 'Calculates and applies uniform scale to best fit the target dimensions.'
- }
- },
-
- 'Animation': {
- 'get_current_value': {
- 'signature': 'get_current_value()',
- 'description': 'Get the current interpolated value.',
- 'returns': 'float: Current animation value'
- },
- 'start': {
- 'signature': 'start(target)',
- 'description': 'Start the animation on a target UI element.',
- 'args': [('target', 'UIDrawable', 'The element to animate')]
- }
- },
-
- # Collection methods (shared by EntityCollection and UICollection)
- 'EntityCollection': {
- 'append': {
- 'signature': 'append(entity)',
- 'description': 'Add an entity to the end of the collection.',
- 'args': [
- ('entity', 'Entity', 'The entity to add')
- ]
- },
- 'remove': {
- 'signature': 'remove(entity)',
- 'description': 'Remove the first occurrence of an entity from the collection.',
- 'args': [
- ('entity', 'Entity', 'The entity to remove')
- ],
- 'note': 'Raises ValueError if entity is not found.'
- },
- 'extend': {
- 'signature': 'extend(iterable)',
- 'description': 'Add multiple entities from an iterable.',
- 'args': [
- ('iterable', 'iterable', 'An iterable of Entity objects')
- ]
- },
- 'count': {
- 'signature': 'count(entity)',
- 'description': 'Count occurrences of an entity in the collection.',
- 'args': [
- ('entity', 'Entity', 'The entity to count')
- ],
- 'returns': 'int: Number of times the entity appears'
- },
- 'index': {
- 'signature': 'index(entity)',
- 'description': 'Find the index of the first occurrence of an entity.',
- 'args': [
- ('entity', 'Entity', 'The entity to find')
- ],
- 'returns': 'int: Zero-based index of the entity',
- 'note': 'Raises ValueError if entity is not found.'
- }
- },
-
- 'UICollection': {
- 'append': {
- 'signature': 'append(drawable)',
- 'description': 'Add a drawable element to the end of the collection.',
- 'args': [
- ('drawable', 'Drawable', 'Any UI element (Frame, Caption, Sprite, Grid)')
- ]
- },
- 'remove': {
- 'signature': 'remove(drawable)',
- 'description': 'Remove the first occurrence of a drawable from the collection.',
- 'args': [
- ('drawable', 'Drawable', 'The drawable to remove')
- ],
- 'note': 'Raises ValueError if drawable is not found.'
- },
- 'extend': {
- 'signature': 'extend(iterable)',
- 'description': 'Add multiple drawables from an iterable.',
- 'args': [
- ('iterable', 'iterable', 'An iterable of Drawable objects')
- ]
- },
- 'count': {
- 'signature': 'count(drawable)',
- 'description': 'Count occurrences of a drawable in the collection.',
- 'args': [
- ('drawable', 'Drawable', 'The drawable to count')
- ],
- 'returns': 'int: Number of times the drawable appears'
- },
- 'index': {
- 'signature': 'index(drawable)',
- 'description': 'Find the index of the first occurrence of a drawable.',
- 'args': [
- ('drawable', 'Drawable', 'The drawable to find')
- ],
- 'returns': 'int: Zero-based index of the drawable',
- 'note': 'Raises ValueError if drawable is not found.'
- }
- }
- }
-
- return method_docs.get(class_name, {}).get(method_name, {})
-
-def generate_function_docs():
- """Generate documentation for all mcrfpy module functions."""
- function_docs = {
- # Scene Management
- 'createScene': {
- 'signature': 'createScene(name: str) -> None',
- 'description': 'Create a new empty scene.',
- 'args': [
- ('name', 'str', 'Unique name for the new scene')
- ],
- 'returns': 'None',
- 'exceptions': [
- ('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': '''mcrfpy.createScene("game")
-mcrfpy.createScene("menu")
-mcrfpy.setScene("game")'''
- },
-
- 'setScene': {
- 'signature': 'setScene(scene: str, transition: str = None, duration: float = 0.0) -> None',
- 'description': 'Switch to a different scene with optional transition effect.',
- 'args': [
- ('scene', 'str', 'Name of the scene to switch to'),
- ('transition', 'str', 'Transition type ("fade", "slide_left", "slide_right", "slide_up", "slide_down"). Default: None'),
- ('duration', 'float', 'Transition duration in seconds. Default: 0.0 for instant')
- ],
- 'returns': 'None',
- 'exceptions': [
- ('KeyError', 'If the scene doesn\'t exist'),
- ('ValueError', 'If the transition type is invalid')
- ],
- 'example': '''mcrfpy.setScene("menu")
-mcrfpy.setScene("game", "fade", 0.5)
-mcrfpy.setScene("credits", "slide_left", 1.0)'''
- },
-
- 'currentScene': {
- 'signature': 'currentScene() -> str',
- 'description': 'Get the name of the currently active scene.',
- 'args': [],
- 'returns': 'str: Name of the current scene',
- 'example': '''scene = mcrfpy.currentScene()
-print(f"Currently in scene: {scene}")'''
- },
-
- 'sceneUI': {
- 'signature': 'sceneUI(scene: str = None) -> list',
- 'description': 'Get all UI elements for a scene.',
- 'args': [
- ('scene', 'str', 'Scene name. If None, uses current scene. Default: None')
- ],
- 'returns': 'list: All UI elements (Frame, Caption, Sprite, Grid) in the scene',
- 'exceptions': [
- ('KeyError', 'If the specified scene doesn\'t exist')
- ],
- 'example': '''# Get UI for current scene
-ui_elements = mcrfpy.sceneUI()
-
-# Get UI for specific scene
-menu_ui = mcrfpy.sceneUI("menu")
-for element in menu_ui:
- print(f"{element.name}: {type(element).__name__}")'''
- },
-
- 'keypressScene': {
- 'signature': 'keypressScene(handler: callable) -> None',
- 'description': 'Set the keyboard event handler for the current scene.',
- 'args': [
- ('handler', 'callable', 'Function that receives (key_name: str, is_pressed: bool)')
- ],
- 'returns': 'None',
- 'note': 'The handler is called for every key press and release event. Key names are single characters (e.g., "A", "1") or special keys (e.g., "Space", "Enter", "Escape").',
- 'example': '''def on_key(key, pressed):
- if pressed:
- if key == "Space":
- player.jump()
- elif key == "Escape":
- mcrfpy.setScene("pause_menu")
- else:
- # Handle key release
- if key in ["A", "D"]:
- player.stop_moving()
-
-mcrfpy.keypressScene(on_key)'''
- },
-
- # Audio Functions
- 'createSoundBuffer': {
- 'signature': 'createSoundBuffer(filename: str) -> int',
- 'description': 'Load a sound effect from a file and return its buffer ID.',
- 'args': [
- ('filename', 'str', 'Path to the sound file (WAV, OGG, FLAC)')
- ],
- 'returns': 'int: Buffer ID for use with playSound()',
- 'exceptions': [
- ('RuntimeError', 'If the file cannot be loaded')
- ],
- 'note': 'Sound buffers are stored in memory for fast playback. Load sound effects once and reuse the buffer ID.',
- 'example': '''# Load sound effects
-jump_sound = mcrfpy.createSoundBuffer("assets/sounds/jump.wav")
-coin_sound = mcrfpy.createSoundBuffer("assets/sounds/coin.ogg")
-
-# Play later
-mcrfpy.playSound(jump_sound)'''
- },
-
- 'loadMusic': {
- 'signature': 'loadMusic(filename: str, loop: bool = True) -> None',
- 'description': 'Load and immediately play background music from a file.',
- 'args': [
- ('filename', 'str', 'Path to the music file (WAV, OGG, FLAC)'),
- ('loop', 'bool', 'Whether to loop the music. Default: True')
- ],
- 'returns': 'None',
- 'note': 'Only one music track can play at a time. Loading new music stops the current track.',
- 'example': '''# Play looping background music
-mcrfpy.loadMusic("assets/music/theme.ogg")
-
-# Play music once without looping
-mcrfpy.loadMusic("assets/music/victory.ogg", loop=False)'''
- },
-
- 'playSound': {
- 'signature': 'playSound(buffer_id: int) -> None',
- 'description': 'Play a sound effect using a previously loaded buffer.',
- 'args': [
- ('buffer_id', 'int', 'Sound buffer ID returned by createSoundBuffer()')
- ],
- 'returns': 'None',
- 'exceptions': [
- ('RuntimeError', 'If the buffer ID is invalid')
- ],
- 'note': 'Multiple sounds can play simultaneously. Each call creates a new sound instance.',
- 'example': '''# Load once
-explosion_sound = mcrfpy.createSoundBuffer("explosion.wav")
-
-# Play multiple times
-for enemy in destroyed_enemies:
- mcrfpy.playSound(explosion_sound)'''
- },
-
- 'getMusicVolume': {
- 'signature': 'getMusicVolume() -> int',
- 'description': 'Get the current music volume level.',
- 'args': [],
- 'returns': 'int: Current volume (0-100)',
- 'example': '''volume = mcrfpy.getMusicVolume()
-print(f"Music volume: {volume}%")'''
- },
-
- 'getSoundVolume': {
- 'signature': 'getSoundVolume() -> int',
- 'description': 'Get the current sound effects volume level.',
- 'args': [],
- 'returns': 'int: Current volume (0-100)',
- 'example': '''volume = mcrfpy.getSoundVolume()
-print(f"Sound effects volume: {volume}%")'''
- },
-
- 'setMusicVolume': {
- 'signature': 'setMusicVolume(volume: int) -> None',
- 'description': 'Set the global music volume.',
- 'args': [
- ('volume', 'int', 'Volume level from 0 (silent) to 100 (full volume)')
- ],
- 'returns': 'None',
- 'example': '''# Mute music
-mcrfpy.setMusicVolume(0)
-
-# Half volume
-mcrfpy.setMusicVolume(50)
-
-# Full volume
-mcrfpy.setMusicVolume(100)'''
- },
-
- 'setSoundVolume': {
- 'signature': 'setSoundVolume(volume: int) -> None',
- 'description': 'Set the global sound effects volume.',
- 'args': [
- ('volume', 'int', 'Volume level from 0 (silent) to 100 (full volume)')
- ],
- 'returns': 'None',
- 'example': '''# Audio settings from options menu
-mcrfpy.setSoundVolume(sound_slider.value)
-mcrfpy.setMusicVolume(music_slider.value)'''
- },
-
- # UI Utilities
- 'find': {
- 'signature': 'find(name: str, scene: str = None) -> UIDrawable | None',
- 'description': 'Find the first UI element with the specified name.',
- 'args': [
- ('name', 'str', 'Exact name to search for'),
- ('scene', 'str', 'Scene to search in. Default: current scene')
- ],
- 'returns': 'Frame, Caption, Sprite, Grid, or Entity if found; None otherwise',
- 'note': 'Searches scene UI elements and entities within grids. Returns the first match found.',
- 'example': '''# Find in current scene
-player = mcrfpy.find("player")
-if player:
- player.x = 100
-
-# Find in specific scene
-menu_button = mcrfpy.find("start_button", "main_menu")'''
- },
-
- 'findAll': {
- 'signature': 'findAll(pattern: str, scene: str = None) -> list',
- 'description': 'Find all UI elements matching a name pattern.',
- 'args': [
- ('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',
- 'note': 'Supports wildcard patterns for flexible searching.',
- 'example': '''# Find all enemies
-enemies = mcrfpy.findAll("enemy*")
-for enemy in enemies:
- enemy.sprite_id = 0 # Reset sprite
-
-# Find all buttons
-buttons = mcrfpy.findAll("*_button")
-for btn in buttons:
- btn.visible = True
-
-# Find exact matches
-health_bars = mcrfpy.findAll("health_bar") # No wildcards = exact match'''
- },
-
- # System Functions
- 'exit': {
- 'signature': 'exit() -> None',
- 'description': 'Cleanly shut down the game engine and exit the application.',
- 'args': [],
- 'returns': 'None',
- 'note': 'This immediately closes the window and terminates the program. Ensure any necessary cleanup is done before calling.',
- 'example': '''def quit_game():
- # Save game state
- save_progress()
-
- # Exit
- mcrfpy.exit()'''
- },
-
- 'getMetrics': {
- 'signature': 'getMetrics() -> dict',
- 'description': 'Get current performance metrics.',
- 'args': [],
- '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': '''metrics = mcrfpy.getMetrics()
-print(f"FPS: {metrics['fps']}")
-print(f"Frame time: {metrics['frame_time']*1000:.1f}ms")
-print(f"Draw calls: {metrics['draw_calls']}")
-print(f"Runtime: {metrics['runtime']:.1f}s")
-
-# Performance monitoring
-if metrics['fps'] < 30:
- print("Performance warning: FPS below 30")'''
- },
-
- 'setTimer': {
- 'signature': 'setTimer(name: str, handler: callable, interval: int) -> None',
- 'description': 'Create or update a recurring timer.',
- 'args': [
- ('name', 'str', 'Unique identifier for the timer'),
- ('handler', 'callable', 'Function called with (runtime: float) parameter'),
- ('interval', 'int', 'Time between calls in milliseconds')
- ],
- 'returns': 'None',
- 'note': 'If a timer with this name exists, it will be replaced. The handler receives the total runtime in seconds as its argument.',
- 'example': '''# Simple repeating timer
-def spawn_enemy(runtime):
- enemy = mcrfpy.Entity()
- enemy.x = random.randint(0, 800)
- grid.entities.append(enemy)
-
-mcrfpy.setTimer("enemy_spawner", spawn_enemy, 2000) # Every 2 seconds
-
-# Timer with runtime check
-def update_timer(runtime):
- time_left = 60 - runtime
- timer_text.text = f"Time: {int(time_left)}"
- if time_left <= 0:
- mcrfpy.delTimer("game_timer")
- game_over()
-
-mcrfpy.setTimer("game_timer", update_timer, 100) # Update every 100ms'''
- },
-
- 'delTimer': {
- 'signature': 'delTimer(name: str) -> None',
- 'description': 'Stop and remove a timer.',
- 'args': [
- ('name', 'str', 'Timer identifier to remove')
- ],
- 'returns': 'None',
- 'note': 'No error is raised if the timer doesn\'t exist.',
- 'example': '''# Stop spawning enemies
-mcrfpy.delTimer("enemy_spawner")
-
-# Clean up all game timers
-for timer_name in ["enemy_spawner", "powerup_timer", "score_updater"]:
- mcrfpy.delTimer(timer_name)'''
- },
-
- 'setScale': {
- 'signature': 'setScale(multiplier: float) -> None',
- 'description': 'Scale the game window size.',
- 'args': [
- ('multiplier', 'float', 'Scale factor (e.g., 2.0 for double size)')
- ],
- 'returns': 'None',
- 'exceptions': [
- ('ValueError', 'If multiplier is not between 0.2 and 4.0')
- ],
- 'note': 'The internal resolution remains 1024x768, but the window is scaled. This is deprecated - use Window.resolution instead.',
- 'example': '''# Double the window size
-mcrfpy.setScale(2.0)
-
-# Half size window
-mcrfpy.setScale(0.5)
-
-# Better approach (not deprecated):
-mcrfpy.Window.resolution = (1920, 1080)'''
- }
- }
-
- return function_docs
-
-def generate_collection_docs(class_name):
- """Generate documentation for collection classes."""
- collection_docs = {
- 'EntityCollection': {
- 'description': 'Container for Entity objects in a Grid. Supports iteration and indexing.',
- 'methods': {
- 'append': 'Add an entity to the collection',
- 'remove': 'Remove an entity from the collection',
- 'extend': 'Add multiple entities from an iterable',
- 'count': 'Count occurrences of an entity',
- 'index': 'Find the index of an entity'
- }
- },
- 'UICollection': {
- 'description': 'Container for UI drawable elements. Supports iteration and indexing.',
- 'methods': {
- 'append': 'Add a UI element to the collection',
- 'remove': 'Remove a UI element from the collection',
- 'extend': 'Add multiple UI elements from an iterable',
- 'count': 'Count occurrences of a UI element',
- 'index': 'Find the index of a UI element'
- }
- },
- 'UICollectionIter': {
- 'description': 'Iterator for UICollection. Automatically created when iterating over a UICollection.'
- },
- 'UIEntityCollectionIter': {
- 'description': 'Iterator for EntityCollection. Automatically created when iterating over an EntityCollection.'
- }
- }
-
- return collection_docs.get(class_name, {})
-
-def format_class_html(cls_info, class_name):
- """Format a class as HTML with proper structure."""
- html_parts = []
-
- # Class header
- html_parts.append(f'')
- html_parts.append(f'
class {class_name}
')
-
- # Inheritance
- if cls_info['bases']:
- html_parts.append(f'
Inherits from: {", ".join(cls_info["bases"])}
')
-
- # Get additional documentation
- init_info = generate_class_init_docs(class_name)
- collection_info = generate_collection_docs(class_name)
-
- # Constructor signature for classes with __init__
- if init_info.get('signature'):
- html_parts.append('
')
- html_parts.append('
')
- html_parts.append(escape_html(init_info['signature']))
- html_parts.append('
')
- html_parts.append('
')
-
- # Description
- description = ""
- if collection_info.get('description'):
- description = collection_info['description']
- elif init_info.get('description'):
- description = init_info['description']
- elif cls_info['doc']:
- # Parse description from docstring
- doc_lines = cls_info['doc'].strip().split('\n')
- # Skip constructor line if present
- start_idx = 1 if doc_lines and '(' in doc_lines[0] else 0
- if start_idx < len(doc_lines):
- description = '\n'.join(doc_lines[start_idx:]).strip()
-
- if description:
- html_parts.append('
')
- html_parts.append(f'
{format_docstring_as_html(description)}
')
- html_parts.append('
')
-
- # Constructor arguments
- if init_info.get('args'):
- html_parts.append('
')
- html_parts.append('
Arguments:
')
- html_parts.append('
')
- for arg_name, arg_type, arg_desc in init_info['args']:
- html_parts.append(f'{arg_name} ({arg_type}) ')
- html_parts.append(f'- {escape_html(arg_desc)}
')
- html_parts.append('
')
- html_parts.append('
')
-
- # Properties/Attributes
- props = cls_info.get('properties', {})
- if props or init_info.get('properties'):
- html_parts.append('
')
- html_parts.append('
Attributes:
')
- html_parts.append('
')
-
- # Add documented properties from init_info
- if init_info.get('properties'):
- for prop_name in init_info['properties']:
- html_parts.append(f'{prop_name} ')
- html_parts.append(f'- Property of {class_name}
')
-
- # Add actual properties
- for prop_name, prop_info in props.items():
- readonly = ' (read-only)' if prop_info.get('readonly') else ''
- html_parts.append(f'{prop_name}{readonly} ')
- if prop_info.get('doc'):
- html_parts.append(f'- {escape_html(prop_info["doc"])}
')
-
- html_parts.append('
')
- html_parts.append('
')
-
- # Methods
- methods = cls_info.get('methods', {})
- collection_methods = collection_info.get('methods', {})
-
- if methods or collection_methods:
- html_parts.append('
')
- html_parts.append('
Methods:
')
-
- for method_name, method_doc in {**collection_methods, **methods}.items():
- if method_name == '__init__':
- continue
-
- html_parts.append('
')
-
- # Get specific method documentation
- method_info = generate_method_docs(method_name, class_name)
-
- if method_info:
- # Use detailed documentation
- html_parts.append(f'
{method_info["signature"]}
')
- html_parts.append(f'
{escape_html(method_info["description"])}
')
-
- if method_info.get('args'):
- html_parts.append('
Arguments:
')
- html_parts.append('
')
- for arg in method_info['args']:
- if len(arg) == 3:
- html_parts.append(f'{arg[0]} ({arg[1]}): {arg[2]} ')
- else:
- html_parts.append(f'{arg[0]} ({arg[1]}) ')
- html_parts.append('
')
-
- if method_info.get('returns'):
- html_parts.append(f'
Returns: {escape_html(method_info["returns"])}
')
-
- if method_info.get('note'):
- html_parts.append(f'
Note: {escape_html(method_info["note"])}
')
- else:
- # Use docstring
- html_parts.append(f'
{method_name}(...)
')
- if isinstance(method_doc, str) and method_doc:
- html_parts.append(f'
{escape_html(method_doc)}
')
-
- html_parts.append('
')
-
- html_parts.append('
')
-
- # Example
- if init_info.get('example'):
- html_parts.append('
')
- html_parts.append('
Example:
')
- html_parts.append('
')
- html_parts.append(escape_html(init_info['example']))
- html_parts.append('
')
- html_parts.append('
')
-
- html_parts.append('
')
- html_parts.append('
')
-
- return '\n'.join(html_parts)
-
-def generate_html_documentation():
- """Generate complete HTML API documentation."""
- html_parts = []
-
- # HTML header
- html_parts.append('''
-
-
-
-
- McRogueFace API Reference
-
-
-
-
-''')
-
- # Title and timestamp
- html_parts.append('
McRogueFace API Reference
')
- html_parts.append(f'
Generated on {datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
')
-
- # Overview
- if mcrfpy.__doc__:
- html_parts.append('
')
- html_parts.append('
Overview
')
- # Process the docstring properly
- doc_lines = mcrfpy.__doc__.strip().split('\\n')
- for line in doc_lines:
- if line.strip().startswith('Example:'):
- html_parts.append('
Example:
')
- html_parts.append('
')
- elif line.strip() and not line.startswith(' '):
- html_parts.append(f'{escape_html(line)}
')
- elif line.strip():
- # Code line
- html_parts.append(escape_html(line))
- html_parts.append('
')
- html_parts.append('
')
-
- # Table of Contents
- html_parts.append('
')
- html_parts.append('
Table of Contents
')
- html_parts.append('
')
- html_parts.append('- Classes')
- html_parts.append('')
- html_parts.append('
')
- html_parts.append('- Functions')
- html_parts.append('')
- html_parts.append('
')
- html_parts.append('- Automation Module
')
- html_parts.append('
')
- html_parts.append('
')
-
- # Collect all components
- classes = {}
- functions = {}
-
- for name in sorted(dir(mcrfpy)):
- if name.startswith('_'):
- continue
-
- obj = getattr(mcrfpy, name)
-
- if isinstance(obj, type):
- classes[name] = obj
- elif callable(obj) and not isinstance(obj, type):
- # Include built-in functions and other callables (but not classes)
- functions[name] = obj
-
-
- # Classes section
- html_parts.append('
Classes
')
-
- # Group classes
- ui_classes = ['Frame', 'Caption', 'Sprite', 'Grid', 'Entity']
- collection_classes = ['EntityCollection', 'UICollection', 'UICollectionIter', 'UIEntityCollectionIter']
- system_classes = ['Color', 'Vector', 'Texture', 'Font']
- other_classes = [name for name in classes if name not in ui_classes + collection_classes + system_classes]
-
- # UI Components
- html_parts.append('
UI Components
')
- for class_name in ui_classes:
- if class_name in classes:
- cls_info = get_class_details(classes[class_name])
- html_parts.append(format_class_html(cls_info, class_name))
-
- # Collections
- html_parts.append('
Collections
')
- for class_name in collection_classes:
- if class_name in classes:
- cls_info = get_class_details(classes[class_name])
- html_parts.append(format_class_html(cls_info, class_name))
-
- # System Types
- html_parts.append('
System Types
')
- for class_name in system_classes:
- if class_name in classes:
- cls_info = get_class_details(classes[class_name])
- html_parts.append(format_class_html(cls_info, class_name))
-
- # Other Classes
- html_parts.append('
Other Classes
')
- for class_name in other_classes:
- if class_name in classes:
- cls_info = get_class_details(classes[class_name])
- html_parts.append(format_class_html(cls_info, class_name))
-
- # Functions section
- html_parts.append('
Functions
')
-
- # Group functions by category
- scene_funcs = ['createScene', 'setScene', 'currentScene', 'sceneUI', 'keypressScene']
- audio_funcs = ['createSoundBuffer', 'loadMusic', 'playSound', 'getMusicVolume',
- 'getSoundVolume', 'setMusicVolume', 'setSoundVolume']
- ui_funcs = ['find', 'findAll']
- system_funcs = ['exit', 'getMetrics', 'setTimer', 'delTimer', 'setScale']
-
- # Scene Management
- html_parts.append('
Scene Management
')
- for func_name in scene_funcs:
- if func_name in functions:
- html_parts.append(format_function_html(func_name, functions[func_name]))
-
- # Audio
- html_parts.append('
Audio
')
- for func_name in audio_funcs:
- if func_name in functions:
- html_parts.append(format_function_html(func_name, functions[func_name]))
-
- # UI Utilities
- html_parts.append('
UI Utilities
')
- for func_name in ui_funcs:
- if func_name in functions:
- html_parts.append(format_function_html(func_name, functions[func_name]))
-
- # System
- html_parts.append('
System
')
- for func_name in system_funcs:
- if func_name in functions:
- html_parts.append(format_function_html(func_name, functions[func_name]))
-
- # Automation Module
- if hasattr(mcrfpy, 'automation'):
- html_parts.append('
')
- html_parts.append('
Automation Module
')
- html_parts.append('
The mcrfpy.automation module provides testing and automation capabilities for simulating user input and capturing screenshots.
')
-
- automation = mcrfpy.automation
- auto_funcs = []
-
- for name in sorted(dir(automation)):
- if not name.startswith('_'):
- obj = getattr(automation, name)
- if callable(obj):
- auto_funcs.append((name, obj))
-
- for name, func in auto_funcs:
- html_parts.append('
')
- html_parts.append(f'
automation.{name}
')
- if func.__doc__:
- # Extract just the description, not the repeated signature
- doc_lines = func.__doc__.strip().split(' - ')
- if len(doc_lines) > 1:
- description = doc_lines[1]
- else:
- description = func.__doc__.strip()
- html_parts.append(f'
{escape_html(description)}
')
- html_parts.append('
')
-
- html_parts.append('
')
-
- # Close HTML
- html_parts.append('''
-
-
-''')
-
- return '\n'.join(html_parts)
-
-def format_function_html(func_name, func):
- """Format a function as HTML using enhanced documentation."""
- html_parts = []
-
- html_parts.append('')
-
- # Get enhanced documentation
- func_docs = generate_function_docs()
-
- if func_name in func_docs:
- doc_info = func_docs[func_name]
-
- # Signature
- signature = doc_info.get('signature', f'{func_name}(...)')
- html_parts.append(f'
{escape_html(signature)}
')
-
- # Description
- if 'description' in doc_info:
- html_parts.append(f'
{escape_html(doc_info["description"])}
')
-
- # Arguments
- if 'args' in doc_info and doc_info['args']:
- html_parts.append('
')
- html_parts.append('
Arguments:
')
- html_parts.append('
')
- for arg_name, arg_type, arg_desc in doc_info['args']:
- html_parts.append(f'{escape_html(arg_name)} : {escape_html(arg_type)} ')
- html_parts.append(f'- {escape_html(arg_desc)}
')
- html_parts.append('
')
- html_parts.append('
')
-
- # Returns
- if 'returns' in doc_info and doc_info['returns']:
- html_parts.append('
')
- html_parts.append('
Returns:
')
- html_parts.append(f'
{escape_html(doc_info["returns"])}
')
- html_parts.append('
')
-
- # Exceptions
- if 'exceptions' in doc_info and doc_info['exceptions']:
- html_parts.append('
')
- html_parts.append('
Raises:
')
- html_parts.append('
')
- for exc_type, exc_desc in doc_info['exceptions']:
- html_parts.append(f'{escape_html(exc_type)} ')
- html_parts.append(f'- {escape_html(exc_desc)}
')
- html_parts.append('
')
- html_parts.append('
')
-
- # Note
- if 'note' in doc_info:
- html_parts.append('
')
- html_parts.append(f'
Note: {escape_html(doc_info["note"])}
')
- html_parts.append('
')
-
- # Example
- if 'example' in doc_info:
- html_parts.append('
')
- html_parts.append('
Example:
')
- html_parts.append('
')
- html_parts.append(escape_html(doc_info['example']))
- html_parts.append('
')
- html_parts.append('
')
- else:
- # Fallback to parsing docstring if not in enhanced docs
- doc = func.__doc__ or ""
- lines = doc.strip().split('\n') if doc else []
-
- # Extract signature
- signature = func_name + '(...)'
- if lines and '(' in lines[0]:
- signature = lines[0].strip()
-
- html_parts.append(f'
{escape_html(signature)}
')
-
- # Process rest of docstring
- if len(lines) > 1:
- in_section = None
- for line in lines[1:]:
- stripped = line.strip()
-
- if stripped in ['Args:', 'Returns:', 'Raises:', 'Note:', 'Example:']:
- in_section = stripped[:-1]
- html_parts.append(f'
{in_section}:
')
- elif in_section == 'Example':
- if not stripped:
- continue
- if stripped.startswith('>>>') or (len(lines) > lines.index(line) + 1 and
- lines[lines.index(line) + 1].strip().startswith('>>>')):
- html_parts.append('
')
- html_parts.append(escape_html(stripped))
- # Get rest of example
- idx = lines.index(line) + 1
- while idx < len(lines) and lines[idx].strip():
- html_parts.append(escape_html(lines[idx]))
- idx += 1
- html_parts.append('
')
- break
- elif in_section and stripped:
- if in_section == 'Args':
- # Format arguments nicely
- if ':' in stripped:
- param, desc = stripped.split(':', 1)
- html_parts.append(f'
{escape_html(param.strip())}: {escape_html(desc.strip())}
')
- else:
- html_parts.append(f'
{escape_html(stripped)}
')
- else:
- html_parts.append(f'
{escape_html(stripped)}
')
- elif stripped and not in_section:
- html_parts.append(f'
{escape_html(stripped)}
')
-
- html_parts.append('
')
- html_parts.append('
')
-
- return '\n'.join(html_parts)
-
-def main():
- """Generate improved HTML API documentation."""
- print("Generating improved HTML API documentation...")
-
- # Generate HTML
- html_content = generate_html_documentation()
-
- # Write to file
- output_path = Path("docs/api_reference_improved.html")
- output_path.parent.mkdir(exist_ok=True)
-
- with open(output_path, 'w', encoding='utf-8') as f:
- f.write(html_content)
-
- print(f"✓ Generated {output_path}")
- print(f" File size: {len(html_content):,} bytes")
-
- # Also generate a test to verify the HTML
- test_content = '''#!/usr/bin/env python3
-"""Test the improved HTML API documentation."""
-
-import os
-import sys
-from pathlib import Path
-
-def test_html_quality():
- """Test that the HTML documentation meets quality standards."""
- html_path = Path("docs/api_reference_improved.html")
-
- if not html_path.exists():
- print("ERROR: HTML documentation not found")
- return False
-
- with open(html_path, 'r') as f:
- content = f.read()
-
- # Check for common issues
- issues = []
-
- # Check that \\n is not present literally
- if '\\\\n' in content:
- issues.append("Found literal \\\\n in HTML content")
-
- # Check that markdown links are converted
- if '[' in content and '](#' in content:
- issues.append("Found unconverted markdown links")
-
- # Check for proper HTML structure
- if 'Args:
' in content:
- issues.append("Args: should not be an H4 heading")
-
- if 'Attributes:
' not in content:
- issues.append("Missing proper Attributes: headings")
-
- # Check for duplicate method descriptions
- if content.count('Get bounding box as (x, y, width, height)') > 20:
- issues.append("Too many duplicate method descriptions")
-
- # Check specific improvements
- if 'Entity' in content and 'Inherits from: Drawable' in content:
- issues.append("Entity incorrectly shown as inheriting from Drawable")
-
- if not issues:
- print("✓ HTML documentation passes all quality checks")
- return True
- else:
- print("Issues found:")
- for issue in issues:
- print(f" - {issue}")
- return False
-
-if __name__ == '__main__':
- if test_html_quality():
- print("PASS")
- sys.exit(0)
- else:
- print("FAIL")
- sys.exit(1)
-'''
-
- test_path = Path("tests/test_html_quality.py")
- with open(test_path, 'w') as f:
- f.write(test_content)
-
- print(f"✓ Generated test at {test_path}")
-
-if __name__ == '__main__':
- main()
\ No newline at end of file
diff --git a/tools/generate_api_docs_simple.py b/tools/generate_api_docs_simple.py
deleted file mode 100644
index 2bb405f..0000000
--- a/tools/generate_api_docs_simple.py
+++ /dev/null
@@ -1,119 +0,0 @@
-#!/usr/bin/env python3
-"""Generate API reference documentation for McRogueFace - Simple version."""
-
-import os
-import sys
-import datetime
-from pathlib import Path
-
-import mcrfpy
-
-def generate_markdown_docs():
- """Generate markdown API documentation."""
- lines = []
-
- # Header
- lines.append("# McRogueFace API Reference")
- lines.append("")
- lines.append("*Generated on {}*".format(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
- lines.append("")
-
- # Module description
- if mcrfpy.__doc__:
- lines.append("## Overview")
- lines.append("")
- lines.extend(mcrfpy.__doc__.strip().split('\n'))
- lines.append("")
-
- # Collect all components
- classes = []
- functions = []
-
- for name in sorted(dir(mcrfpy)):
- if name.startswith('_'):
- continue
-
- obj = getattr(mcrfpy, name)
-
- if isinstance(obj, type):
- classes.append((name, obj))
- elif callable(obj):
- functions.append((name, obj))
-
- # Document classes
- lines.append("## Classes")
- lines.append("")
-
- for name, cls in classes:
- lines.append("### class {}".format(name))
- if cls.__doc__:
- doc_lines = cls.__doc__.strip().split('\n')
- for line in doc_lines[:5]: # First 5 lines
- lines.append(line)
- lines.append("")
- lines.append("---")
- lines.append("")
-
- # Document functions
- lines.append("## Functions")
- lines.append("")
-
- for name, func in functions:
- lines.append("### {}".format(name))
- if func.__doc__:
- doc_lines = func.__doc__.strip().split('\n')
- for line in doc_lines[:5]: # First 5 lines
- lines.append(line)
- lines.append("")
- lines.append("---")
- lines.append("")
-
- # Automation module
- if hasattr(mcrfpy, 'automation'):
- lines.append("## Automation Module")
- lines.append("")
-
- automation = mcrfpy.automation
- for name in sorted(dir(automation)):
- if not name.startswith('_'):
- obj = getattr(automation, name)
- if callable(obj):
- lines.append("### automation.{}".format(name))
- if obj.__doc__:
- lines.append(obj.__doc__.strip().split('\n')[0])
- lines.append("")
-
- return '\n'.join(lines)
-
-def main():
- """Generate API documentation."""
- print("Generating McRogueFace API Documentation...")
-
- # Create docs directory
- docs_dir = Path("docs")
- docs_dir.mkdir(exist_ok=True)
-
- # Generate markdown
- markdown_content = generate_markdown_docs()
-
- # Write markdown
- md_path = docs_dir / "API_REFERENCE.md"
- with open(md_path, 'w') as f:
- f.write(markdown_content)
- print("Written to {}".format(md_path))
-
- # Summary
- lines = markdown_content.split('\n')
- class_count = markdown_content.count('### class')
- func_count = markdown_content.count('### ') - class_count - markdown_content.count('### automation.')
-
- print("\nDocumentation Statistics:")
- print("- Classes documented: {}".format(class_count))
- print("- Functions documented: {}".format(func_count))
- print("- Total lines: {}".format(len(lines)))
-
- print("\nAPI documentation generated successfully!")
- sys.exit(0)
-
-if __name__ == '__main__':
- main()
\ No newline at end of file
diff --git a/tools/generate_complete_api_docs.py b/tools/generate_complete_api_docs.py
deleted file mode 100644
index 8b41446..0000000
--- a/tools/generate_complete_api_docs.py
+++ /dev/null
@@ -1,960 +0,0 @@
-#!/usr/bin/env python3
-"""Generate COMPLETE HTML API reference documentation for McRogueFace with NO missing methods."""
-
-import os
-import sys
-import datetime
-import html
-from pathlib import Path
-import mcrfpy
-
-def escape_html(text: str) -> str:
- """Escape HTML special characters."""
- return html.escape(text) if text else ""
-
-def get_complete_method_documentation():
- """Return complete documentation for ALL methods across all classes."""
- return {
- # Base Drawable methods (inherited by all UI elements)
- 'Drawable': {
- 'get_bounds': {
- 'signature': 'get_bounds()',
- 'description': '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.'
- },
- 'move': {
- 'signature': 'move(dx, dy)',
- 'description': 'Move the element by a relative offset.',
- 'args': [
- ('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.'
- },
- 'resize': {
- 'signature': 'resize(width, height)',
- 'description': 'Resize the element to new dimensions.',
- 'args': [
- ('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.'
- }
- },
-
- # Entity-specific methods
- 'Entity': {
- 'at': {
- 'signature': 'at(x, y)',
- 'description': 'Check if this entity is at the specified grid coordinates.',
- 'args': [
- ('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'
- },
- 'die': {
- 'signature': 'die()',
- 'description': 'Remove this entity from its parent grid.',
- 'note': 'The entity object remains valid but is no longer rendered or updated.'
- },
- 'index': {
- 'signature': 'index()',
- 'description': 'Get the index of this entity in its parent grid\'s entity list.',
- 'returns': 'int: Index position, or -1 if not in a grid'
- }
- },
-
- # Grid-specific methods
- 'Grid': {
- 'at': {
- 'signature': 'at(x, y)',
- 'description': 'Get the GridPoint at the specified grid coordinates.',
- 'args': [
- ('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'
- }
- },
-
- # Collection methods
- 'EntityCollection': {
- 'append': {
- 'signature': 'append(entity)',
- 'description': 'Add an entity to the end of the collection.',
- 'args': [('entity', 'Entity', 'The entity to add')]
- },
- 'remove': {
- 'signature': 'remove(entity)',
- 'description': 'Remove the first occurrence of an entity from the collection.',
- 'args': [('entity', 'Entity', 'The entity to remove')],
- 'raises': 'ValueError: If entity is not in collection'
- },
- 'extend': {
- 'signature': 'extend(iterable)',
- 'description': 'Add all entities from an iterable to the collection.',
- 'args': [('iterable', 'Iterable[Entity]', 'Entities to add')]
- },
- 'count': {
- 'signature': 'count(entity)',
- 'description': 'Count the number of occurrences of an entity in the collection.',
- 'args': [('entity', 'Entity', 'The entity to count')],
- 'returns': 'int: Number of times entity appears in collection'
- },
- 'index': {
- 'signature': 'index(entity)',
- 'description': 'Find the index of the first occurrence of an entity.',
- 'args': [('entity', 'Entity', 'The entity to find')],
- 'returns': 'int: Index of entity in collection',
- 'raises': 'ValueError: If entity is not in collection'
- }
- },
-
- 'UICollection': {
- 'append': {
- 'signature': 'append(drawable)',
- 'description': 'Add a drawable element to the end of the collection.',
- 'args': [('drawable', 'UIDrawable', 'The drawable element to add')]
- },
- 'remove': {
- 'signature': 'remove(drawable)',
- 'description': 'Remove the first occurrence of a drawable from the collection.',
- 'args': [('drawable', 'UIDrawable', 'The drawable to remove')],
- 'raises': 'ValueError: If drawable is not in collection'
- },
- 'extend': {
- 'signature': 'extend(iterable)',
- 'description': 'Add all drawables from an iterable to the collection.',
- 'args': [('iterable', 'Iterable[UIDrawable]', 'Drawables to add')]
- },
- 'count': {
- 'signature': 'count(drawable)',
- 'description': 'Count the number of occurrences of a drawable in the collection.',
- 'args': [('drawable', 'UIDrawable', 'The drawable to count')],
- 'returns': 'int: Number of times drawable appears in collection'
- },
- 'index': {
- 'signature': 'index(drawable)',
- 'description': 'Find the index of the first occurrence of a drawable.',
- 'args': [('drawable', 'UIDrawable', 'The drawable to find')],
- 'returns': 'int: Index of drawable in collection',
- 'raises': 'ValueError: If drawable is not in collection'
- }
- },
-
- # Animation methods
- 'Animation': {
- 'get_current_value': {
- 'signature': 'get_current_value()',
- 'description': 'Get the current interpolated value of the animation.',
- 'returns': 'float: Current animation value between start and end'
- },
- 'start': {
- 'signature': 'start(target)',
- 'description': 'Start the animation on a target UI element.',
- 'args': [('target', 'UIDrawable', 'The UI element to animate')],
- 'note': 'The target must have the property specified in the animation constructor.'
- },
- 'update': {
- 'signature': 'update(delta_time)',
- 'description': 'Update the animation by the given time delta.',
- 'args': [('delta_time', 'float', 'Time elapsed since last update in seconds')],
- 'returns': 'bool: True if animation is still running, False if finished'
- }
- },
-
- # Color methods
- 'Color': {
- 'from_hex': {
- 'signature': 'from_hex(hex_string)',
- 'description': 'Create a Color from a hexadecimal color string.',
- 'args': [('hex_string', 'str', 'Hex color string (e.g., "#FF0000" or "FF0000")')],
- 'returns': 'Color: New Color object from hex string',
- 'example': 'red = Color.from_hex("#FF0000")'
- },
- 'to_hex': {
- 'signature': 'to_hex()',
- 'description': 'Convert this Color to a hexadecimal string.',
- 'returns': 'str: Hex color string in format "#RRGGBB"',
- 'example': 'hex_str = color.to_hex() # Returns "#FF0000"'
- },
- 'lerp': {
- 'signature': 'lerp(other, t)',
- 'description': 'Linearly interpolate between this color and another.',
- 'args': [
- ('other', 'Color', 'The color to interpolate towards'),
- ('t', 'float', 'Interpolation factor from 0.0 to 1.0')
- ],
- 'returns': 'Color: New interpolated Color object',
- 'example': 'mixed = red.lerp(blue, 0.5) # 50% between red and blue'
- }
- },
-
- # Vector methods
- 'Vector': {
- 'magnitude': {
- 'signature': 'magnitude()',
- 'description': 'Calculate the length/magnitude of this vector.',
- 'returns': 'float: The magnitude of the vector',
- 'example': 'length = vector.magnitude()'
- },
- 'magnitude_squared': {
- 'signature': 'magnitude_squared()',
- 'description': '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.'
- },
- 'normalize': {
- 'signature': 'normalize()',
- 'description': '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': {
- 'signature': 'dot(other)',
- 'description': 'Calculate the dot product with another vector.',
- 'args': [('other', 'Vector', 'The other vector')],
- 'returns': 'float: Dot product of the two vectors'
- },
- 'distance_to': {
- 'signature': 'distance_to(other)',
- 'description': 'Calculate the distance to another vector.',
- 'args': [('other', 'Vector', 'The other vector')],
- 'returns': 'float: Distance between the two vectors'
- },
- 'angle': {
- 'signature': 'angle()',
- 'description': 'Get the angle of this vector in radians.',
- 'returns': 'float: Angle in radians from positive x-axis'
- },
- 'copy': {
- 'signature': 'copy()',
- 'description': 'Create a copy of this vector.',
- 'returns': 'Vector: New Vector object with same x and y values'
- }
- },
-
- # Scene methods
- 'Scene': {
- 'activate': {
- 'signature': 'activate()',
- 'description': 'Make this scene the active scene.',
- 'note': 'Equivalent to calling setScene() with this scene\'s name.'
- },
- 'get_ui': {
- 'signature': 'get_ui()',
- 'description': 'Get the UI element collection for this scene.',
- 'returns': 'UICollection: Collection of all UI elements in this scene'
- },
- 'keypress': {
- 'signature': 'keypress(handler)',
- 'description': 'Register a keyboard handler function for this scene.',
- 'args': [('handler', 'callable', 'Function that takes (key_name: str, is_pressed: bool)')],
- 'note': 'Alternative to overriding the on_keypress method.'
- },
- 'register_keyboard': {
- 'signature': 'register_keyboard(callable)',
- 'description': 'Register a keyboard event handler function for the scene.',
- 'args': [('callable', 'callable', 'Function that takes (key: str, action: str) parameters')],
- 'note': 'Alternative to overriding the on_keypress method when subclassing Scene objects.',
- 'example': '''def handle_keyboard(key, action):
- print(f"Key '{key}' was {action}")
- if key == "q" and action == "press":
- # Handle quit
- pass
-scene.register_keyboard(handle_keyboard)'''
- }
- },
-
- # Timer methods
- 'Timer': {
- 'pause': {
- 'signature': 'pause()',
- 'description': 'Pause the timer, stopping its callback execution.',
- 'note': 'Use resume() to continue the timer from where it was paused.'
- },
- 'resume': {
- 'signature': 'resume()',
- 'description': 'Resume a paused timer.',
- 'note': 'Has no effect if timer is not paused.'
- },
- 'cancel': {
- 'signature': 'cancel()',
- 'description': 'Cancel the timer and remove it from the system.',
- 'note': 'After cancelling, the timer object cannot be reused.'
- },
- 'restart': {
- 'signature': 'restart()',
- 'description': 'Restart the timer from the beginning.',
- 'note': 'Resets the timer\'s internal clock to zero.'
- }
- },
-
- # Window methods
- 'Window': {
- 'get': {
- 'signature': 'get()',
- 'description': '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': {
- 'signature': 'center()',
- 'description': 'Center the window on the screen.',
- 'note': 'Only works if the window is not fullscreen.'
- },
- 'screenshot': {
- 'signature': 'screenshot(filename)',
- 'description': 'Take a screenshot and save it to a file.',
- 'args': [('filename', 'str', 'Path where to save the screenshot')],
- 'note': 'Supports PNG, JPG, and BMP formats based on file extension.'
- }
- }
- }
-
-def get_complete_function_documentation():
- """Return complete documentation for ALL module functions."""
- return {
- # Scene Management
- 'createScene': {
- 'signature': 'createScene(name: str) -> None',
- 'description': 'Create a new empty scene with the given name.',
- 'args': [('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': 'mcrfpy.createScene("game_over")'
- },
- 'setScene': {
- 'signature': 'setScene(scene: str, transition: str = None, duration: float = 0.0) -> None',
- 'description': 'Switch to a different scene with optional transition effect.',
- 'args': [
- ('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': 'mcrfpy.setScene("game", "fade", 0.5)'
- },
- 'currentScene': {
- 'signature': 'currentScene() -> str',
- 'description': 'Get the name of the currently active scene.',
- 'returns': 'str: Name of the current scene',
- 'example': 'scene_name = mcrfpy.currentScene()'
- },
- 'sceneUI': {
- 'signature': 'sceneUI(scene: str = None) -> UICollection',
- 'description': 'Get all UI elements for a scene.',
- 'args': [('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': 'ui_elements = mcrfpy.sceneUI("game")'
- },
- 'keypressScene': {
- 'signature': 'keypressScene(handler: callable) -> None',
- 'description': 'Set the keyboard event handler for the current scene.',
- 'args': [('handler', 'callable', 'Function that receives (key_name: str, is_pressed: bool)')],
- 'example': '''def on_key(key, pressed):
- if key == "SPACE" and pressed:
- player.jump()
-mcrfpy.keypressScene(on_key)'''
- },
-
- # Audio Functions
- 'createSoundBuffer': {
- 'signature': 'createSoundBuffer(filename: str) -> int',
- 'description': 'Load a sound effect from a file and return its buffer ID.',
- 'args': [('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': 'jump_sound = mcrfpy.createSoundBuffer("assets/jump.wav")'
- },
- 'loadMusic': {
- 'signature': 'loadMusic(filename: str, loop: bool = True) -> None',
- 'description': 'Load and immediately play background music from a file.',
- 'args': [
- ('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': 'mcrfpy.loadMusic("assets/background.ogg", True)'
- },
- 'playSound': {
- 'signature': 'playSound(buffer_id: int) -> None',
- 'description': 'Play a sound effect using a previously loaded buffer.',
- 'args': [('buffer_id', 'int', 'Sound buffer ID returned by createSoundBuffer()')],
- 'raises': 'RuntimeError: If the buffer ID is invalid',
- 'example': 'mcrfpy.playSound(jump_sound)'
- },
- 'getMusicVolume': {
- 'signature': 'getMusicVolume() -> int',
- 'description': 'Get the current music volume level.',
- 'returns': 'int: Current volume (0-100)',
- 'example': 'current_volume = mcrfpy.getMusicVolume()'
- },
- 'getSoundVolume': {
- 'signature': 'getSoundVolume() -> int',
- 'description': 'Get the current sound effects volume level.',
- 'returns': 'int: Current volume (0-100)',
- 'example': 'current_volume = mcrfpy.getSoundVolume()'
- },
- 'setMusicVolume': {
- 'signature': 'setMusicVolume(volume: int) -> None',
- 'description': 'Set the global music volume.',
- 'args': [('volume', 'int', 'Volume level from 0 (silent) to 100 (full volume)')],
- 'example': 'mcrfpy.setMusicVolume(50) # Set to 50% volume'
- },
- 'setSoundVolume': {
- 'signature': 'setSoundVolume(volume: int) -> None',
- 'description': 'Set the global sound effects volume.',
- 'args': [('volume', 'int', 'Volume level from 0 (silent) to 100 (full volume)')],
- 'example': 'mcrfpy.setSoundVolume(75) # Set to 75% volume'
- },
-
- # UI Utilities
- 'find': {
- 'signature': 'find(name: str, scene: str = None) -> UIDrawable | None',
- 'description': 'Find the first UI element with the specified name.',
- 'args': [
- ('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': 'button = mcrfpy.find("start_button")'
- },
- 'findAll': {
- 'signature': 'findAll(pattern: str, scene: str = None) -> list',
- 'description': 'Find all UI elements matching a name pattern.',
- 'args': [
- ('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': 'enemies = mcrfpy.findAll("enemy_*")'
- },
-
- # System Functions
- 'exit': {
- 'signature': 'exit() -> None',
- 'description': 'Cleanly shut down the game engine and exit the application.',
- 'note': 'This immediately closes the window and terminates the program.',
- 'example': 'mcrfpy.exit()'
- },
- 'getMetrics': {
- 'signature': 'getMetrics() -> dict',
- 'description': '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': 'metrics = mcrfpy.getMetrics()'
- },
- 'setTimer': {
- 'signature': 'setTimer(name: str, handler: callable, interval: int) -> None',
- 'description': 'Create or update a recurring timer.',
- 'args': [
- ('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': '''def update_score(runtime):
- score += 1
-mcrfpy.setTimer("score_update", update_score, 1000)'''
- },
- 'delTimer': {
- 'signature': 'delTimer(name: str) -> None',
- 'description': 'Stop and remove a timer.',
- 'args': [('name', 'str', 'Timer identifier to remove')],
- 'note': 'No error is raised if the timer doesn\'t exist.',
- 'example': 'mcrfpy.delTimer("score_update")'
- },
- 'setScale': {
- 'signature': 'setScale(multiplier: float) -> None',
- 'description': 'Scale the game window size.',
- 'args': [('multiplier', 'float', 'Scale factor (e.g., 2.0 for double size)')],
- 'note': 'The internal resolution remains 1024x768, but the window is scaled.',
- 'example': 'mcrfpy.setScale(2.0) # Double the window size'
- }
- }
-
-def get_complete_property_documentation():
- """Return complete documentation for ALL properties."""
- return {
- 'Animation': {
- '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)'
- },
- 'GridPoint': {
- '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'
- },
- 'GridPointState': {
- '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'
- }
- }
-
-def generate_complete_html_documentation():
- """Generate complete HTML documentation with NO missing methods."""
-
- # Get all documentation data
- method_docs = get_complete_method_documentation()
- function_docs = get_complete_function_documentation()
- property_docs = get_complete_property_documentation()
-
- html_parts = []
-
- # HTML header with enhanced styling
- html_parts.append('''
-
-
-
-
- McRogueFace API Reference - Complete Documentation
-
-
-
-
-''')
-
- # Title and overview
- html_parts.append('
McRogueFace API Reference - Complete Documentation
')
- html_parts.append(f'
Generated on {datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
')
-
- # Table of contents
- html_parts.append('
')
- html_parts.append('
Table of Contents
')
- html_parts.append('
')
- html_parts.append('
')
-
- # Functions section
- html_parts.append('
Functions
')
-
- # Group functions by category
- categories = {
- 'Scene Management': ['createScene', 'setScene', 'currentScene', 'sceneUI', 'keypressScene'],
- 'Audio': ['createSoundBuffer', 'loadMusic', 'playSound', 'getMusicVolume', 'getSoundVolume', 'setMusicVolume', 'setSoundVolume'],
- 'UI Utilities': ['find', 'findAll'],
- 'System': ['exit', 'getMetrics', 'setTimer', 'delTimer', 'setScale']
- }
-
- for category, functions in categories.items():
- html_parts.append(f'
{category}
')
- for func_name in functions:
- if func_name in function_docs:
- html_parts.append(format_function_html(func_name, function_docs[func_name]))
-
- # Classes section
- html_parts.append('
Classes
')
-
- # Get all classes from mcrfpy
- classes = []
- for name in sorted(dir(mcrfpy)):
- if not name.startswith('_'):
- obj = getattr(mcrfpy, name)
- if isinstance(obj, type):
- classes.append((name, obj))
-
- # Generate class documentation
- for class_name, cls in classes:
- html_parts.append(format_class_html_complete(class_name, cls, method_docs, property_docs))
-
- # Automation section
- if hasattr(mcrfpy, 'automation'):
- html_parts.append('
Automation Module
')
- html_parts.append('
The mcrfpy.automation module provides testing and automation capabilities.
')
-
- automation = mcrfpy.automation
- for name in sorted(dir(automation)):
- if not name.startswith('_'):
- obj = getattr(automation, name)
- if callable(obj):
- html_parts.append(f'
')
- html_parts.append(f'
automation.{name}
')
- if obj.__doc__:
- doc_parts = obj.__doc__.split(' - ')
- if len(doc_parts) > 1:
- html_parts.append(f'
{escape_html(doc_parts[1])}
')
- else:
- html_parts.append(f'
{escape_html(obj.__doc__)}
')
- html_parts.append('
')
-
- html_parts.append('
')
- html_parts.append('')
- html_parts.append('')
-
- return '\n'.join(html_parts)
-
-def format_function_html(func_name, func_doc):
- """Format a function with complete documentation."""
- html_parts = []
-
- html_parts.append('')
- html_parts.append(f'
{func_doc["signature"]}
')
- html_parts.append(f'
{escape_html(func_doc["description"])}
')
-
- # Arguments
- if 'args' in func_doc:
- html_parts.append('
')
- html_parts.append('
Arguments:
')
- for arg in func_doc['args']:
- html_parts.append('
')
- html_parts.append(f'{arg[0]} ')
- html_parts.append(f'({arg[1]}): ')
- html_parts.append(f'{escape_html(arg[2])}')
- html_parts.append('
')
- html_parts.append('
')
-
- # Returns
- if 'returns' in func_doc:
- html_parts.append('
')
- html_parts.append(f'Returns: {escape_html(func_doc["returns"])}')
- html_parts.append('
')
-
- # Raises
- if 'raises' in func_doc:
- html_parts.append('
')
- html_parts.append(f'Raises: {escape_html(func_doc["raises"])}')
- html_parts.append('
')
-
- # Note
- if 'note' in func_doc:
- html_parts.append('
')
- html_parts.append(f'Note: {escape_html(func_doc["note"])}')
- html_parts.append('
')
-
- # Example
- if 'example' in func_doc:
- html_parts.append('
')
- html_parts.append('
Example:
')
- html_parts.append('
')
- html_parts.append(escape_html(func_doc['example']))
- html_parts.append('
')
- html_parts.append('
')
-
- html_parts.append('
')
-
- return '\n'.join(html_parts)
-
-def format_class_html_complete(class_name, cls, method_docs, property_docs):
- """Format a class with complete documentation."""
- html_parts = []
-
- html_parts.append('')
- html_parts.append(f'
{class_name}
')
-
- # Class description
- if cls.__doc__:
- html_parts.append(f'
{escape_html(cls.__doc__)}
')
-
- # Properties
- if class_name in property_docs:
- html_parts.append('
Properties:
')
- for prop_name, prop_desc in property_docs[class_name].items():
- html_parts.append(f'
')
- html_parts.append(f'{prop_name}: {escape_html(prop_desc)}')
- html_parts.append('
')
-
- # Methods
- methods_to_document = []
-
- # Add inherited methods for UI classes
- if any(base.__name__ == 'Drawable' for base in cls.__bases__ if hasattr(base, '__name__')):
- methods_to_document.extend(['get_bounds', 'move', 'resize'])
-
- # Add class-specific methods
- if class_name in method_docs:
- methods_to_document.extend(method_docs[class_name].keys())
-
- # Add methods from introspection
- for attr_name in dir(cls):
- if not attr_name.startswith('_') and callable(getattr(cls, attr_name)):
- if attr_name not in methods_to_document:
- methods_to_document.append(attr_name)
-
- if methods_to_document:
- html_parts.append('
Methods:
')
- for method_name in set(methods_to_document):
- # Get method documentation
- method_doc = None
- if class_name in method_docs and method_name in method_docs[class_name]:
- method_doc = method_docs[class_name][method_name]
- elif method_name in method_docs.get('Drawable', {}):
- method_doc = method_docs['Drawable'][method_name]
-
- if method_doc:
- html_parts.append(format_method_html(method_name, method_doc))
- else:
- # Basic method with no documentation
- html_parts.append(f'
')
- html_parts.append(f'{method_name}(...)')
- html_parts.append('
')
-
- html_parts.append('
')
-
- return '\n'.join(html_parts)
-
-def format_method_html(method_name, method_doc):
- """Format a method with complete documentation."""
- html_parts = []
-
- html_parts.append('')
- html_parts.append(f'
{method_doc["signature"]}
')
- html_parts.append(f'
{escape_html(method_doc["description"])}
')
-
- # Arguments
- if 'args' in method_doc:
- for arg in method_doc['args']:
- html_parts.append(f'
')
- html_parts.append(f'{arg[0]} ')
- html_parts.append(f'({arg[1]}): ')
- html_parts.append(f'{escape_html(arg[2])}')
- html_parts.append('
')
-
- # Returns
- if 'returns' in method_doc:
- html_parts.append(f'
')
- html_parts.append(f'Returns: {escape_html(method_doc["returns"])}')
- html_parts.append('
')
-
- # Note
- if 'note' in method_doc:
- html_parts.append(f'
')
- html_parts.append(f'Note: {escape_html(method_doc["note"])}')
- html_parts.append('
')
-
- # Example
- if 'example' in method_doc:
- html_parts.append(f'
')
- html_parts.append('
Example:')
- html_parts.append('
')
- html_parts.append(escape_html(method_doc['example']))
- html_parts.append('
')
- html_parts.append('
')
-
- html_parts.append('
')
-
- return '\n'.join(html_parts)
-
-def main():
- """Generate complete HTML documentation with zero missing methods."""
- print("Generating COMPLETE HTML API documentation...")
-
- # Generate HTML
- html_content = generate_complete_html_documentation()
-
- # Write to file
- output_path = Path("docs/api_reference_complete.html")
- output_path.parent.mkdir(exist_ok=True)
-
- with open(output_path, 'w', encoding='utf-8') as f:
- f.write(html_content)
-
- print(f"✓ Generated {output_path}")
- print(f" File size: {len(html_content):,} bytes")
-
- # Count "..." instances
- ellipsis_count = html_content.count('...')
- print(f" Ellipsis instances: {ellipsis_count}")
-
- if ellipsis_count == 0:
- print("✅ SUCCESS: No missing documentation found!")
- else:
- print(f"❌ WARNING: {ellipsis_count} methods still need documentation")
-
-if __name__ == '__main__':
- main()
\ No newline at end of file
diff --git a/tools/generate_complete_markdown_docs.py b/tools/generate_complete_markdown_docs.py
deleted file mode 100644
index 89fab79..0000000
--- a/tools/generate_complete_markdown_docs.py
+++ /dev/null
@@ -1,821 +0,0 @@
-#!/usr/bin/env python3
-"""Generate COMPLETE Markdown API reference documentation for McRogueFace with NO missing methods."""
-
-import os
-import sys
-import datetime
-from pathlib import Path
-import mcrfpy
-
-def get_complete_method_documentation():
- """Return complete documentation for ALL methods across all classes."""
- return {
- # Base Drawable methods (inherited by all UI elements)
- 'Drawable': {
- 'get_bounds': {
- 'signature': 'get_bounds()',
- 'description': '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.'
- },
- 'move': {
- 'signature': 'move(dx, dy)',
- 'description': 'Move the element by a relative offset.',
- 'args': [
- ('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.'
- },
- 'resize': {
- 'signature': 'resize(width, height)',
- 'description': 'Resize the element to new dimensions.',
- 'args': [
- ('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.'
- }
- },
-
- # Entity-specific methods
- 'Entity': {
- 'at': {
- 'signature': 'at(x, y)',
- 'description': 'Check if this entity is at the specified grid coordinates.',
- 'args': [
- ('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'
- },
- 'die': {
- 'signature': 'die()',
- 'description': 'Remove this entity from its parent grid.',
- 'note': 'The entity object remains valid but is no longer rendered or updated.'
- },
- 'index': {
- 'signature': 'index()',
- 'description': 'Get the index of this entity in its parent grid\'s entity list.',
- 'returns': 'int: Index position, or -1 if not in a grid'
- }
- },
-
- # Grid-specific methods
- 'Grid': {
- 'at': {
- 'signature': 'at(x, y)',
- 'description': 'Get the GridPoint at the specified grid coordinates.',
- 'args': [
- ('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'
- }
- },
-
- # Collection methods
- 'EntityCollection': {
- 'append': {
- 'signature': 'append(entity)',
- 'description': 'Add an entity to the end of the collection.',
- 'args': [('entity', 'Entity', 'The entity to add')]
- },
- 'remove': {
- 'signature': 'remove(entity)',
- 'description': 'Remove the first occurrence of an entity from the collection.',
- 'args': [('entity', 'Entity', 'The entity to remove')],
- 'raises': 'ValueError: If entity is not in collection'
- },
- 'extend': {
- 'signature': 'extend(iterable)',
- 'description': 'Add all entities from an iterable to the collection.',
- 'args': [('iterable', 'Iterable[Entity]', 'Entities to add')]
- },
- 'count': {
- 'signature': 'count(entity)',
- 'description': 'Count the number of occurrences of an entity in the collection.',
- 'args': [('entity', 'Entity', 'The entity to count')],
- 'returns': 'int: Number of times entity appears in collection'
- },
- 'index': {
- 'signature': 'index(entity)',
- 'description': 'Find the index of the first occurrence of an entity.',
- 'args': [('entity', 'Entity', 'The entity to find')],
- 'returns': 'int: Index of entity in collection',
- 'raises': 'ValueError: If entity is not in collection'
- }
- },
-
- 'UICollection': {
- 'append': {
- 'signature': 'append(drawable)',
- 'description': 'Add a drawable element to the end of the collection.',
- 'args': [('drawable', 'UIDrawable', 'The drawable element to add')]
- },
- 'remove': {
- 'signature': 'remove(drawable)',
- 'description': 'Remove the first occurrence of a drawable from the collection.',
- 'args': [('drawable', 'UIDrawable', 'The drawable to remove')],
- 'raises': 'ValueError: If drawable is not in collection'
- },
- 'extend': {
- 'signature': 'extend(iterable)',
- 'description': 'Add all drawables from an iterable to the collection.',
- 'args': [('iterable', 'Iterable[UIDrawable]', 'Drawables to add')]
- },
- 'count': {
- 'signature': 'count(drawable)',
- 'description': 'Count the number of occurrences of a drawable in the collection.',
- 'args': [('drawable', 'UIDrawable', 'The drawable to count')],
- 'returns': 'int: Number of times drawable appears in collection'
- },
- 'index': {
- 'signature': 'index(drawable)',
- 'description': 'Find the index of the first occurrence of a drawable.',
- 'args': [('drawable', 'UIDrawable', 'The drawable to find')],
- 'returns': 'int: Index of drawable in collection',
- 'raises': 'ValueError: If drawable is not in collection'
- }
- },
-
- # Animation methods
- 'Animation': {
- 'get_current_value': {
- 'signature': 'get_current_value()',
- 'description': 'Get the current interpolated value of the animation.',
- 'returns': 'float: Current animation value between start and end'
- },
- 'start': {
- 'signature': 'start(target)',
- 'description': 'Start the animation on a target UI element.',
- 'args': [('target', 'UIDrawable', 'The UI element to animate')],
- 'note': 'The target must have the property specified in the animation constructor.'
- },
- 'update': {
- 'signature': 'update(delta_time)',
- 'description': 'Update the animation by the given time delta.',
- 'args': [('delta_time', 'float', 'Time elapsed since last update in seconds')],
- 'returns': 'bool: True if animation is still running, False if finished'
- }
- },
-
- # Color methods
- 'Color': {
- 'from_hex': {
- 'signature': 'from_hex(hex_string)',
- 'description': 'Create a Color from a hexadecimal color string.',
- 'args': [('hex_string', 'str', 'Hex color string (e.g., "#FF0000" or "FF0000")')],
- 'returns': 'Color: New Color object from hex string',
- 'example': 'red = Color.from_hex("#FF0000")'
- },
- 'to_hex': {
- 'signature': 'to_hex()',
- 'description': 'Convert this Color to a hexadecimal string.',
- 'returns': 'str: Hex color string in format "#RRGGBB"',
- 'example': 'hex_str = color.to_hex() # Returns "#FF0000"'
- },
- 'lerp': {
- 'signature': 'lerp(other, t)',
- 'description': 'Linearly interpolate between this color and another.',
- 'args': [
- ('other', 'Color', 'The color to interpolate towards'),
- ('t', 'float', 'Interpolation factor from 0.0 to 1.0')
- ],
- 'returns': 'Color: New interpolated Color object',
- 'example': 'mixed = red.lerp(blue, 0.5) # 50% between red and blue'
- }
- },
-
- # Vector methods
- 'Vector': {
- 'magnitude': {
- 'signature': 'magnitude()',
- 'description': 'Calculate the length/magnitude of this vector.',
- 'returns': 'float: The magnitude of the vector'
- },
- 'magnitude_squared': {
- 'signature': 'magnitude_squared()',
- 'description': '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.'
- },
- 'normalize': {
- 'signature': 'normalize()',
- 'description': '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': {
- 'signature': 'dot(other)',
- 'description': 'Calculate the dot product with another vector.',
- 'args': [('other', 'Vector', 'The other vector')],
- 'returns': 'float: Dot product of the two vectors'
- },
- 'distance_to': {
- 'signature': 'distance_to(other)',
- 'description': 'Calculate the distance to another vector.',
- 'args': [('other', 'Vector', 'The other vector')],
- 'returns': 'float: Distance between the two vectors'
- },
- 'angle': {
- 'signature': 'angle()',
- 'description': 'Get the angle of this vector in radians.',
- 'returns': 'float: Angle in radians from positive x-axis'
- },
- 'copy': {
- 'signature': 'copy()',
- 'description': 'Create a copy of this vector.',
- 'returns': 'Vector: New Vector object with same x and y values'
- }
- },
-
- # Scene methods
- 'Scene': {
- 'activate': {
- 'signature': 'activate()',
- 'description': 'Make this scene the active scene.',
- 'note': 'Equivalent to calling setScene() with this scene\'s name.'
- },
- 'get_ui': {
- 'signature': 'get_ui()',
- 'description': 'Get the UI element collection for this scene.',
- 'returns': 'UICollection: Collection of all UI elements in this scene'
- },
- 'keypress': {
- 'signature': 'keypress(handler)',
- 'description': 'Register a keyboard handler function for this scene.',
- 'args': [('handler', 'callable', 'Function that takes (key_name: str, is_pressed: bool)')],
- 'note': 'Alternative to overriding the on_keypress method.'
- },
- 'register_keyboard': {
- 'signature': 'register_keyboard(callable)',
- 'description': 'Register a keyboard event handler function for the scene.',
- 'args': [('callable', 'callable', 'Function that takes (key: str, action: str) parameters')],
- 'note': 'Alternative to overriding the on_keypress method when subclassing Scene objects.',
- 'example': '''def handle_keyboard(key, action):
- print(f"Key '{key}' was {action}")
-scene.register_keyboard(handle_keyboard)'''
- }
- },
-
- # Timer methods
- 'Timer': {
- 'pause': {
- 'signature': 'pause()',
- 'description': 'Pause the timer, stopping its callback execution.',
- 'note': 'Use resume() to continue the timer from where it was paused.'
- },
- 'resume': {
- 'signature': 'resume()',
- 'description': 'Resume a paused timer.',
- 'note': 'Has no effect if timer is not paused.'
- },
- 'cancel': {
- 'signature': 'cancel()',
- 'description': 'Cancel the timer and remove it from the system.',
- 'note': 'After cancelling, the timer object cannot be reused.'
- },
- 'restart': {
- 'signature': 'restart()',
- 'description': 'Restart the timer from the beginning.',
- 'note': 'Resets the timer\'s internal clock to zero.'
- }
- },
-
- # Window methods
- 'Window': {
- 'get': {
- 'signature': 'get()',
- 'description': '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': {
- 'signature': 'center()',
- 'description': 'Center the window on the screen.',
- 'note': 'Only works if the window is not fullscreen.'
- },
- 'screenshot': {
- 'signature': 'screenshot(filename)',
- 'description': 'Take a screenshot and save it to a file.',
- 'args': [('filename', 'str', 'Path where to save the screenshot')],
- 'note': 'Supports PNG, JPG, and BMP formats based on file extension.'
- }
- }
- }
-
-def get_complete_function_documentation():
- """Return complete documentation for ALL module functions."""
- return {
- # Scene Management
- 'createScene': {
- 'signature': 'createScene(name: str) -> None',
- 'description': 'Create a new empty scene with the given name.',
- 'args': [('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': 'mcrfpy.createScene("game_over")'
- },
- 'setScene': {
- 'signature': 'setScene(scene: str, transition: str = None, duration: float = 0.0) -> None',
- 'description': 'Switch to a different scene with optional transition effect.',
- 'args': [
- ('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': 'mcrfpy.setScene("game", "fade", 0.5)'
- },
- 'currentScene': {
- 'signature': 'currentScene() -> str',
- 'description': 'Get the name of the currently active scene.',
- 'returns': 'str: Name of the current scene',
- 'example': 'scene_name = mcrfpy.currentScene()'
- },
- 'sceneUI': {
- 'signature': 'sceneUI(scene: str = None) -> UICollection',
- 'description': 'Get all UI elements for a scene.',
- 'args': [('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': 'ui_elements = mcrfpy.sceneUI("game")'
- },
- 'keypressScene': {
- 'signature': 'keypressScene(handler: callable) -> None',
- 'description': 'Set the keyboard event handler for the current scene.',
- 'args': [('handler', 'callable', 'Function that receives (key_name: str, is_pressed: bool)')],
- 'example': '''def on_key(key, pressed):
- if key == "SPACE" and pressed:
- player.jump()
-mcrfpy.keypressScene(on_key)'''
- },
-
- # Audio Functions
- 'createSoundBuffer': {
- 'signature': 'createSoundBuffer(filename: str) -> int',
- 'description': 'Load a sound effect from a file and return its buffer ID.',
- 'args': [('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': 'jump_sound = mcrfpy.createSoundBuffer("assets/jump.wav")'
- },
- 'loadMusic': {
- 'signature': 'loadMusic(filename: str, loop: bool = True) -> None',
- 'description': 'Load and immediately play background music from a file.',
- 'args': [
- ('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': 'mcrfpy.loadMusic("assets/background.ogg", True)'
- },
- 'playSound': {
- 'signature': 'playSound(buffer_id: int) -> None',
- 'description': 'Play a sound effect using a previously loaded buffer.',
- 'args': [('buffer_id', 'int', 'Sound buffer ID returned by createSoundBuffer()')],
- 'raises': 'RuntimeError: If the buffer ID is invalid',
- 'example': 'mcrfpy.playSound(jump_sound)'
- },
- 'getMusicVolume': {
- 'signature': 'getMusicVolume() -> int',
- 'description': 'Get the current music volume level.',
- 'returns': 'int: Current volume (0-100)',
- 'example': 'current_volume = mcrfpy.getMusicVolume()'
- },
- 'getSoundVolume': {
- 'signature': 'getSoundVolume() -> int',
- 'description': 'Get the current sound effects volume level.',
- 'returns': 'int: Current volume (0-100)',
- 'example': 'current_volume = mcrfpy.getSoundVolume()'
- },
- 'setMusicVolume': {
- 'signature': 'setMusicVolume(volume: int) -> None',
- 'description': 'Set the global music volume.',
- 'args': [('volume', 'int', 'Volume level from 0 (silent) to 100 (full volume)')],
- 'example': 'mcrfpy.setMusicVolume(50) # Set to 50% volume'
- },
- 'setSoundVolume': {
- 'signature': 'setSoundVolume(volume: int) -> None',
- 'description': 'Set the global sound effects volume.',
- 'args': [('volume', 'int', 'Volume level from 0 (silent) to 100 (full volume)')],
- 'example': 'mcrfpy.setSoundVolume(75) # Set to 75% volume'
- },
-
- # UI Utilities
- 'find': {
- 'signature': 'find(name: str, scene: str = None) -> UIDrawable | None',
- 'description': 'Find the first UI element with the specified name.',
- 'args': [
- ('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': 'button = mcrfpy.find("start_button")'
- },
- 'findAll': {
- 'signature': 'findAll(pattern: str, scene: str = None) -> list',
- 'description': 'Find all UI elements matching a name pattern.',
- 'args': [
- ('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': 'enemies = mcrfpy.findAll("enemy_*")'
- },
-
- # System Functions
- 'exit': {
- 'signature': 'exit() -> None',
- 'description': 'Cleanly shut down the game engine and exit the application.',
- 'note': 'This immediately closes the window and terminates the program.',
- 'example': 'mcrfpy.exit()'
- },
- 'getMetrics': {
- 'signature': 'getMetrics() -> dict',
- 'description': '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': 'metrics = mcrfpy.getMetrics()'
- },
- 'setTimer': {
- 'signature': 'setTimer(name: str, handler: callable, interval: int) -> None',
- 'description': 'Create or update a recurring timer.',
- 'args': [
- ('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': '''def update_score(runtime):
- score += 1
-mcrfpy.setTimer("score_update", update_score, 1000)'''
- },
- 'delTimer': {
- 'signature': 'delTimer(name: str) -> None',
- 'description': 'Stop and remove a timer.',
- 'args': [('name', 'str', 'Timer identifier to remove')],
- 'note': 'No error is raised if the timer doesn\'t exist.',
- 'example': 'mcrfpy.delTimer("score_update")'
- },
- 'setScale': {
- 'signature': 'setScale(multiplier: float) -> None',
- 'description': 'Scale the game window size.',
- 'args': [('multiplier', 'float', 'Scale factor (e.g., 2.0 for double size)')],
- 'note': 'The internal resolution remains 1024x768, but the window is scaled.',
- 'example': 'mcrfpy.setScale(2.0) # Double the window size'
- }
- }
-
-def get_complete_property_documentation():
- """Return complete documentation for ALL properties."""
- return {
- 'Animation': {
- '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)'
- },
- 'GridPoint': {
- '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'
- },
- 'GridPointState': {
- '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'
- }
- }
-
-def format_method_markdown(method_name, method_doc):
- """Format a method as markdown."""
- lines = []
-
- lines.append(f"#### `{method_doc['signature']}`")
- lines.append("")
- lines.append(method_doc['description'])
- lines.append("")
-
- # Arguments
- if 'args' in method_doc:
- lines.append("**Arguments:**")
- for arg in method_doc['args']:
- lines.append(f"- `{arg[0]}` (*{arg[1]}*): {arg[2]}")
- lines.append("")
-
- # Returns
- if 'returns' in method_doc:
- lines.append(f"**Returns:** {method_doc['returns']}")
- lines.append("")
-
- # Raises
- if 'raises' in method_doc:
- lines.append(f"**Raises:** {method_doc['raises']}")
- lines.append("")
-
- # Note
- if 'note' in method_doc:
- lines.append(f"**Note:** {method_doc['note']}")
- lines.append("")
-
- # Example
- if 'example' in method_doc:
- lines.append("**Example:**")
- lines.append("```python")
- lines.append(method_doc['example'])
- lines.append("```")
- lines.append("")
-
- return lines
-
-def format_function_markdown(func_name, func_doc):
- """Format a function as markdown."""
- lines = []
-
- lines.append(f"### `{func_doc['signature']}`")
- lines.append("")
- lines.append(func_doc['description'])
- lines.append("")
-
- # Arguments
- if 'args' in func_doc:
- lines.append("**Arguments:**")
- for arg in func_doc['args']:
- lines.append(f"- `{arg[0]}` (*{arg[1]}*): {arg[2]}")
- lines.append("")
-
- # Returns
- if 'returns' in func_doc:
- lines.append(f"**Returns:** {func_doc['returns']}")
- lines.append("")
-
- # Raises
- if 'raises' in func_doc:
- lines.append(f"**Raises:** {func_doc['raises']}")
- lines.append("")
-
- # Note
- if 'note' in func_doc:
- lines.append(f"**Note:** {func_doc['note']}")
- lines.append("")
-
- # Example
- if 'example' in func_doc:
- lines.append("**Example:**")
- lines.append("```python")
- lines.append(func_doc['example'])
- lines.append("```")
- lines.append("")
-
- lines.append("---")
- lines.append("")
-
- return lines
-
-def generate_complete_markdown_documentation():
- """Generate complete markdown documentation with NO missing methods."""
-
- # Get all documentation data
- method_docs = get_complete_method_documentation()
- function_docs = get_complete_function_documentation()
- property_docs = get_complete_property_documentation()
-
- lines = []
-
- # Header
- lines.append("# McRogueFace API Reference")
- lines.append("")
- lines.append(f"*Generated on {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}*")
- lines.append("")
-
- # Overview
- if mcrfpy.__doc__:
- lines.append("## Overview")
- lines.append("")
- # Process the docstring properly
- doc_text = mcrfpy.__doc__.replace('\\n', '\n')
- lines.append(doc_text)
- lines.append("")
-
- # Table of Contents
- lines.append("## Table of Contents")
- lines.append("")
- lines.append("- [Functions](#functions)")
- lines.append(" - [Scene Management](#scene-management)")
- lines.append(" - [Audio](#audio)")
- lines.append(" - [UI Utilities](#ui-utilities)")
- lines.append(" - [System](#system)")
- lines.append("- [Classes](#classes)")
- lines.append(" - [UI Components](#ui-components)")
- lines.append(" - [Collections](#collections)")
- lines.append(" - [System Types](#system-types)")
- lines.append(" - [Other Classes](#other-classes)")
- lines.append("- [Automation Module](#automation-module)")
- lines.append("")
-
- # Functions section
- lines.append("## Functions")
- lines.append("")
-
- # Group functions by category
- categories = {
- 'Scene Management': ['createScene', 'setScene', 'currentScene', 'sceneUI', 'keypressScene'],
- 'Audio': ['createSoundBuffer', 'loadMusic', 'playSound', 'getMusicVolume', 'getSoundVolume', 'setMusicVolume', 'setSoundVolume'],
- 'UI Utilities': ['find', 'findAll'],
- 'System': ['exit', 'getMetrics', 'setTimer', 'delTimer', 'setScale']
- }
-
- for category, functions in categories.items():
- lines.append(f"### {category}")
- lines.append("")
- for func_name in functions:
- if func_name in function_docs:
- lines.extend(format_function_markdown(func_name, function_docs[func_name]))
-
- # Classes section
- lines.append("## Classes")
- lines.append("")
-
- # Get all classes from mcrfpy
- classes = []
- for name in sorted(dir(mcrfpy)):
- if not name.startswith('_'):
- obj = getattr(mcrfpy, name)
- if isinstance(obj, type):
- classes.append((name, obj))
-
- # Group classes
- ui_classes = ['Frame', 'Caption', 'Sprite', 'Grid', 'Entity']
- collection_classes = ['EntityCollection', 'UICollection', 'UICollectionIter', 'UIEntityCollectionIter']
- system_classes = ['Color', 'Vector', 'Texture', 'Font']
- other_classes = [name for name, _ in classes if name not in ui_classes + collection_classes + system_classes]
-
- # UI Components
- lines.append("### UI Components")
- lines.append("")
- for class_name in ui_classes:
- if any(name == class_name for name, _ in classes):
- lines.extend(format_class_markdown(class_name, method_docs, property_docs))
-
- # Collections
- lines.append("### Collections")
- lines.append("")
- for class_name in collection_classes:
- if any(name == class_name for name, _ in classes):
- lines.extend(format_class_markdown(class_name, method_docs, property_docs))
-
- # System Types
- lines.append("### System Types")
- lines.append("")
- for class_name in system_classes:
- if any(name == class_name for name, _ in classes):
- lines.extend(format_class_markdown(class_name, method_docs, property_docs))
-
- # Other Classes
- lines.append("### Other Classes")
- lines.append("")
- for class_name in other_classes:
- lines.extend(format_class_markdown(class_name, method_docs, property_docs))
-
- # Automation section
- if hasattr(mcrfpy, 'automation'):
- lines.append("## Automation Module")
- lines.append("")
- lines.append("The `mcrfpy.automation` module provides testing and automation capabilities.")
- lines.append("")
-
- automation = mcrfpy.automation
- for name in sorted(dir(automation)):
- if not name.startswith('_'):
- obj = getattr(automation, name)
- if callable(obj):
- lines.append(f"### `automation.{name}`")
- lines.append("")
- if obj.__doc__:
- doc_parts = obj.__doc__.split(' - ')
- if len(doc_parts) > 1:
- lines.append(doc_parts[1])
- else:
- lines.append(obj.__doc__)
- lines.append("")
- lines.append("---")
- lines.append("")
-
- return '\n'.join(lines)
-
-def format_class_markdown(class_name, method_docs, property_docs):
- """Format a class as markdown."""
- lines = []
-
- lines.append(f"### class `{class_name}`")
- lines.append("")
-
- # Class description from known info
- class_descriptions = {
- 'Frame': 'A rectangular frame UI element that can contain other drawable elements.',
- 'Caption': 'A text display UI element with customizable font and styling.',
- 'Sprite': 'A sprite UI element that displays a texture or portion of a texture atlas.',
- 'Grid': 'A grid-based tilemap UI element for rendering tile-based levels and game worlds.',
- 'Entity': 'Game entity that can be placed in a Grid.',
- 'EntityCollection': 'Container for Entity objects in a Grid. Supports iteration and indexing.',
- 'UICollection': 'Container for UI drawable elements. Supports iteration and indexing.',
- 'UICollectionIter': 'Iterator for UICollection. Automatically created when iterating over a UICollection.',
- 'UIEntityCollectionIter': 'Iterator for EntityCollection. Automatically created when iterating over an EntityCollection.',
- 'Color': 'RGBA color representation.',
- 'Vector': '2D vector for positions and directions.',
- 'Font': 'Font object for text rendering.',
- 'Texture': 'Texture object for image data.',
- 'Animation': 'Animate UI element properties over time.',
- 'GridPoint': 'Represents a single tile in a Grid.',
- 'GridPointState': 'State information for a GridPoint.',
- 'Scene': 'Base class for object-oriented scenes.',
- 'Timer': 'Timer object for scheduled callbacks.',
- 'Window': 'Window singleton for accessing and modifying the game window properties.',
- 'Drawable': 'Base class for all drawable UI elements.'
- }
-
- if class_name in class_descriptions:
- lines.append(class_descriptions[class_name])
- lines.append("")
-
- # Properties
- if class_name in property_docs:
- lines.append("#### Properties")
- lines.append("")
- for prop_name, prop_desc in property_docs[class_name].items():
- lines.append(f"- **`{prop_name}`**: {prop_desc}")
- lines.append("")
-
- # Methods
- methods_to_document = []
-
- # Add inherited methods for UI classes
- if class_name in ['Frame', 'Caption', 'Sprite', 'Grid', 'Entity']:
- methods_to_document.extend(['get_bounds', 'move', 'resize'])
-
- # Add class-specific methods
- if class_name in method_docs:
- methods_to_document.extend(method_docs[class_name].keys())
-
- if methods_to_document:
- lines.append("#### Methods")
- lines.append("")
- for method_name in set(methods_to_document):
- # Get method documentation
- method_doc = None
- if class_name in method_docs and method_name in method_docs[class_name]:
- method_doc = method_docs[class_name][method_name]
- elif method_name in method_docs.get('Drawable', {}):
- method_doc = method_docs['Drawable'][method_name]
-
- if method_doc:
- lines.extend(format_method_markdown(method_name, method_doc))
-
- lines.append("---")
- lines.append("")
-
- return lines
-
-def main():
- """Generate complete markdown documentation with zero missing methods."""
- print("Generating COMPLETE Markdown API documentation...")
-
- # Generate markdown
- markdown_content = generate_complete_markdown_documentation()
-
- # Write to file
- output_path = Path("docs/API_REFERENCE_COMPLETE.md")
- output_path.parent.mkdir(exist_ok=True)
-
- with open(output_path, 'w', encoding='utf-8') as f:
- f.write(markdown_content)
-
- print(f"✓ Generated {output_path}")
- print(f" File size: {len(markdown_content):,} bytes")
-
- # Count "..." instances
- ellipsis_count = markdown_content.count('...')
- print(f" Ellipsis instances: {ellipsis_count}")
-
- if ellipsis_count == 0:
- print("✅ SUCCESS: No missing documentation found!")
- else:
- print(f"❌ WARNING: {ellipsis_count} methods still need documentation")
-
-if __name__ == '__main__':
- main()
\ No newline at end of file