This commit is contained in:
Timothy Warren 2022-01-21 15:55:13 -05:00
parent fe31fdaa13
commit 5b8d127af0
12 changed files with 70 additions and 6 deletions

View File

@ -20,4 +20,10 @@ fmt:
fix: fmt fix: fmt
cargo fix --allow-dirty --allow-staged cargo fix --allow-dirty --allow-staged
.phony: run-pi clean check run fmt fix lint docs:
cargo doc
open-docs:
cargo doc --open
.phony: run-pi clean check run fmt fix lint docs open-docs

View File

@ -1,3 +1,5 @@
//! Systems for making NPCs come to life
mod adjacent_ai_system; mod adjacent_ai_system;
mod approach_ai_system; mod approach_ai_system;
mod chase_ai_system; mod chase_ai_system;

View File

@ -1,11 +1,14 @@
//! Handle rendering of the viewport
use ::rltk::{Point, Rltk}; use ::rltk::{Point, Rltk};
use ::specs::prelude::*; use ::specs::prelude::*;
use crate::map::tile_glyph; use crate::map::tile_glyph;
use crate::{colors, Hidden, Map, Position, Renderable}; use crate::{colors, Hidden, Map, Position, Renderable};
/// Whether to render an outline of the current map's boundaries
const SHOW_BOUNDARIES: bool = false; const SHOW_BOUNDARIES: bool = false;
/// Get the rectangle representing the current viewport
pub fn get_screen_bounds(ecs: &World, _ctx: &mut Rltk) -> (i32, i32, i32, i32) { pub fn get_screen_bounds(ecs: &World, _ctx: &mut Rltk) -> (i32, i32, i32, i32) {
let player_pos = ecs.fetch::<Point>(); let player_pos = ecs.fetch::<Point>();
// let (x_chars, y_chars) = ctx.get_char_size(); // let (x_chars, y_chars) = ctx.get_char_size();
@ -22,6 +25,7 @@ pub fn get_screen_bounds(ecs: &World, _ctx: &mut Rltk) -> (i32, i32, i32, i32) {
(min_x, max_x, min_y, max_y) (min_x, max_x, min_y, max_y)
} }
/// Render the current viewport
pub fn render_camera(ecs: &World, ctx: &mut Rltk) { pub fn render_camera(ecs: &World, ctx: &mut Rltk) {
let map = ecs.fetch::<Map>(); let map = ecs.fetch::<Map>();
let (min_x, max_x, min_y, max_y) = get_screen_bounds(ecs, ctx); let (min_x, max_x, min_y, max_y) = get_screen_bounds(ecs, ctx);

View File

@ -15,9 +15,11 @@ pub use targeting::*;
use crate::spatial; use crate::spatial;
lazy_static! { lazy_static! {
/// The shared queue of effects
pub static ref EFFECT_QUEUE: Mutex<VecDeque<EffectSpawner>> = Mutex::new(VecDeque::new()); pub static ref EFFECT_QUEUE: Mutex<VecDeque<EffectSpawner>> = Mutex::new(VecDeque::new());
} }
/// The kind of effect to cause
pub enum EffectType { pub enum EffectType {
Damage { Damage {
amount: i32, amount: i32,
@ -51,8 +53,8 @@ pub enum EffectType {
}, },
} }
/// Who, or what the effect should affect.
#[derive(Clone)] #[derive(Clone)]
#[allow(dead_code)]
pub enum Targets { pub enum Targets {
Single { target: Entity }, Single { target: Entity },
TargetList { targets: Vec<Entity> }, TargetList { targets: Vec<Entity> },
@ -60,12 +62,14 @@ pub enum Targets {
Tiles { tiles: Vec<i32> }, Tiles { tiles: Vec<i32> },
} }
/// An effect for the queue
pub struct EffectSpawner { pub struct EffectSpawner {
pub creator: Option<Entity>, pub creator: Option<Entity>,
pub effect_type: EffectType, pub effect_type: EffectType,
pub targets: Targets, pub targets: Targets,
} }
/// Adds an effect to the queue
pub fn add_effect(creator: Option<Entity>, effect_type: EffectType, targets: Targets) { pub fn add_effect(creator: Option<Entity>, effect_type: EffectType, targets: Targets) {
EFFECT_QUEUE.lock().unwrap().push_back(EffectSpawner { EFFECT_QUEUE.lock().unwrap().push_back(EffectSpawner {
creator, creator,

View File

@ -1,9 +1,12 @@
/// A struct to hold the player's game log.
#[derive(Default)] #[derive(Default)]
pub struct GameLog { pub struct GameLog {
pub entries: Vec<String>, pub entries: Vec<String>,
} }
impl GameLog { impl GameLog {
/// Convenience constructor that adds the first
/// entry to the game log.
pub fn new<S: ToString>(first_entry: S) -> Self { pub fn new<S: ToString>(first_entry: S) -> Self {
let mut log = GameLog::default(); let mut log = GameLog::default();
log.append(first_entry); log.append(first_entry);
@ -11,6 +14,10 @@ impl GameLog {
log log
} }
/// Convenience method for adding an entry to the game log.
/// Generic on the [`ToString`] trait so that [`&str`], [`String`],
/// and `&String` types, all work without more type juggling
/// at the call site.
pub fn append<S: ToString>(&mut self, s: S) { pub fn append<S: ToString>(&mut self, s: S) {
self.entries.push(s.to_string()); self.entries.push(s.to_string());
} }

View File

@ -1,3 +1,6 @@
//! Roguelike
//!
//! An implementation of a rogue-like dungeon-crawler game.
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;
@ -40,6 +43,21 @@ pub use rect::Rect;
pub use state::*; pub use state::*;
/// Cut down on the amount of syntax to register components /// Cut down on the amount of syntax to register components
///
/// Compare:
/// ```ignore
/// let mut game_state = State::new();
///
/// register!(state <- ComponentA, ComponentB, ...);
/// ```
///
/// Without macro:
/// ```ignore
/// let mut game_state = State::new();
///
/// state.ecs.register::<ComponentA>();
/// state.ecs.register::<ComponentB>();
/// ```
macro_rules! register { macro_rules! register {
// $state is needed to get the scope at the usage point // $state is needed to get the scope at the usage point
// $Type is the Component type that is being registered // $Type is the Component type that is being registered
@ -50,6 +68,14 @@ macro_rules! register {
} }
} }
/// Sets up the game.
///
/// * Creates the [`State`] object
/// * Registers [`components`](register!)
/// * Loads the dynamic game entities using the [`raws`](crate::raws::load_raws) module
/// * Generates the map builder environment
/// * Creates the [`Player`](crate::spawner::player)
/// * Generates the first [`map`](crate::state::State::generate_world_map)
fn init_state() -> State { fn init_state() -> State {
let mut state = State::new(); let mut state = State::new();
@ -146,6 +172,7 @@ fn init_state() -> State {
state state
} }
/// The entry point
fn main() -> ::rltk::BError { fn main() -> ::rltk::BError {
let context = ::rltk::RltkBuilder::simple(80, 60) let context = ::rltk::RltkBuilder::simple(80, 60)
.unwrap() .unwrap()

View File

@ -1,13 +1,13 @@
//! An object to hold the state of the current level's map
mod dungeon; mod dungeon;
mod themes; mod themes;
mod tiletype; mod tiletype;
use std::collections::HashSet; use std::collections::HashSet;
use ::rltk::{Algorithm2D, BaseMap, Point, SmallVec}; use ::rltk::{Algorithm2D, BaseMap, Point, SmallVec, RGB};
use ::serde::{Deserialize, Serialize}; use ::serde::{Deserialize, Serialize};
pub use dungeon::*; pub use dungeon::*;
use rltk::RGB;
pub use themes::*; pub use themes::*;
pub use tiletype::{tile_opaque, tile_walkable, TileType}; pub use tiletype::{tile_opaque, tile_walkable, TileType};
@ -30,6 +30,8 @@ pub struct Map {
} }
impl Map { impl Map {
/// Converts an `(x, y)` coordinate to the flat index within the
/// `Map`'s internal array.
pub fn xy_idx(&self, x: i32, y: i32) -> usize { pub fn xy_idx(&self, x: i32, y: i32) -> usize {
(y as usize * self.width as usize) + x as usize (y as usize * self.width as usize) + x as usize
} }
@ -83,7 +85,7 @@ impl BaseMap for Map {
} }
fn get_available_exits(&self, idx: usize) -> SmallVec<[(usize, f32); 10]> { fn get_available_exits(&self, idx: usize) -> SmallVec<[(usize, f32); 10]> {
let mut exits = rltk::SmallVec::new(); let mut exits = SmallVec::new();
let x = idx as i32 % self.width; let x = idx as i32 % self.width;
let y = idx as i32 / self.width; let y = idx as i32 / self.width;
let w = self.width as usize; let w = self.width as usize;
@ -125,7 +127,7 @@ impl BaseMap for Map {
let p1 = Point::new(idx1 % w, idx1 / w); let p1 = Point::new(idx1 % w, idx1 / w);
let p2 = Point::new(idx2 % w, idx2 / w); let p2 = Point::new(idx2 % w, idx2 / w);
rltk::DistanceAlg::Pythagoras.distance2d(p1, p2) ::rltk::DistanceAlg::Pythagoras.distance2d(p1, p2)
} }
} }

View File

@ -1,3 +1,4 @@
//! Functionality that is common to all the currently generated maps.
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use ::rltk::{Point, RandomNumberGenerator}; use ::rltk::{Point, RandomNumberGenerator};

View File

@ -1,3 +1,4 @@
//! Different looks for the same set of [`TileType`]s
use ::rltk::{to_cp437, FontCharType, RGB}; use ::rltk::{to_cp437, FontCharType, RGB};
use super::{Map, TileType}; use super::{Map, TileType};

View File

@ -1,5 +1,6 @@
use ::serde::{Deserialize, Serialize}; use ::serde::{Deserialize, Serialize};
/// The type of location for a specific square of a [`Map`](crate::map::Map)
#[derive(PartialEq, Eq, Hash, Copy, Clone, Serialize, Deserialize)] #[derive(PartialEq, Eq, Hash, Copy, Clone, Serialize, Deserialize)]
pub enum TileType { pub enum TileType {
Wall, Wall,
@ -17,6 +18,7 @@ pub enum TileType {
Stalagmite, Stalagmite,
} }
/// Can you go through this type of map tile?
pub fn tile_walkable(tt: TileType) -> bool { pub fn tile_walkable(tt: TileType) -> bool {
matches!( matches!(
tt, tt,
@ -32,6 +34,7 @@ pub fn tile_walkable(tt: TileType) -> bool {
) )
} }
/// Is it impossible to see through this type of map tile?
pub fn tile_opaque(tt: TileType) -> bool { pub fn tile_opaque(tt: TileType) -> bool {
matches!( matches!(
tt, tt,
@ -39,6 +42,7 @@ pub fn tile_opaque(tt: TileType) -> bool {
) )
} }
/// How difficult is it to cross this type of map tile?
pub fn tile_cost(tt: TileType) -> f32 { pub fn tile_cost(tt: TileType) -> f32 {
match tt { match tt {
TileType::Road => 0.8, TileType::Road => 0.8,

View File

@ -1,3 +1,4 @@
//! Four points, one plane, 90° angles
use ::serde::{Deserialize, Serialize}; use ::serde::{Deserialize, Serialize};
#[derive(PartialEq, Copy, Clone, Serialize, Deserialize)] #[derive(PartialEq, Copy, Clone, Serialize, Deserialize)]

View File

@ -1,3 +1,4 @@
//!
use ::rltk::{GameState, Point, Rltk}; use ::rltk::{GameState, Point, Rltk};
use ::specs::prelude::*; use ::specs::prelude::*;
@ -21,8 +22,10 @@ use crate::trigger_system::TriggerSystem;
use crate::visibility_system::VisibilitySystem; use crate::visibility_system::VisibilitySystem;
use crate::{ai, camera, damage_system, effects, saveload_system, spawner}; use crate::{ai, camera, damage_system, effects, saveload_system, spawner};
/// Whether to show a visual representation of map generation
pub const SHOW_MAPGEN_VISUALIZER: bool = false; pub const SHOW_MAPGEN_VISUALIZER: bool = false;
/// The main actions possible with a vendor
#[derive(PartialEq, Copy, Clone)] #[derive(PartialEq, Copy, Clone)]
pub enum VendorMode { pub enum VendorMode {
Buy, Buy,
@ -30,6 +33,7 @@ pub enum VendorMode {
} }
#[derive(PartialEq, Copy, Clone)] #[derive(PartialEq, Copy, Clone)]
/// The states for the game engine's state machine
pub enum RunState { pub enum RunState {
AwaitingInput, AwaitingInput,
PreRun, PreRun,
@ -203,6 +207,7 @@ impl State {
} }
impl GameState for State { impl GameState for State {
/// The big, nasty, state machine handler
fn tick(&mut self, ctx: &mut Rltk) { fn tick(&mut self, ctx: &mut Rltk) {
let mut newrunstate; let mut newrunstate;
{ {