diff --git a/.github/workflows/cargo-build-test.yml b/.github/workflows/cargo-build-test.yml deleted file mode 100644 index cafaa58..0000000 --- a/.github/workflows/cargo-build-test.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Rust - -on: - push: - branches: [ "master" ] - pull_request: - branches: [ "master" ] - -env: - CARGO_TERM_COLOR: always - -jobs: - build: - - runs-on: ubuntu-22.04 - - steps: - - uses: actions/checkout@v3 - - name: Build - run: cargo build --verbose - - name: Run tests - run: cargo test --verbose diff --git a/.github/workflows/deploy wasm to gh-pages.yml b/.github/workflows/deploy wasm to gh-pages.yml deleted file mode 100644 index 1a5bb6f..0000000 --- a/.github/workflows/deploy wasm to gh-pages.yml +++ /dev/null @@ -1,34 +0,0 @@ -on: push -name: Build and deploy web page with WASM version -jobs: - build: - runs-on: ubuntu-latest - if: startsWith(github.ref, 'refs/tags/v') - steps: - - name: Checkout project - uses: actions/checkout@v3 - - name: Setup - Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly - target: wasm32-unknown-unknown - override: true - - name: Setup - wasm-bindgen - uses: jetli/wasm-bindgen-action@v0.2.0 - - name: Build, bind WASM - run: | - cargo build --release --target wasm32-unknown-unknown - wasm-bindgen target/wasm32-unknown-unknown/release/rust-rl.wasm --out-dir wasm --no-modules --no-typescript - - name: Publish web\ to gh-pages - env: - GITHUB_TOKEN: ${{ github.token }} - run: | - cd wasm/ - git init --initial-branch=master - git config user.name "GitHub Actions" - git config user.email "github-actions-bot@users.noreply.github.com" - git add . - - git commit -m "Deploy ${GITHUB_REPOSITORY} to ${GITHUB_REPOSITORY}:gh-pages" - git push --force "https://${GITHUB_ACTOR}:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" master:gh-pages - echo "Deploy complete" diff --git a/.gitignore b/.gitignore index 6d00d4c..529ab8d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,19 +1,11 @@ # Build files, documentation, benchmarks target -wasm/index.css -wasm/index.html -docs/gifs/* +docs/* # VSCode/IDE config files -Cargo.lock .vscode/* .rustfmt.toml .prettierignore -.prettierrc.json -# Save files, morgue files -savegame.json -morgue - -# A default user config will be written on first run, if not present -config.toml \ No newline at end of file +# Savegame +savegame.json \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..35d24e7 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,3358 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "ab_glyph_rasterizer" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "adler32" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" + +[[package]] +name = "ahash" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217" + +[[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + +[[package]] +name = "andrew" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c4afb09dd642feec8408e33f92f3ffc4052946f6b20f32fb99c1f58cd4fa7cf" +dependencies = [ + "bitflags 1.3.2", + "rusttype", + "walkdir", + "xdg", + "xml-rs", +] + +[[package]] +name = "android_glue" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "000444226fcff248f2bc4c7625be32c63caccfecc2723a2b9f78a7487a49c407" + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstyle" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" + +[[package]] +name = "anyhow" +version = "1.0.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + +[[package]] +name = "atom" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9ff149ed9780025acfdb36862d35b28856bb693ceb451259a7164442f22fdc3" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "bracket-algorithm-traits" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a13fb98ac33e6cd03fc035d8503f8a80b38c523737c9f84b400a4b9e065cfd3" +dependencies = [ + "bracket-geometry 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec", +] + +[[package]] +name = "bracket-algorithm-traits" +version = "0.8.7" +source = "git+https://github.com/amethyst/bracket-lib.git?rev=851f6f08675444fb6fa088b9e67bee9fd75554c6#851f6f08675444fb6fa088b9e67bee9fd75554c6" +dependencies = [ + "bracket-geometry 0.8.7 (git+https://github.com/amethyst/bracket-lib.git?rev=851f6f08675444fb6fa088b9e67bee9fd75554c6)", + "smallvec", +] + +[[package]] +name = "bracket-color" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c1d1b160817fb74eebedccd678055cd688d1a73dc1a14519fa30ff4c9a5bdee" +dependencies = [ + "byteorder", + "lazy_static", + "parking_lot 0.11.2", + "serde", +] + +[[package]] +name = "bracket-color" +version = "0.8.7" +source = "git+https://github.com/amethyst/bracket-lib.git?rev=851f6f08675444fb6fa088b9e67bee9fd75554c6#851f6f08675444fb6fa088b9e67bee9fd75554c6" +dependencies = [ + "lazy_static", + "parking_lot 0.12.1", + "serde", +] + +[[package]] +name = "bracket-embedding" +version = "0.8.7" +source = "git+https://github.com/amethyst/bracket-lib.git?rev=851f6f08675444fb6fa088b9e67bee9fd75554c6#851f6f08675444fb6fa088b9e67bee9fd75554c6" +dependencies = [ + "lazy_static", + "parking_lot 0.12.1", +] + +[[package]] +name = "bracket-geometry" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f31b525fcd65027885f3a1e3a250a5dd397d70de4a6a5a125f03e0bef951499" +dependencies = [ + "serde", + "ultraviolet 0.9.1", +] + +[[package]] +name = "bracket-geometry" +version = "0.8.7" +source = "git+https://github.com/amethyst/bracket-lib.git?rev=851f6f08675444fb6fa088b9e67bee9fd75554c6#851f6f08675444fb6fa088b9e67bee9fd75554c6" +dependencies = [ + "serde", + "ultraviolet 0.9.1", +] + +[[package]] +name = "bracket-lib" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43bbe13e39628e7f73673737c825001494dee7329cee8ee79dfeaa7cfd136dbf" +dependencies = [ + "bracket-algorithm-traits 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)", + "bracket-color 0.8.2", + "bracket-geometry 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)", + "bracket-noise 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)", + "bracket-pathfinding 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)", + "bracket-random 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)", + "bracket-terminal 0.8.5", +] + +[[package]] +name = "bracket-lib" +version = "0.8.7" +source = "git+https://github.com/amethyst/bracket-lib.git?rev=851f6f08675444fb6fa088b9e67bee9fd75554c6#851f6f08675444fb6fa088b9e67bee9fd75554c6" +dependencies = [ + "bracket-algorithm-traits 0.8.7 (git+https://github.com/amethyst/bracket-lib.git?rev=851f6f08675444fb6fa088b9e67bee9fd75554c6)", + "bracket-color 0.8.7", + "bracket-geometry 0.8.7 (git+https://github.com/amethyst/bracket-lib.git?rev=851f6f08675444fb6fa088b9e67bee9fd75554c6)", + "bracket-noise 0.8.7 (git+https://github.com/amethyst/bracket-lib.git?rev=851f6f08675444fb6fa088b9e67bee9fd75554c6)", + "bracket-pathfinding 0.8.7 (git+https://github.com/amethyst/bracket-lib.git?rev=851f6f08675444fb6fa088b9e67bee9fd75554c6)", + "bracket-random 0.8.7 (git+https://github.com/amethyst/bracket-lib.git?rev=851f6f08675444fb6fa088b9e67bee9fd75554c6)", + "bracket-terminal 0.8.7", +] + +[[package]] +name = "bracket-noise" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0b7443d0990c69db7a83f376f0101d684c20a911698e5f932bffbda2c8b08dd" +dependencies = [ + "bracket-random 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bracket-noise" +version = "0.8.7" +source = "git+https://github.com/amethyst/bracket-lib.git?rev=851f6f08675444fb6fa088b9e67bee9fd75554c6#851f6f08675444fb6fa088b9e67bee9fd75554c6" +dependencies = [ + "bracket-random 0.8.7 (git+https://github.com/amethyst/bracket-lib.git?rev=851f6f08675444fb6fa088b9e67bee9fd75554c6)", +] + +[[package]] +name = "bracket-pathfinding" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32ae7f2ebf242316ba10e84ca3b31af064b8c0c77402b2171a3104a2a77718fc" +dependencies = [ + "bracket-algorithm-traits 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)", + "bracket-geometry 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)", + "num-rational 0.4.1", + "smallvec", +] + +[[package]] +name = "bracket-pathfinding" +version = "0.8.7" +source = "git+https://github.com/amethyst/bracket-lib.git?rev=851f6f08675444fb6fa088b9e67bee9fd75554c6#851f6f08675444fb6fa088b9e67bee9fd75554c6" +dependencies = [ + "bracket-algorithm-traits 0.8.7 (git+https://github.com/amethyst/bracket-lib.git?rev=851f6f08675444fb6fa088b9e67bee9fd75554c6)", + "bracket-geometry 0.8.7 (git+https://github.com/amethyst/bracket-lib.git?rev=851f6f08675444fb6fa088b9e67bee9fd75554c6)", + "num-rational 0.4.1", + "smallvec", +] + +[[package]] +name = "bracket-random" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "437be61484077b1ddb57002ce3c96b7d03cbf500b5d15157ee7e67e22332c39b" +dependencies = [ + "getrandom", + "js-sys", + "lazy_static", + "rand", + "rand_xorshift", + "regex", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "bracket-random" +version = "0.8.7" +source = "git+https://github.com/amethyst/bracket-lib.git?rev=851f6f08675444fb6fa088b9e67bee9fd75554c6#851f6f08675444fb6fa088b9e67bee9fd75554c6" +dependencies = [ + "getrandom", + "js-sys", + "lazy_static", + "rand", + "rand_xorshift", + "regex", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "bracket-rex" +version = "0.8.7" +source = "git+https://github.com/amethyst/bracket-lib.git?rev=851f6f08675444fb6fa088b9e67bee9fd75554c6#851f6f08675444fb6fa088b9e67bee9fd75554c6" +dependencies = [ + "bracket-color 0.8.7", + "bracket-embedding", + "byteorder", + "flate2", +] + +[[package]] +name = "bracket-terminal" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460594df0b3364cae6ce5a5de4d787b293d20df0deffed4a942c10b1e3d50b1d" +dependencies = [ + "anyhow", + "bracket-color 0.8.2", + "bracket-geometry 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)", + "byteorder", + "console_error_panic_hook", + "flate2", + "glow 0.10.0", + "glutin 0.26.0", + "image 0.23.14", + "lazy_static", + "object-pool", + "parking_lot 0.11.2", + "rand", + "ultraviolet 0.8.1", + "wasm-bindgen", + "wasm-timer", + "web-sys", + "winit 0.24.0", +] + +[[package]] +name = "bracket-terminal" +version = "0.8.7" +source = "git+https://github.com/amethyst/bracket-lib.git?rev=851f6f08675444fb6fa088b9e67bee9fd75554c6#851f6f08675444fb6fa088b9e67bee9fd75554c6" +dependencies = [ + "anyhow", + "bracket-color 0.8.7", + "bracket-embedding", + "bracket-geometry 0.8.7 (git+https://github.com/amethyst/bracket-lib.git?rev=851f6f08675444fb6fa088b9e67bee9fd75554c6)", + "bracket-rex", + "console_error_panic_hook", + "glow 0.11.2", + "glutin 0.29.1", + "image 0.24.6", + "lazy_static", + "object-pool", + "parking_lot 0.12.1", + "rand", + "ultraviolet 0.9.1", + "wasm-bindgen", + "wasm-timer", + "web-sys", + "winit 0.26.1", + "winit 0.27.5", +] + +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + +[[package]] +name = "bytemuck" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "calloop" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b036167e76041694579972c28cf4877b4f92da222560ddb49008937b6a6727c" +dependencies = [ + "log", + "nix 0.18.0", +] + +[[package]] +name = "calloop" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf2eec61efe56aa1e813f5126959296933cf0700030e4314786c48779a66ab82" +dependencies = [ + "log", + "nix 0.22.3", +] + +[[package]] +name = "calloop" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52e0d00eb1ea24371a97d2da6201c6747a633dc6dc1988ef503403b4c59504a8" +dependencies = [ + "bitflags 1.3.2", + "log", + "nix 0.25.1", + "slotmap", + "thiserror", + "vec_map", +] + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cgl" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff" +dependencies = [ + "libc", +] + +[[package]] +name = "ciborium" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656" + +[[package]] +name = "ciborium-ll" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "4.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1640e5cc7fb47dbb8338fd471b105e7ed6c3cb2aeb00c2e067127ffd3764a05d" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98c59138d527eeaf9b53f35a77fcc1fad9d883116070c63d5de1c7dc7b00c72b" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" + +[[package]] +name = "cmake" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" +dependencies = [ + "cc", +] + +[[package]] +name = "cocoa" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c54201c07dcf3a5ca33fececb8042aed767ee4bfd5a0235a8ceabcda956044b2" +dependencies = [ + "bitflags 1.3.2", + "block", + "cocoa-foundation", + "core-foundation 0.9.3", + "core-graphics 0.22.3", + "foreign-types 0.3.2", + "libc", + "objc", +] + +[[package]] +name = "cocoa" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a" +dependencies = [ + "bitflags 1.3.2", + "block", + "cocoa-foundation", + "core-foundation 0.9.3", + "core-graphics 0.22.3", + "foreign-types 0.3.2", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "931d3837c286f56e3c58423ce4eba12d08db2374461a785c86f672b08b5650d6" +dependencies = [ + "bitflags 1.3.2", + "block", + "core-foundation 0.9.3", + "core-graphics-types", + "foreign-types 0.3.2", + "libc", + "objc", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen", +] + +[[package]] +name = "core-foundation" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" +dependencies = [ + "core-foundation-sys 0.7.0", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys 0.8.4", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "core-graphics" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3889374e6ea6ab25dba90bb5d96202f61108058361f6dc72e8b03e6f8bbe923" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.7.0", + "foreign-types 0.3.2", + "libc", +] + +[[package]] +name = "core-graphics" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.3", + "core-graphics-types", + "foreign-types 0.3.2", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bb142d41022986c1d8ff29103a1411c8a3dfad3552f87a4f8dc50d61d4f4e33" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.3", + "libc", +] + +[[package]] +name = "core-text" +version = "19.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d74ada66e07c1cefa18f8abfba765b486f250de2e4a999e5727fc0dd4b4a25" +dependencies = [ + "core-foundation 0.9.3", + "core-graphics 0.22.3", + "foreign-types 0.3.2", + "libc", +] + +[[package]] +name = "core-video-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34ecad23610ad9757664d644e369246edde1803fcb43ed72876565098a5d3828" +dependencies = [ + "cfg-if 0.1.10", + "core-foundation-sys 0.7.0", + "core-graphics 0.19.2", + "libc", + "objc", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "is-terminal", + "itertools", + "num-traits", + "once_cell", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils 0.8.16", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch", + "crossbeam-utils 0.8.16", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +dependencies = [ + "autocfg", + "cfg-if 1.0.0", + "crossbeam-utils 0.8.16", + "memoffset 0.9.0", + "scopeguard", +] + +[[package]] +name = "crossbeam-queue" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570" +dependencies = [ + "cfg-if 0.1.10", + "crossbeam-utils 0.7.2", + "maybe-uninit", +] + +[[package]] +name = "crossbeam-utils" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +dependencies = [ + "autocfg", + "cfg-if 0.1.10", + "lazy_static", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "crossfont" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21fd3add36ea31aba1520aa5288714dd63be506106753226d0eb387a93bc9c45" +dependencies = [ + "cocoa 0.24.1", + "core-foundation 0.9.3", + "core-foundation-sys 0.8.4", + "core-graphics 0.22.3", + "core-text", + "dwrote", + "foreign-types 0.5.0", + "freetype-rs", + "libc", + "log", + "objc", + "once_cell", + "pkg-config", + "servo-fontconfig", + "winapi 0.3.9", +] + +[[package]] +name = "cty" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" + +[[package]] +name = "darling" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858" +dependencies = [ + "darling_core 0.10.2", + "darling_macro 0.10.2", +] + +[[package]] +name = "darling" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" +dependencies = [ + "darling_core 0.13.4", + "darling_macro 0.13.4", +] + +[[package]] +name = "darling_core" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.9.3", + "syn 1.0.109", +] + +[[package]] +name = "darling_core" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.10.0", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72" +dependencies = [ + "darling_core 0.10.2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" +dependencies = [ + "darling_core 0.13.4", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "deflate" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" +dependencies = [ + "adler32", + "byteorder", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "dlib" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b11f15d1e3268f140f68d390637d5e76d849782d971ae7063e0da69fe9709a76" +dependencies = [ + "libloading 0.6.7", +] + +[[package]] +name = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading 0.8.0", +] + +[[package]] +name = "downcast-rs" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" + +[[package]] +name = "dwrote" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b" +dependencies = [ + "lazy_static", + "libc", + "serde", + "serde_derive", + "winapi 0.3.9", + "wio", +] + +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "equivalent" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1" + +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "expat-sys" +version = "2.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658f19728920138342f68408b7cf7644d90d4784353d8ebc32e7e8663dbe45fa" +dependencies = [ + "cmake", + "pkg-config", +] + +[[package]] +name = "fdeflate" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "flate2" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" +dependencies = [ + "crc32fast", + "miniz_oxide 0.7.1", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared 0.1.1", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared 0.3.1", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.23", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "freetype-rs" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74eadec9d0a5c28c54bb9882e54787275152a4e36ce206b45d7451384e5bf5fb" +dependencies = [ + "bitflags 1.3.2", + "freetype-sys", + "libc", +] + +[[package]] +name = "freetype-sys" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a37d4011c0cc628dfa766fcc195454f4b068d7afdc2adfd28861191d866e731a" +dependencies = [ + "cmake", + "libc", + "pkg-config", +] + +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +dependencies = [ + "bitflags 1.3.2", + "fuchsia-zircon-sys", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" + +[[package]] +name = "futures" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi", +] + +[[package]] +name = "gl_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +dependencies = [ + "khronos_api", + "log", + "xml-rs", +] + +[[package]] +name = "glow" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "945be163fdb893227410c8b44c2412dade922585b262d1daa6a7e96135217d4c" +dependencies = [ + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "glow" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8bd5877156a19b8ac83a29b2306fe20537429d318f3ff0a1a2119f8d9c61919" +dependencies = [ + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "glutin" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ae1cbb9176b9151c4ce03f012e3cd1c6c18c4be79edeaeb3d99f5d8085c5fa3" +dependencies = [ + "android_glue", + "cgl", + "cocoa 0.23.0", + "core-foundation 0.9.3", + "glutin_egl_sys", + "glutin_emscripten_sys", + "glutin_gles2_sys", + "glutin_glx_sys", + "glutin_wgl_sys", + "lazy_static", + "libloading 0.6.7", + "log", + "objc", + "osmesa-sys", + "parking_lot 0.11.2", + "wayland-client 0.28.6", + "wayland-egl 0.28.6", + "winapi 0.3.9", + "winit 0.24.0", +] + +[[package]] +name = "glutin" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "444c9ad294fdcaf20ccf6726b78f380b5450275540c9b68ab62f49726ad1c713" +dependencies = [ + "cgl", + "cocoa 0.24.1", + "core-foundation 0.9.3", + "glutin_egl_sys", + "glutin_gles2_sys", + "glutin_glx_sys", + "glutin_wgl_sys", + "libloading 0.7.4", + "log", + "objc", + "once_cell", + "osmesa-sys", + "parking_lot 0.12.1", + "raw-window-handle 0.5.2", + "wayland-client 0.29.5", + "wayland-egl 0.29.5", + "winapi 0.3.9", + "winit 0.27.5", +] + +[[package]] +name = "glutin_egl_sys" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68900f84b471f31ea1d1355567eb865a2cf446294f06cef8d653ed7bcf5f013d" +dependencies = [ + "gl_generator", + "winapi 0.3.9", +] + +[[package]] +name = "glutin_emscripten_sys" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80de4146df76e8a6c32b03007bc764ff3249dcaeb4f675d68a06caf1bac363f1" + +[[package]] +name = "glutin_gles2_sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8094e708b730a7c8a1954f4f8a31880af00eb8a1c5b5bf85d28a0a3c6d69103" +dependencies = [ + "gl_generator", + "objc", +] + +[[package]] +name = "glutin_glx_sys" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d93d0575865098580c5b3a423188cd959419912ea60b1e48e8b3b526f6d02468" +dependencies = [ + "gl_generator", + "x11-dl", +] + +[[package]] +name = "glutin_wgl_sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3da5951a1569dbab865c6f2a863efafff193a93caf05538d193e9e3816d21696" +dependencies = [ + "gl_generator", +] + +[[package]] +name = "half" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" + +[[package]] +name = "hashbrown" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96282e96bfcd3da0d3aa9938bedf1e50df3269b6db08b4876d2da0bb1a0841cf" +dependencies = [ + "ahash", + "autocfg", +] + +[[package]] +name = "hashbrown" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" + +[[package]] +name = "hermit-abi" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" + +[[package]] +name = "hibitset" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93a1bb8316a44459a7d14253c4d28dd7395cbd23cc04a68c46e851b8e46d64b1" +dependencies = [ + "atom", + "rayon", +] + +[[package]] +name = "home" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "image" +version = "0.23.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "jpeg-decoder 0.1.22", + "num-iter", + "num-rational 0.3.2", + "num-traits", + "png 0.16.8", +] + +[[package]] +name = "image" +version = "0.24.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527909aa81e20ac3a44803521443a765550f09b5130c2c2fa1ea59c2f8f50a3a" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "jpeg-decoder 0.3.0", + "num-rational 0.4.1", + "num-traits", + "png 0.17.9", +] + +[[package]] +name = "indexmap" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +dependencies = [ + "equivalent", + "hashbrown 0.14.0", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + +[[package]] +name = "is-terminal" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" +dependencies = [ + "hermit-abi", + "rustix", + "windows-sys 0.48.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jpeg-decoder" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2" + +[[package]] +name = "jpeg-decoder" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" + +[[package]] +name = "js-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "khronos_api" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "libloading" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "351a32417a12d5f7e82c368a66781e307834dae04c6ce0cd4456d52989229883" +dependencies = [ + "cfg-if 1.0.0", + "winapi 0.3.9", +] + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if 1.0.0", + "winapi 0.3.9", +] + +[[package]] +name = "libloading" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d580318f95776505201b28cf98eb1fa5e4be3b689633ba6a3e6cd880ff22d8cb" +dependencies = [ + "cfg-if 1.0.0", + "windows-sys 0.48.0", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" + +[[package]] +name = "lock_api" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "maybe-uninit" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memmap2" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b70ca2a6103ac8b665dc150b142ef0e4e89df640c9e6cf295d189c3caebe5a" +dependencies = [ + "libc", +] + +[[package]] +name = "memmap2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b6c2ebff6180198788f5db08d7ce3bc1d0b617176678831a7510825973e357" +dependencies = [ + "libc", +] + +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435" +dependencies = [ + "adler32", +] + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "0.6.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" +dependencies = [ + "cfg-if 0.1.10", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec", + "kernel32-sys", + "libc", + "log", + "miow", + "net2", + "slab", + "winapi 0.2.8", +] + +[[package]] +name = "mio" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.48.0", +] + +[[package]] +name = "mio-extras" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52403fe290012ce777c4626790c8951324a2b9e3316b3143779c72b029742f19" +dependencies = [ + "lazycell", + "log", + "mio 0.6.23", + "slab", +] + +[[package]] +name = "miow" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" +dependencies = [ + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", +] + +[[package]] +name = "mopa" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a785740271256c230f57462d3b83e52f998433a7062fc18f96d5999474a9f915" + +[[package]] +name = "ndk" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eb167c1febed0a496639034d0c76b3b74263636045db5489eee52143c246e73" +dependencies = [ + "jni-sys", + "ndk-sys 0.2.2", + "num_enum 0.4.3", + "thiserror", +] + +[[package]] +name = "ndk" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d868f654c72e75f8687572699cdabe755f03effbb62542768e995d5b8d699d" +dependencies = [ + "bitflags 1.3.2", + "jni-sys", + "ndk-sys 0.2.2", + "num_enum 0.5.11", + "thiserror", +] + +[[package]] +name = "ndk" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" +dependencies = [ + "bitflags 1.3.2", + "jni-sys", + "ndk-sys 0.4.1+23.1.7779620", + "num_enum 0.5.11", + "raw-window-handle 0.5.2", + "thiserror", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-glue" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdf399b8b7a39c6fb153c4ec32c72fd5fe789df24a647f229c239aa7adb15241" +dependencies = [ + "lazy_static", + "libc", + "log", + "ndk 0.2.1", + "ndk-macro 0.2.0", + "ndk-sys 0.2.2", +] + +[[package]] +name = "ndk-glue" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71bee8ea72d685477e28bd004cfe1bf99c754d688cd78cad139eae4089484d4" +dependencies = [ + "lazy_static", + "libc", + "log", + "ndk 0.5.0", + "ndk-context", + "ndk-macro 0.3.0", + "ndk-sys 0.2.2", +] + +[[package]] +name = "ndk-glue" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0434fabdd2c15e0aab768ca31d5b7b333717f03cf02037d5a0a3ff3c278ed67f" +dependencies = [ + "libc", + "log", + "ndk 0.7.0", + "ndk-context", + "ndk-macro 0.3.0", + "ndk-sys 0.4.1+23.1.7779620", + "once_cell", + "parking_lot 0.12.1", +] + +[[package]] +name = "ndk-macro" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d1c6307dc424d0f65b9b06e94f88248e6305726b14729fd67a5e47b2dc481d" +dependencies = [ + "darling 0.10.2", + "proc-macro-crate 0.1.5", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ndk-macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0df7ac00c4672f9d5aece54ee3347520b7e20f158656c7db2e6de01902eb7a6c" +dependencies = [ + "darling 0.13.4", + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ndk-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1bcdd74c20ad5d95aacd60ef9ba40fdf77f767051040541df557b7a9b2a2121" + +[[package]] +name = "ndk-sys" +version = "0.4.1+23.1.7779620" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "net2" +version = "0.2.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b13b648036a2339d06de780866fbdfda0dde886de7b3af2ddeba8b14f4ee34ac" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "nix" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83450fe6a6142ddd95fb064b746083fc4ef1705fe81f64a64e1d4b39f54a1055" +dependencies = [ + "bitflags 1.3.2", + "cc", + "cfg-if 0.1.10", + "libc", +] + +[[package]] +name = "nix" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa9b4819da1bc61c0ea48b63b7bc8604064dd43013e7cc325df098d49cd7c18a" +dependencies = [ + "bitflags 1.3.2", + "cc", + "cfg-if 1.0.0", + "libc", +] + +[[package]] +name = "nix" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4916f159ed8e5de0082076562152a76b7a1f64a01fd9d1e0fea002c37624faf" +dependencies = [ + "bitflags 1.3.2", + "cc", + "cfg-if 1.0.0", + "libc", + "memoffset 0.6.5", +] + +[[package]] +name = "nix" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +dependencies = [ + "bitflags 1.3.2", + "cfg-if 1.0.0", + "libc", + "memoffset 0.6.5", +] + +[[package]] +name = "nix" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if 1.0.0", + "libc", + "memoffset 0.6.5", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "num_enum" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca565a7df06f3d4b485494f25ba05da1435950f4dc263440eda7a6fa9b8e36e4" +dependencies = [ + "derivative", + "num_enum_derive 0.4.3", +] + +[[package]] +name = "num_enum" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" +dependencies = [ + "num_enum_derive 0.5.11", +] + +[[package]] +name = "num_enum_derive" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffa5a33ddddfee04c0283a7653987d634e880347e96b5b2ed64de07efb59db9d" +dependencies = [ + "proc-macro-crate 0.1.5", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "object-pool" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee9a3e7196d09ec86002b939f1576e8e446d58def8fd48fe578e2c72d5328d68" +dependencies = [ + "parking_lot 0.11.2", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + +[[package]] +name = "osmesa-sys" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88cfece6e95d2e717e0872a7f53a8684712ad13822a7979bc760b9c77ec0013b" +dependencies = [ + "shared_library", +] + +[[package]] +name = "owned_ttf_parser" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05e6affeb1632d6ff6a23d2cd40ffed138e82f1532571a26f527c8a284bb2fbb" +dependencies = [ + "ttf-parser", +] + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core 0.8.6", +] + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core 0.9.8", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" +dependencies = [ + "cfg-if 1.0.0", + "instant", + "libc", + "redox_syscall 0.2.16", + "smallvec", + "winapi 0.3.9", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall 0.3.5", + "smallvec", + "windows-targets", +] + +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "plotters" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" + +[[package]] +name = "plotters-svg" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "png" +version = "0.16.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "deflate", + "miniz_oxide 0.3.7", +] + +[[package]] +name = "png" +version = "0.17.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59871cc5b6cce7eaccca5a802b4173377a1c2ba90654246789a8fa2334426d11" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide 0.7.1", +] + +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", + "serde", +] + +[[package]] +name = "raw-window-handle" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28f55143d0548dad60bb4fbdc835a3d7ac6acc3324506450c5fdd6e42903a76" +dependencies = [ + "libc", + "raw-window-handle 0.4.3", +] + +[[package]] +name = "raw-window-handle" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b800beb9b6e7d2df1fe337c9e3d04e3af22a124460fb4c30fcc22c9117cefb41" +dependencies = [ + "cty", +] + +[[package]] +name = "raw-window-handle" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" + +[[package]] +name = "rayon" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils 0.8.16", + "num_cpus", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "regex" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89089e897c013b3deb627116ae56a6955a72b8bed395c9526af31c9fe528b484" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa250384981ea14565685dea16a9ccc4d1c541a13f82b9c168572264d1df8c56" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab07dc67230e4a4718e70fd5c20055a4334b121f1f9db8fe63ef39ce9b8c846" + +[[package]] +name = "rltk" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30587bc9361fc5600650d17ad6b9a91bb6a290f9d06a434dc48198d22ead7543" +dependencies = [ + "bracket-lib 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rust-llyrl" +version = "0.1.0" +dependencies = [ + "bracket-lib 0.8.7 (git+https://github.com/amethyst/bracket-lib.git?rev=851f6f08675444fb6fa088b9e67bee9fd75554c6)", + "criterion", + "lazy_static", + "regex", + "rltk", + "serde", + "serde_json", + "specs", + "specs-derive", +] + +[[package]] +name = "rustix" +version = "0.38.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac5ffa1efe7548069688cd7028f32591853cd7b5b756d41bcffd2353e4fc75b4" +dependencies = [ + "bitflags 2.3.3", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.48.0", +] + +[[package]] +name = "rusttype" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff8374aa04134254b7995b63ad3dc41c7f7236f69528b28553da7d72efaa967" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser", +] + +[[package]] +name = "ryu" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe232bdf6be8c8de797b22184ee71118d63780ea42ac85b61d1baa6d3b782ae9" + +[[package]] +name = "safe_arch" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1ff3d6d9696af502cc3110dacce942840fb06ff4514cad92236ecc455f2ce05" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "safe_arch" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62a7484307bd40f8f7ccbacccac730108f2cae119a3b11c74485b48aa9ea650f" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "sctk-adwaita" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61270629cc6b4d77ec1907db1033d5c2e1a404c412743621981a871dc9c12339" +dependencies = [ + "crossfont", + "log", + "smithay-client-toolkit 0.16.0", + "tiny-skia", +] + +[[package]] +name = "send_wrapper" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0eddf2e8f50ced781f288c19f18621fa72a3779e3cb58dbf23b07469b0abeb4" + +[[package]] +name = "serde" +version = "1.0.166" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d01b7404f9d441d3ad40e6a636a7782c377d2abdbe4fa2440e2edcc2f4f10db8" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.166" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dd83d6dde2b6b2d466e14d9d1acce8816dedee94f735eac6395808b3483c6d6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.23", +] + +[[package]] +name = "serde_json" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f1e14e89be7aa4c4b78bdbdc9eb5bf8517829a600ae8eaa39a6e1d960b5185c" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "servo-fontconfig" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7e3e22fe5fd73d04ebf0daa049d3efe3eae55369ce38ab16d07ddd9ac5c217c" +dependencies = [ + "libc", + "servo-fontconfig-sys", +] + +[[package]] +name = "servo-fontconfig-sys" +version = "5.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e36b879db9892dfa40f95da1c38a835d41634b825fbd8c4c418093d53c24b388" +dependencies = [ + "expat-sys", + "freetype-sys", + "pkg-config", +] + +[[package]] +name = "shared_library" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11" +dependencies = [ + "lazy_static", + "libc", +] + +[[package]] +name = "shred" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5f08237e667ac94ad20f8878b5943d91a93ccb231428446c57c21c57779016d" +dependencies = [ + "arrayvec", + "hashbrown 0.7.2", + "mopa", + "rayon", + "smallvec", + "tynm", +] + +[[package]] +name = "shrev" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ea33232fdcf1bf691ca33450e5a94dde13e1a8cbb8caabc5e4f9d761e10b1a" + +[[package]] +name = "simd-adler32" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f" + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "slotmap" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342" +dependencies = [ + "version_check", +] + +[[package]] +name = "smallvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" + +[[package]] +name = "smithay-client-toolkit" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4750c76fd5d3ac95fa3ed80fe667d6a3d8590a960e5b575b98eea93339a80b80" +dependencies = [ + "andrew", + "bitflags 1.3.2", + "calloop 0.6.5", + "dlib 0.4.2", + "lazy_static", + "log", + "memmap2 0.1.0", + "nix 0.18.0", + "wayland-client 0.28.6", + "wayland-cursor 0.28.6", + "wayland-protocols 0.28.6", +] + +[[package]] +name = "smithay-client-toolkit" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a28f16a97fa0e8ce563b2774d1e732dd5d4025d2772c5dba0a41a0f90a29da3" +dependencies = [ + "bitflags 1.3.2", + "calloop 0.9.3", + "dlib 0.5.2", + "lazy_static", + "log", + "memmap2 0.3.1", + "nix 0.22.3", + "pkg-config", + "wayland-client 0.29.5", + "wayland-cursor 0.29.5", + "wayland-protocols 0.29.5", +] + +[[package]] +name = "smithay-client-toolkit" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f307c47d32d2715eb2e0ece5589057820e0e5e70d07c247d1063e844e107f454" +dependencies = [ + "bitflags 1.3.2", + "calloop 0.10.6", + "dlib 0.5.2", + "lazy_static", + "log", + "memmap2 0.5.10", + "nix 0.24.3", + "pkg-config", + "wayland-client 0.29.5", + "wayland-cursor 0.29.5", + "wayland-protocols 0.29.5", +] + +[[package]] +name = "specs" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fff28a29366aff703d5da8a7e2c8875dc8453ac1118f842cbc0fa70c7db51240" +dependencies = [ + "crossbeam-queue", + "hashbrown 0.7.2", + "hibitset", + "log", + "rayon", + "serde", + "shred", + "shrev", + "tuple_utils", +] + +[[package]] +name = "specs-derive" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e23e09360f3d2190fec4222cd9e19d3158d5da948c0d1ea362df617dd103511" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "strsim" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59fb7d6d8281a51045d62b8eb3a7d1ce347b76f312af50cd3dc0af39c87c1737" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c16a64ba9387ef3fdae4f9c1a7f07a0997fce91985c0336f1ddc1822b3b37802" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d14928354b01c4d6a4f0e549069adef399a284e7995c7ccca94e8a07a5346c59" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.23", +] + +[[package]] +name = "tiny-skia" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "642680569bb895b16e4b9d181c60be1ed136fa0c9c7f11d004daf053ba89bf82" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if 1.0.0", + "png 0.17.9", + "safe_arch 0.5.2", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c114d32f0c2ee43d585367cb013dfaba967ab9f62b90d9af0d696e955e70fa6c" +dependencies = [ + "arrayref", + "bytemuck", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "tokio-executor" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671" +dependencies = [ + "crossbeam-utils 0.7.2", + "futures", +] + +[[package]] +name = "tokio-timer" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296" +dependencies = [ + "crossbeam-utils 0.7.2", + "futures", + "slab", + "tokio-executor", +] + +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" + +[[package]] +name = "toml_edit" +version = "0.19.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c500344a19072298cd05a7224b3c0c629348b78692bf48466c5238656e315a78" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "ttf-parser" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b3e06c9b9d80ed6b745c7159c40b311ad2916abb34a49e9be2653b90db0d8dd" + +[[package]] +name = "tuple_utils" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44834418e2c5b16f47bedf35c28e148db099187dd5feee6367fb2525863af4f1" + +[[package]] +name = "tynm" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b1b3ffc85961d3c7d038e30f5187622b33d971120c047ec8525b32602083645" +dependencies = [ + "nom", +] + +[[package]] +name = "ultraviolet" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b9e3507eba17043af05c8a72fce3ec2c24b58945f45732e71dbc6646d904a7" +dependencies = [ + "wide 0.6.5", +] + +[[package]] +name = "ultraviolet" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca0b28b9a6ce66d47e3c5666aa738c5ec5223fcdd4c263f3edc98ab6fef618b3" +dependencies = [ + "wide 0.7.10", +] + +[[package]] +name = "unicode-ident" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "walkdir" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.23", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.23", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" + +[[package]] +name = "wasm-timer" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa3e01d234bb71760e685cfafa5e2c96f8ad877c161a721646356651069e26ac" +dependencies = [ + "futures", + "js-sys", + "send_wrapper", + "tokio-timer", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wayland-client" +version = "0.28.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ab332350e502f159382201394a78e3cc12d0f04db863429260164ea40e0355" +dependencies = [ + "bitflags 1.3.2", + "downcast-rs", + "libc", + "nix 0.20.0", + "scoped-tls", + "wayland-commons 0.28.6", + "wayland-scanner 0.28.6", + "wayland-sys 0.28.6", +] + +[[package]] +name = "wayland-client" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715" +dependencies = [ + "bitflags 1.3.2", + "downcast-rs", + "libc", + "nix 0.24.3", + "scoped-tls", + "wayland-commons 0.29.5", + "wayland-scanner 0.29.5", + "wayland-sys 0.29.5", +] + +[[package]] +name = "wayland-commons" +version = "0.28.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21817947c7011bbd0a27e11b17b337bfd022e8544b071a2641232047966fbda" +dependencies = [ + "nix 0.20.0", + "once_cell", + "smallvec", + "wayland-sys 0.28.6", +] + +[[package]] +name = "wayland-commons" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902" +dependencies = [ + "nix 0.24.3", + "once_cell", + "smallvec", + "wayland-sys 0.29.5", +] + +[[package]] +name = "wayland-cursor" +version = "0.28.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be610084edd1586d45e7bdd275fe345c7c1873598caa464c4fb835dee70fa65a" +dependencies = [ + "nix 0.20.0", + "wayland-client 0.28.6", + "xcursor", +] + +[[package]] +name = "wayland-cursor" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661" +dependencies = [ + "nix 0.24.3", + "wayland-client 0.29.5", + "xcursor", +] + +[[package]] +name = "wayland-egl" +version = "0.28.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99ba1ab1e18756b23982d36f08856d521d7df45015f404a2d7c4f0b2d2f66956" +dependencies = [ + "wayland-client 0.28.6", + "wayland-sys 0.28.6", +] + +[[package]] +name = "wayland-egl" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "402de949f81a012926d821a2d659f930694257e76dd92b6e0042ceb27be4107d" +dependencies = [ + "wayland-client 0.29.5", + "wayland-sys 0.29.5", +] + +[[package]] +name = "wayland-protocols" +version = "0.28.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "286620ea4d803bacf61fa087a4242ee316693099ee5a140796aaba02b29f861f" +dependencies = [ + "bitflags 1.3.2", + "wayland-client 0.28.6", + "wayland-commons 0.28.6", + "wayland-scanner 0.28.6", +] + +[[package]] +name = "wayland-protocols" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6" +dependencies = [ + "bitflags 1.3.2", + "wayland-client 0.29.5", + "wayland-commons 0.29.5", + "wayland-scanner 0.29.5", +] + +[[package]] +name = "wayland-scanner" +version = "0.28.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce923eb2deb61de332d1f356ec7b6bf37094dc5573952e1c8936db03b54c03f1" +dependencies = [ + "proc-macro2", + "quote", + "xml-rs", +] + +[[package]] +name = "wayland-scanner" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53" +dependencies = [ + "proc-macro2", + "quote", + "xml-rs", +] + +[[package]] +name = "wayland-sys" +version = "0.28.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d841fca9aed7febf9bed2e9796c49bf58d4152ceda8ac949ebe00868d8f0feb8" +dependencies = [ + "dlib 0.5.2", + "lazy_static", + "pkg-config", +] + +[[package]] +name = "wayland-sys" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be12ce1a3c39ec7dba25594b97b42cb3195d54953ddb9d3d95a7c3902bc6e9d4" +dependencies = [ + "dlib 0.5.2", + "lazy_static", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wide" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46bbe7c604a27ca0b05c5503221e76da628225b568e6f1280b42dbad3b72d89b" +dependencies = [ + "bytemuck", + "safe_arch 0.5.2", +] + +[[package]] +name = "wide" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40018623e2dba2602a9790faba8d33f2ebdebf4b86561b83928db735f8784728" +dependencies = [ + "bytemuck", + "safe_arch 0.7.0", +] + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +dependencies = [ + "windows_aarch64_msvc 0.36.1", + "windows_i686_gnu 0.36.1", + "windows_i686_msvc 0.36.1", + "windows_x86_64_gnu 0.36.1", + "windows_x86_64_msvc 0.36.1", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "winit" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da4eda6fce0eb84bd0a33e3c8794eb902e1033d0a1d5a31bc4f19b1b4bbff597" +dependencies = [ + "bitflags 1.3.2", + "cocoa 0.24.1", + "core-foundation 0.9.3", + "core-graphics 0.22.3", + "core-video-sys", + "dispatch", + "instant", + "lazy_static", + "libc", + "log", + "mio 0.6.23", + "mio-extras", + "ndk 0.2.1", + "ndk-glue 0.2.1", + "ndk-sys 0.2.2", + "objc", + "parking_lot 0.11.2", + "percent-encoding", + "raw-window-handle 0.3.4", + "smithay-client-toolkit 0.12.3", + "wasm-bindgen", + "wayland-client 0.28.6", + "web-sys", + "winapi 0.3.9", + "x11-dl", +] + +[[package]] +name = "winit" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b43cc931d58b99461188607efd7acb2a093e65fc621f54cad78517a6063e73a" +dependencies = [ + "bitflags 1.3.2", + "cocoa 0.24.1", + "core-foundation 0.9.3", + "core-graphics 0.22.3", + "core-video-sys", + "dispatch", + "instant", + "lazy_static", + "libc", + "log", + "mio 0.8.8", + "ndk 0.5.0", + "ndk-glue 0.5.2", + "ndk-sys 0.2.2", + "objc", + "parking_lot 0.11.2", + "percent-encoding", + "raw-window-handle 0.4.3", + "smithay-client-toolkit 0.15.4", + "wasm-bindgen", + "wayland-client 0.29.5", + "wayland-protocols 0.29.5", + "web-sys", + "winapi 0.3.9", + "x11-dl", +] + +[[package]] +name = "winit" +version = "0.27.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb796d6fbd86b2fd896c9471e6f04d39d750076ebe5680a3958f00f5ab97657c" +dependencies = [ + "bitflags 1.3.2", + "cocoa 0.24.1", + "core-foundation 0.9.3", + "core-graphics 0.22.3", + "dispatch", + "instant", + "libc", + "log", + "mio 0.8.8", + "ndk 0.7.0", + "ndk-glue 0.7.0", + "objc", + "once_cell", + "parking_lot 0.12.1", + "percent-encoding", + "raw-window-handle 0.4.3", + "raw-window-handle 0.5.2", + "sctk-adwaita", + "smithay-client-toolkit 0.16.0", + "wasm-bindgen", + "wayland-client 0.29.5", + "wayland-protocols 0.29.5", + "web-sys", + "windows-sys 0.36.1", + "x11-dl", +] + +[[package]] +name = "winnow" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9482fe6ceabdf32f3966bfdd350ba69256a97c30253dc616fe0005af24f164e" +dependencies = [ + "memchr", +] + +[[package]] +name = "wio" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "xcursor" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "463705a63313cd4301184381c5e8042f0a7e9b4bb63653f216311d4ae74690b7" +dependencies = [ + "nom", +] + +[[package]] +name = "xdg" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "688597db5a750e9cad4511cb94729a078e274308099a0382b5b8203bbc767fee" +dependencies = [ + "home", +] + +[[package]] +name = "xml-rs" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a56c84a8ccd4258aed21c92f70c0f6dea75356b6892ae27c24139da456f9336" diff --git a/Cargo.toml b/Cargo.toml index 55d7645..543c203 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,18 +1,18 @@ [package] -name = "rust-rl" -version = "0.1.4" +name = "rust-llyrl" +version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +rltk = { version = "^0.8.7", features = ["serde"] } bracket-lib = { git = "https://github.com/amethyst/bracket-lib.git", rev = "851f6f08675444fb6fa088b9e67bee9fd75554c6", features = ["serde"] } regex = "1.3.6" specs = { version = "0.16.1", features = ["serde"] } specs-derive = "0.4.1" serde = { version = "1.0.93", features = ["derive"]} serde_json = "1.0.39" -toml = "0.5" lazy_static = "1.4.0" [dev-dependencies] @@ -20,12 +20,4 @@ criterion = { version = "^0.5" } [[bench]] name = "systems_benchmark" -harness = false - -# Enable max optimizations for dependencies, but not for our code: -[profile.dev.package."*"] -opt-level = 3 - -# Enable only a small amount of optimization in debug mode -# [profile.dev] -# opt-level = 1 \ No newline at end of file +harness = false \ No newline at end of file diff --git a/README.md b/README.md index c1809cf..fddb2fa 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,15 @@ -## a roguelike in rust, playable @ [llyw.co.uk/rl/](https://llyw.co.uk/rl/) +## a roguelike in rust, playable @ [llywelwyn.github.io](https://llywelwyn.github.io/) #### using _rltk/bracket-lib_, and _specs_ -[![Rust](https://github.com/Llywelwyn/rust-rl/actions/workflows/cargo-build-test.yml/badge.svg)](https://github.com/Llywelwyn/rust-rl/actions/workflows/cargo-build-test.yml) - -check out the page in the header for the wasm version, pick [a release](https://github.com/Llywelwyn/rust-rl/releases), or build manually with: - -`git clone https://github.com/Llywelwyn/rust-rl/ && cd rust-rl && cargo build --release`, - ![image](https://github.com/Llywelwyn/rust-rl/assets/82828093/b05e4f0b-2062-4abe-9fee-c679f9ef420d) +this year for roguelikedev does the complete tutorial, i'm following along with thebracket's [_roguelike tutorial - in rust_](https://bfnightly.bracketproductions.com). for most of the 8 weeks, i'll probably just be working through the content rather than diverging too much into doing my own thing, since it's lengthy and i'd rather finish in time. that said, the ultimate aim here is to strip out the vast majority of the existing entities and replace them with my own, using the systems and components from the tutorial as a jumping-off point for something of my own making. + +i'll try to remember to update the web version on my page at the end of every week + --- -
- boring details about the sprint where this project started
week 1 @@ -129,32 +125,3 @@ check out the page in the header for the wasm version, pick [a release](https:// - varying levels of being overweight, with the limit determined by strength, slowing entities down by ~25% per level over unencumbered. right now it's pretty forgiving, and i'd probably like it to stay that way. my ideal balance here would be roughly how it ends up in 5e DnD: everyone can carry a good supply of consumables, but strength characters usually don't carry a much higher quantity of items than anyone else, because the strongest armour is extremely heavy. sort of like a soft strength requirement for the heaviest gear - rather than requiring specific stats or levels to equip it, it's heavy enough that you need to be strong to lug it around. but if someone weaker wanted to, they could, they'd just have to leave some other items behind to do so. or take the speed penalty for being encumbered
- ---- - -
- week 7 - -- character creation! - - it doesn't look too pretty yet, but i managed to get it done in time. classes and ancestries are selectable, with ancestries determining some intrinsic bonuses (dwarves are hardy, catfolk are fast and have claws, etc.) and attribute maximums, and classes determining starting equipment and the actual attribute rolls. along with this, i expanded entity reactions - now a shared ancestry is taken into account first of all, and it checks faction if it doesn't manage to find anything. this means humans wont attack other humans, dwarves wont be attacked by gnomes and other dwarves, etc. - - ![char creation](https://github.com/Llywelwyn/rust-rl/assets/82828093/fb0e99d4-fd76-4995-8542-9690bb3d45dd) - - -- proper identification and beatitude - - item-ID is split from beatitudes as it should be, scrolls of identify and remove curse are in, and blessed/cursed effects are applied for a bunch of things. - - ![beatitude/buc](https://github.com/Llywelwyn/rust-rl/assets/82828093/387a9be4-225b-4b63-b9be-086973feb5ed) - - -- better AI - - straight from thebracket, with a handful of tweaks of my own, i've finally atomised AI into adjacent, visible, chase, flee, and default systems. most notably,rather than hostile mobs attacking everything and passive mobs doing nothing, every mob has a faction, and most mobs have an ancestry. like mentioned above, mobs will take all this into account when determining how they want to react to any other entity. i see a lot of places to expand on this in the future, like going into alignments and other increasingly-specific reasons why any entity might want to murder another. or make friends with them. taming comes to mind here. - -- an effects system - - instead of randomly doing things all around the codebase, everything is in the process of being moved over to an effects system. to put it very simply, there's a big list of every effect that needs to take place on the next tick, and each tick the queue is iterated through, the next effect is checked against a list of every entity that died this turn to make sure that it should still take place (no dead mobs still getting their attacks off), and then it makes the event happen if appropriate. if not, it just gets tossed out. it's all super modular, so effects can pretty much be applied to everything. the same damage and targeting effects work for item use and traps going off, or an entity swinging their sword, for example. i made use of this new system by adding in some aoe scrolls, like mass function and mass healing. see below for blindness improving telepathy range, and nice new particle effects. - - ![blindness](https://github.com/Llywelwyn/rust-rl/assets/82828093/800f7d36-3fa7-42b7-b80f-247e0d56d014) - ![squares](https://github.com/Llywelwyn/rust-rl/assets/82828093/b752e1cb-340d-475d-84ae-68fdb4977a80) - -
-
diff --git a/benches/systems_benchmark.rs b/benches/systems_benchmark.rs index c750fed..3bde091 100644 --- a/benches/systems_benchmark.rs +++ b/benches/systems_benchmark.rs @@ -1,12 +1,12 @@ -use criterion::{ black_box, criterion_group, criterion_main, Criterion }; -use bracket_lib::prelude::*; +use criterion::{black_box, criterion_group, criterion_main, Criterion}; +use rltk::RGB; /// Benchmarks methods from rltk used to desaturate non-visible tiles. // Greyscale is significantly faster, but generally looks worse - the // third alternative is directly setting the desaturated value, if it // is known in advance. fn nonvisible_benchmark(c: &mut Criterion) { - let bg = black_box(RGB::from_f32(0.4, 0.0, 0.0)); + let bg = black_box(RGB::from_f32(0.4, 0., 0.)); c.bench_function("rgb -> greyscale", |b| b.iter(|| bg.to_greyscale())); c.bench_function("rgb -> desaturate", |b| b.iter(|| bg.desaturate())); diff --git a/changelog.md b/changelog.md deleted file mode 100644 index 4976351..0000000 --- a/changelog.md +++ /dev/null @@ -1,20 +0,0 @@ -## v0.1.4 -### added -- **overmap**: bare, but exists. player now starts on the overworld, and can move to local maps (like the old starting town) via >. can leave local maps back to the overmap by walking out of the map boundaries. -- **intrinsics**: speed, regeneration -- **damage types**: immunities, weaknesses, and resistances -- **full keyboard support**: examining and targeting can now be done via keyboard only -- **a config file** read at runtime, unfortunately not compatible with WASM builds yet -- **morgue files**: y/n prompt to write a morgue file on death to /morgue/foo.txt, or to the console for WASM builds -- **dungeon features**: just the basics so far. a grassy, forested treant room, some barracks, etc. -- **named maps**: "Town", "Dungeon" -- **map messages/hints**: "You hear <...>." -### changed -- **colour offsets** are now per-tile (and per-theme) instead of +-% globally. i.e. varying fg/bg offset on a per-tiletype basis -- **chatlog colours** are now consistent -### fixed -- negative starting mana -- status effects only ticking if mob turn aligned with turnclock turn -- map params not being saved on map transition -- mob turns not awaiting the particle queue (mobs moving around mid-animation) -- mobs not re-pathing if their path was blocked, causing traffic jams diff --git a/docs/ascii_encyclopedia.txt b/docs/ascii_encyclopedia.txt deleted file mode 100644 index 2972c1e..0000000 --- a/docs/ascii_encyclopedia.txt +++ /dev/null @@ -1,35 +0,0 @@ -a - insects A - -b - B - -c - chickens C - -d - canines D - -e - E - -f - felines F - -g - goblins G - gnomes -h - humanoids H - -i - I - -j - J - -k - kobolds K - -l - L - -m - M - -n - N - -o - orcs O - ogres -p - P - -q - quadrupeds Q - -r - rats R - -s - spiders S - -t - T - -u - horses U - -v - V - -w - W - -x - X - -y - Y - -z - zombies Z - -@ - humans - -notes -1. zombie takes priority over the creature's original glyph -2. @ is for human-sized creatures - i.e. humans, changelings - h is used for everything else - i.e. dwarfs, halflings -3. purple is reserved for captain/leader entiites - i.e. orc captain -4. similar creatures use similar colours - i.e. little dog, dog, large dog - diff --git a/docs/combat_system.txt b/docs/combat_system.txt deleted file mode 100644 index 44e2f65..0000000 --- a/docs/combat_system.txt +++ /dev/null @@ -1,55 +0,0 @@ -DETERMINING IF AN ATTACK HITS OR NOT: - -1. Whenever someone makes an attack, a flat 1d20 attack roll is made. -2. A target number is generated, depending on the mode of attack, made - up of some combination of the following: - - ATTACKER'S HIT BONUSES: - - level - - any relevant str/dex attribute bonuses - - any relevant skill modifiers - - any to-hit modifiers from equipment - - any to-hit modifiers from status effects - - DEFENDER'S ARMOUR CLASS*: - - base armour class - - any relevant skill modifiers - - any ac modifiers from equipment - - any ac modifiers from status effects - - MISCELLANEOUS MODIFIERS: - - melee attacks always have a +1 bonus - - monsters gain a +10 bonus to-hit players -3. The attack roll is compared against the target number. -4. If the attack roll is less than the target, the attack hits.** -5. If an attack hits, it deals: - - a roll of the weapon's damage die - - plus any relevant attribute bonuses - - plus any relevant skill modifiers - - MINUS a roll of the defender's AC, if the AC is negative. - -notes - * when AC is less than 0, it is treated as a random number from -1 to the value. e.g. -10 AC could be anywhere from -1 to -10. - ** functionally identical to rolling 1d20 + to-hit, and needing to roll higher than the defender's AC. this system just ends up - being easier to work with when involving additional modifiers, as you don't need to decide if they're a to-hit bonus or an - AC modifier -- they just always get added/subtracted from the target number that must be rolled against. - -Simple example, with positive AC: -- You have an AC of 4, and the monster has a +1 to-hit, with a 1d8 damage weapon. -- The monster must roll less than 10 (monster v. player) + 1 (to-hit) + 4 (ac) = 15 to hit you. -- The monster has a 70% hit chance. -- It rolls a 12, which is lower than 15, so it hits. -- It rolls 1d8 for damage, and gets a 6. -- You take 6 points of damage. - -Complex example, with negative AC: -- You have an AC of -14, and the monster has a +3 to-hit, with a 1d8 damage weapon. -- The monster must roll less than 10 (monster v. player) + 3 (to-hit) - 1d14 (ac) to hit you. -- At best (AC rolls a 1), the monster must roll less than 12 to hit you. 55% hit chance. -- At worst (AC rolls a 14), the monster must roll less than -1 to hit you. Impossible. -- It rolls a 9, and your AC rolls a 2. 9 is less than 11 (10 + 3 - 2), so it hits. -- It rolls 1d8 for damage, and gets a 6. -bloodstains: if starts on bloodied tile, remove blood + heal, gain xp, grow (little dog -> dog), etc. -- You have negative AC, so you roll 1d14 for damage reduction, and get an 8. -- The total damage is 6 - 8 = -2, but damage can't be negative, so you take 1 point of damage. - -tl;dr -1. Lower AC is better -2. Aim for 0 AC - it's an important breakpoint. Every point of AC before 0 counts for a lot. diff --git a/raws/ancestries.json b/raws/ancestries.json deleted file mode 100644 index 932e488..0000000 --- a/raws/ancestries.json +++ /dev/null @@ -1,22 +0,0 @@ -[ - { - "id": "human", - "allies": [] - }, - { - "id": "elf", - "allies": [] - }, - { - "id": "dwarf", - "allies": ["gnome"] - }, - { - "id": "gnome", - "allies": ["dwarf"] - }, - { - "id": "catfolk", - "allies": [] - } -] diff --git a/raws/factions.json b/raws/factions.json deleted file mode 100644 index 136f3dc..0000000 --- a/raws/factions.json +++ /dev/null @@ -1,26 +0,0 @@ -[ - { - "id": "player", - "responses": {} - }, - { - "id": "mindless", - "responses": { "default": "attack" } - }, - { - "id": "neutral", - "responses": { "default": "ignore", "hostile": "flee", "mindless": "flee", "carnivore": "flee" } - }, - { - "id": "hostile", - "responses": { "default": "attack", "hostile": "ignore" } - }, - { - "id": "herbivore", - "responses": { "default": "flee", "herbivores": "ignore" } - }, - { - "id": "carnivore", - "responses": { "default": "ignore", "herbivores": "attack", "player": "attack", "neutral": "attack" } - } -] diff --git a/raws/items.json b/raws/items.json index 7599afe..a14c346 100644 --- a/raws/items.json +++ b/raws/items.json @@ -3,212 +3,113 @@ "id": "potion_health", "name": { "name": "potion of health", "plural": "potions of health" }, "renderable": { "glyph": "!", "fg": "#FF00FF", "bg": "#000000", "order": 2 }, - "class": "potion", "weight": 1, "value": 50, - "flags": ["CONSUMABLE", "DESTRUCTIBLE", "STACKABLE"], - "effects": { "heal": "4d4+2" }, - "magic": { "class": "uncommon", "naming": "potion" } + "flags": ["CONSUMABLE", "DESTRUCTIBLE"], + "effects": { "provides_healing": "2d4+2" }, + "magic": { "class": "common", "naming": "potion" } }, { "id": "potion_health_weak", "name": { "name": "potion of lesser health", "plural": "potions of lesser health" }, "renderable": { "glyph": "!", "fg": "#FF00FF", "bg": "#000000", "order": 2 }, - "class": "potion", "weight": 1, "value": 25, - "flags": ["CONSUMABLE", "DESTRUCTIBLE", "STACKABLE"], - "effects": { "heal": "2d4+2" }, - "magic": { "class": "uncommon", "naming": "potion" } - }, - { - "id": "scroll_identify", - "name": { "name": "scroll of identify", "plural": "scrolls of identify" }, - "renderable": { "glyph": "?", "fg": "#0FFFFF", "bg": "#000000", "order": 2 }, - "class": "scroll", - "weight": 0.5, - "value": 100, - "flags": ["CONSUMABLE", "DESTRUCTIBLE", "STACKABLE", "IDENTIFY"], - "magic": { "class": "uncommon", "naming": "scroll" } - }, - { - "id": "scroll_removecurse", - "name": { "name": "scroll of remove curse", "plural": "scrolls of remove curse" }, - "renderable": { "glyph": "?", "fg": "#0FFFFF", "bg": "#000000", "order": 2 }, - "class": "scroll", - "weight": 0.5, - "value": 200, - "flags": ["CONSUMABLE", "DESTRUCTIBLE", "STACKABLE", "REMOVE_CURSE"], - "magic": { "class": "rare", "naming": "scroll" } - }, - { - "id": "scroll_health", - "name": { "name": "scroll of healing word", "plural": "scrolls of healing word" }, - "renderable": { "glyph": "?", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, - "class": "scroll", - "weight": 0.5, - "value": 50, - "flags": ["CONSUMABLE", "DESTRUCTIBLE", "STACKABLE"], - "effects": { "particle_line": "*;-;#53f06d;75.0;#f9ff9f;100.0", "ranged": "12", "heal": "1d4+2" }, - "magic": { "class": "uncommon", "naming": "scroll" } - }, - { - "id": "scroll_mass_health", - "name": { "name": "scroll of mass healing word", "plural": "scrolls of mass healing word" }, - "renderable": { "glyph": "?", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, - "class": "scroll", - "weight": 0.5, - "value": 200, - "flags": ["CONSUMABLE", "DESTRUCTIBLE", "STACKABLE"], - "effects": { "particle": "*;#53f06d;200.0", "ranged": "12", "aoe": "3", "heal": "1d4+2" }, - "magic": { "class": "rare", "naming": "scroll" } + "flags": ["CONSUMABLE", "DESTRUCTIBLE"], + "effects": { "provides_healing": "1d4+2" }, + "magic": { "class": "common", "naming": "potion" } }, { "id": "scroll_magicmissile", "name": { "name": "scroll of magic missile", "plural": "scrolls of magic missile" }, "renderable": { "glyph": "?", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, - "class": "scroll", "weight": 0.5, "value": 50, - "flags": ["CONSUMABLE", "DESTRUCTIBLE", "STACKABLE"], - "effects": { "particle_line": "*;-;#00b7ff;75.0;#f4fc83;100.0", "ranged": "12", "damage": "3d4+3;magic" }, - "magic": { "class": "uncommon", "naming": "scroll" } - }, - { - "id": "scroll_embers", - "name": { "name": "scroll of embers", "plural": "scrolls of embers" }, - "renderable": { "glyph": "?", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, - "class": "scroll", - "weight": 0.5, - "value": 100, - "flags": ["CONSUMABLE", "DESTRUCTIBLE", "STACKABLE"], - "effects": { "particle": "*;#FFA500;200.0", "ranged": "10", "damage": "4d6;fire", "aoe": "2" }, - "magic": { "class": "uncommon", "naming": "scroll" } + "flags": ["CONSUMABLE", "DESTRUCTIBLE"], + "effects": { "ranged": "12", "damage": "3d4+3" }, + "magic": { "class": "common", "naming": "scroll" } }, { "id": "scroll_fireball", "name": { "name": "scroll of fireball", "plural": "scrolls of fireball" }, "renderable": { "glyph": "?", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, - "class": "scroll", "weight": 0.5, - "value": 200, - "flags": ["CONSUMABLE", "DESTRUCTIBLE", "STACKABLE"], - "effects": { - "particle_burst": "▓;*;~;#FFA500;#000000;500.0;#ffd381;60.0", - "ranged": "10", - "damage": "8d6;fire", - "aoe": "3" - }, + "value": 150, + "flags": ["CONSUMABLE", "DESTRUCTIBLE"], + "effects": { "ranged": "10", "damage": "8d6", "aoe": "3" }, + "magic": { "class": "rare", "naming": "scroll" } + }, + { + "id": "scroll_fireball_c", + "name": { "name": "cursed scroll of fireball", "plural": "cursed scrolls of fireball" }, + "renderable": { "glyph": "?", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, + "weight": 0.5, + "value": 150, + "flags": ["CONSUMABLE", "DESTRUCTIBLE", "CURSED"], + "effects": { "ranged": "10", "damage": "8d6", "aoe": "3" }, "magic": { "class": "rare", "naming": "scroll" } }, { "id": "scroll_confusion", "name": { "name": "scroll of confusion", "plural": "scrolls of confusion" }, "renderable": { "glyph": "?", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, - "class": "scroll", "weight": 0.5, "value": 100, - "flags": ["CONSUMABLE", "DESTRUCTIBLE", "STACKABLE"], - "effects": { "particle_line": "*;-;#ad56a6;75.0;#cacaca;100.0", "ranged": "10", "confusion": "4" }, + "flags": ["CONSUMABLE", "DESTRUCTIBLE"], + "effects": { "ranged": "10", "confusion": "4" }, "magic": { "class": "uncommon", "naming": "scroll" } }, - { - "id": "scroll_mass_confusion", - "name": { "name": "scroll of mass confusion", "plural": "scrolls of mass confusion" }, - "renderable": { "glyph": "?", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, - "class": "scroll", - "weight": 0.5, - "value": 200, - "flags": ["CONSUMABLE", "DESTRUCTIBLE", "STACKABLE"], - "effects": { "particle": "*;#ad56a6;200.0", "ranged": "10", "aoe": "3", "confusion": "3" }, - "magic": { "class": "veryrare", "naming": "scroll" } - }, { "id": "scroll_magicmap", "name": { "name": "scroll of magic mapping", "plural": "scrolls of magic mapping" }, "renderable": { "glyph": "?", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, - "class": "scroll", "weight": 0.5, "value": 50, - "flags": ["CONSUMABLE", "DESTRUCTIBLE", "STACKABLE", "MAGICMAP"], - "effects": {}, + "flags": ["CONSUMABLE", "DESTRUCTIBLE"], + "effects": { "magicmapper": "" }, + "magic": { "class": "common", "naming": "scroll" } + }, + { + "id": "scroll_magicmap_c", + "name": { "name": "cursed scroll of magic mapping", "plural": "cursed scrolls of magic mapping" }, + "renderable": { "glyph": "?", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, + "weight": 0.5, + "value": 50, + "flags": ["CONSUMABLE", "DESTRUCTIBLE", "CURSED"], + "effects": { "magicmapper": "" }, "magic": { "class": "common", "naming": "scroll" } }, { "id": "equip_dagger", "name": { "name": "dagger", "plural": "daggers" }, "renderable": { "glyph": ")", "fg": "#808080", "bg": "#000000", "order": 2 }, - "class": "weapon", "weight": 1, "value": 2, - "flags": ["EQUIP_MELEE"], - "equip": { "flag": "FINESSE", "damage": "1d4", "to_hit": 0 } + "flags": ["EQUIP_MELEE", "FINESSE"], + "effects": { "base_damage": "1d4" } }, { "id": "equip_shortsword", "name": { "name": "shortsword", "plural": "shortswords" }, "renderable": { "glyph": ")", "fg": "#C0C0C0", "bg": "#000000", "order": 2 }, - "class": "weapon", "weight": 2, "value": 10, - "flags": ["EQUIP_MELEE"], - "equip": { "flag": "STRENGTH", "damage": "1d6", "to_hit": 0 } - }, - { - "id": "equip_rapier", - "name": { "name": "rapier", "plural": "rapiers" }, - "renderable": { "glyph": ")", "fg": "#C0C0C0", "bg": "#000000", "order": 2 }, - "class": "weapon", - "weight": 2, - "value": 10, - "flags": ["EQUIP_MELEE"], - "equip": { "flag": "FINESSE", "damage": "1d8", "to_hit": 0 } - }, - { - "id": "equip_pitchfork", - "name": { "name": "pitchfork", "plural": "pitchforks" }, - "renderable": { "glyph": ")", "fg": "#C0C0C0", "bg": "#000000", "order": 2 }, - "class": "weapon", - "weight": 2, - "value": 5, - "flags": ["EQUIP_MELEE"], - "equip": { "flag": "FINESSE", "damage": "1d6", "to_hit": 0 } - }, - { - "id": "equip_sickle", - "name": { "name": "sickle", "plural": "sickles" }, - "renderable": { "glyph": ")", "fg": "#C0C0C0", "bg": "#000000", "order": 2 }, - "class": "weapon", - "weight": 2, - "value": 5, - "flags": ["EQUIP_MELEE"], - "equip": { "flag": "FINESSE", "damage": "1d6", "to_hit": 0 } - }, - { - "id": "equip_handaxe", - "name": { "name": "handaxe", "plural": "handaxes" }, - "renderable": { "glyph": ")", "fg": "#C0C0C0", "bg": "#000000", "order": 2 }, - "class": "weapon", - "weight": 2, - "value": 5, - "flags": ["EQUIP_MELEE"], - "equip": { "flag": "STRENGTH", "damage": "1d6", "to_hit": 0 } + "flags": ["EQUIP_MELEE", "STRENGTH"], + "effects": { "base_damage": "1d6" } }, { "id": "equip_longsword", "name": { "name": "longsword", "plural": "longswords" }, "renderable": { "glyph": ")", "fg": "#FFF8DC", "bg": "#000000", "order": 2 }, - "class": "weapon", "weight": 3, "value": 15, - "flags": ["EQUIP_MELEE"], - "equip": { "flag": "STRENGTH", "damage": "1d8", "to_hit": 0 } + "flags": ["EQUIP_MELEE", "STRENGTH"], + "effects": { "base_damage": "1d8" } }, { "id": "equip_smallshield", "name": { "name": "buckler", "plural": "bucklers" }, "renderable": { "glyph": "[", "fg": "#808080", "bg": "#000000", "order": 2 }, - "class": "armour", "weight": 2, "value": 5, "flags": ["EQUIP_SHIELD"], @@ -218,27 +119,24 @@ "id": "equip_mediumshield", "name": { "name": "medium shield", "plural": "medium shields" }, "renderable": { "glyph": "[", "fg": "#C0C0C0", "bg": "#000000", "order": 2 }, - "class": "armour", "weight": 6, "value": 10, "flags": ["EQUIP_SHIELD"], - "effects": { "ac": "2", "to_hit": "-1" } + "effects": { "ac": "2", "melee_power_bonus": "-1" } }, { "id": "equip_largeshield", "name": { "name": "large shield", "plural": "large shields" }, "renderable": { "glyph": "[", "fg": "#FFF8DC", "bg": "#000000", "order": 2 }, - "class": "armour", "weight": 12, "value": 35, "flags": ["EQUIP_SHIELD"], - "effects": { "ac": "3", "to_hit": "-2" } + "effects": { "ac": "4", "melee_power_bonus": "-2" } }, { "id": "equip_body_weakleather", "name": { "name": "leather jacket", "plural": "leather jackets" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, - "class": "armour", "weight": 8, "value": 5, "flags": ["EQUIP_BODY"], @@ -248,7 +146,6 @@ "id": "equip_body_leather", "name": { "name": "leather chestpiece", "plural": "leather chestpiece" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, - "class": "armour", "weight": 10, "value": 10, "flags": ["EQUIP_BODY"], @@ -258,7 +155,6 @@ "id": "equip_body_studdedleather", "name": { "name": "studded leather chestpiece", "plural": "studded leather chestpieces" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, - "class": "armour", "weight": 13, "value": 45, "flags": ["EQUIP_BODY"], @@ -268,7 +164,6 @@ "id": "equip_body_ringmail_o", "name": { "name": "orcish ring mail", "plural": "orcish ring mail" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, - "class": "armour", "weight": 45, "value": 50, "flags": ["EQUIP_BODY"], @@ -278,7 +173,6 @@ "id": "equip_body_ringmail", "name": { "name": "ring mail", "plural": "ring mail" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, - "class": "armour", "weight": 45, "value": 70, "flags": ["EQUIP_BODY"], @@ -288,7 +182,6 @@ "id": "equip_head_leather", "name": { "name": "leather cap", "plural": "leather caps" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, - "class": "armour", "weight": 2, "value": 10, "flags": ["EQUIP_HEAD"], @@ -298,7 +191,6 @@ "id": "equip_head_elvish", "name": { "name": "elvish leather helm", "plural": "elvish leather helms" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, - "class": "armour", "weight": 2, "value": 25, "flags": ["EQUIP_HEAD"], @@ -308,7 +200,6 @@ "id": "equip_head_o", "name": { "name": "orcish helm", "plural": "orcish helm" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, - "class": "armour", "weight": 6, "value": 25, "flags": ["EQUIP_HEAD"], @@ -318,7 +209,6 @@ "id": "equip_head_iron", "name": { "name": "iron helm", "plural": "iron helm" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, - "class": "armour", "weight": 10, "value": 45, "flags": ["EQUIP_HEAD"], @@ -328,7 +218,6 @@ "id": "equip_feet_leather", "name": { "name": "leather shoes", "plural": "leather shoes" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, - "class": "armour", "weight": 2, "value": 10, "flags": ["EQUIP_FEET"] @@ -337,7 +226,6 @@ "id": "equip_feet_elvish", "name": { "name": "elvish leather shoes", "plural": "elvish leather shoes" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, - "class": "armour", "weight": 2, "value": 25, "flags": ["EQUIP_FEET"], @@ -347,7 +235,6 @@ "id": "equip_feet_o", "name": { "name": "orcish boots", "plural": "orcish boots" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, - "class": "armour", "weight": 6, "value": 25, "flags": ["EQUIP_FEET"], @@ -357,7 +244,6 @@ "id": "equip_feet_iron", "name": { "name": "iron boots", "plural": "iron boots" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, - "class": "armour", "weight": 10, "value": 45, "flags": ["EQUIP_FEET"], @@ -367,7 +253,6 @@ "id": "equip_neck_protection", "name": { "name": "amulet of protection", "plural": "amulets of protection" }, "renderable": { "glyph": "\"", "fg": "#aa6000", "bg": "#000000", "order": 2 }, - "class": "amulet", "weight": 1, "value": 200, "flags": ["EQUIP_NECK"], @@ -377,7 +262,6 @@ "id": "equip_back_protection", "name": { "name": "cloak of protection", "plural": "cloaks of protection" }, "renderable": { "glyph": "[", "fg": "#aa6000", "bg": "#000000", "order": 2 }, - "class": "armour", "weight": 1, "value": 200, "flags": ["EQUIP_BACK"], @@ -387,32 +271,29 @@ "id": "wand_magicmissile", "name": { "name": "wand of magic missile", "plural": "wands of magic missile" }, "renderable": { "glyph": "/", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, - "class": "wand", "weight": 2, "value": 100, - "flags": ["CHARGES"], - "effects": { "ranged": "12", "damage": "3d4+3;magic" }, + "flags": ["WAND"], + "effects": { "ranged": "12", "damage": "10" }, "magic": { "class": "uncommon", "naming": "wand" } }, { "id": "wand_fireball", "name": { "name": "wand of fireball", "plural": "wands of fireball" }, "renderable": { "glyph": "/", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, - "class": "wand", "weight": 2, "value": 300, - "flags": ["CHARGES"], - "effects": { "ranged": "10", "damage": "8d6;fire", "aoe": "3" }, + "flags": ["WAND"], + "effects": { "ranged": "10", "damage": "15", "aoe": "3" }, "magic": { "class": "rare", "naming": "wand" } }, { "id": "wand_confusion", "name": { "name": "wand of confusion", "plural": "wands of confusion" }, "renderable": { "glyph": "/", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, - "class": "wand", "weight": 2, "value": 200, - "flags": ["CHARGES"], + "flags": ["WAND"], "effects": { "ranged": "10", "confusion": "4" }, "magic": { "class": "uncommon", "naming": "wand" } }, @@ -420,29 +301,26 @@ "id": "wand_digging", "name": { "name": "wand of digging", "plural": "wands of digging" }, "renderable": { "glyph": "/", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, - "class": "wand", "weight": 2, "value": 300, - "flags": ["CHARGES", "DIGGER"], - "effects": { "ranged": "10" }, + "flags": ["WAND"], + "effects": { "ranged": "10", "digger": "" }, "magic": { "class": "rare", "naming": "wand" } }, { "id": "food_rations", "name": { "name": "rations", "plural": "rations" }, "renderable": { "glyph": "%", "fg": "#FFA07A", "bg": "#000000", "order": 2 }, - "class": "comestible", "weight": 1, "value": 1, - "flags": ["FOOD", "CONSUMABLE", "STACKABLE"] + "flags": ["FOOD", "CONSUMABLE"] }, { "id": "food_apple", "name": { "name": "apple", "plural": "apples" }, "renderable": { "glyph": "%", "fg": "#00FF00", "bg": "#000000", "order": 2 }, - "class": "comestible", "weight": 0.5, "value": 1, - "flags": ["FOOD", "CONSUMABLE", "STACKABLE"] + "flags": ["FOOD", "CONSUMABLE"] } ] diff --git a/raws/mobs.json b/raws/mobs.json index ddd6556..177bace 100644 --- a/raws/mobs.json +++ b/raws/mobs.json @@ -3,7 +3,7 @@ "id": "npc_barkeep", "name": "barkeep", "renderable": { "glyph": "@", "fg": "#EE82EE", "bg": "#000000", "order": 1 }, - "flags": ["NEUTRAL", "IS_HUMAN"], + "flags": ["BYSTANDER", "BLOCKS_TILE"], "vision_range": 4, "quips": ["Drink?", "Something to eat?", "Don't go out on an empty stomach."] }, @@ -11,7 +11,7 @@ "id": "npc_townsperson", "name": "townsperson", "renderable": { "glyph": "@", "fg": "#9fa86c", "bg": "#000000", "order": 1 }, - "flags": ["NEUTRAL", "RANDOM_PATH", "IS_HUMAN"], + "flags": ["BYSTANDER", "BLOCKS_TILE"], "vision_range": 4, "quips": ["Hello!", "Good morning.", ""] }, @@ -19,7 +19,7 @@ "id": "npc_drunk", "name": "drunk", "renderable": { "glyph": "@", "fg": "#a0a83c", "bg": "#000000", "order": 1 }, - "flags": ["NEUTRAL", "IS_HUMAN"], + "flags": ["BYSTANDER", "BLOCKS_TILE"], "vision_range": 4, "quips": ["Hic!", "H-Hic'.", "Get me 'nother, would you?"] }, @@ -27,7 +27,7 @@ "id": "npc_fisher", "name": "fisher", "renderable": { "glyph": "@", "fg": "#3ca3a8", "bg": "#000000", "order": 1 }, - "flags": ["NEUTRAL", "IS_HUMAN"], + "flags": ["BYSTANDER", "BLOCKS_TILE"], "vision_range": 4, "quips": ["Hey."] }, @@ -35,7 +35,7 @@ "id": "npc_dockworker", "name": "dock worker", "renderable": { "glyph": "@", "fg": "#68d8de", "bg": "#000000", "order": 1 }, - "flags": ["NEUTRAL", "IS_HUMAN"], + "flags": ["BYSTANDER", "BLOCKS_TILE"], "vision_range": 4, "quips": ["No boat for a few days.", "Not much for us to do."] }, @@ -43,7 +43,7 @@ "id": "npc_priest", "name": "priest", "renderable": { "glyph": "@", "fg": "#FFFFFF", "bg": "#000000", "order": 1 }, - "flags": ["NEUTRAL", "IS_HUMAN"], + "flags": ["BYSTANDER", "BLOCKS_TILE"], "vision_range": 4, "quips": ["Light's givings.", "", "Bless you."] }, @@ -51,7 +51,7 @@ "id": "npc_miner", "name": "miner", "renderable": { "glyph": "@", "fg": "#946123", "bg": "#000000", "order": 1 }, - "flags": ["NEUTRAL", "IS_HUMAN"], + "flags": ["BYSTANDER", "BLOCKS_TILE"], "vision_range": 4, "attacks": [{ "name": "hits", "hit_bonus": 0, "damage": "1d8" }], "quips": ["You're not borrowing my pick."] @@ -60,8 +60,9 @@ "id": "npc_guard", "name": "smalltown guard", "renderable": { "glyph": "@", "fg": "#034efc", "bg": "#000000", "order": 1 }, - "flags": ["NEUTRAL", "RANDOM_PATH", "IS_HUMAN"], + "flags": ["BYSTANDER", "BLOCKS_TILE"], "level": 2, + "vision_range": 4, "attacks": [{ "name": "hits", "hit_bonus": 0, "damage": "1d8" }], "equipped": ["equip_shortsword", "equip_body_leather"], "quips": ["You wont catch me down the mine.", "Staying out of trouble?"] @@ -70,8 +71,9 @@ "id": "rat", "name": "rat", "renderable": { "glyph": "r", "fg": "#aa6000", "bg": "#000000", "order": 1 }, - "flags": [], + "flags": ["MONSTER", "BLOCKS_TILE"], "bac": 6, + "vision_range": 8, "attacks": [{ "name": "bites", "hit_bonus": 0, "damage": "1d2" }], "loot": { "table": "food", "chance": 0.1 } }, @@ -79,42 +81,47 @@ "id": "chicken", "name": "chicken", "renderable": { "glyph": "c", "fg": "#BB6000", "bg": "#000000", "order": 1 }, - "flags": ["HERBIVORE"], + "flags": ["BYSTANDER", "BLOCKS_TILE"], "bac": 8, + "vision_range": 4, "attacks": [{ "name": "bites", "hit_bonus": 0, "damage": "1d3" }] }, { "id": "deer_little", "name": "fawn", "renderable": { "glyph": "q", "fg": "#a57037", "bg": "#000000", "order": 1 }, - "flags": ["HERBIVORE"], + "flags": ["BYSTANDER", "BLOCKS_TILE"], "bac": 8, + "vision_range": 8, "attacks": [{ "name": "kicks", "hit_bonus": 0, "damage": "1d2" }] }, { "id": "sheep_little", "name": "lamb", "renderable": { "glyph": "q", "fg": "#e7e7e7", "bg": "#000000", "order": 1 }, - "flags": ["HERBIVORE", "SMALL_GROUP"], + "flags": ["BYSTANDER", "BLOCKS_TILE", "SMALL_GROUP"], "bac": 10, + "vision_range": 4, "attacks": [{ "name": "kicks", "hit_bonus": 0, "damage": "1d2" }] }, { "id": "chicken_little", "name": "chick", "renderable": { "glyph": "c", "fg": "#fae478", "bg": "#000000", "order": 1 }, - "flags": ["HERBIVORE"], + "flags": ["BYSTANDER", "BLOCKS_TILE"], "bac": 10, + "vision_range": 4, "attacks": [{ "name": "bites", "hit_bonus": 0, "damage": "1d2" }] }, { "id": "horse_little", "name": "pony", "renderable": { "glyph": "u", "fg": "#b36c29", "bg": "#000000", "order": 1 }, - "flags": ["HERBIVORE", "MULTIATTACK"], + "flags": ["BYSTANDER", "BLOCKS_TILE", "MULTIATTACK"], "level": 3, "bac": 6, "speed": 16, + "vision_range": 8, "attacks": [ { "name": "kicks", "hit_bonus": 0, "damage": "1d6" }, { "name": "bites", "hit_bonus": 0, "damage": "1d2" } @@ -125,10 +132,11 @@ "id": "horse", "name": "horse", "renderable": { "glyph": "u", "fg": "#744d29", "bg": "#000000", "order": 1 }, - "flags": ["MULTIATTACK"], + "flags": ["MONSTER", "BLOCKS_TILE", "MULTIATTACK"], "level": 5, "bac": 5, "speed": 20, + "vision_range": 8, "attacks": [ { "name": "kicks", "hit_bonus": 0, "damage": "1d8" }, { "name": "bites", "hit_bonus": 0, "damage": "1d3" } @@ -138,10 +146,11 @@ "id": "horse_large", "name": "warhorse", "renderable": { "glyph": "u", "fg": "#8a3520", "bg": "#000000", "order": 1 }, - "flags": ["MULTIATTACK"], + "flags": ["MONSTER", "BLOCKS_TILE", "MULTIATTACK"], "level": 7, "bac": 4, "speed": 24, + "vision_range": 8, "attacks": [ { "name": "kicks", "hit_bonus": 0, "damage": "1d10" }, { "name": "bites", "hit_bonus": 0, "damage": "1d4" } @@ -151,9 +160,10 @@ "id": "rat_giant", "name": "giant rat", "renderable": { "glyph": "r", "fg": "#bb8000", "bg": "#000000", "order": 1 }, - "flags": ["SMALL_GROUP"], + "flags": ["MONSTER", "BLOCKS_TILE", "SMALL_GROUP"], "level": 1, "bac": 7, + "vision_range": 8, "attacks": [{ "name": "bites", "hit_bonus": 0, "damage": "1d3" }], "loot": { "table": "scrolls", "chance": 0.05 } }, @@ -161,10 +171,11 @@ "id": "dog_little", "name": "little dog", "renderable": { "glyph": "d", "fg": "#FFFFFF", "bg": "#000000", "order": 1 }, - "flags": ["NEUTRAL"], + "flags": ["BYSTANDER", "BLOCKS_TILE"], "level": 2, "bac": 6, "speed": 18, + "vision_range": 12, "quips": ["", "", ""], "attacks": [{ "name": "bites", "hit_bonus": 0, "damage": "1d6" }] }, @@ -172,40 +183,43 @@ "id": "dog", "name": "dog", "renderable": { "glyph": "d", "fg": "#EEEEEE", "bg": "#000000", "order": 1 }, - "flags": [], + "flags": ["MONSTER", "BLOCKS_TILE"], "level": 4, "bac": 5, "speed": 16, + "vision_range": 12, "attacks": [{ "name": "bites", "hit_bonus": 0, "damage": "1d6" }] }, { "id": "dog_large", "name": "large dog", "renderable": { "glyph": "d", "fg": "#DDDDDD", "bg": "#000000", "order": 1 }, - "flags": [], + "flags": ["MONSTER", "BLOCKS_TILE"], "level": 6, "bac": 4, "speed": 15, + "vision_range": 12, "attacks": [{ "name": "bites", "hit_bonus": 0, "damage": "2d4" }] }, { "id": "gnome", "name": "gnome", "renderable": { "glyph": "G", "fg": "#AA5500", "bg": "#000000", "order": 1 }, - "flags": ["SMALL_GROUP", "IS_GNOME"], + "flags": ["MONSTER", "BLOCKS_TILE", "SMALL_GROUP"], "level": 1, "speed": 6, + "vision_range": 12, "attacks": [{ "name": "claws", "hit_bonus": 0, "damage": "1d6" }], "loot": { "table": "wands", "chance": 0.05 } }, { "id": "zombie_gnome", "name": "gnome zombie", - "renderable": { "glyph": "z", "fg": "#AA5500", "bg": "#000000", "order": 1 }, - "flags": ["MINDLESS"], + "renderable": { "glyph": "G", "fg": "#AA5500", "bg": "#000000", "order": 1 }, + "flags": ["MONSTER", "BLOCKS_TILE"], "level": 1, "speed": 6, - "vision_range": 12, + "vision_range": 7, "attacks": [{ "name": "claws", "hit_bonus": 0, "damage": "1d4" }], "loot": { "table": "wands", "chance": 0.05 } }, @@ -213,18 +227,20 @@ "id": "goblin", "name": "goblin", "renderable": { "glyph": "g", "fg": "#00FF00", "bg": "#000000", "order": 1 }, - "flags": [], + "flags": ["MONSTER", "BLOCKS_TILE"], "level": 1, "speed": 9, + "vision_range": 12, "attacks": [{ "name": "hits", "hit_bonus": 0, "damage": "1d4" }] }, { "id": "kobold", "name": "kobold", "renderable": { "glyph": "k", "fg": "#AA5500", "bg": "#000000", "order": 1 }, - "flags": [], + "flags": ["MONSTER", "BLOCKS_TILE"], "level": 1, "speed": 6, + "vision_range": 7, "attacks": [{ "name": "hits", "hit_bonus": 0, "damage": "1d4" }], "loot": { "table": "food", "chance": 0.05 } }, @@ -232,10 +248,10 @@ "id": "zombie_kobold", "name": "kobold zombie", "renderable": { "glyph": "z", "fg": "#AA5500", "bg": "#000000", "order": 1 }, - "flags": ["MINDLESS"], + "flags": ["MONSTER", "BLOCKS_TILE"], "level": 1, "speed": 6, - "vision_range": 12, + "vision_range": 7, "attacks": [{ "name": "claws", "hit_bonus": 0, "damage": "1d4" }], "loot": { "table": "scrolls", "chance": 0.05 } }, @@ -243,10 +259,10 @@ "id": "kobold_large", "name": "large kobold", "renderable": { "glyph": "k", "fg": "#70461b", "bg": "#000000", "order": 1 }, - "flags": [], + "flags": ["MONSTER", "BLOCKS_TILE"], "level": 1, "speed": 6, - "vision_range": 12, + "vision_range": 8, "attacks": [{ "name": "hits", "hit_bonus": 0, "damage": "1d6" }], "loot": { "table": "food", "chance": 0.05 } }, @@ -254,11 +270,11 @@ "id": "zombie_orc", "name": "orc zombie", "renderable": { "glyph": "z", "fg": "#dbd830", "bg": "#000000", "order": 1 }, - "flags": ["MINDLESS"], + "flags": ["MONSTER", "BLOCKS_TILE"], "level": 2, "bac": 9, "speed": 6, - "vision_range": 12, + "vision_range": 8, "attacks": [{ "name": "claws", "hit_bonus": 0, "damage": "1d6" }], "loot": { "table": "potions", "chance": 0.05 } }, @@ -266,11 +282,12 @@ "id": "dwarf", "name": "dwarf", "renderable": { "glyph": "h", "fg": "#d61b1b", "bg": "#000000", "order": 1 }, - "flags": ["IS_DWARF"], + "flags": ["MONSTER", "BLOCKS_TILE"], "level": 2, "bac": 10, "speed": 6, - "attacks": [{ "name": "hacks", "hit_bonus": 0, "damage": "1d8" }], + "vision_range": 8, + "attacks": [{ "name": "claws", "hit_bonus": 0, "damage": "1d8" }], "equipped": ["equip_feet_iron"], "loot": { "table": "equipment", "chance": 0.05 } }, @@ -278,11 +295,11 @@ "id": "zombie_dwarf", "name": "dwarf zombie", "renderable": { "glyph": "z", "fg": "#d61b1b", "bg": "#000000", "order": 1 }, - "flags": ["MINDLESS"], + "flags": ["MONSTER", "BLOCKS_TILE"], "level": 2, "bac": 9, "speed": 6, - "vision_range": 12, + "vision_range": 8, "attacks": [{ "name": "claws", "hit_bonus": 0, "damage": "1d6" }], "loot": { "table": "equipment", "chance": 0.05 } }, @@ -290,10 +307,10 @@ "id": "kobold_captain", "name": "kobold captain", "renderable": { "glyph": "k", "fg": "#9331ac", "bg": "#000000", "order": 1 }, - "flags": [], + "flags": ["MONSTER", "BLOCKS_TILE"], "level": 2, "speed": 6, - "vision_range": 12, + "vision_range": 7, "attacks": [{ "name": "hits", "hit_bonus": 0, "damage": "2d4" }], "loot": { "table": "food", "chance": 0.05 } }, @@ -301,101 +318,60 @@ "id": "spider_cave", "name": "cave spider", "renderable": { "glyph": "s", "fg": "#6b6b6b", "bg": "#000000", "order": 1 }, - "flags": ["SMALL_GROUP"], + "flags": ["MONSTER", "BLOCKS_TILE", "SMALL_GROUP"], "level": 1, "bac": 3, "speed": 12, + "vision_range": 12, "attacks": [{ "name": "bites", "hit_bonus": 0, "damage": "1d2" }], "loot": { "table": "scrolls", "chance": 0.05 } }, - { - "id": "ant_worker", - "name": "worker ant", - "renderable": { "glyph": "a", "fg": "#ca7631", "bg": "#000000", "order": 1 }, - "flags": ["SMALL_GROUP"], - "level": 2, - "bac": 3, - "speed": 18, - "attacks": [{ "name": "bites", "hit_bonus": 0, "damage": "1d4" }], - "loot": { "table": "food", "chance": 0.05 } - }, - { - "id": "ant_soldier", - "name": "soldier ant", - "renderable": { "glyph": "a", "fg": "#ca3f26", "bg": "#000000", "order": 1 }, - "flags": ["SMALL_GROUP", "POISON_RES"], - "level": 3, - "bac": 3, - "speed": 18, - "attacks": [ - { "name": "bites", "hit_bonus": 0, "damage": "2d4" }, - { "name": "stings", "hit_bonus": 0, "damage": "3d4;poison" } - ], - "loot": { "table": "food", "chance": 0.05 } - }, - { - "id": "caterpillar_cave", - "name": "caterpillar", - "renderable": { "glyph": "a", "fg": "#6b6b6b", "bg": "#000000", "order": 1 }, - "flags": ["SMALL_GROUP"], - "level": 1, - "bac": 3, - "speed": 9, - "attacks": [{ "name": "bites", "hit_bonus": 0, "damage": "1d3" }], - "loot": { "table": "food", "chance": 0.05 } - }, - { - "id": "caterpillar_giant", - "name": "giant caterpillar", - "renderable": { "glyph": "a", "fg": "#b9aeae", "bg": "#000000", "order": 1 }, - "flags": ["SMALL_GROUP"], - "level": 2, - "bac": 7, - "speed": 9, - "attacks": [{ "name": "bites", "hit_bonus": 0, "damage": "1d6" }], - "loot": { "table": "food", "chance": 0.10 } - }, { "id": "jackal", "name": "jackal", "renderable": { "glyph": "d", "fg": "#AA5500", "bg": "#000000", "order": 1 }, - "flags": ["CARNIVORE", "SMALL_GROUP"], + "flags": ["MONSTER", "BLOCKS_TILE", "SMALL_GROUP"], "bac": 7, + "vision_range": 12, "attacks": [{ "name": "bites", "hit_bonus": 0, "damage": "1d2" }] }, { "id": "fox", "name": "fox", "renderable": { "glyph": "d", "fg": "#FF0000", "bg": "#000000", "order": 1 }, - "flags": ["CARNIVORE"], + "flags": ["MONSTER", "BLOCKS_TILE"], "bac": 7, + "vision_range": 12, "attacks": [{ "name": "bites", "hit_bonus": 0, "damage": "1d3" }] }, { "id": "coyote", "name": "coyote", "renderable": { "glyph": "d", "fg": "#6E3215", "bg": "#000000", "order": 1 }, - "flags": ["CARNIVORE", "SMALL_GROUP"], + "flags": ["MONSTER", "BLOCKS_TILE", "SMALL_GROUP"], "level": 1, "bac": 7, + "vision_range": 12, "attacks": [{ "name": "bites", "hit_bonus": 0, "damage": "1d4" }] }, { "id": "wolf", "name": "wolf", "renderable": { "glyph": "d", "fg": "#5E4225", "bg": "#000000", "order": 1 }, - "flags": ["CARNIVORE"], + "flags": ["MONSTER", "BLOCKS_TILE"], "level": 5, "bac": 4, + "vision_range": 12, "attacks": [{ "name": "bites", "hit_bonus": 0, "damage": "2d4" }] }, { "id": "goblin_chieftain", "name": "goblin chieftain", - "renderable": { "glyph": "g", "fg": "#9331ac", "bg": "#000000", "order": 1 }, - "flags": [], + "renderable": { "glyph": "G", "fg": "#00FF00", "bg": "#000000", "order": 1 }, + "flags": ["MONSTER", "BLOCKS_TILE"], "level": 2, "speed": 9, + "vision_range": 12, "attacks": [{ "name": "hits", "hit_bonus": 0, "damage": "1d8" }], "loot": { "table": "wands", "chance": 0.05 } }, @@ -403,9 +379,10 @@ "id": "orc", "name": "orc", "renderable": { "glyph": "o", "fg": "#00FF00", "bg": "#000000", "order": 1 }, - "flags": ["SMALL_GROUP"], + "flags": ["MONSTER", "BLOCKS_TILE", "SMALL_GROUP"], "level": 1, "speed": 9, + "vision_range": 12, "attacks": [{ "name": "hits", "hit_bonus": 0, "damage": "1d6" }], "loot": { "table": "equipment", "chance": 0.05 } }, @@ -413,9 +390,10 @@ "id": "orc_hill", "name": "hill orc", "renderable": { "glyph": "o", "fg": "#dbd830", "bg": "#000000", "order": 1 }, - "flags": ["LARGE_GROUP"], + "flags": ["MONSTER", "BLOCKS_TILE", "LARGE_GROUP"], "level": 2, "speed": 9, + "vision_range": 11, "attacks": [{ "name": "hits", "hit_bonus": 0, "damage": "1d6" }], "loot": { "table": "equipment", "chance": 0.05 } }, @@ -423,91 +401,26 @@ "id": "orc_captain", "name": "orc captain", "renderable": { "glyph": "o", "fg": "#9331ac", "bg": "#000000", "order": 1 }, - "flags": ["MULTIATTACK"], + "flags": ["MONSTER", "BLOCKS_TILE", "MULTIATTACK"], "level": 5, "speed": 5, + "vision_range": 12, "attacks": [ { "name": "hits", "hit_bonus": 0, "damage": "2d4" }, { "name": "hits", "hit_bonus": 0, "damage": "2d4" } ], "loot": { "table": "equipment", "chance": 0.05 } }, - { - "id": "warg", - "name": "warg", - "renderable": { "glyph": "d", "fg": "#8b7164", "bg": "#000000", "order": 1 }, - "flags": ["SMALL_GROUP"], - "level": 7, - "bac": 4, - "speed": 12, - "attacks": [{ "name": "bites", "hit_bonus": 0, "damage": "2d6" }], - "loot": { "table": "food", "chance": 0.05 } - }, - { - "id": "jaguar", - "name": "jaguar", - "renderable": { "glyph": "f", "fg": "#d3b947", "bg": "#000000", "order": 1 }, - "flags": ["MULTIATTACK"], - "level": 4, - "bac": 6, - "speed": 15, - "attacks": [ - { "name": "claws", "hit_bonus": 0, "damage": "1d4" }, - { "name": "claws", "hit_bonus": 0, "damage": "1d4" }, - { "name": "bites", "hit_bonus": 0, "damage": "1d8" } - ], - "loot": { "table": "food", "chance": 0.05 } - }, - { - "id": "lynx", - "name": "lynx", - "renderable": { "glyph": "f", "fg": "#b5d347", "bg": "#000000", "order": 1 }, - "flags": ["MULTIATTACK"], - "level": 5, - "bac": 6, - "speed": 15, - "attacks": [ - { "name": "claws", "hit_bonus": 0, "damage": "1d4" }, - { "name": "claws", "hit_bonus": 0, "damage": "1d4" }, - { "name": "bites", "hit_bonus": 0, "damage": "1d10" } - ], - "loot": { "table": "food", "chance": 0.05 } - }, - { - "id": "panther", - "name": "panther", - "renderable": { "glyph": "f", "fg": "#58554e", "bg": "#000000", "order": 1 }, - "flags": ["MULTIATTACK"], - "level": 5, - "bac": 6, - "speed": 15, - "attacks": [ - { "name": "claws", "hit_bonus": 0, "damage": "1d6" }, - { "name": "claws", "hit_bonus": 0, "damage": "1d6" }, - { "name": "bites", "hit_bonus": 0, "damage": "1d10" } - ], - "loot": { "table": "food", "chance": 0.05 } - }, { "id": "ogre", "name": "ogre", - "renderable": { "glyph": "O", "fg": "#10A70d", "bg": "#000000", "order": 1 }, - "flags": ["SMALL_GROUP"], + "renderable": { "glyph": "O", "fg": "#00FF00", "bg": "#000000", "order": 1 }, + "flags": ["MONSTER", "BLOCKS_TILE", "SMALL_GROUP"], "level": 5, "bac": 5, "speed": 10, + "vision_range": 8, "attacks": [{ "name": "hits", "hit_bonus": 0, "damage": "2d5" }], "loot": { "table": "food", "chance": 0.05 } - }, - { - "id": "treant_small", - "name": "treant sapling", - "renderable": { "glyph": "♠️", "fg": "#10570d", "bg": "#000000", "order": 1 }, - "flags": ["LARGE_GROUP", "GREEN_BLOOD", "FIRE_WEAK"], - "level": 2, - "bac": 12, - "speed": 3, - "attacks": [{ "name": "lashes", "hit_bonus": 4, "damage": "1d8" }], - "loot": { "table": "scrolls", "chance": 0.05 } } ] diff --git a/raws/props.json b/raws/props.json index 0c48291..a759520 100644 --- a/raws/props.json +++ b/raws/props.json @@ -3,82 +3,74 @@ "id": "door", "name": "door", "renderable": { "glyph": "+", "fg": "#00FFFF", "bg": "#000000", "order": 2 }, - "flags": ["DOOR"] + "flags": ["BLOCKS_TILE", "BLOCKS_VISIBILITY", "DOOR", "PROP"] }, { "id": "prop_altar", "name": "altar", "renderable": { "glyph": "_", "fg": "#FFFFFF", "bg": "#000000", "order": 2 }, - "flags": ["ENTRY_TRIGGER"], - "effects": { "heal": "8d8" } + "flags": ["PROP"] }, { "id": "prop_keg", "name": "keg", "renderable": { "glyph": "φ", "fg": "#AAAAAA", "bg": "#000000", "order": 2 }, - "flags": [] + "flags": ["PROP"] }, { "id": "prop_table", "name": "table", "renderable": { "glyph": "-", "fg": "#AAAAAA", "bg": "#000000", "order": 2 }, - "flags": [] + "flags": ["PROP"] }, { "id": "prop_hay", "name": "hay", "renderable": { "glyph": "%", "fg": "#c7ad39", "bg": "#000000", "order": 2 }, - "flags": [] + "flags": ["PROP"] }, { "id": "prop_statue", "name": "statue", "renderable": { "glyph": "@", "fg": "#ffffff", "bg": "#000000", "order": 2 }, - "flags": [] + "flags": ["PROP"] }, { "id": "prop_bed", "name": "bed", "renderable": { "glyph": "=", "fg": "#AAAAAA", "bg": "#000000", "order": 2 }, - "flags": [] + "flags": ["PROP"] }, { "id": "prop_chair", "name": "chair", "renderable": { "glyph": "└", "fg": "#AAAAAA", "bg": "#000000", "order": 2 }, - "flags": [] + "flags": ["PROP"] }, { "id": "prop_candle", "name": "candle", "renderable": { "glyph": "Ä", "fg": "#FFA500", "bg": "#000000", "order": 2 }, - "flags": [] + "flags": ["PROP"] }, { "id": "trap_bear", "name": "bear trap", - "renderable": { "glyph": "^", "fg": "#e6e6e6", "bg": "#000000", "order": 2 }, + "renderable": { "glyph": "^", "fg": "#C0C0C0", "bg": "#000000", "order": 2 }, "flags": ["HIDDEN", "ENTRY_TRIGGER", "SINGLE_ACTIVATION"], "effects": { "damage": "2d4" } }, - { - "id": "trap_mini_mine", - "name": "mini-mine", - "renderable": { "glyph": "^", "fg": "#ff1e00", "bg": "#000000", "order": 2 }, - "flags": ["ENTRY_TRIGGER", "SINGLE_ACTIVATION"], - "effects": { "damage": "2d4", "aoe": "3" } - }, { "id": "trap_stonefall", "name": "stonefall trap", - "renderable": { "glyph": "^", "fg": "#beb5a7", "bg": "#000000", "order": 2 }, + "renderable": { "glyph": "^", "fg": "#C0C0C0", "bg": "#000000", "order": 2 }, "flags": ["HIDDEN", "ENTRY_TRIGGER", "SINGLE_ACTIVATION"], "effects": { "damage": "2d10" } }, { "id": "trap_confusion", "name": "magic trap", - "renderable": { "glyph": "^", "fg": "#df07df", "bg": "#000000", "order": 2 }, + "renderable": { "glyph": "^", "fg": "#800080", "bg": "#000000", "order": 2 }, "flags": ["HIDDEN", "ENTRY_TRIGGER", "SINGLE_ACTIVATION"], "effects": { "confusion": "3" } } diff --git a/raws/spawn_tables.json b/raws/spawn_tables.json index b624a80..a7be6c2 100644 --- a/raws/spawn_tables.json +++ b/raws/spawn_tables.json @@ -2,12 +2,8 @@ { "id": "equipment", "table": [ - { "id": "equip_pitchfork", "weight": 1, "difficulty": 1}, - { "id": "equip_sickle", "weight": 1, "difficulty": 1}, - { "id": "equip_handaxe", "weight": 1, "difficulty": 1}, { "id": "equip_dagger", "weight": 3, "difficulty": 1}, { "id": "equip_shortsword", "weight": 3, "difficulty": 1}, - { "id": "equip_rapier", "weight": 2, "difficulty": 1}, { "id": "equip_smallshield", "weight": 3, "difficulty": 1}, { "id": "equip_mediumshield", "weight": 2, "difficulty": 1}, { "id": "equip_body_weakleather", "weight": 4, "difficulty": 1}, @@ -29,123 +25,81 @@ { "id": "equip_feet_iron", "weight": 2, "difficulty": 4} ] }, - { - "id": "villager_equipment", - "table": [ - { "id": "equip_pitchfork", "weight": 1, "difficulty": 1}, - { "id": "equip_sickle", "weight": 1, "difficulty": 1}, - { "id": "equip_handaxe", "weight": 1, "difficulty": 1} - ] - }, { "id": "potions", "table": [ - { "id": "potion_health_weak", "weight": 6, "difficulty": 1}, - { "id": "potion_health", "weight": 3, "difficulty": 1} + { "id": "potion_health_weak", "weight": 6, "difficulty": 1}, + { "id": "potion_health", "weight": 3, "difficulty": 1} ] }, { "id": "scrolls", "table": [ - { "id": "scroll_identify", "weight": 3, "difficulty": 1}, - { "id": "scroll_removecurse", "weight": 2, "difficulty": 1}, - { "id": "scroll_confusion", "weight": 2, "difficulty": 1}, - { "id": "scroll_magicmap", "weight": 2, "difficulty": 1}, - { "id": "scroll_embers", "weight": 2, "difficulty": 2}, - { "id": "scroll_health", "weight": 2, "difficulty": 2}, - { "id": "scroll_fireball", "weight": 2, "difficulty": 2}, - { "id": "scroll_mass_health", "weight": 1, "difficulty": 2}, - { "id": "scroll_mass_confusion", "weight": 1, "difficulty": 3} + { "id": "scroll_confusion", "weight": 2, "difficulty": 1}, + { "id": "scroll_magicmap_c", "weight": 2, "difficulty": 1}, + { "id": "scroll_magicmap", "weight": 2, "difficulty": 1}, + { "id": "scroll_fireball", "weight": 2, "difficulty": 2}, + { "id": "scroll_fireball_c", "weight": 2, "difficulty": 2} ] }, { "id": "wands", "table": [ - { "id": "wand_magicmissile", "weight": 1, "difficulty": 1}, - { "id": "wand_confusion", "weight": 1, "difficulty": 1}, - { "id": "wand_digging", "weight": 1, "difficulty": 1}, - { "id": "wand_fireball", "weight": 1, "difficulty": 2} + { "id": "wand_magicmissile", "weight": 1, "difficulty": 1}, + { "id": "wand_confusion", "weight": 1, "difficulty": 1}, + { "id": "wand_digging", "weight": 1, "difficulty": 1}, + { "id": "wand_fireball", "weight": 1, "difficulty": 2} ] }, { "id": "food", "table": [ - { "id": "food_rations", "weight": 1, "difficulty": 1}, - { "id": "food_apple", "weight": 1, "difficulty": 1} + { "id": "food_rations", "weight": 1, "difficulty": 1}, + { "id": "food_apple", "weight": 1, "difficulty": 1} ] }, { "id": "mobs", "table": [ - { "id": "sheep_little", "weight": 1, "difficulty": 0}, - { "id": "chicken", "weight": 1, "difficulty": 1}, - { "id": "rat", "weight": 1, "difficulty": 1}, - { "id": "goblin", "weight": 3, "difficulty": 1}, - { "id": "kobold", "weight": 1, "difficulty": 1}, - { "id": "fox", "weight": 1, "difficulty": 1}, - { "id": "jackal", "weight": 4, "difficulty": 1}, - { "id": "deer_little", "weight": 1, "difficulty": 1}, - { "id": "treant_small", "weight": 1, "difficulty": 1}, - { "id": "zombie_kobold", "weight": 1, "difficulty": 1}, - { "id": "zombie_gnome", "weight": 1, "difficulty": 2}, - { "id": "kobold_large", "weight": 1, "difficulty": 2}, - { "id": "rat_giant", "weight": 2, "difficulty": 2}, - { "id": "coyote", "weight": 4, "difficulty": 2}, - { "id": "caterpillar_cave", "weight": 2, "difficulty": 2}, - { "id": "caterpillar_giant", "weight": 2, "difficulty": 3}, - { "id": "zombie_orc", "weight": 1, "difficulty": 3}, - { "id": "zombie_dwarf", "weight": 1, "difficulty": 3}, - { "id": "gnome", "weight": 1, "difficulty": 3}, - { "id": "spider_cave", "weight": 2, "difficulty": 3}, - { "id": "kobold_captain", "weight": 1, "difficulty": 3}, - { "id": "dog_little", "weight": 1, "difficulty": 3}, - { "id": "orc", "weight": 2, "difficulty": 3}, - { "id": "goblin_chieftain", "weight": 1, "difficulty": 3}, - { "id": "dwarf", "weight": 3, "difficulty": 4}, - { "id": "orc_hill", "weight": 1, "difficulty": 4}, - { "id": "horse_little", "weight": 2, "difficulty": 4}, - { "id": "ant_worker", "weight": 3, "difficulty": 4}, - { "id": "dog", "weight": 1, "difficulty": 5}, - { "id": "wolf", "weight": 2, "difficulty": 6}, - { "id": "jaguar", "weight": 2, "difficulty": 6}, - { "id": "ant_soldier", "weight": 2, "difficulty": 6}, - { "id": "orc_captain", "weight": 1, "difficulty": 7}, - { "id": "dog_large", "weight": 1, "difficulty": 7}, - { "id": "lynx", "weight": 1, "difficulty": 7}, - { "id": "panther", "weight": 1, "difficulty": 7}, - { "id": "horse", "weight": 2, "difficulty": 7}, - { "id": "ogre", "weight": 1, "difficulty": 7}, - { "id": "warg", "weight": 2, "difficulty": 8}, - { "id": "horse_large", "weight": 2, "difficulty": 9} - ] - }, - { - "id": "squad_goblin", - "table": [ - { "id": "goblin", "weight": 3, "difficulty": 1} - ] - }, - { - "id": "squad_kobold", - "table": [ - { "id": "kobold", "weight": 3, "difficulty": 1}, - { "id": "kobold_large", "weight": 2, "difficulty": 2} - ] - }, - { - "id": "squad_orc", - "table": [ - { "id": "orc", "weight": 2, "difficulty": 2}, - { "id": "orc_hill", "weight": 1, "difficulty": 4} + { "id": "sheep_little", "weight": 1, "difficulty": 0}, + { "id": "chicken", "weight": 1, "difficulty": 1}, + { "id": "rat", "weight": 1, "difficulty": 1}, + { "id": "goblin", "weight": 3, "difficulty": 1}, + { "id": "kobold", "weight": 1, "difficulty": 1}, + { "id": "fox", "weight": 1, "difficulty": 1}, + { "id": "jackal", "weight": 4, "difficulty": 1}, + { "id": "deer_little", "weight": 1, "difficulty": 1}, + { "id": "zombie_kobold", "weight": 1, "difficulty": 1}, + { "id": "zombie_gnome", "weight": 1, "difficulty": 2}, + { "id": "kobold_large", "weight": 1, "difficulty": 2}, + { "id": "rat_giant", "weight": 2, "difficulty": 2}, + { "id": "coyote", "weight": 4, "difficulty": 2}, + { "id": "zombie_orc", "weight": 1, "difficulty": 3}, + { "id": "zombie_dwarf", "weight": 1, "difficulty": 3}, + { "id": "gnome", "weight": 1, "difficulty": 3}, + { "id": "spider_cave", "weight": 2, "difficulty": 3}, + { "id": "kobold_captain", "weight": 1, "difficulty": 3}, + { "id": "dog_little", "weight": 1, "difficulty": 3}, + { "id": "orc", "weight": 2, "difficulty": 3}, + { "id": "goblin_chieftain", "weight": 1, "difficulty": 3}, + { "id": "dwarf", "weight": 3, "difficulty": 4}, + { "id": "orc_hill", "weight": 1, "difficulty": 4}, + { "id": "horse_little", "weight": 2, "difficulty": 4}, + { "id": "dog", "weight": 1, "difficulty": 5}, + { "id": "wolf", "weight": 2, "difficulty": 6}, + { "id": "orc_captain", "weight": 1, "difficulty": 7}, + { "id": "dog_large", "weight": 1, "difficulty": 7}, + { "id": "horse", "weight": 2, "difficulty": 7}, + { "id": "ogre", "weight": 1, "difficulty": 7}, + { "id": "horse_large", "weight": 2, "difficulty": 9} ] }, { "id": "traps", "table": [ - { "id": "trap_bear", "weight": 2, "difficulty": 1}, - { "id": "trap_confusion", "weight": 1, "difficulty": 1}, - { "id": "trap_mini_mine", "weight": 1, "difficulty": 3}, - { "id": "trap_stonefall", "weight": 1, "difficulty": 5} + { "id": "trap_bear", "weight": 2, "difficulty": 1}, + { "id": "trap_confusion", "weight": 1, "difficulty": 1}, + { "id": "trap_stonefall", "weight": 1, "difficulty": 5} ] } ] diff --git a/resources/terminal10x10_gs_tc.png b/resources/terminal10x10_gs_tc.png new file mode 100644 index 0000000..5e0cdc9 Binary files /dev/null and b/resources/terminal10x10_gs_tc.png differ diff --git a/resources/terminal8x8.jpg b/resources/terminal8x8.jpg new file mode 100644 index 0000000..9f4d79d Binary files /dev/null and b/resources/terminal8x8.jpg differ diff --git a/resources/terminal_10x16.png b/resources/terminal_10x16.png new file mode 100644 index 0000000..e40aa99 Binary files /dev/null and b/resources/terminal_10x16.png differ diff --git a/resources/vga8x16.png b/resources/vga8x16.png new file mode 100644 index 0000000..913e32c Binary files /dev/null and b/resources/vga8x16.png differ diff --git a/src/ai/adjacent_ai_system.rs b/src/ai/adjacent_ai_system.rs deleted file mode 100644 index 5586948..0000000 --- a/src/ai/adjacent_ai_system.rs +++ /dev/null @@ -1,94 +0,0 @@ -use crate::{ raws::Reaction, Faction, HasAncestry, Map, Position, TakingTurn, WantsToMelee }; -use specs::prelude::*; - -pub struct AdjacentAI {} - -impl<'a> System<'a> for AdjacentAI { - #[allow(clippy::type_complexity)] - type SystemData = ( - WriteStorage<'a, TakingTurn>, - ReadStorage<'a, Faction>, - ReadStorage<'a, HasAncestry>, - ReadStorage<'a, Position>, - ReadExpect<'a, Map>, - WriteStorage<'a, WantsToMelee>, - Entities<'a>, - ReadExpect<'a, Entity>, - ); - - fn run(&mut self, data: Self::SystemData) { - let (mut turns, factions, ancestries, positions, map, mut want_melee, entities, player) = data; - - let mut turn_done: Vec = Vec::new(); - for (entity, _turn, pos) in (&entities, &turns, &positions).join() { - if entity != *player { - let mut reactions: Vec<(Entity, Reaction)> = Vec::new(); - let idx = map.xy_idx(pos.x, pos.y); - let w = map.width; - let h = map.height; - // Add possible reactions to adjacents for each direction - if pos.x > 0 { - evaluate(entity, idx - 1, &ancestries, &factions, &mut reactions); - } - if pos.x < w - 1 { - evaluate(entity, idx + 1, &ancestries, &factions, &mut reactions); - } - if pos.y > 0 { - evaluate(entity, idx - (w as usize), &ancestries, &factions, &mut reactions); - } - if pos.y < h - 1 { - evaluate(entity, idx + (w as usize), &ancestries, &factions, &mut reactions); - } - if pos.y > 0 && pos.x > 0 { - evaluate(entity, idx - (w as usize) - 1, &ancestries, &factions, &mut reactions); - } - if pos.y > 0 && pos.x < w - 1 { - evaluate(entity, idx - (w as usize) + 1, &ancestries, &factions, &mut reactions); - } - if pos.y < h - 1 && pos.x > 0 { - evaluate(entity, idx + (w as usize) - 1, &ancestries, &factions, &mut reactions); - } - if pos.y < h - 1 && pos.x < w - 1 { - evaluate(entity, idx + (w as usize) + 1, &ancestries, &factions, &mut reactions); - } - - let mut done = false; - for reaction in reactions.iter() { - if let Reaction::Attack = reaction.1 { - want_melee.insert(entity, WantsToMelee { target: reaction.0 }).expect("Error inserting melee"); - done = true; - } - } - - if done { - turn_done.push(entity); - } - } - } - - // Remove turn marker for those that are done - for done in turn_done.iter() { - turns.remove(*done); - } - } -} - -/// Evaluates all possible reactions between this faction and all entities on a given tile idx. -fn evaluate( - entity: Entity, - idx: usize, - ancestries: &ReadStorage, - factions: &ReadStorage, - reactions: &mut Vec<(Entity, Reaction)> -) { - crate::spatial::for_each_tile_content(idx, |other_entity| { - let result = crate::raws::get_reactions( - entity, - other_entity, - &factions, - &ancestries, - &crate::raws::RAWS.lock().unwrap() - ); - reactions.push((other_entity, result)); - }); -} diff --git a/src/ai/approach_ai_system.rs b/src/ai/approach_ai_system.rs deleted file mode 100644 index c3cc2ca..0000000 --- a/src/ai/approach_ai_system.rs +++ /dev/null @@ -1,105 +0,0 @@ -use crate::{ EntityMoved, Map, Position, TakingTurn, Telepath, Viewshed, WantsToApproach }; -use bracket_lib::prelude::*; -use specs::prelude::*; - -pub struct ApproachAI {} - -impl<'a> System<'a> for ApproachAI { - #[allow(clippy::type_complexity)] - type SystemData = ( - WriteStorage<'a, TakingTurn>, - WriteStorage<'a, WantsToApproach>, - WriteStorage<'a, Position>, - WriteExpect<'a, Map>, - WriteStorage<'a, Viewshed>, - WriteStorage<'a, Telepath>, - WriteStorage<'a, EntityMoved>, - Entities<'a>, - ); - - fn run(&mut self, data: Self::SystemData) { - let ( - mut turns, - mut wants_to_approach, - mut positions, - mut map, - mut viewsheds, - mut telepaths, - mut entity_moved, - entities, - ) = data; - let mut turn_done: Vec = Vec::new(); - for (entity, mut pos, approach, mut viewshed, _turn) in ( - &entities, - &mut positions, - &wants_to_approach, - &mut viewsheds, - &turns, - ).join() { - turn_done.push(entity); - let target_idxs = if - let Some(paths) = get_adjacent_unblocked(&map, approach.idx as usize) - { - paths - } else { - continue; - }; - let mut path: Option = None; - let idx = map.xy_idx(pos.x, pos.y); - for tar_idx in target_idxs { - let potential_path = a_star_search(idx, tar_idx, &mut *map); - if potential_path.success && potential_path.steps.len() > 1 { - if - path.is_none() || - potential_path.steps.len() < path.as_ref().unwrap().steps.len() - { - path = Some(potential_path); - } - } - } - let path = if path.is_some() { - path.unwrap() - } else { - continue; - }; - if path.success && path.steps.len() > 1 { - let idx = map.xy_idx(pos.x, pos.y); - pos.x = (path.steps[1] as i32) % map.width; - pos.y = (path.steps[1] as i32) / map.width; - entity_moved.insert(entity, EntityMoved {}).expect("Unable to insert EntityMoved"); - let new_idx = map.xy_idx(pos.x, pos.y); - crate::spatial::move_entity(entity, idx, new_idx); - viewshed.dirty = true; - if let Some(telepath) = telepaths.get_mut(entity) { - telepath.dirty = true; - } - } - } - wants_to_approach.clear(); - for done in turn_done.iter() { - turns.remove(*done); - } - } -} - -/// Try to get an unblocked index within one tile of a given idx, or None. -pub fn get_adjacent_unblocked(map: &WriteExpect, idx: usize) -> Option> { - let mut adjacent = Vec::new(); - let x = (idx as i32) % map.width; - let y = (idx as i32) / map.width; - for i in -1..2 { - for j in -1..2 { - if i == 0 && j == 0 { - continue; - } - let new_idx = (x + i + (y + j) * map.width) as usize; - if !crate::spatial::is_blocked(new_idx) { - adjacent.push(new_idx); - } - } - } - if adjacent.is_empty() { - return None; - } - return Some(adjacent); -} diff --git a/src/ai/chase_ai_system.rs b/src/ai/chase_ai_system.rs deleted file mode 100644 index 9fd29bf..0000000 --- a/src/ai/chase_ai_system.rs +++ /dev/null @@ -1,116 +0,0 @@ -use crate::{ Chasing, EntityMoved, Map, Position, TakingTurn, Telepath, Viewshed }; -use bracket_lib::prelude::*; -use specs::prelude::*; -use std::collections::HashMap; -use super::approach_ai_system::get_adjacent_unblocked; - -// If the target is beyond this distance, they're no longer being detected, -// so stop following them. This is essentially a combined value of the sound -// the target might be making, noise, light, etc., anything they could do to -// be detected. As those constituent systems are developed, this value should -// be changed to being a result of some calculations between them. -const MAX_CHASE_DISTANCE: usize = 15; -pub struct ChaseAI {} - -impl<'a> System<'a> for ChaseAI { - #[allow(clippy::type_complexity)] - type SystemData = ( - WriteStorage<'a, TakingTurn>, - WriteStorage<'a, Chasing>, - WriteStorage<'a, Position>, - WriteExpect<'a, Map>, - WriteStorage<'a, Viewshed>, - WriteStorage<'a, Telepath>, - WriteStorage<'a, EntityMoved>, - Entities<'a>, - ); - - fn run(&mut self, data: Self::SystemData) { - let ( - mut turns, - mut chasing, - mut positions, - mut map, - mut viewsheds, - mut telepaths, - mut entity_moved, - entities, - ) = data; - let mut targets: HashMap = HashMap::new(); - let mut end_chase: Vec = Vec::new(); - // For every chasing entity with a turn, look for a valid target position, - // and if found, store that position in a temporary HashMap. This gets around - // needing to read Position twice - that would cause borrowchecker issues. - // If there's no valid target found, remove the chasing component. - for (entity, _turn, chasing) in (&entities, &turns, &chasing).join() { - if let Some(target_pos) = positions.get(chasing.target) { - targets.insert(entity, (target_pos.x, target_pos.y)); - } else { - end_chase.push(entity); - } - } - for done in end_chase.iter() { - chasing.remove(*done); - } - end_chase.clear(); - // Iterate over everyone who is *still* chasing, and path to the target - // stored in the HashMap. If successful, follow the path. If not, remove - // the chasing component. - let mut turn_done: Vec = Vec::new(); - for (entity, _turn, mut pos, _chase, mut viewshed) in ( - &entities, - &turns, - &mut positions, - &chasing, - &mut viewsheds, - ).join() { - turn_done.push(entity); - let target_pos = targets[&entity]; - let target_idx = map.xy_idx(target_pos.0, target_pos.1); - let target_idxs = if let Some(paths) = get_adjacent_unblocked(&map, target_idx) { - paths - } else { - continue; - }; - let mut path: Option = None; - let idx = map.xy_idx(pos.x, pos.y); - for tar_idx in target_idxs { - let potential_path = a_star_search(idx, tar_idx, &mut *map); - if potential_path.success && potential_path.steps.len() > 1 { - if - path.is_none() || - potential_path.steps.len() < path.as_ref().unwrap().steps.len() - { - path = Some(potential_path); - } - } - } - let path = if path.is_some() { - path.unwrap() - } else { - continue; - }; - if path.success && path.steps.len() > 1 && path.steps.len() < MAX_CHASE_DISTANCE { - let idx = map.xy_idx(pos.x, pos.y); - pos.x = (path.steps[1] as i32) % map.width; - pos.y = (path.steps[1] as i32) / map.width; - entity_moved.insert(entity, EntityMoved {}).expect("Failed to insert EntityMoved"); - let new_idx = map.xy_idx(pos.x, pos.y); - crate::spatial::move_entity(entity, idx, new_idx); - viewshed.dirty = true; - if let Some(is_telepath) = telepaths.get_mut(entity) { - is_telepath.dirty = true; - } - turn_done.push(entity); - } else { - end_chase.push(entity); - } - } - for done in end_chase.iter() { - chasing.remove(*done); - } - for done in turn_done.iter() { - turns.remove(*done); - } - } -} diff --git a/src/ai/default_move_system.rs b/src/ai/default_move_system.rs deleted file mode 100644 index bd417bd..0000000 --- a/src/ai/default_move_system.rs +++ /dev/null @@ -1,171 +0,0 @@ -use crate::{ - tile_walkable, - EntityMoved, - Map, - MoveMode, - Movement, - Position, - TakingTurn, - Telepath, - Viewshed, -}; -use specs::prelude::*; -use bracket_lib::prelude::*; - -// Rolling a 1d8+x to decide where to move, where x are the number -// of dice rolls in which they will remian stationary. i.e. If this -// const is set to 8, there is a 50% chance of not wandering. -const CHANCE_OF_REMAINING_STATIONARY: i32 = 8; -pub struct DefaultAI {} - -impl<'a> System<'a> for DefaultAI { - type SystemData = ( - WriteStorage<'a, TakingTurn>, - WriteStorage<'a, MoveMode>, - WriteStorage<'a, Position>, - WriteExpect<'a, Map>, - WriteStorage<'a, Viewshed>, - WriteStorage<'a, Telepath>, - WriteStorage<'a, EntityMoved>, - WriteExpect<'a, RandomNumberGenerator>, - Entities<'a>, - ); - - fn run(&mut self, data: Self::SystemData) { - let ( - mut turns, - mut move_mode, - mut positions, - mut map, - mut viewsheds, - mut telepaths, - mut entity_moved, - mut rng, - entities, - ) = data; - let mut turn_done: Vec = Vec::new(); - for (entity, _turn, mut pos, mut move_mode, mut viewshed) in ( - &entities, - &turns, - &mut positions, - &mut move_mode, - &mut viewsheds, - ).join() { - turn_done.push(entity); - match &mut move_mode.mode { - Movement::Static => {} - Movement::Random => { - let mut x = pos.x; - let mut y = pos.y; - let move_roll = rng.roll_dice(1, 8 + CHANCE_OF_REMAINING_STATIONARY); - match move_roll { - 1 => { - x -= 1; - } - 2 => { - x += 1; - } - 3 => { - y -= 1; - } - 4 => { - y += 1; - } - 5 => { - x -= 1; - y -= 1; - } - 6 => { - x += 1; - y -= 1; - } - 7 => { - x -= 1; - y += 1; - } - 8 => { - x += 1; - y += 1; - } - _ => {} - } - - if x > 0 && x < map.width - 1 && y > 0 && y < map.height - 1 { - let dest_idx = map.xy_idx(x, y); - if !crate::spatial::is_blocked(dest_idx) { - let idx = map.xy_idx(pos.x, pos.y); - pos.x = x; - pos.y = y; - entity_moved - .insert(entity, EntityMoved {}) - .expect("Unable to insert EntityMoved"); - crate::spatial::move_entity(entity, idx, dest_idx); - viewshed.dirty = true; - if let Some(is_telepath) = telepaths.get_mut(entity) { - is_telepath.dirty = true; - } - } - } - } - Movement::RandomWaypoint { path } => { - if let Some(path) = path { - // We have a path - follow it - let idx = map.xy_idx(pos.x, pos.y); - if path.len() > 1 { - if !crate::spatial::is_blocked(path[1] as usize) { - pos.x = (path[1] as i32) % map.width; - pos.y = (path[1] as i32) / map.width; - entity_moved - .insert(entity, EntityMoved {}) - .expect("Unable to insert EntityMoved"); - let new_idx = map.xy_idx(pos.x, pos.y); - crate::spatial::move_entity(entity, idx, new_idx); - viewshed.dirty = true; - if let Some(is_telepath) = telepaths.get_mut(entity) { - is_telepath.dirty = true; - } - path.remove(0); - } else { - // If the path is blocked, recalculate a new path to the same waypoint. - let path = a_star_search( - map.xy_idx(pos.x, pos.y) as i32, - map.xy_idx( - (path[path.len() - 1] as i32) % map.width, - (path[path.len() - 1] as i32) / map.width - ) as i32, - &mut *map - ); - if path.success && path.steps.len() > 1 { - move_mode.mode = Movement::RandomWaypoint { - path: Some(path.steps), - }; - } - } - } else { - move_mode.mode = Movement::RandomWaypoint { path: None }; - } - } else { - let target_x = rng.roll_dice(1, map.width - 2); - let target_y = rng.roll_dice(1, map.height - 2); - let idx = map.xy_idx(target_x, target_y); - if tile_walkable(map.tiles[idx]) { - let path = a_star_search( - map.xy_idx(pos.x, pos.y) as i32, - map.xy_idx(target_x, target_y) as i32, - &mut *map - ); - if path.success && path.steps.len() > 1 { - move_mode.mode = Movement::RandomWaypoint { - path: Some(path.steps), - }; - } - } - } - } - } - } - for done in turn_done.iter() { - turns.remove(*done); - } - } -} diff --git a/src/ai/encumbrance_system.rs b/src/ai/encumbrance_system.rs index 0244597..ceb2815 100644 --- a/src/ai/encumbrance_system.rs +++ b/src/ai/encumbrance_system.rs @@ -1,7 +1,8 @@ -use crate::{ gamelog, Attributes, Burden, EquipmentChanged, Equipped, InBackpack, Item, Pools }; +use crate::{gamelog, Attributes, Burden, EquipmentChanged, Equipped, InBackpack, Item, Pools}; use specs::prelude::*; use std::collections::HashMap; -use crate::data::entity::CARRY_CAPACITY_PER_STRENGTH; + +pub const CARRY_CAPACITY_PER_STRENGTH: i32 = 8; pub struct EncumbranceSystem {} @@ -51,7 +52,7 @@ impl<'a> System<'a> for EncumbranceSystem { if let Some(attr) = attributes.get(*entity) { let carry_capacity_lbs = (attr.strength.base + attr.strength.modifiers) * CARRY_CAPACITY_PER_STRENGTH; - if (pool.weight as i32) > 3 * carry_capacity_lbs { + if pool.weight as i32 > 3 * carry_capacity_lbs { // Overloaded burdened .insert(*entity, Burden { level: crate::BurdenLevel::Overloaded }) @@ -59,7 +60,7 @@ impl<'a> System<'a> for EncumbranceSystem { if *entity == *player { gamelog::Logger::new().append("You're overloaded!").log(); } - } else if (pool.weight as i32) > 2 * carry_capacity_lbs { + } else if pool.weight as i32 > 2 * carry_capacity_lbs { // Strained burdened .insert(*entity, Burden { level: crate::BurdenLevel::Strained }) @@ -67,7 +68,7 @@ impl<'a> System<'a> for EncumbranceSystem { if *entity == *player { gamelog::Logger::new().append("You're strained.").log(); } - } else if (pool.weight as i32) > carry_capacity_lbs { + } else if pool.weight as i32 > carry_capacity_lbs { // Burdened burdened .insert(*entity, Burden { level: crate::BurdenLevel::Burdened }) diff --git a/src/ai/energy_system.rs b/src/ai/energy_system.rs index da62467..ef02d2d 100644 --- a/src/ai/energy_system.rs +++ b/src/ai/energy_system.rs @@ -1,30 +1,15 @@ -use crate::data::entity::*; -use crate::{ - Burden, - BurdenLevel, - Clock, - Energy, - Name, - Position, - RunState, - Map, - TakingTurn, - Confusion, - Intrinsics, -}; -use bracket_lib::prelude::*; +use crate::{Burden, BurdenLevel, Clock, Energy, Name, Position, RunState, TakingTurn, LOG_TICKS}; +use rltk::prelude::*; use specs::prelude::*; -use crate::config::CONFIG; -use crate::data::events::*; pub struct EnergySystem {} -const TURN_COST: i32 = NORMAL_SPEED * TURN_COST_MULTIPLIER; +pub const NORMAL_SPEED: i32 = 12; +const TURN_COST: i32 = NORMAL_SPEED * 4; impl<'a> System<'a> for EnergySystem { #[allow(clippy::type_complexity)] type SystemData = ( - ReadExpect<'a, Map>, ReadStorage<'a, Clock>, WriteStorage<'a, Energy>, ReadStorage<'a, Burden>, @@ -35,28 +20,10 @@ impl<'a> System<'a> for EnergySystem { WriteExpect<'a, RunState>, ReadExpect<'a, Entity>, ReadStorage<'a, Name>, - ReadExpect<'a, Point>, - ReadStorage<'a, Confusion>, - ReadStorage<'a, Intrinsics>, ); fn run(&mut self, data: Self::SystemData) { - let ( - map, - clock, - mut energies, - burdens, - positions, - mut turns, - entities, - mut rng, - mut runstate, - player, - names, - player_pos, - confusion, - intrinsics, - ) = data; + let (clock, mut energies, burdens, positions, mut turns, entities, mut rng, mut runstate, player, names) = data; // If not ticking, do nothing. if *runstate != RunState::Ticking { return; @@ -67,37 +34,28 @@ impl<'a> System<'a> for EnergySystem { for (entity, _clock, energy) in (&entities, &clock, &mut energies).join() { energy.current += NORMAL_SPEED; if energy.current >= TURN_COST { - turns - .insert(entity, TakingTurn {}) - .expect("Unable to insert turn for turn counter."); + turns.insert(entity, TakingTurn {}).expect("Unable to insert turn for turn counter."); energy.current -= TURN_COST; - crate::gamelog::record_event(EVENT::Turn(1)); + crate::gamelog::record_event("turns", 1); // Handle spawning mobs each turn - if CONFIG.logging.log_ticks { - console::log( - format!( - "===== TURN {} =====", - crate::gamelog::get_event_count(EVENT::COUNT_TURN) - ) - ); + if LOG_TICKS { + console::log(format!("===== TURN {} =====", crate::gamelog::get_event_count("turns"))); } } } // EVERYTHING ELSE - for (entity, energy, pos, _c) in ( - &entities, - &mut energies, - &positions, - !&confusion, - ).join() { - let burden_modifier = get_burden_modifier(&burdens, entity); - let overmap_mod = get_overmap_modifier(&map); - let intrinsic_speed = get_intrinsic_speed(&intrinsics, entity); + for (entity, energy, _pos) in (&entities, &mut energies, &positions).join() { + let burden_modifier = if let Some(burden) = burdens.get(entity) { + match burden.level { + BurdenLevel::Burdened => 0.75, + BurdenLevel::Strained => 0.5, + BurdenLevel::Overloaded => 0.25, + } + } else { + 1.0 + }; // Every entity has a POTENTIAL equal to their speed. - let mut energy_potential: i32 = ((energy.speed as f32) * - burden_modifier * - overmap_mod * - intrinsic_speed) as i32; + let mut energy_potential: i32 = (energy.speed as f32 * burden_modifier) as i32; // Increment current energy by NORMAL_SPEED for every // whole number of NORMAL_SPEEDS in their POTENTIAL. while energy_potential >= NORMAL_SPEED { @@ -118,61 +76,19 @@ impl<'a> System<'a> for EnergySystem { // has enough energy, they take a turn and decrement their energy // by TURN_COST. If the current entity is the player, await input. if energy.current >= TURN_COST { + turns.insert(entity, TakingTurn {}).expect("Unable to insert turn."); energy.current -= TURN_COST; + if LOG_TICKS { + let name = if let Some(name) = names.get(entity) { &name.name } else { "Unknown entity" }; + console::log(format!( + "ENERGY SYSTEM: {} granted a turn. [leftover energy: {}].", + name, energy.current + )); + } if entity == *player { *runstate = RunState::AwaitingInput; - } else if cull_turn_by_distance(&player_pos, pos) { - continue; - } - turns.insert(entity, TakingTurn {}).expect("Unable to insert turn."); - if CONFIG.logging.log_ticks { - let name = if let Some(name) = names.get(entity) { - &name.name - } else { - "Unknown entity" - }; - console::log( - format!( - "ENERGY SYSTEM: {} granted a turn. [leftover energy: {}].", - name, - energy.current - ) - ); } } } } } - -fn get_burden_modifier(burdens: &ReadStorage, entity: Entity) -> f32 { - return if let Some(burden) = burdens.get(entity) { - match burden.level { - BurdenLevel::Burdened => SPEED_MOD_BURDENED, - BurdenLevel::Strained => SPEED_MOD_STRAINED, - BurdenLevel::Overloaded => SPEED_MOD_OVERLOADED, - } - } else { - 1.0 - }; -} - -fn get_overmap_modifier(map: &ReadExpect) -> f32 { - return if map.overmap { SPEED_MOD_OVERMAP_TRAVEL } else { 1.0 }; -} - -fn cull_turn_by_distance(player_pos: &Point, pos: &Position) -> bool { - let distance = DistanceAlg::Pythagoras.distance2d(*player_pos, Point::new(pos.x, pos.y)); - if distance > 20.0 { - return true; - } - return false; -} - -fn get_intrinsic_speed(intrinsics: &ReadStorage, entity: Entity) -> f32 { - if let Some(intrinsics) = intrinsics.get(entity) { - if intrinsics.list.contains(&crate::Intrinsic::Speed) { - return 4.0 / 3.0; - } - } - return 1.0; -} diff --git a/src/ai/flee_ai_system.rs b/src/ai/flee_ai_system.rs deleted file mode 100644 index 810dcb5..0000000 --- a/src/ai/flee_ai_system.rs +++ /dev/null @@ -1,70 +0,0 @@ -use crate::{ EntityMoved, Map, Position, TakingTurn, Telepath, Viewshed, WantsToFlee }; -use bracket_lib::prelude::*; -use specs::prelude::*; - -pub struct FleeAI {} - -impl<'a> System<'a> for FleeAI { - #[allow(clippy::type_complexity)] - type SystemData = ( - WriteStorage<'a, TakingTurn>, - WriteStorage<'a, WantsToFlee>, - WriteStorage<'a, Position>, - WriteExpect<'a, Map>, - WriteStorage<'a, Viewshed>, - WriteStorage<'a, Telepath>, - WriteStorage<'a, EntityMoved>, - Entities<'a>, - ); - - fn run(&mut self, data: Self::SystemData) { - let ( - mut turns, - mut wants_to_flee, - mut positions, - mut map, - mut viewsheds, - mut telepaths, - mut entity_moved, - entities, - ) = data; - let mut turn_done: Vec = Vec::new(); - for (entity, _turn, mut pos, fleeing, mut viewshed) in ( - &entities, - &turns, - &mut positions, - &wants_to_flee, - &mut viewsheds, - ).join() { - turn_done.push(entity); - let my_idx = map.xy_idx(pos.x, pos.y); - map.populate_blocked(); - let flee_map = DijkstraMap::new( - map.width as usize, - map.height as usize, - &fleeing.indices, - &*map, - 100.0 - ); - let flee_target = DijkstraMap::find_highest_exit(&flee_map, my_idx, &*map); - if let Some(flee_target) = flee_target { - if !crate::spatial::is_blocked(flee_target as usize) { - crate::spatial::move_entity(entity, my_idx, flee_target); - viewshed.dirty = true; - if let Some(is_telepath) = telepaths.get_mut(entity) { - is_telepath.dirty = true; - } - pos.x = (flee_target as i32) % map.width; - pos.y = (flee_target as i32) / map.width; - entity_moved - .insert(entity, EntityMoved {}) - .expect("Unable to insert EntityMoved"); - } - } - } - wants_to_flee.clear(); - for done in turn_done.iter() { - turns.remove(*done); - } - } -} diff --git a/src/ai/mod.rs b/src/ai/mod.rs index 0fb5d1e..88553eb 100644 --- a/src/ai/mod.rs +++ b/src/ai/mod.rs @@ -1,5 +1,5 @@ mod energy_system; -pub use energy_system::EnergySystem; +pub use energy_system::{EnergySystem, NORMAL_SPEED}; mod turn_status_system; pub use turn_status_system::TurnStatusSystem; mod quip_system; @@ -7,16 +7,4 @@ pub use quip_system::QuipSystem; mod regen_system; pub use regen_system::RegenSystem; mod encumbrance_system; -pub use encumbrance_system::EncumbranceSystem; -mod adjacent_ai_system; -pub use adjacent_ai_system::AdjacentAI; -mod visible_ai_system; -pub use visible_ai_system::VisibleAI; -mod approach_ai_system; -pub use approach_ai_system::ApproachAI; -mod chase_ai_system; -pub use chase_ai_system::ChaseAI; -mod flee_ai_system; -pub use flee_ai_system::FleeAI; -mod default_move_system; -pub use default_move_system::DefaultAI; +pub use encumbrance_system::{EncumbranceSystem, CARRY_CAPACITY_PER_STRENGTH}; diff --git a/src/ai/quip_system.rs b/src/ai/quip_system.rs index 87bda2e..5f25cee 100644 --- a/src/ai/quip_system.rs +++ b/src/ai/quip_system.rs @@ -1,5 +1,5 @@ -use crate::{ gamelog, gui::renderable_colour, Name, Quips, Renderable, TakingTurn, Viewshed }; -use bracket_lib::prelude::*; +use crate::{gamelog, Name, Quips, TakingTurn, Viewshed}; +use rltk::prelude::*; use specs::prelude::*; pub struct QuipSystem {} @@ -7,10 +7,8 @@ pub struct QuipSystem {} impl<'a> System<'a> for QuipSystem { #[allow(clippy::type_complexity)] type SystemData = ( - Entities<'a>, WriteStorage<'a, Quips>, ReadStorage<'a, Name>, - ReadStorage<'a, Renderable>, ReadStorage<'a, TakingTurn>, ReadExpect<'a, Point>, ReadStorage<'a, Viewshed>, @@ -18,30 +16,17 @@ impl<'a> System<'a> for QuipSystem { ); fn run(&mut self, data: Self::SystemData) { - let (entities, mut quips, names, renderables, turns, player_pos, viewsheds, mut rng) = data; - for (entity, quip, name, viewshed, _turn) in ( - &entities, - &mut quips, - &names, - &viewsheds, - &turns, - ).join() { - if - !quip.available.is_empty() && - viewshed.visible_tiles.contains(&player_pos) && - rng.roll_dice(1, 6) == 1 - { + let (mut quips, names, turns, player_pos, viewsheds, mut rng) = data; + for (quip, name, viewshed, _turn) in (&mut quips, &names, &viewsheds, &turns).join() { + if !quip.available.is_empty() && viewshed.visible_tiles.contains(&player_pos) && rng.roll_dice(1, 6) == 1 { let quip_index = if quip.available.len() == 1 { 0 } else { (rng.roll_dice(1, quip.available.len() as i32) - 1) as usize }; - gamelog::Logger - ::new() + gamelog::Logger::new() .append("The") - .colour(renderable_colour(&renderables, entity)) - .append(&name.name) - .colour(WHITE) + .npc_name(&name.name) .append_n("says \"") .append_n(&quip.available[quip_index]) .append("\"") diff --git a/src/ai/regen_system.rs b/src/ai/regen_system.rs index 8114c4e..8e17e3d 100644 --- a/src/ai/regen_system.rs +++ b/src/ai/regen_system.rs @@ -1,99 +1,40 @@ -use crate::{ - gamelog, - gui::Class, - Attributes, - Clock, - HasClass, - Player, - Pools, - Position, - RandomNumberGenerator, - TakingTurn, - Intrinsics, -}; +use crate::{gamelog, Clock, Player, Pools, Position, RunState, TakingTurn}; use specs::prelude::*; -use crate::data::events::*; pub struct RegenSystem {} const MONSTER_HP_REGEN_TURN: i32 = 20; const MONSTER_HP_REGEN_PER_TICK: i32 = 1; -const WIZARD_MP_REGEN_MOD: i32 = 3; -const NONWIZARD_MP_REGEN_MOD: i32 = 4; -const MP_REGEN_BASE: i32 = 38; -const MP_REGEN_DIVISOR: i32 = 6; -const MIN_MP_REGEN_PER_TURN: i32 = 1; - impl<'a> System<'a> for RegenSystem { #[allow(clippy::type_complexity)] type SystemData = ( ReadStorage<'a, Clock>, Entities<'a>, + ReadExpect<'a, RunState>, ReadStorage<'a, Position>, WriteStorage<'a, Pools>, ReadStorage<'a, TakingTurn>, ReadStorage<'a, Player>, - ReadStorage<'a, HasClass>, - ReadStorage<'a, Attributes>, - WriteExpect<'a, RandomNumberGenerator>, - ReadStorage<'a, Intrinsics>, - ReadExpect<'a, Entity>, ); fn run(&mut self, data: Self::SystemData) { - let ( - clock, - entities, - positions, - mut pools, - turns, - player, - classes, - attributes, - mut rng, - intrinsics, - player_entity, - ) = data; - let mut clock_turn = false; - for (_e, _c, _t) in (&entities, &clock, &turns).join() { - clock_turn = true; - } - if !clock_turn { + let (clock, entities, runstate, positions, mut pools, turns, player) = data; + if *runstate != RunState::Ticking { return; } - // Monster HP regen - let current_turn = gamelog::get_event_count(EVENT::COUNT_TURN); - if current_turn % MONSTER_HP_REGEN_TURN == 0 { - for (_e, _p, pool, _player) in (&entities, &positions, &mut pools, !&player).join() { - try_hp_regen_tick(pool, MONSTER_HP_REGEN_PER_TICK); + for (_e, _c, _t) in (&entities, &clock, &turns).join() { + let current_turn = gamelog::get_event_count("turns") + 1; + if current_turn % MONSTER_HP_REGEN_TURN == 0 { + for (_e, _p, pool, _player) in (&entities, &positions, &mut pools, !&player).join() { + try_hp_regen_tick(pool, MONSTER_HP_REGEN_PER_TICK); + } } - } - // Player HP regen - let level = gamelog::get_event_count(EVENT::COUNT_LEVEL); - if - current_turn % get_player_hp_regen_turn(level) == 0 || - intrinsics.get(*player_entity).unwrap().list.contains(&crate::Intrinsic::Regeneration) - { - for (_e, _p, pool, _player) in (&entities, &positions, &mut pools, &player).join() { - try_hp_regen_tick(pool, get_player_hp_regen_per_tick(level)); - } - } - // Both MP regen - for (e, _p, pool) in (&entities, &positions, &mut pools).join() { - let is_wizard = if let Some(class) = classes.get(e) { - class.name == Class::Wizard - } else { - false - }; - let numerator = if is_wizard { WIZARD_MP_REGEN_MOD } else { NONWIZARD_MP_REGEN_MOD }; - let multiplier: f32 = (numerator as f32) / (MP_REGEN_DIVISOR as f32); - let mp_regen_tick = (((MP_REGEN_BASE - pool.level) as f32) * multiplier) as i32; - if current_turn % mp_regen_tick == 0 { - try_mana_regen_tick( - pool, - rng.roll_dice(1, get_mana_regen_per_tick(e, &attributes)) - ); + let level = gamelog::get_event_count("player_level"); + if current_turn % get_player_hp_regen_turn(level) == 0 { + for (_e, _p, pool, _player) in (&entities, &positions, &mut pools, &player).join() { + try_hp_regen_tick(pool, get_player_hp_regen_per_tick(level)); + } } } } @@ -101,7 +42,7 @@ impl<'a> System<'a> for RegenSystem { fn get_player_hp_regen_turn(level: i32) -> i32 { if level < 10 { - return 42 / (level + 2) + 1; + return (42 / (level + 2)) + 1; } else { return 3; } @@ -118,16 +59,3 @@ fn get_player_hp_regen_per_tick(level: i32) -> i32 { fn try_hp_regen_tick(pool: &mut Pools, amount: i32) { pool.hit_points.current = i32::min(pool.hit_points.current + amount, pool.hit_points.max); } - -fn get_mana_regen_per_tick(e: Entity, attributes: &ReadStorage) -> i32 { - let regen = if let Some(attributes) = attributes.get(e) { - (attributes.intelligence.bonus + attributes.wisdom.bonus) / 2 + MIN_MP_REGEN_PER_TURN - } else { - MIN_MP_REGEN_PER_TURN - }; - return i32::max(regen, 1); -} - -fn try_mana_regen_tick(pool: &mut Pools, amount: i32) { - pool.mana.current = i32::min(pool.mana.current + amount, pool.mana.max); -} diff --git a/src/ai/turn_status_system.rs b/src/ai/turn_status_system.rs index e072e45..0bd7854 100644 --- a/src/ai/turn_status_system.rs +++ b/src/ai/turn_status_system.rs @@ -1,18 +1,6 @@ -use crate::{ - effects::{ add_effect, EffectType, Targets }, - gamelog, - gui::renderable_colour, - Clock, - Confusion, - Name, - Renderable, - TakingTurn, - Item, - Prop, -}; -use bracket_lib::prelude::*; +use crate::{gamelog, Confusion, Name, ParticleBuilder, Position, RunState, TakingTurn}; +use rltk::prelude::*; use specs::prelude::*; -use crate::data::events::*; pub struct TurnStatusSystem {} @@ -20,111 +8,37 @@ impl<'a> System<'a> for TurnStatusSystem { #[allow(clippy::type_complexity)] type SystemData = ( WriteStorage<'a, TakingTurn>, - ReadStorage<'a, Clock>, WriteStorage<'a, Confusion>, Entities<'a>, + ReadExpect<'a, RunState>, ReadStorage<'a, Name>, - ReadExpect<'a, Entity>, - ReadStorage<'a, Renderable>, - ReadStorage<'a, Item>, - ReadStorage<'a, Prop>, + ReadStorage<'a, Position>, + WriteExpect<'a, ParticleBuilder>, ); fn run(&mut self, data: Self::SystemData) { - let ( - mut turns, - clock, - mut confusion, - entities, - names, - player_entity, - renderables, - items, - props, - ) = data; - let mut clock_tick = false; - for (_e, _c, _t) in (&entities, &clock, &turns).join() { - clock_tick = true; - } - if !clock_tick { + let (mut turns, mut confusion, entities, runstate, names, positions, mut particle_builder) = data; + if *runstate != RunState::Ticking { return; } - let mut logger = gamelog::Logger::new(); - let mut log = false; - let mut not_my_turn: Vec = Vec::new(); - let mut not_confused: Vec = Vec::new(); - for (entity, confused, name, _i, _p) in ( - &entities, - &mut confusion, - &names, - !&items, - !&props, - ).join() { + let mut remove_turn: Vec = Vec::new(); + let mut remove_confusion: Vec = Vec::new(); + for (entity, _turn, confused, name, pos) in (&entities, &mut turns, &mut confusion, &names, &positions).join() { confused.turns -= 1; if confused.turns < 1 { - not_confused.push(entity); - if entity == *player_entity { - logger = logger - .append("You") - .append("snap out of it."); - log = true; - } else { - logger = logger - .append("The") - .colour(renderable_colour(&renderables, entity)) - .append(&name.name) - .colour(WHITE) - .append("snaps out of it."); - log = true; - } - add_effect( - None, - EffectType::Particle { - glyph: to_cp437('!'), - fg: RGB::named(LIGHT_BLUE), - bg: RGB::named(BLACK), - lifespan: 200.0, - delay: 0.0, - }, - Targets::Entity { target: entity } - ); + remove_confusion.push(entity); + gamelog::Logger::new().npc_name(&name.name).colour(WHITE).append("snaps out of it.").log(); + particle_builder.request(pos.x, pos.y, RGB::named(LIGHT_BLUE), RGB::named(BLACK), to_cp437('!'), 200.0); } else { - not_my_turn.push(entity); - if entity == *player_entity { - logger = logger - .append("You") - .append("are confused!"); - log = true; - gamelog::record_event(EVENT::PlayerConfused(1)); - } else { - logger = logger - .append("The") - .colour(renderable_colour(&renderables, entity)) - .append(&name.name) - .colour(WHITE) - .append("is confused!"); - log = true; - } - add_effect( - None, - EffectType::Particle { - glyph: to_cp437('?'), - fg: RGB::named(MAGENTA), - bg: RGB::named(BLACK), - lifespan: 200.0, - delay: 0.0, - }, - Targets::Entity { target: entity } - ); + remove_turn.push(entity); + gamelog::Logger::new().npc_name(&name.name).colour(WHITE).append("is confused.").log(); + particle_builder.request(pos.x, pos.y, RGB::named(MAGENTA), RGB::named(BLACK), to_cp437('?'), 200.0); } } - if log { - logger.log(); - } - for e in not_my_turn { + for e in remove_turn { turns.remove(e); } - for e in not_confused { + for e in remove_confusion { confusion.remove(e); } } diff --git a/src/ai/visible_ai_system.rs b/src/ai/visible_ai_system.rs deleted file mode 100644 index 5441030..0000000 --- a/src/ai/visible_ai_system.rs +++ /dev/null @@ -1,158 +0,0 @@ -use crate::{ - raws::Reaction, - Chasing, - Faction, - HasAncestry, - Map, - Mind, - Position, - TakingTurn, - Telepath, - Viewshed, - WantsToApproach, - WantsToFlee, -}; -use bracket_lib::prelude::*; -use specs::prelude::*; -use std::collections::HashSet; - -pub struct VisibleAI {} - -impl<'a> System<'a> for VisibleAI { - #[allow(clippy::type_complexity)] - type SystemData = ( - ReadStorage<'a, TakingTurn>, - ReadStorage<'a, Faction>, - ReadStorage<'a, HasAncestry>, - ReadStorage<'a, Position>, - ReadExpect<'a, Map>, - WriteStorage<'a, WantsToApproach>, - WriteStorage<'a, WantsToFlee>, - Entities<'a>, - ReadExpect<'a, Entity>, - ReadStorage<'a, Viewshed>, - ReadStorage<'a, Telepath>, - ReadStorage<'a, Mind>, - WriteStorage<'a, Chasing>, - ); - - fn run(&mut self, data: Self::SystemData) { - let ( - turns, - factions, - ancestries, - positions, - map, - mut wants_to_approach, - mut wants_to_flee, - entities, - player, - viewsheds, - telepaths, - minds, - mut chasing, - ) = data; - - for (entity, _turn, pos, viewshed) in (&entities, &turns, &positions, &viewsheds).join() { - if entity == *player { - continue; - } - let this_idx = map.xy_idx(pos.x, pos.y); - let mut reactions: Vec<(usize, Reaction, Entity)> = Vec::new(); - let mut flee: Vec = Vec::new(); - let mut idxs: HashSet = HashSet::new(); - for visible_tile in viewshed.visible_tiles.iter() { - let idx = map.xy_idx(visible_tile.x, visible_tile.y); - if this_idx != idx { - evaluate(entity, idx, &ancestries, &factions, &mut reactions, None); - idxs.insert(idx); - } - } - if let Some(is_telepath) = telepaths.get(entity) { - for telepath_tile in is_telepath.telepath_tiles.iter() { - let idx = map.xy_idx(telepath_tile.x, telepath_tile.y); - // If we didn't already evaluate this idx (if it's not contained in the HashSet), - // and it's not the idx we're standing on, then evaluate here w/ minds taken into - // account. - if this_idx != idx && !idxs.contains(&idx) { - evaluate(entity, idx, &ancestries, &factions, &mut reactions, Some(&minds)); - } - } - } - reactions.sort_by(|(a, _, _), (b, _, _)| { - let (a_x, a_y) = (a % (map.width as usize), a / (map.width as usize)); - let dist_a = DistanceAlg::PythagorasSquared.distance2d( - Point::new(a_x, a_y), - Point::new(pos.x, pos.y) - ); - let dist_a_estimate = dist_a as i32; - let (b_x, b_y) = (b % (map.width as usize), b / (map.width as usize)); - let dist_b = DistanceAlg::PythagorasSquared.distance2d( - Point::new(b_x, b_y), - Point::new(pos.x, pos.y) - ); - let dist_b_estimate = dist_b as i32; - return dist_b_estimate.cmp(&dist_a_estimate); - }); - let mut found_flee = false; - for reaction in reactions.iter() { - match reaction.1 { - Reaction::Attack => { - if !found_flee { - wants_to_approach - .insert(entity, WantsToApproach { idx: reaction.0 as i32 }) - .expect("Error inserting WantsToApproach"); - chasing - .insert(entity, Chasing { target: reaction.2 }) - .expect("Unable to insert Chasing"); - continue; - } - } - Reaction::Flee => { - flee.push(reaction.0); - found_flee = true; - } - _ => {} - } - } - if !flee.is_empty() { - wants_to_flee - .insert(entity, WantsToFlee { indices: flee }) - .expect("Unable to insert"); - } - } - } -} - -fn evaluate( - entity: Entity, - idx: usize, - ancestries: &ReadStorage, - factions: &ReadStorage, - reactions: &mut Vec<(usize, Reaction, Entity)>, - minds: Option<&ReadStorage> -) { - crate::spatial::for_each_tile_content(idx, |other_entity| { - let mut check = true; - if minds.is_some() { - console::log("Minds got passed! Evaluating!"); - if minds.unwrap().get(other_entity).is_none() { - console::log("No brain here. Skipping!"); - check = false; - } - } - if check { - reactions.push(( - idx, - crate::raws::get_reactions( - entity, - other_entity, - &factions, - &ancestries, - &crate::raws::RAWS.lock().unwrap() - ), - other_entity, - )); - } - }); -} diff --git a/src/bystander_ai_system.rs b/src/bystander_ai_system.rs new file mode 100644 index 0000000..e8d4feb --- /dev/null +++ b/src/bystander_ai_system.rs @@ -0,0 +1,63 @@ +use super::{Bystander, EntityMoved, Map, Position, TakingTurn, Viewshed}; +use specs::prelude::*; + +pub struct BystanderAI {} + +impl<'a> System<'a> for BystanderAI { + #[allow(clippy::type_complexity)] + type SystemData = ( + WriteExpect<'a, Map>, + Entities<'a>, + WriteStorage<'a, Viewshed>, + ReadStorage<'a, Bystander>, + WriteStorage<'a, Position>, + WriteStorage<'a, EntityMoved>, + WriteExpect<'a, rltk::RandomNumberGenerator>, + ReadStorage<'a, TakingTurn>, + ); + + fn run(&mut self, data: Self::SystemData) { + let (mut map, entities, mut viewshed, bystander, mut position, mut entity_moved, mut rng, turns) = data; + + for (entity, mut viewshed, _bystander, mut pos, _turn) in + (&entities, &mut viewshed, &bystander, &mut position, &turns).join() + { + if try_move_randomly(&mut pos, &mut rng, &mut map, &mut viewshed) { + entity_moved.insert(entity, EntityMoved {}).expect("Unable to insert marker"); + } + } + } +} + +pub fn try_move_randomly( + pos: &mut Position, + rng: &mut rltk::RandomNumberGenerator, + map: &mut Map, + viewshed: &mut Viewshed, +) -> bool { + // Try to move randomly + let mut x = pos.x; + let mut y = pos.y; + let move_roll = rng.roll_dice(1, 8); + match move_roll { + 1 => x -= 1, + 2 => x += 1, + 3 => y -= 1, + 4 => y += 1, + _ => {} + } + + if x > 0 && x < map.width - 1 && y > 0 && y < map.height - 1 { + let dest_idx = map.xy_idx(x, y); + if !map.blocked[dest_idx] { + let idx = map.xy_idx(pos.x, pos.y); + map.blocked[idx] = false; + pos.x = x; + pos.y = y; + map.blocked[dest_idx] = true; + viewshed.dirty = true; + return true; + } + } + return false; +} diff --git a/src/camera.rs b/src/camera.rs index 00b6869..e304254 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -1,37 +1,27 @@ -use super::{ Hidden, Map, Mind, Position, Prop, Renderable }; -use bracket_lib::prelude::*; +use super::{Hidden, Map, Mind, Position, Prop, Renderable}; +use rltk::prelude::*; use specs::prelude::*; use std::ops::Mul; const SHOW_BOUNDARIES: bool = false; -pub fn get_screen_bounds(ecs: &World, _ctx: &mut BTerm) -> (i32, i32, i32, i32, i32, i32) { +pub fn get_screen_bounds(ecs: &World, _ctx: &mut Rltk) -> (i32, i32, i32, i32, i32, i32) { let player_pos = ecs.fetch::(); - let map = ecs.fetch::(); - let (x_chars, y_chars, mut x_offset, mut y_offset) = (69, 41, 1, 10); + //let (x_chars, y_chars) = ctx.get_char_size(); + let (x_chars, y_chars, x_offset, y_offset) = (69, 41, 1, 10); let centre_x = (x_chars / 2) as i32; let centre_y = (y_chars / 2) as i32; - let min_x = if map.width < (x_chars as i32) { - x_offset += ((x_chars as i32) - map.width) / 2; - 0 - } else { - (player_pos.x - centre_x).clamp(0, map.width - (x_chars as i32)) - }; - let min_y = if map.height < (y_chars as i32) { - y_offset += ((y_chars as i32) - map.height) / 2; - 0 - } else { - (player_pos.y - centre_y).clamp(0, map.height - (y_chars as i32)) - }; - let max_x = min_x + (x_chars as i32); - let max_y = min_y + (y_chars as i32); + let min_x = player_pos.x - centre_x; + let min_y = player_pos.y - centre_y; + let max_x = min_x + x_chars as i32; + let max_y = min_y + y_chars as i32; (min_x, max_x, min_y, max_y, x_offset, y_offset) } -pub fn render_camera(ecs: &World, ctx: &mut BTerm) { +pub fn render_camera(ecs: &World, ctx: &mut Rltk) { let map = ecs.fetch::(); let (min_x, max_x, min_y, max_y, x_offset, y_offset) = get_screen_bounds(ecs, ctx); @@ -43,22 +33,11 @@ pub fn render_camera(ecs: &World, ctx: &mut BTerm) { if t_x >= 0 && t_x < map.width && t_y >= 0 && t_y < map.height { let idx = map.xy_idx(t_x, t_y); if map.revealed_tiles[idx] { - let (glyph, fg, bg) = crate::map::themes::get_tile_renderables_for_id( - idx, - &*map, - Some(*ecs.fetch::()), - None - ); + let (glyph, fg, bg) = crate::map::themes::get_tile_renderables_for_id(idx, &*map); ctx.set(x + x_offset, y + y_offset, fg, bg, glyph); } } else if SHOW_BOUNDARIES { - ctx.set( - x + x_offset, - y + y_offset, - RGB::named(DARKSLATEGRAY), - RGB::named(BLACK), - to_cp437('#') - ); + ctx.set(x + x_offset, y + y_offset, RGB::named(DARKSLATEGRAY), RGB::named(BLACK), rltk::to_cp437('#')); } x += 1; } @@ -84,30 +63,23 @@ pub fn render_camera(ecs: &World, ctx: &mut BTerm) { if pos.x < max_x && pos.y < max_y && pos.x >= min_x && pos.y >= min_y { let mut draw = false; let mut fg = render.fg; - let mut bg = crate::map::themes::get_tile_renderables_for_id( - idx, - &*map, - Some(*ecs.fetch::()), - None - ).2; + let bg = if render.bg == (RGB { r: 0.0, g: 0.0, b: 0.0 }) { + crate::map::themes::get_tile_renderables_for_id(idx, &*map).2 + } else { + render.bg + }; // Draw entities on visible tiles if map.visible_tiles[idx] { draw = true; } else { - fg = fg.mul(crate::data::visuals::NON_VISIBLE_MULTIPLIER); + fg = fg.mul(0.75); // We don't darken BG, because get_tile_renderables_for_id handles this. } - // Draw entities with minds within telepath range - if !draw { - if map.telepath_tiles[idx] { - let has_mind = minds.get(*ent); - if let Some(_) = has_mind { - draw = true; - if !map.revealed_tiles[idx] { - bg = RGB::named(BLACK); - } - } + if map.telepath_tiles[idx] { + let has_mind = minds.get(*ent); + if let Some(_) = has_mind { + draw = true; } } // Draw all props @@ -118,20 +90,14 @@ pub fn render_camera(ecs: &World, ctx: &mut BTerm) { } } if draw { - ctx.set( - entity_offset_x + x_offset, - entity_offset_y + y_offset, - fg, - bg, - render.glyph - ); + ctx.set(entity_offset_x + x_offset, entity_offset_y + y_offset, fg, bg, render.glyph); } } } } } -pub fn render_debug_map(map: &Map, ctx: &mut BTerm) { +pub fn render_debug_map(map: &Map, ctx: &mut Rltk) { let player_pos = Point::new(map.width / 2, map.height / 2); let (x_chars, y_chars) = ctx.get_char_size(); @@ -139,9 +105,9 @@ pub fn render_debug_map(map: &Map, ctx: &mut BTerm) { let center_y = (y_chars / 2) as i32; let min_x = player_pos.x - center_x; - let max_x = min_x + (x_chars as i32); + let max_x = min_x + x_chars as i32; let min_y = player_pos.y - center_y; - let max_y = min_y + (y_chars as i32); + let max_y = min_y + y_chars as i32; let map_width = map.width; let map_height = map.height; @@ -153,16 +119,11 @@ pub fn render_debug_map(map: &Map, ctx: &mut BTerm) { if tx >= 0 && tx < map_width && ty >= 0 && ty < map_height { let idx = map.xy_idx(tx, ty); if map.revealed_tiles[idx] { - let (glyph, fg, bg) = crate::map::themes::get_tile_renderables_for_id( - idx, - &*map, - None, - None - ); + let (glyph, fg, bg) = crate::map::themes::get_tile_renderables_for_id(idx, &*map); ctx.set(x, y, fg, bg, glyph); } } else if SHOW_BOUNDARIES { - ctx.set(x, y, RGB::named(GRAY), RGB::named(BLACK), to_cp437('·')); + ctx.set(x, y, RGB::named(rltk::GRAY), RGB::named(rltk::BLACK), rltk::to_cp437('·')); } x += 1; } diff --git a/src/components.rs b/src/components.rs index 45ca1cf..217e862 100644 --- a/src/components.rs +++ b/src/components.rs @@ -1,12 +1,10 @@ -use crate::gui::Ancestry; -use crate::gui::Class; -use bracket_lib::prelude::*; -use serde::{ Deserialize, Serialize }; +use rltk::RGB; +use serde::{Deserialize, Serialize}; use specs::error::NoError; use specs::prelude::*; -use specs::saveload::{ ConvertSaveload, Marker }; +use specs::saveload::{ConvertSaveload, Marker}; use specs_derive::*; -use std::collections::{ HashMap, HashSet }; +use std::collections::HashMap; // Serialization helper code. We need to implement ConvertSaveload for each type that contains an // Entity. @@ -21,8 +19,7 @@ pub struct SerializationHelper { pub struct DMSerializationHelper { pub map: super::map::MasterDungeonMap, pub log: Vec>, - pub event_counts: HashMap, - pub events: HashMap>, + pub events: HashMap, } #[derive(Component, ConvertSaveload, Clone)] @@ -40,42 +37,21 @@ pub struct OtherLevelPosition { #[derive(Component, ConvertSaveload, Clone)] pub struct Renderable { - pub glyph: FontCharType, + pub glyph: rltk::FontCharType, pub fg: RGB, pub bg: RGB, pub render_order: i32, } -#[derive(Component, Debug, Serialize, Deserialize, Clone)] -pub struct Bleeds { - pub colour: RGB, -} - #[derive(Component, Debug, Serialize, Deserialize, Clone)] pub struct Player {} -#[derive(Component, Debug, Serialize, Deserialize, Clone)] -pub struct Faction { - pub name: String, -} - -#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)] -pub enum Movement { - Static, - Random, - RandomWaypoint { - path: Option>, - }, -} - -#[derive(Component, Debug, Serialize, Deserialize, Clone)] -pub struct MoveMode { - pub mode: Movement, -} - #[derive(Component, Debug, Serialize, Deserialize, Clone)] pub struct Prop {} +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct Monster {} + #[derive(Component, Debug, Serialize, Deserialize, Clone)] pub struct LootTable { pub table: String, @@ -94,6 +70,9 @@ pub struct Clock {} #[derive(Component, Debug, Serialize, Deserialize, Clone)] pub struct TakingTurn {} +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct Bystander {} + #[derive(Component, Debug, Serialize, Deserialize, Clone)] pub struct Quips { pub available: Vec, @@ -104,14 +83,14 @@ pub struct Mind {} #[derive(Component, ConvertSaveload, Clone)] pub struct Viewshed { - pub visible_tiles: Vec, + pub visible_tiles: Vec, pub range: i32, pub dirty: bool, } #[derive(Component, ConvertSaveload, Clone)] pub struct Telepath { - pub telepath_tiles: Vec, + pub telepath_tiles: Vec, pub range: i32, pub dirty: bool, } @@ -140,7 +119,6 @@ pub enum HungerState { Hungry, Weak, Fainting, - Starving, } #[derive(Component, Serialize, Deserialize, Clone)] @@ -152,16 +130,6 @@ pub struct HungerClock { #[derive(Component, Debug, Serialize, Deserialize, Clone)] pub struct ProvidesNutrition {} -#[derive(Component, Serialize, Deserialize, Clone)] -pub struct HasAncestry { - pub name: Ancestry, -} - -#[derive(Component, Serialize, Deserialize, Clone)] -pub struct HasClass { - pub name: Class, -} - #[derive(Debug, Serialize, Deserialize, Clone)] pub struct Pool { pub max: i32, @@ -198,24 +166,6 @@ pub struct Skills { pub skills: HashMap, } -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct KnownSpell { - pub display_name: String, - pub mana_cost: i32, -} - -#[derive(Component, Debug, Serialize, Deserialize, Clone)] -pub struct KnownSpells { - pub list: Vec, -} - -#[derive(Component, Debug, Serialize, Deserialize, Clone)] -pub struct GrantsSpell { - pub spell: String, -} - -// TODO: GrantsIntrinsic, Intrinsics, etc. ? Done the same way as spells? - #[derive(Component, Debug, Serialize, Deserialize, Clone)] pub struct Attributes { pub strength: Attribute, @@ -236,53 +186,18 @@ pub struct GrantsXP { pub amount: i32, } -#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)] -pub enum BUC { - Cursed, - Uncursed, - Blessed, +#[derive(Component, Debug, ConvertSaveload, Clone)] +pub struct SufferDamage { + pub amount: Vec<(i32, bool)>, } -impl BUC { - pub fn noncursed(&self) -> bool { - match self { - BUC::Cursed => false, - _ => true, - } - } -} - -#[derive(Component, Debug, Serialize, Deserialize, Eq, PartialEq, Hash, Clone)] -pub struct Beatitude { - pub buc: BUC, - pub known: bool, -} - -#[derive(Debug, Serialize, Deserialize, Copy, Clone, PartialEq, Eq)] -pub enum ItemType { - Amulet, - Weapon, - Armour, - Comestible, - Scroll, - Spellbook, - Potion, - Ring, - Wand, -} - -impl ItemType { - pub fn string(&self) -> &str { - match self { - ItemType::Amulet => "Amulets", - ItemType::Weapon => "Weapons", - ItemType::Armour => "Armour", - ItemType::Comestible => "Comestibles", - ItemType::Scroll => "Scrolls", - ItemType::Spellbook => "Spellbooks", - ItemType::Potion => "Potions", - ItemType::Ring => "Rings", - ItemType::Wand => "Wands", +impl SufferDamage { + pub fn new_damage(store: &mut WriteStorage, victim: Entity, amount: i32, from_player: bool) { + if let Some(suffering) = store.get_mut(victim) { + suffering.amount.push((amount, from_player)); + } else { + let dmg = SufferDamage { amount: vec![(amount, from_player)] }; + store.insert(victim, dmg).expect("Unable to insert damage."); } } } @@ -290,8 +205,7 @@ impl ItemType { #[derive(Component, Debug, Serialize, Deserialize, Clone)] pub struct Item { pub weight: f32, // in lbs - pub value: f32, // base - pub category: ItemType, + pub value: f32, // base } #[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Hash)] @@ -355,7 +269,6 @@ pub enum WeaponAttribute { #[derive(Component, Serialize, Deserialize, Clone)] pub struct MeleeWeapon { - pub damage_type: DamageType, pub attribute: WeaponAttribute, pub damage_n_dice: i32, pub damage_die_type: i32, @@ -366,7 +279,6 @@ pub struct MeleeWeapon { #[derive(Serialize, Deserialize, Clone)] pub struct NaturalAttack { pub name: String, - pub damage_type: DamageType, pub damage_n_dice: i32, pub damage_die_type: i32, pub damage_bonus: i32, @@ -383,11 +295,6 @@ pub struct ArmourClassBonus { pub amount: i32, } -#[derive(Component, ConvertSaveload, Clone)] -pub struct ToHitBonus { - pub amount: i32, -} - #[derive(Component, Serialize, Deserialize, Clone)] pub struct Equippable { pub slot: EquipmentSlot, @@ -399,6 +306,9 @@ pub struct Equipped { pub slot: EquipmentSlot, } +#[derive(Component, Debug, Serialize, Deserialize, Clone)] +pub struct Cursed {} + #[derive(Component, Debug, ConvertSaveload, Clone)] pub struct ProvidesHealing { pub n_dice: i32, @@ -406,108 +316,8 @@ pub struct ProvidesHealing { pub modifier: i32, } -#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, Serialize, Deserialize)] -pub enum DamageType { - Physical, - Magic, // e.g. magic missiles, silvered weapons - Fire, // e.g. fireball - Cold, // e.g. cone of cold - Poison, // e.g. poison gas - Forced, // Bypasses any immunities. e.g. Hunger ticks. -} - -impl DamageType { - pub fn is_magic(&self) -> bool { - match self { - DamageType::Magic | DamageType::Fire | DamageType::Cold => true, - _ => false, - } - } -} - -#[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)] -pub enum DamageModifier { - None, - Weakness, - Resistance, - Immune, -} - -impl DamageModifier { - const NONE_MOD: f32 = 1.0; - const WEAK_MOD: f32 = 2.0; - const RESIST_MOD: f32 = 0.5; - const IMMUNE_MOD: f32 = 0.0; - - pub fn multiplier(&self) -> f32 { - match self { - DamageModifier::None => Self::NONE_MOD, - DamageModifier::Weakness => Self::WEAK_MOD, - DamageModifier::Resistance => Self::RESIST_MOD, - DamageModifier::Immune => Self::IMMUNE_MOD, - } - } -} - -#[derive(Component, Serialize, Deserialize, Debug, Clone)] -pub struct HasDamageModifiers { - pub modifiers: HashMap, -} - -impl HasDamageModifiers { - pub fn modifier(&self, damage_type: &DamageType) -> &DamageModifier { - self.modifiers.get(damage_type).unwrap_or(&DamageModifier::None) - } -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub enum Intrinsic { - Regeneration, // Regenerate 1 HP on every tick - Speed, // 4/3x speed multiplier -} - -impl Intrinsic { - pub fn describe(&self) -> &str { - match self { - Intrinsic::Regeneration => "regenerates health", - Intrinsic::Speed => "is hasted", - } - } -} - -#[derive(Component, Serialize, Deserialize, Debug, Clone)] -pub struct Intrinsics { - pub list: HashSet, -} - -impl Intrinsics { - pub fn describe(&self) -> String { - let mut descriptions = Vec::new(); - for intrinsic in &self.list { - descriptions.push(intrinsic.describe()); - } - match descriptions.len() { - 0 => - unreachable!("describe() should never be called on an empty Intrinsics component."), - 1 => format!("It {}.", descriptions[0]), - _ => { - let last = descriptions.pop().unwrap(); - let joined = descriptions.join(", "); - format!("It {}, and {}.", joined, last) - } - } - } -} - -#[derive(Component, Serialize, Deserialize, Debug, Clone)] -pub struct IntrinsicChanged { - pub gained: HashSet, - pub lost: HashSet, -} - #[derive(Component, Debug, ConvertSaveload, Clone)] pub struct InflictsDamage { - pub damage_type: DamageType, pub n_dice: i32, pub sides: i32, pub modifier: i32, @@ -528,9 +338,6 @@ pub struct Confusion { pub turns: i32, } -#[derive(Component, Serialize, Deserialize, Clone)] -pub struct Blind {} - #[derive(Component, Serialize, Deserialize, Clone)] pub struct MagicMapper {} @@ -558,71 +365,21 @@ pub struct WantsToRemoveItem { #[derive(Component, Debug, ConvertSaveload)] pub struct WantsToUseItem { pub item: Entity, - pub target: Option, -} - -#[derive(Component, Debug, Serialize, Deserialize, Clone)] -pub struct WantsToApproach { - pub idx: i32, -} - -#[derive(Component, Debug, Serialize, Deserialize, Clone)] -pub struct WantsToFlee { - pub indices: Vec, // Dijkstra -} - -#[derive(Component, Debug, ConvertSaveload, Clone)] -pub struct Chasing { - pub target: Entity, + pub target: Option, } #[derive(Component, Debug, Serialize, Deserialize, Clone)] pub struct Consumable {} #[derive(Component, Debug, ConvertSaveload)] -pub struct Charges { +pub struct Wand { pub uses: i32, pub max_uses: i32, } -#[derive(Component, Serialize, Deserialize, Clone)] -pub struct SpawnParticleLine { - pub glyph: FontCharType, - pub tail_glyph: FontCharType, - pub colour: RGB, - pub lifetime_ms: f32, - pub trail_colour: RGB, - pub trail_lifetime_ms: f32, -} - -#[derive(Component, Serialize, Deserialize, Clone)] -pub struct SpawnParticleSimple { - pub glyph: FontCharType, - pub colour: RGB, - pub lifetime_ms: f32, -} - -#[derive(Component, Serialize, Deserialize, Clone)] -pub struct SpawnParticleBurst { - pub glyph: FontCharType, - pub head_glyph: FontCharType, - pub tail_glyph: FontCharType, - pub colour: RGB, - pub lerp: RGB, - pub lifetime_ms: f32, - pub trail_colour: RGB, - pub trail_lifetime_ms: f32, -} - #[derive(Component, Debug, Serialize, Deserialize, Clone)] pub struct Destructible {} -#[derive(Component, Debug, Serialize, Deserialize, Clone)] -pub struct ProvidesRemoveCurse {} - -#[derive(Component, Debug, Serialize, Deserialize, Clone)] -pub struct ProvidesIdentify {} - #[derive(Component, Debug, Serialize, Deserialize, Clone)] pub struct Digger {} @@ -632,9 +389,6 @@ pub struct Hidden {} #[derive(Component, Debug, Serialize, Deserialize, Clone)] pub struct SingleActivation {} -#[derive(Component, Debug, Serialize, Deserialize, Clone)] -pub struct IdentifiedBeatitude {} - #[derive(Component, Clone, ConvertSaveload)] pub struct ParticleLifetime { pub lifetime_ms: f32, @@ -648,20 +402,3 @@ pub struct EntityMoved {} #[derive(Component, Debug, Serialize, Deserialize, Clone)] pub struct MultiAttack {} - -#[derive(Component, Debug, Serialize, Deserialize, Clone)] -pub struct Stackable {} - -#[derive(Component, Debug, Serialize, Deserialize, Clone)] -pub struct WantsToRemoveKey {} - -#[derive(Component, Debug, Serialize, Deserialize, Clone)] -pub struct WantsToDelete {} - -#[derive(Component, Debug, Serialize, Deserialize, Clone)] -pub struct Key { - pub idx: usize, -} - -#[derive(Component, Debug, Serialize, Deserialize, Clone)] -pub struct WantsToAssignKey {} diff --git a/src/config/mod.rs b/src/config/mod.rs deleted file mode 100644 index f86c5df..0000000 --- a/src/config/mod.rs +++ /dev/null @@ -1,140 +0,0 @@ -use bracket_lib::prelude::*; -use toml::Value; -use serde::{ Serialize, Deserialize }; - -lazy_static! { - pub static ref CONFIG: Config = try_load_configuration(); -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct Config { - pub logging: LogConfig, - pub visuals: VisualConfig, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct LogConfig { - pub show_mapgen: bool, - pub log_combat: bool, - pub log_spawning: bool, - pub log_ticks: bool, -} - -#[derive(Debug, Serialize, Deserialize)] -pub struct VisualConfig { - pub with_scanlines: bool, - pub with_screen_burn: bool, - pub with_darken_by_distance: bool, - pub use_bitset_walls: bool, - pub use_coloured_tile_bg: bool, - pub add_colour_variance: bool, -} - -impl Default for Config { - fn default() -> Self { - Config { - logging: LogConfig { - show_mapgen: false, - log_combat: false, - log_spawning: false, - log_ticks: false, - }, - visuals: VisualConfig { - with_scanlines: false, - with_screen_burn: false, - with_darken_by_distance: true, - use_bitset_walls: true, - use_coloured_tile_bg: true, - add_colour_variance: true, - }, - } - } -} - -impl Config { - pub fn load_from_file(filename: &str) -> Config { - if let Ok(contents) = std::fs::read_to_string(filename) { - let parsed_config: Result = toml::from_str(&contents); - if let Ok(parsed_config) = parsed_config { - let mut config = Config::default(); - let mut requires_write = false; - requires_write |= config.logging.apply_values(&parsed_config); - requires_write |= config.visuals.apply_values(&parsed_config); - if requires_write { - if let Err(write_err) = config.save_to_file(filename) { - console::log(format!("Error writing config: {:?}", write_err)); - } - } - - return config; - } - } - let config = Config::default(); - if let Err(write_err) = config.save_to_file(filename) { - console::log(format!("Error writing config: {:?}", write_err)); - } - return config; - } - pub fn save_to_file(&self, filename: &str) -> Result<(), Box> { - let toml_string = toml::to_string(self)?; - std::fs::write(filename, toml_string)?; - Ok(()) - } -} - -macro_rules! apply_bool_value { - ($config:expr, $parsed_config:expr, $changed:expr, $field:ident) => { - if let Some(value) = $parsed_config.get(stringify!($field)).and_then(|v| v.as_bool()) { - if $config.$field != value { - $config.$field = value; - $changed = true; - } - } - }; -} - -trait Section { - fn apply_values(&mut self, parsed_config: &Value) -> bool; -} - -impl Section for LogConfig { - fn apply_values(&mut self, parsed_config: &Value) -> bool { - if let Some(section) = parsed_config.get("logging") { - let mut missing = false; - apply_bool_value!(self, section, missing, log_spawning); - apply_bool_value!(self, section, missing, log_ticks); - missing - } else { - true - } - } -} - -impl Section for VisualConfig { - fn apply_values(&mut self, parsed_config: &Value) -> bool { - if let Some(section) = parsed_config.get("visuals") { - let mut missing = false; - apply_bool_value!(self, section, missing, with_scanlines); - apply_bool_value!(self, section, missing, with_screen_burn); - apply_bool_value!(self, section, missing, with_darken_by_distance); - apply_bool_value!(self, section, missing, use_bitset_walls); - apply_bool_value!(self, section, missing, use_coloured_tile_bg); - apply_bool_value!(self, section, missing, add_colour_variance); - missing - } else { - true - } - } -} - -#[cfg(not(target_arch = "wasm32"))] -pub fn try_load_configuration() -> Config { - let config: Config = Config::load_from_file("config.toml"); - return config; -} - -#[cfg(target_arch = "wasm32")] -pub fn try_load_configuration() -> Config { - let config = Config::default(); - return config; -} diff --git a/src/damage_system.rs b/src/damage_system.rs index b0cc566..d44e379 100644 --- a/src/damage_system.rs +++ b/src/damage_system.rs @@ -1,22 +1,118 @@ use super::{ - gamelog, - gui::renderable_colour, - Equipped, - InBackpack, - Item, - LootTable, - Name, - Player, - Pools, - Position, - Renderable, - RunState, - WantsToRemoveKey, - WantsToDelete, + gamelog, Attributes, Equipped, GrantsXP, InBackpack, Item, LootTable, Map, Name, ParticleBuilder, Player, Pools, + Position, RunState, SufferDamage, }; -use bracket_lib::prelude::*; +use crate::gamesystem::{mana_per_level, player_hp_per_level}; +use rltk::prelude::*; use specs::prelude::*; -use crate::data::events; + +pub struct DamageSystem {} + +impl<'a> System<'a> for DamageSystem { + #[allow(clippy::type_complexity)] + type SystemData = ( + WriteStorage<'a, Pools>, + WriteStorage<'a, SufferDamage>, + ReadStorage<'a, Position>, + WriteExpect<'a, Map>, + Entities<'a>, + ReadExpect<'a, Entity>, + ReadStorage<'a, Attributes>, + WriteExpect<'a, rltk::RandomNumberGenerator>, + ReadStorage<'a, GrantsXP>, + WriteExpect<'a, ParticleBuilder>, + ReadExpect<'a, rltk::Point>, + ); + + fn run(&mut self, data: Self::SystemData) { + let ( + mut stats, + mut damage, + positions, + mut map, + entities, + player, + attributes, + mut rng, + xp_granters, + mut particle_builder, + player_pos, + ) = data; + let mut xp_gain = 0; + + for (entity, mut stats, damage) in (&entities, &mut stats, &damage).join() { + for dmg in damage.amount.iter() { + if !stats.god { + stats.hit_points.current -= dmg.0; + } + let pos = positions.get(entity); + if let Some(pos) = pos { + let idx = map.xy_idx(pos.x, pos.y); + map.bloodstains.insert(idx); + } + + if stats.hit_points.current < 1 && dmg.1 { + let gives_xp = xp_granters.get(entity); + if let Some(xp_value) = gives_xp { + xp_gain += xp_value.amount; + } + } + } + } + + if xp_gain != 0 { + let mut player_stats = stats.get_mut(*player).unwrap(); + let player_attributes = attributes.get(*player).unwrap(); + player_stats.xp += xp_gain; + let mut next_level_requirement = -1; + if player_stats.level < 10 { + next_level_requirement = 20 * 2_i32.pow(player_stats.level as u32 - 1); + } else if player_stats.level < 20 { + next_level_requirement = 10000 * 2_i32.pow(player_stats.level as u32 - 10); + } else if player_stats.level < 30 { + next_level_requirement = 10000000 * (player_stats.level - 19); + } + if next_level_requirement != -1 && player_stats.xp >= next_level_requirement { + // We've gone up a level! + player_stats.level += 1; + gamelog::record_event("player_level", 1); + gamelog::Logger::new() + .append("Welcome to experience level") + .append(player_stats.level) + .append(".") + .log(); + for i in 0..5 { + if player_pos.y - i > 1 { + particle_builder.request( + player_pos.x, + player_pos.y - i, + rltk::RGB::named(rltk::GOLD), + rltk::RGB::named(rltk::BLACK), + rltk::to_cp437('*'), + 200.0, + ); + } + } + // Roll for HP gain this level + let hp_gained = player_hp_per_level( + &mut rng, + player_attributes.constitution.base + player_attributes.constitution.modifiers, + ); + player_stats.hit_points.max += hp_gained; + player_stats.hit_points.current += hp_gained; + // Roll for MANA gain this level + let mana_gained = mana_per_level( + &mut rng, + player_attributes.intelligence.base + player_attributes.intelligence.modifiers, + ); + player_stats.mana.max += mana_gained; + player_stats.mana.current += mana_gained; + } + } + // Clear the queue + damage.clear(); + } +} pub fn delete_the_dead(ecs: &mut World) { let mut dead: Vec = Vec::new(); @@ -27,7 +123,6 @@ pub fn delete_the_dead(ecs: &mut World) { let names = ecs.read_storage::(); let items = ecs.read_storage::(); let entities = ecs.entities(); - let renderables = ecs.read_storage::(); for (entity, stats) in (&entities, &combat_stats).join() { if stats.hit_points.current < 1 { let player = players.get(entity); @@ -37,26 +132,22 @@ pub fn delete_the_dead(ecs: &mut World) { if let Some(victim_name) = victim_name { let item = items.get(entity); if let Some(_item) = item { - gamelog::Logger - ::new() + gamelog::Logger::new() .append("The") - .colour(renderable_colour(&renderables, entity)) - .append(&victim_name.name) - .colour(WHITE) + .npc_name(&victim_name.name) + .colour(rltk::WHITE) .append("is destroyed!") .log(); } else { - gamelog::Logger - ::new() + gamelog::Logger::new() .append("The") - .colour(renderable_colour(&renderables, entity)) - .append(&victim_name.name) - .colour(WHITE) + .npc_name(&victim_name.name) + .colour(rltk::WHITE) .append("dies!") .log(); } } - dead.push(entity); + dead.push(entity) } // The player died, go to GameOver. Some(_) => { @@ -67,25 +158,14 @@ pub fn delete_the_dead(ecs: &mut World) { } } } - let (mut items_to_delete, loot_to_spawn) = handle_dead_entity_items(ecs, &dead); - { - let entities = ecs.entities(); - let removekeys = ecs.read_storage::(); - let delete = ecs.read_storage::(); - // Add items marked for deletion to the list, but only if they've already had their - // key assignments handled, to ensurew we don't leave any dangling references behind. - for (e, _d, _r) in (&entities, &delete, !&removekeys).join() { - items_to_delete.push(e); - } - } + let (items_to_delete, loot_to_spawn) = handle_dead_entity_items(ecs, &dead); for loot in loot_to_spawn { crate::raws::spawn_named_entity( &crate::raws::RAWS.lock().unwrap(), ecs, &loot.0, - None, crate::raws::SpawnType::AtPosition { x: loot.1.x, y: loot.1.y }, - 0 + 0, ); } for item in items_to_delete { @@ -93,16 +173,12 @@ pub fn delete_the_dead(ecs: &mut World) { } // For everything that died, increment the event log, and delete. for victim in dead { - gamelog::record_event(events::EVENT::Turn(1)); - // TODO: Delete stuff from inventory? This should be handled elsewhere. + gamelog::record_event("death_count", 1); ecs.delete_entity(victim).expect("Unable to delete."); } } -fn handle_dead_entity_items( - ecs: &mut World, - dead: &Vec -) -> (Vec, Vec<(String, Position)>) { +fn handle_dead_entity_items(ecs: &mut World, dead: &Vec) -> (Vec, Vec<(String, Position)>) { let mut to_drop: Vec<(Entity, Position)> = Vec::new(); let mut to_spawn: Vec<(String, Position)> = Vec::new(); let entities = ecs.entities(); @@ -133,11 +209,8 @@ fn handle_dead_entity_items( if let Some(table) = loot_tables.get(*victim) { let roll: f32 = rng.rand(); if roll < table.chance { - let potential_drop = crate::raws::roll_on_loot_table( - &crate::raws::RAWS.lock().unwrap(), - &mut rng, - &table.table - ); + let potential_drop = + crate::raws::roll_on_loot_table(&crate::raws::RAWS.lock().unwrap(), &mut rng, &table.table); if let Some(id) = potential_drop { if let Some(pos) = pos { to_spawn.push((id, pos.clone())); diff --git a/src/data/char_create.rs b/src/data/char_create.rs deleted file mode 100644 index 3426cf9..0000000 --- a/src/data/char_create.rs +++ /dev/null @@ -1,49 +0,0 @@ -// --- GUI --- -pub const CHAR_CREATE_HEADER: &str = "Who are you? [Aa-Zz]"; -pub const ANCESTRY_INFO_HEADER: &str = "Your ancestry grants..."; -pub const CLASS_INFO_HEADER: &str = "Your class grants..."; -// --- ANCESTRY RENDERABLES --- -pub const ELF_GLYPH: char = '@'; -pub const ELF_COLOUR: (u8, u8, u8) = (0, 255, 0); -pub const DWARF_GLYPH: char = 'h'; -pub const DWARF_COLOUR: (u8, u8, u8) = (255, 0, 0); -pub const CATFOLK_GLYPH: char = '@'; -pub const CATFOLK_COLOUR: (u8, u8, u8) = (200, 200, 255); -// --- ANCESTRY BONUSES --- -pub const ELF_SPEED_BONUS: i32 = 1; -pub const ELF_TELEPATH_RANGE: i32 = 6; -pub const DWARF_DEFENCE_MOD: i32 = 1; -pub const CATFOLK_SPEED_BONUS: i32 = 2; -// --- ANCESTRY ATTRIBUTE MAXIMUMS --- -pub const TOTAL_ATTRIBUTE_POINTS_MAXIMUM: i32 = 75; -pub const HUMAN_MAX_ATTR: [i32; 6] = [19, 19, 19, 19, 19, 19]; -pub const ELF_MAX_ATTR: [i32; 6] = [15, 18, 15, 20, 20, 18]; -pub const DWARF_MAX_ATTR: [i32; 6] = [19, 17, 20, 16, 16, 16]; -pub const GNOME_MAX_ATTR: [i32; 6] = [16, 18, 16, 20, 18, 18]; -pub const CATFOLK_MAX_ATTR: [i32; 6] = [16, 20, 16, 16, 18, 20]; -pub const UNKNOWN_MAX_ATTR: [i32; 6] = [18, 18, 18, 18, 18, 18]; -// --- CLASS MIN ATTRIBUTES --- -pub const FIGHTER_MIN_ATTR: (i32, i32, i32, i32, i32, i32) = (10, 8, 10, 6, 6, 8); -pub const ROGUE_MIN_ATTR: (i32, i32, i32, i32, i32, i32) = (8, 10, 8, 6, 8, 10); -pub const WIZARD_MIN_ATTR: (i32, i32, i32, i32, i32, i32) = (6, 8, 6, 10, 10, 8); -pub const VILLAGER_MIN_ATTR: (i32, i32, i32, i32, i32, i32) = (6, 6, 6, 6, 6, 6); -// --- CLASS ATTRIBUTE IMPROVE CHANCES --- -pub const FIGHTER_IMPR_CHANCE: [i32; 6] = [30, 20, 30, 6, 7, 7]; -pub const ROGUE_IMPR_CHANCE: [i32; 6] = [18, 30, 20, 9, 8, 15]; -pub const WIZARD_IMPR_CHANCE: [i32; 6] = [10, 15, 20, 30, 15, 10]; -pub const VILLAGER_IMPR_CHANCE: [i32; 6] = [15, 15, 25, 15, 15, 15]; -// --- CLASS STARTING ITEMS --- ## If any of these are changed, update ancestry infotext in src/gui/character_creation.rs. -pub const FIGHTER_STARTING_FOOD: &str = "1d2+1"; -pub const FIGHTER_STARTING_WEAPON: &str = "equip_shortsword"; -pub const FIGHTER_STARTING_ARMOUR: &str = "equip_body_ringmail"; -pub const FIGHTER_STARTING_SHIELD: &str = "equip_mediumshield"; -pub const ROGUE_STARTING_FOOD: &str = "1d2+2"; -pub const ROGUE_STARTING_WEAPON: &str = "equip_rapier"; -pub const ROGUE_STARTING_ARMOUR: &str = "equip_body_weakleather"; -pub const WIZARD_STARTING_FOOD: &str = "1d2+1"; -pub const WIZARD_STARTING_WEAPON: &str = "equip_dagger"; -pub const WIZARD_STARTING_ARMOUR: &str = "equip_back_protection"; -pub const WIZARD_MAX_SCROLL_LVL: i32 = 3; -pub const WIZARD_SCROLL_AMOUNT: &str = "1d3+1"; -pub const WIZARD_POTION_AMOUNT: &str = "1d3"; -pub const VILLAGER_STARTING_FOOD: &str = "1d3+2"; diff --git a/src/data/entity.rs b/src/data/entity.rs deleted file mode 100644 index a34c526..0000000 --- a/src/data/entity.rs +++ /dev/null @@ -1,16 +0,0 @@ -pub const DEFAULT_VIEWSHED_STANDARD: i32 = 16; // Standard viewshed radius for almost all entities. -pub const CARRY_CAPACITY_PER_STRENGTH: i32 = 5; // How much weight can be carried per point of strength. -pub const NORMAL_SPEED: i32 = 12; // Normal speed for almost all entities. -pub const SPEED_MOD_BURDENED: f32 = 0.75; -pub const SPEED_MOD_STRAINED: f32 = 0.5; -pub const SPEED_MOD_OVERLOADED: f32 = 0.25; -pub const SPEED_MOD_OVERMAP_TRAVEL: f32 = 0.33; -pub const TURN_COST_MULTIPLIER: i32 = 4; // How many ticks for NORMAL_SPEED to get a turn. -pub const ATTR_BONUS_0: i32 = 10; // At this value, the attribute bonus is 0. -pub const ATTR_NEEDED_PER_POINT: i32 = 2; // How many points +- ATTR_BONUS_0 are needed per +- 1 bonus. -pub const STANDARD_HIT_DIE: i32 = 8; // Standard hit die used for rolling HP. -pub const STANDARD_HIT_DIE_0: i32 = 4; // Standard hit die used for rolling HP for level 0. -pub const STANDARD_MANA_DIE: i32 = 4; // Standard mana die used for rolling mana. -pub const MINIMUM_MANA: i32 = 0; // The minimum mana a monster can have. -pub const MINIMUM_MANA_PLAYER: i32 = 1; // The minimum mana a player can have. -pub const STANDARD_BAC: i32 = 10; // Standard BASE AC. diff --git a/src/data/events.rs b/src/data/events.rs deleted file mode 100644 index 74fa459..0000000 --- a/src/data/events.rs +++ /dev/null @@ -1,27 +0,0 @@ -use serde::{ Deserialize, Serialize }; - -#[derive(Serialize, Deserialize, Clone)] -pub enum EVENT { - Turn(i32), - Level(i32), - ChangedFloor(String), - PlayerConfused(i32), - KickedSomething(i32), - BrokeDoor(i32), - LookedForHelp(i32), - Killed(String), - PlayerDied(String), - Discovered(String), - Identified(String), -} - -impl EVENT { - pub const COUNT_TURN: &str = "turns"; - pub const COUNT_KILLED: &str = "killed"; - pub const COUNT_LEVEL: &str = "level"; - pub const COUNT_CHANGED_FLOOR: &str = "changed_floor"; - pub const COUNT_BROKE_DOOR: &str = "BrokeDoor"; - pub const COUNT_PLAYER_CONFUSED: &str = "PlayerConfused"; - pub const COUNT_KICK: &str = "kick"; - pub const COUNT_LOOKED_FOR_HELP: &str = "LookedForHelp"; -} diff --git a/src/data/ids.rs b/src/data/ids.rs deleted file mode 100644 index 864a022..0000000 --- a/src/data/ids.rs +++ /dev/null @@ -1,34 +0,0 @@ -use super::names::*; -use super::visuals::*; -use bracket_lib::prelude::*; - -pub const ID_OVERMAP: i32 = 1; - -pub const ID_TOWN: i32 = 10; -pub const ID_TOWN2: i32 = ID_TOWN + 1; -pub const ID_TOWN3: i32 = ID_TOWN + 2; - -pub const ID_INFINITE: i32 = 1000; - -pub fn get_local_desc(id: i32) -> String { - let str = match id { - ID_TOWN => NAME_STARTER_TOWN, - ID_INFINITE => NAME_DUNGEON_RANDOM, - _ => "an unnamed overmap tile", - }; - return str.to_string(); -} - -pub fn get_local_col(id: i32) -> RGB { - let col = match id { - ID_TOWN => TO_TOWN_COLOUR, - ID_TOWN2 => GRASS_COLOUR, - ID_OVERMAP => TO_OVERMAP_COLOUR, - _ => (255, 255, 255), - }; - return RGB::from_u8(col.0, col.1, col.2); -} - -pub fn rgb_to_u8(col: RGB) -> (u8, u8, u8) { - return ((col.r * 255.0) as u8, (col.g * 255.0) as u8, (col.b * 255.0) as u8); -} diff --git a/src/data/messages.rs b/src/data/messages.rs deleted file mode 100644 index 7175b2a..0000000 --- a/src/data/messages.rs +++ /dev/null @@ -1,52 +0,0 @@ -pub const NOCHARGES_WREST: &str = "You wrest one last charge from the worn-out wand."; -pub const NOCHARGES_DIDNOTHING: &str = "The wand does nothing."; - -pub const IDENTIFY_ALL: &str = "You feel attuned to your belongings!"; -pub const IDENTIFY_ALL_BLESSED: &str = "Divine favour reveals all"; - -pub const REMOVECURSE: &str = "You feel a weight lifted!"; -pub const REMOVECURSE_BLESSED: &str = "You feel righteous"; -pub const REMOVECURSE_BLESSED_FAILED: &str = "You feel righteous! But nothing happened."; - -pub const DAMAGE_PLAYER_HIT: &str = "are hit!"; -pub const DAMAGE_ITEM_HIT: &str = "is ruined!"; -pub const DAMAGE_OTHER_HIT: &str = "is hit!"; - -pub const HEAL_PLAYER_HIT: &str = "recover some vigour."; -pub const HEAL_PLAYER_HIT_BLESSED: &str = "You feel great"; -pub const HEAL_OTHER_HIT: &str = "is rejuvenated!"; - -pub const MAGICMAP: &str = "You recall your surroundings!"; -pub const MAGICMAP_CURSED: &str = "... but forget where you last were"; - -pub const NUTRITION: &str = "You eat the"; -pub const NUTRITION_CURSED: &str = "Blech! Rotten"; -pub const NUTRITION_BLESSED: &str = "Delicious"; - -pub const LEVELUP_PLAYER: &str = "Welcome to experience level"; -pub const YOU_PICKUP_ITEM: &str = "You pick up the"; -pub const NO_MORE_KEYS: &str = "Your backpack cannot accomodate any more items"; -pub const YOU_DROP_ITEM: &str = "You drop the"; -pub const YOU_EQUIP_ITEM: &str = "You equip the"; -pub const YOU_REMOVE_ITEM: &str = "You unequip your"; -pub const YOU_REMOVE_ITEM_CURSED: &str = "You can't remove the"; - -/// Prefixes death message. -pub const PLAYER_DIED: &str = "You died!"; -/// Death message specifiers. Appended after PlayerDied. -pub const PLAYER_DIED_SUICIDE: &str = "You killed yourself"; -pub const PLAYER_DIED_NAMED_ATTACKER: &str = "You were killed by"; -pub const PLAYER_DIED_UNKNOWN: &str = "You were killed"; // Ultimately, this should never be used. Slowly include specific messages for any death. -/// Death message addendums. Appended at end of death message. -pub const PLAYER_DIED_ADDENDUM_FIRST: &str = " "; -pub const PLAYER_DIED_ADDENDUM_MID: &str = ", "; -pub const PLAYER_DIED_ADDENDUM_LAST: &str = ", and "; -pub const STATUS_CONFUSED_STRING: &str = "confused"; -pub const STATUS_BLIND_STRING: &str = "blinded"; -// Results in something like: "You died! You were killed by a kobold captain, whilst confused." - -// Dungeon features -pub const FEATURE_TREANTS: &str = "You feel an unusual freshness in the air."; -pub const FEATURE_BARRACKS_GOBLIN: &str = "You hear an order being barked, and ignored."; -pub const FEATURE_BARRACKS_KOBOLD: &str = "You hear someone being reprimanded for disobedience."; -pub const FEATURE_BARRACKS_ORC: &str = "You hear someone barking orders."; diff --git a/src/data/mod.rs b/src/data/mod.rs deleted file mode 100644 index 8abda34..0000000 --- a/src/data/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub mod entity; -pub mod visuals; -pub mod messages; -pub mod char_create; -pub mod events; -pub mod ids; -pub mod names; diff --git a/src/data/names.rs b/src/data/names.rs deleted file mode 100644 index f9690b8..0000000 --- a/src/data/names.rs +++ /dev/null @@ -1,8 +0,0 @@ -pub const NAME_OVERMAP: &str = "the travel map"; -pub const SHORTNAME_OVERMAP: &str = "Travel"; -pub const NAME_DUNGEON_RANDOM: &str = "the dungeon"; -pub const SHORTNAME_DUNGEON_RANDOM: &str = "D"; -pub const NAME_STARTER_TOWN: &str = "the port town of Saff"; -pub const SHORTNAME_STARTER_TOWN: &str = "Saff"; -pub const NAME_FOREST_BUILDER: &str = "the woods outside of town"; -pub const SHORTNAME_FOREST_BUILDER: &str = "Woods"; diff --git a/src/data/visuals.rs b/src/data/visuals.rs deleted file mode 100644 index 8f59a24..0000000 --- a/src/data/visuals.rs +++ /dev/null @@ -1,92 +0,0 @@ -use bracket_lib::prelude::*; - -// POST-PROCESSING -pub const WITH_DARKEN_BY_DISTANCE: bool = true; // If further away tiles should get darkened, instead of a harsh transition to non-visible. - -pub const BRIGHTEN_FG_COLOUR_BY: i32 = 16; -pub const GLOBAL_OFFSET_MIN_CLAMP: f32 = -0.5; -pub const GLOBAL_OFFSET_MAX_CLAMP: f32 = 1.0; -pub const WITH_SCANLINES_BRIGHTEN_AMOUNT: f32 = 0.1; // 0.0 = no brightening, 1.0 = full brightening. -pub const NON_VISIBLE_MULTIPLIER: f32 = 0.3; // 0.0 = black, 1.0 = full colour. -pub const NON_VISIBLE_MULTIPLIER_IF_SCANLINES: f32 = 0.8; // as above, but when using scanlines. should be higher. -pub const MAX_DARKENING: f32 = 0.45; // 0.0 = black, 1.0 = full colour - only used if WITH_DARKEN_BY_DISTANCE is true. -pub const MAX_DARKENING_IF_SCANLINES: f32 = 0.9; // as above, but when using scanlines. should be higher. -pub const START_DARKEN_AT_N_TILES: f32 = 8.0; // start darkening at this distance (should always be less than entity::DEFAULT_VIEWSHED_STANDARD). - -pub const SHORT_PARTICLE_LIFETIME: f32 = 100.0; // in ms -pub const DEFAULT_PARTICLE_LIFETIME: f32 = 200.0; -pub const LONG_PARTICLE_LIFETIME: f32 = 300.0; - -pub const TARGETING_CURSOR_COL: (u8, u8, u8) = GOLDENROD; -pub const TARGETING_LINE_COL: (u8, u8, u8) = LIGHTGOLDENROD; -pub const TARGETING_AOE_COL: (u8, u8, u8) = (20, 20, 20); -pub const TARGETING_VALID_COL: (u8, u8, u8) = (10, 10, 10); - -// THEMES -pub const BLOODSTAIN_COLOUR: (u8, u8, u8) = (153, 0, 0); -// DEFAULT THEME -pub const DEFAULT_BG_COLOUR: (u8, u8, u8) = (29, 50, 50); -pub const DEFAULT_BG_OFFSETS: (i32, i32, i32) = (10, 10, 10); -pub const WALL_COLOUR: (u8, u8, u8) = (229, 191, 94); -pub const WALL_OFFSETS: (i32, i32, i32) = (48, 48, 48); -pub const FLOOR_COLOUR: (u8, u8, u8) = (25, 204, 122); -pub const FLOOR_OFFSETS: (i32, i32, i32) = (10, 10, 10); -pub const STAIR_COLOUR: (u8, u8, u8) = (200, 200, 0); -pub const STAIR_OFFSETS: (i32, i32, i32) = (10, 10, 10); -pub const WOOD_FLOOR_COLOUR: (u8, u8, u8) = (41, 30, 20); -pub const WOOD_FLOOR_OFFSETS: (i32, i32, i32) = (10, 10, 10); -pub const FENCE_FG_COLOUR: (u8, u8, u8) = (110, 24, 0); -pub const FENCE_COLOUR: (u8, u8, u8) = (45, 30, 10); -pub const FENCE_OFFSETS: (i32, i32, i32) = (10, 10, 10); -pub const BRIDGE_COLOUR: (u8, u8, u8) = (42, 48, 37); -pub const BRIDGE_OFFSETS: (i32, i32, i32) = (10, 10, 10); -pub const GRAVEL_COLOUR: (u8, u8, u8) = (26, 26, 53); -pub const GRAVEL_OFFSETS: (i32, i32, i32) = (10, 10, 10); -pub const ROAD_COLOUR: (u8, u8, u8) = (8, 38, 40); -pub const ROAD_OFFSETS: (i32, i32, i32) = (10, 10, 10); -pub const GRASS_COLOUR: (u8, u8, u8) = (9, 65, 6); -pub const GRASS_OFFSETS: (i32, i32, i32) = (3, 20, 10); -pub const FOLIAGE_COLOUR: (u8, u8, u8) = (5, 60, 5); -pub const FOLIAGE_OFFSETS: (i32, i32, i32) = (10, 10, 10); -pub const HEAVY_FOLIAGE_COLOUR: (u8, u8, u8) = (5, 60, 5); -pub const HEAVY_FOLIAGE_OFFSETS: (i32, i32, i32) = (10, 10, 10); -pub const SAND_COLOUR: (u8, u8, u8) = (70, 70, 21); -pub const SAND_OFFSETS: (i32, i32, i32) = (10, 10, 10); -pub const SHALLOW_WATER_COLOUR: (u8, u8, u8) = (24, 47, 99); -pub const SHALLOW_WATER_OFFSETS: (i32, i32, i32) = (3, 10, 45); -pub const DEEP_WATER_COLOUR: (u8, u8, u8) = (18, 33, 63); -pub const DEEP_WATER_OFFSETS: (i32, i32, i32) = (5, 10, 32); -pub const BARS_COLOUR: (u8, u8, u8) = (100, 100, 100); -pub const IMPASSABLE_MOUNTAIN_COLOUR: (u8, u8, u8) = (20, 23, 20); -pub const IMPASSABLE_MOUNTAIN_OFFSETS: (i32, i32, i32) = (4, 4, 4); -// FOREST THEME -pub const FOREST_WALL_COLOUR: (u8, u8, u8) = (0, 153, 0); - -// DEFAULT THEME -#[allow(dead_code)] -pub const WALL_GLYPH: char = '#'; -pub const FLOOR_GLYPH: char = '.'; -pub const DOWN_STAIR_GLYPH: char = '>'; -pub const UP_STAIR_GLYPH: char = '<'; -pub const WOOD_FLOOR_GLYPH: char = '.'; -pub const FENCE_GLYPH: char = '='; -pub const BRIDGE_GLYPH: char = '.'; -pub const GRAVEL_GLYPH: char = ';'; -pub const ROAD_GLYPH: char = '.'; -pub const GRASS_GLYPH: char = '"'; -pub const FOLIAGE_GLYPH: char = ':'; -pub const HEAVY_FOLIAGE_GLYPH: char = ';'; -pub const SAND_GLYPH: char = '.'; -pub const SHALLOW_WATER_GLYPH: char = '~'; -pub const DEEP_WATER_GLYPH: char = '≈'; -pub const BARS_GLYPH: char = '#'; -pub const IMPASSABLE_MOUNTAIN_GLYPH: char = '▲'; - -// FOREST THEME -pub const FOREST_WALL_GLYPH: char = '♣'; - -// Overmap/transition stuff -pub const TO_OVERMAP_GLYPH: char = '<'; -pub const TO_OVERMAP_COLOUR: (u8, u8, u8) = (205, 127, 50); -pub const TO_TOWN_GLYPH: char = 'o'; -pub const TO_TOWN_COLOUR: (u8, u8, u8) = (205, 127, 50); diff --git a/src/effects/damage.rs b/src/effects/damage.rs deleted file mode 100644 index 80885e2..0000000 --- a/src/effects/damage.rs +++ /dev/null @@ -1,348 +0,0 @@ -use super::{ add_effect, targeting, EffectSpawner, EffectType, Entity, Targets, World }; -use crate::{ - gamelog, - gamesystem::{ hp_per_level, mana_per_level }, - Attributes, - Confusion, - Destructible, - GrantsXP, - Map, - Player, - Pools, - Name, - Blind, - HungerClock, - HungerState, - Bleeds, - HasDamageModifiers, -}; -use crate::gui::with_article; -use crate::data::visuals::{ DEFAULT_PARTICLE_LIFETIME, LONG_PARTICLE_LIFETIME }; -use crate::data::messages::LEVELUP_PLAYER; -use crate::data::events::*; -use crate::data::messages::*; -use bracket_lib::prelude::*; -use specs::prelude::*; - -pub fn inflict_damage(ecs: &mut World, damage: &EffectSpawner, target: Entity) { - let mut pools = ecs.write_storage::(); - if let Some(target_pool) = pools.get_mut(target) { - if !target_pool.god { - if let EffectType::Damage { amount, damage_type } = damage.effect_type { - let mult = if - let Some(modifiers) = ecs.read_storage::().get(target) - { - modifiers.modifier(&damage_type).multiplier() - } else { - 1.0 - }; - target_pool.hit_points.current -= ((amount as f32) * mult) as i32; - let bleeders = ecs.read_storage::(); - if let Some(bleeds) = bleeders.get(target) { - add_effect( - None, - EffectType::Bloodstain { colour: bleeds.colour }, - Targets::Entity { target } - ); - } - add_effect( - None, - EffectType::Particle { - glyph: to_cp437('‼'), - fg: RGB::named(ORANGE), - bg: RGB::named(BLACK), - lifespan: DEFAULT_PARTICLE_LIFETIME, - delay: 0.0, - }, - Targets::Entity { target } - ); - if target_pool.hit_points.current < 1 { - super::DEAD_ENTITIES.lock().unwrap().push_back(target); - add_effect(damage.source, EffectType::EntityDeath, Targets::Entity { target }); - } - } - } - } else if let Some(_destructible) = ecs.read_storage::().get(target) { - add_effect(damage.source, EffectType::EntityDeath, Targets::Entity { target }); - } -} - -pub fn heal_damage(ecs: &mut World, heal: &EffectSpawner, target: Entity) { - let mut pools = ecs.write_storage::(); - if let Some(pool) = pools.get_mut(target) { - if let EffectType::Healing { amount, increment_max } = &heal.effect_type { - let before = pool.hit_points.current; - pool.hit_points.current = i32::min( - pool.hit_points.max, - pool.hit_points.current + amount - ); - if pool.hit_points.current - before < *amount && *increment_max { - // If the heal was not fully effective, and healing source was noncursed, increase max HP by 1. - pool.hit_points.max += 1; - pool.hit_points.current += 1; - } - add_effect( - None, - EffectType::Particle { - glyph: to_cp437('♥'), - fg: RGB::named(BLUE), - bg: RGB::named(BLACK), - lifespan: DEFAULT_PARTICLE_LIFETIME, - delay: 0.0, - }, - Targets::Entity { target } - ); - } - } -} - -pub fn add_confusion(ecs: &mut World, effect: &EffectSpawner, target: Entity) { - if let EffectType::Confusion { turns } = &effect.effect_type { - ecs.write_storage::() - .insert(target, Confusion { turns: *turns }) - .expect("Unable to insert Confusion"); - } -} - -pub fn bloodstain(ecs: &mut World, target: usize, colour: RGB) { - let mut map = ecs.fetch_mut::(); - // If the current tile isn't bloody, bloody it. - if !map.bloodstains.contains_key(&target) { - map.bloodstains.insert(target, colour); - return; - } - if map.bloodstains.get(&target).unwrap() == &colour { - let mut spread: i32 = target as i32; - let mut attempts: i32 = 0; - // Otherwise, roll to move one tile in any direction. - // If this tile isn't bloody, bloody it. If not, loop. - loop { - let mut rng = ecs.write_resource::(); - attempts += 1; - spread = match rng.roll_dice(1, 8) { - 1 => spread + 1, - 2 => spread - 1, - 3 => spread + 1 + map.width, - 4 => spread - 1 + map.width, - 5 => spread + 1 - map.width, - 6 => spread - 1 - map.width, - 7 => spread + map.width, - _ => spread - map.width, - }; - // - If we're in bounds and the tile is unbloodied, bloody it and return. - // - If we ever leave bounds, return. - // - Roll a dice on each failed attempt, with an increasing change to return (soft-capping max spread) - if spread > 0 && spread < map.height * map.width { - if !map.bloodstains.contains_key(&(spread as usize)) { - map.bloodstains.insert(spread as usize, colour); - return; - } - // If bloodied with the same colour, return - if map.bloodstains.get(&(spread as usize)).unwrap() == &colour { - if rng.roll_dice(1, 10 - attempts) == 1 { - return; - } - // If bloodied but a *different* colour, lerp this blood and current blood. - } else { - let new_col = map.bloodstains - .get(&(spread as usize)) - .unwrap() - .lerp(colour, 0.5); - map.bloodstains.insert(spread as usize, new_col); - } - } else { - return; - } - } - } else { - let curr_blood = map.bloodstains.get(&target).unwrap(); - let new_colour = curr_blood.lerp(colour, 0.5); - map.bloodstains.insert(target, new_colour); - return; - } -} - -/// Takes a level, and returns the total XP required to reach the next level. -fn get_next_level_requirement(level: i32) -> i32 { - if level == 0 { - return 5; - } else if level < 10 { - return 20 * (2_i32).pow((level as u32) - 1); - } else if level < 20 { - return 10000 * (2_i32).pow((level as u32) - 10); - } else if level < 30 { - return 10000000 * (level - 19); - } - return -1; -} - -fn get_death_message(ecs: &World, source: Entity) -> String { - let player = ecs.fetch::(); - let mut result: String = format!("{} ", PLAYER_DIED); - // If we killed ourselves, - if source == *player { - result.push_str(format!("{}", PLAYER_DIED_SUICIDE).as_str()); - } else if let Some(name) = ecs.read_storage::().get(source) { - result.push_str( - format!("{} {}", PLAYER_DIED_NAMED_ATTACKER, with_article(name.name.clone())).as_str() - ); - } else { - result.push_str(format!("{}", PLAYER_DIED_UNKNOWN).as_str()); - } - // Status effects - { - let mut addendums: Vec<&str> = Vec::new(); - if let Some(_confused) = ecs.read_storage::().get(*player) { - addendums.push(STATUS_CONFUSED_STRING); - } - if let Some(_blind) = ecs.read_storage::().get(*player) { - addendums.push(STATUS_BLIND_STRING); - } - if !addendums.is_empty() { - result.push_str(" whilst"); - for (i, addendum) in addendums.iter().enumerate() { - if i == 0 { - result.push_str(format!("{}{}", PLAYER_DIED_ADDENDUM_FIRST, addendum).as_str()); - } else if i == addendums.len() { - result.push_str(format!("{}{}", PLAYER_DIED_ADDENDUM_LAST, addendum).as_str()); - } else { - result.push_str(format!("{}{}", PLAYER_DIED_ADDENDUM_MID, addendum).as_str()); - } - } - } - } - return result; -} - -/// Handles EntityDeath effects. -pub fn entity_death(ecs: &mut World, effect: &EffectSpawner, target: Entity) { - let mut xp_gain = 0; - let mut pools = ecs.write_storage::(); - let attributes = ecs.read_storage::(); - let names = ecs.read_storage::(); - let player = ecs.fetch::(); - // If the target has a position, remove it from the SpatialMap. - if let Some(pos) = targeting::entity_position(ecs, target) { - crate::spatial::remove_entity(target, pos as usize); - } - // If the target was killed by a source, cont. - if let Some(source) = effect.source { - // If the target was the player, game over, and record source of death. - if target == *player { - gamelog::record_event(EVENT::PlayerDied(get_death_message(ecs, source))); - return; - } else { - // If the player was the source, record the kill. - if let Some(tar_name) = names.get(target) { - gamelog::record_event(EVENT::Killed(tar_name.name.clone())); - } - } - // Calc XP value of target. - if let Some(xp_value) = ecs.read_storage::().get(target) { - xp_gain += xp_value.amount; - } - // If there was XP, run through XP-gain and level-up. - if xp_gain != 0 { - if let None = pools.get(source) { - return; - } - let mut source_pools = pools.get_mut(source).unwrap(); - let source_attributes = attributes.get(source).unwrap(); - source_pools.xp += xp_gain; - let next_level_requirement = get_next_level_requirement(source_pools.level); - if next_level_requirement != -1 && source_pools.xp >= next_level_requirement { - source_pools.level += 1; - // If it was the PLAYER that levelled up: - if ecs.read_storage::().get(source).is_some() { - gamelog::record_event(EVENT::Level(1)); - gamelog::Logger - ::new() - .append(LEVELUP_PLAYER) - .append_n(source_pools.level) - .append("!") - .log(); - let player_pos = ecs.fetch::(); - let map = ecs.fetch_mut::(); - for i in 0..5 { - if player_pos.y - i > 1 { - add_effect( - None, - EffectType::Particle { - glyph: to_cp437('░'), - fg: RGB::named(GOLD), - bg: RGB::named(BLACK), - lifespan: LONG_PARTICLE_LIFETIME, - delay: (i as f32) * 100.0, - }, - Targets::Tile { target: map.xy_idx(player_pos.x, player_pos.y - i) } - ); - if i > 2 { - add_effect( - None, - EffectType::Particle { - glyph: to_cp437('░'), - fg: RGB::named(GOLD), - bg: RGB::named(BLACK), - lifespan: LONG_PARTICLE_LIFETIME, - delay: (i as f32) * 100.0, - }, - Targets::Tile { - target: map.xy_idx( - player_pos.x + (i - 2), - player_pos.y - i - ), - } - ); - add_effect( - None, - EffectType::Particle { - glyph: to_cp437('░'), - fg: RGB::named(GOLD), - bg: RGB::named(BLACK), - lifespan: LONG_PARTICLE_LIFETIME, - delay: (i as f32) * 100.0, - }, - Targets::Tile { - target: map.xy_idx( - player_pos.x - (i - 2), - player_pos.y - i - ), - } - ); - } - } - } - } else { - console::log("DEBUGINFO: Something other than the player levelled up."); - // TODO: Growing up, NPC-specific level-up cases. - } - let mut rng = ecs.write_resource::(); - let hp_gained = hp_per_level( - &mut rng, - source_attributes.constitution.base + source_attributes.constitution.modifiers - ); - let mana_gained = mana_per_level( - &mut rng, - source_attributes.intelligence.base + source_attributes.intelligence.modifiers - ); - source_pools.hit_points.max += hp_gained; - source_pools.hit_points.current += hp_gained; - // Roll for MANA gain this level - source_pools.mana.max += mana_gained; - source_pools.mana.current += mana_gained; - } - } - } else { - if target == *player { - if let Some(hc) = ecs.read_storage::().get(target) { - if hc.state == HungerState::Starving { - gamelog::record_event(EVENT::PlayerDied("You starved to death!".to_string())); - } - } else { - gamelog::record_event( - EVENT::PlayerDied("You died from unknown causes!".to_string()) - ); - } - } - } -} diff --git a/src/effects/hunger.rs b/src/effects/hunger.rs deleted file mode 100644 index 2384bb5..0000000 --- a/src/effects/hunger.rs +++ /dev/null @@ -1,11 +0,0 @@ -use super::{ EffectSpawner, EffectType }; -use crate::HungerClock; -use specs::prelude::*; - -pub fn modify_nutrition(ecs: &mut World, effect: &EffectSpawner, target: Entity) { - if let EffectType::ModifyNutrition { amount } = &effect.effect_type { - if let Some(hc) = ecs.write_storage::().get_mut(target) { - hc.duration += amount; - } - } -} diff --git a/src/effects/intrinsics.rs b/src/effects/intrinsics.rs deleted file mode 100644 index 01776e1..0000000 --- a/src/effects/intrinsics.rs +++ /dev/null @@ -1,11 +0,0 @@ -use super::{ EffectSpawner, EffectType }; -use specs::prelude::*; - -pub fn add_intrinsic(ecs: &mut World, effect: &EffectSpawner, target: Entity) { - let intrinsic = if let EffectType::AddIntrinsic { intrinsic } = &effect.effect_type { - intrinsic - } else { - unreachable!("add_intrinsic() called with the wrong EffectType") - }; - add_intr!(ecs, target, *intrinsic); -} diff --git a/src/effects/mod.rs b/src/effects/mod.rs deleted file mode 100644 index 5552f5a..0000000 --- a/src/effects/mod.rs +++ /dev/null @@ -1,190 +0,0 @@ -use super::BUC; -use crate::spatial; -use bracket_lib::prelude::*; -use specs::prelude::*; -use std::collections::VecDeque; -use std::sync::Mutex; -use crate::components::*; - -mod damage; -mod hunger; -mod particles; -mod targeting; -mod triggers; -mod intrinsics; - -pub use targeting::aoe_tiles; - -lazy_static! { - pub static ref EFFECT_QUEUE: Mutex> = Mutex::new(VecDeque::new()); -} - -lazy_static! { - pub static ref DEAD_ENTITIES: Mutex> = Mutex::new(VecDeque::new()); -} - -pub enum EffectType { - Damage { - amount: i32, - damage_type: DamageType, - }, - Healing { - amount: i32, - increment_max: bool, - }, - Confusion { - turns: i32, - }, - Bloodstain { - colour: RGB, - }, - Particle { - glyph: FontCharType, - fg: RGB, - bg: RGB, - lifespan: f32, - delay: f32, - }, - EntityDeath, - ItemUse { - item: Entity, - }, - ModifyNutrition { - amount: i32, - }, - AddIntrinsic { - intrinsic: Intrinsic, - }, - TriggerFire { - trigger: Entity, - }, -} - -#[derive(Clone)] -#[allow(dead_code)] -pub enum Targets { - Entity { - target: Entity, - }, - EntityList { - targets: Vec, - }, - Tile { - target: usize, - }, - TileList { - targets: Vec, - }, -} - -pub struct EffectSpawner { - pub source: Option, - pub effect_type: EffectType, - pub target: Targets, -} - -/// Adds an effect to the effects queue -pub fn add_effect(source: Option, effect_type: EffectType, target: Targets) { - let mut lock = EFFECT_QUEUE.lock().unwrap(); - lock.push_back(EffectSpawner { source, effect_type, target }); -} - -/// Iterates through the effects queue, applying each effect to their target. -pub fn run_effects_queue(ecs: &mut World) { - // First removes any effect in the EFFECT_QUEUE with a dead entity as its source. - loop { - let dead_entity: Option = DEAD_ENTITIES.lock().unwrap().pop_front(); - if let Some(dead_entity) = dead_entity { - EFFECT_QUEUE.lock() - .unwrap() - .retain(|x| x.source != Some(dead_entity)); - } else { - break; - } - } - // Then runs every effect that remains in the queue. - loop { - let effect: Option = EFFECT_QUEUE.lock().unwrap().pop_front(); - if let Some(effect) = effect { - target_applicator(ecs, &effect); - } else { - break; - } - } -} - -/// Applies an effect to the correct target(s). -fn target_applicator(ecs: &mut World, effect: &EffectSpawner) { - // Item use is handled differently - it creates other effects with itself - // as the source, passing all effects attached to the item into the queue. - if let EffectType::ItemUse { item } = effect.effect_type { - triggers::item_trigger(effect.source, item, &effect.target, ecs); - return; - } else if let EffectType::TriggerFire { trigger } = effect.effect_type { - triggers::trigger(effect.source, trigger, &effect.target, ecs); - return; - } - // Otherwise, just match the effect and enact it directly. - match &effect.target { - Targets::Tile { target } => affect_tile(ecs, effect, *target), - Targets::TileList { targets } => - targets.iter().for_each(|target| affect_tile(ecs, effect, *target)), - Targets::Entity { target } => affect_entity(ecs, effect, *target), - Targets::EntityList { targets } => - targets.iter().for_each(|target| affect_entity(ecs, effect, *target)), - } -} - -/// Runs an effect on a given tile index -fn affect_tile(ecs: &mut World, effect: &EffectSpawner, target: usize) { - if tile_effect_hits_entities(&effect.effect_type) { - spatial::for_each_tile_content(target, |entity| { - affect_entity(ecs, effect, entity); - }); - } - - match &effect.effect_type { - EffectType::Particle { .. } => particles::particle_to_tile(ecs, target as i32, &effect), - _ => {} - } - // Run the effect -} - -/// Checks if a given effect affects entities or not. -fn tile_effect_hits_entities(effect: &EffectType) -> bool { - match effect { - EffectType::Damage { .. } => true, - EffectType::Healing { .. } => true, - EffectType::ModifyNutrition { .. } => true, - EffectType::Confusion { .. } => true, - EffectType::AddIntrinsic { .. } => true, - _ => false, - } -} - -/// Runs an effect on a given entity -fn affect_entity(ecs: &mut World, effect: &EffectSpawner, target: Entity) { - match &effect.effect_type { - EffectType::Damage { .. } => damage::inflict_damage(ecs, effect, target), - EffectType::Healing { .. } => damage::heal_damage(ecs, effect, target), - EffectType::Confusion { .. } => damage::add_confusion(ecs, effect, target), - EffectType::Bloodstain { colour } => { - if let Some(pos) = targeting::entity_position(ecs, target) { - damage::bloodstain(ecs, pos, *colour); - } - } - EffectType::Particle { .. } => { - if let Some(pos) = targeting::entity_position(ecs, target) { - particles::particle_to_tile(ecs, pos as i32, &effect) - } - } - EffectType::EntityDeath => damage::entity_death(ecs, effect, target), - EffectType::ModifyNutrition { .. } => hunger::modify_nutrition(ecs, effect, target), - EffectType::AddIntrinsic { .. } => intrinsics::add_intrinsic(ecs, effect, target), - _ => {} - } -} - -pub fn get_noncursed(buc: &BUC) -> bool { - if buc == &BUC::Cursed { false } else { true } -} diff --git a/src/effects/particles.rs b/src/effects/particles.rs deleted file mode 100644 index a6170dc..0000000 --- a/src/effects/particles.rs +++ /dev/null @@ -1,187 +0,0 @@ -use super::{ add_effect, targeting, EffectSpawner, EffectType, Targets }; -use crate::{ Map, ParticleBuilder, SpawnParticleBurst, SpawnParticleLine, SpawnParticleSimple }; -use bracket_lib::prelude::*; -use specs::prelude::*; - -pub fn particle_to_tile(ecs: &mut World, target: i32, effect: &EffectSpawner) { - if let EffectType::Particle { glyph, fg, bg, lifespan, delay } = effect.effect_type { - let map = ecs.fetch::(); - let mut particle_builder = ecs.fetch_mut::(); - if delay <= 0.0 { - particle_builder.request( - target % map.width, - target / map.width, - fg, - bg, - glyph, - lifespan - ); - } else { - particle_builder.delay( - target % map.width, - target / map.width, - fg, - bg, - glyph, - lifespan, - delay - ); - } - } -} - -pub fn handle_simple_particles(ecs: &World, entity: Entity, target: &Targets) { - if let Some(part) = ecs.read_storage::().get(entity) { - add_effect( - None, - EffectType::Particle { - glyph: part.glyph, - fg: part.colour, - bg: RGB::named(BLACK), - lifespan: part.lifetime_ms, - delay: 0.0, - }, - target.clone() - ); - } -} - -pub fn handle_burst_particles(ecs: &World, entity: Entity, target: &Targets) { - if let Some(part) = ecs.read_storage::().get(entity) { - if let Some(start_pos) = targeting::find_item_position(ecs, entity) { - let end_pos: i32 = get_centre(ecs, target); - spawn_line_particles( - ecs, - start_pos, - end_pos, - &(SpawnParticleLine { - glyph: part.head_glyph, - tail_glyph: part.tail_glyph, - colour: part.colour, - trail_colour: part.trail_colour, - lifetime_ms: part.trail_lifetime_ms, // 75.0 is good here. - trail_lifetime_ms: part.trail_lifetime_ms, - }) - ); - let map = ecs.fetch::(); - let line = line2d( - LineAlg::Bresenham, - Point::new(start_pos % map.width, start_pos / map.width), - Point::new(end_pos % map.width, end_pos / map.width) - ); - let burst_delay = (line.len() as f32) * part.trail_lifetime_ms; - for i in 0..10 { - add_effect( - None, - EffectType::Particle { - glyph: part.glyph, - fg: part.colour.lerp(part.lerp, (i as f32) * 0.1), - bg: RGB::named(BLACK), - lifespan: part.lifetime_ms / 10.0, // ~50-80 is good here. - delay: burst_delay + ((i as f32) * part.lifetime_ms) / 10.0, // above + burst_delay - }, - target.clone() - ); - } - } - } -} - -fn get_centre(ecs: &World, target: &Targets) -> i32 { - match target { - Targets::Tile { target } => { - return *target as i32; - } - Targets::TileList { targets } => { - let map = ecs.fetch::(); - let (mut count, mut sum_x, mut sum_y) = (0, 0, 0); - for target in targets { - sum_x += (*target as i32) % map.width; - sum_y += (*target as i32) / map.width; - count += 1; - } - let (mean_x, mean_y) = (sum_x / count, sum_y / count); - let centre = map.xy_idx(mean_x, mean_y); - return centre as i32; - } - Targets::Entity { target } => { - return targeting::entity_position(ecs, *target).unwrap() as i32; - } - Targets::EntityList { targets } => { - let map = ecs.fetch::(); - let (mut count, mut sum_x, mut sum_y) = (0, 0, 0); - for target in targets { - if let Some(pos) = targeting::entity_position(ecs, *target) { - sum_x += (pos as i32) % map.width; - sum_y += (pos as i32) / map.width; - count += 1; - } - } - let (mean_x, mean_y) = (sum_x / count, sum_y / count); - let centre = map.xy_idx(mean_x, mean_y); - return centre as i32; - } - } -} - -pub fn handle_line_particles(ecs: &World, entity: Entity, target: &Targets) { - if let Some(part) = ecs.read_storage::().get(entity) { - if let Some(start_pos) = targeting::find_item_position(ecs, entity) { - match target { - Targets::Tile { target } => - spawn_line_particles(ecs, start_pos, *target as i32, part), - Targets::TileList { targets } => { - targets - .iter() - .for_each(|target| - spawn_line_particles(ecs, start_pos, *target as i32, part) - ) - } - Targets::Entity { target } => { - if let Some(end_pos) = targeting::entity_position(ecs, *target) { - spawn_line_particles(ecs, start_pos, end_pos as i32, part); - } - } - Targets::EntityList { targets } => - targets.iter().for_each(|target| { - if let Some(end_pos) = targeting::entity_position(ecs, *target) { - spawn_line_particles(ecs, start_pos, end_pos as i32, part); - } - }), - } - } - } -} - -fn spawn_line_particles(ecs: &World, start: i32, end: i32, part: &SpawnParticleLine) { - let map = ecs.fetch::(); - let start_pt = Point::new(start % map.width, start / map.width); - let end_pt = Point::new(end % map.width, end / map.width); - let line = line2d(LineAlg::Bresenham, start_pt, end_pt); - for (i, pt) in line.iter().enumerate() { - add_effect( - None, - EffectType::Particle { - glyph: part.glyph, - fg: part.colour, - bg: RGB::named(BLACK), - lifespan: part.lifetime_ms, - delay: (i as f32) * part.lifetime_ms, - }, - Targets::Tile { target: map.xy_idx(pt.x, pt.y) } - ); - if i > 0 { - add_effect( - None, - EffectType::Particle { - glyph: part.tail_glyph, - fg: part.trail_colour, - bg: RGB::named(BLACK), - lifespan: part.trail_lifetime_ms, - delay: (i as f32) * part.lifetime_ms, - }, - Targets::Tile { target: map.xy_idx(line[i - 1].x, line[i - 1].y) } - ); - } - } -} diff --git a/src/effects/targeting.rs b/src/effects/targeting.rs deleted file mode 100644 index 1ac124c..0000000 --- a/src/effects/targeting.rs +++ /dev/null @@ -1,45 +0,0 @@ -use crate::{ Equipped, InBackpack, Map, Position }; -use bracket_lib::prelude::*; -use specs::prelude::*; - -pub fn entity_position(ecs: &World, target: Entity) -> Option { - if let Some(position) = ecs.read_storage::().get(target) { - let map = ecs.fetch::(); - return Some(map.xy_idx(position.x, position.y)); - } - return None; -} - -pub fn aoe_tiles(map: &Map, target: Point, radius: i32) -> Vec { - let mut blast_tiles = field_of_view(target, radius, &*map); - blast_tiles.retain(|p| p.x > 0 && p.x < map.width - 1 && p.y > 0 && p.y < map.height - 1); - let mut result = Vec::new(); - for t in blast_tiles.iter() { - result.push(map.xy_idx(t.x, t.y)); - } - result -} - -pub fn find_item_position(ecs: &World, target: Entity) -> Option { - let positions = ecs.read_storage::(); - let map = ecs.fetch::(); - // Does it have a position? - if let Some(pos) = positions.get(target) { - return Some(map.xy_idx(pos.x, pos.y) as i32); - } - // If not, is it carried? - if let Some(carried) = ecs.read_storage::().get(target) { - if let Some(pos) = positions.get(carried.owner) { - return Some(map.xy_idx(pos.x, pos.y) as i32); - } - } - // Is it equipped? - if let Some(carried) = ecs.read_storage::().get(target) { - if let Some(pos) = positions.get(carried.owner) { - return Some(map.xy_idx(pos.x, pos.y) as i32); - } - } - // Out of luck: give up - console::log("DEBUGINFO: Failed to find item position"); - None -} diff --git a/src/effects/triggers.rs b/src/effects/triggers.rs deleted file mode 100644 index cb4e5d3..0000000 --- a/src/effects/triggers.rs +++ /dev/null @@ -1,461 +0,0 @@ -use super::{ add_effect, particles, spatial, EffectType, Entity, Targets, World }; -use crate::{ - gamelog, - gui::item_colour_ecs, - gui::obfuscate_name_ecs, - gui::renderable_colour, - Beatitude, - Charges, - Confusion, - Consumable, - Destructible, - Equipped, - Hidden, - InBackpack, - InflictsDamage, - Item, - MagicMapper, - MasterDungeonMap, - Name, - ObfuscatedName, - Player, - Prop, - ProvidesHealing, - ProvidesIdentify, - ProvidesNutrition, - ProvidesRemoveCurse, - RandomNumberGenerator, - Renderable, - RunState, - SingleActivation, - BUC, - GrantsSpell, - KnownSpells, - Position, - Viewshed, - WantsToRemoveKey, - WantsToDelete, -}; -use crate::data::messages::*; -use bracket_lib::prelude::*; -use specs::prelude::*; -pub fn item_trigger(source: Option, item: Entity, target: &Targets, ecs: &mut World) { - // Check if the item has charges, etc. - if let Some(has_charges) = ecs.write_storage::().get_mut(item) { - if has_charges.uses <= 0 { - let mut rng = ecs.write_resource::(); - if rng.roll_dice(1, 121) != 1 { - gamelog::Logger::new().append(NOCHARGES_DIDNOTHING).log(); - return; - } - gamelog::Logger::new().colour(YELLOW).append(NOCHARGES_WREST); - ecs.write_storage::() - .insert(item, Consumable {}) - .expect("Could not insert consumable"); - } - has_charges.uses -= 1; - } - // Use the item via the generic system - let did_something = event_trigger(source, item, target, ecs); - // If it's a consumable, delete it - if did_something && ecs.read_storage::().get(item).is_some() { - let mut removekey = ecs.write_storage::(); - removekey.insert(item, WantsToRemoveKey {}).expect("Unable to insert WantsToRemoveKey"); - let mut delete = ecs.write_storage::(); - delete.insert(item, WantsToDelete {}).expect("Unable to insert WantsToDelete"); - } -} - -pub fn trigger(source: Option, trigger: Entity, target: &Targets, ecs: &mut World) { - // Remove hidden from the trigger - ecs.write_storage::().remove(trigger); - // Use the trigger via the generic system - let did_something = event_trigger(source, trigger, target, ecs); - // If it was single-activation, delete it - if did_something && ecs.read_storage::().get(trigger).is_some() { - ecs.entities().delete(trigger).expect("Failed to delete entity with a SingleActivation"); - } -} - -struct EventInfo { - source: Option, - entity: Entity, - target: Targets, - buc: BUC, - log: bool, -} - -// TODO: Currently, items can only be used by the player, and so this system is only built for that. -// It does almost no sanity-checking to make sure the logs only appear if the effect is taking -// place on the player -- once monsters can use an item, their item usage will make logs for -// the player saying they were the one who used the item. This will need refactoring then. -fn event_trigger( - source: Option, - entity: Entity, - target: &Targets, - ecs: &mut World -) -> bool { - let buc = if let Some(beatitude) = ecs.read_storage::().get(entity) { - beatitude.buc.clone() - } else { - BUC::Uncursed - }; - let mut event = EventInfo { source, entity, target: target.clone(), buc, log: false }; - let logger = gamelog::Logger::new(); - - let mut did_something = false; - particles::handle_simple_particles(ecs, entity, target); - particles::handle_burst_particles(ecs, entity, &target); - particles::handle_line_particles(ecs, entity, &target); - let (logger, restored_nutrition) = handle_restore_nutrition(ecs, &mut event, logger); - let (logger, magic_mapped) = handle_magic_mapper(ecs, &mut event, logger); - let (logger, granted_spell) = handle_grant_spell(ecs, &mut event, logger); - let (logger, removed_curse) = handle_remove_curse(ecs, &mut event, logger); - let (logger, identified) = handle_identify(ecs, &mut event, logger); - let (logger, healed) = handle_healing(ecs, &mut event, logger); - let (logger, damaged) = handle_damage(ecs, &mut event, logger); - let (logger, confused) = handle_confusion(ecs, &mut event, logger); - //let (logger, dug) = handle_dig(ecs, &mut event, logger); -- NYI i.e. Wand of Digging - did_something |= - restored_nutrition || - magic_mapped || - granted_spell || - healed || - damaged || - confused || - removed_curse || - identified; - - if event.log { - logger.log(); - } - - return did_something; -} - -fn handle_restore_nutrition( - ecs: &mut World, - event: &mut EventInfo, - mut logger: gamelog::Logger -) -> (gamelog::Logger, bool) { - if ecs.read_storage::().get(event.entity).is_some() { - let amount = match event.buc { - BUC::Blessed => 600, - BUC::Uncursed => 400, - BUC::Cursed => 200, - }; - add_effect(event.source, EffectType::ModifyNutrition { amount }, event.target.clone()); - logger = logger - .append(NUTRITION) - .colour(item_colour_ecs(ecs, event.entity)) - .append_n(obfuscate_name_ecs(ecs, event.entity).0) - .colour(WHITE) - .period() - .buc(event.buc.clone(), Some(NUTRITION_CURSED), Some(NUTRITION_BLESSED)); - event.log = true; - return (logger, true); - } - return (logger, false); -} - -fn handle_magic_mapper( - ecs: &mut World, - event: &mut EventInfo, - mut logger: gamelog::Logger -) -> (gamelog::Logger, bool) { - if ecs.read_storage::().get(event.entity).is_some() { - let mut runstate = ecs.fetch_mut::(); - let cursed = if event.buc == BUC::Cursed { true } else { false }; - *runstate = RunState::MagicMapReveal { row: 0, cursed: cursed }; - logger = logger.append(MAGICMAP).buc(event.buc.clone(), Some(MAGICMAP_CURSED), None); - event.log = true; - return (logger, true); - } - return (logger, false); -} - -fn handle_grant_spell( - ecs: &mut World, - event: &mut EventInfo, - logger: gamelog::Logger -) -> (gamelog::Logger, bool) { - if let Some(_granted_spell) = ecs.read_storage::().get(event.entity) { - if - let Some(_known_spells) = ecs - .write_storage::() - .get_mut(event.source.unwrap()) - { - // TODO: Check if the player knows *this* spell, and add it if not. - } else { - // TODO: Grant the KnownSpells component, and then add the spell. - } - } - return (logger, false); -} - -fn handle_healing( - ecs: &mut World, - event: &mut EventInfo, - mut logger: gamelog::Logger -) -> (gamelog::Logger, bool) { - if let Some(healing_item) = ecs.read_storage::().get(event.entity) { - let mut rng = ecs.write_resource::(); - let buc_mod = match event.buc { - BUC::Blessed => 2, - BUC::Cursed => -1, - _ => 0, - }; - let roll = - rng.roll_dice(healing_item.n_dice + buc_mod, healing_item.sides) + - healing_item.modifier; - add_effect( - event.source, - EffectType::Healing { amount: roll, increment_max: event.buc.noncursed() }, - event.target.clone() - ); - for target in get_entity_targets(&event.target) { - if - ecs.read_storage::().get(target).is_some() || - ecs.read_storage::().get(target).is_some() - { - continue; - } - let renderables = ecs.read_storage::(); - if ecs.read_storage::().get(target).is_some() { - logger = logger - .append("You") - .append(HEAL_PLAYER_HIT) - .buc(event.buc.clone(), None, Some(HEAL_PLAYER_HIT_BLESSED)); - } else { - logger = logger - .append("The") - .colour(renderable_colour(&renderables, target)) - .append(obfuscate_name_ecs(ecs, target).0) - .colour(WHITE) - .append(HEAL_OTHER_HIT); - } - event.log = true; - } - return (logger, true); - } - return (logger, false); -} - -fn handle_damage( - ecs: &mut World, - event: &mut EventInfo, - mut logger: gamelog::Logger -) -> (gamelog::Logger, bool) { - if let Some(damage_item) = ecs.read_storage::().get(event.entity) { - let mut rng = ecs.write_resource::(); - let roll = rng.roll_dice(damage_item.n_dice, damage_item.sides) + damage_item.modifier; - add_effect( - event.source, - EffectType::Damage { amount: roll, damage_type: damage_item.damage_type }, - event.target.clone() - ); - for target in get_entity_targets(&event.target) { - if ecs.read_storage::().get(target).is_some() { - continue; - } - let renderables = ecs.read_storage::(); - let positions = ecs.read_storage::(); - let target_pos = positions.get(target).unwrap_or(&(Position { x: 0, y: 0 })); - let viewsheds = ecs.read_storage::(); - let player_viewshed = viewsheds.get(*ecs.fetch::()).unwrap(); - if ecs.read_storage::().get(target).is_some() { - logger = logger - .append("You") - .append(DAMAGE_PLAYER_HIT); - event.log = true; - } else if - player_viewshed.visible_tiles.contains(&Point::new(target_pos.x, target_pos.y)) - { - if ecs.read_storage::().get(target).is_some() { - if ecs.read_storage::().get(target).is_some() { - logger = logger - .append("The") - .colour(renderable_colour(&renderables, target)) - .append(obfuscate_name_ecs(ecs, target).0) - .colour(WHITE) - .append(DAMAGE_ITEM_HIT); - } - } else { - logger = logger - .append("The") - .colour(renderable_colour(&renderables, target)) - .append(obfuscate_name_ecs(ecs, target).0) - .colour(WHITE) - .append(DAMAGE_OTHER_HIT); - } - event.log = true; - } - } - return (logger, true); - } - return (logger, false); -} - -#[allow(unused_mut)] -fn handle_confusion( - ecs: &mut World, - event: &mut EventInfo, - mut logger: gamelog::Logger -) -> (gamelog::Logger, bool) { - if let Some(confusion) = ecs.read_storage::().get(event.entity) { - add_effect( - event.source, - EffectType::Confusion { turns: confusion.turns }, - event.target.clone() - ); - return (logger, true); - } - return (logger, false); -} - -fn select_single(ecs: &World, runstate: RunState) { - let mut new_runstate = ecs.fetch_mut::(); - *new_runstate = runstate; -} - -fn handle_identify( - ecs: &mut World, - event: &mut EventInfo, - mut logger: gamelog::Logger -) -> (gamelog::Logger, bool) { - if let Some(_i) = ecs.read_storage::().get(event.entity) { - let mut rng = ecs.write_resource::(); - let mut dm = ecs.fetch_mut::(); - let identify_all = match event.buc { - BUC::Blessed => rng.roll_dice(1, 5) == 1, - BUC::Uncursed => rng.roll_dice(1, 25) == 1, - _ => false, - }; - if !identify_all { - select_single(ecs, RunState::ShowIdentify); - return (logger, true); - } - let mut to_identify: Vec<(Entity, String)> = Vec::new(); - let mut beatitudes = ecs.write_storage::(); - let obfuscated = ecs.read_storage::(); - for (e, _i, _bp, name) in ( - &ecs.entities(), - &ecs.read_storage::(), - &ecs.read_storage::(), - &ecs.read_storage::(), - ) - .join() - .filter(|(e, _i, bp, name)| { - let in_this_backpack = bp.owner == event.source.unwrap(); - let has_obfuscated_name = obfuscated.get(*e).is_some(); - let already_identified = dm.identified_items.contains(&name.name.clone()); - let known_beatitude = beatitudes - .get(event.source.unwrap()) - .map(|b| b.known) - .unwrap_or(true); - let result = - in_this_backpack && - (has_obfuscated_name || !already_identified || !known_beatitude); - return result; - }) { - to_identify.push((e, name.name.clone())); - } - for item in to_identify { - dm.identified_items.insert(item.1); - if let Some(beatitude) = beatitudes.get_mut(item.0) { - beatitude.known = true; - } - } - logger = logger - .append(IDENTIFY_ALL) - .buc(event.buc.clone(), None, Some(IDENTIFY_ALL_BLESSED)); - event.log = true; - return (logger, true); - } - return (logger, false); -} - -fn handle_remove_curse( - ecs: &mut World, - event: &mut EventInfo, - mut logger: gamelog::Logger -) -> (gamelog::Logger, bool) { - if let Some(_r) = ecs.read_storage::().get(event.entity) { - let mut to_decurse: Vec = Vec::new(); - match event.buc { - // If cursed, show the prompt to select one item. - BUC::Cursed => { - select_single(ecs, RunState::ShowRemoveCurse); - return (logger, true); - } - // If blessed, decurse everything in our backpack. - BUC::Blessed => { - for (entity, _i, _bp, _b) in ( - &ecs.entities(), - &ecs.read_storage::(), - &ecs.read_storage::(), - &ecs.read_storage::(), - ) - .join() - .filter( - |(_e, _i, bp, b)| bp.owner == event.source.unwrap() && b.buc == BUC::Cursed - ) { - to_decurse.push(entity); - } - } - _ => {} - } - // If noncursed, decurse everything we have equipped. - for (entity, _i, _e, _b) in ( - &ecs.entities(), - &ecs.read_storage::(), - &ecs.read_storage::(), - &ecs.read_storage::(), - ) - .join() - .filter(|(_e, _i, e, b)| e.owner == event.source.unwrap() && b.buc == BUC::Cursed) { - to_decurse.push(entity); - } - if to_decurse.len() == 0 { - match event.buc { - BUC::Uncursed => select_single(ecs, RunState::ShowRemoveCurse), - BUC::Blessed => { - logger = logger.append(REMOVECURSE_BLESSED_FAILED); - } - _ => {} - } - return (logger, true); - } - let mut beatitudes = ecs.write_storage::(); - for e in to_decurse { - beatitudes - .insert(e, Beatitude { buc: BUC::Uncursed, known: true }) - .expect("Unable to insert beatitude"); - } - logger = logger.append(REMOVECURSE).buc(event.buc.clone(), None, Some(REMOVECURSE_BLESSED)); - event.log = true; - return (logger, true); - } - return (logger, false); -} - -fn get_entity_targets(target: &Targets) -> Vec { - let mut entities: Vec = Vec::new(); - match target { - Targets::Entity { target } => entities.push(*target), - Targets::EntityList { targets } => targets.iter().for_each(|target| entities.push(*target)), - Targets::Tile { target } => { - spatial::for_each_tile_content(*target, |entity| { - entities.push(entity); - }); - } - Targets::TileList { targets } => { - targets.iter().for_each(|target| { - spatial::for_each_tile_content(*target, |entity| { - entities.push(entity); - }); - }); - } - } - return entities; -} diff --git a/src/gamelog/builder.rs b/src/gamelog/builder.rs index 50c9375..c80f545 100644 --- a/src/gamelog/builder.rs +++ b/src/gamelog/builder.rs @@ -1,6 +1,5 @@ -use super::{ append_entry, LogFragment }; -use crate::BUC; -use bracket_lib::prelude::*; +use super::{append_entry, LogFragment}; +use rltk::prelude::*; pub struct Logger { current_colour: RGB, @@ -10,7 +9,7 @@ pub struct Logger { impl Logger { /// Creates a blank builder for making message log entries. pub fn new() -> Self { - Logger { current_colour: RGB::named(WHITE), fragments: Vec::new() } + Logger { current_colour: RGB::named(rltk::WHITE), fragments: Vec::new() } } /// Sets the colour of the current message logger. @@ -40,31 +39,50 @@ impl Logger { return self; } - pub fn buc(mut self, buc: BUC, cursed: Option, blessed: Option) -> Self { - if buc == BUC::Cursed && cursed.is_some() { - self.fragments.push(LogFragment { - colour: RGB::named(SALMON), - text: cursed.unwrap().to_string(), - }); - self.fragments.push(LogFragment { - colour: self.current_colour, - text: ". ".to_string(), - }); - } else if buc == BUC::Blessed && blessed.is_some() { - self.fragments.push(LogFragment { - colour: RGB::named(CYAN), - text: blessed.unwrap().to_string(), - }); - self.fragments.push(LogFragment { - colour: self.current_colour, - text: ". ".to_string(), - }); - } - return self; - } - /// Pushes the finished log entry. pub fn log(self) { return append_entry(self.fragments); } + + /// Appends text in YELLOW to the current message logger. + pub fn npc_name(mut self, text: T) -> Self { + let mut text_with_space = text.to_string(); + text_with_space.push_str(" "); + self.fragments.push(LogFragment { colour: RGB::named(rltk::YELLOW), text: text_with_space }); + return self; + } + + /// Appends text in YELLOW to the current message logger, with no space. + pub fn npc_name_n(mut self, text: T) -> Self { + self.fragments.push(LogFragment { colour: RGB::named(rltk::YELLOW), text: text.to_string() }); + return self; + } + + /// Appends text in CYAN to the current message logger. + pub fn item_name(mut self, text: T) -> Self { + let mut text_with_space = text.to_string(); + text_with_space.push_str(" "); + self.fragments.push(LogFragment { colour: RGB::named(rltk::CYAN), text: text_with_space }); + return self; + } + + /// Appends text in CYAN to the current message logger, with no space. + pub fn item_name_n(mut self, text: T) -> Self { + self.fragments.push(LogFragment { colour: RGB::named(rltk::CYAN), text: text.to_string() }); + return self; + } + + /// Appends text in RED to the current message logger. + #[allow(dead_code)] + pub fn damage(mut self, damage: i32) -> Self { + self.fragments.push(LogFragment { colour: RGB::named(rltk::RED), text: format!("{} ", damage).to_string() }); + return self; + } + + /// Appends text in RED to the current message logger, with no space. + #[allow(dead_code)] + pub fn damage_n(mut self, damage: i32) -> Self { + self.fragments.push(LogFragment { colour: RGB::named(rltk::RED), text: format!("{}", damage).to_string() }); + return self; + } } diff --git a/src/gamelog/events.rs b/src/gamelog/events.rs index 3e0006f..7781fa1 100644 --- a/src/gamelog/events.rs +++ b/src/gamelog/events.rs @@ -1,56 +1,18 @@ -use std::collections::{ HashSet, HashMap }; +use std::collections::HashMap; use std::sync::Mutex; -use crate::data::events::EVENT; -use crate::data::names::*; lazy_static! { - /// A count of each event that has happened over the run. i.e. "turns", "descended", "ascended" - pub static ref EVENT_COUNTER: Mutex> = Mutex::new(HashMap::new()); - // A record of events that happened on a given turn. i.e. "Advanced to level 2". - pub static ref EVENTS: Mutex>> = Mutex::new(HashMap::new()); - // A record of floors visited, and monsters killed. Used to determine if an event is significant. - static ref VISITED: Mutex> = Mutex::new({ - let mut set = HashSet::new(); - set.insert(NAME_OVERMAP.to_string()); - set - }); - static ref KILLED: Mutex> = Mutex::new(HashSet::new()); + static ref EVENTS: Mutex> = Mutex::new(HashMap::new()); } -/// Makes a copy of event counts (FOR SERIALIZATION) -pub fn clone_event_counts() -> HashMap { - EVENT_COUNTER.lock().unwrap().clone() -} -/// Makes a copy of events (FOR SERIALIZATION) -pub fn clone_events() -> HashMap> { - EVENTS.lock().unwrap().clone() -} -/// Fetches event counter into mutex (FOR DESERIALIZATION) -pub fn restore_event_counter(events: HashMap) { - EVENT_COUNTER.lock().unwrap().clear(); - events.iter().for_each(|(k, v)| { - EVENT_COUNTER.lock().unwrap().insert(k.to_string(), *v); - }); -} -/// Fetches events into mutex (FOR DESERIALIZATION) -pub fn restore_events(events: HashMap>) { - EVENTS.lock().unwrap().clear(); - events.iter().for_each(|(k, v)| { - EVENTS.lock().unwrap().insert(*k, v.to_vec()); - }); -} -/// Wipes all events - for starting a new game. pub fn clear_events() { - let (mut events, mut event_counts) = (EVENTS.lock().unwrap(), EVENT_COUNTER.lock().unwrap()); - events.clear(); - event_counts.clear(); + EVENTS.lock().unwrap().clear(); } #[allow(unused_mut)] -/// Increments the event counter by n for a given event. -fn modify_event_count(event: T, n: i32) { +pub fn record_event(event: T, n: i32) { let event_name = event.to_string(); - let mut events_lock = EVENT_COUNTER.lock(); + let mut events_lock = EVENTS.lock(); let mut events = events_lock.as_mut().unwrap(); if let Some(e) = events.get_mut(&event_name) { *e += n; @@ -58,10 +20,10 @@ fn modify_event_count(event: T, n: i32) { events.insert(event_name, n); } } -/// Returns how many times an event has taken place. + pub fn get_event_count(event: T) -> i32 { let event_name = event.to_string(); - let events_lock = EVENT_COUNTER.lock(); + let events_lock = EVENTS.lock(); let events = events_lock.unwrap(); if let Some(e) = events.get(&event_name) { *e @@ -69,77 +31,14 @@ pub fn get_event_count(event: T) -> i32 { 0 } } -/// Records an event on the current turn. -pub fn record_event(event: EVENT) { - let mut new_event: String = "unknown event".to_string(); - let mut significant_event = true; - match event { - EVENT::Turn(n) => { - modify_event_count(EVENT::COUNT_TURN, n); - significant_event = false; - } - // If de-levelling is ever implemented, this needs refactoring (along with a lot of stuff). - EVENT::Level(n) => { - modify_event_count(EVENT::COUNT_LEVEL, n); - let new_lvl = get_event_count(EVENT::COUNT_LEVEL); - if new_lvl == 1 { - new_event = format!("You embarked on your first adventure!"); - } else { - new_event = format!("Advanced to level {}", new_lvl); - } - } - EVENT::ChangedFloor(n) => { - modify_event_count(EVENT::COUNT_CHANGED_FLOOR, 1); - if VISITED.lock().unwrap().contains(&n) { - significant_event = false; - } else { - VISITED.lock().unwrap().insert(n.clone()); - new_event = format!("Visited {} for the first time", n); - } - } - EVENT::KickedSomething(n) => { - modify_event_count(EVENT::COUNT_KICK, n); - significant_event = false; - } - EVENT::BrokeDoor(n) => { - modify_event_count(EVENT::COUNT_BROKE_DOOR, n); - significant_event = false; - } - EVENT::PlayerConfused(n) => { - modify_event_count(EVENT::COUNT_PLAYER_CONFUSED, n); - significant_event = false; - } - EVENT::LookedForHelp(n) => { - modify_event_count(EVENT::COUNT_LOOKED_FOR_HELP, n); - significant_event = false; - } - EVENT::Killed(name) => { - modify_event_count(EVENT::COUNT_KILLED, 1); - if KILLED.lock().unwrap().contains(&name) { - significant_event = false; - } else { - KILLED.lock().unwrap().insert(name.clone()); - new_event = format!("Killed your first {}", name); - } - } - EVENT::Discovered(name) => { - new_event = format!("Discovered {}", name); - } - EVENT::Identified(name) => { - new_event = format!("Identified {}", crate::gui::with_article(name)); - } - EVENT::PlayerDied(str) => { - // Generating the String is handled in the death effect, to avoid passing the ecs here. - new_event = format!("{}", str); - } - } - if significant_event { - EVENTS.lock() - .as_mut() - .unwrap() - .entry(get_event_count(EVENT::COUNT_TURN) as u32) - .or_insert_with(Vec::new) - .push(new_event); - } +pub fn clone_events() -> HashMap { + EVENTS.lock().unwrap().clone() +} + +pub fn load_events(events: HashMap) { + EVENTS.lock().unwrap().clear(); + events.iter().for_each(|(k, v)| { + EVENTS.lock().unwrap().insert(k.to_string(), *v); + }); } diff --git a/src/gamelog/logstore.rs b/src/gamelog/logstore.rs index edea95b..0627aa5 100644 --- a/src/gamelog/logstore.rs +++ b/src/gamelog/logstore.rs @@ -1,5 +1,5 @@ -use super::{ events, LogFragment, Logger }; -use bracket_lib::prelude::*; +use super::{events, LogFragment, Logger}; +use rltk::prelude::*; use std::sync::Mutex; lazy_static! { @@ -19,71 +19,59 @@ pub fn clear_log() { LOG.lock().unwrap().clear(); } -pub fn print_log( - console: &mut Box, - pos: Point, - _descending: bool, - len: usize, - maximum_len: i32 -) { +pub fn print_log(console: &mut Box, pos: Point, _descending: bool, len: usize, maximum_len: i32) { + // Start at x, y let mut y = pos.y; let mut x = pos.x; // Reverse the log, take the number we want to show, and iterate through them - LOG.lock() - .unwrap() - .iter() - .rev() - .take(len) - .for_each(|log| { - let mut entry_len = -2; - // Iterate through each message fragment, and get the total length - // in lines, by adding the length of every fragment and dividing it - // by the maximum length we desire. Then shuffle our start-y by that much. - log.iter().for_each(|frag| { - entry_len += frag.text.len() as i32; - }); - let lines = entry_len / maximum_len; - y -= lines; - let mut i = 0; - log.iter().for_each(|frag| { - // Split every fragment up into single characters. - let parts = frag.text.split(""); - for part in parts { - // This is an extremely hacky solution to a problem I don't understand yet. - // -- without this, the lines *here* and the line count *above* wont match. - if part == "" || part == "\\" { - continue; - } - if i > entry_len { - break; - } - i += 1; - if x + (part.len() as i32) > pos.x + maximum_len { - if y > pos.y - (len as i32) { - console.print(x, y, "-"); - } - y += 1; - x = pos.x; - } - // Stay within bounds - if y > pos.y - (len as i32) { - console.print_color( - x, - y, - frag.colour.into(), - RGB::named(BLACK).into(), - part - ); - } - x += part.len() as i32; - } - }); - // Take away one from the y-axis, because we want to start each entry - // on a new line, and go up an additional amount depending on how many - // lines our *previous* entry took. - y -= 1 + lines; - x = pos.x; + LOG.lock().unwrap().iter().rev().take(len).for_each(|log| { + let mut len_so_far: i32 = 0; + let mut entry_len = 0; + // Iterate through each message fragment, and get the total length + // in lines, by adding the length of every fragment and dividing it + // by the maximum length we desire. + log.iter().for_each(|frag| { + entry_len += frag.text.len() as i32; }); + let lines = entry_len / maximum_len; + // If the fragment is more than one line long, move our y-value up + // by this much. + y -= lines; + // Iterate through each fragment now, for the draw loop + log.iter().for_each(|frag| { + // Split every fragment up into single characters + let parts = frag.text.split(""); + // For every character, check if the length will exceed + // the maximum length we're looking for. If it will, go + // down 1 in the y-axis, return us to the start of the line, + // and reset our length counter to 0. + for part in parts { + if len_so_far + part.len() as i32 > maximum_len { + y += 1; + x = pos.x; + len_so_far = 0; + } + // If we're still within our "range" (we haven't gone up + // further in the y-axis than our desired amount), then + // print the next character. Otherwise, just skip it. + // -- this makes sure we don't continue drawing outside of + // the bounds of our message box. + if y > pos.y - len as i32 { + console.print_color(x, y, frag.colour.into(), RGB::named(rltk::BLACK).into(), part); + } + // Move across by 1 in the x-axis, and add the length to our counter. + x += part.len() as i32; + len_so_far += part.len() as i32; + } + }); + // Descending is deprecated for now, so we always ascending upwards. + // Take away one from the y-axis, because we want to start each entry + // on a new line, and go up an additional amount depending on how many + // lines our *previous* entry took. + y -= 1 + lines; + // Go back to the start of the new line. + x = pos.x; + }); } pub fn setup_log() { @@ -92,7 +80,7 @@ pub fn setup_log() { Logger::new() .append("Welcome!") - .colour(CYAN) + .colour(rltk::CYAN) .append_n("Press [?] at any time to view controls") .period() .log(); diff --git a/src/gamelog/mod.rs b/src/gamelog/mod.rs index 2289c9f..e83d6c3 100644 --- a/src/gamelog/mod.rs +++ b/src/gamelog/mod.rs @@ -1,13 +1,14 @@ +use rltk::prelude::*; + mod builder; pub use builder::*; mod logstore; use logstore::*; -pub use logstore::{ clear_log, clone_log, print_log, restore_log, setup_log }; +pub use logstore::{clear_log, clone_log, print_log, restore_log, setup_log}; mod events; pub use events::*; -use serde::{ Deserialize, Serialize }; -use bracket_lib::prelude::*; +use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Clone)] pub struct LogFragment { pub colour: RGB, diff --git a/src/gamesystem.rs b/src/gamesystem.rs index 6644723..afd5e06 100644 --- a/src/gamesystem.rs +++ b/src/gamesystem.rs @@ -1,58 +1,45 @@ -use super::{ Skill, Skills }; -use crate::gui::{ Ancestry, Class }; -use crate::data::entity; -use crate::data::char_create::*; -use bracket_lib::prelude::*; -use std::cmp::max; +use super::{Skill, Skills}; -/// Returns the attribute bonus for a given attribute score, where every 2 points above -/// or below 10 is an additional +1 or -1. pub fn attr_bonus(value: i32) -> i32 { - return (value - entity::ATTR_BONUS_0) / entity::ATTR_NEEDED_PER_POINT; + return (value - 10) / 2; } -/// Returns the number of HP gained per level for a given constitution score. -pub fn hp_per_level(rng: &mut RandomNumberGenerator, constitution: i32) -> i32 { - return max(rng.roll_dice(1, entity::STANDARD_HIT_DIE) + attr_bonus(constitution), 1); +pub fn player_hp_per_level(rng: &mut rltk::RandomNumberGenerator, constitution: i32) -> i32 { + return rng.roll_dice(1, 8) + attr_bonus(constitution); } #[allow(dead_code)] -/// Returns a total HP roll for a player, based on a given constitution score and level. -pub fn player_hp_at_level(rng: &mut RandomNumberGenerator, constitution: i32, level: i32) -> i32 { - let mut total = entity::STANDARD_HIT_DIE + attr_bonus(constitution); +pub fn player_hp_at_level(rng: &mut rltk::RandomNumberGenerator, constitution: i32, level: i32) -> i32 { + let mut total = 10 + attr_bonus(constitution); for _i in 0..level { - total += hp_per_level(rng, constitution); + total += player_hp_per_level(rng, constitution); } return total; } -/// Returns a total HP roll for an NPC, based on a given constitution score and level. -pub fn npc_hp_at_level(rng: &mut RandomNumberGenerator, constitution: i32, level: i32) -> i32 { +pub fn npc_hp(rng: &mut rltk::RandomNumberGenerator, constitution: i32, level: i32) -> i32 { if level == 0 { - return rng.roll_dice(1, entity::STANDARD_HIT_DIE_0); + return rng.roll_dice(1, 4); } let mut total = 1; for _i in 0..level { - total += rng.roll_dice(1, entity::STANDARD_HIT_DIE) + attr_bonus(constitution); + total += rng.roll_dice(1, 8) + attr_bonus(constitution); } return total; } -/// Returns the number of mana gained per level for a given intelligence score. -pub fn mana_per_level(rng: &mut RandomNumberGenerator, intelligence: i32) -> i32 { - return max(rng.roll_dice(1, entity::STANDARD_MANA_DIE) + attr_bonus(intelligence), 1); +pub fn mana_per_level(rng: &mut rltk::RandomNumberGenerator, intelligence: i32) -> i32 { + return rng.roll_dice(1, 4) + attr_bonus(intelligence); } -/// Returns the number of mana gained per level for a given intelligence score. -pub fn mana_at_level(rng: &mut RandomNumberGenerator, intelligence: i32, level: i32) -> i32 { - let mut total = entity::MINIMUM_MANA; +pub fn mana_at_level(rng: &mut rltk::RandomNumberGenerator, intelligence: i32, level: i32) -> i32 { + let mut total = 0; for _i in 0..level { total += mana_per_level(rng, intelligence); } return total; } -/// Returns the skill bonus for a given skill, or -4 if the skill is not present. pub fn skill_bonus(skill: Skill, skills: &Skills) -> i32 { if skills.skills.contains_key(&skill) { return skills.skills[&skill]; @@ -62,8 +49,7 @@ pub fn skill_bonus(skill: Skill, skills: &Skills) -> i32 { } /// Roll 4d6 and drop the lowest, for rolling d20-style stats -#[allow(unused)] -pub fn roll_4d6(rng: &mut RandomNumberGenerator) -> i32 { +pub fn roll_4d6(rng: &mut rltk::RandomNumberGenerator) -> i32 { let mut rolls: Vec = Vec::new(); for _i in 0..4 { rolls.push(rng.roll_dice(1, 6)); @@ -77,96 +63,3 @@ pub fn roll_4d6(rng: &mut RandomNumberGenerator) -> i32 { return roll; } - -/// Handles stat distribution for a player character. -pub fn get_attribute_rolls( - rng: &mut RandomNumberGenerator, - class: Class, - ancestry: Ancestry -) -> (i32, i32, i32, i32, i32, i32) { - let (mut str, mut dex, mut con, mut int, mut wis, mut cha) = match class { - Class::Fighter => FIGHTER_MIN_ATTR, - Class::Rogue => ROGUE_MIN_ATTR, - Class::Wizard => WIZARD_MIN_ATTR, - Class::Villager => VILLAGER_MIN_ATTR, - }; - let mut remaining_points = TOTAL_ATTRIBUTE_POINTS_MAXIMUM - (str + dex + con + int + wis + cha); - let improve_chance: [i32; 6] = match class { - Class::Fighter => FIGHTER_IMPR_CHANCE, - Class::Rogue => ROGUE_IMPR_CHANCE, - Class::Wizard => WIZARD_IMPR_CHANCE, - Class::Villager => VILLAGER_IMPR_CHANCE, - }; - let ancestry_maximums: [i32; 6] = match ancestry { - Ancestry::Human => HUMAN_MAX_ATTR, // 114 - Ancestry::Elf => ELF_MAX_ATTR, // 106 - Ancestry::Dwarf => DWARF_MAX_ATTR, // 106 - Ancestry::Gnome => GNOME_MAX_ATTR, // 106 - Ancestry::Catfolk => CATFOLK_MAX_ATTR, // 106 - _ => UNKNOWN_MAX_ATTR, - }; - let improve_table = crate::random_table::RandomTable - ::new() - .add("Strength", improve_chance[0]) - .add("Dexterity", improve_chance[1]) - .add("Constitution", improve_chance[2]) - .add("Intelligence", improve_chance[3]) - .add("Wisdom", improve_chance[4]) - .add("Charisma", improve_chance[5]); - let mut failed_attempts = 0; - while remaining_points > 0 && failed_attempts < 100 { - let roll = improve_table.roll(rng); - match roll.as_str() { - "Strength" => { - if str < ancestry_maximums[0] { - str += 1; - remaining_points -= 1; - } else { - failed_attempts += 1; - } - } - "Dexterity" => { - if dex < ancestry_maximums[1] { - dex += 1; - remaining_points -= 1; - } else { - failed_attempts += 1; - } - } - "Constitution" => { - if con < ancestry_maximums[2] { - con += 1; - remaining_points -= 1; - } else { - failed_attempts += 1; - } - } - "Intelligence" => { - if int < ancestry_maximums[3] { - int += 1; - remaining_points -= 1; - } else { - failed_attempts += 1; - } - } - "Wisdom" => { - if wis < ancestry_maximums[4] { - wis += 1; - remaining_points -= 1; - } else { - failed_attempts += 1; - } - } - "Charisma" => { - if cha < ancestry_maximums[5] { - cha += 1; - remaining_points -= 1; - } else { - failed_attempts += 1; - } - } - _ => {} - } - } - return (str, dex, con, int, wis, cha); -} diff --git a/src/gui/character_creation.rs b/src/gui/character_creation.rs deleted file mode 100644 index da1ecdb..0000000 --- a/src/gui/character_creation.rs +++ /dev/null @@ -1,461 +0,0 @@ -use super::{ - gamesystem::attr_bonus, - gamesystem::get_attribute_rolls, - gamesystem::mana_at_level, - Attributes, - Pools, - Renderable, - RunState, - State, -}; -use crate::data::entity; -use crate::data::char_create::*; -use crate::{ - raws, - Attribute, - Energy, - HasAncestry, - HasClass, - KnownSpell, - KnownSpells, - Pool, - Skill, - Skills, - Telepath, - BUC, -}; -use bracket_lib::prelude::*; -use serde::{ Deserialize, Serialize }; -use specs::prelude::*; -use std::collections::HashMap; - -#[derive(Serialize, Deserialize, Copy, Clone, PartialEq)] -pub enum Ancestry { - NULL, - Human, - Dwarf, - Gnome, - Elf, - Catfolk, -} - -#[derive(Serialize, Deserialize, Copy, Clone, PartialEq)] -pub enum Class { - Fighter, - Rogue, - Wizard, - Villager, -} - -lazy_static! { - static ref ANCESTRY_CLASS_DATA: HashMap> = { - let mut m = HashMap::new(); - // Ancestry - m.insert( - "human".to_string(), - vec![ - "nothing".to_string()]); - m.insert( - "dwarf".to_string(), - vec![ - "a natural bonus to defence".to_string()]); - m.insert( - "elf".to_string(), - vec![ - "minor telepathy".to_string(), - "a slightly increased speed".to_string()]); - m.insert( - "catfolk".to_string(), - vec![ - "increased speed".to_string(), - "increased unarmed damage".to_string()]); - // Class - m.insert( - "fighter".to_string(), - vec![ - format!("a longsword, ring mail, and {} food", FIGHTER_STARTING_FOOD), - "10 str, 8 dex, 10 con, 6 int, 6 wis, 8 cha".to_string(), - "and 27 random stat points".to_string()]); - m.insert( - "rogue".to_string(), - vec![ - format!("a rapier, leather armour, and {} food", ROGUE_STARTING_FOOD), - "8 str, 10 dex, 8 con, 6 int, 8 wis, 10 cha".to_string(), - "and 35 random stat points".to_string()]); - m.insert( - "wizard".to_string(), - vec![ - format!("a dagger, random scrolls/potions, and {} food", WIZARD_STARTING_FOOD), - "6 str, 8 dex, 6 con, 10 int, 10 wis, 8 cha".to_string(), - "and 17 random stat points".to_string()]); - m.insert( - "villager".to_string(), - vec![ - format!("the first weapon you could find, and {} food", VILLAGER_STARTING_FOOD), - "6 str, 6 dex, 6 con, 6 int, 6 wis, 6 cha".to_string(), - "and 39 random stat points".to_string()]); - return m; - }; -} - -#[derive(PartialEq, Copy, Clone)] -pub enum CharCreateResult { - NoSelection { - ancestry: Ancestry, - class: Class, - }, - Selected { - ancestry: Ancestry, - class: Class, - }, -} - -/// Handles the player character creation screen. -pub fn character_creation(gs: &mut State, ctx: &mut BTerm) -> CharCreateResult { - let runstate = gs.ecs.fetch::(); - - let mut x = 2; - let mut y = 11; - let column_width = 20; - - ctx.print_color(x, y, RGB::named(WHITE), RGB::named(BLACK), CHAR_CREATE_HEADER); - y += 2; - - if let RunState::CharacterCreation { ancestry, class } = *runstate { - let selected_fg = RGB::named(GREEN); - let unselected_fg = RGB::named(WHITE); - let mut fg; - let bg = RGB::named(BLACK); - - // Ancestry - ctx.print_color(x, y, bg, unselected_fg, "Ancestry"); - ctx.print_color(x + column_width, y, bg, unselected_fg, "Class"); - y += 1; - let mut race_str = "human"; - if ancestry == Ancestry::Human { - fg = selected_fg; - } else { - fg = unselected_fg; - } - ctx.print_color(x, y, fg, bg, "h. Human"); - if ancestry == Ancestry::Elf { - fg = selected_fg; - race_str = "elf"; - } else { - fg = unselected_fg; - } - ctx.print_color(x, y + 1, fg, bg, "e. Elf"); - if ancestry == Ancestry::Dwarf { - fg = selected_fg; - race_str = "dwarf"; - } else { - fg = unselected_fg; - } - ctx.print_color(x, y + 2, fg, bg, "d. Dwarf"); - if ancestry == Ancestry::Catfolk { - fg = selected_fg; - race_str = "catfolk"; - } else { - fg = unselected_fg; - } - ctx.print_color(x, y + 3, fg, bg, "c. Catfolk"); - // Class - let mut class_str = "fighter"; - x += column_width; - if class == Class::Fighter { - fg = selected_fg; - } else { - fg = unselected_fg; - } - ctx.print_color(x, y, fg, bg, "f. Fighter"); - if class == Class::Rogue { - fg = selected_fg; - class_str = "rogue"; - } else { - fg = unselected_fg; - } - ctx.print_color(x, y + 1, fg, bg, "r. Rogue"); - if class == Class::Wizard { - fg = selected_fg; - class_str = "wizard"; - } else { - fg = unselected_fg; - } - ctx.print_color(x, y + 2, fg, bg, "w. Wizard"); - if class == Class::Villager { - fg = selected_fg; - class_str = "villager"; - } else { - fg = unselected_fg; - } - ctx.print_color(x, y + 3, fg, bg, "v. Villager"); - // Selected ancestry/class benefits - x += column_width; - ctx.print_color(x, y, selected_fg, bg, ANCESTRY_INFO_HEADER); - for line in ANCESTRY_CLASS_DATA.get(race_str).unwrap().iter() { - y += 1; - ctx.print_color(x + 1, y, unselected_fg, bg, line); - } - y += 2; - ctx.print_color(x, y, selected_fg, bg, CLASS_INFO_HEADER); - for line in ANCESTRY_CLASS_DATA.get(class_str).unwrap().iter() { - y += 1; - ctx.print_color(x + 1, y, unselected_fg, bg, line); - } - - match ctx.key { - None => { - return CharCreateResult::NoSelection { ancestry, class }; - } - Some(key) => - match key { - VirtualKeyCode::Escape => { - return CharCreateResult::Selected { ancestry: Ancestry::NULL, class }; - } - VirtualKeyCode::Return => { - return CharCreateResult::Selected { ancestry, class }; - } - VirtualKeyCode::H => { - return CharCreateResult::NoSelection { ancestry: Ancestry::Human, class }; - } - VirtualKeyCode::E => { - return CharCreateResult::NoSelection { ancestry: Ancestry::Elf, class }; - } - VirtualKeyCode::D => { - return CharCreateResult::NoSelection { ancestry: Ancestry::Dwarf, class }; - } - VirtualKeyCode::C => { - return CharCreateResult::NoSelection { ancestry: Ancestry::Catfolk, class }; - } - VirtualKeyCode::F => { - return CharCreateResult::NoSelection { ancestry, class: Class::Fighter }; - } - VirtualKeyCode::R => { - return CharCreateResult::NoSelection { ancestry, class: Class::Rogue }; - } - VirtualKeyCode::W => { - return CharCreateResult::NoSelection { ancestry, class: Class::Wizard }; - } - VirtualKeyCode::V => { - return CharCreateResult::NoSelection { ancestry, class: Class::Villager }; - } - _ => { - return CharCreateResult::NoSelection { ancestry, class }; - } - } - } - } - return CharCreateResult::NoSelection { ancestry: Ancestry::Human, class: Class::Fighter }; -} - -/// Handles player ancestry setup. -pub fn setup_player_ancestry(ecs: &mut World, ancestry: Ancestry) { - let player = ecs.fetch::(); - let mut renderables = ecs.write_storage::(); - // SKILLS - let mut skills = ecs.write_storage::(); - let player_skills = if let Some(skills) = skills.get_mut(*player) { - skills - } else { - skills - .insert(*player, Skills { skills: HashMap::new() }) - .expect("Unable to insert skills component"); - skills.get_mut(*player).unwrap() - }; - let mut ancestries = ecs.write_storage::(); - ancestries.insert(*player, HasAncestry { name: ancestry }).expect("Unable to insert ancestry"); - match ancestry { - Ancestry::Human => {} - Ancestry::Dwarf => { - renderables - .insert(*player, Renderable { - glyph: to_cp437(DWARF_GLYPH), - fg: RGB::named(DWARF_COLOUR), - bg: RGB::named(BLACK), - render_order: 0, - }) - .expect("Unable to insert renderable component"); - *player_skills.skills.entry(Skill::Defence).or_insert(0) += DWARF_DEFENCE_MOD; - } - Ancestry::Elf => { - renderables - .insert(*player, Renderable { - glyph: to_cp437(ELF_GLYPH), - fg: RGB::named(ELF_COLOUR), - bg: RGB::named(BLACK), - render_order: 0, - }) - .expect("Unable to insert renderable component"); - let mut telepaths = ecs.write_storage::(); - telepaths - .insert(*player, Telepath { - telepath_tiles: Vec::new(), - range: ELF_TELEPATH_RANGE, - dirty: true, - }) - .expect("Unable to insert telepath component"); - let mut speeds = ecs.write_storage::(); - speeds - .insert(*player, Energy { - current: 0, - speed: entity::NORMAL_SPEED + ELF_SPEED_BONUS, - }) - .expect("Unable to insert energy component"); - } - Ancestry::Catfolk => { - renderables - .insert(*player, Renderable { - glyph: to_cp437(CATFOLK_GLYPH), - fg: RGB::named(CATFOLK_COLOUR), - bg: RGB::named(BLACK), - render_order: 0, - }) - .expect("Unable to insert renderable component"); - let mut speeds = ecs.write_storage::(); - speeds - .insert(*player, Energy { - current: 0, - speed: entity::NORMAL_SPEED + CATFOLK_SPEED_BONUS, - }) - .expect("Unable to insert energy component"); - } - _ => {} - } -} - -/// Handles player class setup -pub fn setup_player_class(ecs: &mut World, class: Class, ancestry: Ancestry) { - let player = *ecs.fetch::(); - // ATTRIBUTES - { - let mut classes = ecs.write_storage::(); - classes.insert(player, HasClass { name: class }).expect("Unable to insert class component"); - if class == Class::Wizard { - let mut spells = ecs.write_storage::(); - spells - .insert(player, KnownSpells { - list: vec![KnownSpell { display_name: "zap".to_string(), mana_cost: 1 }], - }) - .expect("Unable to insert known spells component"); - } - let mut rng = ecs.write_resource::(); - let mut attributes = ecs.write_storage::(); - let (str, dex, con, int, wis, cha) = get_attribute_rolls(&mut rng, class, ancestry); - attributes - .insert(player, Attributes { - strength: Attribute { base: str, modifiers: 0, bonus: attr_bonus(str) }, - dexterity: Attribute { base: dex, modifiers: 0, bonus: attr_bonus(dex) }, - constitution: Attribute { base: con, modifiers: 0, bonus: attr_bonus(con) }, - intelligence: Attribute { base: int, modifiers: 0, bonus: attr_bonus(int) }, - wisdom: Attribute { base: wis, modifiers: 0, bonus: attr_bonus(wis) }, - charisma: Attribute { base: cha, modifiers: 0, bonus: attr_bonus(cha) }, - }) - .expect("Unable to insert attributes component"); - - let mut pools = ecs.write_storage::(); - let starting_mp = mana_at_level(&mut rng, int, 1); - pools - .insert(player, Pools { - hit_points: Pool { - current: 8 + attr_bonus(con), - max: entity::STANDARD_HIT_DIE + attr_bonus(con), - }, - mana: Pool { - current: starting_mp, - max: starting_mp, - }, - xp: 0, - level: 1, - bac: entity::STANDARD_BAC, - weight: 0.0, - god: false, - }) - .expect("Unable to insert pools component"); - } - - // TODO: use seeded RNG here - let mut rng = RandomNumberGenerator::new(); - let starts_with = get_starting_inventory(class, &mut rng); - for item in starts_with.0.iter() { - let buc = if rng.roll_dice(1, 3) == 1 { Some(BUC::Blessed) } else { Some(BUC::Uncursed) }; - raws::spawn_named_entity( - &raws::RAWS.lock().unwrap(), - ecs, - item, - buc, - raws::SpawnType::Equipped { by: player }, - 0 - ); - } - for item in starts_with.1.iter() { - raws::spawn_named_entity( - &raws::RAWS.lock().unwrap(), - ecs, - item, - None, - raws::SpawnType::Carried { by: player }, - 0 - ); - } -} - -fn get_starting_inventory( - class: Class, - rng: &mut RandomNumberGenerator -) -> (Vec, Vec) { - let mut equipped: Vec = Vec::new(); - let mut carried: Vec = Vec::new(); - let starting_food: &str; - match class { - Class::Fighter => { - starting_food = FIGHTER_STARTING_FOOD; - equipped = vec![ - FIGHTER_STARTING_WEAPON.to_string(), - FIGHTER_STARTING_ARMOUR.to_string(), - FIGHTER_STARTING_SHIELD.to_string() - ]; - } - Class::Rogue => { - starting_food = ROGUE_STARTING_FOOD; - equipped = vec![ROGUE_STARTING_WEAPON.to_string(), ROGUE_STARTING_ARMOUR.to_string()]; - carried = vec!["equip_dagger".to_string(), "equip_dagger".to_string()]; - } - Class::Wizard => { - starting_food = WIZARD_STARTING_FOOD; - equipped = vec![WIZARD_STARTING_WEAPON.to_string(), WIZARD_STARTING_ARMOUR.to_string()]; - pick_random_table_item( - rng, - &mut carried, - "scrolls", - WIZARD_SCROLL_AMOUNT, - Some(WIZARD_MAX_SCROLL_LVL) - ); - pick_random_table_item( - rng, - &mut carried, - "potions", - WIZARD_POTION_AMOUNT, - Some(WIZARD_MAX_SCROLL_LVL) - ); - } - Class::Villager => { - starting_food = VILLAGER_STARTING_FOOD; - pick_random_table_item(rng, &mut equipped, "villager_equipment", "1", None); - } - } - pick_random_table_item(rng, &mut carried, "food", starting_food, None); - return (equipped, carried); -} - -fn pick_random_table_item( - rng: &mut RandomNumberGenerator, - push_to: &mut Vec, - table: &'static str, - dice_str: &'static str, - difficulty: Option -) { - let dice = parse_dice_string(dice_str).expect("Error parsing dice"); - for _i in 0..rng.roll_dice(dice.n_dice, dice.die_type) + dice.bonus { - push_to.push(raws::table_by_name(&raws::RAWS.lock().unwrap(), table, difficulty).roll(rng)); - } -} diff --git a/src/gui/cheat_menu.rs b/src/gui/cheat_menu.rs index eae6f4c..76ece1b 100644 --- a/src/gui/cheat_menu.rs +++ b/src/gui/cheat_menu.rs @@ -1,5 +1,5 @@ use super::State; -use bracket_lib::prelude::*; +use rltk::prelude::*; #[derive(PartialEq, Copy, Clone)] pub enum CheatMenuResult { @@ -12,53 +12,52 @@ pub enum CheatMenuResult { GodMode, } -pub fn show_cheat_menu(_gs: &mut State, ctx: &mut BTerm) -> CheatMenuResult { +pub fn show_cheat_menu(_gs: &mut State, ctx: &mut Rltk) -> CheatMenuResult { let (x_offset, y_offset) = (1, 10); ctx.print_color( 1 + x_offset, 1 + y_offset, - RGB::named(RED), - RGB::named(BLACK), - "DEBUG MENU! [aA-zZ][Esc.]" + RGB::named(rltk::RED), + RGB::named(rltk::BLACK), + "DEBUG MENU! [aA-zZ][Esc.]", ); let x = 1 + x_offset; let mut y = 3 + y_offset; let count = 5; let width = 19; - ctx.draw_box(x, y, width, (count + 1) as i32, RGB::named(RED), RGB::named(BLACK)); + ctx.draw_box(x, y, width, (count + 1) as i32, RGB::named(rltk::RED), RGB::named(rltk::BLACK)); y += 1; // Asc - ctx.set(x_offset + 2, y, RGB::named(YELLOW), RGB::named(BLACK), to_cp437('a')); + ctx.set(x_offset + 2, y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), rltk::to_cp437('a')); ctx.print(x_offset + 4, y, "ASCEND A FLOOR"); y += 1; // Desc - ctx.set(x_offset + 2, y, RGB::named(YELLOW), RGB::named(BLACK), to_cp437('d')); + ctx.set(x_offset + 2, y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), rltk::to_cp437('d')); ctx.print(x_offset + 4, y, "DESCEND A FLOOR"); y += 1; // Heal - ctx.set(x_offset + 2, y, RGB::named(YELLOW), RGB::named(BLACK), to_cp437('h')); + ctx.set(x_offset + 2, y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), rltk::to_cp437('h')); ctx.print(x_offset + 4, y, "HEAL TO FULL"); y += 1; // Reveal map - ctx.set(x_offset + 2, y, RGB::named(YELLOW), RGB::named(BLACK), to_cp437('m')); + ctx.set(x_offset + 2, y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), rltk::to_cp437('m')); ctx.print(x_offset + 4, y, "MAGIC MAP REVEAL"); y += 1; // Godmode - ctx.set(x_offset + 2, y, RGB::named(YELLOW), RGB::named(BLACK), to_cp437('g')); + ctx.set(x_offset + 2, y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), rltk::to_cp437('g')); ctx.print(x_offset + 4, y, "GOD MODE"); // Match keys match ctx.key { None => CheatMenuResult::NoResponse, - Some(key) => - match key { - VirtualKeyCode::A => CheatMenuResult::Ascend, - VirtualKeyCode::D => CheatMenuResult::Descend, - VirtualKeyCode::H => CheatMenuResult::Heal, - VirtualKeyCode::M => CheatMenuResult::MagicMap, - VirtualKeyCode::G => CheatMenuResult::GodMode, - VirtualKeyCode::Escape => CheatMenuResult::Cancel, - _ => CheatMenuResult::NoResponse, - } + Some(key) => match key { + VirtualKeyCode::A => CheatMenuResult::Ascend, + VirtualKeyCode::D => CheatMenuResult::Descend, + VirtualKeyCode::H => CheatMenuResult::Heal, + VirtualKeyCode::M => CheatMenuResult::MagicMap, + VirtualKeyCode::G => CheatMenuResult::GodMode, + VirtualKeyCode::Escape => CheatMenuResult::Cancel, + _ => CheatMenuResult::NoResponse, + }, } } diff --git a/src/gui/farlook.rs b/src/gui/farlook.rs deleted file mode 100644 index c8f6312..0000000 --- a/src/gui/farlook.rs +++ /dev/null @@ -1,54 +0,0 @@ -use super::{ State, RunState, tooltip::draw_tooltips, camera::get_screen_bounds }; -use bracket_lib::prelude::*; - -#[derive(PartialEq, Copy, Clone)] -pub enum FarlookResult { - NoResponse { - x: i32, - y: i32, - }, - Cancel, -} - -pub fn show_farlook(gs: &mut State, ctx: &mut BTerm) -> FarlookResult { - let runstate = gs.ecs.fetch::(); - let (_min_x, _max_x, _min_y, _max_y, x_offset, y_offset) = get_screen_bounds(&gs.ecs, ctx); - - ctx.print_color( - 1 + x_offset, - 1 + y_offset, - RGB::named(WHITE), - RGB::named(BLACK), - "Look at what? [move keys][Esc.]" - ); - - if let RunState::Farlook { x, y } = *runstate { - let (screen_x, screen_y) = (69, 41); - let x = x.clamp(x_offset, x_offset - 1 + (screen_x as i32)); - let y = y.clamp(y_offset, y_offset - 1 + (screen_y as i32)); - - ctx.set(x, y, RGB::named(WHITE), RGB::named(BLACK), to_cp437('X')); - draw_tooltips(&gs.ecs, ctx, Some((x, y))); - - return match ctx.key { - None => FarlookResult::NoResponse { x, y }, - Some(key) => - match key { - VirtualKeyCode::Escape | VirtualKeyCode::X => FarlookResult::Cancel, - VirtualKeyCode::Numpad9 => FarlookResult::NoResponse { x: x + 1, y: y - 1 }, - VirtualKeyCode::Numpad8 => FarlookResult::NoResponse { x, y: y - 1 }, - VirtualKeyCode::Numpad7 => FarlookResult::NoResponse { x: x - 1, y: y - 1 }, - VirtualKeyCode::Numpad6 => FarlookResult::NoResponse { x: x + 1, y }, - VirtualKeyCode::Numpad4 => FarlookResult::NoResponse { x: x - 1, y }, - VirtualKeyCode::Numpad3 => FarlookResult::NoResponse { x: x + 1, y: y + 1 }, - VirtualKeyCode::Numpad2 => FarlookResult::NoResponse { x, y: y + 1 }, - VirtualKeyCode::Numpad1 => FarlookResult::NoResponse { x: x - 1, y: y + 1 }, - _ => FarlookResult::NoResponse { x, y }, - } - }; - } else { - let ppos = gs.ecs.fetch::(); - // TODO: PPOS + offsets (should get these from screen_bounds()) - return FarlookResult::NoResponse { x: ppos.x + x_offset, y: ppos.x + y_offset }; - } -} diff --git a/src/gui/identify_menu.rs b/src/gui/identify_menu.rs deleted file mode 100644 index 31ce8d7..0000000 --- a/src/gui/identify_menu.rs +++ /dev/null @@ -1,145 +0,0 @@ -use super::{ - get_max_inventory_width, - item_colour_ecs, - obfuscate_name_ecs, - print_options, - unique_ecs, - check_key, - letter_to_option, - ItemMenuResult, -}; -use crate::{ - gamelog, - Beatitude, - Entity, - Equipped, - InBackpack, - Item, - MasterDungeonMap, - Name, - ObfuscatedName, - Renderable, - Key, - states::state::*, -}; -use bracket_lib::prelude::*; -use specs::prelude::*; -use std::collections::HashMap; - -/// Handles the Identify menu. -pub fn identify(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option) { - let player_entity = gs.ecs.fetch::(); - let equipped = gs.ecs.read_storage::(); - let backpack = gs.ecs.read_storage::(); - let entities = gs.ecs.entities(); - let items = gs.ecs.read_storage::(); - let obfuscated = gs.ecs.read_storage::(); - let dm = gs.ecs.fetch::(); - let names = gs.ecs.read_storage::(); - let renderables = gs.ecs.read_storage::(); - let beatitudes = gs.ecs.read_storage::(); - let keys = gs.ecs.read_storage::(); - - let build_identify_iterator = || { - (&entities, &items, &renderables, &names, &keys) - .join() - .filter(|(item_entity, _i, _r, n, _k)| { - // If not owned by the player, return false. - let mut keep = false; - if let Some(bp) = backpack.get(*item_entity) { - if bp.owner == *player_entity { - keep = true; - } - } - // If not equipped by the player, return false. - if let Some(equip) = equipped.get(*item_entity) { - if equip.owner == *player_entity { - keep = true; - } - } - if !keep { - return false; - } - // If not obfuscated, or already identified, return false. - if - (!obfuscated.get(*item_entity).is_some() || - dm.identified_items.contains(&n.name)) && - beatitudes - .get(*item_entity) - .map(|beatitude| beatitude.known) - .unwrap_or(true) - { - return false; - } - return true; - }) - }; - - // Build list of items to display - let count = build_identify_iterator().count(); - // If no items, return nothing, wasting the scroll. - if count == 0 { - gamelog::Logger::new().append("You've got nothing to identify! Know-it-all.").log(); - return (ItemMenuResult::Cancel, None); - } - // If only one item, return it. - if count == 1 { - let item = build_identify_iterator().nth(0).unwrap().0; - gamelog::Logger - ::new() - .append("You identify the") - .colour(item_colour_ecs(&gs.ecs, item)) - .append_n(obfuscate_name_ecs(&gs.ecs, item).0) - .colour(WHITE) - .append("!") - .log(); - return (ItemMenuResult::Selected, Some(build_identify_iterator().nth(0).unwrap().0)); - } - let mut player_inventory: super::PlayerInventory = HashMap::new(); - for (entity, _i, _r, _n, key) in build_identify_iterator() { - let unique_item = unique_ecs(&gs.ecs, entity); - player_inventory - .entry(unique_item) - .and_modify(|slot| { - slot.count += 1; - }) - .or_insert(super::InventorySlot { item: entity, count: 1, idx: key.idx }); - } - // Get display args - let width = get_max_inventory_width(&player_inventory); - let (_, _, _, _, x_offset, y_offset) = crate::camera::get_screen_bounds(&gs.ecs, ctx); - let (x, y) = (x_offset + 1, y_offset + 3); - // Draw menu - ctx.print_color( - 1 + x_offset, - 1 + y_offset, - RGB::named(WHITE), - RGB::named(BLACK), - "Identify which item? [aA-zZ][Esc.]" - ); - ctx.draw_box(x, y, width + 2, count + 1, RGB::named(WHITE), RGB::named(BLACK)); - print_options(&gs.ecs, &player_inventory, x + 1, y + 1, ctx); - // Input - match ctx.key { - None => (ItemMenuResult::NoResponse, None), - Some(key) => - match key { - VirtualKeyCode::Escape => (ItemMenuResult::Cancel, None), - _ => { - let selection = letter_to_option::letter_to_option(key, ctx.shift); - if selection != -1 && check_key(selection as usize) { - // Get the first entity with a Key {} component that has an idx matching "selection". - let entities = gs.ecs.entities(); - let keyed_items = gs.ecs.read_storage::(); - let backpack = gs.ecs.read_storage::(); - for (e, key, _b) in (&entities, &keyed_items, &backpack).join() { - if key.idx == (selection as usize) { - return (ItemMenuResult::Selected, Some(e)); - } - } - } - (ItemMenuResult::NoResponse, None) - } - } - } -} diff --git a/src/gui/letter_to_option.rs b/src/gui/letter_to_option.rs index 8b32109..a346bff 100644 --- a/src/gui/letter_to_option.rs +++ b/src/gui/letter_to_option.rs @@ -1,4 +1,4 @@ -use bracket_lib::prelude::*; +use rltk::prelude::*; pub fn letter_to_option(key: VirtualKeyCode, shift: bool) -> i32 { if shift { diff --git a/src/gui/mod.rs b/src/gui/mod.rs index 7604527..be20daf 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -1,103 +1,36 @@ use super::{ - camera, - gamelog, - gamesystem, - hunger_system::get_hunger_colour, - rex_assets::RexAssets, - ArmourClassBonus, - Attributes, - Beatitude, - Burden, - Charges, - Equipped, - Hidden, - HungerClock, - HungerState, - InBackpack, - KnownSpells, - MagicItem, - Map, - MasterDungeonMap, - Name, - ObfuscatedName, - Player, - Point, - Pools, - Position, - Prop, - Renderable, - RunState, - states::state::*, - Skill, - Skills, - Viewshed, - BUC, - Key, - Item, - ItemType, - data::ids::get_local_col, + ai::CARRY_CAPACITY_PER_STRENGTH, camera, gamelog, gamesystem, rex_assets::RexAssets, ArmourClassBonus, Attributes, + Burden, Equipped, Hidden, HungerClock, HungerState, InBackpack, MagicItem, MagicItemClass, Map, MasterDungeonMap, + Name, ObfuscatedName, Player, Point, Pools, Position, Prop, Renderable, RunState, Skill, Skills, State, Viewshed, + Wand, }; -use crate::data::entity::CARRY_CAPACITY_PER_STRENGTH; -use crate::data::visuals::{ - TARGETING_LINE_COL, - TARGETING_CURSOR_COL, - TARGETING_AOE_COL, - TARGETING_VALID_COL, -}; -use bracket_lib::prelude::*; +use rltk::{Rltk, VirtualKeyCode, RGB}; use specs::prelude::*; -use std::collections::HashMap; -use crate::invkeys::check_key; - -mod character_creation; +use std::collections::BTreeMap; mod cheat_menu; mod letter_to_option; -pub use character_creation::*; -mod remove_curse_menu; -pub use remove_curse_menu::*; -mod identify_menu; -pub use identify_menu::*; mod tooltip; pub use cheat_menu::*; -use crate::data::events::*; -mod farlook; -pub use farlook::*; - -/// Gives a popup box with a message and a title, and waits for a keypress. -#[allow(unused)] -pub fn yes_no(ctx: &mut BTerm, question: String) -> Option { - ctx.print_color_centered(15, RGB::named(YELLOW), RGB::named(BLACK), question); - ctx.print_color_centered(17, RGB::named(CYAN), RGB::named(BLACK), "(y)es or (n)o"); - match ctx.key { - None => None, - Some(key) => - match key { - VirtualKeyCode::Y => Some(true), - VirtualKeyCode::N => Some(false), - _ => None, - } - } -} pub fn draw_lerping_bar( - ctx: &mut BTerm, + ctx: &mut Rltk, sx: i32, sy: i32, width: i32, n: i32, max: i32, full_colour: RGB, - empty_colour: RGB + empty_colour: RGB, ) { - let percent = (n as f32) / (max as f32); - let fill_width = (percent * (width as f32)) as i32; + let percent = n as f32 / max as f32; + let fill_width = (percent * width as f32) as i32; let bg = empty_colour.lerp(full_colour, percent); - let fg = RGB::named(BLACK); + let fg = RGB::named(rltk::BLACK); for x in 0..width { if x <= fill_width { ctx.print_color(sx + x, sy, fg, bg, " "); } else { - ctx.print_color(sx + x, sy, RGB::named(BLACK), RGB::named(BLACK), " "); + ctx.print_color(sx + x, sy, RGB::named(rltk::BLACK), RGB::named(rltk::BLACK), " "); } } ctx.print(sx - 1, sy, "["); @@ -106,102 +39,12 @@ pub fn draw_lerping_bar( ctx.print(sx + width, sy, "]"); } -fn draw_xp(ctx: &mut BTerm, pt: Point, pool: &Pools) { - ctx.print_color( - pt.x, - pt.y, - RGB::named(WHITE), - RGB::named(BLACK), - format!("XP{}/{}", pool.level, pool.xp) - ); -} +pub fn draw_ui(ecs: &World, ctx: &mut Rltk) { + ctx.draw_hollow_box(0, 0, 70, 8, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)); // Log box + ctx.draw_hollow_box(0, 9, 70, 42, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)); // Camera box + ctx.draw_hollow_box(0, 52, 70, 3, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)); // Stats box + ctx.draw_hollow_box(71, 0, 33, 55, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)); // Side box -fn calc_ac(ecs: &World, skills: &Skills, stats: &Pools, attr: &Attributes) -> i32 { - let skill_ac_bonus = gamesystem::skill_bonus(Skill::Defence, skills); - let mut armour_ac_bonus = 0; - let equipped = ecs.read_storage::(); - let ac = ecs.read_storage::(); - let player_entity = ecs.fetch::(); - for (wielded, ac) in (&equipped, &ac).join() { - if wielded.owner == *player_entity { - armour_ac_bonus += ac.amount; - } - } - stats.bac - attr.dexterity.bonus / 2 - skill_ac_bonus - armour_ac_bonus -} - -fn draw_ac(ctx: &mut BTerm, pt: Point, ac: i32) { - ctx.print_color(pt.x, pt.y, RGB::named(PINK), RGB::named(BLACK), "AC"); - ctx.print_color(pt.x + 2, pt.y, RGB::named(WHITE), RGB::named(BLACK), ac); -} - -fn draw_attributes(ctx: &mut BTerm, pt: Point, a: &Attributes) { - ctx.print_color(pt.x, pt.y, RGB::named(RED), RGB::named(BLACK), "STR"); - ctx.print_color(pt.x + 3, pt.y, RGB::named(WHITE), RGB::named(BLACK), a.strength.base); - ctx.print_color(pt.x + 7, pt.y, RGB::named(GREEN), RGB::named(BLACK), "DEX"); - ctx.print_color(pt.x + 10, pt.y, RGB::named(WHITE), RGB::named(BLACK), a.dexterity.base); - ctx.print_color(pt.x + 14, pt.y, RGB::named(ORANGE), RGB::named(BLACK), "CON"); - ctx.print_color(pt.x + 17, pt.y, RGB::named(WHITE), RGB::named(BLACK), a.constitution.base); - ctx.print_color(pt.x, 54, RGB::named(CYAN), RGB::named(BLACK), "INT"); - ctx.print_color(pt.x + 3, pt.y + 1, RGB::named(WHITE), RGB::named(BLACK), a.intelligence.base); - ctx.print_color(pt.x + 7, pt.y + 1, RGB::named(YELLOW), RGB::named(BLACK), "WIS"); - ctx.print_color(pt.x + 10, pt.y + 1, RGB::named(WHITE), RGB::named(BLACK), a.wisdom.base); - ctx.print_color(pt.x + 14, pt.y + 1, RGB::named(PURPLE), RGB::named(BLACK), "CHA"); - ctx.print_color(pt.x + 17, pt.y + 1, RGB::named(WHITE), RGB::named(BLACK), a.charisma.base); -} - -fn draw_hunger(ctx: &mut BTerm, pt: Point, hunger: &HungerClock) { - match hunger.state { - HungerState::Satiated => { - ctx.print_color_right( - pt.x, - pt.y, - get_hunger_colour(hunger.state), - RGB::named(BLACK), - "Satiated" - ); - } - HungerState::Normal => {} - HungerState::Hungry => { - ctx.print_color_right( - pt.x, - pt.y, - get_hunger_colour(hunger.state), - RGB::named(BLACK), - "Hungry" - ); - } - HungerState::Weak => { - ctx.print_color_right( - pt.x, - pt.y, - get_hunger_colour(hunger.state), - RGB::named(BLACK), - "Weak" - ); - } - HungerState::Fainting => { - ctx.print_color_right( - pt.x, - pt.y, - get_hunger_colour(hunger.state), - RGB::named(BLACK), - "Fainting" - ); - } - HungerState::Starving => { - ctx.print_color_right( - pt.x, - pt.y, - get_hunger_colour(hunger.state), - RGB::named(BLACK), - "Starving" - ); - } - } -} - -pub fn draw_ui(ecs: &World, ctx: &mut BTerm) { // Render stats let pools = ecs.read_storage::(); let attributes = ecs.read_storage::(); @@ -209,13 +52,7 @@ pub fn draw_ui(ecs: &World, ctx: &mut BTerm) { let hunger = ecs.read_storage::(); let burden = ecs.read_storage::(); let skills = ecs.read_storage::(); - for (_player, stats, attributes, hunger, skills) in ( - &players, - &pools, - &attributes, - &hunger, - &skills, - ).join() { + for (_player, stats, attributes, hunger, skills) in (&players, &pools, &attributes, &hunger, &skills).join() { // Draw hp/mana bars draw_lerping_bar( ctx, @@ -225,7 +62,7 @@ pub fn draw_ui(ecs: &World, ctx: &mut BTerm) { stats.hit_points.current, stats.hit_points.max, RGB::from_u8(0, 255, 0), - RGB::from_u8(255, 0, 0) + RGB::from_u8(255, 0, 0), ); draw_lerping_bar( ctx, @@ -234,97 +71,123 @@ pub fn draw_ui(ecs: &World, ctx: &mut BTerm) { 22, stats.mana.current, stats.mana.max, - RGB::named(BLUE), - RGB::named(BLACK) + RGB::named(rltk::BLUE), + RGB::named(rltk::BLACK), ); - draw_ac(ctx, Point::new(26, 53), calc_ac(ecs, skills, stats, attributes)); - draw_xp(ctx, Point::new(26, 54), stats); - draw_attributes(ctx, Point::new(38, 53), attributes); - draw_hunger(ctx, Point::new(70, 53), hunger); - // Burden + // Draw AC + let skill_ac_bonus = gamesystem::skill_bonus(Skill::Defence, &*skills); + let mut armour_ac_bonus = 0; + let equipped = ecs.read_storage::(); + let ac = ecs.read_storage::(); let player_entity = ecs.fetch::(); + for (wielded, ac) in (&equipped, &ac).join() { + if wielded.owner == *player_entity { + armour_ac_bonus += ac.amount; + } + } + let armour_class = stats.bac - attributes.dexterity.bonus - skill_ac_bonus - armour_ac_bonus; + ctx.print_color(26, 53, RGB::named(rltk::PINK), RGB::named(rltk::BLACK), "AC"); + ctx.print_color(28, 53, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), armour_class); + // Draw level + ctx.print_color( + 26, + 54, + RGB::named(rltk::WHITE), + RGB::named(rltk::BLACK), + format!("XP{}/{}", stats.level, stats.xp), + ); + // Draw attributes + let x = 38; + ctx.print_color(x, 53, RGB::named(rltk::RED), RGB::named(rltk::BLACK), "STR"); + ctx.print_color(x + 3, 53, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), attributes.strength.base); + ctx.print_color(x + 7, 53, RGB::named(rltk::GREEN), RGB::named(rltk::BLACK), "DEX"); + ctx.print_color(x + 10, 53, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), attributes.dexterity.base); + ctx.print_color(x + 14, 53, RGB::named(rltk::ORANGE), RGB::named(rltk::BLACK), "CON"); + ctx.print_color(x + 17, 53, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), attributes.constitution.base); + ctx.print_color(x, 54, RGB::named(rltk::CYAN), RGB::named(rltk::BLACK), "INT"); + ctx.print_color(x + 3, 54, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), attributes.intelligence.base); + ctx.print_color(x + 7, 54, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "WIS"); + ctx.print_color(x + 10, 54, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), attributes.wisdom.base); + ctx.print_color(x + 14, 54, RGB::named(rltk::PURPLE), RGB::named(rltk::BLACK), "CHA"); + ctx.print_color(x + 17, 54, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), attributes.charisma.base); + // Draw hunger + match hunger.state { + HungerState::Satiated => { + ctx.print_color_right(70, 53, RGB::named(rltk::GREEN), RGB::named(rltk::BLACK), "Satiated") + } + HungerState::Normal => {} + HungerState::Hungry => { + ctx.print_color_right(70, 53, RGB::named(rltk::BROWN1), RGB::named(rltk::BLACK), "Hungry") + } + HungerState::Weak => { + ctx.print_color_right(70, 53, RGB::named(rltk::ORANGE), RGB::named(rltk::BLACK), "Weak") + } + HungerState::Fainting => { + ctx.print_color_right(70, 53, RGB::named(rltk::RED), RGB::named(rltk::BLACK), "Fainting") + } + } + // Burden if let Some(burden) = burden.get(*player_entity) { match burden.level { crate::BurdenLevel::Burdened => { - ctx.print_color_right( - 70, - 50, - RGB::named(BROWN1), - RGB::named(BLACK), - "Burdened" - ); + ctx.print_color_right(70, 50, RGB::named(rltk::BROWN1), RGB::named(rltk::BLACK), "Burdened") } crate::BurdenLevel::Strained => { - ctx.print_color_right( - 70, - 50, - RGB::named(ORANGE), - RGB::named(BLACK), - "Strained" - ); + ctx.print_color_right(70, 50, RGB::named(rltk::ORANGE), RGB::named(rltk::BLACK), "Strained") } crate::BurdenLevel::Overloaded => { - ctx.print_color_right(70, 50, RGB::named(RED), RGB::named(BLACK), "Overloaded"); + ctx.print_color_right(70, 50, RGB::named(rltk::RED), RGB::named(rltk::BLACK), "Overloaded") } } } if stats.god { - ctx.print_color(20, 20, RGB::named(YELLOW), RGB::named(BLACK), "--- GODMODE: ON ---"); + ctx.print_color(20, 20, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "--- GODMODE: ON ---"); } // Draw equipment + let renderables = ecs.read_storage::(); + let mut equipment: Vec<(String, RGB, RGB, rltk::FontCharType)> = Vec::new(); + let entities = ecs.entities(); + for (entity, _equipped, renderable) in + (&entities, &equipped, &renderables).join().filter(|item| item.1.owner == *player_entity) + { + equipment.push(( + get_item_display_name(ecs, entity).0, + get_item_colour(ecs, entity), + renderable.fg, + renderable.glyph, + )); + } let mut y = 1; - let equipment = items(&ecs, Filter::Equipped); if !equipment.is_empty() { - ctx.print_color(72, y, RGB::named(BLACK), RGB::named(WHITE), "Equipment"); - y += 1; - y = print_options(&ecs, &equipment, 72, y, ctx); - y += 1; + ctx.print_color(72, y, RGB::named(rltk::BLACK), RGB::named(rltk::WHITE), "Equipment"); + let mut j = 0; + for item in equipment { + y += 1; + ctx.set(72, y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), 97 + j as rltk::FontCharType); + j += 1; + ctx.set(74, y, item.2, RGB::named(rltk::BLACK), item.3); + ctx.print_color(76, y, item.1, RGB::named(rltk::BLACK), &item.0); + ctx.print_color(76 + &item.0.len() + 1, y, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), "(worn)"); + } + y += 2; } - // Draw backpack - ctx.print_color(72, y, RGB::named(BLACK), RGB::named(WHITE), "Backpack"); + // Draw consumables + ctx.print_color(72, y, RGB::named(rltk::BLACK), RGB::named(rltk::WHITE), "Backpack"); ctx.print_color( 81, y, - RGB::named(WHITE), - RGB::named(BLACK), + RGB::named(rltk::WHITE), + RGB::named(rltk::BLACK), &format!( "[{:.1}/{} lbs]", stats.weight, - (attributes.strength.base + attributes.strength.modifiers) * - CARRY_CAPACITY_PER_STRENGTH - ) + (attributes.strength.base + attributes.strength.modifiers) * CARRY_CAPACITY_PER_STRENGTH + ), ); y += 1; - let backpack = items(&ecs, Filter::Backpack); - y = print_options(&ecs, &backpack, 72, y, ctx); - - // Draw spells - if we have any -- NYI! - if let Some(known_spells) = ecs.read_storage::().get(*player_entity) { - y += 1; - // Draw known spells - ctx.print_color(72, y, RGB::named(BLACK), RGB::named(WHITE), "Known Spells"); - y += 1; - let mut index = 1; - for spell in known_spells.list.iter() { - ctx.print_color( - 72, - y, - RGB::named(YELLOW), - RGB::named(BLACK), - &format!("{}", index) - ); - ctx.print_color( - 74, - y, - RGB::named(CYAN), - RGB::named(BLACK), - &format!("{} ({})", "Force Bolt - NYI!", spell.mana_cost) - ); - index += 1; - y += 1; - } - } + let (player_inventory, _inventory_ids) = get_player_inventory(&ecs); + y = print_options(player_inventory, 72, y, ctx).0; // Draw entities seen on screen let viewsheds = ecs.read_storage::(); @@ -337,139 +200,100 @@ pub fn draw_ui(ecs: &World, ctx: &mut BTerm) { let mut seen_entities: Vec<(String, RGB, RGB, u16)> = Vec::new(); for tile in viewshed.visible_tiles.iter() { let idx = map.xy_idx(tile.x, tile.y); - crate::spatial::for_each_tile_content(idx, |entity| { + for entity in map.tile_content[idx].iter() { let mut draw = false; - if let Some(_) = names.get(entity) { + if let Some(_) = names.get(*entity) { draw = true; } - let prop = props.get(entity); + let prop = props.get(*entity); if let Some(_) = prop { draw = false; } - let is_hidden = hidden.get(entity); + let is_hidden = hidden.get(*entity); if let Some(_) = is_hidden { draw = false; } - if entity == *player_entity { + if entity == &*player_entity { draw = false; } if draw { - let (render_fg, glyph) = if let Some(renderable) = renderables.get(entity) { + let (render_fg, glyph) = if let Some(renderable) = renderables.get(*entity) { (renderable.fg, renderable.glyph) } else { - (RGB::named(WHITE), to_cp437('-')) + (RGB::named(rltk::WHITE), rltk::to_cp437('-')) }; seen_entities.push(( - obfuscate_name_ecs(ecs, entity).0, - RGB::named(item_colour_ecs(ecs, entity)), + get_item_display_name(ecs, *entity).0, + get_item_colour(ecs, *entity), render_fg, glyph, )); } - }); + } } seen_entities.sort_by(|a, b| b.0.cmp(&a.0)); if !seen_entities.is_empty() { y += 1; - ctx.print_color(72, y, RGB::named(BLACK), RGB::named(WHITE), "In View"); + ctx.print_color(72, y, RGB::named(rltk::BLACK), RGB::named(rltk::WHITE), "In View"); for entity in seen_entities { y += 1; - ctx.set(72, y, entity.2, RGB::named(BLACK), entity.3); - ctx.print_color(74, y, entity.1, RGB::named(BLACK), entity.0); + ctx.set(72, y, entity.2, RGB::named(rltk::BLACK), entity.3); + ctx.print_color(74, y, entity.1, RGB::named(rltk::BLACK), entity.0); } } } // Render the message log at [1, 7], ascending, with 7 lines and a max width of 68. - gamelog::print_log( - &mut BACKEND_INTERNAL.lock().consoles[0].console, - Point::new(1, 7), - false, - 7, - 68 - ); + gamelog::print_log(&mut rltk::BACKEND_INTERNAL.lock().consoles[0].console, Point::new(1, 7), false, 7, 68); // Render id let map = ecs.fetch::(); - let id = if map.depth > 0 { - format!("{}{}", map.short_name, map.depth) - } else { - format!("{}", map.short_name) - }; - ctx.print_color_right(70, 54, get_local_col(map.id), RGB::named(BLACK), &id); + let id = format!("D{}", map.id); + ctx.print_color_right(70, 54, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), &id); // Render turn - let turns = crate::gamelog::get_event_count(EVENT::COUNT_TURN); ctx.print_color_right( - 69 - id.len(), + 64, 54, - RGB::named(YELLOW), - RGB::named(BLACK), - &format!("T{}", turns) + RGB::named(rltk::YELLOW), + RGB::named(rltk::BLACK), + &format!("T{}", crate::gamelog::get_event_count("turns")), ); - // Boxes and tooltips last, so they draw over everything else. - ctx.draw_hollow_box(0, 0, 70, 8, RGB::named(WHITE), RGB::named(BLACK)); // Log box - ctx.draw_hollow_box(0, 9, 70, 42, RGB::named(WHITE), RGB::named(BLACK)); // Camera box - ctx.draw_hollow_box(0, 52, 70, 3, RGB::named(WHITE), RGB::named(BLACK)); // Stats box - ctx.draw_hollow_box(71, 0, 33, 55, RGB::named(WHITE), RGB::named(BLACK)); // Side box - tooltip::draw_tooltips(ecs, ctx, None); + tooltip::draw_tooltips(ecs, ctx); } pub fn get_input_direction( ecs: &mut World, - ctx: &mut BTerm, - function: fn(i: i32, j: i32, ecs: &mut World) -> RunState + ctx: &mut Rltk, + function: fn(i: i32, j: i32, ecs: &mut World) -> RunState, ) -> RunState { let (_, _, _, _, x_offset, y_offset) = camera::get_screen_bounds(ecs, ctx); ctx.print_color( 1 + x_offset, 1 + y_offset, - RGB::named(WHITE), - RGB::named(BLACK), - "In what direction? [0-9]/[YUHJKLBN]" + RGB::named(rltk::WHITE), + RGB::named(rltk::BLACK), + "In what direction? [0-9]/[YUHJKLBN]", ); match ctx.key { - None => { - return RunState::ActionWithDirection { function }; - } - Some(key) => - match key { - VirtualKeyCode::Escape => { - return RunState::AwaitingInput; - } - // Cardinals - VirtualKeyCode::Left | VirtualKeyCode::Numpad4 | VirtualKeyCode::H => { - return function(-1, 0, ecs); - } - VirtualKeyCode::Right | VirtualKeyCode::Numpad6 | VirtualKeyCode::L => { - return function(1, 0, ecs); - } - VirtualKeyCode::Up | VirtualKeyCode::Numpad8 | VirtualKeyCode::K => { - return function(0, -1, ecs); - } - VirtualKeyCode::Down | VirtualKeyCode::Numpad2 | VirtualKeyCode::J => { - return function(0, 1, ecs); - } - // Diagonals - VirtualKeyCode::Numpad9 | VirtualKeyCode::U => { - return function(1, -1, ecs); - } - VirtualKeyCode::Numpad7 | VirtualKeyCode::Y => { - return function(-1, -1, ecs); - } - VirtualKeyCode::Numpad3 | VirtualKeyCode::N => { - return function(1, 1, ecs); - } - VirtualKeyCode::Numpad1 | VirtualKeyCode::B => { - return function(-1, 1, ecs); - } - _ => { - return RunState::ActionWithDirection { function }; - } - } + None => return RunState::ActionWithDirection { function }, + Some(key) => match key { + VirtualKeyCode::Escape => return RunState::AwaitingInput, + // Cardinals + VirtualKeyCode::Left | VirtualKeyCode::Numpad4 | VirtualKeyCode::H => return function(-1, 0, ecs), + VirtualKeyCode::Right | VirtualKeyCode::Numpad6 | VirtualKeyCode::L => return function(1, 0, ecs), + VirtualKeyCode::Up | VirtualKeyCode::Numpad8 | VirtualKeyCode::K => return function(0, -1, ecs), + VirtualKeyCode::Down | VirtualKeyCode::Numpad2 | VirtualKeyCode::J => return function(0, 1, ecs), + // Diagonals + VirtualKeyCode::Numpad9 | VirtualKeyCode::U => return function(1, -1, ecs), + VirtualKeyCode::Numpad7 | VirtualKeyCode::Y => return function(-1, -1, ecs), + VirtualKeyCode::Numpad3 | VirtualKeyCode::N => return function(1, 1, ecs), + VirtualKeyCode::Numpad1 | VirtualKeyCode::B => return function(-1, 1, ecs), + _ => return RunState::ActionWithDirection { function }, + }, } } @@ -481,105 +305,85 @@ pub enum ItemMenuResult { } pub fn print_options( - _ecs: &World, - inventory: &PlayerInventory, + inventory: BTreeMap, mut x: i32, mut y: i32, - ctx: &mut BTerm -) -> i32 { + ctx: &mut Rltk, +) -> (i32, i32) { + let mut j = 0; let initial_x: i32 = x; - let mut sorted: Vec<_> = inventory.iter().collect(); - sorted.sort_by(|a, b| a.1.idx.cmp(&b.1.idx)); - - for (info, slot) in sorted { + let mut width: i32 = -1; + for (item, item_count) in &inventory { x = initial_x; // Print the character required to access this item. i.e. (a) - if slot.idx < 26 { - ctx.set(x, y, RGB::named(YELLOW), RGB::named(BLACK), 97 + slot.idx); + if j < 26 { + ctx.set(x, y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), 97 + j as rltk::FontCharType); } else { // If we somehow have more than 26, start using capitals - ctx.set(x, y, RGB::named(YELLOW), RGB::named(BLACK), 65 - 26 + slot.idx); + ctx.set(x, y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), 65 - 26 + j as rltk::FontCharType); } x += 2; - let fg = RGB::from_u8(info.renderables.0, info.renderables.1, info.renderables.2); - ctx.set(x, y, fg, RGB::named(BLACK), info.glyph); + let fg = RGB::from_u8(item.renderables.0, item.renderables.1, item.renderables.2); + ctx.set(x, y, fg, RGB::named(rltk::BLACK), item.glyph); x += 2; - let fg = RGB::from_u8(info.rgb.0, info.rgb.1, info.rgb.2); - if slot.count > 1 { + let fg = RGB::from_u8(item.rgb.0, item.rgb.1, item.rgb.2); + if item_count > &1 { // If more than one, print the number and pluralise // i.e. (a) 3 daggers - ctx.print_color(x, y, fg, RGB::named(BLACK), slot.count); + ctx.print_color(x, y, fg, RGB::named(rltk::BLACK), item_count); x += 2; - ctx.print_color(x, y, fg, RGB::named(BLACK), info.display_name.plural.to_string()); + ctx.print_color(x, y, fg, RGB::named(rltk::BLACK), item.display_name.plural.to_string()); + let this_width = x - initial_x + item.display_name.plural.len() as i32; + width = if width > this_width { width } else { this_width }; } else { - if info.display_name.singular.to_lowercase().ends_with("s") { - ctx.print_color(x, y, fg, RGB::named(BLACK), "some"); + if item.display_name.singular.to_lowercase().ends_with("s") { + ctx.print_color(x, y, fg, RGB::named(rltk::BLACK), "some"); x += 5; - } else if - ['a', 'e', 'i', 'o', 'u'] - .iter() - .any(|&v| info.display_name.singular.to_lowercase().starts_with(v)) + } else if ['a', 'e', 'i', 'o', 'u'] + .iter() + .any(|&v| item.display_name.singular.to_lowercase().starts_with(v)) { // If one and starts with a vowel, print 'an' // i.e. (a) an apple - ctx.print_color(x, y, fg, RGB::named(BLACK), "an"); + ctx.print_color(x, y, fg, RGB::named(rltk::BLACK), "an"); x += 3; } else { // If one and not a vowel, print 'a' // i.e. (a) a dagger - ctx.print_color(x, y, fg, RGB::named(BLACK), "a"); + ctx.print_color(x, y, fg, RGB::named(rltk::BLACK), "a"); x += 2; } - /* - let text = if let Some(worn) = ecs.read_storage::().get(slot.item) { - use crate::EquipmentSlot; - let text = match worn.slot { - EquipmentSlot::Melee | EquipmentSlot::Shield => "being held", - _ => "being worn", - }; - format!("{} ({})", info.display_name.singular.to_string(), text) - } else { - info.display_name.singular.to_string() - }; - */ - let text = info.display_name.singular.to_string(); - ctx.print_color(x, y, fg, RGB::named(BLACK), text); + ctx.print_color(x, y, fg, RGB::named(rltk::BLACK), item.display_name.singular.to_string()); + let this_width = x - initial_x + item.display_name.singular.len() as i32; + width = if width > this_width { width } else { this_width }; } y += 1; + j += 1; } - return y; + return (y, width); } -const PADDING: i32 = 4; -const SOME: i32 = 4; -const AN: i32 = 2; -const A: i32 = 1; - -pub fn get_max_inventory_width(inventory: &PlayerInventory) -> i32 { +pub fn get_max_inventory_width(inventory: &BTreeMap) -> i32 { let mut width: i32 = 0; - for (item, slot) in inventory { + for (item, count) in inventory { let mut this_width = item.display_name.singular.len() as i32; - if slot.count <= 1 { - if item.display_name.singular == item.display_name.plural { - this_width += SOME; - } else if - ['a', 'e', 'i', 'o', 'u'].iter().any(|&v| item.display_name.singular.starts_with(v)) - { - this_width += AN; - } else { - this_width += A; + if count < &1 { + this_width += 6; + if item.display_name.singular.ends_with("s") { + this_width += 5; + } else if ['a', 'e', 'i', 'o', 'u'].iter().any(|&v| item.display_name.singular.starts_with(v)) { + this_width += 3; } } else { - this_width = - (item.display_name.plural.len() as i32) + (slot.count.to_string().len() as i32); // i.e. "12".len + this_width += 6; } width = if width > this_width { width } else { this_width }; } - return width + PADDING; + return width; } // Inside the ECS @@ -588,50 +392,30 @@ pub fn obfuscate_name( names: &ReadStorage, magic_items: &ReadStorage, obfuscated_names: &ReadStorage, - beatitudes: &ReadStorage, dm: &MasterDungeonMap, - wand: Option<&ReadStorage> + wand: Option<&ReadStorage>, ) -> (String, String) { - let (mut singular, mut plural) = ( - "nameless item (bug)".to_string(), - "nameless items (bug)".to_string(), - ); + let (mut singular, mut plural) = ("nameless item (bug)".to_string(), "nameless items (bug)".to_string()); if let Some(name) = names.get(item) { if magic_items.get(item).is_some() { if dm.identified_items.contains(&name.name) { (singular, plural) = (name.name.clone(), name.plural.clone()); - if wand.is_some() { - let wands = wand.unwrap(); - if let Some(wand) = wands.get(item) { - let used = wand.max_uses - wand.uses; - for _i in 0..used { - singular.push_str("*"); - plural.push_str("*"); - } - } - } } else if let Some(obfuscated) = obfuscated_names.get(item) { (singular, plural) = (obfuscated.name.clone(), obfuscated.plural.clone()); } else { - (singular, plural) = ( - "unid magic item".to_string(), - "unid magic items".to_string(), - ); + (singular, plural) = ("unid magic item".to_string(), "unid magic items".to_string()); } } else { (singular, plural) = (name.name.clone(), name.plural.clone()); } } - if let Some(has_beatitude) = beatitudes.get(item) { - if has_beatitude.known { - let prefix = match has_beatitude.buc { - BUC::Cursed => Some("cursed "), - BUC::Uncursed => Some("uncursed "), - BUC::Blessed => Some("blessed "), - }; - if prefix.is_some() { - singular.insert_str(0, prefix.unwrap()); - plural.insert_str(0, prefix.unwrap()); + if wand.is_some() { + let wands = wand.unwrap(); + if let Some(wand) = wands.get(item) { + let used = wand.max_uses - wand.uses; + for _i in 0..used { + singular.push_str("*"); + plural.push_str("*"); } } } @@ -639,151 +423,66 @@ pub fn obfuscate_name( } // Outside the ECS -pub fn obfuscate_name_ecs(ecs: &World, item: Entity) -> (String, String) { - let (mut singular, mut plural) = ( - "nameless item (bug)".to_string(), - "nameless items (bug)".to_string(), - ); +pub fn get_item_display_name(ecs: &World, item: Entity) -> (String, String) { + let (mut singular, mut plural) = ("nameless item (bug)".to_string(), "nameless items (bug)".to_string()); if let Some(name) = ecs.read_storage::().get(item) { if ecs.read_storage::().get(item).is_some() { let dm = ecs.fetch::(); if dm.identified_items.contains(&name.name) { (singular, plural) = (name.name.clone(), name.plural.clone()); - if let Some(wand) = ecs.read_storage::().get(item) { - let used = wand.max_uses - wand.uses; - for _i in 0..used { - singular.push_str("*"); - plural.push_str("*"); - } - } } else if let Some(obfuscated) = ecs.read_storage::().get(item) { (singular, plural) = (obfuscated.name.clone(), obfuscated.plural.clone()); } else { - (singular, plural) = ( - "unid magic item".to_string(), - "unid magic items".to_string(), - ); + (singular, plural) = ("unid magic item".to_string(), "unid magic items".to_string()); } } else { (singular, plural) = (name.name.clone(), name.plural.clone()); } } - if let Some(has_beatitude) = ecs.read_storage::().get(item) { - if has_beatitude.known { - let prefix = match has_beatitude.buc { - BUC::Cursed => Some("cursed "), - BUC::Uncursed => Some("uncursed "), - BUC::Blessed => Some("blessed "), - }; - if prefix.is_some() { - singular.insert_str(0, prefix.unwrap()); - plural.insert_str(0, prefix.unwrap()); - } + if let Some(wand) = ecs.read_storage::().get(item) { + let used = wand.max_uses - wand.uses; + for _i in 0..used { + singular.push_str("*"); + plural.push_str("*"); } } return (singular, plural); } -pub fn unobf_name_ecs(ecs: &World, item: Entity) -> (String, String) { - let (mut singular, mut plural) = ("nameless (bug)".to_string(), "nameless (bug)".to_string()); +pub fn get_item_colour(ecs: &World, item: Entity) -> RGB { + let dm = ecs.fetch::(); if let Some(name) = ecs.read_storage::().get(item) { - (singular, plural) = (name.name.clone(), name.plural.clone()); - } - if let Some(has_beatitude) = ecs.read_storage::().get(item) { - let prefix = match has_beatitude.buc { - BUC::Cursed => "cursed ", - BUC::Uncursed => "uncursed ", - BUC::Blessed => "blessed ", - }; - singular.insert_str(0, prefix); - plural.insert_str(0, prefix); - } - return (singular, plural); -} - -/// Gets renderable colour as tuple of u8 -pub fn renderable_colour(renderables: &ReadStorage, entity: Entity) -> (u8, u8, u8) { - return if let Some(renderable) = renderables.get(entity) { - ( - (renderable.fg.r * 255.0) as u8, - (renderable.fg.g * 255.0) as u8, - (renderable.fg.b * 255.0) as u8, - ) - } else { - WHITE - }; -} - -/// Gets renderable colour as tuple of u8 -pub fn renderable_colour_ecs(ecs: &World, entity: Entity) -> (u8, u8, u8) { - return if let Some(renderable) = ecs.read_storage::().get(entity) { - ( - (renderable.fg.r * 255.0) as u8, - (renderable.fg.g * 255.0) as u8, - (renderable.fg.b * 255.0) as u8, - ) - } else { - WHITE - }; -} - -pub fn item_colour_ecs(ecs: &World, item: Entity) -> (u8, u8, u8) { - if let Some(beatitude) = ecs.read_storage::().get(item) { - if beatitude.known { - match beatitude.buc { - BUC::Blessed => { - return GREEN; - } - BUC::Uncursed => { - return WHITE; - } - BUC::Cursed => { - return RED; + if let Some(magic) = ecs.read_storage::().get(item) { + if dm.identified_items.contains(&name.name) { + // If identified magic item, use rarity colour + match magic.class { + MagicItemClass::Common => return RGB::named(rltk::WHITE), + MagicItemClass::Uncommon => return RGB::named(rltk::GREEN), + MagicItemClass::Rare => return RGB::named(rltk::BLUE), + MagicItemClass::VeryRare => return RGB::named(rltk::PURPLE), + MagicItemClass::Legendary => return RGB::named(rltk::GOLD), } + } else { + // Unidentified magic item + return RGB::named(rltk::GREY); } - } else { - // Unidentified magic item - return GREY; } } // If nonmagic, just use white - return WHITE; + return RGB::named(rltk::WHITE); } -pub fn item_colour(item: Entity, beatitudes: &ReadStorage) -> (u8, u8, u8) { - if let Some(beatitude) = beatitudes.get(item) { - if beatitude.known { - match beatitude.buc { - BUC::Blessed => { - return GREEN; - } - BUC::Uncursed => { - return WHITE; - } - BUC::Cursed => { - return RED; - } - } - } else { - // Unidentified magic item - return GREY; - } - } - // If nonmagic, just use white - return WHITE; -} - -pub fn show_help(ctx: &mut BTerm) -> YesNoResult { +pub fn show_help(ctx: &mut Rltk) -> YesNoResult { let mut x = 3; let mut y = 12; let height = 22; let width = 25; - ctx.draw_box(x, y, width, height, RGB::named(WHITE), RGB::named(BLACK)); - ctx.print_color(x + 3, y, RGB::named(YELLOW), RGB::named(BLACK), " Controls "); - ctx.print_color(x + 3, y + height, RGB::named(YELLOW), RGB::named(BLACK), " ESC/? to close "); + ctx.draw_box(x, y, width, height, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)); + ctx.print_color(x + 3, y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), " Controls "); + ctx.print_color(x + 3, y + height, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), " ESC/? to close "); x += 2; y += 2; - ctx.print_color(x, y, RGB::named(GREEN), RGB::named(BLACK), "MOVE COMMANDS"); + ctx.print_color(x, y, RGB::named(rltk::GREEN), RGB::named(rltk::BLACK), "MOVE COMMANDS"); y += 2; ctx.print(x, y, "y k u 7 8 9 > down"); ctx.print(x, y + 1, " \\|/ \\|/"); @@ -791,7 +490,7 @@ pub fn show_help(ctx: &mut BTerm) -> YesNoResult { ctx.print(x, y + 3, " /|\\ /|\\"); ctx.print(x, y + 4, "b j n 1 2 3 . wait"); y += 7; - ctx.print_color(x, y, RGB::named(GREEN), RGB::named(BLACK), "OBJECT INTERACTION"); + ctx.print_color(x, y, RGB::named(rltk::GREEN), RGB::named(rltk::BLACK), "OBJECT INTERACTION"); y += 2; ctx.print(x, y, "g get d drop"); y += 1; @@ -799,214 +498,79 @@ pub fn show_help(ctx: &mut BTerm) -> YesNoResult { y += 1; ctx.print(x, y, "o open c close"); y += 1; - ctx.print(x, y, "f force x farlook"); + ctx.print(x, y, "f force"); y += 2; - ctx.print_color(x, y, RGB::named(GREEN), RGB::named(BLACK), "MOUSE CONTROL"); + ctx.print_color(x, y, RGB::named(rltk::GREEN), RGB::named(rltk::BLACK), "MOUSE CONTROL"); y += 2; ctx.print(x, y, "hover for tooltips"); match ctx.key { None => YesNoResult::NoSelection, - Some(key) => - match key { - VirtualKeyCode::Escape => YesNoResult::Yes, - VirtualKeyCode::Slash => { - if ctx.shift { - return YesNoResult::Yes; - } - return YesNoResult::NoSelection; + Some(key) => match key { + VirtualKeyCode::Escape => YesNoResult::Yes, + VirtualKeyCode::Slash => { + if ctx.shift { + return YesNoResult::Yes; } - _ => YesNoResult::NoSelection, + return YesNoResult::NoSelection; } + _ => YesNoResult::NoSelection, + }, } } -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(PartialEq, Eq, PartialOrd, Ord)] struct DisplayName { singular: String, plural: String, } -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(PartialEq, Eq, PartialOrd, Ord)] pub struct UniqueInventoryItem { display_name: DisplayName, rgb: (u8, u8, u8), renderables: (u8, u8, u8), glyph: u16, - beatitude_status: i32, name: String, } -pub struct InventorySlot { - pub item: Entity, - pub count: i32, - pub idx: usize, -} - -pub type PlayerInventory = HashMap; - -pub enum Filter { - All, - Backpack, - Equipped, - Category(ItemType), -} - -macro_rules! includeitem { - ($inv:expr, $ecs:expr, $e:expr, $k:expr) => { - $inv.entry(unique_ecs($ecs, $e)) - .and_modify(|slot| { - slot.count += 1; - }) - .or_insert(InventorySlot { - item: $e, - count: 1, - idx: $k.idx, - }); - }; -} - -pub fn items(ecs: &World, filter: Filter) -> PlayerInventory { +pub fn get_player_inventory(ecs: &World) -> (BTreeMap, BTreeMap) { + let player_entity = ecs.fetch::(); + let names = ecs.read_storage::(); + let backpack = ecs.read_storage::(); let entities = ecs.entities(); - let keys = ecs.read_storage::(); - let mut inv: PlayerInventory = HashMap::new(); - match filter { - Filter::All => { - for (e, k) in (&entities, &keys).join() { - includeitem!(inv, ecs, e, k); - } - } - Filter::Backpack => { - let backpack = ecs.read_storage::(); - for (e, k, _b) in (&entities, &keys, &backpack).join() { - includeitem!(inv, ecs, e, k); - } - } - Filter::Equipped => { - let equipped = ecs.read_storage::(); - for (e, k, _e) in (&entities, &keys, &equipped).join() { - includeitem!(inv, ecs, e, k); - } - } - Filter::Category(itemtype) => { - let items = ecs.read_storage::(); - for (e, k, _i) in (&entities, &keys, &items) - .join() - .filter(|e| e.2.category == itemtype) { - includeitem!(inv, ecs, e, k); - } - } + let renderables = ecs.read_storage::(); + + let mut inventory_ids: BTreeMap = BTreeMap::new(); + let mut player_inventory: BTreeMap = BTreeMap::new(); + for (entity, _pack, name, renderable) in + (&entities, &backpack, &names, &renderables).join().filter(|item| item.1.owner == *player_entity) + { + // RGB can't be used as a key. This is converting the RGB (tuple of f32) into a tuple of u8s. + let item_colour = get_item_colour(ecs, entity); + let renderables = + ((renderable.fg.r * 255.0) as u8, (renderable.fg.g * 255.0) as u8, (renderable.fg.b * 255.0) as u8); + let (r, g, b): (u8, u8, u8) = + ((item_colour.r * 255.0) as u8, (item_colour.g * 255.0) as u8, (item_colour.b * 255.0) as u8); + let (singular, plural) = get_item_display_name(ecs, entity); + player_inventory + .entry(UniqueInventoryItem { + display_name: DisplayName { singular: singular.clone(), plural: plural }, + rgb: (r, g, b), + renderables: renderables, + glyph: renderable.glyph, + name: name.name.clone(), + }) + .and_modify(|count| *count += 1) + .or_insert(1); + inventory_ids.entry(singular).or_insert(entity); } - inv + + return (player_inventory, inventory_ids); } -pub fn show_inventory(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option) { - let player_inventory = items(&gs.ecs, Filter::Backpack); - let count = player_inventory.len(); - - let (x_offset, y_offset) = (1, 10); - - let on_overmap = gs.ecs.fetch::().overmap; - let message = if !on_overmap { - "Interact with what item? [aA-zZ][Esc.]" - } else { - "You can't use items on the overmap [Esc.]" - }; - - ctx.print_color(1 + x_offset, 1 + y_offset, RGB::named(WHITE), RGB::named(BLACK), message); - - let x = 1 + x_offset; - let y = 3 + y_offset; - let width = get_max_inventory_width(&player_inventory); - ctx.draw_box(x, y, width + 2, (count + 1) as i32, RGB::named(WHITE), RGB::named(BLACK)); - print_options(&gs.ecs, &player_inventory, x + 1, y + 1, ctx); - - match ctx.key { - None => (ItemMenuResult::NoResponse, None), - Some(key) => - match key { - VirtualKeyCode::Escape => (ItemMenuResult::Cancel, None), - _ => { - let selection = letter_to_option::letter_to_option(key, ctx.shift); - if selection != -1 && check_key(selection as usize) { - if on_overmap { - gamelog::Logger - ::new() - .append("You can't use items on the overmap.") - .log(); - } else { - // Get the first entity with a Key {} component that has idx matching selection - let entities = gs.ecs.entities(); - let keyed_items = gs.ecs.read_storage::(); - let backpack = gs.ecs.read_storage::(); - for (e, key, _b) in (&entities, &keyed_items, &backpack).join() { - if key.idx == (selection as usize) { - return (ItemMenuResult::Selected, Some(e)); - } - } - // TODO: Gamelog about not having selected item? - } - } - (ItemMenuResult::NoResponse, None) - } - } - } -} - -pub fn drop_item_menu(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option) { - let player_inventory = items(&gs.ecs, Filter::Backpack); - let count = player_inventory.len(); - - let (x_offset, y_offset) = (1, 10); - - let on_overmap = gs.ecs.fetch::().overmap; - let message = if !on_overmap { - "Drop what? [aA-zZ][Esc.]" - } else { - "You can't drop items on the overmap [Esc.]" - }; - - ctx.print_color(1 + x_offset, 1 + y_offset, RGB::named(WHITE), RGB::named(BLACK), message); - - let x = 1 + x_offset; - let y = 3 + y_offset; - let width = get_max_inventory_width(&player_inventory); - ctx.draw_box(x, y, width + 2, (count + 1) as i32, RGB::named(WHITE), RGB::named(BLACK)); - print_options(&gs.ecs, &player_inventory, x + 1, y + 1, ctx); - - match ctx.key { - None => (ItemMenuResult::NoResponse, None), - Some(key) => - match key { - VirtualKeyCode::Escape => (ItemMenuResult::Cancel, None), - _ => { - let selection = letter_to_option::letter_to_option(key, ctx.shift); - if selection != -1 && check_key(selection as usize) { - if on_overmap { - gamelog::Logger - ::new() - .append("You can't drop items on the overmap.") - .log(); - } else { - // Get the first entity with a Key {} component that has an idx matching "selection". - let entities = gs.ecs.entities(); - let keyed_items = gs.ecs.read_storage::(); - let backpack = gs.ecs.read_storage::(); - for (e, key, _b) in (&entities, &keyed_items, &backpack).join() { - if key.idx == (selection as usize) { - return (ItemMenuResult::Selected, Some(e)); - } - } - } - } - (ItemMenuResult::NoResponse, None) - } - } - } -} - -pub fn remove_item_menu(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option) { - let player_inventory = items(&gs.ecs, Filter::Equipped); +pub fn show_inventory(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Option) { + let (player_inventory, inventory_ids) = get_player_inventory(&gs.ecs); let count = player_inventory.len(); let (x_offset, y_offset) = (1, 10); @@ -1014,59 +578,131 @@ pub fn remove_item_menu(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Opt ctx.print_color( 1 + x_offset, 1 + y_offset, - RGB::named(WHITE), - RGB::named(BLACK), - "Unequip what? [aA-zZ][Esc.]" + RGB::named(rltk::WHITE), + RGB::named(rltk::BLACK), + "Interact with what item? [aA-zZ][Esc.]", ); let x = 1 + x_offset; let y = 3 + y_offset; let width = get_max_inventory_width(&player_inventory); - ctx.draw_box(x, y, width + 2, (count + 1) as i32, RGB::named(WHITE), RGB::named(BLACK)); - print_options(&gs.ecs, &player_inventory, x + 1, y + 1, ctx); + ctx.draw_box(x, y, width + 2, (count + 1) as i32, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)); + print_options(player_inventory, x + 1, y + 1, ctx); match ctx.key { None => (ItemMenuResult::NoResponse, None), - Some(key) => - match key { - VirtualKeyCode::Escape => (ItemMenuResult::Cancel, None), - _ => { - let selection = letter_to_option::letter_to_option(key, ctx.shift); - if selection != -1 && check_key(selection as usize) { - // Get the first entity with a Key {} component that has an idx matching "selection". - let entities = gs.ecs.entities(); - let keyed_items = gs.ecs.read_storage::(); - let equipped = gs.ecs.read_storage::(); - for (e, key, _e) in (&entities, &keyed_items, &equipped).join() { - if key.idx == (selection as usize) { - return (ItemMenuResult::Selected, Some(e)); - } - } - } - (ItemMenuResult::NoResponse, None) + Some(key) => match key { + VirtualKeyCode::Escape => (ItemMenuResult::Cancel, None), + _ => { + let selection = letter_to_option::letter_to_option(key, ctx.shift); + if selection > -1 && selection < count as i32 { + return (ItemMenuResult::Selected, Some(*inventory_ids.iter().nth(selection as usize).unwrap().1)); } + (ItemMenuResult::NoResponse, None) } + }, } } -#[derive(PartialEq, Copy, Clone)] -pub enum TargetResult { - Cancel, - NoResponse { - x: i32, - y: i32, - }, - Selected, +pub fn drop_item_menu(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Option) { + let (player_inventory, inventory_ids) = get_player_inventory(&gs.ecs); + let count = player_inventory.len(); + + let (x_offset, y_offset) = (1, 10); + + ctx.print_color( + 1 + x_offset, + 1 + y_offset, + RGB::named(rltk::WHITE), + RGB::named(rltk::BLACK), + "Drop what? [aA-zZ][Esc.]", + ); + + let x = 1 + x_offset; + let y = 3 + y_offset; + let width = get_max_inventory_width(&player_inventory); + ctx.draw_box(x, y, width + 2, (count + 1) as i32, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)); + print_options(player_inventory, x + 1, y + 1, ctx); + + match ctx.key { + None => (ItemMenuResult::NoResponse, None), + Some(key) => match key { + VirtualKeyCode::Escape => (ItemMenuResult::Cancel, None), + _ => { + let selection = rltk::letter_to_option(key); + if selection > -1 && selection < count as i32 { + return (ItemMenuResult::Selected, Some(*inventory_ids.iter().nth(selection as usize).unwrap().1)); + } + (ItemMenuResult::NoResponse, None) + } + }, + } } -pub fn ranged_target( - gs: &mut State, - ctx: &mut BTerm, - x: i32, - y: i32, - range: i32, - aoe: i32 -) -> (TargetResult, Option) { +pub fn remove_item_menu(gs: &mut State, ctx: &mut Rltk) -> (ItemMenuResult, Option) { + let player_entity = gs.ecs.fetch::(); + let backpack = gs.ecs.read_storage::(); + let entities = gs.ecs.entities(); + let inventory = (&backpack).join().filter(|item| item.owner == *player_entity); + let count = inventory.count(); + + let (x_offset, y_offset) = (1, 10); + + ctx.print_color( + 1 + x_offset, + 1 + y_offset, + RGB::named(rltk::WHITE), + RGB::named(rltk::BLACK), + "Unequip what? [aA-zZ][Esc.]", + ); + + let mut equippable: Vec<(Entity, String)> = Vec::new(); + let mut width = 2; + for (entity, _pack) in (&entities, &backpack).join().filter(|item| item.1.owner == *player_entity) { + let this_name = &get_item_display_name(&gs.ecs, entity).0; + let this_width = 5 + this_name.len(); + width = if width > this_width { width } else { this_width }; + equippable.push((entity, this_name.to_string())); + } + + let x = 1 + x_offset; + let mut y = 3 + y_offset; + + ctx.draw_box(x, y, width, (count + 1) as i32, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)); + y += 1; + + let mut j = 0; + let renderables = gs.ecs.read_storage::(); + for (e, name) in &equippable { + let (mut fg, glyph) = if let Some(renderable) = renderables.get(*e) { + (renderable.fg, renderable.glyph) + } else { + (RGB::named(rltk::WHITE), rltk::to_cp437('-')) + }; + ctx.set(x + 1, y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), 97 + j as rltk::FontCharType); + ctx.set(x + 3, y, fg, RGB::named(rltk::BLACK), glyph); + fg = get_item_colour(&gs.ecs, *e); + ctx.print_color(x + 5, y, fg, RGB::named(rltk::BLACK), name); + y += 1; + j += 1; + } + + match ctx.key { + None => (ItemMenuResult::NoResponse, None), + Some(key) => match key { + VirtualKeyCode::Escape => (ItemMenuResult::Cancel, None), + _ => { + let selection = rltk::letter_to_option(key); + if selection > -1 && selection < count as i32 { + return (ItemMenuResult::Selected, Some(equippable[selection as usize].0)); + } + (ItemMenuResult::NoResponse, None) + } + }, + } +} + +pub fn ranged_target(gs: &mut State, ctx: &mut Rltk, range: i32, aoe: i32) -> (ItemMenuResult, Option) { let (min_x, max_x, min_y, max_y, x_offset, y_offset) = camera::get_screen_bounds(&gs.ecs, ctx); let player_entity = gs.ecs.fetch::(); let player_pos = gs.ecs.fetch::(); @@ -1075,9 +711,9 @@ pub fn ranged_target( ctx.print_color( 1 + x_offset, 1 + y_offset, - RGB::named(WHITE), - RGB::named(BLACK), - "Targeting which tile? [mouse input]" + RGB::named(rltk::WHITE), + RGB::named(rltk::BLACK), + "Targeting which tile? [mouse input]", ); // Highlight available cells @@ -1086,35 +722,22 @@ pub fn ranged_target( if let Some(visible) = visible { // We have a viewshed for idx in visible.visible_tiles.iter() { - let distance = DistanceAlg::Pythagoras.distance2d(*player_pos, *idx); - if distance <= (range as f32) { + let distance = rltk::DistanceAlg::Pythagoras.distance2d(*player_pos, *idx); + if distance <= range as f32 { let screen_x = idx.x - min_x; let screen_y = idx.y - min_y; - if - screen_x > 1 && - screen_x < max_x - min_x - 1 && - screen_y > 1 && - screen_y < max_y - min_y - 1 - { - ctx.set_bg(screen_x + x_offset, screen_y + y_offset, TARGETING_VALID_COL); + if screen_x > 1 && screen_x < (max_x - min_x) - 1 && screen_y > 1 && screen_y < (max_y - min_y) - 1 { + ctx.set_bg(screen_x + x_offset, screen_y + y_offset, RGB::named(rltk::BLUE)); available_cells.push(idx); } } } } else { - return (TargetResult::Cancel, None); + return (ItemMenuResult::Cancel, None); } // Draw mouse cursor - let mouse_pos = (x, y); - let (min_x, _max_x, min_y, _max_y, x_offset, y_offset) = camera::get_screen_bounds( - &gs.ecs, - ctx - ); - let (screen_x, screen_y) = (69, 41); - let x = x.clamp(x_offset, x_offset - 1 + (screen_x as i32)); - let y = y.clamp(y_offset, y_offset - 1 + (screen_y as i32)); - + let mouse_pos = ctx.mouse_pos(); let mut mouse_pos_adjusted = mouse_pos; mouse_pos_adjusted.0 += min_x - x_offset; mouse_pos_adjusted.1 += min_y - y_offset; @@ -1125,85 +748,29 @@ pub fn ranged_target( valid_target = true; } } - let mut result = (TargetResult::NoResponse { x, y }, None); if valid_target { - let path = line2d( - LineAlg::Bresenham, - Point::new(player_pos.x, player_pos.y), - Point::new(mouse_pos_adjusted.0, mouse_pos_adjusted.1) - ); - for (i, point) in path.iter().enumerate() { - if i == 0 || i == path.len() - 1 { - continue; - } - ctx.set( - point.x + x_offset - min_x, - point.y + y_offset - min_y, - RGB::named(TARGETING_LINE_COL), - RGB::named(TARGETING_VALID_COL), - to_cp437('~') - ); - } if aoe > 0 { // We adjust for camera position when getting FOV, but then we need to adjust back // when iterating through the tiles themselves, by taking away min_x/min_y. - let mut blast_tiles = field_of_view( - Point::new(mouse_pos_adjusted.0, mouse_pos_adjusted.1), - aoe, - &*map - ); - blast_tiles.retain( - |p| p.x > 0 && p.x < map.width - 1 && p.y > 0 && p.y < map.height - 1 - ); + let mut blast_tiles = + rltk::field_of_view(Point::new(mouse_pos_adjusted.0, mouse_pos_adjusted.1), aoe, &*map); + blast_tiles.retain(|p| p.x > 0 && p.x < map.width - 1 && p.y > 0 && p.y < map.height - 1); for tile in blast_tiles.iter() { - let bg = if available_cells.contains(&tile) { - let col1 = TARGETING_AOE_COL; - let col2 = TARGETING_VALID_COL; - ((col1.0 + col2.0) / 2, (col1.1 + col2.1) / 2, (col1.2 + col2.2) / 2) - } else { - let col1 = TARGETING_AOE_COL; - let col2 = BLACK; - ((col1.0 + col2.0) / 2, (col1.1 + col2.1) / 2, (col1.2 + col2.2) / 2) - }; - ctx.set_bg(tile.x - min_x + x_offset, tile.y - min_y + y_offset, bg); + ctx.set_bg(tile.x - min_x + x_offset, tile.y - min_y + y_offset, RGB::named(rltk::DARKCYAN)); } } - - ctx.set_bg(mouse_pos.0, mouse_pos.1, RGB::named(TARGETING_CURSOR_COL)); - result = match ctx.key { - None => result, - Some(key) => - match key { - VirtualKeyCode::Return => { - return ( - TargetResult::Selected, - Some(Point::new(mouse_pos_adjusted.0, mouse_pos_adjusted.1)), - ); - } - _ => result, - } - }; + ctx.set_bg(mouse_pos.0, mouse_pos.1, RGB::named(rltk::CYAN)); + if ctx.left_click { + return (ItemMenuResult::Selected, Some(Point::new(mouse_pos_adjusted.0, mouse_pos_adjusted.1))); + } } else { - ctx.set_bg(mouse_pos.0, mouse_pos.1, RGB::named(RED)); + ctx.set_bg(mouse_pos.0, mouse_pos.1, RGB::named(rltk::RED)); + if ctx.left_click { + return (ItemMenuResult::Cancel, None); + } } - result = match ctx.key { - None => result, - Some(key) => - match key { - VirtualKeyCode::Escape => (TargetResult::Cancel, None), - VirtualKeyCode::Numpad9 => (TargetResult::NoResponse { x: x + 1, y: y - 1 }, None), - VirtualKeyCode::Numpad7 => (TargetResult::NoResponse { x: x - 1, y: y - 1 }, None), - VirtualKeyCode::Numpad6 => (TargetResult::NoResponse { x: x + 1, y }, None), - VirtualKeyCode::Numpad4 => (TargetResult::NoResponse { x: x - 1, y }, None), - VirtualKeyCode::Numpad8 => (TargetResult::NoResponse { x, y: y - 1 }, None), - VirtualKeyCode::Numpad3 => (TargetResult::NoResponse { x: x + 1, y: y + 1 }, None), - VirtualKeyCode::Numpad2 => (TargetResult::NoResponse { x, y: y + 1 }, None), - VirtualKeyCode::Numpad1 => (TargetResult::NoResponse { x: x - 1, y: y + 1 }, None), - _ => result, - } - }; - return result; + (ItemMenuResult::NoResponse, None) } #[derive(PartialEq, Copy, Clone)] @@ -1215,15 +782,11 @@ pub enum MainMenuSelection { #[derive(PartialEq, Copy, Clone)] pub enum MainMenuResult { - NoSelection { - selected: MainMenuSelection, - }, - Selected { - selected: MainMenuSelection, - }, + NoSelection { selected: MainMenuSelection }, + Selected { selected: MainMenuSelection }, } -pub fn main_menu(gs: &mut State, ctx: &mut BTerm) -> MainMenuResult { +pub fn main_menu(gs: &mut State, ctx: &mut Rltk) -> MainMenuResult { let save_exists = super::saveload_system::does_save_exist(); let runstate = gs.ecs.fetch::(); let assets = gs.ecs.fetch::(); @@ -1237,96 +800,73 @@ pub fn main_menu(gs: &mut State, ctx: &mut BTerm) -> MainMenuResult { height -= 1; } - ctx.draw_box_double(x, y - 4, 13, height, RGB::named(WHITE), RGB::named(BLACK)); - ctx.print_color(x + 3, y - 2, RGB::named(YELLOW), RGB::named(BLACK), "RUST-RL!"); + ctx.draw_box_double(x, y - 4, 13, height, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)); + ctx.print_color(x + 3, y - 2, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "RUST-RL!"); if let RunState::MainMenu { menu_selection: selection } = *runstate { if save_exists { if selection == MainMenuSelection::LoadGame { - ctx.print_color(x + 2, y, RGB::named(YELLOW), RGB::named(BLACK), "["); - ctx.print_color(x + 3, y, RGB::named(GREEN), RGB::named(BLACK), "continue"); - ctx.print_color(x + 11, y, RGB::named(YELLOW), RGB::named(BLACK), "]"); + ctx.print_color(x + 2, y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "["); + ctx.print_color(x + 3, y, RGB::named(rltk::GREEN), RGB::named(rltk::BLACK), "continue"); + ctx.print_color(x + 11, y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "]"); } else { - ctx.print_color(x + 3, y, RGB::named(WHITE), RGB::named(BLACK), "continue"); + ctx.print_color(x + 3, y, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), "continue"); } y += 1; } if selection == MainMenuSelection::NewGame { - ctx.print_color(x + 2, y, RGB::named(YELLOW), RGB::named(BLACK), "["); - ctx.print_color(x + 3, y, RGB::named(GREEN), RGB::named(BLACK), "new game"); - ctx.print_color(x + 11, y, RGB::named(YELLOW), RGB::named(BLACK), "]"); + ctx.print_color(x + 2, y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "["); + ctx.print_color(x + 3, y, RGB::named(rltk::GREEN), RGB::named(rltk::BLACK), "new game"); + ctx.print_color(x + 11, y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "]"); } else { - ctx.print_color(x + 3, y, RGB::named(WHITE), RGB::named(BLACK), "new game"); + ctx.print_color(x + 3, y, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), "new game"); } y += 1; if selection == MainMenuSelection::Quit { - ctx.print_color(x + 2, y, RGB::named(YELLOW), RGB::named(BLACK), "["); - ctx.print_color(x + 3, y, RGB::named(GREEN), RGB::named(BLACK), "goodbye!"); - ctx.print_color(x + 11, y, RGB::named(YELLOW), RGB::named(BLACK), "]"); + ctx.print_color(x + 2, y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "["); + ctx.print_color(x + 3, y, RGB::named(rltk::GREEN), RGB::named(rltk::BLACK), "goodbye!"); + ctx.print_color(x + 11, y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "]"); } else { - ctx.print_color(x + 5, y, RGB::named(WHITE), RGB::named(BLACK), "quit"); + ctx.print_color(x + 5, y, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK), "quit"); } match ctx.key { - None => { - return MainMenuResult::NoSelection { selected: selection }; - } - Some(key) => - match key { - VirtualKeyCode::Escape | VirtualKeyCode::C => { - return MainMenuResult::NoSelection { selected: MainMenuSelection::Quit }; - } - VirtualKeyCode::N => { - return MainMenuResult::NoSelection { selected: MainMenuSelection::NewGame }; - } - VirtualKeyCode::L => { - return MainMenuResult::NoSelection { - selected: MainMenuSelection::LoadGame, - }; - } - VirtualKeyCode::Up | VirtualKeyCode::Numpad8 | VirtualKeyCode::K => { - let mut new_selection; - match selection { - MainMenuSelection::NewGame => { - new_selection = MainMenuSelection::LoadGame; - } - MainMenuSelection::LoadGame => { - new_selection = MainMenuSelection::Quit; - } - MainMenuSelection::Quit => { - new_selection = MainMenuSelection::NewGame; - } - } - if new_selection == MainMenuSelection::LoadGame && !save_exists { - new_selection = MainMenuSelection::NewGame; - } - return MainMenuResult::NoSelection { selected: new_selection }; - } - VirtualKeyCode::Down | VirtualKeyCode::Numpad2 | VirtualKeyCode::J => { - let mut new_selection; - match selection { - MainMenuSelection::NewGame => { - new_selection = MainMenuSelection::Quit; - } - MainMenuSelection::LoadGame => { - new_selection = MainMenuSelection::NewGame; - } - MainMenuSelection::Quit => { - new_selection = MainMenuSelection::LoadGame; - } - } - if new_selection == MainMenuSelection::LoadGame && !save_exists { - new_selection = MainMenuSelection::Quit; - } - return MainMenuResult::NoSelection { selected: new_selection }; - } - VirtualKeyCode::Return | VirtualKeyCode::NumpadEnter => { - return MainMenuResult::Selected { selected: selection }; - } - _ => { - return MainMenuResult::NoSelection { selected: selection }; - } + None => return MainMenuResult::NoSelection { selected: selection }, + Some(key) => match key { + VirtualKeyCode::Escape | VirtualKeyCode::C => { + return MainMenuResult::NoSelection { selected: MainMenuSelection::Quit } } + VirtualKeyCode::N => return MainMenuResult::NoSelection { selected: MainMenuSelection::NewGame }, + VirtualKeyCode::L => return MainMenuResult::NoSelection { selected: MainMenuSelection::LoadGame }, + VirtualKeyCode::Up | VirtualKeyCode::Numpad8 | VirtualKeyCode::K => { + let mut new_selection; + match selection { + MainMenuSelection::NewGame => new_selection = MainMenuSelection::LoadGame, + MainMenuSelection::LoadGame => new_selection = MainMenuSelection::Quit, + MainMenuSelection::Quit => new_selection = MainMenuSelection::NewGame, + } + if new_selection == MainMenuSelection::LoadGame && !save_exists { + new_selection = MainMenuSelection::NewGame; + } + return MainMenuResult::NoSelection { selected: new_selection }; + } + VirtualKeyCode::Down | VirtualKeyCode::Numpad2 | VirtualKeyCode::J => { + let mut new_selection; + match selection { + MainMenuSelection::NewGame => new_selection = MainMenuSelection::Quit, + MainMenuSelection::LoadGame => new_selection = MainMenuSelection::NewGame, + MainMenuSelection::Quit => new_selection = MainMenuSelection::LoadGame, + } + if new_selection == MainMenuSelection::LoadGame && !save_exists { + new_selection = MainMenuSelection::Quit; + } + return MainMenuResult::NoSelection { selected: new_selection }; + } + VirtualKeyCode::Return | VirtualKeyCode::NumpadEnter => { + return MainMenuResult::Selected { selected: selection } + } + _ => return MainMenuResult::NoSelection { selected: selection }, + }, } } MainMenuResult::NoSelection { selected: MainMenuSelection::NewGame } @@ -1336,177 +876,77 @@ pub fn main_menu(gs: &mut State, ctx: &mut BTerm) -> MainMenuResult { pub enum YesNoResult { NoSelection, Yes, - No, } -pub fn game_over(ctx: &mut BTerm) -> YesNoResult { +pub fn game_over(ctx: &mut Rltk) -> YesNoResult { let mut x = 3; let mut y = 12; let width = 45; let height = 20; - ctx.draw_box(x, y, width, height, RGB::named(WHITE), RGB::named(BLACK)); - ctx.print_color(x + 3, y, RGB::named(YELLOW), RGB::named(BLACK), "You died!"); - ctx.print_color( - x + 3, - y + height, - RGB::named(YELLOW), - RGB::named(BLACK), - " Write a morgue file? [y/n] " - ); + ctx.draw_box(x, y, width, height, RGB::named(rltk::WHITE), RGB::named(rltk::BLACK)); + ctx.print_color(x + 3, y, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "You died!"); + ctx.print_color(x + 3, y + height, RGB::named(rltk::YELLOW), RGB::named(rltk::BLACK), "ESC to close"); x += 2; y += 2; ctx.print_color( x, y, - RGB::named(GREEN), - RGB::named(BLACK), - format!("You survived for {} turns.", crate::gamelog::get_event_count(EVENT::COUNT_TURN)) + RGB::named(rltk::GREEN), + RGB::named(rltk::BLACK), + format!("You survived for {} turns.", crate::gamelog::get_event_count("turns")), ); y += 2; - ctx.print_color(x, y, RGB::named(GREEN), RGB::named(BLACK), format!("And in the process, you")); + ctx.print_color(x, y, RGB::named(rltk::GREEN), RGB::named(rltk::BLACK), format!("And in the process, you")); y += 1; - if crate::gamelog::get_event_count(EVENT::COUNT_CHANGED_FLOOR) > 0 { + if crate::gamelog::get_event_count("descended") > 0 { ctx.print_color( x + 1, y, - RGB::named(WHITE), - RGB::named(BLACK), - format!( - "- changed floor {} times", - crate::gamelog::get_event_count(EVENT::COUNT_CHANGED_FLOOR) - ) + RGB::named(rltk::WHITE), + RGB::named(rltk::BLACK), + format!("- descended {} floor(s)", crate::gamelog::get_event_count("descended")), ); y += 1; } - if crate::gamelog::get_event_count(EVENT::COUNT_KICK) > 0 { + if crate::gamelog::get_event_count("kick_count") > 0 { ctx.print_color( x + 1, y, - RGB::named(WHITE), - RGB::named(BLACK), + RGB::named(rltk::WHITE), + RGB::named(rltk::BLACK), format!( "- kicked {} time(s), breaking {} object(s)", - crate::gamelog::get_event_count(EVENT::COUNT_KICK), - crate::gamelog::get_event_count(EVENT::COUNT_BROKE_DOOR) - ) + crate::gamelog::get_event_count("kick_count"), + crate::gamelog::get_event_count("broken_doors") + ), ); y += 1; } - if crate::gamelog::get_event_count(EVENT::COUNT_KILLED) > 0 { + if crate::gamelog::get_event_count("death_count") > 0 { ctx.print_color( x + 1, y, - RGB::named(WHITE), - RGB::named(BLACK), - format!( - "- slew {} other creature(s)", - crate::gamelog::get_event_count(EVENT::COUNT_KILLED) - ) + RGB::named(rltk::WHITE), + RGB::named(rltk::BLACK), + format!("- slew {} other creature(s)", crate::gamelog::get_event_count("death_count")), ); y += 1; } - if crate::gamelog::get_event_count(EVENT::COUNT_LOOKED_FOR_HELP) > 0 { + if crate::gamelog::get_event_count("looked_for_help") > 0 { ctx.print_color( x + 1, y, - RGB::named(WHITE), - RGB::named(BLACK), - format!( - "- forgot the controls {} time(s)", - crate::gamelog::get_event_count(EVENT::COUNT_LOOKED_FOR_HELP) - ) + RGB::named(rltk::WHITE), + RGB::named(rltk::BLACK), + format!("- forgot the controls {} time(s)", crate::gamelog::get_event_count("looked_for_help")), ); } match ctx.key { None => YesNoResult::NoSelection, - Some(key) => - match key { - VirtualKeyCode::N => YesNoResult::No, - VirtualKeyCode::Y => YesNoResult::Yes, - _ => YesNoResult::NoSelection, - } + Some(key) => match key { + VirtualKeyCode::Escape => YesNoResult::Yes, + _ => YesNoResult::NoSelection, + }, } } - -pub fn with_article(name: String) -> String { - // If first letter is a capital - if name.chars().nth(0).unwrap().is_uppercase() { - return format!("{}", name); - } - // a/an - let vowels = ['a', 'e', 'i', 'o', 'u']; - if vowels.contains(&name.chars().nth(0).unwrap()) { - return format!("an {}", name); - } - format!("a {}", name) -} - -pub fn unique( - entity: Entity, - names: &ReadStorage, - obfuscated_names: &ReadStorage, - renderables: &ReadStorage, - beatitudes: &ReadStorage, - magic_items: &ReadStorage, - charges: Option<&ReadStorage>, - dm: &MasterDungeonMap -) -> UniqueInventoryItem { - let item_colour = item_colour(entity, beatitudes); - let (singular, plural) = obfuscate_name( - entity, - names, - magic_items, - obfuscated_names, - beatitudes, - dm, - charges - ); - let (renderables, glyph) = if let Some(renderable) = renderables.get(entity) { - ( - ( - (renderable.fg.r * 255.0) as u8, - (renderable.fg.g * 255.0) as u8, - (renderable.fg.b * 255.0) as u8, - ), - renderable.glyph, - ) - } else { - unreachable!("Item has no renderable component.") - }; - let name = if let Some(name) = names.get(entity) { - name - } else { - unreachable!("Item has no name component.") - }; - let beatitude_status = if let Some(beatitude) = beatitudes.get(entity) { - match beatitude.buc { - BUC::Blessed => 1, - BUC::Uncursed => 2, - BUC::Cursed => 3, - } - } else { - 0 - }; - UniqueInventoryItem { - display_name: DisplayName { singular: singular.clone(), plural }, - rgb: item_colour, - renderables, - glyph, - beatitude_status, - name: name.name.clone(), - } -} - -pub fn unique_ecs(ecs: &World, entity: Entity) -> UniqueInventoryItem { - return unique( - entity, - &ecs.read_storage::(), - &ecs.read_storage::(), - &ecs.read_storage::(), - &ecs.read_storage::(), - &ecs.read_storage::(), - Some(&ecs.read_storage::()), - &ecs.fetch::() - ); -} diff --git a/src/gui/remove_curse_menu.rs b/src/gui/remove_curse_menu.rs deleted file mode 100644 index 68cea65..0000000 --- a/src/gui/remove_curse_menu.rs +++ /dev/null @@ -1,144 +0,0 @@ -use super::{ - get_max_inventory_width, - item_colour_ecs, - obfuscate_name_ecs, - print_options, - unique_ecs, - check_key, - letter_to_option, - ItemMenuResult, - InventorySlot, -}; -use crate::{ - gamelog, - Beatitude, - Entity, - Equipped, - InBackpack, - Item, - Name, - Renderable, - states::state::*, - BUC, - Key, -}; -use bracket_lib::prelude::*; -use specs::prelude::*; -use std::collections::HashMap; - -/// Handles the Remove Curse menu. -pub fn remove_curse(gs: &mut State, ctx: &mut BTerm) -> (ItemMenuResult, Option) { - let player_entity = gs.ecs.fetch::(); - let equipped = gs.ecs.read_storage::(); - let backpack = gs.ecs.read_storage::(); - let entities = gs.ecs.entities(); - let items = gs.ecs.read_storage::(); - let beatitudes = gs.ecs.read_storage::(); - let names = gs.ecs.read_storage::(); - let renderables = gs.ecs.read_storage::(); - let keys = gs.ecs.read_storage::(); - - let build_cursed_iterator = || { - (&entities, &items, &beatitudes, &renderables, &names, &keys) - .join() - .filter(|(item_entity, _i, b, _r, _n, _k)| { - // Set all items to FALSE initially. - let mut keep = false; - // If found in the player's backpack, set to TRUE - if let Some(bp) = backpack.get(*item_entity) { - if bp.owner == *player_entity { - keep = true; - } - } - // If found in the player's equipslot, set to TRUE - if let Some(equip) = equipped.get(*item_entity) { - if equip.owner == *player_entity { - keep = true; - } - } - // If it's not OUR item, RETURN FALSE. - if !keep { - return false; - } - // If it's identified as noncursed, RETURN FALSE. - if b.known && b.buc != BUC::Cursed { - return false; - } - // Otherwise, return: returns any items that are unidentified, - // or identified as being cursed. - return true; - }) - }; - - // Build list of items to display - let count = build_cursed_iterator().count(); - // If no items, return nothing, wasting the scroll. - if count == 0 { - gamelog::Logger::new().append("You've got nothing to decurse! What a waste.").log(); - return (ItemMenuResult::Cancel, None); - } - // If only one item, return it. - if count == 1 { - let item = build_cursed_iterator().nth(0).unwrap().0; - gamelog::Logger - ::new() - .append("You decurse the") - .colour(item_colour_ecs(&gs.ecs, item)) - .append_n(obfuscate_name_ecs(&gs.ecs, item).0) - .colour(WHITE) - .append("!") - .log(); - return (ItemMenuResult::Selected, Some(item)); - } - let mut player_inventory: super::PlayerInventory = HashMap::new(); - for (entity, _i, _b, _r, _n, key) in build_cursed_iterator() { - let unique_item = unique_ecs(&gs.ecs, entity); - player_inventory - .entry(unique_item) - .and_modify(|slot| { - slot.count += 1; - }) - .or_insert(InventorySlot { - item: entity, - count: 1, - idx: key.idx, - }); - } - // Get display args - let width = get_max_inventory_width(&player_inventory); - let (_, _, _, _, x_offset, y_offset) = crate::camera::get_screen_bounds(&gs.ecs, ctx); - let (x, y) = (x_offset + 1, y_offset + 3); - // Draw menu - ctx.print_color( - 1 + x_offset, - 1 + y_offset, - RGB::named(WHITE), - RGB::named(BLACK), - "Decurse which item? [aA-zZ][Esc.]" - ); - ctx.draw_box(x, y, width + 2, count + 1, RGB::named(WHITE), RGB::named(BLACK)); - print_options(&gs.ecs, &player_inventory, x + 1, y + 1, ctx); - // Input - match ctx.key { - None => (ItemMenuResult::NoResponse, None), - Some(key) => - match key { - VirtualKeyCode::Escape => (ItemMenuResult::Cancel, None), - _ => { - let selection = letter_to_option::letter_to_option(key, ctx.shift); - if selection != -1 && check_key(selection as usize) { - // Get the first entity with a Key {} component that has an idx matching "selection". - let entities = gs.ecs.entities(); - let keyed_items = gs.ecs.read_storage::(); - let backpack = gs.ecs.read_storage::(); - for (e, key, _b) in (&entities, &keyed_items, &backpack).join() { - if key.idx == (selection as usize) { - return (ItemMenuResult::Selected, Some(e)); - } - } - } - (ItemMenuResult::NoResponse, None) - } - } - } -} diff --git a/src/gui/tooltip.rs b/src/gui/tooltip.rs index 94c3f97..6304e63 100644 --- a/src/gui/tooltip.rs +++ b/src/gui/tooltip.rs @@ -1,18 +1,5 @@ -use super::{ - camera::get_screen_bounds, - Attributes, - Hidden, - Map, - Name, - Pools, - Position, - Renderable, - World, - RGB, -}; -use crate::TileType; -use crate::data::ids::*; -use bracket_lib::prelude::*; +use super::{camera::get_screen_bounds, Attributes, Hidden, Map, Pools, Position, Renderable, Rltk, World, RGB}; +use rltk::prelude::*; use specs::prelude::*; struct Tooltip { @@ -39,31 +26,23 @@ impl Tooltip { max = s.0.len(); } } - return (max as i32) + 2i32; + return max as i32 + 2i32; } fn height(&self) -> i32 { - return (self.lines.len() as i32) + 2i32; + return self.lines.len() as i32 + 2i32; } - fn render(&self, ctx: &mut BTerm, x: i32, y: i32) { - ctx.draw_box( - x, - y, - self.width() - 1, - self.height() - 1, - RGB::named(WHITE), - RGB::named(BLACK) - ); + fn render(&self, ctx: &mut Rltk, x: i32, y: i32) { + ctx.draw_box(x, y, self.width() - 1, self.height() - 1, RGB::named(WHITE), RGB::named(BLACK)); for (i, s) in self.lines.iter().enumerate() { - ctx.print_color(x + 1, y + (i as i32) + 1, s.1, RGB::named(BLACK), &s.0); + ctx.print_color(x + 1, y + i as i32 + 1, s.1, RGB::named(BLACK), &s.0); } } } #[rustfmt::skip] -pub fn draw_tooltips(ecs: &World, ctx: &mut BTerm, xy: Option<(i32, i32)>) { +pub fn draw_tooltips(ecs: &World, ctx: &mut Rltk) { let (min_x, _max_x, min_y, _max_y, x_offset, y_offset) = get_screen_bounds(ecs, ctx); let map = ecs.fetch::(); - let names = ecs.read_storage::(); let positions = ecs.read_storage::(); let renderables = ecs.read_storage::(); let hidden = ecs.read_storage::(); @@ -72,7 +51,7 @@ pub fn draw_tooltips(ecs: &World, ctx: &mut BTerm, xy: Option<(i32, i32)>) { let entities = ecs.entities(); let player_entity = ecs.fetch::(); - let mouse_pos = if xy.is_none() { ctx.mouse_pos() } else { xy.unwrap() }; + let mouse_pos = ctx.mouse_pos(); let mut mouse_pos_adjusted = mouse_pos; mouse_pos_adjusted.0 += min_x - x_offset; mouse_pos_adjusted.1 += min_y - y_offset; @@ -90,33 +69,10 @@ pub fn draw_tooltips(ecs: &World, ctx: &mut BTerm, xy: Option<(i32, i32)>) { } let mut tooltips: Vec = Vec::new(); - - match map.tiles[map.xy_idx(mouse_pos_adjusted.0, mouse_pos_adjusted.1)] { - TileType::ToLocal(n) => { - let name = get_local_desc(n); - let mut tip = Tooltip::new(); - tip.add(format!("You see {}.", name), get_local_col(n)); - tooltips.push(tip); - } - TileType::ToOvermap(n) => { - let name = get_local_desc(n); - let mut tip = Tooltip::new(); - tip.add(format!("You see an exit from {}.", name), get_local_col(n)); - tooltips.push(tip); - } - _ => {} - } - - for (entity, position, renderable, _name, _hidden) in (&entities, &positions, &renderables, &names, !&hidden).join() { + for (entity, position, renderable, _hidden) in (&entities, &positions, &renderables, !&hidden).join() { if position.x == mouse_pos_adjusted.0 && position.y == mouse_pos_adjusted.1 { let mut tip = Tooltip::new(); - tip.add(crate::gui::obfuscate_name_ecs(ecs, entity).0, renderable.fg); - let intrinsics = ecs.read_storage::(); - if let Some(intrinsics) = intrinsics.get(entity) { - if !intrinsics.list.is_empty() { - tip.add(intrinsics.describe(), RGB::named(WHITE)); - } - } + tip.add(crate::gui::get_item_display_name(ecs, entity).0, renderable.fg); // Attributes let attr = attributes.get(entity); if let Some(a) = attr { @@ -167,12 +123,12 @@ pub fn draw_tooltips(ecs: &World, ctx: &mut BTerm, xy: Option<(i32, i32)>) { if tooltips.is_empty() { return ; } - let white = RGB::named(WHITE); + let white = RGB::named(rltk::WHITE); let arrow; let arrow_x; let arrow_y = mouse_pos.1; - if mouse_pos.0 > 35 { + if mouse_pos.0 < 35 { // Render to the left arrow = to_cp437('→'); arrow_x = mouse_pos.0 - 1; @@ -181,7 +137,7 @@ pub fn draw_tooltips(ecs: &World, ctx: &mut BTerm, xy: Option<(i32, i32)>) { arrow = to_cp437('←'); arrow_x = mouse_pos.0 + 1; } - ctx.set(arrow_x, arrow_y, white, RGB::named(BLACK), arrow); + ctx.set(arrow_x, arrow_y, white, RGB::named(rltk::BLACK), arrow); let mut total_height = 0; for t in tooltips.iter() { @@ -194,7 +150,7 @@ pub fn draw_tooltips(ecs: &World, ctx: &mut BTerm, xy: Option<(i32, i32)>) { } for t in tooltips.iter() { - let x = if mouse_pos.0 > 35 { + let x = if mouse_pos.0 < 35 { mouse_pos.0 - (1 + t.width()) } else { mouse_pos.0 + (1 + 1) diff --git a/src/hunger_system.rs b/src/hunger_system.rs index 7a1b0fd..fbf55c9 100644 --- a/src/hunger_system.rs +++ b/src/hunger_system.rs @@ -1,158 +1,60 @@ -use super::{ - effects::{ add_effect, EffectType, Targets }, - gamelog, - Clock, - HungerClock, - HungerState, - TakingTurn, - DamageType, - Intrinsics, -}; -use bracket_lib::prelude::*; +use super::{gamelog, HungerClock, HungerState, SufferDamage, LOG_TICKS}; use specs::prelude::*; -use crate::config::CONFIG; -/// HungerSystem is in charge of ticking down the hunger clock for entities with a hunger clock, -/// every time the turn clock ticks. pub struct HungerSystem {} -const MAX_SATIATION: i32 = 2000; -const HUNGER_BREAKPOINTS: [(i32, HungerState); 5] = [ - (1000, HungerState::Satiated), - (600, HungerState::Normal), - (400, HungerState::Hungry), - (200, HungerState::Weak), - (0, HungerState::Fainting), -]; -const BASE_CLOCK_DECREMENT_PER_TURN: i32 = 1; - -pub fn get_hunger_state(duration: i32) -> HungerState { - for (threshold, state) in HUNGER_BREAKPOINTS.iter() { - if duration > *threshold { - return *state; - } - } - return HungerState::Starving; -} - -pub fn get_hunger_colour(state: HungerState) -> (u8, u8, u8) { - match state { - HungerState::Satiated => GREEN, - HungerState::Normal => WHITE, - HungerState::Hungry => BROWN1, - HungerState::Weak => ORANGE, - HungerState::Fainting => RED3, - HungerState::Starving => RED, - } -} - impl<'a> System<'a> for HungerSystem { #[allow(clippy::type_complexity)] - type SystemData = ( - Entities<'a>, - WriteStorage<'a, HungerClock>, - ReadExpect<'a, Entity>, - ReadStorage<'a, Clock>, - ReadStorage<'a, TakingTurn>, - ReadStorage<'a, Intrinsics>, - ); + type SystemData = + (Entities<'a>, WriteStorage<'a, HungerClock>, ReadExpect<'a, Entity>, WriteStorage<'a, SufferDamage>); fn run(&mut self, data: Self::SystemData) { - let (entities, mut hunger_clock, player_entity, turn_clock, turns, intrinsics) = data; + let (entities, mut hunger_clock, player_entity, mut inflict_damage) = data; - // If the turn clock isn't taking a turn this tick, don't bother ticking hunger. - let mut ticked = false; - for (_e, _c, _t) in (&entities, &turn_clock, &turns).join() { - ticked = true; - break; - } - if !ticked { - return; - } - // Otherwise, tick down the hunger clock for all entities with one. - for (entity, mut hunger_clock) in (&entities, &mut hunger_clock).join() { - if hunger_clock.duration >= MAX_SATIATION { - hunger_clock.duration = MAX_SATIATION; - } else { - let mut modifier = 0; - let intrinsic_regen = if let Some(i) = intrinsics.get(entity) { - i.list.contains(&crate::Intrinsic::Regeneration) - } else { - false - }; - if intrinsic_regen { - modifier += 1; + for (entity, mut clock) in (&entities, &mut hunger_clock).join() { + if LOG_TICKS && entity == *player_entity { + rltk::console::log(format!("HUNGER SYSTEM: Ticked for player entity. [clock: {}]", clock.duration)); + } + clock.duration -= 1; + if clock.duration > 0 { + return; + } + + match clock.state { + HungerState::Satiated => { + clock.state = HungerState::Normal; + clock.duration = 1200; + if entity == *player_entity { + gamelog::Logger::new().append("You are no longer satiated.").log(); + } + } + HungerState::Normal => { + clock.state = HungerState::Hungry; + clock.duration = 400; + if entity == *player_entity { + gamelog::Logger::new().colour(rltk::RED).append("You feel hungry.").log(); + } + } + HungerState::Hungry => { + clock.state = HungerState::Weak; + clock.duration = 200; + if entity == *player_entity { + gamelog::Logger::new().colour(rltk::RED).append("You feel weak with hunger.").log(); + } + } + HungerState::Weak => { + clock.state = HungerState::Fainting; + clock.duration = 200; + if entity == *player_entity { + gamelog::Logger::new().colour(rltk::RED).append("You feel hungry enough to faint.").log(); + } + } + HungerState::Fainting => { + SufferDamage::new_damage(&mut inflict_damage, entity, 1, false); + if entity == *player_entity { + gamelog::Logger::new().colour(rltk::RED).append("You can't go on without food...").log(); + } } - hunger_clock.duration -= BASE_CLOCK_DECREMENT_PER_TURN + modifier; - } - let initial_state = hunger_clock.state; - hunger_clock.state = get_hunger_state(hunger_clock.duration); - if hunger_clock.state == HungerState::Starving { - add_effect( - None, - EffectType::Damage { amount: 1, damage_type: DamageType::Forced }, - Targets::Entity { target: entity } - ); - } - if CONFIG.logging.log_ticks && entity == *player_entity { - console::log( - format!( - "HUNGER SYSTEM: Ticked for player entity. [clock: {}]", - hunger_clock.duration - ) - ); - } - if hunger_clock.state == initial_state { - continue; - } - if entity != *player_entity { - continue; - } - // Things which only happen to the player. - match hunger_clock.state { - HungerState::Satiated => - gamelog::Logger - ::new() - .append("You feel") - .colour(get_hunger_colour(hunger_clock.state)) - .append_n("satiated") - .colour(WHITE) - .period() - .log(), - HungerState::Normal => {} - HungerState::Hungry => - gamelog::Logger - ::new() - .append("You feel") - .colour(get_hunger_colour(hunger_clock.state)) - .append_n("hungry") - .colour(WHITE) - .period() - .log(), - HungerState::Weak => - gamelog::Logger - ::new() - .append("You feel") - .colour(get_hunger_colour(hunger_clock.state)) - .append_n("weak with hunger") - .colour(WHITE) - .period() - .log(), - HungerState::Fainting => - gamelog::Logger - ::new() - .append("You feel") - .colour(get_hunger_colour(hunger_clock.state)) - .append_n("hungry enough to faint") - .colour(WHITE) - .period() - .log(), - _ => - gamelog::Logger - ::new() - .colour(get_hunger_colour(hunger_clock.state)) - .append_n("You can't go on without food!") - .log(), } } } diff --git a/src/inventory/collection_system.rs b/src/inventory/collection_system.rs deleted file mode 100644 index 70fb25c..0000000 --- a/src/inventory/collection_system.rs +++ /dev/null @@ -1,94 +0,0 @@ -use crate::{ - gamelog, - gui::obfuscate_name, - gui::item_colour, - Beatitude, - Charges, - EquipmentChanged, - InBackpack, - MagicItem, - MasterDungeonMap, - Name, - ObfuscatedName, - Position, - WantsToPickupItem, - WantsToAssignKey, -}; -use specs::prelude::*; -use crate::data::messages; -use bracket_lib::prelude::*; - -pub struct ItemCollectionSystem {} - -impl<'a> System<'a> for ItemCollectionSystem { - #[allow(clippy::type_complexity)] - type SystemData = ( - ReadExpect<'a, Entity>, - WriteStorage<'a, WantsToPickupItem>, - WriteStorage<'a, Position>, - ReadStorage<'a, Name>, - WriteStorage<'a, InBackpack>, - WriteStorage<'a, EquipmentChanged>, - ReadStorage<'a, MagicItem>, - ReadStorage<'a, ObfuscatedName>, - ReadStorage<'a, Beatitude>, - ReadExpect<'a, MasterDungeonMap>, - ReadStorage<'a, Charges>, - ReadStorage<'a, WantsToAssignKey>, - ); - - fn run(&mut self, data: Self::SystemData) { - let ( - player_entity, - mut wants_pickup, - mut positions, - names, - mut backpack, - mut equipment_changed, - magic_items, - obfuscated_names, - beatitudes, - dm, - wands, - wants_key, - ) = data; - let mut to_remove: Vec = Vec::new(); - // For every item that wants to be picked up that *isn't* waiting on a key assignment. - for (pickup, _key) in (&wants_pickup, !&wants_key).join() { - if pickup.collected_by == *player_entity { - gamelog::Logger - ::new() - .append(messages::YOU_PICKUP_ITEM) - .colour(item_colour(pickup.item, &beatitudes)) - .append_n( - format!( - "{}", - obfuscate_name( - pickup.item, - &names, - &magic_items, - &obfuscated_names, - &beatitudes, - &dm, - Some(&wands) - ).0 - ) - ) - .colour(WHITE) - .period() - .log(); - } - positions.remove(pickup.item); - backpack - .insert(pickup.item, InBackpack { owner: pickup.collected_by }) - .expect("Unable to pickup item"); - equipment_changed - .insert(pickup.collected_by, EquipmentChanged {}) - .expect("Unable to insert EquipmentChanged"); - to_remove.push(pickup.collected_by); - } - for item in to_remove.iter() { - wants_pickup.remove(*item); - } - } -} diff --git a/src/inventory/drop_system.rs b/src/inventory/drop_system.rs deleted file mode 100644 index af2d8a2..0000000 --- a/src/inventory/drop_system.rs +++ /dev/null @@ -1,103 +0,0 @@ -use crate::{ - gamelog, - gui::obfuscate_name, - gui::item_colour, - Beatitude, - Charges, - EquipmentChanged, - InBackpack, - MagicItem, - MasterDungeonMap, - Name, - ObfuscatedName, - Position, - WantsToDropItem, - WantsToRemoveKey, -}; -use specs::prelude::*; -use crate::data::messages; -use bracket_lib::prelude::*; - -pub struct ItemDropSystem {} - -impl<'a> System<'a> for ItemDropSystem { - #[allow(clippy::type_complexity)] - type SystemData = ( - ReadExpect<'a, Entity>, - Entities<'a>, - WriteStorage<'a, WantsToDropItem>, - ReadStorage<'a, Name>, - WriteStorage<'a, Position>, - WriteStorage<'a, InBackpack>, - WriteStorage<'a, EquipmentChanged>, - ReadStorage<'a, MagicItem>, - ReadStorage<'a, Beatitude>, - ReadStorage<'a, ObfuscatedName>, - ReadExpect<'a, MasterDungeonMap>, - ReadStorage<'a, Charges>, - WriteStorage<'a, WantsToRemoveKey>, - ); - - fn run(&mut self, data: Self::SystemData) { - let ( - player_entity, - entities, - mut wants_drop, - names, - mut positions, - mut backpack, - mut equipment_changed, - magic_items, - beatitudes, - obfuscated_names, - dm, - wands, - mut keys, - ) = data; - - for (entity, to_drop) in (&entities, &wants_drop).join() { - equipment_changed - .insert(entity, EquipmentChanged {}) - .expect("Unable to insert EquipmentChanged."); - let mut dropper_pos: Position = Position { x: 0, y: 0 }; - { - let dropped_pos = positions.get(entity).unwrap(); - dropper_pos.x = dropped_pos.x; - dropper_pos.y = dropped_pos.y; - } - positions - .insert(to_drop.item, Position { x: dropper_pos.x, y: dropper_pos.y }) - .expect("Failed to insert position."); - backpack.remove(to_drop.item); - - if entity == *player_entity { - keys.insert(to_drop.item, WantsToRemoveKey {}).expect( - "Unable to insert WantsToRemoveKey" - ); - gamelog::Logger - ::new() - .append(messages::YOU_DROP_ITEM) - .colour(item_colour(to_drop.item, &beatitudes)) - .append_n( - format!( - "{}", - obfuscate_name( - to_drop.item, - &names, - &magic_items, - &obfuscated_names, - &beatitudes, - &dm, - Some(&wands) - ).0 - ) - ) - .colour(WHITE) - .period() - .log(); - } - } - - wants_drop.clear(); - } -} diff --git a/src/inventory/equip_system.rs b/src/inventory/equip_system.rs deleted file mode 100644 index e21db20..0000000 --- a/src/inventory/equip_system.rs +++ /dev/null @@ -1,172 +0,0 @@ -use crate::{ - gamelog, - gui::{ item_colour, obfuscate_name }, - Beatitude, - EquipmentChanged, - Equippable, - Equipped, - IdentifiedBeatitude, - IdentifiedItem, - InBackpack, - MagicItem, - MasterDungeonMap, - Name, - ObfuscatedName, - WantsToUseItem, - BUC, -}; -use specs::prelude::*; -use crate::data::messages; -use bracket_lib::prelude::*; - -pub struct ItemEquipSystem {} - -impl<'a> System<'a> for ItemEquipSystem { - #[allow(clippy::type_complexity)] - type SystemData = ( - ReadExpect<'a, Entity>, - Entities<'a>, - WriteStorage<'a, WantsToUseItem>, - WriteStorage<'a, IdentifiedItem>, - ReadStorage<'a, Name>, - ReadStorage<'a, Equippable>, - WriteStorage<'a, Equipped>, - WriteStorage<'a, InBackpack>, - WriteStorage<'a, EquipmentChanged>, - ReadStorage<'a, MagicItem>, - ReadStorage<'a, ObfuscatedName>, - ReadStorage<'a, Beatitude>, - WriteStorage<'a, IdentifiedBeatitude>, - ReadExpect<'a, MasterDungeonMap>, - ); - - #[allow(clippy::cognitive_complexity)] - fn run(&mut self, data: Self::SystemData) { - let ( - player_entity, - entities, - mut wants_to_use_item, - mut identified_items, - names, - equippable, - mut equipped, - mut backpack, - mut dirty, - magic_items, - obfuscated_names, - beatitudes, - mut identified_beatitude, - dm, - ) = data; - let mut remove: Vec = Vec::new(); - // For every item with a target, if the item is equippable, find the correct slot. - for (target, wants_to_use_item) in (&entities, &wants_to_use_item).join() { - if let Some(can_equip) = equippable.get(wants_to_use_item.item) { - let target_slot = can_equip.slot; - let mut logger = gamelog::Logger::new(); - // Remove any items target has in item's slot - let mut can_equip = true; - let mut to_unequip: Vec = Vec::new(); - for (item_entity, already_equipped, _name) in ( - &entities, - &equipped, - &names, - ).join() { - if already_equipped.owner == target && already_equipped.slot == target_slot { - if let Some(beatitude) = beatitudes.get(item_entity) { - if beatitude.buc == BUC::Cursed { - can_equip = false; - logger = logger - .append(messages::YOU_REMOVE_ITEM_CURSED) - .colour(item_colour(item_entity, &beatitudes)) - .append_n( - obfuscate_name( - item_entity, - &names, - &magic_items, - &obfuscated_names, - &beatitudes, - &dm, - None - ).0 - ) - .colour(WHITE) - .append("!"); - identified_beatitude - .insert(item_entity, IdentifiedBeatitude {}) - .expect("Unable to push"); - } - } - to_unequip.push(item_entity); - } - } - if !can_equip { - logger.log(); - continue; - } - for item in to_unequip.iter() { - equipped.remove(*item); - backpack - .insert(*item, InBackpack { owner: target }) - .expect("Unable to insert backpack"); - if target == *player_entity { - logger = logger - .append(messages::YOU_REMOVE_ITEM) - .colour(item_colour(*item, &beatitudes)) - .append_n( - obfuscate_name( - *item, - &names, - &magic_items, - &obfuscated_names, - &beatitudes, - &dm, - None - ).0 - ) - .colour(WHITE) - .period(); - } - } - - // Wield the item - equipped - .insert(wants_to_use_item.item, Equipped { owner: target, slot: target_slot }) - .expect("Unable to insert equipped component"); - backpack.remove(wants_to_use_item.item); - if target == *player_entity { - logger = logger - .append(messages::YOU_EQUIP_ITEM) - .colour(item_colour(wants_to_use_item.item, &beatitudes)) - .append_n( - obfuscate_name( - wants_to_use_item.item, - &names, - &magic_items, - &obfuscated_names, - &beatitudes, - &dm, - None - ).0 - ) - .colour(WHITE) - .period(); - logger.log(); - identified_items - .insert(target, IdentifiedItem { - name: names.get(wants_to_use_item.item).unwrap().name.clone(), - }) - .expect("Unable to insert IdentifiedItem"); - identified_beatitude - .insert(wants_to_use_item.item, IdentifiedBeatitude {}) - .expect("Unable to push"); - } - remove.push(target); - } - } - remove.iter().for_each(|e| { - dirty.insert(*e, EquipmentChanged {}).expect("Unabble to insert EquipmentChanged"); - wants_to_use_item.remove(*e).expect("Unable to remove *e"); - }) - } -} diff --git a/src/inventory/identification_system.rs b/src/inventory/identification_system.rs deleted file mode 100644 index ce4ad23..0000000 --- a/src/inventory/identification_system.rs +++ /dev/null @@ -1,66 +0,0 @@ -use crate::{ - Beatitude, - IdentifiedBeatitude, - IdentifiedItem, - Item, - MasterDungeonMap, - Name, - ObfuscatedName, - Player, -}; -use specs::prelude::*; -use crate::data::events::*; -use crate::gamelog; - -pub struct ItemIdentificationSystem {} - -impl<'a> System<'a> for ItemIdentificationSystem { - #[allow(clippy::type_complexity)] - type SystemData = ( - ReadStorage<'a, Player>, - WriteStorage<'a, IdentifiedItem>, - WriteStorage<'a, Beatitude>, - WriteStorage<'a, IdentifiedBeatitude>, - WriteExpect<'a, MasterDungeonMap>, - ReadStorage<'a, Item>, - ReadStorage<'a, Name>, - WriteStorage<'a, ObfuscatedName>, - Entities<'a>, - ); - - fn run(&mut self, data: Self::SystemData) { - let ( - player, - mut identified, - mut beatitudes, - mut identified_beatitudes, - mut dm, - items, - names, - mut obfuscated_names, - entities, - ) = data; - for (_p, id) in (&player, &identified).join() { - let tag = crate::raws::get_id_from_name(id.name.clone()); - if !dm.identified_items.contains(&id.name) && crate::raws::is_tag_magic(&tag) { - if gamelog::get_event_count(EVENT::COUNT_TURN) != 1 { - gamelog::record_event(EVENT::Identified(id.name.clone())); - } - dm.identified_items.insert(id.name.clone()); - for (entity, _item, name) in (&entities, &items, &names).join() { - if name.name == id.name { - obfuscated_names.remove(entity); - } - } - } - } - for (e, _id) in (&entities, &identified_beatitudes).join() { - if let Some(beatitude) = beatitudes.get_mut(e) { - beatitude.known = true; - } - } - // Clean up - identified.clear(); - identified_beatitudes.clear(); - } -} diff --git a/src/inventory/keyhandling.rs b/src/inventory/keyhandling.rs deleted file mode 100644 index 7194a18..0000000 --- a/src/inventory/keyhandling.rs +++ /dev/null @@ -1,153 +0,0 @@ -use crate::{ - gamelog, - gui::unique, - Beatitude, - Charges, - MagicItem, - MasterDungeonMap, - Name, - ObfuscatedName, - Stackable, - Renderable, - WantsToAssignKey, - WantsToRemoveKey, - Key, -}; -use specs::prelude::*; -use crate::data::messages; -use bracket_lib::prelude::*; -use crate::invkeys::*; - -pub struct KeyHandling {} - -const DEBUG_KEYHANDLING: bool = true; - -impl<'a> System<'a> for KeyHandling { - #[allow(clippy::type_complexity)] - type SystemData = ( - Entities<'a>, - WriteStorage<'a, WantsToAssignKey>, - WriteStorage<'a, WantsToRemoveKey>, - WriteStorage<'a, Key>, - ReadStorage<'a, Stackable>, - ReadStorage<'a, Name>, - ReadStorage<'a, ObfuscatedName>, - ReadStorage<'a, Renderable>, - ReadStorage<'a, Beatitude>, - ReadStorage<'a, MagicItem>, - ReadStorage<'a, Charges>, - ReadExpect<'a, MasterDungeonMap>, - ); - - fn run(&mut self, data: Self::SystemData) { - let ( - entities, - mut wants_keys, - mut wants_removekey, - mut keys, - stackable, - names, - obfuscated_names, - renderables, - beatitudes, - magic_items, - wands, - dm, - ) = data; - - // For every entity that wants to be picked up, that still needs a key assigned. - for (e, _wants_key) in (&entities, &wants_keys).join() { - if DEBUG_KEYHANDLING { - console::log(&format!("KEYHANDLING: Assigning key to {:?}", e)); - } - let (stacks, mut handled, unique) = ( - if let Some(_) = stackable.get(e) { true } else { false }, - false, - unique( - e, - &names, - &obfuscated_names, - &renderables, - &beatitudes, - &magic_items, - Some(&wands), - &dm - ), - ); - if stacks { - console::log(&format!("KEYHANDLING: Item is stackable.")); - let maybe_key = item_exists(&unique); - if maybe_key.is_some() { - console::log(&format!("KEYHANDLING: Existing stack found for this item.")); - let key = maybe_key.unwrap(); - keys.insert(e, Key { idx: key }).expect("Unable to insert Key."); - console::log(&format!("KEYHANDLING: Assigned key idx {} to item.", key)); - handled = true; - } - } - if !handled { - console::log( - &format!("KEYHANDLING: Item is not stackable, or no existing stack found.") - ); - if let Some(idx) = assign_next_available() { - console::log( - &format!("KEYHANDLING: Assigned next available index {} to item.", idx) - ); - keys.insert(e, Key { idx }).expect("Unable to insert Key."); - register_stackable(stacks, unique, idx); - } else { - console::log(&format!("KEYHANDLING: No more keys available.")); - gamelog::Logger - ::new() - .append(messages::NO_MORE_KEYS) - .colour(WHITE) - .period() - .log(); - } - } - } - for (e, _wants_key) in (&entities, &wants_removekey).join() { - let idx = keys.get(e).unwrap().idx; - if DEBUG_KEYHANDLING { - console::log(&format!("KEYHANDLING: Removing key from {:?}", e)); - } - // If the item is *not* stackable, then we can just remove the key and clear the index. - if let None = stackable.get(e) { - console::log( - &format!("KEYHANDLING: Item is not stackable, clearing index {}.", idx) - ); - clear_idx(idx); - keys.remove(e); - continue; - } - // If the item *is* stackable, then we need to check if there are any other items that - // share this key assignment, before clearing the index. - console::log( - &format!( - "KEYHANDLING: Item is stackable, checking if any other items share this key." - ) - ); - let mut sole_item_with_key = true; - for (entity, key) in (&entities, &keys).join() { - if entity != e && key.idx == idx { - console::log(&format!("KEYHANDLING: Another item shares index {}", idx)); - sole_item_with_key = false; - break; - } - } - // If no other items shared this key, free up the index. - if sole_item_with_key { - console::log( - &format!("KEYHANDLING: No other items found, clearing index {}.", idx) - ); - clear_idx(idx); - } - // Either way, remove the key component from this item, because we're dropping it. - console::log(&format!("KEYHANDLING: Removing key component from item.")); - keys.remove(e); - } - - wants_removekey.clear(); - wants_keys.clear(); - } -} diff --git a/src/inventory/mod.rs b/src/inventory/mod.rs deleted file mode 100644 index 76748e0..0000000 --- a/src/inventory/mod.rs +++ /dev/null @@ -1,17 +0,0 @@ -mod collection_system; -mod drop_system; -mod equip_system; -mod identification_system; -mod remove_system; -mod use_system; -mod keyhandling; - -pub use self::{ - collection_system::ItemCollectionSystem, - drop_system::ItemDropSystem, - equip_system::ItemEquipSystem, - identification_system::ItemIdentificationSystem, - remove_system::ItemRemoveSystem, - use_system::ItemUseSystem, - keyhandling::KeyHandling, -}; diff --git a/src/inventory/remove_system.rs b/src/inventory/remove_system.rs deleted file mode 100644 index 0a46bce..0000000 --- a/src/inventory/remove_system.rs +++ /dev/null @@ -1,109 +0,0 @@ -use crate::{ - gamelog, - gui::{ item_colour, obfuscate_name }, - Beatitude, - Equipped, - InBackpack, - MagicItem, - MasterDungeonMap, - Name, - ObfuscatedName, - WantsToRemoveItem, - BUC, -}; -use bracket_lib::prelude::*; -use specs::prelude::*; -use crate::data::messages; - -pub struct ItemRemoveSystem {} - -impl<'a> System<'a> for ItemRemoveSystem { - #[allow(clippy::type_complexity)] - type SystemData = ( - Entities<'a>, - ReadExpect<'a, Entity>, - ReadStorage<'a, Name>, - WriteStorage<'a, WantsToRemoveItem>, - WriteStorage<'a, Equipped>, - WriteStorage<'a, InBackpack>, - ReadStorage<'a, MagicItem>, - ReadStorage<'a, ObfuscatedName>, - ReadStorage<'a, Beatitude>, - ReadExpect<'a, MasterDungeonMap>, - ); - - fn run(&mut self, data: Self::SystemData) { - let ( - entities, - player_entity, - names, - mut wants_remove, - mut equipped, - mut backpack, - magic_items, - obfuscated_names, - beatitudes, - dm, - ) = data; - - for (entity, to_remove) in (&entities, &wants_remove).join() { - let mut can_remove = true; - if let Some(beatitude) = beatitudes.get(to_remove.item) { - // If cursed, can't remove! - if beatitude.buc == BUC::Cursed { - can_remove = false; - gamelog::Logger - ::new() - .append(messages::YOU_REMOVE_ITEM_CURSED) - .colour(item_colour(to_remove.item, &beatitudes)) - .append_n( - obfuscate_name( - to_remove.item, - &names, - &magic_items, - &obfuscated_names, - &beatitudes, - &dm, - None - ).0 - ) - .colour(WHITE) - .append("!") - .log(); - } - } - if !can_remove { - continue; - } - // If not cursed, remove it - equipped.remove(to_remove.item); - if let Some(_) = names.get(to_remove.item) { - if entity == *player_entity { - gamelog::Logger - ::new() - .append(messages::YOU_REMOVE_ITEM) - .colour(item_colour(to_remove.item, &beatitudes)) - .append_n( - obfuscate_name( - to_remove.item, - &names, - &magic_items, - &obfuscated_names, - &beatitudes, - &dm, - None - ).0 - ) - .colour(WHITE) - .period() - .log(); - } - } - backpack - .insert(to_remove.item, InBackpack { owner: entity }) - .expect("Unable to insert backpack"); - } - - wants_remove.clear(); - } -} diff --git a/src/inventory/use_system.rs b/src/inventory/use_system.rs deleted file mode 100644 index 1407d65..0000000 --- a/src/inventory/use_system.rs +++ /dev/null @@ -1,66 +0,0 @@ -use crate::{ - effects::{ add_effect, aoe_tiles, EffectType, Targets }, - EquipmentChanged, - IdentifiedItem, - IdentifiedBeatitude, - Map, - Name, - WantsToUseItem, - AOE, -}; -use specs::prelude::*; - -pub struct ItemUseSystem {} - -impl<'a> System<'a> for ItemUseSystem { - #[allow(clippy::type_complexity)] - type SystemData = ( - ReadExpect<'a, Entity>, - WriteExpect<'a, Map>, - Entities<'a>, - WriteStorage<'a, WantsToUseItem>, - ReadStorage<'a, Name>, - ReadStorage<'a, AOE>, - WriteStorage<'a, EquipmentChanged>, - WriteStorage<'a, IdentifiedItem>, - WriteStorage<'a, IdentifiedBeatitude>, - ); - - #[allow(clippy::cognitive_complexity)] - fn run(&mut self, data: Self::SystemData) { - let ( - player_entity, - map, - entities, - mut wants_use, - names, - aoe, - mut dirty, - mut identified_item, - mut identified_beatitude, - ) = data; - - for (entity, useitem) in (&entities, &wants_use).join() { - dirty.insert(entity, EquipmentChanged {}).expect("Unable to insert"); - // Identify - if entity == *player_entity { - identified_item - .insert(entity, IdentifiedItem { name: names.get(useitem.item).unwrap().name.clone() }) - .expect("Unable to insert"); - identified_beatitude.insert(useitem.item, IdentifiedBeatitude {}).expect("Unable to push"); - } - // Call the effects system - add_effect(Some(entity), EffectType::ItemUse { item: useitem.item }, match useitem.target { - None => Targets::Entity { target: *player_entity }, - Some(target) => { - if let Some(aoe) = aoe.get(useitem.item) { - Targets::TileList { targets: aoe_tiles(&*map, target, aoe.radius) } - } else { - Targets::Tile { target: map.xy_idx(target.x, target.y) } - } - } - }); - } - wants_use.clear(); - } -} diff --git a/src/inventory_system.rs b/src/inventory_system.rs new file mode 100644 index 0000000..adb555d --- /dev/null +++ b/src/inventory_system.rs @@ -0,0 +1,560 @@ +use super::{ + gamelog, gui::obfuscate_name, Confusion, Consumable, Cursed, Destructible, Digger, EquipmentChanged, Equippable, + Equipped, HungerClock, HungerState, IdentifiedItem, InBackpack, InflictsDamage, MagicItem, MagicMapper, Map, + MasterDungeonMap, Name, ObfuscatedName, ParticleBuilder, Point, Pools, Position, ProvidesHealing, + ProvidesNutrition, RandomNumberGenerator, RunState, SufferDamage, TileType, Viewshed, Wand, WantsToDropItem, + WantsToPickupItem, WantsToRemoveItem, WantsToUseItem, AOE, DEFAULT_PARTICLE_LIFETIME, LONG_PARTICLE_LIFETIME, +}; +use specs::prelude::*; + +pub struct ItemCollectionSystem {} + +impl<'a> System<'a> for ItemCollectionSystem { + #[allow(clippy::type_complexity)] + type SystemData = ( + ReadExpect<'a, Entity>, + WriteStorage<'a, WantsToPickupItem>, + WriteStorage<'a, Position>, + ReadStorage<'a, Name>, + WriteStorage<'a, InBackpack>, + WriteStorage<'a, EquipmentChanged>, + ReadStorage<'a, MagicItem>, + ReadStorage<'a, ObfuscatedName>, + ReadExpect<'a, MasterDungeonMap>, + ReadStorage<'a, Wand>, + ); + + fn run(&mut self, data: Self::SystemData) { + let ( + player_entity, + mut wants_pickup, + mut positions, + names, + mut backpack, + mut equipment_changed, + magic_items, + obfuscated_names, + dm, + wands, + ) = data; + + for pickup in wants_pickup.join() { + positions.remove(pickup.item); + backpack.insert(pickup.item, InBackpack { owner: pickup.collected_by }).expect("Unable to pickup item."); + equipment_changed + .insert(pickup.collected_by, EquipmentChanged {}) + .expect("Unable to insert EquipmentChanged."); + + if pickup.collected_by == *player_entity { + gamelog::Logger::new() + .append("You pick up the") + .item_name_n(format!( + "{}", + obfuscate_name(pickup.item, &names, &magic_items, &obfuscated_names, &dm, Some(&wands)).0 + )) + .period() + .log(); + } + } + + wants_pickup.clear(); + } +} + +// Grouping together components because of type complexity issues - SystemData was too large. +// This is a temporary solution that'll be fixed once inventory use is refactored into separate +// systems. +type EquipComponents<'a> = + (ReadStorage<'a, Equippable>, WriteStorage<'a, Equipped>, WriteStorage<'a, EquipmentChanged>); +type NameComponents<'a> = (ReadStorage<'a, Name>, WriteStorage<'a, IdentifiedItem>); + +pub struct ItemUseSystem {} +impl<'a> System<'a> for ItemUseSystem { + #[allow(clippy::type_complexity)] + type SystemData = ( + ReadExpect<'a, Entity>, + WriteExpect<'a, Map>, + WriteExpect<'a, RandomNumberGenerator>, + Entities<'a>, + WriteStorage<'a, WantsToUseItem>, + NameComponents<'a>, + WriteStorage<'a, Consumable>, + WriteStorage<'a, Wand>, + ReadStorage<'a, Destructible>, + ReadStorage<'a, Cursed>, + ReadStorage<'a, ProvidesHealing>, + ReadStorage<'a, ProvidesNutrition>, + WriteStorage<'a, HungerClock>, + WriteStorage<'a, Pools>, + WriteStorage<'a, SufferDamage>, + WriteExpect<'a, ParticleBuilder>, + ReadStorage<'a, Position>, + ReadStorage<'a, InflictsDamage>, + ReadStorage<'a, AOE>, + ReadStorage<'a, Digger>, + WriteStorage<'a, Confusion>, + ReadStorage<'a, MagicMapper>, + WriteExpect<'a, RunState>, + EquipComponents<'a>, + WriteStorage<'a, InBackpack>, + WriteStorage<'a, Viewshed>, + ); + + fn run(&mut self, data: Self::SystemData) { + let ( + player_entity, + mut map, + mut rng, + entities, + mut wants_to_use, + (names, mut identified_items), + mut consumables, + mut wands, + destructibles, + cursed_items, + provides_healing, + provides_nutrition, + mut hunger_clock, + mut combat_stats, + mut suffer_damage, + mut particle_builder, + positions, + inflicts_damage, + aoe, + digger, + mut confused, + magic_mapper, + mut runstate, + (equippable, mut equipped, mut equipment_changed), + mut backpack, + mut viewsheds, + ) = data; + + for (entity, wants_to_use) in (&entities, &wants_to_use).join() { + // Could probably limit this insert only to if something is consumed/equipped/etc., but this is + // safer and items aren't used nearly frequently enough for this to cause performance issues. + equipment_changed.insert(entity, EquipmentChanged {}).expect("Unable to insert EquipmentChanged."); + let mut verb = "use"; + let mut used_item = true; + let mut aoe_item = false; + + let mut logger = gamelog::Logger::new(); + + let is_cursed = cursed_items.get(wants_to_use.item); + let wand = wands.get_mut(wants_to_use.item); + if let Some(wand) = wand { + // If want has no uses, roll 1d121. On a 121, wrest the wand, then delete it. + if wand.uses == 0 { + if rng.roll_dice(1, 121) != 121 { + gamelog::Logger::new().append("The wand does nothing.").log(); + break; + } + logger = logger.colour(rltk::YELLOW).append("You wrest one last charge from the worn-out wand."); + consumables.insert(wants_to_use.item, Consumable {}).expect("Could not insert consumable"); + } + verb = "zap"; + wand.uses -= 1; + } + + let item_being_used = names.get(wants_to_use.item).unwrap(); + + let is_edible = provides_nutrition.get(wants_to_use.item); + if let Some(_) = is_edible { + verb = "eat"; + } + let item_equippable = equippable.get(wants_to_use.item); + if let Some(_) = item_equippable { + verb = "equip" + } + + logger = + logger.append(format!("You {} the", verb)).item_name_n(format!("{}", &item_being_used.name)).period(); + + // TARGETING + let mut targets: Vec = Vec::new(); + let mut target_idxs: Vec = Vec::new(); + match wants_to_use.target { + None => { + targets.push(*player_entity); + let pos = positions.get(*player_entity); + if let Some(pos) = pos { + target_idxs.push(map.xy_idx(pos.x, pos.y)); + } + } + Some(mut target) => { + let area_effect = aoe.get(wants_to_use.item); + match area_effect { + None => { + // Single target in a tile + let idx = map.xy_idx(target.x, target.y); + target_idxs.push(idx); + for mob in map.tile_content[idx].iter() { + targets.push(*mob); + } + } + Some(area_effect) => { + // If item with a targeted AOE is cursed, get the position + // of the player and set them to be the new target. + match is_cursed { + None => {} + Some(_) => { + let pos = positions.get(*player_entity); + if let Some(pos) = pos { + target = Point::new(pos.x, pos.y); + } + logger = logger + .append("The") + .item_name(&item_being_used.name) + .colour(rltk::WHITE) + .append("disobeys!"); + } + } + // AOE + aoe_item = true; + let mut blast_tiles = rltk::field_of_view(target, area_effect.radius, &*map); + blast_tiles.retain(|p| p.x > 0 && p.x < map.width - 1 && p.y > 0 && p.y < map.height - 1); + for tile_idx in blast_tiles.iter() { + let idx = map.xy_idx(tile_idx.x, tile_idx.y); + target_idxs.push(idx); + for mob in map.tile_content[idx].iter() { + targets.push(*mob); + } + particle_builder.request( + tile_idx.x, + tile_idx.y, + rltk::RGB::named(rltk::ORANGE), + rltk::RGB::named(rltk::BLACK), + rltk::to_cp437('░'), + LONG_PARTICLE_LIFETIME, + ); + } + } + } + } + } + + // EDIBLE + match is_edible { + None => {} + Some(_) => { + let target = targets[0]; + let hc = hunger_clock.get_mut(target); + if let Some(hc) = hc { + hc.state = HungerState::Satiated; + hc.duration = 200; + } + } + } + + // EQUIPMENT + match item_equippable { + None => {} + Some(can_equip) => { + let target_slot = can_equip.slot; + let target = targets[0]; + + // Remove any items target has in item's slot + let mut to_unequip: Vec = Vec::new(); + for (item_entity, already_equipped, _name) in (&entities, &equipped, &names).join() { + if already_equipped.owner == target && already_equipped.slot == target_slot { + to_unequip.push(item_entity); + } + } + for item in to_unequip.iter() { + equipped.remove(*item); + backpack.insert(*item, InBackpack { owner: target }).expect("Unable to insert backpack"); + if target == *player_entity { + if let Some(name) = names.get(*item) { + logger = logger.append("You remove your").item_name_n(&name.name).period(); + } + } + } + + // Wield the item + equipped + .insert(wants_to_use.item, Equipped { owner: target, slot: target_slot }) + .expect("Unable to insert equipped component"); + backpack.remove(wants_to_use.item); + } + } + + // HEALING ITEM + let item_heals = provides_healing.get(wants_to_use.item); + match item_heals { + None => {} + Some(heal) => { + for target in targets.iter() { + let stats = combat_stats.get_mut(*target); + if let Some(stats) = stats { + let roll = rng.roll_dice(heal.n_dice, heal.sides) + heal.modifier; + stats.hit_points.current = i32::min(stats.hit_points.max, stats.hit_points.current + roll); + if entity == *player_entity { + logger = logger.append("You recover some vigour."); + } + let pos = positions.get(entity); + if let Some(pos) = pos { + particle_builder.heal(pos.x, pos.y); + } + } + } + } + } + + let mut damage_logger = gamelog::Logger::new(); + let mut needs_damage_log = false; + + // DAMAGING ITEM + let item_damages = inflicts_damage.get(wants_to_use.item); + match item_damages { + None => {} + Some(damage) => { + let target_point = wants_to_use.target.unwrap(); + let damage_roll = rng.roll_dice(damage.n_dice, damage.sides) + damage.modifier; + if !aoe_item { + particle_builder.request_rainbow_star( + target_point.x, + target_point.y, + rltk::to_cp437('*'), + DEFAULT_PARTICLE_LIFETIME, + ); + } + for mob in targets.iter() { + let destructible = destructibles.get(*mob); + let entity_name = names.get(*mob).unwrap(); + match destructible { + None => { + SufferDamage::new_damage(&mut suffer_damage, *mob, damage_roll, true); + if entity == *player_entity { + damage_logger = + damage_logger.append("The").npc_name(&entity_name.name).append("is hit!"); + needs_damage_log = true; + } + } + Some(_destructible) => { + damage_logger = damage_logger + .append("The") + .item_name(&entity_name.name) + .colour(rltk::WHITE) + .append("is destroyed!"); + needs_damage_log = true; + entities.delete(*mob).expect("Delete failed"); + } + } + + used_item = true; + } + } + } + + // CONFUSION + let mut add_confusion = Vec::new(); + { + let causes_confusion = confused.get(wants_to_use.item); + match causes_confusion { + None => {} + Some(confusion) => { + for mob in targets.iter() { + add_confusion.push((*mob, confusion.turns)); + // Gamelog entry for this is handled turn-by-turn in AI. + } + } + } + } + for mob in add_confusion.iter() { + confused.insert(mob.0, Confusion { turns: mob.1 }).expect("Unable to insert status"); + } + + // MAGIC MAPPERS + let is_mapper = magic_mapper.get(wants_to_use.item); + match is_mapper { + None => {} + Some(_) => { + used_item = true; + match is_cursed { + None => { + logger = logger + .append("You feel") + .colour(rltk::GREEN) + .append("a sense of acuity towards your surroundings."); + *runstate = RunState::MagicMapReveal { row: 0, cursed: false }; + } + Some(_) => { + logger = logger.append("You").colour(rltk::RED).append("forget where you last were."); + *runstate = RunState::MagicMapReveal { row: 0, cursed: true }; + } + } + } + } + + let is_digger = digger.get(wants_to_use.item); + match is_digger { + None => {} + Some(_) => { + used_item = true; + for idx in target_idxs { + if map.tiles[idx] == TileType::Wall { + map.tiles[idx] = TileType::Floor; + } + for viewshed in (&mut viewsheds).join() { + if viewshed + .visible_tiles + .contains(&Point::new(idx % map.width as usize, idx / map.width as usize)) + { + viewshed.dirty = true; + } + } + } + } + } + + // ITEM DELETION AFTER USE + if used_item { + // Identify + if entity == *player_entity { + identified_items + .insert(entity, IdentifiedItem { name: item_being_used.name.clone() }) + .expect("Unable to insert"); + } + let consumable = consumables.get(wants_to_use.item); + match consumable { + None => {} + Some(_) => { + entities.delete(wants_to_use.item).expect("Delete failed"); + } + } + } + + logger.log(); + if needs_damage_log { + damage_logger.log(); + } + } + wants_to_use.clear(); + } +} + +pub struct ItemIdentificationSystem {} + +impl<'a> System<'a> for ItemIdentificationSystem { + #[allow(clippy::type_complexity)] + type SystemData = ( + ReadStorage<'a, crate::components::Player>, + WriteStorage<'a, IdentifiedItem>, + WriteExpect<'a, crate::map::MasterDungeonMap>, + ReadStorage<'a, crate::Item>, + ReadStorage<'a, Name>, + WriteStorage<'a, crate::ObfuscatedName>, + Entities<'a>, + ); + + fn run(&mut self, data: Self::SystemData) { + let (player, mut identified, mut dm, items, names, mut obfuscated_names, entities) = data; + for (_p, id) in (&player, &identified).join() { + rltk::console::log(id.name.clone()); + let tag = crate::raws::get_id_from_name(id.name.clone()); + if !dm.identified_items.contains(&id.name) && crate::raws::is_tag_magic(&tag) { + dm.identified_items.insert(id.name.clone()); + + for (entity, _item, name) in (&entities, &items, &names).join() { + if name.name == id.name { + obfuscated_names.remove(entity); + } + } + } + } + // Clean up + identified.clear(); + } +} + +pub struct ItemDropSystem {} + +impl<'a> System<'a> for ItemDropSystem { + #[allow(clippy::type_complexity)] + type SystemData = ( + ReadExpect<'a, Entity>, + Entities<'a>, + WriteStorage<'a, WantsToDropItem>, + ReadStorage<'a, Name>, + WriteStorage<'a, Position>, + WriteStorage<'a, InBackpack>, + WriteStorage<'a, EquipmentChanged>, + ReadStorage<'a, MagicItem>, + ReadStorage<'a, ObfuscatedName>, + ReadExpect<'a, MasterDungeonMap>, + ReadStorage<'a, Wand>, + ); + + fn run(&mut self, data: Self::SystemData) { + let ( + player_entity, + entities, + mut wants_drop, + names, + mut positions, + mut backpack, + mut equipment_changed, + magic_items, + obfuscated_names, + dm, + wands, + ) = data; + + for (entity, to_drop) in (&entities, &wants_drop).join() { + equipment_changed.insert(entity, EquipmentChanged {}).expect("Unable to insert EquipmentChanged."); + let mut dropper_pos: Position = Position { x: 0, y: 0 }; + { + let dropped_pos = positions.get(entity).unwrap(); + dropper_pos.x = dropped_pos.x; + dropper_pos.y = dropped_pos.y; + } + positions + .insert(to_drop.item, Position { x: dropper_pos.x, y: dropper_pos.y }) + .expect("Failed to insert position."); + backpack.remove(to_drop.item); + + if entity == *player_entity { + gamelog::Logger::new() + .append("You drop the") + .item_name_n(format!( + "{}", + obfuscate_name(to_drop.item, &names, &magic_items, &obfuscated_names, &dm, Some(&wands)).0 + )) + .period() + .log(); + } + } + + wants_drop.clear(); + } +} + +pub struct ItemRemoveSystem {} + +impl<'a> System<'a> for ItemRemoveSystem { + #[allow(clippy::type_complexity)] + type SystemData = ( + Entities<'a>, + ReadExpect<'a, Entity>, + ReadStorage<'a, Name>, + WriteStorage<'a, WantsToRemoveItem>, + WriteStorage<'a, Equipped>, + WriteStorage<'a, InBackpack>, + ); + + fn run(&mut self, data: Self::SystemData) { + let (entities, player_entity, names, mut wants_remove, mut equipped, mut backpack) = data; + + for (entity, to_remove) in (&entities, &wants_remove).join() { + equipped.remove(to_remove.item); + if let Some(name) = names.get(to_remove.item) { + if entity == *player_entity { + gamelog::Logger::new().append("You unequip the").item_name_n(&name.name).period().log(); + } + } + backpack.insert(to_remove.item, InBackpack { owner: entity }).expect("Unable to insert backpack"); + } + + wants_remove.clear(); + } +} diff --git a/src/invkeys.rs b/src/invkeys.rs deleted file mode 100644 index 2cee2f4..0000000 --- a/src/invkeys.rs +++ /dev/null @@ -1,59 +0,0 @@ -use std::sync::Mutex; -use std::collections::HashMap; -use crate::gui::UniqueInventoryItem; - -lazy_static! { - pub static ref INVKEYS: Mutex> = Mutex::new(HashMap::new()); - pub static ref ASSIGNEDKEYS: Mutex> = Mutex::new(vec![false; 52]); -} - -/// For (de)serialization. -pub fn clone_invkeys() -> HashMap { - let invkeys = INVKEYS.lock().unwrap(); - invkeys.clone() -} -pub fn restore_invkeys(invkeys: HashMap) { - INVKEYS.lock().unwrap().clear(); - INVKEYS.lock().unwrap().extend(invkeys); -} - -pub fn check_key(idx: usize) -> bool { - let lock = ASSIGNEDKEYS.lock().unwrap(); - lock[idx] -} - -pub fn item_exists(item: &UniqueInventoryItem) -> Option { - let invkeys = INVKEYS.lock().unwrap(); - use bracket_lib::prelude::*; - console::log(&format!("{:?}", item)); - if invkeys.contains_key(item) { - Some(*invkeys.get(item).unwrap()) - } else { - None - } -} - -pub fn assign_next_available() -> Option { - let mut lock = ASSIGNEDKEYS.lock().unwrap(); - for (i, key) in lock.iter_mut().enumerate() { - if !*key { - *key = true; - return Some(i); - } - } - None -} - -pub fn register_stackable(stacks: bool, item: UniqueInventoryItem, idx: usize) { - if stacks { - let mut invkeys = INVKEYS.lock().unwrap(); - invkeys.insert(item, idx); - } -} - -pub fn clear_idx(idx: usize) { - let mut lock = ASSIGNEDKEYS.lock().unwrap(); - lock[idx] = false; - let mut invkeys = INVKEYS.lock().unwrap(); - invkeys.retain(|_k, v| *v != idx); -} diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index e184a58..0000000 --- a/src/lib.rs +++ /dev/null @@ -1,47 +0,0 @@ -// src/lib.rs -// 31-Aug-2023 - -use bracket_lib::prelude::*; -use specs::prelude::*; -extern crate serde; - -#[macro_use] -extern crate lazy_static; - -#[macro_use] -pub mod macros; - -pub mod camera; -pub mod components; -pub mod raws; -pub mod map; -pub mod player; -pub mod gamelog; -pub mod gui; -pub mod map_builders; -pub mod saveload_system; -pub mod spawner; -pub mod visibility_system; -pub mod damage_system; -pub mod hunger_system; -pub mod melee_combat_system; -pub mod trigger_system; -pub mod inventory; -pub mod particle_system; -pub mod ai; -pub mod data; -pub mod config; -pub mod effects; -pub mod gamesystem; -pub mod random_table; -pub mod rex_assets; -pub mod spatial; -pub mod morgue; -pub mod states; -pub mod invkeys; - -pub use components::*; -use particle_system::ParticleBuilder; -pub use map::*; -pub use states::runstate::RunState; -pub use states::state::State; diff --git a/src/macros/mod.rs b/src/macros/mod.rs deleted file mode 100644 index a064f44..0000000 --- a/src/macros/mod.rs +++ /dev/null @@ -1,93 +0,0 @@ -// macros/mod.rs - -#[macro_export] -/// Used to check if the player has a given component. -macro_rules! player_has_component { - ($ecs:expr, $component:ty) => { - { - let player = $ecs.fetch::(); - let component = $ecs.read_storage::<$component>(); - if let Some(player_component) = component.get(*player) { - true - } else { - false - } - } - }; -} - -#[macro_export] -/// Used to check if a given entity has a given Intrinsic. -macro_rules! has { - ($ecs:expr, $entity:expr, $intrinsic:expr) => { - { - let intrinsics = $ecs.read_storage::(); - if let Some(has_intrinsics) = intrinsics.get($entity) { - has_intrinsics.list.contains(&$intrinsic) - } else { - false - } - } - }; -} - -#[macro_export] -/// Used to check if the player has a given Intrinsic. -macro_rules! player_has { - ($ecs:expr, $intrinsic:expr) => { - { - let player = $ecs.fetch::(); - let intrinsics = $ecs.read_storage::(); - if let Some(player_intrinsics) = intrinsics.get(*player) { - player_intrinsics.list.contains(&$intrinsic) - } else { - false - } - } - }; -} - -#[macro_export] -/// Handles adding an Intrinsic to the player, and adding it to the IntrinsicChanged component. -macro_rules! add_intr { - ($ecs:expr, $entity:expr, $intrinsic:expr) => { - { - let mut intrinsics = $ecs.write_storage::(); - if let Some(player_intrinsics) = intrinsics.get_mut($entity) { - if !player_intrinsics.list.contains(&$intrinsic) { - player_intrinsics.list.insert($intrinsic); - let mut intrinsic_changed = $ecs.write_storage::(); - if let Some(this_intrinsic_changed) = intrinsic_changed.get_mut($entity) { - this_intrinsic_changed.gained.insert($intrinsic); - } else { - intrinsic_changed.insert($entity, crate::IntrinsicChanged { - gained: { - let mut m = std::collections::HashSet::new(); - m.insert($intrinsic); - m - }, - lost: std::collections::HashSet::new() - }).expect("Failed to insert IntrinsicChanged component."); - } - } - } else { - intrinsics.insert($entity, crate::Intrinsics { - list: { - let mut m = std::collections::HashSet::new(); - m.insert($intrinsic); - m - } - }).expect("Failed to insert Intrinsics component."); - let mut intrinsic_changed = $ecs.write_storage::(); - intrinsic_changed.insert($entity, crate::IntrinsicChanged { - gained: { - let mut m = std::collections::HashSet::new(); - m.insert($intrinsic); - m - }, - lost: std::collections::HashSet::new() - }).expect("Failed to insert IntrinsicChanged component."); - } - } - }; -} diff --git a/src/main.rs b/src/main.rs index fc2c72b..f76df06 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,34 +1,514 @@ -use rust_rl::*; +use rltk::{GameState, Point, RandomNumberGenerator, Rltk}; use specs::prelude::*; -use specs::saveload::{ SimpleMarker, SimpleMarkerAllocator }; -use bracket_lib::prelude::*; +use specs::saveload::{SimpleMarker, SimpleMarkerAllocator}; +extern crate serde; + +pub mod camera; +mod components; +pub mod raws; +pub use components::*; +mod map; +pub use map::*; +mod player; +use player::*; +mod rect; +pub use rect::Rect; +mod gamelog; +mod gui; +pub mod map_builders; +mod saveload_system; +mod spawner; +mod visibility_system; +use visibility_system::VisibilitySystem; +mod monster_ai_system; +use monster_ai_system::MonsterAI; +pub mod bystander_ai_system; +mod map_indexing_system; +use map_indexing_system::MapIndexingSystem; +mod damage_system; +use damage_system::*; +mod hunger_system; +mod melee_combat_system; +mod trigger_system; +use melee_combat_system::MeleeCombatSystem; +mod inventory_system; +use inventory_system::*; +mod particle_system; +use particle_system::{ParticleBuilder, DEFAULT_PARTICLE_LIFETIME, LONG_PARTICLE_LIFETIME}; +mod ai; +mod gamesystem; +mod random_table; +mod rex_assets; + +#[macro_use] +extern crate lazy_static; + +//Consts +pub const SHOW_MAPGEN: bool = false; +pub const LOG_SPAWNING: bool = true; +pub const LOG_TICKS: bool = false; + +#[derive(PartialEq, Copy, Clone)] +pub enum RunState { + AwaitingInput, // Player's turn + PreRun, + Ticking, // Tick systems + ShowCheatMenu, // Teleport, godmode, etc. - for debugging + ShowInventory, + ShowDropItem, + ShowRemoveItem, + ShowTargeting { range: i32, item: Entity, aoe: i32 }, + ActionWithDirection { function: fn(i: i32, j: i32, ecs: &mut World) -> RunState }, + MainMenu { menu_selection: gui::MainMenuSelection }, + SaveGame, + GameOver, + NextLevel, + PreviousLevel, + HelpScreen, + MagicMapReveal { row: i32, cursed: bool }, // Animates magic mapping effect + MapGeneration, +} + +pub struct State { + pub ecs: World, + mapgen_next_state: Option, + mapgen_history: Vec, + mapgen_index: usize, + mapgen_timer: f32, +} + +impl State { + fn generate_world_map(&mut self, new_id: i32, offset: i32) { + // Visualisation stuff + self.mapgen_index = 0; + self.mapgen_timer = 0.0; + self.mapgen_history.clear(); + let map_building_info = map::level_transition(&mut self.ecs, new_id, offset); + if let Some(history) = map_building_info { + self.mapgen_history = history; + } else { + map::dungeon::thaw_entities(&mut self.ecs); + } + } + + fn run_systems(&mut self) { + let mut mapindex = MapIndexingSystem {}; + let mut vis = VisibilitySystem {}; + let mut regen_system = ai::RegenSystem {}; + let mut energy = ai::EnergySystem {}; + let mut encumbrance_system = ai::EncumbranceSystem {}; + let mut turn_status_system = ai::TurnStatusSystem {}; + let mut quip_system = ai::QuipSystem {}; + let mut mob = MonsterAI {}; + let mut bystanders = bystander_ai_system::BystanderAI {}; + let mut trigger_system = trigger_system::TriggerSystem {}; + let mut melee_system = MeleeCombatSystem {}; + let mut damage_system = DamageSystem {}; + let mut inventory_system = ItemCollectionSystem {}; + let mut item_use_system = ItemUseSystem {}; + let mut item_drop_system = ItemDropSystem {}; + let mut item_remove_system = ItemRemoveSystem {}; + let mut item_id_system = ItemIdentificationSystem {}; + let mut hunger_clock = hunger_system::HungerSystem {}; + let mut particle_system = particle_system::ParticleSpawnSystem {}; + + mapindex.run_now(&self.ecs); + vis.run_now(&self.ecs); + regen_system.run_now(&self.ecs); + encumbrance_system.run_now(&self.ecs); + energy.run_now(&self.ecs); + turn_status_system.run_now(&self.ecs); + quip_system.run_now(&self.ecs); + mob.run_now(&self.ecs); + bystanders.run_now(&self.ecs); + trigger_system.run_now(&self.ecs); + inventory_system.run_now(&self.ecs); + item_use_system.run_now(&self.ecs); + item_drop_system.run_now(&self.ecs); + item_remove_system.run_now(&self.ecs); + item_id_system.run_now(&self.ecs); + melee_system.run_now(&self.ecs); + damage_system.run_now(&self.ecs); + hunger_clock.run_now(&self.ecs); + particle_system.run_now(&self.ecs); + + self.ecs.maintain(); + } + + fn run_map_index(&mut self) { + let mut mapindex = MapIndexingSystem {}; + mapindex.run_now(&self.ecs); + } + + fn goto_level(&mut self, offset: i32) { + // Build new map + place player + let current_id; + { + let worldmap_resource = self.ecs.fetch::(); + current_id = worldmap_resource.id; + } + // Record the correct type of event + if offset > 0 { + gamelog::record_event("descended", 1); + } else if current_id == 1 { + gamelog::Logger::new().append("CHEAT MENU: YOU CAN'T DO THAT.").colour((255, 0, 0)).log(); + return; + } else { + gamelog::record_event("ascended", 1); + } + // Freeze the current level + map::dungeon::freeze_entities(&mut self.ecs); + self.generate_world_map(current_id + offset, offset); + let mapname = self.ecs.fetch::().name.clone(); + gamelog::Logger::new().append("You head to").npc_name_n(mapname).period().log(); + } + + fn game_over_cleanup(&mut self) { + // Delete everything + let mut to_delete = Vec::new(); + for e in self.ecs.entities().join() { + to_delete.push(e); + } + for del in to_delete.iter() { + self.ecs.delete_entity(*del).expect("Deletion failed"); + } + + // Spawn a new player and build new map + { + let player_entity = spawner::player(&mut self.ecs, 0, 0); + let mut player_entity_writer = self.ecs.write_resource::(); + *player_entity_writer = player_entity; + } + // Replace map list + self.ecs.insert(map::dungeon::MasterDungeonMap::new()); + self.generate_world_map(1, 0); + + gamelog::setup_log(); + gamelog::record_event("player_level", 1); + } +} + +impl GameState for State { + fn tick(&mut self, ctx: &mut Rltk) { + let mut new_runstate; + { + let runstate = self.ecs.fetch::(); + new_runstate = *runstate; + } + // Clear screen + ctx.cls(); + particle_system::particle_ticker(&mut self.ecs, ctx); + + match new_runstate { + RunState::MainMenu { .. } => {} + _ => { + // Draw map and ui + camera::render_camera(&self.ecs, ctx); + gui::draw_ui(&self.ecs, ctx); + } + } + + match new_runstate { + RunState::PreRun => { + self.run_systems(); + self.ecs.maintain(); + new_runstate = RunState::AwaitingInput; + } + RunState::AwaitingInput => { + self.run_map_index(); + // Sanity-checking that the player actually *should* + // be taking a turn before giving them one. If they + // don't have a turn component, go back to ticking. + let mut can_act = false; + { + let player_entity = self.ecs.fetch::(); + let turns = self.ecs.read_storage::(); + if let Some(_) = turns.get(*player_entity) { + can_act = true; + } + } + if can_act { + new_runstate = player_input(self, ctx); + } else { + new_runstate = RunState::Ticking; + } + } + RunState::Ticking => { + while new_runstate == RunState::Ticking { + self.run_systems(); + self.ecs.maintain(); + try_spawn_interval(&mut self.ecs); + match *self.ecs.fetch::() { + RunState::AwaitingInput => new_runstate = RunState::AwaitingInput, + RunState::MagicMapReveal { row, cursed } => { + new_runstate = RunState::MagicMapReveal { row: row, cursed: cursed } + } + _ => new_runstate = RunState::Ticking, + } + } + } + RunState::ShowCheatMenu => { + let result = gui::show_cheat_menu(self, ctx); + match result { + gui::CheatMenuResult::Cancel => new_runstate = RunState::AwaitingInput, + gui::CheatMenuResult::NoResponse => {} + gui::CheatMenuResult::Ascend => { + self.goto_level(-1); + self.mapgen_next_state = Some(RunState::PreRun); + new_runstate = RunState::MapGeneration; + } + gui::CheatMenuResult::Descend => { + self.goto_level(1); + self.mapgen_next_state = Some(RunState::PreRun); + new_runstate = RunState::MapGeneration; + } + gui::CheatMenuResult::Heal => { + let player = self.ecs.fetch::(); + let mut pools = self.ecs.write_storage::(); + let mut player_pools = pools.get_mut(*player).unwrap(); + player_pools.hit_points.current = player_pools.hit_points.max; + new_runstate = RunState::AwaitingInput; + } + gui::CheatMenuResult::MagicMap => { + let mut map = self.ecs.fetch_mut::(); + for v in map.revealed_tiles.iter_mut() { + *v = true; + } + new_runstate = RunState::AwaitingInput; + } + gui::CheatMenuResult::GodMode => { + let player = self.ecs.fetch::(); + let mut pools = self.ecs.write_storage::(); + let mut player_pools = pools.get_mut(*player).unwrap(); + gamelog::Logger::new().item_name("TOGGLED GOD MODE!").log(); + player_pools.god = !player_pools.god; + new_runstate = RunState::AwaitingInput; + } + } + } + RunState::ShowInventory => { + let result = gui::show_inventory(self, ctx); + match result.0 { + gui::ItemMenuResult::Cancel => new_runstate = RunState::AwaitingInput, + gui::ItemMenuResult::NoResponse => {} + gui::ItemMenuResult::Selected => { + let item_entity = result.1.unwrap(); + let is_ranged = self.ecs.read_storage::(); + let ranged_item = is_ranged.get(item_entity); + if let Some(ranged_item) = ranged_item { + let is_aoe = self.ecs.read_storage::(); + let aoe_item = is_aoe.get(item_entity); + if let Some(aoe_item) = aoe_item { + new_runstate = RunState::ShowTargeting { + range: ranged_item.range, + item: item_entity, + aoe: aoe_item.radius, + } + } else { + new_runstate = + RunState::ShowTargeting { range: ranged_item.range, item: item_entity, aoe: 0 } + } + } else { + let mut intent = self.ecs.write_storage::(); + intent + .insert(*self.ecs.fetch::(), WantsToUseItem { item: item_entity, target: None }) + .expect("Unable to insert intent."); + new_runstate = RunState::Ticking; + } + } + } + } + RunState::ShowDropItem => { + let result = gui::drop_item_menu(self, ctx); + match result.0 { + gui::ItemMenuResult::Cancel => new_runstate = RunState::AwaitingInput, + gui::ItemMenuResult::NoResponse => {} + gui::ItemMenuResult::Selected => { + let item_entity = result.1.unwrap(); + let mut intent = self.ecs.write_storage::(); + intent + .insert(*self.ecs.fetch::(), WantsToDropItem { item: item_entity }) + .expect("Unable to insert intent"); + new_runstate = RunState::Ticking; + } + } + } + RunState::ShowRemoveItem => { + let result = gui::remove_item_menu(self, ctx); + match result.0 { + gui::ItemMenuResult::Cancel => new_runstate = RunState::AwaitingInput, + gui::ItemMenuResult::NoResponse => {} + gui::ItemMenuResult::Selected => { + let item_entity = result.1.unwrap(); + let mut intent = self.ecs.write_storage::(); + intent + .insert(*self.ecs.fetch::(), WantsToRemoveItem { item: item_entity }) + .expect("Unable to insert intent"); + new_runstate = RunState::Ticking; + } + } + } + RunState::ShowTargeting { range, item, aoe } => { + let result = gui::ranged_target(self, ctx, range, aoe); + match result.0 { + gui::ItemMenuResult::Cancel => new_runstate = RunState::AwaitingInput, + gui::ItemMenuResult::NoResponse => {} + gui::ItemMenuResult::Selected => { + let mut intent = self.ecs.write_storage::(); + intent + .insert(*self.ecs.fetch::(), WantsToUseItem { item, target: result.1 }) + .expect("Unable to insert intent."); + new_runstate = RunState::Ticking; + } + } + } + RunState::ActionWithDirection { function } => { + new_runstate = gui::get_input_direction(&mut self.ecs, ctx, function); + } + RunState::MainMenu { .. } => { + let result = gui::main_menu(self, ctx); + match result { + gui::MainMenuResult::NoSelection { selected } => { + new_runstate = RunState::MainMenu { menu_selection: selected } + } + gui::MainMenuResult::Selected { selected } => match selected { + gui::MainMenuSelection::NewGame => new_runstate = RunState::PreRun, + gui::MainMenuSelection::LoadGame => { + saveload_system::load_game(&mut self.ecs); + new_runstate = RunState::AwaitingInput; + saveload_system::delete_save(); + } + gui::MainMenuSelection::Quit => { + ::std::process::exit(0); + } + }, + } + } + RunState::SaveGame => { + saveload_system::save_game(&mut self.ecs); + new_runstate = RunState::MainMenu { menu_selection: gui::MainMenuSelection::LoadGame }; + } + RunState::GameOver => { + let result = gui::game_over(ctx); + match result { + gui::YesNoResult::NoSelection => {} + gui::YesNoResult::Yes => { + self.game_over_cleanup(); + new_runstate = RunState::MapGeneration; + self.mapgen_next_state = + Some(RunState::MainMenu { menu_selection: gui::MainMenuSelection::NewGame }); + } + } + } + RunState::NextLevel => { + self.goto_level(1); + self.mapgen_next_state = Some(RunState::PreRun); + new_runstate = RunState::MapGeneration; + } + RunState::PreviousLevel => { + self.goto_level(-1); + self.mapgen_next_state = Some(RunState::PreRun); + new_runstate = RunState::MapGeneration; + } + RunState::HelpScreen => { + let result = gui::show_help(ctx); + match result { + gui::YesNoResult::NoSelection => {} + gui::YesNoResult::Yes => { + gamelog::record_event("looked_for_help", 1); + new_runstate = RunState::AwaitingInput; + } + } + } + RunState::MagicMapReveal { row, cursed } => { + let mut map = self.ecs.fetch_mut::(); + + // Could probably toss this into a function somewhere, and/or + // have multiple simple animations for it. + for x in 0..map.width { + let idx; + if x % 2 == 0 { + idx = map.xy_idx(x as i32, row); + } else { + idx = map.xy_idx(x as i32, (map.height as i32 - 1) - (row)); + } + if !cursed { + map.revealed_tiles[idx] = true; + } else { + map.revealed_tiles[idx] = false; + } + } + + // Dirtify viewshed only if cursed, so our currently visible tiles aren't removed too + if cursed { + let player_entity = self.ecs.fetch::(); + let mut viewshed_components = self.ecs.write_storage::(); + let viewshed = viewshed_components.get_mut(*player_entity); + if let Some(viewshed) = viewshed { + viewshed.dirty = true; + } + } + + if row as usize == map.height as usize - 1 { + new_runstate = RunState::Ticking; + } else { + new_runstate = RunState::MagicMapReveal { row: row + 1, cursed: cursed }; + } + } + RunState::MapGeneration => { + if !SHOW_MAPGEN { + new_runstate = self.mapgen_next_state.unwrap(); + } + if self.mapgen_history.len() != 0 { + ctx.cls(); + camera::render_debug_map(&self.mapgen_history[self.mapgen_index], ctx); + + self.mapgen_timer += ctx.frame_time_ms; + if self.mapgen_timer > 300.0 { + self.mapgen_timer = 0.0; + self.mapgen_index += 1; + if self.mapgen_index >= self.mapgen_history.len() { + new_runstate = self.mapgen_next_state.unwrap(); + } + } + } + } + } + + { + let mut runwriter = self.ecs.write_resource::(); + *runwriter = new_runstate; + } + + damage_system::delete_the_dead(&mut self.ecs); + + let _ = rltk::render_draw_buffer(ctx); + } +} const DISPLAYWIDTH: i32 = 105; const DISPLAYHEIGHT: i32 = 56; -fn main() -> BError { +fn main() -> rltk::BError { // Embedded resources for use in wasm build const CURSES_14_16_BYTES: &[u8] = include_bytes!("../resources/curses14x16.png"); - EMBED.lock().add_resource("resources/curses14x16.png".to_string(), CURSES_14_16_BYTES); + rltk::embedding::EMBED.lock().add_resource("resources/curses14x16.png".to_string(), CURSES_14_16_BYTES); - //link_resource!(CURSES14X16, "../resources/curses_14x16.png"); + //rltk::link_resource!(CURSES14X16, "../resources/curses_14x16.png"); - let mut context = BTermBuilder::new() + use rltk::RltkBuilder; + let context = RltkBuilder::new() .with_title("rust-rl") .with_dimensions(DISPLAYWIDTH, DISPLAYHEIGHT) .with_font("curses14x16.png", 14, 16) .with_tile_dimensions(14, 16) .with_simple_console(DISPLAYWIDTH, DISPLAYHEIGHT, "curses14x16.png") + //.with_simple_console_no_bg(DISPLAYWIDTH, DISPLAYHEIGHT, "terminal8x8.jpg") .build()?; - if config::CONFIG.visuals.with_scanlines { - context.with_post_scanlines(config::CONFIG.visuals.with_screen_burn); - } let mut gs = State { ecs: World::new(), - mapgen_next_state: Some(RunState::MainMenu { - menu_selection: gui::MainMenuSelection::NewGame, - }), + mapgen_next_state: Some(RunState::MainMenu { menu_selection: gui::MainMenuSelection::NewGame }), mapgen_index: 0, mapgen_history: Vec::new(), mapgen_timer: 0.0, @@ -40,11 +520,9 @@ fn main() -> BError { gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); - gs.ecs.register::(); - gs.ecs.register::(); - gs.ecs.register::(); - gs.ecs.register::(); gs.ecs.register::(); + gs.ecs.register::(); + gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); @@ -59,10 +537,9 @@ fn main() -> BError { gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); + gs.ecs.register::(); gs.ecs.register::(); - gs.ecs.register::(); gs.ecs.register::(); - gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); @@ -74,49 +551,29 @@ fn main() -> BError { gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); - gs.ecs.register::(); - gs.ecs.register::(); + gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); - gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); - gs.ecs.register::(); - gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); - gs.ecs.register::(); + gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); gs.ecs.register::(); - gs.ecs.register::(); - gs.ecs.register::(); - gs.ecs.register::(); - gs.ecs.register::(); - gs.ecs.register::(); gs.ecs.register::(); - gs.ecs.register::(); - gs.ecs.register::(); - gs.ecs.register::(); - gs.ecs.register::(); - gs.ecs.register::(); - gs.ecs.register::(); - gs.ecs.register::(); - gs.ecs.register::(); - gs.ecs.register::(); - gs.ecs.register::(); - gs.ecs.register::(); gs.ecs.register::>(); gs.ecs.register::(); gs.ecs.register::(); @@ -125,11 +582,10 @@ fn main() -> BError { raws::load_raws(); // Insert calls - gs.ecs.insert(RandomNumberGenerator::new()); + gs.ecs.insert(rltk::RandomNumberGenerator::new()); gs.ecs.insert(map::MasterDungeonMap::new()); // Master map list - gs.ecs.insert(Map::new(true, 1, 64, 64, 0, "New Map", "N", 0)); // Map + gs.ecs.insert(Map::new(1, 64, 64, 0, "New Map")); // Map gs.ecs.insert(Point::new(0, 0)); // Player pos - gs.ecs.insert(gui::Ancestry::Human); // ancestry let player_entity = spawner::player(&mut gs.ecs, 0, 0); gs.ecs.insert(player_entity); // Player entity gs.ecs.insert(RunState::MapGeneration {}); // RunState @@ -137,8 +593,8 @@ fn main() -> BError { gs.ecs.insert(rex_assets::RexAssets::new()); gamelog::setup_log(); - gamelog::record_event(data::events::EVENT::Level(1)); - gs.generate_world_map(1, TileType::Floor); + gamelog::record_event("player_level", 1); + gs.generate_world_map(1, 0); - main_loop(context, gs) + rltk::main_loop(context, gs) } diff --git a/src/map/colours.rs b/src/map/colours.rs new file mode 100644 index 0000000..3bd60a9 --- /dev/null +++ b/src/map/colours.rs @@ -0,0 +1,24 @@ +pub const NON_VISIBLE_MULTIPLIER: f32 = 0.7; +pub const BLOODSTAIN_COLOUR: (u8, u8, u8) = (153, 0, 0); + +// DEFAULT THEME +pub const DEFAULT_BG_COLOUR: (u8, u8, u8) = (29, 50, 50); +pub const WALL_COLOUR: (u8, u8, u8) = (229, 191, 94); +pub const FLOOR_COLOUR: (u8, u8, u8) = (25, 204, 122); +pub const STAIR_COLOUR: (u8, u8, u8) = (200, 200, 0); +pub const WOOD_FLOOR_COLOUR: (u8, u8, u8) = (41, 30, 20); +pub const FENCE_FG_COLOUR: (u8, u8, u8) = (110, 24, 0); +pub const FENCE_COLOUR: (u8, u8, u8) = (45, 30, 10); +pub const BRIDGE_COLOUR: (u8, u8, u8) = (42, 48, 37); +pub const GRAVEL_COLOUR: (u8, u8, u8) = (26, 26, 53); +pub const ROAD_COLOUR: (u8, u8, u8) = (8, 38, 40); +pub const GRASS_COLOUR: (u8, u8, u8) = (9, 65, 6); +pub const FOLIAGE_COLOUR: (u8, u8, u8) = (5, 60, 5); +pub const HEAVY_FOLIAGE_COLOUR: (u8, u8, u8) = (5, 55, 5); +pub const SAND_COLOUR: (u8, u8, u8) = (70, 70, 21); +pub const SHALLOW_WATER_COLOUR: (u8, u8, u8) = (24, 47, 99); +pub const DEEP_WATER_COLOUR: (u8, u8, u8) = (18, 33, 63); +pub const BARS_COLOUR: (u8, u8, u8) = (100, 100, 100); + +// FOREST THEME +pub const FOREST_WALL_COLOUR: (u8, u8, u8) = (0, 153, 0); diff --git a/src/map/dungeon.rs b/src/map/dungeon.rs index 4407efa..221d8b5 100644 --- a/src/map/dungeon.rs +++ b/src/map/dungeon.rs @@ -1,10 +1,9 @@ -use super::{ Map, TileType }; -use crate::{ gamelog, map_builders, OtherLevelPosition, Position, Telepath, Viewshed }; -use bracket_lib::prelude::*; -use serde::{ Deserialize, Serialize }; +use super::{Map, TileType}; +use crate::{gamelog, map_builders, OtherLevelPosition, Position, Telepath, Viewshed}; +use rltk::prelude::*; +use serde::{Deserialize, Serialize}; use specs::prelude::*; -use std::collections::{ HashMap, HashSet }; -use crate::data::events::*; +use std::collections::{HashMap, HashSet}; #[derive(Default, Serialize, Deserialize, Clone)] pub struct MasterDungeonMap { @@ -51,7 +50,8 @@ impl MasterDungeonMap { /// Gets a map by ID from the MasterDungeonMap pub fn get_map(&self, id: i32) -> Option { if self.maps.contains_key(&id) { - let result = self.maps[&id].clone(); + let mut result = self.maps[&id].clone(); + result.tile_content = vec![Vec::new(); (result.width * result.height) as usize]; return Some(result); } else { return None; @@ -61,11 +61,7 @@ impl MasterDungeonMap { fn make_scroll_name(rng: &mut RandomNumberGenerator) -> String { let len = 4 + rng.roll_dice(1, 6); - let space_at_i = if len > 6 && rng.roll_dice(1, 2) == 1 { - rng.roll_dice(1, len - 6) + 3 - } else { - -1 - }; + let space_at_i = if len > 6 && rng.roll_dice(1, 2) == 1 { rng.roll_dice(1, len - 6) + 3 } else { -1 }; let offset = rng.roll_dice(1, 2) - 1; let mut name = "".to_string(); for i in 0..len { @@ -113,44 +109,17 @@ fn make_scroll_name(rng: &mut RandomNumberGenerator) -> String { } const POTION_COLOURS: &[&str] = &[ - "red", - "orange", - "yellow", - "green", - "blue", - "indigo", - "violet", - "black", - "white", - "silver", - "gold", - "rainbow", - "blood", - "purple", - "cyan", - "brown", - "grey", - "octarine", -]; -const POTION_ADJECTIVES: &[&str] = &[ - "swirling", - "viscous", - "effervescent", - "slimy", - "oily", - "metallic", - "prismatic", - "goopy", + "red", "orange", "yellow", "green", "blue", "indigo", "violet", "black", "white", "silver", "gold", "rainbow", + "blood", "purple", "cyan", "brown", "grey", ]; +const POTION_ADJECTIVES: &[&str] = &["swirling", "viscous", "effervescent", "slimy", "oily", "metallic"]; fn make_potion_name(rng: &mut RandomNumberGenerator, used_names: &mut HashSet) -> String { loop { let mut name: String = - POTION_ADJECTIVES[ - (rng.roll_dice(1, POTION_ADJECTIVES.len() as i32) as usize) - 1 - ].to_string(); + POTION_ADJECTIVES[rng.roll_dice(1, POTION_ADJECTIVES.len() as i32) as usize - 1].to_string(); name += " "; - name += POTION_COLOURS[(rng.roll_dice(1, POTION_COLOURS.len() as i32) as usize) - 1]; + name += POTION_COLOURS[rng.roll_dice(1, POTION_COLOURS.len() as i32) as usize - 1]; name += " potion"; if !used_names.contains(&name) { @@ -161,31 +130,22 @@ fn make_potion_name(rng: &mut RandomNumberGenerator, used_names: &mut HashSet) -> String { loop { - let mut name: String = - WAND_TYPES[(rng.roll_dice(1, WAND_TYPES.len() as i32) as usize) - 1].to_string(); + let mut name: String = WAND_TYPES[rng.roll_dice(1, WAND_TYPES.len() as i32) as usize - 1].to_string(); name += " wand"; if !used_names.contains(&name) { @@ -195,57 +155,41 @@ fn make_wand_name(rng: &mut RandomNumberGenerator, used_names: &mut HashSet Option> { +pub fn level_transition(ecs: &mut World, new_id: i32, offset: i32) -> Option> { // Obtain master let dungeon_master = ecs.read_resource::(); if dungeon_master.get_map(new_id).is_some() { std::mem::drop(dungeon_master); - transition_to_existing_map(ecs, new_id, dest_tile); + transition_to_existing_map(ecs, new_id, offset); return None; } else { std::mem::drop(dungeon_master); - return Some(transition_to_new_map(ecs, new_id, dest_tile)); + return Some(transition_to_new_map(ecs, new_id)); } } -fn transition_to_existing_map(ecs: &mut World, new_id: i32, dest_tile: TileType) { - let mut dungeon_master = ecs.write_resource::(); +fn transition_to_existing_map(ecs: &mut World, new_id: i32, offset: i32) { + let dungeon_master = ecs.read_resource::(); // Unwrapping here panics if new_id isn't present. But this should // never be called without new_id being present by level_transition. let map = dungeon_master.get_map(new_id).unwrap(); let mut worldmap_resource = ecs.write_resource::(); let player_entity = ecs.fetch::(); - + // Find down stairs, place player let w = map.width; - let mut possible_destinations: Vec = Vec::new(); + let stair_type = if offset < 0 { TileType::DownStair } else { TileType::UpStair }; for (idx, tt) in map.tiles.iter().enumerate() { - if *tt == dest_tile { - possible_destinations.push(idx); + if *tt == stair_type { + let mut player_position = ecs.write_resource::(); + *player_position = Point::new(idx as i32 % w, idx as i32 / w); + let mut position_components = ecs.write_storage::(); + let player_pos_component = position_components.get_mut(*player_entity); + if let Some(player_pos_component) = player_pos_component { + player_pos_component.x = idx as i32 % w; + player_pos_component.y = idx as i32 / w; + } } } - if possible_destinations.is_empty() { - console::log("WARNING: No destination tiles found on map transition."); - match dest_tile { - TileType::DownStair => console::log("DESTINATION: DownStair"), - TileType::UpStair => console::log("DESTINATION: UpStair"), - TileType::ToOvermap(id) => console::log(format!("DESTINATION: ToOvermap({})", id)), - TileType::ToLocal(id) => console::log(format!("DESTINATION: ToLocal({})", id)), - _ => console::log("DESTINATION: Unknown"), - } - possible_destinations.push(((map.width * map.height) as usize) / 2); // Centre of map - } - let mut rng = ecs.write_resource::(); - let idx = - possible_destinations[(rng.roll_dice(1, possible_destinations.len() as i32) as usize) - 1]; - let mut player_position = ecs.write_resource::(); - *player_position = Point::new((idx as i32) % w, (idx as i32) / w); - let mut position_components = ecs.write_storage::(); - let player_pos_component = position_components.get_mut(*player_entity); - if let Some(player_pos_component) = player_pos_component { - player_pos_component.x = (idx as i32) % w; - player_pos_component.y = (idx as i32) / w; - } - dungeon_master.store_map(&worldmap_resource); *worldmap_resource = map; // Dirtify viewsheds (forces refresh) let mut viewshed_components = ecs.write_storage::(); @@ -260,29 +204,25 @@ fn transition_to_existing_map(ecs: &mut World, new_id: i32, dest_tile: TileType) } } -fn transition_to_new_map(ecs: &mut World, new_id: i32, _dest_tile: TileType) -> Vec { - let mut rng = ecs.write_resource::(); +fn transition_to_new_map(ecs: &mut World, new_id: i32) -> Vec { + let mut rng = ecs.write_resource::(); // Might need this to fallback to 1, but if player // level isn't found at all, there's a bigger concern // concern than just this function not working. - let player_level = gamelog::get_event_count(EVENT::COUNT_LEVEL); + let player_level = gamelog::get_event_count("player_level"); let mut builder = map_builders::level_builder(new_id, &mut rng, 100, 50, player_level); builder.build_map(&mut rng); std::mem::drop(rng); + if new_id > 1 { + if let Some(pos) = &builder.build_data.starting_position { + let up_idx = builder.build_data.map.xy_idx(pos.x, pos.y); + builder.build_data.map.tiles[up_idx] = TileType::UpStair; + } + } let mapgen_history = builder.build_data.history.clone(); let player_start; - let old_map: Map; { let mut worldmap_resource = ecs.write_resource::(); - old_map = worldmap_resource.clone(); - // If there is zero overmap involvement, place an upstair where we ended up. - // Otherwise, this should be hand-placed. - if !old_map.overmap && !builder.build_data.map.overmap { - if let Some(pos) = &builder.build_data.starting_position { - let up_idx = builder.build_data.map.xy_idx(pos.x, pos.y); - builder.build_data.map.tiles[up_idx] = TileType::UpStair; - } - } *worldmap_resource = builder.build_data.map.clone(); // Unwrap so we get a CTD if there's no starting pos. player_start = builder.build_data.starting_position.as_mut().unwrap().clone(); @@ -312,7 +252,6 @@ fn transition_to_new_map(ecs: &mut World, new_id: i32, _dest_tile: TileType) -> } // Store newly minted map let mut dungeon_master = ecs.write_resource::(); - dungeon_master.store_map(&old_map); dungeon_master.store_map(&builder.build_data.map); return mapgen_history; } @@ -354,9 +293,7 @@ pub fn thaw_entities(ecs: &mut World) { let mut pos_to_delete: Vec = Vec::new(); for (entity, pos) in (&entities, &other_positions).join() { if entity != *player_entity && pos.id == map_id { - positions - .insert(entity, Position { x: pos.x, y: pos.y }) - .expect("Failed to insert OtherLevelPosition"); + positions.insert(entity, Position { x: pos.x, y: pos.y }).expect("Failed to insert OtherLevelPosition"); pos_to_delete.push(entity); } } diff --git a/src/map/glyphs.rs b/src/map/glyphs.rs new file mode 100644 index 0000000..ba9ae67 --- /dev/null +++ b/src/map/glyphs.rs @@ -0,0 +1,21 @@ +// DEFAULT THEME +#[allow(dead_code)] +pub const WALL_GLYPH: char = '#'; +pub const FLOOR_GLYPH: char = '.'; +pub const DOWN_STAIR_GLYPH: char = '>'; +pub const UP_STAIR_GLYPH: char = '<'; +pub const WOOD_FLOOR_GLYPH: char = '.'; +pub const FENCE_GLYPH: char = '='; +pub const BRIDGE_GLYPH: char = '.'; +pub const GRAVEL_GLYPH: char = ';'; +pub const ROAD_GLYPH: char = '.'; +pub const GRASS_GLYPH: char = '"'; +pub const FOLIAGE_GLYPH: char = ':'; +pub const HEAVY_FOLIAGE_GLYPH: char = ';'; +pub const SAND_GLYPH: char = '.'; +pub const SHALLOW_WATER_GLYPH: char = '~'; +pub const DEEP_WATER_GLYPH: char = '≈'; +pub const BARS_GLYPH: char = '#'; + +// FOREST THEME +pub const FOREST_WALL_GLYPH: char = '♣'; diff --git a/src/map/interval_spawning_system.rs b/src/map/interval_spawning_system.rs index b03918a..3ff2e36 100644 --- a/src/map/interval_spawning_system.rs +++ b/src/map/interval_spawning_system.rs @@ -1,45 +1,7 @@ -use crate::{ - config::CONFIG, - gamelog, - raws, - spawner, - Clock, - Map, - RandomNumberGenerator, - TakingTurn, -}; +use crate::{gamelog, raws, spawner, Clock, Map, RandomNumberGenerator, TakingTurn, LOG_SPAWNING}; use specs::prelude::*; -use bracket_lib::prelude::*; -use crate::data::events::*; const TRY_SPAWN_CHANCE: i32 = 70; -const FEATURE_MESSAGE_CHANCE: i32 = 110; - -pub fn maybe_map_message(ecs: &mut World) { - let mut maybe_message = false; - let map = ecs.fetch::(); - if map.messages.is_empty() { - return; - } - // Scope for borrow checker (ECS) - { - let clock = ecs.read_storage::(); - let turns = ecs.read_storage::(); - let mut rng = ecs.write_resource::(); - for (_c, _t) in (&clock, &turns).join() { - if rng.roll_dice(1, FEATURE_MESSAGE_CHANCE) == 1 { - maybe_message = true; - } - } - } - if maybe_message { - let mut logger = gamelog::Logger::new(); - for message in map.messages.clone() { - logger = logger.append(message); - } - logger.log(); - } -} pub fn try_spawn_interval(ecs: &mut World) { let mut try_spawn = false; @@ -52,7 +14,7 @@ pub fn try_spawn_interval(ecs: &mut World) { } let clock = ecs.read_storage::(); let turns = ecs.read_storage::(); - let mut rng = ecs.write_resource::(); + let mut rng = ecs.write_resource::(); for (_c, _t) in (&clock, &turns).join() { if rng.roll_dice(1, TRY_SPAWN_CHANCE) == 1 { try_spawn = true; @@ -60,8 +22,8 @@ pub fn try_spawn_interval(ecs: &mut World) { } } if try_spawn { - if CONFIG.logging.log_spawning { - console::log("SPAWNINFO: Trying spawn."); + if LOG_SPAWNING { + rltk::console::log("SPAWNINFO: Trying spawn."); } spawn_random_mob_in_free_nonvisible_tile(ecs); } @@ -70,39 +32,41 @@ pub fn try_spawn_interval(ecs: &mut World) { fn spawn_random_mob_in_free_nonvisible_tile(ecs: &mut World) { let map = ecs.fetch::(); let mut available_tiles = populate_unblocked_nonvisible(&map); - let player_level = gamelog::get_event_count(EVENT::COUNT_LEVEL); - console::log(player_level); + let player_level = gamelog::get_event_count("player_level"); + rltk::console::log(player_level); let difficulty = (map.difficulty + player_level) / 2; if available_tiles.len() == 0 { - if CONFIG.logging.log_spawning { - console::log("SPAWNINFO: No free tiles; not spawning anything.."); + if LOG_SPAWNING { + rltk::console::log("SPAWNINFO: No free tiles; not spawning anything.."); } return; } let mut spawn_locations: Vec<(i32, i32)> = Vec::new(); let mut rng = ecs.write_resource::(); - let key = spawner::mob_table(Some(difficulty)).roll(&mut rng); + // Get mob type + let key = spawner::mob_table(difficulty).roll(&mut rng); + // Check if it spawns in a group, and roll for how many to spawn accordingly. let spawn_type = raws::get_mob_spawn_type(&raws::RAWS.lock().unwrap(), &key); let roll = raws::get_mob_spawn_amount(&mut rng, &spawn_type, player_level); + // Get that many idxs, and push them to the spawn list. for _i in 0..roll { let idx = get_random_idx_from_tiles(&mut rng, &mut available_tiles); - spawn_locations.push(((idx as i32) % map.width, (idx as i32) / map.width)); + spawn_locations.push((idx as i32 % map.width, idx as i32 / map.width)); } // Dropping resources for borrow-checker. std::mem::drop(map); std::mem::drop(rng); // For every idx in the spawn list, spawn mob. for idx in spawn_locations { - if CONFIG.logging.log_spawning { - console::log(format!("SPAWNINFO: Spawning {} at {}, {}.", key, idx.0, idx.1)); + if LOG_SPAWNING { + rltk::console::log(format!("SPAWNINFO: Spawning {} at {}, {}.", key, idx.0, idx.1)); } raws::spawn_named_entity( &raws::RAWS.lock().unwrap(), ecs, &key, - None, raws::SpawnType::AtPosition { x: idx.0, y: idx.1 }, - difficulty + difficulty, ); } } @@ -111,7 +75,7 @@ fn spawn_random_mob_in_free_nonvisible_tile(ecs: &mut World) { fn populate_unblocked_nonvisible(map: &Map) -> Vec { let mut tiles: Vec = Vec::new(); for (i, _tile) in map.tiles.iter().enumerate() { - if !crate::spatial::is_blocked(i) && !map.visible_tiles[i] { + if !map.blocked[i] && !map.visible_tiles[i] { tiles.push(i); } } @@ -119,12 +83,8 @@ fn populate_unblocked_nonvisible(map: &Map) -> Vec { } /// Picks a random index from a vector of indexes, and removes it from the vector. -fn get_random_idx_from_tiles(rng: &mut RandomNumberGenerator, area: &mut Vec) -> usize { - let idx = if area.len() == 1 { - 0usize - } else { - (rng.roll_dice(1, area.len() as i32) - 1) as usize - }; +fn get_random_idx_from_tiles(rng: &mut rltk::RandomNumberGenerator, area: &mut Vec) -> usize { + let idx = if area.len() == 1 { 0usize } else { (rng.roll_dice(1, area.len() as i32) - 1) as usize }; area.remove(idx); return area[idx]; } diff --git a/src/map/mod.rs b/src/map/mod.rs index 99a0c6a..f4f66f2 100644 --- a/src/map/mod.rs +++ b/src/map/mod.rs @@ -1,18 +1,16 @@ -use bracket_lib::prelude::*; -use serde::{ Deserialize, Serialize }; -use std::collections::{ HashSet, HashMap }; +use rltk::{Algorithm2D, BaseMap, Point}; +use serde::{Deserialize, Serialize}; +use specs::prelude::*; +use std::collections::HashSet; +pub mod colours; +mod glyphs; mod tiletype; -pub use tiletype::{ tile_cost, tile_opaque, tile_walkable, TileType, get_dest, Destination }; +pub use tiletype::{tile_cost, tile_opaque, tile_walkable, TileType}; mod interval_spawning_system; -pub use interval_spawning_system::{ maybe_map_message, try_spawn_interval }; +pub use interval_spawning_system::try_spawn_interval; pub mod dungeon; -pub use dungeon::{ level_transition, MasterDungeonMap }; +pub use dungeon::{level_transition, MasterDungeonMap}; pub mod themes; -use super::data::visuals::{ - BRIGHTEN_FG_COLOUR_BY, - GLOBAL_OFFSET_MIN_CLAMP, - GLOBAL_OFFSET_MAX_CLAMP, -}; // FIXME: If the map size gets too small, entities stop being rendered starting from the right. // i.e. on a map size of 40*40, only entities to the left of the player are rendered. @@ -20,7 +18,6 @@ use super::data::visuals::{ #[derive(Default, Serialize, Deserialize, Clone)] pub struct Map { - pub overmap: bool, pub tiles: Vec, pub width: i32, pub height: i32, @@ -28,16 +25,19 @@ pub struct Map { pub visible_tiles: Vec, pub lit_tiles: Vec, pub telepath_tiles: Vec, - pub colour_offset: Vec<((f32, f32, f32), (f32, f32, f32))>, - pub additional_fg_offset: RGB, + // Combine these offsets into one Vec<(u8, u8, u8)> + pub colour_offset: Vec<(f32, f32, f32)>, + pub additional_fg_offset: rltk::RGB, + pub blocked: Vec, pub id: i32, pub name: String, - pub short_name: String, - pub depth: i32, - pub messages: HashSet, pub difficulty: i32, - pub bloodstains: HashMap, + pub bloodstains: HashSet, pub view_blocked: HashSet, + + #[serde(skip_serializing)] + #[serde(skip_deserializing)] + pub tile_content: Vec>, } impl Map { @@ -45,20 +45,9 @@ impl Map { (y as usize) * (self.width as usize) + (x as usize) } - pub fn new( - overmap: bool, - new_id: i32, - width: i32, - height: i32, - difficulty: i32, - name: S, - short_name: S, - depth: i32 - ) -> Map { + pub fn new(new_id: i32, width: i32, height: i32, difficulty: i32, name: S) -> Map { let map_tile_count = (width * height) as usize; - crate::spatial::set_size(map_tile_count); let mut map = Map { - overmap: overmap, tiles: vec![TileType::Wall; map_tile_count], width: width, height: height, @@ -66,35 +55,26 @@ impl Map { visible_tiles: vec![false; map_tile_count], lit_tiles: vec![true; map_tile_count], // NYI: Light sources. Once those exist, we can set this to false. telepath_tiles: vec![false; map_tile_count], - colour_offset: vec![((0.0, 0.0, 0.0), (0.0, 0.0, 0.0)); map_tile_count], - additional_fg_offset: RGB::from_u8( - BRIGHTEN_FG_COLOUR_BY as u8, - BRIGHTEN_FG_COLOUR_BY as u8, - BRIGHTEN_FG_COLOUR_BY as u8 - ), + colour_offset: vec![(1.0, 1.0, 1.0); map_tile_count], + additional_fg_offset: rltk::RGB::from_u8(OFFSET_PERCENT as u8, OFFSET_PERCENT as u8, OFFSET_PERCENT as u8), + blocked: vec![false; map_tile_count], id: new_id, name: name.to_string(), - short_name: short_name.to_string(), - messages: HashSet::new(), - depth: depth, difficulty: difficulty, - bloodstains: HashMap::new(), + bloodstains: HashSet::new(), view_blocked: HashSet::new(), + tile_content: vec![Vec::new(); map_tile_count], }; - let mut rng = RandomNumberGenerator::new(); + const OFFSET_PERCENT: i32 = 10; + const TWICE_OFFSET: i32 = OFFSET_PERCENT * 2; + let mut rng = rltk::RandomNumberGenerator::new(); for idx in 0..map.colour_offset.len() { - map.colour_offset[idx].0 = ( - rng.range(GLOBAL_OFFSET_MIN_CLAMP, GLOBAL_OFFSET_MAX_CLAMP), - rng.range(GLOBAL_OFFSET_MIN_CLAMP, GLOBAL_OFFSET_MAX_CLAMP), - rng.range(GLOBAL_OFFSET_MIN_CLAMP, GLOBAL_OFFSET_MAX_CLAMP), - ); - map.colour_offset[idx].1 = ( - rng.range(GLOBAL_OFFSET_MIN_CLAMP, GLOBAL_OFFSET_MAX_CLAMP), - rng.range(GLOBAL_OFFSET_MIN_CLAMP, GLOBAL_OFFSET_MAX_CLAMP), - rng.range(GLOBAL_OFFSET_MIN_CLAMP, GLOBAL_OFFSET_MAX_CLAMP), - ); + let red_roll: f32 = (rng.roll_dice(1, TWICE_OFFSET - 1) + 1 - OFFSET_PERCENT) as f32 / 100f32 + 1.0; + let green_roll: f32 = (rng.roll_dice(1, TWICE_OFFSET - 1) + 1 - OFFSET_PERCENT) as f32 / 100f32 + 1.0; + let blue_roll: f32 = (rng.roll_dice(1, TWICE_OFFSET - 1) + 1 - OFFSET_PERCENT) as f32 / 100f32 + 1.0; + map.colour_offset[idx] = (red_roll, green_roll, blue_roll); } return map; @@ -106,15 +86,19 @@ impl Map { return false; } let idx = self.xy_idx(x, y); - return !crate::spatial::is_blocked(idx); + !self.blocked[idx] } pub fn populate_blocked(&mut self) { - crate::spatial::populate_blocked_from_map(self); + for (i, tile) in self.tiles.iter_mut().enumerate() { + self.blocked[i] = !tile_walkable(*tile); + } } pub fn clear_content_index(&mut self) { - crate::spatial::clear(); + for content in self.tile_content.iter_mut() { + content.clear(); + } } } @@ -138,12 +122,12 @@ impl BaseMap for Map { let w = self.width as usize; let p1 = Point::new(idx1 % w, idx1 / w); let p2 = Point::new(idx2 % w, idx2 / w); - DistanceAlg::Pythagoras.distance2d(p1, p2) + rltk::DistanceAlg::Pythagoras.distance2d(p1, p2) } /// Evaluate every possible exit from a given tile in a cardinal direction, and return it as a vector. - fn get_available_exits(&self, idx: usize) -> SmallVec<[(usize, f32); 10]> { - let mut exits = SmallVec::new(); + fn get_available_exits(&self, idx: usize) -> rltk::SmallVec<[(usize, f32); 10]> { + let mut exits = rltk::SmallVec::new(); let x = (idx as i32) % self.width; let y = (idx as i32) / self.width; let w = self.width as usize; diff --git a/src/map/themes.rs b/src/map/themes.rs index 8ddcc2a..e801ef1 100644 --- a/src/map/themes.rs +++ b/src/map/themes.rs @@ -1,208 +1,134 @@ -use super::{ Map, Point, TileType }; -use crate::data::visuals::*; -use crate::config::CONFIG; -use crate::data::ids::*; -use bracket_lib::prelude::*; -use std::ops::{ Add, Mul }; +use super::{colours::*, glyphs::*, Map, TileType}; +use rltk::RGB; +use std::ops::{Add, Mul}; -/// Gets the renderables for a tile, with darkening/offset/post-processing/etc. Passing a val for "debug" will ignore viewshed. -pub fn get_tile_renderables_for_id( - idx: usize, - map: &Map, - other_pos: Option, - debug: Option -) -> (FontCharType, RGB, RGB) { - let coloured_bg = CONFIG.visuals.use_coloured_tile_bg; - - let (glyph, mut fg, mut bg, fg_offset, bg_offset) = match map.id { - ID_TOWN2 => get_forest_theme_renderables(idx, map, debug), - _ => get_default_theme_renderables(idx, map, debug), +pub fn get_tile_renderables_for_id(idx: usize, map: &Map) -> (rltk::FontCharType, RGB, RGB) { + let (glyph, mut fg, mut bg) = match map.id { + 2 => get_forest_theme_renderables(idx, map), + _ => get_default_theme_renderables(idx, map), }; // If one of the colours was left blank, make them the same. - let mut same_col: bool = false; if fg == RGB::new() { - fg = bg; - same_col = true; + fg = bg } else if bg == RGB::new() { bg = fg; - same_col = true; } - if same_col && coloured_bg { - fg = fg.add(map.additional_fg_offset); - } - if CONFIG.visuals.add_colour_variance { - fg = apply_colour_offset(fg, map, idx, fg_offset, true); - bg = if coloured_bg { apply_colour_offset(bg, map, idx, bg_offset, false) } else { bg }; - } - - if CONFIG.visuals.with_scanlines && WITH_SCANLINES_BRIGHTEN_AMOUNT > 0.0 { - fg = brighten_by(fg, WITH_SCANLINES_BRIGHTEN_AMOUNT); - bg = if coloured_bg { brighten_by(bg, WITH_SCANLINES_BRIGHTEN_AMOUNT) } else { bg }; - } - let (mut multiplier, mut nonvisible, mut darken) = (1.0, false, false); - if !map.visible_tiles[idx] { - multiplier = if CONFIG.visuals.with_scanlines { - NON_VISIBLE_MULTIPLIER_IF_SCANLINES - } else { - NON_VISIBLE_MULTIPLIER - }; - nonvisible = true; - } - if other_pos.is_some() && WITH_DARKEN_BY_DISTANCE && !nonvisible { - let distance = darken_by_distance( - Point::new((idx as i32) % map.width, (idx as i32) / map.width), - other_pos.unwrap() - ); - multiplier = distance.clamp( - if CONFIG.visuals.with_scanlines { - NON_VISIBLE_MULTIPLIER_IF_SCANLINES - } else { - NON_VISIBLE_MULTIPLIER - }, - 1.0 - ); - darken = true; - } - if nonvisible || darken { - fg = fg.mul(multiplier); - bg = if coloured_bg { bg.mul(multiplier) } else { bg }; - } - if !CONFIG.visuals.use_coloured_tile_bg { - bg = RGB::named(BLACK); - } + fg = fg.add(map.additional_fg_offset); + (fg, bg) = apply_colour_offset(fg, bg, map, idx); bg = apply_bloodstain_if_necessary(bg, map, idx); + (fg, bg) = darken_if_not_visible(fg, bg, map, idx); return (glyph, fg, bg); } #[rustfmt::skip] -pub fn get_default_theme_renderables(idx: usize, map: &Map, debug: Option) -> (FontCharType, RGB, RGB, (i32, i32, i32), (i32, i32, i32)) { - let glyph: FontCharType; +pub fn get_default_theme_renderables(idx: usize, map: &Map) -> (rltk::FontCharType, RGB, RGB) { + let glyph: rltk::FontCharType; #[allow(unused_assignments)] let mut fg: RGB = RGB::new(); #[allow(unused_assignments)] let mut bg: RGB = RGB::new(); - let mut offsets: (i32, i32, i32) = (0, 0, 0); - let mut bg_offsets: (i32, i32, i32) = (-1, -1, -1); match map.tiles[idx] { - TileType::Floor => { glyph = to_cp437(FLOOR_GLYPH); fg = RGB::named(FLOOR_COLOUR); bg = RGB::named(DEFAULT_BG_COLOUR); offsets = FLOOR_OFFSETS; } - TileType::WoodFloor => { glyph = to_cp437(WOOD_FLOOR_GLYPH); bg = RGB::named(WOOD_FLOOR_COLOUR); offsets = WOOD_FLOOR_OFFSETS; } - TileType::Fence => { glyph = to_cp437(FENCE_GLYPH); fg = RGB::named(FENCE_FG_COLOUR); bg = RGB::named(FENCE_COLOUR); offsets = FENCE_OFFSETS; } - TileType::Wall => { let x = idx as i32 % map.width; let y = idx as i32 / map.width; glyph = wall_glyph(&*map, x, y, debug); fg = RGB::named(WALL_COLOUR); bg = RGB::named(DEFAULT_BG_COLOUR); offsets = WALL_OFFSETS; bg_offsets = DEFAULT_BG_OFFSETS } - TileType::DownStair => { glyph = to_cp437(DOWN_STAIR_GLYPH); fg = RGB::named(STAIR_COLOUR); bg = RGB::named(DEFAULT_BG_COLOUR); offsets = STAIR_OFFSETS;} - TileType::UpStair => { glyph = to_cp437(UP_STAIR_GLYPH); fg = RGB::named(STAIR_COLOUR); bg = RGB::named(DEFAULT_BG_COLOUR); offsets = STAIR_OFFSETS; } - TileType::Bridge => { glyph = to_cp437(BRIDGE_GLYPH); bg = RGB::named(BRIDGE_COLOUR); offsets = BRIDGE_OFFSETS; } - TileType::Gravel => { glyph = to_cp437(GRAVEL_GLYPH); bg = RGB::named(GRAVEL_COLOUR); offsets = GRAVEL_OFFSETS;} - TileType::Road => { glyph = to_cp437(ROAD_GLYPH); bg = RGB::named(ROAD_COLOUR); offsets = ROAD_OFFSETS;} - TileType::Grass => { glyph = to_cp437(GRASS_GLYPH); bg = RGB::named(GRASS_COLOUR); offsets = GRASS_OFFSETS; } - TileType::Foliage => { glyph = to_cp437(FOLIAGE_GLYPH); bg = RGB::named(FOLIAGE_COLOUR); offsets = FOLIAGE_OFFSETS; } - TileType::HeavyFoliage => { glyph = to_cp437(HEAVY_FOLIAGE_GLYPH); bg = RGB::named(HEAVY_FOLIAGE_COLOUR); offsets = HEAVY_FOLIAGE_OFFSETS; } - TileType::Sand => { glyph = to_cp437(SAND_GLYPH); bg = RGB::named(SAND_COLOUR); offsets = SAND_OFFSETS; } - TileType::ShallowWater => { glyph = to_cp437(SHALLOW_WATER_GLYPH); bg = RGB::named(SHALLOW_WATER_COLOUR); offsets = SHALLOW_WATER_OFFSETS; } - TileType::DeepWater => { glyph = to_cp437(DEEP_WATER_GLYPH); bg = RGB::named(DEEP_WATER_COLOUR); offsets = DEEP_WATER_OFFSETS; } - TileType::Bars => { glyph = to_cp437(BARS_GLYPH); fg = RGB::named(BARS_COLOUR); bg = RGB::named(FLOOR_COLOUR); } - TileType::ImpassableMountain => { glyph = to_cp437(IMPASSABLE_MOUNTAIN_GLYPH); bg = RGB::named(IMPASSABLE_MOUNTAIN_COLOUR); offsets = IMPASSABLE_MOUNTAIN_OFFSETS } - TileType::ToOvermap(_) => { glyph = to_cp437(TO_OVERMAP_GLYPH); fg = RGB::named(TO_OVERMAP_COLOUR); bg = RGB::named(GRASS_COLOUR); } - TileType::ToLocal(_) => { glyph = to_cp437(TO_TOWN_GLYPH); fg = RGB::named(TO_TOWN_COLOUR); bg = RGB::named(GRASS_COLOUR); } + TileType::Floor => { glyph = rltk::to_cp437(FLOOR_GLYPH); fg = RGB::named(FLOOR_COLOUR); bg = RGB::named(DEFAULT_BG_COLOUR); } + TileType::WoodFloor => { glyph = rltk::to_cp437(WOOD_FLOOR_GLYPH); bg = RGB::named(WOOD_FLOOR_COLOUR); } + TileType::Fence => { glyph = rltk::to_cp437(FENCE_GLYPH); fg = RGB::named(FENCE_FG_COLOUR); bg = RGB::named(FENCE_COLOUR); } + TileType::Wall => { let x = idx as i32 % map.width; let y = idx as i32 / map.width; glyph = wall_glyph(&*map, x, y); fg = RGB::named(WALL_COLOUR); bg = RGB::named(DEFAULT_BG_COLOUR); } + TileType::DownStair => { glyph = rltk::to_cp437(DOWN_STAIR_GLYPH); fg = RGB::named(STAIR_COLOUR); bg = RGB::named(DEFAULT_BG_COLOUR); } + TileType::UpStair => { glyph = rltk::to_cp437(UP_STAIR_GLYPH); fg = RGB::named(STAIR_COLOUR); bg = RGB::named(DEFAULT_BG_COLOUR); } + TileType::Bridge => { glyph = rltk::to_cp437(BRIDGE_GLYPH); bg = RGB::named(BRIDGE_COLOUR); } + TileType::Gravel => { glyph = rltk::to_cp437(GRAVEL_GLYPH); bg = RGB::named(GRAVEL_COLOUR); } + TileType::Road => { glyph = rltk::to_cp437(ROAD_GLYPH); bg = RGB::named(ROAD_COLOUR); } + TileType::Grass => { glyph = rltk::to_cp437(GRASS_GLYPH); bg = RGB::named(GRASS_COLOUR); } + TileType::Foliage => { glyph = rltk::to_cp437(FOLIAGE_GLYPH); bg = RGB::named(FOLIAGE_COLOUR); } + TileType::HeavyFoliage => { glyph = rltk::to_cp437(HEAVY_FOLIAGE_GLYPH); bg = RGB::named(HEAVY_FOLIAGE_COLOUR); } + TileType::Sand => { glyph = rltk::to_cp437(SAND_GLYPH); bg = RGB::named(SAND_COLOUR); } + TileType::ShallowWater => { glyph = rltk::to_cp437(SHALLOW_WATER_GLYPH); bg = RGB::named(SHALLOW_WATER_COLOUR); } + TileType::DeepWater => { glyph = rltk::to_cp437(DEEP_WATER_GLYPH); bg = RGB::named(DEEP_WATER_COLOUR); } + TileType::Bars => { glyph = rltk::to_cp437(BARS_GLYPH); fg = RGB::named(BARS_COLOUR); bg = RGB::named(FLOOR_COLOUR); } } - if bg_offsets == (-1, -1, -1) { - bg_offsets = offsets; - } - return (glyph, fg, bg, offsets, bg_offsets); + return (glyph, fg, bg); } #[rustfmt::skip] -fn get_forest_theme_renderables(idx:usize, map: &Map, debug: Option) -> (FontCharType, RGB, RGB, (i32, i32, i32), (i32, i32, i32)) { +fn get_forest_theme_renderables(idx:usize, map: &Map) -> (rltk::FontCharType, RGB, RGB) { let glyph; #[allow(unused_assignments)] let mut fg = RGB::new(); #[allow(unused_assignments)] let mut bg = RGB::new(); - let mut offsets: (i32, i32, i32) = (0, 0, 0); - let mut bg_offsets: (i32, i32, i32) = (-1, -1, -1); match map.tiles[idx] { - TileType::Wall => { glyph = to_cp437(FOREST_WALL_GLYPH); fg = RGB::named(FOREST_WALL_COLOUR); bg = RGB::named(GRASS_COLOUR); offsets = GRASS_OFFSETS; } - TileType::Road => { glyph = to_cp437(ROAD_GLYPH); bg = RGB::named(ROAD_COLOUR); } - TileType::ShallowWater => { glyph = to_cp437(SHALLOW_WATER_GLYPH); bg = RGB::named(SHALLOW_WATER_COLOUR); offsets = SHALLOW_WATER_OFFSETS; } - _ => { (glyph, fg, _, offsets, _) = get_default_theme_renderables(idx, map, debug); bg = RGB::named(GRASS_COLOUR); bg_offsets = GRASS_OFFSETS; } + TileType::Wall => { glyph = rltk::to_cp437(FOREST_WALL_GLYPH); fg = RGB::named(FOREST_WALL_COLOUR); bg = RGB::named(GRASS_COLOUR) } + TileType::Road => { glyph = rltk::to_cp437(ROAD_GLYPH); bg = RGB::named(ROAD_COLOUR); } + TileType::ShallowWater => { glyph = rltk::to_cp437(SHALLOW_WATER_GLYPH); bg = RGB::named(SHALLOW_WATER_COLOUR); } + _ => { (glyph, fg, _) = get_default_theme_renderables(idx, map); bg = RGB::named(GRASS_COLOUR) } } - if bg_offsets == (-1, -1, -1) { - bg_offsets = offsets; - } - return (glyph, fg, bg, offsets, bg_offsets); + + (glyph, fg, bg) } -fn is_revealed_and_wall(map: &Map, x: i32, y: i32, debug: Option) -> bool { +fn is_revealed_and_wall(map: &Map, x: i32, y: i32) -> bool { let idx = map.xy_idx(x, y); - map.tiles[idx] == TileType::Wall && - (if debug.is_none() { map.revealed_tiles[idx] } else { true }) + map.tiles[idx] == TileType::Wall && map.revealed_tiles[idx] } -fn wall_glyph(map: &Map, x: i32, y: i32, debug: Option) -> FontCharType { - if - x < 1 || - x > map.width - 2 || - y < 1 || - y > map.height - (2 as i32) || - !CONFIG.visuals.use_bitset_walls - { +fn wall_glyph(map: &Map, x: i32, y: i32) -> rltk::FontCharType { + if x < 1 || x > map.width - 2 || y < 1 || y > map.height - 2 as i32 { return 35; } - let mut mask: u8 = 0; let diagonals_matter: Vec = vec![7, 11, 13, 14, 15]; - if is_revealed_and_wall(map, x, y - 1, debug) { + if is_revealed_and_wall(map, x, y - 1) { // N mask += 1; } - if is_revealed_and_wall(map, x, y + 1, debug) { + if is_revealed_and_wall(map, x, y + 1) { // S mask += 2; } - if is_revealed_and_wall(map, x - 1, y, debug) { + if is_revealed_and_wall(map, x - 1, y) { // W mask += 4; } - if is_revealed_and_wall(map, x + 1, y, debug) { + if is_revealed_and_wall(map, x + 1, y) { // E mask += 8; } if diagonals_matter.contains(&mask) { - if is_revealed_and_wall(map, x + 1, y - 1, debug) { + if is_revealed_and_wall(map, x + 1, y - 1) { // Top right mask += 16; } - if is_revealed_and_wall(map, x - 1, y - 1, debug) { + if is_revealed_and_wall(map, x - 1, y - 1) { // Top left mask += 32; } - if is_revealed_and_wall(map, x + 1, y + 1, debug) { + if is_revealed_and_wall(map, x + 1, y + 1) { // Bottom right mask += 64; } - if is_revealed_and_wall(map, x - 1, y + 1, debug) { + if is_revealed_and_wall(map, x - 1, y + 1) { // Bottom left mask += 128; } } match mask { - 0 => 254, // ■ (254) square pillar; but maybe ○ (9) looks better - 1 => 186, // Wall only to the north - 2 => 186, // Wall only to the south - 3 => 186, // Wall to the north and south - 4 => 205, // Wall only to the west - 5 => 188, // Wall to the north and west - 6 => 187, // Wall to the south and west - 7 => 185, // Wall to the north, south and west - 8 => 205, // Wall only to the east - 9 => 200, // Wall to the north and east + 0 => 254, // ■ (254) square pillar; but maybe ○ (9) looks better + 1 => 186, // Wall only to the north + 2 => 186, // Wall only to the south + 3 => 186, // Wall to the north and south + 4 => 205, // Wall only to the west + 5 => 188, // Wall to the north and west + 6 => 187, // Wall to the south and west + 7 => 185, // Wall to the north, south and west + 8 => 205, // Wall only to the east + 9 => 200, // Wall to the north and east 10 => 201, // Wall to the south and east 11 => 204, // Wall to the north, south and east 12 => 205, // Wall to the east and west @@ -261,7 +187,7 @@ fn wall_glyph(map: &Map, x: i32, y: i32, debug: Option) -> FontCharType { 205 => 202, 175 => 204, 203 => 204, - 61 => 205, // NEW cases + 61 => 205, // NEW cases 125 => 205, // NEW cases 189 => 205, // NEW cases 206 => 205, @@ -271,7 +197,7 @@ fn wall_glyph(map: &Map, x: i32, y: i32, debug: Option) -> FontCharType { 253 => 205, 254 => 205, 167 => 186, // NSW, NW, SW - 91 => 186, // NSE, NE, SE + 91 => 186, // NSE, NE, SE 183 => 186, // NSW, NW, SW, NE 123 => 186, // NSE, NE, SE, NW 231 => 186, // NSW, NW, SW, SE @@ -282,69 +208,36 @@ fn wall_glyph(map: &Map, x: i32, y: i32, debug: Option) -> FontCharType { 191 => 201, // Everything except NW 223 => 188, // Everything except SE 239 => 200, // Everything except SW - _ => 35, // We missed one? + _ => 35, // We missed one? } } -fn apply_colour_offset( - mut rgb: RGB, - map: &Map, - idx: usize, - offset: (i32, i32, i32), - fg: bool -) -> RGB { - let offset_mod = if fg { map.colour_offset[idx].0 } else { map.colour_offset[idx].1 }; - let offset = ( - (offset.0 as f32) * offset_mod.0, - (offset.1 as f32) * offset_mod.1, - (offset.2 as f32) * offset_mod.2, - ); - rgb = add_i32_offsets(rgb, offset); - return rgb; +fn apply_colour_offset(mut fg: RGB, mut bg: RGB, map: &Map, idx: usize) -> (RGB, RGB) { + let offsets = map.colour_offset[idx]; + fg = multiply_by_float(fg.add(map.additional_fg_offset), offsets); + bg = multiply_by_float(bg, offsets); + return (fg, bg); +} + +fn darken_if_not_visible(mut fg: RGB, mut bg: RGB, map: &Map, idx: usize) -> (RGB, RGB) { + if !map.visible_tiles[idx] { + fg = fg.mul(NON_VISIBLE_MULTIPLIER); + bg = bg.mul(NON_VISIBLE_MULTIPLIER); + } + return (fg, bg); } fn apply_bloodstain_if_necessary(mut bg: RGB, map: &Map, idx: usize) -> RGB { - if map.bloodstains.contains_key(&idx) { - bg = bg.add(map.bloodstains[&idx]); + if map.bloodstains.contains(&idx) { + bg = bg.add(RGB::named(BLOODSTAIN_COLOUR)); } return bg; } -pub fn add_i32_offsets(rgb: RGB, offsets: (f32, f32, f32)) -> RGB { - let r = rgb.r + (offsets.0 as f32) / 255.0; - let g = rgb.g + (offsets.1 as f32) / 255.0; - let b = rgb.b + (offsets.2 as f32) / 255.0; - - return RGB::from_f32(r, g, b); -} - -pub fn multiply_by_float(rgb: RGB, offsets: (f32, f32, f32)) -> RGB { +pub fn multiply_by_float(rgb: rltk::RGB, offsets: (f32, f32, f32)) -> RGB { let r = rgb.r * offsets.0; let g = rgb.g * offsets.1; let b = rgb.b * offsets.2; - return RGB::from_f32(r, g, b); -} - -fn darken_by_distance(pos: Point, other_pos: Point) -> f32 { - let distance = DistanceAlg::Pythagoras.distance2d(pos, other_pos) as f32; // Get distance in tiles. - let interp_factor = - (distance - START_DARKEN_AT_N_TILES) / - ((crate::data::entity::DEFAULT_VIEWSHED_STANDARD as f32) - START_DARKEN_AT_N_TILES); - let interp_factor = interp_factor.max(0.0).min(1.0); // Clamp [0-1] - let result = - 1.0 - - interp_factor * - (1.0 - - (if CONFIG.visuals.with_scanlines { - MAX_DARKENING_IF_SCANLINES - } else { - MAX_DARKENING - })); - return result; -} - -fn brighten_by(mut rgb: RGB, amount: f32) -> RGB { - rgb = rgb.add(RGB::from_f32(amount, amount, amount)); - return rgb; + return rltk::RGB::from_f32(r, g, b); } diff --git a/src/map/tiletype.rs b/src/map/tiletype.rs index f451c9b..8d9c47c 100644 --- a/src/map/tiletype.rs +++ b/src/map/tiletype.rs @@ -1,9 +1,8 @@ -use serde::{ Deserialize, Serialize }; +use serde::{Deserialize, Serialize}; -#[derive(PartialEq, Eq, Hash, Copy, Clone, Serialize, Deserialize, Debug)] +#[derive(PartialEq, Eq, Hash, Copy, Clone, Serialize, Deserialize)] pub enum TileType { // Walls (opaque) - ImpassableMountain, Wall, // Impassable (transparent) DeepWater, @@ -23,54 +22,38 @@ pub enum TileType { // Stairs (changes floor) DownStair, UpStair, - // To/From Overmap - ids are in src/data/ids.rs, are used in try_change_level() in src/player.rs - ToOvermap(i32), - ToLocal(i32), } + pub fn tile_walkable(tt: TileType) -> bool { match tt { - TileType::ImpassableMountain | TileType::Wall | TileType::DeepWater | TileType::Fence | TileType::Bars => false, - _ => true, + TileType::Floor + | TileType::WoodFloor + | TileType::Gravel + | TileType::Road + | TileType::Grass + | TileType::Foliage + | TileType::HeavyFoliage + | TileType::Sand + | TileType::ShallowWater + | TileType::Bridge + | TileType::DownStair + | TileType::UpStair => true, + _ => false, } } + pub fn tile_opaque(tt: TileType) -> bool { match tt { - TileType::ImpassableMountain => true, TileType::Wall => true, _ => false, } } + pub fn tile_cost(tt: TileType) -> f32 { match tt { - TileType::Road => 0.75, - TileType::Grass => 1.2, - TileType::ShallowWater => 1.5, + TileType::Road => 0.8, + TileType::Grass => 1.1, + TileType::ShallowWater => 1.2, _ => 1.0, } } -pub fn get_dest(this_tile: TileType, backtracking: bool) -> Destination { - let result = if !backtracking { - match this_tile { - // If on downstair, GOTO next level, and end up on an upstair - TileType::DownStair => Destination::NextLevel, - // If on overmap ToLocal tile, GOTO local map, and end up on an overmap ToOvermap tile with corresponding ID - TileType::ToLocal(id) => Destination::ToOvermap(id), - _ => Destination::None, - } - } else { - match this_tile { - TileType::UpStair => Destination::PreviousLevel, - TileType::ToOvermap(id) => Destination::ToLocal(id), - _ => Destination::None, - } - }; - return result; -} - -pub enum Destination { - PreviousLevel, - NextLevel, - ToOvermap(i32), - ToLocal(i32), - None, -} diff --git a/src/map_builders/area_starting_points.rs b/src/map_builders/area_starting_points.rs index 9707ed6..aea8996 100644 --- a/src/map_builders/area_starting_points.rs +++ b/src/map_builders/area_starting_points.rs @@ -1,5 +1,5 @@ -use super::{ BuilderMap, MetaMapBuilder, Position }; -use bracket_lib::prelude::*; +use super::{BuilderMap, MetaMapBuilder, Position}; +use rltk::RandomNumberGenerator; #[allow(dead_code)] pub enum XStart { @@ -21,7 +21,7 @@ pub struct AreaStartingPosition { } impl MetaMapBuilder for AreaStartingPosition { - fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { self.build(rng, build_data); } } @@ -37,26 +37,14 @@ impl AreaStartingPosition { let seed_y; match self.x { - XStart::LEFT => { - seed_x = 1; - } - XStart::CENTRE => { - seed_x = build_data.map.width / 2; - } - XStart::RIGHT => { - seed_x = build_data.map.width - 2; - } + XStart::LEFT => seed_x = 1, + XStart::CENTRE => seed_x = build_data.map.width / 2, + XStart::RIGHT => seed_x = build_data.map.width - 2, } match self.y { - YStart::TOP => { - seed_y = 1; - } - YStart::CENTRE => { - seed_y = build_data.map.height / 2; - } - YStart::BOTTOM => { - seed_y = build_data.map.height - 2; - } + YStart::TOP => seed_y = 1, + YStart::CENTRE => seed_y = build_data.map.height / 2, + YStart::BOTTOM => seed_y = build_data.map.height - 2, } let mut available_floors: Vec<(usize, f32)> = Vec::new(); @@ -64,24 +52,21 @@ impl AreaStartingPosition { if crate::tile_walkable(*tiletype) { available_floors.push(( idx, - DistanceAlg::PythagorasSquared.distance2d( - Point::new( - (idx as i32) % build_data.map.width, - (idx as i32) / build_data.map.width - ), - Point::new(seed_x, seed_y) + rltk::DistanceAlg::PythagorasSquared.distance2d( + rltk::Point::new(idx as i32 % build_data.map.width, idx as i32 / build_data.map.width), + rltk::Point::new(seed_x, seed_y), ), )); } } if available_floors.is_empty() { - unreachable!("No valid floors to start on."); + panic!("No valid floors to start on"); } available_floors.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap()); - let start_x = (available_floors[0].0 as i32) % build_data.map.width; - let start_y = (available_floors[0].0 as i32) / build_data.map.width; + let start_x = available_floors[0].0 as i32 % build_data.map.width; + let start_y = available_floors[0].0 as i32 / build_data.map.width; build_data.starting_position = Some(Position { x: start_x, y: start_y }); } diff --git a/src/map_builders/bsp_dungeon.rs b/src/map_builders/bsp_dungeon.rs index 5180d29..8746400 100644 --- a/src/map_builders/bsp_dungeon.rs +++ b/src/map_builders/bsp_dungeon.rs @@ -1,5 +1,5 @@ -use super::{ BuilderMap, InitialMapBuilder, Rect, TileType }; -use bracket_lib::prelude::*; +use super::{BuilderMap, InitialMapBuilder, Rect, TileType}; +use rltk::RandomNumberGenerator; pub struct BspDungeonBuilder { rects: Vec, @@ -7,7 +7,7 @@ pub struct BspDungeonBuilder { impl InitialMapBuilder for BspDungeonBuilder { #[allow(dead_code)] - fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { self.build(rng, build_data); } } @@ -21,7 +21,7 @@ impl BspDungeonBuilder { fn build(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { let mut rooms: Vec = Vec::new(); self.rects.clear(); - self.rects.push(Rect::with_size(2, 2, build_data.map.width - 5, build_data.map.height - 5)); // Start with a single map-sized rectangle + self.rects.push(Rect::new(2, 2, build_data.map.width - 5, build_data.map.height - 5)); // Start with a single map-sized rectangle let first_room = self.rects[0]; self.add_subrects(first_room); // Divide the first room @@ -49,12 +49,10 @@ impl BspDungeonBuilder { let half_width = i32::max(width / 2, 1); let half_height = i32::max(height / 2, 1); - self.rects.push(Rect::with_size(rect.x1, rect.y1, half_width, half_height)); - self.rects.push(Rect::with_size(rect.x1, rect.y1 + half_height, half_width, half_height)); - self.rects.push(Rect::with_size(rect.x1 + half_width, rect.y1, half_width, half_height)); - self.rects.push( - Rect::with_size(rect.x1 + half_width, rect.y1 + half_height, half_width, half_height) - ); + self.rects.push(Rect::new(rect.x1, rect.y1, half_width, half_height)); + self.rects.push(Rect::new(rect.x1, rect.y1 + half_height, half_width, half_height)); + self.rects.push(Rect::new(rect.x1 + half_width, rect.y1, half_width, half_height)); + self.rects.push(Rect::new(rect.x1 + half_width, rect.y1 + half_height, half_width, half_height)); } fn get_random_rect(&mut self, rng: &mut RandomNumberGenerator) -> Rect { diff --git a/src/map_builders/bsp_interior.rs b/src/map_builders/bsp_interior.rs index 1b81753..a2ab03d 100644 --- a/src/map_builders/bsp_interior.rs +++ b/src/map_builders/bsp_interior.rs @@ -1,5 +1,5 @@ -use super::{ draw_corridor, BuilderMap, InitialMapBuilder, Rect, TileType }; -use bracket_lib::prelude::*; +use super::{draw_corridor, BuilderMap, InitialMapBuilder, Rect, TileType}; +use rltk::RandomNumberGenerator; const MIN_ROOM_SIZE: i32 = 8; @@ -9,7 +9,7 @@ pub struct BspInteriorBuilder { impl InitialMapBuilder for BspInteriorBuilder { #[allow(dead_code)] - fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { self.build(rng, build_data); } } @@ -23,7 +23,7 @@ impl BspInteriorBuilder { fn build(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { let mut rooms: Vec = Vec::new(); self.rects.clear(); - self.rects.push(Rect::with_size(1, 1, build_data.map.width - 2, build_data.map.height - 2)); // Start with a single map-sized rectangle + self.rects.push(Rect::new(1, 1, build_data.map.width - 2, build_data.map.height - 2)); // Start with a single map-sized rectangle let first_room = self.rects[0]; self.add_subrects(first_room, rng); // Divide the first room @@ -36,10 +36,7 @@ impl BspInteriorBuilder { for y in room.y1..room.y2 { for x in room.x1..room.x2 { let idx = build_data.map.xy_idx(x, y); - if - idx > 0 && - idx < ((build_data.map.width * build_data.map.height - 1) as usize) - { + if idx > 0 && idx < ((build_data.map.width * build_data.map.height) - 1) as usize { build_data.map.tiles[idx] = TileType::Floor; } } @@ -53,10 +50,8 @@ impl BspInteriorBuilder { let next_room = rooms[i + 1]; let start_x = room.x1 + (rng.roll_dice(1, i32::abs(room.x1 - room.x2)) - 1); let start_y = room.y1 + (rng.roll_dice(1, i32::abs(room.y1 - room.y2)) - 1); - let end_x = - next_room.x1 + (rng.roll_dice(1, i32::abs(next_room.x1 - next_room.x2)) - 1); - let end_y = - next_room.y1 + (rng.roll_dice(1, i32::abs(next_room.y1 - next_room.y2)) - 1); + let end_x = next_room.x1 + (rng.roll_dice(1, i32::abs(next_room.x1 - next_room.x2)) - 1); + let end_y = next_room.y1 + (rng.roll_dice(1, i32::abs(next_room.y1 - next_room.y2)) - 1); draw_corridor(&mut build_data.map, start_x, start_y, end_x, end_y); build_data.take_snapshot(); } @@ -80,24 +75,24 @@ impl BspInteriorBuilder { if split <= 2 { // Horizontal split - let h1 = Rect::with_size(rect.x1, rect.y1, half_width - 1, height); + let h1 = Rect::new(rect.x1, rect.y1, half_width - 1, height); self.rects.push(h1); if half_width > MIN_ROOM_SIZE { self.add_subrects(h1, rng); } - let h2 = Rect::with_size(rect.x1 + half_width, rect.y1, half_width, height); + let h2 = Rect::new(rect.x1 + half_width, rect.y1, half_width, height); self.rects.push(h2); if half_width > MIN_ROOM_SIZE { self.add_subrects(h2, rng); } } else { // Vertical split - let v1 = Rect::with_size(rect.x1, rect.y1, width, half_height - 1); + let v1 = Rect::new(rect.x1, rect.y1, width, half_height - 1); self.rects.push(v1); if half_height > MIN_ROOM_SIZE { self.add_subrects(v1, rng); } - let v2 = Rect::with_size(rect.x1, rect.y1 + half_height, width, half_height); + let v2 = Rect::new(rect.x1, rect.y1 + half_height, width, half_height); self.rects.push(v2); if half_height > MIN_ROOM_SIZE { self.add_subrects(v2, rng); diff --git a/src/map_builders/cellular_automata.rs b/src/map_builders/cellular_automata.rs index 69f2015..17dd869 100644 --- a/src/map_builders/cellular_automata.rs +++ b/src/map_builders/cellular_automata.rs @@ -1,20 +1,18 @@ -use super::{ BuilderMap, InitialMapBuilder, MetaMapBuilder, TileType }; -use bracket_lib::prelude::*; +use super::{BuilderMap, InitialMapBuilder, MetaMapBuilder, TileType}; +use rltk::RandomNumberGenerator; -pub struct CellularAutomataBuilder { - floor_tile: TileType, -} +pub struct CellularAutomataBuilder {} impl InitialMapBuilder for CellularAutomataBuilder { #[allow(dead_code)] - fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { self.build(rng, build_data); } } impl MetaMapBuilder for CellularAutomataBuilder { #[allow(dead_code)] - fn build_map(&mut self, _rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + fn build_map(&mut self, _rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { self.apply_iteration(build_data); } } @@ -22,10 +20,7 @@ impl MetaMapBuilder for CellularAutomataBuilder { impl CellularAutomataBuilder { #[allow(dead_code)] pub fn new() -> Box { - Box::new(CellularAutomataBuilder { floor_tile: TileType::Floor }) - } - pub fn floor(floor_tile: TileType) -> Box { - Box::new(CellularAutomataBuilder { floor_tile }) + Box::new(CellularAutomataBuilder {}) } #[allow(clippy::map_entry)] @@ -36,9 +31,9 @@ impl CellularAutomataBuilder { let roll = rng.roll_dice(1, 100); let idx = build_data.map.xy_idx(x, y); if roll > 55 { - build_data.map.tiles[idx] = TileType::Floor; + build_data.map.tiles[idx] = TileType::Floor } else { - build_data.map.tiles[idx] = TileType::Wall; + build_data.map.tiles[idx] = TileType::Wall } } } @@ -63,41 +58,29 @@ impl CellularAutomataBuilder { if build_data.map.tiles[idx + 1] == TileType::Wall { neighbors += 1; } - if build_data.map.tiles[idx - (build_data.map.width as usize)] == TileType::Wall { + if build_data.map.tiles[idx - build_data.map.width as usize] == TileType::Wall { neighbors += 1; } - if build_data.map.tiles[idx + (build_data.map.width as usize)] == TileType::Wall { + if build_data.map.tiles[idx + build_data.map.width as usize] == TileType::Wall { neighbors += 1; } - if - build_data.map.tiles[idx - ((build_data.map.width as usize) - 1)] == - TileType::Wall - { + if build_data.map.tiles[idx - (build_data.map.width as usize - 1)] == TileType::Wall { neighbors += 1; } - if - build_data.map.tiles[idx - ((build_data.map.width as usize) + 1)] == - TileType::Wall - { + if build_data.map.tiles[idx - (build_data.map.width as usize + 1)] == TileType::Wall { neighbors += 1; } - if - build_data.map.tiles[idx + ((build_data.map.width as usize) - 1)] == - TileType::Wall - { + if build_data.map.tiles[idx + (build_data.map.width as usize - 1)] == TileType::Wall { neighbors += 1; } - if - build_data.map.tiles[idx + ((build_data.map.width as usize) + 1)] == - TileType::Wall - { + if build_data.map.tiles[idx + (build_data.map.width as usize + 1)] == TileType::Wall { neighbors += 1; } if neighbors > 4 || neighbors == 0 { newtiles[idx] = TileType::Wall; } else { - newtiles[idx] = self.floor_tile; + newtiles[idx] = TileType::Floor; } } } diff --git a/src/map_builders/common.rs b/src/map_builders/common.rs index ffed6fa..80c5343 100644 --- a/src/map_builders/common.rs +++ b/src/map_builders/common.rs @@ -1,5 +1,5 @@ -use super::{ Map, Rect, TileType }; -use std::cmp::{ max, min }; +use super::{Map, Rect, TileType}; +use std::cmp::{max, min}; #[allow(dead_code)] pub fn apply_room_to_map(map: &mut Map, room: &Rect) { diff --git a/src/map_builders/cull_unreachable.rs b/src/map_builders/cull_unreachable.rs index b53559e..4b2213b 100644 --- a/src/map_builders/cull_unreachable.rs +++ b/src/map_builders/cull_unreachable.rs @@ -1,11 +1,10 @@ -use super::{ BuilderMap, MetaMapBuilder, TileType }; -use crate::tile_walkable; -use bracket_lib::prelude::*; +use super::{BuilderMap, MetaMapBuilder, TileType}; +use rltk::RandomNumberGenerator; pub struct CullUnreachable {} impl MetaMapBuilder for CullUnreachable { - fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { self.build(rng, build_data); } } @@ -21,15 +20,15 @@ impl CullUnreachable { let start_idx = build_data.map.xy_idx(starting_pos.x, starting_pos.y); build_data.map.populate_blocked(); let map_starts: Vec = vec![start_idx]; - let dijkstra_map = DijkstraMap::new( + let dijkstra_map = rltk::DijkstraMap::new( build_data.map.width as usize, build_data.map.height as usize, &map_starts, &build_data.map, - 1000.0 + 1000.0, ); for (i, tile) in build_data.map.tiles.iter_mut().enumerate() { - if tile_walkable(*tile) { + if *tile == TileType::Floor { let distance_to_start = dijkstra_map.map[i]; // We can't get to this tile - so we'll make it a wall if distance_to_start == std::f32::MAX { diff --git a/src/map_builders/distant_exit.rs b/src/map_builders/distant_exit.rs index 771e0b2..74873ec 100644 --- a/src/map_builders/distant_exit.rs +++ b/src/map_builders/distant_exit.rs @@ -1,11 +1,10 @@ -use super::{ BuilderMap, MetaMapBuilder, TileType }; -use crate::tile_walkable; -use bracket_lib::prelude::*; +use super::{BuilderMap, MetaMapBuilder, TileType}; +use rltk::RandomNumberGenerator; pub struct DistantExit {} impl MetaMapBuilder for DistantExit { - fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { self.build(rng, build_data); } } @@ -21,16 +20,16 @@ impl DistantExit { let start_idx = build_data.map.xy_idx(starting_pos.x, starting_pos.y); build_data.map.populate_blocked(); let map_starts: Vec = vec![start_idx]; - let dijkstra_map = DijkstraMap::new( + let dijkstra_map = rltk::DijkstraMap::new( build_data.map.width as usize, build_data.map.height as usize, &map_starts, &build_data.map, - 1000.0 + 1000.0, ); let mut exit_tile = (0, 0.0f32); for (i, tile) in build_data.map.tiles.iter_mut().enumerate() { - if tile_walkable(*tile) { + if *tile == TileType::Floor { let distance_to_start = dijkstra_map.map[i]; if distance_to_start != std::f32::MAX { // If it is further away than our current exit candidate, move the exit diff --git a/src/map_builders/dla.rs b/src/map_builders/dla.rs index d1b7a4a..2cb5fa3 100644 --- a/src/map_builders/dla.rs +++ b/src/map_builders/dla.rs @@ -1,5 +1,5 @@ -use super::{ paint, BuilderMap, InitialMapBuilder, MetaMapBuilder, Position, Symmetry, TileType }; -use bracket_lib::prelude::*; +use super::{paint, BuilderMap, InitialMapBuilder, MetaMapBuilder, Position, Symmetry, TileType}; +use rltk::RandomNumberGenerator; #[derive(PartialEq, Copy, Clone)] #[allow(dead_code)] @@ -18,14 +18,14 @@ pub struct DLABuilder { impl InitialMapBuilder for DLABuilder { #[allow(dead_code)] - fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { self.build(rng, build_data); } } impl MetaMapBuilder for DLABuilder { #[allow(dead_code)] - fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { self.build(rng, build_data); } } @@ -94,25 +94,19 @@ impl DLABuilder { #[allow(clippy::map_entry)] fn build(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { // Carve a starting seed - let starting_position = Position { - x: build_data.map.width / 2, - y: build_data.map.height / 2, - }; + let starting_position = Position { x: build_data.map.width / 2, y: build_data.map.height / 2 }; let start_idx = build_data.map.xy_idx(starting_position.x, starting_position.y); build_data.take_snapshot(); build_data.map.tiles[start_idx] = TileType::Floor; build_data.map.tiles[start_idx - 1] = TileType::Floor; build_data.map.tiles[start_idx + 1] = TileType::Floor; - build_data.map.tiles[start_idx - (build_data.map.width as usize)] = TileType::Floor; - build_data.map.tiles[start_idx + (build_data.map.width as usize)] = TileType::Floor; + build_data.map.tiles[start_idx - build_data.map.width as usize] = TileType::Floor; + build_data.map.tiles[start_idx + build_data.map.width as usize] = TileType::Floor; // Random walker let total_tiles = build_data.map.width * build_data.map.height; - let desired_floor_tiles = (self.floor_percent * (total_tiles as f32)) as usize; - let mut floor_tile_count = build_data.map.tiles - .iter() - .filter(|a| **a == TileType::Floor) - .count(); + let desired_floor_tiles = (self.floor_percent * total_tiles as f32) as usize; + let mut floor_tile_count = build_data.map.tiles.iter().filter(|a| **a == TileType::Floor).count(); while floor_tile_count < desired_floor_tiles { match self.algorithm { DLAAlgorithm::WalkInwards => { @@ -192,10 +186,10 @@ impl DLABuilder { let mut prev_y = digger_y; let mut digger_idx = build_data.map.xy_idx(digger_x, digger_y); - let mut path = line2d( - LineAlg::Bresenham, - Point::new(digger_x, digger_y), - Point::new(starting_position.x, starting_position.y) + let mut path = rltk::line2d( + rltk::LineAlg::Bresenham, + rltk::Point::new(digger_x, digger_y), + rltk::Point::new(starting_position.x, starting_position.y), ); while build_data.map.tiles[digger_idx] == TileType::Wall && !path.is_empty() { @@ -212,10 +206,7 @@ impl DLABuilder { build_data.take_snapshot(); - floor_tile_count = build_data.map.tiles - .iter() - .filter(|a| **a == TileType::Floor) - .count(); + floor_tile_count = build_data.map.tiles.iter().filter(|a| **a == TileType::Floor).count(); } } } diff --git a/src/map_builders/door_placement.rs b/src/map_builders/door_placement.rs index 53f5967..5c26eb9 100644 --- a/src/map_builders/door_placement.rs +++ b/src/map_builders/door_placement.rs @@ -1,11 +1,11 @@ -use super::{ BuilderMap, MetaMapBuilder, TileType }; -use bracket_lib::prelude::*; +use super::{BuilderMap, MetaMapBuilder, TileType}; +use rltk::RandomNumberGenerator; pub struct DoorPlacement {} impl MetaMapBuilder for DoorPlacement { #[allow(dead_code)] - fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { self.doors(rng, build_data); } } @@ -30,11 +30,7 @@ impl DoorPlacement { // There are no corridors - scan for possible places let tiles = build_data.map.tiles.clone(); for (i, tile) in tiles.iter().enumerate() { - if - *tile == TileType::Floor && - self.door_possible(build_data, i) && - rng.roll_dice(1, 6) == 1 - { + if *tile == TileType::Floor && self.door_possible(build_data, i) && rng.roll_dice(1, 6) == 1 { build_data.spawn_list.push((i, "door".to_string())); } } @@ -49,35 +45,27 @@ impl DoorPlacement { } } - let x = idx % (build_data.map.width as usize); - let y = idx / (build_data.map.width as usize); + let x = idx % build_data.map.width as usize; + let y = idx / build_data.map.width as usize; // Check for east-west door possibility - if - build_data.map.tiles[idx] == TileType::Floor && - x > 1 && - build_data.map.tiles[idx - 1] == TileType::Floor && - x < (build_data.map.width as usize) - 2 && - build_data.map.tiles[idx + 1] == TileType::Floor && - y > 1 && - build_data.map.tiles[idx - (build_data.map.width as usize)] == TileType::Wall && - y < (build_data.map.height as usize) - 2 && - build_data.map.tiles[idx + (build_data.map.width as usize)] == TileType::Wall + if build_data.map.tiles[idx] == TileType::Floor + && (x > 1 && build_data.map.tiles[idx - 1] == TileType::Floor) + && (x < build_data.map.width as usize - 2 && build_data.map.tiles[idx + 1] == TileType::Floor) + && (y > 1 && build_data.map.tiles[idx - build_data.map.width as usize] == TileType::Wall) + && (y < build_data.map.height as usize - 2 + && build_data.map.tiles[idx + build_data.map.width as usize] == TileType::Wall) { return true; } // Check for north-south door possibility - if - build_data.map.tiles[idx] == TileType::Floor && - x > 1 && - build_data.map.tiles[idx - 1] == TileType::Wall && - x < (build_data.map.width as usize) - 2 && - build_data.map.tiles[idx + 1] == TileType::Wall && - y > 1 && - build_data.map.tiles[idx - (build_data.map.width as usize)] == TileType::Floor && - y < (build_data.map.height as usize) - 2 && - build_data.map.tiles[idx + (build_data.map.width as usize)] == TileType::Floor + if build_data.map.tiles[idx] == TileType::Floor + && (x > 1 && build_data.map.tiles[idx - 1] == TileType::Wall) + && (x < build_data.map.width as usize - 2 && build_data.map.tiles[idx + 1] == TileType::Wall) + && (y > 1 && build_data.map.tiles[idx - build_data.map.width as usize] == TileType::Floor) + && (y < build_data.map.height as usize - 2 + && build_data.map.tiles[idx + build_data.map.width as usize] == TileType::Floor) { return true; } diff --git a/src/map_builders/drunkard.rs b/src/map_builders/drunkard.rs index eef629c..151e100 100644 --- a/src/map_builders/drunkard.rs +++ b/src/map_builders/drunkard.rs @@ -1,5 +1,5 @@ -use super::{ paint, BuilderMap, InitialMapBuilder, MetaMapBuilder, Position, Symmetry, TileType }; -use bracket_lib::prelude::*; +use super::{paint, BuilderMap, InitialMapBuilder, MetaMapBuilder, Position, Symmetry, TileType}; +use rltk::RandomNumberGenerator; #[derive(PartialEq, Copy, Clone)] #[allow(dead_code)] @@ -22,14 +22,14 @@ pub struct DrunkardsWalkBuilder { impl InitialMapBuilder for DrunkardsWalkBuilder { #[allow(dead_code)] - fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { self.build(rng, build_data); } } impl MetaMapBuilder for DrunkardsWalkBuilder { #[allow(dead_code)] - fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { self.build(rng, build_data); } } @@ -107,19 +107,13 @@ impl DrunkardsWalkBuilder { fn build(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { // Set a central starting point - let starting_position = Position { - x: build_data.map.width / 2, - y: build_data.map.height / 2, - }; + let starting_position = Position { x: build_data.map.width / 2, y: build_data.map.height / 2 }; let start_idx = build_data.map.xy_idx(starting_position.x, starting_position.y); build_data.map.tiles[start_idx] = TileType::Floor; let total_tiles = build_data.map.width * build_data.map.height; - let desired_floor_tiles = (self.settings.floor_percent * (total_tiles as f32)) as usize; - let mut floor_tile_count = build_data.map.tiles - .iter() - .filter(|a| **a == TileType::Floor) - .count(); + let desired_floor_tiles = (self.settings.floor_percent * total_tiles as f32) as usize; + let mut floor_tile_count = build_data.map.tiles.iter().filter(|a| **a == TileType::Floor).count(); let mut digger_count = 0; while floor_tile_count < desired_floor_tiles { let mut did_something = false; @@ -147,13 +141,7 @@ impl DrunkardsWalkBuilder { if build_data.map.tiles[drunk_idx] == TileType::Wall { did_something = true; } - paint( - &mut build_data.map, - self.settings.symmetry, - self.settings.brush_size, - drunk_x, - drunk_y - ); + paint(&mut build_data.map, self.settings.symmetry, self.settings.brush_size, drunk_x, drunk_y); build_data.map.tiles[drunk_idx] = TileType::DownStair; let stagger_direction = rng.roll_dice(1, 4); @@ -192,10 +180,7 @@ impl DrunkardsWalkBuilder { *t = TileType::Floor; } } - floor_tile_count = build_data.map.tiles - .iter() - .filter(|a| **a == TileType::Floor) - .count(); + floor_tile_count = build_data.map.tiles.iter().filter(|a| **a == TileType::Floor).count(); } } } diff --git a/src/map_builders/fill_edges.rs b/src/map_builders/fill_edges.rs index 6d8c624..3c80bc4 100644 --- a/src/map_builders/fill_edges.rs +++ b/src/map_builders/fill_edges.rs @@ -1,15 +1,13 @@ -use super::{ BuilderMap, MetaMapBuilder, TileType }; -use crate::tile_walkable; -use bracket_lib::prelude::*; +use super::{BuilderMap, MetaMapBuilder, TileType}; +use rltk::RandomNumberGenerator; pub struct FillEdges { fill_with: TileType, - only_walkable: bool, } impl MetaMapBuilder for FillEdges { #[allow(dead_code)] - fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { self.fill_edges(rng, build_data); } } @@ -17,32 +15,21 @@ impl MetaMapBuilder for FillEdges { impl FillEdges { #[allow(dead_code)] pub fn wall() -> Box { - return Box::new(FillEdges { fill_with: TileType::Wall, only_walkable: false }); - } - pub fn overmap_transition(id: i32) -> Box { - return Box::new(FillEdges { fill_with: TileType::ToOvermap(id), only_walkable: true }); + return Box::new(FillEdges { fill_with: TileType::Wall }); } fn fill_edges(&mut self, _rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { - // Get map edges as possible points to fill - let mut possible_idxs: Vec = Vec::new(); for x in 0..build_data.map.width { let mut idx = build_data.map.xy_idx(x, 0); - possible_idxs.push(idx); + build_data.map.tiles[idx] = self.fill_with; idx = build_data.map.xy_idx(x, build_data.map.height - 1); - possible_idxs.push(idx); + build_data.map.tiles[idx] = self.fill_with; } for y in 0..build_data.map.height { let mut idx = build_data.map.xy_idx(0, y); - possible_idxs.push(idx); + build_data.map.tiles[idx] = self.fill_with; idx = build_data.map.xy_idx(build_data.map.width - 1, y); - possible_idxs.push(idx); - } - // For every possible point, first check if we only want to fill walkable tiles (and if its walkable if so) - for idx in possible_idxs { - if !self.only_walkable || tile_walkable(build_data.map.tiles[idx]) { - build_data.map.tiles[idx] = self.fill_with; - } + build_data.map.tiles[idx] = self.fill_with; } } } diff --git a/src/map_builders/foliage.rs b/src/map_builders/foliage.rs deleted file mode 100644 index 1988ddc..0000000 --- a/src/map_builders/foliage.rs +++ /dev/null @@ -1,38 +0,0 @@ -use super::{ BuilderMap, MetaMapBuilder, TileType }; -use bracket_lib::prelude::*; - -pub struct Foliage { - start_tile: TileType, - percent: i32, -} - -impl MetaMapBuilder for Foliage { - #[allow(dead_code)] - fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { - self.apply(rng, build_data); - } -} - -impl Foliage { - #[allow(dead_code)] - pub fn percent(start_tile: TileType, percent: i32) -> Box { - return Box::new(Foliage { start_tile, percent }); - } - - fn apply(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { - for tile in build_data.map.tiles.iter_mut() { - if *tile == self.start_tile { - if rng.roll_dice(1, 100) < self.percent { - match rng.roll_dice(1, 2) { - 1 => { - *tile = TileType::Foliage; - } - _ => { - *tile = TileType::HeavyFoliage; - } - }; - } - } - } - } -} diff --git a/src/map_builders/forest.rs b/src/map_builders/forest.rs index e30f270..e83bfe7 100644 --- a/src/map_builders/forest.rs +++ b/src/map_builders/forest.rs @@ -1,47 +1,25 @@ use super::{ - AreaStartingPosition, - BuilderChain, - BuilderMap, - CellularAutomataBuilder, - CullUnreachable, - MetaMapBuilder, - TileType, - VoronoiSpawning, - XStart, - YStart, - Foliage, + AreaStartingPosition, BuilderChain, BuilderMap, CellularAutomataBuilder, CullUnreachable, MetaMapBuilder, TileType, + VoronoiSpawning, XStart, YStart, }; -use bracket_lib::prelude::*; -use crate::data::names::*; +use rltk::prelude::*; pub fn forest_builder( new_id: i32, - _rng: &mut RandomNumberGenerator, + _rng: &mut rltk::RandomNumberGenerator, width: i32, height: i32, difficulty: i32, - initial_player_level: i32 + initial_player_level: i32, ) -> BuilderChain { - let mut chain = BuilderChain::new( - false, - new_id, - width, - height, - difficulty, - NAME_FOREST_BUILDER, - SHORTNAME_FOREST_BUILDER, - 0, - initial_player_level - ); - chain.start_with(CellularAutomataBuilder::floor(TileType::Grass)); - // Change ~30% of the floor to some sort of foliage. + let mut chain = BuilderChain::new(new_id, width, height, difficulty, "the woods", initial_player_level); + chain.start_with(CellularAutomataBuilder::new()); chain.with(AreaStartingPosition::new(XStart::CENTRE, YStart::CENTRE)); chain.with(CullUnreachable::new()); chain.with(AreaStartingPosition::new(XStart::LEFT, YStart::CENTRE)); // Setup an exit and spawn mobs - chain.with(RoadExit::new()); - chain.with(Foliage::percent(TileType::Grass, 30)); chain.with(VoronoiSpawning::new()); + chain.with(RoadExit::new()); return chain; } @@ -66,21 +44,18 @@ impl RoadExit { available_floors.push(( idx, DistanceAlg::PythagorasSquared.distance2d( - Point::new( - (idx as i32) % build_data.map.width, - (idx as i32) / build_data.map.width - ), - Point::new(seed_x, seed_y) + Point::new(idx as i32 % build_data.map.width, idx as i32 / build_data.map.width), + Point::new(seed_x, seed_y), ), )); } } if available_floors.is_empty() { - unreachable!("No valid floors to start on."); + panic!("No valid floors to start on."); } available_floors.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap()); - let end_x = (available_floors[0].0 as i32) % build_data.map.width; - let end_y = (available_floors[0].0 as i32) / build_data.map.width; + let end_x = available_floors[0].0 as i32 % build_data.map.width; + let end_y = available_floors[0].0 as i32 / build_data.map.width; return (end_x, end_y); } @@ -97,18 +72,14 @@ impl RoadExit { fn build(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { let starting_pos = build_data.starting_position.as_ref().unwrap().clone(); let start_idx = build_data.map.xy_idx(starting_pos.x, starting_pos.y); - let (end_x, end_y) = self.find_exit( - build_data, - build_data.map.width - 2, - build_data.height / 2 - ); + let (end_x, end_y) = self.find_exit(build_data, build_data.map.width - 2, build_data.height / 2); let end_idx = build_data.map.xy_idx(end_x, end_y); build_data.map.populate_blocked(); let path = a_star_search(start_idx, end_idx, &mut build_data.map); for idx in path.steps.iter() { - let x = (*idx as i32) % build_data.map.width; - let y = (*idx as i32) / build_data.map.width; + let x = *idx as i32 % build_data.map.width; + let y = *idx as i32 / build_data.map.width; self.paint_road(build_data, x, y); self.paint_road(build_data, x - 1, y); self.paint_road(build_data, x + 1, y); @@ -129,9 +100,9 @@ impl RoadExit { let stream_idx = build_data.map.xy_idx(stream_x, stream_y) as usize; let stream = a_star_search(stairs_idx, stream_idx, &mut build_data.map); for tile in stream.steps.iter() { - // Maybe only turn grass to water here, and turn the road into a bridge. - // i.e. if build_data.map.tiles[*tile as usize] == TileType::Grass - build_data.map.tiles[*tile as usize] = TileType::ShallowWater; + if build_data.map.tiles[*tile as usize] == TileType::Floor { + build_data.map.tiles[*tile as usize] = TileType::ShallowWater; + } } build_data.map.tiles[stairs_idx] = TileType::DownStair; build_data.take_snapshot(); diff --git a/src/map_builders/maze.rs b/src/map_builders/maze.rs index 4b0de71..5d37496 100644 --- a/src/map_builders/maze.rs +++ b/src/map_builders/maze.rs @@ -1,11 +1,11 @@ -use super::{ BuilderMap, InitialMapBuilder, Map, TileType }; -use bracket_lib::prelude::*; +use super::{BuilderMap, InitialMapBuilder, Map, TileType}; +use rltk::RandomNumberGenerator; pub struct MazeBuilder {} impl InitialMapBuilder for MazeBuilder { #[allow(dead_code)] - fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { self.build(rng, build_data); } } @@ -19,7 +19,7 @@ impl MazeBuilder { #[allow(clippy::map_entry)] fn build(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { // Maze gen - let mut maze = Grid::new(build_data.map.width / 2 - 2, build_data.map.height / 2 - 2, rng); + let mut maze = Grid::new((build_data.map.width / 2) - 2, (build_data.map.height / 2) - 2, rng); maze.generate_maze(build_data); } } @@ -75,14 +75,7 @@ struct Grid<'a> { impl<'a> Grid<'a> { fn new(width: i32, height: i32, rng: &mut RandomNumberGenerator) -> Grid { - let mut grid = Grid { - width, - height, - cells: Vec::new(), - backtrace: Vec::new(), - current: 0, - rng, - }; + let mut grid = Grid { width, height, cells: Vec::new(), backtrace: Vec::new(), current: 0, rng }; for row in 0..height { for column in 0..width { @@ -97,7 +90,7 @@ impl<'a> Grid<'a> { if row < 0 || column < 0 || column > self.width - 1 || row > self.height - 1 { -1 } else { - column + row * self.width + column + (row * self.width) } } @@ -129,9 +122,7 @@ impl<'a> Grid<'a> { if neighbors.len() == 1 { return Some(neighbors[0]); } else { - return Some( - neighbors[(self.rng.roll_dice(1, neighbors.len() as i32) - 1) as usize] - ); + return Some(neighbors[(self.rng.roll_dice(1, neighbors.len() as i32) - 1) as usize]); } } None @@ -150,9 +141,7 @@ impl<'a> Grid<'a> { // __lower_part__ __higher_part_ // / \ / \ // --------cell1------ | cell2----------- - let (lower_part, higher_part) = self.cells.split_at_mut( - std::cmp::max(self.current, next) - ); + let (lower_part, higher_part) = self.cells.split_at_mut(std::cmp::max(self.current, next)); let cell1 = &mut lower_part[std::cmp::min(self.current, next)]; let cell2 = &mut higher_part[0]; cell1.remove_walls(cell2); @@ -189,16 +178,16 @@ impl<'a> Grid<'a> { map.tiles[idx] = TileType::Floor; if !cell.walls[TOP] { - map.tiles[idx - (map.width as usize)] = TileType::Floor; + map.tiles[idx - map.width as usize] = TileType::Floor } if !cell.walls[RIGHT] { - map.tiles[idx + 1] = TileType::Floor; + map.tiles[idx + 1] = TileType::Floor } if !cell.walls[BOTTOM] { - map.tiles[idx + (map.width as usize)] = TileType::Floor; + map.tiles[idx + map.width as usize] = TileType::Floor } if !cell.walls[LEFT] { - map.tiles[idx - 1] = TileType::Floor; + map.tiles[idx - 1] = TileType::Floor } } } diff --git a/src/map_builders/mod.rs b/src/map_builders/mod.rs index 7b38efe..69a46ef 100644 --- a/src/map_builders/mod.rs +++ b/src/map_builders/mod.rs @@ -1,6 +1,4 @@ -use super::{ spawner, Map, Position, Rect, TileType }; -use bracket_lib::prelude::*; - +use super::{spawner, Map, Position, Rect, TileType, SHOW_MAPGEN}; mod bsp_dungeon; use bsp_dungeon::BspDungeonBuilder; mod bsp_interior; @@ -28,7 +26,7 @@ use room_based_stairs::*; mod room_based_starting_position; use room_based_starting_position::*; mod area_starting_points; -use area_starting_points::{ AreaStartingPosition, XStart, YStart }; +use area_starting_points::{AreaStartingPosition, XStart, YStart}; mod cull_unreachable; use cull_unreachable::CullUnreachable; mod distant_exit; @@ -37,9 +35,6 @@ mod voronoi_spawning; use common::*; use specs::prelude::*; use voronoi_spawning::VoronoiSpawning; -use super::config::CONFIG; -use super::data::ids::*; -use super::data::names::*; //use wfc::WaveFunctionCollapseBuilder; mod room_exploder; use room_exploder::RoomExploder; @@ -50,7 +45,7 @@ use rooms_corridors_dogleg::DoglegCorridors; mod rooms_corridors_bsp; use rooms_corridors_bsp::BspCorridors; mod room_sorter; -use room_sorter::{ RoomSort, RoomSorter }; +use room_sorter::{RoomSort, RoomSorter}; mod room_draw; use room_draw::RoomDrawer; mod rooms_corridors_nearest; @@ -67,10 +62,6 @@ mod town; use town::town_builder; mod forest; use forest::forest_builder; -mod foliage; -use foliage::Foliage; -mod room_themer; -use room_themer::ThemeRooms; // Shared data to be passed around build chain pub struct BuilderMap { @@ -87,7 +78,7 @@ pub struct BuilderMap { impl BuilderMap { fn take_snapshot(&mut self) { - if CONFIG.logging.show_mapgen { + if SHOW_MAPGEN { let mut snapshot = self.map.clone(); for v in snapshot.revealed_tiles.iter_mut() { *v = true; @@ -105,22 +96,19 @@ pub struct BuilderChain { impl BuilderChain { pub fn new( - overmap: bool, new_id: i32, width: i32, height: i32, difficulty: i32, name: S, - short_name: S, - depth: i32, - initial_player_level: i32 + initial_player_level: i32, ) -> BuilderChain { BuilderChain { starter: None, builders: Vec::new(), build_data: BuilderMap { spawn_list: Vec::new(), - map: Map::new(overmap, new_id, width, height, difficulty, name, short_name, depth), + map: Map::new(new_id, width, height, difficulty, name), starting_position: None, rooms: None, corridors: None, @@ -134,10 +122,8 @@ impl BuilderChain { pub fn start_with(&mut self, starter: Box) { match self.starter { - None => { - self.starter = Some(starter); - } - Some(_) => unreachable!("You can only have one starting builder."), + None => self.starter = Some(starter), + Some(_) => panic!("You can only have one starting builder."), }; } @@ -145,9 +131,9 @@ impl BuilderChain { self.builders.push(metabuilder); } - pub fn build_map(&mut self, rng: &mut RandomNumberGenerator) { + pub fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator) { match &mut self.starter { - None => unreachable!("Cannot run a map builder chain without a starting build system"), + None => panic!("Cannot run a map builder chain without a starting build system"), Some(starter) => { // Build the starting map starter.build_map(rng, &mut self.build_data); @@ -166,53 +152,39 @@ impl BuilderChain { spawned_entities.push(&entity.1); spawner::spawn_entity(ecs, &(&entity.0, &entity.1)); } - if CONFIG.logging.log_spawning { - console::log(format!("DEBUGINFO: SPAWNED ENTITIES = {:?}", spawned_entities)); - } + rltk::console::log(format!("DEBUGINFO: SPAWNED ENTITIES = {:?}", spawned_entities)); } } pub trait InitialMapBuilder { - fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap); + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap); } pub trait MetaMapBuilder { - fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap); + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap); } -fn random_start_position(rng: &mut RandomNumberGenerator) -> (XStart, YStart) { +fn random_start_position(rng: &mut rltk::RandomNumberGenerator) -> (XStart, YStart) { let x; let xroll = rng.roll_dice(1, 3); match xroll { - 1 => { - x = XStart::LEFT; - } - 2 => { - x = XStart::CENTRE; - } - _ => { - x = XStart::RIGHT; - } + 1 => x = XStart::LEFT, + 2 => x = XStart::CENTRE, + _ => x = XStart::RIGHT, } let y; let yroll = rng.roll_dice(1, 3); match yroll { - 1 => { - y = YStart::BOTTOM; - } - 2 => { - y = YStart::CENTRE; - } - _ => { - y = YStart::TOP; - } + 1 => y = YStart::BOTTOM, + 2 => y = YStart::CENTRE, + _ => y = YStart::TOP, } (x, y) } -fn random_room_builder(rng: &mut RandomNumberGenerator, builder: &mut BuilderChain, end: bool) { +fn random_room_builder(rng: &mut rltk::RandomNumberGenerator, builder: &mut BuilderChain) { let build_roll = rng.roll_dice(1, 3); // Start with a room builder. match build_roll { @@ -274,12 +246,10 @@ fn random_room_builder(rng: &mut RandomNumberGenerator, builder: &mut BuilderCha } // Decide where to put the exit - in a room or far away, anywhere. - if !end { - let exit_roll = rng.roll_dice(1, 2); - match exit_roll { - 1 => builder.with(RoomBasedStairs::new()), - _ => builder.with(DistantExit::new()), - } + let exit_roll = rng.roll_dice(1, 2); + match exit_roll { + 1 => builder.with(RoomBasedStairs::new()), + _ => builder.with(DistantExit::new()), } // Decide whether to spawn entities only in rooms, or with voronoi noise. @@ -288,16 +258,9 @@ fn random_room_builder(rng: &mut RandomNumberGenerator, builder: &mut BuilderCha 1 => builder.with(RoomBasedSpawner::new()), _ => builder.with(VoronoiSpawning::new()), } - - builder.with(ThemeRooms::grass(5, 5 * 5)); // 5% chance of an overgrown treant room. Must be 5*5 tiles minimum. - builder.with(ThemeRooms::barracks(5, 6 * 6)); // 5% chance of a squad barracks. Must be 6*6 tiles minimum. } -fn random_shape_builder( - rng: &mut RandomNumberGenerator, - builder: &mut BuilderChain, - end: bool -) -> bool { +fn random_shape_builder(rng: &mut rltk::RandomNumberGenerator, builder: &mut BuilderChain) -> bool { // Pick an initial builder let builder_roll = rng.roll_dice(1, 16); let mut want_doors = true; @@ -318,10 +281,7 @@ fn random_shape_builder( 11 => builder.start_with(DLABuilder::insectoid()), 12 => builder.start_with(VoronoiBuilder::pythagoras()), 13 => builder.start_with(VoronoiBuilder::manhattan()), - _ => - builder.start_with( - PrefabBuilder::constant(prefab_builder::prefab_levels::WFC_POPULATED) - ), + _ => builder.start_with(PrefabBuilder::constant(prefab_builder::prefab_levels::WFC_POPULATED)), } // 'Select' the centre by placing a starting position, and cull everywhere unreachable. @@ -334,74 +294,26 @@ fn random_shape_builder( // Place the exit and spawn mobs builder.with(VoronoiSpawning::new()); - if !end { - builder.with(DistantExit::new()); - } + builder.with(DistantExit::new()); return want_doors; } -fn overmap_builder() -> BuilderChain { - let mut builder = BuilderChain::new( - true, - ID_OVERMAP, - 69, - 41, - 0, - NAME_OVERMAP, - SHORTNAME_OVERMAP, - 0, - 1 - ); - builder.start_with(PrefabBuilder::overmap()); - builder.with(Foliage::percent(TileType::Grass, 30)); - return builder; -} - -pub enum BuildType { - Room = 1, - Shape = 2, - Any = 3, -} - pub fn random_builder( new_id: i32, - rng: &mut RandomNumberGenerator, + rng: &mut rltk::RandomNumberGenerator, width: i32, height: i32, difficulty: i32, - depth: i32, initial_player_level: i32, - end: bool, - build_type: BuildType ) -> BuilderChain { - console::log(format!("DEBUGINFO: Building random (ID:{}, DIFF:{})", new_id, difficulty)); - let mut builder = BuilderChain::new( - false, - new_id, - width, - height, - difficulty, - NAME_DUNGEON_RANDOM, - SHORTNAME_DUNGEON_RANDOM, - depth, - initial_player_level - ); + rltk::console::log(format!("DEBUGINFO: Building random (ID:{}, DIFF:{})", new_id, difficulty)); + let mut builder = BuilderChain::new(new_id, width, height, difficulty, "the dungeon", initial_player_level); + let type_roll = rng.roll_dice(1, 2); let mut want_doors = true; - match build_type { - BuildType::Room => random_room_builder(rng, &mut builder, end), - BuildType::Shape => { - want_doors = random_shape_builder(rng, &mut builder, end); - } - BuildType::Any => { - let roll = rng.roll_dice(1, 2); - match roll { - 1 => random_room_builder(rng, &mut builder, end), - _ => { - want_doors = random_shape_builder(rng, &mut builder, end); - } - } - } + match type_roll { + 1 => random_room_builder(rng, &mut builder), + _ => want_doors = random_shape_builder(rng, &mut builder), } /* @@ -438,55 +350,17 @@ pub fn random_builder( } pub fn level_builder( - id: i32, - rng: &mut RandomNumberGenerator, + new_id: i32, + rng: &mut rltk::RandomNumberGenerator, width: i32, height: i32, - initial_player_level: i32 + initial_player_level: i32, ) -> BuilderChain { - match id { - ID_OVERMAP => overmap_builder(), - ID_TOWN => town_builder(id, rng, width, height, 0, initial_player_level), - ID_TOWN2 => forest_builder(id, rng, width, height, 1, initial_player_level), - ID_TOWN3 => - random_builder( - id, - rng, - width, - height, - 2, - 1, - initial_player_level, - true, - BuildType::Room - ), - _ if id >= ID_INFINITE => - random_builder( - id, - rng, - width, - height, - 4 + diff(ID_INFINITE, id), - 1 + diff(ID_INFINITE, id), - initial_player_level, - false, - BuildType::Room - ), - _ => // This should be unreachable!() eventually. Right now it's reachable with the debug/cheat menu. It should not be in normal gameplay. - random_builder( - id, - rng, - width, - height, - 1, - 404, - initial_player_level, - false, - BuildType::Room - ), + // TODO: With difficulty and ID/depth decoupled, this can be used for branches later. + let difficulty = new_id; + match new_id { + 1 => town_builder(new_id, rng, width, height, 0, initial_player_level), + 2 => forest_builder(new_id, rng, width, height, 1, initial_player_level), + _ => random_builder(new_id, rng, width, height, difficulty, initial_player_level), } } - -fn diff(branch_id: i32, lvl_id: i32) -> i32 { - return lvl_id - branch_id; -} diff --git a/src/map_builders/prefab_builder/mod.rs b/src/map_builders/prefab_builder/mod.rs index 582577d..c97b1f1 100644 --- a/src/map_builders/prefab_builder/mod.rs +++ b/src/map_builders/prefab_builder/mod.rs @@ -1,21 +1,16 @@ -use super::{ spawner, BuilderMap, InitialMapBuilder, MetaMapBuilder, Position, TileType }; -use bracket_lib::prelude::*; +use super::{spawner, BuilderMap, InitialMapBuilder, MetaMapBuilder, Position, TileType}; +use rltk::RandomNumberGenerator; pub mod prefab_levels; pub mod prefab_sections; pub mod prefab_vaults; use std::collections::HashSet; -use crate::data::ids::*; #[derive(PartialEq, Copy, Clone)] #[allow(dead_code)] pub enum PrefabMode { - Overmap, - Constant { - level: prefab_levels::PrefabLevel, - }, - Sectional { - section: prefab_sections::PrefabSection, - }, + RexLevel { template: &'static str }, + Constant { level: prefab_levels::PrefabLevel }, + Sectional { section: prefab_sections::PrefabSection }, RoomVaults, } @@ -25,14 +20,14 @@ pub struct PrefabBuilder { } impl MetaMapBuilder for PrefabBuilder { - fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { self.build(rng, build_data); } } impl InitialMapBuilder for PrefabBuilder { #[allow(dead_code)] - fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { self.build(rng, build_data); } } @@ -44,8 +39,8 @@ impl PrefabBuilder { } #[allow(dead_code)] - pub fn overmap() -> Box { - Box::new(PrefabBuilder { mode: PrefabMode::Overmap }) + pub fn rex_level(template: &'static str) -> Box { + Box::new(PrefabBuilder { mode: PrefabMode::RexLevel { template } }) } #[allow(dead_code)] @@ -65,42 +60,24 @@ impl PrefabBuilder { fn build(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { match self.mode { - PrefabMode::Overmap => - self.load_ascii_map(&prefab_levels::OVERMAP, rng, build_data, true), - PrefabMode::Constant { level } => self.load_ascii_map(&level, rng, build_data, false), + PrefabMode::RexLevel { template } => self.load_rex_map(&template, rng, build_data), + PrefabMode::Constant { level } => self.load_ascii_map(&level, rng, build_data), PrefabMode::Sectional { section } => self.apply_sectional(§ion, rng, build_data), PrefabMode::RoomVaults => self.apply_room_vaults(rng, build_data), } build_data.take_snapshot(); } - fn char_to_map( - &mut self, - ch: char, - idx: usize, - rng: &mut RandomNumberGenerator, - build_data: &mut BuilderMap - ) { + fn char_to_map(&mut self, ch: char, idx: usize, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { let difficulty = (build_data.map.difficulty + build_data.initial_player_level) / 2; match ch { - ' ' => { - build_data.map.tiles[idx] = TileType::Floor; - } - '.' => { - build_data.map.tiles[idx] = TileType::Grass; - } - '#' => { - build_data.map.tiles[idx] = TileType::Wall; - } - '>' => { - build_data.map.tiles[idx] = TileType::DownStair; - } - '≈' => { - build_data.map.tiles[idx] = TileType::DeepWater; - } // Placeholder for vines/brush + ' ' => build_data.map.tiles[idx] = TileType::Floor, + '#' => build_data.map.tiles[idx] = TileType::Wall, + '>' => build_data.map.tiles[idx] = TileType::DownStair, + '≈' => build_data.map.tiles[idx] = TileType::Floor, // Placeholder for vines/brush '@' => { - let x = (idx as i32) % build_data.map.width; - let y = (idx as i32) / build_data.map.width; + let x = idx as i32 % build_data.map.width; + let y = idx as i32 / build_data.map.width; build_data.map.tiles[idx] = TileType::Floor; build_data.starting_position = Some(Position { x: x as i32, y: y as i32 }); } @@ -126,100 +103,39 @@ impl PrefabBuilder { } '%' => { build_data.map.tiles[idx] = TileType::Floor; - build_data.spawn_list.push((idx, spawner::food_table(Some(difficulty)).roll(rng))); + build_data.spawn_list.push((idx, spawner::food_table(difficulty).roll(rng))); } '!' => { build_data.map.tiles[idx] = TileType::Floor; - build_data.spawn_list.push(( - idx, - spawner::potion_table(Some(difficulty)).roll(rng), - )); + build_data.spawn_list.push((idx, spawner::potion_table(difficulty).roll(rng))); } '/' => { build_data.map.tiles[idx] = TileType::Floor; - build_data.spawn_list.push((idx, spawner::wand_table(Some(difficulty)).roll(rng))); + build_data.spawn_list.push((idx, spawner::wand_table(difficulty).roll(rng))); } '?' => { build_data.map.tiles[idx] = TileType::Floor; - build_data.spawn_list.push(( - idx, - spawner::scroll_table(Some(difficulty)).roll(rng), - )); + build_data.spawn_list.push((idx, spawner::scroll_table(difficulty).roll(rng))); } ')' => { build_data.map.tiles[idx] = TileType::Floor; - build_data.spawn_list.push(( - idx, - spawner::equipment_table(Some(difficulty)).roll(rng), - )); + build_data.spawn_list.push((idx, spawner::equipment_table(difficulty).roll(rng))); } _ => { - console::log(format!("Unknown glyph '{}' when loading prefab", ch as u8 as char)); - } - } - } - - fn overmap_char_to_map( - &mut self, - ch: char, - idx: usize, - _rng: &mut RandomNumberGenerator, - build_data: &mut BuilderMap - ) { - match ch { - ' ' => { - build_data.map.tiles[idx] = TileType::Floor; - } - '.' => { - build_data.map.tiles[idx] = TileType::Grass; - } - '#' => { - build_data.map.tiles[idx] = TileType::Wall; - } - '>' => { - build_data.map.tiles[idx] = TileType::DownStair; - } - '~' => { - build_data.map.tiles[idx] = TileType::ShallowWater; - } - '≈' => { - build_data.map.tiles[idx] = TileType::DeepWater; - } - '@' => { - let x = (idx as i32) % build_data.map.width; - let y = (idx as i32) / build_data.map.width; - build_data.map.tiles[idx] = TileType::Grass; - build_data.starting_position = Some(Position { x: x as i32, y: y as i32 }); - } - '^' => { - build_data.map.tiles[idx] = TileType::ImpassableMountain; - } - '1' => { - build_data.map.tiles[idx] = TileType::ToLocal(ID_TOWN); - } - '2' => { - build_data.map.tiles[idx] = TileType::ToLocal(ID_INFINITE); - } - _ => { - console::log(format!("Unknown glyph '{}' when loading overmap", ch as u8 as char)); + rltk::console::log(format!("Unknown glyph '{}' when loading prefab", (ch as u8) as char)); } } } #[allow(dead_code)] - fn load_rex_map( - &mut self, - path: &str, - rng: &mut RandomNumberGenerator, - build_data: &mut BuilderMap - ) { - let xp_file = rex::XpFile::from_resource(path).unwrap(); + fn load_rex_map(&mut self, path: &str, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + let xp_file = rltk::rex::XpFile::from_resource(path).unwrap(); for layer in &xp_file.layers { for y in 0..layer.height { for x in 0..layer.width { let cell = layer.get(x, y).unwrap(); - if x < (build_data.map.width as usize) && y < (build_data.map.height as usize) { + if x < build_data.map.width as usize && y < build_data.map.height as usize { let idx = build_data.map.xy_idx(x as i32, y as i32); // We're doing some nasty casting to make it easier to type things like '#' in the match self.char_to_map(cell.ch as u8 as char, idx, rng, build_data); @@ -230,12 +146,9 @@ impl PrefabBuilder { } fn read_ascii_to_vec(template: &str) -> Vec { - let mut string_vec: Vec = template - .chars() - .filter(|a| *a != '\r' && *a != '\n') - .collect(); + let mut string_vec: Vec = template.chars().filter(|a| *a != '\r' && *a != '\n').collect(); for c in string_vec.iter_mut() { - if (*c as u8) == 160u8 { + if *c as u8 == 160u8 { *c = ' '; } } @@ -248,21 +161,16 @@ impl PrefabBuilder { level: &prefab_levels::PrefabLevel, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap, - overmap: bool ) { let string_vec = PrefabBuilder::read_ascii_to_vec(level.template); let mut i = 0; for ty in 0..level.height { for tx in 0..level.width { - if tx < (build_data.map.width as usize) && ty < (build_data.map.height as usize) { + if tx < build_data.map.width as usize && ty < build_data.map.height as usize { let idx = build_data.map.xy_idx(tx as i32, ty as i32); if i < string_vec.len() { - if overmap { - self.overmap_char_to_map(string_vec[i], idx, rng, build_data); - } else { - self.char_to_map(string_vec[i], idx, rng, build_data); - } + self.char_to_map(string_vec[i], idx, rng, build_data); } } i += 1; @@ -274,14 +182,14 @@ impl PrefabBuilder { &mut self, mut filter: F, _rng: &mut RandomNumberGenerator, - build_data: &mut BuilderMap - ) - where F: FnMut(i32, i32) -> bool + build_data: &mut BuilderMap, + ) where + F: FnMut(i32, i32) -> bool, { let width = build_data.map.width; build_data.spawn_list.retain(|(idx, _name)| { - let x = (*idx as i32) % width; - let y = (*idx as i32) / width; + let x = *idx as i32 % width; + let y = *idx as i32 / width; filter(x, y) }); build_data.take_snapshot(); @@ -292,7 +200,7 @@ impl PrefabBuilder { &mut self, section: &prefab_sections::PrefabSection, rng: &mut RandomNumberGenerator, - build_data: &mut BuilderMap + build_data: &mut BuilderMap, ) { use prefab_sections::*; @@ -301,52 +209,36 @@ impl PrefabBuilder { // Place the new section let chunk_x; match section.placement.0 { - HorizontalPlacement::Left => { - chunk_x = 0; - } - HorizontalPlacement::Center => { - chunk_x = build_data.map.width / 2 - (section.width as i32) / 2; - } - HorizontalPlacement::Right => { - chunk_x = build_data.map.width - 1 - (section.width as i32); - } + HorizontalPlacement::Left => chunk_x = 0, + HorizontalPlacement::Center => chunk_x = (build_data.map.width / 2) - (section.width as i32 / 2), + HorizontalPlacement::Right => chunk_x = (build_data.map.width - 1) - section.width as i32, } let chunk_y; match section.placement.1 { - VerticalPlacement::Top => { - chunk_y = 0; - } - VerticalPlacement::Center => { - chunk_y = build_data.map.height / 2 - (section.height as i32) / 2; - } - VerticalPlacement::Bottom => { - chunk_y = build_data.map.height - 1 - (section.height as i32); - } + VerticalPlacement::Top => chunk_y = 0, + VerticalPlacement::Center => chunk_y = (build_data.map.height / 2) - (section.height as i32 / 2), + VerticalPlacement::Bottom => chunk_y = (build_data.map.height - 1) - section.height as i32, } // Build the map self.apply_previous_iteration( |x, y| { - x < chunk_x || - x > chunk_x + (section.width as i32) || - y < chunk_y || - y > chunk_y + (section.height as i32) + x < chunk_x + || x > (chunk_x + section.width as i32) + || y < chunk_y + || y > (chunk_y + section.height as i32) }, rng, - build_data + build_data, ); let mut i = 0; for ty in 0..section.height { for tx in 0..section.width { - if - tx > 0 && - tx < (build_data.map.width as usize) - 1 && - ty < (build_data.map.height as usize) - 1 && - ty > 0 + if tx > 0 && tx < build_data.map.width as usize - 1 && ty < build_data.map.height as usize - 1 && ty > 0 { - let idx = build_data.map.xy_idx((tx as i32) + chunk_x, (ty as i32) + chunk_y); + let idx = build_data.map.xy_idx(tx as i32 + chunk_x, ty as i32 + chunk_y); if i < string_vec.len() { self.char_to_map(string_vec[i], idx, rng, build_data); } @@ -382,7 +274,7 @@ impl PrefabBuilder { FLUFF2_6X3, HOUSE_NOTRAP_7X7, HOUSE_TRAP_7X7, - ORC_HOUSE_8X8 + ORC_HOUSE_8X8, ]; // Filter the vault list down to ones that are applicable to the current id @@ -421,12 +313,8 @@ impl PrefabBuilder { let roll = rng.roll_dice(1, 4); match roll { 1 => {} - 2 => { - flip_x = true; - } - 3 => { - flip_y = true; - } + 2 => flip_x = true, + 3 => flip_y = true, _ => { flip_x = true; flip_y = true; @@ -440,15 +328,14 @@ impl PrefabBuilder { let mut idx = 0usize; loop { - let x = (idx % (build_data.map.width as usize)) as i32; - let y = (idx / (build_data.map.width as usize)) as i32; + let x = (idx % build_data.map.width as usize) as i32; + let y = (idx / build_data.map.width as usize) as i32; // Check that we won't overflow the map - if - x > 1 && - x + (vault.width as i32) < build_data.map.width - 2 && - y > 1 && - y + (vault.height as i32) < build_data.map.height - 2 + if x > 1 + && (x + vault.width as i32) < build_data.map.width - 2 + && y > 1 + && (y + vault.height as i32) < build_data.map.height - 2 { let mut possible = true; for ty in 0..vault.height as i32 { @@ -492,10 +379,7 @@ impl PrefabBuilder { let idx = e.0 as i32; let x = idx % width; let y = idx / height; - x < chunk_x || - x > chunk_x + (vault.width as i32) || - y < chunk_y || - y > chunk_y + (vault.height as i32) + x < chunk_x || x > chunk_x + vault.width as i32 || y < chunk_y || y > chunk_y + vault.height as i32 }); let string_vec = PrefabBuilder::read_ascii_to_vec(vault.template); @@ -506,22 +390,17 @@ impl PrefabBuilder { let mut y_: i32 = tile_y as i32; // Handle flipping if flip_x { - x_ = (vault.width as i32) - 1 - x_; + x_ = vault.width as i32 - 1 - x_; } if flip_y { - y_ = (vault.height as i32) - 1 - y_; + y_ = vault.height as i32 - 1 - y_; } if x_ < 0 || y_ < 0 { // If either of these go below 0, we run the risk of CTD, so just panic. // Something went wrong with flipping/rotating/defining a vault. panic!( "X or Y went below 0 when trying to place a vault! DEBUGINFO == [H: {}, W: {}; FLIPPED X: {}, FLIPPED Y: {}; X_: {}, Y_: {}]", - vault.width, - vault.height, - flip_x, - flip_y, - x_, - y_ + vault.width, vault.height, flip_x, flip_y, x_, y_ ); } let idx = build_data.map.xy_idx(x_ + chunk_x, y_ + chunk_y); diff --git a/src/map_builders/prefab_builder/prefab_levels.rs b/src/map_builders/prefab_builder/prefab_levels.rs index 3fae32c..23d1d90 100644 --- a/src/map_builders/prefab_builder/prefab_levels.rs +++ b/src/map_builders/prefab_builder/prefab_levels.rs @@ -7,11 +7,9 @@ pub struct PrefabLevel { #[allow(dead_code)] pub const WFC_POPULATED: PrefabLevel = PrefabLevel { template: LEVEL_MAP, width: 80, height: 43 }; -pub const OVERMAP: PrefabLevel = PrefabLevel { template: OVERMAP_TEMPLATE, width: 69, height: 41 }; #[allow(dead_code)] -const LEVEL_MAP: &str = - " +const LEVEL_MAP: &str = " ################################################################################ #          ########################################################    ######### #    @     ######    #########       ####     ###################        ####### @@ -55,47 +53,3 @@ const LEVEL_MAP: &str = #!%^   ###  ###     ############### ########      ##### g     ####      # g#   # # %^##  ^   ###     ############### ########      #####       ################## ################################################################################"; - -const OVERMAP_TEMPLATE: &str = - " -^^^^^^^^^^^^^^^^^^^^^^^^^^^≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈........≈≈≈≈≈≈≈≈ -^^^^^^^^^^^^^^^^^^^^^^^^^^≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈...........≈≈≈≈≈≈≈ -^^^^^^^^^^^^^^^....^^^^^^≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈.............≈≈≈≈≈ -^^^^^^^^^^^^^^...........≈≈≈≈≈≈≈≈≈........≈≈≈≈≈≈≈≈..............≈≈≈≈≈ -^^^^^^^^^^^^^............≈≈≈≈≈≈≈≈...........≈≈≈≈≈..............≈≈≈≈≈≈ -^^^^^^^^^^^.............≈≈≈≈≈≈≈≈≈...........≈≈≈≈≈.............≈≈≈≈≈≈≈ -^^^^^^^^^...............≈≈≈≈≈≈≈≈≈............≈≈≈≈............≈≈≈≈≈≈≈≈ -^^^^^^^^................≈≈≈≈≈≈≈≈≈............≈≈≈≈............≈≈≈≈≈≈≈≈ -^^^^^^^..................≈≈≈≈≈≈≈≈≈...........≈≈≈.............≈≈≈≈≈≈≈≈ -^^^^.....................≈≈≈≈≈≈≈≈≈≈≈........≈≈≈≈..............≈≈≈≈≈≈≈ -^^.........................≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈..............≈≈≈≈≈≈≈ -^^..............................≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈................≈≈≈≈≈≈ -^.................................≈≈≈≈≈≈≈≈≈≈≈≈≈.................≈≈≈≈≈ -^..................................≈≈≈≈≈≈≈≈≈≈≈≈.................≈≈≈≈≈ -^....................................≈≈≈≈≈≈≈≈≈..................≈≈≈≈≈ -^......................................≈≈≈≈≈≈...................≈≈≈≈≈ -^........................................≈≈.....................≈≈≈≈≈ -^..............................................................≈≈≈≈≈≈ -^..............................................................≈≈≈≈≈≈ -^^.............................................................≈≈≈≈≈≈ -^^.............................................................≈≈≈≈≈≈ -^^.............................................................≈≈≈≈≈≈ -^^^............................................................≈≈≈≈≈≈ -^^^^............................................................≈≈≈≈≈ -^^^^^...........................................................≈≈≈≈≈ -^^^^^.^^........................................................≈≈≈≈≈ -^^^^..^^^.......................................................≈≈≈≈≈ -^^^...^^^..............................≈≈........................≈≈≈≈ -^^^2.^^^^.............................≈≈≈≈≈≈≈≈≈...................≈≈≈ -^^^^^^^^..............................≈≈≈≈≈≈≈≈≈≈......≈............≈≈ -^^^^^^^..............................≈≈≈≈≈≈≈≈≈≈≈≈≈...≈≈..........≈..≈ -^^^^^^^..........................≈≈.≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈....≈≈≈≈ -^^^^^^^........................≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈.....≈ -^^^^^^^^.......................≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈....≈ -^^^^^^^^......@...........≈...≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈...≈≈≈...≈≈≈≈≈≈≈≈.≈≈ -^^^^^^^^^.................≈≈....≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈....≈≈......≈≈≈≈≈≈≈≈≈ -^^^^^^^^^........≈≈≈≈1..≈≈≈≈....≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈..≈≈≈......≈≈≈≈≈≈≈≈≈ -^^^^^^^^^^......≈≈≈≈≈..≈≈≈≈≈≈..≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈........≈≈≈≈≈≈≈≈ -^^^^^^^^^^.....≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈........≈≈≈≈≈≈≈≈≈ -^^^^^^^^^^....≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈....≈≈≈≈≈≈≈≈≈≈ -^^^^^^^^^^^^.≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈"; diff --git a/src/map_builders/room_based_spawner.rs b/src/map_builders/room_based_spawner.rs index 8b86a7f..6d8f981 100644 --- a/src/map_builders/room_based_spawner.rs +++ b/src/map_builders/room_based_spawner.rs @@ -1,10 +1,10 @@ -use super::{ spawner, BuilderMap, MetaMapBuilder }; -use bracket_lib::prelude::*; +use super::{spawner, BuilderMap, MetaMapBuilder}; +use rltk::RandomNumberGenerator; pub struct RoomBasedSpawner {} impl MetaMapBuilder for RoomBasedSpawner { - fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { self.build(rng, build_data); } } @@ -23,11 +23,11 @@ impl RoomBasedSpawner { rng, room, &mut build_data.spawn_list, - build_data.initial_player_level + build_data.initial_player_level, ); } } else { - unreachable!("RoomBasedSpawner tried to run without any rooms."); + panic!("RoomBasedSpawner only works after rooms have been created"); } } } diff --git a/src/map_builders/room_based_stairs.rs b/src/map_builders/room_based_stairs.rs index c849e00..1c1a268 100644 --- a/src/map_builders/room_based_stairs.rs +++ b/src/map_builders/room_based_stairs.rs @@ -1,10 +1,10 @@ -use super::{ BuilderMap, MetaMapBuilder, TileType }; -use bracket_lib::prelude::*; +use super::{BuilderMap, MetaMapBuilder, TileType}; +use rltk::RandomNumberGenerator; pub struct RoomBasedStairs {} impl MetaMapBuilder for RoomBasedStairs { - fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { self.build(rng, build_data); } } @@ -17,12 +17,12 @@ impl RoomBasedStairs { fn build(&mut self, _rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { if let Some(rooms) = &build_data.rooms { - let stairs_position = rooms[rooms.len() - 1].center(); - let stairs_idx = build_data.map.xy_idx(stairs_position.x, stairs_position.y); + let stairs_position = rooms[rooms.len() - 1].centre(); + let stairs_idx = build_data.map.xy_idx(stairs_position.0, stairs_position.1); build_data.map.tiles[stairs_idx] = TileType::DownStair; build_data.take_snapshot(); } else { - unreachable!("RoomBasedStairs tried to run without any rooms."); + panic!("RoomBasedStairs only works after rooms have been created"); } } } diff --git a/src/map_builders/room_based_starting_position.rs b/src/map_builders/room_based_starting_position.rs index 16d092b..0550f03 100644 --- a/src/map_builders/room_based_starting_position.rs +++ b/src/map_builders/room_based_starting_position.rs @@ -1,10 +1,10 @@ -use super::{ BuilderMap, MetaMapBuilder, Position }; -use bracket_lib::prelude::*; +use super::{BuilderMap, MetaMapBuilder, Position}; +use rltk::RandomNumberGenerator; pub struct RoomBasedStartingPosition {} impl MetaMapBuilder for RoomBasedStartingPosition { - fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { self.build(rng, build_data); } } @@ -17,10 +17,10 @@ impl RoomBasedStartingPosition { fn build(&mut self, _rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { if let Some(rooms) = &build_data.rooms { - let start_pos = rooms[0].center(); - build_data.starting_position = Some(Position { x: start_pos.x, y: start_pos.y }); + let start_pos = rooms[0].centre(); + build_data.starting_position = Some(Position { x: start_pos.0, y: start_pos.1 }); } else { - unreachable!("RoomBasedStartingPosition tried to run without any rooms."); + panic!("RoomBasedStartingPosition only works after rooms have been created"); } } } diff --git a/src/map_builders/room_corner_rounding.rs b/src/map_builders/room_corner_rounding.rs index 516635f..e87a465 100644 --- a/src/map_builders/room_corner_rounding.rs +++ b/src/map_builders/room_corner_rounding.rs @@ -1,10 +1,10 @@ -use super::{ BuilderMap, MetaMapBuilder, Rect, TileType }; -use bracket_lib::prelude::*; +use super::{BuilderMap, MetaMapBuilder, Rect, TileType}; +use rltk::RandomNumberGenerator; pub struct RoomCornerRounder {} impl MetaMapBuilder for RoomCornerRounder { - fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { self.build(rng, build_data); } } @@ -23,13 +23,13 @@ impl RoomCornerRounder { if x > 0 && build_data.map.tiles[idx - 1] == TileType::Wall { neighbour_walls += 1; } - if y > 0 && build_data.map.tiles[idx - (w as usize)] == TileType::Wall { + if y > 0 && build_data.map.tiles[idx - w as usize] == TileType::Wall { neighbour_walls += 1; } if x < w - 2 && build_data.map.tiles[idx + 1] == TileType::Wall { neighbour_walls += 1; } - if y < h - 2 && build_data.map.tiles[idx + (w as usize)] == TileType::Wall { + if y < h - 2 && build_data.map.tiles[idx + w as usize] == TileType::Wall { neighbour_walls += 1; } if neighbour_walls == 2 { @@ -42,7 +42,7 @@ impl RoomCornerRounder { if let Some(rooms_builder) = &build_data.rooms { rooms = rooms_builder.clone(); } else { - unreachable!("RoomCornerRounding tried to run without any rooms."); + panic!("RoomCornerRounding requires a builder with rooms."); } for room in rooms.iter() { diff --git a/src/map_builders/room_draw.rs b/src/map_builders/room_draw.rs index 8169b82..1dcac3b 100644 --- a/src/map_builders/room_draw.rs +++ b/src/map_builders/room_draw.rs @@ -1,10 +1,10 @@ -use super::{ BuilderMap, MetaMapBuilder, Rect, TileType }; -use bracket_lib::prelude::*; +use super::{BuilderMap, MetaMapBuilder, Rect, TileType}; +use rltk::RandomNumberGenerator; pub struct RoomDrawer {} impl MetaMapBuilder for RoomDrawer { - fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { self.build(rng, build_data); } } @@ -19,7 +19,7 @@ impl RoomDrawer { for y in room.y1 + 1..=room.y2 { for x in room.x1 + 1..=room.x2 { let idx = build_data.map.xy_idx(x, y); - if idx > 0 && idx < ((build_data.map.width * build_data.map.height - 1) as usize) { + if idx > 0 && idx < ((build_data.map.width * build_data.map.height) - 1) as usize { build_data.map.tiles[idx] = TileType::Floor; } } @@ -27,17 +27,14 @@ impl RoomDrawer { } fn circle(&mut self, build_data: &mut BuilderMap, room: &Rect) { - let radius = (i32::min(room.x2 - room.x1, room.y2 - room.y1) as f32) / 2.0; - let center = room.center(); - let center_pt = Point::new(center.x, center.y); + let radius = i32::min(room.x2 - room.x1, room.y2 - room.y1) as f32 / 2.0; + let center = room.centre(); + let center_pt = rltk::Point::new(center.0, center.1); for y in room.y1..=room.y2 { for x in room.x1..=room.x2 { let idx = build_data.map.xy_idx(x, y); - let distance = DistanceAlg::Pythagoras.distance2d(center_pt, Point::new(x, y)); - if - idx > 0 && - idx < ((build_data.map.width * build_data.map.height - 1) as usize) && - distance <= radius + let distance = rltk::DistanceAlg::Pythagoras.distance2d(center_pt, rltk::Point::new(x, y)); + if idx > 0 && idx < ((build_data.map.width * build_data.map.height) - 1) as usize && distance <= radius { build_data.map.tiles[idx] = TileType::Floor; } @@ -50,7 +47,7 @@ impl RoomDrawer { if let Some(rooms_builder) = &build_data.rooms { rooms = rooms_builder.clone(); } else { - unreachable!("RoomDrawer tried to run without any rooms."); + panic!("RoomDrawer require a builder with rooms"); } for room in rooms.iter() { diff --git a/src/map_builders/room_exploder.rs b/src/map_builders/room_exploder.rs index eea3875..18c997b 100644 --- a/src/map_builders/room_exploder.rs +++ b/src/map_builders/room_exploder.rs @@ -1,10 +1,10 @@ -use super::{ paint, BuilderMap, MetaMapBuilder, Rect, Symmetry, TileType }; -use bracket_lib::prelude::*; +use super::{paint, BuilderMap, MetaMapBuilder, Rect, Symmetry, TileType}; +use rltk::RandomNumberGenerator; pub struct RoomExploder {} impl MetaMapBuilder for RoomExploder { - fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { self.build(rng, build_data); } } @@ -20,15 +20,15 @@ impl RoomExploder { if let Some(rooms_builder) = &build_data.rooms { rooms = rooms_builder.clone(); } else { - unreachable!("RoomExploder tried to run without any rooms."); + panic!("RoomExploder requires a builder with rooms."); } for room in rooms.iter() { - let start = room.center(); + let start = room.centre(); let n_diggers = rng.roll_dice(1, 20) - 5; if n_diggers > 0 { for _i in 0..n_diggers { - let mut drunk_x = start.x; - let mut drunk_y = start.y; + let mut drunk_x = start.0; + let mut drunk_y = start.1; let mut drunk_life = 20; let mut did_something = false; diff --git a/src/map_builders/room_sorter.rs b/src/map_builders/room_sorter.rs index 074d439..7cd082d 100644 --- a/src/map_builders/room_sorter.rs +++ b/src/map_builders/room_sorter.rs @@ -1,5 +1,5 @@ -use super::{ BuilderMap, MetaMapBuilder, Rect }; -use bracket_lib::prelude::*; +use super::{BuilderMap, MetaMapBuilder, Rect}; +use rltk::RandomNumberGenerator; #[allow(dead_code)] pub enum RoomSort { @@ -16,7 +16,7 @@ pub struct RoomSorter { impl MetaMapBuilder for RoomSorter { #[allow(dead_code)] - fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { self.sorter(rng, build_data); } } @@ -29,44 +29,19 @@ impl RoomSorter { fn sorter(&mut self, _rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { match self.sort_by { - RoomSort::LEFTMOST => - build_data.rooms - .as_mut() - .unwrap() - .sort_by(|a, b| a.x1.cmp(&b.x1)), - RoomSort::RIGHTMOST => - build_data.rooms - .as_mut() - .unwrap() - .sort_by(|a, b| b.x2.cmp(&a.x2)), - RoomSort::TOPMOST => - build_data.rooms - .as_mut() - .unwrap() - .sort_by(|a, b| a.y1.cmp(&b.y1)), - RoomSort::BOTTOMMOST => - build_data.rooms - .as_mut() - .unwrap() - .sort_by(|a, b| b.y2.cmp(&a.y2)), + RoomSort::LEFTMOST => build_data.rooms.as_mut().unwrap().sort_by(|a, b| a.x1.cmp(&b.x1)), + RoomSort::RIGHTMOST => build_data.rooms.as_mut().unwrap().sort_by(|a, b| b.x2.cmp(&a.x2)), + RoomSort::TOPMOST => build_data.rooms.as_mut().unwrap().sort_by(|a, b| a.y1.cmp(&b.y1)), + RoomSort::BOTTOMMOST => build_data.rooms.as_mut().unwrap().sort_by(|a, b| b.y2.cmp(&a.y2)), RoomSort::CENTRAL => { - let map_centre = Point::new(build_data.map.width / 2, build_data.map.height / 2); - build_data.rooms - .as_mut() - .unwrap() - .sort_by(|a: &Rect, b: &Rect| { - let a_centre_pt = Point::new(a.center().x, a.center().y); - let b_centre_pt = Point::new(b.center().x, b.center().y); - let distance_a = DistanceAlg::Pythagoras.distance2d( - a_centre_pt, - map_centre - ); - let distance_b = DistanceAlg::Pythagoras.distance2d( - b_centre_pt, - map_centre - ); - return distance_a.partial_cmp(&distance_b).unwrap(); - }) + let map_centre = rltk::Point::new(build_data.map.width / 2, build_data.map.height / 2); + build_data.rooms.as_mut().unwrap().sort_by(|a: &Rect, b: &Rect| { + let a_centre_pt = rltk::Point::new(a.centre().0, a.centre().1); + let b_centre_pt = rltk::Point::new(b.centre().0, b.centre().1); + let distance_a = rltk::DistanceAlg::Pythagoras.distance2d(a_centre_pt, map_centre); + let distance_b = rltk::DistanceAlg::Pythagoras.distance2d(b_centre_pt, map_centre); + return distance_a.partial_cmp(&distance_b).unwrap(); + }) } } } diff --git a/src/map_builders/room_themer.rs b/src/map_builders/room_themer.rs deleted file mode 100644 index 2cc4ca1..0000000 --- a/src/map_builders/room_themer.rs +++ /dev/null @@ -1,182 +0,0 @@ -use super::{ BuilderMap, MetaMapBuilder, Rect, TileType }; -use crate::tile_walkable; -use crate::data::messages::{ - FEATURE_TREANTS, - FEATURE_BARRACKS_GOBLIN, - FEATURE_BARRACKS_KOBOLD, - FEATURE_BARRACKS_ORC, -}; -use crate::raws; -use bracket_lib::prelude::*; -use std::collections::HashSet; - -pub enum Theme { - Grass, - Barrack, -} - -pub struct ThemeRooms { - pub theme: Theme, - pub percent: i32, - pub min_size: i32, -} - -impl MetaMapBuilder for ThemeRooms { - fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { - self.build(rng, build_data); - } -} - -impl ThemeRooms { - #[allow(dead_code)] - pub fn grass(percent: i32, min_size: i32) -> Box { - return Box::new(ThemeRooms { theme: Theme::Grass, percent, min_size }); - } - pub fn barracks(percent: i32, min_size: i32) -> Box { - return Box::new(ThemeRooms { theme: Theme::Barrack, percent, min_size }); - } - - fn grassify( - &mut self, - rng: &mut RandomNumberGenerator, - build_data: &mut BuilderMap, - room: &Rect - ) { - let (var_x, var_y) = (rng.roll_dice(1, 3), rng.roll_dice(1, 3)); - let x1 = if room.x1 - var_x > 0 { room.x1 - var_x } else { room.x1 }; - let x2 = if room.x2 + var_x < build_data.map.width - 1 { room.x2 + var_x } else { room.x2 }; - let y1 = if room.y1 - var_y > 0 { room.y1 - var_y } else { room.y1 }; - let y2 = if room.y2 + var_y < build_data.map.height - 1 { - room.y2 + var_y - } else { - room.y2 - }; - for x in x1..x2 { - for y in y1..y2 { - let idx = build_data.map.xy_idx(x, y); - if - tile_walkable(build_data.map.tiles[idx]) && - build_data.map.tiles[idx] != TileType::DownStair - { - let tar = if x < room.x1 || x > room.x2 || y < room.y1 || y > room.y2 { - 45 - } else { - 90 - }; - let roll = rng.roll_dice(1, 100); - if roll <= tar { - match rng.roll_dice(1, 6) { - 1..=4 => { - build_data.map.tiles[idx] = TileType::Grass; - } - 5 => { - build_data.map.tiles[idx] = TileType::Foliage; - } - _ => { - build_data.map.tiles[idx] = TileType::HeavyFoliage; - } - } - if roll < 5 { - build_data.spawn_list.push((idx, "treant_small".to_string())); - } - } - } - } - } - build_data.map.messages.insert(FEATURE_TREANTS.to_string()); - } - - fn place_barracks( - &mut self, - rng: &mut RandomNumberGenerator, - build_data: &mut BuilderMap, - room: &Rect - ) { - let mut possible: Vec = Vec::new(); - let (x1, x2, y1, y2) = (room.x1 + 1, room.x2 - 1, room.y1 + 1, room.y2 - 1); - for x in x1..x2 { - for y in y1..y2 { - let idx = build_data.map.xy_idx(x, y); - if - tile_walkable(build_data.map.tiles[idx]) && - build_data.map.tiles[idx] != TileType::DownStair - { - possible.push(idx); - } - } - } - - let mut needs_captain = if rng.roll_dice(1, 3) == 1 { false } else { true }; - let (captain, squad) = match rng.roll_dice(1, 4) { - 1 => { - build_data.map.messages.insert(FEATURE_BARRACKS_GOBLIN.to_string()); - ("goblin_chieftain", "squad_goblin") - } - 2 => { - build_data.map.messages.insert(FEATURE_BARRACKS_KOBOLD.to_string()); - ("kobold_captain", "squad_kobold") - } - _ => { - build_data.map.messages.insert(FEATURE_BARRACKS_ORC.to_string()); - ("orc_captain", "squad_orc") - } - }; - for idx in possible { - if idx % 2 == 0 && rng.roll_dice(1, 2) == 1 { - build_data.spawn_list.push((idx, "prop_bed".to_string())); - } else if rng.roll_dice(1, 5) == 1 { - let mob = if needs_captain { - captain.to_string() - } else { - raws::table_by_name(&raws::RAWS.lock().unwrap(), squad, None).roll(rng) - }; - needs_captain = false; - build_data.spawn_list.push((idx, mob)); - } - } - } - - fn build(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { - let rooms: Vec; - if let Some(rooms_builder) = &build_data.rooms { - rooms = rooms_builder.clone(); - } else { - unreachable!("RoomCornerRounding tried to run without any rooms."); - } - - let count = roll_until_fail(rng, self.percent); - let mut used: HashSet = HashSet::new(); - - for _i in 0..count { - let mut attempts = 0; - while attempts < 20 { - let room = rng.random_slice_entry(&rooms).unwrap(); - let (w, h) = (room.width(), room.height()); - let idx = build_data.map.xy_idx(room.x1, room.y1); - if !used.contains(&idx) && w * h >= self.min_size { - used.insert(idx); - match self.theme { - Theme::Grass => self.grassify(rng, build_data, &room), - Theme::Barrack => self.place_barracks(rng, build_data, &room), - } - build_data.take_snapshot(); - break; - } - attempts += 1; - } - } - } -} - -fn roll_until_fail(rng: &mut RandomNumberGenerator, target: i32) -> i32 { - let mut accumulator = 0; - loop { - if rng.roll_dice(1, 100) <= target - accumulator { - accumulator += 1; - console::log(accumulator); - } else { - break; - } - } - return accumulator; -} diff --git a/src/map_builders/rooms_corridors_bresenham.rs b/src/map_builders/rooms_corridors_bresenham.rs index 53e7d26..66c6290 100644 --- a/src/map_builders/rooms_corridors_bresenham.rs +++ b/src/map_builders/rooms_corridors_bresenham.rs @@ -1,12 +1,12 @@ -use super::{ BuilderMap, MetaMapBuilder, Rect, TileType }; -use bracket_lib::prelude::*; +use super::{BuilderMap, MetaMapBuilder, Rect, TileType}; +use rltk::RandomNumberGenerator; use std::collections::HashSet; pub struct BresenhamCorridors {} impl MetaMapBuilder for BresenhamCorridors { #[allow(dead_code)] - fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { self.corridors(rng, build_data); } } @@ -22,34 +22,31 @@ impl BresenhamCorridors { if let Some(rooms_builder) = &build_data.rooms { rooms = rooms_builder.clone(); } else { - unreachable!("BresenhamCorridors tried to run without any rooms."); + panic!("BresenhamCorridors require a builder with room structures"); } let mut connected: HashSet = HashSet::new(); let mut corridors: Vec> = Vec::new(); for (i, room) in rooms.iter().enumerate() { let mut room_distance: Vec<(usize, f32)> = Vec::new(); - let room_centre = room.center(); - let room_centre_pt = Point::new(room_centre.x, room_centre.y); + let room_centre = room.centre(); + let room_centre_pt = rltk::Point::new(room_centre.0, room_centre.1); for (j, other_room) in rooms.iter().enumerate() { if i != j && !connected.contains(&j) { - let other_centre = other_room.center(); - let other_centre_pt = Point::new(other_centre.x, other_centre.y); - let distance = DistanceAlg::Pythagoras.distance2d( - room_centre_pt, - other_centre_pt - ); + let other_centre = other_room.centre(); + let other_centre_pt = rltk::Point::new(other_centre.0, other_centre.1); + let distance = rltk::DistanceAlg::Pythagoras.distance2d(room_centre_pt, other_centre_pt); room_distance.push((j, distance)); } } if !room_distance.is_empty() { room_distance.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap()); - let dest_centre = rooms[room_distance[0].0].center(); - let line = line2d( - LineAlg::Bresenham, + let dest_centre = rooms[room_distance[0].0].centre(); + let line = rltk::line2d( + rltk::LineAlg::Bresenham, room_centre_pt, - Point::new(dest_centre.x, dest_centre.y) + rltk::Point::new(dest_centre.0, dest_centre.1), ); let mut corridor = Vec::new(); for cell in line.iter() { diff --git a/src/map_builders/rooms_corridors_bsp.rs b/src/map_builders/rooms_corridors_bsp.rs index 052897c..2cf2dee 100644 --- a/src/map_builders/rooms_corridors_bsp.rs +++ b/src/map_builders/rooms_corridors_bsp.rs @@ -1,11 +1,11 @@ -use super::{ draw_corridor, BuilderMap, MetaMapBuilder, Rect }; -use bracket_lib::prelude::*; +use super::{draw_corridor, BuilderMap, MetaMapBuilder, Rect}; +use rltk::RandomNumberGenerator; pub struct BspCorridors {} impl MetaMapBuilder for BspCorridors { #[allow(dead_code)] - fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { self.corridors(rng, build_data); } } @@ -21,7 +21,7 @@ impl BspCorridors { if let Some(rooms_builder) = &build_data.rooms { rooms = rooms_builder.clone(); } else { - unreachable!("BSP Corridors tried to run without any rooms."); + panic!("BSP Corridors require a builder with room structures"); } let mut corridors: Vec> = Vec::new(); @@ -30,10 +30,8 @@ impl BspCorridors { let next_room = rooms[i + 1]; let start_x = room.x1 + (rng.roll_dice(1, i32::abs(room.x1 - room.x2)) - 1); let start_y = room.y1 + (rng.roll_dice(1, i32::abs(room.y1 - room.y2)) - 1); - let end_x = - next_room.x1 + (rng.roll_dice(1, i32::abs(next_room.x1 - next_room.x2)) - 1); - let end_y = - next_room.y1 + (rng.roll_dice(1, i32::abs(next_room.y1 - next_room.y2)) - 1); + let end_x = next_room.x1 + (rng.roll_dice(1, i32::abs(next_room.x1 - next_room.x2)) - 1); + let end_y = next_room.y1 + (rng.roll_dice(1, i32::abs(next_room.y1 - next_room.y2)) - 1); let corridor = draw_corridor(&mut build_data.map, start_x, start_y, end_x, end_y); corridors.push(corridor); build_data.take_snapshot(); diff --git a/src/map_builders/rooms_corridors_dogleg.rs b/src/map_builders/rooms_corridors_dogleg.rs index 907a1a4..854b92f 100644 --- a/src/map_builders/rooms_corridors_dogleg.rs +++ b/src/map_builders/rooms_corridors_dogleg.rs @@ -1,11 +1,11 @@ -use super::{ apply_horizontal_tunnel, apply_vertical_tunnel, BuilderMap, MetaMapBuilder, Rect }; -use bracket_lib::prelude::*; +use super::{apply_horizontal_tunnel, apply_vertical_tunnel, BuilderMap, MetaMapBuilder, Rect}; +use rltk::RandomNumberGenerator; pub struct DoglegCorridors {} impl MetaMapBuilder for DoglegCorridors { #[allow(dead_code)] - fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { self.corridors(rng, build_data); } } @@ -21,27 +21,22 @@ impl DoglegCorridors { if let Some(rooms_builder) = &build_data.rooms { rooms = rooms_builder.clone(); } else { - unreachable!("DoglegCorridors tried to run without any rooms."); + panic!("DoglegCorridors require a builder with rooms."); } let mut corridors: Vec> = Vec::new(); for (i, room) in rooms.iter().enumerate() { if i > 0 { - let new = room.center(); - let prev = rooms[(i as usize) - 1].center(); + let (new_x, new_y) = room.centre(); + let (prev_x, prev_y) = rooms[i as usize - 1].centre(); if rng.range(0, 2) == 1 { - let mut c1 = apply_horizontal_tunnel( - &mut build_data.map, - prev.x, - new.x, - prev.y - ); - let mut c2 = apply_vertical_tunnel(&mut build_data.map, prev.y, new.y, new.x); + let mut c1 = apply_horizontal_tunnel(&mut build_data.map, prev_x, new_x, prev_y); + let mut c2 = apply_vertical_tunnel(&mut build_data.map, prev_y, new_y, new_x); c1.append(&mut c2); corridors.push(c1); } else { - let mut c1 = apply_vertical_tunnel(&mut build_data.map, prev.y, new.y, prev.x); - let mut c2 = apply_horizontal_tunnel(&mut build_data.map, prev.x, new.x, new.y); + let mut c1 = apply_vertical_tunnel(&mut build_data.map, prev_y, new_y, prev_x); + let mut c2 = apply_horizontal_tunnel(&mut build_data.map, prev_x, new_x, new_y); c1.append(&mut c2); corridors.push(c1); } diff --git a/src/map_builders/rooms_corridors_nearest.rs b/src/map_builders/rooms_corridors_nearest.rs index 21b0312..7bad650 100644 --- a/src/map_builders/rooms_corridors_nearest.rs +++ b/src/map_builders/rooms_corridors_nearest.rs @@ -1,12 +1,12 @@ -use super::{ draw_corridor, BuilderMap, MetaMapBuilder, Rect }; -use bracket_lib::prelude::*; +use super::{draw_corridor, BuilderMap, MetaMapBuilder, Rect}; +use rltk::RandomNumberGenerator; use std::collections::HashSet; pub struct NearestCorridors {} impl MetaMapBuilder for NearestCorridors { #[allow(dead_code)] - fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { self.corridors(rng, build_data); } } @@ -22,37 +22,29 @@ impl NearestCorridors { if let Some(rooms_builder) = &build_data.rooms { rooms = rooms_builder.clone(); } else { - unreachable!("NearestCorridors tried to run without any rooms."); + panic!("NearestCorridors requires a builder with rooms"); } let mut connected: HashSet = HashSet::new(); let mut corridors: Vec> = Vec::new(); for (i, room) in rooms.iter().enumerate() { let mut room_distance: Vec<(usize, f32)> = Vec::new(); - let room_centre = room.center(); - let room_centre_pt = Point::new(room_centre.x, room_centre.y); + let room_centre = room.centre(); + let room_centre_pt = rltk::Point::new(room_centre.0, room_centre.1); for (j, other_room) in rooms.iter().enumerate() { if i != j && !connected.contains(&j) { - let other_centre = other_room.center(); - let other_centre_pt = Point::new(other_centre.x, other_centre.y); - let distance = DistanceAlg::Pythagoras.distance2d( - room_centre_pt, - other_centre_pt - ); + let other_centre = other_room.centre(); + let other_centre_pt = rltk::Point::new(other_centre.0, other_centre.1); + let distance = rltk::DistanceAlg::Pythagoras.distance2d(room_centre_pt, other_centre_pt); room_distance.push((j, distance)); } } if !room_distance.is_empty() { room_distance.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap()); - let dest_centre = rooms[room_distance[0].0].center(); - let corridor = draw_corridor( - &mut build_data.map, - room_centre.x, - room_centre.y, - dest_centre.x, - dest_centre.y - ); + let dest_centre = rooms[room_distance[0].0].centre(); + let corridor = + draw_corridor(&mut build_data.map, room_centre.0, room_centre.1, dest_centre.0, dest_centre.1); connected.insert(i); build_data.take_snapshot(); corridors.push(corridor); diff --git a/src/map_builders/rooms_corridors_spawner.rs b/src/map_builders/rooms_corridors_spawner.rs index a62c87a..2a7d9b6 100644 --- a/src/map_builders/rooms_corridors_spawner.rs +++ b/src/map_builders/rooms_corridors_spawner.rs @@ -1,10 +1,10 @@ -use super::{ spawner, BuilderMap, MetaMapBuilder }; -use bracket_lib::prelude::*; +use super::{spawner, BuilderMap, MetaMapBuilder}; +use rltk::RandomNumberGenerator; pub struct CorridorSpawner {} impl MetaMapBuilder for CorridorSpawner { - fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { self.build(rng, build_data); } } @@ -23,11 +23,11 @@ impl CorridorSpawner { rng, &corridor, &mut build_data.spawn_list, - build_data.initial_player_level + build_data.initial_player_level, ); } } else { - unreachable!("CorridorSpawner tried to run without any corridors."); + panic!("CorridorSpawner only works after corridors have been created"); } } } diff --git a/src/map_builders/scaffold.txt b/src/map_builders/scaffold.txt index d8d7ee4..e0f7ed9 100644 --- a/src/map_builders/scaffold.txt +++ b/src/map_builders/scaffold.txt @@ -2,7 +2,7 @@ use super::{ generate_voronoi_spawn_regions, remove_unreachable_areas_returning_most_distant, spawner, Map, MapBuilder, Position, TileType, SHOW_MAPGEN, }; -use bracket_lib::prelude::*; +use rltk::RandomNumberGenerator; use specs::prelude::*; use std::collections::HashMap; diff --git a/src/map_builders/simple_map.rs b/src/map_builders/simple_map.rs index 54ab237..d0b03b9 100644 --- a/src/map_builders/simple_map.rs +++ b/src/map_builders/simple_map.rs @@ -1,5 +1,5 @@ -use super::{ BuilderMap, InitialMapBuilder, Rect }; -use bracket_lib::prelude::*; +use super::{BuilderMap, InitialMapBuilder, Rect}; +use rltk::RandomNumberGenerator; pub struct SimpleMapBuilder { room_params: (i32, i32, i32), @@ -7,7 +7,7 @@ pub struct SimpleMapBuilder { impl InitialMapBuilder for SimpleMapBuilder { #[allow(dead_code)] - fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { self.build_rooms(rng, build_data); } } @@ -22,13 +22,9 @@ impl SimpleMapBuilder { let (max_rooms, min_size, max_size); if let Some(room_params) = room_params { - (max_rooms, min_size, max_size) = (room_params.0, room_params.1, room_params.2); + (max_rooms, min_size, max_size) = (room_params.0, room_params.1, room_params.2) } else { - (max_rooms, min_size, max_size) = ( - DEFAULT_MAX_ROOMS, - DEFAULT_MIN_SIZE, - DEFAULT_MAX_SIZE, - ); + (max_rooms, min_size, max_size) = (DEFAULT_MAX_ROOMS, DEFAULT_MIN_SIZE, DEFAULT_MAX_SIZE) } Box::new(SimpleMapBuilder { room_params: (max_rooms, min_size, max_size) }) @@ -42,11 +38,11 @@ impl SimpleMapBuilder { let h = rng.range(self.room_params.1, self.room_params.2); let x = rng.roll_dice(1, build_data.map.width - w - 1) - 1; let y = rng.roll_dice(1, build_data.map.height - h - 1) - 1; - let new_room = Rect::with_size(x, y, w, h); + let new_room = Rect::new(x, y, w, h); let mut ok = true; for other_room in rooms.iter() { if new_room.intersect(other_room) { - ok = false; + ok = false } } if ok { diff --git a/src/map_builders/town.rs b/src/map_builders/town.rs index c9cf3f9..9f6122d 100644 --- a/src/map_builders/town.rs +++ b/src/map_builders/town.rs @@ -1,30 +1,17 @@ -use super::{ BuilderChain, BuilderMap, InitialMapBuilder, Position, TileType, FillEdges }; +use super::{BuilderChain, BuilderMap, InitialMapBuilder, Position, TileType}; use std::collections::HashSet; -use crate::data::names::*; -use bracket_lib::prelude::*; pub fn town_builder( new_id: i32, - _rng: &mut RandomNumberGenerator, + _rng: &mut rltk::RandomNumberGenerator, width: i32, height: i32, difficulty: i32, - initial_player_level: i32 + initial_player_level: i32, ) -> BuilderChain { - console::log(format!("DEBUGINFO: Building town (ID:{}, DIFF:{})", new_id, difficulty)); - let mut chain = BuilderChain::new( - false, - new_id, - width, - height, - difficulty, - NAME_STARTER_TOWN, - SHORTNAME_STARTER_TOWN, - 0, - initial_player_level - ); + rltk::console::log(format!("DEBUGINFO: Building town (ID:{}, DIFF:{})", new_id, difficulty)); + let mut chain = BuilderChain::new(new_id, width, height, difficulty, "the town", initial_player_level); chain.start_with(TownBuilder::new()); - chain.with(FillEdges::overmap_transition(new_id)); return chain; } @@ -33,7 +20,7 @@ pub struct TownBuilder {} impl InitialMapBuilder for TownBuilder { #[allow(dead_code)] - fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { self.build_map(rng, build_data); } } @@ -53,7 +40,7 @@ impl TownBuilder { return Box::new(TownBuilder {}); } - pub fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + pub fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { // Make visible for snapshot for t in build_data.map.visible_tiles.iter_mut() { *t = true; @@ -65,20 +52,8 @@ impl TownBuilder { let (mut available_building_tiles, wall_gap_y) = self.town_walls(rng, build_data); let mut buildings = self.buildings(rng, build_data, &mut available_building_tiles); let doors = self.add_doors(rng, build_data, &mut buildings, wall_gap_y); - self.path_from_tiles_to_nearest_tiletype( - build_data, - &doors, - TileType::Road, - TileType::Road, - true - ); - self.path_from_tiles_to_nearest_tiletype( - build_data, - &piers, - TileType::Road, - TileType::Road, - false - ); + self.path_from_tiles_to_nearest_tiletype(build_data, &doors, TileType::Road, TileType::Road, true); + self.path_from_tiles_to_nearest_tiletype(build_data, &piers, TileType::Road, TileType::Road, false); // Spawn entities let building_size = self.sort_buildings(&buildings); @@ -86,18 +61,10 @@ impl TownBuilder { self.spawn_dockers(build_data, rng); self.spawn_townsfolk(build_data, rng, &mut available_building_tiles); - build_data.starting_position = Some(Position { - x: build_data.width - 2, - y: wall_gap_y, - }); - build_data.take_snapshot(); } - fn sort_buildings( - &mut self, - buildings: &[(i32, i32, i32, i32)] - ) -> Vec<(usize, i32, BuildingTag)> { + fn sort_buildings(&mut self, buildings: &[(i32, i32, i32, i32)]) -> Vec<(usize, i32, BuildingTag)> { // Sort buildings by size, defaulting them to Unassigned buildings let mut building_size: Vec<(usize, i32, BuildingTag)> = Vec::new(); for (i, building) in buildings.iter().enumerate() { @@ -111,7 +78,7 @@ impl TownBuilder { building_size[2].2 = BuildingTag::Mine; building_size[3].2 = BuildingTag::PlayerHouse; for b in building_size.iter_mut().skip(4) { - b.2 = BuildingTag::NPCHouse; + b.2 = BuildingTag::NPCHouse } let last_idx = building_size.len() - 1; building_size[last_idx].2 = BuildingTag::Abandoned; @@ -121,10 +88,10 @@ impl TownBuilder { fn building_factory( &mut self, - rng: &mut RandomNumberGenerator, + rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap, buildings: &[(i32, i32, i32, i32)], - building_index: &[(usize, i32, BuildingTag)] + building_index: &[(usize, i32, BuildingTag)], ) { for (i, building) in buildings.iter().enumerate() { let build_tag = &building_index[i].2; @@ -140,7 +107,7 @@ impl TownBuilder { } } - fn spawn_dockers(&mut self, build_data: &mut BuilderMap, rng: &mut RandomNumberGenerator) { + fn spawn_dockers(&mut self, build_data: &mut BuilderMap, rng: &mut rltk::RandomNumberGenerator) { for (idx, tt) in build_data.map.tiles.iter().enumerate() { if *tt == TileType::Bridge && rng.roll_dice(1, 20) == 1 { let roll = rng.roll_dice(1, 2); @@ -155,8 +122,8 @@ impl TownBuilder { fn spawn_townsfolk( &mut self, build_data: &mut BuilderMap, - rng: &mut RandomNumberGenerator, - available_building_tiles: &mut HashSet + rng: &mut rltk::RandomNumberGenerator, + available_building_tiles: &mut HashSet, ) { for idx in available_building_tiles.iter() { if rng.roll_dice(1, 40) == 1 { @@ -192,18 +159,17 @@ impl TownBuilder { &mut self, building: &(i32, i32, i32, i32), build_data: &mut BuilderMap, - rng: &mut RandomNumberGenerator, + rng: &mut rltk::RandomNumberGenerator, to_place: &mut Vec<&str>, - avoid_tile: usize + avoid_tile: usize, ) { for y in building.1..building.1 + building.3 { for x in building.0..building.0 + building.2 { let idx = build_data.map.xy_idx(x, y); - if - build_data.map.tiles[idx] == TileType::WoodFloor && - idx != avoid_tile && - rng.roll_dice(1, 3) == 1 && - !to_place.is_empty() + if build_data.map.tiles[idx] == TileType::WoodFloor + && idx != avoid_tile + && rng.roll_dice(1, 3) == 1 + && !to_place.is_empty() { let entity_tag = to_place[0]; to_place.remove(0); @@ -217,12 +183,12 @@ impl TownBuilder { &mut self, building: &(i32, i32, i32, i32), build_data: &mut BuilderMap, - rng: &mut RandomNumberGenerator + rng: &mut rltk::RandomNumberGenerator, ) { - let player_idx = build_data.map.xy_idx( - building.0 + building.2 / 2, - building.1 + building.3 / 2 - ); + // Place player + build_data.starting_position = + Some(Position { x: building.0 + (building.2 / 2), y: building.1 + (building.3 / 2) }); + let player_idx = build_data.map.xy_idx(building.0 + (building.2 / 2), building.1 + (building.3 / 2)); // Place other items let mut to_place: Vec<&str> = vec![ @@ -234,7 +200,7 @@ impl TownBuilder { "prop_table", "prop_table", "prop_chair", - "prop_chair" + "prop_chair", ]; self.random_building_spawn(building, build_data, rng, &mut to_place, player_idx); } @@ -243,7 +209,7 @@ impl TownBuilder { &mut self, building: &(i32, i32, i32, i32), build_data: &mut BuilderMap, - rng: &mut RandomNumberGenerator + rng: &mut rltk::RandomNumberGenerator, ) { let mut to_place: Vec<&str> = vec![ "npc_priest", @@ -254,7 +220,7 @@ impl TownBuilder { "prop_table", "prop_table", "prop_candle", - "prop_candle" + "prop_candle", ]; self.random_building_spawn(building, build_data, rng, &mut to_place, 0) } @@ -263,13 +229,10 @@ impl TownBuilder { &mut self, building: &(i32, i32, i32, i32), build_data: &mut BuilderMap, - rng: &mut RandomNumberGenerator + rng: &mut rltk::RandomNumberGenerator, ) { // Place exit - let exit_idx = build_data.map.xy_idx( - building.0 + building.2 / 2, - building.1 + building.3 / 2 - ); + let exit_idx = build_data.map.xy_idx(building.0 + (building.2 / 2), building.1 + (building.3 / 2)); build_data.map.tiles[exit_idx] = TileType::DownStair; let mut to_place: Vec<&str> = vec!["npc_miner", "npc_miner", "npc_guard", "prop_chair"]; self.random_building_spawn(building, build_data, rng, &mut to_place, exit_idx) @@ -279,15 +242,9 @@ impl TownBuilder { &mut self, building: &(i32, i32, i32, i32), build_data: &mut BuilderMap, - rng: &mut RandomNumberGenerator + rng: &mut rltk::RandomNumberGenerator, ) { - let mut to_place: Vec<&str> = vec![ - "prop_bed", - "prop_table", - "dog_little", - "prop_chair", - "prop_chair" - ]; + let mut to_place: Vec<&str> = vec!["prop_bed", "prop_table", "dog_little", "prop_chair", "prop_chair"]; self.random_building_spawn(building, build_data, rng, &mut to_place, 0); } @@ -295,14 +252,9 @@ impl TownBuilder { &mut self, building: &(i32, i32, i32, i32), build_data: &mut BuilderMap, - rng: &mut RandomNumberGenerator + rng: &mut rltk::RandomNumberGenerator, ) { - let mut to_place: Vec<&str> = vec![ - "npc_townsperson", - "prop_bed", - "prop_table", - "prop_chair" - ]; + let mut to_place: Vec<&str> = vec!["npc_townsperson", "prop_bed", "prop_table", "prop_chair"]; self.random_building_spawn(building, build_data, rng, &mut to_place, 0); } @@ -310,7 +262,7 @@ impl TownBuilder { &mut self, building: &(i32, i32, i32, i32), build_data: &mut BuilderMap, - rng: &mut RandomNumberGenerator + rng: &mut rltk::RandomNumberGenerator, ) { let mut to_place: Vec<&str> = vec!["rat", "rat", "rat", "prop_table", "prop_chair"]; self.random_building_spawn(building, build_data, rng, &mut to_place, 0); @@ -324,11 +276,7 @@ impl TownBuilder { build_data.take_snapshot(); } - fn water_and_piers( - &mut self, - rng: &mut RandomNumberGenerator, - build_data: &mut BuilderMap - ) -> Vec { + fn water_and_piers(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) -> Vec { let mut n = (rng.roll_dice(1, 65535) as f32) / 65535f32; let mut water_width: Vec = Vec::new(); let variance = 5; @@ -337,8 +285,7 @@ impl TownBuilder { let sand_width = shallow_width + 4; for y in 0..build_data.height { - let n_water = - ((f32::sin(n) * (variance as f32)) as i32) + minimum_width + rng.roll_dice(1, 2); + let n_water = (f32::sin(n) * variance as f32) as i32 + minimum_width + rng.roll_dice(1, 2); water_width.push(n_water); n += 0.1; for x in 0..n_water { @@ -376,10 +323,10 @@ impl TownBuilder { let start_roll = rng.roll_dice(1, 4); let largest_water_width; - if water_width[y as usize] > water_width[(y as usize) + 1] { + if water_width[y as usize] > water_width[y as usize + 1] { largest_water_width = water_width[y as usize]; } else { - largest_water_width = water_width[(y as usize) + 1]; + largest_water_width = water_width[y as usize + 1]; } // Make pier length @@ -412,8 +359,8 @@ impl TownBuilder { fn town_walls( &mut self, - rng: &mut RandomNumberGenerator, - build_data: &mut BuilderMap + rng: &mut rltk::RandomNumberGenerator, + build_data: &mut BuilderMap, ) -> (HashSet, i32) { let mut available_building_tiles: HashSet = HashSet::new(); @@ -423,10 +370,7 @@ impl TownBuilder { const HALF_PATH_THICKNESS: i32 = 3; let wall_gap_y = - build_data.height / 2 + - rng.roll_dice(1, PATH_OFFSET_FROM_CENTRE * 2) - - 1 - - PATH_OFFSET_FROM_CENTRE; + (build_data.height / 2) + rng.roll_dice(1, PATH_OFFSET_FROM_CENTRE * 2) - 1 - PATH_OFFSET_FROM_CENTRE; for y in BORDER..build_data.height - BORDER { if !(y > wall_gap_y - HALF_PATH_THICKNESS && y < wall_gap_y + HALF_PATH_THICKNESS) { @@ -440,19 +384,14 @@ impl TownBuilder { let gravel_idx = build_data.map.xy_idx(x, y); let roll = rng.roll_dice(1, 6); match roll { - 1 => { - build_data.map.tiles[gravel_idx] = TileType::Foliage; - } - 2 => { - build_data.map.tiles[gravel_idx] = TileType::HeavyFoliage; - } + 1 => build_data.map.tiles[gravel_idx] = TileType::Foliage, + 2 => build_data.map.tiles[gravel_idx] = TileType::HeavyFoliage, _ => {} } - if - y > BORDER + 1 && - y < build_data.height - BORDER - 1 && - x > OFFSET_FROM_LEFT + 2 && - x < build_data.width - BORDER - 1 + if y > BORDER + 1 + && y < build_data.height - BORDER - 1 + && x > OFFSET_FROM_LEFT + 2 + && x < build_data.width - BORDER - 1 { available_building_tiles.insert(gravel_idx); } @@ -479,9 +418,9 @@ impl TownBuilder { fn buildings( &mut self, - rng: &mut RandomNumberGenerator, + rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap, - available_building_tiles: &mut HashSet + available_building_tiles: &mut HashSet, ) -> Vec<(i32, i32, i32, i32)> { let mut buildings: Vec<(i32, i32, i32, i32)> = Vec::new(); let mut n_buildings = 0; @@ -493,9 +432,7 @@ impl TownBuilder { const MAX_BUILDING_SIZE: i32 = 10; while n_buildings < REQUIRED_BUILDINGS { - let bx = - rng.roll_dice(1, build_data.map.width - OFFSET_FROM_LEFT - BORDER) + - OFFSET_FROM_LEFT; + let bx = rng.roll_dice(1, build_data.map.width - OFFSET_FROM_LEFT - BORDER) + OFFSET_FROM_LEFT; let by = rng.roll_dice(1, build_data.map.height) - BORDER; let bw = rng.roll_dice(1, MAX_BUILDING_SIZE - MIN_BUILDING_SIZE) + MIN_BUILDING_SIZE; let bh = rng.roll_dice(1, MAX_BUILDING_SIZE - MIN_BUILDING_SIZE) + MIN_BUILDING_SIZE; @@ -521,9 +458,9 @@ impl TownBuilder { build_data.map.tiles[idx] = TileType::WoodFloor; available_building_tiles.remove(&idx); available_building_tiles.remove(&(idx + 1)); - available_building_tiles.remove(&(idx + (build_data.width as usize))); + available_building_tiles.remove(&(idx + build_data.width as usize)); available_building_tiles.remove(&(idx - 1)); - available_building_tiles.remove(&(idx - (build_data.width as usize))); + available_building_tiles.remove(&(idx - build_data.width as usize)); } } build_data.take_snapshot(); @@ -543,16 +480,10 @@ impl TownBuilder { if build_data.map.tiles[idx + 1] != TileType::WoodFloor { neighbours += 1; } - if - build_data.map.tiles[idx - (build_data.width as usize)] != - TileType::WoodFloor - { + if build_data.map.tiles[idx - build_data.width as usize] != TileType::WoodFloor { neighbours += 1; } - if - build_data.map.tiles[idx + (build_data.width as usize)] != - TileType::WoodFloor - { + if build_data.map.tiles[idx + build_data.width as usize] != TileType::WoodFloor { neighbours += 1; } if neighbours > 0 { @@ -569,15 +500,15 @@ impl TownBuilder { fn add_doors( &mut self, - rng: &mut RandomNumberGenerator, + rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap, buildings: &mut Vec<(i32, i32, i32, i32)>, - wall_gap_y: i32 + wall_gap_y: i32, ) -> Vec { let mut doors = Vec::new(); for building in buildings.iter() { let door_x = building.0 + 1 + rng.roll_dice(1, building.2 - 3); - let cy = building.1 + building.3 / 2; + let cy = building.1 + (building.3 / 2); let idx = if cy > wall_gap_y { // Door on north wall build_data.map.xy_idx(door_x, building.1) @@ -598,33 +529,30 @@ impl TownBuilder { tiles: &[usize], tiletype: TileType, new_road_tiletype: TileType, - include_new_tiles: bool + include_new_tiles: bool, ) { let mut roads = self.find_tiletype(build_data, tiletype); build_data.map.populate_blocked(); for tile_idx in tiles.iter() { let mut nearest_tiletype: Vec<(usize, f32)> = Vec::new(); - let tile_pt = Point::new( - (*tile_idx as i32) % (build_data.map.width as i32), - (*tile_idx as i32) / (build_data.map.width as i32) + let tile_pt = rltk::Point::new( + *tile_idx as i32 % build_data.map.width as i32, + *tile_idx as i32 / build_data.map.width as i32, ); for r in roads.iter() { nearest_tiletype.push(( *r, - DistanceAlg::Manhattan.distance2d( + rltk::DistanceAlg::Manhattan.distance2d( tile_pt, - Point::new( - (*r as i32) % build_data.map.width, - (*r as i32) / build_data.map.width - ) + rltk::Point::new(*r as i32 % build_data.map.width, *r as i32 / build_data.map.width), ), )); } nearest_tiletype.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap()); let destination = nearest_tiletype[0].0; - let path = a_star_search(*tile_idx, destination, &mut build_data.map); + let path = rltk::a_star_search(*tile_idx, destination, &mut build_data.map); if path.success { for step in path.steps.iter() { let idx = *step as usize; diff --git a/src/map_builders/voronoi.rs b/src/map_builders/voronoi.rs index 585a1b2..b48bcb7 100644 --- a/src/map_builders/voronoi.rs +++ b/src/map_builders/voronoi.rs @@ -1,5 +1,5 @@ -use super::{ BuilderMap, InitialMapBuilder, TileType }; -use bracket_lib::prelude::*; +use super::{BuilderMap, InitialMapBuilder, TileType}; +use rltk::RandomNumberGenerator; #[derive(PartialEq, Copy, Clone)] #[allow(dead_code)] @@ -16,7 +16,7 @@ pub struct VoronoiBuilder { impl InitialMapBuilder for VoronoiBuilder { #[allow(dead_code)] - fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { self.build(rng, build_data); } } @@ -40,39 +40,35 @@ impl VoronoiBuilder { #[allow(clippy::map_entry)] fn build(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { // Make a Voronoi diagram. We'll do this the hard way to learn about the technique! - let mut voronoi_seeds: Vec<(usize, Point)> = Vec::new(); + let mut voronoi_seeds: Vec<(usize, rltk::Point)> = Vec::new(); while voronoi_seeds.len() < self.n_seeds { let vx = rng.roll_dice(1, build_data.map.width - 1); let vy = rng.roll_dice(1, build_data.map.height - 1); let vidx = build_data.map.xy_idx(vx, vy); - let candidate = (vidx, Point::new(vx, vy)); + let candidate = (vidx, rltk::Point::new(vx, vy)); if !voronoi_seeds.contains(&candidate) { voronoi_seeds.push(candidate); } } let mut voronoi_distance = vec![(0, 0.0f32); self.n_seeds]; - let mut voronoi_membership: Vec = - vec![0; build_data.map.width as usize * build_data.map.height as usize]; + let mut voronoi_membership: Vec = vec![0; build_data.map.width as usize * build_data.map.height as usize]; for (i, vid) in voronoi_membership.iter_mut().enumerate() { - let x = (i as i32) % build_data.map.width; - let y = (i as i32) / build_data.map.width; + let x = i as i32 % build_data.map.width; + let y = i as i32 / build_data.map.width; for (seed, pos) in voronoi_seeds.iter().enumerate() { let distance; match self.distance_algorithm { DistanceAlgorithm::Pythagoras => { - distance = DistanceAlg::PythagorasSquared.distance2d( - Point::new(x, y), - pos.1 - ); + distance = rltk::DistanceAlg::PythagorasSquared.distance2d(rltk::Point::new(x, y), pos.1); } DistanceAlgorithm::Manhattan => { - distance = DistanceAlg::Manhattan.distance2d(Point::new(x, y), pos.1); + distance = rltk::DistanceAlg::Manhattan.distance2d(rltk::Point::new(x, y), pos.1); } DistanceAlgorithm::Chebyshev => { - distance = DistanceAlg::Chebyshev.distance2d(Point::new(x, y), pos.1); + distance = rltk::DistanceAlg::Chebyshev.distance2d(rltk::Point::new(x, y), pos.1); } } voronoi_distance[seed] = (seed, distance); diff --git a/src/map_builders/voronoi_spawning.rs b/src/map_builders/voronoi_spawning.rs index 9a25d50..81e15b4 100644 --- a/src/map_builders/voronoi_spawning.rs +++ b/src/map_builders/voronoi_spawning.rs @@ -1,12 +1,11 @@ -use super::{ spawner, BuilderMap, MetaMapBuilder }; -use crate::tile_walkable; -use bracket_lib::prelude::*; +use super::{spawner, BuilderMap, MetaMapBuilder, TileType}; +use rltk::RandomNumberGenerator; use std::collections::HashMap; pub struct VoronoiSpawning {} impl MetaMapBuilder for VoronoiSpawning { - fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { self.build(rng, build_data); } } @@ -20,15 +19,15 @@ impl VoronoiSpawning { #[allow(clippy::map_entry)] fn build(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { let mut noise_areas: HashMap> = HashMap::new(); - let mut noise = FastNoise::seeded(rng.roll_dice(1, 65536) as u64); - noise.set_noise_type(NoiseType::Cellular); + let mut noise = rltk::FastNoise::seeded(rng.roll_dice(1, 65536) as u64); + noise.set_noise_type(rltk::NoiseType::Cellular); noise.set_frequency(0.08); - noise.set_cellular_distance_function(CellularDistanceFunction::Manhattan); + noise.set_cellular_distance_function(rltk::CellularDistanceFunction::Manhattan); for y in 1..build_data.map.height - 1 { for x in 1..build_data.map.width - 1 { let idx = build_data.map.xy_idx(x, y); - if tile_walkable(build_data.map.tiles[idx]) { + if build_data.map.tiles[idx] == TileType::Floor { let cell_value_f = noise.get_noise(x as f32, y as f32) * 10240.0; let cell_value = cell_value_f as i32; @@ -48,7 +47,7 @@ impl VoronoiSpawning { rng, area.1, &mut build_data.spawn_list, - build_data.initial_player_level + build_data.initial_player_level, ); } } diff --git a/src/map_builders/wfc/constraints.rs b/src/map_builders/wfc/constraints.rs index 3851a6e..e9504a0 100644 --- a/src/map_builders/wfc/constraints.rs +++ b/src/map_builders/wfc/constraints.rs @@ -1,13 +1,7 @@ -use super::{ Map, MapChunk, TileType }; +use super::{Map, MapChunk, TileType}; use std::collections::HashSet; -use bracket_lib::prelude::*; -pub fn build_patterns( - map: &Map, - chunk_size: i32, - include_flipping: bool, - dedupe: bool -) -> Vec> { +pub fn build_patterns(map: &Map, chunk_size: i32, include_flipping: bool, dedupe: bool) -> Vec> { let chunks_x = map.width / chunk_size; let chunks_y = map.height / chunk_size; let mut patterns = Vec::new(); @@ -55,22 +49,16 @@ pub fn build_patterns( // Dedupe if dedupe { - console::log(format!("Pre de-duplication, there are {} patterns.", patterns.len())); + rltk::console::log(format!("Pre de-duplication, there are {} patterns.", patterns.len())); let set: HashSet> = patterns.drain(..).collect(); // Dedupes patterns.extend(set.into_iter()); - console::log(format!("There are {} patterns.", patterns.len())); + rltk::console::log(format!("There are {} patterns.", patterns.len())); } return patterns; } -pub fn render_pattern_to_map( - map: &mut Map, - chunk: &MapChunk, - chunk_size: i32, - start_x: i32, - start_y: i32 -) { +pub fn render_pattern_to_map(map: &mut Map, chunk: &MapChunk, chunk_size: i32, start_x: i32, start_y: i32) { let mut i = 0usize; for tile_y in 0..chunk_size { for tile_x in 0..chunk_size { @@ -82,25 +70,25 @@ pub fn render_pattern_to_map( } for (x, northbound) in chunk.exits[0].iter().enumerate() { if *northbound { - let map_idx = map.xy_idx(start_x + (x as i32), start_y); + let map_idx = map.xy_idx(start_x + x as i32, start_y); map.tiles[map_idx] = TileType::DownStair; } } for (x, southbound) in chunk.exits[1].iter().enumerate() { if *southbound { - let map_idx = map.xy_idx(start_x + (x as i32), start_y + chunk_size - 1); + let map_idx = map.xy_idx(start_x + x as i32, start_y + chunk_size - 1); map.tiles[map_idx] = TileType::DownStair; } } for (x, westbound) in chunk.exits[2].iter().enumerate() { if *westbound { - let map_idx = map.xy_idx(start_x, start_y + (x as i32)); + let map_idx = map.xy_idx(start_x, start_y + x as i32); map.tiles[map_idx] = TileType::DownStair; } } for (x, eastbound) in chunk.exits[3].iter().enumerate() { if *eastbound { - let map_idx = map.xy_idx(start_x + chunk_size - 1, start_y + (x as i32)); + let map_idx = map.xy_idx(start_x + chunk_size - 1, start_y + x as i32); map.tiles[map_idx] = TileType::DownStair; } } diff --git a/src/map_builders/wfc/mod.rs b/src/map_builders/wfc/mod.rs index 3ee7504..46168da 100644 --- a/src/map_builders/wfc/mod.rs +++ b/src/map_builders/wfc/mod.rs @@ -1,5 +1,5 @@ -use super::{ BuilderMap, Map, MetaMapBuilder, TileType }; -use bracket_lib::prelude::*; +use super::{BuilderMap, Map, MetaMapBuilder, TileType}; +use rltk::RandomNumberGenerator; mod common; use common::*; mod constraints; @@ -11,7 +11,7 @@ use solver::*; pub struct WaveFunctionCollapseBuilder {} impl MetaMapBuilder for WaveFunctionCollapseBuilder { - fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) { + fn build_map(&mut self, rng: &mut rltk::RandomNumberGenerator, build_data: &mut BuilderMap) { self.build(rng, build_data); } } @@ -32,14 +32,11 @@ impl WaveFunctionCollapseBuilder { self.render_tile_gallery(&constraints, CHUNK_SIZE, build_data); build_data.map = Map::new( - build_data.map.overmap, build_data.map.id, build_data.map.width, build_data.map.height, build_data.map.difficulty, &build_data.map.name, - &build_data.map.short_name, - build_data.map.depth ); loop { let mut solver = Solver::new(constraints.clone(), CHUNK_SIZE, &build_data.map); @@ -54,22 +51,8 @@ impl WaveFunctionCollapseBuilder { build_data.spawn_list.clear(); } - fn render_tile_gallery( - &mut self, - constraints: &[MapChunk], - chunk_size: i32, - build_data: &mut BuilderMap - ) { - build_data.map = Map::new( - false, - 0, - build_data.width, - build_data.height, - 0, - &build_data.map.name, - &build_data.map.short_name, - build_data.map.depth - ); + fn render_tile_gallery(&mut self, constraints: &[MapChunk], chunk_size: i32, build_data: &mut BuilderMap) { + build_data.map = Map::new(0, build_data.width, build_data.height, 0, &build_data.map.name); let mut counter = 0; let mut x = 1; let mut y = 1; @@ -85,16 +68,7 @@ impl WaveFunctionCollapseBuilder { if y + chunk_size > build_data.map.height { // Move to the next page build_data.take_snapshot(); - build_data.map = Map::new( - false, - 0, - build_data.width, - build_data.height, - 0, - &build_data.map.name, - &build_data.map.short_name, - build_data.map.depth - ); + build_data.map = Map::new(0, build_data.width, build_data.height, 0, &build_data.map.name); x = 1; y = 1; diff --git a/src/map_builders/wfc/solver.rs b/src/map_builders/wfc/solver.rs index 1a8f826..548f4df 100644 --- a/src/map_builders/wfc/solver.rs +++ b/src/map_builders/wfc/solver.rs @@ -1,6 +1,5 @@ -use super::{ Map, MapChunk }; +use super::{Map, MapChunk}; use std::collections::HashSet; -use bracket_lib::prelude::*; pub struct Solver { constraints: Vec, @@ -17,7 +16,7 @@ impl Solver { let chunks_x = (map.width / chunk_size) as usize; let chunks_y = (map.height / chunk_size) as usize; let mut remaining: Vec<(usize, i32)> = Vec::new(); - for i in 0..chunks_x * chunks_y { + for i in 0..(chunks_x * chunks_y) { remaining.push((i, 0)); } @@ -33,7 +32,7 @@ impl Solver { } fn chunk_idx(&self, x: usize, y: usize) -> usize { - return (y * self.chunks_x + x) as usize; + return ((y * self.chunks_x) + x) as usize; } fn count_neighbours(&self, chunk_x: usize, chunk_y: usize) -> i32 { @@ -100,11 +99,8 @@ impl Solver { self.remaining = remain_copy; // Pick a random chunk we haven't dealt with yet and get its index, remove from remaining list - let remaining_index = if !neighbours_exist { - (rng.roll_dice(1, self.remaining.len() as i32) - 1) as usize - } else { - 0usize - }; + let remaining_index = + if !neighbours_exist { (rng.roll_dice(1, self.remaining.len() as i32) - 1) as usize } else { 0usize }; let chunk_index = self.remaining[remaining_index].0; self.remaining.remove(remaining_index); @@ -162,10 +158,10 @@ impl Solver { // There is nothing nearby, so we can have anything! let new_chunk_idx = (rng.roll_dice(1, self.constraints.len() as i32) - 1) as usize; self.chunks[chunk_index] = Some(new_chunk_idx); - let left_x = (chunk_x as i32) * (self.chunk_size as i32); - let right_x = ((chunk_x as i32) + 1) * (self.chunk_size as i32); - let top_y = (chunk_y as i32) * (self.chunk_size as i32); - let bottom_y = ((chunk_y as i32) + 1) * (self.chunk_size as i32); + let left_x = chunk_x as i32 * self.chunk_size as i32; + let right_x = (chunk_x as i32 + 1) * self.chunk_size as i32; + let top_y = chunk_y as i32 * self.chunk_size as i32; + let bottom_y = (chunk_y as i32 + 1) * self.chunk_size as i32; let mut i: usize = 0; for y in top_y..bottom_y { @@ -199,21 +195,18 @@ impl Solver { } if possible_options.is_empty() { - console::log("Oh no! It's not possible!"); + rltk::console::log("Oh no! It's not possible!"); self.possible = false; return true; } else { - let new_chunk_idx = if possible_options.len() == 1 { - 0 - } else { - rng.roll_dice(1, possible_options.len() as i32) - 1 - }; + let new_chunk_idx = + if possible_options.len() == 1 { 0 } else { rng.roll_dice(1, possible_options.len() as i32) - 1 }; self.chunks[chunk_index] = Some(new_chunk_idx as usize); - let left_x = (chunk_x as i32) * (self.chunk_size as i32); - let right_x = ((chunk_x as i32) + 1) * (self.chunk_size as i32); - let top_y = (chunk_y as i32) * (self.chunk_size as i32); - let bottom_y = ((chunk_y as i32) + 1) * (self.chunk_size as i32); + let left_x = chunk_x as i32 * self.chunk_size as i32; + let right_x = (chunk_x as i32 + 1) * self.chunk_size as i32; + let top_y = chunk_y as i32 * self.chunk_size as i32; + let bottom_y = (chunk_y as i32 + 1) * self.chunk_size as i32; let mut i: usize = 0; for y in top_y..bottom_y { diff --git a/src/map_indexing_system.rs b/src/map_indexing_system.rs new file mode 100644 index 0000000..f49b28c --- /dev/null +++ b/src/map_indexing_system.rs @@ -0,0 +1,26 @@ +use super::{BlocksTile, Map, Position}; +use specs::prelude::*; + +pub struct MapIndexingSystem {} + +impl<'a> System<'a> for MapIndexingSystem { + type SystemData = (WriteExpect<'a, Map>, ReadStorage<'a, Position>, ReadStorage<'a, BlocksTile>, Entities<'a>); + + fn run(&mut self, data: Self::SystemData) { + let (mut map, position, blockers, entities) = data; + + map.populate_blocked(); + map.clear_content_index(); + for (entity, position) in (&entities, &position).join() { + let idx = map.xy_idx(position.x, position.y); + + let _p: Option<&BlocksTile> = blockers.get(entity); + if let Some(_p) = _p { + map.blocked[idx] = true; + } + + // Push the entity to the appropriate index slot. + map.tile_content[idx].push(entity); + } + } +} diff --git a/src/melee_combat_system.rs b/src/melee_combat_system.rs index cc63807..48c8fd7 100644 --- a/src/melee_combat_system.rs +++ b/src/melee_combat_system.rs @@ -1,31 +1,8 @@ use super::{ - effects::{ add_effect, EffectType, Targets }, - gamelog, - gamesystem, - gui::renderable_colour, - ArmourClassBonus, - Attributes, - Blind, - EquipmentSlot, - Equipped, - HungerClock, - HungerState, - MeleeWeapon, - MultiAttack, - Name, - NaturalAttacks, - ParticleBuilder, - Pools, - Position, - Renderable, - Skill, - Skills, - ToHitBonus, - WantsToMelee, + gamelog, gamesystem, ArmourClassBonus, Attributes, EquipmentSlot, Equipped, HungerClock, HungerState, MeleeWeapon, + MultiAttack, Name, NaturalAttacks, ParticleBuilder, Pools, Position, Skill, Skills, SufferDamage, WantsToMelee, WeaponAttribute, - config::CONFIG, }; -use bracket_lib::prelude::*; use specs::prelude::*; pub struct MeleeCombatSystem {} @@ -34,45 +11,41 @@ impl<'a> System<'a> for MeleeCombatSystem { type SystemData = ( Entities<'a>, ReadExpect<'a, Entity>, - ReadStorage<'a, Renderable>, WriteStorage<'a, WantsToMelee>, ReadStorage<'a, Name>, ReadStorage<'a, Attributes>, ReadStorage<'a, Skills>, ReadStorage<'a, Pools>, + WriteStorage<'a, SufferDamage>, WriteExpect<'a, ParticleBuilder>, ReadStorage<'a, Position>, ReadStorage<'a, Equipped>, ReadStorage<'a, MeleeWeapon>, ReadStorage<'a, NaturalAttacks>, ReadStorage<'a, ArmourClassBonus>, - ReadStorage<'a, ToHitBonus>, ReadStorage<'a, HungerClock>, ReadStorage<'a, MultiAttack>, - ReadStorage<'a, Blind>, - WriteExpect<'a, RandomNumberGenerator>, + WriteExpect<'a, rltk::RandomNumberGenerator>, ); fn run(&mut self, data: Self::SystemData) { let ( entities, player_entity, - renderables, mut wants_melee, names, attributes, skills, pools, + mut inflict_damage, mut particle_builder, positions, equipped, melee_weapons, natural_attacks, ac, - to_hit, hunger_clock, multi_attackers, - blind_entities, mut rng, ) = data; @@ -89,17 +62,13 @@ impl<'a> System<'a> for MeleeCombatSystem { // e.g. An attacker with +0 to-hit hitting a target with 0 AC: // 1d20 must be less than 10, 45% chance of a hit + const COMBAT_LOGGING: bool = true; let mut logger = gamelog::Logger::new(); let mut something_to_log = false; - for (entity, wants_melee, name, attacker_attributes, attacker_skills, attacker_pools) in ( - &entities, - &wants_melee, - &names, - &attributes, - &skills, - &pools, - ).join() { + for (entity, wants_melee, name, attacker_attributes, attacker_skills, attacker_pools) in + (&entities, &wants_melee, &names, &attributes, &skills, &pools).join() + { // Create blank vector of attacks being attempted. let mut attacks: Vec<(MeleeWeapon, String)> = Vec::new(); let mut multi_attack = false; @@ -123,12 +92,11 @@ impl<'a> System<'a> for MeleeCombatSystem { } else { attacks.push(( MeleeWeapon { - damage_type: crate::DamageType::Physical, attribute: WeaponAttribute::Strength, + hit_bonus: 0, damage_n_dice: 1, damage_die_type: 4, damage_bonus: 0, - hit_bonus: 0, }, "punches".to_string(), )); @@ -145,22 +113,16 @@ impl<'a> System<'a> for MeleeCombatSystem { if target_pools.hit_points.current <= 0 { break; } + let weapon_info = attack.0; let attack_verb = attack.1; + // Get all offensive bonuses let d20 = rng.roll_dice(1, 20); let attribute_hit_bonus = attacker_attributes.dexterity.bonus; let skill_hit_bonus = gamesystem::skill_bonus(Skill::Melee, &*attacker_skills); - let mut equipment_hit_bonus = weapon_info.hit_bonus; - for (wielded, to_hit) in (&equipped, &to_hit).join() { - if wielded.owner == entity { - equipment_hit_bonus += to_hit.amount; - } - } + let weapon_hit_bonus = weapon_info.hit_bonus; let mut status_hit_bonus = 0; - if let Some(_) = blind_entities.get(entity) { - status_hit_bonus -= 4; - } let hc = hunger_clock.get(entity); if let Some(hc) = hc { match hc.state { @@ -176,18 +138,12 @@ impl<'a> System<'a> for MeleeCombatSystem { _ => {} } } - // Total to-hit bonus let attacker_bonuses = - 1 + // +1 for being in melee combat - attacker_pools.level + // + level - attribute_hit_bonus + // +- str/dex bonus depending on weapon used - skill_hit_bonus + // +- relevant skill modifier - equipment_hit_bonus + // +- any other to-hit modifiers from equipment - status_hit_bonus; // +- any to-hit modifiers from status effects + attacker_pools.level + attribute_hit_bonus + skill_hit_bonus + weapon_hit_bonus + status_hit_bonus; // Get armour class let bac = target_pools.bac; - let attribute_ac_bonus = target_attributes.dexterity.bonus / 2; + let attribute_ac_bonus = target_attributes.dexterity.bonus; let skill_ac_bonus = gamesystem::skill_bonus(Skill::Defence, &*target_skills); let mut armour_ac_bonus = 0; for (wielded, ac) in (&equipped, &ac).join() { @@ -195,8 +151,7 @@ impl<'a> System<'a> for MeleeCombatSystem { armour_ac_bonus += ac.amount; } } - let actual_armour_class = - bac - attribute_ac_bonus - skill_ac_bonus - armour_ac_bonus; + let actual_armour_class = bac - attribute_ac_bonus - skill_ac_bonus - armour_ac_bonus; let mut armour_class_roll = actual_armour_class; if actual_armour_class < 0 { @@ -206,145 +161,100 @@ impl<'a> System<'a> for MeleeCombatSystem { armour_class_roll = -armour_class_roll; } - // Monster attacks receive a +10 to-hit bonus against the player. - let monster_v_player_bonus = if wants_melee.target == *player_entity { - 10 - } else { - 0 - }; - - let target_number = monster_v_player_bonus + armour_class_roll + attacker_bonuses; + let target_number = 10 + armour_class_roll + attacker_bonuses; let target_name = names.get(wants_melee.target).unwrap(); - if CONFIG.logging.log_combat { - console::log( - format!( - "ATTACKLOG: {} *{}* {}: rolled ({}) 1d20 vs. {} ({} + {}AC + {}to-hit)", - &name.name, - attack_verb, - &target_name.name, - d20, - target_number, - monster_v_player_bonus, - armour_class_roll, - attacker_bonuses - ) - ); + if COMBAT_LOGGING { + rltk::console::log(format!( + "ATTACKLOG: {} *{}* {}: rolled ({}) 1d20 vs. {} (10 + {}AC + {}to-hit)", + &name.name, + attack_verb, + &target_name.name, + d20, + target_number, + armour_class_roll, + attacker_bonuses + )); } if d20 < target_number { // Target hit! - let base_damage = rng.roll_dice( - weapon_info.damage_n_dice, - weapon_info.damage_die_type - ); - let skill_damage_bonus = gamesystem::skill_bonus( - Skill::Melee, - &*attacker_skills - ); + let base_damage = rng.roll_dice(weapon_info.damage_n_dice, weapon_info.damage_die_type); + let skill_damage_bonus = gamesystem::skill_bonus(Skill::Melee, &*attacker_skills); let mut attribute_damage_bonus = weapon_info.damage_bonus; match weapon_info.attribute { - WeaponAttribute::Dexterity => { - attribute_damage_bonus += attacker_attributes.dexterity.bonus; - } - WeaponAttribute::Strength => { - attribute_damage_bonus += attacker_attributes.strength.bonus; - } + WeaponAttribute::Dexterity => attribute_damage_bonus += attacker_attributes.dexterity.bonus, + WeaponAttribute::Strength => attribute_damage_bonus += attacker_attributes.strength.bonus, WeaponAttribute::Finesse => { - if - attacker_attributes.dexterity.bonus > - attacker_attributes.strength.bonus - { + if attacker_attributes.dexterity.bonus > attacker_attributes.strength.bonus { attribute_damage_bonus += attacker_attributes.dexterity.bonus; } else { attribute_damage_bonus += attacker_attributes.strength.bonus; } } } - let mut damage = i32::max( - 0, - base_damage + skill_damage_bonus + attribute_damage_bonus - ); + let mut damage = i32::max(0, base_damage + skill_damage_bonus + attribute_damage_bonus); - if CONFIG.logging.log_combat { - console::log( - format!( - "ATTACKLOG: {} HIT for {} ({}[{}d{}]+{}[skill]+{}[attr])", - &name.name, - damage, - base_damage, - weapon_info.damage_n_dice, - weapon_info.damage_die_type, - skill_damage_bonus, - attribute_damage_bonus - ) - ); + if COMBAT_LOGGING { + rltk::console::log(format!( + "ATTACKLOG: {} HIT for {} ({}[{}d{}]+{}[skill]+{}[attr])", + &name.name, + damage, + base_damage, + weapon_info.damage_n_dice, + weapon_info.damage_die_type, + skill_damage_bonus, + attribute_damage_bonus + )); } if actual_armour_class < 0 { let ac_damage_reduction = rng.roll_dice(1, -actual_armour_class); damage = i32::min(1, damage - ac_damage_reduction); - if CONFIG.logging.log_combat { - console::log( - format!( - "ATTACKLOG: {} reduced their damage taken by {} (1dAC), and took {} hp damage.", - &target_name.name, - ac_damage_reduction, - damage - ) - ); + if COMBAT_LOGGING { + rltk::console::log(format!( + "ATTACKLOG: {} reduced their damage taken by {} (1dAC), and took {} hp damage.", + &target_name.name, ac_damage_reduction, damage + )); } } let pos = positions.get(wants_melee.target); if let Some(pos) = pos { - particle_builder.damage_taken(pos.x, pos.y); + particle_builder.damage_taken(pos.x, pos.y) } - add_effect( - Some(entity), - EffectType::Damage { amount: damage, damage_type: weapon_info.damage_type }, - Targets::Entity { target: wants_melee.target } - ); + SufferDamage::new_damage(&mut inflict_damage, wants_melee.target, damage, entity == *player_entity); if entity == *player_entity { something_to_log = true; logger = logger // You hit the . .append("You hit the") - .colour(renderable_colour(&renderables, wants_melee.target)) - .append_n(&target_name.name) - .colour(WHITE) + .npc_name_n(&target_name.name) .period(); } else if wants_melee.target == *player_entity { something_to_log = true; logger = logger // hits you! .append("The") - .colour(renderable_colour(&renderables, entity)) - .append(&name.name) - .colour(WHITE) + .npc_name(&name.name) .append(attack_verb) .append("you!"); } else { - gamelog::Logger - ::new() // misses the . + gamelog::Logger::new() // misses the . .append("The") - .colour(renderable_colour(&renderables, entity)) - .append(&name.name) - .colour(WHITE) + .npc_name(&name.name) .append(attack_verb) .append("the") - .colour(renderable_colour(&renderables, wants_melee.target)) - .append_n(&target_name.name) - .colour(WHITE) + .npc_name_n(&target_name.name) .period() .log(); } } else { - if CONFIG.logging.log_combat { - console::log(format!("ATTACKLOG: {} *MISSED*", &name.name)); + if COMBAT_LOGGING { + rltk::console::log(format!("ATTACKLOG: {} *MISSED*", &name.name)); } let pos = positions.get(wants_melee.target); if let Some(pos) = pos { - particle_builder.attack_miss(pos.x, pos.y); + particle_builder.attack_miss(pos.x, pos.y) } if entity == *player_entity { something_to_log = true; @@ -354,21 +264,16 @@ impl<'a> System<'a> for MeleeCombatSystem { something_to_log = true; logger = logger // misses! .append("The") - .colour(renderable_colour(&renderables, entity)) - .append(&name.name) - .colour(WHITE) + .npc_name(&name.name) + .colour(rltk::WHITE) .append("misses!"); } else { - gamelog::Logger - ::new() // misses the . + gamelog::Logger::new() // misses the . .append("The") - .colour(renderable_colour(&renderables, entity)) - .append(&name.name) - .colour(WHITE) + .npc_name(&name.name) + .colour(rltk::WHITE) .append("misses the") - .colour(renderable_colour(&renderables, wants_melee.target)) - .append_n(&target_name.name) - .colour(WHITE) + .npc_name_n(&target_name.name) .period() .log(); } @@ -383,17 +288,16 @@ impl<'a> System<'a> for MeleeCombatSystem { } fn get_natural_attacks( - rng: &mut RandomNumberGenerator, + rng: &mut rltk::RandomNumberGenerator, nat: NaturalAttacks, multi_attack: bool, - attacks: &mut Vec<(MeleeWeapon, String)> + attacks: &mut Vec<(MeleeWeapon, String)>, ) { if !nat.attacks.is_empty() { if multi_attack { for a in nat.attacks.iter() { attacks.push(( MeleeWeapon { - damage_type: a.damage_type, attribute: WeaponAttribute::Strength, hit_bonus: a.hit_bonus, damage_n_dice: a.damage_n_dice, @@ -404,14 +308,10 @@ fn get_natural_attacks( )); } } else { - let attack_index = if nat.attacks.len() == 1 { - 0 - } else { - (rng.roll_dice(1, nat.attacks.len() as i32) as usize) - 1 - }; + let attack_index = + if nat.attacks.len() == 1 { 0 } else { rng.roll_dice(1, nat.attacks.len() as i32) as usize - 1 }; attacks.push(( MeleeWeapon { - damage_type: nat.attacks[attack_index].damage_type, attribute: WeaponAttribute::Strength, hit_bonus: nat.attacks[attack_index].hit_bonus, damage_n_dice: nat.attacks[attack_index].damage_n_dice, @@ -428,7 +328,7 @@ fn get_weapon_attack( wielded: &Equipped, melee: &MeleeWeapon, entity: Entity, - attacks: &mut Vec<(MeleeWeapon, String)> + attacks: &mut Vec<(MeleeWeapon, String)>, ) -> bool { if wielded.owner == entity && wielded.slot == EquipmentSlot::Melee { attacks.push((melee.clone(), "hits".to_string())); diff --git a/src/monster_ai_system.rs b/src/monster_ai_system.rs new file mode 100644 index 0000000..a3fed20 --- /dev/null +++ b/src/monster_ai_system.rs @@ -0,0 +1,69 @@ +use super::{bystander_ai_system, EntityMoved, Map, Monster, Position, TakingTurn, Viewshed, WantsToMelee}; +use rltk::Point; +use specs::prelude::*; + +pub struct MonsterAI {} + +impl<'a> System<'a> for MonsterAI { + #[allow(clippy::type_complexity)] + type SystemData = ( + WriteExpect<'a, Map>, + WriteExpect<'a, rltk::RandomNumberGenerator>, + ReadExpect<'a, Point>, + ReadExpect<'a, Entity>, + Entities<'a>, + WriteStorage<'a, Viewshed>, + ReadStorage<'a, Monster>, + WriteStorage<'a, Position>, + WriteStorage<'a, WantsToMelee>, + WriteStorage<'a, EntityMoved>, + ReadStorage<'a, TakingTurn>, + ); + + fn run(&mut self, data: Self::SystemData) { + let ( + mut map, + mut rng, + player_pos, + player_entity, + entities, + mut viewshed, + monster, + mut position, + mut wants_to_melee, + mut entity_moved, + turns, + ) = data; + + for (entity, mut viewshed, _monster, mut pos, _turn) in + (&entities, &mut viewshed, &monster, &mut position, &turns).join() + { + let distance = rltk::DistanceAlg::Pythagoras.distance2d(Point::new(pos.x, pos.y), *player_pos); + if distance < 1.5 { + wants_to_melee + .insert(entity, WantsToMelee { target: *player_entity }) + .expect("Unable to insert attack."); + } else if viewshed.visible_tiles.contains(&*player_pos) { + // If the player is visible, but the path is obstructed, this will currently search + // the entire map (i.e. Will do a huge ASTAR to find an alternate route), and the + // mob will follow that path until it leaves vision, then lose sight of the player + // and stop. + let path = rltk::a_star_search(map.xy_idx(pos.x, pos.y), map.xy_idx(player_pos.x, player_pos.y), &*map); + if path.success && path.steps.len() > 1 { + let mut idx = map.xy_idx(pos.x, pos.y); + map.blocked[idx] = false; + pos.x = (path.steps[1] as i32) % map.width; + pos.y = (path.steps[1] as i32) / map.width; + idx = map.xy_idx(pos.x, pos.y); + map.blocked[idx] = true; + viewshed.dirty = true; + entity_moved.insert(entity, EntityMoved {}).expect("Unable to insert marker"); + } + } else { + if bystander_ai_system::try_move_randomly(&mut pos, &mut rng, &mut map, &mut viewshed) { + entity_moved.insert(entity, EntityMoved {}).expect("Unable to insert marker"); + } + } + } + } +} diff --git a/src/morgue.rs b/src/morgue.rs deleted file mode 100644 index b0160d9..0000000 --- a/src/morgue.rs +++ /dev/null @@ -1,284 +0,0 @@ -use std::fs::{ File, create_dir_all }; -use std::io::{ self, Write }; -use std::time::SystemTime; -use super::Map; -use crate::gamelog; -use crate::components::*; -use crate::gui::{ Class, Ancestry, unobf_name_ecs }; -use specs::prelude::*; -use bracket_lib::prelude::*; -use to_char; -use std::collections::HashMap; -use crate::data::events::*; - -#[cfg(target_arch = "wasm32")] -pub fn create_morgue_file(ecs: &World) { - console::log("wasm32 doesn't support writing files yet, so here's the morgue info:"); - let morgue_info = create_morgue_string(ecs); - console::log(morgue_info); -} - -#[cfg(not(target_arch = "wasm32"))] -pub fn create_morgue_file(ecs: &World) { - let morgue_dir = "morgue"; - if let Err(err) = create_dir_all(&morgue_dir) { - console::log(format!("Unable to create the directory (/{}): {}", morgue_dir, err)); - } - let morgue_info = create_morgue_string(ecs); - let file_name = create_file_name(ecs, morgue_dir); - if let Err(err) = write_morgue_file(file_name.as_str(), morgue_info.as_str()) { - console::log(format!("Unable to write the morgue file: {}", err)); - }; -} - -fn create_file_name(ecs: &World, morgue_dir: &str) -> String { - let e = ecs.fetch::(); - let pools = ecs.read_storage::(); - let pool = pools.get(*e).unwrap(); - let class = match ecs.read_storage::().get(*e).unwrap().name { - Class::Fighter => "fighter", - Class::Wizard => "wizard", - Class::Rogue => "rogue", - Class::Villager => "villager", - }; - let ancestry = match ecs.read_storage::().get(*e).unwrap().name { - Ancestry::Human => "human", - Ancestry::Elf => "elf", - Ancestry::Dwarf => "dwarf", - Ancestry::Gnome => "gnome", - Ancestry::Catfolk => "catfolk", - Ancestry::NULL => "NULL", - }; - return format!( - "{}/lv{}-{}-{}-{}.txt", - morgue_dir, - &pool.level, - &ancestry, - &class, - get_timestamp() - ); -} - -fn create_morgue_string(ecs: &World) -> String { - // Initialise default - let mut morgue_info: String = Default::default(); - let e = ecs.fetch::(); - let class = match ecs.read_storage::().get(*e).unwrap().name { - Class::Fighter => "fighter", - Class::Wizard => "wizard", - Class::Rogue => "rogue", - Class::Villager => "villager", - }; - let ancestry = match ecs.read_storage::().get(*e).unwrap().name { - Ancestry::Human => "human", - Ancestry::Elf => "elf", - Ancestry::Dwarf => "dwarf", - Ancestry::Gnome => "gnome", - Ancestry::Catfolk => "catfolk", - Ancestry::NULL => "NULL", - }; - let pools = ecs.read_storage::(); - let pool = pools.get(*e).unwrap(); - let header = format!("{} {}, level {}/{}", &ancestry, &class, &pool.level, &pool.xp); - morgue_info.push_str(&create_boxed_text(header.as_str(), None)); - morgue_info.push_str(&draw_tombstone(ecs, header.len())); - morgue_info.push_str(&draw_map(ecs)); - morgue_info.push_str("\n"); - morgue_info.push_str(&create_boxed_text("Equipment", None)); - morgue_info.push_str(&draw_equipment(ecs)); - morgue_info.push_str(&create_boxed_text("Backpack", None)); - morgue_info.push_str(&draw_backpack(ecs)); - morgue_info.push_str(&create_boxed_text("Significant Events", None)); - morgue_info.push_str(&draw_events_list()); - - return morgue_info; -} - -fn write_morgue_file(file_name: &str, morgue_info: &str) -> Result<(), io::Error> { - // Save to file - let mut file = File::create(&file_name)?; // Open/create morgue file - file.write_all(morgue_info.as_bytes())?; - Ok(()) -} - -fn get_timestamp() -> String { - return SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs().to_string(); -} - -fn create_boxed_text(content: &str, width: Option) -> String { - let width = if width.is_some() { width.unwrap() } else { content.len() + 2 }; - let horizontal = format!("{:═^w$}", "", w = width); - return format!("╔{h}╗\n║ {c} ║\n╚{h}╝\n", h = horizontal, c = content); -} - -fn draw_tombstone(ecs: &World, len: usize) -> String { - let pad = (len - 17) / 2; - let map = ecs.fetch::(); - let pools = ecs.read_storage::(); - let pool = pools.get(*ecs.fetch::()).unwrap(); - let attrs = ecs.read_storage::(); - let attr = attrs.get(*ecs.fetch::()).unwrap(); - return format!( - "{:^p$} .-'~~~`-. HP {}/{} MP {}/{}\n{:^p$} .' `.\n{:^p$} | rest | STR {:>2} ({:+}) CON {:>2} ({:+}) WIS {:>2} ({:+})\n{:^p$} | in | DEX {:>2} ({:+}) INT {:>2} ({:+}) CHA {:>2} ({:+})\n{:^p$} | peace |\n{:^p$}\\\\| |// You died in {} [id {}], after {} turns.\n{:^p$}^^^^^^^^^^^^^^^^^{:^p$}\n", - "", - pool.hit_points.current, - pool.hit_points.max, - pool.mana.current, - pool.mana.max, - "", - "", - attr.strength.base + attr.strength.modifiers, - attr.strength.bonus, - attr.constitution.base + attr.constitution.modifiers, - attr.constitution.bonus, - attr.wisdom.base + attr.wisdom.modifiers, - attr.wisdom.bonus, - "", - attr.dexterity.base + attr.dexterity.modifiers, - attr.dexterity.bonus, - attr.intelligence.base + attr.intelligence.modifiers, - attr.intelligence.bonus, - attr.charisma.base + attr.charisma.modifiers, - attr.charisma.bonus, - "", - "", - map.name, - map.id, - gamelog::get_event_count(EVENT::COUNT_TURN), - "", - "", - p = pad - ); -} - -fn draw_map(ecs: &World) -> String { - let map = ecs.fetch::(); - let mut result: String = Default::default(); - let point = ecs.fetch::(); - for y in 0..map.height { - for x in 0..map.width { - let idx = map.xy_idx(x, y); - let mut glyph_u16: u16 = 0; - if idx == map.xy_idx(point.x, point.y) { - glyph_u16 = to_cp437('@'); - } else if crate::spatial::has_tile_content(idx) { - let mut render_order = 0; - crate::spatial::for_each_tile_content(idx, |e| { - if let Some(renderable) = ecs.read_storage::().get(e) { - if renderable.render_order >= render_order { - render_order = renderable.render_order; - glyph_u16 = renderable.glyph; - } - } - }); - } else { - glyph_u16 = crate::map::themes::get_tile_renderables_for_id( - idx, - &*map, - None, - Some(true) - ).0; - } - let char = to_char((glyph_u16 & 0xff) as u8); - result.push_str(&char.to_string()); - } - result.push_str("\n"); - } - return result; -} - -fn draw_equipment(ecs: &World) -> String { - // Get all of the player's equipment. - let mut equip: HashMap = HashMap::new(); - let equipped = ecs.read_storage::(); - for (entity, _e, _n) in (&ecs.entities(), &equipped, &ecs.read_storage::()) - .join() - .filter(|item| item.1.owner == *ecs.fetch::()) { - equip - .entry(entity) - .and_modify(|count| { - *count += 1; - }) - .or_insert(1); - } - let mut result: String = Default::default(); - for item in equip { - let slot = match equipped.get(item.0).unwrap().slot { - EquipmentSlot::Melee => "l-hand -", - EquipmentSlot::Shield => "r-hand -", - EquipmentSlot::Head => "head -", - EquipmentSlot::Body => "body -", - EquipmentSlot::Feet => "feet -", - EquipmentSlot::Hands => "hands -", - EquipmentSlot::Back => "back -", - EquipmentSlot::Neck => "neck -", - }; - let name = if item.1 != 1 { - unobf_name_ecs(ecs, item.0).1 - } else { - unobf_name_ecs(ecs, item.0).0 - }; - result.push_str(&format!("{:>8} {}\n", slot, name)); - } - result.push_str("\n"); - return result; -} - -fn draw_backpack(ecs: &World) -> String { - // Get all of the player's backpack. - let mut pack: HashMap<(String, String), (i32, Entity)> = HashMap::new(); - for (entity, _bp, _n) in ( - &ecs.entities(), - &ecs.read_storage::(), - &ecs.read_storage::(), - ) - .join() - .filter(|item| item.1.owner == *ecs.fetch::()) { - pack.entry(unobf_name_ecs(ecs, entity)) - .and_modify(|(count, _e)| { - *count += 1; - }) - .or_insert((1, entity)); - } - let mut result: String = Default::default(); - for item in pack { - let name = if item.1.0 != 1 { - format!("{} {}", item.1.0, item.0.1) - } else { - // TODO: Get correct article (a/an/some) here, write a fn for it. - item.0.0 - }; - result.push_str(&format!("- {}\n", name)); - } - result.push_str("\n"); - return result; -} - -fn draw_events_list() -> String { - // Initialise default (empty) string - let mut result: String = Default::default(); - // Get lock on events mutex - let lock = gamelog::EVENTS.lock().unwrap(); - // Collect all keys, and sort in ascending value (by turn count) - let mut sorted_keys: Vec = lock - .keys() - .map(|k| *k) - .collect(); - sorted_keys.sort(); - // Iterate through sorted keys, looking for corresponding values, and append on newline - for key in sorted_keys { - if let Some(value) = lock.get(&key) { - result.push_str(&format!("{:<4} | ", key)); - for (i, event) in value.iter().enumerate() { - if i > 0 { - result.push_str(&format!("; {}", event.to_lowercase())); - } else { - result.push_str(&format!("{}", event)); - } - } - result.push_str("\n"); - } - } - - return result; -} diff --git a/src/particle_system.rs b/src/particle_system.rs index 69e12b7..48f5811 100644 --- a/src/particle_system.rs +++ b/src/particle_system.rs @@ -1,19 +1,25 @@ -use super::{ ParticleLifetime, Position, Renderable, BTerm }; -use bracket_lib::prelude::*; +use super::{ParticleLifetime, Position, Renderable, Rltk}; +use rltk::RGB; use specs::prelude::*; -use crate::data::visuals::{ DEFAULT_PARTICLE_LIFETIME, SHORT_PARTICLE_LIFETIME }; + +pub const SHORT_PARTICLE_LIFETIME: f32 = 100.0; +// For things which will happen frequently - i.e. attacking. +pub const DEFAULT_PARTICLE_LIFETIME: f32 = 200.0; +// For exceptional things, like large AOEs, to make sure the +// player can actually see what's being impacted - i.e. fireball. +pub const LONG_PARTICLE_LIFETIME: f32 = 300.0; /// Runs each tick, deleting particles who are past their expiry. // Should make an addition to this to also spawn delayed particles, // running through a list and removing the frame_time_ms from the // delay. When delay is <= 0, make a particle_builder.request for // the particle. -pub fn particle_ticker(ecs: &mut World, ctx: &BTerm) { +pub fn particle_ticker(ecs: &mut World, ctx: &Rltk) { cull_dead_particles(ecs, ctx); create_delayed_particles(ecs, ctx); } -fn cull_dead_particles(ecs: &mut World, ctx: &BTerm) { +fn cull_dead_particles(ecs: &mut World, ctx: &Rltk) { let mut dead_particles: Vec = Vec::new(); { // Age out particles @@ -31,18 +37,11 @@ fn cull_dead_particles(ecs: &mut World, ctx: &BTerm) { } } -pub fn check_queue(ecs: &World) -> bool { - let particle_builder = ecs.read_resource::(); - if particle_builder.delayed_requests.is_empty() && particle_builder.requests.is_empty() { - return true; - } - return false; -} - -fn create_delayed_particles(ecs: &mut World, ctx: &BTerm) { +fn create_delayed_particles(ecs: &mut World, ctx: &Rltk) { let mut particle_builder = ecs.write_resource::(); let mut handled_particles: Vec = Vec::new(); for delayed_particle in particle_builder.delayed_requests.iter_mut() { + rltk::console::log(delayed_particle.delay); delayed_particle.delay -= ctx.frame_time_ms; if delayed_particle.delay < 0.0 { handled_particles.push(ParticleRequest { @@ -70,26 +69,14 @@ fn create_delayed_particles(ecs: &mut World, ctx: &BTerm) { let mut renderables = ecs.write_storage::(); let mut particles = ecs.write_storage::(); for handled in handled_particles { - let index = particle_builder.delayed_requests - .iter() - .position(|x| x.particle == handled) - .unwrap(); + let index = particle_builder.delayed_requests.iter().position(|x| x.particle == handled).unwrap(); particle_builder.delayed_requests.remove(index); let p = entities.create(); - positions - .insert(p, Position { x: handled.x, y: handled.y }) - .expect("Could not insert position"); + positions.insert(p, Position { x: handled.x, y: handled.y }).expect("Could not insert position"); renderables - .insert(p, Renderable { - fg: handled.fg, - bg: handled.bg, - glyph: handled.glyph, - render_order: 0, - }) + .insert(p, Renderable { fg: handled.fg, bg: handled.bg, glyph: handled.glyph, render_order: 0 }) .expect("Could not insert renderables"); - particles - .insert(p, ParticleLifetime { lifetime_ms: handled.lifetime }) - .expect("Could not insert lifetime"); + particles.insert(p, ParticleLifetime { lifetime_ms: handled.lifetime }).expect("Could not insert lifetime"); } } @@ -99,7 +86,7 @@ pub struct ParticleRequest { y: i32, fg: RGB, bg: RGB, - glyph: FontCharType, + glyph: rltk::FontCharType, lifetime: f32, } @@ -121,42 +108,23 @@ impl ParticleBuilder { } /// Makes a single particle request. - pub fn request( - &mut self, - x: i32, - y: i32, - fg: RGB, - bg: RGB, - glyph: FontCharType, - lifetime: f32 - ) { + pub fn request(&mut self, x: i32, y: i32, fg: RGB, bg: RGB, glyph: rltk::FontCharType, lifetime: f32) { self.requests.push(ParticleRequest { x, y, fg, bg, glyph, lifetime }); } - pub fn delay( - &mut self, - x: i32, - y: i32, - fg: RGB, - bg: RGB, - glyph: FontCharType, - lifetime: f32, - delay: f32 - ) { - self.delayed_requests.push(DelayedParticleRequest { - delay: delay, - particle: ParticleRequest { x, y, fg, bg, glyph, lifetime }, - }); + pub fn delay(&mut self, x: i32, y: i32, fg: RGB, bg: RGB, glyph: rltk::FontCharType, lifetime: f32, delay: f32) { + self.delayed_requests + .push(DelayedParticleRequest { delay: delay, particle: ParticleRequest { x, y, fg, bg, glyph, lifetime } }); } pub fn damage_taken(&mut self, x: i32, y: i32) { self.request( x, y, - RGB::named(ORANGE), - RGB::named(BLACK), - to_cp437('‼'), - DEFAULT_PARTICLE_LIFETIME + rltk::RGB::named(rltk::ORANGE), + rltk::RGB::named(rltk::BLACK), + rltk::to_cp437('‼'), + DEFAULT_PARTICLE_LIFETIME, ); } @@ -164,10 +132,32 @@ impl ParticleBuilder { self.request( x, y, - RGB::named(CYAN), - RGB::named(BLACK), - to_cp437('‼'), - DEFAULT_PARTICLE_LIFETIME + rltk::RGB::named(rltk::CYAN), + rltk::RGB::named(rltk::BLACK), + rltk::to_cp437('‼'), + DEFAULT_PARTICLE_LIFETIME, + ); + } + + pub fn trap_triggered(&mut self, x: i32, y: i32) { + self.request( + x, + y, + rltk::RGB::named(rltk::RED), + rltk::RGB::named(rltk::RED), + rltk::to_cp437('‼'), + DEFAULT_PARTICLE_LIFETIME, + ); + } + + pub fn heal(&mut self, x: i32, y: i32) { + self.request( + x, + y, + rltk::RGB::named(rltk::GREEN), + rltk::RGB::named(rltk::BLACK), + rltk::to_cp437('♥'), + DEFAULT_PARTICLE_LIFETIME, ); } @@ -175,10 +165,10 @@ impl ParticleBuilder { self.request( x, y, - RGB::named(CHOCOLATE), - RGB::named(BLACK), - to_cp437('‼'), - SHORT_PARTICLE_LIFETIME + rltk::RGB::named(rltk::CHOCOLATE), + rltk::RGB::named(rltk::BLACK), + rltk::to_cp437('‼'), + SHORT_PARTICLE_LIFETIME, ); } @@ -190,97 +180,56 @@ impl ParticleBuilder { y: i32, fg: RGB, bg: RGB, - glyph: FontCharType, + glyph: rltk::FontCharType, lifetime: f32, - secondary_fg: RGB + secondary_fg: RGB, ) { let eighth_l = lifetime / 8.0; let quarter_l = eighth_l * 2.0; self.request(x, y, fg, bg, glyph, lifetime); - self.delay( - x + 1, - y + 1, - secondary_fg.lerp(bg, 0.8), - bg, - to_cp437('/'), - quarter_l, - eighth_l - ); - self.delay( - x + 1, - y - 1, - secondary_fg.lerp(bg, 0.6), - bg, - to_cp437('\\'), - quarter_l, - quarter_l - ); - self.delay( - x - 1, - y - 1, - secondary_fg.lerp(bg, 0.2), - bg, - to_cp437('/'), - quarter_l, - eighth_l * 3.0 - ); - self.delay( - x - 1, - y + 1, - secondary_fg.lerp(bg, 0.4), - bg, - to_cp437('\\'), - quarter_l, - lifetime - ); + self.delay(x + 1, y + 1, secondary_fg.lerp(bg, 0.8), bg, rltk::to_cp437('/'), quarter_l, eighth_l); + self.delay(x + 1, y - 1, secondary_fg.lerp(bg, 0.6), bg, rltk::to_cp437('\\'), quarter_l, quarter_l); + self.delay(x - 1, y - 1, secondary_fg.lerp(bg, 0.2), bg, rltk::to_cp437('/'), quarter_l, eighth_l * 3.0); + self.delay(x - 1, y + 1, secondary_fg.lerp(bg, 0.4), bg, rltk::to_cp437('\\'), quarter_l, lifetime); } // Makes a rainbow particle request in the shape of an 'x'. Sort of. - #[allow(dead_code)] - pub fn request_rainbow_star(&mut self, x: i32, y: i32, glyph: FontCharType, lifetime: f32) { - let bg = RGB::named(BLACK); + pub fn request_rainbow_star(&mut self, x: i32, y: i32, glyph: rltk::FontCharType, lifetime: f32) { + let bg = RGB::named(rltk::BLACK); let eighth_l = lifetime / 8.0; let quarter_l = eighth_l * 2.0; let half_l = quarter_l * 2.0; - self.request(x, y, RGB::named(CYAN), bg, glyph, lifetime); - self.delay(x + 1, y + 1, RGB::named(RED), bg, to_cp437('\\'), half_l, eighth_l); - self.delay(x + 1, y - 1, RGB::named(ORANGE), bg, to_cp437('/'), half_l, quarter_l); - self.delay(x - 1, y - 1, RGB::named(GREEN), bg, to_cp437('\\'), half_l, eighth_l * 3.0); - self.delay(x - 1, y + 1, RGB::named(YELLOW), bg, to_cp437('/'), half_l, half_l); + self.request(x, y, RGB::named(rltk::CYAN), bg, glyph, lifetime); + self.delay(x + 1, y + 1, RGB::named(rltk::RED), bg, rltk::to_cp437('\\'), half_l, eighth_l); + self.delay(x + 1, y - 1, RGB::named(rltk::ORANGE), bg, rltk::to_cp437('/'), half_l, quarter_l); + self.delay(x - 1, y - 1, RGB::named(rltk::GREEN), bg, rltk::to_cp437('\\'), half_l, eighth_l * 3.0); + self.delay(x - 1, y + 1, RGB::named(rltk::YELLOW), bg, rltk::to_cp437('/'), half_l, half_l); } // Makes a rainbow particle request. Sort of. #[allow(dead_code)] - pub fn request_rainbow(&mut self, x: i32, y: i32, glyph: FontCharType, lifetime: f32) { - let bg = RGB::named(BLACK); + pub fn request_rainbow(&mut self, x: i32, y: i32, glyph: rltk::FontCharType, lifetime: f32) { + let bg = RGB::named(rltk::BLACK); let eighth_l = lifetime / 8.0; - self.request(x, y, RGB::named(RED), bg, glyph, eighth_l); - self.delay(x, y, RGB::named(ORANGE), bg, glyph, eighth_l, eighth_l); - self.delay(x, y, RGB::named(YELLOW), bg, glyph, eighth_l, eighth_l * 2.0); - self.delay(x, y, RGB::named(GREEN), bg, glyph, eighth_l, eighth_l * 3.0); - self.delay(x, y, RGB::named(BLUE), bg, glyph, eighth_l, eighth_l * 4.0); - self.delay(x, y, RGB::named(INDIGO), bg, glyph, eighth_l, eighth_l * 5.0); - self.delay(x, y, RGB::named(VIOLET), bg, glyph, eighth_l, eighth_l * 6.0); + self.request(x, y, RGB::named(rltk::RED), bg, glyph, eighth_l); + self.delay(x, y, RGB::named(rltk::ORANGE), bg, glyph, eighth_l, eighth_l); + self.delay(x, y, RGB::named(rltk::YELLOW), bg, glyph, eighth_l, eighth_l * 2.0); + self.delay(x, y, RGB::named(rltk::GREEN), bg, glyph, eighth_l, eighth_l * 3.0); + self.delay(x, y, RGB::named(rltk::BLUE), bg, glyph, eighth_l, eighth_l * 4.0); + self.delay(x, y, RGB::named(rltk::INDIGO), bg, glyph, eighth_l, eighth_l * 5.0); + self.delay(x, y, RGB::named(rltk::VIOLET), bg, glyph, eighth_l, eighth_l * 6.0); } /// Makes a particle request in the shape of a +. #[allow(dead_code)] - pub fn request_plus( - &mut self, - x: i32, - y: i32, - fg: RGB, - bg: RGB, - glyph: FontCharType, - lifetime: f32 - ) { + pub fn request_plus(&mut self, x: i32, y: i32, fg: RGB, bg: RGB, glyph: rltk::FontCharType, lifetime: f32) { self.request(x, y, fg, bg, glyph, lifetime * 2.0); - self.request(x + 1, y, fg, bg, to_cp437('─'), lifetime); - self.request(x - 1, y, fg, bg, to_cp437('─'), lifetime); - self.request(x, y + 1, fg, bg, to_cp437('│'), lifetime); - self.request(x, y - 1, fg, bg, to_cp437('│'), lifetime); + self.request(x + 1, y, fg, bg, rltk::to_cp437('─'), lifetime); + self.request(x - 1, y, fg, bg, rltk::to_cp437('─'), lifetime); + self.request(x, y + 1, fg, bg, rltk::to_cp437('│'), lifetime); + self.request(x, y - 1, fg, bg, rltk::to_cp437('│'), lifetime); } } @@ -301,16 +250,12 @@ impl<'a> System<'a> for ParticleSpawnSystem { for new_particle in particle_builder.requests.iter() { let p = entities.create(); - positions - .insert(p, Position { x: new_particle.x, y: new_particle.y }) - .expect("Could not insert position"); + positions.insert(p, Position { x: new_particle.x, y: new_particle.y }).expect("Could not insert position"); renderables - .insert(p, Renderable { - fg: new_particle.fg, - bg: new_particle.bg, - glyph: new_particle.glyph, - render_order: 0, - }) + .insert( + p, + Renderable { fg: new_particle.fg, bg: new_particle.bg, glyph: new_particle.glyph, render_order: 0 }, + ) .expect("Could not insert renderables"); particles .insert(p, ParticleLifetime { lifetime_ms: new_particle.lifetime }) diff --git a/src/player.rs b/src/player.rs index 36af74b..5d8da5b 100644 --- a/src/player.rs +++ b/src/player.rs @@ -1,45 +1,11 @@ use super::{ - effects::{ add_effect, EffectType, Targets }, - gamelog, - gui::obfuscate_name_ecs, - gui::renderable_colour_ecs, - gui::item_colour_ecs, - camera::get_screen_bounds, - raws::Reaction, - Attributes, - BlocksTile, - BlocksVisibility, - Door, - EntityMoved, - Faction, - HasAncestry, - Hidden, - HungerClock, - HungerState, - Item, - Map, - Name, - Player, - Pools, - Position, - Renderable, - RunState, - states::state::*, - Telepath, - TileType, - Viewshed, - WantsToMelee, - WantsToPickupItem, - WantsToAssignKey, - get_dest, - Destination, - DamageType, + gamelog, gui::get_item_display_name, Attributes, BlocksTile, BlocksVisibility, Bystander, Door, EntityMoved, + Hidden, HungerClock, HungerState, Item, Map, Monster, Name, ParticleBuilder, Player, Pools, Position, Renderable, + RunState, State, SufferDamage, Telepath, TileType, Viewshed, WantsToMelee, WantsToPickupItem, }; -use bracket_lib::prelude::*; +use rltk::{Point, RandomNumberGenerator, Rltk, VirtualKeyCode}; use specs::prelude::*; -use std::cmp::{ max, min }; -use crate::data::events::*; -use crate::data::ids::*; +use std::cmp::{max, min}; pub fn try_door(i: i32, j: i32, ecs: &mut World) -> RunState { let mut positions = ecs.write_storage::(); @@ -52,88 +18,52 @@ pub fn try_door(i: i32, j: i32, ecs: &mut World) -> RunState { let mut doors = ecs.write_storage::(); let mut blocks_visibility = ecs.write_storage::(); let mut blocks_movement = ecs.write_storage::(); + let mut renderables = ecs.write_storage::(); let names = ecs.read_storage::(); let mut rng = ecs.write_resource::(); let mut result = RunState::AwaitingInput; let mut door_pos: Option = None; - for (_entity, _player, pos, attributes) in ( - &entities, - &mut players, - &mut positions, - &attributes, - ).join() { + for (_entity, _player, pos, attributes) in (&entities, &mut players, &mut positions, &attributes).join() { let delta_x = i; let delta_y = j; - if - !( - pos.x + delta_x < 0 || - pos.x + delta_x > map.width - 1 || - pos.y + delta_y < 0 || - pos.y + delta_y > map.height - 1 - ) + if !(pos.x + delta_x < 0 + || pos.x + delta_x > map.width - 1 + || pos.y + delta_y < 0 + || pos.y + delta_y > map.height - 1) { let destination_idx = map.xy_idx(pos.x + delta_x, pos.y + delta_y); - if !crate::spatial::has_tile_content(destination_idx) { + if map.tile_content[destination_idx].len() == 0 { gamelog::Logger::new().append("You see no door there.").log(); } - let mut multiple_tile_content = false; - if crate::spatial::length(destination_idx) > 1 { - multiple_tile_content = true; - } - crate::spatial::for_each_tile_content(destination_idx, |potential_target| { - let door = doors.get_mut(potential_target); + for potential_target in map.tile_content[destination_idx].iter() { + let door = doors.get_mut(*potential_target); if let Some(door) = door { if door.open == true { - let renderables = ecs.read_storage::(); - if multiple_tile_content { - if let Some(name) = names.get(potential_target) { - gamelog::Logger - ::new() - .append("The") - .colour(renderable_colour_ecs(ecs, potential_target)) - .append(&name.name) - .colour(WHITE) - .append("is blocked.") - .log(); + if map.tile_content[destination_idx].len() > 1 { + if let Some(name) = names.get(*potential_target) { + gamelog::Logger::new().append("The").item_name(&name.name).append("is blocked.").log(); } } else if rng.roll_dice(1, 6) + attributes.strength.bonus < 2 { - if let Some(name) = names.get(potential_target) { - gamelog::Logger - ::new() - .append("The") - .colour(renderable_colour_ecs(ecs, potential_target)) - .append(&name.name) - .colour(WHITE) - .append("resists!") - .log(); + if let Some(name) = names.get(*potential_target) { + gamelog::Logger::new().append("The").item_name(&name.name).append("resists!").log(); } } else { door.open = false; blocks_visibility - .insert(potential_target, BlocksVisibility {}) + .insert(*potential_target, BlocksVisibility {}) .expect("Unable to insert BlocksVisibility."); blocks_movement - .insert(potential_target, BlocksTile {}) + .insert(*potential_target, BlocksTile {}) .expect("Unable to insert BlocksTile."); - if let Some(name) = names.get(potential_target) { - gamelog::Logger - ::new() - .append("You close the") - .colour(renderable_colour_ecs(ecs, potential_target)) - .append_n(&name.name) - .colour(WHITE) - .period() - .log(); + let render_data = renderables.get_mut(*potential_target).unwrap(); + if let Some(name) = names.get(*potential_target) { + gamelog::Logger::new().append("You close the").item_name_n(&name.name).period().log(); } - //Re-get renderables as mutable - std::mem::drop(renderables); - let mut renderables = ecs.write_storage::(); - let render_data = renderables.get_mut(potential_target).unwrap(); - render_data.glyph = to_cp437('+'); // Nethack open door, maybe just use '/' instead. + render_data.glyph = rltk::to_cp437('+'); // Nethack open door, maybe just use '/' instead. door_pos = Some(Point::new(pos.x + delta_x, pos.y + delta_y)); } result = RunState::Ticking; @@ -141,7 +71,7 @@ pub fn try_door(i: i32, j: i32, ecs: &mut World) -> RunState { gamelog::Logger::new().append("It's already closed.").log(); } } - }); + } } } @@ -169,68 +99,44 @@ pub fn open(i: i32, j: i32, ecs: &mut World) -> RunState { let mut doors = ecs.write_storage::(); let mut blocks_visibility = ecs.write_storage::(); let mut blocks_movement = ecs.write_storage::(); + let mut renderables = ecs.write_storage::(); let names = ecs.read_storage::(); let mut rng = ecs.write_resource::(); let mut result = RunState::AwaitingInput; let mut door_pos: Option = None; - for (_entity, _player, pos, attributes) in ( - &entities, - &mut players, - &mut positions, - &attributes, - ).join() { + for (_entity, _player, pos, attributes) in (&entities, &mut players, &mut positions, &attributes).join() { let delta_x = i; let delta_y = j; - if - !( - pos.x + delta_x < 0 || - pos.x + delta_x > map.width - 1 || - pos.y + delta_y < 0 || - pos.y + delta_y > map.height - 1 - ) + if !(pos.x + delta_x < 0 + || pos.x + delta_x > map.width - 1 + || pos.y + delta_y < 0 + || pos.y + delta_y > map.height - 1) { let destination_idx = map.xy_idx(pos.x + delta_x, pos.y + delta_y); - if !crate::spatial::has_tile_content(destination_idx) { + if map.tile_content[destination_idx].len() == 0 { gamelog::Logger::new().append("You see no door there.").log(); } - crate::spatial::for_each_tile_content(destination_idx, |potential_target| { - let door = doors.get_mut(potential_target); + for potential_target in map.tile_content[destination_idx].iter() { + let door = doors.get_mut(*potential_target); if let Some(door) = door { if door.open == false { - let renderables = ecs.read_storage::(); if rng.roll_dice(1, 6) + attributes.strength.bonus < 2 { - if let Some(name) = names.get(potential_target) { - gamelog::Logger - ::new() - .append("The") - .colour(renderable_colour_ecs(ecs, potential_target)) - .append(&name.name) - .colour(WHITE) - .append("resists!") - .log(); + if let Some(name) = names.get(*potential_target) { + gamelog::Logger::new().append("The").item_name(&name.name).append("resists!").log(); } } else { door.open = true; - blocks_visibility.remove(potential_target); - blocks_movement.remove(potential_target); - if let Some(name) = names.get(potential_target) { - gamelog::Logger - ::new() - .append("You open the") - .colour(renderable_colour_ecs(ecs, potential_target)) - .append_n(&name.name) - .colour(WHITE) - .period() - .log(); + blocks_visibility.remove(*potential_target); + blocks_movement.remove(*potential_target); + let render_data = renderables.get_mut(*potential_target).unwrap(); + if let Some(name) = names.get(*potential_target) { + gamelog::Logger::new().append("You open the").item_name_n(&name.name).period().log(); } - std::mem::drop(renderables); - let mut renderables = ecs.write_storage::(); - let render_data = renderables.get_mut(potential_target).unwrap(); - render_data.glyph = to_cp437('▓'); // Nethack open door, maybe just use '/' instead. + render_data.glyph = rltk::to_cp437('▓'); // Nethack open door, maybe just use '/' instead. door_pos = Some(Point::new(pos.x + delta_x, pos.y + delta_y)); } result = RunState::Ticking; @@ -238,7 +144,7 @@ pub fn open(i: i32, j: i32, ecs: &mut World) -> RunState { gamelog::Logger::new().append("It's already open.").log(); } } - }); + } } } @@ -264,41 +170,28 @@ pub fn kick(i: i32, j: i32, ecs: &mut World) -> RunState { let mut viewsheds = ecs.write_storage::(); let attributes = ecs.read_storage::(); let map = ecs.fetch::(); + let entities = ecs.entities(); let mut doors = ecs.write_storage::(); let names = ecs.read_storage::(); let mut rng = ecs.write_resource::(); - for (entity, _player, pos, attributes) in ( - &entities, - &mut players, - &mut positions, - &attributes, - ).join() { + for (entity, _player, pos, attributes) in (&entities, &mut players, &mut positions, &attributes).join() { let delta_x = i; let delta_y = j; - if - !( - pos.x + delta_x < 0 || - pos.x + delta_x > map.width - 1 || - pos.y + delta_y < 0 || - pos.y + delta_y > map.height - 1 - ) + if !(pos.x + delta_x < 0 + || pos.x + delta_x > map.width - 1 + || pos.y + delta_y < 0 + || pos.y + delta_y > map.height - 1) { let destination_idx = map.xy_idx(pos.x + delta_x, pos.y + delta_y); - if !crate::spatial::has_tile_content(destination_idx) { + if map.tile_content[destination_idx].len() == 0 { if rng.roll_dice(1, 20) == 20 { - add_effect( - None, - EffectType::Damage { amount: 1, damage_type: DamageType::Physical }, - Targets::Entity { target: entity } - ); - gamelog::Logger - ::new() - .append("Ouch! You kick the open air, and pull something.") - .log(); + let mut suffer_damage = ecs.write_storage::(); + SufferDamage::new_damage(&mut suffer_damage, entity, 1, false); + gamelog::Logger::new().append("Ouch! You kick the open air, and pull something.").log(); break; } else { // If there's nothing at all, just kick the air and waste a turn. @@ -308,96 +201,52 @@ pub fn kick(i: i32, j: i32, ecs: &mut World) -> RunState { } else { let mut last_non_door_target: Option = None; let mut target_name = "thing"; - let mut colour = WHITE; - crate::spatial::for_each_tile_content_with_bool( - destination_idx, - |potential_target| { - if let Some(name) = names.get(potential_target) { - target_name = &name.name; - } - let items = ecs.read_storage::(); - colour = if let Some(_) = items.get(potential_target) { - item_colour_ecs(ecs, potential_target) - } else { - renderable_colour_ecs(ecs, potential_target) - }; - - // If it's a door, - let door = doors.get_mut(potential_target); - if let Some(door) = door { - // If the door is closed, - if door.open == false { - add_effect( - None, - EffectType::Particle { - glyph: to_cp437('‼'), - fg: RGB::named(CHOCOLATE), - bg: RGB::named(BLACK), - lifespan: 150.0, - delay: 0.0, - }, - Targets::Entity { target: potential_target } - ); - // ~33% chance of breaking it down + str - if rng.roll_dice(1, 10) + attributes.strength.bonus > 6 { - gamelog::Logger - ::new() - .append("As you kick the") - .colour(colour) - .append_n(obfuscate_name_ecs(ecs, potential_target).0) - .colour(WHITE) - .append(", it crashes open!") - .log(); - something_was_destroyed = Some(potential_target); - destroyed_pos = Some( - Point::new(pos.x + delta_x, pos.y + delta_y) - ); - gamelog::record_event(EVENT::BrokeDoor(1)); - return false; - // 66% chance of just kicking it. - } else { - gamelog::Logger - ::new() - .append("You kick the") - .colour(colour) - .append_n(obfuscate_name_ecs(ecs, potential_target).0) - .colour(WHITE) - .period() - .log(); - return false; - } - // If the door is open and there's nothing else on the tile, - } else if crate::spatial::length(destination_idx) == 1 { - // Just kick the air. - gamelog::Logger::new().append("You kick the open air.").log(); - return false; - } - } else { - last_non_door_target = Some(potential_target); - } - return true; + for potential_target in map.tile_content[destination_idx].iter() { + if let Some(name) = names.get(*potential_target) { + target_name = &name.name; } - ); - if let Some(e) = last_non_door_target { - gamelog::Logger - ::new() - .append("You kick the") - .colour(colour) - .append_n(obfuscate_name_ecs(ecs, e).0) - .colour(WHITE) - .period() - .log(); - add_effect( - None, - EffectType::Particle { - glyph: to_cp437('‼'), - fg: RGB::named(CHOCOLATE), - bg: RGB::named(BLACK), - lifespan: 150.0, - delay: 0.0, - }, - Targets::Entity { target: e } - ); + + // If it's a door, + let door = doors.get_mut(*potential_target); + if let Some(door) = door { + // If the door is closed, + if door.open == false { + let mut particle_builder = ecs.write_resource::(); + particle_builder.kick(pos.x + delta_x, pos.y + delta_y); + // ~33% chance of breaking it down + str + if rng.roll_dice(1, 10) + attributes.strength.bonus > 6 { + gamelog::Logger::new() + .append("As you kick the") + .item_name_n(target_name) + .append(", it crashes open!") + .log(); + something_was_destroyed = Some(*potential_target); + destroyed_pos = Some(Point::new(pos.x + delta_x, pos.y + delta_y)); + gamelog::record_event("broken_doors", 1); + break; + // 66% chance of just kicking it. + } else { + gamelog::Logger::new() + .append("You kick the") + .item_name_n(target_name) + .period() + .log(); + break; + } + // If the door is open and there's nothing else on the tile, + } else if map.tile_content[destination_idx].len() == 1 { + // Just kick the air. + gamelog::Logger::new().append("You kick the open air.").log(); + break; + } + } else { + last_non_door_target = Some(*potential_target); + } + } + if let Some(_) = last_non_door_target { + gamelog::Logger::new().append("You kick the").item_name_n(target_name).period().log(); + let mut particle_builder = ecs.write_resource::(); + particle_builder.kick(pos.x + delta_x, pos.y + delta_y); // Do something here if it's anything other than a door. break; } @@ -416,205 +265,132 @@ pub fn kick(i: i32, j: i32, ecs: &mut World) -> RunState { ecs.delete_entity(destroyed_thing).expect("Unable to delete."); } - gamelog::record_event(EVENT::KickedSomething(1)); + gamelog::record_event("kick_count", 1); return RunState::Ticking; } -pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> RunState { +pub fn try_move_player(delta_x: i32, delta_y: i32, ecs: &mut World) -> bool { let mut positions = ecs.write_storage::(); let mut players = ecs.write_storage::(); let mut viewsheds = ecs.write_storage::(); let mut telepaths = ecs.write_storage::(); let mut entity_moved = ecs.write_storage::(); - let factions = ecs.read_storage::(); - let ancestries = ecs.read_storage::(); + let friendlies = ecs.read_storage::(); let pools = ecs.read_storage::(); let map = ecs.fetch::(); + let entities = ecs.entities(); let mut wants_to_melee = ecs.write_storage::(); let mut doors = ecs.write_storage::(); let names = ecs.read_storage::(); let mut swap_entities: Vec<(Entity, i32, i32)> = Vec::new(); - let mut result: Option; - for (entity, _player, pos, viewshed) in ( - &entities, - &mut players, - &mut positions, - &mut viewsheds, - ).join() { - if - pos.x + delta_x < 0 || - pos.x + delta_x > map.width - 1 || - pos.y + delta_y < 0 || - pos.y + delta_y > map.height - 1 + for (entity, _player, pos, viewshed) in (&entities, &mut players, &mut positions, &mut viewsheds).join() { + if pos.x + delta_x < 0 + || pos.x + delta_x > map.width - 1 + || pos.y + delta_y < 0 + || pos.y + delta_y > map.height - 1 { - return RunState::AwaitingInput; + return false; } let destination_idx = map.xy_idx(pos.x + delta_x, pos.y + delta_y); - result = crate::spatial::for_each_tile_content_with_runstate( - destination_idx, - |potential_target| { - let mut hostile = true; - if pools.get(potential_target).is_some() { - // We get the reaction of the target to this entity -- - // i.e. in reverse to usual. We want to know if the target - // is hostile to us. If it isn't, we can swap places. - let result = crate::raws::get_reactions( - potential_target, - entity, - &factions, - &ancestries, - &crate::raws::RAWS.lock().unwrap() - ); - if result != Reaction::Attack { - hostile = false; - } + for potential_target in map.tile_content[destination_idx].iter() { + let friendly = friendlies.get(*potential_target); + if friendly.is_some() { + swap_entities.push((*potential_target, pos.x, pos.y)); + pos.x = min(map.width - 1, max(0, pos.x + delta_x)); + pos.y = min(map.height - 1, max(0, pos.y + delta_y)); + entity_moved.insert(entity, EntityMoved {}).expect("Unable to insert marker"); + viewshed.dirty = true; + let mut ppos = ecs.write_resource::(); + ppos.x = pos.x; + ppos.y = pos.y; + } else { + let target = pools.get(*potential_target); + if let Some(_target) = target { + wants_to_melee + .insert(entity, WantsToMelee { target: *potential_target }) + .expect("Add target failed."); + return true; + } + } + let door = doors.get_mut(*potential_target); + if let Some(door) = door { + if door.open == false { + if let Some(name) = names.get(*potential_target) { + gamelog::Logger::new().append("The").item_name(&name.name).append("is in your way.").log(); + } + return false; } - if !hostile { - swap_entities.push((potential_target, pos.x, pos.y)); - pos.x = min(map.width - 1, max(0, pos.x + delta_x)); - pos.y = min(map.height - 1, max(0, pos.y + delta_y)); - entity_moved.insert(entity, EntityMoved {}).expect("Unable to insert marker"); - viewshed.dirty = true; - let mut ppos = ecs.write_resource::(); - ppos.x = pos.x; - ppos.y = pos.y; - } else { - let target = pools.get(potential_target); - if let Some(_target) = target { - wants_to_melee - .insert(entity, WantsToMelee { target: potential_target }) - .expect("Add target failed."); - return Some(RunState::Ticking); - } - } - let door = doors.get_mut(potential_target); - if let Some(door) = door { - if door.open == false { - if let Some(name) = names.get(potential_target) { - let colour = if - let Some(_) = ecs.read_storage::().get(potential_target) - { - item_colour_ecs(ecs, potential_target) - } else { - renderable_colour_ecs(ecs, potential_target) - }; - gamelog::Logger - ::new() - .append("The") - .colour(colour) - .append(&name.name) - .colour(WHITE) - .append("is in your way.") - .log(); - } - return Some(RunState::AwaitingInput); - } - } - return None; } - ); - - if result.is_some() { - return result.unwrap(); } - if swap_entities.len() <= 0 { - if crate::spatial::is_blocked(destination_idx) { - gamelog::Logger::new().append("You can't move there.").log(); - return RunState::AwaitingInput; - } - let hidden = ecs.read_storage::(); - // Push every entity name in the pile to a vector of strings - let mut seen_items: Vec<(String, (u8, u8, u8))> = Vec::new(); - let mut some = false; - crate::spatial::for_each_tile_content(destination_idx, |entity| { - if !hidden.get(entity).is_some() && names.get(entity).is_some() { - let item_name = obfuscate_name_ecs(ecs, entity).0; - let item_colour = item_colour_ecs(ecs, entity); - seen_items.push((item_name, item_colour)); - some = true; + if swap_entities.len() > 0 { + for m in swap_entities.iter() { + let their_pos = positions.get_mut(m.0); + if let Some(name) = names.get(m.0) { + gamelog::Logger::new().append("You swap places with the").npc_name_n(&name.name).period().log(); } - }); - match map.tiles[destination_idx] { - TileType::ToLocal(id) => { - let name = get_local_desc(id); - let colour = rgb_to_u8(get_local_col(id)); - gamelog::Logger - ::new() - .append("You see") - .colour(colour) - .append_n(&name) - .colour(WHITE) - .period() - .log(); + if let Some(their_pos) = their_pos { + their_pos.x = m.1; + their_pos.y = m.2; } - _ => {} } - // If some names were found, append. Logger = logger is necessary - // makes logger called a mutable self. It's not the most efficient - // but it happens infrequently enough (once per player turn at most) - // that it shouldn't matter. - if some { - let mut logger = gamelog::Logger::new().append("You see"); - for i in 0..seen_items.len() { - if i > 0 && i < seen_items.len() { - logger = logger.append(", a"); - } - logger = logger - .colour(seen_items[i].1) - .append_n(&seen_items[i].0) - .colour(WHITE); - } - logger.period().log(); - } - let old_idx = map.xy_idx(pos.x, pos.y); - pos.x = min(map.width - 1, max(0, pos.x + delta_x)); - pos.y = min(map.height - 1, max(0, pos.y + delta_y)); - let new_idx = map.xy_idx(pos.x, pos.y); - entity_moved.insert(entity, EntityMoved {}).expect("Unable to insert marker"); - crate::spatial::move_entity(entity, old_idx, new_idx); - // Dirty viewsheds, and check only now if telepath viewshed exists - viewshed.dirty = true; - if let Some(telepathy) = telepaths.get_mut(entity) { - telepathy.dirty = true; - } - let mut ppos = ecs.write_resource::(); - ppos.x = pos.x; - ppos.y = pos.y; - if map.tiles[new_idx] == TileType::ToOvermap(map.id) { - return RunState::GoToLevel(ID_OVERMAP, TileType::ToLocal(map.id)); - } - return RunState::Ticking; + + return true; } + if map.blocked[destination_idx] { + gamelog::Logger::new().append("You can't move there.").log(); + return false; + } + let hidden = ecs.read_storage::(); + // Push every entity name in the pile to a vector of strings + let mut item_names: Vec = Vec::new(); + let mut some = false; + for entity in map.tile_content[destination_idx].iter() { + if !hidden.get(*entity).is_some() && names.get(*entity).is_some() { + let item_name = get_item_display_name(ecs, *entity).0; + item_names.push(item_name); + some = true; + } + } + // If some names were found, append. Logger = logger is necessary + // makes logger called a mutable self. It's not the most efficient + // but it happens infrequently enough (once per player turn at most) + // that it shouldn't matter. + if some { + let mut logger = gamelog::Logger::new().append("You see a"); + for i in 0..item_names.len() { + if i > 0 && i < item_names.len() { + logger = logger.append(", a"); + } + logger = logger.item_name_n(&item_names[i]); + } + logger.period().log(); + } + pos.x = min(map.width - 1, max(0, pos.x + delta_x)); + pos.y = min(map.height - 1, max(0, pos.y + delta_y)); + + // Dirty viewsheds, and check only now if telepath viewshed exists + viewshed.dirty = true; + + let is_telepath = telepaths.get_mut(entity); + if let Some(telepathy) = is_telepath { + telepathy.dirty = true; + } + let mut ppos = ecs.write_resource::(); + ppos.x = pos.x; + ppos.y = pos.y; + entity_moved.insert(entity, EntityMoved {}).expect("Unable to insert marker"); + + return true; } - for m in swap_entities.iter() { - if let Some(name) = names.get(m.0) { - gamelog::Logger - ::new() - .append("You swap places with the") - .colour(renderable_colour_ecs(ecs, m.0)) - .append_n(&name.name) - .colour(WHITE) - .period() - .log(); - } - if let Some(their_pos) = positions.get_mut(m.0) { - let old_idx = map.xy_idx(their_pos.x, their_pos.y); - their_pos.x = m.1; - their_pos.y = m.2; - let new_idx = map.xy_idx(their_pos.x, their_pos.y); - crate::spatial::move_entity(m.0, old_idx, new_idx); - return RunState::Ticking; - } - } - return RunState::AwaitingInput; + return false; } -fn get_item(ecs: &mut World) -> RunState { +fn get_item(ecs: &mut World) -> bool { let player_pos = ecs.fetch::(); let player_entity = ecs.fetch::(); let entities = ecs.entities(); @@ -631,189 +407,142 @@ fn get_item(ecs: &mut World) -> RunState { match target_item { None => { gamelog::Logger::new().append("There is nothing to pick up.").log(); - return RunState::AwaitingInput; + return false; } Some(item) => { - let mut assignkey = ecs.write_storage::(); let mut pickup = ecs.write_storage::(); - assignkey.insert(item, WantsToAssignKey {}).expect("Unable to insert WantsToAssignKey"); pickup .insert(*player_entity, WantsToPickupItem { collected_by: *player_entity, item }) .expect("Unable to insert want to pickup item."); - return RunState::Ticking; + return true; } } } -pub fn player_input(gs: &mut State, ctx: &mut BTerm, on_overmap: bool) -> RunState { +pub fn player_input(gs: &mut State, ctx: &mut Rltk) -> RunState { + // Player movement + let mut result = false; match ctx.key { - None => { - return RunState::AwaitingInput; - } - Some(key) => - match key { - // Cardinals - VirtualKeyCode::Left | VirtualKeyCode::Numpad4 | VirtualKeyCode::H => { - return try_move_player(-1, 0, &mut gs.ecs); - } - VirtualKeyCode::Right | VirtualKeyCode::Numpad6 | VirtualKeyCode::L => { - return try_move_player(1, 0, &mut gs.ecs); - } - VirtualKeyCode::Up | VirtualKeyCode::Numpad8 | VirtualKeyCode::K => { - return try_move_player(0, -1, &mut gs.ecs); - } - VirtualKeyCode::Down | VirtualKeyCode::Numpad2 | VirtualKeyCode::J => { - return try_move_player(0, 1, &mut gs.ecs); - } - // Diagonals - VirtualKeyCode::Numpad9 | VirtualKeyCode::U => { - return try_move_player(1, -1, &mut gs.ecs); - } - VirtualKeyCode::Numpad7 | VirtualKeyCode::Y => { - return try_move_player(-1, -1, &mut gs.ecs); - } - VirtualKeyCode::Numpad3 | VirtualKeyCode::N => { - return try_move_player(1, 1, &mut gs.ecs); - } - VirtualKeyCode::Numpad1 | VirtualKeyCode::B => { - return try_move_player(-1, 1, &mut gs.ecs); - } - // id - VirtualKeyCode::Period => { - if ctx.shift { - let dest = try_change_level(&mut gs.ecs, false); - let curr_map_id = gs.ecs.fetch::().id; - return match dest { - // If we have no destination, do nothing. - Destination::None => RunState::AwaitingInput, - // If we want to go to the next level, go to the up-stair tile of id + 1. - Destination::NextLevel => - RunState::GoToLevel(curr_map_id + 1, TileType::UpStair), - // If we want to go to the previous level, go to the down-stair tile of id - 1. - Destination::PreviousLevel => - RunState::GoToLevel(curr_map_id - 1, TileType::DownStair), - Destination::ToLocal(id) => - RunState::GoToLevel(ID_OVERMAP, TileType::ToLocal(id)), - Destination::ToOvermap(id) => - RunState::GoToLevel(id, TileType::ToOvermap(id)), - }; - } else { - return skip_turn(&mut gs.ecs); // (Wait a turn) + None => return RunState::AwaitingInput, + Some(key) => match key { + // Cardinals + VirtualKeyCode::Left | VirtualKeyCode::Numpad4 | VirtualKeyCode::H => { + result = try_move_player(-1, 0, &mut gs.ecs); + } + VirtualKeyCode::Right | VirtualKeyCode::Numpad6 | VirtualKeyCode::L => { + result = try_move_player(1, 0, &mut gs.ecs); + } + VirtualKeyCode::Up | VirtualKeyCode::Numpad8 | VirtualKeyCode::K => { + result = try_move_player(0, -1, &mut gs.ecs); + } + VirtualKeyCode::Down | VirtualKeyCode::Numpad2 | VirtualKeyCode::J => { + result = try_move_player(0, 1, &mut gs.ecs); + } + // Diagonals + VirtualKeyCode::Numpad9 | VirtualKeyCode::U => result = try_move_player(1, -1, &mut gs.ecs), + VirtualKeyCode::Numpad7 | VirtualKeyCode::Y => result = try_move_player(-1, -1, &mut gs.ecs), + VirtualKeyCode::Numpad3 | VirtualKeyCode::N => result = try_move_player(1, 1, &mut gs.ecs), + VirtualKeyCode::Numpad1 | VirtualKeyCode::B => result = try_move_player(-1, 1, &mut gs.ecs), + // id + VirtualKeyCode::Period => { + if ctx.shift { + if !try_next_level(&mut gs.ecs) { + return RunState::AwaitingInput; } - } - VirtualKeyCode::Comma => { - if ctx.shift { - let dest = try_change_level(&mut gs.ecs, true); - let curr_map_id = gs.ecs.fetch::().id; - return match dest { - Destination::None => RunState::AwaitingInput, - Destination::NextLevel => - RunState::GoToLevel(curr_map_id + 1, TileType::UpStair), - Destination::PreviousLevel => - RunState::GoToLevel(curr_map_id - 1, TileType::DownStair), - Destination::ToLocal(id) => - RunState::GoToLevel(ID_OVERMAP, TileType::ToLocal(id)), - Destination::ToOvermap(id) => - RunState::GoToLevel(id, TileType::ToOvermap(id)), - }; - } - } - VirtualKeyCode::Slash => { - if ctx.shift { - return RunState::HelpScreen; - } - } - VirtualKeyCode::NumpadDecimal => { - return skip_turn(&mut gs.ecs); - } - - // Items - VirtualKeyCode::C => { - if !on_overmap { - return RunState::ActionWithDirection { function: try_door }; - } - } - VirtualKeyCode::O => { - if !on_overmap { - return RunState::ActionWithDirection { function: open }; - } - } - VirtualKeyCode::F => { - if !on_overmap { - return RunState::ActionWithDirection { function: kick }; - } - } - VirtualKeyCode::G => { - return get_item(&mut gs.ecs); - } - VirtualKeyCode::I => { - return RunState::ShowInventory; - } - VirtualKeyCode::D => { - return RunState::ShowDropItem; - } - VirtualKeyCode::R => { - return RunState::ShowRemoveItem; - } - // Other - VirtualKeyCode::Minus => { - return RunState::ShowCheatMenu; - } - VirtualKeyCode::Escape => { - return RunState::SaveGame; - } - VirtualKeyCode::X => { - let (min_x, _max_x, min_y, _max_y, x_offset, y_offset) = get_screen_bounds( - &gs.ecs, - ctx - ); - let ppos = gs.ecs.fetch::(); - let (x, y) = (ppos.x + x_offset - min_x, ppos.y + y_offset - min_y); - return RunState::Farlook { x, y }; - } - _ => { - return RunState::AwaitingInput; + return RunState::NextLevel; // > to descend + } else { + result = skip_turn(&mut gs.ecs); // (Wait a turn) } } + VirtualKeyCode::Comma => { + if ctx.shift { + if !try_previous_level(&mut gs.ecs) { + return RunState::AwaitingInput; + } + return RunState::PreviousLevel; // < to ascend + } + } + VirtualKeyCode::Slash => { + if ctx.shift { + return RunState::HelpScreen; + } + } + VirtualKeyCode::NumpadDecimal => { + result = skip_turn(&mut gs.ecs); + } + + // Items + VirtualKeyCode::C => return RunState::ActionWithDirection { function: try_door }, + VirtualKeyCode::O => return RunState::ActionWithDirection { function: open }, + VirtualKeyCode::F => return RunState::ActionWithDirection { function: kick }, + VirtualKeyCode::G => { + result = get_item(&mut gs.ecs); + } + VirtualKeyCode::I => return RunState::ShowInventory, + VirtualKeyCode::D => return RunState::ShowDropItem, + VirtualKeyCode::R => return RunState::ShowRemoveItem, + // Other + VirtualKeyCode::Minus => return RunState::ShowCheatMenu, + VirtualKeyCode::Escape => return RunState::SaveGame, + _ => { + return RunState::AwaitingInput; + } + }, + } + if result { + return RunState::Ticking; + } else { + return RunState::AwaitingInput; } - return RunState::AwaitingInput; } -fn try_change_level(ecs: &mut World, backtracking: bool) -> Destination { +pub fn try_next_level(ecs: &mut World) -> bool { let player_pos = ecs.fetch::(); let map = ecs.fetch::(); let player_idx = map.xy_idx(player_pos.x, player_pos.y); - let this_tile = map.tiles[player_idx]; - return get_dest(this_tile, backtracking); + if map.tiles[player_idx] == TileType::DownStair { + return true; + } else { + gamelog::Logger::new().append("You don't see a way down from here.").log(); + return false; + } } -fn skip_turn(ecs: &mut World) -> RunState { +pub fn try_previous_level(ecs: &mut World) -> bool { + let player_pos = ecs.fetch::(); + let map = ecs.fetch::(); + let player_idx = map.xy_idx(player_pos.x, player_pos.y); + if map.tiles[player_idx] == TileType::UpStair { + return true; + } else { + gamelog::Logger::new().append("You don't see a way up from here.").log(); + return false; + } +} + +fn skip_turn(ecs: &mut World) -> bool { let player_entity = ecs.fetch::(); let mut viewsheds = ecs.write_storage::(); + let monsters = ecs.read_storage::(); let worldmap_resource = ecs.fetch::(); let hunger_clocks = ecs.read_storage::(); // Default to being able to heal by waiting. let mut can_heal = true; - let factions = ecs.read_storage::(); - let ancestries = ecs.read_storage::(); // Check viewshed for monsters nearby. If we can see a monster, we can't heal. let viewshed = viewsheds.get_mut(*player_entity).unwrap(); for tile in viewshed.visible_tiles.iter() { let idx = worldmap_resource.xy_idx(tile.x, tile.y); - crate::spatial::for_each_tile_content(idx, |entity_id| { - let result = crate::raws::get_reactions( - *player_entity, - entity_id, - &factions, - &ancestries, - &crate::raws::RAWS.lock().unwrap() - ); - if result == Reaction::Attack { - can_heal = false; + for entity_id in worldmap_resource.tile_content[idx].iter() { + let mob = monsters.get(*entity_id); + match mob { + None => {} + Some(_) => { + can_heal = false; + } } - }); + } } // Dirty viewshed (so we search for hidden tiles whenever we wait) viewshed.dirty = true; @@ -822,15 +551,9 @@ fn skip_turn(ecs: &mut World) -> RunState { let player_hunger_clock = hunger_clocks.get(*player_entity); if let Some(clock) = player_hunger_clock { match clock.state { - HungerState::Hungry => { - can_heal = false; - } - HungerState::Weak => { - can_heal = false; - } - HungerState::Fainting => { - can_heal = false; - } + HungerState::Hungry => can_heal = false, + HungerState::Weak => can_heal = false, + HungerState::Fainting => can_heal = false, _ => {} } } @@ -840,14 +563,14 @@ fn skip_turn(ecs: &mut World) -> RunState { let pools = health_components.get_mut(*player_entity).unwrap(); let mut rng = ecs.write_resource::(); let roll = rng.roll_dice(1, 6); - if roll == 6 && pools.hit_points.current < pools.hit_points.max { + if (roll == 6) && pools.hit_points.current < pools.hit_points.max { pools.hit_points.current += 1; } } gamelog::Logger::new().append("You wait a turn.").log(); - return RunState::Ticking; + return true; } /* Playing around with autoexplore, without having read how to do it. @@ -880,7 +603,7 @@ pub fn auto_explore(ecs: &mut World) { } } - let path = a_star_search(map.xy_idx(player_pos.x, player_pos.y), unexplored_tile.0, &*map); + let path = rltk::a_star_search(map.xy_idx(player_pos.x, player_pos.y), unexplored_tile.0, &*map); if path.success && path.steps.len() > 1 { let mut idx = map.xy_idx(player_pos.x, player_pos.y); map.blocked[idx] = false; diff --git a/src/random_table.rs b/src/random_table.rs index d4720f0..4baa921 100644 --- a/src/random_table.rs +++ b/src/random_table.rs @@ -1,4 +1,4 @@ -use bracket_lib::random::RandomNumberGenerator; +use rltk::RandomNumberGenerator; // FIXME: note to self, // passing around strings here is super inefficient, so this is diff --git a/src/raws/item_structs.rs b/src/raws/item_structs.rs index c670e54..f93fd89 100644 --- a/src/raws/item_structs.rs +++ b/src/raws/item_structs.rs @@ -6,10 +6,8 @@ pub struct Item { pub id: String, pub name: Name, pub renderable: Option, - pub class: String, pub weight: Option, pub value: Option, - pub equip: Option, pub flags: Option>, pub effects: Option>, pub magic: Option, @@ -21,13 +19,6 @@ pub struct Name { pub plural: String, } -#[derive(Deserialize, Debug)] -pub struct Equippable { - pub flag: String, - pub damage: String, - pub to_hit: Option, -} - #[derive(Deserialize, Debug)] pub struct Renderable { pub glyph: String, diff --git a/src/raws/mob_structs.rs b/src/raws/mob_structs.rs index 854149b..3f0f03e 100644 --- a/src/raws/mob_structs.rs +++ b/src/raws/mob_structs.rs @@ -14,8 +14,7 @@ pub struct Mob { pub attacks: Option>, pub attributes: Option, pub skills: Option>, - pub vision_range: Option, - pub telepathy_range: Option, + pub vision_range: i32, pub equipped: Option>, pub loot: Option, pub quips: Option>, diff --git a/src/raws/mod.rs b/src/raws/mod.rs index 7fc28ed..3b0dfee 100644 --- a/src/raws/mod.rs +++ b/src/raws/mod.rs @@ -11,11 +11,7 @@ mod spawn_table_structs; use spawn_table_structs::*; mod loot_table_structs; use loot_table_structs::*; -mod reaction_structs; -pub use reaction_structs::Reaction; -use reaction_structs::{ AncestryData, FactionData }; use std::sync::Mutex; -use bracket_lib::prelude::*; lazy_static! { pub static ref RAWS: Mutex = Mutex::new(RawMaster::empty()); @@ -28,26 +24,20 @@ pub struct Raws { pub props: Vec, pub spawn_tables: Vec, pub loot_tables: Vec, - pub factions: Vec, - pub ancestries: Vec, } -embedded_resource!(RAW_ITEMS, "../../raws/items.json"); -embedded_resource!(RAW_MOBS, "../../raws/mobs.json"); -embedded_resource!(RAW_PROPS, "../../raws/props.json"); -embedded_resource!(RAW_SPAWN_TABLES, "../../raws/spawn_tables.json"); -embedded_resource!(RAW_LOOT_TABLES, "../../raws/loot_tables.json"); -embedded_resource!(RAW_FACTIONS, "../../raws/factions.json"); -embedded_resource!(RAW_ANCESTRIES, "../../raws/ancestries.json"); +rltk::embedded_resource!(RAW_ITEMS, "../../raws/items.json"); +rltk::embedded_resource!(RAW_MOBS, "../../raws/mobs.json"); +rltk::embedded_resource!(RAW_PROPS, "../../raws/props.json"); +rltk::embedded_resource!(RAW_SPAWN_TABLES, "../../raws/spawn_tables.json"); +rltk::embedded_resource!(RAW_LOOT_TABLES, "../../raws/loot_tables.json"); pub fn load_raws() { - link_resource!(RAW_ITEMS, "../../raws/items.json"); - link_resource!(RAW_MOBS, "../../raws/mobs.json"); - link_resource!(RAW_PROPS, "../../raws/props.json"); - link_resource!(RAW_SPAWN_TABLES, "../../raws/spawn_tables.json"); - link_resource!(RAW_LOOT_TABLES, "../../raws/loot_tables.json"); - link_resource!(RAW_FACTIONS, "../../raws/factions.json"); - link_resource!(RAW_ANCESTRIES, "../../raws/ancestries.json"); + rltk::link_resource!(RAW_ITEMS, "../../raws/items.json"); + rltk::link_resource!(RAW_MOBS, "../../raws/mobs.json"); + rltk::link_resource!(RAW_PROPS, "../../raws/props.json"); + rltk::link_resource!(RAW_SPAWN_TABLES, "../../raws/spawn_tables.json"); + rltk::link_resource!(RAW_LOOT_TABLES, "../../raws/loot_tables.json"); let decoded_raws = get_decoded_raws(); RAWS.lock().unwrap().load(decoded_raws); @@ -57,20 +47,10 @@ pub fn get_decoded_raws() -> Raws { let items: Vec = ParseJson::parse_raws_into_vector("../../raws/items.json".to_string()); let mobs: Vec = ParseJson::parse_raws_into_vector("../../raws/mobs.json".to_string()); let props: Vec = ParseJson::parse_raws_into_vector("../../raws/props.json".to_string()); - let spawn_tables: Vec = ParseJson::parse_raws_into_vector( - "../../raws/spawn_tables.json".to_string() - ); - let loot_tables: Vec = ParseJson::parse_raws_into_vector( - "../../raws/loot_tables.json".to_string() - ); - let factions: Vec = ParseJson::parse_raws_into_vector( - "../../raws/factions.json".to_string() - ); - let ancestries: Vec = ParseJson::parse_raws_into_vector( - "../../raws/ancestries.json".to_string() - ); + let spawn_tables: Vec = ParseJson::parse_raws_into_vector("../../raws/spawn_tables.json".to_string()); + let loot_tables: Vec = ParseJson::parse_raws_into_vector("../../raws/loot_tables.json".to_string()); - return Raws { items, mobs, props, spawn_tables, loot_tables, factions, ancestries }; + return Raws { items, mobs, props, spawn_tables, loot_tables }; } trait ParseJson { @@ -80,11 +60,11 @@ macro_rules! impl_ParseJson { (for $($t:ty),+) => { $(impl ParseJson for $t { fn parse_raws_into_vector(path: String) -> $t { - let raw_data = EMBED.lock().get_resource(path).unwrap(); - let raw_string = std::str::from_utf8(&raw_data).expect("Failed to convert UTF-8 to &str."); - return serde_json::from_str(&raw_string).expect("Failed to convert &str to json"); + let raw_data = rltk::embedding::EMBED.lock().get_resource(path).unwrap(); + let raw_string = std::str::from_utf8(&raw_data).expect("Unable to convert to a valid UTF-8 string."); + return serde_json::from_str(&raw_string).expect("Unable to parse items.json"); } })* - }; + } } -impl_ParseJson!(for Vec, Vec, Vec, Vec, Vec, Vec, Vec); +impl_ParseJson!(for Vec, Vec, Vec, Vec, Vec); diff --git a/src/raws/rawmaster.rs b/src/raws/rawmaster.rs index 0e2b227..8002e3f 100644 --- a/src/raws/rawmaster.rs +++ b/src/raws/rawmaster.rs @@ -1,141 +1,18 @@ -use super::{ Raws, Reaction }; +use super::Raws; use crate::components::*; use crate::gamesystem::*; -use crate::gui::Ancestry; use crate::random_table::RandomTable; -use crate::config::CONFIG; -use crate::data::visuals::BLOODSTAIN_COLOUR; -use crate::data::entity::DEFAULT_VIEWSHED_STANDARD; -use bracket_lib::prelude::*; +use crate::LOG_SPAWNING; +use regex::Regex; +use rltk::prelude::*; use specs::prelude::*; -use specs::saveload::{ MarkedBuilder, SimpleMarker }; -use std::collections::{ HashMap, HashSet }; - -/// Applies effects to the entity - e.g. "healing: 1d4+1", where -/// effects are components on the entity with varying parameters. -macro_rules! apply_effects { - ($effects:expr, $eb:expr) => { - for effect in $effects.iter() { - let effect_name = effect.0.as_str(); - match effect_name { - "heal" => { - let dice = parse_dice_string(effect.1.as_str()).expect("Failed to parse dice string"); - $eb = $eb.with(ProvidesHealing { n_dice: dice.n_dice, sides: dice.die_type, modifier: dice.bonus }) - } - "ranged" => $eb = $eb.with(Ranged { range: effect.1.parse::().unwrap() }), - "damage" => { - let (damage_type, dice) = parse_damage_string(effect.1.as_str()); - $eb = $eb.with(InflictsDamage { damage_type, n_dice: dice.n_dice, sides: dice.die_type, modifier: dice.bonus }) - } - "aoe" => $eb = $eb.with(AOE { radius: effect.1.parse::().unwrap() }), - "confusion" => $eb = $eb.with(Confusion { turns: effect.1.parse::().unwrap() }), - "ac" => $eb = $eb.with(ArmourClassBonus { amount: effect.1.parse::().unwrap() }), - "to_hit" => $eb = $eb.with(ToHitBonus { amount: effect.1.parse::().unwrap() }), - "particle_line" => $eb = $eb.with(parse_particle_line(&effect.1)), - "particle_burst" => $eb = $eb.with(parse_particle_burst(&effect.1)), - "particle" => $eb = $eb.with(parse_particle(&effect.1)), - _ => console::log(format!("Warning: effect {} not implemented.", effect_name)), - } - } - }; -} - -/// Applies flags to the entity - e.g. "blocks_tile", where -/// flags are components that have no parameters to modify. -macro_rules! apply_flags { - ($flags:expr, $eb:expr) => { - let mut damage_modifiers: HashMap = HashMap::new(); - for flag in $flags.iter() { - match flag.as_str() { - // --- PROP FLAGS BEGIN HERE --- - "HIDDEN" => $eb = $eb.with(Hidden {}), - "BLOCKS_TILE" => $eb = $eb.with(BlocksTile {}), - "BLOCKS_VISIBILITY" => $eb = $eb.with(BlocksVisibility {}), - "ENTRY_TRIGGER" => $eb = $eb.with(EntryTrigger {}), - "SINGLE_ACTIVATION" => $eb = $eb.with(SingleActivation {}), - "DOOR" => { - $eb = $eb.with(Door { open: false }); - $eb = $eb.with(BlocksVisibility {}); - $eb = $eb.with(BlocksTile {}); - } - // --- EFFECT FLAGS --- - "FOOD" => $eb = $eb.with(ProvidesNutrition {}), - "CONSUMABLE" => $eb = $eb.with(Consumable {}), - "CHARGES" => $eb = $eb.with(Charges { uses: 3, max_uses: 3 }), - "REMOVE_CURSE" => $eb = $eb.with(ProvidesRemoveCurse {}), - "IDENTIFY" => $eb = $eb.with(ProvidesIdentify {}), - "DIGGER" => $eb = $eb.with(Digger {}), - "MAGICMAP" => $eb = $eb.with(MagicMapper {}), - "STACKABLE" => $eb = $eb.with(Stackable {}), - // CAN BE DESTROYED BY DAMAGE - "DESTRUCTIBLE" => $eb = $eb.with(Destructible {}), - // --- EQUIP SLOTS --- - "EQUIP_MELEE" => $eb = $eb.with(Equippable { slot: EquipmentSlot::Melee }), - "EQUIP_SHIELD" => $eb = $eb.with(Equippable { slot: EquipmentSlot::Shield }), - "EQUIP_HEAD" => $eb = $eb.with(Equippable { slot: EquipmentSlot::Head }), - "EQUIP_BODY" => $eb = $eb.with(Equippable { slot: EquipmentSlot::Body }), - "EQUIP_FEET" => $eb = $eb.with(Equippable { slot: EquipmentSlot::Feet }), - "EQUIP_HANDS" => $eb = $eb.with(Equippable { slot: EquipmentSlot::Hands }), - "EQUIP_NECK" => $eb = $eb.with(Equippable { slot: EquipmentSlot::Neck }), - "EQUIP_BACK" => $eb = $eb.with(Equippable { slot: EquipmentSlot::Back }), - // --- MOB ANCESTRIES --- - "IS_PLAYER" => $eb = $eb.with(Player {}), - "IS_HUMAN" => $eb = $eb.with(HasAncestry { name: Ancestry::Human }), - "IS_DWARF" => $eb = $eb.with(HasAncestry { name: Ancestry::Dwarf }), - "IS_ELF" => $eb = $eb.with(HasAncestry { name: Ancestry::Elf }), - "IS_CATFOLK" => $eb = $eb.with(HasAncestry { name: Ancestry::Catfolk }), - "IS_GNOME" => $eb = $eb.with(HasAncestry { name: Ancestry::Gnome }), - // --- MOB FACTIONS --- - "MINDLESS" => $eb = $eb.with(Faction { name: "mindless".to_string() }), - "NEUTRAL" => $eb = $eb.with(Faction { name: "neutral".to_string() }), - "HERBIVORE" => $eb = $eb.with(Faction { name: "herbivore".to_string() }), - "CARNIVORE" => $eb = $eb.with(Faction { name: "carnivore".to_string() }), - // --- DAMAGE MODIFIERS --- - "PHYS_IMMUNITY" => { damage_modifiers.insert(DamageType::Physical, DamageModifier::Immune); } - "PHYS_WEAK" => { damage_modifiers.insert(DamageType::Physical, DamageModifier::Weakness); } - "PHYS_RES" => { damage_modifiers.insert(DamageType::Physical, DamageModifier::Resistance); } - "MAGIC_IMMUNITY" => { damage_modifiers.insert(DamageType::Magic, DamageModifier::Immune); } - "MAGIC_WEAK" => { damage_modifiers.insert(DamageType::Magic, DamageModifier::Weakness); } - "MAGIC_RES" => { damage_modifiers.insert(DamageType::Magic, DamageModifier::Resistance); } - "FIRE_IMMUNITY" => { damage_modifiers.insert(DamageType::Fire, DamageModifier::Immune); } - "FIRE_WEAK" => { damage_modifiers.insert(DamageType::Fire, DamageModifier::Weakness); } - "FIRE_RES" => { damage_modifiers.insert(DamageType::Fire, DamageModifier::Resistance); } - "COLD_IMMUNITY" => { damage_modifiers.insert(DamageType::Cold, DamageModifier::Immune); } - "COLD_WEAK" => { damage_modifiers.insert(DamageType::Cold, DamageModifier::Weakness); } - "COLD_RES" => { damage_modifiers.insert(DamageType::Cold, DamageModifier::Resistance); } - "POISON_IMMUNITY" => { damage_modifiers.insert(DamageType::Poison, DamageModifier::Immune); } - "POISON_WEAK" => { damage_modifiers.insert(DamageType::Poison, DamageModifier::Weakness); } - "POISON_RES" => { damage_modifiers.insert(DamageType::Poison, DamageModifier::Resistance); } - // --- MOVEMENT MODES --- ( defaults to WANDER ) - "STATIC" => $eb = $eb.with(MoveMode { mode: Movement::Static }), - "RANDOM_PATH" => $eb = $eb.with(MoveMode { mode: Movement::RandomWaypoint { path: None } }), - // --- RANDOM MOB ATTRIBUTES --- - "GREEN_BLOOD" => $eb = $eb.with(Bleeds { colour: RGB::named((0, 153, 0)) }), - "BLUE_BLOOD" => $eb = $eb.with(Bleeds { colour: RGB::named((0, 0, 153)) }), - "SMALL_GROUP" => {} // These flags are for region spawning, - "LARGE_GROUP" => {} // and don't need to apply a component. - "MULTIATTACK" => $eb = $eb.with(MultiAttack {}), - "BLIND" => $eb = $eb.with(Blind {}), - _ => console::log(format!("Unrecognised flag: {}", flag.as_str())), - } - } - if damage_modifiers.len() > 0 { - $eb = $eb.with(HasDamageModifiers { modifiers: damage_modifiers }); - } - }; -} +use specs::saveload::{MarkedBuilder, SimpleMarker}; +use std::collections::{HashMap, HashSet}; pub enum SpawnType { - AtPosition { - x: i32, - y: i32, - }, - Equipped { - by: Entity, - }, - Carried { - by: Entity, - }, + AtPosition { x: i32, y: i32 }, + Equipped { by: Entity }, + Carried { by: Entity }, } pub struct RawMaster { @@ -145,8 +22,6 @@ pub struct RawMaster { prop_index: HashMap, table_index: HashMap, loot_index: HashMap, - faction_index: HashMap>, - ancestry_index: HashMap>, } impl RawMaster { @@ -158,16 +33,12 @@ impl RawMaster { props: Vec::new(), spawn_tables: Vec::new(), loot_tables: Vec::new(), - factions: Vec::new(), - ancestries: Vec::new(), }, item_index: HashMap::new(), mob_index: HashMap::new(), prop_index: HashMap::new(), table_index: HashMap::new(), loot_index: HashMap::new(), - faction_index: HashMap::new(), - ancestry_index: HashMap::new(), } } @@ -194,43 +65,29 @@ impl RawMaster { self.table_index.insert(table.id.clone(), i); used_names.insert(table.id.clone()); for entry in table.table.iter() { - check_for_unspecified_entity(&used_names, &entry.id); + check_for_unspecified_entity(&used_names, &entry.id) } } for (i, loot_table) in self.raws.loot_tables.iter().enumerate() { check_for_duplicate_entries(&used_names, &loot_table.id); self.loot_index.insert(loot_table.id.clone(), i); for entry in loot_table.table.iter() { - check_for_unspecified_entity(&used_names, &entry.id); + check_for_unspecified_entity(&used_names, &entry.id) } } - for faction in self.raws.factions.iter() { - let mut reactions: HashMap = HashMap::new(); - for other in faction.responses.iter() { - reactions.insert(other.0.clone(), match other.1.as_str() { - "flee" => Reaction::Flee, - "attack" => Reaction::Attack, - _ => Reaction::Ignore, - }); - } - self.faction_index.insert(faction.id.clone(), reactions); - } - for ancestry in self.raws.ancestries.iter() { - self.ancestry_index.insert(ancestry.id.clone(), ancestry.allies.clone()); - } } } /// Checks a string against a HashSet, logging if a duplicate is found. fn check_for_duplicate_entries(used_names: &HashSet, id: &String) { if used_names.contains(id) { - console::log(format!("DEBUGINFO: Duplicate ID found in raws [{}]", id)); + rltk::console::log(format!("DEBUGINFO: Duplicate ID found in raws [{}]", id)); } } /// Checks a string against a HashSet, logging if the string isn't found. fn check_for_unspecified_entity(used_names: &HashSet, id: &String) { if !used_names.contains(id) { - console::log(format!("DEBUGINFO: Table references unspecified entity [{}]", id)); + rltk::console::log(format!("DEBUGINFO: Table references unspecified entity [{}]", id)); } } @@ -238,12 +95,11 @@ pub fn spawn_named_entity( raws: &RawMaster, ecs: &mut World, key: &str, - buc: Option, pos: SpawnType, - map_difficulty: i32 + map_difficulty: i32, ) -> Option { if raws.item_index.contains_key(key) { - return spawn_named_item(raws, ecs, key, buc, pos); + return spawn_named_item(raws, ecs, key, pos); } else if raws.mob_index.contains_key(key) { return spawn_named_mob(raws, ecs, key, pos, map_difficulty); } else if raws.prop_index.contains_key(key) { @@ -252,88 +108,77 @@ pub fn spawn_named_entity( None } -pub fn spawn_named_item( - raws: &RawMaster, - ecs: &mut World, - key: &str, - buc: Option, - pos: SpawnType -) -> Option { +pub fn spawn_named_item(raws: &RawMaster, ecs: &mut World, key: &str, pos: SpawnType) -> Option { if raws.item_index.contains_key(key) { let item_template = &raws.raws.items[raws.item_index[key]]; - let mut dm = ecs.fetch_mut::(); + let dm = ecs.fetch::(); let scroll_names = dm.scroll_map.clone(); let potion_names = dm.potion_map.clone(); let wand_names = dm.wand_map.clone(); let identified_items = dm.identified_items.clone(); - // --- WE GET ALL THE VALUES FROM THE ECS WE NEED HERE, BEFORE --- - // --- WE CREATE THE EB, TO AVOID BORROW CHECKER COMPLAINTS --- - let roll = ecs.write_resource::().roll_dice(1, 6); - let player_entity = ecs.fetch::(); - let known_beatitude = match pos { - SpawnType::Equipped { by } => { - if by == *player_entity { true } else { false } - } - SpawnType::Carried { by } => { - if by == *player_entity { true } else { false } - } - _ => false, - }; - if known_beatitude && !identified_items.contains(&item_template.name.name) { - dm.identified_items.insert(item_template.name.name.clone()); - } - let needs_key = is_player_owned(&player_entity, &pos); - std::mem::drop(player_entity); std::mem::drop(dm); - // -- DROP EVERYTHING THAT INVOLVES THE ECS BEFORE THIS POINT --- let mut eb = ecs.create_entity().marked::>(); - eb = eb.with(Name { - name: item_template.name.name.clone(), - plural: item_template.name.plural.clone(), - }); - eb = eb.with(Item { - weight: item_template.weight.unwrap_or(0.0), - value: item_template.value.unwrap_or(0.0), - category: match item_template.class.as_str() { - "amulet" => ItemType::Amulet, - "weapon" => ItemType::Weapon, - "armour" => ItemType::Armour, - "comestible" => ItemType::Comestible, - "scroll" => ItemType::Scroll, - "spellbook" => ItemType::Spellbook, - "potion" => ItemType::Potion, - "ring" => ItemType::Ring, - "wand" => ItemType::Wand, - _ => unreachable!("Unknown item type."), - }, - }); + eb = eb.with(Name { name: item_template.name.name.clone(), plural: item_template.name.plural.clone() }); + eb = eb.with(Item { weight: item_template.weight.unwrap_or(0.0), value: item_template.value.unwrap_or(0.0) }); eb = spawn_position(pos, eb, key, raws); - if needs_key { - eb = eb.with(WantsToAssignKey {}); - } + if let Some(renderable) = &item_template.renderable { eb = eb.with(get_renderable_component(renderable)); } - // BEATITUDE - let buc = if let Some(buc_status) = buc { - buc_status - } else { - match roll { - 1 => BUC::Cursed, - 2 => BUC::Blessed, - _ => BUC::Uncursed, - } - }; - eb = eb.with(Beatitude { buc, known: known_beatitude }); + + let mut weapon_type = -1; if let Some(flags) = &item_template.flags { - apply_flags!(flags, eb); - } - if let Some(effects_list) = &item_template.effects { - apply_effects!(effects_list, eb); + for flag in flags.iter() { + match flag.as_str() { + "CONSUMABLE" => eb = eb.with(Consumable {}), + "DESTRUCTIBLE" => eb = eb.with(Destructible {}), + "CURSED" => eb = eb.with(Cursed {}), + "EQUIP_MELEE" => eb = eb.with(Equippable { slot: EquipmentSlot::Melee }), + "EQUIP_SHIELD" => eb = eb.with(Equippable { slot: EquipmentSlot::Shield }), + "EQUIP_HEAD" => eb = eb.with(Equippable { slot: EquipmentSlot::Head }), + "EQUIP_BODY" => eb = eb.with(Equippable { slot: EquipmentSlot::Body }), + "EQUIP_FEET" => eb = eb.with(Equippable { slot: EquipmentSlot::Feet }), + "EQUIP_HANDS" => eb = eb.with(Equippable { slot: EquipmentSlot::Hands }), + "EQUIP_NECK" => eb = eb.with(Equippable { slot: EquipmentSlot::Neck }), + "EQUIP_BACK" => eb = eb.with(Equippable { slot: EquipmentSlot::Back }), + "WAND" => eb = eb.with(Wand { uses: 3, max_uses: 3 }), + "FOOD" => eb = eb.with(ProvidesNutrition {}), + "STRENGTH" => weapon_type = 0, + "DEXTERITY" => weapon_type = 2, + "FINESSE" => weapon_type = 3, + _ => rltk::console::log(format!("Unrecognised flag: {}", flag.as_str())), + } + } } + let mut base_damage = "1d4"; + let mut hit_bonus = 0; + if let Some(effects_list) = &item_template.effects { + for effect in effects_list.iter() { + let effect_name = effect.0.as_str(); + match effect_name { + "provides_healing" => { + let (n_dice, sides, modifier) = parse_dice_string(effect.1.as_str()); + eb = eb.with(ProvidesHealing { n_dice, sides, modifier }) + } + "ranged" => eb = eb.with(Ranged { range: effect.1.parse::().unwrap() }), + "damage" => { + let (n_dice, sides, modifier) = parse_dice_string(effect.1.as_str()); + eb = eb.with(InflictsDamage { n_dice, sides, modifier }) + } + "aoe" => eb = eb.with(AOE { radius: effect.1.parse::().unwrap() }), + "confusion" => eb = eb.with(Confusion { turns: effect.1.parse::().unwrap() }), + "base_damage" => base_damage = effect.1, + "hit_bonus" => hit_bonus = effect.1.parse::().unwrap(), + "ac" => eb = eb.with(ArmourClassBonus { amount: effect.1.parse::().unwrap() }), + "magicmapper" => eb = eb.with(MagicMapper {}), + "digger" => eb = eb.with(Digger {}), + _ => rltk::console::log(format!("Warning: effect {} not implemented.", effect_name)), + } + } + } if let Some(magic_item) = &item_template.magic { let item_class = match magic_item.class.as_str() { "common" => MagicItemClass::Common, @@ -343,6 +188,7 @@ pub fn spawn_named_item( _ => MagicItemClass::Legendary, }; eb = eb.with(MagicItem { class: item_class }); + if !identified_items.contains(&item_template.name.name) { #[allow(clippy::single_match)] match magic_item.naming.as_str() { @@ -350,45 +196,44 @@ pub fn spawn_named_item( let singular = scroll_names[&item_template.name.name].clone(); let mut plural = singular.clone(); plural += "s"; - eb = eb.with(ObfuscatedName { name: singular, plural: plural }); + eb = eb.with(ObfuscatedName { name: singular, plural: plural }) } "potion" => { let singular = potion_names[&item_template.name.name].clone(); let mut plural = singular.clone(); plural += "s"; - eb = eb.with(ObfuscatedName { name: singular, plural: plural }); + eb = eb.with(ObfuscatedName { name: singular, plural: plural }) } "wand" => { let singular = wand_names[&item_template.name.name].clone(); let mut plural = singular.clone(); plural += "s"; - eb = eb.with(ObfuscatedName { name: singular, plural: plural }); + eb = eb.with(ObfuscatedName { name: singular, plural: plural }) } _ => { let singular = magic_item.naming.clone(); let mut plural = singular.clone(); plural += "s"; - eb = eb.with(ObfuscatedName { name: singular, plural: plural }); + eb = eb.with(ObfuscatedName { name: singular, plural: plural }) } } } } - if let Some(weapon) = &item_template.equip { - let (damage_type, dice) = parse_damage_string(weapon.damage.as_str()); - let weapon_attribute = match weapon.flag.as_str() { - "DEXTERITY" => WeaponAttribute::Dexterity, - "FINESSE" => WeaponAttribute::Finesse, - _ => WeaponAttribute::Strength, - }; - let wpn = MeleeWeapon { - damage_type, - attribute: weapon_attribute, - damage_n_dice: dice.n_dice, - damage_die_type: dice.die_type, - damage_bonus: dice.bonus, - hit_bonus: weapon.to_hit.unwrap_or(0), + if weapon_type != -1 { + let (n_dice, die_type, bonus) = parse_dice_string(base_damage); + let mut wpn = MeleeWeapon { + attribute: WeaponAttribute::Strength, + damage_n_dice: n_dice, + damage_die_type: die_type, + damage_bonus: bonus, + hit_bonus: hit_bonus, }; + match weapon_type { + 0 => wpn.attribute = WeaponAttribute::Strength, + 1 => wpn.attribute = WeaponAttribute::Dexterity, + _ => wpn.attribute = WeaponAttribute::Finesse, + } eb = eb.with(wpn); } @@ -403,12 +248,11 @@ pub fn spawn_named_mob( ecs: &mut World, key: &str, pos: SpawnType, - map_difficulty: i32 + map_difficulty: i32, ) -> Option { if raws.mob_index.contains_key(key) { let mob_template = &raws.raws.mobs[raws.mob_index[key]]; let mut player_level = 1; - let needs_key; { let pools = ecs.read_storage::(); let player_entity = ecs.fetch::(); @@ -416,50 +260,39 @@ pub fn spawn_named_mob( if let Some(pool) = player_pool { player_level = pool.level; } - needs_key = is_player_owned(&player_entity, &pos); } + let mut eb; + let mut xp_value = 1; // New entity with a position, name, combatstats, and viewshed eb = ecs.create_entity().marked::>(); eb = spawn_position(pos, eb, key, raws); - if needs_key { - eb = eb.with(WantsToAssignKey {}); - } eb = eb.with(Name { name: mob_template.name.clone(), plural: mob_template.name.clone() }); - eb = eb.with(Viewshed { - visible_tiles: Vec::new(), - range: if let Some(range) = mob_template.vision_range { - range - } else { - DEFAULT_VIEWSHED_STANDARD - }, - dirty: true, - }); - if let Some(telepath) = &mob_template.telepathy_range { - eb = eb.with(Telepath { telepath_tiles: Vec::new(), range: *telepath, dirty: true }); - } + eb = eb.with(Viewshed { visible_tiles: Vec::new(), range: mob_template.vision_range, dirty: true }); if let Some(renderable) = &mob_template.renderable { eb = eb.with(get_renderable_component(renderable)); } - eb = eb.with(BlocksTile {}); - eb = eb.with(Faction { name: "hostile".to_string() }); - eb = eb.with(MoveMode { mode: Movement::Random }); - eb = eb.with(Bleeds { colour: RGB::named(BLOODSTAIN_COLOUR) }); - let mut xp_value = 1; let mut has_mind = true; if let Some(flags) = &mob_template.flags { - apply_flags!(flags, eb); - if flags.contains(&"MULTIATTACK".to_string()) { - xp_value += 3; - } - if flags.contains(&"MINDLESS".to_string()) { - has_mind = false; + for flag in flags.iter() { + match flag.as_str() { + "BLOCKS_TILE" => eb = eb.with(BlocksTile {}), + "BYSTANDER" => eb = eb.with(Bystander {}), + "MONSTER" => eb = eb.with(Monster {}), + "MINDLESS" => has_mind = false, + "SMALL_GROUP" => {} // These flags are for region spawning, + "LARGE_GROUP" => {} // and don't matter here (yet)? + "MULTIATTACK" => { + eb = eb.with(MultiAttack {}); + xp_value += 3; + } + _ => rltk::console::log(format!("Unrecognised flag: {}", flag.as_str())), + } } } if has_mind { eb = eb.with(Mind {}); } - // Add quips, if we have some listed. if let Some(quips) = &mob_template.quips { eb = eb.with(Quips { available: quips.clone() }); } @@ -502,11 +335,7 @@ pub fn spawn_named_mob( let speed = if mob_template.speed.is_some() { mob_template.speed.unwrap() } else { 12 }; eb = eb.with(Energy { current: 0, speed: speed }); - let base_mob_level = if mob_template.level.is_some() { - mob_template.level.unwrap() - } else { - 0 - }; + let base_mob_level = if mob_template.level.is_some() { mob_template.level.unwrap() } else { 0 }; let mut mob_level = base_mob_level; // If the level difficulty is smaller than the mob's base level, subtract 1; // else, if the level difficulty is larger, add one-fifth of the difference @@ -520,11 +349,11 @@ pub fn spawn_named_mob( mob_level += (player_level - base_mob_level) / 4; } // If the resulting mob level is more than 1.5x the base, lower it to that number - mob_level = i32::min(mob_level, (1.5 * (base_mob_level as f32)).trunc() as i32); + mob_level = i32::min(mob_level, (1.5 * base_mob_level as f32).trunc() as i32); // Should really use existing RNG here - let mut rng = RandomNumberGenerator::new(); - let mob_hp = npc_hp_at_level(&mut rng, mob_con, mob_level); + let mut rng = rltk::RandomNumberGenerator::new(); + let mob_hp = npc_hp(&mut rng, mob_con, mob_level); let mob_mana = mana_at_level(&mut rng, mob_int, mob_level); let mob_bac = if mob_template.bac.is_some() { mob_template.bac.unwrap() } else { 10 }; @@ -556,7 +385,7 @@ pub fn spawn_named_mob( skills.skills.insert(Skill::Magic, *sk.1); } _ => { - console::log(format!("Unknown skill referenced: [{}]", sk.0)); + rltk::console::log(format!("Unknown skill referenced: [{}]", sk.0)); } } } @@ -566,14 +395,13 @@ pub fn spawn_named_mob( if let Some(natural_attacks) = &mob_template.attacks { let mut natural = NaturalAttacks { attacks: Vec::new() }; for na in natural_attacks.iter() { - let (damage_type, dice) = parse_damage_string(&na.damage); + let (n, d, b) = parse_dice_string(&na.damage); let attack = NaturalAttack { name: na.name.clone(), - damage_type, hit_bonus: na.hit_bonus, - damage_n_dice: dice.n_dice, - damage_die_type: dice.die_type, - damage_bonus: dice.bonus, + damage_n_dice: n, + damage_die_type: d, + damage_bonus: b, }; natural.attacks.push(attack); } @@ -607,21 +435,11 @@ pub fn spawn_named_mob( eb = eb.with(LootTable { table: loot.table.clone(), chance: loot.chance }); } - if CONFIG.logging.log_spawning { - console::log( - format!( - "SPAWNLOG: {} ({}HP, {}MANA, {}BAC) spawned at level {} ({}[base], {}[map difficulty], {}[player level]), worth {} XP", - &mob_template.name, - mob_hp, - mob_mana, - mob_bac, - mob_level, - base_mob_level, - map_difficulty, - player_level, - xp_value - ) - ); + if LOG_SPAWNING { + rltk::console::log(format!( + "SPAWNLOG: {} ({}HP, {}MANA, {}BAC) spawned at level {} ({}[base], {}[map difficulty], {}[player level]), worth {} XP", + &mob_template.name, mob_hp, mob_mana, mob_bac, mob_level, base_mob_level, map_difficulty, player_level, xp_value + )); } let new_mob = eb.build(); @@ -629,14 +447,7 @@ pub fn spawn_named_mob( // Build entity, then check for anything they're wearing if let Some(wielding) = &mob_template.equipped { for tag in wielding.iter() { - spawn_named_entity( - raws, - ecs, - tag, - None, - SpawnType::Equipped { by: new_mob }, - map_difficulty - ); + spawn_named_entity(raws, ecs, tag, SpawnType::Equipped { by: new_mob }, map_difficulty); } } @@ -645,120 +456,89 @@ pub fn spawn_named_mob( None } -pub fn spawn_named_prop( - raws: &RawMaster, - ecs: &mut World, - key: &str, - pos: SpawnType -) -> Option { +pub fn spawn_named_prop(raws: &RawMaster, ecs: &mut World, key: &str, pos: SpawnType) -> Option { if raws.prop_index.contains_key(key) { - let needs_key; - { - let player_entity = ecs.fetch::(); - needs_key = is_player_owned(&player_entity, &pos); - } - // ENTITY BUILDER PREP let prop_template = &raws.raws.props[raws.prop_index[key]]; + let mut eb = ecs.create_entity().marked::>(); eb = spawn_position(pos, eb, key, raws); - if needs_key { - eb = eb.with(WantsToAssignKey {}); - } - // APPLY MANDATORY COMPONENTS FOR A PROP: - // - Name - // - Prop {} - eb = eb.with(Name { name: prop_template.name.clone(), plural: prop_template.name.clone() }); - eb = eb.with(Prop {}); - // APPLY OPTIONAL COMPONENTS FOR A PROP: - // - Renderable - // - Flags - // - Effects if let Some(renderable) = &prop_template.renderable { eb = eb.with(get_renderable_component(renderable)); } + eb = eb.with(Name { name: prop_template.name.clone(), plural: prop_template.name.clone() }); if let Some(flags) = &prop_template.flags { - apply_flags!(flags, eb); + for flag in flags.iter() { + match flag.as_str() { + "HIDDEN" => eb = eb.with(Hidden {}), + "BLOCKS_TILE" => eb = eb.with(BlocksTile {}), + "BLOCKS_VISIBILITY" => eb = eb.with(BlocksVisibility {}), + "ENTRY_TRIGGER" => eb = eb.with(EntryTrigger {}), + "SINGLE_ACTIVATION" => eb = eb.with(SingleActivation {}), + "DOOR" => eb = eb.with(Door { open: false }), + "PROP" => eb = eb.with(Prop {}), + _ => rltk::console::log(format!("Unrecognised flag: {}", flag.as_str())), + } + } } if let Some(effects_list) = &prop_template.effects { - apply_effects!(effects_list, eb); + for effect in effects_list.iter() { + let effect_name = effect.0.as_str(); + match effect_name { + "damage" => { + let (n_dice, sides, modifier) = parse_dice_string(effect.1.as_str()); + eb = eb.with(InflictsDamage { n_dice, sides, modifier }) + } + "healing" => { + let (n_dice, sides, modifier) = parse_dice_string(effect.1.as_str()); + eb = eb.with(ProvidesHealing { n_dice, sides, modifier }) + } + "confusion" => eb = eb.with(Confusion { turns: effect.1.parse::().unwrap() }), + _ => rltk::console::log(format!("Warning: effect {} not implemented.", effect_name)), + } + } } - // BUILD THE ENTITY + return Some(eb.build()); } None } -fn spawn_position<'a>( - pos: SpawnType, - new_entity: EntityBuilder<'a>, - tag: &str, - raws: &RawMaster -) -> EntityBuilder<'a> { +fn spawn_position<'a>(pos: SpawnType, new_entity: EntityBuilder<'a>, tag: &str, raws: &RawMaster) -> EntityBuilder<'a> { let mut eb = new_entity; match pos { - SpawnType::AtPosition { x, y } => { - eb = eb.with(Position { x, y }); - } - SpawnType::Carried { by } => { - eb = eb.with(InBackpack { owner: by }); - } + SpawnType::AtPosition { x, y } => eb = eb.with(Position { x, y }), + SpawnType::Carried { by } => eb = eb.with(InBackpack { owner: by }), SpawnType::Equipped { by } => { let slot = find_slot_for_equippable_item(tag, raws); - eb = eb.with(Equipped { owner: by, slot }); + eb = eb.with(Equipped { owner: by, slot }) } } eb } -fn is_player_owned(player: &Entity, pos: &SpawnType) -> bool { - match pos { - SpawnType::Carried { by } => { - if by == player { - return true; - } - } - SpawnType::Equipped { by } => { - if by == player { - return true; - } - } - _ => {} - } - false -} - -fn get_renderable_component( - renderable: &super::item_structs::Renderable -) -> crate::components::Renderable { +fn get_renderable_component(renderable: &super::item_structs::Renderable) -> crate::components::Renderable { crate::components::Renderable { - glyph: to_cp437(renderable.glyph.chars().next().unwrap()), - fg: RGB::from_hex(&renderable.fg).expect("Invalid RGB"), - bg: RGB::from_hex(&renderable.bg).expect("Invalid RGB"), + glyph: rltk::to_cp437(renderable.glyph.chars().next().unwrap()), + fg: rltk::RGB::from_hex(&renderable.fg).expect("Invalid RGB"), + bg: rltk::RGB::from_hex(&renderable.bg).expect("Invalid RGB"), render_order: renderable.order, } } -pub fn table_by_name(raws: &RawMaster, key: &str, optional_difficulty: Option) -> RandomTable { - let (difficulty, upper_bound, lower_bound); - if optional_difficulty.is_some() { - difficulty = optional_difficulty.unwrap(); - upper_bound = difficulty; - lower_bound = if key != "mobs" { 0 } else { difficulty / 6 }; - } else { - difficulty = -1; - upper_bound = 100; - lower_bound = 0; - } +pub fn table_by_name(raws: &RawMaster, key: &str, difficulty: i32) -> RandomTable { + let upper_bound = difficulty; + let lower_bound = if key != "mobs" { 0 } else { difficulty / 6 }; if raws.table_index.contains_key(key) { let spawn_table = &raws.raws.spawn_tables[raws.table_index[key]]; use super::SpawnTableEntry; - let available_options: Vec<&SpawnTableEntry> = spawn_table.table + let available_options: Vec<&SpawnTableEntry> = spawn_table + .table .iter() .filter(|entry| entry.difficulty >= lower_bound && entry.difficulty <= upper_bound) .collect(); @@ -772,21 +552,19 @@ pub fn table_by_name(raws: &RawMaster, key: &str, optional_difficulty: Option (i32, i32, i32) { +pub fn parse_dice_string(dice: &str) -> (i32, i32, i32) { lazy_static! { static ref DICE_RE: Regex = Regex::new(r"(\d+)d(\d+)([\+\-]\d+)?").unwrap(); } let mut n_dice = 1; - let mut die_type = 1; + let mut die_type = 4; let mut die_bonus = 0; for cap in DICE_RE.captures_iter(dice) { if let Some(group) = cap.get(1) { @@ -800,53 +578,33 @@ pub fn table_by_name(raws: &RawMaster, key: &str, optional_difficulty: Option EquipmentSlot { if !raws.item_index.contains_key(tag) { - unreachable!("Tried to equip an unknown item: {}", tag); + panic!("Trying to equip an unknown item: {}", tag); } let item_index = raws.item_index[tag]; let item = &raws.raws.items[item_index]; if let Some(flags) = &item.flags { for flag in flags { match flag.as_str() { - "EQUIP_MELEE" => { - return EquipmentSlot::Melee; - } - "EQUIP_SHIELD" => { - return EquipmentSlot::Shield; - } - "EQUIP_BODY" => { - return EquipmentSlot::Body; - } - "EQUIP_HEAD" => { - return EquipmentSlot::Head; - } - "EQUIP_FEET" => { - return EquipmentSlot::Feet; - } - "EQUIP_NECK" => { - return EquipmentSlot::Neck; - } - "EQUIP_BACK" => { - return EquipmentSlot::Back; - } - "EQUIP_HANDS" => { - return EquipmentSlot::Hands; - } + "EQUIP_MELEE" => return EquipmentSlot::Melee, + "EQUIP_SHIELD" => return EquipmentSlot::Shield, + "EQUIP_BODY" => return EquipmentSlot::Body, + "EQUIP_HEAD" => return EquipmentSlot::Head, + "EQUIP_FEET" => return EquipmentSlot::Feet, + "EQUIP_NECK" => return EquipmentSlot::Neck, + "EQUIP_BACK" => return EquipmentSlot::Back, + "EQUIP_HANDS" => return EquipmentSlot::Hands, _ => {} } } } - unreachable!("Tried to equip {}, but it has no slot tag.", tag); + panic!("Trying to equip {}, but it has no slot tag.", tag); } -pub fn roll_on_loot_table( - raws: &RawMaster, - rng: &mut RandomNumberGenerator, - key: &str -) -> Option { +pub fn roll_on_loot_table(raws: &RawMaster, rng: &mut RandomNumberGenerator, key: &str) -> Option { if raws.loot_index.contains_key(key) { console::log(format!("DEBUGINFO: Rolling on loot table: {}", key)); let mut rt = RandomTable::new(); @@ -875,20 +633,14 @@ pub enum SpawnsAs { LargeGroup, } -/// Looks up a key in the mob index, and if a corresponding mob is found, searches the -/// entry for a group spawn flag. i.e. SMALL_GROUP/LARGE_GROUP. pub fn get_mob_spawn_type(raws: &RawMaster, key: &str) -> SpawnsAs { if raws.mob_index.contains_key(key) { let mob_template = &raws.raws.mobs[raws.mob_index[key]]; if let Some(flags) = &mob_template.flags { for flag in flags { match flag.as_str() { - "SMALL_GROUP" => { - return SpawnsAs::SmallGroup; - } - "LARGE_GROUP" => { - return SpawnsAs::LargeGroup; - } + "SMALL_GROUP" => return SpawnsAs::SmallGroup, + "LARGE_GROUP" => return SpawnsAs::LargeGroup, _ => {} } } @@ -897,37 +649,35 @@ pub fn get_mob_spawn_type(raws: &RawMaster, key: &str) -> SpawnsAs { return SpawnsAs::Single; } -pub fn get_mob_spawn_amount( - rng: &mut RandomNumberGenerator, - spawn_type: &SpawnsAs, - player_level: i32 -) -> i32 { +pub fn get_mob_spawn_amount(rng: &mut RandomNumberGenerator, spawn_type: &SpawnsAs, player_level: i32) -> i32 { let n = match spawn_type { // Single mobs always spawn alone. SpawnsAs::Single => 1, // Small groups either spawn alone or as a small group (2-4). SpawnsAs::SmallGroup => { - if rng.roll_dice(1, 2) == 1 { 1 } else { 4 } + if rng.roll_dice(1, 2) == 1 { + 1 + } else { + 4 + } } // Large groups either spawn in a small group or as a large group (2-11). SpawnsAs::LargeGroup => { - if rng.roll_dice(1, 2) == 1 { 4 } else { 11 } + if rng.roll_dice(1, 2) == 1 { + 4 + } else { + 11 + } } }; let roll = if n == 1 { 1 } else { rng.roll_dice(2, n) }; // We want to constrain group sizes depending on player's level, so // we don't get large groups of mobs when the player is unequipped. match player_level { - 0..=2 => { - return i32::max(1, roll / 4); - } - 3..=4 => { - return i32::max(1, roll / 2); - } - _ => { - return roll; - } - } + 0..=2 => return i32::max(1, roll / 4), + 3..=4 => return i32::max(1, roll / 2), + _ => return roll, + }; } pub fn get_scroll_tags() -> Vec { @@ -988,135 +738,3 @@ pub fn is_tag_magic(tag: &str) -> bool { return false; } } - -/// Queries the faction index to obtain one faction's reaction to another faction. -pub fn faction_reaction(this_faction: &str, other_faction: &str, raws: &RawMaster) -> Reaction { - if raws.faction_index.contains_key(this_faction) { - let mine = &raws.faction_index[this_faction]; - if mine.contains_key(other_faction) { - return mine[other_faction]; - } else if mine.contains_key("default") { - return mine["default"]; - } - } - return Reaction::Ignore; -} - -pub fn ancestry_reaction( - this_ancestry: Ancestry, - other_ancestry: Ancestry, - raws: &RawMaster -) -> Option { - if this_ancestry == other_ancestry { - return Some(Reaction::Ignore); - } else { - let this_ancestry = get_ancestry_string(this_ancestry); - let other_ancestry = get_ancestry_string(other_ancestry); - if raws.ancestry_index.contains_key(this_ancestry) { - let mine = &raws.ancestry_index[this_ancestry]; - if mine.contains(other_ancestry) { - return Some(Reaction::Ignore); - } - } - } - return None; -} - -pub fn get_reactions( - this_entity: Entity, - other_entity: Entity, - factions: &ReadStorage, - ancestries: &ReadStorage, - raws: &RawMaster -) -> Reaction { - if let Some(this_ancestry) = ancestries.get(this_entity) { - if let Some(other_ancestry) = ancestries.get(other_entity) { - let result = ancestry_reaction(this_ancestry.name, other_ancestry.name, raws); - if result.is_some() { - return result.unwrap(); - } - } - } - if let Some(this_faction) = factions.get(this_entity) { - if let Some(other_faction) = factions.get(other_entity) { - return faction_reaction(&this_faction.name, &other_faction.name, raws); - } - } - return Reaction::Ignore; -} - -fn get_ancestry_string(ancestry: Ancestry) -> &'static str { - match ancestry { - Ancestry::Human => { - return "human"; - } - Ancestry::Elf => { - return "elf"; - } - Ancestry::Dwarf => { - return "dwarf"; - } - Ancestry::Catfolk => { - return "catfolk"; - } - Ancestry::Gnome => { - return "gnome"; - } - Ancestry::NULL => { - return "NULL"; - } - } -} - -fn parse_particle_line(n: &str) -> SpawnParticleLine { - let tokens: Vec<_> = n.split(';').collect(); - SpawnParticleLine { - glyph: to_cp437(tokens[0].chars().next().unwrap()), - tail_glyph: to_cp437(tokens[1].chars().next().unwrap()), - colour: RGB::from_hex(tokens[2]).expect("Invalid RGB"), - lifetime_ms: tokens[3].parse::().unwrap(), - trail_colour: RGB::from_hex(tokens[4]).expect("Invalid trail RGB"), - trail_lifetime_ms: tokens[5].parse::().unwrap(), - } -} - -fn parse_particle(n: &str) -> SpawnParticleSimple { - let tokens: Vec<_> = n.split(';').collect(); - SpawnParticleSimple { - glyph: to_cp437(tokens[0].chars().next().unwrap()), - colour: RGB::from_hex(tokens[1]).expect("Invalid RGB"), - lifetime_ms: tokens[2].parse::().unwrap(), - } -} - -fn parse_particle_burst(n: &str) -> SpawnParticleBurst { - let tokens: Vec<_> = n.split(';').collect(); - SpawnParticleBurst { - glyph: to_cp437(tokens[0].chars().next().unwrap()), - head_glyph: to_cp437(tokens[1].chars().next().unwrap()), - tail_glyph: to_cp437(tokens[2].chars().next().unwrap()), - colour: RGB::from_hex(tokens[3]).expect("Invalid RGB"), - lerp: RGB::from_hex(tokens[4]).expect("Invalid LERP RGB"), - lifetime_ms: tokens[5].parse::().unwrap(), - trail_colour: RGB::from_hex(tokens[6]).expect("Invalid trail RGB"), - trail_lifetime_ms: tokens[7].parse::().unwrap(), - } -} - -fn parse_damage_string(n: &str) -> (DamageType, DiceType) { - let tokens: Vec<_> = n.split(';').collect(); - let damage_type = if tokens.len() > 1 { - match tokens[1] { - "physical" => DamageType::Physical, - "magic" => DamageType::Magic, - "fire" => DamageType::Fire, - "cold" => DamageType::Cold, - "poison" => DamageType::Poison, - _ => unreachable!("Unrecognised damage type in raws: {}", tokens[1]), - } - } else { - DamageType::Physical - }; - let dice = parse_dice_string(tokens[0]).expect("Failed to parse dice string"); - return (damage_type, dice); -} diff --git a/src/raws/reaction_structs.rs b/src/raws/reaction_structs.rs deleted file mode 100644 index 2399444..0000000 --- a/src/raws/reaction_structs.rs +++ /dev/null @@ -1,21 +0,0 @@ -use serde::Deserialize; -use std::collections::{ HashMap, HashSet }; - -#[derive(Deserialize, Debug)] -pub struct FactionData { - pub id: String, - pub responses: HashMap, -} - -#[derive(Deserialize, Debug)] -pub struct AncestryData { - pub id: String, - pub allies: HashSet, -} - -#[derive(PartialEq, Eq, Hash, Copy, Clone)] -pub enum Reaction { - Ignore, - Attack, - Flee, -} diff --git a/src/rect.rs b/src/rect.rs new file mode 100644 index 0000000..b705387 --- /dev/null +++ b/src/rect.rs @@ -0,0 +1,24 @@ +use serde::{Deserialize, Serialize}; + +#[derive(PartialEq, Copy, Clone, Serialize, Deserialize)] +pub struct Rect { + pub x1: i32, + pub x2: i32, + pub y1: i32, + pub y2: i32, +} + +impl Rect { + pub fn new(x: i32, y: i32, w: i32, h: i32) -> Rect { + Rect { x1: x, y1: y, x2: x + w, y2: y + h } + } + + //Returns true if this overlaps with other + pub fn intersect(&self, other: &Rect) -> bool { + self.x1 <= other.x2 && self.x2 >= other.x1 && self.y1 <= other.y2 && self.y2 >= other.y1 + } + + pub fn centre(&self) -> (i32, i32) { + ((self.x1 + self.x2) / 2, (self.y1 + self.y2) / 2) + } +} diff --git a/src/rex_assets.rs b/src/rex_assets.rs index 4302c34..b451e94 100644 --- a/src/rex_assets.rs +++ b/src/rex_assets.rs @@ -1,8 +1,8 @@ -use bracket_lib::prelude::*; +use rltk::rex::XpFile; -embedded_resource!(TITLEIMAGE_105_56_BYTES, "../resources/title_image.xp"); -embedded_resource!(WFC_DEMO_IMAGE1, "../resources/wfc-demo1.xp"); -embedded_resource!(WFC_POPULATED, "../resources/wfc-populated.xp"); +rltk::embedded_resource!(TITLEIMAGE_105_56_BYTES, "../resources/title_image.xp"); +rltk::embedded_resource!(WFC_DEMO_IMAGE1, "../resources/wfc-demo1.xp"); +rltk::embedded_resource!(WFC_POPULATED, "../resources/wfc-populated.xp"); pub struct RexAssets { pub menu: XpFile, @@ -11,9 +11,9 @@ pub struct RexAssets { impl RexAssets { #[allow(clippy::new_without_default)] pub fn new() -> RexAssets { - link_resource!(TITLEIMAGE_105_56_BYTES, "../resources/title_image.xp"); - link_resource!(WFC_DEMO_IMAGE1, "../resources/wfc-demo1.xp"); - link_resource!(WFC_POPULATED, "../resources/wfc-populated.xp"); + rltk::link_resource!(TITLEIMAGE_105_56_BYTES, "../resources/title_image.xp"); + rltk::link_resource!(WFC_DEMO_IMAGE1, "../resources/wfc-demo1.xp"); + rltk::link_resource!(WFC_POPULATED, "../resources/wfc-populated.xp"); RexAssets { menu: XpFile::from_resource("../resources/title_image.xp").unwrap() } } diff --git a/src/saveload_system.rs b/src/saveload_system.rs index f3b284d..a0506f0 100644 --- a/src/saveload_system.rs +++ b/src/saveload_system.rs @@ -1,21 +1,13 @@ use super::components::*; -use bracket_lib::prelude::*; use specs::error::NoError; use specs::prelude::*; -use specs::saveload::{ - DeserializeComponents, - MarkedBuilder, - SerializeComponents, - SimpleMarker, - SimpleMarkerAllocator, -}; - +use specs::saveload::{DeserializeComponents, MarkedBuilder, SerializeComponents, SimpleMarker, SimpleMarkerAllocator}; use std::fs; use std::fs::File; use std::path::Path; macro_rules! serialize_individually { - ($ecs:expr, $ser:expr, $data:expr, $($type:ty),*) => { + ($ecs:expr, $ser:expr, $data:expr, $( $type:ty),*) => { $( SerializeComponents::>::serialize( &( $ecs.read_storage::<$type>(), ), @@ -36,17 +28,13 @@ pub fn save_game(ecs: &mut World) { // Create helper let mapcopy = ecs.get_mut::().unwrap().clone(); let dungeon_master = ecs.get_mut::().unwrap().clone(); - let savehelper = ecs - .create_entity() - .with(SerializationHelper { map: mapcopy }) - .marked::>() - .build(); + let savehelper = + ecs.create_entity().with(SerializationHelper { map: mapcopy }).marked::>().build(); let savehelper2 = ecs .create_entity() .with(DMSerializationHelper { map: dungeon_master, log: crate::gamelog::clone_log(), - event_counts: crate::gamelog::clone_event_counts(), events: crate::gamelog::clone_events(), }) .marked::>() @@ -65,16 +53,14 @@ pub fn save_game(ecs: &mut World) { AOE, ArmourClassBonus, Attributes, - Beatitude, - Bleeds, - Blind, BlocksTile, BlocksVisibility, Burden, - Chasing, + Bystander, Clock, Confusion, Consumable, + Cursed, Destructible, Digger, Door, @@ -84,29 +70,19 @@ pub fn save_game(ecs: &mut World) { EquipmentChanged, Equippable, Equipped, - Faction, - GrantsSpell, GrantsXP, - HasAncestry, - HasClass, - HasDamageModifiers, Hidden, HungerClock, - IdentifiedBeatitude, IdentifiedItem, InBackpack, InflictsDamage, - IntrinsicChanged, - Intrinsics, Item, - Key, - KnownSpells, LootTable, MagicItem, MagicMapper, MeleeWeapon, Mind, - MoveMode, + Monster, MultiAttack, NaturalAttacks, Name, @@ -118,32 +94,21 @@ pub fn save_game(ecs: &mut World) { Position, Prop, ProvidesHealing, - ProvidesIdentify, ProvidesNutrition, - ProvidesRemoveCurse, Quips, Ranged, Renderable, SingleActivation, Skills, - SpawnParticleBurst, - SpawnParticleLine, - SpawnParticleSimple, - Stackable, + SufferDamage, TakingTurn, Telepath, - ToHitBonus, Viewshed, - Charges, - WantsToApproach, - WantsToAssignKey, - WantsToDelete, + Wand, WantsToDropItem, - WantsToFlee, WantsToMelee, WantsToPickupItem, WantsToRemoveItem, - WantsToRemoveKey, WantsToUseItem, SerializationHelper, DMSerializationHelper @@ -160,7 +125,7 @@ pub fn does_save_exist() -> bool { } macro_rules! deserialize_individually { - ($ecs:expr, $de:expr, $data:expr, $($type:ty),*) => { + ($ecs:expr, $de:expr, $data:expr, $( $type:ty),*) => { $( DeserializeComponents::::deserialize( &mut ( &mut $ecs.write_storage::<$type>(), ), @@ -203,16 +168,14 @@ pub fn load_game(ecs: &mut World) { AOE, ArmourClassBonus, Attributes, - Beatitude, - Bleeds, - Blind, BlocksTile, BlocksVisibility, Burden, - Chasing, + Bystander, Clock, Confusion, Consumable, + Cursed, Destructible, Digger, Door, @@ -222,29 +185,19 @@ pub fn load_game(ecs: &mut World) { EquipmentChanged, Equippable, Equipped, - Faction, - GrantsSpell, GrantsXP, - HasAncestry, - HasClass, - HasDamageModifiers, Hidden, HungerClock, - IdentifiedBeatitude, IdentifiedItem, InBackpack, InflictsDamage, - IntrinsicChanged, - Intrinsics, Item, - Key, - KnownSpells, LootTable, MagicItem, MagicMapper, MeleeWeapon, Mind, - MoveMode, + Monster, MultiAttack, NaturalAttacks, Name, @@ -256,32 +209,21 @@ pub fn load_game(ecs: &mut World) { Position, Prop, ProvidesHealing, - ProvidesIdentify, ProvidesNutrition, - ProvidesRemoveCurse, Quips, Ranged, Renderable, SingleActivation, Skills, - SpawnParticleBurst, - SpawnParticleLine, - SpawnParticleSimple, - Stackable, + SufferDamage, TakingTurn, Telepath, - ToHitBonus, Viewshed, - Charges, - WantsToApproach, - WantsToAssignKey, - WantsToDelete, + Wand, WantsToDropItem, - WantsToFlee, WantsToMelee, WantsToPickupItem, WantsToRemoveItem, - WantsToRemoveKey, WantsToUseItem, SerializationHelper, DMSerializationHelper @@ -299,7 +241,7 @@ pub fn load_game(ecs: &mut World) { for (e, h) in (&entities, &helper).join() { let mut worldmap = ecs.write_resource::(); *worldmap = h.map.clone(); - crate::spatial::set_size((worldmap.width * worldmap.height) as usize); + worldmap.tile_content = vec![Vec::new(); (worldmap.width * worldmap.height) as usize]; deleteme = Some(e); } for (e, h) in (&entities, &helper2).join() { @@ -307,12 +249,11 @@ pub fn load_game(ecs: &mut World) { *dungeonmaster = h.map.clone(); deleteme2 = Some(e); crate::gamelog::restore_log(&mut h.log.clone()); - crate::gamelog::restore_event_counter(h.event_counts.clone()); - crate::gamelog::restore_events(h.events.clone()); + crate::gamelog::load_events(h.events.clone()); } for (e, _p, pos) in (&entities, &player, &position).join() { - let mut ppos = ecs.write_resource::(); - *ppos = Point::new(pos.x, pos.y); + let mut ppos = ecs.write_resource::(); + *ppos = rltk::Point::new(pos.x, pos.y); let mut player_resource = ecs.write_resource::(); *player_resource = e; } diff --git a/src/spatial/map_indexing_system.rs b/src/spatial/map_indexing_system.rs deleted file mode 100644 index 27300fe..0000000 --- a/src/spatial/map_indexing_system.rs +++ /dev/null @@ -1,33 +0,0 @@ -use crate::{ spatial, BlocksTile, Map, Pools, Position }; -use specs::prelude::*; - -pub struct MapIndexingSystem {} - -impl<'a> System<'a> for MapIndexingSystem { - type SystemData = ( - ReadExpect<'a, Map>, - ReadStorage<'a, Position>, - ReadStorage<'a, BlocksTile>, - Entities<'a>, - ReadStorage<'a, Pools>, - ); - - fn run(&mut self, data: Self::SystemData) { - let (map, position, blockers, entities, pools) = data; - - spatial::clear(); - spatial::populate_blocked_from_map(&*map); - for (entity, position) in (&entities, &position).join() { - let mut alive = true; - if let Some(pools) = pools.get(entity) { - if pools.hit_points.current < 1 { - alive = false; - } - } - if alive { - let idx = map.xy_idx(position.x, position.y); - spatial::index_entity(entity, idx, blockers.get(entity).is_some()); - } - } - } -} diff --git a/src/spatial/mod.rs b/src/spatial/mod.rs deleted file mode 100644 index ca5d5c7..0000000 --- a/src/spatial/mod.rs +++ /dev/null @@ -1,158 +0,0 @@ -use crate::{ tile_walkable, Map, RunState }; -use specs::prelude::*; -use std::sync::Mutex; - -mod map_indexing_system; -pub use map_indexing_system::MapIndexingSystem; - -struct SpatialMap { - blocked: Vec<(bool, bool)>, - tile_content: Vec>, -} - -impl SpatialMap { - fn new() -> Self { - return Self { blocked: Vec::new(), tile_content: Vec::new() }; - } -} - -lazy_static! { - static ref SPATIAL_MAP: Mutex = Mutex::new(SpatialMap::new()); -} - -/// Sets the size of the SpatialMap. -pub fn set_size(map_tile_count: usize) { - let mut lock = SPATIAL_MAP.lock().unwrap(); - lock.blocked = vec![(false, false); map_tile_count]; - lock.tile_content = vec![Vec::new(); map_tile_count]; -} - -/// Clears the SpatialMap. Blocked is set to (false, false), -/// and all tile content is cleared. -pub fn clear() { - let mut lock = SPATIAL_MAP.lock().unwrap(); - lock.blocked.iter_mut().for_each(|b| { - b.0 = false; - b.1 = false; - }); - for content in lock.tile_content.iter_mut() { - content.clear(); - } -} - -/// Iterates through every tile in the map, setting the SpatialMap's -/// blocked-by-map tuple entry to true wherever a tile is impassable. -pub fn populate_blocked_from_map(map: &Map) { - let mut lock = SPATIAL_MAP.lock().unwrap(); - for (i, tile) in map.tiles.iter().enumerate() { - lock.blocked[i].0 = !tile_walkable(*tile); - } -} - -/// Indexes a new entity within the SpatialMap, storing the entity -/// and their BlocksTile status. -pub fn index_entity(entity: Entity, idx: usize, blocks_tile: bool) { - let mut lock = SPATIAL_MAP.lock().unwrap(); - lock.tile_content[idx].push((entity, blocks_tile)); - if blocks_tile { - lock.blocked[idx].1 = true; - } -} - -/// Removes an entity from SpatialMap tilecontent. -pub fn remove_entity(entity: Entity, idx: usize) { - let mut lock = SPATIAL_MAP.lock().unwrap(); - lock.tile_content[idx].retain(|(e, _)| *e != entity); - let mut from_blocked = false; - lock.tile_content[idx].iter().for_each(|(_, blocks)| { - if *blocks { - from_blocked = true; - } - }); - lock.blocked[idx].1 = from_blocked; -} - -/// Returns is_empty on a given tile content idx. -pub fn has_tile_content(idx: usize) -> bool { - let lock = SPATIAL_MAP.lock().unwrap(); - if lock.tile_content[idx].is_empty() { - return false; - } - return true; -} - -/// Returns the number of entries on a given index. -pub fn length(idx: usize) -> usize { - let lock = SPATIAL_MAP.lock().unwrap(); - return lock.tile_content[idx].len(); -} - -/// Returns true if the idx is blocked by either a map tile or an entity. -pub fn is_blocked(idx: usize) -> bool { - let lock = SPATIAL_MAP.lock().unwrap(); - return lock.blocked[idx].0 || lock.blocked[idx].1; -} - -/// Calls a function on every entity within a given tile idx. -pub fn for_each_tile_content(idx: usize, mut f: F) where F: FnMut(Entity) { - let lock = SPATIAL_MAP.lock().unwrap(); - for entity in lock.tile_content[idx].iter() { - f(entity.0); - } -} - -/// Calls a function on every entity within a given tile idx, with the -/// added ability to return a RunState mid-calc. -pub fn for_each_tile_content_with_runstate(idx: usize, mut f: F) -> Option - where F: FnMut(Entity) -> Option -{ - let lock = SPATIAL_MAP.lock().unwrap(); - for entity in lock.tile_content[idx].iter() { - if let Some(rs) = f(entity.0) { - return Some(rs); - } - } - return None; -} - -/// Calls a function on every entity within a given tile idx, breaking if -/// the closure ever returns false. -pub fn for_each_tile_content_with_bool(idx: usize, mut f: F) where F: FnMut(Entity) -> bool { - let lock = SPATIAL_MAP.lock().unwrap(); - for entity in lock.tile_content[idx].iter() { - if !f(entity.0) { - break; - } - } -} - -/// Moves an entity from one index to another in the SpatialMap, and -/// recalculates blocks for both affected tiles. -pub fn move_entity(entity: Entity, moving_from: usize, moving_to: usize) { - let mut lock = SPATIAL_MAP.lock().unwrap(); - let mut entity_blocks = false; - lock.tile_content[moving_from].retain(|(e, blocks)| { - if *e == entity { - entity_blocks = *blocks; - return false; - } else { - return true; - } - }); - lock.tile_content[moving_to].push((entity, entity_blocks)); - // Recalculate blocks - let mut from_blocked = false; - let mut to_blocked = false; - lock.tile_content[moving_from].iter().for_each(|(_, blocks)| { - if *blocks { - from_blocked = true; - } - }); - lock.tile_content[moving_to].iter().for_each(|(_, blocks)| { - if *blocks { - to_blocked = true; - } - }); - lock.blocked[moving_from].1 = from_blocked; - lock.blocked[moving_to].1 = to_blocked; -} diff --git a/src/spawner.rs b/src/spawner.rs index 3fa673c..fe8bf79 100644 --- a/src/spawner.rs +++ b/src/spawner.rs @@ -1,40 +1,12 @@ use super::{ - random_table::RandomTable, - raws, - Attribute, - Attributes, - Clock, - Energy, - EquipmentChanged, - Faction, - HungerClock, - HungerState, - Map, - Mind, - Name, - Player, - Pool, - Pools, - Position, - Rect, - Renderable, - SerializeMe, - Skill, - Skills, - tile_walkable, - Viewshed, - BlocksTile, - Bleeds, - HasDamageModifiers, - Intrinsics, + ai::NORMAL_SPEED, gamesystem, gamesystem::attr_bonus, random_table::RandomTable, raws, Attribute, Attributes, + Clock, Energy, EquipmentChanged, HungerClock, HungerState, Map, Name, Player, Pool, Pools, Position, Rect, + Renderable, SerializeMe, Skill, Skills, TileType, Viewshed, }; -use crate::data::entity; -use crate::data::visuals::BLOODSTAIN_COLOUR; -use crate::gamesystem::*; -use bracket_lib::prelude::*; +use rltk::{RandomNumberGenerator, RGB}; use specs::prelude::*; -use specs::saveload::{ MarkedBuilder, SimpleMarker }; -use std::collections::{ HashMap, HashSet }; +use specs::saveload::{MarkedBuilder, SimpleMarker}; +use std::collections::HashMap; /// Spawns the player and returns his/her entity object. pub fn player(ecs: &mut World, player_x: i32, player_y: i32) -> Entity { @@ -42,60 +14,69 @@ pub fn player(ecs: &mut World, player_x: i32, player_y: i32) -> Entity { skills.skills.insert(Skill::Melee, 0); skills.skills.insert(Skill::Defence, 0); skills.skills.insert(Skill::Magic, 0); - let (int, con) = (10, 10); + + let mut rng = ecs.write_resource::(); + let str = gamesystem::roll_4d6(&mut rng); + let dex = gamesystem::roll_4d6(&mut rng); + let con = gamesystem::roll_4d6(&mut rng); + let int = gamesystem::roll_4d6(&mut rng); + let wis = gamesystem::roll_4d6(&mut rng); + let cha = gamesystem::roll_4d6(&mut rng); + std::mem::drop(rng); + // We only create the player once, so create the Clock here for counting turns too. - ecs.create_entity() - .with(Clock {}) - .with(Energy { current: 0, speed: entity::NORMAL_SPEED }) - .build(); + ecs.create_entity().with(Clock {}).with(Energy { current: 0, speed: NORMAL_SPEED }).build(); let player = ecs .create_entity() .with(Position { x: player_x, y: player_y }) - .with(BlocksTile {}) .with(Renderable { - glyph: to_cp437('@'), - fg: RGB::named(YELLOW), - bg: RGB::named(BLACK), + glyph: rltk::to_cp437('@'), + fg: RGB::named(rltk::YELLOW), + bg: RGB::named(rltk::BLACK), render_order: 0, }) - .with(Bleeds { colour: RGB::named(BLOODSTAIN_COLOUR) }) .with(Player {}) - .with(Mind {}) - .with(Faction { name: "player".to_string() }) - .with(Viewshed { - visible_tiles: Vec::new(), - range: entity::DEFAULT_VIEWSHED_STANDARD, - dirty: true, - }) + .with(Viewshed { visible_tiles: Vec::new(), range: 12, dirty: true }) .with(Name { name: "you".to_string(), plural: "you".to_string() }) - .with(HungerClock { state: HungerState::Satiated, duration: 1200 }) + .with(HungerClock { state: HungerState::Satiated, duration: 200 }) .with(Attributes { - // These are overwritten with chargen later -- placeholders. - strength: Attribute { base: 10, modifiers: 0, bonus: 0 }, - dexterity: Attribute { base: 10, modifiers: 0, bonus: 0 }, - constitution: Attribute { base: 10, modifiers: 0, bonus: 0 }, - intelligence: Attribute { base: 10, modifiers: 0, bonus: 0 }, - wisdom: Attribute { base: 10, modifiers: 0, bonus: 0 }, - charisma: Attribute { base: 10, modifiers: 0, bonus: 0 }, + strength: Attribute { base: str, modifiers: 0, bonus: attr_bonus(str) }, + dexterity: Attribute { base: dex, modifiers: 0, bonus: attr_bonus(dex) }, + constitution: Attribute { base: con, modifiers: 0, bonus: attr_bonus(con) }, + intelligence: Attribute { base: int, modifiers: 0, bonus: attr_bonus(int) }, + wisdom: Attribute { base: wis, modifiers: 0, bonus: attr_bonus(wis) }, + charisma: Attribute { base: cha, modifiers: 0, bonus: attr_bonus(cha) }, }) .with(Pools { - // These are overwritten with chargen later -- placeholders. - hit_points: Pool { current: 8 + attr_bonus(con), max: 8 + attr_bonus(con) }, - mana: Pool { current: 1 + attr_bonus(int), max: 1 + attr_bonus(int) }, + hit_points: Pool { current: 10 + attr_bonus(con), max: 10 + attr_bonus(con) }, + mana: Pool { current: 2 + attr_bonus(int), max: 2 + attr_bonus(int) }, xp: 0, level: 1, bac: 10, weight: 0.0, god: false, }) - .with(HasDamageModifiers { modifiers: HashMap::new() }) - .with(Intrinsics { list: HashSet::new() }) - .with(EquipmentChanged {}) // To force re-calc of equipment bonuses. + .with(EquipmentChanged {}) .with(skills) - .with(Energy { current: 0, speed: entity::NORMAL_SPEED }) + .with(Energy { current: 0, speed: NORMAL_SPEED }) .marked::>() .build(); + raws::spawn_named_entity( + &raws::RAWS.lock().unwrap(), + ecs, + "equip_dagger", + raws::SpawnType::Equipped { by: player }, + 0, + ); + raws::spawn_named_entity( + &raws::RAWS.lock().unwrap(), + ecs, + "food_apple", + raws::SpawnType::Carried { by: player }, + 0, + ); + return player; } @@ -105,7 +86,7 @@ pub fn spawn_room( rng: &mut RandomNumberGenerator, room: &Rect, spawn_list: &mut Vec<(usize, String)>, - player_level: i32 + player_level: i32, ) { let mut possible_targets: Vec = Vec::new(); { @@ -113,7 +94,7 @@ pub fn spawn_room( for y in room.y1 + 1..room.y2 { for x in room.x1 + 1..room.x2 { let idx = map.xy_idx(x, y); - if tile_walkable(map.tiles[idx]) { + if map.tiles[idx] == TileType::Floor { possible_targets.push(idx); } } @@ -128,22 +109,23 @@ pub fn spawn_region( rng: &mut RandomNumberGenerator, area: &[usize], spawn_list: &mut Vec<(usize, String)>, - player_level: i32 + player_level: i32, ) { let mut spawn_points: HashMap = HashMap::new(); let mut areas: Vec = Vec::from(area); let difficulty = (map.difficulty + player_level) / 2; // If no area, log and return. if areas.len() == 0 { - console::log("DEBUGINFO: No areas capable of spawning mobs!"); + rltk::console::log("DEBUGINFO: No areas capable of spawning mobs!"); return; } // Get num of each entity type. let spawn_mob: bool = rng.roll_dice(1, 3) == 1; let num_items = match rng.roll_dice(1, 20) { 1..=2 => 1, // 10% chance of spawning 1 item - 3 => 2, // 5% chance of spawning 2 items - _ => 0, // 85% chance of spawning nothing + 3 => 2, // 5% chance of spawning 2 items + 4 => 3, // 5% chance of spawning 3 items + _ => 0, // 80% chance of spawning nothing }; let num_traps = match rng.roll_dice(1, 20) { 1 => 1, // 5% chance of spawning 1 trap @@ -152,7 +134,7 @@ pub fn spawn_region( }; // Roll on each table, getting an entity + spawn point if spawn_mob { - let key = mob_table(Some(difficulty)).roll(rng); + let key = mob_table(difficulty).roll(rng); let spawn_type = raws::get_mob_spawn_type(&raws::RAWS.lock().unwrap(), &key); let roll = raws::get_mob_spawn_amount(rng, &spawn_type, player_level); for _i in 0..roll { @@ -160,13 +142,13 @@ pub fn spawn_region( } } for _i in 0..num_traps { - let key = trap_table(Some(difficulty)).roll(rng); + let key = trap_table(difficulty).roll(rng); entity_to_spawn_list(rng, &mut areas, key, &mut spawn_points); } for _i in 0..num_items { // Player level isn't taken into account for item spawning, to encourage // delving deeper to gear up more quickly. - let key = get_random_item_category(rng, Some(map.difficulty)).roll(rng); + let key = get_random_item_category(rng, map.difficulty).roll(rng); entity_to_spawn_list(rng, &mut areas, key, &mut spawn_points); } // Push entities and their spawn points to map's spawn list @@ -179,16 +161,13 @@ fn entity_to_spawn_list( rng: &mut RandomNumberGenerator, possible_areas: &mut Vec, key: String, - spawn_points: &mut HashMap + spawn_points: &mut HashMap, ) { if possible_areas.len() == 0 { return; } - let array_idx = if possible_areas.len() == 1 { - 0usize - } else { - (rng.roll_dice(1, possible_areas.len() as i32) - 1) as usize - }; + let array_idx = + if possible_areas.len() == 1 { 0usize } else { (rng.roll_dice(1, possible_areas.len() as i32) - 1) as usize }; let map_idx = possible_areas[array_idx]; spawn_points.insert(map_idx, key); possible_areas.remove(array_idx); @@ -207,83 +186,61 @@ pub fn spawn_entity(ecs: &mut World, spawn: &(&usize, &String)) { &raws::RAWS.lock().unwrap(), ecs, &spawn.1, - None, raws::SpawnType::AtPosition { x, y }, - map_difficulty + map_difficulty, ); if spawn_result.is_some() { return; } - console::log(format!("WARNING: We don't know how to spawn [{}]!", spawn.1)); + rltk::console::log(format!("WARNING: We don't know how to spawn [{}]!", spawn.1)); } // 3 scrolls : 3 potions : 1 equipment : 1 wand? fn item_category_table() -> RandomTable { - return RandomTable::new() - .add("equipment", 20) - .add("food", 20) - .add("potion", 16) - .add("scroll", 16) - .add("wand", 4); + return RandomTable::new().add("equipment", 20).add("food", 20).add("potion", 16).add("scroll", 16).add("wand", 4); } fn debug_table() -> RandomTable { return RandomTable::new().add("debug", 1); } -fn get_random_item_category( - rng: &mut RandomNumberGenerator, - difficulty: Option -) -> RandomTable { +fn get_random_item_category(rng: &mut RandomNumberGenerator, difficulty: i32) -> RandomTable { let item_category = item_category_table().roll(rng); match item_category.as_ref() { - "equipment" => { - return equipment_table(difficulty); - } - "food" => { - return food_table(difficulty); - } - "potion" => { - return potion_table(difficulty); - } - "scroll" => { - return scroll_table(difficulty); - } - "wand" => { - return wand_table(difficulty); - } - _ => { - return debug_table(); - } + "equipment" => return equipment_table(difficulty), + "food" => return food_table(difficulty), + "potion" => return potion_table(difficulty), + "scroll" => return scroll_table(difficulty), + "wand" => return wand_table(difficulty), + _ => return debug_table(), }; } -pub fn equipment_table(difficulty: Option) -> RandomTable { +pub fn equipment_table(difficulty: i32) -> RandomTable { raws::table_by_name(&raws::RAWS.lock().unwrap(), "equipment", difficulty) } -pub fn potion_table(difficulty: Option) -> RandomTable { +pub fn potion_table(difficulty: i32) -> RandomTable { raws::table_by_name(&raws::RAWS.lock().unwrap(), "potions", difficulty) } -pub fn scroll_table(difficulty: Option) -> RandomTable { +pub fn scroll_table(difficulty: i32) -> RandomTable { raws::table_by_name(&raws::RAWS.lock().unwrap(), "scrolls", difficulty) } -pub fn wand_table(difficulty: Option) -> RandomTable { +pub fn wand_table(difficulty: i32) -> RandomTable { raws::table_by_name(&raws::RAWS.lock().unwrap(), "wands", difficulty) } -pub fn food_table(difficulty: Option) -> RandomTable { +pub fn food_table(difficulty: i32) -> RandomTable { raws::table_by_name(&raws::RAWS.lock().unwrap(), "food", difficulty) } -/// Locks RAWS, and provides access to master list of all mobs. -pub fn mob_table(difficulty: Option) -> RandomTable { +pub fn mob_table(difficulty: i32) -> RandomTable { raws::table_by_name(&raws::RAWS.lock().unwrap(), "mobs", difficulty) } -pub fn trap_table(difficulty: Option) -> RandomTable { +pub fn trap_table(difficulty: i32) -> RandomTable { raws::table_by_name(&raws::RAWS.lock().unwrap(), "traps", difficulty) } diff --git a/src/states/mod.rs b/src/states/mod.rs deleted file mode 100644 index f1c858e..0000000 --- a/src/states/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod runstate; -pub mod state; diff --git a/src/states/runstate.rs b/src/states/runstate.rs deleted file mode 100644 index 2abe9b5..0000000 --- a/src/states/runstate.rs +++ /dev/null @@ -1,46 +0,0 @@ -use specs::prelude::*; -use crate::gui; -use crate::map::TileType; - -#[derive(PartialEq, Copy, Clone)] -pub enum RunState { - AwaitingInput, // Player's turn - PreRun, - Ticking, // Tick systems - ShowCheatMenu, // Teleport, godmode, etc. - for debugging - ShowInventory, - ShowDropItem, - ShowRemoveItem, - ShowTargeting { - x: i32, - y: i32, - range: i32, - item: Entity, - aoe: i32, - }, - ShowRemoveCurse, - ShowIdentify, - ActionWithDirection { - function: fn(i: i32, j: i32, ecs: &mut World) -> RunState, - }, - MainMenu { - menu_selection: gui::MainMenuSelection, - }, - CharacterCreation { - ancestry: gui::Ancestry, - class: gui::Class, - }, - SaveGame, - GameOver, - GoToLevel(i32, TileType), - HelpScreen, - MagicMapReveal { - row: i32, - cursed: bool, - }, // Animates magic mapping effect - MapGeneration, - Farlook { - x: i32, - y: i32, - }, -} diff --git a/src/states/state.rs b/src/states/state.rs deleted file mode 100644 index 3dde32b..0000000 --- a/src/states/state.rs +++ /dev/null @@ -1,592 +0,0 @@ -use specs::prelude::*; -use bracket_lib::prelude::*; -use super::runstate::RunState; -use crate::map::*; -use crate::hunger_system; -use crate::particle_system; -use crate::trigger_system; -use crate::inventory; -use crate::melee_combat_system::MeleeCombatSystem; -use crate::spatial; -use crate::effects; -use crate::visibility_system::VisibilitySystem; -use crate::ai; -use crate::gamelog; -use crate::spawner; -use crate::data::ids::*; -use crate::data::events::*; -use crate::components::*; -use crate::player::*; -use crate::gui; -use crate::config; -use crate::camera; -use crate::saveload_system; -use crate::morgue; -use crate::damage_system; - -pub struct State { - pub ecs: World, - pub mapgen_next_state: Option, - pub mapgen_history: Vec, - pub mapgen_index: usize, - pub mapgen_timer: f32, -} - -impl State { - pub fn generate_world_map(&mut self, new_id: i32, dest_tile: TileType) { - // Visualisation stuff - self.mapgen_index = 0; - self.mapgen_timer = 0.0; - self.mapgen_history.clear(); - let map_building_info = level_transition(&mut self.ecs, new_id, dest_tile); - if let Some(history) = map_building_info { - self.mapgen_history = history; - } else { - dungeon::thaw_entities(&mut self.ecs); - } - } - - fn run_systems(&mut self) { - let mut hunger_clock = hunger_system::HungerSystem {}; - let mut particle_system = particle_system::ParticleSpawnSystem {}; - - // Order is *very* important here, to ensure effects take place in the right order, - // and that the player/AI are making decisions based on the most up-to-date info. - - self.resolve_entity_decisions(); // Push Player messages of intent to effects queue, and run it. - self.refresh_indexes(); // Get up-to-date map and viewsheds prior to AI turn. - self.run_ai(); // Get AI decision-making. - self.resolve_entity_decisions(); // Push AI messages of intent to effects queue, and run it. - hunger_clock.run_now(&self.ecs); // Tick the hunger clock (on the turn clock's turn) - particle_system.run_now(&self.ecs); // Spawn/delete particles (turn independent) - self.ecs.maintain(); - } - - fn resolve_entity_decisions(&mut self) { - let mut trigger_system = trigger_system::TriggerSystem {}; - let mut item_equip_system = inventory::ItemEquipSystem {}; - let mut item_use_system = inventory::ItemUseSystem {}; - let mut item_drop_system = inventory::ItemDropSystem {}; - let mut item_remove_system = inventory::ItemRemoveSystem {}; - let mut inventory_system = inventory::ItemCollectionSystem {}; - let mut item_id_system = inventory::ItemIdentificationSystem {}; - let mut key_system = inventory::KeyHandling {}; - let mut melee_system = MeleeCombatSystem {}; - trigger_system.run_now(&self.ecs); - inventory_system.run_now(&self.ecs); - item_equip_system.run_now(&self.ecs); - item_use_system.run_now(&self.ecs); - item_drop_system.run_now(&self.ecs); - item_remove_system.run_now(&self.ecs); - item_id_system.run_now(&self.ecs); - key_system.run_now(&self.ecs); - melee_system.run_now(&self.ecs); - - effects::run_effects_queue(&mut self.ecs); - } - - fn refresh_indexes(&mut self) { - let mut mapindex = spatial::MapIndexingSystem {}; - let mut vis = VisibilitySystem {}; - mapindex.run_now(&self.ecs); - vis.run_now(&self.ecs); - } - - fn run_ai(&mut self) { - let mut encumbrance_system = ai::EncumbranceSystem {}; // Must run first, as it affects energy regen. - let mut energy = ai::EnergySystem {}; // Figures out who deserves a turn. - let mut regen_system = ai::RegenSystem {}; // Restores HP on appropriate clock ticks. - let mut turn_status_system = ai::TurnStatusSystem {}; // Ticks statuses. Should anyone now lose their turn? i.e. confusion - let mut quip_system = ai::QuipSystem {}; // Quipping is "free". It doesn't use up a turn. - let mut adjacent_ai = ai::AdjacentAI {}; // AdjacentAI -> DefaultAI are all exclusive. If one acts, the entity's turn is over. - let mut visible_ai = ai::VisibleAI {}; - let mut approach_ai = ai::ApproachAI {}; - let mut flee_ai = ai::FleeAI {}; - let mut chase_ai = ai::ChaseAI {}; - let mut default_move_ai = ai::DefaultAI {}; - encumbrance_system.run_now(&self.ecs); - energy.run_now(&self.ecs); - regen_system.run_now(&self.ecs); - turn_status_system.run_now(&self.ecs); - quip_system.run_now(&self.ecs); - adjacent_ai.run_now(&self.ecs); - visible_ai.run_now(&self.ecs); - approach_ai.run_now(&self.ecs); - flee_ai.run_now(&self.ecs); - chase_ai.run_now(&self.ecs); - default_move_ai.run_now(&self.ecs); - } - - fn goto_id(&mut self, id: i32, dest_tile: TileType) { - // Freeze curr level - dungeon::freeze_entities(&mut self.ecs); - self.generate_world_map(id, dest_tile); - let mapname = self.ecs.fetch::().name.clone(); - gamelog::Logger - ::new() - .append("You head to") - .colour(rgb_to_u8(get_local_col(id))) - .append_n(&mapname) - .colour(WHITE) - .period() - .log(); - gamelog::record_event(EVENT::ChangedFloor(mapname)); - } - - fn game_over_cleanup(&mut self) { - // Delete everything - let mut to_delete = Vec::new(); - for e in self.ecs.entities().join() { - to_delete.push(e); - } - for del in to_delete.iter() { - self.ecs.delete_entity(*del).expect("Deletion failed"); - } - - // Spawn a new player and build new map - { - let player_entity = spawner::player(&mut self.ecs, 0, 0); - let mut player_entity_writer = self.ecs.write_resource::(); - *player_entity_writer = player_entity; - } - // Replace map list - self.ecs.insert(dungeon::MasterDungeonMap::new()); - self.generate_world_map(1, TileType::Floor); - - gamelog::setup_log(); - gamelog::record_event(EVENT::Level(1)); - } -} - -impl GameState for State { - fn tick(&mut self, ctx: &mut BTerm) { - let mut new_runstate; - { - let runstate = self.ecs.fetch::(); - new_runstate = *runstate; - } - // Clear screen - ctx.cls(); - particle_system::particle_ticker(&mut self.ecs, ctx); - - match new_runstate { - RunState::MainMenu { .. } => {} - RunState::CharacterCreation { .. } => {} - _ => { - // Draw map and ui - camera::render_camera(&self.ecs, ctx); - gui::draw_ui(&self.ecs, ctx); - } - } - - match new_runstate { - RunState::PreRun => { - self.run_systems(); - self.ecs.maintain(); - new_runstate = RunState::AwaitingInput; - } - RunState::AwaitingInput => { - // We refresh the index, and run anything that might - // still be in the queue, just to make 100% sure that - // there are no lingering effects from the last tick. - self.refresh_indexes(); - effects::run_effects_queue(&mut self.ecs); - // Sanity-checking that the player actually *should* - // be taking a turn before giving them one. If they - // don't have a turn component, go back to ticking. - let mut can_act = false; - { - let player_entity = self.ecs.fetch::(); - let turns = self.ecs.read_storage::(); - if let Some(_) = turns.get(*player_entity) { - can_act = true; - } - } - if can_act { - let on_overmap = self.ecs.fetch::().overmap; - new_runstate = player_input(self, ctx, on_overmap); - } else { - new_runstate = RunState::Ticking; - } - } - RunState::Ticking => { - while new_runstate == RunState::Ticking && particle_system::check_queue(&self.ecs) { - self.run_systems(); - self.ecs.maintain(); - try_spawn_interval(&mut self.ecs); - maybe_map_message(&mut self.ecs); - match *self.ecs.fetch::() { - RunState::AwaitingInput => { - new_runstate = RunState::AwaitingInput; - } - RunState::MagicMapReveal { row, cursed } => { - new_runstate = RunState::MagicMapReveal { row: row, cursed: cursed }; - } - RunState::ShowRemoveCurse => { - new_runstate = RunState::ShowRemoveCurse; - } - RunState::ShowIdentify => { - new_runstate = RunState::ShowIdentify; - } - _ => { - new_runstate = RunState::Ticking; - } - } - } - } - RunState::Farlook { .. } => { - let result = gui::show_farlook(self, ctx); - match result { - gui::FarlookResult::NoResponse { x, y } => { - new_runstate = RunState::Farlook { x, y }; - } - gui::FarlookResult::Cancel => { - new_runstate = RunState::AwaitingInput; - } - } - } - RunState::ShowCheatMenu => { - let result = gui::show_cheat_menu(self, ctx); - match result { - gui::CheatMenuResult::Cancel => { - new_runstate = RunState::AwaitingInput; - } - gui::CheatMenuResult::NoResponse => {} - gui::CheatMenuResult::Ascend => { - let id = self.ecs.fetch::().id - 1; - self.goto_id(id, TileType::DownStair); - self.mapgen_next_state = Some(RunState::PreRun); - new_runstate = RunState::MapGeneration; - } - gui::CheatMenuResult::Descend => { - let id = self.ecs.fetch::().id + 1; - self.goto_id(id, TileType::UpStair); - self.mapgen_next_state = Some(RunState::PreRun); - new_runstate = RunState::MapGeneration; - } - gui::CheatMenuResult::Heal => { - let player = self.ecs.fetch::(); - let mut pools = self.ecs.write_storage::(); - let mut player_pools = pools.get_mut(*player).unwrap(); - player_pools.hit_points.current = player_pools.hit_points.max; - new_runstate = RunState::AwaitingInput; - } - gui::CheatMenuResult::MagicMap => { - let mut map = self.ecs.fetch_mut::(); - for v in map.revealed_tiles.iter_mut() { - *v = true; - } - new_runstate = RunState::AwaitingInput; - } - gui::CheatMenuResult::GodMode => { - let player = self.ecs.fetch::(); - let mut pools = self.ecs.write_storage::(); - let mut player_pools = pools.get_mut(*player).unwrap(); - gamelog::Logger::new().append("TOGGLED GOD MODE!").log(); - player_pools.god = !player_pools.god; - new_runstate = RunState::AwaitingInput; - } - } - } - RunState::ShowInventory => { - let result = gui::show_inventory(self, ctx); - match result.0 { - gui::ItemMenuResult::Cancel => { - new_runstate = RunState::AwaitingInput; - } - gui::ItemMenuResult::NoResponse => {} - gui::ItemMenuResult::Selected => { - let item_entity = result.1.unwrap(); - let is_ranged = self.ecs.read_storage::(); - let ranged_item = is_ranged.get(item_entity); - if let Some(ranged_item) = ranged_item { - let is_aoe = self.ecs.read_storage::(); - let aoe_item = is_aoe.get(item_entity); - let (min_x, _max_x, min_y, _max_y, x_offset, y_offset) = - camera::get_screen_bounds(&self.ecs, ctx); - let ppos = self.ecs.fetch::(); - if let Some(aoe_item) = aoe_item { - new_runstate = RunState::ShowTargeting { - x: ppos.x + x_offset - min_x, - y: ppos.y + y_offset - min_y, - range: ranged_item.range, - item: item_entity, - aoe: aoe_item.radius, - }; - } else { - new_runstate = RunState::ShowTargeting { - x: ppos.x + x_offset - min_x, - y: ppos.y + y_offset - min_y, - range: ranged_item.range, - item: item_entity, - aoe: 0, - }; - } - } else { - let mut intent = self.ecs.write_storage::(); - intent - .insert(*self.ecs.fetch::(), WantsToUseItem { - item: item_entity, - target: None, - }) - .expect("Unable to insert intent."); - new_runstate = RunState::Ticking; - } - } - } - } - RunState::ShowDropItem => { - let result = gui::drop_item_menu(self, ctx); - match result.0 { - gui::ItemMenuResult::Cancel => { - new_runstate = RunState::AwaitingInput; - } - gui::ItemMenuResult::NoResponse => {} - gui::ItemMenuResult::Selected => { - let item_entity = result.1.unwrap(); - let mut removekey = self.ecs.write_storage::(); - let mut intent = self.ecs.write_storage::(); - removekey - .insert(item_entity, WantsToRemoveKey {}) - .expect("Unable to insert WantsToRemoveKey"); - intent - .insert(*self.ecs.fetch::(), WantsToDropItem { - item: item_entity, - }) - .expect("Unable to insert intent"); - new_runstate = RunState::Ticking; - } - } - } - RunState::ShowRemoveItem => { - let result = gui::remove_item_menu(self, ctx); - match result.0 { - gui::ItemMenuResult::Cancel => { - new_runstate = RunState::AwaitingInput; - } - gui::ItemMenuResult::NoResponse => {} - gui::ItemMenuResult::Selected => { - let item_entity = result.1.unwrap(); - let mut intent = self.ecs.write_storage::(); - intent - .insert(*self.ecs.fetch::(), WantsToRemoveItem { - item: item_entity, - }) - .expect("Unable to insert intent"); - new_runstate = RunState::Ticking; - } - } - } - RunState::ShowTargeting { x, y, range, item, aoe } => { - let result = gui::ranged_target(self, ctx, x, y, range, aoe); - match result.0 { - gui::TargetResult::Cancel => { - new_runstate = RunState::AwaitingInput; - } - gui::TargetResult::NoResponse { x, y } => { - new_runstate = RunState::ShowTargeting { x, y, range, item, aoe }; - } - gui::TargetResult::Selected => { - let mut intent = self.ecs.write_storage::(); - intent - .insert(*self.ecs.fetch::(), WantsToUseItem { - item, - target: result.1, - }) - .expect("Unable to insert intent."); - new_runstate = RunState::Ticking; - } - } - } - RunState::ShowRemoveCurse => { - let result = gui::remove_curse(self, ctx); - match result.0 { - gui::ItemMenuResult::Cancel => { - new_runstate = RunState::AwaitingInput; - } - gui::ItemMenuResult::NoResponse => {} - gui::ItemMenuResult::Selected => { - let item_entity = result.1.unwrap(); - self.ecs - .write_storage::() - .insert(item_entity, Beatitude { buc: BUC::Uncursed, known: true }) - .expect("Unable to insert beatitude"); - new_runstate = RunState::Ticking; - } - } - } - RunState::ShowIdentify => { - let result = gui::identify(self, ctx); - match result.0 { - gui::ItemMenuResult::Cancel => { - new_runstate = RunState::AwaitingInput; - } - gui::ItemMenuResult::NoResponse => {} - gui::ItemMenuResult::Selected => { - let item_entity = result.1.unwrap(); - if let Some(name) = self.ecs.read_storage::().get(item_entity) { - let mut dm = self.ecs.fetch_mut::(); - dm.identified_items.insert(name.name.clone()); - } - if - let Some(beatitude) = self.ecs - .write_storage::() - .get_mut(item_entity) - { - beatitude.known = true; - } - new_runstate = RunState::Ticking; - } - } - } - RunState::ActionWithDirection { function } => { - new_runstate = gui::get_input_direction(&mut self.ecs, ctx, function); - } - RunState::MainMenu { .. } => { - let result = gui::main_menu(self, ctx); - match result { - gui::MainMenuResult::NoSelection { selected } => { - new_runstate = RunState::MainMenu { menu_selection: selected }; - } - gui::MainMenuResult::Selected { selected } => - match selected { - gui::MainMenuSelection::NewGame => { - new_runstate = RunState::CharacterCreation { - ancestry: gui::Ancestry::Human, - class: gui::Class::Fighter, - }; - } - gui::MainMenuSelection::LoadGame => { - saveload_system::load_game(&mut self.ecs); - new_runstate = RunState::AwaitingInput; - saveload_system::delete_save(); - } - gui::MainMenuSelection::Quit => { - ::std::process::exit(0); - } - } - } - } - RunState::CharacterCreation { .. } => { - let result = gui::character_creation(self, ctx); - match result { - gui::CharCreateResult::NoSelection { ancestry, class } => { - new_runstate = RunState::CharacterCreation { ancestry, class }; - } - gui::CharCreateResult::Selected { ancestry, class } => { - if ancestry == gui::Ancestry::NULL { - new_runstate = RunState::MainMenu { - menu_selection: gui::MainMenuSelection::NewGame, - }; - } else { - gui::setup_player_ancestry(&mut self.ecs, ancestry); - gui::setup_player_class(&mut self.ecs, class, ancestry); - new_runstate = RunState::PreRun; - } - } - } - } - RunState::SaveGame => { - saveload_system::save_game(&mut self.ecs); - new_runstate = RunState::MainMenu { - menu_selection: gui::MainMenuSelection::LoadGame, - }; - } - RunState::GameOver => { - let result = gui::game_over(ctx); - let write_to_morgue: Option = match result { - gui::YesNoResult::NoSelection => None, - gui::YesNoResult::No => Some(false), - gui::YesNoResult::Yes => Some(true), - }; - if let Some(response) = write_to_morgue { - if response { - morgue::create_morgue_file(&self.ecs); - } - self.game_over_cleanup(); - new_runstate = RunState::MapGeneration; - self.mapgen_next_state = Some(RunState::MainMenu { - menu_selection: gui::MainMenuSelection::NewGame, - }); - } - } - RunState::GoToLevel(id, dest_tile) => { - self.goto_id(id, dest_tile); - self.mapgen_next_state = Some(RunState::PreRun); - new_runstate = RunState::MapGeneration; - } - RunState::HelpScreen => { - let result = gui::show_help(ctx); - match result { - gui::YesNoResult::Yes => { - gamelog::record_event(EVENT::LookedForHelp(1)); - new_runstate = RunState::AwaitingInput; - } - _ => {} - } - } - RunState::MagicMapReveal { row, cursed } => { - let mut map = self.ecs.fetch_mut::(); - - // Could probably toss this into a function somewhere, and/or - // have multiple simple animations for it. - for x in 0..map.width { - let idx; - if x % 2 == 0 { - idx = map.xy_idx(x as i32, row); - } else { - idx = map.xy_idx(x as i32, (map.height as i32) - 1 - row); - } - if !cursed { - map.revealed_tiles[idx] = true; - } else { - map.revealed_tiles[idx] = false; - } - } - - // Dirtify viewshed only if cursed, so our currently visible tiles aren't removed too - if cursed { - let player_entity = self.ecs.fetch::(); - let mut viewshed_components = self.ecs.write_storage::(); - let viewshed = viewshed_components.get_mut(*player_entity); - if let Some(viewshed) = viewshed { - viewshed.dirty = true; - } - } - - if (row as usize) == (map.height as usize) - 1 { - new_runstate = RunState::Ticking; - } else { - new_runstate = RunState::MagicMapReveal { row: row + 1, cursed: cursed }; - } - } - RunState::MapGeneration => { - if !config::CONFIG.logging.show_mapgen { - new_runstate = self.mapgen_next_state.unwrap(); - } - if self.mapgen_history.len() != 0 { - ctx.cls(); - camera::render_debug_map(&self.mapgen_history[self.mapgen_index], ctx); - - self.mapgen_timer += ctx.frame_time_ms; - if self.mapgen_timer > 300.0 { - self.mapgen_timer = 0.0; - self.mapgen_index += 1; - if self.mapgen_index >= self.mapgen_history.len() { - new_runstate = self.mapgen_next_state.unwrap(); - } - } - } - } - } - - { - let mut runwriter = self.ecs.write_resource::(); - *runwriter = new_runstate; - } - - damage_system::delete_the_dead(&mut self.ecs); - - let _ = render_draw_buffer(ctx); - } -} diff --git a/src/trigger_system.rs b/src/trigger_system.rs index 4c2c779..e951b1d 100644 --- a/src/trigger_system.rs +++ b/src/trigger_system.rs @@ -1,17 +1,7 @@ use super::{ - effects::{ add_effect, aoe_tiles, EffectType, Targets }, - gamelog, - gui::renderable_colour, - EntityMoved, - EntryTrigger, - Map, - Name, - Point, - Position, - Renderable, - AOE, + gamelog, Confusion, EntityMoved, EntryTrigger, Hidden, InflictsDamage, Map, Name, ParticleBuilder, Position, + SingleActivation, SufferDamage, }; -use bracket_lib::prelude::*; use specs::prelude::*; pub struct TriggerSystem {} @@ -23,55 +13,80 @@ impl<'a> System<'a> for TriggerSystem { WriteStorage<'a, EntityMoved>, ReadStorage<'a, Position>, ReadStorage<'a, EntryTrigger>, + ReadStorage<'a, InflictsDamage>, + WriteStorage<'a, Confusion>, + WriteStorage<'a, SufferDamage>, + WriteStorage<'a, Hidden>, + ReadStorage<'a, SingleActivation>, ReadStorage<'a, Name>, + WriteExpect<'a, ParticleBuilder>, Entities<'a>, - ReadStorage<'a, AOE>, - ReadStorage<'a, Renderable>, + WriteExpect<'a, rltk::RandomNumberGenerator>, ); fn run(&mut self, data: Self::SystemData) { - let (map, mut entity_moved, position, entry_trigger, names, entities, aoes, renderables) = - data; + let ( + map, + mut entity_moved, + position, + entry_trigger, + inflicts_damage, + mut confusion, + mut inflict_damage, + mut hidden, + single_activation, + names, + mut particle_builder, + entities, + mut rng, + ) = data; + + // Iterate entities that moved, and their final position + let mut remove_entities: Vec = Vec::new(); for (entity, mut _entity_moved, pos) in (&entities, &mut entity_moved, &position).join() { let idx = map.xy_idx(pos.x, pos.y); - crate::spatial::for_each_tile_content(idx, |entity_id| { - if entity != entity_id { - let maybe_trigger = entry_trigger.get(entity_id); + for entity_id in map.tile_content[idx].iter() { + if entity != *entity_id { + let maybe_trigger = entry_trigger.get(*entity_id); match maybe_trigger { None => {} Some(_trigger) => { - if map.visible_tiles[idx] == true { - if let Some(name) = names.get(entity_id) { - gamelog::Logger - ::new() - .append("The") - .colour(renderable_colour(&renderables, entity_id)) - .append(&name.name) - .colour(WHITE) - .append("triggers!") - .log(); - } + // Something on this pos had a trigger + let name = names.get(*entity_id); + hidden.remove(*entity_id); + if let Some(name) = name { + particle_builder.trap_triggered(pos.x, pos.y); + gamelog::Logger::new().item_name(&name.name).append("triggers!").log(); + } + + let damage = inflicts_damage.get(*entity_id); + if let Some(damage) = damage { + let damage_roll = rng.roll_dice(damage.n_dice, damage.sides) + damage.modifier; + particle_builder.damage_taken(pos.x, pos.y); + SufferDamage::new_damage(&mut inflict_damage, entity, damage_roll, false); + } + + let confuses = confusion.get(*entity_id); + if let Some(confuses) = confuses { + confusion + .insert(entity, Confusion { turns: confuses.turns }) + .expect("Unable to insert confusion"); + } + + let sa = single_activation.get(*entity_id); + if let Some(_sa) = sa { + remove_entities.push(*entity_id); } - add_effect( - Some(entity_id), - EffectType::TriggerFire { trigger: entity_id }, - if let Some(aoe) = aoes.get(entity_id) { - Targets::TileList { - targets: aoe_tiles( - &*map, - Point::new(pos.x, pos.y), - aoe.radius - ), - } - } else { - Targets::Tile { target: idx } - } - ); } } } - }); + } } + + for trap in remove_entities.iter() { + entities.delete(*trap).expect("Unable to delete trap"); + } + entity_moved.clear(); } } diff --git a/src/visibility_system.rs b/src/visibility_system.rs index e53b0e1..f313240 100644 --- a/src/visibility_system.rs +++ b/src/visibility_system.rs @@ -1,29 +1,13 @@ -use super::{ - gamelog, - Blind, - BlocksVisibility, - Hidden, - Map, - Name, - Player, - Position, - Telepath, - Viewshed, - Renderable, - gui::renderable_colour, -}; -use bracket_lib::prelude::*; -use bracket_lib::pathfinding::FieldOfViewAlg::SymmetricShadowcasting; +use super::{gamelog, BlocksVisibility, Hidden, Map, Name, Player, Position, Telepath, Viewshed}; +use rltk::{FieldOfViewAlg::SymmetricShadowcasting, Point}; use specs::prelude::*; pub struct VisibilitySystem {} -const BLIND_TELEPATHY_RANGE_MULTIPLIER: i32 = 3; - impl<'a> System<'a> for VisibilitySystem { type SystemData = ( WriteExpect<'a, Map>, - WriteExpect<'a, RandomNumberGenerator>, + WriteExpect<'a, rltk::RandomNumberGenerator>, Entities<'a>, WriteStorage<'a, Viewshed>, WriteStorage<'a, Telepath>, @@ -31,26 +15,12 @@ impl<'a> System<'a> for VisibilitySystem { ReadStorage<'a, Player>, WriteStorage<'a, Hidden>, ReadStorage<'a, Name>, - ReadStorage<'a, Blind>, ReadStorage<'a, BlocksVisibility>, - ReadStorage<'a, Renderable>, ); fn run(&mut self, data: Self::SystemData) { - let ( - mut map, - mut rng, - entities, - mut viewshed, - mut telepath, - pos, - player, - mut hidden, - names, - blind_entities, - blocks_visibility, - renderables, - ) = data; + let (mut map, mut rng, entities, mut viewshed, mut telepath, pos, player, mut hidden, names, blocks_visibility) = + data; map.view_blocked.clear(); for (block_pos, _block) in (&pos, &blocks_visibility).join() { @@ -61,20 +31,15 @@ impl<'a> System<'a> for VisibilitySystem { for (ent, viewshed, pos) in (&entities, &mut viewshed, &pos).join() { if viewshed.dirty { viewshed.dirty = false; - let range = if let Some(_is_blind) = blind_entities.get(ent) { - 1 - } else { - if map.overmap { viewshed.range / 2 } else { viewshed.range } - }; let origin = Point::new(pos.x, pos.y); - viewshed.visible_tiles = SymmetricShadowcasting.field_of_view(origin, range, &*map); + viewshed.visible_tiles = SymmetricShadowcasting.field_of_view(origin, viewshed.range, &*map); viewshed.visible_tiles.retain(|p| { - p.x >= 0 && - p.x < map.width && - p.y >= 0 && - p.y < map.height && - (map.lit_tiles[map.xy_idx(p.x, p.y)] == true || - DistanceAlg::Pythagoras.distance2d(Point::new(p.x, p.y), origin) < 1.5) + p.x >= 0 + && p.x < map.width + && p.y >= 0 + && p.y < map.height + && (map.lit_tiles[map.xy_idx(p.x, p.y)] == true + || rltk::DistanceAlg::Pythagoras.distance2d(Point::new(p.x, p.y), origin) < 1.5) }); // If this is the player, reveal what they can see @@ -89,25 +54,22 @@ impl<'a> System<'a> for VisibilitySystem { map.visible_tiles[idx] = true; // Reveal hidden things - crate::spatial::for_each_tile_content(idx, |e| { - let is_hidden = hidden.get(e); + for thing in map.tile_content[idx].iter() { + let is_hidden = hidden.get(*thing); if let Some(_is_hidden) = is_hidden { if rng.roll_dice(1, 12) == 1 { - let name = names.get(e); + let name = names.get(*thing); if let Some(name) = name { - gamelog::Logger - ::new() + gamelog::Logger::new() .append("You spot a") - .colour(renderable_colour(&renderables, e)) - .append_n(&name.name) - .colour(WHITE) + .item_name_n(&name.name) .period() .log(); } - hidden.remove(e); + hidden.remove(*thing); } } - }); + } } } } @@ -116,14 +78,9 @@ impl<'a> System<'a> for VisibilitySystem { for (ent, telepath, pos) in (&entities, &mut telepath, &pos).join() { if telepath.dirty { telepath.dirty = false; - let mut range = telepath.range; - if let Some(_is_blind) = blind_entities.get(ent) { - range *= BLIND_TELEPATHY_RANGE_MULTIPLIER; - } - telepath.telepath_tiles = fast_fov(pos.x, pos.y, range); - telepath.telepath_tiles.retain( - |p| p.x >= 0 && p.x < map.width && p.y >= 0 && p.y < map.height - ); + + telepath.telepath_tiles = fast_fov(pos.x, pos.y, telepath.range); + telepath.telepath_tiles.retain(|p| p.x >= 0 && p.x < map.width && p.y >= 0 && p.y < map.height); // If this is the player, reveal what they can see let _p: Option<&Player> = player.get(ent); @@ -146,11 +103,11 @@ pub fn fast_fov(p_x: i32, p_y: i32, r: i32) -> Vec { let mut i = 0; while i <= 360 { - let x: f32 = f32::cos((i as f32) * (0.01745 as f32)); - let y: f32 = f32::sin((i as f32) * (0.01745 as f32)); + let x: f32 = f32::cos(i as f32 * 0.01745 as f32); + let y: f32 = f32::sin(i as f32 * 0.01745 as f32); - let mut ox: f32 = (p_x as f32) + (0.5 as f32); - let mut oy: f32 = (p_y as f32) + (0.5 as f32); + let mut ox: f32 = p_x as f32 + 0.5 as f32; + let mut oy: f32 = p_y as f32 + 0.5 as f32; for _i in 0..r { visible_tiles.push(Point::new(ox as i32, oy as i32)); ox += x; diff --git a/tests/components_test.rs b/tests/components_test.rs deleted file mode 100644 index 1581f90..0000000 --- a/tests/components_test.rs +++ /dev/null @@ -1,47 +0,0 @@ -// tests/components_test.rs -use rust_rl::components::*; -use std::collections::HashMap; - -#[test] -fn damagetype_equality() { - let dt1 = DamageType::Physical; - let dt2 = DamageType::Physical; - assert_eq!(dt1, dt2); - let dt3 = DamageType::Magic; - assert_ne!(dt1, dt3); -} - -#[test] -fn damagetype_ismagic() { - let dt1 = DamageType::Physical; - let dt2 = DamageType::Magic; - assert!(!dt1.is_magic()); - assert!(dt2.is_magic()); -} - -#[test] -fn get_damage_modifiers() { - let dm = HasDamageModifiers { - modifiers: { - let mut m = HashMap::new(); - m.insert(DamageType::Physical, DamageModifier::Weakness); - m.insert(DamageType::Magic, DamageModifier::Resistance); - m - }, - }; - assert_eq!(dm.modifier(&DamageType::Physical), &DamageModifier::Weakness); - assert_eq!(dm.modifier(&DamageType::Magic), &DamageModifier::Resistance); - assert_ne!(dm.modifier(&DamageType::Forced), &DamageModifier::Immune); -} - -#[test] -fn get_damage_modifier_multiplier() { - let none_mod = &DamageModifier::None.multiplier(); - let weak_mod = &DamageModifier::Weakness.multiplier(); - let res_mod = &DamageModifier::Resistance.multiplier(); - let immune_mod = &DamageModifier::Immune.multiplier(); - assert_eq!(none_mod, &1.0); - assert_eq!(weak_mod, &2.0); - assert_eq!(res_mod, &0.5); - assert_eq!(immune_mod, &0.0); -} diff --git a/tests/gamelog_test.rs b/tests/gamelog_test.rs deleted file mode 100644 index 0e18d44..0000000 --- a/tests/gamelog_test.rs +++ /dev/null @@ -1,43 +0,0 @@ -// tests/gamelog_test.rs -use rust_rl::gamelog::*; -use rust_rl::data::events::*; -use lazy_static::lazy_static; -use std::sync::Mutex; - -// To ensure this test module uses a single thread. -lazy_static! { - static ref SINGLE_THREAD: Mutex<()> = Mutex::new(()); -} - -#[test] -fn recording_event() { - let _lock = SINGLE_THREAD.lock(); - clear_events(); - record_event(EVENT::Turn(1)); - record_event(EVENT::Turn(0)); - record_event(EVENT::Turn(-1)); - record_event(EVENT::Killed("mob".to_string())); -} - -#[test] -fn getting_event_count() { - let _lock = SINGLE_THREAD.lock(); - clear_events(); - record_event(EVENT::Turn(1)); - assert_eq!(get_event_count(EVENT::COUNT_TURN), 1); - record_event(EVENT::Turn(3)); - assert_eq!(get_event_count(EVENT::COUNT_TURN), 4); - clear_events(); - assert_eq!(get_event_count(EVENT::COUNT_TURN), 0); -} - -#[test] -fn cloning_events() { - let _lock = SINGLE_THREAD.lock(); - clear_events(); - record_event(EVENT::Level(1)); - record_event(EVENT::Turn(5)); - record_event(EVENT::Identified("item".to_string())); - let cloned_events = clone_events(); - assert_eq!(EVENTS.lock().unwrap().clone(), cloned_events); -} diff --git a/tests/map_test.rs b/tests/map_test.rs deleted file mode 100644 index 336c4d8..0000000 --- a/tests/map_test.rs +++ /dev/null @@ -1,85 +0,0 @@ -// tests/map_test.rs -use rust_rl::map::*; -use std::collections::HashSet; - -#[test] -fn map_settings() { - let (overmap, id, width, height, difficulty, name, short_name, depth) = ( - false, - 0, - 80, - 50, - 0, - "Test Map", - "Test Map", - 0, - ); - let map = Map::new(overmap, id, width, height, difficulty, name, short_name, depth); - - assert_eq!(map.overmap, overmap); - assert_eq!(map.id, id); - assert_eq!(map.width, width); - assert_eq!(map.height, height); - assert_eq!(map.difficulty, difficulty); - assert_eq!(map.name, name); - assert_eq!(map.short_name, short_name); - assert_eq!(map.depth, depth); - assert_eq!(map.tiles.len(), (width * height) as usize); - assert_eq!(map.messages, HashSet::new()); -} - -#[test] -fn tiletype_equality() { - let tile1 = TileType::ImpassableMountain; - let tile2 = TileType::ImpassableMountain; - assert_eq!(tile1, tile2); - - let tile3 = TileType::Floor; - assert_ne!(tile1, tile3); -} - -#[test] -fn tiletype_with_var_equality() { - let tile1 = TileType::ToLocal(5); - let tile2 = TileType::ToLocal(3); - assert_ne!(tile1, tile2); - - let tile3 = TileType::ToLocal(3); - assert_eq!(tile2, tile3); -} - -fn init_maps_for_tests() -> (MasterDungeonMap, Map, Map) { - let dm = MasterDungeonMap::new(); - let (overmap, difficulty, name, short_name, depth) = (false, 0, "Test Map", "Test Map", 0); - let map1 = Map::new(overmap, 1, 64, 64, difficulty, name, short_name, depth); - let map2 = Map::new(overmap, 2, 128, 128, difficulty, name, short_name, depth); - (dm, map1, map2) -} - -#[test] -fn map_saving() { - let (mut dm, map1, map2) = init_maps_for_tests(); - dm.store_map(&map1); - dm.store_map(&map2); -} - -#[test] -fn map_loading() { - let (mut dm, map1, map2) = init_maps_for_tests(); - dm.store_map(&map1); - let loaded_map1 = dm.get_map(map1.id).unwrap(); - assert_eq!(loaded_map1.overmap, map1.overmap); - assert_eq!(loaded_map1.id, map1.id); - assert_eq!(loaded_map1.width, map1.width); - assert_eq!(loaded_map1.height, map1.height); - assert_eq!(loaded_map1.difficulty, map1.difficulty); - assert_eq!(loaded_map1.name, map1.name); - assert_eq!(loaded_map1.short_name, map1.short_name); - assert_eq!(loaded_map1.depth, map1.depth); - assert_eq!(loaded_map1.tiles.len(), map1.tiles.len()); - assert_eq!(loaded_map1.messages, map1.messages); - dm.store_map(&map2); - let loaded_map2 = dm.get_map(map2.id).unwrap(); - assert_eq!(loaded_map2.width, map2.width); - assert_ne!(loaded_map2.width, map1.width); -} diff --git a/tests/mod.rs b/tests/mod.rs deleted file mode 100644 index e1561c3..0000000 --- a/tests/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -// tests/mod.rs -mod map_test; -mod gamelog_test; -mod components_test; diff --git a/wasm/index.css b/wasm/index.css new file mode 100644 index 0000000..2a5cbb4 --- /dev/null +++ b/wasm/index.css @@ -0,0 +1,23 @@ +html { + display: table; + margin: auto; + background-color: black; +} + +body { + display: flex; + justify-content: center; + align-items: center; + text-align: center; + min-height: 95vh; +} + +figure { + display: inline-flex; + border: 1px solid goldenrod; +} + +canvas { + background-color: black; + border: 4px solid darkgreen; +} \ No newline at end of file diff --git a/wasm/index.html b/wasm/index.html new file mode 100644 index 0000000..a237547 --- /dev/null +++ b/wasm/index.html @@ -0,0 +1,19 @@ + + + + + + Home Page + + +
+ +
+ + + + diff --git a/wasm/rust-rl.js b/wasm/rust-llyrl.js similarity index 95% rename from wasm/rust-rl.js rename to wasm/rust-llyrl.js index ffbe20e..aa2dad2 100644 --- a/wasm/rust-rl.js +++ b/wasm/rust-llyrl.js @@ -36,7 +36,9 @@ function addHeapObject(obj) { return idx; } -let WASM_VECTOR_LEN = 0; +const cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } ); + +if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); }; let cachedUint8Memory0 = null; @@ -47,70 +49,9 @@ function getUint8Memory0() { return cachedUint8Memory0; } -const cachedTextEncoder = (typeof TextEncoder !== 'undefined' ? new TextEncoder('utf-8') : { encode: () => { throw Error('TextEncoder not available') } } ); - -const encodeString = (typeof cachedTextEncoder.encodeInto === 'function' - ? function (arg, view) { - return cachedTextEncoder.encodeInto(arg, view); -} - : function (arg, view) { - const buf = cachedTextEncoder.encode(arg); - view.set(buf); - return { - read: arg.length, - written: buf.length - }; -}); - -function passStringToWasm0(arg, malloc, realloc) { - - if (realloc === undefined) { - const buf = cachedTextEncoder.encode(arg); - const ptr = malloc(buf.length, 1) >>> 0; - getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf); - WASM_VECTOR_LEN = buf.length; - return ptr; - } - - let len = arg.length; - let ptr = malloc(len, 1) >>> 0; - - const mem = getUint8Memory0(); - - let offset = 0; - - for (; offset < len; offset++) { - const code = arg.charCodeAt(offset); - if (code > 0x7F) break; - mem[ptr + offset] = code; - } - - if (offset !== len) { - if (offset !== 0) { - arg = arg.slice(offset); - } - ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0; - const view = getUint8Memory0().subarray(ptr + offset, ptr + len); - const ret = encodeString(arg, view); - - offset += ret.written; - } - - WASM_VECTOR_LEN = offset; - return ptr; -} - -function isLikeNone(x) { - return x === undefined || x === null; -} - -let cachedInt32Memory0 = null; - -function getInt32Memory0() { - if (cachedInt32Memory0 === null || cachedInt32Memory0.byteLength === 0) { - cachedInt32Memory0 = new Int32Array(wasm.memory.buffer); - } - return cachedInt32Memory0; +function getStringFromWasm0(ptr, len) { + ptr = ptr >>> 0; + return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); } function debugString(val) { @@ -178,13 +119,68 @@ function debugString(val) { return className; } -const cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } ); +let WASM_VECTOR_LEN = 0; -if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); }; +const cachedTextEncoder = (typeof TextEncoder !== 'undefined' ? new TextEncoder('utf-8') : { encode: () => { throw Error('TextEncoder not available') } } ); -function getStringFromWasm0(ptr, len) { - ptr = ptr >>> 0; - return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); +const encodeString = (typeof cachedTextEncoder.encodeInto === 'function' + ? function (arg, view) { + return cachedTextEncoder.encodeInto(arg, view); +} + : function (arg, view) { + const buf = cachedTextEncoder.encode(arg); + view.set(buf); + return { + read: arg.length, + written: buf.length + }; +}); + +function passStringToWasm0(arg, malloc, realloc) { + + if (realloc === undefined) { + const buf = cachedTextEncoder.encode(arg); + const ptr = malloc(buf.length, 1) >>> 0; + getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf); + WASM_VECTOR_LEN = buf.length; + return ptr; + } + + let len = arg.length; + let ptr = malloc(len, 1) >>> 0; + + const mem = getUint8Memory0(); + + let offset = 0; + + for (; offset < len; offset++) { + const code = arg.charCodeAt(offset); + if (code > 0x7F) break; + mem[ptr + offset] = code; + } + + if (offset !== len) { + if (offset !== 0) { + arg = arg.slice(offset); + } + ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0; + const view = getUint8Memory0().subarray(ptr + offset, ptr + len); + const ret = encodeString(arg, view); + + offset += ret.written; + } + + WASM_VECTOR_LEN = offset; + return ptr; +} + +let cachedInt32Memory0 = null; + +function getInt32Memory0() { + if (cachedInt32Memory0 === null || cachedInt32Memory0.byteLength === 0) { + cachedInt32Memory0 = new Int32Array(wasm.memory.buffer); + } + return cachedInt32Memory0; } function makeMutClosure(arg0, arg1, dtor, f) { @@ -212,11 +208,11 @@ function makeMutClosure(arg0, arg1, dtor, f) { return real; } function __wbg_adapter_20(arg0, arg1) { - wasm._dyn_core__ops__function__FnMut_____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__hb6c6b1cd103d974c(arg0, arg1); + wasm._dyn_core__ops__function__FnMut_____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h648b303b06146952(arg0, arg1); } function __wbg_adapter_23(arg0, arg1, arg2) { - wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h02b9f16709be0849(arg0, arg1, addHeapObject(arg2)); + wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__h254fddd5612fbaed(arg0, arg1, addHeapObject(arg2)); } function handleError(f, args) { @@ -232,6 +228,10 @@ function getArrayU8FromWasm0(ptr, len) { return getUint8Memory0().subarray(ptr / 1, ptr / 1 + len); } +function isLikeNone(x) { + return x === undefined || x === null; +} + async function __wbg_load(module, imports) { if (typeof Response === 'function' && module instanceof Response) { if (typeof WebAssembly.instantiateStreaming === 'function') { @@ -278,7 +278,7 @@ function __wbg_get_imports() { imports.wbg.__wbindgen_object_drop_ref = function(arg0) { takeObject(arg0); }; - imports.wbg.__wbg_log_0e24d345b14995ec = function(arg0, arg1) { + imports.wbg.__wbg_log_b09521c515df0f23 = function(arg0, arg1) { console.log(getStringFromWasm0(arg0, arg1)); }; imports.wbg.__wbindgen_object_clone_ref = function(arg0) { @@ -307,19 +307,45 @@ function __wbg_get_imports() { wasm.__wbindgen_free(deferred0_0, deferred0_1, 1); } }; - imports.wbg.__wbindgen_string_get = function(arg0, arg1) { - const obj = getObject(arg1); - const ret = typeof(obj) === 'string' ? obj : undefined; - var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); - var len1 = WASM_VECTOR_LEN; - getInt32Memory0()[arg0 / 4 + 1] = len1; - getInt32Memory0()[arg0 / 4 + 0] = ptr1; + imports.wbg.__wbindgen_string_new = function(arg0, arg1) { + const ret = getStringFromWasm0(arg0, arg1); + return addHeapObject(ret); }; imports.wbg.__wbindgen_boolean_get = function(arg0) { const v = getObject(arg0); const ret = typeof(v) === 'boolean' ? (v ? 1 : 0) : 2; return ret; }; + imports.wbg.__wbg_randomFillSync_bf67eeddb65b346b = function() { return handleError(function (arg0, arg1, arg2) { + getObject(arg0).randomFillSync(getArrayU8FromWasm0(arg1, arg2)); + }, arguments) }; + imports.wbg.__wbg_getRandomValues_f6c9b08ef5448767 = function() { return handleError(function (arg0, arg1) { + getObject(arg0).getRandomValues(getObject(arg1)); + }, arguments) }; + imports.wbg.__wbg_self_1c2814d86e6e51e3 = function() { return handleError(function () { + const ret = self.self; + return addHeapObject(ret); + }, arguments) }; + imports.wbg.__wbg_crypto_70532d614bc7e028 = function(arg0) { + const ret = getObject(arg0).crypto; + return addHeapObject(ret); + }; + imports.wbg.__wbg_msCrypto_4e9b4dd0e1abade6 = function(arg0) { + const ret = getObject(arg0).msCrypto; + return addHeapObject(ret); + }; + imports.wbg.__wbindgen_is_undefined = function(arg0) { + const ret = getObject(arg0) === undefined; + return ret; + }; + imports.wbg.__wbg_static_accessor_MODULE_7781e47b50010688 = function() { + const ret = module; + return addHeapObject(ret); + }; + imports.wbg.__wbg_require_9ace3ae680954e98 = function() { return handleError(function (arg0, arg1, arg2) { + const ret = getObject(arg0).require(getStringFromWasm0(arg1, arg2)); + return addHeapObject(ret); + }, arguments) }; imports.wbg.__wbg_instanceof_WebGl2RenderingContext_f921526c513bf717 = function(arg0) { let result; try { @@ -413,10 +439,6 @@ function __wbg_get_imports() { const ret = getObject(arg0).getExtension(getStringFromWasm0(arg1, arg2)); return isLikeNone(ret) ? 0 : addHeapObject(ret); }, arguments) }; - imports.wbg.__wbg_getParameter_55b36a787dbbfb74 = function() { return handleError(function (arg0, arg1) { - const ret = getObject(arg0).getParameter(arg1 >>> 0); - return addHeapObject(ret); - }, arguments) }; imports.wbg.__wbg_getProgramInfoLog_b81bc53188e286fa = function(arg0, arg1, arg2) { const ret = getObject(arg1).getProgramInfoLog(getObject(arg2)); var ptr1 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc); @@ -439,10 +461,6 @@ function __wbg_get_imports() { const ret = getObject(arg0).getShaderParameter(getObject(arg1), arg2 >>> 0); return addHeapObject(ret); }; - imports.wbg.__wbg_getSupportedExtensions_fafc31aab913037d = function(arg0) { - const ret = getObject(arg0).getSupportedExtensions(); - return isLikeNone(ret) ? 0 : addHeapObject(ret); - }; imports.wbg.__wbg_getUniformLocation_9f6eb60c560a347b = function(arg0, arg1, arg2, arg3) { const ret = getObject(arg0).getUniformLocation(getObject(arg1), getStringFromWasm0(arg2, arg3)); return isLikeNone(ret) ? 0 : addHeapObject(ret); @@ -686,48 +704,14 @@ function __wbg_get_imports() { const ret = getObject(arg0).createVertexArrayOES(); return isLikeNone(ret) ? 0 : addHeapObject(ret); }; - imports.wbg.__wbg_self_1c2814d86e6e51e3 = function() { return handleError(function () { - const ret = self.self; - return addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbg_crypto_70532d614bc7e028 = function(arg0) { - const ret = getObject(arg0).crypto; - return addHeapObject(ret); - }; - imports.wbg.__wbg_msCrypto_4e9b4dd0e1abade6 = function(arg0) { - const ret = getObject(arg0).msCrypto; - return addHeapObject(ret); - }; - imports.wbg.__wbindgen_is_undefined = function(arg0) { - const ret = getObject(arg0) === undefined; - return ret; - }; - imports.wbg.__wbg_static_accessor_MODULE_7781e47b50010688 = function() { - const ret = module; - return addHeapObject(ret); - }; - imports.wbg.__wbg_require_9ace3ae680954e98 = function() { return handleError(function (arg0, arg1, arg2) { - const ret = getObject(arg0).require(getStringFromWasm0(arg1, arg2)); - return addHeapObject(ret); - }, arguments) }; - imports.wbg.__wbg_getRandomValues_f6c9b08ef5448767 = function() { return handleError(function (arg0, arg1) { - getObject(arg0).getRandomValues(getObject(arg1)); - }, arguments) }; - imports.wbg.__wbg_randomFillSync_bf67eeddb65b346b = function() { return handleError(function (arg0, arg1, arg2) { - getObject(arg0).randomFillSync(getArrayU8FromWasm0(arg1, arg2)); - }, arguments) }; - imports.wbg.__wbg_get_44be0491f933a435 = function(arg0, arg1) { - const ret = getObject(arg0)[arg1 >>> 0]; - return addHeapObject(ret); - }; - imports.wbg.__wbg_length_fff51ee6522a1a18 = function(arg0) { - const ret = getObject(arg0).length; - return ret; - }; imports.wbg.__wbg_newnoargs_581967eacc0e2604 = function(arg0, arg1) { const ret = new Function(getStringFromWasm0(arg0, arg1)); return addHeapObject(ret); }; + imports.wbg.__wbg_get_97b561fb56f034b5 = function() { return handleError(function (arg0, arg1) { + const ret = Reflect.get(getObject(arg0), getObject(arg1)); + return addHeapObject(ret); + }, arguments) }; imports.wbg.__wbg_call_cb65541d95d71282 = function() { return handleError(function (arg0, arg1) { const ret = getObject(arg0).call(getObject(arg1)); return addHeapObject(ret); @@ -817,16 +801,16 @@ function __wbg_get_imports() { const ret = wasm.memory; return addHeapObject(ret); }; - imports.wbg.__wbindgen_closure_wrapper258 = function(arg0, arg1, arg2) { - const ret = makeMutClosure(arg0, arg1, 15, __wbg_adapter_20); + imports.wbg.__wbindgen_closure_wrapper1814 = function(arg0, arg1, arg2) { + const ret = makeMutClosure(arg0, arg1, 374, __wbg_adapter_20); return addHeapObject(ret); }; - imports.wbg.__wbindgen_closure_wrapper2954 = function(arg0, arg1, arg2) { - const ret = makeMutClosure(arg0, arg1, 696, __wbg_adapter_23); + imports.wbg.__wbindgen_closure_wrapper2295 = function(arg0, arg1, arg2) { + const ret = makeMutClosure(arg0, arg1, 457, __wbg_adapter_23); return addHeapObject(ret); }; - imports.wbg.__wbindgen_closure_wrapper2956 = function(arg0, arg1, arg2) { - const ret = makeMutClosure(arg0, arg1, 696, __wbg_adapter_23); + imports.wbg.__wbindgen_closure_wrapper2297 = function(arg0, arg1, arg2) { + const ret = makeMutClosure(arg0, arg1, 457, __wbg_adapter_23); return addHeapObject(ret); }; diff --git a/wasm/rust-llyrl_bg.wasm b/wasm/rust-llyrl_bg.wasm new file mode 100644 index 0000000..ee3dc12 Binary files /dev/null and b/wasm/rust-llyrl_bg.wasm differ diff --git a/wasm/rust-rl_bg.wasm b/wasm/rust-rl_bg.wasm deleted file mode 100644 index f48ccb7..0000000 Binary files a/wasm/rust-rl_bg.wasm and /dev/null differ