kroz-rs/src/state.rs
2022-02-01 02:54:08 -05:00

244 lines
9.3 KiB
Rust

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>();
map.draw(ctx);
let positions = self.ecs.read_storage::<Position>();
let renderables = self.ecs.read_storage::<Renderable>();
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::<Entity>();
let mut stats = self.ecs.write_resource::<Stats>();
let positions = self.ecs.read_storage::<Position>();
let mut wants_to_whips = self.ecs.write_storage::<WantsToWhip>();
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::<SoundOutput>();
let sound_effects = self.ecs.fetch::<SoundEffects>();
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::<SoundOutput>();
let sound_effects = self.ecs.fetch::<SoundEffects>();
sound_system.play_sound(sound_effects.pickup.clone());
}
VirtualKeyCode::D => {
let mut show_debug_info = self.ecs.write_resource::<ShowDebugInfo>();
show_debug_info.0 = !show_debug_info.0;
}
VirtualKeyCode::Escape => {
ctx.quit();
}
_ => {
let mut sound_system = self.ecs.write_resource::<SoundOutput>();
let sound_effects = self.ecs.fetch::<SoundEffects>();
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::<Position>();
let mut players = self.ecs.write_storage::<Player>();
let monsters = self.ecs.write_storage::<Monster>();
let mut map = self.ecs.write_resource::<Map>();
let mut stats = self.ecs.write_resource::<Stats>();
let mut sound_output = self.ecs.write_resource::<SoundOutput>();
let wants_to_whips = self.ecs.read_storage::<WantsToWhip>();
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::<SoundEffects>();
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::<Point>();
player_pos.x = pos.x;
player_pos.y = pos.y;
map.set_tile_content_at(destination, player_entity);
self.ecs.write_resource::<Clock>().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::<Position>();
let wants_to_whips = self.ecs.read_storage::<WantsToWhip>();
let map = self.ecs.read_resource::<Map>();
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::<Clock>();
if !self.ecs.read_resource::<StopClock>().0 {
clock.try_tick();
} else {
clock.reset();
}
}
}