From 3460d889aa2e7af1a60d218a1a8589027b4e4e63 Mon Sep 17 00:00:00 2001 From: salmonik Date: Thu, 9 Jul 2026 23:27:55 +0200 Subject: init --- .gitignore | 1 + .zed/tasks.json | 14 + Cargo.lock | 783 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 8 + src/game.rs | 204 +++++++++++++++ src/main.rs | 90 +++++++ src/renderer.rs | 196 ++++++++++++++ src/texture.rs | 102 ++++++++ 8 files changed, 1398 insertions(+) create mode 100644 .gitignore create mode 100644 .zed/tasks.json create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/game.rs create mode 100644 src/main.rs create mode 100644 src/renderer.rs create mode 100644 src/texture.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/.zed/tasks.json b/.zed/tasks.json new file mode 100644 index 0000000..090709f --- /dev/null +++ b/.zed/tasks.json @@ -0,0 +1,14 @@ +[ + { + "label": "run release", + "command": "cargo r -r", + "show_command": false, + "show_summary": false, + }, + { + "label": "run debug", + "command": "cargo r", + "show_command": false, + "show_summary": false, + }, +] diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..1d07e99 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,783 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[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 = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "cc" +version = "1.2.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5d6cac793997bd970000024b2934968efe83b382de4fdcf4fcb46b6ee4ad996" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "dlib" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab8ecd87370524b461f8557c119c405552c396ed91fc0a8eec68679eab26f94a" +dependencies = [ + "libloading", +] + +[[package]] +name = "doom" +version = "0.1.0" +dependencies = [ + "minifb", + "rand", +] + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + +[[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 = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[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-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "libc", + "r-efi", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "js-sys" +version = "0.3.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[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.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652" +dependencies = [ + "bitflags 2.13.0", + "libc", + "plain", + "redox_syscall", +] + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + +[[package]] +name = "minifb" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c470a74618b43cd182c21b3dc1e6123501249f3bad9a0085e95d1304ca2478" +dependencies = [ + "cc", + "dlib", + "futures", + "instant", + "js-sys", + "lazy_static", + "libc", + "orbclient", + "raw-window-handle", + "serde", + "serde_derive", + "tempfile", + "wasm-bindgen-futures", + "wayland-client", + "wayland-cursor", + "wayland-protocols", + "winapi", + "x11-dl", +] + +[[package]] +name = "nix" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "orbclient" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5df339f526ea9a60e371768d50efc2f2508c7203290731565d1f7a6f71d21747" +dependencies = [ + "libc", + "libredox", + "sdl2", +] + +[[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 = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[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 = "quote" +version = "1.0.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[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.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5102a6aaa05aa011a238e178e6bca86d2cb56fc9f586d37cb80f5bca6e07759" +dependencies = [ + "bitflags 2.13.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", + "windows-sys", +] + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "sdl2" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d42407afc6a8ab67e36f92e80b8ba34cbdc55aaeed05249efe9a2e8d0e9feef" +dependencies = [ + "bitflags 1.3.2", + "lazy_static", + "libc", + "sdl2-sys", +] + +[[package]] +name = "sdl2-sys" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff61407fc75d4b0bbc93dc7e4d6c196439965fbef8e4a4f003a36095823eac0" +dependencies = [ + "cfg-if", + "libc", + "version-compare", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[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 = "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 = "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 = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.3", + "once_cell", + "rustix", + "windows-sys", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "version-compare" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasm-bindgen" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wayland-client" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715" +dependencies = [ + "bitflags 1.3.2", + "downcast-rs", + "libc", + "nix", + "scoped-tls", + "wayland-commons", + "wayland-scanner", + "wayland-sys", +] + +[[package]] +name = "wayland-commons" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902" +dependencies = [ + "nix", + "once_cell", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-cursor" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661" +dependencies = [ + "nix", + "wayland-client", + "xcursor", +] + +[[package]] +name = "wayland-protocols" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6" +dependencies = [ + "bitflags 1.3.2", + "wayland-client", + "wayland-commons", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53" +dependencies = [ + "proc-macro2", + "quote", + "xml-rs", +] + +[[package]] +name = "wayland-sys" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be12ce1a3c39ec7dba25594b97b42cb3195d54953ddb9d3d95a7c3902bc6e9d4" +dependencies = [ + "dlib", + "lazy_static", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[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 = "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 = "xcursor" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b" + +[[package]] +name = "xml-rs" +version = "0.8.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" + +[[package]] +name = "zerocopy" +version = "0.8.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..b2d135e --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "doom" +version = "0.1.0" +edition = "2021" + +[dependencies] +minifb = "0.27" +rand = "0.8" diff --git a/src/game.rs b/src/game.rs new file mode 100644 index 0000000..4bedecc --- /dev/null +++ b/src/game.rs @@ -0,0 +1,204 @@ +use rand::Rng; + +// --------------- maze grid --------------- + +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum Cell { + Wall, + Passage, + Exit, +} + +pub struct Maze { + cells: Vec, + width: usize, + height: usize, +} + +impl Maze { + /// Recursive-backtracker perfect maze. `room_w × room_h` is the logical + /// room count; the actual grid will be `(room_w*2+1) × (room_h*2+1)`. + pub fn generate(room_w: usize, room_h: usize, rng: &mut impl Rng) -> Self { + let w = room_w * 2 + 1; + let h = room_h * 2 + 1; + + let mut cells = vec![Cell::Wall; w * h]; + cells[1 * w + 1] = Cell::Passage; + + let mut stack = vec![(1usize, 1usize)]; + + while let Some(&(cy, cx)) = stack.last() { + let mut unvisited = Vec::new(); + + for (dy, dx) in [(-2isize, 0isize), (2, 0), (0, -2), (0, 2)] { + let ny = cy as isize + dy; + let nx = cx as isize + dx; + if ny >= 1 && ny < (h - 1) as isize && nx >= 1 && nx < (w - 1) as isize { + if cells[ny as usize * w + nx as usize] == Cell::Wall { + unvisited.push((ny as usize, nx as usize)); + } + } + } + + if unvisited.is_empty() { + stack.pop(); + } else { + let idx = rng.gen_range(0..unvisited.len()); + let (ny, nx) = unvisited[idx]; + + // carve the wall between current cell and chosen neighbour + let wall_y = (cy + ny) / 2; + let wall_x = (cx + nx) / 2; + cells[wall_y * w + wall_x] = Cell::Passage; + cells[ny * w + nx] = Cell::Passage; + + stack.push((ny, nx)); + } + } + + // ---- place exit in a far-away passage ---- + let mut passages: Vec<(f64, usize, usize)> = Vec::new(); + for y in (1..h).step_by(2) { + for x in (1..w).step_by(2) { + if cells[y * w + x] == Cell::Passage && !(y == 1 && x == 1) { + let dist = ((y as f64 - 1.0).powi(2) + (x as f64 - 1.0).powi(2)).sqrt(); + passages.push((dist, y, x)); + } + } + } + + if !passages.is_empty() { + passages.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap()); + let far_count = (passages.len() / 4).max(1); + let idx = rng.gen_range(0..far_count); + let (_, ey, ex) = passages[idx]; + cells[ey * w + ex] = Cell::Exit; + } + + Self { + cells, + width: w, + height: h, + } + } + + #[inline] + pub fn is_wall(&self, y: usize, x: usize) -> bool { + self.cells + .get(y * self.width + x) + .map_or(true, |&c| c == Cell::Wall) + } + + /// Floating-point query — out-of-bounds is treated as wall. + #[inline] + pub fn is_passage_at_float(&self, x: f64, y: f64) -> bool { + let xi = x as usize; + let yi = y as usize; + if xi >= self.width || yi >= self.height { + return false; + } + self.cells[yi * self.width + xi] != Cell::Wall + } + + #[inline] + pub fn is_exit(&self, x: f64, y: f64) -> bool { + let xi = x as usize; + let yi = y as usize; + if xi >= self.width || yi >= self.height { + return false; + } + self.cells[yi * self.width + xi] == Cell::Exit + } + + pub fn width(&self) -> usize { + self.width + } + pub fn height(&self) -> usize { + self.height + } +} + +// --------------- player --------------- + +pub struct Player { + pub x: f64, + pub y: f64, + pub angle: f64, // radians, 0 = +x (east), π/2 = +y (south) +} + +impl Player { + pub fn new(x: f64, y: f64, angle: f64) -> Self { + Self { x, y, angle } + } + + pub fn rotate(&mut self, delta: f64) { + self.angle = (self.angle + delta).rem_euclid(2.0 * std::f64::consts::PI); + } + + /// Apply a raw (dx, dy) displacement with wall-sliding collision. + /// Checks are done per-axis so the player slides along walls naturally. + pub fn try_move(&mut self, dx: f64, dy: f64, maze: &Maze) { + let m = 0.35; // collision margin — keeps the camera out of walls + + let nx = self.x + dx; + if maze.is_passage_at_float(nx + m, self.y + m) + && maze.is_passage_at_float(nx + m, self.y - m) + && maze.is_passage_at_float(nx - m, self.y + m) + && maze.is_passage_at_float(nx - m, self.y - m) + { + self.x = nx; + } + + let ny = self.y + dy; + if maze.is_passage_at_float(self.x + m, ny + m) + && maze.is_passage_at_float(self.x - m, ny + m) + && maze.is_passage_at_float(self.x + m, ny - m) + && maze.is_passage_at_float(self.x - m, ny - m) + { + self.y = ny; + } + } +} + +// --------------- top-level game --------------- + +pub struct Game { + pub maze: Maze, + pub player: Player, + pub wall_texture: Vec, + pub floor_texture: Vec, +} + +impl Game { + pub fn new() -> Self { + let mut rng = rand::thread_rng(); + let maze = Maze::generate(16, 16, &mut rng); // 33×33 grid + let player = Player::new(1.5, 1.5, 0.0); // start centre of (1,1), facing east + let wall_texture = crate::texture::generate_wall_texture(&mut rng); + let floor_texture = crate::texture::generate_floor_texture(&mut rng); + + Self { + maze, + player, + wall_texture, + floor_texture, + } + } + + pub fn rotate(&mut self, delta: f64) { + self.player.rotate(delta); + } + + /// Apply forward + strafe as a single combined displacement so that + /// diagonal wall-hugging is checked atomically (no two-step clip). + pub fn apply_movement(&mut self, forward: f64, strafe: f64) { + let a = self.player.angle; + let dx = a.cos() * forward + (a + std::f64::consts::FRAC_PI_2).cos() * strafe; + let dy = a.sin() * forward + (a + std::f64::consts::FRAC_PI_2).sin() * strafe; + self.player.try_move(dx, dy, &self.maze); + } + + pub fn check_win(&self) -> bool { + self.maze.is_exit(self.player.x, self.player.y) + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..d51fdfd --- /dev/null +++ b/src/main.rs @@ -0,0 +1,90 @@ +mod game; +mod renderer; +mod texture; + +use game::Game; +use minifb::{Key, Window, WindowOptions}; +use renderer::Renderer; +use std::time::Instant; + +const SCREEN_W: usize = 640; +const SCREEN_H: usize = 480; + +fn main() { + println!("=== DOOM-LIKE MAZE ==="); + println!("Find the exit!"); + println!(" W / Up — forward"); + println!(" S / Down — backward"); + println!(" A / D — strafe left / right"); + println!(" ← / → — turn"); + println!(" Esc — quit\n"); + + let mut game = Game::new(); + let renderer = Renderer::new(SCREEN_W, SCREEN_H); + + let mut window = Window::new( + "Maze — Find the Exit!", + SCREEN_W, + SCREEN_H, + WindowOptions::default(), + ) + .expect("Failed to create window"); + + let mut framebuffer = vec![0u32; SCREEN_W * SCREEN_H]; + let mut last = Instant::now(); + let frame_budget = std::time::Duration::from_micros(16600); // ~60 fps + + while window.is_open() && !window.is_key_down(Key::Escape) { + let frame_start = Instant::now(); + let dt = (frame_start - last).as_secs_f64().min(0.1); + last = frame_start; + + // ---- input ---- + let move_speed = 2.8; + let turn_speed = 2.8; + let strafe_speed = 2.2; + + if window.is_key_down(Key::Left) { + game.rotate(-turn_speed * dt); + } + if window.is_key_down(Key::Right) { + game.rotate(turn_speed * dt); + } + // Combine forward + strafe into one displacement (prevents two-step + // wall clipping when pressing W+D / W+A near a wall). + let fwd = if window.is_key_down(Key::W) || window.is_key_down(Key::Up) { + move_speed * dt + } else if window.is_key_down(Key::S) || window.is_key_down(Key::Down) { + -move_speed * dt + } else { + 0.0 + }; + let strf = if window.is_key_down(Key::D) { + strafe_speed * dt + } else if window.is_key_down(Key::A) { + -strafe_speed * dt + } else { + 0.0 + }; + game.apply_movement(fwd, strf); + + // ---- win condition ---- + if game.check_win() { + println!("🎉 You found the exit! Generating a new maze…"); + game = Game::new(); + last = Instant::now(); // avoid huge dt spike + } + + // ---- render ---- + renderer.render(&game, &mut framebuffer); + window + .update_with_buffer(&framebuffer, SCREEN_W, SCREEN_H) + .unwrap(); + + // Sleep the remainder of the frame budget (saves CPU). + let elapsed = frame_start.elapsed(); + if elapsed < frame_budget { + std::thread::sleep(frame_budget - elapsed); + } + } +} diff --git a/src/renderer.rs b/src/renderer.rs new file mode 100644 index 0000000..0792537 --- /dev/null +++ b/src/renderer.rs @@ -0,0 +1,196 @@ +use crate::game::Game; +use crate::texture::TEX_SIZE; + +pub struct Renderer { + screen_w: usize, + screen_h: usize, + /// Precomputed: floor_dist_lut[row] = perpendicular distance to floor at that row. + floor_dist_lut: Vec, + /// Precomputed: floor_bright_lut[row] = brightness × 256 for that row (fixed-point). + floor_bright_lut: Vec, +} + +impl Renderer { + pub fn new(screen_w: usize, screen_h: usize) -> Self { + let half_h = screen_h as f64 / 2.0; + let mut floor_dist_lut = vec![0.0f64; screen_h]; + let mut floor_bright_lut = vec![0u32; screen_h]; + + // Only rows below the horizon have meaningful floor values. + for py in (screen_h / 2 + 1)..screen_h { + let dy = py as f64 - half_h; + let dist = 0.5 * screen_h as f64 / dy; + floor_dist_lut[py] = dist; + let bright = (1.0 / (1.0 + dist * 0.15)).max(0.05); + floor_bright_lut[py] = (bright * 256.0) as u32; + } + + Self { + screen_w, + screen_h, + floor_dist_lut, + floor_bright_lut, + } + } + + /// Produce one frame into `buffer` (packed `0xAARRGGBB`). + pub fn render(&self, game: &Game, buffer: &mut [u32]) { + buffer.fill(0xFF00_0000); // pure-black ceiling + + let half_h = self.screen_h as f64 / 2.0; + let fov = std::f64::consts::PI / 3.0; // 60° + + for sx in 0..self.screen_w { + let cam_x = 2.0 * sx as f64 / self.screen_w as f64 - 1.0; + let ray_angle = game.player.angle + (fov / 2.0) * cam_x; + let rdx = ray_angle.cos(); + let rdy = ray_angle.sin(); + + // ---- DDA setup ------------------------------------------------- + let mut map_x = game.player.x as isize; + let mut map_y = game.player.y as isize; + + let ddx = if rdx == 0.0 { + f64::MAX + } else { + (1.0 / rdx).abs() + }; + let ddy = if rdy == 0.0 { + f64::MAX + } else { + (1.0 / rdy).abs() + }; + + let (step_x, mut side_x) = if rdx < 0.0 { + (-1isize, (game.player.x - map_x as f64) * ddx) + } else { + (1isize, (map_x as f64 + 1.0 - game.player.x) * ddx) + }; + + let (step_y, mut side_y) = if rdy < 0.0 { + (-1isize, (game.player.y - map_y as f64) * ddy) + } else { + (1isize, (map_y as f64 + 1.0 - game.player.y) * ddy) + }; + + let mut hit_side: bool; + let euclidean: f64; + + // ---- DDA loop -------------------------------------------------- + loop { + if side_x < side_y { + side_x += ddx; + map_x += step_x; + hit_side = false; + } else { + side_y += ddy; + map_y += step_y; + hit_side = true; + } + + if map_y < 0 + || map_y >= game.maze.height() as isize + || map_x < 0 + || map_x >= game.maze.width() as isize + { + euclidean = 1.0; + break; + } + + if game.maze.is_wall(map_y as usize, map_x as usize) { + euclidean = if !hit_side { + (map_x as f64 - game.player.x + (1.0 - step_x as f64) / 2.0) / rdx + } else { + (map_y as f64 - game.player.y + (1.0 - step_y as f64) / 2.0) / rdy + }; + break; + } + } + + // Fisheye correction + let perp_dist = (euclidean * (ray_angle - game.player.angle).cos().abs()).max(0.001); + + // ---- wall slice ------------------------------------------------ + let wall_h = (self.screen_h as f64 / perp_dist) as isize; + let draw_start = ((-wall_h / 2) + self.screen_h as isize / 2).max(0) as usize; + let draw_end = ((wall_h / 2) + self.screen_h as isize / 2) + .min(self.screen_h as isize - 1) as usize; + + // Texture x-coordinate (uses Euclidean, not perp) + let wall_x = if !hit_side { + game.player.y + euclidean * rdy + } else { + game.player.x + euclidean * rdx + }; + let wall_x = wall_x - wall_x.floor(); + let tex_x = ((wall_x * TEX_SIZE as f64) as usize).min(TEX_SIZE - 1); + + // Hoist: brightness is identical for every pixel in this column + let wall_bright_fx = { + let b = (1.0 / (1.0 + perp_dist * 0.12)).max(0.08); + (b * 256.0) as u32 + }; + + for py in draw_start..=draw_end { + let tex_y = ((py as f64 - half_h + wall_h as f64 / 2.0) * TEX_SIZE as f64 + / wall_h as f64) as usize; + let tex_y = tex_y.min(TEX_SIZE - 1); + + let color = game.wall_texture[tex_y * TEX_SIZE + tex_x]; + buffer[py * self.screen_w + sx] = darken_fx(color, wall_bright_fx); + } + + // ---- floor below the wall -------------------------------------- + let floor_start = draw_end + 1; + if floor_start < self.screen_h { + for py in floor_start..self.screen_h { + let floor_dist = self.floor_dist_lut[py]; + + if floor_dist > 15.0 { + // Far floor → just a dark pixel, skip texture & world calc. + let gray = darken_fx(0xFF80_8080, self.floor_bright_lut[py]); + buffer[py * self.screen_w + sx] = gray; + } else { + let fx = game.player.x + floor_dist * rdx; + let fy = game.player.y + floor_dist * rdy; + + let ftx = ((fx.fract().abs() * TEX_SIZE as f64) as usize).min(TEX_SIZE - 1); + let fty = ((fy.fract().abs() * TEX_SIZE as f64) as usize).min(TEX_SIZE - 1); + + let color = game.floor_texture[fty * TEX_SIZE + ftx]; + buffer[py * self.screen_w + sx] = + darken_fx(color, self.floor_bright_lut[py]); + } + } + } + } + + // ---- crosshair ----------------------------------------------------- + let cx = self.screen_w / 2; + let cy = self.screen_h / 2; + let cross_color = 0xFF00_FF00; + for i in 0..=4isize { + if cy as isize - i >= 0 { + buffer[(cy - i as usize) * self.screen_w + cx] = cross_color; + } + if (cy + i as usize) < self.screen_h { + buffer[(cy + i as usize) * self.screen_w + cx] = cross_color; + } + if cx as isize - i >= 0 { + buffer[cy * self.screen_w + (cx - i as usize)] = cross_color; + } + if (cx + i as usize) < self.screen_w { + buffer[cy * self.screen_w + (cx + i as usize)] = cross_color; + } + } + } +} + +/// Integer darken: `bright_fx` is brightness × 256 in fixed-point. +#[inline(always)] +fn darken_fx(color: u32, bright_fx: u32) -> u32 { + let r = (((color >> 16) & 0xFF) * bright_fx) >> 8; + let g = (((color >> 8) & 0xFF) * bright_fx) >> 8; + let b = ((color & 0xFF) * bright_fx) >> 8; + 0xFF00_0000 | (r << 16) | (g << 8) | b +} diff --git a/src/texture.rs b/src/texture.rs new file mode 100644 index 0000000..bdbb15c --- /dev/null +++ b/src/texture.rs @@ -0,0 +1,102 @@ +use rand::Rng; + +pub const TEX_SIZE: usize = 64; + +/// Deterministic hash for coherent procedural noise. +fn hash(x: u32, y: u32, seed: u64) -> u64 { + let mut h = (x as u64).wrapping_mul(0x9e3779b9); + h = h.wrapping_add((y as u64).wrapping_mul(0x517cc1b727220a95)); + h = h.wrapping_add(seed); + h = (h ^ (h >> 33)).wrapping_mul(0xc6a4a7935bd1e995); + h = (h ^ (h >> 29)).wrapping_mul(0x5bd1e995); + h +} + +/// Brown cracked wall texture — 64×64, brick pattern with mortar lines and fissures. +pub fn generate_wall_texture(rng: &mut impl Rng) -> Vec { + let seed = rng.gen::(); + let mut tex = vec![0u32; TEX_SIZE * TEX_SIZE]; + + for y in 0..TEX_SIZE { + for x in 0..TEX_SIZE { + let h = hash(x as u32, y as u32, seed); + + let r = 105u8.wrapping_add((h % 50) as u8); + let g = 55u8.wrapping_add(((h >> 8) % 35) as u8); + let b = 22u8.wrapping_add(((h >> 16) % 25) as u8); + + // Brick mortar every 16 pixels + let mortar = y % 16 == 0 || y % 16 == 15; + + let (r, g, b) = if mortar { + (42, 20, 8) + } else { + let crack_hash = hash(x.wrapping_add(1000) as u32, y as u32, seed); + let crack_val = (crack_hash % 100) as u8; + if crack_val < 8 { + // dark crack + let factor = 0.30 + (crack_val as f64 / 8.0) * 0.25; + ( + (r as f64 * factor) as u8, + (g as f64 * factor) as u8, + (b as f64 * factor) as u8, + ) + } else if crack_val < 14 { + // light crack highlight + ( + (r.saturating_add(25)).min(255), + (g.saturating_add(18)).min(255), + (b.saturating_add(10)).min(255), + ) + } else { + (r, g, b) + } + }; + + tex[y * TEX_SIZE + x] = + 0xFF00_0000 | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32); + } + } + tex +} + +/// Gray stone-tile floor — 64×64, checkerboard with grout and subtle colour tint. +pub fn generate_floor_texture(rng: &mut impl Rng) -> Vec { + let seed = rng.gen::(); + let mut tex = vec![0u32; TEX_SIZE * TEX_SIZE]; + + for y in 0..TEX_SIZE { + for x in 0..TEX_SIZE { + let h = hash(x as u32, y as u32, seed); + let h2 = hash(x.wrapping_add(2000) as u32, y.wrapping_add(2000) as u32, seed); + + let tile_x = x / 16; + let tile_y = y / 16; + let is_light_tile = (tile_x + tile_y) % 2 == 0; + + let local_x = x % 16; + let local_y = y % 16; + let is_grout = local_x == 0 || local_y == 0; + + let base_gray: u8 = if is_light_tile { 115 } else { 95 }; + let noise = (h % 15) as u8; + + let tint_r = (h2 % 8) as i16 - 4i16; + let tint_b = ((h2 >> 8) % 8) as i16 - 4i16; + + let gray = if is_grout { + base_gray.saturating_sub(30).saturating_add(noise / 3) + } else { + base_gray.saturating_add(noise / 2) + }; + + let r = (gray as i16 + tint_r).clamp(0, 255) as u8; + let g = gray; + let b = (gray as i16 + tint_b).clamp(0, 255) as u8; + + tex[y * TEX_SIZE + x] = + 0xFF00_0000 | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32); + } + } + tex +} -- cgit v1.2.3