summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsalmonik <raisersoft@proton.me>2026-07-09 23:27:55 +0200
committersalmonik <raisersoft@proton.me>2026-07-09 23:27:55 +0200
commit3460d889aa2e7af1a60d218a1a8589027b4e4e63 (patch)
tree89e66acd05fba086835337bfa95b36710dc8c4da
downloaddungeon-3460d889aa2e7af1a60d218a1a8589027b4e4e63.tar.gz
dungeon-3460d889aa2e7af1a60d218a1a8589027b4e4e63.zip
init
-rw-r--r--.gitignore1
-rw-r--r--.zed/tasks.json14
-rw-r--r--Cargo.lock783
-rw-r--r--Cargo.toml8
-rw-r--r--src/game.rs204
-rw-r--r--src/main.rs90
-rw-r--r--src/renderer.rs196
-rw-r--r--src/texture.rs102
8 files changed, 1398 insertions, 0 deletions
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 @@
1[
2 {
3 "label": "run release",
4 "command": "cargo r -r",
5 "show_command": false,
6 "show_summary": false,
7 },
8 {
9 "label": "run debug",
10 "command": "cargo r",
11 "show_command": false,
12 "show_summary": false,
13 },
14]
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..1d07e99
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,783 @@
1# This file is automatically @generated by Cargo.
2# It is not intended for manual editing.
3version = 4
4
5[[package]]
6name = "autocfg"
7version = "1.5.1"
8source = "registry+https://github.com/rust-lang/crates.io-index"
9checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
10
11[[package]]
12name = "bitflags"
13version = "1.3.2"
14source = "registry+https://github.com/rust-lang/crates.io-index"
15checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
16
17[[package]]
18name = "bitflags"
19version = "2.13.0"
20source = "registry+https://github.com/rust-lang/crates.io-index"
21checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
22
23[[package]]
24name = "bumpalo"
25version = "3.20.3"
26source = "registry+https://github.com/rust-lang/crates.io-index"
27checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
28
29[[package]]
30name = "cc"
31version = "1.2.66"
32source = "registry+https://github.com/rust-lang/crates.io-index"
33checksum = "f5d6cac793997bd970000024b2934968efe83b382de4fdcf4fcb46b6ee4ad996"
34dependencies = [
35 "find-msvc-tools",
36 "shlex",
37]
38
39[[package]]
40name = "cfg-if"
41version = "1.0.4"
42source = "registry+https://github.com/rust-lang/crates.io-index"
43checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
44
45[[package]]
46name = "dlib"
47version = "0.5.3"
48source = "registry+https://github.com/rust-lang/crates.io-index"
49checksum = "ab8ecd87370524b461f8557c119c405552c396ed91fc0a8eec68679eab26f94a"
50dependencies = [
51 "libloading",
52]
53
54[[package]]
55name = "doom"
56version = "0.1.0"
57dependencies = [
58 "minifb",
59 "rand",
60]
61
62[[package]]
63name = "downcast-rs"
64version = "1.2.1"
65source = "registry+https://github.com/rust-lang/crates.io-index"
66checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
67
68[[package]]
69name = "errno"
70version = "0.3.14"
71source = "registry+https://github.com/rust-lang/crates.io-index"
72checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
73dependencies = [
74 "libc",
75 "windows-sys",
76]
77
78[[package]]
79name = "fastrand"
80version = "2.4.1"
81source = "registry+https://github.com/rust-lang/crates.io-index"
82checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
83
84[[package]]
85name = "find-msvc-tools"
86version = "0.1.9"
87source = "registry+https://github.com/rust-lang/crates.io-index"
88checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
89
90[[package]]
91name = "futures"
92version = "0.3.32"
93source = "registry+https://github.com/rust-lang/crates.io-index"
94checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d"
95dependencies = [
96 "futures-channel",
97 "futures-core",
98 "futures-executor",
99 "futures-io",
100 "futures-sink",
101 "futures-task",
102 "futures-util",
103]
104
105[[package]]
106name = "futures-channel"
107version = "0.3.32"
108source = "registry+https://github.com/rust-lang/crates.io-index"
109checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
110dependencies = [
111 "futures-core",
112 "futures-sink",
113]
114
115[[package]]
116name = "futures-core"
117version = "0.3.32"
118source = "registry+https://github.com/rust-lang/crates.io-index"
119checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
120
121[[package]]
122name = "futures-executor"
123version = "0.3.32"
124source = "registry+https://github.com/rust-lang/crates.io-index"
125checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d"
126dependencies = [
127 "futures-core",
128 "futures-task",
129 "futures-util",
130]
131
132[[package]]
133name = "futures-io"
134version = "0.3.32"
135source = "registry+https://github.com/rust-lang/crates.io-index"
136checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718"
137
138[[package]]
139name = "futures-macro"
140version = "0.3.32"
141source = "registry+https://github.com/rust-lang/crates.io-index"
142checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b"
143dependencies = [
144 "proc-macro2",
145 "quote",
146 "syn",
147]
148
149[[package]]
150name = "futures-sink"
151version = "0.3.32"
152source = "registry+https://github.com/rust-lang/crates.io-index"
153checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893"
154
155[[package]]
156name = "futures-task"
157version = "0.3.32"
158source = "registry+https://github.com/rust-lang/crates.io-index"
159checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
160
161[[package]]
162name = "futures-util"
163version = "0.3.32"
164source = "registry+https://github.com/rust-lang/crates.io-index"
165checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
166dependencies = [
167 "futures-channel",
168 "futures-core",
169 "futures-io",
170 "futures-macro",
171 "futures-sink",
172 "futures-task",
173 "memchr",
174 "pin-project-lite",
175 "slab",
176]
177
178[[package]]
179name = "getrandom"
180version = "0.2.17"
181source = "registry+https://github.com/rust-lang/crates.io-index"
182checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
183dependencies = [
184 "cfg-if",
185 "libc",
186 "wasi",
187]
188
189[[package]]
190name = "getrandom"
191version = "0.4.3"
192source = "registry+https://github.com/rust-lang/crates.io-index"
193checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
194dependencies = [
195 "cfg-if",
196 "libc",
197 "r-efi",
198]
199
200[[package]]
201name = "instant"
202version = "0.1.13"
203source = "registry+https://github.com/rust-lang/crates.io-index"
204checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222"
205dependencies = [
206 "cfg-if",
207 "js-sys",
208 "wasm-bindgen",
209 "web-sys",
210]
211
212[[package]]
213name = "js-sys"
214version = "0.3.103"
215source = "registry+https://github.com/rust-lang/crates.io-index"
216checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102"
217dependencies = [
218 "cfg-if",
219 "futures-util",
220 "wasm-bindgen",
221]
222
223[[package]]
224name = "lazy_static"
225version = "1.5.0"
226source = "registry+https://github.com/rust-lang/crates.io-index"
227checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
228
229[[package]]
230name = "libc"
231version = "0.2.186"
232source = "registry+https://github.com/rust-lang/crates.io-index"
233checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
234
235[[package]]
236name = "libloading"
237version = "0.8.9"
238source = "registry+https://github.com/rust-lang/crates.io-index"
239checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
240dependencies = [
241 "cfg-if",
242 "windows-link",
243]
244
245[[package]]
246name = "libredox"
247version = "0.1.18"
248source = "registry+https://github.com/rust-lang/crates.io-index"
249checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652"
250dependencies = [
251 "bitflags 2.13.0",
252 "libc",
253 "plain",
254 "redox_syscall",
255]
256
257[[package]]
258name = "linux-raw-sys"
259version = "0.12.1"
260source = "registry+https://github.com/rust-lang/crates.io-index"
261checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
262
263[[package]]
264name = "memchr"
265version = "2.8.3"
266source = "registry+https://github.com/rust-lang/crates.io-index"
267checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
268
269[[package]]
270name = "memoffset"
271version = "0.6.5"
272source = "registry+https://github.com/rust-lang/crates.io-index"
273checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
274dependencies = [
275 "autocfg",
276]
277
278[[package]]
279name = "minifb"
280version = "0.27.0"
281source = "registry+https://github.com/rust-lang/crates.io-index"
282checksum = "b0c470a74618b43cd182c21b3dc1e6123501249f3bad9a0085e95d1304ca2478"
283dependencies = [
284 "cc",
285 "dlib",
286 "futures",
287 "instant",
288 "js-sys",
289 "lazy_static",
290 "libc",
291 "orbclient",
292 "raw-window-handle",
293 "serde",
294 "serde_derive",
295 "tempfile",
296 "wasm-bindgen-futures",
297 "wayland-client",
298 "wayland-cursor",
299 "wayland-protocols",
300 "winapi",
301 "x11-dl",
302]
303
304[[package]]
305name = "nix"
306version = "0.24.3"
307source = "registry+https://github.com/rust-lang/crates.io-index"
308checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069"
309dependencies = [
310 "bitflags 1.3.2",
311 "cfg-if",
312 "libc",
313 "memoffset",
314]
315
316[[package]]
317name = "once_cell"
318version = "1.21.4"
319source = "registry+https://github.com/rust-lang/crates.io-index"
320checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
321
322[[package]]
323name = "orbclient"
324version = "0.3.55"
325source = "registry+https://github.com/rust-lang/crates.io-index"
326checksum = "5df339f526ea9a60e371768d50efc2f2508c7203290731565d1f7a6f71d21747"
327dependencies = [
328 "libc",
329 "libredox",
330 "sdl2",
331]
332
333[[package]]
334name = "pin-project-lite"
335version = "0.2.17"
336source = "registry+https://github.com/rust-lang/crates.io-index"
337checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
338
339[[package]]
340name = "pkg-config"
341version = "0.3.33"
342source = "registry+https://github.com/rust-lang/crates.io-index"
343checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
344
345[[package]]
346name = "plain"
347version = "0.2.3"
348source = "registry+https://github.com/rust-lang/crates.io-index"
349checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
350
351[[package]]
352name = "ppv-lite86"
353version = "0.2.21"
354source = "registry+https://github.com/rust-lang/crates.io-index"
355checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
356dependencies = [
357 "zerocopy",
358]
359
360[[package]]
361name = "proc-macro2"
362version = "1.0.106"
363source = "registry+https://github.com/rust-lang/crates.io-index"
364checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
365dependencies = [
366 "unicode-ident",
367]
368
369[[package]]
370name = "quote"
371version = "1.0.46"
372source = "registry+https://github.com/rust-lang/crates.io-index"
373checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368"
374dependencies = [
375 "proc-macro2",
376]
377
378[[package]]
379name = "r-efi"
380version = "6.0.0"
381source = "registry+https://github.com/rust-lang/crates.io-index"
382checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
383
384[[package]]
385name = "rand"
386version = "0.8.6"
387source = "registry+https://github.com/rust-lang/crates.io-index"
388checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a"
389dependencies = [
390 "libc",
391 "rand_chacha",
392 "rand_core",
393]
394
395[[package]]
396name = "rand_chacha"
397version = "0.3.1"
398source = "registry+https://github.com/rust-lang/crates.io-index"
399checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
400dependencies = [
401 "ppv-lite86",
402 "rand_core",
403]
404
405[[package]]
406name = "rand_core"
407version = "0.6.4"
408source = "registry+https://github.com/rust-lang/crates.io-index"
409checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
410dependencies = [
411 "getrandom 0.2.17",
412]
413
414[[package]]
415name = "raw-window-handle"
416version = "0.6.2"
417source = "registry+https://github.com/rust-lang/crates.io-index"
418checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539"
419
420[[package]]
421name = "redox_syscall"
422version = "0.9.0"
423source = "registry+https://github.com/rust-lang/crates.io-index"
424checksum = "c5102a6aaa05aa011a238e178e6bca86d2cb56fc9f586d37cb80f5bca6e07759"
425dependencies = [
426 "bitflags 2.13.0",
427]
428
429[[package]]
430name = "rustix"
431version = "1.1.4"
432source = "registry+https://github.com/rust-lang/crates.io-index"
433checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
434dependencies = [
435 "bitflags 2.13.0",
436 "errno",
437 "libc",
438 "linux-raw-sys",
439 "windows-sys",
440]
441
442[[package]]
443name = "rustversion"
444version = "1.0.23"
445source = "registry+https://github.com/rust-lang/crates.io-index"
446checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
447
448[[package]]
449name = "scoped-tls"
450version = "1.0.1"
451source = "registry+https://github.com/rust-lang/crates.io-index"
452checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
453
454[[package]]
455name = "sdl2"
456version = "0.38.0"
457source = "registry+https://github.com/rust-lang/crates.io-index"
458checksum = "2d42407afc6a8ab67e36f92e80b8ba34cbdc55aaeed05249efe9a2e8d0e9feef"
459dependencies = [
460 "bitflags 1.3.2",
461 "lazy_static",
462 "libc",
463 "sdl2-sys",
464]
465
466[[package]]
467name = "sdl2-sys"
468version = "0.38.0"
469source = "registry+https://github.com/rust-lang/crates.io-index"
470checksum = "3ff61407fc75d4b0bbc93dc7e4d6c196439965fbef8e4a4f003a36095823eac0"
471dependencies = [
472 "cfg-if",
473 "libc",
474 "version-compare",
475]
476
477[[package]]
478name = "serde"
479version = "1.0.228"
480source = "registry+https://github.com/rust-lang/crates.io-index"
481checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
482dependencies = [
483 "serde_core",
484 "serde_derive",
485]
486
487[[package]]
488name = "serde_core"
489version = "1.0.228"
490source = "registry+https://github.com/rust-lang/crates.io-index"
491checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
492dependencies = [
493 "serde_derive",
494]
495
496[[package]]
497name = "serde_derive"
498version = "1.0.228"
499source = "registry+https://github.com/rust-lang/crates.io-index"
500checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
501dependencies = [
502 "proc-macro2",
503 "quote",
504 "syn",
505]
506
507[[package]]
508name = "shlex"
509version = "2.0.1"
510source = "registry+https://github.com/rust-lang/crates.io-index"
511checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
512
513[[package]]
514name = "slab"
515version = "0.4.12"
516source = "registry+https://github.com/rust-lang/crates.io-index"
517checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
518
519[[package]]
520name = "smallvec"
521version = "1.15.2"
522source = "registry+https://github.com/rust-lang/crates.io-index"
523checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90"
524
525[[package]]
526name = "syn"
527version = "2.0.118"
528source = "registry+https://github.com/rust-lang/crates.io-index"
529checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
530dependencies = [
531 "proc-macro2",
532 "quote",
533 "unicode-ident",
534]
535
536[[package]]
537name = "tempfile"
538version = "3.27.0"
539source = "registry+https://github.com/rust-lang/crates.io-index"
540checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
541dependencies = [
542 "fastrand",
543 "getrandom 0.4.3",
544 "once_cell",
545 "rustix",
546 "windows-sys",
547]
548
549[[package]]
550name = "unicode-ident"
551version = "1.0.24"
552source = "registry+https://github.com/rust-lang/crates.io-index"
553checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
554
555[[package]]
556name = "version-compare"
557version = "0.1.1"
558source = "registry+https://github.com/rust-lang/crates.io-index"
559checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29"
560
561[[package]]
562name = "wasi"
563version = "0.11.1+wasi-snapshot-preview1"
564source = "registry+https://github.com/rust-lang/crates.io-index"
565checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
566
567[[package]]
568name = "wasm-bindgen"
569version = "0.2.126"
570source = "registry+https://github.com/rust-lang/crates.io-index"
571checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4"
572dependencies = [
573 "cfg-if",
574 "once_cell",
575 "rustversion",
576 "wasm-bindgen-macro",
577 "wasm-bindgen-shared",
578]
579
580[[package]]
581name = "wasm-bindgen-futures"
582version = "0.4.76"
583source = "registry+https://github.com/rust-lang/crates.io-index"
584checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d"
585dependencies = [
586 "js-sys",
587 "wasm-bindgen",
588]
589
590[[package]]
591name = "wasm-bindgen-macro"
592version = "0.2.126"
593source = "registry+https://github.com/rust-lang/crates.io-index"
594checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1"
595dependencies = [
596 "quote",
597 "wasm-bindgen-macro-support",
598]
599
600[[package]]
601name = "wasm-bindgen-macro-support"
602version = "0.2.126"
603source = "registry+https://github.com/rust-lang/crates.io-index"
604checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e"
605dependencies = [
606 "bumpalo",
607 "proc-macro2",
608 "quote",
609 "syn",
610 "wasm-bindgen-shared",
611]
612
613[[package]]
614name = "wasm-bindgen-shared"
615version = "0.2.126"
616source = "registry+https://github.com/rust-lang/crates.io-index"
617checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24"
618dependencies = [
619 "unicode-ident",
620]
621
622[[package]]
623name = "wayland-client"
624version = "0.29.5"
625source = "registry+https://github.com/rust-lang/crates.io-index"
626checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715"
627dependencies = [
628 "bitflags 1.3.2",
629 "downcast-rs",
630 "libc",
631 "nix",
632 "scoped-tls",
633 "wayland-commons",
634 "wayland-scanner",
635 "wayland-sys",
636]
637
638[[package]]
639name = "wayland-commons"
640version = "0.29.5"
641source = "registry+https://github.com/rust-lang/crates.io-index"
642checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902"
643dependencies = [
644 "nix",
645 "once_cell",
646 "smallvec",
647 "wayland-sys",
648]
649
650[[package]]
651name = "wayland-cursor"
652version = "0.29.5"
653source = "registry+https://github.com/rust-lang/crates.io-index"
654checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661"
655dependencies = [
656 "nix",
657 "wayland-client",
658 "xcursor",
659]
660
661[[package]]
662name = "wayland-protocols"
663version = "0.29.5"
664source = "registry+https://github.com/rust-lang/crates.io-index"
665checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6"
666dependencies = [
667 "bitflags 1.3.2",
668 "wayland-client",
669 "wayland-commons",
670 "wayland-scanner",
671]
672
673[[package]]
674name = "wayland-scanner"
675version = "0.29.5"
676source = "registry+https://github.com/rust-lang/crates.io-index"
677checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53"
678dependencies = [
679 "proc-macro2",
680 "quote",
681 "xml-rs",
682]
683
684[[package]]
685name = "wayland-sys"
686version = "0.29.5"
687source = "registry+https://github.com/rust-lang/crates.io-index"
688checksum = "be12ce1a3c39ec7dba25594b97b42cb3195d54953ddb9d3d95a7c3902bc6e9d4"
689dependencies = [
690 "dlib",
691 "lazy_static",
692 "pkg-config",
693]
694
695[[package]]
696name = "web-sys"
697version = "0.3.103"
698source = "registry+https://github.com/rust-lang/crates.io-index"
699checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141"
700dependencies = [
701 "js-sys",
702 "wasm-bindgen",
703]
704
705[[package]]
706name = "winapi"
707version = "0.3.9"
708source = "registry+https://github.com/rust-lang/crates.io-index"
709checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
710dependencies = [
711 "winapi-i686-pc-windows-gnu",
712 "winapi-x86_64-pc-windows-gnu",
713]
714
715[[package]]
716name = "winapi-i686-pc-windows-gnu"
717version = "0.4.0"
718source = "registry+https://github.com/rust-lang/crates.io-index"
719checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
720
721[[package]]
722name = "winapi-x86_64-pc-windows-gnu"
723version = "0.4.0"
724source = "registry+https://github.com/rust-lang/crates.io-index"
725checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
726
727[[package]]
728name = "windows-link"
729version = "0.2.1"
730source = "registry+https://github.com/rust-lang/crates.io-index"
731checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
732
733[[package]]
734name = "windows-sys"
735version = "0.61.2"
736source = "registry+https://github.com/rust-lang/crates.io-index"
737checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
738dependencies = [
739 "windows-link",
740]
741
742[[package]]
743name = "x11-dl"
744version = "2.21.0"
745source = "registry+https://github.com/rust-lang/crates.io-index"
746checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f"
747dependencies = [
748 "libc",
749 "once_cell",
750 "pkg-config",
751]
752
753[[package]]
754name = "xcursor"
755version = "0.3.10"
756source = "registry+https://github.com/rust-lang/crates.io-index"
757checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b"
758
759[[package]]
760name = "xml-rs"
761version = "0.8.28"
762source = "registry+https://github.com/rust-lang/crates.io-index"
763checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f"
764
765[[package]]
766name = "zerocopy"
767version = "0.8.54"
768source = "registry+https://github.com/rust-lang/crates.io-index"
769checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19"
770dependencies = [
771 "zerocopy-derive",
772]
773
774[[package]]
775name = "zerocopy-derive"
776version = "0.8.54"
777source = "registry+https://github.com/rust-lang/crates.io-index"
778checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5"
779dependencies = [
780 "proc-macro2",
781 "quote",
782 "syn",
783]
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..b2d135e
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,8 @@
1[package]
2name = "doom"
3version = "0.1.0"
4edition = "2021"
5
6[dependencies]
7minifb = "0.27"
8rand = "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 @@
1use rand::Rng;
2
3// --------------- maze grid ---------------
4
5#[derive(Clone, Copy, PartialEq, Eq)]
6pub enum Cell {
7 Wall,
8 Passage,
9 Exit,
10}
11
12pub struct Maze {
13 cells: Vec<Cell>,
14 width: usize,
15 height: usize,
16}
17
18impl Maze {
19 /// Recursive-backtracker perfect maze. `room_w × room_h` is the logical
20 /// room count; the actual grid will be `(room_w*2+1) × (room_h*2+1)`.
21 pub fn generate(room_w: usize, room_h: usize, rng: &mut impl Rng) -> Self {
22 let w = room_w * 2 + 1;
23 let h = room_h * 2 + 1;
24
25 let mut cells = vec![Cell::Wall; w * h];
26 cells[1 * w + 1] = Cell::Passage;
27
28 let mut stack = vec![(1usize, 1usize)];
29
30 while let Some(&(cy, cx)) = stack.last() {
31 let mut unvisited = Vec::new();
32
33 for (dy, dx) in [(-2isize, 0isize), (2, 0), (0, -2), (0, 2)] {
34 let ny = cy as isize + dy;
35 let nx = cx as isize + dx;
36 if ny >= 1 && ny < (h - 1) as isize && nx >= 1 && nx < (w - 1) as isize {
37 if cells[ny as usize * w + nx as usize] == Cell::Wall {
38 unvisited.push((ny as usize, nx as usize));
39 }
40 }
41 }
42
43 if unvisited.is_empty() {
44 stack.pop();
45 } else {
46 let idx = rng.gen_range(0..unvisited.len());
47 let (ny, nx) = unvisited[idx];
48
49 // carve the wall between current cell and chosen neighbour
50 let wall_y = (cy + ny) / 2;
51 let wall_x = (cx + nx) / 2;
52 cells[wall_y * w + wall_x] = Cell::Passage;
53 cells[ny * w + nx] = Cell::Passage;
54
55 stack.push((ny, nx));
56 }
57 }
58
59 // ---- place exit in a far-away passage ----
60 let mut passages: Vec<(f64, usize, usize)> = Vec::new();
61 for y in (1..h).step_by(2) {
62 for x in (1..w).step_by(2) {
63 if cells[y * w + x] == Cell::Passage && !(y == 1 && x == 1) {
64 let dist = ((y as f64 - 1.0).powi(2) + (x as f64 - 1.0).powi(2)).sqrt();
65 passages.push((dist, y, x));
66 }
67 }
68 }
69
70 if !passages.is_empty() {
71 passages.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap());
72 let far_count = (passages.len() / 4).max(1);
73 let idx = rng.gen_range(0..far_count);
74 let (_, ey, ex) = passages[idx];
75 cells[ey * w + ex] = Cell::Exit;
76 }
77
78 Self {
79 cells,
80 width: w,
81 height: h,
82 }
83 }
84
85 #[inline]
86 pub fn is_wall(&self, y: usize, x: usize) -> bool {
87 self.cells
88 .get(y * self.width + x)
89 .map_or(true, |&c| c == Cell::Wall)
90 }
91
92 /// Floating-point query — out-of-bounds is treated as wall.
93 #[inline]
94 pub fn is_passage_at_float(&self, x: f64, y: f64) -> bool {
95 let xi = x as usize;
96 let yi = y as usize;
97 if xi >= self.width || yi >= self.height {
98 return false;
99 }
100 self.cells[yi * self.width + xi] != Cell::Wall
101 }
102
103 #[inline]
104 pub fn is_exit(&self, x: f64, y: f64) -> bool {
105 let xi = x as usize;
106 let yi = y as usize;
107 if xi >= self.width || yi >= self.height {
108 return false;
109 }
110 self.cells[yi * self.width + xi] == Cell::Exit
111 }
112
113 pub fn width(&self) -> usize {
114 self.width
115 }
116 pub fn height(&self) -> usize {
117 self.height
118 }
119}
120
121// --------------- player ---------------
122
123pub struct Player {
124 pub x: f64,
125 pub y: f64,
126 pub angle: f64, // radians, 0 = +x (east), π/2 = +y (south)
127}
128
129impl Player {
130 pub fn new(x: f64, y: f64, angle: f64) -> Self {
131 Self { x, y, angle }
132 }
133
134 pub fn rotate(&mut self, delta: f64) {
135 self.angle = (self.angle + delta).rem_euclid(2.0 * std::f64::consts::PI);
136 }
137
138 /// Apply a raw (dx, dy) displacement with wall-sliding collision.
139 /// Checks are done per-axis so the player slides along walls naturally.
140 pub fn try_move(&mut self, dx: f64, dy: f64, maze: &Maze) {
141 let m = 0.35; // collision margin — keeps the camera out of walls
142
143 let nx = self.x + dx;
144 if maze.is_passage_at_float(nx + m, self.y + m)
145 && maze.is_passage_at_float(nx + m, self.y - m)
146 && maze.is_passage_at_float(nx - m, self.y + m)
147 && maze.is_passage_at_float(nx - m, self.y - m)
148 {
149 self.x = nx;
150 }
151
152 let ny = self.y + dy;
153 if maze.is_passage_at_float(self.x + m, ny + m)
154 && maze.is_passage_at_float(self.x - m, ny + m)
155 && maze.is_passage_at_float(self.x + m, ny - m)
156 && maze.is_passage_at_float(self.x - m, ny - m)
157 {
158 self.y = ny;
159 }
160 }
161}
162
163// --------------- top-level game ---------------
164
165pub struct Game {
166 pub maze: Maze,
167 pub player: Player,
168 pub wall_texture: Vec<u32>,
169 pub floor_texture: Vec<u32>,
170}
171
172impl Game {
173 pub fn new() -> Self {
174 let mut rng = rand::thread_rng();
175 let maze = Maze::generate(16, 16, &mut rng); // 33×33 grid
176 let player = Player::new(1.5, 1.5, 0.0); // start centre of (1,1), facing east
177 let wall_texture = crate::texture::generate_wall_texture(&mut rng);
178 let floor_texture = crate::texture::generate_floor_texture(&mut rng);
179
180 Self {
181 maze,
182 player,
183 wall_texture,
184 floor_texture,
185 }
186 }
187
188 pub fn rotate(&mut self, delta: f64) {
189 self.player.rotate(delta);
190 }
191
192 /// Apply forward + strafe as a single combined displacement so that
193 /// diagonal wall-hugging is checked atomically (no two-step clip).
194 pub fn apply_movement(&mut self, forward: f64, strafe: f64) {
195 let a = self.player.angle;
196 let dx = a.cos() * forward + (a + std::f64::consts::FRAC_PI_2).cos() * strafe;
197 let dy = a.sin() * forward + (a + std::f64::consts::FRAC_PI_2).sin() * strafe;
198 self.player.try_move(dx, dy, &self.maze);
199 }
200
201 pub fn check_win(&self) -> bool {
202 self.maze.is_exit(self.player.x, self.player.y)
203 }
204}
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 @@
1mod game;
2mod renderer;
3mod texture;
4
5use game::Game;
6use minifb::{Key, Window, WindowOptions};
7use renderer::Renderer;
8use std::time::Instant;
9
10const SCREEN_W: usize = 640;
11const SCREEN_H: usize = 480;
12
13fn main() {
14 println!("=== DOOM-LIKE MAZE ===");
15 println!("Find the exit!");
16 println!(" W / Up — forward");
17 println!(" S / Down — backward");
18 println!(" A / D — strafe left / right");
19 println!(" ← / → — turn");
20 println!(" Esc — quit\n");
21
22 let mut game = Game::new();
23 let renderer = Renderer::new(SCREEN_W, SCREEN_H);
24
25 let mut window = Window::new(
26 "Maze — Find the Exit!",
27 SCREEN_W,
28 SCREEN_H,
29 WindowOptions::default(),
30 )
31 .expect("Failed to create window");
32
33 let mut framebuffer = vec![0u32; SCREEN_W * SCREEN_H];
34 let mut last = Instant::now();
35 let frame_budget = std::time::Duration::from_micros(16600); // ~60 fps
36
37 while window.is_open() && !window.is_key_down(Key::Escape) {
38 let frame_start = Instant::now();
39 let dt = (frame_start - last).as_secs_f64().min(0.1);
40 last = frame_start;
41
42 // ---- input ----
43 let move_speed = 2.8;
44 let turn_speed = 2.8;
45 let strafe_speed = 2.2;
46
47 if window.is_key_down(Key::Left) {
48 game.rotate(-turn_speed * dt);
49 }
50 if window.is_key_down(Key::Right) {
51 game.rotate(turn_speed * dt);
52 }
53 // Combine forward + strafe into one displacement (prevents two-step
54 // wall clipping when pressing W+D / W+A near a wall).
55 let fwd = if window.is_key_down(Key::W) || window.is_key_down(Key::Up) {
56 move_speed * dt
57 } else if window.is_key_down(Key::S) || window.is_key_down(Key::Down) {
58 -move_speed * dt
59 } else {
60 0.0
61 };
62 let strf = if window.is_key_down(Key::D) {
63 strafe_speed * dt
64 } else if window.is_key_down(Key::A) {
65 -strafe_speed * dt
66 } else {
67 0.0
68 };
69 game.apply_movement(fwd, strf);
70
71 // ---- win condition ----
72 if game.check_win() {
73 println!("🎉 You found the exit! Generating a new maze…");
74 game = Game::new();
75 last = Instant::now(); // avoid huge dt spike
76 }
77
78 // ---- render ----
79 renderer.render(&game, &mut framebuffer);
80 window
81 .update_with_buffer(&framebuffer, SCREEN_W, SCREEN_H)
82 .unwrap();
83
84 // Sleep the remainder of the frame budget (saves CPU).
85 let elapsed = frame_start.elapsed();
86 if elapsed < frame_budget {
87 std::thread::sleep(frame_budget - elapsed);
88 }
89 }
90}
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 @@
1use crate::game::Game;
2use crate::texture::TEX_SIZE;
3
4pub struct Renderer {
5 screen_w: usize,
6 screen_h: usize,
7 /// Precomputed: floor_dist_lut[row] = perpendicular distance to floor at that row.
8 floor_dist_lut: Vec<f64>,
9 /// Precomputed: floor_bright_lut[row] = brightness × 256 for that row (fixed-point).
10 floor_bright_lut: Vec<u32>,
11}
12
13impl Renderer {
14 pub fn new(screen_w: usize, screen_h: usize) -> Self {
15 let half_h = screen_h as f64 / 2.0;
16 let mut floor_dist_lut = vec![0.0f64; screen_h];
17 let mut floor_bright_lut = vec![0u32; screen_h];
18
19 // Only rows below the horizon have meaningful floor values.
20 for py in (screen_h / 2 + 1)..screen_h {
21 let dy = py as f64 - half_h;
22 let dist = 0.5 * screen_h as f64 / dy;
23 floor_dist_lut[py] = dist;
24 let bright = (1.0 / (1.0 + dist * 0.15)).max(0.05);
25 floor_bright_lut[py] = (bright * 256.0) as u32;
26 }
27
28 Self {
29 screen_w,
30 screen_h,
31 floor_dist_lut,
32 floor_bright_lut,
33 }
34 }
35
36 /// Produce one frame into `buffer` (packed `0xAARRGGBB`).
37 pub fn render(&self, game: &Game, buffer: &mut [u32]) {
38 buffer.fill(0xFF00_0000); // pure-black ceiling
39
40 let half_h = self.screen_h as f64 / 2.0;
41 let fov = std::f64::consts::PI / 3.0; // 60°
42
43 for sx in 0..self.screen_w {
44 let cam_x = 2.0 * sx as f64 / self.screen_w as f64 - 1.0;
45 let ray_angle = game.player.angle + (fov / 2.0) * cam_x;
46 let rdx = ray_angle.cos();
47 let rdy = ray_angle.sin();
48
49 // ---- DDA setup -------------------------------------------------
50 let mut map_x = game.player.x as isize;
51 let mut map_y = game.player.y as isize;
52
53 let ddx = if rdx == 0.0 {
54 f64::MAX
55 } else {
56 (1.0 / rdx).abs()
57 };
58 let ddy = if rdy == 0.0 {
59 f64::MAX
60 } else {
61 (1.0 / rdy).abs()
62 };
63
64 let (step_x, mut side_x) = if rdx < 0.0 {
65 (-1isize, (game.player.x - map_x as f64) * ddx)
66 } else {
67 (1isize, (map_x as f64 + 1.0 - game.player.x) * ddx)
68 };
69
70 let (step_y, mut side_y) = if rdy < 0.0 {
71 (-1isize, (game.player.y - map_y as f64) * ddy)
72 } else {
73 (1isize, (map_y as f64 + 1.0 - game.player.y) * ddy)
74 };
75
76 let mut hit_side: bool;
77 let euclidean: f64;
78
79 // ---- DDA loop --------------------------------------------------
80 loop {
81 if side_x < side_y {
82 side_x += ddx;
83 map_x += step_x;
84 hit_side = false;
85 } else {
86 side_y += ddy;
87 map_y += step_y;
88 hit_side = true;
89 }
90
91 if map_y < 0
92 || map_y >= game.maze.height() as isize
93 || map_x < 0
94 || map_x >= game.maze.width() as isize
95 {
96 euclidean = 1.0;
97 break;
98 }
99
100 if game.maze.is_wall(map_y as usize, map_x as usize) {
101 euclidean = if !hit_side {
102 (map_x as f64 - game.player.x + (1.0 - step_x as f64) / 2.0) / rdx
103 } else {
104 (map_y as f64 - game.player.y + (1.0 - step_y as f64) / 2.0) / rdy
105 };
106 break;
107 }
108 }
109
110 // Fisheye correction
111 let perp_dist = (euclidean * (ray_angle - game.player.angle).cos().abs()).max(0.001);
112
113 // ---- wall slice ------------------------------------------------
114 let wall_h = (self.screen_h as f64 / perp_dist) as isize;
115 let draw_start = ((-wall_h / 2) + self.screen_h as isize / 2).max(0) as usize;
116 let draw_end = ((wall_h / 2) + self.screen_h as isize / 2)
117 .min(self.screen_h as isize - 1) as usize;
118
119 // Texture x-coordinate (uses Euclidean, not perp)
120 let wall_x = if !hit_side {
121 game.player.y + euclidean * rdy
122 } else {
123 game.player.x + euclidean * rdx
124 };
125 let wall_x = wall_x - wall_x.floor();
126 let tex_x = ((wall_x * TEX_SIZE as f64) as usize).min(TEX_SIZE - 1);
127
128 // Hoist: brightness is identical for every pixel in this column
129 let wall_bright_fx = {
130 let b = (1.0 / (1.0 + perp_dist * 0.12)).max(0.08);
131 (b * 256.0) as u32
132 };
133
134 for py in draw_start..=draw_end {
135 let tex_y = ((py as f64 - half_h + wall_h as f64 / 2.0) * TEX_SIZE as f64
136 / wall_h as f64) as usize;
137 let tex_y = tex_y.min(TEX_SIZE - 1);
138
139 let color = game.wall_texture[tex_y * TEX_SIZE + tex_x];
140 buffer[py * self.screen_w + sx] = darken_fx(color, wall_bright_fx);
141 }
142
143 // ---- floor below the wall --------------------------------------
144 let floor_start = draw_end + 1;
145 if floor_start < self.screen_h {
146 for py in floor_start..self.screen_h {
147 let floor_dist = self.floor_dist_lut[py];
148
149 if floor_dist > 15.0 {
150 // Far floor → just a dark pixel, skip texture & world calc.
151 let gray = darken_fx(0xFF80_8080, self.floor_bright_lut[py]);
152 buffer[py * self.screen_w + sx] = gray;
153 } else {
154 let fx = game.player.x + floor_dist * rdx;
155 let fy = game.player.y + floor_dist * rdy;
156
157 let ftx = ((fx.fract().abs() * TEX_SIZE as f64) as usize).min(TEX_SIZE - 1);
158 let fty = ((fy.fract().abs() * TEX_SIZE as f64) as usize).min(TEX_SIZE - 1);
159
160 let color = game.floor_texture[fty * TEX_SIZE + ftx];
161 buffer[py * self.screen_w + sx] =
162 darken_fx(color, self.floor_bright_lut[py]);
163 }
164 }
165 }
166 }
167
168 // ---- crosshair -----------------------------------------------------
169 let cx = self.screen_w / 2;
170 let cy = self.screen_h / 2;
171 let cross_color = 0xFF00_FF00;
172 for i in 0..=4isize {
173 if cy as isize - i >= 0 {
174 buffer[(cy - i as usize) * self.screen_w + cx] = cross_color;
175 }
176 if (cy + i as usize) < self.screen_h {
177 buffer[(cy + i as usize) * self.screen_w + cx] = cross_color;
178 }
179 if cx as isize - i >= 0 {
180 buffer[cy * self.screen_w + (cx - i as usize)] = cross_color;
181 }
182 if (cx + i as usize) < self.screen_w {
183 buffer[cy * self.screen_w + (cx + i as usize)] = cross_color;
184 }
185 }
186 }
187}
188
189/// Integer darken: `bright_fx` is brightness × 256 in fixed-point.
190#[inline(always)]
191fn darken_fx(color: u32, bright_fx: u32) -> u32 {
192 let r = (((color >> 16) & 0xFF) * bright_fx) >> 8;
193 let g = (((color >> 8) & 0xFF) * bright_fx) >> 8;
194 let b = ((color & 0xFF) * bright_fx) >> 8;
195 0xFF00_0000 | (r << 16) | (g << 8) | b
196}
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 @@
1use rand::Rng;
2
3pub const TEX_SIZE: usize = 64;
4
5/// Deterministic hash for coherent procedural noise.
6fn hash(x: u32, y: u32, seed: u64) -> u64 {
7 let mut h = (x as u64).wrapping_mul(0x9e3779b9);
8 h = h.wrapping_add((y as u64).wrapping_mul(0x517cc1b727220a95));
9 h = h.wrapping_add(seed);
10 h = (h ^ (h >> 33)).wrapping_mul(0xc6a4a7935bd1e995);
11 h = (h ^ (h >> 29)).wrapping_mul(0x5bd1e995);
12 h
13}
14
15/// Brown cracked wall texture — 64×64, brick pattern with mortar lines and fissures.
16pub fn generate_wall_texture(rng: &mut impl Rng) -> Vec<u32> {
17 let seed = rng.gen::<u64>();
18 let mut tex = vec![0u32; TEX_SIZE * TEX_SIZE];
19
20 for y in 0..TEX_SIZE {
21 for x in 0..TEX_SIZE {
22 let h = hash(x as u32, y as u32, seed);
23
24 let r = 105u8.wrapping_add((h % 50) as u8);
25 let g = 55u8.wrapping_add(((h >> 8) % 35) as u8);
26 let b = 22u8.wrapping_add(((h >> 16) % 25) as u8);
27
28 // Brick mortar every 16 pixels
29 let mortar = y % 16 == 0 || y % 16 == 15;
30
31 let (r, g, b) = if mortar {
32 (42, 20, 8)
33 } else {
34 let crack_hash = hash(x.wrapping_add(1000) as u32, y as u32, seed);
35 let crack_val = (crack_hash % 100) as u8;
36 if crack_val < 8 {
37 // dark crack
38 let factor = 0.30 + (crack_val as f64 / 8.0) * 0.25;
39 (
40 (r as f64 * factor) as u8,
41 (g as f64 * factor) as u8,
42 (b as f64 * factor) as u8,
43 )
44 } else if crack_val < 14 {
45 // light crack highlight
46 (
47 (r.saturating_add(25)).min(255),
48 (g.saturating_add(18)).min(255),
49 (b.saturating_add(10)).min(255),
50 )
51 } else {
52 (r, g, b)
53 }
54 };
55
56 tex[y * TEX_SIZE + x] =
57 0xFF00_0000 | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32);
58 }
59 }
60 tex
61}
62
63/// Gray stone-tile floor — 64×64, checkerboard with grout and subtle colour tint.
64pub fn generate_floor_texture(rng: &mut impl Rng) -> Vec<u32> {
65 let seed = rng.gen::<u64>();
66 let mut tex = vec![0u32; TEX_SIZE * TEX_SIZE];
67
68 for y in 0..TEX_SIZE {
69 for x in 0..TEX_SIZE {
70 let h = hash(x as u32, y as u32, seed);
71 let h2 = hash(x.wrapping_add(2000) as u32, y.wrapping_add(2000) as u32, seed);
72
73 let tile_x = x / 16;
74 let tile_y = y / 16;
75 let is_light_tile = (tile_x + tile_y) % 2 == 0;
76
77 let local_x = x % 16;
78 let local_y = y % 16;
79 let is_grout = local_x == 0 || local_y == 0;
80
81 let base_gray: u8 = if is_light_tile { 115 } else { 95 };
82 let noise = (h % 15) as u8;
83
84 let tint_r = (h2 % 8) as i16 - 4i16;
85 let tint_b = ((h2 >> 8) % 8) as i16 - 4i16;
86
87 let gray = if is_grout {
88 base_gray.saturating_sub(30).saturating_add(noise / 3)
89 } else {
90 base_gray.saturating_add(noise / 2)
91 };
92
93 let r = (gray as i16 + tint_r).clamp(0, 255) as u8;
94 let g = gray;
95 let b = (gray as i16 + tint_b).clamp(0, 255) as u8;
96
97 tex[y * TEX_SIZE + x] =
98 0xFF00_0000 | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32);
99 }
100 }
101 tex
102}