diff --git a/src/components/monster.rs b/src/components/monster.rs index 7aa6c32..7df8a42 100644 --- a/src/components/monster.rs +++ b/src/components/monster.rs @@ -1,6 +1,6 @@ use crate::{ - graphics::vga_color as vga, resources::{sound_output::SoundSamples, SoundEffects}, + vga_color as vga, }; use bracket_lib::prelude::*; use specs::prelude::*; diff --git a/src/graphics/entities.rs b/src/graphics/entities.rs deleted file mode 100644 index 9086204..0000000 --- a/src/graphics/entities.rs +++ /dev/null @@ -1,19 +0,0 @@ -use bracket_lib::prelude::*; -use specs::prelude::*; - -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() { - bterm.set( - pos.x + MAP_X as i32, - pos.y + MAP_Y as i32, - render.fg, - render.bg, - render.glyph, - ); - } -} diff --git a/src/graphics/map.rs b/src/graphics/map.rs deleted file mode 100644 index 0731052..0000000 --- a/src/graphics/map.rs +++ /dev/null @@ -1,9 +0,0 @@ -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); -} diff --git a/src/graphics/mod.rs b/src/graphics/mod.rs deleted file mode 100644 index 4b148ba..0000000 --- a/src/graphics/mod.rs +++ /dev/null @@ -1,16 +0,0 @@ -use bracket_lib::prelude::*; -use specs::prelude::*; - -mod entities; -mod map; -mod sidebar; -pub mod vga_color; -mod whip; - -pub fn draw(world: &World, bterm: &mut BTerm) { - bterm.cls(); - map::draw(world, bterm); - entities::draw(world, bterm); - whip::draw(world, bterm); - sidebar::draw(world, bterm); -} diff --git a/src/graphics/whip.rs b/src/graphics/whip.rs deleted file mode 100644 index f20fff3..0000000 --- a/src/graphics/whip.rs +++ /dev/null @@ -1,53 +0,0 @@ -use bracket_lib::prelude::*; -use specs::prelude::*; - -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::(); - let mut rng = RandomNumberGenerator::new(); - for (position, wants_to_whip) in (&positions, &wants_to_whips).join() { - let color = RGB::named(get_by_index(rng.range(1, 16))); - let mut rendered_frame = wants_to_whip.frame; - let frame_data = loop { - let frame_data = match rendered_frame { - 0 => Some((-1, -1, '\\')), - 1 => Some((-1, 0, '─')), - 2 => Some((-1, 1, '/')), - 3 => Some((0, 1, '│')), - 4 => Some((1, 1, '\\')), - 5 => Some((1, 0, '─')), - 6 => Some((1, -1, '/')), - 7 => Some((0, -1, '│')), - _ => None, - }; - - if let Some(data) = frame_data { - let dest = Point { - x: position.x + data.0, - y: position.y + data.1, - }; - if map.in_bounds(dest) { - break frame_data; - } - rendered_frame += 1; - if rendered_frame > 7 { - break None; - } - } - }; - if let Some(data) = frame_data { - bterm.set( - (position.x + MAP_X as i32) + data.0, - (position.y + MAP_Y as i32) + data.1, - color, - RGB::named(vga::BLACK), - to_cp437(data.2), - ); - }; - } -} diff --git a/src/input/mod.rs b/src/input/mod.rs deleted file mode 100644 index 4da204b..0000000 --- a/src/input/mod.rs +++ /dev/null @@ -1,50 +0,0 @@ -use bracket_lib::prelude::*; -use specs::prelude::*; - -use crate::resources::*; - -mod player; - -pub fn handle(world: &World, bterm: &mut BTerm) { - match bterm.key { - None => {} - Some(key) => match key { - VirtualKeyCode::Left | VirtualKeyCode::J => { - player::try_move(-1, 0, world); - } - VirtualKeyCode::U | VirtualKeyCode::Home => player::try_move(-1, -1, world), - VirtualKeyCode::Up | VirtualKeyCode::I => { - player::try_move(0, -1, world); - } - VirtualKeyCode::O | VirtualKeyCode::PageUp => player::try_move(1, -1, world), - VirtualKeyCode::Right | VirtualKeyCode::K => { - player::try_move(1, 0, world); - } - VirtualKeyCode::Comma | VirtualKeyCode::PageDown => player::try_move(1, 1, world), - VirtualKeyCode::Down | VirtualKeyCode::M => { - player::try_move(0, 1, world); - } - VirtualKeyCode::N | VirtualKeyCode::End => player::try_move(-1, 1, world), - VirtualKeyCode::W => { - player::whip(world); - } - VirtualKeyCode::S => { - let mut sound_system = world.write_resource::(); - let sound_effects = world.fetch::(); - sound_system.play_sound(sound_effects.pickup.clone()); - } - VirtualKeyCode::D => { - let mut show_debug_info = world.write_resource::(); - show_debug_info.0 = !show_debug_info.0; - } - 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()); - } - }, - } -} diff --git a/src/input/player.rs b/src/input/player.rs deleted file mode 100644 index 3d38fe5..0000000 --- a/src/input/player.rs +++ /dev/null @@ -1,95 +0,0 @@ -use std::time::{Duration, Instant}; - -use bracket_lib::prelude::*; -use specs::prelude::*; - -use crate::{ - components::monster::*, components::*, constants::*, resources::*, systems::TimeSystem, -}; - -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::(); - - for (player_entity, player, pos) in (&entities, &mut players, &mut positions).join() { - // The player shouldn't be able to move while whipping - if let Some(_wants_to_whip) = wants_to_whips.get(player_entity) { - continue; - } - - let now = Instant::now(); - if now - player.last_moved > Duration::from_secs_f32(PLAYER_STEP_PERIOD) { - let destination = Point { - x: pos.x + delta_x, - 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()); - } 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); - } - } - - map.clear_tile_content_at(Point::from(*pos)); - - pos.x = destination.x; - pos.y = destination.y; - - map.set_tile_content_at(destination, player_entity); - - TimeSystem::force_tick(&mut clock); - - sound_output.play_sound(sound_effects.step.clone()); - } - } else { - let static_sound = sound_effects.get_new_static_effect(&sound_output); - 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; - } - } -} diff --git a/src/main.rs b/src/main.rs index 33cd6df..af06cc6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,13 +3,13 @@ pub mod components; pub mod constants; pub mod difficulty; -mod graphics; -pub mod input; pub mod levels; pub mod resources; +mod sidebar; mod state; pub mod systems; pub mod tile_data; +pub mod vga_color; use bracket_lib::prelude::*; use components::{Monster, Player, Position, Renderable, WantsToWhip}; @@ -30,23 +30,23 @@ fn main() -> BError { let sound_effects = SoundEffects::new(&sound_system); sound_system.play_sound(sound_effects.startup.clone()); - let mut world = World::new(); + let mut ecs = 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 { + ecs.insert(sound_system); + ecs.insert(LevelNumber(starting_level)); + ecs.insert(ShowDebugInfo(false)); + ecs.insert(StopClock(false)); + ecs.insert(Clock { last_ticked: Instant::now(), has_ticked: false, ticks: 0, }); - world.insert(sound_effects); + ecs.insert(sound_effects); let selected_difficulty = difficulty::SECRET; - world.insert(selected_difficulty); - world.insert(Stats { + ecs.insert(selected_difficulty); + ecs.insert(Stats { score: 0, gems: selected_difficulty.starting_gems, whips: selected_difficulty.starting_whips, @@ -55,15 +55,15 @@ fn main() -> BError { keys: selected_difficulty.starting_keys, }); - world.register::(); - world.register::(); - world.register::(); - world.register::(); - world.register::(); + ecs.register::(); + ecs.register::(); + ecs.register::(); + ecs.register::(); + ecs.register::(); let mut map = Map::from_level(levels::get_level(starting_level)); - map.spawn_entities(&mut world); - world.insert(map); + map.spawn_entities(&mut ecs); + ecs.insert(map); // let descent_sounds: Vec = (20..100) // .rev() @@ -83,5 +83,5 @@ fn main() -> BError { // let _ = gs.sound_system.play_sound(descent_effect); - main_loop(context, State::new(world)) + main_loop(context, State::new(ecs)) } diff --git a/src/resources/clock.rs b/src/resources/clock.rs deleted file mode 100644 index 02ca0de..0000000 --- a/src/resources/clock.rs +++ /dev/null @@ -1,9 +0,0 @@ -use std::time::Instant; - -pub struct Clock { - pub last_ticked: Instant, - pub has_ticked: bool, - pub ticks: u128, -} - -pub struct StopClock(pub bool); diff --git a/src/resources/map.rs b/src/resources/map.rs index 3c257b0..b9bb6a2 100644 --- a/src/resources/map.rs +++ b/src/resources/map.rs @@ -3,9 +3,9 @@ use std::time::Instant; use crate::{ components::{monster::*, Monster, Player, Position, Renderable}, constants::*, - graphics::vga_color as vga, levels::Level, tile_data::{self, TileType}, + vga_color as vga, }; use bracket_lib::{prelude::*, random::RandomNumberGenerator}; use specs::{Builder, Entity, World, WorldExt}; @@ -80,7 +80,7 @@ impl Map { } } - pub fn spawn_entities(&mut self, world: &mut World) { + pub fn spawn_entities(&mut self, ecs: &mut World) { for (index, tile) in self .tiles .iter_mut() @@ -90,7 +90,7 @@ impl Map { let point = Point::new(index % MAP_WIDTH, index / MAP_WIDTH); match tile { TileType::Player => { - let player_entity = world + let player_entity = ecs .create_entity() .with(Position { x: point.x, @@ -105,12 +105,13 @@ impl Map { last_moved: Instant::now(), }) .build(); + ecs.insert(point); + ecs.insert(player_entity); self.tile_content[index] = Some(player_entity); } TileType::Slow => { let mut rng = RandomNumberGenerator::new(); - world - .create_entity() + ecs.create_entity() .with(Position { x: point.x, y: point.y, @@ -130,8 +131,7 @@ impl Map { } TileType::Medium => { let mut rng = RandomNumberGenerator::new(); - world - .create_entity() + ecs.create_entity() .with(Position { x: point.x, y: point.y, @@ -151,8 +151,7 @@ impl Map { } TileType::Fast => { let mut rng = RandomNumberGenerator::new(); - world - .create_entity() + ecs.create_entity() .with(Position { x: point.x, y: point.y, @@ -200,9 +199,9 @@ impl Map { &self.tiles } - pub fn draw(&self, bterm: &mut BTerm) { + pub fn draw(&self, ctx: &mut BTerm) { // Border - bterm.fill_region( + ctx.fill_region( Rect { x1: MAP_X as i32 - 1, x2: MAP_WIDTH as i32 + MAP_X as i32 + 1, @@ -219,7 +218,7 @@ impl Map { let (x, y) = (point.x as usize, point.y as usize); let data = tile_data::tile_data(*tile); - bterm.set( + ctx.set( x + MAP_X, y + MAP_Y, if *tile == TileType::Gem { diff --git a/src/resources/mod.rs b/src/resources/mod.rs index 636a13d..df734a6 100644 --- a/src/resources/mod.rs +++ b/src/resources/mod.rs @@ -1,22 +1,52 @@ -pub mod clock; pub mod map; pub mod sound_effects; pub mod sound_output; -use bracket_lib::prelude::*; -pub use clock::{Clock, StopClock}; pub use map::Map; pub use sound_effects::SoundEffects; pub use sound_output::SoundOutput; +use crate::constants::CLOCK_PERIOD; +use std::time::{Duration, Instant}; + #[derive(Default)] pub struct LevelNumber(pub u32); #[derive(Default)] pub struct ShowDebugInfo(pub bool); -#[derive(Default)] -pub struct PlayerInput(pub Option); +pub struct Clock { + pub last_ticked: Instant, + pub has_ticked: bool, + pub ticks: u128, +} + +impl Clock { + pub fn try_tick(&mut self) { + if Instant::now() - self.last_ticked > Duration::from_secs_f32(CLOCK_PERIOD) { + self.tick(); + } else { + self.has_ticked = false; + } + } + + pub fn force_tick(&mut self) { + self.tick(); + } + + fn tick(&mut self) { + self.has_ticked = true; + self.last_ticked = Instant::now(); + self.ticks += 1; + } + + pub fn reset(&mut self) { + self.last_ticked = Instant::now(); + self.has_ticked = false; + } +} + +pub struct StopClock(pub bool); pub struct Stats { pub score: u32, diff --git a/src/graphics/sidebar.rs b/src/sidebar.rs similarity index 52% rename from src/graphics/sidebar.rs rename to src/sidebar.rs index bd3ef9d..4e09acf 100644 --- a/src/graphics/sidebar.rs +++ b/src/sidebar.rs @@ -1,12 +1,12 @@ 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::vga_color as vga; use bracket_lib::prelude::*; use specs::prelude::*; -pub fn draw(world: &World, bterm: &mut BTerm) { +pub fn draw(ecs: &World, ctx: &mut BTerm) { // Blue background - bterm.fill_region( + ctx.fill_region( Rect { x1: SIDEBAR_POS_X, x2: SIDEBAR_POS_X + 14, @@ -20,7 +20,7 @@ pub fn draw(world: &World, bterm: &mut BTerm) { // Gray number boxes (1..17).step_by(3).for_each(|y| { - bterm.fill_region( + ctx.fill_region( Rect { x1: SIDEBAR_POS_X + 3, x2: SIDEBAR_POS_X + 10, @@ -34,27 +34,27 @@ pub fn draw(world: &World, bterm: &mut BTerm) { }); // Stats - bterm.print(SIDEBAR_POS_X + 4, SIDEBAR_POS_Y, "Score"); - bterm.print(SIDEBAR_POS_X + 4, SIDEBAR_POS_Y + 3, "Level"); - bterm.print_centered_at( + ctx.print(SIDEBAR_POS_X + 4, SIDEBAR_POS_Y, "Score"); + ctx.print(SIDEBAR_POS_X + 4, SIDEBAR_POS_Y + 3, "Level"); + ctx.print_centered_at( SIDEBAR_POS_X + 6, SIDEBAR_POS_Y + 4, - world.read_resource::().0 + 1, + ecs.read_resource::().0 + 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"); + ctx.print(SIDEBAR_POS_X + 4, SIDEBAR_POS_Y + 6, "Gems"); + ctx.print(SIDEBAR_POS_X + 4, SIDEBAR_POS_Y + 9, "Whips"); + ctx.print(SIDEBAR_POS_X + 2, SIDEBAR_POS_Y + 12, "Teleports"); + ctx.print(SIDEBAR_POS_X + 4, SIDEBAR_POS_Y + 15, "Keys"); - let stats = world.read_resource::(); - 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); - bterm.print_centered_at(SIDEBAR_POS_X + 6, SIDEBAR_POS_Y + 13, stats.teleports); - bterm.print_centered_at(SIDEBAR_POS_X + 6, SIDEBAR_POS_Y + 16, stats.keys); + let stats = ecs.read_resource::(); + ctx.print_centered_at(SIDEBAR_POS_X + 6, SIDEBAR_POS_Y + 1, stats.score); + ctx.print_centered_at(SIDEBAR_POS_X + 6, SIDEBAR_POS_Y + 7, stats.gems); + ctx.print_centered_at(SIDEBAR_POS_X + 6, SIDEBAR_POS_Y + 10, stats.whips); + ctx.print_centered_at(SIDEBAR_POS_X + 6, SIDEBAR_POS_Y + 13, stats.teleports); + ctx.print_centered_at(SIDEBAR_POS_X + 6, SIDEBAR_POS_Y + 16, stats.keys); // Hotkey list - bterm.print_color( + ctx.print_color( SIDEBAR_POS_X + 3, SIDEBAR_POS_Y + 18, RGB::named(vga::CYAN_BRIGHT), @@ -62,7 +62,7 @@ pub fn draw(world: &World, bterm: &mut BTerm) { "OPTIONS", ); - bterm.fill_region( + ctx.fill_region( Rect { x1: SIDEBAR_POS_X, x2: SIDEBAR_POS_X + 14, @@ -74,7 +74,7 @@ pub fn draw(world: &World, bterm: &mut BTerm) { RGB::named(vga::BLUE), ); - bterm.fill_region( + ctx.fill_region( Rect { x1: SIDEBAR_POS_X + 3, x2: SIDEBAR_POS_X + 4, @@ -86,28 +86,28 @@ pub fn draw(world: &World, bterm: &mut BTerm) { RGB::named(vga::BLUE), ); - bterm.print(SIDEBAR_POS_X + 3, SIDEBAR_POS_Y + 19, "Whip"); - bterm.print(SIDEBAR_POS_X + 3, SIDEBAR_POS_Y + 20, "Teleport"); - bterm.print(SIDEBAR_POS_X + 3, SIDEBAR_POS_Y + 21, "Pause"); - bterm.print(SIDEBAR_POS_X + 3, SIDEBAR_POS_Y + 22, "Quit"); - bterm.print(SIDEBAR_POS_X + 3, SIDEBAR_POS_Y + 23, "Save"); - bterm.print(SIDEBAR_POS_X + 3, SIDEBAR_POS_Y + 24, "Restore"); + ctx.print(SIDEBAR_POS_X + 3, SIDEBAR_POS_Y + 19, "Whip"); + ctx.print(SIDEBAR_POS_X + 3, SIDEBAR_POS_Y + 20, "Teleport"); + ctx.print(SIDEBAR_POS_X + 3, SIDEBAR_POS_Y + 21, "Pause"); + ctx.print(SIDEBAR_POS_X + 3, SIDEBAR_POS_Y + 22, "Quit"); + ctx.print(SIDEBAR_POS_X + 3, SIDEBAR_POS_Y + 23, "Save"); + ctx.print(SIDEBAR_POS_X + 3, SIDEBAR_POS_Y + 24, "Restore"); - if world.read_resource::().0 { - bterm.print_color_right( + if ecs.read_resource::().0 { + ctx.print_color_right( SIDEBAR_POS_X + 14, SIDEBAR_POS_Y, RGB::named(vga::GREEN_BRIGHT), RGB::named(vga::BLACK), - &format!("{}", bterm.fps), + &format!("{}", ctx.fps), ); - bterm.print_color( + ctx.print_color( 0, 0, RGB::named(vga::YELLOW_BRIGHT), RGB::named(vga::BLACK), - &format!("{}", world.read_resource::().ticks), + &format!("{}", ecs.read_resource::().ticks), ); } } diff --git a/src/state.rs b/src/state.rs index 261e071..45232c9 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,23 +1,245 @@ -use crate::graphics::draw; -use crate::input::handle; +use std::time::{Duration, Instant}; + +use crate::components::monster::damage_for_kind; +use crate::components::monster::sound_effect_for_kind; +use crate::components::*; +use crate::resources::*; use crate::systems::*; +use crate::vga_color as vga; +use crate::{constants::*, sidebar}; use bracket_lib::prelude::*; use specs::prelude::*; pub struct State { - world: World, + ecs: World, } impl GameState for State { - fn tick(&mut self, bterm: &mut BTerm) { - handle(&self.world, bterm); - run_systems(&mut self.world); - draw(&self.world, bterm); + fn tick(&mut self, ctx: &mut BTerm) { + ctx.cls(); + + self.handle_input(ctx); + self.run_systems(); + + let map = self.ecs.fetch::(); + map.draw(ctx); + + let positions = self.ecs.read_storage::(); + let renderables = self.ecs.read_storage::(); + + for (pos, render) in (&positions, &renderables).join() { + ctx.set( + pos.x + MAP_X as i32, + pos.y + MAP_Y as i32, + render.fg, + render.bg, + render.glyph, + ); + } + + self.draw_whip(ctx); + + sidebar::draw(&self.ecs, ctx); } } impl State { - pub fn new(world: World) -> Self { - State { world } + pub fn new(ecs: World) -> Self { + State { ecs } + } + + fn handle_input(&mut self, ctx: &mut BTerm) { + // Player movement + match ctx.key { + None => {} // Nothing happened + Some(key) => match key { + VirtualKeyCode::Left | VirtualKeyCode::J => { + self.try_move_player(-1, 0); + } + VirtualKeyCode::U | VirtualKeyCode::Home => self.try_move_player(-1, -1), + VirtualKeyCode::Up | VirtualKeyCode::I => { + self.try_move_player(0, -1); + } + VirtualKeyCode::O | VirtualKeyCode::PageUp => self.try_move_player(1, -1), + VirtualKeyCode::Right | VirtualKeyCode::K => { + self.try_move_player(1, 0); + } + VirtualKeyCode::Comma | VirtualKeyCode::PageDown => self.try_move_player(1, 1), + VirtualKeyCode::Down | VirtualKeyCode::M => { + self.try_move_player(0, 1); + } + VirtualKeyCode::N | VirtualKeyCode::End => self.try_move_player(-1, 1), + VirtualKeyCode::W => { + let player_entity = self.ecs.read_resource::(); + let mut stats = self.ecs.write_resource::(); + let positions = self.ecs.read_storage::(); + let mut wants_to_whips = self.ecs.write_storage::(); + if let Some(_position) = positions.get(*player_entity) { + if wants_to_whips.get(*player_entity).is_none() && stats.whips > 0 { + let mut sound_output = self.ecs.write_resource::(); + let sound_effects = self.ecs.fetch::(); + let _ = wants_to_whips.insert( + *player_entity, + WantsToWhip { + frame: 0, + last_frame: Instant::now(), + sound: Some( + sound_output.play_sound(sound_effects.whipping.clone()), + ), + }, + ); + stats.whips -= 1; + } + } + } + VirtualKeyCode::S => { + let mut sound_system = self.ecs.write_resource::(); + let sound_effects = self.ecs.fetch::(); + sound_system.play_sound(sound_effects.pickup.clone()); + } + VirtualKeyCode::D => { + let mut show_debug_info = self.ecs.write_resource::(); + show_debug_info.0 = !show_debug_info.0; + } + VirtualKeyCode::Escape => { + ctx.quit(); + } + _ => { + let mut sound_system = self.ecs.write_resource::(); + let sound_effects = self.ecs.fetch::(); + sound_system.play_sound(sound_effects.bad_key.clone()); + } + }, + } + } + + fn try_move_player(&mut self, delta_x: i32, delta_y: i32) { + let entities = self.ecs.entities(); + let mut positions = self.ecs.write_storage::(); + let mut players = self.ecs.write_storage::(); + let monsters = self.ecs.write_storage::(); + let mut map = self.ecs.write_resource::(); + let mut stats = self.ecs.write_resource::(); + let mut sound_output = self.ecs.write_resource::(); + let wants_to_whips = self.ecs.read_storage::(); + + for (player_entity, player, pos) in (&entities, &mut players, &mut positions).join() { + // The player shouldn't be able to move while whipping + if let Some(_wants_to_whip) = wants_to_whips.get(player_entity) { + continue; + } + + let now = Instant::now(); + if now - player.last_moved > Duration::from_secs_f32(PLAYER_STEP_PERIOD) { + let destination = Point { + x: pos.x + delta_x, + y: pos.y + delta_y, + }; + + let mut sound_effects = self.ecs.fetch_mut::(); + + if map.in_bounds(destination) { + if map.is_solid(destination) { + sound_output.play_sound(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); + } + } + + map.clear_tile_content_at(Point::from(*pos)); + + pos.x = destination.x; + pos.y = destination.y; + + let mut player_pos = self.ecs.write_resource::(); + player_pos.x = pos.x; + player_pos.y = pos.y; + + map.set_tile_content_at(destination, player_entity); + + self.ecs.write_resource::().force_tick(); + + sound_output.play_sound(sound_effects.step.clone()); + } + } else { + let static_sound = sound_effects.get_new_static_effect(&sound_output); + sound_output.play_sound(static_sound); + } + + player.last_moved = now; + } + } + } + + pub fn draw_whip(&self, ctx: &mut BTerm) { + let positions = self.ecs.read_storage::(); + let wants_to_whips = self.ecs.read_storage::(); + let map = self.ecs.read_resource::(); + let mut rng = RandomNumberGenerator::new(); + for (position, wants_to_whip) in (&positions, &wants_to_whips).join() { + let color = RGB::named(vga::get_by_index(rng.range(1, 16))); + let mut rendered_frame = wants_to_whip.frame; + let frame_data = loop { + let frame_data = match rendered_frame { + 0 => Some((-1, -1, '\\')), + 1 => Some((-1, 0, '─')), + 2 => Some((-1, 1, '/')), + 3 => Some((0, 1, '│')), + 4 => Some((1, 1, '\\')), + 5 => Some((1, 0, '─')), + 6 => Some((1, -1, '/')), + 7 => Some((0, -1, '│')), + _ => None, + }; + + if let Some(data) = frame_data { + let dest = Point { + x: position.x + data.0, + y: position.y + data.1, + }; + if map.in_bounds(dest) { + break frame_data; + } + rendered_frame += 1; + if rendered_frame > 7 { + break None; + } + } + }; + if let Some(data) = frame_data { + ctx.set( + (position.x + MAP_X as i32) + data.0, + (position.y + MAP_Y as i32) + data.1, + color, + RGB::named(vga::BLACK), + to_cp437(data.2), + ); + }; + } + } + + fn run_systems(&mut self) { + let mut whip_system = WhipSystem {}; + whip_system.run_now(&self.ecs); + + let mut monster_ai_system = MonsterAiSystem {}; + monster_ai_system.run_now(&self.ecs); + + self.ecs.maintain(); + + let mut clock = self.ecs.write_resource::(); + if !self.ecs.read_resource::().0 { + clock.try_tick(); + } else { + clock.reset(); + } } } diff --git a/src/systems/mod.rs b/src/systems/mod.rs index 3a65728..2845938 100644 --- a/src/systems/mod.rs +++ b/src/systems/mod.rs @@ -1,21 +1,5 @@ pub mod monster_ai_system; -pub mod time_system; pub mod whip_system; pub use monster_ai_system::MonsterAiSystem; -use specs::prelude::*; -pub use time_system::TimeSystem; pub use whip_system::WhipSystem; - -pub fn run_systems(world: &mut World) { - let mut whip_system = WhipSystem {}; - whip_system.run_now(world); - - let mut monster_ai_system = MonsterAiSystem {}; - monster_ai_system.run_now(world); - - let mut time_system = TimeSystem {}; - time_system.run_now(world); - - world.maintain(); -} diff --git a/src/systems/monster_ai_system.rs b/src/systems/monster_ai_system.rs index 7967435..231f8be 100644 --- a/src/systems/monster_ai_system.rs +++ b/src/systems/monster_ai_system.rs @@ -16,6 +16,7 @@ impl<'a> System<'a> for MonsterAiSystem { type SystemData = ( Entities<'a>, ReadExpect<'a, Clock>, + ReadExpect<'a, Point>, WriteExpect<'a, Map>, WriteExpect<'a, Stats>, ReadExpect<'a, SoundEffects>, @@ -31,6 +32,7 @@ impl<'a> System<'a> for MonsterAiSystem { ( entities, clock, + player_pos, mut map, mut stats, sound_effects, @@ -41,78 +43,69 @@ impl<'a> System<'a> for MonsterAiSystem { players, ): Self::SystemData, ) { - let mut player_position = None; - for (_entity, _player, position) in (&entities, &players, &positions).join() { - player_position = Some(Point::from(*position)); - } + let mut data = (&entities, &mut monsters, &mut positions, &mut renderables) + .join() + .collect::>(); - 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; + } - 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, + }; - // Move monster - let (mut delta_x, mut delta_y) = (0, 0); - if player_pos.x < position.x { - delta_x = -1; + 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); } - 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) { + } else { + let tile = map.get_tile_at_mut(destination); + match tile { + TileType::Wall => {} + TileType::Block => { // 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, - )); + *tile = TileType::Floor; 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); - } + _ => { + 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); } + + monster.ticks_until_move = ticks_for_kind(monster.kind); } } } 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/tile_data.rs b/src/tile_data.rs index 775889b..b9dba3a 100644 --- a/src/tile_data.rs +++ b/src/tile_data.rs @@ -1,10 +1,10 @@ use bracket_lib::prelude::*; -use crate::graphics::vga_color as vga; +use crate::vga_color as vga; #[derive(PartialEq, Copy, Clone)] pub struct TileData { - pub glyph: FontCharType, + pub glyph: u16, pub color_fg: (u8, u8, u8), pub color_bg: (u8, u8, u8), pub serialized_char: char, diff --git a/src/graphics/vga_color.rs b/src/vga_color.rs similarity index 100% rename from src/graphics/vga_color.rs rename to src/vga_color.rs