diff options
author | Guillaume Pasquet <dev@etenil.net> | 2019-11-24 11:31:55 +0000 |
---|---|---|
committer | Guillaume Pasquet <dev@etenil.net> | 2019-11-24 11:31:55 +0000 |
commit | 4032fc8200980025b015c5f798de87a5918b798b (patch) | |
tree | 692544c2ca52009be18fd79e95a1e33646288c68 | |
parent | f14e0005f082b5d4cbca0f2e0d501ad15da3c856 (diff) |
Line of sight work
-rw-r--r-- | src/state.rs | 2 | ||||
-rw-r--r-- | src/tiling.rs | 65 |
2 files changed, 60 insertions, 7 deletions
diff --git a/src/state.rs b/src/state.rs index be32228..8561486 100644 --- a/src/state.rs +++ b/src/state.rs @@ -6,7 +6,7 @@ use crate::entities::{Character, Entity, Player}; use crate::tiling::{tile_to_str, Tile, TileGrid, TileType}; use crate::world::{apply_movement, Dungeon, Generatable, Level, Movement}; -const PLAYER_SIGHT: usize = 5; +const PLAYER_SIGHT: usize = 3; pub struct State { pub player: Character, diff --git a/src/tiling.rs b/src/tiling.rs index 9319a94..f2a9b02 100644 --- a/src/tiling.rs +++ b/src/tiling.rs @@ -103,13 +103,66 @@ impl TileGrid { self.ysize } - pub fn clear_fog_of_war(&mut self, center: &(usize, usize), radius: usize) { - let startx: usize = 0.max(center.0 as isize - radius as isize) as usize; - let starty: usize = 0.max(center.1 as isize - radius as isize) as usize; - for x in startx..self.xsize.min(center.0 + radius) { - for y in starty..self.ysize.min(center.1 + radius) { - self.grid[y][x].visibility(true) + /// Clears all blocks in a single line of sight ray; stop when encountering a wall + /// This uses the bresenham algorithm, see: + /// https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm + fn clear_los(&mut self, start: &(usize, usize), end: &(usize, usize)) { + let dx = (end.0 as isize - start.0 as isize).abs(); + let sx: isize = if start.0 < end.0 { 1 } else { -1 }; + let dy = -(end.1 as isize - start.1 as isize).abs(); + let sy: isize = if start.1 < end.1 { 1 } else { -1 }; + let mut err = dx + dy; + + let mut x = start.0; + let mut y = start.1; + + loop { + if x == end.0 && y == end.1 { + break; + } + if let TileType::Wall = self.grid[y][x].get_type() { + break; + } + + let err2 = 2 * err; + if err2 >= dy { + err += dy; + x = (x as isize + sx).max(0) as usize; + } + if err2 <= dx { + err += dx; + y = (y as isize + sy).max(0) as usize; } + self.grid[y][x].visibility(true); + } + } + + /// Walk around the perimeter of the line of sight and ray-trace to clear tiles + /// up to the nearest obstacle. + pub fn clear_fog_of_war(&mut self, center: &(usize, usize), radius: usize) { + let a = ( + center.0.saturating_sub(radius), + center.1.saturating_sub(radius), + ); + let b = (center.0 + radius, center.1.saturating_sub(radius)); + let c = (center.0 + radius, center.1 + radius); + let d = (center.0.saturating_sub(radius), center.1 + radius); + + // From a to b + for x in a.0..b.0 { + self.clear_los(center, &(x, a.1)); + } + // From b to c + for y in b.1..c.1 { + self.clear_los(center, &(b.0, y)); + } + // From c to d + for x in d.0..c.0 { + self.clear_los(center, &(x, c.1)); + } + // From d to a + for y in a.1..d.1 { + self.clear_los(center, &(d.0, y)); } } } |