Add first draft of monsters
This commit is contained in:
parent
71a3eb3135
commit
4696b53f90
9 changed files with 262 additions and 34 deletions
|
@ -1,9 +1,11 @@
|
|||
pub use left_mover::LeftMover;
|
||||
pub use player::Player;
|
||||
pub use position::Position;
|
||||
pub use renderable::Renderable;
|
||||
|
||||
pub mod left_mover;
|
||||
pub mod monster;
|
||||
pub mod player;
|
||||
pub mod position;
|
||||
pub mod renderable;
|
||||
|
||||
pub use left_mover::LeftMover;
|
||||
pub use monster::Monster;
|
||||
pub use player::Player;
|
||||
pub use position::Position;
|
||||
pub use renderable::Renderable;
|
||||
|
|
41
src/components/monster.rs
Normal file
41
src/components/monster.rs
Normal file
|
@ -0,0 +1,41 @@
|
|||
use crate::vga_color as vga;
|
||||
use bracket_lib::prelude::*;
|
||||
use specs::prelude::*;
|
||||
use specs_derive::Component;
|
||||
|
||||
#[derive(Component, Debug)]
|
||||
pub struct Monster {
|
||||
pub kind: MonsterKind,
|
||||
pub ticks_until_move: i32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum MonsterKind {
|
||||
Slow,
|
||||
Medium,
|
||||
Fast,
|
||||
}
|
||||
|
||||
pub fn glyphs_for_kind(kind: MonsterKind) -> Vec<u16> {
|
||||
match kind {
|
||||
MonsterKind::Slow => vec![to_cp437('Ä'), to_cp437('A')],
|
||||
MonsterKind::Medium => vec![to_cp437('ö'), to_cp437('Ö')],
|
||||
MonsterKind::Fast => vec![to_cp437('Ω')],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn color_for_kind(kind: MonsterKind) -> RGB {
|
||||
match kind {
|
||||
MonsterKind::Slow => RGB::named(vga::RED_BRIGHT),
|
||||
MonsterKind::Medium => RGB::named(vga::GREEN_BRIGHT),
|
||||
MonsterKind::Fast => RGB::named(vga::BLUE_BRIGHT),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ticks_for_kind(kind: MonsterKind) -> i32 {
|
||||
match kind {
|
||||
MonsterKind::Slow => 10,
|
||||
MonsterKind::Medium => 8,
|
||||
MonsterKind::Fast => 6,
|
||||
}
|
||||
}
|
|
@ -1,3 +1,8 @@
|
|||
/// Determined from trial and error, comparing to the original game in DOSBox,
|
||||
/// Fast PC mode at 400 cycles/ms.
|
||||
pub const CLOCK_PERIOD: f32 = 0.184;
|
||||
pub const PLAYER_STEP_PERIOD: f32 = 1.0 / 7.5;
|
||||
|
||||
pub const SIDEBAR_POS_X: i32 = 66;
|
||||
pub const SIDEBAR_POS_Y: i32 = 0;
|
||||
|
||||
|
|
92
src/main.rs
92
src/main.rs
|
@ -1,17 +1,5 @@
|
|||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
use bracket_lib::prelude::*;
|
||||
use components::{LeftMover, Player, Position, Renderable};
|
||||
use constants::{MAP_X, MAP_Y};
|
||||
use map::Map;
|
||||
use resources::LevelNumber;
|
||||
use sound::SoundSystem;
|
||||
use sound_effects::SoundEffects;
|
||||
use specs::prelude::*;
|
||||
use std::time::{Duration, Instant};
|
||||
use systems::LeftWalker;
|
||||
use vga_color as vga;
|
||||
|
||||
pub mod components;
|
||||
pub mod constants;
|
||||
mod map;
|
||||
|
@ -22,6 +10,21 @@ mod sound_effects;
|
|||
pub mod systems;
|
||||
pub mod vga_color;
|
||||
|
||||
use bracket_lib::prelude::*;
|
||||
use components::{
|
||||
monster::{color_for_kind, glyphs_for_kind, ticks_for_kind, MonsterKind},
|
||||
LeftMover, Monster, Player, Position, Renderable,
|
||||
};
|
||||
use constants::*;
|
||||
use map::{Map, TileType};
|
||||
use resources::{Clock, LevelNumber, ShowDebugInfo};
|
||||
use sound::SoundSystem;
|
||||
use sound_effects::SoundEffects;
|
||||
use specs::prelude::*;
|
||||
use std::time::{Duration, Instant};
|
||||
use systems::{LeftWalker, MonsterMotion};
|
||||
use vga_color as vga;
|
||||
|
||||
struct State {
|
||||
ecs: World,
|
||||
sound_system: SoundSystem,
|
||||
|
@ -34,7 +37,7 @@ fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World, sound_system: &m
|
|||
|
||||
for (player, pos) in (&mut players, &mut positions).join() {
|
||||
let now = Instant::now();
|
||||
if now - player.last_moved > Duration::from_secs_f32(0.15) {
|
||||
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,
|
||||
|
@ -43,13 +46,19 @@ fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World, sound_system: &m
|
|||
let mut sound_effects = ecs.fetch_mut::<SoundEffects>();
|
||||
|
||||
if map.in_bounds(destination) {
|
||||
if map.can_player_enter(destination) {
|
||||
if map.is_solid(destination) {
|
||||
sound_system.play_sound(sound_effects.blocked.clone());
|
||||
} else {
|
||||
pos.x = destination.x;
|
||||
pos.y = destination.y;
|
||||
|
||||
let mut player_pos = ecs.write_resource::<Point>();
|
||||
player_pos.x = pos.x;
|
||||
player_pos.y = pos.y;
|
||||
|
||||
ecs.write_resource::<Clock>().force_tick();
|
||||
|
||||
sound_system.play_sound(sound_effects.step.clone());
|
||||
} else {
|
||||
sound_system.play_sound(sound_effects.blocked.clone());
|
||||
}
|
||||
} else {
|
||||
let static_sound = sound_effects.get_new_static_effect(sound_system);
|
||||
|
@ -94,6 +103,10 @@ fn player_input(gs: &mut State, ctx: &mut BTerm) {
|
|||
let sound_effects = gs.ecs.fetch::<SoundEffects>();
|
||||
gs.sound_system.play_sound(sound_effects.pickup.clone());
|
||||
}
|
||||
VirtualKeyCode::D => {
|
||||
let mut show_debug_info = gs.ecs.write_resource::<ShowDebugInfo>();
|
||||
show_debug_info.0 = !show_debug_info.0;
|
||||
}
|
||||
VirtualKeyCode::Escape => {
|
||||
ctx.quit();
|
||||
}
|
||||
|
@ -136,7 +149,10 @@ impl State {
|
|||
fn run_systems(&mut self) {
|
||||
let mut lw = LeftWalker {};
|
||||
lw.run_now(&self.ecs);
|
||||
let mut mm = MonsterMotion {};
|
||||
mm.run_now(&self.ecs);
|
||||
self.ecs.maintain();
|
||||
self.ecs.write_resource::<Clock>().try_tick();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -157,17 +173,54 @@ fn main() -> BError {
|
|||
};
|
||||
|
||||
gs.ecs.insert(LevelNumber(0));
|
||||
gs.ecs.insert(ShowDebugInfo(false));
|
||||
gs.ecs.insert(Clock {
|
||||
last_ticked: Instant::now(),
|
||||
has_ticked: false,
|
||||
ticks: 0,
|
||||
});
|
||||
gs.ecs.insert(sound_effects);
|
||||
gs.ecs.insert(Map::new());
|
||||
|
||||
gs.ecs.register::<Position>();
|
||||
gs.ecs.register::<Renderable>();
|
||||
gs.ecs.register::<LeftMover>();
|
||||
gs.ecs.register::<Monster>();
|
||||
gs.ecs.register::<Player>();
|
||||
|
||||
let map = Map::new();
|
||||
let mut rng = RandomNumberGenerator::new();
|
||||
for (i, tile) in &mut map.get_tiles().iter().enumerate() {
|
||||
if rng.roll_dice(1, 16) < 2 && *tile == TileType::Floor {
|
||||
let position = map.index_to_point2d(i);
|
||||
let kind = MonsterKind::Slow;
|
||||
gs.ecs
|
||||
.create_entity()
|
||||
.with(Position {
|
||||
x: position.x,
|
||||
y: position.y,
|
||||
})
|
||||
.with(Renderable {
|
||||
glyph: *rng.random_slice_entry(&glyphs_for_kind(kind)).unwrap(),
|
||||
fg: color_for_kind(kind),
|
||||
bg: RGB::named(vga::BLACK),
|
||||
})
|
||||
.with(Monster {
|
||||
kind,
|
||||
ticks_until_move: ticks_for_kind(kind),
|
||||
})
|
||||
.build();
|
||||
}
|
||||
}
|
||||
gs.ecs.insert(map);
|
||||
|
||||
let player_start_pos = Point { x: 40, y: 22 };
|
||||
|
||||
gs.ecs
|
||||
.create_entity()
|
||||
.with(Position { x: 40, y: 22 })
|
||||
.with(Position {
|
||||
x: player_start_pos.x,
|
||||
y: player_start_pos.y,
|
||||
})
|
||||
.with(Renderable {
|
||||
glyph: to_cp437('☻'),
|
||||
fg: RGB::named(vga::YELLOW_BRIGHT),
|
||||
|
@ -183,6 +236,9 @@ fn main() -> BError {
|
|||
})
|
||||
.build();
|
||||
|
||||
gs.ecs
|
||||
.insert(Point::new(player_start_pos.x, player_start_pos.y));
|
||||
|
||||
// for i in 0..10 {
|
||||
// gs.ecs
|
||||
// .create_entity()
|
||||
|
|
14
src/map.rs
14
src/map.rs
|
@ -24,6 +24,12 @@ impl Algorithm2D for Map {
|
|||
}
|
||||
}
|
||||
|
||||
impl Default for Map {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Map {
|
||||
pub fn new() -> Self {
|
||||
let mut tiles = vec![TileType::Floor; MAP_SIZE];
|
||||
|
@ -40,6 +46,10 @@ impl Map {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_tiles(&self) -> &Vec<TileType> {
|
||||
&self.tiles
|
||||
}
|
||||
|
||||
pub fn draw(&self, ctx: &mut BTerm) {
|
||||
// Border
|
||||
ctx.fill_region(
|
||||
|
@ -85,7 +95,7 @@ impl Map {
|
|||
self.tiles[self.point2d_to_index(point)]
|
||||
}
|
||||
|
||||
pub fn can_player_enter(&self, point: Point) -> bool {
|
||||
self.tile_at(point) != TileType::Wall
|
||||
pub fn is_solid(&self, point: Point) -> bool {
|
||||
self.tile_at(point) == TileType::Wall
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,2 +1,34 @@
|
|||
use crate::constants::CLOCK_PERIOD;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct LevelNumber(pub u32);
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ShowDebugInfo(pub bool);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::constants::{SIDEBAR_POS_X, SIDEBAR_POS_Y};
|
||||
use crate::resources::{Clock, ShowDebugInfo};
|
||||
use crate::vga_color as vga;
|
||||
use crate::{LevelNumber, Player};
|
||||
use bracket_lib::prelude::*;
|
||||
|
@ -95,6 +96,7 @@ pub fn draw(ecs: &World, ctx: &mut BTerm) {
|
|||
ctx.print(SIDEBAR_POS_X + 3, SIDEBAR_POS_Y + 23, "Save");
|
||||
ctx.print(SIDEBAR_POS_X + 3, SIDEBAR_POS_Y + 24, "Restore");
|
||||
|
||||
if ecs.read_resource::<ShowDebugInfo>().0 {
|
||||
ctx.print_color_right(
|
||||
SIDEBAR_POS_X + 14,
|
||||
SIDEBAR_POS_Y,
|
||||
|
@ -102,4 +104,13 @@ pub fn draw(ecs: &World, ctx: &mut BTerm) {
|
|||
RGB::named(vga::BLACK),
|
||||
&format!("{}", ctx.fps),
|
||||
);
|
||||
|
||||
ctx.print_color(
|
||||
0,
|
||||
0,
|
||||
RGB::named(vga::YELLOW_BRIGHT),
|
||||
RGB::named(vga::BLACK),
|
||||
&format!("{}", ecs.read_resource::<Clock>().ticks),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
pub use left_walker::LeftWalker;
|
||||
|
||||
pub mod left_walker;
|
||||
pub mod monster_motion;
|
||||
|
||||
pub use left_walker::LeftWalker;
|
||||
pub use monster_motion::MonsterMotion;
|
||||
|
|
69
src/systems/monster_motion.rs
Normal file
69
src/systems/monster_motion.rs
Normal file
|
@ -0,0 +1,69 @@
|
|||
use crate::{
|
||||
components::{
|
||||
monster::{self, ticks_for_kind},
|
||||
Monster, Position, Renderable,
|
||||
},
|
||||
map::Map,
|
||||
resources::Clock,
|
||||
};
|
||||
use bracket_lib::{prelude::*, random::RandomNumberGenerator};
|
||||
use specs::prelude::*;
|
||||
|
||||
pub struct MonsterMotion {}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
impl<'a> System<'a> for MonsterMotion {
|
||||
type SystemData = (
|
||||
ReadExpect<'a, Clock>,
|
||||
ReadExpect<'a, Point>,
|
||||
ReadExpect<'a, Map>,
|
||||
WriteStorage<'a, Monster>,
|
||||
WriteStorage<'a, Position>,
|
||||
WriteStorage<'a, Renderable>,
|
||||
);
|
||||
|
||||
fn run(
|
||||
&mut self,
|
||||
(clock, player_pos, map, mut monster, mut position, mut renderable): Self::SystemData,
|
||||
) {
|
||||
for (monster, position, renderable) in (&mut monster, &mut position, &mut renderable).join()
|
||||
{
|
||||
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 !map.is_solid(destination) {
|
||||
position.x = destination.x;
|
||||
position.y = destination.y;
|
||||
}
|
||||
monster.ticks_until_move = ticks_for_kind(monster.kind);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue