diff --git a/README.md b/README.md index c511357..7f5e528 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,18 @@ # 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 new file mode 100644 index 0000000..7306325 Binary files /dev/null and b/assets/Cantarell-VF.otf differ diff --git a/assets/UacariLegacy-Thin.ttf b/assets/UacariLegacy-Thin.ttf deleted file mode 100644 index c3de821..0000000 Binary files a/assets/UacariLegacy-Thin.ttf and /dev/null differ diff --git a/src/game.rs b/src/game.rs index b85a065..fb625c9 100644 --- a/src/game.rs +++ b/src/game.rs @@ -1,21 +1,17 @@ #![allow(clippy::precedence)] -#![allow(clippy::too_many_arguments)] use crate::AppState; use bevy::{ input::{keyboard::KeyCode, Input}, prelude::{shape::Quad, *}, - sprite::Mesh2dHandle, + sprite::{MaterialMesh2dBundle, 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 { @@ -24,29 +20,24 @@ 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(crate::levels::post_setup_level) + .with_system(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 -pub struct LevelStartupEvent(pub Entity); +struct LevelStartupEvent(Entity); // Resources -pub struct CurrentLevel(pub Option); +struct CurrentLevel(Option); -pub struct CharacterMeshes { +struct CharacterMeshes { square: Mesh2dHandle, } @@ -66,20 +57,17 @@ impl FromWorld for CharacterMeshes { // Components -#[derive(Component)] -pub struct Level; +#[derive(Clone, Component, Copy, Eq, Hash, PartialEq)] +struct LevelId(u32); #[derive(Clone, Component, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub struct CharacterId(pub u32); +struct CharacterId(u32); #[derive(Clone, Component, Copy, Eq, Hash, PartialEq)] -pub struct SelectedCharacterId(pub Option); +struct SelectedCharacterId(Option); #[derive(Component)] -pub struct CharacterIdList(pub BTreeSet); - -#[derive(Clone, Component, PartialEq)] -pub struct CharacterColor(pub Color); +struct CharacterIdList(BTreeSet); // Systems @@ -88,16 +76,69 @@ fn setup( mut current_level: ResMut, mut level_startup_event: EventWriter, ) { - 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, - ); + 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)); } -pub fn spawn_character( +// 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( commands: &mut Commands, character_meshes: &Res, effects: &mut ResMut>, @@ -133,22 +174,20 @@ pub fn spawn_character( .clamp(Vec4::new(0., 0., 0., 0.), Vec4::new(1., 1., 1., 0.)), ); commands - .spawn_bundle(ColorMesh2dBundle { + .spawn_bundle(MaterialMesh2dBundle { 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(0.8)) + .insert(Friction::new(1.0)) .insert(Damping { linear_damping: 0.5, angular_damping: 0.5, @@ -192,50 +231,31 @@ pub fn spawn_character( fn collision_event_system( mut commands: Commands, - character_meshes: Res, - mut materials: ResMut>, - mut effects: ResMut>, + //materials: ResMut>, + current_level: Res, mut collision_events: EventReader, - character_query: Query<(&CharacterId, &CharacterColor, &Transform)>, + //character_query: Query<(&CharacterId, &Handle)>, + character_query: Query<&CharacterId>, mut level_query: Query<(&mut SelectedCharacterId, &mut CharacterIdList)>, - mut app_state: ResMut>, ) { - 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; - - // 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 + 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)) { - app_state.replace(AppState::Win).ok(); - } + //c1_material.color = (Vec4::from(c1_material.color) + Vec4::from(c2_material.color)).into(); - spawn_character( - &mut commands, - &character_meshes, - &mut effects, - &mut materials, - &mut selected_character_id, - &mut character_id_list, - *c1_transform, - new_color.into(), - ); + 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(); + } } } } @@ -244,9 +264,10 @@ 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, @@ -255,92 +276,100 @@ fn keyboard_input_system( mut effect: Query<&mut ParticleEffect>, dsp_assets: Res, audio: Res