93 Commits

Author SHA1 Message Date
441072811e Update Rust crate serde_json to v1.0.140
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-03-04 02:22:19 +00:00
f12f007b28 Update Rust crate serde_json to v1.0.139
All checks were successful
continuous-integration/drone/push Build is passing
2025-02-20 07:35:58 +01:00
02b0a19aa4 Update Rust crate serde to v1.0.218
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-02-20 06:16:35 +00:00
675164df57 Update Rust crate serde_json to v1.0.138
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-01-29 02:18:11 +00:00
b6b97bf11f Update Rust crate serde_json to v1.0.137
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-01-20 02:08:46 +00:00
1d991ddaf5 Update Rust crate serde_json to v1.0.136
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-01-19 02:12:11 +00:00
2b12ab95e6 Update Rust crate serde_json to v1.0.135
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2025-01-07 02:03:02 +00:00
5720200e32 Update Rust crate serde to v1.0.217
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-12-28 02:03:57 +00:00
86636b1d89 Update Rust crate serde_json to v1.0.134
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-12-22 01:54:09 +00:00
697e2b765c Update Rust crate serde to v1.0.216
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-12-11 02:23:00 +00:00
594583f7f1 Update Rust crate serde_json to v1.0.133
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-11-17 01:56:47 +00:00
77ed37f853 Update Rust crate serde to v1.0.215
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-11-12 01:55:04 +00:00
308630d306 Update Rust crate serde to v1.0.214
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-10-29 02:14:12 +00:00
c389a0806a Update Rust crate serde to v1.0.213
Some checks are pending
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is pending
2024-10-23 01:17:49 +00:00
cdfebbec7b Update Rust crate serde_json to v1.0.132
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-10-20 01:11:47 +00:00
7296368bf4 Update Rust crate serde_json to v1.0.131
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-10-19 01:20:50 +00:00
6b3593a2f5 Update Rust crate serde to v1.0.210
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-10-18 05:00:14 +00:00
71d1a31d0f Update Rust crate serde_json to v1.0.129
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-10-18 01:10:18 +00:00
a47fd370d7 Update Rust crate serde_json to v1.0.125
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-08-21 23:04:15 +00:00
0fa1670aa0 Update Rust crate serde to v1.0.208
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-08-21 21:38:05 +00:00
a5988c9ce3 Update Rust crate serde to v1.0.203
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-05-25 18:19:17 +00:00
e64d6efd98 Update Rust crate serde to ^1.0.202
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-05-15 08:24:47 +00:00
de56c96884 Update all dependencies
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-05-08 01:08:00 +00:00
bcb749682f Update Rust crate serde to ^1.0.200
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-05-01 16:29:02 +00:00
c331b04f0e Update Rust crate serde to ^1.0.198
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-04-16 21:51:35 +00:00
3210ac3389 Update Rust crate serde_json to 1.0.116
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-04-16 05:59:08 +00:00
bf31ef75f6 Update Rust crate serde_json to 1.0.115
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-03-26 07:03:18 +00:00
a2cb3d7166 Update all dependencies
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-02-20 01:11:45 +00:00
d85910bd20 Update Rust crate serde_json to 1.0.113
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-01-29 04:31:54 +00:00
977b40d5e8 Update all dependencies
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-01-26 22:14:09 +00:00
05db5b566f Update Rust crate serde to ^1.0.195
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-01-06 03:25:47 +00:00
d5c423e94f Update Rust crate serde_json to 1.0.111
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-01-04 07:45:48 +00:00
174ebb3aa5 Update all dependencies
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-01-02 07:56:32 +00:00
15774db12d Update Rust crate serde_json to 1.0.109
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2024-01-01 01:19:26 +00:00
390406145f Update Rust crate serde to ^1.0.193
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-11-21 02:14:10 +00:00
08e63fc99a Update Rust crate serde_json to 1.0.108
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-10-30 16:16:47 +00:00
fa8ced3b05 Update Rust crate serde to ^1.0.190
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-10-26 03:41:48 +00:00
e60e895fe3 Update Rust crate serde to ^1.0.189
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-10-13 02:54:52 +00:00
2123b32615 Update Rust crate specs to 0.20.0
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-09-25 03:44:26 +00:00
cd14b20f2e Update Rust crate serde_json to 1.0.107
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-09-13 23:40:01 +00:00
36ffbfed1d Update Rust crate serde_json to 1.0.106
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-09-09 19:34:59 +00:00
10c836d987 Update Rust crate serde to ^1.0.188
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-08-26 02:59:20 +00:00
e98521c1c4 Update Rust crate serde to ^1.0.187
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-08-25 18:14:00 +00:00
ffecdba71d Update Rust crate serde to ^1.0.186
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-08-24 00:43:22 +00:00
269135318c Update Rust crate serde to ^1.0.185
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-08-21 05:04:51 +00:00
56d6d88ef3 Update Rust crate serde_json to 1.0.105
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-08-15 22:40:33 +00:00
91dac008dc Update Rust crate serde to ^1.0.183
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-08-07 04:30:32 +00:00
8fba60a5e8 Update Rust crate serde to ^1.0.182
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-08-06 06:03:30 +00:00
aa596b8b81 Update Rust crate serde to ^1.0.181
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-08-04 00:42:09 +00:00
e3561d3fe2 Update Rust crate serde to ^1.0.180
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-07-31 19:05:05 +00:00
c1d19fa106 Update Rust crate serde to ^1.0.179
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-07-31 00:41:35 +00:00
f7df41cbeb Update Rust crate serde to ^1.0.178
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-07-28 23:44:37 +00:00
d4654d8113 Update Rust crate serde to ^1.0.177
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-07-27 18:24:54 +00:00
0dda955141 Update Rust crate serde to ^1.0.176
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-07-26 21:04:07 +00:00
b1e06dec5d Update Rust crate serde_json to 1.0.104
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-07-26 19:52:26 +00:00
0bbfe859a3 Update all dependencies
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-07-24 03:57:15 +00:00
1f491c017c Merge pull request 'Update Rust crate serde_json to 1.0.100' (#29) from renovate/all into main
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-04 20:51:53 +00:00
6accff6ca5 Merge pull request 'Update Rust crate serde to ^1.0.166' (#28) from renovate/all into main
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-03 19:13:26 +00:00
7a5341a986 Merge pull request 'Update Rust crate serde to ^1.0.165' (#27) from renovate/all into main
All checks were successful
continuous-integration/drone/push Build is passing
2023-07-03 12:08:59 +00:00
212f8d870d Merge pull request 'Update all dependencies' (#26) from renovate/all into main
All checks were successful
continuous-integration/drone/push Build is passing
2023-06-24 05:23:12 +00:00
41b8407f5d Update Rust crate serde to ^1.0.162
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-05-05 08:18:48 +00:00
e85057ebfc Update Rust crate serde_json to 1.0.96
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-04-13 00:12:13 +00:00
02d92d928b Update Rust crate serde to ^1.0.160
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-04-11 06:20:51 +00:00
b918f88db5 Update Rust crate serde to ^1.0.159
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-03-28 06:34:53 +00:00
858d557a3b Update Rust crate serde_json to 1.0.95
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-03-27 17:30:52 +00:00
84fef1c758 Update Rust crate serde to ^1.0.158
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-03-20 17:44:19 +00:00
62c225b5dd Update Rust crate serde to ^1.0.157
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-03-18 10:09:19 +00:00
9c98b848f5 Update Rust crate serde to ^1.0.156
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-03-14 17:34:52 +00:00
e2dbdc1bbf Update Rust crate serde to ^1.0.154
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-03-08 20:39:19 +00:00
f91daaf46a Update Rust crate serde to ^1.0.153
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-03-07 19:26:47 +00:00
931cf083e8 Update Rust crate serde_json to 1.0.94
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2023-03-05 18:35:24 +00:00
d4a6d9aef8 Update Rust crate serde to ^1.0.152
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2022-12-26 18:03:49 +00:00
c03cd1208c Update Rust crate serde_json to 1.0.91
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2022-12-18 17:43:19 +00:00
99941e701b Update all dependencies
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2022-12-18 11:22:22 +00:00
bf80ced3e0 Update Rust crate serde to ^1.0.150
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2022-12-12 00:40:19 +00:00
a8e53b6e73 Update Rust crate serde to ^1.0.148
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2022-12-01 19:34:50 +00:00
e35a8b14d6 Update Rust crate serde_json to 1.0.89
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2022-11-22 14:39:29 +00:00
3049210660 Update Rust crate serde_json to 1.0.88
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2022-11-18 14:25:20 +00:00
7e6309d777 Update all dependencies
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2022-10-26 18:22:57 +00:00
eeb4b5f089 Update Rust crate rltk to 0.8.7
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2022-10-26 15:13:31 +00:00
7bab0c7123 Add .drone.yml
All checks were successful
continuous-integration/drone/push Build is passing
2022-10-26 16:17:04 +02:00
596c04d791 Add .drone.yml
All checks were successful
continuous-integration/drone Build is passing
2022-10-26 11:30:38 +02:00
90d60ff54c Merge pull request 'Configure Renovate' (#1) from renovate/configure into main
Reviewed-on: https://git.front.kjuulh.io/kjuulh/roguelike/pulls/1
2022-10-25 21:00:02 +00:00
162fb413a5 Add renovate.json 2022-10-25 20:58:59 +00:00
34c4aa35fc Add drunkards walk 2022-01-30 20:10:23 +01:00
d4892e33bd Add cellular automata 2022-01-30 15:50:36 +01:00
5a00ebf1f1 Add bsp interior 2022-01-30 14:24:56 +01:00
c1893aec74 Bsp: format 2022-01-29 22:38:38 +01:00
0511c00f09 Add base bsp 2022-01-29 22:38:26 +01:00
9c0e546887 Add map test 2022-01-29 21:29:23 +01:00
ce4eb0b4cf Refactor 2022-01-29 21:27:59 +01:00
fa71410453 Refactor map 2022-01-29 20:21:13 +01:00
0af23af3d3 Add basic map 2022-01-29 16:35:10 +01:00
23 changed files with 1793 additions and 655 deletions

9
.drone.yml Executable file
View File

@@ -0,0 +1,9 @@
kind: pipeline
type: docker
name: "test"
steps:
- name: test
image: harbor.front.kjuulh.io/docker-proxy/library/bash:latest
commands:
- echo 'Run tests'

8
.idea/.gitignore generated vendored
View File

@@ -1,8 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

99
.idea/workspace.xml generated Normal file
View File

@@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="CMakeSettings">
<configurations>
<configuration PROFILE_NAME="Debug" ENABLED="true" CONFIG_NAME="Debug" />
</configurations>
</component>
<component name="CargoProjects">
<cargoProject FILE="$PROJECT_DIR$/Cargo.toml" />
</component>
<component name="ChangeListManager">
<list default="true" id="a9f5cad0-d253-42e8-a38a-89dfec417dbc" name="Changes" comment="">
<change afterPath="$PROJECT_DIR$/src/map_builders/drunkard.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/savegame.json" beforeDir="false" afterPath="$PROJECT_DIR$/savegame.json" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/map_builders/common.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/map_builders/common.rs" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/map_builders/mod.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/map_builders/mod.rs" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="ClangdSettings">
<option name="formatViaClangd" value="false" />
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="GitSEFilterConfiguration">
<file-type-list>
<filtered-out-file-type name="LOCAL_BRANCH" />
<filtered-out-file-type name="REMOTE_BRANCH" />
<filtered-out-file-type name="TAG" />
<filtered-out-file-type name="COMMIT_BY_MESSAGE" />
</file-type-list>
</component>
<component name="MacroExpansionManager">
<option name="directoryName" value="7zted9t8" />
</component>
<component name="MarkdownSettingsMigration">
<option name="stateVersion" value="1" />
</component>
<component name="ProjectId" id="24PyiDwZbQQQ0gzNvMewyQ4Zryi" />
<component name="ProjectLevelVcsManager">
<ConfirmationsSetting value="2" id="Add" />
</component>
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">
<property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
<property name="RunOnceActivity.ShowReadmeOnStart" value="true" />
<property name="RunOnceActivity.cidr.known.project.marker" value="true" />
<property name="WebServerToolWindowFactoryState" value="false" />
<property name="cf.first.check.clang-format" value="false" />
<property name="cidr.known.project.marker" value="true" />
<property name="node.js.detected.package.eslint" value="true" />
<property name="node.js.detected.package.tslint" value="true" />
<property name="node.js.selected.package.eslint" value="(autodetect)" />
<property name="node.js.selected.package.tslint" value="(autodetect)" />
<property name="nodejs_package_manager_path" value="npm" />
<property name="org.rust.cargo.project.model.PROJECT_DISCOVERY" value="true" />
<property name="settings.editor.selected.configurable" value="reference.settingsdialog.project.grazie" />
</component>
<component name="RustProjectSettings">
<option name="toolchainHomeDirectory" value="$USER_HOME$/.cargo/bin" />
</component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="a9f5cad0-d253-42e8-a38a-89dfec417dbc" name="Changes" comment="" />
<created>1643546643557</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1643546643557</updated>
<workItem from="1643546645546" duration="12996000" />
</task>
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" />
</component>
<component name="Vcs.Log.Tabs.Properties">
<option name="TAB_STATES">
<map>
<entry key="MAIN">
<value>
<State />
</value>
</entry>
</map>
</option>
</component>
</project>

189
Cargo.lock generated
View File

@@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
version = 4
[[package]]
name = "ab_glyph_rasterizer"
@@ -22,9 +22,14 @@ checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
[[package]]
name = "ahash"
version = "0.3.8"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8fd72866655d1904d6b0997d0b07ba561047d070fbe29de039031c641b61217"
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
dependencies = [
"getrandom",
"once_cell",
"version_check",
]
[[package]]
name = "aho-corasick"
@@ -62,15 +67,15 @@ checksum = "94a45b455c14666b85fc40a019e8ab9eb75e3a124e05494f5397122bc9eb06e0"
[[package]]
name = "arrayvec"
version = "0.5.2"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
[[package]]
name = "atom"
version = "0.3.6"
name = "atomic_refcell"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9ff149ed9780025acfdb36862d35b28856bb693ceb451259a7164442f22fdc3"
checksum = "112ef6b3f6cb3cb6fc5b6b494ef7a848492cff1ab0ef4de10b0f7d572861c905"
[[package]]
name = "autocfg"
@@ -441,13 +446,12 @@ dependencies = [
[[package]]
name = "crossbeam-queue"
version = "0.2.3"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570"
checksum = "1cd42583b04998a5363558e5f9291ee5a5ff6b49944332103f251e7479a82aa7"
dependencies = [
"cfg-if 0.1.10",
"crossbeam-utils 0.7.2",
"maybe-uninit",
"cfg-if 1.0.0",
"crossbeam-utils 0.8.6",
]
[[package]]
@@ -498,7 +502,7 @@ dependencies = [
"proc-macro2",
"quote",
"strsim",
"syn",
"syn 1.0.105",
]
[[package]]
@@ -509,7 +513,7 @@ checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72"
dependencies = [
"darling_core",
"quote",
"syn",
"syn 1.0.105",
]
[[package]]
@@ -530,7 +534,7 @@ checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.105",
]
[[package]]
@@ -750,16 +754,6 @@ dependencies = [
"gl_generator",
]
[[package]]
name = "hashbrown"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96282e96bfcd3da0d3aa9938bedf1e50df3269b6db08b4876d2da0bb1a0841cf"
dependencies = [
"ahash",
"autocfg",
]
[[package]]
name = "hermit-abi"
version = "0.1.19"
@@ -771,11 +765,10 @@ dependencies = [
[[package]]
name = "hibitset"
version = "0.6.3"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93a1bb8316a44459a7d14253c4d28dd7395cbd23cc04a68c46e851b8e46d64b1"
checksum = "f3ede5cfa60c958e60330d65163adbc4211e15a2653ad80eb0cce878de120121"
dependencies = [
"atom",
"rayon",
]
@@ -921,6 +914,22 @@ dependencies = [
"cfg-if 1.0.0",
]
[[package]]
name = "macro_rules_attribute"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf0c9b980bf4f3a37fd7b1c066941dd1b1d0152ce6ee6e8fe8c49b9f6810d862"
dependencies = [
"macro_rules_attribute-proc_macro",
"paste",
]
[[package]]
name = "macro_rules_attribute-proc_macro"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58093314a45e00c77d5c508f76e77c3396afbbc0d01506e7fae47b018bac2b1d"
[[package]]
name = "malloc_buf"
version = "0.0.6"
@@ -930,12 +939,6 @@ 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.4.1"
@@ -1028,12 +1031,6 @@ dependencies = [
"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"
@@ -1070,7 +1067,7 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn",
"syn 1.0.105",
]
[[package]]
@@ -1135,6 +1132,27 @@ dependencies = [
"version_check",
]
[[package]]
name = "nougat"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97b57b9ced431322f054fc673f1d3c7fa52d80efd9df74ad2fc759f044742510"
dependencies = [
"macro_rules_attribute",
"nougat-proc_macros",
]
[[package]]
name = "nougat-proc_macros"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c84f77a45e99a2f9b492695d99e1c23844619caa5f3e57647cffacad773ca257"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.105",
]
[[package]]
name = "num-integer"
version = "0.1.44"
@@ -1205,7 +1223,7 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn",
"syn 1.0.105",
]
[[package]]
@@ -1275,6 +1293,12 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "paste"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
[[package]]
name = "percent-encoding"
version = "2.1.0"
@@ -1316,18 +1340,18 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.36"
version = "1.0.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
dependencies = [
"unicode-xid",
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.15"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
@@ -1464,9 +1488,9 @@ checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "rltk"
version = "0.8.1"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c83076f8174384edf79d3f4a91934b8c0e1feed6eed943608d54af790f0a7dd"
checksum = "30587bc9361fc5600650d17ad6b9a91bb6a290f9d06a434dc48198d22ead7543"
dependencies = [
"bracket-lib",
]
@@ -1536,31 +1560,32 @@ checksum = "a0eddf2e8f50ced781f288c19f18621fa72a3779e3cb58dbf23b07469b0abeb4"
[[package]]
name = "serde"
version = "1.0.136"
version = "1.0.218"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.136"
version = "1.0.218"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.82",
]
[[package]]
name = "serde_json"
version = "1.0.78"
version = "1.0.140"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d23c1ba4cf0efd44be32017709280b32d1cea5c3f1275c3b6d9e8bc54f758085"
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
]
@@ -1577,13 +1602,13 @@ dependencies = [
[[package]]
name = "shred"
version = "0.10.2"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5f08237e667ac94ad20f8878b5943d91a93ccb231428446c57c21c57779016d"
checksum = "dc6b2cd1ccb08cf2b25d75c936e0cc9c8cb93c39a83814956da32653236338c0"
dependencies = [
"ahash",
"arrayvec",
"hashbrown",
"mopa",
"atomic_refcell",
"rayon",
"smallvec",
"tynm",
@@ -1637,14 +1662,15 @@ dependencies = [
[[package]]
name = "specs"
version = "0.16.1"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fff28a29366aff703d5da8a7e2c8875dc8453ac1118f842cbc0fa70c7db51240"
checksum = "a60eabdfd5a80e458c3e7bcc9f1076d6ce3cc8ddb71d69691f00fc0de735a635"
dependencies = [
"ahash",
"crossbeam-queue",
"hashbrown",
"hibitset",
"log",
"nougat",
"rayon",
"serde",
"shred",
@@ -1660,7 +1686,7 @@ checksum = "3e23e09360f3d2190fec4222cd9e19d3158d5da948c0d1ea362df617dd103511"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.105",
]
[[package]]
@@ -1671,13 +1697,24 @@ checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
[[package]]
name = "syn"
version = "1.0.86"
version = "1.0.105"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b"
checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83540f837a8afc019423a8edb95b52a8effe46957ee402287f4292fae35be021"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
@@ -1697,7 +1734,7 @@ checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.105",
]
[[package]]
@@ -1739,9 +1776,9 @@ checksum = "3e5d7cd7ab3e47dda6e56542f4bbf3824c15234958c6e1bd6aaa347e93499fdc"
[[package]]
name = "tuple_utils"
version = "0.3.0"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44834418e2c5b16f47bedf35c28e148db099187dd5feee6367fb2525863af4f1"
checksum = "cffaaf9392ef73cd30828797152476aaa2fa37a17856934fa63d4843f34290e9"
[[package]]
name = "tynm"
@@ -1762,10 +1799,10 @@ dependencies = [
]
[[package]]
name = "unicode-xid"
version = "0.2.2"
name = "unicode-ident"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
[[package]]
name = "version_check"
@@ -1811,7 +1848,7 @@ dependencies = [
"log",
"proc-macro2",
"quote",
"syn",
"syn 1.0.105",
"wasm-bindgen-shared",
]
@@ -1833,7 +1870,7 @@ checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.105",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]

View File

@@ -6,8 +6,8 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rltk = { version = "0.8.0", features = ["serde"] }
specs = { version = "0.16.1", features = ["serde"] }
rltk = { version = "0.8.7", features = ["serde"] }
specs = { version = "0.20.0", features = ["serde"] }
specs-derive = { version = "0.4.1" }
serde = { version = "^1.0.44", features = ["derive"] }
serde_json = "1.0.44"
serde = { version = "^1.0.202", features = ["derive"] }
serde_json = "1.0.117"

3
renovate.json Normal file
View File

@@ -0,0 +1,3 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json"
}

File diff suppressed because one or more lines are too long

View File

@@ -11,6 +11,18 @@ pub struct Position {
pub y: i32,
}
impl Position {
pub fn new(x: i32, y: i32) -> Self {
Self { x, y }
}
}
impl Default for Position {
fn default() -> Self {
Self { x: 0, y: 0 }
}
}
#[derive(Component, ConvertSaveload, Clone)]
pub struct Renderable {
pub glyph: rltk::FontCharType,

View File

@@ -91,7 +91,7 @@ impl<'a> System<'a> for ItemUseSystem {
healing,
inflicts_damage,
_ranged,
mut map,
map,
mut suffer_damage,
aoe,
mut confused,

View File

@@ -14,12 +14,14 @@ use player::*;
use visibility_system::*;
use crate::gamelog::GameLog;
use crate::gui::MainMenuSelection;
use crate::healing_system::HealingSystem;
use crate::hunger_system::HungerSystem;
use crate::inventory_system::{
ItemCollectionSystem, ItemDropSystem, ItemRemoveSystem, ItemUseSystem,
};
use crate::particle_system::ParticleSpawnSystem;
use crate::state::{RunState, State};
use crate::trigger_system::TriggerSystem;
mod components;
@@ -30,6 +32,7 @@ mod healing_system;
mod hunger_system;
mod inventory_system;
mod map;
mod map_builders;
mod map_indexing_system;
mod melee_combat_system;
mod monster_ai_system;
@@ -40,423 +43,11 @@ mod rect;
mod rex_assets;
mod save_load_system;
mod spawner;
mod visibility_system;
mod state;
mod trigger_system;
mod visibility_system;
#[derive(PartialEq, Copy, Clone)]
pub enum RunState {
AwaitingInput,
PreRun,
PlayerTurn,
MonsterTurn,
ShowInventory,
ShowDropItem,
ShowTargeting {
range: i32,
item: Entity,
},
MainMenu {
menu_selection: gui::MainMenuSelection,
},
SaveGame,
NextLevel,
ShowRemoveItem,
GameOver,
MagicMapReveal {
row: i32,
},
}
pub struct State {
pub ecs: World,
}
impl State {
pub 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");
}
// Build a new map and place the player
let worldmap;
{
let mut worldmap_resource = self.ecs.write_resource::<Map>();
*worldmap_resource = Map::new_map_rooms_and_corridors(1);
worldmap = worldmap_resource.clone();
}
// Spawn bad guys
for room in worldmap.rooms.iter().skip(1) {
spawner::spawn_room(&mut self.ecs, room, 1);
}
// Place the player and update resources
let (player_x, player_y) = worldmap.rooms[0].center();
let player_entity = spawner::player(&mut self.ecs, player_x, player_y);
let mut player_position = self.ecs.write_resource::<Point>();
*player_position = Point::new(player_x, player_y);
let mut position_components = self.ecs.write_storage::<Position>();
let mut player_entity_writer = self.ecs.write_resource::<Entity>();
*player_entity_writer = player_entity;
let player_pos_comp = position_components.get_mut(player_entity);
if let Some(player_pos_comp) = player_pos_comp {
player_pos_comp.x = player_x;
player_pos_comp.y = player_y;
}
// Mark the player's visibility as dirty
let mut viewshed_components = self.ecs.write_storage::<Viewshed>();
let vs = viewshed_components.get_mut(player_entity);
if let Some(vs) = vs {
vs.dirty = true;
}
}
}
impl State {
fn entities_to_remove_on_level_change(&mut self) -> Vec<Entity> {
let entities = self.ecs.entities();
let player = self.ecs.read_storage::<Player>();
let backpack = self.ecs.read_storage::<InBackpack>();
let player_entity = self.ecs.fetch::<Entity>();
let equipped = self.ecs.read_storage::<Equipped>();
let mut to_delete: Vec<Entity> = Vec::new();
for entity in entities.join() {
let mut should_delete = true;
let p = player.get(entity);
if let Some(_p) = p {
should_delete = false;
}
let bp = backpack.get(entity);
if let Some(bp) = bp {
if bp.owner == *player_entity {
should_delete = false;
}
}
if let Some(eq) = equipped.get(entity) {
if eq.owner == *player_entity {
should_delete = false;
}
}
if should_delete {
to_delete.push(entity);
}
}
to_delete
}
fn goto_next_level(&mut self) {
let to_delete = self.entities_to_remove_on_level_change();
for target in to_delete {
self.ecs
.delete_entity(target)
.expect("Unable to delete entity")
}
let worldmap;
let current_depth;
{
let mut worldmap_resource = self.ecs.write_resource::<Map>();
current_depth = worldmap_resource.depth;
*worldmap_resource = Map::new_map_rooms_and_corridors(current_depth + 1);
worldmap = worldmap_resource.clone();
}
for room in worldmap.rooms.iter().skip(1) {
spawner::spawn_room(&mut self.ecs, room, current_depth + 1);
}
let (player_x, player_y) = worldmap.rooms[0].center();
let mut player_position = self.ecs.write_resource::<Point>();
*player_position = Point::new(player_x, player_y);
let mut position_components = self.ecs.write_storage::<Position>();
let player_entity = self.ecs.fetch::<Entity>();
let player_pos_comp = position_components.get_mut(*player_entity);
if let Some(player_pos) = player_pos_comp {
player_pos.x = player_x;
player_pos.y = player_y;
}
let mut viewshed_components = self.ecs.write_storage::<Viewshed>();
let vs = viewshed_components.get_mut(*player_entity);
if let Some(vs) = vs {
vs.dirty = true;
}
let mut gamelog = self.ecs.fetch_mut::<GameLog>();
gamelog
.entries
.push("You descend to the next level, and take a moment to heal".to_string());
let mut player_health_store = self.ecs.write_storage::<CombatStats>();
let player_health = player_health_store.get_mut(*player_entity);
if let Some(player_health) = player_health {
player_health.hp = i32::max(player_health.hp, player_health.max_hp / 2);
}
}
}
impl State {
fn run_systems(&mut self) {
let mut vis = VisibilitySystem {};
vis.run_now(&self.ecs);
let mut mob = MonsterAI {};
mob.run_now(&self.ecs);
let mut trigger = TriggerSystem {};
trigger.run_now(&self.ecs);
let mut map_index = MapIndexingSystem {};
map_index.run_now(&self.ecs);
let mut melee_system = MeleeCombatSystem {};
melee_system.run_now(&self.ecs);
let mut damage_system = DamageSystem {};
damage_system.run_now(&self.ecs);
let mut healing_system = HealingSystem {};
healing_system.run_now(&self.ecs);
let mut inventory = ItemCollectionSystem {};
inventory.run_now(&self.ecs);
let mut potions = ItemUseSystem {};
potions.run_now(&self.ecs);
let mut drop_items = ItemDropSystem {};
drop_items.run_now(&self.ecs);
let mut remove_items = ItemRemoveSystem {};
remove_items.run_now(&self.ecs);
let mut hunger = HungerSystem {};
hunger.run_now(&self.ecs);
let mut particle_spawn = ParticleSpawnSystem {};
particle_spawn.run_now(&self.ecs);
self.ecs.maintain();
}
}
impl GameState for State {
fn tick(&mut self, ctx: &mut rltk::Rltk) {
let mut new_run_state;
{
let run_state = self.ecs.fetch::<RunState>();
new_run_state = *run_state;
}
ctx.cls();
particle_system::cull_dead_particles(&mut self.ecs, ctx);
match new_run_state {
RunState::MainMenu { .. } => {}
_ => {
draw_map(&self.ecs, ctx);
let positions = self.ecs.read_storage::<Position>();
let renderables = self.ecs.read_storage::<Renderable>();
let hidden = self.ecs.read_storage::<Hidden>();
let map = self.ecs.fetch::<Map>();
let mut data = (&positions, &renderables, !&hidden)
.join()
.collect::<Vec<_>>();
data.sort_by(|&a, &b| b.1.render_order.cmp(&a.1.render_order));
for (pos, render, _hidden) in data.iter() {
let idx = map.xy_idx(pos.x, pos.y);
if map.visible_tiles[idx] {
ctx.set(pos.x, pos.y, render.fg, render.bg, render.glyph)
}
}
gui::draw_ui(&self.ecs, ctx);
}
}
match new_run_state {
RunState::PreRun => {
self.run_systems();
new_run_state = RunState::AwaitingInput;
}
RunState::AwaitingInput => new_run_state = player_input(self, ctx),
RunState::PlayerTurn => {
self.run_systems();
new_run_state = RunState::MonsterTurn;
match *self.ecs.fetch::<RunState>() {
RunState::MagicMapReveal { .. } => {
new_run_state = RunState::MagicMapReveal { row: 0 }
}
_ => {
new_run_state = RunState::MonsterTurn;
}
}
}
RunState::MonsterTurn => {
self.run_systems();
new_run_state = RunState::AwaitingInput;
}
RunState::ShowInventory => {
let result = gui::show_inventory(self, ctx);
match result.0 {
gui::ItemMenuResult::Cancel => new_run_state = RunState::AwaitingInput,
gui::ItemMenuResult::NoResponse => {}
gui::ItemMenuResult::Selected => {
let item_entity = result.1.unwrap();
let is_ranged = self.ecs.read_storage::<Ranged>();
let is_item_ranged = is_ranged.get(item_entity);
if let Some(is_item_ranged) = is_item_ranged {
new_run_state = RunState::ShowTargeting {
range: is_item_ranged.range,
item: item_entity,
}
} else {
let mut intent = self.ecs.write_storage::<WantsToUseItem>();
intent
.insert(
*self.ecs.fetch::<Entity>(),
WantsToUseItem {
item: item_entity,
target: None,
},
)
.expect("Unable to insert intent");
new_run_state = RunState::PlayerTurn;
}
}
}
}
RunState::ShowDropItem => {
let result = gui::drop_item_menu(self, ctx);
match result.0 {
gui::ItemMenuResult::Cancel => new_run_state = RunState::AwaitingInput,
gui::ItemMenuResult::NoResponse => {}
gui::ItemMenuResult::Selected => {
let item_entity = result.1.unwrap();
let mut intent = self.ecs.write_storage::<WantsToDropItem>();
intent
.insert(
*self.ecs.fetch::<Entity>(),
WantsToDropItem { item: item_entity },
)
.expect("Unable to insert intent");
new_run_state = RunState::PlayerTurn;
}
}
}
RunState::ShowTargeting { range, item } => {
let result = gui::ranged_target(self, ctx, range);
match result.0 {
gui::ItemMenuResult::Cancel => new_run_state = RunState::AwaitingInput,
gui::ItemMenuResult::NoResponse => {}
gui::ItemMenuResult::Selected => {
let mut intent = self.ecs.write_storage::<WantsToUseItem>();
intent
.insert(
*self.ecs.fetch::<Entity>(),
WantsToUseItem {
item,
target: result.1,
},
)
.expect("Unable to insert intent");
new_run_state = RunState::PlayerTurn;
}
}
}
RunState::MainMenu { .. } => {
let result = gui::main_menu(self, ctx);
match result {
gui::MainMenuResult::NoSelection { selected } => {
new_run_state = RunState::MainMenu {
menu_selection: selected,
}
}
gui::MainMenuResult::Selected { selected } => match selected {
gui::MainMenuSelection::NewGame => new_run_state = RunState::PreRun,
gui::MainMenuSelection::LoadGame => {
save_load_system::load_game(&mut self.ecs);
new_run_state = RunState::AwaitingInput;
save_load_system::delete_save();
}
gui::MainMenuSelection::Quit => ::std::process::exit(0),
},
}
}
RunState::SaveGame => {
save_load_system::save_game(&mut self.ecs);
new_run_state = RunState::MainMenu {
menu_selection: gui::MainMenuSelection::Quit,
};
}
RunState::NextLevel => {
self.goto_next_level();
new_run_state = RunState::PreRun;
}
RunState::ShowRemoveItem => {
let result = gui::remove_item_menu(self, ctx);
match result.0 {
gui::ItemMenuResult::Cancel => new_run_state = RunState::AwaitingInput,
gui::ItemMenuResult::NoResponse => {}
gui::ItemMenuResult::Selected => {
let item_entity = result.1.unwrap();
let mut intent = self.ecs.write_storage::<WantsToRemoveItem>();
intent
.insert(
*self.ecs.fetch::<Entity>(),
WantsToRemoveItem { item: item_entity },
)
.expect("Unable to insert intent");
new_run_state = RunState::PlayerTurn;
}
}
}
RunState::GameOver => {
let result = gui::game_over(ctx);
match result {
gui::GameOverResult::NoSelection => {}
gui::GameOverResult::QuitToMenu => {
self.game_over_cleanup();
new_run_state = RunState::MainMenu {
menu_selection: gui::MainMenuSelection::NewGame,
};
}
}
}
RunState::MagicMapReveal { row } => {
let mut map = self.ecs.fetch_mut::<Map>();
for x in 0..MAP_WIDTH {
let idx = map.xy_idx(x as i32, row);
map.revealed_tiles[idx] = true;
}
if row as usize == MAP_HEIGHT - 1 {
new_run_state = RunState::MonsterTurn;
} else {
new_run_state = RunState::MagicMapReveal { row: row + 1 }
}
}
}
{
let mut run_writer = self.ecs.write_resource::<RunState>();
*run_writer = new_run_state;
}
damage_system::delete_the_dead(&mut self.ecs);
}
}
const SHOW_MAPGEN_VISUALIZER: bool = true;
fn main() -> rltk::BError {
use rltk::RltkBuilder;
@@ -466,11 +57,7 @@ fn main() -> rltk::BError {
.with_dimensions(80 * 3, 50 * 3)
.build()?;
context.with_post_scanlines(true);
let mut gs = State { ecs: World::new() };
gs.ecs.insert(rltk::RandomNumberGenerator::new());
gs.ecs.insert(particle_system::ParticleBuilder::new());
gs.ecs.insert(rex_assets::RexAssets::new());
let mut gs = State::new();
gs.ecs.register::<Position>();
gs.ecs.register::<Renderable>();
@@ -512,24 +99,19 @@ fn main() -> rltk::BError {
gs.ecs.insert(SimpleMarkerAllocator::<SerializeMe>::new());
let map = Map::new_map_rooms_and_corridors(1);
let (player_x, player_y) = map.rooms[0].center();
let player_entity = spawner::player(&mut gs.ecs, player_x, player_y);
for room in map.rooms.iter().skip(1) {
spawner::spawn_room(&mut gs.ecs, room, 1);
}
gs.ecs.insert(map);
gs.ecs.insert(Point::new(player_x, player_y));
gs.ecs.insert(Map::new(1));
gs.ecs.insert(Point::new(0, 0));
gs.ecs.insert(rltk::RandomNumberGenerator::new());
let player_entity = spawner::player(&mut gs.ecs, 0, 0);
gs.ecs.insert(player_entity);
gs.ecs.insert(RunState::MainMenu {
menu_selection: gui::MainMenuSelection::NewGame,
});
gs.ecs.insert(RunState::MapGeneration {});
gs.ecs.insert(gamelog::GameLog {
entries: vec!["Welcome to Rusty Roguelike".to_string()],
});
gs.ecs.insert(particle_system::ParticleBuilder::new());
gs.ecs.insert(rex_assets::RexAssets::new());
gs.generate_world_map(1);
rltk::main_loop(context, gs)
}

View File

@@ -1,12 +1,9 @@
use std::cmp::{max, min};
use std::collections::HashSet;
use rltk::{Algorithm2D, BaseMap, FontCharType, Point, RandomNumberGenerator, Rltk, RGB};
use rltk::{Algorithm2D, BaseMap, FontCharType, Point, Rltk, RGB};
use serde::{Deserialize, Serialize};
use specs::prelude::*;
use crate::rect::Rect;
#[derive(PartialEq, Copy, Clone, Serialize, Deserialize)]
pub enum TileType {
Wall,
@@ -22,7 +19,6 @@ pub const MAX_MONSTER: i32 = 4;
#[derive(Default, Serialize, Deserialize, Clone)]
pub struct Map {
pub tiles: Vec<TileType>,
pub rooms: Vec<Rect>,
pub width: i32,
pub height: i32,
pub revealed_tiles: Vec<bool>,
@@ -41,33 +37,6 @@ impl Map {
(y as usize * self.width as usize) + x as usize
}
fn apply_room_to_map(&mut self, room: &Rect) {
for y in room.y1 + 1..=room.y2 {
for x in room.x1 + 1..=room.x2 {
let idx = self.xy_idx(x, y);
self.tiles[idx] = TileType::Floor;
}
}
}
fn apply_horizontal_tunnel(&mut self, x1: i32, x2: i32, y: i32) {
for x in min(x1, x2)..=max(x1, x2) {
let idx = self.xy_idx(x, y);
if idx > 0 && idx < self.width as usize * self.height as usize {
self.tiles[idx as usize] = TileType::Floor;
}
}
}
fn apply_vertical_tunnel(&mut self, y1: i32, y2: i32, x: i32) {
for y in min(y1, y2)..=max(y1, y2) {
let idx = self.xy_idx(x, y);
if idx > 0 && idx < self.width as usize * self.height as usize {
self.tiles[idx as usize] = TileType::Floor;
}
}
}
fn is_exit_valid(&self, x: i32, y: i32) -> bool {
if x < 1 || x > self.width - 1 || y < 1 || y > self.height - 1 {
return false;
@@ -88,10 +57,9 @@ impl Map {
}
}
pub fn new_map_rooms_and_corridors(new_depth: i32) -> Map {
let mut map = Map {
pub fn new(new_depth: i32) -> Map {
Map {
tiles: vec![TileType::Wall; MAP_COUNT],
rooms: Vec::new(),
width: MAP_WIDTH as i32,
height: MAP_HEIGHT as i32,
revealed_tiles: vec![false; MAP_COUNT],
@@ -100,50 +68,7 @@ impl Map {
tile_content: vec![Vec::new(); MAP_COUNT],
depth: new_depth,
bloodstains: HashSet::new(),
};
const MAX_ROOMS: i32 = 30;
const MIN_SIZE: i32 = 6;
const MAX_SIZE: i32 = 10;
let mut rng = RandomNumberGenerator::new();
for _ in 0..MAX_ROOMS {
let w = rng.range(MIN_SIZE, MAX_SIZE);
let h = rng.range(MIN_SIZE, MAX_SIZE);
let x = rng.roll_dice(1, map.width - w - 1) - 1;
let y = rng.roll_dice(1, map.height - h - 1) - 1;
let new_room = Rect::new(x, y, w, h);
let mut ok = true;
for other_room in map.rooms.iter() {
if new_room.intersect(other_room) {
ok = false
};
}
if ok {
map.apply_room_to_map(&new_room);
if !map.rooms.is_empty() {
let (new_x, new_y) = new_room.center();
let (prev_x, prev_y) = map.rooms[map.rooms.len() - 1].center();
if rng.range(0, 2) == 1 {
map.apply_horizontal_tunnel(prev_x, new_x, prev_y);
map.apply_vertical_tunnel(prev_y, new_y, new_x);
} else {
map.apply_vertical_tunnel(prev_y, new_y, prev_x);
map.apply_horizontal_tunnel(prev_x, new_x, new_y);
}
}
map.rooms.push(new_room);
}
}
let stairs_position = map.rooms[map.rooms.len() - 1].center();
let stairs_idx = map.xy_idx(stairs_position.0, stairs_position.1);
map.tiles[stairs_idx] = TileType::DownStairs;
map
}
}
@@ -201,9 +126,7 @@ impl Algorithm2D for Map {
}
}
pub fn draw_map(ecs: &World, ctx: &mut Rltk) {
let map = ecs.fetch::<Map>();
pub fn draw_map(map: &Map, ctx: &mut Rltk) {
let mut y = 0;
let mut x = 0;
for (idx, tile) in map.tiles.iter().enumerate() {

View File

@@ -0,0 +1,22 @@
use crate::{Map, TileType};
#[inline(always)]
pub fn draw_corridor(map: &mut Map, x1: i32, y1: i32, x2: i32, y2: i32) {
let mut x = x1;
let mut y = y1;
while x != x2 || y != y2 {
if x < x2 {
x += 1;
} else if x > x2 {
x -= 1;
} else if y < y2 {
y += 1;
} else if y > y2 {
y -= 1;
}
let idx = map.xy_idx(x, y);
map.tiles[idx] = TileType::Floor;
}
}

View File

@@ -0,0 +1,214 @@
use rltk::{Point, RandomNumberGenerator};
use crate::map_builders::common::apply_room_to_map;
use crate::map_builders::{bsp, MapBuilder};
use crate::rect::Rect;
use crate::{spawner, Map, Position, TileType, World, SHOW_MAPGEN_VISUALIZER};
pub struct BspDungeonBuilder {
map: Map,
starting_position: Position,
depth: i32,
rooms: Vec<Rect>,
history: Vec<Map>,
rects: Vec<Rect>,
}
impl BspDungeonBuilder {
pub fn new(new_depth: i32) -> Self {
Self {
starting_position: Position { x: 0, y: 0 },
depth: new_depth,
rects: Vec::new(),
map: Map::new(new_depth),
history: Vec::new(),
rooms: Vec::new(),
}
}
fn build(&mut self) {
let mut rng = RandomNumberGenerator::new();
self.rects.clear();
self.place_rooms(&mut rng);
self.place_corridors(&mut rng);
self.place_stairs();
self.place_start();
}
fn place_rooms(&mut self, rng: &mut RandomNumberGenerator) {
self.rects
.push(Rect::new(2, 2, self.map.width - 5, self.map.height - 5));
let first_room = self.rects[0];
self.add_subrects(first_room);
let mut n_rooms = 0;
while n_rooms < 240 {
let rect = self.get_random_rect(rng);
let candidate = self.get_random_sub_rect(rect, rng);
if self.is_possible(candidate) {
apply_room_to_map(&mut self.map, &candidate);
self.rooms.push(candidate);
self.add_subrects(rect);
self.take_snapshot();
}
n_rooms += 1;
}
self.rooms.sort_by(|a, b| a.x1.cmp(&b.x1));
}
fn place_corridors(&mut self, rng: &mut RandomNumberGenerator) {
for i in 0..self.rooms.len() - 1 {
let room = self.rooms[i];
let next_room = self.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);
self.draw_corridor(start_x, start_y, end_x, end_y);
self.take_snapshot();
}
}
fn place_start(&mut self) {
let start = self.rooms[0].center();
self.starting_position = Position {
x: start.0,
y: start.1,
};
}
fn place_stairs(&mut self) {
let stairs = self.rooms[self.rooms.len() - 1].center();
let stairs_idx = self.map.xy_idx(stairs.0, stairs.1);
self.map.tiles[stairs_idx] = TileType::DownStairs;
}
fn add_subrects(&mut self, rect: Rect) {
let width = i32::abs(rect.x1 - rect.x2);
let height = i32::abs(rect.y1 - rect.y2);
let half_width = i32::max(width / 2, 1);
let half_height = i32::max(height / 2, 1);
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 {
if self.rects.len() == 1 {
return self.rects[0];
}
let idx = (rng.roll_dice(1, self.rects.len() as i32) - 1) as usize;
self.rects[idx]
}
fn get_random_sub_rect(&self, rect: Rect, rng: &mut RandomNumberGenerator) -> Rect {
let mut result = rect;
let rect_width = i32::abs(rect.x1 - rect.x2);
let rect_height = i32::abs(rect.y1 - rect.y2);
let w = i32::max(3, rng.roll_dice(1, i32::min(rect_width, 10)) - 1) + 1;
let h = i32::max(3, rng.roll_dice(1, i32::min(rect_height, 10)) - 1) + 1;
result.x1 += rng.roll_dice(1, 6) - 1;
result.y1 += rng.roll_dice(1, 6) - 1;
result.x2 = result.x1 + w;
result.y2 = result.y1 + h;
result
}
fn is_possible(&self, rect: Rect) -> bool {
let mut expanded = rect;
expanded.x1 -= 2;
expanded.x2 += 2;
expanded.y1 -= 2;
expanded.y2 += 2;
let mut can_build = true;
for y in expanded.y1..=expanded.y2 {
for x in expanded.x1..=expanded.x2 {
if x > self.map.width - 2 {
can_build = false;
}
if y > self.map.height - 2 {
can_build = false;
}
if x < 1 {
can_build = false;
}
if y < 1 {
can_build = false;
}
if can_build {
let idx = self.map.xy_idx(x, y);
if self.map.tiles[idx] != TileType::Wall {
can_build = false;
}
}
}
}
can_build
}
fn draw_corridor(&mut self, x1: i32, y1: i32, x2: i32, y2: i32) {
bsp::draw_corridor(&mut self.map, x1, y1, x2, y2);
}
}
impl MapBuilder for BspDungeonBuilder {
fn build_map(&mut self) {
self.build();
}
fn spawn_entities(&mut self, ecs: &mut World) {
for room in self.rooms.iter().skip(1) {
spawner::spawn_room(ecs, room, self.depth);
}
}
fn get_map(&self) -> Map {
self.map.clone()
}
fn get_starting_position(&self) -> Position {
self.starting_position.clone()
}
fn get_snapshot_history(&self) -> Vec<Map> {
self.history.clone()
}
fn take_snapshot(&mut self) {
if SHOW_MAPGEN_VISUALIZER {
let mut snapshot = self.map.clone();
for v in snapshot.revealed_tiles.iter_mut() {
*v = true;
}
self.history.push(snapshot);
}
}
}

View File

@@ -0,0 +1,158 @@
use crate::map_builders::common::reveal_all;
use crate::map_builders::{bsp, MapBuilder};
use crate::rect::Rect;
use crate::{spawner, Map, Position, TileType, World, SHOW_MAPGEN_VISUALIZER};
use rltk::RandomNumberGenerator;
const MIN_ROOM_SIZE: i32 = 8;
pub struct BspInteriorBuilder {
map: Map,
starting_position: Position,
depth: i32,
rooms: Vec<Rect>,
history: Vec<Map>,
rects: Vec<Rect>,
}
impl BspInteriorBuilder {
pub fn new(new_depth: i32) -> Self {
Self {
map: Map::new(new_depth),
rects: Vec::new(),
history: Vec::new(),
depth: new_depth,
rooms: Vec::new(),
starting_position: Position::default(),
}
}
fn build(&mut self) {
let mut rng = RandomNumberGenerator::new();
self.place_rooms(&mut rng);
self.place_corridors(&mut rng);
self.place_stairs();
self.place_start()
}
fn place_start(&mut self) {
let start = self.rooms[0].center();
self.starting_position = Position::new(start.0, start.1)
}
fn place_stairs(&mut self) {
let stairs = self.rooms[self.rooms.len() - 1].center();
let stairs_idx = self.map.xy_idx(stairs.0, stairs.1);
self.map.tiles[stairs_idx] = TileType::DownStairs;
}
fn place_corridors(&mut self, rng: &mut RandomNumberGenerator) {
for i in 0..self.rooms.len() - 1 {
let room = self.rooms[i];
let next_room = self.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);
self.draw_corridor(start_x, start_y, end_x, end_y);
self.take_snapshot();
}
}
fn place_rooms(&mut self, mut rng: &mut RandomNumberGenerator) {
self.rects.clear();
self.rects
.push(Rect::new(1, 1, self.map.width - 2, self.map.height - 2));
let first_room = self.rects[0];
self.add_subrects(first_room, &mut rng);
let rooms = self.rects.clone();
for r in rooms.iter() {
let room = *r;
self.rooms.push(room);
for y in room.y1..room.y2 {
for x in room.x1..room.x2 {
let idx = self.map.xy_idx(x, y);
if idx > 0 && idx < ((self.map.width * self.map.height) - 1) as usize {
self.map.tiles[idx] = TileType::Floor;
}
}
}
self.take_snapshot();
}
}
fn add_subrects(&mut self, rect: Rect, rng: &mut RandomNumberGenerator) {
if !self.rects.is_empty() {
self.rects.remove(self.rects.len() - 1);
}
let width = rect.x2 - rect.x1;
let height = rect.y2 - rect.y1;
let half_width = width / 2;
let half_height = height / 2;
let split = rng.roll_dice(1, 4);
if split <= 2 {
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::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 {
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::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);
}
}
}
fn draw_corridor(&mut self, x1: i32, y1: i32, x2: i32, y2: i32) {
bsp::draw_corridor(&mut self.map, x1, y1, x2, y2);
}
}
impl MapBuilder for BspInteriorBuilder {
fn build_map(&mut self) {
self.build();
}
fn spawn_entities(&mut self, ecs: &mut World) {
for room in self.rooms.iter().skip(1) {
spawner::spawn_room(ecs, room, self.depth);
}
}
fn get_map(&self) -> Map {
self.map.clone()
}
fn get_starting_position(&self) -> Position {
self.starting_position.clone()
}
fn get_snapshot_history(&self) -> Vec<Map> {
self.history.clone()
}
fn take_snapshot(&mut self) {
if SHOW_MAPGEN_VISUALIZER {
self.history.push(reveal_all(&self.map));
}
}
}

View File

@@ -0,0 +1,168 @@
use std::collections::HashMap;
use std::process::exit;
use rltk::RandomNumberGenerator;
use crate::map_builders::{common, MapBuilder};
use crate::{Map, Position, World, SHOW_MAPGEN_VISUALIZER, TileType, spawner};
const MIN_ROOM_SIZE: i32 = 8;
pub struct CellularAutomataBuilder {
map: Map,
starting_position: Position,
depth: i32,
history: Vec<Map>,
noise_areas : HashMap<i32, Vec<usize>>
}
impl CellularAutomataBuilder {
pub fn new(new_depth: i32) -> Self {
Self {
history: Vec::new(),
map: Map::new(new_depth),
depth: new_depth,
starting_position: Position::default(),
noise_areas: HashMap::new()
}
}
fn build(&mut self) {
let mut rng = RandomNumberGenerator::new();
self.place_random_level(&mut rng);
self.take_snapshot();
self.process_noise();
let start_idx = self.place_start();
self.place_exit(start_idx);
self.take_snapshot();
self.place_spawn_areas(rng)
}
fn place_spawn_areas(&mut self, mut rng: RandomNumberGenerator) {
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(rltk::CellularDistanceFunction::Manhattan);
for y in 1..self.map.height - 1 {
for x in 1..self.map.width - 1 {
let idx = self.map.xy_idx(x, y);
if self.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;
if self.noise_areas.contains_key(&cell_value) {
self.noise_areas.get_mut(&cell_value).unwrap().push(idx);
} else {
self.noise_areas.insert(cell_value, vec![idx]);
}
}
}
}
}
fn place_exit(&mut self, start_idx: usize) {
let map_starts: Vec<usize> = vec![start_idx];
let dijkstra_map = rltk::DijkstraMap::new(self.map.width, self.map.height, &map_starts, &self.map, 200.0);
let mut exit_tile = (0, 0.0f32);
for (i, tile) in self.map.tiles.iter_mut().enumerate() {
if *tile != TileType::Floor {
continue;
}
let distance_to_start = dijkstra_map.map[i];
if distance_to_start == f32::MAX {
*tile = TileType::Wall;
} else {
if distance_to_start > exit_tile.1 {
exit_tile.0 = i;
exit_tile.1 = distance_to_start;
}
}
}
self.take_snapshot();
self.map.tiles[exit_tile.0] = TileType::DownStairs;
}
fn place_start(&mut self) -> usize {
self.starting_position = Position::new(self.map.width / 2, self.map.height / 2);
let mut start_idx = self.map.xy_idx(self.starting_position.x, self.starting_position.y);
while self.map.tiles[start_idx] != TileType::Floor {
self.starting_position.x -= 1;
start_idx = self.map.xy_idx(self.starting_position.x, self.starting_position.y);
}
return start_idx;
}
fn process_noise(&mut self) {
for _i in 0..15 {
let mut new_tiles = self.map.tiles.clone();
for y in 1..self.map.height - 1 {
for x in 1..self.map.width - 1 {
let idx = self.map.xy_idx(x, y);
let mut neighbors = 0;
if self.map.tiles[idx - 1] == TileType::Wall { neighbors += 1; }
if self.map.tiles[idx + 1] == TileType::Wall { neighbors += 1; }
if self.map.tiles[idx - self.map.width as usize] == TileType::Wall { neighbors += 1; }
if self.map.tiles[idx + self.map.width as usize] == TileType::Wall { neighbors += 1; }
if self.map.tiles[idx - self.map.width as usize - 1] == TileType::Wall { neighbors += 1; }
if self.map.tiles[idx - self.map.width as usize + 1] == TileType::Wall { neighbors += 1; }
if self.map.tiles[idx + self.map.width as usize - 1] == TileType::Wall { neighbors += 1; }
if self.map.tiles[idx + self.map.width as usize + 1] == TileType::Wall { neighbors += 1; }
if neighbors > 4 || neighbors == 0 {
new_tiles[idx] = TileType::Wall;
} else {
new_tiles[idx] = TileType::Floor;
}
}
}
self.map.tiles = new_tiles.clone();
self.take_snapshot();
}
}
fn place_random_level(&mut self, rng: &mut RandomNumberGenerator) {
for y in 1..self.map.height - 1 {
for x in 1..self.map.width - 1 {
let roll = rng.roll_dice(1, 100);
let idx = self.map.xy_idx(x, y);
if roll > 55 {
self.map.tiles[idx] = TileType::Floor
} else {
self.map.tiles[idx] = TileType::Wall
}
}
}
}
}
impl MapBuilder for CellularAutomataBuilder {
fn build_map(&mut self) {
self.build();
}
fn spawn_entities(&mut self, ecs: &mut World) {
for area in self.noise_areas.iter() {
spawner::spawn_region(ecs, area.1, self.depth);
}
}
fn get_map(&self) -> Map {
self.map.clone()
}
fn get_starting_position(&self) -> Position {
self.starting_position.clone()
}
fn get_snapshot_history(&self) -> Vec<Map> {
self.history.clone()
}
fn take_snapshot(&mut self) {
if SHOW_MAPGEN_VISUALIZER {
self.history.push(common::reveal_all(&self.map))
}
}
}

View File

@@ -0,0 +1,90 @@
use std::cmp::{max, min};
use std::collections::HashMap;
use crate::rect::Rect;
use crate::{Map, TileType};
pub fn apply_room_to_map(map: &mut Map, room: &Rect) {
for y in room.y1 + 1..=room.y2 {
for x in room.x1 + 1..=room.x2 {
let idx = map.xy_idx(x, y);
map.tiles[idx] = TileType::Floor;
}
}
}
pub fn apply_horizontal_tunnel(map: &mut Map, x1: i32, x2: i32, y: i32) {
for x in min(x1, x2)..=max(x1, x2) {
let idx = map.xy_idx(x, y);
if idx > 0 && idx < map.width as usize * map.height as usize {
map.tiles[idx as usize] = TileType::Floor;
}
}
}
pub fn apply_vertical_tunnel(map: &mut Map, y1: i32, y2: i32, x: i32) {
for y in min(y1, y2)..=max(y1, y2) {
let idx = map.xy_idx(x, y);
if idx > 0 && idx < map.width as usize * map.height as usize {
map.tiles[idx as usize] = TileType::Floor;
}
}
}
pub fn reveal_all(map: &Map) -> Map {
let mut snapshot = map.clone();
for v in snapshot.revealed_tiles.iter_mut() {
*v = true;
}
snapshot
}
pub fn remove_unreachable_areas_returning_most_distant(map: &mut Map, start_idx: usize) -> usize {
map.populate_blocked();
let map_starts : Vec<usize> = vec![start_idx];
let dijkstra_map = rltk::DijkstraMap::new(map.width as usize, map.height as usize, &map_starts , map, 200.0);
let mut exit_tile = (0, 0.0f32);
for (i, tile) in map.tiles.iter_mut().enumerate() {
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 {
*tile = TileType::Wall;
} else {
// If it is further away than our current exit candidate, move the exit
if distance_to_start > exit_tile.1 {
exit_tile.0 = i;
exit_tile.1 = distance_to_start;
}
}
}
}
exit_tile.0
}
pub fn generate_voronois_spawn_regions(map: &Map, rng: &mut rltk::RandomNumberGenerator) -> HashMap<i32, Vec<usize>> {
let mut noise_areas : HashMap<i32, Vec<usize>> = HashMap::new();
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(rltk::CellularDistanceFunction::Manhattan);
for y in 1 .. map.height-1 {
for x in 1 .. map.width-1 {
let idx = map.xy_idx(x, y);
if 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;
if noise_areas.contains_key(&cell_value) {
noise_areas.get_mut(&cell_value).unwrap().push(idx);
} else {
noise_areas.insert(cell_value, vec![idx]);
}
}
}
}
noise_areas
}

View File

@@ -0,0 +1,180 @@
use std::collections::HashMap;
use rltk::RandomNumberGenerator;
use crate::{Map, Position, SHOW_MAPGEN_VISUALIZER, TileType, World};
use crate::map_builders::common::{generate_voronois_spawn_regions, remove_unreachable_areas_returning_most_distant, reveal_all};
use crate::map_builders::MapBuilder;
use crate::spawner::spawn_region;
pub struct DrunkardsWalkBuilder {
map: Map,
starting_position: Position,
depth: i32,
history: Vec<Map>,
noise_areas: HashMap<i32, Vec<usize>>,
settings: DrunkardSettings,
}
#[derive(PartialEq, Copy, Clone)]
pub enum DrunkSpawnMode { StartingPoint, Random }
pub struct DrunkardSettings {
pub spawn_mode: DrunkSpawnMode,
pub drunken_lifetime: i32,
pub floor_percent: f32,
}
impl DrunkardsWalkBuilder {
pub fn new(new_depth: i32, settings: DrunkardSettings) -> Self {
Self {
depth: new_depth,
map: Map::new(new_depth),
history: Vec::new(),
noise_areas: HashMap::new(),
starting_position: Position::default(),
settings,
}
}
pub fn open_area(new_depth: i32) -> Self {
DrunkardsWalkBuilder::new(
new_depth,
DrunkardSettings {
spawn_mode: DrunkSpawnMode::StartingPoint,
drunken_lifetime: 400,
floor_percent: 0.5,
},
)
}
pub fn open_halls(new_depth: i32) -> Self {
DrunkardsWalkBuilder::new(
new_depth,
DrunkardSettings {
spawn_mode: DrunkSpawnMode::Random,
drunken_lifetime: 400,
floor_percent: 0.5,
},
)
}
pub fn winding_passages(new_depth: i32) -> Self {
DrunkardsWalkBuilder::new(
new_depth,
DrunkardSettings {
spawn_mode: DrunkSpawnMode::Random,
drunken_lifetime: 100,
floor_percent: 0.4,
},
)
}
fn build(&mut self) {
let mut rng = RandomNumberGenerator::new();
self.starting_position = Position::new(self.map.width / 2, self.map.height / 2);
let start_idx = self.map.xy_idx(self.starting_position.x, self.starting_position.y);
self.map.tiles[start_idx] = TileType::Floor;
let total_tiles = self.map.width * self.map.height;
let desired_floor_tiles = (self.settings.floor_percent * total_tiles as f32) as usize;
let mut floor_tile_count = self.map.tiles.iter().filter(|a| **a == TileType::Floor).count();
let mut digger_count = 0;
let mut active_digger_count = 0;
while floor_tile_count < desired_floor_tiles {
let mut did_something = false;
let mut drunk_x = self.starting_position.x;
let mut drunk_y = self.starting_position.y;
match self.settings.spawn_mode {
DrunkSpawnMode::StartingPoint => {
drunk_x = self.starting_position.x;
drunk_y = self.starting_position.y;
}
DrunkSpawnMode::Random => {
if digger_count == 0 {
drunk_x = self.starting_position.x;
drunk_y = self.starting_position.y;
} else {
drunk_x = rng.roll_dice(1, self.map.width - 3) + 1;
drunk_y = rng.roll_dice(1, self.map.height - 3) + 1;
}
}
}
let mut drunk_life = self.settings.drunken_lifetime;
while drunk_life > 0 {
let drunk_idx = self.map.xy_idx(drunk_x, drunk_y);
if self.map.tiles[drunk_idx] == TileType::Wall {
did_something = true;
}
self.map.tiles[drunk_idx] = TileType::DownStairs;
let stagger_direction = rng.roll_dice(1, 4);
match stagger_direction {
1 if drunk_x > 2 => { drunk_x -= 1; }
2 if drunk_x < self.map.width - 2 => { drunk_x += 1; }
3 if drunk_y > 2 => { drunk_y -= 1; }
_ if drunk_y < self.map.height - 2 => { drunk_y += 1; }
_ => {}
}
drunk_life -= 1;
}
if did_something {
self.take_snapshot();
active_digger_count += 1;
}
digger_count += 1;
for t in self.map.tiles.iter_mut() {
if *t == TileType::DownStairs {
*t = TileType::Floor;
}
}
floor_tile_count = self.map.tiles.iter().filter(|a| **a == TileType::Floor).count();
}
rltk::console::log(format!("{} dwarves gave up their sobriety, of whom {} actually found a wall.", digger_count, active_digger_count));
let exit_tile = remove_unreachable_areas_returning_most_distant(&mut self.map, start_idx);
self.take_snapshot();
self.map.tiles[exit_tile] = TileType::DownStairs;
self.take_snapshot();
self.noise_areas = generate_voronois_spawn_regions(&self.map, &mut rng);
}
}
impl MapBuilder for DrunkardsWalkBuilder {
fn build_map(&mut self) {
self.build();
}
fn spawn_entities(&mut self, ecs: &mut World) {
for area in self.noise_areas.iter() {
spawn_region(ecs, area.1, self.depth);
}
}
fn get_map(&self) -> Map {
self.map.clone()
}
fn get_starting_position(&self) -> Position {
self.starting_position.clone()
}
fn get_snapshot_history(&self) -> Vec<Map> {
self.history.clone()
}
fn take_snapshot(&mut self) {
if SHOW_MAPGEN_VISUALIZER {
self.history.push(reveal_all(&self.map))
}
}
}

40
src/map_builders/mod.rs Normal file
View File

@@ -0,0 +1,40 @@
use specs::World;
use crate::map_builders::bsp_dungeon::BspDungeonBuilder;
use crate::map_builders::bsp_interior::BspInteriorBuilder;
use crate::map_builders::simple_map::SimpleMapBuilder;
use crate::{Map, Position};
use crate::map_builders::cellular_automata::CellularAutomataBuilder;
use crate::map_builders::drunkard::{DrunkardSettings, DrunkardsWalkBuilder, DrunkSpawnMode};
mod bsp;
mod bsp_dungeon;
mod bsp_interior;
mod cellular_automata;
mod common;
mod simple_map;
mod drunkard;
pub trait MapBuilder {
fn build_map(&mut self);
fn spawn_entities(&mut self, ecs: &mut World);
fn get_map(&self) -> Map;
fn get_starting_position(&self) -> Position;
fn get_snapshot_history(&self) -> Vec<Map>;
fn take_snapshot(&mut self);
}
pub fn new_random_builder(new_depth: i32) -> Box<dyn MapBuilder> {
let mut rng = rltk::RandomNumberGenerator::new();
//let builder_choice = rng.roll_dice(1, 7);
let builder_choice = 4;
match builder_choice {
6 => Box::new(DrunkardsWalkBuilder::open_area(new_depth)),
5 => Box::new(DrunkardsWalkBuilder::open_halls(new_depth)),
4 => Box::new(DrunkardsWalkBuilder::winding_passages(new_depth)),
3 => Box::new(CellularAutomataBuilder::new(new_depth)),
2 => Box::new(BspInteriorBuilder::new(new_depth)),
1 => Box::new(SimpleMapBuilder::new(new_depth)),
_ => Box::new(BspDungeonBuilder::new(new_depth)),
}
}

View File

@@ -0,0 +1,111 @@
use rltk::RandomNumberGenerator;
use specs::prelude::*;
use crate::map_builders::{common, MapBuilder};
use crate::rect::Rect;
use crate::{spawner, Map, Position, TileType, SHOW_MAPGEN_VISUALIZER};
pub struct SimpleMapBuilder {
map: Map,
starting_position: Position,
depth: i32,
rooms: Vec<Rect>,
history: Vec<Map>,
}
impl SimpleMapBuilder {
pub fn new(new_depth: i32) -> Self {
Self {
starting_position: Position { x: 0, y: 0 },
map: Map::new(new_depth),
depth: new_depth,
rooms: Vec::new(),
history: Vec::new(),
}
}
fn rooms_and_corridors(&mut self) {
const MAX_ROOMS: i32 = 30;
const MIN_SIZE: i32 = 6;
const MAX_SIZE: i32 = 10;
let mut rng = RandomNumberGenerator::new();
for _ in 0..MAX_ROOMS {
let w = rng.range(MIN_SIZE, MAX_SIZE);
let h = rng.range(MIN_SIZE, MAX_SIZE);
let x = rng.roll_dice(1, self.map.width - w - 1) - 1;
let y = rng.roll_dice(1, self.map.height - h - 1) - 1;
let new_room = Rect::new(x, y, w, h);
let mut ok = true;
for other_room in self.rooms.iter() {
if new_room.intersect(other_room) {
ok = false
};
}
if ok {
common::apply_room_to_map(&mut self.map, &new_room);
self.take_snapshot();
if !self.rooms.is_empty() {
let (new_x, new_y) = new_room.center();
let (prev_x, prev_y) = self.rooms[self.rooms.len() - 1].center();
if rng.range(0, 2) == 1 {
common::apply_horizontal_tunnel(&mut self.map, prev_x, new_x, prev_y);
common::apply_vertical_tunnel(&mut self.map, prev_y, new_y, new_x);
} else {
common::apply_vertical_tunnel(&mut self.map, prev_y, new_y, prev_x);
common::apply_horizontal_tunnel(&mut self.map, prev_x, new_x, new_y);
}
}
self.rooms.push(new_room);
self.take_snapshot();
}
}
let stairs_position = self.rooms[self.rooms.len() - 1].center();
let stairs_idx = self.map.xy_idx(stairs_position.0, stairs_position.1);
self.map.tiles[stairs_idx] = TileType::DownStairs;
let start_pos = self.rooms[0].center();
self.starting_position = Position {
x: start_pos.0,
y: start_pos.1,
}
}
}
impl MapBuilder for SimpleMapBuilder {
fn build_map(&mut self) {
self.rooms_and_corridors();
}
fn spawn_entities(&mut self, ecs: &mut World) {
for room in self.rooms.iter().skip(1) {
spawner::spawn_room(ecs, room, self.depth);
}
}
fn get_map(&self) -> Map {
self.map.clone()
}
fn get_starting_position(&self) -> Position {
self.starting_position.clone()
}
fn get_snapshot_history(&self) -> Vec<Map> {
self.history.clone()
}
fn take_snapshot(&mut self) {
if SHOW_MAPGEN_VISUALIZER {
let mut snapshot = self.map.clone();
for v in snapshot.revealed_tiles.iter_mut() {
*v = true;
}
self.history.push(snapshot);
}
}
}

View File

@@ -4,9 +4,14 @@ use rltk::{FontCharType, RandomNumberGenerator, RGB};
use specs::prelude::*;
use specs::saveload::{MarkedBuilder, SimpleMarker};
use crate::{AreaOfEffect, BlocksTile, CombatStats, Confusion, Consumable, DefenseBonus, EntryTrigger, EquipmentSlot, Equippable, Hidden, HungerClock, HungerState, InflictsDamage, Item, MagicMapper, MAP_WIDTH, MAX_MONSTER, MeleePowerBonus, Monster, Name, Player, Position, ProvidesFood, ProvidesHealing, Ranged, Renderable, SerializeMe, SingleActivation, Viewshed};
use crate::random_table::RandomTable;
use crate::rect::Rect;
use crate::{
AreaOfEffect, BlocksTile, CombatStats, Confusion, Consumable, DefenseBonus, EntryTrigger,
EquipmentSlot, Equippable, Hidden, HungerClock, HungerState, InflictsDamage, Item, MagicMapper,
Map, MeleePowerBonus, Monster, Name, Player, Position, ProvidesFood, ProvidesHealing, Ranged,
Renderable, SerializeMe, SingleActivation, TileType, Viewshed, MAP_WIDTH, MAX_MONSTER,
};
pub fn player(ecs: &mut World, player_x: i32, player_y: i32) -> Entity {
ecs.create_entity()
@@ -82,54 +87,77 @@ fn monster<S: ToString>(ecs: &mut World, x: i32, y: i32, glyph: FontCharType, na
#[allow(clippy::map_entry)]
pub fn spawn_room(ecs: &mut World, room: &Rect, map_depth: i32) {
let spawn_table = room_table(map_depth);
let mut spawn_points: HashMap<usize, String> = HashMap::new();
let mut possible_targets: Vec<usize> = Vec::new();
{
let mut rng = ecs.write_resource::<RandomNumberGenerator>();
let num_spawns = rng.roll_dice(1, MAX_MONSTER + 3) + (map_depth - 1) - 3;
for _i in 0..num_spawns {
let mut added = false;
let mut tried = 0;
while !added && tried < 20 {
let x = (room.x1 + rng.roll_dice(1, i32::abs(room.x2 - room.x1))) as usize;
let y = (room.y1 + rng.roll_dice(1, i32::abs(room.y2 - room.y1))) as usize;
let idx = (y * MAP_WIDTH) + x;
if !spawn_points.contains_key(&idx) {
spawn_points.insert(idx, spawn_table.roll(&mut rng));
added = true;
} else {
tried += 1;
let map = ecs.fetch::<Map>();
for y in room.y1 + 1..room.y2 {
for x in room.x1 + 1..room.x2 {
let idx = map.xy_idx(x, y);
if map.tiles[idx] == TileType::Floor {
possible_targets.push(idx)
}
}
}
}
for spawn in spawn_points.iter() {
let x = (*spawn.0 % MAP_WIDTH) as i32;
let y = (*spawn.0 / MAP_WIDTH) as i32;
spawn_region(ecs, &possible_targets, map_depth);
}
match spawn.1.as_ref() {
"Goblin" => goblin(ecs, x, y),
"Orc" => orc(ecs, x, y),
"Health Potion" => health_potion(ecs, x, y),
"Fireball Scroll" => fireball_scroll(ecs, x, y),
"Confusion Scroll" => confusion_scroll(ecs, x, y),
"Magic Missile Scroll" => magic_missile_scroll(ecs, x, y),
"Dagger" => dagger(ecs, x, y),
"Longsword" => longsword(ecs, x, y),
"Shield" => shield(ecs, x, y),
"Tower Shield" => tower_shield(ecs, x, y),
"Helmet" => helmet(ecs, x, y),
"Breastplate" => breastplate(ecs, x, y),
"Leggings" => leggings(ecs, x, y),
"Sabatons" => sabatons(ecs, x, y),
"Rations" => rations(ecs, x, y),
"Magic Mapping Scroll" => magic_mapper_scroll(ecs, x, y),
"Bear Trap" => bear_trap(ecs, x, y),
_ => {}
}
pub(crate) fn spawn_region(ecs: &mut World, area: &[usize], map_depth: i32) {
if let Some(spawn_points) = calculate_spawn_points(ecs, area, map_depth) {
spawn_entities(ecs, spawn_points)
}
}
fn spawn_entities(ecs: &mut World, spawn_points: HashMap<usize, String>) {
for spawn in spawn_points.iter() {
spawn_entity(ecs, &spawn);
}
}
fn calculate_spawn_points(ecs: &mut World, area: &[usize], map_depth: i32) -> Option<HashMap<usize, String>> {
let mut spawn_points: HashMap<usize, String> = HashMap::new();
let spawn_table = room_table(map_depth);
let mut areas: Vec<usize> = Vec::from(area);
let mut rng = ecs.write_resource::<RandomNumberGenerator>();
let num_spawns = i32::min(areas.len() as i32, rng.roll_dice(1, MAX_MONSTER + 3));
if num_spawns == 0 { return None; }
for _i in 0..num_spawns {
let array_idx = if areas.len() == 1 { 0usize } else { (rng.roll_dice(1, areas.len() as i32 - 1) as usize) };
let map_idx = areas[array_idx];
spawn_points.insert(map_idx, spawn_table.roll(&mut rng));
areas.remove(array_idx);
}
Some(spawn_points)
}
fn spawn_entity(ecs: &mut World, spawn: &(&usize, &String)) {
let x = (*spawn.0 % MAP_WIDTH) as i32;
let y = (*spawn.0 / MAP_WIDTH) as i32;
match spawn.1.as_ref() {
"Goblin" => goblin(ecs, x, y),
"Orc" => orc(ecs, x, y),
"Health Potion" => health_potion(ecs, x, y),
"Fireball Scroll" => fireball_scroll(ecs, x, y),
"Confusion Scroll" => confusion_scroll(ecs, x, y),
"Magic Missile Scroll" => magic_missile_scroll(ecs, x, y),
"Dagger" => dagger(ecs, x, y),
"Longsword" => longsword(ecs, x, y),
"Shield" => shield(ecs, x, y),
"Tower Shield" => tower_shield(ecs, x, y),
"Helmet" => helmet(ecs, x, y),
"Breastplate" => breastplate(ecs, x, y),
"Leggings" => leggings(ecs, x, y),
"Sabatons" => sabatons(ecs, x, y),
"Rations" => rations(ecs, x, y),
"Magic Mapping Scroll" => magic_mapper_scroll(ecs, x, y),
"Bear Trap" => bear_trap(ecs, x, y),
_ => {}
}
}

432
src/state.rs Normal file
View File

@@ -0,0 +1,432 @@
use specs::prelude::*;
use crate::*;
#[derive(PartialEq, Copy, Clone)]
pub enum RunState {
AwaitingInput,
PreRun,
PlayerTurn,
MonsterTurn,
ShowInventory,
ShowDropItem,
ShowTargeting {
range: i32,
item: Entity,
},
MainMenu {
menu_selection: gui::MainMenuSelection,
},
SaveGame,
NextLevel,
ShowRemoveItem,
GameOver,
MagicMapReveal {
row: i32,
},
MapGeneration,
}
pub struct State {
pub ecs: World,
mapgen_next_state: Option<RunState>,
mapgen_history: Vec<Map>,
mapgen_index: usize,
mapgen_timer: f32,
}
impl State {
pub fn new() -> Self {
Self {
ecs: World::new(),
mapgen_history: Vec::new(),
mapgen_index: 0,
mapgen_timer: 0.,
mapgen_next_state: Some(RunState::MainMenu {
menu_selection: MainMenuSelection::NewGame,
}),
}
}
pub 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");
}
{
let player_entity = spawner::player(&mut self.ecs, 0, 0);
let mut player_entity_writer = self.ecs.write_resource::<Entity>();
*player_entity_writer = player_entity
}
self.generate_world_map(1);
}
fn entities_to_remove_on_level_change(&mut self) -> Vec<Entity> {
let entities = self.ecs.entities();
let player = self.ecs.read_storage::<Player>();
let backpack = self.ecs.read_storage::<InBackpack>();
let player_entity = self.ecs.fetch::<Entity>();
let equipped = self.ecs.read_storage::<Equipped>();
let mut to_delete: Vec<Entity> = Vec::new();
for entity in entities.join() {
let mut should_delete = true;
let p = player.get(entity);
if let Some(_p) = p {
should_delete = false;
}
let bp = backpack.get(entity);
if let Some(bp) = bp {
if bp.owner == *player_entity {
should_delete = false;
}
}
if let Some(eq) = equipped.get(entity) {
if eq.owner == *player_entity {
should_delete = false;
}
}
if should_delete {
to_delete.push(entity);
}
}
to_delete
}
fn goto_next_level(&mut self) {
let to_delete = self.entities_to_remove_on_level_change();
for target in to_delete {
self.ecs
.delete_entity(target)
.expect("Unable to delete entity")
}
let current_depth;
{
let worldmap_resource = self.ecs.write_resource::<Map>();
current_depth = worldmap_resource.depth;
}
self.generate_world_map(current_depth + 1);
let player_entity = self.ecs.fetch::<Entity>();
let mut gamelog = self.ecs.fetch_mut::<GameLog>();
gamelog
.entries
.push("You descend to the next level, and take a moment to heal".to_string());
let mut player_health_store = self.ecs.write_storage::<CombatStats>();
let player_health = player_health_store.get_mut(*player_entity);
if let Some(player_health) = player_health {
player_health.hp = i32::max(player_health.hp, player_health.max_hp / 2);
}
}
fn run_systems(&mut self) {
let mut vis = VisibilitySystem {};
vis.run_now(&self.ecs);
let mut mob = MonsterAI {};
mob.run_now(&self.ecs);
let mut trigger = TriggerSystem {};
trigger.run_now(&self.ecs);
let mut map_index = MapIndexingSystem {};
map_index.run_now(&self.ecs);
let mut melee_system = MeleeCombatSystem {};
melee_system.run_now(&self.ecs);
let mut damage_system = DamageSystem {};
damage_system.run_now(&self.ecs);
let mut healing_system = HealingSystem {};
healing_system.run_now(&self.ecs);
let mut inventory = ItemCollectionSystem {};
inventory.run_now(&self.ecs);
let mut potions = ItemUseSystem {};
potions.run_now(&self.ecs);
let mut drop_items = ItemDropSystem {};
drop_items.run_now(&self.ecs);
let mut remove_items = ItemRemoveSystem {};
remove_items.run_now(&self.ecs);
let mut hunger = HungerSystem {};
hunger.run_now(&self.ecs);
let mut particle_spawn = ParticleSpawnSystem {};
particle_spawn.run_now(&self.ecs);
self.ecs.maintain();
}
pub fn generate_world_map(&mut self, new_depth: i32) {
self.mapgen_index = 0;
self.mapgen_timer = 0.0;
self.mapgen_history.clear();
let mut map_builder = map_builders::new_random_builder(new_depth);
map_builder.build_map();
self.mapgen_history = map_builder.get_snapshot_history();
let player_start;
{
let mut world_map_resource = self.ecs.write_resource::<Map>();
*world_map_resource = map_builder.get_map();
player_start = map_builder.get_starting_position();
}
map_builder.spawn_entities(&mut self.ecs);
let (player_x, player_y) = (player_start.x, player_start.y);
let mut player_pos = self.ecs.write_resource::<Point>();
*player_pos = Point::new(player_x, player_y);
let mut pos_comp = self.ecs.write_storage::<Position>();
let player_entity = self.ecs.fetch::<Entity>();
if let Some(player_pos_comp) = pos_comp.get_mut(*player_entity) {
player_pos_comp.x = player_x;
player_pos_comp.y = player_y;
}
let mut viewshed_comp = self.ecs.write_storage::<Viewshed>();
if let Some(vs) = viewshed_comp.get_mut(*player_entity) {
vs.dirty = true;
}
}
}
impl GameState for State {
fn tick(&mut self, ctx: &mut rltk::Rltk) {
let mut new_run_state;
{
let run_state = self.ecs.fetch::<RunState>();
new_run_state = *run_state;
}
ctx.cls();
particle_system::cull_dead_particles(&mut self.ecs, ctx);
match new_run_state {
RunState::MainMenu { .. } => {}
_ => {
draw_map(&self.ecs.fetch::<Map>(), ctx);
let positions = self.ecs.read_storage::<Position>();
let renderables = self.ecs.read_storage::<Renderable>();
let hidden = self.ecs.read_storage::<Hidden>();
let map = self.ecs.fetch::<Map>();
let mut data = (&positions, &renderables, !&hidden)
.join()
.collect::<Vec<_>>();
data.sort_by(|&a, &b| b.1.render_order.cmp(&a.1.render_order));
for (pos, render, _hidden) in data.iter() {
let idx = map.xy_idx(pos.x, pos.y);
if map.visible_tiles[idx] {
ctx.set(pos.x, pos.y, render.fg, render.bg, render.glyph)
}
}
gui::draw_ui(&self.ecs, ctx);
}
}
match new_run_state {
RunState::PreRun => {
self.run_systems();
new_run_state = RunState::AwaitingInput;
}
RunState::AwaitingInput => new_run_state = player_input(self, ctx),
RunState::PlayerTurn => {
self.run_systems();
new_run_state = RunState::MonsterTurn;
match *self.ecs.fetch::<RunState>() {
RunState::MagicMapReveal { .. } => {
new_run_state = RunState::MagicMapReveal { row: 0 }
}
_ => {
new_run_state = RunState::MonsterTurn;
}
}
}
RunState::MonsterTurn => {
self.run_systems();
new_run_state = RunState::AwaitingInput;
}
RunState::ShowInventory => {
let result = gui::show_inventory(self, ctx);
match result.0 {
gui::ItemMenuResult::Cancel => new_run_state = RunState::AwaitingInput,
gui::ItemMenuResult::NoResponse => {}
gui::ItemMenuResult::Selected => {
let item_entity = result.1.unwrap();
let is_ranged = self.ecs.read_storage::<Ranged>();
let is_item_ranged = is_ranged.get(item_entity);
if let Some(is_item_ranged) = is_item_ranged {
new_run_state = RunState::ShowTargeting {
range: is_item_ranged.range,
item: item_entity,
}
} else {
let mut intent = self.ecs.write_storage::<WantsToUseItem>();
intent
.insert(
*self.ecs.fetch::<Entity>(),
WantsToUseItem {
item: item_entity,
target: None,
},
)
.expect("Unable to insert intent");
new_run_state = RunState::PlayerTurn;
}
}
}
}
RunState::ShowDropItem => {
let result = gui::drop_item_menu(self, ctx);
match result.0 {
gui::ItemMenuResult::Cancel => new_run_state = RunState::AwaitingInput,
gui::ItemMenuResult::NoResponse => {}
gui::ItemMenuResult::Selected => {
let item_entity = result.1.unwrap();
let mut intent = self.ecs.write_storage::<WantsToDropItem>();
intent
.insert(
*self.ecs.fetch::<Entity>(),
WantsToDropItem { item: item_entity },
)
.expect("Unable to insert intent");
new_run_state = RunState::PlayerTurn;
}
}
}
RunState::ShowTargeting { range, item } => {
let result = gui::ranged_target(self, ctx, range);
match result.0 {
gui::ItemMenuResult::Cancel => new_run_state = RunState::AwaitingInput,
gui::ItemMenuResult::NoResponse => {}
gui::ItemMenuResult::Selected => {
let mut intent = self.ecs.write_storage::<WantsToUseItem>();
intent
.insert(
*self.ecs.fetch::<Entity>(),
WantsToUseItem {
item,
target: result.1,
},
)
.expect("Unable to insert intent");
new_run_state = RunState::PlayerTurn;
}
}
}
RunState::MainMenu { .. } => {
let result = gui::main_menu(self, ctx);
match result {
gui::MainMenuResult::NoSelection { selected } => {
new_run_state = RunState::MainMenu {
menu_selection: selected,
}
}
gui::MainMenuResult::Selected { selected } => match selected {
gui::MainMenuSelection::NewGame => new_run_state = RunState::PreRun,
gui::MainMenuSelection::LoadGame => {
save_load_system::load_game(&mut self.ecs);
new_run_state = RunState::AwaitingInput;
save_load_system::delete_save();
}
gui::MainMenuSelection::Quit => ::std::process::exit(0),
},
}
}
RunState::SaveGame => {
save_load_system::save_game(&mut self.ecs);
new_run_state = RunState::MainMenu {
menu_selection: gui::MainMenuSelection::Quit,
};
}
RunState::NextLevel => {
self.goto_next_level();
new_run_state = RunState::PreRun;
}
RunState::ShowRemoveItem => {
let result = gui::remove_item_menu(self, ctx);
match result.0 {
gui::ItemMenuResult::Cancel => new_run_state = RunState::AwaitingInput,
gui::ItemMenuResult::NoResponse => {}
gui::ItemMenuResult::Selected => {
let item_entity = result.1.unwrap();
let mut intent = self.ecs.write_storage::<WantsToRemoveItem>();
intent
.insert(
*self.ecs.fetch::<Entity>(),
WantsToRemoveItem { item: item_entity },
)
.expect("Unable to insert intent");
new_run_state = RunState::PlayerTurn;
}
}
}
RunState::GameOver => {
let result = gui::game_over(ctx);
match result {
gui::GameOverResult::NoSelection => {}
gui::GameOverResult::QuitToMenu => {
self.game_over_cleanup();
new_run_state = RunState::MainMenu {
menu_selection: gui::MainMenuSelection::NewGame,
};
}
}
}
RunState::MagicMapReveal { row } => {
let mut map = self.ecs.fetch_mut::<Map>();
for x in 0..MAP_WIDTH {
let idx = map.xy_idx(x as i32, row);
map.revealed_tiles[idx] = true;
}
if row as usize == MAP_HEIGHT - 1 {
new_run_state = RunState::MonsterTurn;
} else {
new_run_state = RunState::MagicMapReveal { row: row + 1 }
}
}
RunState::MapGeneration => {
if !SHOW_MAPGEN_VISUALIZER {
new_run_state = self.mapgen_next_state.unwrap();
}
ctx.cls();
draw_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_run_state = self.mapgen_next_state.unwrap();
}
}
}
}
{
let mut run_writer = self.ecs.write_resource::<RunState>();
*run_writer = new_run_state;
}
damage_system::delete_the_dead(&mut self.ecs);
}
}

View File

@@ -1,7 +1,10 @@
use specs::prelude::*;
use crate::{EntityMoved, EntryTrigger, GameLog, Hidden, InflictsDamage, Map, Name, Position, SingleActivation, SufferDamage};
use crate::particle_system::ParticleBuilder;
use crate::{
EntityMoved, EntryTrigger, GameLog, Hidden, InflictsDamage, Map, Name, Position,
SingleActivation, SufferDamage,
};
pub struct TriggerSystem {}
@@ -18,11 +21,24 @@ impl<'a> System<'a> for TriggerSystem {
ReadStorage<'a, InflictsDamage>,
WriteExpect<'a, ParticleBuilder>,
WriteStorage<'a, SufferDamage>,
WriteStorage<'a, SingleActivation>
WriteStorage<'a, SingleActivation>,
);
fn run(&mut self, data: Self::SystemData) {
let (map, mut entity_moved, position, entry_trigger, mut hidden, names, entities, mut game_log, inflicts_damage, mut particle_builder, mut inflict_damage, mut single_activation) = data;
let (
map,
mut entity_moved,
position,
entry_trigger,
mut hidden,
names,
entities,
mut game_log,
inflicts_damage,
mut particle_builder,
mut inflict_damage,
single_activation,
) = data;
let mut remove_entities: Vec<Entity> = Vec::new();
for (entity, mut _entity_moved, pos) in (&entities, &mut entity_moved, &position).join() {
@@ -41,7 +57,14 @@ impl<'a> System<'a> for TriggerSystem {
hidden.remove(*entity_id);
if let Some(damage) = inflicts_damage.get(*entity_id) {
particle_builder.request(pos.x, pos.y, rltk::RGB::named(rltk::ORANGE), rltk::RGB::named(rltk::BLACK), rltk::to_cp437('‼'), 200.0);
particle_builder.request(
pos.x,
pos.y,
rltk::RGB::named(rltk::ORANGE),
rltk::RGB::named(rltk::BLACK),
rltk::to_cp437('‼'),
200.0,
);
SufferDamage::new_damage(&mut inflict_damage, entity, damage.damage);
}
@@ -59,4 +82,3 @@ impl<'a> System<'a> for TriggerSystem {
entity_moved.clear();
}
}

View File

@@ -1,7 +1,11 @@
use rltk::{field_of_view, Point, RandomNumberGenerator};
use specs::prelude::*;
use crate::{components::{Player, Position, Viewshed}, GameLog, Hidden, map::Map, Name};
use crate::{
components::{Player, Position, Viewshed},
map::Map,
GameLog, Hidden, Name,
};
pub struct VisibilitySystem {}
@@ -15,11 +19,21 @@ impl<'a> System<'a> for VisibilitySystem {
WriteStorage<'a, Hidden>,
WriteExpect<'a, RandomNumberGenerator>,
ReadStorage<'a, Name>,
WriteExpect<'a, GameLog>
WriteExpect<'a, GameLog>,
);
fn run(&mut self, data: Self::SystemData) {
let (mut map, entities, mut viewshed, pos, player, mut hidden, mut rng, names, mut game_log) = data;
let (
mut map,
entities,
mut viewshed,
pos,
player,
mut hidden,
mut rng,
names,
mut game_log,
) = data;
for (ent, viewshed, pos) in (&entities, &mut viewshed, &pos).join() {
if !viewshed.dirty {
continue;
@@ -45,7 +59,9 @@ impl<'a> System<'a> for VisibilitySystem {
if let Some(_maybe_hidden) = hidden.get(*e) {
if rng.roll_dice(1, 24) == 1 {
if let Some(name) = names.get(*e) {
game_log.entries.push(format!("You spotted a {}.", &name.name));
game_log
.entries
.push(format!("You spotted a {}.", &name.name));
}
hidden.remove(*e);
}