diff --git a/AGENTS.md b/AGENTS.md index ecbaed1..e433606 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -27,10 +27,12 @@ Rust edition 2024, requires rustc >= 1.96. Vulkan 1.2+ required for `ascii`/`gra - **AI observation** uses multi-spectrum ASCII layers via pipe protocol: - `materials` — material type per cell - `temperature` — heat levels encoded as characters - - `light` — light intensity per cell + - `light` — light intensity per cell (world-space fallback) - `entities` — entity positions and types only - `density` — material density visualization - `velocity` — entity movement speed and CA activity + - `gas` — gas type + density per cell (NEW) + - `pressure` — pressure levels per cell (NEW) ## Tape System @@ -44,7 +46,7 @@ Each tape frame contains: - Tick number, depth, kills, score - Camera position - Player HP, position, entity count -- All 6 spectrum layers as ASCII text +- All 8 spectrum layers as ASCII text Pipe protocol spectrum commands: - `{"cmd":"get_spectrum","spectrum":"materials","w":80,"h":25}` — single spectrum @@ -107,7 +109,7 @@ SPV files are committed. `include_bytes!` embeds them at compile time. **Source of truth**: `ChunkedGrid` of `Cell` structs with parallel per-chunk layer arrays. Replaces the old fixed-size `Grid` with dual storage: - **Bounded mode**: `Vec` for deterministic 250x250 test/AI grids. -- **Infinite mode**: `HashMap<(i64, i64), Chunk>` for continuous 12500x12500 cell (100000x100000 px) Noita-scale worlds. +- **Infinite mode**: `HashMap<(i64, i64), Chunk>` for continuous Noita-scale worlds. Main game (`--mode terminal`, `--mode ascii`, `--mode graphics`) uses the infinite mode. Each `Cell` stores `material`, `fg`/`bg` color, `variant` inline (9 bytes, no temp). Temperature, gas, pressure, and light are stored in parallel arrays per chunk. Chunks are 64x64 cells with `active`, `modified`, `was_modified`, `generated`, and `dirty` flags. @@ -120,10 +122,12 @@ Main game (`--mode terminal`, `--mode ascii`, `--mode graphics`) uses the infini Layer access via `grid.get_temp()`/`set_temp()`, `grid.get_gas()`/`set_gas()`, `grid.get_pressure()`/`set_pressure()`, `grid.get_light()`/`set_light()`. All `set_*` methods call `mark_dirty()` (shared dirty rect). `cells_swap` swaps all layers. `set_material` also sets `default_temp()`. -**Simulation steps** per `fixed_update()`: `apply_gas_damage()` → `ca.step()` (material CA + heat_transfer + gas_step + pressure_step + light_step) → entity updates → combat → status effects. The `ca.step()` saves pre-clear dirty rects and passes them to layer steps so heat/gas/pressure can diffuse beyond CA-active cells. +**Simulation steps** per `fixed_update()`: `apply_gas_damage()` → `ca.step()` (material CA + heat_transfer + gas_step + pressure_step + light_step) → entity updates → combat → status effects. The `ca.step()` saves pre-clear dirty rects and passes them to layer steps so heat/gas/pressure can diffuse beyond CA-active cells. All loaded chunks are activated every tick — uniform simulation rate across the world. **Serialization**: Multi-section chunk format with `VWM1` magic header. Sections: cells (8 bytes × 4096), temps (4 × 4096), gas (2 × 4096), pressure (1 × 4096), light (3 × 4096). Old 12-byte format auto-detected and loaded for backward compat. +**Audio** (`src/audio/mod.rs`): `AudioEngine` using `rodio` for WAV playback. 15 procedurally generated sounds (jump, shoot, hit, explosion, death, pickup, descend, powerup, step, lava_bubble, acid_sizzle, water_splash, fire_crackle, ui_click, goblin_growl) embedded via `include_bytes!`. Each sound creates its own `Sink` + `detach()` for overlapping playback. Ambient sounds scan 15-cell radius around player. `M` key toggles audio. Gracefully degrades when no audio device available. + **Four entity kinds**: `Player`, `Goblin`, `Slime`, `Corpse`. Three physics types: cellular (CA materials in grid), rigid (alive entities, AABB + slope stepping), ragdoll (corpses, Verlet constraints). **Three render modes**: `terminal` (crossterm ANSI), `ascii` (Vulkan glyph atlas + instanced), `graphics` (Vulkan colored quads). All three read the same `ChunkedGrid` + `EntityManager` and an optional `LightGrid` overlay. GPU renderers upload a viewport-relative grid buffer and index it in shaders with `cam_pos`. @@ -149,11 +153,11 @@ Layer access via `grid.get_temp()`/`set_temp()`, `grid.get_gas()`/`set_gas()`, ` **Items & inventory**: `Item` (weapon, armor, consumable), `ItemManager`, and inventory/equipment slots on `Player`. Items spawn in the world and are picked up on contact. `Game::use_item(0)` equips weapons/armor or consumes potions; `Game::drop_item(0)` returns an item to the world. Equipped weapon adds damage bonus to projectiles; equipped armor reduces enemy contact damage. -**UI layer**: `ui::UiLayer` overlays non-destructive UI on all renderers. Health bars above entities, bottom-line HUD, scrolling message log, floating damage numbers, screen-edge indicators, death screen, entity labels, status icons, minimap, and a character panel. UI is drawn unlit on top of the world. In GPU (`ascii`/`graphics`) and capture modes the UI is rendered as a separate 2x2 pixel-per-cell pass; terminal mode renders UI at full character size. +**UI layer**: `ui::UiLayer` overlays non-destructive UI on all renderers. Uses a flat `Vec>` array (not HashMap) for O(1) cell access. `font_scale` auto-computed from screen height — text, panels, health bars, and minimap scale proportionally. Health bars above entities, bottom-line HUD, scrolling message log, floating damage numbers, screen-edge indicators, death screen, entity labels, status icons, minimap (sampled with step_by(4)), and a character panel. UI is drawn unlit on top of the world. In GPU (`ascii`/`graphics`) and capture modes the UI is rendered as a separate 2x2 pixel-per-cell pass; terminal mode renders UI at full character size. -**Chunk system**: `ChunkedGrid` is divided into 64x64 `Chunk`s. Each chunk tracks `active`, `modified`, `was_modified`, `generated`, and `dirty` (an optional bounding rect of cells that need processing). `save_chunk(path, cx, cy)` and `load_chunk(path, cx, cy)` serialize chunk cells via 12-byte binary format. Cell serialization is handled by `Cell::to_bytes()` / `Cell::from_bytes()`. `Chunk::generated` is set when a chunk is generated or loaded from cache, preventing accidental regeneration. +**Chunk system**: `ChunkedGrid` is divided into 64x64 `Chunk`s. Each chunk tracks `active`, `modified`, `was_modified`, `generated`, and `dirty` (an optional bounding rect of cells that need processing). `save_chunk(path, cx, cy)` and `load_chunk(path, cx, cy)` serialize chunk data via multi-section `VWM1` binary format. Cell serialization is handled by `Cell::to_bytes()` / `Cell::from_bytes()` (8 bytes/cell). `Chunk::generated` is set when a chunk is generated or loaded from cache, preventing accidental regeneration. -**Dirty rect optimization** (Noita-style): Each chunk maintains a `dirty: Option<(i32, i32, i32, i32)>` bounding rect of cells that need CA processing. When a cell changes via `set`, `set_material`, `cells_swap`, or `set_cell_index`, the chunk's dirty rect is expanded to include that cell ±1 (for neighbor influence). The CA step only iterates cells within dirty rects, skipping chunks with no dirty rect entirely. Liquids and gases (water, lava, acid, steam, fire, smoke) re-mark themselves dirty after processing so they continue to flow and react. Sand and other solids can sleep when at rest. `heat_transfer` only processes cells within dirty rects and reuses its temperature buffer across frames. `update_active_chunks` activates chunks with dirty rects in addition to chunks near entities. This reduces CA step time from ~500μs to ~25μs on a 250x250 grid. +**Dirty rect optimization** (Noita-style): Each chunk maintains a `dirty: Option<(i32, i32, i32, i32)>` bounding rect of cells that need CA processing. When a cell changes via `set`, `set_material`, `cells_swap`, or `set_cell_index`, the chunk's dirty rect is expanded to include that cell ±1 (for neighbor influence). The CA step only iterates cells within dirty rects, skipping chunks with no dirty rect entirely. Liquids and gases (water, lava, acid, steam, fire, smoke) re-mark themselves dirty after processing so they continue to flow and react. Sand and other solids can sleep when at rest. All dirty rects are processed fully — no size limits — ensuring uniform simulation across chunks. `update_active_chunks` activates all loaded chunks every tick for consistent physics. **World cache**: main game seeds are saved in `Game::seed` and written to `cache/worlds//`. Each cached world stores per-chunk binary files plus a `meta.json` with player spawn and item placement. `Game::init_world()` loads the cache if it exists; otherwise it generates the spawn region and saves it. This makes Noita-scale worlds load instantly after the first visit. @@ -162,7 +166,7 @@ Layer access via `grid.get_temp()`/`set_temp()`, `grid.get_gas()`/`set_gas()`, ` - `2 <= cy < 6` — caves (stone, CA-carved empty space, lava/water/acid pools) - `cy >= 6` — dungeon (BSP rooms, corridors, stone walls) -**Chunk streaming**: `Game::stream_chunks()` is called every `fixed_update`. It loads cached chunks (or generates new ones) in a 3-chunk radius around the player, saves modified chunks beyond that radius, and unloads distant chunks. Streaming is active for infinite grids and for bounded grids larger than 2048×2048; test/AI 250×250 grids skip streaming. +**Chunk streaming**: `Game::stream_chunks()` is called every `fixed_update`. It loads cached chunks (or generates new ones) in a 2-chunk radius around the player, saves modified chunks beyond that radius every 10 ticks (1 chunk at a time), and unloads distant chunks every 120 ticks. All loaded chunks are activated for uniform simulation. Streaming is active for infinite grids; test/AI 250×250 grids skip streaming. **Vertical descent**: `MaterialId::Stairs` is a solid feature material. Player stands on stairs and presses `>` to descend. `Game::descend()` increments depth, resets the world, and respawns the player at the top. HUD shows current depth. @@ -181,6 +185,9 @@ Layer access via `grid.get_temp()`/`set_temp()`, `grid.get_gas()`/`set_gas()`, ` - **Random seeding** — `Game::new()` uses a fixed seed for tests/AI sessions; `Game::new_random()` seeds from system time and is used by terminal/ascii/graphics modes. Cached worlds are keyed by seed. - **Dirty rects** — `ChunkedGrid::mark_dirty(x, y)` expands the chunk's dirty rect to include (x,y) ±1 and propagates to neighbor chunks at boundaries. `cells_swap` and `set_cell_index` call `mark_dirty` automatically. `set` and `set_material` also call `mark_dirty`. The CA step clears each chunk's dirty rect at the start of processing and rebuilds it from cell modifications during processing. - **World scale**: `WORLD_SCALE = 5` in `worldgen.rs` — all world features (trees, pools, walls, dunes, rooms, corridors, terrain amplitude) are multiplied by this factor. Change this constant to adjust entity-to-world size ratio. +- **Surface terrain**: Multi-octave sine noise with low frequencies (base 0.012, detail 0.04, micro 0.11) scaled by `WORLD_SCALE`. Produces wide rolling hills (~260 cell wavelength) instead of narrow peaks. +- **Uniform tick rate**: All loaded chunks simulate every tick — no chunk is frozen or partially updated. `cells_swap` uses `split_at_mut` for direct chunk array access in bounded mode. +- **Fire propagation**: Newly ignited cells get `updated_this_tick = true` to prevent chain reactions within a single tick. Fire spreads 1 cell/tick (linear, not exponential). ## Module Layout @@ -190,12 +197,13 @@ src/ lib.rs # pub mod declarations game.rs # Game struct, world gen, fixed_update, collision, combat, slime AI input.rs # Terminal input (crossterm, InputHandler) — terminal mode only + audio/mod.rs # AudioEngine (rodio), 15 embedded WAV sounds, ambient scanning world/ # Cell, MaterialId, MaterialRegistry, ChunkedGrid, Grid (legacy), Chunk, CellularAutomaton, WorldGenerator, WorldCache - physics/ # VerletSolver, SubBody (with color field), Constraint, resolve_grid_collision + physics/ # VerletSolver, SubBody (with color field), Constraint, resolve_grid_collision, ProjectileManager entity/ # Entity (rigid/ragdoll), EntityManager, Player, BodyTemplate, Item, ItemManager - render/ # terminal.rs, vulkan.rs (ASCII), graphics.rs (cells), lighting.rs, window_input.rs - ai/ # GameSession, AiAction, pipe protocol, replay, scenarios - ui/ # UiLayer, HUD, messages, damage numbers, edge indicators + render/ # terminal.rs, vulkan.rs (ASCII), graphics.rs (cells), lighting.rs, window_input.rs, capture.rs + ai/ # GameSession, AiAction, pipe protocol, replay, scenarios, spectrum + ui/ # UiLayer (flat Vec), HUD, messages, damage numbers, edge indicators, scalable font ``` ## PLAN.md diff --git a/PLAN.md b/PLAN.md index d5f108f..987e9a2 100644 --- a/PLAN.md +++ b/PLAN.md @@ -10,50 +10,60 @@ | System | Status | Details | |--------|--------|---------| | Cellular automaton | Working | 14 materials: sand, water, stone, lava, wood, flesh, bone, steam, fire, acid, smoke, grass, dirt, empty | -| Rigid entities | Working | AABB collider, slope stepping, 27 sub-bodies (5x5 + arm), player + goblins | +| Multi-layer world | Working | Temperature, gas (smoke/poison/CO2/steam), pressure, world-space light — parallel per-chunk arrays | +| Rigid entities | Working | AABB collider, slope stepping, 70 sub-bodies, player + goblins + slimes | | Ragdoll corpses | Working | Verlet constraints, death = rigid→ragdoll transition with inherited velocity | | Terminal renderer | Working | Full terminal size, ANSI truecolor, diff-based rendering | -| ASCII renderer (Vulkan) | Working | ash + winit, instanced rendering, glyph atlas, 16x16 square cells, adaptive viewport | +| ASCII renderer (Vulkan) | Working | ash + winit, instanced rendering, glyph atlas, 8x8 cells, adaptive viewport | | Graphics renderer (Vulkan) | Working | ash + winit, colored cells (no glyphs), each material = unique base color, adaptive viewport | -| AI pipe protocol | Working | JSON stdin/stdout, 16 commands, full state export | -| Test framework | Working | 109 Rust tests + 14 JSON scenarios, all passing | +| AI pipe protocol | Working | JSON stdin/stdout, 16 commands, full state export, 8 spectrums (materials/temp/light/entities/density/velocity/gas/pressure) | +| Audio | Working | 15 procedurally generated sounds via rodio, overlapping playback, ambient scanning, M to toggle | +| UI layer | Working | Procedurally scalable font, flat Vec array, health bars, HUD, minimap, inventory overlay, character panel | +| Test framework | Working | 185 Rust tests + 14 JSON scenarios, all passing | | Replay system | Working | Seeded determinism, record/playback, play_until_tick | -| World generation | Working | Procedural chunk-based biomes: surface (noise), caves (CA), dungeons (BSP); vertical biome progression by chunk Y; 12500×12500 continuous world; chunk streaming + cache | +| World generation | Working | ×5 scale: trees 30 cells, pools r20, BSP rooms 20×20, corridors 5 wide; chunk streaming + cache | | Cross-platform | Working | Windows/Linux/macOS via winit + ash_window, no platform-specific code | -| Adaptive viewport | Working | Window resize → more/fewer cells visible, cells stay 16x16 pixels | -| Per-cell color (reality layer) | Working | Each cell stores fg/bg color inline, no registry lookup in render path | +| Adaptive viewport | Working | Window resize → more/fewer cells visible, cells stay 8x8 pixels | +| Per-cell color | Working | Each cell stores fg/bg color inline, no registry lookup in render path | +| Performance | Working | 130-150 FPS (graphics), uniform tick rate across all chunks | ### Architecture ``` -Source of truth: `ChunkedGrid` of `Cell` structs +Source of truth: `ChunkedGrid` of `Cell` structs with parallel layer arrays - Bounded mode: `Vec` for 250x250 test/AI grids -- Infinite mode: `HashMap<(i64, i64), Chunk>` for continuous 12500x12500 cell worlds -- 64x64 chunks, dirty rects, active flags, per-chunk persistence -- Chunk streaming: load/generate around player, save/unload distant chunks +- Infinite mode: `HashMap<(i64,i64), Chunk>` for continuous worlds +- 64x64 chunks, dirty rects, all loaded chunks active every tick +- Multi-layer: temps (f32), pressure (u8), gas_type+density (u8×2), light ([u8;3]) +- Cell: 9 bytes (material + variant + fg/bg, no temp) +- Serialization: VWM1 multi-section format, backward compat with old 12-byte + +World scale: WORLD_SCALE=5, adjustable via single constant +Surface: multi-octave sine noise, wide rolling hills (~260 cell wavelength) Three entity types: 1. Cellular — materials in grid, per-cell CA rules 2. Rigid — alive entities, AABB collider, slope stepping, single velocity 3. Ragdoll — corpses, loose Verlet bodies, independent physics -Game loop: fixed 60Hz timestep - Physics tick: CA step → rigid update (slope step) → ragdoll update → damage - Render: terminal (ANSI) / ascii (Vulkan glyphs) / graphics (Vulkan cells) / pipe (JSON) / headless (file) +Game loop: fixed 60Hz timestep, uniform across all chunks + Physics tick: CA step → heat_transfer → gas_step → pressure_step → light_step + → entity updates → combat → status effects → gas damage + Render: terminal (ANSI) / ascii (Vulkan glyphs) / graphics (Vulkan cells) / pipe (JSON) -Three render modes: - --mode terminal → pure ANSI ASCII in terminal - --mode ascii → Vulkan window with ASCII characters (glyph atlas) - --mode graphics → Vulkan window with colored cells (no glyphs, material base colors) +Audio: 15 embedded WAV sounds via rodio, overlapping playback, ambient scanning +UI: flat Vec array, procedurally scalable font, non-destructive overlay ``` ### Numbers -- ~6700 lines Rust -- 171 integration tests, 14 JSON scenarios -- 40+ git commits +- ~9000 lines Rust +- 185 integration tests, 14 JSON scenarios +- 50+ git commits - 0 compiler warnings (excluding winit deprecation notices) - Cross-platform: Windows/Linux/macOS +- 15 embedded sound effects +- 130-150 FPS (graphics mode, release, ×5 world scale) --- @@ -71,17 +81,17 @@ loop is: aim → shoot → projectile travels → hits enemy → damage/kill. - [x] Knockback: damage applies velocity impulse to rigid body center - [x] Goblin AI: move toward player, attack when adjacent - [x] Slime AI: jump toward player, contact damage -- [ ] **Projectile system**: lightweight rigid bodies (arrows, fireballs, magic bolts) +- [x] **Projectile system**: lightweight rigid bodies (arrows, fireballs, magic bolts) - Player shoots with directional input (mouse aim or movement-direction) - Projectile = small AABB, velocity, damage, lifetime - On hit with entity: deal damage, destroy projectile - On hit with solid cell: stop/destroy - Fireball: ignites materials it touches (Noita-style material interaction) - Magic bolt: pure damage, no material effect -- [ ] Health bars in render (colored indicator above entity) -- [ ] Death → ragdoll → corpse decomposition (flesh cells drop into grid over time) -- [ ] Material interaction with entities: entity walks through fire → ignites, acid → dissolves -- [ ] Goblin AI: flee when low HP +- [x] Health bars in render (colored indicator above entity) +- [x] Death → ragdoll → corpse decomposition (flesh cells drop into grid over time) +- [x] Material interaction with entities: entity walks through fire → ignites, acid → dissolves +- [x] Goblin AI: flee when low HP **Tests needed:** - Projectile travels and deals damage on hit @@ -106,34 +116,34 @@ Render pipeline per frame: 3. Present to screen ``` -- [ ] `UiLayer` struct: sparse map of (screen_x, screen_y) → (char, fg_color, bg_color) - - UI elements write to this map, not to the grid +- [x] `UiLayer` struct: flat `Vec>` array for O(1) cell access + - UI elements write to this array, not to the grid - Renderer composites: if UiLayer has a cell at (x,y), it overrides the world cell visually - World state is never modified by UI -- [ ] Health bar: colored bar above player entity, shows current/max HP +- [x] Health bar: colored bar above player entity, shows current/max HP - ████░░░░ style, colored green→yellow→red by HP ratio - Positioned relative to player's screen position, scrolls with camera -- [ ] Entity labels: small text above/below entities (name, level for RPG) -- [ ] Status effect icons: burning 🔥, poisoned, frozen — shown next to entity -- [ ] HUD bar (bottom of screen, non-destructive): +- [x] Entity labels: small text above/below entities (name, level for RPG) +- [x] Status effect icons: burning, poisoned, frozen — shown next to entity +- [x] HUD bar (bottom of screen, non-destructive): - HP: ████████░░ 80/100 - Material brush: [Sand] (current selected) - Tick: 1234 Depth: 1 - FPS counter (debug mode) - [ ] Tooltip on hover: when cursor is over a cell, show material name + temperature -- [ ] Message log (top of screen, scrolling): "Goblin hits you for 10 damage" +- [x] Message log (top of screen, scrolling): "Goblin hits you for 10 damage" - Last N messages, older ones fade (dimmer color) -- [ ] Inventory overlay (toggle with 'i'): semi-transparent panel, doesn't modify world +- [x] Inventory overlay (toggle with 'i'/Tab): semi-transparent panel, doesn't modify world - List of items, selected highlight, weight/value display - Opens/closes without affecting simulation - [ ] Menu system (pause, settings, save/load): full-screen overlay with border - Game loop pauses (or continues in background), UI captures input -- [ ] Minimap (corner of screen): compressed world view, explored areas only +- [x] Minimap (corner of screen): compressed world view, explored areas only - Each minimap cell = 5x5 world cells, averaged material color - Player position marker, entity dots - [ ] Crosshair/targeting: when aiming projectiles, shows trajectory preview -- [ ] Damage numbers: floating text above entities when hit, rises and fades -- [ ] Screen-edge indicators: arrows pointing to off-screen entities of interest +- [x] Damage numbers: floating text above entities when hit, rises and fades +- [x] Screen-edge indicators: arrows pointing to off-screen entities of interest **Key principle: UI layer NEVER writes to grid, entities, or any game state. It reads state and renders visuals on top. This keeps the source of truth clean @@ -180,18 +190,18 @@ instanced quads with UI texture coordinates. Transparent background, drawn on to **Goal: character progression, inventory, abilities** -- [ ] Stats: strength, agility, toughness, willpower — affect damage, speed, HP, etc. -- [ ] Inventory system: items as data structs, pick up by walking over, drop with key -- [ ] Equipment: weapon affects melee damage/range, armor affects damage reduction -- [ ] Items in world: weapons, potions, scrolls, food — rendered as distinct ASCII chars +- [x] Stats: strength, agility, toughness, willpower — affect damage, speed, HP, etc. +- [x] Inventory system: items as data structs, pick up by walking over, drop with key +- [x] Equipment: weapon affects melee damage/range, armor affects damage reduction +- [x] Items in world: weapons, potions, scrolls, food — rendered as distinct ASCII chars - [ ] Mutations (Caves of Qud style): modify entity properties - "Silicon skin" → entity material becomes Stone, immune to acid - "Flame body" → entity emits fire cells, immune to fire - "Liquid form" → entity can squeeze through 1-cell gaps - "Multiple arms" → extra attack, can hold more items -- [ ] XP and leveling: kill entities → gain XP → level up → choose mutation +- [x] XP and leveling: kill entities → gain XP → level up → choose mutation - [ ] Skills: active abilities on cooldown (dash, stomp, material blast) -- [ ] Status effects: burning, poisoned, frozen, bleeding — each with tick effect +- [x] Status effects: burning, poisoned, frozen, bleeding — each with tick effect - [ ] Dialogue: talk to NPCs, simple text tree **Tests needed:** @@ -277,17 +287,17 @@ Two distinct render modes, both GPU-accelerated via Vulkan: - [ ] Boss entity: large rigid body (10x10), multiple attack patterns - [ ] Books/readable items: lore text displayed in terminal - [ ] Crafting: combine materials to create new ones (water + dirt = mud) -- [ ] Sound: procedural audio via terminal bell or optional ALSA +- [x] Sound: procedural audio via rodio, 15 embedded WAV sounds, overlapping playback - [ ] Save/load: full game state to file (grid + entities + player + inventory) - [ ] Death screen: stats summary, cause of death - [ ] Tutorial: first-time controls overlay - [ ] Difficulty scaling: deeper levels = stronger enemies -### Phase 6: Advanced Physics +### Phase 6: Advanced Physics (DONE) **Goal: deeper Noita-style material simulation** -- [ ] Multi-layer world: separate grid layers for material, temperature, pressure, gas/air, light +- [x] Multi-layer world: separate grid layers for material, temperature, pressure, gas/air, light - Air layer: gas flow, ventilation in caves, gas accumulates at ceiling, displaced by fire - Pressure layer: liquids have pressure, flow through pipes and U-bends - Temperature layer: proper heat diffusion, materials melt/freeze at thresholds @@ -547,10 +557,12 @@ assets/ |-----------|---------|--------| | 0.1 (done) | Core engine: CA, rigid, ragdoll, terminal, AI pipe | June 2026 | | 0.2 (done) | Vulkan ASCII + graphics renderers, adaptive viewport, per-cell color, slope stepping | June 2026 | -| 0.3 | Combat, goblin AI, projectiles, corpse decomposition | July 2026 | -| 0.35 | UI layer: health bar, HUD, message log, minimap, inventory overlay | July 2026 | -| 0.4 | Chunks, biomes, dungeon gen, camera zoom | August 2026 | -| 0.5 | RPG layer: stats, inventory, mutations, XP | October 2026 | +| 0.3 (done) | Combat, goblin AI, projectiles, corpse decomposition | June 2026 | +| 0.35 (done) | UI layer: health bar, HUD, message log, minimap, inventory overlay, scalable font | June 2026 | +| 0.4 (done) | Chunks, biomes, dungeon gen, world scale ×5, chunk streaming + cache | June 2026 | +| 0.45 (done) | RPG layer: stats, inventory, XP, status effects, items | June 2026 | +| 0.5 (done) | Multi-layer world: temp, gas, pressure, light + audio system | June 2026 | +| 0.55 (done) | Performance: uniform tick rate, 130-150 FPS, fire chain fix | June 2026 | | 0.6 | Lighting/particles/textures (Phase 4b) | December 2026 | | 0.7 | Multi-layer world: air, pressure, temperature, light as separate grids | Feb 2027 | | 0.8 | AI agent: LLM + RL bridge, agent recording | April 2027 |