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,
|
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
|
// Serialization helper code. We need to implement ConvertSaveLoad for each type that contains an
|
||||||
// Entity.
|
// Entity.
|
||||||
|
|
||||||
|
95
src/main.rs
95
src/main.rs
@ -343,11 +343,11 @@ impl GameState for State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
RunState::NextLevel => {
|
RunState::NextLevel => {
|
||||||
self.goto_next_level();
|
self.goto_level(1);
|
||||||
newrunstate = RunState::PreRun;
|
newrunstate = RunState::PreRun;
|
||||||
}
|
}
|
||||||
RunState::PreviousLevel => {
|
RunState::PreviousLevel => {
|
||||||
self.goto_previous_level();
|
self.goto_level(-1);
|
||||||
self.mapgen_next_state = Some(RunState::PreRun);
|
self.mapgen_next_state = Some(RunState::PreRun);
|
||||||
newrunstate = RunState::MapGeneration;
|
newrunstate = RunState::MapGeneration;
|
||||||
}
|
}
|
||||||
@ -376,86 +376,16 @@ impl GameState for State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
fn entities_to_remove_on_level_change(&mut self) -> Vec<Entity> {
|
fn goto_level(&mut self, offset: i32) {
|
||||||
let entities = self.ecs.entities();
|
freeze_level_entities(&mut self.ecs);
|
||||||
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build a new map and place the player
|
// Build a new map and place the player
|
||||||
#[allow(unused_assignments)]
|
let current_depth = self.ecs.fetch::<Map>().depth;
|
||||||
let mut current_depth = 1;
|
self.generate_world_map(current_depth + offset, offset);
|
||||||
{
|
|
||||||
let worldmap_resource = self.ecs.fetch::<Map>();
|
|
||||||
current_depth = worldmap_resource.depth;
|
|
||||||
}
|
|
||||||
self.generate_world_map(current_depth + 1);
|
|
||||||
|
|
||||||
// Notify the player
|
// Notify the player
|
||||||
let mut gamelog = self.ecs.fetch_mut::<GameLog>();
|
let mut gamelog = self.ecs.fetch_mut::<GameLog>();
|
||||||
gamelog.append("You descend to the next level.");
|
gamelog.append("You change 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.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn game_over_cleanup(&mut self) {
|
fn game_over_cleanup(&mut self) {
|
||||||
@ -481,16 +411,18 @@ impl State {
|
|||||||
self.ecs.insert(map::MasterDungeonMap::new());
|
self.ecs.insert(map::MasterDungeonMap::new());
|
||||||
|
|
||||||
// Build a new map and place the player
|
// 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_index = 0;
|
||||||
self.mapgen_timer = 0.0;
|
self.mapgen_timer = 0.0;
|
||||||
self.mapgen_history.clear();
|
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;
|
self.mapgen_history = history;
|
||||||
|
} else {
|
||||||
|
map::thaw_level_entities(&mut self.ecs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -530,6 +462,7 @@ fn main() -> ::rltk::BError {
|
|||||||
Monster,
|
Monster,
|
||||||
Name,
|
Name,
|
||||||
NaturalAttackDefense,
|
NaturalAttackDefense,
|
||||||
|
OtherLevelPosition,
|
||||||
ParticleLifetime,
|
ParticleLifetime,
|
||||||
Player,
|
Player,
|
||||||
Pools,
|
Pools,
|
||||||
@ -571,7 +504,7 @@ fn main() -> ::rltk::BError {
|
|||||||
gs.ecs.insert(particle_system::ParticleBuilder::new());
|
gs.ecs.insert(particle_system::ParticleBuilder::new());
|
||||||
gs.ecs.insert(rex_assets::RexAssets::new());
|
gs.ecs.insert(rex_assets::RexAssets::new());
|
||||||
|
|
||||||
gs.generate_world_map(1);
|
gs.generate_world_map(1, 0);
|
||||||
|
|
||||||
rltk::main_loop(context, gs)
|
rltk::main_loop(context, gs)
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ use ::rltk::{Point, RandomNumberGenerator};
|
|||||||
use ::serde::{Deserialize, Serialize};
|
use ::serde::{Deserialize, Serialize};
|
||||||
use ::specs::prelude::*;
|
use ::specs::prelude::*;
|
||||||
|
|
||||||
use crate::components::{Position, Viewshed};
|
use crate::components::{OtherLevelPosition, Position, Viewshed};
|
||||||
use crate::map::{Map, TileType};
|
use crate::map::{Map, TileType};
|
||||||
use crate::map_builders::level_builder;
|
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
|
// Obtain the master dungeon map
|
||||||
let dungeon_master = ecs.read_resource::<MasterDungeonMap>();
|
let dungeon_master = ecs.read_resource::<MasterDungeonMap>();
|
||||||
|
|
||||||
// Do we already have a map?
|
// Do we already have a map?
|
||||||
if dungeon_master.get_map(new_depth).is_some() {
|
if dungeon_master.get_map(new_depth).is_some() {
|
||||||
std::mem::drop(dungeon_master);
|
std::mem::drop(dungeon_master);
|
||||||
transition_to_existing_map(ecs, new_depth);
|
transition_to_existing_map(ecs, new_depth, offset);
|
||||||
|
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
@ -102,7 +102,7 @@ fn transition_to_new_map(ecs: &mut World, new_depth: i32) -> Vec<Map> {
|
|||||||
mapgen_history
|
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 dungeon_master = ecs.write_resource::<MasterDungeonMap>();
|
||||||
let map = dungeon_master.get_map(new_depth).unwrap();
|
let map = dungeon_master.get_map(new_depth).unwrap();
|
||||||
let mut worldmap_resource = ecs.write_resource::<Map>();
|
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
|
// Find the down stairs and place the player
|
||||||
let w = map.width;
|
let w = map.width;
|
||||||
|
let stair_type = if offset < 0 {
|
||||||
|
TileType::DownStairs
|
||||||
|
} else {
|
||||||
|
TileType::UpStairs
|
||||||
|
};
|
||||||
for (idx, tt) in map.tiles.iter().enumerate() {
|
for (idx, tt) in map.tiles.iter().enumerate() {
|
||||||
if *tt == TileType::DownStairs {
|
if *tt == stair_type {
|
||||||
let mut player_position = ecs.write_resource::<Point>();
|
let mut player_position = ecs.write_resource::<Point>();
|
||||||
*player_position = Point::new(idx as i32 % w, idx as i32 / w);
|
*player_position = Point::new(idx as i32 % w, idx as i32 / w);
|
||||||
let mut position_components = ecs.write_storage::<Position>();
|
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;
|
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,
|
Monster,
|
||||||
Name,
|
Name,
|
||||||
NaturalAttackDefense,
|
NaturalAttackDefense,
|
||||||
|
OtherLevelPosition,
|
||||||
ParticleLifetime,
|
ParticleLifetime,
|
||||||
Player,
|
Player,
|
||||||
Pools,
|
Pools,
|
||||||
@ -177,6 +178,7 @@ pub fn load_game(ecs: &mut World) {
|
|||||||
Monster,
|
Monster,
|
||||||
Name,
|
Name,
|
||||||
NaturalAttackDefense,
|
NaturalAttackDefense,
|
||||||
|
OtherLevelPosition,
|
||||||
ParticleLifetime,
|
ParticleLifetime,
|
||||||
Player,
|
Player,
|
||||||
Pools,
|
Pools,
|
||||||
|
Loading…
Reference in New Issue
Block a user