170 lines
4.1 KiB
Rust
170 lines
4.1 KiB
Rust
mod arrow;
|
|
mod gun;
|
|
mod thrown;
|
|
|
|
use crate::{
|
|
GameState,
|
|
aim::AimTarget,
|
|
global_observer,
|
|
heads::ActiveHeads,
|
|
heads_database::HeadsDatabase,
|
|
hitpoints::Hit,
|
|
physics_layers::GameLayer,
|
|
player::{Player, PlayerBodyMesh},
|
|
sounds::PlaySound,
|
|
tb_entities::EnemySpawn,
|
|
};
|
|
use avian3d::prelude::*;
|
|
use bevy::prelude::*;
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
#[derive(Component)]
|
|
pub struct Projectile {
|
|
pub owner_head: usize,
|
|
}
|
|
|
|
#[derive(Event, Reflect)]
|
|
pub enum TriggerState {
|
|
Active,
|
|
Inactive,
|
|
}
|
|
|
|
#[derive(Event, Reflect)]
|
|
pub struct TriggerCashHeal;
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Reflect, Default, Serialize, Deserialize)]
|
|
pub enum HeadAbility {
|
|
#[default]
|
|
None,
|
|
Arrow,
|
|
Thrown,
|
|
Gun,
|
|
}
|
|
|
|
#[derive(Debug, Reflect, Clone, Copy)]
|
|
pub struct TriggerData {
|
|
target: Option<Entity>,
|
|
dir: Dir3,
|
|
rot: Quat,
|
|
pos: Vec3,
|
|
target_layer: GameLayer,
|
|
head: usize,
|
|
}
|
|
|
|
impl TriggerData {
|
|
pub fn new(
|
|
target: Option<Entity>,
|
|
dir: Dir3,
|
|
rot: Quat,
|
|
pos: Vec3,
|
|
target_layer: GameLayer,
|
|
head: usize,
|
|
) -> Self {
|
|
Self {
|
|
target,
|
|
dir,
|
|
rot,
|
|
pos,
|
|
target_layer,
|
|
head,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Event, Reflect)]
|
|
pub struct TriggerGun(pub TriggerData);
|
|
#[derive(Event, Reflect)]
|
|
pub struct TriggerArrow(pub TriggerData);
|
|
#[derive(Event, Reflect)]
|
|
pub struct TriggerThrow(pub TriggerData);
|
|
|
|
pub fn plugin(app: &mut App) {
|
|
app.add_plugins(gun::plugin);
|
|
app.add_plugins(thrown::plugin);
|
|
app.add_plugins(arrow::plugin);
|
|
|
|
app.add_systems(Update, enemy_hit.run_if(in_state(GameState::Playing)));
|
|
|
|
global_observer!(app, on_trigger_state);
|
|
}
|
|
|
|
fn enemy_hit(
|
|
mut commands: Commands,
|
|
mut collision_event_reader: EventReader<CollisionStarted>,
|
|
query_shot: Query<&Projectile>,
|
|
query_npc: Query<&EnemySpawn>,
|
|
heads_db: Res<HeadsDatabase>,
|
|
) {
|
|
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))
|
|
};
|
|
|
|
if let Ok(head) = projectile.map(|p| p.owner_head) {
|
|
let damage = heads_db.head_stats(head).damage;
|
|
commands.entity(enemy_entity).trigger(Hit { damage });
|
|
}
|
|
}
|
|
}
|
|
|
|
fn on_trigger_state(
|
|
trigger: Trigger<TriggerState>,
|
|
mut commands: Commands,
|
|
player_rot: Query<&Transform, With<PlayerBodyMesh>>,
|
|
player_query: Query<(&Transform, &AimTarget), With<Player>>,
|
|
mut active_heads: Query<&mut ActiveHeads, With<Player>>,
|
|
heads_db: Res<HeadsDatabase>,
|
|
time: Res<Time>,
|
|
) {
|
|
if matches!(trigger.event(), TriggerState::Active) {
|
|
let Ok(mut active_heads) = active_heads.get_single_mut() else {
|
|
return;
|
|
};
|
|
|
|
let Some(state) = active_heads.current() else {
|
|
return;
|
|
};
|
|
|
|
if !state.has_ammo() {
|
|
commands.trigger(PlaySound::Invalid);
|
|
return;
|
|
}
|
|
|
|
let Some((transform, target)) = player_query.iter().next() else {
|
|
return;
|
|
};
|
|
|
|
let Some((rot, dir)) = player_rot.iter().next().map(|t| (t.rotation, t.forward())) else {
|
|
return;
|
|
};
|
|
|
|
let trigger_state = TriggerData {
|
|
dir,
|
|
rot,
|
|
pos: transform.translation,
|
|
target: target.0,
|
|
target_layer: GameLayer::Npc,
|
|
head: state.head,
|
|
};
|
|
|
|
active_heads.use_ammo(time.elapsed_secs());
|
|
|
|
let ability = heads_db.head_stats(state.head).ability;
|
|
match ability {
|
|
HeadAbility::Thrown => commands.trigger(TriggerThrow(trigger_state)),
|
|
HeadAbility::Gun => commands.trigger(TriggerGun(trigger_state)),
|
|
HeadAbility::Arrow => commands.trigger(TriggerArrow(trigger_state)),
|
|
HeadAbility::None => (),
|
|
};
|
|
}
|
|
}
|