From b4fc2ba28f0bdfd2671b73de023802ed5ca2db31 Mon Sep 17 00:00:00 2001 From: Timothy Warren Date: Thu, 6 Jan 2022 09:34:17 -0500 Subject: [PATCH] Allow restoring of NPCs on level changes --- src/components.rs | 7 ++++ src/main.rs | 95 +++++++----------------------------------- src/map/dungeon.rs | 73 +++++++++++++++++++++++++++++--- src/saveload_system.rs | 2 + 4 files changed, 91 insertions(+), 86 deletions(-) diff --git a/src/components.rs b/src/components.rs index 5b6617f..bef2089 100644 --- a/src/components.rs +++ b/src/components.rs @@ -278,6 +278,13 @@ pub struct LootTable { pub table: String, } +#[derive(Component, Serialize, Deserialize, Clone)] +pub struct OtherLevelPosition { + pub x: i32, + pub y: i32, + pub depth: i32, +} + // Serialization helper code. We need to implement ConvertSaveLoad for each type that contains an // Entity. diff --git a/src/main.rs b/src/main.rs index e7415cd..d14ea06 100644 --- a/src/main.rs +++ b/src/main.rs @@ -343,11 +343,11 @@ impl GameState for State { } } RunState::NextLevel => { - self.goto_next_level(); + self.goto_level(1); newrunstate = RunState::PreRun; } RunState::PreviousLevel => { - self.goto_previous_level(); + self.goto_level(-1); self.mapgen_next_state = Some(RunState::PreRun); newrunstate = RunState::MapGeneration; } @@ -376,86 +376,16 @@ impl GameState for State { } impl State { - fn entities_to_remove_on_level_change(&mut self) -> Vec { - let entities = self.ecs.entities(); - let player = self.ecs.read_storage::(); - let backpack = self.ecs.read_storage::(); - let player_entity = self.ecs.fetch::(); - let equipped = self.ecs.read_storage::(); - - let mut to_delete: Vec = Vec::new(); - for entity in entities.join() { - let mut should_delete = true; - - // Don't delete the player - if let Some(_p) = player.get(entity) { - should_delete = false; - } - - // Don't delete the player's equipment - if let Some(bp) = backpack.get(entity) { - if bp.owner == *player_entity { - should_delete = false; - } - } - if let Some(eq) = equipped.get(entity) { - if eq.owner == *player_entity { - should_delete = false; - } - } - - if should_delete { - to_delete.push(entity); - } - } - - to_delete - } - - fn goto_next_level(&mut self) { - // Delete entities that aren't the palyer or their equipment - let to_delete = self.entities_to_remove_on_level_change(); - for target in to_delete { - self.ecs - .delete_entity(target) - .expect("failed to delete entity"); - } + fn goto_level(&mut self, offset: i32) { + freeze_level_entities(&mut self.ecs); // Build a new map and place the player - #[allow(unused_assignments)] - let mut current_depth = 1; - { - let worldmap_resource = self.ecs.fetch::(); - current_depth = worldmap_resource.depth; - } - self.generate_world_map(current_depth + 1); + let current_depth = self.ecs.fetch::().depth; + self.generate_world_map(current_depth + offset, offset); // Notify the player let mut gamelog = self.ecs.fetch_mut::(); - gamelog.append("You descend to the next level."); - } - - fn goto_previous_level(&mut self) { - // Delete entities that aren't the palyer or their equipment - let to_delete = self.entities_to_remove_on_level_change(); - for target in to_delete { - self.ecs - .delete_entity(target) - .expect("failed to delete entity"); - } - - // Build a new map and place the player - #[allow(unused_assignments)] - let current_depth; - { - let worldmap_resource = self.ecs.fetch::(); - current_depth = worldmap_resource.depth; - } - self.generate_world_map(current_depth - 1); - - // Notify the player - let mut gamelog = self.ecs.fetch_mut::(); - gamelog.append("You ascend to the previous level."); + gamelog.append("You change level."); } fn game_over_cleanup(&mut self) { @@ -481,16 +411,18 @@ impl State { self.ecs.insert(map::MasterDungeonMap::new()); // Build a new map and place the player - self.generate_world_map(1); + self.generate_world_map(1, 0); } - fn generate_world_map(&mut self, new_depth: i32) { + fn generate_world_map(&mut self, new_depth: i32, offset: i32) { self.mapgen_index = 0; self.mapgen_timer = 0.0; self.mapgen_history.clear(); - if let Some(history) = map::level_transition(&mut self.ecs, new_depth) { + if let Some(history) = map::level_transition(&mut self.ecs, new_depth, offset) { self.mapgen_history = history; + } else { + map::thaw_level_entities(&mut self.ecs); } } } @@ -530,6 +462,7 @@ fn main() -> ::rltk::BError { Monster, Name, NaturalAttackDefense, + OtherLevelPosition, ParticleLifetime, Player, Pools, @@ -571,7 +504,7 @@ fn main() -> ::rltk::BError { gs.ecs.insert(particle_system::ParticleBuilder::new()); gs.ecs.insert(rex_assets::RexAssets::new()); - gs.generate_world_map(1); + gs.generate_world_map(1, 0); rltk::main_loop(context, gs) } diff --git a/src/map/dungeon.rs b/src/map/dungeon.rs index d3ed45b..00d4ab9 100644 --- a/src/map/dungeon.rs +++ b/src/map/dungeon.rs @@ -4,7 +4,7 @@ use ::rltk::{Point, RandomNumberGenerator}; use ::serde::{Deserialize, Serialize}; use ::specs::prelude::*; -use crate::components::{Position, Viewshed}; +use crate::components::{OtherLevelPosition, Position, Viewshed}; use crate::map::{Map, TileType}; use crate::map_builders::level_builder; @@ -36,14 +36,14 @@ impl MasterDungeonMap { } } -pub fn level_transition(ecs: &mut World, new_depth: i32) -> Option> { +pub fn level_transition(ecs: &mut World, new_depth: i32, offset: i32) -> Option> { // Obtain the master dungeon map let dungeon_master = ecs.read_resource::(); // Do we already have a map? if dungeon_master.get_map(new_depth).is_some() { std::mem::drop(dungeon_master); - transition_to_existing_map(ecs, new_depth); + transition_to_existing_map(ecs, new_depth, offset); None } else { @@ -102,7 +102,7 @@ fn transition_to_new_map(ecs: &mut World, new_depth: i32) -> Vec { mapgen_history } -fn transition_to_existing_map(ecs: &mut World, new_depth: i32) { +fn transition_to_existing_map(ecs: &mut World, new_depth: i32, offset: i32) { let dungeon_master = ecs.write_resource::(); let map = dungeon_master.get_map(new_depth).unwrap(); let mut worldmap_resource = ecs.write_resource::(); @@ -110,8 +110,13 @@ fn transition_to_existing_map(ecs: &mut World, new_depth: i32) { // Find the down stairs and place the player let w = map.width; + let stair_type = if offset < 0 { + TileType::DownStairs + } else { + TileType::UpStairs + }; for (idx, tt) in map.tiles.iter().enumerate() { - if *tt == TileType::DownStairs { + if *tt == stair_type { let mut player_position = ecs.write_resource::(); *player_position = Point::new(idx as i32 % w, idx as i32 / w); let mut position_components = ecs.write_storage::(); @@ -130,3 +135,61 @@ fn transition_to_existing_map(ecs: &mut World, new_depth: i32) { vs.dirty = true; } } + +pub fn freeze_level_entities(ecs: &mut World) { + // Obtain ECS access + let entities = ecs.entities(); + let mut positions = ecs.write_storage::(); + let mut other_level_positions = ecs.write_storage::(); + let player_entity = ecs.fetch::(); + let map_depth = ecs.fetch::().depth; + + // Find positions and make OtherLevelPosition + let mut pos_to_delete: Vec = Vec::new(); + for (entity, pos) in (&entities, &positions).join() { + if entity != *player_entity { + other_level_positions + .insert( + entity, + OtherLevelPosition { + x: pos.x, + y: pos.y, + depth: map_depth, + }, + ) + .expect("Failed to insert OtherLevelPosition"); + + pos_to_delete.push(entity); + } + } + + // Remove positions + for p in pos_to_delete.iter() { + positions.remove(*p); + } +} + +pub fn thaw_level_entities(ecs: &mut World) { + // Obtain ECS access + let entities = ecs.entities(); + let mut positions = ecs.write_storage::(); + let mut other_level_positions = ecs.write_storage::(); + let player_entity = ecs.fetch::(); + let map_depth = ecs.fetch::().depth; + + // Find OtherLevelPosition + let mut pos_to_delete: Vec = Vec::new(); + for (entity, pos) in (&entities, &other_level_positions).join() { + if entity != *player_entity && pos.depth == map_depth { + positions + .insert(entity, Position { x: pos.x, y: pos.y }) + .expect("Failed to insert Position"); + pos_to_delete.push(entity); + } + } + + // Remove positions + for p in pos_to_delete.iter() { + other_level_positions.remove(*p); + } +} diff --git a/src/saveload_system.rs b/src/saveload_system.rs index 4d50705..bb97b7c 100644 --- a/src/saveload_system.rs +++ b/src/saveload_system.rs @@ -77,6 +77,7 @@ pub fn save_game(ecs: &mut World) { Monster, Name, NaturalAttackDefense, + OtherLevelPosition, ParticleLifetime, Player, Pools, @@ -177,6 +178,7 @@ pub fn load_game(ecs: &mut World) { Monster, Name, NaturalAttackDefense, + OtherLevelPosition, ParticleLifetime, Player, Pools,