From d6ff88c35973f91ea66d357ac0e234bd995494d0 Mon Sep 17 00:00:00 2001 From: Cameron Date: Thu, 9 Jul 2026 23:23:15 -0700 Subject: [PATCH] Implement the feel floor: rails, pads, and the build beam. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Defense: feel-floor.md criteria 1-6 — tick-one drops blueprint room seed; Sim::feel_rail_segments / growable_bays and FactSource::Feel drive inspect and both frontends; Bevy draws shimmer rails/pads and a build- beam birth; work blips still ride the same edges. Co-authored-by: Cursor --- src/actions.rs | 26 ++- src/bin/bevy.rs | 194 ++++++++++++----- src/bin/terminal/agent.rs | 46 ++++ src/bin/terminal/ui.rs | 74 ++++++- src/reach.rs | 10 + src/sim.rs | 203 +++++++++++++----- wiki/engineering/current-build.md | 2 +- wiki/interface/feel-floor.md | 13 +- wiki/log/2026-07-09-feel-floor-implemented.md | 42 ++++ wiki/log/DEVLOG.md | 8 + wiki/log/shots/2026-07-09-feel-floor-dark.png | Bin 0 -> 2616889 bytes wiki/process/ROADMAP.md | 7 +- wiki/process/specs.md | 2 +- 13 files changed, 509 insertions(+), 118 deletions(-) create mode 100644 wiki/log/2026-07-09-feel-floor-implemented.md create mode 100644 wiki/log/shots/2026-07-09-feel-floor-dark.png diff --git a/src/actions.rs b/src/actions.rs index d212b36e..3b5df87e 100644 --- a/src/actions.rs +++ b/src/actions.rs @@ -697,10 +697,10 @@ impl Sim { // ── Anchors ──────────────────────────────────────────────────────────── - /// Tile anchor: fog gates everything (cursor.md provenance). An - /// Unknown tile exposes nothing; tile-identity verbs need Seen / - /// Remembered / Blueprint; machines the player runs answer through - /// telemetry regardless of fog (proprioception, not sight). + /// Tile anchor: fog gates room-shape verbs (cursor.md provenance). An + /// Unknown tile exposes no room identity; growable bays and known + /// feel-joined devices still answer through feel (feel-floor.md); + /// machines the player runs answer through telemetry regardless of fog. fn tile_actions(&self, x: i32, y: i32) -> Vec { let mut out = Vec::new(); let fog = self.fog_at(x, y); @@ -723,7 +723,11 @@ impl Sim { } } - if fog == Fog::Unknown && machine.is_none() { + // Feel: empty growable bays are placement affordances without + // room sight; known devices on the feel graph keep their verbs. + let growable = self.is_growable_bay(x, y); + let known_device = self.reach.known_at(x, y); + if fog == Fog::Unknown && machine.is_none() && !growable && known_device.is_none() { return out; } @@ -731,6 +735,7 @@ impl Sim { let tile_known = match fog { Fog::Seen | Fog::Blueprint => Some(self.map.get_tile(x, y)), Fog::Remembered => self.remembered.get(&(x, y)).map(|m| m.tile), + Fog::Unknown if growable => Some(self.map.get_tile(x, y)), _ => None, }; if let Some(tile) = tile_known { @@ -744,13 +749,13 @@ impl Sim { automate: None, }); } - if tile == TileType::Rack && machine.is_none() { + if tile == TileType::Rack && machine.is_none() && (growable || fog != Fog::Unknown) { out.push(self.buy_rack_action(x, y)); } } // A known device on the tile brings its digital verbs. - if let Some(d) = self.reach.known_at(x, y) { + if let Some(d) = known_device { out.extend(self.device_actions(d.id)); } @@ -2074,7 +2079,8 @@ mod tests { } /// Criterion 2 (fog): an Unknown tile exposes nothing; the salvage - /// verb appears only where the tile identity is earned. + /// verb appears only where the tile identity is earned. Feel pads and + /// feel-joined devices are the deliberate exceptions (feel-floor.md). #[test] fn unknown_tiles_expose_nothing() { let s = sim(); @@ -2090,12 +2096,14 @@ mod tests { .is_empty() ); } - // Find some Unknown floor tile far from the core. + // Find some Unknown floor tile that is not a feel pad or known device. let mut checked = false; for y in 0..s.map.height { for x in 0..s.map.width { if s.fog_at(x, y) == Fog::Unknown && !s.compute.machines.iter().any(|m| m.x == x && m.y == y) + && !s.is_growable_bay(x, y) + && s.reach.known_at(x, y).is_none() { assert!( s.available_actions(Anchor::Tile { x, y }).is_empty(), diff --git a/src/bin/bevy.rs b/src/bin/bevy.rs index 310b4afb..37cc0d6e 100644 --- a/src/bin/bevy.rs +++ b/src/bin/bevy.rs @@ -332,6 +332,89 @@ struct ShotHarness { // ─── The wake (world/story/opening.md beat 1 — glow choreography) ──────────── +/// Build-beam birth gesture (feel-floor.md): pad → rising beam → chassis. +/// Frontend-only; the sim already placed the machine. +#[derive(Resource, Default)] +struct BuildBeam { + /// Tile where the beam plays; None when idle. + at: Option<(i32, i32)>, + /// Elapsed seconds of the gesture. + t: f32, + /// Machine count last observed — detect new placements. + last_count: usize, +} + +const BUILD_BEAM_LEN: f32 = 0.85; + +fn render_build_beam( + game: Res, + mode: Res, + time: Res