Set up discovery system
This commit is contained in:
parent
98cea211fc
commit
763f4b7396
|
@ -16,9 +16,12 @@ pub struct CharRender {
|
|||
pub glyph: char,
|
||||
}
|
||||
|
||||
/// Entities that the user can control using the keyboard.
|
||||
/// Entities that users can control.
|
||||
#[derive(Component)]
|
||||
pub struct Player;
|
||||
pub struct Player {
|
||||
/// The list of cells that are known to the player.
|
||||
pub known_cells: Vec<Vec<bool>>,
|
||||
}
|
||||
|
||||
/// Entities that take turns periodically.
|
||||
#[derive(Component)]
|
||||
|
@ -46,6 +49,18 @@ pub fn register_all(world: &mut World) {
|
|||
world.register::<Mobile>();
|
||||
}
|
||||
|
||||
impl From<&Position> for (i32, i32) {
|
||||
fn from(pos: &Position) -> Self {
|
||||
(pos.x, pos.y)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(i32, i32)> for Position {
|
||||
fn from((x, y): (i32, i32)) -> Self {
|
||||
Self { x, y }
|
||||
}
|
||||
}
|
||||
|
||||
/// An action that a mob can perform that takes up a turn.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum MobAction {
|
||||
|
|
67
src/level.rs
67
src/level.rs
|
@ -4,7 +4,12 @@ use pancurses::Window;
|
|||
use rand::Rng;
|
||||
use specs::prelude::*;
|
||||
|
||||
use crate::{components::{CharRender, Position}, io::{Color, set_color}, rooms};
|
||||
use crate::{
|
||||
components::{CharRender, Position},
|
||||
io::{set_color, Color},
|
||||
rooms,
|
||||
visibility::{visible, CellVisibility, Lighting},
|
||||
};
|
||||
|
||||
/// The size of a dungeon level, in tiles.
|
||||
pub const LEVEL_SIZE: (usize, usize) = (80, 24);
|
||||
|
@ -39,6 +44,23 @@ pub enum DungeonTile {
|
|||
Downstair,
|
||||
}
|
||||
|
||||
/// A style for drawing a particular tile in the dungeon.
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum DrawStyle {
|
||||
/// Don't draw the tile. (The player has never seen this tile
|
||||
/// before, and is unaware of its contents.)
|
||||
Undiscovered,
|
||||
|
||||
/// Draw the tile in a darker color than normal. (The player has
|
||||
/// seen this tile before and remembers its contents, but is not
|
||||
/// actively looking at it.)
|
||||
Discovered,
|
||||
|
||||
/// Draw the tile in a normal color. (The player can see the tile
|
||||
/// from where they are standing.)
|
||||
Visible,
|
||||
}
|
||||
|
||||
impl DungeonTile {
|
||||
/// Whether this tile is considered a floor tile, for the purposes
|
||||
/// of rendering walls.
|
||||
|
@ -99,20 +121,22 @@ impl DungeonLevel {
|
|||
/// Draws a level on the display window. Draws only the cells for
|
||||
/// which `filter` returns true; use `|_| true` to draw the whole
|
||||
/// level.
|
||||
pub fn draw(&self, win: &Window, filter: impl Fn((i32, i32)) -> bool) {
|
||||
pub fn draw(&self, win: &Window, visibility: impl Fn((i32, i32)) -> DrawStyle) {
|
||||
for y in 0..LEVEL_SIZE.1 {
|
||||
win.mv(y as _, 0);
|
||||
for x in 0..LEVEL_SIZE.0 {
|
||||
if (x + y) % 2 == 0 {
|
||||
set_color(win, Color::Yellow);
|
||||
} else {
|
||||
set_color(win, Color::Red);
|
||||
}
|
||||
|
||||
win.addch(if filter((x as _, y as _)) {
|
||||
self.render_tile(x, y)
|
||||
} else {
|
||||
' '
|
||||
win.addch(match visibility((x as _, y as _)) {
|
||||
DrawStyle::Undiscovered => ' ',
|
||||
DrawStyle::Discovered => {
|
||||
// Using red as a placeholder; black doesn't
|
||||
// seem to work rn(?)
|
||||
set_color(win, Color::Red);
|
||||
self.render_tile(x, y)
|
||||
}
|
||||
DrawStyle::Visible => {
|
||||
set_color(win, Color::White);
|
||||
self.render_tile(x, y)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -168,6 +192,25 @@ impl DungeonLevel {
|
|||
pub fn tile(&self, x: i32, y: i32) -> &DungeonTile {
|
||||
&self.tiles[y as usize][x as usize]
|
||||
}
|
||||
|
||||
/// Whether a monster standing at `from` can see the contents of cell
|
||||
/// `to`.
|
||||
pub fn can_see(&self, from: (i32, i32), to: (i32, i32)) -> bool {
|
||||
visible(
|
||||
from,
|
||||
to,
|
||||
Some(10),
|
||||
|(x, y)| {
|
||||
if self.tile(x, y).is_navigable() {
|
||||
CellVisibility::Transparent
|
||||
} else {
|
||||
CellVisibility::Blocking
|
||||
}
|
||||
},
|
||||
// Level is fully lit for now.
|
||||
|(_x, _y)| Lighting::Lit,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for DungeonLevel {
|
||||
|
|
18
src/main.rs
18
src/main.rs
|
@ -1,11 +1,11 @@
|
|||
use components::{register_all, CharRender, MobAction, Mobile, Player, Position, TurnTaker};
|
||||
use io::init_window;
|
||||
use level::DungeonLevel;
|
||||
use level::{DungeonLevel, LEVEL_SIZE};
|
||||
|
||||
use player::player_turn;
|
||||
use rand::thread_rng;
|
||||
use specs::prelude::*;
|
||||
use systems::{MobSystem, TimeSystem};
|
||||
use systems::{DiscoverySystem, MobSystem, TimeSystem};
|
||||
|
||||
mod components;
|
||||
mod io;
|
||||
|
@ -28,12 +28,13 @@ fn main() {
|
|||
|
||||
world
|
||||
.create_entity()
|
||||
.with(Position {
|
||||
x: spawn_pos.0,
|
||||
y: spawn_pos.1,
|
||||
})
|
||||
.with(Position::from(spawn_pos))
|
||||
.with(CharRender { glyph: '@' })
|
||||
.with(Player)
|
||||
.with(Player {
|
||||
known_cells: (0..LEVEL_SIZE.1)
|
||||
.map(|_| (0..LEVEL_SIZE.0).map(|_| false).collect())
|
||||
.collect(),
|
||||
})
|
||||
.with(Mobile {
|
||||
next_action: MobAction::Nop,
|
||||
})
|
||||
|
@ -46,6 +47,7 @@ fn main() {
|
|||
let mut dispatcher = DispatcherBuilder::new()
|
||||
.with(TimeSystem, "time", &[])
|
||||
.with(MobSystem, "mobs", &[])
|
||||
.with(DiscoverySystem, "discovery", &[])
|
||||
.build();
|
||||
|
||||
let mut window = match init_window() {
|
||||
|
@ -53,7 +55,7 @@ fn main() {
|
|||
Err(err) => {
|
||||
println!("Error initializing window: {}", err);
|
||||
return;
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
loop {
|
||||
|
|
|
@ -5,9 +5,8 @@ use specs::prelude::*;
|
|||
|
||||
use crate::{
|
||||
components::{CharRender, MobAction, Mobile, Player, Position},
|
||||
level::DungeonLevel,
|
||||
io::quit,
|
||||
visibility::{visible, CellVisibility, Lighting},
|
||||
level::{DrawStyle, DungeonLevel},
|
||||
};
|
||||
|
||||
/// Runs a player turn on the ECS, using the given `screen` for input
|
||||
|
@ -96,21 +95,18 @@ fn render_screen(ecs: &mut World, screen: &mut Window) {
|
|||
|
||||
// Draw the base level.
|
||||
let level = ecs.fetch::<DungeonLevel>();
|
||||
let known_cells = &plrs.join().next().expect("Player must exist").known_cells;
|
||||
level.draw(screen, |cell| {
|
||||
visible(
|
||||
(player_pos.x, player_pos.y),
|
||||
cell,
|
||||
Some(10),
|
||||
|(x, y)| {
|
||||
if level.tile(x, y).is_navigable() {
|
||||
CellVisibility::Transparent
|
||||
match level.can_see(player_pos.into(), cell) {
|
||||
true => DrawStyle::Visible,
|
||||
false => {
|
||||
if known_cells[cell.1 as usize][cell.0 as usize] {
|
||||
DrawStyle::Discovered
|
||||
} else {
|
||||
CellVisibility::Blocking
|
||||
DrawStyle::Undiscovered
|
||||
}
|
||||
},
|
||||
// Level is fully lit for now.
|
||||
|(_x, _y)| Lighting::Lit,
|
||||
)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Draw all renderable entities.
|
||||
|
|
|
@ -2,7 +2,10 @@
|
|||
|
||||
use specs::prelude::*;
|
||||
|
||||
use crate::components::{MobAction, Mobile, Position, TurnTaker};
|
||||
use crate::{
|
||||
components::{MobAction, Mobile, Player, Position, TurnTaker},
|
||||
level::DungeonLevel,
|
||||
};
|
||||
|
||||
/// System for ticking the turn counter on every entity; this system
|
||||
/// implements the relationship between real-world time and in-game
|
||||
|
@ -46,3 +49,26 @@ impl<'a> System<'a> for MobSystem {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// System for updating player-discovered cells.
|
||||
pub struct DiscoverySystem;
|
||||
|
||||
impl<'a> System<'a> for DiscoverySystem {
|
||||
type SystemData = (
|
||||
WriteStorage<'a, Player>,
|
||||
ReadStorage<'a, Position>,
|
||||
ReadExpect<'a, DungeonLevel>,
|
||||
);
|
||||
|
||||
fn run(&mut self, (mut players, position, level): Self::SystemData) {
|
||||
for (player, pos) in (&mut players, &position).join() {
|
||||
for (y, row) in player.known_cells.iter_mut().enumerate() {
|
||||
for (x, known) in row.iter_mut().enumerate() {
|
||||
if level.can_see(pos.into(), (x as _, y as _)) {
|
||||
*known = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,10 +14,9 @@ pub enum CellVisibility {
|
|||
/// How well-lit a cell is.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Lighting {
|
||||
/// Monsters can only see in this cell if the cell is immediately
|
||||
/// adjacent to the monster.
|
||||
Dark,
|
||||
|
||||
// /// Monsters can only see in this cell if the cell is immediately
|
||||
// /// adjacent to the monster.
|
||||
// Dark,
|
||||
/// Monsters can see in this cell from far away.
|
||||
Lit,
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue