diff --git a/Cargo.lock b/Cargo.lock index 3d09eb26..77a65181 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -93,6 +93,8 @@ version = "0.1.0" dependencies = [ "clap", "image", + "log", + "pretty_env_logger", "rayon", ] @@ -269,43 +271,56 @@ dependencies = [ [[package]] name = "async-channel" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d37875bd9915b7d67c2f117ea2c30a0989874d0b2cb694fe25403c85763c0c9e" +checksum = "1ca33f4bc4ed1babef42cad36cc1f51fa88be00420404e5b1e80ab1b18f7678c" dependencies = [ "concurrent-queue", - "event-listener 3.1.0", + "event-listener 4.0.0", "event-listener-strategy", "futures-core", "pin-project-lite", ] +[[package]] +name = "async-compression" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc2d0cfb2a7388d34f590e76686704c494ed7aaceed62ee1ba35cbf363abc2a5" +dependencies = [ + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", +] + [[package]] name = "async-executor" -version = "1.7.2" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc5ea910c42e5ab19012bab31f53cb4d63d54c3a27730f9a833a88efcf4bb52d" +checksum = "17ae5ebefcc48e7452b4987947920dac9450be1110cadf34d1b8c116bdbaf97c" dependencies = [ - "async-lock 3.1.0", + "async-lock 3.2.0", "async-task", "concurrent-queue", "fastrand 2.0.1", - "futures-lite 2.0.1", + "futures-lite 2.1.0", "slab", ] [[package]] name = "async-global-executor" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776" +checksum = "9b4353121d5644cdf2beb5726ab752e79a8db1ebb52031770ec47db31d245526" dependencies = [ - "async-channel 1.9.0", + "async-channel 2.1.1", "async-executor", - "async-io 1.13.0", - "async-lock 2.8.0", + "async-io 2.2.1", + "async-lock 3.2.0", "blocking", - "futures-lite 1.13.0", + "futures-lite 2.1.0", "once_cell", ] @@ -331,22 +346,21 @@ dependencies = [ [[package]] name = "async-io" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41ed9d5715c2d329bf1b4da8d60455b99b187f27ba726df2883799af9af60997" +checksum = "d6d3b15875ba253d1110c740755e246537483f152fa334f91abd7fe84c88b3ff" dependencies = [ - "async-lock 3.1.0", + "async-lock 3.2.0", "cfg-if", "concurrent-queue", "futures-io", - "futures-lite 2.0.1", + "futures-lite 2.1.0", "parking", - "polling 3.3.0", - "rustix 0.38.24", + "polling 3.3.1", + "rustix 0.38.26", "slab", "tracing", - "waker-fn", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -360,11 +374,11 @@ dependencies = [ [[package]] name = "async-lock" -version = "3.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deb2ab2aa8a746e221ab826c73f48bc6ba41be6763f0855cb249eb6d154cf1d7" +checksum = "7125e42787d53db9dd54261812ef17e937c95a51e4d291373b670342fa44310c" dependencies = [ - "event-listener 3.1.0", + "event-listener 4.0.0", "event-listener-strategy", "pin-project-lite", ] @@ -382,7 +396,7 @@ dependencies = [ "cfg-if", "event-listener 3.1.0", "futures-lite 1.13.0", - "rustix 0.38.24", + "rustix 0.38.26", "windows-sys 0.48.0", ] @@ -392,13 +406,13 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" dependencies = [ - "async-io 2.2.0", + "async-io 2.2.1", "async-lock 2.8.0", "atomic-waker", "cfg-if", "futures-core", "futures-io", - "rustix 0.38.24", + "rustix 0.38.26", "signal-hook-registry", "slab", "windows-sys 0.48.0", @@ -560,12 +574,12 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" dependencies = [ - "async-channel 2.1.0", - "async-lock 3.1.0", + "async-channel 2.1.1", + "async-lock 3.2.0", "async-task", "fastrand 2.0.1", "futures-io", - "futures-lite 2.0.1", + "futures-lite 2.1.0", "piper", "tracing", ] @@ -865,9 +879,9 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f057a694a54f12365049b0958a1685bb52d567f5593b355fbf685838e873d400" +checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" dependencies = [ "crossbeam-utils", ] @@ -901,9 +915,9 @@ checksum = "e763eef8846b13b380f37dfecda401770b0ca4e56e95170237bd7c25c7db3582" [[package]] name = "core-foundation" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -911,9 +925,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "core-graphics" @@ -930,9 +944,9 @@ dependencies = [ [[package]] name = "core-graphics-types" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bb142d41022986c1d8ff29103a1411c8a3dfad3552f87a4f8dc50d61d4f4e33" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" dependencies = [ "bitflags 1.3.2", "core-foundation", @@ -1160,9 +1174,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" +checksum = "8eb30d70a07a3b04884d2677f06bec33509dc67ca60d92949e5535352d3191dc" dependencies = [ "powerfmt", "serde", @@ -1311,18 +1325,18 @@ dependencies = [ [[package]] name = "equivalent" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f258a7194e7f7c2a7837a8913aeab7fd8c383457034fa20ce4dd3dcb813e8eb8" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -1371,13 +1385,24 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "event-listener" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "770d968249b5d99410d61f5bf89057f3199a077a04d087092f58e7d10692baae" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + [[package]] name = "event-listener-strategy" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96b852f1345da36d551b9473fa1e2b1eb5c5195585c6c018118bc92a8d91160" +checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" dependencies = [ - "event-listener 3.1.0", + "event-listener 4.0.0", "pin-project-lite", ] @@ -1534,9 +1559,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] @@ -1607,14 +1632,13 @@ dependencies = [ [[package]] name = "futures-lite" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3831c2651acb5177cbd83943f3d9c8912c5ad03c76afcc0e9511ba568ec5ebb" +checksum = "aeee267a1883f7ebef3700f262d2d54de95dfaf38189015a74fdc4e0c7ad8143" dependencies = [ "fastrand 2.0.1", "futures-core", "futures-io", - "memchr", "parking", "pin-project-lite", ] @@ -1695,9 +1719,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "glam" @@ -1793,7 +1817,7 @@ checksum = "cc11df1ace8e7e564511f53af41f3e42ddc95b56fd07b3f4445d2a6048bc682c" dependencies = [ "bitflags 2.4.1", "gpu-descriptor-types", - "hashbrown 0.14.2", + "hashbrown 0.14.3", ] [[package]] @@ -1861,9 +1885,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ "ahash", "allocator-api2", @@ -2191,9 +2215,9 @@ dependencies = [ [[package]] name = "idna" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -2241,7 +2265,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", - "hashbrown 0.14.2", + "hashbrown 0.14.3", ] [[package]] @@ -2280,7 +2304,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" dependencies = [ "hermit-abi", - "rustix 0.38.24", + "rustix 0.38.26", "windows-sys 0.48.0", ] @@ -2344,9 +2368,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.65" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" dependencies = [ "wasm-bindgen", ] @@ -2460,9 +2484,9 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" [[package]] name = "llq" @@ -2532,7 +2556,7 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4a83fb7698b3643a0e34f9ae6f2e8f0178c0fd42f8b59d493aa271ff3a5bf21" dependencies = [ - "hashbrown 0.14.2", + "hashbrown 0.14.3", ] [[package]] @@ -3218,17 +3242,19 @@ dependencies = [ name = "overlay" version = "0.1.4" dependencies = [ + "alphagen", + "bytes", "clap", "coarsetime", "confy", "crossbeam-channel", "directories 5.0.1", + "futures", "image", "log", "log-panics", "log4rs", "macroquad", - "pollster", "reqwest", "serde", "serde_json", @@ -3336,9 +3362,9 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "percent-encoding" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "phf" @@ -3474,24 +3500,18 @@ dependencies = [ [[package]] name = "polling" -version = "3.3.0" +version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e53b6af1f60f36f8c2ac2aad5459d75a5a9b4be1e8cdd40264f315d78193e531" +checksum = "cf63fa624ab313c11656b4cda960bfc46c410187ad493c41f6ba2d8c1e991c9e" dependencies = [ "cfg-if", "concurrent-queue", "pin-project-lite", - "rustix 0.38.24", + "rustix 0.38.26", "tracing", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] -[[package]] -name = "pollster" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" - [[package]] name = "powerfmt" version = "0.2.0" @@ -3504,6 +3524,16 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "pretty_env_logger" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "865724d4dbe39d9f3dd3b52b88d859d66bcb2d6a0acfd5ea68a65fb66d4bdc1c" +dependencies = [ + "env_logger", + "log", +] + [[package]] name = "primal-check" version = "0.3.3" @@ -3549,9 +3579,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.69" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" dependencies = [ "unicode-ident", ] @@ -3731,7 +3761,7 @@ dependencies = [ "embedded-graphics", "enum-derive-2018", "env_logger", - "futures-lite 2.0.1", + "futures-lite 2.1.0", "iced", "iced_core", "iced_futures", @@ -3801,6 +3831,7 @@ version = "0.11.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" dependencies = [ + "async-compression", "base64", "bytes", "encoding_rs", @@ -3825,6 +3856,7 @@ dependencies = [ "system-configuration", "tokio", "tokio-native-tls", + "tokio-util", "tower-service", "url", "wasm-bindgen", @@ -3941,15 +3973,15 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.24" +version = "0.38.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ad981d6c340a49cdc40a1028d9c6084ec7e9fa33fcb839cab656a267071e234" +checksum = "9470c4bf8246c8daf25f9598dca807fb6510347b1e1cfa55749113850c79d88a" dependencies = [ "bitflags 2.4.1", "errno", "libc", - "linux-raw-sys 0.4.11", - "windows-sys 0.48.0", + "linux-raw-sys 0.4.12", + "windows-sys 0.52.0", ] [[package]] @@ -4179,9 +4211,9 @@ dependencies = [ [[package]] name = "slotmap" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342" +checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" dependencies = [ "version_check", ] @@ -4540,7 +4572,7 @@ dependencies = [ "cfg-if", "fastrand 2.0.1", "redox_syscall 0.4.1", - "rustix 0.38.24", + "rustix 0.38.26", "windows-sys 0.48.0", ] @@ -4702,7 +4734,9 @@ dependencies = [ "libc", "mio", "num_cpus", + "parking_lot 0.12.1", "pin-project-lite", + "signal-hook-registry", "socket2 0.5.5", "tokio-macros", "windows-sys 0.48.0", @@ -4963,9 +4997,9 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "url" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", "idna", @@ -5123,9 +5157,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -5133,9 +5167,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" dependencies = [ "bumpalo", "log", @@ -5148,9 +5182,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.38" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afec9963e3d0994cac82455b2b3502b81a7f40f9a0d32181f7528d9f4b43e02" +checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" dependencies = [ "cfg-if", "js-sys", @@ -5160,9 +5194,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5170,9 +5204,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", @@ -5183,9 +5217,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.88" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" [[package]] name = "wasm-timer" @@ -5353,9 +5387,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.65" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5db499c5f66323272151db0e666cd34f78617522fb0c1604d31a27c50c206a85" +checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" dependencies = [ "js-sys", "wasm-bindgen", @@ -5572,6 +5606,15 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + [[package]] name = "windows-targets" version = "0.42.2" @@ -5602,6 +5645,21 @@ dependencies = [ "windows_x86_64_msvc 0.48.5", ] +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -5614,6 +5672,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -5626,6 +5690,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -5638,6 +5708,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -5650,6 +5726,12 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -5662,6 +5744,12 @@ 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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -5674,6 +5762,12 @@ 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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -5686,6 +5780,12 @@ 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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + [[package]] name = "winit" version = "0.28.7" @@ -5838,18 +5938,18 @@ checksum = "dd15f8e0dbb966fd9245e7498c7e9e5055d9e5c8b676b95bd67091cd11a1e697" [[package]] name = "zerocopy" -version = "0.7.26" +version = "0.7.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e97e415490559a91254a2979b4829267a57d2fcd741a98eee8b722fb57289aa0" +checksum = "7d6f15f7ade05d2a4935e34a457b936c23dc70a05cc1d97133dc99e7a3fe0f0e" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.26" +version = "0.7.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd7e48ccf166952882ca8bd778a43502c64f33bf94c12ebe2a7f08e5a0f6689f" +checksum = "dbbad221e3f78500350ecbd7dfa4e63ef945c05f4c61cb7f4d3f84cd0bba649b" dependencies = [ "proc-macro2", "quote", diff --git a/alphagen/Cargo.lock b/alphagen/Cargo.lock new file mode 100644 index 00000000..c1743904 --- /dev/null +++ b/alphagen/Cargo.lock @@ -0,0 +1,951 @@ +# This file is automatically @generated by Cargo. +# 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 = "aho-corasick" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +dependencies = [ + "memchr", +] + +[[package]] +name = "alphagen" +version = "0.1.0" +dependencies = [ + "clap", + "image", + "log", + "pretty_env_logger", + "rayon", +] + +[[package]] +name = "anstream" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is-terminal", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" + +[[package]] +name = "anstyle-parse" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bumpalo" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" + +[[package]] +name = "bytemuck" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93aae7a4192245f70fe75dd9157fc7b4a5bf53e88d30bd4396f7d8f9284d5acc" +dependencies = [ + "clap_builder", + "clap_derive", + "once_cell", +] + +[[package]] +name = "clap_builder" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f423e341edefb78c9caba2d9c7f7687d0e72e89df3ce3394554754393ac3990" +dependencies = [ + "anstream", + "anstyle", + "bitflags", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "191d9573962933b4027f932c600cd252ce27a8ad5979418fe78e43c07996f27b" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "exr" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdd2162b720141a91a054640662d3edce3d50a944a50ffca5313cd951abb35b4" +dependencies = [ + "bit_field", + "flume", + "half", + "lebe", + "miniz_oxide 0.6.2", + "rayon-core", + "smallvec", + "zune-inflate", +] + +[[package]] +name = "fdeflate" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "flate2" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" +dependencies = [ + "crc32fast", + "miniz_oxide 0.7.1", +] + +[[package]] +name = "flume" +version = "0.10.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1657b4441c3403d9f7b3409e47575237dac27b1b5726df654a6ecbf92f0f7577" +dependencies = [ + "futures-core", + "futures-sink", + "nanorand", + "pin-project", + "spin", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-sink" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" + +[[package]] +name = "getrandom" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "gif" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "half" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0" +dependencies = [ + "crunchy", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "image" +version = "0.24.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527909aa81e20ac3a44803521443a765550f09b5130c2c2fa1ea59c2f8f50a3a" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "exr", + "gif", + "jpeg-decoder", + "num-rational", + "num-traits", + "png", + "qoi", + "tiff", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.1", + "libc", + "windows-sys", +] + +[[package]] +name = "is-terminal" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" +dependencies = [ + "hermit-abi 0.3.1", + "io-lifetimes", + "rustix", + "windows-sys", +] + +[[package]] +name = "jpeg-decoder" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" +dependencies = [ + "rayon", +] + +[[package]] +name = "js-sys" +version = "0.3.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f37a4a5928311ac501dee68b3c7613a1037d0edb30c8e5427bd832d55d1b790" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + +[[package]] +name = "libc" +version = "0.2.144" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" + +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "518ef76f2f87365916b142844c16d8fefd85039bc5699050210a7778ee1cd1de" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +dependencies = [ + "autocfg", +] + +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", + "simd-adler32", +] + +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +dependencies = [ + "getrandom", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9670a07f94779e00908f3e686eab508878ebb390ba6e604d3a284c00e8d0487b" + +[[package]] +name = "pin-project" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c95a7476719eab1e366eaf73d0260af3021184f18177925b07f54b30089ceead" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "png" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaeebc51f9e7d2c150d3f3bfeb667f2aa985db5ef1e3d212847bdedb488beeaa" +dependencies = [ + "bitflags", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide 0.7.1", +] + +[[package]] +name = "pretty_env_logger" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "865724d4dbe39d9f3dd3b52b88d859d66bcb2d6a0acfd5ea68a65fb66d4bdc1c" +dependencies = [ + "env_logger", + "log", +] + +[[package]] +name = "proc-macro2" +version = "1.0.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b" +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.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rayon" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + +[[package]] +name = "regex" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81ca098a9821bd52d6b24fd8b10bd081f47d39c22778cafaa75a2857a62c6390" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" + +[[package]] +name = "rustix" +version = "0.37.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d" +dependencies = [ + "bitflags", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "simd-adler32" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f" + +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "tiff" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7449334f9ff2baf290d55d73983a7d6fa15e01198faef72af07e2a8db851e471" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + +[[package]] +name = "unicode-ident" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bba0e8cb82ba49ff4e229459ff22a191bbe9a1cb3a341610c9c33efc27ddf73" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b04bc93f9d6bdee709f6bd2118f57dd6679cf1176a1af464fca3ab0d66d8fb" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14d6b024f1a526bb0234f52840389927257beb670610081360e5a03c5df9c258" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9d5b4305409d1fc9482fee2d7f9bcbf24b3972bf59817ef757e23982242a93" + +[[package]] +name = "weezl" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +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", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[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/alphagen/Cargo.toml b/alphagen/Cargo.toml index b2159d5a..4cc81b85 100644 --- a/alphagen/Cargo.toml +++ b/alphagen/Cargo.toml @@ -2,11 +2,16 @@ name = "alphagen" version = "0.1.0" edition = "2021" -description = "Used to generate greyscale mask images based on the alpha channel of existing images" +description = "Used to generate greyscale mask images based on the alpha channel of an image" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -clap = { version = "4", features = ["derive"] } -image = "*" +clap = { version = "4.3.0", features = ["derive"] } +image = "0.24.6" +log = "0.4.18" +pretty_env_logger = "0.5.0" rayon = "*" + +[lib] +name = "alphagen" diff --git a/alphagen/readme.md b/alphagen/readme.md new file mode 100644 index 00000000..03dfe21e --- /dev/null +++ b/alphagen/readme.md @@ -0,0 +1,21 @@ +# AlphaGen + +AlphaGen is a small Rust application used to generate greyscale mask images based on the alpha channel of an image. It takes input image files and an output directory as command-line arguments and processes the images in parallel to generate greyscale mask images. + +## Usage + +```shell +alphagen input_file(s) output_directory +``` + +### Installation + +To use AlphaGen, you need to have Rust and Cargo installed. If you don't have them installed, you can follow the official Rust installation guide: https://www.rust-lang.org/tools/install + +Once Rust is installed, you can clone the project repository and navigate to the project directory: + +```shell +git clone +cd alphagen +cargo install --path . +``` diff --git a/alphagen/src/lib.rs b/alphagen/src/lib.rs new file mode 100644 index 00000000..a1a44b76 --- /dev/null +++ b/alphagen/src/lib.rs @@ -0,0 +1,51 @@ +use image::io::Reader; +use image::GenericImageView; +use image::ImageFormat; +use rayon::iter::ParallelBridge; +use rayon::iter::ParallelIterator; +use std::fs; +use std::io::BufWriter; +use std::io::Cursor; +use std::path::PathBuf; + +/// Processes all files in `paths` and writes output images to `dir_output` +pub fn on_paths(paths: Vec<&PathBuf>, dir_output: PathBuf) { + paths.iter().par_bridge().for_each(|path| { + let file = image::open(path).unwrap(); + let mut output_image_buff = image::GrayAlphaImage::new(file.width(), file.height()); + let mut pixs = output_image_buff.pixels_mut(); + let mut file_out = fs::File::create(dir_output.join(path.file_name().unwrap())) + .expect("Couldn't create output file"); + for (_, _, alpha_channel) in file.pixels() { + let p = pixs.next().unwrap(); + p.0[0] = alpha_channel[3]; + p.0[1] = alpha_channel[3]; + } + output_image_buff + .write_to(&mut file_out, image::ImageFormat::Png) + .unwrap_or_else(|_| { + panic!( + "Couldn't write to output directory {}/{:?}", + dir_output.display(), + path.file_name().unwrap_or_default() + ) + }); + }); +} + +/// Process raw image data +pub fn on_raw(input: &[u8]) -> Result, Box> { + let img_in = Reader::new(Cursor::new(input)) + .with_guessed_format()? + .decode()?; + let mut img_out = image::GrayAlphaImage::new(img_in.width(), img_in.height()); + let mut pixs = img_out.pixels_mut(); + for (_, _, alpha_channel) in img_in.pixels() { + let p = pixs.next().unwrap(); + p.0[0] = alpha_channel[3]; + p.0[1] = alpha_channel[3]; + } + let mut writer = BufWriter::new(Cursor::new(Vec::new())); + img_out.write_to(&mut writer, ImageFormat::Png)?; + Ok(writer.into_inner()?.into_inner()) +} diff --git a/alphagen/src/main.rs b/alphagen/src/main.rs index f4b27ef3..6a5940a5 100644 --- a/alphagen/src/main.rs +++ b/alphagen/src/main.rs @@ -1,63 +1,43 @@ +use alphagen::on_paths; use clap::Parser; -use image::GenericImageView; +use log::warn; use rayon::iter::ParallelBridge; use rayon::iter::ParallelIterator; -use std::{fs, path::PathBuf}; +use std::path::PathBuf; #[derive(Parser, Debug)] -#[clap(author, version, about, long_about = None)] -struct Cli { - #[clap(long, short, required = true)] - /// Directory within which to find the color images - path: PathBuf, +#[command(author, version, about)] +struct Args { + #[clap(help = "Input files", required = true)] + input_location: Vec, - #[clap(long, short)] - /// Output directory for the alpha images - out: Option, + #[clap(help = "Output directory", required = true)] + output_location: PathBuf, } fn main() { - let args = Cli::parse(); + pretty_env_logger::init(); + let args = Args::parse(); - if let Some(ref out) = args.out { - if out == &args.path { - println!("Error: if the arg is specified, it cannot match the arg"); - return; - } - assert!(out.is_dir()); + let dir_output = args.output_location; + if !dir_output.is_dir() { + std::fs::create_dir_all(&dir_output).expect("Could not create output directory!"); } - assert!(args.path.is_dir()); - - let append_alpha = args.out.is_none(); - let out_dir = args.out.as_ref().unwrap_or(&args.path).to_path_buf(); - - let input_paths = fs::read_dir(args.path).unwrap(); - - input_paths.par_bridge().for_each(|path| { - if let Ok(path) = path { - let file_name: String = path.path().file_stem().unwrap().to_str().unwrap().into(); - let file = image::open(path.path()).unwrap(); - - let mut imgbuf = image::GrayAlphaImage::new(file.width(), file.height()); - let mut pixs = imgbuf.pixels_mut(); - - let mut out_path = out_dir.clone(); - out_path.push(if append_alpha { - format!("{file_name}_alpha.png") + let paths = args + .input_location + .iter() + .par_bridge() + .filter(|p| { + if p.is_file() { + true } else { - format!("{file_name}.png") - }); - let fout = &mut fs::File::create(out_path).unwrap(); - - for pixel in file.pixels() { - let p = pixs.next().unwrap(); - p.0[0] = pixel.2[3]; - p.0[1] = pixel.2[3]; + warn!("{} is not a valid file path. Skipping.", p.display()); + false } + }) + .collect::>(); - imgbuf.write_to(fout, image::ImageFormat::Png).unwrap(); - println!("Completed: {}", path.path().to_str().unwrap()); - } - }); + assert!(paths.is_empty(), "No valid input file paths!"); + on_paths(paths, dir_output); } diff --git a/overlay/Cargo.toml b/overlay/Cargo.toml index a117dab3..6681c87e 100644 --- a/overlay/Cargo.toml +++ b/overlay/Cargo.toml @@ -6,19 +6,21 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -clap = { version = "4", features = ["derive"] } -coarsetime = "*" +clap = { version = "4.3.1", features = ["derive"] } +futures = "0.3" +coarsetime = "0.1.23" confy = "0.5" -crossbeam-channel = "*" -directories = "5" -image = { version = "*", features = ["png", "jpeg"] } -log = "*" +crossbeam-channel = "0.5.8" +directories = "5.0.1" +log = "0.4.18" log-panics = { version = "2", features = ["with-backtrace"]} log4rs = { version = "1", default-features = false, features = ["background_rotation", "compound_policy", "console_appender", "fixed_window_roller", "gzip", "pattern_encoder", "rolling_file_appender", "size_trigger"]} macroquad = { version = "0.3", default-features = false } -pollster = "*" -reqwest = { version = "*", features = ["blocking"] } +reqwest = {version = "0.11", features = ["gzip"]} serde = { version = "1", features = ["derive"] } -serde_json = { version = "*" } -tokio = { version = "*", features = ["macros"] } +serde_json = { version = "1" } +tokio = { version = "1.28", features = ["full"] } uwh-common = { path = "../uwh-common/" } +bytes = "1.4.0" +alphagen = {path="../alphagen"} +image = { version = "0.24", default-features = false, features = ["png", "jpeg_rayon"] } diff --git a/overlay/assets/alpha/1080/Atlantis Logo.png b/overlay/assets/alpha/1080/Atlantis Logo.png index ec8442a3..8ed89d07 100644 Binary files a/overlay/assets/alpha/1080/Atlantis Logo.png and b/overlay/assets/alpha/1080/Atlantis Logo.png differ diff --git a/overlay/assets/alpha/1080/Black Double Line Name Background.png b/overlay/assets/alpha/1080/Black Double Line Name Background.png new file mode 100644 index 00000000..e64811cc Binary files /dev/null and b/overlay/assets/alpha/1080/Black Double Line Name Background.png differ diff --git a/overlay/assets/alpha/1080/Black Frame with Number.png b/overlay/assets/alpha/1080/Black Frame with Number.png new file mode 100644 index 00000000..d8485696 Binary files /dev/null and b/overlay/assets/alpha/1080/Black Frame with Number.png differ diff --git a/overlay/assets/alpha/1080/Black Frame without Number.png b/overlay/assets/alpha/1080/Black Frame without Number.png new file mode 100644 index 00000000..ed4fa2df Binary files /dev/null and b/overlay/assets/alpha/1080/Black Frame without Number.png differ diff --git a/overlay/assets/alpha/1080/Black Picture Background.png b/overlay/assets/alpha/1080/Black Picture Background.png new file mode 100644 index 00000000..da1cdb59 Binary files /dev/null and b/overlay/assets/alpha/1080/Black Picture Background.png differ diff --git a/overlay/assets/alpha/1080/Black Single Line Name Background.png b/overlay/assets/alpha/1080/Black Single Line Name Background.png new file mode 100644 index 00000000..e273d4b9 Binary files /dev/null and b/overlay/assets/alpha/1080/Black Single Line Name Background.png differ diff --git a/overlay/assets/alpha/1080/Black Team Member Role Background.png b/overlay/assets/alpha/1080/Black Team Member Role Background.png new file mode 100644 index 00000000..232121d1 Binary files /dev/null and b/overlay/assets/alpha/1080/Black Team Member Role Background.png differ diff --git a/overlay/assets/alpha/1080/Black Team Name.png b/overlay/assets/alpha/1080/Black Team Name.png new file mode 100644 index 00000000..74ec8605 Binary files /dev/null and b/overlay/assets/alpha/1080/Black Team Name.png differ diff --git a/overlay/assets/alpha/1080/Black Timeout Flag.png b/overlay/assets/alpha/1080/Black Timeout Flag.png index 45a99ff3..4b6c87d4 100644 Binary files a/overlay/assets/alpha/1080/Black Timeout Flag.png and b/overlay/assets/alpha/1080/Black Timeout Flag.png differ diff --git a/overlay/assets/alpha/1080/Black Triple Line Name Background.png b/overlay/assets/alpha/1080/Black Triple Line Name Background.png new file mode 100644 index 00000000..a5b02bf8 Binary files /dev/null and b/overlay/assets/alpha/1080/Black Triple Line Name Background.png differ diff --git a/overlay/assets/alpha/1080/Bottom.png b/overlay/assets/alpha/1080/Bottom.png index cf4b950f..ff90fa09 100644 Binary files a/overlay/assets/alpha/1080/Bottom.png and b/overlay/assets/alpha/1080/Bottom.png differ diff --git a/overlay/assets/alpha/1080/Final Score.png b/overlay/assets/alpha/1080/Final Score.png index acc23e4b..8c612742 100644 Binary files a/overlay/assets/alpha/1080/Final Score.png and b/overlay/assets/alpha/1080/Final Score.png differ diff --git a/overlay/assets/alpha/1080/Frame without Number.png b/overlay/assets/alpha/1080/Frame without Number.png new file mode 100644 index 00000000..dd645684 Binary files /dev/null and b/overlay/assets/alpha/1080/Frame without Number.png differ diff --git a/overlay/assets/alpha/1080/Number Background.png b/overlay/assets/alpha/1080/Number Background.png new file mode 100644 index 00000000..afdbfb42 Binary files /dev/null and b/overlay/assets/alpha/1080/Number Background.png differ diff --git a/overlay/assets/alpha/1080/Penalty Black.png b/overlay/assets/alpha/1080/Penalty Black.png index fe2bd8e9..68fd9b3a 100644 Binary files a/overlay/assets/alpha/1080/Penalty Black.png and b/overlay/assets/alpha/1080/Penalty Black.png differ diff --git a/overlay/assets/alpha/1080/Penalty Shot Flag.png b/overlay/assets/alpha/1080/Penalty Shot Flag.png index e22217ef..517ea2be 100644 Binary files a/overlay/assets/alpha/1080/Penalty Shot Flag.png and b/overlay/assets/alpha/1080/Penalty Shot Flag.png differ diff --git a/overlay/assets/alpha/1080/Penalty White.png b/overlay/assets/alpha/1080/Penalty White.png index d12f4e9e..57dc98ac 100644 Binary files a/overlay/assets/alpha/1080/Penalty White.png and b/overlay/assets/alpha/1080/Penalty White.png differ diff --git a/overlay/assets/alpha/1080/Red Double Line Name Background.png b/overlay/assets/alpha/1080/Red Double Line Name Background.png new file mode 100644 index 00000000..253d92c7 Binary files /dev/null and b/overlay/assets/alpha/1080/Red Double Line Name Background.png differ diff --git a/overlay/assets/alpha/1080/Red Frame with Number.png b/overlay/assets/alpha/1080/Red Frame with Number.png new file mode 100644 index 00000000..4ed0a78e Binary files /dev/null and b/overlay/assets/alpha/1080/Red Frame with Number.png differ diff --git a/overlay/assets/alpha/1080/Red Frame without Number.png b/overlay/assets/alpha/1080/Red Frame without Number.png new file mode 100644 index 00000000..ea038c8b Binary files /dev/null and b/overlay/assets/alpha/1080/Red Frame without Number.png differ diff --git a/overlay/assets/alpha/1080/Red Picture Background.png b/overlay/assets/alpha/1080/Red Picture Background.png new file mode 100644 index 00000000..7d09ba26 Binary files /dev/null and b/overlay/assets/alpha/1080/Red Picture Background.png differ diff --git a/overlay/assets/alpha/1080/Red Single Line Name Background.png b/overlay/assets/alpha/1080/Red Single Line Name Background.png new file mode 100644 index 00000000..3c15c0d0 Binary files /dev/null and b/overlay/assets/alpha/1080/Red Single Line Name Background.png differ diff --git a/overlay/assets/alpha/1080/Red Team Member Role Background.png b/overlay/assets/alpha/1080/Red Team Member Role Background.png new file mode 100644 index 00000000..1f67476a Binary files /dev/null and b/overlay/assets/alpha/1080/Red Team Member Role Background.png differ diff --git a/overlay/assets/alpha/1080/Red Team Name.png b/overlay/assets/alpha/1080/Red Team Name.png new file mode 100644 index 00000000..b496a078 Binary files /dev/null and b/overlay/assets/alpha/1080/Red Team Name.png differ diff --git a/overlay/assets/alpha/1080/Red Triple Line Name Background.png b/overlay/assets/alpha/1080/Red Triple Line Name Background.png new file mode 100644 index 00000000..d73efe2e Binary files /dev/null and b/overlay/assets/alpha/1080/Red Triple Line Name Background.png differ diff --git a/overlay/assets/alpha/1080/Referee Timeout Flag.png b/overlay/assets/alpha/1080/Referee Timeout Flag.png index e22217ef..517ea2be 100644 Binary files a/overlay/assets/alpha/1080/Referee Timeout Flag.png and b/overlay/assets/alpha/1080/Referee Timeout Flag.png differ diff --git a/overlay/assets/alpha/1080/Team Bars.png b/overlay/assets/alpha/1080/Team Bars.png index c406f4c2..d085baf6 100644 Binary files a/overlay/assets/alpha/1080/Team Bars.png and b/overlay/assets/alpha/1080/Team Bars.png differ diff --git a/overlay/assets/alpha/1080/Team Black.png b/overlay/assets/alpha/1080/Team Black.png index d12f4e9e..57dc98ac 100644 Binary files a/overlay/assets/alpha/1080/Team Black.png and b/overlay/assets/alpha/1080/Team Black.png differ diff --git a/overlay/assets/alpha/1080/Team Information.png b/overlay/assets/alpha/1080/Team Information.png index 20bc3097..78e104cd 100644 Binary files a/overlay/assets/alpha/1080/Team Information.png and b/overlay/assets/alpha/1080/Team Information.png differ diff --git a/overlay/assets/alpha/1080/Team White.png b/overlay/assets/alpha/1080/Team White.png index cfac34b4..076e89c9 100644 Binary files a/overlay/assets/alpha/1080/Team White.png and b/overlay/assets/alpha/1080/Team White.png differ diff --git a/overlay/assets/alpha/1080/Time and Game State.png b/overlay/assets/alpha/1080/Time and Game State.png index 360b06f1..0ca49262 100644 Binary files a/overlay/assets/alpha/1080/Time and Game State.png and b/overlay/assets/alpha/1080/Time and Game State.png differ diff --git a/overlay/assets/alpha/1080/White Double Line Name Background.png b/overlay/assets/alpha/1080/White Double Line Name Background.png new file mode 100644 index 00000000..e64811cc Binary files /dev/null and b/overlay/assets/alpha/1080/White Double Line Name Background.png differ diff --git a/overlay/assets/alpha/1080/White Frame with Number.png b/overlay/assets/alpha/1080/White Frame with Number.png new file mode 100644 index 00000000..0941e3d1 Binary files /dev/null and b/overlay/assets/alpha/1080/White Frame with Number.png differ diff --git a/overlay/assets/alpha/1080/White Frame without Number.png b/overlay/assets/alpha/1080/White Frame without Number.png new file mode 100644 index 00000000..5dfdda81 Binary files /dev/null and b/overlay/assets/alpha/1080/White Frame without Number.png differ diff --git a/overlay/assets/alpha/1080/White Picture Background.png b/overlay/assets/alpha/1080/White Picture Background.png new file mode 100644 index 00000000..da1cdb59 Binary files /dev/null and b/overlay/assets/alpha/1080/White Picture Background.png differ diff --git a/overlay/assets/alpha/1080/White Single Line Name Background.png b/overlay/assets/alpha/1080/White Single Line Name Background.png new file mode 100644 index 00000000..08f97c91 Binary files /dev/null and b/overlay/assets/alpha/1080/White Single Line Name Background.png differ diff --git a/overlay/assets/alpha/1080/White Team Member Role Background.png b/overlay/assets/alpha/1080/White Team Member Role Background.png new file mode 100644 index 00000000..232121d1 Binary files /dev/null and b/overlay/assets/alpha/1080/White Team Member Role Background.png differ diff --git a/overlay/assets/alpha/1080/White Team Name.png b/overlay/assets/alpha/1080/White Team Name.png new file mode 100644 index 00000000..74ec8605 Binary files /dev/null and b/overlay/assets/alpha/1080/White Team Name.png differ diff --git a/overlay/assets/alpha/1080/White Timeout Flag.png b/overlay/assets/alpha/1080/White Timeout Flag.png index 1e3857b6..415cd062 100644 Binary files a/overlay/assets/alpha/1080/White Timeout Flag.png and b/overlay/assets/alpha/1080/White Timeout Flag.png differ diff --git a/overlay/assets/alpha/1080/White Triple Line Name Background.png b/overlay/assets/alpha/1080/White Triple Line Name Background.png new file mode 100644 index 00000000..a5b02bf8 Binary files /dev/null and b/overlay/assets/alpha/1080/White Triple Line Name Background.png differ diff --git a/overlay/assets/alpha/1080/mask.png b/overlay/assets/alpha/1080/mask.png deleted file mode 100644 index bcaf60f7..00000000 Binary files a/overlay/assets/alpha/1080/mask.png and /dev/null differ diff --git a/overlay/assets/color/1080/Black Double Line Name Background.png b/overlay/assets/color/1080/Black Double Line Name Background.png new file mode 100644 index 00000000..b54bb076 Binary files /dev/null and b/overlay/assets/color/1080/Black Double Line Name Background.png differ diff --git a/overlay/assets/color/1080/Black Picture Background.png b/overlay/assets/color/1080/Black Picture Background.png new file mode 100644 index 00000000..cc83b437 Binary files /dev/null and b/overlay/assets/color/1080/Black Picture Background.png differ diff --git a/overlay/assets/color/1080/Black Single Line Name Background.png b/overlay/assets/color/1080/Black Single Line Name Background.png new file mode 100644 index 00000000..fd4ee379 Binary files /dev/null and b/overlay/assets/color/1080/Black Single Line Name Background.png differ diff --git a/overlay/assets/color/1080/Black Team Member Role Background.png b/overlay/assets/color/1080/Black Team Member Role Background.png new file mode 100644 index 00000000..20628360 Binary files /dev/null and b/overlay/assets/color/1080/Black Team Member Role Background.png differ diff --git a/overlay/assets/color/1080/Black Team Name.png b/overlay/assets/color/1080/Black Team Name.png new file mode 100644 index 00000000..e41c6698 Binary files /dev/null and b/overlay/assets/color/1080/Black Team Name.png differ diff --git a/overlay/assets/color/1080/Black Triple Line Name Background.png b/overlay/assets/color/1080/Black Triple Line Name Background.png new file mode 100644 index 00000000..8f8849bb Binary files /dev/null and b/overlay/assets/color/1080/Black Triple Line Name Background.png differ diff --git a/overlay/assets/color/1080/Frame without Number.png b/overlay/assets/color/1080/Frame without Number.png new file mode 100644 index 00000000..5e782707 Binary files /dev/null and b/overlay/assets/color/1080/Frame without Number.png differ diff --git a/overlay/assets/color/1080/Number Background.png b/overlay/assets/color/1080/Number Background.png new file mode 100644 index 00000000..a99080d2 Binary files /dev/null and b/overlay/assets/color/1080/Number Background.png differ diff --git a/overlay/assets/color/1080/Red Double Line Name Background.png b/overlay/assets/color/1080/Red Double Line Name Background.png new file mode 100644 index 00000000..3206084c Binary files /dev/null and b/overlay/assets/color/1080/Red Double Line Name Background.png differ diff --git a/overlay/assets/color/1080/Red Picture Background.png b/overlay/assets/color/1080/Red Picture Background.png new file mode 100644 index 00000000..944d3e7b Binary files /dev/null and b/overlay/assets/color/1080/Red Picture Background.png differ diff --git a/overlay/assets/color/1080/Red Single Line Name Background.png b/overlay/assets/color/1080/Red Single Line Name Background.png new file mode 100644 index 00000000..7eea59cf Binary files /dev/null and b/overlay/assets/color/1080/Red Single Line Name Background.png differ diff --git a/overlay/assets/color/1080/Red Team Member Role Background.png b/overlay/assets/color/1080/Red Team Member Role Background.png new file mode 100644 index 00000000..451d1e40 Binary files /dev/null and b/overlay/assets/color/1080/Red Team Member Role Background.png differ diff --git a/overlay/assets/color/1080/Red Team Name.png b/overlay/assets/color/1080/Red Team Name.png new file mode 100644 index 00000000..7ef47939 Binary files /dev/null and b/overlay/assets/color/1080/Red Team Name.png differ diff --git a/overlay/assets/color/1080/Red Triple Line Name Background.png b/overlay/assets/color/1080/Red Triple Line Name Background.png new file mode 100644 index 00000000..025b596c Binary files /dev/null and b/overlay/assets/color/1080/Red Triple Line Name Background.png differ diff --git a/overlay/assets/color/1080/White Double Line Name Background.png b/overlay/assets/color/1080/White Double Line Name Background.png new file mode 100644 index 00000000..351a8845 Binary files /dev/null and b/overlay/assets/color/1080/White Double Line Name Background.png differ diff --git a/overlay/assets/color/1080/White Picture Background.png b/overlay/assets/color/1080/White Picture Background.png new file mode 100644 index 00000000..89ce26d4 Binary files /dev/null and b/overlay/assets/color/1080/White Picture Background.png differ diff --git a/overlay/assets/color/1080/White Single Line Name Background.png b/overlay/assets/color/1080/White Single Line Name Background.png new file mode 100644 index 00000000..f06a27cd Binary files /dev/null and b/overlay/assets/color/1080/White Single Line Name Background.png differ diff --git a/overlay/assets/color/1080/White Team Member Role Background.png b/overlay/assets/color/1080/White Team Member Role Background.png new file mode 100644 index 00000000..fcc19c37 Binary files /dev/null and b/overlay/assets/color/1080/White Team Member Role Background.png differ diff --git a/overlay/assets/color/1080/White Team Name.png b/overlay/assets/color/1080/White Team Name.png new file mode 100644 index 00000000..4cf22004 Binary files /dev/null and b/overlay/assets/color/1080/White Team Name.png differ diff --git a/overlay/assets/color/1080/White Triple Line Name Background.png b/overlay/assets/color/1080/White Triple Line Name Background.png new file mode 100644 index 00000000..130d7a09 Binary files /dev/null and b/overlay/assets/color/1080/White Triple Line Name Background.png differ diff --git a/overlay/assets/color/1080/mask.png b/overlay/assets/color/1080/mask.png deleted file mode 100644 index bcaf60f7..00000000 Binary files a/overlay/assets/color/1080/mask.png and /dev/null differ diff --git a/overlay/src/flag.rs b/overlay/src/flag.rs index 6d302598..7f9db85d 100644 --- a/overlay/src/flag.rs +++ b/overlay/src/flag.rs @@ -3,15 +3,16 @@ //! Flags are discarded automatically after their 5 second show time as long as the draw function is called. use crate::load_images::Texture; -use crate::pages::center_text_offset; use crate::pages::draw_text_both_ex; use crate::pages::draw_texture_both; +use crate::pages::fit_text; use crate::pages::Interpolate; +use crate::pages::Justify; use macroquad::prelude::*; use uwh_common::game_snapshot::Color as UWHColor; use uwh_common::game_snapshot::PenaltyTime; -use crate::load_images::load; +use crate::load_images::asset_load; /// Distance from the top of the screen from where the flags are rendered const BASE_HEIGHT: f32 = 150f32; @@ -19,8 +20,8 @@ const BASE_HEIGHT: f32 = 150f32; /// Vertical space allocated to each flag const FLAG_HEIGHT: f32 = 70f32; -#[derive(PartialEq, Debug, Clone)] -pub enum FlagType { +#[derive(PartialEq, Eq, Debug, Clone)] +pub enum Type { Goal(UWHColor, bool), /// Third enum value is used to keep track of whether the flag was visited in last sync. Unvisited flags need to be deleted. Penalty(UWHColor, PenaltyTime, bool), @@ -38,7 +39,7 @@ struct Textures { pub struct Flag { player_name: String, player_number: u8, - flag_type: FlagType, + flag_type: Type, /// Index of the flag's position starting from the top flag. vertical_position: u32, alpha_animation_counter: f32, @@ -46,8 +47,8 @@ pub struct Flag { } impl Flag { - pub fn new(player_name: String, player_number: u8, flag_type: FlagType) -> Self { - Flag { + pub const fn new(player_name: String, player_number: u8, flag_type: Type) -> Self { + Self { player_name, player_number, flag_type, @@ -58,31 +59,27 @@ impl Flag { } } -pub struct FlagRenderer { +pub struct Renderer { active_flags: Vec, inactive_flags: Vec, textures: Textures, } -impl FlagRenderer { +impl Renderer { pub fn add_flag(&mut self, mut flag: Flag, game_state: &crate::State) { flag.vertical_position = self.active_flags.len() as u32; flag.player_name = match flag.flag_type { - FlagType::Penalty(UWHColor::Black, _, _) | FlagType::Goal(UWHColor::Black, _) => { - game_state - .black - .players - .iter() - .find(|player| player.1 == flag.player_number) - .map(|player| player.0.clone()) - .unwrap_or_default() - } + Type::Penalty(UWHColor::Black, _, _) | Type::Goal(UWHColor::Black, _) => game_state + .black + .get_players() + .find(|player| player.number.unwrap() == flag.player_number) + .map(|player| player.name.clone()) + .unwrap_or_default(), _ => game_state .white - .players - .iter() - .find(|player| player.1 == flag.player_number) - .map(|player| player.0.clone()) + .get_players() + .find(|player| player.number.unwrap() == flag.player_number) + .map(|player| player.name.clone()) .unwrap_or_default(), }; self.active_flags.push(flag); @@ -98,22 +95,10 @@ impl FlagRenderer { active_flags: Vec::new(), inactive_flags: Vec::new(), textures: Textures { - black_goal: Texture { - alpha: load!("../assets/alpha/1080/Team Black.png"), - color: load!("../assets/color/1080/Team Black.png"), - }, - white_goal: Texture { - alpha: load!("../assets/alpha/1080/Team White.png"), - color: load!("../assets/color/1080/Team White.png"), - }, - white_penalty: Texture { - alpha: load!("../assets/alpha/1080/Penalty White.png"), - color: load!("../assets/color/1080/Penalty White.png"), - }, - black_penalty: Texture { - alpha: load!("../assets/alpha/1080/Penalty Black.png"), - color: load!("../assets/color/1080/Penalty Black.png"), - }, + black_goal: asset_load!("Team Black.png"), + white_goal: asset_load!("Team White.png"), + white_penalty: asset_load!("Penalty White.png"), + black_penalty: asset_load!("Penalty Black.png"), font: load_ttf_font_from_bytes(include_bytes!("./../assets/BAHNSCHRIFT.TTF")) .unwrap(), }, @@ -130,11 +115,11 @@ impl FlagRenderer { // mark all goal flags as unvisited for flag in &mut self.active_flags { if let Flag { - flag_type: FlagType::Goal(_, is_visited), + flag_type: Type::Goal(_, is_visited), .. } = flag { - *is_visited = false + *is_visited = false; } } @@ -144,12 +129,12 @@ impl FlagRenderer { .active_flags .iter() .position(|flag| { - matches!(flag.flag_type, FlagType::Goal(color, _) if color == goal.0 ) + matches!(flag.flag_type, Type::Goal(color, _) if color == goal.0 ) && flag.player_number == goal.1 }) .unwrap_or_else(|| { self.add_flag( - Flag::new(String::new(), goal.1, FlagType::Goal(goal.0, true)), + Flag::new(String::new(), goal.1, Type::Goal(goal.0, true)), game_state, ); self.active_flags.len() - 1 @@ -158,7 +143,7 @@ impl FlagRenderer { // get the goal flag in the snapshot and mark it as visited match self.active_flags.get_mut(flag_pos).unwrap() { Flag { - flag_type: FlagType::Goal(_, is_visited), + flag_type: Type::Goal(_, is_visited), .. } => { *is_visited = true; @@ -170,7 +155,7 @@ impl FlagRenderer { self.active_flags .iter() .filter_map(|x| { - if let FlagType::Goal(_, false) = x.flag_type { + if let Type::Goal(_, false) = x.flag_type { Some(x.clone()) } else { None @@ -178,7 +163,7 @@ impl FlagRenderer { }) .for_each(|x| self.inactive_flags.push(x)); self.active_flags - .retain(|x| !matches!(x.flag_type, FlagType::Goal(_, false))); + .retain(|x| !matches!(x.flag_type, Type::Goal(_, false))); } /// Used to synchronise penalty info from snapshot with the local penalty list. @@ -187,12 +172,12 @@ impl FlagRenderer { // mark all penalty flags as unvisited for flag in &mut self.active_flags { if let Flag { - flag_type: FlagType::Penalty(color, _, is_visited), + flag_type: Type::Penalty(color, _, is_visited), .. } = flag { if *color == team_color { - *is_visited = false + *is_visited = false; } } } @@ -206,28 +191,28 @@ impl FlagRenderer { if !matches!(penalty.time, PenaltyTime::Seconds(0)) { // find the penalty in the local list, create a new penalty if it doesn't exist. let flag_pos = self - .active_flags - .iter() - .position(|flag| { - matches!(flag.flag_type, FlagType::Penalty(color, _, _) if color == team_color ) - && flag.player_number == penalty.player_number - }) - .unwrap_or_else(|| { - self.add_flag( - Flag::new( - String::new(), - penalty.player_number, - FlagType::Penalty(team_color, penalty.time, true), - ), - game_state, - ); - self.active_flags.len() - 1 - }); + .active_flags + .iter() + .position(|flag| { + matches!(flag.flag_type, Type::Penalty(color, _, _) if color == team_color ) + && flag.player_number == penalty.player_number + }) + .unwrap_or_else(|| { + self.add_flag( + Flag::new( + String::new(), + penalty.player_number, + Type::Penalty(team_color, penalty.time, true), + ), + game_state, + ); + self.active_flags.len() - 1 + }); // update time on all the penalty flags match self.active_flags.get_mut(flag_pos).unwrap() { Flag { - flag_type: FlagType::Penalty(_, time, is_visited), + flag_type: Type::Penalty(_, time, is_visited), .. } => { *time = penalty.time; @@ -241,13 +226,10 @@ impl FlagRenderer { self.active_flags .iter() .filter_map(|x| { - if let FlagType::Penalty(color, _, false) = x.flag_type { + if let Type::Penalty(color, _, false) = x.flag_type { let mut y = x.clone(); - if matches!( - y.flag_type, - FlagType::Penalty(_, PenaltyTime::Seconds(1), _) - ) { - y.flag_type = FlagType::Penalty(color, PenaltyTime::Seconds(0), false); + if matches!(y.flag_type, Type::Penalty(_, PenaltyTime::Seconds(1), _)) { + y.flag_type = Type::Penalty(color, PenaltyTime::Seconds(0), false); } Some(y) } else { @@ -256,17 +238,17 @@ impl FlagRenderer { }) .for_each(|x| self.inactive_flags.push(x)); self.active_flags - .retain(|x| !matches!(x.flag_type, FlagType::Penalty(_, _, false))); + .retain(|x| !matches!(x.flag_type, Type::Penalty(_, _, false))); // sort the flags based on: TD penalties on top, then time penalties sorted by longest time and lastly, goal callouts self.active_flags.sort_by(|a, b| { if let ( // if both flags are penalty flags Flag { - flag_type: FlagType::Penalty(_, time_a, _), + flag_type: Type::Penalty(_, time_a, _), .. }, Flag { - flag_type: FlagType::Penalty(_, time_b, _), + flag_type: Type::Penalty(_, time_b, _), .. }, ) = (a, b) @@ -291,11 +273,11 @@ impl FlagRenderer { } else if let ( // if both flags are goal flags, keep same ordering Flag { - flag_type: FlagType::Goal(_, _), + flag_type: Type::Goal(_, _), .. }, Flag { - flag_type: FlagType::Goal(_, _), + flag_type: Type::Goal(_, _), .. }, ) = (a, b) @@ -304,7 +286,7 @@ impl FlagRenderer { } else { // if one is a goal flag and the other a penalty flag, put the penalty flag on top if let Flag { - flag_type: FlagType::Goal(_, _), + flag_type: Type::Goal(_, _), .. } = a { @@ -313,7 +295,7 @@ impl FlagRenderer { std::cmp::Ordering::Less } } - }) + }); } /// Responsible for drawing the flags, deleting them, etc. @@ -322,7 +304,7 @@ impl FlagRenderer { if flag.alpha_animation_counter < 1f32 { flag.alpha_animation_counter += 1f32 / 60f32; } - let alpha_offset = (0f32, 255f32).interpolate_linear(flag.alpha_animation_counter); + let alpha_offset = (0f32, 1f32).interpolate_linear(flag.alpha_animation_counter); let movement_offset = if flag.vertical_position == idx as u32 { 0f32 } else { @@ -333,25 +315,24 @@ impl FlagRenderer { flag.movement_animation_counter += 1f32 / 60f32; } ( - (BASE_HEIGHT + flag.vertical_position as f32 * FLAG_HEIGHT) - - (BASE_HEIGHT + idx as f32 * FLAG_HEIGHT), + (flag.vertical_position as f32).mul_add(FLAG_HEIGHT, BASE_HEIGHT) + - (idx as f32).mul_add(FLAG_HEIGHT, BASE_HEIGHT), 0f32, ) .interpolate_linear(flag.movement_animation_counter) }; let color = match flag.flag_type { - FlagType::Goal(color, _) => color, - FlagType::Penalty(color, _, _) => color, + Type::Goal(color, _) | Type::Penalty(color, _, _) => color, }; let tex = match flag.flag_type { - FlagType::Goal(_, _) => { + Type::Goal(_, _) => { if color == UWHColor::White { &self.textures.white_goal } else { &self.textures.black_goal } } - FlagType::Penalty(_, _, _) => { + Type::Penalty(_, _, _) => { if color == UWHColor::White { &self.textures.white_penalty } else { @@ -362,49 +343,67 @@ impl FlagRenderer { draw_texture_both!( tex, 25f32, - BASE_HEIGHT + idx as f32 * FLAG_HEIGHT + movement_offset, - Color::from_rgba(255, 255, 255, alpha_offset as u8) + (idx as f32).mul_add(FLAG_HEIGHT, BASE_HEIGHT) + movement_offset, + Color { + a: alpha_offset, + ..WHITE + } ); draw_text_both_ex!( format!("#{} {}", flag.player_number, flag.player_name).as_str(), 160f32, - BASE_HEIGHT + idx as f32 * FLAG_HEIGHT + movement_offset + 33f32, + (idx as f32).mul_add(FLAG_HEIGHT, BASE_HEIGHT) + movement_offset + 33f32, TextParams { font: self.textures.font, font_size: 30, color: if color == uwh_common::game_snapshot::Color::Black { - Color::from_rgba(255, 255, 255, alpha_offset as u8) + Color { + a: alpha_offset, + ..WHITE + } } else { - Color::from_rgba(0, 0, 0, alpha_offset as u8) + Color { + a: alpha_offset, + ..BLACK + } }, ..Default::default() }, TextParams { font: self.textures.font, font_size: 30, - color: Color::from_rgba(255, 255, 255, alpha_offset as u8), + color: Color { + a: alpha_offset, + ..WHITE + }, ..Default::default() } ); match flag.flag_type { - FlagType::Goal(color, _) => draw_text_ex( + Type::Goal(color, _) => draw_text_ex( "GOAL", 45f32, - BASE_HEIGHT + idx as f32 * FLAG_HEIGHT + movement_offset + 33f32, + (idx as f32).mul_add(FLAG_HEIGHT, BASE_HEIGHT) + movement_offset + 33f32, TextParams { font: self.textures.font, font_size: 30, color: if color == uwh_common::game_snapshot::Color::Black { - Color::from_rgba(255, 255, 255, alpha_offset as u8) + Color { + a: alpha_offset, + ..WHITE + } } else { - Color::from_rgba(0, 0, 0, alpha_offset as u8) + Color { + a: alpha_offset, + ..BLACK + } }, ..Default::default() }, ), - FlagType::Penalty(color, timeout, _) => { - let text = &match timeout { + Type::Penalty(color, timeout, _) => { + let text = match timeout { PenaltyTime::Seconds(s) => { let mins = s / 60; let secs = s % 60; @@ -412,31 +411,38 @@ impl FlagRenderer { format!( "{}:{}", if mins < 10 { - format!("0{}", mins) + format!("0{mins}") } else { - format!("{}", mins) + format!("{mins}") }, if secs < 10 { - format!("0{}", secs) + format!("0{secs}") } else { - format!("{}", secs) + format!("{secs}") } ) } PenaltyTime::TotalDismissal => String::from("TD"), }; - let (x_off, text) = center_text_offset!(47f32, text, 30, self.textures.font); + let (x_off, text) = + fit_text(94f32, &text, 30, self.textures.font, Justify::Center); draw_text_ex( text.as_str(), 35f32 + x_off, - BASE_HEIGHT + idx as f32 * FLAG_HEIGHT + movement_offset + 33f32, + (idx as f32).mul_add(FLAG_HEIGHT, BASE_HEIGHT) + movement_offset + 33f32, TextParams { font: self.textures.font, font_size: 30, color: if color == uwh_common::game_snapshot::Color::Black { - Color::from_rgba(255, 255, 255, alpha_offset as u8) + Color { + a: alpha_offset, + ..WHITE + } } else { - Color::from_rgba(0, 0, 0, alpha_offset as u8) + Color { + a: alpha_offset, + ..BLACK + } }, ..Default::default() }, @@ -444,22 +450,21 @@ impl FlagRenderer { } } } - for flag in self.inactive_flags.iter_mut() { + for flag in &mut self.inactive_flags { let color = match flag.flag_type { - FlagType::Goal(color, _) => color, - FlagType::Penalty(color, _, _) => color, + Type::Goal(color, _) | Type::Penalty(color, _, _) => color, }; flag.alpha_animation_counter -= 1f32 / 60f32; - let alpha_offset = (0f32, 255f32).interpolate_linear(flag.alpha_animation_counter); + let alpha_offset = (0f32, 1f32).interpolate_linear(flag.alpha_animation_counter); let tex = match flag.flag_type { - FlagType::Goal(_, _) => { + Type::Goal(_, _) => { if color == UWHColor::White { &self.textures.white_goal } else { &self.textures.black_goal } } - FlagType::Penalty(_, _, _) => { + Type::Penalty(_, _, _) => { if color == UWHColor::White { &self.textures.white_penalty } else { @@ -470,48 +475,66 @@ impl FlagRenderer { draw_texture_both!( tex, 25f32, - BASE_HEIGHT + flag.vertical_position as f32 * FLAG_HEIGHT, - Color::from_rgba(255, 255, 255, alpha_offset as u8) + (flag.vertical_position as f32).mul_add(FLAG_HEIGHT, BASE_HEIGHT), + Color { + a: alpha_offset, + ..WHITE + } ); draw_text_both_ex!( format!("#{} {}", flag.player_number, flag.player_name).as_str(), 160f32, - BASE_HEIGHT + flag.vertical_position as f32 * FLAG_HEIGHT + 33f32, + (flag.vertical_position as f32).mul_add(FLAG_HEIGHT, BASE_HEIGHT) + 33f32, TextParams { font: self.textures.font, font_size: 30, color: if color == uwh_common::game_snapshot::Color::Black { - Color::from_rgba(255, 255, 255, alpha_offset as u8) + Color { + a: alpha_offset, + ..WHITE + } } else { - Color::from_rgba(0, 0, 0, alpha_offset as u8) + Color { + a: alpha_offset, + ..BLACK + } }, ..Default::default() }, TextParams { font: self.textures.font, font_size: 30, - color: Color::from_rgba(255, 255, 255, alpha_offset as u8), + color: Color { + a: alpha_offset, + ..WHITE + }, ..Default::default() } ); match flag.flag_type { - FlagType::Goal(_, _) => draw_text_ex( + Type::Goal(_, _) => draw_text_ex( "GOAL", 45f32, - BASE_HEIGHT + flag.vertical_position as f32 * FLAG_HEIGHT + 33f32, + (flag.vertical_position as f32).mul_add(FLAG_HEIGHT, BASE_HEIGHT) + 33f32, TextParams { font: self.textures.font, font_size: 30, color: if color == uwh_common::game_snapshot::Color::Black { - Color::from_rgba(255, 255, 255, alpha_offset as u8) + Color { + a: alpha_offset, + ..WHITE + } } else { - Color::from_rgba(0, 0, 0, alpha_offset as u8) + Color { + a: alpha_offset, + ..BLACK + } }, ..Default::default() }, ), - FlagType::Penalty(_, timeout, _) => { - let text = &match timeout { + Type::Penalty(_, timeout, _) => { + let text = match timeout { PenaltyTime::Seconds(s) => { let mins = s / 60; let secs = s % 60; @@ -519,31 +542,38 @@ impl FlagRenderer { format!( "{}:{}", if mins < 10 { - format!("0{}", mins) + format!("0{mins}") } else { - format!("{}", mins) + format!("{mins}") }, if secs < 10 { - format!("0{}", secs) + format!("0{secs}") } else { - format!("{}", secs) + format!("{secs}") } ) } PenaltyTime::TotalDismissal => String::from("TD"), }; - let (x_off, text) = center_text_offset!(47f32, text, 30, self.textures.font); + let (x_off, text) = + fit_text(94f32, &text, 30, self.textures.font, Justify::Center); draw_text_ex( text.as_str(), 35f32 + x_off, - BASE_HEIGHT + flag.vertical_position as f32 * FLAG_HEIGHT + 33f32, + (flag.vertical_position as f32).mul_add(FLAG_HEIGHT, BASE_HEIGHT) + 33f32, TextParams { font: self.textures.font, font_size: 30, color: if color == uwh_common::game_snapshot::Color::Black { - Color::from_rgba(255, 255, 255, alpha_offset as u8) + Color { + a: alpha_offset, + ..WHITE + } } else { - Color::from_rgba(0, 0, 0, alpha_offset as u8) + Color { + a: alpha_offset, + ..BLACK + } }, ..Default::default() }, diff --git a/overlay/src/load_images.rs b/overlay/src/load_images.rs index 4a381b8e..6834a5a3 100644 --- a/overlay/src/load_images.rs +++ b/overlay/src/load_images.rs @@ -2,32 +2,55 @@ use std::{fs::File, io::Read, path::Path}; use macroquad::prelude::*; -macro_rules! load { +macro_rules! asset_load { ($file:literal) => { - Texture2D::from_file_with_format(include_bytes!($file), None) + Texture { + color: Texture2D::from_file_with_format( + include_bytes!(concat!("../assets/color/1080/", $file)), + None, + ), + alpha: Texture2D::from_file_with_format( + include_bytes!(concat!("../assets/alpha/1080/", $file)), + None, + ), + } }; } -pub(crate) use load; +pub(crate) use asset_load; +#[derive(Clone)] pub struct Texture { pub alpha: Texture2D, pub color: Texture2D, } +pub struct RpdTextures { + pub team_name_bg: Texture, + pub single_line_name_bg: Texture, + pub double_line_name_bg: Texture, + pub triple_line_name_bg: Texture, + pub team_member_role_bg: Texture, + pub frame_bg: Texture, +} + pub struct Textures { - pub atlantis_logo_graphic: Texture, - pub bottom_graphic: Texture, - pub team_information_graphic: Texture, - pub team_black_graphic: Texture, - pub team_white_graphic: Texture, - pub team_bar_graphic: Texture, - pub time_and_game_state_graphic: Texture, - pub final_score_graphic: Texture, - pub in_game_mask: Texture, - pub penalty_graphic: Texture, - pub white_timout_graphic: Texture, - pub black_timout_graphic: Texture, - pub referee_timout_graphic: Texture, + pub atlantis_logo: Texture, + pub bottom: Texture, + pub team_information: Texture, + pub team_black_banner: Texture, + pub team_white_banner: Texture, + pub team_bar: Texture, + pub time_and_game_state: Texture, + pub final_score: Texture, + pub penalty: Texture, + pub white_timout: Texture, + pub black_timout: Texture, + pub referee_timout: Texture, + pub black_rpd: RpdTextures, + pub white_rpd: RpdTextures, + pub red_rpd: RpdTextures, + pub frame_rpd: Texture, + pub number_bg_rpd: Texture, pub tournament_logo: Option, pub font: Font, } @@ -36,59 +59,45 @@ impl Default for Textures { fn default() -> Self { Self { font: load_ttf_font_from_bytes(include_bytes!("./../assets/BAHNSCHRIFT.TTF")).unwrap(), - final_score_graphic: Texture { - color: load!("../assets/color/1080/Final Score.png"), - alpha: load!("../assets/alpha/1080/Final Score.png"), - }, - time_and_game_state_graphic: Texture { - color: load!("../assets/color/1080/Time and Game State.png"), - alpha: load!("../assets/alpha/1080/Time and Game State.png"), - }, - team_bar_graphic: Texture { - color: load!("../assets/color/1080/Team Bars.png"), - alpha: load!("../assets/alpha/1080/Team Bars.png"), - }, - team_black_graphic: Texture { - color: load!("../assets/color/1080/Team Black.png"), - alpha: load!("../assets/alpha/1080/Team Black.png"), - }, - team_white_graphic: Texture { - color: load!("../assets/color/1080/Team White.png"), - alpha: load!("../assets/alpha/1080/Team White.png"), + final_score: asset_load!("Final Score.png"), + time_and_game_state: asset_load!("Time and Game State.png"), + team_bar: asset_load!("Team Bars.png"), + team_black_banner: asset_load!("Team Black.png"), + team_white_banner: asset_load!("Team White.png"), + team_information: asset_load!("Team Information.png"), + bottom: asset_load!("Bottom.png"), + atlantis_logo: asset_load!("Atlantis Logo.png"), + penalty: asset_load!("Penalty Shot Flag.png"), + white_timout: asset_load!("White Timeout Flag.png"), + black_timout: asset_load!("Black Timeout Flag.png"), + referee_timout: asset_load!("Referee Timeout Flag.png"), + number_bg_rpd: asset_load!("Number Background.png"), + frame_rpd: asset_load!("Frame without Number.png"), + black_rpd: RpdTextures { + team_name_bg: asset_load!("Black Team Name.png"), + single_line_name_bg: asset_load!("Black Single Line Name Background.png"), + double_line_name_bg: asset_load!("Black Double Line Name Background.png"), + triple_line_name_bg: asset_load!("Black Triple Line Name Background.png"), + team_member_role_bg: asset_load!("Black Team Member Role Background.png"), + frame_bg: asset_load!("Black Picture Background.png"), }, - team_information_graphic: Texture { - color: load!("../assets/color/1080/Team Information.png"), - alpha: load!("../assets/alpha/1080/Team Information.png"), + white_rpd: RpdTextures { + team_name_bg: asset_load!("White Team Name.png"), + single_line_name_bg: asset_load!("White Single Line Name Background.png"), + double_line_name_bg: asset_load!("White Double Line Name Background.png"), + triple_line_name_bg: asset_load!("White Triple Line Name Background.png"), + team_member_role_bg: asset_load!("White Team Member Role Background.png"), + frame_bg: asset_load!("White Picture Background.png"), }, - bottom_graphic: Texture { - color: load!("../assets/color/1080/Bottom.png"), - alpha: load!("../assets/alpha/1080/Bottom.png"), - }, - atlantis_logo_graphic: Texture { - color: load!("../assets/color/1080/Atlantis Logo.png"), - alpha: load!("../assets/alpha/1080/Atlantis Logo.png"), - }, - in_game_mask: Texture { - color: load!("../assets/alpha/1080/mask.png"), - alpha: load!("../assets/alpha/1080/mask.png"), - }, - penalty_graphic: Texture { - color: load!("../assets/color/1080/Penalty Shot Flag.png"), - alpha: load!("../assets/alpha/1080/Penalty Shot Flag.png"), - }, - white_timout_graphic: Texture { - color: load!("../assets/color/1080/White Timeout Flag.png"), - alpha: load!("../assets/alpha/1080/White Timeout Flag.png"), - }, - black_timout_graphic: Texture { - color: load!("../assets/color/1080/Black Timeout Flag.png"), - alpha: load!("../assets/alpha/1080/Black Timeout Flag.png"), + red_rpd: RpdTextures { + team_name_bg: asset_load!("Red Team Name.png"), + single_line_name_bg: asset_load!("Red Single Line Name Background.png"), + double_line_name_bg: asset_load!("Red Double Line Name Background.png"), + triple_line_name_bg: asset_load!("Red Triple Line Name Background.png"), + team_member_role_bg: asset_load!("Red Team Member Role Background.png"), + frame_bg: asset_load!("Red Picture Background.png"), }, tournament_logo: None, - referee_timout_graphic: Texture { - color: load!("../assets/color/1080/Referee Timeout Flag.png"), - alpha: load!("../assets/alpha/1080/Referee Timeout Flag.png"), - }, } } } diff --git a/overlay/src/main.rs b/overlay/src/main.rs index 6986985f..ea469634 100644 --- a/overlay/src/main.rs +++ b/overlay/src/main.rs @@ -1,7 +1,7 @@ use clap::Parser; use coarsetime::Instant; use crossbeam_channel::bounded; -use log::{debug, warn, LevelFilter}; +use log::{warn, LevelFilter}; #[cfg(debug_assertions)] use log4rs::append::console::{ConsoleAppender, Target}; use log4rs::{ @@ -15,8 +15,8 @@ use log4rs::{ encode::pattern::PatternEncoder, }; use macroquad::prelude::*; -use network::{StatePacket, TeamInfo}; -use std::str::FromStr; +use network::{GameData, StatePacket, TeamInfoRaw}; +use std::{cmp::Ordering, str::FromStr}; use std::{net::IpAddr, path::PathBuf}; use uwh_common::game_snapshot::{GamePeriod, GameSnapshot, TimeoutSnapshot}; @@ -28,35 +28,23 @@ mod pages; use load_images::{read_image_from_file, Texture}; const APP_NAME: &str = "overlay"; -const TIME_AND_STATE_SHRINK_TO: f32 = -200f32; -const TIME_AND_STATE_SHRINK_FROM: f32 = 0f32; -const ALPHA_MAX: f32 = 255f32; -const ALPHA_MIN: f32 = 0f32; -fn window_conf() -> Conf { - Conf { - window_title: String::from("Overlay Program"), - window_width: 3840, - window_height: 1080, - window_resizable: false, - ..Default::default() - } -} - -#[derive(serde::Serialize, serde::Deserialize)] +#[derive(serde::Serialize, serde::Deserialize, Debug)] pub struct AppConfig { refbox_ip: IpAddr, refbox_port: u64, uwhscores_url: String, + uwhportal_url: String, tournament_logo_path: PathBuf, } impl Default for AppConfig { fn default() -> Self { - AppConfig { + Self { refbox_ip: IpAddr::from_str("127.0.0.1").unwrap(), refbox_port: 8000, - uwhscores_url: String::from("uwhscores.com"), + uwhscores_url: String::from("https://uwhscores.com"), + uwhportal_url: String::from("https://api.uwhscores.prod.zmvp.host"), tournament_logo_path: PathBuf::new(), } } @@ -66,12 +54,116 @@ pub struct State { snapshot: GameSnapshot, black: TeamInfo, white: TeamInfo, + referees: Vec, game_id: u32, pool: String, start_time: String, - white_flag: Option, - black_flag: Option, half_play_duration: Option, + sponsor_logo: Option, +} + +// TODO: Change this to return Result. We're not rn cause from_file_with_format +// panics anyways if image bytes is invalid +pub fn texture_from_bytes(bytes: Vec) -> Texture { + Texture { + color: Texture2D::from_file_with_format(&bytes, None), + alpha: alphagen::on_raw(&bytes) + .map(|bytes| Texture2D::from_file_with_format(&bytes, None)) + .expect("Failed to decode image"), + } +} + +impl State { + fn update_state(&mut self, recieved_state: StatePacket) { + if let Some(GameData { + black, + white, + pool, + start_time, + sponsor_logo, + referees, + .. + }) = recieved_state.data + { + self.black = TeamInfo::from(black); + self.white = TeamInfo::from(white); + self.start_time = start_time; + self.sponsor_logo = sponsor_logo.map(texture_from_bytes); + self.referees = referees.into_iter().map(Member::from).collect(); + self.pool = pool; + } + if let Some(game_id) = recieved_state.game_id { + self.game_id = game_id; + } + self.snapshot = recieved_state.snapshot; + } +} + +/// processed, non serialisable version of `network::MemberRaw` +#[derive(Clone)] +pub struct Member { + name: String, + role: Option, + number: Option, + picture: Option, + geared_picture: Option, +} + +impl From for Member { + fn from(member_raw: network::MemberRaw) -> Self { + Self { + name: member_raw.name, + role: member_raw.role, + number: member_raw.number, + picture: member_raw.picture.map(texture_from_bytes), + geared_picture: member_raw.geared_picture.map(texture_from_bytes), + } + } +} + +/// processed, non serialisable version of `network::TeamInfoRaw` +pub struct TeamInfo { + pub team_name: String, + pub members: Vec, + pub flag: Option, +} + +impl From for TeamInfo { + fn from(mut team_info_raw: TeamInfoRaw) -> Self { + // Players get sorted by number, support members by name. + team_info_raw + .members + .sort_unstable_by(|a, b| match (a.number, b.number) { + (Some(num_a), Some(num_b)) => num_a.cmp(&num_b), + (Some(_), None) => Ordering::Greater, + (None, Some(_)) => Ordering::Less, + (None, None) => a.name.cmp(&b.name), + }); + Self { + team_name: team_info_raw.team_name, + members: team_info_raw + .members + .into_iter() + .map(Member::from) + .collect(), + flag: team_info_raw.flag.map(texture_from_bytes), + } + } +} + +impl TeamInfo { + fn with_name(name: &str) -> Self { + Self { + team_name: name.to_string(), + members: Vec::new(), + flag: None, + } + } + + /// `number` can always be unwrapped on elements returned from here + fn get_players(&self) -> impl Iterator { + self.members.iter().filter(|m| m.number.is_some()) + } } #[derive(Parser, Debug)] @@ -96,83 +188,10 @@ struct Cli { #[macroquad::main(window_conf())] async fn main() { - let args = Cli::parse(); - - let log_level = match args.verbose { - 0 => LevelFilter::Info, - 1 => LevelFilter::Debug, - _ => LevelFilter::Trace, - }; - - let log_base_path = args.log_location.unwrap_or_else(|| { - let mut path = directories::BaseDirs::new() - .expect("Could not find a directory to store logs") - .data_local_dir() - .to_path_buf(); - path.push("uwh-overlay-logs"); - path - }); - let mut log_path = log_base_path.clone(); - let mut archived_log_path = log_base_path.clone(); - log_path.push(format!("{APP_NAME}-log.txt")); - archived_log_path.push(format!("{APP_NAME}-log-{{}}.txt.gz")); - - #[cfg(debug_assertions)] - println!("Log path: {}", log_path.display()); - - // Only log to the console in debug mode - #[cfg(all(debug_assertions, not(target_os = "windows")))] - let console_target = Target::Stderr; - #[cfg(all(debug_assertions, target_os = "windows"))] - let console_target = Target::Stdout; // Windows apps don't get a stderr handle - #[cfg(debug_assertions)] - let console = ConsoleAppender::builder() - .target(console_target) - .encoder(Box::new(PatternEncoder::new("[{d} {h({l:5})} {M}] {m}{n}"))) - .build(); - - // Setup the file log roller - let roller = FixedWindowRoller::builder() - .build( - archived_log_path.as_os_str().to_str().unwrap(), - args.num_old_logs, - ) - .unwrap(); - let file_policy = CompoundPolicy::new( - Box::new(SizeTrigger::new(args.log_max_file_size)), - Box::new(roller), - ); - let file_appender = RollingFileAppender::builder() - .append(true) - .encoder(Box::new(PatternEncoder::new("[{d} {l:5} {M}] {m}{n}"))) - .build(log_path, Box::new(file_policy)) - .unwrap(); - - // Setup the logging from all locations to use `LevelFilter::Error` - let root = Root::builder().appender("file_appender"); - #[cfg(debug_assertions)] - let root = root.appender("console"); - let root = root.build(LevelFilter::Error); - - // Setup the top level logging config - let log_config = LogConfig::builder() - .appender(Appender::builder().build("file_appender", Box::new(file_appender))); - - #[cfg(debug_assertions)] - let log_config = log_config.appender(Appender::builder().build("console", Box::new(console))); - - let log_config = log_config - .logger(Logger::builder().build("overlay", log_level)) // Setup the logging from the refbox app to use `log_level` - .build(root) - .unwrap(); - - log4rs::init_config(log_config).unwrap(); - log_panics::init(); - - let (tx, rx) = bounded::(3); + init_logging(); let config: AppConfig = match confy::load(APP_NAME, None) { - Ok(c) => c, + Ok(config) => config, Err(e) => { warn!("Failed to read config file, overwriting with default. Error: {e}"); let config = AppConfig::default(); @@ -181,6 +200,7 @@ async fn main() { } }; + let (tx, rx) = bounded::(3); let mut tournament_logo_color_path = config.tournament_logo_path.clone(); tournament_logo_color_path.push("color.png"); let mut tournament_logo_alpha_path = config.tournament_logo_path.clone(); @@ -190,12 +210,15 @@ async fn main() { network::networking_thread(tx, config); }); - let mut textures = load_images::Textures::default(); + let mut assets = load_images::Textures::default(); let tournament_logo_color = match read_image_from_file(tournament_logo_color_path.as_path()) { Ok(texture) => Some(texture), Err(e) => { - warn!("Failed to read tournament logo color file: {e}"); + warn!( + "Failed to read tournament logo color file: {} : {e}", + tournament_logo_color_path.display() + ); None } }; @@ -203,13 +226,16 @@ async fn main() { let tournament_logo_alpha = match read_image_from_file(tournament_logo_alpha_path.as_path()) { Ok(texture) => Some(texture), Err(e) => { - warn!("Failed to read tournament logo alpha file: {e}"); + warn!( + "Failed to read tournament logo alpha file: {} : {e}", + tournament_logo_alpha_path.display() + ); None } }; - textures.tournament_logo = tournament_logo_color - .and_then(|color| tournament_logo_alpha.map(|alpha| Texture { color, alpha })); + assets.tournament_logo = tournament_logo_color + .and_then(|color| tournament_logo_alpha.map(|alpha| Texture { alpha, color })); let mut local_state: State = State { snapshot: GameSnapshot { @@ -218,68 +244,36 @@ async fn main() { ..Default::default() }, - black: TeamInfo { - team_name: String::from("BLACK"), - flag: None, - players: Vec::new(), - }, - white: TeamInfo { - team_name: String::from("WHITE"), - flag: None, - players: Vec::new(), - }, + black: TeamInfo::with_name("BLACK"), + referees: Vec::new(), + white: TeamInfo::with_name("WHITE"), game_id: 0, pool: String::new(), start_time: String::new(), - white_flag: None, - black_flag: None, half_play_duration: None, + sponsor_logo: None, }; let mut renderer = pages::PageRenderer { + animation_register0: Instant::now(), animation_register1: Instant::now(), animation_register2: Instant::now(), animation_register3: false, - textures, + assets, last_snapshot_timeout: TimeoutSnapshot::None, }; - let mut flag_renderer = flag::FlagRenderer::new(); + + let mut flag_renderer = flag::Renderer::new(); unsafe { get_internal_gl().quad_context.show_mouse(false); } loop { - assert!(!net_worker.is_finished(), "Error in Networking thread!"); + assert!(!net_worker.is_finished(), "Networking thread panikd!"); clear_background(BLACK); if let Ok(recieved_state) = rx.try_recv() { - if let Some(team) = recieved_state.black { - debug!("Building Black's flag texture"); - local_state.black = team; - if let Some(flag_bytes) = local_state.black.flag.clone() { - local_state.black_flag = - Some(Texture2D::from_file_with_format(&flag_bytes, None)); - } - } - if let Some(team) = recieved_state.white { - debug!("Building White's flag texture"); - local_state.white = team; - if let Some(flag_bytes) = local_state.white.flag.clone() { - local_state.white_flag = - Some(Texture2D::from_file_with_format(&flag_bytes, None)); - } - } - if let Some(game_id) = recieved_state.game_id { - local_state.game_id = game_id; - } - if let Some(pool) = recieved_state.pool { - local_state.pool = pool; - } - if let Some(start_time) = recieved_state.start_time { - local_state.start_time = start_time; - } - local_state.snapshot = recieved_state.snapshot; - + local_state.update_state(recieved_state); // sync local penalty list flag_renderer.synchronize_flags(&local_state); } @@ -288,10 +282,10 @@ async fn main() { GamePeriod::BetweenGames => { flag_renderer.reset(); if let Some(duration) = local_state.snapshot.next_period_len_secs { - local_state.half_play_duration = Some(duration) + local_state.half_play_duration = Some(duration); } match local_state.snapshot.secs_in_period { - 151..=u32::MAX => { + 182..=u32::MAX => { // If an old game just finished, display its scores if local_state.snapshot.is_old_game { renderer.final_scores(&local_state); @@ -299,11 +293,21 @@ async fn main() { renderer.next_game(&local_state); } } - 30..=150 => { - renderer.roster(&local_state); + 30..=181 => { + if local_state.snapshot.is_old_game { + renderer.final_scores(&local_state); + } else { + renderer.roster(&local_state); + } } _ => { - renderer.pre_game_display(&local_state); + if local_state.snapshot.is_old_game + && local_state.snapshot.secs_in_period > 5 + { + renderer.final_scores(&local_state); + } else { + renderer.pre_game_display(&local_state); + } } } } @@ -324,3 +328,88 @@ async fn main() { next_frame().await; } } + +fn init_logging() { + let args = Cli::parse(); + + let log_level = match args.verbose { + 0 => LevelFilter::Info, + 1 => LevelFilter::Debug, + _ => LevelFilter::Trace, + }; + + let log_base_path = args.log_location.unwrap_or_else(|| { + let mut path = directories::BaseDirs::new() + .expect("Could not find a directory to store logs") + .data_local_dir() + .to_path_buf(); + path.push("uwh-overlay-logs"); + path + }); + let mut log_path = log_base_path.clone(); + let mut archived_log_path = log_base_path.clone(); + log_path.push(format!("{APP_NAME}-log.txt")); + archived_log_path.push(format!("{APP_NAME}-log-{{}}.txt.gz")); + + #[cfg(debug_assertions)] + println!("Log path: {}", log_path.display()); + + // Only log to the console in debug mode + #[cfg(all(debug_assertions, not(target_os = "windows")))] + let console_target = Target::Stderr; + #[cfg(all(debug_assertions, target_os = "windows"))] + let console_target = Target::Stdout; // Windows apps don't get a stderr handle + #[cfg(debug_assertions)] + let console = ConsoleAppender::builder() + .target(console_target) + .encoder(Box::new(PatternEncoder::new("[{d} {h({l:5})} {M}] {m}{n}"))) + .build(); + + // Setup the file log roller + let roller = FixedWindowRoller::builder() + .build( + archived_log_path.as_os_str().to_str().unwrap(), + args.num_old_logs, + ) + .unwrap(); + let file_policy = CompoundPolicy::new( + Box::new(SizeTrigger::new(args.log_max_file_size)), + Box::new(roller), + ); + let file_appender = RollingFileAppender::builder() + .append(true) + .encoder(Box::new(PatternEncoder::new("[{d} {l:5} {M}] {m}{n}"))) + .build(log_path, Box::new(file_policy)) + .unwrap(); + + // Setup the logging from all locations to use `LevelFilter::Error` + let root = Root::builder().appender("file_appender"); + #[cfg(debug_assertions)] + let root = root.appender("console"); + let root = root.build(LevelFilter::Error); + + // Setup the top level logging config + let log_config = LogConfig::builder() + .appender(Appender::builder().build("file_appender", Box::new(file_appender))); + + #[cfg(debug_assertions)] + let log_config = log_config.appender(Appender::builder().build("console", Box::new(console))); + + let log_config = log_config + .logger(Logger::builder().build("overlay", log_level)) // Setup the logging from the refbox app to use `log_level` + .build(root) + .unwrap(); + + log4rs::init_config(log_config).unwrap(); + log_panics::init(); +} + +fn window_conf() -> Conf { + Conf { + window_title: String::from("Overlay Program"), + window_width: 3840, + window_height: 1080, + window_resizable: false, + ..Default::default() + } +} diff --git a/overlay/src/network.rs b/overlay/src/network.rs index a6e129cc..2eafade5 100644 --- a/overlay/src/network.rs +++ b/overlay/src/network.rs @@ -1,96 +1,267 @@ -use log::{debug, error, warn}; +use log::{error, info, warn}; +use reqwest::{Client, ClientBuilder}; use serde::{Deserialize, Serialize}; use serde_json::Value; -use std::io::Read; use std::net::TcpStream; +use std::sync::OnceLock; +use std::{io::Read, time::Duration}; use uwh_common::game_snapshot::{Color, GamePeriod, GameSnapshot}; +static CLIENT_CELL: OnceLock = OnceLock::new(); + +async fn get_image_from_opt_url(url: Option<&str>) -> Option> { + let client = CLIENT_CELL.get().unwrap(); + match url { + Some("") => { + warn!("Image URL is empty. Skipping."); + None + } + Some(url) => Some( + client + .get(url) + .send() + .await + .map_err(|e| { + warn!("Couldn't get image \"{url}\" from network: {e}"); + e + }) + .ok()? + .bytes() + .await + .map_err(|e| { + warn!("Couldn't get image \"{url}\" body: {e}"); + e + }) + .ok()? + .to_vec(), + ), + None => None, + } +} + +/// Contains data of each individual in the roster. pictures are raw unprocessed bytes that are +/// serialisable +#[derive(Serialize, Deserialize, Clone)] +pub struct MemberRaw { + pub name: String, + pub role: Option, + pub number: Option, + pub picture: Option>, + pub geared_picture: Option>, +} +/// Contains information about team. `flag` here is a byte array for `Serialize`, which is +/// processed into `Texture2D` when struct is converted into `TeamInfo` #[derive(Serialize, Deserialize, Clone)] -pub struct TeamInfo { +pub struct TeamInfoRaw { pub team_name: String, - pub players: Vec<(String, u8)>, + pub members: Vec, pub flag: Option>, } -impl TeamInfo { - pub async fn new(url: &String, tournament_id: u32, team_id: u64, team_color: Color) -> Self { - debug!( +impl TeamInfoRaw { + pub async fn new( + uwhportal_url: &str, + tournament_id: u32, + team_id: u64, + team_color: Color, + ) -> Self { + let client = CLIENT_CELL.get().unwrap(); + info!( "Requesting UWH API for team information for team {}", team_id ); let data: Value = serde_json::from_str( - &reqwest::get(format!( - "https://{}/api/v1/tournaments/{}/teams/{}", - url, tournament_id, team_id - )) - .await - .unwrap() - .text() - .await - .unwrap(), + &client + .get(format!( + "{uwhportal_url}/api/admin/get-event-team?legacyEventId={tournament_id}&legacyTeamId={team_id}" + )) + .send() + .await + .expect("Coudn't request team data") + .text() + .await + .expect("Coudn't get team data body"), ) .unwrap(); - let players: Vec = data["team"]["roster"] - .as_array() - .map(|x| x.to_vec()) - .unwrap_or_default(); - let mut player_list: Vec<(String, u8)> = Vec::new(); - for player in players { - player_list.push(( - player["name"].as_str().unwrap_or("").to_string(), - player["number"].as_u64().unwrap_or(0) as u8, - )); - } + + info!( + "Got team information for team {}, requesting member details", + team_id + ); + + let (members, flag) = tokio::join!( + futures::future::join_all( + data["roster"] + .as_array() + .map(|x| x.to_vec()) + .unwrap_or_default() + .into_iter() + .map(|member| async move { + let (picture, geared_picture) = tokio::join!( + get_image_from_opt_url(member["photos"]["uniform"].as_str()), + get_image_from_opt_url( + member["photos"][match team_color { + Color::Black => "darkGear", + Color::White => "lightGear", + }] + .as_str(), + ) + ); + MemberRaw { + name: member["rosterName"] + .as_str() + .unwrap_or("Player") + .trim() + .to_string(), + number: member["capNumber"].as_u64().map(|e| e as u8), + role: member["roles"].as_array().and_then(|a| { + a.iter() + .map(|v| v.as_str().unwrap_or("").to_owned()) + .find(|v| *v != "Player") + }), + picture, + geared_picture, + } + }) + ), + get_image_from_opt_url(data["logoUrl"].as_str()) + ); + let members = members.into_iter().collect(); + + info!("Got member details for team {}", team_id); Self { - team_name: data["team"]["name"] - .as_str() - .unwrap_or(match team_color { - Color::Black => "Black", - Color::White => "White", - }) - .to_string(), - players: player_list, - flag: data["team"]["flag_url"] - .as_str() - .map(|s| reqwest::blocking::get(s).unwrap().bytes().unwrap().to_vec()), + team_name: data["name"].as_str().map_or( + match team_color { + Color::Black => String::from("Black"), + Color::White => String::from("White"), + }, + |s| s.trim().to_uppercase(), + ), + members, + flag, } } } -#[derive(Serialize, Deserialize, Clone)] +#[derive(Serialize, Deserialize)] pub struct StatePacket { pub snapshot: GameSnapshot, - pub black: Option, - pub white: Option, pub game_id: Option, - pub pool: Option, - pub start_time: Option, + pub data: Option, +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct GameData { + pub pool: String, + pub start_time: String, + pub referees: Vec, + pub black: TeamInfoRaw, + pub white: TeamInfoRaw, + pub sponsor_logo: Option>, + pub game_id: u32, + pub tournament_id: u32, } async fn fetch_game_data( - tr: crossbeam_channel::Sender<(Value, TeamInfo, TeamInfo)>, - url: String, + tr: crossbeam_channel::Sender<(GameData, bool)>, + uwhscores_url: &str, + uwhportal_url: &str, tournament_id: u32, game_id: u32, + is_current_game: bool, ) { - debug!("Requesting game data from UWH API"); - let data = reqwest::get(format!( - "https://{}/api/v1/tournaments/{}/games/{}", - url, tournament_id, game_id - )) - .await - .unwrap(); - let text = data.text().await.unwrap(); - let data: Value = serde_json::from_str(text.as_str()).unwrap(); - let team_id_black = data["game"]["black_id"].as_u64().unwrap_or(0); - let team_id_white = data["game"]["white_id"].as_u64().unwrap_or(0); - tr.send(( - data, - TeamInfo::new(&url, tournament_id, team_id_black, Color::Black).await, - TeamInfo::new(&url, tournament_id, team_id_white, Color::White).await, - )) - .unwrap(); + let client = CLIENT_CELL.get().unwrap(); + // retry periodically if no connection + loop { + if let Ok(data) = client + .get(format!( + "{uwhscores_url}/api/v1/tournaments/{tournament_id}/games/{game_id}" + )) + .send() + .await + { + info!("Got game data for tid:{tournament_id}, gid:{game_id} from UWH API"); + let text = data + .text() + .await + .expect("Response body could not be recieved!"); + let data: Value = serde_json::from_str(text.as_str()) + .unwrap_or_else(|_| panic!("Server did not return valid json!: {}", text)); + let team_id_black = data["game"]["black_id"].as_u64().unwrap_or(0); + let team_id_white = data["game"]["white_id"].as_u64().unwrap_or(0); + + let pool = data["game"]["pool"] + .as_str() + .map(|s| format!("POOL: {s}")) + .unwrap_or_default(); + let start_time = data["game"]["start_time"] + .as_str() + .map(|s| String::from("START: ") + s.split_at(11).1.split_at(5).0) + .unwrap_or_default(); + let sponsor_logo = get_image_from_opt_url(data["game"]["sponsor_logo"].as_str()).await; + let mut referees = Vec::new(); + futures::future::join_all( + data["game"]["referees"] + .as_array() + .map(|x| x.to_vec()) + .unwrap_or_default() + .iter() + .map(|referee| async { + let (picture, geared_picture) = tokio::join!( + get_image_from_opt_url(referee["picture_url"].as_str()), + get_image_from_opt_url(referee["geared_picture_url"].as_str()) + ); + ( + referee["name"].as_str().map(|s| s.trim().to_uppercase()), + referee["number"].as_u64().map(|e| e as u8), + referee["role"].as_str().map(|s| s.trim().to_uppercase()), + picture, + geared_picture, + ) + }), + ) + .await + .into_iter() + .filter_map(|data| { + if let (Some(name), number, role, picture, geared_picture) = data { + Some(MemberRaw { + name, + role, + number, + picture, + geared_picture, + }) + } else { + None + } + }) + .for_each(|referee| referees.push(referee)); + let (black, white) = tokio::join!( + TeamInfoRaw::new(uwhportal_url, tournament_id, team_id_black, Color::Black,), + TeamInfoRaw::new(uwhportal_url, tournament_id, team_id_white, Color::White,) + ); + info!("Got all data for tid:{tournament_id}, gid:{game_id}. Sending to network thread"); + tr.send(( + GameData { + pool, + start_time, + referees, + black, + white, + sponsor_logo, + tournament_id, + game_id, + }, + is_current_game, + )) + .map_err(|e| error!("Couldn't send data: {e}")) + .unwrap(); + return; + } + warn!("Game data request for tid:{tournament_id}, gid:{game_id} failed. Trying again in 5 seconds."); + tokio::time::sleep(std::time::Duration::from_secs(5)).await; + } } #[tokio::main] @@ -98,90 +269,211 @@ pub async fn networking_thread( tx: crossbeam_channel::Sender, config: crate::AppConfig, ) { - debug!("Attempting refbox connection!"); + CLIENT_CELL + .set( + ClientBuilder::new() + .connect_timeout(Duration::from_secs(20)) + .build() + .expect("Couldn't create HTTP client!"), + ) + .unwrap(); + + info!("Attempting refbox connection!"); let mut stream = loop { if let Ok(stream) = TcpStream::connect((config.refbox_ip, config.refbox_port as u16)) { break stream; } + tokio::time::sleep(std::time::Duration::from_secs(1)).await; }; + info!("Connected to refbox!"); + + #[derive(PartialEq, Eq)] + struct RequestedInfo { + tournament_id: u32, + game_id: u32, + is_current_game: bool, + } + + let mut requested_infos = Vec::new(); - let (tr, rc) = crossbeam_channel::bounded::<(Value, TeamInfo, TeamInfo)>(3); - let url = config.uwhscores_url.clone(); + let (tr, rc) = crossbeam_channel::bounded::<(GameData, bool)>(3); let mut buff = vec![0u8; 1024]; let mut read_bytes; let mut game_id = None; let mut tournament_id = None; - debug!("Networking thread initialized!"); + let mut next_game_data: Option = None; + info!("Networking thread initialized!"); loop { read_bytes = stream.read(&mut buff).unwrap(); if read_bytes == 0 { error!("Connection to refbox lost! Attempting to reconnect!"); stream = loop { + tokio::time::sleep(std::time::Duration::from_secs(1)).await; if let Ok(stream) = TcpStream::connect((config.refbox_ip, config.refbox_port as u16)) { + info!("Found refbox!"); break stream; } }; } if let Ok(snapshot) = serde_json::de::from_slice::(&buff[..read_bytes]) { - let tid = snapshot.tournament_id; - let gid = + let tournament_id_new = snapshot.tournament_id; + let game_id_new = if snapshot.current_period == GamePeriod::BetweenGames && !snapshot.is_old_game { snapshot.next_game_number } else { snapshot.game_number }; - if (tournament_id.is_some() && tournament_id.unwrap() != tid - || game_id.is_some() && game_id.unwrap() != gid) - || tournament_id.is_none() && game_id.is_none() + let next_gid = snapshot.next_game_number; + + let tr_ = tr.clone(); + let uwhscores_url = config.uwhscores_url.clone(); + let uwhportal_url = config.uwhportal_url.clone(); + + // Request new game cache if empty or invalid + if next_game_data.is_none() + || next_game_data.as_ref().unwrap().game_id != next_gid + || next_game_data.as_ref().unwrap().tournament_id != tournament_id_new { - let url = url.clone(); - let tr = tr.clone(); - game_id = Some(gid); - tournament_id = Some(tid); - debug!( - "Fetching game data for tid: {}, gid: {}", - tournament_id.unwrap(), - game_id.unwrap() - ); - tokio::spawn(async move { - fetch_game_data(tr, url, tournament_id.unwrap(), game_id.unwrap()).await - }); + let requested_info = RequestedInfo { + tournament_id: tournament_id_new, + game_id: next_gid, + is_current_game: false, + }; + if !requested_infos.contains(&requested_info) { + info!( + "Fetching game data to cache for tid: {}, gid: {}", + tournament_id_new, next_gid, + ); + requested_infos.push(requested_info); + tokio::spawn(async move { + fetch_game_data( + tr_, + &uwhscores_url, + &uwhportal_url, + tournament_id_new, + next_gid, + false, + ) + .await; + }); + } } - if let Ok((data, black, white)) = rc.try_recv() { - tx.send(StatePacket { - snapshot, - game_id, - black: Some(black), - white: Some(white), - pool: Some( - data["game"]["pool"] - .as_str() - .map(|s| format!("POOL: {}", s)) - .unwrap_or_default(), - ), - start_time: Some( - data["game"]["start_time"] - .as_str() - .map(|s| String::from("START: ") + s.split_at(11).1.split_at(5).0) - .unwrap_or_default(), - ), - }) - .unwrap_or_else(|e| error!("Frontend could not recieve snapshot!: {e}")) + + // initial case when no data is initialised + if game_id.is_none() { + let tr_ = tr.clone(); + let uwhscores_url = config.uwhscores_url.clone(); + let uwhportal_url = config.uwhportal_url.clone(); + game_id = Some(game_id_new); + tournament_id = Some(tournament_id_new); + let requested_info = RequestedInfo { + tournament_id: tournament_id_new, + game_id: game_id_new, + is_current_game: true, + }; + if !requested_infos.contains(&requested_info) { + info!( + "Fetching intial game data for tid: {}, gid: {}", + tournament_id_new, game_id_new + ); + requested_infos.push(requested_info); + tokio::spawn(async move { + fetch_game_data( + tr_, + &uwhscores_url, + &uwhportal_url, + tournament_id_new, + game_id_new, + true, + ) + .await; + }); + } + } + + if let (Some(game_id_old), Some(tournament_id_old)) = + (game_id.as_mut(), tournament_id.as_mut()) + { + let tr_ = tr.clone(); + let uwhscores_url = config.uwhscores_url.clone(); + let uwhportal_url = config.uwhportal_url.clone(); + if *game_id_old != game_id_new || *tournament_id_old != tournament_id_new { + *game_id_old = game_id_new; + *tournament_id_old = tournament_id_new; + info!( + "Got new game ID {} / tournament ID {}", + game_id_new, tournament_id_new + ); + if next_game_data.is_some() + && next_game_data.as_ref().unwrap().game_id == game_id_new + && next_game_data.as_ref().unwrap().tournament_id == tournament_id_new + { + let next_game_data = next_game_data.clone().unwrap(); + info!("Sending cached game data for next game"); + tx.send(StatePacket { + snapshot, + game_id, + data: Some(next_game_data), + }) + .unwrap_or_else(|e| error!("Frontend could not recieve snapshot!: {e}")); + } else { + let requested_info = RequestedInfo { + tournament_id: tournament_id_new, + game_id: game_id_new, + is_current_game: true, + }; + if !requested_infos.contains(&requested_info) { + info!("Fetching game data for tid: {tournament_id_new}, gid: {game_id_new}. Cache is empty or invalid!"); + requested_infos.push(requested_info); + let (uwhscores_url_, uwhportal_url_, tr__) = + (uwhscores_url.clone(), uwhportal_url.clone(), tr_.clone()); + tokio::spawn(async move { + fetch_game_data( + tr__, + &uwhscores_url_, + &uwhportal_url_, + tournament_id_new, + game_id_new, + true, + ) + .await; + }); + } + } + continue; + } + } + if let Ok((game_data, is_current_game)) = rc.try_recv() { + let requested_info = RequestedInfo { + tournament_id: game_data.tournament_id, + game_id: game_data.game_id, + is_current_game, + }; + requested_infos.retain(|info| *info != requested_info); + if is_current_game { + info!("Got game state update from network!"); + tx.send(StatePacket { + snapshot, + game_id, + data: Some(game_data), + }) + .unwrap_or_else(|e| error!("Frontend could not recieve snapshot!: {e}")); + } else { + info!("Got game state update from network for next_game!"); + next_game_data = Some(game_data); + } } else { tx.send(StatePacket { snapshot, game_id, - black: None, - white: None, - pool: None, - start_time: None, + data: None, }) - .unwrap_or_else(|e| error!("Frontend could not recieve snapshot!: {e}")) + .unwrap_or_else(|e| error!("Frontend could not recieve snapshot!: {e}")); } } else { - warn!("Corrupted snapshot discarded!") + warn!("Corrupted snapshot discarded!"); } } } diff --git a/overlay/src/pages/final_scores.rs b/overlay/src/pages/final_scores.rs index 05d2fc66..9446d16f 100644 --- a/overlay/src/pages/final_scores.rs +++ b/overlay/src/pages/final_scores.rs @@ -1,76 +1,71 @@ -use super::center_text_offset; use super::draw_texture_both; +use super::fit_text; use super::PageRenderer; use crate::pages::draw_text_both; use crate::pages::draw_text_both_ex; +use crate::pages::draw_texture_both_ex; +use crate::pages::Justify; use crate::State; +use coarsetime::Instant; use macroquad::prelude::*; impl PageRenderer { /// Display final scores after game is done pub fn final_scores(&mut self, state: &State) { - draw_texture_both!(self.textures.atlantis_logo_graphic, 836f32, 725f32, WHITE); - draw_texture_both!(self.textures.final_score_graphic, 314f32, 347f32, WHITE); + self.animation_register1 = Instant::now(); + draw_texture_both!(self.assets.atlantis_logo, 836f32, 725f32, WHITE); + draw_texture_both!(self.assets.final_score, 314f32, 347f32, WHITE); - if let Some(logo) = self.textures.tournament_logo.as_ref() { + if let Some(logo) = self.assets.tournament_logo.as_ref() { let x = (1920f32 - logo.color.width()) / 2f32; let y = 675f32 - logo.color.height(); draw_texture_both!(logo, x, y, WHITE); } - draw_texture_both!( - self.textures.team_information_graphic, - 130f32, - 710f32, - WHITE - ); - let (x_off, text) = center_text_offset!( - 217f32, - state.black.team_name.to_uppercase().as_str(), + draw_texture_both!(self.assets.team_information, 130f32, 710f32, WHITE); + let (x_off, text) = fit_text( + 434f32, + &state.black.team_name, 45, - self.textures.font + self.assets.font, + Justify::Center, ); draw_text_both!( text.as_str(), 1350f32 + x_off, 805f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 45, ..Default::default() } ); - let (x_off, text) = center_text_offset!( - 220f32, - state.white.team_name.to_uppercase().as_str(), + let (x_off, text) = fit_text( + 440f32, + &state.white.team_name, 45, - self.textures.font + self.assets.font, + Justify::Center, ); draw_text_both_ex!( text.as_str(), 135f32 + x_off, 805f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 45, color: BLACK, ..Default::default() }, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 45, color: WHITE, ..Default::default() } ); - if state.white_flag.is_some() { - draw_rectangle(2500f32, 738f32, 180f32, 100f32, WHITE); - } - if state.black_flag.is_some() { - draw_rectangle(3083f32, 738f32, 180f32, 100f32, WHITE); - } - if let Some(flag) = state.white_flag { - draw_texture_ex( + if let Some(flag) = &state.white.flag { + draw_texture_both_ex!( flag, 580f32, 738f32, @@ -78,11 +73,11 @@ impl PageRenderer { DrawTextureParams { dest_size: Some(vec2(180f32, 100f32)), ..Default::default() - }, + } ); } - if let Some(flag) = state.black_flag { - draw_texture_ex( + if let Some(flag) = &state.black.flag { + draw_texture_both_ex!( flag, 1163f32, 738f32, @@ -90,82 +85,90 @@ impl PageRenderer { DrawTextureParams { dest_size: Some(vec2(180f32, 100f32)), ..Default::default() - }, + } ); } - let (x_off, text) = center_text_offset!( - 135f32, - format!("GAME #{}", &state.game_id.to_string()).as_str(), + let (x_off, text) = fit_text( + 270f32, + &format!("GAME #{}", &state.game_id.to_string()), 25, - self.textures.font + self.assets.font, + Justify::Center, ); draw_text_ex( text.as_str(), 830f32 + x_off, 745f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 25, ..Default::default() }, ); - let (x_off, text) = - center_text_offset!(124f32, state.start_time.as_str(), 25, self.textures.font); + let (x_off, text) = fit_text( + 248f32, + &state.start_time, + 25, + self.assets.font, + Justify::Center, + ); draw_text_ex( text.as_str(), 838f32 + x_off, 780f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 25, ..Default::default() }, ); - let (x_off, text) = center_text_offset!(110f32, &state.pool, 25, self.textures.font); + let (x_off, text) = fit_text(220f32, &state.pool, 25, self.assets.font, Justify::Center); draw_text_ex( text.as_str(), 855f32 + x_off, 815f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 25, ..Default::default() }, ); - let (x_off, text) = center_text_offset!( - 145f32, - state.snapshot.b_score.to_string().as_str(), + let (x_off, text) = fit_text( + 290f32, + &state.snapshot.b_score.to_string(), 180, - self.textures.font + self.assets.font, + Justify::Center, ); draw_text_both!( text.as_str(), 1295f32 + x_off, 580f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 180, ..Default::default() } ); - let (x_off, text) = center_text_offset!( - 145f32, - state.snapshot.w_score.to_string().as_str(), + let (x_off, text) = fit_text( + 290f32, + &state.snapshot.w_score.to_string(), 180, - self.textures.font + self.assets.font, + Justify::Center, ); draw_text_both_ex!( text.as_str(), 340f32 + x_off, 580f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 180, color: BLACK, ..Default::default() }, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 180, color: WHITE, ..Default::default() diff --git a/overlay/src/pages/in_game.rs b/overlay/src/pages/in_game.rs index 6a73f54e..02184e08 100644 --- a/overlay/src/pages/in_game.rs +++ b/overlay/src/pages/in_game.rs @@ -1,14 +1,12 @@ -use super::center_text_offset; use super::draw_texture_both; +use super::fit_text; use super::Interpolate; use super::PageRenderer; use crate::pages::draw_text_both; use crate::pages::draw_text_both_ex; +use crate::pages::draw_texture_both_ex; +use crate::pages::Justify; use crate::State; -use crate::ALPHA_MAX; -use crate::ALPHA_MIN; -use crate::TIME_AND_STATE_SHRINK_FROM; -use crate::TIME_AND_STATE_SHRINK_TO; use coarsetime::Instant; use macroquad::prelude::*; use uwh_common::game_snapshot::GamePeriod; @@ -18,103 +16,7 @@ impl PageRenderer { /// Display info during game play pub fn in_game_display(&mut self, state: &State) { // animate the state and time graphic 5 seconds since period started) - let (position_offset, alpha_offset) = if state.snapshot.secs_in_period < 1 { - // reset animation counters if page is nearing termination - self.animation_register1 = Instant::now(); - self.animation_register2 = Instant::now(); - if state.snapshot.current_period == GamePeriod::HalfTime { - ( - (TIME_AND_STATE_SHRINK_FROM, TIME_AND_STATE_SHRINK_TO).interpolate_linear(0f32), - (ALPHA_MAX, ALPHA_MIN).interpolate_linear(0f32) as u8, - ) - } else { - ( - (TIME_AND_STATE_SHRINK_FROM, TIME_AND_STATE_SHRINK_TO).interpolate_linear(1f32), - (ALPHA_MAX, ALPHA_MIN).interpolate_linear(1f32) as u8, - ) - } - } else if state.snapshot.current_period == GamePeriod::FirstHalf { - let time = Instant::now() - .duration_since(self.animation_register1) - .as_f64(); - match time { - x if (..=5f64).contains(&x) => ( - (TIME_AND_STATE_SHRINK_FROM, TIME_AND_STATE_SHRINK_TO).interpolate_linear(0f32), - (ALPHA_MAX, ALPHA_MIN).interpolate_linear(0f32) as u8, - ), - x if (5f64..=6f64).contains(&x) => ( - (TIME_AND_STATE_SHRINK_FROM, TIME_AND_STATE_SHRINK_TO) - .interpolate_linear(time as f32 - 5f32), - (ALPHA_MAX, ALPHA_MIN).interpolate_linear(time as f32 - 5f32) as u8, - ), - _ => ( - (TIME_AND_STATE_SHRINK_FROM, TIME_AND_STATE_SHRINK_TO).interpolate_linear(1f32), - (ALPHA_MAX, ALPHA_MIN).interpolate_linear(1f32) as u8, - ), - } - } else { - let time = Instant::now() - .duration_since(self.animation_register1) - .as_f64(); - match time { - x if (..=1f64).contains(&x) => { - if state.snapshot.current_period == GamePeriod::SecondHalf { - ( - (TIME_AND_STATE_SHRINK_FROM, TIME_AND_STATE_SHRINK_TO) - .interpolate_linear(0f32), - (ALPHA_MAX, ALPHA_MIN).interpolate_linear(0f32) as u8, - ) - } else { - ( - (TIME_AND_STATE_SHRINK_FROM, TIME_AND_STATE_SHRINK_TO) - .interpolate_linear(1f32 - time as f32), - (ALPHA_MAX, ALPHA_MIN).interpolate_linear(1f32 - time as f32) as u8, - ) - } - } - x if (1f64..=5f64).contains(&x) => ( - (TIME_AND_STATE_SHRINK_FROM, TIME_AND_STATE_SHRINK_TO).interpolate_linear(0f32), - (ALPHA_MAX, ALPHA_MIN).interpolate_linear(0f32) as u8, - ), - x if (5f64..=6f64).contains(&x) => { - if state.snapshot.current_period == GamePeriod::HalfTime { - ( - (TIME_AND_STATE_SHRINK_FROM, TIME_AND_STATE_SHRINK_TO) - .interpolate_linear(0f32), - (ALPHA_MAX, ALPHA_MIN).interpolate_linear(0f32) as u8, - ) - } else { - ( - (TIME_AND_STATE_SHRINK_FROM, TIME_AND_STATE_SHRINK_TO) - .interpolate_linear(time as f32 - 5f32), - (ALPHA_MAX, ALPHA_MIN).interpolate_linear(time as f32 - 5f32) as u8, - ) - } - } - _ => { - if state.snapshot.current_period == GamePeriod::HalfTime { - ( - (TIME_AND_STATE_SHRINK_FROM, TIME_AND_STATE_SHRINK_TO) - .interpolate_linear(0f32), - (ALPHA_MAX, ALPHA_MIN).interpolate_linear(0f32) as u8, - ) - } else { - ( - (TIME_AND_STATE_SHRINK_FROM, TIME_AND_STATE_SHRINK_TO) - .interpolate_linear(1f32), - (ALPHA_MAX, ALPHA_MIN).interpolate_linear(1f32) as u8, - ) - } - } - } - }; - draw_texture_both!(self.textures.team_bar_graphic, 26f32, 37f32, WHITE); - draw_texture_both!( - self.textures.in_game_mask, - 580f32 + position_offset, - 37f32, - WHITE - ); + draw_texture_both!(self.assets.team_bar, 26f32, 37f32, WHITE); let mut time = Instant::now() .duration_since(self.animation_register2) .as_f64() as f32; @@ -129,12 +31,12 @@ impl PageRenderer { if time < 1f32 { ( (0f32, -200f32).interpolate_linear(1f32 - time), - (0f32, 255f32).interpolate_exponential_end(time), + (0f32, 1f32).interpolate_exponential_end(time), ) } else { ( (0f32, -200f32).interpolate_linear(0f32), - (0f32, 255f32).interpolate_exponential_end(1f32), + (0f32, 1f32).interpolate_exponential_end(1f32), ) } } else if self.last_snapshot_timeout != TimeoutSnapshot::None { @@ -151,203 +53,266 @@ impl PageRenderer { self.last_snapshot_timeout = TimeoutSnapshot::None; ( (0f32, -200f32).interpolate_linear(1f32), - (0f32, 255f32).interpolate_exponential_end(0f32), + (0f32, 1f32).interpolate_exponential_end(0f32), ) } else { ( (0f32, -200f32).interpolate_linear(time), - (0f32, 255f32).interpolate_exponential_end(1f32 - time), + (0f32, 1f32).interpolate_exponential_end(1f32 - time), ) } } else { // return any values when both are None, cause we won't be redering anyways ( (0f32, -200f32).interpolate_linear(0f32), - (0f32, 255f32).interpolate_exponential_end(1f32), + (0f32, 1f32).interpolate_exponential_end(1f32), ) }; match self.last_snapshot_timeout { // draw text for each type of penalty TimeoutSnapshot::Ref(_) => { draw_texture_both!( - self.textures.referee_timout_graphic, - position_offset + timeout_offset + 580f32, + self.assets.referee_timout, + timeout_offset + 580f32, 35f32, - Color::from_rgba(255, 255, 255, timeout_alpha_offset as u8) + Color { + a: timeout_alpha_offset, + ..WHITE + } ); draw_text_both_ex!( "REFEREE", - 675f32 + position_offset + timeout_offset, + 675f32 + timeout_offset, 67f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 20, - color: Color::from_rgba(0, 0, 0, timeout_alpha_offset as u8), + color: Color { + a: timeout_alpha_offset, + ..BLACK + }, ..Default::default() }, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 20, - color: Color::from_rgba(255, 255, 255, timeout_alpha_offset as u8), + color: Color { + a: timeout_alpha_offset, + ..WHITE + }, ..Default::default() } ); draw_text_both_ex!( "TIMEOUT", - 680f32 + position_offset + timeout_offset, + 680f32 + timeout_offset, 95f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 20, - color: Color::from_rgba(0, 0, 0, timeout_alpha_offset as u8), + color: Color { + a: timeout_alpha_offset, + ..BLACK + }, ..Default::default() }, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 20, - color: Color::from_rgba(255, 255, 255, timeout_alpha_offset as u8), + color: Color { + a: timeout_alpha_offset, + ..WHITE + }, ..Default::default() } ); } TimeoutSnapshot::White(time) => { draw_texture_both!( - self.textures.white_timout_graphic, - position_offset + timeout_offset + 580f32, + self.assets.white_timout, + timeout_offset + 580f32, 35f32, - Color::from_rgba(255, 255, 255, timeout_alpha_offset as u8) + Color { + a: timeout_alpha_offset, + ..WHITE + } ); draw_text_both_ex!( "WHITE", - 675f32 + position_offset + timeout_offset, + 675f32 + timeout_offset, 67f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 20, - color: Color::from_rgba(0, 0, 0, timeout_alpha_offset as u8), + color: Color { + a: timeout_alpha_offset, + ..BLACK + }, ..Default::default() }, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 20, - color: Color::from_rgba(255, 255, 255, timeout_alpha_offset as u8), + color: Color { + a: timeout_alpha_offset, + ..WHITE + }, ..Default::default() } ); draw_text_both_ex!( "TIMEOUT", - 665f32 + position_offset + timeout_offset, + 665f32 + timeout_offset, 95f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 20, - color: Color::from_rgba(0, 0, 0, timeout_alpha_offset as u8), + color: Color { + a: timeout_alpha_offset, + ..BLACK + }, ..Default::default() }, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 20, - color: Color::from_rgba(255, 255, 255, timeout_alpha_offset as u8), + color: Color { + a: timeout_alpha_offset, + ..WHITE + }, ..Default::default() } ); draw_text_both_ex!( format!("{time}").as_str(), - 773f32 + position_offset + timeout_offset, + 773f32 + timeout_offset, 90f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 50, - color: Color::from_rgba(0, 0, 0, timeout_alpha_offset as u8), + color: Color { + a: timeout_alpha_offset, + ..BLACK + }, ..Default::default() }, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 50, - color: Color::from_rgba(255, 255, 255, timeout_alpha_offset as u8), + color: Color { + a: timeout_alpha_offset, + ..WHITE + }, ..Default::default() } ); } TimeoutSnapshot::Black(time) => { draw_texture_both!( - self.textures.black_timout_graphic, - position_offset + timeout_offset + 580f32, + self.assets.black_timout, + timeout_offset + 580f32, 35f32, - Color::from_rgba(255, 255, 255, timeout_alpha_offset as u8) + Color { + a: timeout_alpha_offset, + ..WHITE + } ); draw_text_both!( "BLACK", - 675f32 + position_offset + timeout_offset, + 675f32 + timeout_offset, 67f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 20, - color: Color::from_rgba(255, 255, 255, timeout_alpha_offset as u8), + color: Color { + a: timeout_alpha_offset, + ..WHITE + }, ..Default::default() } ); draw_text_both!( "TIMEOUT", - 665f32 + position_offset + timeout_offset, + 665f32 + timeout_offset, 95f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 20, - color: Color::from_rgba(255, 255, 255, timeout_alpha_offset as u8), + color: Color { + a: timeout_alpha_offset, + ..WHITE + }, ..Default::default() } ); draw_text_both!( format!("{time}").as_str(), - 773f32 + position_offset + timeout_offset, + 773f32 + timeout_offset, 90f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 50, - color: Color::from_rgba(255, 255, 255, timeout_alpha_offset as u8), + color: Color { + a: timeout_alpha_offset, + ..WHITE + }, ..Default::default() } ); } TimeoutSnapshot::PenaltyShot(_) => { draw_texture_both!( - self.textures.penalty_graphic, - position_offset + timeout_offset + 580f32, + self.assets.penalty, + timeout_offset + 580f32, 35f32, - Color::from_rgba(255, 255, 255, timeout_alpha_offset as u8) + Color { + a: timeout_alpha_offset, + ..WHITE + } ); draw_text_both_ex!( "PENALTY", - 675f32 + position_offset + timeout_offset, + 675f32 + timeout_offset, 67f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 20, - color: Color::from_rgba(0, 0, 0, timeout_alpha_offset as u8), + color: Color { + a: timeout_alpha_offset, + ..BLACK + }, ..Default::default() }, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 20, - color: Color::from_rgba(255, 255, 255, timeout_alpha_offset as u8), + color: Color { + a: timeout_alpha_offset, + ..WHITE + }, ..Default::default() } ); draw_text_both_ex!( "SHOT", - 690f32 + position_offset + timeout_offset, + 690f32 + timeout_offset, 95f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 20, - color: Color::from_rgba(0, 0, 0, timeout_alpha_offset as u8), + color: Color { + a: timeout_alpha_offset, + ..BLACK + }, ..Default::default() }, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 20, - color: Color::from_rgba(255, 255, 255, timeout_alpha_offset as u8), + color: Color { + a: timeout_alpha_offset, + ..WHITE + }, ..Default::default() } ); @@ -356,102 +321,64 @@ impl PageRenderer { } draw_text_both_ex!( - state.white.team_name.to_uppercase().as_str(), - if state.white_flag.is_some() { + &state.white.team_name, + if state.white.flag.is_some() { 160f32 } else { 79f32 }, 64f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 20, - color: Color::from_rgba( - 0, - 0, - 0, - if state.white_flag.is_some() { - alpha_offset - } else { - 255 - }, - ), // don't fade out team name if flags aren't available + color: BLACK, ..Default::default() }, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 20, - color: Color::from_rgba( - 255, - 255, - 255, - if state.white_flag.is_some() { - alpha_offset - } else { - 255 - }, - ), // don't fade out team name if flags aren't available + color: WHITE, ..Default::default() } ); draw_text_both!( - state.black.team_name.to_uppercase().as_str(), - if state.black_flag.is_some() { + &state.black.team_name, + if state.black.flag.is_some() { 160f32 } else { 79f32 }, 100f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 20, - color: Color::from_rgba( - 255, - 255, - 255, - if state.black_flag.is_some() { - alpha_offset - } else { - 255 - }, - ), + color: WHITE, ..Default::default() } ); - draw_texture_both!( - self.textures.time_and_game_state_graphic, - position_offset + 367f32, - 18f32, - WHITE - ); - if state.white_flag.is_some() { - draw_rectangle(1999f32, 39f32, 70f32, 33f32, WHITE); - } - if state.black_flag.is_some() { - draw_rectangle(1999f32, 75f32, 70f32, 33f32, WHITE); - } + draw_texture_both!(self.assets.time_and_game_state, 367f32, 18f32, WHITE); let min = state.snapshot.secs_in_period / 60; let secs = state.snapshot.secs_in_period % 60; let text = format!( "{}:{}", if min < 10 { - format!("0{}", min) + format!("0{min}") } else { - format!("{}", min) + format!("{min}") }, if secs < 10 { - format!("0{}", secs) + format!("0{secs}") } else { - format!("{}", secs) + format!("{secs}") } ); - let (x_off, text) = center_text_offset!(90f32, text.as_str(), 50, self.textures.font); + let (x_off, text) = fit_text(180f32, &text, 50, self.assets.font, Justify::Center); draw_text_ex( text.as_str(), - 430f32 + position_offset + x_off, + 430f32 + x_off, 67f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 50, ..Default::default() }, @@ -462,16 +389,16 @@ impl PageRenderer { GamePeriod::SecondHalf => "2ND HALF", _ => "HALF TIME", }, - 478f32 + position_offset, + 478f32, 100f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 20, ..Default::default() }, ); - if let Some(flag) = state.white_flag { - draw_texture_ex( + if let Some(flag) = &state.white.flag { + draw_texture_both_ex!( flag, 79f32, 39f32, @@ -479,11 +406,11 @@ impl PageRenderer { DrawTextureParams { dest_size: Some(vec2(70f32, 33f32)), ..Default::default() - }, + } ); } - if let Some(flag) = state.black_flag { - draw_texture_ex( + if let Some(flag) = &state.black.flag { + draw_texture_both_ex!( flag, 79f32, 75f32, @@ -491,7 +418,7 @@ impl PageRenderer { DrawTextureParams { dest_size: Some(vec2(70f32, 33f32)), ..Default::default() - }, + } ); } @@ -500,7 +427,7 @@ impl PageRenderer { 40f32, 104f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 30, ..Default::default() } @@ -510,20 +437,20 @@ impl PageRenderer { 40f32, 65f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 30, color: BLACK, ..Default::default() }, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 30, color: WHITE, ..Default::default() } ); - if let Some(logo) = self.textures.tournament_logo.as_ref() { + if let Some(logo) = self.assets.tournament_logo.as_ref() { let x = 1900f32 - logo.color.width(); draw_texture_both!(logo, x, 20f32, WHITE); } diff --git a/overlay/src/pages/mod.rs b/overlay/src/pages/mod.rs index 9cde2c43..d09dd012 100644 --- a/overlay/src/pages/mod.rs +++ b/overlay/src/pages/mod.rs @@ -7,11 +7,12 @@ mod in_game; mod next_game; mod overtime_and_sudden_death; mod pre_game; -mod roster; +pub mod roster; -pub(crate) trait Interpolate { - /// `value` must be a floater varying from 0 to 1, denoting the lowest to highest limits of the range +pub trait Interpolate { + /// `value` must be a `float` normally varying from `0f32` to `1f32` fn interpolate_linear(&self, value: f32) -> f32; + /// Performs exponential interpolation towards the end of the range. fn interpolate_exponential_end(&self, value: f32) -> f32; } @@ -31,24 +32,98 @@ impl Interpolate for (f32, f32) { } } -macro_rules! center_text_offset { - ($field_width: expr, $string: expr, $font_size: literal, $font: expr) => {{ - let mut text = $string.to_string(); - let text = { - while 2f32 * $field_width - < measure_text(text.as_str(), Some($font), $font_size, 1.0).width +/// Wrap the given `text` into lines that fit within the specified `width`. +/// +/// Divides the `text` into multiple lines, breaking at whitespace such that new words go on a new +/// line if they overflow the `width`. Lines may still overflow if there is no whitespace to break +/// at, so use the `center_text_offset` macro to center lines and crop each one to fit the width. +pub fn multilinify(text: &str, width: f32, font: Option, font_size: u16) -> Vec { + let mut lines = Vec::new(); + let mut current_line = String::new(); + + for word in text.split_whitespace() { + let word_width = measure_text(word, font, font_size, 1.0).width; + let line_width = measure_text(¤t_line, font, font_size, 1.0).width; + + if line_width + word_width <= width { + // Word fits within the current line + if !current_line.is_empty() { + current_line.push(' '); + } + current_line.push_str(word); + } else { + // Word doesn't fit within the current line, start a new line + lines.push(current_line.clone()); + current_line = word.to_string(); + } + } + + // Push the remaining text as the last line + if !current_line.is_empty() { + lines.push(current_line); + } + + lines +} + +pub enum Justify { + Left, + Center, + #[allow(dead_code)] + Right, +} + +/// Calculates the offset for fitting text within a text field. +/// +/// The function takes the following parameters: +/// - `field_width`: Width of the text field. +/// - `text`: The string to be fitted within the text field. +/// - `font_size`: The font size. +/// - `font`: The font used for rendering the text. +/// +/// Returns a tuple containing the offset from the left side of the text field +/// to render text from, and the modified string that fits within the field. +pub fn fit_text( + field_width: f32, + text: &str, + font_size: u16, + font: Font, + justify: Justify, +) -> (f32, String) { + let mut text = text.to_string(); + + let mut popped = false; + while field_width < measure_text(text.as_str(), Some(font), font_size, 1.0).width { + text.pop(); + popped = true; + } + + let text = if popped { + if field_width < measure_text(&(text.clone() + ".."), Some(font), font_size, 1.0).width { + while field_width + < measure_text(&(text.clone() + ".."), Some(font), font_size, 1.0).width { text.pop(); } - text - }; - ( - $field_width - measure_text(text.as_str(), Some($font), $font_size, 1.0).width / 2f32, - text, - ) - }}; + text + ".." + } else { + text + ".." + } + } else { + text + }; + let x_off = match justify { + Justify::Left => 0f32, + Justify::Center => { + (field_width / 2f32) + - measure_text(text.as_str(), Some(font), font_size, 1.0).width / 2f32 + } + Justify::Right => { + field_width - measure_text(text.as_str(), Some(font), font_size, 1.0).width / 2f32 + } + }; + (x_off, text) } -pub(crate) use center_text_offset; macro_rules! draw_texture_both { ($texture: expr, $x: expr, $y: expr, $color: expr) => { @@ -58,6 +133,14 @@ macro_rules! draw_texture_both { } pub(crate) use draw_texture_both; +macro_rules! draw_texture_both_ex { + ($texture: expr, $x: expr, $y: expr, $color: expr, $params: expr) => { + draw_texture_ex($texture.color, $x, $y, $color, $params); + draw_texture_ex($texture.alpha, $x + 1920f32, $y, $color, $params); + }; +} +pub(crate) use draw_texture_both_ex; + macro_rules! draw_text_both { ($text: expr, $x: expr, $y: expr, $params: expr) => { draw_text_ex($text, $x, $y, $params); @@ -80,7 +163,7 @@ use uwh_common::game_snapshot::TimeoutSnapshot; /// Utility function used to place overlay elements quickly through user input without recompiling pub fn get_input(prompt: &str) -> T { let mut buffer = String::new(); - println!(" Enter {}: ", prompt); + println!(" Enter {prompt}: "); std::io::stdin() .read_line(&mut buffer) .expect("Failed to init stdin"); @@ -92,9 +175,10 @@ pub struct PageRenderer { pub animation_register1: Instant, /// Use if there are more than one simultenous animations pub animation_register2: Instant, + pub animation_register0: Instant, pub animation_register3: bool, /// Contains textures, alpha in alpha mode, color in color mode - pub textures: Textures, + pub assets: Textures, /// We need to keep track of the last timeout snapshot in order to display information during the fade out pub last_snapshot_timeout: TimeoutSnapshot, } diff --git a/overlay/src/pages/next_game.rs b/overlay/src/pages/next_game.rs index 5dd00184..3ca07731 100644 --- a/overlay/src/pages/next_game.rs +++ b/overlay/src/pages/next_game.rs @@ -1,8 +1,10 @@ -use super::center_text_offset; use super::draw_texture_both; +use super::fit_text; use super::PageRenderer; use crate::pages::draw_text_both; use crate::pages::draw_text_both_ex; +use crate::pages::draw_texture_both_ex; +use crate::pages::Justify; use crate::State; use coarsetime::Instant; use macroquad::prelude::*; @@ -11,67 +13,58 @@ impl PageRenderer { /// The Next Game screen, shown up to 150 seconds before the next game pub fn next_game(&mut self, state: &State) { self.animation_register1 = Instant::now(); - draw_texture_both!(self.textures.atlantis_logo_graphic, 836f32, 725f32, WHITE); - draw_texture_both!(self.textures.bottom_graphic, 822f32, 977f32, WHITE); - draw_texture_both!( - self.textures.team_information_graphic, - 130f32, - 710f32, - WHITE - ); + draw_texture_both!(self.assets.atlantis_logo, 836f32, 725f32, WHITE); + draw_texture_both!(self.assets.bottom, 822f32, 977f32, WHITE); + draw_texture_both!(self.assets.team_information, 130f32, 710f32, WHITE); - if let Some(logo) = self.textures.tournament_logo.as_ref() { + if let Some(logo) = self.assets.tournament_logo.as_ref() { let x = 1900f32 - logo.color.width(); draw_texture_both!(logo, x, 20f32, WHITE); } - let (x_off, text) = center_text_offset!( - 217f32, - state.black.team_name.to_uppercase().as_str(), + let (x_off, text) = fit_text( + 434f32, + &state.black.team_name, 45, - self.textures.font + self.assets.font, + Justify::Center, ); draw_text_both!( text.as_str(), 1350f32 + x_off, 805f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 45, ..Default::default() } ); - let (x_off, text) = center_text_offset!( - 220f32, - state.white.team_name.to_uppercase().as_str(), + let (x_off, text) = fit_text( + 440f32, + &state.white.team_name, 45, - self.textures.font + self.assets.font, + Justify::Center, ); draw_text_both_ex!( text.as_str(), 135f32 + x_off, 805f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 45, color: BLACK, ..Default::default() }, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 45, color: WHITE, ..Default::default() } ); - if state.white_flag.is_some() { - draw_rectangle(2500f32, 738f32, 180f32, 100f32, WHITE); - } - if state.black_flag.is_some() { - draw_rectangle(3083f32, 738f32, 180f32, 100f32, WHITE); - } - if let Some(flag) = state.white_flag { - draw_texture_ex( + if let Some(flag) = &state.white.flag { + draw_texture_both_ex!( flag, 580f32, 738f32, @@ -79,11 +72,11 @@ impl PageRenderer { DrawTextureParams { dest_size: Some(vec2(180f32, 100f32)), ..Default::default() - }, + } ); } - if let Some(flag) = state.black_flag { - draw_texture_ex( + if let Some(flag) = &state.black.flag { + draw_texture_both_ex!( flag, 1163f32, 738f32, @@ -91,44 +84,50 @@ impl PageRenderer { DrawTextureParams { dest_size: Some(vec2(180f32, 100f32)), ..Default::default() - }, + } ); } - let (x_off, text) = center_text_offset!( - 135f32, - format!("GAME #{}", &state.game_id.to_string()).as_str(), + let (x_off, text) = fit_text( + 270f32, + &format!("GAME #{}", &state.game_id.to_string()), 25, - self.textures.font + self.assets.font, + Justify::Center, ); draw_text_ex( text.as_str(), 830f32 + x_off, 745f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 25, ..Default::default() }, ); - let (x_off, text) = - center_text_offset!(124f32, state.start_time.as_str(), 25, self.textures.font); + let (x_off, text) = fit_text( + 248f32, + &state.start_time, + 25, + self.assets.font, + Justify::Center, + ); draw_text_ex( text.as_str(), 838f32 + x_off, 780f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 25, ..Default::default() }, ); - let (x_off, text) = center_text_offset!(110f32, &state.pool, 25, self.textures.font); + let (x_off, text) = fit_text(220f32, &state.pool, 25, self.assets.font, Justify::Center); draw_text_ex( text.as_str(), 855f32 + x_off, 815f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 25, ..Default::default() }, @@ -138,23 +137,23 @@ impl PageRenderer { let text = format!( "{}:{}", if min < 10 { - format!("0{}", min) + format!("0{min}") } else { - format!("{}", min) + format!("{min}") }, if secs < 10 { - format!("0{}", secs) + format!("0{secs}") } else { - format!("{}", secs) + format!("{secs}") } ); - let (x_off, text) = center_text_offset!(90f32, text.as_str(), 50, self.textures.font); + let (x_off, text) = fit_text(180f32, &text, 50, self.assets.font, Justify::Center); draw_text_ex( text.as_str(), 870f32 + x_off, 1020f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 50, ..Default::default() }, @@ -164,7 +163,7 @@ impl PageRenderer { 907f32, 1044f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 20, ..Default::default() }, diff --git a/overlay/src/pages/overtime_and_sudden_death.rs b/overlay/src/pages/overtime_and_sudden_death.rs index 32828484..cffa1eb6 100644 --- a/overlay/src/pages/overtime_and_sudden_death.rs +++ b/overlay/src/pages/overtime_and_sudden_death.rs @@ -1,12 +1,12 @@ -use super::center_text_offset; use super::draw_texture_both; +use super::fit_text; use super::Interpolate; use super::PageRenderer; use crate::pages::draw_text_both; use crate::pages::draw_text_both_ex; +use crate::pages::draw_texture_both_ex; +use crate::pages::Justify; use crate::State; -use crate::ALPHA_MAX; -use crate::ALPHA_MIN; use coarsetime::Instant; use macroquad::prelude::*; use uwh_common::game_snapshot::GamePeriod; @@ -30,12 +30,12 @@ impl PageRenderer { if time < 1f32 { ( (0f32, -200f32).interpolate_linear(1f32 - time), - (ALPHA_MIN, ALPHA_MAX).interpolate_exponential_end(time), + (0f32, 1f32).interpolate_exponential_end(time), ) } else { ( (0f32, -200f32).interpolate_linear(0f32), - (ALPHA_MIN, ALPHA_MAX).interpolate_exponential_end(1f32), + (0f32, 1f32).interpolate_exponential_end(1f32), ) } } else if self.last_snapshot_timeout != TimeoutSnapshot::None { @@ -52,47 +52,55 @@ impl PageRenderer { self.last_snapshot_timeout = TimeoutSnapshot::None; ( (0f32, -200f32).interpolate_linear(1f32), - (ALPHA_MIN, ALPHA_MAX).interpolate_exponential_end(0f32), + (0f32, 1f32).interpolate_exponential_end(0f32), ) } else { ( (0f32, -200f32).interpolate_linear(time), - (ALPHA_MIN, ALPHA_MAX).interpolate_exponential_end(1f32 - time), + (0f32, 1f32).interpolate_exponential_end(1f32 - time), ) } } else { // return any values when both are None, cause we won't be redering anyways ( (0f32, -200f32).interpolate_linear(0f32), - (ALPHA_MIN, ALPHA_MAX).interpolate_exponential_end(1f32), + (0f32, 1f32).interpolate_exponential_end(1f32), ) }; - draw_texture_both!(self.textures.team_bar_graphic, 26f32, 37f32, WHITE); - draw_texture_both!(self.textures.in_game_mask, 359f32, 0f32, WHITE); + draw_texture_both!(self.assets.team_bar, 26f32, 37f32, WHITE); // No penalty shot, black or white timeouts in overtime match self.last_snapshot_timeout { TimeoutSnapshot::Ref(_) => { draw_texture_both!( - self.textures.referee_timout_graphic, + self.assets.referee_timout, timeout_offset + 380f32, 35f32, - Color::from_rgba(255, 255, 255, timeout_alpha_offset as u8) + Color { + a: timeout_alpha_offset, + ..WHITE + } ); draw_text_both_ex!( "REFEREE", 475f32 + timeout_offset, 67f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 20, - color: Color::from_rgba(0, 0, 0, timeout_alpha_offset as u8), + color: Color { + a: timeout_alpha_offset, + ..BLACK + }, ..Default::default() }, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 20, - color: Color::from_rgba(255, 255, 255, timeout_alpha_offset as u8), + color: Color { + a: timeout_alpha_offset, + ..WHITE + }, ..Default::default() } ); @@ -101,40 +109,55 @@ impl PageRenderer { 480f32 + timeout_offset, 95f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 20, - color: Color::from_rgba(0, 0, 0, timeout_alpha_offset as u8), + color: Color { + a: timeout_alpha_offset, + ..BLACK + }, ..Default::default() }, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 20, - color: Color::from_rgba(255, 255, 255, timeout_alpha_offset as u8), + color: Color { + a: timeout_alpha_offset, + ..WHITE + }, ..Default::default() } ); } TimeoutSnapshot::PenaltyShot(_) => { draw_texture_both!( - self.textures.penalty_graphic, + self.assets.penalty, timeout_offset + 380f32, 35f32, - Color::from_rgba(255, 255, 255, timeout_alpha_offset as u8) + Color { + a: timeout_alpha_offset, + ..WHITE + } ); draw_text_both_ex!( "PENALTY", 475f32 + timeout_offset, 67f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 20, - color: Color::from_rgba(0, 0, 0, timeout_alpha_offset as u8), + color: Color { + a: timeout_alpha_offset, + ..BLACK + }, ..Default::default() }, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 20, - color: Color::from_rgba(255, 255, 255, timeout_alpha_offset as u8), + color: Color { + a: timeout_alpha_offset, + ..WHITE + }, ..Default::default() } ); @@ -143,100 +166,95 @@ impl PageRenderer { 490f32 + timeout_offset, 95f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 20, - color: Color::from_rgba(0, 0, 0, timeout_alpha_offset as u8), + color: Color { + a: timeout_alpha_offset, + ..BLACK + }, ..Default::default() }, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 20, - color: Color::from_rgba(255, 255, 255, timeout_alpha_offset as u8), + color: Color { + a: timeout_alpha_offset, + ..WHITE + }, ..Default::default() } ); } _ => {} } - if state.white_flag.is_none() { + if state.white.flag.is_none() { draw_text_both_ex!( - state.white.team_name.to_uppercase().as_str(), - if state.white_flag.is_some() { + &state.white.team_name, + if state.white.flag.is_some() { 160f32 } else { 79f32 }, 64f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 20, - color: Color::from_rgba(0, 0, 0, 255,), // don't fade out team name if flags aren't available + color: BLACK, // don't fade out team name if flags aren't available ..Default::default() }, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 20, - color: Color::from_rgba(255, 255, 255, 255,), // don't fade out team name if flags aren't available + color: WHITE, // don't fade out team name if flags aren't available ..Default::default() } ); draw_text_both!( - state.black.team_name.to_uppercase().as_str(), - if state.black_flag.is_some() { + &state.black.team_name, + if state.black.flag.is_some() { 160f32 } else { 79f32 }, 100f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 20, - color: Color::from_rgba(255, 255, 255, 255), + color: WHITE, ..Default::default() } ); } - draw_texture_both!( - self.textures.time_and_game_state_graphic, - 167f32, - 18f32, - WHITE - ); - if state.white_flag.is_some() { - draw_rectangle(1999f32, 39f32, 70f32, 33f32, WHITE); - } - if state.black_flag.is_some() { - draw_rectangle(1999f32, 75f32, 70f32, 33f32, WHITE); - } + draw_texture_both!(self.assets.time_and_game_state, 367f32, 18f32, WHITE); let min = state.snapshot.secs_in_period / 60; let secs = state.snapshot.secs_in_period % 60; let text = format!( "{}:{}", if min < 10 { - format!("0{}", min) + format!("0{min}") } else { - format!("{}", min) + format!("{min}") }, if secs < 10 { - format!("0{}", secs) + format!("0{secs}") } else { - format!("{}", secs) + format!("{secs}") } ); - let (x_off, text) = center_text_offset!(90f32, text.as_str(), 50, self.textures.font); + let (x_off, text) = fit_text(180f32, &text, 50, self.assets.font, Justify::Center); draw_text_ex( text.as_str(), - 230f32 + x_off, + 430f32 + x_off, 95f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 50, color: if [GamePeriod::SuddenDeath, GamePeriod::PreSuddenDeath] .contains(&state.snapshot.current_period) { - Color::from_rgba(255, 150, 0, 255) + GOLD } else { - Color::from_rgba(255, 0, 0, 255) + RED }, ..Default::default() }, @@ -249,26 +267,26 @@ impl PageRenderer { GamePeriod::PreSuddenDeath => "PRE SUDDEN DEATH", _ => "PRE OVERTIME", }; - let (x_off, text) = center_text_offset!(100f32, ot_text, 20, self.textures.font); + let (x_off, text) = fit_text(200f32, ot_text, 20, self.assets.font, Justify::Center); draw_text_ex( text.as_str(), - 220f32 + x_off, + 420f32 + x_off, 45f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 20, color: if [GamePeriod::SuddenDeath, GamePeriod::PreSuddenDeath] .contains(&state.snapshot.current_period) { - Color::from_rgba(255, 150, 0, 255) + GOLD } else { - Color::from_rgba(255, 0, 0, 255) + RED }, ..Default::default() }, ); - if let Some(flag) = state.white_flag { - draw_texture_ex( + if let Some(flag) = &state.white.flag { + draw_texture_both_ex!( flag, 79f32, 39f32, @@ -276,11 +294,11 @@ impl PageRenderer { DrawTextureParams { dest_size: Some(vec2(70f32, 33f32)), ..Default::default() - }, + } ); } - if let Some(flag) = state.black_flag { - draw_texture_ex( + if let Some(flag) = &state.black.flag { + draw_texture_both_ex!( flag, 79f32, 75f32, @@ -288,7 +306,7 @@ impl PageRenderer { DrawTextureParams { dest_size: Some(vec2(70f32, 33f32)), ..Default::default() - }, + } ); } @@ -297,7 +315,7 @@ impl PageRenderer { 40f32, 104f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 30, ..Default::default() } @@ -307,13 +325,13 @@ impl PageRenderer { 40f32, 65f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 30, color: BLACK, ..Default::default() }, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 30, color: WHITE, ..Default::default() diff --git a/overlay/src/pages/pre_game.rs b/overlay/src/pages/pre_game.rs index fef84ba1..646e504e 100644 --- a/overlay/src/pages/pre_game.rs +++ b/overlay/src/pages/pre_game.rs @@ -1,140 +1,165 @@ -use super::center_text_offset; use super::draw_texture_both; +use super::fit_text; use super::Interpolate; use super::PageRenderer; use crate::pages::draw_text_both; use crate::pages::draw_text_both_ex; -use crate::State; -use crate::ALPHA_MAX; -use crate::ALPHA_MIN; +use crate::pages::draw_texture_both_ex; +use crate::{pages::Justify, State}; use coarsetime::Instant; use macroquad::prelude::*; impl PageRenderer { /// Displayed from 30 seconds before a game begins. pub fn pre_game_display(&mut self, state: &State) { - match state.snapshot.secs_in_period { - 16.. => { - //draw_texture_both!(self.textures.atlantis_logo_graphic, 836f32, 725f32, WHITE); - draw_texture_both!(self.textures.bottom_graphic, 822f32, 977f32, WHITE); - let min = state.snapshot.secs_in_period / 60; - let secs = state.snapshot.secs_in_period % 60; - let text = format!( - "{}:{}", - if min < 10 { - format!("0{}", min) - } else { - format!("{}", min) - }, - if secs < 10 { - format!("0{}", secs) - } else { - format!("{}", secs) - } - ); - let (x_off, text) = - center_text_offset!(90f32, text.as_str(), 50, self.textures.font); - draw_text_ex( - text.as_str(), - 870f32 + x_off, - 1020f32, - TextParams { - font: self.textures.font, - font_size: 50, - ..Default::default() - }, - ); - draw_text_ex( - "NEXT GAME", - 905f32, - 1044f32, - TextParams { - font: self.textures.font, - font_size: 20, - ..Default::default() - }, - ); + let (sponsor_alpha, midfade_alpha, tandg_alpha) = match state.snapshot.secs_in_period { + 31.. => { self.animation_register1 = Instant::now(); + ( + Instant::now() + .duration_since(self.animation_register0) + .as_f64() as f32 + * 2f32, + Instant::now() + .duration_since(self.animation_register0) + .as_f64() as f32 + * 2f32, + Instant::now() + .duration_since(self.animation_register0) + .as_f64() as f32 + * 2f32, + ) } - 15 => { - // animate a fade on the fifteenth second - let offset = (ALPHA_MAX, ALPHA_MIN).interpolate_linear( + 30 => ( + (1f32, 0f32).interpolate_linear( Instant::now() .duration_since(self.animation_register1) .as_f64() as f32, - ) as u8; + ), + 1f32, + 1f32, + ), + 16.. => { + self.animation_register1 = Instant::now(); + (0f32, 1f32, 1f32) + } + 15 => { + ( + 0f32, // animate a fade on the fifteenth second + (1f32, 0f32).interpolate_linear( + Instant::now() + .duration_since(self.animation_register1) + .as_f64() as f32, + ), + 1f32, + ) + } + _ => { + self.animation_register1 = Instant::now(); + (0f32, 0f32, 1f32) + } + }; - // draw_texture_both!( - // self.textures.atlantis_logo_graphic, - // 836f32, - // 725f32, - // Color::from_rgba(255, 255, 255, offset) - // ); + if let Some(sponsor_logo) = &state.sponsor_logo { + if sponsor_alpha > 0f32 { draw_texture_both!( - self.textures.bottom_graphic, - 822f32, - 977f32, - Color::from_rgba(255, 255, 255, offset) - ); - let min = state.snapshot.secs_in_period / 60; - let secs = state.snapshot.secs_in_period % 60; - let text = format!( - "{}:{}", - if min < 10 { - format!("0{}", min) - } else { - format!("{}", min) - }, - if secs < 10 { - format!("0{}", secs) - } else { - format!("{}", secs) + sponsor_logo, + 300f32, + 100f32, + Color { + a: sponsor_alpha, + ..WHITE } ); - let (x_off, text) = - center_text_offset!(90f32, text.as_str(), 50, self.textures.font); - draw_text_ex( - text.as_str(), - 870f32 + x_off, - 1020f32, - TextParams { - font: self.textures.font, - font_size: 50, - color: Color::from_rgba(255, 255, 255, offset), - ..Default::default() - }, - ); - draw_text_ex( - "NEXT GAME", - 905f32, - 1044f32, - TextParams { - font: self.textures.font, - font_size: 20, - color: Color::from_rgba(255, 255, 255, offset), + } + } - ..Default::default() + if midfade_alpha > 0f32 { + // draw_texture_both!( + // self.assets.atlantis_logo, + // 836f32, + // 725f32, + // Color { + // a: midfade_alpha, + // ..WHITE + // } + // ); + draw_texture_both!( + self.assets.bottom, + 822f32, + 977f32, + Color { + a: midfade_alpha, + ..WHITE + } + ); + let min = state.snapshot.secs_in_period / 60; + let secs = state.snapshot.secs_in_period % 60; + let text = format!( + "{}:{}", + if min < 10 { + format!("0{min}") + } else { + format!("{min}") + }, + if secs < 10 { + format!("0{secs}") + } else { + format!("{secs}") + } + ); + let (x_off, text) = fit_text(180f32, &text, 50, self.assets.font, Justify::Center); + draw_text_ex( + text.as_str(), + 870f32 + x_off, + 1020f32, + TextParams { + font: self.assets.font, + font_size: 50, + color: Color { + a: midfade_alpha, + ..WHITE }, - ); - } - _ => { - self.animation_register1 = Instant::now(); - } + ..Default::default() + }, + ); + draw_text_ex( + "NEXT GAME", + 905f32, + 1044f32, + TextParams { + font: self.assets.font, + font_size: 20, + color: Color { + a: midfade_alpha, + ..WHITE + }, + + ..Default::default() + }, + ); } - draw_texture_both!(self.textures.team_bar_graphic, 26f32, 37f32, WHITE); + draw_texture_both!( - self.textures.time_and_game_state_graphic, - 367f32, - 18f32, - WHITE + self.assets.team_bar, + 26f32, + 37f32, + Color { + a: tandg_alpha, + ..WHITE + } ); draw_text_both!( state.snapshot.b_score.to_string().as_str(), 40f32, 104f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 30, + color: Color { + a: tandg_alpha, + ..WHITE + }, ..Default::default() } ); @@ -143,75 +168,106 @@ impl PageRenderer { 40f32, 65f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 30, - color: BLACK, + color: Color { + a: tandg_alpha, + ..BLACK + }, ..Default::default() }, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 30, + color: Color { + a: tandg_alpha, + ..WHITE + }, ..Default::default() } ); draw_text_both_ex!( - state.white.team_name.to_uppercase().as_str(), - if state.white_flag.is_some() { + &state.white.team_name, + if state.white.flag.is_some() { 160f32 } else { 79f32 }, 64f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 20, - color: BLACK, + color: Color { + a: tandg_alpha, + ..BLACK + }, ..Default::default() }, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 20, + color: Color { + a: tandg_alpha, + ..WHITE + }, ..Default::default() } ); draw_text_both!( - state.black.team_name.to_uppercase().as_str(), - if state.black_flag.is_some() { + &state.black.team_name, + if state.black.flag.is_some() { 160f32 } else { 79f32 }, 100f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 20, + color: Color { + a: tandg_alpha, + ..WHITE + }, ..Default::default() } ); + draw_texture_both!( + self.assets.time_and_game_state, + 367f32, + 18f32, + Color { + a: tandg_alpha, + ..WHITE + } + ); let min = state.half_play_duration.unwrap_or(900) / 60; let secs = state.half_play_duration.unwrap_or(900) % 60; let text = format!( "{}:{}", if min < 10 { - format!("0{}", min) + format!("0{min}") } else { - format!("{}", min) + format!("{min}") }, if secs < 10 { - format!("0{}", secs) + format!("0{secs}") } else { - format!("{}", secs) + format!("{secs}") } ); - let (x_off, text) = center_text_offset!(90f32, text.as_str(), 50, self.textures.font); + let (x_off, text) = fit_text(180f32, &text, 50, self.assets.font, Justify::Center); draw_text_ex( text.as_str(), 430f32 + x_off, 67f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 50, + color: Color { + a: tandg_alpha, + ..WHITE + }, ..Default::default() }, ); @@ -220,40 +276,44 @@ impl PageRenderer { 478f32, 100f32, TextParams { - font: self.textures.font, + font: self.assets.font, font_size: 20, + color: Color { + a: tandg_alpha, + ..WHITE + }, ..Default::default() }, ); - if let Some(flag) = state.white_flag { - draw_texture_ex( + if let Some(flag) = &state.white.flag { + draw_texture_both_ex!( flag, 79f32, 39f32, - WHITE, + Color { + a: tandg_alpha, + ..WHITE + }, DrawTextureParams { dest_size: Some(vec2(70f32, 33f32)), ..Default::default() - }, + } ); } - if let Some(flag) = state.black_flag { - draw_texture_ex( + if let Some(flag) = &state.black.flag { + draw_texture_both_ex!( flag, 79f32, 75f32, - WHITE, + Color { + a: tandg_alpha, + ..WHITE + }, DrawTextureParams { dest_size: Some(vec2(70f32, 33f32)), ..Default::default() - }, + } ); } - if state.white_flag.is_some() { - draw_rectangle(1999f32, 39f32, 70f32, 33f32, WHITE); - } - if state.black_flag.is_some() { - draw_rectangle(1999f32, 75f32, 70f32, 33f32, WHITE); - } } } diff --git a/overlay/src/pages/roster.rs b/overlay/src/pages/roster.rs deleted file mode 100644 index 9c888655..00000000 --- a/overlay/src/pages/roster.rs +++ /dev/null @@ -1,273 +0,0 @@ -use super::center_text_offset; -use super::draw_texture_both; -use super::Interpolate; -use super::PageRenderer; -use crate::pages::draw_text_both; -use crate::pages::draw_text_both_ex; -use crate::State; -use coarsetime::Instant; -use macroquad::prelude::*; -use uwh_common::game_snapshot::Color; - -impl PageRenderer { - /// Roster screen, displayed between 150 and 30 seconds before the next game. - pub fn roster(&mut self, state: &State) { - let offset = if state.snapshot.secs_in_period == 150 { - (0f32, -650f32).interpolate_linear( - Instant::now() - .duration_since(self.animation_register1) - .as_f64() as f32, - ) - } else { - self.animation_register1 = Instant::now(); - (0f32, -650f32).interpolate_linear(1f32) - }; - - if let Some(logo) = self.textures.tournament_logo.as_ref() { - if offset <= -210f32 { - let x = (1920f32 - logo.color.width()) / 2f32; - let y = 675f32 - logo.color.height(); - draw_texture_both!(logo, x, y, WHITE); - } - } - - draw_texture_both!(self.textures.atlantis_logo_graphic, 836f32, 725f32, WHITE); - draw_texture_both!(self.textures.bottom_graphic, 822f32, 977f32, WHITE); - for (i, player_identifier) in state - .white - .players - .iter() - .map(|player| (player, Color::White)) - .enumerate() - .chain( - state - .black - .players - .iter() - .map(|player| (player, Color::Black)) - .enumerate(), - ) - { - if 60f32 * i as f32 + 220f32 > 650f32 + offset + 100f32 { - if player_identifier.1 == Color::White { - draw_texture_both!( - self.textures.team_white_graphic, - if player_identifier.1 == Color::White { - 150f32 - } else { - 1090f32 - }, - 60f32 * i as f32 + 220f32, - WHITE - ); - } else { - draw_texture_both!( - self.textures.team_black_graphic, - if player_identifier.1 == Color::White { - 150f32 - } else { - 1090f32 - }, - 60f32 * i as f32 + 220f32, - WHITE - ); - } - draw_text_ex( - format!("#{}", player_identifier.0 .1).as_str(), - if player_identifier.1 == Color::White { - 185f32 - } else { - 1120f32 - }, - 252f32 + 60f32 * i as f32, - TextParams { - font: self.textures.font, - font_size: 35, - color: if player_identifier.1 == Color::White { - BLACK - } else { - WHITE - }, - ..Default::default() - }, - ); - draw_text_both_ex!( - player_identifier.0 .0.as_str(), - if player_identifier.1 == Color::White { - 285f32 - } else { - 1220f32 - }, - 252f32 + 60f32 * i as f32, - TextParams { - font: self.textures.font, - font_size: 35, - color: if player_identifier.1 == Color::White { - BLACK - } else { - WHITE - }, - ..Default::default() - }, - TextParams { - font: self.textures.font, - font_size: 35, - color: WHITE, - ..Default::default() - } - ); - } - } - draw_texture_both!( - self.textures.team_information_graphic, - 130f32, - 710f32 + offset, - WHITE - ); - let (x_off, text) = center_text_offset!( - 217f32, - state.black.team_name.to_uppercase().as_str(), - 45, - self.textures.font - ); - draw_text_both!( - text.as_str(), - 1350f32 + x_off, - 805f32 + offset, - TextParams { - font: self.textures.font, - font_size: 45, - ..Default::default() - } - ); - let (x_off, text) = center_text_offset!( - 220f32, - state.white.team_name.to_uppercase().as_str(), - 45, - self.textures.font - ); - draw_text_both_ex!( - text.as_str(), - 135f32 + x_off, - 805f32 + offset, - TextParams { - font: self.textures.font, - font_size: 45, - color: BLACK, - ..Default::default() - }, - TextParams { - font: self.textures.font, - font_size: 45, - color: WHITE, - ..Default::default() - } - ); - if state.white_flag.is_some() { - draw_rectangle(2500f32, 738f32 + offset, 180f32, 100f32, WHITE); - } - if state.black_flag.is_some() { - draw_rectangle(3083f32, 738f32 + offset, 180f32, 100f32, WHITE); - } - if let Some(flag) = state.white_flag { - draw_texture_ex( - flag, - 580f32, - 738f32 + offset, - WHITE, - DrawTextureParams { - dest_size: Some(vec2(180f32, 100f32)), - ..Default::default() - }, - ); - } - if let Some(flag) = state.black_flag { - draw_texture_ex( - flag, - 1163f32, - 738f32 + offset, - WHITE, - DrawTextureParams { - dest_size: Some(vec2(180f32, 100f32)), - ..Default::default() - }, - ); - } - let (x_off, text) = center_text_offset!( - 135f32, - format!("GAME #{}", &state.game_id.to_string()).as_str(), - 25, - self.textures.font - ); - draw_text_ex( - text.as_str(), - 830f32 + x_off, - 745f32 + offset, - TextParams { - font: self.textures.font, - font_size: 25, - ..Default::default() - }, - ); - let (x_off, text) = - center_text_offset!(124f32, state.start_time.as_str(), 25, self.textures.font); - draw_text_ex( - text.as_str(), - 838f32 + x_off, - 780f32 + offset, - TextParams { - font: self.textures.font, - font_size: 25, - ..Default::default() - }, - ); - let (x_off, text) = - center_text_offset!(110f32, &state.pool.to_string(), 25, self.textures.font); - draw_text_ex( - text.as_str(), - 855f32 + x_off, - 815f32 + offset, - TextParams { - font: self.textures.font, - font_size: 25, - ..Default::default() - }, - ); - let min = state.snapshot.secs_in_period / 60; - let secs = state.snapshot.secs_in_period % 60; - let text = format!( - "{}:{}", - if min < 10 { - format!("0{}", min) - } else { - format!("{}", min) - }, - if secs < 10 { - format!("0{}", secs) - } else { - format!("{}", secs) - } - ); - let (x_off, text) = center_text_offset!(90f32, text.as_str(), 50, self.textures.font); - draw_text_ex( - text.as_str(), - 870f32 + x_off, - 1020f32, - TextParams { - font: self.textures.font, - font_size: 50, - ..Default::default() - }, - ); - draw_text_ex( - "NEXT GAME", - 905f32, - 1044f32, - TextParams { - font: self.textures.font, - font_size: 20, - ..Default::default() - }, - ); - } -} diff --git a/overlay/src/pages/roster/list.rs b/overlay/src/pages/roster/list.rs new file mode 100644 index 00000000..458844d2 --- /dev/null +++ b/overlay/src/pages/roster/list.rs @@ -0,0 +1,382 @@ +use super::{draw_texture_both, fit_text, Interpolate, Justify, PageRenderer}; +use crate::{ + pages::{draw_text_both, draw_text_both_ex, draw_texture_both_ex}, + State, +}; +use coarsetime::Instant; +use macroquad::prelude::*; +use uwh_common::game_snapshot::Color as UwhColor; + +const LIST_NUMBER_BG_WIDTH: f32 = 85f32; +const PLAYER_ROW_HEIGHT: f32 = 58f32; +const TEAM_BANNER_ROSTER_OFFSET: f32 = -650f32; + +pub fn draw(renderer: &mut PageRenderer, state: &State) { + let offset = if state.snapshot.secs_in_period == 181 { + (0f32, TEAM_BANNER_ROSTER_OFFSET).interpolate_linear( + Instant::now() + .duration_since(renderer.animation_register1) + .as_f64() as f32, + ) + } else { + if state.snapshot.secs_in_period != 169 { + renderer.animation_register1 = Instant::now(); + } + (0f32, TEAM_BANNER_ROSTER_OFFSET).interpolate_linear(1f32) + }; + let timeout_alpha_offset = if state.snapshot.secs_in_period == 169 { + renderer.animation_register2 = Instant::now(); + (1f32, 0f32).interpolate_linear( + 2f32 * (Instant::now() + .duration_since(renderer.animation_register1) + .as_f64() as f32), + ) + } else { + 1f32 + }; + if let Some(logo) = renderer.assets.tournament_logo.as_ref() { + if offset <= -210f32 { + let x = (1920f32 - logo.color.width()) / 2f32; + let y = 675f32 - logo.color.height(); + draw_texture_both!( + logo, + x, + y, + Color { + a: timeout_alpha_offset, + ..WHITE + } + ); + } + } + + draw_texture_both!( + renderer.assets.atlantis_logo, + 836f32, + 725f32, + Color { + a: timeout_alpha_offset, + ..WHITE + } + ); + draw_texture_both!( + renderer.assets.bottom, + 822f32, + 977f32, + Color { + a: timeout_alpha_offset, + ..WHITE + } + ); + for (i, player_identifier) in state + .white + .get_players() + .map(|player| (player, UwhColor::White)) + .enumerate() + .chain( + state + .black + .get_players() + .map(|player| (player, UwhColor::Black)) + .enumerate(), + ) + { + if PLAYER_ROW_HEIGHT.mul_add(i as f32, 220f32) + > -1f32 * TEAM_BANNER_ROSTER_OFFSET + offset + 100f32 + { + // display only cards revealed by the bottom bar sliding up + if player_identifier.1 == UwhColor::White { + draw_texture_both!( + renderer.assets.team_white_banner, + if player_identifier.1 == UwhColor::White { + 130f32 + } else { + 1108f32 + }, + PLAYER_ROW_HEIGHT.mul_add(i as f32, 220f32), + Color { + a: timeout_alpha_offset, + ..WHITE + } + ); + } else { + draw_texture_both!( + renderer.assets.team_black_banner, + if player_identifier.1 == UwhColor::White { + 130f32 + } else { + 1108f32 + }, + PLAYER_ROW_HEIGHT.mul_add(i as f32, 220f32), + Color { + a: timeout_alpha_offset, + ..WHITE + } + ); + } + let (_, text) = fit_text( + LIST_NUMBER_BG_WIDTH, + &format!("#{}", player_identifier.0.number.unwrap()), + 35, + renderer.assets.font, + Justify::Left, + ); + draw_text_ex( + &text, + if player_identifier.1 == UwhColor::White { + 140f32 + } else { + 1118f32 + }, + PLAYER_ROW_HEIGHT.mul_add(i as f32, 255f32), + TextParams { + font: renderer.assets.font, + font_size: 35, + color: if player_identifier.1 == UwhColor::White { + Color { + a: timeout_alpha_offset, + ..BLACK + } + } else { + Color { + a: timeout_alpha_offset, + ..WHITE + } + }, + ..Default::default() + }, + ); + draw_text_both_ex!( + player_identifier.0.name.as_str(), + if player_identifier.1 == UwhColor::White { + 265f32 + } else { + 1238f32 + }, + PLAYER_ROW_HEIGHT.mul_add(i as f32, 255f32), + TextParams { + font: renderer.assets.font, + font_size: 35, + color: if player_identifier.1 == UwhColor::White { + Color { + a: timeout_alpha_offset, + ..BLACK + } + } else { + Color { + a: timeout_alpha_offset, + ..WHITE + } + }, + ..Default::default() + }, + TextParams { + font: renderer.assets.font, + font_size: 35, + color: Color { + a: timeout_alpha_offset, + ..WHITE + }, + ..Default::default() + } + ); + } + } + draw_texture_both!( + renderer.assets.team_information, + 130f32, + 710f32 + offset, + Color { + a: timeout_alpha_offset, + ..WHITE + } + ); + let (x_off, text) = fit_text( + 434f32, + &state.black.team_name, + 45, + renderer.assets.font, + Justify::Center, + ); + draw_text_both!( + text.as_str(), + 1350f32 + x_off, + 805f32 + offset, + TextParams { + font: renderer.assets.font, + font_size: 45, + color: Color { + a: timeout_alpha_offset, + ..WHITE + }, + ..Default::default() + } + ); + let (x_off, text) = fit_text( + 440f32, + &state.white.team_name, + 45, + renderer.assets.font, + Justify::Center, + ); + draw_text_both_ex!( + text.as_str(), + 135f32 + x_off, + 805f32 + offset, + TextParams { + font: renderer.assets.font, + font_size: 45, + color: Color { + a: timeout_alpha_offset, + ..BLACK + }, + ..Default::default() + }, + TextParams { + font: renderer.assets.font, + font_size: 45, + color: Color { + a: timeout_alpha_offset, + ..WHITE + }, + ..Default::default() + } + ); + if let Some(flag) = &state.white.flag { + draw_texture_both_ex!( + flag, + 580f32, + 738f32 + offset, + Color { + a: timeout_alpha_offset, + ..WHITE + }, + DrawTextureParams { + dest_size: Some(vec2(180f32, 100f32)), + ..Default::default() + } + ); + } + if let Some(flag) = &state.black.flag { + draw_texture_both_ex!( + flag, + 1163f32, + 738f32 + offset, + Color { + a: timeout_alpha_offset, + ..WHITE + }, + DrawTextureParams { + dest_size: Some(vec2(180f32, 100f32)), + ..Default::default() + } + ); + } + let (x_off, text) = fit_text( + 270f32, + &format!("GAME #{}", &state.game_id.to_string()), + 25, + renderer.assets.font, + Justify::Center, + ); + draw_text_ex( + text.as_str(), + 830f32 + x_off, + 745f32 + offset, + TextParams { + font: renderer.assets.font, + font_size: 25, + color: Color { + a: timeout_alpha_offset, + ..WHITE + }, + ..Default::default() + }, + ); + let (x_off, text) = fit_text( + 248f32, + &state.start_time, + 25, + renderer.assets.font, + Justify::Center, + ); + draw_text_ex( + text.as_str(), + 838f32 + x_off, + 780f32 + offset, + TextParams { + font: renderer.assets.font, + font_size: 25, + color: Color { + a: timeout_alpha_offset, + ..WHITE + }, + ..Default::default() + }, + ); + let (x_off, text) = fit_text( + 220f32, + &state.pool, + 25, + renderer.assets.font, + Justify::Center, + ); + draw_text_ex( + text.as_str(), + 855f32 + x_off, + 815f32 + offset, + TextParams { + font: renderer.assets.font, + font_size: 25, + color: Color { + a: timeout_alpha_offset, + ..WHITE + }, + ..Default::default() + }, + ); + let min = state.snapshot.secs_in_period / 60; + let secs = state.snapshot.secs_in_period % 60; + let text = format!( + "{}:{}", + if min < 10 { + format!("0{min}") + } else { + format!("{min}") + }, + if secs < 10 { + format!("0{secs}") + } else { + format!("{secs}") + } + ); + let (x_off, text) = fit_text(180f32, &text, 50, renderer.assets.font, Justify::Center); + draw_text_ex( + text.as_str(), + 870f32 + x_off, + 1020f32, + TextParams { + font: renderer.assets.font, + font_size: 50, + color: Color { + a: timeout_alpha_offset, + ..WHITE + }, + ..Default::default() + }, + ); + draw_text_ex( + "NEXT GAME", + 905f32, + 1044f32, + TextParams { + font: renderer.assets.font, + font_size: 20, + color: Color { + a: timeout_alpha_offset, + ..WHITE + }, + ..Default::default() + }, + ); +} diff --git a/overlay/src/pages/roster/mod.rs b/overlay/src/pages/roster/mod.rs new file mode 100644 index 00000000..56e81bd9 --- /dev/null +++ b/overlay/src/pages/roster/mod.rs @@ -0,0 +1,14 @@ +use super::{draw_texture_both, fit_text, Interpolate, Justify, PageRenderer}; +use crate::State; +mod list; +mod picture; + +impl PageRenderer { + pub fn roster(&mut self, state: &State) { + if state.snapshot.secs_in_period >= 169 { + list::draw(self, state); + } else if state.snapshot.secs_in_period < 169 { + picture::draw(self, state); + } + } +} diff --git a/overlay/src/pages/roster/picture.rs b/overlay/src/pages/roster/picture.rs new file mode 100644 index 00000000..599f5568 --- /dev/null +++ b/overlay/src/pages/roster/picture.rs @@ -0,0 +1,420 @@ +use super::{draw_texture_both, fit_text, Justify, PageRenderer}; +use crate::{ + pages::{draw_text_both_ex, draw_texture_both_ex, multilinify}, + Member, State, +}; +use coarsetime::Instant; +use macroquad::prelude::*; + +const RPD_GROUP_TIME: f32 = 10f32; +const RPD_NUMBER_BG_WIDTH: f32 = 100f32; +const CARD_WIDTH: f32 = 440f32; + +fn rpd_groups(members: &Vec) -> impl Iterator> { + let mut n4 = members.len() / 4; + let mut n3 = 0; + // 1,2,5 are failing cases for this algorithm + if [1, 2].contains(&members.len()) { + return vec![members.clone()].into_iter(); // 1,2 get returned as is + } else if members.len() == 5 { + return vec![members[..3].to_vec(), members[3..].to_vec()].into_iter(); // partition 5 into 3, 2 + } + match members.len() % 4 { + 3 => { + n3 += 1; + } + 2 => { + n3 += 2; + n4 -= 1; + } + 1 => { + // 1 + n3 += 3; + n4 -= 2; + } + 0 => {} + _ => unreachable!(), + } + let mut divd = Vec::new(); + for i in 0..n4 { + divd.push(members[i * 4..i * 4 + 4].to_vec()); + } + for i in 0..n3 { + divd.push(members[n4 * 4 + i * 3..n4 * 4 + i * 3 + 3].to_vec()); + } + divd.into_iter() +} + +pub fn get_cycle_times(state: &State) -> (f32, f32, f32) { + // Page Transition Points: + // p1: Team White -> Team Black + // p2: Team Black -> Referees + // p3: Referees -> Fade out + let p1 = if !state.white.members.is_empty() + && state + .white + .members + .iter() + .any(|member| member.geared_picture.is_some() || member.picture.is_some()) + { + 4.5 + ((state.white.members.len() + 3) / 4 * RPD_GROUP_TIME as usize) as f32 + } else { + 0f32 + }; + let p2 = p1 + + if !state.black.members.is_empty() + && state + .black + .members + .iter() + .any(|member| member.geared_picture.is_some() || member.picture.is_some()) + { + 4.5 + ((state.black.members.len() + 3) / 4 * RPD_GROUP_TIME as usize) as f32 + } else { + 0f32 + }; + let p3 = p2 + + if !state.referees.is_empty() + && state + .referees + .iter() + .any(|referee| referee.geared_picture.is_some() || referee.picture.is_some()) + { + 4.5 + ((state.referees.len() + 3) / 4 * RPD_GROUP_TIME as usize) as f32 + } else { + 0f32 + }; + (p1, p2, p3) +} + +pub fn draw(renderer: &mut PageRenderer, state: &State) { + let (p1, p2, p3) = get_cycle_times(state); + let (team_name, team_flag, card_repr, team_textures, text_color, (page_start, page_end)) = + match Instant::now() + .duration_since(renderer.animation_register2) + .as_f64() as f32 + { + a if (0f32..=p1).contains(&a) => ( + state.white.team_name.as_str(), + &state.white.flag, + &state.white.members, + &renderer.assets.white_rpd, + BLACK, + (0f32, p1), + ), + a if (p1..=p2).contains(&a) => ( + state.black.team_name.as_str(), + &state.black.flag, + &state.black.members, + &renderer.assets.black_rpd, + WHITE, + (p1, p2), + ), + a if (p2..=p3).contains(&a) => { + renderer.animation_register0 = Instant::now(); + ( + "REFEREES", + &None, + &state.referees, + &renderer.assets.red_rpd, + YELLOW, + (p2, p3), + ) + } + _ => { + renderer.pre_game_display(state); + return; + } // time after rpd display + }; + + let since_page_start = Instant::now() + .duration_since(renderer.animation_register2) + .as_f64() as f32 + - page_start; + let to_page_end = page_end + - Instant::now() + .duration_since(renderer.animation_register2) + .as_f64() as f32; + let top_banner_alpha = if to_page_end <= 1.5 { + 2f32.mul_add(to_page_end, -2f32) + } else { + 2f32 * (since_page_start - 1.5f32) + }; + draw_texture_both!( + team_textures.team_name_bg, + 249f32, + 32f32, + Color { + a: top_banner_alpha, + ..WHITE + } + ); + if let Some(flag) = team_flag { + let f_width = flag.color.width() * (170f32 / flag.color.height()); + draw_texture_both_ex!( + flag, + 484f32 - f_width / 2f32, + 57f32, + Color { + a: top_banner_alpha, + ..WHITE + }, + DrawTextureParams { + dest_size: Some(vec2(f_width, 170f32)), + ..Default::default() + } + ); + } + let (x_off, text) = fit_text( + if team_flag.is_some() { 900f32 } else { 1200f32 }, + team_name, + 100, + renderer.assets.font, + Justify::Center, + ); + draw_text_both_ex!( + text.as_str(), + if team_flag.is_some() { 680f32 } else { 361f32 } + x_off, + 180f32, + TextParams { + font: renderer.assets.font, + font_size: 100, + color: Color { + a: top_banner_alpha, + ..text_color + }, + ..Default::default() + }, + TextParams { + font: renderer.assets.font, + font_size: 100, + color: Color { + a: top_banner_alpha, + ..WHITE + }, + ..Default::default() + } + ); + if let Some(cards) = + rpd_groups(card_repr).nth((since_page_start - 3f32).div_euclid(RPD_GROUP_TIME) as usize) + { + let since_rpdgroup_start = (since_page_start - 3f32) + .div_euclid(RPD_GROUP_TIME) + .mul_add(-RPD_GROUP_TIME, since_page_start) + - 3f32; + let to_rpdgroup_end = RPD_GROUP_TIME - since_rpdgroup_start; + let rpdgroup_alpha = if since_page_start <= 3f32 { + 0f32 + } else if to_rpdgroup_end <= 0.5 { + 2f32 * to_rpdgroup_end + } else { + 2f32 * since_rpdgroup_start + }; + let rpd_picture_alpha = (RPD_GROUP_TIME / 2f32 - since_rpdgroup_start) * 2f32; + let no_cards = cards.len() as f32; + let margin = CARD_WIDTH.mul_add(-no_cards, 1920f32) / (no_cards + 1f32); + cards.iter().enumerate().for_each( + |( + i, + Member { + name, + role, + number, + geared_picture, + picture, + }, + )| { + draw_texture_both!( + team_textures.frame_bg, + (i as f32).mul_add(CARD_WIDTH + margin, margin) + 12f32, + 407f32, + Color { + a: rpdgroup_alpha, + ..WHITE + } + ); + draw_texture_both!( + renderer.assets.frame_rpd, + (i as f32).mul_add(CARD_WIDTH + margin, margin), + 395f32, + Color { + a: rpdgroup_alpha, + ..WHITE + } + ); + if let (Some(picture), Some(geared_picture)) = (picture, geared_picture) { + draw_texture_both_ex!( + *picture, + (i as f32).mul_add(CARD_WIDTH + margin, margin) + 12f32, + 407f32, + Color { + a: rpdgroup_alpha.min(rpd_picture_alpha), + ..WHITE + }, + DrawTextureParams { + dest_size: Some(vec2(416f32, 416f32)), + ..Default::default() + } + ); + draw_texture_both_ex!( + *geared_picture, + (i as f32).mul_add(CARD_WIDTH + margin, margin) + 12f32, + 407f32, + Color { + a: rpdgroup_alpha.min(-rpd_picture_alpha), + ..WHITE + }, + DrawTextureParams { + dest_size: Some(vec2(416f32, 416f32)), + ..Default::default() + } + ); + } else if let Some(picture) = picture { + draw_texture_both_ex!( + *picture, + (i as f32).mul_add(CARD_WIDTH + margin, margin) + 12f32, + 407f32, + Color { + a: rpdgroup_alpha, + ..WHITE + }, + DrawTextureParams { + dest_size: Some(vec2(416f32, 416f32)), + ..Default::default() + } + ); + } else if let Some(picture) = geared_picture { + draw_texture_both_ex!( + *picture, + (i as f32).mul_add(CARD_WIDTH + margin, margin) + 12f32, + 407f32, + Color { + a: rpdgroup_alpha, + ..WHITE + }, + DrawTextureParams { + dest_size: Some(vec2(416f32, 416f32)), + ..Default::default() + } + ); + } + if let Some(number) = number { + draw_texture_both!( + renderer.assets.number_bg_rpd, + (i as f32).mul_add(CARD_WIDTH + margin, margin), + 395f32, + Color { + a: rpdgroup_alpha, + ..WHITE + } + ); + let (x_off, text) = fit_text( + RPD_NUMBER_BG_WIDTH, + &format!("#{number}"), + 40, + renderer.assets.font, + Justify::Left, + ); + draw_text_ex( + text.as_str(), + (i as f32).mul_add(CARD_WIDTH + margin, margin) + x_off + 15f32, + 445f32, + TextParams { + font: renderer.assets.font, + font_size: 40, + color: Color { + a: rpdgroup_alpha, + ..text_color + }, + ..Default::default() + }, + ); + } + if let Some(role) = role { + draw_texture_both!( + team_textures.team_member_role_bg, + (i as f32).mul_add(CARD_WIDTH + margin, margin), + 340f32, + Color { + a: rpdgroup_alpha, + ..WHITE + } + ); + let (x_off, text) = + fit_text(400f32, role, 40, renderer.assets.font, Justify::Center); + draw_text_both_ex!( + &text, + (i as f32).mul_add(CARD_WIDTH + margin, margin) + 22f32 + x_off, + 377f32, + TextParams { + font: renderer.assets.font, + font_size: 40, + color: Color { + a: rpdgroup_alpha, + ..text_color + }, + ..Default::default() + }, + TextParams { + font: renderer.assets.font, + font_size: 40, + color: Color { + a: rpdgroup_alpha, + ..WHITE + }, + ..Default::default() + } + ); + } + + let lines = multilinify(name, 400f32, Some(renderer.assets.font), 33); + let text_box_texture = match lines.len() { + 1 => &team_textures.single_line_name_bg, + 2 => &team_textures.double_line_name_bg, + _ => &team_textures.triple_line_name_bg, + }; + draw_texture_both!( + text_box_texture, + (i as f32).mul_add(CARD_WIDTH + margin, margin), + 847f32, + Color { + a: rpdgroup_alpha, + ..WHITE + } + ); + let text_height = measure_text("Q", Some(renderer.assets.font), 33, 1.0).height; + let v_margin = text_height.mul_add( + -(lines.len().min(3) as f32), + text_box_texture.color.height(), + ) / (lines.len().min(3) as f32 + 1f32); + for (j, line) in lines.iter().take(3).enumerate() { + let (x_off, text) = + fit_text(400f32, line, 33, renderer.assets.font, Justify::Center); + draw_text_both_ex!( + text.as_str(), + (CARD_WIDTH + margin).mul_add(i as f32, margin + 12f32 + x_off), + (v_margin + text_height).mul_add(j as f32 + 1f32, 847f32), + TextParams { + font: renderer.assets.font, + font_size: 33, + color: Color { + a: rpdgroup_alpha, + ..text_color + }, + ..Default::default() + }, + TextParams { + font: renderer.assets.font, + font_size: 33, + color: Color { + a: rpdgroup_alpha, + ..WHITE + }, + ..Default::default() + } + ); + } + }, + ); + } +} diff --git a/overlay/test_server/main.py b/overlay/test_server/main.py new file mode 100644 index 00000000..a17a3375 --- /dev/null +++ b/overlay/test_server/main.py @@ -0,0 +1,318 @@ +from flask import Flask, jsonify, request + +app = Flask(__name__) + + +@app.route("/api/admin/get-event-team") +def team(): + tournament = request.args.get("legacyEventId") + team = request.args.get("legacyTeamId") + if team == "13": + d = { + "logoUrl": "https://uwhscores.blob.core.windows.net/images/408c31e8-3351-456e-a0b9-9fb4de18ddc7_main.jpg", + "name": "Door County Salty Sturgeons", + "photos": [], + "roster": [ + { + "capNumber": 23, + "photos": { + "darkGear": "https://underwaterrugby.blob.core.windows.net/images/43226f46-5f89-4162-9b94-ecbadf6b4be0_main.png", + "lightGear": None, + "uniform": "https://underwaterrugby.blob.core.windows.net/images/6667b743-0413-4067-b418-526c896e3422_main.png", + }, + "roles": ["Player", "Coach"], + "rosterName": "Susan Banks", + }, + { + "capNumber": 22, + "photos": {"darkGear": None, "lightGear": None, "uniform": None}, + "roles": ["Player", "goo"], + "rosterName": "Trisha Filar", + }, + { + "capNumber": 88, + "photos": { + "darkGear": "https://underwaterrugby.blob.core.windows.net/images/43226f46-5f89-4162-9b94-ecbadf6b4be0_main.png", + "lightGear": None, + "uniform": None, + }, + "roles": ["Player"], + "rosterName": "David Foulds", + }, + { + "capNumber": 27, + "photos": { + "darkGear": "https://underwaterrugby.blob.core.windows.net/images/43226f46-5f89-4162-9b94-ecbadf6b4be0_main.png", + "lightGear": None, + "uniform": "https://underwaterrugby.blob.core.windows.net/images/6667b743-0413-4067-b418-526c896e3422_main.png", + }, + "roles": ["Player"], + "rosterName": "Peter Covach", + }, + { + "capNumber": 60, + "photos": { + "darkGear": "https://underwaterrugby.blob.core.windows.net/images/43226f46-5f89-4162-9b94-ecbadf6b4be0_main.png", + "lightGear": None, + "uniform": None, + }, + "roles": ["Player"], + "rosterName": "Matt Brown", + }, + { + "capNumber": 7, + "photos": { + "darkGear": None, + "lightGear": None, + "uniform": "https://underwaterrugby.blob.core.windows.net/images/6667b743-0413-4067-b418-526c896e3422_main.png", + }, + "roles": ["Player"], + "rosterName": "Richard Heller", + }, + { + "capNumber": 99, + "photos": { + "darkGear": None, + "lightGear": None, + "uniform": "https://underwaterrugby.blob.core.windows.net/images/6667b743-0413-4067-b418-526c896e3422_main.png", + }, + "roles": ["Player"], + "rosterName": "Craig Hutchinson", + }, + { + "capNumber": 39, + "photos": { + "darkGear": None, + "lightGear": None, + "uniform": "https://underwaterrugby.blob.core.windows.net/images/6667b743-0413-4067-b418-526c896e3422_main.png", + }, + "roles": ["Player"], + "rosterName": "Sean Linnan", + }, + { + "capNumber": 11, + "photos": {"darkGear": None, "lightGear": None, "uniform": None}, + "roles": ["Player"], + "rosterName": "Steven Shephard", + }, + { + "capNumber": 29, + "photos": {"darkGear": None, "lightGear": None, "uniform": None}, + "roles": ["Player"], + "rosterName": "Justin Therrian", + }, + { + "capNumber": 50, + "photos": {"darkGear": None, "lightGear": None, "uniform": None}, + "roles": ["Player"], + "rosterName": "Albert Weber III", + }, + ], + } + else: + d = { + "logoUrl": "https://uwhscores.blob.core.windows.net/images/d7a68495-f2b5-4403-8788-ac9246f03867_main.jpg", + "name": "Team Oregon", + "photos": [], + "roster": [ + { + "capNumber": 8, + "photos": {"darkGear": None, "lightGear": None, "uniform": None}, + "roles": ["Player", "Alpha"], + "rosterName": None, + }, + { + "capNumber": 2, + "photos": {"darkGear": None, "lightGear": None, "uniform": None}, + "roles": ["Player"], + "rosterName": None, + }, + { + "capNumber": 7, + "photos": {"darkGear": None, "lightGear": None, "uniform": None}, + "roles": ["Player"], + "rosterName": None, + }, + { + "capNumber": 4, + "photos": {"darkGear": None, "lightGear": None, "uniform": None}, + "roles": ["Player"], + "rosterName": None, + }, + { + "capNumber": 13, + "photos": {"darkGear": None, "lightGear": None, "uniform": None}, + "roles": ["Player"], + "rosterName": "Jenny Mohn", + }, + { + "capNumber": 14, + "photos": {"darkGear": None, "lightGear": None, "uniform": None}, + "roles": ["Player"], + "rosterName": None, + }, + { + "capNumber": 9, + "photos": {"darkGear": None, "lightGear": None, "uniform": None}, + "roles": ["Player"], + "rosterName": None, + }, + { + "capNumber": 6, + "photos": {"darkGear": None, "lightGear": None, "uniform": None}, + "roles": ["Player"], + "rosterName": None, + }, + { + "capNumber": 11, + "photos": {"darkGear": None, "lightGear": None, "uniform": None}, + "roles": ["Player"], + "rosterName": None, + }, + { + "capNumber": 12, + "photos": {"darkGear": None, "lightGear": None, "uniform": None}, + "roles": ["Player"], + "rosterName": None, + }, + { + "capNumber": 3, + "photos": {"darkGear": None, "lightGear": None, "uniform": None}, + "roles": ["Player"], + "rosterName": None, + }, + { + "capNumber": 1, + "photos": {"darkGear": None, "lightGear": None, "uniform": None}, + "roles": ["Player"], + "rosterName": "Kat Kurtz", + }, + ], + } + return jsonify(d) + + +@app.route("/api/v1/tournaments/0/games/1") +def hello(): + d = { + "game": { + "black": "Team Oregon", + "black_id": 13, + "day": "Sat-8th", + "description": None, + "division": "B", + "forfeit": None, + "game_type": "RR-B", + "gid": 1, + "note_b": None, + "note_w": None, + "pod": "B", + "pool": "1", + "score_b": 7, + "score_w": 1, + "start_time": "2023-04-08T09:00:00", + "tid": 35, + "timing_rules": { + "game_timeouts": {"allowed": 0, "duration": 30, "per_half": True}, + "half_duration": 540, + "half_time_duration": 120, + "max_sudden_death_duration": None, + "min_game_break": 240, + "overtime_allowed": False, + "overtime_duration": 300, + "pre_overtime_break": 120, + "pre_sudden_death_break": 30, + "sudden_death_allowed": False, + }, + "referees": [ + { + "name": "joe", + "number": 0, + "picture_url": "https://underwaterrugby.blob.core.windows.net/images/43226f46-5f89-4162-9b94-ecbadf6b4be0_main.png", + "geared_picture_url": "", + "role": "Chief ref", + }, + { + "name": "joke", + "number": None, + "picture_url": "https://underwaterrugby.blob.core.windows.net/images/43226f46-5f89-4162-9b94-ecbadf6b4be0_main.png", + "geared_picture_url": "", + "role": None, + }, + { + "name": "jock", + "number": None, + "picture_url": "", + "geared_picture_url": "", + "role": None, + }, + ], + "white": "Door County Salty Sturgeons", + "white_id": 10, + "sponsor_logo": "https://w7.pngwing.com/pngs/381/29/png-transparent-logo-graphic-design-company-company-logo-angle-building-company.png", + } + } + return jsonify(d) + +@app.route("/api/v1/tournaments/42/games/1") +def hellos(): + d = { + "game": { + "black": "Team", + "black_id": 13, + "day": "Sat-8th", + "description": None, + "division": "B", + "forfeit": None, + "game_type": "RR-B", + "gid": 1, + "note_b": None, + "note_w": None, + "pod": "B", + "pool": "FART", + "score_b": 7, + "score_w": 1, + "start_time": "2023-04-08T09:00:00", + "tid": 35, + "timing_rules": { + "game_timeouts": {"allowed": 0, "duration": 30, "per_half": True}, + "half_duration": 540, + "half_time_duration": 120, + "max_sudden_death_duration": None, + "min_game_break": 240, + "overtime_allowed": False, + "overtime_duration": 300, + "pre_overtime_break": 120, + "pre_sudden_death_break": 30, + "sudden_death_allowed": False, + }, + "referees": [ + { + "name": "joe", + "number": 0, + "picture_url": "https://underwaterrugby.blob.core.windows.net/images/43226f46-5f89-4162-9b94-ecbadf6b4be0_main.png", + "geared_picture_url": "", + "role": "Chief ref", + }, + { + "name": "joke", + "number": None, + "picture_url": "https://underwaterrugby.blob.core.windows.net/images/43226f46-5f89-4162-9b94-ecbadf6b4be0_main.png", + "geared_picture_url": "", + "role": None, + }, + { + "name": "jock", + "number": None, + "picture_url": "", + "geared_picture_url": "", + "role": None, + }, + ], + "white": "FAZZZ", + "white_id": 10, + "sponsor_logo": "https://w7.pngwing.com/pngs/381/29/png-transparent-logo-graphic-design-company-company-logo-angle-building-company.png", + } + } + return jsonify(d) +app.run()