Compare commits

...

430 Commits

Author SHA1 Message Date
5919881568 Vita progress 2026-03-28 19:09:21 -05:00
cbb68a399d Fix compile error 2026-03-28 15:43:38 -05:00
0e794f28b1 Disable paletted textures for now 2026-03-28 15:40:30 -05:00
87d2d9123e Re-implement RGBA textures 2026-03-28 15:21:33 -05:00
6823a4ddb5 Try again again 2026-03-28 11:35:11 -05:00
20a7c70081 Fixiing weird action path missing? 2026-03-28 11:26:25 -05:00
9caa33b3bb Restore all builds 2026-03-28 11:14:15 -05:00
2d7e61460a fix 2026-03-28 11:05:36 -05:00
a4b7fb3f44 Try again 2026-03-28 11:04:42 -05:00
70056cf4ca Temp only build knulli
Some checks failed
Build Dusk / build-knulli (push) Failing after 21s
2026-03-28 11:02:43 -05:00
5f4ab71ade Add knulli build 2026-03-28 11:02:34 -05:00
f3adb3257b Cleanup knulli 2026-03-28 11:00:18 -05:00
438edda7fd Fixed knulli 2026-03-28 10:56:40 -05:00
d5b0441e6f Fixed GLES support (partially), PSP still not working 2026-03-28 10:51:50 -05:00
9ba0ceb000 Moved texture setting around 2026-03-28 09:48:24 -05:00
9474a68995 Slightly more accurate, likely going to have to change how paletted textures work 2026-03-27 21:01:29 -05:00
09c35f0aa6 Builds on knulli 2026-03-27 20:48:43 -05:00
a2113442cb Builds on knulli 2026-03-27 15:59:26 -05:00
d91808487f Allow texture to be NULL.
Some checks failed
Build Dusk / run-tests (push) Failing after 14s
Build Dusk / build-linux (push) Failing after 20s
Build Dusk / build-psp (push) Failing after 16s
Build Dusk / build-gamecube (push) Failing after 18s
Build Dusk / build-wii (push) Failing after 19s
2026-03-27 13:46:18 -05:00
933949cc19 Progress on PSP paletted textures
Some checks failed
Build Dusk / run-tests (push) Failing after 19s
Build Dusk / build-linux (push) Failing after 17s
Build Dusk / build-psp (push) Failing after 21s
Build Dusk / build-gamecube (push) Failing after 19s
Build Dusk / build-wii (push) Failing after 15s
2026-03-27 08:04:34 -05:00
407620387d Test paletted stuff
Some checks failed
Build Dusk / run-tests (push) Failing after 26s
Build Dusk / build-linux (push) Failing after 25s
Build Dusk / build-psp (push) Failing after 18s
Build Dusk / build-gamecube (push) Failing after 18s
Build Dusk / build-wii (push) Failing after 18s
2026-03-26 14:48:20 -05:00
98947dea26 starting textures
Some checks failed
Build Dusk / run-tests (push) Failing after 16s
Build Dusk / build-linux (push) Failing after 17s
Build Dusk / build-psp (push) Failing after 18s
Build Dusk / build-gamecube (push) Failing after 17s
Build Dusk / build-wii (push) Failing after 17s
2026-03-23 19:42:24 -05:00
ebff7af9b5 fix
Some checks failed
Build Dusk / run-tests (push) Failing after 17s
Build Dusk / build-linux (push) Failing after 20s
Build Dusk / build-psp (push) Failing after 20s
Build Dusk / build-gamecube (push) Failing after 19s
Build Dusk / build-wii (push) Failing after 17s
2026-03-23 15:37:53 -05:00
b23c4b83ae played around with color, will likely stick to textures.
Some checks failed
Build Dusk / run-tests (push) Failing after 13s
Build Dusk / build-linux (push) Failing after 15s
Build Dusk / build-psp (push) Failing after 14s
Build Dusk / build-gamecube (push) Failing after 13s
Build Dusk / build-wii (push) Failing after 14s
2026-03-22 23:53:23 -05:00
c0cff40628 Merge pull request 'shader' (#2) from shader into main
Some checks failed
Build Dusk / run-tests (push) Failing after 14s
Build Dusk / build-linux (push) Failing after 14s
Build Dusk / build-psp (push) Failing after 14s
Build Dusk / build-gamecube (push) Failing after 13s
Build Dusk / build-wii (push) Failing after 13s
Reviewed-on: #2
2026-03-23 04:33:20 +00:00
97513e354c Dolphin shaders
Some checks failed
Build Dusk / run-tests (pull_request) Failing after 13s
Build Dusk / build-linux (pull_request) Failing after 18s
Build Dusk / build-psp (pull_request) Failing after 18s
Build Dusk / build-gamecube (pull_request) Failing after 16s
Build Dusk / build-wii (pull_request) Failing after 13s
2026-03-22 23:32:43 -05:00
c277ae7aff DOlphin shader prog 2026-03-22 18:14:56 -05:00
e1835e6282 Merge pull request 'Pull shader code into main' (#1) from shader into main
Some checks failed
Build Dusk / build-linux (push) Has been cancelled
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / run-tests (push) Has been cancelled
Build Dusk / build-gamecube (push) Has been cancelled
Build Dusk / build-wii (push) Has been cancelled
Reviewed-on: #1
2026-03-22 15:46:37 +00:00
5ac21db997 Shaders adapted for Legacy GL
Some checks failed
Build Dusk / run-tests (pull_request) Failing after 24s
Build Dusk / build-linux (pull_request) Failing after 18s
Build Dusk / build-psp (pull_request) Failing after 18s
Build Dusk / build-gamecube (pull_request) Failing after 15s
Build Dusk / build-wii (pull_request) Failing after 16s
2026-03-22 10:44:28 -05:00
ca0e9fc3b2 Implement spritebatch properly. 2026-03-22 09:13:42 -05:00
66ebcb1608 shader prog 2026-03-17 17:05:39 -05:00
ff92a78dda Shader first pass 2026-03-17 08:42:43 -05:00
7356286fe0 Adjust how deadzones work
Some checks failed
Build Dusk / run-tests (push) Failing after 15s
Build Dusk / build-linux (push) Failing after 21s
Build Dusk / build-psp (push) Failing after 17s
Build Dusk / build-gamecube (push) Failing after 17s
Build Dusk / build-wii (push) Failing after 17s
2026-03-11 13:00:11 -05:00
54e8e68f86 Update build to use checkout v6
Some checks failed
Build Dusk / run-tests (push) Failing after 18s
Build Dusk / build-linux (push) Failing after 17s
Build Dusk / build-psp (push) Failing after 18s
Build Dusk / build-gamecube (push) Failing after 13s
Build Dusk / build-wii (push) Failing after 16s
2026-03-11 10:42:54 -05:00
d21cd7f78b Update error and debug logging methods
Some checks failed
Build Dusk / run-tests (push) Failing after 14s
Build Dusk / build-linux (push) Failing after 15s
Build Dusk / build-psp (push) Failing after 22s
Build Dusk / build-gamecube (push) Failing after 18s
Build Dusk / build-wii (push) Failing after 15s
2026-03-11 10:33:43 -05:00
1d7516982a Fixed dolphin input
Some checks failed
Build Dusk / run-tests (push) Failing after 15s
Build Dusk / build-linux (push) Failing after 15s
Build Dusk / build-psp (push) Failing after 15s
Build Dusk / build-gamecube (push) Failing after 17s
Build Dusk / build-wii (push) Failing after 14s
2026-03-11 08:11:49 -05:00
c77a11442c Fix input on linux 2026-03-11 07:56:03 -05:00
5bd43a4643 Fix Dolphin crash 2026-03-11 07:27:06 -05:00
9b87dfa1a9 Only exec action on main 2026-03-10 21:59:15 -05:00
2e3173ea40 Enable all jobs.
Some checks failed
Build Dusk / run-tests (push) Failing after 14s
Build Dusk / build-linux (push) Failing after 16s
Build Dusk / build-psp (push) Failing after 15s
Build Dusk / build-gamecube (push) Failing after 16s
Build Dusk / build-wii (push) Failing after 14s
2026-03-10 21:53:13 -05:00
68eac7cf83 Build wii
Some checks failed
Build Dusk / build-wii (push) Failing after 14s
2026-03-10 21:48:12 -05:00
6709505630 Build gamecube
Some checks failed
Build Dusk / build-gamecube (push) Failing after 16s
2026-03-10 21:43:59 -05:00
af6e962a5d Try rename
Some checks failed
Build Dusk / build-psp (push) Failing after 13s
2026-03-10 21:39:00 -05:00
18e6bdabaa test 2
Some checks failed
Build Dusk / build-psp (push) Failing after 14s
2026-03-10 21:35:50 -05:00
9743942eae Try zip PSP
Some checks failed
Build Dusk / build-psp (push) Failing after 17s
2026-03-10 21:33:27 -05:00
23062137a8 Disable tests for now.
Some checks failed
Build Dusk / build-linux (push) Failing after 14s
2026-03-10 21:23:08 -05:00
46f7fb5ccd Use v6
Some checks failed
Build Dusk / run-tests (push) Failing after 14s
Build Dusk / build-linux (push) Failing after 28s
2026-03-10 21:21:37 -05:00
9c90c49a6b Test build linux
Some checks failed
Build Dusk / run-tests (push) Failing after 14s
Build Dusk / build-linux (push) Failing after 28s
2026-03-10 21:19:55 -05:00
4517b63557 Fixed compiling
Some checks failed
Build Dusk / run-tests (push) Failing after 15s
2026-03-10 21:11:12 -05:00
58c239f4b4 Fixing tests more.
Some checks failed
Build Dusk / run-tests (push) Failing after 14s
2026-03-10 20:53:28 -05:00
cc8845ba3e Run linux tests
Some checks failed
Build Dusk / run-tests (push) Failing after 14s
2026-03-10 20:51:58 -05:00
6b69ce2901 Try github
Some checks failed
Build Dusk / run-tests (push) Failing after 36s
2026-03-10 20:46:29 -05:00
55300ed21c test2
Some checks failed
Build Dusk / run-tests (push) Successful in 30s
Build Dusk / build-linux (push) Failing after 6s
2026-03-10 16:48:14 -05:00
7346dd4339 Test
Some checks failed
Build Dusk / run-tests (push) Successful in 25s
Build Dusk / build-linux (push) Failing after 7s
2026-03-10 16:45:24 -05:00
2caf3b92ce Try github workspace
Some checks failed
Build Dusk / run-tests (push) Successful in 6s
Build Dusk / build-linux (push) Failing after 7s
2026-03-10 16:30:46 -05:00
af2cd72a1f Try mount rather than volume
Some checks failed
Build Dusk / run-tests (push) Successful in 4s
Build Dusk / build-linux (push) Failing after 4s
2026-03-10 16:30:08 -05:00
3d455ec1f8 Remove volume
Some checks failed
Build Dusk / run-tests (push) Failing after 3s
Build Dusk / build-linux (push) Failing after 3s
2026-03-10 16:26:29 -05:00
15982d7735 Try realpath over pwd
Some checks failed
Build Dusk / run-tests (push) Failing after 4s
Build Dusk / build-linux (push) Failing after 6s
2026-03-10 16:24:41 -05:00
5ae3542bd9 where am I?
Some checks failed
Build Dusk / run-tests (push) Failing after 4s
Build Dusk / build-linux (push) Failing after 5s
2026-03-10 16:23:32 -05:00
b1b02ae24b Test lsla
Some checks failed
Build Dusk / run-tests (push) Successful in 4s
Build Dusk / build-linux (push) Failing after 4s
2026-03-10 16:21:31 -05:00
f0964e2c92 Test runner
Some checks failed
Build Dusk / run-tests (push) Failing after 2m18s
Build Dusk / build-linux (push) Failing after 6s
2026-03-10 16:16:53 -05:00
e9661d2998 ADd check
Some checks failed
Build Dusk / run-tests (push) Failing after 2m21s
2026-03-10 16:01:44 -05:00
ea6468f2a9 Use git runner temp
Some checks failed
Build Dusk / run-tests (push) Failing after 29s
2026-03-10 15:36:28 -05:00
a2b38d3b83 Test Docker user
Some checks failed
Build Dusk / run-tests (push) Failing after 25s
2026-03-10 15:34:53 -05:00
d67ef02941 Use script system
All checks were successful
Build Dusk / run-tests (push) Successful in 1m15s
Build Dusk / build-linux (push) Successful in 1m6s
2026-03-10 15:15:31 -05:00
549ebe25d8 Let's get it building on linux in gitea
All checks were successful
Build Dusk / run-tests (push) Successful in 2m16s
Build Dusk / build-linux (push) Successful in 1m23s
2026-03-10 15:10:18 -05:00
9a98348582 Renders on Dolphin also. 2026-03-10 15:07:50 -05:00
c5f5b025a6 Game no longer crashes on Dolphin 2026-03-09 08:05:26 -05:00
23eaffa3a7 Fix some dolphin stuff. 2026-03-08 19:55:48 -05:00
c161809248 Renders on PSP identically. 2026-03-08 19:51:00 -05:00
4bf26dc818 Let's get this rendering on PSP and Dolphin. 2026-03-08 15:46:38 -05:00
5dd22fad6c Fixed some bugs. 2026-03-08 13:55:11 -05:00
2c3fdf7803 Add compile time endianess 2026-03-08 13:44:52 -05:00
e984b9f5d7 Asset compartmentalized 2026-03-08 13:29:40 -05:00
a3c2e37b17 Fixed errors 2026-03-08 12:01:22 -05:00
edf1b5a0a3 Technically working 2026-03-08 11:35:21 -05:00
8efdf59ebd More fixes 2026-03-08 10:20:55 -05:00
5c4537b2fa input prog 2026-03-07 22:11:11 -06:00
71e6079054 More code moving 2026-03-07 12:09:40 -06:00
dd048d9b0d Moved a bunch of code around 2026-03-07 09:35:56 -06:00
93074d653e idk 2026-03-06 16:34:45 -06:00
9139c4350a Moved all files. 2026-03-06 14:01:21 -06:00
38ce768168 kms 2026-03-06 13:40:27 -06:00
82b3dc576c remove un-needed files 2026-03-03 12:29:04 -06:00
2167889f48 Merge branch 'main' into break-literally-everything 2026-03-03 12:28:48 -06:00
e9b02c2acf Whatewver
Some checks failed
Build Dusk / run-tests (push) Successful in 1m26s
Build Dusk / build-linux (push) Successful in 1m26s
Build Dusk / build-psp (push) Failing after 1m28s
Build Dusk / build-dolphin (push) Failing after 1m28s
2026-03-02 20:14:21 -06:00
9ee446431b Moved build stuff to docker 2026-03-02 06:59:51 -06:00
df106e3988 "progress" 2026-02-28 09:55:21 -06:00
d0a057e0ee Moved all defs into main file. 2026-02-17 11:33:00 -06:00
8b49902bf6 Moved some files around 2026-02-17 10:59:21 -06:00
71c1e56564 Fix endian again
Some checks failed
Build Dusk / run-tests (push) Successful in 1m35s
Build Dusk / build-linux (push) Successful in 1m42s
Build Dusk / build-psp (push) Failing after 1m44s
Build Dusk / build-dolphin (push) Failing after 2m18s
2026-02-16 19:15:29 -06:00
1b12e67de2 Use internal endian tool
Some checks failed
Build Dusk / run-tests (push) Successful in 1m27s
Build Dusk / build-linux (push) Successful in 1m21s
Build Dusk / build-psp (push) Failing after 1m31s
Build Dusk / build-dolphin (push) Failing after 1m49s
2026-02-16 13:34:20 -06:00
291bb4bb81 Build?
Some checks failed
Build Dusk / run-tests (push) Successful in 1m20s
Build Dusk / build-linux (push) Successful in 1m30s
Build Dusk / build-psp (push) Failing after 1m35s
Build Dusk / build-dolphin (push) Failing after 2m6s
2026-02-16 13:21:04 -06:00
342ddb19f8 Finally got text rendering again. 2026-02-16 13:12:12 -06:00
9c9d2d548e Fixed whatever problem was with texture loading. 2026-02-16 12:29:25 -06:00
d7a0bb4509 Fix palette indexer bytes 2026-02-16 12:01:06 -06:00
2b1a3323a8 Tileset creator done 2026-02-16 12:00:55 -06:00
99d030003c DEbug not working so moving pcs 2026-02-15 16:41:33 -06:00
92a753560b prog 2026-02-15 01:09:28 -06:00
af9904c892 Scripts work again.
All checks were successful
Build Dusk / run-tests (push) Successful in 1m40s
Build Dusk / build-linux (push) Successful in 1m45s
Build Dusk / build-psp (push) Successful in 1m47s
Build Dusk / build-dolphin (push) Successful in 2m23s
2026-02-13 19:36:59 -06:00
e5e8c49f6c Mostly nuking old system
Some checks failed
Build Dusk / build-linux (push) Failing after 1m24s
Build Dusk / run-tests (push) Failing after 1m17s
Build Dusk / build-psp (push) Failing after 1m34s
Build Dusk / build-dolphin (push) Failing after 2m5s
2026-02-13 19:13:26 -06:00
b37e5f45ca Sweeper
All checks were successful
Build Dusk / run-tests (push) Successful in 1m28s
Build Dusk / build-linux (push) Successful in 1m33s
Build Dusk / build-psp (push) Successful in 1m53s
Build Dusk / build-dolphin (push) Successful in 2m19s
2026-02-10 21:06:09 -06:00
e1f08b07aa Need a break from Dolphin
All checks were successful
Build Dusk / run-tests (push) Successful in 1m36s
Build Dusk / build-linux (push) Successful in 1m14s
Build Dusk / build-psp (push) Successful in 2m2s
Build Dusk / build-dolphin (push) Successful in 2m49s
2026-02-09 22:18:44 -06:00
073ee8dca9 Trying to find dolphin texture bug
All checks were successful
Build Dusk / run-tests (push) Successful in 1m37s
Build Dusk / build-linux (push) Successful in 1m23s
Build Dusk / build-psp (push) Successful in 1m45s
Build Dusk / build-dolphin (push) Successful in 2m20s
2026-02-09 14:53:27 -06:00
a26e51cf46 Use local mirror for cglm
All checks were successful
Build Dusk / run-tests (push) Successful in 1m26s
Build Dusk / build-linux (push) Successful in 1m33s
Build Dusk / build-psp (push) Successful in 2m8s
Build Dusk / build-dolphin (push) Successful in 2m1s
2026-02-09 13:19:13 -06:00
dfed732825 Try different branch for upload binary action
All checks were successful
Build Dusk / run-tests (push) Successful in 1m46s
Build Dusk / build-linux (push) Successful in 2m10s
Build Dusk / build-psp (push) Successful in 2m42s
Build Dusk / build-dolphin (push) Successful in 3m23s
2026-02-09 12:13:48 -06:00
87aa70c6d2 Push userdata?
Some checks failed
Build Dusk / run-tests (push) Successful in 1m43s
Build Dusk / build-linux (push) Failing after 1m40s
Build Dusk / build-psp (push) Failing after 2m21s
Build Dusk / build-dolphin (push) Failing after 2m42s
2026-02-09 12:03:35 -06:00
aa2979ffe7 Fix main
Some checks failed
Build Dusk / run-tests (push) Failing after 1m15s
Build Dusk / build-linux (push) Failing after 1m34s
Build Dusk / build-psp (push) Failing after 1m45s
Build Dusk / build-dolphin (push) Failing after 2m7s
2026-02-09 11:54:53 -06:00
236e16aa6d Fix url format
Some checks failed
Build Dusk / run-tests (push) Failing after 1s
Build Dusk / build-linux (push) Failing after 1s
Build Dusk / build-psp (push) Failing after 1s
Build Dusk / build-dolphin (push) Failing after 1s
2026-02-09 11:51:04 -06:00
184bb970e6 Update to not rely on third party actions
Some checks failed
Build Dusk / run-tests (push) Failing after 2s
Build Dusk / build-linux (push) Failing after 2s
Build Dusk / build-psp (push) Failing after 1s
Build Dusk / build-dolphin (push) Failing after 2s
2026-02-09 10:59:34 -06:00
bd54469891 test some stuff
Some checks failed
Build Dusk / run-tests (push) Failing after 2m48s
Build Dusk / build-linux (push) Failing after 2m21s
Build Dusk / build-psp (push) Failing after 1m46s
Build Dusk / build-dolphin (push) Failing after 1m57s
2026-02-09 10:39:46 -06:00
2f5dccc3ef Texture loading
All checks were successful
Build Dusk / run-tests (push) Successful in 1m44s
Build Dusk / build-linux (push) Successful in 1m38s
Build Dusk / build-psp (push) Successful in 1m59s
Build Dusk / build-dolphin (push) Successful in 1m59s
2026-02-09 09:29:16 -06:00
592edb90a0 Test lua rgb rainbow
All checks were successful
Build Dusk / run-tests (push) Successful in 1m15s
Build Dusk / build-linux (push) Successful in 1m23s
Build Dusk / build-psp (push) Successful in 1m26s
Build Dusk / build-dolphin (push) Successful in 2m6s
2026-02-08 22:30:53 -06:00
3db7e6b1b9 Fixed palette lookup
All checks were successful
Build Dusk / run-tests (push) Successful in 1m18s
Build Dusk / build-linux (push) Successful in 1m21s
Build Dusk / build-psp (push) Successful in 1m30s
Build Dusk / build-dolphin (push) Successful in 1m51s
2026-02-08 21:26:42 -06:00
13c4df0d85 Compiles on dolphin, finally
All checks were successful
Build Dusk / run-tests (push) Successful in 1m39s
Build Dusk / build-linux (push) Successful in 1m34s
Build Dusk / build-psp (push) Successful in 2m7s
Build Dusk / build-dolphin (push) Successful in 1m48s
2026-02-08 19:30:02 -06:00
ef25fb09da Fix linux building
All checks were successful
Build Dusk / run-tests (push) Successful in 2m20s
Build Dusk / build-linux (push) Successful in 2m5s
Build Dusk / build-psp (push) Successful in 2m19s
Build Dusk / build-dolphin (push) Successful in 3m39s
2026-02-08 10:14:52 -06:00
03cf4a9efe See if dolphin will render the floating text demo
Some checks failed
Build Dusk / run-tests (push) Successful in 1m6s
Build Dusk / build-linux (push) Failing after 1m12s
Build Dusk / build-psp (push) Successful in 2m0s
Build Dusk / build-dolphin (push) Successful in 2m41s
2026-02-08 10:09:45 -06:00
53dd36efdd Fixed alpha textures properly on PSP
Some checks failed
Build Dusk / run-tests (push) Successful in 1m25s
Build Dusk / build-linux (push) Failing after 1m13s
Build Dusk / build-psp (push) Successful in 2m2s
Build Dusk / build-dolphin (push) Successful in 2m22s
2026-02-08 09:54:40 -06:00
ad9e841a42 Removed CXX target
All checks were successful
Build Dusk / run-tests (push) Successful in 1m46s
Build Dusk / build-linux (push) Successful in 1m36s
Build Dusk / build-psp (push) Successful in 2m56s
Build Dusk / build-dolphin (push) Successful in 5m38s
2026-02-07 17:08:03 -06:00
14f3f464c7 Prog on fixing psp alpha textures
All checks were successful
Build Dusk / run-tests (push) Successful in 1m20s
Build Dusk / build-linux (push) Successful in 1m39s
Build Dusk / build-psp (push) Successful in 1m51s
Build Dusk / build-dolphin (push) Successful in 2m36s
2026-02-07 15:29:29 -06:00
cbe51cc8d0 Speedup build
All checks were successful
Build Dusk / run-tests (push) Successful in 1m10s
Build Dusk / build-linux (push) Successful in 1m4s
Build Dusk / build-psp (push) Successful in 1m36s
Build Dusk / build-dolphin (push) Successful in 2m6s
2026-02-06 16:50:50 -06:00
efaa3f6eea Fix building on PSP
All checks were successful
Build Dusk / run-tests (push) Successful in 1m26s
Build Dusk / build-linux (push) Successful in 1m31s
Build Dusk / build-psp (push) Successful in 1m44s
Build Dusk / build-dolphin (push) Successful in 2m36s
2026-02-06 16:29:12 -06:00
52cce9a3b0 ADd meta.xml
Some checks failed
Build Dusk / build-dolphin (push) Successful in 3m6s
Build Dusk / run-tests (push) Successful in 1m38s
Build Dusk / build-linux (push) Failing after 2m15s
Build Dusk / build-psp (push) Failing after 2m23s
2026-02-06 16:14:54 -06:00
b7b390311e Forgor wii stuff
Some checks failed
Build Dusk / build-dolphin (push) Failing after 3m13s
Build Dusk / build-linux (push) Has been cancelled
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / run-tests (push) Has been cancelled
2026-02-06 16:09:31 -06:00
c1eeddd14b Fix dir spelling
Some checks failed
Build Dusk / build-dolphin (push) Failing after 2m22s
Build Dusk / run-tests (push) Successful in 2m4s
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / build-linux (push) Has been cancelled
2026-02-06 16:03:47 -06:00
1a7a55dfc3 Fix missing mkdirp
Some checks failed
Build Dusk / build-dolphin (push) Failing after 4m14s
Build Dusk / run-tests (push) Successful in 3m24s
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / build-linux (push) Has been cancelled
2026-02-06 15:54:06 -06:00
fe5927ea6a Add more bin
Some checks failed
Build Dusk / build-dolphin (push) Failing after 3m41s
Build Dusk / build-linux (push) Has been cancelled
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / run-tests (push) Has been cancelled
2026-02-06 15:49:52 -06:00
119c794ad7 Add bin
Some checks failed
Build Dusk / build-dolphin (push) Failing after 4m13s
Build Dusk / build-linux (push) Has been cancelled
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / run-tests (push) Has been cancelled
2026-02-06 15:44:11 -06:00
e2076b2c1c Also build wii
Some checks failed
Build Dusk / build-dolphin (push) Failing after 3m55s
Build Dusk / build-linux (push) Has been cancelled
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / run-tests (push) Has been cancelled
2026-02-06 15:39:33 -06:00
5208c5148e Fix cmake?
Some checks failed
Build Dusk / build-dolphin (push) Failing after 5m18s
Build Dusk / build-linux (push) Has been cancelled
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / run-tests (push) Has been cancelled
2026-02-06 15:32:00 -06:00
d80660b097 cur dir
Some checks failed
Build Dusk / build-dolphin (push) Failing after 3m30s
Build Dusk / build-linux (push) Has been cancelled
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / run-tests (push) Has been cancelled
2026-02-06 15:27:33 -06:00
b916d0278b install cmake
Some checks failed
Build Dusk / build-dolphin (push) Failing after 3m5s
Build Dusk / build-linux (push) Has been cancelled
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / run-tests (push) Has started running
2026-02-06 15:21:24 -06:00
d51e13e620 Update
Some checks failed
Build Dusk / build-gamecube (push) Failing after 1m36s
Build Dusk / run-tests (push) Successful in 4m9s
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / build-linux (push) Has started running
2026-02-06 15:13:03 -06:00
40ad4326ef Do it all ourselves
Some checks failed
Build Dusk / build-gamecube (push) Failing after 12s
Build Dusk / build-linux (push) Has been cancelled
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / run-tests (push) Has been cancelled
2026-02-06 15:11:50 -06:00
b8afc1684a with --yes
Some checks failed
Build Dusk / build-gamecube (push) Failing after 41s
Build Dusk / build-linux (push) Has been cancelled
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / run-tests (push) Has been cancelled
2026-02-06 15:10:47 -06:00
8348b31ac8 re
Some checks failed
Build Dusk / build-gamecube (push) Failing after 20s
Build Dusk / run-tests (push) Has started running
Build Dusk / build-linux (push) Has been cancelled
Build Dusk / build-psp (push) Has been cancelled
2026-02-06 15:08:58 -06:00
411f2dbcce 403 fix?
Some checks failed
Build Dusk / run-tests (push) Waiting to run
Build Dusk / build-gamecube (push) Failing after 18s
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / build-linux (push) Has been cancelled
2026-02-06 15:02:41 -06:00
ee89c08160 Manually setup ppc
Some checks failed
Build Dusk / build-gamecube (push) Failing after 6s
Build Dusk / build-linux (push) Has been cancelled
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / run-tests (push) Has started running
2026-02-06 15:00:28 -06:00
357607a89f more echoing
Some checks failed
Build Dusk / build-gamecube (push) Failing after 1m13s
Build Dusk / run-tests (push) Successful in 3m11s
Build Dusk / build-linux (push) Failing after 2m36s
Build Dusk / build-psp (push) Has been cancelled
2026-02-06 14:52:45 -06:00
8d6dc2df44 Try sudo
Some checks failed
Build Dusk / build-gamecube (push) Failing after 1m16s
Build Dusk / build-linux (push) Has been cancelled
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / run-tests (push) Has been cancelled
2026-02-06 14:50:49 -06:00
5207582ab3 Try more
Some checks failed
Build Dusk / build-gamecube (push) Failing after 1m15s
Build Dusk / build-linux (push) Has been cancelled
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / run-tests (push) Has been cancelled
2026-02-06 14:47:44 -06:00
71768e6154 Try outside
Some checks failed
Build Dusk / build-gamecube (push) Failing after 1m33s
Build Dusk / build-linux (push) Has been cancelled
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / run-tests (push) Has been cancelled
2026-02-06 14:45:07 -06:00
ecbe235523 Try try again
Some checks failed
Build Dusk / build-gamecube (push) Failing after 53s
Build Dusk / build-linux (push) Has been cancelled
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / run-tests (push) Has been cancelled
2026-02-06 14:43:10 -06:00
afef079d1e Fix cli order
Some checks failed
Build Dusk / build-gamecube (push) Failing after 1m0s
Build Dusk / build-linux (push) Has been cancelled
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / run-tests (push) Has been cancelled
2026-02-06 14:40:07 -06:00
df17696c69 try append u
Some checks failed
Build Dusk / build-gamecube (push) Failing after 52s
Build Dusk / build-linux (push) Has been cancelled
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / run-tests (push) Has been cancelled
2026-02-06 14:38:49 -06:00
065bf0908f Fix crap
Some checks failed
Build Dusk / build-gamecube (push) Failing after 42s
Build Dusk / build-linux (push) Has been cancelled
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / run-tests (push) Has been cancelled
2026-02-06 14:34:53 -06:00
5b6755e9cf Try fix workdir
Some checks failed
Build Dusk / build-gamecube (push) Failing after 1m38s
Build Dusk / run-tests (push) Successful in 4m0s
Build Dusk / build-linux (push) Failing after 2m53s
Build Dusk / build-psp (push) Failing after 3m30s
2026-02-06 14:19:59 -06:00
b08482acf1 Build cube again
Some checks failed
Build Dusk / build-gamecube (push) Failing after 1m11s
Build Dusk / build-linux (push) Has been cancelled
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / run-tests (push) Has been cancelled
2026-02-06 14:15:29 -06:00
80c9c1d389 Try mount differently.
Some checks failed
Build Dusk / build-gamecube (push) Failing after 1m12s
Build Dusk / build-linux (push) Has been cancelled
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / run-tests (push) Has been cancelled
2026-02-06 14:12:05 -06:00
bb7db57bda yes
Some checks failed
Build Dusk / build-gamecube (push) Failing after 50s
Build Dusk / run-tests (push) Successful in 2m5s
Build Dusk / build-linux (push) Failing after 2m17s
Build Dusk / build-psp (push) Has been cancelled
2026-02-06 14:05:28 -06:00
6a83ac767c cli 2026-02-06 14:05:18 -06:00
7e47ef9d74 try official docker steps
Some checks failed
Build Dusk / build-gamecube (push) Failing after 38s
Build Dusk / build-linux (push) Has been cancelled
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / run-tests (push) Has been cancelled
2026-02-06 14:03:34 -06:00
e7ec603526 yet another
Some checks failed
Build Dusk / build-gamecube (push) Failing after 44s
Build Dusk / build-linux (push) Has been cancelled
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / run-tests (push) Has been cancelled
2026-02-06 14:00:49 -06:00
2d8ae09bd8 Test again?
Some checks failed
Build Dusk / build-gamecube (push) Has been cancelled
Build Dusk / run-tests (push) Has been cancelled
Build Dusk / build-linux (push) Has been cancelled
Build Dusk / build-psp (push) Has been cancelled
2026-02-06 13:58:48 -06:00
b2affbc0a7 Try something else?
Some checks failed
Build Dusk / build-gamecube (push) Failing after 39s
Build Dusk / build-linux (push) Has been cancelled
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / run-tests (push) Has been cancelled
2026-02-06 13:56:39 -06:00
d50bc61ada Prog
Some checks failed
Build Dusk / build-linux (push) Waiting to run
Build Dusk / build-psp (push) Waiting to run
Build Dusk / build-gamecube (push) Failing after 29s
Build Dusk / run-tests (push) Has been cancelled
2026-02-06 13:54:53 -06:00
ec6b032b45 Take sH out
Some checks failed
Build Dusk / build-gamecube (push) Failing after 56s
Build Dusk / build-wii (push) Failing after 44s
Build Dusk / build-linux (push) Has been cancelled
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / run-tests (push) Has been cancelled
2026-02-06 13:51:07 -06:00
bc72f48496 Try again?
Some checks failed
Build Dusk / build-gamecube (push) Failing after 44s
Build Dusk / run-tests (push) Has been cancelled
Build Dusk / build-linux (push) Has been cancelled
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / build-wii (push) Has been cancelled
2026-02-06 13:48:11 -06:00
dcf06fbd36 Update shell
Some checks failed
Build Dusk / build-gamecube (push) Failing after 1m3s
Build Dusk / run-tests (push) Has been cancelled
Build Dusk / build-linux (push) Has been cancelled
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / build-wii (push) Has been cancelled
2026-02-06 13:46:15 -06:00
96311d72c2 Retry build
Some checks failed
Build Dusk / build-gamecube (push) Failing after 1m26s
Build Dusk / build-wii (push) Failing after 1m19s
Build Dusk / build-linux (push) Has been cancelled
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / run-tests (push) Has been cancelled
2026-02-06 13:41:27 -06:00
07938cccc7 Build dolphin first while I test.
Some checks failed
Build Dusk / build-dolphin (push) Failing after 5m38s
Build Dusk / build-linux (push) Has been cancelled
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / run-tests (push) Has been cancelled
2026-02-06 13:34:56 -06:00
097c8c00f9 Fixed flickering
Some checks failed
Build Dusk / run-tests (push) Successful in 1m42s
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / build-linux (push) Has been cancelled
Build Dusk / build-dolphin (push) Has been cancelled
2026-02-06 13:31:15 -06:00
aa5b41fe31 First texture rendering (if broken) 2026-02-06 12:48:49 -06:00
0d56859d94 Alpha textures 2026-02-05 23:28:26 -06:00
1af5f238e4 RGBA textures 2026-02-05 21:57:56 -06:00
dd697d5650 Emu vs Real! 2026-02-05 08:43:49 -06:00
5cf299a1c7 Mesh working, lua broken 2026-02-05 08:21:19 -06:00
67bf825cc9 Camera 2026-02-05 00:42:04 -06:00
56e1696cd4 Asset loading 2026-02-04 21:52:14 -06:00
d955fb6430 Debugging functions. 2026-02-04 18:32:20 -06:00
dd910a31aa Actually compiled 2026-02-04 17:44:53 -06:00
708c4d0ec3 "Improved" 2026-02-04 15:54:09 -06:00
ad13d6c6a1 SDL2 example builds. 2026-02-04 11:17:25 -06:00
1c32158142 DOlphin progress 2026-02-04 10:16:16 -06:00
5cea284906 Map loading
All checks were successful
Build Dusk / run-tests (push) Successful in 1m15s
Build Dusk / build-linux (push) Successful in 1m33s
Build Dusk / build-psp (push) Successful in 1m56s
2026-02-03 19:20:50 -06:00
13dba8b604 Restore some map stuff
All checks were successful
Build Dusk / run-tests (push) Successful in 1m44s
Build Dusk / build-linux (push) Successful in 1m35s
Build Dusk / build-psp (push) Successful in 1m53s
2026-02-03 15:40:56 -06:00
22398ddcef Added story flags
All checks were successful
Build Dusk / run-tests (push) Successful in 1m27s
Build Dusk / build-linux (push) Successful in 1m13s
Build Dusk / build-psp (push) Successful in 1m46s
2026-02-03 15:16:21 -06:00
94e2cc6210 Screen background
All checks were successful
Build Dusk / run-tests (push) Successful in 1m33s
Build Dusk / build-linux (push) Successful in 1m40s
Build Dusk / build-psp (push) Successful in 1m50s
2026-02-03 13:37:00 -06:00
da3513f63d Fix 1
All checks were successful
Build Dusk / run-tests (push) Successful in 1m28s
Build Dusk / build-linux (push) Successful in 1m50s
Build Dusk / build-psp (push) Successful in 2m37s
2026-02-03 13:18:40 -06:00
2c83e4ba9f mkdirs
Some checks failed
Build Dusk / build-linux (push) Has been cancelled
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / run-tests (push) Has been cancelled
2026-02-03 13:18:32 -06:00
c862071126 Text
Some checks failed
Build Dusk / run-tests (push) Failing after 1m47s
Build Dusk / build-linux (push) Failing after 1m47s
Build Dusk / build-psp (push) Failing after 1m53s
2026-02-03 10:22:39 -06:00
fed819e9b2 renders
Some checks failed
Build Dusk / run-tests (push) Failing after 1m10s
Build Dusk / build-linux (push) Failing after 1m22s
Build Dusk / build-psp (push) Failing after 1m31s
2026-02-01 22:04:07 -06:00
c6f4518684 More lua stuff, yay.
Some checks failed
Build Dusk / run-tests (push) Failing after 1m18s
Build Dusk / build-linux (push) Failing after 1m43s
Build Dusk / build-psp (push) Failing after 1m46s
2026-02-01 21:54:33 -06:00
053778a502 Refactored and simplified lua stuff a lot.
Some checks failed
Build Dusk / run-tests (push) Failing after 1m23s
Build Dusk / build-linux (push) Failing after 1m47s
Build Dusk / build-psp (push) Failing after 1m41s
2026-02-01 21:28:21 -06:00
78e1ae885a Add color support.
Some checks failed
Build Dusk / run-tests (push) Failing after 1m35s
Build Dusk / build-linux (push) Failing after 1m34s
Build Dusk / build-psp (push) Failing after 1m53s
2026-02-01 11:16:52 -06:00
982d28a3e0 prog
All checks were successful
Build Dusk / run-tests (push) Successful in 1m39s
Build Dusk / build-linux (push) Successful in 1m36s
Build Dusk / build-psp (push) Successful in 1m52s
2026-01-31 21:20:33 -06:00
c2cad858a5 Allow strings to be returned from structs
All checks were successful
Build Dusk / run-tests (push) Successful in 1m33s
Build Dusk / build-linux (push) Successful in 1m53s
Build Dusk / build-psp (push) Successful in 1m51s
2026-01-28 18:34:23 -06:00
794e0574ad Moved a few things around, definitely not clean but better.
Some checks failed
Build Dusk / run-tests (push) Failing after 2m3s
Build Dusk / build-linux (push) Successful in 2m13s
Build Dusk / build-psp (push) Successful in 1m56s
2026-01-28 15:00:59 -06:00
c190271565 Builds on PSP properly.
Some checks failed
Build Dusk / run-tests (push) Failing after 2m16s
Build Dusk / build-linux (push) Successful in 2m0s
Build Dusk / build-psp (push) Successful in 1m53s
2026-01-28 12:28:27 -06:00
ae8a869f64 Sort fix
All checks were successful
Build Dusk / run-tests (push) Successful in 3m44s
Build Dusk / build-linux (push) Successful in 4m34s
Build Dusk / build-psp (push) Successful in 2m43s
2026-01-28 11:44:25 -06:00
69d64eb8e4 Update PSP toolchain
Some checks failed
Build Dusk / run-tests (push) Failing after 3m35s
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / build-linux (push) Has been cancelled
2026-01-28 11:40:07 -06:00
b2f2df650a restored build
Some checks failed
Build Dusk / run-tests (push) Failing after 1m43s
Build Dusk / build-linux (push) Successful in 1m50s
Build Dusk / build-psp (push) Has been cancelled
2026-01-28 11:34:42 -06:00
6af570fab2 See why PSP fails on ubuntu vm 2026-01-28 11:33:12 -06:00
9ed902017c Cleanup scene.
Some checks failed
Build Dusk / run-tests (push) Failing after 2m9s
Build Dusk / build-linux (push) Successful in 1m50s
Build Dusk / build-psp (push) Failing after 1m49s
2026-01-28 11:02:53 -06:00
32b41c98e1 Rendering a moving square entirely from lua.
Some checks failed
Build Dusk / build-linux (push) Has been cancelled
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / run-tests (push) Has been cancelled
2026-01-28 11:01:07 -06:00
6bdb4ae30d Scene methods
Some checks failed
Build Dusk / run-tests (push) Failing after 2m14s
Build Dusk / build-linux (push) Successful in 2m12s
Build Dusk / build-psp (push) Failing after 1m30s
2026-01-28 10:31:31 -06:00
25dc97e3cc Update some script stuff.
Some checks failed
Build Dusk / run-tests (push) Failing after 2m12s
Build Dusk / build-linux (push) Successful in 2m3s
Build Dusk / build-psp (push) Failing after 1m28s
2026-01-27 23:48:46 -06:00
cc85983736 Add struct metafield
Some checks failed
Build Dusk / run-tests (push) Failing after 1m55s
Build Dusk / build-linux (push) Successful in 1m58s
Build Dusk / build-psp (push) Failing after 1m26s
2026-01-27 21:16:53 -06:00
6e78ee188d Event in lua (partial)
Some checks failed
Build Dusk / run-tests (push) Failing after 2m30s
Build Dusk / build-linux (push) Failing after 2m37s
Build Dusk / build-psp (push) Failing after 1m54s
2026-01-27 12:47:15 -06:00
9b73f1717f Event
Some checks failed
Build Dusk / run-tests (push) Failing after 1m44s
Build Dusk / build-linux (push) Successful in 1m50s
Build Dusk / build-psp (push) Failing after 1m53s
2026-01-27 10:41:32 -06:00
c7b9a53535 Locale script
Some checks failed
Build Dusk / run-tests (push) Failing after 2m12s
Build Dusk / build-linux (push) Successful in 1m49s
Build Dusk / build-psp (push) Failing after 1m39s
2026-01-27 09:52:08 -06:00
2b9be6675c Locale
Some checks failed
Build Dusk / run-tests (push) Failing after 2m3s
Build Dusk / build-linux (push) Successful in 1m50s
Build Dusk / build-psp (push) Failing after 1m48s
2026-01-27 09:24:16 -06:00
fb93453482 Cleaned some tools up
Some checks failed
Build Dusk / run-tests (push) Failing after 2m13s
Build Dusk / build-linux (push) Successful in 2m4s
Build Dusk / build-psp (push) Failing after 1m47s
2026-01-27 08:40:13 -06:00
81b08b2eba idk
Some checks failed
Build Dusk / run-tests (push) Successful in 1m57s
Build Dusk / build-linux (push) Successful in 2m9s
Build Dusk / build-psp (push) Failing after 1m39s
2026-01-26 22:54:05 -06:00
d1b03c4cb3 prog
Some checks failed
Build Dusk / run-tests (push) Failing after 1m58s
Build Dusk / build-linux (push) Failing after 1m26s
Build Dusk / build-psp (push) Failing after 1m41s
2026-01-26 18:27:12 -06:00
9544d15a18 Dynamically assign script values for items and inputs
Some checks failed
Build Dusk / run-tests (push) Failing after 1m22s
Build Dusk / build-linux (push) Successful in 2m8s
Build Dusk / build-psp (push) Failing after 1m38s
2026-01-26 08:48:17 -06:00
0392dd0e7f Added csv_to_array tool
Some checks failed
Build Dusk / run-tests (push) Failing after 1m41s
Build Dusk / build-linux (push) Failing after 1m26s
Build Dusk / build-psp (push) Failing after 1m46s
2026-01-26 08:37:36 -06:00
9c25fde548 Fixed ints
Some checks failed
Build Dusk / run-tests (push) Successful in 2m10s
Build Dusk / build-linux (push) Successful in 2m22s
Build Dusk / build-psp (push) Failing after 1m39s
2026-01-26 00:19:25 -06:00
2c9d0c6cff Fixed duskdefs
Some checks failed
Build Dusk / build-linux (push) Has been cancelled
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / run-tests (push) Has been cancelled
2026-01-26 00:18:43 -06:00
9897dbe031 env_to_h cleaned
Some checks failed
Build Dusk / run-tests (push) Failing after 1m40s
Build Dusk / build-linux (push) Failing after 1m34s
Build Dusk / build-psp (push) Failing after 1m41s
2026-01-25 23:00:11 -06:00
e78f117cfd Switched env to python
Some checks failed
Build Dusk / run-tests (push) Failing after 1m20s
Build Dusk / build-linux (push) Failing after 1m25s
Build Dusk / build-psp (push) Failing after 1m25s
2026-01-25 22:52:12 -06:00
07afc3813a Refactor
Some checks failed
Build Dusk / run-tests (push) Successful in 1m55s
Build Dusk / build-linux (push) Successful in 2m0s
Build Dusk / build-psp (push) Failing after 1m40s
2026-01-25 21:23:09 -06:00
d788de8637 Starting refactor of tools, thank gosh
Some checks failed
Build Dusk / run-tests (push) Successful in 1m56s
Build Dusk / build-linux (push) Successful in 1m44s
Build Dusk / build-psp (push) Failing after 1m35s
2026-01-25 21:07:31 -06:00
d749ac8a91 Add missing dnfs
Some checks failed
Build Dusk / run-tests (push) Failing after 1m28s
Build Dusk / build-linux (push) Failing after 1m24s
Build Dusk / build-psp (push) Failing after 1m53s
2026-01-25 16:07:44 -06:00
f71c271c97 item
Some checks failed
Build Dusk / run-tests (push) Failing after 1m31s
Build Dusk / build-linux (push) Failing after 1m8s
Build Dusk / build-psp (push) Failing after 1m32s
2026-01-25 15:01:25 -06:00
e1d7b7308f Progness
Some checks failed
Build Dusk / run-tests (push) Successful in 2m32s
Build Dusk / build-linux (push) Successful in 2m3s
Build Dusk / build-psp (push) Failing after 1m41s
2026-01-24 10:07:50 -06:00
26a71a823a Test assert
Some checks failed
Build Dusk / run-tests (push) Successful in 2m6s
Build Dusk / build-linux (push) Successful in 1m56s
Build Dusk / build-psp (push) Failing after 1m48s
2026-01-06 21:23:39 -06:00
5e39097faa Improve sorting
Some checks failed
Build Dusk / run-tests (push) Successful in 1m53s
Build Dusk / build-linux (push) Successful in 1m35s
Build Dusk / build-psp (push) Failing after 1m50s
2026-01-06 11:30:26 -06:00
0df7845f2c Added memory checks
Some checks failed
Build Dusk / run-tests (push) Successful in 2m6s
Build Dusk / build-linux (push) Successful in 2m6s
Build Dusk / build-psp (push) Failing after 1m47s
2026-01-06 11:02:26 -06:00
af5bf987c8 Add inventory.
All checks were successful
Build Dusk / run-tests (push) Successful in 2m12s
Build Dusk / build-linux (push) Successful in 1m49s
Build Dusk / build-psp (push) Successful in 1m52s
2026-01-06 07:47:16 -06:00
024ace1078 Fixed PSP building
All checks were successful
Build Dusk / run-tests (push) Successful in 1m18s
Build Dusk / build-linux (push) Successful in 1m53s
Build Dusk / build-psp (push) Successful in 2m8s
2026-01-05 20:13:11 -06:00
8d00fe9d16 Test PSP update
Some checks failed
Build Dusk / run-tests (push) Successful in 1m50s
Build Dusk / build-linux (push) Successful in 1m43s
Build Dusk / build-psp (push) Failing after 1m27s
2026-01-05 20:00:26 -06:00
ab422b14dd Update PSP SDK
Some checks failed
Build Dusk / run-tests (push) Successful in 2m0s
Build Dusk / build-linux (push) Successful in 2m11s
Build Dusk / build-psp (push) Failing after 1m43s
2026-01-05 19:19:16 -06:00
95c0690216 Add extra time checks.
Some checks failed
Build Dusk / run-tests (push) Successful in 1m51s
Build Dusk / build-linux (push) Successful in 1m49s
Build Dusk / build-psp (push) Failing after 1m47s
2026-01-05 18:11:02 -06:00
6cb80e9e23 Testing time (and found some bugs!)
Some checks failed
Build Dusk / build-linux (push) Has been cancelled
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / run-tests (push) Has been cancelled
2026-01-05 18:09:31 -06:00
83b799caa8 String.h
Some checks failed
Build Dusk / run-tests (push) Successful in 2m2s
Build Dusk / build-linux (push) Successful in 2m1s
Build Dusk / build-psp (push) Failing after 1m21s
2026-01-05 17:20:17 -06:00
a793ac2ff7 Add runner for testing
Some checks failed
Build Dusk / run-tests (push) Successful in 1m58s
Build Dusk / build-linux (push) Successful in 1m53s
Build Dusk / build-psp (push) Failing after 1m56s
2026-01-05 16:14:12 -06:00
aec937b04b Add some tests
Some checks failed
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / build-linux (push) Has been cancelled
2026-01-05 16:13:14 -06:00
8ee46fd204 add tests.
Some checks failed
Build Dusk / build-linux (push) Successful in 2m5s
Build Dusk / build-psp (push) Failing after 1m44s
2026-01-05 13:25:39 -06:00
726233e55f Map exec
Some checks failed
Build Dusk / build-linux (push) Successful in 1m40s
Build Dusk / build-psp (push) Failing after 1m38s
2025-12-26 20:38:24 +10:00
7940f4c487 Map refactoring to prep for loading
Some checks failed
Build Dusk / build-linux (push) Successful in 1m35s
Build Dusk / build-psp (push) Failing after 1m46s
2025-12-26 15:01:43 +10:00
b16dbaceec Modules
Some checks failed
Build Dusk / build-linux (push) Successful in 1m13s
Build Dusk / build-psp (push) Failing after 1m27s
2025-12-25 08:02:11 +10:00
f39b2060a8 iuno just screwing around tbh
Some checks failed
Build Dusk / build-linux (push) Failing after 1m5s
Build Dusk / build-psp (push) Failing after 1m24s
2025-12-24 10:44:53 +10:00
aed202ebf9 Add include()
Some checks failed
Build Dusk / build-linux (push) Successful in 1m16s
Build Dusk / build-psp (push) Failing after 1m14s
2025-12-24 09:41:05 +10:00
a495179e5f Prog
Some checks failed
Build Dusk / build-linux (push) Successful in 1m19s
Build Dusk / build-psp (push) Failing after 1m36s
2025-12-05 14:41:13 -06:00
4e1b404820 Add script context
Some checks failed
Build Dusk / build-linux (push) Successful in 1m41s
Build Dusk / build-psp (push) Failing after 2m36s
2025-12-04 20:57:12 -06:00
8c74ee31e0 Add lua diff
All checks were successful
Build Dusk / build-linux (push) Successful in 1m51s
Build Dusk / build-psp (push) Successful in 2m2s
2025-12-04 00:39:09 -06:00
77d3c54ebb Fixed PSP build
Some checks failed
Build Dusk / build-linux (push) Failing after 1m53s
Build Dusk / build-psp (push) Failing after 1m27s
2025-12-04 00:33:42 -06:00
b5de39926b Lua 2025-12-04 00:30:44 -06:00
3a8dafbb91 Fix compile issue
Some checks failed
Build Dusk / build-linux (push) Failing after 2m13s
Build Dusk / build-psp (push) Failing after 2m36s
2025-12-04 00:26:49 -06:00
6b22f547fe PyGL
Some checks failed
Build Dusk / build-linux (push) Failing after 2m5s
Build Dusk / build-psp (push) Has been cancelled
2025-12-04 00:23:50 -06:00
de78be3e25 Attempt install pyqt5
Some checks failed
Build Dusk / build-linux (push) Failing after 1m48s
Build Dusk / build-psp (push) Failing after 2m4s
2025-12-04 00:12:04 -06:00
9f507be7bc Lua script something
Some checks failed
Build Dusk / build-linux (push) Failing after 2m56s
Build Dusk / build-psp (push) Failing after 1m49s
2025-12-04 00:02:26 -06:00
9aaf271996 fixing some stuff but nothing really.
Some checks failed
Build Dusk / build-linux (push) Failing after 50s
Build Dusk / build-psp (push) Failing after 1m1s
2025-11-28 10:45:07 -06:00
b01c0d37b0 Region editor
Some checks failed
Build Dusk / build-linux (push) Failing after 51s
Build Dusk / build-psp (push) Failing after 55s
2025-11-28 08:48:42 -06:00
538079880d Abt to cutscene
Some checks failed
Build Dusk / build-linux (push) Failing after 6m26s
Build Dusk / build-psp (push) Failing after 1m1s
2025-11-25 08:45:12 -06:00
fe0529d021 Fixed initial chunk load buggy
Some checks failed
Build Dusk / build-linux (push) Failing after 2m15s
Build Dusk / build-psp (push) Failing after 1m48s
2025-11-25 08:27:43 -06:00
d068f0f2c3 Fixed double clicking to ent
Some checks failed
Build Dusk / build-linux (push) Failing after 1m8s
Build Dusk / build-psp (push) Has been cancelled
2025-11-25 08:23:27 -06:00
f9a64b8d54 Fixed entity positioning
Some checks failed
Build Dusk / build-linux (push) Failing after 57s
Build Dusk / build-psp (push) Has been cancelled
2025-11-25 08:21:56 -06:00
01cbfaae95 Trying to find entity editor bug
Some checks failed
Build Dusk / build-linux (push) Failing after 41s
Build Dusk / build-psp (push) Failing after 1m1s
2025-11-24 13:13:49 -06:00
f9006a90d5 Editor has chunk loading
Some checks failed
Build Dusk / build-linux (push) Failing after 57s
Build Dusk / build-psp (push) Failing after 1m0s
2025-11-23 22:44:31 -06:00
7daeaee6b5 Basically entity editing done
Some checks failed
Build Dusk / build-linux (push) Failing after 43s
Build Dusk / build-psp (push) Failing after 49s
2025-11-22 11:56:08 -06:00
03218ce20f Ent saving and loading
Some checks failed
Build Dusk / build-linux (push) Failing after 49s
Build Dusk / build-psp (push) Failing after 59s
2025-11-22 11:20:07 -06:00
6f33522c1c Begin adding entities to editor
Some checks failed
Build Dusk / build-linux (push) Failing after 56s
Build Dusk / build-psp (push) Failing after 1m8s
2025-11-22 10:38:16 -06:00
3697cc3eef Prep ent
Some checks failed
Build Dusk / build-linux (push) Failing after 55s
Build Dusk / build-psp (push) Failing after 56s
2025-11-20 16:45:50 -06:00
51a1077fda Finally merged map asset and map tool
Some checks failed
Build Dusk / build-linux (push) Failing after 39s
Build Dusk / build-psp (push) Failing after 54s
2025-11-19 20:25:25 -06:00
8740c2b165 Fixed underflow error on worldpos.
All checks were successful
Build Dusk / build-linux (push) Successful in 55s
Build Dusk / build-psp (push) Successful in 1m4s
2025-11-19 15:59:44 -06:00
6ed2bdd4c5 Added some extra checks around world positions, revealing bug. Likely going to sign all world coordinates.
All checks were successful
Build Dusk / build-linux (push) Successful in 48s
Build Dusk / build-psp (push) Successful in 59s
2025-11-19 15:52:43 -06:00
c32df89490 Added diagonal ramps
All checks were successful
Build Dusk / build-linux (push) Successful in 55s
Build Dusk / build-psp (push) Successful in 1m4s
2025-11-19 15:40:37 -06:00
bd5a67676b Minor improvements, add east and west ramp
All checks were successful
Build Dusk / build-linux (push) Successful in 48s
Build Dusk / build-psp (push) Successful in 1m0s
2025-11-19 13:25:58 -06:00
903dab49e3 Editor partially started.
All checks were successful
Build Dusk / build-linux (push) Successful in 44s
Build Dusk / build-psp (push) Successful in 55s
2025-11-19 13:00:35 -06:00
1668c4b0d2 Bit more rendering
Some checks failed
Build Dusk / build-linux (push) Failing after 53s
Build Dusk / build-psp (push) Failing after 47s
2025-11-19 09:52:31 -06:00
2179a27bf5 Prog
All checks were successful
Build Dusk / build-linux (push) Successful in 1m3s
Build Dusk / build-psp (push) Successful in 1m10s
2025-11-19 09:14:32 -06:00
6e7a0cba76 Readme
All checks were successful
Build Dusk / build-linux (push) Successful in 54s
Build Dusk / build-psp (push) Successful in 1m8s
2025-11-17 11:28:10 -06:00
69b37b30bc Finally ready to merge the two tool codebases
All checks were successful
Build Dusk / build-linux (push) Successful in 54s
Build Dusk / build-psp (push) Successful in 1m3s
2025-11-16 23:52:52 -06:00
ae941a0fdb Fixed crash
All checks were successful
Build Dusk / build-linux (push) Successful in 51s
Build Dusk / build-psp (push) Successful in 56s
2025-11-16 17:24:54 -06:00
1b741a81e5 Add .editor to ignore
All checks were successful
Build Dusk / build-linux (push) Successful in 42s
Build Dusk / build-psp (push) Successful in 51s
2025-11-16 16:26:59 -06:00
edf321515b Remove .editor
Some checks failed
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / build-linux (push) Has been cancelled
2025-11-16 16:26:49 -06:00
c874e6c197 Fixed some stuff, procrastinating the real problem
All checks were successful
Build Dusk / build-linux (push) Successful in 47s
Build Dusk / build-psp (push) Successful in 1m6s
2025-11-16 16:18:01 -06:00
9a59c22288 Try load chunk data.
All checks were successful
Build Dusk / build-linux (push) Successful in 53s
Build Dusk / build-psp (push) Successful in 56s
2025-11-16 15:02:18 -06:00
750e8840f0 Prepping editor more...
All checks were successful
Build Dusk / build-linux (push) Successful in 39s
Build Dusk / build-psp (push) Successful in 1m6s
2025-11-16 14:43:29 -06:00
cf59989167 Closer to actually editing
All checks were successful
Build Dusk / build-linux (push) Successful in 50s
Build Dusk / build-psp (push) Successful in 59s
2025-11-16 10:40:20 -06:00
7c194ab4b4 About to draw chunk
All checks were successful
Build Dusk / build-linux (push) Successful in 1m5s
Build Dusk / build-psp (push) Successful in 1m5s
2025-11-16 09:11:58 -06:00
be422d0a1e More langtool improvements
All checks were successful
Build Dusk / build-linux (push) Successful in 1m2s
Build Dusk / build-psp (push) Successful in 1m5s
2025-11-16 00:04:28 -06:00
68b63d3007 Start work on editor
All checks were successful
Build Dusk / build-linux (push) Successful in 54s
Build Dusk / build-psp (push) Successful in 1m10s
2025-11-15 23:38:31 -06:00
8525138594 test
All checks were successful
Build Dusk / build-linux (push) Successful in 54s
Build Dusk / build-psp (push) Successful in 1m9s
2025-11-15 22:31:08 -06:00
7b9f8b190e Fix?
Some checks failed
Build Dusk / build-linux (push) Failing after 52s
Build Dusk / build-psp (push) Failing after 55s
2025-11-15 22:29:07 -06:00
67f62daa9f Cmake fix
Some checks failed
Build Dusk / build-linux (push) Failing after 38s
Build Dusk / build-psp (push) Failing after 43s
2025-11-15 22:27:09 -06:00
0ec701f30b Libs
Some checks failed
Build Dusk / build-linux (push) Failing after 41s
Build Dusk / build-psp (push) Failing after 46s
2025-11-15 22:23:56 -06:00
c53439066e Cleanup, prep for editor
Some checks failed
Build Dusk / build-linux (push) Failing after 40s
Build Dusk / build-psp (push) Failing after 1m7s
2025-11-15 22:21:03 -06:00
7278bd0c6f Remove file
Some checks failed
Build Dusk / build-linux (push) Failing after 43s
Build Dusk / build-psp (push) Failing after 1m6s
2025-11-15 20:06:44 -06:00
b842e5821a Add defs generator.
Some checks failed
Build Dusk / build-linux (push) Failing after 52s
Build Dusk / build-psp (push) Failing after 1m9s
2025-11-12 19:25:10 -06:00
f7d4cce485 Remove release for now
All checks were successful
Build Dusk / build-linux (push) Successful in 47s
Build Dusk / build-psp (push) Successful in 56s
2025-11-12 15:41:48 -06:00
4f502b707f Test4
All checks were successful
Build Dusk / build-linux (push) Successful in 42s
Build Dusk / build-psp (push) Successful in 49s
Build Dusk / release (push) Successful in 6s
2025-11-12 15:38:39 -06:00
09f182228f Test3? 2025-11-12 15:38:23 -06:00
69ce48a8b9 Test2
All checks were successful
Build Dusk / build-linux (push) Successful in 47s
Build Dusk / build-psp (push) Successful in 52s
Build Dusk / release (push) Successful in 7s
2025-11-12 15:33:46 -06:00
5c2788efe4 Test
Some checks failed
Build Dusk / build-linux (push) Successful in 48s
Build Dusk / build-psp (push) Successful in 53s
Build Dusk / release (push) Failing after 7s
2025-11-12 15:31:18 -06:00
768323b5b6 Fix GHES error
Some checks failed
Build Dusk / build-linux (push) Successful in 50s
Build Dusk / build-psp (push) Successful in 53s
Build Dusk / release (push) Failing after 15s
2025-11-12 15:25:36 -06:00
e203f225e2 Fix build
Some checks failed
Build Dusk / build-psp (push) Failing after 50s
Build Dusk / release (push) Has been skipped
Build Dusk / build-linux (push) Failing after 1m1s
2025-11-12 15:20:41 -06:00
ab1e2476a0 PSP
Some checks failed
Build Dusk / build-linux (push) Successful in 48s
Build Dusk / build-psp (push) Failing after 56s
Build Dusk / release (push) Has been skipped
2025-11-12 15:15:46 -06:00
312f32e786 Add PSP build
Some checks failed
Build Dusk / build-linux (push) Successful in 43s
Build Dusk / build-psp (push) Successful in 58s
Build Dusk / release (push) Failing after 6s
2025-11-12 15:11:00 -06:00
397466f0a8 Fix ubuntu compiling
Some checks failed
Build Dusk / build-linux (push) Successful in 43s
Build Dusk / release (push) Failing after 12s
2025-11-12 15:09:17 -06:00
1a773cb8ba TEST
Some checks failed
Build Dusk / build-linux (push) Failing after 1m32s
Build Dusk / release (push) Has been skipped
2025-11-12 14:54:32 -06:00
8441c325fa Add pthread
Some checks failed
Build Dusk / build-linux (push) Failing after 42s
Build Dusk / release (push) Has been skipped
2025-11-12 14:51:38 -06:00
cd4a1afbba add case
Some checks failed
Build Dusk / build-linux (push) Failing after 50s
Build Dusk / release (push) Has been skipped
2025-11-12 14:48:45 -06:00
ae75a932bf WSrap switch with braces
Some checks failed
Build Dusk / build-linux (push) Failing after 51s
Build Dusk / release (push) Has been skipped
2025-11-12 14:46:20 -06:00
0fa2beede4 Init error state
Some checks failed
Build Dusk / build-linux (push) Failing after 37s
Build Dusk / release (push) Has been skipped
2025-11-12 14:12:57 -06:00
8e5d5ca1d7 Try affixing to const in initialization
Some checks failed
Build Dusk / build-linux (push) Failing after 49s
Build Dusk / release (push) Has been skipped
2025-11-12 14:11:48 -06:00
2a68414eec Try fix error state (s)
Some checks failed
Build Dusk / build-linux (push) Failing after 49s
Build Dusk / release (push) Has been skipped
2025-11-12 14:10:17 -06:00
3f1c8e28e9 Try fix error state?
Some checks failed
Build Dusk / build-linux (push) Failing after 42s
Build Dusk / release (push) Has been skipped
2025-11-12 14:08:41 -06:00
348531352e libzip dev
Some checks failed
Build Dusk / build-linux (push) Failing after 48s
Build Dusk / release (push) Has been skipped
2025-11-12 13:10:01 -06:00
6770cc422a polib
Some checks failed
Build Dusk / build-linux (push) Failing after 45s
Build Dusk / release (push) Has been skipped
2025-11-12 13:07:41 -06:00
7c157e22c7 Test linux build first
Some checks failed
Build Dusk / build-linux (push) Failing after 41s
Build Dusk / release (push) Has been skipped
2025-11-12 13:05:06 -06:00
0cfc6d0503 Omit sudo
Some checks failed
Build Dusk / build-linux (push) Failing after 36s
Build Dusk / build-psp (push) Failing after 31s
Build Dusk / release (push) Has been skipped
2025-11-12 13:03:13 -06:00
f7fbd16e57 TEst
Some checks failed
Build Dusk / build-linux (push) Failing after 48s
Build Dusk / build-psp (push) Failing after 6s
Build Dusk / release (push) Has been skipped
2025-11-12 12:59:12 -06:00
542aeadf0f Handle stairs better 2025-11-11 23:40:50 -06:00
84593867dc Realized why so much vram was being used. 2025-11-11 22:54:02 -06:00
9f23533069 Make stairs work 2025-11-11 22:12:08 -06:00
4f8f6a47cb Fixed some memory things on PSP 2025-11-11 20:07:28 -06:00
7d7a3f30e6 Test 2025-11-11 19:52:09 -06:00
d39ed1ea5a Chunk loading improvements 2025-11-11 19:36:04 -06:00
5c8b314689 basically chunk loading 2025-11-11 19:24:56 -06:00
9953d7d388 Prog 2025-11-11 15:50:57 -06:00
5adf8a0773 Prepping map stuff 2025-11-11 12:25:46 -06:00
26bfb912f1 Tiles 2025-11-11 07:50:20 -06:00
c07d0b32a9 qucik fx 2025-11-10 16:41:15 -06:00
562da971e9 Tile under foot 2025-11-10 11:01:41 -06:00
3eb24da475 idk2 2025-11-09 22:17:26 -06:00
8977d50992 idk 2025-11-09 22:09:22 -06:00
13365dd390 Couple world pos fixes/improvements. 2025-11-09 21:19:28 -06:00
aee06f51f0 Improve worldpos.h 2025-11-09 20:55:41 -06:00
d6c497731f Fix PSP compiled 2025-11-09 20:42:03 -06:00
f23e26d9da Add ragequit 2025-11-09 19:21:00 -06:00
ec324e02f2 Fix PSP Deadzones 2025-11-09 19:19:36 -06:00
e2ce809762 Add more debug 2025-11-09 19:04:40 -06:00
d054cf9e36 Fixed animation 2025-11-09 18:50:02 -06:00
943e775364 Time is better. 2025-11-09 18:32:33 -06:00
b9ec6523d6 Back to work 2025-11-09 16:41:54 -06:00
5206d47b43 PSP now reads data directly from EBOOT if requested 2025-11-09 15:42:26 -06:00
aaa8622956 Fixed PSP rendering 2025-11-09 14:54:33 -06:00
587d716aae Fix PSP build issues 2025-11-09 13:23:15 -06:00
5a8710cc76 Map loading and rendering 2025-11-09 13:00:43 -06:00
307f3a9dec Chunk loading example 2025-11-09 12:50:15 -06:00
eff5fc3d9a Fixed asset header compare incosistenty 2025-11-09 10:24:45 -06:00
5a3004f1d1 map 2025-11-09 09:52:47 -06:00
db589b7d91 Textbox example. 2025-11-08 17:26:25 -06:00
0a83175b66 Brought over microjrpg cutscene system (partially implemented) 2025-11-08 15:23:22 -06:00
bc4776f096 Language finished. 2025-11-08 11:12:04 -06:00
ab534bb998 Asset custom ready. 2025-11-08 08:41:32 -06:00
cf2aacd75b About to implement load strategy 2025-11-08 08:32:21 -06:00
9f88374627 Language chunking script done (untested) 2025-11-08 08:17:11 -06:00
b7d898b505 working on asset still 2025-11-07 23:04:40 -06:00
12c1fb6000 lang 2025-11-07 19:19:17 -06:00
1ce1fdff8d Refactor asset loading 2025-11-04 22:23:44 -06:00
7c11a7e5bc Started asset refact 2025-11-04 10:15:19 -06:00
7d46b98310 Ent movement 2025-11-04 09:03:36 -06:00
68c4834a62 Cursed input update 2025-11-04 08:46:47 -06:00
c9608ad7a7 Made input work on fixed timesteps primarily. 2025-11-04 08:41:18 -06:00
6ea4132ff9 Interact 2025-11-03 22:35:40 -06:00
be79356f42 Ent movement fixed. 2025-11-03 21:35:21 -06:00
d4a2e059d7 Entity 2025-11-03 19:50:23 -06:00
f3d985ecbc Starting ent stuff 2025-11-03 17:04:07 -06:00
bcba693afb Initial scene 2025-11-03 14:45:05 -06:00
b4fb7bf99f default binds 2025-11-03 14:33:42 -06:00
3ef6205ea3 Nuked console 2025-11-03 09:22:18 -06:00
3feb43fdad idk 2025-10-26 08:06:39 -05:00
d74226dab1 Ent 2025-10-25 21:15:13 -05:00
5c3db5d991 Fix vec3 copy 2025-10-24 11:30:09 -05:00
bcb8bea0fe Fixes build on the laptop? dunno why 2025-10-14 15:44:38 -05:00
0c0650a2c3 Fixed camera 2025-10-13 12:26:59 -05:00
2c0fd84c72 commit deez 2025-10-12 18:24:09 -05:00
81cd03e0c3 pixel? 2025-10-10 16:28:44 -05:00
349e6e7c94 Back to floats. 2025-10-10 09:16:08 -05:00
c4c43b23ad prog 2025-10-09 15:07:07 -05:00
7622f81309 Friction, velocity, rendering 2025-10-09 09:44:17 -05:00
c31bcf7f6a cam test 2025-10-08 23:06:39 -05:00
fef31b9102 Example rendering 2025-10-08 22:34:27 -05:00
20cf016b06 scene stuff 2025-10-08 15:18:38 -05:00
67604eca8d FPS 2025-10-08 14:17:58 -05:00
46f820690d Setup rpg stuff 2025-10-08 07:11:53 -05:00
e36256abe3 Scene fixing 2025-10-08 06:53:37 -05:00
b00ca3d48c scene crap 2025-10-06 23:16:19 -05:00
cf2e6bf382 cmd screen 2025-10-06 19:18:30 -05:00
fc52afdb00 Refator pass 1 2025-10-06 19:14:52 -05:00
85434b4edb aspect 2025-10-06 16:30:44 -05:00
f3a6c8df71 More screen modes. 2025-10-06 16:23:36 -05:00
6e5c5f61db screen mode 2025-10-06 16:18:53 -05:00
12c31ba9d1 screen 2025-10-06 15:43:27 -05:00
ea50d893d4 prog 2025-10-06 13:59:59 -05:00
bacd0e6e39 Minesweeper prog 2025-10-06 09:35:05 -05:00
c0cd4ead04 Frame 2025-10-02 18:53:47 -05:00
4b04fc65ad scerne stuff 2025-10-01 19:21:20 -05:00
83243ba32f Draw FPS 2025-10-01 17:59:41 -05:00
a734ecaa10 Game updates 2025-10-01 17:07:29 -05:00
22e2f703db Refact 2025-10-01 13:20:34 -05:00
28174b8dc8 Show Frame time 2025-09-24 19:33:10 -05:00
061352bcff load map first pass 2025-09-19 12:43:57 -05:00
2f40724258 Map saving first pass 2025-09-18 17:01:10 -05:00
a45a2a5bd7 Basic interaction 2025-09-17 18:54:12 -05:00
08221af3f8 entity dir 2025-09-17 18:38:14 -05:00
f799690d3c Physics I guess 2025-09-15 19:37:01 -05:00
07ab2b4b02 Gave up on editor 2025-09-15 09:53:34 -05:00
cafeda4368 Subdirize assets, add TSX support 2025-09-15 09:28:12 -05:00
517b39649c npc start 2025-09-14 08:58:54 -05:00
067b0d2e9f Added screen 2025-09-12 15:56:21 -05:00
9b98181d28 Process tileset. 2025-09-12 12:43:56 -05:00
46a94ecacd Renders on PSP again 2025-09-12 00:25:17 -05:00
964a9f64f2 Overworld render test. 2025-09-11 23:33:24 -05:00
b4d94c2cbe FPS toggle 2025-09-11 22:25:57 -05:00
268e9ffefd Render is color not uint8_t 2025-09-11 22:20:21 -05:00
c8f8170ec2 Command aliasing 2025-09-11 22:07:51 -05:00
8b20f0bf31 Allowed binds to execute commands. 2025-09-11 12:58:04 -05:00
fe9af039fc Add kb defs 2025-09-10 22:01:00 -05:00
6fad5bef4a Input bind complete. 2025-09-08 13:30:27 -05:00
16a0403fd4 Working on cmd bind 2025-09-07 23:53:21 -05:00
e32d1f0900 Add exec command 2025-09-07 23:24:21 -05:00
3f37b7cdb5 Beginning input refactor 2025-09-03 11:57:48 -05:00
059ccf41b6 draw text. 2025-09-02 23:01:15 -05:00
1af2b8f47b Render text working. 2025-09-02 22:47:07 -05:00
87f18d0e13 Rendering on PSP again. 2025-09-02 21:14:07 -05:00
8de12da1ec Palettized image test. 2025-09-02 18:57:28 -05:00
71080682cc Fixed an error bug 2025-09-02 09:23:46 -05:00
f915a4208b Roughly planning asset locking 2025-09-01 22:07:36 -05:00
14c41d33a7 Asset manager refactor begin. 2025-09-01 21:00:50 -05:00
3e61d6f84d Palette assets improved. 2025-09-01 20:07:12 -05:00
4541d5219b About to refator tools... again 2025-09-01 17:22:33 -05:00
3ce1566a2e Merge 2025-09-01 11:10:28 -05:00
368729f0f3 Fixed some camera bugs. 2025-09-01 11:02:30 -05:00
127392a1ae Added findzip 2025-09-01 09:39:38 -05:00
af1329710d Made a big mess of the codebase 2025-08-28 07:14:13 -05:00
30232d1275 Palette image (incomplete) 2025-08-27 22:55:47 -05:00
31fa4948d5 Image generation? 2025-08-27 22:43:38 -05:00
6c11096fd2 Palette loading done. 2025-08-27 19:59:55 -05:00
7a90d2d38f prog 2025-08-27 07:46:11 -05:00
a543bc7c00 commit asset prog 2025-08-26 16:23:08 -05:00
8af2f044ed Asset prog 2025-08-25 10:16:55 -05:00
947f21cac7 Relative 2025-08-24 15:18:01 -05:00
479aad2f06 Going to redo assets. 2025-08-24 13:57:12 -05:00
329925ea54 Asset 2025-08-23 17:43:22 -05:00
c8a3ebfcec FindSDL2 2025-08-23 15:53:08 -05:00
688 changed files with 28810 additions and 11713 deletions

117
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,117 @@
name: Build Dusk
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
run-tests:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Set up Docker
uses: docker/setup-docker-action@v5
- name: Run tests in Docker
run: ./scripts/test-linux-docker.sh
build-linux:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Set up Docker
uses: docker/setup-docker-action@v5
- name: Build Linux
run: ./scripts/build-linux-docker.sh
- name: Upload Linux binary
uses: actions/upload-artifact@v6
with:
name: dusk-linux
path: build-linux/Dusk
if-no-files-found: error
build-psp:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Set up Docker
uses: docker/setup-docker-action@v5
- name: Build psp
run: ./scripts/build-psp-docker.sh
- name: Move EBOOT.PBP to Dusk subfolder
run: |
mkdir -p ./git-artifcats/Dusk/PSP/GAME/Dusk
cp build-psp/EBOOT.PBP ./git-artifcats/Dusk/PSP/GAME/Dusk/EBOOT.PBP
- name: Upload psp binary
uses: actions/upload-artifact@v6
with:
name: dusk-psp
path: ./git-artifcats/Dusk
if-no-files-found: error
build-knulli:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Set up Docker
uses: docker/setup-docker-action@v5
- name: Build knulli
run: ./scripts/build-knulli-docker.sh
- name: Move output to Dusk subfolder
run: |
mkdir -p ./git-artifcats/Dusk
cp -r build-knulli/dusk ./git-artifcats/Dusk
- name: Upload knulli binary
uses: actions/upload-artifact@v6
with:
name: dusk-knulli
path: ./git-artifcats/Dusk
if-no-files-found: error
build-gamecube:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Set up Docker
uses: docker/setup-docker-action@v5
- name: Build GameCube
run: ./scripts/build-gamecube-docker.sh
- name: Copy output files.
run: |
mkdir -p ./git-artifcats/Dusk
cp build-gamecube/Dusk.dol ./git-artifcats/Dusk/Dusk.dol
cp build-gamecube/dusk.dsk ./git-artifcats/Dusk/dusk.dsk
- name: Upload GameCube binary
uses: actions/upload-artifact@v6
with:
name: dusk-gamecube
path: ./git-artifcats/Dusk
if-no-files-found: error
build-wii:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Set up Docker
uses: docker/setup-docker-action@v5
- name: Build Wii
run: ./scripts/build-wii-docker.sh
- name: Copy output files.
run: |
mkdir -p ./git-artifcats/Dusk/apps/Dusk
cp build-wii/Dusk.dol ./git-artifcats/Dusk/apps/Dusk/Dusk.dol
cp build-wii/dusk.dsk ./git-artifcats/Dusk/apps/Dusk/dusk.dsk
cp docker/dolphin/meta.xml ./git-artifcats/Dusk/apps/Dusk/meta.xml
- name: Upload Wii binary
uses: actions/upload-artifact@v6
with:
name: dusk-wii
path: ./git-artifcats/Dusk
if-no-files-found: error

10
.gitignore vendored
View File

@@ -95,3 +95,13 @@ assets/borrowed
# /archive
__pycache__
package-lock.json
yarn-error.log
yarn.lock
.editor
.venv
/build2
/build*

View File

@@ -4,15 +4,13 @@
# https://opensource.org/licenses/MIT
# Setup
cmake_minimum_required(VERSION 3.13)
set(CMAKE_C_STANDARD 99)
cmake_minimum_required(VERSION 3.18)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules)
if(NOT DEFINED DUSK_TARGET_SYSTEM)
set(DUSK_TARGET_SYSTEM "linux")
# set(DUSK_TARGET_SYSTEM "psp")
endif()
option(DUSK_BUILD_TESTS "Enable tests" OFF)
# Prep cache
set(DUSK_CACHE_TARGET "dusk-target")
@@ -21,76 +19,96 @@ set(DUSK_CACHE_TARGET "dusk-target")
set(DUSK_ROOT_DIR "${CMAKE_SOURCE_DIR}")
set(DUSK_BUILD_DIR "${CMAKE_BINARY_DIR}")
set(DUSK_SOURCES_DIR "${DUSK_ROOT_DIR}/src")
set(DUSK_TEST_DIR "${DUSK_ROOT_DIR}/test")
set(DUSK_TEMP_DIR "${DUSK_BUILD_DIR}/temp")
set(DUSK_TOOLS_DIR "${DUSK_ROOT_DIR}/tools")
set(DUSK_DATA_DIR "${DUSK_ROOT_DIR}/data")
set(DUSK_ASSETS_DIR "${DUSK_ROOT_DIR}/assets")
set(DUSK_BUILT_ASSETS_DIR "${DUSK_BUILD_DIR}/built_assets" CACHE INTERNAL ${DUSK_CACHE_TARGET})
set(DUSK_GENERATED_HEADERS_DIR "${DUSK_BUILD_DIR}/generated")
set(DUSK_TARGET_NAME "Dusk" CACHE INTERNAL ${DUSK_CACHE_TARGET})
set(DUSK_BUILD_BINARY ${DUSK_BUILD_DIR}/Dusk CACHE INTERNAL ${DUSK_CACHE_TARGET})
set(DUSK_ASSETS "" CACHE INTERNAL ${DUSK_CACHE_TARGET})
set(DUSK_LIBRARY_TARGET_NAME "DuskCore" CACHE INTERNAL ${DUSK_CACHE_TARGET})
set(DUSK_BINARY_TARGET_NAME "Dusk" CACHE INTERNAL ${DUSK_CACHE_TARGET})
set(DUSK_ASSETS_ZIP "${DUSK_BUILD_DIR}/dusk.dsk" CACHE INTERNAL ${DUSK_CACHE_TARGET})
# Toolchain
if(NOT DEFINED DUSK_TARGET_SYSTEM)
set(DUSK_TARGET_SYSTEM "linux")
endif()
# Create directories
file(MAKE_DIRECTORY ${DUSK_GENERATED_HEADERS_DIR})
file(MAKE_DIRECTORY ${DUSK_ASSETS_BUILD_DIR})
file(MAKE_DIRECTORY ${DUSK_TEMP_DIR})
file(MAKE_DIRECTORY ${DUSK_BUILT_ASSETS_DIR})
# Compilers
if(DUSK_TARGET_SYSTEM STREQUAL "psp")
find_package(pspsdk REQUIRED)
endif()
# Required build packages
find_package(Python3 COMPONENTS Interpreter REQUIRED)
# Init Project
project(${DUSK_TARGET_NAME}
# Init Project.
project(${DUSK_LIBRARY_TARGET_NAME}
VERSION 1.0.0
LANGUAGES C
)
# Executable
add_executable(${DUSK_TARGET_NAME})
# Either, create library and binary separately (used for tests), or make them
# one in the same so all code is in the binary only.
# Binary Executable
add_executable(${DUSK_BINARY_TARGET_NAME} ${DUSK_SOURCES_DIR}/dusk/null.c)
if(DUSK_BUILD_TESTS)
# MainLibrary
add_library(${DUSK_LIBRARY_TARGET_NAME} STATIC)
# Link library to binary
target_link_libraries(${DUSK_BINARY_TARGET_NAME}
PUBLIC
${DUSK_LIBRARY_TARGET_NAME}
)
else()
set(DUSK_LIBRARY_TARGET_NAME "${DUSK_BINARY_TARGET_NAME}" CACHE INTERNAL ${DUSK_CACHE_TARGET})
endif()
# Definitions
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
DUSK_TARGET_SYSTEM="${DUSK_TARGET_SYSTEM}"
)
# Toolchains
include(cmake/targets/${DUSK_TARGET_SYSTEM}.cmake)
# Add tools
add_subdirectory(tools)
# Add libraries
if(DUSK_TARGET_SYSTEM STREQUAL "linux")
find_package(SDL2 REQUIRED)
find_package(OpenGL REQUIRED)
target_link_libraries(${DUSK_TARGET_NAME} PRIVATE
SDL2::SDL2
OpenGL::GL
GL
)
elseif(DUSK_TARGET_SYSTEM STREQUAL "psp")
find_package(SDL2 REQUIRED)
target_link_libraries(${DUSK_TARGET_NAME}
PRIVATE
# pspsdk
${SDL2_LIBRARIES}
)
target_include_directories(${DUSK_TARGET_NAME}
PRIVATE
${SDL2_INCLUDE_DIRS}
)
endif()
# Add code
add_subdirectory(src)
# Include generated headers
target_include_directories(${DUSK_TARGET_NAME} PUBLIC
# Include generated headers from tools.
target_include_directories(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
${DUSK_GENERATED_HEADERS_DIR}
)
# Postbuild, create PBP file for PSP.
if(DUSK_TARGET_SYSTEM STREQUAL "psp")
create_pbp_file(
TARGET "${DUSK_TARGET_NAME}"
ICON_PATH NULL
BACKGROUND_PATH NULL
PREVIEW_PATH NULL
TITLE "${DUSK_TARGET_NAME}"
VERSION 01.00
)
# Add main code
add_subdirectory(${DUSK_SOURCES_DIR})
# Include generated headers
target_include_directories(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
${DUSK_GENERATED_HEADERS_DIR}
)
# Handle tests
if(DUSK_BUILD_TESTS)
enable_testing()
add_subdirectory(test)
endif()
# Build assets
file(GLOB_RECURSE DUSK_ASSET_FILES CONFIGURE_DEPENDS "${DUSK_ASSETS_DIR}/*")
add_custom_command(
OUTPUT "${DUSK_ASSETS_ZIP}"
COMMAND ${CMAKE_COMMAND} -E make_directory "${DUSK_ASSETS_DIR}"
COMMAND ${CMAKE_COMMAND} -E rm -f "${DUSK_ASSETS_ZIP}"
COMMAND ${CMAKE_COMMAND} -E tar "cf" "${DUSK_ASSETS_ZIP}" --format=zip -- .
WORKING_DIRECTORY "${DUSK_ASSETS_DIR}"
DEPENDS ${DUSK_ASSET_FILES}
VERBATIM
)
add_custom_target(DUSK_ASSETS_BUILT DEPENDS "${DUSK_ASSETS_ZIP}")
add_dependencies(${DUSK_LIBRARY_TARGET_NAME} DUSK_ASSETS_BUILT)

23
README.md Normal file
View File

@@ -0,0 +1,23 @@
# Dusk
RPG Game Project, small and able to run on a PSP.
# Building
Each build target has different requirements. You can take a look at the git
workflow to see how the builds are done for each target. In addition, for
accessing the editor and building the game on your host system, install the
following packages, depending on your system;
Fedora;
```
sudo dnf install git make gcc cmake python python-polib python3-pillow python3-dotenv python3-numpy python-qt5 python3-pyopengl SDL2-devel zlib-devel libzip-devel bzip2-devel openssl-devel lzma-sdk-devel xz xz-devel lua-devel
```
Ubuntu;
```
sudo apt-get install git build-essential gcc python python-polib python3-pillow python3-dotenv python3-numpy python3-pyqt5 python3-opengl
```
Arch Linux;
```
sudo pacman -S git base-devel gcc python python-polib python-pillow python-dotenv python-numpy python-pyqt5 python-opengl
```

12
archive/asset/args.py Normal file
View File

@@ -0,0 +1,12 @@
import sys, os
import argparse
# Check if the script is run with the correct arguments
parser = argparse.ArgumentParser(description="Generate chunk header files")
parser.add_argument('--assets', required=True, help='Dir to output built assets')
parser.add_argument('--headers-dir', required=True, help='Directory to output individual asset headers (required for header build)')
parser.add_argument('--output-headers', help='Output header file for built assets (required for header build)')
parser.add_argument('--output-assets', required=True, help='Output directory for built assets')
parser.add_argument('--output-file', required=True, help='Output file for built assets (required for wad build)')
parser.add_argument('--input', required=True, help='Input assets to process', nargs='+')
args = parser.parse_args()

View File

@@ -0,0 +1,66 @@
import sys, os
from tools.asset.args import args
from tools.asset.process.asset import processAsset
from tools.asset.process.palette import processPaletteList
from tools.asset.process.tileset import processTilesetList
from tools.asset.process.language import processLanguageList
from tools.asset.path import getBuiltAssetsRelativePath
import zipfile
# Parse input file args.
inputAssets = []
for inputArg in args.input:
files = inputArg.split('$')
for file in files:
if str(file).strip() == '':
continue
pieces = file.split('#')
if len(pieces) < 2:
print(f"Error: Invalid input asset format '{file}'. Expected format: type#path[#option1%option2...]")
sys.exit(1)
options = {}
if len(pieces) > 2:
optionParts = pieces[2].split('%')
for part in optionParts:
partSplit = part.split('=')
if len(partSplit) < 1:
continue
if len(partSplit) == 2:
options[partSplit[0]] = partSplit[1]
else:
options[partSplit[0]] = True
inputAssets.append({
'type': pieces[0],
'path': pieces[1],
'options': options
})
if not inputAssets:
print("Error: No input assets provided.")
sys.exit(1)
# Process each asset.
files = []
for asset in inputAssets:
asset = processAsset(asset)
files.extend(asset['files'])
# Generate additional files
files.extend(processLanguageList()['files'])
# Take assets and add to a zip archive.
outputFileName = args.output_file
print(f"Creating output file: {outputFileName}")
with zipfile.ZipFile(outputFileName, 'w') as zipf:
for file in files:
relativeOutputPath = getBuiltAssetsRelativePath(file)
zipf.write(file, arcname=relativeOutputPath)
# Generate additional headers.
processPaletteList()
processTilesetList()

12
archive/asset/cache.py Normal file
View File

@@ -0,0 +1,12 @@
processedAssets = {}
def assetGetCache(assetPath):
if assetPath in processedAssets:
return processedAssets[assetPath]
return None
def assetCache(assetPath, processedData):
if assetPath in processedAssets:
return processedAssets[assetPath]
processedAssets[assetPath] = processedData
return processedData

10
archive/asset/path.py Normal file
View File

@@ -0,0 +1,10 @@
import os
from tools.asset.args import args
def getAssetRelativePath(fullPath):
# Get the relative path to the asset
return os.path.relpath(fullPath, start=args.assets).replace('\\', '/')
def getBuiltAssetsRelativePath(fullPath):
# Get the relative path to the built asset
return os.path.relpath(fullPath, start=args.output_assets).replace('\\', '/')

View File

@@ -0,0 +1,33 @@
import sys
# from processtileset import processTileset
from tools.asset.process.image import processImage
from tools.asset.process.palette import processPalette
from tools.asset.process.tileset import processTileset
from tools.asset.process.map import processMap
from tools.asset.process.language import processLanguage
from tools.asset.process.script import processScript
processedAssets = []
def processAsset(asset):
if asset['path'] in processedAssets:
return
processedAssets.append(asset['path'])
# Handle tiled tilesets
t = asset['type'].lower()
if t == 'palette':
return processPalette(asset)
elif t == 'image':
return processImage(asset)
elif t == 'tileset':
return processTileset(asset)
elif t == 'map':
return processMap(asset)
elif t == 'language':
return processLanguage(asset)
elif t == 'script':
return processScript(asset)
else:
print(f"Error: Unknown asset type '{asset['type']}' for path '{asset['path']}'")
sys.exit(1)

View File

@@ -0,0 +1,134 @@
import os
import sys
from PIL import Image
from tools.asset.process.palette import extractPaletteFromImage, palettes
from tools.asset.args import args
from tools.asset.path import getAssetRelativePath
from tools.asset.cache import assetGetCache, assetCache
images = []
def processImage(asset):
cache = assetGetCache(asset['path'])
if cache is not None:
return cache
type = None
if 'type' in asset['options']:
type = asset['options'].get('type', 'PALETTIZED').upper()
if type == 'PALETTIZED' or type is None:
return assetCache(asset['path'], processPalettizedImage(asset))
elif type == 'ALPHA':
return assetCache(asset['path'], processAlphaImage(asset))
else:
print(f"Error: Unknown image type {type} for asset {asset['path']}")
sys.exit(1)
def processPalettizedImage(asset):
assetPath = asset['path']
cache = assetGetCache(assetPath)
if cache is not None:
return cache
image = Image.open(assetPath)
imagePalette = extractPaletteFromImage(image)
# Find palette that contains every color
palette = None
for p in palettes:
hasAllColors = True
for color in imagePalette:
for palColor in p['pixels']:
if color[0] == palColor[0] and color[1] == palColor[1] and color[2] == palColor[2] and color[3] == palColor[3]:
break
elif color[3] == 0 and palColor[3] == 0:
break
else:
print('Pallete {} does not contain color #{}'.format(p['paletteName'], '{:02x}{:02x}{:02x}{:02x}'.format(color[0], color[1], color[2], color[3])))
hasAllColors = False
break
if hasAllColors:
palette = p
break
if palette is None:
palette = palettes[0] # Just to avoid reference error
print(f"No matching palette found for {assetPath}!")
# Find which pixel is missing
for color in imagePalette:
if color in palette['pixels']:
continue
# Convert to hex (with alpha)
hexColor = '#{:02x}{:02x}{:02x}{:02x}'.format(color[0], color[1], color[2], color[3])
print(f"Missing color: {hexColor} in palette {palette['paletteName']}")
sys.exit(1)
print(f"Converting image {assetPath} to use palette")
paletteIndexes = []
for pixel in list(image.getdata()):
if pixel[3] == 0:
pixel = (0, 0, 0, 0)
paletteIndex = palette['pixels'].index(pixel)
paletteIndexes.append(paletteIndex)
data = bytearray()
data.extend(b"DPI") # Dusk Palettized Image
data.extend(image.width.to_bytes(4, 'little')) # Width
data.extend(image.height.to_bytes(4, 'little')) # Height
data.append(palette['paletteIndex']) # Palette index
for paletteIndex in paletteIndexes:
if paletteIndex > 255 or paletteIndex < 0:
print(f"Error: Palette index {paletteIndex} exceeds 255!")
sys.exit(1)
data.append(paletteIndex.to_bytes(1, 'little')[0]) # Pixel index
relative = getAssetRelativePath(assetPath)
fileNameWithoutExt = os.path.splitext(os.path.basename(assetPath))[0]
outputFileRelative = os.path.join(os.path.dirname(relative), f"{fileNameWithoutExt}.dpi")
outputFilePath = os.path.join(args.output_assets, outputFileRelative)
os.makedirs(os.path.dirname(outputFilePath), exist_ok=True)
with open(outputFilePath, "wb") as f:
f.write(data)
outImage = {
"imagePath": outputFileRelative,
"files": [ outputFilePath ],
'width': image.width,
'height': image.height,
}
return assetCache(assetPath, outImage)
def processAlphaImage(asset):
assetPath = asset['path']
cache = assetGetCache(assetPath)
if cache is not None:
return cache
print(f"Processing alpha image: {assetPath}")
data = bytearray()
data.extend(b"DAI") # Dusk Alpha Image
image = Image.open(assetPath).convert("RGBA")
data.extend(image.width.to_bytes(4, 'little')) # Width
data.extend(image.height.to_bytes(4, 'little')) # Height
for pixel in list(image.getdata()):
# Only write alpha channel
data.append(pixel[3].to_bytes(1, 'little')[0]) # Pixel alpha
relative = getAssetRelativePath(assetPath)
fileNameWithoutExt = os.path.splitext(os.path.basename(assetPath))[0]
outputFileRelative = os.path.join(os.path.dirname(relative), f"{fileNameWithoutExt}.dai")
outputFilePath = os.path.join(args.output_assets, outputFileRelative)
os.makedirs(os.path.dirname(outputFilePath), exist_ok=True)
with open(outputFilePath, "wb") as f:
f.write(data)
outImage = {
"imagePath": outputFileRelative,
"files": [ outputFilePath ],
'width': image.width,
'height': image.height,
}
return assetCache(assetPath, outImage)

View File

@@ -0,0 +1,228 @@
import sys
import os
from tools.asset.args import args
from tools.asset.cache import assetCache, assetGetCache
from tools.asset.path import getAssetRelativePath
from tools.dusk.defs import defs
import polib
import re
LANGUAGE_CHUNK_CHAR_COUNT = int(defs.get('ASSET_LANG_CHUNK_CHAR_COUNT'))
LANGUAGE_DATA = {}
LANGUAGE_KEYS = []
def processLanguageList():
# Language keys header data
headerKeys = "// Auto-generated language keys header file.\n"
headerKeys += "#pragma once\n"
headerKeys += "#include \"dusk.h\"\n\n"
# This is the desired chunk groups list.. if a language key STARTS with any
# of the keys in this list we would "like to" put it in that chunk group.
# If there is no match, or the list is full then we will add it to the next
# available chunk group (that isn't a 'desired' one). If the chunk becomes
# full, then we attempt to make another chunk with the same prefix so that
# a second batching can occur.
desiredChunkGroups = {
'ui': 0
}
# Now, for each language key, create the header reference and index.
keyIndex = 0
languageKeyIndexes = {}
languageKeyChunk = {}
languageKeyChunkIndexes = {}
languageKeyChunkOffsets = {}
for key in LANGUAGE_KEYS:
headerKeys += f"#define {getLanguageVariableName(key)} {keyIndex}\n"
languageKeyIndexes[key] = keyIndex
keyIndex += 1
# Find desired chunk group
assignedChunk = None
for desiredKey in desiredChunkGroups:
if key.lower().startswith(desiredKey):
assignedChunk = desiredChunkGroups[desiredKey]
break
# If no desired chunk group matched, assign to -1
if assignedChunk is None:
assignedChunk = -1
languageKeyChunk[key] = assignedChunk
# Setup header.
for lang in LANGUAGE_DATA:
if key not in LANGUAGE_DATA[lang]:
print(f"Warning: Missing translation for key '{key}' in language '{lang}'")
sys.exit(1)
# Seal the header.
headerKeys += f"\n#define LANG_KEY_COUNT {len(LANGUAGE_KEYS)}\n"
# Now we can generate the language string chunks.
nextChunkIndex = max(desiredChunkGroups.values()) + 1
files = []
for lang in LANGUAGE_DATA:
langData = LANGUAGE_DATA[lang]
# Key = chunkIndex, value = chunkInfo
languageChunks = {}
for key in LANGUAGE_KEYS:
keyIndex = languageKeyIndexes[key]
chunkIndex = languageKeyChunk[key]
wasSetChunk = chunkIndex != -1
# This will keep looping until we find a chunk
while True:
# Determine the next chunkIndex IF chunkIndex is -1
if chunkIndex == -1:
chunkIndex = nextChunkIndex
# Is the chunk full?
curLen = languageChunks.get(chunkIndex, {'len': 0})['len']
newLen = curLen + len(langData[key])
if newLen > LANGUAGE_CHUNK_CHAR_COUNT:
# Chunk is full, need to create a new chunk.
chunkIndex = -1
if wasSetChunk:
wasSetChunk = False
else:
nextChunkIndex += 1
continue
# Chunk is not full, we can use it.
if chunkIndex not in languageChunks:
languageChunks[chunkIndex] = {
'len': 0,
'keys': []
}
languageChunks[chunkIndex]['len'] = newLen
languageChunks[chunkIndex]['keys'].append(key)
languageKeyChunkIndexes[key] = chunkIndex
languageKeyChunkOffsets[key] = curLen
break
# We have now chunked all the keys for this language!
langBuffer = b""
# Write header info
langBuffer += b'DLF' # Dusk Language File
for key in LANGUAGE_KEYS:
# Write the chunk that this key belongs to as uint32_t
chunkIndex = languageKeyChunkIndexes[key]
langBuffer += chunkIndex.to_bytes(4, byteorder='little')
# Write the offset for this key as uint32_t
offset = languageKeyChunkOffsets[key]
langBuffer += offset.to_bytes(4, byteorder='little')
# Write the length of the string as uint32_t
strData = langData[key].encode('utf-8')
langBuffer += len(strData).to_bytes(4, byteorder='little')
# Now write out each chunk's string data, packed tight and no null term.
for chunkIndex in sorted(languageChunks.keys()):
chunkInfo = languageChunks[chunkIndex]
for key in chunkInfo['keys']:
strData = langData[key].encode('utf-8')
langBuffer += strData
# Now pad the chunk to full size
curLen = chunkInfo['len']
if curLen < LANGUAGE_CHUNK_CHAR_COUNT:
padSize = LANGUAGE_CHUNK_CHAR_COUNT - curLen
langBuffer += b'\0' * padSize
# Write out the language data file
outputFile = os.path.join(args.output_assets, "language", f"{lang}.dlf")
files.append(outputFile)
os.makedirs(os.path.dirname(outputFile), exist_ok=True)
with open(outputFile, "wb") as f:
f.write(langBuffer)
# Write out the language keys header file
outputFile = os.path.join(args.headers_dir, "locale", "language", "keys.h")
os.makedirs(os.path.dirname(outputFile), exist_ok=True)
with open(outputFile, "w") as f:
f.write(headerKeys)
# Generate language list.
langValues = {}
headerLocale = "#pragma once\n#include \"locale/localeinfo.h\"\n\n"
headerLocale += "typedef enum {\n"
count = 0
headerLocale += f" DUSK_LOCALE_NULL = {count},\n"
count += 1
for lang in LANGUAGE_DATA:
langKey = lang.replace('-', '_').replace(' ', '_').upper()
langValues[lang] = count
headerLocale += f" DUSK_LOCALE_{langKey} = {count},\n"
count += 1
headerLocale += f" DUSK_LOCALE_COUNT = {count}\n"
headerLocale += "} dusklocale_t;\n\n"
headerLocale += f"static const localeinfo_t LOCALE_INFOS[DUSK_LOCALE_COUNT] = {{\n"
for lang in LANGUAGE_DATA:
langKey = lang.replace('-', '_').replace(' ', '_').upper()
headerLocale += f" [DUSK_LOCALE_{langKey}] = {{\n"
headerLocale += f" .file = \"{lang}\"\n"
headerLocale += f" }},\n"
headerLocale += "};\n"
headerLocale += f"static const char_t *LOCALE_SCRIPT = \n"
for lang in LANGUAGE_DATA:
langKey = lang.replace('-', '_').replace(' ', '_').upper()
langValue = langValues[lang]
headerLocale += f" \"DUSK_LOCALE_{langKey} = {langValue}\\n\"\n"
headerLocale += ";\n"
# Write out the locale enum header file
outputFile = os.path.join(args.headers_dir, "locale", "locale.h")
os.makedirs(os.path.dirname(outputFile), exist_ok=True)
with open(outputFile, "w") as f:
f.write(headerLocale)
return {
'files': files
}
def getLanguageVariableName(languageKey):
# Take the language key, prepend LANG_, uppercase, replace any non symbols
# with _
key = languageKey.strip().upper()
key = re.sub(r'[^A-Z0-9]', '_', key)
return f"LANG_{key}"
def processLanguage(asset):
cache = assetGetCache(asset['path'])
if cache is not None:
return cache
# Load PO File
po = polib.pofile(asset['path'])
langName = po.metadata.get('Language')
if langName not in LANGUAGE_DATA:
LANGUAGE_DATA[langName] = {}
for entry in po:
key = entry.msgid
val = entry.msgstr
if key not in LANGUAGE_KEYS:
LANGUAGE_KEYS.append(key)
if key not in LANGUAGE_DATA[langName]:
LANGUAGE_DATA[langName][key] = val
else:
print(f"Error: Duplicate translation key '{key}' in language '{langName}'")
sys.exit(1)
outLanguageData = {
'data': po,
'path': asset['path'],
'files': []
}
return assetCache(asset['path'], outLanguageData)

View File

@@ -0,0 +1,154 @@
import struct
import sys
import os
import json
from tools.asset.args import args
from tools.asset.cache import assetCache, assetGetCache
from tools.asset.path import getAssetRelativePath
from tools.dusk.defs import TILE_WIDTH, TILE_HEIGHT, TILE_DEPTH, CHUNK_WIDTH, CHUNK_HEIGHT, CHUNK_DEPTH, CHUNK_TILE_COUNT
from tools.dusk.map import Map
from tools.dusk.chunk import Chunk
def convertModelData(modelData):
# TLDR; Model data stores things efficiently with indices, but we buffer it
# out to 6 vertex quads for simplicity.
outVertices = []
outUVs = []
outColors = []
for indice in modelData['indices']:
vertex = modelData['vertices'][indice]
uv = modelData['uvs'][indice]
color = modelData['colors'][indice]
outVertices.append(vertex)
outUVs.append(uv)
outColors.append(color)
return {
'vertices': outVertices,
'uvs': outUVs,
'colors': outColors
}
def processChunk(chunk):
cache = assetGetCache(chunk.getFilename())
if cache:
return cache
baseModel = {
'vertices': [],
'colors': [],
'uvs': []
}
models = [ baseModel ]
for tileIndex, tile in chunk.tiles.items():
tileBase = tile.getBaseTileModel()
convertedBase = convertModelData(tileBase)
baseModel['vertices'].extend(convertedBase['vertices'])
baseModel['colors'].extend(convertedBase['colors'])
baseModel['uvs'].extend(convertedBase['uvs'])
# Generate binary buffer for efficient output
buffer = bytearray()
buffer.extend(b'DMC')# Header
buffer.extend(len(chunk.tiles).to_bytes(4, 'little')) # Number of tiles
buffer.extend(len(models).to_bytes(1, 'little')) # Number of models
buffer.extend(len(chunk.entities).to_bytes(1, 'little')) # Number of entities
# Buffer tile data as array of uint8_t
for tileIndex, tile in chunk.tiles.items():
buffer.extend(tile.shape.to_bytes(1, 'little'))
# # For each model
for model in models:
vertexCount = len(model['vertices'])
buffer.extend(vertexCount.to_bytes(4, 'little'))
for i in range(vertexCount):
vertex = model['vertices'][i]
uv = model['uvs'][i]
color = model['colors'][i]
buffer.extend(color[0].to_bytes(1, 'little'))
buffer.extend(color[1].to_bytes(1, 'little'))
buffer.extend(color[2].to_bytes(1, 'little'))
buffer.extend(color[3].to_bytes(1, 'little'))
buffer.extend(bytearray(struct.pack('<f', uv[0])))
buffer.extend(bytearray(struct.pack('<f', uv[1])))
buffer.extend(bytearray(struct.pack('<f', vertex[0])))
buffer.extend(bytearray(struct.pack('<f', vertex[1])))
buffer.extend(bytearray(struct.pack('<f', vertex[2])))
# For each entity
for entity in chunk.entities.values():
buffer.extend(entity.type.to_bytes(1, 'little'))
buffer.extend(entity.localX.to_bytes(1, 'little'))
buffer.extend(entity.localY.to_bytes(1, 'little'))
buffer.extend(entity.localZ.to_bytes(1, 'little'))
pass
# Write out map file
relative = getAssetRelativePath(chunk.getFilename())
fileNameWithoutExt = os.path.splitext(os.path.basename(relative))[0]
outputFileRelative = os.path.join(os.path.dirname(relative), f"{fileNameWithoutExt}.dmc")
outputFilePath = os.path.join(args.output_assets, outputFileRelative)
os.makedirs(os.path.dirname(outputFilePath), exist_ok=True)
with open(outputFilePath, "wb") as f:
f.write(buffer)
outChunk = {
'files': [ outputFilePath ],
'chunk': chunk
}
return assetCache(chunk.getFilename(), outChunk)
def processMap(asset):
cache = assetGetCache(asset['path'])
if cache is not None:
return cache
map = Map(None)
map.load(asset['path'])
chunksDir = map.getChunkDirectory()
files = os.listdir(chunksDir)
if len(files) == 0:
print(f"Error: No chunk files found in {chunksDir}.")
sys.exit(1)
chunkFiles = []
for fileName in files:
if not fileName.endswith('.json'):
continue
fNameNoExt = os.path.splitext(fileName)[0]
fnPieces = fNameNoExt.split('_')
if len(fnPieces) != 3:
print(f"Error: Chunk filename {fileName} does not contain valid chunk coordinates.")
sys.exit(1)
chunk = Chunk(map, int(fnPieces[0]), int(fnPieces[1]), int(fnPieces[2]))
chunk.load()
result = processChunk(chunk)
chunkFiles.extend(result['files'])
# Map file
outBuffer = bytearray()
outBuffer.extend(b'DMF')
outBuffer.extend(len(chunkFiles).to_bytes(4, 'little'))
# DMF (Dusk Map file)
fileRelative = getAssetRelativePath(asset['path'])
fileNameWithoutExt = os.path.splitext(os.path.basename(fileRelative))[0]
outputMapRelative = os.path.join(os.path.dirname(fileRelative), f"{fileNameWithoutExt}.dmf")
outputMapPath = os.path.join(args.output_assets, outputMapRelative)
os.makedirs(os.path.dirname(outputMapPath), exist_ok=True)
with open(outputMapPath, "wb") as f:
f.write(outBuffer)
outMap = {
'files': chunkFiles
}
outMap['files'].append(outputMapPath)
return assetCache(asset['path'], outMap)

View File

@@ -0,0 +1,96 @@
import json
import os
from PIL import Image
import datetime
from tools.asset.args import args
from tools.asset.cache import assetCache, assetGetCache
palettes = []
def extractPaletteFromImage(image):
# goes through and finds all unique colors in the image
if image.mode != 'RGBA':
image = image.convert('RGBA')
pixels = list(image.getdata())
uniqueColors = []
for color in pixels:
# We treat all alpha 0 as rgba(0,0,0,0) for palette purposes
if color[3] == 0:
color = (0, 0, 0, 0)
if color not in uniqueColors:
uniqueColors.append(color)
return uniqueColors
def processPalette(asset):
print(f"Processing palette: {asset['path']}")
cache = assetGetCache(asset['path'])
if cache is not None:
return cache
paletteIndex = len(palettes)
image = Image.open(asset['path'])
pixels = extractPaletteFromImage(image)
fileNameWithoutExt = os.path.splitext(os.path.basename(asset['path']))[0]
fileNameWithoutPalette = os.path.splitext(fileNameWithoutExt)[0]
# PSP requires that the palette size be a power of two, so we will pad the
# palette with transparent colors if needed.
def mathNextPowTwo(x):
return 1 << (x - 1).bit_length()
nextPowTwo = mathNextPowTwo(len(pixels))
while len(pixels) < nextPowTwo:
pixels.append((0, 0, 0, 0))
# Header
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
data = f"// Palette Generated for {asset['path']} at {now}\n"
data += f"#include \"display/palette/palette.h\"\n\n"
data += f"#define PALETTE_{paletteIndex}_COLOR_COUNT {len(pixels)}\n\n"
data += f"#pragma pack(push, 1)\n"
data += f"static const color_t PALETTE_{paletteIndex}_COLORS[PALETTE_{paletteIndex}_COLOR_COUNT] = {{\n"
for pixel in pixels:
data += f" {{ 0x{pixel[0]:02X}, 0x{pixel[1]:02X}, 0x{pixel[2]:02X}, 0x{pixel[3]:02X} }},\n"
data += f"}};\n"
data += f"#pragma pack(pop)\n\n"
data += f"static const palette_t PALETTE_{paletteIndex} = {{\n"
data += f" .colorCount = PALETTE_{paletteIndex}_COLOR_COUNT,\n"
data += f" .colors = PALETTE_{paletteIndex}_COLORS,\n"
data += f"}};\n"
# Write Header
outputFile = os.path.join(args.headers_dir, "display", "palette", f"palette_{paletteIndex}.h")
os.makedirs(os.path.dirname(outputFile), exist_ok=True)
with open(outputFile, "w") as f:
f.write(data)
palette = {
"paletteIndex": paletteIndex,
"paletteName": fileNameWithoutPalette,
"pixels": pixels,
"headerFile": os.path.relpath(outputFile, args.headers_dir),
"asset": asset,
"files": [ ],# No zippable files.
}
palettes.append(palette)
return assetCache(asset['path'], palette)
def processPaletteList():
data = f"// Auto-generated palette list\n"
print(f"Generating palette list with {len(palettes)} palettes.")
for palette in palettes:
data += f"#include \"{palette['headerFile']}\"\n"
data += f"\n"
data += f"#define PALETTE_LIST_COUNT {len(palettes)}\n\n"
data += f"static const palette_t* PALETTE_LIST[PALETTE_LIST_COUNT] = {{\n"
for palette in palettes:
data += f" &PALETTE_{palette['paletteIndex']},\n"
data += f"}};\n"
# Write the palette list to a header file
outputFile = os.path.join(args.headers_dir, "display", "palette", "palettelist.h")
os.makedirs(os.path.dirname(outputFile), exist_ok=True)
with open(outputFile, "w") as f:
f.write(data)

View File

@@ -0,0 +1,43 @@
import sys
import os
from tools.asset.args import args
from tools.asset.cache import assetCache, assetGetCache
from tools.asset.path import getAssetRelativePath
from tools.dusk.defs import fileDefs
def processScript(asset):
cache = assetGetCache(asset['path'])
if cache is not None:
return cache
# Load the lua file as a string
with open(asset['path'], 'r', encoding='utf-8') as f:
luaCode = f.read()
# TODO: I will precompile or minify the Lua code here in the future
# Replace all definitions in the code
for key, val in fileDefs.items():
luaCode = luaCode.replace(key, str(val))
# Create output Dusk Script File (DSF) data
data = ""
data += "DSF"
data += luaCode
# Write to relative output file path.
relative = getAssetRelativePath(asset['path'])
fileNameWithoutExt = os.path.splitext(os.path.basename(asset['path']))[0]
outputFileRelative = os.path.join(os.path.dirname(relative), f"{fileNameWithoutExt}.dsf")
outputFilePath = os.path.join(args.output_assets, outputFileRelative)
os.makedirs(os.path.dirname(outputFilePath), exist_ok=True)
with open(outputFilePath, "wb") as f:
f.write(data.encode('utf-8'))
outScript = {
'data': data,
'path': asset['path'],
'files': [ outputFilePath ],
'scriptPath': outputFileRelative,
}
return assetCache(asset['path'], outScript)

View File

@@ -0,0 +1,178 @@
import json
import sys
import os
import datetime
from xml.etree import ElementTree
from tools.asset.process.image import processImage
from tools.asset.path import getAssetRelativePath
from tools.asset.args import args
from tools.asset.cache import assetGetCache, assetCache
tilesets = []
def loadTilesetFromTSX(asset):
# Load the TSX file
tree = ElementTree.parse(asset['path'])
root = tree.getroot()
# Expect tileheight, tilewidth, columns and tilecount attributes
if 'tilewidth' not in root.attrib or 'tileheight' not in root.attrib or 'columns' not in root.attrib or 'tilecount' not in root.attrib:
print(f"Error: TSX file {asset['path']} is missing required attributes (tilewidth, tileheight, columns, tilecount)")
sys.exit(1)
tileWidth = int(root.attrib['tilewidth'])
tileHeight = int(root.attrib['tileheight'])
columns = int(root.attrib['columns'])
tileCount = int(root.attrib['tilecount'])
rows = (tileCount + columns - 1) // columns # Calculate rows based on tileCount and columns
# Find the image element
imageElement = root.find('image')
if imageElement is None or 'source' not in imageElement.attrib:
print(f"Error: TSX file {asset['path']} is missing an image element with a source attribute")
sys.exit(1)
imagePath = imageElement.attrib['source']
# Image is relative to the TSX file
imageAssetPath = os.path.join(os.path.dirname(asset['path']), imagePath)
image = processImage({
'path': imageAssetPath,
'options': asset['options'],
})
return {
"image": image,
"tileWidth": tileWidth,
"tileHeight": tileHeight,
"columns": columns,
"rows": rows,
"originalWidth": tileWidth * columns,
"originalHeight": tileHeight * rows,
}
def loadTilesetFromArgs(asset):
# We need to determine how big each tile is. This can either be provided as
# an arg of tileWidth/tileHeight or as a count of rows/columns.
# Additionally, if the image has been factored, then the user can provide both
# tile sizes AND cols/rows to indicate the original size of the image.
image = processImage(asset)
tileWidth, tileHeight = None, None
columns, rows = None, None
originalWidth, originalHeight = image['width'], image['height']
if 'tileWidth' in asset['options'] and 'columns' in asset['options']:
tileWidth = int(asset['options']['tileWidth'])
columns = int(asset['options']['columns'])
originalWidth = tileWidth * columns
elif 'tileWidth' in asset['options']:
tileWidth = int(asset['options']['tileWidth'])
columns = image['width'] // tileWidth
elif 'columns' in asset['options']:
columns = int(asset['options']['columns'])
tileWidth = image['width'] // columns
else:
print(f"Error: Tileset {asset['path']} must specify either tileWidth or columns")
sys.exit(1)
if 'tileHeight' in asset['options'] and 'rows' in asset['options']:
tileHeight = int(asset['options']['tileHeight'])
rows = int(asset['options']['rows'])
originalHeight = tileHeight * rows
elif 'tileHeight' in asset['options']:
tileHeight = int(asset['options']['tileHeight'])
rows = image['height'] // tileHeight
elif 'rows' in asset['options']:
rows = int(asset['options']['rows'])
tileHeight = image['height'] // rows
else:
print(f"Error: Tileset {asset['path']} must specify either tileHeight or rows")
sys.exit(1)
return {
"image": image,
"tileWidth": tileWidth,
"tileHeight": tileHeight,
"columns": columns,
"rows": rows,
"originalWidth": originalWidth,
"originalHeight": originalHeight,
}
def processTileset(asset):
cache = assetGetCache(asset['path'])
if cache is not None:
return cache
print(f"Processing tileset: {asset['path']}")
tilesetData = None
if asset['path'].endswith('.tsx'):
tilesetData = loadTilesetFromTSX(asset)
else:
tilesetData = loadTilesetFromArgs(asset)
fileNameWithoutExtension = os.path.splitext(os.path.basename(asset['path']))[0]
now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
tilesetName = fileNameWithoutExtension
tilesetNameUpper = tilesetName.upper()
widthScale = tilesetData['originalWidth'] / tilesetData['image']['width']
heightScale = tilesetData['originalHeight'] / tilesetData['image']['height']
# Create header
data = f"// Tileset Generated for {asset['path']} at {now}\n"
data += f"#pragma once\n"
data += f"#include \"display/tileset/tileset.h\"\n\n"
data += f"static const tileset_t TILESET_{tilesetNameUpper} = {{\n"
data += f" .name = {json.dumps(tilesetName)},\n"
data += f" .tileWidth = {tilesetData['tileWidth']},\n"
data += f" .tileHeight = {tilesetData['tileHeight']},\n"
data += f" .tileCount = {tilesetData['columns'] * tilesetData['rows']},\n"
data += f" .columns = {tilesetData['columns']},\n"
data += f" .rows = {tilesetData['rows']},\n"
data += f" .uv = {{ {widthScale / tilesetData['columns']}f, {heightScale / tilesetData['rows']}f }},\n"
data += f" .image = {json.dumps(tilesetData['image']['imagePath'])},\n"
data += f"}};\n"
# Write Header
outputFile = os.path.join(args.headers_dir, "display", "tileset", f"tileset_{tilesetName}.h")
os.makedirs(os.path.dirname(outputFile), exist_ok=True)
with open(outputFile, 'w') as f:
f.write(data)
print(f"Write header for tileset: {outputFile}")
tileset = {
"files": [],
"image": tilesetData['image'],
"headerFile": os.path.relpath(outputFile, args.headers_dir),
"tilesetName": tilesetName,
"tilesetNameUpper": tilesetNameUpper,
"tilesetIndex": len(tilesets),
"tilesetData": tilesetData,
"files": tilesetData['image']['files'],
}
tilesets.append(tileset)
return assetCache(asset['path'], tileset)
def processTilesetList():
data = f"// Tileset List Generated at {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
data += f"#pragma once\n"
for tileset in tilesets:
data += f"#include \"{tileset['headerFile']}\"\n"
data += f"\n"
data += f"#define TILESET_LIST_COUNT {len(tilesets)}\n\n"
data += f"static const tileset_t* TILESET_LIST[TILESET_LIST_COUNT] = {{\n"
for tileset in tilesets:
data += f" &TILESET_{tileset['tilesetNameUpper']},\n"
data += f"}};\n"
# Write header.
outputFile = os.path.join(args.headers_dir, "display", "tileset", f"tilesetlist.h")
os.makedirs(os.path.dirname(outputFile), exist_ok=True)
with open(outputFile, 'w') as f:
f.write(data)

47
archive/assetpalette.c Normal file
View File

@@ -0,0 +1,47 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "assetpalette.h"
#include "asset/assettype.h"
#include "assert/assert.h"
errorret_t assetPaletteLoad(assetentire_t entire) {
assertNotNull(entire.data, "Data pointer cannot be NULL.");
assertNotNull(entire.output, "Output pointer cannot be NULL.");
assetpalette_t *assetData = (assetpalette_t *)entire.data;
palette_t *palette = (palette_t *)entire.output;
// Read header and version (first 4 bytes)
if(
assetData->header[0] != 'D' ||
assetData->header[1] != 'P' ||
assetData->header[2] != 'F'
) {
errorThrow("Invalid palette header");
}
// Version (can only be 1 atm)
if(assetData->version != 0x01) {
errorThrow("Unsupported palette version");
}
// Check color count.
if(
assetData->colorCount == 0 ||
assetData->colorCount > PALETTE_COLOR_COUNT_MAX
) {
errorThrow("Invalid palette color count");
}
paletteInit(
palette,
assetData->colorCount,
assetData->colors
);
errorOk();
}

30
archive/assetpalette.h Normal file
View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "error/error.h"
#include "display/texture/palette.h"
typedef struct assetentire_s assetentire_t;
#pragma pack(push, 1)
typedef struct {
char_t header[3];
uint8_t version;
uint8_t colorCount;
color_t colors[PALETTE_COLOR_COUNT_MAX];
} assetpalette_t;
#pragma pack(pop)
/**
* Loads a palette from the given data pointer into the output palette.
*
* @param entire Data received from the asset loader system.
* @return An error code.
*/
errorret_t assetPaletteLoad(assetentire_t entire);

View File

@@ -1,37 +0,0 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Libs
target_link_libraries(${DUSK_TARGET_NAME}
PUBLIC
m
)
# Includes
target_include_directories(${DUSK_TARGET_NAME}
PRIVATE
${CMAKE_CURRENT_LIST_DIR}
)
# Sources
target_sources(${DUSK_TARGET_NAME}
PRIVATE
game.c
input.c
time.c
)
# Subdirs
add_subdirectory(assert)
add_subdirectory(console)
add_subdirectory(display)
add_subdirectory(error)
add_subdirectory(entity)
add_subdirectory(event)
add_subdirectory(item)
add_subdirectory(locale)
add_subdirectory(ui)
add_subdirectory(util)
add_subdirectory(world)

View File

@@ -1,85 +0,0 @@
/**
* Copyright (c) 2023 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "assert.h"
#ifndef ASSERTIONS_FAKED
void assertTrueImpl(
const char *file,
const int32_t line,
const bool x,
const char *message
) {
if(x != true) {
fprintf(
stderr,
"Assertion Failed in %s:%i\n\n%s\n",
file,
line,
message
);
abort();
}
}
void assertFalseImpl(
const char *file,
const int32_t line,
bool x,
const char *message
) {
assertTrueImpl(file, line, !x, message);
}
void assertUnreachableImpl(
const char *file,
const int32_t line,
const char *message
) {
assertTrueImpl(file, line, false, message);
}
void assertNotNullImpl(
const char *file,
const int32_t line,
const void *pointer,
const char *message
) {
assertTrueImpl(
file,
line,
pointer != NULL,
message
);
// Ensure we can touch it
volatile char temp;
temp = *((char*)pointer);
}
void assertNullImpl(
const char *file,
const int32_t line,
const void *pointer,
const char *message
) {
assertTrueImpl(
file,
line,
pointer == NULL,
message
);
}
void assertDeprecatedImpl(
const char *file,
const int32_t line,
const char *message
) {
assertUnreachableImpl(file, line, message);
}
#endif

View File

@@ -1,143 +0,0 @@
/**
* Copyright (c) 2023 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
#ifndef ASSERTIONS_FAKED
/**
* Assert a given value to be true.
*
* @param file File that the assertion is being made from.
* @param line Line that the assertion is being made from.
* @param x Value to assert as true.
* @param message Message to throw against assertion failure.
*/
void assertTrueImpl(
const char *file,
const int32_t line,
const bool_t x,
const char *message
);
/**
* Asserts a given statement to be false.
*
* @param file File that the assertion is being made from.
* @param line Line that the assertion is being made from.
* @param x Value to assert as false.
* @param message Message to throw against assertion failure.
*/
void assertFalseImpl(
const char *file,
const int32_t line,
const bool_t x,
const char *message
);
/**
* Asserts that a given line of code is unreachable. Essentially a forced
* assertion failure, good for "edge cases"
*
* @param file File that the assertion is being made from.
* @param line Line that the assertion is being made from.
* @param message Message to throw against assertion failure.
*/
void assertUnreachableImpl(
const char *file,
const int32_t line,
const char *message
);
/**
* Assert a given pointer to not point to a null pointer.
*
* @param file File that the assertion is being made from.
* @param line Line that the assertion is being made from.
* @param pointer Pointer to assert is not a null pointer.
* @param message Message to throw against assertion failure.
*/
void assertNotNullImpl(
const char *file,
const int32_t line,
const void *pointer,
const char *message
);
/**
* Asserts a given pointer to be a nullptr.
*
* @param file File that the assertion is being made from.
* @param line Line that the assertion is being made from.
* @param pointer Pointer to assert is nullptr.
* @param message Message to throw against assertion failure.
*/
void assertNullImpl(
const char *file,
const int32_t line,
const void *pointer,
const char *message
);
/**
* Asserts a function as being deprecated.
*
* @param file File that the assertion is being made from.
* @param line Line that the assertion is being made from.
* @param message Message to throw against assertion failure.
*/
void assertDeprecatedImpl(
const char *file,
const int32_t line,
const char *message
);
void assertMemoryRangeMatchesImpl(
const char *file,
const int32_t line,
const void *start,
const void *end,
const size_t size,
const char *message
);
#define assertTrue(x, message) \
assertTrueImpl(__FILE__, __LINE__, x, message)
#define assertFalse(x, message) \
assertFalseImpl(__FILE__, __LINE__, x, message)
#define assertUnreachable(message) \
assertUnreachableImpl(__FILE__, __LINE__, message)
#define assertNotNull(pointer, message) \
assertNotNullImpl(__FILE__, __LINE__, pointer, message)
#define assertNull(pointer, message) \
assertNullImpl(__FILE__, __LINE__, pointer, message)
#define assertDeprecated(message) \
assertDeprecatedImpl(__FILE__, __LINE__, message)
#define assertStrLenMax(str, len, message) \
assertTrue(strlen(str) < len, message)
#define assertStrLenMin(str, len, message) \
assertTrue(strlen(str) >= len, message)
#else
// If assertions are faked, we define the macros to do nothing.
#define assertTrue(x, message) ((void)0)
#define assertFalse(x, message) ((void)0)
#define assertUnreachable(message) ((void)0)
#define assertNotNull(pointer, message) ((void)0)
#define assertNull(pointer, message) ((void)0)
#define assertDeprecated(message) ((void)0)
#define assertStrLenMax(str, len, message) ((void)0)
#define assertStrLenMin(str, len, message) ((void)0)
#endif

158
archive/dusk/chunk.py Normal file
View File

@@ -0,0 +1,158 @@
import json
import os
from tools.dusk.event import Event
from tools.dusk.defs import CHUNK_WIDTH, CHUNK_HEIGHT, CHUNK_DEPTH, CHUNK_VERTEX_COUNT_MAX, TILE_SHAPE_NULL
from tools.dusk.tile import Tile
from tools.dusk.entity import Entity
from tools.dusk.region import Region
from tools.editor.map.vertexbuffer import VertexBuffer
from OpenGL.GL import *
class Chunk:
def __init__(self, map, x, y, z):
self.map = map
self.x = x
self.y = y
self.z = z
self.current = {}
self.original = {}
self.entities = {}
self.regions = {}
self.onChunkData = Event()
self.dirty = False
self.tiles = {}
self.vertexBuffer = VertexBuffer()
# Test Region
region = self.regions[0] = Region(self)
region.minX = 0
region.minY = 0
region.minZ = 0
region.maxX = 32
region.maxY = 32
region.maxZ = 32
region.updateVertexs()
# Gen tiles.
tileIndex = 0
for tz in range(CHUNK_DEPTH):
for ty in range(CHUNK_HEIGHT):
for tx in range(CHUNK_WIDTH):
self.tiles[tileIndex] = Tile(self, tx, ty, tz, tileIndex)
tileIndex += 1
# Update vertices
self.tileUpdateVertices()
def reload(self, newX, newY, newZ):
self.x = newX
self.y = newY
self.z = newZ
self.entities = {}
for tile in self.tiles.values():
tile.chunkReload(newX, newY, newZ)
self.load()
def tileUpdateVertices(self):
self.vertexBuffer.clear()
for tile in self.tiles.values():
tile.buffer(self.vertexBuffer)
self.vertexBuffer.buildData()
def load(self):
fname = self.getFilename()
if not fname or not os.path.exists(fname):
self.new()
return
try:
with open(fname, 'r') as f:
data = json.load(f)
if not 'shapes' in data:
data['shapes'] = []
# For each tile.
for tile in self.tiles.values():
tile.load(data)
# For each entity.
self.entities = {}
if 'entities' in data:
for id, entData in enumerate(data['entities']):
ent = Entity(self)
ent.load(entData)
self.entities[id] = ent
self.tileUpdateVertices()
self.dirty = False
self.onChunkData.invoke(self)
self.map.onEntityData.invoke()
except Exception as e:
raise RuntimeError(f"Failed to load chunk file: {e}")
def save(self):
if not self.isDirty():
return
dataOut = {
'shapes': [],
'entities': []
}
for tile in self.tiles.values():
dataOut['shapes'].append(tile.shape)
for ent in self.entities.values():
entData = {}
ent.save(entData)
dataOut['entities'].append(entData)
fname = self.getFilename()
if not fname:
raise ValueError("No filename specified for saving chunk.")
try:
with open(fname, 'w') as f:
json.dump(dataOut, f)
self.dirty = False
self.onChunkData.invoke(self)
except Exception as e:
raise RuntimeError(f"Failed to save chunk file: {e}")
def new(self):
for tile in self.tiles.values():
tile.shape = TILE_SHAPE_NULL
self.tileUpdateVertices()
self.dirty = False
self.onChunkData.invoke(self)
def isDirty(self):
return self.dirty
def getFilename(self):
if not self.map or not hasattr(self.map, 'getChunkDirectory'):
return None
dirPath = self.map.getChunkDirectory()
if dirPath is None:
return None
return f"{dirPath}/{self.x}_{self.y}_{self.z}.json"
def draw(self):
self.vertexBuffer.draw()
def addEntity(self, localX=0, localY=0, localZ=0):
ent = Entity(self, localX, localY, localZ)
self.entities[len(self.entities)] = ent
self.map.onEntityData.invoke()
self.dirty = True
return ent
def removeEntity(self, entity):
for key, val in list(self.entities.items()):
if val == entity:
del self.entities[key]
self.map.onEntityData.invoke()
self.dirty = True
return True
return False

View File

@@ -1,18 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "console/console.h"
void cmdEcho(const consolecmdexec_t *exec) {
assertTrue(
exec->argc >= 1,
"echo command requires 1 argument."
);
consolePrint("%s", exec->argv[0]);
}

View File

@@ -1,25 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "console/console.h"
void cmdGet(const consolecmdexec_t *exec) {
assertTrue(
exec->argc >= 1,
"Get command requires 1 argument."
);
for(uint32_t i = 0; i < CONSOLE.variableCount; i++) {
consolevar_t *var = &CONSOLE.variables[i];
if(stringCompare(var->name, exec->argv[0]) != 0) continue;
consolePrint("%s", var->value);
return;
}
consolePrint("Error: Variable '%s' not found.", exec->argv[0]);
}

View File

@@ -1,15 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "console/console.h"
#include "game.h"
void cmdQuit(const consolecmdexec_t *exec) {
consolePrint("Quitting application...");
GAME.running = false;
}

View File

@@ -1,27 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "console/console.h"
void cmdSet(const consolecmdexec_t *exec) {
assertTrue(exec->argc >= 2, "set command requires 2 arguments.");
for(uint32_t i = 0; i < CONSOLE.variableCount; i++) {
consolevar_t *var = &CONSOLE.variables[i];
if(stringCompare(var->name, exec->argv[0]) != 0) continue;
consoleVarSetValue(var, exec->argv[1]);
consolePrint("%s %s", var->name, var->value);
for(i = 0; i < var->eventCount; i++) {
assertNotNull(var->events[i], "Event is NULL");
var->events[i](var);
}
return;
}
consolePrint("Error: Variable '%s' not found.", exec->argv[0]);
}

View File

@@ -1,329 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "console.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "util/string.h"
#include "console/cmd/cmdquit.h"
#include "console/cmd/cmdecho.h"
#include "console/cmd/cmdset.h"
#include "console/cmd/cmdget.h"
#include "input.h"
console_t CONSOLE;
void consoleInit() {
memoryZero(&CONSOLE, sizeof(console_t));
// Register the get and set command.
CONSOLE.cmdGet = consoleRegCmd("get", cmdGet);
CONSOLE.cmdSet = consoleRegCmd("set", cmdSet);
consoleRegCmd("quit", cmdQuit);
consoleRegCmd("echo", cmdEcho);
consolePrint(" = Dawn Console = ");
}
consolecmd_t * consoleRegCmd(const char_t *name, consolecmdfunc_t function) {
consolecmd_t *cmd = &CONSOLE.commands[CONSOLE.commandCount++];
consoleCmdInit(cmd, name, function);
return cmd;
}
consolevar_t * consoleRegVar(
const char_t *name,
const char_t *value,
consolevarchanged_t event
) {
consolevar_t *var = &CONSOLE.variables[CONSOLE.variableCount++];
consoleVarInitListener(var, name, value, event);
return var;
}
void consolePrint(const char_t *message, ...) {
char_t buffer[CONSOLE_LINE_MAX];
va_list args;
va_start(args, message);
int32_t len = stringFormatVA(buffer, CONSOLE_LINE_MAX, message, args);
va_end(args);
// Move all lines back
memoryMove(
CONSOLE.line[0],
CONSOLE.line[1],
(CONSOLE_HISTORY_MAX - 1) * CONSOLE_LINE_MAX
);
// Copy the new line
memoryCopy(
CONSOLE.line[CONSOLE_HISTORY_MAX - 1],
buffer,
len + 1
);
printf("%s\n", buffer);
}
void consoleExec(const char_t *line) {
assertNotNull(line, "line must not be NULL");
assertTrue(
CONSOLE.execBufferCount < CONSOLE_EXEC_BUFFER_MAX,
"Too many commands in the buffer."
);
char_t buffer[CONSOLE_LINE_MAX];
size_t i = 0, j = 0;
char_t c;
consoleexecstate_t state = CONSOLE_EXEC_STATE_INITIAL;
consolecmdexec_t *exec = NULL;
while(state != CONSOLE_EXEC_STATE_FULLY_PARSED) {
c = line[i];
switch(state) {
case CONSOLE_EXEC_STATE_INITIAL:
assertTrue(j == 0, "Buffer not empty?");
if(c == '\0') {
state = CONSOLE_EXEC_STATE_FULLY_PARSED;
break;
}
if(stringIsWhitespace(c) || c == ';') {
i++;
continue;
}
state = CONSOLE_EXEC_STATE_PARSE_CMD;
break;
case CONSOLE_EXEC_STATE_PARSE_CMD:
if(stringIsWhitespace(c) || c == '\0' || c == ';') {
state = CONSOLE_EXEC_STATE_CMD_PARSED;
continue;
}
if(c == '"') {
// Can't handle quotes within the command.
consolePrint("Invalid command");
while(c != '\0' && c != ';') c = line[++i];
continue;
}
buffer[j++] = c;
i++;
if(j >= CONSOLE_LINE_MAX) {
consolePrint("Command too long");
state = CONSOLE_EXEC_STATE_FULLY_PARSED;
continue;
}
break;
case CONSOLE_EXEC_STATE_CMD_PARSED:
if(j == 0) {
state = CONSOLE_EXEC_STATE_INITIAL;
continue;
}
// Create exec
assertNull(exec, "Existing command parsing?");
exec = &CONSOLE.execBuffer[CONSOLE.execBufferCount];
memoryZero(exec, sizeof(consolecmdexec_t));
buffer[j] = '\0';
stringCopy(exec->command, buffer, CONSOLE_LINE_MAX);
state = CONSOLE_EXEC_STATE_FIND_ARG;
j = 0;// Free up buffer
break;
case CONSOLE_EXEC_STATE_FIND_ARG:
if(c == '\0' || c == ';') {
state = CONSOLE_EXEC_STATE_CMD_FINISHED;
continue;
}
if(stringIsWhitespace(c)) {
i++;
continue;
}
if(c == '"') {
state = CONSOLE_EXEC_STATE_PARSE_ARG_QUOTED;
i++;
} else {
state = CONSOLE_EXEC_STATE_PARSE_ARG;
}
break;
case CONSOLE_EXEC_STATE_PARSE_ARG:
if(stringIsWhitespace(c) || c == '\0' || c == ';') {
state = CONSOLE_EXEC_STATE_ARG_PARSED;
continue;
}
buffer[j++] = c;
i++;
if(j >= CONSOLE_LINE_MAX) {
consolePrint("Arg too long");
state = CONSOLE_EXEC_STATE_FULLY_PARSED;
continue;
}
break;
case CONSOLE_EXEC_STATE_PARSE_ARG_QUOTED:
if(c == '"') {
state = CONSOLE_EXEC_STATE_ARG_PARSED;
i++;
continue;
}
if(c == '\0' || c == ';') {
consolePrint("Unterminated quote");
state = CONSOLE_EXEC_STATE_FULLY_PARSED;
continue;
}
if(c == '\\') {
c = line[++i];
if(c == '\0' || c == ';') {
consolePrint("Unterminated quote");
state = CONSOLE_EXEC_STATE_FULLY_PARSED;
continue;
}
}
buffer[j++] = c;
i++;
if(j >= CONSOLE_LINE_MAX) {
consolePrint("Arg too long");
state = CONSOLE_EXEC_STATE_FULLY_PARSED;
continue;
}
break;
case CONSOLE_EXEC_STATE_ARG_PARSED:
buffer[j] = '\0';
stringCopy(exec->argv[exec->argc++], buffer, CONSOLE_LINE_MAX);
state = CONSOLE_EXEC_STATE_FIND_ARG;
j = 0;// Free up buffer
break;
case CONSOLE_EXEC_STATE_CMD_FINISHED:
assertNotNull(exec, "No command found?");
// Now, is there a command that matches?
for(uint32_t k = 0; k < CONSOLE.commandCount; k++) {
consolecmd_t *cmd = &CONSOLE.commands[k];
if(stringCompare(cmd->name, exec->command) != 0) continue;
exec->cmd = cmd;
break;
}
if(exec->cmd == NULL) {
// Command wasn't found, is there a variable that matches?
for(uint32_t k = 0; k < CONSOLE.variableCount; k++) {
consolevar_t *var = &CONSOLE.variables[k];
if(stringCompare(var->name, exec->command) != 0) continue;
// Matching variable found, is this a GET or a SET?
if(exec->argc == 0) {
exec->cmd = CONSOLE.cmdGet;
stringCopy(exec->argv[0], exec->command, CONSOLE_LINE_MAX);
exec->argc = 1;
} else {
exec->cmd = CONSOLE.cmdSet;
stringCopy(exec->argv[1], exec->argv[0], CONSOLE_LINE_MAX);
stringCopy(exec->argv[0], exec->command, CONSOLE_LINE_MAX);
exec->argc = 2;
}
break;
}
if(exec->cmd == NULL) {
consolePrint("Command not found", exec->command);
exec = NULL;
state = CONSOLE_EXEC_STATE_INITIAL;
break;
}
}
// Prep for next command.
exec = NULL;
state = CONSOLE_EXEC_STATE_INITIAL;
CONSOLE.execBufferCount++;
break;
default:
assertUnreachable("Invalid state.");
break;
}
}
}
// May move these later
void consoleUpdate() {
if(inputPressed(INPUT_BIND_CONSOLE)) {
CONSOLE.visible = !CONSOLE.visible;
if(CONSOLE.visible) {
consolePrint("Console opened.");
} else {
consolePrint("Console closed.");
}
}
for(uint32_t i = 0; i < CONSOLE.execBufferCount; i++) {
consolecmdexec_t *exec = &CONSOLE.execBuffer[i];
assertNotNull(exec->cmd, "Command execution has no command.");
exec->cmd->function(exec);
}
// #if KEYBOARD_SUPPORT == 1
// uint8_t key;
// while((key = inputKeyboardPop()) != 0) {
// printf("Key pressed: %c\n", key);
// switch(key) {
// case 0:
// break;
// case INPUT_KEY_ENTER:
// consoleExec(CONSOLE.inputBuffer);
// CONSOLE.inputIndex = 0;
// CONSOLE.inputBuffer[0] = '\0';
// break;
// case INPUT_KEY_BACKSPACE:
// if(CONSOLE.inputIndex > 0) {
// CONSOLE.inputIndex--;
// CONSOLE.inputBuffer[CONSOLE.inputIndex] = '\0';
// }
// break;
// default:
// if(
// key >= INPUT_KEY_ASCII_START && key <= INPUT_KEY_ASCII_END &&
// CONSOLE.inputIndex < CONSOLE_LINE_MAX - 1
// ) {
// CONSOLE.inputBuffer[CONSOLE.inputIndex++] = key;
// CONSOLE.inputBuffer[CONSOLE.inputIndex] = '\0';
// }
// break;
// }
// }
// #endif
// Clear the exec buffer
CONSOLE.execBufferCount = 0;
}

View File

@@ -1,106 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "consolevar.h"
#include "consolecmd.h"
typedef enum {
CONSOLE_EXEC_STATE_INITIAL,
CONSOLE_EXEC_STATE_PARSE_CMD,
CONSOLE_EXEC_STATE_CMD_PARSED,
CONSOLE_EXEC_STATE_FIND_ARG,
CONSOLE_EXEC_STATE_PARSE_ARG,
CONSOLE_EXEC_STATE_PARSE_ARG_QUOTED,
CONSOLE_EXEC_STATE_ARG_PARSED,
CONSOLE_EXEC_STATE_CMD_FINISHED,
CONSOLE_EXEC_STATE_FULLY_PARSED
} consoleexecstate_t;
typedef struct {
consolecmd_t commands[CONSOLE_COMMANDS_MAX];
uint32_t commandCount;
consolevar_t variables[CONSOLE_VARIABLES_MAX];
uint32_t variableCount;
char_t line[CONSOLE_HISTORY_MAX][CONSOLE_LINE_MAX];
consolecmdexec_t execBuffer[CONSOLE_EXEC_BUFFER_MAX];
uint32_t execBufferCount;
consolecmd_t *cmdGet;
consolecmd_t *cmdSet;
bool_t visible;
// May move these later
// #if KEYBOARD_SUPPORT == 1
// char_t inputBuffer[CONSOLE_LINE_MAX];
// int32_t inputIndex;
// #endif
} console_t;
extern console_t CONSOLE;
/**
* Initializes the console.
*/
void consoleInit();
/**
* Registers a console command.
*
* @param name The name of the command.
* @param function The function to execute when the command is called.
* @return The registered command.
*/
consolecmd_t * consoleRegCmd(const char_t *name, consolecmdfunc_t function);
/**
* Registers a console variable.
*
* @param name The name of the variable.
* @param value The initial value of the variable.
* @param event The event to register.
* @return The registered variable.
*/
consolevar_t * consoleRegVar(
const char_t *name,
const char_t *value,
consolevarchanged_t event
);
/**
* Sets the value of a console variable.
*
* @param name The name of the variable.
* @param value The new value of the variable.
*/
void consolePrint(
const char_t *message,
...
);
/**
* Executes a console command.
*
* @param line The line to execute.
*/
void consoleExec(const char_t *line);
/**
* Processes the console's pending commands.
*/
void consoleUpdate();
void cmdGet(const consolecmdexec_t *exec);
void cmdSet(const consolecmdexec_t *exec);
void cmdEcho(const consolecmdexec_t *exec);
void cmdQuit(const consolecmdexec_t *exec);

View File

@@ -1,27 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "consolecmd.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "util/string.h"
void consoleCmdInit(
consolecmd_t *cmd,
const char_t *name,
consolecmdfunc_t function
) {
assertNotNull(cmd, "Command is NULL.");
assertNotNull(name, "Name is NULL.");
assertNotNull(function, "Function is NULL.");
assertStrLenMin(name, 1, "Name is empty.");
assertStrLenMax(name, CONSOLE_CMD_NAME_MAX, "Name is too long.");
memoryZero(cmd, sizeof(consolecmd_t));
stringCopy(cmd->name, name, CONSOLE_CMD_NAME_MAX);
cmd->function = function;
}

View File

@@ -1,39 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
#include "consoledefs.h"
typedef struct consolecmd_s consolecmd_t;
typedef struct {
consolecmd_t *cmd;
char_t command[CONSOLE_LINE_MAX];
char_t argv[CONSOLE_CMD_ARGC_MAX][CONSOLE_LINE_MAX];
uint32_t argc;
} consolecmdexec_t;
typedef void (*consolecmdfunc_t)(const consolecmdexec_t *exec);
typedef struct consolecmd_s {
char_t name[CONSOLE_CMD_NAME_MAX];
consolecmdfunc_t function;
} consolecmd_t;
/**
* Initializes a console command.
*
* @param cmd Pointer to the console command.
* @param name The name of the command.
* @param function The function to execute when the command is called.
*/
void consoleCmdInit(
consolecmd_t *cmd,
const char_t *name,
consolecmdfunc_t function
);

View File

@@ -1,21 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#define CONSOLE_CMD_NAME_MAX 32
#define CONSOLE_CMD_ARGC_MAX 16
#define CONSOLE_COMMANDS_MAX 128
#define CONSOLE_VARIABLES_MAX 128
#define CONSOLE_LINE_MAX 256
#define CONSOLE_HISTORY_MAX 32
#define CONSOLE_EXEC_BUFFER_MAX 16
#define CONSOLE_VAR_NAME_MAX 32
#define CONSOLE_VAR_VALUE_MAX 128
#define CONSOLE_VAR_EVENTS_MAX 8

View File

@@ -1,64 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "consolevar.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "util/string.h"
void consoleVarInit(
consolevar_t *var,
const char_t *name,
const char_t *value
) {
assertNotNull(var, "var must not be NULL");
assertNotNull(name, "name must not be NULL");
assertNotNull(value, "value must not be NULL");
assertStrLenMin(name, 1, "name must not be empty");
assertStrLenMax(name, CONSOLE_VAR_NAME_MAX, "name is too long");
assertStrLenMax(value, CONSOLE_VAR_VALUE_MAX, "value is too long");
memoryZero(var, sizeof(consolevar_t));
stringCopy(var->name, name, CONSOLE_VAR_NAME_MAX);
stringCopy(var->value, value, CONSOLE_VAR_VALUE_MAX);
}
void consoleVarInitListener(
consolevar_t *var,
const char_t *name,
const char_t *value,
consolevarchanged_t event
) {
consoleVarInit(var, name, value);
if(event) consoleVarListen(var, event);
}
void consoleVarSetValue(consolevar_t *var, const char_t *value) {
assertNotNull(var, "var must not be NULL");
assertNotNull(value, "value must not be NULL");
assertStrLenMax(value, CONSOLE_VAR_VALUE_MAX, "value is too long");
stringCopy(var->value, value, CONSOLE_VAR_VALUE_MAX);
uint8_t i = 0;
while (i < var->eventCount) {
var->events[i](var);
i++;
}
}
void consoleVarListen(consolevar_t *var, consolevarchanged_t event) {
assertNotNull(var, "var must not be NULL");
assertNotNull(event, "event must not be NULL");
assertTrue(
var->eventCount < CONSOLE_VAR_EVENTS_MAX,
"Event count is too high"
);
var->events[var->eventCount++] = event;
}

View File

@@ -1,65 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
#include "consoledefs.h"
typedef struct consolevar_s consolevar_t;
typedef void (*consolevarchanged_t)(const consolevar_t *var);
typedef struct consolevar_s {
char_t name[CONSOLE_VAR_NAME_MAX];
char_t value[CONSOLE_VAR_VALUE_MAX];
consolevarchanged_t events[CONSOLE_VAR_EVENTS_MAX];
uint8_t eventCount;
} consolevar_t;
/**
* Initializes a console variable.
*
* @param var Pointer to the console variable.
* @param name The name of the variable.
* @param value The initial value of the variable.
*/
void consoleVarInit(
consolevar_t *var,
const char_t *name,
const char_t *value
);
/**
* Initializes a console variable with a listener.
*
* @param var Pointer to the console variable.
* @param name The name of the variable.
* @param value The initial value of the variable.
* @param event The event to register.
*/
void consoleVarInitListener(
consolevar_t *var,
const char_t *name,
const char_t *value,
consolevarchanged_t event
);
/**
* Sets the value of a console variable.
*
* @param var Pointer to the console variable.
* @param value The new value of the variable.
*/
void consoleVarSetValue(consolevar_t *var, const char_t *value);
/**
* Registers an event to be called when the value of a console variable changes.
*
* @param var Pointer to the console variable.
* @param event The event to register.
*/
void consoleVarListen(consolevar_t *var, consolevarchanged_t event);

49
archive/dusk/defs.py Normal file
View File

@@ -0,0 +1,49 @@
from dotenv import load_dotenv, dotenv_values
import os
import sys
current_file_path = os.path.abspath(__file__)
duskDefsPath = os.path.join(os.path.dirname(current_file_path), "..", "..", "src", "duskdefs.env")
# Ensure the .env file exists
if not os.path.isfile(duskDefsPath):
print(f"Error: .env file not found at {duskDefsPath}")
sys.exit(1)
load_dotenv(dotenv_path=duskDefsPath)
defs = {key: os.getenv(key) for key in os.environ.keys()}
fileDefs = dotenv_values(dotenv_path=duskDefsPath)
# Parsed out definitions
CHUNK_WIDTH = int(defs.get('CHUNK_WIDTH'))
CHUNK_HEIGHT = int(defs.get('CHUNK_HEIGHT'))
CHUNK_DEPTH = int(defs.get('CHUNK_DEPTH'))
CHUNK_TILE_COUNT = CHUNK_WIDTH * CHUNK_HEIGHT * CHUNK_DEPTH
CHUNK_VERTEX_COUNT_MAX = int(defs.get('CHUNK_VERTEX_COUNT_MAX'))
TILE_WIDTH = float(defs.get('TILE_WIDTH'))
TILE_HEIGHT = float(defs.get('TILE_HEIGHT'))
TILE_DEPTH = float(defs.get('TILE_DEPTH'))
RPG_CAMERA_PIXELS_PER_UNIT = float(defs.get('RPG_CAMERA_PIXELS_PER_UNIT'))
RPG_CAMERA_Z_OFFSET = float(defs.get('RPG_CAMERA_Z_OFFSET'))
RPG_CAMERA_FOV = float(defs.get('RPG_CAMERA_FOV'))
MAP_WIDTH = 5
MAP_HEIGHT = 5
MAP_DEPTH = 3
MAP_CHUNK_COUNT = MAP_WIDTH * MAP_HEIGHT * MAP_DEPTH
TILE_SHAPES = {}
for key in defs.keys():
if key.startswith('TILE_SHAPE_'):
globals()[key] = int(defs.get(key))
TILE_SHAPES[key] = int(defs.get(key))
ENTITY_TYPES = {}
for key in defs.keys():
if key.startswith('ENTITY_TYPE_'):
globals()[key] = int(defs.get(key))
if key != 'ENTITY_TYPE_COUNT':
ENTITY_TYPES[key] = int(defs.get(key))

View File

@@ -1,31 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "error/error.h"
#ifndef RENDER_WIDTH
#define RENDER_WIDTH 320
#endif
#ifndef RENDER_HEIGHT
#define RENDER_HEIGHT 240
#endif
/**
* Initializes the rendering system.
*/
errorret_t renderInit(void);
/**
* Tells the rendering system to actually draw the frame.
*/
errorret_t renderDraw(void);
/**
* Disposes of the rendering system.
*/
errorret_t renderDispose(void);

View File

@@ -1,51 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "scene.h"
#include "world/overworld.h"
scene_t SCENE_CURRENT;
scenecallback_t SCENE_CALLBACKS[SCENE_COUNT] = {
[SCENE_INITIAL] = {
.init = NULL,
.update = NULL
},
[SCENE_OVERWORLD] = {
.init = overworldInit,
.update = overworldUpdate,
.dispose = NULL
}
};
void sceneInit(void) {
for(uint8_t i = 0; i < SCENE_COUNT; i++) {
if(SCENE_CALLBACKS[i].init) {
SCENE_CALLBACKS[i].init();
}
}
SCENE_CURRENT = SCENE_OVERWORLD;
}
void sceneSet(const scene_t scene) {
SCENE_CURRENT = scene;
}
void sceneUpdate(void) {
if(SCENE_CALLBACKS[SCENE_CURRENT].update) {
SCENE_CALLBACKS[SCENE_CURRENT].update();
}
}
void sceneDispose(void) {
for(uint8_t i = 0; i < SCENE_COUNT; i++) {
if(SCENE_CALLBACKS[i].dispose) {
SCENE_CALLBACKS[i].dispose();
}
}
}

View File

@@ -1,47 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
typedef enum {
SCENE_INITIAL,
SCENE_OVERWORLD,
SCENE_COUNT
} scene_t;
typedef struct {
void (*init)(void);
void (*update)(void);
void (*dispose)(void);
} scenecallback_t;
extern scene_t SCENE_CURRENT;
extern scenecallback_t SCENE_CALLBACKS[SCENE_COUNT];
/**
* Initializes the scene module.
*/
void sceneInit(void);
/**
* Sets the current scene.
*
* @param scene The scene to set.
*/
void sceneSet(const scene_t scene);
/**
* Updates the current scene.
*/
void sceneUpdate(void);
/**
* Disposes of the current scene.
*/
void sceneDispose(void);

View File

@@ -1,23 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <errno.h>
#include <ctype.h>
#include <stdarg.h>
#include <float.h>
typedef bool bool_t;
typedef int int_t;
typedef float float_t;
typedef char char_t;

90
archive/dusk/entity.py Normal file
View File

@@ -0,0 +1,90 @@
from tools.dusk.defs import ENTITY_TYPE_NULL, ENTITY_TYPE_NPC, CHUNK_WIDTH, CHUNK_HEIGHT, CHUNK_DEPTH, TILE_WIDTH, TILE_HEIGHT, TILE_DEPTH
from tools.editor.map.vertexbuffer import VertexBuffer
class Entity:
def __init__(self, chunk, localX=0, localY=0, localZ=0):
self.type = ENTITY_TYPE_NPC
self.name = "Unititled"
self.localX = localX % CHUNK_WIDTH
self.localY = localY % CHUNK_HEIGHT
self.localZ = localZ % CHUNK_DEPTH
self.chunk = chunk
self.vertexBuffer = VertexBuffer()
pass
def load(self, obj):
self.type = obj.get('type', ENTITY_TYPE_NULL)
self.localX = obj.get('x', 0)
self.localY = obj.get('y', 0)
self.localZ = obj.get('z', 0)
self.name = obj.get('name', "Untitled")
pass
def save(self, obj):
obj['type'] = self.type
obj['name'] = self.name
obj['x'] = self.localX
obj['y'] = self.localY
obj['z'] = self.localZ
pass
def setType(self, entityType):
if self.type == entityType:
return
self.type = entityType
self.chunk.dirty = True
self.chunk.map.onEntityData.invoke()
def setName(self, name):
if self.name == name:
return
self.name = name
self.chunk.dirty = True
self.chunk.map.onEntityData.invoke()
def draw(self):
self.vertexBuffer.clear()
startX = (self.chunk.x * CHUNK_WIDTH + self.localX) * TILE_WIDTH
startY = (self.chunk.y * CHUNK_HEIGHT + self.localY) * TILE_HEIGHT
startZ = (self.chunk.z * CHUNK_DEPTH + self.localZ) * TILE_DEPTH
w = TILE_WIDTH
h = TILE_HEIGHT
d = TILE_DEPTH
# Center
startX -= w / 2
startY -= h / 2
startZ -= d / 2
# Offset upwards a little
startZ += 1
# Buffer simple quad at current position (need 6 positions)
self.vertexBuffer.vertices = [
startX, startY, startZ,
startX + w, startY, startZ,
startX + w, startY + h, startZ,
startX, startY, startZ,
startX + w, startY + h, startZ,
startX, startY + h, startZ,
]
self.vertexBuffer.colors = [
1.0, 0.0, 1.0, 1.0,
1.0, 0.0, 1.0, 1.0,
1.0, 0.0, 1.0, 1.0,
1.0, 0.0, 1.0, 1.0,
1.0, 0.0, 1.0, 1.0,
1.0, 0.0, 1.0, 1.0,
]
self.vertexBuffer.uvs = [
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
0.0, 0.0,
1.0, 1.0,
0.0, 1.0,
]
self.vertexBuffer.buildData()
self.vertexBuffer.draw()

View File

@@ -1,53 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "direction.h"
#include "assert/assert.h"
float_t directionToAngle(const direction_t dir) {
switch(dir) {
case DIRECTION_NORTH: return (M_PI_2);
case DIRECTION_SOUTH: return -(M_PI_2);
case DIRECTION_EAST: return 0;
case DIRECTION_WEST: return (M_PI);
default: return 0; // Should never happen
}
}
void directionGetCoordinates(
const direction_t dir,
int8_t *x, int8_t *y
) {
assertNotNull(x, "X coordinate pointer cannot be NULL");
assertNotNull(y, "Y coordinate pointer cannot be NULL");
switch(dir) {
case DIRECTION_NORTH:
*x = 0;
*y = -1;
break;
case DIRECTION_SOUTH:
*x = 0;
*y = 1;
break;
case DIRECTION_EAST:
*x = 1;
*y = 0;
break;
case DIRECTION_WEST:
*x = -1;
*y = 0;
break;
default:
assertUnreachable("Invalid direction");
break;
}
}

View File

@@ -1,41 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
typedef enum {
DIRECTION_SOUTH = 0,
DIRECTION_EAST = 1,
DIRECTION_WEST = 2,
DIRECTION_NORTH = 3,
DIRECTION_UP = DIRECTION_NORTH,
DIRECTION_DOWN = DIRECTION_SOUTH,
DIRECTION_LEFT = DIRECTION_WEST,
DIRECTION_RIGHT = DIRECTION_EAST,
} direction_t;
/**
* Converts a direction to an angle in float_t format.
*
* @param dir The direction to convert.
* @return The angle corresponding to the direction.
*/
float_t directionToAngle(const direction_t dir);
/**
* Gets the relative coordinates for a given direction.
*
* @param dir The direction to get coordinates for.
* @param x Pointer to store the x coordinate.
* @param y Pointer to store the y coordinate.
*/
void directionGetCoordinates(
const direction_t dir,
int8_t *x, int8_t *y
);

View File

@@ -1,131 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "entity.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "world/world.h"
#include "world/tiledata.h"
#include "time.h"
entity_t ENTITIES[ENTITY_COUNT_MAX] = {0};
entitycallback_t ENTITY_CALLBACKS[ENTITY_TYPE_COUNT] = {
{NULL}, // ENTITY_TYPE_NULL
{
.load = playerEntityLoad,
.update = playerEntityUpdate,
},
{
.load = npcLoad,
.update = npcUpdate,
.interact = npcInteract,
},
};
void entityLoad(entity_t *entity, const entity_t *source) {
assertNotNull(entity, "Entity pointer cannot be NULL");
assertNotNull(source, "Source entity pointer cannot be NULL");
assertTrue(source->type != ENTITY_TYPE_NULL, "Source entity type NULL");
assertTrue(source->type < ENTITY_TYPE_COUNT, "Source entity type bad");
assertNotNull(
ENTITY_CALLBACKS[source->type].load,
"Entity type has no i nit callback"
);
memoryZero(entity, sizeof(entity_t));
entity->type = source->type;
entity->x = source->x;
entity->y = source->y;
entity->dir = source->dir;
entity->id = source->id;
ENTITY_CALLBACKS[entity->type].load(entity, source);
}
void entityUpdate(entity_t *entity) {
assertNotNull(entity, "Entity pointer cannot be NULL");
assertTrue(entity->type != ENTITY_TYPE_NULL, "Entity type NULL");
assertTrue(entity->type < ENTITY_TYPE_COUNT, "Entity type out of bounds");
assertNotNull(
ENTITY_CALLBACKS[entity->type].update,
"Entity type has no update callback"
);
ENTITY_CALLBACKS[entity->type].update(entity);
if(entity->subX > 0) {
entity->subX -= entity->moveSpeed;
} else if(entity->subX < 0) {
entity->subX += entity->moveSpeed;
}
if(entity->subY > 0) {
entity->subY -= entity->moveSpeed;
} else if(entity->subY < 0) {
entity->subY += entity->moveSpeed;
}
}
void entityMove(entity_t *entity, const uint8_t moveSpeed) {
assertNotNull(entity, "Entity pointer cannot be NULL");
assertTrue(entity->type != ENTITY_TYPE_NULL, "Entity type NULL");
assertTrue(entity->type < ENTITY_TYPE_COUNT, "Entity type out of bounds");
assertFalse(
entityIsMoving(entity),
"Entity is already moving, cannot move again"
);
int8_t x = 0, y = 0;
directionGetCoordinates(entity->dir, &x, &y);
// entity in way?
entity_t *ent = entityGetAt(entity->x + x, entity->y + y);
if(ent != NULL) return;
entity->x += x;
entity->y += y;
entity->subX = TILE_WIDTH_HEIGHT * -x;
entity->subY = TILE_WIDTH_HEIGHT * -y;
entity->moveSpeed = moveSpeed;
}
void entityTurn(entity_t *entity, const direction_t dir) {
assertNotNull(entity, "Entity pointer cannot be NULL");
assertTrue(entity->type != ENTITY_TYPE_NULL, "Entity type NULL");
assertTrue(entity->type < ENTITY_TYPE_COUNT, "Entity type out of bounds");
assertTrue(
dir >= DIRECTION_SOUTH && dir <= DIRECTION_NORTH, "Invalid direction"
);
assertFalse(
entityIsMoving(entity), "Entity is already moving, cannot turn"
);
entity->dir = dir;
}
bool_t entityIsMoving(const entity_t *entity) {
assertNotNull(entity, "Entity pointer cannot be NULL");
assertTrue(entity->type != ENTITY_TYPE_NULL, "Entity type NULL");
assertTrue(entity->type < ENTITY_TYPE_COUNT, "Entity type out of bounds");
return entity->subX != 0 || entity->subY != 0;
}
entity_t * entityGetAt(
const uint32_t tileX,
const uint32_t tileY
) {
entity_t *entity = ENTITIES;
do {
if(entity->type == ENTITY_TYPE_NULL) continue;
if(entity->x == tileX && entity->y == tileY) return entity;
} while((entity++) < &ENTITIES[ENTITY_COUNT_MAX - 1]);
return NULL;
}

View File

@@ -1,99 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "direction.h"
#include "player.h"
#include "npc.h"
#define ENTITY_COUNT_MAX 32
#define ENTITY_TURN_DURATION 0.075f // Duration for turning in seconds
#define ENTITY_MOVE_DURATION 0.1f // Duration for moving 1 tile, in seconds.
typedef enum {
ENTITY_TYPE_NULL = 0,
ENTITY_TYPE_PLAYER = 1,
ENTITY_TYPE_NPC = 2,
} entitytype_t;
#define ENTITY_TYPE_COUNT 3
typedef struct _entity_t {
uint32_t id;// Completely unique ID for this entity.
uint32_t x, y;
int8_t subX, subY;
uint8_t moveSpeed;
entitytype_t type;
direction_t dir;
union {
npc_t npc;
playerentity_t player;
};
} entity_t;
typedef struct {
void (*load) (entity_t *entity, const entity_t *source);
void (*update) (entity_t *entity);
void (*interact)(entity_t *player, entity_t *self);
} entitycallback_t;
extern entity_t ENTITIES[ENTITY_COUNT_MAX];
extern entitycallback_t ENTITY_CALLBACKS[ENTITY_TYPE_COUNT];
/**
* Loads an entity from the generated entity data.
*
* @param entity Pointer to the entity to initialize.
* @param source Pointer to the source entity data.
*/
void entityLoad(entity_t *entity, const entity_t *source);
/**
* Updates the entity's state.
*
* @param entity Pointer to the entity to update.
*/
void entityUpdate(entity_t *entity);
/**
* Moves the entity by the specified x and y offsets.
*
* @param entity Pointer to the entity to move.
* @param moveSpeed The speed at which to move the entity.
*/
void entityMove(entity_t *entity, const uint8_t moveSpeed);
/**
* Turns the entity to face the specified direction.
*
* @param entity Pointer to the entity to turn.
* @param dir The direction to turn the entity to.
*/
void entityTurn(entity_t *entity, const direction_t dir);
/**
* Returns whether or not an entity is currently moving.
*
* @param entity Pointer to the entity to check.
* @return True if the entity is moving, false otherwise.
*/
bool_t entityIsMoving(const entity_t *entity);
/**
* Gets the entity at the specified tile coordinates.
*
* @param tileX The x coordinate of the tile to get the entity from.
* @param tileY The y coordinate of the tile to get the entity from.
* @return Pointer to the entity at the specified coordinates, or NULL if no
* entity exists there.
*/
entity_t *entityGetAt(
const uint32_t tileX,
const uint32_t tileY
);

View File

@@ -1,45 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "entity.h"
#include "ui/uitextbox.h"
#include "locale/language.h"
#include "assert/assert.h"
void npcLoad(entity_t *entity, const entity_t *source) {
assertNotNull(entity, "Entity pointer cannot be NULL");
assertNotNull(source, "Source entity pointer cannot be NULL");
assertTrue(source->type == ENTITY_TYPE_NPC, "Source entity type must be NPC");
entity->npc = source->npc;
}
void npcUpdate(entity_t *entity) {
}
void npcInteract(entity_t *player, entity_t *self) {
assertTrue(self->type == ENTITY_TYPE_NPC, "Entity must be of type NPC");
switch(self->npc.interactType) {
case NPC_INTERACT_TYPE_NONE:
break;
case NPC_INTERACT_TYPE_TEXT:
uiTextboxSetText(languageGet(self->npc.text));
break;
case NPC_INTERACT_TYPE_CONVO:
break;
case NPC_INTERACT_TYPE_EVENT:
eventSetActive(self->npc.eventData);
break;
default:
assertUnreachable("Unknown NPC interaction type");
}
}

View File

@@ -1,44 +0,0 @@
#pragma once
#include "event/eventlist.h"
typedef struct _entity_t entity_t;
typedef enum {
NPC_INTERACT_TYPE_NONE = 0,
NPC_INTERACT_TYPE_TEXT = 1,
NPC_INTERACT_TYPE_CONVO = 2,
NPC_INTERACT_TYPE_EVENT = 3,
} npcinteracttype_t;
typedef struct {
npcinteracttype_t interactType;
union {
const char_t* text;
const eventdata_t *eventData;
};
} npc_t;
/**
* Initializes the NPC entity.
*
* @param entity The entity to initialize.
* @param source The source entity to copy data from.
*/
void npcLoad(entity_t *entity, const entity_t *source);
/**
* Updates the NPC entity.
*
* @param entity The entity to update.
*/
void npcUpdate(entity_t *entity);
/**
* Handles interaction between the player and the NPC.
*
* @param player The player entity interacting with the NPC.
* @param self The NPC entity being interacted with.
*/
void npcInteract(entity_t *player, entity_t *self);

View File

@@ -1,94 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "entity.h"
#include "assert/assert.h"
#include "input.h"
#include "display/render.h"
#include "world/world.h"
#include "ui/uitextbox.h"
inventory_t PLAYER_INVENTORY;
void playerInit() {
entity_t *ent = &ENTITIES[0];
entity_t playerEntityData = {
.id = PLAYER_ENTITY_ID,
.type = ENTITY_TYPE_PLAYER,
.x = WORLD_PLAYER_SPAWN_X,
.y = WORLD_PLAYER_SPAWN_Y,
};
entityLoad(ent, &playerEntityData);
inventoryInit(&PLAYER_INVENTORY, INVENTORY_SIZE_MAX);
}
void playerEntityLoad(entity_t *entity, const entity_t *source) {
assertNotNull(entity, "Entity pointer cannot be NULL");
assertNotNull(source, "Source entity pointer cannot be NULL");
assertTrue(entity->type == ENTITY_TYPE_PLAYER, "Entity type must be PLAYER");
assertTrue(source->type == entity->type, "Source/Entity type mismatch");
}
void playerEntityUpdate(entity_t *entity) {
assertNotNull(entity, "Entity pointer cannot be NULL");
assertTrue(entity->type == ENTITY_TYPE_PLAYER, "Entity type must be PLAYER");
// TODO: make this just a method somewhere.
if(UI_TEXTBOX.visible) return;
if(entityIsMoving(entity)) return;
const uint8_t moveSpeed = inputIsDown(INPUT_BIND_CANCEL) ? PLAYER_SPEED_RUN : PLAYER_SPEED_WALK;
if(inputIsDown(INPUT_BIND_UP)) {
if(entity->dir != DIRECTION_NORTH) {
entityTurn(entity, DIRECTION_NORTH);
return;
}
entityMove(entity, moveSpeed);
return;
} else if(inputIsDown(INPUT_BIND_DOWN)) {
if(entity->dir != DIRECTION_SOUTH) {
entityTurn(entity, DIRECTION_SOUTH);
return;
}
entityMove(entity, moveSpeed);
return;
} else if(inputIsDown(INPUT_BIND_LEFT)) {
if(entity->dir != DIRECTION_WEST) {
entityTurn(entity, DIRECTION_WEST);
return;
}
entityMove(entity, moveSpeed);
return;
} else if(inputIsDown(INPUT_BIND_RIGHT)) {
if(entity->dir != DIRECTION_EAST) {
entityTurn(entity, DIRECTION_EAST);
return;
}
entityMove(entity, moveSpeed);
return;
}
// Interact
if(inputPressed(INPUT_BIND_ACTION)) {
int8_t x, y;
directionGetCoordinates(entity->dir, &x, &y);
entity_t *ent = entityGetAt(entity->x + x, entity->y + y);
if(ent != NULL && ENTITY_CALLBACKS[ent->type].interact != NULL) {
assertTrue(ent->type < ENTITY_TYPE_COUNT, "Entity type out of bounds");
ENTITY_CALLBACKS[ent->type].interact(entity, ent);
}
}
}

View File

@@ -1,43 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
#include "item/inventory.h"
#define PLAYER_SPEED_WALK 1
#define PLAYER_SPEED_RUN 2
typedef struct _entity_t entity_t;
typedef struct {
uint32_t nothing;
} playerentity_t;
#define PLAYER_ENTITY_ID (UINT32_MAX-1)
extern inventory_t PLAYER_INVENTORY;
/**
* Initializes the player and all player-related entities.
*/
void playerInit(void);
/**
* Loads the player entity.
*
* @param entity The entity to initialize.
* @param source The source entity to copy data from.
*/
void playerEntityLoad(entity_t *entity, const entity_t *source);
/**
* Updates the player entity.
*
* @param entity The entity to update.
*/
void playerEntityUpdate(entity_t *entity);

View File

@@ -1,125 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "assert/assert.h"
#include "error.h"
#include "util/memory.h"
#include "util/string.h"
errorret_t errorThrowImpl(
errorstate_t *state,
errorcode_t code,
const char_t *file,
const char_t *function,
const int32_t line,
const char_t *message,
...
) {
assertNotNull(state, "Error state cannot be NULL");
assertTrue(code != ERROR_OK, "Error code must not be OK");
assertNotNull(file, "File cannot be NULL");
assertNotNull(function, "Function cannot be NULL");
assertTrue(line >= 0, "File pointer must be valid");
assertNotNull(message, "Message cannot be NULL");
memoryZero(state, sizeof(errorstate_t));
state->code = code;
// Format args.
va_list args;
va_start(args, message);
// Get length of formatted message
va_list argsCopy;
va_copy(argsCopy, args);
int32_t len = stringFormatVA(NULL, 0, message, argsCopy);
va_end(argsCopy);
// Create string to hold the formatted message
state->message = (char_t *)memoryAllocate(len + 1);
stringFormatVA(state->message, len + 1, message, args);
va_end(args);
// Format lines
len = stringFormat(NULL, 0, ERROR_LINE_FORMAT, file, line, function);
assertTrue(len >= 0, "Line formatting failed");
state->lines = (char_t *)memoryAllocate(len + 1);
stringFormat(state->lines, len + 1, ERROR_LINE_FORMAT, file, line, function);
return (errorret_t) {
.code = code,
.state = state
};
}
errorret_t errorOkImpl() {
return (errorret_t) {
.code = ERROR_OK,
.state = NULL
};
}
errorret_t errorChainImpl(
const errorret_t retval,
const char_t *file,
const char_t *function,
const int32_t line
) {
if (retval.code == ERROR_OK) return retval;
assertNotNull(retval.state, "Error state cannot be NULL");
assertNotNull(retval.state->message, "Message cannot be NULL");
// Create a new line string.
int32_t newLineLen = snprintf(NULL, 0, ERROR_LINE_FORMAT, file, line, function);
assertTrue(newLineLen >= 0, "Line formatting failed");
char_t *newLine = (char_t *)memoryAllocate(newLineLen + 1);
snprintf(newLine, newLineLen + 1, ERROR_LINE_FORMAT, file, line, function);
// Resize the existing lines to accommodate the new line
size_t existingLen = strlen(retval.state->lines);
memoryResize(
(void**)&retval.state->lines,
existingLen,
existingLen + newLineLen + 1
);
// Now append the new line to the existing lines
memoryCopy(
retval.state->lines + existingLen,
newLine,
newLineLen + 1
);
// Cleanup the temporary new line
memoryFree(newLine);
return retval;
}
void errorCatch(const errorret_t retval) {
if (retval.code == ERROR_OK) return;
assertNotNull(retval.state, "Error state cannot be NULL");
assertNotNull(retval.state->message, "Message cannot be NULL");
memoryFree((void*)retval.state->message);
}
errorret_t errorPrint(const errorret_t retval) {
if (retval.code == ERROR_OK) return retval;
assertNotNull(retval.state, "Error state cannot be NULL");
assertNotNull(retval.state->message, "Message cannot be NULL");
printf(
ERROR_PRINT_FORMAT,
retval.state->code,
retval.state->message,
retval.state->lines
);
return retval;
}

View File

@@ -1,137 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
typedef uint8_t errorcode_t;
typedef struct {
errorcode_t code;
char_t *message;
char_t *lines;
} errorstate_t;
typedef struct {
errorcode_t code;
errorstate_t *state;
} errorret_t;
static const errorcode_t ERROR_OK = 0;
static const errorcode_t ERROR_NOT_OK = 1;
static const char_t *ERROR_PRINT_FORMAT = "Error (%d): %s\n%s";
static const char_t *ERROR_LINE_FORMAT = " at %s:%d in function %s\n";
static errorstate_t ERROR_STATE = {
.code = ERROR_OK,
.message = NULL,
.lines = NULL
};
/**
* Sets the error state with the provided code and message.
*
* @param state The error state to initialize.
* @param code The error code to set.
* @param file The file where the error occurred.
* @param function The function where the error occurred.
* @param line The line number where the error occurred.
* @param message The error message.
* @param args The arguments for the error message.
* @return The error code.
*/
errorret_t errorThrowImpl(
errorstate_t *state,
errorcode_t code,
const char_t *file,
const char_t *function,
const int32_t line,
const char_t *message,
...
);
/**
* Returns an error state with no error.
*
* @return An error state with code ERROR_OK.
*/
errorret_t errorOkImpl();
/**
* Chains an error state, allowing for error propagation.
*
* @param retval The return value containing the error state.
* @param file The file where the error occurred.
* @param function The function where the error occurred.
* @param line The line number where the error occurred.
* @return The error code if an error occurred, otherwise continues execution.
*/
errorret_t errorChainImpl(
const errorret_t retval,
const char_t *file,
const char_t *function,
const int32_t line
);
/**
* Catches an error and handles it.
*
* @param retval The return value containing the error state.
*/
void errorCatch(const errorret_t retval);
/**
* Prints the error state to the console.
*
* @param retval The return value containing the error state.
* @return Passed retval for chaining.
*/
errorret_t errorPrint(const errorret_t retval);
/**
* Throws an error with a formatted message.
*
* @param code The error code to throw.
* @param message The format string for the error message.
* @param ... Additional arguments for the format string.
* @return The error code.
*/
#define errorThrowWithCode(code, message, ... ) \
return errorThrowImpl(\
&ERROR_STATE, (code), __FILE__, __func__, __LINE__, (message), \
__VA_ARGS__ \
)
/**
* Throws an error with a default error code of ERROR_NOT_OK.
*
* @param message The format string for the error message.
* @param ... Additional arguments for the format string.
* @return The error code.
*/
#define errorThrow(message, ...) \
return errorThrowImpl(\
&ERROR_STATE, ERROR_NOT_OK, __FILE__, __func__, __LINE__, (message), \
__VA_ARGS__ \
)
/**
* Checks if a child method errored, and if it did, then send it up the chain.
* @param retval The return value containing the error state.
* @return The error code if an error occurred, otherwise continues execution.
*/
#define errorChain(retval) \
if ((retval).code != ERROR_OK) { \
return errorChainImpl(retval, __FILE__, __func__, __LINE__); \
}
/**
* Returns without an error.
*/
#define errorOk() \
return errorOkImpl()
// EOF

18
archive/dusk/event.py Normal file
View File

@@ -0,0 +1,18 @@
class Event:
def __init__(self):
self._subscribers = []
def sub(self, callback):
"""Subscribe a callback to the event."""
if callback not in self._subscribers:
self._subscribers.append(callback)
def unsub(self, callback):
"""Unsubscribe a callback from the event."""
if callback in self._subscribers:
self._subscribers.remove(callback)
def invoke(self, *args, **kwargs):
"""Invoke all subscribers with the given arguments."""
for callback in self._subscribers:
callback(*args, **kwargs)

View File

@@ -1,73 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "event.h"
#include "util/memory.h"
#include "assert/assert.h"
eventcallback_t EVENT_CALLBACKS[] = {
[EVENT_TYPE_NULL] = { NULL, NULL },
[EVENT_TYPE_TEXT] = { eventTextStart, eventTextUpdate },
};
event_t EVENT;
void eventInit() {
memoryZero(&EVENT, sizeof(event_t));
}
void eventUpdate() {
if(EVENT.active == NULL) {
return; // No active event to update
}
const eventitem_t *item = &EVENT.active->items[EVENT.item];
assertNotNull(
EVENT_CALLBACKS[item->type].update,
"Event type does not have an update callback"
);
EVENT_CALLBACKS[item->type].update(item);
}
void eventSetActive(const eventdata_t *event) {
assertNotNull(event, "Event data cannot be NULL");
assertTrue(
event->itemCount <= EVENT_ITEM_COUNT_MAX,
"Event count too high"
);
assertTrue(event->itemCount > 0, "Event must have at least one item");
EVENT.active = event;
EVENT.item = 0;
const eventitem_t *firstItem = &EVENT.active->items[EVENT.item];
assertNotNull(
EVENT_CALLBACKS[firstItem->type].start,
"Event type does not have a start callback"
);
EVENT_CALLBACKS[firstItem->type].start(firstItem);
}
void eventNext() {
assertNotNull(EVENT.active, "No active event to proceed with");
assertTrue(EVENT.item < EVENT.active->itemCount, "No more items in the event");
EVENT.item++;
if (EVENT.item >= EVENT.active->itemCount) {
EVENT.active = NULL;
return;
}
const eventitem_t *nextItem = &EVENT.active->items[EVENT.item];
assertNotNull(
EVENT_CALLBACKS[nextItem->type].start,
"Event type does not have a start callback"
);
EVENT_CALLBACKS[nextItem->type].start(nextItem);
}

View File

@@ -1,46 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "eventdata.h"
typedef struct {
eventdata_t data;
const eventdata_t *active;
uint8_t item;
} event_t;
typedef struct {
void (*start)(const eventitem_t *item);
void (*update)(const eventitem_t *item);
} eventcallback_t;
extern eventcallback_t EVENT_CALLBACKS[EVENT_TYPE_COUNT];
extern event_t EVENT;
/**
* Initializes the event system.
*/
void eventInit();
/**
* Updates the active event.
*/
void eventUpdate();
/**
* Sets the active event.
*
* @param event The event to set as active.
*/
void eventSetActive(const eventdata_t *eventData);
/**
* Goes to the next item in the active event. Only meant to be called by
* event items.
*/
void eventNext();

View File

@@ -1,26 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma ocne
#include "eventtext.h"
typedef enum {
EVENT_TYPE_NULL = 0,
EVENT_TYPE_TEXT,
} eventtype_t;
#define EVENT_TYPE_COUNT 2
typedef struct _eventitem_t {
eventtype_t type;
union {
eventtext_t text;
};
} eventitem_t;
#define EVENT_ITEM_COUNT_MAX 32

View File

@@ -1,26 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "event.h"
#include "ui/uitextbox.h"
#include "assert/assert.h"
void eventTextStart(const eventitem_t *item) {
assertNotNull(item, "Event item cannot be NULL");
assertTrue(item->type == EVENT_TYPE_TEXT, "Event item must be of type TEXT");
assertNotNull(item->text, "Event item must have at least one text");
uiTextboxSetText(item->text);
}
void eventTextUpdate(const eventitem_t *item) {
assertNotNull(item, "Event item cannot be NULL");
assertTrue(item->type == EVENT_TYPE_TEXT, "Event item must be of type TEXT");
if(!UI_TEXTBOX.visible) {
eventNext();
}
}

View File

@@ -1,36 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
typedef struct _eventitem_t eventitem_t;
#define EVENT_TEXT_STRING_COUNT_MAX 8
typedef const char_t* eventtext_t;
/**
* Starts the text event for the given item.
*
* @param item The event item to start.
*/
void eventTextStart(const eventitem_t *item);
/**
* Updates the text event for the given item.
*
* @param item The event item to update.
*/
void eventTextUpdate(const eventitem_t *item);
/**
* Query whether the text event is done or not.
*
* @param item The event item to check.
*/
bool_t eventTextIsDone(const eventitem_t *item);

View File

@@ -1,57 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "game.h"
#include "util/memory.h"
#include "world/chunk.h"
#include "display/scene.h"
#include "world/overworld.h"
#include "input.h"
#include "event/event.h"
#include "ui/uitextbox.h"
#include "console/console.h"
#include "util/memory.h"
#include "time.h"
game_t GAME;
void gameInit(void) {
memoryZero(&GAME, sizeof(game_t));
GAME.running = true;
timeInit();
consoleInit();
inputInit();
eventInit();
uiTextboxInit();
sceneInit();
}
void gameUpdate(void) {
timeUpdate();
// Game logic is tied to 60FPS for now, saves me a lot of hassle with float
// issues
float_t timeSinceLastTick = TIME.time - TIME.lastTick;
while(timeSinceLastTick >= TIME_STEP) {
sceneUpdate();
uiTextboxUpdate();
eventUpdate();
inputUpdate();
timeSinceLastTick -= TIME_STEP;
TIME.lastTick = TIME.time;
}
if(inputPressed(INPUT_BIND_QUIT)) consoleExec("quit");
consoleUpdate();
}
void gameDispose(void) {
sceneDispose();
}

View File

@@ -1,49 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
typedef struct {
bool_t running;
} game_t;
extern game_t GAME;
/**
* Initializes the game, this should be called before any other game functions.
* This should be called by the parent platform at a time that it deems
* appropriate. Any game systems cannot be used until this function has
* been called.
*
* By the point this is called, we expect;
* - Rendering has initialized and is ready to draw.
* - Input has been initialized and is ready to be read.
* - If your system handles time dynamically, it should be ready to be used.
*
* The systems called (in order) are;
* - Console.
* - Input system (Not the platforms input, but the game's input system).
* - Time system (if applicable).
* - Event System
* - UI Systems.
* - Gameplay systems.
*/
void gameInit(void);
/**
* Asks the game to update, this will not do any drawing and should be called
* in the main loop of the system, ideally either after or before the rendering
* has occured.
*/
void gameUpdate(void);
/**
* Cleans up resources used by the game, rendering really should still be
* available at this point because we want to cleanup nicely.
*/
void gameDispose(void);

View File

@@ -1,39 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "input.h"
#include "assert/assert.h"
#include "util/memory.h"
input_t INPUT;
void inputInit(void) {
memoryZero(&INPUT, sizeof(input_t));
}
void inputUpdate(void) {
INPUT.previous = INPUT.current;
INPUT.current = inputStateGet();
}
bool_t inputIsDown(const inputbind_t bind) {
assertTrue(bind < INPUT_BIND_COUNT, "Input bind out of bounds");
return (INPUT.current & bind) != 0;
}
bool_t inputWasDown(const inputbind_t bind) {
assertTrue(bind < INPUT_BIND_COUNT, "Input bind out of bounds");
return (INPUT.previous & bind) != 0;
}
bool_t inputPressed(const inputbind_t bind) {
return inputIsDown(bind) && !inputWasDown(bind);
}
bool_t inputReleased(const inputbind_t bind) {
return !inputIsDown(bind) && inputWasDown(bind);
}

View File

@@ -1,80 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
typedef uint8_t inputbind_t;
typedef inputbind_t inputstate_t;
#define INPUT_BIND_UP (1 << 0)
#define INPUT_BIND_DOWN (1 << 1)
#define INPUT_BIND_LEFT (1 << 2)
#define INPUT_BIND_RIGHT (1 << 3)
#define INPUT_BIND_ACTION (1 << 4)
#define INPUT_BIND_CANCEL (1 << 5)
#define INPUT_BIND_CONSOLE (1 << 6)
#define INPUT_BIND_QUIT (1 << 7)
#define INPUT_BIND_COUNT (INPUT_BIND_QUIT + 1)
typedef struct {
uint8_t current;
uint8_t previous;
} input_t;
extern input_t INPUT;
/**
* Initialize the input system.
*/
void inputInit(void);
/**
* Updates the input state.
*/
void inputUpdate(void);
/**
* Gets the current input state as a bitmask.
*
* @return The current input state as a bitmask.
*/
inputstate_t inputStateGet(void);
/**
* Checks if a specific input bind is currently pressed.
*
* @param bind The input bind to check.
* @return true if the bind is currently pressed, false otherwise.
*/
bool_t inputIsDown(const inputbind_t bind);
/**
* Checks if a specific input bind was pressed in the last update.
*
* @param bind The input bind to check.
* @return true if the bind was pressed in the last update, false otherwise.
*/
bool_t inputWasDown(const inputbind_t bind);
/**
* Checks if a specific input bind was down this frame but not in the the
* previous frame.
*
* @param bind The input bind to check.
* @return true if the bind is currently pressed, false otherwise.
*/
bool_t inputPressed(const inputbind_t bind);
/**
* Checks if a specific input bind was released this frame.
*
* @param bind The input bind to check.
* @return true if the bind was released this frame, false otherwise.
*/
bool_t inputReleased(const inputbind_t bind);

View File

@@ -1,68 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "inventory.h"
#include "util/memory.h"
#include "assert/assert.h"
void inventoryInit(inventory_t *inventory, const uint8_t size) {
assertNotNull(inventory, "inventory must not be NULL");
assertTrue(size <= INVENTORY_SIZE_MAX, "size exceeding INVENTORY_SIZE_MAX");
assertTrue(size > 0, "size must be greater than 0");
memoryZero(inventory, sizeof(inventory_t));
inventory->size = size;
}
uint8_t inventoryItemIndexByType(
const inventory_t *inventory,
const itemtype_t type
) {
assertNotNull(inventory, "inventory must not be NULL");
assertTrue(type > ITEM_TYPE_NULL, "type must be greater than ITEM_TYPE_NULL");
uint8_t item = inventory->itemCount;
while(item--) {
if(inventory->items[item].type == type) return item;
}
return INVENTORY_SIZE_MAX;
}
uint8_t inventoryItemCount(
const inventory_t *inventory,
const itemtype_t type
) {
assertNotNull(inventory, "inventory must not be NULL");
assertTrue(type > ITEM_TYPE_NULL, "type must be greater than ITEM_TYPE_NULL");
const uint8_t index = inventoryItemIndexByType(inventory, type);
if(index == INVENTORY_SIZE_MAX) return 0;
return inventory->items[index].count;
}
void inventoryItemSet(
inventory_t *inventory,
const itemtype_t type,
const uint8_t count
) {
assertNotNull(inventory, "inventory must not be NULL");
assertTrue(type > ITEM_TYPE_NULL, "type must be greater than ITEM_TYPE_NULL");
assertTrue(count > 0, "count must be greater than 0");
const uint8_t index = inventoryItemIndexByType(inventory, type);
if(index == INVENTORY_SIZE_MAX) {
// Item does not exist, add it
assertTrue(inventory->itemCount < inventory->size, "inventory is full");
inventory->items[inventory->itemCount].type = type;
inventory->items[inventory->itemCount].count = count;
inventory->itemCount++;
} else {
// Item exists, update the count
inventory->items[index].count = count;
}
}

View File

@@ -1,63 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "itemstack.h"
#define INVENTORY_SIZE_MAX UINT8_MAX
typedef struct {
itemstack_t items[INVENTORY_SIZE_MAX];
uint8_t itemCount;
uint8_t size;
} inventory_t;
/**
* Initializes an inventory with a specified size.
*
* @param inventory Pointer to the inventory to initialize.
* @param size The size of the inventory (maximum is INVENTORY_SIZE_MAX).
*/
void inventoryInit(inventory_t *inventory, const uint8_t size);
/**
* Finds the index of the item of a specified type in the inventory.
*
* @param inventory Pointer to the inventory to search.
* @param type The type of item to find.
* @return The index of the item, or INVENTORY_SIZE_MAX if not found.
*/
uint8_t inventoryItemIndexByType(
const inventory_t *inventory,
const itemtype_t type
);
/**
* Gets the count of items of a specified type in the inventory.
*
* @param inventory Pointer to the inventory to check.
* @param type The type of item to count.
* @return The count of items of the specified type.
*/
uint8_t inventoryItemCount(
const inventory_t *inventory,
const itemtype_t type
);
/**
* Sets the count of items of a specified type in the inventory.
* If the item does not exist, it will be added.
*
* @param inventory Pointer to the inventory to modify.
* @param type The type of item to set.
* @param count The count of items to set.
*/
void inventoryItemSet(
inventory_t *inventory,
const itemtype_t type,
const uint8_t count
);

View File

@@ -1,24 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
typedef enum {
ITEM_TYPE_NULL = 0,
// MEDICINE
ITEM_TYPE_POTION,
// INGREDIENTS
ITEM_TYPE_ONION,
ITEM_TYPE_SWEET_POTATO,
ITEM_TYPE_CARROT,
// COOKED FOOD
ITEM_TYPE_BAKED_SWEET_POTATO,
} itemtype_t;

View File

@@ -1,145 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "language.h"
#include "assert/assert.h"
language_t LANGUAGE;
void languageInit(void) {
LANGUAGE.current = LANGUAGE_EN;
}
const char_t * languageGet(const char_t *key) {
assertNotNull(key, "Key cannot be NULL");
assertTrue(LANGUAGE.current < LANGUAGE_COUNT, "Invalid language index");
int16_t keyIndex = -1;
for(uint16_t i = 0; i < LANGUAGE_COUNTS[LANGUAGE.current]; i++) {
if(strcmp(LANGUAGE_KEYS[LANGUAGE.current][i], key) != 0) continue;
keyIndex = i;
break;
}
assertTrue(keyIndex != -1, "Key not found in language");
return LANGUAGE_VALUES[LANGUAGE.current][keyIndex];
}
uint16_t langaugeGetLength(const char_t *key) {
assertNotNull(key, "Key cannot be NULL");
assertTrue(LANGUAGE.current < LANGUAGE_COUNT, "Invalid language index");
int16_t keyIndex = -1;
for(uint16_t i = 0; i < LANGUAGE_COUNTS[LANGUAGE.current]; i++) {
if(strcmp(LANGUAGE_KEYS[LANGUAGE.current][i], key) != 0) continue;
keyIndex = i;
break;
}
assertTrue(keyIndex != -1, "Key not found in language");
return strlen(LANGUAGE_VALUES[LANGUAGE.current][keyIndex]);
}
uint16_t languageFormat(
const char_t *key,
char_t *buffer,
uint16_t buffSize,
const char_t **keys,
const char_t **values,
const uint16_t valueCount
) {
if(buffer != NULL) {
assertTrue(buffSize > 0, "Buffer size must be greater than 0");
} else {
assertTrue(buffSize == 0, "Buffer size must be 0 if buffer is NULL");
}
assertNotNull(key, "Key cannot be NULL");
assertNotNull(keys, "Keys cannot be NULL");
assertNotNull(values, "Values cannot be NULL");
const char_t *val = languageGet(key);
assertNotNull(val, "Value for key cannot be NULL");
char_t c;
uint16_t i = 0;
uint16_t j = 0;
uint8_t k = 0;
bool_t inBraces = false;
char_t braceBuffer[64] = {0};
#define bufferChar(c) ( \
(buffer ? (buffer[j++] = c) : (j++)), \
assertTrue(buffer ? j < buffSize : true, "Buffer overflow") \
)
while((c = val[i++]) != '\0') {
if(c == '{' && val[i] == '{') {
goto startBraces;
} else if(c == '}' && val[i] == '}') {
goto endBraces;
} else if(inBraces) {
goto braceBuffering;
} else {
goto character;
}
character: {
bufferChar(c);
continue;
}
braceBuffering: {
assertFalse(val[i] == '\0', "Unexpected end of string.");
braceBuffer[k++] = c;
assertTrue(k < sizeof(braceBuffer), "Brace buffer overflow");
if(val[i] == ' ') i++;
continue;
}
startBraces: {
assertFalse(inBraces, "Nested braces are not allowed");
inBraces = true;
i++;
k = 0;
assertFalse(val[i] == '\0', "Unexpected end of string.");
if(val[i] == ' ') i++;
continue;
}
endBraces: {
assertTrue(inBraces, "Unmatched closing brace found");
inBraces = false;
i++;
braceBuffer[k] = '\0';
uint16_t l;
for(l = 0; l < valueCount; l++) {
if(strcmp(braceBuffer, keys[l]) != 0) {
continue;
}
const char_t *replacement = values[l];
uint16_t r = 0;
while((c = replacement[r++]) != '\0') {
bufferChar(c);
}
break;
}
assertTrue(l < valueCount, "No string replacement found!");
continue;
}
}
if(buffer){
assertTrue(j < buffSize, "Buffer overflow");
buffer[j] = '\0';
}
assertFalse(inBraces, "Unmatched opening brace found");
return j;
}

View File

@@ -1,65 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "locale/language/languages.h"
typedef struct {
uint8_t current;
} language_t;
extern language_t LANGUAGE;
/**
* Initializes the language system.
*
* This function sets up the language system, loading the default language
* and preparing any necessary resources for language handling.
*/
void languageInit(void);
/**
* Gets a language string by its key.
*
* @param key The key for the language string.
* @return The language string associated with the key.
*/
const char_t * languageGet(const char_t *key);
/**
* Gets the length of a language string by its key.
*
* @param key The key for the language string.
* @return The length of the language string associated with the key.
*/
uint16_t langaugeGetLength(const char_t *key);
/**
* Formats a language string with given keys and values.
*
* This function replaces placeholders in the language string with the provided
* values based on the keys.
*
* If buffer is NULL, the function will instead calculate the length of the
* formatted string.
*
* @param key The key for the language string to format.
* @param buffer The buffer to store the formatted string.
* @param buffSize The size of the buffer.
* @param keys An array of keys to replace in the language string.
* @param values An array of values corresponding to the keys.
* @param valueCount The number of key-value pairs.
* @return The number of characters written to the buffer.
*/
uint16_t languageFormat(
const char_t *key,
char_t *buffer,
uint16_t buffSize,
const char_t **keys,
const char_t **values,
const uint16_t valueCount
);

259
archive/dusk/map.py Normal file
View File

@@ -0,0 +1,259 @@
import json
import sys
from tools.dusk.event import Event
from PyQt5.QtWidgets import QFileDialog, QMessageBox
from PyQt5.QtCore import QTimer
import os
from tools.dusk.chunk import Chunk
from tools.dusk.defs import MAP_WIDTH, MAP_HEIGHT, MAP_DEPTH, CHUNK_WIDTH, CHUNK_HEIGHT, CHUNK_DEPTH
import traceback
MAP_DEFAULT_PATH = os.path.join(os.path.dirname(__file__), '../../assets/map/')
EDITOR_CONFIG_PATH = os.path.join(os.path.dirname(__file__), '.editor')
class Map:
def __init__(self, parent):
self.parent = parent
self.data = {}
self.dataOriginal = {}
self.position = [None, None, None] # x, y, z
self.topLeftX = None
self.topLeftY = None
self.topLeftZ = None
self.chunks = {}
self.onMapData = Event()
self.onPositionChange = Event()
self.onEntityData = Event()
self.mapFileName = None
self.lastFile = None
self.firstLoad = True
index = 0
for x in range(MAP_WIDTH):
for y in range(MAP_HEIGHT):
for z in range(MAP_DEPTH):
self.chunks[index] = Chunk(self, x, y, z)
index += 1
# Only in editor instances:
self.moveTo(0, 0, 0)
if parent is not None:
QTimer.singleShot(16, self.loadLastFile)
def loadLastFile(self):
if not os.path.exists(EDITOR_CONFIG_PATH):
return
try:
with open(EDITOR_CONFIG_PATH, 'r') as f:
config = json.load(f)
lastFile = config.get('lastFile')
lastPosition = config.get('lastPosition')
leftPanelIndex = config.get('leftPanelIndex')
if lastFile and os.path.exists(lastFile):
self.load(lastFile)
if lastPosition and isinstance(lastPosition, list) and len(lastPosition) == 3:
self.moveTo(*lastPosition)
if leftPanelIndex is not None:
self.parent.leftPanel.tabs.setCurrentIndex(leftPanelIndex)
except Exception:
traceback.print_exc()
def updateEditorConfig(self):
if self.parent is None:
return
try:
mapFileName = self.getMapFilename()
config = {
'lastFile': mapFileName if mapFileName else "",
'lastPosition': self.position,
'leftPanelIndex': self.parent.leftPanel.tabs.currentIndex()
}
config_dir = os.path.dirname(EDITOR_CONFIG_PATH)
if not os.path.exists(config_dir):
os.makedirs(config_dir, exist_ok=True)
with open(EDITOR_CONFIG_PATH, 'w') as f:
json.dump(config, f, indent=2)
except Exception:
traceback.print_exc()
def newFile(self):
self.data = {}
self.dataOriginal = {}
self.mapFileName = None
self.lastFile = None
for chunk in self.chunks.values():
chunk.new()
self.moveTo(0, 0, 0)
self.onMapData.invoke(self.data)
self.updateEditorConfig()
def save(self, fname=None):
if not self.getMapFilename() and fname is None:
filePath, _ = QFileDialog.getSaveFileName(None, "Save Map File", MAP_DEFAULT_PATH, "Map Files (*.json)")
if not filePath:
return
self.mapFileName = filePath
if fname:
self.mapFileName = fname
try:
with open(self.getMapFilename(), 'w') as f:
json.dump(self.data, f, indent=2)
self.dataOriginal = json.loads(json.dumps(self.data)) # Deep copy
for chunk in self.chunks.values():
chunk.save()
self.updateEditorConfig()
except Exception as e:
traceback.print_exc()
QMessageBox.critical(None, "Save Error", f"Failed to save map file:\n{e}")
def load(self, fileName):
try:
with open(fileName, 'r') as f:
self.data = json.load(f)
self.mapFileName = fileName
self.dataOriginal = json.loads(json.dumps(self.data)) # Deep copy
for chunk in self.chunks.values():
chunk.load()
self.onMapData.invoke(self.data)
self.updateEditorConfig()
except Exception as e:
traceback.print_exc()
QMessageBox.critical(None, "Load Error", f"Failed to load map file:\n{e}")
def isMapFileDirty(self):
return json.dumps(self.data, sort_keys=True) != json.dumps(self.dataOriginal, sort_keys=True)
def isDirty(self):
return self.isMapFileDirty() or self.anyChunksDirty()
def getMapFilename(self):
return self.mapFileName if self.mapFileName and os.path.exists(self.mapFileName) else None
def getMapDirectory(self):
if self.mapFileName is None:
return None
dirname = os.path.dirname(self.mapFileName)
return dirname
def getChunkDirectory(self):
dirName = self.getMapDirectory()
if dirName is None:
return None
return os.path.join(dirName, 'chunks')
def anyChunksDirty(self):
for chunk in self.chunks.values():
if chunk.isDirty():
return True
return False
def moveTo(self, x, y, z):
if self.position == [x, y, z]:
return
# We need to decide if the chunks should be unloaded here or not.
newTopLeftChunkX = x // CHUNK_WIDTH - (MAP_WIDTH // 2)
newTopLeftChunkY = y // CHUNK_HEIGHT - (MAP_HEIGHT // 2)
newTopLeftChunkZ = z // CHUNK_DEPTH - (MAP_DEPTH // 2)
if(newTopLeftChunkX != self.topLeftX or
newTopLeftChunkY != self.topLeftY or
newTopLeftChunkZ != self.topLeftZ):
chunksToUnload = []
chunksToKeep = []
for chunk in self.chunks.values():
chunkWorldX = chunk.x
chunkWorldY = chunk.y
chunkWorldZ = chunk.z
if(chunkWorldX < newTopLeftChunkX or
chunkWorldX >= newTopLeftChunkX + MAP_WIDTH or
chunkWorldY < newTopLeftChunkY or
chunkWorldY >= newTopLeftChunkY + MAP_HEIGHT or
chunkWorldZ < newTopLeftChunkZ or
chunkWorldZ >= newTopLeftChunkZ + MAP_DEPTH):
chunksToUnload.append(chunk)
else:
chunksToKeep.append(chunk)
# Unload chunks that are out of the new bounds.
for chunk in chunksToUnload:
if chunk.isDirty():
print(f"Can't move map, some chunks are dirty: ({chunk.x}, {chunk.y}, {chunk.z})")
return
# Now we can safely unload the chunks.
chunkIndex = 0
newChunks = {}
for chunk in chunksToKeep:
newChunks[chunkIndex] = chunk
chunkIndex += 1
for xPos in range(newTopLeftChunkX, newTopLeftChunkX + MAP_WIDTH):
for yPos in range(newTopLeftChunkY, newTopLeftChunkY + MAP_HEIGHT):
for zPos in range(newTopLeftChunkZ, newTopLeftChunkZ + MAP_DEPTH):
# Check if we already have this chunk.
found = False
for chunk in chunksToKeep:
if chunk.x == xPos and chunk.y == yPos and chunk.z == zPos:
found = True
break
if not found:
# Create a new chunk.
newChunk = chunksToUnload.pop()
newChunk.reload(xPos, yPos, zPos)
newChunks[chunkIndex] = newChunk
chunkIndex += 1
self.chunks = newChunks
self.topLeftX = newTopLeftChunkX
self.topLeftY = newTopLeftChunkY
self.topLeftZ = newTopLeftChunkZ
self.position = [x, y, z]
self.onPositionChange.invoke(self.position)
if not self.firstLoad:
self.updateEditorConfig()
self.firstLoad = False
def moveRelative(self, x, y, z):
self.moveTo(
self.position[0] + x,
self.position[1] + y,
self.position[2] + z
)
def draw(self):
for chunk in self.chunks.values():
chunk.draw()
for chunk in self.chunks.values():
for entity in chunk.entities.values():
entity.draw()
# Only render on Region tab
if self.parent.leftPanel.tabs.currentWidget() == self.parent.leftPanel.regionPanel:
for chunk in self.chunks.values():
for region in chunk.regions.values():
region.draw()
def getChunkAtWorldPos(self, x, y, z):
chunkX = x // CHUNK_WIDTH
chunkY = y // CHUNK_HEIGHT
chunkZ = z // CHUNK_DEPTH
for chunk in self.chunks.values():
if chunk.x == chunkX and chunk.y == chunkY and chunk.z == chunkZ:
return chunk
return None
def getTileAtWorldPos(self, x, y, z):
chunk = self.getChunkAtWorldPos(x, y, z)
if not chunk:
print("No chunk found at position:", (x, y, z))
return None
tileX = x % CHUNK_WIDTH
tileY = y % CHUNK_HEIGHT
tileZ = z % CHUNK_DEPTH
tileIndex = tileX + tileY * CHUNK_WIDTH + tileZ * CHUNK_WIDTH * CHUNK_HEIGHT
return chunk.tiles.get(tileIndex)

141
archive/dusk/region.py Normal file
View File

@@ -0,0 +1,141 @@
from tools.dusk.defs import CHUNK_WIDTH, CHUNK_HEIGHT, CHUNK_DEPTH, TILE_WIDTH, TILE_HEIGHT, TILE_DEPTH
from tools.editor.map.vertexbuffer import VertexBuffer
from OpenGL.GL import *
from OpenGL.GLU import *
class Region:
def __init__(self, chunk):
self.minX = 0
self.minY = 0
self.minZ = 0
self.maxX = 0
self.maxY = 0
self.maxZ = 0
self.chunk = chunk
self.vertexBuffer = VertexBuffer()
self.color = (1.0, 0.0, 0.0)
self.updateVertexs()
pass
def updateVertexs(self):
# Draw a quad, semi transparent with solid outlines
vminX = (self.minX * CHUNK_WIDTH) * TILE_WIDTH
vminY = (self.minY * CHUNK_HEIGHT) * TILE_HEIGHT
vminZ = (self.minZ * CHUNK_DEPTH) * TILE_DEPTH
vmaxX = (self.maxX * CHUNK_WIDTH) * TILE_WIDTH
vmaxY = (self.maxY * CHUNK_HEIGHT) * TILE_HEIGHT
vmaxZ = (self.maxZ * CHUNK_DEPTH) * TILE_DEPTH
alpha = 0.25
# Move back half a tile width
vminX -= TILE_WIDTH / 2
vmaxX -= TILE_WIDTH / 2
vminY -= TILE_HEIGHT / 2
vmaxY -= TILE_HEIGHT / 2
vminZ -= TILE_DEPTH / 2
vmaxZ -= TILE_DEPTH / 2
# Cube (6 verts per face)
self.vertexBuffer.vertices = [
# Front face
vminX, vminY, vmaxZ,
vmaxX, vminY, vmaxZ,
vmaxX, vmaxY, vmaxZ,
vminX, vminY, vmaxZ,
vmaxX, vmaxY, vmaxZ,
vminX, vmaxY, vmaxZ,
# Back face
vmaxX, vminY, vminZ,
vminX, vminY, vminZ,
vminX, vmaxY, vminZ,
vmaxX, vminY, vminZ,
vminX, vmaxY, vminZ,
vmaxX, vmaxY, vminZ,
# Left face
vminX, vminY, vminZ,
vminX, vminY, vmaxZ,
vminX, vmaxY, vmaxZ,
vminX, vminY, vminZ,
vminX, vmaxY, vmaxZ,
vminX, vmaxY, vminZ,
# Right face
vmaxX, vminY, vmaxZ,
vmaxX, vminY, vminZ,
vmaxX, vmaxY, vminZ,
vmaxX, vminY, vmaxZ,
vmaxX, vmaxY, vminZ,
vmaxX, vmaxY, vmaxZ,
# Top face
vminX, vmaxY, vmaxZ,
vmaxX, vmaxY, vmaxZ,
vmaxX, vmaxY, vminZ,
vminX, vmaxY, vmaxZ,
vmaxX, vmaxY, vminZ,
vminX, vmaxY, vminZ,
# Bottom face
vminX, vminY, vminZ,
vmaxX, vminY, vminZ,
vmaxX, vminY, vmaxZ,
vminX, vminY, vminZ,
vmaxX, vminY, vmaxZ,
vminX, vminY, vmaxZ,
]
self.vertexBuffer.colors = [
# Front face
self.color[0], self.color[1], self.color[2], alpha,
self.color[0], self.color[1], self.color[2], alpha,
self.color[0], self.color[1], self.color[2], alpha,
self.color[0], self.color[1], self.color[2], alpha,
self.color[0], self.color[1], self.color[2], alpha,
self.color[0], self.color[1], self.color[2], alpha,
# Back face
self.color[0], self.color[1], self.color[2], alpha,
self.color[0], self.color[1], self.color[2], alpha,
self.color[0], self.color[1], self.color[2], alpha,
self.color[0], self.color[1], self.color[2], alpha,
self.color[0], self.color[1], self.color[2], alpha,
self.color[0], self.color[1], self.color[2], alpha,
# Left face
self.color[0], self.color[1], self.color[2], alpha,
self.color[0], self.color[1], self.color[2], alpha,
self.color[0], self.color[1], self.color[2], alpha,
self.color[0], self.color[1], self.color[2], alpha,
self.color[0], self.color[1], self.color[2], alpha,
self.color[0], self.color[1], self.color[2], alpha,
# Right face
self.color[0], self.color[1], self.color[2], alpha,
self.color[0], self.color[1], self.color[2], alpha,
self.color[0], self.color[1], self.color[2], alpha,
self.color[0], self.color[1], self.color[2], alpha,
self.color[0], self.color[1], self.color[2], alpha,
self.color[0], self.color[1], self.color[2], alpha,
# Top face
self.color[0], self.color[1], self.color[2], alpha,
self.color[0], self.color[1], self.color[2], alpha,
self.color[0], self.color[1], self.color[2], alpha,
self.color[0], self.color[1], self.color[2], alpha,
self.color[0], self.color[1], self.color[2], alpha,
self.color[0], self.color[1], self.color[2], alpha,
# Bottom face
self.color[0], self.color[1], self.color[2], alpha,
self.color[0], self.color[1], self.color[2], alpha,
self.color[0], self.color[1], self.color[2], alpha,
self.color[0], self.color[1], self.color[2], alpha,
self.color[0], self.color[1], self.color[2], alpha,
self.color[0], self.color[1], self.color[2], alpha,
]
self.vertexBuffer.buildData()
def draw(self):
self.vertexBuffer.draw()

206
archive/dusk/tile.py Normal file
View File

@@ -0,0 +1,206 @@
from OpenGL.GL import *
from tools.dusk.defs import (
TILE_WIDTH, TILE_HEIGHT, TILE_DEPTH,
CHUNK_WIDTH, CHUNK_HEIGHT, CHUNK_DEPTH,
TILE_SHAPE_NULL, TILE_SHAPE_FLOOR,
TILE_SHAPE_RAMP_NORTH, TILE_SHAPE_RAMP_SOUTH,
TILE_SHAPE_RAMP_EAST, TILE_SHAPE_RAMP_WEST,
TILE_SHAPE_RAMP_SOUTHWEST, TILE_SHAPE_RAMP_SOUTHEAST,
TILE_SHAPE_RAMP_NORTHWEST, TILE_SHAPE_RAMP_NORTHEAST
)
def getItem(arr, index, default):
if index < len(arr):
return arr[index]
return default
class Tile:
def __init__(self, chunk, x, y, z, tileIndex):
self.shape = TILE_SHAPE_NULL
self.chunk = chunk
self.x = x
self.y = y
self.z = z
self.index = tileIndex
self.posX = x * TILE_WIDTH + chunk.x * CHUNK_WIDTH * TILE_WIDTH
self.posY = y * TILE_HEIGHT + chunk.y * CHUNK_HEIGHT * TILE_HEIGHT
self.posZ = z * TILE_DEPTH + chunk.z * CHUNK_DEPTH * TILE_DEPTH
def chunkReload(self, newX, newY, newZ):
self.posX = self.x * TILE_WIDTH + newX * CHUNK_WIDTH * TILE_WIDTH
self.posY = self.y * TILE_HEIGHT + newY * CHUNK_HEIGHT * TILE_HEIGHT
self.posZ = self.z * TILE_DEPTH + newZ * CHUNK_DEPTH * TILE_DEPTH
def load(self, chunkData):
self.shape = getItem(chunkData['shapes'], self.index, TILE_SHAPE_NULL)
def setShape(self, shape):
if shape == self.shape:
return
self.shape = shape
self.chunk.dirty = True
self.chunk.tileUpdateVertices()
self.chunk.onChunkData.invoke(self.chunk)
def getBaseTileModel(self):
vertices = []
indices = []
uvs = []
colors = []
if self.shape == TILE_SHAPE_NULL:
pass
elif self.shape == TILE_SHAPE_FLOOR:
vertices = [
(self.posX, self.posY, self.posZ),
(self.posX + TILE_WIDTH, self.posY, self.posZ),
(self.posX + TILE_WIDTH, self.posY + TILE_HEIGHT, self.posZ),
(self.posX, self.posY + TILE_HEIGHT, self.posZ)
]
indices = [0, 1, 2, 0, 2, 3]
uvs = [ (0, 0), (1, 0), (1, 1), (0, 1) ]
colors = [ (255, 255, 255, 255) ] * 4
elif self.shape == TILE_SHAPE_RAMP_NORTH:
vertices = [
(self.posX, self.posY, self.posZ + TILE_DEPTH),
(self.posX + TILE_WIDTH, self.posY, self.posZ + TILE_DEPTH),
(self.posX + TILE_WIDTH, self.posY + TILE_HEIGHT, self.posZ),
(self.posX, self.posY + TILE_HEIGHT, self.posZ)
]
indices = [0, 1, 2, 0, 2, 3]
uvs = [ (0, 0), (1, 0), (1, 1), (0, 1) ]
colors = [ (255, 0, 0, 255) ] * 4
elif self.shape == TILE_SHAPE_RAMP_SOUTH:
vertices = [
(self.posX, self.posY, self.posZ),
(self.posX + TILE_WIDTH, self.posY, self.posZ),
(self.posX + TILE_WIDTH, self.posY + TILE_HEIGHT, self.posZ + TILE_DEPTH),
(self.posX, self.posY + TILE_HEIGHT, self.posZ + TILE_DEPTH)
]
indices = [0, 1, 2, 0, 2, 3]
uvs = [ (0, 0), (1, 0), (1, 1), (0, 1) ]
colors = [ (0, 255, 0, 255) ] * 4
elif self.shape == TILE_SHAPE_RAMP_EAST:
vertices = [
(self.posX, self.posY, self.posZ),
(self.posX + TILE_WIDTH, self.posY, self.posZ + TILE_DEPTH),
(self.posX + TILE_WIDTH, self.posY + TILE_HEIGHT, self.posZ + TILE_DEPTH),
(self.posX, self.posY + TILE_HEIGHT, self.posZ)
]
indices = [0, 1, 2, 0, 2, 3]
uvs = [ (0, 0), (1, 0), (1, 1), (0, 1) ]
colors = [ (0, 0, 255, 255) ] * 4
elif self.shape == TILE_SHAPE_RAMP_WEST:
vertices = [
(self.posX, self.posY, self.posZ + TILE_DEPTH),
(self.posX + TILE_WIDTH, self.posY, self.posZ),
(self.posX + TILE_WIDTH, self.posY + TILE_HEIGHT, self.posZ),
(self.posX, self.posY + TILE_HEIGHT, self.posZ + TILE_DEPTH)
]
indices = [0, 1, 2, 0, 2, 3]
uvs = [ (0, 0), (1, 0), (1, 1), (0, 1) ]
colors = [ (255, 255, 0, 255) ] * 4
elif self.shape == TILE_SHAPE_RAMP_SOUTHWEST:
vertices = [
(self.posX, self.posY, self.posZ + TILE_DEPTH),
(self.posX + TILE_WIDTH, self.posY, self.posZ),
(self.posX + TILE_WIDTH, self.posY + TILE_HEIGHT, self.posZ + TILE_DEPTH),
(self.posX, self.posY + TILE_HEIGHT, self.posZ + TILE_DEPTH)
]
indices = [0, 1, 2, 0, 2, 3]
uvs = [ (0, 0), (1, 0), (1, 1), (0, 1) ]
colors = [ (255, 128, 0, 255) ] * 4
elif self.shape == TILE_SHAPE_RAMP_NORTHWEST:
vertices = [
(self.posX, self.posY, self.posZ + TILE_DEPTH),
(self.posX + TILE_WIDTH, self.posY, self.posZ + TILE_DEPTH),
(self.posX + TILE_WIDTH, self.posY + TILE_HEIGHT, self.posZ),
(self.posX, self.posY + TILE_HEIGHT, self.posZ + TILE_DEPTH)
]
indices = [0, 1, 2, 0, 2, 3]
uvs = [ (0, 0), (1, 0), (1, 1), (0, 1) ]
colors = [ (128, 255, 0, 255) ] * 4
elif self.shape == TILE_SHAPE_RAMP_NORTHEAST:
vertices = [
(self.posX, self.posY, self.posZ + TILE_DEPTH),
(self.posX + TILE_WIDTH, self.posY, self.posZ + TILE_DEPTH),
(self.posX + TILE_WIDTH, self.posY + TILE_HEIGHT, self.posZ + TILE_DEPTH),
(self.posX, self.posY + TILE_HEIGHT, self.posZ)
]
indices = [0, 1, 2, 0, 2, 3]
uvs = [ (0, 0), (1, 0), (1, 1), (0, 1) ]
colors = [ (0, 255, 128, 255) ] * 4
elif self.shape == TILE_SHAPE_RAMP_SOUTHEAST:
vertices = [
(self.posX, self.posY, self.posZ),
(self.posX + TILE_WIDTH, self.posY, self.posZ + TILE_DEPTH),
(self.posX + TILE_WIDTH, self.posY + TILE_HEIGHT, self.posZ + TILE_DEPTH),
(self.posX, self.posY + TILE_HEIGHT, self.posZ + TILE_DEPTH)
]
indices = [0, 1, 2, 0, 2, 3]
uvs = [ (0, 0), (1, 0), (1, 1), (0, 1) ]
colors = [ (255, 128, 255, 255) ] * 4
else:
# Solid black cube for unknown shape
x0, y0, z0 = self.posX, self.posY, self.posZ
x1, y1, z1 = self.posX + TILE_WIDTH, self.posY + TILE_HEIGHT, self.posZ + TILE_DEPTH
vertices = [
(x0, y0, z0), (x1, y0, z0), (x1, y1, z0), (x0, y1, z0), # bottom
(x0, y0, z1), (x1, y0, z1), (x1, y1, z1), (x0, y1, z1) # top
]
indices = [
0,1,2, 0,2,3, # bottom
4,5,6, 4,6,7, # top
0,1,5, 0,5,4, # front
2,3,7, 2,7,6, # back
1,2,6, 1,6,5, # right
3,0,4, 3,4,7 # left
]
uvs = [ (0,0) ] * 8
colors = [ (0,0,0,255) ] * 8
return {
'vertices': vertices,
'indices': indices,
'uvs': uvs,
'colors': colors
}
def buffer(self, vertexBuffer):
if self.shape == TILE_SHAPE_NULL:
return
# New code:
baseData = self.getBaseTileModel()
# Base data is indiced but we need to buffer unindiced data
for index in baseData['indices']:
verts = baseData['vertices'][index]
uv = baseData['uvs'][index]
color = baseData['colors'][index]
vertexBuffer.vertices.extend([
verts[0] - (TILE_WIDTH / 2.0),
verts[1] - (TILE_HEIGHT / 2.0),
verts[2] - (TILE_DEPTH / 2.0)
])
vertexBuffer.colors.extend([
color[0] / 255.0,
color[1] / 255.0,
color[2] / 255.0,
color[3] / 255.0
])

View File

@@ -1,37 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "time.h"
#include "util/memory.h"
#include "assert/assert.h"
dusktime_t TIME;
void timeInit(void) {
memoryZero(&TIME, sizeof(TIME));
// Set these to something non-zero.
TIME.lastTick = TIME_STEP;
TIME.delta = TIME.realDelta = TIME_STEP;
TIME.realTime = TIME.time = TIME_STEP * 2;
}
void timeUpdate(void) {
TIME.realDelta = timeDeltaGet();
#if TIME_DYNAMIC
TIME.delta = TIME.realDelta;
#else
TIME.delta = TIME_PLATFORM_STEP;
#endif
assertTrue(TIME.delta >= 0.0f, "Time delta is negative");
assertTrue(TIME.realDelta >= 0.0f, "Real time delta is negative");
TIME.time += TIME.delta;
TIME.realTime += TIME.realDelta;
}

View File

@@ -1,52 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
typedef struct {
float_t delta;
float_t lastTick;
float_t time;
float_t realDelta;
float_t realTime;
} dusktime_t;
extern dusktime_t TIME;
#define TIME_STEP (1.0f / 60.0f) // Default to 60FPS
#ifndef TIME_DYNAMIC
#define TIME_DYNAMIC 1
#endif
#if TIME_DYNAMIC == 0
#ifndef TIME_PLATFORM_STEP
#define TIME_PLATFORM_STEP TIME_STEP
#endif
#endif
/**
* Initializes the time system.
*/
void timeInit(void);
/**
* Updates the time system
*/
void timeUpdate(void);
#if TIME_DYNAMIC == 1
/**
* Gets the time delta since the last frame, in seconds. Tied to the
* platform.
*
* This will only get called once per gameUpdate.
*/
float_t timeDeltaGet(void);
#endif

View File

@@ -1,191 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "uitextbox.h"
#include "util/memory.h"
#include "assert/assert.h"
#include "input.h"
uitextbox_t UI_TEXTBOX;
void uiTextboxInit() {
memoryZero(&UI_TEXTBOX, sizeof(uitextbox_t));
}
void uiTextboxUpdate() {
if(UI_TEXTBOX.visible == false) return;
if(UI_TEXTBOX.charsRevealed < UI_TEXTBOX.pageChars[UI_TEXTBOX.page]) {
UI_TEXTBOX.charsRevealed++;
if(inputIsDown(INPUT_BIND_ACTION)) {
UI_TEXTBOX.charsRevealed++;
}
} else {
if(inputPressed(INPUT_BIND_ACTION)) {
if(UI_TEXTBOX.page < UI_TEXTBOX.pageCount - 1) {
UI_TEXTBOX.page++;
UI_TEXTBOX.charsRevealed = 0;
} else {
// Close the textbox
UI_TEXTBOX.visible = false;
UI_TEXTBOX.page = 0;
UI_TEXTBOX.charsRevealed = 0;
}
}
}
}
void uiTextboxSetText(const char_t *text) {
assertNotNull(text, "Text pointer cannot be NULL, call uiTextboxClose()");
memoryZero(UI_TEXTBOX.text, sizeof(UI_TEXTBOX.text));
memoryZero(UI_TEXTBOX.lineLengths, sizeof(UI_TEXTBOX.lineLengths));
memoryZero(UI_TEXTBOX.pageChars, sizeof(UI_TEXTBOX.pageChars));
UI_TEXTBOX.pageCount = 1;// Always at least one page.
UI_TEXTBOX.totalChars = 0;
UI_TEXTBOX.page = 0;
UI_TEXTBOX.charsRevealed = 0;
UI_TEXTBOX.visible = true;
char_t c;// current char
uint16_t i = 0;// Index of character we are pulling
uint16_t lastWordStart = 0;// Index of the last word start (in src string).
uint8_t line = 0;// Which line we are currently writing to.
uint8_t page = 0;
bool_t startOfLine = true;// Are we at the start of a line?
while((c = text[i++]) != '\0') {
// HARD disallowed characters.
assertTrue(c != '\r', "Carriage return characters not allowed.");
assertTrue(c != '\t', "Tab characters not allowed.");
// Is this the beginning of a new line?
if(startOfLine) {
// Yes, we are at the start of a new line.
startOfLine = false;
// Is this the first line?
if(line == 0) {
// nothing to do, just continue.
} else {
// Yes, start of new line. Is this line the first line on a new page?
if((line % UI_TEXTBOX_LINES_PER_PAGE) == 0) {
// Yes, start a new page.
i--;// Rewind so that this character can go through the loop again.
goto newPage;
}
}
}
// Change what we do depending on the character.
if(c == '\n') {
goto newline;
} else if(c == ' ') {
goto whitespace;
} else {
goto character;
}
// Handle whitespace characters (not newlines)
whitespace: {
// Is this whitespace the last char of the line?
if(UI_TEXTBOX.lineLengths[line] == UI_TEXTBOX_CHARS_PER_LINE) {
goto newline;
} else if(UI_TEXTBOX.lineLengths[line] == 0) {
// If this is the first character of the line, we can just ignore it.
continue;
}
lastWordStart = i;
goto appendCharacter;
}
// Handle regular characters
character: {
// Is this character going to cause a wrap to occur?
if(UI_TEXTBOX.lineLengths[line] == UI_TEXTBOX_CHARS_PER_LINE) {
// How long ago was the last whitespace?
uint16_t charsSinceLastSpace = i - lastWordStart;
// Is the word longer than a line can possibly hold?
assertTrue(
charsSinceLastSpace < UI_TEXTBOX_CHARS_PER_LINE,
"Word longer than a line can hold."
);
// Undo appending of all characters since the last whitespace.
UI_TEXTBOX.totalChars -= charsSinceLastSpace;
UI_TEXTBOX.lineLengths[line] -= charsSinceLastSpace;
UI_TEXTBOX.pageChars[page] -= charsSinceLastSpace;
// Rewind the loop so that printing will begin on the new line at the
// start of the last word.
i -= charsSinceLastSpace;
// Newline.
goto newline;
}
// Append the character to the textbox.
goto appendCharacter;
}
// Handle newlines
newline: {
// Ensure we don't exceed the maximum number of lines.
assertTrue(
line < UI_TEXTBOX_LINE_COUNT,
"Exceeded maximum number of lines in textbox."
);
// Add a line to the textbox.
line++;
startOfLine = true;// Next iteration will be a start of line.
lastWordStart = i;// We also mark this as the last word start.
continue;
}
newPage: {
// Make sure we don't exceed the maximum number of pages.
assertTrue(
UI_TEXTBOX.pageCount < UI_TEXTBOX_PAGE_COUNT_MAX,
"Exceeded maximum number of pages in textbox."
);
UI_TEXTBOX.pageCount++;
page++;
continue;
}
appendCharacter: {
assertTrue(
UI_TEXTBOX.totalChars < UI_TEXTBOX_CHARS_MAX,
"Exceeded maximum number of characters in textbox."
);
assertTrue(
line < UI_TEXTBOX_LINE_COUNT,
"Exceeded maximum number of lines in textbox."
);
assertTrue(
UI_TEXTBOX.lineLengths[line] < UI_TEXTBOX_CHARS_PER_LINE,
"Exceeded maximum number of chars per line in textbox."
);
// Push the character to the textbox.
UI_TEXTBOX.text[
line * UI_TEXTBOX_CHARS_PER_LINE + UI_TEXTBOX.lineLengths[line]
] = c;
// Increment the line length and page character count.
UI_TEXTBOX.totalChars++;
UI_TEXTBOX.lineLengths[line]++;
UI_TEXTBOX.pageChars[page]++;
continue;
}
assertUnreachable("Code should not reach here, all cases handled.");
}
}

View File

@@ -1,77 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "display/renderbase.h"
#include "ui/font.h"
#define UI_TEXTBOX_LINES_PER_PAGE 4
#define UI_TEXTBOX_WIDTH RENDER_WIDTH
#define UI_TEXTBOX_HEIGHT_INNER ( \
FONT_TILE_HEIGHT * UI_TEXTBOX_LINES_PER_PAGE \
)
#define UI_TEXTBOX_BORDER_WIDTH 4
#define UI_TEXTBOX_BORDER_HEIGHT UI_TEXTBOX_BORDER_WIDTH
#define UI_TEXTBOX_PADDING_X 2
#define UI_TEXTBOX_PADDING_Y UI_TEXTBOX_PADDING_X
#define UI_TEXTBOX_WIDTH_INNER ( \
UI_TEXTBOX_WIDTH - (UI_TEXTBOX_BORDER_WIDTH * 2) - \
(UI_TEXTBOX_PADDING_X * 2) \
)
#define UI_TEXTBOX_HEIGHT ( \
UI_TEXTBOX_HEIGHT_INNER + (UI_TEXTBOX_BORDER_HEIGHT * 2) + \
(UI_TEXTBOX_PADDING_Y * 2) \
)
#define UI_TEXTBOX_CHARS_PER_LINE (UI_TEXTBOX_WIDTH_INNER / FONT_TILE_WIDTH)
#define UI_TEXTBOX_CHARS_PER_PAGE ( \
UI_TEXTBOX_CHARS_PER_LINE * UI_TEXTBOX_LINES_PER_PAGE \
)
#define UI_TEXTBOX_PAGE_COUNT_MAX 6
#define UI_TEXTBOX_LINE_COUNT ( \
UI_TEXTBOX_LINES_PER_PAGE * UI_TEXTBOX_PAGE_COUNT_MAX \
)
#define UI_TEXTBOX_CHARS_MAX ( \
UI_TEXTBOX_CHARS_PER_PAGE * UI_TEXTBOX_PAGE_COUNT_MAX \
)
#define UI_TEXTBOX_REVEAL_RATE 2
typedef struct {
char_t text[UI_TEXTBOX_CHARS_MAX];
uint8_t lineLengths[UI_TEXTBOX_LINE_COUNT];
uint8_t pageCount;
uint8_t pageChars[UI_TEXTBOX_PAGE_COUNT_MAX];
uint16_t totalChars;
uint8_t page;
uint8_t charsRevealed;
bool_t visible;
} uitextbox_t;
extern uitextbox_t UI_TEXTBOX;
/**
* Initializes the UI textbox.
*/
void uiTextboxInit(void);
/**
* Updates the UI textbox, handling text input and scrolling.
*/
void uiTextboxUpdate(void);
/**
* Sets the text for the UI textbox.
*
* @param text The text to display in the textbox.
*/
void uiTextboxSetText(
const char_t *text
);

