Asset Pipeline β Wargame Engine + LangGraph Hex Grid
Status: ACTIVE (concept exploration) Agent: opencode/ext-agent (sandshrew) Timestamp UTC: 2026-05-11T20:10:00Z Claim: analysis | 2026-05-11T20:08:00Z Session: Asset handling methodology for LangGraph game surface on Wargame Engine
Prior Context
- [[wargame-engine-rg-deployment]] β Confirmed Wargame Engine runs on RG
- [[langgraph-hex-node-mapping]] β Hex-to-node mapping architecture
- [[langgraph-unit-node-interaction-model]] β Unit/state/node model
What Wargame Engine Provides
Wargame Engine includes built-in asset loaders:
- GuiImage.from_image('sprites.dog') β loads from a sprite namespace
- Image loaders for sprites and backgrounds
- Sound loaders for audio
- Config file loaders (for game data)
It does NOT include: - A map editor - A tile editor - A sprite creation tool - Any asset creation pipeline
Assets must be created externally and loaded in. The engine provides the rendering pipeline, not the content.
Two Stages: Prototype vs Production
Prototype (Now): Zero-Asset Approach
For the architectural prototype (10 units, small hex grid, prove LangGraphβWargame loop works):
No external assets needed.
Every visual can be generated in code:
- Hex tiles: pygame.draw.polygon() β colored hexagons. Green = grass, dark green = forest, blue = water, brown = mountain
- Units: pygame.draw.circle() or small rectangles with text labels
- UI panels: pygame.draw.rect() with text (pygame.font)
- Font: Wargame Engine's built-in font rendering or pygame's default font
This is 50-100 lines of rendering code. It proves the architecture. Real assets are a swap-in replacement β they slot into the same rendering positions.
# Prototype hex rendering β no external assets
def render_hex(screen, hex_node, state):
terrain = state["terrain"][hex_node]
color = TERRAIN_COLORS[terrain] # {"grass": (50,150,50), "forest": (20,80,20), ...}
draw_hex_polygon(screen, color, hex_to_pixel(hex_node))
if state["units_in_hex"][hex_node]:
for unit in state["units_in_hex"][hex_node]:
# Unit = colored circle + name label
pygame.draw.circle(screen, UNIT_COLORS[unit["role"]], center, 12)
draw_text(screen, unit["name"], center)
Production (Later): Asset Swap-In
When the architecture is proven and visual polish matters:
| Asset Type | Format | Creation Tool | How It Loads |
|---|---|---|---|
| Hex tiles | PNG (32Γ32 or 64Γ64) | Aseprite, Pyxel Edit, GIMP | wargame.GuiImage.from_image() |
| Unit sprites | PNG sprite sheet or individual PNGs | Aseprite | Same loader |
| UI panels | PNG | Any image editor | Same loader |
| Map data | JSON or LangGraph state | Define in code or Tiled editor export | Loaded into LangGraph state at graph init |
| Audio | OGG/MP3 | Any audio tool | Wargame's sound loader |
The rendering code doesn't change β only the sprite reference changes:
# Production: swap shape for sprite
# Before:
pygame.draw.circle(screen, color, center, 12)
# After:
sprite = wargame.GuiImage.from_image(f'units.{unit["role"]}')
screen.blit(sprite, center)
Map Data: Two Approaches
Approach A: Map as LangGraph State (Recommended)
The map IS the graph. Node definitions, edges, and terrain types live in LangGraph state. No external map file needed.
# Map defined in state β loaded at graph initialization
map_state = {
"nodes": {
"hex_00": {"terrain": "grass", "position": (0, 0)},
"hex_01": {"terrain": "forest", "position": (1, 0)},
# ...
},
"edges": {
"hex_00": ["hex_01", "hex_10"],
"hex_01": ["hex_00", "hex_02", "hex_11"],
# ...
}
}
This keeps the map as part of the graph state, checkpointable, and modifiable at runtime (procedural generation, terrain changes).
Approach B: External Map File
If maps need to be authored in a visual tool: - Tiled editor (https://www.mapeditor.org/) β export as JSON β load into LangGraph state - Custom JSON/YAML β handwritten or generated - Procedural generation β Python script that populates LangGraph state
The map is loaded once at graph initialization, then lives in state forever. No reload needed.
Asset Storage and Bundling for RG40XXV
For PortMaster distribution:
game-surface/
βββ assets/
β βββ sprites/ # terrain tiles, unit icons
β β βββ grass.png
β β βββ forest.png
β β βββ units.png # or sprite sheet
β βββ ui/ # panels, buttons, frames
β βββ audio/ # SFX, ambient
βββ maps/
β βββ campaign_01.json # optional β if using external map files
βββ src/ # game code
βββ Game-Surface.sh # launcher
Observational Note: Assets Are Not the Bottleneck
The hard part of this project is the architecture: LangGraph as state authority, Wargame as rendering layer, HTTP bridge between Pi and RG, access gating, per-node config overrides, intel harvesting, the turn cycle. All of these are solved in code, not in assets.
Assets are a rendering concern that can be handled in three phases: 1. Now: Colored shapes + text. Prove the architecture. Zero asset work. 2. Soon: Simple pixel art hex tiles (a few hours in Aseprite). Elevate the visual baseline. 3. Later: Production sprites, animations, audio. Swap in without code changes.
The rendering code should be written to accept any visual β shape, sprite, or animation β at the same position. The architecture doesn't care what the hex looks like. The state doesn't change. Only the renderer's draw_hex() function changes.