Added staring position modifier
This commit is contained in:
parent
51e0d585df
commit
31e503faac
|
@ -34,7 +34,7 @@ If you want to check how the maps look like, then:
|
|||
* [ ] Wave Function Collapse
|
||||
* Map modifiers (filters)
|
||||
* [ ] Area exit point
|
||||
* [ ] Area starting point
|
||||
* [x] Area starting point
|
||||
* [x] Cellular automata
|
||||
* [ ] Cull unreachable areas
|
||||
* [ ] Voronoi spawning
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "mapgen-demo"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
authors = ["Krzysztof Langner <klangner@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
use rand::prelude::*;
|
||||
use amethyst::{
|
||||
assets::{AssetStorage, Loader},
|
||||
core::{
|
||||
|
@ -14,6 +13,7 @@ use amethyst::{
|
|||
sprite::{SpriteSheet, SpriteSheetFormat, SpriteSheetHandle},
|
||||
types::DefaultBackend,
|
||||
RenderFlat2D, RenderToWindow, RenderingBundle, Texture,
|
||||
palette::Srgba,
|
||||
},
|
||||
tiles::{MortonEncoder, RenderTiles2D, Tile, TileMap},
|
||||
utils::application_root_dir,
|
||||
|
@ -21,9 +21,10 @@ use amethyst::{
|
|||
winit,
|
||||
};
|
||||
use mapgen::dungeon::{
|
||||
MapGenerator,
|
||||
map::{Map, TileType},
|
||||
MapBuilder,
|
||||
map::{Map, Point, TileType},
|
||||
cellular_automata::CellularAutomataGen,
|
||||
starting_point::{AreaStartingPosition, XStart, YStart},
|
||||
};
|
||||
|
||||
|
||||
|
@ -33,12 +34,25 @@ struct MapTiles ;
|
|||
impl Tile for MapTiles {
|
||||
fn sprite(&self, p: Point3<u32>, world: &World) -> Option<usize> {
|
||||
let map = world.read_resource::<Map>();
|
||||
if map.at(p.x as usize, p.y as usize) == TileType::Wall {
|
||||
let player_pos = Point::new(p.x as usize, p.y as usize);
|
||||
if map.starting_point == Some(player_pos) {
|
||||
Some(64)
|
||||
} else if map.at(p.x as usize, p.y as usize) == TileType::Wall {
|
||||
Some(35)
|
||||
} else {
|
||||
Some(46)
|
||||
}
|
||||
}
|
||||
|
||||
fn tint(&self, p: Point3<u32>, world: &World) -> Srgba {
|
||||
let map = world.read_resource::<Map>();
|
||||
let player_pos = Point::new(p.x as usize, p.y as usize);
|
||||
if map.starting_point == Some(player_pos) {
|
||||
Srgba::new(1.0, 1.0, 0.0, 1.0)
|
||||
} else {
|
||||
Srgba::new(1.0, 1.0, 1.0, 1.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn load_tiles_sprite_sheet(world: &mut World, png_path: &str, ron_path: &str) -> SpriteSheetHandle {
|
||||
|
@ -67,9 +81,9 @@ fn init_camera(world: &mut World, transform: Transform, camera: Camera) -> Entit
|
|||
}
|
||||
|
||||
fn init_map(world: &mut World) {
|
||||
let gen = CellularAutomataGen::new(80, 50);
|
||||
let mut rng = StdRng::seed_from_u64(0);
|
||||
let map = gen.generate_map(&mut rng);
|
||||
let map = MapBuilder::new(Box::new(CellularAutomataGen::new(80, 50)))
|
||||
.with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER))
|
||||
.build_map();
|
||||
world.insert(map);
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
//! ```
|
||||
//!
|
||||
|
||||
|
||||
use rand::prelude::*;
|
||||
use super::{MapGenerator, MapModifier};
|
||||
use super::map::{Map, TileType};
|
||||
|
|
|
@ -9,14 +9,22 @@
|
|||
|
||||
/// Position on the map
|
||||
#[derive(PartialEq, Copy, Clone, Debug, Eq, Hash)]
|
||||
pub struct Position {
|
||||
pub struct Point {
|
||||
x: usize,
|
||||
y: usize
|
||||
}
|
||||
|
||||
impl Position {
|
||||
pub fn new(x: usize, y: usize) -> Position {
|
||||
Position {x, y}
|
||||
impl Point {
|
||||
/// Create new point
|
||||
pub fn new(x: usize, y: usize) -> Point {
|
||||
Point {x, y}
|
||||
}
|
||||
|
||||
/// Euclidean distance to a given point
|
||||
pub fn distance_to(self, point: &Point) -> f32 {
|
||||
let a = (self.x as f32 - point.x as f32).powf(2.0);
|
||||
let b = (self.y as f32 - point.y as f32).powf(2.0);
|
||||
(a + b).sqrt()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,8 +40,8 @@ pub struct Map {
|
|||
pub tiles : Vec<TileType>,
|
||||
pub width : usize,
|
||||
pub height : usize,
|
||||
pub starting_point: Option<Position>,
|
||||
pub exit_point: Option<Position>
|
||||
pub starting_point: Option<Point>,
|
||||
pub exit_point: Option<Point>
|
||||
}
|
||||
|
||||
impl Map {
|
||||
|
@ -70,6 +78,14 @@ impl Map {
|
|||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_distance() {
|
||||
let p1 = Point::new(10, 10);
|
||||
let p2 = Point::new(14, 7);
|
||||
let distance = p1.distance_to(&p2);
|
||||
assert_eq!(distance, 5.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new_map() {
|
||||
let map = Map::new(10, 10);
|
||||
|
|
|
@ -6,10 +6,30 @@
|
|||
//! * MapGenerators are use to create initial map.
|
||||
//! * MapModifiers modify existing map.
|
||||
//!
|
||||
//! Example
|
||||
//! ```
|
||||
//! use mapgen::dungeon::{
|
||||
//! MapBuilder,
|
||||
//! map::{Map, Point, TileType},
|
||||
//! cellular_automata::CellularAutomataGen,
|
||||
//! starting_point::{AreaStartingPosition, XStart, YStart},
|
||||
//! };
|
||||
//!
|
||||
//! let map = MapBuilder::new(Box::new(CellularAutomataGen::new(80, 50)))
|
||||
//! .with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER))
|
||||
//! .build_map();
|
||||
//!
|
||||
//! assert_eq!(map.width, 80);
|
||||
//! assert_eq!(map.height, 50);
|
||||
//! assert_eq!(map.starting_point.is_some(), true);
|
||||
//! ```
|
||||
//!
|
||||
|
||||
pub mod map;
|
||||
pub mod cellular_automata;
|
||||
pub mod starting_point;
|
||||
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use rand::prelude::*;
|
||||
use map::Map;
|
||||
|
||||
|
@ -34,16 +54,19 @@ pub struct MapBuilder {
|
|||
}
|
||||
|
||||
impl MapBuilder {
|
||||
/// Create Map Builder with initial map generator
|
||||
pub fn new(generator : Box<dyn MapGenerator>) -> MapBuilder {
|
||||
let system_time = SystemTime::now().duration_since(UNIX_EPOCH).expect("Can't access system time");
|
||||
MapBuilder {
|
||||
generator,
|
||||
modifiers: Vec::new(),
|
||||
rng: StdRng::seed_from_u64(0)
|
||||
rng: StdRng::seed_from_u64(system_time.as_secs())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with(&mut self, modifier : Box<dyn MapModifier>) {
|
||||
pub fn with(&mut self, modifier : Box<dyn MapModifier>) -> &mut MapBuilder {
|
||||
self.modifiers.push(modifier);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build_map(&mut self) -> Map {
|
||||
|
@ -51,10 +74,31 @@ impl MapBuilder {
|
|||
|
||||
// Build additional layers in turn
|
||||
for modifier in self.modifiers.iter() {
|
||||
modifier.modify_map(&mut self.rng, &mut map);
|
||||
map = modifier.modify_map(&mut self.rng, &map);
|
||||
}
|
||||
|
||||
map
|
||||
}
|
||||
}
|
||||
|
||||
/// ------------------------------------------------------------------------------------------------
|
||||
/// Module unit tests
|
||||
/// ------------------------------------------------------------------------------------------------
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use cellular_automata::CellularAutomataGen;
|
||||
use starting_point::{AreaStartingPosition, XStart, YStart};
|
||||
|
||||
#[test]
|
||||
fn test_ca_map() {
|
||||
let map = MapBuilder::new(Box::new(CellularAutomataGen::new(80, 50)))
|
||||
.with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER))
|
||||
.build_map();
|
||||
|
||||
assert_eq!(map.width, 80);
|
||||
assert_eq!(map.height, 50);
|
||||
assert_eq!(map.starting_point.is_some(), true);
|
||||
}
|
||||
|
||||
}
|
97
src/dungeon/starting_point.rs
Normal file
97
src/dungeon/starting_point.rs
Normal file
|
@ -0,0 +1,97 @@
|
|||
//! Add starting point to the map
|
||||
//!
|
||||
//! This modifier will try to add starting point by finding the floor title closes
|
||||
//! to the given point.
|
||||
//!
|
||||
//! Example generator usage:
|
||||
//! ```
|
||||
//! use rand::prelude::*;
|
||||
//! use mapgen::dungeon::{
|
||||
//! MapModifier,
|
||||
//! map::{Map, Point, TileType},
|
||||
//! starting_point::{AreaStartingPosition, XStart, YStart}
|
||||
//! };
|
||||
//!
|
||||
//! let mut rng = StdRng::seed_from_u64(100);
|
||||
//! let mut map = Map::new(80, 50);
|
||||
//! map.set_tile(10, 10, TileType::Floor);
|
||||
//! let modifier = AreaStartingPosition::new(XStart::LEFT, YStart::TOP);
|
||||
//! let new_map = modifier.modify_map(&mut rng, &map);
|
||||
//!
|
||||
//! assert_eq!(new_map.starting_point, Some(Point::new(10, 10)));
|
||||
//! ```
|
||||
//!
|
||||
|
||||
use rand::prelude::StdRng;
|
||||
use super::{MapModifier};
|
||||
use super::map::{Map, Point, TileType};
|
||||
|
||||
|
||||
/// Initial x region position
|
||||
pub enum XStart { LEFT, CENTER, RIGHT }
|
||||
|
||||
/// Initial y region position
|
||||
pub enum YStart { TOP, CENTER, BOTTOM }
|
||||
|
||||
/// Add starting position to the map
|
||||
pub struct AreaStartingPosition {
|
||||
x : XStart,
|
||||
y : YStart
|
||||
}
|
||||
|
||||
impl MapModifier for AreaStartingPosition {
|
||||
fn modify_map(&self, _: &mut StdRng, map: &Map) -> Map {
|
||||
self.build(map)
|
||||
}
|
||||
}
|
||||
|
||||
impl AreaStartingPosition {
|
||||
/// Create new modifier with given region
|
||||
pub fn new(x : XStart, y : YStart) -> Box<AreaStartingPosition> {
|
||||
Box::new(AreaStartingPosition{
|
||||
x, y
|
||||
})
|
||||
}
|
||||
|
||||
fn build(&self, map : &Map) -> Map {
|
||||
let seed_x;
|
||||
let seed_y;
|
||||
|
||||
match self.x {
|
||||
XStart::LEFT => seed_x = 1,
|
||||
XStart::CENTER => seed_x = map.width / 2,
|
||||
XStart::RIGHT => seed_x = map.width - 2
|
||||
}
|
||||
|
||||
match self.y {
|
||||
YStart::TOP => seed_y = 1,
|
||||
YStart::CENTER => seed_y = map.height / 2,
|
||||
YStart::BOTTOM => seed_y = map.height - 2
|
||||
}
|
||||
|
||||
let mut available_floors : Vec<(usize, f32)> = Vec::new();
|
||||
for (idx, tiletype) in map.tiles.iter().enumerate() {
|
||||
if *tiletype == TileType::Floor {
|
||||
available_floors.push(
|
||||
(
|
||||
idx,
|
||||
Point::new(idx % map.width, idx / map.width)
|
||||
.distance_to(&Point::new(seed_x, seed_y))
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
if available_floors.is_empty() {
|
||||
panic!("No valid floors to start on");
|
||||
}
|
||||
|
||||
available_floors.sort_by(|a,b| a.1.partial_cmp(&b.1).unwrap());
|
||||
|
||||
let start_x = available_floors[0].0 % map.width;
|
||||
let start_y = available_floors[0].0 / map.width;
|
||||
|
||||
let mut new_map = map.clone();
|
||||
new_map.starting_point = Some(Point::new(start_x, start_y));
|
||||
new_map
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user