View File

@@ -1,17 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
/**
* Finds the next power of two greater than or equal to the given value.
*
* @param value The value to find the next power of two for.
* @return The next power of two greater than or equal to the value.
*/
uint32_t mathNextPowTwo(uint32_t value);

View File

@@ -1,96 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "memory.h"
#include "assert/assert.h"
void * memoryAllocate(const size_t size) {
assertTrue(size > 0, "Cannot allocate 0 bytes of memory.");
void *ptr = malloc(size);
assertNotNull(ptr, "Memory allocation failed.");
return ptr;
}
void memoryFree(void *ptr) {
assertNotNull(ptr, "Cannot free NULL memory.");
free(ptr);
}
void memoryCopy(void *dest, const void *src, const size_t size) {
assertNotNull(dest, "Cannot copy to NULL memory.");
assertNotNull(src, "Cannot copy from NULL memory.");
assertTrue(size > 0, "Cannot copy 0 bytes of memory.");
assertTrue(dest != src, "Cannot copy memory to itself.");
memcpy(dest, src, size);
}
void memorySet(void *dest, const uint8_t value, const size_t size) {
assertNotNull(dest, "Cannot set NULL memory.");
assertTrue(size > 0, "Cannot set 0 bytes of memory.");
memset(dest, value, size);
}
void memoryZero(void *dest, const size_t size) {
memorySet(dest, 0, size);
}
void memoryCopyRangeSafe(
void *dest,
const void *start,
const void *end,
const size_t sizeMax
) {
assertFalse(start == end, "Start and end pointers are the same.");
assertTrue(end > start, "End pointer is not after start pointer.");
size_t copy = (size_t)end - (size_t)start;
assertTrue(copy <= sizeMax, "Size of memory to copy is too large.");
memoryCopy(dest, start, copy);
}
void memoryMove(void *dest, const void *src, const size_t size) {
assertNotNull(dest, "Cannot move to NULL memory.");
assertNotNull(src, "Cannot move from NULL memory.");
assertTrue(size > 0, "Cannot move 0 bytes of memory.");
assertTrue(dest != src, "Cannot move memory to itself.");
memmove(dest, src, size);
}
int_t memoryCompare(
const void *a,
const void *b,
const size_t size
) {
assertNotNull(a, "Cannot compare NULL memory.");
assertNotNull(b, "Cannot compare NULL memory.");
assertTrue(size > 0, "Cannot compare 0 bytes of memory.");
return memcmp(a, b, size);
}
void memoryReallocate(void **ptr, const size_t size) {
assertNotNull(ptr, "Cannot reallocate NULL pointer.");
assertTrue(size > 0, "Cannot reallocate to 0 bytes of memory.");
void *newPointer = memoryAllocate(size);
assertNotNull(newPointer, "Memory reallocation failed.");
memoryFree(*ptr);
*ptr = newPointer;
}
void memoryResize(void **ptr, const size_t oldSize, const size_t newSize) {
assertNotNull(ptr, "Cannot resize NULL pointer.");
if(newSize == oldSize) return;
if(oldSize == 0) return memoryReallocate(ptr, newSize);
assertTrue(newSize > oldSize, "New size must be greater than old size.");
void *newPointer = memoryAllocate(newSize);
assertNotNull(newPointer, "Memory resizing failed.");
memoryCopy(newPointer, *ptr, oldSize);
memoryFree(*ptr);
*ptr = newPointer;
}

