From bf8b1b095be3bbe097407458e631ca3c2b98a7bf Mon Sep 17 00:00:00 2001 From: ZTL-UwU Date: Thu, 27 Nov 2025 21:11:35 -0800 Subject: [PATCH 01/10] keycap art Signed-off-by: ZTL-UwU --- assets/prototype/keybind.aseprite | Bin 0 -> 853 bytes assets/prototype/waveforge.aseprite | Bin 7755 -> 10338 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 assets/prototype/keybind.aseprite diff --git a/assets/prototype/keybind.aseprite b/assets/prototype/keybind.aseprite new file mode 100644 index 0000000000000000000000000000000000000000..931416235263acae0d09258c74d965307e4702c4 GIT binary patch literal 853 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

Xf5>){x) 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 From 4c5581c218986bb921823b7d3d0de385fa155d1a Mon Sep 17 00:00:00 2001 From: ZTL-UwU Date: Thu, 27 Nov 2025 22:32:47 -0800 Subject: [PATCH 02/10] keybind prototype Signed-off-by: ZTL-UwU --- assets/prototype/howtoplay.aseprite | Bin 0 -> 3346 bytes assets/prototype/keybind.aseprite | Bin 853 -> 3073 bytes assets/prototype/waveforge.aseprite | Bin 10338 -> 10405 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 assets/prototype/howtoplay.aseprite diff --git a/assets/prototype/howtoplay.aseprite b/assets/prototype/howtoplay.aseprite new file mode 100644 index 0000000000000000000000000000000000000000..050ae22f7000614f4484ac6e99988172efa04a9d GIT binary patch literal 3346 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 X+izTaHz83JS)R{yD_8Ne$&395DhuN{ literal 0 HcmV?d00001 diff --git a/assets/prototype/keybind.aseprite b/assets/prototype/keybind.aseprite index 931416235263acae0d09258c74d965307e4702c4..d51a536cf57ef3ecf3f0f2aa9c99d43433e57c1f 100644 GIT binary patch delta 2497 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 literal 10338 zcmcI~dpy(q`~SPTOGoZo-JKYfTPI4$ag9n69Z2QO)SMz@!(vVy-9?+T6iFz;mYfwTSG*ZX>1uj|^Cy#VmFco|^3 zym{FUU>N`aKH&TRyi9;)ib3Fa@bCY}>-!!65YPVhi|cP7gU#SuX~!>?|BIauIIwMp z$$v(S|JRlO^-&H0Mh9Ua9zX61;>C*>L3DGw4x+cWH;9FB0}$U`SIp`5wE&S7;{@Vq zst$_M!8aPfIJmT}oY%_Wsn`n_4lm&re;vr`NIm z6kPAcrj;y&q>W}Vf|Fz= zRj_pK_BU4MC+fxaK7n$~OMR_~EndRUwab8`*2PgDWUh7n`$nKWFAOQmf##2XYX16U zm*eBQLS(_Dj;I|p22Km1ZIh)_&aIGTUie^|dh(4sa z`<Tc~Ksnk~yc#kYzwZVn`4oc1xiL_4}iIw}{+NZVKaFe7whl zR>Dkf+~kxU1`g~t9yd+O!JJz|tNPgL>k;*U4q7G_)_T#+gPI1VsJe~Ss1sKJ3F4UG z5Z%*17eP^X(8|Yk3&J+ikfr0gf4%_){W+!o!ZB?9c(i7zXu}tQE%E6m`axP1064&L z`hQZ7B&jBYUk+7nBfBodYz8dkQSz9$FFKYMa=zYE>a@fm6|eKUFblE2U~+94d)nk% z^k%?haLCH?MTd#(HhtMCyKOp^@#h+CTmm@7uj@f#r_$13MeNm_fYz|hhU@aSkD73f z8ZSSz8j5)o3z<+pdT)qwPplV+1J$e>Fm4t80Qu%(ohqpR%s6of^G@<&lTSO;1k?bf3m;#{0kTyU1J++Iv}~!{*-NQK|Ti zuM0I0`)dgPpGP{0p)MV!NkmVi(#fJX_i~_Z8c`mov&-fugHS=qjdwt|0T*}hF-5&T z1v-}lGCtUh^;7r1|M1CM(4Bdm_MnSA?v<+hV3FpSQ?wO>=W+U(4}$B1;sWQRvj*q< z;41mlTdAexvju32NE!E~WwflVL30mAz1ei#j>6W6{bmR~59;!a3pp(uo&(6>9FT$l zZDNtXvcZu!{zNmLS3G5*^27XLN0ZIgeCqP#orj%N*$pZ6Pd{;>X4+#Wx`!#>*>(HM zwR)0spcl_uo~B*(VaA$+>*T#&8=-C1*-wEaS9$xZ60K%hoZWa0_Z);Nc(vDsd9ALF z5~#AD*0Y>3qE)cG)7~-)ksqCGlR5N83FP#wd1!xW=cg>C7d=&^%bE0ow{H@|^8lck zTAl&j1O?Sq0sy)XZf1(E@6PO*;(@{ZTC|4^bzhDVDqtQBI*dgow21AH)S=NE0Yv)1 z{l7QxR*ICBgnkr$3VvNeR4NqsIMr3aN`JM>a}R*pg$DlCM>IxyJf zmzk+O_0!Rsf}B_;E!t)!5W6Hok44rtSxOA<60ZfAc}Em*{%|+RfE{O-mKTTw-$JD+ z)D5c+X=iG4=EN^8ZtSb{jqO|p?J;Z7z}UJvv({rc+SS-;hC`IqjD2h|$?6WT%3y)M ze#d8#jKey(?K@mP$I`Qo<*{21Wp6m3#QBg3bEHN1;(F#@#am5o5JNxa67cwglaxIF z+{v*JnGH&4VEaYN~7kT$$*<*ZCnt`Ki?mH|@*MI@`!KX|8#k$24)Cve?Uymp9n8w5!BR>C=7kMGbZV0BY_ z*2P8=C=h(uHtCJ45R;Q!a38exRjy)uKogAe^gL*zy^Qn8VAL42n~tdVBi(B}B87Z9 z7`4loa1mzDJ(@6&v5J-hyPu1pUF?9^{BLFad+d65G4z2aOF8erJe4^ zgS#=-odhSZ`q`={+W6{YmLUs0MQECQ#5nI6o_sgxYN8)Qer#Gme|&?V1Bp?^Ql-oauN`A`!_KLbKC)S82F969~hS zQ2E5<_bzYdz1-kOIaV{4CYX}Ri_7s`rJv&bwq?p15>%NDNuwmO!P9TBUzmvYN4o?@}VBFkmPQ_#KV7GKmPHj z*`b%CtjOH*MF^hi6KqyD?$gb?K11rKD}SQV3MH;v{fBM`*2Vp(;1M(67;0#KzI2dv zP}>Nt?kZ|CO>BIaMlYyKePY%Xfnxf{ELD;zuQDc-VOhuHm42VdrS8l%JNL3J*AWxv z%QGux*63EC$OQR&OZrq$r6j5i!bbTj32JP(ugPXe{EtH;(RpS_mf!Sb1b6R@-e+xJ zsg@MNgf+K8621uD-ZYEz_ET=Bf7_2|2dMQ?8{%mZ)f!m0H$~%pr9~nI z5sC)>IOqW<;>n-shuGo@y#M_QrK7pw4AQ(zZ$8#GLU{epR@VW4A^OqcP5S3ZDLv@& zBgV2br|TgZWAY76m?APa^BmXZGt7#@!YZ`ouA^Oa9UBLLKknijzI?^BPu}~HoB^H=O&Yk$tBol5KE5O~<4=!>s zM=yw>yG=I(YB&1E+*Kk%NmdfOQMs>~fT+u)eT*oJVn{3^7onkXGsO76cA|j<-552eF%ko_bwA4m4n4`}+L;PPvX8|pIeHF%?Gb3c zDq-yjD02ptc;b+Mf2&Tf#J>FY!V-3i*Ns_v{k+ACm7aC|^{@fUCpi0FCqlR`Abf2X zpBM@qo$`1l7nb#Y_kTmaT-U0-yNUP7j=bDkQ;Vrg8z+uTiP28}cQoJ_$d~6%fxkFl zw(ms0ecI^?{|n4vHXe|`+8}ikO)@_d&aCz?%=&>O`W|VkwBJye9ji-ReD84%=JG}ZOy!s# z1iGH1Z_#&SsSlc^q+9Hq;1Ei*63-+OG13(MX?nulBm#yR{eV~ogny7i>tCfok4yh@ zB(`u*x0_|9(NileuT2Z=V}F(>g?l%deHWfMmZYXIU!yj*9?ufiX>kzXV zh~k-}01*)kLyhR+&dsd)nXY#>H+X_b=w{Zel()sQ zaHPC6zV6}ppt9mt33P0*edC^Z?$n|Ns(;zf``C?-##ins>z;W#7DE1h!~#jCPw`yb zSz%df+pG?u?@R-4%li}yNEyE{({B%tx+-Q|9XG9gZkX8N2`U$Bj*1a|(bPttk(sPL zTk-GIV8L2qiz&9EhgNBPGpe|-dNH|bN4Cx$$%BdL21sWG;zF>XP@efx)@Y1H=zyjO z&V`nY%V+W*TNIZ-pO(=VN(n$y;_zr$D zwkP&}SQhS`WR{;ewm3Mn1g1vbzyubI*aNj!xh}Ulwlmo;h!q^+o=( z&_#yFafDv-TA$}2fq#a4glDTcq8(`O;~rE4Gf<~C2nzI!pS#d!IH!5>OV^mnf~xR5 zhb??N*2g8~=A^K!`=z-B9W-9!qPR?Pfr#+(pzit*5kHcFp$fbR1CiBbOf4>Uz=g^N&b(8m4hSIqiZf`*To!cCGj&;a&9! z?u~Hu5eHW*T;(Cko83tAySMPGXp`ZtL={ahyTG`;0agUs7m2~?hxGG>QffB@FPPy! zdUIwkx}-MxF8#7P+39WQ;P6)eoGns_rzaJ2zviwJX3B%wACfpwB|aM_F4#)) zU2tD5PNNd_2&LeS67*Jb<(~$i{_025_7ko2LgUZ6QfUl*6!$vBxul4x%vCcs`DLG@ zs-I=b@Dau?^hB+^Q4BRuD4R=xKs6-F;ams)ks|i;_2t#_f??CsI2pDV`4_rKSd{`5 zf_uFham6X*)$uDR{eC`vsVA&4P@(?xXH>q24fG%LA5CLNUZ_Kh{rR&FaZ}Y^vy~Yc z$r^n)GH4ms*D_x6sCWw-}nx^e=G`AtE}1exf0(cN!L zY-3;mj6XJ7UxaramK*#GJ8b=r1jo6L_Mkn4tRTe9^AQhc=+ie(^;G)WARW)hpEoJo zOnJu|A_7kjGjGS0s%!WUW#*uZ*tf*dqlAm2w2r2H<`b{QS=a0=$*TSPg5l8wXNgN- z|Lt0JThQ6rnb3KdQ8yXPRe>iINUz|inmM|9Oa+W?DYsWpj5W0_G0G=E?;p@XzfFNARCq9WWuFB)E+6rt(<65&>M_RZ39TVM$~0Ej zr?}l{&ii+Xe}iF8Q)H{u^S*+5x5{$eL17gI;)8?rFe;73P~b4L3xbzzKq_tewRFJt zBq4ebmkt$vTEf=olU5v60@t#E*J47tO|P`z^8*xW&eOr{JmDVRvPIU$sw@$-L)dJo zS@={ZhqnU*1WcHtr#LC@T@m&Z*vYW7ox^WmtUQY7x4>o5E0x4Y22SQHCA8Kvv3l=> zH~D*uzbtyH4k3blRDcZ;osd@*a`3#W@gfD*k%KtkgA+Ug5xms5a4oAyfAbFuWifO* z%opdhIIc)zohB6zp`#n8uy`Ar*P$_?0wnx}dH0DXFUU{&6N6z!Ml{U0w_eKE>ZCZy zHxmf3vF~ho5B1Z7Mi{&{>`&)dtZtCz_>>@YR=m79*>R+!(7I;m)y%o8ybJuJ)ZTho zg%_i*_KlORVj$tf)k|_Gf*#P-LG!C0?jD?D8r4S=UE+_~x##xYqq@nB3DeYx>axno z?x1j7^=zcnaro!w=}--u5?KWt4jvz;qW-9#$->*?huA9rR;&J>{|ncm0ifs8;`!t7!Z`rInW*nJ8u3JVS=luZIvOz|yJ0v>7E5d?&vqq!klKi1 z4a7#<)u8~;aNiH>MbsL||Mn{1m8wP5ieM<1KJXmw%;*ZUTgtbrk~Y@J`z(~;+_u5^ z*(M!`;x6K6Vg2x2y3yvawbi5p$iz@N3~4itPy)P7XfwWh!7^8PgBuh*C6x@v(PTlB z*tFPm@V>oi8BjM7`s1)HOA67J>;{j|4tHeWN&~=@_CEAra2SvNO-yv=2Ha3+vHLMJ z98;D=7!A&Z#z=Un(CwNE=4+VPGzq%84I-3TiFhS>(Cr$E;rmQ%t|apnF}ng~vscZR zOA3s)d0*-OMRMpgr?M|1hRqx}pO*xW9cc*37(|q^7Y2gE@XLU+7bW>{IRA4Ysjyd; zj#92XVT>cb;970wgiwqU+Zo|9sLxy>~yqo6SFx zz7sE^Ned8`xeygSx|y{d==u0i9&9C7deS*8*L+fZ7`>Wfd6lt83w_Z-cuixifJ~35TjG${p>Hi9ND(5vbK9 zg39bTRZ#BdzuJ>K)~Mz&)}>2~2%lWjZ}Lseuu^i+@j>1(w_LV*0YBgI3gI$aaJu6Z z%l>&++>#Mn#iy5d3~93iHRZTCSVbIGgKOR^y%G z?sG9^k+d3KAAibiAV!aMZ6t;+knShL3n@*c0|zL7WF6;%JI1vFxgLiRn`iA2uhtc% z$*fn=IAWR$MYf(gKJ)?o5Ik~8>EEL5C1757Q1q@|cr~5?XAR-t5UXx;iGlq?)NSoc z!RQJDv8)(e+eMSsr7`Y^194X$?G*_rvoXIk|zjkLv}5^$rr_bWT0|bU;!!ZtTm;X zc@SGP{Aq*>CGd~^CYj=Q1URhr99QNhCGpA{cv6k+Hm7FRU7w+iy$sD8!jb&)Fg_Om z14qLX-D&RRzV+v;sue_72aLInb2ljRS)&2{ktPjd;k?J&EPZ&QL)_{Lwp(cCIN`iM z4Rd!9((Kj-8E0Z0C|kAB8Gfpz^gmuz1#OdH;FQ0Ezto-M(Lc{E1ofFY5w^2dCFp{= z1hKG4-L;Ns96gE6;AcHnxiS+{GFokTZ;{U-!*w>^p=c6UxY*!wFK5%u)1cFn%I&IU z@iTlAw=lt;M`lIB*+k^MQ0xuv8zaMGNhAo+sx>Pzsl!fS%Zef~^H)Tn^V7Vo#cx{+ z+*ryq%qI(;SYI035uwMd?2DFiP~Z{u_3ry55;5X0bPCjt!U)g?Rh2Aw4t>bn!ePbm zPsUQgk{(_J1;t4G$Y3a>V0LY*j6}brry3+aR|s%oFQ!Q5(I69AfLm-2P6yrf)T#}_ zwYd9Kq!Zz*((tq?-wJWC25D2?XKDf@jCjPO{Hc(p+8cHBZ}lSgvT^j}-x*EWJ&hEP zp-Lz2PeJ>2xiS#YDZ_-y&t`?#;A$HiCb15yrlOXby1s1>wUw&B+YFxi&0e+=mjUz_ zBeyb9iBftQ6=oFCfSh0II}%5W!ZjLKN?wa=t++tK;!FzEO-9lynD>}}#P~Mda{WMh z`lY~lyk|#LSy%`F)ZIlUhR2ychJqDEot=V(SXN=AXN+{!zVSW#qT0ut9-1dwIA6aj zT3x@5Tg6Ztnl$}<6I9bHk^(6(%YkvzLufnuYM*FN8U_aWE{8rzg*trgeK9RcUL;P| zm#{o>PpJ8=q1kA&bKw&m5S_`Cf${aKNK|6nJJCD0hTuBBt?r&Q=%ErdUbX{9Ef9Ap zYTiptAg%2TdtT+jeWZ`!93g2LQ=eON4~a6xQMyersdF9=&B=D)1LbQy^4)rP+!MPo zuef@|Fmntf?MS_?x1zcvDKFv*tyBEQdM~CAOgmy)rO=eG;>H})+TVk5O2O%%JgXge zcl!zcSBJQEdtsUtJhiMe&EtHt3k;OeG(B2Zsq;ZCc~4Nx_e}xmN+Gyx({nUNx*}*R zoK_b7l^;!W;8(3BR3If63!rn5uMvs=C*SehCmmL@?E_)Kx)Q*a0$6l4sivDG<3#HM$g1 z@FFsY)H$`Q6jp1%xjqwiOB?$|Aks1xjdD)6T@8(idM@}yVm|B%Ssx=OQ;01zihI0f zeP8jV((|lA`7R^MhOBU@#rhg@LH^1S1g-IKjA&sxEO#sxs-g2eaJ--QHYjHN{cWW> zaJrE{aDv;s>@!1eT%;=&l_I#Nd2_?a_VH`Rgz!(ud39R{0W&&_SfoMH zi?^l0hhKqf_Tidk*(L-=%k{fy@yK(HV%B20bw8$`ep&wRH1`m0C=+bxsGPZWUugPX zmAr3sb1IbXVTKScLYjiR7hUNAKs}NhdGY$QykOr!bpxYfZ;j&|= ztI2D!OwawO{hR(Rt2cz|6iJ2P`QBq?&h@h9kFRP!Ic;nIm+-|#t8raJKb*^GF?I3$ zK62BrWD|h2t+Sp`u5;n0SiH0`AY*A&3)onpXdmFcArgFIgNkY!p%cpRRpVfHs(H1C z6jBAZQS02<%UUW)>pBLOw35C1>6Maqj*9MHavLb0CJO9ifmL|#BLoXkhM{{3wACqE zBRmyv33$Vy0(1{|Ik2NsVOu=LcUEoz98C)!adC9ZOS~k3IyGW91HZGL*!&)?8v$X| z7l;Sh)5f;3Mk6|yZ8RiPbf9kJdxWb#`XlmG5`Q(YHAyOx%cPRJ;oqt}v9QoBsKpH7 zCmOe5*uCLkBxSbdOHJL#bcE}S`EWlgb2;#xBWw;q&5-9s_(-krhu$IF)is2IuCIY? zk6?KBOl`EMq7~5pc6cbmn5IE`ixGuXH-661cG7Inh_p=B)0~Q1<2TQ_Q?FBfNGO z>;e3&*?_lL@652~n9;VL4UFXo+bJjf;$E{unJeMklA2K`!s!{=!|fwxZ@>c+(A=-Q z(CQw8(!YRAbUiM(UlWXH#RQ|ySyEDGYHxVP5L=A0CZnZ}13E)q9WqCaJ!qi$siWM+ zi2N6r2IPt^<0W6;-`-Dbq3AUc!I)&L>)tmQQbyB0Oe#EiFQo%QC#?X4lY!};v1+fn zA9|+0dY}_O0aoB55ppP5yw@$HuAlwA1JcC#BYI4f8)bp4-7@kbM{k~A;9(Q51aMkt zVD)ZGfyD~=zN;@0Q9H7|BA_W}*DEP)c_XsiOsDj)TeX_BUQYLgT~7AioE>`3S@w z&?VS4%K`7mLa@-TzRo%8I*j*Z{5-0m(W@*qOalN6)7p+5KxN!f^x^r**5Y?pds&xh zd}Tn@L%KFnerr_zwf^LXe%j~2hF(^e5NyW7*mFON?Y$lZ!n??xogcF

Xf5>){x) zAN&{HNLk>Fvp8l&!e8(P-ge$=-mEX~$wI4bV5qY=cPJ*ZE-?9j;&z)af z1=tGd4;h%@Oi) z2fvaU&mSni^rw1Qb3X^m zug?vAqE!>Gl}mam$+TbqJKBwmRkxe0d6-n>rH2VuC>U8pZ6qV{zBb(ut*4~nPTTxY zAZ4Nxh$P(&4Jm;|s&;NRs?~|5T+^O$41gfXE-ZrZANl5dmrJ&*GHUkkLmdzLrEL*w zUD4*MSoH&F&cz~y3J)f4x?q0_Jge#Wd9y0pIz(lzesp}Is~(rJ3S!%#v#mR|NWQ0g*-QxjYCJiBXcgVG zk`GOIL0Pl1`8OnrTrvYM$K8~mfiFbLCN%d5s<6Z?T8P+Vu=>#5OH6d~VbOE4f?z;f zj?oojOZqWjy&sFN&(Br)!QXILwQD2Drb820mG&4qzcpbr&W;98e;WFJN%`yRBHh$< zu>0S9L%~K0@oRh=uXIQosr33LU0&ti27$HO6=kPeFpm1Ro9!||r@#^_%MD0c171{x z#5Xs9rKqzRU;&T;QhWBiysKaxam6Eh%3#M7QLE3gSqe?K?CY;A!MssAgv%5|-5zEp zzg0XQ`#Ut?=krY{OOD>>h(tu6OqR;@nja)6E`jZEwS6t~(x3@Z zdwnSp@j{YWKg`Zy zAyc5QUvoGq(5B;DpfwFj@b07UgfVU{LXQ3cYMTT8nBq$8dNAy6RVA(%Jad)1bw;HLVJH(JW3D%j%c+&2F Date: Fri, 28 Nov 2025 14:17:40 -0800 Subject: [PATCH 03/10] add key guide scene Signed-off-by: ZTL-UwU --- CMakeLists.txt | 1 + assets/manifest.json | 18 ++++++ .../key-guide.aseprite} | Bin assets/ui/key-guide.json | 4 ++ assets/ui/key-guide.png | Bin 0 -> 3225 bytes assets/ui/main-menu.json | 19 ++++++- include/wforge/save.h | 3 + include/wforge/scene.h | 18 ++++++ src/main.cpp | 15 +++-- src/save.cpp | 2 +- src/scenes/key_guide.cpp | 53 ++++++++++++++++++ src/scenes/main_menu.cpp | 11 ++++ 12 files changed, 138 insertions(+), 6 deletions(-) rename assets/{prototype/keybind.aseprite => ui/key-guide.aseprite} (100%) create mode 100644 assets/ui/key-guide.json create mode 100644 assets/ui/key-guide.png create mode 100644 src/scenes/key_guide.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 86be9e3..afdc5c2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -91,6 +91,7 @@ add_executable(waveforge src/items/water.cpp src/items/oil.cpp src/scenes/duckdeath.cpp + src/scenes/key_guide.cpp src/scenes/level_menu.cpp src/scenes/level_switch.cpp src/scenes/level.cpp diff --git a/assets/manifest.json b/assets/manifest.json index 40caeef..e58e0a4 100644 --- a/assets/manifest.json +++ b/assets/manifest.json @@ -435,6 +435,24 @@ "input": "ui/level-link/raw", "description": "Creating level link texture" }, + { + "id": "ui/key-guide/raw", + "type": "image", + "file": "ui/key-guide.png", + "description": "Loading key guide background image" + }, + { + "id": "ui/key-guide", + "type": "create-texture", + "input": "ui/key-guide/raw", + "description": "Creating key guide background texture" + }, + { + "id": "ui-config/key-guide", + "type": "json", + "file": "ui/key-guide.json", + "description": "Loading key guide UI configuration" + }, { "id": "ui-config/level-menu", "type": "json", diff --git a/assets/prototype/keybind.aseprite b/assets/ui/key-guide.aseprite similarity index 100% rename from assets/prototype/keybind.aseprite rename to assets/ui/key-guide.aseprite diff --git a/assets/ui/key-guide.json b/assets/ui/key-guide.json new file mode 100644 index 0000000..5d3c0f0 --- /dev/null +++ b/assets/ui/key-guide.json @@ -0,0 +1,4 @@ +{ + "width": 256, + "height": 192 +} \ No newline at end of file diff --git a/assets/ui/key-guide.png b/assets/ui/key-guide.png new file mode 100644 index 0000000000000000000000000000000000000000..0ae63f85e57245026332f0a2498884f11ab7f0ca GIT binary patch literal 3225 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 a3lH(SBonRT8cCZ!4~w%lXI`1P-u@S;?G^|C literal 0 HcmV?d00001 diff --git a/assets/ui/main-menu.json b/assets/ui/main-menu.json index ba7dd85..cd64e4b 100644 --- a/assets/ui/main-menu.json +++ b/assets/ui/main-menu.json @@ -50,7 +50,7 @@ 255 ] }, - "exit": { + "keyguide": { "x": 72, "y": 128, "size": 2, @@ -66,6 +66,23 @@ 9, 255 ] + }, + "exit": { + "x": 72, + "y": 152, + "size": 2, + "color": [ + 0, + 0, + 0, + 200 + ], + "active-color": [ + 207, + 158, + 9, + 255 + ] } } } \ No newline at end of file diff --git a/include/wforge/save.h b/include/wforge/save.h index 77a3de6..81a8751 100644 --- a/include/wforge/save.h +++ b/include/wforge/save.h @@ -21,6 +21,9 @@ struct SaveData { void save() const; void resetSettings(); void resetAll(); + bool is_first_launch() const noexcept { + return completed_levels == 0; + } SaveData(const SaveData &) = delete; SaveData &operator=(const SaveData &) = delete; diff --git a/include/wforge/scene.h b/include/wforge/scene.h index 521073c..643c40f 100644 --- a/include/wforge/scene.h +++ b/include/wforge/scene.h @@ -253,6 +253,7 @@ struct MainMenu { int _current_button_index; ButtonDescriptor _play_button; ButtonDescriptor _settings_button; + ButtonDescriptor _keyguide_button; ButtonDescriptor _exit_button; UITextDescriptor _version_text; }; @@ -321,6 +322,23 @@ struct Credits { std::vector> _content; }; +struct KeyGuide { + KeyGuide(); + + std::array size() const; + void setup(SceneManager &mgr); + void handleEvent(SceneManager &mgr, sf::Event &evt); + void step(SceneManager &mgr); + void render( + const SceneManager &mgr, sf::RenderTarget &target, int scale + ) const; + +private: + sf::Texture *_background_texture; + int _width; + int _height; +}; + } // namespace scene } // namespace wf diff --git a/src/main.cpp b/src/main.cpp index 5de2c09..5d54f00 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,7 +13,7 @@ #include #include -void entry(const std::string &level_id, int scale_config); +void entry(const std::string &level_id, int scale_config, bool is_first_launch); std::filesystem::path wf::_executable_path; int main(int argc, char **argv) { @@ -64,7 +64,10 @@ int main(int argc, char **argv) { } CPPTRACE_TRY { - entry(program.get("level"), program.get("--scale")); + entry( + program.get("level"), program.get("--scale"), + save.is_first_launch() + ); } CPPTRACE_CATCH(const std::exception &e) { std::cerr << "Unhandled exception: " << e.what() << "\n"; @@ -75,10 +78,14 @@ int main(int argc, char **argv) { return 0; } -void entry(const std::string &level_id, int scale_config) { +void entry( + const std::string &level_id, int scale_config, bool is_first_launch +) { wf::SceneManager scene_mgr( level_id == "-" - ? pro::make_proxy() + ? is_first_launch + ? pro::make_proxy() + : pro::make_proxy() : pro::make_proxy( level_id ), diff --git a/src/save.cpp b/src/save.cpp index f6e5ecf..a2bae5f 100644 --- a/src/save.cpp +++ b/src/save.cpp @@ -94,7 +94,7 @@ SaveData &SaveData::instance() noexcept { } } - // Please don't relay on this saving mechanism + // Please don't rely on this saving mechanism // it only works as an extra safeguard against data loss // Please save manually whenever change is made std::atexit([]() { diff --git a/src/scenes/key_guide.cpp b/src/scenes/key_guide.cpp new file mode 100644 index 0000000..819ed7d --- /dev/null +++ b/src/scenes/key_guide.cpp @@ -0,0 +1,53 @@ +#include "wforge/assets.h" +#include "wforge/scene.h" +#include + +namespace wf::scene { + +KeyGuide::KeyGuide() { + const auto &json_data = AssetsManager::instance().getAsset( + "ui-config/key-guide" + ); + + _width = json_data.at("width"); + _height = json_data.at("height"); + + _background_texture = &AssetsManager::instance().getAsset( + "ui/key-guide" + ); +} + +std::array KeyGuide::size() const { + return {_width, _height}; +} + +void KeyGuide::handleEvent(SceneManager &mgr, sf::Event &evt) { + if (auto kb = evt.getIf()) { + switch (kb->code) { + case sf::Keyboard::Key::Escape: + case sf::Keyboard::Key::Enter: + case sf::Keyboard::Key::Space: + UISounds::instance().forward.play(); + mgr.changeScene(pro::make_proxy()); + return; + + default: + break; + } + } +} + +void KeyGuide::setup(SceneManager &mgr) {} +void KeyGuide::step(SceneManager &mgr) {} + +void KeyGuide::render( + const SceneManager &mgr, sf::RenderTarget &target, int scale +) const { + sf::Sprite background_sprite(*_background_texture); + background_sprite.setPosition(sf::Vector2f(0, 0)); + background_sprite.setScale(sf::Vector2f(scale, scale)); + + target.draw(background_sprite); +} + +} // namespace wf::scene \ No newline at end of file diff --git a/src/scenes/main_menu.cpp b/src/scenes/main_menu.cpp index ff97432..f35401c 100644 --- a/src/scenes/main_menu.cpp +++ b/src/scenes/main_menu.cpp @@ -14,6 +14,7 @@ namespace { enum MainMenuButton { PLAY = 0, SETTINGS, + KEYGUIDE, EXIT, BUTTON_COUNT }; @@ -67,6 +68,7 @@ MainMenu::MainMenu() _play_button = parseButtonDescriptor(buttons.at("play")); _settings_button = parseButtonDescriptor(buttons.at("settings")); + _keyguide_button = parseButtonDescriptor(buttons.at("keyguide")); _exit_button = parseButtonDescriptor(buttons.at("exit")); _version_text = UITextDescriptor::fromJson(json_data.at("version-text")); @@ -114,6 +116,10 @@ void MainMenu::handleEvent(SceneManager &mgr, sf::Event &evt) { mgr.changeScene(pro::make_proxy()); return; + case MainMenuButton::KEYGUIDE: + mgr.changeScene(pro::make_proxy()); + return; + case MainMenuButton::EXIT: std::exit(0); return; @@ -168,6 +174,11 @@ void MainMenu::render( _current_button_index == MainMenuButton::SETTINGS ); + renderButton( + "Key Guide", _keyguide_button, + _current_button_index == MainMenuButton::KEYGUIDE + ); + renderButton( "Exit", _exit_button, _current_button_index == MainMenuButton::EXIT ); From 407a85c2606134a5c5b2ad9017824f228edda037 Mon Sep 17 00:00:00 2001 From: ZTL-UwU Date: Fri, 28 Nov 2025 16:40:01 -0800 Subject: [PATCH 04/10] reformat key guide page Signed-off-by: ZTL-UwU --- assets/ui/key-guide.aseprite | Bin 3073 -> 3268 bytes assets/ui/key-guide.png | Bin 3225 -> 3634 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/assets/ui/key-guide.aseprite b/assets/ui/key-guide.aseprite index d51a536cf57ef3ecf3f0f2aa9c99d43433e57c1f..2087765d8bc4c61498f2c13edb8d9af74fbf1031 100644 GIT binary patch delta 2677 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 GoBancONa^p delta 2455 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 a3lH(SBonRT8cCZ!4~w%lXI`1P-u@S;?G^|C From 1213f73a40d4e5b36b01cdc9ca817b9734439529 Mon Sep 17 00:00:00 2001 From: ZTL-UwU Date: Sat, 29 Nov 2025 01:17:17 -0800 Subject: [PATCH 05/10] add pause menu Signed-off-by: ZTL-UwU --- include/wforge/save.h | 4 +- include/wforge/scene.h | 22 +++-- src/save.cpp | 4 + src/scenes/level.cpp | 182 +++++++++++++++++++++++++++++++++++++---- 4 files changed, 187 insertions(+), 25 deletions(-) diff --git a/include/wforge/save.h b/include/wforge/save.h index 81a8751..4c8d737 100644 --- a/include/wforge/save.h +++ b/include/wforge/save.h @@ -21,9 +21,7 @@ struct SaveData { void save() const; void resetSettings(); void resetAll(); - bool is_first_launch() const noexcept { - return completed_levels == 0; - } + bool is_first_launch() const noexcept; SaveData(const SaveData &) = delete; SaveData &operator=(const SaveData &) = delete; diff --git a/include/wforge/scene.h b/include/wforge/scene.h index 643c40f..f926544 100644 --- a/include/wforge/scene.h +++ b/include/wforge/scene.h @@ -82,6 +82,14 @@ class SceneManager { int _scale; }; +struct ButtonDescriptor { + int x; + int y; + int size; + sf::Color color; + sf::Color active_color; +}; + namespace scene { struct LevelPlaying { @@ -105,6 +113,12 @@ struct LevelPlaying { int _hint_type; int _hint_opacity; PixelFont &font; + bool _paused; + + // Paused Menu + int _paused_menu_current_button_index; + bool _show_keyguide; + sf::Texture *_keybind_texture; }; struct DuckDeath { @@ -242,14 +256,6 @@ struct MainMenu { const PixelFont &font; sf::Texture *_background_texture; - struct ButtonDescriptor { - int x; - int y; - int size; - sf::Color color; - sf::Color active_color; - }; - int _current_button_index; ButtonDescriptor _play_button; ButtonDescriptor _settings_button; diff --git a/src/save.cpp b/src/save.cpp index a2bae5f..ac65837 100644 --- a/src/save.cpp +++ b/src/save.cpp @@ -142,6 +142,10 @@ void SaveData::resetAll() { save(); } +bool SaveData::is_first_launch() const noexcept { + return completed_levels == 0; +} + UserSettings UserSettings::defaultSettings() noexcept { return UserSettings{ .scale = 0, diff --git a/src/scenes/level.cpp b/src/scenes/level.cpp index 49dea2e..a3b626d 100644 --- a/src/scenes/level.cpp +++ b/src/scenes/level.cpp @@ -13,6 +13,14 @@ namespace wf::scene { namespace { +enum PausedMenuButton { + RESUME = 0, + RETRY, + KEYGUIDE, + QUIT, + BUTTON_COUNT +}; + auto loadFont() { static PixelFont *font = nullptr; if (!font) { @@ -56,11 +64,18 @@ LevelPlaying::LevelPlaying(const std::string &level_id) LevelPlaying::LevelPlaying(Level level) : _tick(0) + , _paused(false) + , _show_keyguide(false) + , _paused_menu_current_button_index(PausedMenuButton::RESUME) , _level(std::move(level)) , _renderer(_level) , _hint_type(HintType::None) , _hint_opacity(0) - , font(*loadFont()) {} + , font(*loadFont()) { + _keybind_texture = &AssetsManager::instance().getAsset( + "ui/key-guide" + ); +} std::array LevelPlaying::size() const { return {_level.width(), _level.height()}; @@ -76,6 +91,82 @@ void LevelPlaying::setup(SceneManager &mgr) { } void LevelPlaying::handleEvent(SceneManager &mgr, sf::Event &ev) { + if (_paused) { + if (auto kb = ev.getIf()) { + if (_show_keyguide) { + switch (kb->code) { + case sf::Keyboard::Key::Escape: + case sf::Keyboard::Key::Enter: + case sf::Keyboard::Key::Space: + UISounds::instance().forward.play(); + _show_keyguide = false; + return; + } + } else { + switch (kb->code) { + case sf::Keyboard::Key::Escape: + _paused = false; + break; + + case sf::Keyboard::Key::Enter: + case sf::Keyboard::Key::Space: + switch (_paused_menu_current_button_index) { + case PausedMenuButton::RESUME: + _paused = false; + return; + + case PausedMenuButton::KEYGUIDE: + _show_keyguide = true; + return; + + case PausedMenuButton::RETRY: + _restartLevel(mgr, false); + return; + + case PausedMenuButton::QUIT: + mgr.changeScene( + pro::make_proxy() + ); + return; + + default: + // Errorneous state? + _paused_menu_current_button_index = PausedMenuButton:: + RESUME; + break; + } + break; + + case sf::Keyboard::Key::Up: + case sf::Keyboard::Key::W: + UISounds::instance().backward.play(); + _paused_menu_current_button_index--; + if (_paused_menu_current_button_index < 0) { + _paused_menu_current_button_index + = PausedMenuButton::BUTTON_COUNT - 1; + } + break; + + case sf::Keyboard::Key::Down: + case sf::Keyboard::Key::S: + UISounds::instance().forward.play(); + _paused_menu_current_button_index++; + if (_paused_menu_current_button_index + >= PausedMenuButton::BUTTON_COUNT) { + _paused_menu_current_button_index = 0; + } + break; + + default: + break; + } + } + } + + // Ignore other events when paused + return; + } + constexpr int retry_cooldown_ticks = 24 * 2; if (auto mw = ev.getIf()) { @@ -96,8 +187,12 @@ void LevelPlaying::handleEvent(SceneManager &mgr, sf::Event &ev) { if (auto kb = ev.getIf()) { constexpr int min_num_key = static_cast(sf::Keyboard::Key::Num1); constexpr int max_num_key = static_cast(sf::Keyboard::Key::Num9); - constexpr int min_numpad_key = static_cast(sf::Keyboard::Key::Numpad1); - constexpr int max_numpad_key = static_cast(sf::Keyboard::Key::Numpad9); + constexpr int min_numpad_key = static_cast( + sf::Keyboard::Key::Numpad1 + ); + constexpr int max_numpad_key = static_cast( + sf::Keyboard::Key::Numpad9 + ); const int key_code = static_cast(kb->code); if (key_code >= min_num_key && key_code <= max_num_key) { @@ -127,15 +222,7 @@ void LevelPlaying::handleEvent(SceneManager &mgr, sf::Event &ev) { break; case sf::Keyboard::Key::Escape: - if (_hint_opacity > 0 && _hint_type == HintType::QuitLevel) { - mgr.changeScene( - pro::make_proxy() - ); - return; - } else { - _hint_type = HintType::QuitLevel; - _hint_opacity = hint_max_opacity; - } + _paused = true; break; case sf::Keyboard::Key::Up: @@ -192,11 +279,78 @@ void LevelPlaying::render( sf::Color text_color = ui_text_color(_hint_opacity); font.renderText(target, hint_text, text_color, x, y, scale); } + + // Render Paused Menu + if (_paused) { + sf::RectangleShape overlay_mask; + overlay_mask.setSize( + sf::Vector2f(_level.width() * scale, _level.height() * scale) + ); + overlay_mask.setFillColor(sf::Color(0, 0, 0, 150)); + target.draw(overlay_mask); + + // Render "PAUSED" text + const std::string pause_text = "PAUSED"; + int x = (_level.width() - pause_text.size() * font.charWidth() * 2) / 2; + int y = 64; + font.renderText( + target, pause_text, sf::Color(255, 255, 255, 255), x, y, scale, 2 + ); + + // Render Buttons + auto renderButton = [&](std::string_view label, + const ButtonDescriptor &desc, bool is_active) { + sf::Color color = is_active ? desc.active_color : desc.color; + font.renderText( + target, std::string(label), color, desc.x, desc.y, scale, + desc.size + ); + }; + + const auto _button_list = std::array< + std::pair, PausedMenuButton::BUTTON_COUNT>{ + std::make_pair("Resume", PausedMenuButton::RESUME), + std::make_pair("Retry", PausedMenuButton::RETRY), + std::make_pair("Key Guide", PausedMenuButton::KEYGUIDE), + std::make_pair("Quit", PausedMenuButton::QUIT) + }; + + for (std::size_t i = 0; i < _button_list.size(); i++) { + const auto button = _button_list[i]; + + // Center the buttons horizontally + int button_x = (_level.width() + - button.first.size() * font.charWidth()) + / 2; + int button_y = (_level.height() / 2) + i * (font.charHeight() + 5); + + renderButton( + button.first, + ButtonDescriptor{ + button_x, button_y, 1, sf::Color(255, 255, 255, 255), + sf::Color(207, 158, 9, 255) + }, + _paused_menu_current_button_index == button.second + ); + } + } + + // Render Keyguide Overlay + if (_show_keyguide) { + sf::Sprite keyguide_sprite(*_keybind_texture); + keyguide_sprite.setPosition(sf::Vector2f(0, 0)); + keyguide_sprite.setScale(sf::Vector2f(scale, scale)); + + target.draw(keyguide_sprite); + } } void LevelPlaying::step(SceneManager &mgr) { - _tick += 1; - _level.step(); + // Only step the level when not paused + if (!_paused) { + _tick += 1; + _level.step(); + } if (_hint_opacity > 0) { _hint_opacity -= hint_fade_speed; From 2432a0a7b12fd2e636952a583777560d705ba2f9 Mon Sep 17 00:00:00 2001 From: ZTL-UwU Date: Sat, 29 Nov 2025 01:26:36 -0800 Subject: [PATCH 06/10] tweak pause overlay darkness Signed-off-by: ZTL-UwU --- src/scenes/level.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scenes/level.cpp b/src/scenes/level.cpp index a3b626d..1797da0 100644 --- a/src/scenes/level.cpp +++ b/src/scenes/level.cpp @@ -286,7 +286,7 @@ void LevelPlaying::render( overlay_mask.setSize( sf::Vector2f(_level.width() * scale, _level.height() * scale) ); - overlay_mask.setFillColor(sf::Color(0, 0, 0, 150)); + overlay_mask.setFillColor(sf::Color(0, 0, 0, 200)); target.draw(overlay_mask); // Render "PAUSED" text From c5a0559a1e9a1444b73887182cec486e49a66db0 Mon Sep 17 00:00:00 2001 From: ZTL-UwU Date: Sat, 29 Nov 2025 01:46:23 -0800 Subject: [PATCH 07/10] lower volume when paused Signed-off-by: ZTL-UwU --- include/wforge/audio.h | 1 + include/wforge/scene.h | 3 +++ src/audio.cpp | 8 ++++++++ src/scenes/level.cpp | 18 ++++++++++++++---- src/scenes/main_menu.cpp | 2 +- 5 files changed, 27 insertions(+), 5 deletions(-) diff --git a/include/wforge/audio.h b/include/wforge/audio.h index f52f915..baf67e6 100644 --- a/include/wforge/audio.h +++ b/include/wforge/audio.h @@ -33,6 +33,7 @@ class BGMManager { void fadeInCurrent(int duration_ticks, float starting_volume); void step(); void nextMusic(); + void setVolume(float volume); private: float _cur_volume; diff --git a/include/wforge/scene.h b/include/wforge/scene.h index f926544..aa5f268 100644 --- a/include/wforge/scene.h +++ b/include/wforge/scene.h @@ -104,6 +104,9 @@ struct LevelPlaying { const SceneManager &mgr, sf::RenderTarget &target, int scale ) const; + void pause(SceneManager &mgr) noexcept; + void unpause(SceneManager &mgr) noexcept; + private: void _restartLevel(SceneManager &mgr, bool is_failed = true); diff --git a/src/audio.cpp b/src/audio.cpp index 5b160ae..6d8406e 100644 --- a/src/audio.cpp +++ b/src/audio.cpp @@ -120,6 +120,14 @@ void BGMManager::step() { } } +void BGMManager::setVolume(float volume) { { + _cur_volume = volume; + if (_cur_bgm) { + _cur_bgm->setVolume(_cur_volume); + } + } +} + void BGMManager::nextMusic() { if (!_collection) { return; diff --git a/src/scenes/level.cpp b/src/scenes/level.cpp index 1797da0..fa98a31 100644 --- a/src/scenes/level.cpp +++ b/src/scenes/level.cpp @@ -90,6 +90,16 @@ void LevelPlaying::setup(SceneManager &mgr) { ); } +void LevelPlaying::pause(SceneManager &mgr) noexcept { + _paused = true; + mgr.bgm.setVolume(0.15f); +} + +void LevelPlaying::unpause(SceneManager &mgr) noexcept { + _paused = false; + mgr.bgm.setVolume(1.0f); +} + void LevelPlaying::handleEvent(SceneManager &mgr, sf::Event &ev) { if (_paused) { if (auto kb = ev.getIf()) { @@ -105,14 +115,14 @@ void LevelPlaying::handleEvent(SceneManager &mgr, sf::Event &ev) { } else { switch (kb->code) { case sf::Keyboard::Key::Escape: - _paused = false; + unpause(mgr); break; case sf::Keyboard::Key::Enter: case sf::Keyboard::Key::Space: switch (_paused_menu_current_button_index) { case PausedMenuButton::RESUME: - _paused = false; + unpause(mgr); return; case PausedMenuButton::KEYGUIDE: @@ -130,7 +140,7 @@ void LevelPlaying::handleEvent(SceneManager &mgr, sf::Event &ev) { return; default: - // Errorneous state? + // Erroneous state? _paused_menu_current_button_index = PausedMenuButton:: RESUME; break; @@ -222,7 +232,7 @@ void LevelPlaying::handleEvent(SceneManager &mgr, sf::Event &ev) { break; case sf::Keyboard::Key::Escape: - _paused = true; + pause(mgr); break; case sf::Keyboard::Key::Up: diff --git a/src/scenes/main_menu.cpp b/src/scenes/main_menu.cpp index f35401c..4002063 100644 --- a/src/scenes/main_menu.cpp +++ b/src/scenes/main_menu.cpp @@ -125,7 +125,7 @@ void MainMenu::handleEvent(SceneManager &mgr, sf::Event &evt) { return; default: - // Errorneous state? + // Erroneous state? _current_button_index = MainMenuButton::PLAY; break; } From 3cf670692148515911d6acbae65e1d8587ec44a9 Mon Sep 17 00:00:00 2001 From: ZTL-UwU Date: Sat, 29 Nov 2025 01:48:59 -0800 Subject: [PATCH 08/10] fix main menu first launch logic Signed-off-by: ZTL-UwU --- src/scenes/main_menu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/scenes/main_menu.cpp b/src/scenes/main_menu.cpp index 4002063..f51cbe1 100644 --- a/src/scenes/main_menu.cpp +++ b/src/scenes/main_menu.cpp @@ -165,7 +165,7 @@ void MainMenu::render( auto &save = SaveData::instance(); renderButton( - save.completed_levels >= 0 ? "Play" : "New Game", _play_button, + save.is_first_launch() ? "New Game" : "Play", _play_button, _current_button_index == MainMenuButton::PLAY ); From 685cc46077dbcfdf6ced46f4a58497265cd568db Mon Sep 17 00:00:00 2001 From: ZTL-UwU Date: Sat, 29 Nov 2025 15:06:52 -0800 Subject: [PATCH 09/10] rename key guide to help Signed-off-by: ZTL-UwU --- CMakeLists.txt | 2 +- assets/manifest.json | 18 +++++----- .../ui/{key-guide.aseprite => help.aseprite} | Bin assets/ui/{key-guide.json => help.json} | 0 assets/ui/{key-guide.png => help.png} | Bin assets/ui/main-menu.json | 2 +- include/wforge/scene.h | 10 +++--- src/main.cpp | 23 ++++++++----- src/scenes/{key_guide.cpp => help.cpp} | 16 ++++----- src/scenes/level.cpp | 32 +++++++++--------- src/scenes/main_menu.cpp | 12 +++---- 11 files changed, 61 insertions(+), 54 deletions(-) rename assets/ui/{key-guide.aseprite => help.aseprite} (100%) rename assets/ui/{key-guide.json => help.json} (100%) rename assets/ui/{key-guide.png => help.png} (100%) rename src/scenes/{key_guide.cpp => help.cpp} (77%) diff --git a/CMakeLists.txt b/CMakeLists.txt index afdc5c2..82bd1e9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -91,7 +91,7 @@ add_executable(waveforge src/items/water.cpp src/items/oil.cpp src/scenes/duckdeath.cpp - src/scenes/key_guide.cpp + src/scenes/help.cpp src/scenes/level_menu.cpp src/scenes/level_switch.cpp src/scenes/level.cpp diff --git a/assets/manifest.json b/assets/manifest.json index e58e0a4..1537fdf 100644 --- a/assets/manifest.json +++ b/assets/manifest.json @@ -436,22 +436,22 @@ "description": "Creating level link texture" }, { - "id": "ui/key-guide/raw", + "id": "ui/help/raw", "type": "image", - "file": "ui/key-guide.png", - "description": "Loading key guide background image" + "file": "ui/help.png", + "description": "Loading help background image" }, { - "id": "ui/key-guide", + "id": "ui/help", "type": "create-texture", - "input": "ui/key-guide/raw", - "description": "Creating key guide background texture" + "input": "ui/help/raw", + "description": "Creating help background texture" }, { - "id": "ui-config/key-guide", + "id": "ui-config/help", "type": "json", - "file": "ui/key-guide.json", - "description": "Loading key guide UI configuration" + "file": "ui/help.json", + "description": "Loading help UI configuration" }, { "id": "ui-config/level-menu", diff --git a/assets/ui/key-guide.aseprite b/assets/ui/help.aseprite similarity index 100% rename from assets/ui/key-guide.aseprite rename to assets/ui/help.aseprite diff --git a/assets/ui/key-guide.json b/assets/ui/help.json similarity index 100% rename from assets/ui/key-guide.json rename to assets/ui/help.json diff --git a/assets/ui/key-guide.png b/assets/ui/help.png similarity index 100% rename from assets/ui/key-guide.png rename to assets/ui/help.png diff --git a/assets/ui/main-menu.json b/assets/ui/main-menu.json index cd64e4b..9e16e81 100644 --- a/assets/ui/main-menu.json +++ b/assets/ui/main-menu.json @@ -50,7 +50,7 @@ 255 ] }, - "keyguide": { + "help": { "x": 72, "y": 128, "size": 2, diff --git a/include/wforge/scene.h b/include/wforge/scene.h index aa5f268..5f9955f 100644 --- a/include/wforge/scene.h +++ b/include/wforge/scene.h @@ -120,8 +120,8 @@ struct LevelPlaying { // Paused Menu int _paused_menu_current_button_index; - bool _show_keyguide; - sf::Texture *_keybind_texture; + bool _show_help; + sf::Texture *_help_texture; }; struct DuckDeath { @@ -262,7 +262,7 @@ struct MainMenu { int _current_button_index; ButtonDescriptor _play_button; ButtonDescriptor _settings_button; - ButtonDescriptor _keyguide_button; + ButtonDescriptor _help_button; ButtonDescriptor _exit_button; UITextDescriptor _version_text; }; @@ -331,8 +331,8 @@ struct Credits { std::vector> _content; }; -struct KeyGuide { - KeyGuide(); +struct Help { + Help(); std::array size() const; void setup(SceneManager &mgr); diff --git a/src/main.cpp b/src/main.cpp index 5d54f00..db9d813 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -81,15 +81,22 @@ int main(int argc, char **argv) { void entry( const std::string &level_id, int scale_config, bool is_first_launch ) { + auto initialScene = [&](const std::string &level_id, bool is_first_launch) { + if (level_id == "-") { + if (is_first_launch) { + return pro::make_proxy(); + } else { + return pro::make_proxy(); + } + } else { + return pro::make_proxy( + level_id + ); + } + }; + wf::SceneManager scene_mgr( - level_id == "-" - ? is_first_launch - ? pro::make_proxy() - : pro::make_proxy() - : pro::make_proxy( - level_id - ), - scale_config + initialScene(level_id, is_first_launch), scale_config ); auto &window = scene_mgr.window; diff --git a/src/scenes/key_guide.cpp b/src/scenes/help.cpp similarity index 77% rename from src/scenes/key_guide.cpp rename to src/scenes/help.cpp index 819ed7d..8f9c099 100644 --- a/src/scenes/key_guide.cpp +++ b/src/scenes/help.cpp @@ -4,24 +4,24 @@ namespace wf::scene { -KeyGuide::KeyGuide() { +Help::Help() { const auto &json_data = AssetsManager::instance().getAsset( - "ui-config/key-guide" + "ui-config/help" ); _width = json_data.at("width"); _height = json_data.at("height"); _background_texture = &AssetsManager::instance().getAsset( - "ui/key-guide" + "ui/help" ); } -std::array KeyGuide::size() const { +std::array Help::size() const { return {_width, _height}; } -void KeyGuide::handleEvent(SceneManager &mgr, sf::Event &evt) { +void Help::handleEvent(SceneManager &mgr, sf::Event &evt) { if (auto kb = evt.getIf()) { switch (kb->code) { case sf::Keyboard::Key::Escape: @@ -37,10 +37,10 @@ void KeyGuide::handleEvent(SceneManager &mgr, sf::Event &evt) { } } -void KeyGuide::setup(SceneManager &mgr) {} -void KeyGuide::step(SceneManager &mgr) {} +void Help::setup(SceneManager &mgr) {} +void Help::step(SceneManager &mgr) {} -void KeyGuide::render( +void Help::render( const SceneManager &mgr, sf::RenderTarget &target, int scale ) const { sf::Sprite background_sprite(*_background_texture); diff --git a/src/scenes/level.cpp b/src/scenes/level.cpp index fa98a31..95f1a69 100644 --- a/src/scenes/level.cpp +++ b/src/scenes/level.cpp @@ -16,7 +16,7 @@ namespace { enum PausedMenuButton { RESUME = 0, RETRY, - KEYGUIDE, + HELP, QUIT, BUTTON_COUNT }; @@ -65,15 +65,15 @@ LevelPlaying::LevelPlaying(const std::string &level_id) LevelPlaying::LevelPlaying(Level level) : _tick(0) , _paused(false) - , _show_keyguide(false) + , _show_help(false) , _paused_menu_current_button_index(PausedMenuButton::RESUME) , _level(std::move(level)) , _renderer(_level) , _hint_type(HintType::None) , _hint_opacity(0) , font(*loadFont()) { - _keybind_texture = &AssetsManager::instance().getAsset( - "ui/key-guide" + _help_texture = &AssetsManager::instance().getAsset( + "ui/help" ); } @@ -103,13 +103,13 @@ void LevelPlaying::unpause(SceneManager &mgr) noexcept { void LevelPlaying::handleEvent(SceneManager &mgr, sf::Event &ev) { if (_paused) { if (auto kb = ev.getIf()) { - if (_show_keyguide) { + if (_show_help) { switch (kb->code) { case sf::Keyboard::Key::Escape: case sf::Keyboard::Key::Enter: case sf::Keyboard::Key::Space: - UISounds::instance().forward.play(); - _show_keyguide = false; + UISounds::instance().backward.play(); + _show_help = false; return; } } else { @@ -125,8 +125,8 @@ void LevelPlaying::handleEvent(SceneManager &mgr, sf::Event &ev) { unpause(mgr); return; - case PausedMenuButton::KEYGUIDE: - _show_keyguide = true; + case PausedMenuButton::HELP: + _show_help = true; return; case PausedMenuButton::RETRY: @@ -321,7 +321,7 @@ void LevelPlaying::render( std::pair, PausedMenuButton::BUTTON_COUNT>{ std::make_pair("Resume", PausedMenuButton::RESUME), std::make_pair("Retry", PausedMenuButton::RETRY), - std::make_pair("Key Guide", PausedMenuButton::KEYGUIDE), + std::make_pair("Help", PausedMenuButton::HELP), std::make_pair("Quit", PausedMenuButton::QUIT) }; @@ -345,13 +345,13 @@ void LevelPlaying::render( } } - // Render Keyguide Overlay - if (_show_keyguide) { - sf::Sprite keyguide_sprite(*_keybind_texture); - keyguide_sprite.setPosition(sf::Vector2f(0, 0)); - keyguide_sprite.setScale(sf::Vector2f(scale, scale)); + // Render help Overlay + if (_show_help) { + sf::Sprite help_sprite(*_help_texture); + help_sprite.setPosition(sf::Vector2f(0, 0)); + help_sprite.setScale(sf::Vector2f(scale, scale)); - target.draw(keyguide_sprite); + target.draw(help_sprite); } } diff --git a/src/scenes/main_menu.cpp b/src/scenes/main_menu.cpp index f51cbe1..073c47b 100644 --- a/src/scenes/main_menu.cpp +++ b/src/scenes/main_menu.cpp @@ -14,7 +14,7 @@ namespace { enum MainMenuButton { PLAY = 0, SETTINGS, - KEYGUIDE, + HELP, EXIT, BUTTON_COUNT }; @@ -68,7 +68,7 @@ MainMenu::MainMenu() _play_button = parseButtonDescriptor(buttons.at("play")); _settings_button = parseButtonDescriptor(buttons.at("settings")); - _keyguide_button = parseButtonDescriptor(buttons.at("keyguide")); + _help_button = parseButtonDescriptor(buttons.at("help")); _exit_button = parseButtonDescriptor(buttons.at("exit")); _version_text = UITextDescriptor::fromJson(json_data.at("version-text")); @@ -116,8 +116,8 @@ void MainMenu::handleEvent(SceneManager &mgr, sf::Event &evt) { mgr.changeScene(pro::make_proxy()); return; - case MainMenuButton::KEYGUIDE: - mgr.changeScene(pro::make_proxy()); + case MainMenuButton::HELP: + mgr.changeScene(pro::make_proxy()); return; case MainMenuButton::EXIT: @@ -175,8 +175,8 @@ void MainMenu::render( ); renderButton( - "Key Guide", _keyguide_button, - _current_button_index == MainMenuButton::KEYGUIDE + "Help", _help_button, + _current_button_index == MainMenuButton::HELP ); renderButton( From 2e99344d631c1eb9d227bb9bb0afba4bb885e120 Mon Sep 17 00:00:00 2001 From: ZTL-UwU Date: Sat, 29 Nov 2025 15:39:35 -0800 Subject: [PATCH 10/10] refactor active color Signed-off-by: ZTL-UwU --- assets/ui/main-menu.json | 56 +++-------------------------------- assets/ui/settings.json | 14 +-------- include/wforge/colorpalette.h | 5 ++++ include/wforge/scene.h | 2 -- src/scenes/level.cpp | 13 +++----- src/scenes/main_menu.cpp | 11 ++----- src/scenes/settings.cpp | 16 ++-------- 7 files changed, 18 insertions(+), 99 deletions(-) diff --git a/assets/ui/main-menu.json b/assets/ui/main-menu.json index 9e16e81..db3bf20 100644 --- a/assets/ui/main-menu.json +++ b/assets/ui/main-menu.json @@ -19,70 +19,22 @@ "play": { "x": 72, "y": 80, - "size": 2, - "color": [ - 0, - 0, - 0, - 200 - ], - "active-color": [ - 207, - 158, - 9, - 255 - ] + "size": 2 }, "settings": { "x": 72, "y": 104, - "size": 2, - "color": [ - 0, - 0, - 0, - 200 - ], - "active-color": [ - 207, - 158, - 9, - 255 - ] + "size": 2 }, "help": { "x": 72, "y": 128, - "size": 2, - "color": [ - 0, - 0, - 0, - 200 - ], - "active-color": [ - 207, - 158, - 9, - 255 - ] + "size": 2 }, "exit": { "x": 72, "y": 152, - "size": 2, - "color": [ - 0, - 0, - 0, - 200 - ], - "active-color": [ - 207, - 158, - 9, - 255 - ] + "size": 2 } } } \ No newline at end of file diff --git a/assets/ui/settings.json b/assets/ui/settings.json index eaa56fe..95f54d8 100644 --- a/assets/ui/settings.json +++ b/assets/ui/settings.json @@ -28,18 +28,6 @@ "y": 64, "width": 192, "spacing": 12, - "size": 1, - "color": [ - 0, - 0, - 0, - 255 - ], - "active-color": [ - 255, - 200, - 0, - 255 - ] + "size": 1 } } \ No newline at end of file diff --git a/include/wforge/colorpalette.h b/include/wforge/colorpalette.h index a6f6920..246c901 100644 --- a/include/wforge/colorpalette.h +++ b/include/wforge/colorpalette.h @@ -17,6 +17,11 @@ constexpr sf::Color ui_text_color(std::uint8_t a) { return sf::Color(0, 0, 0, a); } +constexpr sf::Color ui_active_color{250, 200, 46, 255}; +constexpr sf::Color ui_text_bright_color(std::uint8_t a) { + return sf::Color(255, 255, 255, a); +} + // All indexed colors must be here, for dynamic generated textures // Colors in static assets (e.g. PNG files) can be outside this palette constexpr ColorPaletteEntry _colors[] = { diff --git a/include/wforge/scene.h b/include/wforge/scene.h index 5f9955f..b77124f 100644 --- a/include/wforge/scene.h +++ b/include/wforge/scene.h @@ -86,8 +86,6 @@ struct ButtonDescriptor { int x; int y; int size; - sf::Color color; - sf::Color active_color; }; namespace scene { diff --git a/src/scenes/level.cpp b/src/scenes/level.cpp index 95f1a69..d1e97ac 100644 --- a/src/scenes/level.cpp +++ b/src/scenes/level.cpp @@ -72,9 +72,7 @@ LevelPlaying::LevelPlaying(Level level) , _hint_type(HintType::None) , _hint_opacity(0) , font(*loadFont()) { - _help_texture = &AssetsManager::instance().getAsset( - "ui/help" - ); + _help_texture = &AssetsManager::instance().getAsset("ui/help"); } std::array LevelPlaying::size() const { @@ -310,7 +308,8 @@ void LevelPlaying::render( // Render Buttons auto renderButton = [&](std::string_view label, const ButtonDescriptor &desc, bool is_active) { - sf::Color color = is_active ? desc.active_color : desc.color; + sf::Color color = is_active ? ui_active_color + : ui_text_bright_color(255); font.renderText( target, std::string(label), color, desc.x, desc.y, scale, desc.size @@ -335,11 +334,7 @@ void LevelPlaying::render( int button_y = (_level.height() / 2) + i * (font.charHeight() + 5); renderButton( - button.first, - ButtonDescriptor{ - button_x, button_y, 1, sf::Color(255, 255, 255, 255), - sf::Color(207, 158, 9, 255) - }, + button.first, ButtonDescriptor{button_x, button_y, 1}, _paused_menu_current_button_index == button.second ); } diff --git a/src/scenes/main_menu.cpp b/src/scenes/main_menu.cpp index 073c47b..ef86cf8 100644 --- a/src/scenes/main_menu.cpp +++ b/src/scenes/main_menu.cpp @@ -2,6 +2,7 @@ #include "wforge/audio.h" #include "wforge/save.h" #include "wforge/scene.h" +#include "wforge/colorpalette.h" #include #include #include @@ -55,14 +56,6 @@ MainMenu::MainMenu() desc.x = data.at("x"); desc.y = data.at("y"); desc.size = data.at("size"); - desc.color = sf::Color( - data.at("color").at(0), data.at("color").at(1), - data.at("color").at(2), data.at("color").at(3) - ); - desc.active_color = sf::Color( - data.at("active-color").at(0), data.at("active-color").at(1), - data.at("active-color").at(2), data.at("active-color").at(3) - ); return desc; }; @@ -157,7 +150,7 @@ void MainMenu::render( // Render buttons auto renderButton = [&](std::string_view label, const ButtonDescriptor &desc, bool is_active) { - sf::Color color = is_active ? desc.active_color : desc.color; + sf::Color color = is_active ? ui_active_color : ui_text_color(255); font.renderText( target, std::string(label), color, desc.x, desc.y, scale, desc.size ); diff --git a/src/scenes/settings.cpp b/src/scenes/settings.cpp index 639110c..91eb914 100644 --- a/src/scenes/settings.cpp +++ b/src/scenes/settings.cpp @@ -233,18 +233,6 @@ SettingsMenu::SettingsMenu() _option_text_size = option_data.at("size"); _option_width = option_data.at("width"); - _option_color = sf::Color( - option_data.at("color").at(0), option_data.at("color").at(1), - option_data.at("color").at(2), option_data.at("color").at(3) - ); - - _option_active_color = sf::Color( - option_data.at("active-color").at(0), - option_data.at("active-color").at(1), - option_data.at("active-color").at(2), - option_data.at("active-color").at(3) - ); - _options.push_back(std::make_unique()); _options.push_back(std::make_unique()); _options.push_back(std::make_unique()); @@ -386,8 +374,8 @@ void SettingsMenu::render( for (size_t i = 0; i < _options.size(); ++i) { const auto &option = _options[i]; sf::Color color = (i == _current_option_index) - ? _option_active_color - : _option_color; + ? ui_active_color + : ui_text_color(255); std::string option_text = option->displayText(); std::string value_text = option->valueText();