diff --git a/src/components.rs b/src/components.rs index 77d5560..eb195e1 100644 --- a/src/components.rs +++ b/src/components.rs @@ -63,6 +63,14 @@ pub struct Name { #[derive(Component, Debug, Serialize, Deserialize, Clone)] pub struct BlocksTile {} +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct BlocksVisibility {} + +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct Door { + pub open: bool, +} + #[derive(Serialize, Deserialize, Copy, Clone, PartialEq)] pub enum HungerState { Satiated, diff --git a/src/main.rs b/src/main.rs index 2fa47b1..96afca2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -132,7 +132,6 @@ impl State { let mut particle_system = particle_system::ParticleSpawnSystem {}; vis.run_now(&self.ecs); - hunger_clock.run_now(&self.ecs); mob.run_now(&self.ecs); mapindex.run_now(&self.ecs); trigger_system.run_now(&self.ecs); @@ -142,6 +141,7 @@ impl State { item_remove_system.run_now(&self.ecs); melee_system.run_now(&self.ecs); damage_system.run_now(&self.ecs); + hunger_clock.run_now(&self.ecs); particle_system.run_now(&self.ecs); self.ecs.maintain(); @@ -515,6 +515,8 @@ fn main() -> rltk::BError { gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); diff --git a/src/map.rs b/src/map.rs index dffa42d..213ac28 100644 --- a/src/map.rs +++ b/src/map.rs @@ -24,12 +24,14 @@ pub struct Map { pub visible_tiles: Vec, pub lit_tiles: Vec, pub telepath_tiles: Vec, + // Combine these offsets into one Vec<(u8, u8, u8)> pub red_offset: Vec, pub green_offset: Vec, pub blue_offset: Vec, pub blocked: Vec, pub depth: i32, pub bloodstains: HashSet, + pub view_blocked: HashSet, #[serde(skip_serializing)] #[serde(skip_deserializing)] @@ -56,6 +58,7 @@ impl Map { blocked: vec![false; MAPCOUNT], depth: new_depth, bloodstains: HashSet::new(), + view_blocked: HashSet::new(), tile_content: vec![Vec::new(); MAPCOUNT], }; @@ -108,7 +111,8 @@ impl Algorithm2D for Map { impl BaseMap for Map { fn is_opaque(&self, idx: usize) -> bool { - self.tiles[idx as usize] == TileType::Wall + let idx_u = idx as usize; + return self.tiles[idx_u] == TileType::Wall || self.view_blocked.contains(&idx_u); } fn get_pathing_distance(&self, idx1: usize, idx2: usize) -> f32 { diff --git a/src/map_builders/bsp_dungeon.rs b/src/map_builders/bsp_dungeon.rs index e073624..8746400 100644 --- a/src/map_builders/bsp_dungeon.rs +++ b/src/map_builders/bsp_dungeon.rs @@ -1,4 +1,4 @@ -use super::{BuilderMap, InitialMapBuilder, Map, Rect, TileType}; +use super::{BuilderMap, InitialMapBuilder, Rect, TileType}; use rltk::RandomNumberGenerator; pub struct BspDungeonBuilder { diff --git a/src/map_builders/common.rs b/src/map_builders/common.rs index 85698ee..dd945c8 100644 --- a/src/map_builders/common.rs +++ b/src/map_builders/common.rs @@ -10,25 +10,32 @@ pub fn apply_room_to_map(map: &mut Map, room: &Rect) { } } -pub fn apply_horizontal_tunnel(map: &mut Map, x1: i32, x2: i32, y: i32) { +pub fn apply_horizontal_tunnel(map: &mut Map, x1: i32, x2: i32, y: i32) -> Vec { + let mut corridor = Vec::new(); for x in min(x1, x2)..=max(x1, x2) { let idx = map.xy_idx(x, y); if idx > 0 && idx < (map.width as usize) * (map.height as usize) { map.tiles[idx as usize] = TileType::Floor; + corridor.push(idx as usize); } } + return corridor; } -pub fn apply_vertical_tunnel(map: &mut Map, y1: i32, y2: i32, x: i32) { +pub fn apply_vertical_tunnel(map: &mut Map, y1: i32, y2: i32, x: i32) -> Vec { + let mut corridor = Vec::new(); for y in min(y1, y2)..=max(y1, y2) { let idx = map.xy_idx(x, y); if idx > 0 && idx < (map.width as usize) * (map.height as usize) { map.tiles[idx as usize] = TileType::Floor; + corridor.push(idx as usize); } } + return corridor; } -pub fn draw_corridor(map: &mut Map, x1: i32, y1: i32, x2: i32, y2: i32) { +pub fn draw_corridor(map: &mut Map, x1: i32, y1: i32, x2: i32, y2: i32) -> Vec { + let mut corridor = Vec::new(); let mut x = x1; let mut y = y1; @@ -44,8 +51,12 @@ pub fn draw_corridor(map: &mut Map, x1: i32, y1: i32, x2: i32, y2: i32) { } let idx = map.xy_idx(x, y); - map.tiles[idx] = TileType::Floor; + if map.tiles[idx] != TileType::Floor { + map.tiles[idx] = TileType::Floor; + corridor.push(idx); + } } + return corridor; } #[allow(dead_code)] diff --git a/src/map_builders/door_placement.rs b/src/map_builders/door_placement.rs new file mode 100644 index 0000000..6f32dcd --- /dev/null +++ b/src/map_builders/door_placement.rs @@ -0,0 +1,75 @@ +use super::{BuilderMap, MetaMapBuilder, TileType}; +use rltk::RandomNumberGenerator; + +pub struct DoorPlacement {} + +impl MetaMapBuilder for DoorPlacement { + #[allow(dead_code)] + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { + self.doors(rng, build_data); + } +} + +impl DoorPlacement { + #[allow(dead_code)] + pub fn new() -> Box { + Box::new(DoorPlacement {}) + } + + fn doors(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + if let Some(halls_original) = &build_data.corridors { + let halls = halls_original.clone(); // Avoids nested borrow + for hall in halls.iter() { + if hall.len() > 2 { + if self.door_possible(build_data, hall[0]) { + build_data.spawn_list.push((hall[0], "door".to_string())); + } + } + } + } else { + // There are no corridors - scan for possible places + let tiles = build_data.map.tiles.clone(); + for (i, tile) in tiles.iter().enumerate() { + if *tile == TileType::Floor && self.door_possible(build_data, i) && rng.roll_dice(1, 3) == 1 { + build_data.spawn_list.push((i, "door".to_string())); + } + } + } + } + + fn door_possible(&self, build_data: &mut BuilderMap, idx: usize) -> bool { + // Iterate through spawn list. If another entity wants to spawn on this tile, return false + for spawn in build_data.spawn_list.iter() { + if spawn.0 == idx { + return false; + } + } + + let x = idx % build_data.map.width as usize; + let y = idx / build_data.map.width as usize; + + // Check for east-west door possibility + if build_data.map.tiles[idx] == TileType::Floor + && (x > 1 && build_data.map.tiles[idx - 1] == TileType::Floor) + && (x < build_data.map.width as usize - 2 && build_data.map.tiles[idx + 1] == TileType::Floor) + && (y > 1 && build_data.map.tiles[idx - build_data.map.width as usize] == TileType::Wall) + && (y < build_data.map.height as usize - 2 + && build_data.map.tiles[idx + build_data.map.width as usize] == TileType::Wall) + { + return true; + } + + // Check for north-south door possibility + if build_data.map.tiles[idx] == TileType::Floor + && (x > 1 && build_data.map.tiles[idx - 1] == TileType::Wall) + && (x < build_data.map.width as usize - 2 && build_data.map.tiles[idx + 1] == TileType::Wall) + && (y > 1 && build_data.map.tiles[idx - build_data.map.width as usize] == TileType::Floor) + && (y < build_data.map.height as usize - 2 + && build_data.map.tiles[idx + build_data.map.width as usize] == TileType::Floor) + { + return true; + } + + false + } +} diff --git a/src/map_builders/mod.rs b/src/map_builders/mod.rs index 0d23e9f..eb9c76e 100644 --- a/src/map_builders/mod.rs +++ b/src/map_builders/mod.rs @@ -48,6 +48,14 @@ mod room_sorter; use room_sorter::{RoomSort, RoomSorter}; mod room_draw; use room_draw::RoomDrawer; +mod rooms_corridors_nearest; +use rooms_corridors_nearest::NearestCorridors; +mod rooms_corridors_bresenham; +use rooms_corridors_bresenham::BresenhamCorridors; +mod rooms_corridors_spawner; +use rooms_corridors_spawner::CorridorSpawner; +mod door_placement; +use door_placement::DoorPlacement; // Shared data to be passed around build chain pub struct BuilderMap { @@ -55,6 +63,7 @@ pub struct BuilderMap { pub map: Map, pub starting_position: Option, pub rooms: Option>, + pub corridors: Option>>, pub history: Vec, } @@ -86,6 +95,7 @@ impl BuilderChain { map: Map::new(new_depth), starting_position: None, rooms: None, + corridors: None, history: Vec::new(), }, } @@ -182,12 +192,19 @@ fn random_room_builder(rng: &mut rltk::RandomNumberGenerator, builder: &mut Buil _ => builder.with(BspCorridors::new()), } - let corridor_roll = rng.roll_dice(1, 2); + let corridor_roll = rng.roll_dice(1, 4); match corridor_roll { 1 => builder.with(DoglegCorridors::new()), + 2 => builder.with(NearestCorridors::new()), + 3 => builder.with(BresenhamCorridors::new()), _ => builder.with(BspCorridors::new()), } + let cspawn_roll = rng.roll_dice(1, 2); + if cspawn_roll == 1 { + builder.with(CorridorSpawner::new()); + } + let modifier_roll = rng.roll_dice(1, 6); match modifier_roll { 1 => builder.with(RoomExploder::new()), @@ -255,7 +272,7 @@ fn random_shape_builder(rng: &mut rltk::RandomNumberGenerator, builder: &mut Bui } pub fn random_builder(new_depth: i32, rng: &mut rltk::RandomNumberGenerator) -> BuilderChain { - let mut builder = BuilderChain::new(new_depth); + /*let mut builder = BuilderChain::new(new_depth); let type_roll = rng.roll_dice(1, 2); match type_roll { 1 => random_room_builder(rng, &mut builder), @@ -264,13 +281,30 @@ pub fn random_builder(new_depth: i32, rng: &mut rltk::RandomNumberGenerator) -> /*if rng.roll_dice(1, 3)==1 { builder.with(WaveformCollapseBuilder::new()); + + // Now set the start to a random starting area + let (start_x, start_y) = random_start_position(rng); + builder.with(AreaStartingPosition::new(start_x, start_y)); + + // Setup an exit and spawn mobs + builder.with(VoronoiSpawning::new()); + builder.with(DistantExit::new()); }*/ if rng.roll_dice(1, 20) == 1 { builder.with(PrefabBuilder::sectional(prefab_builder::prefab_sections::UNDERGROUND_FORT)); } + builder.with(DoorPlacement::new()); builder.with(PrefabBuilder::vaults()); + builder*/ + + let mut builder = BuilderChain::new(new_depth); + builder.start_with(BspInteriorBuilder::new()); + builder.with(DoorPlacement::new()); + builder.with(RoomBasedSpawner::new()); + builder.with(RoomBasedStairs::new()); + builder.with(RoomBasedStartingPosition::new()); builder } diff --git a/src/map_builders/room_draw.rs b/src/map_builders/room_draw.rs index 7f4c192..1dcac3b 100644 --- a/src/map_builders/room_draw.rs +++ b/src/map_builders/room_draw.rs @@ -47,7 +47,7 @@ impl RoomDrawer { if let Some(rooms_builder) = &build_data.rooms { rooms = rooms_builder.clone(); } else { - panic!("Room Drawing require a builder with room structures"); + panic!("RoomDrawer require a builder with rooms"); } for room in rooms.iter() { diff --git a/src/map_builders/rooms_corridors_bresenham.rs b/src/map_builders/rooms_corridors_bresenham.rs new file mode 100644 index 0000000..66c6290 --- /dev/null +++ b/src/map_builders/rooms_corridors_bresenham.rs @@ -0,0 +1,64 @@ +use super::{BuilderMap, MetaMapBuilder, Rect, TileType}; +use rltk::RandomNumberGenerator; +use std::collections::HashSet; + +pub struct BresenhamCorridors {} + +impl MetaMapBuilder for BresenhamCorridors { + #[allow(dead_code)] + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { + self.corridors(rng, build_data); + } +} + +impl BresenhamCorridors { + #[allow(dead_code)] + pub fn new() -> Box { + Box::new(BresenhamCorridors {}) + } + + fn corridors(&mut self, _rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + let rooms: Vec; + if let Some(rooms_builder) = &build_data.rooms { + rooms = rooms_builder.clone(); + } else { + panic!("BresenhamCorridors require a builder with room structures"); + } + + let mut connected: HashSet = HashSet::new(); + let mut corridors: Vec> = Vec::new(); + for (i, room) in rooms.iter().enumerate() { + let mut room_distance: Vec<(usize, f32)> = Vec::new(); + let room_centre = room.centre(); + let room_centre_pt = rltk::Point::new(room_centre.0, room_centre.1); + for (j, other_room) in rooms.iter().enumerate() { + if i != j && !connected.contains(&j) { + let other_centre = other_room.centre(); + let other_centre_pt = rltk::Point::new(other_centre.0, other_centre.1); + let distance = rltk::DistanceAlg::Pythagoras.distance2d(room_centre_pt, other_centre_pt); + room_distance.push((j, distance)); + } + } + + if !room_distance.is_empty() { + room_distance.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap()); + let dest_centre = rooms[room_distance[0].0].centre(); + let line = rltk::line2d( + rltk::LineAlg::Bresenham, + room_centre_pt, + rltk::Point::new(dest_centre.0, dest_centre.1), + ); + let mut corridor = Vec::new(); + for cell in line.iter() { + let idx = build_data.map.xy_idx(cell.x, cell.y); + build_data.map.tiles[idx] = TileType::Floor; + corridor.push(idx); + } + corridors.push(corridor); + connected.insert(i); + build_data.take_snapshot(); + } + } + build_data.corridors = Some(corridors); + } +} diff --git a/src/map_builders/rooms_corridors_bsp.rs b/src/map_builders/rooms_corridors_bsp.rs index 688ab9b..2cf2dee 100644 --- a/src/map_builders/rooms_corridors_bsp.rs +++ b/src/map_builders/rooms_corridors_bsp.rs @@ -24,6 +24,7 @@ impl BspCorridors { panic!("BSP Corridors require a builder with room structures"); } + let mut corridors: Vec> = Vec::new(); for i in 0..rooms.len() - 1 { let room = rooms[i]; let next_room = rooms[i + 1]; @@ -31,8 +32,10 @@ impl BspCorridors { let start_y = room.y1 + (rng.roll_dice(1, i32::abs(room.y1 - room.y2)) - 1); let end_x = next_room.x1 + (rng.roll_dice(1, i32::abs(next_room.x1 - next_room.x2)) - 1); let end_y = next_room.y1 + (rng.roll_dice(1, i32::abs(next_room.y1 - next_room.y2)) - 1); - draw_corridor(&mut build_data.map, start_x, start_y, end_x, end_y); + let corridor = draw_corridor(&mut build_data.map, start_x, start_y, end_x, end_y); + corridors.push(corridor); build_data.take_snapshot(); } + build_data.corridors = Some(corridors); } } diff --git a/src/map_builders/rooms_corridors_dogleg.rs b/src/map_builders/rooms_corridors_dogleg.rs index 2d5c721..854b92f 100644 --- a/src/map_builders/rooms_corridors_dogleg.rs +++ b/src/map_builders/rooms_corridors_dogleg.rs @@ -24,19 +24,25 @@ impl DoglegCorridors { panic!("DoglegCorridors require a builder with rooms."); } + let mut corridors: Vec> = Vec::new(); for (i, room) in rooms.iter().enumerate() { if i > 0 { let (new_x, new_y) = room.centre(); let (prev_x, prev_y) = rooms[i as usize - 1].centre(); if rng.range(0, 2) == 1 { - apply_horizontal_tunnel(&mut build_data.map, prev_x, new_x, prev_y); - apply_vertical_tunnel(&mut build_data.map, prev_y, new_y, new_x); + let mut c1 = apply_horizontal_tunnel(&mut build_data.map, prev_x, new_x, prev_y); + let mut c2 = apply_vertical_tunnel(&mut build_data.map, prev_y, new_y, new_x); + c1.append(&mut c2); + corridors.push(c1); } else { - apply_vertical_tunnel(&mut build_data.map, prev_y, new_y, prev_x); - apply_horizontal_tunnel(&mut build_data.map, prev_x, new_x, new_y); + let mut c1 = apply_vertical_tunnel(&mut build_data.map, prev_y, new_y, prev_x); + let mut c2 = apply_horizontal_tunnel(&mut build_data.map, prev_x, new_x, new_y); + c1.append(&mut c2); + corridors.push(c1); } build_data.take_snapshot(); } } + build_data.corridors = Some(corridors); } } diff --git a/src/map_builders/rooms_corridors_nearest.rs b/src/map_builders/rooms_corridors_nearest.rs new file mode 100644 index 0000000..7bad650 --- /dev/null +++ b/src/map_builders/rooms_corridors_nearest.rs @@ -0,0 +1,55 @@ +use super::{draw_corridor, BuilderMap, MetaMapBuilder, Rect}; +use rltk::RandomNumberGenerator; +use std::collections::HashSet; + +pub struct NearestCorridors {} + +impl MetaMapBuilder for NearestCorridors { + #[allow(dead_code)] + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { + self.corridors(rng, build_data); + } +} + +impl NearestCorridors { + #[allow(dead_code)] + pub fn new() -> Box { + return Box::new(NearestCorridors {}); + } + + fn corridors(&mut self, _rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + let rooms: Vec; + if let Some(rooms_builder) = &build_data.rooms { + rooms = rooms_builder.clone(); + } else { + panic!("NearestCorridors requires a builder with rooms"); + } + + let mut connected: HashSet = HashSet::new(); + let mut corridors: Vec> = Vec::new(); + for (i, room) in rooms.iter().enumerate() { + let mut room_distance: Vec<(usize, f32)> = Vec::new(); + let room_centre = room.centre(); + let room_centre_pt = rltk::Point::new(room_centre.0, room_centre.1); + for (j, other_room) in rooms.iter().enumerate() { + if i != j && !connected.contains(&j) { + let other_centre = other_room.centre(); + let other_centre_pt = rltk::Point::new(other_centre.0, other_centre.1); + let distance = rltk::DistanceAlg::Pythagoras.distance2d(room_centre_pt, other_centre_pt); + room_distance.push((j, distance)); + } + } + + if !room_distance.is_empty() { + room_distance.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap()); + let dest_centre = rooms[room_distance[0].0].centre(); + let corridor = + draw_corridor(&mut build_data.map, room_centre.0, room_centre.1, dest_centre.0, dest_centre.1); + connected.insert(i); + build_data.take_snapshot(); + corridors.push(corridor); + } + } + build_data.corridors = Some(corridors); + } +} diff --git a/src/map_builders/rooms_corridors_spawner.rs b/src/map_builders/rooms_corridors_spawner.rs new file mode 100644 index 0000000..db297c3 --- /dev/null +++ b/src/map_builders/rooms_corridors_spawner.rs @@ -0,0 +1,28 @@ +use super::{spawner, BuilderMap, MetaMapBuilder}; +use rltk::RandomNumberGenerator; + +pub struct CorridorSpawner {} + +impl MetaMapBuilder for CorridorSpawner { + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { + self.build(rng, build_data); + } +} + +impl CorridorSpawner { + #[allow(dead_code)] + pub fn new() -> Box { + return Box::new(CorridorSpawner {}); + } + + fn build(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + if let Some(corridors) = &build_data.corridors { + for corridor in corridors.iter() { + let depth = build_data.map.depth; + spawner::spawn_region(&build_data.map, rng, &corridor, depth, &mut build_data.spawn_list); + } + } else { + panic!("CorridorSpawner only works after corridors have been created"); + } + } +} diff --git a/src/particle_system.rs b/src/particle_system.rs index c9b7634..8fb7637 100644 --- a/src/particle_system.rs +++ b/src/particle_system.rs @@ -59,6 +59,10 @@ impl ParticleBuilder { self.request(x, y, rltk::RGB::named(rltk::ORANGE), rltk::RGB::named(rltk::BLACK), rltk::to_cp437('‼'), 200.0); } + pub fn trap_triggered(&mut self, x: i32, y: i32) { + self.request(x, y, rltk::RGB::named(rltk::RED), rltk::RGB::named(rltk::RED), rltk::to_cp437('‼'), 200.0); + } + // Makes a particle request in the shape of an 'x'. Sort of. pub fn request_star(&mut self, x: i32, y: i32, fg: RGB, bg: RGB, glyph: rltk::FontCharType, lifetime: f32) { self.request(x, y, fg, bg, glyph, lifetime * 2.0); diff --git a/src/player.rs b/src/player.rs index 0640754..eb8164a 100644 --- a/src/player.rs +++ b/src/player.rs @@ -1,6 +1,7 @@ use super::{ - gamelog, CombatStats, EntityMoved, Hidden, HungerClock, HungerState, Item, Map, Monster, Name, Player, Position, - RunState, State, Telepath, TileType, Viewshed, WantsToMelee, WantsToPickupItem, MAPHEIGHT, MAPWIDTH, + gamelog, BlocksTile, BlocksVisibility, CombatStats, Door, EntityMoved, Hidden, HungerClock, HungerState, Item, Map, + Monster, Name, Player, Position, Renderable, RunState, State, Telepath, TileType, Viewshed, WantsToMelee, + WantsToPickupItem, MAPHEIGHT, MAPWIDTH, }; use rltk::{Point, RandomNumberGenerator, Rltk, VirtualKeyCode}; use specs::prelude::*; @@ -17,6 +18,11 @@ pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> bool { let entities = ecs.entities(); let mut wants_to_melee = ecs.write_storage::(); + let mut doors = ecs.write_storage::(); + let mut blocks_visibility = ecs.write_storage::(); + let mut blocks_movement = ecs.write_storage::(); + let mut renderables = ecs.write_storage::(); + let names = ecs.read_storage::(); for (entity, _player, pos, viewshed) in (&entities, &mut players, &mut positions, &mut viewsheds).join() { if pos.x + delta_x < 1 @@ -34,14 +40,27 @@ pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> bool { wants_to_melee.insert(entity, WantsToMelee { target: *potential_target }).expect("Add target failed."); return true; } + let door = doors.get_mut(*potential_target); + if let Some(door) = door { + if door.open == false { + door.open = true; + blocks_visibility.remove(*potential_target); + blocks_movement.remove(*potential_target); + let render_data = renderables.get_mut(*potential_target).unwrap(); + if let Some(name) = names.get(entity) { + gamelog::Logger::new().append("You open the").item_name_n(&name.name).period().log(); + } + render_data.glyph = rltk::to_cp437('▓'); // Nethack open door, maybe just use '/' instead. + viewshed.dirty = true; + return true; + } + } } if map.blocked[destination_idx] { gamelog::Logger::new().append("You can't move there.").log(); return false; } - - let names = ecs.read_storage::(); let hidden = ecs.read_storage::(); // Push every entity name in the pile to a vector of strings let mut item_names: Vec = Vec::new(); diff --git a/src/saveload_system.rs b/src/saveload_system.rs index 29f5278..0bc56a9 100644 --- a/src/saveload_system.rs +++ b/src/saveload_system.rs @@ -50,12 +50,14 @@ pub fn save_game(ecs: &mut World) { AOE, Attributes, BlocksTile, + BlocksVisibility, CombatStats, Confusion, Consumable, Cursed, DefenceBonus, Destructible, + Door, EntityMoved, EntryTrigger, Equippable, @@ -143,12 +145,14 @@ pub fn load_game(ecs: &mut World) { AOE, Attributes, BlocksTile, + BlocksVisibility, CombatStats, Confusion, Consumable, Cursed, DefenceBonus, Destructible, + Door, EntityMoved, EntryTrigger, Equippable, diff --git a/src/spawner.rs b/src/spawner.rs index 7c90a27..438709e 100644 --- a/src/spawner.rs +++ b/src/spawner.rs @@ -1,9 +1,9 @@ use super::{ - random_table::RandomTable, Attribute, Attributes, BlocksTile, CombatStats, Confusion, Consumable, Cursed, - DefenceBonus, Destructible, EntryTrigger, EquipmentSlot, Equippable, Hidden, HungerClock, HungerState, - InflictsDamage, Item, MagicMapper, Map, MeleePowerBonus, Mind, Monster, Name, Player, Position, ProvidesHealing, - ProvidesNutrition, Ranged, Rect, Renderable, SerializeMe, SingleActivation, TileType, Viewshed, Wand, AOE, - MAPWIDTH, + random_table::RandomTable, Attribute, Attributes, BlocksTile, BlocksVisibility, CombatStats, Confusion, Consumable, + Cursed, DefenceBonus, Destructible, Door, EntryTrigger, EquipmentSlot, Equippable, Hidden, HungerClock, + HungerState, InflictsDamage, Item, MagicMapper, Map, MeleePowerBonus, Mind, Monster, Name, Player, Position, + ProvidesHealing, ProvidesNutrition, Ranged, Rect, Renderable, SerializeMe, SingleActivation, TileType, Viewshed, + Wand, AOE, MAPWIDTH, }; use rltk::{console, RandomNumberGenerator, RGB}; use specs::prelude::*; @@ -113,15 +113,6 @@ pub fn spawn_region( ) { let mut spawn_points: HashMap = HashMap::new(); let mut areas: Vec = Vec::from(area); - let category = category_table().roll(rng); - let spawn_table; - match category.as_ref() { - "mob" => spawn_table = mob_table(map_depth), - "item" => spawn_table = item_table(map_depth), - "food" => spawn_table = food_table(map_depth), - "trap" => spawn_table = trap_table(map_depth), - _ => spawn_table = debug_table(), - } let num_spawns = i32::min(areas.len() as i32, rng.roll_dice(1, MAX_ENTITIES + 2) - 2); if num_spawns <= 0 { @@ -129,6 +120,15 @@ pub fn spawn_region( } for _i in 0..num_spawns { + let category = category_table().roll(rng); + let spawn_table; + match category.as_ref() { + "mob" => spawn_table = mob_table(map_depth), + "item" => spawn_table = item_table(map_depth), + "food" => spawn_table = food_table(map_depth), + "trap" => spawn_table = trap_table(map_depth), + _ => spawn_table = debug_table(), + } let array_idx = if areas.len() == 1 { 0usize } else { (rng.roll_dice(1, areas.len() as i32) - 1) as usize }; let map_idx = areas[array_idx]; spawn_points.insert(map_idx, spawn_table.roll(rng)); @@ -174,6 +174,8 @@ pub fn spawn_entity(ecs: &mut World, spawn: &(&usize, &String)) { // Traps "bear trap" => bear_trap(ecs, x, y), "confusion trap" => confusion_trap(ecs, x, y), + // Other + "door" => door(ecs, x, y), _ => console::log(format!("Tried to spawn nothing ({}). Bugfix needed!", spawn.1)), } } @@ -228,6 +230,23 @@ fn trap_table(_map_depth: i32) -> RandomTable { return RandomTable::new().add("bear trap", 0).add("confusion trap", 1); } +fn door(ecs: &mut World, x: i32, y: i32) { + ecs.create_entity() + .with(Position { x, y }) + .with(Renderable { + glyph: rltk::to_cp437('+'), + fg: RGB::named(rltk::LIGHTYELLOW), + bg: RGB::named(rltk::BLACK), + render_order: 2, + }) + .with(Name { name: "door".to_string(), plural: "doors".to_string() }) + .with(BlocksTile {}) + .with(BlocksVisibility {}) + .with(Door { open: false }) + .marked::>() + .build(); +} + fn health_potion(ecs: &mut World, x: i32, y: i32) { ecs.create_entity() .with(Position { x, y }) diff --git a/src/trigger_system.rs b/src/trigger_system.rs index 6a5c267..5d0ee68 100644 --- a/src/trigger_system.rs +++ b/src/trigger_system.rs @@ -53,6 +53,7 @@ impl<'a> System<'a> for TriggerSystem { let name = names.get(*entity_id); hidden.remove(*entity_id); if let Some(name) = name { + particle_builder.trap_triggered(pos.x, pos.y); gamelog::Logger::new().item_name(&name.name).append("triggers!").log(); } diff --git a/src/visibility_system.rs b/src/visibility_system.rs index ace0826..f313240 100644 --- a/src/visibility_system.rs +++ b/src/visibility_system.rs @@ -1,4 +1,4 @@ -use super::{gamelog, Hidden, Map, Name, Player, Position, Telepath, Viewshed}; +use super::{gamelog, BlocksVisibility, Hidden, Map, Name, Player, Position, Telepath, Viewshed}; use rltk::{FieldOfViewAlg::SymmetricShadowcasting, Point}; use specs::prelude::*; @@ -15,10 +15,18 @@ impl<'a> System<'a> for VisibilitySystem { ReadStorage<'a, Player>, WriteStorage<'a, Hidden>, ReadStorage<'a, Name>, + ReadStorage<'a, BlocksVisibility>, ); fn run(&mut self, data: Self::SystemData) { - let (mut map, mut rng, entities, mut viewshed, mut telepath, pos, player, mut hidden, names) = data; + let (mut map, mut rng, entities, mut viewshed, mut telepath, pos, player, mut hidden, names, blocks_visibility) = + data; + + map.view_blocked.clear(); + for (block_pos, _block) in (&pos, &blocks_visibility).join() { + let idx = map.xy_idx(block_pos.x, block_pos.y); + map.view_blocked.insert(idx); + } for (ent, viewshed, pos) in (&entities, &mut viewshed, &pos).join() { if viewshed.dirty {