diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/bin/main.rs | 39 | ||||
-rw-r--r-- | src/bin/web.rs | 73 | ||||
-rw-r--r-- | src/canvas.rs | 61 | ||||
-rw-r--r-- | src/lib.rs (renamed from src/main.rs) | 139 |
4 files changed, 231 insertions, 81 deletions
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(); + } + } + } + } +} @@ -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); -} |