645 Commits

Author SHA1 Message Date
YourWishes 88903fee94 No need for asset batching on text.c 2026-06-01 22:36:02 -05:00
YourWishes 1e8311fc04 Add asset batch 2026-06-01 22:34:44 -05:00
YourWishes 2b78370cb8 Add asset reaping 2026-06-01 22:20:57 -05:00
YourWishes 8f78bba9e9 Restoring JerryScript a bit cleaner 2026-06-01 21:52:36 -05:00
YourWishes 41a4be678e Added a tiny sleep on assets to stop pegging the CPU 2026-06-01 15:48:10 -05:00
YourWishes 8b2b4b7c3d Fixed JSON loader, added some tests 2026-06-01 15:31:22 -05:00
YourWishes 1f3a29f89d Asyncify other loaders 2026-06-01 15:10:58 -05:00
YourWishes c4c93097cd Add async texture loading 2026-06-01 14:53:18 -05:00
YourWishes eedb7769e6 Add some extra tests 2026-06-01 13:48:29 -05:00
YourWishes 98db62a4bc Add some more tests, prepping for asset testing 2026-06-01 13:37:14 -05:00
YourWishes df48c8e500 Consistency and fixing thread unit tests 2026-06-01 11:33:27 -05:00
YourWishes db9cc0f4c6 Add thread tests 2026-06-01 10:59:56 -05:00
YourWishes a79ee429b4 Prepping for async 2026-06-01 10:57:40 -05:00
YourWishes 6acfca6d48 Consistent 2026-05-30 20:30:13 -05:00
YourWishes 1cd6f4cb72 First refactor of new asset system 2026-05-30 08:21:58 -05:00
YourWishes 3271e8c7d6 FInished porting last asset loader types 2026-05-30 07:59:06 -05:00
YourWishes 0bcde064af Asset refactor, phase one. 2026-05-29 14:27:40 -05:00
YourWishes 957980b3c5 Updating event handler 2026-05-28 14:22:13 -05:00
YourWishes 03eb328d81 Allow dynamic trace on any platform that can support it. 2026-05-28 11:21:36 -05:00
YourWishes e1716a741f Trigger test 2026-05-26 22:18:41 -05:00
YourWishes e24707c847 Scene loading example 2026-05-26 21:42:37 -05:00
YourWishes 7c4b8c307f Fix flocking bug 2026-05-26 20:24:34 -05:00
YourWishes 109318aeaf Remove useless void checks 2026-05-26 19:24:17 -05:00
YourWishes 1f2657cea0 Spritebatch cleanup 2026-05-26 19:07:07 -05:00
YourWishes 382c435bac Entity refactoring 2026-05-22 23:01:45 -05:00
YourWishes 130fe4ca5d Delete JS assets 2026-05-22 00:00:23 -05:00
YourWishes 31ba3fe127 add build to corner of screen 2026-05-21 23:59:26 -05:00
YourWishes f68b31158f Asset refactor 2026-05-21 23:42:56 -05:00
YourWishes 653ca9a72d PSP rendering fix 2026-05-21 22:07:56 -05:00
YourWishes ba7857f4df Fix rendering 2026-05-21 18:24:18 -05:00
YourWishes 23e617ea21 Optimizing entityposition as much as possible. 2026-05-21 13:17:48 -05:00
YourWishes cdf5a5229c Refactor cleaned a few things 2026-05-21 12:52:23 -05:00
YourWishes f841a35a53 Revert "Disable old ent code"
This reverts commit efd31237be.
2026-05-21 11:07:21 -05:00
YourWishes efd31237be Disable old ent code 2026-05-21 10:18:20 -05:00
YourWishes 6502822583 Update render, spritebatch and input stuffs. 2026-05-21 09:51:56 -05:00
YourWishes a9e6f2b2a5 Scene rendering native. 2026-05-20 23:45:27 -05:00
YourWishes 510a94b42c Remove Jerryscript further 2026-05-20 21:34:00 -05:00
YourWishes d805be47ce No script 2026-05-20 20:58:56 -05:00
YourWishes f9ea8e380a Temporarily disable save code 2026-05-20 09:52:07 -05:00
YourWishes 5cb05beb30 Fix dolphin compile 2026-05-19 23:24:27 -05:00
YourWishes 677768e6ab Map Base 2026-05-19 23:13:41 -05:00
YourWishes ed6c951783 Script improvements 2026-05-17 23:40:42 -05:00
YourWishes 54254348b8 Add parent/child 2026-05-17 21:46:08 -05:00
YourWishes 782fd07a8d Savestream update 2026-05-16 17:51:00 -05:00
YourWishes a8fd55cb38 Save file update (incomplete) 2026-05-10 11:20:09 -05:00
YourWishes d7f515575a Working on burned DVD for gamecube 2026-05-09 00:14:28 -05:00
YourWishes bafbf2ec2f Fix compile error 2026-05-08 23:11:20 -05:00
YourWishes 7415944e0a scripting improvements 2026-05-08 22:46:24 -05:00
YourWishes 1ff990ff44 Add strided memory pushing and improved spritebatching 2026-05-08 20:53:05 -05:00
YourWishes 73e73d8772 Cleanup animation 2026-05-08 15:44:55 -05:00
YourWishes 6d876bb767 Anim tweak 2026-05-07 19:37:30 -05:00
YourWishes 2be0fe9f06 Fix wii build 2026-05-07 17:54:10 -05:00
YourWishes e1fb082927 Increase spritebatch flushing count 2026-05-07 17:47:39 -05:00
YourWishes 65ca5ae4c4 Dolphin Bootable ISO working! 2026-05-07 17:38:41 -05:00
YourWishes 2cea43dc70 Switch to ogc2 2026-05-07 17:21:52 -05:00
YourWishes 44a0700800 Fixed memalign again 2026-05-07 14:11:59 -05:00
YourWishes 1613a378f1 Added memalign 2026-05-07 12:39:07 -05:00
YourWishes deed98a27d 2026-05-07 12:31:22 -05:00
YourWishes 9d0cb8fb46 ISO build (partial) 2026-05-07 12:18:30 -05:00
YourWishes d8fe0f6923 textbox 2026-05-06 22:42:28 -05:00
YourWishes 581dbc2b3c Update linux docker 2026-05-06 20:31:50 -05:00
YourWishes 7301d2ad76 luce bree 2026-05-06 20:24:16 -05:00
YourWishes 3232a14d1d Consistent build 2026-05-06 14:40:06 -05:00
YourWishes 84c1f88d42 Fix PSP blending issues 2026-05-06 11:17:34 -05:00
YourWishes 3695b10e4b Easing test 2026-05-05 22:24:25 -05:00
YourWishes 6da02b25fa Testing cutscenes 2026-05-05 22:10:47 -05:00
YourWishes 3bc544fba1 Re-enable build pulling fetched modules 2026-05-05 19:29:48 -05:00
YourWishes 368d370f49 Cleanup modules 2026-05-05 19:29:29 -05:00
YourWishes bb29c0edef Working on some script modules 2026-05-05 16:22:04 -05:00
YourWishes 6edcf75a0c add display state 2026-05-04 22:16:30 -05:00
YourWishes 31cc186424 Fixed small compile bugs 2026-05-04 08:39:47 -05:00
YourWishes 0e94c1fa6d Fixed a bunch of messy over 80 char lines 2026-05-04 08:29:43 -05:00
YourWishes 6d9e2dd3e1 UI first pass 2026-05-03 21:52:12 -05:00
YourWishes 4a4adeb3c8 Finally fixed linux asset weirdness 2026-05-02 15:18:49 -05:00
YourWishes ff77f8cfa0 Fix dolphin rendering 2026-05-01 23:11:59 -05:00
YourWishes 36db89c36e Nuke the old input system, use the new UI system 2026-05-01 15:21:46 -05:00
YourWishes a9948142ad Fix linux warning 2026-05-01 14:00:24 -05:00
YourWishes 8d05510584 Fix linux building 2026-05-01 13:58:05 -05:00
YourWishes d373de7a29 Some adjustments 2026-05-01 13:44:51 -05:00
YourWishes 1efa9a9f7b More cleanup 2026-05-01 09:43:50 -05:00
YourWishes 0fb3ba2f91 Cleanup, prepping for example game stuff 2026-04-30 23:43:49 -05:00
YourWishes 3b4c5b5153 Added FPS meter 2026-04-30 23:34:32 -05:00
YourWishes 9293aeeec8 Fix position 2026-04-30 23:18:36 -05:00
YourWishes 03ae83b119 More cleanup? 2026-04-30 23:07:17 -05:00
YourWishes abd63cc6cf More cleanup 2026-04-30 22:40:32 -05:00
YourWishes 2e43aa2c44 Bit more cleanup 2026-04-30 20:03:44 -05:00
YourWishes 3d984e13c2 Module input improvements 2026-04-29 23:40:01 -05:00
YourWishes 010900fe21 Better again. 2026-04-29 23:26:21 -05:00
YourWishes ffed626447 More cleanup 2026-04-29 22:39:47 -05:00
YourWishes 61f69af35a Refactor pass 1 2026-04-29 14:53:35 -05:00
YourWishes bd248ee91c Build script on PSP, Dolphin and Engine. 2026-04-28 21:34:09 -05:00
YourWishes 194255bffe Fix merge conflcits 2026-04-28 14:02:59 -05:00
YourWishes 52ee627079 Merge branch 'jerryscript' into playertest 2026-04-28 14:02:53 -05:00
YourWishes bd4200e707 Finished getting JerryScript on all the platforms. 2026-04-28 13:59:46 -05:00
YourWishes 73e7d6c7f3 Add epoch 2026-04-28 10:33:23 -05:00
YourWishes a41b0e916b prog 2026-04-28 08:04:01 -05:00
YourWishes 19f2a2c616 Bit more consistent but still far from perfect 2026-04-27 09:14:14 -05:00
YourWishes 998601f722 Playertest: scene/script system refactor and Wii ABI fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 23:30:08 -05:00
YourWishes 7c3386cf3e Add entity scripting 2026-04-20 17:01:12 -05:00
YourWishes d161182997 Entity modules 2026-04-20 16:50:16 -05:00
YourWishes 1646dc2dbd Fixed build 2026-04-20 15:43:18 -05:00
YourWishes b640295be2 Scene script 2026-04-20 15:34:24 -05:00
YourWishes b89ae2391b Reg console. 2026-04-20 14:31:22 -05:00
YourWishes a0fad441d0 Updated bind command 2026-04-20 12:59:25 -05:00
YourWishes 340084dac3 Removed console aliases 2026-04-20 12:49:06 -05:00
YourWishes c78135aa09 Fixed bugs with console 2026-04-20 12:05:35 -05:00
YourWishes d19f8bbd30 Restored console, has a bug 2026-04-20 09:26:25 -05:00
YourWishes 4205899f5a No idea why gamecube is crashing, disabling this for now 2026-04-18 21:57:57 -05:00
YourWishes 7dd3940770 Moved code to dolphin for network 2026-04-18 17:41:30 -05:00
YourWishes 00d94e3015 Slight wii improvements 2026-04-18 16:01:53 -05:00
YourWishes 7bacb3ee2b Testing on real wii hardware some more 2026-04-18 15:59:25 -05:00
YourWishes 8e49be5ac4 Testing some wii rendering bugs 2026-04-18 15:29:40 -05:00
YourWishes 3b94598d2c Fixed dolphin matricies the ugly way 2026-04-18 00:36:35 -05:00
YourWishes bddc9af3b6 "Improved" Dolphin matricies slightly 2026-04-18 00:32:50 -05:00
YourWishes 2451d73a7c Improved Wii aspect ratio significantly 2026-04-17 23:49:39 -05:00
YourWishes 1dd2efa182 Dolphin compiles, network untested 2026-04-17 22:53:49 -05:00
YourWishes acea610773 Disable curl on linux 2026-04-17 22:53:29 -05:00
YourWishes 8f2f1fd496 Added network info 2026-04-17 17:00:03 -05:00
YourWishes 39c775872a PSP networking matches linux now. 2026-04-17 16:32:45 -05:00
YourWishes bdb3cbd109 Fixed crash for cross/cancel logic 2026-04-17 16:02:45 -05:00
YourWishes ff84ce2b04 Added PSP Accept/Cance 2026-04-17 15:28:03 -05:00
YourWishes 225f405592 PSP Networking refactor 2026-04-17 14:57:10 -05:00
YourWishes 715ecffa18 Taking a break on net 2026-04-16 06:38:56 -05:00
YourWishes e51cdc8992 PSP net code first pass 2026-04-15 15:50:43 -05:00
YourWishes 133685ea37 Linux HTTP implementation 2026-04-15 15:11:44 -05:00
YourWishes 6aff98d555 Fix PSP build 2026-04-15 06:10:38 -05:00
YourWishes acdc524284 bit more accurate 2026-04-15 06:04:30 -05:00
YourWishes 1ee5ec7b43 Vita builds for the first time 2026-04-15 05:52:30 -05:00
YourWishes 46a5403511 Optimized entity memory. 2026-04-14 14:48:26 -05:00
YourWishes 87bfb92576 Physics position optimization 2026-04-14 14:46:07 -05:00
YourWishes c7a3e5601c De-ugifying 2026-04-14 14:29:48 -05:00
YourWishes 4009130f6e Refactor Physics further 2026-04-14 13:55:48 -05:00
YourWishes c91243f6e9 Physics refactor 2026-04-14 13:45:16 -05:00
YourWishes 0e3871ac26 Reset position 2026-04-14 09:47:17 -05:00
YourWishes 55baafec8a Fixed some of the rendering problems on Dolphin, things still look wrong though. 2026-04-14 09:40:58 -05:00
YourWishes b5a66993ca Phyiscs engine first pass 2026-04-14 09:34:57 -05:00
YourWishes 0b570b5fd6 Add a few more mesh types 2026-04-14 08:38:50 -05:00
YourWishes 378227c377 Fixed more memory tests 2026-04-13 22:55:59 -05:00
YourWishes 650645eaff Fixing memory tests 2026-04-13 22:42:39 -05:00
YourWishes 62c71f3fe6 Remove malloc log 2026-04-13 20:36:48 -05:00
YourWishes 041ec3d710 Add texture padder tool 2026-04-13 20:34:54 -05:00
YourWishes 5f2d871bad Cleaned a bit more 2026-04-13 20:05:34 -05:00
YourWishes a30b151e4d Bit of cleanup 2026-04-13 20:03:02 -05:00
YourWishes 5a651d2d1f Dusk texture creator 2026-04-13 19:51:11 -05:00
YourWishes 4b3826edd9 Cleanup the test mesh 2026-04-13 13:05:39 -05:00
YourWishes bae1ff3759 Allow reaxising mesh 2026-04-13 12:58:54 -05:00
YourWishes fd82486431 Fix dolphin color-less 2026-04-13 12:37:54 -05:00
YourWishes c9cd91cbd8 Make color optional 2026-04-13 12:29:06 -05:00
YourWishes c8abd374fe STL Loader 2026-04-13 11:41:51 -05:00
YourWishes 2b9ee8f721 Entity does not own mesh. 2026-04-13 09:40:40 -05:00
YourWishes d02673e04a 3D OBJ loading 2026-04-10 22:09:01 -05:00
YourWishes f0117b8e6e Renders on PSP but it's inconsistent 2026-04-10 20:59:38 -05:00
YourWishes bb7c41c754 Rotation 2026-04-10 18:47:46 -05:00
YourWishes efa583c154 Fixed Dolphin culling 2026-04-10 18:37:27 -05:00
YourWishes d16ea13c14 Dolphin shader handler 2026-04-10 18:34:58 -05:00
YourWishes 673d8e0a18 Shader material ECS example 2026-04-10 12:48:05 -05:00
YourWishes 37cfdde1ee Mesh component 2026-04-10 10:19:44 -05:00
YourWishes 0778ffb57a ECS POC 2026-04-10 07:31:31 -05:00
YourWishes 42099f7241 ECS Enhancements 2026-04-10 07:09:25 -05:00
YourWishes c52e1d22b7 Basic ECS 2026-04-09 22:07:17 -05:00
YourWishes 0d7b0aadd1 ECS 2026-04-09 11:53:11 -05:00
YourWishes 4cd3355ef1 Fix memory tests 2026-04-04 19:45:29 -05:00
YourWishes 98d70b96d1 Added proper plural support 2026-04-04 15:21:27 -05:00
YourWishes 64735bdf43 Implemented lua locale gettext 2026-04-04 11:32:46 -05:00
YourWishes 7b87347b77 Fixed small bug with parsing plurals 2026-04-04 10:19:07 -05:00
YourWishes b5b29d7061 locale parsing done 2026-04-04 10:11:46 -05:00
YourWishes 9ec21f85a0 Asset moved some code around 2026-04-03 14:41:38 -05:00
YourWishes da1a5a3f1b Asset refactor 2026-04-03 12:56:04 -05:00
YourWishes 0885da8d44 Fixed dynamic updates on scene rendering 2026-03-29 19:08:58 -05:00
YourWishes 8af961c6d3 Fixed knulli rendering 2026-03-29 18:53:42 -05:00
YourWishes ef5febdde3 Fixed dolphin rendering. 2026-03-29 18:42:59 -05:00
YourWishes 6d7fbd3926 Change to square only 2026-03-29 17:58:13 -05:00
YourWishes 2680d373d8 Fixed boot.dol in wii 2026-03-29 16:51:54 -05:00
YourWishes 2b2ddb3cf2 Fixed spritebatch flickering on Dolphin 2026-03-29 16:10:39 -05:00
YourWishes 85ff95296b Fix Linux again 2026-03-29 15:19:15 -05:00
YourWishes 314a2de41a Fixed text on PSP 2026-03-29 14:45:40 -05:00
YourWishes 26fafab47a Fix copy issues 2026-03-29 14:25:10 -05:00
YourWishes e56ff20e2d Attempting to fix PSP alpha textures 2026-03-29 13:38:55 -05:00
YourWishes 55d44f229d Fixed crash on PSP 2026-03-29 10:35:57 -05:00
YourWishes 1c5e50cc4d Test text rendering 2026-03-29 10:15:22 -05:00
YourWishes ea898da6c2 Fix compile 2026-03-28 21:52:52 -05:00
YourWishes dbb7e9f53c Getting shaders working with lua. 2026-03-28 21:50:59 -05:00
YourWishes cbb68a399d Fix compile error 2026-03-28 15:43:38 -05:00
YourWishes 0e794f28b1 Disable paletted textures for now 2026-03-28 15:40:30 -05:00
YourWishes 87d2d9123e Re-implement RGBA textures 2026-03-28 15:21:33 -05:00
YourWishes 6823a4ddb5 Try again again 2026-03-28 11:35:11 -05:00
YourWishes 20a7c70081 Fixiing weird action path missing? 2026-03-28 11:26:25 -05:00
YourWishes 9caa33b3bb Restore all builds 2026-03-28 11:14:15 -05:00
YourWishes 2d7e61460a fix 2026-03-28 11:05:36 -05:00
YourWishes a4b7fb3f44 Try again 2026-03-28 11:04:42 -05:00
YourWishes 70056cf4ca Temp only build knulli
Build Dusk / build-knulli (push) Failing after 21s
2026-03-28 11:02:43 -05:00
YourWishes 5f4ab71ade Add knulli build 2026-03-28 11:02:34 -05:00
YourWishes f3adb3257b Cleanup knulli 2026-03-28 11:00:18 -05:00
YourWishes 438edda7fd Fixed knulli 2026-03-28 10:56:40 -05:00
YourWishes d5b0441e6f Fixed GLES support (partially), PSP still not working 2026-03-28 10:51:50 -05:00
YourWishes 9ba0ceb000 Moved texture setting around 2026-03-28 09:48:24 -05:00
YourWishes 9474a68995 Slightly more accurate, likely going to have to change how paletted textures work 2026-03-27 21:01:29 -05:00
YourWishes 09c35f0aa6 Builds on knulli 2026-03-27 20:48:43 -05:00
YourWishes a2113442cb Builds on knulli 2026-03-27 15:59:26 -05:00
YourWishes d91808487f Allow texture to be NULL.
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
YourWishes 933949cc19 Progress on PSP paletted textures
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
YourWishes 407620387d Test paletted stuff
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
YourWishes 98947dea26 starting textures
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
YourWishes ebff7af9b5 fix
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
YourWishes b23c4b83ae played around with color, will likely stick to textures.
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
YourWishes c0cff40628 Merge pull request 'shader' (#2) from shader into main
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
YourWishes 97513e354c Dolphin shaders
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
YourWishes c277ae7aff DOlphin shader prog 2026-03-22 18:14:56 -05:00
YourWishes e1835e6282 Merge pull request 'Pull shader code into main' (#1) from shader into main
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
YourWishes 5ac21db997 Shaders adapted for Legacy GL
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
YourWishes ca0e9fc3b2 Implement spritebatch properly. 2026-03-22 09:13:42 -05:00
YourWishes 66ebcb1608 shader prog 2026-03-17 17:05:39 -05:00
YourWishes ff92a78dda Shader first pass 2026-03-17 08:42:43 -05:00
YourWishes 7356286fe0 Adjust how deadzones work
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
YourWishes 54e8e68f86 Update build to use checkout v6
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
YourWishes d21cd7f78b Update error and debug logging methods
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
YourWishes 1d7516982a Fixed dolphin input
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
YourWishes c77a11442c Fix input on linux 2026-03-11 07:56:03 -05:00
YourWishes 5bd43a4643 Fix Dolphin crash 2026-03-11 07:27:06 -05:00
YourWishes 9b87dfa1a9 Only exec action on main 2026-03-10 21:59:15 -05:00
YourWishes 2e3173ea40 Enable all jobs.
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
YourWishes 68eac7cf83 Build wii
Build Dusk / build-wii (push) Failing after 14s
2026-03-10 21:48:12 -05:00
YourWishes 6709505630 Build gamecube
Build Dusk / build-gamecube (push) Failing after 16s
2026-03-10 21:43:59 -05:00
YourWishes af6e962a5d Try rename
Build Dusk / build-psp (push) Failing after 13s
2026-03-10 21:39:00 -05:00
YourWishes 18e6bdabaa test 2
Build Dusk / build-psp (push) Failing after 14s
2026-03-10 21:35:50 -05:00
YourWishes 9743942eae Try zip PSP
Build Dusk / build-psp (push) Failing after 17s
2026-03-10 21:33:27 -05:00
YourWishes 23062137a8 Disable tests for now.
Build Dusk / build-linux (push) Failing after 14s
2026-03-10 21:23:08 -05:00
YourWishes 46f7fb5ccd Use v6
Build Dusk / run-tests (push) Failing after 14s
Build Dusk / build-linux (push) Failing after 28s
2026-03-10 21:21:37 -05:00
YourWishes 9c90c49a6b Test build linux
Build Dusk / run-tests (push) Failing after 14s
Build Dusk / build-linux (push) Failing after 28s
2026-03-10 21:19:55 -05:00
YourWishes 4517b63557 Fixed compiling
Build Dusk / run-tests (push) Failing after 15s
2026-03-10 21:11:12 -05:00
YourWishes 58c239f4b4 Fixing tests more.
Build Dusk / run-tests (push) Failing after 14s
2026-03-10 20:53:28 -05:00
YourWishes cc8845ba3e Run linux tests
Build Dusk / run-tests (push) Failing after 14s
2026-03-10 20:51:58 -05:00
YourWishes 6b69ce2901 Try github
Build Dusk / run-tests (push) Failing after 36s
2026-03-10 20:46:29 -05:00
YourWishes 55300ed21c test2
Build Dusk / run-tests (push) Successful in 30s
Build Dusk / build-linux (push) Failing after 6s
2026-03-10 16:48:14 -05:00
YourWishes 7346dd4339 Test
Build Dusk / run-tests (push) Successful in 25s
Build Dusk / build-linux (push) Failing after 7s
2026-03-10 16:45:24 -05:00
YourWishes 2caf3b92ce Try github workspace
Build Dusk / run-tests (push) Successful in 6s
Build Dusk / build-linux (push) Failing after 7s
2026-03-10 16:30:46 -05:00
YourWishes af2cd72a1f Try mount rather than volume
Build Dusk / run-tests (push) Successful in 4s
Build Dusk / build-linux (push) Failing after 4s
2026-03-10 16:30:08 -05:00
YourWishes 3d455ec1f8 Remove volume
Build Dusk / run-tests (push) Failing after 3s
Build Dusk / build-linux (push) Failing after 3s
2026-03-10 16:26:29 -05:00
YourWishes 15982d7735 Try realpath over pwd
Build Dusk / run-tests (push) Failing after 4s
Build Dusk / build-linux (push) Failing after 6s
2026-03-10 16:24:41 -05:00
YourWishes 5ae3542bd9 where am I?
Build Dusk / run-tests (push) Failing after 4s
Build Dusk / build-linux (push) Failing after 5s
2026-03-10 16:23:32 -05:00
YourWishes b1b02ae24b Test lsla
Build Dusk / run-tests (push) Successful in 4s
Build Dusk / build-linux (push) Failing after 4s
2026-03-10 16:21:31 -05:00
YourWishes f0964e2c92 Test runner
Build Dusk / run-tests (push) Failing after 2m18s
Build Dusk / build-linux (push) Failing after 6s
2026-03-10 16:16:53 -05:00
YourWishes e9661d2998 ADd check
Build Dusk / run-tests (push) Failing after 2m21s
2026-03-10 16:01:44 -05:00
YourWishes ea6468f2a9 Use git runner temp
Build Dusk / run-tests (push) Failing after 29s
2026-03-10 15:36:28 -05:00
YourWishes a2b38d3b83 Test Docker user
Build Dusk / run-tests (push) Failing after 25s
2026-03-10 15:34:53 -05:00
YourWishes d67ef02941 Use script system
Build Dusk / run-tests (push) Successful in 1m15s
Build Dusk / build-linux (push) Successful in 1m6s
2026-03-10 15:15:31 -05:00
YourWishes 549ebe25d8 Let's get it building on linux in gitea
Build Dusk / run-tests (push) Successful in 2m16s
Build Dusk / build-linux (push) Successful in 1m23s
2026-03-10 15:10:18 -05:00
YourWishes 9a98348582 Renders on Dolphin also. 2026-03-10 15:07:50 -05:00
YourWishes c5f5b025a6 Game no longer crashes on Dolphin 2026-03-09 08:05:26 -05:00
YourWishes 23eaffa3a7 Fix some dolphin stuff. 2026-03-08 19:55:48 -05:00
YourWishes c161809248 Renders on PSP identically. 2026-03-08 19:51:00 -05:00
YourWishes 4bf26dc818 Let's get this rendering on PSP and Dolphin. 2026-03-08 15:46:38 -05:00
YourWishes 5dd22fad6c Fixed some bugs. 2026-03-08 13:55:11 -05:00
YourWishes 2c3fdf7803 Add compile time endianess 2026-03-08 13:44:52 -05:00
YourWishes e984b9f5d7 Asset compartmentalized 2026-03-08 13:29:40 -05:00
YourWishes a3c2e37b17 Fixed errors 2026-03-08 12:01:22 -05:00
YourWishes edf1b5a0a3 Technically working 2026-03-08 11:35:21 -05:00
YourWishes 8efdf59ebd More fixes 2026-03-08 10:20:55 -05:00
YourWishes 5c4537b2fa input prog 2026-03-07 22:11:11 -06:00
YourWishes 71e6079054 More code moving 2026-03-07 12:09:40 -06:00
YourWishes dd048d9b0d Moved a bunch of code around 2026-03-07 09:35:56 -06:00
YourWishes 93074d653e idk 2026-03-06 16:34:45 -06:00
YourWishes 9139c4350a Moved all files. 2026-03-06 14:01:21 -06:00
YourWishes 38ce768168 kms 2026-03-06 13:40:27 -06:00
YourWishes 82b3dc576c remove un-needed files 2026-03-03 12:29:04 -06:00
YourWishes 2167889f48 Merge branch 'main' into break-literally-everything 2026-03-03 12:28:48 -06:00
YourWishes e9b02c2acf Whatewver
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
YourWishes 9ee446431b Moved build stuff to docker 2026-03-02 06:59:51 -06:00
YourWishes df106e3988 "progress" 2026-02-28 09:55:21 -06:00
YourWishes d0a057e0ee Moved all defs into main file. 2026-02-17 11:33:00 -06:00
YourWishes 8b49902bf6 Moved some files around 2026-02-17 10:59:21 -06:00
YourWishes 71c1e56564 Fix endian again
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
YourWishes 1b12e67de2 Use internal endian tool
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
YourWishes 291bb4bb81 Build?
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
YourWishes 342ddb19f8 Finally got text rendering again. 2026-02-16 13:12:12 -06:00
YourWishes 9c9d2d548e Fixed whatever problem was with texture loading. 2026-02-16 12:29:25 -06:00
YourWishes d7a0bb4509 Fix palette indexer bytes 2026-02-16 12:01:06 -06:00
YourWishes 2b1a3323a8 Tileset creator done 2026-02-16 12:00:55 -06:00
YourWishes 99d030003c DEbug not working so moving pcs 2026-02-15 16:41:33 -06:00
YourWishes 92a753560b prog 2026-02-15 01:09:28 -06:00
YourWishes af9904c892 Scripts work again.
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
YourWishes e5e8c49f6c Mostly nuking old system
Build Dusk / run-tests (push) Failing after 1m17s
Build Dusk / build-linux (push) Failing after 1m24s
Build Dusk / build-psp (push) Failing after 1m34s
Build Dusk / build-dolphin (push) Failing after 2m5s
2026-02-13 19:13:26 -06:00
YourWishes b37e5f45ca Sweeper
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
YourWishes e1f08b07aa Need a break from Dolphin
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
YourWishes 073ee8dca9 Trying to find dolphin texture bug
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
YourWishes a26e51cf46 Use local mirror for cglm
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
YourWishes dfed732825 Try different branch for upload binary action
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
YourWishes 87aa70c6d2 Push userdata?
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
YourWishes aa2979ffe7 Fix main
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
YourWishes 236e16aa6d Fix url format
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
YourWishes 184bb970e6 Update to not rely on third party actions
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
YourWishes bd54469891 test some stuff
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
YourWishes 2f5dccc3ef Texture loading
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
YourWishes 592edb90a0 Test lua rgb rainbow
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
YourWishes 3db7e6b1b9 Fixed palette lookup
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
YourWishes 13c4df0d85 Compiles on dolphin, finally
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
YourWishes ef25fb09da Fix linux building
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
YourWishes 03cf4a9efe See if dolphin will render the floating text demo
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
YourWishes 53dd36efdd Fixed alpha textures properly on PSP
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
YourWishes ad9e841a42 Removed CXX target
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
YourWishes 14f3f464c7 Prog on fixing psp alpha textures
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
YourWishes cbe51cc8d0 Speedup build
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
YourWishes efaa3f6eea Fix building on PSP
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
YourWishes 52cce9a3b0 ADd meta.xml
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
YourWishes b7b390311e Forgor wii stuff
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
YourWishes c1eeddd14b Fix dir spelling
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
YourWishes 1a7a55dfc3 Fix missing mkdirp
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
YourWishes fe5927ea6a Add more bin
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
YourWishes 119c794ad7 Add bin
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
YourWishes e2076b2c1c Also build wii
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
YourWishes 5208c5148e Fix cmake?
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
YourWishes d80660b097 cur dir
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
YourWishes b916d0278b install cmake
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
YourWishes d51e13e620 Update
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
YourWishes 40ad4326ef Do it all ourselves
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
YourWishes b8afc1684a with --yes
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
YourWishes 8348b31ac8 re
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
YourWishes 411f2dbcce 403 fix?
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
YourWishes ee89c08160 Manually setup ppc
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
YourWishes 357607a89f more echoing
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
YourWishes 8d6dc2df44 Try sudo
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
YourWishes 5207582ab3 Try more
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
YourWishes 71768e6154 Try outside
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
YourWishes ecbe235523 Try try again
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
YourWishes afef079d1e Fix cli order
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
YourWishes df17696c69 try append u
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
YourWishes 065bf0908f Fix crap
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
YourWishes 5b6755e9cf Try fix workdir
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
YourWishes b08482acf1 Build cube again
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
YourWishes 80c9c1d389 Try mount differently.
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
YourWishes bb7db57bda yes
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
YourWishes 6a83ac767c cli 2026-02-06 14:05:18 -06:00
YourWishes 7e47ef9d74 try official docker steps
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
YourWishes e7ec603526 yet another
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
YourWishes 2d8ae09bd8 Test again?
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
YourWishes b2affbc0a7 Try something else?
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
YourWishes d50bc61ada Prog
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
YourWishes ec6b032b45 Take sH out
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
YourWishes bc72f48496 Try again?
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
YourWishes dcf06fbd36 Update shell
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
YourWishes 96311d72c2 Retry build
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
YourWishes 07938cccc7 Build dolphin first while I test.
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
YourWishes 097c8c00f9 Fixed flickering
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
YourWishes aa5b41fe31 First texture rendering (if broken) 2026-02-06 12:48:49 -06:00
YourWishes 0d56859d94 Alpha textures 2026-02-05 23:28:26 -06:00
YourWishes 1af5f238e4 RGBA textures 2026-02-05 21:57:56 -06:00
YourWishes dd697d5650 Emu vs Real! 2026-02-05 08:43:49 -06:00
YourWishes 5cf299a1c7 Mesh working, lua broken 2026-02-05 08:21:19 -06:00
YourWishes 67bf825cc9 Camera 2026-02-05 00:42:04 -06:00
YourWishes 56e1696cd4 Asset loading 2026-02-04 21:52:14 -06:00
YourWishes d955fb6430 Debugging functions. 2026-02-04 18:32:20 -06:00
YourWishes dd910a31aa Actually compiled 2026-02-04 17:44:53 -06:00
YourWishes 708c4d0ec3 "Improved" 2026-02-04 15:54:09 -06:00
YourWishes ad13d6c6a1 SDL2 example builds. 2026-02-04 11:17:25 -06:00
YourWishes 1c32158142 DOlphin progress 2026-02-04 10:16:16 -06:00
YourWishes 5cea284906 Map loading
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
YourWishes 13dba8b604 Restore some map stuff
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
YourWishes 22398ddcef Added story flags
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
YourWishes 94e2cc6210 Screen background
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
YourWishes da3513f63d Fix 1
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
YourWishes 2c83e4ba9f mkdirs
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
YourWishes c862071126 Text
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
YourWishes fed819e9b2 renders
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
YourWishes c6f4518684 More lua stuff, yay.
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
YourWishes 053778a502 Refactored and simplified lua stuff a lot.
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
YourWishes 78e1ae885a Add color support.
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
YourWishes 982d28a3e0 prog
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
YourWishes c2cad858a5 Allow strings to be returned from structs
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
YourWishes 794e0574ad Moved a few things around, definitely not clean but better.
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
YourWishes c190271565 Builds on PSP properly.
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
YourWishes ae8a869f64 Sort fix
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
YourWishes 69d64eb8e4 Update PSP toolchain
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
YourWishes b2f2df650a restored build
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
YourWishes 6af570fab2 See why PSP fails on ubuntu vm 2026-01-28 11:33:12 -06:00
YourWishes 9ed902017c Cleanup scene.
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
YourWishes 32b41c98e1 Rendering a moving square entirely from lua.
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
YourWishes 6bdb4ae30d Scene methods
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
YourWishes 25dc97e3cc Update some script stuff.
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
YourWishes cc85983736 Add struct metafield
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
YourWishes 6e78ee188d Event in lua (partial)
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
YourWishes 9b73f1717f Event
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
YourWishes c7b9a53535 Locale script
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
YourWishes 2b9be6675c Locale
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
YourWishes fb93453482 Cleaned some tools up
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
YourWishes 81b08b2eba idk
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
YourWishes d1b03c4cb3 prog
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
YourWishes 9544d15a18 Dynamically assign script values for items and inputs
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
YourWishes 0392dd0e7f Added csv_to_array tool
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
YourWishes 9c25fde548 Fixed ints
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
YourWishes 2c9d0c6cff Fixed duskdefs
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
YourWishes 9897dbe031 env_to_h cleaned
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
YourWishes e78f117cfd Switched env to python
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
YourWishes 07afc3813a Refactor
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
YourWishes d788de8637 Starting refactor of tools, thank gosh
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
YourWishes d749ac8a91 Add missing dnfs
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
YourWishes f71c271c97 item
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
YourWishes e1d7b7308f Progness
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
YourWishes 26a71a823a Test assert
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
YourWishes 5e39097faa Improve sorting
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
YourWishes 0df7845f2c Added memory checks
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
YourWishes af5bf987c8 Add inventory.
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
YourWishes 024ace1078 Fixed PSP building
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
YourWishes 8d00fe9d16 Test PSP update
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
YourWishes ab422b14dd Update PSP SDK
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
YourWishes 95c0690216 Add extra time checks.
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
YourWishes 6cb80e9e23 Testing time (and found some bugs!)
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
YourWishes 83b799caa8 String.h
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
YourWishes a793ac2ff7 Add runner for testing
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
YourWishes aec937b04b Add some tests
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / build-linux (push) Has been cancelled
2026-01-05 16:13:14 -06:00
YourWishes 8ee46fd204 add tests.
Build Dusk / build-linux (push) Successful in 2m5s
Build Dusk / build-psp (push) Failing after 1m44s
2026-01-05 13:25:39 -06:00
YourWishes 726233e55f Map exec
Build Dusk / build-linux (push) Successful in 1m40s
Build Dusk / build-psp (push) Failing after 1m38s
2025-12-26 20:38:24 +10:00
YourWishes 7940f4c487 Map refactoring to prep for loading
Build Dusk / build-linux (push) Successful in 1m35s
Build Dusk / build-psp (push) Failing after 1m46s
2025-12-26 15:01:43 +10:00
YourWishes b16dbaceec Modules
Build Dusk / build-linux (push) Successful in 1m13s
Build Dusk / build-psp (push) Failing after 1m27s
2025-12-25 08:02:11 +10:00
YourWishes f39b2060a8 iuno just screwing around tbh
Build Dusk / build-linux (push) Failing after 1m5s
Build Dusk / build-psp (push) Failing after 1m24s
2025-12-24 10:44:53 +10:00
YourWishes aed202ebf9 Add include()
Build Dusk / build-linux (push) Successful in 1m16s
Build Dusk / build-psp (push) Failing after 1m14s
2025-12-24 09:41:05 +10:00
YourWishes a495179e5f Prog
Build Dusk / build-linux (push) Successful in 1m19s
Build Dusk / build-psp (push) Failing after 1m36s
2025-12-05 14:41:13 -06:00
YourWishes 4e1b404820 Add script context
Build Dusk / build-linux (push) Successful in 1m41s
Build Dusk / build-psp (push) Failing after 2m36s
2025-12-04 20:57:12 -06:00
YourWishes 8c74ee31e0 Add lua diff
Build Dusk / build-linux (push) Successful in 1m51s
Build Dusk / build-psp (push) Successful in 2m2s
2025-12-04 00:39:09 -06:00
YourWishes 77d3c54ebb Fixed PSP build
Build Dusk / build-linux (push) Failing after 1m53s
Build Dusk / build-psp (push) Failing after 1m27s
2025-12-04 00:33:42 -06:00
YourWishes b5de39926b Lua 2025-12-04 00:30:44 -06:00
YourWishes 3a8dafbb91 Fix compile issue
Build Dusk / build-linux (push) Failing after 2m13s
Build Dusk / build-psp (push) Failing after 2m36s
2025-12-04 00:26:49 -06:00
YourWishes 6b22f547fe PyGL
Build Dusk / build-linux (push) Failing after 2m5s
Build Dusk / build-psp (push) Has been cancelled
2025-12-04 00:23:50 -06:00
YourWishes de78be3e25 Attempt install pyqt5
Build Dusk / build-linux (push) Failing after 1m48s
Build Dusk / build-psp (push) Failing after 2m4s
2025-12-04 00:12:04 -06:00
YourWishes 9f507be7bc Lua script something
Build Dusk / build-linux (push) Failing after 2m56s
Build Dusk / build-psp (push) Failing after 1m49s
2025-12-04 00:02:26 -06:00
YourWishes 9aaf271996 fixing some stuff but nothing really.
Build Dusk / build-linux (push) Failing after 50s
Build Dusk / build-psp (push) Failing after 1m1s
2025-11-28 10:45:07 -06:00
YourWishes b01c0d37b0 Region editor
Build Dusk / build-linux (push) Failing after 51s
Build Dusk / build-psp (push) Failing after 55s
2025-11-28 08:48:42 -06:00
YourWishes 538079880d Abt to cutscene
Build Dusk / build-linux (push) Failing after 6m26s
Build Dusk / build-psp (push) Failing after 1m1s
2025-11-25 08:45:12 -06:00
YourWishes fe0529d021 Fixed initial chunk load buggy
Build Dusk / build-linux (push) Failing after 2m15s
Build Dusk / build-psp (push) Failing after 1m48s
2025-11-25 08:27:43 -06:00
YourWishes d068f0f2c3 Fixed double clicking to ent
Build Dusk / build-linux (push) Failing after 1m8s
Build Dusk / build-psp (push) Has been cancelled
2025-11-25 08:23:27 -06:00
YourWishes f9a64b8d54 Fixed entity positioning
Build Dusk / build-linux (push) Failing after 57s
Build Dusk / build-psp (push) Has been cancelled
2025-11-25 08:21:56 -06:00
YourWishes 01cbfaae95 Trying to find entity editor bug
Build Dusk / build-linux (push) Failing after 41s
Build Dusk / build-psp (push) Failing after 1m1s
2025-11-24 13:13:49 -06:00
YourWishes f9006a90d5 Editor has chunk loading
Build Dusk / build-linux (push) Failing after 57s
Build Dusk / build-psp (push) Failing after 1m0s
2025-11-23 22:44:31 -06:00
YourWishes 7daeaee6b5 Basically entity editing done
Build Dusk / build-linux (push) Failing after 43s
Build Dusk / build-psp (push) Failing after 49s
2025-11-22 11:56:08 -06:00
YourWishes 03218ce20f Ent saving and loading
Build Dusk / build-linux (push) Failing after 49s
Build Dusk / build-psp (push) Failing after 59s
2025-11-22 11:20:07 -06:00
YourWishes 6f33522c1c Begin adding entities to editor
Build Dusk / build-linux (push) Failing after 56s
Build Dusk / build-psp (push) Failing after 1m8s
2025-11-22 10:38:16 -06:00
YourWishes 3697cc3eef Prep ent
Build Dusk / build-linux (push) Failing after 55s
Build Dusk / build-psp (push) Failing after 56s
2025-11-20 16:45:50 -06:00
YourWishes 51a1077fda Finally merged map asset and map tool
Build Dusk / build-linux (push) Failing after 39s
Build Dusk / build-psp (push) Failing after 54s
2025-11-19 20:25:25 -06:00
YourWishes 8740c2b165 Fixed underflow error on worldpos.
Build Dusk / build-linux (push) Successful in 55s
Build Dusk / build-psp (push) Successful in 1m4s
2025-11-19 15:59:44 -06:00
YourWishes 6ed2bdd4c5 Added some extra checks around world positions, revealing bug. Likely going to sign all world coordinates.
Build Dusk / build-linux (push) Successful in 48s
Build Dusk / build-psp (push) Successful in 59s
2025-11-19 15:52:43 -06:00
YourWishes c32df89490 Added diagonal ramps
Build Dusk / build-linux (push) Successful in 55s
Build Dusk / build-psp (push) Successful in 1m4s
2025-11-19 15:40:37 -06:00
YourWishes bd5a67676b Minor improvements, add east and west ramp
Build Dusk / build-linux (push) Successful in 48s
Build Dusk / build-psp (push) Successful in 1m0s
2025-11-19 13:25:58 -06:00
YourWishes 903dab49e3 Editor partially started.
Build Dusk / build-linux (push) Successful in 44s
Build Dusk / build-psp (push) Successful in 55s
2025-11-19 13:00:35 -06:00
YourWishes 1668c4b0d2 Bit more rendering
Build Dusk / build-linux (push) Failing after 53s
Build Dusk / build-psp (push) Failing after 47s
2025-11-19 09:52:31 -06:00
YourWishes 2179a27bf5 Prog
Build Dusk / build-linux (push) Successful in 1m3s
Build Dusk / build-psp (push) Successful in 1m10s
2025-11-19 09:14:32 -06:00
YourWishes 6e7a0cba76 Readme
Build Dusk / build-linux (push) Successful in 54s
Build Dusk / build-psp (push) Successful in 1m8s
2025-11-17 11:28:10 -06:00
YourWishes 69b37b30bc Finally ready to merge the two tool codebases
Build Dusk / build-linux (push) Successful in 54s
Build Dusk / build-psp (push) Successful in 1m3s
2025-11-16 23:52:52 -06:00
YourWishes ae941a0fdb Fixed crash
Build Dusk / build-linux (push) Successful in 51s
Build Dusk / build-psp (push) Successful in 56s
2025-11-16 17:24:54 -06:00
YourWishes 1b741a81e5 Add .editor to ignore
Build Dusk / build-linux (push) Successful in 42s
Build Dusk / build-psp (push) Successful in 51s
2025-11-16 16:26:59 -06:00
YourWishes edf321515b Remove .editor
Build Dusk / build-psp (push) Has been cancelled
Build Dusk / build-linux (push) Has been cancelled
2025-11-16 16:26:49 -06:00
YourWishes c874e6c197 Fixed some stuff, procrastinating the real problem
Build Dusk / build-linux (push) Successful in 47s
Build Dusk / build-psp (push) Successful in 1m6s
2025-11-16 16:18:01 -06:00
YourWishes 9a59c22288 Try load chunk data.
Build Dusk / build-linux (push) Successful in 53s
Build Dusk / build-psp (push) Successful in 56s
2025-11-16 15:02:18 -06:00
YourWishes 750e8840f0 Prepping editor more...
Build Dusk / build-linux (push) Successful in 39s
Build Dusk / build-psp (push) Successful in 1m6s
2025-11-16 14:43:29 -06:00
YourWishes cf59989167 Closer to actually editing
Build Dusk / build-linux (push) Successful in 50s
Build Dusk / build-psp (push) Successful in 59s
2025-11-16 10:40:20 -06:00
YourWishes 7c194ab4b4 About to draw chunk
Build Dusk / build-linux (push) Successful in 1m5s
Build Dusk / build-psp (push) Successful in 1m5s
2025-11-16 09:11:58 -06:00
YourWishes be422d0a1e More langtool improvements
Build Dusk / build-linux (push) Successful in 1m2s
Build Dusk / build-psp (push) Successful in 1m5s
2025-11-16 00:04:28 -06:00
YourWishes 68b63d3007 Start work on editor
Build Dusk / build-linux (push) Successful in 54s
Build Dusk / build-psp (push) Successful in 1m10s
2025-11-15 23:38:31 -06:00
YourWishes 8525138594 test
Build Dusk / build-linux (push) Successful in 54s
Build Dusk / build-psp (push) Successful in 1m9s
2025-11-15 22:31:08 -06:00
YourWishes 7b9f8b190e Fix?
Build Dusk / build-linux (push) Failing after 52s
Build Dusk / build-psp (push) Failing after 55s
2025-11-15 22:29:07 -06:00
YourWishes 67f62daa9f Cmake fix
Build Dusk / build-linux (push) Failing after 38s
Build Dusk / build-psp (push) Failing after 43s
2025-11-15 22:27:09 -06:00
YourWishes 0ec701f30b Libs
Build Dusk / build-linux (push) Failing after 41s
Build Dusk / build-psp (push) Failing after 46s
2025-11-15 22:23:56 -06:00
YourWishes c53439066e Cleanup, prep for editor
Build Dusk / build-linux (push) Failing after 40s
Build Dusk / build-psp (push) Failing after 1m7s
2025-11-15 22:21:03 -06:00
YourWishes 7278bd0c6f Remove file
Build Dusk / build-linux (push) Failing after 43s
Build Dusk / build-psp (push) Failing after 1m6s
2025-11-15 20:06:44 -06:00
YourWishes b842e5821a Add defs generator.
Build Dusk / build-linux (push) Failing after 52s
Build Dusk / build-psp (push) Failing after 1m9s
2025-11-12 19:25:10 -06:00
YourWishes f7d4cce485 Remove release for now
Build Dusk / build-linux (push) Successful in 47s
Build Dusk / build-psp (push) Successful in 56s
2025-11-12 15:41:48 -06:00
YourWishes 4f502b707f Test4
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
YourWishes 09f182228f Test3? 2025-11-12 15:38:23 -06:00
YourWishes 69ce48a8b9 Test2
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
YourWishes 5c2788efe4 Test
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
YourWishes 768323b5b6 Fix GHES error
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
YourWishes e203f225e2 Fix build
Build Dusk / build-linux (push) Failing after 1m1s
Build Dusk / build-psp (push) Failing after 50s
Build Dusk / release (push) Has been skipped
2025-11-12 15:20:41 -06:00
YourWishes ab1e2476a0 PSP
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
YourWishes 312f32e786 Add PSP build
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
YourWishes 397466f0a8 Fix ubuntu compiling
Build Dusk / build-linux (push) Successful in 43s
Build Dusk / release (push) Failing after 12s
2025-11-12 15:09:17 -06:00
YourWishes 1a773cb8ba TEST
Build Dusk / build-linux (push) Failing after 1m32s
Build Dusk / release (push) Has been skipped
2025-11-12 14:54:32 -06:00
YourWishes 8441c325fa Add pthread
Build Dusk / build-linux (push) Failing after 42s
Build Dusk / release (push) Has been skipped
2025-11-12 14:51:38 -06:00
YourWishes cd4a1afbba add case
Build Dusk / build-linux (push) Failing after 50s
Build Dusk / release (push) Has been skipped
2025-11-12 14:48:45 -06:00
YourWishes ae75a932bf WSrap switch with braces
Build Dusk / build-linux (push) Failing after 51s
Build Dusk / release (push) Has been skipped
2025-11-12 14:46:20 -06:00
YourWishes 0fa2beede4 Init error state
Build Dusk / build-linux (push) Failing after 37s
Build Dusk / release (push) Has been skipped
2025-11-12 14:12:57 -06:00
YourWishes 8e5d5ca1d7 Try affixing to const in initialization
Build Dusk / build-linux (push) Failing after 49s
Build Dusk / release (push) Has been skipped
2025-11-12 14:11:48 -06:00
YourWishes 2a68414eec Try fix error state (s)
Build Dusk / build-linux (push) Failing after 49s
Build Dusk / release (push) Has been skipped
2025-11-12 14:10:17 -06:00
YourWishes 3f1c8e28e9 Try fix error state?
Build Dusk / build-linux (push) Failing after 42s
Build Dusk / release (push) Has been skipped
2025-11-12 14:08:41 -06:00
YourWishes 348531352e libzip dev
Build Dusk / build-linux (push) Failing after 48s
Build Dusk / release (push) Has been skipped
2025-11-12 13:10:01 -06:00
YourWishes 6770cc422a polib
Build Dusk / build-linux (push) Failing after 45s
Build Dusk / release (push) Has been skipped
2025-11-12 13:07:41 -06:00
YourWishes 7c157e22c7 Test linux build first
Build Dusk / build-linux (push) Failing after 41s
Build Dusk / release (push) Has been skipped
2025-11-12 13:05:06 -06:00
YourWishes 0cfc6d0503 Omit sudo
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
YourWishes f7fbd16e57 TEst
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
YourWishes 542aeadf0f Handle stairs better 2025-11-11 23:40:50 -06:00
YourWishes 84593867dc Realized why so much vram was being used. 2025-11-11 22:54:02 -06:00
YourWishes 9f23533069 Make stairs work 2025-11-11 22:12:08 -06:00
YourWishes 4f8f6a47cb Fixed some memory things on PSP 2025-11-11 20:07:28 -06:00
YourWishes 7d7a3f30e6 Test 2025-11-11 19:52:09 -06:00
YourWishes d39ed1ea5a Chunk loading improvements 2025-11-11 19:36:04 -06:00
YourWishes 5c8b314689 basically chunk loading 2025-11-11 19:24:56 -06:00
YourWishes 9953d7d388 Prog 2025-11-11 15:50:57 -06:00
YourWishes 5adf8a0773 Prepping map stuff 2025-11-11 12:25:46 -06:00
YourWishes 26bfb912f1 Tiles 2025-11-11 07:50:20 -06:00
YourWishes c07d0b32a9 qucik fx 2025-11-10 16:41:15 -06:00
YourWishes 562da971e9 Tile under foot 2025-11-10 11:01:41 -06:00
YourWishes 3eb24da475 idk2 2025-11-09 22:17:26 -06:00
YourWishes 8977d50992 idk 2025-11-09 22:09:22 -06:00
YourWishes 13365dd390 Couple world pos fixes/improvements. 2025-11-09 21:19:28 -06:00
YourWishes aee06f51f0 Improve worldpos.h 2025-11-09 20:55:41 -06:00
YourWishes d6c497731f Fix PSP compiled 2025-11-09 20:42:03 -06:00
YourWishes f23e26d9da Add ragequit 2025-11-09 19:21:00 -06:00
YourWishes ec324e02f2 Fix PSP Deadzones 2025-11-09 19:19:36 -06:00
YourWishes e2ce809762 Add more debug 2025-11-09 19:04:40 -06:00
YourWishes d054cf9e36 Fixed animation 2025-11-09 18:50:02 -06:00
YourWishes 943e775364 Time is better. 2025-11-09 18:32:33 -06:00
YourWishes b9ec6523d6 Back to work 2025-11-09 16:41:54 -06:00
YourWishes 5206d47b43 PSP now reads data directly from EBOOT if requested 2025-11-09 15:42:26 -06:00
YourWishes aaa8622956 Fixed PSP rendering 2025-11-09 14:54:33 -06:00
YourWishes 587d716aae Fix PSP build issues 2025-11-09 13:23:15 -06:00
YourWishes 5a8710cc76 Map loading and rendering 2025-11-09 13:00:43 -06:00
YourWishes 307f3a9dec Chunk loading example 2025-11-09 12:50:15 -06:00
YourWishes eff5fc3d9a Fixed asset header compare incosistenty 2025-11-09 10:24:45 -06:00
YourWishes 5a3004f1d1 map 2025-11-09 09:52:47 -06:00
YourWishes db589b7d91 Textbox example. 2025-11-08 17:26:25 -06:00
YourWishes 0a83175b66 Brought over microjrpg cutscene system (partially implemented) 2025-11-08 15:23:22 -06:00
YourWishes bc4776f096 Language finished. 2025-11-08 11:12:04 -06:00
YourWishes ab534bb998 Asset custom ready. 2025-11-08 08:41:32 -06:00
YourWishes cf2aacd75b About to implement load strategy 2025-11-08 08:32:21 -06:00
YourWishes 9f88374627 Language chunking script done (untested) 2025-11-08 08:17:11 -06:00
YourWishes b7d898b505 working on asset still 2025-11-07 23:04:40 -06:00
YourWishes 12c1fb6000 lang 2025-11-07 19:19:17 -06:00
YourWishes 1ce1fdff8d Refactor asset loading 2025-11-04 22:23:44 -06:00
YourWishes 7c11a7e5bc Started asset refact 2025-11-04 10:15:19 -06:00
YourWishes 7d46b98310 Ent movement 2025-11-04 09:03:36 -06:00
YourWishes 68c4834a62 Cursed input update 2025-11-04 08:46:47 -06:00
YourWishes c9608ad7a7 Made input work on fixed timesteps primarily. 2025-11-04 08:41:18 -06:00
YourWishes 6ea4132ff9 Interact 2025-11-03 22:35:40 -06:00
YourWishes be79356f42 Ent movement fixed. 2025-11-03 21:35:21 -06:00
YourWishes d4a2e059d7 Entity 2025-11-03 19:50:23 -06:00
YourWishes f3d985ecbc Starting ent stuff 2025-11-03 17:04:07 -06:00
YourWishes bcba693afb Initial scene 2025-11-03 14:45:05 -06:00
YourWishes b4fb7bf99f default binds 2025-11-03 14:33:42 -06:00
YourWishes 3ef6205ea3 Nuked console 2025-11-03 09:22:18 -06:00
YourWishes 3feb43fdad idk 2025-10-26 08:06:39 -05:00
YourWishes d74226dab1 Ent 2025-10-25 21:15:13 -05:00
YourWishes 5c3db5d991 Fix vec3 copy 2025-10-24 11:30:09 -05:00
YourWishes bcb8bea0fe Fixes build on the laptop? dunno why 2025-10-14 15:44:38 -05:00
YourWishes 0c0650a2c3 Fixed camera 2025-10-13 12:26:59 -05:00
YourWishes 2c0fd84c72 commit deez 2025-10-12 18:24:09 -05:00
YourWishes 81cd03e0c3 pixel? 2025-10-10 16:28:44 -05:00
YourWishes 349e6e7c94 Back to floats. 2025-10-10 09:16:08 -05:00
YourWishes c4c43b23ad prog 2025-10-09 15:07:07 -05:00
YourWishes 7622f81309 Friction, velocity, rendering 2025-10-09 09:44:17 -05:00
YourWishes c31bcf7f6a cam test 2025-10-08 23:06:39 -05:00
YourWishes fef31b9102 Example rendering 2025-10-08 22:34:27 -05:00
YourWishes 20cf016b06 scene stuff 2025-10-08 15:18:38 -05:00
YourWishes 67604eca8d FPS 2025-10-08 14:17:58 -05:00
YourWishes 46f820690d Setup rpg stuff 2025-10-08 07:11:53 -05:00
YourWishes e36256abe3 Scene fixing 2025-10-08 06:53:37 -05:00
YourWishes b00ca3d48c scene crap 2025-10-06 23:16:19 -05:00
YourWishes cf2e6bf382 cmd screen 2025-10-06 19:18:30 -05:00
YourWishes fc52afdb00 Refator pass 1 2025-10-06 19:14:52 -05:00
YourWishes 85434b4edb aspect 2025-10-06 16:30:44 -05:00
YourWishes f3a6c8df71 More screen modes. 2025-10-06 16:23:36 -05:00
YourWishes 6e5c5f61db screen mode 2025-10-06 16:18:53 -05:00
YourWishes 12c31ba9d1 screen 2025-10-06 15:43:27 -05:00
YourWishes ea50d893d4 prog 2025-10-06 13:59:59 -05:00
YourWishes bacd0e6e39 Minesweeper prog 2025-10-06 09:35:05 -05:00
YourWishes c0cd4ead04 Frame 2025-10-02 18:53:47 -05:00
YourWishes 4b04fc65ad scerne stuff 2025-10-01 19:21:20 -05:00
YourWishes 83243ba32f Draw FPS 2025-10-01 17:59:41 -05:00
YourWishes a734ecaa10 Game updates 2025-10-01 17:07:29 -05:00
YourWishes 22e2f703db Refact 2025-10-01 13:20:34 -05:00
YourWishes 28174b8dc8 Show Frame time 2025-09-24 19:33:10 -05:00
YourWishes 061352bcff load map first pass 2025-09-19 12:43:57 -05:00
YourWishes 2f40724258 Map saving first pass 2025-09-18 17:01:10 -05:00
YourWishes a45a2a5bd7 Basic interaction 2025-09-17 18:54:12 -05:00
YourWishes 08221af3f8 entity dir 2025-09-17 18:38:14 -05:00
YourWishes f799690d3c Physics I guess 2025-09-15 19:37:01 -05:00
YourWishes 07ab2b4b02 Gave up on editor 2025-09-15 09:53:34 -05:00
YourWishes cafeda4368 Subdirize assets, add TSX support 2025-09-15 09:28:12 -05:00
YourWishes 517b39649c npc start 2025-09-14 08:58:54 -05:00
YourWishes 067b0d2e9f Added screen 2025-09-12 15:56:21 -05:00
YourWishes 9b98181d28 Process tileset. 2025-09-12 12:43:56 -05:00
YourWishes 46a94ecacd Renders on PSP again 2025-09-12 00:25:17 -05:00
YourWishes 964a9f64f2 Overworld render test. 2025-09-11 23:33:24 -05:00
YourWishes b4d94c2cbe FPS toggle 2025-09-11 22:25:57 -05:00
YourWishes 268e9ffefd Render is color not uint8_t 2025-09-11 22:20:21 -05:00
YourWishes c8f8170ec2 Command aliasing 2025-09-11 22:07:51 -05:00
YourWishes 8b20f0bf31 Allowed binds to execute commands. 2025-09-11 12:58:04 -05:00
YourWishes fe9af039fc Add kb defs 2025-09-10 22:01:00 -05:00
YourWishes 6fad5bef4a Input bind complete. 2025-09-08 13:30:27 -05:00
YourWishes 16a0403fd4 Working on cmd bind 2025-09-07 23:53:21 -05:00
YourWishes e32d1f0900 Add exec command 2025-09-07 23:24:21 -05:00
YourWishes 3f37b7cdb5 Beginning input refactor 2025-09-03 11:57:48 -05:00
YourWishes 059ccf41b6 draw text. 2025-09-02 23:01:15 -05:00
YourWishes 1af2b8f47b Render text working. 2025-09-02 22:47:07 -05:00
YourWishes 87f18d0e13 Rendering on PSP again. 2025-09-02 21:14:07 -05:00
YourWishes 8de12da1ec Palettized image test. 2025-09-02 18:57:28 -05:00
YourWishes 71080682cc Fixed an error bug 2025-09-02 09:23:46 -05:00
YourWishes f915a4208b Roughly planning asset locking 2025-09-01 22:07:36 -05:00
YourWishes 14c41d33a7 Asset manager refactor begin. 2025-09-01 21:00:50 -05:00
YourWishes 3e61d6f84d Palette assets improved. 2025-09-01 20:07:12 -05:00
YourWishes 4541d5219b About to refator tools... again 2025-09-01 17:22:33 -05:00
YourWishes 3ce1566a2e Merge 2025-09-01 11:10:28 -05:00
YourWishes 368729f0f3 Fixed some camera bugs. 2025-09-01 11:02:30 -05:00
YourWishes 127392a1ae Added findzip 2025-09-01 09:39:38 -05:00
YourWishes af1329710d Made a big mess of the codebase 2025-08-28 07:14:13 -05:00
YourWishes 30232d1275 Palette image (incomplete) 2025-08-27 22:55:47 -05:00
YourWishes 31fa4948d5 Image generation? 2025-08-27 22:43:38 -05:00
YourWishes 6c11096fd2 Palette loading done. 2025-08-27 19:59:55 -05:00
YourWishes 7a90d2d38f prog 2025-08-27 07:46:11 -05:00
YourWishes a543bc7c00 commit asset prog 2025-08-26 16:23:08 -05:00
YourWishes 8af2f044ed Asset prog 2025-08-25 10:16:55 -05:00
YourWishes 947f21cac7 Relative 2025-08-24 15:18:01 -05:00
YourWishes 479aad2f06 Going to redo assets. 2025-08-24 13:57:12 -05:00
YourWishes 329925ea54 Asset 2025-08-23 17:43:22 -05:00
YourWishes c8a3ebfcec FindSDL2 2025-08-23 15:53:08 -05:00
YourWishes 1dfde00317 Removed DUSK_ prefix 2025-08-23 10:13:49 -05:00
YourWishes 1bf6fe6eaf Add backbuffer 2025-08-22 23:30:23 -05:00
YourWishes 995bbe1acd Fixed time. 2025-08-22 22:42:38 -05:00
YourWishes 1fb9485ee8 Fixed PSP compiling 2025-08-22 20:52:59 -05:00
YourWishes f9385ed233 ECS rendering 2025-08-22 16:15:42 -05:00
YourWishes 94ad64675d cleaned more stuff 2025-08-22 12:40:18 -05:00
YourWishes b1be1deb79 ecs work 2025-08-21 22:58:39 -05:00
YourWishes 1b4c270ccb ECS improvements! 2025-08-21 18:58:34 -05:00
YourWishes 9f5894cdcb About to break scene tree. 2025-08-21 16:21:01 -05:00
YourWishes 4688358d6a Scene tree 2025-08-21 09:52:15 -05:00
YourWishes c8390bdb12 Committing progress. 2025-08-21 07:33:08 -05:00
YourWishes 3a753b1299 sdl back 2025-08-20 21:56:55 -05:00
YourWishes 84f2735246 Console input 2025-08-20 21:23:12 -05:00
YourWishes fbfcbe9578 archivemuh 2025-08-20 19:18:38 -05:00
YourWishes 1411c2e96b Allow different texture formats. 2025-08-18 22:51:42 -05:00
YourWishes 6f42a6e195 Moved entitydir to direction_t 2025-08-18 22:28:22 -05:00
YourWishes 1e57183ca1 Load is now rounded compile time. 2025-08-18 21:46:12 -05:00
YourWishes d79e12ffaa Switched to using tile coordinates, losing my angled movement unfortunately. 2025-08-18 21:40:01 -05:00
YourWishes bcb1616201 Matrices 2025-08-18 19:18:55 -05:00
YourWishes 38127d9260 Ortho re-added 2025-08-18 19:15:37 -05:00
YourWishes d48d927202 textbox renders. 2025-08-17 17:58:53 -05:00
YourWishes 150a410f00 Is it really that time again? 2025-08-17 16:39:42 -05:00
YourWishes fbbd2176fd Moved render UI code 2025-08-17 16:31:08 -05:00
YourWishes 3e19771d8f Time dr freeman? 2025-08-17 16:18:56 -05:00
YourWishes 3d4317260f Time stuff 2025-08-17 16:11:22 -05:00
YourWishes 6d6a0e4886 Technically rendering everything. 2025-08-17 14:34:51 -05:00
YourWishes 91b93b5b1e add quit command 2025-08-17 12:55:08 -05:00
YourWishes c3310a036f Nothing really changed 2025-08-16 14:52:52 -05:00
YourWishes 69acbd017c Fixed framebuffer 2025-08-15 21:45:21 -05:00
YourWishes 81ed15b171 Builds on PSP again. 2025-08-15 21:37:44 -05:00
YourWishes d1ab8b0cc8 Framebuffer done. 2025-08-15 18:50:43 -05:00
YourWishes cbdc271a5e Framebuffer first 2025-08-15 18:39:19 -05:00
YourWishes 3c908dc1ed Camera 2025-08-15 15:56:30 -05:00
YourWishes 217f00ff4c Font texture. 2025-08-12 22:32:57 -05:00
806 changed files with 45405 additions and 6749 deletions
+177
View File
@@ -0,0 +1,177 @@
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-vita:
# runs-on: ubuntu-latest
# steps:
# - name: Checkout repository
# uses: actions/checkout@v6
# - name: Set up Docker
# uses: docker/setup-docker-action@v5
# - name: Build Vita
# run: ./scripts/build-vita-docker.sh
# - name: Upload Vita binary
# uses: actions/upload-artifact@v6
# with:
# name: dusk-vita
# path: build-vita/Dusk.vpk
# 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-gamecube-iso:
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 ISO
run: ./scripts/build-gamecube-iso-docker.sh
- name: Copy output files.
run: |
mkdir -p ./git-artifcats/Dusk
cp build-gamecube-iso/Dusk-NTSC-J.iso ./git-artifcats/Dusk/Dusk-NTSC-J.iso
cp build-gamecube-iso/Dusk-NTSC-U.iso ./git-artifcats/Dusk/Dusk-NTSC-U.iso
cp build-gamecube-iso/Dusk-PAL.iso ./git-artifcats/Dusk/Dusk-PAL.iso
- name: Upload GameCube ISO
uses: actions/upload-artifact@v6
with:
name: dusk-gamecube-iso
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/boot.dol
cp build-wii/dusk.dsk ./git-artifcats/Dusk/apps/Dusk/dusk.dsk
cp build-wii/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
build-wii-iso:
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 ISO
run: ./scripts/build-wii-iso-docker.sh
- name: Copy output files.
run: |
mkdir -p ./git-artifcats/Dusk
cp build-wii-iso/Dusk-NTSC-J.iso ./git-artifcats/Dusk/Dusk-NTSC-J.iso
cp build-wii-iso/Dusk-NTSC-U.iso ./git-artifcats/Dusk/Dusk-NTSC-U.iso
cp build-wii-iso/Dusk-PAL.iso ./git-artifcats/Dusk/Dusk-PAL.iso
- name: Upload Wii ISO
uses: actions/upload-artifact@v6
with:
name: dusk-wii-iso
path: ./git-artifcats/Dusk
if-no-files-found: error
+12 -1
View File
@@ -83,7 +83,6 @@ assets/borrowed
.VSCode* .VSCode*
/vita /vita
._* ._*
*~ *~
@@ -95,3 +94,15 @@ assets/borrowed
# /archive # /archive
__pycache__ __pycache__
package-lock.json
yarn-error.log
yarn.lock
.editor
.venv
/build2
/build*
/assets/test
/tools_old
+89 -34
View File
@@ -5,14 +5,20 @@
# Setup # Setup
cmake_minimum_required(VERSION 3.13) cmake_minimum_required(VERSION 3.13)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules)
cmake_policy(SET CMP0079 NEW)
# set(FETCHCONTENT_UPDATES_DISCONNECTED ON)
if(NOT DEFINED DUSK_TARGET_SYSTEM) option(DUSK_BUILD_TESTS "Enable tests" OFF)
# set(DUSK_TARGET_SYSTEM "linux")
set(DUSK_TARGET_SYSTEM "psp") # Game identity — override these per-project
endif() set(DUSK_GAME_NAME "Dusk" CACHE STRING "Game display name")
set(DUSK_GAME_AUTHOR "YouWish" CACHE STRING "Game author / coder")
set(DUSK_GAME_SHORT_DESCRIPTION "Dusk game" CACHE STRING "One-line description")
set(DUSK_GAME_LONG_DESCRIPTION "No description yet." CACHE STRING "Full description")
# Prep cache # Prep cache
set(DUSK_CACHE_TARGET "dusk-target") set(DUSK_CACHE_TARGET "dusk-target")
@@ -21,56 +27,105 @@ set(DUSK_CACHE_TARGET "dusk-target")
set(DUSK_ROOT_DIR "${CMAKE_SOURCE_DIR}") set(DUSK_ROOT_DIR "${CMAKE_SOURCE_DIR}")
set(DUSK_BUILD_DIR "${CMAKE_BINARY_DIR}") set(DUSK_BUILD_DIR "${CMAKE_BINARY_DIR}")
set(DUSK_SOURCES_DIR "${DUSK_ROOT_DIR}/src") 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_TEMP_DIR "${DUSK_BUILD_DIR}/temp")
set(DUSK_TOOLS_DIR "${DUSK_ROOT_DIR}/tools") set(DUSK_TOOLS_DIR "${DUSK_ROOT_DIR}/tools")
# set(DUSK_ASSETS_DIR "${DUSK_ROOT_DIR}/assets")
# set(DUSK_ASSETS_BUILD_DIR "${DUSK_BUILD_DIR}/assets")
set(DUSK_DATA_DIR "${DUSK_ROOT_DIR}/data") 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_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_BUILD_BINARY ${DUSK_BUILD_DIR}/Dusk CACHE INTERNAL ${DUSK_CACHE_TARGET})
set(DUSK_ASSETS "" 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 # Create directories
file(MAKE_DIRECTORY ${DUSK_GENERATED_HEADERS_DIR}) 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 # Required build packages
if(DUSK_TARGET_SYSTEM STREQUAL "psp") find_package(Python3 COMPONENTS Interpreter REQUIRED)
find_package(pspsdk REQUIRED)
endif()
# Init Project # Init Project.
project(${DUSK_TARGET_NAME} project(${DUSK_LIBRARY_TARGET_NAME}
VERSION 1.0.0 VERSION 1.0.0
LANGUAGES C LANGUAGES C
) )
# Executable # Either, create library and binary separately (used for tests), or make them
add_executable(${DUSK_TARGET_NAME}) # 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()
if(NOT DEFINED DUSK_VERSION)
string(TIMESTAMP DUSK_VERSION "debug-%y%m%d%H%M%S")
endif()
# Definitions
target_compile_definitions(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
DUSK_TARGET_SYSTEM="${DUSK_TARGET_SYSTEM}"
DUSK_GAME_NAME="${DUSK_GAME_NAME}"
DUSK_GAME_AUTHOR="${DUSK_GAME_AUTHOR}"
DUSK_GAME_SHORT_DESCRIPTION="${DUSK_GAME_SHORT_DESCRIPTION}"
DUSK_GAME_LONG_DESCRIPTION="${DUSK_GAME_LONG_DESCRIPTION}"
DUSK_VERSION="${DUSK_VERSION}"
)
# Toolchains
include(cmake/targets/${DUSK_TARGET_SYSTEM}.cmake)
# Add tools # Add tools
add_subdirectory(tools) add_subdirectory(tools)
# Add code # Include generated headers from tools.
add_subdirectory(src) target_include_directories(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
# Include generated headers
target_include_directories(${DUSK_TARGET_NAME}
PUBLIC
${DUSK_GENERATED_HEADERS_DIR} ${DUSK_GENERATED_HEADERS_DIR}
) )
# Postbuild, create PBP file for PSP. # Add main code
if(DUSK_TARGET_SYSTEM STREQUAL "psp") add_subdirectory(${DUSK_SOURCES_DIR})
create_pbp_file(
TARGET "${DUSK_TARGET_NAME}" # Include generated headers
ICON_PATH NULL target_include_directories(${DUSK_LIBRARY_TARGET_NAME} PUBLIC
BACKGROUND_PATH NULL ${DUSK_GENERATED_HEADERS_DIR}
PREVIEW_PATH NULL )
TITLE "${DUSK_TARGET_NAME}"
VERSION 01.00 # Handle tests
) if(DUSK_BUILD_TESTS)
enable_testing()
add_subdirectory(test)
endif() 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
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
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()
+66
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
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
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('\\', '/')
+33
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)
+134
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)
+228
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)
+154
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)
+96
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)
+43
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)
+178
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
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
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);
+10
View File
@@ -0,0 +1,10 @@
# Copyright (c) 2026 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
camera.c
)
+107
View File
@@ -0,0 +1,107 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "camera.h"
#include "display/display.h"
#include "assert/assert.h"
#include "display/framebuffer/framebuffer.h"
#include "display/screen/screen.h"
void cameraInit(camera_t *camera) {
cameraInitPerspective(camera);
}
void cameraInitPerspective(camera_t *camera) {
assertNotNull(camera, "Not a camera component");
camera->projType = CAMERA_PROJECTION_TYPE_PERSPECTIVE;
camera->perspective.fov = glm_rad(45.0f);
camera->nearClip = 0.1f;
camera->farClip = 10000.0f;
camera->viewType = CAMERA_VIEW_TYPE_LOOKAT;
glm_vec3_copy((vec3){ 5.0f, 5.0f, 5.0f }, camera->lookat.position);
glm_vec3_copy((vec3){ 0.0f, 1.0f, 0.0f }, camera->lookat.up);
glm_vec3_copy((vec3){ 0.0f, 0.0f, 0.0f }, camera->lookat.target);
}
void cameraInitOrthographic(camera_t *camera) {
assertNotNull(camera, "Not a camera component");
camera->projType = CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC;
camera->orthographic.left = 0.0f;
camera->orthographic.right = SCREEN.width;
camera->orthographic.top = SCREEN.height;
camera->orthographic.bottom = 0.0f;
camera->nearClip = 0.1f;
camera->farClip = 1.0f;
camera->viewType = CAMERA_VIEW_TYPE_2D;
glm_vec2_copy((vec2){ 0.0f, 0.0f }, camera->_2d.position);
camera->_2d.zoom = 1.0f;
}
void cameraGetProjectionMatrix(camera_t *camera, mat4 dest) {
assertNotNull(camera, "Not a camera component");
assertNotNull(dest, "Destination matrix must not be null");
if(
camera->projType == CAMERA_PROJECTION_TYPE_PERSPECTIVE ||
camera->projType == CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED
) {
glm_mat4_identity(dest);
glm_perspective(
camera->perspective.fov,
SCREEN.aspect,
camera->nearClip,
camera->farClip,
dest
);
if(camera->projType == CAMERA_PROJECTION_TYPE_PERSPECTIVE_FLIPPED) {
dest[1][1] *= -1.0f;
}
} else if(camera->projType == CAMERA_PROJECTION_TYPE_ORTHOGRAPHIC) {
glm_mat4_identity(dest);
glm_ortho(
camera->orthographic.left,
camera->orthographic.right,
camera->orthographic.top,
camera->orthographic.bottom,
camera->nearClip,
camera->farClip,
dest
);
}
}
void cameraGetViewMatrix(camera_t *camera, mat4 dest) {
assertNotNull(camera, "Not a camera component");
assertNotNull(dest, "Destination matrix must not be null");
if(camera->viewType == CAMERA_VIEW_TYPE_MATRIX) {
glm_mat4_ucopy(camera->view, dest);
} else if(camera->viewType == CAMERA_VIEW_TYPE_LOOKAT) {
glm_mat4_identity(dest);
glm_lookat(
camera->lookat.position,
camera->lookat.target,
camera->lookat.up,
dest
);
} else if(camera->viewType == CAMERA_VIEW_TYPE_2D) {
glm_mat4_identity(dest);
glm_lookat(
(vec3){ camera->_2d.position[0], camera->_2d.position[1], 0.5f },
(vec3){ camera->_2d.position[0], camera->_2d.position[1], 0.0f },
(vec3){ 0.0f, 1.0f, 0.0f },
dest
);
} else if(camera->viewType == CAMERA_VIEW_TYPE_LOOKAT_PIXEL_PERFECT) {
assertUnreachable("LOOKAT_PIXEL_PERFECT view type is not implemented yet");
}
}
+97
View File
@@ -0,0 +1,97 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
#include "display/color.h"
#include "display/camera/cameraplatform.h"
#ifndef cameraPushMatrixPlatform
#error "cameraPushMatrixPlatform must be defined"
#endif
typedef enum {
CAMERA_VIEW_TYPE_MATRIX,
CAMERA_VIEW_TYPE_LOOKAT,
CAMERA_VIEW_TYPE_2D,
CAMERA_VIEW_TYPE_LOOKAT_PIXEL_PERFECT
} cameraviewtype_t;
typedef struct camera_s {
union {
mat4 view;
struct {
vec3 position;
vec3 target;
vec3 up;
} lookat;
struct {
vec3 offset;
vec3 target;
vec3 up;
float_t pixelsPerUnit;
} lookatPixelPerfect;
struct {
vec2 position;
float_t zoom;
} _2d;
};
union {
struct {
float_t fov;
} perspective;
struct {
float_t left;
float_t right;
float_t top;
float_t bottom;
} orthographic;
};
float_t nearClip;
float_t farClip;
cameraprojectiontype_t projType;
cameraviewtype_t viewType;
} camera_t;
/**
* Initializes a camera to default values. This calls cameraInitPerspective.
*/
void cameraInit(camera_t *camera);
/**
* Initializes a camera for perspective projection.
*/
void cameraInitPerspective(camera_t *camera);
/**
* Initializes a camera for orthographic projection.
*/
void cameraInitOrthographic(camera_t *camera);
/**
* Gets the projection matrix for a camera.
*
* @param camera Camera to get the projection matrix for
* @param dest Matrix to store the projection matrix in
*/
void cameraGetProjectionMatrix(camera_t *camera, mat4 dest);
/**
* Gets the view matrix for a camera.
*
* @param camera Camera to get the view matrix for
* @param dest Matrix to store the view matrix in
*/
void cameraGetViewMatrix(camera_t *camera, mat4 dest);
+158
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
+49
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))
+90
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()
+18
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)
+259
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
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
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
])
+57
View File
@@ -0,0 +1,57 @@
#!/usr/bin/env python3
import sys
from PyQt5.QtWidgets import (
QApplication, QVBoxLayout, QPushButton,
QDialog
)
from OpenGL.GL import *
from OpenGL.GLU import *
from tools.editor.maptool import MapWindow
from tools.editor.langtool import LangToolWindow
from tools.editor.cutscenetool import CutsceneToolWindow
DEFAULT_TOOL = None
DEFAULT_TOOL = "map"
# DEFAULT_TOOL = "cutscene"
TOOLS = [
("Map Editor", "map", MapWindow),
("Language Editor", "language", LangToolWindow),
("Cutscene Editor", "cutscene", CutsceneToolWindow),
]
class EditorChoiceDialog(QDialog):
def __init__(self):
super().__init__()
self.setWindowTitle("Choose Tool")
layout = QVBoxLayout(self)
self.selected = None
for label, key, win_cls in TOOLS:
btn = QPushButton(label)
btn.clicked.connect(lambda checked, w=win_cls: self.choose_tool(w))
layout.addWidget(btn)
def choose_tool(self, win_cls):
self.selected = win_cls
self.accept()
def get_choice(self):
return self.selected
def main():
app = QApplication(sys.argv)
tool_map = { key: win_cls for _, key, win_cls in TOOLS }
if DEFAULT_TOOL in tool_map:
win_cls = tool_map[DEFAULT_TOOL]
else:
choice_dialog = EditorChoiceDialog()
if choice_dialog.exec_() == QDialog.Accepted:
win_cls = choice_dialog.get_choice()
else:
sys.exit(0)
win = win_cls()
win.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
@@ -0,0 +1,58 @@
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLineEdit, QLabel, QSizePolicy, QComboBox, QHBoxLayout, QSpacerItem
from PyQt5.QtCore import Qt, pyqtSignal
from .cutscenewait import CutsceneWaitEditor
from .cutscenetext import CutsceneTextEditor
EDITOR_MAP = (
( "wait", "Wait", CutsceneWaitEditor ),
( "text", "Text", CutsceneTextEditor )
)
class CutsceneItemEditor(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.layout = QVBoxLayout(self)
self.layout.setAlignment(Qt.AlignTop | Qt.AlignLeft)
self.layout.addWidget(QLabel("Item Properties:"))
rowLayout = QHBoxLayout()
rowLayout.setSpacing(8)
rowLayout.addWidget(QLabel("Name:"))
self.nameInput = QLineEdit()
rowLayout.addWidget(self.nameInput)
rowLayout.addWidget(QLabel("Type:"))
self.typeDropdown = QComboBox()
self.typeDropdown.addItems([typeName for typeKey, typeName, editorClass in EDITOR_MAP])
rowLayout.addWidget(self.typeDropdown)
self.layout.addLayout(rowLayout)
self.activeEditor = None
# Events
self.nameInput.textChanged.connect(self.onNameChanged)
self.typeDropdown.currentTextChanged.connect(self.onTypeChanged)
# First load
self.onNameChanged(self.nameInput.text())
self.onTypeChanged(self.typeDropdown.currentText())
def onNameChanged(self, nameText):
pass
def onTypeChanged(self, typeText):
typeKey = typeText.lower()
# Remove existing editor
if self.activeEditor:
self.layout.removeWidget(self.activeEditor)
self.activeEditor.deleteLater()
self.activeEditor = None
# Create new editor
for key, name, editorClass in EDITOR_MAP:
if key == typeKey:
self.activeEditor = editorClass()
self.layout.addWidget(self.activeEditor)
break
@@ -0,0 +1,54 @@
from PyQt5.QtWidgets import QMenuBar, QAction, QFileDialog, QMessageBox
from PyQt5.QtGui import QKeySequence
class CutsceneMenuBar(QMenuBar):
def __init__(self, parent):
super().__init__(parent)
self.parent = parent
fileMenu = self.addMenu("File")
self.newAction = QAction("New", self)
self.newAction.setShortcut(QKeySequence.New)
self.newAction.triggered.connect(self.newFile)
fileMenu.addAction(self.newAction)
self.openAction = QAction("Open", self)
self.openAction.setShortcut(QKeySequence.Open)
self.openAction.triggered.connect(self.openFile)
fileMenu.addAction(self.openAction)
self.saveAction = QAction("Save", self)
self.saveAction.setShortcut(QKeySequence.Save)
self.saveAction.triggered.connect(self.saveFile)
fileMenu.addAction(self.saveAction)
self.saveAsAction = QAction("Save As", self)
self.saveAsAction.setShortcut(QKeySequence.SaveAs)
self.saveAsAction.triggered.connect(self.saveFileAs)
fileMenu.addAction(self.saveAsAction)
def newFile(self):
self.parent.clearCutscene()
def openFile(self):
path, _ = QFileDialog.getOpenFileName(self.parent, "Open Cutscene File", "", "JSON Files (*.json);;All Files (*)")
if not path:
return
# TODO: Load file contents into timeline
self.parent.currentFile = path
pass
def saveFile(self):
if not self.parent.currentFile:
self.saveFileAs()
return
# TODO: Save timeline to self.parent.currentFile
pass
def saveFileAs(self):
path, _ = QFileDialog.getSaveFileName(self.parent, "Save Cutscene File As", "", "JSON Files (*.json);;All Files (*)")
if path:
self.parent.currentFile = path
self.saveFile()
+21
View File
@@ -0,0 +1,21 @@
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QTextEdit
from PyQt5.QtCore import Qt
class CutsceneTextEditor(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
layout = QVBoxLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
label = QLabel("Text:")
label.setSizePolicy(label.sizePolicy().Expanding, label.sizePolicy().Fixed)
layout.addWidget(label)
self.textInput = QTextEdit()
self.textInput.setSizePolicy(self.textInput.sizePolicy().Expanding, self.textInput.sizePolicy().Expanding)
layout.addWidget(self.textInput, stretch=1)
def setText(self, text):
self.textInput.setPlainText(text)
def getText(self):
return self.textInput.toPlainText()
+20
View File
@@ -0,0 +1,20 @@
from PyQt5.QtWidgets import QWidget, QFormLayout, QDoubleSpinBox, QLabel
class CutsceneWaitEditor(QWidget):
def __init__(self, parent=None):
super().__init__(parent)
layout = QFormLayout(self)
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
self.waitTimeInput = QDoubleSpinBox()
self.waitTimeInput.setMinimum(0.0)
self.waitTimeInput.setMaximum(9999.0)
self.waitTimeInput.setDecimals(2)
self.waitTimeInput.setSingleStep(0.1)
layout.addRow(QLabel("Wait Time (seconds):"), self.waitTimeInput)
def setWaitTime(self, value):
self.waitTimeInput.setValue(value)
def getWaitTime(self):
return self.waitTimeInput.value()
+101
View File
@@ -0,0 +1,101 @@
from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QListWidget, QListWidgetItem, QMenuBar, QAction, QFileDialog, QMessageBox
from PyQt5.QtGui import QKeySequence
from tools.editor.cutscene.cutsceneitemeditor import CutsceneItemEditor
from tools.editor.cutscene.cutscenemenubar import CutsceneMenuBar
import sys
class CutsceneToolWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Dusk Cutscene Editor")
self.setGeometry(100, 100, 800, 600)
self.nextItemNumber = 1 # Track next available number
self.currentFile = None
self.dirty = False # Track unsaved changes
# File menu (handled by CutsceneMenuBar)
menubar = CutsceneMenuBar(self)
self.setMenuBar(menubar)
# Main layout: horizontal split
central = QWidget()
mainLayout = QHBoxLayout(central)
self.setCentralWidget(central)
# Timeline
leftPanel = QWidget()
leftLayout = QVBoxLayout(leftPanel)
self.timelineList = QListWidget()
self.timelineList.setSelectionMode(QListWidget.SingleSelection)
leftLayout.addWidget(QLabel("Cutscene Timeline"))
leftLayout.addWidget(self.timelineList)
btnLayout = QHBoxLayout()
self.addBtn = QPushButton("Add")
self.removeBtn = QPushButton("Remove")
btnLayout.addWidget(self.addBtn)
btnLayout.addWidget(self.removeBtn)
leftLayout.addLayout(btnLayout)
mainLayout.addWidget(leftPanel, 2)
# Property editor
self.editorPanel = QWidget()
self.editorLayout = QVBoxLayout(self.editorPanel)
self.itemEditor = None # Only create when needed
mainLayout.addWidget(self.editorPanel, 3)
# Events
self.timelineList.currentItemChanged.connect(self.onItemSelected)
self.addBtn.clicked.connect(self.addCutsceneItem)
self.removeBtn.clicked.connect(self.removeCutsceneItem)
def addCutsceneItem(self):
name = f"Cutscene item {self.nextItemNumber}"
timelineItem = QListWidgetItem(name)
self.timelineList.addItem(timelineItem)
self.timelineList.setCurrentItem(timelineItem)
self.nextItemNumber += 1
self.dirty = True
def removeCutsceneItem(self):
row = self.timelineList.currentRow()
if row < 0:
return
self.timelineList.takeItem(row)
self.dirty = True
# Remove editor if nothing selected
if self.timelineList.currentItem() is None or self.itemEditor is None:
return
self.editorLayout.removeWidget(self.itemEditor)
self.itemEditor.deleteLater()
self.itemEditor = None
def clearCutscene(self):
self.timelineList.clear()
self.nextItemNumber = 1
self.currentFile = None
self.dirty = False
if self.itemEditor:
self.editorLayout.removeWidget(self.itemEditor)
def onItemSelected(self, current, previous):
if current:
if not self.itemEditor:
self.itemEditor = CutsceneItemEditor()
self.editorLayout.addWidget(self.itemEditor)
return
if not self.itemEditor:
return
self.editorLayout.removeWidget(self.itemEditor)
self.itemEditor.deleteLater()
self.itemEditor = None
if __name__ == "__main__":
app = QApplication(sys.argv)
window = CutsceneToolWindow()
window.show()
sys.exit(app.exec_())
+202
View File
@@ -0,0 +1,202 @@
#!/usr/bin/env python3
from PyQt5.QtWidgets import QMainWindow, QApplication, QAction, QMenuBar, QMessageBox, QFileDialog, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QTableWidget, QTableWidgetItem, QHeaderView, QPushButton, QTabWidget, QFormLayout
from PyQt5.QtCore import Qt
import sys
import os
import polib
class LangToolWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Dusk Language Editor")
self.setGeometry(100, 100, 800, 600)
self.current_file = None
self.dirty = False
self.init_menu()
self.init_ui()
def init_ui(self):
central = QWidget()
layout = QVBoxLayout(central)
tabs = QTabWidget()
# Header Tab
header_tab = QWidget()
header_layout = QFormLayout(header_tab)
self.language_edit = QLineEdit()
self.language_edit.setMaximumWidth(220)
header_layout.addRow(QLabel("Language:"), self.language_edit)
self.plural_edit = QLineEdit()
self.plural_edit.setMaximumWidth(320)
header_layout.addRow(QLabel("Plural-Forms:"), self.plural_edit)
self.content_type_edit = QLineEdit("text/plain; charset=UTF-8")
self.content_type_edit.setMaximumWidth(320)
header_layout.addRow(QLabel("Content-Type:"), self.content_type_edit)
tabs.addTab(header_tab, "Header")
# Strings Tab
strings_tab = QWidget()
strings_layout = QVBoxLayout(strings_tab)
self.po_table = QTableWidget()
self.po_table.setColumnCount(2)
self.po_table.setHorizontalHeaderLabels(["msgid", "msgstr"])
self.po_table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
self.po_table.verticalHeader().setMinimumWidth(22)
self.po_table.verticalHeader().setDefaultAlignment(Qt.AlignRight | Qt.AlignVCenter)
strings_layout.addWidget(self.po_table)
row_btn_layout = QHBoxLayout()
add_row_btn = QPushButton("Add Row")
remove_row_btn = QPushButton("Remove Row")
row_btn_layout.addWidget(add_row_btn)
row_btn_layout.addWidget(remove_row_btn)
strings_layout.addLayout(row_btn_layout)
tabs.addTab(strings_tab, "Strings")
layout.addWidget(tabs)
add_row_btn.clicked.connect(self.add_row)
remove_row_btn.clicked.connect(self.remove_row)
self.add_row_btn = add_row_btn
self.remove_row_btn = remove_row_btn
self.setCentralWidget(central)
# Connect edits to dirty flag
self.language_edit.textChanged.connect(self.set_dirty)
self.plural_edit.textChanged.connect(self.set_dirty)
self.content_type_edit.textChanged.connect(self.set_dirty)
self.po_table.itemChanged.connect(self.set_dirty)
def set_dirty(self):
self.dirty = True
self.update_save_action()
def init_menu(self):
menubar = self.menuBar()
file_menu = menubar.addMenu("File")
new_action = QAction("New", self)
open_action = QAction("Open", self)
save_action = QAction("Save", self)
save_as_action = QAction("Save As", self)
new_action.triggered.connect(lambda: self.handle_file_action("new"))
open_action.triggered.connect(lambda: self.handle_file_action("open"))
save_action.triggered.connect(self.handle_save)
save_as_action.triggered.connect(self.handle_save_as)
file_menu.addAction(new_action)
file_menu.addAction(open_action)
file_menu.addAction(save_action)
file_menu.addAction(save_as_action)
self.save_action = save_action # Store reference for enabling/disabling
self.update_save_action()
def update_save_action(self):
self.save_action.setEnabled(self.current_file is not None)
def handle_file_action(self, action):
if self.dirty:
reply = QMessageBox.question(self, "Save Changes?", "Do you want to save changes before proceeding?", QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)
if reply == QMessageBox.Cancel:
return
elif reply == QMessageBox.Yes:
self.handle_save()
if action == "new":
self.new_file()
elif action == "open":
default_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../assets/locale"))
file_path, _ = QFileDialog.getOpenFileName(self, "Open Language File", default_dir, "PO Files (*.po)")
if file_path:
self.open_file(file_path)
def new_file(self):
self.current_file = None
self.dirty = False
self.language_edit.setText("")
self.plural_edit.setText("")
self.po_table.setRowCount(0)
self.update_save_action()
def open_file(self, file_path):
self.current_file = file_path
self.dirty = False
self.update_save_action()
self.load_po_file(file_path)
def load_po_file(self, file_path):
po = polib.pofile(file_path)
language = po.metadata.get('Language', '')
plural = po.metadata.get('Plural-Forms', '')
content_type = po.metadata.get('Content-Type', 'text/plain; charset=UTF-8')
self.language_edit.setText(language)
self.plural_edit.setText(plural)
self.content_type_edit.setText(content_type)
self.po_table.setRowCount(len(po))
for row, entry in enumerate(po):
self.po_table.setItem(row, 0, QTableWidgetItem(entry.msgid))
self.po_table.setItem(row, 1, QTableWidgetItem(entry.msgstr))
def save_file(self, file_path):
po = polib.POFile()
po.metadata = {
'Language': self.language_edit.text(),
'Content-Type': self.content_type_edit.text(),
'Plural-Forms': self.plural_edit.text(),
}
for row in range(self.po_table.rowCount()):
msgid_item = self.po_table.item(row, 0)
msgstr_item = self.po_table.item(row, 1)
msgid = msgid_item.text() if msgid_item else ''
msgstr = msgstr_item.text() if msgstr_item else ''
if msgid or msgstr:
entry = polib.POEntry(msgid=msgid, msgstr=msgstr)
po.append(entry)
po.save(file_path)
self.dirty = False
self.update_save_action()
def handle_save(self):
if self.current_file:
self.save_file(self.current_file)
else:
self.handle_save_as()
def handle_save_as(self):
default_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../assets/locale"))
file_path, _ = QFileDialog.getSaveFileName(self, "Save Language File As", default_dir, "PO Files (*.po)")
if file_path:
self.current_file = file_path
self.update_save_action()
self.save_file(file_path)
def add_row(self):
row = self.po_table.rowCount()
self.po_table.insertRow(row)
self.po_table.setItem(row, 0, QTableWidgetItem(""))
self.po_table.setItem(row, 1, QTableWidgetItem(""))
self.po_table.setCurrentCell(row, 0)
self.set_dirty()
def remove_row(self):
row = self.po_table.currentRow()
if row >= 0:
self.po_table.removeRow(row)
self.set_dirty()
def closeEvent(self, event):
if self.dirty:
reply = QMessageBox.question(self, "Save Changes?", "Do you want to save changes before exiting?", QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)
if reply == QMessageBox.Cancel:
event.ignore()
return
elif reply == QMessageBox.Yes:
self.handle_save()
event.accept()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = LangToolWindow()
window.show()
sys.exit(app.exec_())
+55
View File
@@ -0,0 +1,55 @@
import math
import time
from OpenGL.GL import *
from OpenGL.GLU import *
from tools.dusk.defs import TILE_WIDTH, TILE_HEIGHT, TILE_DEPTH, RPG_CAMERA_PIXELS_PER_UNIT, RPG_CAMERA_Z_OFFSET, RPG_CAMERA_FOV
class Camera:
def __init__(self, parent):
self.parent = parent
self.pixelsPerUnit = RPG_CAMERA_PIXELS_PER_UNIT
self.yOffset = RPG_CAMERA_Z_OFFSET
self.fov = RPG_CAMERA_FOV
self.scale = 8.0
self.lastTime = time.time()
self.lookAtTarget = [0.0, 0.0, 0.0]
def setup(self, vw, vh, duration=0.1):
now = time.time()
delta = now - self.lastTime
self.lastTime = now
# Calculate ease factor for exponential smoothing over 'duration' seconds
ease = 1 - math.exp(-delta / duration)
z = (vh / 2.0) / (
(self.pixelsPerUnit * self.scale) * math.tan(math.radians(self.fov / 2.0))
)
lookAt = [
self.parent.map.position[0] * TILE_WIDTH,
self.parent.map.position[1] * TILE_HEIGHT,
self.parent.map.position[2] * TILE_DEPTH,
]
aspectRatio = vw / vh
# Ease the lookAt target
for i in range(3):
self.lookAtTarget[i] += (lookAt[i] - self.lookAtTarget[i]) * ease
# Camera position is now based on the eased lookAtTarget
cameraPosition = (
self.lookAtTarget[0],
self.lookAtTarget[1] + self.yOffset,
self.lookAtTarget[2] + z
)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
gluPerspective(self.fov, aspectRatio, 0.1, 1000.0)
glScalef(1.0, -1.0, 1.0) # Flip the projection matrix upside down
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
gluLookAt(
cameraPosition[0], cameraPosition[1], cameraPosition[2],
self.lookAtTarget[0], self.lookAtTarget[1], self.lookAtTarget[2],
0.0, 1.0, 0.0
)
+80
View File
@@ -0,0 +1,80 @@
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QGridLayout, QTreeWidget, QTreeWidgetItem, QComboBox
from tools.dusk.defs import CHUNK_WIDTH, CHUNK_HEIGHT, CHUNK_DEPTH, TILE_SHAPES
class ChunkPanel(QWidget):
def __init__(self, parent):
super().__init__(parent)
self.parent = parent
layout = QVBoxLayout(self)
# Tile shape dropdown
self.tileShapeDropdown = QComboBox()
self.tileShapeDropdown.addItems(TILE_SHAPES.keys())
self.tileShapeDropdown.setToolTip("Tile Shape")
layout.addWidget(self.tileShapeDropdown)
# Add expandable tree list
self.tree = QTreeWidget()
self.tree.setHeaderLabel("Chunks")
self.tree.expandAll() # Expand by default, remove if you want collapsed
layout.addWidget(self.tree) # Removed invalid stretch factor
# Add stretch so tree expands
layout.setStretchFactor(self.tree, 1)
# Event subscriptions
self.parent.map.onMapData.sub(self.onMapData)
self.parent.map.onPositionChange.sub(self.onPositionChange)
self.tileShapeDropdown.currentTextChanged.connect(self.onTileShapeChanged)
# For each chunk
for chunk in self.parent.map.chunks.values():
# Create tree element
item = QTreeWidgetItem(self.tree, ["Chunk ({}, {}, {})".format(chunk.x, chunk.y, chunk.z)])
chunk.chunkPanelTree = item
chunk.chunkPanelTree.setExpanded(True)
item.setData(0, 0, chunk) # Store chunk reference
chunk.onChunkData.sub(self.onChunkData)
def onMapData(self, data):
pass
def onPositionChange(self, pos):
self.updateChunkList()
tile = self.parent.map.getTileAtWorldPos(*self.parent.map.position)
if tile is None:
return
key = "TILE_SHAPE_NULL"
for k, v in TILE_SHAPES.items():
if v != tile.shape:
continue
key = k
break
self.tileShapeDropdown.setCurrentText(key)
def onTileShapeChanged(self, shape_key):
tile = self.parent.map.getTileAtWorldPos(*self.parent.map.position)
if tile is None or shape_key not in TILE_SHAPES:
return
tile.setShape(TILE_SHAPES[shape_key])
def updateChunkList(self):
# Clear existing items
currentChunk = self.parent.map.getChunkAtWorldPos(*self.parent.map.position)
# Example tree items
for chunk in self.parent.map.chunks.values():
title = "Chunk ({}, {}, {})".format(chunk.x, chunk.y, chunk.z)
if chunk == currentChunk:
title += " [C]"
if chunk.isDirty():
title += " *"
item = chunk.chunkPanelTree
item.setText(0, title)
def onChunkData(self, chunk):
self.updateChunkList()
+154
View File
@@ -0,0 +1,154 @@
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QComboBox, QHBoxLayout, QPushButton, QLineEdit, QListWidget, QListWidgetItem
from PyQt5.QtCore import Qt
from tools.dusk.entity import Entity
from tools.dusk.defs import CHUNK_WIDTH, CHUNK_HEIGHT, CHUNK_DEPTH, ENTITY_TYPES, ENTITY_TYPE_NULL
class EntityPanel(QWidget):
def __init__(self, parent):
super().__init__(parent)
self.parent = parent
layout = QVBoxLayout(self)
self.setLayout(layout)
# Top panel placeholder
topWidget = QLabel("Entity Editor")
layout.addWidget(topWidget)
# Name input
nameLayout = QHBoxLayout()
nameLabel = QLabel("Name:")
self.nameInput = QLineEdit()
nameLayout.addWidget(nameLabel)
nameLayout.addWidget(self.nameInput)
layout.addLayout(nameLayout)
# Entity Type dropdown (single selection)
typeLayout = QHBoxLayout()
typeLabel = QLabel("Type:")
self.typeDropdown = QComboBox()
self.typeDropdown.addItems(ENTITY_TYPES)
typeLayout.addWidget(typeLabel)
typeLayout.addWidget(self.typeDropdown)
layout.addLayout(typeLayout)
# Entity list and buttons
self.entityList = QListWidget()
self.entityList.addItems([])
layout.addWidget(self.entityList, stretch=1)
btnLayout = QHBoxLayout()
self.btnAdd = QPushButton("Add")
self.btnRemove = QPushButton("Remove")
btnLayout.addWidget(self.btnAdd)
btnLayout.addWidget(self.btnRemove)
layout.addLayout(btnLayout)
# Events
self.btnAdd.clicked.connect(self.onAddEntity)
self.btnRemove.clicked.connect(self.onRemoveEntity)
self.parent.map.onEntityData.sub(self.onEntityData)
self.parent.map.onPositionChange.sub(self.onPositionChange)
self.entityList.itemClicked.connect(self.onEntityClicked)
self.entityList.itemDoubleClicked.connect(self.onEntityDoubleClicked)
self.typeDropdown.currentIndexChanged.connect(self.onTypeSelected)
self.nameInput.textChanged.connect(self.onNameChanged)
# Call once to populate
self.onEntityData()
self.onEntityUnselect()
def onEntityUnselect(self):
self.entityList.setCurrentItem(None)
self.nameInput.setText("")
self.typeDropdown.setCurrentIndex(ENTITY_TYPE_NULL)
def onEntitySelect(self, entity):
self.entityList.setCurrentItem(entity.item)
self.nameInput.setText(entity.name)
self.typeDropdown.setCurrentIndex(entity.type)
def onEntityDoubleClicked(self, item):
entity = item.data(Qt.UserRole)
chunk = entity.chunk
worldX = (chunk.x * CHUNK_WIDTH) + entity.localX
worldY = (chunk.y * CHUNK_HEIGHT) + entity.localY
worldZ = (chunk.z * CHUNK_DEPTH) + entity.localZ
self.parent.map.moveTo(worldX, worldY, worldZ)
def onEntityClicked(self, item):
pass
def onAddEntity(self):
chunk = self.parent.map.getChunkAtWorldPos(*self.parent.map.position)
if chunk is None:
return
localX = (self.parent.map.position[0] - (chunk.x * CHUNK_WIDTH)) % CHUNK_WIDTH
localY = (self.parent.map.position[1] - (chunk.y * CHUNK_HEIGHT)) % CHUNK_HEIGHT
localZ = (self.parent.map.position[2] - (chunk.z * CHUNK_DEPTH)) % CHUNK_DEPTH
# Make sure there's not already an entity here
for ent in chunk.entities.values():
if ent.localX == localX and ent.localY == localY and ent.localZ == localZ:
print("Entity already exists at this location")
return
ent = chunk.addEntity(localX, localY, localZ)
def onRemoveEntity(self):
item = self.entityList.currentItem()
if item is None:
return
entity = item.data(Qt.UserRole)
if entity:
chunk = entity.chunk
chunk.removeEntity(entity)
pass
def onEntityData(self):
self.onEntityUnselect()
self.entityList.clear()
for chunk in self.parent.map.chunks.values():
for id, entity in chunk.entities.items():
item = QListWidgetItem(entity.name)
item.setData(Qt.UserRole, entity) # Store the entity object
entity.item = item
self.entityList.addItem(item)
# Select if there is something at current position
self.onPositionChange(self.parent.map.position)
def onPositionChange(self, position):
self.onEntityUnselect()
# Get Entity at..
chunk = self.parent.map.getChunkAtWorldPos(*position)
if chunk is None:
return
localX = (position[0] - (chunk.x * CHUNK_WIDTH)) % CHUNK_WIDTH
localY = (position[1] - (chunk.y * CHUNK_HEIGHT)) % CHUNK_HEIGHT
localZ = (position[2] - (chunk.z * CHUNK_DEPTH)) % CHUNK_DEPTH
for ent in chunk.entities.values():
if ent.localX != localX or ent.localY != localY or ent.localZ != localZ:
continue
self.onEntitySelect(ent)
self.entityList.setCurrentItem(ent.item)
break
def onTypeSelected(self, index):
item = self.entityList.currentItem()
if item is None:
return
entity = item.data(Qt.UserRole)
if entity:
entity.setType(index)
def onNameChanged(self, text):
item = self.entityList.currentItem()
if item is None:
return
entity = item.data(Qt.UserRole)
if entity:
entity.setName(text)
+41
View File
@@ -0,0 +1,41 @@
from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QOpenGLWidget
from OpenGL.GL import *
from OpenGL.GLU import *
class GLWidget(QOpenGLWidget):
def __init__(self, parent):
super().__init__(parent)
self.parent = parent
self.timer = QTimer(self)
self.timer.timeout.connect(self.update)
self.timer.start(16) # ~60 FPS
def initializeGL(self):
glClearColor(0.392, 0.584, 0.929, 1.0)
glEnable(GL_DEPTH_TEST)
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glEnable(GL_POLYGON_OFFSET_FILL)
glPolygonOffset(1.0, 1.0)
glDisable(GL_POLYGON_OFFSET_FILL)
def resizeGL(self, w, h):
pass
def paintGL(self):
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glLoadIdentity()
w = self.width()
h = self.height()
if h <= 0:
h = 1
if w <= 0:
w = 1
glViewport(0, 0, w, h)
self.parent.camera.setup(w, h)
self.parent.grid.draw()
self.parent.map.draw()
self.parent.selectBox.draw()
+46
View File
@@ -0,0 +1,46 @@
from OpenGL.GL import *
from tools.dusk.defs import TILE_WIDTH, TILE_HEIGHT, TILE_DEPTH
class Grid:
def __init__(self, lines=1000):
self.cellWidth = TILE_WIDTH
self.cellHeight = TILE_HEIGHT
self.lines = lines
self.enabled = True
def draw(self):
if not self.enabled:
return
center = [0.01,0.01,0.01]
halfWidth = self.cellWidth * self.lines // 2
halfHeight = self.cellHeight * self.lines // 2
# Draw origin axes
glBegin(GL_LINES)
# X axis - RED
glColor3f(1.0, 0.0, 0.0)
glVertex3f(center[0] - halfWidth, center[1], center[2])
glVertex3f(center[0] + halfWidth, center[1], center[2])
# Y axis - GREEN
glColor3f(0.0, 1.0, 0.0)
glVertex3f(center[0], center[1] - halfHeight, center[2])
glVertex3f(center[0], center[1] + halfHeight, center[2])
# Z axis - BLUE
glColor3f(0.0, 0.0, 1.0)
glVertex3f(center[0], center[1], center[2] - halfWidth)
glVertex3f(center[0], center[1], center[2] + halfWidth)
glEnd()
# Draw grid
glColor3f(0.8, 0.8, 0.8)
glBegin(GL_LINES)
for i in range(-self.lines // 2, self.lines // 2 + 1):
# Vertical lines
glVertex3f(center[0] + i * self.cellWidth, center[1] - halfHeight, center[2])
glVertex3f(center[0] + i * self.cellWidth, center[1] + halfHeight, center[2])
# Horizontal lines
glVertex3f(center[0] - halfWidth, center[1] + i * self.cellHeight, center[2])
glVertex3f(center[0] + halfWidth, center[1] + i * self.cellHeight, center[2])
glEnd()
+83
View File
@@ -0,0 +1,83 @@
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QLineEdit, QPushButton, QHBoxLayout, QMessageBox
from tools.dusk.defs import CHUNK_WIDTH, CHUNK_HEIGHT, CHUNK_DEPTH
class MapInfoPanel(QWidget):
def __init__(self, parent):
super().__init__(parent)
self.parent = parent
# Components
layout = QVBoxLayout()
mapTitleLabel = QLabel("Map Title")
self.mapTitleInput = QLineEdit()
layout.addWidget(mapTitleLabel)
layout.addWidget(self.mapTitleInput)
tilePositionLabel = QLabel("Tile Position")
layout.addWidget(tilePositionLabel)
tilePositionRow = QHBoxLayout()
self.tileXInput = QLineEdit()
self.tileXInput.setPlaceholderText("X")
tilePositionRow.addWidget(self.tileXInput)
self.tileYInput = QLineEdit()
self.tileYInput.setPlaceholderText("Y")
tilePositionRow.addWidget(self.tileYInput)
self.tileZInput = QLineEdit()
self.tileZInput.setPlaceholderText("Z")
tilePositionRow.addWidget(self.tileZInput)
self.tileGo = QPushButton("Go")
tilePositionRow.addWidget(self.tileGo)
layout.addLayout(tilePositionRow)
self.chunkPosLabel = QLabel()
layout.addWidget(self.chunkPosLabel)
self.chunkLabel = QLabel()
layout.addWidget(self.chunkLabel)
layout.addStretch()
self.setLayout(layout)
# Events
self.tileGo.clicked.connect(self.goToPosition)
self.tileXInput.returnPressed.connect(self.goToPosition)
self.tileYInput.returnPressed.connect(self.goToPosition)
self.tileZInput.returnPressed.connect(self.goToPosition)
self.parent.map.onPositionChange.sub(self.updatePositionLabels)
self.parent.map.onMapData.sub(self.onMapData)
self.mapTitleInput.textChanged.connect(self.onMapNameChange)
# Initial label setting
self.updatePositionLabels(self.parent.map.position)
def goToPosition(self):
try:
x = int(self.tileXInput.text())
y = int(self.tileYInput.text())
z = int(self.tileZInput.text())
self.parent.map.moveTo(x, y, z)
except ValueError:
QMessageBox.warning(self, "Invalid Input", "Please enter valid integer coordinates.")
def updatePositionLabels(self, pos):
self.tileXInput.setText(str(pos[0]))
self.tileYInput.setText(str(pos[1]))
self.tileZInput.setText(str(pos[2]))
chunkTileX = pos[0] % CHUNK_WIDTH
chunkTileY = pos[1] % CHUNK_HEIGHT
chunkTileZ = pos[2] % CHUNK_DEPTH
chunkTileIndex = chunkTileX + chunkTileY * CHUNK_WIDTH + chunkTileZ * CHUNK_WIDTH * CHUNK_HEIGHT
self.chunkPosLabel.setText(f"Chunk Position: {chunkTileX}, {chunkTileY}, {chunkTileZ} ({chunkTileIndex})")
chunkX = pos[0] // CHUNK_WIDTH
chunkY = pos[1] // CHUNK_HEIGHT
chunkZ = pos[2] // CHUNK_DEPTH
self.chunkLabel.setText(f"Chunk: {chunkX}, {chunkY}, {chunkZ}")
def onMapData(self, data):
self.updatePositionLabels(self.parent.map.position)
self.mapTitleInput.setText(data.get("mapName", ""))
def onMapNameChange(self, text):
self.parent.map.data['mapName'] = text
+51
View File
@@ -0,0 +1,51 @@
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QGridLayout, QPushButton, QTabWidget, QLabel
from tools.editor.map.chunkpanel import ChunkPanel
from tools.editor.map.entitypanel import EntityPanel
from tools.editor.map.regionpanel import RegionPanel
class MapLeftPanel(QWidget):
def __init__(self, parent):
super().__init__(parent)
self.parent = parent
layout = QVBoxLayout(self)
self.setLayout(layout)
# Nav buttons
self.chunkInfoLabel = QLabel("Tile Information")
layout.addWidget(self.chunkInfoLabel)
grid = QGridLayout()
self.btnUp = QPushButton("U")
self.btnN = QPushButton("N")
self.btnDown = QPushButton("D")
self.btnW = QPushButton("W")
self.btnS = QPushButton("S")
self.btnE = QPushButton("E")
grid.addWidget(self.btnUp, 0, 0)
grid.addWidget(self.btnN, 0, 1)
grid.addWidget(self.btnDown, 0, 2)
grid.addWidget(self.btnW, 1, 0)
grid.addWidget(self.btnS, 1, 1)
grid.addWidget(self.btnE, 1, 2)
layout.addLayout(grid)
# Panels
self.chunkPanel = ChunkPanel(self.parent)
self.entityPanel = EntityPanel(self.parent)
self.regionPanel = RegionPanel(self.parent)
# Tabs
self.tabs = QTabWidget()
self.tabs.addTab(self.chunkPanel, "Tiles")
self.tabs.addTab(self.entityPanel, "Entities")
self.tabs.addTab(self.regionPanel, "Regions")
self.tabs.addTab(None, "Triggers")
layout.addWidget(self.tabs)
# Event subscriptions
self.btnN.clicked.connect(lambda: self.parent.map.moveRelative(0, -1, 0))
self.btnS.clicked.connect(lambda: self.parent.map.moveRelative(0, 1, 0))
self.btnE.clicked.connect(lambda: self.parent.map.moveRelative(1, 0, 0))
self.btnW.clicked.connect(lambda: self.parent.map.moveRelative(-1, 0, 0))
self.btnUp.clicked.connect(lambda: self.parent.map.moveRelative(0, 0, 1))
self.btnDown.clicked.connect(lambda: self.parent.map.moveRelative(0, 0, -1))
+46
View File
@@ -0,0 +1,46 @@
import os
from PyQt5.QtWidgets import QAction, QMenuBar, QFileDialog
from PyQt5.QtGui import QKeySequence
from tools.dusk.map import MAP_DEFAULT_PATH
class MapMenubar:
def __init__(self, parent):
self.parent = parent
self.menubar = QMenuBar(parent)
parent.setMenuBar(self.menubar)
self.fileMenu = self.menubar.addMenu("File")
self.actionNew = QAction("New", parent)
self.actionOpen = QAction("Open", parent)
self.actionSave = QAction("Save", parent)
self.actionSaveAs = QAction("Save As", parent)
self.actionNew.setShortcut(QKeySequence("Ctrl+N"))
self.actionOpen.setShortcut(QKeySequence("Ctrl+O"))
self.actionSave.setShortcut(QKeySequence("Ctrl+S"))
self.actionSaveAs.setShortcut(QKeySequence("Ctrl+Shift+S"))
self.actionNew.triggered.connect(self.newFile)
self.actionOpen.triggered.connect(self.openFile)
self.actionSave.triggered.connect(self.saveFile)
self.actionSaveAs.triggered.connect(self.saveAsFile)
self.fileMenu.addAction(self.actionNew)
self.fileMenu.addAction(self.actionOpen)
self.fileMenu.addAction(self.actionSave)
self.fileMenu.addAction(self.actionSaveAs)
def newFile(self):
self.parent.map.newFile()
def openFile(self):
filePath, _ = QFileDialog.getOpenFileName(self.menubar, "Open Map File", MAP_DEFAULT_PATH, "Map Files (*.json)")
if filePath:
self.parent.map.load(filePath)
def saveFile(self):
self.parent.map.save()
def saveAsFile(self):
filePath, _ = QFileDialog.getSaveFileName(self.menubar, "Save Map File As", MAP_DEFAULT_PATH, "Map Files (*.json)")
if filePath:
self.parent.map.save(filePath)
+15
View File
@@ -0,0 +1,15 @@
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLabel, QComboBox, QHBoxLayout, QPushButton, QLineEdit, QListWidget, QListWidgetItem
from PyQt5.QtCore import Qt
from tools.dusk.entity import Entity
from tools.dusk.defs import CHUNK_WIDTH, CHUNK_HEIGHT, CHUNK_DEPTH, ENTITY_TYPES, ENTITY_TYPE_NULL
class RegionPanel(QWidget):
def __init__(self, parent):
super().__init__(parent)
self.parent = parent
layout = QVBoxLayout(self)
self.setLayout(layout)
# Top panel placeholder
topWidget = QLabel("Region Editor")
layout.addWidget(topWidget)
+44
View File
@@ -0,0 +1,44 @@
import OpenGL.GL as gl
from tools.dusk.defs import defs
import colorsys
from tools.dusk.defs import TILE_WIDTH, TILE_HEIGHT, TILE_DEPTH
class SelectBox:
def __init__(self, parent):
self.parent = parent
self.hue = 0.0
def draw(self):
position = [
self.parent.map.position[0] * TILE_WIDTH - (TILE_WIDTH / 2.0),
self.parent.map.position[1] * TILE_HEIGHT - (TILE_HEIGHT / 2.0),
self.parent.map.position[2] * TILE_DEPTH - (TILE_DEPTH / 2.0)
]
vertices = [
(position[0], position[1], position[2]),
(position[0]+TILE_WIDTH, position[1], position[2]),
(position[0]+TILE_WIDTH, position[1]+TILE_HEIGHT, position[2]),
(position[0], position[1]+TILE_HEIGHT, position[2]),
(position[0], position[1], position[2]+TILE_DEPTH),
(position[0]+TILE_WIDTH, position[1], position[2]+TILE_DEPTH),
(position[0]+TILE_WIDTH, position[1]+TILE_HEIGHT, position[2]+TILE_DEPTH),
(position[0], position[1]+TILE_HEIGHT, position[2]+TILE_DEPTH)
]
edges = [
(0, 1), (1, 2), (2, 3), (3, 0), # bottom face
(4, 5), (5, 6), (6, 7), (7, 4), # top face
(4, 5), (5, 6), (6, 7), (7, 4), # top face
(0, 4), (1, 5), (2, 6), (3, 7) # vertical edges
]
# Cycle hue
self.hue = (self.hue + 0.01) % 1.0
r, g, b = colorsys.hsv_to_rgb(self.hue, 1.0, 1.0)
gl.glColor3f(r, g, b)
gl.glLineWidth(2.0)
gl.glBegin(gl.GL_LINES)
for edge in edges:
for vertex in edge:
gl.glVertex3f(*vertices[vertex])
gl.glEnd()
+18
View File
@@ -0,0 +1,18 @@
from PyQt5.QtWidgets import QStatusBar, QLabel
class StatusBar(QStatusBar):
def __init__(self, parent=None):
super().__init__(parent)
self.parent = parent
self.leftLabel = QLabel("")
self.rightLabel = QLabel("")
self.addWidget(self.leftLabel, 1)
self.addPermanentWidget(self.rightLabel)
parent.map.onMapData.sub(self.onMapData)
def setStatus(self, message):
self.leftLabel.setText(message)
def onMapData(self, data):
self.rightLabel.setText(self.parent.map.mapFileName if self.parent.map.mapFileName else "Untitled.json")
+80
View File
@@ -0,0 +1,80 @@
from OpenGL.GL import *
import array
class VertexBuffer:
def __init__(self, componentsPerVertex=3):
self.componentsPerVertex = componentsPerVertex
self.vertices = []
self.colors = []
self.uvs = []
self.data = None
self.colorData = None
self.uvData = None
def buildData(self):
hasColors = len(self.colors) > 0
hasUvs = len(self.uvs) > 0
vertexCount = len(self.vertices) // self.componentsPerVertex
dataList = []
colorList = []
uvList = []
for i in range(vertexCount):
vStart = i * self.componentsPerVertex
dataList.extend(self.vertices[vStart:vStart+self.componentsPerVertex])
if hasColors:
cStart = i * 4 # Assuming RGBA
colorList.extend(self.colors[cStart:cStart+4])
if hasUvs:
uStart = i * 2 # Assuming UV
uvList.extend(self.uvs[uStart:uStart+2])
self.data = array.array('f', dataList)
if hasColors:
self.colorData = array.array('f', colorList)
else:
self.colorData = None
if hasUvs:
self.uvData = array.array('f', uvList)
else:
self.uvData = None
def draw(self, mode=GL_TRIANGLES, count=-1):
if count == -1:
count = len(self.data) // self.componentsPerVertex
if count == 0:
return
glEnableClientState(GL_VERTEX_ARRAY)
glVertexPointer(self.componentsPerVertex, GL_FLOAT, 0, self.data.tobytes())
if self.colorData:
glEnableClientState(GL_COLOR_ARRAY)
glColorPointer(4, GL_FLOAT, 0, self.colorData.tobytes())
if self.uvData:
glEnableClientState(GL_TEXTURE_COORD_ARRAY)
glTexCoordPointer(2, GL_FLOAT, 0, self.uvData.tobytes())
glDrawArrays(mode, 0, count)
glDisableClientState(GL_VERTEX_ARRAY)
if self.colorData:
glDisableClientState(GL_COLOR_ARRAY)
if self.uvData:
glDisableClientState(GL_TEXTURE_COORD_ARRAY)
def clear(self):
self.vertices = []
self.colors = []
self.uvs = []
self.data = array.array('f')
self.colorData = None
self.uvData = None
+143
View File
@@ -0,0 +1,143 @@
import os
from PyQt5.QtWidgets import QMainWindow, QWidget, QHBoxLayout, QMessageBox
from PyQt5.QtCore import Qt
from tools.editor.map.glwidget import GLWidget
from tools.editor.map.mapleftpanel import MapLeftPanel
from tools.editor.map.mapinfopanel import MapInfoPanel
from tools.editor.map.menubar import MapMenubar
from tools.editor.map.statusbar import StatusBar
from tools.dusk.map import Map
from tools.dusk.defs import CHUNK_WIDTH, CHUNK_HEIGHT, CHUNK_DEPTH, TILE_SHAPE_NULL, TILE_SHAPE_FLOOR
from tools.editor.map.selectbox import SelectBox
from tools.editor.map.camera import Camera
from tools.editor.map.grid import Grid
class MapWindow(QMainWindow):
def __init__(self):
super().__init__()
self.insertPressed = False
self.deletePressed = False
# Subclasses
self.map = Map(self)
self.camera = Camera(self)
self.grid = Grid()
self.selectBox = SelectBox(self)
# Window setup
self.setWindowTitle("Dusk Map Editor")
self.resize(1600, 900)
# Menubar (TESTING)
self.menubar = MapMenubar(self)
central = QWidget()
self.setCentralWidget(central)
mainLayout = QHBoxLayout(central)
# Left panel (tabs + nav buttons)
self.leftPanel = MapLeftPanel(self)
self.leftPanel.setFixedWidth(350)
mainLayout.addWidget(self.leftPanel)
# Center panel (GLWidget + controls)
self.glWidget = GLWidget(self)
mainLayout.addWidget(self.glWidget, stretch=3)
# Right panel (MapInfoPanel)
self.mapInfoPanel = MapInfoPanel(self)
rightWidget = self.mapInfoPanel
rightWidget.setFixedWidth(250)
mainLayout.addWidget(rightWidget)
# Status bar
self.statusBar = StatusBar(self)
self.setStatusBar(self.statusBar)
self.installEventFilter(self)
self.installEventFilterRecursively(self)
def installEventFilterRecursively(self, widget):
for child in widget.findChildren(QWidget):
child.installEventFilter(self)
self.installEventFilterRecursively(child)
def closeEvent(self, event):
if not self.map.isDirty():
event.accept()
return
reply = QMessageBox.question(
self,
"Unsaved Changes",
"You have unsaved changes. Do you want to save before closing?",
QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel,
QMessageBox.Save
)
if reply == QMessageBox.Save:
self.map.save()
elif reply == QMessageBox.Cancel:
event.ignore()
return
event.accept()
def eventFilter(self, obj, event):
if event.type() == event.KeyPress:
amtX, amtY, amtZ = 0, 0, 0
key = event.key()
if key == Qt.Key_Left:
amtX = -1
elif key == Qt.Key_Right:
amtX = 1
elif key == Qt.Key_Up:
amtY = -1
elif key == Qt.Key_Down:
amtY = 1
elif key == Qt.Key_PageUp:
amtZ = 1
elif key == Qt.Key_PageDown:
amtZ = -1
if event.modifiers() & Qt.ShiftModifier:
amtX *= CHUNK_WIDTH
amtY *= CHUNK_HEIGHT
amtZ *= CHUNK_DEPTH
if amtX != 0 or amtY != 0 or amtZ != 0:
self.map.moveRelative(amtX, amtY, amtZ)
if self.insertPressed:
tile = self.map.getTileAtWorldPos(*self.map.position)
if tile is not None and tile.shape == TILE_SHAPE_NULL:
tile.setShape(TILE_SHAPE_FLOOR)
if self.deletePressed:
tile = self.map.getTileAtWorldPos(*self.map.position)
if tile is not None:
tile.setShape(TILE_SHAPE_NULL)
event.accept()
return True
if key == Qt.Key_Delete:
tile = self.map.getTileAtWorldPos(*self.map.position)
self.deletePressed = True
if tile is not None:
tile.setShape(TILE_SHAPE_NULL)
event.accept()
return True
if key == Qt.Key_Insert:
tile = self.map.getTileAtWorldPos(*self.map.position)
self.insertPressed = True
if tile is not None and tile.shape == TILE_SHAPE_NULL:
tile.setShape(TILE_SHAPE_FLOOR)
event.accept()
elif event.type() == event.KeyRelease:
key = event.key()
if key == Qt.Key_Delete:
self.deletePressed = False
event.accept()
return True
if key == Qt.Key_Insert:
self.insertPressed = False
event.accept()
return super().eventFilter(obj, event)
-257
View File
@@ -1,257 +0,0 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "fixed.h"
#include "assert/assert.h"
float_t fx248Fromi32(const int32_t b) {
return (float_t)b << FIXED248_FRACTION_BITS;
}
float_t fx248Fromu32(const uint32_t b) {
return (float_t)((int32_t)b << FIXED248_FRACTION_BITS);
}
float_t fx248Fromf32(const float_t b) {
return (float_t)(b * (1 << FIXED248_FRACTION_BITS));
}
float_t fx248Fromu16(const uint16_t b) {
return (float_t)((int32_t)b << FIXED248_FRACTION_BITS);
}
float_t fx248Fromu8(const uint8_t b) {
return (float_t)((int32_t)b << FIXED248_FRACTION_BITS);
}
int32_t fx248Toi32(const float_t a) {
return a >> FIXED248_FRACTION_BITS;
}
uint32_t fx248Tou32(const float_t a) {
return (uint32_t)(a >> FIXED248_FRACTION_BITS);
}
float_t fx248Tof32(const float_t a) {
return (float_t)a / (1 << FIXED248_FRACTION_BITS);
}
uint16_t fx248Tou16(const float_t a) {
return (uint16_t)(a >> FIXED248_FRACTION_BITS);
}
uint8_t fx248Tou8(const float_t a) {
return (uint8_t)(a >> FIXED248_FRACTION_BITS);
}
float_t fx248Addfx248(const float_t a, const float_t b) {
return a + b;
}
float_t fx248Addi32(const float_t a, const int32_t b) {
return fx248Addfx248(a, fx248Fromi32(b));
}
float_t fx248Addu32(const float_t a, const uint32_t b) {
return fx248Addfx248(a, fx248Fromu32(b));
}
float_t fx248Addf32(const float_t a, const float_t b) {
return fx248Addfx248(a, fx248Fromf32(b));
}
float_t fx248Subfx248(const float_t a, const float_t b) {
return a - b;
}
float_t fx248Subi32(const float_t a, const int32_t b) {
return fx248Subfx248(a, fx248Fromi32(b));
}
float_t fx248Subu32(const float_t a, const uint32_t b) {
return fx248Subfx248(a, fx248Fromu32(b));
}
float_t fx248Subf32(const float_t a, const float_t b) {
return fx248Subfx248(a, fx248Fromf32(b));
}
float_t fx248Mulfx248(const float_t a, const float_t b) {
return (float_t)(((int64_t)a * (int64_t)b) >> FIXED248_FRACTION_BITS);
}
float_t fx248Muli32(const float_t a, const int32_t b) {
return (float_t)(((int64_t)a * (int64_t)b) >> FIXED248_FRACTION_BITS);
}
float_t fx248Mulu32(const float_t a, const uint32_t b) {
return (float_t)(
((int64_t)a * (int64_t)(int32_t)b
) >> FIXED248_FRACTION_BITS);
}
float_t fx248Mulf32(const float_t a, const float_t b) {
return (float_t)((
(int64_t)a * (int64_t)(b * (1 << FIXED248_FRACTION_BITS))
) >> FIXED248_FRACTION_BITS);
}
float_t fx248Divfx248(const float_t a, const float_t b) {
assertFalse(b == 0, "Division by zero in fx248Divfx248");
return (float_t)(((int64_t)a << FIXED248_FRACTION_BITS) / (int64_t)b);
}
float_t fx248Divi32(const float_t a, const int32_t b) {
assertFalse(b == 0, "Division by zero in fx248Divi32");
return (float_t)(((int64_t)a << FIXED248_FRACTION_BITS) / (int64_t)b);
}
float_t fx248Divu32(const float_t a, const uint32_t b) {
assertFalse(b == 0, "Division by zero in fx248Divu32");
return (float_t)(
((int64_t)a << FIXED248_FRACTION_BITS
) / (int64_t)(int32_t)b);
}
float_t fx248Divf32(const float_t a, const float_t b) {
assertFalse(b == 0, "Division by zero in fx248Divf32");
return (float_t)((
(int64_t)a << FIXED248_FRACTION_BITS
) / (int64_t)(b * (1 << FIXED248_FRACTION_BITS)));
}
float_t fx248Floor(const float_t a) {
return a & ~((1 << FIXED248_FRACTION_BITS) - 1);
}
float_t fx248Ceil(const float_t a) {
if(a & ((1 << FIXED248_FRACTION_BITS) - 1)) {
return (a & ~((1 << FIXED248_FRACTION_BITS) - 1)) + (1 << FIXED248_FRACTION_BITS);
}
return a;
}
float_t fx248Round(const float_t a) {
if(a & ((1 << (FIXED248_FRACTION_BITS - 1)) - 1)) {
return (a & ~((1 << FIXED248_FRACTION_BITS) - 1)) + (1 << FIXED248_FRACTION_BITS);
}
return a & ~((1 << FIXED248_FRACTION_BITS) - 1);
}
uint32_t fx248Flooru32(const float_t a) {
return (uint32_t)((a >> FIXED248_FRACTION_BITS) & 0xFFFFFFFF);
}
uint32_t fx248Ceilu32(const float_t a) {
return (uint32_t)(((a + ((1 << FIXED248_FRACTION_BITS) - 1)) >> FIXED248_FRACTION_BITS) & 0xFFFFFFFF);
}
uint32_t fx248Roundu32(const float_t a) {
return (uint32_t)(((a + (1 << (FIXED248_FRACTION_BITS - 1))) >> FIXED248_FRACTION_BITS) & 0xFFFFFFFF);
}
float_t fx248Sqrt(const float_t a) {
if(a == 0) return 0;
float_t y = a > FIXED248(1, 0) ? a : FIXED248(1, 0);
float_t last = 0;
int max_iter = 16;
while(y != last && max_iter-- > 0) {
last = y;
int32_t div = (int32_t)(((int64_t)a << FIXED248_FRACTION_BITS) / y);
y = (y + div) >> 1;
}
return y;
}
float_t fx248Max(const float_t a, const float_t b) {
return (a > b) ? a : b;
}
float_t fx248Min(const float_t a, const float_t b) {
return (a < b) ? a : b;
}
float_t fx248Clamp(
const float_t a,
const float_t min,
const float_t max
) {
return (a < min) ? min : (a > max) ? max : a;
}
float_t fx248Abs(const float_t a) {
return (a < 0) ? -a : a;
}
float_t fx248Atan2(
const float_t y,
const float_t x
) {
// Handle special cases
if (x == 0) {
if (y > 0) return FX248_HALF_PI;
if (y < 0) return -FX248_HALF_PI;
return 0;
}
// Use absolute values for quadrant correction
float_t abs_y = y;
if (abs_y < 0) abs_y = -abs_y;
float_t angle;
if (abs_y < fx248Abs(x)) {
float_t z = fx248Divfx248(y, x);
float_t z2 = fx248Mulfx248(z, z);
float_t z3 = fx248Mulfx248(z2, z);
float_t z5 = fx248Mulfx248(z3, z2);
angle = fx248Subfx248(
fx248Addfx248(z, fx248Divfx248(z5, fx248Fromi32(5))),
fx248Divfx248(z3, fx248Fromi32(3))
);
if (x < 0) {
if (y < 0) {
angle -= FX248_PI;
} else {
angle += FX248_PI;
}
}
} else {
float_t z = fx248Divfx248(x, y);
float_t z2 = fx248Mulfx248(z, z);
float_t z3 = fx248Mulfx248(z2, z);
float_t z5 = fx248Mulfx248(z3, z2);
angle = fx248Subfx248(
fx248Addfx248(z, fx248Divfx248(z5, fx248Fromi32(5))),
fx248Divfx248(z3, fx248Fromi32(3))
);
if (y > 0) {
angle = FX248_HALF_PI - angle;
} else {
angle = -FX248_HALF_PI - angle;
}
}
return angle;
}
-379
View File
@@ -1,379 +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 int32_t float_t;
#define FIXED248_FRACTION_BITS 8
#define FIXED248_HIGH_MULTIPLIER (1 << FIXED248_FRACTION_BITS)
#define FIXED248_MIN INT32_MIN
#define FIXED248_MAX INT32_MAX
#define FIXED248(i, f) ((float_t)( \
((i) << FIXED248_FRACTION_BITS) + \
(((f) * FIXED248_HIGH_MULTIPLIER) / 100) \
))
#define FIXED248_ONE (FIXED248(1, 0))
#define FIXED248_ZERO (FIXED248(0, 0))
#define FX248_PI 804
#define FX248_HALF_PI 402
#define FX248_3PI_4 603
#define FX248_NEG_PI -804
/**
* Convert an int32_t value to a float_t value.
*
* @param b The int32_t value to convert.
* @return The converted float_t value.
*/
float_t fx248Fromi32(const int32_t b);
/**
* Convert a uint32_t value to a float_t value.
*
* @param b The uint32_t value to convert.
* @return The converted float_t value.
*/
float_t fx248Fromu32(const uint32_t b);
/**
* Convert a float_t value to a float_t value.
*
* @param b The float_t value to convert.
* @return The converted float_t value.
*/
float_t fx248Fromf32(const float_t b);
/**
* Convert a uint16_t value to a float_t value.
*
* @param b The uint16_t value to convert.
* @return The converted float_t value.
*/
float_t fx248Fromu16(const uint16_t b);
/**
* Convert a uint8_t value to a float_t value.
*
* @param b The uint8_t value to convert.
* @return The converted float_t value.
*/
float_t fx248Fromu8(const uint8_t b);
/**
* Convert a float_t value to an int32_t value.
*
* @param a The float_t value to convert.
* @return The converted int32_t value.
*/
int32_t fx248Toi32(const float_t a);
/**
* Convert a float_t value to a uint32_t value.
*
* @param a The float_t value to convert.
* @return The converted uint32_t value.
*/
uint32_t fx248Tou32(const float_t a);
/**
* Convert a float_t value to a float_t value.
*
* @param a The float_t value to convert.
* @return The converted float_t value.
*/
float_t fx248Tof32(const float_t a);
/**
* Convert a float_t value to a uint16_t value.
*
* @param a The float_t value to convert.
* @return The converted uint16_t value.
*/
uint16_t fx248Tou16(const float_t a);
/**
* Convert a float_t value to an uint8_t value.
*
* @param a The float_t value to convert.
* @return The converted uint8_t value.
*/
uint8_t fx248Tou8(const float_t a);
/**
* Add a float_t value to another float_t value.
*
* @param a First float_t value.
* @param b Second float_t value to add to the first value.
* @return The result of the addition as a float_t value.
*/
float_t fx248Addfx248(const float_t a, const float_t b);
/**
* Add an int32_t value to a float_t value.
*
* @param a The float_t value to which the int32_t will be added.
* @param b The int32_t value to add to the float_t value.
* @return The result of the addition as a float_t value.
*/
float_t fx248Addi32(const float_t a, const int32_t b);
/**
* Add a uint32_t value to a float_t value.
*
* @param a The float_t value to which the uint32_t will be added.
* @param b The uint32_t value to add to the float_t value.
* @return The result of the addition as a float_t value.
*/
float_t fx248Addu32(const float_t a, const uint32_t b);
/**
* Add a float_t value to a float_t value.
*
* @param a Pointer to the float_t value (will be modified).
* @param b The float_t value to add to the float_t value.
* @return The result of the addition as a float_t value.
*/
float_t fx248Addf32(const float_t a, const float_t b);
/**
* Subtract a float_t value from another float_t value.
*
* @param a First float_t value.
* @param b The float_t value to subtract from the first value.
* @return The result of the subtraction as a float_t value.
*/
float_t fx248Subfx248(const float_t a, const float_t b);
/**
* Subtract an int32_t value from a float_t value.
*
* @param a The float_t value from which the int32_t will be subtracted.
* @param b The int32_t value to subtract from the float_t value.
* @return The result of the subtraction as a float_t value.
*/
float_t fx248Subi32(const float_t a, const int32_t b);
/**
* Subtract a uint32_t value from a float_t value.
*
* @param a The float_t value from which the uint32_t will be subtracted.
* @param b The uint32_t value to subtract from the float_t value.
* @return The result of the subtraction as a float_t value.
*/
float_t fx248Subu32(const float_t a, const uint32_t b);
/**
* Subtract a float_t value from a float_t value.
*
* @param a The float_t value from which the float_t will be subtracted.
* @param b The float_t value to subtract from the float_t value.
* @return The result of the subtraction as a float_t value.
*/
float_t fx248Subf32(const float_t a, const float_t b);
/**
* Multiply two float_t values.
*
* @param a First float_t value.
* @param b Second float_t value to multiply with the first value.
* @return The result of the multiplication as a float_t value.
*/
float_t fx248Mulfx248(const float_t a, const float_t b);
/**
* Multiply a float_t value by an int32_t value.
*
* @param a The float_t value to multiply.
* @param b The int32_t value to multiply with the float_t value.
* @return The result of the multiplication as a float_t value.
*/
float_t fx248Muli32(const float_t a, const int32_t b);
/**
* Multiply a float_t value by a uint32_t value.
*
* @param a The float_t value to multiply.
* @param b The uint32_t value to multiply with the float_t value.
* @return The result of the multiplication as a float_t value.
*/
float_t fx248Mulu32(const float_t a, const uint32_t b);
/**
* Multiply a float_t value by a float_t value.
*
* @param a The float_t value to multiply.
* @param b The float_t value to multiply with the float_t value.
* @return The result of the multiplication as a float_t value.
*/
float_t fx248Mulf32(const float_t a, const float_t b);
/**
* Divide two float_t values.
*
* @param a The float_t value to be divided.
* @param b The float_t value to divide by.
* @return The result of the division as a float_t value.
*/
float_t fx248Divfx248(const float_t a, const float_t b);
/**
* Divide a float_t value by an int32_t value.
*
* @param a The float_t value to be divided.
* @param b The int32_t value to divide by.
* @return The result of the division as a float_t value.
*/
float_t fx248Divi32(const float_t a, const int32_t b);
/**
* Divide a float_t value by a uint32_t value.
*
* @param a The float_t value to be divided.
* @param b The uint32_t value to divide by.
* @return The result of the division as a float_t value.
*/
float_t fx248Divu32(const float_t a, const uint32_t b);
/**
* Divide a float_t value by a float_t value.
*
* @param a The float_t value to be divided.
* @param b The float_t value to divide by.
* @return The result of the division as a float_t value.
*/
float_t fx248Divf32(const float_t a, const float_t b);
/**
* Convert a float_t value to an int32_t value, rounding towards zero.
*
* @param a The float_t value to convert.
* @return The converted int32_t value.
*/
float_t fx248Floor(const float_t a);
/**
* Convert a float_t value to an int32_t value, rounding towards positive
* infinity.
*
* @param a The float_t value to convert.
* @return The converted int32_t value.
*/
float_t fx248Ceil(const float_t a);
/**
* Convert a float_t value to an int32_t value, rounding to the nearest
* integer.
*
* @param a The float_t value to convert.
* @return The converted int32_t value.
*/
float_t fx248Round(const float_t a);
/**
* Convert a float_t value to a uint32_t value, rounding towards zero.
*
* @param a The float_t value to convert.
* @return The converted uint32_t value.
*/
uint32_t fx248Flooru32(const float_t a);
/**
* Convert a float_t value to a uint32_t value, rounding towards positive
* infinity.
*
* @param a The float_t value to convert.
* @return The converted uint32_t value.
*/
uint32_t fx248Ceilu32(const float_t a);
/**
* Convert a float_t value to a uint32_t value, rounding to the nearest
* integer.
*
* @param a The float_t value to convert.
* @return The converted uint32_t value.
*/
uint32_t fx248Roundu32(const float_t a);
/**
* Returns the square root of a float_t value.
*
* @param a The float_t value to calculate the square root of.
*/
float_t fx248Sqrt(const float_t a);
/**
* Returns the maximum of two float_t values.
*
* @param a First float_t value.
* @param b Second float_t value.
* @return The maximum of the two values.
*/
float_t fx248Max(const float_t a, const float_t b);
/**
* Returns the minimum of two float_t values.
*
* @param a First float_t value.
* @param b Second float_t value.
* @return The minimum of the two values.
*/
float_t fx248Min(const float_t a, const float_t b);
/**
* Clamp a float_t value between a minimum and maximum value.
*
* @param a The float_t value to clamp.
* @param min The minimum value to clamp to.
* @param max The maximum value to clamp to.
* @return The clamped float_t value.
*/
float_t fx248Clamp(
const float_t a,
const float_t min,
const float_t max
);
/**
* Returns the absolute value of a float_t value.
*
* @param a The float_t value to calculate the absolute value of.
* @return The absolute value as a float_t value.
*/
float_t fx248Abs(const float_t a);
/**
* Calculate the arctangent of a float_t value.
*
* @param y Y coordinate value.
* @param x X coordinate value.
* @return The arctangent of the value as a float_t value.
*/
float_t fx248Atan2(
const float_t y,
const float_t x
);
+13
View File
@@ -0,0 +1,13 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
assettexture.c
assettileset.c
assetlanguage.c
assetscript.c
)
+45
View File
@@ -0,0 +1,45 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "asset/asset.h"
#include "assert/assert.h"
#include "locale/localemanager.h"
errorret_t assetLanguageHandler(assetcustom_t custom) {
assertNotNull(custom.zipFile, "Custom asset zip file cannot be NULL");
assertNotNull(custom.output, "Custom asset output cannot be NULL");
assetlanguage_t *lang = (assetlanguage_t *)custom.output;
errorChain(assetLanguageInit(lang, custom.zipFile));
errorOk();
}
errorret_t assetLanguageInit(
assetlanguage_t *lang,
zip_file_t *zipFile
) {
errorThrow("Language asset initialization is not yet implemented.");
}
errorret_t assetLanguageRead(
assetlanguage_t *lang,
const uint32_t key,
char_t *buffer,
const uint32_t bufferSize,
uint32_t *outLength
) {
errorThrow("Language string reading is not yet implemented.");
}
void assetLanguageDispose(assetlanguage_t *lang) {
assertNotNull(lang, "Language asset cannot be NULL");
if(lang->zip) {
zip_fclose(lang->zip);
}
}
+61
View File
@@ -0,0 +1,61 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "error/error.h"
#include "duskdefs.h"
#include <zip.h>
typedef struct {
zip_file_t *zip;
zip_int64_t chunksOffset;
} assetlanguage_t;
typedef struct assetcustom_s assetcustom_t;
/**
* Receiving function from the asset manager to handle language assets.
*
* @param custom Custom asset loading data.
* @return Error code.
*/
errorret_t assetLanguageHandler(assetcustom_t custom);
/**
* Initializes a language asset and loads the header data into memory.
*
* @param lang Language asset to initialize.
* @param zipFile Zip file handle for the language asset.
* @return Error code.
*/
errorret_t assetLanguageInit(assetlanguage_t *lang, zip_file_t *zipFile);
/**
* Reads a string from the language asset into the provided buffer.
*
* @param lang Language asset to read from.
* @param key Language key to read.
* @param buffer Buffer to read the string into.
* @param bufferSize Size of the provided buffer.
* @param outLength Pointer to store the length of the string read.
* @return Error code.
*/
errorret_t assetLanguageRead(
assetlanguage_t *lang,
const uint32_t key,
char_t *buffer,
const uint32_t bufferSize,
uint32_t *outLength
);
/**
* Disposes of language asset resources.
*
* @param custom Custom asset loading data.
* @return Error code.
*/
void assetLanguageDispose(assetlanguage_t *lang);
+58
View File
@@ -0,0 +1,58 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "asset/asset.h"
#include "assert/assert.h"
errorret_t assetScriptHandler(assetcustom_t custom) {
assertNotNull(custom.zipFile, "Custom asset zip file cannot be NULL");
assertNotNull(custom.output, "Custom asset output cannot be NULL");
assetscript_t *script = (assetscript_t *)custom.output;
errorChain(assetScriptInit(script, custom.zipFile));
errorOk();
}
errorret_t assetScriptInit(
assetscript_t *script,
zip_file_t *zipFile
) {
assertNotNull(script, "Script asset cannot be NULL");
assertNotNull(zipFile, "Zip file cannot be NULL");
// We now own the zip file handle.
script->zip = zipFile;
errorOk();
}
const char_t * assetScriptReader(lua_State* lState, void* data, size_t* size) {
assetscript_t *script = (assetscript_t *)data;
zip_int64_t bytesRead = zip_fread(
script->zip, script->buffer, sizeof(script->buffer)
);
if(bytesRead < 0) {
*size = 0;
return NULL;
}
*size = (size_t)bytesRead;
return script->buffer;
}
errorret_t assetScriptDispose(assetscript_t *script) {
assertNotNull(script, "Script asset cannot be NULL");
if(script->zip != NULL) {
zip_fclose(script->zip);
script->zip = NULL;
}
errorOk();
}
+57
View File
@@ -0,0 +1,57 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "error/error.h"
#include "duskdefs.h"
#include <zip.h>
#include "script/scriptcontext.h"
#define ASSET_SCRIPT_BUFFER_SIZE 1024
typedef struct assetscript_s {
zip_file_t *zip;
char_t buffer[ASSET_SCRIPT_BUFFER_SIZE];
} assetscript_t;
typedef struct assetcustom_s assetcustom_t;
/**
* Receiving function from the asset manager to handle script assets.
*
* @param custom Custom asset loading data.
* @return Error code.
*/
errorret_t assetScriptHandler(assetcustom_t custom);
/**
* Initializes a script asset.
*
* @param script Script asset to initialize.
* @param zipFile Zip file handle for the script asset.
* @return Error code.
*/
errorret_t assetScriptInit(assetscript_t *script, zip_file_t *zipFile);
/**
* Reader function for Lua to read script data from the asset.
*
* @param L Lua state.
* @param data Pointer to the assetscript_t structure.
* @param size Pointer to store the size of the read data.
* @return Pointer to the read data buffer.
*/
const char_t * assetScriptReader(lua_State* L, void* data, size_t* size);
/**
* Disposes of a script asset, freeing any allocated resources.
*
* @param script Script asset to dispose of.
* @return Error code.
*/
errorret_t assetScriptDispose(assetscript_t *script);
+78
View File
@@ -0,0 +1,78 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "assettexture.h"
#include "asset/assettype.h"
#include "assert/assert.h"
#include "display/texture/texture.h"
#include "util/endian.h"
errorret_t assetTextureLoad(assetentire_t entire) {
assertNotNull(entire.data, "Data pointer cannot be NULL.");
assertNotNull(entire.output, "Output pointer cannot be NULL.");
assettexture_t *assetData = (assettexture_t *)entire.data;
texture_t *texture = (texture_t *)entire.output;
// Read header and version (first 4 bytes)
if(
assetData->header[0] != 'D' ||
assetData->header[1] != 'T' ||
assetData->header[2] != 'X'
) {
errorThrow("Invalid texture header");
}
// Version (can only be 1 atm)
if(assetData->version != 0x01) {
errorThrow("Unsupported texture version");
}
// Fix endian
assetData->width = endianLittleToHost32(assetData->width);
assetData->height = endianLittleToHost32(assetData->height);
// Check dimensions.
if(
assetData->width == 0 || assetData->width > ASSET_TEXTURE_WIDTH_MAX ||
assetData->height == 0 || assetData->height > ASSET_TEXTURE_HEIGHT_MAX
) {
errorThrow("Invalid texture dimensions");
}
// Validate format
textureformat_t format;
texturedata_t data;
switch(assetData->type) {
case 0x00: // RGBA8888
format = TEXTURE_FORMAT_RGBA;
data.rgbaColors = (color_t *)assetData->data;
break;
// case 0x01:
// format = TEXTURE_FORMAT_RGB;
// break;
// case 0x02:
// format = TEXTURE_FORMAT_RGB565;
// break;
// case 0x03:
// format = TEXTURE_FORMAT_RGB5A3;
// break;
default:
errorThrow("Unsupported texture format");
}
errorChain(textureInit(
texture, assetData->width, assetData->height, format, data
));
errorOk();
}
+39
View File
@@ -0,0 +1,39 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "error/error.h"
#include "display/color.h"
#define ASSET_TEXTURE_WIDTH_MAX 2048
#define ASSET_TEXTURE_HEIGHT_MAX 2048
#define ASSET_TEXTURE_SIZE_MAX ( \
ASSET_TEXTURE_WIDTH_MAX * ASSET_TEXTURE_HEIGHT_MAX \
)
typedef struct assetentire_s assetentire_t;
#pragma pack(push, 1)
typedef struct {
char_t header[3];
uint8_t version;
uint8_t type;
uint32_t width;
uint32_t height;
uint8_t data[ASSET_TEXTURE_SIZE_MAX * sizeof(color4b_t)];
} assettexture_t;
#pragma pack(pop)
/**
* Loads a palettized texture from the given data pointer into the output
* texture.
*
* @param data Pointer to the raw assettexture_t data.
* @param output Pointer to the texture_t to load the image into.
* @return An error code.
*/
errorret_t assetTextureLoad(assetentire_t entire);
+70
View File
@@ -0,0 +1,70 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "asset/asset.h"
#include "assert/assert.h"
#include "display/texture/tileset.h"
#include "util/memory.h"
#include "util/endian.h"
errorret_t assetTilesetLoad(assetentire_t entire) {
assertNotNull(entire.data, "Asset data cannot be null");
assertNotNull(entire.output, "Asset output cannot be null");
assettileset_t *tilesetData = (assettileset_t *)entire.data;
tileset_t *tileset = (tileset_t *)entire.output;
if(
tilesetData->header[0] != 'D' ||
tilesetData->header[1] != 'T' ||
tilesetData->header[2] != 'F'
) {
errorThrow("Invalid tileset header");
}
if(tilesetData->version != 0x00) {
errorThrow("Unsupported tileset version");
}
// Fix endianness
tilesetData->tileWidth = endianLittleToHost16(tilesetData->tileWidth);
tilesetData->tileHeight = endianLittleToHost16(tilesetData->tileHeight);
tilesetData->columnCount = endianLittleToHost16(tilesetData->columnCount);
tilesetData->rowCount = endianLittleToHost16(tilesetData->rowCount);
tilesetData->right = endianLittleToHost16(tilesetData->right);
tilesetData->bottom = endianLittleToHost16(tilesetData->bottom);
if(tilesetData->tileWidth == 0) {
errorThrow("Tile width cannot be 0");
}
if(tilesetData->tileHeight == 0) {
errorThrow("Tile height cannot be 0");
}
if(tilesetData->columnCount == 0) {
errorThrow("Column count cannot be 0");
}
if(tilesetData->rowCount == 0) {
errorThrow("Row count cannot be 0");
}
tilesetData->u0 = endianLittleToHostFloat(tilesetData->u0);
tilesetData->v0 = endianLittleToHostFloat(tilesetData->v0);
if(tilesetData->v0 < 0.0f || tilesetData->v0 > 1.0f) {
errorThrow("Invalid v0 value in tileset");
}
// Setup tileset
tileset->tileWidth = tilesetData->tileWidth;
tileset->tileHeight = tilesetData->tileHeight;
tileset->tileCount = tilesetData->columnCount * tilesetData->rowCount;
tileset->columns = tilesetData->columnCount;
tileset->rows = tilesetData->rowCount;
tileset->uv[0] = tilesetData->u0;
tileset->uv[1] = tilesetData->v0;
errorOk();
}
+32
View File
@@ -0,0 +1,32 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "error/error.h"
#pragma pack(push, 1)
typedef struct {
char_t header[3];
uint8_t version;
uint16_t tileWidth;
uint16_t tileHeight;
uint16_t columnCount;
uint16_t rowCount;
uint16_t right;
uint16_t bottom;
float_t u0;
float_t v0;
} assettileset_t;
#pragma pack(pop)
/**
* Loads a tileset from the given data pointer into the output tileset.
*
* @param entire Data received from the asset loader system.
* @return An error code.
*/
errorret_t assetTilesetLoad(assetentire_t entire);
+6
View File
@@ -0,0 +1,6 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
add_subdirectory(testmap)
+6
View File
@@ -0,0 +1,6 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
add_asset(MAP testmap.json)
+1
View File
@@ -0,0 +1 @@
{"shapes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "entities": []}
+1
View File
@@ -0,0 +1 @@
{"shapes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "entities": []}
+1
View File
@@ -0,0 +1 @@
{"shapes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "entities": []}
+1
View File
@@ -0,0 +1 @@
{"shapes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "entities": []}
+1
View File
@@ -0,0 +1 @@
{"shapes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "entities": []}
+1
View File
@@ -0,0 +1 @@
{"shapes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "entities": []}
+1
View File
@@ -0,0 +1 @@
{"shapes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 5, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 1, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 1, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 2, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "entities": []}
+1
View File
@@ -0,0 +1 @@
{"shapes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "entities": []}
+1
View File
@@ -0,0 +1 @@
{"shapes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "entities": []}
+1
View File
@@ -0,0 +1 @@
{"shapes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "entities": []}
+1
View File
@@ -0,0 +1 @@
{"shapes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], "entities": []}
+1
View File
@@ -0,0 +1 @@
{"shapes": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]}
+3
View File
@@ -0,0 +1,3 @@
{
"name": "Test"
}
+49
View File
@@ -0,0 +1,49 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "script/scriptcontext.h"
#include "debug/debug.h"
#include "assert/assert.h"
#include "rpg/overworld/map.h"
int moduleMapLoad(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
if(!lua_isstring(L, 1)) {
luaL_error(L, "Expected string map filename");
return 0;
}
// Potentially provide up to 3 params
chunkpos_t initial = { .x = 0, .y = 0, .z = 0 };
if(lua_isnumber(L, 2)) {
initial.x = (chunkunit_t)lua_tonumber(L, 2);
}
if(lua_isnumber(L, 3)) {
initial.y = (chunkunit_t)lua_tonumber(L, 3);
}
if(lua_isnumber(L, 4)) {
initial.z = (chunkunit_t)lua_tonumber(L, 4);
}
// Load the map.
errorret_t ret = mapLoad(luaL_checkstring(L, 1), initial);
if(ret.code != ERROR_OK) {
luaL_error(L, "Failed to load map");
errorCatch(errorPrint(ret));
return 0;
}
return 0;
}
void moduleMapSystem(scriptcontext_t *context) {
assertNotNull(context, "Script context cannot be NULL");
lua_register(context->luaState, "mapLoad", moduleMapLoad);
}
+12
View File
@@ -0,0 +1,12 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
sdl2.c
psp.c
dolphin.c
)
+92
View File
@@ -0,0 +1,92 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "dolphin.h"
void displayInitDolphin(void) {
VIDEO_Init();
DISPLAY.screenMode = VIDEO_GetPreferredMode(NULL);
DISPLAY.frameBuffer[0] = MEM_K0_TO_K1(
SYS_AllocateFramebuffer(DISPLAY.screenMode)
);
DISPLAY.frameBuffer[1] = MEM_K0_TO_K1(
SYS_AllocateFramebuffer(DISPLAY.screenMode)
);
VIDEO_Configure(DISPLAY.screenMode);
VIDEO_SetNextFramebuffer(DISPLAY.frameBuffer[DISPLAY.whichFrameBuffer]);
// VIDEO_SetPostRetraceCallback(copy_buffers);
VIDEO_SetBlack(FALSE);
VIDEO_Flush();
VIDEO_WaitVSync();
if(DISPLAY.screenMode->viTVMode & VI_NON_INTERLACE) VIDEO_WaitVSync();
DISPLAY.fifoBuffer = memalign(32, DISPLAY_FIFO_SIZE);
memoryZero(DISPLAY.fifoBuffer, DISPLAY_FIFO_SIZE);
GX_Init(DISPLAY.fifoBuffer, DISPLAY_FIFO_SIZE);
// This seems to be mostly related to interlacing vs progressive
GX_SetViewport(
0, 0,
DISPLAY.screenMode->fbWidth, DISPLAY.screenMode->efbHeight,
0, 1
);
float_t yscale = GX_GetYScaleFactor(
DISPLAY.screenMode->efbHeight, DISPLAY.screenMode->xfbHeight
);
uint32_t xfbHeight = GX_SetDispCopyYScale(yscale);
GX_SetScissor(
0, 0,
DISPLAY.screenMode->fbWidth, DISPLAY.screenMode->efbHeight
);
GX_SetDispCopySrc(
0, 0,
DISPLAY.screenMode->fbWidth, DISPLAY.screenMode->efbHeight
);
GX_SetDispCopyDst(DISPLAY.screenMode->fbWidth, xfbHeight);
GX_SetCopyFilter(
DISPLAY.screenMode->aa,
DISPLAY.screenMode->sample_pattern,
GX_TRUE,
DISPLAY.screenMode->vfilter
);
GX_SetFieldMode(
DISPLAY.screenMode->field_rendering,
(
(DISPLAY.screenMode->viHeight == 2 * DISPLAY.screenMode->xfbHeight) ?
GX_ENABLE :
GX_DISABLE
)
);
// Setup cull modes
GX_SetCullMode(GX_CULL_NONE);
GX_SetZMode(GX_FALSE, GX_ALWAYS, GX_FALSE);
GX_CopyDisp(DISPLAY.frameBuffer[DISPLAY.whichFrameBuffer], GX_TRUE);
GX_SetDispCopyGamma(GX_GM_1_0);
GX_ClearVtxDesc();
GX_SetVtxDesc(GX_VA_POS, GX_INDEX16);
GX_SetVtxDesc(GX_VA_CLR0, GX_INDEX16);
GX_SetVtxDesc(GX_VA_TEX0, GX_INDEX16);
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0);
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_U8, 0);
GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0);
}
void displayDolphinSwap(void) {
GX_DrawDone();
DISPLAY.whichFrameBuffer ^= 1;
GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE);
GX_SetColorUpdate(GX_TRUE);
GX_CopyDisp(DISPLAY.frameBuffer[DISPLAY.whichFrameBuffer], GX_TRUE);
VIDEO_SetNextFramebuffer(DISPLAY.frameBuffer[DISPLAY.whichFrameBuffer]);
VIDEO_Flush();
VIDEO_WaitVSync();
}
+27
View File
@@ -0,0 +1,27 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
#include "display/displaydefs.h"
typedef struct {
void *frameBuffer[2];// Double-Bufferred
int whichFrameBuffer;
GXRModeObj *screenMode;
void *fifoBuffer;
} displaydolphin_t;
/**
* Initializes the display for Dolphin.
*/
void displayDolphinInit(void);
/**
* Swaps the back buffer to the front for Dolphin.
*/
void displayDolphinSwap(void);
+12
View File
@@ -0,0 +1,12 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "psp.h"
void displayInitPSP(void) {
DISPLAY.usingShaderedPalettes = false;
}
+13
View File
@@ -0,0 +1,13 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
/**
* Initializes the display for PSP.
*/
void displayInitPSP(void);
+32
View File
@@ -0,0 +1,32 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "sdl2.h"
void displaySDL2Update(void) {
}
void displaySDL2Swap(void) {
SDL_GL_SwapWindow(DISPLAY.window);
GLenum err;
while((err = glGetError()) != GL_NO_ERROR) {
debugPrint("GL Error: %d\n", err);
}
}
void displaySDL2Dispose(void) {
if(DISPLAY.glContext) {
SDL_GL_DeleteContext(DISPLAY.glContext);
DISPLAY.glContext = NULL;
}
if(DISPLAY.window) {
SDL_DestroyWindow(DISPLAY.window);
DISPLAY.window = NULL;
}
SDL_Quit();
}
+35
View File
@@ -0,0 +1,35 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
typedef struct {
SDL_Window *window;
SDL_GLContext glContext;
bool_t usingShaderedPalettes;
} displaysdl2_t;
/**
* Initializes the display for SDL2.
*/
void displaySDL2Init(void);
/**
* Updates the display for SDL2.
*/
void displaySDL2Update(void);
/**
* Swaps the display buffers for SDL2.
*/
void displaySDL2Swap(void);
/**
* Disposes of the display for SDL2.
*/
void displaySDL2Dispose(void);
+17
View File
@@ -0,0 +1,17 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
rpg.c
rpgcamera.c
rpgtextbox.c
)
# Subdirs
add_subdirectory(cutscene)
add_subdirectory(entity)
add_subdirectory(overworld)
+14
View File
@@ -0,0 +1,14 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
cutscenesystem.c
cutscenemode.c
)
# Subdirs
add_subdirectory(item)
@@ -6,9 +6,9 @@
*/ */
#pragma once #pragma once
#include "eventitem.h" #include "rpg/cutscene/item/cutsceneitem.h"
typedef struct { typedef struct cutscene_s {
const cutsceneitem_t *items;
uint8_t itemCount; uint8_t itemCount;
eventitem_t items[EVENT_ITEM_COUNT_MAX]; } cutscene_t;
} eventdata_t;
+19
View File
@@ -0,0 +1,19 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "rpg/cutscene/cutscenesystem.h"
bool_t cutsceneModeIsInputAllowed() {
switch(CUTSCENE_SYSTEM.mode) {
case CUTSCENE_MODE_FULL_FREEZE:
case CUTSCENE_MODE_INPUT_FREEZE:
return false;
default:
return true;
}
}
+26
View File
@@ -0,0 +1,26 @@
/**
* 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 {
CUTSCENE_MODE_NONE,
CUTSCENE_MODE_FULL_FREEZE,
CUTSCENE_MODE_INPUT_FREEZE,
CUTSCENE_MODE_GAMEPLAY
} cutscenemode_t;
// Default mode for all cutscenes.
#define CUTSCENE_MODE_INITIAL CUTSCENE_MODE_INPUT_FREEZE
/**
* Check if input is allowed in the current cutscene mode.
*
* @return true if input is allowed, false otherwise.
*/
bool_t cutsceneModeIsInputAllowed();
+56
View File
@@ -0,0 +1,56 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "cutscenesystem.h"
#include "util/memory.h"
cutscenesystem_t CUTSCENE_SYSTEM;
void cutsceneSystemInit() {
memoryZero(&CUTSCENE_SYSTEM, sizeof(cutscenesystem_t));
}
void cutsceneSystemStartCutscene(const cutscene_t *cutscene) {
CUTSCENE_SYSTEM.scene = cutscene;
CUTSCENE_SYSTEM.mode = CUTSCENE_MODE_INITIAL;
CUTSCENE_SYSTEM.currentItem = 0xFF;// Set to 0xFF so start wraps.
cutsceneSystemNext();
}
void cutsceneSystemUpdate() {
if(CUTSCENE_SYSTEM.scene == NULL) return;
const cutsceneitem_t *item = cutsceneSystemGetCurrentItem();
cutsceneItemUpdate(item, &CUTSCENE_SYSTEM.data);
}
void cutsceneSystemNext() {
if(CUTSCENE_SYSTEM.scene == NULL) return;
CUTSCENE_SYSTEM.currentItem++;
// End of the cutscene?
if(
CUTSCENE_SYSTEM.currentItem >= CUTSCENE_SYSTEM.scene->itemCount
) {
CUTSCENE_SYSTEM.scene = NULL;
CUTSCENE_SYSTEM.currentItem = 0xFF;
CUTSCENE_SYSTEM.mode = CUTSCENE_MODE_NONE;
return;
}
// Start item.
const cutsceneitem_t *item = cutsceneSystemGetCurrentItem();
memset(&CUTSCENE_SYSTEM.data, 0, sizeof(CUTSCENE_SYSTEM.data));
cutsceneItemStart(item, &CUTSCENE_SYSTEM.data);
}
const cutsceneitem_t * cutsceneSystemGetCurrentItem() {
if(CUTSCENE_SYSTEM.scene == NULL) return NULL;
return &CUTSCENE_SYSTEM.scene->items[CUTSCENE_SYSTEM.currentItem];
}
+50
View File
@@ -0,0 +1,50 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "cutscene.h"
#include "cutscenemode.h"
typedef struct {
const cutscene_t *scene;
uint8_t currentItem;
// Data (used by the current item).
cutsceneitemdata_t data;
cutscenemode_t mode;
} cutscenesystem_t;
extern cutscenesystem_t CUTSCENE_SYSTEM;
/**
* Initialize the cutscene system.
*/
void cutsceneSystemInit();
/**
* Start a cutscene.
*
* @param cutscene Pointer to the cutscene to start.
*/
void cutsceneSystemStartCutscene(const cutscene_t *cutscene);
/**
* Advance to the next item in the cutscene.
*/
void cutsceneSystemNext();
/**
* Update the cutscene system for one frame.
*/
void cutsceneSystemUpdate();
/**
* Get the current cutscene item.
*
* @return Pointer to the current cutscene item.
*/
const cutsceneitem_t * cutsceneSystemGetCurrentItem();
+3 -4
View File
@@ -4,8 +4,7 @@
# https://opensource.org/licenses/MIT # https://opensource.org/licenses/MIT
# Sources # Sources
target_sources(${DUSK_TARGET_NAME} target_sources(${DUSK_LIBRARY_TARGET_NAME}
PRIVATE PUBLIC
chunk.c cutsceneitem.c
overworld.c
) )
@@ -6,4 +6,6 @@
*/ */
#pragma once #pragma once
#include "ui/fontdata.h" #include "dusk.h"
typedef void (*cutscenecallback_t)(void);
+54
View File
@@ -0,0 +1,54 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "rpg/cutscene/cutscenesystem.h"
#include "input/input.h"
#include "time/time.h"
void cutsceneItemStart(const cutsceneitem_t *item, cutsceneitemdata_t *data) {
switch(item->type) {
case CUTSCENE_ITEM_TYPE_TEXT: {
rpgTextboxShow(
item->text.position,
item->text.text
);
break;
}
case CUTSCENE_ITEM_TYPE_WAIT:
data->wait = item->wait;
break;
case CUTSCENE_ITEM_TYPE_CALLBACK:
if(item->callback != NULL) item->callback();
break;
case CUTSCENE_ITEM_TYPE_CUTSCENE:
if(item->cutscene != NULL) cutsceneSystemStartCutscene(item->cutscene);
break;
default:
break;
}
}
void cutsceneItemUpdate(const cutsceneitem_t *item, cutsceneitemdata_t *data) {
switch(item->type) {
case CUTSCENE_ITEM_TYPE_TEXT:
if(rpgTextboxIsVisible()) return;
cutsceneSystemNext();
break;
case CUTSCENE_ITEM_TYPE_WAIT:
data->wait -= TIME.delta;
if(data->wait <= 0) cutsceneSystemNext();
break;
default:
break;
}
}
+53
View File
@@ -0,0 +1,53 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "cutscenewait.h"
#include "cutscenecallback.h"
#include "cutscenetext.h"
typedef struct cutscene_s cutscene_t;
typedef enum {
CUTSCENE_ITEM_TYPE_NULL,
CUTSCENE_ITEM_TYPE_TEXT,
CUTSCENE_ITEM_TYPE_CALLBACK,
CUTSCENE_ITEM_TYPE_WAIT,
CUTSCENE_ITEM_TYPE_CUTSCENE
} cutsceneitemtype_t;
typedef struct cutsceneitem_s {
cutsceneitemtype_t type;
// Arguments/Data that will be used when this item is invoked.
union {
cutscenetext_t text;
cutscenecallback_t callback;
cutscenewait_t wait;
const cutscene_t *cutscene;
};
} cutsceneitem_t;
typedef union {
cutscenewaitdata_t wait;
} cutsceneitemdata_t;
/**
* Start the given cutscene item.
*
* @param item The cutscene item to start.
* @param data The cutscene item data storage.
*/
void cutsceneItemStart(const cutsceneitem_t *item, cutsceneitemdata_t *data);
/**
* Tick the given cutscene item (one frame).
*
* @param item The cutscene item to tick.
* @param data The cutscene item data storage.
*/
void cutsceneItemUpdate(const cutsceneitem_t *item, cutsceneitemdata_t *data);
@@ -6,9 +6,9 @@
*/ */
#pragma once #pragma once
#include "itemtype.h" #include "rpg/rpgtextbox.h"
typedef struct { typedef struct {
itemtype_t type; char_t text[RPG_TEXTBOX_MAX_CHARS];
uint8_t count; rpgtextboxpos_t position;
} itemstack_t; } cutscenetext_t;
@@ -7,7 +7,6 @@
#pragma once #pragma once
#include "dusk.h" #include "dusk.h"
#include <pspkernel.h>
#include <pspdebug.h> typedef float_t cutscenewait_t;
#include <pspdisplay.h> typedef float_t cutscenewaitdata_t;
#include <pspctrl.h>
+30
View File
@@ -0,0 +1,30 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "rpg/cutscene/cutscenesystem.h"
static const cutsceneitem_t TEST_CUTSCENE_ONE_ITEMS[] = {
{ .type = CUTSCENE_ITEM_TYPE_TEXT, .text = { .text = "This is a test cutscene.", .position = RPG_TEXTBOX_POS_BOTTOM } },
{ .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = 2.0f },
{ .type = CUTSCENE_ITEM_TYPE_TEXT, .text = { .text = "It has multiple lines of text.\nAnd waits in between.", .position = RPG_TEXTBOX_POS_TOP } },
};
static const cutscene_t TEST_CUTSCENE_ONE = {
.items = TEST_CUTSCENE_ONE_ITEMS,
.itemCount = sizeof(TEST_CUTSCENE_ONE_ITEMS) / sizeof(cutsceneitem_t)
};
static const cutsceneitem_t TEST_CUTSCENE_TWO_ITEMS[] = {
{ .type = CUTSCENE_ITEM_TYPE_WAIT, .wait = 1.0f },
{ .type = CUTSCENE_ITEM_TYPE_CUTSCENE, .cutscene = &TEST_CUTSCENE_ONE },
};
static const cutscene_t TEST_CUTSCENE = {
.items = TEST_CUTSCENE_TWO_ITEMS,
.itemCount = sizeof(TEST_CUTSCENE_TWO_ITEMS) / sizeof(cutsceneitem_t)
};
+14
View File
@@ -0,0 +1,14 @@
# Copyright (c) 2025 Dominic Masters
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT
# Sources
target_sources(${DUSK_LIBRARY_TARGET_NAME}
PUBLIC
entity.c
entityanim.c
npc.c
player.c
entitydir.c
)
+200
View File
@@ -0,0 +1,200 @@
/**
* 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 "time/time.h"
#include "util/math.h"
#include "rpg/cutscene/cutscenemode.h"
#include "rpg/overworld/map.h"
entity_t ENTITIES[ENTITY_COUNT];
void entityInit(entity_t *entity, const entitytype_t type) {
assertNotNull(entity, "Entity pointer cannot be NULL");
assertTrue(type < ENTITY_TYPE_COUNT, "Invalid entity type");
assertTrue(type != ENTITY_TYPE_NULL, "Cannot have NULL entity type");
assertTrue(
entity >= ENTITIES && entity < ENTITIES + ENTITY_COUNT,
"Entity pointer is out of bounds"
);
memoryZero(entity, sizeof(entity_t));
entity->id = (uint8_t)(entity - ENTITIES);
entity->type = type;
if(ENTITY_CALLBACKS[type].init != NULL) ENTITY_CALLBACKS[type].init(entity);
}
void entityUpdate(entity_t *entity) {
assertNotNull(entity, "Entity pointer cannot be NULL");
assertTrue(entity->type < ENTITY_TYPE_COUNT, "Invalid entity type");
assertTrue(entity->type != ENTITY_TYPE_NULL, "Cannot have NULL entity type");
// What state is the entity in?
if(entity->animation != ENTITY_ANIM_IDLE) {
// Entity is mid animation, tick it (down).
entity->animTime -= TIME.delta;
if(entity->animTime <= 0) {
entity->animation = ENTITY_ANIM_IDLE;
entity->animTime = 0;
}
return;
}
// Movement code.
if(
cutsceneModeIsInputAllowed() &&
ENTITY_CALLBACKS[entity->type].movement != NULL
) {
ENTITY_CALLBACKS[entity->type].movement(entity);
}
}
void entityTurn(entity_t *entity, const entitydir_t direction) {
entity->direction = direction;
entity->animation = ENTITY_ANIM_TURN;
entity->animTime = ENTITY_ANIM_TURN_DURATION;
}
void entityWalk(entity_t *entity, const entitydir_t direction) {
// TODO: Animation, delay, etc.
entity->direction = direction;
// Where are we moving?
worldpos_t newPos = entity->position;
{
worldunits_t relX, relY;
entityDirGetRelative(direction, &relX, &relY);
newPos.x += relX;
newPos.y += relY;
}
// Get tile under foot
tile_t tileCurrent = mapGetTile(entity->position);
tile_t tileNew = mapGetTile(newPos);
bool_t fall = false;
bool_t raise = false;
// Are we walking up a ramp?
if(
tileIsRamp(tileCurrent) &&
(
// Can only walk UP the direction the ramp faces.
(direction+TILE_SHAPE_RAMP_SOUTH) == tileCurrent ||
// If diagonal ramp, can go up one of two ways only.
(
(
tileCurrent == TILE_SHAPE_RAMP_SOUTHEAST &&
(direction == ENTITY_DIR_SOUTH || direction == ENTITY_DIR_EAST)
) ||
(
tileCurrent == TILE_SHAPE_RAMP_SOUTHWEST &&
(direction == ENTITY_DIR_SOUTH || direction == ENTITY_DIR_WEST)
) ||
(
tileCurrent == TILE_SHAPE_RAMP_NORTHEAST &&
(direction == ENTITY_DIR_NORTH || direction == ENTITY_DIR_EAST)
) ||
(
tileCurrent == TILE_SHAPE_RAMP_NORTHWEST &&
(direction == ENTITY_DIR_NORTH || direction == ENTITY_DIR_WEST)
)
)
// Must be able to walk up.
)
) {
tileNew = TILE_SHAPE_NULL;// Force check for ramp above.
worldpos_t abovePos = newPos;
abovePos.z += 1;
tile_t tileAbove = mapGetTile(abovePos);
if(tileAbove != TILE_SHAPE_NULL && tileIsWalkable(tileAbove)) {
// We can go up the ramp.
raise = true;
}
} else if(tileNew == TILE_SHAPE_NULL && newPos.z > 0) {
// Falling down?
worldpos_t belowPos = newPos;
belowPos.z -= 1;
tile_t tileBelow = mapGetTile(belowPos);
if(
tileBelow != TILE_SHAPE_NULL &&
tileIsRamp(tileBelow) &&
(
// This handles regular cardinal ramps
(entityDirGetOpposite(direction)+TILE_SHAPE_RAMP_SOUTH) == tileBelow ||
// This handles diagonal ramps
(
(
tileBelow == TILE_SHAPE_RAMP_SOUTHEAST &&
(direction == ENTITY_DIR_NORTH || direction == ENTITY_DIR_WEST)
) ||
(
tileBelow == TILE_SHAPE_RAMP_SOUTHWEST &&
(direction == ENTITY_DIR_NORTH || direction == ENTITY_DIR_EAST)
) ||
(
tileBelow == TILE_SHAPE_RAMP_NORTHEAST &&
(direction == ENTITY_DIR_SOUTH || direction == ENTITY_DIR_WEST)
) ||
(
tileBelow == TILE_SHAPE_RAMP_NORTHWEST &&
(direction == ENTITY_DIR_SOUTH || direction == ENTITY_DIR_EAST)
)
)
)
) {
// We will fall to this tile.
fall = true;
}
}
// Can we walk here?
if(!raise && !fall && !tileIsWalkable(tileNew)) return;// Blocked
// Entity in way?
entity_t *other = ENTITIES;
do {
if(other == entity) continue;
if(other->type == ENTITY_TYPE_NULL) continue;
if(!worldPosIsEqual(other->position, newPos)) continue;
return;// Blocked
} while(++other, other < &ENTITIES[ENTITY_COUNT]);
entity->lastPosition = entity->position;
entity->position = newPos;
entity->animation = ENTITY_ANIM_WALK;
entity->animTime = ENTITY_ANIM_WALK_DURATION;// TODO: Running vs walking
if(raise) {
entity->position.z += 1;
} else if(fall) {
entity->position.z -= 1;
}
}
entity_t * entityGetAt(const worldpos_t position) {
entity_t *ent = ENTITIES;
do {
if(ent->type == ENTITY_TYPE_NULL) continue;
if(!worldPosIsEqual(ent->position, position)) continue;
return ent;
} while(++ent, ent < &ENTITIES[ENTITY_COUNT]);
return NULL;
}
uint8_t entityGetAvailable() {
entity_t *ent = ENTITIES;
do {
if(ent->type == ENTITY_TYPE_NULL) return ent - ENTITIES;
} while(++ent, ent < &ENTITIES[ENTITY_COUNT]);
return 0xFF;
}
+77
View File
@@ -0,0 +1,77 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "entitydir.h"
#include "entityanim.h"
#include "entitytype.h"
#include "npc.h"
typedef struct map_s map_t;
typedef struct entity_s {
uint8_t id;
entitytype_t type;
entitytypedata_t data;
// Movement
entitydir_t direction;
worldpos_t position;
worldpos_t lastPosition;
entityanim_t animation;
float_t animTime;
} entity_t;
extern entity_t ENTITIES[ENTITY_COUNT];
/**
* Initializes an entity structure.
*
* @param entity Pointer to the entity structure to initialize.
* @param type The type of the entity.
*/
void entityInit(entity_t *entity, const entitytype_t type);
/**
* Updates an entity.
*
* @param entity Pointer to the entity structure to update.
*/
void entityUpdate(entity_t *entity);
/**
* Turn an entity to face a new direction.
*
* @param entity Pointer to the entity to turn.
* @param direction The direction to face.
*/
void entityTurn(entity_t *entity, const entitydir_t direction);
/**
* Make an entity walk in a direction.
*
* @param entity Pointer to the entity to make walk.
* @param direction The direction to walk in.
*/
void entityWalk(entity_t *entity, const entitydir_t direction);
/**
* Gets the entity at a specific world position.
*
* @param map Pointer to the map to check.
* @param pos The world position to check.
* @return Pointer to the entity at the position, or NULL if none.
*/
entity_t *entityGetAt(const worldpos_t pos);
/**
* Gets an available entity index.
*
* @return The index of an available entity, or 0xFF if none are available.
*/
uint8_t entityGetAvailable();
@@ -5,4 +5,5 @@
* https://opensource.org/licenses/MIT * https://opensource.org/licenses/MIT
*/ */
#include "display/render.h" #include "entityanim.h"
+18
View File
@@ -0,0 +1,18 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
#define ENTITY_ANIM_TURN_DURATION 0.06f
#define ENTITY_ANIM_WALK_DURATION 0.1f
typedef enum {
ENTITY_ANIM_IDLE,
ENTITY_ANIM_TURN,
ENTITY_ANIM_WALK,
} entityanim_t;

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