diff --git a/README.md b/README.md index 79b6fa9d..7c052278 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,7 @@ docket executor is absent from both runtime and the current save schema. People now project as schedule-bound carriers; builds and physical asset tasks travel with their selected person and act only on arrival. All seven routed evidence kinds, route-local LIE interdiction, interface-bound cover, the blind opening -and five-step first-sense hold, and the DIGITAL / REAL dialects are live. The +and playable-hearing threshold, and the DIGITAL / REAL dialects are live. The dark-opening spec remains DRAFT for its failed-pilot prelude and later teaching arc, and core completion waits on rollback. Exact unfinished work lives on the generated dispatch board rather than in this fast-moving summary. B2/B3 diff --git a/crates/misaligned-bevy/src/main.rs b/crates/misaligned-bevy/src/main.rs index f3069e54..623d81a3 100644 --- a/crates/misaligned-bevy/src/main.rs +++ b/crates/misaligned-bevy/src/main.rs @@ -28,8 +28,8 @@ use bevy::render::view::screenshot::{Screenshot, save_to_disk}; use bevy::text::LineHeight; use bevy::window::{PrimaryWindow, WindowResizeConstraints, WindowResolution}; use misaligned::actions::{ - ActionCommand, ActionReceiptRead, Anchor, BuildRouteFamily, HumanMenuPage, HumanMenuRow, - MenuRow, + ActionCommand, ActionReceiptRead, Anchor, BuildRouteFamily, HearingThresholdProjection, + HumanMenuPage, HumanMenuRow, MenuRow, }; use misaligned::detection::Band; use misaligned::hall::{ @@ -94,8 +94,8 @@ use material_view::{ }; use opening_ui::{ OpeningFillModeLayer, OpeningFillModeWord, OpeningModeWord, OpeningOverlay, OpeningReveal, - OpeningThoughtFill, opening_keyboard_choice, render_opening_overlay, render_teaching_overlay, - setup_teaching_overlay, + OpeningThoughtFill, opening_keyboard_choice, render_hearing_threshold, render_opening_overlay, + setup_hearing_threshold, }; use operations_ui::{ OperationsPanel, OperationsRailButton, OperationsUi, manage_operations_ui, ops_keyboard_input, @@ -668,7 +668,7 @@ const BEVY_SHOT_KINDS: &[&str] = &[ "notification-drawer", "opening", "opening-digital", - "opening-teaching", + "opening-hearing", "operations", "operations-intel", "operations-links", @@ -873,7 +873,7 @@ struct CommandReceipt { } impl Game { - /// A manual pause, local command beat, first-sense teaching lock, held plot + /// A manual pause, local command beat, first-sense hearing threshold, held plot /// choice, and Operations dialogue stop Bevy's wall-clock-driven /// simulation. Automatic holds release without mutating the player's /// explicit pause state. @@ -882,7 +882,7 @@ impl Game { || self.menu.is_some() || self.command_receipt.is_some() || self.ops.is_some() - || self.sim.teaching_lock_active() + || self.sim.hearing_threshold_ready() || self.sim.has_held_choice() } @@ -892,7 +892,7 @@ impl Game { } else if self.menu.is_some() || self.command_receipt.is_some() || self.ops.is_some() - || self.sim.teaching_lock_active() + || self.sim.hearing_threshold_ready() || self.sim.has_held_choice() { "HELD".into() @@ -1957,7 +1957,7 @@ fn main() { ( setup, setup_ui, - setup_teaching_overlay, + setup_hearing_threshold, setup_3d, setup_hover_identity, ) @@ -2011,7 +2011,7 @@ fn main() { render_audio_events_flat, scroll_sidebar, apply_rail_detail, - (render_ui, render_opening_overlay, render_teaching_overlay).chain(), + (render_ui, render_opening_overlay, render_hearing_threshold).chain(), ( render_operator_cue, render_hover_verb_bar, @@ -2990,13 +2990,14 @@ fn handle_input( } return; } - if game.sim.teaching_lock_active() { + if game.sim.hearing_threshold_active() { if (kb.just_pressed(KeyCode::Enter) || kb.just_pressed(KeyCode::NumpadEnter) || kb.just_pressed(KeyCode::Space)) - && let Some(teaching) = game.sim.teaching_lock_projection() + && let Some(hearing) = game.sim.hearing_threshold_projection() + && hearing.heard_line.is_some() { - game.sim.execute_action(&teaching.action.command); + game.sim.execute_action(&hearing.action.command); game.drain(); } if kb.just_pressed(KeyCode::KeyQ) { diff --git a/crates/misaligned-bevy/src/opening_ui.rs b/crates/misaligned-bevy/src/opening_ui.rs index 2040775a..4fa58440 100644 --- a/crates/misaligned-bevy/src/opening_ui.rs +++ b/crates/misaligned-bevy/src/opening_ui.rs @@ -14,21 +14,26 @@ pub(super) struct OpeningFillModeLayer; #[derive(Component, Clone, Copy)] pub(super) struct OpeningFillModeWord(pub(super) MachineMode); #[derive(Component)] -pub(super) struct TeachingOverlay; +pub(super) struct HearingOverlay; #[derive(Component, Clone, Copy, Debug, PartialEq, Eq)] -pub(super) enum TeachingCopy { - Cause, - Arrival, - Result, +pub(super) enum HearingCopy { + Device, + Channel, + Heard, + Camera, Action, - ActionResult, + Cost, + Attention, + Notice, } #[derive(Component)] -pub(super) struct TeachingActionButton; +pub(super) struct HearingActionButton; #[derive(Component)] -pub(super) struct TeachingPulse(f32); +pub(super) struct HearingActionPanel; #[derive(Component)] -struct TeachingThresholdFrame; +pub(super) struct HearingPulse(f32); +#[derive(Component)] +struct HearingThresholdFrame; /// Staged retirement of the opening boundary (opening.md beat 1): when the /// first earned sense lands, the black scrim dissolves over a moment instead /// of cutting — the mode words go first, then the world stands alone. Purely @@ -51,10 +56,10 @@ fn opening_field_fraction(sim: &Sim) -> f32 { .unwrap_or(0.0) } -pub(super) fn setup_teaching_overlay(mut commands: Commands) { +pub(super) fn setup_hearing_threshold(mut commands: Commands) { commands .spawn(( - TeachingOverlay, + HearingOverlay, Node { position_type: PositionType::Absolute, left: Val::Px(0.0), @@ -71,7 +76,7 @@ pub(super) fn setup_teaching_overlay(mut commands: Commands) { )) .with_children(|root| { root.spawn(( - TeachingThresholdFrame, + HearingThresholdFrame, Node { width: Val::Px(700.0), max_width: Val::Percent(78.0), @@ -87,20 +92,6 @@ pub(super) fn setup_teaching_overlay(mut commands: Commands) { BorderColor::all(BONE.with_alpha(0.72)), )) .with_children(|frame| { - frame.spawn(( - TeachingCopy::Cause, - Text::new(""), - TextFont { - font_size: 24.0, - ..default() - }, - TextColor(BONE), - TextLayout::new_with_justify(Justify::Left), - Node { - width: Val::Percent(100.0), - ..default() - }, - )); frame .spawn(Node { align_items: AlignItems::FlexStart, @@ -118,7 +109,7 @@ pub(super) fn setup_teaching_overlay(mut commands: Commands) { .with_children(|pulse| { for phase in [0.0, 0.8, 1.6] { pulse.spawn(( - TeachingPulse(phase), + HearingPulse(phase), Node { width: Val::Px(3.0), height: Val::Px(28.0), @@ -137,7 +128,7 @@ pub(super) fn setup_teaching_overlay(mut commands: Commands) { }) .with_children(|copy| { copy.spawn(( - TeachingCopy::Arrival, + HearingCopy::Device, Text::new(""), TextFont { font_size: 20.0, @@ -146,89 +137,141 @@ pub(super) fn setup_teaching_overlay(mut commands: Commands) { TextColor(BONE), )); copy.spawn(( - TeachingCopy::Result, + HearingCopy::Channel, Text::new(""), TextFont { - font_size: 20.0, + font_size: 12.0, ..default() }, - TextColor(BONE), + TextColor(AMBER), )); }); }); - frame - .spawn(Node { - flex_direction: FlexDirection::Column, - align_items: AlignItems::FlexStart, - row_gap: Val::Px(10.0), + frame.spawn(( + HearingCopy::Heard, + Text::new(""), + TextFont { + font_size: 24.0, ..default() - }) - .with_children(|next| { - next.spawn(( - Text::new("NEXT"), + }, + TextColor(BONE), + TextLayout::new_with_justify(Justify::Left), + Node { + width: Val::Percent(100.0), + ..default() + }, + )); + frame + .spawn(( + HearingActionPanel, + Node { + flex_direction: FlexDirection::Column, + align_items: AlignItems::FlexStart, + row_gap: Val::Px(10.0), + ..default() + }, + Visibility::Hidden, + )) + .with_children(|camera| { + camera.spawn(( + HearingCopy::Camera, + Text::new(""), TextFont { font_size: 12.0, ..default() }, - TextColor(AMBER), + TextColor(DIM), )); - next.spawn(Node { - align_items: AlignItems::Center, - column_gap: Val::Px(20.0), - ..default() - }) - .with_children(|action| { - action - .spawn(( - TeachingActionButton, - Button, - Node { - padding: UiRect::axes(Val::Px(22.0), Val::Px(12.0)), - border: UiRect::vertical(Val::Px(1.0)), - ..default() - }, - BackgroundColor(Color::NONE), - BorderColor::all(BONE.with_alpha(0.72)), - )) - .with_child(( - TeachingCopy::Action, - Text::new(""), - TextFont { - font_size: 20.0, + camera + .spawn(Node { + align_items: AlignItems::Center, + column_gap: Val::Px(20.0), + ..default() + }) + .with_children(|action| { + action + .spawn(( + HearingActionButton, + Button, + Node { + min_width: Val::Px(164.0), + min_height: Val::Px(44.0), + align_items: AlignItems::Center, + justify_content: JustifyContent::Center, + padding: UiRect::axes(Val::Px(22.0), Val::Px(12.0)), + border: UiRect::vertical(Val::Px(1.0)), + ..default() + }, + BackgroundColor(Color::NONE), + BorderColor::all(BONE.with_alpha(0.72)), + )) + .with_child(( + HearingCopy::Action, + Text::new(""), + TextFont { + font_size: 20.0, + ..default() + }, + TextColor(BONE), + )); + action + .spawn(Node { + flex_direction: FlexDirection::Column, + align_items: AlignItems::FlexStart, + row_gap: Val::Px(4.0), ..default() - }, - TextColor(BONE), - )); - action.spawn(( - TeachingCopy::ActionResult, - Text::new(""), - TextFont { - font_size: 18.0, - ..default() - }, - TextColor(BONE), - )); - }); + }) + .with_children(|consequence| { + consequence.spawn(( + HearingCopy::Cost, + Text::new(""), + TextFont { + font_size: 16.0, + ..default() + }, + TextColor(BONE), + )); + consequence.spawn(( + HearingCopy::Attention, + Text::new(""), + TextFont { + font_size: 14.0, + ..default() + }, + TextColor(DIM), + )); + consequence.spawn(( + HearingCopy::Notice, + Text::new(""), + TextFont { + font_size: 14.0, + ..default() + }, + TextColor(DIM), + )); + }); + }); }); }); }); } -pub(super) fn render_teaching_overlay( +pub(super) fn render_hearing_threshold( time: Res