diff --git a/Grid-System.-.md b/Grid-System.md similarity index 96% rename from Grid-System.-.md rename to Grid-System.md index 19f9903..18cc674 100644 --- a/Grid-System.-.md +++ b/Grid-System.md @@ -1,245 +1,245 @@ -# Grid System - -The Grid System is McRogueFace's core spatial container for roguelike game maps. It provides tilemap rendering, entity management, FOV (field of view), and pathfinding integration with libtcod. - -## Quick Reference - -**Related Issues:** -- [#113](../../issues/113) - Batch Operations for Grid (Open - Tier 1) -- [#124](../../issues/124) - Grid Point Animation (Open - Tier 1) -- [#150](../../issues/150) - User-driven Layer Rendering (Closed - Implemented) -- [#148](../../issues/148) - Dirty Flag RenderTexture Caching (Closed - Implemented) -- [#147](../../issues/147) - Dynamic Layer System (Closed - Implemented) -- [#123](../../issues/123) - Chunk-based Grid Rendering (Closed - Implemented) - -**Key Files:** -- `src/UIGrid.h` / `src/UIGrid.cpp` - Main grid implementation -- `src/GridLayers.h` / `src/GridLayers.cpp` - ColorLayer and TileLayer -- `src/UIGridPoint.h` - Individual grid cell (walkability, transparency) -- `src/UIGridPointState.h` - Per-entity perspective/knowledge -- `src/UIEntity.h` / `src/UIEntity.cpp` - Entity system (lives on grid) - -**API Reference:** -- See [mcrfpy.Grid](../../docs/api_reference_dynamic.html#Grid) in generated API docs -- See [mcrfpy.Entity](../../docs/api_reference_dynamic.html#Entity) in generated API docs - -## Architecture Overview - -### Three-Layer Design - -The Grid System uses a three-layer architecture for sophisticated roguelike features: - -1. **Visual Layer** (Rendering Layers) - - What's displayed: tile sprites, colors, overlays - - Implemented via `ColorLayer` and `TileLayer` objects - - Multiple layers per grid with z_index ordering - - Files: `src/GridLayers.h`, `src/GridLayers.cpp` - -2. **World State Layer** (`TCODMap`) - - Physical properties: walkable, transparent, cost - - Used for pathfinding and FOV calculations - - Integration: libtcod via `src/UIGrid.cpp` - -3. **Perspective Layer** (`UIGridPointState`) - - Per-entity knowledge: what each entity has seen/explored - - Enables fog of war, asymmetric information - - File: `src/UIGridPointState.h` - - **Note:** Python access currently limited - see Known Issues - -This architecture follows proven patterns from Caves of Qud, Cogmind, and DCSS. - -### Dynamic Layer System - -Grids support multiple rendering layers that can be added/removed at runtime: - -| Layer Type | Purpose | Methods | -|------------|---------|---------| -| `ColorLayer` | Solid colors per cell (fog, highlights) | `set(x, y, color)`, `at(x, y)`, `fill(color)` | -| `TileLayer` | Texture sprites per cell (terrain, items) | `set(x, y, sprite_index)`, `at(x, y)`, `fill(index)` | - -**z_index semantics:** -- Negative z_index: Renders **below** entities -- Zero or positive z_index: Renders **above** entities -- Lower z_index renders first (behind higher z_index) - -```python -# Create grid with no default layers -grid = mcrfpy.Grid(grid_size=(50, 50), pos=(100, 100), size=(400, 400), layers={}) - -# Add layers explicitly -background = grid.add_layer("color", z_index=-2) # Furthest back -tiles = grid.add_layer("tile", z_index=-1) # Above background, below entities -fog_overlay = grid.add_layer("color", z_index=1) # Above entities (fog of war) - -# Access existing layers -for layer in grid.layers: - print(f"{type(layer).__name__} at z={layer.z_index}") - -# Remove a layer -grid.remove_layer(fog_overlay) -``` - -### Grid → Entity Relationship - -**Entity Lifecycle:** -- Entities live on exactly 0 or 1 grids -- `grid.entities.append(entity)` sets `entity.grid = grid` -- `grid.entities.remove(entity)` sets `entity.grid = None` -- Entity removal handled automatically on grid destruction - -**Spatial Queries:** -- Currently: Linear iteration through entity list -- Planned: SpatialHash for O(1) lookups (see [#115](../../issues/115)) - -## Sub-Pages - -- [[Grid-Rendering-Pipeline]] - How grid renders each frame (chunks, dirty flags, caching) -- [[Grid-TCOD-Integration]] - FOV, pathfinding, walkability -- [[Grid-Entity-Lifecycle]] - Entity creation, movement, removal - -## Common Tasks - -### Creating a Grid - -```python -import mcrfpy - -# Create grid with default tile layer -grid = mcrfpy.Grid( - grid_size=(50, 50), # 50x50 cells - pos=(100, 100), # Screen position - size=(400, 400), # Viewport size in pixels - texture=my_texture # Texture for default TileLayer -) - -# Or create with specific layer configuration -grid = mcrfpy.Grid( - grid_size=(50, 50), - pos=(100, 100), - size=(400, 400), - layers={"terrain": "tile", "highlights": "color"} -) - -# Or start empty and add layers manually -grid = mcrfpy.Grid(grid_size=(50, 50), pos=(100, 100), size=(400, 400), layers={}) -terrain = grid.add_layer("tile", z_index=-1, texture=my_texture) - -# Add to scene -mcrfpy.sceneUI("game").append(grid) -``` - -### Setting Tile Properties - -```python -# Access layers for visual properties -tile_layer = grid.layers[0] # First layer (usually tiles) -tile_layer.set(x, y, 42) # Set sprite index at cell - -color_layer = grid.add_layer("color", z_index=-2) -color_layer.set(x, y, mcrfpy.Color(64, 64, 128)) # Blue tint - -# Access grid point for world properties -point = grid.at(x, y) -point.walkable = True # Can entities walk here? -point.transparent = True # Can see through for FOV? -``` - -### FOV and Pathfinding - -```python -# Compute field of view from position -grid.compute_fov(entity.grid_x, entity.grid_y, radius=10) - -# Check if cell is visible -if grid.is_in_fov(target_x, target_y): - print("Target is visible!") - -# A* pathfinding -path = grid.find_path(start_x, start_y, end_x, end_y) - -# Dijkstra maps for AI -grid.compute_dijkstra([(goal_x, goal_y)]) -distance = grid.get_dijkstra_distance(entity.grid_x, entity.grid_y) -path_to_goal = grid.get_dijkstra_path(entity.grid_x, entity.grid_y) -``` - -### Mouse Events - -Grids support mouse interaction at both the grid level and cell level: - -```python -# Grid-level events (screen coordinates) -def on_grid_click(x, y, button): - print(f"Grid clicked at pixel ({x}, {y}), button {button}") - -grid.on_click = on_grid_click -grid.on_enter = lambda: print("Mouse entered grid") -grid.on_exit = lambda: print("Mouse left grid") -grid.on_move = lambda x, y: print(f"Mouse at ({x}, {y})") - -# Cell-level events (grid coordinates) -def on_cell_click(grid_x, grid_y): - print(f"Cell ({grid_x}, {grid_y}) clicked!") - point = grid.at(grid_x, grid_y) - # Do something with the cell... - -grid.on_cell_click = on_cell_click -grid.on_cell_enter = lambda x, y: print(f"Entered cell ({x}, {y})") -grid.on_cell_exit = lambda x, y: print(f"Left cell ({x}, {y})") - -# Query currently hovered cell -if grid.hovered_cell: - hx, hy = grid.hovered_cell - print(f"Hovering over ({hx}, {hy})") -``` - -### Camera Control - -```python -# Center viewport on a position (pixel coordinates within grid space) -grid.center = (player.grid_x * 16 + 8, player.grid_y * 16 + 8) - -# Or set components individually -grid.center_x = player.grid_x * 16 + 8 -grid.center_y = player.grid_y * 16 + 8 - -# Zoom (1.0 = normal, 2.0 = 2x zoom in, 0.5 = zoom out) -grid.zoom = 1.5 -``` - -## Performance Characteristics - -**Implemented Optimizations:** -- **Chunk-based rendering** ([#123](../../issues/123)): Large grids divided into chunks, only visible chunks rendered -- **Dirty flag system** ([#148](../../issues/148)): Layers track changes, skip redraw when unchanged -- **RenderTexture caching**: Each chunk cached to texture, reused until dirty -- **Viewport culling**: Only cells within viewport are processed - -**Current Performance:** -- Grids of 1000x1000+ cells render efficiently -- Static scenes near-zero CPU (cached textures reused) -- Entity rendering: O(visible entities) - -**Profiling:** Use the F3 overlay or `mcrfpy.setTimer()` with `mcrfpy.getMetrics()` to measure grid performance. See [[Performance-and-Profiling]]. - -## Known Issues & Limitations - -**Current Limitations:** -- **FOV Python access limited:** `compute_fov()` works but fog-of-war overlay must be managed manually via color layers. Per-entity perspective (`UIGridPointState`) not yet exposed to Python. See [#113 discussion](../../issues/113). -- No tile-level animation yet (planned: [#124](../../issues/124)) -- Entity spatial queries are O(n) (planned: SpatialHash [#115](../../issues/115)) - -**Workarounds:** -- For fog of war: Use a `ColorLayer` with positive z_index, update cell colors based on `is_in_fov()` results -- For entity queries: Use list comprehension filtering on `grid.entities` - -## Related Systems - -- [[UI-Component-Hierarchy]] - Grid inherits from UIDrawable -- [[Animation-System]] - Grid properties are animatable (pos, zoom, center, etc.) -- [[Performance-and-Profiling]] - Grid rendering instrumented with metrics -- [[Entity-Management]] - Entities live within Grid containers - ---- - +# Grid System + +The Grid System is McRogueFace's core spatial container for roguelike game maps. It provides tilemap rendering, entity management, FOV (field of view), and pathfinding integration with libtcod. + +## Quick Reference + +**Related Issues:** +- [#113](../../issues/113) - Batch Operations for Grid (Open - Tier 1) +- [#124](../../issues/124) - Grid Point Animation (Open - Tier 1) +- [#150](../../issues/150) - User-driven Layer Rendering (Closed - Implemented) +- [#148](../../issues/148) - Dirty Flag RenderTexture Caching (Closed - Implemented) +- [#147](../../issues/147) - Dynamic Layer System (Closed - Implemented) +- [#123](../../issues/123) - Chunk-based Grid Rendering (Closed - Implemented) + +**Key Files:** +- `src/UIGrid.h` / `src/UIGrid.cpp` - Main grid implementation +- `src/GridLayers.h` / `src/GridLayers.cpp` - ColorLayer and TileLayer +- `src/UIGridPoint.h` - Individual grid cell (walkability, transparency) +- `src/UIGridPointState.h` - Per-entity perspective/knowledge +- `src/UIEntity.h` / `src/UIEntity.cpp` - Entity system (lives on grid) + +**API Reference:** +- See [mcrfpy.Grid](../../docs/api_reference_dynamic.html#Grid) in generated API docs +- See [mcrfpy.Entity](../../docs/api_reference_dynamic.html#Entity) in generated API docs + +## Architecture Overview + +### Three-Layer Design + +The Grid System uses a three-layer architecture for sophisticated roguelike features: + +1. **Visual Layer** (Rendering Layers) + - What's displayed: tile sprites, colors, overlays + - Implemented via `ColorLayer` and `TileLayer` objects + - Multiple layers per grid with z_index ordering + - Files: `src/GridLayers.h`, `src/GridLayers.cpp` + +2. **World State Layer** (`TCODMap`) + - Physical properties: walkable, transparent, cost + - Used for pathfinding and FOV calculations + - Integration: libtcod via `src/UIGrid.cpp` + +3. **Perspective Layer** (`UIGridPointState`) + - Per-entity knowledge: what each entity has seen/explored + - Enables fog of war, asymmetric information + - File: `src/UIGridPointState.h` + - **Note:** Python access currently limited - see Known Issues + +This architecture follows proven patterns from Caves of Qud, Cogmind, and DCSS. + +### Dynamic Layer System + +Grids support multiple rendering layers that can be added/removed at runtime: + +| Layer Type | Purpose | Methods | +|------------|---------|---------| +| `ColorLayer` | Solid colors per cell (fog, highlights) | `set(x, y, color)`, `at(x, y)`, `fill(color)` | +| `TileLayer` | Texture sprites per cell (terrain, items) | `set(x, y, sprite_index)`, `at(x, y)`, `fill(index)` | + +**z_index semantics:** +- Negative z_index: Renders **below** entities +- Zero or positive z_index: Renders **above** entities +- Lower z_index renders first (behind higher z_index) + +```python +# Create grid with no default layers +grid = mcrfpy.Grid(grid_size=(50, 50), pos=(100, 100), size=(400, 400), layers={}) + +# Add layers explicitly +background = grid.add_layer("color", z_index=-2) # Furthest back +tiles = grid.add_layer("tile", z_index=-1) # Above background, below entities +fog_overlay = grid.add_layer("color", z_index=1) # Above entities (fog of war) + +# Access existing layers +for layer in grid.layers: + print(f"{type(layer).__name__} at z={layer.z_index}") + +# Remove a layer +grid.remove_layer(fog_overlay) +``` + +### Grid → Entity Relationship + +**Entity Lifecycle:** +- Entities live on exactly 0 or 1 grids +- `grid.entities.append(entity)` sets `entity.grid = grid` +- `grid.entities.remove(entity)` sets `entity.grid = None` +- Entity removal handled automatically on grid destruction + +**Spatial Queries:** +- Currently: Linear iteration through entity list +- Planned: SpatialHash for O(1) lookups (see [#115](../../issues/115)) + +## Sub-Pages + +- [[Grid-Rendering-Pipeline]] - How grid renders each frame (chunks, dirty flags, caching) +- [[Grid-TCOD-Integration]] - FOV, pathfinding, walkability +- [[Grid-Entity-Lifecycle]] - Entity creation, movement, removal + +## Common Tasks + +### Creating a Grid + +```python +import mcrfpy + +# Create grid with default tile layer +grid = mcrfpy.Grid( + grid_size=(50, 50), # 50x50 cells + pos=(100, 100), # Screen position + size=(400, 400), # Viewport size in pixels + texture=my_texture # Texture for default TileLayer +) + +# Or create with specific layer configuration +grid = mcrfpy.Grid( + grid_size=(50, 50), + pos=(100, 100), + size=(400, 400), + layers={"terrain": "tile", "highlights": "color"} +) + +# Or start empty and add layers manually +grid = mcrfpy.Grid(grid_size=(50, 50), pos=(100, 100), size=(400, 400), layers={}) +terrain = grid.add_layer("tile", z_index=-1, texture=my_texture) + +# Add to scene +mcrfpy.sceneUI("game").append(grid) +``` + +### Setting Tile Properties + +```python +# Access layers for visual properties +tile_layer = grid.layers[0] # First layer (usually tiles) +tile_layer.set(x, y, 42) # Set sprite index at cell + +color_layer = grid.add_layer("color", z_index=-2) +color_layer.set(x, y, mcrfpy.Color(64, 64, 128)) # Blue tint + +# Access grid point for world properties +point = grid.at(x, y) +point.walkable = True # Can entities walk here? +point.transparent = True # Can see through for FOV? +``` + +### FOV and Pathfinding + +```python +# Compute field of view from position +grid.compute_fov(entity.grid_x, entity.grid_y, radius=10) + +# Check if cell is visible +if grid.is_in_fov(target_x, target_y): + print("Target is visible!") + +# A* pathfinding +path = grid.find_path(start_x, start_y, end_x, end_y) + +# Dijkstra maps for AI +grid.compute_dijkstra([(goal_x, goal_y)]) +distance = grid.get_dijkstra_distance(entity.grid_x, entity.grid_y) +path_to_goal = grid.get_dijkstra_path(entity.grid_x, entity.grid_y) +``` + +### Mouse Events + +Grids support mouse interaction at both the grid level and cell level: + +```python +# Grid-level events (screen coordinates) +def on_grid_click(x, y, button): + print(f"Grid clicked at pixel ({x}, {y}), button {button}") + +grid.on_click = on_grid_click +grid.on_enter = lambda: print("Mouse entered grid") +grid.on_exit = lambda: print("Mouse left grid") +grid.on_move = lambda x, y: print(f"Mouse at ({x}, {y})") + +# Cell-level events (grid coordinates) +def on_cell_click(grid_x, grid_y): + print(f"Cell ({grid_x}, {grid_y}) clicked!") + point = grid.at(grid_x, grid_y) + # Do something with the cell... + +grid.on_cell_click = on_cell_click +grid.on_cell_enter = lambda x, y: print(f"Entered cell ({x}, {y})") +grid.on_cell_exit = lambda x, y: print(f"Left cell ({x}, {y})") + +# Query currently hovered cell +if grid.hovered_cell: + hx, hy = grid.hovered_cell + print(f"Hovering over ({hx}, {hy})") +``` + +### Camera Control + +```python +# Center viewport on a position (pixel coordinates within grid space) +grid.center = (player.grid_x * 16 + 8, player.grid_y * 16 + 8) + +# Or set components individually +grid.center_x = player.grid_x * 16 + 8 +grid.center_y = player.grid_y * 16 + 8 + +# Zoom (1.0 = normal, 2.0 = 2x zoom in, 0.5 = zoom out) +grid.zoom = 1.5 +``` + +## Performance Characteristics + +**Implemented Optimizations:** +- **Chunk-based rendering** ([#123](../../issues/123)): Large grids divided into chunks, only visible chunks rendered +- **Dirty flag system** ([#148](../../issues/148)): Layers track changes, skip redraw when unchanged +- **RenderTexture caching**: Each chunk cached to texture, reused until dirty +- **Viewport culling**: Only cells within viewport are processed + +**Current Performance:** +- Grids of 1000x1000+ cells render efficiently +- Static scenes near-zero CPU (cached textures reused) +- Entity rendering: O(visible entities) + +**Profiling:** Use the F3 overlay or `mcrfpy.setTimer()` with `mcrfpy.getMetrics()` to measure grid performance. See [[Performance-and-Profiling]]. + +## Known Issues & Limitations + +**Current Limitations:** +- **FOV Python access limited:** `compute_fov()` works but fog-of-war overlay must be managed manually via color layers. Per-entity perspective (`UIGridPointState`) not yet exposed to Python. See [#113 discussion](../../issues/113). +- No tile-level animation yet (planned: [#124](../../issues/124)) +- Entity spatial queries are O(n) (planned: SpatialHash [#115](../../issues/115)) + +**Workarounds:** +- For fog of war: Use a `ColorLayer` with positive z_index, update cell colors based on `is_in_fov()` results +- For entity queries: Use list comprehension filtering on `grid.entities` + +## Related Systems + +- [[UI-Component-Hierarchy]] - Grid inherits from UIDrawable +- [[Animation-System]] - Grid properties are animatable (pos, zoom, center, etc.) +- [[Performance-and-Profiling]] - Grid rendering instrumented with metrics +- [[Entity-Management]] - Entities live within Grid containers + +--- + *Last updated: 2025-11-29* \ No newline at end of file