-
-
Notifications
You must be signed in to change notification settings - Fork 27
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement simple attacking functionality
Relates to #24.
- Loading branch information
Showing
5 changed files
with
144 additions
and
2 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
use std::{cmp::Ordering, collections::BinaryHeap}; | ||
|
||
use bevy::prelude::*; | ||
use de_behaviour::ChaseTarget; | ||
use de_core::state::GameState; | ||
use de_objects::LaserCannon; | ||
use iyes_loopless::prelude::*; | ||
use parry3d::query::Ray; | ||
|
||
use crate::laser::LaserFireEvent; | ||
use crate::{sightline::LineOfSight, AttackingLabels}; | ||
|
||
pub(crate) struct AttackPlugin; | ||
|
||
impl Plugin for AttackPlugin { | ||
fn build(&self, app: &mut App) { | ||
app.add_system_set_to_stage( | ||
CoreStage::Update, | ||
SystemSet::new() | ||
.with_system( | ||
update | ||
.run_in_state(GameState::Playing) | ||
.label(AttackingLabels::Update), | ||
) | ||
.with_system( | ||
aim_and_fire | ||
.run_in_state(GameState::Playing) | ||
.label(AttackingLabels::Aim) | ||
.after(AttackingLabels::Update) | ||
.before(AttackingLabels::Fire), | ||
), | ||
); | ||
} | ||
} | ||
|
||
fn update(time: Res<Time>, mut cannons: Query<&mut LaserCannon>) { | ||
for mut cannon in cannons.iter_mut() { | ||
cannon.timer_mut().tick(time.delta()); | ||
} | ||
} | ||
|
||
fn aim_and_fire( | ||
mut attackers: Query<(Entity, &GlobalTransform, &mut LaserCannon, &ChaseTarget)>, | ||
targets: Query<&GlobalTransform>, | ||
sightline: LineOfSight, | ||
mut events: EventWriter<LaserFireEvent>, | ||
) { | ||
let attackers = attackers.iter_mut(); | ||
// The queue is used so that attacking has the same result as if it was | ||
// done in real-time (unaffected by update frequency). | ||
let mut fire_queue = BinaryHeap::new(); | ||
|
||
for (attacker, attacker_transform, mut cannon, target) in attackers { | ||
let target_transform = match targets.get(target.entity()) { | ||
Ok(transform) => transform, | ||
Err(_) => continue, | ||
}; | ||
|
||
let muzzle = attacker_transform.translation + cannon.muzzle(); | ||
// TODO do not aim at the object position but at center of its body | ||
let target_position = target_transform.translation; | ||
let to_target = (target_position - muzzle) | ||
.try_normalize() | ||
.expect("Attacker and target to close together"); | ||
let ray = Ray::new(muzzle.into(), to_target.into()); | ||
|
||
let observation = sightline.sight(&ray, cannon.range(), attacker); | ||
if observation.entity().map_or(true, |e| e != target.entity()) { | ||
cannon.timer_mut().reset(); | ||
} else if cannon.timer_mut().check_and_update() { | ||
fire_queue.push(FireScheduleItem::new(attacker, ray, cannon.into_inner())); | ||
} | ||
} | ||
|
||
while let Some(mut fire_schedule_item) = fire_queue.pop() { | ||
if fire_schedule_item.fire(&mut events) { | ||
fire_queue.push(fire_schedule_item); | ||
} | ||
} | ||
} | ||
|
||
struct FireScheduleItem<'a> { | ||
attacker: Entity, | ||
ray: Ray, | ||
cannon: &'a mut LaserCannon, | ||
} | ||
|
||
impl<'a> FireScheduleItem<'a> { | ||
fn new(attacker: Entity, ray: Ray, cannon: &'a mut LaserCannon) -> Self { | ||
Self { | ||
attacker, | ||
ray, | ||
cannon, | ||
} | ||
} | ||
|
||
fn fire(&mut self, events: &mut EventWriter<LaserFireEvent>) -> bool { | ||
events.send(LaserFireEvent::new( | ||
self.attacker, | ||
self.ray, | ||
self.cannon.range(), | ||
self.cannon.damage(), | ||
)); | ||
self.cannon.timer_mut().check_and_update() | ||
} | ||
} | ||
|
||
impl<'a> Ord for FireScheduleItem<'a> { | ||
fn cmp(&self, other: &Self) -> Ordering { | ||
let ordering = self.cannon.timer().cmp(other.cannon.timer()); | ||
if let Ordering::Equal = ordering { | ||
// Make it more deterministic, objects with smaller coordinates | ||
// have disadvantage. | ||
self.ray | ||
.origin | ||
.partial_cmp(&other.ray.origin) | ||
.unwrap_or(Ordering::Equal) | ||
} else { | ||
ordering | ||
} | ||
} | ||
} | ||
|
||
impl<'a> PartialOrd for FireScheduleItem<'a> { | ||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { | ||
Some(self.cmp(other)) | ||
} | ||
} | ||
|
||
impl<'a> PartialEq for FireScheduleItem<'a> { | ||
fn eq(&self, other: &Self) -> bool { | ||
self.ray.origin == other.ray.origin && self.cannon.timer() == other.cannon.timer() | ||
} | ||
} | ||
|
||
impl<'a> Eq for FireScheduleItem<'a> {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,25 @@ | ||
use attack::AttackPlugin; | ||
use bevy::{ | ||
app::PluginGroupBuilder, | ||
prelude::{PluginGroup, SystemLabel}, | ||
}; | ||
use laser::LaserPlugin; | ||
|
||
mod attack; | ||
mod laser; | ||
mod sightline; | ||
|
||
pub struct AttackingPluginGroup; | ||
|
||
impl PluginGroup for AttackingPluginGroup { | ||
fn build(&mut self, group: &mut PluginGroupBuilder) { | ||
group.add(LaserPlugin); | ||
group.add(LaserPlugin).add(AttackPlugin); | ||
} | ||
} | ||
|
||
#[derive(Copy, Clone, Hash, Debug, PartialEq, Eq, SystemLabel)] | ||
enum AttackingLabels { | ||
Update, | ||
Aim, | ||
Fire, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -54,7 +54,7 @@ impl ChaseTarget { | |
} | ||
} | ||
|
||
fn entity(&self) -> Entity { | ||
pub fn entity(&self) -> Entity { | ||
self.entity | ||
} | ||
|
||
|