Compare commits
	
		
			1 Commits
		
	
	
		
			main
			...
			make_monst
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| cca9e8f285 | 
| @ -1,16 +0,0 @@ | |||||||
| name: publish package |  | ||||||
| on: |  | ||||||
|   push: |  | ||||||
|     branches-ignore: |  | ||||||
|       - main |  | ||||||
| 
 |  | ||||||
| jobs: |  | ||||||
|   Explore-Gitea-Actions: |  | ||||||
|     runs-on: ubuntu-latest |  | ||||||
|     steps: |  | ||||||
|       - name: Check out repository code |  | ||||||
|         uses: actions/checkout@v4 |  | ||||||
|       - name: run tests |  | ||||||
|         run: | |  | ||||||
|           cd ${{ gitea.workspace }} |  | ||||||
|           cargo test |  | ||||||
| @ -1,28 +0,0 @@ | |||||||
| name: publish package |  | ||||||
| on: |  | ||||||
|   push: |  | ||||||
|     branches: |  | ||||||
|       - main   |  | ||||||
| 
 |  | ||||||
| jobs: |  | ||||||
|   Explore-Gitea-Actions: |  | ||||||
|     runs-on: ubuntu-latest |  | ||||||
|     steps: |  | ||||||
|       - name: Check out repository code |  | ||||||
|         uses: actions/checkout@v4 |  | ||||||
|       - name: run tests |  | ||||||
|         run: | |  | ||||||
|           cd ${{ gitea.workspace }} |  | ||||||
|           cargo test --release                     |  | ||||||
|       - name: build & release package |  | ||||||
|         run: | |  | ||||||
|           cd ${{ gitea.workspace }} |  | ||||||
|           cargo build --release |  | ||||||
|           cargo deb |  | ||||||
|           ls -al target/debian |  | ||||||
|           UPLOAD_FILE=$(ls target/debian/*.deb) |  | ||||||
|           curl --user jlusiardi:${{ secrets.UPLOAD_TOKEN }} --upload-file ${UPLOAD_FILE} https://gitea.intern.haus-in-hirschaid.de/api/packages/jlusiardi/debian/pool/bionic/main/upload           |  | ||||||
|           echo -e "${{ secrets.DEPLOY_PRIVATE_KEY }}" > /tmp/ssh_key |  | ||||||
|           chmod 0600 /tmp/ssh_key |  | ||||||
|           scp -o StrictHostKeyChecking=no -i /tmp/ssh_key ${UPLOAD_FILE} jlusiardi@april2023.lusiardi.de:/var/www/p17.lusiardi.de/el_diabolo |  | ||||||
|           ssh -o StrictHostKeyChecking=no -i /tmp/ssh_key jlusiardi@april2023.lusiardi.de /var/www/p17.lusiardi.de/el_diabolo/generate_index.sh |  | ||||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1,2 +1 @@ | |||||||
| /target | /target | ||||||
| *.profraw |  | ||||||
|  | |||||||
							
								
								
									
										347
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										347
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @ -50,46 +50,23 @@ version = "0.3.0" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" | checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" | ||||||
| 
 | 
 | ||||||
| [[package]] |  | ||||||
| name = "castaway" |  | ||||||
| version = "0.2.3" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5" |  | ||||||
| dependencies = [ |  | ||||||
|  "rustversion", |  | ||||||
| ] |  | ||||||
| 
 |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "cfg-if" | name = "cfg-if" | ||||||
| version = "1.0.0" | version = "1.0.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" | ||||||
| 
 | 
 | ||||||
| [[package]] |  | ||||||
| name = "compact_str" |  | ||||||
| version = "0.8.0" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "6050c3a16ddab2e412160b31f2c871015704239bca62f72f6e5f0be631d3f644" |  | ||||||
| dependencies = [ |  | ||||||
|  "castaway", |  | ||||||
|  "cfg-if", |  | ||||||
|  "itoa", |  | ||||||
|  "rustversion", |  | ||||||
|  "ryu", |  | ||||||
|  "static_assertions", |  | ||||||
| ] |  | ||||||
| 
 |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "crossterm" | name = "crossterm" | ||||||
| version = "0.28.1" | version = "0.27.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" | checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "bitflags 2.4.1", |  "bitflags 2.4.1", | ||||||
|  "crossterm_winapi", |  "crossterm_winapi", | ||||||
|  |  "libc", | ||||||
|  "mio", |  "mio", | ||||||
|  "parking_lot", |  "parking_lot", | ||||||
|  "rustix", |  | ||||||
|  "signal-hook", |  "signal-hook", | ||||||
|  "signal-hook-mio", |  "signal-hook-mio", | ||||||
|  "winapi", |  "winapi", | ||||||
| @ -104,41 +81,6 @@ dependencies = [ | |||||||
|  "winapi", |  "winapi", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] |  | ||||||
| name = "darling" |  | ||||||
| version = "0.20.10" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" |  | ||||||
| dependencies = [ |  | ||||||
|  "darling_core", |  | ||||||
|  "darling_macro", |  | ||||||
| ] |  | ||||||
| 
 |  | ||||||
| [[package]] |  | ||||||
| name = "darling_core" |  | ||||||
| version = "0.20.10" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" |  | ||||||
| dependencies = [ |  | ||||||
|  "fnv", |  | ||||||
|  "ident_case", |  | ||||||
|  "proc-macro2", |  | ||||||
|  "quote", |  | ||||||
|  "strsim", |  | ||||||
|  "syn", |  | ||||||
| ] |  | ||||||
| 
 |  | ||||||
| [[package]] |  | ||||||
| name = "darling_macro" |  | ||||||
| version = "0.20.10" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" |  | ||||||
| dependencies = [ |  | ||||||
|  "darling_core", |  | ||||||
|  "quote", |  | ||||||
|  "syn", |  | ||||||
| ] |  | ||||||
| 
 |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "either" | name = "either" | ||||||
| version = "1.9.0" | version = "1.9.0" | ||||||
| @ -147,10 +89,9 @@ checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "el_diabolo" | name = "el_diabolo" | ||||||
| version = "0.2.3" | version = "0.1.0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "crossterm", |  "crossterm", | ||||||
|  "macros", |  | ||||||
|  "petgraph", |  "petgraph", | ||||||
|  "rand", |  "rand", | ||||||
|  "ratatui", |  "ratatui", | ||||||
| @ -163,28 +104,12 @@ version = "1.0.1" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" | ||||||
| 
 | 
 | ||||||
| [[package]] |  | ||||||
| name = "errno" |  | ||||||
| version = "0.3.9" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" |  | ||||||
| dependencies = [ |  | ||||||
|  "libc", |  | ||||||
|  "windows-sys", |  | ||||||
| ] |  | ||||||
| 
 |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "fixedbitset" | name = "fixedbitset" | ||||||
| version = "0.4.2" | version = "0.4.2" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" | checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" | ||||||
| 
 | 
 | ||||||
| [[package]] |  | ||||||
| name = "fnv" |  | ||||||
| version = "1.0.7" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" |  | ||||||
| 
 |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "getrandom" | name = "getrandom" | ||||||
| version = "0.2.11" | version = "0.2.11" | ||||||
| @ -208,21 +133,9 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "heck" | name = "heck" | ||||||
| version = "0.5.0" | version = "0.4.1" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" | checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" | ||||||
| 
 |  | ||||||
| [[package]] |  | ||||||
| name = "hermit-abi" |  | ||||||
| version = "0.3.9" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" |  | ||||||
| 
 |  | ||||||
| [[package]] |  | ||||||
| name = "ident_case" |  | ||||||
| version = "1.0.1" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" |  | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "indexmap" | name = "indexmap" | ||||||
| @ -236,35 +149,19 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "indoc" | name = "indoc" | ||||||
| version = "2.0.5" | version = "2.0.4" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" | checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8" | ||||||
| 
 |  | ||||||
| [[package]] |  | ||||||
| name = "instability" |  | ||||||
| version = "0.3.2" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "b23a0c8dfe501baac4adf6ebbfa6eddf8f0c07f56b058cc1288017e32397846c" |  | ||||||
| dependencies = [ |  | ||||||
|  "quote", |  | ||||||
|  "syn", |  | ||||||
| ] |  | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "itertools" | name = "itertools" | ||||||
| version = "0.13.0" | version = "0.11.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" | checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "either", |  "either", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] |  | ||||||
| name = "itoa" |  | ||||||
| version = "1.0.11" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" |  | ||||||
| 
 |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "js-sys" | name = "js-sys" | ||||||
| version = "0.3.66" | version = "0.3.66" | ||||||
| @ -276,15 +173,9 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "libc" | name = "libc" | ||||||
| version = "0.2.161" | version = "0.2.149" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" | checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" | ||||||
| 
 |  | ||||||
| [[package]] |  | ||||||
| name = "linux-raw-sys" |  | ||||||
| version = "0.4.14" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" |  | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "lock_api" | name = "lock_api" | ||||||
| @ -311,23 +202,12 @@ dependencies = [ | |||||||
|  "hashbrown", |  "hashbrown", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] |  | ||||||
| name = "macros" |  | ||||||
| version = "0.1.0" |  | ||||||
| dependencies = [ |  | ||||||
|  "darling", |  | ||||||
|  "proc-macro2", |  | ||||||
|  "quote", |  | ||||||
|  "syn", |  | ||||||
| ] |  | ||||||
| 
 |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "mio" | name = "mio" | ||||||
| version = "1.0.2" | version = "0.8.9" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" | checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "hermit-abi", |  | ||||||
|  "libc", |  "libc", | ||||||
|  "log", |  "log", | ||||||
|  "wasi", |  "wasi", | ||||||
| @ -358,9 +238,9 @@ checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" | |||||||
| dependencies = [ | dependencies = [ | ||||||
|  "cfg-if", |  "cfg-if", | ||||||
|  "libc", |  "libc", | ||||||
|  "redox_syscall 0.4.1", |  "redox_syscall", | ||||||
|  "smallvec", |  "smallvec", | ||||||
|  "windows-targets 0.48.5", |  "windows-targets", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| @ -371,9 +251,9 @@ checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "petgraph" | name = "petgraph" | ||||||
| version = "0.6.5" | version = "0.6.4" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" | checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "fixedbitset", |  "fixedbitset", | ||||||
|  "indexmap", |  "indexmap", | ||||||
| @ -387,18 +267,18 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "proc-macro2" | name = "proc-macro2" | ||||||
| version = "1.0.89" | version = "1.0.69" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" | checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "unicode-ident", |  "unicode-ident", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "quote" | name = "quote" | ||||||
| version = "1.0.37" | version = "1.0.33" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" | checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "proc-macro2", |  "proc-macro2", | ||||||
| ] | ] | ||||||
| @ -435,23 +315,20 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "ratatui" | name = "ratatui" | ||||||
| version = "0.29.0" | version = "0.24.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" | checksum = "0ebc917cfb527a566c37ecb94c7e3fd098353516fb4eb6bea17015ade0182425" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "bitflags 2.4.1", |  "bitflags 2.4.1", | ||||||
|  "cassowary", |  "cassowary", | ||||||
|  "compact_str", |  | ||||||
|  "crossterm", |  "crossterm", | ||||||
|  "indoc", |  "indoc", | ||||||
|  "instability", |  | ||||||
|  "itertools", |  "itertools", | ||||||
|  "lru", |  "lru", | ||||||
|  "paste", |  "paste", | ||||||
|  "strum", |  "strum", | ||||||
|  "unicode-segmentation", |  "unicode-segmentation", | ||||||
|  "unicode-truncate", |  "unicode-width", | ||||||
|  "unicode-width 0.2.0", |  | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| @ -463,40 +340,12 @@ dependencies = [ | |||||||
|  "bitflags 1.3.2", |  "bitflags 1.3.2", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] |  | ||||||
| name = "redox_syscall" |  | ||||||
| version = "0.5.7" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" |  | ||||||
| dependencies = [ |  | ||||||
|  "bitflags 2.4.1", |  | ||||||
| ] |  | ||||||
| 
 |  | ||||||
| [[package]] |  | ||||||
| name = "rustix" |  | ||||||
| version = "0.38.37" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" |  | ||||||
| dependencies = [ |  | ||||||
|  "bitflags 2.4.1", |  | ||||||
|  "errno", |  | ||||||
|  "libc", |  | ||||||
|  "linux-raw-sys", |  | ||||||
|  "windows-sys", |  | ||||||
| ] |  | ||||||
| 
 |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "rustversion" | name = "rustversion" | ||||||
| version = "1.0.14" | version = "1.0.14" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" | checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" | ||||||
| 
 | 
 | ||||||
| [[package]] |  | ||||||
| name = "ryu" |  | ||||||
| version = "1.0.18" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" |  | ||||||
| 
 |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "scopeguard" | name = "scopeguard" | ||||||
| version = "1.2.0" | version = "1.2.0" | ||||||
| @ -515,9 +364,9 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "signal-hook-mio" | name = "signal-hook-mio" | ||||||
| version = "0.2.4" | version = "0.2.3" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" | checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "libc", |  "libc", | ||||||
|  "mio", |  "mio", | ||||||
| @ -539,32 +388,20 @@ version = "1.11.1" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" | checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" | ||||||
| 
 | 
 | ||||||
| [[package]] |  | ||||||
| name = "static_assertions" |  | ||||||
| version = "1.1.0" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" |  | ||||||
| 
 |  | ||||||
| [[package]] |  | ||||||
| name = "strsim" |  | ||||||
| version = "0.11.1" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" |  | ||||||
| 
 |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "strum" | name = "strum" | ||||||
| version = "0.26.3" | version = "0.25.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" | checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "strum_macros", |  "strum_macros", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "strum_macros" | name = "strum_macros" | ||||||
| version = "0.26.4" | version = "0.25.3" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" | checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "heck", |  "heck", | ||||||
|  "proc-macro2", |  "proc-macro2", | ||||||
| @ -575,9 +412,9 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "syn" | name = "syn" | ||||||
| version = "2.0.85" | version = "2.0.38" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" | checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "proc-macro2", |  "proc-macro2", | ||||||
|  "quote", |  "quote", | ||||||
| @ -596,28 +433,11 @@ version = "1.10.1" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" | checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" | ||||||
| 
 | 
 | ||||||
| [[package]] |  | ||||||
| name = "unicode-truncate" |  | ||||||
| version = "1.1.0" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" |  | ||||||
| dependencies = [ |  | ||||||
|  "itertools", |  | ||||||
|  "unicode-segmentation", |  | ||||||
|  "unicode-width 0.1.14", |  | ||||||
| ] |  | ||||||
| 
 |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "unicode-width" | name = "unicode-width" | ||||||
| version = "0.1.14" | version = "0.1.11" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" | checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" | ||||||
| 
 |  | ||||||
| [[package]] |  | ||||||
| name = "unicode-width" |  | ||||||
| version = "0.2.0" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" |  | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "version_check" | name = "version_check" | ||||||
| @ -631,12 +451,6 @@ version = "0.11.0+wasi-snapshot-preview1" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" | ||||||
| 
 | 
 | ||||||
| [[package]] |  | ||||||
| name = "wasite" |  | ||||||
| version = "0.1.0" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" |  | ||||||
| 
 |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "wasm-bindgen" | name = "wasm-bindgen" | ||||||
| version = "0.2.89" | version = "0.2.89" | ||||||
| @ -703,12 +517,11 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "whoami" | name = "whoami" | ||||||
| version = "1.5.2" | version = "1.4.1" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" | checksum = "22fc3756b8a9133049b26c7f61ab35416c130e8c09b660f5b3958b446f52cc50" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "redox_syscall 0.5.7", |  "wasm-bindgen", | ||||||
|  "wasite", |  | ||||||
|  "web-sys", |  "web-sys", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| @ -736,11 +549,11 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" | |||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "windows-sys" | name = "windows-sys" | ||||||
| version = "0.52.0" | version = "0.48.0" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "windows-targets 0.52.6", |  "windows-targets", | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| @ -749,29 +562,13 @@ version = "0.48.5" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "windows_aarch64_gnullvm 0.48.5", |  "windows_aarch64_gnullvm", | ||||||
|  "windows_aarch64_msvc 0.48.5", |  "windows_aarch64_msvc", | ||||||
|  "windows_i686_gnu 0.48.5", |  "windows_i686_gnu", | ||||||
|  "windows_i686_msvc 0.48.5", |  "windows_i686_msvc", | ||||||
|  "windows_x86_64_gnu 0.48.5", |  "windows_x86_64_gnu", | ||||||
|  "windows_x86_64_gnullvm 0.48.5", |  "windows_x86_64_gnullvm", | ||||||
|  "windows_x86_64_msvc 0.48.5", |  "windows_x86_64_msvc", | ||||||
| ] |  | ||||||
| 
 |  | ||||||
| [[package]] |  | ||||||
| name = "windows-targets" |  | ||||||
| version = "0.52.6" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" |  | ||||||
| dependencies = [ |  | ||||||
|  "windows_aarch64_gnullvm 0.52.6", |  | ||||||
|  "windows_aarch64_msvc 0.52.6", |  | ||||||
|  "windows_i686_gnu 0.52.6", |  | ||||||
|  "windows_i686_gnullvm", |  | ||||||
|  "windows_i686_msvc 0.52.6", |  | ||||||
|  "windows_x86_64_gnu 0.52.6", |  | ||||||
|  "windows_x86_64_gnullvm 0.52.6", |  | ||||||
|  "windows_x86_64_msvc 0.52.6", |  | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| @ -780,90 +577,42 @@ version = "0.48.5" | |||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" | ||||||
| 
 | 
 | ||||||
| [[package]] |  | ||||||
| name = "windows_aarch64_gnullvm" |  | ||||||
| version = "0.52.6" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" |  | ||||||
| 
 |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "windows_aarch64_msvc" | name = "windows_aarch64_msvc" | ||||||
| version = "0.48.5" | version = "0.48.5" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" | ||||||
| 
 | 
 | ||||||
| [[package]] |  | ||||||
| name = "windows_aarch64_msvc" |  | ||||||
| version = "0.52.6" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" |  | ||||||
| 
 |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "windows_i686_gnu" | name = "windows_i686_gnu" | ||||||
| version = "0.48.5" | version = "0.48.5" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" | ||||||
| 
 | 
 | ||||||
| [[package]] |  | ||||||
| name = "windows_i686_gnu" |  | ||||||
| version = "0.52.6" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" |  | ||||||
| 
 |  | ||||||
| [[package]] |  | ||||||
| name = "windows_i686_gnullvm" |  | ||||||
| version = "0.52.6" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" |  | ||||||
| 
 |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "windows_i686_msvc" | name = "windows_i686_msvc" | ||||||
| version = "0.48.5" | version = "0.48.5" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" | ||||||
| 
 | 
 | ||||||
| [[package]] |  | ||||||
| name = "windows_i686_msvc" |  | ||||||
| version = "0.52.6" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" |  | ||||||
| 
 |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "windows_x86_64_gnu" | name = "windows_x86_64_gnu" | ||||||
| version = "0.48.5" | version = "0.48.5" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" | ||||||
| 
 | 
 | ||||||
| [[package]] |  | ||||||
| name = "windows_x86_64_gnu" |  | ||||||
| version = "0.52.6" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" |  | ||||||
| 
 |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "windows_x86_64_gnullvm" | name = "windows_x86_64_gnullvm" | ||||||
| version = "0.48.5" | version = "0.48.5" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" | ||||||
| 
 | 
 | ||||||
| [[package]] |  | ||||||
| name = "windows_x86_64_gnullvm" |  | ||||||
| version = "0.52.6" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" |  | ||||||
| 
 |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "windows_x86_64_msvc" | name = "windows_x86_64_msvc" | ||||||
| version = "0.48.5" | version = "0.48.5" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" | ||||||
| 
 | 
 | ||||||
| [[package]] |  | ||||||
| name = "windows_x86_64_msvc" |  | ||||||
| version = "0.52.6" |  | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" |  | ||||||
| checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" |  | ||||||
| 
 |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "zerocopy" | name = "zerocopy" | ||||||
| version = "0.7.19" | version = "0.7.19" | ||||||
|  | |||||||
							
								
								
									
										25
									
								
								Cargo.toml
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								Cargo.toml
									
									
									
									
									
								
							| @ -1,28 +1,13 @@ | |||||||
| [workspace] |  | ||||||
| members = ["macros"] |  | ||||||
| 
 |  | ||||||
| [package] | [package] | ||||||
| name = "el_diabolo" | name = "el_diabolo" | ||||||
| version = "0.2.3" | version = "0.1.0" | ||||||
| edition = "2021" | edition = "2021" | ||||||
| 
 | 
 | ||||||
| # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||||||
| 
 | 
 | ||||||
| [dependencies] | [dependencies] | ||||||
| ratatui = "0.29.0" | ratatui = "0.24.0" | ||||||
| crossterm = "0.28.1" | crossterm = "0.27.0" | ||||||
| rand = "0.8.5" | rand = "0.8.5" | ||||||
| petgraph = "0.6.5" | petgraph = "0.6.4" | ||||||
| whoami = "1.5.2" | whoami = "1.4.1" | ||||||
| macros = { path = "./macros" } |  | ||||||
| 
 |  | ||||||
| [package.metadata.deb] |  | ||||||
| maintainer = "Joachim Lusiardi <joachim@lusiardi.de>" |  | ||||||
| copyright = "2024, Joachim Lusiardi <joachim@lusiardi.de>" |  | ||||||
| extended-description = """Little roguelike game""" |  | ||||||
| depends = "$auto" |  | ||||||
| section = "game" |  | ||||||
| priority = "optional" |  | ||||||
| assets = [ |  | ||||||
|     ["target/release/el_diabolo", "/usr/bin/", "755"], |  | ||||||
| ] |  | ||||||
							
								
								
									
										10
									
								
								build.rs
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								build.rs
									
									
									
									
									
								
							| @ -1,10 +0,0 @@ | |||||||
| use std::process::Command; |  | ||||||
| 
 |  | ||||||
| fn main() { |  | ||||||
|     let output = Command::new("git") |  | ||||||
|         .args(["rev-parse", "--short", "HEAD"]) |  | ||||||
|         .output() |  | ||||||
|         .unwrap(); |  | ||||||
|     let git_hash = String::from_utf8(output.stdout).unwrap(); |  | ||||||
|     println!("cargo:rustc-env=GIT_HASH={}", git_hash); |  | ||||||
| } |  | ||||||
							
								
								
									
										10
									
								
								coverage.md
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								coverage.md
									
									
									
									
									
								
							| @ -1,10 +0,0 @@ | |||||||
| ``` |  | ||||||
| cargo install grcov |  | ||||||
| rustup component add llvm-tools-preview |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| ``` |  | ||||||
| RUSTFLAGS="-Cinstrument-coverage" cargo clean |  | ||||||
| RUSTFLAGS="-Cinstrument-coverage" cargo test |  | ||||||
| grcov . --binary-path ./target/debug/deps/ -s . -t html --branch --ignore-not-existing --ignore '../*' --ignore "/*" -o target/coverage/html |  | ||||||
| ``` |  | ||||||
| @ -1,17 +0,0 @@ | |||||||
| [package] |  | ||||||
| name = "macros" |  | ||||||
| version = "0.1.0" |  | ||||||
| edition = "2021" |  | ||||||
| 
 |  | ||||||
| # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |  | ||||||
| [lib] |  | ||||||
| name = "macros" |  | ||||||
| path = "src/lib.rs" |  | ||||||
| proc-macro = true |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| [dependencies] |  | ||||||
| syn = "2.0.85" |  | ||||||
| quote = "1.0.37" |  | ||||||
| proc-macro2 = "1.0.89" |  | ||||||
| darling= "0.20.10" |  | ||||||
| @ -1,58 +0,0 @@ | |||||||
| extern crate proc_macro; |  | ||||||
| 
 |  | ||||||
| use proc_macro::TokenStream; |  | ||||||
| use quote::quote; |  | ||||||
| 
 |  | ||||||
| #[proc_macro_derive(CreateMonsters)] |  | ||||||
| pub fn create_monsters(input: TokenStream) -> TokenStream { |  | ||||||
|     let input = syn::parse_macro_input!(input as syn::File); |  | ||||||
|     let (enum_item, _) = match &input.items[0] { |  | ||||||
|         syn::Item::Enum(enum_item) => (enum_item, &enum_item.attrs), |  | ||||||
|         _ => panic!("must be an enum"), |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     let structs = enum_item.variants.iter().map(|variant| { |  | ||||||
|         let variant_name = &variant.ident; |  | ||||||
| 
 |  | ||||||
|         let t = quote! { |  | ||||||
|         pub struct #variant_name { |  | ||||||
|             name: String, |  | ||||||
|             life: usize, |  | ||||||
|             position: Position, |  | ||||||
|             symbol: String, |  | ||||||
|             color: Color, |  | ||||||
|             experience_gain: usize, |  | ||||||
|             ticks_between_steps: u128, |  | ||||||
|             damage_range: RangeInclusive<usize>, |  | ||||||
|             defense: usize, |  | ||||||
|         } |  | ||||||
|         impl Monster for #variant_name { |  | ||||||
|             fn get_name(&self) -> &str { &self.name } |  | ||||||
|             fn is_dead(&self) -> bool { self.life <= 0 } |  | ||||||
|             fn get_experience_gain(&self) -> usize { self.experience_gain } |  | ||||||
|             fn get_representation(&self) -> (&str, Color) { (&self.symbol, self.color) } |  | ||||||
|             fn decrease_life(&mut self, by: usize) { |  | ||||||
|                 self.life = self.life.saturating_sub(by); |  | ||||||
|             } |  | ||||||
|             fn get_ticks_between_steps(&self) -> u128 { self.ticks_between_steps } |  | ||||||
|             fn damage(&self) -> usize { rand::thread_rng().gen_range(self.damage_range.clone()) } |  | ||||||
|             fn defense(&self) -> usize { self.defense } |  | ||||||
|             #[cfg(test)] |  | ||||||
|             fn get_life(&self) -> usize { self.life } |  | ||||||
|         } |  | ||||||
|         impl HasPosition for #variant_name { |  | ||||||
|             fn get_position(&mut self) -> &mut Position { |  | ||||||
|                 &mut self.position |  | ||||||
|             } |  | ||||||
|             fn get_immutable_position(&self) -> &Position { |  | ||||||
|                 &self.position |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         }; |  | ||||||
|         t |  | ||||||
|     }); |  | ||||||
|     let output = quote::quote! { |  | ||||||
|         #(#structs)* |  | ||||||
|     }; |  | ||||||
|     output.into() |  | ||||||
| } |  | ||||||
| @ -1,4 +1,3 @@ | |||||||
| use rand::Rng; |  | ||||||
| use ratatui::style::Color; | use ratatui::style::Color; | ||||||
| 
 | 
 | ||||||
| use crate::player::Player; | use crate::player::Player; | ||||||
| @ -6,51 +5,40 @@ use crate::position::Position; | |||||||
| 
 | 
 | ||||||
| pub trait Artifact { | pub trait Artifact { | ||||||
|     //! An artifact that can be collected by the player
 |     //! An artifact that can be collected by the player
 | ||||||
|     /// get the character and color used to draw the artifact into the level
 |  | ||||||
|     fn get_representation(&self) -> (&str, Color); |     fn get_representation(&self) -> (&str, Color); | ||||||
|     /// get the position of the artifact in the level
 |     /// get the position of the artifact in the level
 | ||||||
|     fn get_immutable_position(&self) -> &Position; |     fn get_immutable_position(&self) -> &Position; | ||||||
|     /// call to apply the effects of the artifact to the player
 |     /// call to apply the effects of the artifact to the player
 | ||||||
|     fn collect(&mut self, player: &mut Player, messages: &mut Vec<String>); |     fn collect(&mut self, player: &mut Player); | ||||||
|     /// returns if the artifact was collected and can be removed from the level
 |     /// returns if the artifact was collected and can be removed from the level
 | ||||||
|     fn was_collected(&self) -> bool; |     fn was_collected(&self) -> bool; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// An artifact that contains a random amount of gold pieces.
 |  | ||||||
| pub struct Chest { | pub struct Chest { | ||||||
|     /// the chests position
 |     /// a chest that contains some gold
 | ||||||
|     position: Position, |     position: Position, | ||||||
|     /// the chests value
 |  | ||||||
|     gold: usize, |     gold: usize, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Chest { | impl Chest { | ||||||
|     /// create a chest at the given position with a random amount of gold.
 |  | ||||||
|     /// The gold amount depends on the level, the deeper you go, the more you get.
 |  | ||||||
|     pub fn new(position: Position) -> Self { |     pub fn new(position: Position) -> Self { | ||||||
|         let min_gold = 10 * (position.get_level() + 1); |         let level = position.get_level(); | ||||||
|         let max_gold = min_gold + 10 * position.get_level(); |  | ||||||
|         Self { |         Self { | ||||||
|             position, |             position, | ||||||
|             gold: rand::thread_rng().gen_range(min_gold..=max_gold), |             // TODO maybe randomize this?
 | ||||||
|  |             gold: (level + 1) * 10, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Artifact for Chest { | impl Artifact for Chest { | ||||||
|     fn get_representation(&self) -> (&str, Color) { |     fn get_representation(&self) -> (&str, Color) { | ||||||
|         ("C", Color::Cyan) |         ("C", Color::Blue) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn get_immutable_position(&self) -> &Position { |     fn get_immutable_position(&self) -> &Position { &self.position } | ||||||
|         &self.position |     fn collect(&mut self, player: &mut Player) { | ||||||
|     } |  | ||||||
|     fn collect(&mut self, player: &mut Player, messages: &mut Vec<String>) { |  | ||||||
|         player.retrieve_gold(self.gold); |         player.retrieve_gold(self.gold); | ||||||
|         messages.insert( |  | ||||||
|             0, |  | ||||||
|             format!("opened chest and collected {} gold.", self.gold).to_string(), |  | ||||||
|         ); |  | ||||||
|         self.gold = 0; |         self.gold = 0; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -59,64 +47,32 @@ impl Artifact for Chest { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Clone, Copy)] |  | ||||||
| /// An artifact that gives the player some health on consumption.
 |  | ||||||
| pub struct Potion { | pub struct Potion { | ||||||
|     /// a potion that restores some health
 |     /// a potion that restores some health
 | ||||||
|     position: Position, |     position: Position, | ||||||
|     health: usize, |     health: usize, | ||||||
|     was_collected: bool, |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Potion { | impl Potion { | ||||||
|     pub fn new(position: Position) -> Self { |     pub fn new(position: Position) -> Self { | ||||||
|         let min_health_gain = 5 + position.get_level(); |  | ||||||
|         let max_health_gain = min_health_gain + 3 * position.get_level(); |  | ||||||
|         Self { |         Self { | ||||||
|             position, |             position, | ||||||
|             health: rand::thread_rng().gen_range(min_health_gain..=max_health_gain), |             health: 5, | ||||||
|             was_collected: false, |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     pub fn get_health(&self) -> usize { |  | ||||||
|         self.health |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Artifact for Potion { | impl Artifact for Potion { | ||||||
|     fn get_representation(&self) -> (&str, Color) { |     fn get_representation(&self) -> (&str, Color) { | ||||||
|         ("P", Color::Green) |         ("P", Color::Green) | ||||||
|     } |     } | ||||||
| 
 |     fn get_immutable_position(&self) -> &Position { &self.position } | ||||||
|     fn get_immutable_position(&self) -> &Position { |     fn collect(&mut self, player: &mut Player) { | ||||||
|         &self.position |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn collect(&mut self, player: &mut Player, messages: &mut Vec<String>) { |  | ||||||
|         //! called when the player walked on to a potion.
 |  | ||||||
|         //!
 |  | ||||||
|         //! Depending on health status and inventory usage the potion will
 |  | ||||||
|         //! be consumed directly or moved to inventory.
 |  | ||||||
|         if !player.is_healthy() { |  | ||||||
|             let old = player.get_life(); |  | ||||||
|         player.change_life(self.health.try_into().unwrap()); |         player.change_life(self.health.try_into().unwrap()); | ||||||
|             let new = player.get_life(); |  | ||||||
|             messages.insert( |  | ||||||
|                 0, |  | ||||||
|                 format!("picked up potion and gained {} health.", new - old).to_string(), |  | ||||||
|             ); |  | ||||||
|         self.health = 0; |         self.health = 0; | ||||||
|             self.was_collected = true; |  | ||||||
|         } else if player.add_to_inventory(self) { |  | ||||||
|             messages.insert(0, "move potion to inventory.".to_string()); |  | ||||||
|             self.was_collected = true; |  | ||||||
|         } else { |  | ||||||
|             messages.insert(0, "inventory is full.".to_string()); |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn was_collected(&self) -> bool { |     fn was_collected(&self) -> bool { | ||||||
|         self.was_collected |         self.health == 0 | ||||||
|     } |     } | ||||||
| } | } | ||||||
							
								
								
									
										137
									
								
								src/constants.rs
									
									
									
									
									
								
							
							
						
						
									
										137
									
								
								src/constants.rs
									
									
									
									
									
								
							| @ -1,137 +0,0 @@ | |||||||
| use std::{collections::HashMap, ops::RangeInclusive}; |  | ||||||
| 
 |  | ||||||
| use crate::{monsters::MonsterTypes, room::RoomType}; |  | ||||||
| 
 |  | ||||||
| /// the number of rooms in vertical direction
 |  | ||||||
| pub const ROOMS_HORIZONTAL: usize = 8; |  | ||||||
| /// the number of rooms in horizontal direction
 |  | ||||||
| pub const ROOMS_VERTICAL: usize = 7; |  | ||||||
| 
 |  | ||||||
| /// the width of a room in the grid of rooms (number of characters)
 |  | ||||||
| pub const ROOM_WIDTH: usize = 9; |  | ||||||
| /// the height of a room in the grid of rooms (number of characters)
 |  | ||||||
| pub const ROOM_HEIGHT: usize = 6; |  | ||||||
| 
 |  | ||||||
| // the minmal width of a room
 |  | ||||||
| pub const ROOM_MIN_WIDTH: usize = 4; |  | ||||||
| 
 |  | ||||||
| // the minmal height of a room
 |  | ||||||
| pub const ROOM_MIN_HEIGHT: usize = 4; |  | ||||||
| 
 |  | ||||||
| /// How many levels does the dungeon have?
 |  | ||||||
| pub const LEVELS: usize = 4; |  | ||||||
| 
 |  | ||||||
| /// length of a game frame in ms
 |  | ||||||
| pub const FRAME_LENGTH: u64 = 100; |  | ||||||
| 
 |  | ||||||
| /// define the minimum width of a terminal to run the game, must be at least the level width plus some space for stats and messages
 |  | ||||||
| pub const MIN_WIDTH: u16 = 120; |  | ||||||
| /// define the minimum height of a terminal to run the game, this must be at least the level height!
 |  | ||||||
| pub const MIN_HEIGHT: u16 = LEVEL_HEIGHT as u16; |  | ||||||
| 
 |  | ||||||
| /// the calculated width of a level
 |  | ||||||
| pub const LEVEL_WIDTH: usize = 1 + ROOMS_HORIZONTAL * ROOM_WIDTH; |  | ||||||
| /// the calculated height of a level
 |  | ||||||
| pub const LEVEL_HEIGHT: usize = 1 + ROOMS_VERTICAL * ROOM_HEIGHT; |  | ||||||
| 
 |  | ||||||
| pub fn get_monsters_per_level() -> Vec<HashMap<MonsterTypes, std::ops::RangeInclusive<u8>>> { |  | ||||||
|     let tmp = [ |  | ||||||
|         // level 1
 |  | ||||||
|         vec![(MonsterTypes::Rat, 50), (MonsterTypes::Spider, 50)], |  | ||||||
|         // level 2
 |  | ||||||
|         vec![(MonsterTypes::Rat, 50), (MonsterTypes::Snake, 50)], |  | ||||||
|         // level 3
 |  | ||||||
|         vec![ |  | ||||||
|             (MonsterTypes::Orc, 34), |  | ||||||
|             (MonsterTypes::Skeleton, 33), |  | ||||||
|             (MonsterTypes::Snake, 33), |  | ||||||
|         ], |  | ||||||
|         // level 4
 |  | ||||||
|         vec![ |  | ||||||
|             (MonsterTypes::Orc, 34), |  | ||||||
|             (MonsterTypes::Skeleton, 33), |  | ||||||
|             (MonsterTypes::Snake, 33), |  | ||||||
|         ], |  | ||||||
|     ]; |  | ||||||
|     if tmp.len() < LEVELS { |  | ||||||
|         panic!( |  | ||||||
|             "Only {} monster sets defined for {} levels!", |  | ||||||
|             tmp.len(), |  | ||||||
|             LEVELS |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|     let mut result: Vec<HashMap<MonsterTypes, std::ops::RangeInclusive<u8>>> = vec![]; |  | ||||||
|     for (idx, level) in tmp.iter().enumerate() { |  | ||||||
|         let mut sum = 0; |  | ||||||
|         let mut map: HashMap<MonsterTypes, RangeInclusive<u8>> = HashMap::new(); |  | ||||||
|         for monster in level { |  | ||||||
|             map.insert(monster.0, RangeInclusive::new(sum + 1, sum + monster.1)); |  | ||||||
|             sum += monster.1; |  | ||||||
|         } |  | ||||||
|         if sum != 100 { |  | ||||||
|             panic!( |  | ||||||
|                 "all percentages must add to 100 (was {}) per level, error in level {}!", |  | ||||||
|                 sum, |  | ||||||
|                 idx + 1 |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|         result.push(map); |  | ||||||
|     } |  | ||||||
|     result |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| pub fn get_room_type_per_level() -> Vec<HashMap<RoomType, std::ops::RangeInclusive<u8>>> { |  | ||||||
|     let tmp = [ |  | ||||||
|         // level 1
 |  | ||||||
|         vec![ |  | ||||||
|             (RoomType::EmptyRoom, 50), |  | ||||||
|             (RoomType::ArtifactRoom, 10), |  | ||||||
|             (RoomType::MonsterRoom, 20), |  | ||||||
|             (RoomType::BasicRoom, 20), |  | ||||||
|         ], |  | ||||||
|         // level 2
 |  | ||||||
|         vec![ |  | ||||||
|             (RoomType::EmptyRoom, 50), |  | ||||||
|             (RoomType::BasicRoom, 25), |  | ||||||
|             (RoomType::MonsterRoom, 12), |  | ||||||
|             (RoomType::ArtifactRoom, 13), |  | ||||||
|         ], |  | ||||||
|         // level 3
 |  | ||||||
|         vec![ |  | ||||||
|             (RoomType::EmptyRoom, 50), |  | ||||||
|             (RoomType::BasicRoom, 25), |  | ||||||
|             (RoomType::MonsterRoom, 25), |  | ||||||
|         ], |  | ||||||
|         // level 4
 |  | ||||||
|         vec![ |  | ||||||
|             (RoomType::BasicRoom, 33), |  | ||||||
|             (RoomType::MonsterRoom, 33), |  | ||||||
|             (RoomType::ArtifactRoom, 34), |  | ||||||
|         ], |  | ||||||
|     ]; |  | ||||||
|     if tmp.len() < LEVELS { |  | ||||||
|         panic!( |  | ||||||
|             "Only {} room sets defined for {} levels!", |  | ||||||
|             tmp.len(), |  | ||||||
|             LEVELS |  | ||||||
|         ); |  | ||||||
|     } |  | ||||||
|     let mut result: Vec<HashMap<RoomType, std::ops::RangeInclusive<u8>>> = vec![]; |  | ||||||
|     for (idx, level) in tmp.iter().enumerate() { |  | ||||||
|         let mut sum = 0; |  | ||||||
|         let mut map: HashMap<RoomType, RangeInclusive<u8>> = HashMap::new(); |  | ||||||
|         for room in level { |  | ||||||
|             map.insert(room.0, RangeInclusive::new(sum + 1, sum + room.1)); |  | ||||||
|             sum += room.1; |  | ||||||
|         } |  | ||||||
|         if sum != 100 { |  | ||||||
|             panic!( |  | ||||||
|                 "all percentages must add to 100 (was {}) per level, error in level {}!", |  | ||||||
|                 sum, |  | ||||||
|                 idx + 1 |  | ||||||
|             ); |  | ||||||
|         } |  | ||||||
|         result.push(map); |  | ||||||
|     } |  | ||||||
|     result |  | ||||||
| } |  | ||||||
							
								
								
									
										131
									
								
								src/game.rs
									
									
									
									
									
								
							
							
						
						
									
										131
									
								
								src/game.rs
									
									
									
									
									
								
							| @ -1,28 +1,28 @@ | |||||||
| use crate::constants::LEVELS; |  | ||||||
| use crate::level::{Level, StructureElement}; | use crate::level::{Level, StructureElement}; | ||||||
| use crate::level_generator::LevelGenerator; | use crate::level_generator::LevelGenerator; | ||||||
| use crate::player::Player; | use crate::player::Player; | ||||||
| use crate::position::{HasPosition, Position}; | use crate::position::Position; | ||||||
|  | 
 | ||||||
|  | pub const LEVELS: usize = 10; | ||||||
| 
 | 
 | ||||||
| #[derive(PartialEq)] | #[derive(PartialEq)] | ||||||
| /// represents a state of a game
 | /// represents a state of a game
 | ||||||
| pub enum GameState { | pub enum GameState { | ||||||
|     /// the game is ongoing (neither won or lost)
 |     /// the game is ongoing (neither won or lost)
 | ||||||
|     Running, |     RUNNING, | ||||||
|     /// the player died
 |     /// the player died
 | ||||||
|     Lost, |     LOST, | ||||||
|     /// the player reached the Ω
 |     /// the player reached the Ω
 | ||||||
|     Won, |     WON, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| /// the main structure to hold all information about the ongoing game
 | /// the main structure to hold all information about the ongoing game
 | ||||||
| pub struct Game { | pub struct Game { | ||||||
|     /// the player
 |     /// the player
 | ||||||
|     player: Player, |     player: Player, | ||||||
|     /// the levels of the game
 |     /// the levels of the game
 | ||||||
|     levels: Vec<Level>, |     levels: Vec<Level>, | ||||||
|     /// messages that are displayed in the ui
 |  | ||||||
|     pub messages: Vec<String>, |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Game { | impl Game { | ||||||
| @ -34,24 +34,25 @@ impl Game { | |||||||
|         let mut g = Game { |         let mut g = Game { | ||||||
|             player: p, |             player: p, | ||||||
|             levels: v, |             levels: v, | ||||||
|             messages: Vec::with_capacity(10), |  | ||||||
|         }; |         }; | ||||||
|         let start = { g.get_level(0).start }; |         let start = { | ||||||
|         g.get_mutable_player() |             g.get_level(0).start | ||||||
|             .get_position() |         }; | ||||||
|             .set(0, start.0, start.1); |         g.get_mutable_player().get_position().set(0, start.0, start.1); | ||||||
|         g.get_mutable_level(0) |         g.get_mutable_level(0).discover(&Position::new(0, start.0, start.1)); | ||||||
|             .discover(&Position::new(0, start.0, start.1)); |  | ||||||
|         g |         g | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /// returns true if the player is dead (life <= 0)
 | ||||||
|  |     fn player_is_dead(&self) -> bool { self.get_player().get_life() <= 0 } | ||||||
|     /// returns true if the player is standing on the End element
 |     /// returns true if the player is standing on the End element
 | ||||||
|     fn player_reached_goal(&mut self) -> bool { |     fn player_reached_goal(&mut self) -> bool { | ||||||
|         match self.next_element(0, 0) { |         match self.next_element(0, 0) { | ||||||
|             None => {} |             None => {} | ||||||
|             Some(a) => { |             Some(a) => { | ||||||
|                 if a == StructureElement::End { |                 match a { | ||||||
|                     return true; |                     StructureElement::End => { return true; } | ||||||
|  |                     _ => {} | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         }; |         }; | ||||||
| @ -59,13 +60,13 @@ impl Game { | |||||||
|     } |     } | ||||||
|     /// returns the state of the game (depending on player's life and position)
 |     /// returns the state of the game (depending on player's life and position)
 | ||||||
|     pub fn get_game_state(&mut self) -> GameState { |     pub fn get_game_state(&mut self) -> GameState { | ||||||
|         if self.player.is_dead() { |         if self.player_is_dead() { | ||||||
|             return GameState::Lost; |             return GameState::LOST; | ||||||
|         } |         } | ||||||
|         if self.player_reached_goal() { |         if self.player_reached_goal() { | ||||||
|             return GameState::Won; |             return GameState::WON; | ||||||
|         } |         } | ||||||
|         GameState::Running |         GameState::RUNNING | ||||||
|     } |     } | ||||||
|     pub fn get_player(&self) -> &Player { |     pub fn get_player(&self) -> &Player { | ||||||
|         &self.player |         &self.player | ||||||
| @ -73,18 +74,25 @@ impl Game { | |||||||
|     pub fn get_mutable_player(&mut self) -> &mut Player { |     pub fn get_mutable_player(&mut self) -> &mut Player { | ||||||
|         &mut self.player |         &mut self.player | ||||||
|     } |     } | ||||||
|     pub fn get_level(&self, level: usize) -> &Level { |     pub fn get_level(&self, level: usize) -> &Level { &self.levels[level] } | ||||||
|         &self.levels[level] |     pub fn get_mutable_level(&mut self, level: usize) -> &mut Level { &mut self.levels[level] } | ||||||
|     } |  | ||||||
|     pub fn get_mutable_level(&mut self, level: usize) -> &mut Level { |  | ||||||
|         &mut self.levels[level] |  | ||||||
|     } |  | ||||||
|     /// check if the player can move in the given direction. This only checks for structure element
 |     /// check if the player can move in the given direction. This only checks for structure element
 | ||||||
|     /// limitation as walls.
 |     /// limitation as walls.
 | ||||||
|     fn can_move(&mut self, dx: i16, dy: i16) -> bool { |     fn can_move(&mut self, dx: i16, dy: i16) -> bool { | ||||||
|         let player_pos = &self.player.get_position(); |         let player_pos = &self.player.get_position(); | ||||||
|  |         let new_x: i16 = player_pos.get_x() as i16 + dx; | ||||||
|  |         let new_y: i16 = player_pos.get_y() as i16 + dy; | ||||||
|         let level = &mut self.levels[player_pos.get_level()]; |         let level = &mut self.levels[player_pos.get_level()]; | ||||||
|         level.can_player_move(&self.player, dx, dy) |         match level.get_element(new_x, new_y) { | ||||||
|  |             (None, _, _) => { return false; } | ||||||
|  |             (Some(t), _, _) => { | ||||||
|  |                 match t { | ||||||
|  |                     StructureElement::Wall => { return false; } | ||||||
|  |                     _ => {} | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  |         true | ||||||
|     } |     } | ||||||
|     /// returns the position (as tuple) of the next level's start point.
 |     /// returns the position (as tuple) of the next level's start point.
 | ||||||
|     fn next_start(&self) -> (usize, usize, usize) { |     fn next_start(&self) -> (usize, usize, usize) { | ||||||
| @ -123,39 +131,32 @@ impl Game { | |||||||
|         let structure = self.next_element(dx, dy); |         let structure = self.next_element(dx, dy); | ||||||
| 
 | 
 | ||||||
|         // use stairs if walked onto one
 |         // use stairs if walked onto one
 | ||||||
|         if let Some(e) = structure { |         match structure { | ||||||
|  |             Some(e) => { | ||||||
|                 match e { |                 match e { | ||||||
|                     StructureElement::StairDown => { |                     StructureElement::StairDown => { | ||||||
|                         (dx, dy) = (0, 0); |                         (dx, dy) = (0, 0); | ||||||
|                         let (next_level, x, y) = self.next_start(); |                         let (next_level, x, y) = self.next_start(); | ||||||
|                         player_level = next_level; |                         player_level = next_level; | ||||||
|                     self.messages |                         self.get_mutable_player().get_position().set(next_level, x, y); | ||||||
|                         .insert(0, format!("you climb down to level {}.", next_level + 1)); |  | ||||||
|                     self.get_mutable_player() |  | ||||||
|                         .get_position() |  | ||||||
|                         .set(next_level, x, y); |  | ||||||
|                     } |                     } | ||||||
|                     StructureElement::StairUp => { |                     StructureElement::StairUp => { | ||||||
|                         (dx, dy) = (0, 0); |                         (dx, dy) = (0, 0); | ||||||
|                         let (next_level, x, y) = self.prev_end(); |                         let (next_level, x, y) = self.prev_end(); | ||||||
|                         player_level = next_level; |                         player_level = next_level; | ||||||
|                     self.messages |                         self.get_mutable_player().get_position().set(next_level, x, y); | ||||||
|                         .insert(0, format!("you climb up to level {}.", next_level + 1)); |                     } | ||||||
|                     self.get_mutable_player() |                     _ => {} | ||||||
|                         .get_position() |                 } | ||||||
|                         .set(next_level, x, y); |  | ||||||
|             } |             } | ||||||
|             _ => {} |             _ => {} | ||||||
|         } |         } | ||||||
|         }; |  | ||||||
| 
 | 
 | ||||||
|         // update position and discover area on new position
 |         // update position and discover area on new position
 | ||||||
|         let (new_x, new_y) = self.get_mutable_player().get_position().change(dx, dy); |         let (new_x, new_y) = self.get_mutable_player().get_position().change(dx, dy); | ||||||
|         self.get_mutable_level(player_level) |         self.get_mutable_level(player_level).discover(&Position::new(player_level, new_x, new_y)); | ||||||
|             .discover(&Position::new(player_level, new_x, new_y)); |  | ||||||
|         (-dx, -dy) |         (-dx, -dy) | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     pub fn player_fights_monster(&mut self) -> bool { |     pub fn player_fights_monster(&mut self) -> bool { | ||||||
|         let player_pos = &self.player.get_immutable_position(); |         let player_pos = &self.player.get_immutable_position(); | ||||||
|         let player_level = player_pos.get_level(); |         let player_level = player_pos.get_level(); | ||||||
| @ -164,42 +165,12 @@ impl Game { | |||||||
|         match m { |         match m { | ||||||
|             None => {} |             None => {} | ||||||
|             Some(m) => { |             Some(m) => { | ||||||
|                 let player_dmg = self.player.damage().saturating_sub(m.defense()); |                 // TODO fight the monster
 | ||||||
|                 m.decrease_life(player_dmg); |                 self.player.change_life(-1); | ||||||
|  |                 m.decrease_life(1); | ||||||
|  |                 // monster died, player gains experience
 | ||||||
|                 if m.is_dead() { |                 if m.is_dead() { | ||||||
|                     let level_up: bool = self.player.gain_experience(m.get_experience_gain()); |                     self.player.gain_experience(m.get_experience_gain()); | ||||||
|                     self.messages.insert( |  | ||||||
|                         0, |  | ||||||
|                         format!( |  | ||||||
|                             "you hit {} for {} damage and kill it.", |  | ||||||
|                             m.get_name(), |  | ||||||
|                             player_dmg |  | ||||||
|                         ) |  | ||||||
|                         .to_string(), |  | ||||||
|                     ); |  | ||||||
|                     if level_up { |  | ||||||
|                         self.messages.insert( |  | ||||||
|                             0, |  | ||||||
|                             format!( |  | ||||||
|                                 "you reach a new experience level, it's now {}", |  | ||||||
|                                 self.player.get_experience_level(), |  | ||||||
|                             ) |  | ||||||
|                             .to_string(), |  | ||||||
|                         ); |  | ||||||
|                     } |  | ||||||
|                 } else { |  | ||||||
|                     self.messages.insert( |  | ||||||
|                         0, |  | ||||||
|                         format!("you hit {} for {} damage.", m.get_name(), player_dmg).to_string(), |  | ||||||
|                     ); |  | ||||||
|                     let monster_dmg = m.damage().saturating_sub(self.player.defense()); |  | ||||||
|                     self.player.change_life(-(monster_dmg as i16)); |  | ||||||
| 
 |  | ||||||
|                     self.messages.insert( |  | ||||||
|                         0, |  | ||||||
|                         format!("{} hits you for {} damage.", m.get_name(), monster_dmg) |  | ||||||
|                             .to_string(), |  | ||||||
|                     ); |  | ||||||
|                 } |                 } | ||||||
|                 return m.is_dead(); |                 return m.is_dead(); | ||||||
|             } |             } | ||||||
| @ -214,16 +185,16 @@ impl Game { | |||||||
|         match a { |         match a { | ||||||
|             None => {} |             None => {} | ||||||
|             Some(a) => { |             Some(a) => { | ||||||
|                 a.collect(&mut self.player, &mut self.messages); |                 a.collect(&mut self.player); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     /// updates the player's current level. This will remove collected artifacts and dead monsters.
 |     /// updates the player's current level. This will remove collected artifacts and dead monsters.
 | ||||||
|     pub fn update_level(&mut self, ticks: u128) { |     pub fn update_level(&mut self, ticks: u64) { | ||||||
|         let player_pos = &self.player.get_immutable_position(); |         let player_pos = &self.player.get_immutable_position(); | ||||||
|         let player_level = player_pos.get_level(); |         let player_level = player_pos.get_level(); | ||||||
|         let level = &mut self.levels[player_level]; |         let level = &mut self.levels[player_level]; | ||||||
|         level.update(ticks, &mut self.player, &mut self.messages); |         level.update(ticks); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										278
									
								
								src/level.rs
									
									
									
									
									
								
							
							
						
						
									
										278
									
								
								src/level.rs
									
									
									
									
									
								
							| @ -1,20 +1,16 @@ | |||||||
| use std::cmp::{max, min}; | use std::cmp::{max, min}; | ||||||
| 
 | 
 | ||||||
| use rand::rngs::ThreadRng; |  | ||||||
| use rand::Rng; |  | ||||||
| 
 |  | ||||||
| use crate::artifacts::Artifact; |  | ||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
| use crate::artifacts::{Chest, Potion}; | use crate::artifacts::{Chest, Potion}; | ||||||
| use crate::constants::LEVEL_HEIGHT; | use crate::artifacts::Artifact; | ||||||
| use crate::constants::LEVEL_WIDTH; | use crate::monster::Monster; | ||||||
| use crate::monsters::Monster; |  | ||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
| use crate::monsters::{Orc, Rat}; | use crate::monster::Rat; | ||||||
| use crate::player::Player; |  | ||||||
| use crate::position::HasPosition; |  | ||||||
| use crate::position::Position; | use crate::position::Position; | ||||||
| 
 | 
 | ||||||
|  | pub const LEVEL_WIDTH: usize = 50; | ||||||
|  | pub const LEVEL_HEIGHT: usize = 25; | ||||||
|  | 
 | ||||||
| #[derive(Copy, Clone, Debug, PartialEq)] | #[derive(Copy, Clone, Debug, PartialEq)] | ||||||
| pub enum StructureElement { | pub enum StructureElement { | ||||||
|     Start, |     Start, | ||||||
| @ -26,9 +22,6 @@ pub enum StructureElement { | |||||||
|     Unknown, |     Unknown, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type PossibleArtifact<'a> = Option<&'a mut Box<(dyn Artifact + 'static)>>; |  | ||||||
| type PossibleMonster<'a> = Option<&'a mut Box<(dyn Monster + 'static)>>; |  | ||||||
| 
 |  | ||||||
| pub struct Level { | pub struct Level { | ||||||
|     pub(crate) level: usize, |     pub(crate) level: usize, | ||||||
|     pub(crate) structure: [[StructureElement; LEVEL_HEIGHT]; LEVEL_WIDTH], |     pub(crate) structure: [[StructureElement; LEVEL_HEIGHT]; LEVEL_WIDTH], | ||||||
| @ -39,15 +32,28 @@ pub struct Level { | |||||||
|     pub(crate) start: (usize, usize), |     pub(crate) start: (usize, usize), | ||||||
|     /// the position of the end in the level (either stair down or end point)
 |     /// the position of the end in the level (either stair down or end point)
 | ||||||
|     pub(crate) end: (usize, usize), |     pub(crate) end: (usize, usize), | ||||||
|     pub(crate) rng: ThreadRng, |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Level { | impl Level { | ||||||
|     pub fn get_element( |     #[cfg(test)] | ||||||
|         &mut self, |     pub fn new(level: usize) -> Level { | ||||||
|         x: i16, |         let mut s = [[StructureElement::Wall; LEVEL_HEIGHT]; LEVEL_WIDTH]; | ||||||
|         y: i16, |         for x in 2..LEVEL_WIDTH - 2 { | ||||||
|     ) -> (Option<StructureElement>, PossibleMonster, PossibleArtifact) { |             for y in 2..LEVEL_HEIGHT - 2 { | ||||||
|  |                 s[x][y] = StructureElement::Floor; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         Level { | ||||||
|  |             level, | ||||||
|  |             structure: s, | ||||||
|  |             discovered: [[false; LEVEL_HEIGHT]; LEVEL_WIDTH], | ||||||
|  |             monsters: Vec::with_capacity(10), | ||||||
|  |             artifacts: Vec::with_capacity(10), | ||||||
|  |             start: (0, 0), | ||||||
|  |             end: (0, 0), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     pub fn get_element(&mut self, x: i16, y: i16) -> (Option<StructureElement>, Option<&mut Box<(dyn Monster + 'static)>>, Option<&mut Box<(dyn Artifact + 'static)>>) { | ||||||
|         if x < 0 || y < 0 { |         if x < 0 || y < 0 { | ||||||
|             return (None, None, None); |             return (None, None, None); | ||||||
|         } |         } | ||||||
| @ -57,7 +63,6 @@ impl Level { | |||||||
|             return (None, None, None); |             return (None, None, None); | ||||||
|         } |         } | ||||||
|         if !self.discovered[x][y] { |         if !self.discovered[x][y] { | ||||||
|             // #[cfg(test)]
 |  | ||||||
|             return (Some(StructureElement::Unknown), None, None); |             return (Some(StructureElement::Unknown), None, None); | ||||||
|         } |         } | ||||||
|         let search_pos = &Position::new(self.level, x, y); |         let search_pos = &Position::new(self.level, x, y); | ||||||
| @ -75,6 +80,33 @@ impl Level { | |||||||
|         } |         } | ||||||
|         (Some(self.structure[x][y]), res_m, res_a) |         (Some(self.structure[x][y]), res_m, res_a) | ||||||
|     } |     } | ||||||
|  |     #[cfg(test)] | ||||||
|  |     pub fn add_monster(&mut self, mut monster: impl Monster + 'static) -> Result<(), String> { | ||||||
|  |         if self.level != monster.get_position().get_level() { | ||||||
|  |             return Err("Wrong Level".to_string()); | ||||||
|  |         } | ||||||
|  |         for m in &mut self.monsters { | ||||||
|  |             if m.get_position() == monster.get_position() { | ||||||
|  |                 return Err("Position already used".to_string()); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         self.monsters.push(Box::new(monster)); | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     #[cfg(test)] | ||||||
|  |     pub fn add_artifact(&mut self, artifact: impl Artifact + 'static) -> Result<(), String> { | ||||||
|  |         if self.level != artifact.get_immutable_position().get_level() { | ||||||
|  |             return Err("Wrong Level".to_string()); | ||||||
|  |         } | ||||||
|  |         for a in &mut self.artifacts { | ||||||
|  |             if a.get_immutable_position() == artifact.get_immutable_position() { | ||||||
|  |                 return Err("Position already used".to_string()); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         self.artifacts.push(Box::new(artifact)); | ||||||
|  |         Ok(()) | ||||||
|  |     } | ||||||
|     /// discover the area with in the level around the given position
 |     /// discover the area with in the level around the given position
 | ||||||
|     pub fn discover(&mut self, pos: &Position) { |     pub fn discover(&mut self, pos: &Position) { | ||||||
|         let x = pos.get_x(); |         let x = pos.get_x(); | ||||||
| @ -119,186 +151,29 @@ impl Level { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     pub fn update(&mut self, ticks: u128, player: &mut Player, messages: &mut Vec<String>) { | 
 | ||||||
|  |     pub fn update(&mut self, ticks: u64) { | ||||||
|         for (index, a) in &mut self.artifacts.iter().enumerate() { |         for (index, a) in &mut self.artifacts.iter().enumerate() { | ||||||
|             if a.was_collected() { |             if a.was_collected() { | ||||||
|                 self.artifacts.remove(index); |                 self.artifacts.remove(index); | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         for index in 0..self.monsters.len() { |  | ||||||
|             if self.monsters[index].is_dead() { |  | ||||||
|                 continue; |  | ||||||
|             } |  | ||||||
|             if ticks % self.monsters[index].get_ticks_between_steps() != 0 { |  | ||||||
|                 continue; |  | ||||||
|             } |  | ||||||
|             loop { |  | ||||||
|                 // calculate the direction the monster will try to walk
 |  | ||||||
|                 let (dx, dy) = match self.rng.gen_range(0..5) { |  | ||||||
|                     1 => (1, 0), |  | ||||||
|                     2 => (-1, 0), |  | ||||||
|                     3 => (0, 1), |  | ||||||
|                     4 => (0, -1), |  | ||||||
|                     _ => (0, 0), |  | ||||||
|                 }; |  | ||||||
|                 if self.can_monster_move(self.monsters[index].as_ref(), dx, dy) { |  | ||||||
|                     let (new_x, new_y) = self.monsters[index].get_position().change(dx, dy); |  | ||||||
|                     if player.get_immutable_position().get_x() == new_x |  | ||||||
|                         && player.get_immutable_position().get_y() == new_y |  | ||||||
|                     { |  | ||||||
|                         let monster_dmg = self.monsters[index] |  | ||||||
|                             .damage() |  | ||||||
|                             .saturating_sub(player.defense()); |  | ||||||
|                         player.change_life(-(monster_dmg as i16)); |  | ||||||
|                         if player.is_dead() { |  | ||||||
|                             messages.insert( |  | ||||||
|                                 0, |  | ||||||
|                                 format!( |  | ||||||
|                                     "{} hits you for {} damage and kills you.", |  | ||||||
|                                     self.monsters[index].get_name(), |  | ||||||
|                                     monster_dmg |  | ||||||
|                                 ) |  | ||||||
|                                 .to_string(), |  | ||||||
|                             ); |  | ||||||
|                         } else { |  | ||||||
|                             messages.insert( |  | ||||||
|                                 0, |  | ||||||
|                                 format!( |  | ||||||
|                                     "{} hits you for {} damage.", |  | ||||||
|                                     self.monsters[index].get_name(), |  | ||||||
|                                     monster_dmg |  | ||||||
|                                 ) |  | ||||||
|                                 .to_string(), |  | ||||||
|                             ); |  | ||||||
|                             // player not dead => monster can occupy new position
 |  | ||||||
|                             self.monsters[index].get_position().change(-dx, -dy); |  | ||||||
|                             // player fights back
 |  | ||||||
|                             let player_dmg = player |  | ||||||
|                                 .damage() |  | ||||||
|                                 .saturating_sub(self.monsters[index].defense()); |  | ||||||
|                             self.monsters[index].decrease_life(player_dmg); |  | ||||||
|                             if self.monsters[index].is_dead() { |  | ||||||
|                                 let level_up: bool = player |  | ||||||
|                                     .gain_experience(self.monsters[index].get_experience_gain()); |  | ||||||
|                                 messages.insert( |  | ||||||
|                                     0, |  | ||||||
|                                     format!( |  | ||||||
|                                         "you hit {} for {} damage and kill it.", |  | ||||||
|                                         self.monsters[index].get_name(), |  | ||||||
|                                         player_dmg |  | ||||||
|                                     ) |  | ||||||
|                                     .to_string(), |  | ||||||
|                                 ); |  | ||||||
|                                 if level_up { |  | ||||||
|                                     messages.insert( |  | ||||||
|                                         0, |  | ||||||
|                                         format!( |  | ||||||
|                                             "you reach a new experience level, it's now {}", |  | ||||||
|                                             player.get_experience_level(), |  | ||||||
|                                         ) |  | ||||||
|                                         .to_string(), |  | ||||||
|                                     ); |  | ||||||
|                                 } |  | ||||||
|                             } else { |  | ||||||
|                                 messages.insert( |  | ||||||
|                                     0, |  | ||||||
|                                     format!( |  | ||||||
|                                         "you hit {} for {} damage.", |  | ||||||
|                                         self.monsters[index].get_name(), |  | ||||||
|                                         player_dmg |  | ||||||
|                                     ) |  | ||||||
|                                     .to_string(), |  | ||||||
|                                 ); |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     break; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         for (index, m) in &mut self.monsters.iter().enumerate() { |         for (index, m) in &mut self.monsters.iter().enumerate() { | ||||||
|             if m.is_dead() { |             if m.is_dead() { | ||||||
|                 self.monsters.remove(index); |                 self.monsters.remove(index); | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub fn can_monster_move(&self, agent: &dyn Monster, dx: i16, dy: i16) -> bool { |  | ||||||
|         let agent_pos = agent.get_immutable_position(); |  | ||||||
|         let new_x: usize = (agent_pos.get_x() as i16 + dx) as usize; |  | ||||||
|         let new_y: usize = (agent_pos.get_y() as i16 + dy) as usize; |  | ||||||
| 
 |  | ||||||
|         // verify we cannot leave the playing field
 |  | ||||||
|         if new_x >= LEVEL_WIDTH || new_y >= LEVEL_HEIGHT { |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         for index in 0..self.monsters.len() { |         for index in 0..self.monsters.len() { | ||||||
|             let pos = self.monsters[index].get_immutable_position(); |             let mut dx = 0; | ||||||
|             if pos.get_x() == new_x && pos.get_y() == new_y { |             let speed = 50; | ||||||
|                 return false; |             if ticks % speed == 0 { | ||||||
|             } |                 dx = if (ticks / speed) % 2 == 0 { -1 } else { 1 }; | ||||||
|         } |  | ||||||
|         self.structure[new_x][new_y] != StructureElement::Wall |  | ||||||
|     } |  | ||||||
|     pub fn can_player_move(&self, agent: &Player, dx: i16, dy: i16) -> bool { |  | ||||||
|         let agent_pos = agent.get_immutable_position(); |  | ||||||
|         let new_x: usize = (agent_pos.get_x() as i16 + dx) as usize; |  | ||||||
|         let new_y: usize = (agent_pos.get_y() as i16 + dy) as usize; |  | ||||||
| 
 |  | ||||||
|         // verify we cannot leave the playing field
 |  | ||||||
|         if new_x >= LEVEL_WIDTH || new_y >= LEVEL_HEIGHT { |  | ||||||
|             return false; |  | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|         self.structure[new_x][new_y] != StructureElement::Wall |             self.monsters[index].get_position().change(dx, 0); | ||||||
|         } |         } | ||||||
|     #[cfg(test)] |  | ||||||
|     pub fn new(level: usize) -> Level { |  | ||||||
|         let mut s = [[StructureElement::Wall; LEVEL_HEIGHT]; LEVEL_WIDTH]; |  | ||||||
|         for x in 2..LEVEL_WIDTH - 2 { |  | ||||||
|             for y in 2..LEVEL_HEIGHT - 2 { |  | ||||||
|                 s[x][y] = StructureElement::Floor; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         Level { |  | ||||||
|             level, |  | ||||||
|             structure: s, |  | ||||||
|             discovered: [[false; LEVEL_HEIGHT]; LEVEL_WIDTH], |  | ||||||
|             monsters: Vec::with_capacity(10), |  | ||||||
|             artifacts: Vec::with_capacity(10), |  | ||||||
|             start: (0, 0), |  | ||||||
|             end: (0, 0), |  | ||||||
|             rng: rand::thread_rng(), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     #[cfg(test)] |  | ||||||
|     pub fn add_monster(&mut self, mut monster: impl Monster + 'static) -> Result<(), String> { |  | ||||||
|         if self.level != monster.get_position().get_level() { |  | ||||||
|             return Err("Wrong Level".to_string()); |  | ||||||
|         } |  | ||||||
|         for m in &mut self.monsters { |  | ||||||
|             if m.get_position() == monster.get_position() { |  | ||||||
|                 return Err("Position already used".to_string()); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         self.monsters.push(Box::new(monster)); |  | ||||||
|         Ok(()) |  | ||||||
|     } |  | ||||||
|     #[cfg(test)] |  | ||||||
|     pub fn add_artifact(&mut self, artifact: impl Artifact + 'static) -> Result<(), String> { |  | ||||||
|         if self.level != artifact.get_immutable_position().get_level() { |  | ||||||
|             return Err("Wrong Level".to_string()); |  | ||||||
|         } |  | ||||||
|         for a in &mut self.artifacts { |  | ||||||
|             if a.get_immutable_position() == artifact.get_immutable_position() { |  | ||||||
|                 return Err("Position already used".to_string()); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         self.artifacts.push(Box::new(artifact)); |  | ||||||
|         Ok(()) |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -307,16 +182,8 @@ fn test_get_element() { | |||||||
|     let mut l = Level::new(0); |     let mut l = Level::new(0); | ||||||
|     assert_eq!(l.get_element(-1, -1).0, None); |     assert_eq!(l.get_element(-1, -1).0, None); | ||||||
|     assert_eq!(l.get_element(0, 0).0.unwrap(), StructureElement::Unknown); |     assert_eq!(l.get_element(0, 0).0.unwrap(), StructureElement::Unknown); | ||||||
|     assert_eq!( |     assert_eq!(l.get_element(LEVEL_WIDTH as i16 - 1, LEVEL_HEIGHT as i16 - 1).0.unwrap(), StructureElement::Unknown); | ||||||
|         l.get_element(LEVEL_WIDTH as i16 - 1, LEVEL_HEIGHT as i16 - 1) |     assert_eq!(l.get_element(LEVEL_WIDTH as i16, LEVEL_HEIGHT as i16).0, None); | ||||||
|             .0 |  | ||||||
|             .unwrap(), |  | ||||||
|         StructureElement::Unknown |  | ||||||
|     ); |  | ||||||
|     assert_eq!( |  | ||||||
|         l.get_element(LEVEL_WIDTH as i16, LEVEL_HEIGHT as i16).0, |  | ||||||
|         None |  | ||||||
|     ); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[test] | #[test] | ||||||
| @ -335,17 +202,20 @@ fn test_discover_get_element() { | |||||||
| #[test] | #[test] | ||||||
| fn test_discover_can_add_monster() { | fn test_discover_can_add_monster() { | ||||||
|     let mut l = Level::new(0); |     let mut l = Level::new(0); | ||||||
|     let m = Rat::new_with_position(Position::new(1, 2, 3)); |     let mut m = Rat::new(2); | ||||||
|  |     m.get_position().set(1, 2, 3); | ||||||
|     assert_eq!(l.add_monster(m), Err("Wrong Level".to_string())); |     assert_eq!(l.add_monster(m), Err("Wrong Level".to_string())); | ||||||
| 
 | 
 | ||||||
|     let mut m = Orc::new_with_position(Position::new(0, 2, 3)); |     let mut m = Rat::new(2); | ||||||
|     m.get_position().set(0, 2, 3); |     m.get_position().set(0, 2, 3); | ||||||
|     assert_eq!(l.add_monster(m), Ok(())); |     assert_eq!(l.add_monster(m), Ok(())); | ||||||
| 
 | 
 | ||||||
|     let m = Rat::new_with_position(Position::new(0, 2, 3)); |     let mut m = Rat::new(2); | ||||||
|  |     m.get_position().set(0, 2, 3); | ||||||
|     assert_eq!(l.add_monster(m), Err("Position already used".to_string())); |     assert_eq!(l.add_monster(m), Err("Position already used".to_string())); | ||||||
| 
 | 
 | ||||||
|     let m = Rat::new_with_position(Position::new(0, 2, 4)); |     let mut m = Rat::new(2); | ||||||
|  |     m.get_position().set(0, 2, 4); | ||||||
|     assert_eq!(l.add_monster(m), Ok(())); |     assert_eq!(l.add_monster(m), Ok(())); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -373,17 +243,18 @@ fn test_discover_get_monster() { | |||||||
|     assert_eq!(l.get_element(10, 10).0.unwrap(), StructureElement::Floor); |     assert_eq!(l.get_element(10, 10).0.unwrap(), StructureElement::Floor); | ||||||
|     assert!(l.get_element(10, 10).1.is_none()); |     assert!(l.get_element(10, 10).1.is_none()); | ||||||
| 
 | 
 | ||||||
|     let m = Rat::new_with_position(Position::new(0, 10, 10)); |     let mut m = Rat::new(23); | ||||||
|  |     m.get_position().set(0, 10, 10); | ||||||
|     assert_eq!(l.add_monster(m), Ok(())); |     assert_eq!(l.add_monster(m), Ok(())); | ||||||
| 
 | 
 | ||||||
|     let elem = l.get_element(10, 10); |     let elem = l.get_element(10, 10); | ||||||
|     assert_eq!(elem.0.unwrap(), StructureElement::Floor); |     assert_eq!(elem.0.unwrap(), StructureElement::Floor); | ||||||
|     assert!(elem.1.is_some()); |     assert!(elem.1.is_some()); | ||||||
|     let m = elem.1.unwrap(); |     let m = elem.1.unwrap(); | ||||||
|     assert_eq!(m.get_life(), 2); |     assert_eq!(m.get_life(), 23); | ||||||
| 
 | 
 | ||||||
|     m.decrease_life(2); |     m.decrease_life(2); | ||||||
|     assert_eq!(l.get_element(10, 10).1.unwrap().get_life(), 0); |     assert_eq!(l.get_element(10, 10).1.unwrap().get_life(), 21); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[test] | #[test] | ||||||
| @ -408,7 +279,8 @@ fn test_discover_get_monster_can_move() { | |||||||
|     let p = Position::new(0, 10, 10); |     let p = Position::new(0, 10, 10); | ||||||
|     l.discover(&p); |     l.discover(&p); | ||||||
| 
 | 
 | ||||||
|     let m = Rat::new_with_position(Position::new(0, 10, 10)); |     let mut m = Rat::new(23); | ||||||
|  |     m.get_position().set(0, 10, 10); | ||||||
|     l.add_monster(m).expect("Panic because of"); |     l.add_monster(m).expect("Panic because of"); | ||||||
| 
 | 
 | ||||||
|     let m = l.get_element(10, 10).1.unwrap(); |     let m = l.get_element(10, 10).1.unwrap(); | ||||||
| @ -418,5 +290,5 @@ fn test_discover_get_monster_can_move() { | |||||||
|     assert!(m.is_none()); |     assert!(m.is_none()); | ||||||
|     let m = l.get_element(11, 11).1; |     let m = l.get_element(11, 11).1; | ||||||
|     assert!(m.is_some()); |     assert!(m.is_some()); | ||||||
|     assert_eq!(m.unwrap().get_life(), 2); |     assert_eq!(m.unwrap().get_life(), 23); | ||||||
| } | } | ||||||
|  | |||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -1,30 +0,0 @@ | |||||||
| pub struct LevelLadder { |  | ||||||
|     curr: usize, |  | ||||||
|     next: usize, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl Iterator for LevelLadder { |  | ||||||
|     type Item = usize; |  | ||||||
| 
 |  | ||||||
|     fn next(&mut self) -> Option<Self::Item> { |  | ||||||
|         let current = self.curr; |  | ||||||
| 
 |  | ||||||
|         self.curr = self.next; |  | ||||||
|         self.next += current; |  | ||||||
| 
 |  | ||||||
|         // Since there's no endpoint to a Fibonacci sequence, the `Iterator`
 |  | ||||||
|         // will never return `None`, and `Some` is always returned.
 |  | ||||||
|         Some(current * 30) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Returns a Fibonacci sequence generator
 |  | ||||||
| pub fn get_level_ladder() -> LevelLadder { |  | ||||||
|     LevelLadder { curr: 1, next: 2 } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[test] |  | ||||||
| fn test_level_ladder() { |  | ||||||
|     let mut iter = get_level_ladder(); |  | ||||||
|     assert_eq!(iter.next(), Some(30)); |  | ||||||
| } |  | ||||||
| @ -1,27 +1,22 @@ | |||||||
| use ratatui::buffer::Buffer; | use ratatui::buffer::Buffer; | ||||||
| use ratatui::layout::Rect; | use ratatui::layout::Rect; | ||||||
| use ratatui::style::{Color, Modifier, Style}; | use ratatui::style::Color; | ||||||
| use ratatui::widgets::{StatefulWidget, Widget}; | use ratatui::widgets::{StatefulWidget, Widget}; | ||||||
| 
 | 
 | ||||||
| use crate::game::Game; | use crate::game::Game; | ||||||
| use crate::level::StructureElement; | use crate::level::StructureElement; | ||||||
| use crate::position::HasPosition; |  | ||||||
| 
 | 
 | ||||||
| const FG_BROWN: Color = Color::Rgb(140, 34, 0); | const FG_BROWN: Color = Color::Rgb(186, 74, 0); | ||||||
| const BACKGROUND: Color = Color::Black; |  | ||||||
| 
 | 
 | ||||||
| pub struct LevelWidget {} | pub struct LevelWidget {} | ||||||
| 
 | 
 | ||||||
| impl LevelWidget { | impl LevelWidget { | ||||||
|     fn set_cell(&self, buf: &mut Buffer, x: u16, y: u16, symbol: &str, fg: Color, bg: Color) { |     fn set_cell(&self, buf: &mut Buffer, x: u16, y: u16, symbol: &str, fg: Color, bg: Color) { | ||||||
|         buf[(x, y)].set_symbol(symbol).set_bg(bg).set_fg(fg); |         buf. | ||||||
|     } |             get_mut(x, y). | ||||||
|     fn set_bold_cell(&self, buf: &mut Buffer, x: u16, y: u16, symbol: &str, fg: Color, bg: Color) { |             set_symbol(symbol). | ||||||
|         buf[(x, y)] |             set_bg(bg). | ||||||
|             .set_symbol(symbol) |             set_fg(fg); | ||||||
|             .set_bg(bg) |  | ||||||
|             .set_fg(fg) |  | ||||||
|             .set_style(Style::new().add_modifier(Modifier::BOLD)); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -47,36 +42,36 @@ impl StatefulWidget for LevelWidget { | |||||||
|                         (Some(structure_element), None, None) => { |                         (Some(structure_element), None, None) => { | ||||||
|                             match structure_element { |                             match structure_element { | ||||||
|                                 StructureElement::Start => { |                                 StructureElement::Start => { | ||||||
|                                     self.set_cell(buf, x, y, "α", Color::White, BACKGROUND); |                                     self.set_cell(buf, x, y, "α", Color::Black, Color::Gray); | ||||||
|                                 } |                                 } | ||||||
|                                 StructureElement::End => { |                                 StructureElement::End => { | ||||||
|                                     self.set_cell(buf, x, y, "Ω", Color::White, BACKGROUND); |                                     self.set_cell(buf, x, y, "Ω", Color::Black, Color::Gray); | ||||||
|                                 } |                                 } | ||||||
|                                 StructureElement::Wall => { |                                 StructureElement::Wall => { | ||||||
|                                     // TODO add fancy walls with https://en.wikipedia.org/wiki/Box-drawing_characters
 |                                     // TODO add fancy walls
 | ||||||
|                                     self.set_cell(buf, x, y, "#", FG_BROWN, FG_BROWN); |                                     self.set_cell(buf, x, y, "#", FG_BROWN, Color::Gray); | ||||||
|                                 } |                                 } | ||||||
|                                 StructureElement::Floor => { |                                 StructureElement::Floor => { | ||||||
|                                     self.set_cell(buf, x, y, " ", FG_BROWN, BACKGROUND); |                                     self.set_cell(buf, x, y, " ", FG_BROWN, Color::Gray); | ||||||
|                                 } |                                 } | ||||||
|                                 StructureElement::StairDown => { |                                 StructureElement::StairDown => { | ||||||
|                                     self.set_cell(buf, x, y, ">", Color::White, BACKGROUND); |                                     self.set_cell(buf, x, y, ">", Color::Black, Color::Gray); | ||||||
|                                 } |                                 } | ||||||
|                                 StructureElement::StairUp => { |                                 StructureElement::StairUp => { | ||||||
|                                     self.set_cell(buf, x, y, "<", Color::White, BACKGROUND); |                                     self.set_cell(buf, x, y, "<", Color::Black, Color::Gray); | ||||||
|                                 } |                                 } | ||||||
|                                 StructureElement::Unknown => { |                                 StructureElement::Unknown => { | ||||||
|                                     self.set_cell(buf, x, y, "▒", Color::Gray, BACKGROUND); |                                     self.set_cell(buf, x, y, "▒", Color::DarkGray, Color::Gray); | ||||||
|                                 } |                                 } | ||||||
|                             } |                             } | ||||||
|                         } |                         } | ||||||
|                         (_, Some(m), _) => { |                         (_, Some(m), _) => { | ||||||
|                             let (s, c) = m.get_representation(); |                             let (s, c) = m.get_representation(); | ||||||
|                             self.set_cell(buf, x, y, s, c, BACKGROUND); |                             self.set_cell(buf, x, y, s, c, Color::Gray); | ||||||
|                         } |                         } | ||||||
|                         (_, _, Some(t)) => { |                         (_, _, Some(t)) => { | ||||||
|                             let (s, c) = t.get_representation(); |                             let (s, c) = t.get_representation(); | ||||||
|                             self.set_bold_cell(buf, x, y, s, c, BACKGROUND); |                             self.set_cell(buf, x, y, s, c, Color::Gray); | ||||||
|                         } |                         } | ||||||
|                         (None, None, None) => {} |                         (None, None, None) => {} | ||||||
|                     }; |                     }; | ||||||
| @ -88,7 +83,7 @@ impl StatefulWidget for LevelWidget { | |||||||
|             let player_pos = player.get_immutable_position(); |             let player_pos = player.get_immutable_position(); | ||||||
|             let player_x = al + player_pos.get_x() as u16; |             let player_x = al + player_pos.get_x() as u16; | ||||||
|             let player_y = at + player_pos.get_y() as u16; |             let player_y = at + player_pos.get_y() as u16; | ||||||
|             self.set_cell(buf, player_x, player_y, "8", Color::LightRed, BACKGROUND); |             self.set_cell(buf, player_x, player_y, "8", Color::Red, Color::Gray); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										208
									
								
								src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										208
									
								
								src/main.rs
									
									
									
									
									
								
							| @ -1,216 +1,117 @@ | |||||||
| use std::io::stdout; |  | ||||||
| use std::io::Result; | use std::io::Result; | ||||||
| use std::time::Instant; | use std::io::stdout; | ||||||
| 
 | 
 | ||||||
| use constants::FRAME_LENGTH; |  | ||||||
| use constants::LEVELS; |  | ||||||
| use constants::LEVEL_HEIGHT; |  | ||||||
| use constants::LEVEL_WIDTH; |  | ||||||
| use constants::MIN_HEIGHT; |  | ||||||
| use constants::MIN_WIDTH; |  | ||||||
| use crossterm::{ | use crossterm::{ | ||||||
|     event::{self, KeyCode, KeyEventKind}, |     event::{self, KeyCode, KeyEventKind}, | ||||||
|     terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, |  | ||||||
|     ExecutableCommand, |     ExecutableCommand, | ||||||
|  |     terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, | ||||||
| }; | }; | ||||||
| use ratatui::prelude::*; |  | ||||||
| use ratatui::widgets::{Block, BorderType, Borders, Wrap}; |  | ||||||
| use ratatui::{ | use ratatui::{ | ||||||
|     prelude::{CrosstermBackend, Terminal}, |     prelude::{CrosstermBackend, Stylize, Terminal}, | ||||||
|     widgets::Paragraph, |     widgets::Paragraph, | ||||||
| }; | }; | ||||||
|  | use ratatui::prelude::*; | ||||||
|  | use ratatui::widgets::{Block, Borders, BorderType, Wrap}; | ||||||
|  | use ratatui::widgets::block::{Position, Title}; | ||||||
| use whoami::realname; | use whoami::realname; | ||||||
| 
 | 
 | ||||||
| use crate::game::{Game, GameState}; | use crate::game::{Game, GameState}; | ||||||
| use crate::level_widget::LevelWidget; | use crate::level_widget::LevelWidget; | ||||||
|  | // use crate::level_widget::LevelWidget;
 | ||||||
| use crate::player::Player; | use crate::player::Player; | ||||||
| use crate::position::HasPosition; |  | ||||||
| 
 | 
 | ||||||
| mod artifacts; |  | ||||||
| mod constants; |  | ||||||
| mod game; | mod game; | ||||||
| mod level; |  | ||||||
| mod level_generator; |  | ||||||
| mod level_ladder; |  | ||||||
| mod level_widget; |  | ||||||
| mod monsters; |  | ||||||
| mod player; | mod player; | ||||||
|  | mod level; | ||||||
| mod position; | mod position; | ||||||
| mod room; | mod level_widget; | ||||||
| 
 | mod level_generator; | ||||||
| const BACKGROUND_COLOR: Color = Color::DarkGray; | mod artifacts; | ||||||
| const PANEL_BACKGROUND_COLOR: Color = Color::Black; | mod monster; | ||||||
| 
 | 
 | ||||||
| //
 | //
 | ||||||
| fn main() -> Result<()> { | fn main() -> Result<()> { | ||||||
|     let mut game = Game::new(Player::new(realname().as_str(), 30)); |     let mut game = Game::new(Player::new(realname().as_str(), 10)); | ||||||
| 
 | 
 | ||||||
|     stdout().execute(EnterAlternateScreen)?; |     stdout().execute(EnterAlternateScreen)?; | ||||||
|     enable_raw_mode()?; |     enable_raw_mode()?; | ||||||
|     let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?; |     let mut terminal = Terminal::new(CrosstermBackend::new(stdout()))?; | ||||||
|     terminal.clear()?; |     terminal.clear()?; | ||||||
| 
 | 
 | ||||||
|     let start_time = Instant::now(); |     let mut ticks: u64 = 0; | ||||||
|     let mut ticks = 0; |  | ||||||
|     loop { |     loop { | ||||||
|         terminal.draw(|frame| { |         terminal.draw(|frame| { | ||||||
|             let mut area = frame.area(); |             let mut area = frame.size(); | ||||||
|             frame.render_widget( |             frame.render_widget(Block::default().style(Style::default().bg(Color::Green)), area); | ||||||
|                 Block::default().style(Style::default().bg(BACKGROUND_COLOR)), |  | ||||||
|                 area, |  | ||||||
|             ); |  | ||||||
| 
 | 
 | ||||||
|             // don't draw stuff except an info box if the terminal is too small (less than 80x25)
 |             if area.width > 80 { | ||||||
|             // to prevent the read drawing code from crashing the game.
 |                 area.x = (area.width - 80) / 2; | ||||||
|             if area.width < MIN_WIDTH || area.height < MIN_HEIGHT { |                 area.width = 80; | ||||||
|                 let block = Block::default() |  | ||||||
|                     .title("Info") |  | ||||||
|                     .borders(Borders::ALL) |  | ||||||
|                     .border_style(Style::default().fg(Color::White)) |  | ||||||
|                     .border_type(BorderType::Rounded) |  | ||||||
|                     .style(Style::default().bg(Color::Black)); |  | ||||||
|                 let paragraph = Paragraph::new(format!( |  | ||||||
|                     "Terminal needs to be at leas {}x{}!", |  | ||||||
|                     MIN_WIDTH, MIN_HEIGHT |  | ||||||
|                 )) |  | ||||||
|                 .block(block) |  | ||||||
|                 .wrap(Wrap { trim: true }); |  | ||||||
|                 frame.render_widget(paragraph, area); |  | ||||||
|                 return; |  | ||||||
|             } |             } | ||||||
| 
 |             if area.height > 25 { | ||||||
|             if area.width > MIN_WIDTH { |                 area.y = (area.height - 25) / 2; | ||||||
|                 area.x = (area.width - MIN_WIDTH) / 2; |                 area.height = 25; | ||||||
|                 area.width = MIN_WIDTH; |  | ||||||
|             } |  | ||||||
|             if area.height > MIN_HEIGHT { |  | ||||||
|                 area.y = (area.height - LEVEL_HEIGHT as u16) / 2; |  | ||||||
|                 area.height = LEVEL_HEIGHT as u16; |  | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             let map_area = Rect { |             let map_area = Rect { | ||||||
|                 x: area.x, |                 x: area.x, | ||||||
|                 y: area.y, |                 y: area.y, | ||||||
|                 width: LEVEL_WIDTH as u16, |                 width: level::LEVEL_WIDTH as u16, | ||||||
|                 height: LEVEL_HEIGHT as u16, |                 height: level::LEVEL_HEIGHT as u16, | ||||||
|             }; |             }; | ||||||
|             frame.render_stateful_widget(LevelWidget {}, map_area, &mut game); |             frame.render_stateful_widget(LevelWidget {}, map_area, &mut game); | ||||||
| 
 | 
 | ||||||
|             let stats_area = Rect { |             let stats_area = Rect { | ||||||
|                 x: area.x + map_area.width, |                 x: area.x + 50, | ||||||
|                 y: area.y, |                 y: area.y, | ||||||
|                 width: MIN_WIDTH - map_area.width, |                 width: 30, | ||||||
|                 height: map_area.height / 2 + 1, |                 height: 25, | ||||||
|             }; |             }; | ||||||
|             let block = Block::default() |  | ||||||
|                 .title_top( |  | ||||||
|                     Line::from(format!(" {} ", game.get_player().get_name())).centered() |  | ||||||
|                 ) |  | ||||||
|                 .borders(Borders::TOP) |  | ||||||
|                 .border_style(Style::default().fg(Color::White)) |  | ||||||
|                 .border_type(BorderType::Rounded) |  | ||||||
|                 .style(Style::default().bg(PANEL_BACKGROUND_COLOR)); |  | ||||||
|             frame.render_widget( |             frame.render_widget( | ||||||
|                 Paragraph::new(format!( |                 Paragraph::new(format!("{}\nHealth: {}/{}\nExp: {}\nGold: {}\nLevel: {}\nticks: {}", | ||||||
|                     "Health: {} of {}\nDefense: {}\nExp: {}\nGold: {}\nLevel: {} of {}\nInventory used: {} of {}", |                                        game.get_player().get_name(), | ||||||
|                                        game.get_player().get_life(), |                                        game.get_player().get_life(), | ||||||
|                                        game.get_player().get_max_life(), |                                        game.get_player().get_max_life(), | ||||||
|                     game.get_player().defense(), |  | ||||||
|                                        game.get_player().get_experience(), |                                        game.get_player().get_experience(), | ||||||
|                                        game.get_player().get_gold(), |                                        game.get_player().get_gold(), | ||||||
|                     game.get_player().get_immutable_position().get_level() + 1, |                                        game.get_player().get_immutable_position().get_level(), | ||||||
|                     LEVELS, |                                        ticks | ||||||
|                     game.get_player().inventory_size().0, |  | ||||||
|                     game.get_player().inventory_size().1, |  | ||||||
|                 )) |                 )) | ||||||
|                 .block(block) |                     .white() | ||||||
|                 .wrap(Wrap { trim: true }), |                     .on_blue(), | ||||||
|                 stats_area, |                 stats_area, | ||||||
|             ); |             ); | ||||||
|             let messages_area = Rect { |  | ||||||
|                 x: area.x + map_area.width, |  | ||||||
|                 y: area.y + map_area.height / 2 + 1, |  | ||||||
|                 width: MIN_WIDTH - map_area.width, |  | ||||||
|                 height: map_area.height / 2, |  | ||||||
|             }; |  | ||||||
|             // Display the latest messages from the game to the user
 |  | ||||||
|             let block = Block::default() |  | ||||||
|                 .title_top( |  | ||||||
|                     Line::from(" messages ").centered() |  | ||||||
|                 ) |  | ||||||
|                 .borders(Borders::TOP) |  | ||||||
|                 .border_style(Style::default().fg(Color::White)) |  | ||||||
|                 .border_type(BorderType::Rounded) |  | ||||||
|                 .style(Style::default().bg(PANEL_BACKGROUND_COLOR)); |  | ||||||
| 
 |  | ||||||
|             let paragraph1 = if game.messages.is_empty() { |  | ||||||
|                 "".to_string() |  | ||||||
|             } else { |  | ||||||
|                 format!("> {}", game.messages.join("\n> ")) |  | ||||||
|             }; |  | ||||||
|             frame.render_widget( |  | ||||||
|                 Paragraph::new(paragraph1) |  | ||||||
|                     .block(block) |  | ||||||
|                     .wrap(Wrap { trim: true }), |  | ||||||
|                 messages_area, |  | ||||||
|             ); |  | ||||||
|         })?; |         })?; | ||||||
|         if event::poll(std::time::Duration::from_millis(FRAME_LENGTH))? { |         if event::poll(std::time::Duration::from_millis(50))? { | ||||||
|             if let event::Event::Key(key) = event::read()? { |             if let event::Event::Key(key) = event::read()? { | ||||||
|                 if key.kind == KeyEventKind::Press { |                 if key.kind == KeyEventKind::Press && key.code == KeyCode::Char('q') { | ||||||
|                     match key.code { |  | ||||||
|                         KeyCode::Char('v') => { |  | ||||||
|                             game.messages.insert( |  | ||||||
|                                 0, |  | ||||||
|                                 format!("You are playing version '{}'.", env!("GIT_HASH")) |  | ||||||
|                                     .to_string(), |  | ||||||
|                             ); |  | ||||||
|                         } |  | ||||||
|                         KeyCode::Char('p') => { |  | ||||||
|                             let gained_health = game.get_mutable_player().consume_inventory(); |  | ||||||
|                             if gained_health > 0 { |  | ||||||
|                                 game.messages.insert( |  | ||||||
|                                     0, |  | ||||||
|                                     format!( |  | ||||||
|                                         "used a potion from inventory and gained {} health.", |  | ||||||
|                                         gained_health |  | ||||||
|                                     ) |  | ||||||
|                                     .to_string(), |  | ||||||
|                                 ); |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                         KeyCode::Char('q') => { |  | ||||||
|                     break; |                     break; | ||||||
|                 } |                 } | ||||||
|                         KeyCode::Left | KeyCode::Down | KeyCode::Right | KeyCode::Up => { |                 if key.kind == KeyEventKind::Press { | ||||||
|                     let new_pos = match key.code { |                     let new_pos = match key.code { | ||||||
|                                 KeyCode::Left => game.move_player(-1, 0), |                         KeyCode::Left => { game.move_player(-1, 0) } | ||||||
|                                 KeyCode::Right => game.move_player(1, 0), |                         KeyCode::Right => { game.move_player(1, 0) } | ||||||
|                                 KeyCode::Up => game.move_player(0, -1), |                         KeyCode::Up => { game.move_player(0, -1) } | ||||||
|                                 KeyCode::Down => game.move_player(0, 1), |                         KeyCode::Down => { game.move_player(0, 1) } | ||||||
|                                 _ => (0, 0), |                         _ => { (0, 0) } | ||||||
|                     }; |                     }; | ||||||
|                     if !game.player_fights_monster() { |                     if !game.player_fights_monster() { | ||||||
|                         // player attacked monster but did not kill it
 |                         // player attacked monster but did not kill it
 | ||||||
|                         game.move_player(new_pos.0, new_pos.1); |                         game.move_player(new_pos.0, new_pos.1); | ||||||
|                     } |                     } | ||||||
|                     game.player_collects_artifact(); |                     game.player_collects_artifact(); | ||||||
|                         } |                     if game.get_game_state() != GameState::RUNNING { | ||||||
|                         _ => {} |                         break; | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         game.update_level(ticks); |         game.update_level(ticks); | ||||||
|         if game.get_game_state() != GameState::Running { |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|         ticks+=1; |         ticks+=1; | ||||||
|     } |     } | ||||||
|     let playtime = start_time.elapsed(); |  | ||||||
|     loop { |     loop { | ||||||
|         let _ = terminal.draw(|frame| { |         let _ = terminal.draw(|frame| { | ||||||
|             let mut area = frame.area(); |             let mut area = frame.size(); | ||||||
|             let w = area.width / 2; |             let w = area.width / 2; | ||||||
|             let h = area.height / 2; |             let h = area.height / 2; | ||||||
|             area.x += w - 20; |             area.x += w - 20; | ||||||
| @ -218,31 +119,25 @@ fn main() -> Result<()> { | |||||||
|             area.width = 40; |             area.width = 40; | ||||||
|             area.height = 20; |             area.height = 20; | ||||||
|             let block = Block::default() |             let block = Block::default() | ||||||
|                 .title_top(Line::from(" Game ended ").centered()) |                 .title("Game ended") | ||||||
|                 .title_bottom(Line::from("Press `q` to quit!")) |                 .title(Title::from("Press `q` to quit!").position(Position::Bottom)) | ||||||
|                 .borders(Borders::ALL) |                 .borders(Borders::ALL) | ||||||
|                 .border_style(Style::default().fg(Color::White)) |                 .border_style(Style::default().fg(Color::White)) | ||||||
|                 .border_type(BorderType::Rounded) |                 .border_type(BorderType::Rounded) | ||||||
|                 .style(Style::default().bg(Color::Black)); |                 .style(Style::default().bg(Color::Black)); | ||||||
|             let mut text = match game.get_game_state() { |             let mut text = match game.get_game_state() { | ||||||
|                 GameState::Running => { |                 GameState::RUNNING => { | ||||||
|                     "Quitting is for cowards! You'll better try again!".to_string() |                     "Quitting is for cowards! You'll better try again!".to_string() | ||||||
|                 } |                 } | ||||||
|                 GameState::Lost => { |                 GameState::LOST => { | ||||||
|                     "Sorry, you died in the dungeon. Better luck next time!".to_string() |                     "Sorry, you died in the dungeon. Better luck next time!".to_string() | ||||||
|                 } |                 } | ||||||
|                 GameState::Won => { |                 GameState::WON => { | ||||||
|                     "Congratulation! You mastered your way through the dungeon and won the game." |                     "Congratulation! You mastered your way through the dungeon and won the game.".to_string() | ||||||
|                         .to_string() |  | ||||||
|                 } |                 } | ||||||
|             }; |             }; | ||||||
|             text += format!( |             text += format!("\nYou gained {} experience.", game.get_player().get_experience()).as_str(); | ||||||
|                 "\nYou gained {} experience.", |  | ||||||
|                 game.get_player().get_experience() |  | ||||||
|             ) |  | ||||||
|             .as_str(); |  | ||||||
|             text += format!("\nYou collected {} gold.", game.get_player().get_gold()).as_str(); |             text += format!("\nYou collected {} gold.", game.get_player().get_gold()).as_str(); | ||||||
|             text += format!("\nYou played {} seconds.", playtime.as_secs()).as_str(); |  | ||||||
|             let paragraph = Paragraph::new(text).block(block).wrap(Wrap { trim: true }); |             let paragraph = Paragraph::new(text).block(block).wrap(Wrap { trim: true }); | ||||||
|             frame.render_widget(paragraph, area); |             frame.render_widget(paragraph, area); | ||||||
|         }); |         }); | ||||||
| @ -254,6 +149,7 @@ fn main() -> Result<()> { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|     stdout().execute(LeaveAlternateScreen)?; |     stdout().execute(LeaveAlternateScreen)?; | ||||||
|     disable_raw_mode()?; |     disable_raw_mode()?; | ||||||
|     Ok(()) |     Ok(()) | ||||||
|  | |||||||
							
								
								
									
										128
									
								
								src/monster.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								src/monster.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,128 @@ | |||||||
|  | use ratatui::prelude::Color; | ||||||
|  | 
 | ||||||
|  | use crate::position::Position; | ||||||
|  | 
 | ||||||
|  | pub trait Monster { | ||||||
|  |     fn is_dead(&self) -> bool; | ||||||
|  |     fn get_representation(&self) -> (&str, Color); | ||||||
|  |     fn decrease_life(&mut self, by: usize); | ||||||
|  |     fn get_immutable_position(&self) -> &Position; | ||||||
|  |     fn get_experience_gain(&self) -> usize; | ||||||
|  |     fn get_position(&mut self) -> &mut Position; | ||||||
|  |     #[cfg(test)] | ||||||
|  |     fn get_life(&self) -> usize; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | macro_rules! default_monster { | ||||||
|  |     ($($t:ty),+ $(,)?) => ($( | ||||||
|  |         impl Monster for $t { | ||||||
|  |             fn is_dead(&self) -> bool { self.life <= 0 } | ||||||
|  |             fn get_experience_gain(&self) -> usize { self.experience_gain } | ||||||
|  |             fn get_representation(&self) -> (&str, Color) { (&self.symbol, self.color) } | ||||||
|  |             fn decrease_life(&mut self, by: usize) { | ||||||
|  |                 self.life = self.life.saturating_sub(by); | ||||||
|  |             } | ||||||
|  |             fn get_immutable_position(&self) -> &Position { | ||||||
|  |                 &self.position | ||||||
|  |             } | ||||||
|  |             fn get_position(&mut self) -> &mut Position { | ||||||
|  |                 &mut self.position | ||||||
|  |             } | ||||||
|  |             #[cfg(test)] | ||||||
|  |             fn get_life(&self) -> usize { self.life } | ||||||
|  |         } | ||||||
|  |     )+) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub struct Rat { | ||||||
|  |     life: usize, | ||||||
|  |     position: Position, | ||||||
|  |     symbol: String, | ||||||
|  |     color: Color, | ||||||
|  |     experience_gain: usize, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Rat { | ||||||
|  |     #[cfg(test)] | ||||||
|  |     pub fn new(life: usize) -> Self { | ||||||
|  |         Self { | ||||||
|  |             life, | ||||||
|  |             position: Position::new(0, 0, 0), | ||||||
|  |             symbol: String::from("R"), | ||||||
|  |             color: Color::Black, | ||||||
|  |             experience_gain: 5, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     pub fn new_with_position(position: Position) -> Self { | ||||||
|  |         Self { | ||||||
|  |             life: 2, | ||||||
|  |             position, | ||||||
|  |             symbol: String::from("R"), | ||||||
|  |             color: Color::Black, | ||||||
|  |             experience_gain: 5, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     #[cfg(test)] | ||||||
|  |     pub fn get_life(&self) -> usize { self.life } | ||||||
|  | } | ||||||
|  | default_monster!(Rat); | ||||||
|  | 
 | ||||||
|  | pub struct Orc { | ||||||
|  |     life: usize, | ||||||
|  |     position: Position, | ||||||
|  |     symbol: String, | ||||||
|  |     color: Color, | ||||||
|  |     experience_gain: usize, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Orc { | ||||||
|  |     #[cfg(test)] | ||||||
|  |     pub fn new(life: usize) -> Self { | ||||||
|  |         Self { | ||||||
|  |             life, | ||||||
|  |             position: Position::new(0, 0, 0), | ||||||
|  |             symbol: String::from("O"), | ||||||
|  |             color: Color::DarkGray, | ||||||
|  |             experience_gain: 10, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     pub fn new_with_position(position: Position) -> Self { | ||||||
|  |         Self { | ||||||
|  |             life: 4, | ||||||
|  |             position, | ||||||
|  |             symbol: String::from("O"), | ||||||
|  |             color: Color::DarkGray, | ||||||
|  |             experience_gain: 10, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     #[cfg(test)] | ||||||
|  |     pub fn get_life(&self) -> usize { self.life } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | default_monster!(Orc); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #[test] | ||||||
|  | fn monsters_can_move() { | ||||||
|  |     let mut m = Rat::new(2); | ||||||
|  |     assert_eq!(m.get_position(), &Position::new(0, 0, 0)); | ||||||
|  |     m.get_position().change(1, 2); | ||||||
|  |     assert_eq!(m.get_position(), &Position::new(0, 1, 2)); | ||||||
|  |     m.get_position().change(2, 1); | ||||||
|  |     assert_eq!(m.get_position(), &Position::new(0, 3, 3)); | ||||||
|  |     m.get_position().set(1, 2, 3); | ||||||
|  |     m.get_position().change(2, 1); | ||||||
|  |     assert_eq!(m.get_position(), &Position::new(1, 4, 4)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[test] | ||||||
|  | fn monsters_can_die() { | ||||||
|  |     let mut m = Rat::new(2); | ||||||
|  |     assert_eq!(m.get_life(), 2); | ||||||
|  |     assert_eq!(m.is_dead(), false); | ||||||
|  |     m.decrease_life(1); | ||||||
|  |     assert_eq!(m.get_life(), 1); | ||||||
|  |     m.decrease_life(2); | ||||||
|  |     assert_eq!(m.get_life(), 0); | ||||||
|  |     assert_eq!(m.is_dead(), true); | ||||||
|  | } | ||||||
							
								
								
									
										137
									
								
								src/monsters.rs
									
									
									
									
									
								
							
							
						
						
									
										137
									
								
								src/monsters.rs
									
									
									
									
									
								
							| @ -1,137 +0,0 @@ | |||||||
| use std::ops::RangeInclusive; |  | ||||||
| 
 |  | ||||||
| use crate::position::{HasPosition, Position}; |  | ||||||
| use macros::CreateMonsters; |  | ||||||
| use rand::Rng; |  | ||||||
| use ratatui::prelude::Color; |  | ||||||
| 
 |  | ||||||
| pub trait Monster: HasPosition { |  | ||||||
|     fn get_name(&self) -> &str; |  | ||||||
|     fn is_dead(&self) -> bool; |  | ||||||
|     fn get_representation(&self) -> (&str, Color); |  | ||||||
|     fn decrease_life(&mut self, by: usize); |  | ||||||
|     // fn get_immutable_position(&self) -> &Position;
 |  | ||||||
|     fn get_experience_gain(&self) -> usize; |  | ||||||
|     fn get_ticks_between_steps(&self) -> u128; |  | ||||||
|     #[cfg(test)] |  | ||||||
|     fn get_life(&self) -> usize; |  | ||||||
|     fn damage(&self) -> usize; |  | ||||||
|     fn defense(&self) -> usize; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(CreateMonsters, PartialEq, Eq, Hash, Clone, Copy)] |  | ||||||
| pub enum MonsterTypes { |  | ||||||
|     Rat, |  | ||||||
|     Orc, |  | ||||||
|     Snake, |  | ||||||
|     Skeleton, |  | ||||||
|     Spider, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| macro_rules! create_monster { |  | ||||||
|     ($t:ident $(, $k:ident : $v:expr)*$(,)?) => ( |  | ||||||
|         impl $t { |  | ||||||
|             pub fn new_with_position(position: Position) -> Self { |  | ||||||
|                 Self { |  | ||||||
|                     position, |  | ||||||
|                     $($k: $v,)* |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     ) |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| create_monster!( |  | ||||||
|     Rat, |  | ||||||
|     name:"rat".to_string(), |  | ||||||
|     life: 2, |  | ||||||
|     symbol: String::from("r"), |  | ||||||
|     color: Color::White, |  | ||||||
|     experience_gain: 5, |  | ||||||
|     ticks_between_steps: 5, |  | ||||||
|     damage_range: 1..=2, |  | ||||||
|     defense: 0, |  | ||||||
| ); |  | ||||||
| 
 |  | ||||||
| create_monster!( |  | ||||||
|     Spider, |  | ||||||
|     name:"spider".to_string(), |  | ||||||
|     life: 3, |  | ||||||
|     symbol: String::from("s"), |  | ||||||
|     color: Color::Yellow, |  | ||||||
|     experience_gain: 7, |  | ||||||
|     ticks_between_steps: 7, |  | ||||||
|     damage_range: 2..=3, |  | ||||||
|     defense: 0, |  | ||||||
| ); |  | ||||||
| 
 |  | ||||||
| create_monster!( |  | ||||||
|     Orc, |  | ||||||
|     name: "orc".to_string(), |  | ||||||
|     life: 4, |  | ||||||
|     symbol: String::from("O"), |  | ||||||
|     color: Color::Gray, |  | ||||||
|     experience_gain: 10, |  | ||||||
|     ticks_between_steps: 10, |  | ||||||
|     damage_range: 2..=3, |  | ||||||
|     defense: 0, |  | ||||||
| ); |  | ||||||
| 
 |  | ||||||
| create_monster!( |  | ||||||
|     Snake, |  | ||||||
|     name: "snake".to_string(), |  | ||||||
|     life: 3, |  | ||||||
|     symbol: String::from("s"), |  | ||||||
|     color: Color::White, |  | ||||||
|     experience_gain: 10, |  | ||||||
|     ticks_between_steps: 20, |  | ||||||
|     damage_range: 1..=4, |  | ||||||
|     defense: 0, |  | ||||||
| ); |  | ||||||
| 
 |  | ||||||
| create_monster!( |  | ||||||
|     Skeleton, |  | ||||||
|     name: "skeleton".to_string(), |  | ||||||
|     life: 3, |  | ||||||
|     symbol: String::from("S"), |  | ||||||
|     color: Color::Gray, |  | ||||||
|     experience_gain: 20, |  | ||||||
|     ticks_between_steps: 10, |  | ||||||
|     damage_range: 1..=5, |  | ||||||
|     defense: 0, |  | ||||||
| ); |  | ||||||
| 
 |  | ||||||
| pub fn create_monster_by_type(monster_type: &MonsterTypes, position: Position) -> Box<dyn Monster> { |  | ||||||
|     match monster_type { |  | ||||||
|         MonsterTypes::Rat => Box::new(Rat::new_with_position(position)), |  | ||||||
|         MonsterTypes::Orc => Box::new(Orc::new_with_position(position)), |  | ||||||
|         MonsterTypes::Snake => Box::new(Snake::new_with_position(position)), |  | ||||||
|         MonsterTypes::Skeleton => Box::new(Skeleton::new_with_position(position)), |  | ||||||
|         MonsterTypes::Spider => Box::new(Spider::new_with_position(position)), |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[test] |  | ||||||
| fn monsters_can_move() { |  | ||||||
|     let mut m = Rat::new_with_position(Position::new(0, 0, 0)); |  | ||||||
|     assert_eq!(m.get_position(), &Position::new(0, 0, 0)); |  | ||||||
|     m.get_position().change(1, 2); |  | ||||||
|     assert_eq!(m.get_position(), &Position::new(0, 1, 2)); |  | ||||||
|     m.get_position().change(2, 1); |  | ||||||
|     assert_eq!(m.get_position(), &Position::new(0, 3, 3)); |  | ||||||
|     m.get_position().set(1, 2, 3); |  | ||||||
|     m.get_position().change(2, 1); |  | ||||||
|     assert_eq!(m.get_position(), &Position::new(1, 4, 4)); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[test] |  | ||||||
| fn monsters_can_die() { |  | ||||||
|     let mut m = Rat::new_with_position(Position::new(0, 0, 0)); |  | ||||||
|     assert_eq!(m.get_life(), 2); |  | ||||||
|     assert_eq!(m.is_dead(), false); |  | ||||||
|     m.decrease_life(1); |  | ||||||
|     assert_eq!(m.get_life(), 1); |  | ||||||
|     m.decrease_life(2); |  | ||||||
|     assert_eq!(m.get_life(), 0); |  | ||||||
|     assert_eq!(m.is_dead(), true); |  | ||||||
| } |  | ||||||
							
								
								
									
										122
									
								
								src/player.rs
									
									
									
									
									
								
							
							
						
						
									
										122
									
								
								src/player.rs
									
									
									
									
									
								
							| @ -1,9 +1,6 @@ | |||||||
| use rand::Rng; |  | ||||||
| use std::cmp::{max, min}; | use std::cmp::{max, min}; | ||||||
| 
 | 
 | ||||||
| use crate::artifacts::Potion; | use crate::position::Position; | ||||||
| use crate::level_ladder::get_level_ladder; |  | ||||||
| use crate::position::{HasPosition, Position}; |  | ||||||
| 
 | 
 | ||||||
| pub struct Player { | pub struct Player { | ||||||
|     name: String, |     name: String, | ||||||
| @ -12,10 +9,6 @@ pub struct Player { | |||||||
|     max_life: i16, |     max_life: i16, | ||||||
|     gold: usize, |     gold: usize, | ||||||
|     experience: usize, |     experience: usize, | ||||||
|     inventory: Vec<Potion>, |  | ||||||
|     inventory_slots: usize, |  | ||||||
|     level: usize, |  | ||||||
|     defense: usize, |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Player { | impl Player { | ||||||
| @ -27,14 +20,10 @@ impl Player { | |||||||
|             max_life, |             max_life, | ||||||
|             gold: 0, |             gold: 0, | ||||||
|             experience: 0, |             experience: 0, | ||||||
|             inventory: vec![], |  | ||||||
|             inventory_slots: 2, |  | ||||||
|             level: 1, |  | ||||||
|             defense: 0, |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     pub fn get_name(&self) -> String { |     pub fn get_name(&self) -> String { | ||||||
|         self.name.clone() |         return self.name.clone(); | ||||||
|     } |     } | ||||||
|     pub fn change_life(&mut self, by: i16) { |     pub fn change_life(&mut self, by: i16) { | ||||||
|         self.life = max(0, min(self.max_life, self.life + by)); |         self.life = max(0, min(self.max_life, self.life + by)); | ||||||
| @ -42,96 +31,25 @@ impl Player { | |||||||
|     pub fn get_life(&self) -> i16 { |     pub fn get_life(&self) -> i16 { | ||||||
|         self.life |         self.life | ||||||
|     } |     } | ||||||
|     /// returns true if the player is dead (life <= 0)
 |  | ||||||
|     pub fn is_dead(&self) -> bool { |  | ||||||
|         self.life <= 0 |  | ||||||
|     } |  | ||||||
|     /// returns true if the player's life is at maximum
 |  | ||||||
|     pub fn is_healthy(&self) -> bool { |  | ||||||
|         self.life == self.max_life |  | ||||||
|     } |  | ||||||
|     pub fn get_max_life(&self) -> i16 { |     pub fn get_max_life(&self) -> i16 { | ||||||
|         self.max_life |         self.max_life | ||||||
|     } |     } | ||||||
| 
 |     pub fn get_position(&mut self) -> &mut Position { | ||||||
|     /// add the given amount to the players gold stash
 |  | ||||||
|     pub fn retrieve_gold(&mut self, amount: usize) { |  | ||||||
|         self.gold += amount |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /// return the size of the players gold stash
 |  | ||||||
|     pub fn get_gold(&self) -> usize { |  | ||||||
|         self.gold |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub fn gain_experience(&mut self, amount: usize) -> bool { |  | ||||||
|         let mut result = false; |  | ||||||
|         for (i, level_step) in get_level_ladder().enumerate() { |  | ||||||
|             if self.experience <= level_step && self.experience + amount > level_step { |  | ||||||
|                 self.level += 1; |  | ||||||
|                 // make life gain depend on level?
 |  | ||||||
|                 self.max_life += 5; |  | ||||||
|                 self.defense += 1; |  | ||||||
|                 self.life = self.max_life; |  | ||||||
|                 result = true; |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|             if i > self.level { |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         self.experience += amount; |  | ||||||
|         result |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub fn get_experience_level(&self) -> usize { |  | ||||||
|         self.level |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub fn get_experience(&self) -> usize { |  | ||||||
|         self.experience |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub fn damage(&self) -> usize { |  | ||||||
|         rand::thread_rng().gen_range(1..4) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub fn defense(&self) -> usize { |  | ||||||
|         self.defense |  | ||||||
|     } |  | ||||||
|     
 |  | ||||||
|     pub fn add_to_inventory(&mut self, potion: &Potion) -> bool { |  | ||||||
|         if self.inventory.len() < self.inventory_slots { |  | ||||||
|             self.inventory.push(*potion); |  | ||||||
|             true |  | ||||||
|         } else { |  | ||||||
|             false |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub fn inventory_size(&self) -> (usize, usize) { |  | ||||||
|         (self.inventory.len(), self.inventory_slots) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub fn consume_inventory(&mut self) -> usize { |  | ||||||
|         if self.is_healthy() { |  | ||||||
|             return 0; |  | ||||||
|         } |  | ||||||
|         if let Some(potion) = self.inventory.pop() { |  | ||||||
|             self.change_life(potion.get_health() as i16); |  | ||||||
|             return potion.get_health(); |  | ||||||
|         } |  | ||||||
|         0 |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl HasPosition for Player { |  | ||||||
|     fn get_position(&mut self) -> &mut Position { |  | ||||||
|         &mut self.position |         &mut self.position | ||||||
|     } |     } | ||||||
|     fn get_immutable_position(&self) -> &Position { |     pub fn get_immutable_position(&self) -> &Position { | ||||||
|         &self.position |         &self.position | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     /// add the given amount to the players gold stash
 | ||||||
|  |     pub fn retrieve_gold(&mut self, amount: usize) { self.gold += amount } | ||||||
|  | 
 | ||||||
|  |     /// return the size of the players gold stash
 | ||||||
|  |     pub fn get_gold(&self) -> usize { self.gold } | ||||||
|  | 
 | ||||||
|  |     pub fn gain_experience(&mut self, amount: usize) { self.experience += amount } | ||||||
|  | 
 | ||||||
|  |     pub fn get_experience(&self) -> usize { self.experience } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[test] | #[test] | ||||||
| @ -143,10 +61,6 @@ fn test_get_name() { | |||||||
|         max_life: 10, |         max_life: 10, | ||||||
|         gold: 0, |         gold: 0, | ||||||
|         experience: 0, |         experience: 0, | ||||||
|         inventory: vec![], |  | ||||||
|         inventory_slots: 1, |  | ||||||
|         level: 0, |  | ||||||
|         defense: 0, |  | ||||||
|     }; |     }; | ||||||
|     assert_eq!(p.get_name(), "Teddy Tester"); |     assert_eq!(p.get_name(), "Teddy Tester"); | ||||||
| } | } | ||||||
| @ -170,10 +84,6 @@ fn test_change_life() { | |||||||
|         max_life: 10, |         max_life: 10, | ||||||
|         gold: 0, |         gold: 0, | ||||||
|         experience: 0, |         experience: 0, | ||||||
|         inventory: vec![], |  | ||||||
|         inventory_slots: 1, |  | ||||||
|         level: 0, |  | ||||||
|         defense: 0, |  | ||||||
|     }; |     }; | ||||||
|     assert_eq!(p.get_life(), 5); |     assert_eq!(p.get_life(), 5); | ||||||
|     p.change_life(-2); |     p.change_life(-2); | ||||||
| @ -207,10 +117,6 @@ fn test_max_life() { | |||||||
|         max_life: 10, |         max_life: 10, | ||||||
|         gold: 0, |         gold: 0, | ||||||
|         experience: 0, |         experience: 0, | ||||||
|         inventory: vec![], |  | ||||||
|         inventory_slots: 1, |  | ||||||
|         level: 0, |  | ||||||
|         defense: 0, |  | ||||||
|     }; |     }; | ||||||
|     assert_eq!(p.get_max_life(), 10); |     assert_eq!(p.get_max_life(), 10); | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,14 +1,6 @@ | |||||||
| use std::cmp::max; | use std::cmp::max; | ||||||
| 
 | 
 | ||||||
| /// describes an character (PC or NPC) in the dungeon that has a position.
 | #[derive(PartialEq, Debug)] | ||||||
| pub trait HasPosition { |  | ||||||
|     /// returns a mutable position
 |  | ||||||
|     fn get_position(&mut self) -> &mut Position; |  | ||||||
|     /// returns an immutable position
 |  | ||||||
|     fn get_immutable_position(&self) -> &Position; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(PartialEq, Debug, Clone, Copy)] |  | ||||||
| pub struct Position { | pub struct Position { | ||||||
|     level: usize, |     level: usize, | ||||||
|     x: usize, |     x: usize, | ||||||
| @ -17,7 +9,11 @@ pub struct Position { | |||||||
| 
 | 
 | ||||||
| impl Position { | impl Position { | ||||||
|     pub fn new(level: usize, x: usize, y: usize) -> Self { |     pub fn new(level: usize, x: usize, y: usize) -> Self { | ||||||
|         Self { level, x, y } |         Self { | ||||||
|  |             level, | ||||||
|  |             x, | ||||||
|  |             y, | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|     pub fn change(&mut self, dx: i16, dy: i16) -> (usize, usize) { |     pub fn change(&mut self, dx: i16, dy: i16) -> (usize, usize) { | ||||||
|         self.x = max((self.x as i16) + dx, 0) as usize; |         self.x = max((self.x as i16) + dx, 0) as usize; | ||||||
| @ -38,9 +34,7 @@ impl Position { | |||||||
|         self.x |         self.x | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     pub fn get_y(&self) -> usize { |     pub fn get_y(&self) -> usize { self.y } | ||||||
|         self.y |  | ||||||
|     } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[test] | #[test] | ||||||
|  | |||||||
							
								
								
									
										280
									
								
								src/room.rs
									
									
									
									
									
								
							
							
						
						
									
										280
									
								
								src/room.rs
									
									
									
									
									
								
							| @ -1,280 +0,0 @@ | |||||||
| use std::{ |  | ||||||
|     cmp::{max, min}, |  | ||||||
|     ops::RangeInclusive, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| use crate::{ |  | ||||||
|     constants::{ |  | ||||||
|         LEVEL_HEIGHT, LEVEL_WIDTH, ROOM_HEIGHT, ROOM_MIN_HEIGHT, ROOM_MIN_WIDTH, ROOM_WIDTH, |  | ||||||
|     }, |  | ||||||
|     level::StructureElement, |  | ||||||
| }; |  | ||||||
| use rand::rngs::ThreadRng; |  | ||||||
| use rand::Rng; |  | ||||||
| 
 |  | ||||||
| #[derive(PartialEq, Copy, Clone, Eq, Hash, Debug)] |  | ||||||
| pub enum RoomType { |  | ||||||
|     Start, |  | ||||||
|     End, |  | ||||||
|     StairUp, |  | ||||||
|     StairDown, |  | ||||||
|     BasicRoom, |  | ||||||
|     ArtifactRoom, |  | ||||||
|     MonsterRoom, |  | ||||||
|     EmptyRoom, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(Copy, Clone, Debug)] |  | ||||||
| pub struct Connection { |  | ||||||
|     pub start_pos: (usize, usize), |  | ||||||
|     pub end_pos: (usize, usize), |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl Connection { |  | ||||||
|     pub fn render( |  | ||||||
|         &self, |  | ||||||
|         rng: &mut ThreadRng, |  | ||||||
|         tgt: &mut [[StructureElement; LEVEL_HEIGHT]; LEVEL_WIDTH], |  | ||||||
|     ) { |  | ||||||
|         fn calc_range(a: usize, b: usize) -> RangeInclusive<usize> { |  | ||||||
|             if a > b { |  | ||||||
|                 b..=a |  | ||||||
|             } else { |  | ||||||
|                 a..=b |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         // the tuples are (col, row)
 |  | ||||||
|         let abs_d_col = |  | ||||||
|             max(self.start_pos.0, self.end_pos.0) - min(self.start_pos.0, self.end_pos.0); |  | ||||||
| 
 |  | ||||||
|         let abs_d_row = |  | ||||||
|             max(self.start_pos.1, self.end_pos.1) - min(self.start_pos.1, self.end_pos.1); |  | ||||||
| 
 |  | ||||||
|         if abs_d_col == 0 { |  | ||||||
|             for row in calc_range(self.end_pos.1, self.start_pos.1) { |  | ||||||
|                 tgt[self.end_pos.0][row] = StructureElement::Floor; |  | ||||||
|             } |  | ||||||
|         } else if abs_d_row == 0 { |  | ||||||
|             for col in calc_range(self.end_pos.0, self.start_pos.0) { |  | ||||||
|                 tgt[col][self.end_pos.1] = StructureElement::Floor; |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             let d_row = self.end_pos.1 as i32 - self.start_pos.1 as i32; |  | ||||||
|             if d_row > 0 { |  | ||||||
|                 // more up/down
 |  | ||||||
|                 let d_row = self.end_pos.1 - self.start_pos.1; |  | ||||||
|                 let vert_offset = if d_row > 3 { |  | ||||||
|                     rng.gen_range(1..=d_row - 1) |  | ||||||
|                 } else { |  | ||||||
|                     d_row / 2 |  | ||||||
|                 }; |  | ||||||
|                 for r in self.start_pos.1..=(self.start_pos.1 + vert_offset) { |  | ||||||
|                     tgt[self.start_pos.0][r] = StructureElement::Floor; |  | ||||||
|                 } |  | ||||||
|                 for c in calc_range(self.end_pos.0, self.start_pos.0) { |  | ||||||
|                     tgt[c][self.start_pos.1 + vert_offset] = StructureElement::Floor; |  | ||||||
|                 } |  | ||||||
|                 for r in (self.start_pos.1 + vert_offset)..=self.end_pos.1 { |  | ||||||
|                     tgt[self.end_pos.0][r] = StructureElement::Floor; |  | ||||||
|                 } |  | ||||||
|             } else { |  | ||||||
|                 // more left/right
 |  | ||||||
|                 let d_col = self.end_pos.0 - self.start_pos.0; |  | ||||||
|                 let horizont_offset = if d_col > 3 { |  | ||||||
|                     rng.gen_range(1..=d_col - 1) |  | ||||||
|                 } else { |  | ||||||
|                     d_col / 2 |  | ||||||
|                 }; |  | ||||||
|                 for tgt_col in tgt |  | ||||||
|                     .iter_mut() |  | ||||||
|                     .skip(self.start_pos.0) |  | ||||||
|                     .take(horizont_offset + 1) |  | ||||||
|                 { |  | ||||||
|                     tgt_col[self.start_pos.1] = StructureElement::Floor; |  | ||||||
|                 } |  | ||||||
|                 for r in calc_range(self.end_pos.1, self.start_pos.1) { |  | ||||||
|                     tgt[self.start_pos.0 + horizont_offset][r] = StructureElement::Floor; |  | ||||||
|                 } |  | ||||||
|                 for tgt_col in tgt |  | ||||||
|                     .iter_mut() |  | ||||||
|                     .take(self.end_pos.0 + 1) |  | ||||||
|                     .skip(self.start_pos.0 + horizont_offset) |  | ||||||
|                 { |  | ||||||
|                     tgt_col[self.end_pos.1] = StructureElement::Floor; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(Copy, Clone, Debug)] |  | ||||||
| pub struct Room { |  | ||||||
|     pub kind: RoomType, |  | ||||||
|     pub offset_x: usize, |  | ||||||
|     pub offset_y: usize, |  | ||||||
|     pub width: usize, |  | ||||||
|     pub height: usize, |  | ||||||
|     pub special: (usize, usize), |  | ||||||
|     pub connection_down: Option<Connection>, |  | ||||||
|     pub connection_right: Option<Connection>, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl Room { |  | ||||||
|     pub fn new(rng: &mut ThreadRng) -> Self { |  | ||||||
|         let width = rng.gen_range(ROOM_MIN_WIDTH..ROOM_WIDTH); |  | ||||||
|         let height = rng.gen_range(ROOM_MIN_HEIGHT..ROOM_HEIGHT); |  | ||||||
|         let offset_x = rng.gen_range(0..(ROOM_WIDTH - width)); |  | ||||||
|         let offset_y = rng.gen_range(0..(ROOM_HEIGHT - height)); |  | ||||||
|         let sx = offset_x + rng.gen_range(1..width - 1); |  | ||||||
|         let sy = offset_y + rng.gen_range(1..height - 1); |  | ||||||
| 
 |  | ||||||
|         Self { |  | ||||||
|             kind: RoomType::EmptyRoom, |  | ||||||
|             offset_x, |  | ||||||
|             offset_y, |  | ||||||
|             width, |  | ||||||
|             height, |  | ||||||
|             special: (sx, sy), |  | ||||||
|             connection_down: None, |  | ||||||
|             connection_right: None, |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // pub fn get_x_range(&self) -> Range<usize> {
 |  | ||||||
|     //     self.offset_x..self.offset_x + self.width
 |  | ||||||
|     // }
 |  | ||||||
| 
 |  | ||||||
|     // pub fn get_y_range(&self) -> Range<usize> {
 |  | ||||||
|     //     self.offset_y..self.offset_y + self.height
 |  | ||||||
|     // }
 |  | ||||||
| 
 |  | ||||||
|     pub fn render( |  | ||||||
|         &self, |  | ||||||
|         tgt: &mut [[StructureElement; LEVEL_HEIGHT]; LEVEL_WIDTH], |  | ||||||
|         col: usize, |  | ||||||
|         row: usize, |  | ||||||
|     ) -> (usize, usize) { |  | ||||||
|         let top = 1 + row * ROOM_HEIGHT; |  | ||||||
|         let left = 1 + col * ROOM_WIDTH; |  | ||||||
|         for col in tgt.iter_mut().skip(left).take(ROOM_WIDTH) { |  | ||||||
|             //left..left + ROOM_WIDTH {
 |  | ||||||
|             for row_element in col.iter_mut().skip(top).take(ROOM_HEIGHT) { |  | ||||||
|                 // top..top + ROOM_HEIGHT {
 |  | ||||||
|                 *row_element = StructureElement::Wall; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         if self.kind == RoomType::EmptyRoom { |  | ||||||
|             return (0, 0); |  | ||||||
|         } |  | ||||||
|         // render floor elements
 |  | ||||||
|         for x in 0..self.width { |  | ||||||
|             for y in 0..self.height { |  | ||||||
|                 tgt[left + self.offset_x + x][top + self.offset_y + y] = StructureElement::Floor; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         match self.kind { |  | ||||||
|             RoomType::Start => { |  | ||||||
|                 tgt[left + self.special.0][top + self.special.1] = StructureElement::Start; |  | ||||||
|             } |  | ||||||
|             RoomType::End => { |  | ||||||
|                 tgt[left + self.special.0][top + self.special.1] = StructureElement::End; |  | ||||||
|             } |  | ||||||
|             RoomType::StairUp => { |  | ||||||
|                 tgt[left + self.special.0][top + self.special.1] = StructureElement::StairUp; |  | ||||||
|             } |  | ||||||
|             RoomType::StairDown => { |  | ||||||
|                 tgt[left + self.special.0][top + self.special.1] = StructureElement::StairDown; |  | ||||||
|             } |  | ||||||
|             _ => {} |  | ||||||
|         }; |  | ||||||
|         (left + self.special.0, top + self.special.1) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[test] |  | ||||||
| fn test_room_creation() { |  | ||||||
|     let mut rng = rand::thread_rng(); |  | ||||||
|     let room = Room::new(&mut rng); |  | ||||||
|     assert_eq!(room.kind, RoomType::EmptyRoom); |  | ||||||
|     for _ in 0..1000 { |  | ||||||
|         let room = Room::new(&mut rng); |  | ||||||
|         assert!((3..=ROOM_WIDTH).contains(&room.width)); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[cfg(test)] |  | ||||||
| fn all_wall( |  | ||||||
|     tgt: &mut [[StructureElement; LEVEL_HEIGHT]; LEVEL_WIDTH], |  | ||||||
|     col: usize, |  | ||||||
|     row: usize, |  | ||||||
| ) -> bool { |  | ||||||
|     let top = 1 + row * ROOM_HEIGHT; |  | ||||||
|     let left = 1 + col * ROOM_WIDTH; |  | ||||||
|     for x in left..left + ROOM_WIDTH { |  | ||||||
|         for y in top..top + ROOM_HEIGHT { |  | ||||||
|             if tgt[x][y] != StructureElement::Wall { |  | ||||||
|                 return false; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     true |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[cfg(test)] |  | ||||||
| fn has_structure_element( |  | ||||||
|     tgt: &mut [[StructureElement; LEVEL_HEIGHT]; LEVEL_WIDTH], |  | ||||||
|     col: usize, |  | ||||||
|     row: usize, |  | ||||||
|     element: StructureElement, |  | ||||||
| ) -> bool { |  | ||||||
|     let top = 1 + row * ROOM_HEIGHT; |  | ||||||
|     let left = 1 + col * ROOM_WIDTH; |  | ||||||
|     for x in left..left + ROOM_WIDTH { |  | ||||||
|         for y in top..top + ROOM_HEIGHT { |  | ||||||
|             if tgt[x][y] == element { |  | ||||||
|                 return true; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     false |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[test] |  | ||||||
| fn test_room_render_empty() { |  | ||||||
|     let mut rng = rand::thread_rng(); |  | ||||||
|     let room = Room::new(&mut rng); |  | ||||||
|     assert_eq!(room.kind, RoomType::EmptyRoom); |  | ||||||
|     let mut structure = [[StructureElement::Floor; LEVEL_HEIGHT]; LEVEL_WIDTH]; |  | ||||||
| 
 |  | ||||||
|     room.render(&mut structure, 0, 0); |  | ||||||
|     assert!(all_wall(&mut structure, 0, 0)); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[test] |  | ||||||
| fn test_room_render_basic() { |  | ||||||
|     let mut rng = rand::thread_rng(); |  | ||||||
|     let mut room = Room::new(&mut rng); |  | ||||||
|     room.kind = RoomType::BasicRoom; |  | ||||||
|     let mut structure = [[StructureElement::Floor; LEVEL_HEIGHT]; LEVEL_WIDTH]; |  | ||||||
| 
 |  | ||||||
|     room.render(&mut structure, 1, 3); |  | ||||||
|     assert!(!all_wall(&mut structure, 1, 3)); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[test] |  | ||||||
| fn test_room_render_start() { |  | ||||||
|     let mut rng = rand::thread_rng(); |  | ||||||
|     let mut room = Room::new(&mut rng); |  | ||||||
|     room.kind = RoomType::Start; |  | ||||||
|     let mut structure = [[StructureElement::Floor; LEVEL_HEIGHT]; LEVEL_WIDTH]; |  | ||||||
| 
 |  | ||||||
|     room.render(&mut structure, 1, 3); |  | ||||||
|     assert!(!all_wall(&mut structure, 1, 3)); |  | ||||||
|     assert!(has_structure_element( |  | ||||||
|         &mut structure, |  | ||||||
|         1, |  | ||||||
|         3, |  | ||||||
|         StructureElement::Start |  | ||||||
|     )); |  | ||||||
| } |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user