|
|
|
@ -4,13 +4,17 @@
|
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
rltk::add_wasm_support!(); |
|
|
|
|
mod crypto; |
|
|
|
|
mod map; |
|
|
|
|
use crate::map::Chunk; |
|
|
|
|
use map::TileType; |
|
|
|
|
use rltk::prelude::*; |
|
|
|
|
use std::collections::HashMap; |
|
|
|
|
|
|
|
|
|
// Just like example 3, but we're adding an additional vector: visible
|
|
|
|
|
struct State { |
|
|
|
|
map: Vec<map::TileType>, |
|
|
|
|
view: Chunk, |
|
|
|
|
loaded_chunks: HashMap<(i32, i32), Chunk>, |
|
|
|
|
world_pos: (i32, i32), |
|
|
|
|
player_position: usize, |
|
|
|
|
visible: Vec<bool>, |
|
|
|
@ -18,11 +22,31 @@ struct State {
|
|
|
|
|
|
|
|
|
|
impl State { |
|
|
|
|
pub fn new() -> State { |
|
|
|
|
let seed = RandomNumberGenerator::new().rand::<u64>(); |
|
|
|
|
// Same as example 3, but we've added the visible tiles
|
|
|
|
|
let mut chunks: HashMap<(i32, i32), Chunk> = HashMap::new(); |
|
|
|
|
|
|
|
|
|
// Coordinates of the current and surrounding chunks
|
|
|
|
|
let keys: Vec<(i32, i32)> = [ |
|
|
|
|
(-1, 1), |
|
|
|
|
(0, 1), |
|
|
|
|
(1, 1), |
|
|
|
|
(-1, 0), |
|
|
|
|
(0, 0), |
|
|
|
|
(1, 0), |
|
|
|
|
(-1, -1), |
|
|
|
|
(0, -1), |
|
|
|
|
(1, -1), |
|
|
|
|
] |
|
|
|
|
.into(); |
|
|
|
|
for key in keys { |
|
|
|
|
let seed = RandomNumberGenerator::new().rand::<u64>(); |
|
|
|
|
let chunk = map::new_chunk(seed); |
|
|
|
|
chunks.insert(key, chunk); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let state = State { |
|
|
|
|
world_pos: (0, 0), |
|
|
|
|
map: map::new_chunk(seed), |
|
|
|
|
view: chunks.get(&(0, 0)).unwrap().to_vec(), |
|
|
|
|
loaded_chunks: chunks, |
|
|
|
|
player_position: (25 * 80) + 40, // Equivalent to point2d_to_index(40, 25) but we haven't initialized it yet
|
|
|
|
|
visible: vec![false; 80 * 50], |
|
|
|
|
}; |
|
|
|
@ -30,12 +54,46 @@ impl State {
|
|
|
|
|
state |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn move_to_chunk(&mut self, delta: (i32, i32)) { |
|
|
|
|
// Move world position by delta
|
|
|
|
|
self.world_pos = (self.world_pos.0 + delta.0, self.world_pos.1 + delta.1); |
|
|
|
|
|
|
|
|
|
// Update loaded chunks
|
|
|
|
|
match self.loaded_chunks.get(&self.world_pos) { |
|
|
|
|
Some(chunk) => self.view = chunk.to_vec(), |
|
|
|
|
None => { |
|
|
|
|
self.loaded_chunks.insert( |
|
|
|
|
self.world_pos, |
|
|
|
|
map::new_chunk(crypto::hash_to_number(&self.world_pos)), |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn move_player(&mut self, delta: Point) { |
|
|
|
|
let current_position = self.index_to_point2d(self.player_position); |
|
|
|
|
let new_position = current_position + delta; |
|
|
|
|
let new_idx = self.point2d_to_index(new_position); |
|
|
|
|
if self.map[new_idx] == TileType::Floor { |
|
|
|
|
self.player_position = new_idx; |
|
|
|
|
|
|
|
|
|
// Check if the user has left the chunk to either side.
|
|
|
|
|
// If that's the case, the current chunk is updated
|
|
|
|
|
// and the player position is updated
|
|
|
|
|
if new_position.x >= 80 { |
|
|
|
|
self.move_to_chunk((1, 0)); |
|
|
|
|
self.move_player(Point::new(-79, 0)); |
|
|
|
|
} else if new_position.x < 0 { |
|
|
|
|
self.move_to_chunk((-1, 0)); |
|
|
|
|
self.move_player(Point::new(79, 0)); |
|
|
|
|
} else if new_position.y >= 50 { |
|
|
|
|
self.move_to_chunk((0, 1)); |
|
|
|
|
self.move_player(Point::new(0, -49)); |
|
|
|
|
} else if new_position.y < 0 { |
|
|
|
|
self.move_to_chunk((0, -1)); |
|
|
|
|
self.move_player(Point::new(0, 49)); |
|
|
|
|
} else { |
|
|
|
|
let new_idx = self.point2d_to_index(new_position); |
|
|
|
|
if self.view[new_idx] == TileType::Floor { |
|
|
|
|
self.player_position = new_idx; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -93,7 +151,7 @@ impl GameState for State {
|
|
|
|
|
// Iterate the map array, incrementing coordinates as we go.
|
|
|
|
|
let mut y = 0; |
|
|
|
|
let mut x = 0; |
|
|
|
|
for (i, tile) in self.map.iter().enumerate() { |
|
|
|
|
for (i, tile) in self.view.iter().enumerate() { |
|
|
|
|
// Render a tile depending upon the tile type; now we check visibility as well!
|
|
|
|
|
let mut fg; |
|
|
|
|
let mut glyph = "."; |
|
|
|
@ -138,7 +196,7 @@ impl GameState for State {
|
|
|
|
|
impl BaseMap for State { |
|
|
|
|
// We'll use this one - if its a wall, we can't see through it
|
|
|
|
|
fn is_opaque(&self, idx: usize) -> bool { |
|
|
|
|
self.map[idx as usize] == TileType::Wall |
|
|
|
|
self.view[idx as usize] == TileType::Wall |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|