diff --git a/assets/elements/item/machine_gun/bullet/machine_gun.bullet.yaml b/assets/elements/item/machine_gun/bullet/machine_gun.bullet.yaml new file mode 100644 index 0000000000..26ec60e84e --- /dev/null +++ b/assets/elements/item/machine_gun/bullet/machine_gun.bullet.yaml @@ -0,0 +1,11 @@ +lifetime: 0.65 +speed: 650 +body_diameter: 15 +atlas: ./machine_gun_bullet.atlas.yaml + +explosion_fps: 12 +explosion_frames: 3 +explosion_volume: 0.025 +explosion_lifetime: 0.4 +explosion_sound: ../explosion/bullet_hit_dull.ogg +explosion_atlas: ../explosion/explosion.atlas.yaml diff --git a/assets/elements/item/machine_gun/bullet/machine_gun_bullet.atlas.yaml b/assets/elements/item/machine_gun/bullet/machine_gun_bullet.atlas.yaml new file mode 100644 index 0000000000..f8733ba77a --- /dev/null +++ b/assets/elements/item/machine_gun/bullet/machine_gun_bullet.atlas.yaml @@ -0,0 +1,4 @@ +image: ./machine_gun_bullet.png +tile_size: [30, 4] +rows: 1 +columns: 1 diff --git a/assets/elements/item/machine_gun/bullet/machine_gun_bullet.png b/assets/elements/item/machine_gun/bullet/machine_gun_bullet.png new file mode 100644 index 0000000000..2eca8aaf92 Binary files /dev/null and b/assets/elements/item/machine_gun/bullet/machine_gun_bullet.png differ diff --git a/assets/elements/item/machine_gun/explosion/bullet_hit_dull.ogg b/assets/elements/item/machine_gun/explosion/bullet_hit_dull.ogg new file mode 100644 index 0000000000..572186bf98 Binary files /dev/null and b/assets/elements/item/machine_gun/explosion/bullet_hit_dull.ogg differ diff --git a/assets/elements/item/machine_gun/explosion/explosion.atlas.yaml b/assets/elements/item/machine_gun/explosion/explosion.atlas.yaml new file mode 100644 index 0000000000..8740da4ed9 --- /dev/null +++ b/assets/elements/item/machine_gun/explosion/explosion.atlas.yaml @@ -0,0 +1,4 @@ +image: ./explosion.png +tile_size: [11, 11] +rows: 1 +columns: 4 diff --git a/assets/elements/item/machine_gun/explosion/explosion.png b/assets/elements/item/machine_gun/explosion/explosion.png new file mode 100644 index 0000000000..f1313c3d2c Binary files /dev/null and b/assets/elements/item/machine_gun/explosion/explosion.png differ diff --git a/assets/elements/item/machine_gun/machine_gun.atlas.yaml b/assets/elements/item/machine_gun/machine_gun.atlas.yaml new file mode 100644 index 0000000000..6cbf74537c --- /dev/null +++ b/assets/elements/item/machine_gun/machine_gun.atlas.yaml @@ -0,0 +1,4 @@ +image: ./machine_gun.png +tile_size: [80, 24] +rows: 2 +columns: 2 diff --git a/assets/elements/item/machine_gun/machine_gun.element.yaml b/assets/elements/item/machine_gun/machine_gun.element.yaml index 8b9eb1005f..195e5310c6 100644 --- a/assets/elements/item/machine_gun/machine_gun.element.yaml +++ b/assets/elements/item/machine_gun/machine_gun.element.yaml @@ -1,2 +1,3 @@ -name: Machine Gun +name: MachineGun category: Weapons +data: machine_gun.yaml diff --git a/assets/elements/item/machine_gun/machine_gun.png b/assets/elements/item/machine_gun/machine_gun.png new file mode 100644 index 0000000000..cda97b02ec Binary files /dev/null and b/assets/elements/item/machine_gun/machine_gun.png differ diff --git a/assets/elements/item/machine_gun/machine_gun.yaml b/assets/elements/item/machine_gun/machine_gun.yaml new file mode 100644 index 0000000000..1c62256ced --- /dev/null +++ b/assets/elements/item/machine_gun/machine_gun.yaml @@ -0,0 +1,21 @@ +atlas: ./machine_gun.atlas.yaml + +max_ammo: 25 +cooldown: 125ms +empty_cooldown: 600ms +bullet_meta: ./bullet/machine_gun.bullet.yaml +bullet_spread: 0.1 + +shoot_sound_volume: 0.1 +shoot_sound: ./shoot/shoot.ogg +empty_shoot_sound_volume: 0.1 +empty_shoot_sound: ./shoot/gun_empty.ogg + +bounciness: 0.3 +can_rotate: true +body_size: [32, 8] +fin_anim: grab_2 +angular_velocity: 0.1 +throw_velocity: 360 +grab_offset: [13, -3] +kickback: 75 diff --git a/assets/elements/item/machine_gun/shoot/gun_empty.ogg b/assets/elements/item/machine_gun/shoot/gun_empty.ogg new file mode 100644 index 0000000000..5e4e182a59 Binary files /dev/null and b/assets/elements/item/machine_gun/shoot/gun_empty.ogg differ diff --git a/assets/elements/item/machine_gun/shoot/shoot.ogg b/assets/elements/item/machine_gun/shoot/shoot.ogg new file mode 100644 index 0000000000..dd90956768 Binary files /dev/null and b/assets/elements/item/machine_gun/shoot/shoot.ogg differ diff --git a/assets/game.yaml b/assets/game.yaml index 68048afe0d..3d402e730f 100644 --- a/assets/game.yaml +++ b/assets/game.yaml @@ -346,6 +346,7 @@ core: - /elements/item/jellyfish/jellyfish.element.yaml - /elements/item/kick_bomb/kick_bomb.element.yaml - /elements/item/mine/mine.element.yaml + - /elements/item/machine_gun/machine_gun.element.yaml - /elements/item/musket/musket.element.yaml - /elements/item/buss/buss.element.yaml - /elements/item/periscope/periscope.element.yaml diff --git a/assets/map/levels/level_7.map.yaml b/assets/map/levels/level_7.map.yaml index 7b15a1b797..98cfa1b29a 100644 --- a/assets/map/levels/level_7.map.yaml +++ b/assets/map/levels/level_7.map.yaml @@ -1173,7 +1173,7 @@ layers: - pos: - 216.0 - 310.5 - element: /elements/item/musket/musket.element.yaml + element: /elements/item/machine_gun/machine_gun.element.yaml - pos: - 248.0 - 438.5 diff --git a/src/core/elements.rs b/src/core/elements.rs index e15734337c..6a3e619069 100644 --- a/src/core/elements.rs +++ b/src/core/elements.rs @@ -15,6 +15,7 @@ pub mod flappy_jellyfish; pub mod grenade; pub mod jellyfish; pub mod kick_bomb; +pub mod machine_gun; pub mod mine; pub mod musket; pub mod periscope; @@ -31,9 +32,9 @@ pub mod urchin; pub mod prelude { pub use super::{ buss::*, crab::*, crate_item::*, decoration::*, fish_school::*, grenade::*, jellyfish::*, - kick_bomb::*, mine::*, musket::*, periscope::*, player_spawner::*, slippery::*, - slippery_seaweed::*, snail::*, spike::*, sproinger::*, stomp_boots::*, sword::*, urchin::*, - *, + kick_bomb::*, machine_gun::*, mine::*, musket::*, periscope::*, player_spawner::*, + slippery::*, slippery_seaweed::*, snail::*, spike::*, sproinger::*, stomp_boots::*, + sword::*, urchin::*, *, }; } @@ -316,6 +317,7 @@ install_plugins!( jellyfish, kick_bomb, mine, + machine_gun, musket, buss, player_spawner, diff --git a/src/core/elements/buss.rs b/src/core/elements/buss.rs index 251bbfc0a2..bd74f0b210 100644 --- a/src/core/elements/buss.rs +++ b/src/core/elements/buss.rs @@ -188,14 +188,15 @@ fn update( // If the item is being used let item_used = items_used.remove(entity).is_some(); if item_used && buss.cooldown.finished() { + // Reset fire cooldown + buss.cooldown = Timer::new(*cooldown, TimerMode::Once); // Empty if buss.ammo.eq(&0) { audio_center.play_sound(*empty_shoot_sound, *empty_shoot_sound_volume); continue; } - // Reset fire cooldown and subtract ammo - buss.cooldown = Timer::new(*cooldown, TimerMode::Once); + // Subtract ammo buss.ammo = buss.ammo.saturating_sub(1).clamp(0, buss.ammo); audio_center.play_sound(*shoot_sound, *shoot_sound_volume); diff --git a/src/core/elements/machine_gun.rs b/src/core/elements/machine_gun.rs new file mode 100644 index 0000000000..60a6d320dc --- /dev/null +++ b/src/core/elements/machine_gun.rs @@ -0,0 +1,306 @@ +use crate::prelude::*; + +#[derive(HasSchema, Default, Debug, Clone)] +#[type_data(metadata_asset("machine_gun"))] +#[repr(C)] +pub struct MachineGunMeta { + pub grab_offset: Vec2, + pub fin_anim: Ustr, + + pub body_size: Vec2, + pub bounciness: f32, + pub can_rotate: bool, + pub throw_velocity: f32, + pub angular_velocity: f32, + pub atlas: Handle, + + pub max_ammo: u32, + pub cooldown: Duration, + pub empty_cooldown: Duration, + pub bullet_meta: Handle, + pub bullet_spread: f32, + pub kickback: f32, + + pub shoot_sound_volume: f64, + pub empty_shoot_sound_volume: f64, + pub shoot_sound: Handle, + pub empty_shoot_sound: Handle, +} + +pub fn game_plugin(game: &mut Game) { + MachineGunMeta::register_schema(); + game.init_shared_resource::(); +} + +pub fn session_plugin(session: &mut Session) { + session + .stages + .add_system_to_stage(CoreStage::PreUpdate, hydrate) + .add_system_to_stage(CoreStage::PostUpdate, update); +} + +#[derive(Clone, Debug, HasSchema, Default)] +pub struct MachineGun { + pub ammo: u32, + pub cooldown: Timer, + pub empty_cooldown: Timer, + pub state: MachineGunState, +} + +#[derive(Default, Clone, Copy, Debug)] +pub enum MachineGunState { + #[default] + Idle, + Shooting, +} + +fn hydrate( + game_meta: Root, + mut entities: ResMutInit, + mut hydrated: CompMut, + mut element_handles: CompMut, + assets: Res, + mut machine_guns: CompMut, + mut atlas_sprites: CompMut, + mut bodies: CompMut, + mut transforms: CompMut, + mut items: CompMut, + mut item_throws: CompMut, + mut item_grabs: CompMut, + mut respawn_points: CompMut, + mut spawner_manager: SpawnerManager, +) { + let mut not_hydrated_bitset = hydrated.bitset().clone(); + not_hydrated_bitset.bit_not(); + not_hydrated_bitset.bit_and(element_handles.bitset()); + + let spawner_entities = entities + .iter_with_bitset(¬_hydrated_bitset) + .collect::>(); + + for spawner_ent in spawner_entities { + let transform = *transforms.get(spawner_ent).unwrap(); + let element_handle = *element_handles.get(spawner_ent).unwrap(); + let element_meta = assets.get(element_handle.0); + + if let Ok(MachineGunMeta { + atlas, + fin_anim, + grab_offset, + max_ammo, + body_size, + can_rotate, + bounciness, + throw_velocity, + angular_velocity, + .. + }) = assets.get(element_meta.data).try_cast_ref() + { + hydrated.insert(spawner_ent, MapElementHydrated); + + let entity = entities.create(); + items.insert(entity, Item); + item_throws.insert( + entity, + ItemThrow::strength(*throw_velocity) + .with_spin(*angular_velocity) + .with_system(machine_gun_drop(entity, *max_ammo)), + ); + item_grabs.insert( + entity, + ItemGrab { + fin_anim: *fin_anim, + sync_animation: false, + grab_offset: *grab_offset, + }, + ); + machine_guns.insert( + entity, + MachineGun { + ammo: *max_ammo, + cooldown: Timer::new(Duration::from_millis(0), TimerMode::Once), + empty_cooldown: Timer::new(Duration::from_millis(0), TimerMode::Once), + state: MachineGunState::Idle, + }, + ); + atlas_sprites.insert(entity, AtlasSprite::new(*atlas)); + respawn_points.insert(entity, DehydrateOutOfBounds(spawner_ent)); + transforms.insert(entity, transform); + element_handles.insert(entity, element_handle); + hydrated.insert(entity, MapElementHydrated); + bodies.insert( + entity, + KinematicBody { + shape: ColliderShape::Rectangle { size: *body_size }, + has_mass: true, + has_friction: true, + can_rotate: *can_rotate, + bounciness: *bounciness, + gravity: game_meta.core.physics.gravity, + ..default() + }, + ); + spawner_manager.create_spawner(spawner_ent, vec![entity]) + } + } +} + +fn update( + entities: Res, + mut commands: Commands, + element_handles: Comp, + assets: Res, + + mut machine_guns: CompMut, + transforms: CompMut, + mut sprites: CompMut, + mut audio_center: ResMut, + + player_inventories: PlayerInventories, + mut items_used: CompMut, + items_dropped: CompMut, + time: Res