summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.toml1
-rw-r--r--src/bin/main.rs39
-rw-r--r--src/bin/web.rs73
-rw-r--r--src/canvas.rs61
-rw-r--r--src/lib.rs (renamed from src/main.rs)139
-rw-r--r--static/index.html25
6 files changed, 257 insertions, 81 deletions
diff --git a/Cargo.toml b/Cargo.toml
index a88fc23..008d7d0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,3 +6,4 @@ edition = "2018"
[dependencies]
rand = {version = "0.7.0", features = ["rand_pcg", "small_rng"]}
+stdweb = "0.4.18"
diff --git a/src/bin/main.rs b/src/bin/main.rs
new file mode 100644
index 0000000..0806dfd
--- /dev/null
+++ b/src/bin/main.rs
@@ -0,0 +1,39 @@
+use darkcloud2_levelgen::{ Tile, Level, TileTemplate, Region, VecRegion, place_template,
+connect_rooms, create_deadend, tunnel_branches, pretty_print, random_dungeon, create_templates};
+
+use rand::{Rng, SeedableRng};
+use rand::rngs::SmallRng;
+use rand::thread_rng;
+
+fn main() {
+ let mut thread_rng = thread_rng();
+ let mut rng = SmallRng::from_seed([14;16]);
+
+ let templates = create_templates();
+ let template = &templates[0];
+
+ let mut level: VecRegion<Option<Tile>> = Region::new_with(15, 11);
+ let mut level = Level::new(Box::new(level));
+ let res = place_template(&mut level, template, 0, 0);
+ let res = place_template(&mut level, template, 3, 7);
+ let res = place_template(&mut level, template, 5, 2);
+ let res = place_template(&mut level, template, 8, 7);
+ let res = connect_rooms(&mut level, 0, 1, &mut rng);
+ let res = connect_rooms(&mut level, 2, 1, &mut rng);
+ let res = connect_rooms(&mut level, 3, 2, &mut rng);
+ create_deadend(&mut level, &mut rng);
+ tunnel_branches(&mut level, &mut rng);
+ tunnel_branches(&mut level, &mut rng);
+ tunnel_branches(&mut level, &mut rng);
+ println!("{:?}", res);
+ pretty_print(&level);
+
+
+ let templates = create_templates();
+ for _ in 0..10 {
+ let l = random_dungeon(&templates, &mut rng);
+ if l.is_ok() {
+ pretty_print(&l.unwrap());
+ }
+ }
+}
diff --git a/src/bin/web.rs b/src/bin/web.rs
new file mode 100644
index 0000000..166e3b2
--- /dev/null
+++ b/src/bin/web.rs
@@ -0,0 +1,73 @@
+#[macro_use]
+extern crate stdweb;
+extern crate darkcloud2_levelgen;
+
+use rand::{Rng, SeedableRng};
+use rand::rngs::SmallRng;
+
+use darkcloud2_levelgen::canvas::render_level;
+use darkcloud2_levelgen::{random_dungeon, create_templates};
+
+use stdweb::traits::*;
+use stdweb::unstable::TryInto;
+use stdweb::web::{
+ document,
+ window,
+ CanvasRenderingContext2d
+};
+
+use stdweb::web::event::{
+ MouseMoveEvent,
+ ResizeEvent,
+ ClickEvent,
+};
+
+use stdweb::web::html_element::CanvasElement;
+
+macro_rules! enclose {
+ ( ($( $x:ident ),*) $y:expr ) => {
+ {
+ $(let $x = $x.clone();)*
+ $y
+ }
+ };
+}
+
+static mut CANVAS: Option<CanvasRenderingContext2d> = None;
+static mut RNG: Option<SmallRng> = None;
+
+fn draw_new_level() {
+ unsafe {
+ let mut canvas = CANVAS.as_mut().unwrap();
+ let mut rng = RNG.as_mut().unwrap();
+ canvas.clear_rect(0.0, 0.0, 1000.0, 1000.0);
+ let templates = create_templates();
+ let mut l = random_dungeon(&templates, &mut rng);
+ while l.is_err() {
+ l = random_dungeon(&templates, &mut rng);
+
+ }
+ let level = l.unwrap();
+ render_level(&mut canvas, &level);
+ }
+}
+
+fn main() {
+ stdweb::initialize();
+
+ let mut rng = SmallRng::from_seed([14;16]);
+ unsafe { RNG = Some(rng); }
+
+ let canvas: CanvasElement = document().query_selector( "#canvas" ).unwrap().unwrap().try_into().unwrap();
+ let mut context: CanvasRenderingContext2d = canvas.get_context().unwrap();
+ unsafe { CANVAS = Some(context); }
+ draw_new_level();
+
+ let new = document().query_selector("#new").unwrap().unwrap();
+
+ new.add_event_listener(|_: ClickEvent| {
+ draw_new_level()
+ });
+
+ stdweb::event_loop();
+}
diff --git a/src/canvas.rs b/src/canvas.rs
new file mode 100644
index 0000000..ead8636
--- /dev/null
+++ b/src/canvas.rs
@@ -0,0 +1,61 @@
+use stdweb::web::CanvasRenderingContext2d;
+use crate::{Level, Tile};
+
+use crate::Direction;
+
+const SCALE_X: f64 = 8.0;
+const SCALE_Y: f64 = 4.0;
+
+pub fn render_level(canvas: &mut CanvasRenderingContext2d, level: &Level) {
+ let region = &level.region;
+
+ let x_offset = 20.0;
+ let y_offset = 20.0;
+
+
+ for y in 0..region.height() {
+ for x in 0..region.width() {
+ let old_width = canvas.get_line_width();
+ canvas.set_line_width(2.0);
+ canvas.set_fill_style_color("lightgrey");
+ let x_f = x as f64;
+ let y_f = y as f64;
+
+ let x_l = x_offset + (x_f * SCALE_X);
+ let x_r = x_offset + (x_f + 1.0) * SCALE_X;
+
+ let y_t = y_offset + (y_f * SCALE_Y);
+ let y_b = y_offset + (y_f + 1.0) * SCALE_Y;
+
+
+ let tile = region.get(x, y).unwrap();
+ if let Some(tile) = tile {
+ canvas.fill_rect(x_l, y_t, x_r - x_l, y_b - y_t);
+ if tile.connections & Direction::UP == 0 {
+ canvas.begin_path();
+ canvas.move_to(x_l, y_t);
+ canvas.line_to(x_r, y_t);
+ canvas.stroke();
+ }
+ if tile.connections & Direction::RIGHT == 0 {
+ canvas.begin_path();
+ canvas.move_to(x_r, y_t);
+ canvas.line_to(x_r, y_b);
+ canvas.stroke();
+ }
+ if tile.connections & Direction::DOWN == 0 {
+ canvas.begin_path();
+ canvas.move_to(x_l, y_b);
+ canvas.line_to(x_r, y_b);
+ canvas.stroke();
+ }
+ if tile.connections & Direction::LEFT == 0 {
+ canvas.begin_path();
+ canvas.move_to(x_l, y_t);
+ canvas.line_to(x_l, y_b);
+ canvas.stroke();
+ }
+ }
+ }
+ }
+}
diff --git a/src/main.rs b/src/lib.rs
index fe32d14..54d5fa3 100644
--- a/src/main.rs
+++ b/src/lib.rs
@@ -1,17 +1,14 @@
+#[macro_use]
+extern crate stdweb;
+
use rand::{Rng, SeedableRng};
-use rand::thread_rng;
-use rand::rngs::SmallRng;
+pub mod canvas;
type RoomId = u32;
-enum LevelBuilderError {
- OutOfBounds,
- InvalidRoomId,
-}
-
#[derive(Debug)]
-enum Error {
+pub enum Error {
OutOfBounds,
MaximumIterations,
NonEmpty,
@@ -19,7 +16,7 @@ enum Error {
}
-mod Direction {
+pub mod Direction {
pub const UP: u8 = 1 << 0;
pub const RIGHT: u8 = 1 << 1;
pub const DOWN: u8 = 1 << 2;
@@ -55,15 +52,13 @@ mod Direction {
}
-struct RoomDescription {
+pub struct RoomDescription {
width: u32,
height: u32,
data: Vec<u32>,
}
-struct LevelBuilder { }
-
-trait Region<T> {
+pub trait Region<T> {
fn width(&self) -> u32;
fn height(&self) -> u32;
fn set(&mut self, x: u32, y: u32, value: T) -> Result<(), Error>;
@@ -71,7 +66,7 @@ trait Region<T> {
fn get_mut(&mut self, x: u32, y: u32) -> Result<&mut T, Error>;
}
-struct VecRegion<T> {
+pub struct VecRegion<T> {
inner: Vec<T>,
width: u32,
height: u32,
@@ -113,7 +108,7 @@ impl<T> Region<T> for VecRegion<T> {
}
impl<T: Default> Region<T> {
- fn new_with(width: u32, height: u32) -> VecRegion<T> {
+ pub fn new_with(width: u32, height: u32) -> VecRegion<T> {
let size = (width * height) as usize;
let mut inner = Vec::with_capacity(size);
inner.resize_with(size, T::default);
@@ -129,15 +124,15 @@ impl<T: Default> Region<T> {
const TILE_IS_PATH: u8 = 0x4;
#[derive(Clone, Debug)]
-struct Tile {
+pub struct Tile {
owner: Owner,
flags: u8,
connections: u8,
}
#[derive(Clone)]
-struct TileTemplate {
- connections: u8
+pub struct TileTemplate {
+ pub connections: u8
}
#[derive(Eq, PartialEq, Clone, Debug, Copy)]
@@ -166,13 +161,23 @@ struct Room {
height: u32,
}
-struct Level {
+pub struct Level {
region: Box<dyn Region<Option<Tile>>>,
rooms: Vec<Room>,
tunnels: u32,
}
-fn place_template(level: &mut Level, template: &RoomTemplate, x: u32, y: u32) -> Result<RoomId, Error> {
+impl Level {
+ pub fn new(region: Box<dyn Region<Option<Tile>>>) -> Level {
+ Level {
+ region: region,
+ rooms: Vec::new(),
+ tunnels: 0
+ }
+ }
+}
+
+pub fn place_template(level: &mut Level, template: &RoomTemplate, x: u32, y: u32) -> Result<RoomId, Error> {
let region = &mut level.region;
let width = region.width();
let height = region.height();
@@ -235,6 +240,8 @@ fn tunnel<R: Rng + ?Sized>(level: &mut Level, from: (u32, u32), to: (u32, u32),
let tile = level.region.get_mut(from.0, from.1)?;
if let Some(tile) = tile {
tile.connections = tile.connections | connection;
+ } else {
+ level.region.set(from.0, from.1, Some(Tile { owner: owner, flags: TILE_IS_PATH, connections: direction}));
}
from = Direction::step_towards(direction, from).unwrap();
@@ -258,7 +265,7 @@ fn tunnel<R: Rng + ?Sized>(level: &mut Level, from: (u32, u32), to: (u32, u32),
Ok(())
}
-fn connect_rooms<R: Rng + ?Sized>(level: &mut Level, from_id: RoomId, to_id: RoomId, rng: &mut R) -> Result<(), Error> {
+pub fn connect_rooms<R: Rng + ?Sized>(level: &mut Level, from_id: RoomId, to_id: RoomId, rng: &mut R) -> Result<(), Error> {
let mut candidates = Vec::new();
let from = level.rooms.get(from_id as usize).ok_or(Error::InvalidRoomId)?;
@@ -329,7 +336,7 @@ fn connect_rooms<R: Rng + ?Sized>(level: &mut Level, from_id: RoomId, to_id: Roo
tunnel(level, pos, to_center, Owner::Room(from_id), rng)
}
-fn create_deadend<R: Rng + ?Sized>(level: &mut Level, rng: &mut R) -> Result<(), Error> {
+pub fn create_deadend<R: Rng + ?Sized>(level: &mut Level, rng: &mut R) -> Result<(), Error> {
let mut attempts = 0;
loop {
let x = rng.gen_range(0, level.region.width() - 3);
@@ -349,10 +356,11 @@ fn create_deadend<R: Rng + ?Sized>(level: &mut Level, rng: &mut R) -> Result<(),
if attempts > 128 {break;}
if empty {
+ let owner = Owner::Tunnel(level.tunnels);
let rnd_index = rng.gen_range(0, level.rooms.len());
let room = &mut level.rooms[rnd_index];
let room_center: (u32, u32) = ((room.x + (room.width/2)), (room.y + (room.height/2)));
- let res = tunnel(level, (x,y), room_center, Owner::Tunnel(level.tunnels), rng);
+ let res = tunnel(level, (x,y), room_center, owner , rng);
level.tunnels += 1;
return res;
}
@@ -384,7 +392,7 @@ fn empty_neighbours(level: &Level, x: u32, y:u32, options: &mut [u8; 4], neighbo
}
}
-fn random_walk<R: Rng + ?Sized>(level: &mut Level, rng: &mut R) -> Result<(), Error> {
+pub fn tunnel_branches<R: Rng + ?Sized>(level: &mut Level, rng: &mut R) -> Result<(), Error> {
let mut iterations = 0;
const max_iterations: u32 = 128;
@@ -464,7 +472,7 @@ fn random_walk<R: Rng + ?Sized>(level: &mut Level, rng: &mut R) -> Result<(), Er
Ok(())
}
-fn pretty_print(level: &Level) {
+pub fn pretty_print(level: &Level) {
let region = &level.region;
@@ -487,7 +495,26 @@ fn pretty_print(level: &Level) {
}
}
-fn random_dungeon<R: Rng + ?Sized>(templates: Vec<VecRegion<Option<TileTemplate>>>, mut rng: &mut R) -> Result<Level, Error> {
+pub fn create_templates() -> Vec<VecRegion<Option<TileTemplate>>> {
+
+ let mut template:VecRegion<Option<TileTemplate>> = Region::new_with(3, 3);
+ use Direction::{UP, DOWN, LEFT, RIGHT};
+ template.set(0, 0, Some(TileTemplate { connections: RIGHT | DOWN}));
+ template.set(1, 0, Some(TileTemplate { connections: RIGHT | DOWN | LEFT}));
+ template.set(2, 0, Some(TileTemplate { connections: DOWN | LEFT}));
+
+ template.set(0, 1, Some(TileTemplate { connections: UP | RIGHT | DOWN}));
+ template.set(1, 1, Some(TileTemplate { connections: UP | RIGHT | DOWN | LEFT}));
+ template.set(2, 1, Some(TileTemplate { connections: UP | DOWN | LEFT}));
+
+ template.set(0, 2, Some(TileTemplate { connections: UP | RIGHT}));
+ template.set(1, 2, Some(TileTemplate { connections: UP | RIGHT | LEFT}));
+ template.set(2, 2, Some(TileTemplate { connections: UP | LEFT}));
+
+ vec!(template)
+}
+
+pub fn random_dungeon<R: Rng + ?Sized>(templates: &Vec<VecRegion<Option<TileTemplate>>>, mut rng: &mut R) -> Result<Level, Error> {
let mut region: VecRegion<Option<Tile>> = Region::new_with(15, 15);
let mut level = Level {
rooms: Vec::new(),
@@ -512,7 +539,7 @@ fn random_dungeon<R: Rng + ?Sized>(templates: Vec<VecRegion<Option<TileTemplate>
let x = rng.gen_range(0, level.region.width() - 2) + 1;
let y = rng.gen_range(0, level.region.height() - 2) + 1;
let res = place_template(&mut level, template, x, y);
- if attempts <= 0 && res.is_err() { panic!() }
+ if attempts <= 0 && res.is_err() { return Err(Error::MaximumIterations); }
if res.is_ok() { break res.unwrap() }
attempts += -1;
};
@@ -527,7 +554,7 @@ fn random_dungeon<R: Rng + ?Sized>(templates: Vec<VecRegion<Option<TileTemplate>
let x = rng.gen_range(0, level.region.width() - 2) + 1;
let y = rng.gen_range(0, level.region.height() - 2) + 1;
let res = place_template(&mut level, template, x, y);
- if attempts <= 0 && res.is_err() { panic!() }
+ if attempts <= 0 && res.is_err() { return Err(Error::MaximumIterations); }
if res.is_ok() { break res.unwrap() }
attempts += -1;
};
@@ -555,59 +582,9 @@ fn random_dungeon<R: Rng + ?Sized>(templates: Vec<VecRegion<Option<TileTemplate>
deadends += -1;
}
- random_walk(&mut level, &mut rng)?;
- random_walk(&mut level, &mut rng)?;
- random_walk(&mut level, &mut rng)?;
+ tunnel_branches(&mut level, &mut rng)?;
+ tunnel_branches(&mut level, &mut rng)?;
+ tunnel_branches(&mut level, &mut rng)?;
Ok(level)
}
-
-
-fn main() {
- let mut thread_rng = thread_rng();
- //18
- let mut rng = SmallRng::from_seed([14;16]);
-
-
- let mut template:VecRegion<Option<TileTemplate>> = Region::new_with(3, 3);
- use Direction::{UP, DOWN, LEFT, RIGHT};
- template.set(0, 0, Some(TileTemplate { connections: RIGHT | DOWN}));
- template.set(1, 0, Some(TileTemplate { connections: RIGHT | DOWN | LEFT}));
- template.set(2, 0, Some(TileTemplate { connections: DOWN | LEFT}));
-
- template.set(0, 1, Some(TileTemplate { connections: UP | RIGHT | DOWN}));
- template.set(1, 1, Some(TileTemplate { connections: UP | RIGHT | DOWN | LEFT}));
- template.set(2, 1, Some(TileTemplate { connections: UP | DOWN | LEFT}));
-
- template.set(0, 2, Some(TileTemplate { connections: UP | RIGHT}));
- template.set(1, 2, Some(TileTemplate { connections: UP | RIGHT | LEFT}));
- template.set(2, 2, Some(TileTemplate { connections: UP | LEFT}));
-
-
- let mut level: VecRegion<Option<Tile>> = Region::new_with(15, 11);
- let mut level = Level {
- rooms: Vec::new(),
- region: Box::new(level),
- tunnels: 0,
- };
-
- let res = place_template(&mut level, &mut template, 0, 0);
- let res = place_template(&mut level, &mut template, 3, 7);
- let res = place_template(&mut level, &mut template, 5, 2);
- let res = place_template(&mut level, &mut template, 8, 7);
- let res = connect_rooms(&mut level, 0, 1, &mut rng);
- let res = connect_rooms(&mut level, 2, 1, &mut rng);
- let res = connect_rooms(&mut level, 3, 2, &mut rng);
- //let res = connect_rooms(&mut level, 1, 3, &mut rng);
- create_deadend(&mut level, &mut rng);
- random_walk(&mut level, &mut rng);
- random_walk(&mut level, &mut rng);
- random_walk(&mut level, &mut rng);
- //create_deadend(&mut level, &mut rng);
- println!("{:?}", res);
- pretty_print(&level);
-
- let l = random_dungeon(vec!(template), &mut rng).unwrap();
- pretty_print(&l);
- //println!("{:?}", level);
-}
diff --git a/static/index.html b/static/index.html
new file mode 100644
index 0000000..9a27422
--- /dev/null
+++ b/static/index.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title>Level Generation</title>
+ <style>
+ html, body, canvas {
+ margin: 0px;
+ padding: 0px;
+ overflow: hidden;
+ }
+ canvas {
+ width: 350px;
+ height: 350px;
+ }
+ </style>
+ </head>
+ <body>
+ <canvas id="canvas"></canvas>
+ <div>
+ <button id="new">New</button>
+ </div>
+ <script src="web.js"></script>
+ </body>
+</html>