diff --git a/assets/martian.tweak.toml b/assets/martian.tweak.toml index baae01f..f1af478 100644 --- a/assets/martian.tweak.toml +++ b/assets/martian.tweak.toml @@ -14,13 +14,6 @@ main = "/Music/Main Track2" select = "/SFX/MenuSelect" hover = "/SFX/MenuHover" -### -# 2D Mode SFX -### -[audio.display2d] -pick_up = "/SFX/2D/2DPickUpPiece" -put_down = "/SFX/2D/2DPutDownPiece" - ### # 3D Mode SFX ### @@ -146,27 +139,4 @@ tonemapping = "ReinhardLuminance" exposure = 1.0 gamma = 1.0 pre_saturation = 1.0 -post_saturation = 1.0 - - -#################### -# Display2d settings -#################### -[display2d.sprites] -# Sprite file path inside the `assets` folder -file = "images/sprites.png" -# Size of each tile [x,y] (they have to be the same size) -tile_size_x = 16 -tile_size_y = 16 -columns = 8 -rows = 1 -# The order of each sprite left-to-right, top-to-bottom -sprite_order = ["LightTile" - ,"DarkTile" - ,"RedQueen" - ,"RedDrone" - ,"RedPawn" - ,"BlueQueen" - ,"BlueDrone" - ,"BluePawn" - ] +post_saturation = 1.0 \ No newline at end of file diff --git a/src/audio.rs b/src/audio.rs index 5c3598e..3c1c431 100644 --- a/src/audio.rs +++ b/src/audio.rs @@ -74,22 +74,10 @@ fn audio_trigger( } AudioEvent::MenuHover => tweak.get::("audio_menu_hover").unwrap(), AudioEvent::MenuSelect => tweak.get::("audio_menu_select").unwrap(), - AudioEvent::PickUp => match state { - DisplayState::Display2d => tweak.get::("audio_display2d_pick_up").unwrap(), - DisplayState::Display3d => tweak.get::("audio_display3d_pick_up").unwrap(), - }, - AudioEvent::PutDown => match state { - DisplayState::Display2d => tweak.get::("audio_display2d_put_down").unwrap(), - DisplayState::Display3d => tweak.get::("audio_display3d_put_down").unwrap(), - }, - AudioEvent::Idle | AudioEvent::StopIdle => match state { - DisplayState::Display2d => tweak.get::("audio_display2d_idle").unwrap(), - DisplayState::Display3d => tweak.get::("audio_display3d_idle").unwrap(), - }, - AudioEvent::Invalid => match state { - DisplayState::Display2d => tweak.get::("audio_display2d_invalid").unwrap(), - DisplayState::Display3d => tweak.get::("audio_display3d_invalid").unwrap(), - }, + AudioEvent::PickUp => tweak.get::("audio_display3d_pick_up").unwrap(), + AudioEvent::PutDown => tweak.get::("audio_display3d_put_down").unwrap(), + AudioEvent::Idle | AudioEvent::StopIdle => tweak.get::("audio_display3d_idle").unwrap(), + AudioEvent::Invalid => tweak.get::("audio_display3d_invalid").unwrap(), }; // There is an event for this if !aud.is_empty() { diff --git a/src/debug.rs b/src/debug.rs index d25b07c..1e6f7be 100644 --- a/src/debug.rs +++ b/src/debug.rs @@ -19,6 +19,20 @@ impl Plugin for DebugPlugin { SystemInformationDiagnosticsPlugin::default(), )) .init_resource::() + .insert_resource( + GizmoConfig { + depth_bias: -0.1, + ..default() + }, + ) + .add_systems(Update, ( + aabb_gizmo, + )) + // Systems that run in the editor mode + .add_systems(Update, ( + selected_gizmo.run_if(any_with_component::()), + selected_position.run_if(any_with_component::()), + ).run_if(resource_exists::())) .add_systems(Startup, init_debug_ui) .add_systems( Update, @@ -141,10 +155,48 @@ fn camera_info(mut debug_infos: ResMut, cameras: Query<(&Camera, &Nam debug_infos.set("Cameras".into(), camera_names); } -fn gltf_info( - gltfs: Res>, +fn aabb_gizmo( + added: Query>, + mut removed: RemovedComponents, + selected: Query>, + active: Option>, + mut commands: Commands, ) { - gltfs.iter().for_each(|(_, gltf)| { - info!("Named animations: {:#?}", gltf.named_animations); - }); + added.iter().for_each(|e| { + commands.entity(e).insert(AabbGizmo { color: Some(Color::RED) }); + }); + removed.read().for_each(|e| { + commands.entity(e).remove::(); + }); + match active { + Some(_) => selected.iter().for_each(|e| { + commands.entity(e).insert(AabbGizmo { color: Some(Color::RED) }); + }), + None => selected.iter().for_each(|e| { + commands.entity(e).remove::(); + }), + } +} + +/// Draw a gizmo showing cardinal directions for a selected object +fn selected_gizmo( + selected: Query<&GlobalTransform, With>, + mut gizmos: Gizmos, +) { + selected.iter().for_each(|g| { + let s = g.translation(); + gizmos.ray(s, Vec3::X, Color::RED); + gizmos.ray(s, Vec3::Y, Color::GREEN); + gizmos.ray(s, Vec3::Z, Color::BLUE); + }); +} + +fn selected_position( + selected: Query<(Entity, &GlobalTransform), With>, + mut debug_info: ResMut, +) { + let val = selected.iter().map(|(e, gt)| { + format!("\n{:?} {:?}", e, gt.translation()) + }).collect::>().join(""); + debug_info.set("Position".into(), val); } \ No newline at end of file diff --git a/src/display2d.rs b/src/display2d.rs deleted file mode 100644 index f79dbad..0000000 --- a/src/display2d.rs +++ /dev/null @@ -1,404 +0,0 @@ -/// TODO: Pick up and move pieces! -/// TODO: Custom Asset: SpriteSheetAtlas Mapper -/// TODO: Handle Cursor! -use bevy::window::{PrimaryWindow, WindowResized}; - -use crate::{ - game::{Board, BoardIndex, Piece, Side, Tile}, - prelude::*, - tweak::Tweaks, -}; -use serde::Deserialize; - -const SCALE: f32 = 4.0; - -pub(crate) struct Display2dPlugin; - -impl Plugin for Display2dPlugin { - fn build(&self, app: &mut App) { - app.add_systems(Startup, (initialize_camera, set_background)) - .add_systems(OnExit(GameState::Loading), initialize_board) - .add_systems( - Update, - ( - move_piece - .run_if(in_state(GameState::Play)) - .run_if(in_state(DisplayState::Display2d)) - .run_if(any_with_component::()), - select - .run_if(in_state(GameState::Play)) - .run_if(in_state(DisplayState::Display2d)) - .run_if(|buttons: Res>| -> bool { - buttons.just_pressed(MouseButton::Left) - }), - update_background.run_if(on_event::()), - set_transform - .after(game::update_board) - .run_if(any_component_changed::), - sync_sprite.run_if(any_component_changed::), - load_spritesheet.run_if(on_event::>()), - // Set Sprite for Pieces - set_sprite.run_if(any_component_changed::), - // When tweakfile is updated - set_sprite.run_if(resource_exists_and_changed::()), - capture_piece.run_if(any_component_added::), - ), - ) - .add_systems(OnEnter(DisplayState::Display2d), activate::) - .add_systems(OnExit(DisplayState::Display2d), deactivate::) - .add_systems( - OnEnter(GameState::Play), - activate::.run_if(in_state(DisplayState::Display2d)), - ); - } -} - -/// Sprite sheet Resource for later reference -#[derive(Debug, Resource)] -struct SpriteSheet { - handle: Handle, -} - -/// Marker component for the 2d entitys -#[derive(Debug, Component)] -pub(crate) struct Display2d; - -#[derive(Debug, Component)] -struct BackgroundImage; - -/// All possible sprites -/// Necessary because individual components of Piece, Side, and Light/Dark are not homogeneous -#[derive(Debug, Deserialize, Component, Clone, PartialEq)] -pub(crate) enum GameSprite { - RedQueen, - RedDrone, - RedPawn, - BlueQueen, - BlueDrone, - BluePawn, - LightTile, - DarkTile, -} - -impl From<(game::Piece, game::Side)> for GameSprite { - fn from((piece, side): (game::Piece, game::Side)) -> GameSprite { - match (piece, side) { - (Piece::Queen, Side::A) => GameSprite::RedQueen, - (Piece::Drone, Side::A) => GameSprite::RedDrone, - (Piece::Pawn, Side::A) => GameSprite::RedPawn, - (Piece::Queen, Side::B) => GameSprite::BlueQueen, - (Piece::Drone, Side::B) => GameSprite::BlueDrone, - (Piece::Pawn, Side::B) => GameSprite::BluePawn, - } - } -} - -impl From for GameSprite { - fn from(tile: game::Tile) -> GameSprite { - match tile { - Tile::Light => GameSprite::LightTile, - Tile::Dark => GameSprite::DarkTile, - } - } -} - -/// STARTUP: Initialize 2d gameplay Camera -fn initialize_camera(mut commands: Commands) { - commands.spawn(( - Display2d, - DisplayState::Display2d, - Camera2dBundle { - camera: Camera { - is_active: false, - ..default() - }, - ..default() - }, - UiCameraConfig { show_ui: true }, - Name::new("2D Camera"), - )); -} - -/// STARTUP: Load sprite sheet and insert texture atlas -fn load_spritesheet( - mut texture_atlases: ResMut>, - tweaks: Res>, - mut commands: Commands, - tweaks_file: Res, -) { - info!("Loading spritesheet"); - let tweak = tweaks - .get(&tweaks_file.handle.clone()) - .expect("Load tweakfiles"); - let atlas = TextureAtlas::from_grid( - tweak - .get_handle_unchecked::("display2d_sprites_file") - .unwrap(), - Vec2::new( - tweak.get::("display2d_sprites_tile_size_x").unwrap(), - tweak.get::("display2d_sprites_tile_size_y").unwrap(), - ), - tweak.get::("display2d_sprites_columns").unwrap(), - tweak.get::("display2d_sprites_rows").unwrap(), - None, - None, - ); - commands.insert_resource(SpriteSheet { - handle: texture_atlases.add(atlas), - }); -} - -fn set_background( - server: Res, - mut commands: Commands, - window: Query<&Window, With>, -) { - commands.spawn(( - BackgroundImage, - Display2d, - SpriteBundle { - texture: server.load("images/mars-daybreak.png"), - sprite: Sprite { - custom_size: Some(Vec2 { - x: window.single().width(), - y: window.single().height(), - }), - ..default() - }, - transform: Transform { - translation: Vec3::NEG_Z, - ..default() - }, - visibility: Visibility::Hidden, - ..default() - }, - )); -} - -fn update_background( - mut sprites: Query<&mut Sprite, With>, - mut events: EventReader, -) { - events - .read() - .for_each(|&WindowResized { width, height, .. }| { - sprites.iter_mut().for_each(|mut sprite| { - sprite.custom_size = Some(Vec2 { - x: width, - y: height, - }); - }); - }); -} - -/// STARTUP: Initialize the board for representation -fn initialize_board(board: Option>, mut commands: Commands) { - if let Some(board) = board { - commands - .spawn(( - Display2d, - SpatialBundle { - visibility: Visibility::Hidden, - ..default() - }, - )) - .with_children(|parent| { - // Spawn tiles - game::tiles().for_each(|(index, tile)| { - let game_sprite: GameSprite = tile.clone().into(); - - parent.spawn(( - game_sprite, - tile.clone(), - index, - Display2d, - SpriteSheetBundle { ..default() }, - game::Selectable, - )); - }); - - // Spawn pieces - board.pieces().iter().for_each(|(index, piece)| { - let side = Board::side(*index).expect("Spawn valid side"); - - let game_sprite: GameSprite = (*piece, side).into(); - - parent.spawn(( - game_sprite, - piece.clone(), - Display2d, - index.clone(), - side.clone(), - SpriteSheetBundle { ..default() }, - game::Selectable, - )); - }); - }); - } -} - -fn sync_sprite(mut events: Query<(&mut GameSprite, &Piece, &Side), Changed>) { - events - .iter_mut() - .for_each(|(mut game_sprite, piece, side)| { - *game_sprite = (*piece, *side).into(); - }); -} - -fn set_sprite( - mut entities: Query< - ( - &mut TextureAtlasSprite, - &mut Handle, - &GameSprite, - ), - With, - >, - sprite_sheet: Option>, - tweaks: Res>, - tweaks_file: Res, -) { - let tweak = tweaks - .get(tweaks_file.handle.clone()) - .expect("Load tweaksfile in set piece sprite"); - let sprite_sheet = sprite_sheet.expect("Sprite sheet"); - entities - .iter_mut() - .for_each(|(mut sprite, mut texture_atlas, game_sprite)| { - if let Some(index) = tweak - .get::>("display2d_sprites_sprite_order") - .unwrap() - .iter() - .position(|s| s == game_sprite) - { - if *texture_atlas != sprite_sheet.handle { - *texture_atlas = sprite_sheet.handle.clone(); - } - sprite.index = index; - } - }); -} - -/// Sets a piece location given it's board index -fn set_transform( - mut events: Query< - (&mut Transform, &BoardIndex, Option<&Piece>, Option<&Tile>), - ( - With, - Or<(Changed, Added)>, - ), - >, -) { - events.iter_mut().for_each(|(mut t, i, piece, tile)| { - let x = SCALE * 16.0 * ((i.x as f32) - 3.5); - let y = SCALE * 16.0 * ((i.y as f32) - 1.5); - let z = match (piece, tile) { - (None, None) | (Some(_), Some(_)) => { - error!("Entity with BoardIndex is neither a Piece nor a Tile!"); - 0.0 - } - // Piece - (Some(_), None) => 2.0, - // Tile - (None, Some(_)) => 1.0, - }; - *t = Transform::from_scale(Vec3::splat(SCALE)).with_translation(Vec3::new(x, y, z)); - debug!("setting position of {:?} to {:?}", i, t); - }); -} - -/// Select pieces and tiles in 2d -fn select( - candidates: Query< - ( - Entity, - &TextureAtlasSprite, - &Handle, - &GlobalTransform, - &BoardIndex, - ), - ( - With, - Without, - With, - ), - >, - windows: Query<&Window, With>, - cameras: Query<(&Camera, &GlobalTransform), With>, - atlases: Res>, - mut selections: EventWriter, -) { - // For each window (there is only one) - windows - .iter() - .filter_map(|window| window.cursor_position()) - .for_each(|position| { - // For each 2d Camera (there is only one) - cameras - .iter() - .filter_map(|(camera, transform)| camera.viewport_to_world_2d(transform, position)) - .for_each(|pos| { - // For each selectable sprite (there are many) - // Filter down to the list of hit objects - candidates - .iter() - .filter_map( - |( - _, - TextureAtlasSprite { index, anchor, .. }, - handle, - transform, - board_index, - )| { - let sprite_size = atlases - .get(handle) - .map(|atlas| { - atlas.textures.get(*index).expect("Atlas Sprite Texture") - }) - .expect("Atlas Sprite Rectangle") - .size(); - hit::intersects2d(sprite_size, anchor, transform, pos) - .then_some((transform, board_index)) - }, - ) - .max_by(|(a, _), (b, _)| { - a.translation().z.partial_cmp(&b.translation().z).unwrap() - }) - .map(|(_, board_index)| board_index) - .iter() - .for_each(|&board_index| { - selections.send(game::Selection(board_index.clone())); - }); - }); - }); -} - -fn move_piece( - window: Query<&Window, With>, - mut query: Query<&mut Transform, (With, With, With)>, - camera_query: Query<(&Camera, &GlobalTransform), With>, -) { - query.iter_mut().for_each(|mut t| { - let (camera, camera_t) = camera_query.single(); - if let Some(pos) = window - .single() - .cursor_position() - .and_then(|cursor| camera.viewport_to_world_2d(camera_t, cursor)) - { - t.translation.x = pos.x; - t.translation.y = pos.y; - } - }) -} - -/// When a piece is captured, we make it invisible in 2D -fn capture_piece( - mut events: Query<(Entity, &mut Visibility), (With, Added)>, - mut commands: Commands, -) { - events.iter_mut().for_each(|(entity, mut vis)| { - info!("Hiding captured piece"); - *vis = Visibility::Hidden; - commands.entity(entity).remove::(); - }); -} \ No newline at end of file diff --git a/src/display3d.rs b/src/display3d.rs index df87a1e..82fd9e2 100644 --- a/src/display3d.rs +++ b/src/display3d.rs @@ -426,14 +426,6 @@ fn set_board_model( With, ), >, - mut tiles: Query< - &mut Handle, - ( - With, - Without, - With, - ), - >, gltfs: Res>, tweaks: Res>, tweaks_file: Res, diff --git a/src/editor.rs b/src/editor.rs deleted file mode 100644 index 7e26010..0000000 --- a/src/editor.rs +++ /dev/null @@ -1,97 +0,0 @@ -use bevy::input::keyboard::KeyboardInput; - -use crate::prelude::*; - -pub struct EditorPlugin; - -impl Plugin for EditorPlugin { - fn build(&self, app: &mut App) { - app.add_systems(Startup, init_editor) - .insert_resource( - GizmoConfig { - depth_bias: -0.1, - ..default() - }, - ) - .add_systems(Update, ( - toggle_editor.run_if(on_event::()), - aabb_gizmo, - )) - // Systems that run in the editor mode - .add_systems(Update, ( - selected_gizmo.run_if(any_with_component::()), - selected_position.run_if(any_with_component::()), - ).run_if(resource_exists::())); - } -} - -#[derive(Debug, Resource)] -struct EditorActive; - -fn init_editor( - mut commands: Commands, -) { - info!("Starting editor"); -} - -fn toggle_editor( - mut events: EventReader, - active: Option>, - mut commands: Commands, -) { - events - .read() - .filter(|KeyboardInput { - state, key_code, .. - }| *state == ButtonState::Pressed && *key_code == Some(KeyCode::F3)) - .for_each(|_| match active { - Some(_) => commands.remove_resource::(), - None => commands.insert_resource(EditorActive), - }); -} - -fn aabb_gizmo( - added: Query>, - mut removed: RemovedComponents, - selected: Query>, - active: Option>, - mut commands: Commands, -) { - added.iter().for_each(|e| { - commands.entity(e).insert(AabbGizmo { color: Some(Color::RED) }); - }); - removed.read().for_each(|e| { - commands.entity(e).remove::(); - }); - match active { - Some(_) => selected.iter().for_each(|e| { - commands.entity(e).insert(AabbGizmo { color: Some(Color::RED) }); - }), - None => selected.iter().for_each(|e| { - commands.entity(e).remove::(); - }), - } -} - -/// Draw a gizmo showing cardinal directions for a selected object -fn selected_gizmo( - selected: Query<(Entity, &Transform, &GlobalTransform), With>, - mut gizmos: Gizmos, -) { - selected.iter().for_each(|(e, t, g)| { - let s = g.translation(); - gizmos.ray(s, Vec3::X, Color::RED); - gizmos.ray(s, Vec3::Y, Color::GREEN); - gizmos.ray(s, Vec3::Z, Color::BLUE); - }); -} - -fn selected_position( - selected: Query<(Entity, &GlobalTransform), With>, - mut debug_info: ResMut, -) { - let val = selected.iter().map(|(e, gt)| { - format!("\n{:?} {:?}", e, gt.translation()) - }).collect::>().join(""); - debug_info.set("Position".into(), val); -} \ No newline at end of file diff --git a/src/game.rs b/src/game.rs index a8c3eb1..7d12863 100644 --- a/src/game.rs +++ b/src/game.rs @@ -34,11 +34,7 @@ impl Plugin for GamePlugin { ) .add_systems( PreUpdate, - ( - asserts::.run_if(in_state(DisplayState::Display2d)), - asserts::.run_if(in_state(DisplayState::Display3d)), - ) - .run_if(in_state(GameState::Play)), + asserts::.run_if(in_state(DisplayState::Display3d)).run_if(in_state(GameState::Play)), ) .add_systems( PostUpdate, @@ -481,13 +477,13 @@ fn debug_board(board: Res, mut debug_info: ResMut) { pub(crate) fn update_board( mut audio_events: EventWriter, mut events: EventReader, - mut pieces: Query<(Entity, &mut BoardIndex, &Side), With>, + mut pieces: Query<(Entity, &mut BoardIndex), With>, selected: Query>, mut commands: Commands, mut played: Local, ) { events.read().for_each(|Move { from, to, .. }| { - pieces.iter_mut().for_each(|(entity, mut index, side)| { + pieces.iter_mut().for_each(|(entity, mut index)| { if *index == *from { match to { Some(to_idx) => { diff --git a/src/hit.rs b/src/hit.rs index 442bd01..3dd7032 100644 --- a/src/hit.rs +++ b/src/hit.rs @@ -3,7 +3,6 @@ use bevy::{ mesh::{MeshVertexAttribute, VertexAttributeValues}, render_resource::VertexFormat, }, - sprite::Anchor, }; use crate::prelude::*; @@ -123,24 +122,3 @@ pub(crate) fn intersects3d(ray: &Ray, mesh: &Mesh, gt: &GlobalTransform) -> Opti None } } - -pub(crate) fn intersects2d( - sprite_size: Vec2, - anchor: &Anchor, - transform: &GlobalTransform, - pos: Vec2, -) -> bool { - // Implementation credit goes to the sprite bevy_mod_picking backend - // TODO: Upstream changes - let p = transform.translation(); - - let size = { - let (transform_scale, _, _) = transform.to_scale_rotation_translation(); - sprite_size * transform_scale.truncate() - }; - - let center = p.truncate() - (anchor.as_vec() * size); - let rect = Rect::from_center_half_size(center, size / 2.0); - - rect.contains(pos) -} diff --git a/src/main.rs b/src/main.rs index 529c5d3..c773d2e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,9 +6,7 @@ mod audio; mod credits; mod debug; -mod display2d; mod display3d; -mod editor; mod game; mod hit; mod loading; @@ -60,9 +58,7 @@ fn main() { ); app.add_plugins(credits::CreditsPlugin); app.add_plugins(debug::DebugPlugin); - app.add_plugins(display2d::Display2dPlugin); app.add_plugins(display3d::Display3dPlugin); - app.add_plugins(editor::EditorPlugin); app.add_plugins(game::GamePlugin); app.add_plugins(loading::LoadingPlugin); app.add_plugins(menu::MenuPlugin); @@ -83,8 +79,8 @@ pub enum GameState { #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default, States, Component)] pub(crate) enum DisplayState { - #[default] Display2d, + #[default] Display3d, } diff --git a/src/ui.rs b/src/ui.rs index e3ed8c1..11f7958 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -6,18 +6,9 @@ pub(crate) struct UiPlugin; impl Plugin for UiPlugin { fn build(&self, app: &mut App) { - app.add_systems(Startup, initialize); - app.add_systems(OnEnter(GameState::Menu), sync_display_mode); app.add_systems( Update, ( - toggle_display_mode.run_if(any_component_changed::), - sync_display_mode - .run_if(state_changed::()) - .run_if(in_state(GameState::Menu)), - sync_display_mode - .run_if(state_changed::()) - .run_if(in_state(GameState::Play)), manage_cursor.run_if(any_component_changed::), interactive_button.run_if(any_component_changed::), ), @@ -28,79 +19,6 @@ impl Plugin for UiPlugin { #[derive(Debug, Component)] struct UiRoot; -fn initialize(mut commands: Commands) { - commands - .spawn(( - NodeBundle { - style: Style { - position_type: PositionType::Absolute, - right: Val::Px(5.0), - bottom: Val::Px(5.0), - ..default() - }, - background_color: Color::WHITE.into(), - visibility: Visibility::Hidden, - ..default() - }, - UiRoot, - )) - .with_children(|parent| { - parent.spawn(( - ButtonBundle { - style: Style { - width: Val::Px(25.0), - height: Val::Px(25.0), - ..default() - }, - background_color: Color::WHITE.into(), - ..default() - }, - DisplayState::Display2d, - )); - }); -} - -/// When button is clicked, switch display state to that state and hide the other option. -/// Display2d -> Click -> Display2d mode & Display3d butto -fn toggle_display_mode( - events: Query<(&Interaction, &DisplayState), (Changed, With