120 lines
2.8 KiB
Rust
120 lines
2.8 KiB
Rust
mod gun;
|
|
mod thrown;
|
|
|
|
use crate::{
|
|
GameState,
|
|
heads::ActiveHeads,
|
|
npc::Hit,
|
|
player::{Player, PlayerRig},
|
|
tb_entities::EnemySpawn,
|
|
};
|
|
use avian3d::prelude::*;
|
|
use bevy::prelude::*;
|
|
|
|
#[derive(Component)]
|
|
pub struct Projectile {
|
|
pub damage: u32,
|
|
}
|
|
|
|
#[derive(Event, Reflect)]
|
|
pub enum TriggerState {
|
|
Active,
|
|
Inactive,
|
|
}
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Reflect)]
|
|
pub enum HeadAbility {
|
|
None,
|
|
Arrow,
|
|
Thrown,
|
|
Gun,
|
|
}
|
|
|
|
#[derive(Debug, Reflect, Clone, Copy)]
|
|
pub struct PlayerTriggerState {
|
|
dir: Dir3,
|
|
rot: Quat,
|
|
pos: Vec3,
|
|
}
|
|
|
|
#[derive(Event, Reflect)]
|
|
pub struct TriggerGun(pub PlayerTriggerState);
|
|
#[derive(Event, Reflect)]
|
|
pub struct TriggerThrow(pub PlayerTriggerState);
|
|
|
|
pub fn plugin(app: &mut App) {
|
|
app.add_plugins(gun::plugin);
|
|
app.add_plugins(thrown::plugin);
|
|
|
|
app.add_systems(Update, enemy_hit.run_if(in_state(GameState::Playing)));
|
|
|
|
app.add_observer(on_trigger_state);
|
|
}
|
|
|
|
fn enemy_hit(
|
|
mut commands: Commands,
|
|
mut collision_event_reader: EventReader<CollisionStarted>,
|
|
query_shot: Query<&Projectile>,
|
|
query_npc: Query<&EnemySpawn>,
|
|
) {
|
|
for CollisionStarted(e1, e2) in collision_event_reader.read() {
|
|
if !query_shot.contains(*e1) && !query_shot.contains(*e2) {
|
|
continue;
|
|
}
|
|
if !query_npc.contains(*e1) && !query_npc.contains(*e2) {
|
|
continue;
|
|
}
|
|
|
|
let (enemy_entity, projectile) = if query_npc.contains(*e1) {
|
|
(*e1, query_shot.get(*e2))
|
|
} else {
|
|
(*e2, query_shot.get(*e1))
|
|
};
|
|
|
|
let damage = projectile.map(|p| p.damage).unwrap_or_default();
|
|
commands.entity(enemy_entity).trigger(Hit { damage });
|
|
}
|
|
}
|
|
|
|
fn on_trigger_state(
|
|
trigger: Trigger<TriggerState>,
|
|
mut commands: Commands,
|
|
player_rot: Query<&Transform, With<PlayerRig>>,
|
|
player_transform: Query<&Transform, With<Player>>,
|
|
mut active_heads: ResMut<ActiveHeads>,
|
|
time: Res<Time>,
|
|
) {
|
|
if matches!(trigger.event(), TriggerState::Active) {
|
|
let Some(state) = active_heads.current() else {
|
|
return;
|
|
};
|
|
|
|
if !state.has_ammo() {
|
|
//TOOD: play sound
|
|
return;
|
|
}
|
|
|
|
let Some(transform) = player_transform.iter().next().copied() else {
|
|
return;
|
|
};
|
|
|
|
let Some((rot, dir)) = player_rot.iter().next().map(|t| (t.rotation, t.forward())) else {
|
|
return;
|
|
};
|
|
|
|
let trigger_state = PlayerTriggerState {
|
|
dir,
|
|
rot,
|
|
pos: transform.translation,
|
|
};
|
|
|
|
active_heads.use_ammo(time.elapsed_secs());
|
|
|
|
match state.ability {
|
|
HeadAbility::Thrown => commands.trigger(TriggerThrow(trigger_state)),
|
|
HeadAbility::Gun => commands.trigger(TriggerGun(trigger_state)),
|
|
_ => (),
|
|
};
|
|
}
|
|
}
|