diff --git a/assets/marian-chess.credits.txt b/assets/marian-chess.credits.txt new file mode 100644 index 0000000..ae3b1fd --- /dev/null +++ b/assets/marian-chess.credits.txt @@ -0,0 +1,27 @@ +# Martian Chess + +An Icehouse game by Andrew Looney + +Art by Sam Hall + +Programming by Elijah Voigt + +--- + +# Third Party Tools + +Bevy Engine: bevyengine.org + +FMOD/FMOD Studio: fmod.com + +--- + +# Art Assets + +Image Textures retrieved from textures.com: + +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 \ No newline at end of file diff --git a/src/credits.rs b/src/credits.rs new file mode 100644 index 0000000..da8e521 --- /dev/null +++ b/src/credits.rs @@ -0,0 +1,152 @@ +use std::str::Utf8Error; + +use bevy::{ + asset::{AssetLoader, LoadContext, LoadedAsset}, + reflect::{TypePath, TypeUuid}, + utils::BoxedFuture, +}; + +use crate::prelude::*; + +pub(crate) struct CreditsPlugin; + +impl Plugin for CreditsPlugin { + fn build(&self, app: &mut App) { + app.add_asset::() + .init_asset_loader::() + .add_systems(Startup, init_credits_ui) + .add_systems( + Update, + ( + update_credits.run_if(on_event::>()), + menu::exit_to_menu.run_if(in_state(GameState::Credits)), + ), + ) + .add_systems( + OnEnter(GameState::Credits), + activate::, + ) + .add_systems( + OnExit(GameState::Credits), + deactivate::, + ); + } +} + +#[derive(Debug, TypeUuid, TypePath)] +#[uuid = "43df4a09-b5f0-4619-9223-8cf67dc9e844"] +pub struct CreditsText { + sections: Vec, +} + +#[derive(Default)] +struct CreditsTextLoader; + +impl AssetLoader for CreditsTextLoader { + fn load<'a>( + &'a self, + bytes: &'a [u8], + load_context: &'a mut LoadContext, + ) -> BoxedFuture<'a, Result<(), bevy::asset::Error>> { + Box::pin(async move { + let custom_asset = parse_credits(bytes)?; + load_context.set_default_asset(LoadedAsset::new(custom_asset)); + Ok(()) + }) + } + + fn extensions(&self) -> &[&str] { + &["credits.txt"] + } +} + +fn parse_credits(bytes: &[u8]) -> Result { + let s = std::str::from_utf8(bytes)?; + let sections: Vec = s + .split('\n') + .filter(|l| l.len() > 0) + .map(|l| TextSection { + value: String::from(l), + ..default() + }) + .collect(); + + info!("Split lines: {:?}", s); + + Ok(CreditsText { sections }) +} + +#[derive(Debug, Component)] +struct Credits; + +#[derive(Debug, Component)] +struct CreditsCamera; + +fn init_credits_ui(mut commands: Commands, server: Res) { + commands.spawn(( + CreditsCamera, + Camera2dBundle { + camera: Camera { + is_active: false, + ..default() + }, + ..default() + }, + UiCameraConfig { show_ui: true }, + )); + + commands + .spawn(( + Credits, + 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, + ..default() + }, + )) + .with_children(|parent| { + let handle: Handle = server.load("marian-chess.credits.txt"); + + parent.spawn(( + handle.clone(), + TextBundle { + text: Text { + alignment: TextAlignment::Center, + sections: vec![], + ..default() + }, + background_color: Color::BLACK.with_a(0.5).into(), + ..default() + }, + )); + }); +} + +fn update_credits( + mut reader: EventReader>, + credits_texts: Res>, + mut query: Query<(&mut Text, &Handle)>, +) { + reader.iter().for_each(|event| match event { + AssetEvent::Created { handle } | AssetEvent::Modified { handle } => { + query + .iter_mut() + .filter(|(_, this_handle)| handle.clone() == (*this_handle).clone()) + .for_each(|(mut text, this_handle)| { + if let Some(credits_text) = credits_texts.get(this_handle) { + text.sections = credits_text.sections.clone(); + } + }); + } + AssetEvent::Removed { .. } => { + info!("Removed Credit resource... we don't handle that..."); + } + }) +} diff --git a/src/display2d.rs b/src/display2d.rs index 42f7be1..1af580d 100644 --- a/src/display2d.rs +++ b/src/display2d.rs @@ -52,8 +52,14 @@ impl Plugin for Display2dPlugin { set_transform, ), ) - .add_systems(OnEnter(GameState::Display2d), activate) - .add_systems(OnExit(GameState::Display2d), deactivate); + .add_systems( + OnEnter(GameState::Display2d), + activate::, + ) + .add_systems( + OnExit(GameState::Display2d), + deactivate::, + ); } } @@ -242,30 +248,6 @@ fn set_transform( }); } -fn activate( - mut cameras: Query<&mut Camera, With>, - mut boards: Query<&mut Visibility, With>, -) { - cameras.iter_mut().for_each(|mut camera| { - camera.is_active = true; - }); - boards.iter_mut().for_each(|mut visibility| { - *visibility = Visibility::Visible; - }); -} - -fn deactivate( - mut cameras: Query<&mut Camera, With>, - mut boards: Query<&mut Visibility, With>, -) { - cameras.iter_mut().for_each(|mut camera| { - camera.is_active = false; - }); - boards.iter_mut().for_each(|mut visibility| { - *visibility = Visibility::Hidden; - }); -} - fn active_tile( mut events: EventReader, sprite_q: Query<( diff --git a/src/display3d.rs b/src/display3d.rs index 8af92c5..2f37e3b 100644 --- a/src/display3d.rs +++ b/src/display3d.rs @@ -21,8 +21,14 @@ impl Plugin for Display3dPlugin { .run_if(in_state(GameState::Display3d)) .run_if(resource_exists::()), ) - .add_systems(OnEnter(GameState::Display3d), activate) - .add_systems(OnExit(GameState::Display3d), deactivate); + .add_systems( + OnEnter(GameState::Display3d), + activate::, + ) + .add_systems( + OnExit(GameState::Display3d), + deactivate::, + ); } } @@ -32,8 +38,12 @@ struct Board3d; #[derive(Debug, Component)] pub(crate) struct Piece3d; +#[derive(Debug, Component)] +struct Display3dCamera; + fn initialize_camera(mut commands: Commands) { commands.spawn(( + Display3dCamera, Camera3dBundle { camera: Camera { is_active: false, @@ -160,32 +170,6 @@ fn set_piece_position( }); } -/// Make this the active state -fn activate( - mut cameras: Query<&mut Camera, With>, - mut boards: Query<&mut Visibility, With>, -) { - cameras.iter_mut().for_each(|mut camera| { - camera.is_active = true; - }); - boards.iter_mut().for_each(|mut visibility| { - *visibility = Visibility::Visible; - }); -} - -/// Make this the non-active state -fn deactivate( - mut cameras: Query<&mut Camera, With>, - mut boards: Query<&mut Visibility, With>, -) { - cameras.iter_mut().for_each(|mut camera| { - camera.is_active = false; - }); - boards.iter_mut().for_each(|mut visibility| { - *visibility = Visibility::Hidden; - }); -} - /// Given a board index returns the Vec3 location in space fn board_translation(&BoardIndex { x, y }: &BoardIndex) -> Vec3 { // Scale x down by 4 to account for -4..4 scaling diff --git a/src/main.rs b/src/main.rs index 77730a2..a18f3ae 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ mod audio; +mod credits; mod debug; mod display2d; mod display3d; @@ -10,7 +11,6 @@ use std::time::Duration; use bevy::{ asset::{ChangeWatcher, HandleId}, - gltf::{GltfMesh, GltfNode}, input::{keyboard::KeyboardInput, ButtonState}, }; @@ -50,15 +50,17 @@ fn main() { display3d::Display3dPlugin, game::GamePlugin, menu::MenuPlugin, + credits::CreditsPlugin, )) .run(); } -#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States)] +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States, Component)] pub enum GameState { #[default] Loading, Menu, + Credits, Display2d, Display3d, } @@ -123,3 +125,27 @@ fn toggle_display_mode( _ => (), }) } + +fn activate( + mut cameras: Query<&mut Camera, With>, + mut entities: Query<&mut Visibility, With>, +) { + cameras.iter_mut().for_each(|mut camera| { + camera.is_active = true; + }); + entities.iter_mut().for_each(|mut visibility| { + *visibility = Visibility::Visible; + }); +} + +fn deactivate( + mut cameras: Query<&mut Camera, With>, + mut entities: Query<&mut Visibility, With>, +) { + cameras.iter_mut().for_each(|mut camera| { + camera.is_active = false; + }); + entities.iter_mut().for_each(|mut visibility| { + *visibility = Visibility::Hidden; + }); +} diff --git a/src/menu.rs b/src/menu.rs index af55f20..5406c81 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -14,15 +14,15 @@ impl Plugin for MenuPlugin { .add_systems( Update, ( - handle_menu_start, + handle_menu_button, handle_menu_quit, bevy::window::close_on_esc, interactive_button, ) .run_if(in_state(GameState::Menu)), ) - .add_systems(OnEnter(GameState::Menu), activate) - .add_systems(OnExit(GameState::Menu), deactivate); + .add_systems(OnEnter(GameState::Menu), activate::) + .add_systems(OnExit(GameState::Menu), deactivate::); } } @@ -32,9 +32,6 @@ struct MenuCamera; #[derive(Debug, Component)] struct MenuRoot; -#[derive(Debug, Component)] -struct Start; - #[derive(Debug, Component)] struct Quit; @@ -72,7 +69,7 @@ fn init_menu_ui(mut commands: Commands) { .with_children(|parent| { parent .spawn(( - Start, + GameState::Display2d, ButtonBundle { style: Style { padding: UiRect::all(Val::Px(5.0)), @@ -85,7 +82,7 @@ fn init_menu_ui(mut commands: Commands) { )) .with_children(|parent| { parent.spawn(( - Start, + GameState::Display2d, TextBundle::from_section( "Start", TextStyle { @@ -97,6 +94,33 @@ fn init_menu_ui(mut commands: Commands) { )); }); + parent + .spawn(( + GameState::Credits, + ButtonBundle { + style: Style { + padding: UiRect::all(Val::Px(5.0)), + margin: UiRect::all(Val::Px(5.0)), + ..default() + }, + background_color: Color::ORANGE.with_a(0.5).into(), + ..default() + }, + )) + .with_children(|parent| { + parent.spawn(( + GameState::Credits, + TextBundle::from_section( + "Credits", + TextStyle { + color: Color::BLACK, + font_size: 32.0, + ..default() + }, + ), + )); + }); + parent .spawn(( Quit, @@ -126,38 +150,14 @@ fn init_menu_ui(mut commands: Commands) { }); } -fn activate( - mut cameras: Query<&mut Camera, With>, - mut boards: Query<&mut Visibility, With>, -) { - cameras.iter_mut().for_each(|mut camera| { - camera.is_active = true; - }); - boards.iter_mut().for_each(|mut visibility| { - *visibility = Visibility::Visible; - }); -} - -fn deactivate( - mut cameras: Query<&mut Camera, With>, - mut boards: Query<&mut Visibility, With>, -) { - cameras.iter_mut().for_each(|mut camera| { - camera.is_active = false; - }); - boards.iter_mut().for_each(|mut visibility| { - *visibility = Visibility::Hidden; - }); -} - -fn handle_menu_start( - events: Query<&Interaction, (With, With