diff --git a/README.md b/README.md index 7f5e528..c511357 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,21 @@ # Bevyjam +## Controls + +* **Move**: arrows +* **Switch character**: Tab +* **Level up**: Enter (when character is white) + ## TODO * name -* scene management * stream audio (with HexoSynthDSP) -* character fusion * color filters * level design -* win -* menu GUI * (?) can jump only from a surface (no mid-air jump) * (?) multiplayer * make WASM build work again (replace hanabi) +* level reset ## Build diff --git a/assets/Cantarell-VF.otf b/assets/Cantarell-VF.otf deleted file mode 100644 index 7306325..0000000 Binary files a/assets/Cantarell-VF.otf and /dev/null differ diff --git a/assets/UacariLegacy-Thin.ttf b/assets/UacariLegacy-Thin.ttf new file mode 100644 index 0000000..c3de821 Binary files /dev/null and b/assets/UacariLegacy-Thin.ttf differ diff --git a/src/game.rs b/src/game.rs index fb625c9..b85a065 100644 --- a/src/game.rs +++ b/src/game.rs @@ -1,17 +1,21 @@ #![allow(clippy::precedence)] +#![allow(clippy::too_many_arguments)] use crate::AppState; use bevy::{ input::{keyboard::KeyCode, Input}, prelude::{shape::Quad, *}, - sprite::{MaterialMesh2dBundle, Mesh2dHandle}, + sprite::Mesh2dHandle, }; use bevy_fundsp::prelude::*; use bevy_hanabi::*; use bevy_rapier2d::prelude::*; use std::collections::BTreeSet; +#[derive(Clone, Copy, Eq, Hash, PartialEq)] +pub struct LevelId(pub u32); + pub struct GamePlugin; impl Plugin for GamePlugin { @@ -20,24 +24,29 @@ impl Plugin for GamePlugin { .init_resource::() .insert_resource(CurrentLevel(None)) .add_system_set(SystemSet::on_enter(AppState::Game).with_system(setup)) + .add_system_set(SystemSet::on_enter(AppState::Win).with_system(win_setup)) + .add_system_set( + SystemSet::on_exit(AppState::Win).with_system(crate::levels::despawn_level), + ) .add_system_set( SystemSet::on_update(AppState::Game) - .with_system(post_setup_level) + .with_system(crate::levels::post_setup_level) .with_system(keyboard_input_system), ) + .add_system_set(SystemSet::on_update(AppState::Win).with_system(keyboard_input_system)) .add_system_to_stage(CoreStage::PostUpdate, collision_event_system); } } // Events -struct LevelStartupEvent(Entity); +pub struct LevelStartupEvent(pub Entity); // Resources -struct CurrentLevel(Option); +pub struct CurrentLevel(pub Option); -struct CharacterMeshes { +pub struct CharacterMeshes { square: Mesh2dHandle, } @@ -57,17 +66,20 @@ impl FromWorld for CharacterMeshes { // Components -#[derive(Clone, Component, Copy, Eq, Hash, PartialEq)] -struct LevelId(u32); +#[derive(Component)] +pub struct Level; #[derive(Clone, Component, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)] -struct CharacterId(u32); +pub struct CharacterId(pub u32); #[derive(Clone, Component, Copy, Eq, Hash, PartialEq)] -struct SelectedCharacterId(Option); +pub struct SelectedCharacterId(pub Option); #[derive(Component)] -struct CharacterIdList(BTreeSet); +pub struct CharacterIdList(pub BTreeSet); + +#[derive(Clone, Component, PartialEq)] +pub struct CharacterColor(pub Color); // Systems @@ -76,69 +88,16 @@ fn setup( mut current_level: ResMut, mut level_startup_event: EventWriter, ) { - let level_entity = commands - .spawn() - .insert(LevelId(0)) - .insert(SelectedCharacterId(None)) - .insert(CharacterIdList(BTreeSet::new())) - .id(); - current_level.0 = Some(level_entity); - - commands - .spawn_bundle(TransformBundle::from(Transform::from_xyz(0.0, -256.0, 0.0))) - .insert(Collider::cuboid(400., 10.)); - - level_startup_event.send(LevelStartupEvent(level_entity)); + let level_id = LevelId(current_level.0.map_or(0, |level_id| level_id.0 + 1)); + crate::levels::setup_level( + &mut commands, + &mut current_level, + &mut level_startup_event, + level_id, + ); } -// This is a bad design, but it's the only way I found -fn post_setup_level( - mut commands: Commands, - character_meshes: Res, - mut effects: ResMut>, - mut materials: ResMut>, - mut level_query: Query<(&mut SelectedCharacterId, &mut CharacterIdList)>, - mut level_startup_event: EventReader, -) { - for LevelStartupEvent(level_entity) in level_startup_event.iter() { - if let Ok((mut selected_character_id, mut character_id_list)) = - level_query.get_mut(*level_entity) - { - spawn_character( - &mut commands, - &character_meshes, - &mut effects, - &mut materials, - &mut selected_character_id, - &mut character_id_list, - Transform::from_xyz(-128., -64., 0.), - Color::RED, - ); - spawn_character( - &mut commands, - &character_meshes, - &mut effects, - &mut materials, - &mut selected_character_id, - &mut character_id_list, - Transform::from_xyz(0., -64., 0.), - Color::GREEN, - ); - spawn_character( - &mut commands, - &character_meshes, - &mut effects, - &mut materials, - &mut selected_character_id, - &mut character_id_list, - Transform::from_xyz(128., -64., 0.), - Color::BLUE, - ); - } - } -} - -fn spawn_character( +pub fn spawn_character( commands: &mut Commands, character_meshes: &Res, effects: &mut ResMut>, @@ -174,20 +133,22 @@ fn spawn_character( .clamp(Vec4::new(0., 0., 0., 0.), Vec4::new(1., 1., 1., 0.)), ); commands - .spawn_bundle(MaterialMesh2dBundle { + .spawn_bundle(ColorMesh2dBundle { mesh: character_meshes.square.clone(), material: materials.add(ColorMaterial::from(color)), transform, ..default() }) + .insert(Level) .insert(character_id) + .insert(CharacterColor(color)) .insert(RigidBody::Dynamic) .insert(Collider::cuboid(32., 32.)) .insert(ExternalForce::default()) .insert(Velocity::default()) .insert(GravityScale(10.0)) .insert(LockedAxes::ROTATION_LOCKED) - .insert(Friction::new(1.0)) + .insert(Friction::new(0.8)) .insert(Damping { linear_damping: 0.5, angular_damping: 0.5, @@ -231,31 +192,50 @@ fn spawn_character( fn collision_event_system( mut commands: Commands, - //materials: ResMut>, - current_level: Res, + character_meshes: Res, + mut materials: ResMut>, + mut effects: ResMut>, mut collision_events: EventReader, - //character_query: Query<(&CharacterId, &Handle)>, - character_query: Query<&CharacterId>, + character_query: Query<(&CharacterId, &CharacterColor, &Transform)>, mut level_query: Query<(&mut SelectedCharacterId, &mut CharacterIdList)>, + mut app_state: ResMut>, ) { - if let Some(level_entity) = current_level.0 { - for collision_event in collision_events.iter() { - if let CollisionEvent::Started(e1, e2, flags) = collision_event { - if flags.is_empty() { - //if let (Ok((c1_id, c1_material)), Ok((c2_id, c2_material))) = - if let (Ok(c1_id), Ok(c2_id)) = - (character_query.get(*e1), character_query.get(*e2)) - { - //c1_material.color = (Vec4::from(c1_material.color) + Vec4::from(c2_material.color)).into(); + for collision_event in collision_events.iter() { + if let CollisionEvent::Started(e1, e2, flags) = collision_event { + if flags.is_empty() { + if let (Ok((c1_id, c1_color, c1_transform)), Ok((c2_id, c2_color, _c2_transform))) = + (character_query.get(*e1), character_query.get(*e2)) + { + let (mut selected_character_id, mut character_id_list) = + level_query.single_mut(); + character_id_list.0.remove(c1_id); + character_id_list.0.remove(c2_id); + selected_character_id.0 = None; - let (mut selected_character_id, mut character_id_list) = - level_query.get_mut(level_entity).unwrap(); - character_id_list.0.remove(c2_id); - if selected_character_id.0 == Some(*c2_id) { - selected_character_id.0 = Some(*c1_id); - } - commands.entity(*e2).despawn_recursive(); + // TODO completely remove particles + commands.entity(*e1).despawn_recursive(); + commands.entity(*e2).despawn_recursive(); + + let new_color = (Vec4::from(c1_color.0) + Vec4::from(c2_color.0)) + .clamp(Vec4::ZERO, Vec4::ONE); + + // If color approximately white + if app_state.current() == &AppState::Game + && 4. - new_color.length_squared() < 0.1 + { + app_state.replace(AppState::Win).ok(); } + + spawn_character( + &mut commands, + &character_meshes, + &mut effects, + &mut materials, + &mut selected_character_id, + &mut character_id_list, + *c1_transform, + new_color.into(), + ); } } } @@ -264,10 +244,9 @@ fn collision_event_system( fn keyboard_input_system( keyboard_input: Res>, - current_level: Res, mut characters: Query<( &CharacterId, - &mut Velocity, + &mut Velocity, &mut ExternalImpulse, &mut ExternalForce, &Children, @@ -276,100 +255,92 @@ fn keyboard_input_system( mut effect: Query<&mut ParticleEffect>, dsp_assets: Res, audio: Res