diff --git a/crates/misaligned-assets/src/bin/misaligned-effects.rs b/crates/misaligned-assets/src/bin/misaligned-effects.rs index 52236c5d..92af5d98 100644 --- a/crates/misaligned-assets/src/bin/misaligned-effects.rs +++ b/crates/misaligned-assets/src/bin/misaligned-effects.rs @@ -1,6 +1,7 @@ //! Focused Thought-fluid laboratory using the same effects plugin as the game. //! -//! Controls: 1 route, 2 pools, 3 both, 4 stress; Q/W/E force close/medium/far; +//! Controls: 1 route, 2 pools, 3 both, 4 stress, 5 vessel lineup, 6 pool lineup; +//! Q/W/E force close/medium/far; //! A automatic LOD; Space pause; arrows change amounts; drag orbit; scroll zoom. use bevy::core_pipeline::tonemapping::Tonemapping; @@ -9,9 +10,11 @@ use bevy::prelude::*; use bevy::render::view::screenshot::{Screenshot, save_to_disk}; use bevy::window::{CursorGrabMode, CursorOptions, WindowResolution}; use misaligned_assets::effects::{ - EffectLod, EffectsControl, MaterialEffects, MaterialEffectsPlugin, spawn_material_effects, + EffectLod, EffectsControl, MaterialEffects, MaterialEffectsPlugin, ThoughtVesselGeometry, + ThoughtVesselStroke, spawn_material_effects, thought_snap_segments, thought_vessel_segments, }; use misaligned_assets::palette::{BONE, GUNMETAL, NEAR_BLACK, PORCELAIN, scaled}; +use misaligned_assets::rack::spawn_token_anchors; #[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Resource)] enum LabPreset { @@ -20,6 +23,8 @@ enum LabPreset { #[default] Both, Stress, + Lineup, + PoolLineup, } impl LabPreset { @@ -29,6 +34,8 @@ impl LabPreset { Self::Pools => "pools", Self::Both => "both", Self::Stress => "stress", + Self::Lineup => "vessel lineup", + Self::PoolLineup => "pool lineup", } } } @@ -76,6 +83,23 @@ fn main() { fixed_step: shot.as_ref().map(|_| 1.0 / 60.0), ..default() }; + let orbit = if preset == LabPreset::PoolLineup { + Orbit { + yaw: 0.0, + pitch: 0.12, + distance: 4.3, + target: Vec3::new(0.0, 0.72, 0.18), + } + } else if preset == LabPreset::Lineup { + Orbit { + yaw: 0.0, + pitch: 0.14, + distance: 4.8, + target: Vec3::new(0.0, 0.50, 0.18), + } + } else { + Orbit::default() + }; let hidden = shot.is_some(); let mut app = App::new(); @@ -97,7 +121,7 @@ fn main() { }) .insert_resource(control) .insert_resource(preset) - .insert_resource(Orbit::default()) + .insert_resource(orbit) .add_systems(Startup, setup) .add_systems( Update, @@ -107,6 +131,7 @@ fn main() { rebuild_lab_effects, orbit_camera, update_hud, + draw_vessel_lineup, shot_harness_system, ) .chain(), @@ -136,6 +161,8 @@ fn shot_settings() -> Option<(LabPreset, EffectLod, String)> { "pools" => LabPreset::Pools, "both" => LabPreset::Both, "stress" => LabPreset::Stress, + "lineup" => LabPreset::Lineup, + "pool-lineup" => LabPreset::PoolLineup, _ => return None, }; let path = std::env::var("MISALIGNED_SHOT_PATH") @@ -146,10 +173,18 @@ fn shot_settings() -> Option<(LabPreset, EffectLod, String)> { fn setup( mut commands: Commands, orbit: Res, + preset: Res, mut meshes: ResMut>, mut materials: ResMut>, + mut config_store: ResMut, harness: Option>, ) { + let (gizmos, _) = config_store.config_mut::(); + gizmos.line.width = if *preset == LabPreset::Lineup { + 5.0 + } else { + 2.0 + }; let eye = orbit_eye(&orbit); commands.spawn(( Camera3d::default(), @@ -194,28 +229,112 @@ fn setup( ..default() }); commands.spawn(( - Mesh3d(meshes.add(Cuboid::new(7.5, 0.08, 5.0))), + Mesh3d(meshes.add( + if matches!(*preset, LabPreset::Lineup | LabPreset::PoolLineup) { + Cuboid::new(7.5, 0.08, 2.8) + } else { + Cuboid::new(7.5, 0.08, 5.0) + }, + )), MeshMaterial3d(floor), Transform::from_xyz(0.0, -0.06, 0.0), Name::new("clinical_floor"), )); - // Two matte chalk forms provide reflections and make the route/pool - // silhouette judgeable without borrowing a game machine. + // Matte chalk forms provide reflections and make route/pool or + // hardware-attached vessel silhouettes judgeable without borrowing a + // game machine. let chalk = materials.add(StandardMaterial { base_color: PORCELAIN, metallic: 0.0, perceptual_roughness: 0.92, ..default() }); - for x in [-1.65, 1.55] { + let anchors: &[f32] = if *preset == LabPreset::Lineup { + &[-2.4, -0.8, 0.8, 2.4] + } else if *preset == LabPreset::PoolLineup { + &[-2.2, 0.0, 2.2] + } else { + &[-1.65, 1.55] + }; + for &x in anchors { commands.spawn(( - Mesh3d(meshes.add(Cuboid::new(0.42, 1.20, 0.62))), + Mesh3d(meshes.add(Cuboid::new( + if *preset == LabPreset::PoolLineup { + 1.0 + } else { + 0.42 + }, + if *preset == LabPreset::PoolLineup { + 1.50 + } else if *preset == LabPreset::Lineup { + 0.72 + } else { + 1.20 + }, + if *preset == LabPreset::PoolLineup { + 0.78 + } else { + 0.62 + }, + ))), MeshMaterial3d(chalk.clone()), - Transform::from_xyz(x, 0.58, 0.0), + Transform::from_xyz( + x, + if *preset == LabPreset::PoolLineup { + 0.73 + } else if *preset == LabPreset::Lineup { + 0.34 + } else { + 0.58 + }, + 0.0, + ), Name::new("chalk_effect_anchor"), )); } + if *preset == LabPreset::Lineup { + for (x, label) in [ + (-2.4_f32, "HELD"), + (-0.8, "FED"), + (0.8, "DRAIN"), + (2.4, "SNAP"), + ] { + commands.spawn(( + Text2d::new(label), + TextFont::from_font_size(22.0), + TextColor(BONE), + Transform::from_xyz(x, 0.07, 0.58), + Name::new("thought_vessel_label"), + )); + } + } + if *preset == LabPreset::PoolLineup { + for (x, label, amount, stranded) in [ + (-2.2_f32, "COUNTABLE", 2.0_f32, false), + (0.0, "TAUT", 6.0, false), + (2.2, "STRANDED", 10.0, true), + ] { + spawn_token_anchors( + &mut commands, + &mut meshes, + &mut materials, + 0, + amount, + 0.0, + stranded, + Vec3::new(x, 0.0, 0.0), + None, + ); + commands.spawn(( + Text2d::new(label), + TextFont::from_font_size(22.0), + TextColor(BONE), + Transform::from_xyz(x, 0.07, 0.58), + Name::new("thought_pool_label"), + )); + } + } if harness.is_none() { commands.spawn(( @@ -268,6 +387,10 @@ fn handle_controls( Some(LabPreset::Both) } else if keys.just_pressed(KeyCode::Digit4) { Some(LabPreset::Stress) + } else if keys.just_pressed(KeyCode::Digit5) { + Some(LabPreset::Lineup) + } else if keys.just_pressed(KeyCode::Digit6) { + Some(LabPreset::PoolLineup) } else { None }; @@ -333,7 +456,7 @@ fn rebuild_lab_effects( commands.entity(root).insert(LabEffectRoot); } } - } else { + } else if !matches!(*preset, LabPreset::Lineup | LabPreset::PoolLineup) { let mut effects = MaterialEffects::default(); apply_preset(*preset, &mut effects); let root = spawn_material_effects(&mut commands, effects, Transform::IDENTITY); @@ -382,9 +505,6 @@ fn update_hud( effects: Query<&MaterialEffects, With>, mut hud: Query<&mut Text, With>, ) { - let Some(effect) = effects.iter().next() else { - return; - }; let Ok(mut text) = hud.single_mut() else { return; }; @@ -392,19 +512,78 @@ fn update_hud( .forced_lod .map(EffectLod::label) .unwrap_or("auto (zoom)"); + let amount = effects + .iter() + .next() + .map(|effect| { + format!( + "thought {:.0} @ {:.2}", + effect.thought_amount, effect.thought_rate + ) + }) + .unwrap_or_else(|| match *preset { + LabPreset::Lineup => "STARVED · FED STEP · TAP SAG · SNAP".into(), + LabPreset::PoolLineup => "COUNTABLE · TAUT · STRANDED".into(), + _ => String::new(), + }); *text = Text::new(format!( "MISALIGNED EFFECTS LAB · {} · LOD {}{}\n\ - thought {:.0} @ {:.2}\n\ - 1 route 2 pools 3 both 4 stress Q/W/E close/medium/far A auto Space pause\n\ + {amount}\n\ + 1 route 2 pools 3 both 4 stress 5 vessels 6 pools Q/W/E close/medium/far A auto Space pause\n\ arrows amount/rate drag orbit scroll zoom", preset.label(), lod, if control.paused { " · PAUSED" } else { "" }, - effect.thought_amount, - effect.thought_rate, )); } +fn draw_vessel_lineup( + preset: Res, + harness: Option>, + time: Res