diff --git a/Cargo.lock b/Cargo.lock index 4a6e782..126b32c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -923,26 +923,6 @@ dependencies = [ "winit", ] -[[package]] -name = "bevyjam" -version = "0.1.0" -dependencies = [ - "bevy", - "bevy-inspector-egui", - "bevy_common_assets", - "bevy_mod_picking", - "bevy_rapier2d", - "cpal 0.14.0", - "crossbeam-channel", - "hexodsp", - "rand", - "rand_distr", - "rapier2d", - "serde", - "serde_json", - "ticktock", -] - [[package]] name = "bindgen" version = "0.59.2" @@ -1333,6 +1313,7 @@ dependencies = [ "parking_lot 0.12.1", "stdweb", "thiserror", + "wasm-bindgen", "web-sys", "windows", ] @@ -2246,6 +2227,26 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "lux-synthese" +version = "0.1.0" +dependencies = [ + "bevy", + "bevy-inspector-egui", + "bevy_common_assets", + "bevy_mod_picking", + "bevy_rapier2d", + "cpal 0.14.0", + "crossbeam-channel", + "hexodsp", + "rand", + "rand_distr", + "rapier2d", + "serde", + "serde_json", + "ticktock", +] + [[package]] name = "mach" version = "0.3.2" diff --git a/Cargo.toml b/Cargo.toml index 3eac3b7..fd3c589 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "bevyjam" +name = "lux-synthese" version = "0.1.0" authors = ["tuxmain "] license = "AGPL-3.0-only" @@ -24,6 +24,7 @@ serde_json = "1.0.85" ticktock = "0.8.0" [target."cfg(target_arch = \"wasm32\")".dependencies] +cpal = { version = "0.14.0", features = ["wasm-bindgen"] } [profile.dev.package."*"] opt-level = 3 diff --git a/README.md b/README.md index b53f02d..d46aca3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Bevyjam +# Lux synthesĕ ## Controls @@ -9,17 +9,14 @@ ## TODO -* name * more filters * despawn black characters * despawn character when too far * more levels * (?) multiplayer * more audio -* "jumpable" component to avoid jumping on sensors * bug: in level2, move the blue character to win, then reset. The characters are lighter than expected. (also level 4) * redshift warning -* itchio test ## Build @@ -66,6 +63,6 @@ Edit the level `N: u32` with the command `bevyjam e`. GNU AGPL v3, CopyLeft 2022 Pascal Engélibert, Nixon Cheng -This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3 of the License. -This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. -You should have received a copy of the GNU Affero General Public License along with this program. If not, see https://www.gnu.org/licenses/. +_Lux synthesĕ_ is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3 of the License. +_Lux synthesĕ_ is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. +You should have received a copy of the GNU Affero General Public License along with _Lux synthesĕ_. If not, see https://www.gnu.org/licenses/. diff --git a/build-itchio.sh b/build-itchio.sh new file mode 100644 index 0000000..62759b7 --- /dev/null +++ b/build-itchio.sh @@ -0,0 +1,12 @@ +sh build-wasm.sh || exit 1 + +mkdir -p target/itchio/wasm/target +mkdir -p target/itchio/wasm/assets + +cp assets/* target/itchio/wasm/assets/ +cp index.html target/itchio/wasm/ +cp target/lux-synthese.js target/itchio/wasm/target/ +cp target/lux-synthese_bg.wasm target/itchio/wasm/target/ + +cd target/itchio/wasm +zip -r ../wasm.zip . diff --git a/build-wasm.sh b/build-wasm.sh index 0a2d039..793798d 100644 --- a/build-wasm.sh +++ b/build-wasm.sh @@ -1,3 +1,3 @@ cargo build --release --target wasm32-unknown-unknown || exit 1 echo "==> wasm-bindgen..." -wasm-bindgen --out-name bevyjam --out-dir target --target web target/wasm32-unknown-unknown/release/bevyjam.wasm || exit 1 +wasm-bindgen --out-name lux-synthese --out-dir target --target web target/wasm32-unknown-unknown/release/lux-synthese.wasm || exit 1 diff --git a/cover.png b/cover.png new file mode 100644 index 0000000..282923f Binary files /dev/null and b/cover.png differ diff --git a/cover.xcf b/cover.xcf new file mode 100644 index 0000000..9a7f4b5 Binary files /dev/null and b/cover.xcf differ diff --git a/index.html b/index.html index 3b956aa..f36746d 100644 --- a/index.html +++ b/index.html @@ -2,12 +2,35 @@ - Bevyjam + Lux synthesĕ +
+

