use crate::components::{ LogoHandle, ScaleToWindow, SplashSettings, SplashTime, StingHandle, TweenClearColor, }; use crate::SplashState; use micro_musicbox::prelude::MusicBox; use std::ops::Deref; use std::time::Duration; use bevy_asset::{Assets, AssetServer, Handle, LoadState, RecursiveDependencyLoadState}; use bevy_ecs::prelude::*; use bevy_input::prelude::*; use bevy_math::Vec2; use bevy_render::prelude::*; use bevy_render::texture::ImageSampler; use bevy_sprite::prelude::*; use bevy_time::Time; use bevy_transform::prelude::Transform; use bevy_hierarchy::DespawnRecursiveExt; use log::error; fn interpolate_colours(from: Color, to: Color, percent: f32) -> Color { let diff_r = to.r() - from.r(); let diff_g = to.g() - from.g(); let diff_b = to.b() - from.b(); let diff_a = to.a() - from.a(); Color::rgba( from.r() + diff_r * percent, from.g() + diff_g * percent, from.b() + diff_b * percent, from.a() + diff_a * percent, ) } pub fn load_assets<StateTypes: States>( mut commands: Commands, settings: Res<SplashSettings<StateTypes>>, assets: Res<AssetServer>, ) { commands.insert_resource(LogoHandle(assets.load(&settings.logo_path))); commands.insert_resource(StingHandle(assets.load(&settings.sting_path))); } pub fn monitor_asset_loading( logo: Res<LogoHandle>, sting: Res<StingHandle>, assets: Res<AssetServer>, mut state: ResMut<SplashState>, ) { if [ logo.as_ref().0.id().untyped(), sting.as_ref().0.id().untyped(), ] .iter() .flat_map(|handle| assets.get_recursive_dependency_load_state(*handle)) .fold(LoadState::Loaded, |total, current| { if total == LoadState::Loaded { match current { RecursiveDependencyLoadState::NotLoaded => LoadState::NotLoaded, RecursiveDependencyLoadState::Loading => LoadState::Loading, RecursiveDependencyLoadState::Loaded => LoadState::Loaded, RecursiveDependencyLoadState::Failed => LoadState::Failed, } } else { total } }) == LoadState::Loaded { *state = SplashState::Running; } } pub fn spawn_splash_assets<T: States>( mut commands: Commands, mut musicbox: MusicBox<AssetServer>, mut textures: ResMut<Assets<Image>>, logo: Res<LogoHandle>, settings: Res<SplashSettings<T>>, ) { if let (Some((from, to)), Some(duration)) = (settings.bg_color_tween, settings.tween_duration) { commands.insert_resource(TweenClearColor { from, to, duration, progress: Duration::ZERO, }); } musicbox.play_ui_sfx(&settings.sting_path); if let Some(tex) = textures.get_mut(&logo.0) { tex.sampler = ImageSampler::linear(); commands.spawn(( ScaleToWindow, SpriteBundle { texture: logo.clone_weak(), transform: Transform::from_xyz(0.0, 0.0, 10.0), ..Default::default() }, )); } else { error!("Failed to load splash texture"); } } pub fn tween_clear_color( mut commands: Commands, time: Res<Time>, mut conf: ResMut<TweenClearColor>, ) { conf.progress = conf.progress.saturating_add(time.delta()); if conf.progress >= conf.duration { commands.insert_resource(ClearColor(conf.to)); commands.remove_resource::<TweenClearColor>(); return; } let progress = conf.progress.as_secs_f32() / conf.duration.as_secs_f32(); commands.insert_resource(ClearColor(interpolate_colours( conf.from, conf.to, progress, ))); } pub fn apply_window_scale( window_query: Query<&OrthographicProjection>, mut scaled_query: Query< ( &mut Transform, Option<&Handle<Image>>, ), ( With<Handle<Image>>, With<ScaleToWindow>, ), >, images: Res<Assets<Image>>, ) { let ortho = match window_query.get_single() { Ok(val) => val, Err(_e) => { return; } }; let Vec2 { x: camera_width, y: camera_height, } = ortho.area.size(); for (mut transform, img_handle) in &mut scaled_query { let image = match img_handle { Some(handle) => match images.get(handle) { Some(image) => image, _ => continue, }, None => continue, }; let scale_factor = match camera_width > camera_height { true => camera_height / image.texture_descriptor.size.height as f32, false => camera_width / image.texture_descriptor.size.width as f32, }; transform.scale = [scale_factor, scale_factor, 1.0].into(); } } pub fn tick_time<StateType: States + Copy>( mut next_state: ResMut<NextState<StateType>>, time: Res<Time>, conf: Res<SplashSettings<StateType>>, mut timer: ResMut<SplashTime>, ) { *timer = SplashTime(timer.saturating_sub(time.delta())); if timer.0.is_zero() { next_state.set(conf.transition_to); } } pub fn window_skip<StateType: States + Copy>( input: Res<ButtonInput<KeyCode>>, mut state: ResMut<NextState<StateType>>, settings: Res<SplashSettings<StateType>>, ) { if input.any_just_pressed(vec![KeyCode::Escape, KeyCode::Space, KeyCode::Enter]) { state.set(settings.transition_to); } } pub fn cleanup_assets(mut commands: Commands, ents: Query<Entity, With<ScaleToWindow>>) { commands.remove_resource::<LogoHandle>(); commands.remove_resource::<StingHandle>(); commands.remove_resource::<TweenClearColor>(); commands.remove_resource::<SplashTime>(); commands.insert_resource(ClearColor(Color::BLACK)); for ent in &ents { commands.entity(ent).despawn_recursive(); } }