mirror of
https://github.com/edg-l/ddnet-map-gen.git
synced 2024-09-19 01:02:23 +00:00
more progress
This commit is contained in:
parent
8b1143de74
commit
ff49a772de
|
@ -1,7 +1,11 @@
|
|||
[package]
|
||||
name = "ddnet-map-gen"
|
||||
authors = ["Edgar <git@edgarluque.com>"]
|
||||
description = "A DDraceNetwork map generator."
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "AGPL-3-only"
|
||||
keywords = ["ddnet", "teeworlds", "mapgen"]
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
|
2
LICENSE
2
LICENSE
|
@ -630,7 +630,7 @@ state the exclusion of warranty; and each file should have at least
|
|||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
Copyright (C) 2022 Edgar Luque
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
|
|
46
src/cli.rs
46
src/cli.rs
|
@ -5,12 +5,38 @@ use std::path::Path;
|
|||
|
||||
pub fn run_cli() -> Result<()> {
|
||||
let matches = command!()
|
||||
.about("A DDraceNetwork map generator.")
|
||||
.about("A DDraceNetwork map generator")
|
||||
.arg_required_else_help(true)
|
||||
.subcommand_required(true)
|
||||
.arg(arg!(<FILE> "The output file").required(true))
|
||||
.subcommand(Command::new("maze").about("Generate a maze-like map."))
|
||||
.subcommand(Command::new("fly").about("Generate a map for fly techniques."))
|
||||
.arg(arg!(<FILE> "The output map file").required(true))
|
||||
.subcommand(
|
||||
Command::new("maze")
|
||||
.about("Generate a maze-like map")
|
||||
.arg(
|
||||
arg!(--width <WIDTH> "The width of the map")
|
||||
.default_value("1000")
|
||||
.required(false),
|
||||
)
|
||||
.arg(
|
||||
arg!(--height <HEIGHT> "The height of the map")
|
||||
.default_value("1000")
|
||||
.required(false),
|
||||
),
|
||||
)
|
||||
.subcommand(
|
||||
Command::new("fly")
|
||||
.about("Generate a map for fly techniques")
|
||||
.arg(
|
||||
arg!(--width <WIDTH> "The width of the map")
|
||||
.default_value("1000")
|
||||
.required(false),
|
||||
)
|
||||
.arg(
|
||||
arg!(--height <HEIGHT> "The height of the map")
|
||||
.default_value("1000")
|
||||
.required(false),
|
||||
),
|
||||
)
|
||||
.get_matches();
|
||||
|
||||
let output = matches.value_of("FILE").expect("output is required");
|
||||
|
@ -19,8 +45,16 @@ pub fn run_cli() -> Result<()> {
|
|||
let mut rng = rand::thread_rng();
|
||||
|
||||
match matches.subcommand() {
|
||||
Some(("maze", _sub_m)) => MazeGenerator::save_file(&mut rng, output)?,
|
||||
Some(("fly", _sub_m)) => FlyGenerator::save_file(&mut rng, output)?,
|
||||
Some(("maze", sub_m)) => {
|
||||
let width: usize = sub_m.value_of_t("width").unwrap_or_else(|e| e.exit());
|
||||
let height: usize = sub_m.value_of_t("height").unwrap_or_else(|e| e.exit());
|
||||
MazeGenerator::save_file(&mut rng, width, height, output)?
|
||||
}
|
||||
Some(("fly", sub_m)) => {
|
||||
let width: usize = sub_m.value_of_t("width").unwrap_or_else(|e| e.exit());
|
||||
let height: usize = sub_m.value_of_t("height").unwrap_or_else(|e| e.exit());
|
||||
FlyGenerator::save_file(&mut rng, width, height, output)?
|
||||
}
|
||||
_ => panic!("invalid command"),
|
||||
}
|
||||
|
||||
|
|
|
@ -6,52 +6,58 @@ use rand::Rng;
|
|||
pub struct FlyGenerator;
|
||||
|
||||
impl MapGenerator for FlyGenerator {
|
||||
fn generate<R: Rng + ?Sized>(rng: &mut R) -> Result<TwMap> {
|
||||
fn generate<R: Rng + ?Sized>(rng: &mut R, width: usize, height: usize) -> Result<TwMap> {
|
||||
let mut map = create_initial_map()?;
|
||||
|
||||
const HEIGHT: usize = 1000;
|
||||
const WIDTH: usize = 100;
|
||||
|
||||
let mut tiles = Array2::from_shape_simple_fn((HEIGHT, WIDTH), || {
|
||||
let mut tiles = Array2::from_shape_simple_fn((height, width), || {
|
||||
GameTile::new(TILE_EMPTY, TileFlags::empty())
|
||||
});
|
||||
let mut front_tiles = Array2::from_shape_simple_fn((HEIGHT, WIDTH), || {
|
||||
let mut front_tiles = Array2::from_shape_simple_fn((height, width), || {
|
||||
GameTile::new(TILE_EMPTY, TileFlags::empty())
|
||||
});
|
||||
|
||||
let mut unhookable_tiles =
|
||||
Array2::from_shape_simple_fn((HEIGHT, WIDTH), || Tile::new(0, TileFlags::empty()));
|
||||
Array2::from_shape_simple_fn((height, width), || Tile::new(0, TileFlags::empty()));
|
||||
let mut freeze_tiles =
|
||||
Array2::from_shape_simple_fn((HEIGHT, WIDTH), || Tile::new(0, TileFlags::empty()));
|
||||
Array2::from_shape_simple_fn((height, width), || Tile::new(0, TileFlags::empty()));
|
||||
|
||||
tiles
|
||||
.row_mut(HEIGHT - 2)
|
||||
.row_mut(height - 2)
|
||||
.iter_mut()
|
||||
.for_each(|tile| tile.id = TILE_UNHOOKABLE);
|
||||
unhookable_tiles
|
||||
.row_mut(HEIGHT - 2)
|
||||
.row_mut(height - 2)
|
||||
.iter_mut()
|
||||
.for_each(|tile| tile.id = 1);
|
||||
.for_each(|tile| tile.id = 2);
|
||||
|
||||
tiles[(HEIGHT - 3, WIDTH / 2)].id = TILE_SPAWN;
|
||||
tiles[(height - 3, width / 2)].id = TILE_SPAWN;
|
||||
|
||||
for x in 0..WIDTH {
|
||||
front_tiles[(HEIGHT - 6, x)].id = TILE_START;
|
||||
for x in 0..width {
|
||||
front_tiles[(height - 6, x)].id = TILE_START;
|
||||
front_tiles[(10, x)].id = TILE_FINISH;
|
||||
}
|
||||
|
||||
let mut center: i64 = WIDTH as i64 / 2;
|
||||
let mut center: i64 = width as i64 / 2;
|
||||
let mut fly_width: i64 = 10;
|
||||
|
||||
for y in (0..=(HEIGHT - 3)).rev() {
|
||||
let direction: i64 = rng.gen_range(-1..=1);
|
||||
let max_steps = (height as f64 * 0.30f64).round() as i64;
|
||||
|
||||
let mut direction_steps: i64 = rng.gen_range((max_steps / 2)..=max_steps);
|
||||
let mut direction: i64 = rng.gen_range(-1..=1);
|
||||
|
||||
for y in (0..=(height - 3)).rev() {
|
||||
if direction_steps == 0 {
|
||||
direction_steps = rng.gen_range(1..=10);
|
||||
direction = rng.gen_range(-1..=1);
|
||||
}
|
||||
|
||||
let width_change: i64 = rng.gen_range(-1..=1);
|
||||
center += direction;
|
||||
fly_width += width_change;
|
||||
center = center.clamp(fly_width, WIDTH as i64 - fly_width);
|
||||
fly_width = fly_width.clamp(2, 12);
|
||||
fly_width = fly_width.clamp(3, 12);
|
||||
center = center.clamp(fly_width, width as i64 - fly_width - 1);
|
||||
|
||||
for x in ((center + fly_width) as usize)..WIDTH {
|
||||
for x in ((center + fly_width) as usize)..width {
|
||||
tiles[(y, x)].id = TILE_FREEZE;
|
||||
freeze_tiles[(y, x)].id = 4;
|
||||
}
|
||||
|
@ -60,6 +66,8 @@ impl MapGenerator for FlyGenerator {
|
|||
tiles[(y, x)].id = TILE_FREEZE;
|
||||
freeze_tiles[(y, x)].id = 4;
|
||||
}
|
||||
|
||||
direction_steps -= 1;
|
||||
}
|
||||
|
||||
let game_layer = GameLayer {
|
||||
|
@ -70,11 +78,11 @@ impl MapGenerator for FlyGenerator {
|
|||
tiles: CompressedData::Loaded(front_tiles),
|
||||
};
|
||||
|
||||
let mut unhook_tiles_layer = TilesLayer::new((HEIGHT, WIDTH));
|
||||
let mut unhook_tiles_layer = TilesLayer::new((height, width));
|
||||
unhook_tiles_layer.image = Some(0);
|
||||
unhook_tiles_layer.tiles = CompressedData::Loaded(unhookable_tiles);
|
||||
|
||||
let mut freeze_tiles_layer = TilesLayer::new((HEIGHT, WIDTH));
|
||||
let mut freeze_tiles_layer = TilesLayer::new((height, width));
|
||||
freeze_tiles_layer.image = Some(1);
|
||||
freeze_tiles_layer.tiles = CompressedData::Loaded(freeze_tiles);
|
||||
freeze_tiles_layer.color = Color {
|
||||
|
|
|
@ -6,33 +6,55 @@ use ndarray::Array2;
|
|||
pub struct MazeGenerator;
|
||||
|
||||
impl MapGenerator for MazeGenerator {
|
||||
fn generate<R: Rng + ?Sized>(rng: &mut R) -> Result<TwMap> {
|
||||
let mut map = create_initial_map()?;
|
||||
let maze = Maze::new(1001, 1001).unwrap().generate(rng);
|
||||
fn generate<R: Rng + ?Sized>(rng: &mut R, width: usize, height: usize) -> Result<TwMap> {
|
||||
// Must be odd.
|
||||
let width = {
|
||||
if width % 2 == 0 {
|
||||
width + 1
|
||||
} else {
|
||||
width
|
||||
}
|
||||
};
|
||||
let height = {
|
||||
if height % 2 == 0 {
|
||||
height + 1
|
||||
} else {
|
||||
height
|
||||
}
|
||||
};
|
||||
|
||||
let mut tiles = Array2::from_shape_fn((1001, 1001), |(x, y)| {
|
||||
let mut map = create_initial_map()?;
|
||||
let maze = Maze::new(width, height).unwrap().generate(rng);
|
||||
|
||||
let hookable_tiles =
|
||||
Array2::from_shape_fn((height, width), |(y, x)| {
|
||||
let mut t = 0;
|
||||
if maze[x][y] == 1 {
|
||||
t = 9;
|
||||
}
|
||||
Tile::new(t, TileFlags::empty())
|
||||
});
|
||||
|
||||
let mut tiles = Array2::from_shape_fn((width, height), |(y, x)| {
|
||||
GameTile::new(maze[x][y], TileFlags::empty())
|
||||
});
|
||||
|
||||
// Put spawn and start tile on top left most tile.
|
||||
let mut added_spawn = false;
|
||||
'outerStart: for y in 0..101 {
|
||||
for x in 0..101 {
|
||||
let tile = &mut tiles[(x, y)];
|
||||
if tile.id == 0 && !added_spawn {
|
||||
*tile = GameTile::new(TILE_SPAWN, TileFlags::empty());
|
||||
added_spawn = true;
|
||||
} else if tile.id == 0 && added_spawn {
|
||||
*tile = GameTile::new(TILE_START, TileFlags::empty());
|
||||
'outerStart: for y in 0..height {
|
||||
for x in 0..width {
|
||||
let tile = &mut tiles[(y, x)];
|
||||
if tile.id == 0 {
|
||||
tile.id = TILE_SPAWN;
|
||||
replace_around_gametile(&mut tiles, x, y, TILE_EMPTY, TILE_START);
|
||||
break 'outerStart;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Put finish tile on bottom right most tile.
|
||||
'outerFinish: for y in (0..1001).rev() {
|
||||
for x in (0..1001).rev() {
|
||||
let tile = &mut tiles[(x, y)];
|
||||
'outerFinish: for y in (0..height).rev() {
|
||||
for x in (0..width).rev() {
|
||||
let tile = &mut tiles[(y, x)];
|
||||
if tile.id == 0 {
|
||||
*tile = GameTile::new(TILE_FINISH, TileFlags::empty());
|
||||
break 'outerFinish;
|
||||
|
@ -40,12 +62,19 @@ impl MapGenerator for MazeGenerator {
|
|||
}
|
||||
}
|
||||
|
||||
let mut hook_tiles_layer = TilesLayer::new((height, width));
|
||||
hook_tiles_layer.image = Some(0);
|
||||
hook_tiles_layer.tiles = CompressedData::Loaded(hookable_tiles);
|
||||
|
||||
let game_layer = GameLayer {
|
||||
tiles: CompressedData::Loaded(tiles),
|
||||
};
|
||||
|
||||
let mut physics = Group::physics();
|
||||
physics.layers.push(Layer::Game(game_layer));
|
||||
physics.layers.push(Layer::Tiles(hook_tiles_layer));
|
||||
|
||||
map.groups.push(quads_sky());
|
||||
map.groups.push(physics);
|
||||
|
||||
Ok(map)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use std::path::Path;
|
||||
|
||||
use eyre::Result;
|
||||
use ndarray::{Array2};
|
||||
use rand::Rng;
|
||||
use twmap::*;
|
||||
|
||||
|
@ -17,10 +18,10 @@ pub const TILE_FINISH: u8 = 34;
|
|||
pub const TILE_SPAWN: u8 = 192;
|
||||
|
||||
pub trait MapGenerator {
|
||||
fn generate<R: Rng + ?Sized>(rng: &mut R) -> Result<TwMap>;
|
||||
fn generate<R: Rng + ?Sized>(rng: &mut R, width: usize, height: usize) -> Result<TwMap>;
|
||||
|
||||
fn save_file<R: Rng + ?Sized>(rng: &mut R, path: &Path) -> Result<()> {
|
||||
let mut map = Self::generate(rng)?;
|
||||
fn save_file<R: Rng + ?Sized>(rng: &mut R, width: usize, height: usize, path: &Path) -> Result<()> {
|
||||
let mut map = Self::generate(rng, width, height)?;
|
||||
map.save_file(path)?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -81,3 +82,32 @@ pub fn quads_sky() -> Group {
|
|||
quads_group.layers.push(Layer::Quads(quads_layer));
|
||||
quads_group
|
||||
}
|
||||
|
||||
// Changed the id of the tile if matches oldid.
|
||||
pub fn replace_gametile(tiles: &mut Array2<GameTile>, x: usize, y: usize, oldid: u8, newid: u8) {
|
||||
if tiles[(y, x)].id == oldid {
|
||||
tiles[(y, x)].id = newid;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn replace_around_gametile(tiles: &mut Array2<GameTile>, x: usize, y: usize, oldid: u8, newid: u8) {
|
||||
let width = tiles.ncols();
|
||||
let height = tiles.nrows();
|
||||
|
||||
let directions = [-1, 0, 1];
|
||||
|
||||
for diry in directions {
|
||||
for dirx in directions {
|
||||
if dirx == 0 && diry == 0 {
|
||||
continue;
|
||||
}
|
||||
if (y as i64) + diry < 0 || (y as i64) + diry >= height as i64 {
|
||||
continue;
|
||||
}
|
||||
if (x as i64) + dirx < 0 || (x as i64) + dirx >= width as i64 {
|
||||
continue;
|
||||
}
|
||||
replace_gametile(tiles, ((x as i64) + dirx) as usize, ((y as i64) + diry) as usize, oldid, newid);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue