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 { ecs: World, } impl GameState for State { 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(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.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(); } } }