feat: add dungeon map streaming and rendering in radar

- Backend: dungeon_map event handler with permanent in-memory cache
  by landblock ID, request_dungeon_map for late-joining browsers
- Frontend: render dungeon cells as colored rectangles when in dungeon,
  multi-level Z support (current floor bright, others dimmed),
  automatic overworld/dungeon switching based on is_dungeon flag,
  raw physics coordinate positioning for dungeon objects

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-04-08 13:16:36 +02:00
parent 2f21159acb
commit b941a29f04
2 changed files with 114 additions and 39 deletions

18
main.py
View file

@ -982,6 +982,7 @@ live_vitals: Dict[str, dict] = {}
live_character_stats: Dict[str, dict] = {}
live_equipment_cantrip_states: Dict[str, dict] = {}
live_nearby_objects: Dict[str, dict] = {}
dungeon_map_cache: Dict[str, dict] = {} # landblock hex string -> dungeon map data
# Shared secret used to authenticate plugin WebSocket connections (override for production)
SHARED_SECRET = "your_shared_secret"
@ -2685,6 +2686,14 @@ async def ws_receive_snapshots(
live_nearby_objects[character_name] = data
await _broadcast_to_browser_clients(data)
continue
if msg_type == "dungeon_map":
landblock = data.get("landblock")
if landblock:
dungeon_map_cache[landblock] = data
logger.info(f"Cached dungeon map for {landblock} ({len(data.get('z_levels', []))} z-levels)")
await _broadcast_to_browser_clients(data)
continue
# Unknown message types are ignored
if msg_type:
logger.warning(
@ -2800,6 +2809,15 @@ async def ws_live_updates(websocket: WebSocket):
except WebSocketDisconnect:
logger.info(f"Browser WebSocket disconnected: {websocket.client}")
break
# Handle dungeon map requests from browser
if data.get("type") == "request_dungeon_map":
landblock = data.get("landblock")
cached = dungeon_map_cache.get(landblock)
if cached:
await websocket.send_json(cached)
logger.debug(f"Sent cached dungeon map {landblock} to browser")
continue
# Determine command envelope format (new or legacy)
if "player_name" in data and "command" in data:
# New format: { player_name, command }