diff --git a/assets/fonts/Silkscreen/OFL.txt b/assets/fonts/Silkscreen/OFL.txt new file mode 100644 index 0000000..a1fe7d5 --- /dev/null +++ b/assets/fonts/Silkscreen/OFL.txt @@ -0,0 +1,93 @@ +Copyright 2001 The Silkscreen Project Authors (https://github.com/googlefonts/silkscreen) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/assets/fonts/Silkscreen/Silkscreen-Bold.ttf b/assets/fonts/Silkscreen/Silkscreen-Bold.ttf new file mode 100644 index 0000000..e934b6f Binary files /dev/null and b/assets/fonts/Silkscreen/Silkscreen-Bold.ttf differ diff --git a/assets/fonts/Silkscreen/Silkscreen-Regular.ttf b/assets/fonts/Silkscreen/Silkscreen-Regular.ttf new file mode 100644 index 0000000..ecb242b Binary files /dev/null and b/assets/fonts/Silkscreen/Silkscreen-Regular.ttf differ diff --git a/assets/images/Liquid Mirror.jpg b/assets/images/Liquid Mirror.jpg new file mode 100644 index 0000000..c007522 Binary files /dev/null and b/assets/images/Liquid Mirror.jpg differ diff --git a/assets/martian.tweak.toml b/assets/martian.tweak.toml index c9cc7d2..0eb9e68 100644 --- a/assets/martian.tweak.toml +++ b/assets/martian.tweak.toml @@ -30,29 +30,45 @@ Concrete Energy Pole - https://www.textures.com/download/PBR0340/136381 Space Blanket Folds - https://www.textures.com/download/PBR0152/133187 Background 2D art by NASA: LINK HERE + +Silkscreen Font by Jason Kottke - https://www.kottke.org/plus/type/silkscreen/ """ ### # Intro text, typed out during normal gameplay ### [intro] +# Colors are in hex (RED GREEN BLUE ALPHA) +rgba_hidden = "#00000000" +rgba_visible = "#FFFFFFFF" +rgba_background = "#000000FF" # Higher rate is slower typing speed. Integers only! -rate = 5 -text = """ +delay = 1.0 +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. - +""", +""" On Earth, a long tradition of combat and destruction amongst different groups has been upheld and respected; given a place in the social mind. As spacetime stretches ever on, the rhyme of the universe reveals itself, and even includes such traditions. - +""", +""" And so, humanity came face to face with a curiosity. Looking to our martian analogues, we find a twin destructive tradition, but one which seems backwards. In their tradition, each victory is also a loss. Each battle is waged simultaneously with opponent and self, and a mutual pact of annihilation and tactics unfolds until one side lays barren, just as likely to be the victor. - +""", +""" But Can you adapt? Will you be a part of the universal rhyme, and if so, which side are you on? -""" +""", +] ### # Tutorial text, revealed interactively based on context ### [tutorial] +# Hex colors for in/visible text (only visible used for now) +rgba_hidden = "#00000000" +rgba_visible = "#FFFFFFFF" +rgba_background = "#000000FF" + intro = [ """ Greetings general, and welcome to 'Martian Advanced Rules for Generals: Long-Term engagement strategies'. As a part of your assignment to operation Red Rocks, it is in your best interest to thoroughly utilize this simulation to best prepare you for engagement with, and strategic domination of, Martian forces. @@ -242,4 +258,7 @@ tonemapping = "ReinhardLuminance" exposure = 2.0 gamma = 1.0 pre_saturation = 1.0 -post_saturation = 1.0 \ No newline at end of file +post_saturation = 1.0 + +[display3d.bloom] +intensity = 0.15 \ No newline at end of file diff --git a/src/display3d.rs b/src/display3d.rs index b68b46a..d5ac6fa 100644 --- a/src/display3d.rs +++ b/src/display3d.rs @@ -6,12 +6,16 @@ use crate::{ use bevy::{ animation::RepeatAnimation, core_pipeline::{ + bloom::BloomSettings, experimental::taa::{TemporalAntiAliasPlugin, TemporalAntiAliasSettings}, prepass::MotionVectorPrepass, tonemapping::{DebandDither, Tonemapping}, Skybox, }, - input::mouse::{MouseButtonInput, MouseMotion, MouseScrollUnit, MouseWheel}, + input::{ + mouse::{MouseButtonInput, MouseMotion, MouseScrollUnit, MouseWheel}, + ButtonState, + }, pbr::{ ExtendedMaterial, MaterialExtension, ScreenSpaceAmbientOcclusionBundle, ScreenSpaceAmbientOcclusionSettings, @@ -254,6 +258,7 @@ fn hydrate_camera( color_grading: ColorGrading { ..default() }, ..default() }, + BloomSettings { ..default() }, Skybox(skybox_handle.clone()), EnvironmentMapLight { diffuse_map: skybox_handle.clone(), @@ -304,6 +309,7 @@ fn update_tweaks( &mut FogSettings, &mut ColorGrading, &mut Tonemapping, + &mut BloomSettings, ), With, >, @@ -321,7 +327,7 @@ fn update_tweaks( ) { if let Some(tweak) = tweaks.get(tweaks_file.handle.clone()) { camera_settings.iter_mut().for_each( - |(entity, mut fog, mut color_grading, mut tonemapping)| { + |(entity, mut fog, mut color_grading, mut tonemapping, mut bloom)| { *fog = tweak .get::("display3d_fog") .unwrap() @@ -334,6 +340,10 @@ fn update_tweaks( .get::("display3d_color_tonemapping") .unwrap() .into(); + *bloom = tweak + .get::("display3d_bloom") + .unwrap() + .into(); let quality_level = tweak.get::( "display3d_ssao_quality_level", @@ -1116,9 +1126,10 @@ fn scale_lighting( pub(super) mod tweaks { use bevy::{ - core_pipeline::tonemapping::Tonemapping, + core_pipeline::{bloom::BloomSettings, tonemapping::Tonemapping}, math::Vec3, pbr::{FogFalloff, FogSettings, ScreenSpaceAmbientOcclusionQualityLevel}, + prelude::*, render::{ color::Color, view::{ColorGrading, Msaa}, @@ -1155,6 +1166,20 @@ pub(super) mod tweaks { } } + #[derive(Debug, Deserialize)] + pub struct TweakBloomSettings { + intensity: f32, + } + + impl From for BloomSettings { + fn from(src: TweakBloomSettings) -> BloomSettings { + BloomSettings { + intensity: src.intensity, + ..default() + } + } + } + #[derive(Debug, Deserialize)] pub enum TweakMsaa { Off, diff --git a/src/intro.rs b/src/intro.rs index 7220ce9..c16af98 100644 --- a/src/intro.rs +++ b/src/intro.rs @@ -1,7 +1,11 @@ -use bevy::core::FrameCount; +use std::collections::VecDeque; + +use bevy::{core::FrameCount, utils::HashMap}; use crate::prelude::*; +use self::ui::TextScroll; + pub(crate) struct IntroPlugin; impl Plugin for IntroPlugin { @@ -12,27 +16,22 @@ impl Plugin for IntroPlugin { .run_if(resource_exists::()) .run_if(run_once()), ) - .init_resource::() - .add_systems(OnEnter(GameState::Intro), activate::) + .add_systems(OnEnter(GameState::Intro), (activate::, start_intro)) .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 + play_text_scroll .run_if(in_state(GameState::Intro)) - .run_if(resource_changed::()), + .run_if(any_component_changed::), ) - // Continue to play state if the intro is done playing out .add_systems( Update, - continue_to_play - .run_if(|keys: Res>| -> bool { keys.just_pressed(KeyCode::Return) }) - .run_if(resource_exists::()), + // Play intro manages playing the intro of each individual paragraph + play_intro.run_if( + any_component_added:: + .or_else(any_component_removed::()) + .or_else(|keys: Res>| -> bool { keys.just_pressed(KeyCode::Return) }) + ) ); } } @@ -43,19 +42,20 @@ 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( tweaks_file: Res, tweaks: Res>, + ui_font: Res, mut commands: Commands, ) { let tweak = tweaks.get(tweaks_file.handle.clone()).expect("Load tweaks"); - let text = tweak.get::("intro_text").expect("Intro text"); + let texts = tweak.get::>("intro_text").expect("Intro text"); + + let background_hex = tweak.get::("intro_rgba_background").unwrap(); + let text_hidden_hex = tweak.get::("intro_rgba_hidden").unwrap(); commands .spawn(( @@ -85,88 +85,124 @@ fn init_intro_text( padding: UiRect::all(Val::Px(25.0)), ..default() }, - background_color: Color::BLACK.with_a(1.0).into(), + background_color: Color::hex(&background_hex).unwrap().into(), ..default() }, )) .with_children(|parent| { - parent.spawn(( - Intro, - TextBundle::from_sections(text.chars().into_iter().map(|c| TextSection { - value: c.to_string(), - style: TextStyle { - font_size: 16.0, - color: Color::WHITE.with_a(0.2), - ..default() - }, - })) - .with_text_alignment(TextAlignment::Center), - )); + texts.iter().for_each(|text| { + parent.spawn(( + Intro, + TextBundle::from_sections(text.chars().into_iter().map(|c| TextSection { + value: c.to_string(), + style: TextStyle { + font_size: 16.0, + color: Color::hex(&text_hidden_hex).unwrap(), + font: ui_font.handle.clone(), + }, + })) + .with_text_alignment(TextAlignment::Center), + )); + }); }); }); } -fn manage_intro_progress( - keys: Res>, - mut start: Local, - mut progress: ResMut, - framecount: Res, +fn start_intro( + query: Query, With)>, + mut commands: Commands, +) { + query.iter().for_each(|e| { + commands.entity(e).insert(TextScroll { progress: 0, start: 0 }); + }); +} + +// Manages playing the intro animation +// Each paragrpah types out letter by letter in a separate animation handled by play_text_scroll +// This simply marks each paragraph for play and (should) stay dormant while those animations are playing +fn play_intro( + parents: Query, With, With)>, + mut texts: Query<(Entity, Option<&mut TextScroll>), (With, With)>, + children: Query<&Children>, + mut queues: Local>>, mut commands: Commands, tweaks_file: Res, tweaks: Res>, + framecount: 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 = #"); + info!("Play intro..."); - progress.0 = ((framecount.0 - *start) / rate) as usize; - } + // Iterate over all UI nodes which are marked for animation + parents.iter().for_each(|p| { + // If the hash does not contain this parent key, add the VecDeque of child entities + if !(*queues).contains_key(&p) { + (*queues).insert(p, texts.iter_many(children.iter_descendants(p)).map(|(e, _)| e).collect()); + } + // Fetch the VecDeque so we can play with it + let queue = (*queues).get_mut(&p).unwrap(); + + // Pop the front of the line, this is the next entity to animate + if let Some(e) = queue.pop_front() { + if let Ok((e, opt_ts)) = texts.get_mut(e) { + // If this entity has a TextScroll already, update it + if let Some(mut ts) = opt_ts { + // Increment the progress counter by some variable amount + let tweak = tweaks.get(tweaks_file.handle.clone()).expect("Load tweaks"); + let delay = tweak.get::("intro_delay").expect("[intro] delay = float"); + + ts.progress = ((framecount.0 - ts.start) as f32 / delay) as usize; + // Otherwise, initialize it with one + } else { + // Insert a fresh TextScroll into this paragraph + commands.entity(e).insert(TextScroll { start: framecount.0, progress: 0 }); + } + } else { + panic!("This shouldn't happen!"); + } + } else { + // We have played all child entities, so remove the animate marker + commands.entity(p).remove::(); + } + }) } // Upon entering the Intro state, start the intro "animation" -fn play_intro( - mut text: Query<(&mut Text, &mut Visibility), With>, - progress: Res, +fn play_text_scroll( + tweaks_file: Res, + tweaks: Res>, + mut text: Query<(Entity, &mut Text, &mut Visibility, &ui::TextScroll)>, mut commands: Commands, ) { + info!("Play text scroll"); + // Iterate over all (one) text - text.iter_mut().for_each(|(mut t, mut v)| { + text.iter_mut().for_each(|(e, mut t, mut v, ui::TextScroll { progress, .. })| { // If this is the first frame of the animation, make the object visibility - if progress.0 == 0 { + if *progress == 0 { *v = Visibility::Inherited; } + let tweak = tweaks.get(tweaks_file.handle.clone()).expect("Load tweaks"); + let text_visible_hex = tweak.get::("intro_rgba_visible").unwrap(); + let text_color = Color::hex(&text_visible_hex).unwrap(); + // 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)) + .filter_map(|(i, s)| (i <= *progress).then_some(s)) // Set the alpha to 1.0 making it visible .for_each(|s| { - s.style.color.set_a(1.0); + s.style.color = text_color.clone(); }); - if t.sections.iter().all(|s| s.style.color.a() == 1.0) { - commands.insert_resource(IntroPlayed); + // Once the animation is done, remove the marker component + if t.sections.iter().all(|s| s.style.color == text_color) { + commands.entity(e).remove::(); } }); } // Intro animation reveals one character every nth frame -// Hit enter to skip the "animation" - -// Hit enter once the animation is complete to start the game -fn continue_to_play(mut next_state: ResMut>) { - next_state.set(GameState::Play) -} +// Hit enter to skip the "animation" \ No newline at end of file diff --git a/src/loading.rs b/src/loading.rs index 5b0b572..ce69c1b 100644 --- a/src/loading.rs +++ b/src/loading.rs @@ -6,48 +6,48 @@ impl Plugin for LoadingPlugin { fn build(&self, app: &mut App) { app.add_systems(Startup, initialize) .add_systems(Update, loading.run_if(in_state(GameState::Loading))) - .add_systems(OnEnter(GameState::Loading), activate::) - .add_systems(OnExit(GameState::Loading), deactivate::); + .add_systems(OnEnter(GameState::Loading), (activate::, enter_loading)) + .add_systems(OnExit(GameState::Loading), (deactivate::, exit_loading)); } } #[derive(Debug, Component)] struct Loading; -fn initialize(mut commands: Commands) { - commands - .spawn(( - Loading, - NodeBundle { - style: Style { - width: Val::Percent(100.0), - height: Val::Percent(100.0), - justify_content: JustifyContent::Center, - align_items: AlignItems::Center, - position_type: PositionType::Absolute, - ..default() - }, - visibility: Visibility::Hidden, +// TODO: Why is this image not showing?? +fn initialize(mut commands: Commands, server: Res) { + commands.spawn(( + Loading, + Camera2dBundle { ..default() }, + UiCameraConfig { show_ui: true }, + )); + commands.spawn(( + Loading, + ImageBundle { + image: UiImage { + texture: server.load("images/Liquid Mirror.jpg"), ..default() }, - )) - .with_children(|parent| { - parent.spawn((TextBundle { - text: Text { - alignment: TextAlignment::Center, - sections: vec![TextSection { - value: "l o a d i n g . . .".into(), - style: TextStyle { - color: Color::WHITE.into(), - ..default() - }, - }], - ..default() - }, - background_color: Color::BLACK.with_a(0.5).into(), + style: Style { + width: Val::Percent(100.0), + height: Val::Percent(100.0), ..default() - },)); - }); + }, + visibility: Visibility::Hidden, + ..default() + }, + )); +} + +/// On enter loading, activate camera +fn enter_loading(mut cameras: Query<&mut Camera, With>) { + cameras.iter_mut().for_each(|mut camera| camera.is_active = true); +} + +/// On exit loading, deactivate camera +/// This used to be general purpose, but we got rid of the 2d display mode, so it is hard coded for loading -> main game +fn exit_loading(mut cameras: Query<&mut Camera, With>) { + cameras.iter_mut().for_each(|mut camera| camera.is_active = false); } fn loading( @@ -55,6 +55,7 @@ fn loading( sprites: Res>, gltfs: Res>, tweaks: Res>, + fonts: Res>, mut next_state: ResMut>, ) { let s = (!sprites.is_empty()) @@ -66,11 +67,11 @@ fn loading( && tweaks .ids() .all(|id| server.is_loaded_with_dependencies(id)); + let f = (!fonts.is_empty()) && fonts.ids().all(|id| server.is_loaded_with_dependencies(id)); - debug!("s {} g {} t {}", s, g, t); + debug!("s {} g {} t {} f {}", s, g, t, f); if t { - // s && g && t { next_state.set(GameState::Menu) } } diff --git a/src/main.rs b/src/main.rs index d699300..9e1dbc7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,8 +18,6 @@ mod ui; use std::time::Duration; -use bevy::input::ButtonState; - use crate::prelude::*; fn main() { @@ -39,14 +37,6 @@ fn main() { .run_if(resource_changed::>()), ), ); - app.add_systems( - Update, - ( - // TODO: Run this other times too so we ensure a camera is always active - toggle_display_camera.run_if(state_changed::()), - toggle_display_camera.run_if(state_changed::()), - ), - ); app.add_plugins( DefaultPlugins .set(ImagePlugin::default_nearest()) @@ -58,6 +48,7 @@ fn main() { ..default() }), ); + app.add_plugins(credits::CreditsPlugin); app.add_plugins(debug::DebugPlugin); app.add_plugins(display3d::Display3dPlugin); @@ -96,15 +87,6 @@ fn debug_state(state: Res>) { info!("State change {:?}", *state); } -fn toggle_display_camera( - state: Res>, - mut cameras: Query<(&mut Camera, &DisplayState)>, -) { - cameras.iter_mut().for_each(|(mut camera, display_state)| { - camera.is_active = display_state == state.get(); - }); -} - fn activate( mut entities: Query< &mut Visibility, diff --git a/src/tutorial.rs b/src/tutorial.rs index d5a73c9..d7f6195 100644 --- a/src/tutorial.rs +++ b/src/tutorial.rs @@ -75,6 +75,7 @@ pub(crate) enum TutorialState { // Init animations, initialize all text windows but make invisible // This should be run when TutorialState::Intro fn initialize_tutorial( + ui_font: Res, tweaks_file: Res, tweaks: Res>, mut commands: Commands, @@ -83,6 +84,9 @@ fn initialize_tutorial( info!("Initializing tutorial entities"); + let background_hex = tweak.get::("tutorial_rgba_background").unwrap(); + let text_visible_hex = tweak.get::("tutorial_rgba_visible").unwrap(); + // List of (state, lines) for tutorial steps [ ( @@ -182,7 +186,7 @@ fn initialize_tutorial( padding: UiRect::all(Val::Px(25.0)), ..default() }, - background_color: Color::BLACK.with_a(1.0).into(), + background_color: Color::hex(&background_hex).unwrap().into(), ..default() }, )) @@ -197,8 +201,8 @@ fn initialize_tutorial( value: line.clone(), style: TextStyle { font_size: 16.0, - color: Color::WHITE.with_a(1.0), - ..default() + color: Color::hex(&text_visible_hex).unwrap(), + font: ui_font.handle.clone(), }, }) .collect(), diff --git a/src/ui.rs b/src/ui.rs index e98a40f..c007926 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -6,10 +6,12 @@ pub(crate) struct UiPlugin; impl Plugin for UiPlugin { fn build(&self, app: &mut App) { - app.add_systems( + app.add_systems(Startup, load_assets).add_systems( Update, ( - manage_cursor.run_if(any_component_changed::), + manage_cursor.run_if( + any_component_changed::.or_else(state_changed::()), + ), interactive_button.run_if(any_component_changed::), scale_ui.run_if( on_event::>() @@ -24,6 +26,26 @@ impl Plugin for UiPlugin { #[derive(Debug, Component)] struct UiRoot; +#[derive(Debug, Resource)] +pub(crate) struct UiFont { + pub handle: Handle, +} + +#[derive(Debug, Component)] +pub(crate) struct TextScroll { + pub progress: usize, + pub start: u32, +} + +#[derive(Debug, Component)] +pub(crate) struct TextScrollAnimation; + +fn load_assets(server: ResMut, mut commands: Commands) { + commands.insert_resource(UiFont { + handle: server.load("fonts/Silkscreen/Silkscreen-Regular.ttf"), + }); +} + fn manage_cursor( mut window: Query<&mut Window, With>, state: Res>, @@ -74,9 +96,6 @@ fn scale_ui( tweakfile: Res, tweaks: Res>, ) { - // There is only 1 window - assert_eq!(windows.iter().count(), 1); - if !tweaks.contains(tweakfile.handle.clone()) { return; }