diff --git a/src/core.rs b/src/core.rs index 08aac68ad1..fd1a6be4e2 100644 --- a/src/core.rs +++ b/src/core.rs @@ -31,8 +31,8 @@ use crate::{prelude::*, settings::PlayerControlMapping}; pub mod prelude { pub use super::{ attachment::*, audio::*, bullet::*, camera::*, damage::*, debug::*, editor::*, editor::*, - elements::prelude::*, globals::*, input::*, item::*, lifetime::*, map::*, - map_constructor::*, metadata::*, physics::*, player::*, random::*, utils::*, FPS, + elements::prelude::*, flappy_jellyfish::*, globals::*, input::*, item::*, lifetime::*, + map::*, map_constructor::*, metadata::*, physics::*, player::*, random::*, utils::*, FPS, MAX_PLAYERS, }; } diff --git a/src/core/camera.rs b/src/core/camera.rs index 8e6ade06ed..b853e3475b 100644 --- a/src/core/camera.rs +++ b/src/core/camera.rs @@ -22,17 +22,24 @@ pub struct ParallaxBackgroundSprite { pub meta: ParallaxLayerMeta, } +/// A subject of the camera. +/// +/// The camera will move and zoom to ensure that all subjects remain visible. Entities must also +/// have a `Transform` component and a `KinematicBody` component for this to work properly. +#[derive(Clone, Copy, Debug, Default, HasSchema)] +pub struct CameraSubject { + /// A rectangle around the subject that is larger than the subject, and will always move to + /// contain it. The camera will seek to contain this rectangle, instead of the subject itself. + /// + /// The advantage of this processing method is that the larger rect doesn't move as much as the + /// subject, because, for instance, a player jumping up and down in place will still be inside + /// of their camera rect, so the camera will not move around annoyingly. + rect: Rect, +} + /// The state of the camera. #[derive(Clone, Debug, HasSchema, Default)] pub struct CameraState { - /// A rectangle around the player that is larger than the player, and will always move to - /// contain the player. The camra will seek to contain this rectangle, instead of the player - /// itself. - /// - /// The advantage of this processing method is that the larger rect doesn't move as much as the - /// player, because, for instance, while jumping up and down in place, the player will still be - /// inside of their camera rect, so the camera will not move around annoyingly. - pub player_camera_rects: [Rect; MAX_PLAYERS], /// Disables the default camera controller. Useful, for example, when taking over the camera /// from the editor. pub disable_controller: bool, @@ -45,16 +52,16 @@ fn camera_controller( map: Res, mut cameras: CompMut, mut camera_shakes: CompMut, - mut camera_states: CompMut, + camera_states: Comp, + mut camera_subjects: CompMut, transforms: Comp, - player_indexes: Comp, bodies: Comp, window: Res, ) { let meta = &meta.core.camera; let Some((_ent, (camera, camera_shake, camera_state))) = entities - .iter_with((&mut cameras, &mut camera_shakes, &mut camera_states)) + .iter_with((&mut cameras, &mut camera_shakes, &camera_states)) .next() else { return; @@ -64,13 +71,13 @@ fn camera_controller( } // Update player camera rects - for (_ent, (transform, player_idx, body)) in - entities.iter_with((&transforms, &player_indexes, &bodies)) + for (_ent, (camera_subj, transform, body)) in + entities.iter_with((&mut camera_subjects, &transforms, &bodies)) { let camera_box_size = meta.player_camera_box_size; // Get the player's camera box - let camera_box = &mut camera_state.player_camera_rects[player_idx.0 as usize]; + let camera_box = &mut camera_subj.rect; // If it's not be initialized. if camera_box.min == Vec2::ZERO && camera_box.max == Vec2::ZERO { @@ -112,27 +119,19 @@ fn camera_controller( .unwrap_or(window.size); let viewport_aspect = viewport_size.x / viewport_size.y; let default_height = meta.default_height; - let camera_height = if let CameraSize::FixedHeight(height) = &camera.size { - *height + let default_width = viewport_aspect * default_height; + let camera_height = if let CameraSize::FixedHeight(height) = camera.size { + height } else { 400.0 }; let mut scale = camera_height / default_height; - let default_width = viewport_aspect * default_height; let map_size = map.grid_size.as_vec2() * map.tile_size; - let mut min = Vec2::new(f32::MAX, f32::MAX); - let mut max = Vec2::new(f32::MIN, f32::MIN); - - let players: Vec = entities - .iter_with(&player_indexes) - .map(|x| x.1 .0) - .collect(); - let player_count = players.len(); - - for player_idx in players { - let rect = camera_state.player_camera_rects[player_idx as usize]; + let mut min = Vec2::MAX; + let mut max = Vec2::MIN; + for CameraSubject { rect } in camera_subjects.iter_mut() { min = (rect.min - vec2(meta.border_left, meta.border_bottom)) .min(min) .max(Vec2::ZERO); @@ -142,7 +141,8 @@ fn camera_controller( let camera_pos = &mut camera_shake.center; - let mut middle_point = if player_count == 0 { + let subject_count = camera_subjects.iter().count(); + let mut middle_point = if subject_count == 0 { camera_pos.truncate() } else { Rect { min, max }.center() diff --git a/src/core/elements/flappy_jellyfish.rs b/src/core/elements/flappy_jellyfish.rs index b7fd223429..b7986c8e94 100644 --- a/src/core/elements/flappy_jellyfish.rs +++ b/src/core/elements/flappy_jellyfish.rs @@ -26,13 +26,13 @@ impl FlappyJellyfishMeta { } } -pub trait AssetGetFlappyJellyfishMeta { +pub trait FlappyJellyfishMetaSchemaExts { /// Try to cast the `asset` to a `JellyfishMeta` and get the /// `FlappyJellyfishMeta` from it. fn try_get_flappy_meta(&self) -> Option>; } -impl AssetGetFlappyJellyfishMeta for SchemaBox { +impl FlappyJellyfishMetaSchemaExts for SchemaBox { fn try_get_flappy_meta(&self) -> Option> { self.try_cast_ref::() .ok() @@ -43,7 +43,7 @@ impl AssetGetFlappyJellyfishMeta for SchemaBox { pub fn session_plugin(session: &mut Session) { session .stages - .add_system_to_stage(CoreStage::PostUpdate, move_flappy_jellyfish) + .add_system_to_stage(CoreStage::PostUpdate, control_flappy_jellyfish) .add_system_to_stage(CoreStage::PostUpdate, explode_flappy_jellyfish); } @@ -53,17 +53,54 @@ pub struct FlappyJellyfish { pub jellyfish: Entity, } -pub fn spawn(owner: Entity, jellyfish_ent: Entity) -> StaticSystem<(), ()> { +/// A player with a jellyfish is holding shoot. Take control of the flappy if +/// one exists or spawn one. +pub fn spawn_or_take_control( + owner: Entity, + jellyfish_ent: Entity, + flappy_ent: Option, +) -> StaticSystem<(), ()> { + (move |world: &World| { + if let Some(flappy_ent) = flappy_ent { + take_control(owner, flappy_ent).run(world, ()); + } else { + { + let mut jellyfishes = world.components.get::().borrow_mut(); + let Some(jellyfish) = jellyfishes.get_mut(jellyfish_ent) else { + return; + }; + if jellyfish.ammo == 0 { + return; + } + jellyfish.ammo -= 1; + } + spawn(owner, jellyfish_ent).run(world, ()); + } + }) + .system() +} + +/// Take control of the flappy associated with a jellyfish for a player. +fn take_control(owner: Entity, flappy_ent: Entity) -> StaticSystem<(), ()> { + (move |mut player_driving: CompMut| { + player_driving.insert(owner, PlayerDrivingJellyfish { flappy: flappy_ent }); + }) + .system() +} + +/// Spawn a flappy jellyfish. +fn spawn(owner: Entity, jellyfish_ent: Entity) -> StaticSystem<(), ()> { (move |mut entities: ResMut, element_handles: Comp, - mut driving_jellyfishes: CompMut, + mut player_driving: CompMut, + mut jellyfishes: CompMut, mut flappy_jellyfishes: CompMut, mut bodies: CompMut, - mut fall_velocities: CompMut, assets: Res, mut atlas_sprites: CompMut, mut animated_sprites: CompMut, - mut transforms: CompMut| { + mut transforms: CompMut, + mut camera_subjects: CompMut| { let Some(flappy_meta) = element_handles .get(jellyfish_ent) .map(|element_h| assets.get(element_h.0)) @@ -76,13 +113,12 @@ pub fn spawn(owner: Entity, jellyfish_ent: Entity) -> StaticSystem<(), ()> { }; let flappy_ent = entities.create(); - driving_jellyfishes.insert( - jellyfish_ent, - DrivingJellyfish { - owner, - flappy: flappy_ent, - }, - ); + + player_driving.insert(owner, PlayerDrivingJellyfish { flappy: flappy_ent }); + if let Some(jellyfish) = jellyfishes.get_mut(jellyfish_ent) { + jellyfish.flappy = Some(flappy_ent); + } + flappy_jellyfishes.insert( flappy_ent, FlappyJellyfish { @@ -96,11 +132,13 @@ pub fn spawn(owner: Entity, jellyfish_ent: Entity) -> StaticSystem<(), ()> { shape: ColliderShape::Rectangle { size: flappy_meta.body_size, }, - is_deactivated: true, + has_mass: true, + gravity: GRAVITY, + fall_through: true, + is_controlled: true, ..default() }, ); - fall_velocities.insert(flappy_ent, FallVelocity::default()); atlas_sprites.insert(flappy_ent, AtlasSprite::new(flappy_meta.atlas)); animated_sprites.insert( flappy_ent, @@ -114,10 +152,61 @@ pub fn spawn(owner: Entity, jellyfish_ent: Entity) -> StaticSystem<(), ()> { let mut transf = *transforms.get(owner).unwrap(); transf.translation += flappy_meta.spawn_offset.extend(0.0); transforms.insert(flappy_ent, transf); + camera_subjects.insert(flappy_ent, default()); }) .system() } +const SPEED_X: f32 = 324.0; +const SPEED_JUMP: f32 = 3.5; +const GRAVITY: f32 = 0.1; +const MIN_SPEED: Vec2 = vec2(-SPEED_X, -4.0); +const MAX_SPEED: Vec2 = vec2(SPEED_X, 4.0); + +fn control_flappy_jellyfish( + time: Res