diff --git a/.gitignore b/.gitignore index 49e95d5..39266be 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ /target /demo/target Cargo.lock -.vscode/ \ No newline at end of file +.vscode/ +pkg/ +package-lock.json \ No newline at end of file diff --git a/README.md b/README.md index 13cbdb2..5c1a66b 100644 --- a/README.md +++ b/README.md @@ -6,16 +6,9 @@ Generate procedural maps for games. -This library is based on the code from the [Roguelike tutorial](https://github.com/thebracket/rustrogueliketutorial). -I highly recommend it for learning how to write Roguelike in Rust. - - ## Demo app -If you want to check how the maps look like, then: - * Clone this rep - * Go to the demo folder - * Run demo app (`cargo run`) +Check [demo app](https://klangner.github.io/mapgen.rs/) ## Features @@ -80,6 +73,11 @@ let map = MapBuilder::new(Box::new(CellularAutomataGen::new(80, 50))) For more information check the [doc](https://docs.rs/mapgen) + +This library is based on the code from the [Roguelike tutorial](https://github.com/thebracket/rustrogueliketutorial). +I highly recommend it for learning how to write Roguelike in Rust. + + # License Licensed under either of diff --git a/demo/Cargo.toml b/demo/Cargo.toml index 68fd312..76bccfc 100644 --- a/demo/Cargo.toml +++ b/demo/Cargo.toml @@ -2,15 +2,26 @@ name = "mapgen-demo" version = "0.1.1" authors = ["Krzysztof Langner "] +description = "Map generator demo" +license = "MIT OR Apache-2.0" +repository = "https://github.com/klangner/mapgen.rs" edition = "2018" -[dependencies] -rand = "0.7" -amethyst = {version = "0.15", features = ["tiles", "no-slow-safety-checks"]} -log = { version = "0.4.8", features = ["serde"] } -mapgen = {path=".."} +[lib] +crate-type = ["cdylib"] -[features] -default = ["metal"] -metal = ["amethyst/metal"] -vulkan = ["amethyst/vulkan"] +[dependencies] +rand = { version = "0.7", features = ["wasm-bindgen"] } +mapgen = {path=".."} +wasm-bindgen = "0.2" +js-sys = "0.3" + +[dependencies.web-sys] +version = "0.3.4" +features = [ + 'Document', + 'Element', + 'HtmlElement', + 'Node', + 'Window', +] \ No newline at end of file diff --git a/demo/LICENSE-APACHE b/demo/LICENSE-APACHE new file mode 100644 index 0000000..9e0cec1 --- /dev/null +++ b/demo/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright 2017 Krzysztof Langner + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/demo/README.md b/demo/README.md index ad7baa8..d9669b8 100644 --- a/demo/README.md +++ b/demo/README.md @@ -1,6 +1,12 @@ # Demo application for mapgen +## Build the project: +``` +wasm-pack build +cd www +npm run build +``` This app uses: * [Urizen OneBit Tilesets](https://vurmux.itch.io/urizen-onebit-tilesets) \ No newline at end of file diff --git a/demo/config/display.ron b/demo/config/display.ron deleted file mode 100644 index c5a98fd..0000000 --- a/demo/config/display.ron +++ /dev/null @@ -1,4 +0,0 @@ -( - title: "mapgen demo", - dimensions: Some((840, 520)), -) \ No newline at end of file diff --git a/demo/config/input.ron b/demo/config/input.ron deleted file mode 100644 index cb5dae1..0000000 --- a/demo/config/input.ron +++ /dev/null @@ -1,27 +0,0 @@ - -( - axes: { - "camera_x": Emulated( - pos: Key(D), - neg: Key(A), - ), - "camera_y": Emulated( - pos: Key(W), - neg: Key(S), - ), - "camera_scale": Emulated( - pos: Key(E), - neg: Key(Q), - ), - "camera_z": Emulated( - pos: Key(R), - neg: Key(F), - ), - }, - actions: { - "camera_switch": [[Key(Space)]], - "select": [[Mouse(Left)]], - "toggle_rotation": [[Key(Y)]], - "toggle_translation": [[Key(T)]], - }, -) \ No newline at end of file diff --git a/demo/index.html b/demo/index.html new file mode 100644 index 0000000..02f635b --- /dev/null +++ b/demo/index.html @@ -0,0 +1,59 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/demo/src/lib.rs b/demo/src/lib.rs new file mode 100644 index 0000000..993c453 --- /dev/null +++ b/demo/src/lib.rs @@ -0,0 +1,56 @@ +use wasm_bindgen::prelude::*; +use rand::prelude::*; +use js_sys::Date; +use mapgen::dungeon::{ + MapBuilder, + map::TileType, + cellular_automata::CellularAutomataGen, + starting_point::{AreaStartingPosition, XStart, YStart}, + cull_unreachable::CullUnreachable, + distant_exit::DistantExit, +}; + + +#[wasm_bindgen] +pub struct World { + width: u32, + height: u32, + tiles: Vec, +} + +#[wasm_bindgen] +impl World { + pub fn new(width: u32, height: u32) -> World { + let seed = Date::new_0().get_time() as u64; + let mut rng = StdRng::seed_from_u64(seed); + let map = MapBuilder::new(Box::new(CellularAutomataGen::new(width as usize, height as usize))) + .with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER)) + .with(CullUnreachable::new()) + .with(DistantExit::new()) + .build_map_with_rng(&mut rng); + let tiles = (0..map.tiles.len()).map(|i| map.tiles[i] == TileType::Floor).collect(); + World { + width, + height, + tiles } + } + + pub fn width(&self) -> u32 { + self.width + } + + pub fn height(&self) -> u32 { + self.height + } + + pub fn tiles(&self) -> *const bool { + self.tiles.as_ptr() + } +} + + +// Called when the wasm module is instantiated +// #[wasm_bindgen(start)] +// pub fn main() -> Result<(), JsValue> { +// Ok(()) +// } \ No newline at end of file diff --git a/demo/src/main.rs b/demo/src/main.rs deleted file mode 100644 index 436b976..0000000 --- a/demo/src/main.rs +++ /dev/null @@ -1,179 +0,0 @@ -use amethyst::{ - assets::{AssetStorage, Loader}, - core::{ - math::{Point3, Vector3}, - Transform, TransformBundle, - }, - ecs::Entity, - input::{is_close_requested, is_key_down, InputBundle, StringBindings}, - prelude::*, - renderer::{ - camera::Camera, - formats::texture::ImageFormat, - sprite::{SpriteSheet, SpriteSheetFormat, SpriteSheetHandle}, - types::DefaultBackend, - RenderFlat2D, RenderToWindow, RenderingBundle, Texture, - palette::Srgba, - }, - tiles::{MortonEncoder, RenderTiles2D, Tile, TileMap}, - utils::application_root_dir, - window::ScreenDimensions, - winit, -}; -use mapgen::dungeon::{ - MapBuilder, - map::{Map, Point, TileType}, - cellular_automata::CellularAutomataGen, - starting_point::{AreaStartingPosition, XStart, YStart}, - cull_unreachable::CullUnreachable, - distant_exit::DistantExit, -}; - - -#[derive(Default, Clone)] -struct MapTiles ; - -impl Tile for MapTiles { - fn sprite(&self, p: Point3, world: &World) -> Option { - let map = world.read_resource::(); - let pos = Point::new(p.x as usize, p.y as usize); - if map.starting_point == Some(pos) { - Some(160) - } else if map.exit_point == Some(pos) { - Some(12) - } else if map.at(p.x as usize, p.y as usize) == TileType::Wall { - Some(140) - } else { - Some(19) - } - } - - fn tint(&self, p: Point3, world: &World) -> Srgba { - let map = world.read_resource::(); - let pos = Some(Point::new(p.x as usize, p.y as usize)); - if map.starting_point == pos || map.exit_point == 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 { - let texture_handle = { - let loader = world.read_resource::(); - let texture_storage = world.read_resource::>(); - loader.load(png_path, ImageFormat::default(), (), &texture_storage) - }; - let loader = world.read_resource::(); - let sprite_sheet_store = world.read_resource::>(); - loader.load( - ron_path, - SpriteSheetFormat(texture_handle), - (), - &sprite_sheet_store, - ) -} - -fn init_camera(world: &mut World, transform: Transform, camera: Camera) -> Entity { - world - .create_entity() - .with(transform) - .with(camera) - .named("camera") - .build() -} - -fn init_map(world: &mut World) { - let map = MapBuilder::new(Box::new(CellularAutomataGen::new(80, 50))) - .with(AreaStartingPosition::new(XStart::CENTER, YStart::CENTER)) - .with(CullUnreachable::new()) - .with(DistantExit::new()) - .build_map(); - world.insert(map); -} - - -struct PlayState; -impl SimpleState for PlayState { - fn on_start(&mut self, data: StateData<'_, GameData<'_, '_>>) { - - let mut world = data.world; - - // Create map - init_map(&mut world); - - let map_sprite_sheet_handle = - load_tiles_sprite_sheet(world, "texture/basic.png", "texture/basic.ron"); - - let (width, height) = { - let dim = world.read_resource::(); - (dim.width(), dim.height()) - }; - - let _camera = init_camera( - world, - Transform::from(Vector3::new(-10.0, 10.0, 1.1)), - Camera::standard_2d(width, height), - ); - - let tile_map = TileMap::::new( - Vector3::new(80, 50, 1), - Vector3::new(20, 20, 1), - Some(map_sprite_sheet_handle), - ); - - let _map_entity = world - .create_entity() - .with(tile_map) - .with(Transform::default()) - .build(); - } - - fn handle_event( - &mut self, - data: StateData<'_, GameData<'_, '_>>, - event: StateEvent, - ) -> SimpleTrans { - let StateData { .. } = data; - if let StateEvent::Window(event) = &event { - if is_close_requested(&event) || is_key_down(&event, winit::VirtualKeyCode::Escape) { - Trans::Quit - } else { - Trans::None - } - } else { - Trans::None - } - } -} - -fn main() -> amethyst::Result<()> { - amethyst::Logger::from_config(Default::default()) - .level_for("demo", log::LevelFilter::Warn) - .start(); - - let app_root = application_root_dir()?; - let assets_directory = app_root.join("assets"); - let display_config_path = app_root.join("config/display.ron"); - - let game_data = GameDataBuilder::default() - .with_bundle(TransformBundle::new())? - .with_bundle( - InputBundle::::new() - .with_bindings_from_file("config/input.ron")?, - )? - .with_bundle( - RenderingBundle::::new() - .with_plugin( - RenderToWindow::from_config_path(display_config_path)? - .with_clear([0.0, 0.0, 0.0, 1.0]), - ) - .with_plugin(RenderFlat2D::default()) - .with_plugin(RenderTiles2D::::default()), - )?; - - let mut game = Application::build(assets_directory, PlayState)?.build(game_data)?; - game.run(); - Ok(()) -} \ No newline at end of file diff --git a/demo/src/utils.rs b/demo/src/utils.rs new file mode 100644 index 0000000..971a4bc --- /dev/null +++ b/demo/src/utils.rs @@ -0,0 +1,12 @@ + +pub fn set_panic_hook() { + // When the `console_error_panic_hook` feature is enabled, we can call the + // `set_panic_hook` function at least once during initialization, and then + // we will get better error messages if our code ever panics. + // + // For more details see + // https://github.com/rustwasm/console_error_panic_hook#readme + #[cfg(feature = "console_error_panic_hook")] + console_error_panic_hook::set_once(); +} + diff --git a/demo/www b/demo/www new file mode 160000 index 0000000..9ac3dff --- /dev/null +++ b/demo/www @@ -0,0 +1 @@ +Subproject commit 9ac3dff9ebea4675e5c478bcdcbc0fd547d1529f diff --git a/docs/0.bootstrap.js b/docs/0.bootstrap.js new file mode 100644 index 0000000..e0ae0ba --- /dev/null +++ b/docs/0.bootstrap.js @@ -0,0 +1,61 @@ +(window["webpackJsonp"] = window["webpackJsonp"] || []).push([[0],{ + +/***/ "../pkg/mapgen_demo.js": +/*!*****************************!*\ + !*** ../pkg/mapgen_demo.js ***! + \*****************************/ +/*! exports provided: World, __wbindgen_object_drop_ref, __wbg_getTime_29addd71c7089c47, __wbg_new0_a3af66503e735141, __wbindgen_throw */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _mapgen_demo_bg_wasm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./mapgen_demo_bg.wasm */ \"../pkg/mapgen_demo_bg.wasm\");\n/* harmony import */ var _mapgen_demo_bg_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./mapgen_demo_bg.js */ \"../pkg/mapgen_demo_bg.js\");\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"World\", function() { return _mapgen_demo_bg_js__WEBPACK_IMPORTED_MODULE_1__[\"World\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"__wbindgen_object_drop_ref\", function() { return _mapgen_demo_bg_js__WEBPACK_IMPORTED_MODULE_1__[\"__wbindgen_object_drop_ref\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"__wbg_getTime_29addd71c7089c47\", function() { return _mapgen_demo_bg_js__WEBPACK_IMPORTED_MODULE_1__[\"__wbg_getTime_29addd71c7089c47\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"__wbg_new0_a3af66503e735141\", function() { return _mapgen_demo_bg_js__WEBPACK_IMPORTED_MODULE_1__[\"__wbg_new0_a3af66503e735141\"]; });\n\n/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, \"__wbindgen_throw\", function() { return _mapgen_demo_bg_js__WEBPACK_IMPORTED_MODULE_1__[\"__wbindgen_throw\"]; });\n\n\n\n\n//# sourceURL=webpack:///../pkg/mapgen_demo.js?"); + +/***/ }), + +/***/ "../pkg/mapgen_demo_bg.js": +/*!********************************!*\ + !*** ../pkg/mapgen_demo_bg.js ***! + \********************************/ +/*! exports provided: World, __wbindgen_object_drop_ref, __wbg_getTime_29addd71c7089c47, __wbg_new0_a3af66503e735141, __wbindgen_throw */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* WEBPACK VAR INJECTION */(function(module) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"World\", function() { return World; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"__wbindgen_object_drop_ref\", function() { return __wbindgen_object_drop_ref; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"__wbg_getTime_29addd71c7089c47\", function() { return __wbg_getTime_29addd71c7089c47; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"__wbg_new0_a3af66503e735141\", function() { return __wbg_new0_a3af66503e735141; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"__wbindgen_throw\", function() { return __wbindgen_throw; });\n/* harmony import */ var _mapgen_demo_bg_wasm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./mapgen_demo_bg.wasm */ \"../pkg/mapgen_demo_bg.wasm\");\n\n\nconst heap = new Array(32).fill(undefined);\n\nheap.push(undefined, null, true, false);\n\nfunction getObject(idx) { return heap[idx]; }\n\nlet heap_next = heap.length;\n\nfunction dropObject(idx) {\n if (idx < 36) return;\n heap[idx] = heap_next;\n heap_next = idx;\n}\n\nfunction takeObject(idx) {\n const ret = getObject(idx);\n dropObject(idx);\n return ret;\n}\n\nconst lTextDecoder = typeof TextDecoder === 'undefined' ? (0, module.require)('util').TextDecoder : TextDecoder;\n\nlet cachedTextDecoder = new lTextDecoder('utf-8', { ignoreBOM: true, fatal: true });\n\ncachedTextDecoder.decode();\n\nlet cachegetUint8Memory0 = null;\nfunction getUint8Memory0() {\n if (cachegetUint8Memory0 === null || cachegetUint8Memory0.buffer !== _mapgen_demo_bg_wasm__WEBPACK_IMPORTED_MODULE_0__[\"memory\"].buffer) {\n cachegetUint8Memory0 = new Uint8Array(_mapgen_demo_bg_wasm__WEBPACK_IMPORTED_MODULE_0__[\"memory\"].buffer);\n }\n return cachegetUint8Memory0;\n}\n\nfunction getStringFromWasm0(ptr, len) {\n return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));\n}\n\nfunction addHeapObject(obj) {\n if (heap_next === heap.length) heap.push(heap.length + 1);\n const idx = heap_next;\n heap_next = heap[idx];\n\n heap[idx] = obj;\n return idx;\n}\n/**\n*/\nclass World {\n\n static __wrap(ptr) {\n const obj = Object.create(World.prototype);\n obj.ptr = ptr;\n\n return obj;\n }\n\n free() {\n const ptr = this.ptr;\n this.ptr = 0;\n\n _mapgen_demo_bg_wasm__WEBPACK_IMPORTED_MODULE_0__[\"__wbg_world_free\"](ptr);\n }\n /**\n * @param {number} width\n * @param {number} height\n * @returns {World}\n */\n static new(width, height) {\n var ret = _mapgen_demo_bg_wasm__WEBPACK_IMPORTED_MODULE_0__[\"world_new\"](width, height);\n return World.__wrap(ret);\n }\n /**\n * @returns {number}\n */\n width() {\n var ret = _mapgen_demo_bg_wasm__WEBPACK_IMPORTED_MODULE_0__[\"world_width\"](this.ptr);\n return ret >>> 0;\n }\n /**\n * @returns {number}\n */\n height() {\n var ret = _mapgen_demo_bg_wasm__WEBPACK_IMPORTED_MODULE_0__[\"world_height\"](this.ptr);\n return ret >>> 0;\n }\n /**\n * @returns {number}\n */\n tiles() {\n var ret = _mapgen_demo_bg_wasm__WEBPACK_IMPORTED_MODULE_0__[\"world_tiles\"](this.ptr);\n return ret;\n }\n}\n\nconst __wbindgen_object_drop_ref = function(arg0) {\n takeObject(arg0);\n};\n\nconst __wbg_getTime_29addd71c7089c47 = function(arg0) {\n var ret = getObject(arg0).getTime();\n return ret;\n};\n\nconst __wbg_new0_a3af66503e735141 = function() {\n var ret = new Date();\n return addHeapObject(ret);\n};\n\nconst __wbindgen_throw = function(arg0, arg1) {\n throw new Error(getStringFromWasm0(arg0, arg1));\n};\n\n\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../www/node_modules/webpack/buildin/harmony-module.js */ \"./node_modules/webpack/buildin/harmony-module.js\")(module)))\n\n//# sourceURL=webpack:///../pkg/mapgen_demo_bg.js?"); + +/***/ }), + +/***/ "../pkg/mapgen_demo_bg.wasm": +/*!**********************************!*\ + !*** ../pkg/mapgen_demo_bg.wasm ***! + \**********************************/ +/*! exports provided: memory, __wbg_world_free, world_new, world_width, world_height, world_tiles */ +/***/ (function(module, exports, __webpack_require__) { + +eval("\"use strict\";\n// Instantiate WebAssembly module\nvar wasmExports = __webpack_require__.w[module.i];\n__webpack_require__.r(exports);\n// export exports from WebAssembly module\nfor(var name in wasmExports) if(name != \"__webpack_init__\") exports[name] = wasmExports[name];\n// exec imports from WebAssembly module (for esm order)\n/* harmony import */ var m0 = __webpack_require__(/*! ./mapgen_demo_bg.js */ \"../pkg/mapgen_demo_bg.js\");\n\n\n// exec wasm module\nwasmExports[\"__webpack_init__\"]()\n\n//# sourceURL=webpack:///../pkg/mapgen_demo_bg.wasm?"); + +/***/ }), + +/***/ "./index.js": +/*!******************!*\ + !*** ./index.js ***! + \******************/ +/*! no exports provided */ +/***/ (function(module, __webpack_exports__, __webpack_require__) { + +"use strict"; +eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var mapgen_demo__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! mapgen-demo */ \"../pkg/mapgen_demo.js\");\n/* harmony import */ var mapgen_demo_mapgen_demo_bg__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! mapgen-demo/mapgen_demo_bg */ \"../pkg/mapgen_demo_bg.wasm\");\n\n\n\nconst CELL_SIZE = 12;\nconst GRID_COLOR = \"#CCCCCC\";\nconst DEAD_COLOR = \"#FFFFFF\";\nconst ALIVE_COLOR = \"#000000\";\n\nconst world = mapgen_demo__WEBPACK_IMPORTED_MODULE_0__[\"World\"].new(80, 50);\nconst width = world.width();\nconst height = world.height();\n\n// Give the canvas room for all of our cells and a 1px border\n// around each of them.\nconst canvas = document.getElementById(\"mapgen-canvas\");\ncanvas.height = (CELL_SIZE + 1) * height + 1;\ncanvas.width = (CELL_SIZE + 1) * width + 1;\n\nconst ctx = canvas.getContext('2d');\n\nconst renderLoop = () => {\n // universe.tick();\n\n drawGrid();\n drawCells();\n\n requestAnimationFrame(renderLoop);\n};\n\nconst drawGrid = () => {\n ctx.beginPath();\n ctx.strokeStyle = GRID_COLOR;\n\n // Vertical lines.\n for (let i = 0; i <= width; i++) {\n ctx.moveTo(i * (CELL_SIZE + 1) + 1, 0);\n ctx.lineTo(i * (CELL_SIZE + 1) + 1, (CELL_SIZE + 1) * height + 1);\n }\n\n // Horizontal lines.\n for (let j = 0; j <= height; j++) {\n ctx.moveTo(0, j * (CELL_SIZE + 1) + 1);\n ctx.lineTo((CELL_SIZE + 1) * width + 1, j * (CELL_SIZE + 1) + 1);\n }\n\n ctx.stroke();\n};\n\nconst getIndex = (row, column) => {\n return row * width + column;\n};\n\nconst drawCells = () => {\n const tilesPtr = world.tiles();\n const tiles = new Uint8Array(mapgen_demo_mapgen_demo_bg__WEBPACK_IMPORTED_MODULE_1__[\"memory\"].buffer, tilesPtr, width * height);\n\n ctx.beginPath();\n\n for (let row = 0; row < height; row++) {\n for (let col = 0; col < width; col++) {\n const idx = getIndex(row, col);\n\n ctx.fillStyle = tiles[idx]\n ? DEAD_COLOR\n : ALIVE_COLOR;\n\n ctx.fillRect(\n col * (CELL_SIZE + 1) + 1,\n row * (CELL_SIZE + 1) + 1,\n CELL_SIZE,\n CELL_SIZE\n );\n }\n }\n\n ctx.stroke();\n};\n\ndrawGrid();\ndrawCells();\nrequestAnimationFrame(renderLoop);\n\n//# sourceURL=webpack:///./index.js?"); + +/***/ }), + +/***/ "./node_modules/webpack/buildin/harmony-module.js": +/*!*******************************************!*\ + !*** (webpack)/buildin/harmony-module.js ***! + \*******************************************/ +/*! no static exports found */ +/***/ (function(module, exports) { + +eval("module.exports = function(originalModule) {\n\tif (!originalModule.webpackPolyfill) {\n\t\tvar module = Object.create(originalModule);\n\t\t// module.parent = undefined by default\n\t\tif (!module.children) module.children = [];\n\t\tObject.defineProperty(module, \"loaded\", {\n\t\t\tenumerable: true,\n\t\t\tget: function() {\n\t\t\t\treturn module.l;\n\t\t\t}\n\t\t});\n\t\tObject.defineProperty(module, \"id\", {\n\t\t\tenumerable: true,\n\t\t\tget: function() {\n\t\t\t\treturn module.i;\n\t\t\t}\n\t\t});\n\t\tObject.defineProperty(module, \"exports\", {\n\t\t\tenumerable: true\n\t\t});\n\t\tmodule.webpackPolyfill = 1;\n\t}\n\treturn module;\n};\n\n\n//# sourceURL=webpack:///(webpack)/buildin/harmony-module.js?"); + +/***/ }) + +}]); \ No newline at end of file diff --git a/docs/bf853f7fb17b7aed38db.module.wasm b/docs/bf853f7fb17b7aed38db.module.wasm new file mode 100644 index 0000000..3874075 Binary files /dev/null and b/docs/bf853f7fb17b7aed38db.module.wasm differ diff --git a/docs/bootstrap.js b/docs/bootstrap.js new file mode 100644 index 0000000..a792eb6 --- /dev/null +++ b/docs/bootstrap.js @@ -0,0 +1,276 @@ +/******/ (function(modules) { // webpackBootstrap +/******/ // install a JSONP callback for chunk loading +/******/ function webpackJsonpCallback(data) { +/******/ var chunkIds = data[0]; +/******/ var moreModules = data[1]; +/******/ +/******/ +/******/ // add "moreModules" to the modules object, +/******/ // then flag all "chunkIds" as loaded and fire callback +/******/ var moduleId, chunkId, i = 0, resolves = []; +/******/ for(;i < chunkIds.length; i++) { +/******/ chunkId = chunkIds[i]; +/******/ if(Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) { +/******/ resolves.push(installedChunks[chunkId][0]); +/******/ } +/******/ installedChunks[chunkId] = 0; +/******/ } +/******/ for(moduleId in moreModules) { +/******/ if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) { +/******/ modules[moduleId] = moreModules[moduleId]; +/******/ } +/******/ } +/******/ if(parentJsonpFunction) parentJsonpFunction(data); +/******/ +/******/ while(resolves.length) { +/******/ resolves.shift()(); +/******/ } +/******/ +/******/ }; +/******/ +/******/ +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // object to store loaded and loading chunks +/******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched +/******/ // Promise = chunk loading, 0 = chunk loaded +/******/ var installedChunks = { +/******/ "main": 0 +/******/ }; +/******/ +/******/ +/******/ +/******/ // script path function +/******/ function jsonpScriptSrc(chunkId) { +/******/ return __webpack_require__.p + "" + chunkId + ".bootstrap.js" +/******/ } +/******/ +/******/ // object to store loaded and loading wasm modules +/******/ var installedWasmModules = {}; +/******/ +/******/ function promiseResolve() { return Promise.resolve(); } +/******/ +/******/ var wasmImportObjects = { +/******/ "../pkg/mapgen_demo_bg.wasm": function() { +/******/ return { +/******/ "./mapgen_demo_bg.js": { +/******/ "__wbindgen_object_drop_ref": function(p0i32) { +/******/ return installedModules["../pkg/mapgen_demo_bg.js"].exports["__wbindgen_object_drop_ref"](p0i32); +/******/ }, +/******/ "__wbg_getTime_29addd71c7089c47": function(p0i32) { +/******/ return installedModules["../pkg/mapgen_demo_bg.js"].exports["__wbg_getTime_29addd71c7089c47"](p0i32); +/******/ }, +/******/ "__wbg_new0_a3af66503e735141": function() { +/******/ return installedModules["../pkg/mapgen_demo_bg.js"].exports["__wbg_new0_a3af66503e735141"](); +/******/ }, +/******/ "__wbindgen_throw": function(p0i32,p1i32) { +/******/ return installedModules["../pkg/mapgen_demo_bg.js"].exports["__wbindgen_throw"](p0i32,p1i32); +/******/ } +/******/ } +/******/ }; +/******/ }, +/******/ }; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ // This file contains only the entry chunk. +/******/ // The chunk loading function for additional chunks +/******/ __webpack_require__.e = function requireEnsure(chunkId) { +/******/ var promises = []; +/******/ +/******/ +/******/ // JSONP chunk loading for javascript +/******/ +/******/ var installedChunkData = installedChunks[chunkId]; +/******/ if(installedChunkData !== 0) { // 0 means "already installed". +/******/ +/******/ // a Promise means "currently loading". +/******/ if(installedChunkData) { +/******/ promises.push(installedChunkData[2]); +/******/ } else { +/******/ // setup Promise in chunk cache +/******/ var promise = new Promise(function(resolve, reject) { +/******/ installedChunkData = installedChunks[chunkId] = [resolve, reject]; +/******/ }); +/******/ promises.push(installedChunkData[2] = promise); +/******/ +/******/ // start chunk loading +/******/ var script = document.createElement('script'); +/******/ var onScriptComplete; +/******/ +/******/ script.charset = 'utf-8'; +/******/ script.timeout = 120; +/******/ if (__webpack_require__.nc) { +/******/ script.setAttribute("nonce", __webpack_require__.nc); +/******/ } +/******/ script.src = jsonpScriptSrc(chunkId); +/******/ +/******/ // create error before stack unwound to get useful stacktrace later +/******/ var error = new Error(); +/******/ onScriptComplete = function (event) { +/******/ // avoid mem leaks in IE. +/******/ script.onerror = script.onload = null; +/******/ clearTimeout(timeout); +/******/ var chunk = installedChunks[chunkId]; +/******/ if(chunk !== 0) { +/******/ if(chunk) { +/******/ var errorType = event && (event.type === 'load' ? 'missing' : event.type); +/******/ var realSrc = event && event.target && event.target.src; +/******/ error.message = 'Loading chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')'; +/******/ error.name = 'ChunkLoadError'; +/******/ error.type = errorType; +/******/ error.request = realSrc; +/******/ chunk[1](error); +/******/ } +/******/ installedChunks[chunkId] = undefined; +/******/ } +/******/ }; +/******/ var timeout = setTimeout(function(){ +/******/ onScriptComplete({ type: 'timeout', target: script }); +/******/ }, 120000); +/******/ script.onerror = script.onload = onScriptComplete; +/******/ document.head.appendChild(script); +/******/ } +/******/ } +/******/ +/******/ // Fetch + compile chunk loading for webassembly +/******/ +/******/ var wasmModules = {"0":["../pkg/mapgen_demo_bg.wasm"]}[chunkId] || []; +/******/ +/******/ wasmModules.forEach(function(wasmModuleId) { +/******/ var installedWasmModuleData = installedWasmModules[wasmModuleId]; +/******/ +/******/ // a Promise means "currently loading" or "already loaded". +/******/ if(installedWasmModuleData) +/******/ promises.push(installedWasmModuleData); +/******/ else { +/******/ var importObject = wasmImportObjects[wasmModuleId](); +/******/ var req = fetch(__webpack_require__.p + "" + {"../pkg/mapgen_demo_bg.wasm":"bf853f7fb17b7aed38db"}[wasmModuleId] + ".module.wasm"); +/******/ var promise; +/******/ if(importObject instanceof Promise && typeof WebAssembly.compileStreaming === 'function') { +/******/ promise = Promise.all([WebAssembly.compileStreaming(req), importObject]).then(function(items) { +/******/ return WebAssembly.instantiate(items[0], items[1]); +/******/ }); +/******/ } else if(typeof WebAssembly.instantiateStreaming === 'function') { +/******/ promise = WebAssembly.instantiateStreaming(req, importObject); +/******/ } else { +/******/ var bytesPromise = req.then(function(x) { return x.arrayBuffer(); }); +/******/ promise = bytesPromise.then(function(bytes) { +/******/ return WebAssembly.instantiate(bytes, importObject); +/******/ }); +/******/ } +/******/ promises.push(installedWasmModules[wasmModuleId] = promise.then(function(res) { +/******/ return __webpack_require__.w[wasmModuleId] = (res.instance || res).exports; +/******/ })); +/******/ } +/******/ }); +/******/ return Promise.all(promises); +/******/ }; +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); +/******/ } +/******/ }; +/******/ +/******/ // define __esModule on exports +/******/ __webpack_require__.r = function(exports) { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ +/******/ // create a fake namespace object +/******/ // mode & 1: value is a module id, require it +/******/ // mode & 2: merge all properties of value into the ns +/******/ // mode & 4: return value when already ns object +/******/ // mode & 8|1: behave like require +/******/ __webpack_require__.t = function(value, mode) { +/******/ if(mode & 1) value = __webpack_require__(value); +/******/ if(mode & 8) return value; +/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; +/******/ var ns = Object.create(null); +/******/ __webpack_require__.r(ns); +/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); +/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); +/******/ return ns; +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ // on error function for async loading +/******/ __webpack_require__.oe = function(err) { console.error(err); throw err; }; +/******/ +/******/ // object with all WebAssembly.instance exports +/******/ __webpack_require__.w = {}; +/******/ +/******/ var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || []; +/******/ var oldJsonpFunction = jsonpArray.push.bind(jsonpArray); +/******/ jsonpArray.push = webpackJsonpCallback; +/******/ jsonpArray = jsonpArray.slice(); +/******/ for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]); +/******/ var parentJsonpFunction = oldJsonpFunction; +/******/ +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = "./bootstrap.js"); +/******/ }) +/************************************************************************/ +/******/ ({ + +/***/ "./bootstrap.js": +/*!**********************!*\ + !*** ./bootstrap.js ***! + \**********************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +eval("// A dependency graph that contains any wasm must all be imported\n// asynchronously. This `bootstrap.js` file does the single async import, so\n// that no one else needs to worry about it again.\n__webpack_require__.e(/*! import() */ 0).then(__webpack_require__.bind(null, /*! ./index.js */ \"./index.js\"))\n .catch(e => console.error(\"Error importing `index.js`:\", e));\n\n\n//# sourceURL=webpack:///./bootstrap.js?"); + +/***/ }) + +/******/ }); \ No newline at end of file diff --git a/docs/favicon.ico b/docs/favicon.ico new file mode 100644 index 0000000..d9eaec7 Binary files /dev/null and b/docs/favicon.ico differ diff --git a/docs/index.html b/docs/index.html index 4d44e59..80e6e05 100644 --- a/docs/index.html +++ b/docs/index.html @@ -3,29 +3,11 @@ Mapgen demo - -

Map generator demo

- -
- - +

Mapgen demo

+ + + diff --git a/docs/index.js b/docs/index.js deleted file mode 100644 index fb41e3f..0000000 --- a/docs/index.js +++ /dev/null @@ -1,199 +0,0 @@ -import { Universe, Cell } from "wasm-game-of-life"; -import { memory } from "wasm-game-of-life/wasm_game_of_life_bg"; - -const CELL_SIZE = 8; // px -const GRID_COLOR = "#CCCCCC"; -const DEAD_COLOR = "#FFFFFF"; -const ALIVE_COLOR = "#000000"; - -// Construct the universe, and get its width and height. -// const universe = Universe.new_spaceship(); -const universe = Universe.new(); -const width = universe.width(); -const height = universe.height(); - -// Give the canvas room for all of our cells and a 1px border -// around each of them. -const canvas = document.getElementById("game-of-life-canvas"); -canvas.height = (CELL_SIZE + 1) * height + 1; -canvas.width = (CELL_SIZE + 1) * width + 1; - -const ctx = canvas.getContext('2d'); - -let animationId = null; - -const renderLoop = () => { - fps.render(); - for (let i = 0; i < 9; i++) { - universe.tick(); - } - - drawGrid(); - drawCells(); - - animationId = requestAnimationFrame(renderLoop); -}; - -const drawGrid = () => { - ctx.beginPath(); - ctx.strokeStyle = GRID_COLOR; - - // Vertical lines. - for (let i = 0; i <= width; i++) { - ctx.moveTo(i * (CELL_SIZE + 1) + 1, 0); - ctx.lineTo(i * (CELL_SIZE + 1) + 1, (CELL_SIZE + 1) * height + 1); - } - - // Horizontal lines. - for (let j = 0; j <= height; j++) { - ctx.moveTo(0, j * (CELL_SIZE + 1) + 1); - ctx.lineTo((CELL_SIZE + 1) * width + 1, j * (CELL_SIZE + 1) + 1); - } - - ctx.stroke(); -}; - -const getIndex = (row, column) => { - return row * width + column; -}; - -const drawCells = () => { - const cellsPtr = universe.cells(); - - // This is updated! - const cells = new Uint8Array(memory.buffer, cellsPtr, width * height / 8); - - ctx.beginPath(); - - // Alive cells. - ctx.fillStyle = ALIVE_COLOR; - for (let row = 0; row < height; row++) { - for (let col = 0; col < width; col++) { - const idx = getIndex(row, col); - if (!bitIsSet(idx, cells)) { - continue; - } - - ctx.fillRect( - col * (CELL_SIZE + 1) + 1, - row * (CELL_SIZE + 1) + 1, - CELL_SIZE, - CELL_SIZE - ); - } - } - - // Dead cells. - ctx.fillStyle = DEAD_COLOR; - for (let row = 0; row < height; row++) { - for (let col = 0; col < width; col++) { - const idx = getIndex(row, col); - if (bitIsSet(idx, cells)) { - continue; - } - - ctx.fillRect( - col * (CELL_SIZE + 1) + 1, - row * (CELL_SIZE + 1) + 1, - CELL_SIZE, - CELL_SIZE - ); - } - } - - ctx.stroke(); -}; - -const bitIsSet = (n, arr) => { - const byte = Math.floor(n / 8); - const mask = 1 << (n % 8); - return (arr[byte] & mask) === mask; -}; - -const isPaused = () => { - return animationId === null; -}; - -const playPauseButton = document.getElementById("play-pause"); - -const play = () => { - playPauseButton.textContent = "⏸"; - renderLoop(); -}; - -const pause = () => { - playPauseButton.textContent = "▶"; - cancelAnimationFrame(animationId); - animationId = null; -}; - -playPauseButton.addEventListener("click", event => { - if (isPaused()) { - play(); - } else { - pause(); - } -}); - -canvas.addEventListener("click", event => { - const boundingRect = canvas.getBoundingClientRect(); - - const scaleX = canvas.width / boundingRect.width; - const scaleY = canvas.height / boundingRect.height; - - const canvasLeft = (event.clientX - boundingRect.left) * scaleX; - const canvasTop = (event.clientY - boundingRect.top) * scaleY; - - const row = Math.min(Math.floor(canvasTop / (CELL_SIZE + 1)), height - 1); - const col = Math.min(Math.floor(canvasLeft / (CELL_SIZE + 1)), width - 1); - - universe.toggle_cell(row, col); - - drawGrid(); - drawCells(); -}); - -const fps = new class { - constructor() { - this.fps = document.getElementById("fps"); - this.frames = []; - this.lastFrameTimeStamp = performance.now(); - } - - render() { - // Convert the delta time since the last frame render into a measure - // of frames per second. - const now = performance.now(); - const delta = now - this.lastFrameTimeStamp; - this.lastFrameTimeStamp = now; - const fps = 1 / delta * 1000; - - // Save only the latest 100 timings. - this.frames.push(fps); - if (this.frames.length > 100) { - this.frames.shift(); - } - - // Find the max, min, and mean of our 100 latest timings. - let min = Infinity; - let max = -Infinity; - let sum = 0; - for (let i = 0; i < this.frames.length; i++) { - sum += this.frames[i]; - min = Math.min(this.frames[i], min); - max = Math.max(this.frames[i], max); - } - let mean = sum / this.frames.length; - - // Render the statistics. - this.fps.textContent = ` -Frames per Second: - latest = ${Math.round(fps)} -avg of last 100 = ${Math.round(mean)} -min of last 100 = ${Math.round(min)} -max of last 100 = ${Math.round(max)} -`.trim(); - } -}; - -play(); \ No newline at end of file diff --git a/src/dungeon/mod.rs b/src/dungeon/mod.rs index 7064e77..484d72b 100644 --- a/src/dungeon/mod.rs +++ b/src/dungeon/mod.rs @@ -53,17 +53,14 @@ pub trait MapModifier { pub struct MapBuilder { generator: Box, modifiers: Vec>, - rng: StdRng, } impl MapBuilder { /// Create Map Builder with initial map generator pub fn new(generator : Box) -> 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(system_time.as_secs()) } } @@ -72,16 +69,25 @@ impl MapBuilder { self } + /// Build map using random number seeded with system time pub fn build_map(&mut self) -> Map { - let mut map = self.generator.generate_map(&mut self.rng); + let system_time = SystemTime::now().duration_since(UNIX_EPOCH).expect("Can't access system time"); + let mut rng = StdRng::seed_from_u64(system_time.as_millis() as u64); + self.build_map_with_rng(&mut rng) + } + + /// Build map using provided random number generator + pub fn build_map_with_rng(&mut self, rng: &mut StdRng) -> Map { + let mut map = self.generator.generate_map(rng); // Build additional layers in turn for modifier in self.modifiers.iter() { - map = modifier.modify_map(&mut self.rng, &map); + map = modifier.modify_map(rng, &map); } map } + } /// ------------------------------------------------------------------------------------------------