diff --git a/assets/maps/map1.map b/assets/maps/map1.map index 78b3d92..502dab5 100644 --- a/assets/maps/map1.map +++ b/assets/maps/map1.map @@ -1117,6 +1117,7 @@ "head" "field medic" "key" "fence_gate" "disable_ai" "true" +"spawn_order" "0" } // entity 3 { @@ -1130,6 +1131,7 @@ "head" "field medic" "angles" "0 180 0" "disable_ai" "true" +"spawn_order" "1" } // entity 5 { @@ -1386,6 +1388,7 @@ "origin" "2568 4504 -232" "angles" "0 0 0" "head" "super market shopper" +"spawn_order" "2" } // entity 24 { @@ -1394,6 +1397,7 @@ "angles" "0 -90 0" "head" "green grocer" "key" "fence_shaft" +"spawn_order" "3" } // entity 25 { @@ -1600,8 +1604,8 @@ // entity 45 { "classname" "enemy_spawn" -"origin" "552 -680 -232" -"angles" "0 90 0" +"origin" "-184 -584 -232" +"angles" "0 0 0" "head" "commando" "key" "" "disable_ai" "true" @@ -1609,8 +1613,8 @@ // entity 46 { "classname" "enemy_spawn" -"origin" "456 -680 -232" -"angles" "0 90 0" +"origin" "-184 -504 -232" +"angles" "0 0 0" "head" "angry demonstrator" "key" "" "disable_ai" "true" @@ -1618,8 +1622,8 @@ // entity 47 { "classname" "enemy_spawn" -"origin" "360 -680 -232" -"angles" "0 90 0" +"origin" "-184 -440 -232" +"angles" "0 0 0" "head" "goblin" "key" "" "disable_ai" "true" @@ -1627,8 +1631,8 @@ // entity 48 { "classname" "enemy_spawn" -"origin" "264 -680 -232" -"angles" "0 90 0" +"origin" "-184 -360 -232" +"angles" "0 0 0" "head" "green grocer" "key" "" "disable_ai" "true" @@ -1636,8 +1640,8 @@ // entity 49 { "classname" "enemy_spawn" -"origin" "168 -680 -232" -"angles" "0 90 0" +"origin" "-184 -280 -232" +"angles" "0 0 0" "head" "highland hammer thrower" "key" "" "disable_ai" "true" @@ -1645,8 +1649,8 @@ // entity 50 { "classname" "enemy_spawn" -"origin" "72 -680 -232" -"angles" "0 90 0" +"origin" "-184 -184 -232" +"angles" "0 0 0" "head" "legionnaire" "key" "" "disable_ai" "true" @@ -1654,8 +1658,8 @@ // entity 51 { "classname" "enemy_spawn" -"origin" "-24 -680 -232" -"angles" "0 90 0" +"origin" "-184 -104 -232" +"angles" "0 0 0" "head" "mig pilot" "key" "" "disable_ai" "true" @@ -1663,8 +1667,8 @@ // entity 52 { "classname" "enemy_spawn" -"origin" "-104 -680 -232" -"angles" "0 90 0" +"origin" "-184 -8 -232" +"angles" "0 0 0" "head" "soldier ant" "key" "" "disable_ai" "true" @@ -1673,7 +1677,7 @@ { "classname" "enemy_spawn" "origin" "-184 -680 -232" -"angles" "0 90 0" +"angles" "0 0 0" "head" "super market shopper" "key" "" "disable_ai" "true" diff --git a/assets/sfx/effects/beam_out.ogg b/assets/sfx/effects/beam_in_out.ogg similarity index 100% rename from assets/sfx/effects/beam_out.ogg rename to assets/sfx/effects/beam_in_out.ogg diff --git a/src/aim/mod.rs b/src/aim/mod.rs index cbffbe1..3688b59 100644 --- a/src/aim/mod.rs +++ b/src/aim/mod.rs @@ -5,6 +5,7 @@ use crate::{ GameState, head::ActiveHead, heads_database::HeadsDatabase, + hitpoints::Hitpoints, physics_layers::GameLayer, player::{Player, PlayerBodyMesh}, tb_entities::EnemySpawn, @@ -68,12 +69,12 @@ fn head_change( fn update_player_aim( mut commands: Commands, - potential_targets: Query<(Entity, &Transform), With>, + potential_targets: Query<(Entity, &Transform), With>, player_rot: Query<(&Transform, &GlobalTransform), With>, - mut player_aim: Query<(&AimState, &mut AimTarget), With>, + mut player_aim: Query<(Entity, &AimState, &mut AimTarget), With>, spatial_query: SpatialQuery, ) { - let Some((state, mut aim_target)) = player_aim.iter_mut().next() else { + let Some((player, state, mut aim_target)) = player_aim.iter_mut().next() else { return; }; @@ -89,6 +90,10 @@ fn update_player_aim( let mut target_distance = f32::MAX; for (e, t) in potential_targets.iter() { + if e == player { + continue; + } + let delta = player_pos - t.translation; let distance = delta.length(); diff --git a/src/loading_assets.rs b/src/loading_assets.rs index 5eb80a3..46dbbc4 100644 --- a/src/loading_assets.rs +++ b/src/loading_assets.rs @@ -54,6 +54,8 @@ pub struct AudioAssets { pub secret_head_collect: Handle, #[asset(path = "sfx/effects/head_drop.ogg")] pub head_drop: Handle, + #[asset(path = "sfx/effects/beam_in_out.ogg")] + pub beaming: Handle, #[asset(path = "sfx/hit", collection(typed))] pub hit: Vec>, diff --git a/src/npc.rs b/src/npc.rs index 12e6c2f..d2b2c3e 100644 --- a/src/npc.rs +++ b/src/npc.rs @@ -2,12 +2,14 @@ use crate::{ GameState, ai::Ai, character::AnimatedCharacter, + global_observer, head::ActiveHead, head_drop::HeadDrops, heads::{ActiveHeads, HEAD_COUNT, HeadState}, heads_database::HeadsDatabase, hitpoints::{Hitpoints, Kill}, keys::KeySpawn, + sounds::PlaySound, tb_entities::EnemySpawn, }; use bevy::prelude::*; @@ -17,11 +19,34 @@ use std::collections::HashMap; #[reflect(Component)] pub struct Npc; -pub fn plugin(app: &mut App) { - app.add_systems(OnEnter(GameState::Playing), init); +#[derive(Resource, Reflect, Default)] +#[reflect(Resource)] +struct NpcSpawning { + spawn_index: u32, } -fn init(mut commands: Commands, query: Query<(Entity, &EnemySpawn)>, heads_db: Res) { +#[derive(Event)] +struct OnCheckSpawns; + +pub fn plugin(app: &mut App) { + app.init_resource::(); + app.add_systems(OnEnter(GameState::Playing), setup); + + global_observer!(app, on_spawn_check); +} + +fn setup(mut commands: Commands) { + commands.init_resource::(); + commands.trigger(OnCheckSpawns); +} + +fn on_spawn_check( + _trigger: Trigger, + mut commands: Commands, + query: Query<(Entity, &EnemySpawn), Without>, + heads_db: Res, + spawning: Res, +) { //TODO: move into HeadsDatabase let mut names: HashMap = HashMap::default(); for i in 0..HEAD_COUNT { @@ -29,6 +54,12 @@ fn init(mut commands: Commands, query: Query<(Entity, &EnemySpawn)>, heads_db: R } for (e, spawn) in query.iter() { + if let Some(order) = spawn.spawn_order { + if order > spawning.spawn_index { + continue; + } + } + let id = names[&spawn.head]; commands .entity(e) @@ -47,6 +78,8 @@ fn init(mut commands: Commands, query: Query<(Entity, &EnemySpawn)>, heads_db: R .insert_if(Ai, || !spawn.disable_ai) .with_child((Name::from("body-rig"), AnimatedCharacter::new(id))) .observe(on_kill); + + commands.trigger(PlaySound::Beaming); } } @@ -59,7 +92,14 @@ fn on_kill( return; }; + if let Some(order) = enemy.spawn_order { + commands.insert_resource(NpcSpawning { + spawn_index: order + 1, + }); + } + commands.trigger(HeadDrops::new(transform.translation, head.0)); + commands.trigger(OnCheckSpawns); commands.entity(trigger.target()).despawn(); diff --git a/src/sounds.rs b/src/sounds.rs index 18c02df..8db87d4 100644 --- a/src/sounds.rs +++ b/src/sounds.rs @@ -19,6 +19,7 @@ pub enum PlaySound { Reloaded, CashHeal, Crossbow, + Beaming, Backpack { open: bool }, Head(String), } @@ -60,6 +61,7 @@ fn on_spawn_sounds( PlaySound::HeadCollect => assets.head_collect.clone(), PlaySound::SecretHeadCollect => assets.secret_head_collect.clone(), PlaySound::MissileExplosion => assets.missile_explosion.clone(), + PlaySound::Beaming => assets.beaming.clone(), PlaySound::Backpack { open } => { if *open { assets.backpack_open.clone() diff --git a/src/tb_entities.rs b/src/tb_entities.rs index e0ed48e..e80908a 100644 --- a/src/tb_entities.rs +++ b/src/tb_entities.rs @@ -120,6 +120,7 @@ pub struct EnemySpawn { pub head: String, pub key: String, pub disable_ai: bool, + pub spawn_order: Option, } impl EnemySpawn { diff --git a/trenchbroom/hedz/hedz.fgd b/trenchbroom/hedz/hedz.fgd index 9b82845..ea5e9d8 100644 --- a/trenchbroom/hedz/hedz.fgd +++ b/trenchbroom/hedz/hedz.fgd @@ -53,6 +53,7 @@ "true" : "true" "false" : "false" ] + spawn_order(integer) : "spawn_order" : : "" ] @SolidClass base(__transform, __target) = movable