From ea79d4064fb1836be60d7c8c9bb8d93fa07ce287 Mon Sep 17 00:00:00 2001 From: Llywelwyn Date: Thu, 20 Jul 2023 21:55:03 +0100 Subject: [PATCH] refactors image loading into fully-fledged prefab builder --- resources/wfc-demo2.xp | Bin 117 -> 0 bytes src/map_builders/mod.rs | 48 +++++---- src/map_builders/prefab_builder.rs | 144 +++++++++++++++++++++++++++ src/map_builders/wfc/image_loader.rs | 24 ----- src/map_builders/wfc/mod.rs | 31 +----- src/rex_assets.rs | 2 + src/spawner.rs | 2 +- 7 files changed, 181 insertions(+), 70 deletions(-) delete mode 100644 resources/wfc-demo2.xp create mode 100644 src/map_builders/prefab_builder.rs delete mode 100644 src/map_builders/wfc/image_loader.rs diff --git a/resources/wfc-demo2.xp b/resources/wfc-demo2.xp deleted file mode 100644 index 02698690ae2cb5336622dc2ae90a56d5f21e39c6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 117 zcmb2|=3oE=?%)6a|F3srV`Gz4NMlb*RoHj(KtfJVPw&!(aIyV-Ns)_}2_&CVTeYDl z{onC}K?k(E Box { - let mut rng = rltk::RandomNumberGenerator::new(); - let builder = rng.roll_dice(1, 17); + /*let mut rng = rltk::RandomNumberGenerator::new(); + let builder = rng.roll_dice(1, 16); let mut result : Box; match builder { - 1 => { result = Box::new(bsp_dungeon::BspDungeonBuilder::new(new_depth)); } - 2 => { result = Box::new(bsp_interior::BspInteriorBuilder::new(new_depth)); } - 3 => { result = Box::new(cellular_automata::CellularAutomataBuilder::new(new_depth)); } - 4 => { result = Box::new(drunkard::DrunkardsWalkBuilder::open_area(new_depth)); } - 5 => { result = Box::new(drunkard::DrunkardsWalkBuilder::open_halls(new_depth)); } - 6 => { result = Box::new(drunkard::DrunkardsWalkBuilder::winding_passages(new_depth)); } - 7 => { result = Box::new(drunkard::DrunkardsWalkBuilder::fat_passages(new_depth)); } - 8 => { result = Box::new(drunkard::DrunkardsWalkBuilder::fearful_symmetry(new_depth)); } - 9 => { result = Box::new(maze::MazeBuilder::new(new_depth)); } - 10 => { result = Box::new(dla::DLABuilder::walk_inwards(new_depth)); } - 11 => { result = Box::new(dla::DLABuilder::walk_outwards(new_depth)); } - 12 => { result = Box::new(dla::DLABuilder::central_attractor(new_depth)); } - 13 => { result = Box::new(dla::DLABuilder::insectoid(new_depth)); } - 14 => { result = Box::new(voronoi::VoronoiBuilder::pythagoras(new_depth)); } - 15 => { result = Box::new(voronoi::VoronoiBuilder::manhattan(new_depth)); } - 16 => { result = Box::new(wfc::WaveFunctionCollapseBuilder::test_map(new_depth)); } + 1 => { result = Box::new(BspDungeonBuilder::new(new_depth)); } + 2 => { result = Box::new(BspInteriorBuilder::new(new_depth)); } + 3 => { result = Box::new(CellularAutomataBuilder::new(new_depth)); } + 4 => { result = Box::new(DrunkardsWalkBuilder::open_area(new_depth)); } + 5 => { result = Box::new(DrunkardsWalkBuilder::open_halls(new_depth)); } + 6 => { result = Box::new(DrunkardsWalkBuilder::winding_passages(new_depth)); } + 7 => { result = Box::new(DrunkardsWalkBuilder::fat_passages(new_depth)); } + 8 => { result = Box::new(DrunkardsWalkBuilder::fearful_symmetry(new_depth)); } + 9 => { result = Box::new(MazeBuilder::new(new_depth)); } + 10 => { result = Box::new(DLABuilder::walk_inwards(new_depth)); } + 11 => { result = Box::new(DLABuilder::walk_outwards(new_depth)); } + 12 => { result = Box::new(DLABuilder::central_attractor(new_depth)); } + 13 => { result = Box::new(DLABuilder::insectoid(new_depth)); } + 14 => { result = Box::new(VoronoiBuilder::pythagoras(new_depth)); } + 15 => { result = Box::new(VoronoiBuilder::manhattan(new_depth)); } _ => { result = Box::new(simple_map::SimpleMapBuilder::new(new_depth)); } } @@ -51,5 +59,7 @@ pub fn random_builder(new_depth: i32) -> Box { result = Box::new(wfc::WaveFunctionCollapseBuilder::derived_map(new_depth, result)); } - result + result*/ + Box::new(prefab_builder::PrefabBuilder::new(new_depth)) + } diff --git a/src/map_builders/prefab_builder.rs b/src/map_builders/prefab_builder.rs new file mode 100644 index 0000000..b491042 --- /dev/null +++ b/src/map_builders/prefab_builder.rs @@ -0,0 +1,144 @@ +use super::{ + remove_unreachable_areas_returning_most_distant, spawner, Map, MapBuilder, Position, TileType, SHOW_MAPGEN, +}; +use rltk::RandomNumberGenerator; +use specs::prelude::*; + +#[allow(dead_code)] +#[derive(PartialEq, Clone)] +pub enum PrefabMode { + RexLevel { template: &'static str }, +} + +pub struct PrefabBuilder { + map: Map, + starting_position: Position, + depth: i32, + history: Vec, + mode: PrefabMode, + spawns: Vec<(usize, String)>, +} + +impl MapBuilder for PrefabBuilder { + fn build_map(&mut self, rng: &mut RandomNumberGenerator) { + return self.build(rng); + } + fn spawn_entities(&mut self, ecs: &mut World) { + for entity in self.spawns.iter() { + spawner::spawn_entity(ecs, &(&entity.0, &entity.1)); + } + } + // Getters + fn get_map(&mut self) -> Map { + return self.map.clone(); + } + fn get_starting_pos(&mut self) -> Position { + return self.starting_position.clone(); + } + // Mapgen visualisation stuff + fn get_snapshot_history(&self) -> Vec { + return self.history.clone(); + } + fn take_snapshot(&mut self) { + if SHOW_MAPGEN { + let mut snapshot = self.map.clone(); + for v in snapshot.revealed_tiles.iter_mut() { + *v = true; + } + self.history.push(snapshot); + } + } +} + +impl PrefabBuilder { + pub fn new(new_depth: i32) -> PrefabBuilder { + PrefabBuilder { + map: Map::new(new_depth), + starting_position: Position { x: 0, y: 0 }, + depth: new_depth, + history: Vec::new(), + mode: PrefabMode::RexLevel { template: "../resources/wfc-populated.xp" }, + spawns: Vec::new(), + } + } + + fn build(&mut self, rng: &mut RandomNumberGenerator) { + match self.mode { + PrefabMode::RexLevel { template } => self.load_rex_map(&template), + } + self.take_snapshot(); + + // Find starting pos by starting at middle and walking left until finding a floor tile + if self.starting_position.x == 0 { + self.starting_position = Position { x: self.map.width / 2, y: self.map.height / 2 }; + let mut start_idx = self.map.xy_idx(self.starting_position.x, self.starting_position.y); + while self.map.tiles[start_idx] != TileType::Floor { + self.starting_position.x -= 1; + start_idx = self.map.xy_idx(self.starting_position.x, self.starting_position.y); + } + self.take_snapshot(); + + // Find all tiles we can reach from the starting point + let exit_tile = remove_unreachable_areas_returning_most_distant(&mut self.map, start_idx); + self.take_snapshot(); + + // Place the stairs + self.map.tiles[exit_tile] = TileType::DownStair; + self.take_snapshot(); + } + } + + #[allow(dead_code)] + fn load_rex_map(&mut self, path: &str) { + let xp_file = rltk::rex::XpFile::from_resource(path).unwrap(); + + for layer in &xp_file.layers { + for y in 0..layer.height { + for x in 0..layer.width { + let cell = layer.get(x, y).unwrap(); + if x < self.map.width as usize && y < self.map.height as usize { + // Saving these for later, for flipping the pref horizontally/vertically/both. + // let flipped_x = (self.map.width - 1) - x as i32; + // let flipped_y = (self.map.height - 1) - y as i32; + let idx = self.map.xy_idx(x as i32, y as i32); + match (cell.ch as u8) as char { + '@' => { + self.map.tiles[idx] = TileType::Floor; + self.starting_position = Position { x: x as i32, y: y as i32 } + } + ' ' => self.map.tiles[idx] = TileType::Floor, + '#' => self.map.tiles[idx] = TileType::Wall, + '>' => self.map.tiles[idx] = TileType::DownStair, + 'g' => { + self.map.tiles[idx] = TileType::Floor; + self.spawns.push((idx, "goblin".to_string())); + } + 'o' => { + self.map.tiles[idx] = TileType::Floor; + self.spawns.push((idx, "orc".to_string())); + } + '^' => { + self.map.tiles[idx] = TileType::Floor; + self.spawns.push((idx, "bear trap".to_string())); + } + '%' => { + self.map.tiles[idx] = TileType::Floor; + self.spawns.push((idx, "rations".to_string())); + } + '!' => { + self.map.tiles[idx] = TileType::Floor; + self.spawns.push((idx, "health potion".to_string())); + } + _ => { + rltk::console::log(format!( + "Unknown glyph {} when loading map", + (cell.ch as u8) as char + )); + } + } + } + } + } + } + } +} diff --git a/src/map_builders/wfc/image_loader.rs b/src/map_builders/wfc/image_loader.rs deleted file mode 100644 index 5087742..0000000 --- a/src/map_builders/wfc/image_loader.rs +++ /dev/null @@ -1,24 +0,0 @@ -use super::{Map, TileType}; -use rltk::rex::XpFile; - -// Load RexPaint file, convert to map format -pub fn load_rex_map(new_depth: i32, xp_file: &XpFile) -> Map { - let mut map: Map = Map::new(new_depth); - - for layer in &xp_file.layers { - for y in 0..layer.height { - for x in 0..layer.width { - let cell = layer.get(x, y).unwrap(); - if x < map.width as usize && y < map.height as usize { - let idx = map.xy_idx(x as i32, y as i32); - match cell.ch { - 32 => map.tiles[idx] = TileType::Floor, // . - 35 => map.tiles[idx] = TileType::Wall, // # - _ => {} - } - } - } - } - } - map -} diff --git a/src/map_builders/wfc/mod.rs b/src/map_builders/wfc/mod.rs index 420e380..d270880 100644 --- a/src/map_builders/wfc/mod.rs +++ b/src/map_builders/wfc/mod.rs @@ -2,8 +2,7 @@ use super::{ generate_voronoi_spawn_regions, remove_unreachable_areas_returning_most_distant, spawner, Map, MapBuilder, Position, TileType, SHOW_MAPGEN, }; -mod image_loader; -use image_loader::load_rex_map; + mod common; use common::MapChunk; mod constraints; @@ -13,19 +12,12 @@ use solver::Solver; use specs::prelude::*; use std::collections::HashMap; -#[derive(PartialEq, Copy, Clone)] -pub enum WaveFunctionMode { - TestMap, - Derived, -} - pub struct WaveFunctionCollapseBuilder { map: Map, starting_position: Position, depth: i32, history: Vec, noise_areas: HashMap>, - mode: WaveFunctionMode, derive_from: Option>, } @@ -61,36 +53,22 @@ impl MapBuilder for WaveFunctionCollapseBuilder { } impl WaveFunctionCollapseBuilder { - pub fn new( - new_depth: i32, - mode: WaveFunctionMode, - derive_from: Option>, - ) -> WaveFunctionCollapseBuilder { + pub fn new(new_depth: i32, derive_from: Option>) -> WaveFunctionCollapseBuilder { WaveFunctionCollapseBuilder { map: Map::new(new_depth), starting_position: Position { x: 0, y: 0 }, depth: new_depth, history: Vec::new(), noise_areas: HashMap::new(), - mode, derive_from, } } - pub fn test_map(new_depth: i32) -> WaveFunctionCollapseBuilder { - WaveFunctionCollapseBuilder::new(new_depth, WaveFunctionMode::TestMap, None) - } + pub fn derived_map(new_depth: i32, builder: Box) -> WaveFunctionCollapseBuilder { - WaveFunctionCollapseBuilder::new(new_depth, WaveFunctionMode::Derived, Some(builder)) + WaveFunctionCollapseBuilder::new(new_depth, Some(builder)) } fn build(&mut self, rng: &mut RandomNumberGenerator) { - if self.mode == WaveFunctionMode::TestMap { - self.map = - load_rex_map(self.depth, &rltk::rex::XpFile::from_resource("../resources/wfc-demo1.xp").unwrap()); - self.take_snapshot(); - return; - } - const CHUNK_SIZE: i32 = 8; let prebuilder = &mut self.derive_from.as_mut().unwrap(); @@ -153,6 +131,7 @@ impl WaveFunctionCollapseBuilder { // Next row x = 1; y += chunk_size + 1; + self.take_snapshot(); if y + chunk_size > self.map.height { // Next page diff --git a/src/rex_assets.rs b/src/rex_assets.rs index 0b0ab8f..8a7be72 100644 --- a/src/rex_assets.rs +++ b/src/rex_assets.rs @@ -2,6 +2,7 @@ use rltk::rex::XpFile; rltk::embedded_resource!(CAVE_TUNNEL, "../resources/cave_tunnel80x60.xp"); rltk::embedded_resource!(WFC_DEMO_IMAGE1, "../resources/wfc-demo1.xp"); +rltk::embedded_resource!(WFC_POPULATED, "../resources/wfc-populated.xp"); pub struct RexAssets { pub menu: XpFile, @@ -12,6 +13,7 @@ impl RexAssets { pub fn new() -> RexAssets { rltk::link_resource!(CAVE_TUNNEL, "../resources/cave_tunnel80x60.xp"); rltk::link_resource!(WFC_DEMO_IMAGE1, "../resources/wfc-demo1.xp"); + rltk::link_resource!(WFC_POPULATED, "../resources/wfc-populated.xp"); RexAssets { menu: XpFile::from_resource("../resources/cave_tunnel80x60.xp").unwrap() } } diff --git a/src/spawner.rs b/src/spawner.rs index 97909ec..2d27a18 100644 --- a/src/spawner.rs +++ b/src/spawner.rs @@ -121,7 +121,7 @@ pub fn spawn_region(ecs: &mut World, area: &[usize], map_depth: i32) { } } -fn spawn_entity(ecs: &mut World, spawn: &(&usize, &String)) { +pub fn spawn_entity(ecs: &mut World, spawn: &(&usize, &String)) { let x = (*spawn.0 % MAPWIDTH) as i32; let y = (*spawn.0 / MAPWIDTH) as i32;