cleans up particles, and SFX/ambience w/ placeholders
This commit is contained in:
parent
465cd51a60
commit
e8d376fecf
5 changed files with 142 additions and 67 deletions
|
|
@ -38,56 +38,61 @@ pub fn inflict_damage(ecs: &mut World, damage: &EffectSpawner, target: Entity) {
|
||||||
};
|
};
|
||||||
target_pool.hit_points.current -= ((amount as f32) * mult) as i32;
|
target_pool.hit_points.current -= ((amount as f32) * mult) as i32;
|
||||||
let bleeders = ecs.read_storage::<Bleeds>();
|
let bleeders = ecs.read_storage::<Bleeds>();
|
||||||
if target_pool.hit_points.current < 1 {
|
// If the target bleeds, handle bloodstains and use the bleed colour for sfx.
|
||||||
super::DEAD_ENTITIES.lock().unwrap().push_back(target);
|
if let Some(bleeds) = bleeders.get(target) {
|
||||||
add_effect(damage.source, EffectType::EntityDeath, Targets::Entity { target });
|
if target_pool.hit_points.current < 1 {
|
||||||
for i in 0..3 {
|
super::DEAD_ENTITIES.lock().unwrap().push_back(target);
|
||||||
let sprite = (
|
add_effect(damage.source, EffectType::EntityDeath, Targets::Entity {
|
||||||
match i {
|
target,
|
||||||
0 => "explode1",
|
});
|
||||||
1 => "explode2",
|
for i in 0..3 {
|
||||||
_ => "explode3",
|
let sprite = (
|
||||||
}
|
match i {
|
||||||
).to_string();
|
0 => "explode1",
|
||||||
add_effect(
|
1 => "explode2",
|
||||||
None,
|
_ => "explode3",
|
||||||
EffectType::Particle {
|
}
|
||||||
glyph: to_cp437('‼'),
|
).to_string();
|
||||||
sprite,
|
add_effect(
|
||||||
fg: RGB::named(RED),
|
None,
|
||||||
lifespan: 75.0,
|
EffectType::Particle {
|
||||||
delay: 75.0 * (i as f32),
|
glyph: to_cp437('‼'),
|
||||||
},
|
sprite,
|
||||||
Targets::Entity { target }
|
fg: bleeds.colour,
|
||||||
);
|
lifespan: 75.0,
|
||||||
if let Some(bleeds) = bleeders.get(target) {
|
delay: 75.0 * (i as f32),
|
||||||
|
},
|
||||||
|
Targets::Entity { target }
|
||||||
|
);
|
||||||
add_effect(
|
add_effect(
|
||||||
None,
|
None,
|
||||||
EffectType::Bloodstain { colour: bleeds.colour },
|
EffectType::Bloodstain { colour: bleeds.colour },
|
||||||
Targets::Entity { target }
|
Targets::Entity { target }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
} else {
|
// Regular damage taken effect - use damagetype to determine which one to play.
|
||||||
// Regular damage taken effect - use damagetype to determine which one to play.
|
add_effect(
|
||||||
add_effect(
|
None,
|
||||||
None,
|
EffectType::Particle {
|
||||||
EffectType::Particle {
|
glyph: to_cp437('‼'),
|
||||||
glyph: to_cp437('‼'),
|
sprite: "gnome".to_string(), // FIXME: REMOVE THE GNOMES
|
||||||
sprite: "gnome".to_string(), // FIXME: REMOVE THE GNOMES
|
fg: RGB::named(ORANGE),
|
||||||
fg: RGB::named(ORANGE),
|
lifespan: DEFAULT_PARTICLE_LIFETIME,
|
||||||
lifespan: DEFAULT_PARTICLE_LIFETIME,
|
delay: 0.0,
|
||||||
delay: 0.0,
|
},
|
||||||
},
|
Targets::Entity { target }
|
||||||
Targets::Entity { target }
|
);
|
||||||
);
|
|
||||||
if let Some(bleeds) = bleeders.get(target) {
|
|
||||||
add_effect(
|
add_effect(
|
||||||
None,
|
None,
|
||||||
EffectType::Bloodstain { colour: bleeds.colour },
|
EffectType::Bloodstain { colour: bleeds.colour },
|
||||||
Targets::Entity { target }
|
Targets::Entity { target }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// TODO: Damage taken particle effects when the target does not bleed.
|
||||||
|
// Also damage types, etc.
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ use super::BUC;
|
||||||
use crate::spatial;
|
use crate::spatial;
|
||||||
use bracket_lib::prelude::*;
|
use bracket_lib::prelude::*;
|
||||||
use specs::prelude::*;
|
use specs::prelude::*;
|
||||||
|
use notan::prelude::*;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use crate::components::*;
|
use crate::components::*;
|
||||||
|
|
@ -13,6 +14,7 @@ mod targeting;
|
||||||
mod triggers;
|
mod triggers;
|
||||||
mod attr;
|
mod attr;
|
||||||
mod intrinsics;
|
mod intrinsics;
|
||||||
|
pub mod sound;
|
||||||
|
|
||||||
pub use targeting::aoe_tiles;
|
pub use targeting::aoe_tiles;
|
||||||
|
|
||||||
|
|
@ -63,6 +65,9 @@ pub enum EffectType {
|
||||||
TriggerFire {
|
TriggerFire {
|
||||||
trigger: Entity,
|
trigger: Entity,
|
||||||
},
|
},
|
||||||
|
Sound {
|
||||||
|
sound: String,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|
@ -95,7 +100,7 @@ pub fn add_effect(source: Option<Entity>, effect_type: EffectType, target: Targe
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterates through the effects queue, applying each effect to their target.
|
/// Iterates through the effects queue, applying each effect to their target.
|
||||||
pub fn run_effects_queue(ecs: &mut World) {
|
pub fn run_effects_queue(app: &mut App, ecs: &mut World) {
|
||||||
// First removes any effect in the EFFECT_QUEUE with a dead entity as its source.
|
// First removes any effect in the EFFECT_QUEUE with a dead entity as its source.
|
||||||
loop {
|
loop {
|
||||||
let dead_entity: Option<Entity> = DEAD_ENTITIES.lock().unwrap().pop_front();
|
let dead_entity: Option<Entity> = DEAD_ENTITIES.lock().unwrap().pop_front();
|
||||||
|
|
@ -111,7 +116,7 @@ pub fn run_effects_queue(ecs: &mut World) {
|
||||||
loop {
|
loop {
|
||||||
let effect: Option<EffectSpawner> = EFFECT_QUEUE.lock().unwrap().pop_front();
|
let effect: Option<EffectSpawner> = EFFECT_QUEUE.lock().unwrap().pop_front();
|
||||||
if let Some(effect) = effect {
|
if let Some(effect) = effect {
|
||||||
target_applicator(ecs, &effect);
|
target_applicator(app, ecs, &effect);
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -119,7 +124,7 @@ pub fn run_effects_queue(ecs: &mut World) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Applies an effect to the correct target(s).
|
/// Applies an effect to the correct target(s).
|
||||||
fn target_applicator(ecs: &mut World, effect: &EffectSpawner) {
|
fn target_applicator(app: &mut App, ecs: &mut World, effect: &EffectSpawner) {
|
||||||
// Item use is handled differently - it creates other effects with itself
|
// Item use is handled differently - it creates other effects with itself
|
||||||
// as the source, passing all effects attached to the item into the queue.
|
// as the source, passing all effects attached to the item into the queue.
|
||||||
if let EffectType::ItemUse { item } = effect.effect_type {
|
if let EffectType::ItemUse { item } = effect.effect_type {
|
||||||
|
|
@ -131,25 +136,26 @@ fn target_applicator(ecs: &mut World, effect: &EffectSpawner) {
|
||||||
}
|
}
|
||||||
// Otherwise, just match the effect and enact it directly.
|
// Otherwise, just match the effect and enact it directly.
|
||||||
match &effect.target {
|
match &effect.target {
|
||||||
Targets::Tile { target } => affect_tile(ecs, effect, *target),
|
Targets::Tile { target } => affect_tile(app, ecs, effect, *target),
|
||||||
Targets::TileList { targets } =>
|
Targets::TileList { targets } =>
|
||||||
targets.iter().for_each(|target| affect_tile(ecs, effect, *target)),
|
targets.iter().for_each(|target| affect_tile(app, ecs, effect, *target)),
|
||||||
Targets::Entity { target } => affect_entity(ecs, effect, *target),
|
Targets::Entity { target } => affect_entity(app, ecs, effect, *target),
|
||||||
Targets::EntityList { targets } =>
|
Targets::EntityList { targets } =>
|
||||||
targets.iter().for_each(|target| affect_entity(ecs, effect, *target)),
|
targets.iter().for_each(|target| affect_entity(app, ecs, effect, *target)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs an effect on a given tile index
|
/// Runs an effect on a given tile index
|
||||||
fn affect_tile(ecs: &mut World, effect: &EffectSpawner, target: usize) {
|
fn affect_tile(app: &mut App, ecs: &mut World, effect: &EffectSpawner, target: usize) {
|
||||||
if tile_effect_hits_entities(&effect.effect_type) {
|
if tile_effect_hits_entities(&effect.effect_type) {
|
||||||
spatial::for_each_tile_content(target, |entity| {
|
spatial::for_each_tile_content(target, |entity| {
|
||||||
affect_entity(ecs, effect, entity);
|
affect_entity(app, ecs, effect, entity);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
match &effect.effect_type {
|
match &effect.effect_type {
|
||||||
EffectType::Particle { .. } => particles::particle_to_tile(ecs, target as i32, &effect),
|
EffectType::Particle { .. } => particles::particle_to_tile(ecs, target as i32, &effect),
|
||||||
|
EffectType::Sound { .. } => sound::play_sound(app, ecs, &effect, target),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
// Run the effect
|
// Run the effect
|
||||||
|
|
@ -168,7 +174,7 @@ fn tile_effect_hits_entities(effect: &EffectType) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs an effect on a given entity
|
/// Runs an effect on a given entity
|
||||||
fn affect_entity(ecs: &mut World, effect: &EffectSpawner, target: Entity) {
|
fn affect_entity(app: &mut App, ecs: &mut World, effect: &EffectSpawner, target: Entity) {
|
||||||
match &effect.effect_type {
|
match &effect.effect_type {
|
||||||
EffectType::Damage { .. } => damage::inflict_damage(ecs, effect, target),
|
EffectType::Damage { .. } => damage::inflict_damage(ecs, effect, target),
|
||||||
EffectType::Healing { .. } => damage::heal_damage(ecs, effect, target),
|
EffectType::Healing { .. } => damage::heal_damage(ecs, effect, target),
|
||||||
|
|
@ -187,6 +193,11 @@ fn affect_entity(ecs: &mut World, effect: &EffectSpawner, target: Entity) {
|
||||||
EffectType::EntityDeath => damage::entity_death(ecs, effect, target),
|
EffectType::EntityDeath => damage::entity_death(ecs, effect, target),
|
||||||
EffectType::ModifyNutrition { .. } => hunger::modify_nutrition(ecs, effect, target),
|
EffectType::ModifyNutrition { .. } => hunger::modify_nutrition(ecs, effect, target),
|
||||||
EffectType::AddIntrinsic { .. } => intrinsics::add_intrinsic(ecs, effect, target),
|
EffectType::AddIntrinsic { .. } => intrinsics::add_intrinsic(ecs, effect, target),
|
||||||
|
EffectType::Sound { .. } => {
|
||||||
|
if let Some(pos) = targeting::entity_position(ecs, target) {
|
||||||
|
sound::play_sound(app, ecs, &effect, pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
65
src/effects/sound.rs
Normal file
65
src/effects/sound.rs
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
use bracket_lib::prelude::*;
|
||||||
|
use notan::prelude::*;
|
||||||
|
use specs::prelude::*;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use super::{ EffectSpawner, EffectType };
|
||||||
|
use crate::Map;
|
||||||
|
|
||||||
|
lazy_static::lazy_static! {
|
||||||
|
pub static ref SOUNDS: Mutex<HashMap<String, (AudioSource, AudioType)>> = Mutex::new(HashMap::new());
|
||||||
|
pub static ref VOLUME: Mutex<f32> = Mutex::new(1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Copy, Clone)]
|
||||||
|
pub enum AudioType {
|
||||||
|
Ambient,
|
||||||
|
SFX,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn play_sound(app: &mut App, ecs: &mut World, effect: &EffectSpawner, target: usize) {
|
||||||
|
// Extract sound from the EffectType, or panic if we somehow called this with the wrong effect.
|
||||||
|
let sound = if let EffectType::Sound { sound } = &effect.effect_type {
|
||||||
|
sound
|
||||||
|
} else {
|
||||||
|
unreachable!("add_intrinsic() called with the wrong EffectType")
|
||||||
|
};
|
||||||
|
// Fetch all the relevant precursors.
|
||||||
|
let sounds = SOUNDS.lock().unwrap();
|
||||||
|
let volume = VOLUME.lock().unwrap();
|
||||||
|
let source = sounds.get(sound).unwrap();
|
||||||
|
let (vol, repeat) = match source.1 {
|
||||||
|
AudioType::Ambient => (*volume * 0.5, true),
|
||||||
|
AudioType::SFX => {
|
||||||
|
let map = ecs.fetch::<Map>();
|
||||||
|
let ppos = ecs.fetch::<Point>();
|
||||||
|
// Calc distance from player to target.
|
||||||
|
let dist = DistanceAlg::PythagorasSquared.distance2d(
|
||||||
|
*ppos,
|
||||||
|
Point::new((target as i32) % map.width, (target as i32) / map.width)
|
||||||
|
);
|
||||||
|
// Play sound at volume proportional to distance.
|
||||||
|
(*volume * (1.0 - (dist as f32) / 14.0), false)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Play the sound.
|
||||||
|
app.audio.play_sound(&source.0, vol, repeat);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_sounds(app: &mut App) {
|
||||||
|
let list: Vec<(&str, (&[u8], AudioType))> = vec![
|
||||||
|
//key, (bytes, type) - audiotype determines final volume, looping, etc.
|
||||||
|
("hit", (include_bytes!("../../resources/sounds/hit.wav"), AudioType::Ambient)),
|
||||||
|
("other", (include_bytes!("../../resources/sounds/hit.wav"), AudioType::SFX)),
|
||||||
|
("another", (include_bytes!("../../resources/sounds/hit.wav"), AudioType::SFX))
|
||||||
|
];
|
||||||
|
let mut sounds = SOUNDS.lock().unwrap();
|
||||||
|
for (k, (bytes, audiotype)) in list.iter() {
|
||||||
|
sounds.insert(k.to_string(), (app.audio.create_source(bytes).unwrap(), *audiotype));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_volume(vol: f32) {
|
||||||
|
let mut volume = VOLUME.lock().unwrap();
|
||||||
|
*volume = vol;
|
||||||
|
}
|
||||||
10
src/main.rs
10
src/main.rs
|
|
@ -33,14 +33,8 @@ fn main() -> Result<(), String> {
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup(_app: &mut App, gfx: &mut Graphics) -> State {
|
fn setup(app: &mut App, gfx: &mut Graphics) -> State {
|
||||||
/*
|
effects::sound::init_sounds(app);
|
||||||
let sound = app.audio.create_source(include_bytes!("../resources/sounds/hit.wav")).unwrap();
|
|
||||||
let sounds: HashMap<String, AudioSource> = vec![("hit".to_string(), sound)]
|
|
||||||
.into_iter()
|
|
||||||
.collect();
|
|
||||||
*/
|
|
||||||
|
|
||||||
let texture = gfx
|
let texture = gfx
|
||||||
.create_texture()
|
.create_texture()
|
||||||
.from_image(include_bytes!("../resources/atlas.png"))
|
.from_image(include_bytes!("../resources/atlas.png"))
|
||||||
|
|
|
||||||
|
|
@ -85,23 +85,23 @@ impl State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_systems(&mut self) {
|
fn run_systems(&mut self, ctx: &mut App) {
|
||||||
let mut hunger_clock = hunger_system::HungerSystem {};
|
let mut hunger_clock = hunger_system::HungerSystem {};
|
||||||
let mut particle_system = particle_system::ParticleSpawnSystem {};
|
let mut particle_system = particle_system::ParticleSpawnSystem {};
|
||||||
|
|
||||||
// Order is *very* important here, to ensure effects take place in the right order,
|
// Order is *very* important here, to ensure effects take place in the right order,
|
||||||
// and that the player/AI are making decisions based on the most up-to-date info.
|
// and that the player/AI are making decisions based on the most up-to-date info.
|
||||||
|
|
||||||
self.resolve_entity_decisions(); // Push Player messages of intent to effects queue, and run it.
|
self.resolve_entity_decisions(ctx); // Push Player messages of intent to effects queue, and run it.
|
||||||
self.refresh_indexes(); // Get up-to-date map and viewsheds prior to AI turn.
|
self.refresh_indexes(); // Get up-to-date map and viewsheds prior to AI turn.
|
||||||
self.run_ai(); // Get AI decision-making.
|
self.run_ai(); // Get AI decision-making.
|
||||||
self.resolve_entity_decisions(); // Push AI messages of intent to effects queue, and run it.
|
self.resolve_entity_decisions(ctx); // Push AI messages of intent to effects queue, and run it.
|
||||||
hunger_clock.run_now(&self.ecs); // Tick the hunger clock (on the turn clock's turn)
|
hunger_clock.run_now(&self.ecs); // Tick the hunger clock (on the turn clock's turn)
|
||||||
particle_system.run_now(&self.ecs); // Spawn/delete particles (turn independent)
|
particle_system.run_now(&self.ecs); // Spawn/delete particles (turn independent)
|
||||||
self.ecs.maintain();
|
self.ecs.maintain();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_entity_decisions(&mut self) {
|
fn resolve_entity_decisions(&mut self, ctx: &mut App) {
|
||||||
let mut trigger_system = trigger_system::TriggerSystem {};
|
let mut trigger_system = trigger_system::TriggerSystem {};
|
||||||
let mut item_equip_system = inventory::ItemEquipSystem {};
|
let mut item_equip_system = inventory::ItemEquipSystem {};
|
||||||
let mut item_use_system = inventory::ItemUseSystem {};
|
let mut item_use_system = inventory::ItemUseSystem {};
|
||||||
|
|
@ -121,7 +121,7 @@ impl State {
|
||||||
key_system.run_now(&self.ecs);
|
key_system.run_now(&self.ecs);
|
||||||
melee_system.run_now(&self.ecs);
|
melee_system.run_now(&self.ecs);
|
||||||
|
|
||||||
effects::run_effects_queue(&mut self.ecs);
|
effects::run_effects_queue(ctx, &mut self.ecs);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn refresh_indexes(&mut self) {
|
fn refresh_indexes(&mut self) {
|
||||||
|
|
@ -207,13 +207,13 @@ impl State {
|
||||||
particle_system::particle_ticker(&mut self.ecs, ctx);
|
particle_system::particle_ticker(&mut self.ecs, ctx);
|
||||||
match new_runstate {
|
match new_runstate {
|
||||||
RunState::PreRun => {
|
RunState::PreRun => {
|
||||||
self.run_systems();
|
self.run_systems(ctx);
|
||||||
self.ecs.maintain();
|
self.ecs.maintain();
|
||||||
new_runstate = RunState::AwaitingInput;
|
new_runstate = RunState::AwaitingInput;
|
||||||
}
|
}
|
||||||
RunState::AwaitingInput => {
|
RunState::AwaitingInput => {
|
||||||
self.refresh_indexes();
|
self.refresh_indexes();
|
||||||
effects::run_effects_queue(&mut self.ecs);
|
effects::run_effects_queue(ctx, &mut self.ecs);
|
||||||
let mut can_act = false;
|
let mut can_act = false;
|
||||||
{
|
{
|
||||||
let player_entity = self.ecs.fetch::<Entity>();
|
let player_entity = self.ecs.fetch::<Entity>();
|
||||||
|
|
@ -231,7 +231,7 @@ impl State {
|
||||||
}
|
}
|
||||||
RunState::Ticking => {
|
RunState::Ticking => {
|
||||||
while new_runstate == RunState::Ticking && particle_system::check_queue(&self.ecs) {
|
while new_runstate == RunState::Ticking && particle_system::check_queue(&self.ecs) {
|
||||||
self.run_systems();
|
self.run_systems(ctx);
|
||||||
self.ecs.maintain();
|
self.ecs.maintain();
|
||||||
try_spawn_interval(&mut self.ecs);
|
try_spawn_interval(&mut self.ecs);
|
||||||
maybe_map_message(&mut self.ecs);
|
maybe_map_message(&mut self.ecs);
|
||||||
|
|
@ -585,7 +585,7 @@ impl State {
|
||||||
|
|
||||||
match new_runstate {
|
match new_runstate {
|
||||||
RunState::PreRun => {
|
RunState::PreRun => {
|
||||||
self.run_systems();
|
//self.run_systems();
|
||||||
self.ecs.maintain();
|
self.ecs.maintain();
|
||||||
new_runstate = RunState::AwaitingInput;
|
new_runstate = RunState::AwaitingInput;
|
||||||
}
|
}
|
||||||
|
|
@ -594,7 +594,7 @@ impl State {
|
||||||
// still be in the queue, just to make 100% sure that
|
// still be in the queue, just to make 100% sure that
|
||||||
// there are no lingering effects from the last tick.
|
// there are no lingering effects from the last tick.
|
||||||
self.refresh_indexes();
|
self.refresh_indexes();
|
||||||
effects::run_effects_queue(&mut self.ecs);
|
//effects::run_effects_queue(&mut self.ecs);
|
||||||
// Sanity-checking that the player actually *should*
|
// Sanity-checking that the player actually *should*
|
||||||
// be taking a turn before giving them one. If they
|
// be taking a turn before giving them one. If they
|
||||||
// don't have a turn component, go back to ticking.
|
// don't have a turn component, go back to ticking.
|
||||||
|
|
@ -615,7 +615,7 @@ impl State {
|
||||||
}
|
}
|
||||||
RunState::Ticking => {
|
RunState::Ticking => {
|
||||||
while new_runstate == RunState::Ticking && particle_system::check_queue(&self.ecs) {
|
while new_runstate == RunState::Ticking && particle_system::check_queue(&self.ecs) {
|
||||||
self.run_systems();
|
//self.run_systems();
|
||||||
self.ecs.maintain();
|
self.ecs.maintain();
|
||||||
try_spawn_interval(&mut self.ecs);
|
try_spawn_interval(&mut self.ecs);
|
||||||
maybe_map_message(&mut self.ecs);
|
maybe_map_message(&mut self.ecs);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue