From c0cd4ead04a92f7ab31c15230899e460d7ee6b0a Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Thu, 2 Oct 2025 18:53:47 -0500 Subject: [PATCH] Frame --- assets/minesweeper/CMakeLists.txt | 3 +- assets/minesweeper/config/init.dcf | 4 +- assets/minesweeper/config/init_psp.dcf | 4 +- assets/minesweeper/palette/palette0.png | Bin 241 -> 145 bytes assets/minesweeper/palette/palette0.pxo | Bin 1576 -> 1463 bytes assets/minesweeper/sweep/CMakeLists.txt | 2 - assets/minesweeper/sweep/ui.png | Bin 4681 -> 0 bytes assets/minesweeper/ui/CMakeLists.txt | 4 +- assets/minesweeper/ui/ui.png | Bin 0 -> 1715 bytes assets/minesweeper/ui/ui.pxo | Bin 0 -> 1841 bytes assets/minesweeper/ui/ui_frame | Bin 0 -> 913 bytes assets/minesweeper/ui/ui_frame.png | Bin 0 -> 293 bytes src/asset/assetmanager.h | 2 +- src/asset/type/assetpaletteimage.h | 4 +- src/console/cmd/cmdscene.h | 38 +++ src/console/console.c | 2 + src/engine/engine.c | 1 + src/error/error.c | 5 + src/game/minesweeper/CMakeLists.txt | 1 + src/game/minesweeper/game.c | 7 +- src/game/minesweeper/scene/CMakeLists.txt | 12 + src/game/minesweeper/scene/scenesweep.c | 8 +- src/game/minesweeper/scene/scenesweep.h | 2 +- src/game/minesweeper/ui/ui.c | 44 ++- src/game/minesweeper/ui/ui.h | 12 +- src/scene/scene.h | 3 + src/scene/scenemanager.c | 74 ++++- src/scene/scenemanager.h | 19 ++ src/ui/CMakeLists.txt | 1 + src/ui/uiconsole.c | 9 +- src/ui/uiconsole.h | 9 +- src/ui/uiframe.c | 384 ++++++++++++++++++++++ src/ui/uiframe.h | 57 ++++ tools/assetstool/processimage.py | 8 +- 34 files changed, 684 insertions(+), 35 deletions(-) delete mode 100644 assets/minesweeper/sweep/ui.png create mode 100644 assets/minesweeper/ui/ui.png create mode 100644 assets/minesweeper/ui/ui.pxo create mode 100644 assets/minesweeper/ui/ui_frame create mode 100644 assets/minesweeper/ui/ui_frame.png create mode 100644 src/console/cmd/cmdscene.h create mode 100644 src/game/minesweeper/scene/CMakeLists.txt create mode 100644 src/ui/uiframe.c create mode 100644 src/ui/uiframe.h diff --git a/assets/minesweeper/CMakeLists.txt b/assets/minesweeper/CMakeLists.txt index 1f51776..4dc5bc9 100644 --- a/assets/minesweeper/CMakeLists.txt +++ b/assets/minesweeper/CMakeLists.txt @@ -5,6 +5,7 @@ set(DUSK_GAME_ASSETS_DIR "${CMAKE_CURRENT_SOURCE_DIR}" CACHE INTERNAL ${DUSK_CACHE_TARGET}) -add_subdirectory(palette)# Palette asset needs to be added before any images. +# Palette asset needs to be added before any images. +add_subdirectory(palette) add_subdirectory(config) add_subdirectory(ui) \ No newline at end of file diff --git a/assets/minesweeper/config/init.dcf b/assets/minesweeper/config/init.dcf index 714d2c7..30edff1 100644 --- a/assets/minesweeper/config/init.dcf +++ b/assets/minesweeper/config/init.dcf @@ -14,4 +14,6 @@ bind enter accept; bind q cancel; bind esc quit; -fps 1; \ No newline at end of file +fps 1; + +scene sweep; \ No newline at end of file diff --git a/assets/minesweeper/config/init_psp.dcf b/assets/minesweeper/config/init_psp.dcf index 18162c5..802b20a 100644 --- a/assets/minesweeper/config/init_psp.dcf +++ b/assets/minesweeper/config/init_psp.dcf @@ -14,4 +14,6 @@ bind lstick_positive_y down; bind lstick_negative_x left; bind lstick_positive_x right; -fps 1; \ No newline at end of file +fps 1; + +scene sweep; \ No newline at end of file diff --git a/assets/minesweeper/palette/palette0.png b/assets/minesweeper/palette/palette0.png index e60ee7f03b8203942557e7c074c2a48812e84763..51dbd9feb0bdc8e332a90f84a6e1b653d3358045 100644 GIT binary patch literal 145 zcmeAS@N?(olHy`uVBq!ia0vp^0zk~d!3HGTx9oodq!^2X+?^QKos)S9omi;t~Jz|Ns9hD}$d{&To8Z&tTvoU$b0c*#=i1b8AR%#qqQstn z``@3-=$>}U=4HrSV4$B{aFadA`RJL~3YUHQdn=`K602tM@4fuB_ppWURp#AXdeZx9 z0{IV}KNR?5ckQNooqyjRu<-Ahf9j=YjQB&5ca2UOuD|BJbJ!dpx^({}zmS$!E+5@? oElzfP-Bo8T#q8FVdQ&MBb@0Pmh#umAu6 diff --git a/assets/minesweeper/palette/palette0.pxo b/assets/minesweeper/palette/palette0.pxo index 070274afbdfd1fc0eef1d4f6e873b51cbf7d8a86..f31416028861b6b0a0e24c45856e57ccadaebcff 100644 GIT binary patch delta 1037 zcmZ3%vz=Q!z?+#xgn@yBgMq)+Df;0wZ{4@d3=9`1ir3da3HrpKdvRMRe^30CTDG)f*AZrO`Y&pE8({PX7~7vIy@oaP1eef`TGD4{I+-hTm`*y8!>NB`-$?0x8-depUY zi)D#V*D|?f_ZNSCde<>$&gWz5*$1mnzCJ6!{_v;T!=ly?VR`F{D>zHL6orJkOz=zOwr>$U@oKScHh z@GXcbV_!E{M%q}Xxin<%3gfM6mR9Ru9X*xiu;1zDqtLnfUmhIdeSL9iotEcg5Bt2D z5HtI=7m8*iiaDO=_;Mii<#N@)B#p<5+uN3BTIF?LJfA;7!04seMIB#f{d)CTM-@E< zq(g*)^t&8Z7%W<>;Vb2)80Dk1Ml;jRz@z(ldv?*=Ns)6p0(jcATLV-)f_xVSvb1Y6 z2b&y8`5~f@92@^`EaMs@EP(_;gw4*yhh-%~BqoP0x?>U19WP zF7h?+ntbCtQ|JR9<26_KnOGBQXVps^1+ep(>!&ObOPaMT+`Q03Jz{}S%)uR#SFJzM zWK@u6b@cX;&kr3WE#|Dw<>WQC=bLiSG^=n1kKFHw1#ELN70)F#uyQ>xyuM7F@k&F~ z`3p1OZV5#SUEA>1xV>#vX?oD^d z(4A9bII&Wm`CVyJzumPxXU;e2w8qYv{%73}|N7R(pZ33Mz6U<|`#*r`xVwB_*-`m$ zdHHi{`+rqlzHYL5RfVqZS-*&dw&J_@N-CE<@XmR1@9kQBk8;hZ!Y8MA!ZtpdT)y|^ z%?18{n&Mv+>x<8Pm$72@u4nS`rw@Hy*p_xIw)^BZ<12F~FJrb~+&%d|vmGZW%lyB1 zfyI8Z8jG6G?DDmb?3o2*E?j57yrk*>%9a1?&vO2+FZw;_|Lty3PqWA8uZ#b;m+Sp? zKXU5l`-d;KrTqQh_~6RQ;FO)ROJo^Pa^hqzR%c$69633PRZ9VGAp;OFGB8MkXgCmI wnXJzu%OBv)$i%<^&(xDIuu3!afTYAG>$CEU1$eWvfwV9K;YA>Qfd#|^0N(+~cK`qY delta 1143 zcmdnay@E$Pz?+#xgn@yBgMl^3C^{r@_SQGd3=9`0ir3fc?Buy1lpFnZ&kxtTX(xU? zOyBZs>EGbOlV(0fQ|9Y?U)hqQ`-Jmp#Ql1K){TJ=|FL8x82+jZdh_1tigaCwTg(*a z+0Mt8WV}lJWg8WLPxi>MpLsEx#P&?zbB`zSg5UH-xvqP)wXW4Ks5yOVo;LsX=zY`j ziZ=gyF#Gk^joi)u>%Z1Bi$&aw_`Kd}cl4A+`{tW{+nQ>1MWgnE>}liFRSSxYltJgs-xY@x!L z)a>Q-%2kj<)U8Bhspnx0mu}A%Q`NH^Y?&Yb6!@--HR=`?>^fMmRG^E+bCsq^*Mox8 zgs!$5It#7+ZnYik;eTv#QNrgj&$$YPr}_J=-Yi)!;Zry3Xhqq*qyT13&kuiYG+Hgl zbXfP&tx)K9lIWAyW<6m6w%=YTQ!)L8LSDbtq)A(%Jm{-v zWB}Va-{lpvE9_)6nA?lD>Ml%tcD&JOhnp_n?jvl=0^`l?`PflcrTpQ=!sowH@Ax(#u z);BLVi?6X}4d4;%dt;q=;1{!aqgRKp)BEq~!a4zL{aZ7dOrzP>y}Kpy?V*EePg%Hd zS)&w3niWUd{~Zb1(@rMH^NAf0SibQ|twjKXS=`~Y#6!LYUz?-%`8RUR`QrSvyw5Gs z?T>}Qd-=op=WBOV#xtHf!fF1zmTgCc?=)E^wfesm)jWGlIA{FoSKO@q@bKB+LL6&b zC+n*jy-z-{*YC~6f8eftrz&65&o zHcnP&QL7Iq`P^XKMK!+^@Q{X|yZycgiJLDKP3T;ecWr_p}2%PXb z!lhDlL}h+FbK<1wYQ}%}kzdEFk( zUDpDh^m)pZ=+5Bd)oYyTl6=)EY0c05eZFQT7cXpf*S5d@xpYG@cdFS-nRRa-ZfW1c zwdebN8;ip!9-e_7s~@&~f6P_gci~asmCee5jWY_D-~YpolJ_UiWO3$2DFY_IWYJQ9 z`;h?%7#SF(K{Ol)vrIN%k)0gC%EJs)I60nGn&~e{NNn;x7Jjh+Z&o&tQf45$2&5fZ GK|BC74fdP> diff --git a/assets/minesweeper/sweep/CMakeLists.txt b/assets/minesweeper/sweep/CMakeLists.txt index d8eda53..45c387e 100644 --- a/assets/minesweeper/sweep/CMakeLists.txt +++ b/assets/minesweeper/sweep/CMakeLists.txt @@ -2,5 +2,3 @@ # # This software is released under the MIT License. # https://opensource.org/licenses/MIT - -add_asset(PALETTE palette0.png) \ No newline at end of file diff --git a/assets/minesweeper/sweep/ui.png b/assets/minesweeper/sweep/ui.png deleted file mode 100644 index 28b2df5f1c1f31b028da22b378c3abdea01ad394..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4681 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 diff --git a/assets/minesweeper/ui/CMakeLists.txt b/assets/minesweeper/ui/CMakeLists.txt index ffe622f..a2ded2e 100644 --- a/assets/minesweeper/ui/CMakeLists.txt +++ b/assets/minesweeper/ui/CMakeLists.txt @@ -3,4 +3,6 @@ # This software is released under the MIT License. # https://opensource.org/licenses/MIT -add_asset(TILESET minogram.png type=ALPHA tileWidth=6 tileHeight=10 columns=16 rows=6) \ No newline at end of file +add_asset(TILESET minogram.png type=ALPHA tileWidth=6 tileHeight=10 columns=16 rows=6) +add_asset(TILESET ui.png type=PALETTIZED tileWidth=16 tileHeight=16) +add_asset(TILESET ui_frame.png type=PALETTIZED tileWidth=16 tileHeight=16) \ No newline at end of file diff --git a/assets/minesweeper/ui/ui.png b/assets/minesweeper/ui/ui.png new file mode 100644 index 0000000000000000000000000000000000000000..74832f9ad95676728976db18e1d2462544074450 GIT binary patch literal 1715 zcmV;k22A;hP)Px*ZAnByRCt{2oUv;hM-+#D>l^`bkZaYdOPoeQAV^LYRwu(&L14JjM2%2o6%zUb zgjL9mMVd1z4+6&3fn3Ew@yT!?q(+iEwbx#81k8y^aXYKo*_oZ$ncdSWc^@3Uot-x` zx9`n+Z)ZjZ2!iKvqIsJa0ab-u>5U)-0)5K^1vhso010+zncDBCGWaUS7a}3dr zFNA|ignHro-z+JxN`n6-=t{MQ&H!cB8W-gz-yiBgED?8t~#Oa$aIDPXaA*+**#+DeL92TgCHGErYH)5&X;ouVr4A5ag19bN6@Qa+ax6)h!vS@)Fe?`|d@ri}prV9|TJ7M=2n zke3#I;xM4Ou7m-o)&T+souF#* zA1KEP`OH<$u$=wMF4a{oZyUbys&*G{g7V|-&oEp=xBCtN zFzihhL5`<6I5L9Li9^7%gPVXW|LmljsC0JHMScD?hP@V^9o$THfD{hOMVSOHEQgub z(}s+oJW>WA&i)S{KOqd)5Qb|hub?7mw-d*`{Nx}m(nw?QaNi&}y2!bH&Q6@{` z-{*_e>a*6#mE4A{tgdg{HhkqxZJFB0>fd$@KF>dI|EZP0tOiVt8|rx{XtwS~``MVEMYf5?>`n7Mf@yPyfF1QT(j zXi#1YRO8Wr6_Lk?Sp6SC)5MU(712eWL^iiEW+}*_E!A&!&SO;1uPhHx2pWZ3DNu}s z+?7V>0gO3-FLE5tn}bL=d7upCC;G0m0YvyHi_7NDVZ<`Wd`2Aiy)IGfln)pTK#pNH z=-eD{E9Y85s(;`2a7zkFB7Jdr{uwBrTPg?0dcXz6;8NeCv$*dQK{owQ;rS=XnoYU~ z?fd7|Hf@wC?;29_{Hb>l{rfzBl{{^c69hpJ1VIoJN;wZu#CZZ@I;0fhd!V6+0};gZ z$lMI_-Hh;+C(6fWH_&&+(u}V#nM=X)cIbUCr4q&CvK#2NnL|{I%tN7a47X7zC3z2v z!p{T(#sd*Vg(&o^8l1YR*$mI0~8@o5ClP#%D;Xp5Ia!O*aH9n002ov JPDHLkV1lG}J5~Sy literal 0 HcmV?d00001 diff --git a/assets/minesweeper/ui/ui.pxo b/assets/minesweeper/ui/ui.pxo new file mode 100644 index 0000000000000000000000000000000000000000..1ff4fcfaaa11f1df1527d0e996139915ed642802 GIT binary patch literal 1841 zcmZ`)3s93+7XA@rd5RQSK`5Z2o_}VMOQQ4+Y`66t zjt*y$Q$M@a-RN6TjQ?Kx`+4Dn@Sp&DX7v~An>X$0^c=r#1JJU|A z-g*wKH`+L?E-{JOS|j*wXMx(Yr_ae9gY^0(1EiAW-+w}gZd*mZ3=?fwXOAhB9_^A5 zj(6mZ_Nz8LGRs6Ab6IqIz*GgD9JAWyevlgb_(A)D{EqF|=yTi_e>(v-Ca0zpb4FKe z9pNTyrhpB99xpxod7zCoek5B>1N;};fod(+#77jTW|c9r36}hmdbSh&@ zA4dFv7*5s`h3l!eBcuFfjRQ;wTL%D@!BR=Gc+ZIZ-pVliy5R3}qK1GypYgE5H)C3Kaj`nine$YYci+4`6)5Xl{)N5X6rmg$SEwUV&J5slFQ~`W zQ_{D=>EnYtp=_1djyAdP=5*7bHh-MwR;EiGxb^wTBR`22rS;f{aQUzV?4%1M1Z8Ix z6uxT4K$rh>j-?{LSt(Y*JrHLkTj=@b!l>-o{J!AMr?uJW>iHrf9yI4M@|z2H z(Oya753I6!6Dy?n#jCS0_3k(rIs+B&)kaJZW$N1dXhv--(+a@3us9)$vwv?|=VhPK z>KC8Pz3|8nXJK(OQhB4ORokzS`Lvp;S(GX}7kGzNI}Ted)m5~8WG*fbmN{P0vK;ky z?r))L1HFW;Wy^=doDi$Qm8cL6!)`Vwn?FN_M)we6aJ?~&a2>+<41_S&xG0q6<{;~m zxfBEKZi8d<4SHzp^p7Rr3y3F#WAnaY!g&S`!=yQxD6Y|!Kj;Z_z#pZwFagYNr%z)~ z<7(z%%I=lmQa%AH)UdXoq8FS|g1Z&)j#D!M*1nuAaPj#g#tO^2D?r_JX4d_kLB_3tD@@G;tAJte}}nSj_Hbsl{}r{{6f7P0YWn z^lv>d0}6b6%I6KL5R!uNRDz|8k9-p9a0m#>*Kzt>R+m9 z(b>yc-kPAjkfkO;9WLWc)xFC~;JsTtvO_CSqzXKgQaqHWv9j|=C^_uR0$$D%aoK~` z=%mk3|JB_jT<}v&WY?V1;U7#fH3!tS-T6~e9o1Gv>n-=W-c_rV8o|K*nh5mZC^B+Z}K(S7!KL_-U06^&HheRbY zB{ngJl1}PVWKrTg#@nG8v=l#e)Q|{=x@7U0m=DvIsgCw literal 0 HcmV?d00001 diff --git a/assets/minesweeper/ui/ui_frame b/assets/minesweeper/ui/ui_frame new file mode 100644 index 0000000000000000000000000000000000000000..3f3c281a92f2d2564f208852037dc373241bcfb0 GIT binary patch literal 913 zcmWIWW@Zs#VBp|jD4*sOEy!1!)WpcZ;Ka_4xm%W9aZA6m8XeEIV^N6(r5YlwRie|x7! zkCETCWiAu*e{3)0E1fX4dDg_MEgBs!s#vVEWu{MXij)j@u4pQ~uPY_h@odp1n-94s zOKm4DoA28As%nLy11FU1n0Y>$OSuPD}j>d?KpRpR;k*29D&-ntc)X z-pT3mFxD(%+}vZD^=IPAbsP267t?(5$zlm^ zE@uhf=vNbNE?~R4>9?Kc#kA8oJ0pKEbhdZT`I)x5=*~X}Lp`(5lC)RPr$64{q^BDY z#QVE&#&Th=duM(nTO5BVlYQ;QqAhJD^I!a$%e$1nob?9(U&X!4wus(5DO>SL`Q~TW zH-~3^urU48dA#$);~iIyEuMV+$)uBFCO@64*v;qOKCK^s5>XGow{2_zMi7Gv1A{b> z%FInnPmKpfmwsANVs2`&zM+0jVr6Pkyy4r)8#x;cL|Bby^v*qO(I~y6x!R(6_Kt?R z0-q0-BqiEavb}pCnDfOVi34OF!~f?t-(KvjluFM1U$W!Oy{&7zkJP`|y(qQ*b-lg$ z%lyC5-iFLGOqYNOi~*R?7?gn+o8vR{QZkcMi{qi5NM>LN@MdIUV8ESLfd+w11kp$-7hM~A w$U&5Y$dy3%U~>x83Fro)`vPH58ql5C3_|l$fHx}}NE0&argc < 1) { + consolePrint("Usage: scene "); + return; + } + + scene_t *scene = sceneManagerGetSceneByName(exec->argv[0]); + if(scene == NULL) { + consolePrint("Error: Scene '%s' not found.", exec->argv[0]); + return; + } + + if((scene->flags & SCENE_FLAG_INITIALIZED) == 0) { + if(scene->init) { + errorret_t ret = errorPrint(scene->init()); + if(ret.code != ERROR_OK) { + errorCatch(ret); + consolePrint("Error: Failed to initialize scene '%s'.", exec->argv[0]); + return; + } + } + scene->flags |= SCENE_FLAG_INITIALIZED; + } + + sceneManagerSetScene(scene); +} \ No newline at end of file diff --git a/src/console/console.c b/src/console/console.c index 0decd27..18fd874 100644 --- a/src/console/console.c +++ b/src/console/console.c @@ -19,6 +19,7 @@ #include "console/cmd/cmdbind.h" #include "console/cmd/cmdtoggleconsole.h" #include "console/cmd/cmdalias.h" +#include "console/cmd/cmdscene.h" console_t CONSOLE; @@ -34,6 +35,7 @@ void consoleInit() { consoleRegCmd("bind", cmdBind); consoleRegCmd("toggleconsole", cmdToggleConsole); consoleRegCmd("alias", cmdAlias); + consoleRegCmd("scene", cmdScene); #if CONSOLE_POSIX threadInit(&CONSOLE.thread, consoleInputThread); diff --git a/src/engine/engine.c b/src/engine/engine.c index abad84d..ee9814a 100644 --- a/src/engine/engine.c +++ b/src/engine/engine.c @@ -58,6 +58,7 @@ errorret_t engineUpdate(void) { errorret_t engineDispose(void) { gameDispose(); + sceneManagerDispose(); errorChain(displayDispose()); assetManagerDispose(); consoleDispose(); diff --git a/src/error/error.c b/src/error/error.c index f9bab6a..11f9ad9 100644 --- a/src/error/error.c +++ b/src/error/error.c @@ -57,6 +57,11 @@ errorret_t errorThrowImpl( } errorret_t errorOkImpl() { + assertTrue( + ERROR_STATE.code == ERROR_OK, + "Global error state is not OK (Likely missing errorCatch)" + ); + return (errorret_t) { .code = ERROR_OK, .state = NULL diff --git a/src/game/minesweeper/CMakeLists.txt b/src/game/minesweeper/CMakeLists.txt index 86f2d8e..459d03e 100644 --- a/src/game/minesweeper/CMakeLists.txt +++ b/src/game/minesweeper/CMakeLists.txt @@ -10,4 +10,5 @@ target_sources(${DUSK_TARGET_NAME} ) # Subdirs +add_subdirectory(scene) add_subdirectory(ui) \ No newline at end of file diff --git a/src/game/minesweeper/game.c b/src/game/minesweeper/game.c index b030180..605aa39 100644 --- a/src/game/minesweeper/game.c +++ b/src/game/minesweeper/game.c @@ -7,9 +7,14 @@ #include "game/game.h" #include "game/minesweeper/ui/ui.h" +#include "game/minesweeper/scene/scenesweep.h" +#include "scene/scenemanager.h" +#include "console/console.h" errorret_t gameInit(void) { - uiInit(); + errorChain(uiInit()); + + sceneManagerRegisterScene(&SCENE_SWEEP); errorOk(); } diff --git a/src/game/minesweeper/scene/CMakeLists.txt b/src/game/minesweeper/scene/CMakeLists.txt new file mode 100644 index 0000000..cb19f3a --- /dev/null +++ b/src/game/minesweeper/scene/CMakeLists.txt @@ -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_TARGET_NAME} + PRIVATE + scenesweep.c +) + +# Subdirs \ No newline at end of file diff --git a/src/game/minesweeper/scene/scenesweep.c b/src/game/minesweeper/scene/scenesweep.c index bd92583..ad877c6 100644 --- a/src/game/minesweeper/scene/scenesweep.c +++ b/src/game/minesweeper/scene/scenesweep.c @@ -6,22 +6,20 @@ */ #include "scenesweep.h" +#include "game/minesweeper/ui/ui.h" scene_t SCENE_SWEEP = { + .name = "sweep", .init = sceneSweepInit, .update = sceneSweepUpdate, .render = sceneSweepRender, .dispose = sceneSweepDispose, - .active = NULL, - .sleep = NULL, - .flags = 0 }; scenesweep_t SCENE_SWEEP_DATA; errorret_t sceneSweepInit(void) { // Initialize scene data here - errorOk(); } @@ -31,8 +29,10 @@ void sceneSweepUpdate(void) { void sceneSweepRender(void) { // Render scene here + uiRender(); } void sceneSweepDispose(void) { // Clean up scene resources here + printf("Disposing sweep scene\n"); } \ No newline at end of file diff --git a/src/game/minesweeper/scene/scenesweep.h b/src/game/minesweeper/scene/scenesweep.h index 4285098..6a4fbf8 100644 --- a/src/game/minesweeper/scene/scenesweep.h +++ b/src/game/minesweeper/scene/scenesweep.h @@ -9,7 +9,7 @@ #include "scene/scene.h" typedef struct { - + int32_t x; } scenesweep_t; extern scene_t SCENE_SWEEP; diff --git a/src/game/minesweeper/ui/ui.c b/src/game/minesweeper/ui/ui.c index c64435d..4b62020 100644 --- a/src/game/minesweeper/ui/ui.c +++ b/src/game/minesweeper/ui/ui.c @@ -8,22 +8,41 @@ #include "ui.h" #include "ui/uifps.h" #include "ui/uiconsole.h" +#include "ui/uiframe.h" #include "display/framebuffer/framebuffer.h" +#include "display/spritebatch/spritebatch.h" #include "display/tileset/tileset_minogram.h" +#include "display/tileset/tileset_ui.h" +#include "display/tileset/tileset_ui_frame.h" ui_t UI; -void uiInit(void) { +errorret_t uiInit(void) { cameraInitOrthographic(&UI.camera); UI.minogramTileset = (tileset_t*)&TILESET_MINOGRAM; - - assetManagerLoadAsset( + errorChain(assetManagerLoadAsset( UI.minogramTileset->image, &UI.minogramAsset, &UI.minogramRef - ); + )); + + UI.uiTileset = (tileset_t*)&TILESET_UI; + errorChain(assetManagerLoadAsset( + UI.uiTileset->image, + &UI.uiAsset, + &UI.uiRef + )); + + UI.frameTileset = (tileset_t*)&TILESET_UI_FRAME; + errorChain(assetManagerLoadAsset( + UI.frameTileset->image, + &UI.frameAsset, + &UI.frameRef + )); + + errorOk(); } void uiRender(void) { @@ -38,7 +57,22 @@ void uiRender(void) { cameraPushMatrix(&UI.camera); uiFPSRender(UI.minogramTileset, &UI.minogramAsset->alphaImage.texture); - uiConsoleRender(UI.minogramTileset, &UI.minogramAsset->alphaImage.texture); + + float_t x, y; + x = 32, y = 32; + uiFrameDrawTiled( + x, y, + 256, 256, + UI.frameTileset, + 0, 0, + &UI.frameAsset->paletteImage.texture + ); + + uiConsoleRender( + 0, 0, + UI.minogramTileset, &UI.minogramAsset->alphaImage.texture + ); + spriteBatchFlush(); cameraPopMatrix(); } diff --git a/src/game/minesweeper/ui/ui.h b/src/game/minesweeper/ui/ui.h index 0283751..8f7b321 100644 --- a/src/game/minesweeper/ui/ui.h +++ b/src/game/minesweeper/ui/ui.h @@ -17,14 +17,24 @@ typedef struct { tileset_t *minogramTileset; asset_t *minogramAsset; ref_t minogramRef; + + tileset_t *uiTileset; + asset_t *uiAsset; + ref_t uiRef; + + tileset_t *frameTileset; + asset_t *frameAsset; + ref_t frameRef; } ui_t; extern ui_t UI; /** * Initializes the Minesweeper UI. + * + * @return Error code indicating success or failure. */ -void uiInit(void); +errorret_t uiInit(void); /** * Renders the Minesweeper UI. diff --git a/src/scene/scene.h b/src/scene/scene.h index f17a7dd..d45a239 100644 --- a/src/scene/scene.h +++ b/src/scene/scene.h @@ -10,8 +10,11 @@ #include "error/error.h" #define SCENE_FLAG_ACTIVE (1 << 0) +#define SCENE_FLAG_INITIALIZED (1 << 1) typedef struct { + const char_t *name; + errorret_t (*init)(void); void (*update)(void); void (*render)(void); diff --git a/src/scene/scenemanager.c b/src/scene/scenemanager.c index f486d52..ae44643 100644 --- a/src/scene/scenemanager.c +++ b/src/scene/scenemanager.c @@ -8,47 +8,103 @@ #include "scenemanager.h" #include "util/memory.h" #include "assert/assert.h" +#include "console/console.h" +#include "util/string.h" scenemanager_t SCENE_MANAGER; errorret_t sceneManagerInit(void) { memoryZero(&SCENE_MANAGER, sizeof(scenemanager_t)); - + errorOk(); } +scene_t * sceneManagerGetSceneByName(const char_t *name) { + assertNotNull(name, "Name is null"); + + for(uint8_t i = 0; i < SCENE_MANAGER.sceneCount; i++) { + if(strcmp(SCENE_MANAGER.scenes[i]->name, name) != 0) continue; + return SCENE_MANAGER.scenes[i]; + } + + return NULL; +} + +void sceneManagerRegisterScene(scene_t *scene) { + assertNotNull(scene, "Scene is null"); + assertTrue( + SCENE_MANAGER.sceneCount < SCENE_MANAGER_SCENE_COUNT_MAX, + "Scene count exceeded max" + ); + assertNotNull(scene->name, "Scene name is null"); + assertNull( + sceneManagerGetSceneByName(scene->name), "Scene name already registered" + ); + + SCENE_MANAGER.scenes[SCENE_MANAGER.sceneCount++] = scene; +} + void sceneManagerSetScene(scene_t *scene) { if(SCENE_MANAGER.current) { - SCENE_MANAGER.current->sleep(); - SCENE_MANAGER.current->flags &= ~SCENE_FLAG_ACTIVE; + if(SCENE_MANAGER.current->sleep) SCENE_MANAGER.current->sleep(); + + // TODO: Should dispose? + SCENE_MANAGER.current->flags &= ~( + SCENE_FLAG_INITIALIZED | SCENE_FLAG_ACTIVE + ); + if(SCENE_MANAGER.current->dispose) SCENE_MANAGER.current->dispose(); } SCENE_MANAGER.current = scene; - if(SCENE_MANAGER.current) { - SCENE_MANAGER.current->active(); - SCENE_MANAGER.current->flags |= SCENE_FLAG_ACTIVE; + if(scene) { + assertTrue( + scene->flags & SCENE_FLAG_INITIALIZED, + "Scene not initialized" + ); + + if(scene->active) scene->active(); + scene->flags |= SCENE_FLAG_ACTIVE; } } void sceneManagerUpdate(void) { if(!SCENE_MANAGER.current) return; + assertTrue( SCENE_MANAGER.current->flags & SCENE_FLAG_ACTIVE, "Current scene not active" ); - SCENE_MANAGER.current->update(); + assertTrue( + SCENE_MANAGER.current->flags & SCENE_FLAG_INITIALIZED, + "Current scene not initialized" + ); + + if(SCENE_MANAGER.current->update) SCENE_MANAGER.current->update(); } void sceneManagerRender(void) { if(!SCENE_MANAGER.current) return; + assertTrue( SCENE_MANAGER.current->flags & SCENE_FLAG_ACTIVE, "Current scene not active" ); - SCENE_MANAGER.current->render(); + assertTrue( + SCENE_MANAGER.current->flags & SCENE_FLAG_INITIALIZED, + "Current scene not initialized" + ); + + if(SCENE_MANAGER.current->render) SCENE_MANAGER.current->render(); } void sceneManagerDispose(void) { - assertNull(SCENE_MANAGER.current, "Current scene not null"); + for(uint8_t i = 0; i < SCENE_MANAGER.sceneCount; i++) { + scene_t *scene = SCENE_MANAGER.scenes[i]; + if(scene->flags & SCENE_FLAG_INITIALIZED) { + scene->dispose(); + } + } + + SCENE_MANAGER.sceneCount = 0; } diff --git a/src/scene/scenemanager.h b/src/scene/scenemanager.h index 138c977..5b39dc9 100644 --- a/src/scene/scenemanager.h +++ b/src/scene/scenemanager.h @@ -8,8 +8,12 @@ #pragma once #include "scene.h" +#define SCENE_MANAGER_SCENE_COUNT_MAX 32 + typedef struct { scene_t *current; + scene_t *scenes[SCENE_MANAGER_SCENE_COUNT_MAX]; + uint8_t sceneCount; } scenemanager_t; extern scenemanager_t SCENE_MANAGER; @@ -19,6 +23,21 @@ extern scenemanager_t SCENE_MANAGER; */ errorret_t sceneManagerInit(void); +/** + * Retrieves a registered scene by its name. + * + * @param name The name of the scene to retrieve. + * @return The scene with the specified name, or NULL if not found. + */ +scene_t * sceneManagerGetSceneByName(const char_t *name); + +/** + * Registers a scene with the scene manager. + * + * @param scene The scene to register. + */ +void sceneManagerRegisterScene(scene_t *scene); + /** * Sets the current active scene. * diff --git a/src/ui/CMakeLists.txt b/src/ui/CMakeLists.txt index b0d132a..14dadcf 100644 --- a/src/ui/CMakeLists.txt +++ b/src/ui/CMakeLists.txt @@ -8,5 +8,6 @@ target_sources(${DUSK_TARGET_NAME} PRIVATE uitext.c uifps.c + uiframe.c uiconsole.c ) \ No newline at end of file diff --git a/src/ui/uiconsole.c b/src/ui/uiconsole.c index a678b66..00865c8 100644 --- a/src/ui/uiconsole.c +++ b/src/ui/uiconsole.c @@ -9,7 +9,12 @@ #include "uitext.h" #include "console/console.h" -void uiConsoleRender(const tileset_t *tileset, texture_t *texture) { +void uiConsoleRender( + const float_t x, + const float_t y, + const tileset_t *tileset, + texture_t *texture +) { if(!CONSOLE.visible) return; int32_t i = CONSOLE_HISTORY_MAX - 1; @@ -21,7 +26,7 @@ void uiConsoleRender(const tileset_t *tileset, texture_t *texture) { continue; } uiTextDraw( - 0, i * TILESET_MINOGRAM.tileHeight, + x, y + (i * TILESET_MINOGRAM.tileHeight), line, COLOR_WHITE, tileset, texture ); diff --git a/src/ui/uiconsole.h b/src/ui/uiconsole.h index 6c4953b..a1a1260 100644 --- a/src/ui/uiconsole.h +++ b/src/ui/uiconsole.h @@ -12,7 +12,14 @@ /** * Renders the console UI. * + * @param x The x-coordinate to start rendering the console. + * @param y The y-coordinate to start rendering the console. * @param tileset The tileset to use for rendering text. * @param texture The texture associated with the tileset. */ -void uiConsoleRender(const tileset_t *tileset, texture_t *texture); \ No newline at end of file +void uiConsoleRender( + const float_t x, + const float_t y, + const tileset_t *tileset, + texture_t *texture +); \ No newline at end of file diff --git a/src/ui/uiframe.c b/src/ui/uiframe.c new file mode 100644 index 0000000..648d735 --- /dev/null +++ b/src/ui/uiframe.c @@ -0,0 +1,384 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "uiframe.h" +#include "display/spritebatch/spritebatch.h" +#include "assert/assert.h" +#include + +void uiFrameDraw( + const float_t x, + const float_t y, + const float_t width, + const float_t height, + const tileset_t *tileset, + const uint16_t column, + const uint16_t row, + texture_t *texture +) { + assertNotNull(tileset, "Tileset cannot be NULL"); + assertNotNull(texture, "Texture cannot be NULL"); + + if(height <= 0 || width <= 0) return; + + // Top-Left + vec4 uv; + + tilesetPositionGetUV( + tileset, + column, + row, + uv + ); + spriteBatchPush( + texture, + x, y, + x + tileset->tileWidth, + y + tileset->tileHeight, + COLOR_WHITE, + uv[0], uv[1], uv[2], uv[3] + ); + + // Top-Center + tilesetPositionGetUV( + tileset, + column + 1, + row, + uv + ); + spriteBatchPush( + texture, + x + tileset->tileWidth, y, + x + width - tileset->tileWidth, y + tileset->tileHeight, + COLOR_WHITE, + uv[0], uv[1], uv[2], uv[3] + ); + + // Top-Right + tilesetPositionGetUV( + tileset, + column + 2, + row, + uv + ); + spriteBatchPush( + texture, + x + width - tileset->tileWidth, y, + x + width, y + tileset->tileHeight, + COLOR_WHITE, + uv[0], uv[1], uv[2], uv[3] + ); + + // Middle-Left + tilesetPositionGetUV( + tileset, + column, + row + 1, + uv + ); + spriteBatchPush( + texture, + x, y + tileset->tileHeight, + x + tileset->tileWidth, y + height - tileset->tileHeight, + COLOR_WHITE, + uv[0], uv[1], uv[2], uv[3] + ); + + // Middle-Center + tilesetPositionGetUV( + tileset, + column + 1, + row + 1, + uv + ); + spriteBatchPush( + texture, + x + tileset->tileWidth, y + tileset->tileHeight, + x + width - tileset->tileWidth, y + height - tileset->tileHeight, + COLOR_WHITE, + uv[0], uv[1], uv[2], uv[3] + ); + + // Middle-Right + tilesetPositionGetUV( + tileset, + column + 2, + row + 1, + uv + ); + spriteBatchPush( + texture, + x + width - tileset->tileWidth, y + tileset->tileHeight, + x + width, y + height - tileset->tileHeight, + COLOR_WHITE, + uv[0], uv[1], uv[2], uv[3] + ); + + // Bottom-Left + tilesetPositionGetUV( + tileset, + column, + row + 2, + uv + ); + spriteBatchPush( + texture, + x, y + height - tileset->tileHeight, + x + tileset->tileWidth, y + height, + COLOR_WHITE, + uv[0], uv[1], uv[2], uv[3] + ); + + // Bottom-Center + tilesetPositionGetUV( + tileset, + column + 1, + row + 2, + uv + ); + spriteBatchPush( + texture, + x + tileset->tileWidth, y + height - tileset->tileHeight, + x + width - tileset->tileWidth, y + height, + COLOR_WHITE, + uv[0], uv[1], uv[2], uv[3] + ); + + // Bottom-Right + tilesetPositionGetUV( + tileset, + column + 2, + row + 2, + uv + ); + spriteBatchPush( + texture, + x + width - tileset->tileWidth, y + height - tileset->tileHeight, + x + width, y + height, + COLOR_WHITE, + uv[0], uv[1], uv[2], uv[3] + ); +} + +void uiFrameDrawTiled( + const float_t x, + const float_t y, + const float_t width, + const float_t height, + const tileset_t *tileset, + const uint16_t column, + const uint16_t row, + texture_t *texture +) { + assertNotNull(tileset, "Tileset cannot be NULL"); + assertNotNull(texture, "Texture cannot be NULL"); + + if(height <= 0 || width <= 0) return; + + uint32_t segmentsX, segmentsY; + float_t remainderX, remainderY; + segmentsX = (width - (tileset->tileWidth * 2)) / tileset->tileWidth; + segmentsY = (height - (tileset->tileHeight * 2)) / tileset->tileHeight; + remainderX = fmodf(width - (tileset->tileWidth * 2), tileset->tileWidth); + remainderY = fmodf(height - (tileset->tileHeight * 2), tileset->tileHeight); + + // Corners + vec4 uv; + + // Top-Left + tilesetPositionGetUV( + tileset, + column, + row, + uv + ); + spriteBatchPush( + texture, + x, y, + x + tileset->tileWidth, + y + tileset->tileHeight, + COLOR_WHITE, + uv[0], uv[1], uv[2], uv[3] + ); + + // Top-Right + tilesetPositionGetUV( + tileset, + column + 2, + row, + uv + ); + spriteBatchPush( + texture, + x + width - tileset->tileWidth, y, + x + width, y + tileset->tileHeight, + COLOR_WHITE, + uv[0], uv[1], uv[2], uv[3] + ); + + // Bottom-Left + tilesetPositionGetUV( + tileset, + column, + row + 2, + uv + ); + spriteBatchPush( + texture, + x, y + height - tileset->tileHeight, + x + tileset->tileWidth, y + height, + COLOR_WHITE, + uv[0], uv[1], uv[2], uv[3] + ); + + // Bottom-Right + tilesetPositionGetUV( + tileset, + column + 2, + row + 2, + uv + ); + spriteBatchPush( + texture, + x + width - tileset->tileWidth, y + height - tileset->tileHeight, + x + width, y + height, + COLOR_WHITE, + uv[0], uv[1], uv[2], uv[3] + ); + + // Top and Bottom Edges (Tiled) + tilesetPositionGetUV(tileset, column + 1, row, uv); // Top edge + float_t edgeX = x + tileset->tileWidth; + for(uint32_t i = 0; i < segmentsX; ++i, edgeX += tileset->tileWidth) { + spriteBatchPush( + texture, + edgeX, y, + edgeX + tileset->tileWidth, y + tileset->tileHeight, + COLOR_WHITE, + uv[0], uv[1], uv[2], uv[3] + ); + } + if(remainderX) { + spriteBatchPush( + texture, + edgeX, y, + edgeX + remainderX, y + tileset->tileHeight, + COLOR_WHITE, + uv[0], uv[1], uv[2], uv[3] + ); + } + + tilesetPositionGetUV(tileset, column + 1, row + 2, uv); // Bottom edge + edgeX = x + tileset->tileWidth; + for(uint32_t i = 0; i < segmentsX; ++i, edgeX += tileset->tileWidth) { + spriteBatchPush( + texture, + edgeX, y + height - tileset->tileHeight, + edgeX + tileset->tileWidth, y + height, + COLOR_WHITE, + uv[0], uv[1], uv[2], uv[3] + ); + } + if(remainderX) { + spriteBatchPush( + texture, + edgeX, y + height - tileset->tileHeight, + edgeX + remainderX, y + height, + COLOR_WHITE, + uv[0], uv[1], uv[2], uv[3] + ); + } + + // Left and Right Edges (Tiled) + tilesetPositionGetUV(tileset, column, row + 1, uv); // Left edge + float_t edgeY = y + tileset->tileHeight; + for(uint32_t i = 0; i < segmentsY; ++i, edgeY += tileset->tileHeight) { + spriteBatchPush( + texture, + x, edgeY, + x + tileset->tileWidth, edgeY + tileset->tileHeight, + COLOR_WHITE, + uv[0], uv[1], uv[2], uv[3] + ); + } + if(remainderY) { + spriteBatchPush( + texture, + x, edgeY, + x + tileset->tileWidth, edgeY + remainderY, + COLOR_WHITE, + uv[0], uv[1], uv[2], uv[3] + ); + } + + tilesetPositionGetUV(tileset, column + 2, row + 1, uv); // Right edge + edgeY = y + tileset->tileHeight; + for(uint32_t i = 0; i < segmentsY; ++i, edgeY += tileset->tileHeight) { + spriteBatchPush( + texture, + x + width - tileset->tileWidth, edgeY, + x + width, edgeY + tileset->tileHeight, + COLOR_WHITE, + uv[0], uv[1], uv[2], uv[3] + ); + } + if(remainderY) { + spriteBatchPush( + texture, + x + width - tileset->tileWidth, edgeY, + x + width, edgeY + remainderY, + COLOR_WHITE, + uv[0], uv[1], uv[2], uv[3] + ); + } + + // Center (Tiled) + tilesetPositionGetUV(tileset, column + 1, row + 1, uv); // Center tile + float_t centerY = y + tileset->tileHeight; + for(uint32_t j = 0; j < segmentsY; ++j, centerY += tileset->tileHeight) { + float_t centerX = x + tileset->tileWidth; + for(uint32_t i = 0; i < segmentsX; ++i, centerX += tileset->tileWidth) { + spriteBatchPush( + texture, + centerX, centerY, + centerX + tileset->tileWidth, centerY + tileset->tileHeight, + COLOR_WHITE, + uv[0], uv[1], uv[2], uv[3] + ); + } + if(remainderX) { + spriteBatchPush( + texture, + centerX, centerY, + centerX + remainderX, centerY + tileset->tileHeight, + COLOR_WHITE, + uv[0], uv[1], uv[2], uv[3] + ); + } + } + if(remainderY) { + float_t centerX = x + tileset->tileWidth; + for(uint32_t i = 0; i < segmentsX; ++i, centerX += tileset->tileWidth) { + spriteBatchPush( + texture, + centerX, centerY, + centerX + tileset->tileWidth, centerY + remainderY, + COLOR_WHITE, + uv[0], uv[1], uv[2], uv[3] + ); + } + if(remainderX) { + spriteBatchPush( + texture, + centerX, centerY, + centerX + remainderX, centerY + remainderY, + COLOR_WHITE, + uv[0], uv[1], uv[2], uv[3] + ); + } + } +} \ No newline at end of file diff --git a/src/ui/uiframe.h b/src/ui/uiframe.h new file mode 100644 index 0000000..b5d844a --- /dev/null +++ b/src/ui/uiframe.h @@ -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 "display/tileset.h" +#include "display/texture/texture.h" + +/** + * Draw a UI Frame (using the 9-slice technique). + * + * @param x The x position to draw the frame at. + * @param y The y position to draw the frame at. + * @param width The width of the frame. + * @param height The height of the frame. + * @param tileset The tileset to use for rendering the frame. + * @param column The column in the tileset to use for the frame. + * @param row The row in the tileset to use for the frame. + * @param texture The texture associated with the tileset. + */ +void uiFrameDraw( + const float_t x, + const float_t y, + const float_t width, + const float_t height, + const tileset_t *tileset, + const uint16_t column, + const uint16_t row, + texture_t *texture +); + +/** + * Draw a tiled UI Frame (using the 9-slice technique). This will tile the + * center components to allow them to repeat without distortion. + * + * @param x The x position to draw the frame at. + * @param y The y position to draw the frame at. + * @param width The width of the frame. + * @param height The height of the frame. + * @param tileset The tileset to use for rendering the frame. + * @param column The column in the tileset to use for the frame. + * @param row The row in the tileset to use for the frame. + * @param texture The texture associated with the tileset. + */ +void uiFrameDrawTiled( + const float_t x, + const float_t y, + const float_t width, + const float_t height, + const tileset_t *tileset, + const uint16_t column, + const uint16_t row, + texture_t *texture +); \ No newline at end of file diff --git a/tools/assetstool/processimage.py b/tools/assetstool/processimage.py index 7ed0ec6..8038cd7 100644 --- a/tools/assetstool/processimage.py +++ b/tools/assetstool/processimage.py @@ -39,11 +39,15 @@ def processPalettizedImage(asset): if all(color in palette['pixels'] for color in imagePalette): break else: + 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 not in palette: - print(f"Missing color: {color}") + 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")