use crate::components::{
LogoHandle, ScaleToWindow, SplashSettings, SplashTime, StingHandle, TweenClearColor,
};
use crate::SplashState;
use micro_musicbox::prelude::MusicBox;
use std::time::Duration;
use bevy_asset::{AssetServer, Assets, Handle, LoadState, RecursiveDependencyLoadState};
use bevy_ecs::prelude::*;
use bevy_hierarchy::DespawnRecursiveExt;
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 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();
}
}