Lux synthesĕ

+

+ Note: audio does not work in the WASM build. +

+

Controls

+
    +
  • Move: Arrows
  • +
  • Switch: Tab
  • +
  • Level up: Enter
  • +
  • Reset: R
  • +
+

Source

+

+ The source code of this free software is available in our Git repository. +

+

+ GNU AGPL v3: CopyLeft 2022 Pascal Engélibert, Nixon Cheng
+ Lux synthesĕ is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3 of the License.
+ Lux synthesĕ is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+ You should have received a copy of the GNU Affero General Public License along with Lux synthesĕ. If not, see https://www.gnu.org/licenses/. +

+
diff --git a/src/game.rs b/src/game.rs index b5c803e..0816bfa 100644 --- a/src/game.rs +++ b/src/game.rs @@ -57,7 +57,13 @@ impl Plugin for GamePlugin { .with_system(character_particle_effect_system) .with_system(move_win_text_system), ) - .add_system_to_stage(CoreStage::PostUpdate, collision_event_system); + .add_system_to_stage(CoreStage::PostUpdate, char_char_collision_event_system) + .add_system_to_stage(CoreStage::PostUpdate, char_platform_collision_event_system) + // collision event system might remove items, therefore, we should detect platforms first before removing them + .add_system_to_stage( + CoreStage::PostUpdate, + collision_event_system.after(char_platform_collision_event_system), + ); } } @@ -102,7 +108,28 @@ pub struct CharacterColor(pub Color); pub struct Player; #[derive(Component)] -pub struct CollisionCount(usize); +pub struct Platform; + +#[derive(Component)] +pub struct PlatformCount(usize); + +impl PlatformCount { + fn increment(&mut self) { + self.0 += 1; + } + + fn decrement(&mut self) { + self.0 = self.0.saturating_sub(1); + } + + fn reset(&mut self) { + self.0 = 0; + } + + fn is_landed(&self) -> bool { + self.0 != 0 + } +} #[derive(Component)] pub struct Melty(pub Color); @@ -190,7 +217,7 @@ pub fn spawn_character( .insert(Sensor) .insert(Collider::cuboid(30., 0.5)) .insert(ActiveEvents::COLLISION_EVENTS) - .insert(CollisionCount(0)); + .insert(PlatformCount(0)); }); character_list.0.insert(entity_commands.id()); @@ -231,7 +258,8 @@ pub fn spawn_platform( ..default() }) .insert(Collider::cuboid(size.x / 2., size.y / 2.)) - .insert(Level); + .insert(Level) + .insert(Platform); } pub fn spawn_melty_platform( @@ -258,6 +286,7 @@ pub fn spawn_melty_platform( .insert(Collider::cuboid(48., 8.)) .insert(Melty(color)) .insert(Level) + .insert(Platform) .with_children(|c| { c.spawn_bundle(SpriteBundle { texture: asset_server.get_handle("melty.png"), @@ -267,9 +296,109 @@ pub fn spawn_melty_platform( }); } +fn char_char_collision_event_system( + mut commands: Commands, + + mut collision_events: EventReader, + character_query: Query<( + &CharacterColor, + &Transform, + Option<&Player>, + )>, + + mut character_list: ResMut, + mut app_state: ResMut>, + character_meshes: Res, + audio: Res>, + mut materials: ResMut>, +) { + for collision_event in collision_events.iter() { + if let CollisionEvent::Started(e1, e2, _flags) = collision_event { + if let ( + Ok((c1_color, c1_transform, c1_player)), + Ok((c2_color, c2_transform, c2_player)), + ) = (character_query.get(*e1), character_query.get(*e2)) + { + character_list.0.remove(e1); + character_list.0.remove(e2); + 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 && new_color.min_element() >= 0.9 { + app_state.replace(AppState::Win).ok(); + } + + // position character based on current player location + spawn_character( + &mut commands, + &character_meshes, + &mut materials, + &audio, + &mut character_list, + if c1_player.is_some() { + *c1_transform + } else if c2_player.is_some() { + *c2_transform + } else { + Transform::identity().with_translation( + (c1_transform.translation + c2_transform.translation) * 0.5, + ) + }, + new_color.into(), + c1_player.is_some() || c2_player.is_some(), + ); + + audio.send(AudioMsg::Fusion).ok(); + } + } + } +} + +fn char_platform_collision_event_system( + mut collision_events: EventReader, + mut platform_count_query: Query<&mut PlatformCount>, + platform_query: Query<&Platform>, +) { + // detect platform + player collisions only + for collision_event in collision_events.iter() { + match collision_event { + CollisionEvent::Started(e1, e2, flags) => { + if *flags == CollisionEventFlags::SENSOR { + if let (Ok(mut platform_count), Ok(_)) = + (platform_count_query.get_mut(*e1), platform_query.get(*e2)) + { + platform_count.increment(); + } else if let (Ok(mut platform_count), Ok(_)) = + (platform_count_query.get_mut(*e2), platform_query.get(*e1)) + { + platform_count.increment(); + } + } + } + + CollisionEvent::Stopped(e1, e2, flags) => { + if *flags == CollisionEventFlags::SENSOR { + if let (Ok(mut platform_count), Ok(_)) = + (platform_count_query.get_mut(*e1), platform_query.get(*e2)) + { + platform_count.decrement(); + } else if let (Ok(mut platform_count), Ok(_)) = + (platform_count_query.get_mut(*e2), platform_query.get(*e1)) + { + platform_count.decrement(); + } + } + } + } + } +} + fn collision_event_system( mut commands: Commands, - character_meshes: Res, mut materials: ResMut>, mut collision_events: EventReader, mut character_query: Query<( @@ -280,131 +409,61 @@ fn collision_event_system( )>, pass_through_filter_query: Query<&PassThroughFilter>, melty_query: Query<&Melty>, - mut collision_counter_query: Query<&mut CollisionCount>, - mut app_state: ResMut>, audio: Res>, - mut character_list: ResMut, ) { for collision_event in collision_events.iter() { - match collision_event { - CollisionEvent::Started(e1, e2, flags) => { - if flags.is_empty() { - if let ( - Ok((c1_color, c1_transform, _c1_material, c1_player)), - Ok((c2_color, c2_transform, _c2_material, c2_player)), - ) = (character_query.get(*e1), character_query.get(*e2)) - { - character_list.0.remove(e1); - character_list.0.remove(e2); - commands.entity(*e1).despawn_recursive(); + if let CollisionEvent::Started(e1, e2, flags) = collision_event { + if flags.is_empty() { + if let (Ok((c_color, _c_transform, _c_material, _c_player)), Ok(melty)) = + (character_query.get_mut(*e1), melty_query.get(*e2)) + { + if (Vec4::from(melty.0) - Vec4::from(c_color.0)).max_element() <= 0. { 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 && new_color.min_element() >= 0.9 - { - app_state.replace(AppState::Win).ok(); - } - - // position character based on current player location - spawn_character( - &mut commands, - &character_meshes, - &mut materials, - &audio, - &mut character_list, - if c1_player.is_some() { - *c1_transform - } else if c2_player.is_some() { - *c2_transform - } else { - Transform::identity().with_translation( - (c1_transform.translation + c2_transform.translation) / 2., - ) - }, - new_color.into(), - c1_player.is_some() || c2_player.is_some(), - ); - - audio.send(AudioMsg::Fusion).ok(); - } else if let (Ok((c_color, _c_transform, _c_material, _c_player)), Ok(melty)) = - (character_query.get_mut(*e1), melty_query.get(*e2)) - { - if (Vec4::from(melty.0) - Vec4::from(c_color.0)).max_element() <= 0. { - commands.entity(*e2).despawn_recursive(); - } - } else if let (Ok((c_color, _c_transform, _c_material, _c_player)), Ok(melty)) = - (character_query.get_mut(*e2), melty_query.get(*e1)) - { - if (Vec4::from(melty.0) - Vec4::from(c_color.0)).max_element() <= 0. { - commands.entity(*e1).despawn_recursive(); - } } - } else if *flags == CollisionEventFlags::SENSOR { - if let (Ok((mut c_color, _c_transform, mut c_material, c_player)), Ok(filter)) = ( - character_query.get_mut(*e1), - pass_through_filter_query.get(*e2), - ) { - c_color.0 = filter.apply(c_color.0); - *c_material = materials.add(ColorMaterial::from(c_color.0)); - - if c_player.is_some() { - audio - .send(AudioMsg::Color([ - c_color.0.r(), - c_color.0.g(), - c_color.0.b(), - ])) - .ok(); - audio.send(AudioMsg::Switch).ok(); - } - } else if let ( - Ok((mut c_color, _c_transform, mut c_material, c_player)), - Ok(filter), - ) = ( - character_query.get_mut(*e2), - pass_through_filter_query.get(*e1), - ) { - c_color.0 = filter.apply(c_color.0); - *c_material = materials.add(ColorMaterial::from(c_color.0)); - - if c_player.is_some() { - audio - .send(AudioMsg::Color([ - c_color.0.r(), - c_color.0.g(), - c_color.0.b(), - ])) - .ok(); - audio.send(AudioMsg::Switch).ok(); - } - } else if let (Ok(mut collision_count), Err(_)) = ( - collision_counter_query.get_mut(*e1), - character_query.get_mut(*e2), - ) { - collision_count.0 += 1; - } else if let (Ok(mut collision_count), Err(_)) = ( - collision_counter_query.get_mut(*e2), - character_query.get_mut(*e1), - ) { - collision_count.0 += 1; + } else if let (Ok((c_color, _c_transform, _c_material, _c_player)), Ok(melty)) = + (character_query.get_mut(*e2), melty_query.get(*e1)) + { + if (Vec4::from(melty.0) - Vec4::from(c_color.0)).max_element() <= 0. { + commands.entity(*e1).despawn_recursive(); } } - } - CollisionEvent::Stopped(e1, e2, flags) => { - if *flags == CollisionEventFlags::SENSOR { - if let (Ok(mut collision_count), Err(_)) = ( - collision_counter_query.get_mut(*e1), - character_query.get_mut(*e2), - ) { - collision_count.0 = collision_count.0.saturating_sub(1); - } else if let (Ok(mut collision_count), Err(_)) = ( - collision_counter_query.get_mut(*e2), - character_query.get_mut(*e1), - ) { - collision_count.0 = collision_count.0.saturating_sub(1); + } else if *flags == CollisionEventFlags::SENSOR { + if let (Ok((mut c_color, _c_transform, mut c_material, c_player)), Ok(filter)) = ( + character_query.get_mut(*e1), + pass_through_filter_query.get(*e2), + ) { + c_color.0 = filter.apply(c_color.0); + *c_material = materials.add(ColorMaterial::from(c_color.0)); + + if c_player.is_some() { + audio + .send(AudioMsg::Color([ + c_color.0.r(), + c_color.0.g(), + c_color.0.b(), + ])) + .ok(); + audio.send(AudioMsg::Switch).ok(); + } + } else if let ( + Ok((mut c_color, _c_transform, mut c_material, c_player)), + Ok(filter), + ) = ( + character_query.get_mut(*e2), + pass_through_filter_query.get(*e1), + ) { + c_color.0 = filter.apply(c_color.0); + *c_material = materials.add(ColorMaterial::from(c_color.0)); + + if c_player.is_some() { + audio + .send(AudioMsg::Color([ + c_color.0.r(), + c_color.0.g(), + c_color.0.b(), + ])) + .ok(); + audio.send(AudioMsg::Switch).ok(); } } } @@ -450,7 +509,7 @@ fn change_character_system( fn player_movement_system( keyboard_input: Res>, mut characters: Query<(&mut Velocity, &Children), With>, - collision_counter_query: Query<&CollisionCount>, + mut platform_count_query: Query<&mut PlatformCount>, audio: Res>, ) { let right_pressed: bool = @@ -461,11 +520,12 @@ fn player_movement_system( for (mut velocity, children) in characters.iter_mut() { velocity.linvel.x = 200. * (right_pressed as i8 - left_pressed as i8) as f32; - if keyboard_input.just_pressed(KeyCode::Space) - && collision_counter_query.get(children[0]).unwrap().0 != 0 - { + let mut platform_count: Mut = + platform_count_query.get_mut(children[0]).unwrap(); + if keyboard_input.just_pressed(KeyCode::Space) && platform_count.is_landed() { audio.send(AudioMsg::Jump).ok(); velocity.linvel.y = 700.; + platform_count.reset(); } } } diff --git a/src/menu.rs b/src/menu.rs index f35f024..0fd8a8f 100644 --- a/src/menu.rs +++ b/src/menu.rs @@ -39,7 +39,7 @@ fn setup(mut commands: Commands, asset_server: Res) { commands .spawn_bundle(Text2dBundle { text: Text::from_section( - "BEVYJAM", + "Lux synthesĕ", TextStyle { font: font.clone(), font_size: 96.0, diff --git a/src/particle_effect.rs b/src/particle_effect.rs index 26e275a..146ac73 100644 --- a/src/particle_effect.rs +++ b/src/particle_effect.rs @@ -143,5 +143,6 @@ fn particle_effect_system( / particle_effect.radius_squared, ); } + transform.translation.z = 0.005; } }