View File

@@ -1,128 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "string.h"
#include "assert/assert.h"
#include "util/memory.h"
bool_t stringIsWhitespace(const char_t c) {
return isspace(c);
}
void stringCopy(char_t *dest, const char_t *src, const size_t destSize) {
assertNotNull(dest, "dest must not be NULL");
assertNotNull(src, "src must not be NULL");
assertTrue(destSize > 0, "destSize must be greater than 0");
assertStrLenMax(src, destSize, "src is too long");
memoryCopy(dest, src, strlen(src) + 1);
}
int stringCompare(const char_t *str1, const char_t *str2) {
assertNotNull(str1, "str1 must not be NULL");
assertNotNull(str2, "str2 must not be NULL");
return strcmp(str1, str2);
}
void stringTrim(char_t *str) {
assertNotNull(str, "str must not be NULL");
// Trim leading whitespace
char_t *start = str;
while(stringIsWhitespace(*start)) start++;
// Trim trailing whitespace
char_t *end = start + strlen(start) - 1;
while (end >= start && stringIsWhitespace(*end)) end--;
// Null-terminate the string
*(end + 1) = '\0';
// Move trimmed string to the original buffer
if (start != str) memmove(str, start, end - start + 2);
}
char_t * stringToken(char_t *str, const char_t *delim) {
assertNotNull(str, "str must not be NULL");
assertNotNull(delim, "delim must not be NULL");
return strtok(str, delim);
}
int32_t stringFormat(
char_t *dest,
const size_t destSize,
const char_t *format,
...
) {
va_list args;
va_start(args, format);
int32_t len = stringFormatVA(dest, destSize, format, args);
va_end(args);
return len;
}
int32_t stringFormatVA(
char_t *dest,
const size_t destSize,
const char_t *format,
const va_list args
) {
assertNotNull(format, "format must not be NULL");
if(dest == NULL) {
int32_t ret = vsnprintf(NULL, 0, format, args);
assertTrue(ret >= 0, "Failed to format string.");
return ret;
}
assertTrue(destSize > 0, "destSize must be greater than 0");
int32_t ret = vsnprintf(dest, destSize, format, args);
assertTrue(ret >= 0, "Failed to format string.");
assertTrue(ret < destSize, "Formatted string is too long.");
return ret;
}
bool_t stringToI32(const char_t *str, int32_t *out) {
assertNotNull(str, "str must not be NULL");
assertNotNull(out, "out must not be NULL");
char_t *endptr;
errno = 0;
long int result = strtol(str, &endptr, 10);
if (errno != 0 || *endptr != '\0') {
return false;
}
*out = (int32_t)result;
return true;
}
bool_t stringToI64(const char_t *str, int64_t *out) {
assertNotNull(str, "str must not be NULL");
assertNotNull(out, "out must not be NULL");
char_t *endptr;
errno = 0;
long long int result = strtoll(str, &endptr, 10);
if (errno != 0 || *endptr != '\0') {
return false;
}
*out = (int64_t)result;
return true;
}
bool_t stringToU16(const char_t *str, uint16_t *out) {
assertNotNull(str, "str must not be NULL");
assertNotNull(out, "out must not be NULL");
char_t *endptr;
errno = 0;
unsigned long int result = strtoul(str, &endptr, 10);
if (errno != 0 || *endptr != '\0' || result > UINT16_MAX) {
return false;
}
*out = (uint16_t)result;
return true;
}

