Skip to content
Snippets Groups Projects
system.rs 5.07 KiB
Newer Older
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();
	}
}