Add constraints and common submodules to waveform_collapse map builder
This commit is contained in:
parent
3ce4994417
commit
5fcc22ab6a
@ -7,7 +7,7 @@ pub const MAP_WIDTH: usize = 80;
|
|||||||
pub const MAP_HEIGHT: usize = 43;
|
pub const MAP_HEIGHT: usize = 43;
|
||||||
pub const MAP_COUNT: usize = MAP_HEIGHT * MAP_WIDTH;
|
pub const MAP_COUNT: usize = MAP_HEIGHT * MAP_WIDTH;
|
||||||
|
|
||||||
#[derive(PartialEq, Copy, Clone, Serialize, Deserialize)]
|
#[derive(PartialEq, Eq, Hash, Copy, Clone, Serialize, Deserialize)]
|
||||||
pub enum TileType {
|
pub enum TileType {
|
||||||
Wall,
|
Wall,
|
||||||
Floor,
|
Floor,
|
||||||
|
13
src/map_builders/waveform_collapse/common.rs
Normal file
13
src/map_builders/waveform_collapse/common.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
use crate::TileType;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Hash, Clone)]
|
||||||
|
pub struct MapChunk {
|
||||||
|
pub pattern: Vec<TileType>,
|
||||||
|
pub exits: [Vec<bool>; 4],
|
||||||
|
pub has_exits: bool,
|
||||||
|
pub compatible_with: [Vec<usize>; 4],
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tile_idx_in_chunk(chunk_size: i32, x: i32, y: i32) -> usize {
|
||||||
|
((y * chunk_size) + x) as usize
|
||||||
|
}
|
226
src/map_builders/waveform_collapse/constraints.rs
Normal file
226
src/map_builders/waveform_collapse/constraints.rs
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
use super::MapChunk;
|
||||||
|
use crate::map_builders::waveform_collapse::common::tile_idx_in_chunk;
|
||||||
|
use crate::{Map, TileType};
|
||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
pub fn build_patterns(
|
||||||
|
map: &Map,
|
||||||
|
chunk_size: i32,
|
||||||
|
include_flipping: bool,
|
||||||
|
dedupe: bool,
|
||||||
|
) -> Vec<Vec<TileType>> {
|
||||||
|
let chunks_x = map.width / chunk_size;
|
||||||
|
let chunks_y = map.height / chunk_size;
|
||||||
|
let mut patterns = Vec::new();
|
||||||
|
|
||||||
|
for cy in 0..chunks_y {
|
||||||
|
for cx in 0..chunks_x {
|
||||||
|
// Normal orientation
|
||||||
|
let mut pattern: Vec<TileType> = Vec::new();
|
||||||
|
let start_x = cx * chunk_size;
|
||||||
|
let end_x = (cx + 1) * chunk_size;
|
||||||
|
let start_y = cy * chunk_size;
|
||||||
|
let end_y = (cy + 1) * chunk_size;
|
||||||
|
|
||||||
|
for y in start_y..end_y {
|
||||||
|
for x in start_x..end_x {
|
||||||
|
let idx = map.xy_idx(x, y);
|
||||||
|
pattern.push(map.tiles[idx]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
patterns.push(pattern);
|
||||||
|
|
||||||
|
if include_flipping {
|
||||||
|
// Flip horizontal
|
||||||
|
pattern = Vec::new();
|
||||||
|
for y in start_y..end_y {
|
||||||
|
for x in start_x..end_x {
|
||||||
|
let idx = map.xy_idx(end_x - (x + 1), y);
|
||||||
|
pattern.push(map.tiles[idx]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
patterns.push(pattern);
|
||||||
|
|
||||||
|
// Flip vertical
|
||||||
|
pattern = Vec::new();
|
||||||
|
for y in start_y..end_y {
|
||||||
|
for x in start_x..end_x {
|
||||||
|
let idx = map.xy_idx(x, end_y - (y + 1));
|
||||||
|
pattern.push(map.tiles[idx]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
patterns.push(pattern);
|
||||||
|
|
||||||
|
// Flip both
|
||||||
|
pattern = Vec::new();
|
||||||
|
for y in start_y..end_y {
|
||||||
|
for x in start_x..end_x {
|
||||||
|
let idx = map.xy_idx(end_x - (x + 1), end_y - (y + 1));
|
||||||
|
pattern.push(map.tiles[idx]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
patterns.push(pattern);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dedupe
|
||||||
|
if dedupe {
|
||||||
|
rltk::console::log(format!(
|
||||||
|
"Pre de-duplication, there are {} patterns",
|
||||||
|
patterns.len()
|
||||||
|
));
|
||||||
|
|
||||||
|
// Use a set to de-duplicate
|
||||||
|
let set: HashSet<Vec<TileType>> = patterns.drain(..).collect();
|
||||||
|
patterns.extend(set.into_iter());
|
||||||
|
|
||||||
|
rltk::console::log(format!("There are {} patterns", patterns.len()));
|
||||||
|
}
|
||||||
|
|
||||||
|
patterns
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_pattern_to_map(
|
||||||
|
map: &mut Map,
|
||||||
|
chunk: &MapChunk,
|
||||||
|
chunk_size: i32,
|
||||||
|
start_x: i32,
|
||||||
|
start_y: i32,
|
||||||
|
) {
|
||||||
|
let mut i = 0_usize;
|
||||||
|
for tile_y in 0..chunk_size {
|
||||||
|
for tile_x in 0..chunk_size {
|
||||||
|
let map_idx = map.xy_idx(start_x + tile_x, start_y + tile_y);
|
||||||
|
map.tiles[map_idx] = chunk.pattern[i];
|
||||||
|
map.visible_tiles[map_idx] = true;
|
||||||
|
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (x, northbound) in chunk.exits[0].iter().enumerate() {
|
||||||
|
if *northbound {
|
||||||
|
let map_idx = map.xy_idx(start_x + x as i32, start_y);
|
||||||
|
map.tiles[map_idx] = TileType::DownStairs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (x, southbound) in chunk.exits[1].iter().enumerate() {
|
||||||
|
if *southbound {
|
||||||
|
let map_idx = map.xy_idx(start_x + x as i32, start_y + chunk_size - 1);
|
||||||
|
map.tiles[map_idx] = TileType::DownStairs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (x, westbound) in chunk.exits[2].iter().enumerate() {
|
||||||
|
if *westbound {
|
||||||
|
let map_idx = map.xy_idx(start_x, start_y + x as i32);
|
||||||
|
map.tiles[map_idx] = TileType::DownStairs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (x, eastbound) in chunk.exits[3].iter().enumerate() {
|
||||||
|
if *eastbound {
|
||||||
|
let map_idx = map.xy_idx(start_x + chunk_size - 1, start_y + x as i32);
|
||||||
|
map.tiles[map_idx] = TileType::DownStairs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn patterns_to_constraints(patterns: Vec<Vec<TileType>>, chunk_size: i32) -> Vec<MapChunk> {
|
||||||
|
// Move into the new constraints object
|
||||||
|
let mut constraints: Vec<MapChunk> = Vec::new();
|
||||||
|
for p in patterns {
|
||||||
|
let mut new_chunk = MapChunk {
|
||||||
|
pattern: p,
|
||||||
|
exits: [Vec::new(), Vec::new(), Vec::new(), Vec::new()],
|
||||||
|
has_exits: true,
|
||||||
|
compatible_with: [Vec::new(), Vec::new(), Vec::new(), Vec::new()],
|
||||||
|
};
|
||||||
|
for exit in new_chunk.exits.iter_mut() {
|
||||||
|
for _i in 0..chunk_size {
|
||||||
|
exit.push(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut n_exits = 0;
|
||||||
|
for x in 0..chunk_size {
|
||||||
|
// Check for north-bound exits
|
||||||
|
let north_idx = tile_idx_in_chunk(chunk_size, x, 0);
|
||||||
|
if new_chunk.pattern[north_idx] == TileType::Floor {
|
||||||
|
new_chunk.exits[0][x as usize] = true;
|
||||||
|
n_exits += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for south-bound exits
|
||||||
|
let south_idx = tile_idx_in_chunk(chunk_size, x, chunk_size - 1);
|
||||||
|
if new_chunk.pattern[south_idx] == TileType::Floor {
|
||||||
|
new_chunk.exits[1][x as usize] = true;
|
||||||
|
n_exits += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for west-bound exits
|
||||||
|
let west_idx = tile_idx_in_chunk(chunk_size, 0, x);
|
||||||
|
if new_chunk.pattern[west_idx] == TileType::Floor {
|
||||||
|
new_chunk.exits[2][x as usize] = true;
|
||||||
|
n_exits += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for east-bound exits
|
||||||
|
let east_idx = tile_idx_in_chunk(chunk_size, chunk_size - 1, x);
|
||||||
|
if new_chunk.pattern[east_idx] == TileType::Floor {
|
||||||
|
new_chunk.exits[3][x as usize] = true;
|
||||||
|
n_exits += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if n_exits == 0 {
|
||||||
|
new_chunk.has_exits = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
constraints.push(new_chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build compatibility matrix
|
||||||
|
let ch = constraints.clone();
|
||||||
|
for c in constraints.iter_mut() {
|
||||||
|
for (j, potential) in ch.iter().enumerate() {
|
||||||
|
// If there are no exits at all, it's compatible
|
||||||
|
if !c.has_exits || !potential.has_exits {
|
||||||
|
for compat in c.compatible_with.iter_mut() {
|
||||||
|
compat.push(j);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Evaluate compatibility by direction
|
||||||
|
for (direction, exit_list) in c.exits.iter_mut().enumerate() {
|
||||||
|
let opposite = match direction {
|
||||||
|
0 => 1, // Our North, Their South
|
||||||
|
1 => 0, // Our South, Their North
|
||||||
|
2 => 3, // Our West, Their East
|
||||||
|
_ => 2, // Our East, Their West
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut it_fits = false;
|
||||||
|
let mut has_any = false;
|
||||||
|
for (slot, can_enter) in exit_list.iter().enumerate() {
|
||||||
|
if *can_enter {
|
||||||
|
has_any = true;
|
||||||
|
if potential.exits[opposite][slot] {
|
||||||
|
it_fits = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if it_fits {
|
||||||
|
c.compatible_with[direction].push(j);
|
||||||
|
}
|
||||||
|
if !has_any {
|
||||||
|
// There's no exits on this side, we don't care what goes there
|
||||||
|
for compat in c.compatible_with.iter_mut() {
|
||||||
|
compat.push(j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constraints
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
use rltk::rex::XpFile;
|
|
||||||
use crate::{Map, TileType};
|
use crate::{Map, TileType};
|
||||||
|
use rltk::rex::XpFile;
|
||||||
|
|
||||||
/// Loads a RexPaint file, and converts it into our map format
|
/// Loads a RexPaint file, and converts it into our map format
|
||||||
pub fn load_rex_map(new_depth: i32, xp_file: &XpFile) -> Map {
|
pub fn load_rex_map(new_depth: i32, xp_file: &XpFile) -> Map {
|
||||||
|
0
src/map_builders/waveform_collapse/solver.rs
Normal file
0
src/map_builders/waveform_collapse/solver.rs
Normal file
Loading…
Reference in New Issue
Block a user