View File

@@ -1,118 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
/**
* Determines if a character is whitespace.
*
* @param c The character to check.
* @return TRUE if the character is whitespace, FALSE otherwise.
*/
bool_t stringIsWhitespace(const char_t c);
/**
* Copies a string from src to dest, ensuring the dest string is null-terminated
* and does not exceed the specified size.
*
* @param dest The destination string.
* @param src The source string.
* @param destSize The size of the destination string exc. null terminator.
*/
void stringCopy(char_t *dest, const char_t *src, const size_t destSize);
/**
* Compares two strings.
*
* @param str1 The first string.
* @param str2 The second string.
* @return 0 if the strings are equal, -1 if str1 is less than str2, 1 if str1
* is greater than str2.
*/
int stringCompare(const char_t *str1, const char_t *str2);
/**
* Trims whitespace from the beginning and end of a string.
*
* @param str The string to trim.
*/
void stringTrim(char_t *str);
/**
* Gets the next token in a string using a delimiter.
* e.g. input: "Hello, World, Happy Monday!" with stringToken(input, ",") will
* return "Hello" then " World" then " Happy Monday!" on each subsequent call.
*
* @param str The string to split.
* @param delim The delimiter to split by.
* @return A pointer to the next token in the string.
*/
char_t * stringToken(char_t *str, const char_t *delim);
/**
* Formats a string.
*
* @param dest The destination string, or NULL to get the length of the
* formatted string.
* @param destSize The size of the destination string exc. null terminator, can
* be anything if dest is NULL.
* @param format The format string.
* @param ... The arguments to format.
* @return The number of characters written.
*/
int32_t stringFormat(
char_t *dest,
const size_t destSize,
const char_t *format,
...
);
/**
* Formats a string using a va_list.
*
* @param dest The destination string, or NULL to get the length of the
* formatted string.
* @param destSize The size of the destination string exc. null terminator, can
* be anything if dest is NULL.
* @param format The format string.
* @param args The va_list of arguments.
* @return The number of characters written.
*/
int32_t stringFormatVA(
char_t *dest,
const size_t destSize,
const char_t *format,
const va_list args
);
/**
* Converts a string to an integer.
*
* @param str The string to convert.
* @param out The output integer.
* @return TRUE if the conversion was successful, FALSE otherwise.
*/
bool_t stringToI32(const char_t *str, int32_t *out);
/**
* Converts a string to a signed 16-bit integer.
*
* @param str The string to convert.
* @param out The output signed integer.
* @return TRUE if the conversion was successful, FALSE otherwise.
*/
bool_t stringToI16(const char_t *str, int16_t *out);
/**
* Converts a string to an unsigned 16-bit integer.
*
* @param str The string to convert.
* @param out The output unsigned integer.
* @return TRUE if the conversion was successful, FALSE otherwise.
*/
bool_t stringToU16(const char_t *str, uint16_t *out);

