Initial Commit
This commit is contained in:
97
src/braa.rs
Normal file
97
src/braa.rs
Normal file
@@ -0,0 +1,97 @@
|
||||
use std::fmt::Display;
|
||||
|
||||
use guardian_core::components::{Heading, Position};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Aspect {
|
||||
Hot,
|
||||
Flank,
|
||||
Cold,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Aspect {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Hot => write!(f, "Hot"),
|
||||
Self::Flank => write!(f, "Flank"),
|
||||
Self::Cold => write!(f, "Cold"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Braa {
|
||||
bearing: i32,
|
||||
range: i32,
|
||||
angels: i32,
|
||||
aspect: Aspect,
|
||||
}
|
||||
|
||||
impl Display for Braa {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.to_text())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
impl Braa {
|
||||
pub fn new(unit_a: &Position, unit_b: &Position, heading_b: &Heading) -> Self {
|
||||
let bearing = unit_a.get_bearing_to(unit_b);
|
||||
let range = unit_a.distance_to_nmi(unit_b).round() as i32;
|
||||
let angels = unit_b.angels();
|
||||
let aspect = {
|
||||
let bearing = unit_b.get_bearing_to(unit_a);
|
||||
let angle = (bearing - heading_b.0 as i32) % 360;
|
||||
if (45..135).contains(&angle) {
|
||||
Aspect::Flank
|
||||
} else if (135..225).contains(&angle) {
|
||||
Aspect::Cold
|
||||
} else if (225..315).contains(&angle) {
|
||||
Aspect::Flank
|
||||
} else if !(45..=315).contains(&angle) {
|
||||
Aspect::Hot
|
||||
} else {
|
||||
Aspect::Cold
|
||||
}
|
||||
};
|
||||
|
||||
Self {
|
||||
bearing,
|
||||
range,
|
||||
angels,
|
||||
aspect,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_text(&self) -> String {
|
||||
format!(
|
||||
"BRA {}° for {}NM at {} thousand {}",
|
||||
self.bearing, self.range, self.angels, self.aspect
|
||||
)
|
||||
}
|
||||
|
||||
pub fn to_voice(&self) -> String {
|
||||
let bearing = split(&self.bearing.to_string(), 1);
|
||||
let range = split(&self.range.to_string(), 1);
|
||||
format!(
|
||||
"BRA <break time=\"250ms\"/> {} <break /> {} <break /> {} thousand <break /> {}",
|
||||
bearing, range, self.angels, self.aspect
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn split(input: &str, n: usize) -> String {
|
||||
input
|
||||
.chars()
|
||||
.enumerate()
|
||||
.flat_map(|(i, c)| {
|
||||
if i != 0 && i % n == 0 {
|
||||
Some(' ')
|
||||
} else {
|
||||
None
|
||||
}
|
||||
.into_iter()
|
||||
.chain(std::iter::once(c))
|
||||
})
|
||||
.collect::<String>()
|
||||
}
|
||||
85
src/commands/bogeydope.rs
Normal file
85
src/commands/bogeydope.rs
Normal file
@@ -0,0 +1,85 @@
|
||||
use bevy::ecs::{
|
||||
query::{With, Without},
|
||||
system::{Commands, Query, Resource},
|
||||
};
|
||||
use clap::Parser;
|
||||
use guardian_commands::{ConsoleCommand, NamedCommand};
|
||||
use guardian_core::{components::*, dcs::text::TextMessage, srs::voice::VoiceMessage};
|
||||
|
||||
use crate::braa::Braa;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
pub struct BogeyDope;
|
||||
|
||||
impl NamedCommand for BogeyDope {
|
||||
fn name() -> &'static str {
|
||||
"[shopping|bogey dope]"
|
||||
}
|
||||
}
|
||||
|
||||
impl Resource for BogeyDope {}
|
||||
|
||||
pub fn bogey_dope(
|
||||
mut commands: Commands,
|
||||
cmd: ConsoleCommand<BogeyDope>,
|
||||
awacs: Query<&Callsign>,
|
||||
player: Query<(&Callsign, &Position)>,
|
||||
npc: Query<(&Position, &Heading), (Without<Player>, With<Red>)>,
|
||||
) {
|
||||
let Some(pilot) = &cmd.pilot else {
|
||||
return; // no pilot
|
||||
};
|
||||
let Some(operator) = &cmd.operator else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Ok((p_callsign, p_pos)) = player.get(*pilot) else {
|
||||
eprintln!("no player, died?");
|
||||
return;
|
||||
};
|
||||
|
||||
let Ok(a_callsign) = awacs.get(*operator) else {
|
||||
eprintln!("no awacs, they died?");
|
||||
return;
|
||||
};
|
||||
|
||||
let mut distance: f64 = 0.0;
|
||||
let mut n = (None, None);
|
||||
for (n_pos, n_heading) in npc.iter() {
|
||||
let d = p_pos.distance_to_nmi(n_pos);
|
||||
if distance < d {
|
||||
distance = d;
|
||||
n = (Some(n_pos), Some(n_heading));
|
||||
}
|
||||
}
|
||||
|
||||
let (Some(n_position), Some(n_heading)) = n else {
|
||||
let message = format!("{}, {}, {}", p_callsign, a_callsign, "Skies are clear");
|
||||
let voice_message = format!(
|
||||
"{}, {}, {}",
|
||||
p_callsign.to_voice(),
|
||||
a_callsign.to_voice(),
|
||||
"Skies are clear"
|
||||
);
|
||||
|
||||
commands.spawn(TextMessage::new(message));
|
||||
commands
|
||||
.entity(*operator)
|
||||
.insert(VoiceMessage::new(voice_message));
|
||||
return;
|
||||
};
|
||||
|
||||
let bra = Braa::new(p_pos, n_position, n_heading);
|
||||
let message = format!("{}, {}, {}", p_callsign, a_callsign, bra);
|
||||
let voice_message = format!(
|
||||
"{}, {}, {}",
|
||||
p_callsign.to_voice(),
|
||||
a_callsign.to_voice(),
|
||||
bra.to_voice()
|
||||
);
|
||||
|
||||
commands.spawn(TextMessage::new(message));
|
||||
commands
|
||||
.entity(*operator)
|
||||
.insert(VoiceMessage::new(voice_message));
|
||||
}
|
||||
21
src/commands/mod.rs
Normal file
21
src/commands/mod.rs
Normal file
@@ -0,0 +1,21 @@
|
||||
use bevy::app::Plugin;
|
||||
use guardian_commands::AddConsoleCommand;
|
||||
|
||||
mod bogeydope;
|
||||
mod radiocheck;
|
||||
mod tripwire;
|
||||
|
||||
use bogeydope::*;
|
||||
use radiocheck::*;
|
||||
use tripwire::*;
|
||||
|
||||
pub struct CommandsPlugin;
|
||||
|
||||
impl Plugin for CommandsPlugin {
|
||||
fn build(&self, app: &mut bevy::prelude::App) {
|
||||
app.add_plugins(guardian_commands::CommandsPlugin);
|
||||
app.add_console_command::<BogeyDope, _>(bogey_dope);
|
||||
app.add_console_command::<RadioCheck, _>(radio_check);
|
||||
app.add_console_command::<TripwireCommand, _>(set_tripwire);
|
||||
}
|
||||
}
|
||||
51
src/commands/radiocheck.rs
Normal file
51
src/commands/radiocheck.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
use bevy::ecs::system::{Commands, Query, Resource};
|
||||
use clap::Parser;
|
||||
use guardian_commands::{ConsoleCommand, NamedCommand};
|
||||
use guardian_core::{components::*, dcs::text::TextMessage, srs::voice::VoiceMessage};
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
pub struct RadioCheck;
|
||||
|
||||
impl NamedCommand for RadioCheck {
|
||||
fn name() -> &'static str {
|
||||
"radio check"
|
||||
}
|
||||
}
|
||||
|
||||
impl Resource for RadioCheck {}
|
||||
|
||||
pub fn radio_check(
|
||||
mut commands: Commands,
|
||||
cmd: ConsoleCommand<RadioCheck>,
|
||||
callsign: Query<&Callsign>,
|
||||
) {
|
||||
let Some(pilot) = &cmd.pilot else {
|
||||
return; // no pilot
|
||||
};
|
||||
let Some(operator) = &cmd.operator else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Ok(p_callsign) = callsign.get(*pilot) else {
|
||||
eprintln!("no player, died?");
|
||||
return;
|
||||
};
|
||||
|
||||
let Ok(a_callsign) = callsign.get(*operator) else {
|
||||
eprintln!("no awacs, they died?");
|
||||
return;
|
||||
};
|
||||
|
||||
let message = format!("{}, {}, {}", p_callsign, a_callsign, "five by five");
|
||||
let voice_message = format!(
|
||||
"{}, {}, {}",
|
||||
p_callsign.to_voice(),
|
||||
a_callsign.to_voice(),
|
||||
"five by five"
|
||||
);
|
||||
|
||||
commands.spawn(TextMessage::new(message));
|
||||
commands
|
||||
.entity(*operator)
|
||||
.insert(VoiceMessage::new(voice_message));
|
||||
}
|
||||
102
src/commands/tripwire.rs
Normal file
102
src/commands/tripwire.rs
Normal file
@@ -0,0 +1,102 @@
|
||||
use bevy::ecs::system::{Commands, Query, Resource};
|
||||
use clap::Parser;
|
||||
use guardian_commands::{ConsoleCommand, NamedCommand};
|
||||
use guardian_core::{components::*, dcs::text::TextMessage, srs::voice::VoiceMessage};
|
||||
|
||||
use crate::tripwire::Tripwire;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
pub struct TripwireCommand;
|
||||
|
||||
impl NamedCommand for TripwireCommand {
|
||||
fn name() -> &'static str {
|
||||
"set [tripwire|warning]"
|
||||
}
|
||||
}
|
||||
|
||||
impl Resource for TripwireCommand {}
|
||||
|
||||
pub fn set_tripwire(
|
||||
mut commands: Commands,
|
||||
cmd: ConsoleCommand<TripwireCommand>,
|
||||
callsign: Query<&Callsign>,
|
||||
) {
|
||||
let Some(pilot) = &cmd.pilot else {
|
||||
eprintln!("no pilot");
|
||||
return; // no pilot
|
||||
};
|
||||
let Some(operator) = &cmd.operator else {
|
||||
eprintln!("no operator");
|
||||
return;
|
||||
};
|
||||
let Some(readback) = &cmd.raw else {
|
||||
eprintln!("no readback");
|
||||
// no raw found, wut
|
||||
return;
|
||||
};
|
||||
|
||||
let Ok(p_callsign) = callsign.get(*pilot) else {
|
||||
eprintln!("no player, died?");
|
||||
return;
|
||||
};
|
||||
|
||||
let Ok(a_callsign) = callsign.get(*operator) else {
|
||||
eprintln!("no awacs, they died?");
|
||||
return;
|
||||
};
|
||||
|
||||
let mut parts = readback.split(' ');
|
||||
|
||||
// check the last part, if it's a number, interpret as miles
|
||||
let Some(last) = parts.nth_back(0) else {
|
||||
eprintln!("no last part?");
|
||||
// there is no last.. dafuq?
|
||||
// there is always a last, otherwise we wouldnt be here
|
||||
return; // readback error
|
||||
};
|
||||
|
||||
let distance = match last.parse::<i32>() {
|
||||
Ok(distance) => distance, // a number is found, interpret as miles
|
||||
Err(_) => {
|
||||
let Some(maybe_number) = parts.nth_back(0) else {
|
||||
// Report message garbled..
|
||||
return;
|
||||
};
|
||||
|
||||
match maybe_number.parse::<i32>() {
|
||||
Ok(n) => {
|
||||
if last == "kilometers" || last == "km" {
|
||||
(n as f64 * 0.621371).round() as i32
|
||||
} else {
|
||||
n
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
// let message = format!("{}, {}. Unable to read, Say again.", caller, receiver);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// clamp distance between 0 and 120
|
||||
let distance = distance.min(120).max(0);
|
||||
|
||||
commands.entity(*pilot).insert(Tripwire::new(distance));
|
||||
|
||||
let message = format!(
|
||||
"{}, {}, tripwire set to {} miles",
|
||||
p_callsign, a_callsign, distance
|
||||
);
|
||||
let message_voice = format!(
|
||||
"{}, {}, tripwire set to {} miles",
|
||||
p_callsign.to_voice(),
|
||||
a_callsign.to_voice(),
|
||||
distance
|
||||
);
|
||||
|
||||
commands.spawn(TextMessage::new(message));
|
||||
commands
|
||||
.entity(*operator)
|
||||
.insert(VoiceMessage::new(message_voice));
|
||||
}
|
||||
162
src/main.rs
162
src/main.rs
@@ -1,158 +1,50 @@
|
||||
mod braa;
|
||||
mod commands;
|
||||
mod tripwire;
|
||||
|
||||
use bevy::{
|
||||
app::{App, Update},
|
||||
ecs::{
|
||||
component::Component,
|
||||
entity::Entity,
|
||||
query::{Added, With, Without},
|
||||
query::{With, Without},
|
||||
system::{Commands, Query},
|
||||
},
|
||||
};
|
||||
use guardian_core::*;
|
||||
use commands::CommandsPlugin;
|
||||
use guardian_core::{
|
||||
components::{Awacs, Callsign},
|
||||
srs::{Modulation, Radio},
|
||||
DefaultPlugins, TokioResource,
|
||||
};
|
||||
use tokio::runtime::Handle;
|
||||
|
||||
// Every incomming (voice) message is an event
|
||||
// Systems can handle the events, eg bogey dope, set tripwire or radio check
|
||||
// voice responses; srs should be a resource?
|
||||
// |- srs plugin adds a resource to use to send messages i guess
|
||||
// |-- or spawn an entity with a message commands.spawn((VoiceMessage("Hello World"), TextMessage("Hello World!")))
|
||||
// |-- srs plugin will check if these components are Added<VoiceMessage> or w/e and handle accordingly
|
||||
use tripwire::TripwirePlugin;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
App::new()
|
||||
.insert_resource(TokioResource(Handle::current()))
|
||||
.add_plugins(DefaultPlugins)
|
||||
.add_systems(Update, (add_tripwire, tripwire))
|
||||
.add_plugins(TripwirePlugin)
|
||||
.add_plugins(CommandsPlugin)
|
||||
.add_systems(Update, give_awacs_radio)
|
||||
.run();
|
||||
}
|
||||
|
||||
// this will be on a tripwire command event (from srs), instead of Added<Player>, not everyone may want it; opt-in over opt-out
|
||||
fn add_tripwire(mut commands: Commands, players: Query<Entity, Added<Player>>) {
|
||||
for ent in players.iter() {
|
||||
commands.entity(ent).insert(Tripwire {
|
||||
range: 30.0,
|
||||
reported: vec![],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn tripwire(
|
||||
fn give_awacs_radio(
|
||||
mut commands: Commands,
|
||||
mut players: Query<(&Id, &Callsign, &Position, &mut Tripwire), With<Player>>,
|
||||
npc: Query<(&Id, &Position, &Heading, &Side), Without<Player>>,
|
||||
units: Query<(Entity, &Callsign), (With<Awacs>, Without<Radio>)>,
|
||||
) {
|
||||
for (p_id, p_callsign, p_position, mut p_tripwire) in players.iter_mut() {
|
||||
let _p_id = &p_id.0;
|
||||
let _p_callsign = &p_callsign.0;
|
||||
for (n_id, n_position, n_heading, side) in npc.iter() {
|
||||
if side.0 != Coalition::Red {
|
||||
continue;
|
||||
}
|
||||
for (ent, callsign) in units.iter() {
|
||||
if callsign.as_str() == "Overlord1-1" {
|
||||
commands
|
||||
.entity(ent)
|
||||
.insert(Radio::new(251000000, Modulation::Am));
|
||||
}
|
||||
|
||||
let n_id = &n_id.0;
|
||||
|
||||
let distance = n_position.distance_to_nmi(p_position);
|
||||
if distance <= p_tripwire.range && !p_tripwire.reported.contains(n_id) {
|
||||
p_tripwire.reported.push(*n_id);
|
||||
|
||||
let bra = Braa::new(p_position, n_position, n_heading);
|
||||
commands.spawn(TextMessage::new(bra.to_text()));
|
||||
} else if distance > p_tripwire.range && p_tripwire.reported.contains(n_id) {
|
||||
p_tripwire.reported.retain(|b| b != n_id);
|
||||
}
|
||||
if callsign.as_str() == "Magic1-1" {
|
||||
commands
|
||||
.entity(ent)
|
||||
.insert(Radio::new(266000000, Modulation::Am));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
struct Tripwire {
|
||||
range: f64,
|
||||
reported: Vec<u32>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Aspect {
|
||||
Hot,
|
||||
Flank,
|
||||
Cold,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Aspect {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Hot => write!(f, "Hot"),
|
||||
Self::Flank => write!(f, "Flank"),
|
||||
Self::Cold => write!(f, "Cold"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Braa {
|
||||
bearing: i32,
|
||||
range: i32,
|
||||
angels: i32,
|
||||
aspect: Aspect,
|
||||
}
|
||||
|
||||
impl Braa {
|
||||
pub fn new(unit_a: &Position, unit_b: &Position, heading_b: &Heading) -> Self {
|
||||
let bearing = unit_a.get_bearing_to(unit_b);
|
||||
let range = unit_a.distance_to_nmi(unit_b).round() as i32;
|
||||
let angels = unit_b.angels();
|
||||
let aspect = {
|
||||
let bearing = unit_b.get_bearing_to(unit_a);
|
||||
let angle = (bearing - heading_b.0 as i32) % 360;
|
||||
if (45..135).contains(&angle) {
|
||||
Aspect::Flank
|
||||
} else if (135..225).contains(&angle) {
|
||||
Aspect::Cold
|
||||
} else if (225..315).contains(&angle) {
|
||||
Aspect::Flank
|
||||
} else if !(45..=315).contains(&angle) {
|
||||
Aspect::Hot
|
||||
} else {
|
||||
Aspect::Cold
|
||||
}
|
||||
};
|
||||
|
||||
Self {
|
||||
bearing,
|
||||
range,
|
||||
angels,
|
||||
aspect,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_text(&self) -> String {
|
||||
format!(
|
||||
"BRA {}° for {}NM at {} thousand {}",
|
||||
self.bearing, self.range, self.angels, self.aspect
|
||||
)
|
||||
}
|
||||
|
||||
pub fn to_voice(&self) -> String {
|
||||
let bearing = split(&self.bearing.to_string(), 1);
|
||||
let range = split(&self.range.to_string(), 1);
|
||||
format!(
|
||||
"BRA <break time=\"250ms\"/> {} <break /> {} <break /> {} thousand <break /> {}",
|
||||
bearing, range, self.angels, self.aspect
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn split(input: &str, n: usize) -> String {
|
||||
input
|
||||
.chars()
|
||||
.enumerate()
|
||||
.flat_map(|(i, c)| {
|
||||
if i != 0 && i % n == 0 {
|
||||
Some(' ')
|
||||
} else {
|
||||
None
|
||||
}
|
||||
.into_iter()
|
||||
.chain(std::iter::once(c))
|
||||
})
|
||||
.collect::<String>()
|
||||
}
|
||||
|
||||
96
src/tripwire.rs
Normal file
96
src/tripwire.rs
Normal file
@@ -0,0 +1,96 @@
|
||||
use bevy::{
|
||||
app::{Plugin, Update},
|
||||
ecs::{
|
||||
component::Component,
|
||||
entity::Entity,
|
||||
query::{Added, With, Without},
|
||||
system::{Commands, Query},
|
||||
},
|
||||
};
|
||||
use guardian_core::{
|
||||
components::*,
|
||||
dcs::text::TextMessage,
|
||||
srs::{voice::VoiceMessage, Radio, RadioInfo},
|
||||
};
|
||||
|
||||
use crate::braa::Braa;
|
||||
|
||||
pub struct TripwirePlugin;
|
||||
|
||||
impl Plugin for TripwirePlugin {
|
||||
fn build(&self, app: &mut bevy::prelude::App) {
|
||||
app.add_systems(Update, tripwire);
|
||||
}
|
||||
}
|
||||
|
||||
// this will be on a tripwire command event (from srs), instead of Added<Player>, not everyone may want it; opt-in over opt-out
|
||||
// fn add_tripwire(mut commands: Commands, players: Query<Entity, Added<Player>>) {
|
||||
// for ent in players.iter() {
|
||||
// commands.entity(ent).insert(Tripwire {
|
||||
// range: 2.0,
|
||||
// reported: vec![],
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
||||
fn tripwire(
|
||||
mut commands: Commands,
|
||||
awacs: Query<(Entity, &Callsign, &Radio), (With<Awacs>, With<Blue>, With<Radio>)>,
|
||||
mut players: Query<(&Callsign, &Position, &RadioInfo, &mut Tripwire), With<Player>>,
|
||||
npc: Query<(&Id, &Position, &Heading), (Without<Player>, With<Red>)>,
|
||||
) {
|
||||
// get the players radio frequencies
|
||||
// match the tuned frequency with one of the awacs'
|
||||
// broadcast message on that ferquency and as that awacs
|
||||
|
||||
for (ent, awacs, radio) in awacs.iter() {
|
||||
for (p_callsign, p_position, radioinfo, mut p_tripwire) in players.iter_mut() {
|
||||
// check if user is on the same frequency as awacs
|
||||
if !radioinfo
|
||||
.radios
|
||||
.iter()
|
||||
.any(|f| f.freq as u64 == radio.frequency && f.modulation == radio.modulation)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (n_id, n_position, n_heading) in npc.iter() {
|
||||
let distance = n_position.distance_to_nmi(p_position);
|
||||
if distance <= p_tripwire.range && !p_tripwire.reported.contains(n_id) {
|
||||
p_tripwire.reported.push(*n_id);
|
||||
|
||||
let bra = Braa::new(p_position, n_position, n_heading);
|
||||
let message = format!("{}, {} {}", p_callsign, awacs, bra);
|
||||
let voice_message = format!(
|
||||
"{}, {}, {}",
|
||||
p_callsign.to_voice(),
|
||||
awacs.to_voice(),
|
||||
bra.to_voice()
|
||||
);
|
||||
|
||||
commands.spawn(TextMessage::new(message));
|
||||
commands
|
||||
.entity(ent)
|
||||
.insert(VoiceMessage::new(voice_message));
|
||||
} else if distance > p_tripwire.range && p_tripwire.reported.contains(n_id) {
|
||||
p_tripwire.reported.retain(|b| b != n_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Component)]
|
||||
pub struct Tripwire {
|
||||
range: f64,
|
||||
reported: Vec<Id>,
|
||||
}
|
||||
|
||||
impl Tripwire {
|
||||
pub fn new(distance: i32) -> Self {
|
||||
Self {
|
||||
range: distance as f64,
|
||||
reported: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user