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
|
* [ ] Wave Function Collapse
|
||||||
* Map modifiers (filters)
|
* Map modifiers (filters)
|
||||||
* [ ] Area exit point
|
* [ ] Area exit point
|
||||||
* [ ] Area starting point
|
* [x] Area starting point
|
||||||
* [x] Cellular automata
|
* [x] Cellular automata
|
||||||
* [ ] Cull unreachable areas
|
* [ ] Cull unreachable areas
|
||||||
* [ ] Voronoi spawning
|
* [ ] Voronoi spawning
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "mapgen-demo"
|
name = "mapgen-demo"
|
||||||
version = "0.1.0"
|
version = "0.1.1"
|
||||||
authors = ["Krzysztof Langner <klangner@gmail.com>"]
|
authors = ["Krzysztof Langner <klangner@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use rand::prelude::*;
|
|
||||||
use amethyst::{
|
use amethyst::{
|
||||||
assets::{AssetStorage, Loader},
|
assets::{AssetStorage, Loader},
|
||||||
core::{
|
core::{
|
||||||
|
@ -14,6 +13,7 @@ use amethyst::{
|
||||||
sprite::{SpriteSheet, SpriteSheetFormat, SpriteSheetHandle},
|
sprite::{SpriteSheet, SpriteSheetFormat, SpriteSheetHandle},
|
||||||
types::DefaultBackend,
|
types::DefaultBackend,
|
||||||
RenderFlat2D, RenderToWindow, RenderingBundle, Texture,
|
RenderFlat2D, RenderToWindow, RenderingBundle, Texture,
|
||||||
|
palette::Srgba,
|
||||||
},
|
},
|
||||||
tiles::{MortonEncoder, RenderTiles2D, Tile, TileMap},
|
tiles::{MortonEncoder, RenderTiles2D, Tile, TileMap},
|
||||||
utils::application_root_dir,
|
utils::application_root_dir,
|
||||||
|
@ -21,9 +21,10 @@ use amethyst::{
|
||||||
winit,
|
winit,
|
||||||
};
|
};
|
||||||
use mapgen::dungeon::{
|
use mapgen::dungeon::{
|
||||||
MapGenerator,
|
MapBuilder,
|
||||||
map::{Map, TileType},
|
map::{Map, Point, TileType},
|
||||||
cellular_automata::CellularAutomataGen,
|
cellular_automata::CellularAutomataGen,
|
||||||
|
starting_point::{AreaStartingPosition, XStart, YStart},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -33,12 +34,25 @@ struct MapTiles ;
|
||||||
impl Tile for MapTiles {
|
impl Tile for MapTiles {
|
||||||
fn sprite(&self, p: Point3<u32>, world: &World) -> Option<usize> {
|
fn sprite(&self, p: Point3<u32>, world: &World) -> Option<usize> {
|
||||||
let map = world.read_resource::<Map>();
|
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)
|
Some(35)
|
||||||
} else {
|
} else {
|
||||||
Some(46)
|
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 {
|
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) {
|
fn init_map(world: &mut World) {
|
||||||
let gen = CellularAutomataGen::new(80, 50);
|
let map = MapBuilder::new(Box::new(CellularAutomataGen::new(80, 50)))
|
||||||
let mut rng = StdRng::seed_from_u64(0);
|
.with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER))
|
||||||
let map = gen.generate_map(&mut rng);
|
.build_map();
|
||||||
world.insert(map);
|
world.insert(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,6 @@
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
|
|
||||||
|
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use super::{MapGenerator, MapModifier};
|
use super::{MapGenerator, MapModifier};
|
||||||
use super::map::{Map, TileType};
|
use super::map::{Map, TileType};
|
||||||
|
|
|
@ -9,14 +9,22 @@
|
||||||
|
|
||||||
/// Position on the map
|
/// Position on the map
|
||||||
#[derive(PartialEq, Copy, Clone, Debug, Eq, Hash)]
|
#[derive(PartialEq, Copy, Clone, Debug, Eq, Hash)]
|
||||||
pub struct Position {
|
pub struct Point {
|
||||||
x: usize,
|
x: usize,
|
||||||
y: usize
|
y: usize
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Position {
|
impl Point {
|
||||||
pub fn new(x: usize, y: usize) -> Position {
|
/// Create new point
|
||||||
Position {x, y}
|
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 tiles : Vec<TileType>,
|
||||||
pub width : usize,
|
pub width : usize,
|
||||||
pub height : usize,
|
pub height : usize,
|
||||||
pub starting_point: Option<Position>,
|
pub starting_point: Option<Point>,
|
||||||
pub exit_point: Option<Position>
|
pub exit_point: Option<Point>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Map {
|
impl Map {
|
||||||
|
@ -70,6 +78,14 @@ impl Map {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
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]
|
#[test]
|
||||||
fn test_new_map() {
|
fn test_new_map() {
|
||||||
let map = Map::new(10, 10);
|
let map = Map::new(10, 10);
|
||||||
|
|
|
@ -6,10 +6,30 @@
|
||||||
//! * MapGenerators are use to create initial map.
|
//! * MapGenerators are use to create initial map.
|
||||||
//! * MapModifiers modify existing 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 map;
|
||||||
pub mod cellular_automata;
|
pub mod cellular_automata;
|
||||||
|
pub mod starting_point;
|
||||||
|
|
||||||
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use map::Map;
|
use map::Map;
|
||||||
|
|
||||||
|
@ -34,16 +54,19 @@ pub struct MapBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MapBuilder {
|
impl MapBuilder {
|
||||||
|
/// Create Map Builder with initial map generator
|
||||||
pub fn new(generator : Box<dyn MapGenerator>) -> MapBuilder {
|
pub fn new(generator : Box<dyn MapGenerator>) -> MapBuilder {
|
||||||
|
let system_time = SystemTime::now().duration_since(UNIX_EPOCH).expect("Can't access system time");
|
||||||
MapBuilder {
|
MapBuilder {
|
||||||
generator,
|
generator,
|
||||||
modifiers: Vec::new(),
|
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.modifiers.push(modifier);
|
||||||
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_map(&mut self) -> Map {
|
pub fn build_map(&mut self) -> Map {
|
||||||
|
@ -51,10 +74,31 @@ impl MapBuilder {
|
||||||
|
|
||||||
// Build additional layers in turn
|
// Build additional layers in turn
|
||||||
for modifier in self.modifiers.iter() {
|
for modifier in self.modifiers.iter() {
|
||||||
modifier.modify_map(&mut self.rng, &mut map);
|
map = modifier.modify_map(&mut self.rng, &map);
|
||||||
}
|
}
|
||||||
|
|
||||||
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