View File

@@ -1,321 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "chunk.h"
#include "util/memory.h"
#include "assert/assert.h"
#include "world/world.h"
void renderChunkUpdated(chunk_t *chunk);
chunkmap_t CHUNK_MAP;
void chunkMapInit() {
memoryZero(&CHUNK_MAP, sizeof(chunkmap_t));
// Load default chunks, YX order.
uint16_t i = 0;
chunk_t *chunk;
for(uint8_t y = 0; y < CHUNK_MAP_HEIGHT; y++) {
for(uint8_t x = 0; x < CHUNK_MAP_WIDTH; x++) {
assertTrue(i < CHUNK_MAP_COUNT, "Chunk index out of bounds");
chunk = CHUNK_MAP.chunks + i;
CHUNK_MAP.chunkOrder[i] = chunk;
chunkLoad(chunk, x, y);
assertTrue(
chunk->x == x && chunk->y == y,
"Chunk coordinates do not match expected values"
);
i++;
}
}
}
void chunkMapShift(const int16_t x, const int16_t y) {
if(x == 0 && y == 0) assertUnreachable("ChunkMapShift called with no shift");
chunk_t *newChunkOrder[CHUNK_MAP_COUNT];
chunk_t *unloadedChunks[CHUNK_MAP_COUNT];
chunk_t *chunk;
uint8_t i, j;
uint16_t
/** New Map Coordinates */
newX, newY,
newChunkX, newChunkY
;
// Calculate the new map coordinates
newX = CHUNK_MAP.topLeftX + x;
newY = CHUNK_MAP.topLeftY + y;
// Zero the new chunk order
memoryZero(newChunkOrder, sizeof(newChunkOrder));
// For each chunk...
j = 0;
chunk = CHUNK_MAP.chunks;
do {
// Is this chunk still going to be within the map bounds?
if(
chunk->x < newX || chunk->y < newY ||
chunk->x >= newX + CHUNK_MAP_WIDTH ||
chunk->y >= newY + CHUNK_MAP_HEIGHT
) {
// No, it's not, let's unload it and make it available for reuse.
chunkUnload(chunk);
assertTrue(
j < CHUNK_MAP_COUNT,
"Unloaded chunk index out of bounds"
);
unloadedChunks[j++] = chunk;
chunk++;
continue;
}
// Yes it is still valid, determine the new index that it will be at
i = (chunk->y - newY) * CHUNK_MAP_WIDTH + (chunk->x - newX);
assertTrue(
i < CHUNK_MAP_COUNT,
"Chunk index out of bounds after shifting"
);
assertNull(
newChunkOrder[i],
"New chunk order index is already occupied"
);
// Set the new chunk order
newChunkOrder[i] = chunk;
chunk++;
} while(chunk < CHUNK_MAP.chunks + CHUNK_MAP_COUNT);
// Now check the new chunk order list for missing chunks.
i = 0;
do {
assertTrue(
i < CHUNK_MAP_COUNT,
"New chunk order index out of bounds after shifting"
);
// Is this chunk loaded still?
chunk = newChunkOrder[i];
if(chunk != NULL) {
i++;
continue;
}
// Determine the new chunk coordinates.
newChunkX = i % CHUNK_MAP_WIDTH + newX;
newChunkY = i / CHUNK_MAP_WIDTH + newY;
assertTrue(
newChunkX >= newX && newChunkX < newX + CHUNK_MAP_WIDTH,
"New chunk X coordinate out of bounds after shifting"
);
assertTrue(
newChunkY >= newY && newChunkY < newY + CHUNK_MAP_HEIGHT,
"New chunk Y coordinate out of bounds after shifting"
);
// Pop a chunk from the unloaded chunks list.
assertTrue(j > 0, "No unloaded chunks available to reuse");
chunk = unloadedChunks[--j];
assertNotNull(chunk, "Unloaded chunk pointer is null");
// Load the chunk at the new coordinates.
chunkLoad(chunk, newChunkX, newChunkY);
assertTrue(
chunk->x == newChunkX && chunk->y == newChunkY,
"Chunk coordinates do not match expected values after shifting"
);
// Set it in order.
newChunkOrder[i] = chunk;
i++;
} while(i < CHUNK_MAP_COUNT);
// Update Absolutes.
CHUNK_MAP.topLeftX = newX;
CHUNK_MAP.topLeftY = newY;
// Update the chunk order.
memoryCopy(
CHUNK_MAP.chunkOrder,
newChunkOrder,
sizeof(CHUNK_MAP.chunkOrder)
);
}
void chunkMapSetPosition(const uint16_t x, const uint16_t y) {
if(x == CHUNK_MAP.topLeftX && y == CHUNK_MAP.topLeftY) {
return;
}
int16_t shiftX = x - CHUNK_MAP.topLeftX;
int16_t shiftY = y - CHUNK_MAP.topLeftY;
// Are we shifting the entire map?
if(
shiftX >= CHUNK_MAP_WIDTH || shiftX < -CHUNK_MAP_WIDTH ||
shiftY >= CHUNK_MAP_HEIGHT || shiftY < -CHUNK_MAP_HEIGHT
) {
printf("Shifting chunk map to new position (%u, %u)\n", x, y);
}
// Shift the chunk map by the specified offsets.
chunkMapShift(shiftX, shiftY);
}
chunk_t * chunkGetChunkAt(const uint16_t chunkX, const uint16_t chunkY) {
assertTrue(
chunkX < WORLD_WIDTH && chunkY < WORLD_HEIGHT,
"Chunk coordinates out of bounds"
);
chunk_t *chunk = CHUNK_MAP.chunks;
do {
if(chunk->x == chunkX && chunk->y == chunkY) return chunk;
chunk++;
} while(chunk < CHUNK_MAP.chunks + CHUNK_MAP_COUNT);
return NULL;
}
void chunkLoad(chunk_t *chunk, const uint16_t x, const uint16_t y) {
assertNotNull(chunk, "Chunk pointer is null");
// Zero out the chunk data.
memoryZero(chunk, sizeof(chunk_t));
// Set the chunk coordinates.
chunk->x = x;
chunk->y = y;
// Only load data if the chunk is within bounds.
if(x >= WORLD_WIDTH || y >= WORLD_HEIGHT) {
memorySet(chunk->tilesBase, 0, sizeof(chunk->tilesBase));
memorySet(chunk->tilesBaseOverlay, 0, sizeof(chunk->tilesBaseOverlay));
return;
}
// Is chunk data defined?
const chunkdata_t *chunkData = WORLD_CHUNKS[y * WORLD_WIDTH + x];
if(chunkData == NULL) {
memorySet(chunk->tilesBase, 0, sizeof(chunk->tilesBase));
memorySet(chunk->tilesBaseOverlay, 0, sizeof(chunk->tilesBaseOverlay));
return;
}
// Load tile data into chunk
// printf("Loading chunk at (%u, %u)\n", x, y);
memoryCopy(
chunk->tilesBase,
chunkData->layerBase,
sizeof(chunk->tilesBase)
);
memoryCopy(
chunk->tilesBaseOverlay,
chunkData->layerBaseOverlay,
sizeof(chunk->tilesBaseOverlay)
);
// Load chunk entities
const entity_t *data;
entity_t *entity;
data = chunkData->entities;
while(data < chunkData->entities + CHUNK_ENTITY_COUNT_MAX) {
if(data->type == ENTITY_TYPE_NULL) break;
// Store that this chunk owns this entity ID.
chunk->entityIDs[chunk->entityCount++] = data->id;
// Check entity isn't loaded (still).
entity = ENTITIES;
do {
if(entity->type != ENTITY_TYPE_NULL && entity->id == data->id) break;
entity++;
} while(entity < ENTITIES + ENTITY_COUNT_MAX);
if(entity != ENTITIES + ENTITY_COUNT_MAX) {
// Entity is already loaded, skip it.
printf("Entity ID %u already loaded, skipping...\n", data->id);
data++;
continue;
}
// Find an empty entity slot.
entity = ENTITIES;
while(true) {
assertTrue(
entity < ENTITIES + ENTITY_COUNT_MAX,
"Out of available entities"
);
if(entity->type == ENTITY_TYPE_NULL) break;
entity++;
};
// Load this entity.
entityLoad(entity, data);
data++;
}
// Allow the rendering platform to know this chunk is loaded.
renderChunkUpdated(chunk);
}
void chunkUnload(chunk_t *chunk) {
uint8_t i;
entity_t *entity;
uint32_t id;
assertNotNull(chunk, "Chunk pointer is null");
// Iterate over each entity this chunk owns.
i = 0;
while(i < chunk->entityCount) {
id = chunk->entityIDs[i++];
// Now, do we need to unload this entity?
bool_t shouldUnload = false;
// Now, find the entity loaded with this ID. It should be impossible for
// this entity to be unloaded (but may change in future).
entity = ENTITIES;
do {
if(entity->type != ENTITY_TYPE_NULL && entity->id == id) break;
entity++;
} while(entity < ENTITIES + ENTITY_COUNT_MAX);
assertTrue(
entity < ENTITIES + ENTITY_COUNT_MAX,
"Entity ID not found in ENTITIES array, cannot unload"
);
// If the entity is still within our chunk bounds, it's getting unloaded
if(
floorf(entity->x) >= chunk->x * CHUNK_WIDTH * TILE_WIDTH_HEIGHT &&
ceilf(entity->x) < (chunk->x + 1) * CHUNK_WIDTH * TILE_WIDTH_HEIGHT &&
floorf(entity->y) >= chunk->y * CHUNK_HEIGHT * TILE_WIDTH_HEIGHT &&
ceilf(entity->y) < (chunk->y + 1) * CHUNK_HEIGHT * TILE_WIDTH_HEIGHT
) {
shouldUnload = true;
} else {
assertUnreachable(
"Entity has left its chunk bounds, we should not be unloading it but "
"I have yet to implement that properly. It will need to self-manage "
"its own unloading somehow, and also not be in a null chunk "
"when it does so."
);
}
// This entity is still in use, leave it loaded.
if(!shouldUnload) continue;
// NULL the entity type, effectively unloading it.
entity->type = ENTITY_TYPE_NULL;
}
}

