diff --git a/.cargo/config.toml b/.cargo/config.toml index b6c3f68..5a34c43 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -3,6 +3,10 @@ rustflags = [ "-C", "linker=gcc", "-L", "/usr/lib", ] +[target.'cfg(all(target_os = "windows"))'] +rustflags = [ + "-C", "target-feature=+crt-static", +] [target.'cfg(target_abi = "musl")'] rustflags = [ diff --git a/BITCRUSH.NOTE b/BITCRUSH.txt similarity index 82% rename from BITCRUSH.NOTE rename to BITCRUSH.txt index a8889be..b776250 100644 --- a/BITCRUSH.NOTE +++ b/BITCRUSH.txt @@ -3,4 +3,4 @@ ultimately masking the voice making the sound. ffmpeg -i Rustemon_Intro_Sound.m4a -af acrusher=.2:.55:32:0.7:log:0.25:1:20 Rustemon_Intro_Sound.wav -It may need to be adjusted for other audio files. +It may need to be adjusted for other audio files. \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 2ba931a..15b0705 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,18 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + [[package]] name = "ahash" version = "0.8.11" @@ -16,15 +28,21 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "autocfg" -version = "1.2.0" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "bit_field" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" [[package]] name = "bitflags" @@ -34,15 +52,21 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] -name = "c_vec" -version = "2.0.0" +name = "bytemuck" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdd7a427adc0135366d99db65b36dae9237130997e560ed61118041fb72be6e8" +checksum = "94bbb0ad554ad961ddc5da507a12a29b14e4ae5bda06b19f575a3e6079d2e2ae" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cassowary" @@ -52,9 +76,9 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" [[package]] name = "castaway" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc" +checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5" dependencies = [ "rustversion", ] @@ -65,6 +89,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + [[package]] name = "compact_str" version = "0.7.1" @@ -78,13 +108,47 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + [[package]] name = "crossterm" version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "crossterm_winapi", "libc", "mio", @@ -103,28 +167,98 @@ dependencies = [ "winapi", ] +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + [[package]] name = "either" -version = "1.10.0" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "exr" +version = "1.72.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4" +dependencies = [ + "bit_field", + "flume", + "half", + "lebe", + "miniz_oxide 0.7.4", + "rayon-core", + "smallvec", + "zune-inflate", +] + +[[package]] +name = "fdeflate" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "flate2" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +checksum = "324a1be68054ef05ad64b861cc9eaf1d623d2d8cb25b4bf2cb9cdd902b4bf253" +dependencies = [ + "crc32fast", + "miniz_oxide 0.8.0", +] + +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "spin", +] [[package]] name = "getrandom" -version = "0.2.12" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", "wasi", ] +[[package]] +name = "gif" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", "allocator-api2", @@ -132,15 +266,27 @@ dependencies = [ [[package]] name = "heck" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] -name = "indoc" -version = "2.0.5" +name = "image" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "exr", + "gif", + "jpeg-decoder", + "num-traits", + "png", + "qoi", + "tiff", +] [[package]] name = "itertools" @@ -151,29 +297,53 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "jpeg-decoder" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" +dependencies = [ + "rayon", +] + [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lebe" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -181,9 +351,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lru" @@ -194,6 +364,25 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", + "simd-adler32", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + [[package]] name = "mio" version = "0.8.11" @@ -206,6 +395,15 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "once_cell" version = "1.19.0" @@ -214,9 +412,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -224,28 +422,42 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] name = "paste" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "png" +version = "0.17.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide 0.7.4", +] [[package]] name = "pokemon_structs" version = "0.1.0" dependencies = [ "crossterm", + "image", "rand", "ratatui", "sdl2", @@ -259,18 +471,27 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.79" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -307,44 +528,64 @@ dependencies = [ [[package]] name = "ratatui" -version = "0.26.1" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcb12f8fbf6c62614b0d56eb352af54f6a22410c3b079eb53ee93c7b97dd31d8" +checksum = "f44c9e68fd46eda15c646fbb85e1040b657a58cdc8c98db1d97a55930d991eef" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "cassowary", "compact_str", "crossterm", - "indoc", - "itertools", + "itertools 0.12.1", "lru", "paste", "stability", "strum", "unicode-segmentation", + "unicode-truncate", "unicode-width", ] +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", ] [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "scopeguard" @@ -354,11 +595,10 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sdl2" -version = "0.36.0" -source = "git+https://github.com/rust-sdl2/rust-sdl2#79f8ad02fef30b8dcea2e3513b00dbf745d9596c" +version = "0.37.0" +source = "git+https://github.com/rust-sdl2/rust-sdl2#dba66e80b14e16de309df49df0c20fdaf35b8c67" dependencies = [ "bitflags 1.3.2", - "c_vec", "lazy_static", "libc", "sdl2-sys", @@ -366,8 +606,8 @@ dependencies = [ [[package]] name = "sdl2-sys" -version = "0.36.0" -source = "git+https://github.com/rust-sdl2/rust-sdl2#79f8ad02fef30b8dcea2e3513b00dbf745d9596c" +version = "0.37.0" +source = "git+https://github.com/rust-sdl2/rust-sdl2#dba66e80b14e16de309df49df0c20fdaf35b8c67" dependencies = [ "cfg-if", "libc", @@ -397,27 +637,42 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + [[package]] name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + [[package]] name = "stability" -version = "0.1.1" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebd1b177894da2a2d9120208c3386066af06a488255caabc5de8ddca22dbc3ce" +checksum = "d904e7009df136af5297832a3ace3370cd14ff1546a232f4f185036c2736fcac" dependencies = [ "quote", - "syn 1.0.109", + "syn", ] [[package]] @@ -428,31 +683,31 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "strum" -version = "0.26.2" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ "heck", "proc-macro2", "quote", "rustversion", - "syn 2.0.55", + "syn", ] [[package]] name = "syn" -version = "1.0.109" +version = "2.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" dependencies = [ "proc-macro2", "quote", @@ -460,14 +715,14 @@ dependencies = [ ] [[package]] -name = "syn" -version = "2.0.55" +name = "tiff" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0" +checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", + "flate2", + "jpeg-decoder", + "weezl", ] [[package]] @@ -482,11 +737,22 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +[[package]] +name = "unicode-truncate" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" +dependencies = [ + "itertools 0.13.0", + "unicode-segmentation", + "unicode-width", +] + [[package]] name = "unicode-width" -version = "0.1.11" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "version-compare" @@ -496,9 +762,9 @@ checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wasi" @@ -506,6 +772,12 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "weezl" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" + [[package]] name = "winapi" version = "0.3.9" @@ -534,7 +806,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -543,13 +815,29 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[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]] @@ -558,58 +846,115 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" 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]] name = "zerocopy" -version = "0.7.32" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.55", + "syn", +] + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", ] diff --git a/Cargo.toml b/Cargo.toml index bb0f76d..6dc3b35 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,10 +14,11 @@ panic = "abort" [dependencies] crossterm = "0.27.0" +image = "0.24" rand = "0.8.5" ratatui = "0.26.1" -[target.'cfg(all(target_os = "android"))'.dependencies.sdl2] +[target.'cfg(any(target_os = "android"))'.dependencies.sdl2] git = "https://github.com/rust-sdl2/rust-sdl2" default-features = false features = ["ttf","image","mixer"]#,"gfx"] @@ -25,5 +26,5 @@ features = ["ttf","image","mixer"]#,"gfx"] [target.'cfg(not(target_os = "android"))'.dependencies.sdl2] git = "https://github.com/rust-sdl2/rust-sdl2" default-features = false -features = ["ttf","image","mixer","static-link","gfx"]#,"bundled"] +features = ["ttf","mixer"]#,"image","static-link","gfx","bundled"] diff --git a/Dream(test).wav b/Dream(test).wav new file mode 100644 index 0000000..725c1c8 Binary files /dev/null and b/Dream(test).wav differ diff --git a/Dream.wav b/Dream.wav new file mode 100644 index 0000000..33433c5 Binary files /dev/null and b/Dream.wav differ diff --git a/Mysterious_Cyborg.wav b/Mysterious_Cyborg.wav new file mode 100644 index 0000000..7edbba3 Binary files /dev/null and b/Mysterious_Cyborg.wav differ diff --git a/TESTFILE.txt b/TESTFILE.txt new file mode 100644 index 0000000..e69de29 diff --git a/TODO.txt b/TODO.txt new file mode 100644 index 0000000..4d40fc6 --- /dev/null +++ b/TODO.txt @@ -0,0 +1,27 @@ +GRAPHICS: + - Reimplement intro using the gfx submodule for proper graphics rendering + - ( Retain terminal-based portion for android testing / development as the creation of a window with the NDK doesn't work properly; and on that note, test building for android to see if using the gfx bindings would work at all ) + +AUDIO: + - Complete midi writing and testing for themes/sound effects + - Use Dream.wav for character creation sequence + +FONTS: + - Look into using custom font to allow use of custom characters for special cases (e.g. for key items, special in-game objects, etc.) + - Refer to `rcd sdl2_test`, its usage of the tff submodule, and implement something like it to display text in a proper window + - Refer to fontforge docs and the style of the PokemonGb ttf to implement special characters as needed + +INPUT: + - Refer to the event processing in src/menu/mod.rs to only process the last given (valid) event + - Implement interactivity into the dialog box, as well as a border. The border should use the same one + as indexed by the global options + - Time-based input refresh used in main menu should probably be used everywhere + +SAVES: + - Basic global options saving functionality is present, but needs to be expanded as game is developed further + - Conditionally store/read save files in/from different locations based on OS + +IMPLEMENTATION NOTES/IDEAS/SPECIFICS: + - How to come up with consistently unique player IDs (for use in Monster struct)? + - Party should have a slice of &mut Monster, up to 6 + - in src/menu/mod.rs, instead of matching on the event code THEN the selected menu option, match on the menu option THEN the event code (button press in this case) diff --git a/build.rs b/build.rs index 6fcd1bb..3a8456c 100644 --- a/build.rs +++ b/build.rs @@ -10,7 +10,15 @@ use std::io::prelude::*; use std::iter::Iterator; use std::*; + fn main() { + // Only run if files have changed + println!(r#"cargo::rerun-if-changed=build.rs"#); + println!(r#"cwrgo::rerun-if-changed=realnames.txt"#); + println!(r#"cwrgo::rerun-if-changed=enumnames.txt"#); + println!(r#"cwrgo::rerun-if-changed=types.txt"#); + println!(r#"cwrgo::rerun-if-changed=stats.txt"#); + // real names in English let mut real_names = fs::read_to_string("realnames.txt") .unwrap_or_else(|_| { @@ -145,7 +153,7 @@ fn main() { } code.push_str("];\n\n"); -// penum.write(code.as_bytes()); +// penum.write_all(code.as_bytes()); // code = String::new(); // impl block @@ -215,7 +223,7 @@ fn main() { // close impl code.push_str("}\n\n"); -// pimpl.write(code.as_bytes()); +// pimpl.write_all(code.as_bytes()); // code = String::new(); // get Pokemon from dex number (made obsolete by `pub const Pokemon: [Pokemon; 1026] = ...` @@ -231,11 +239,11 @@ fn main() { code.push_str("\t\t_ => { println!(\"Invalid dex number. \"); Pokemon::Bulbasaur },\n"); code.push_str("\t}\n}\n\n"); - // pfuncs.write(code.as_bytes()); + // pfuncs.write_all(code.as_bytes()); let mut source_file = fs::File::create("src/mons.rs").expect("Unable to create `src/mons.rs`."); source_file.set_len(0); - source_file.write(code.as_bytes()); + source_file.write_all(code.as_bytes()); println!("End of code generation."); diff --git a/buildtargets.sh b/buildtargets.sh new file mode 100644 index 0000000..532920f --- /dev/null +++ b/buildtargets.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +cargotarget() { + #First argument is the desired target/toolchain, the rest are passed directly to cargo + #(such as '-vvv' or 'build/run') + local target="$1" + shift 1 + cargo "+stable-$target" "$@" "--target=$target" +} + +HOST_ARCH="${MSYSTEM_CHOST%%-*}" + + + +BUILDRUN="build" + +if [ "$(echo $1 | tr '[:upper:]' '[:lower:]')" = "run" ] ; then + + if [ "$HOST_ARCH" = "i686" ] ; then + BUILDRUN="run" + else + echo "Incompatible host architecture. Building only." + fi +fi + +if [ "$(echo $2 | tr '[:upper:]' '[:lower:]')" = "release" ] ; then + cargotarget "i686-pc-windows-gnu" -vv "$BUILDRUN" "--release" +else + cargotarget "i686-pc-windows-gnu" -vv "$BUILDRUN" +fi + + +BUILDRUN="build" + +if [ "$(echo $1 | tr '[:upper:]' '[:lower:]')" = "run" ] ; then + + if [ "$HOST_ARCH" = "x86_64" ] ; then + BUILDRUN="run" + else + echo "Incompatible host architecture. Building only." + fi +fi + +if [ "$(echo $2 | tr '[:upper:]' '[:lower:]')" = "release" ] ; then + cargotarget "x86_64-pc-windows-gnu" -vv "$BUILDRUN" "--release" +else + cargotarget "x86_64-pc-windows-gnu" -vv "$BUILDRUN" +fi + + +# TEMPORARY (until audio data is compiled into binary) +for i in target/*/debug target/*/release +do + cp *.wav "$i" +done diff --git a/cfg.txt b/cfg.txt new file mode 100644 index 0000000..fff0e84 --- /dev/null +++ b/cfg.txt @@ -0,0 +1,748 @@ +debug_assertions +panic="unwind" +target_abi="" +target_arch="aarch64" +target_endian="little" +target_env="" +target_family="unix" +target_feature="neon" +target_has_atomic="128" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="android" +target_pointer_width="64" +target_vendor="unknown" +unix +debug_assertions +feature="default" +feature="std" +panic="unwind" +target_abi="" +target_arch="aarch64" +target_endian="little" +target_env="" +target_family="unix" +target_feature="neon" +target_has_atomic="128" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="android" +target_pointer_width="64" +target_vendor="unknown" +unix +debug_assertions +panic="unwind" +target_abi="" +target_arch="aarch64" +target_endian="little" +target_env="" +target_family="unix" +target_feature="neon" +target_has_atomic="128" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="android" +target_pointer_width="64" +target_vendor="unknown" +unix +debug_assertions +feature="default" +feature="std" +panic="unwind" +target_abi="" +target_arch="aarch64" +target_endian="little" +target_env="" +target_family="unix" +target_feature="neon" +target_has_atomic="128" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="android" +target_pointer_width="64" +target_vendor="unknown" +unix +debug_assertions +panic="unwind" +target_abi="" +target_arch="aarch64" +target_endian="little" +target_env="" +target_family="unix" +target_feature="neon" +target_has_atomic="128" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="android" +target_pointer_width="64" +target_vendor="unknown" +unix +debug_assertions +feature="default" +feature="proc-macro" +panic="unwind" +target_abi="" +target_arch="aarch64" +target_endian="little" +target_env="" +target_family="unix" +target_feature="neon" +target_has_atomic="128" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="android" +target_pointer_width="64" +target_vendor="unknown" +unix +debug_assertions +panic="unwind" +target_abi="" +target_arch="aarch64" +target_endian="little" +target_env="" +target_family="unix" +target_feature="neon" +target_has_atomic="128" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="android" +target_pointer_width="64" +target_vendor="unknown" +unix +debug_assertions +feature="use_std" +panic="unwind" +target_abi="" +target_arch="aarch64" +target_endian="little" +target_env="" +target_family="unix" +target_feature="neon" +target_has_atomic="128" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="android" +target_pointer_width="64" +target_vendor="unknown" +unix +debug_assertions +panic="unwind" +target_abi="" +target_arch="aarch64" +target_endian="little" +target_env="" +target_family="unix" +target_feature="neon" +target_has_atomic="128" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="android" +target_pointer_width="64" +target_vendor="unknown" +unix +debug_assertions +panic="unwind" +target_abi="" +target_arch="aarch64" +target_endian="little" +target_env="" +target_family="unix" +target_feature="neon" +target_has_atomic="128" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="android" +target_pointer_width="64" +target_vendor="unknown" +unix +debug_assertions +panic="unwind" +target_abi="" +target_arch="aarch64" +target_endian="little" +target_env="" +target_family="unix" +target_feature="neon" +target_has_atomic="128" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="android" +target_pointer_width="64" +target_vendor="unknown" +unix +debug_assertions +feature="const-generics" +feature="default" +feature="std" +panic="unwind" +target_abi="" +target_arch="aarch64" +target_endian="little" +target_env="" +target_family="unix" +target_feature="neon" +target_has_atomic="128" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="android" +target_pointer_width="64" +target_vendor="unknown" +unix +debug_assertions +panic="unwind" +target_abi="" +target_arch="aarch64" +target_endian="little" +target_env="" +target_family="unix" +target_feature="neon" +target_has_atomic="128" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="android" +target_pointer_width="64" +target_vendor="unknown" +unix +debug_assertions +feature="default" +feature="std" +panic="unwind" +target_abi="" +target_arch="aarch64" +target_endian="little" +target_env="" +target_family="unix" +target_feature="neon" +target_has_atomic="128" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="android" +target_pointer_width="64" +target_vendor="unknown" +unix +debug_assertions +feature="default" +feature="std" +freebsd11 +libc_align +libc_cfg_target_vendor +libc_const_extern_fn +libc_const_size_of +libc_core_cvoid +libc_int128 +libc_long_array +libc_non_exhaustive +libc_packedN +libc_priv_mod_use +libc_ptr_addr_of +libc_underscore_const_names +libc_union +panic="unwind" +target_abi="" +target_arch="aarch64" +target_endian="little" +target_env="" +target_family="unix" +target_feature="neon" +target_has_atomic="128" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="android" +target_pointer_width="64" +target_vendor="unknown" +unix +debug_assertions +feature="atomic_usize" +feature="default" +panic="unwind" +target_abi="" +target_arch="aarch64" +target_endian="little" +target_env="" +target_family="unix" +target_feature="neon" +target_has_atomic="128" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="android" +target_pointer_width="64" +target_vendor="unknown" +unix +debug_assertions +panic="unwind" +proc_macro +target_abi="" +target_arch="aarch64" +target_endian="little" +target_env="" +target_family="unix" +target_feature="neon" +target_has_atomic="128" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="android" +target_pointer_width="64" +target_vendor="unknown" +unix +debug_assertions +panic="unwind" +target_abi="" +target_arch="aarch64" +target_endian="little" +target_env="" +target_family="unix" +target_feature="neon" +target_has_atomic="128" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="android" +target_pointer_width="64" +target_vendor="unknown" +unix +debug_assertions +feature="channel" +feature="default" +feature="iterator" +panic="unwind" +target_abi="" +target_arch="aarch64" +target_endian="little" +target_env="" +target_family="unix" +target_feature="neon" +target_has_atomic="128" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="android" +target_pointer_width="64" +target_vendor="unknown" +unix +debug_assertions +panic="unwind" +target_abi="" +target_arch="aarch64" +target_endian="little" +target_env="" +target_family="unix" +target_feature="neon" +target_has_atomic="128" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="android" +target_pointer_width="64" +target_vendor="unknown" +unix +debug_assertions +feature="alloc" +feature="std" +panic="unwind" +target_abi="" +target_arch="aarch64" +target_endian="little" +target_env="" +target_family="unix" +target_feature="neon" +target_has_atomic="128" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="android" +target_pointer_width="64" +target_vendor="unknown" +unix +debug_assertions +panic="unwind" +target_abi="" +target_arch="aarch64" +target_endian="little" +target_env="" +target_family="unix" +target_feature="neon" +target_has_atomic="128" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="android" +target_pointer_width="64" +target_vendor="unknown" +unix +debug_assertions +panic="unwind" +target_abi="" +target_arch="aarch64" +target_endian="little" +target_env="" +target_family="unix" +target_feature="neon" +target_has_atomic="128" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="android" +target_pointer_width="64" +target_vendor="unknown" +unix +debug_assertions +feature="default" +feature="std" +panic="unwind" +target_abi="" +target_arch="aarch64" +target_endian="little" +target_env="" +target_family="unix" +target_feature="neon" +target_has_atomic="128" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="android" +target_pointer_width="64" +target_vendor="unknown" +unix +debug_assertions +feature="alloc" +feature="race" +panic="unwind" +target_abi="" +target_arch="aarch64" +target_endian="little" +target_env="" +target_family="unix" +target_feature="neon" +target_has_atomic="128" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="android" +target_pointer_width="64" +target_vendor="unknown" +unix +debug_assertions +panic="unwind" +target_abi="" +target_arch="aarch64" +target_endian="little" +target_env="" +target_family="unix" +target_feature="neon" +target_has_atomic="128" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="android" +target_pointer_width="64" +target_vendor="unknown" +unix +debug_assertions +feature="default" +feature="image" +feature="mixer" +feature="ttf" +panic="unwind" +target_abi="" +target_arch="aarch64" +target_endian="little" +target_env="" +target_family="unix" +target_feature="neon" +target_has_atomic="128" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="android" +target_pointer_width="64" +target_vendor="unknown" +unix +debug_assertions +feature="default" +feature="std" +panic="unwind" +target_abi="" +target_arch="aarch64" +target_endian="little" +target_env="" +target_family="unix" +target_feature="neon" +target_has_atomic="128" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="android" +target_pointer_width="64" +target_vendor="unknown" +unix +debug_assertions +feature="default" +feature="proc-macro" +panic="unwind" +proc_macro_span +target_abi="" +target_arch="aarch64" +target_endian="little" +target_env="" +target_family="unix" +target_feature="neon" +target_has_atomic="128" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="android" +target_pointer_width="64" +target_vendor="unknown" +unix +wrap_proc_macro +debug_assertions +feature="with-alloc" +panic="unwind" +target_abi="" +target_arch="aarch64" +target_endian="little" +target_env="" +target_family="unix" +target_feature="neon" +target_has_atomic="128" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="android" +target_pointer_width="64" +target_vendor="unknown" +unix +debug_assertions +feature="atomic_usize" +feature="default" +has_const_fn_trait_bound +panic="unwind" +target_abi="" +target_arch="aarch64" +target_endian="little" +target_env="" +target_family="unix" +target_feature="neon" +target_has_atomic="128" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="android" +target_pointer_width="64" +target_vendor="unknown" +unix +debug_assertions +feature="simd" +panic="unwind" +target_abi="" +target_arch="aarch64" +target_endian="little" +target_env="" +target_family="unix" +target_feature="neon" +target_has_atomic="128" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="android" +target_pointer_width="64" +target_vendor="unknown" +unix +debug_assertions +feature="channel" +feature="default" +feature="iterator" +panic="unwind" +target_abi="" +target_arch="aarch64" +target_endian="little" +target_env="" +target_family="unix" +target_feature="neon" +target_has_atomic="128" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="android" +target_pointer_width="64" +target_vendor="unknown" +unix +debug_assertions +panic="unwind" +target_abi="" +target_arch="aarch64" +target_endian="little" +target_env="" +target_family="unix" +target_feature="neon" +target_has_atomic="128" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="android" +target_pointer_width="64" +target_vendor="unknown" +unix +debug_assertions +panic="unwind" +target_abi="" +target_arch="aarch64" +target_endian="little" +target_env="" +target_family="unix" +target_feature="neon" +target_has_atomic="128" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="android" +target_pointer_width="64" +target_vendor="unknown" +unix +debug_assertions +feature="default" +feature="proc-macro" +panic="unwind" +target_abi="" +target_arch="aarch64" +target_endian="little" +target_env="" +target_family="unix" +target_feature="neon" +target_has_atomic="128" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="android" +target_pointer_width="64" +target_vendor="unknown" +unix +debug_assertions +feature="any_impl" +feature="default" +feature="miniz_oxide" +feature="rust_backend" +panic="unwind" +target_abi="" +target_arch="aarch64" +target_endian="little" +target_env="" +target_family="unix" +target_feature="neon" +target_has_atomic="128" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="android" +target_pointer_width="64" +target_vendor="unknown" +unix +debug_assertions +panic="unwind" +target_abi="" +target_arch="aarch64" +target_endian="little" +target_env="" +target_family="unix" +target_feature="neon" +target_has_atomic="128" +target_has_atomic="16" +target_has_atomic="32" +target_has_atomic="64" +target_has_atomic="8" +target_has_atomic="ptr" +target_os="android" +target_pointer_width="64" +target_vendor="unknown" +unix diff --git a/config.log b/config.log new file mode 100644 index 0000000..a2a84ae --- /dev/null +++ b/config.log @@ -0,0 +1,10 @@ +core.repositoryformatversion=0 +core.filemode=false +core.bare=false +core.logallrefupdates=true +core.symlinks=false +core.ignorecase=true +remote.origin.url=git@github.com:ionarevamp/Rustemon.git +remote.origin.fetch=+refs/heads/*:refs/remotes/origin/* +branch.master.remote=origin +branch.master.merge=refs/heads/master diff --git a/font/PokemonGb-RAeo.ttf b/font/PokemonGb-RAeo.ttf new file mode 100644 index 0000000..b5025f0 Binary files /dev/null and b/font/PokemonGb-RAeo.ttf differ diff --git a/font/PokemonGbJapanHrregular-Bq93.ttf b/font/PokemonGbJapanHrregular-Bq93.ttf new file mode 100644 index 0000000..72a887f Binary files /dev/null and b/font/PokemonGbJapanHrregular-Bq93.ttf differ diff --git a/font/PokemonGbJapanKt-8Rw2.ttf b/font/PokemonGbJapanKt-8Rw2.ttf new file mode 100644 index 0000000..d4e0b2f Binary files /dev/null and b/font/PokemonGbJapanKt-8Rw2.ttf differ diff --git a/font/PokemonUnownGb-YAWa.ttf b/font/PokemonUnownGb-YAWa.ttf new file mode 100644 index 0000000..3aafe31 Binary files /dev/null and b/font/PokemonUnownGb-YAWa.ttf differ diff --git a/font/info.txt b/font/info.txt new file mode 100644 index 0000000..355523a --- /dev/null +++ b/font/info.txt @@ -0,0 +1,2 @@ +license: Freeware +link: https://www.fontspace.com/pokemon-gb-font-f9621 \ No newline at end of file diff --git a/font/pokemon-gb-font.zip b/font/pokemon-gb-font.zip new file mode 100644 index 0000000..7da3d40 Binary files /dev/null and b/font/pokemon-gb-font.zip differ diff --git a/incremental_vs_not.txt b/incremental_vs_not.txt new file mode 100644 index 0000000..0dda9e3 --- /dev/null +++ b/incremental_vs_not.txt @@ -0,0 +1,9 @@ +time cargotarget x86_64-pc-windows-gnu build --release: (USB 3.2.1 external drive) +with `CARGO_INCREMENTAL=1`: ~25m +with `CARGO_INCREMENTAL=0`: ~20m + +This is probably due to running cargo clean beforehand each time. + +without cargo clean (no code changes): +incremental on: 0.51s +incremental off: 0.75s \ No newline at end of file diff --git a/libdinput8.a b/libdinput8.a new file mode 100644 index 0000000..db444d4 Binary files /dev/null and b/libdinput8.a differ diff --git a/libdxguid.a b/libdxguid.a new file mode 100644 index 0000000..4a08391 Binary files /dev/null and b/libdxguid.a differ diff --git a/libimm32.a b/libimm32.a new file mode 100644 index 0000000..48df6da Binary files /dev/null and b/libimm32.a differ diff --git a/libversion.a b/libversion.a new file mode 100644 index 0000000..3412ef5 Binary files /dev/null and b/libversion.a differ diff --git a/pokemon_structs b/pokemon_structs new file mode 100644 index 0000000..507f39e Binary files /dev/null and b/pokemon_structs differ diff --git a/sound.log b/sound.log new file mode 100644 index 0000000..e69de29 diff --git a/sound/354024__stondi__thunder-during-rainstorm-2.wav b/sound/354024__stondi__thunder-during-rainstorm-2.wav new file mode 100644 index 0000000..f89ade9 Binary files /dev/null and b/sound/354024__stondi__thunder-during-rainstorm-2.wav differ diff --git a/sound/575613__the_frisbee_of_peace__rainstorm-medium-intensity.wav b/sound/575613__the_frisbee_of_peace__rainstorm-medium-intensity.wav new file mode 100644 index 0000000..0d512e4 Binary files /dev/null and b/sound/575613__the_frisbee_of_peace__rainstorm-medium-intensity.wav differ diff --git a/sound/thunder.wav b/sound/thunder.wav new file mode 100644 index 0000000..760dccc Binary files /dev/null and b/sound/thunder.wav differ diff --git a/sound/trimaudio.sh b/sound/trimaudio.sh new file mode 100644 index 0000000..60ccf55 --- /dev/null +++ b/sound/trimaudio.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +startfile="$1" +shift 1 +outfile="$1" +shift 1 +begin="$1" +shift 1 +end="$1" +shift 1 + +# in case I want to pass the -y flag +force="$1" + +ffmpeg $force -ss $begin -i "$startfile" -to $end "$outfile" && \ + ffplay -fast -nodisp -autoexit -loglevel -16 $outfile + +if [ -f $outfile ]; then + echo "Used $startfile: $begin to $end, to produce $outfile" \ + > "${outfile%%.*}".txt + cat "${outfile%%.*}".txt +else + echo "Usage: ./trimaudio.sh " +fi diff --git a/src/area/mod.rs b/src/area/mod.rs new file mode 100644 index 0000000..be05dfe --- /dev/null +++ b/src/area/mod.rs @@ -0,0 +1,3 @@ +pub enum Area { + TestZone +} diff --git a/src/extras/mod.rs b/src/extras/mod.rs index 1dfef87..e75f33f 100644 --- a/src/extras/mod.rs +++ b/src/extras/mod.rs @@ -10,10 +10,14 @@ use std::path::{Path, PathBuf}; use std::thread::{self, *}; use std::time::{Duration, Instant}; - -// TODO: change `data` to be of type `Vec<(u8,f32)>` (preferably aligned) -// TODO: ^^ also change fade funcs -#[repr(Rust, align(128))] +//DEBUG +use std::fs::File; +use std::fs::OpenOptions; +use std::io::Write; +//END DEBUG + +// TODO: figure out a more efficient way to set volume for a given portion of audio +#[repr(C)] pub struct Sound { data: Vec<(u8, f32)>, //volume: f32, @@ -28,7 +32,7 @@ pub struct OneWaySound { // buffer defined by a sound file impl Sound { pub fn volume(&self) -> f32 { - self.data[self.pos].1.clone() + self.data[self.pos].1 } pub fn pos(&self) -> usize { self.pos @@ -61,16 +65,28 @@ impl Sound { } // fade_in and fade_out are linear, while fade_percent is logarithmic - pub fn fade_percent(&mut self, rate: f32, limit: f32) { + pub fn fade_percent(&mut self, rate: f32, limit: f32) -> String { // rate can be negative, and determines whether limit // is an upper bound or lower bound // + let mut log = String::new(); + let rate = rate / 100.0; for p in self.pos..(self.pos+u16::MAX as usize) { - if p >= self.len() { break; } + if p >= self.len() { + let _ = log.push_str(format!("Encountered maximum file size at iteration {}, breaking loop.\n", p).as_str()); + break; + } + else if p == self.pos+u16::MAX as usize { + for pp in p..self.len() { + self.data[pp].1 = limit; + + let _ = log.push_str("Encountered maximum iteration, setting volume to limit.\n"); + } + } if rate < 0.0 { if self.data[p].1 > limit { - self.data[p].1 *= (1.0 + rate); + self.data[p].1 *= 1.0 + rate; } if self.data[p].1 < limit { self.data[p].1 = limit; @@ -80,13 +96,17 @@ impl Sound { self.data[p].1 = 0.1; } if self.data[p].1 < limit { - self.data[p].1 *= (1.0 + rate); + self.data[p].1 *= 1.0 + rate; + let _ = log.push_str(format!("Changing volume to {}\n", self.data[p].1).as_str()); } if self.data[p].1 > limit { + let _ = log.push_str(format!("Setting volume to limit from {}\n", self.data[p].1).as_str()); self.data[p].1 = limit; } } } + + log } pub fn restart(&mut self) { self.pos = 0; @@ -106,8 +126,8 @@ impl Sound { } //Updates the volume for the rest of the buffer. pub fn set_volume(&mut self, volume: f32) { - let len = self.data.len()-1; - for p in self.pos..=len { + let len = self.data.len(); + for p in self.pos..len { self.data[p].1 = volume; } } @@ -142,6 +162,9 @@ impl AudioCallback for Sound { .get(self.pos) .unwrap_or(&(128,0.0)) ); + + //let volume = self.volume; + let scaled_signed_float = (pre_scale.0 as f32 - 128.0) * pre_scale.1; let mut scaled = @@ -339,7 +362,8 @@ pub fn loop_test() -> Result<()> { if skip { - + let mut log = File::create(Path::new("sound.log")).unwrap(); + let mut log_string = String::new(); let mut queue_device = generate_sound(&audio_subsystem, &path4, 0.0, 0).unwrap(); for _ in 0..1 { queue_device.lock().queue(&path4, 0.0); @@ -349,14 +373,15 @@ pub fn loop_test() -> Result<()> { let audio_max_pos = queue_device.lock().len() - 1; while queue_device.lock().pos() < audio_max_pos { + log_string.push_str(queue_device.lock().fade_percent(10.0, 0.7).as_str()); use std::io::Write; - queue_device.lock().fade_percent(10.0, 0.7); print!("\x1b[s\x1b[2K Current volume (1.0 = max/normal): {:.4}\x1b[u", queue_device.lock().volume()); std::io::stdout().flush(); //queue_device.lock().fade_in(0.08, 0.7); sleep(Duration::from_millis(99)); } + let _ = log.write(log_string.as_bytes()); } diff --git a/src/interface.rs b/src/interface.rs new file mode 100644 index 0000000..2e8fe11 --- /dev/null +++ b/src/interface.rs @@ -0,0 +1,203 @@ + +#![allow(unused_imports)] +#![allow(unused_must_use)] +#![allow(unused_parens)] +use crate::extras::*; +use crate::mons::*; +use crate::moves::{Effect, PokeMove, Target}; +use crate::natures::Nature; +use crate::types::*; + +use rand::prelude::*; +use std::io::Write; +use std::process::Command; +use std::sync::mpsc::*; +use std::thread::{self, sleep, JoinHandle}; + +use std::{ + io, + time::{Duration, Instant}, +}; + +use sdl2::audio::{ + AudioCVT, AudioCallback, AudioDevice, AudioFormatNum, AudioSpec, AudioSpecDesired, AudioSpecWAV, +}; +use sdl2::AudioSubsystem; +use sdl2::mixer::{self, *}; + +use std::borrow::Cow; +use std::path::{Path, PathBuf}; + +#[allow(unused_imports)] +use ratatui::{ + symbols::border::Set, + backend::CrosstermBackend, + layout::{Constraint, Direction, Layout, Rect}, + style::{Color::{self, *}, Style}, + text::Span, + widgets::{Block, Borders, BorderType, List, ListItem, Tabs, Widget}, + Terminal, +}; + +#[allow(unused_imports)] +use crossterm::{ + style::{ + Color::{self as CrosstermColor, *}, + SetForegroundColor, + SetBackgroundColor, + Print + }, + cursor::{ + DisableBlinking, EnableBlinking, Hide, MoveDown, MoveTo, MoveUp, RestorePosition, + SavePosition, Show, + }, + event::{ + self, poll, read, DisableMouseCapture, EnableMouseCapture, Event, + KeyCode::{self, *}, + KeyEvent, KeyEventKind, KeyModifiers, + KeyboardEnhancementFlags, + PushKeyboardEnhancementFlags, + PopKeyboardEnhancementFlags + }, + execute, + terminal::{ + self, + disable_raw_mode, enable_raw_mode, Clear, ClearType, EnterAlternateScreen, + LeaveAlternateScreen, size + }, + ExecutableCommand, +}; +use std::result::Result; + +use crate::menu::Options; +use crate::*; + +pub fn calc_text_rate(text_speed: u8) -> u64 { + // returns how much time to wait between printing each scrolling character in milliseconds + 1000/((text_speed as u64 + 5u64)*2u64) +} + +// TODO: Implement global tick. For now / testing, just use a timeout +// TODO: Implement proper Border Set processing to avoid unnecessary overhead +pub fn dialog_box(stdout: &mut std::io::Stdout, global_options: &Options, text: &str, speaker: &str) { + + let (_termwidth, termheight) = get_term_size(); + let mut text = text; + let mut speaker = speaker; + let timeout = calc_text_rate(global_options.text_speed()); + + let max_line_length = TERM_WIDTH-4; + let max_lines = 3; + let mut char_idx = 0usize; + let mut in_line_position = 0; + let mut which_line = 0; + + let x_left = 3 as u16; +// let x_right = (termwidth-3) as u16; + let y_top = termheight-max_lines-1; + let y_bottom = termheight-1; + + let mut text_chars = text.chars().collect::>(); + +// let mut top_line: Vec = Vec::new(); +// let mut middle_line: Vec = Vec::new(); +// let mut bottom_line: Vec = Vec::new(); + + let speaker_name_offset = 2+speaker.len(); + + let mut in_msg = true; + let mut msg_start = Instant::now(); + while in_msg { + + // DRAW BORDER HERE + + { + let border_idx = global_options.frame_style_index; + let border_color_idx = global_options.frame_color_index; + } + + if which_line == 0 && speaker.len() > 0 && in_line_position == 0 { + execute!(stdout, MoveTo(x_left, y_top)); + print!("{}",speaker.to_string() + ": "); + in_line_position += speaker_name_offset; + stdout.flush(); + } + + while (msg_start.elapsed().as_millis() as u64) < (timeout as u64 * char_idx as u64) { + std::thread::sleep(Duration::from_millis( (timeout/4).into() )); + } + + execute!(stdout, MoveTo((x_left+in_line_position as u16), y_top+which_line)); + print!("{}", text_chars[char_idx]); + stdout.flush(); + + + // break up text by spaces + 'check_line_break: { + let mut next_word_length = 0; + if char_idx >= text_chars.len() { + break 'check_line_break; + } + if text_chars[char_idx] == ' ' { + for check_char_idx in char_idx+1..text_chars.len() { + if text_chars[check_char_idx] != ' ' { + next_word_length += 1; + } + else { + break; + } + } + } + + in_line_position += 1; + if next_word_length > 0 && in_line_position+next_word_length > max_line_length { + // execute!(stdout, MoveTo(x_left, termheight-max_lines-1+which_line as u16)); + execute!(stdout, MoveTo(x_left, y_top+which_line as u16)); + stdout.flush(); + //char_idx += 1; + in_line_position = 0; + which_line += 1; + break 'check_line_break; + } + + if in_line_position > max_line_length { + // execute!(stdout, MoveTo(x_left, termheight-max_lines-1+which_line as u16)); + execute!(stdout, MoveTo(x_left, y_top+which_line as u16)); + let _ = stdout.flush(); + in_line_position = 0; + which_line += 1; + } + } + + if which_line == max_lines-1 && in_line_position == max_line_length { + + //Sleep instead of interaction (test) + sleep(Duration::from_millis(1300)); + msg_start = Instant::now(); + // + for i in y_top-1..=y_bottom+1 { + execute!(stdout, MoveTo(x_left, i), Clear(ClearType::CurrentLine)); + } + which_line = 0; + in_line_position = 0; + speaker = ""; + text = &text[char_idx..]; + text_chars = text.chars().collect::>(); + char_idx = 0; + + } + + char_idx += 1; + if char_idx >= text.len() { + in_msg = false; + } + } + + +} + + + + + + diff --git a/src/main.rs b/src/main.rs index 30dea13..2f4f803 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,12 +24,20 @@ mod mons; mod moves; mod natures; mod types; +mod menu; +mod interface; +mod saves; +mod area; +mod tile_to_ascii; use crate::extras::*; use crate::mons::*; use crate::moves::{Effect, PokeMove, Target}; use crate::natures::Nature; use crate::types::*; +use crate::interface::{dialog_box}; +use crate::saves::*; +use crate::area::*; use rand::prelude::*; use std::io::Write; @@ -92,13 +100,17 @@ use crossterm::{ }; use std::result::Result; -#[derive(Debug, Clone)] -struct Options { - frame_color: Color, - frame_style: ratatui::symbols::border::Set, - text_speed: u8, +pub fn get_term_size() -> (u16, u16) { + let termsize = terminal::size().unwrap(); + + let width = termsize.0; + let height = termsize.1; + + (width.clone(), height.clone()) } + + #[derive(Debug, Clone)] struct Party<'a> { mons: [RefCell>; 6], @@ -143,9 +155,73 @@ impl<'a> std::ops::IndexMut for Party<'a> { } } +// TODO: move this into items.rs to simplify main file +// mod items; +enum Item { + Pokeball, +} + +// TODO: maybe move this into its own file as well +// mod stance; +enum Stance { + Default, +} + +enum CharacterState { + Running, + Walking, + Standing, + Biking(u8), // which bike + Surfing(u16), // which pokemon? + Holding(Item, Stance), // self-explanatory +} + struct Player<'a> { name: String, party: Party<'a>, + pub area: Area, + pub position: (u16, u16, u16), // x, y, z + pub animation_state: u16, + pub movement_state: CharacterState, +} + +impl<'a> Player<'a> { + pub fn move_left(&mut self) { + if self.position.0 > 0 { + self.position.0 -= 1; + } + } + pub fn move_right(&mut self) { + if self.position.0 < u16::MAX { + self.position.0 += 1; + } + } + pub fn move_up(&mut self) { + if self.position.1 < u16::MAX { + self.position.1 += 1; + } + } + pub fn move_down(&mut self) { + if self.position.1 > 0 { + self.position.1 -= 1; + } + } + pub fn ascend(&mut self) { + if self.position.2 < u16::MAX { + self.position.2 += 1; + } + } + pub fn descend(&mut self) { + if self.position.2 > 0 { + self.position.2 -= 1; + } + } + + pub fn swap_party(&mut self, first: usize, second: usize) { + let swap = self.party[first].clone(); + self.party[first] = self.party[second].clone(); + self.party[second] = swap; + } } #[derive(Clone, Debug)] @@ -154,17 +230,18 @@ struct Monster<'a> { battle_type: [BattleType; 2], iv: [u8; 6], ev: [u8; 6], - stats: [u8; 6], - curstats: [u16; 6], - crit_level: u8, - accuracy: f64, - lvl: u8, - status: Effect<'a>, - moves: &'static [PokeMove], + pub stats: [u8; 6], + pub curstats: [u16; 6], + pub crit_level: u8, + pub accuracy: f64, + pub lvl: u8, + pub status: Effect<'a>, + pub moves: &'static [PokeMove], owned: bool, - affection: i32, + pub affection: i32, nature: Nature, - name: String, + pub name: String, + overworld: Option<(u8, u16, u16, u16, u16)>, // direction, x, y, z, animation state } impl<'a> Monster<'a> { @@ -189,6 +266,7 @@ impl<'a> Monster<'a> { affection: 0i32, nature: Nature::new(), name: Pokemon[dex].name(), + overworld: None }) } @@ -229,7 +307,7 @@ impl<'a> Monster<'a> { * Other Stats = (floor(0.01 x (2 x Base + IV + floor(0.25 x EV)) x Level) + 5) x Nature */ pub fn init_stats(&mut self) { - let mut mon = self.borrow_mut(); + let mon = self.borrow_mut(); mon.accuracy = 100.0f64; for i in 0..mon.stats.len() { let base = mon.stats[i] as f64; @@ -280,406 +358,11 @@ impl<'a> Monster<'a> { } } -pub fn main_menu( - player_data: &mut Player, - audio_subsystem: &AudioSubsystem, - beep_wav: &Cow<'static, Path>, - select_wav: &Cow<'static, Path>, -) -> io::Result<()> { - - // READ OPTIONS FROM FILE - - let entries = vec!["New Game", "Continue", "Options"]; - - let mut in_menu = true; - let mut menu_select = 0; - let mut item_selected = false; - let mut item_count = 0; - - let mut stdout = io::stdout(); - let backend = CrosstermBackend::new(stdout); - let mut terminal = Terminal::new(backend)?; - - let mut options_state = Options { - frame_color: Color::White, - frame_style: BorderType::Plain.to_border_set(), - text_speed: 0, - }; - - //execute!(io::stdout(), EnterAlternateScreen, EnableMouseCapture)?; - - let mut beep_sounds = Vec::new(); - let mut select_sound = extras::generate_sound(&audio_subsystem, &select_wav, 1.5, 0) - .unwrap(); - for _ in 0..2 { - beep_sounds.push( - extras::generate_sound(&audio_subsystem, &beep_wav, 2.0, 0) - .unwrap(), - ); - } - - while in_menu { - if poll(Duration::from_millis(100)).expect("IO error in menu.") { - std::hint::spin_loop(); - - match read().expect("Error reading event.") { - Event::Key(event) => { - if event.kind != KeyEventKind::Release { // Only act on press or repeat - // - let swap = beep_sounds.remove(0); - beep_sounds.push(swap); - - for j in 1..beep_sounds.len() { - //beep_sounds[j].lock().set_volume(0.8); - beep_sounds[j].pause(); - } - let len = beep_sounds.len() - 1; - beep_sounds[len].lock().restart(); - - match event.code { - KeyCode::Up => { - //beep_sounds[0].lock().set_volume(2.0); - beep_sounds[0].resume(); - if menu_select > 0 { - menu_select -= 1; - } else { - menu_select = 2; - } - } - KeyCode::Down => { - //beep_sounds[0].lock().set_volume(2.0); - beep_sounds[0].resume(); - if menu_select < 2 { - menu_select += 1; - } else { - menu_select = 0; - } - } - KeyCode::Enter => { - select_sound.lock().restart(); - select_sound.resume(); - match menu_select { - 0 => unimplemented!(), //New Game - 1 => unimplemented!(), //Continue - 2 => { - options_state = options_menu( - &audio_subsystem, - &mut beep_sounds, - &mut select_sound, - options_state.clone(), - ); //Options - } - _ => panic!("`menu_select` outside of expected parameters\n"), - } - } - KeyCode::End | Char('c') | KeyCode::Esc => in_menu = false, - _ => {}, - } - } - }, - _ => {}, // add some kind of feedback (preferably a short sound effect) - } - } else { - // Render menu based on which item is selected - let menu = List::new( - entries - .clone() - .into_iter() - .map(|item| { - if menu_select == item_count { - item_count += 1; - ListItem::new(">".to_owned() + item) - .style(Style::default().fg(Color::Gray)) - } else { - item_count += 1; - ListItem::new(" ".to_owned() + item) - .style(Style::default().fg(Color::White)) - } - }) - .collect::>(), - ); - terminal.draw(|f| { - let chunks = Layout::default() - .direction(Direction::Horizontal) - .margin(1) - .constraints([Constraint::Percentage(40), Constraint::Percentage(50)].as_ref()) - .split(Rect { - x: 0, - y: 0, - width: f.size().width, - height: (entries.len() + 3 + 2) as u16, // plus 3 to account for margins, then another 2 to add whitespace - }); - - let tabs = menu.block( - Block::default() - .title("MENU (Press 'c'/End to exit)") - .title_bottom("--ENTER key to select--") - .borders(Borders::ALL) - .border_set(options_state.frame_style.clone()) - .style(Style::default().fg(options_state.frame_color.clone())), - ); - - f.render_widget(tabs, chunks[0]); - })?; - } - - item_count = 0; - } - - //execute!(io::stdout(), LeaveAlternateScreen, DisableMouseCapture)?; - - // SAVE OPTIONS TO FILE - - Ok(()) -} - -pub fn options_menu( - audio: &AudioSubsystem, - beep_sounds: &mut Vec>, - select_sound: &mut AudioDevice, - options_state: Options, -) -> Options { - //TODO: Write option settings to file - - execute!(io::stdout(), EnterAlternateScreen).unwrap(); - - let mut options = options_state.clone(); - - let borders = vec![ - BorderType::Plain.to_border_set(), - BorderType::Rounded.to_border_set(), - BorderType::Double.to_border_set(), - BorderType::Thick.to_border_set(), - BorderType::QuadrantInside.to_border_set(), - BorderType::QuadrantOutside.to_border_set(), - CUSTOM_BORDER1.clone(), - CUSTOM_BORDER2.clone(), - ]; - - let colors = vec![ - Color::Black, - Color::DarkGray, - Color::Red, - Color::LightRed, - Color::Green, - Color::LightGreen, - Color::Yellow, - Color::LightYellow, - Color::Blue, - Color::LightBlue, - Color::Magenta, - Color::LightMagenta, - Color::Cyan, - Color::LightCyan, - Color::White, - Color::Gray, - ]; - - let custom_colors = vec![ - Rgb(240, 190, 190), //Pink - ]; - - let entries = vec!["Frame color", "Frame style", "Text speed"]; - - let mut in_menu = true; - let mut confirm = false; - - let mut menu_select = 0; - let mut item_selected = false; - let mut item_count = 0; - - let mut color_index = 14; - let mut border_index = 0; - - let mut stdout = io::stdout(); - let backend = CrosstermBackend::new(stdout); - let mut terminal = Terminal::new(backend).unwrap(); - - execute!(io::stdout(), EnterAlternateScreen).unwrap(); - - while in_menu { - if poll(Duration::from_millis(100)).expect("IO error in menu.") { - std::hint::spin_loop(); - - match read() { - Ok(Event::Key(event)) => { - if event.kind != KeyEventKind::Release { // Only act on press or repeat - // - let swap = beep_sounds.remove(0); - beep_sounds.push(swap); - - for j in 1..beep_sounds.len() { - //beep_sounds[j].lock().set_volume(0.8); - beep_sounds[j].pause(); - } - let len = beep_sounds.len() - 1; - beep_sounds[len].lock().restart(); - - - match event.code { - KeyCode::Up => { - //beep_sounds[0].lock().set_volume(2.0); - beep_sounds[0].resume(); - if menu_select > 0 { - menu_select -= 1; - } else { - menu_select = 2; - } - } - KeyCode::Down => { - //beep_sounds[0].lock().set_volume(2.0); - beep_sounds[0].resume(); - if menu_select < entries.len() - 1 { - menu_select += 1; - } else { - menu_select = 0; - } - } - KeyCode::Left => match menu_select { - 0 => { - if color_index == 0 { - color_index = colors.len() - 1; - } else { - color_index -= 1; - } - } - 1 => { - if border_index == 0 { - border_index = borders.len() - 1; - } else { border_index -= 1; } - } - 2 => { - if options.text_speed > 0 { - options.text_speed -= 1; - } - } - _ => {} - }, - KeyCode::Right => match menu_select { - 0 => { - if color_index == colors.len() - 1 { - color_index = 0; - } else { - color_index += 1; - } - } - 1 => { - if border_index == borders.len() - 1 { - border_index = 0; - } else { border_index += 1; } - } - 2 => { - if options.text_speed < 5 { - options.text_speed += 1; - } - } - _ => {} - }, - KeyCode::Enter => { - select_sound.lock().restart(); - select_sound.resume(); - sleep(Duration::from_millis(500)); - confirm = true; - in_menu = false; - } - KeyCode::End | Char('c') | KeyCode::Esc => { - options = options_state.clone(); - in_menu = false; - } - _ => {} - } - } - } - _ => {} // add some kind of feedback (preferably a short sound effect) - } - } - // Render menu based on which item is selected - let menu = List::new( - entries - .clone() - .into_iter() - .map(|item| { - let state = match item_count { - 0 => format!("{:?}", colors[color_index]), - 1 => format!("{:?}", borders[border_index]), - 2 => format!("{:?}", options.text_speed), - _ => "Error: `item_count` out of bounds.".to_string(), - }; - - if menu_select == item_count { - item_count += 1; - ListItem::new(">".to_owned() + item + " : " + state.as_str()) - .style(Style::default().fg(Color::Gray)) - } else { - item_count += 1; - ListItem::new(" ".to_owned() + item + " : " + state.as_str()) - .style(Style::default().fg(Color::White)) - } - }) - .collect::>(), - ); - terminal - .draw(|f| { - let chunks = Layout::default() - .direction(Direction::Horizontal) - .margin(1) - .constraints([Constraint::Percentage(70), Constraint::Percentage(30)].as_ref()) - .split(Rect { - x: 0, - y: 0, - width: f.size().width, - height: (entries.len() + 3 + 2) as u16, // plus 3 to account for margins, then another 2 to add whitespace - }); - - let tabs = menu.block( - Block::default() - .title("OPTIONS (Press 'c'/End to go back)") - .title_bottom("--ENTER key to confirm--") - .borders(Borders::ALL) - .border_set(borders[border_index]) - .style(Style::default().fg(colors[color_index])), - ); - - f.render_widget(tabs, chunks[0]); - }) - .unwrap(); - - item_count = 0; - } - - execute!(io::stdout(), LeaveAlternateScreen).unwrap(); - - if confirm { - options = Options { - frame_color: colors[color_index], - frame_style: borders[border_index], - text_speed: options.text_speed, - }; - } - options -} - -const CUSTOM_BORDER1: Set = Set { - top_left: r"\", - top_right: r"/", - bottom_left: r"/", - bottom_right: r"\", - vertical_left: r"│", - vertical_right: r"│", - horizontal_top: r"─", - horizontal_bottom: r"─", -}; -const CUSTOM_BORDER2: Set = Set { - top_left: r"o", - top_right: r"o", - bottom_left: r"°", - bottom_right: r"°", - vertical_left: r"│", - vertical_right: r"│", - horizontal_top: r"─", - horizontal_bottom: r"─", -}; +// DIMENSIONS (PROGRAM NEEDS AT LEAST 80x24 characters) +pub const TERM_WIDTH: usize = 80; +pub const TERM_HEIGHT: usize = 24; +pub const RIGHT_POINTING_TRIANGLE: &str = "▶"; // COPYRIGHT MESSAGE const COPYRIGHT_MESSAGE: [&str; 4] = [ @@ -690,6 +373,9 @@ const COPYRIGHT_MESSAGE: [&str; 4] = [ ]; fn main() -> Result<(), io::Error> { + + let mut stdout = io::stdout(); + //INITIALIZE println!("Loading..."); let sdl_context = sdl2::init().expect("Unable to initialize SDL2"); @@ -698,7 +384,22 @@ fn main() -> Result<(), io::Error> { .audio() .expect("Unable to initialize audio subsystem"); println!("Audio subsystem initialized."); + + // load options if present + let mut global_options = saves::read_save().unwrap(); + /* + // TEST + execute!(&stdout, Clear(ClearType::All), MoveTo(1,1)); + dialog_box(&mut stdout, &global_options, "This is a test dialog box, with a looooooooooooooooooooooooooong sentence!! ... And this is a second sentence. Yeaaaaaaaah.", "Yo mama"); + sleep(Duration::from_millis(1000)); + + execute!(&stdout, Clear(ClearType::All), MoveTo(1,1)); + dialog_box(&mut stdout, &global_options, "LONGWORDLONGWORDLONGWORDLONGWORDLONGWORDLONGWORDLONGWORDLONGWORDLONGWORDLONGWORDLONGWORDLONGWORDLONGWORDLONGWORDLONGWORDLONGWORDLONGWORDLONGWORDLONGWORDLONGWORDLONGWORDLONGWORDLONGWORDLONGWORDLONGWORDLONGWORDLONGWORDLONGWORDLONGWORDLONGWORDLONGWORDLONGWORDLONGWORDLONGWORDLONGWORDLONGWORDLONGWORD", "DEV"); + sleep(Duration::from_millis(1000)); + // + */ + let beep_wav = Cow::from(Path::new("shortbeep.wav")); let select_wav = Cow::from(Path::new("select.wav")); @@ -707,8 +408,7 @@ fn main() -> Result<(), io::Error> { // let mut beep_sound = extras::generate_sound(&audio_subsystem, &beep_wav, 0.7, 0) // .expect("Unable to create device.").0; - let mut stdout = io::stdout(); - execute!(stdout, Hide); + execute!(&stdout, Hide)?; // KEEP THIS HERE FOR LEGAL REASONS!!! // ( Copyright message, stylized ) @@ -717,7 +417,7 @@ fn main() -> Result<(), io::Error> { for _ in 0..terminal::size().expect("Error getting terminal size").1 { println!(); } - execute!(stdout, MoveTo(0, 0), Clear(ClearType::All)); + execute!(&stdout, MoveTo(0, 0), Clear(ClearType::All))?; enable_raw_mode()?; @@ -726,13 +426,13 @@ fn main() -> Result<(), io::Error> { let message_wav = Cow::from(Path::new("Rustemon_Intro_Sound.wav")); // load sound ahead of time - let mut message_sound = - extras::generate_sound(&audio_subsystem, &message_wav, 3.5, 0) + let message_sound = + extras::generate_sound(&audio_subsystem, &message_wav, 3.5 * global_options.master_volume, 0) .unwrap(); let timeout: u64 = 17; //60 fps let pause_time = 3000; - let limit = ((5000 - pause_time)/timeout); + let limit = (5000 - pause_time)/timeout; let mut msg_pause: u64 = 0; let start_time = Instant::now(); @@ -740,22 +440,20 @@ fn main() -> Result<(), io::Error> { let mut step = 0; while in_message { - if (start_time.elapsed().as_millis() as u64) >= (timeout * step as u64) + msg_pause { + if (start_time.elapsed().as_millis() as u64) >= (timeout * step) + msg_pause { let mut i = step; + #[allow(clippy::comparison_chain)] if step == (limit/2) { msg_pause += pause_time; message_sound.resume(); if poll(Duration::from_millis(0))? { - execute!(stdout, Clear(ClearType::All), MoveTo(0, 0)); - match read()? { - _ => { - in_message = false; - } // allow interrupt during sound effect - } + execute!(stdout, Clear(ClearType::All), MoveTo(0, 0))?; + in_message = false; + // allow interrupt during sound effect } } else if step > (limit/2) { @@ -765,11 +463,11 @@ fn main() -> Result<(), io::Error> { let shade = (i as f32 * (limit as f32/ 2.0)/ 17.0) as u8; execute!(stdout, SetForegroundColor(CrosstermColor::Rgb { r: shade, g: shade, b: shade }))?; for line_num in 0..4 { - execute!(stdout, MoveTo(0, 0 + line_num))?; + execute!(stdout, MoveTo(0, line_num))?; print!("{}", ©RIGHT_MESSAGE[line_num as usize]); } - stdout.flush(); + stdout.flush()?; if step >= limit { in_message = false; } @@ -777,11 +475,12 @@ fn main() -> Result<(), io::Error> { step += 1; } else { + #[allow(clippy::collapsible_if)] if poll(Duration::from_millis(0))? { - match read()? { - _ => { - in_message = false; // any input skips - } + if let Ok(Event::Key(event)) = read() { + if event.kind == KeyEventKind::Press { + in_message = false; + } // any keypress skips } } } @@ -801,7 +500,7 @@ fn main() -> Result<(), io::Error> { { let mut skip = false; - let mut device = generate_sound(&audio_subsystem, &Cow::from(Path::new("Epic_Theme.wav")), 0.8, 0).unwrap(); + let mut device = generate_sound(&audio_subsystem, &Cow::from(Path::new("Epic_Theme.wav")), 0.8 * global_options.master_volume, 0).unwrap(); device.resume(); let song_length = device.lock().len(); @@ -814,12 +513,12 @@ fn main() -> Result<(), io::Error> { while playing_intro { if intro_start.elapsed().as_millis() as usize >= (animation_speed * intro_state) { - let frame = intro_ptr[intro_state as usize] + let frame = intro_ptr[intro_state] .lines() .collect::>(); - for i in 0..frame.len() { + for (i, cell) in frame.iter().enumerate() { execute!(stdout, MoveTo(0,0 + i as u16))?; - let _ = io::stdout().write(frame[i].as_bytes()); + let _ = io::stdout().write(cell.as_bytes()); stdout.flush()?; } @@ -828,7 +527,7 @@ fn main() -> Result<(), io::Error> { intro_state += 1; if skip { - if device.lock().volume() <= 0.01f32 { + if device.lock().volume() <= 0.015f32 { playing_intro = false; } else { device.lock().fade_out(0.08, 0.0); @@ -837,15 +536,10 @@ fn main() -> Result<(), io::Error> { } if poll(Duration::from_millis(0))? { - match read()? { - Event::Key(event) => 'event: { - if event.kind == KeyEventKind::Release { - break 'event; - } + if let Ok(Event::Key(event)) = read() { + if event.kind == KeyEventKind::Press { skip = true; - }, //Any key skips, but not on release ( not doing a .kind check here catches - //the key release if a key was pressed to skip the intro message ) - _ => {}, + } // any keypress skips } } @@ -855,38 +549,43 @@ fn main() -> Result<(), io::Error> { } // DEV CHECK -- Second evaluation checks if song is done playing - if intro_state >= intro_ptr.len().try_into().unwrap() - && device.lock().pos() >= song_length + if intro_state >= intro_ptr.len() - 1 + || device.lock().pos() >= song_length { playing_intro = false; } } - execute!(&stdout, Clear(ClearType::All)); + let _ = execute!(&stdout, Clear(ClearType::All)); // Intro sequence scope ends here } // MAIN LOGIC - //TODO: Load saved player data if present //Initialize player data let mut player = Player { name: "DEV".to_string(), party: Party::new(), + area: Area::TestZone, + animation_state: 0, + position: (10, 10, 0), + movement_state: CharacterState::Standing, }; player.party[0] = Monster::new(150, 0u8); + + // Menu { - main_menu(&mut player, &audio_subsystem, &beep_wav, &select_wav); - disable_raw_mode(); + global_options = menu::main_menu(&mut player, &audio_subsystem, &beep_wav, &select_wav, global_options).unwrap(); + disable_raw_mode()?; } - disable_raw_mode()?; - execute!(io::stdout(), Show); + disable_raw_mode()?; + execute!(io::stdout(), Show)?; // TESTING //println!(); diff --git a/src/menu/mod.rs b/src/menu/mod.rs new file mode 100644 index 0000000..8437083 --- /dev/null +++ b/src/menu/mod.rs @@ -0,0 +1,712 @@ +use std::io::Write; +use std::process::Command; +use std::sync::mpsc::*; +use std::thread::{self, sleep, JoinHandle}; + +use std::{ + io, + time::{Duration, Instant}, +}; + +#[allow(unused_imports)] +use sdl2::audio::{ + AudioCVT, AudioCallback, AudioDevice, AudioFormatNum, AudioSpec, AudioSpecDesired, AudioSpecWAV, +}; +use sdl2::AudioSubsystem; +use sdl2::mixer::{self, *}; + +use std::borrow::Cow; +use std::path::{Path, PathBuf}; + +#[allow(unused_imports)] +use ratatui::{ + symbols::border::Set, + backend::CrosstermBackend, + layout::{Constraint, Direction, Layout, Rect}, + style::{Color::{self, *}, Style}, + text::Span, + widgets::{Block, Borders, BorderType, List, ListItem, Tabs, Widget}, + Terminal, +}; + +#[allow(unused_imports)] +use crossterm::{ + style::{ + Color::{self as CrosstermColor, *}, + SetForegroundColor, + SetBackgroundColor + }, + cursor::{ + DisableBlinking, EnableBlinking, Hide, MoveDown, MoveTo, MoveUp, RestorePosition, + SavePosition, Show, + }, + event::{ + self, poll, read, DisableMouseCapture, EnableMouseCapture, Event, + KeyCode::{self, *}, + KeyEvent, KeyEventKind, KeyModifiers, KeyEventState, + KeyboardEnhancementFlags, + PushKeyboardEnhancementFlags, + PopKeyboardEnhancementFlags + }, + execute, + terminal::{ + self, + disable_raw_mode, enable_raw_mode, Clear, ClearType, EnterAlternateScreen, + LeaveAlternateScreen, + }, + ExecutableCommand, +}; +use std::result::Result; + +use crate::Player; +use crate::extras; +use crate::Sound; +use crate::saves::*; +use crate::RIGHT_POINTING_TRIANGLE; +use crate::interface::*; + +pub const CUSTOM_BORDERS: [Set; 2] = +[ + Set { + top_left: r"\", + top_right: r"/", + bottom_left: r"/", + bottom_right: r"\", + vertical_left: r"│", + vertical_right: r"│", + horizontal_top: r"─", + horizontal_bottom: r"─", + }, + Set { + top_left: r"o", + top_right: r"o", + bottom_left: r"°", + bottom_right: r"°", + vertical_left: r"│", + vertical_right: r"│", + horizontal_top: r"─", + horizontal_bottom: r"─", + } +]; + +pub const BORDERS: [ratatui::symbols::border::Set; 8] = [ + + BorderType::Plain.to_border_set(), + BorderType::Rounded.to_border_set(), + BorderType::Double.to_border_set(), + BorderType::Thick.to_border_set(), + BorderType::QuadrantInside.to_border_set(), + BorderType::QuadrantOutside.to_border_set(), + CUSTOM_BORDERS[0], + CUSTOM_BORDERS[1], + +]; + +pub const BORDER_NAMES: [&'static str; 8] = [ + "Plain", + "Rounded", + "Double", + "Thick", + "QuadrantInside", + "QuadrantOutside", + "Pokéborder 1", + "Pokéborder 2" +]; + + +pub const CUSTOM_COLORS: [Color; 19] = [ + Color::Black, + Color::DarkGray, + Color::Red, + Color::LightRed, + Color::Green, + Color::LightGreen, + Color::Yellow, + Color::LightYellow, + Color::Blue, + Color::LightBlue, + Color::Magenta, + Color::LightMagenta, + Color::Cyan, + Color::LightCyan, + Color::White, + Color::Gray, + Rgb(240, 176, 206), //Light Pink + Rgb(255, 109, 178), //Pink + Rgb(0, 255, 142), //Sea Green +]; + +pub const COLOR_NAMES: [&'static str; 19] = [ + "Black", + "Dark Gray", + "Red", + "Light Red", + "Green", + "Light Green", + "Yellow", + "Light Yellow", + "Blue", + "Light Blue", + "Magenta", + "Light Magenta", + "Cyan", + "Light Cyan", + "White", + "Gray", + "Light Pink", + "Pink", + "Sea Green" +]; + +pub const BORDER_COLORS: [Color; 19] = [ + CUSTOM_COLORS[0], + CUSTOM_COLORS[1], + CUSTOM_COLORS[2], + CUSTOM_COLORS[3], + CUSTOM_COLORS[4], + CUSTOM_COLORS[5], + CUSTOM_COLORS[6], + CUSTOM_COLORS[7], + CUSTOM_COLORS[8], + CUSTOM_COLORS[9], + CUSTOM_COLORS[10], + CUSTOM_COLORS[11], + CUSTOM_COLORS[12], + CUSTOM_COLORS[13], + CUSTOM_COLORS[14], + CUSTOM_COLORS[15], + CUSTOM_COLORS[16], + CUSTOM_COLORS[17], + CUSTOM_COLORS[18] +]; + +#[derive(Debug, Clone)] +pub struct Slider { + pub length: u16, + pub position: f64, +} + +impl Slider { + pub fn new(length: u16, position: f64) -> Self { + Self { + length, + position, + } + } +} + +impl std::fmt::Display for Slider { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use std::fmt::Write; + let position = self.position.clamp(0.0, 100.0); + + let mut placed_bar = false; + + for i in 0..=self.length { + let portion = 100.0 / self.length as f64; + let j = portion * i as f64; + f.write_char( + if j >= position && !placed_bar { + placed_bar = true; + print!("\x1b[s\x1b[100;1H portion = {}, j = {} \x1b[u", portion, j); + '|' + } else { + '―' + //'=' + } + )?; + } + + Ok(()) + //write!(f, "{}", slider) + + } +} + +#[derive(Debug, Clone)] +pub struct Options { + pub frame_color_index: usize, + pub frame_style_index: usize, + pub text_speed: u8, + pub master_volume: f32, +} + +impl Options { + pub fn default() -> Options { + Options { + frame_color_index: 14, + frame_style_index: 0, + text_speed: 3, + master_volume: 1.0, + } + } + + pub fn frame_color_index(&self) -> usize { + self.frame_color_index + } + pub fn frame_style_index(&self) -> usize { + self.frame_style_index + } + pub fn text_speed(&self) -> u8 { + self.text_speed + } + + pub fn set_frame_color(&mut self, frame_color_index: usize) { + self.frame_color_index = frame_color_index; + } + pub fn set_frame_style(&mut self, frame_style_index: usize) { + self.frame_style_index = frame_style_index; + } + pub fn set_text_speed(&mut self, text_speed: u8) { + self.text_speed = text_speed; + } +} + + +// TODO: Refactor menu functions to use a helper function which generates a menu based on the +// number of items and an optional size parameter + +use crate::extras::*; + +pub fn main_menu( + player_data: &mut Player, + audio_subsystem: &AudioSubsystem, + beep_wav: &Cow<'static, Path>, + select_wav: &Cow<'static, Path>, + current_options: Options, +) -> io::Result { + + let entries = vec!["New Game", "Continue", "Options", "Save Options"]; + + let mut in_menu = true; + let mut menu_select = 0; + let mut item_count = 0; + + let mut stdout = io::stdout(); + let backend = CrosstermBackend::new(io::stdout()); + let mut terminal = Terminal::new(backend)?; + + let mut options_state = current_options; + + //execute!(io::stdout(), EnterAlternateScreen, EnableMouseCapture)?; + + let (select_volume, beep_volume) = (1.5, 2.0); + + let mut beep_sounds = Vec::new(); + let mut select_sound = extras::generate_sound(audio_subsystem, select_wav, select_volume * options_state.master_volume, 0) + .unwrap(); + for _ in 0..2 { + beep_sounds.push( + extras::generate_sound(audio_subsystem, beep_wav, beep_volume * options_state.master_volume, 0) + .unwrap(), + ); + } + + while in_menu { + let input_wait = Instant::now(); + + let mut event_list = vec![Event::Key(KeyEvent::new_with_kind_and_state( + KeyCode::Null, + KeyModifiers::NONE, + KeyEventKind::Release, + KeyEventState::NONE + ))]; + + + while input_wait.elapsed().as_millis() <= 100 { + if poll(Duration::from_millis(1)).unwrap() { + let next_event = read().unwrap(); + #[allow(clippy::single_match)] + match next_event { + Event::Key(event) => { + if event.kind != KeyEventKind::Release && event.code != KeyCode::Null { + event_list.push(Event::Key(event)); + } + }, + _ => {}, + } + } + } + if event_list.len() > 1 { + for _ in 0..event_list.len()-1 { + event_list.remove(0); + } + } + + #[allow(clippy::single_match)] + match event_list[0] { + Event::Key(event) => { + if event.kind != KeyEventKind::Release && event.code != KeyCode::Null { // Only act on press or repeat + + + let swap = beep_sounds.remove(0); + beep_sounds.push(swap); + + for j in 1..beep_sounds.len() { + //beep_sounds[j].lock().set_volume(0.8); + beep_sounds[j].pause(); + } + let len = beep_sounds.len() - 1; + + if event.code != KeyCode::Enter { + beep_sounds[len].lock().restart(); + } + match event.code { + KeyCode::Up => { + //beep_sounds[0].lock().set_volume(2.0); + beep_sounds[0].resume(); + if menu_select > 0 { + menu_select -= 1; + } else { + menu_select = entries.len()-1; + } + } + KeyCode::Down => { + //beep_sounds[0].lock().set_volume(2.0); + beep_sounds[0].resume(); + if menu_select < entries.len()-1 { + menu_select += 1; + } else { + menu_select = 0; + } + } + KeyCode::Enter => { + select_sound.lock().restart(); + select_sound.resume(); + match menu_select { + 0 => { + //enter_game(&mut *player_data); + dialog_box(&mut stdout, &options_state, "You have selected \"New Game\".", "???"); + }, //New Game + 1 => unimplemented!(), //Continue + 2 => { + options_state = options_menu( + &mut beep_sounds, + beep_volume, + &mut select_sound, + select_volume, + options_state.clone(), + ); //Options + for track in beep_sounds.iter_mut() { + track.lock().set_volume(beep_volume * options_state.master_volume); + } + select_sound.lock().set_volume(select_volume * options_state.master_volume); + } + 3 => { + write_save(&options_state)?; + } + _ => panic!("`menu_select` outside of expected parameters\n"), + } + } + KeyCode::End | Char('c') | KeyCode::Esc => in_menu = false, + _ => {}, + } + } + }, + _ => {}, // add some kind of feedback (preferably a short sound effect) + } + // Render menu based on which item is selected + let menu = List::new( + entries + .clone() + .into_iter() + .map(|item| { + if menu_select == item_count { + item_count += 1; + ListItem::new(RIGHT_POINTING_TRIANGLE.to_owned() + item) + .style(Style::default().fg(Color::Gray)) + } else { + item_count += 1; + ListItem::new(" ".to_owned() + item) + .style(Style::default().fg(Color::White)) + } + }) + .collect::>(), + ); + terminal.draw(|f| { + let chunks = Layout::default() + .direction(Direction::Horizontal) + .margin(1) + .constraints([Constraint::Percentage(40), Constraint::Percentage(50)].as_ref()) + .split(Rect { + x: 0, + y: 0, + width: f.size().width, + height: (entries.len() + 3 + 2) as u16, // plus 3 to account for margins, then another 2 to add whitespace + }); + + let tabs = menu.block( + Block::default() + .title("MENU (Press 'c'/End to exit)") + .title_bottom("--ENTER key to select--") + .borders(Borders::ALL) + .border_set(BORDERS[options_state.frame_style_index].clone()) + .style(Style::default().fg(BORDER_COLORS[options_state.frame_color_index].clone())), + ); + + f.render_widget(tabs, chunks[0]); + })?; + + item_count = 0; + } + + //execute!(io::stdout(), LeaveAlternateScreen, DisableMouseCapture)?; + + Ok(options_state) +} + +pub fn options_menu( + beep_sounds: &mut Vec>, + beep_volume: f32, + select_sound: &mut AudioDevice, + select_volume: f32, + options_state: Options, +) -> Options { + + + execute!(io::stdout(), EnterAlternateScreen).unwrap(); + + let mut options = options_state.clone(); + + let entries = vec!["Frame color", "Frame style", "Text speed", "Master Volume"]; + let slider_length = 20; + let mut master_volume_slider = Slider::new(slider_length, options.master_volume as f64 * 100.0); + + let mut in_menu = true; + let mut confirm = false; + + let mut menu_select = 0; + let mut item_count = 0; + + let mut color_index = options.frame_color_index; + let mut border_index = options.frame_style_index; + + let mut stdout = io::stdout(); + let backend = CrosstermBackend::new(stdout); + let mut terminal = Terminal::new(backend).unwrap(); + + execute!(io::stdout(), EnterAlternateScreen).unwrap(); + + while in_menu { + let input_wait = Instant::now(); + + let mut event_list = vec![Event::Key(KeyEvent::new_with_kind_and_state( + KeyCode::Null, + KeyModifiers::NONE, + KeyEventKind::Release, + KeyEventState::NONE + ))]; + + + while input_wait.elapsed().as_millis() <= 100 { + if poll(Duration::from_millis(1)).unwrap() { + let next_event = read().unwrap(); + #[allow(clippy::single_match)] + match next_event { + Event::Key(event) => { + if event.kind != KeyEventKind::Release && event.code != KeyCode::Null { + event_list.push(Event::Key(event)); + } + }, + _ => {}, + } + } + } + if event_list.len() > 1 { + for _ in 0..event_list.len()-1 { + event_list.remove(0); + } + } + + #[allow(clippy::single_match)] + match event_list[0] { + Event::Key(event) => { + if event.kind != KeyEventKind::Release && event.code != KeyCode::Null { // Only act on press or repeat + let swap = beep_sounds.remove(0); + beep_sounds.push(swap); + + for j in 1..beep_sounds.len() { + //beep_sounds[j].lock().set_volume(0.8); + beep_sounds[j].pause(); + } + let len = beep_sounds.len() - 1; + beep_sounds[len].lock().restart(); + + match event.code { + KeyCode::Up => { + //beep_sounds[0].lock().set_volume(2.0); + beep_sounds[0].resume(); + if menu_select > 0 { + menu_select -= 1; + } else { + menu_select = 2; + } + } + KeyCode::Down => { + //beep_sounds[0].lock().set_volume(2.0); + beep_sounds[0].resume(); + if menu_select < entries.len() - 1 { + menu_select += 1; + } else { + menu_select = 0; + } + } + KeyCode::Left => match menu_select { + 0 => { + if color_index == 0 { + color_index = BORDER_COLORS.len() - 1; + } else { + color_index -= 1; + } + } + 1 => { + if border_index == 0 { + border_index = BORDERS.len() - 1; + } else { border_index -= 1; } + } + 2 => { + if options.text_speed > 1 { + options.text_speed -= 1; + } + } + 3 => { + if options.master_volume > 0.0 { + options.master_volume = + if options.master_volume > 1.0 / slider_length as f32 { + options.master_volume - 0.05 + } else { + 0.0 + }; + } + for track in beep_sounds.iter_mut() { + track.lock().set_volume(beep_volume * options.master_volume); + } + select_sound.lock().set_volume(select_volume * options.master_volume); + beep_sounds[0].resume(); + } + _ => {} + }, + KeyCode::Right => match menu_select { + 0 => { + if color_index == BORDER_COLORS.len() - 1 { + color_index = 0; + } else { + color_index += 1; + } + } + 1 => { + if border_index == BORDERS.len() - 1 { + border_index = 0; + } else { border_index += 1; } + } + 2 => { + if options.text_speed < 5 { + options.text_speed += 1; + } + } + 3 => { + if options.master_volume < 1.0 { + options.master_volume = + if 1.0 - options.master_volume < 0.05 { + 1.0 + } else { + options.master_volume + 0.05 + }; + } + for track in beep_sounds.iter_mut() { + track.lock().set_volume(beep_volume * options.master_volume); + } + select_sound.lock().set_volume(select_volume * options.master_volume); + beep_sounds[0].resume(); + + } + + _ => {} + }, + KeyCode::Enter => { + select_sound.lock().restart(); + select_sound.resume(); + sleep(Duration::from_millis(500)); + confirm = true; + in_menu = false; + } + KeyCode::End | Char('c') | KeyCode::Esc => { + options = options_state.clone(); + in_menu = false; + } + _ => {} + } + } + } + _ => {} + } + // Render menu based on which item is selected + master_volume_slider.position = options.master_volume as f64 * 100.0; + let menu = List::new( + entries + .clone() + .into_iter() + .map(|item| { + let state = match item_count { + 0 => COLOR_NAMES[color_index].to_string(), + 1 => BORDER_NAMES[border_index].to_string(), + 2 => format!("{:?}", options.text_speed), + 3 => format!("{} ({}%)", master_volume_slider, (options.master_volume * 100.0).round()), + _ => "Error: `item_count` out of bounds.".to_string(), + }; + + if menu_select == item_count { + item_count += 1; + ListItem::new(RIGHT_POINTING_TRIANGLE.to_owned() + item + " : " + state.as_str()) + .style(Style::default().fg(Color::Gray)) + } else { + item_count += 1; + ListItem::new(" ".to_owned() + item + " : " + state.as_str()) + .style(Style::default().fg(Color::White)) + } + }) + .collect::>(), + ); + terminal + .draw(|f| { + let chunks = Layout::default() + .direction(Direction::Horizontal) + .margin(1) + .constraints([Constraint::Percentage(70), Constraint::Percentage(30)].as_ref()) + .split(Rect { + x: 0, + y: 0, + width: f.size().width, + height: (entries.len() + 3 + 2) as u16, // plus 3 to account for margins, then another 2 to add whitespace + }); + + let tabs = menu.block( + Block::default() + .title("OPTIONS (Press 'c'/End to go back)") + .title_bottom("--ENTER key to confirm--") + .borders(Borders::ALL) + .border_set(BORDERS[border_index]) + .style(Style::default().fg(BORDER_COLORS[color_index])), + ); + + f.render_widget(tabs, chunks[0]); + }) + .unwrap(); + + item_count = 0; + } + + execute!(io::stdout(), LeaveAlternateScreen).unwrap(); + + if confirm { + options = Options { + frame_color_index: color_index, + frame_style_index: border_index, + text_speed: options.text_speed, + master_volume: options.master_volume, + }; + } + options +} + + diff --git a/src/saves.rs b/src/saves.rs new file mode 100644 index 0000000..5a93e55 --- /dev/null +++ b/src/saves.rs @@ -0,0 +1,170 @@ +use std::env::{self, var}; +use std::fs::{self, File}; +use std::path::Path; +use std::io::{self, Read, Write}; +use std::result::Result; +use std::str::FromStr; + +// SHOULD work for any type that has the Display trait, and if not, either change which data is +// stored or implement the trait for said type +macro_rules! generate_save_str { + ( $( $x:expr ),* ) => { + { + let mut save_str = String::new(); + + $( + let cur_str = &(format!("{}", $x) + "\n"); + save_str = save_str + cur_str; + )* + save_str + } + }; +} + +// TODO: Parse save string into relative values + +use crate::menu::{self, Options}; + +pub const SAVE_PREFIX: &str = "Rustemon"; +pub const SAVE_DIR: &str = "saves"; +pub const GLOBAL_OPTIONS_FILE: &str = "global.txt"; + + +pub fn generate_save_path() -> String { + #[cfg(all(tsrget_os = "windows"))] + { + let mut path_string = String::from(env::var("LOCALAPPDATA").unwrap()) + "/" + SAVE_PREFIX; + path_string = path_string + "/" + SAVE_DIR; + + fs::create_dir_all(&Path::new(&path_string.clone())).expect("Could not create saves directory"); + path_string + } + #[cfg(all(target_os = "linux"))] + { + let mut path_string = String::from("/usr/share") + "/" + SAVE_PREFIX + "/" + SAVE_DIR; + + fs::create_dir_all(&Path::new(&path_string.clone())).expect("Could not create saves directory"); + + path_string + + } + #[cfg(target_os = "android")] + { + //TODO: Use textbox message. + let prefix = env::var("HOME").expect("Error: Could not read $HOME. Set it in order to enable saves.") + + "/.local"; + let path_string = prefix + "/" + SAVE_PREFIX + "/" + SAVE_DIR; + fs::create_dir_all(Path::new(&path_string)).expect("Could not create saves directory"); + + path_string + } + +} + +// Needs dialog box +pub fn write_save(global_options: &Options) -> io::Result<&str> { + + let save_path = generate_save_path(); + let globals_path = save_path + "/" + GLOBAL_OPTIONS_FILE; + + let save_str = generate_save_str!( + global_options.frame_color_index(), + global_options.frame_style_index(), + global_options.text_speed(), + global_options.master_volume + ); + + match fs::metadata(&globals_path) { + Ok(_) => { + // TODO: ASK USER FOR CONFIRMATION USING DIALOG BOX + + let mut save_file = File::create(Path::new(&globals_path)).expect("Could not create open file 'saves/global.txt' for writing."); + + save_file.set_len(0).expect("Could not clear 'globals.txt'"); + save_file.write_all(save_str.as_bytes()).expect("Could not write to 'globals.txt'"); + + }, + Err(_) => { + + let mut save_file = File::create(Path::new(&globals_path)).expect("Could not create save file 'saves/global.txt'."); + + save_file.set_len(0).expect("Could not clear 'globals.txt' after creation"); + save_file.write_all(save_str.as_bytes()).expect("Could not write to 'globals.txt' after creation"); + }, + } + + Ok("Save successful.") + +} + +pub fn read_save() -> io::Result { // TODO: Return value should be a tuple containing all + // necessary data to resume game state + + let mut default_opts = Options::default(); + let save_path = generate_save_path(); + let globals_path = save_path + "/" + GLOBAL_OPTIONS_FILE; + + #[allow(unused_variables)] //Rust is stupid and can't see that + //this is to make the next line easier + //to read + if !Path::new(&globals_path).exists() { + return Ok(Options::default()); + } + + //let global_opts = File::open(Path::new(&globals_path)).expect("Could not find save file 'saves/global.txt'."); + let options_string = match fs::read_to_string(&Path::new(&globals_path)) { + Ok(string) => string, + Err(_) => return Ok(Options::default()), + }; + + + + // The values here should be checked so that they are bounded to the expected ranges in order + // to avoid errors + for (line_count, line) in options_string.lines().enumerate() { + match line_count { + 0 => default_opts.set_frame_color( + match usize::from_str(line) { + Ok(num) if num < menu::BORDER_COLORS.len() => num, + _ => default_opts.frame_color_index(), + } + ), + 1 => default_opts.set_frame_style( + match usize::from_str(line) { + Ok(num) if num < menu::BORDERS.len() => num, + _ => default_opts.frame_style_index(), + } + ), + 2 => default_opts.set_text_speed( + match u8::from_str(line) { + Ok(num) => { + if num <= 5 { + num + } else { + 5 + } + }, + _ => default_opts.text_speed(), + } + ), + 3 => default_opts.master_volume = + match f32::from_str(line) { + Ok(num) => { + if num > 1.0 { + 1.0 + } else if num < 0.0 { + 0.0 + } else { + num + } + }, + _ => default_opts.master_volume, + }, + _ => {/* TODO: Make a dialog box that says, "Too many options in options file. Exceeding lines ignored."*/}, + } + + } + + Ok(default_opts) +} + diff --git a/src/tile_to_ascii/mod.rs b/src/tile_to_ascii/mod.rs new file mode 100644 index 0000000..508c88e --- /dev/null +++ b/src/tile_to_ascii/mod.rs @@ -0,0 +1,47 @@ +use image::{Rgba, Rgb, RgbImage}; + +pub struct Tile { + // alpha, r, g, b + data: Vec>, + pub width: u16, + pub height: u16, +} + +impl Tile { + fn get_pixel(&self, x: u16, y: u16) -> Option<[u8; 4]> { + if x > self.width || y > self.height { + return None; + } + + let idx = ( (self.data.len() / self.width as usize) * y as usize ) + x as usize; + + self.data[idx] + } +} + +pub struct CharTile { + pub color: Rgba, + pub char: char +} + +pub trait ArrayToPixel { + fn to_pixel(&self, data: [u8; 4]) -> Rgba; +} +impl ArrayToPixel for [u8; 4] { + fn to_pixel(&self, data: [u8; 4]) -> Rgba { + Rgba(data) + } +} + +pub trait TileToChar { + fn to_char(&self) -> CharTile { + + // TODO + CharTile { color: Rgba([255, 255, 255, 255]), char: '.' } + } +} + +fn to_char(tile: Tile) -> char { + + ' ' +}