Files
HEDZReloaded/src/abilities/mod.rs
2025-05-10 00:19:58 +02:00

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);
}
}
}