Allow restoring of NPCs on level changes
This commit is contained in:
parent
dd6a4c26d9
commit
b4fc2ba28f
@ -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.
|
||||
|
||||
|
95
src/main.rs
95
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<Entity> {
|
||||
let entities = self.ecs.entities();
|
||||
let player = self.ecs.read_storage::<Player>();
|
||||
let backpack = self.ecs.read_storage::<InBackpack>();
|
||||
let player_entity = self.ecs.fetch::<Entity>();
|
||||
let equipped = self.ecs.read_storage::<Equipped>();
|
||||
|
||||
let mut to_delete: Vec<Entity> = 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::<Map>();
|
||||
current_depth = worldmap_resource.depth;
|
||||
}
|
||||
self.generate_world_map(current_depth + 1);
|
||||
let current_depth = self.ecs.fetch::<Map>().depth;
|
||||
self.generate_world_map(current_depth + offset, offset);
|
||||
|
||||
// Notify the player
|
||||
let mut gamelog = self.ecs.fetch_mut::<GameLog>();
|
||||
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::<Map>();
|
||||
current_depth = worldmap_resource.depth;
|
||||
}
|
||||
self.generate_world_map(current_depth - 1);
|
||||
|
||||
// Notify the player
|
||||
let mut gamelog = self.ecs.fetch_mut::<GameLog>();
|
||||
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)
|
||||
}
|
||||
|
@ -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<Vec<Map>> {
|
||||
pub fn level_transition(ecs: &mut World, new_depth: i32, offset: i32) -> Option<Vec<Map>> {
|
||||
// Obtain the master dungeon map
|
||||
let dungeon_master = ecs.read_resource::<MasterDungeonMap>();
|
||||
|
||||
// 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<Map> {
|
||||
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::<MasterDungeonMap>();
|
||||
let map = dungeon_master.get_map(new_depth).unwrap();
|
||||
let mut worldmap_resource = ecs.write_resource::<Map>();
|
||||
@ -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::<Point>();
|
||||
*player_position = Point::new(idx as i32 % w, idx as i32 / w);
|
||||
let mut position_components = ecs.write_storage::<Position>();
|
||||
@ -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::<Position>();
|
||||
let mut other_level_positions = ecs.write_storage::<OtherLevelPosition>();
|
||||
let player_entity = ecs.fetch::<Entity>();
|
||||
let map_depth = ecs.fetch::<Map>().depth;
|
||||
|
||||
// Find positions and make OtherLevelPosition
|
||||
let mut pos_to_delete: Vec<Entity> = 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::<Position>();
|
||||
let mut other_level_positions = ecs.write_storage::<OtherLevelPosition>();
|
||||
let player_entity = ecs.fetch::<Entity>();
|
||||
let map_depth = ecs.fetch::<Map>().depth;
|
||||
|
||||
// Find OtherLevelPosition
|
||||
let mut pos_to_delete: Vec<Entity> = 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);
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user