View File

@@ -1,84 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "tile.h"
#include "display/render.h"
#define CHUNK_WIDTH 8
#define CHUNK_HEIGHT 8
#define CHUNK_TILE_COUNT (CHUNK_WIDTH * CHUNK_HEIGHT)
#define CHUNK_ENTITY_COUNT_MAX 8
#define CHUNK_MAP_WIDTH (((RENDER_WIDTH / TILE_WIDTH_HEIGHT)/CHUNK_WIDTH)+2)
#define CHUNK_MAP_HEIGHT (((RENDER_HEIGHT / TILE_WIDTH_HEIGHT)/CHUNK_HEIGHT)+2)
#define CHUNK_MAP_COUNT (CHUNK_MAP_WIDTH * CHUNK_MAP_HEIGHT)
typedef struct {
uint16_t x, y;
tile_t tilesBase[CHUNK_TILE_COUNT];
tile_t tilesBaseOverlay[CHUNK_TILE_COUNT];
uint32_t entityIDs[CHUNK_ENTITY_COUNT_MAX];
uint8_t entityCount;
} chunk_t;
typedef struct {
chunk_t chunks[CHUNK_MAP_COUNT];
chunk_t *chunkOrder[CHUNK_MAP_COUNT];
uint16_t topLeftX;
uint16_t topLeftY;
} chunkmap_t;
extern chunkmap_t CHUNK_MAP;
/**
* Initializes the chunk map.
*/
void chunkMapInit();
/**
* Shifts the chunk map by the specified x and y offsets.
*
* @param x The x offset to shift the chunk map.
* @param y The y offset to shift the chunk map.
*/
void chunkMapShift(const int16_t x, const int16_t y);
/**
* Sets the position of the chunk map to the specified coordinates.
*
* @param x The x coordinate of the top-left chunk.
* @param y The y coordinate of the top-left chunk.
*/
void chunkMapSetPosition(const uint16_t x, const uint16_t y);
/**
* Gets the chunk at the specified chunk coordinates.
*
* @param chunkX The x coordinate of the chunk.
* @param chunkY The y coordinate of the chunk.
* @return A pointer to the chunk at the specified chunk coordinates, or NULL if
* no chunk exists at those coordinates.
*/
chunk_t * chunkGetChunkAt(const uint16_t chunkX, const uint16_t chunkY);
/**
* Loads a chunk at the specified coordinates.
*
* @param chunk The chunk to load.
* @param x The x coordinate of the chunk.
* @param y The y coordinate of the chunk.
*/
void chunkLoad(chunk_t *chunk, const uint16_t x, const uint16_t y);
/**
* Unloads a chunk (that is currently loaded).
*
* @param chunk The chunk to unload.
*/
void chunkUnload(chunk_t *chunk);

