From 8e3ed5ceadb78df3e41c4bc71382370d77efa183 Mon Sep 17 00:00:00 2001 From: Llywelwyn Date: Sun, 27 Aug 2023 16:21:15 +0100 Subject: [PATCH] huge refactor of overmap-local map travel --- src/data/ids.rs | 3 -- src/main.rs | 57 ++++++-------------- src/map/dungeon.rs | 66 ++++++++++------------- src/map/mod.rs | 2 +- src/map/themes.rs | 5 +- src/map/tiletype.rs | 34 +++++++++--- src/map_builders/prefab_builder/mod.rs | 5 +- src/map_builders/town.rs | 2 +- src/player.rs | 74 +++++++++----------------- 9 files changed, 103 insertions(+), 145 deletions(-) diff --git a/src/data/ids.rs b/src/data/ids.rs index 3bfa36f..5ec95b5 100644 --- a/src/data/ids.rs +++ b/src/data/ids.rs @@ -1,6 +1,3 @@ -pub const ID_PREVIOUS_LEVEL: i32 = -5; -pub const ID_NEXT_LEVEL: i32 = -6; - pub const ID_OVERMAP: i32 = 1; pub const ID_TOWN: i32 = 10; diff --git a/src/main.rs b/src/main.rs index 7414dcd..eeb62d0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -70,9 +70,7 @@ pub enum RunState { }, SaveGame, GameOver, - PreviousLevel, - NextLevel, - GoToLevel(i32, Option), + GoToLevel(i32, TileType), HelpScreen, MagicMapReveal { row: i32, @@ -90,12 +88,12 @@ pub struct State { } impl State { - fn generate_world_map(&mut self, new_id: i32, offset: i32, from_tile: Option) { + fn generate_world_map(&mut self, new_id: i32, dest_tile: TileType) { // Visualisation stuff self.mapgen_index = 0; self.mapgen_timer = 0.0; self.mapgen_history.clear(); - let map_building_info = map::level_transition(&mut self.ecs, new_id, offset, from_tile); + let map_building_info = map::level_transition(&mut self.ecs, new_id, dest_tile); if let Some(history) = map_building_info { self.mapgen_history = history; } else { @@ -172,35 +170,18 @@ impl State { default_move_ai.run_now(&self.ecs); } - fn goto_id(&mut self, id: i32, from_tile: Option) { + fn goto_id(&mut self, id: i32, dest_tile: TileType) { let current_id; { let worldmap_resource = self.ecs.fetch::(); current_id = worldmap_resource.id; } - let offset = id - current_id; - self.goto_level(offset, from_tile); - } - - fn goto_level(&mut self, offset: i32, from_tile: Option) { - // Build new map + place player - let current_id; - { - let worldmap_resource = self.ecs.fetch::(); - current_id = worldmap_resource.id; - } - // Record the correct type of event - if offset < 0 && current_id == 1 { - gamelog::Logger::new().append("CHEAT MENU: YOU CAN'T DO THAT.").colour((255, 0, 0)).log(); - return; - } else { - gamelog::record_event(EVENT::CHANGED_FLOOR(current_id + offset)); - } - // Freeze the current level + // Freeze curr level map::dungeon::freeze_entities(&mut self.ecs); - self.generate_world_map(current_id + offset, offset, from_tile); + self.generate_world_map(id, dest_tile); let mapname = self.ecs.fetch::().name.clone(); gamelog::Logger::new().append("You head to").npc_name_n(mapname).period().log(); + gamelog::record_event(EVENT::CHANGED_FLOOR(id)); } fn game_over_cleanup(&mut self) { @@ -221,7 +202,7 @@ impl State { } // Replace map list self.ecs.insert(map::dungeon::MasterDungeonMap::new()); - self.generate_world_map(1, 0, None); + self.generate_world_map(1, TileType::Floor); gamelog::setup_log(); gamelog::record_event(EVENT::LEVEL(1)); @@ -310,12 +291,14 @@ impl GameState for State { } gui::CheatMenuResult::NoResponse => {} gui::CheatMenuResult::Ascend => { - self.goto_level(-1, Some(TileType::UpStair)); + let id = self.ecs.fetch::().id - 1; + self.goto_id(id, TileType::DownStair); self.mapgen_next_state = Some(RunState::PreRun); new_runstate = RunState::MapGeneration; } gui::CheatMenuResult::Descend => { - self.goto_level(1, Some(TileType::DownStair)); + let id = self.ecs.fetch::().id + 1; + self.goto_id(id, TileType::UpStair); self.mapgen_next_state = Some(RunState::PreRun); new_runstate = RunState::MapGeneration; } @@ -534,18 +517,8 @@ impl GameState for State { }); } } - RunState::NextLevel => { - self.goto_level(1, Some(TileType::DownStair)); - self.mapgen_next_state = Some(RunState::PreRun); - new_runstate = RunState::MapGeneration; - } - RunState::PreviousLevel => { - self.goto_level(-1, Some(TileType::UpStair)); - self.mapgen_next_state = Some(RunState::PreRun); - new_runstate = RunState::MapGeneration; - } - RunState::GoToLevel(id, from_tile) => { - self.goto_id(id, from_tile); + RunState::GoToLevel(id, dest_tile) => { + self.goto_id(id, dest_tile); self.mapgen_next_state = Some(RunState::PreRun); new_runstate = RunState::MapGeneration; } @@ -750,7 +723,7 @@ fn main() -> rltk::BError { gamelog::setup_log(); gamelog::record_event(EVENT::LEVEL(1)); - gs.generate_world_map(1, 0, None); + gs.generate_world_map(1, TileType::Floor); rltk::main_loop(context, gs) } diff --git a/src/map/dungeon.rs b/src/map/dungeon.rs index e17af07..161795c 100644 --- a/src/map/dungeon.rs +++ b/src/map/dungeon.rs @@ -189,60 +189,55 @@ fn make_wand_name(rng: &mut RandomNumberGenerator, used_names: &mut HashSet) -> Option> { +pub fn level_transition(ecs: &mut World, new_id: i32, dest_tile: TileType) -> Option> { // Obtain master let dungeon_master = ecs.read_resource::(); if dungeon_master.get_map(new_id).is_some() { std::mem::drop(dungeon_master); - transition_to_existing_map(ecs, new_id, offset, from_tile); + transition_to_existing_map(ecs, new_id, dest_tile); return None; } else { std::mem::drop(dungeon_master); - return Some(transition_to_new_map(ecs, new_id)); + return Some(transition_to_new_map(ecs, new_id, dest_tile)); } } -fn transition_to_existing_map(ecs: &mut World, new_id: i32, offset: i32, from_tile: Option) { +fn transition_to_existing_map(ecs: &mut World, new_id: i32, dest_tile: TileType) { let mut dungeon_master = ecs.write_resource::(); // Unwrapping here panics if new_id isn't present. But this should // never be called without new_id being present by level_transition. let map = dungeon_master.get_map(new_id).unwrap(); let mut worldmap_resource = ecs.write_resource::(); let player_entity = ecs.fetch::(); - // Find down stairs, place player - let dest_tile = if from_tile.is_some() { - match from_tile.unwrap() { - TileType::UpStair => TileType::DownStair, - TileType::DownStair => TileType::UpStair, - TileType::ToTown => TileType::ToOvermap, - TileType::ToOvermap => { - match worldmap_resource.id { - ID_TOWN => TileType::ToTown, - ID_INFINITE => TileType::ToInfinite, - _ => panic!("Tried to transition to overmap from somewhere unaccounted for!"), - } - } - _ => if offset < 0 { TileType::DownStair } else { TileType::UpStair } - } - } else if offset < 0 { - TileType::DownStair - } else { - TileType::UpStair - }; let w = map.width; + let mut possible_destinations: Vec = Vec::new(); for (idx, tt) in map.tiles.iter().enumerate() { if *tt == dest_tile { - 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::(); - let player_pos_component = position_components.get_mut(*player_entity); - if let Some(player_pos_component) = player_pos_component { - player_pos_component.x = (idx as i32) % w; - player_pos_component.y = (idx as i32) / w; - } + possible_destinations.push(idx); } } + if possible_destinations.is_empty() { + console::log("WARNING: No destination tiles found on map transition."); + match dest_tile { + TileType::DownStair => console::log("DESTINATION: DownStair"), + TileType::UpStair => console::log("DESTINATION: UpStair"), + TileType::ToOvermap(id) => console::log(format!("DESTINATION: ToOvermap({})", id)), + TileType::ToLocal(id) => console::log(format!("DESTINATION: ToLocal({})", id)), + _ => console::log("DESTINATION: Unknown"), + } + possible_destinations.push(((map.width * map.height) as usize) / 2); // Centre of map + } + let mut rng = ecs.write_resource::(); + let idx = possible_destinations[(rng.roll_dice(1, possible_destinations.len() as i32) as usize) - 1]; + 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::(); + let player_pos_component = position_components.get_mut(*player_entity); + if let Some(player_pos_component) = player_pos_component { + player_pos_component.x = (idx as i32) % w; + player_pos_component.y = (idx as i32) / w; + } dungeon_master.store_map(&worldmap_resource); *worldmap_resource = map; // Dirtify viewsheds (forces refresh) @@ -258,7 +253,7 @@ fn transition_to_existing_map(ecs: &mut World, new_id: i32, offset: i32, from_ti } } -fn transition_to_new_map(ecs: &mut World, new_id: i32) -> Vec { +fn transition_to_new_map(ecs: &mut World, new_id: i32, _dest_tile: TileType) -> Vec { let mut rng = ecs.write_resource::(); // Might need this to fallback to 1, but if player // level isn't found at all, there's a bigger concern @@ -280,11 +275,6 @@ fn transition_to_new_map(ecs: &mut World, new_id: i32) -> Vec { let up_idx = builder.build_data.map.xy_idx(pos.x, pos.y); builder.build_data.map.tiles[up_idx] = TileType::UpStair; } - } else if old_map.overmap && !builder.build_data.map.overmap { - if let Some(pos) = &builder.build_data.starting_position { - let down_idx = builder.build_data.map.xy_idx(pos.x, pos.y); - builder.build_data.map.tiles[down_idx] = TileType::ToOvermap; - } } *worldmap_resource = builder.build_data.map.clone(); // Unwrap so we get a CTD if there's no starting pos. diff --git a/src/map/mod.rs b/src/map/mod.rs index 57d5453..39a621f 100644 --- a/src/map/mod.rs +++ b/src/map/mod.rs @@ -2,7 +2,7 @@ use rltk::{ Algorithm2D, BaseMap, Point }; use serde::{ Deserialize, Serialize }; use std::collections::HashSet; mod tiletype; -pub use tiletype::{ tile_cost, tile_opaque, tile_walkable, TileType }; +pub use tiletype::{ tile_cost, tile_opaque, tile_walkable, TileType, get_dest, Destination }; mod interval_spawning_system; pub use interval_spawning_system::try_spawn_interval; pub mod dungeon; diff --git a/src/map/themes.rs b/src/map/themes.rs index 0d29ea1..35a005c 100644 --- a/src/map/themes.rs +++ b/src/map/themes.rs @@ -88,9 +88,8 @@ pub fn get_default_theme_renderables(idx: usize, map: &Map, debug: Option) TileType::DeepWater => { glyph = rltk::to_cp437(DEEP_WATER_GLYPH); bg = RGB::named(DEEP_WATER_COLOUR); offsets = DEEP_WATER_OFFSETS; } TileType::Bars => { glyph = rltk::to_cp437(BARS_GLYPH); fg = RGB::named(BARS_COLOUR); bg = RGB::named(FLOOR_COLOUR); } TileType::ImpassableMountain => { glyph = rltk::to_cp437(IMPASSABLE_MOUNTAIN_GLYPH); bg = RGB::named(IMPASSABLE_MOUNTAIN_COLOUR); offsets = IMPASSABLE_MOUNTAIN_OFFSETS } - TileType::ToOvermap => { glyph = rltk::to_cp437(TO_OVERMAP_GLYPH); fg = RGB::named(TO_OVERMAP_COLOUR); bg = RGB::named(DEFAULT_BG_COLOUR); bg_main_col = false; } - TileType::ToTown => { glyph = rltk::to_cp437(TO_TOWN_GLYPH); fg = RGB::named(TO_TOWN_COLOUR); bg = RGB::named(DEFAULT_BG_COLOUR); bg_main_col = false; } - TileType::ToInfinite => { glyph = rltk::to_cp437(TO_INFINITE_GLYPH); fg = RGB::named(TO_INFINITE_COLOUR); bg = RGB::named(DEFAULT_BG_COLOUR); bg_main_col = true; } + TileType::ToOvermap(_) => { glyph = rltk::to_cp437(TO_OVERMAP_GLYPH); fg = RGB::named(TO_OVERMAP_COLOUR); bg = RGB::named(DEFAULT_BG_COLOUR); bg_main_col = false; } + TileType::ToLocal(_) => { glyph = rltk::to_cp437(TO_TOWN_GLYPH); fg = RGB::named(TO_TOWN_COLOUR); bg = RGB::named(DEFAULT_BG_COLOUR); bg_main_col = false; } } return (glyph, fg, bg, offsets, bg_main_col); } diff --git a/src/map/tiletype.rs b/src/map/tiletype.rs index 34a08b1..253b5bc 100644 --- a/src/map/tiletype.rs +++ b/src/map/tiletype.rs @@ -24,18 +24,15 @@ pub enum TileType { DownStair, UpStair, // To/From Overmap - ids are in src/data/ids.rs, are used in try_change_level() in src/player.rs - ToOvermap, - ToTown, - ToInfinite, + ToOvermap(i32), + ToLocal(i32), } - pub fn tile_walkable(tt: TileType) -> bool { match tt { TileType::ImpassableMountain | TileType::Wall | TileType::DeepWater | TileType::Fence | TileType::Bars => false, _ => true, } } - pub fn tile_opaque(tt: TileType) -> bool { match tt { TileType::ImpassableMountain => true, @@ -43,7 +40,6 @@ pub fn tile_opaque(tt: TileType) -> bool { _ => false, } } - pub fn tile_cost(tt: TileType) -> f32 { match tt { TileType::Road => 0.5, @@ -52,3 +48,29 @@ pub fn tile_cost(tt: TileType) -> f32 { _ => 1.0, } } +pub fn get_dest(this_tile: TileType, backtracking: bool) -> Destination { + let result = if !backtracking { + match this_tile { + // If on downstair, GOTO next level, and end up on an upstair + TileType::DownStair => Destination::NextLevel, + // If on overmap ToLocal tile, GOTO local map, and end up on an overmap ToOvermap tile with corresponding ID + TileType::ToLocal(id) => Destination::ToOvermap(id), + _ => Destination::None, + } + } else { + match this_tile { + TileType::UpStair => Destination::PreviousLevel, + TileType::ToOvermap(id) => Destination::ToLocal(id), + _ => Destination::None, + } + }; + return result; +} + +pub enum Destination { + PreviousLevel, + NextLevel, + ToOvermap(i32), + ToLocal(i32), + None, +} diff --git a/src/map_builders/prefab_builder/mod.rs b/src/map_builders/prefab_builder/mod.rs index db2b8d7..478db11 100644 --- a/src/map_builders/prefab_builder/mod.rs +++ b/src/map_builders/prefab_builder/mod.rs @@ -4,6 +4,7 @@ pub mod prefab_levels; pub mod prefab_sections; pub mod prefab_vaults; use std::collections::HashSet; +use crate::data::ids::*; #[derive(PartialEq, Copy, Clone)] #[allow(dead_code)] @@ -178,10 +179,10 @@ impl PrefabBuilder { build_data.map.tiles[idx] = TileType::ImpassableMountain; } '1' => { - build_data.map.tiles[idx] = TileType::ToTown; + build_data.map.tiles[idx] = TileType::ToLocal(ID_TOWN); } '2' => { - build_data.map.tiles[idx] = TileType::ToInfinite; + build_data.map.tiles[idx] = TileType::ToLocal(ID_INFINITE); } _ => { rltk::console::log(format!("Unknown glyph '{}' when loading overmap", ch as u8 as char)); diff --git a/src/map_builders/town.rs b/src/map_builders/town.rs index f55d38b..b768219 100644 --- a/src/map_builders/town.rs +++ b/src/map_builders/town.rs @@ -75,7 +75,7 @@ impl TownBuilder { y: wall_gap_y, }); let overmap_entrance = build_data.map.xy_idx(build_data.width - 2, wall_gap_y); - build_data.map.tiles[overmap_entrance] = TileType::ToOvermap; + build_data.map.tiles[overmap_entrance] = TileType::ToOvermap(build_data.map.id); build_data.take_snapshot(); } diff --git a/src/player.rs b/src/player.rs index deb4e48..b3e7304 100644 --- a/src/player.rs +++ b/src/player.rs @@ -29,6 +29,8 @@ use super::{ Viewshed, WantsToMelee, WantsToPickupItem, + get_dest, + Destination, }; use rltk::prelude::*; use rltk::{ Point, RandomNumberGenerator, Rltk, VirtualKeyCode }; @@ -530,26 +532,33 @@ pub fn player_input(gs: &mut State, ctx: &mut Rltk) -> RunState { // id VirtualKeyCode::Period => { if ctx.shift { - let (id, from_tile) = try_next_level(&mut gs.ecs); - if from_tile.is_none() { - return RunState::AwaitingInput; - } else if id == ID_NEXT_LEVEL { - return RunState::NextLevel; - } - return RunState::GoToLevel(id, from_tile); + let dest = try_change_level(&mut gs.ecs, false); + let curr_map_id = gs.ecs.fetch::().id; + return match dest { + // If we have no destination, do nothing. + Destination::None => RunState::AwaitingInput, + // If we want to go to the next level, go to the up-stair tile of id + 1. + Destination::NextLevel => RunState::GoToLevel(curr_map_id + 1, TileType::UpStair), + // If we want to go to the previous level, go to the down-stair tile of id - 1. + Destination::PreviousLevel => RunState::GoToLevel(curr_map_id - 1, TileType::DownStair), + Destination::ToLocal(id) => RunState::GoToLevel(ID_OVERMAP, TileType::ToLocal(id)), + Destination::ToOvermap(id) => RunState::GoToLevel(id, TileType::ToOvermap(id)), + }; } else { return skip_turn(&mut gs.ecs); // (Wait a turn) } } VirtualKeyCode::Comma => { if ctx.shift { - let (id, from_tile) = try_prev_level(&mut gs.ecs); - if from_tile.is_none() { - return RunState::AwaitingInput; - } else if id == ID_PREVIOUS_LEVEL { - return RunState::PreviousLevel; - } - return RunState::GoToLevel(id, from_tile); + let dest = try_change_level(&mut gs.ecs, true); + let curr_map_id = gs.ecs.fetch::().id; + return match dest { + Destination::None => RunState::AwaitingInput, + Destination::NextLevel => RunState::GoToLevel(curr_map_id + 1, TileType::UpStair), + Destination::PreviousLevel => RunState::GoToLevel(curr_map_id - 1, TileType::DownStair), + Destination::ToLocal(id) => RunState::GoToLevel(ID_OVERMAP, TileType::ToLocal(id)), + Destination::ToOvermap(id) => RunState::GoToLevel(id, TileType::ToOvermap(id)), + }; } else { return skip_turn(&mut gs.ecs); // (Wait a turn) } @@ -600,45 +609,12 @@ pub fn player_input(gs: &mut State, ctx: &mut Rltk) -> RunState { return RunState::AwaitingInput; } -fn try_next_level(ecs: &mut World) -> (i32, Option) { +fn try_change_level(ecs: &mut World, backtracking: bool) -> Destination { let player_pos = ecs.fetch::(); let map = ecs.fetch::(); let player_idx = map.xy_idx(player_pos.x, player_pos.y); let this_tile = map.tiles[player_idx]; - match this_tile { - TileType::DownStair => { - return (ID_NEXT_LEVEL, Some(this_tile)); - } - TileType::ToTown => { - return (ID_TOWN, Some(this_tile)); - } - TileType::ToInfinite => { - return (ID_INFINITE, Some(this_tile)); - } - _ => { - gamelog::Logger::new().append("You don't see a way down from here.").log(); - return (0, None); - } - } -} - -fn try_prev_level(ecs: &mut World) -> (i32, Option) { - let player_pos = ecs.fetch::(); - let map = ecs.fetch::(); - let player_idx = map.xy_idx(player_pos.x, player_pos.y); - let this_tile = map.tiles[player_idx]; - match this_tile { - TileType::UpStair => { - return (ID_PREVIOUS_LEVEL, Some(this_tile)); - } - TileType::ToOvermap => { - return (ID_OVERMAP, Some(this_tile)); - } - _ => { - gamelog::Logger::new().append("You don't see a way out from here.").log(); - return (0, None); - } - } + return get_dest(this_tile, backtracking); } fn skip_turn(ecs: &mut World) -> RunState {