From af9db4789b24035361ce059e813d3ad4d17b25ac Mon Sep 17 00:00:00 2001 From: Ben Morrison Date: Wed, 18 Sep 2019 14:43:14 -0400 Subject: [PATCH] visible area limited --- src/component.rs | 8 ++++ src/main.rs | 13 +++++- src/map.rs | 106 ++++++++++++++++++++++++++++++++-------------- src/state.rs | 5 +-- src/visibility.rs | 27 ++++++++++++ 5 files changed, 123 insertions(+), 36 deletions(-) create mode 100644 src/component.rs create mode 100644 src/visibility.rs diff --git a/src/component.rs b/src/component.rs new file mode 100644 index 0000000..8ba082d --- /dev/null +++ b/src/component.rs @@ -0,0 +1,8 @@ +use rltk; +use specs::prelude::*; + +#[derive(Component)] +pub struct Viewshed { + pub visible_tiles: Vec, + pub range: i32, +} diff --git a/src/main.rs b/src/main.rs index 89739c6..aa9da43 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,26 +4,33 @@ extern crate specs_derive; use rltk::{Rltk, RGB}; use specs::prelude::*; +mod component; mod entity; mod map; mod player; mod rect; mod state; +mod visibility; +use crate::component::Viewshed; use crate::entity::{Player, Position, Renderable}; use crate::map::Map; use crate::state::State; +use crate::visibility::VisibilitySystem; fn main() { let context = Rltk::init_simple8x8(80, 50, "MORTAL WOMBAT", "resources"); let mut gs = State { ecs: World::new(), - systems: DispatcherBuilder::new().build(), + systems: DispatcherBuilder::new() + .with(VisibilitySystem {}, "visibility_system", &[]) + .build(), }; gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); + gs.ecs.register::(); let map = Map::new_room_corridors(); gs.ecs.insert(map.clone()); @@ -39,6 +46,10 @@ fn main() { bg: RGB::named(rltk::BLACK), }) .with(Player {}) + .with(Viewshed { + visible_tiles: vec![], + range: 8, + }) .build(); rltk::main_loop(context, gs); diff --git a/src/map.rs b/src/map.rs index 5bd0bfa..9e56083 100644 --- a/src/map.rs +++ b/src/map.rs @@ -1,8 +1,13 @@ use std::cmp::{max, min}; -use rltk::{Console, RandomNumberGenerator, Rltk, RGB}; +use rltk::{ + Algorithm2D, BaseMap, Console, Point, RandomNumberGenerator, Rltk, + RGB, +}; +use specs::prelude::*; -use crate::entity::TileType; +use crate::component::Viewshed; +use crate::entity::{Player, TileType}; use crate::rect::Rect; #[derive(Clone)] @@ -13,6 +18,32 @@ pub struct Map { pub height: i32, } +impl Algorithm2D for Map { + fn point2d_to_index(&self, pt: Point) -> i32 { + (pt.y * self.width) + pt.x + } + fn index_to_point2d(&self, idx: i32) -> Point { + Point { + x: idx % self.width, + y: idx / self.width, + } + } +} + +impl BaseMap for Map { + fn is_opaque(&self, idx: i32) -> bool { + self.tiles[idx as usize] == TileType::Wall + } + fn get_available_exits(&self, _idx: i32) -> Vec<(i32, f32)> { + Vec::new() + } + fn get_pathing_distance(&self, idx1: i32, idx2: i32) -> f32 { + let p1 = Point::new(idx1 % self.width, idx1 / self.width); + let p2 = Point::new(idx2 % self.width, idx2 / self.width); + rltk::DistanceAlg::Pythagoras.distance2d(p1, p2) + } +} + impl Map { pub fn xy_idx(&self, x: i32, y: i32) -> usize { (y as usize * 80) + x as usize @@ -131,35 +162,46 @@ pub fn new_test() -> Vec { } */ -pub fn draw(map: &[TileType], ctx: &mut Rltk) { - let mut y = 0; - let mut x = 0; - map.iter().for_each(|tile| { - match tile { - TileType::Floor => { - ctx.set( - x, - y, - RGB::from_f32(0.5, 0.5, 0.5), - RGB::from_f32(0., 0., 0.), - rltk::to_cp437('.'), - ); - } - TileType::Wall => { - ctx.set( - x, - y, - RGB::from_f32(0.0, 1.0, 0.0), - RGB::from_f32(0., 0., 0.), - rltk::to_cp437('#'), - ); - } - } +pub fn draw(ecs: &World, ctx: &mut Rltk) { + let mut viewsheds = ecs.write_storage::(); + let mut players = ecs.write_storage::(); + let map = ecs.fetch::(); - x += 1; - if x > 79 { - x = 0; - y += 1; - } - }); + (&mut players, &mut viewsheds).join().into_iter().for_each( + |(_player, viewshed)| { + let mut y = 0; + let mut x = 0; + map.tiles.iter().for_each(|tile| { + let pt = Point::new(x, y); + if viewshed.visible_tiles.contains(&pt) { + match tile { + TileType::Floor => { + ctx.set( + x, + y, + RGB::from_f32(0.5, 0.5, 0.5), + RGB::from_f32(0., 0., 0.), + rltk::to_cp437('.'), + ); + } + TileType::Wall => { + ctx.set( + x, + y, + RGB::from_f32(0.0, 1.0, 0.0), + RGB::from_f32(0., 0., 0.), + rltk::to_cp437('#'), + ); + } + } + } + + x += 1; + if x > 79 { + x = 0; + y += 1; + } + }); + }, + ); } diff --git a/src/state.rs b/src/state.rs index 9890927..583dc66 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,7 +1,7 @@ use rltk::{Console, GameState, Rltk}; use specs::prelude::*; -use crate::entity::{Position, Renderable, TileType}; +use crate::entity::{Position, Renderable}; use crate::map; use crate::player; @@ -17,8 +17,7 @@ impl GameState for State { player::input(self, ctx); self.systems.dispatch(&self.ecs); - let map = self.ecs.fetch::>(); - map::draw(&map, ctx); + map::draw(&self.ecs, ctx); let positions = self.ecs.read_storage::(); let renderables = self.ecs.read_storage::(); diff --git a/src/visibility.rs b/src/visibility.rs new file mode 100644 index 0000000..19b053d --- /dev/null +++ b/src/visibility.rs @@ -0,0 +1,27 @@ +use super::{Map, Position, Viewshed}; +use rltk::{field_of_view, Point}; +use specs::prelude::*; + +pub struct VisibilitySystem {} + +impl<'a> System<'a> for VisibilitySystem { + type SystemData = ( + ReadExpect<'a, Map>, + WriteStorage<'a, Viewshed>, + WriteStorage<'a, Position>, + ); + + fn run(&mut self, data: Self::SystemData) { + let (map, mut viewshed, pos) = data; + (&mut viewshed, &pos).join().into_iter().for_each( + |(viewshed, pos)| { + viewshed.visible_tiles.clear(); + viewshed.visible_tiles = field_of_view( + Point::new(pos.x, pos.y), + viewshed.range, + &*map, + ); + }, + ); + } +}