View File

@@ -1,16 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "chunk.h"
#include "entity/entity.h"
typedef struct {
uint8_t layerBase[CHUNK_TILE_COUNT];
uint8_t layerBaseOverlay[CHUNK_TILE_COUNT];
entity_t entities[CHUNK_ENTITY_COUNT_MAX];
} chunkdata_t;

View File

@@ -1,72 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "overworld.h"
#include "chunk.h"
#include "display/render.h"
#include "assert/assert.h"
#include "entity/entity.h"
uint32_t OVERWORLD_CAMERA_X;
uint32_t OVERWORLD_CAMERA_Y;
overworldcameratype_t OVERWORLD_CAMERA_TYPE;
void overworldInit(void) {
playerInit();
chunkMapInit();
OVERWORLD_CAMERA_X = 0;
OVERWORLD_CAMERA_Y = 0;
OVERWORLD_CAMERA_TYPE = OVERWORLD_CAMERA_TYPE_CENTERED_POSITION;
}
void overworldUpdate() {
entity_t *entity;
assertTrue(
OVERWORLD_CAMERA_X < OVERWORLD_CAMERA_LIMIT_X,
"Camera position limit (just because I haven't tested properly)"
);
assertTrue(
OVERWORLD_CAMERA_Y < OVERWORLD_CAMERA_LIMIT_Y,
"Camera position limit (just because I haven't tested properly)"
);
entity = ENTITIES;
do {
entityUpdate(entity++);
} while(entity->type != ENTITY_TYPE_NULL);
// Testing, follow player
entity = &ENTITIES[0]; // Player entity
assertTrue(
entity->type == ENTITY_TYPE_PLAYER,
"First entity must be player"
);
OVERWORLD_CAMERA_X = entity->x * TILE_WIDTH_HEIGHT + entity->subX;
OVERWORLD_CAMERA_Y = entity->y * TILE_WIDTH_HEIGHT + entity->subY;
uint16_t x, y;
uint16_t halfWidth, halfHeight;
halfWidth = ((CHUNK_MAP_WIDTH - 1) * CHUNK_WIDTH * TILE_WIDTH_HEIGHT) / 2;
halfHeight = ((CHUNK_MAP_HEIGHT - 1) * CHUNK_HEIGHT * TILE_WIDTH_HEIGHT) / 2;
// Calculate the chunk map position based on the camera position.
if(OVERWORLD_CAMERA_X < halfWidth) {
x = 0;
} else {
x = (OVERWORLD_CAMERA_X - halfWidth) / (CHUNK_WIDTH*TILE_WIDTH_HEIGHT);
}
if(OVERWORLD_CAMERA_Y < halfHeight) {
y = 0;
} else {
y = (OVERWORLD_CAMERA_Y - halfHeight) / (CHUNK_HEIGHT*TILE_WIDTH_HEIGHT);
}
chunkMapSetPosition(x, y);
}

