This an entry for a seven-hour roguelike competition. (Some work has been done to it since the end of the competition.)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

238 lines
6.6 KiB

pub mod render;
pub mod gen;
pub mod entity;
use rogue_util::{coord::*, grid::{Grid, region::{Region, RegionConfig}, path::Traversable}};
use std::cell::RefCell;
use std::io::{self, Write};
use std::ops::{Deref, DerefMut};
use std::fmt::Debug;
use entity::Entity;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Content {
Air,
Stone,
}
impl Default for Content {
fn default() -> Content {
Content::Stone
}
}
#[derive(Debug, Clone)]
pub struct Tile {
pub content: Content,
}
impl Default for Tile {
fn default() -> Tile {
Tile {
content: Default::default(),
}
}
}
impl Traversable for Tile {
fn can_pass(&self) -> bool {
self.content == Content::Air
}
}
pub struct World {
seed: u64,
cur_region: usize,
regions: Vec<RefCell<Region<Tile>>>,
entities: Vec<RefCell<Box<dyn Entity>>>, // NB: Player is ent 0
}
impl World {
pub fn new<R>(seed: u64, r: R) -> World
where
R: Iterator<Item=io::Result<termion::event::Key>> + 'static
{
let start_region = RegionConfig::default()
.with_grid_gen(Some(Box::new(move |_, r, o, d| {
let mut g = Grid::from_default(o, d).expect("Failed to generate Grid");
gen::carve(seed, &mut g, r);
g
})))
.build().expect("Failed to make World");
// TODO: Worldgen on start_region
World {
seed,
cur_region: 0,
regions: vec![RefCell::new(start_region)],
entities: vec![
RefCell::new(Box::new(entity::player::Player::new(V2i(0, 0), r)))
],
}
}
pub fn region<'s>(&'s self) -> impl Deref<Target=Region<Tile>> + 's {
self.regions[self.cur_region].borrow()
}
pub fn region_mut<'s>(&'s self) -> impl DerefMut<Target=Region<Tile>> + 's {
self.regions[self.cur_region].borrow_mut()
}
pub fn player<'s>(&'s self) -> impl Deref<Target=Box<dyn Entity>> + 's {
self.entities[0].borrow()
}
pub fn player_mut<'s>(&'s mut self) -> impl DerefMut<Target=Box<dyn Entity>> + 's {
self.entities[0].borrow_mut()
}
pub fn entities(&self) -> impl Iterator<Item=&RefCell<Box<dyn Entity>>> {
self.entities.iter()
}
pub fn entity<'s>(&'s self, idx: usize) -> impl Deref<Target=Box<dyn Entity>> + 's{
self.entities[idx].borrow()
}
pub fn entity_mut<'s>(&'s self, idx: usize) -> impl DerefMut<Target=Box<dyn Entity>> + 's {
self.entities[idx].borrow_mut()
}
pub fn add_entity(&mut self, ent: Box<dyn Entity>) {
self.entities.push(RefCell::new(ent));
}
pub fn render<W: Write>(&self, rs: &mut render::RenderState, mut out: &mut W) -> io::Result<()> {
let ppos = self.player().pos();
let center = rs.center();
if (ppos - center).l2_sq() > 225 {
rs.recenter(ppos);
}
rs.render(&self.region(), &mut render::EdgeRenderer, &mut out)?;
let sr = rs.screen_rect();
for eref in self.entities() {
let e = eref.borrow();
let pos = e.pos();
if sr.contains(pos) {
rs.go_to_pt(pos, out)?;
write!(out, "{}", e.char())?;
}
}
Ok(())
}
pub fn step(&mut self) -> Option<()> {
let mut ents: Vec<_> = self.entities().enumerate().map(|(i, eref)| {
(i, eref.borrow().next_time())
}).collect();
ents.sort_by_key(|&(_, t)| t);
if let Some((i, t)) = ents.pop() {
let (pos, sr) = {
let mut e = self.entity_mut(i);
let pos = e.pos();
let sr = e.sight();
let dt = e.act_time();
e.set_next_time(t + dt);
(pos, sr)
};
let obs = entity::Observation::new(self as &World, pos, sr);
{
let mut e = self.entity_mut(i);
let act = e.act(&obs);
self.perform_act(e, act)
}
} else { Some(()) }
}
pub fn perform_act<E: DerefMut<Target=Box<dyn Entity>>>(&self, mut e: E, act: entity::Action) -> Option<()> {
use entity::{Action::*, LinfDir};
match act {
Wait => (),
Move(d) => {
let p = e.pos();
let dp = d.to_v2i();
{
let mut rg = self.region_mut();
let tile = rg.get_or_create(p + dp);
if !tile.can_pass() {
return None;
}
}
e.set_pos(p + dp);
},
Quit => {
return Some(());
panic!("Thank you for playing Wing Commander");
},
}
None
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn room_align() -> io::Result<()> {
let mut w = World::new(0, Vec::new().into_iter());
{
let mut r = w.region_mut();
let gs = r.grid_size();
for v in R2i::origin_dim(V2i(-10, -2), V2i(20, 5)).iter()
{
r.get_or_create(v * gs);
}
//r.get_or_create(V2i(0, 0));
//r.get_or_create(V2i(0, gs.1));
//r.get_or_create(V2i(gs.0, 0));
//r.get_or_create(gs);
//r.get_or_create(V2i(0, 0) - gs);
//r.get_or_create(V2i(0, -gs.1));
//r.get_or_create(V2i(-gs.0, 0));
}
let (width, height) = termion::terminal_size()?;
for ii in 0..height { println!(""); }
let rs = render::RenderState::new(V2i(width as isize, height as isize));
let mut out = std::io::stdout();
let r = w.region();
rs.render(&r, &mut render::InvertRenderer, &mut out)?;
rs.draw_unpop(&r, &mut out)?;
rs.draw_grid(&r, &mut out)
}
#[test]
fn world_with_entity() -> io::Result<()> {
let mut w = World::new(0, Vec::new().into_iter());
{
let mut r = w.region_mut();
r.get_or_create(V2i(0, 0));
}
let (width, height) = termion::terminal_size()?;
let mut rs = render::RenderState::new(V2i(width as isize, height as isize));
let mut out = std::io::stdout();
#[derive(Debug)]
struct Ent;
impl Entity for Ent {
fn pos(&self) -> V2i { V2i(0, 0) }
fn set_pos(&mut self, v: V2i) {}
fn next_time(&self) -> usize { 0 }
fn set_next_time(&mut self, t: usize) {}
}
w.add_entity(Box::new(Ent));
w.render(&mut rs, &mut out)
}
}