209 lines
4.9 KiB
Rust
209 lines
4.9 KiB
Rust
mod arrow;
|
|
mod gun;
|
|
mod healing;
|
|
mod missile;
|
|
mod thrown;
|
|
|
|
pub use healing::Healing;
|
|
|
|
use crate::{
|
|
GameState,
|
|
aim::AimTarget,
|
|
character::CharacterHierarchy,
|
|
global_observer,
|
|
heads::ActiveHeads,
|
|
heads_database::HeadsDatabase,
|
|
physics_layers::GameLayer,
|
|
player::{Player, PlayerBodyMesh},
|
|
sounds::PlaySound,
|
|
};
|
|
use bevy::prelude::*;
|
|
use healing::HealingStateChanged;
|
|
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,
|
|
Missile,
|
|
Medic,
|
|
}
|
|
|
|
#[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);
|
|
#[derive(Event, Reflect)]
|
|
pub struct TriggerMissile(pub TriggerData);
|
|
|
|
#[derive(Resource, Default)]
|
|
pub struct TriggerStateRes {
|
|
cooldown: f32,
|
|
active: bool,
|
|
}
|
|
|
|
impl TriggerStateRes {
|
|
pub fn is_active(&self) -> bool {
|
|
self.active
|
|
}
|
|
}
|
|
|
|
pub fn plugin(app: &mut App) {
|
|
app.init_resource::<TriggerStateRes>();
|
|
|
|
app.add_plugins(gun::plugin);
|
|
app.add_plugins(thrown::plugin);
|
|
app.add_plugins(arrow::plugin);
|
|
app.add_plugins(missile::plugin);
|
|
app.add_plugins(healing::plugin);
|
|
|
|
app.add_systems(
|
|
Update,
|
|
(update, update_heal_ability).run_if(in_state(GameState::Playing)),
|
|
);
|
|
|
|
global_observer!(app, on_trigger_state);
|
|
}
|
|
|
|
fn on_trigger_state(trigger: Trigger<TriggerState>, mut res: ResMut<TriggerStateRes>) {
|
|
res.active = matches!(trigger.event(), TriggerState::Active);
|
|
if !res.active {
|
|
res.cooldown = 0.0;
|
|
}
|
|
}
|
|
|
|
fn update(
|
|
mut res: ResMut<TriggerStateRes>,
|
|
mut commands: Commands,
|
|
player_rot: Query<&Transform, With<PlayerBodyMesh>>,
|
|
player_query: Query<(Entity, &AimTarget), With<Player>>,
|
|
mut active_heads: Single<&mut ActiveHeads, With<Player>>,
|
|
heads_db: Res<HeadsDatabase>,
|
|
time: Res<Time>,
|
|
character: CharacterHierarchy,
|
|
) {
|
|
if res.active && res.cooldown < time.elapsed_secs() {
|
|
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(projectile_origin) = character
|
|
.projectile_origin(player)
|
|
.map(|origin| origin.translation())
|
|
else {
|
|
return;
|
|
};
|
|
|
|
let Some((rot, dir)) = player_rot.iter().next().map(|t| (t.rotation, t.forward())) else {
|
|
return;
|
|
};
|
|
|
|
let head = heads_db.head_stats(state.head);
|
|
|
|
if matches!(head.ability, HeadAbility::None | HeadAbility::Medic) {
|
|
return;
|
|
}
|
|
|
|
active_heads.use_ammo(time.elapsed_secs());
|
|
|
|
res.cooldown = time.elapsed_secs() + (1. / head.aps);
|
|
|
|
let trigger_state = TriggerData {
|
|
dir,
|
|
rot,
|
|
pos: projectile_origin,
|
|
target: target.0,
|
|
target_layer: GameLayer::Npc,
|
|
head: state.head,
|
|
};
|
|
|
|
match head.ability {
|
|
HeadAbility::Thrown => commands.trigger(TriggerThrow(trigger_state)),
|
|
HeadAbility::Gun => commands.trigger(TriggerGun(trigger_state)),
|
|
HeadAbility::Missile => commands.trigger(TriggerMissile(trigger_state)),
|
|
HeadAbility::Arrow => commands.trigger(TriggerArrow(trigger_state)),
|
|
_ => panic!("Unhandled head ability"),
|
|
};
|
|
}
|
|
}
|
|
|
|
fn update_heal_ability(
|
|
res: Res<TriggerStateRes>,
|
|
mut commands: Commands,
|
|
active_heads: Single<(Entity, &ActiveHeads), With<Player>>,
|
|
heads_db: Res<HeadsDatabase>,
|
|
) {
|
|
if res.is_changed() {
|
|
let Some(state) = active_heads.1.current() else {
|
|
return;
|
|
};
|
|
|
|
let player = active_heads.0;
|
|
|
|
let head = heads_db.head_stats(state.head);
|
|
|
|
if !matches!(head.ability, HeadAbility::Medic) {
|
|
return;
|
|
}
|
|
|
|
if res.active {
|
|
commands.trigger_targets(HealingStateChanged::Started, player);
|
|
} else {
|
|
commands.trigger_targets(HealingStateChanged::Stopped, player);
|
|
}
|
|
}
|
|
}
|