143 lines
3.4 KiB
Rust
143 lines
3.4 KiB
Rust
mod arrow;
|
|
mod gun;
|
|
mod thrown;
|
|
|
|
use crate::{
|
|
aim::AimTarget,
|
|
character::ProjectileOrigin,
|
|
global_observer,
|
|
heads::ActiveHeads,
|
|
heads_database::HeadsDatabase,
|
|
physics_layers::GameLayer,
|
|
player::{Player, PlayerBodyMesh},
|
|
sounds::PlaySound,
|
|
};
|
|
use bevy::prelude::*;
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
#[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);
|
|
|
|
global_observer!(app, on_trigger_state);
|
|
}
|
|
|
|
fn on_trigger_state(
|
|
trigger: Trigger<TriggerState>,
|
|
mut commands: Commands,
|
|
player_rot: Query<&Transform, With<PlayerBodyMesh>>,
|
|
player_query: Query<(Entity, &AimTarget), With<Player>>,
|
|
mut active_heads: Query<&mut ActiveHeads, With<Player>>,
|
|
heads_db: Res<HeadsDatabase>,
|
|
time: Res<Time>,
|
|
descendants: Query<&Children>,
|
|
projectile_origin: Query<&GlobalTransform, With<ProjectileOrigin>>,
|
|
) {
|
|
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((player, target)) = player_query.iter().next() else {
|
|
return;
|
|
};
|
|
|
|
let Some(transform) = descendants
|
|
.iter_descendants(player)
|
|
.find_map(|child| projectile_origin.get(child).ok())
|
|
else {
|
|
return;
|
|
};
|
|
|
|
let projectile_origin = transform.translation();
|
|
|
|
let Some((rot, dir)) = player_rot.iter().next().map(|t| (t.rotation, t.forward())) else {
|
|
return;
|
|
};
|
|
|
|
let trigger_state = TriggerData {
|
|
dir,
|
|
rot,
|
|
pos: projectile_origin,
|
|
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 => (),
|
|
};
|
|
}
|
|
}
|