diff --git a/assets/martian.tweak.toml b/assets/martian.tweak.toml index 8ecfaf3..bc68187 100644 --- a/assets/martian.tweak.toml +++ b/assets/martian.tweak.toml @@ -30,6 +30,8 @@ Background 2D art by NASA: LINK HERE """ [intro] +# Higher rate is slower typing speed. Integers only! +rate = 5 text = """ At the intersection of humanity's wildest imaginations and the infinite of the cosmos, the lines between possible and real fall apart like dissolving paper. diff --git a/src/intro.rs b/src/intro.rs index b18cc6a..8aa0c37 100644 --- a/src/intro.rs +++ b/src/intro.rs @@ -1,3 +1,5 @@ +use bevy::core::FrameCount; + use crate::prelude::*; pub(crate) struct IntroPlugin; @@ -8,12 +10,17 @@ impl Plugin for IntroPlugin { OnExit(GameState::Loading), init_intro_text.run_if(resource_exists::()), ) + .init_resource::() .add_systems(OnEnter(GameState::Intro), activate::) .add_systems(OnExit(GameState::Intro), deactivate::) + .add_systems(Update, manage_intro_progress + .run_if(in_state(GameState::Intro)) + .run_if(not(resource_exists::())) + ) .add_systems(Update, play_intro - .run_if(in_state(GameState::())) - .run_if(not(resource_exists::())) - ) + .run_if(in_state(GameState::Intro)) + .run_if(resource_changed::()) + ) // Continue to play state if the intro is done playing out .add_systems( Update, @@ -30,6 +37,9 @@ struct Intro; #[derive(Debug, Resource)] struct IntroPlayed; +#[derive(Debug, Resource, Default)] +struct IntroProgress(usize); + // Draw the intro text (invisible) on startup // Requires the Tweakfile to be loaded fn init_intro_text( @@ -67,7 +77,7 @@ fn init_intro_text( value: c.to_string(), style: TextStyle { font_size: 16.0, - color: Color::WHITE, + color: Color::WHITE.with_a(0.0), ..default() }, })) @@ -76,14 +86,65 @@ fn init_intro_text( }); } +fn manage_intro_progress( + keys: Res>, + mut start: Local, + mut progress: ResMut, + framecount: Res, + mut commands: Commands, + tweaks_file: Res, + tweaks: Res>, +) { + // If this is the first run, initialize the starting point + if *start == 0 { + *start = framecount.0; + } + + // If the user hits 'return' set this to the end of the animation + if keys.just_pressed(KeyCode::Return) { + progress.0 = usize::MAX; + commands.insert_resource(IntroPlayed); + // Otherwise progress by N characters (1) + } else { + let tweak = tweaks + .get(tweaks_file.handle.clone()) + .expect("Load tweaks"); + let rate = tweak.get::("intro_rate").expect("[intro] rate = #"); + + progress.0 = ((framecount.0 - *start) / rate) as usize; + } +} + // Upon entering the Intro state, start the intro "animation" fn play_intro( - mut text: Query<&mut Text, With>, - progress: Local, + mut text: Query<(&mut Text, &mut Visibility), With>, + progress: Res, mut commands: Commands, ) { - todo!("Play the intro animation, typing each character one at a time..."); - commands.insert_resource(IntroPlayed); + // Iterate over all (one) text + text.iter_mut().for_each(|(mut t, mut v)| { + // If this is the first frame of the animation, make the object visibility + if progress.0 == 0 { + *v = Visibility::Inherited; + } + + // Iterate over all characters in the intro text + t.sections + .iter_mut() + .enumerate() + // Only operate on sections up to this point + .filter_map(|(i, s)| { + (i <= progress.0).then_some(s) + }) + // Set the alpha to 1.0 making it visible + .for_each(|s| { + s.style.color.set_a(1.0); + }); + + if t.sections.iter().all(|s| s.style.color.a() == 1.0) { + commands.insert_resource(IntroPlayed); + } + }); } // Intro animation reveals one character every nth frame