From d9606e8b8725a4175e0c5c3ece0d910a99a23030 Mon Sep 17 00:00:00 2001 From: Alex Page Date: Thu, 3 Feb 2022 00:07:12 -0500 Subject: [PATCH] Switch from specs to hecs --- Cargo.lock | 124 +++++---------------------- Cargo.toml | 2 +- src/components/mod.rs | 6 +- src/components/monster.rs | 4 +- src/components/player.rs | 5 +- src/components/position.rs | 4 +- src/components/renderable.rs | 4 +- src/graphics/entities.rs | 7 +- src/graphics/map.rs | 6 +- src/graphics/mod.rs | 12 +-- src/graphics/sidebar.rs | 13 ++- src/graphics/whip.rs | 12 ++- src/input/mod.rs | 39 ++++----- src/input/player.rs | 118 +++++++++++++------------- src/main.rs | 78 ++++++++--------- src/{ => resources}/difficulty.rs | 0 src/resources/map.rs | 89 ++++++++++---------- src/resources/mod.rs | 50 ++++------- src/resources/stats.rs | 27 ++++++ src/state.rs | 18 ++-- src/systems/mod.rs | 26 ++---- src/systems/monster_ai.rs | 92 ++++++++++++++++++++ src/systems/monster_ai_system.rs | 120 -------------------------- src/systems/time.rs | 36 ++++++++ src/systems/time_system.rs | 45 ---------- src/systems/whip.rs | 115 +++++++++++++++++++++++++ src/systems/whip_system.rs | 135 ------------------------------ 27 files changed, 514 insertions(+), 673 deletions(-) rename src/{ => resources}/difficulty.rs (100%) create mode 100644 src/resources/stats.rs create mode 100644 src/systems/monster_ai.rs delete mode 100644 src/systems/monster_ai_system.rs create mode 100644 src/systems/time.rs delete mode 100644 src/systems/time_system.rs create mode 100644 src/systems/whip.rs delete mode 100644 src/systems/whip_system.rs diff --git a/Cargo.lock b/Cargo.lock index 867133a..4d6f327 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22,9 +22,14 @@ checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" [[package]] name = "ahash" -version = "0.3.8" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] [[package]] name = "aho-corasick" @@ -82,18 +87,6 @@ version = "1.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94a45b455c14666b85fc40a019e8ab9eb75e3a124e05494f5397122bc9eb06e0" -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - -[[package]] -name = "atom" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9ff149ed9780025acfdb36862d35b28856bb693ceb451259a7164442f22fdc3" - [[package]] name = "autocfg" version = "1.0.1" @@ -543,7 +536,7 @@ dependencies = [ "crossbeam-channel", "crossbeam-deque", "crossbeam-epoch", - "crossbeam-queue 0.3.3", + "crossbeam-queue", "crossbeam-utils 0.8.6", ] @@ -581,17 +574,6 @@ dependencies = [ "scopeguard", ] -[[package]] -name = "crossbeam-queue" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" -dependencies = [ - "cfg-if 0.1.10", - "crossbeam-utils 0.7.2", - "maybe-uninit", -] - [[package]] name = "crossbeam-queue" version = "0.3.3" @@ -821,9 +803,9 @@ checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" [[package]] name = "fundsp" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e44dcde4aedad5675c131b5114d431b4d7c739dc32dd599bf0460f36413d247e" +checksum = "914385c9ea3226815d12f436c2c60b2e6f88a89af684ed9e24d62e55f1b0dba1" dependencies = [ "generic-array", "lazy_static", @@ -964,21 +946,20 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.7.2" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96282e96bfcd3da0d3aa9938bedf1e50df3269b6db08b4876d2da0bb1a0841cf" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" dependencies = [ "ahash", - "autocfg", ] [[package]] -name = "hibitset" -version = "0.6.3" +name = "hecs" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93a1bb8316a44459a7d14253c4d28dd7395cbd23cc04a68c46e851b8e46d64b1" +checksum = "ddd0e227c3f78ad06fcc590966b327a2f8fef03ef51812dad4fbff5b0a6424bd" dependencies = [ - "atom", + "hashbrown", ] [[package]] @@ -1091,8 +1072,8 @@ dependencies = [ "bracket-lib", "cpal", "fundsp", + "hecs", "oddio", - "specs", "specs-derive", "typenum", "winres", @@ -1112,9 +1093,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.116" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "565dbd88872dbe4cc8a46e527f26483c1d1f7afa6b884a3bd6cd893d4f98da74" +checksum = "e74d72e0f9b65b5b4ca49a346af3976df0f9c61d550727f349ecd559f251a26c" [[package]] name = "libloading" @@ -1172,12 +1153,6 @@ dependencies = [ "libc", ] -[[package]] -name = "maybe-uninit" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" - [[package]] name = "memchr" version = "2.4.1" @@ -1229,9 +1204,9 @@ dependencies = [ [[package]] name = "mint" -version = "0.5.8" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162e591484b4b8fe9e1ca16ebf07ab584fdc3334508d76a788cd54d89cfc20dc" +checksum = "e53debba6bda7a793e5f99b8dacf19e626084f525f7829104ba9898f367d85ff" [[package]] name = "mio" @@ -1284,7 +1259,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b47412f3a52115b936ff2a229b803498c7b4d332adeb87c2f1498c9da54c398c" dependencies = [ "crossbeam", - "crossbeam-queue 0.3.3", + "crossbeam-queue", "log", "mio 0.7.14", ] @@ -1310,12 +1285,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "mopa" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a785740271256c230f57462d3b83e52f998433a7062fc18f96d5999474a9f915" - [[package]] name = "ndk" version = "0.2.1" @@ -1987,25 +1956,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" -[[package]] -name = "shred" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f08237e667ac94ad20f8878b5943d91a93ccb231428446c57c21c57779016d" -dependencies = [ - "arrayvec", - "hashbrown", - "mopa", - "smallvec", - "tynm", -] - -[[package]] -name = "shrev" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5752e017e03af9d735b4b069f53b7a7fd90fefafa04d8bd0c25581b0bff437f" - [[package]] name = "slab" version = "0.4.5" @@ -2046,21 +1996,6 @@ dependencies = [ "wayland-protocols", ] -[[package]] -name = "specs" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff28a29366aff703d5da8a7e2c8875dc8453ac1118f842cbc0fa70c7db51240" -dependencies = [ - "crossbeam-queue 0.2.3", - "hashbrown", - "hibitset", - "log", - "shred", - "shrev", - "tuple_utils", -] - [[package]] name = "specs-derive" version = "0.4.1" @@ -2199,21 +2134,6 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e5d7cd7ab3e47dda6e56542f4bbf3824c15234958c6e1bd6aaa347e93499fdc" -[[package]] -name = "tuple_utils" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44834418e2c5b16f47bedf35c28e148db099187dd5feee6367fb2525863af4f1" - -[[package]] -name = "tynm" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4df2caa2dc9c3d1f7641ba981f4cd40ab229775aa7aeb834c9ab2850d50623d" -dependencies = [ - "nom 5.1.2", -] - [[package]] name = "typenum" version = "1.15.0" diff --git a/Cargo.toml b/Cargo.toml index 374353b..82c7fad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ build = "build.rs" [dependencies] # bracket-lib = { path = "extern/bracket-lib" } bracket-lib = { git = "https://github.com/amethyst/bracket-lib" } -specs = { version = "0.16.1", default-features = false } +hecs = "0.5.1" specs-derive = "0.4.1" cpal = "0.13" oddio = "0.5" diff --git a/src/components/mod.rs b/src/components/mod.rs index 2875e23..a44df4b 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -1,8 +1,5 @@ use std::time::Instant; -use specs::prelude::*; -use specs_derive::Component; - pub mod monster; pub mod player; pub mod position; @@ -15,9 +12,10 @@ pub use renderable::Renderable; use crate::resources::sound_output::SoundEffectHandle; -#[derive(Component)] pub struct WantsToWhip { pub frame: u8, pub last_frame: Instant, pub sound: Option, } + +pub struct Killed {} diff --git a/src/components/monster.rs b/src/components/monster.rs index 7aa6c32..a62a453 100644 --- a/src/components/monster.rs +++ b/src/components/monster.rs @@ -3,10 +3,8 @@ use crate::{ resources::{sound_output::SoundSamples, SoundEffects}, }; use bracket_lib::prelude::*; -use specs::prelude::*; -use specs_derive::Component; -#[derive(Component, Debug)] +#[derive(Debug)] pub struct Monster { pub kind: MonsterKind, pub ticks_until_move: i32, diff --git a/src/components/player.rs b/src/components/player.rs index 3f2c48d..1b1e03e 100644 --- a/src/components/player.rs +++ b/src/components/player.rs @@ -1,9 +1,6 @@ use std::time::Instant; -use specs::prelude::*; -use specs_derive::Component; - -#[derive(Component, Debug)] +#[derive(Debug)] pub struct Player { pub last_moved: Instant, } diff --git a/src/components/position.rs b/src/components/position.rs index a6f7935..1fdd49d 100644 --- a/src/components/position.rs +++ b/src/components/position.rs @@ -1,8 +1,6 @@ use bracket_lib::prelude::*; -use specs::prelude::*; -use specs_derive::Component; -#[derive(Component, Clone, Copy)] +#[derive(Clone, Copy, Debug)] pub struct Position { pub x: i32, pub y: i32, diff --git a/src/components/renderable.rs b/src/components/renderable.rs index 0cfe390..198fd93 100644 --- a/src/components/renderable.rs +++ b/src/components/renderable.rs @@ -1,8 +1,6 @@ use bracket_lib::prelude::*; -use specs::prelude::*; -use specs_derive::Component; -#[derive(Component)] +#[derive(Debug)] pub struct Renderable { pub glyph: FontCharType, pub fg: RGB, diff --git a/src/graphics/entities.rs b/src/graphics/entities.rs index 9086204..51b33f3 100644 --- a/src/graphics/entities.rs +++ b/src/graphics/entities.rs @@ -1,13 +1,10 @@ use bracket_lib::prelude::*; -use specs::prelude::*; +use hecs::World; use crate::{components::*, constants::*}; pub fn draw(world: &World, bterm: &mut BTerm) { - let positions = world.read_storage::(); - let renderables = world.read_storage::(); - - for (pos, render) in (&positions, &renderables).join() { + for (_, (pos, render)) in &mut world.query::<(&Position, &Renderable)>() { bterm.set( pos.x + MAP_X as i32, pos.y + MAP_Y as i32, diff --git a/src/graphics/map.rs b/src/graphics/map.rs index 0731052..7602cf6 100644 --- a/src/graphics/map.rs +++ b/src/graphics/map.rs @@ -1,9 +1,7 @@ use bracket_lib::prelude::*; -use specs::prelude::*; use crate::resources::*; -pub fn draw(world: &World, bterm: &mut BTerm) { - let map = world.fetch::(); - map.draw(bterm); +pub fn draw(resources: &Resources, bterm: &mut BTerm) { + resources.map.draw(bterm); } diff --git a/src/graphics/mod.rs b/src/graphics/mod.rs index 4b148ba..c2ef34b 100644 --- a/src/graphics/mod.rs +++ b/src/graphics/mod.rs @@ -1,5 +1,7 @@ use bracket_lib::prelude::*; -use specs::prelude::*; +use hecs::World; + +use crate::resources::Resources; mod entities; mod map; @@ -7,10 +9,10 @@ mod sidebar; pub mod vga_color; mod whip; -pub fn draw(world: &World, bterm: &mut BTerm) { +pub fn draw(world: &World, resources: &Resources, bterm: &mut BTerm) { bterm.cls(); - map::draw(world, bterm); + map::draw(resources, bterm); entities::draw(world, bterm); - whip::draw(world, bterm); - sidebar::draw(world, bterm); + whip::draw(world, resources, bterm); + sidebar::draw(resources, bterm); } diff --git a/src/graphics/sidebar.rs b/src/graphics/sidebar.rs index bd3ef9d..8d86c24 100644 --- a/src/graphics/sidebar.rs +++ b/src/graphics/sidebar.rs @@ -1,10 +1,9 @@ use crate::constants::{SIDEBAR_POS_X, SIDEBAR_POS_Y}; use crate::graphics::vga_color as vga; -use crate::resources::{Clock, LevelNumber, ShowDebugInfo, Stats}; +use crate::resources::Resources; use bracket_lib::prelude::*; -use specs::prelude::*; -pub fn draw(world: &World, bterm: &mut BTerm) { +pub fn draw(resources: &Resources, bterm: &mut BTerm) { // Blue background bterm.fill_region( Rect { @@ -39,14 +38,14 @@ pub fn draw(world: &World, bterm: &mut BTerm) { bterm.print_centered_at( SIDEBAR_POS_X + 6, SIDEBAR_POS_Y + 4, - world.read_resource::().0 + 1, + resources.level_number + 1, ); bterm.print(SIDEBAR_POS_X + 4, SIDEBAR_POS_Y + 6, "Gems"); bterm.print(SIDEBAR_POS_X + 4, SIDEBAR_POS_Y + 9, "Whips"); bterm.print(SIDEBAR_POS_X + 2, SIDEBAR_POS_Y + 12, "Teleports"); bterm.print(SIDEBAR_POS_X + 4, SIDEBAR_POS_Y + 15, "Keys"); - let stats = world.read_resource::(); + let stats = &resources.stats; bterm.print_centered_at(SIDEBAR_POS_X + 6, SIDEBAR_POS_Y + 1, stats.score); bterm.print_centered_at(SIDEBAR_POS_X + 6, SIDEBAR_POS_Y + 7, stats.gems); bterm.print_centered_at(SIDEBAR_POS_X + 6, SIDEBAR_POS_Y + 10, stats.whips); @@ -93,7 +92,7 @@ pub fn draw(world: &World, bterm: &mut BTerm) { bterm.print(SIDEBAR_POS_X + 3, SIDEBAR_POS_Y + 23, "Save"); bterm.print(SIDEBAR_POS_X + 3, SIDEBAR_POS_Y + 24, "Restore"); - if world.read_resource::().0 { + if resources.show_debug_info { bterm.print_color_right( SIDEBAR_POS_X + 14, SIDEBAR_POS_Y, @@ -107,7 +106,7 @@ pub fn draw(world: &World, bterm: &mut BTerm) { 0, RGB::named(vga::YELLOW_BRIGHT), RGB::named(vga::BLACK), - &format!("{}", world.read_resource::().ticks), + &format!("{}", resources.clock.ticks), ); } } diff --git a/src/graphics/whip.rs b/src/graphics/whip.rs index f20fff3..1e90e73 100644 --- a/src/graphics/whip.rs +++ b/src/graphics/whip.rs @@ -1,16 +1,14 @@ use bracket_lib::prelude::*; -use specs::prelude::*; +use hecs::World; use crate::{components::*, constants::*, resources::*}; use super::{vga_color as vga, vga_color::get_by_index}; -pub fn draw(world: &World, bterm: &mut BTerm) { - let positions = world.read_storage::(); - let wants_to_whips = world.read_storage::(); - let map = world.read_resource::(); +pub fn draw(world: &World, resources: &Resources, bterm: &mut BTerm) { let mut rng = RandomNumberGenerator::new(); - for (position, wants_to_whip) in (&positions, &wants_to_whips).join() { + + for (_, (position, wants_to_whip)) in &mut world.query::<(&Position, &WantsToWhip)>() { let color = RGB::named(get_by_index(rng.range(1, 16))); let mut rendered_frame = wants_to_whip.frame; let frame_data = loop { @@ -31,7 +29,7 @@ pub fn draw(world: &World, bterm: &mut BTerm) { x: position.x + data.0, y: position.y + data.1, }; - if map.in_bounds(dest) { + if resources.map.in_bounds(dest) { break frame_data; } rendered_frame += 1; diff --git a/src/input/mod.rs b/src/input/mod.rs index 4da204b..6ce8527 100644 --- a/src/input/mod.rs +++ b/src/input/mod.rs @@ -1,49 +1,50 @@ use bracket_lib::prelude::*; -use specs::prelude::*; +use hecs::World; use crate::resources::*; mod player; -pub fn handle(world: &World, bterm: &mut BTerm) { +pub fn handle(world: &mut World, resources: &mut Resources, bterm: &mut BTerm) { match bterm.key { None => {} Some(key) => match key { VirtualKeyCode::Left | VirtualKeyCode::J => { - player::try_move(-1, 0, world); + player::try_move(-1, 0, world, resources); } - VirtualKeyCode::U | VirtualKeyCode::Home => player::try_move(-1, -1, world), + VirtualKeyCode::U | VirtualKeyCode::Home => player::try_move(-1, -1, world, resources), VirtualKeyCode::Up | VirtualKeyCode::I => { - player::try_move(0, -1, world); + player::try_move(0, -1, world, resources); } - VirtualKeyCode::O | VirtualKeyCode::PageUp => player::try_move(1, -1, world), + VirtualKeyCode::O | VirtualKeyCode::PageUp => player::try_move(1, -1, world, resources), VirtualKeyCode::Right | VirtualKeyCode::K => { - player::try_move(1, 0, world); + player::try_move(1, 0, world, resources); + } + VirtualKeyCode::Comma | VirtualKeyCode::PageDown => { + player::try_move(1, 1, world, resources) } - VirtualKeyCode::Comma | VirtualKeyCode::PageDown => player::try_move(1, 1, world), VirtualKeyCode::Down | VirtualKeyCode::M => { - player::try_move(0, 1, world); + player::try_move(0, 1, world, resources); } - VirtualKeyCode::N | VirtualKeyCode::End => player::try_move(-1, 1, world), + VirtualKeyCode::N | VirtualKeyCode::End => player::try_move(-1, 1, world, resources), VirtualKeyCode::W => { - player::whip(world); + player::whip(world, resources); } VirtualKeyCode::S => { - let mut sound_system = world.write_resource::(); - let sound_effects = world.fetch::(); - sound_system.play_sound(sound_effects.pickup.clone()); + resources + .sound_output + .play_sound(resources.sound_effects.pickup.clone()); } VirtualKeyCode::D => { - let mut show_debug_info = world.write_resource::(); - show_debug_info.0 = !show_debug_info.0; + resources.show_debug_info = !resources.show_debug_info; } VirtualKeyCode::Escape | VirtualKeyCode::Q => { bterm.quit(); } _ => { - let mut sound_system = world.write_resource::(); - let sound_effects = world.fetch::(); - sound_system.play_sound(sound_effects.bad_key.clone()); + resources + .sound_output + .play_sound(resources.sound_effects.bad_key.clone()); } }, } diff --git a/src/input/player.rs b/src/input/player.rs index 3d38fe5..86163a6 100644 --- a/src/input/player.rs +++ b/src/input/player.rs @@ -1,26 +1,16 @@ use std::time::{Duration, Instant}; use bracket_lib::prelude::*; -use specs::prelude::*; +use hecs::{Entity, With, Without, World}; -use crate::{ - components::monster::*, components::*, constants::*, resources::*, systems::TimeSystem, -}; +use crate::{components::monster::*, components::*, constants::*, resources::*, systems}; -pub fn try_move(delta_x: i32, delta_y: i32, world: &World) { - let entities = world.entities(); - let mut positions = world.write_storage::(); - let mut players = world.write_storage::(); - let monsters = world.write_storage::(); - let mut map = world.write_resource::(); - let mut stats = world.write_resource::(); - let mut sound_output = world.write_resource::(); - let wants_to_whips = world.read_storage::(); - let mut clock = world.write_resource::(); +pub fn try_move(delta_x: i32, delta_y: i32, world: &mut World, resources: &mut Resources) { + let mut to_kill: Vec = Vec::new(); - for (player_entity, player, pos) in (&entities, &mut players, &mut positions).join() { + for (player_entity, (player, pos)) in &mut world.query::<(&mut Player, &mut Position)>() { // The player shouldn't be able to move while whipping - if let Some(_wants_to_whip) = wants_to_whips.get(player_entity) { + if let Ok(_wants_to_whip) = world.get::(player_entity) { continue; } @@ -31,65 +21,77 @@ pub fn try_move(delta_x: i32, delta_y: i32, world: &World) { y: pos.y + delta_y, }; - let mut sound_effects = world.fetch_mut::(); - - if map.in_bounds(destination) { - if map.is_solid(destination) { - sound_output.play_sound(sound_effects.blocked.clone()); + if resources.map.in_bounds(destination) { + if resources.map.is_solid(destination) { + resources + .sound_output + .play_sound(resources.sound_effects.blocked.clone()); } else { - if let Some(e) = map.get_tile_content_at(destination) { - if let Some(monster) = monsters.get(e) { - stats.add_score(damage_for_kind(monster.kind)); - stats.take_gems(damage_for_kind(monster.kind)); - sound_output - .play_sound(sound_effect_for_kind(monster.kind, &sound_effects)); - let _ = entities.delete(e); + if let Some(monster_entity) = resources.map.get_tile_content_at(destination) { + if let Ok(monster) = world.get::(monster_entity) { + resources.stats.add_score(damage_for_kind(monster.kind)); + resources.stats.take_gems(damage_for_kind(monster.kind)); + resources.sound_output.play_sound(sound_effect_for_kind( + monster.kind, + &resources.sound_effects, + )); + to_kill.push(monster_entity); } } - map.clear_tile_content_at(Point::from(*pos)); + resources.map.clear_tile_content_at(Point::from(*pos)); pos.x = destination.x; pos.y = destination.y; - map.set_tile_content_at(destination, player_entity); + resources + .map + .set_tile_content_at(destination, player_entity); - TimeSystem::force_tick(&mut clock); + systems::time::force_tick(&mut resources.clock); - sound_output.play_sound(sound_effects.step.clone()); + resources + .sound_output + .play_sound(resources.sound_effects.step.clone()); } } else { - let static_sound = sound_effects.get_new_static_effect(&sound_output); - sound_output.play_sound(static_sound); + let static_sound = resources + .sound_effects + .get_new_static_effect(&resources.sound_output); + resources.sound_output.play_sound(static_sound); } player.last_moved = now; } } -} -pub fn whip(world: &World) { - let entities = world.entities(); - - let players = world.read_storage::(); - let positions = world.read_storage::(); - let mut wants_to_whips = world.write_storage::(); - - let mut stats = world.write_resource::(); - let mut sound_output = world.write_resource::(); - let sound_effects = world.fetch::(); - - for (entity, _player, _position) in (&entities, &players, &positions).join() { - if wants_to_whips.get(entity).is_none() && stats.whips > 0 { - let _ = wants_to_whips.insert( - entity, - WantsToWhip { - frame: 0, - last_frame: Instant::now(), - sound: Some(sound_output.play_sound(sound_effects.whipping.clone())), - }, - ); - stats.whips -= 1; - } + for e in to_kill { + let _ = world.despawn(e); + } +} + +pub fn whip(world: &mut World, resources: &mut Resources) { + let mut to_add: Vec = Vec::new(); + + for (entity, _) in world.query_mut::>>>() { + if resources.stats.whips > 0 { + to_add.push(entity); + } + } + + for e in to_add { + let _ = world.insert_one( + e, + WantsToWhip { + frame: 0, + last_frame: Instant::now(), + sound: Some( + resources + .sound_output + .play_sound(resources.sound_effects.whipping.clone()), + ), + }, + ); + resources.stats.whips -= 1; } } diff --git a/src/main.rs b/src/main.rs index 33cd6df..317ae33 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,6 @@ pub mod components; pub mod constants; -pub mod difficulty; mod graphics; pub mod input; pub mod levels; @@ -11,59 +10,54 @@ mod state; pub mod systems; pub mod tile_data; -use bracket_lib::prelude::*; -use components::{Monster, Player, Position, Renderable, WantsToWhip}; -use resources::{ - Clock, LevelNumber, Map, ShowDebugInfo, SoundEffects, SoundOutput, Stats, StopClock, -}; -use specs::prelude::*; -use state::State; use std::time::Instant; +use bracket_lib::prelude::*; +use hecs::World; +use resources::{difficulty::SECRET, *}; +use state::State; + fn main() -> BError { let context = BTermBuilder::vga(80, 25) .with_fps_cap(60.0) .with_title("Kroz") .build()?; - let mut sound_system = SoundOutput::new(); - let sound_effects = SoundEffects::new(&sound_system); - sound_system.play_sound(sound_effects.startup.clone()); + let mut sound_output = SoundOutput::new(); + let sound_effects = SoundEffects::new(&sound_output); + sound_output.play_sound(sound_effects.startup.clone()); + + let starting_level = 0; + let selected_difficulty = SECRET; let mut world = World::new(); - let starting_level = 0; - world.insert(sound_system); - world.insert(LevelNumber(starting_level)); - world.insert(ShowDebugInfo(false)); - world.insert(StopClock(false)); - world.insert(Clock { - last_ticked: Instant::now(), - has_ticked: false, - ticks: 0, - }); - world.insert(sound_effects); - - let selected_difficulty = difficulty::SECRET; - world.insert(selected_difficulty); - world.insert(Stats { - score: 0, - gems: selected_difficulty.starting_gems, - whips: selected_difficulty.starting_whips, - whip_power: selected_difficulty.starting_whip_power, - teleports: selected_difficulty.starting_teleports, - keys: selected_difficulty.starting_keys, - }); - - world.register::(); - world.register::(); - world.register::(); - world.register::(); - world.register::(); - let mut map = Map::from_level(levels::get_level(starting_level)); map.spawn_entities(&mut world); - world.insert(map); + + let resources = Resources { + level_number: starting_level, + show_debug_info: false, + player_input: None, + stats: Stats { + score: 0, + gems: selected_difficulty.starting_gems, + whips: selected_difficulty.starting_whips, + whip_power: selected_difficulty.starting_whip_power, + teleports: selected_difficulty.starting_teleports, + keys: selected_difficulty.starting_keys, + }, + clock: Clock { + last_ticked: Instant::now(), + has_ticked: false, + ticks: 0, + }, + stop_clock: false, + map, + sound_effects, + sound_output, + selected_difficulty: Some(selected_difficulty), + }; // let descent_sounds: Vec = (20..100) // .rev() @@ -83,5 +77,5 @@ fn main() -> BError { // let _ = gs.sound_system.play_sound(descent_effect); - main_loop(context, State::new(world)) + main_loop(context, State::new(world, resources)) } diff --git a/src/difficulty.rs b/src/resources/difficulty.rs similarity index 100% rename from src/difficulty.rs rename to src/resources/difficulty.rs diff --git a/src/resources/map.rs b/src/resources/map.rs index 3c257b0..d47b263 100644 --- a/src/resources/map.rs +++ b/src/resources/map.rs @@ -8,7 +8,7 @@ use crate::{ tile_data::{self, TileType}, }; use bracket_lib::{prelude::*, random::RandomNumberGenerator}; -use specs::{Builder, Entity, World, WorldExt}; +use hecs::{Entity, World}; pub struct Map { tiles: Vec, @@ -90,85 +90,84 @@ impl Map { let point = Point::new(index % MAP_WIDTH, index / MAP_WIDTH); match tile { TileType::Player => { - let player_entity = world - .create_entity() - .with(Position { + let entity = world.spawn(( + Player { + last_moved: Instant::now(), + }, + Position { x: point.x, y: point.y, - }) - .with(Renderable { + }, + Renderable { glyph: to_cp437('☻'), fg: RGB::named(vga::YELLOW_BRIGHT), bg: RGB::named(vga::BLACK), - }) - .with(Player { - last_moved: Instant::now(), - }) - .build(); - self.tile_content[index] = Some(player_entity); + }, + )); + self.tile_content[index] = Some(entity); } TileType::Slow => { let mut rng = RandomNumberGenerator::new(); - world - .create_entity() - .with(Position { + let entity = world.spawn(( + Monster { + kind: MonsterKind::Slow, + ticks_until_move: ticks_for_kind(MonsterKind::Slow), + }, + Position { x: point.x, y: point.y, - }) - .with(Renderable { + }, + Renderable { glyph: *rng .random_slice_entry(&glyphs_for_kind(MonsterKind::Slow)) .unwrap(), fg: color_for_kind(MonsterKind::Slow), bg: RGB::named(vga::BLACK), - }) - .with(Monster { - kind: MonsterKind::Slow, - ticks_until_move: ticks_for_kind(MonsterKind::Slow), - }) - .build(); + }, + )); + self.tile_content[index] = Some(entity); } TileType::Medium => { let mut rng = RandomNumberGenerator::new(); - world - .create_entity() - .with(Position { + let entity = world.spawn(( + Monster { + kind: MonsterKind::Medium, + ticks_until_move: ticks_for_kind(MonsterKind::Medium), + }, + Position { x: point.x, y: point.y, - }) - .with(Renderable { + }, + Renderable { glyph: *rng .random_slice_entry(&glyphs_for_kind(MonsterKind::Medium)) .unwrap(), fg: color_for_kind(MonsterKind::Medium), bg: RGB::named(vga::BLACK), - }) - .with(Monster { - kind: MonsterKind::Medium, - ticks_until_move: ticks_for_kind(MonsterKind::Medium), - }) - .build(); + }, + )); + self.tile_content[index] = Some(entity); } TileType::Fast => { let mut rng = RandomNumberGenerator::new(); - world - .create_entity() - .with(Position { + let entity = world.spawn(( + Monster { + kind: MonsterKind::Fast, + ticks_until_move: ticks_for_kind(MonsterKind::Fast), + }, + Position { x: point.x, y: point.y, - }) - .with(Renderable { + }, + Renderable { glyph: *rng .random_slice_entry(&glyphs_for_kind(MonsterKind::Fast)) .unwrap(), fg: color_for_kind(MonsterKind::Fast), bg: RGB::named(vga::BLACK), - }) - .with(Monster { - kind: MonsterKind::Fast, - ticks_until_move: ticks_for_kind(MonsterKind::Fast), - }) - .build(); + }, + )); + self.tile_content[index] = Some(entity); } _ => {} } diff --git a/src/resources/mod.rs b/src/resources/mod.rs index 636a13d..0a3425d 100644 --- a/src/resources/mod.rs +++ b/src/resources/mod.rs @@ -1,47 +1,27 @@ pub mod clock; +pub mod difficulty; pub mod map; pub mod sound_effects; pub mod sound_output; +pub mod stats; use bracket_lib::prelude::*; pub use clock::{Clock, StopClock}; +pub use difficulty::Difficulty; pub use map::Map; pub use sound_effects::SoundEffects; pub use sound_output::SoundOutput; +pub use stats::Stats; -#[derive(Default)] -pub struct LevelNumber(pub u32); - -#[derive(Default)] -pub struct ShowDebugInfo(pub bool); - -#[derive(Default)] -pub struct PlayerInput(pub Option); - -pub struct Stats { - pub score: u32, - pub gems: u32, - pub whips: u32, - pub whip_power: u32, - pub teleports: u32, - pub keys: u32, -} - -type PlayerSurvived = bool; - -impl Stats { - pub fn take_gems(&mut self, num_gems: u32) -> PlayerSurvived { - let new_num_gems = self.gems as i64 - num_gems as i64; - if new_num_gems <= 0 { - self.gems = 0; - false - } else { - self.gems = new_num_gems as u32; - true - } - } - - pub fn add_score(&mut self, score: u32) { - self.score += score; - } +pub struct Resources { + pub level_number: u32, + pub show_debug_info: bool, + pub player_input: Option, + pub stats: Stats, + pub clock: Clock, + pub stop_clock: bool, + pub map: Map, + pub selected_difficulty: Option, + pub sound_effects: SoundEffects, + pub sound_output: SoundOutput, } diff --git a/src/resources/stats.rs b/src/resources/stats.rs new file mode 100644 index 0000000..a7d81ce --- /dev/null +++ b/src/resources/stats.rs @@ -0,0 +1,27 @@ +pub struct Stats { + pub score: u32, + pub gems: u32, + pub whips: u32, + pub whip_power: u32, + pub teleports: u32, + pub keys: u32, +} + +type PlayerSurvived = bool; + +impl Stats { + pub fn take_gems(&mut self, num_gems: u32) -> PlayerSurvived { + let new_num_gems = self.gems as i64 - num_gems as i64; + if new_num_gems <= 0 { + self.gems = 0; + false + } else { + self.gems = new_num_gems as u32; + true + } + } + + pub fn add_score(&mut self, score: u32) { + self.score += score; + } +} diff --git a/src/state.rs b/src/state.rs index 261e071..b992e9f 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,23 +1,23 @@ -use crate::graphics::draw; -use crate::input::handle; -use crate::systems::*; +use crate::resources::Resources; +use crate::{graphics, input, systems}; use bracket_lib::prelude::*; -use specs::prelude::*; +use hecs::World; pub struct State { world: World, + resources: Resources, } impl GameState for State { fn tick(&mut self, bterm: &mut BTerm) { - handle(&self.world, bterm); - run_systems(&mut self.world); - draw(&self.world, bterm); + input::handle(&mut self.world, &mut self.resources, bterm); + systems::run(&mut self.world, &mut self.resources); + graphics::draw(&self.world, &self.resources, bterm); } } impl State { - pub fn new(world: World) -> Self { - State { world } + pub fn new(world: World, resources: Resources) -> Self { + State { world, resources } } } diff --git a/src/systems/mod.rs b/src/systems/mod.rs index 3a65728..6c7c96a 100644 --- a/src/systems/mod.rs +++ b/src/systems/mod.rs @@ -1,21 +1,13 @@ -pub mod monster_ai_system; -pub mod time_system; -pub mod whip_system; +pub mod monster_ai; +pub mod time; +pub mod whip; -pub use monster_ai_system::MonsterAiSystem; -use specs::prelude::*; -pub use time_system::TimeSystem; -pub use whip_system::WhipSystem; +use hecs::World; -pub fn run_systems(world: &mut World) { - let mut whip_system = WhipSystem {}; - whip_system.run_now(world); +use crate::resources::Resources; - let mut monster_ai_system = MonsterAiSystem {}; - monster_ai_system.run_now(world); - - let mut time_system = TimeSystem {}; - time_system.run_now(world); - - world.maintain(); +pub fn run(world: &mut World, resources: &mut Resources) { + whip::run(world, resources); + monster_ai::run(world, resources); + time::run(resources); } diff --git a/src/systems/monster_ai.rs b/src/systems/monster_ai.rs new file mode 100644 index 0000000..6d79e5f --- /dev/null +++ b/src/systems/monster_ai.rs @@ -0,0 +1,92 @@ +use crate::{ + components::{monster::*, *}, + resources::Resources, + tile_data::TileType, +}; + +use bracket_lib::{prelude::*, random::RandomNumberGenerator}; +use hecs::*; + +pub fn run(world: &mut World, resources: &mut Resources) { + let player_position = world + .query::<(&Player, &Position)>() + .iter() + .map(|(_, (_, &pos))| pos) + .collect::>() + .first() + .cloned(); + + if let Some(player_pos) = player_position { + let mut has_died = None; + for (entity, (monster, position, renderable)) in + &mut world.query::<(&mut Monster, &mut Position, &mut Renderable)>() + { + if resources.clock.has_ticked { + monster.ticks_until_move -= 1; + if monster.ticks_until_move <= 0 { + // Change glyph + let mut rng = RandomNumberGenerator::new(); + if let Some(glyph) = + rng.random_slice_entry(&monster::glyphs_for_kind(monster.kind)) + { + renderable.glyph = *glyph; + } + + // Move monster + let (mut delta_x, mut delta_y) = (0, 0); + if player_pos.x < position.x { + delta_x = -1; + } + if player_pos.x > position.x { + delta_x = 1; + } + if player_pos.y < position.y { + delta_y = -1; + } + if player_pos.y > position.y { + delta_y = 1; + } + let destination = Point { + x: position.x + delta_x, + y: position.y + delta_y, + }; + + if let Some(e) = resources.map.get_tile_content_at(destination) { + if let Ok(_player) = world.get::(e) { + // TODO: Sound + resources.map.clear_tile_content_at(Point::from(*position)); + resources.stats.take_gems(damage_for_kind(monster.kind)); + resources.sound_output.play_sound(sound_effect_for_kind( + monster.kind, + &resources.sound_effects, + )); + has_died = Some(entity); + } + } else { + let tile = resources.map.get_tile_at_mut(destination); + match tile { + TileType::Wall => {} + TileType::Block => { + // TODO: Sound + *tile = TileType::Floor; + has_died = Some(entity); + } + _ => { + resources.map.clear_tile_content_at(Point::from(*position)); + position.x = destination.x; + position.y = destination.y; + resources.map.set_tile_content_at(destination, entity); + } + } + } + + monster.ticks_until_move = ticks_for_kind(monster.kind); + } + } + } + + if let Some(e) = has_died { + let _ = world.despawn(e); + } + } +} diff --git a/src/systems/monster_ai_system.rs b/src/systems/monster_ai_system.rs deleted file mode 100644 index 7967435..0000000 --- a/src/systems/monster_ai_system.rs +++ /dev/null @@ -1,120 +0,0 @@ -use crate::{ - components::{ - monster::{self, damage_for_kind, sound_effect_for_kind, ticks_for_kind}, - Monster, Player, Position, Renderable, - }, - resources::{Clock, Map, SoundEffects, SoundOutput, Stats}, - tile_data::TileType, -}; -use bracket_lib::{prelude::*, random::RandomNumberGenerator}; -use specs::prelude::*; - -pub struct MonsterAiSystem {} - -#[allow(clippy::type_complexity)] -impl<'a> System<'a> for MonsterAiSystem { - type SystemData = ( - Entities<'a>, - ReadExpect<'a, Clock>, - WriteExpect<'a, Map>, - WriteExpect<'a, Stats>, - ReadExpect<'a, SoundEffects>, - WriteExpect<'a, SoundOutput>, - WriteStorage<'a, Monster>, - WriteStorage<'a, Position>, - WriteStorage<'a, Renderable>, - ReadStorage<'a, Player>, - ); - - fn run( - &mut self, - ( - entities, - clock, - mut map, - mut stats, - sound_effects, - mut sound_output, - mut monsters, - mut positions, - mut renderables, - players, - ): Self::SystemData, - ) { - let mut player_position = None; - for (_entity, _player, position) in (&entities, &players, &positions).join() { - player_position = Some(Point::from(*position)); - } - - if let Some(player_pos) = player_position { - let mut data = (&entities, &mut monsters, &mut positions, &mut renderables) - .join() - .collect::>(); - - for (entity, monster, position, renderable) in &mut data { - if clock.has_ticked { - monster.ticks_until_move -= 1; - if monster.ticks_until_move <= 0 { - // Change glyph - let mut rng = RandomNumberGenerator::new(); - if let Some(glyph) = - rng.random_slice_entry(&monster::glyphs_for_kind(monster.kind)) - { - renderable.glyph = *glyph; - } - - // Move monster - let (mut delta_x, mut delta_y) = (0, 0); - if player_pos.x < position.x { - delta_x = -1; - } - if player_pos.x > position.x { - delta_x = 1; - } - if player_pos.y < position.y { - delta_y = -1; - } - if player_pos.y > position.y { - delta_y = 1; - } - let destination = Point { - x: position.x + delta_x, - y: position.y + delta_y, - }; - - if let Some(e) = map.get_tile_content_at(destination) { - if let Some(_player) = players.get(e) { - // TODO: Sound - map.clear_tile_content_at(Point::from(**position)); - stats.take_gems(damage_for_kind(monster.kind)); - sound_output.play_sound(sound_effect_for_kind( - monster.kind, - &sound_effects, - )); - let _ = entities.delete(*entity); - } - } else { - let tile = map.get_tile_at_mut(destination); - match tile { - TileType::Wall => {} - TileType::Block => { - // TODO: Sound - *tile = TileType::Floor; - let _ = entities.delete(*entity); - } - _ => { - map.clear_tile_content_at(Point::from(**position)); - position.x = destination.x; - position.y = destination.y; - map.set_tile_content_at(destination, *entity); - } - } - } - - monster.ticks_until_move = ticks_for_kind(monster.kind); - } - } - } - } - } -} diff --git a/src/systems/time.rs b/src/systems/time.rs new file mode 100644 index 0000000..4686e31 --- /dev/null +++ b/src/systems/time.rs @@ -0,0 +1,36 @@ +use std::time::{Duration, Instant}; + +use crate::constants::CLOCK_PERIOD; + +use crate::resources::{Clock, Resources}; + +pub fn run(resources: &mut Resources) { + if !resources.stop_clock { + try_tick(&mut resources.clock); + } else { + reset(&mut resources.clock); + } +} + +fn try_tick(clock: &mut Clock) { + if Instant::now() - clock.last_ticked > Duration::from_secs_f32(CLOCK_PERIOD) { + tick(clock); + } else { + clock.has_ticked = false; + } +} + +pub fn force_tick(clock: &mut Clock) { + tick(clock); +} + +fn tick(clock: &mut Clock) { + clock.has_ticked = true; + clock.last_ticked = Instant::now(); + clock.ticks += 1; +} + +fn reset(clock: &mut Clock) { + clock.last_ticked = Instant::now(); + clock.has_ticked = false; +} diff --git a/src/systems/time_system.rs b/src/systems/time_system.rs deleted file mode 100644 index a4900e1..0000000 --- a/src/systems/time_system.rs +++ /dev/null @@ -1,45 +0,0 @@ -use crate::constants::CLOCK_PERIOD; -use specs::prelude::*; -use std::time::{Duration, Instant}; - -use crate::resources::{Clock, StopClock}; - -pub struct TimeSystem {} - -#[allow(clippy::type_complexity)] -impl<'a> System<'a> for TimeSystem { - type SystemData = (WriteExpect<'a, Clock>, ReadExpect<'a, StopClock>); - - fn run(&mut self, (mut clock, stop_clock): Self::SystemData) { - if !stop_clock.0 { - Self::try_tick(&mut clock); - } else { - Self::reset(&mut clock); - } - } -} - -impl TimeSystem { - pub fn try_tick(clock: &mut Clock) { - if Instant::now() - clock.last_ticked > Duration::from_secs_f32(CLOCK_PERIOD) { - Self::tick(clock); - } else { - clock.has_ticked = false; - } - } - - pub fn force_tick(clock: &mut Clock) { - Self::tick(clock); - } - - fn tick(clock: &mut Clock) { - clock.has_ticked = true; - clock.last_ticked = Instant::now(); - clock.ticks += 1; - } - - pub fn reset(clock: &mut Clock) { - clock.last_ticked = Instant::now(); - clock.has_ticked = false; - } -} diff --git a/src/systems/whip.rs b/src/systems/whip.rs new file mode 100644 index 0000000..e41d66a --- /dev/null +++ b/src/systems/whip.rs @@ -0,0 +1,115 @@ +use std::time::{Duration, Instant}; + +use bracket_lib::prelude::*; +use hecs::{Entity, World}; + +use crate::{ + components::{monster::damage_for_kind, Monster, Position, WantsToWhip}, + resources::Resources, +}; + +pub fn run(world: &mut World, resources: &mut Resources) { + let mut to_kill: Vec = Vec::new(); + let mut to_remove: Vec = Vec::new(); + + for (entity, (position, mut wants_to_whip)) in + &mut world.query::<(&Position, &mut WantsToWhip)>() + { + resources.stop_clock = true; + let now = Instant::now(); + if now - wants_to_whip.last_frame > Duration::from_secs_f32(0.1) { + let destination = loop { + let destination = match wants_to_whip.frame { + 0 => Some(Point { + x: position.x - 1, + y: position.y - 1, + }), + 1 => Some(Point { + x: position.x - 1, + y: position.y, + }), + 2 => Some(Point { + x: position.x - 1, + y: position.y + 1, + }), + 3 => Some(Point { + x: position.x, + y: position.y + 1, + }), + 4 => Some(Point { + x: position.x + 1, + y: position.y + 1, + }), + 5 => Some(Point { + x: position.x + 1, + y: position.y, + }), + 6 => Some(Point { + x: position.x + 1, + y: position.y - 1, + }), + 7 => Some(Point { + x: position.x, + y: position.y - 1, + }), + _ => None, + }; + + if let Some(dest) = destination { + if resources.map.in_bounds(dest) { + break destination; + } + wants_to_whip.frame += 1; + if wants_to_whip.frame > 7 { + break None; + } + } + }; + + if let Some(dest) = destination { + if let Some(e) = resources.map.get_tile_content_at(dest) { + if let Ok(monster) = world.get::(e) { + resources.stats.add_score(damage_for_kind(monster.kind)); + to_kill.push(e); + resources.map.clear_tile_content_at(dest); + if let Some(sound) = &mut wants_to_whip.sound { + sound.control::, _>().stop(); + } + if wants_to_whip.frame == 7 { + wants_to_whip.sound = None; + resources + .sound_output + .play_sound(resources.sound_effects.whipping_hit_end.clone()); + } else { + wants_to_whip.sound = Some( + resources + .sound_output + .play_sound(resources.sound_effects.whipping_hit.clone()), + ); + } + } + } + } + + if wants_to_whip.frame < 7 { + wants_to_whip.frame += 1; + wants_to_whip.last_frame = now; + } else { + to_remove.push(entity); + resources.stop_clock = false; + if let Some(sound) = &mut wants_to_whip.sound { + sound.control::, _>().stop(); + wants_to_whip.sound = None; + } + } + } + } + + for e in to_kill { + let _ = world.despawn(e); + } + + for e in to_remove { + let _ = world.remove_one::(e); + } +} diff --git a/src/systems/whip_system.rs b/src/systems/whip_system.rs deleted file mode 100644 index 4ffe493..0000000 --- a/src/systems/whip_system.rs +++ /dev/null @@ -1,135 +0,0 @@ -use std::time::{Duration, Instant}; - -use bracket_lib::prelude::*; -use specs::prelude::*; - -use crate::{ - components::{monster::damage_for_kind, Monster, Position, WantsToWhip}, - resources::{Map, SoundEffects, SoundOutput, Stats, StopClock}, -}; - -pub struct WhipSystem {} - -#[allow(clippy::type_complexity)] -impl<'a> System<'a> for WhipSystem { - type SystemData = ( - WriteExpect<'a, Map>, - WriteExpect<'a, StopClock>, - WriteExpect<'a, SoundOutput>, - ReadExpect<'a, SoundEffects>, - WriteExpect<'a, Stats>, - ReadStorage<'a, Position>, - WriteStorage<'a, WantsToWhip>, - WriteStorage<'a, Monster>, - Entities<'a>, - ); - - fn run(&mut self, data: Self::SystemData) { - let ( - mut map, - mut stop_clock, - mut sound_output, - sound_effects, - mut stats, - positions, - mut wants_to_whips, - monsters, - entities, - ) = data; - - let mut entities_to_remove: Vec = vec![]; - - for (entity, position, mut wants_to_whip) in - (&entities, &positions, &mut wants_to_whips).join() - { - stop_clock.0 = true; - let now = Instant::now(); - if now - wants_to_whip.last_frame > Duration::from_secs_f32(0.1) { - let destination = loop { - let destination = match wants_to_whip.frame { - 0 => Some(Point { - x: position.x - 1, - y: position.y - 1, - }), - 1 => Some(Point { - x: position.x - 1, - y: position.y, - }), - 2 => Some(Point { - x: position.x - 1, - y: position.y + 1, - }), - 3 => Some(Point { - x: position.x, - y: position.y + 1, - }), - 4 => Some(Point { - x: position.x + 1, - y: position.y + 1, - }), - 5 => Some(Point { - x: position.x + 1, - y: position.y, - }), - 6 => Some(Point { - x: position.x + 1, - y: position.y - 1, - }), - 7 => Some(Point { - x: position.x, - y: position.y - 1, - }), - _ => None, - }; - - if let Some(dest) = destination { - if map.in_bounds(dest) { - break destination; - } - wants_to_whip.frame += 1; - if wants_to_whip.frame > 7 { - break None; - } - } - }; - - if let Some(dest) = destination { - if let Some(e) = map.get_tile_content_at(dest) { - if let Some(monster) = monsters.get(e) { - stats.add_score(damage_for_kind(monster.kind)); - let _ = entities.delete(e); - map.clear_tile_content_at(dest); - if let Some(sound) = &mut wants_to_whip.sound { - sound.control::, _>().stop(); - } - if wants_to_whip.frame == 7 { - wants_to_whip.sound = None; - sound_output.play_sound(sound_effects.whipping_hit_end.clone()); - } else { - wants_to_whip.sound = Some( - sound_output.play_sound(sound_effects.whipping_hit.clone()), - ); - } - } - } - } - - if wants_to_whip.frame < 7 { - wants_to_whip.frame += 1; - wants_to_whip.last_frame = now; - } else { - entities_to_remove.push(entity); - stop_clock.0 = false; - if let Some(sound) = &mut wants_to_whip.sound { - sound.control::, _>().stop(); - wants_to_whip.sound = None; - } - } - } - } - - for entity in entities_to_remove { - wants_to_whips.remove(entity); - } - } -}