From 3c7796f066647090ed8436778bdfe25f11844c05 Mon Sep 17 00:00:00 2001 From: LLLL Colonq Date: Tue, 4 Aug 2026 04:35:44 -0400 Subject: Prepare for upload --- 2026/games/asrael_io/source/Cargo.lock | 2622 ++++++++++++++++++++ 2026/games/asrael_io/source/Cargo.toml | 38 + 2026/games/asrael_io/source/Trunk.toml | 2 + .../games/asrael_io/source/assets/sprites.aseprite | Bin 0 -> 1260 bytes 2026/games/asrael_io/source/index.html | 53 + 2026/games/asrael_io/source/index.scss | 18 + 2026/games/asrael_io/source/src/anim.rs | 42 + 2026/games/asrael_io/source/src/bullet.rs | 118 + 2026/games/asrael_io/source/src/color.rs | 66 + 2026/games/asrael_io/source/src/enemy.rs | 174 ++ 2026/games/asrael_io/source/src/gfx.rs | 113 + 2026/games/asrael_io/source/src/main.rs | 642 +++++ 2026/games/asrael_io/source/src/math.rs | 13 + 2026/games/asrael_io/source/src/player.rs | 96 + 2026/games/asrael_io/source/src/rng.rs | 35 + 2026/games/asrael_io/source/src/sfx.rs | 302 +++ 2026/games/asrael_io/source/src/sprite.rs | 89 + 2026/games/asrael_io/source/src/starfield.rs | 59 + 18 files changed, 4482 insertions(+) create mode 100644 2026/games/asrael_io/source/Cargo.lock create mode 100644 2026/games/asrael_io/source/Cargo.toml create mode 100644 2026/games/asrael_io/source/Trunk.toml create mode 100644 2026/games/asrael_io/source/assets/sprites.aseprite create mode 100644 2026/games/asrael_io/source/index.html create mode 100644 2026/games/asrael_io/source/index.scss create mode 100644 2026/games/asrael_io/source/src/anim.rs create mode 100644 2026/games/asrael_io/source/src/bullet.rs create mode 100644 2026/games/asrael_io/source/src/color.rs create mode 100644 2026/games/asrael_io/source/src/enemy.rs create mode 100644 2026/games/asrael_io/source/src/gfx.rs create mode 100644 2026/games/asrael_io/source/src/main.rs create mode 100644 2026/games/asrael_io/source/src/math.rs create mode 100644 2026/games/asrael_io/source/src/player.rs create mode 100644 2026/games/asrael_io/source/src/rng.rs create mode 100644 2026/games/asrael_io/source/src/sfx.rs create mode 100644 2026/games/asrael_io/source/src/sprite.rs create mode 100644 2026/games/asrael_io/source/src/starfield.rs (limited to '2026/games/asrael_io/source') diff --git a/2026/games/asrael_io/source/Cargo.lock b/2026/games/asrael_io/source/Cargo.lock new file mode 100644 index 0000000..d2740d4 --- /dev/null +++ b/2026/games/asrael_io/source/Cargo.lock @@ -0,0 +1,2622 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ab_glyph" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01c0457472c38ea5bd1c3b5ada5e368271cb550be7a4ca4a0b4634e9913f6cc2" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser", +] + +[[package]] +name = "ab_glyph_rasterizer" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "alsa" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812947049edcd670a82cd5c73c3661d2e58468577ba8489de58e1a73c04cbd5d" +dependencies = [ + "alsa-sys", + "bitflags 2.13.0", + "cfg-if", + "libc", +] + +[[package]] +name = "alsa-sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad7569085a265dd3f607ebecce7458eaab2132a84393534c95b18dcbc3f31e04" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "android-activity" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f2a1bb052857d5dd49572219344a7332b31b76405648eabac5bc68978251bcd" +dependencies = [ + "android-properties", + "bitflags 2.13.0", + "cc", + "jni", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "num_enum", + "thiserror 2.0.18", +] + +[[package]] +name = "android-properties" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" + +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "as-raw-xcb-connection" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" + +[[package]] +name = "aseprite-io" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "303c59908b2ef2b9fafa3636f803b59c4a7f67b4d5d06a4d8185051cb019299a" +dependencies = [ + "flate2", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "az" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" + +[[package]] +name = "bit_field" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "block2" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +dependencies = [ + "objc2 0.5.2", +] + +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2 0.6.4", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "calloop" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" +dependencies = [ + "bitflags 2.13.0", + "log", + "polling", + "rustix 0.38.44", + "slab", + "thiserror 1.0.69", +] + +[[package]] +name = "calloop-wayland-source" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20" +dependencies = [ + "calloop", + "rustix 0.38.44", + "wayland-backend", + "wayland-client", +] + +[[package]] +name = "cc" +version = "1.2.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dad887fd958be91b5098c0248def011f4523ab786cd411be668777e55063501f" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "cj2k26" +version = "0.1.0" +dependencies = [ + "aseprite-io", + "console_error_panic_hook", + "cpal", + "embedded-graphics", + "env_logger", + "gilrs", + "glam", + "log", + "resid-rs", + "softbuffer", + "wasm-bindgen", + "web-time", + "winit", +] + +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "console_error_panic_hook" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" +dependencies = [ + "cfg-if", + "wasm-bindgen", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + +[[package]] +name = "coreaudio-rs" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5d7dca3ebcf65a035582c9ad4385371a9d9ee6537474d2a278f4e1e475bb58" +dependencies = [ + "bitflags 2.13.0", + "libc", + "objc2-audio-toolbox", + "objc2-core-audio", + "objc2-core-audio-types", + "objc2-core-foundation", +] + +[[package]] +name = "cpal" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f77b11176c37874be37e8d691c946e31b2b8c357abce9526f6a99eb469e1028" +dependencies = [ + "alsa", + "block2 0.6.2", + "coreaudio-rs", + "dasp_sample", + "jni", + "js-sys", + "libc", + "mach2", + "ndk", + "ndk-context", + "num-derive", + "num-traits", + "objc2 0.6.4", + "objc2-audio-toolbox", + "objc2-avf-audio", + "objc2-core-audio", + "objc2-core-audio-types", + "objc2-core-foundation", + "objc2-foundation 0.3.2", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows", + "windows-core", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "ctor" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83cf0d42651b16c6dfe68685716d18480d18a9c39c62d76e8cf3eb6ed5d8bcbf" +dependencies = [ + "dtor", +] + +[[package]] +name = "cursor-icon" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" + +[[package]] +name = "dasp_sample" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f" + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags 2.13.0", + "objc2 0.6.4", +] + +[[package]] +name = "dlib" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab8ecd87370524b461f8557c119c405552c396ed91fc0a8eec68679eab26f94a" +dependencies = [ + "libloading", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "dpi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" + +[[package]] +name = "drm" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80bc8c5c6c2941f70a55c15f8d9f00f9710ebda3ffda98075f996a0e6c92756f" +dependencies = [ + "bitflags 2.13.0", + "bytemuck", + "drm-ffi", + "drm-fourcc", + "libc", + "rustix 0.38.44", +] + +[[package]] +name = "drm-ffi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51a91c9b32ac4e8105dec255e849e0d66e27d7c34d184364fb93e469db08f690" +dependencies = [ + "drm-sys", + "rustix 1.1.4", +] + +[[package]] +name = "drm-fourcc" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4" + +[[package]] +name = "drm-sys" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8e1361066d91f5ffccff060a3c3be9c3ecde15be2959c1937595f7a82a9f8" +dependencies = [ + "libc", + "linux-raw-sys 0.9.4", +] + +[[package]] +name = "dtor" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edf234dd1594d6dd434a8fb8cada51ddbbc593e40e4a01556a0b31c62da2775b" + +[[package]] +name = "embedded-graphics" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e8da660bb0c829b34a56a965490597f82a55e767b91f9543be80ce8ccb416fe" +dependencies = [ + "az", + "byteorder", + "embedded-graphics-core", + "float-cmp", + "micromath", +] + +[[package]] +name = "embedded-graphics-core" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95743bef3ff70fcba3930246c4e6872882bbea0dcc6da2ca860112e0cd4bd09f" +dependencies = [ + "az", + "byteorder", +] + +[[package]] +name = "env_filter" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e90c2accc4b07a8456ea0debdc2e7587bdd890680d71173a15d4ae604f6eef" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0621c04f2196ac3f488dd583365b9c09be011a4ab8b9f37248ffcc8f6198b56a" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "jiff", + "log", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +dependencies = [ + "num-traits", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "gethostname" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" +dependencies = [ + "rustix 1.1.4", + "windows-link", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "gilrs" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "902fb00d3f6398e635be22e5c837b303c501835cca7ac11a47bba138f7aafdd8" +dependencies = [ + "fnv", + "gilrs-core", + "log", + "uuid", + "vec_map", +] + +[[package]] +name = "gilrs-core" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc7f0ce6237abcc0523f2a5502b1e3fe5802daaae47ac14e166fe49551301ea9" +dependencies = [ + "inotify", + "js-sys", + "libc", + "libudev-sys", + "log", + "nix", + "objc2-core-foundation", + "objc2-io-kit", + "uuid", + "vec_map", + "wasm-bindgen", + "web-sys", + "windows", +] + +[[package]] +name = "glam" +version = "0.33.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "898f5a568a84989b6c0f8caa50a93074b97dbdc58fc6d9543157bb4562758933" + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "inotify" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "533e68a5842e734946fe159fb03fc9bbbb254f590dd0d8ad321ae5ff7beca2c1" +dependencies = [ + "bitflags 2.13.0", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "jiff" +version = "0.2.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4603d3033e49e2b0e31229fcab20a5d40089c607d975cd9c80551dc69eed9102" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", +] + +[[package]] +name = "jiff-static" +version = "0.2.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "782d32378dddf207193ac91cefb848ad41abb58195c95168e1291227a0832b47" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "jni" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +dependencies = [ + "cfg-if", + "combine", + "jni-macros", + "jni-sys 0.4.1", + "log", + "simd_cesu8", + "thiserror 2.0.18", + "walkdir", + "windows-link", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn", +] + +[[package]] +name = "jni-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" +dependencies = [ + "jni-sys 0.4.1", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "libredox" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3" +dependencies = [ + "bitflags 2.13.0", + "libc", + "plain", + "redox_syscall 0.8.1", +] + +[[package]] +name = "libudev-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "log" +version = "0.4.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" + +[[package]] +name = "mach2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dae608c151f68243f2b000364e1f7b186d9c29845f7d2d85bd31b9ad77ad552b" + +[[package]] +name = "memchr" +version = "2.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" + +[[package]] +name = "memmap2" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" +dependencies = [ + "libc", +] + +[[package]] +name = "micromath" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c8dda44ff03a2f238717214da50f65d5a53b45cd213a7370424ffdb6fae815" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "ndk" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" +dependencies = [ + "bitflags 2.13.0", + "jni-sys 0.3.1", + "log", + "ndk-sys", + "num_enum", + "raw-window-handle", + "thiserror 1.0.69", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys 0.3.1", +] + +[[package]] +name = "nix" +version = "0.31.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" +dependencies = [ + "bitflags 2.13.0", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "objc-sys" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" + +[[package]] +name = "objc2" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" +dependencies = [ + "objc-sys", + "objc2-encode", +] + +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-app-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" +dependencies = [ + "bitflags 2.13.0", + "block2 0.5.1", + "libc", + "objc2 0.5.2", + "objc2-core-data", + "objc2-core-image", + "objc2-foundation 0.2.2", + "objc2-quartz-core 0.2.2", +] + +[[package]] +name = "objc2-audio-toolbox" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6948501a91121d6399b79abaa33a8aa4ea7857fe019f341b8c23ad6e81b79b08" +dependencies = [ + "bitflags 2.13.0", + "libc", + "objc2 0.6.4", + "objc2-core-audio", + "objc2-core-audio-types", + "objc2-core-foundation", + "objc2-foundation 0.3.2", +] + +[[package]] +name = "objc2-avf-audio" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13a380031deed8e99db00065c45937da434ca987c034e13b87e4441f9e4090be" +dependencies = [ + "bitflags 2.13.0", + "objc2 0.6.4", + "objc2-foundation 0.3.2", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" +dependencies = [ + "bitflags 2.13.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-core-location", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-contacts" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" +dependencies = [ + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-core-audio" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1eebcea8b0dbff5f7c8504f3107c68fc061a3eb44932051c8cf8a68d969c3b2" +dependencies = [ + "dispatch2", + "objc2 0.6.4", + "objc2-core-audio-types", + "objc2-core-foundation", + "objc2-foundation 0.3.2", +] + +[[package]] +name = "objc2-core-audio-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a89f2ec274a0cf4a32642b2991e8b351a404d290da87bb6a9a9d8632490bd1c" +dependencies = [ + "bitflags 2.13.0", + "objc2 0.6.4", +] + +[[package]] +name = "objc2-core-data" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" +dependencies = [ + "bitflags 2.13.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags 2.13.0", + "block2 0.6.2", + "dispatch2", + "libc", + "objc2 0.6.4", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags 2.13.0", + "dispatch2", + "objc2 0.6.4", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-core-image" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" +dependencies = [ + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", + "objc2-metal", +] + +[[package]] +name = "objc2-core-location" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" +dependencies = [ + "block2 0.5.1", + "objc2 0.5.2", + "objc2-contacts", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" +dependencies = [ + "bitflags 2.13.0", + "block2 0.5.1", + "dispatch", + "libc", + "objc2 0.5.2", +] + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags 2.13.0", + "block2 0.6.2", + "libc", + "objc2 0.6.4", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15" +dependencies = [ + "bitflags 2.13.0", + "libc", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" +dependencies = [ + "bitflags 2.13.0", + "objc2 0.6.4", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-link-presentation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" +dependencies = [ + "block2 0.5.1", + "objc2 0.5.2", + "objc2-app-kit", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-metal" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" +dependencies = [ + "bitflags 2.13.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" +dependencies = [ + "bitflags 2.13.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", + "objc2-metal", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" +dependencies = [ + "bitflags 2.13.0", + "objc2 0.6.4", + "objc2-core-foundation", + "objc2-foundation 0.3.2", +] + +[[package]] +name = "objc2-symbols" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" +dependencies = [ + "objc2 0.5.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" +dependencies = [ + "bitflags 2.13.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-image", + "objc2-core-location", + "objc2-foundation 0.2.2", + "objc2-link-presentation", + "objc2-quartz-core 0.2.2", + "objc2-symbols", + "objc2-uniform-type-identifiers", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-uniform-type-identifiers" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" +dependencies = [ + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" +dependencies = [ + "bitflags 2.13.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-core-location", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "orbclient" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5df339f526ea9a60e371768d50efc2f2508c7203290731565d1f7a6f71d21747" +dependencies = [ + "libc", + "libredox", +] + +[[package]] +name = "owned_ttf_parser" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36820e9051aca1014ddc75770aab4d68bc1e9e632f0f5627c4086bc216fb583b" +dependencies = [ + "ttf-parser", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "plain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" + +[[package]] +name = "polling" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix 1.1.4", + "windows-sys 0.61.2", +] + +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "portable-atomic-util" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quick-xml" +version = "0.39.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdcc8dd4e2f670d309a5f0e83fe36dfdc05af317008fea29144da1a2ac858e5e" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.13.0", +] + +[[package]] +name = "redox_syscall" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b44b894f2a6e36457d665d1e08c3866add6ed5e70050c1b4ba8a8ddedb02ce7" +dependencies = [ + "bitflags 2.13.0", +] + +[[package]] +name = "regex" +version = "1.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" + +[[package]] +name = "resid-rs" +version = "1.1.1" +source = "git+https://github.com/asrael/resid-rs#d8c05733e22053f951dbf96ef63bc58b3e02c69a" +dependencies = [ + "bit_field", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags 2.13.0", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags 2.13.0", + "errno", + "libc", + "linux-raw-sys 0.12.1", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "sctk-adwaita" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6277f0217056f77f1d8f49f2950ac6c278c0d607c45f5ee99328d792ede24ec" +dependencies = [ + "ab_glyph", + "log", + "memmap2", + "smithay-client-toolkit", + "tiny-skia", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "simd-adler32" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" + +[[package]] +name = "simd_cesu8" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" +dependencies = [ + "rustc_version", + "simdutf8", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" + +[[package]] +name = "smithay-client-toolkit" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" +dependencies = [ + "bitflags 2.13.0", + "calloop", + "calloop-wayland-source", + "cursor-icon", + "libc", + "log", + "memmap2", + "rustix 0.38.44", + "thiserror 1.0.69", + "wayland-backend", + "wayland-client", + "wayland-csd-frame", + "wayland-cursor", + "wayland-protocols", + "wayland-protocols-wlr", + "wayland-scanner", + "xkeysym", +] + +[[package]] +name = "smol_str" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" +dependencies = [ + "serde", +] + +[[package]] +name = "softbuffer" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aac18da81ebbf05109ab275b157c22a653bb3c12cf884450179942f81bcbf6c3" +dependencies = [ + "as-raw-xcb-connection", + "bytemuck", + "drm", + "fastrand", + "js-sys", + "memmap2", + "ndk", + "objc2 0.6.4", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation 0.3.2", + "objc2-quartz-core 0.3.2", + "raw-window-handle", + "redox_syscall 0.5.18", + "rustix 1.1.4", + "tiny-xlib", + "tracing", + "wasm-bindgen", + "wayland-backend", + "wayland-client", + "wayland-sys", + "web-sys", + "windows-sys 0.61.2", + "x11rb", +] + +[[package]] +name = "strict-num" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" + +[[package]] +name = "syn" +version = "2.0.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tiny-skia" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "log", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + +[[package]] +name = "tiny-xlib" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a90a0ca3ee6a69f2ad28fd11621a4c3f03b371f366be500b64df260c4ffbafb4" +dependencies = [ + "as-raw-xcb-connection", + "ctor", + "libloading", + "pkg-config", + "tracing", +] + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.25.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" +dependencies = [ + "indexmap", + "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" + +[[package]] +name = "ttf-parser" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.23.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "144d6b123cef80b301b8f72a9e2ca4370ddec21950d0a103dd22c437006d2db7" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasip2" +version = "1.0.4+wasi-0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "503b14d284f2c8dac03b819967e155ea753f573586193b2b2c95990cb5d69280" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.125" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wayland-backend" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2857dd20b54e916ec7253b3d6b4d5c4d7d4ca2c33c2e11c6c76a99bd8744755d" +dependencies = [ + "cc", + "downcast-rs", + "rustix 1.1.4", + "scoped-tls", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645c7c96bb74690c3189b5c9cb4ca1627062bb23693a4fad9d8c3de958260144" +dependencies = [ + "bitflags 2.13.0", + "rustix 1.1.4", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-csd-frame" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" +dependencies = [ + "bitflags 2.13.0", + "cursor-icon", + "wayland-backend", +] + +[[package]] +name = "wayland-cursor" +version = "0.31.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a52d18780be9b1314328a3de5f930b73d2200112e3849ca6cb11822793fb34d" +dependencies = [ + "rustix 1.1.4", + "wayland-client", + "xcursor", +] + +[[package]] +name = "wayland-protocols" +version = "0.32.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "563a85523cade2429938e790815fd7319062103b9f4a2dc806e9b53b95982d8f" +dependencies = [ + "bitflags 2.13.0", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-plasma" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b6d8cf1eb2c1c31ed1f5643c88a6e53538129d4af80030c8cabd1f9fa884d91" +dependencies = [ + "bitflags 2.13.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234" +dependencies = [ + "bitflags 2.13.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c324a910fd86ebdc364a3e61ec1f11737d3b1d6c273c0239ee8ff4bc0d24b4a" +dependencies = [ + "proc-macro2", + "quick-xml", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be" +dependencies = [ + "dlib", + "log", + "once_cell", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6430a72df5eb332242960fe84b3002a241163998241eb596d4f739b9757061d" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winit" +version = "0.30.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6755fa58a9f8350bd1e472d4c3fcc25f824ec358933bba33306d0b63df5978d" +dependencies = [ + "ahash", + "android-activity", + "atomic-waker", + "bitflags 2.13.0", + "block2 0.5.1", + "bytemuck", + "calloop", + "cfg_aliases", + "concurrent-queue", + "core-foundation", + "core-graphics", + "cursor-icon", + "dpi", + "js-sys", + "libc", + "memmap2", + "ndk", + "objc2 0.5.2", + "objc2-app-kit", + "objc2-foundation 0.2.2", + "objc2-ui-kit", + "orbclient", + "percent-encoding", + "pin-project", + "raw-window-handle", + "redox_syscall 0.4.1", + "rustix 0.38.44", + "sctk-adwaita", + "smithay-client-toolkit", + "smol_str", + "tracing", + "unicode-segmentation", + "wasm-bindgen", + "wasm-bindgen-futures", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-protocols-plasma", + "web-sys", + "web-time", + "windows-sys 0.52.0", + "x11-dl", + "x11rb", + "xkbcommon-dl", +] + +[[package]] +name = "winnow" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "x11rb" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414" +dependencies = [ + "as-raw-xcb-connection", + "gethostname", + "libc", + "libloading", + "once_cell", + "rustix 1.1.4", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" + +[[package]] +name = "xcursor" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b" + +[[package]] +name = "xkbcommon-dl" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" +dependencies = [ + "bitflags 2.13.0", + "dlib", + "log", + "once_cell", + "xkeysym", +] + +[[package]] +name = "xkeysym" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" + +[[package]] +name = "zerocopy" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/2026/games/asrael_io/source/Cargo.toml b/2026/games/asrael_io/source/Cargo.toml new file mode 100644 index 0000000..5853732 --- /dev/null +++ b/2026/games/asrael_io/source/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "cj2k26" +version = "0.1.0" +license = "MIT" +repository = "https://github.com/asrael/cj2k26" +edition = "2024" + +[dependencies] +aseprite-io = "0.2" +cpal = "0.18" +embedded-graphics = "0.8" +env_logger = "0.11" +gilrs = "0.11" +glam = "0.33" +log = "0.4" +resid-rs = { git = "https://github.com/asrael/resid-rs" } +softbuffer = "0.4" +web-time = "1.1" +winit = "0.30" + +[target.'cfg(target_arch = "wasm32")'.dependencies] +console_error_panic_hook = "0.1" +cpal = { version = "0.18", features = ["wasm-bindgen"] } +wasm-bindgen = "0.2" + +[profile.dev] +opt-level = 1 +panic = "abort" + +[profile.release] +codegen-units = 1 +lto = true +panic = "abort" +strip = "debuginfo" + +[profile.wasm-release] +inherits = "release" +opt-level = "z" diff --git a/2026/games/asrael_io/source/Trunk.toml b/2026/games/asrael_io/source/Trunk.toml new file mode 100644 index 0000000..964e421 --- /dev/null +++ b/2026/games/asrael_io/source/Trunk.toml @@ -0,0 +1,2 @@ +[build] +public_url = "./" diff --git a/2026/games/asrael_io/source/assets/sprites.aseprite b/2026/games/asrael_io/source/assets/sprites.aseprite new file mode 100644 index 0000000..8e6a04b Binary files /dev/null and b/2026/games/asrael_io/source/assets/sprites.aseprite differ diff --git a/2026/games/asrael_io/source/index.html b/2026/games/asrael_io/source/index.html new file mode 100644 index 0000000..5002f27 --- /dev/null +++ b/2026/games/asrael_io/source/index.html @@ -0,0 +1,53 @@ + + + + + + + + diff --git a/2026/games/asrael_io/source/index.scss b/2026/games/asrael_io/source/index.scss new file mode 100644 index 0000000..4b0f47f --- /dev/null +++ b/2026/games/asrael_io/source/index.scss @@ -0,0 +1,18 @@ +html, +body { + margin: 0; + overflow: hidden; + background: #000; +} + +canvas { + display: block; + position: fixed; + left: 50%; + top: 50%; + width: 240px !important; + height: 160px !important; + image-rendering: pixelated; + transform-origin: center; + outline: none; +} diff --git a/2026/games/asrael_io/source/src/anim.rs b/2026/games/asrael_io/source/src/anim.rs new file mode 100644 index 0000000..427e22e --- /dev/null +++ b/2026/games/asrael_io/source/src/anim.rs @@ -0,0 +1,42 @@ +use crate::color::Palette; +use crate::sprite::Sprite; + +use std::rc::Rc; + +use glam::Vec2; + +const FRAME_TICKS: u32 = 6; + +pub struct Anim { + center: Vec2, + frames: Rc>, + tick: u32, +} + +impl Anim { + pub fn new(frames: Rc>, center: Vec2) -> Self { + Self { + center, + frames, + tick: 0, + } + } + + pub fn done(&self) -> bool { + self.tick >= FRAME_TICKS * self.frames.len() as u32 + } + + pub fn update(&mut self) { + self.tick += 1; + } + + pub fn draw(&self, frame: &mut [u32], palette: &Palette) { + let i = (self.tick / FRAME_TICKS) as usize; + let Some(sprite) = self.frames.get(i) else { + return; + }; + + let pos = self.center - sprite.size.as_vec2() / 2.0; + sprite.draw_at(frame, palette, pos.round().as_ivec2()); + } +} diff --git a/2026/games/asrael_io/source/src/bullet.rs b/2026/games/asrael_io/source/src/bullet.rs new file mode 100644 index 0000000..ac44f25 --- /dev/null +++ b/2026/games/asrael_io/source/src/bullet.rs @@ -0,0 +1,118 @@ +use crate::color::Palette; +use crate::color::db32::{LIGHT_RED, LIME}; +use crate::math::aabb; +use crate::sprite::Sprite; +use crate::{GAME_H, GAME_W}; + +use glam::Vec2; + +const ENEMY_BULLET_W: i32 = 2; +const ENEMY_BULLET_H: i32 = 6; +const PLAYER_BULLET_W: i32 = 3; +const PLAYER_BULLET_H: i32 = 6; +const ENEMY_SHOT_SPEED: f32 = 1.5; +const PLAYER_SHOT_SPEED: f32 = 4.0; + +#[rustfmt::skip] +const ENEMY_BULLET_PIXELS: [u8; (ENEMY_BULLET_W * ENEMY_BULLET_H) as usize] = [ + LIME, LIME, + LIME, LIME, + LIME, LIME, + LIME, LIME, + LIME, LIME, + LIME, LIME, +]; + +#[rustfmt::skip] +const PLAYER_BULLET_PIXELS: [u8; (PLAYER_BULLET_W * PLAYER_BULLET_H) as usize] = [ + 0, LIGHT_RED, 0, + LIGHT_RED, LIGHT_RED, LIGHT_RED, + 0, 0, 0, + 0, LIGHT_RED, 0, + 0, LIGHT_RED, 0, + 0, LIGHT_RED, 0, +]; + +pub struct Bullet { + angle: Option, + pos: Vec2, + vel: Vec2, + sprite: Sprite, +} + +impl Bullet { + fn new( + pos: Vec2, + vel: Vec2, + angle: Option, + width: i32, + height: i32, + pixels: &[u8], + ) -> Self { + let sprite = Sprite::new(width, height, pixels.to_vec()); + + Self { + angle, + pos, + vel, + sprite, + } + } + + pub fn aimed(from: Vec2, target: Vec2) -> Self { + let dir = (target - from).normalize_or_zero(); + let vel = dir * ENEMY_SHOT_SPEED; + let pos = from + Vec2::new(-(ENEMY_BULLET_W as f32) / 2.0, 0.0); + let angle = (-dir.x).atan2(dir.y); + + Self::new( + pos, + vel, + Some(angle), + ENEMY_BULLET_W, + ENEMY_BULLET_H, + &ENEMY_BULLET_PIXELS, + ) + } + + pub fn fired(muzzle_pos: Vec2) -> Self { + let pos = + muzzle_pos + Vec2::new(-(PLAYER_BULLET_W as f32) / 2.0, -(PLAYER_BULLET_H as f32)); + + Self::new( + pos, + Vec2::new(0.0, -PLAYER_SHOT_SPEED), + None, + PLAYER_BULLET_W, + PLAYER_BULLET_H, + &PLAYER_BULLET_PIXELS, + ) + } + + pub fn hits(&self, pos: Vec2, size: Vec2) -> bool { + aabb(self.pos, self.sprite.size.as_vec2(), pos, size) + } + + pub fn offscreen(&self) -> bool { + self.pos.x + self.sprite.size.x as f32 <= 0.0 + || self.pos.x >= GAME_W as f32 + || self.pos.y + self.sprite.size.y as f32 <= 0.0 + || self.pos.y >= GAME_H as f32 + } + + pub fn update(&mut self) { + self.pos += self.vel; + } + + pub fn draw(&self, frame: &mut [u32], palette: &Palette, a: f32) { + let p = self.pos - self.vel * (1.0 - a); + + match self.angle { + Some(angle) => { + let center = p + self.sprite.size.as_vec2() / 2.0; + self.sprite.draw_rotated(frame, palette, center, angle, 0); + } + None => self.sprite.draw_at(frame, palette, p.round().as_ivec2()), + } + } +} diff --git a/2026/games/asrael_io/source/src/color.rs b/2026/games/asrael_io/source/src/color.rs new file mode 100644 index 0000000..1db0786 --- /dev/null +++ b/2026/games/asrael_io/source/src/color.rs @@ -0,0 +1,66 @@ +pub type Color = u32; + +#[allow(dead_code)] +pub mod db32 { + pub const BLACK: u8 = 0; + pub const DARK_PURPLE: u8 = 1; + pub const MAROON: u8 = 2; + pub const DARK_BROWN: u8 = 3; + pub const BROWN: u8 = 4; + pub const ORANGE: u8 = 5; + pub const TAN: u8 = 6; + pub const PEACH: u8 = 7; + pub const YELLOW: u8 = 8; + pub const LIME: u8 = 9; + pub const GREEN: u8 = 10; + pub const SEA_GREEN: u8 = 11; + pub const DARK_GREEN: u8 = 12; + pub const OLIVE: u8 = 13; + pub const CHARCOAL: u8 = 14; + pub const NAVY: u8 = 15; + pub const STEEL_BLUE: u8 = 16; + pub const INDIGO: u8 = 17; + pub const BLUE: u8 = 18; + pub const CYAN: u8 = 19; + pub const PALE_BLUE: u8 = 20; + pub const WHITE: u8 = 21; + pub const GRAY: u8 = 22; + pub const STONE: u8 = 23; + pub const DIM_GRAY: u8 = 24; + pub const SLATE: u8 = 25; + pub const PURPLE: u8 = 26; + pub const RED: u8 = 27; + pub const LIGHT_RED: u8 = 28; + pub const PINK: u8 = 29; + pub const MOSS: u8 = 30; + pub const KHAKI: u8 = 31; +} + +const fn from_rgb(r: u8, g: u8, b: u8) -> Color { + (r as u32) << 16 | (g as u32) << 8 | b as u32 +} + +#[derive(Clone, Copy)] +pub struct Palette([Color; 256]); + +impl Default for Palette { + fn default() -> Self { + Self([0; 256]) + } +} + +impl Palette { + pub fn at(&self, index: u8) -> Color { + self.0[index as usize] + } + + pub fn from_ase(src: &[aseprite::Color]) -> Self { + let mut out = [0u32; 256]; + + for (dst, s) in out.iter_mut().zip(src) { + *dst = from_rgb(s.r, s.g, s.b); + } + + Palette(out) + } +} diff --git a/2026/games/asrael_io/source/src/enemy.rs b/2026/games/asrael_io/source/src/enemy.rs new file mode 100644 index 0000000..46eabc0 --- /dev/null +++ b/2026/games/asrael_io/source/src/enemy.rs @@ -0,0 +1,174 @@ +use crate::color::Palette; +use crate::color::db32::{LIGHT_RED, PINK, PURPLE}; +use crate::gfx; +use crate::math; +use crate::rng::Rng; +use crate::sprite::Sprite; +use crate::{GAME_H, GAME_W}; + +use std::f32::consts::TAU; + +use aseprite::AsepriteFile; +use glam::Vec2; + +const ROT_STEPS: f32 = 16.0; + +#[derive(Default)] +enum State { + Dive { + path: [Vec2; 4], + fired: u32, + shots: u32, + t: f32, + }, + #[default] + Formation, +} + +pub struct Enemy { + base: Vec2, + flash: u32, + hp: u32, + pos: Vec2, + step: Vec2, + sprite: Sprite, + state: State, +} + +impl Enemy { + pub fn new(sprites: &AsepriteFile, layer: &str, base: Vec2, hp: u32) -> Self { + let pos = base; + let step = Vec2::ZERO; + let sprite = Sprite::from_ase(sprites, layer); + let state = State::default(); + + Self { + base, + flash: 0, + hp, + pos, + step, + sprite, + state, + } + } + + pub fn damage(&mut self) -> bool { + self.hp = self.hp.saturating_sub(1); + self.flash = 12; + self.hp == 0 + } + + pub fn center(&self) -> Vec2 { + self.pos + self.size() / 2.0 + } + + pub fn pos(&self) -> Vec2 { + self.pos + } + + pub fn size(&self) -> Vec2 { + self.sprite.size.as_vec2() + } + + pub fn start_dive(&mut self, player_x: f32, shots: u32, rng: &mut Rng) { + if matches!(self.state, State::Dive { .. }) { + return; + } + + let side = if self.pos.x < 70.0 { + 1.0 + } else if self.pos.x > GAME_W as f32 - 70.0 { + -1.0 + } else if rng.chance(0.5) { + 1.0 + } else { + -1.0 + }; + + let min = Vec2::new(4.0, 4.0); + let max = Vec2::new(GAME_W as f32 - 20.0, GAME_H as f32 - 20.0); + let swing = (self.pos + Vec2::new(side * 70.0, -40.0)).clamp(min, max); + let plunge = Vec2::new(player_x, GAME_H as f32 - 20.0).clamp(min, max); + + let path = [self.pos, swing, plunge, self.base]; + + self.state = State::Dive { + path, + fired: 0, + shots, + t: 0.0, + }; + } + + pub fn update(&mut self, sway: f32, player: Vec2) -> Option { + let before = self.pos; + let mut shot = None; + + self.flash = self.flash.saturating_sub(1); + + match &mut self.state { + State::Formation => { + let target = self.base.x + sway; + self.pos.x += (target - self.pos.x) * 0.2; + } + + State::Dive { + path, + fired, + shots, + t, + } => { + *t += 1.0 / 240.0; + + if *t >= 1.0 { + self.pos = self.base; + self.state = State::Formation; + } else { + self.pos = math::bezier(*path, *t); + + let center = self.pos + self.sprite.size.as_vec2() / 2.0; + let facing = self.step.normalize_or_zero(); + let to_player = (player - center).normalize_or_zero(); + + let next = 0.25 + 0.5 * *fired as f32 / *shots as f32; + if *fired < *shots && *t > next && facing.dot(to_player) > 0.8 { + *fired += 1; + shot = Some(center); + } + } + } + } + + self.step = self.pos - before; + shot + } + + pub fn draw(&self, frame: &mut [u32], palette: &Palette, a: f32, tick: u32) { + let pos = self.pos - self.step * (1.0 - a); + let tint = if self.flash > 0 && (tick / 2).is_multiple_of(2) { + LIGHT_RED + } else { + 0 + }; + + if matches!(self.state, State::Dive { .. }) && self.step.length_squared() > 0.001 { + let dir = self.step.normalize(); + let angle = (-dir.x).atan2(dir.y); + let angle = (angle * ROT_STEPS / TAU).round() * (TAU / ROT_STEPS); + let center = pos + self.sprite.size.as_vec2() / 2.0; + self.sprite + .draw_rotated(frame, palette, center, angle, tint); + + let half = self.sprite.size.y as f32 / 2.0; + for i in 0..3 { + let tail = center - dir * (half + 1.0 + i as f32 * 2.0); + let c = [PURPLE, PINK][(tick / 4 + i) as usize % 2]; + gfx::blit(frame, palette, &[c], tail.round().as_ivec2(), 1, 0); + } + } else { + self.sprite + .draw_tinted(frame, palette, pos.round().as_ivec2(), tint); + } + } +} diff --git a/2026/games/asrael_io/source/src/gfx.rs b/2026/games/asrael_io/source/src/gfx.rs new file mode 100644 index 0000000..4f515c1 --- /dev/null +++ b/2026/games/asrael_io/source/src/gfx.rs @@ -0,0 +1,113 @@ +use crate::color::Palette; +use crate::{GAME_H, GAME_W}; + +use embedded_graphics::Drawable; +use embedded_graphics::Pixel; +use embedded_graphics::draw_target::DrawTarget; +use embedded_graphics::geometry::{OriginDimensions, Point, Size}; +use embedded_graphics::mono_font::{MonoFont, MonoTextStyle}; +use embedded_graphics::pixelcolor::{Rgb888, RgbColor}; +use embedded_graphics::text::{Baseline, Text}; +use glam::{IVec2, Vec2}; + +pub struct Frame<'a>(pub &'a mut [u32]); + +impl OriginDimensions for Frame<'_> { + fn size(&self) -> Size { + Size::new(GAME_W, GAME_H) + } +} + +impl DrawTarget for Frame<'_> { + type Color = Rgb888; + type Error = core::convert::Infallible; + + fn draw_iter(&mut self, pixels: I) -> Result<(), Self::Error> + where + I: IntoIterator>, + { + for Pixel(p, color) in pixels { + if p.x < 0 || p.x >= GAME_W as i32 || p.y < 0 || p.y >= GAME_H as i32 { + continue; + } + + self.0[(p.y * GAME_W as i32 + p.x) as usize] = + (color.r() as u32) << 16 | (color.g() as u32) << 8 | color.b() as u32; + } + + Ok(()) + } +} + +pub fn draw_text(frame: &mut [u32], font: &MonoFont, text: &str, pos: IVec2, color: u32) { + let color = Rgb888::new((color >> 16) as u8, (color >> 8) as u8, color as u8); + let style = MonoTextStyle::new(font, color); + + let _ = Text::with_baseline(text, Point::new(pos.x, pos.y), style, Baseline::Top) + .draw(&mut Frame(frame)); +} + +pub fn blit( + frame: &mut [u32], + palette: &Palette, + pixels: &[u8], + origin: IVec2, + width: i32, + tint: u8, +) { + for (i, &px) in pixels.iter().enumerate() { + if px == 0 { + continue; + } + + let sx = origin.x + i as i32 % width; + let sy = origin.y + i as i32 / width; + if sx < 0 || sx >= GAME_W as i32 || sy < 0 || sy >= GAME_H as i32 { + continue; + } + + let px = if tint != 0 { tint } else { px }; + frame[(sy * GAME_W as i32 + sx) as usize] = palette.at(px); + } +} + +pub fn blit_rotated( + frame: &mut [u32], + palette: &Palette, + pixels: &[u8], + center: Vec2, + width: i32, + angle: f32, + tint: u8, +) { + let height = pixels.len() as i32 / width; + let half = Vec2::new(width as f32, height as f32) / 2.0; + let (sin, cos) = angle.sin_cos(); + let r = half.length().ceil() as i32; + + for dy in -r..=r { + for dx in -r..=r { + let x = dx as f32 + 0.5; + let y = dy as f32 + 0.5; + let sx = (cos * x + sin * y + half.x).floor() as i32; + let sy = (-sin * x + cos * y + half.y).floor() as i32; + if sx < 0 || sx >= width || sy < 0 || sy >= height { + continue; + } + + let px = pixels[(sy * width + sx) as usize]; + if px == 0 { + continue; + } + + let fx = center.x.round() as i32 + dx; + let fy = center.y.round() as i32 + dy; + if fx < 0 || fx >= GAME_W as i32 || fy < 0 || fy >= GAME_H as i32 { + continue; + } + + let px = if tint != 0 { tint } else { px }; + frame[(fy * GAME_W as i32 + fx) as usize] = palette.at(px); + } + } +} diff --git a/2026/games/asrael_io/source/src/main.rs b/2026/games/asrael_io/source/src/main.rs new file mode 100644 index 0000000..7e57f8b --- /dev/null +++ b/2026/games/asrael_io/source/src/main.rs @@ -0,0 +1,642 @@ +mod anim; +mod bullet; +mod color; +mod enemy; +mod gfx; +mod math; +mod player; +mod rng; +mod sfx; +mod sprite; +mod starfield; + +use anim::Anim; +use bullet::Bullet; +use color::Palette; +use color::db32::{BLACK, CYAN, LIGHT_RED, LIME, RED, WHITE, YELLOW}; +use enemy::Enemy; +use math::aabb; +use player::Player; +use rng::Rng; +use sfx::Sfx; +use sprite::Sprite; +use starfield::Starfield; + +use std::f32::consts::TAU; +use std::num::NonZeroU32; +use std::rc::Rc; +use std::sync::Arc; +use std::time::Duration; + +use aseprite::AsepriteFile; +use embedded_graphics::mono_font::ascii::{FONT_6X10, FONT_10X20}; +use glam::{IVec2, Vec2}; +use softbuffer::{Context, Surface}; +use web_time::Instant; +use winit::application::ApplicationHandler; +use winit::event::{ElementState, KeyEvent, MouseButton, WindowEvent}; +use winit::event_loop::{ActiveEventLoop, EventLoop}; +use winit::keyboard::{KeyCode, PhysicalKey}; +use winit::window::{Window, WindowId}; + +const MAX_FRAME: Duration = Duration::from_millis(100); +const TICK: Duration = Duration::from_nanos(16_666_667); + +const PLAYER_MOVES: [(Action, IVec2); 4] = [ + (Action::Up, IVec2::new(0, -1)), + (Action::Down, IVec2::new(0, 1)), + (Action::Left, IVec2::new(-1, 0)), + (Action::Right, IVec2::new(1, 0)), +]; + +pub(crate) const GAME_W: u32 = 240; +pub(crate) const GAME_H: u32 = 160; + +const SPRITES: &[u8] = include_bytes!("../assets/sprites.aseprite"); + +const DONE_HOLD: u32 = 120; + +const WIN_CYCLE: [u8; 4] = [LIME, WHITE, CYAN, YELLOW]; +const OVER_CYCLE: [u8; 2] = [RED, LIGHT_RED]; + +#[derive(Default)] +struct Cj2k26 { + accumulator: Duration, + anims: Vec, + cursor: Option, + difficulty: f32, + dive_timer: u32, + done_timer: u32, + enemies: Vec, + enemy_bullets: Vec, + explosion_frames: Rc>, + frame: Vec, + input: u32, + interacted: bool, + last: Option, + state: State, + palette: Palette, + player: Player, + player_bullets: Vec, + rng: Rng, + sfx: Option, + starfield: Starfield, + surface: Option, Arc>>, + tick: u32, + volley_timer: u32, + window: Option>, +} + +#[derive(Clone, Copy)] +enum Action { + Up, + Down, + Left, + Right, + Fire, +} + +#[derive(Default)] +enum State { + #[default] + Waiting, + Playing, + Win, + GameOver, +} + +#[cfg(target_arch = "wasm32")] +mod jam { + use wasm_bindgen::prelude::wasm_bindgen; + + #[wasm_bindgen] + extern "C" { + #[wasm_bindgen(js_name = jamShouldStart)] + pub fn should_start() -> bool; + + #[wasm_bindgen(js_name = jamGetDifficulty)] + pub fn difficulty() -> f32; + + #[wasm_bindgen(js_name = jamStarted)] + pub fn started(verb: &str); + + #[wasm_bindgen(js_name = jamDone)] + pub fn done(win: bool); + } +} + +#[cfg(not(target_arch = "wasm32"))] +mod jam { + pub fn should_start() -> bool { + true + } + + pub fn difficulty() -> f32 { + 0.0 + } + + pub fn started(_verb: &str) {} + + pub fn done(_win: bool) {} +} + +impl Cj2k26 { + fn has_action(&self, action: Action) -> bool { + self.input & (1 << action as u32) != 0 + } + + fn unlock_audio(&mut self) { + if let Some(sfx) = &mut self.sfx { + sfx.resume(); + } + } + + fn update(&mut self) { + self.tick = self.tick.wrapping_add(1); + + if matches!(self.state, State::Waiting) && jam::should_start() { + jam::started("Shoot!"); + self.difficulty = jam::difficulty().min(7.0); + self.state = State::Playing; + } + + if self.input != 0 { + self.interacted = true; + } + + let playing = matches!(self.state, State::Playing); + + self.starfield.update(); + + if playing { + let player_dir = PLAYER_MOVES + .iter() + .filter(|(a, _)| self.has_action(*a)) + .fold(IVec2::ZERO, |acc, (_, v)| acc + *v); + + if player_dir != IVec2::ZERO { + self.cursor = None; + } + + let mut mouse = Vec2::ZERO; + if let Some(cursor) = self.cursor { + let target = cursor - self.player.size() / 2.0; + mouse = target - self.player.pos(); + } + + self.player.update(player_dir, mouse); + } + + let player_center = self.player.pos() + self.player.size() / 2.0; + + if playing && self.interacted && !self.enemies.is_empty() { + let dive_after = (90.0 - self.difficulty * 6.0).max(45.0) as u32; + self.dive_timer += 1; + if self.dive_timer >= dive_after { + self.dive_timer = 0; + + let i = self.rng.range(self.enemies.len() as u32) as usize; + let shots = (2.0 + self.difficulty / 4.0).min(5.0) as u32; + self.enemies[i].start_dive(player_center.x, shots, &mut self.rng); + } + + let volley_after = (70.0 - self.difficulty * 4.0).max(30.0) as u32; + self.volley_timer += 1; + if self.volley_timer >= volley_after { + self.volley_timer = 0; + + let volley = (2.0 + self.difficulty / 3.0).min(5.0) as u32; + for _ in 0..volley { + let i = self.rng.range(self.enemies.len() as u32) as usize; + let enemy = &self.enemies[i]; + let muzzle = enemy.center() + Vec2::new(0.0, enemy.size().y / 2.0); + let spread = Vec2::new((self.rng.f32() - 0.5) * 60.0, 0.0); + + self.enemy_bullets + .push(Bullet::aimed(muzzle, player_center + spread)); + } + + if let Some(sfx) = &self.sfx { + sfx.enemy_shoot(); + } + } + } + + let seconds = self.tick as f32 / 60.0; + let sway = 12.0 * (seconds * TAU / 3.0).sin(); + for enemy in &mut self.enemies { + if let Some(muzzle_pos) = enemy.update(sway, player_center) + && playing + { + self.enemy_bullets + .push(Bullet::aimed(muzzle_pos, self.player.pos())); + if let Some(sfx) = &self.sfx { + sfx.enemy_shoot(); + } + } + } + + if playing + && self.has_action(Action::Fire) + && let Some(muzzle_pos) = self.player.try_fire() + { + if let Some(sfx) = &self.sfx { + sfx.shoot(); + } + + self.player_bullets.push(Bullet::fired(muzzle_pos)); + } + + for bullet in self + .player_bullets + .iter_mut() + .chain(&mut self.enemy_bullets) + { + bullet.update(); + } + + self.player_bullets.retain(|b| !b.offscreen()); + self.enemy_bullets.retain(|b| !b.offscreen()); + + self.player_bullets.retain(|b| { + match self.enemies.iter().position(|e| b.hits(e.pos(), e.size())) { + Some(i) => { + if self.enemies[i].damage() { + let enemy = self.enemies.swap_remove(i); + self.anims + .push(Anim::new(self.explosion_frames.clone(), enemy.center())); + if let Some(sfx) = &self.sfx { + sfx.explode(); + } + } else if let Some(sfx) = &self.sfx { + sfx.hit(); + } + false + } + None => true, + } + }); + + if playing { + let p_pos = self.player.pos(); + let p_size = self.player.size(); + + let mut player_hit = false; + self.enemy_bullets.retain(|b| { + !b.hits(p_pos, p_size) || { + player_hit = true; + false + } + }); + + player_hit |= self + .enemies + .iter() + .any(|e| aabb(p_pos, p_size, e.pos(), e.size())); + + if player_hit { + self.state = State::GameOver; + self.done_timer = DONE_HOLD; + self.anims.push(Anim::new( + self.explosion_frames.clone(), + p_pos + p_size / 2.0, + )); + if let Some(sfx) = &self.sfx { + sfx.explode(); + sfx.lose(); + } + } + } + + if matches!(self.state, State::Playing) && self.enemies.is_empty() { + self.state = State::Win; + self.done_timer = DONE_HOLD; + if let Some(sfx) = &self.sfx { + sfx.win(); + } + } + + if matches!(self.state, State::Win) { + self.player + .win_anim(DONE_HOLD.saturating_sub(self.done_timer)); + } + + if self.done_timer > 0 { + self.done_timer -= 1; + if self.done_timer == 0 { + jam::done(matches!(self.state, State::Win)); + } + } + + for anim in &mut self.anims { + anim.update(); + } + self.anims.retain(|a| !a.done()); + } + + fn draw(&mut self, a: f32) { + self.frame.fill(self.palette.at(BLACK)); + + self.starfield.draw(&mut self.frame, &self.palette, a); + + if !matches!(self.state, State::GameOver) { + self.player + .draw(&mut self.frame, &self.palette, a, self.tick); + } + + for enemy in &self.enemies { + enemy.draw(&mut self.frame, &self.palette, a, self.tick); + } + + for bullet in self.player_bullets.iter().chain(&self.enemy_bullets) { + bullet.draw(&mut self.frame, &self.palette, a); + } + + for anim in &self.anims { + anim.draw(&mut self.frame, &self.palette); + } + + let cycle = (self.tick / 8) as usize; + let banner = match self.state { + State::Win => Some(("YOU WIN", WIN_CYCLE[cycle % WIN_CYCLE.len()])), + State::GameOver => Some(("GAME OVER", OVER_CYCLE[cycle % OVER_CYCLE.len()])), + _ => None, + }; + + if let Some((text, color)) = banner { + let w = text.len() as i32 * FONT_10X20.character_size.width as i32; + let pos = IVec2::new((GAME_W as i32 - w) / 2, (GAME_H as i32 - 20) / 2); + gfx::draw_text( + &mut self.frame, + &FONT_10X20, + text, + pos, + self.palette.at(color), + ); + } + + if !self.interacted && matches!(self.state, State::Waiting | State::Playing) { + let lines = ["Mouse or WASD to Move", "Left Click or Space to Fire"]; + for (i, hint) in lines.iter().enumerate() { + let w = hint.len() as i32 * FONT_6X10.character_size.width as i32; + let y = GAME_H as i32 / 2 + 20 + i as i32 * 12; + let pos = IVec2::new((GAME_W as i32 - w) / 2, y); + gfx::draw_text( + &mut self.frame, + &FONT_6X10, + hint, + pos, + self.palette.at(WHITE), + ); + } + } + + self.present(); + } + + fn present(&mut self) { + let Some(window) = self.window.as_ref() else { + return; + }; + let Some(surface) = self.surface.as_mut() else { + return; + }; + + let size = window.inner_size(); + let (w, h) = (size.width as usize, size.height as usize); + let scale = (w / GAME_W as usize).min(h / GAME_H as usize); + if scale == 0 { + return; + } + + let Ok(mut buffer) = surface.buffer_mut() else { + return; + }; + if buffer.len() != w * h { + return; + } + let out_w = GAME_W as usize * scale; + let x0 = w.saturating_sub(out_w) / 2; + let y0 = h.saturating_sub(GAME_H as usize * scale) / 2; + + buffer.fill(0); + + let mut row = vec![0u32; out_w]; + for gy in 0..GAME_H as usize { + let src = &self.frame[gy * GAME_W as usize..(gy + 1) * GAME_W as usize]; + for (i, &c) in src.iter().enumerate() { + row[i * scale..(i + 1) * scale].fill(c); + } + + for sy in 0..scale { + let start = (y0 + gy * scale + sy) * w + x0; + buffer[start..start + out_w].copy_from_slice(&row); + } + } + + let _ = buffer.present(); + } +} + +impl ApplicationHandler for Cj2k26 { + fn resumed(&mut self, event_loop: &ActiveEventLoop) { + let attributes = Window::default_attributes().with_title("cj2k26"); + + #[cfg(not(target_arch = "wasm32"))] + let attributes = { + use winit::dpi::LogicalSize; + + let size = LogicalSize::new(GAME_W * 4, GAME_H * 4); + attributes.with_inner_size(size).with_min_inner_size(size) + }; + + #[cfg(target_arch = "wasm32")] + let attributes = { + use winit::platform::web::WindowAttributesExtWebSys; + attributes.with_append(true) + }; + + let window = Arc::new( + event_loop + .create_window(attributes) + .expect("failed to create window"), + ); + window.set_cursor_visible(false); + + #[cfg(not(target_arch = "wasm32"))] + { + use winit::window::CursorGrabMode; + let _ = window + .set_cursor_grab(CursorGrabMode::Confined) + .or_else(|_| window.set_cursor_grab(CursorGrabMode::Locked)); + } + + let context = Context::new(window.clone()).expect("failed to create softbuffer context"); + let mut surface = Surface::new(&context, window.clone()).expect("failed to create surface"); + + let inner = window.inner_size(); + if let (Some(w), Some(h)) = (NonZeroU32::new(inner.width), NonZeroU32::new(inner.height)) { + let _ = surface.resize(w, h); + } + + self.frame = vec![0; (GAME_W * GAME_H) as usize]; + self.surface = Some(surface); + + let sprites = AsepriteFile::from_reader(SPRITES).expect("failed to read sprites aseprite!"); + + self.enemies = (0..8) + .map(|i| { + let hp = if i % 3 == 0 { 2 } else { 1 }; + Enemy::new( + &sprites, + "enemy", + Vec2::new(24.0 + i as f32 * 24.0, 24.0), + hp, + ) + }) + .collect(); + + self.explosion_frames = Rc::new(Sprite::frames_from_ase(&sprites, "explosion")); + self.palette = Palette::from_ase(sprites.palette()); + self.player = Player::new(&sprites, 2.0); + self.rng = Rng::default(); + self.sfx = Some(Sfx::new()); + self.starfield = Starfield::new(60, &mut self.rng); + self.window = Some(window.clone()); + + window.request_redraw(); + } + + fn window_event(&mut self, event_loop: &ActiveEventLoop, _id: WindowId, event: WindowEvent) { + match event { + WindowEvent::CloseRequested => event_loop.exit(), + + WindowEvent::Resized(size) => { + if let Some(surface) = self.surface.as_mut() + && let (Some(w), Some(h)) = + (NonZeroU32::new(size.width), NonZeroU32::new(size.height)) + { + let _ = surface.resize(w, h); + } + } + + WindowEvent::RedrawRequested => { + let now = Instant::now(); + let dt = self + .last + .replace(now) + .map(|prev| (now - prev).min(MAX_FRAME)) + .unwrap_or_default(); + self.accumulator += dt; + + while self.accumulator >= TICK { + self.update(); + self.accumulator -= TICK; + } + + let alpha = self.accumulator.as_secs_f32() / TICK.as_secs_f32(); + self.draw(alpha); + + #[cfg(not(target_arch = "wasm32"))] + std::thread::sleep(TICK.saturating_sub(now.elapsed())); + + if let Some(window) = self.window.as_ref() { + window.request_redraw(); + } + } + + WindowEvent::KeyboardInput { + event: + KeyEvent { + physical_key: PhysicalKey::Code(code), + state, + .. + }, + .. + } => { + self.unlock_audio(); + + if let Some(action) = bind_key(code) { + let bit = 1 << action as u32; + + match state { + ElementState::Pressed => self.input |= bit, + ElementState::Released => self.input &= !bit, + } + } + } + + WindowEvent::CursorMoved { position, .. } => { + if let Some(window) = self.window.as_ref() { + let size = window.inner_size(); + let (w, h) = (size.width as i32, size.height as i32); + let scale = (w / GAME_W as i32).min(h / GAME_H as i32); + + if scale > 0 { + let x0 = (w - GAME_W as i32 * scale) / 2; + let y0 = (h - GAME_H as i32 * scale) / 2; + let gx = (position.x as i32 - x0) as f32 / scale as f32; + let gy = (position.y as i32 - y0) as f32 / scale as f32; + + self.cursor = Some(Vec2::new(gx, gy)); + if matches!(self.state, State::Playing) { + self.interacted = true; + } + } + } + } + + WindowEvent::MouseInput { + state, + button: MouseButton::Left, + .. + } => { + self.unlock_audio(); + + let bit = 1 << Action::Fire as u32; + match state { + ElementState::Pressed => self.input |= bit, + ElementState::Released => self.input &= !bit, + } + } + + WindowEvent::MouseInput { .. } | WindowEvent::KeyboardInput { .. } => { + self.unlock_audio(); + } + + _ => {} + } + } +} + +fn bind_key(code: KeyCode) -> Option { + match code { + KeyCode::KeyW | KeyCode::ArrowUp => Some(Action::Up), + KeyCode::KeyS | KeyCode::ArrowDown => Some(Action::Down), + KeyCode::KeyA | KeyCode::ArrowLeft => Some(Action::Left), + KeyCode::KeyD | KeyCode::ArrowRight => Some(Action::Right), + KeyCode::Space => Some(Action::Fire), + + _ => None, + } +} + +fn main() { + let event_loop = EventLoop::new().expect("failed to create event loop"); + + #[cfg(not(target_arch = "wasm32"))] + { + env_logger::init_from_env(env_logger::Env::default().default_filter_or("info")); + event_loop + .run_app(&mut Cj2k26::default()) + .expect("failed to run app"); + } + + #[cfg(target_arch = "wasm32")] + { + use winit::platform::web::EventLoopExtWebSys; + + console_error_panic_hook::set_once(); + event_loop.spawn_app(Cj2k26::default()); + } +} diff --git a/2026/games/asrael_io/source/src/math.rs b/2026/games/asrael_io/source/src/math.rs new file mode 100644 index 0000000..371f165 --- /dev/null +++ b/2026/games/asrael_io/source/src/math.rs @@ -0,0 +1,13 @@ +use glam::Vec2; + +pub fn aabb(a_pos: Vec2, a_size: Vec2, b_pos: Vec2, b_size: Vec2) -> bool { + a_pos.x < b_pos.x + b_size.x + && b_pos.x < a_pos.x + a_size.x + && a_pos.y < b_pos.y + b_size.y + && b_pos.y < a_pos.y + a_size.y +} + +pub fn bezier(p: [Vec2; 4], t: f32) -> Vec2 { + let u = 1.0 - t; + u * u * u * p[0] + 3.0 * u * u * t * p[1] + 3.0 * u * t * t * p[2] + t * t * t * p[3] +} diff --git a/2026/games/asrael_io/source/src/player.rs b/2026/games/asrael_io/source/src/player.rs new file mode 100644 index 0000000..637160b --- /dev/null +++ b/2026/games/asrael_io/source/src/player.rs @@ -0,0 +1,96 @@ +use crate::color::Palette; +use crate::color::db32::{ORANGE, YELLOW}; +use crate::gfx; +use crate::sprite::Sprite; +use crate::{GAME_H, GAME_W}; + +use aseprite::AsepriteFile; +use glam::{IVec2, Vec2}; + +const FIRE_COOLDOWN: u32 = 12; +const MOUSE_MAX: f32 = 2.0; + +#[derive(Default)] +pub struct Player { + cooldown: u32, + pos: Vec2, + step: Vec2, + speed: f32, + sprite: Sprite, +} + +impl Player { + pub fn new(sprites: &AsepriteFile, speed: f32) -> Self { + let cooldown = 0; + let pos = Vec2::new(GAME_W as f32 / 2.0, GAME_H as f32 / 2.0); + let step = Vec2::ZERO; + let sprite = Sprite::from_ase(sprites, "player"); + + Self { + cooldown, + pos, + step, + speed, + sprite, + } + } + + pub fn win_anim(&mut self, t: u32) { + let before = self.pos; + + if t < 40 { + self.pos.y += 0.3; + } else { + self.pos.y -= ((t - 40) as f32 * 0.25).min(8.0); + } + + self.step = self.pos - before; + } + + pub fn pos(&self) -> Vec2 { + self.pos + } + + pub fn size(&self) -> Vec2 { + self.sprite.size.as_vec2() + } + + pub fn try_fire(&mut self) -> Option { + (self.cooldown == 0).then(|| { + self.cooldown = FIRE_COOLDOWN; + self.pos + Vec2::new(self.sprite.size.x as f32 / 2.0, 0.0) + }) + } + + pub fn update(&mut self, direction: IVec2, mouse: Vec2) { + let before = self.pos; + + self.cooldown = self.cooldown.saturating_sub(1); + + let dir = direction.as_vec2().normalize_or_zero(); + self.pos += dir * self.speed + mouse.clamp_length_max(MOUSE_MAX); + + let w = self.sprite.size.x as f32; + let h = self.sprite.size.y as f32; + + self.pos.x = self.pos.x.clamp(0.0, GAME_W as f32 - w); + self.pos.y = self.pos.y.clamp(0.0, GAME_H as f32 - h); + self.step = self.pos - before; + } + + pub fn draw(&self, frame: &mut [u32], palette: &Palette, a: f32, tick: u32) { + let pos = (self.pos - self.step * (1.0 - a)).round().as_ivec2(); + self.sprite.draw_at(frame, palette, pos); + + if self.step.length_squared() > 0.01 { + let flame = if (tick / 4).is_multiple_of(2) { + ORANGE + } else { + YELLOW + }; + let exhaust = [flame, flame]; + let nozzle = pos + IVec2::new(self.sprite.size.x / 2, self.sprite.size.y); + gfx::blit(frame, palette, &exhaust, nozzle, 1, 0); + } + } +} diff --git a/2026/games/asrael_io/source/src/rng.rs b/2026/games/asrael_io/source/src/rng.rs new file mode 100644 index 0000000..6e46de0 --- /dev/null +++ b/2026/games/asrael_io/source/src/rng.rs @@ -0,0 +1,35 @@ +pub struct Rng(u32); + +impl Rng { + pub fn new(seed: u32) -> Self { + let mut rng = Self(seed.max(1)); + rng.next(); + rng + } + + pub fn next(&mut self) -> u32 { + self.0 ^= self.0 << 13; + self.0 ^= self.0 >> 17; + self.0 ^= self.0 << 5; + + self.0 + } + + pub fn range(&mut self, n: u32) -> u32 { + self.next() % n + } + + pub fn f32(&mut self) -> f32 { + self.next() as f32 / u32::MAX as f32 + } + + pub fn chance(&mut self, p: f32) -> bool { + self.f32() < p + } +} + +impl Default for Rng { + fn default() -> Self { + Self::new(u32::from_be_bytes(*b"CLNK")) + } +} diff --git a/2026/games/asrael_io/source/src/sfx.rs b/2026/games/asrael_io/source/src/sfx.rs new file mode 100644 index 0000000..a1367df --- /dev/null +++ b/2026/games/asrael_io/source/src/sfx.rs @@ -0,0 +1,302 @@ +use std::sync::mpsc::{Receiver, Sender, channel}; + +use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; +use cpal::{Device, OutputCallbackInfo, Stream, SupportedStreamConfig}; +use resid::{ChipModel, PAL_CLOCK, SamplingMethod, Sid}; + +const GATE: u8 = 0x01; +const TRIANGLE: u8 = 0x10; +const SAWTOOTH: u8 = 0x20; +const PULSE: u8 = 0x40; +const NOISE: u8 = 0x80; + +enum Sound { + EnemyShoot, + Explosion, + Hit, + Lose, + Shoot, + Win, +} + +struct Pending { + config: SupportedStreamConfig, + device: Device, + rx: Receiver, + sid: Sid, +} + +pub struct Sfx { + pending: Option, + stream: Option, + tx: Sender, +} + +impl Sfx { + pub fn new() -> Self { + let device = cpal::default_host() + .default_output_device() + .expect("no audio output device"); + + let config = device + .default_output_config() + .expect("no default audio config"); + + let mut sid = Sid::new(ChipModel::Mos6581); + let (tx, rx) = channel::(); + + sid.set_sampling_parameters(SamplingMethod::ResampleFast, PAL_CLOCK, config.sample_rate()); + Self::set_volume(&mut sid, 0x0F); + Self::set_envelope(&mut sid, 0, 0x06, 0x00); + Self::set_envelope(&mut sid, 1, 0x06, 0x00); + Self::set_envelope(&mut sid, 2, 0x08, 0xA9); + + Self { + pending: Some(Pending { + config, + device, + rx, + sid, + }), + stream: None, + tx, + } + } + + pub fn resume(&mut self) { + if let Some(stream) = &self.stream { + let _ = stream.play(); + return; + } + + let Some(Pending { + config, + device, + rx, + mut sid, + }) = self.pending.take() + else { + return; + }; + + let channels = config.channels() as usize; + let sample_rate = config.sample_rate(); + let mut scratch: Vec = Vec::new(); + + let mut enemy_shoot_hz: f32 = 0.0; + let mut explosion_hz: f32 = 0.0; + let mut hit_t: u32 = 0; + let mut lose_hz: f32 = 0.0; + let mut lose_t: u32 = 0; + let mut lose_wob: f32 = 0.0; + let mut shoot_hz: f32 = 0.0; + let mut win_t: u32 = 0; + + let stream = device + .build_output_stream( + config.into(), + move |data: &mut [f32], _: &OutputCallbackInfo| { + while let Ok(sound) = rx.try_recv() { + match sound { + Sound::EnemyShoot => { + enemy_shoot_hz = 520.0; + Self::set_freq(&mut sid, 1, enemy_shoot_hz); + Self::gate_on(&mut sid, 1, TRIANGLE); + } + + Sound::Explosion => { + hit_t = 0; + Self::set_envelope(&mut sid, 2, 0x08, 0xA9); + + explosion_hz = 2500.0; + Self::set_freq(&mut sid, 2, explosion_hz); + Self::gate_on(&mut sid, 2, NOISE); + } + + Sound::Hit => { + Self::set_envelope(&mut sid, 2, 0x05, 0x00); + + hit_t = 45; + Self::set_freq(&mut sid, 2, 2500.0); + Self::gate_on(&mut sid, 2, NOISE); + } + + Sound::Lose => { + enemy_shoot_hz = 0.0; + Self::set_envelope(&mut sid, 1, 0x00, 0xC6); + Self::set_pulse_width(&mut sid, 1, 0x0800); + + lose_hz = 392.0; + lose_t = 300; + lose_wob = 0.0; + Self::set_freq(&mut sid, 1, lose_hz); + Self::gate_on(&mut sid, 1, PULSE); + } + + Sound::Shoot => { + shoot_hz = 1760.0; + Self::set_freq(&mut sid, 0, shoot_hz); + Self::gate_on(&mut sid, 0, SAWTOOTH); + } + + Sound::Win => { + shoot_hz = 0.0; + Self::set_envelope(&mut sid, 0, 0x00, 0xA9); + Self::set_pulse_width(&mut sid, 0, 0x0800); + + win_t = 300; + Self::set_freq(&mut sid, 0, 987.77); + Self::gate_on(&mut sid, 0, PULSE); + } + } + } + + let frames = data.len() / channels; + if scratch.len() < frames { + scratch.resize(frames, 0); + } + let out = &mut scratch[..frames]; + + for block in out.chunks_mut(64) { + Self::sweep(&mut sid, &mut shoot_hz, 0.975, 220.0, 0, SAWTOOTH); + Self::sweep(&mut sid, &mut enemy_shoot_hz, 0.97, 200.0, 1, TRIANGLE); + Self::sweep(&mut sid, &mut explosion_hz, 0.985, 150.0, 2, NOISE); + + if hit_t > 0 { + hit_t -= 1; + if hit_t == 0 { + Self::gate_off(&mut sid, 2, NOISE); + } + } + + if win_t > 0 { + win_t -= 1; + + if win_t == 245 || win_t == 190 { + let hz = if win_t == 245 { 1318.5 } else { 1661.2 }; + Self::set_freq(&mut sid, 0, hz); + Self::gate_on(&mut sid, 0, PULSE); + } + + if win_t == 0 { + Self::gate_off(&mut sid, 0, PULSE); + } + } + + if lose_t > 0 { + lose_t -= 1; + lose_hz *= 0.995; + lose_wob += 0.35; + + let hz = lose_hz * (1.0 + 0.05 * lose_wob.sin()); + Self::set_freq(&mut sid, 1, hz); + + if lose_t == 0 { + Self::gate_off(&mut sid, 1, PULSE); + } + } + + Self::fill(&mut sid, block, sample_rate); + } + + for (frame, &s) in data.chunks_mut(channels).zip(out.iter()) { + frame.fill(s as f32 / 32768.0); + } + }, + move |err| log::error!("audio stream error: {err}"), + None, + ) + .expect("failed to build audio stream"); + + stream.play().expect("failed to start audio stream"); + self.stream = Some(stream); + } + + pub fn enemy_shoot(&self) { + let _ = self.tx.send(Sound::EnemyShoot); + } + + pub fn explode(&self) { + let _ = self.tx.send(Sound::Explosion); + } + + pub fn hit(&self) { + let _ = self.tx.send(Sound::Hit); + } + + pub fn lose(&self) { + let _ = self.tx.send(Sound::Lose); + } + + pub fn shoot(&self) { + let _ = self.tx.send(Sound::Shoot); + } + + pub fn win(&self) { + let _ = self.tx.send(Sound::Win); + } + + fn fill(sid: &mut Sid, out: &mut [i16], sample_rate: u32) { + let budget = PAL_CLOCK / sample_rate + 1; + let mut i = 0; + + while i < out.len() { + let target = (out.len() - i) as u32; + let (n, _) = sid.sample(target * budget, &mut out[i..], 1); + + if n == 0 { + break; + } + + i += n; + } + } + + fn gate_off(sid: &mut Sid, voice: u8, wave: u8) { + sid.write(voice * 7 + 4, wave); + } + + fn gate_on(sid: &mut Sid, voice: u8, wave: u8) { + sid.write(voice * 7 + 4, wave); + sid.write(voice * 7 + 4, wave | GATE); + } + + fn set_envelope(sid: &mut Sid, voice: u8, attack_decay: u8, sustain_release: u8) { + let base = voice * 7; + + sid.write(base + 5, attack_decay); + sid.write(base + 6, sustain_release); + } + + fn set_freq(sid: &mut Sid, voice: u8, hz: f32) { + let base = voice * 7; + let freq = (hz * 16_777_216.0 / PAL_CLOCK as f32) as u16; + + sid.write(base, freq as u8); + sid.write(base + 1, (freq >> 8) as u8); + } + + fn set_pulse_width(sid: &mut Sid, voice: u8, width: u16) { + let base = voice * 7; + + sid.write(base + 2, width as u8); + sid.write(base + 3, (width >> 8) as u8); + } + + fn set_volume(sid: &mut Sid, volume: u8) { + sid.write(0x18, volume); + } + + fn sweep(sid: &mut Sid, hz: &mut f32, rate: f32, floor: f32, voice: u8, wave: u8) { + if *hz > 0.0 { + *hz *= rate; + + if *hz < floor { + *hz = 0.0; + Self::gate_off(sid, voice, wave); + } else { + Self::set_freq(sid, voice, *hz); + } + } + } +} diff --git a/2026/games/asrael_io/source/src/sprite.rs b/2026/games/asrael_io/source/src/sprite.rs new file mode 100644 index 0000000..2bfb104 --- /dev/null +++ b/2026/games/asrael_io/source/src/sprite.rs @@ -0,0 +1,89 @@ +use crate::color::Palette; +use crate::gfx; + +use aseprite::{AsepriteFile, CelKind}; +use glam::{IVec2, Vec2}; + +#[derive(Clone, Default)] +pub struct Sprite { + pixels: Vec, + pub size: IVec2, +} + +impl Sprite { + pub fn new(width: i32, height: i32, pixels: Vec) -> Self { + Self { + pixels, + size: IVec2::new(width, height), + } + } + + pub fn from_ase(file: &AsepriteFile, layer: &str) -> Self { + let index = file + .layers() + .iter() + .position(|l| l.name == layer) + .unwrap_or_else(|| panic!("no layer named {layer}")); + let cel = file.cel(file.layer_ref(index).unwrap(), 0).unwrap(); + + let (CelKind::Raw { pixels, .. } | CelKind::Compressed { pixels, .. }) = &cel.kind else { + panic!("layer {layer} has no pixel cel"); + }; + + Self::new( + pixels.width as i32, + pixels.height as i32, + pixels.data.clone(), + ) + } + + pub fn frames_from_ase(file: &AsepriteFile, layer: &str) -> Vec { + let index = file + .layers() + .iter() + .position(|l| l.name == layer) + .unwrap_or_else(|| panic!("no layer named {layer}")); + let layer_ref = file.layer_ref(index).unwrap(); + + (0..file.frames().len()) + .filter_map(|f| file.resolve_cel(layer_ref, f)) + .filter_map(|cel| match &cel.kind { + CelKind::Raw { pixels, .. } | CelKind::Compressed { pixels, .. } => { + Some(Self::new( + pixels.width as i32, + pixels.height as i32, + pixels.data.clone(), + )) + } + _ => None, + }) + .collect() + } + + pub fn draw_at(&self, frame: &mut [u32], palette: &Palette, pos: IVec2) { + gfx::blit(frame, palette, &self.pixels, pos, self.size.x, 0); + } + + pub fn draw_tinted(&self, frame: &mut [u32], palette: &Palette, pos: IVec2, tint: u8) { + gfx::blit(frame, palette, &self.pixels, pos, self.size.x, tint); + } + + pub fn draw_rotated( + &self, + frame: &mut [u32], + palette: &Palette, + center: Vec2, + angle: f32, + tint: u8, + ) { + gfx::blit_rotated( + frame, + palette, + &self.pixels, + center, + self.size.x, + angle, + tint, + ); + } +} diff --git a/2026/games/asrael_io/source/src/starfield.rs b/2026/games/asrael_io/source/src/starfield.rs new file mode 100644 index 0000000..ab7a199 --- /dev/null +++ b/2026/games/asrael_io/source/src/starfield.rs @@ -0,0 +1,59 @@ +use crate::color::Palette; +use crate::color::db32::{BLUE, CYAN, GRAY, LIGHT_RED, LIME, PALE_BLUE, WHITE, YELLOW}; +use crate::rng::Rng; +use crate::{GAME_H, GAME_W}; + +const STAR_COLORS: [u8; 8] = [WHITE, GRAY, PALE_BLUE, CYAN, BLUE, YELLOW, LIGHT_RED, LIME]; + +struct Star { + color: u8, + speed: f32, + x: i32, + y: f32, +} + +#[derive(Default)] +pub struct Starfield { + stars: Vec, +} + +impl Starfield { + pub fn new(count: usize, rng: &mut Rng) -> Self { + let stars = (0..count) + .map(|i| { + let speed = match i % 3 { + 0 => 0.5, + 1 => 1.0, + _ => 1.5, + }; + + Star { + color: STAR_COLORS[rng.range(STAR_COLORS.len() as u32) as usize], + speed, + x: rng.range(GAME_W) as i32, + y: rng.range(GAME_H) as f32, + } + }) + .collect(); + + Self { stars } + } + + pub fn update(&mut self) { + for star in &mut self.stars { + star.y += star.speed; + if star.y >= GAME_H as f32 { + star.y -= GAME_H as f32; + } + } + } + + pub fn draw(&self, frame: &mut [u32], palette: &Palette, a: f32) { + for star in &self.stars { + let y = star.y - star.speed * (1.0 - a); + let y = (y as i32).rem_euclid(GAME_H as i32); + + frame[(y * GAME_W as i32 + star.x) as usize] = palette.at(star.color); + } + } +} -- cgit v1.3.1