View File

@@ -1,30 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
typedef enum {
OVERWORLD_CAMERA_TYPE_CENTERED_POSITION,
} overworldcameratype_t;
extern uint32_t OVERWORLD_CAMERA_X;
extern uint32_t OVERWORLD_CAMERA_Y;
extern overworldcameratype_t OVERWORLD_CAMERA_TYPE;
#define OVERWORLD_CAMERA_LIMIT_X (UINT32_MAX / 4)
#define OVERWORLD_CAMERA_LIMIT_Y (UINT32_MAX / 4)
/**
* Initializes the overworld.
*/
void overworldInit(void);
/**
* Updates the overworld.
*/
void overworldUpdate(void);

View File

@@ -1,26 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
#define TILE_WIDTH_HEIGHT 16
typedef uint8_t tile_t;
typedef enum {
TILE_SOLID_NONE = 0,
TILE_SOLID_FULL = 1,
TILE_SOLID_TRIANGLE_TOP_RIGHT = 2,
TILE_SOLID_TRIANGLE_TOP_LEFT = 3,
TILE_SOLID_TRIANGLE_BOTTOM_RIGHT = 4,
TILE_SOLID_TRIANGLE_BOTTOM_LEFT = 5,
} tilesolidtype_t;
typedef struct {
tilesolidtype_t solidType;
} tilemetadata_t;

View File

@@ -1,40 +0,0 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Libs
find_package(pspsdk REQUIRED)
find_package(SDL2 REQUIRED)
target_link_libraries(${DUSK_TARGET_NAME}
PRIVATE
# pspsdk
${SDL2_LIBRARIES}
)
# Compile definitions
target_compile_definitions(${DUSK_TARGET_NAME}
PRIVATE
RENDER_WIDTH=480
RENDER_HEIGHT=272
RENDER_WINDOW_WIDTH_DEFAULT=480
RENDER_WINDOW_HEIGHT_DEFAULT=272
# DUSK_TIME_DYNAMIC=0
DUSK_TIME_DYNAMIC=1
)
# Includes
target_include_directories(${DUSK_TARGET_NAME}
PRIVATE
${CMAKE_CURRENT_LIST_DIR}
${SDL2_INCLUDE_DIRS}
)
# Sources
target_sources(${DUSK_TARGET_NAME}
PRIVATE
duskpsp.c
)
# Subdirs

View File

@@ -1,11 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "duskpsp.h"
// PSP_MODULE_INFO("Dusk", 0, 1, 0);
// PSP_MAIN_THREAD_ATTR(PSP_THREAD_ATTR_USER | THREAD_ATTR_VFPU);

View File

@@ -1,41 +0,0 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Compile defs
target_compile_definitions(${DUSK_TARGET_NAME}
PUBLIC
# DUSK_KEYBOARD_SUPPORT=1
)
# Libs
find_package(SDL2 REQUIRED)
find_package(OpenGL REQUIRED)
find_package(cglm REQUIRED)
target_link_libraries(${DUSK_TARGET_NAME}
PUBLIC
SDL2::SDL2
OpenGL::GL
GL
cglm
)
# Includes
target_include_directories(${DUSK_TARGET_NAME}
PUBLIC
${CMAKE_CURRENT_LIST_DIR}
)
# Sources
target_sources(${DUSK_TARGET_NAME}
PRIVATE
dusksdl2input.c
main.c
time.c
)
# Subdirs
add_subdirectory(display)

View File

@@ -1,21 +0,0 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DUSK_TARGET_NAME}
PRIVATE
render.c
renderbackbuffer.c
)
# Subdirs
add_subdirectory(camera)
add_subdirectory(framebuffer)
add_subdirectory(mesh)
add_subdirectory(overworld)
add_subdirectory(texture)
add_subdirectory(spritebatch)
add_subdirectory(scene)
add_subdirectory(ui)

View File

@@ -1,125 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "camera.h"
#include "display/render.h"
#include "world/overworld.h"
void cameraUIPush(void) {
glPushMatrix();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glViewport(0, 0, RENDER_WIDTH, RENDER_HEIGHT);
mat4 ortho;
glm_ortho(
0.0f, (float_t)RENDER_WIDTH,
(float_t)RENDER_HEIGHT, 0.0f,
-1.0f, 1.0f,
ortho
);
glLoadMatrixf((const GLfloat*)ortho);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void cameraUIPop(void) {
glPopMatrix();
}
void cameraScreenPush(void) {
glPushMatrix();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
mat4 ortho;
#if RENDER_USE_FRAMEBUFFER
int32_t windowWidth, windowHeight;
SDL_GetWindowSize(RENDER_WINDOW, &windowWidth, &windowHeight);
glViewport(0, 0, windowWidth, windowHeight);
glm_ortho(
0.0f, (float_t) windowWidth,
(float_t)windowHeight, 0.0f,
-1.0f, 1.0f,
ortho
);
#else
glm_ortho(
0.0f, (float_t)RENDER_WIDTH,
(float_t)RENDER_HEIGHT, 0.0f,
-1.0f, 1.0f,
ortho
);
#endif
glLoadMatrixf((const GLfloat*)ortho);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void cameraScreenPop(void) {
glPopMatrix();
}
void cameraOverworldPush(void) {
glPushMatrix();
glLoadIdentity();
#if RENDER_USE_FRAMEBUFFER
glViewport(0, 0, RENDER_WIDTH, RENDER_HEIGHT);
#endif
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
const float_t fov = glm_rad(75.0f);
const float_t camOffset = 12.0f;
const float_t aspect = (float_t)RENDER_WIDTH / (float_t)RENDER_HEIGHT;
const float_t pixelPerfectOffset = (
tanf((glm_rad(180) - fov) / 2.0f) *
((float_t)RENDER_HEIGHT/ 2.0f)
);
vec3 look = {
OVERWORLD_CAMERA_X,
OVERWORLD_CAMERA_Y,
0.0f
};
vec3 eye = {
look[0],
look[1] + camOffset,
look[2] + pixelPerfectOffset
};
vec3 up = { 0.0f, 1.0f, 0.0f };
mat4 proj;
glm_perspective(fov, aspect, 0.1f, 1000.0f, proj);
// Flips rendering on the Y axis, so that it is still right-down even in 3D;
proj[1][1] = -proj[1][1];
mat4 view;
glm_lookat(eye, look, up, view);
mat4 pv;
glm_mat4_mul(proj, view, pv);
glLoadMatrixf((const GLfloat*)pv);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void cameraOverworldPop(void) {
glPopMatrix();
}

View File

@@ -1,39 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusksdl2.h"
/**
* Pushes the UI camera matrix onto the stack.
*/
void cameraUIPush(void);
/**
* Pops the UI camera matrix from the stack.
*/
void cameraUIPop(void);
/**
* Pushes the screen space camera matrix onto the stack.
*/
void cameraScreenPush(void);
/**
* Pops the screen space camera matrix.
*/
void cameraScreenPop(void);
/**
* Pushes the overworld camera matrix onto the stack.
*/
void cameraOverworldPush(void);
/**
* Pops the overworld camera matrix.
*/
void cameraOverworldPop(void);

View File

@@ -1,59 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "framebuffer.h"
#include "assert/assert.h"
#include "util/memory.h"
#if RENDER_USE_FRAMEBUFFER
void frameBufferInit(
framebuffer_t *framebuffer,
const uint32_t width,
const uint32_t height
) {
assertNotNull(framebuffer, "Framebuffer cannot be NULL");
assertTrue(width > 0 && height > 0, "Width & height must be greater than 0");
memoryZero(framebuffer, sizeof(framebuffer_t));
textureInit(&framebuffer->texture, width, height, GL_RGBA, NULL);
// Generate the framebuffer object using EXT
glGenFramebuffersEXT(1, &framebuffer->id);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer->id);
// Attach the texture to the framebuffer
glFramebufferTexture2DEXT(
GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
GL_TEXTURE_2D, framebuffer->texture.id, 0
);
// Check if the framebuffer is complete
if(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT) {
assertUnreachable("Framebuffer is not complete");
}
// Unbind the framebuffer
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
}
void frameBufferBind(const framebuffer_t *framebuffer) {
if(framebuffer == NULL) {
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
return;
}
// Bind the framebuffer for rendering
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer->id);
}
void frameBufferDispose(framebuffer_t *framebuffer) {
assertNotNull(framebuffer, "Framebuffer cannot be NULL");
glDeleteFramebuffersEXT(1, &framebuffer->id);
textureDispose(&framebuffer->texture);
}
#endif

View File

@@ -1,45 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "display/render.h"
#include "display/texture/texture.h"
#if RENDER_USE_FRAMEBUFFER
typedef struct {
GLuint id;
texture_t texture;
} framebuffer_t;
/**
* Initializes a framebuffer using EXT methods.
*
* @param framebuffer The framebuffer to initialize.
* @param width The width of the framebuffer.
* @param height The height of the framebuffer.
* @return An error code indicating success or failure.
*/
void frameBufferInit(
framebuffer_t *framebuffer,
const uint32_t width,
const uint32_t height
);
/**
* Binds the framebuffer for rendering using EXT methods.
*
* @param framebuffer The framebuffer to bind, or NULL to unbind.
*/
void frameBufferBind(const framebuffer_t *framebuffer);
/**
* Disposes of the framebuffer using EXT methods.
*
* @param framebuffer The framebuffer to dispose of.
*/
void frameBufferDispose(framebuffer_t *framebuffer);
#endif

View File

@@ -1,82 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "mesh.h"
#include "util/memory.h"
#include "assert/assert.h"
#include "console/console.h"
void meshInit(
mesh_t *mesh,
const GLenum primitiveType,
const int32_t vertexCount,
const meshvertex_t *vertices
) {
assertNotNull(mesh, "Mesh cannot be NULL");
assertNotNull(vertices, "Vertices cannot be NULL");
assertTrue(vertexCount > 0, "Vertex count must be greater than 0");
memoryZero(mesh, sizeof(mesh_t));
mesh->primitiveType = primitiveType;
mesh->vertexCount = vertexCount;
mesh->vertices = vertices;
}
void meshDraw(
const mesh_t *mesh,
const int32_t vertexOffset,
const int32_t vertexCount
) {
const int32_t offset = vertexOffset == -1 ? 0 : vertexOffset;
const int32_t count = vertexCount == -1 ? mesh->vertexCount : vertexCount;
assertNotNull(mesh, "Mesh cannot be NULL");
assertTrue(offset >= 0, "Vertex offset must be non-negative");
assertTrue(count >= 0, "Vertex count must be non-negative");
assertTrue(offset + count <= mesh->vertexCount,
"Vertex offset + count must not exceed vertex count"
);
#if 1
// PSP style pointer legacy OpenGL
const GLsizei stride = sizeof(meshvertex_t);
glColorPointer(
MESH_VERTEX_COLOR_SIZE,
GL_UNSIGNED_BYTE,
stride,
(const GLvoid*)&mesh->vertices[offset].color[0]
);
glTexCoordPointer(
MESH_VERTEX_UV_SIZE,
GL_FLOAT,
stride,
(const GLvoid*)&mesh->vertices[offset].uv[0]
);
glVertexPointer(
MESH_VERTEX_POS_SIZE,
GL_FLOAT,
stride,
(const GLvoid*)&mesh->vertices[offset].pos[0]
);
glDrawArrays(
mesh->primitiveType,
0,
count
);
#else
#error "Need to support modern OpenGL with VAOs and VBOs"
#endif
}
void meshDispose(mesh_t *mesh) {
assertNotNull(mesh, "Mesh cannot be NULL");
memoryZero(mesh, sizeof(mesh_t));
}

View File

@@ -1,58 +0,0 @@
// Copyright (c) 2025 Dominic Masters
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
#pragma once
#include "dusksdl2.h"
#define MESH_VERTEX_COLOR_SIZE 4
#define MESH_VERTEX_UV_SIZE 2
#define MESH_VERTEX_POS_SIZE 3
typedef struct {
GLubyte color[MESH_VERTEX_COLOR_SIZE];
GLfloat uv[MESH_VERTEX_UV_SIZE];
GLfloat pos[MESH_VERTEX_POS_SIZE];
} meshvertex_t;
typedef struct {
const meshvertex_t *vertices;
int32_t vertexCount;
GLenum primitiveType;
} mesh_t;
/**
* Initializes a mesh.
*
* @param mesh The mesh to initialize.
* @param primitiveType The OpenGL primitive type (e.g., GL_TRIANGLES).
* @param vertexCount The number of vertices in the mesh.
* @param vertices The vertex data for the mesh.
*/
void meshInit(
mesh_t *mesh,
const GLenum primitiveType,
const int32_t vertexCount,
const meshvertex_t *vertices
);
/**
* Draws a mesh.
*
* @param mesh The mesh to draw.
* @param vertexOffset The offset in the vertex array to start drawing from.
* @param vertexCount The number of vertices to draw. If -1, draws all vertices.
*/
void meshDraw(
const mesh_t *mesh,
const int32_t vertexOffset,
const int32_t vertexCount
);
/**
* Disposes a mesh.
*
* @param mesh The mesh to dispose.
*/
void meshDispose(mesh_t *mesh);

View File

@@ -1,62 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "quad.h"
#include "assert/assert.h"
void quadBuffer(
meshvertex_t *vertices,
const float_t minX,
const float_t minY,
const float_t maxX,
const float_t maxY,
const uint8_t r,
const uint8_t g,
const uint8_t b,
const uint8_t a,
const float_t u0,
const float_t v0,
const float_t u1,
const float_t v1
) {
const float_t z = 0.0f; // Z coordinate for 2D rendering
assertNotNull(vertices, "Vertices cannot be NULL");
// First triangle
vertices[0] = (meshvertex_t) {
{ r, g, b, a }, // Color
{ u0, v0 }, // UV
{ minX, minY, z } // Position
};
vertices[1] = (meshvertex_t) {
{ r, g, b, a }, // Color
{ u1, v0 }, // UV
{ maxX, minY, z } // Position
};
vertices[2] = (meshvertex_t) {
{ r, g, b, a }, // Color
{ u1, v1 }, // UV
{ maxX, maxY, z } // Position
};
// Second triangle
vertices[3] = (meshvertex_t) {
{ r, g, b, a }, // Color
{ u0, v0 }, // UV
{ minX, minY, z } // Position
};
vertices[4] = (meshvertex_t) {
{ r, g, b, a }, // Color
{ u1, v1 }, // UV
{ maxX, maxY, z } // Position
};
vertices[5] = (meshvertex_t) {
{ r, g, b, a }, // Color
{ u0, v1 }, // UV
{ minX, maxY, z } // Position
};
}

View File

@@ -1,44 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "mesh.h"
#define QUAD_VERTEX_COUNT 6
/**
* Buffers a quad into the provided vertex array.
*
* @param vertices The vertex array to buffer into.
* @param minX The minimum X coordinate of the quad.
* @param minY The minimum Y coordinate of the quad.
* @param maxX The maximum X coordinate of the quad.
* @param maxY The maximum Y coordinate of the quad.
* @param r The red color component (0-255).
* @param g The green color component (0-255).
* @param b The blue color component (0-255).
* @param a The alpha color component (0-255).
* @param u0 The U texture coordinate for the first vertex.
* @param v0 The V texture coordinate for the first vertex.
* @param u1 The U texture coordinate for the second vertex.
* @param v1 The V texture coordinate for the second vertex.
*/
void quadBuffer(
meshvertex_t *vertices,
const float_t minX,
const float_t minY,
const float_t maxX,
const float_t maxY,
const uint8_t r,
const uint8_t g,
const uint8_t b,
const uint8_t a,
const float_t u0,
const float_t v0,
const float_t u1,
const float_t v1
);

View File

@@ -1,10 +0,0 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DUSK_TARGET_NAME}
PRIVATE
renderoverworld.c
)

View File

@@ -1,125 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "renderoverworld.h"
#include "util/memory.h"
#include "assert/assert.h"
#include "display/camera/camera.h"
#include "entity/entity.h"
#include "display/spritebatch/spritebatch.h"
renderoverworld_t RENDER_OVERWORLD;
void renderOverworldInit(void) {
memoryZero(&RENDER_OVERWORLD, sizeof(RENDER_OVERWORLD));
for(uint8_t i = 0; i < CHUNK_MAP_COUNT; i++) {
renderchunk_t *chunk = &RENDER_OVERWORLD.chunks[i];
meshInit(
&chunk->meshBase,
GL_TRIANGLES,
CHUNK_TILE_COUNT * QUAD_VERTEX_COUNT,
chunk->verticesBase
);
meshInit(
&chunk->meshBaseOverlay,
GL_TRIANGLES,
CHUNK_TILE_COUNT,
chunk->verticesBaseOverlay
);
}
}
void renderOverworldDraw(void) {
cameraOverworldPush();
for(uint8_t i = 0; i < CHUNK_MAP_COUNT; i++) {
renderchunk_t *chunk = &RENDER_OVERWORLD.chunks[i];
meshDraw(&chunk->meshBase, -1, -1);
}
for(uint8_t i = 0; i < ENTITY_COUNT_MAX; i++) {
entity_t *entity = &ENTITIES[i];
if(entity->type == ENTITY_TYPE_NULL) continue;
float_t x = (entity->x * TILE_WIDTH_HEIGHT) + entity->subX;
float_t y = (entity->y * TILE_WIDTH_HEIGHT) + entity->subY;
// Draw the entity
spriteBatchPush(
NULL,
x, y,
x + TILE_WIDTH_HEIGHT, y + TILE_WIDTH_HEIGHT,
0xFF, 0x00, 0xFF, 0XFF,
0.0f, 0.0f, 1.0f, 1.0f
);
}
spriteBatchFlush();
cameraOverworldPop();
}
void renderChunkUpdated(chunk_t *chunk) {
uint8_t r, g, b;
assertNotNull(chunk, "Chunk pointer is null");
int32_t chunkIndex = chunk - CHUNK_MAP.chunks;
assertTrue(
chunkIndex >= 0 && chunkIndex < CHUNK_MAP_COUNT,
"Chunk index out of bounds"
);
for(uint32_t i = 0; i < CHUNK_TILE_COUNT; i++) {
tile_t base = chunk->tilesBase[i];
tile_t overlay = chunk->tilesBaseOverlay[i];
float_t posX = (i % CHUNK_WIDTH) + (chunk->x * CHUNK_WIDTH);
float_t posY = (i / CHUNK_WIDTH) + (chunk->y * CHUNK_HEIGHT);
switch(base) {
case 0:
r = 0; g = 0; b = 0; // Black for empty
break;
case 1:
r = 34; g = 139; b = 34; // Forest Green
break;
case 2:
r = 0; g = 191; b = 255; // Deep Sky Blue
break;
case 3:
r = 139; g = 69; b = 19; // Saddle Brown
break;
case 4:
r = 255; g = 255; b = 0; // Yellow
break;
default:
r = 255; g = 20; b = 147; // Pink for unknown
break;
}
quadBuffer(
&RENDER_OVERWORLD.chunks[chunkIndex].verticesBase[i * QUAD_VERTEX_COUNT],
posX * TILE_WIDTH_HEIGHT,
posY * TILE_WIDTH_HEIGHT,
(posX + 1) * TILE_WIDTH_HEIGHT,
(posY + 1) * TILE_WIDTH_HEIGHT,
r, g, b, 255,
0, 0, 1, 1
);
}
}
void renderOverworldDispose(void) {
// Clean up overworld rendering resources here
for(uint8_t i = 0; i < CHUNK_MAP_COUNT; i++) {
renderchunk_t *chunk = &RENDER_OVERWORLD.chunks[i];
meshDispose(&chunk->meshBase);
meshDispose(&chunk->meshBaseOverlay);
}
}

View File

@@ -1,39 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "world/chunk.h"
#include "display/mesh/quad.h"
typedef struct {
mesh_t meshBase;
meshvertex_t verticesBase[CHUNK_TILE_COUNT * QUAD_VERTEX_COUNT];
mesh_t meshBaseOverlay;
meshvertex_t verticesBaseOverlay[CHUNK_TILE_COUNT];
} renderchunk_t;
typedef struct {
renderchunk_t chunks[CHUNK_MAP_COUNT];
} renderoverworld_t;
extern renderoverworld_t RENDER_OVERWORLD;
/**
* Initializes the render overworld.
*/
void renderOverworldInit(void);
/**
* Draws the render overworld.
*/
void renderOverworldDraw(void);
/**
* Disposes of the render overworld.
*/
void renderOverworldDispose(void);

View File

@@ -1,119 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "dusksdl2input.h"
#include "render.h"
#include "renderbackbuffer.h"
#include "display/scene/renderscene.h"
#include "display/spritebatch/spritebatch.h"
#include "display/ui/renderui.h"
SDL_Window *RENDER_WINDOW;
SDL_GLContext RENDER_GL_CONTEXT;
bool_t RENDER_RUNNING;
errorret_t renderInit(void) {
// Init SDL
uint32_t flags = SDL_INIT_VIDEO;
#if INPUT_SUPPORT_GAMEPAD
flags |= SDL_INIT_GAMECONTROLLER;
#endif
if(SDL_Init(flags) != 0) {
errorThrow(
"SDL Failed to Initialize: %s",
SDL_GetError()
);
}
// Set OpenGL attributes (Needs to be done now or later?)
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
// Create window with OpenGL flag.
RENDER_WINDOW = SDL_CreateWindow(
"DuskSDL2",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
RENDER_WINDOW_WIDTH_DEFAULT,
RENDER_WINDOW_HEIGHT_DEFAULT,
SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI |
SDL_WINDOW_OPENGL
);
if(!RENDER_WINDOW) {
errorThrow("SDL_CreateWindow failed: %s", SDL_GetError());
}
// Create OpenGL context
RENDER_GL_CONTEXT = SDL_GL_CreateContext(RENDER_WINDOW);
if(!RENDER_GL_CONTEXT) {
errorThrow("SDL_GL_CreateContext failed: %s", SDL_GetError());
}
SDL_GL_SetSwapInterval(1);
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glDisable(GL_LIGHTING);// PSP defaults this on?
glShadeModel(GL_SMOOTH); // Fixes color on PSP?
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glEnableClientState(GL_COLOR_ARRAY);// To confirm: every frame on PSP?
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
spriteBatchInit();
renderBackBufferInit();
renderSceneInit();
renderUIInit();
RENDER_RUNNING = true;
errorOk();
}
errorret_t renderDraw(void) {
SDL_Event event;
while(SDL_PollEvent(&event)) {
switch(event.type) {
case SDL_QUIT:
RENDER_RUNNING = false;
break;
default:
break;
}
}
// Reset the state
spriteBatchClear();
renderBackBufferBind();
renderSceneDraw();
renderUIDraw();
// Finish rendering, now render back buffer.
renderBackBufferUnbind();
renderBackBufferDraw();
textureBind(NULL);
SDL_GL_SwapWindow(RENDER_WINDOW);
errorOk();
}
errorret_t renderDispose(void) {
renderUIDispose();
renderSceneDispose();
renderBackBufferDispose();
spriteBatchDispose();
// Destroy OpenGL context
SDL_GL_DeleteContext(RENDER_GL_CONTEXT);
SDL_DestroyWindow(RENDER_WINDOW);
SDL_Quit();
errorOk();
}

View File

@@ -1,27 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusksdl2.h"
#include "display/renderbase.h"
#ifndef RENDER_WINDOW_WIDTH_DEFAULT
#define RENDER_WINDOW_WIDTH_DEFAULT RENDER_WIDTH * 3
#endif
#ifndef RENDER_WINDOW_HEIGHT_DEFAULT
#define RENDER_WINDOW_HEIGHT_DEFAULT RENDER_HEIGHT * 3
#endif
#if RENDER_WIDTH == RENDER_WINDOW_WIDTH_DEFAULT && RENDER_HEIGHT == RENDER_WINDOW_HEIGHT_DEFAULT
#define RENDER_USE_FRAMEBUFFER 0
#else
#define RENDER_USE_FRAMEBUFFER 1
#endif
extern SDL_Window *RENDER_WINDOW;
extern SDL_Renderer *RENDER_RENDERER;
extern bool_t RENDER_RUNNING;

View File

@@ -1,93 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "renderbackbuffer.h"
#include "render.h"
#include "display/spritebatch/spritebatch.h"
#include "display/camera/camera.h"
#if RENDER_USE_FRAMEBUFFER
framebuffer_t RENDER_BACKBUFFER;
#endif
errorret_t renderBackBufferInit(void) {
#if RENDER_USE_FRAMEBUFFER
frameBufferInit(
&RENDER_BACKBUFFER,
RENDER_WIDTH,
RENDER_HEIGHT
);
#else
// No back buffer needed for window rendering
#endif
errorOk();
}
void renderBackBufferBind(void) {
#if RENDER_USE_FRAMEBUFFER
frameBufferBind(&RENDER_BACKBUFFER);
#endif
// Fill background with cornflower blue.
glClearColor(0.392f, 0.584f, 0.929f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
void renderBackBufferUnbind(void) {
#if RENDER_USE_FRAMEBUFFER
frameBufferBind(NULL);
#endif
}
void renderBackBufferDraw(void) {
#if RENDER_USE_FRAMEBUFFER
int32_t windowWidth, windowHeight;
SDL_GetWindowSize(RENDER_WINDOW, &windowWidth, &windowHeight);
// Set viewport to match window size
cameraScreenPush();
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Create a quad that is scaled to fit but maintain original aspect ratio
int32_t renderWidth, renderHeight, renderX, renderY;
if(RENDER_WIDTH * windowHeight > RENDER_HEIGHT * windowWidth) {
renderWidth = windowWidth;
renderHeight = (RENDER_HEIGHT * windowWidth) / RENDER_WIDTH;
renderX = 0;
renderY = (windowHeight - renderHeight) / 2;
} else {
renderWidth = (RENDER_WIDTH * windowHeight) / RENDER_HEIGHT;
renderHeight = windowHeight;
renderX = (windowWidth - renderWidth) / 2;
renderY = 0;
}
// Draw the back buffer texture
spriteBatchClear();
spriteBatchPush(
&RENDER_BACKBUFFER.texture,
renderX, renderY,
renderX+renderWidth, renderY+renderHeight,
0xFF, 0xFF, 0xFF, 0xFF,
0, 1, 1, 0
);
spriteBatchFlush();
cameraScreenPop();
#else
// No back buffer to draw
#endif
}
errorret_t renderBackBufferDispose(void) {
#if RENDER_USE_FRAMEBUFFER
frameBufferDispose(&RENDER_BACKBUFFER);
#endif
errorOk();
}

View File

@@ -1,42 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "display/renderbase.h"
#include "display/framebuffer/framebuffer.h"
#if RENDER_USE_FRAMEBUFFER
extern framebuffer_t RENDER_BACKBUFFER;
#endif
/**
* Initializes the render back buffer. May be either a framebuffer or a texture
* depending on the render settings.
*/
errorret_t renderBackBufferInit(void);
/**
* Binds the render back buffer as the current render target.
*/
void renderBackBufferBind(void);
/**
* Unbinds the render back buffer, returning to the default render target.
*/
void renderBackBufferUnbind(void);
/**
* Draws the render back buffer to the screen, scaling it to fit the window.
*/
void renderBackBufferDraw(void);
/**
* Disposes of the render back buffer, freeing any resources it holds.
*
* @return An error state if an error occurred, otherwise OK.
*/
errorret_t renderBackBufferDispose(void);

View File

@@ -1,42 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "renderscene.h"
#include "display/overworld/renderoverworld.h"
renderscenecallback_t RENDER_SCENE_CALLBACKS[SCENE_COUNT] = {
[SCENE_INITIAL] = {
.init = NULL,
.draw = NULL,
.dispose = NULL
},
[SCENE_OVERWORLD] = {
.init = renderOverworldInit,
.draw = renderOverworldDraw,
.dispose = renderOverworldDispose
},
};
void renderSceneInit(void) {
for(int32_t i = 0; i < SCENE_COUNT; i++) {
if(!RENDER_SCENE_CALLBACKS[i].init) continue;
RENDER_SCENE_CALLBACKS[i].init();
}
}
void renderSceneDraw(void) {
if(!RENDER_SCENE_CALLBACKS[SCENE_CURRENT].draw) return;
RENDER_SCENE_CALLBACKS[SCENE_CURRENT].draw();
}
void renderSceneDispose(void) {
for(int32_t i = 0; i < SCENE_COUNT; i++) {
if(!RENDER_SCENE_CALLBACKS[i].dispose) continue;
RENDER_SCENE_CALLBACKS[i].dispose();
}
}

Some files were not shown because too many files have changed in this diff Show More