From eab894cd47917d7c5b8afb5d298cc243b34aa4de Mon Sep 17 00:00:00 2001 From: Randy Thiemann Date: Sat, 28 Oct 2023 19:04:55 +0200 Subject: [PATCH] Secret SMOL mode --- bin/DMGTRIS.GBC | Bin 65536 -> 131072 bytes bin/DMGTRIS.pocket | Bin 65536 -> 131072 bytes src/bankid.asm | 61 +- src/banking.asm | 28 +- src/constants.asm | 940 ------- src/field.asm | 2185 ++++++++++++++++- src/include/globals.asm | 65 +- src/level.asm | 15 +- src/main.asm | 12 +- ...gameplay_map.inc => gameplay_big_data.inc} | 230 +- src/res/gameplay_data.inc | 325 +++ src/res/music_data.inc | 6 +- src/res/{tiles.inc => other_data.inc} | 612 ++++- src/res/sfx_data.inc | 6 +- src/res/{title_map.inc => title_data.inc} | 59 +- src/rng.asm | 3 + src/sfx.asm | 7 +- src/sprites.asm | 20 + src/state_gameplay.asm | 658 ++++- src/state_title.asm | 225 +- 20 files changed, 4299 insertions(+), 1158 deletions(-) delete mode 100644 src/constants.asm rename src/res/{gameplay_map.inc => gameplay_big_data.inc} (55%) create mode 100644 src/res/gameplay_data.inc rename src/res/{tiles.inc => other_data.inc} (66%) rename src/res/{title_map.inc => title_data.inc} (79%) diff --git a/bin/DMGTRIS.GBC b/bin/DMGTRIS.GBC index 96dc7ec454d089d7bf78a1b48e114eeb80ebd8b1..4ada00d779874a8618d09a33d530402fe9d7c8e1 100644 GIT binary patch delta 7461 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 delta 4880 zcma)A3vg3a8onp*q|cN-(iY)fTO}4sE0j`da<}qofog-+DaczM1xLjZcNbkCMNvT> z0)f2+5#sI)u)vsgJ4zj0EMCN<+&fT&B8qz0kpcnqC=W5}&Hm@+CIrFRZ6`VZcfRlZ z=l{>UZE3Nzv=~Nw68m8y<{Er`VVGe~? z%`wz0MdkpSXlMrhpgnF2bv3Bm5}7V3_RfNmw!Y>i`rf;$$&o|- zV!sV0hTAv6x)`@C!b`aaC+sa@>>DO-eLKKEN`dy%4U<*vFJ7`=%d{>7v3oDwlzd1 zONy=&+8t4hP$)WQKghHQJliQnm1Omy+K)P|j`Zgg)cLGcF*=thR_F6-7cLq4czPtt zanf;Q^GTNrcipZdRqc+eE8}YS?bV5{k0>U#jvRH7 zJ4lMr66l<7BP(L)CU1k)NQ(X+XGh)1VN|FMr$fX49R{5YS7N9N)RuX)>bKbopN&Ef ze1*ylqRh~0Q z$f!k+>7Yzsj+}OFSMZB#>9&wzTkNSt&is9;vGt15ME1KuRBMe>Gp;r%+)+dkPYwx{ui@Wn%93tl67(uH$%sE=-QrC=F@nslF5 z2{Xo^?CK6Xp8`>lH}D!>4`OgZd=RXG9v5Vd8?RBA4zg2;I*omhyt$Id_=x=L5D^a+ z0%K6M&YLb=7=sEYdec*R!+Sw)=OI%{39N3XK{2q`0`08Lq?6<1=37a9f?RbgX#i4~ zHWn4qZWd|fCMaETkBzrBvLrl?zLm66k3{p7HZJD5L@PU9Mfrv_s`!#x>C{@Paa*~d} zz{hz8ck>>6Lf&{c9DUb#AFh${VAEPMvYq|e#HdAxTaZV(O-zIJytpn;d_Pa@&J)9V z;w*>wl0*E&A>L6WUT}zGi^vJZ!#-sL2fSTgHHe&WvnKG`%{%c4yT!u|2*FJUVTl(& z>W1ZQ!rcL&vTmYyEAEz`M08zgOXornY>ks}=XHU%qj19Gf>QE*c*Cf?9CfRe7>_wXG zV2CV#SVoS?PnL7#LHoN=`Eohu3EFq7#@wJiKn;1n6Ow@mIUadbQgabbZAArUnDkd+ zq=l!aq2j(8aHP<+V1sIyG;*w#9KpvH;v=Cp4?2k|_A_u~H00{(a%CBKVdNs@S4%VM zpf3(P)5n34>X~#)wI|PkEfcR7Fg5wA8KGD0bFCBFp=i(ryD);n51O01@=x4H>wpG)wOfj z!_C%g$4nY~6a;IN1RIkC!`0;2`yw->L$#pW?BPbXqEywv;W>>6^c0;Kic;LLzU&*XWfoaw&dR8yK(Vv`! z+GPW7s$Dh!2{H7JOwsr$QrN1skDGAo-3e<7+NZh0Q>C!YFV8NeF&LAP;x&SsX_}j9 zQaDA6Gj2NcH2v-K$&O}pISTLU?cr4aBD^D<<`=t5V~G~hM3K|xN@N#ESdeES&C@{X zc$$HPrFeC~)0ZaYu2Atyl8rA{66YwYvzW{gy;PDzpfbRl4Lc>b| z=L2rgupV#<;L#d>5%8CQAHtWsYKI1>csEI~3~)cdjamaE;7NdMHT)9bzW{zj!zRF6 z0oQ5xWx$I8H>p^$0qg=eCI(*xI0N8BfV$F(^%;+QzYgFZ@Z}J^PD~9;ivHs`zZn0p z2wxWA4*pj8mNlC6haGy8?l8GHpImBw2Tm~uv!qB-Si{$%3r1IKb`d&V1YV(;;Vt|z zF}{TlQtK0H1?>YTaswq)q#okqCvxK@WTv)UJhl=x*-x;Sui|5L)9YEYTwEDG91X#Z z9KxOW5SC~?<23O)3V@BbfIa*@4uO_F>VF;L8=Bdtq`1=Y6>pq&hcgF+TqwHFd8KHd zlfdU9e9k%c^Jb0oc{6izV;c5#5Ig>=QmkB1>D!grM|Y~i*O^(UE3WjN&3xvbluC#K z9-d2=(zmBlAr=l1M-LHyst~J&$O9{T{-6|<{}=+x_kz8jZ;yS9UfeU(_mh2wdCyQj zQ(QI3HzTV=2gT~F!5Q!he5aP|X?Gg=47iAVhqGM1^j_t*AfJu>a88PWTdG}aw4_?2O7WyI)LN_}ZL_ot+V^?rU>*x=_&eG_vkbgAQgujS>VaQ%gE2%C-)4MuidNO)K0T@k;;F%51x^7X@#t- zkgpGwH!xgBHMtght+V~bd3kZ+=TYwS1mhSeb|BSLFx{pp$Z)rBYVHepKZamu-VcxS zQ7|gQVF5$~IOFF~+uM16G_0?&{C#X~rckpDIfbtbXu7Z&{%a(BF%4ClsvE!(mkf{pDK2;fIZ0wkf?Pudca zg~%i%8;3^?lk6HwH|mlP(A|8h=A$-s+mO-@Z8Y{w0T)aNNp=!~8x#^|%d1hc(w;jr z8p*G2y8o;{w&tAsJLlZT%)Rrqy1NbC-Il;x$`6)!fR8`OC+_Ft5AcaKeEfbsVfM$@ z@SEZo$)!>$XbU2B4UZy0eGo;;>h8dul*w>hu=p>tr7mQh)2tT*mszLFgdb;$OYffL z#UkPjj@F4)v`MU`F|iu#R77-qfx1YgFn0e`#(>vYbOvlLe*2>G=yS8q;`45E)415= z(oF1bTk%U&x5m!@*l&yQU8ezd?gh=hWoJ` zANeJ%Uj{W;XqJm6+IeQc+qG;hPut_`_{T~T>v$g+cd^kZUY&W@NemL zP8YMT^l?{Ex}G#`A}!Rq4q{t}$3&g$AZ~Opua)I9e=UtpB1c~<+l#w5%gB0ey!?14SoRprK*pEKO6jj z>Td|Bv8* zQ}s>Y|10?Qs{b7LTfko?`$;RfC&6t{+!w&@0(S+tx@`{T$8#Slyc`k#B3|K=DX*#N zsge&p7q^Ii*(_dRz0B?N3PWD(sgh)pG+bsGSV}eI67;YcN_a8J@DuU(lJO_vACUDH zvSKFVapFoRbq9}^VDN+!t}bdRkDHNwSqKqH-1~V_=e;JLO049QdeRJS@#85DHVr2Z zh=U@J+A*&QHrwDdkaI;Ia$Uj^bjN8NS2h{;J{+$l+V$lBV7s6SN!MWoVfmiI*bQ#>ad$fQofH+&#OE>AZH8aDUM$aw zf|yH-o^LRT*Tk#whxquzeEbofHi~90qPd)OwNkt~f{WJ@kMNbCCo7Le;F5S4yoVL< zVLtH?cn|IVO<5Z}r9wrE{0cA~7mh8JEoj@TwcRXR!~r+;P71eT>}_reOX4qME=;%^ z%$RJ#`ANsbI@;?a|C@{MZhw@DhTA_f?eSZp_NCS40~WtC-5NCiTX**!zd2-(mW<%u z9I0yXaWWARmk@LPjSM}bRF!^?KLb*|j2c?gjq>Dd&4c60Wju)@u*aZ4Y8 zKLy@Cu&a%}cxVI9(I%+INf$sMkYMr}D^UyTgp)>+_&{q_(z(D6FEu@!5eA{Pij)2f z&C{!)*`_w@h3VCtT$l7D?nUJ-zWBX-+!Th>L^YrXk8+@ZwvvyV!yyhHec9rYIuKW> ziL3ay1rl{}#ei--iPfsMnvdszW{4{W?4}v;!h{ZOzIZzyw}yrD)f{ci*hwQ~cc|GN zeB34vNj5^92a^syDjiyjUrFVR+IY%b+-NSi*HWoYA(EMj8%@Q4BxM4T%tYL1B03}~ z4L z%npp68CosUfIF+X5Zn?2-WNctZ@_<0$DtdHe*#wnTsQYalfLb|0x zZ;GpI;gZ zN+*S1_}f+sgMQO97fye0lI^c%&Y7l%vh)K;f7>tgS0z(1=}+QL*gb_2@lRTT(E&T* znJa<`Os?xe*H5McJU}L)j!iAi_E#~_n?r#E{tHmKhseV|D7=sv6={}wD#aeppFfa# zd}0qL4T(F1PQP?g+>XX8^}!Qtrc+`cTT#a}TILhyYNegN5j{xq3LNk=y_Tu%7yNKQ z7f3smp}myEe|N67QhbR$SKB0S(E&(1ku<^uw(A_2W(m|Kkc1+oorA&OC2Bl*PrzyS zN)2qyr;<6|C*>Y01?a+`Gu4aMkSvr=R*EMl>PHUtslnyxVBf^STE{1nCbC`{@}Zll z^iz*g(U^vO;!vg|j5I0p$(kC>2vbKt@N%uxqj=fH=E~zJ`6{)Bd|88-C+BrS-v2%b zbQ>zut5hT>x>3v6nKOs2_e{-x-bjPAT|6n=8DOloCB`GU?4}^I+P3QE?3jV?1>xpD zhZc87zt;vYFN`$OcHu}a^Pjf+bZ`mSy@a|A=q*9C4+Pi;o9*7h&w|P$qN>tAY3dPs zk?#BT%vt+>*CP!{!$5ZfyQe|g`v-%#SE%<3#~PUJxzmUpi`kFs;q`NAP}(omd@-B?cY3=opZ8`NUmG?5K1YDBd^& z7d|H>K|Fy#y28PP@)qeL4a~3eidRG$?6l#RG3xN3$#$y|f(>viUyt;YR+COhi~Gil zdQIFhUNnHnlsbx0CySNKdPi|(x?J1%42xE-ZoHU-${kheyr*0eS>^J^XXI+-u<>FZ zD!0@rkM7&bWxF%860w1CD=|eY_p9+@z9zmpUMxU;rIb#l5ptIciDIj|5}fpXrk9v% zfdA%uk)aE?nO%e%`Fp@G(8GBhJxVMyhNgERWPA}JqsrqiTj`Gwu+hDMY`=C0g$2To z3HX*HBAy0(8>XAZGeU>U>TiF|} z>^<#laU1(&yL2YmOopro0|~%D>5PvwLDgZ}E1n^u9n|d{z%mSrE$%>?kv56L5s;3I zlIR?9SeWfcws7I(nzItx^P5~>Zsi$Oe%5YlTD0v(=vA?mDxcy{Sx5@V)%nk#J8 z@OYYOk2nl{(GGD~It>ju(pmV_qoKtC9rA=Szcehq4K$0wg!TZl-{V;n=%DS8@T|N) zrpVHS;r~)E!M1@t1mi{Nm#d*MIgbw;7&*7tlYhfmyR| zQf-e7Jr`DJg&QcH3Q8x$Q=7yS9AjzrGN+msM3*eL;>?0oUyIIO_4*9-&yeY>a0>eN z3L%RTbD^)rsDICxKg*bJW=vSkbnH;Zgmq1~j;Q8ps0K@%Zcb!OSnPCbUdDt~Pq)t0 zOz!VsD5u6y;SPnN@~edTRm$t3r3z@|!77!ZS_3jeW$851zf&c&!jnIWq0$B9_mviq zVW_k|y1-6APKBW+)MbX6PpD&4Q`y#PnW5;a(F~<_Iw3=SF%d%zDGar-ZWKe&RWd_$ zBfwD@3WkOlYGWN@j*WFHLqQs1sEu{w846VdhI*-1VW@A^j$tV10x=X?!)%5c(~rtf zN^p5PIL%OFf)$36$CP0xCAA)HOW zYW-M-I{$Yt)NJ>H@o<3&jg8EEj>45V7z$cV8y}_9#0lUMS%Ek@0OX zOvu>oEL>^U@+LqSizZHhFgcny0m4{O{S&U2}EGGX^*!k#R_uGLcTC}0fDgdZA1pEHZKAJ2!}*l*stJO7UuWttSD z$^fH63P&wzVK=p~ObdIcg{4~A>Q;7JEBnrB_HZj}Ryb-BjPhr((qV|q!l=ekQW~S+ zF;y|@a64=;_gP@5aU6A`oiTX2LI*S01i_PH(_jXh4z~-R2Y_GTt27z0reswYPPC69 zDHyNj#5V#ix)8|xdTNgqNs-*DG)%9@({Cnv;Vod2|;jn+eyyaqK^dfvUGC$-1UUqc)p@=jC zYL21iP-GsUdER^SDsuP$9>d$6?5}D#QAVt)@#@g1I;3NUpt7N#`0*wl;SHQGfC!U| zq>~KS(uE#Jxlss8t<`~P^#!h|_If_?HI}z!|t7yQaO=egl zAH{8=F-3TOBpPbnZ6Rx%y;|UuL0F_0I!B_M>F?_0D4R{z#G>0MqmOr69C)L++tLu3 z8j>|#(C&z=heFoa`$MM1;Mqzksw8U;xjybP+tQztQP(qOS#Mt^o9)jlT{tA@>w#dO`vO`g|x=dP2Pqmkre+w&W<{h!>Cjm&V+{lI}AD*j>J$EC@u47)o-&GJ|B%9 z$Vpy#td6Y0-4ZehHDk~O{k*|qU8=BV44Qg(vZ`xzDy)N-=^(G}u&0nSFn@YJRhl9VfN zoi09_PBtCX;Z4#yByC6+KBz-Q)~QypBaPREFCHRW@LJKGE}W}FeKnh_1k+g5r1`8` zm^Bt<*K}I>6o`tviP!R45Q7WigeNl#nn4gf;~wb=MAwzNbS@Ob);I}wUN?9<3MV{1PAl084kUZUDI zhDg#!<>Z+3L-~N-n~wt*F2Vlm04< zwD8mnRMJlcwiLP+EKm)TMvl425qxYhJ`!wqqm!s|KLbZbL#~!CSC)YnMlM1=r8J^W z`r@!MZ5(p|F*+CuKW^ZS;koV8v>nK!wlva~z}}(^D({X?>iU4{+Q_j7&`b?z$(7y= z81b~o+>k>yR%>`BJU0ZOiK{(;T4?`~1tACLJAi5#vy=UW*EWF<*k3WHk?A4t2K}t2 z*&#kPJSX&Yab!-&2?l%B9GMN)>=4^)4FotyA(!E^0rM813-0U_7hXvS&nUtJ$wF_v z!I`0Y{7@Bo+YC!R@OYZgi(pOPf9P2@;>Q!LlP1&reRQKo9u3LaY=O1Q&5dqDdS&e# zc5`!8+culV9tFX=B*Dfc!3ZTe_P)rh&>fqf?Vl+=Lj4BGXiUS}1H*+s93~_3nf<1?|(E;pw5U#V5@zqcP}{k?hffn;EK` z8KH2B8fV52X&u`bk0b z;lpt`mpW0AbGgtAfEp2b)3rh7rfxm%xbQ z9oEZHsL3bW|HbB!9Urn;AfCA(KUo6vCB0lC2wKz+EfDrF-al(H;Cz~tH)z~s01A%t-T-2TcO`|7mR+) z?TT1`n`8aiQmw1qT)B!~-#A{gtH!&{@~nY7=C%^IcVNmv)oeGVZbEsvgW;?xmXx9> zR^&k8?MSQhlvOTvE)~B7_|JeJRYxP!lwzhm_V?O~hNpgBw~E+m(l--T1m#w;tAWmfl%=z!5xn_YxXmw;Djrh6NI zLiBIrgVg$zS^?{z$=sk2Dpn5hiIcgBA!MYsR5Gp#Hrdaxm#^Yubkl2Dqf}BAJ{%3g zjU2>X_z(`!dd6GfphP8XV?qOr z!;$S53BI>P;XC~reAU6nR=`Ic=IUQQxw^RA^;~&__Ef*&UQhO%($#|_j88txJ{Unh znELS%jZZFOM@6(gIgNEjbUt}FTNTm! zsg$k{lQuA1XAQX)d#$qr#07bA;TKWviv(jEBz7XjQy|@L*@drlXr{0k{%a(BIRjOZJ= Yh08Nh>0VwBZ - -; This program is free software: you can redistribute it and/or modify -; it under the terms of the GNU General Public License as published by -; the Free Software Foundation, either version 3 of the License, or -; (at your option) any later version. - -; This program is distributed in the hope that it will be useful, -; but WITHOUT ANY WARRANTY; without even the implied warranty of -; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -; GNU General Public License for more details. - -; You should have received a copy of the GNU General Public License -; along with this program. If not, see . - - -IF !DEF(CONSTANTS_ASM) -DEF CONSTANTS_ASM EQU 1 - -CHARMAP " ", 1 -CHARMAP "0", 66 -CHARMAP "1", 67 -CHARMAP "2", 68 -CHARMAP "3", 69 -CHARMAP "4", 70 -CHARMAP "5", 71 -CHARMAP "6", 72 -CHARMAP "7", 73 -CHARMAP "8", 74 -CHARMAP "9", 75 -CHARMAP "A", 76 -CHARMAP "B", 77 -CHARMAP "C", 78 -CHARMAP "D", 79 -CHARMAP "E", 80 -CHARMAP "F", 81 -CHARMAP "G", 82 -CHARMAP "H", 83 -CHARMAP "I", 84 -CHARMAP "J", 85 -CHARMAP "K", 86 -CHARMAP "L", 87 -CHARMAP "M", 88 -CHARMAP "N", 89 -CHARMAP "O", 90 -CHARMAP "P", 91 -CHARMAP "Q", 92 -CHARMAP "R", 93 -CHARMAP "S", 94 -CHARMAP "T", 95 -CHARMAP "U", 96 -CHARMAP "V", 97 -CHARMAP "W", 98 -CHARMAP "X", 99 -CHARMAP "Y", 100 -CHARMAP "Z", 101 -CHARMAP "!", 102 -CHARMAP "?", 103 -CHARMAP "[", 129 -CHARMAP "]", 130 -CHARMAP "/", 128 -CHARMAP "-", 127 -CHARMAP "#", 126 -CHARMAP ".", 216 -CHARMAP ":", 222 - - -SECTION "Static Data", ROMX, BANK[1] -sLeady:: db " READY? " -sGo:: db " GO " -sPause:: - db "P A U S E " - db " P A U S E" -sOption0:: - db "NORM" - db " INV" -sOption1:: - db "TGM1" - db "TGM2" - db "TGM3" - db "HELL" - db " NES" -sOption2:: - db "ARS1" - db "ARS2" - db " NES" -sOption3:: - db "FIRM" - db "SNIC" - db "HARD" - db "LOCK" - db "NONE" -sOption4:: - db "DMGT" - db "TGM1" - db "TGM3" - db "DEAT" - db "SHIR" - db "CHIL" -sOption5:: - db " NO" - db " YES" -sDisabled:: - db "----" - -sEasterM0:: db $C4, $C6, $C8, $CA, $CC -sEasterM1:: db $C5, $C7, $C9, $CB, $CD -sEasterC0:: db $CE, $D0, $C8, $CA, $CC, $72, $74, $76, $78, $7A, $D6, $D7 -sEasterC1:: db $CF, $D1, $C9, $CB, $CD, $73, $75, $77, $79, $7B, $01, $01 -sEasterA0:: db $D2, $D4, $C8, $CA, $CC, $72, $74, $76, $78, $7A, $D6, $D7 -sEasterA1:: db $D3, $D5, $C9, $CB, $CD, $73, $75, $77, $79, $7B, $01, $01 -sEasterS0:: db $F7, $F9, $C8, $CA, $CC -sEasterS1:: db $F8, $FA, $C9, $CB, $CD - -sPieceXOffsets:: ; How to draw each piece. X-offsets of the sprites. - db 0, 8, 16, 24 ; I - db 0, 8, 8, 16 ; Z - db 0, 8, 8, 16 ; S - db 0, 8, 16, 16 ; J - db 0, 0, 8, 16 ; L - db 8, 8, 16, 16 ; O - db 0, 8, 8, 16 ; T - -sPieceYOffsets:: ; How to draw each piece. Y-offsets of the sprites. - db 0, 0, 0, 0 ; I - db 0, 0, 7, 7 ; Z - db 7, 7, 0, 0 ; S - db 0, 0, 0, 7 ; J - db 7, 0, 0, 0 ; L - db 0, 7, 0, 7 ; O - db 0, 0, 7, 0 ; T - -; Speed curve data is defined as follows: -; N blocks of: -; dw BCD_START_LEVEL, START_LEVEL, BCD_NEXT_100_LEVEL_BREAKPOINT -; db GRID_CELLS_PER_MOVE_ON_OVERFLOW, INCREMENT_PER_FRAME (0 means overflow each frame) -; db NORMAL_ARE, LINE_ARE, DAS, LOCK_DELAY, CLEAR_DELAY -; -; Followed by one single: -; dw $FFFF -sDMGTSpeedCurve:: ; Speed curve of the game. - dw $0000, 0, $0100 ; Level 0000 - db 1, 16 - db 25, 15, 14, 30, 40 - - dw $0015, 15, $0100 ; Level 0015 - db 1, 17 - db 25, 15, 14, 30, 40 - - dw $0030, 30, $0100 ; Level 0030 - db 1, 18 - db 25, 15, 14, 30, 40 - - dw $0040, 40, $0100 ; Level 0040 - db 1, 20 - db 25, 15, 14, 30, 40 - - dw $0050, 50, $0100 ; Level 0050 - db 1, 21 - db 25, 15, 14, 30, 40 - - dw $0060, 60, $0100 ; Level 0060 - db 1, 23 - db 25, 15, 14, 30, 40 - - dw $0070, 70, $0100 ; Level 0070 - db 1, 26 - db 25, 15, 14, 30, 40 - - dw $0080, 80, $0100 ; Level 0080 - db 1, 28 - db 25, 15, 14, 30, 40 - - dw $0090, 90, $0100 ; Level 0090 - db 1, 32 - db 25, 15, 14, 30, 40 - - dw $0100, 100, $0200 ; Level 0100 - db 1, 37 - db 25, 15, 14, 30, 40 - - dw $0150, 150, $0200 ; Level 0150 - db 1, 43 - db 25, 15, 14, 30, 40 - - dw $0200, 200, $0300 ; Level 0200 - db 1, 51 - db 25, 15, 14, 30, 40 - - dw $0225, 225, $0300 ; Level 0225 - db 1, 64 - db 25, 15, 14, 30, 40 - - dw $0250, 250, $0300 ; Level 0250 - db 1, 85 - db 25, 15, 14, 30, 40 - - dw $0275, 275, $0300 ; Level 0275 - db 1, 128 - db 25, 15, 14, 30, 40 - - dw $0300, 300, $0400 ; Level 0300 - db 1, $00 - db 25, 7, 14, 30, 32 - - dw $0350, 350, $0350 ; Level 0350 - db 2, $00 - db 25, 7, 14, 30, 32 - - dw $0400, 400, $0400 ; Level 0400 - db 3, $00 - db 25, 7, 14, 30, 32 - - dw $0450, 450, $0500 ; Level 0450 - db 4, $00 - db 25, 7, 14, 30, 32 - - dw $0475, 475, $0500 ; Level 0475 - db 5, $00 - db 25, 7, 14, 30, 32 - - dw $0500, 500, $0600 ; Level 0500 - db 20, $00 - db 25, 6, 14, 30, 24 - - dw $0600, 600, $0700 ; Level 0600 - db 20, $00 - db 25, 6, 8, 30, 24 - - dw $0700, 700, $0800 ; Level 0700 - db 20, $00 - db 20, 6, 8, 30, 24 - - dw $0900, 900, $1000 ; Level 0900 - db 20, $00 - db 16, 4, 6, 25, 16 - - dw $1100, 1100, $1200 ; Level 1100 - db 20, $00 - db 12, 4, 6, 25, 16 - - dw $1200, 1200, $1300 ; Level 1200 - db 20, $00 - db 12, 4, 6, 25, 8 - - dw $1300, 1300, $1400 ; Level 1300 - db 20, $00 - db 10, 4, 6, 20, 7 - - dw $1400, 1400, $1500 ; Level 1400 - db 20, $00 - db 10, 4, 6, 18, 6 - - dw $1500, 1500, $1600 ; Level 1500 - db 20, $00 - db 8, 4, 4, 16, 5 - - dw $1600, 1600, $1700 ; Level 1600 - db 20, $00 - db 8, 4, 4, 14, 4 - - dw $1700, 1700, $1800 ; Level 1700 - db 20, $00 - db 6, 4, 4, 12, 3 - - dw $1800, 1800, $1900 ; Level 1800 - db 20, $00 - db 6, 4, 4, 10, 3 - - dw $1900, 1900, $2000 ; Level 1900 - db 20, $00 - db 4, 4, 4, 8, 3 - - dw $2000, 2000, $2100 ; Level 2000 - db 20, $00 - db 4, 4, 3, 8, 3 - - dw $2500, 2500, $2600 ; Level 2500 - db 20, $00 - db 2, 2, 1, 8, 2 - - dw $3000, 3000, $3100 ; Level 3000 - db 20, $00 - db 1, 1, 1, 8, 1 - - dw $4000, 4000, $4100 ; Level 4000 - db 20, $00 - db 1, 1, 1, 6, 1 - - dw $5000, 5000, $5100 ; Level 5000 - db 20, $00 - db 1, 1, 1, 4, 1 - - dw $6666, 6666, $6700 ; Level 6666 - db 20, $00 - db 1, 1, 1, 2, 1 - - dw $9999, 9999, $9999 ; Level 9999 - db 20, $00 - db 1, 1, 1, 1, 1 - -sDMGTSpeedCurveEnd:: - dw $FFFF ; End. - -sTGM1SpeedCurve:: - dw $0000, 0, $0100 - db 1, 4 - db 30, 30, 16, 30, 41 - - dw $0030, 30, $0100 - db 1, 6 - db 30, 30, 16, 30, 41 - - dw $0035, 35, $0100 - db 1, 8 - db 30, 30, 16, 30, 41 - - dw $0040, 40, $0100 - db 1, 10 - db 30, 30, 16, 30, 41 - - dw $0050, 50, $0100 - db 1, 12 - db 30, 30, 16, 30, 41 - - dw $0060, 60, $0100 - db 1, 16 - db 30, 30, 16, 30, 41 - - dw $0070, 70, $0100 - db 1, 32 - db 30, 30, 16, 30, 41 - - dw $0080, 80, $0100 - db 1, 48 - db 30, 30, 16, 30, 41 - - dw $0090, 90, $0100 - db 1, 64 - db 30, 30, 16, 30, 41 - - dw $0100, 100, $0200 - db 1, 80 - db 30, 30, 16, 30, 41 - - dw $0120, 120, $0200 - db 1, 96 - db 30, 30, 16, 30, 41 - - dw $0140, 140, $0200 - db 1, 112 - db 30, 30, 16, 30, 41 - - dw $0160, 160, $0200 - db 1, 128 - db 30, 30, 16, 30, 41 - - dw $0170, 170, $0200 - db 1, 144 - db 30, 30, 16, 30, 41 - - dw $0200, 200, $0300 - db 1, 4 - db 30, 30, 16, 30, 41 - - dw $0220, 220, $0300 - db 1, 32 - db 30, 30, 16, 30, 41 - - dw $0230, 230, $0300 - db 1, 64 - db 30, 30, 16, 30, 41 - - dw $0233, 233, $0300 - db 1, 96 - db 30, 30, 16, 30, 41 - - dw $0236, 236, $0300 - db 1, 128 - db 30, 30, 16, 30, 41 - - dw $0239, 239, $0300 - db 1, 160 - db 30, 30, 16, 30, 41 - - dw $0243, 243, $0300 - db 1, 192 - db 30, 30, 16, 30, 41 - - dw $0247, 239, $0300 - db 1, 224 - db 30, 30, 16, 30, 41 - - dw $0251, 251, $0300 - db 1, $00 - db 30, 30, 16, 30, 41 - - dw $0300, 300, $0400 - db 2, $00 - db 30, 30, 16, 30, 41 - - dw $0330, 330, $0400 - db 3, $00 - db 30, 30, 16, 30, 41 - - dw $0360, 360, $0400 - db 4, $00 - db 30, 30, 16, 30, 41 - - dw $0400, 400, $0500 - db 5, $00 - db 30, 30, 16, 30, 41 - - dw $0420, 420, $0500 - db 4, $00 - db 30, 30, 16, 30, 41 - - dw $0450, 450, $0500 - db 3, $00 - db 30, 30, 16, 30, 41 - - dw $0500, 500, $0600 - db 20, $00 - db 30, 30, 16, 30, 41 - -sTGM1SpeedCurveEnd:: - dw $FFFF - - -sCHILSpeedCurve:: - dw $0000, 0, $0100 - db 1, 5 - db 10, 10, 16, 25, 17 - - dw $0100, 100, $0200 - db 1, 6 - db 10, 10, 16, 25, 17 - - dw $0200, 200, $0300 - db 1, 7 - db 10, 10, 16, 25, 17 - - dw $0300, 300, $0400 - db 1, 8 - db 10, 10, 16, 25, 17 - - dw $0400, 400, $0500 - db 1, 9 - db 10, 10, 16, 25, 17 - - dw $0500, 500, $0600 - db 1, 11 - db 10, 10, 16, 25, 17 - - dw $0600, 600, $0700 - db 1, 14 - db 10, 10, 16, 25, 17 - - dw $0700, 700, $0800 - db 1, 20 - db 10, 10, 16, 25, 17 - - dw $0800, 800, $0900 - db 1, 32 - db 10, 10, 16, 25, 17 - - dw $0900, 900, $1000 - db 1, 43 - db 10, 10, 16, 25, 17 - - dw $1000, 1000, $1100 - db 1, 51 - db 10, 10, 16, 25, 17 - - dw $1300, 1300, $1400 - db 1, 64 - db 10, 10, 16, 25, 17 - - dw $1600, 1600, $1700 - db 1, 85 - db 10, 10, 16, 25, 17 - - dw $1900, 1900, $2000 - db 1, 128 - db 10, 10, 16, 25, 17 - - dw $2900, 2900, $3000 - db 1, $00 - db 10, 10, 16, 25, 17 - - dw $3333, 3333, $3400 - db 2, $00 - db 10, 10, 12, 25, 17 - - dw $4444, 4444, $4500 - db 3, $00 - db 10, 10, 12, 25, 17 - - dw $5555, 5555, $5600 - db 4, $00 - db 10, 10, 12, 25, 17 - - dw $6666, 6666, $6700 - db 5, $00 - db 10, 10, 12, 25, 17 - - dw $7777, 7777, $7800 - db 20, $00 - db 10, 10, 8, 25, 17 - - dw $8888, 8888, $8900 - db 20, $00 - db 10, 10, 6, 18, 17 - - dw $9999, 9999, $9999 - db 20, $00 - db 5, 5, 6, 14, 10 - -sCHILSpeedCurveEnd:: - dw $FFFF - - -sTGM3SpeedCurve:: - dw $0000, 0, $0100 - db 1, 4 - db 27, 27, 16, 30, 40 - - dw $0030, 30, $0100 - db 1, 6 - db 27, 27, 16, 30, 40 - - dw $0035, 35, $0100 - db 1, 8 - db 27, 27, 16, 30, 40 - - dw $0040, 40, $0100 - db 1, 10 - db 27, 27, 16, 30, 40 - - dw $0050, 50, $0100 - db 1, 12 - db 27, 27, 16, 30, 40 - - dw $0060, 60, $0100 - db 1, 16 - db 27, 27, 16, 30, 40 - - dw $0070, 70, $0100 - db 1, 32 - db 27, 27, 16, 30, 40 - - dw $0080, 80, $0100 - db 1, 48 - db 27, 27, 16, 30, 40 - - dw $0090, 90, $0100 - db 1, 64 - db 27, 27, 16, 30, 40 - - dw $0100, 100, $0200 - db 1, 80 - db 27, 27, 16, 30, 40 - - dw $0120, 120, $0200 - db 1, 96 - db 27, 27, 16, 30, 40 - - dw $0140, 140, $0200 - db 1, 112 - db 27, 27, 16, 30, 40 - - dw $0160, 160, $0200 - db 1, 128 - db 27, 27, 16, 30, 40 - - dw $0170, 170, $0200 - db 1, 144 - db 27, 27, 16, 30, 40 - - dw $0200, 200, $0300 - db 1, 4 - db 27, 27, 16, 30, 40 - - dw $0220, 220, $0300 - db 1, 32 - db 27, 27, 16, 30, 40 - - dw $0230, 230, $0300 - db 1, 64 - db 27, 27, 16, 30, 40 - - dw $0233, 233, $0300 - db 1, 96 - db 27, 27, 16, 30, 40 - - dw $0236, 236, $0300 - db 1, 128 - db 27, 27, 16, 30, 40 - - dw $0239, 239, $0300 - db 1, 160 - db 27, 27, 16, 30, 40 - - dw $0243, 243, $0300 - db 1, 192 - db 27, 27, 16, 30, 40 - - dw $0247, 239, $0300 - db 1, 224 - db 27, 27, 16, 30, 40 - - dw $0251, 251, $0300 - db 1, $00 - db 27, 27, 16, 30, 40 - - dw $0300, 300, $0400 - db 2, $00 - db 27, 27, 16, 30, 40 - - dw $0330, 330, $0400 - db 3, $00 - db 27, 27, 16, 30, 40 - - dw $0360, 360, $0400 - db 4, $00 - db 27, 27, 16, 30, 40 - - dw $0400, 400, $0500 - db 5, $00 - db 27, 27, 16, 30, 40 - - dw $0420, 420, $0500 - db 4, $00 - db 27, 27, 16, 30, 40 - - dw $0450, 450, $0500 - db 3, $00 - db 27, 27, 16, 30, 40 - - dw $0500, 500, $0600 - db 20, $00 - db 27, 27, 10, 30, 25 - - dw $0600, 600, $0700 - db 20, $00 - db 27, 18, 10, 30, 16 - - dw $0700, 700, $0800 - db 20, $00 - db 18, 14, 10, 30, 12 - - dw $0800, 800, $0900 - db 20, $00 - db 14, 8, 10, 30, 6 - - dw $0900, 900, $1000 - db 20, $00 - db 14, 8, 8, 17, 6 - - dw $1000, 1000, $1100 - db 20, $00 - db 8, 8, 8, 17, 6 - - dw $1100, 1000, $1200 - db 20, $00 - db 7, 7, 8, 15, 6 - - dw $1200, 1000, $1300 - db 20, $00 - db 6, 6, 8, 15, 6 - -sTGM3SpeedCurveEnd:: - dw $FFFF - -sDEATSpeedCurve:: - dw $0000, 0, $0100 - db 20, $00 - db 18, 14, 12, 30, 12 - - dw $0100, 0, $0200 - db 20, $00 - db 14, 8, 12, 26, 6 - - dw $0200, 0, $0300 - db 20, $00 - db 14, 8, 11, 22, 6 - - dw $0300, 0, $0400 - db 20, $00 - db 8, 8, 10, 18, 6 - - dw $0400, 0, $0500 - db 20, $00 - db 7, 7, 8, 15, 5 - - dw $0500, 0, $0600 - db 20, $00 - db 6, 6, 8, 15, 4 - -sDEATSpeedCurveEnd:: - dw $FFFF - -sSHIRSpeedCurve:: - dw $0000, 0, $0100 - db 20, $00 - db 12, 8, 10, 18, 6 - - dw $0100, 100, $0200 - db 20, $00 - db 12, 7, 8, 18, 5 - - dw $0200, 200, $0300 - db 20, $00 - db 12, 6, 8, 17, 4 - - dw $0300, 300, $0400 - db 20, $00 - db 6, 6, 8, 15, 4 - - dw $0500, 500, $0600 - db 20, $00 - db 6, 5, 6, 13, 3 - - dw $1100, 1100, $1200 - db 20, $00 - db 6, 5, 6, 10, 3 - - dw $1200, 1200, $1300 - db 20, $00 - db 6, 5, 6, 8, 3 - -sSHIRSpeedCurveEnd:: - dw $FFFF - -sPieceFastRotationStates:: - ; I - db 14, 1, 1, 1 - db 2, 14, 14, 14 - db 14, 1, 1, 1 - db 2, 14, 14, 14 - - ; Z - db 14, 1, 14, 1 - db 2, 13, 1, 13 - db 14, 1, 14, 1 - db 2, 13, 1, 13 - - ; S - db 15, 1, 12, 1 - db 0, 14, 1, 14 - db 15, 1, 12, 1 - db 0, 14, 1, 14 - - ; J - db 14, 1, 1, 14 - db 1, 1, 13, 14 - db 14, 14, 1, 1 - db 1, 14, 13, 1 - - ; L - db 14, 1, 1, 12 - db 1, 14, 14, 1 - db 16, 12, 1, 1 - db 0, 1, 14, 14 - - ; O - db 15, 1, 13, 1 - db 15, 1, 13, 1 - db 15, 1, 13, 1 - db 15, 1, 13, 1 - - ; T - db 14, 1, 1, 13 - db 1, 14, 1, 13 - db 15, 13, 1, 1 - db 1, 13, 1, 14 - -sPieceRotationStates:: ; How each piece is rotated. - ; I - db %0000 - db %1111 - db %0000 - db %0000 - - db %0010 - db %0010 - db %0010 - db %0010 - - db %0000 - db %1111 - db %0000 - db %0000 - - db %0010 - db %0010 - db %0010 - db %0010 - - ; Z - db %0000 - db %1100 - db %0110 - db %0000 - - db %0010 - db %0110 - db %0100 - db %0000 - - db %0000 - db %1100 - db %0110 - db %0000 - - db %0010 - db %0110 - db %0100 - db %0000 - - ; S - db %0000 - db %0110 - db %1100 - db %0000 - - db %1000 - db %1100 - db %0100 - db %0000 - - db %0000 - db %0110 - db %1100 - db %0000 - - db %1000 - db %1100 - db %0100 - db %0000 - - ; J - db %0000 - db %1110 - db %0010 - db %0000 - - db %0110 - db %0100 - db %0100 - db %0000 - - db %0000 - db %1000 - db %1110 - db %0000 - - db %0100 - db %0100 - db %1100 - db %0000 - - ; L - db %0000 - db %1110 - db %1000 - db %0000 - - db %0100 - db %0100 - db %0110 - db %0000 - - db %0000 - db %0010 - db %1110 - db %0000 - - db %1100 - db %0100 - db %0100 - db %0000 - - ; O - db %0000 - db %0110 - db %0110 - db %0000 - - db %0000 - db %0110 - db %0110 - db %0000 - - db %0000 - db %0110 - db %0110 - db %0000 - - db %0000 - db %0110 - db %0110 - db %0000 - - ; T - db %0000 - db %1110 - db %0100 - db %0000 - - db %0100 - db %0110 - db %0100 - db %0000 - - db %0000 - db %0100 - db %1110 - db %0000 - - db %0100 - db %1100 - db %0100 - db %0000 - -sTGM3Bag:: - db 0, 0, 0, 0, 0 - db 1, 1, 1, 1, 1 - db 2, 2, 2, 2, 2 - db 3, 3, 3, 3, 3 - db 4, 4, 4, 4, 4 - db 5, 5, 5, 5, 5 - db 6, 6, 6, 6, 6 - -sTGM3Droughts:: - db 0, 0, 0, 0, 0, 0, 0 - - -ENDC diff --git a/src/field.asm b/src/field.asm index dc63a97..c3c4a28 100644 --- a/src/field.asm +++ b/src/field.asm @@ -61,7 +61,64 @@ hBravo: ds 1 hShouldLockIfGrounded: ds 1 -SECTION "Field Functions", ROM0 +SECTION "Field Function Unbanked", ROM0 + ; Blits the field onto the tile map. + ; On the GBC, this chain calls into a special version that takes + ; advantage of the GBC's CPU. +BlitField:: + ld a, [wInitialA] + cp a, $11 + jp z, GBCBlitField + + ; What to copy + ld de, wField + 40 + ; Where to put it + ld hl, FIELD_TOP_LEFT + ; How much to increment hl after each row + ld bc, 32-10 + + ; The first 14 rows can be blitted without checking for vram access. + REPT 14 + REPT 10 + ld a, [de] + ld [hl+], a + inc de + ENDR + add hl, bc + ENDR + +: ldh a, [rLY] + cp a, 0 + jr nz, :- + + ; The last 6 rows need some care. + REPT 6 + ; Wait until start of drawing, then insert 35 nops. +: ldh a, [rSTAT] + and a, 3 + cp a, 3 + jr nz, :- + REPT 35 + nop + ENDR + + ; Blit a line. + REPT 10 + ld a, [de] + ld [hl+], a + inc de + ENDR + + ; Increment HL so that the next line can be blitted. + add hl, bc + ENDR + + ; This function is actually called as the vblank handler for the gameplay state. + ; This is why it jumps straight back to the event loop. + jp EventLoop + + +SECTION "Field Function Banked Gameplay", ROMX, BANK[BANK_GAMEPLAY] ; Initializes the field completely blank. ; Initializes the combo counter to 1. ; Initializes the bravo counter to 0. @@ -149,63 +206,6 @@ FromShadowField: jr nz, .outer ret - - ; Blits the field onto the tile map. - ; On the GBC, this chain calls into a special version that takes - ; advantage of the GBC's CPU. -BlitField:: - ld a, [wInitialA] - cp a, $11 - jp z, GBCBlitField - - ; What to copy - ld de, wField + 40 - ; Where to put it - ld hl, FIELD_TOP_LEFT - ; How much to increment hl after each row - ld bc, 32-10 - - ; The first 14 rows can be blitted without checking for vram access. - REPT 14 - REPT 10 - ld a, [de] - ld [hl+], a - inc de - ENDR - add hl, bc - ENDR - -: ldh a, [rLY] - cp a, 0 - jr nz, :- - - ; The last 6 rows need some care. - REPT 6 - ; Wait until start of drawing, then insert 35 nops. -: ldh a, [rSTAT] - and a, 3 - cp a, 3 - jr nz, :- - REPT 35 - nop - ENDR - - ; Blit a line. - REPT 10 - ld a, [de] - ld [hl+], a - inc de - ENDR - - ; Increment HL so that the next line can be blitted. - add hl, bc - ENDR - - ; This function is actually called as the vblank handler for the gameplay state. - ; This is why it jumps straight back to the event loop. - jp EventLoop - - ; The current piece ID is used to get the offset into the rotation states ; corresponding to that piece's zero rotation. SetPieceData: @@ -2159,4 +2159,2073 @@ ClearLines: ret +SECTION "Field Function Banked Gameplay Big", ROMX, BANK[BANK_GAMEPLAY_BIG] + ; Initializes the field completely blank. + ; Initializes the combo counter to 1. + ; Initializes the bravo counter to 0. + ; Initializes the shadow field. +BigFieldInit:: + xor a, a + ldh [hBravo], a + ld a, 1 + ldh [hComboCt], a + ld hl, wField + ld bc, 10*24 + ld d, TILE_BLANK + call UnsafeMemSet + ld hl, wShadowField + ld bc, 14*26 + ld d, $FF + jp UnsafeMemSet + + ; Fills the field with the empty tile. +BigFieldClear:: + ld hl, wField + ld bc, 10*24 + ld d, 0 + call UnsafeMemSet + DEF row = 0 + REPT 14 + ld hl, wField + (row*10) + ld bc, 5 + ld d, TILE_FIELD_EMPTY + call UnsafeMemSet + DEF row += 1 + ENDR + ret + + + ; Backs up the field. + ; This backup field is used for pausing the game. +BigToBackupField:: + ld de, wField + ld hl, wBackupField + ld bc, 10*24 + jp UnsafeMemCopy + + + ; Restores the backup of the field for ending pause mode. +BigFromBackupField:: + ld hl, wField + ld de, wBackupField + ld bc, 10*24 + jp UnsafeMemCopy + + + ; Copies the field to the shadow field. + ; This shadow field is used to calculate whether or not the piece can fit. +BigToShadowField:: + ld hl, wField + ld de, wShadowField+2 + ld c, 24 +.outer + ld b, 10 +.inner + ld a, [hl+] + ld [de], a + inc de + dec b + jr nz, .inner + inc de + inc de + inc de + inc de + dec c + jr nz, .outer + ret + + + ; Restores the shadow field to the main field. +BigFromShadowField: + ld hl, wField + ld de, wShadowField+2 + ld c, 24 +.outer + ld b, 10 +.inner + ld a, [de] + ld [hl+], a + inc de + dec b + jr nz, .inner + inc de + inc de + inc de + inc de + dec c + jr nz, .outer + ret + + ; The current piece ID is used to get the offset into the rotation states + ; corresponding to that piece's zero rotation. +BigSetPieceData: + ldh a, [hCurrentPiece] + sla a + sla a + sla a + sla a + ld c, a + ld b, 0 + + ld hl, sBigPieceRotationStates + add hl, bc + ld a, l + ldh [hPieceDataBase], a + ld a, h + ldh [hPieceDataBase+1], a + + ld hl, sBigPieceFastRotationStates + add hl, bc + ld a, l + ldh [hPieceDataBaseFast], a + ld a, h + ldh [hPieceDataBaseFast+1], a + ret + + + ; The rotation state is a further offset of 4 bytes. +BigSetPieceDataOffset: + ldh a, [hCurrentPieceRotationState] + sla a + sla a + ldh [hPieceDataOffset], a + ret + + + ; Converts piece Y in B and a piece X in A to a pointer to the shadow field in HL. +BigXYToSFieldPtr: + ld hl, wShadowField + ld de, 14 + inc a + inc b +: dec b + jr z, :+ + add hl, de + jr :- +: dec a + ret z + inc hl + jr :- + ret + + + ; Converts piece Y in B and a piece X in A to a pointer to the field in HL. +BigXYToFieldPtr: + ld hl, wField-2 + ld de, 10 + inc a + inc b +: dec b + jr z, :+ + add hl, de + jr :- +: dec a + ret z + inc hl + jr :- + ret + + + ; This function makes HL point to the correct offset into the rotation data. + ; This version of the data is used for thorough checking (T, J, and L have + ; a middle column exception.) +BigGetPieceData: + ldh a, [hPieceDataBase] + ld l, a + ldh a, [hPieceDataBase+1] + ld h, a + ldh a, [hPieceDataOffset] + ld c, a + ld b, 0 + add hl, bc + ret + + + ; Same as the above but for the fast data. This data is used when the exact + ; cell that failed isn't important. +BigGetPieceDataFast: + ldh a, [hPieceDataBaseFast] + ld l, a + ldh a, [hPieceDataBaseFast+1] + ld h, a + ldh a, [hPieceDataOffset] + ld c, a + xor a, a + ld b, a + add hl, bc + ret + + + ; Checks if the piece can fit at the current position. + ; HL should point to the piece's rotation state data. + ; DE should be pointing to the right place in the SHADOW field. + ; This will return with $FF in A if the piece fits, or with the + ; exact cell that caused the first failure in A. +BigCanPieceFit: + xor a, a + ld b, a + + ; Row 1 + bit 3, [hl] + jr z, :+ + ld a, [de] + cp a, TILE_FIELD_EMPTY + ld a, b + ret nz +: inc de + inc b + bit 2, [hl] + jr z, :+ + ld a, [de] + cp a, TILE_FIELD_EMPTY + ld a, b + ret nz +: inc de + inc b + bit 1, [hl] + jr z, :+ + ld a, [de] + cp a, TILE_FIELD_EMPTY + ld a, b + ret nz +: inc de + inc b + bit 0, [hl] + jr z, .r1end + ld a, [de] + cp a, TILE_FIELD_EMPTY + ld a, b + ret nz + +.r1end + REPT 11 + inc de + ENDR + + ; Row 2 + inc b + inc hl + bit 3, [hl] + jr z, :+ + ld a, [de] + cp a, TILE_FIELD_EMPTY + ld a, b + ret nz +: inc de + inc b + bit 2, [hl] + jr z, :+ + ld a, [de] + cp a, TILE_FIELD_EMPTY + ld a, b + ret nz +: inc de + inc b + bit 1, [hl] + jr z, :+ + ld a, [de] + cp a, TILE_FIELD_EMPTY + ld a, b + ret nz +: inc de + inc b + bit 0, [hl] + jr z, .r2end + ld a, [de] + cp a, TILE_FIELD_EMPTY + ld a, b + ret nz + +.r2end + REPT 11 + inc de + ENDR + + ; Row 3 + inc b + inc hl + bit 3, [hl] + jr z, :+ + ld a, [de] + cp a, TILE_FIELD_EMPTY + ld a, b + ret nz +: inc de + inc b + bit 2, [hl] + jr z, :+ + ld a, [de] + cp a, TILE_FIELD_EMPTY + ld a, b + ret nz +: inc de + inc b + bit 1, [hl] + jr z, :+ + ld a, [de] + cp a, TILE_FIELD_EMPTY + ld a, b + ret nz +: inc de + inc b + bit 0, [hl] + jr z, .r3end + ld a, [de] + cp a, TILE_FIELD_EMPTY + ret nz + +.r3end + REPT 11 + inc de + ENDR + + ; Row 4 + inc b + inc hl + bit 3, [hl] + jr z, :+ + ld a, [de] + cp a, TILE_FIELD_EMPTY + ld a, b + ret nz +: inc de + inc b + bit 2, [hl] + jr z, :+ + ld a, [de] + cp a, TILE_FIELD_EMPTY + ld a, b + ret nz +: inc de + inc b + bit 1, [hl] + jr z, :+ + ld a, [de] + cp a, TILE_FIELD_EMPTY + ld a, b + ret nz +: inc de + inc b + bit 0, [hl] + jr z, :+ + ld a, [de] + cp a, TILE_FIELD_EMPTY + ld a, b + ret nz + + ; If we got here, the piece can fit. +: ld a, $FF + ret + + + ; Checks if the piece can fit at the current position, but fast. + ; HL should point to the piece's fast rotation state data. + ; DE should be pointing to the right place in the SHADOW field. + ; This will return with $FF in A if the piece fits, or with a non-$FF + ; value if it doesn't. +BigCanPieceFitFast: + ld a, [hl+] + add a, e + ld e, a + adc a, d + sub e + ld d, a + ld a, [de] + cp a, TILE_FIELD_EMPTY + jr z, :+ + xor a, a + ret +: ld a, [hl+] + add a, e + ld e, a + adc a, d + sub e + ld d, a + ld a, [de] + cp a, TILE_FIELD_EMPTY + jr z, :+ + xor a, a + ret +: ld a, [hl+] + add a, e + ld e, a + adc a, d + sub e + ld d, a + ld a, [de] + cp a, TILE_FIELD_EMPTY + jr z, :+ + xor a, a + ret +: ld a, [hl+] + add a, e + ld e, a + adc a, d + sub e + ld d, a + ld a, [de] + cp a, TILE_FIELD_EMPTY + jr z, :+ + xor a, a + ret +: ld a, $FF + ret + + + ; This function will draw the piece even if it can't fit. + ; We use this to draw a final failed spawn before going game + ; over. +BigForceSpawnPiece:: + call BigSetPieceData + call BigSetPieceDataOffset + ldh a, [hCurrentPieceY] + ld b, a + ldh a, [hCurrentPieceX] + call BigXYToFieldPtr + ld d, h + ld e, l + call BigGetPieceData + ld b, GAME_OVER_OTHER + push hl + push de + pop hl + pop de + jp BigDrawPiece + + + ; Initialize the state for a new piece and attempts to spawn it. + ; On return, A will be $FF if the piece fit. +BigTrySpawnPiece:: + ; Always reset these for a new piece. + xor a, a + ldh [hStalePiece], a + ldh [hDownFrames], a + ldh [hAwardDownBonus], a + ldh [hLockDelayForce], a + ldh [hShouldLockIfGrounded], a + ldh [hGravityCtr], a + ldh [hGrounded], a + ldh a, [hCurrentLockDelay] + ldh [hCurrentLockDelayRemaining], a + ld a, $FF + ldh [hRemainingDelay], a + ld a, DELAY_STATE_DETERMINE_DELAY + ld [wDelayState], a + + ; Point the piece data to the correct piece. + call BigSetPieceData + call BigSetPieceDataOffset + + ; Get the piece's spawn position. + ldh a, [hCurrentPieceY] + ld b, a + ldh a, [hCurrentPieceX] + call BigXYToSFieldPtr + + ; Check if the piece can spawn. + ld d, h + ld e, l + call BigGetPieceDataFast + call BigCanPieceFitFast + + ; A will be $FF if the piece can fit. + cp a, $FF + ret z + + ; Otherwise, try in this order: 0, 1, 3, 2 + ldh a, [hCurrentPieceRotationState] + ldh [hWantRotation], a + + ; Try rotation state 0. +.try0 + cp a, 0 + jr z, .try1 + xor a, a + ldh [hCurrentPieceRotationState], a + call BigSetPieceDataOffset + ldh a, [hCurrentPieceY] + ld b, a + ldh a, [hCurrentPieceX] + call XYToSFieldPtr + ld d, h + ld e, l + call BigGetPieceDataFast + call BigCanPieceFitFast + cp a, $FF + ret z + + ; Try rotation state 1. +.try1 + ldh a, [hWantRotation] + cp a, 1 + jr z, .try3 + ld a, 1 + ldh [hCurrentPieceRotationState], a + call BigSetPieceDataOffset + ldh a, [hCurrentPieceY] + ld b, a + ldh a, [hCurrentPieceX] + call BigXYToSFieldPtr + ld d, h + ld e, l + call BigGetPieceDataFast + call BigCanPieceFitFast + cp a, $FF + ret z + + ; Try rotation state 3. +.try3 + ldh a, [hWantRotation] + cp a, 3 + jr z, .try2 + ld a, 3 + ldh [hCurrentPieceRotationState], a + call BigSetPieceDataOffset + ldh a, [hCurrentPieceY] + ld b, a + ldh a, [hCurrentPieceX] + call BigXYToSFieldPtr + ld d, h + ld e, l + call BigGetPieceDataFast + call BigCanPieceFitFast + cp a, $FF + ret z + + ; Try rotation state 2. +.try2 + ld a, 2 + ldh [hCurrentPieceRotationState], a + call BigSetPieceDataOffset + ldh a, [hCurrentPieceY] + ld b, a + ldh a, [hCurrentPieceX] + call BigXYToSFieldPtr + ld d, h + ld e, l + call BigGetPieceDataFast + jp BigCanPieceFitFast + + + ; Draws the piece onto the field. + ; B is the tile. + ; DE should point to the piece's rotation state data. + ; HL should be pointing to the right place in the NORMAL field. +BigDrawPiece: + ld a, [de] + inc de + + bit 3, a + jr z, :+ + ld [hl], b +: inc hl + bit 2, a + jr z, :+ + ld [hl], b +: inc hl + bit 1, a + jr z, :+ + ld [hl], b +: inc hl + bit 0, a + jr z, .r1end2 + ld [hl], b + +.r1end2 + REPT 7 + inc hl + ENDR + ld a, [de] + inc de + + bit 3, a + jr z, :+ + ld [hl], b +: inc hl + bit 2, a + jr z, :+ + ld [hl], b +: inc hl + bit 1, a + jr z, :+ + ld [hl], b +: inc hl + bit 0, a + jr z, .r2end2 + ld [hl], b + +.r2end2 + REPT 7 + inc hl + ENDR + ld a, [de] + inc de + + bit 3, a + jr z, :+ + ld [hl], b +: inc hl + bit 2, a + jr z, :+ + ld [hl], b +: inc hl + bit 1, a + jr z, :+ + ld [hl], b +: inc hl + bit 0, a + jr z, .r3end2 + ld [hl], b + +.r3end2 + REPT 7 + inc hl + ENDR + ld a, [de] + inc de + + bit 3, a + jr z, :+ + ld [hl], b +: inc hl + bit 2, a + jr z, :+ + ld [hl], b +: inc hl + bit 1, a + jr z, :+ + ld [hl], b +: inc hl + bit 0, a + ret z + ld [hl], b + ret + + +BigFindMaxG: + ; Find the deepest the piece can go. + ; We cache this pointer, cause it otherwise takes too much time. + ldh a, [hCurrentPieceY] + ld b, a + ldh a, [hCurrentPieceX] + call BigXYToSFieldPtr + + push hl + ld a, 1 + ldh [hActualG], a +.try + ld de, 14 + pop hl + add hl, de + push hl + ld d, h + ld e, l + call BigGetPieceDataFast + call BigCanPieceFitFast + cp a, $FF + jr nz, .found + ldh a, [hActualG] + inc a + ldh [hActualG], a + jr .try + +.found + pop hl + ldh a, [hActualG] + dec a + ldh [hActualG], a + ret + + + ; This is the main function that will process input, gravity, and locking. + ; It should be ran once per frame as long as lock delay is greater than 0. +BigFieldProcess:: + ; ************************************************************** + ; SETUP + ; Wipe out the piece. + ldh a, [hCurrentPieceY] + ldh [hYPosAtStartOfFrame], a + call BigFromShadowField + + ; Cleanup from last frame. + ldh a, [hCurrentPieceX] + ldh [hWantX], a + ldh a, [hCurrentPieceRotationState] + ldh [hWantRotation], a + + ; Is this the first frame of the piece? +.firstframe + ldh a, [hStalePiece] + cp a, 0 + jr nz, .handleselect + ld a, $FF + ldh [hStalePiece], a + jp .skipmovement + + + ; ************************************************************** + ; HANDLE SELECT + ; Check if we're about to hold. Return if so. +.handleselect + ldh a, [hSelectState] + cp a, 1 + jr nz, .wantrotccw + ldh a, [hHoldSpent] + cp a, $FF + ret nz + + + ; ************************************************************** + ; HANDLE ROTATION + ; Want rotate CCW? +.wantrotccw + ld a, [wSwapABState] + cp a, 0 + jr z, .ldb1 +.lda1 + ldh a, [hAState] + jr .cp1 +.ldb1 + ldh a, [hBState] +.cp1 + cp a, 1 + jr nz, .wantrotcw + ldh a, [hWantRotation] + inc a + and a, $03 + ldh [hWantRotation], a + jr .tryrot + + ; Want rotate CW? +.wantrotcw + ld a, [wSwapABState] + cp a, 0 + jr z, .lda2 +.ldb2 + ldh a, [hBState] + jr .cp2 +.lda2 + ldh a, [hAState] +.cp2 + cp a, 1 + jp nz, .norot + ldh a, [hWantRotation] + dec a + and a, $03 + ldh [hWantRotation], a + + ; Try the rotation. +.tryrot + ldh a, [hCurrentPieceY] + ld b, a + ldh a, [hCurrentPieceX] + call BigXYToSFieldPtr + ld d, h + ld e, l + ldh a, [hPieceDataBase] + ld l, a + ldh a, [hPieceDataBase+1] + ld h, a + ldh a, [hWantRotation] + sla a + sla a + push bc + ld c, a + xor a, a + ld b, a + add hl, bc + pop bc + call BigCanPieceFit ; This does have to be the "slow" version. + cp a, $FF + jr nz, .maybekick + ldh a, [hWantRotation] + ldh [hCurrentPieceRotationState], a + call BigSetPieceDataOffset + ldh a, [hLockDelayForce] ; Set the forced lock delay to 2 if it's 1. + cp a, 1 + jp nz, .norot + inc a + ldh [hLockDelayForce], a + jp .norot + + ; Try kicks if the piece isn't I or O. And in the case of J L and T, only if the blocked side is the left or right. +.maybekick + ld c, a + ; No kicks for NES mode. + ld a, [wRotModeState] + cp a, ROT_MODE_NES + jp z, .norot + + ldh a, [hCurrentPiece] + ; O pieces never kick, obviously. + cp a, PIECE_O + jp z, .norot + + ; S/Z always kick. + cp a, PIECE_S + jr z, .trykickright + cp a, PIECE_Z + jr z, .trykickright + + ; I piece only kicks in ARS2 + cp a, PIECE_I + jr nz, :+ + ld a, [wRotModeState] + cp a, ROT_MODE_ARSTI + jp nz, .norot + ldh a, [hWantRotation] + bit 0, a + jp nz, .checki + jr .trykickright + + ; T/L/J only kick if not through the middle axis. +: ld a, c + cp a, 1 + jp z, .maybetgm3rot + cp a, 5 + jr z, .maybetgm3rot + cp a, 9 + jr z, .maybetgm3rot + + ; A step to the right. +.trykickright + ldh a, [hCurrentPieceY] + ld b, a + ldh a, [hCurrentPieceX] + inc a + call BigXYToSFieldPtr + ld d, h + ld e, l + ldh a, [hPieceDataBaseFast] + ld l, a + ldh a, [hPieceDataBaseFast+1] + ld h, a + ldh a, [hWantRotation] + sla a + sla a + push bc + ld c, a + xor a, a + ld b, a + add hl, bc + pop bc + call BigCanPieceFitFast + cp a, $FF + jr nz, .trykickleft + ldh a, [hCurrentPieceX] + inc a + ldh [hCurrentPieceX], a + ldh a, [hWantRotation] + ldh [hCurrentPieceRotationState], a + call BigSetPieceDataOffset + ldh a, [hLockDelayForce] ; Set the forced lock delay to 2 if it's 1. + cp a, 1 + jp nz, .norot + inc a + ldh [hLockDelayForce], a + jp .norot + + ; And a step to the left. +.trykickleft + ldh a, [hCurrentPieceY] + ld b, a + ldh a, [hCurrentPieceX] + dec a + call BigXYToSFieldPtr + ld d, h + ld e, l + ldh a, [hPieceDataBaseFast] + ld l, a + ldh a, [hPieceDataBaseFast+1] + ld h, a + ldh a, [hWantRotation] + sla a + sla a + push bc + ld c, a + xor a, a + ld b, a + add hl, bc + pop bc + call BigCanPieceFitFast + cp a, $FF + jr nz, .maybetgm3rot + ldh a, [hCurrentPieceX] + dec a + ldh [hCurrentPieceX], a + ldh a, [hWantRotation] + ldh [hCurrentPieceRotationState], a + call BigSetPieceDataOffset + ldh a, [hLockDelayForce] ; Set the forced lock delay to 2 if it's 1. + cp a, 1 + jp nz, .norot + inc a + ldh [hLockDelayForce], a + jp .norot + + ; In ARS2 mode, there are a few other kicks possible. +.maybetgm3rot + ld a, [wRotModeState] + cp a, ROT_MODE_ARSTI + jp nz, .norot + + ; In the case of a T piece, try the space above. +.checkt + ldh a, [hCurrentPiece] + cp a, PIECE_T + jr nz, .checki + + ldh a, [hCurrentPieceY] + dec a + ld b, a + ldh a, [hCurrentPieceX] + call BigXYToSFieldPtr + ld d, h + ld e, l + ldh a, [hPieceDataBaseFast] + ld l, a + ldh a, [hPieceDataBaseFast+1] + ld h, a + ldh a, [hWantRotation] + sla a + sla a + push bc + ld c, a + xor a, a + ld b, a + add hl, bc + pop bc + call BigCanPieceFitFast + cp a, $FF + jp nz, .norot + ldh a, [hCurrentPieceY] + dec a + ldh [hCurrentPieceY], a + ldh a, [hWantRotation] + ldh [hCurrentPieceRotationState], a + call SetPieceDataOffset + ldh a, [hLockDelayForce] ; Set lock delay forcing to 1 if it's 0. + cp a, 0 + jr nz, :+ + inc a + ldh [hLockDelayForce], a + jp .norot +: cp a, 1 ; Or to 2 if it's 1. + jp nz, .norot + inc a + ldh [hLockDelayForce], a + jp .norot + + ; In the case of an I piece... +.checki + ldh a, [hCurrentPiece] + cp a, PIECE_I + jp nz, .norot + + ; What direction do we want to end up? + ldh a, [hWantRotation] + bit 0, a + jp z, .tryiright2 ; Flat? Sideways kicks are fine. + ; Upright? Only up kicks. + + ; Are we grounded? Don't kick if we aren't. + ldh a, [hActualG] + cp a, 0 + jp nz, .norot + + ; Try up once. +.tryiup1 + ldh a, [hCurrentPieceY] + dec a + ld b, a + ldh a, [hCurrentPieceX] + call BigXYToSFieldPtr + ld d, h + ld e, l + ldh a, [hPieceDataBaseFast] + ld l, a + ldh a, [hPieceDataBaseFast+1] + ld h, a + ldh a, [hWantRotation] + sla a + sla a + push bc + ld c, a + xor a, a + ld b, a + add hl, bc + pop bc + call BigCanPieceFitFast + cp a, $FF + jr nz, .tryiup2 + ldh a, [hCurrentPieceY] + dec a + ldh [hCurrentPieceY], a + ldh a, [hWantRotation] + ldh [hCurrentPieceRotationState], a + call SetPieceDataOffset + ldh a, [hLockDelayForce] ; Set lock delay forcing to 1 if it's 0. + cp a, 0 + jr nz, :+ + inc a + ldh [hLockDelayForce], a + jp .norot +: cp a, 1 ; Or to 2 if it's 1. + jp nz, .norot + inc a + ldh [hLockDelayForce], a + jp .norot + + ; Try up twice. +.tryiup2 + ldh a, [hCurrentPieceY] + dec a + dec a + ld b, a + ldh a, [hCurrentPieceX] + call BigXYToSFieldPtr + ld d, h + ld e, l + ldh a, [hPieceDataBaseFast] + ld l, a + ldh a, [hPieceDataBaseFast+1] + ld h, a + ldh a, [hWantRotation] + sla a + sla a + push bc + ld c, a + xor a, a + ld b, a + add hl, bc + pop bc + call BigCanPieceFitFast + cp a, $FF + jr nz, .norot + ldh a, [hCurrentPieceY] + dec a + dec a + ldh [hCurrentPieceY], a + ldh a, [hWantRotation] + ldh [hCurrentPieceRotationState], a + call BigSetPieceDataOffset + ldh a, [hLockDelayForce] ; Set lock delay forcing to 1 if it's 0. + cp a, 0 + jr nz, :+ + inc a + ldh [hLockDelayForce], a + jp .norot +: cp a, 1 ; Or to 2 if it's 1. + jp nz, .norot + inc a + ldh [hLockDelayForce], a + jp .norot + + ; Try right twice. +.tryiright2 + ldh a, [hCurrentPieceY] + ld b, a + ldh a, [hCurrentPieceX] + inc a + inc a + call XYToSFieldPtr + ld d, h + ld e, l + ldh a, [hPieceDataBaseFast] + ld l, a + ldh a, [hPieceDataBaseFast+1] + ld h, a + ldh a, [hWantRotation] + sla a + sla a + push bc + ld c, a + xor a, a + ld b, a + add hl, bc + pop bc + call BigCanPieceFitFast + cp a, $FF + jr nz, .norot + ldh a, [hCurrentPieceX] + inc a + inc a + ldh [hCurrentPieceX], a + ldh a, [hWantRotation] + ldh [hCurrentPieceRotationState], a + call SetPieceDataOffset + ldh a, [hLockDelayForce] ; Set the forced lock delay to 2 if it's 1. + cp a, 1 + jp nz, .norot + inc a + ldh [hLockDelayForce], a + + + ; ************************************************************** + ; HANDLE MOVEMENT + ; Do we want to move left? +.norot + ldh a, [hLeftState] ; Check if held for 1 frame. If so we move. + cp a, 1 + jr z, .doleft + cp a, 0 ; We never want to move if the button wasn't held. + jr z, .wantright + ld b, a + ldh a, [hGrounded] ; If we're grounded, assume some urgency in getting DAS charged, charge at twice the rate. + cp a, $FF + jr nz, .checkdasleft + inc b + ld a, b + ldh [hLeftState], a +.checkdasleft + ldh a, [hCurrentDAS] + ld c, a + ld a, b + cp a, c + jr c, .wantright +.doleft + ldh a, [hWantX] + dec a + ldh [hWantX], a + jr .trymove + + ; Do we want to move right? +.wantright + ldh a, [hRightState] ; Check if held for 1 frame. If so we move. + cp a, 1 + jr z, .doright + cp a, 0 ; We never want to move if the button wasn't held. + jr z, .donemanipulating + ld b, a + ldh a, [hGrounded] ; If we're grounded, assume some urgency in getting DAS charged, charge at twice the rate. + cp a, $FF + jr nz, .checkdasright + inc b + ld a, b + ldh [hRightState], a +.checkdasright + ldh a, [hCurrentDAS] + ld c, a + ld a, b + cp a, c + jr c, .donemanipulating +.doright + ldh a, [hWantX] + inc a + ldh [hWantX], a + + ; Try the movement. +.trymove + ldh a, [hCurrentPieceY] + ld b, a + ldh a, [hWantX] + call BigXYToSFieldPtr + ld d, h + ld e, l + call BigGetPieceDataFast + call BigCanPieceFitFast + cp a, $FF + jr nz, .donemanipulating + ldh a, [hWantX] + ldh [hCurrentPieceX], a + + + ; ************************************************************** + ; HANDLE MAXIMUM FALL + ; This little maneuver is going to cost us 51 years. +.skipmovement +.donemanipulating +: call BigFindMaxG + + + ; ************************************************************** + ; HANDLE UP + ; Assume 1G or lower. + ld a, 1 + ldh [hWantedG], a + + ; Is a hard/sonic drop requested? Skip if in 20G mode. + ldh a, [hCurrentIntegerGravity] + cp a, 20 + jr z, .postdrop + ldh a, [hUpState] + cp a, 1 + jr nz, .postdrop + + ; What kind, if any? + ld a, [wDropModeState] + cp a, DROP_MODE_NONE + jr z, .postdrop + cp a, DROP_MODE_LOCK + jr z, .harddrop + cp a, DROP_MODE_HARD + jr z, .harddrop + + ; Sonic drop. +.sonicdrop + ld a, [wDropModeState] + cp a, DROP_MODE_SNIC + jr z, :+ + ld a, $FF + ldh [hShouldLockIfGrounded], a +: ld a, $FF + ldh [hAwardDownBonus], a + ld a, 20 + ldh [hWantedG], a + jr .grav + + ; Hard drop. +.harddrop + ld a, $FF + ldh [hAwardDownBonus], a + ld a, 20 + ldh [hWantedG], a + ld b, a + ldh a, [hActualG] + cp a, b + jr nc, :+ + ld b, a +: ldh a, [hCurrentPieceY] + add a, b + ldh [hCurrentPieceY], a + xor a, a + ldh [hCurrentLockDelayRemaining], a + ld a, SFX_LOCK + call SFXTriggerNoise + jp .draw + + ; If we press down, we want to do a soft drop. +.postdrop + ldh a, [hDownState] + cp a, 0 + jr z, :+ + ldh a, [hDownFrames] + inc a + ldh [hDownFrames], a + ld a, $FF + ldh [hGravityCtr], a + ld a, [wDropModeState] + cp a, DROP_MODE_HARD + jr nz, :+ + ld a, $FF + ldh [hShouldLockIfGrounded], a + + ; Gravity? +: ldh a, [hCurrentFractionalGravity] + cp a, $00 ; 0 is the sentinel value that should be interpreted as "every frame" + jr z, :+ + ld b, a + ldh a, [hGravityCtr] + add a, b + ldh [hGravityCtr], a + jr nc, .nograv +: ldh a, [hCurrentIntegerGravity] + ldh [hWantedG], a + + ; Can we drop the full requested distance? +.grav + ldh a, [hWantedG] + ld b, a + ldh a, [hActualG] + cp a, b + jr c, .smallg + + ; Yes. Do it. +.bigg + ldh a, [hWantedG] + ld b, a + ldh a, [hCurrentPieceY] + add a, b + ldh [hCurrentPieceY], a + jr .postgrav + + ; No. Smaller distance. +.smallg + ldh a, [hActualG] + ld b, a + ldh a, [hCurrentPieceY] + add a, b + ldh [hCurrentPieceY], a + + + ; ************************************************************** + ; HANDLE LOCKING + ; Are we grounded? +.postgrav +.nograv + xor a, a + ldh [hGrounded], a + ldh a, [hYPosAtStartOfFrame] + ld b, a + ldh a, [hCurrentPieceY] + cp a, b + jr z, .noreset + ldh a, [hCurrentLockDelay] + ldh [hCurrentLockDelayRemaining], a +.noreset + ldh a, [hCurrentPieceY] + inc a + ld b, a + ldh a, [hCurrentPieceX] + call BigXYToSFieldPtr + ld d, h + ld e, l + call BigGetPieceDataFast + call BigCanPieceFitFast + cp a, $FF + jp z, .notgrounded + + ; We're grounded. +.grounded + ld a, $FF + ldh [hGrounded], a + ldh a, [hCurrentPieceY] + ld b, a + ldh a, [hYPosAtStartOfFrame] + cp a, b + jr z, .postcheckforfirmdropsound ; Never play the sound if we didn't change rows. + ldh a, [hDownState] + cp a, 0 + jr nz, .postcheckforfirmdropsound ; Don't play the sound if we're holding down. + + ; Play the firm drop sound, and also reset the lock delay since the piece stepped down. +.playfirmdropsound + ld a, SFX_LAND + call SFXTriggerNoise + + ; If the down button is held, lock. +.postcheckforfirmdropsound + ldh a, [hDownState] + cp a, 0 + jr z, .neutralcheck + + ; Don't lock on down for hard drop mode immediately. + ld a, [wDropModeState] + cp a, DROP_MODE_HARD + jr nz, :+ + ld a, $FF + ldh [hShouldLockIfGrounded], a + jr .dontforcelock + + ; Lock on down in modes <20G. +: ldh a, [hCurrentIntegerGravity] + cp a, 20 + jr nz, .forcelock + + ; In 20G mode, only lock if down has been pressed for exactly 1 frame. + ldh a, [hDownState] + cp a, 1 + jr z, .forcelock + jr .dontforcelock + + ; If the down button is not held, check if we're neutral and if that should lock. +.neutralcheck + ldh a, [hShouldLockIfGrounded] + cp a, 0 + jr z, .dontforcelock + + ; Check for neutral. + ldh a, [hUpState] + cp a, 0 + jr nz, .dontforcelock + ldh a, [hLeftState] + cp a, 0 + jr nz, .dontforcelock + ldh a, [hRightState] + cp a, 0 + jr nz, .dontforcelock + + ; Lock on neutral for a few modes. + ld a, [wDropModeState] + cp a, DROP_MODE_FIRM + jr z, .forcelock + cp a, DROP_MODE_HARD + jr z, .forcelock + jr .dontforcelock + + ; Set the lock delay to 0 and save it. +.forcelock + xor a, a + ldh [hCurrentLockDelayRemaining], a + jr .dolock + + ; Load the lock delay. + ; Decrement it by one and save it. +.dontforcelock + ldh a, [hCurrentLockDelayRemaining] + dec a + ldh [hCurrentLockDelayRemaining], a + + ; Are we out of lock delay? +.checklockdelay + cp a, 0 + jr nz, .checkfortgm3lockexception ; If not, check if the TGM3 exception applies. + jr .dolock ; Otherwise, lock! + + ; TGM3 sometimes forces a piece to immediately lock. +.checkfortgm3lockexception + ldh a, [hLockDelayForce] + cp a, 2 + jr nz, .draw ; It's not forced, so go to drawing. + xor a, a ; It is forced, so force it! + ldh [hCurrentLockDelayRemaining], a + + ; Play the locking sound and draw the piece. +.dolock + ld a, SFX_LOCK + call SFXTriggerNoise + jr .draw + + ; If we weren't grounded, reset the lock force. +.notgrounded + xor a, a + ldh [hShouldLockIfGrounded], a + + + ; ************************************************************** + ; HANDLE DRAWING + ; Draw the piece. +.draw + ; If the piece is locked, skip the ghost piece. + ldh a, [hCurrentLockDelayRemaining] + cp a, 0 + jr z, .postghost + + ; If the gravity is <= 1G, draw a ghost piece. + ldh a, [hWantedG] + cp a, 1 + jr nz, .postghost + ld a, [wInitialA] ; Let's not do the flickering on the GBC. + cp a, $11 + jr z, .ghost + ldh a, [hEvenFrame] + cp a, 1 + jr nz, .postghost + +.ghost + ldh a, [hYPosAtStartOfFrame] + ld b, a + ldh a, [hActualG] + add a, b + ld b, a + ldh a, [hCurrentPieceX] + call BigXYToFieldPtr + ld d, h + ld e, l + call BigGetPieceData + ld a, TILE_GHOST + ld b, a + push hl + push de + pop hl + pop de + call BigDrawPiece + +.postghost + ; If the lock delay is at the highest value, draw the piece normally. + ldh a, [hCurrentPiece] + ld b, TILE_PIECE_0 + add a, b + ldh [hWantedTile], a + ldh a, [hCurrentLockDelay] + ld b, a + ldh a, [hCurrentLockDelayRemaining] + cp a, b + jr z, .drawpiece + + ; If the lock delay is 0, draw the piece in the final color. + ldh a, [hWantedTile] + add a, 7 + ldh [hWantedTile], a + ldh a, [hCurrentLockDelayRemaining] + cp a, 0 + jr z, .drawpiece + + ; If we're not grounded, draw the piece normally. + ldh a, [hWantedTile] + sub a, 7 + ldh [hWantedTile], a + ldh a, [hGrounded] + cp a, $FF + jr nz, .drawpiece + + ; Otherwise, look it up. + call BigGetTileShade + +.drawpiece + ldh a, [hCurrentPieceY] + ld b, a + ldh a, [hCurrentPieceX] + call BigXYToFieldPtr + ld d, h + ld e, l + call BigGetPieceData + ldh a, [hWantedTile] + ld b, a + push hl + push de + pop hl + pop de + call BigDrawPiece + ret + + ; Performs a lookup to see how "locked" the piece is. +BigGetTileShade: + ldh a, [hCurrentLockDelay] + cp a, 30 + jr nc, .max30 + cp a, 20 + jr nc, .max20 + cp a, 10 + jr nc, .max10 + jr .max0 + ret +.max30 + ldh a, [hCurrentLockDelayRemaining] + cp a, 4 + ret c + cp a, 8 + jp c, .s6 + cp a, 12 + jr c, .s5 + cp a, 16 + jr c, .s4 + cp a, 20 + jr c, .s3 + cp a, 24 + jr c, .s2 + cp a, 28 + jr c, .s1 + jr .s0 +.max20 + ldh a, [hCurrentLockDelayRemaining] + cp a, 2 + ret c + cp a, 5 + jr c, .s6 + cp a, 7 + jr c, .s5 + cp a, 10 + jr c, .s4 + cp a, 12 + jr c, .s3 + cp a, 15 + jr c, .s2 + cp a, 17 + jr c, .s1 + jr .s0 +.max10 + ldh a, [hCurrentLockDelayRemaining] + cp a, 1 + ret c + cp a, 2 + jr c, .s6 + cp a, 3 + jr c, .s5 + cp a, 5 + jr c, .s4 + cp a, 6 + jr c, .s3 + cp a, 7 + jr c, .s2 + cp a, 8 + jr c, .s1 + jr .s0 +.max0 + jr .s4 +.s0 + ldh a, [hCurrentPiece] + ld b, TILE_PIECE_0 + add a, b + ldh [hWantedTile], a + ret +.s1 + ldh a, [hCurrentPiece] + ld b, TILE_PIECE_0+(2*7) + add a, b + ldh [hWantedTile], a + ret +.s2 + ldh a, [hCurrentPiece] + ld b, TILE_PIECE_0+(3*7) + add a, b + ldh [hWantedTile], a + ret +.s3 + ldh a, [hCurrentPiece] + ld b, TILE_PIECE_0+(4*7) + add a, b + ldh [hWantedTile], a + ret +.s4 + ldh a, [hCurrentPiece] + ld b, TILE_PIECE_0+(5*7) + add a, b + ldh [hWantedTile], a + ret +.s5 + ldh a, [hCurrentPiece] + ld b, TILE_PIECE_0+(6*7) + add a, b + ldh [hWantedTile], a + ret +.s6 + ldh a, [hCurrentPiece] + ld b, TILE_PIECE_0+(7*7) + add a, b + ldh [hWantedTile], a + ret + + + ; This is called every frame after a piece has been locked until the delay state ends. + ; Lines are cleared, levels and score are awarded, and ARE time is waited out. +BigFieldDelay:: + ; Switch on the delay state. + ld a, [wDelayState] + cp DELAY_STATE_DETERMINE_DELAY + jr z, .determine + cp DELAY_STATE_LINE_PRE_CLEAR + jr z, .prelineclear + cp DELAY_STATE_LINE_CLEAR + jp z, .lineclear + cp DELAY_STATE_PRE_ARE + jp z, .preare + jp .are + + + ; Check if there were line clears. + ; If so, we need to do a line clear delay. + ; Otherwise, we skip to ARE delay. +.determine + ; Increment bravo by 4. + ldh a, [hBravo] + add a, 4 + ldh [hBravo], a + + ; Are there line clears? + call BigToShadowField + call BigFindClearedLines + ldh a, [hClearedLines] + ld b, a + ldh a, [hClearedLines+1] + ld c, a + ldh a, [hClearedLines+2] + ld d, a + ldh a, [hClearedLines+3] + and a, b + and a, c + and a, d + cp a, $FF + jr z, .skip + ld a, DELAY_STATE_LINE_PRE_CLEAR ; If there were line clears, do a line clear delay, then a LINE_ARE delay. + ld [wDelayState], a + ldh a, [hCurrentLineClearDelay] + ldh [hRemainingDelay], a + call BigMarkClear + jp .prelineclear +.skip + ld a, DELAY_STATE_PRE_ARE ; If there were no line clears, do an ARE delay. + ld [wDelayState], a + ldh a, [hCurrentARE] + ldh [hRemainingDelay], a + jp .preare + + + ; Pre-line clear delay. + ; If we had line clears, immediately hand out the score and the levels. +.prelineclear: + ld a, DELAY_STATE_LINE_CLEAR + ld [wDelayState], a + + ldh a, [hLineClearCt] + cp a, 0 + jr z, .lineclear ; If not, just skip the phase. + + ; There were line clears! Clear the level counter breakpoint. + xor a, a + ldh [hRequiresLineClear], a + + ; Decrement bravo by 10 for each line clear. + ldh a, [hLineClearCt] + ld b, a + ldh a, [hBravo] +: sub a, 10 + dec b + jr nz, :- + ldh [hBravo], a + + ; Increment the level counter by the amount of lines. +.applylines + ldh a, [hLineClearCt] + ld e, a + call LevelUp + + ; Score the line clears. + ; Get the new level. + ldh a, [hLevel] + ld l, a + ldh a, [hLevel+1] + ld h, a + + ; Divide by 4. + srl h + rr l + srl h + rr l + + ; Add 1. + inc hl + + ; Add soft drop points. + ldh a, [hDownFrames] + ld c, a + xor a, a + ld b, a + + ; Lock bonus? + ldh a, [hAwardDownBonus] + cp a, $FF + jr nz, .premultiplier + ld a, 10 + add a, c + ld c, a + + ; Final total pre-multipliers. +.premultiplier + add hl, bc + + ; Copy the running total for multiplication. + ld b, h + ld c, l + + ; Do we have a bravo? x4 if so. +.bravo + ldh a, [hBravo] + cp a, 0 + jr nz, .lineclears + add hl, bc + jr c, .forcemax + add hl, bc + jr c, .forcemax + add hl, bc + jr c, .forcemax + ld b, h + ld c, l + + ; x line clears +.lineclears + ldh a, [hLineClearCt] + dec a + jr z, .combo +: add hl, bc + jr c, .forcemax + dec a + jr nz, :- + ld b, h + ld c, l + + ; x combo +.combo + ldh a, [hComboCt] + dec a + jr z, .applyscore +: add hl, bc + jr c, .forcemax + dec a + jr nz, :- + jr .applyscore + + ; Overflow = 65535 +.forcemax + ld a, $FF + ld h, a + ld l, a + + ; And apply the score. +.applyscore + ld a, l + ldh [hScoreIncrement], a + ld a, h + ldh [hScoreIncrement+1], a + call IncreaseScore + + ; Update the combo counter. + ldh a, [hLineClearCt] + ld b, a + ldh a, [hComboCt] ; Old combo count. + add b ; + lines + add b ; + lines + sub 2 ; - 2 + ldh [hComboCt], a + + + ; Line clear delay. + ; Count down the delay. If we're out of delay, clear the lines and go to LINE_ARE. +.lineclear + ldh a, [hRemainingDelay] + dec a + ldh [hRemainingDelay], a + cp a, 0 + ret nz + + call BigClearLines + ld a, SFX_LINE_CLEAR + call SFXTriggerNoise + +: ldh a, [hCurrentLineARE] + ldh [hRemainingDelay], a + + + ; Pre-ARE delay. +.preare: + ld a, DELAY_STATE_ARE + ld [wDelayState], a + + ; Copy over the newly cleaned field. + call BigToShadowField + + ; Don't do anything if there were line clears + ldh a, [hLineClearCt] + cp a, 0 + jr nz, .are + + ; Otherwise, reset the combo. + ld a, 1 + ldh [hComboCt], a + + ; ARE delay. + ; Count down the delay. If it hits 0, award levels and score if necessary, then end the delay phase. +.are + ldh a, [hRemainingDelay] + dec a + ldh [hRemainingDelay], a + cp a, 0 + ret nz + + ; Add one level if we're not at a breakpoint. + ldh a, [hRequiresLineClear] + cp a, $FF + jr z, :+ + ld e, 1 + call LevelUp + + ; Cycle the RNG. +: ldh a, [hNextPiece] + ldh [hCurrentPiece], a + call GetNextPiece + + ; Kill the sound for the next piece. + jp SFXKill + + + ; Shifts B into the line clear list. + ; Also increments the line clear count. +BigAppendClearedLine: + ldh a, [hLineClearCt] + inc a + ldh [hLineClearCt], a + ldh a, [hClearedLines+2] + ldh [hClearedLines+3], a + ldh a, [hClearedLines+1] + ldh [hClearedLines+2], a + ldh a, [hClearedLines] + ldh [hClearedLines+1], a + ld a, b + ldh [hClearedLines], a + ret + + + ; Scans the field for lines that are completely filled with non-empty spaces. + ; Every time one is found, it is added to a list. +BigFindClearedLines: + xor a, a + ldh [hLineClearCt], a + ld a, $FF + ld c, 0 + ldh [hClearedLines], a + ldh [hClearedLines+1], a + ldh [hClearedLines+2], a + ldh [hClearedLines+3], a + + DEF row = 13 + REPT 14 + ld hl, wShadowField+2+(row*14) + ld b, 11 +: ld a, [hl+] + dec b + cp a, $FF + jr z, :+ + cp a, TILE_FIELD_EMPTY + jr nz, :- +: xor a, a + cp a, b + jr nz, .next\@ + ld b, 13-row + call BigAppendClearedLine + inc c + ld a, 4 + cp a, c + ret z + DEF row -= 1 +.next\@ + ENDR + + ret + + ; Goes through the list of cleared lines and marks those lines with the "line clear" tile. +BigMarkClear: + ldh a, [hClearedLines] + cp a, $FF + ret z + ld hl, wField+(14*10) +: ld bc, -10 + add hl, bc + dec a + cp a, $FF + jr nz, :- + ld bc, 5 + ld d, TILE_CLEARING + call UnsafeMemSet + + ldh a, [hClearedLines+1] + cp a, $FF + ret z + ld hl, wField+(14*10) +: ld bc, -10 + add hl, bc + dec a + cp a, $FF + jr nz, :- + ld bc, 5 + ld d, TILE_CLEARING + call UnsafeMemSet + + ldh a, [hClearedLines+2] + cp a, $FF + ret z + ld hl, wField+(14*10) +: ld bc, -10 + add hl, bc + dec a + cp a, $FF + jr nz, :- + ld bc, 5 + ld d, TILE_CLEARING + call UnsafeMemSet + + ldh a, [hClearedLines+3] + cp a, $FF + ret z + ld hl, wField+(14*10) +: ld bc, -10 + add hl, bc + dec a + cp a, $FF + jr nz, :- + ld bc, 5 + ld d, TILE_CLEARING + jp UnsafeMemSet + + + ; Once again, scans the field for cleared lines, but this time removes them. +BigClearLines: + ld de, 0 + + DEF row = 23 + REPT 23 + ; Check if the row begins with a clearing tile. + ld hl, wField+(row*10) + ld a, [hl] + cp a, TILE_CLEARING + + ; If it does, increment the clearing counter, but skip this line. + jr nz, .clear\@ + inc de + inc de + inc de + inc de + inc de + inc de + inc de + inc de + inc de + inc de + jr .r\@ + +.clear\@ + ; If there's 0 lines that need to be moved down, skip this line. + xor a, a + cp a, e + jr z, .r\@ + + ; Otherwise... + ld bc, wField+(row*10) + add hl, de +: ld a, [bc] + ld [hl+], a + inc bc + ld a, [bc] + ld [hl+], a + inc bc + ld a, [bc] + ld [hl+], a + inc bc + ld a, [bc] + ld [hl+], a + inc bc + ld a, [bc] + ld [hl+], a + inc bc + ld a, [bc] + ld [hl+], a + inc bc + ld a, [bc] + ld [hl+], a + inc bc + ld a, [bc] + ld [hl+], a + inc bc + ld a, [bc] + ld [hl+], a + inc bc + ld a, [bc] + ld [hl+], a + inc bc +.r\@ + DEF row -= 1 + ENDR + + ; Make sure there's no garbage in the top de lines. +.fixgarbo + ld hl, wField +: xor a, a + or a, d + or a, e + ret z + ld a, TILE_FIELD_EMPTY + ld [hl+], a + ld [hl+], a + ld [hl+], a + ld [hl+], a + ld [hl+], a + ld a, 0 + ld [hl+], a + ld [hl+], a + ld [hl+], a + ld [hl+], a + ld [hl+], a + dec de + dec de + dec de + dec de + dec de + dec de + dec de + dec de + dec de + dec de + jr :- + ret + + ENDC diff --git a/src/include/globals.asm b/src/include/globals.asm index c2ba887..953b7ac 100644 --- a/src/include/globals.asm +++ b/src/include/globals.asm @@ -23,6 +23,55 @@ INCLUDE "hardware.inc" INCLUDE "structs.asm" +; Set up charmap. +CHARMAP " ", 1 +CHARMAP "0", 66 +CHARMAP "1", 67 +CHARMAP "2", 68 +CHARMAP "3", 69 +CHARMAP "4", 70 +CHARMAP "5", 71 +CHARMAP "6", 72 +CHARMAP "7", 73 +CHARMAP "8", 74 +CHARMAP "9", 75 +CHARMAP "A", 76 +CHARMAP "B", 77 +CHARMAP "C", 78 +CHARMAP "D", 79 +CHARMAP "E", 80 +CHARMAP "F", 81 +CHARMAP "G", 82 +CHARMAP "H", 83 +CHARMAP "I", 84 +CHARMAP "J", 85 +CHARMAP "K", 86 +CHARMAP "L", 87 +CHARMAP "M", 88 +CHARMAP "N", 89 +CHARMAP "O", 90 +CHARMAP "P", 91 +CHARMAP "Q", 92 +CHARMAP "R", 93 +CHARMAP "S", 94 +CHARMAP "T", 95 +CHARMAP "U", 96 +CHARMAP "V", 97 +CHARMAP "W", 98 +CHARMAP "X", 99 +CHARMAP "Y", 100 +CHARMAP "Z", 101 +CHARMAP "!", 102 +CHARMAP "?", 103 +CHARMAP "[", 129 +CHARMAP "]", 130 +CHARMAP "/", 128 +CHARMAP "-", 127 +CHARMAP "#", 126 +CHARMAP ".", 216 +CHARMAP ":", 222 + + ; Waits for PPU mode to be 0 or 1. ; We don't wait for 2 because it's super short and impractical to do much of anything in. MACRO wait_vram @@ -87,11 +136,14 @@ MACRO lb ENDM -; Magic bytes for save files. -DEF SAVE_MAGIC_0 EQU "D" -DEF SAVE_MAGIC_1 EQU "M" -DEF SAVE_MAGIC_2 EQU "G" -DEF SAVE_MAGIC_3 EQU "5" +; Bank names +DEF BANK_MAIN EQU 0 +DEF BANK_OTHER EQU 1 +DEF BANK_SFX EQU 2 +DEF BANK_MUSIC EQU 3 +DEF BANK_TITLE EQU 4 +DEF BANK_GAMEPLAY EQU 5 +DEF BANK_GAMEPLAY_BIG EQU 6 ; Some useful palettes. DEF PALETTE_REGULAR EQU %11100100 @@ -231,6 +283,8 @@ DEF LEADY_TIME EQU 80 DEF GO_TIME EQU 40 DEF PIECE_SPAWN_X EQU 5 DEF PIECE_SPAWN_Y EQU 3 +DEF PIECE_SPAWN_X_BIG EQU 3 +DEF PIECE_SPAWN_Y_BIG EQU 3 DEF ROTATION_STATE_DEF EQU 0 DEF ROTATION_STATE_CW EQU 1 DEF ROTATION_STATE_180 EQU 2 @@ -239,6 +293,7 @@ DEF ROTATION_STATE_CCW EQU 3 ; Game states. (Let these increase by 3) DEF STATE_TITLE EQU 0 DEF STATE_GAMEPLAY EQU 3 +DEF STATE_GAMEPLAY_BIG EQU 6 ; Other DEF STACK_SIZE EQU 64 diff --git a/src/level.asm b/src/level.asm index b3d03c1..ce9456a 100644 --- a/src/level.asm +++ b/src/level.asm @@ -43,6 +43,10 @@ hPrevHundreds:: ds 1 SECTION "Level Functions", ROM0 ; Loads the initial state of the speed curve. LevelInit:: + ; Bank to speed curve data. + ld b, BANK_OTHER + rst RSTSwitchBank + xor a, a ldh [hRequiresLineClear], a @@ -98,6 +102,9 @@ LevelInit:: and a, $0F ldh [hNLevel], a + ; Restore the bank before returning. + rst RSTRestoreBank + jp DoSpeedUp @@ -303,6 +310,10 @@ LevelUp:: ; Iterates over the speed curve and loads the new constants. DoSpeedUp: + ; Bank to speed curve data. + ld b, BANK_OTHER + rst RSTSwitchBank + ; Load curve ptr. ldh a, [hSpeedCurvePtr] ld l, a @@ -344,12 +355,12 @@ DoSpeedUp: ; Do we want to force 20G? ld a, [wAlways20GState] cp a, 0 - ret z + jp z, RSTRestoreBank ld a, 20 ldh [hCurrentIntegerGravity], a ld a, $00 ldh [hCurrentFractionalGravity], a - ret + jp RSTRestoreBank ENDC diff --git a/src/main.asm b/src/main.asm index b7000d4..e1f5f54 100644 --- a/src/main.asm +++ b/src/main.asm @@ -20,9 +20,7 @@ DEF MAIN_ASM EQU 1 INCLUDE "globals.asm" -INCLUDE "res/tiles.inc" -INCLUDE "res/gameplay_map.inc" -INCLUDE "res/title_map.inc" +INCLUDE "res/other_data.inc" SECTION "High Globals", HRAM @@ -85,13 +83,13 @@ Main:: ld [rRAMG], a xor a, a ld [rRAMB], a - ld a, BANK("Static Data") + ld a, BANK_OTHER ld [rROMB0], a ; We use a single set of tiles for the entire game, so we copy it at the start. - ld de, Tiles + ld de, sTiles ld hl, _VRAM - ld bc, TilesEnd - Tiles + ld bc, sTilesEnd - sTiles call SafeMemCopy ; Clear OAM. @@ -138,6 +136,7 @@ EventLoop:: .eventloopjumps jp TitleEventLoopHandler jp GamePlayEventLoopHandler + jp GamePlayBigEventLoopHandler EventLoopPostHandler:: ; Wait for vblank. @@ -157,6 +156,7 @@ EventLoopPostHandler:: .vblankjumps jp TitleVBlankHandler jp BlitField + jp BlitField ; The VBlank Handler is expected to end with jp EventLoop. diff --git a/src/res/gameplay_map.inc b/src/res/gameplay_big_data.inc similarity index 55% rename from src/res/gameplay_map.inc rename to src/res/gameplay_big_data.inc index 885f03a..e4c24f4 100644 --- a/src/res/gameplay_map.inc +++ b/src/res/gameplay_big_data.inc @@ -15,12 +15,232 @@ ; along with this program. If not, see . -IF !DEF(GAMEPLAY_MAP_INC) -DEF GAMEPLAY_MAP_INC EQU 1 +IF !DEF(GAMEPLAY_BIG_MAP_INC) +DEF GAMEPLAY_BIG_MAP_INC EQU 1 -SECTION "Gameplay Tilemap", ROMX, BANK[1] -GameplayTilemap:: +INCLUDE "globals.asm" + + +SECTION "Gameplay Big Static Data", ROMX, BANK[BANK_GAMEPLAY_BIG] +sBigLeady:: db " READY? " + +sBigGo:: db " GO " + +sBigPause:: + db "P A U S E " + db " P A U S E" + +sBigPieceXOffsets:: ; How to draw each piece. X-offsets of the sprites. + db 0, 8, 16, 24 ; I + db 0, 8, 8, 16 ; Z + db 0, 8, 8, 16 ; S + db 0, 8, 16, 16 ; J + db 0, 0, 8, 16 ; L + db 8, 8, 16, 16 ; O + db 0, 8, 8, 16 ; T + +sBigPieceYOffsets:: ; How to draw each piece. Y-offsets of the sprites. + db 0, 0, 0, 0 ; I + db 0, 0, 7, 7 ; Z + db 7, 7, 0, 0 ; S + db 0, 0, 0, 7 ; J + db 7, 0, 0, 0 ; L + db 0, 7, 0, 7 ; O + db 0, 0, 7, 0 ; T + +sBigPieceFastRotationStates:: + ; I + db 14, 1, 1, 1 + db 2, 14, 14, 14 + db 14, 1, 1, 1 + db 2, 14, 14, 14 + + ; Z + db 14, 1, 14, 1 + db 2, 13, 1, 13 + db 14, 1, 14, 1 + db 2, 13, 1, 13 + + ; S + db 15, 1, 12, 1 + db 0, 14, 1, 14 + db 15, 1, 12, 1 + db 0, 14, 1, 14 + + ; J + db 14, 1, 1, 14 + db 1, 1, 13, 14 + db 14, 14, 1, 1 + db 1, 14, 13, 1 + + ; L + db 14, 1, 1, 12 + db 1, 14, 14, 1 + db 16, 12, 1, 1 + db 0, 1, 14, 14 + + ; O + db 15, 1, 13, 1 + db 15, 1, 13, 1 + db 15, 1, 13, 1 + db 15, 1, 13, 1 + + ; T + db 14, 1, 1, 13 + db 1, 14, 1, 13 + db 15, 13, 1, 1 + db 1, 13, 1, 14 + +sBigPieceRotationStates:: ; How each piece is rotated. + ; I + db %0000 + db %1111 + db %0000 + db %0000 + + db %0010 + db %0010 + db %0010 + db %0010 + + db %0000 + db %1111 + db %0000 + db %0000 + + db %0010 + db %0010 + db %0010 + db %0010 + + ; Z + db %0000 + db %1100 + db %0110 + db %0000 + + db %0010 + db %0110 + db %0100 + db %0000 + + db %0000 + db %1100 + db %0110 + db %0000 + + db %0010 + db %0110 + db %0100 + db %0000 + + ; S + db %0000 + db %0110 + db %1100 + db %0000 + + db %1000 + db %1100 + db %0100 + db %0000 + + db %0000 + db %0110 + db %1100 + db %0000 + + db %1000 + db %1100 + db %0100 + db %0000 + + ; J + db %0000 + db %1110 + db %0010 + db %0000 + + db %0110 + db %0100 + db %0100 + db %0000 + + db %0000 + db %1000 + db %1110 + db %0000 + + db %0100 + db %0100 + db %1100 + db %0000 + + ; L + db %0000 + db %1110 + db %1000 + db %0000 + + db %0100 + db %0100 + db %0110 + db %0000 + + db %0000 + db %0010 + db %1110 + db %0000 + + db %1100 + db %0100 + db %0100 + db %0000 + + ; O + db %0000 + db %0110 + db %0110 + db %0000 + + db %0000 + db %0110 + db %0110 + db %0000 + + db %0000 + db %0110 + db %0110 + db %0000 + + db %0000 + db %0110 + db %0110 + db %0000 + + ; T + db %0000 + db %1110 + db %0100 + db %0000 + + db %0100 + db %0110 + db %0100 + db %0000 + + db %0000 + db %0100 + db %1110 + db %0000 + + db %0100 + db %1100 + db %0100 + db %0000 + +sBigGameplayTileMap:: DB $02,$01,$01,$01,$01,$01,$01,$01,$01,$01 DB $01,$03,$09,$01,$01,$01,$01,$01,$01,$09 DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00 @@ -87,7 +307,7 @@ GameplayTilemap:: DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00 DB $02,$00,$00,$00,$00,$00,$00,$00,$00,$00 DB $00,$03,$09,$01,$01,$01,$01,$01,$01,$09 -GameplayTilemapEnd:: +sBigGameplayTileMapEnd:: ENDC diff --git a/src/res/gameplay_data.inc b/src/res/gameplay_data.inc new file mode 100644 index 0000000..dc42834 --- /dev/null +++ b/src/res/gameplay_data.inc @@ -0,0 +1,325 @@ +; DMGTRIS +; Copyright (C) 2023 - Randy Thiemann + +; This program is free software: you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation, either version 3 of the License, or +; (at your option) any later version. + +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. + +; You should have received a copy of the GNU General Public License +; along with this program. If not, see . + + +IF !DEF(GAMEPLAY_MAP_INC) +DEF GAMEPLAY_MAP_INC EQU 1 + + +INCLUDE "globals.asm" + + +SECTION "Gameplay Static Data", ROMX, BANK[BANK_GAMEPLAY] +sLeady:: db " READY? " + +sGo:: db " GO " + +sPause:: + db "P A U S E " + db " P A U S E" + +sPieceXOffsets:: ; How to draw each piece. X-offsets of the sprites. + db 0, 8, 16, 24 ; I + db 0, 8, 8, 16 ; Z + db 0, 8, 8, 16 ; S + db 0, 8, 16, 16 ; J + db 0, 0, 8, 16 ; L + db 8, 8, 16, 16 ; O + db 0, 8, 8, 16 ; T + +sPieceYOffsets:: ; How to draw each piece. Y-offsets of the sprites. + db 0, 0, 0, 0 ; I + db 0, 0, 7, 7 ; Z + db 7, 7, 0, 0 ; S + db 0, 0, 0, 7 ; J + db 7, 0, 0, 0 ; L + db 0, 7, 0, 7 ; O + db 0, 0, 7, 0 ; T + +sPieceFastRotationStates:: + ; I + db 14, 1, 1, 1 + db 2, 14, 14, 14 + db 14, 1, 1, 1 + db 2, 14, 14, 14 + + ; Z + db 14, 1, 14, 1 + db 2, 13, 1, 13 + db 14, 1, 14, 1 + db 2, 13, 1, 13 + + ; S + db 15, 1, 12, 1 + db 0, 14, 1, 14 + db 15, 1, 12, 1 + db 0, 14, 1, 14 + + ; J + db 14, 1, 1, 14 + db 1, 1, 13, 14 + db 14, 14, 1, 1 + db 1, 14, 13, 1 + + ; L + db 14, 1, 1, 12 + db 1, 14, 14, 1 + db 16, 12, 1, 1 + db 0, 1, 14, 14 + + ; O + db 15, 1, 13, 1 + db 15, 1, 13, 1 + db 15, 1, 13, 1 + db 15, 1, 13, 1 + + ; T + db 14, 1, 1, 13 + db 1, 14, 1, 13 + db 15, 13, 1, 1 + db 1, 13, 1, 14 + +sPieceRotationStates:: ; How each piece is rotated. + ; I + db %0000 + db %1111 + db %0000 + db %0000 + + db %0010 + db %0010 + db %0010 + db %0010 + + db %0000 + db %1111 + db %0000 + db %0000 + + db %0010 + db %0010 + db %0010 + db %0010 + + ; Z + db %0000 + db %1100 + db %0110 + db %0000 + + db %0010 + db %0110 + db %0100 + db %0000 + + db %0000 + db %1100 + db %0110 + db %0000 + + db %0010 + db %0110 + db %0100 + db %0000 + + ; S + db %0000 + db %0110 + db %1100 + db %0000 + + db %1000 + db %1100 + db %0100 + db %0000 + + db %0000 + db %0110 + db %1100 + db %0000 + + db %1000 + db %1100 + db %0100 + db %0000 + + ; J + db %0000 + db %1110 + db %0010 + db %0000 + + db %0110 + db %0100 + db %0100 + db %0000 + + db %0000 + db %1000 + db %1110 + db %0000 + + db %0100 + db %0100 + db %1100 + db %0000 + + ; L + db %0000 + db %1110 + db %1000 + db %0000 + + db %0100 + db %0100 + db %0110 + db %0000 + + db %0000 + db %0010 + db %1110 + db %0000 + + db %1100 + db %0100 + db %0100 + db %0000 + + ; O + db %0000 + db %0110 + db %0110 + db %0000 + + db %0000 + db %0110 + db %0110 + db %0000 + + db %0000 + db %0110 + db %0110 + db %0000 + + db %0000 + db %0110 + db %0110 + db %0000 + + ; T + db %0000 + db %1110 + db %0100 + db %0000 + + db %0100 + db %0110 + db %0100 + db %0000 + + db %0000 + db %0100 + db %1110 + db %0000 + + db %0100 + db %1100 + db %0100 + db %0000 + +sTGM3Bag:: + db 0, 0, 0, 0, 0 + db 1, 1, 1, 1, 1 + db 2, 2, 2, 2, 2 + db 3, 3, 3, 3, 3 + db 4, 4, 4, 4, 4 + db 5, 5, 5, 5, 5 + db 6, 6, 6, 6, 6 + +sTGM3Droughts:: + db 0, 0, 0, 0, 0, 0, 0 + +sGameplayTileMap:: + DB $02,$01,$01,$01,$01,$01,$01,$01,$01,$01 + DB $01,$03,$09,$01,$01,$01,$01,$01,$01,$09 + DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + DB $00,$00,$02,$01,$01,$01,$01,$01,$01,$01 + DB $01,$01,$01,$03,$09,$59,$50,$63,$5F,$01 + DB $01,$09,$00,$00,$00,$00,$00,$00,$00,$00 + DB $00,$00,$00,$00,$02,$01,$01,$01,$01,$01 + DB $01,$01,$01,$01,$01,$03,$09,$01,$01,$01 + DB $01,$01,$01,$09,$00,$00,$00,$00,$00,$00 + DB $00,$00,$00,$00,$00,$00,$02,$01,$01,$01 + DB $01,$01,$01,$01,$01,$01,$01,$03,$09,$01 + DB $01,$01,$01,$01,$01,$09,$00,$00,$00,$00 + DB $00,$00,$00,$00,$00,$00,$00,$00,$02,$01 + DB $01,$01,$01,$01,$01,$01,$01,$01,$01,$03 + DB $09,$01,$01,$01,$01,$01,$01,$09,$00,$00 + DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + DB $02,$01,$01,$01,$01,$01,$01,$01,$01,$01 + DB $01,$03,$09,$01,$01,$01,$01,$01,$01,$09 + DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + DB $00,$00,$02,$01,$01,$01,$01,$01,$01,$01 + DB $01,$01,$01,$03,$08,$06,$06,$06,$06,$06 + DB $06,$07,$00,$00,$00,$00,$00,$00,$00,$00 + DB $00,$00,$00,$00,$02,$01,$01,$01,$01,$01 + DB $01,$01,$01,$01,$01,$03,$09,$53,$5A,$57 + DB $4F,$01,$01,$09,$00,$00,$00,$00,$00,$00 + DB $00,$00,$00,$00,$00,$00,$02,$01,$01,$01 + DB $01,$01,$01,$01,$01,$01,$01,$03,$09,$01 + DB $01,$01,$01,$01,$01,$09,$00,$00,$00,$00 + DB $00,$00,$00,$00,$00,$00,$00,$00,$02,$01 + DB $01,$01,$01,$01,$01,$01,$01,$01,$01,$03 + DB $09,$01,$01,$01,$01,$01,$01,$09,$00,$00 + DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + DB $02,$01,$01,$01,$01,$01,$01,$01,$01,$01 + DB $01,$03,$09,$01,$01,$01,$01,$01,$01,$09 + DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + DB $00,$00,$02,$01,$01,$01,$01,$01,$01,$01 + DB $01,$01,$01,$03,$09,$01,$01,$01,$01,$01 + DB $01,$09,$00,$00,$00,$00,$00,$00,$00,$00 + DB $00,$00,$00,$00,$02,$01,$01,$01,$01,$01 + DB $01,$01,$01,$01,$01,$03,$08,$06,$06,$06 + DB $06,$06,$06,$07,$00,$00,$00,$00,$00,$00 + DB $00,$00,$00,$00,$00,$00,$02,$01,$01,$01 + DB $01,$01,$01,$01,$01,$01,$01,$03,$09,$5E + DB $4E,$5A,$5D,$50,$01,$09,$00,$00,$00,$00 + DB $00,$00,$00,$00,$00,$00,$00,$00,$02,$01 + DB $01,$01,$01,$01,$01,$01,$01,$01,$01,$03 + DB $09,$01,$01,$01,$01,$01,$01,$09,$00,$00 + DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + DB $02,$01,$01,$01,$01,$01,$01,$01,$01,$01 + DB $01,$03,$08,$06,$06,$06,$06,$06,$06,$07 + DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + DB $00,$00,$02,$01,$01,$01,$01,$01,$01,$01 + DB $01,$01,$01,$03,$09,$57,$50,$61,$50,$57 + DB $01,$09,$00,$00,$00,$00,$00,$00,$00,$00 + DB $00,$00,$00,$00,$02,$01,$01,$01,$01,$01 + DB $01,$01,$01,$01,$01,$03,$09,$01,$01,$01 + DB $01,$01,$01,$09,$00,$00,$00,$00,$00,$00 + DB $00,$00,$00,$00,$00,$00,$02,$01,$01,$01 + DB $01,$01,$01,$01,$01,$01,$01,$03,$09,$01 + DB $05,$05,$05,$05,$01,$09,$00,$00,$00,$00 + DB $00,$00,$00,$00,$00,$00,$00,$00,$02,$01 + DB $01,$01,$01,$01,$01,$01,$01,$01,$01,$03 + DB $09,$01,$01,$01,$01,$01,$01,$09,$00,$00 + DB $00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + DB $02,$00,$00,$00,$00,$00,$00,$00,$00,$00 + DB $00,$03,$09,$01,$01,$01,$01,$01,$01,$09 +sGameplayTileMapEnd:: + + +ENDC diff --git a/src/res/music_data.inc b/src/res/music_data.inc index 4a97a81..77d09cb 100644 --- a/src/res/music_data.inc +++ b/src/res/music_data.inc @@ -18,7 +18,11 @@ IF !DEF(MUSIC_DATA_INC) DEF MUSIC_DATA_INC EQU 1 -SECTION "Music Data", ROMX, BANK[3] + +INCLUDE "globals.asm" + + +SECTION "Music Data", ROMX, BANK[BANK_MUSIC] sMusicMenu:: db REG_NR12_CH1_VOLEV, $00, REG_NR14_CH1_FRQHI, $80, REG_NR22_CH2_VOLEV, $00, REG_NR24_CH2_FRQHI, $80 diff --git a/src/res/tiles.inc b/src/res/other_data.inc similarity index 66% rename from src/res/tiles.inc rename to src/res/other_data.inc index 43c152b..d9e8bec 100644 --- a/src/res/tiles.inc +++ b/src/res/other_data.inc @@ -18,8 +18,614 @@ IF !DEF(TILES_INC) DEF TILES_INC EQU 1 -SECTION "Tile data", ROMX, BANK[1] -Tiles:: + +INCLUDE "globals.asm" + + +SECTION "Tile data", ROMX, BANK[BANK_OTHER] +; Speed curve data is defined as follows: +; N blocks of: +; dw BCD_START_LEVEL, START_LEVEL, BCD_NEXT_100_LEVEL_BREAKPOINT +; db GRID_CELLS_PER_MOVE_ON_OVERFLOW, INCREMENT_PER_FRAME (0 means overflow each frame) +; db NORMAL_ARE, LINE_ARE, DAS, LOCK_DELAY, CLEAR_DELAY +; +; Followed by one single: +; dw $FFFF +sDMGTSpeedCurve:: ; Speed curve of the game. + dw $0000, 0, $0100 ; Level 0000 + db 1, 16 + db 25, 15, 14, 30, 40 + + dw $0015, 15, $0100 ; Level 0015 + db 1, 17 + db 25, 15, 14, 30, 40 + + dw $0030, 30, $0100 ; Level 0030 + db 1, 18 + db 25, 15, 14, 30, 40 + + dw $0040, 40, $0100 ; Level 0040 + db 1, 20 + db 25, 15, 14, 30, 40 + + dw $0050, 50, $0100 ; Level 0050 + db 1, 21 + db 25, 15, 14, 30, 40 + + dw $0060, 60, $0100 ; Level 0060 + db 1, 23 + db 25, 15, 14, 30, 40 + + dw $0070, 70, $0100 ; Level 0070 + db 1, 26 + db 25, 15, 14, 30, 40 + + dw $0080, 80, $0100 ; Level 0080 + db 1, 28 + db 25, 15, 14, 30, 40 + + dw $0090, 90, $0100 ; Level 0090 + db 1, 32 + db 25, 15, 14, 30, 40 + + dw $0100, 100, $0200 ; Level 0100 + db 1, 37 + db 25, 15, 14, 30, 40 + + dw $0150, 150, $0200 ; Level 0150 + db 1, 43 + db 25, 15, 14, 30, 40 + + dw $0200, 200, $0300 ; Level 0200 + db 1, 51 + db 25, 15, 14, 30, 40 + + dw $0225, 225, $0300 ; Level 0225 + db 1, 64 + db 25, 15, 14, 30, 40 + + dw $0250, 250, $0300 ; Level 0250 + db 1, 85 + db 25, 15, 14, 30, 40 + + dw $0275, 275, $0300 ; Level 0275 + db 1, 128 + db 25, 15, 14, 30, 40 + + dw $0300, 300, $0400 ; Level 0300 + db 1, $00 + db 25, 7, 14, 30, 32 + + dw $0350, 350, $0350 ; Level 0350 + db 2, $00 + db 25, 7, 14, 30, 32 + + dw $0400, 400, $0400 ; Level 0400 + db 3, $00 + db 25, 7, 14, 30, 32 + + dw $0450, 450, $0500 ; Level 0450 + db 4, $00 + db 25, 7, 14, 30, 32 + + dw $0475, 475, $0500 ; Level 0475 + db 5, $00 + db 25, 7, 14, 30, 32 + + dw $0500, 500, $0600 ; Level 0500 + db 20, $00 + db 25, 6, 14, 30, 24 + + dw $0600, 600, $0700 ; Level 0600 + db 20, $00 + db 25, 6, 8, 30, 24 + + dw $0700, 700, $0800 ; Level 0700 + db 20, $00 + db 20, 6, 8, 30, 24 + + dw $0900, 900, $1000 ; Level 0900 + db 20, $00 + db 16, 4, 6, 25, 16 + + dw $1100, 1100, $1200 ; Level 1100 + db 20, $00 + db 12, 4, 6, 25, 16 + + dw $1200, 1200, $1300 ; Level 1200 + db 20, $00 + db 12, 4, 6, 25, 8 + + dw $1300, 1300, $1400 ; Level 1300 + db 20, $00 + db 10, 4, 6, 20, 7 + + dw $1400, 1400, $1500 ; Level 1400 + db 20, $00 + db 10, 4, 6, 18, 6 + + dw $1500, 1500, $1600 ; Level 1500 + db 20, $00 + db 8, 4, 4, 16, 5 + + dw $1600, 1600, $1700 ; Level 1600 + db 20, $00 + db 8, 4, 4, 14, 4 + + dw $1700, 1700, $1800 ; Level 1700 + db 20, $00 + db 6, 4, 4, 12, 3 + + dw $1800, 1800, $1900 ; Level 1800 + db 20, $00 + db 6, 4, 4, 10, 3 + + dw $1900, 1900, $2000 ; Level 1900 + db 20, $00 + db 4, 4, 4, 8, 3 + + dw $2000, 2000, $2100 ; Level 2000 + db 20, $00 + db 4, 4, 3, 8, 3 + + dw $2500, 2500, $2600 ; Level 2500 + db 20, $00 + db 2, 2, 1, 8, 2 + + dw $3000, 3000, $3100 ; Level 3000 + db 20, $00 + db 1, 1, 1, 8, 1 + + dw $4000, 4000, $4100 ; Level 4000 + db 20, $00 + db 1, 1, 1, 6, 1 + + dw $5000, 5000, $5100 ; Level 5000 + db 20, $00 + db 1, 1, 1, 4, 1 + + dw $6666, 6666, $6700 ; Level 6666 + db 20, $00 + db 1, 1, 1, 2, 1 + + dw $9999, 9999, $9999 ; Level 9999 + db 20, $00 + db 1, 1, 1, 1, 1 + +sDMGTSpeedCurveEnd:: + dw $FFFF ; End. + +sTGM1SpeedCurve:: + dw $0000, 0, $0100 + db 1, 4 + db 30, 30, 16, 30, 41 + + dw $0030, 30, $0100 + db 1, 6 + db 30, 30, 16, 30, 41 + + dw $0035, 35, $0100 + db 1, 8 + db 30, 30, 16, 30, 41 + + dw $0040, 40, $0100 + db 1, 10 + db 30, 30, 16, 30, 41 + + dw $0050, 50, $0100 + db 1, 12 + db 30, 30, 16, 30, 41 + + dw $0060, 60, $0100 + db 1, 16 + db 30, 30, 16, 30, 41 + + dw $0070, 70, $0100 + db 1, 32 + db 30, 30, 16, 30, 41 + + dw $0080, 80, $0100 + db 1, 48 + db 30, 30, 16, 30, 41 + + dw $0090, 90, $0100 + db 1, 64 + db 30, 30, 16, 30, 41 + + dw $0100, 100, $0200 + db 1, 80 + db 30, 30, 16, 30, 41 + + dw $0120, 120, $0200 + db 1, 96 + db 30, 30, 16, 30, 41 + + dw $0140, 140, $0200 + db 1, 112 + db 30, 30, 16, 30, 41 + + dw $0160, 160, $0200 + db 1, 128 + db 30, 30, 16, 30, 41 + + dw $0170, 170, $0200 + db 1, 144 + db 30, 30, 16, 30, 41 + + dw $0200, 200, $0300 + db 1, 4 + db 30, 30, 16, 30, 41 + + dw $0220, 220, $0300 + db 1, 32 + db 30, 30, 16, 30, 41 + + dw $0230, 230, $0300 + db 1, 64 + db 30, 30, 16, 30, 41 + + dw $0233, 233, $0300 + db 1, 96 + db 30, 30, 16, 30, 41 + + dw $0236, 236, $0300 + db 1, 128 + db 30, 30, 16, 30, 41 + + dw $0239, 239, $0300 + db 1, 160 + db 30, 30, 16, 30, 41 + + dw $0243, 243, $0300 + db 1, 192 + db 30, 30, 16, 30, 41 + + dw $0247, 239, $0300 + db 1, 224 + db 30, 30, 16, 30, 41 + + dw $0251, 251, $0300 + db 1, $00 + db 30, 30, 16, 30, 41 + + dw $0300, 300, $0400 + db 2, $00 + db 30, 30, 16, 30, 41 + + dw $0330, 330, $0400 + db 3, $00 + db 30, 30, 16, 30, 41 + + dw $0360, 360, $0400 + db 4, $00 + db 30, 30, 16, 30, 41 + + dw $0400, 400, $0500 + db 5, $00 + db 30, 30, 16, 30, 41 + + dw $0420, 420, $0500 + db 4, $00 + db 30, 30, 16, 30, 41 + + dw $0450, 450, $0500 + db 3, $00 + db 30, 30, 16, 30, 41 + + dw $0500, 500, $0600 + db 20, $00 + db 30, 30, 16, 30, 41 + +sTGM1SpeedCurveEnd:: + dw $FFFF + + +sCHILSpeedCurve:: + dw $0000, 0, $0100 + db 1, 5 + db 10, 10, 16, 25, 17 + + dw $0100, 100, $0200 + db 1, 6 + db 10, 10, 16, 25, 17 + + dw $0200, 200, $0300 + db 1, 7 + db 10, 10, 16, 25, 17 + + dw $0300, 300, $0400 + db 1, 8 + db 10, 10, 16, 25, 17 + + dw $0400, 400, $0500 + db 1, 9 + db 10, 10, 16, 25, 17 + + dw $0500, 500, $0600 + db 1, 11 + db 10, 10, 16, 25, 17 + + dw $0600, 600, $0700 + db 1, 14 + db 10, 10, 16, 25, 17 + + dw $0700, 700, $0800 + db 1, 20 + db 10, 10, 16, 25, 17 + + dw $0800, 800, $0900 + db 1, 32 + db 10, 10, 16, 25, 17 + + dw $0900, 900, $1000 + db 1, 43 + db 10, 10, 16, 25, 17 + + dw $1000, 1000, $1100 + db 1, 51 + db 10, 10, 16, 25, 17 + + dw $1300, 1300, $1400 + db 1, 64 + db 10, 10, 16, 25, 17 + + dw $1600, 1600, $1700 + db 1, 85 + db 10, 10, 16, 25, 17 + + dw $1900, 1900, $2000 + db 1, 128 + db 10, 10, 16, 25, 17 + + dw $2900, 2900, $3000 + db 1, $00 + db 10, 10, 16, 25, 17 + + dw $3333, 3333, $3400 + db 2, $00 + db 10, 10, 12, 25, 17 + + dw $4444, 4444, $4500 + db 3, $00 + db 10, 10, 12, 25, 17 + + dw $5555, 5555, $5600 + db 4, $00 + db 10, 10, 12, 25, 17 + + dw $6666, 6666, $6700 + db 5, $00 + db 10, 10, 12, 25, 17 + + dw $7777, 7777, $7800 + db 20, $00 + db 10, 10, 8, 25, 17 + + dw $8888, 8888, $8900 + db 20, $00 + db 10, 10, 6, 18, 17 + + dw $9999, 9999, $9999 + db 20, $00 + db 5, 5, 6, 14, 10 + +sCHILSpeedCurveEnd:: + dw $FFFF + + +sTGM3SpeedCurve:: + dw $0000, 0, $0100 + db 1, 4 + db 27, 27, 16, 30, 40 + + dw $0030, 30, $0100 + db 1, 6 + db 27, 27, 16, 30, 40 + + dw $0035, 35, $0100 + db 1, 8 + db 27, 27, 16, 30, 40 + + dw $0040, 40, $0100 + db 1, 10 + db 27, 27, 16, 30, 40 + + dw $0050, 50, $0100 + db 1, 12 + db 27, 27, 16, 30, 40 + + dw $0060, 60, $0100 + db 1, 16 + db 27, 27, 16, 30, 40 + + dw $0070, 70, $0100 + db 1, 32 + db 27, 27, 16, 30, 40 + + dw $0080, 80, $0100 + db 1, 48 + db 27, 27, 16, 30, 40 + + dw $0090, 90, $0100 + db 1, 64 + db 27, 27, 16, 30, 40 + + dw $0100, 100, $0200 + db 1, 80 + db 27, 27, 16, 30, 40 + + dw $0120, 120, $0200 + db 1, 96 + db 27, 27, 16, 30, 40 + + dw $0140, 140, $0200 + db 1, 112 + db 27, 27, 16, 30, 40 + + dw $0160, 160, $0200 + db 1, 128 + db 27, 27, 16, 30, 40 + + dw $0170, 170, $0200 + db 1, 144 + db 27, 27, 16, 30, 40 + + dw $0200, 200, $0300 + db 1, 4 + db 27, 27, 16, 30, 40 + + dw $0220, 220, $0300 + db 1, 32 + db 27, 27, 16, 30, 40 + + dw $0230, 230, $0300 + db 1, 64 + db 27, 27, 16, 30, 40 + + dw $0233, 233, $0300 + db 1, 96 + db 27, 27, 16, 30, 40 + + dw $0236, 236, $0300 + db 1, 128 + db 27, 27, 16, 30, 40 + + dw $0239, 239, $0300 + db 1, 160 + db 27, 27, 16, 30, 40 + + dw $0243, 243, $0300 + db 1, 192 + db 27, 27, 16, 30, 40 + + dw $0247, 239, $0300 + db 1, 224 + db 27, 27, 16, 30, 40 + + dw $0251, 251, $0300 + db 1, $00 + db 27, 27, 16, 30, 40 + + dw $0300, 300, $0400 + db 2, $00 + db 27, 27, 16, 30, 40 + + dw $0330, 330, $0400 + db 3, $00 + db 27, 27, 16, 30, 40 + + dw $0360, 360, $0400 + db 4, $00 + db 27, 27, 16, 30, 40 + + dw $0400, 400, $0500 + db 5, $00 + db 27, 27, 16, 30, 40 + + dw $0420, 420, $0500 + db 4, $00 + db 27, 27, 16, 30, 40 + + dw $0450, 450, $0500 + db 3, $00 + db 27, 27, 16, 30, 40 + + dw $0500, 500, $0600 + db 20, $00 + db 27, 27, 10, 30, 25 + + dw $0600, 600, $0700 + db 20, $00 + db 27, 18, 10, 30, 16 + + dw $0700, 700, $0800 + db 20, $00 + db 18, 14, 10, 30, 12 + + dw $0800, 800, $0900 + db 20, $00 + db 14, 8, 10, 30, 6 + + dw $0900, 900, $1000 + db 20, $00 + db 14, 8, 8, 17, 6 + + dw $1000, 1000, $1100 + db 20, $00 + db 8, 8, 8, 17, 6 + + dw $1100, 1000, $1200 + db 20, $00 + db 7, 7, 8, 15, 6 + + dw $1200, 1000, $1300 + db 20, $00 + db 6, 6, 8, 15, 6 + +sTGM3SpeedCurveEnd:: + dw $FFFF + +sDEATSpeedCurve:: + dw $0000, 0, $0100 + db 20, $00 + db 18, 14, 12, 30, 12 + + dw $0100, 0, $0200 + db 20, $00 + db 14, 8, 12, 26, 6 + + dw $0200, 0, $0300 + db 20, $00 + db 14, 8, 11, 22, 6 + + dw $0300, 0, $0400 + db 20, $00 + db 8, 8, 10, 18, 6 + + dw $0400, 0, $0500 + db 20, $00 + db 7, 7, 8, 15, 5 + + dw $0500, 0, $0600 + db 20, $00 + db 6, 6, 8, 15, 4 + +sDEATSpeedCurveEnd:: + dw $FFFF + +sSHIRSpeedCurve:: + dw $0000, 0, $0100 + db 20, $00 + db 12, 8, 10, 18, 6 + + dw $0100, 100, $0200 + db 20, $00 + db 12, 7, 8, 18, 5 + + dw $0200, 200, $0300 + db 20, $00 + db 12, 6, 8, 17, 4 + + dw $0300, 300, $0400 + db 20, $00 + db 6, 6, 8, 15, 4 + + dw $0500, 500, $0600 + db 20, $00 + db 6, 5, 6, 13, 3 + + dw $1100, 1100, $1200 + db 20, $00 + db 6, 5, 6, 10, 3 + + dw $1200, 1200, $1300 + db 20, $00 + db 6, 5, 6, 8, 3 + +sSHIRSpeedCurveEnd:: + dw $FFFF + +sTiles:: DB $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF DB $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF DB $00,$00,$00,$00,$00,$00,$00,$00 @@ -532,7 +1138,7 @@ Tiles:: DB $8A,$8A,$8A,$8A,$E4,$E4,$00,$00 DB $00,$00,$62,$62,$94,$94,$88,$88 DB $94,$94,$62,$62,$00,$00,$00,$00 -TilesEnd:: +sTilesEnd:: ENDC diff --git a/src/res/sfx_data.inc b/src/res/sfx_data.inc index bdf9b84..b359c5a 100644 --- a/src/res/sfx_data.inc +++ b/src/res/sfx_data.inc @@ -1,7 +1,11 @@ IF !DEF(SFX_DATA_INC) DEF SFX_DATA_INC EQU 1 -SECTION "SFX Data", ROMX, BANK[2] + +INCLUDE "globals.asm" + + +SECTION "SFX Data", ROMX, BANK[BANK_SFX] ; These sound effects can contain any channel. sSFXPieceI:: db REG_NR12_CH1_VOLEV, $F0, REG_NR22_CH2_VOLEV, $F0, REG_NR11_CH1_LENDT, $BF, REG_NR11_CH1_LENDT, $BF diff --git a/src/res/title_map.inc b/src/res/title_data.inc similarity index 79% rename from src/res/title_map.inc rename to src/res/title_data.inc index 2ff17c3..de405ce 100644 --- a/src/res/title_map.inc +++ b/src/res/title_data.inc @@ -19,8 +19,61 @@ IF !DEF(TITLE_MAP_INC) DEF TITLE_MAP_INC EQU 1 -SECTION "Title Screen Tilemap", ROMX, BANK[1] -TitleScreenTilemap:: +INCLUDE "globals.asm" + + +SECTION "Title Screen Static Data", ROMX, BANK[BANK_TITLE] +sEasterM0:: db $C4, $C6, $C8, $CA, $CC +sEasterM1:: db $C5, $C7, $C9, $CB, $CD + +sEasterC0:: db $CE, $D0, $C8, $CA, $CC, $72, $74, $76, $78, $7A, $D6, $D7 +sEasterC1:: db $CF, $D1, $C9, $CB, $CD, $73, $75, $77, $79, $7B, $01, $01 + +sEasterA0:: db $D2, $D4, $C8, $CA, $CC, $72, $74, $76, $78, $7A, $D6, $D7 +sEasterA1:: db $D3, $D5, $C9, $CB, $CD, $73, $75, $77, $79, $7B, $01, $01 + +sEasterS0:: db $F7, $F9, $C8, $CA, $CC +sEasterS1:: db $F8, $FA, $C9, $CB, $CD + +sOption0:: + db "NORM" + db " INV" + +sOption1:: + db "TGM1" + db "TGM2" + db "TGM3" + db "HELL" + db " NES" + +sOption2:: + db "ARS1" + db "ARS2" + db " NES" + +sOption3:: + db "FIRM" + db "SNIC" + db "HARD" + db "LOCK" + db "NONE" + +sOption4:: + db "DMGT" + db "TGM1" + db "TGM3" + db "DEAT" + db "SHIR" + db "CHIL" + +sOption5:: + db " NO" + db " YES" + +sDisabled:: + db "----" + +sTitleScreenTileMap:: DB $C3,$5B,$4C,$59,$4F,$5A,$5D,$4C,$01,$59 DB $5A,$61,$4C,$01,$01,$FB,$FC,$FD,$FE,$FF DB $01,$01,$01,$01,$01,$01,$01,$01,$01,$01 @@ -89,7 +142,7 @@ TitleScreenTilemap:: DB $01,$01,$01,$01,$01,$01,$01,$01,$01,$01 DB $01,$01,$01,$01,$01,$01,$01,$01,$01,$01 DB $01,$01 -TitleScreenTilemapEnd:: +sTitleScreenTileMapEnd:: ENDC diff --git a/src/rng.asm b/src/rng.asm index c50c297..6ed5116 100644 --- a/src/rng.asm +++ b/src/rng.asm @@ -58,6 +58,8 @@ RNGInit:: ldh [hRNGSeed+3], a ; TGM3 vars + ld b, BANK_GAMEPLAY + rst RSTSwitchBank ld de, sTGM3Bag ld hl, wTGM3Bag ld bc, 35 @@ -66,6 +68,7 @@ RNGInit:: ld hl, wTGM3Droughts ld bc, 7 call UnsafeMemCopy + rst RSTRestoreBank ; Start with a random non-S/Z piece held. : call Next7Piece diff --git a/src/sfx.asm b/src/sfx.asm index 3bc1413..0713e2b 100644 --- a/src/sfx.asm +++ b/src/sfx.asm @@ -421,7 +421,8 @@ SFXPlayNoise:: ; Bail if it's null ret z - ld b, BANK("SFX Data") + ; Bank to sound effects. + ld b, BANK_SFX rst RSTSwitchBank ; Get the register to write to @@ -469,10 +470,10 @@ SFXPlay:: ldh a, [hPlayQueue] cp a, MUSIC_MENU jr nz, :+ - ld b, BANK("Music Data") + ld b, BANK_MUSIC rst RSTSwitchBank jr .play -: ld b, BANK("SFX Data") +: ld b, BANK_SFX rst RSTSwitchBank ; Load the playhead position into HL. diff --git a/src/sprites.asm b/src/sprites.asm index 143988d..f715ec4 100644 --- a/src/sprites.asm +++ b/src/sprites.asm @@ -176,8 +176,18 @@ ApplyNext:: sub a, 7 ; X positions + ld b, a + ldh a, [hGameState] + cp a, STATE_GAMEPLAY_BIG + ld a, b + jr nz, .regular + ld hl, sBigPieceXOffsets + ld de, sBigPieceYOffsets + jr .postoffsets +.regular ld hl, sPieceXOffsets ld de, sPieceYOffsets +.postoffsets cp 0 jr z, .skipoffn .getoffn @@ -296,8 +306,18 @@ ApplyHold:: ; X positions .x + ld b, a + ldh a, [hGameState] + cp a, STATE_GAMEPLAY_BIG + ld a, b + jr nz, .regular + ld hl, sBigPieceXOffsets + ld de, sBigPieceYOffsets + jr .postoffsets +.regular ld hl, sPieceXOffsets ld de, sPieceYOffsets +.postoffsets cp 0 jr z, .skipoffh .getoffh diff --git a/src/state_gameplay.asm b/src/state_gameplay.asm index 6f72bb6..e174984 100644 --- a/src/state_gameplay.asm +++ b/src/state_gameplay.asm @@ -20,6 +20,8 @@ DEF STATE_GAMEPLAY_ASM EQU 1 INCLUDE "globals.asm" +INCLUDE "res/gameplay_data.inc" +INCLUDE "res/gameplay_big_data.inc" DEF MODE_LEADY EQU 0 @@ -47,9 +49,43 @@ hPrePause: ds 1 hRequestedJingle: ds 1 -SECTION "Gameplay Functions", ROM0 - ; Change to game play mode. The event loop will call the event loop and vblank handlers for this mode after this returns. +SECTION "Gameplay Function Trampolines", ROM0 + ; Trampolines to the banked function. SwitchToGameplay:: + ld b, BANK_GAMEPLAY + rst RSTSwitchBank + call SwitchToGameplayB + rst RSTRestoreBank + jp EventLoopPostHandler + + ; Trampolines to the banked function. +SwitchToGameplayBig:: + ld b, BANK_GAMEPLAY_BIG + rst RSTSwitchBank + call SwitchToGameplayBigB + rst RSTRestoreBank + jp EventLoopPostHandler + + ; Banks and jumps to the actual handler. +GamePlayEventLoopHandler:: + ld b, BANK_GAMEPLAY + rst RSTSwitchBank + call GamePlayEventLoopHandlerB + rst RSTRestoreBank + jp EventLoopPostHandler + + ; Banks and jumps to the actual handler. +GamePlayBigEventLoopHandler:: + ld b, BANK_GAMEPLAY_BIG + rst RSTSwitchBank + call GamePlayBigEventLoopHandlerB + rst RSTRestoreBank + jp EventLoopPostHandler + + +SECTION "Gameplay Function Banked", ROMX, BANK[BANK_GAMEPLAY] + ; Change to game play mode. The event loop will call the event loop and vblank handlers for this mode after this returns. +SwitchToGameplayB: ; Turn the screen off if it's on. ldh a, [rLCDC] and LCDCF_ON @@ -59,9 +95,9 @@ SwitchToGameplay:: ldh [rLCDC], a ; Load the gameplay tilemap. -: ld de, GameplayTilemap +: ld de, sGameplayTileMap ld hl, $9800 - ld bc, GameplayTilemapEnd - GameplayTilemap + ld bc, sGameplayTileMapEnd - sGameplayTileMap call UnsafeMemCopy ; Clear OAM. @@ -115,7 +151,7 @@ SwitchToGameplay:: ; Main gameplay event loop. -GamePlayEventLoopHandler:: +GamePlayEventLoopHandlerB:: ; What mode are we in? ld hl, .modejumps ldh a, [hMode] @@ -577,9 +613,7 @@ drawStaticInfo: ld de, hNLevel call ApplyNumbers - call GBCGameplayProcess - - jp EventLoopPostHandler + jp GBCGameplayProcess ; Do the hold action. @@ -657,4 +691,612 @@ DoHold: ret + +SECTION "Gameplay Function Big Banked", ROMX, BANK[BANK_GAMEPLAY_BIG] +; Change to game play mode. The event loop will call the event loop and vblank handlers for this mode after this returns. +SwitchToGameplayBigB: + ; Turn the screen off if it's on. + ldh a, [rLCDC] + and LCDCF_ON + jr z, :+ ; Screen is already off. + wait_vram + xor a, a + ldh [rLCDC], a + + ; Load the gameplay tilemap. +: ld de, sBigGameplayTileMap + ld hl, $9800 + ld bc, sBigGameplayTileMapEnd - sBigGameplayTileMap + call UnsafeMemCopy + + ; Clear OAM. + call ClearOAM + call SetNumberSpritePositions + call ApplyTells + + ; Set up the palettes. + ld a, PALETTE_REGULAR + set_bg_palette + set_obj0_palette + ld a, PALETTE_LIGHTER_1 + set_obj1_palette + + ; Initialize the RNG. + call RNGInit + + ; Initialize the score, level and field. + call ScoreInit + call LevelInit + call BigFieldInit + + ; We don't start with hold spent. + xor a, a + ldh [hHoldSpent], a + + ; Leady mode. + ld a, MODE_LEADY + ldh [hMode], a + ld a, LEADY_TIME + ldh [hModeCounter], a + + ; GBC init + call GBCGameplayInit + + ; Install the event loop handlers. + ld a, STATE_GAMEPLAY_BIG + ldh [hGameState], a + + ; And turn the LCD back on before we start. + ld a, LCDCF_ON | LCDCF_BGON | LCDCF_OBJON | LCDCF_BLK01 + ldh [rLCDC], a + + ; Music end + call SFXKill + + ; Make sure the first game loop starts just like all the future ones. + wait_vblank + wait_vblank_end + ret + + ; Main gameplay event loop. +GamePlayBigEventLoopHandlerB: + ; What mode are we in? + ld hl, .modejumps + ldh a, [hMode] + ld b, 0 + ld c, a + add hl, bc + jp hl + +.modejumps + jp .leadyMode + jp .goMode + jp .postGoMode + jp .prefetchedPieceMode + jp .spawnPieceMode + jp .pieceInMotionMode + jp .delayMode + jp .gameOverMode + jp .preGameOverMode + jp .pauseMode + + + ; Draw "READY" and wait a bit. +.leadyMode + ldh a, [hModeCounter] + cp a, LEADY_TIME + jr nz, :+ + call SFXKill + ld a, SFX_READYGO + call SFXEnqueue + ldh a, [hModeCounter] +: dec a + jr nz, :+ + ld a, MODE_GO + ldh [hMode], a + ld a, GO_TIME +: ldh [hModeCounter], a + ld de, sBigLeady + ld hl, wField+(14*10) + ld bc, 10 + call UnsafeMemCopy + jp .drawStaticInfo + + + ; Draw "GO" and wait a bit. +.goMode + ldh a, [hModeCounter] + dec a + jr nz, :+ + ld a, MODE_POSTGO + ldh [hMode], a + xor a, a +: ldh [hModeCounter], a + ld de, sBigGo + ld hl, wField+(14*10) + ld bc, 10 + call UnsafeMemCopy + jp .drawStaticInfo + + + ; Clear the field, fetch the piece, ready for gameplay. +.postGoMode + ld a, MODE_PREFETCHED_PIECE + ldh [hMode], a + call BigFieldClear + call BigToShadowField + ldh a, [hNextPiece] + ldh [hCurrentPiece], a + call GetNextPiece + jp .drawStaticInfo + + + ; Fetch the next piece. +.prefetchedPieceMode + ; A piece will spawn in the middle, at the top of the screen, not rotated by default. + ld a, $FF + ldh [hRequestedJingle], a + ld a, PIECE_SPAWN_X_BIG + ldh [hCurrentPieceX], a + ld a, PIECE_SPAWN_Y_BIG + ldh [hCurrentPieceY], a + xor a, a ; ROTATION_STATE_DEF + ldh [hCurrentPieceRotationState], a + ldh [hHoldSpent], a + + ; Check if IHS is requested. + ; Apply the hold if so. +.checkIHS + ldh a, [hSelectState] + cp a, 0 + jr z, .loaddefaultjingle + call BigDoHold + jr .postjingle + + ; Enqueue the jingle. +.loaddefaultjingle + ldh a, [hNextPiece] + ldh [hRequestedJingle], a + + ; Check if IRS is requested. + ; Apply the rotation if so. +.checkIRSA + ld a, [wSwapABState] + cp a, 0 + jr z, .lda1 +.ldb1 + ldh a, [hBState] + cp a, 0 + jr z, .checkIRSB + ld a, $FF + ldh [hBState], a + jr .cp1 +.lda1 + ldh a, [hAState] + cp a, 0 + jr z, .checkIRSB + ld a, $FF + ldh [hAState], a +.cp1 + ld a, ROTATION_STATE_CCW + ldh [hCurrentPieceRotationState], a + ldh a, [hNextPiece] + ld b, a + ld a, SFX_IRS + or a, b + ldh [hRequestedJingle], a + jr .postjingle + +.checkIRSB + ld a, [wSwapABState] + cp a, 0 + jr z, .ldb2 +.lda2 + ldh a, [hAState] + cp a, 0 + jr z, .postjingle + ld a, $FF + ldh [hAState], a + jr .cp2 +.ldb2 + ldh a, [hBState] + cp a, 0 + jr z, .postjingle + ld a, $FF + ldh [hBState], a +.cp2 + ld a, ROTATION_STATE_CW + ldh [hCurrentPieceRotationState], a + ldh a, [hNextPiece] + ld b, a + ld a, SFX_IRS + or a, b + ldh [hRequestedJingle], a + jr .postjingle + +.postjingle + ld a, MODE_SPAWN_PIECE + ldh [hMode], a + ; State falls through to the next. + + + ; Spawn the piece. +.spawnPieceMode + call BigTrySpawnPiece + cp a, $FF + jr z, :+ + ld a, MODE_PRE_GAME_OVER + ldh [hMode], a + jp .drawStaticInfo +: ld a, MODE_PIECE_IN_MOTION + ldh [hMode], a + + ; Play the next jingle... maybe! + ldh a, [hHoldSpent] + cp a, $FF + jr z, .pieceInMotionMode + ldh a, [hRequestedJingle] + cp a, $FF + jr z, .pieceInMotionMode + call SFXEnqueue + + + ; This mode lasts for as long as the piece is in motion. + ; Field will let us know when it has locked in place. +.pieceInMotionMode + ldh a, [hStartState] + cp a, 1 + jr nz, :+ + call BigToBackupField + ldh a, [hMode] + ldh [hPrePause], a + ld a, MODE_PAUSED + ldh [hMode], a + jp .drawStaticInfo + +: call BigFieldProcess + + ; Do we hold? + ldh a, [hSelectState] + cp a, 1 + jr nz, :+ + ldh a, [hHoldSpent] + cp a, $FF + jr z, :+ + ; Reset position and rotation. + ld a, PIECE_SPAWN_X_BIG + ldh [hCurrentPieceX], a + ld a, PIECE_SPAWN_Y_BIG + ldh [hCurrentPieceY], a + xor a, a ; ROTATION_STATE_DEF + ldh [hCurrentPieceRotationState], a + call BigDoHold + ld a, MODE_SPAWN_PIECE + ldh [hMode], a + + ; Do we go into delay state? +: ldh a, [hCurrentLockDelayRemaining] + cp a, 0 + jr nz, :+ + ld a, MODE_DELAY + ldh [hMode], a + ; No fall through this time. + +: jp .drawStaticInfo + + +.delayMode + ldh a, [hStartState] + cp a, 1 + jr nz, :+ + call BigToBackupField + ldh a, [hMode] + ldh [hPrePause], a + ld a, MODE_PAUSED + ldh [hMode], a + jp .drawStaticInfo + +: call BigFieldDelay + + ldh a, [hRemainingDelay] + cp a, 0 + jr nz, :+ + ld a, MODE_PREFETCHED_PIECE + ldh [hMode], a + +: jp .drawStaticInfo + + +.preGameOverMode + ; Spawn the failed piece. + call BigForceSpawnPiece + + ; Draw the field in grey. + ; Yes. This really unrolls the loop that many times. + ld hl, wField+(4*10) + REPT 60 + ld a, [hl] + cp a, TILE_FIELD_EMPTY + jr nz, .notempty1\@ + ld a, GAME_OVER_OTHER+1 + ld [hl+], a + jr .skip1\@ +.notempty1\@ + ld a, GAME_OVER_OTHER + ld [hl+], a +.skip1\@ + ENDR + DEF off = 0 + REPT 10 + ld a, [hl] + cp a, TILE_FIELD_EMPTY + jr nz, .notempty2\@ + ld a, GAME_OVER_R10+10+off + ld [hl+], a + jr .skip2\@ +.notempty2\@ + ld a, GAME_OVER_R10+off + ld [hl+], a +.skip2\@ + DEF off += 1 + ENDR + REPT 10 + ld a, [hl] + cp a, TILE_FIELD_EMPTY + jr nz, .notempty3\@ + ld a, GAME_OVER_OTHER+1 + ld [hl+], a + jr .skip3\@ +.notempty3\@ + ld a, GAME_OVER_OTHER + ld [hl+], a +.skip3\@ + ENDR + DEF off = 0 + REPT 10 + ld a, [hl] + cp a, TILE_FIELD_EMPTY + jr nz, .notempty4\@ + ld a, GAME_OVER_R12+10+off + ld [hl+], a + jr .skip4\@ +.notempty4\@ + ld a, GAME_OVER_R12+off + ld [hl+], a +.skip4\@ + DEF off += 1 + ENDR + REPT 10 + ld a, [hl] + cp a, TILE_FIELD_EMPTY + jr nz, .notempty5\@ + ld a, GAME_OVER_OTHER+1 + ld [hl+], a + jr .skip5\@ +.notempty5\@ + ld a, GAME_OVER_OTHER + ld [hl+], a +.skip5\@ + ENDR + DEF off = 0 + REPT 10 + ld a, [hl] + cp a, TILE_FIELD_EMPTY + jr nz, .notempty6\@ + ld a, GAME_OVER_R14+10+off + ld [hl+], a + jr .skip6\@ +.notempty6\@ + ld a, GAME_OVER_R14+off + ld [hl+], a +.skip6\@ + DEF off += 1 + ENDR + REPT 90 + ld a, [hl] + cp a, TILE_FIELD_EMPTY + jr nz, .notempty7\@ + ld a, GAME_OVER_OTHER+1 + ld [hl+], a + jr .skip7\@ +.notempty7\@ + ld a, GAME_OVER_OTHER + ld [hl+], a +.skip7\@ + ENDR + + ld a, MODE_GAME_OVER + ldh [hMode], a + + +.gameOverMode + ; Retry? + ldh a, [hAState] + cp a, 1 + jr nz, :+ + call RNGInit + call ScoreInit + call LevelInit + call BigFieldInit + xor a, a + ldh [hHoldSpent], a + ld a, MODE_LEADY + ldh [hMode], a + ld a, LEADY_TIME + ldh [hModeCounter], a + jp .drawStaticInfo + + ; Quit +: ldh a, [hBState] + cp a, 1 + jp nz, .drawStaticInfo + call SwitchToTitle + jp EventLoopPostHandler + + +.pauseMode + ; Quick reset. + ldh a, [hAState] + cp a, 0 + jr z, :+ + ldh a, [hBState] + cp a, 0 + jr z, :+ + ldh a, [hSelectState] + cp a, 0 + jr z, :+ + call SwitchToTitle + jp EventLoopPostHandler + + ; Unpause +: ldh a, [hStartState] + cp a, 1 + jr nz, :+ + call BigFromBackupField + ldh a, [hPrePause] + ldh [hMode], a + jr .drawStaticInfo + + ; Draw PAUSE all over the field. +: ld de, sBigPause + ld hl, wField+(4*10) + ld bc, 20 + call UnsafeMemCopy + ld de, sBigPause + ld hl, wField+(6*10) + ld bc, 20 + call UnsafeMemCopy + ld de, sBigPause + ld hl, wField+(8*10) + ld bc, 20 + call UnsafeMemCopy + ld de, sBigPause + ld hl, wField+(10*10) + ld bc, 20 + call UnsafeMemCopy + ld de, sBigPause + ld hl, wField+(12*10) + ld bc, 20 + call UnsafeMemCopy + ld de, sBigPause + ld hl, wField+(14*10) + ld bc, 20 + call UnsafeMemCopy + ld de, sBigPause + ld hl, wField+(16*10) + ld bc, 20 + call UnsafeMemCopy + ld de, sBigPause + ld hl, wField+(18*10) + ld bc, 20 + call UnsafeMemCopy + ld de, sBigPause + ld hl, wField+(20*10) + ld bc, 20 + call UnsafeMemCopy + ld de, sBigPause + ld hl, wField+(22*10) + ld bc, 20 + call UnsafeMemCopy + jr .drawStaticInfo + + + ; Always draw the score, level, next piece, and held piece. +.drawStaticInfo +: ldh a, [hNextPiece] + call ApplyNext + + ldh a, [hHeldPiece] + call ApplyHold + + ld hl, wSPRScore1 + ld de, hScore + call ApplyNumbers + + ld hl, wSPRCLevel1 + ld de, hCLevel + call ApplyNumbers + + ld hl, wSPRNLevel1 + ld de, hNLevel + call ApplyNumbers + + jp GBCGameplayProcess + + + ; Do the hold action. +BigDoHold: + ; Mark hold as spent. + ld a, $FF + ldh [hHoldSpent], a + + ; Check if IRS is requested. + ; Apply the rotation if so. +.checkIRSHA + ld a, [wSwapABState] + cp a, 0 + jr z, .lda3 +.ldb3 + ldh a, [hBState] + cp a, 0 + jr z, .checkIRSHB + ld a, $FF + ldh [hBState], a + jr .cp3 +.lda3 + ldh a, [hAState] + cp a, 0 + jr z, .checkIRSHB + ld a, $FF + ldh [hAState], a +.cp3 + ld a, ROTATION_STATE_CCW + ldh [hCurrentPieceRotationState], a + call SFXKill + ld a, SFX_IRS | SFX_IHS + call SFXEnqueue + jr .doHoldOperation + +.checkIRSHB + ld a, [wSwapABState] + cp a, 0 + jr z, .ldb4 +.lda4 + ldh a, [hAState] + cp a, 0 + jr z, .noRotation + ld a, $FF + ldh [hAState], a + jr .cp4 +.ldb4 + ldh a, [hBState] + cp a, 0 + jr z, .noRotation + ld a, $FF + ldh [hBState], a +.cp4 + ld a, ROTATION_STATE_CW + ldh [hCurrentPieceRotationState], a + call SFXKill + ld a, SFX_IRS | SFX_IHS + call SFXEnqueue + jr .doHoldOperation + +.noRotation + call SFXKill + ld a, SFX_IHS + call SFXEnqueue + xor a, a ; ROTATION_STATE_DEF + ldh [hCurrentPieceRotationState], a + +.doHoldOperation + ldh a, [hHeldPiece] + ld b, a + ldh a, [hCurrentPiece] + ldh [hHeldPiece], a + ld a, b + ldh [hCurrentPiece], a + ret + + ENDC diff --git a/src/state_title.asm b/src/state_title.asm index 4b2aa02..3cba7b8 100644 --- a/src/state_title.asm +++ b/src/state_title.asm @@ -20,15 +20,99 @@ DEF STATE_TITLE_ASM EQU 1 INCLUDE "globals.asm" +INCLUDE "res/title_data.inc" SECTION "Title Variables", WRAM0 wSelected:: ds 1 -SECTION "Title Functions", ROM0 - ; Change to title mode. The event loop will call the event loop and vblank handlers for this mode after this returns. +SECTION "Title Function Trampolines", ROM0 + ; Trampolines to the banked function. SwitchToTitle:: + ld b, BANK_TITLE + rst RSTSwitchBank + call SwitchToTitleB + rst RSTRestoreBank + jp EventLoopPostHandler + + ; Banks and jumps to the actual handler. +TitleEventLoopHandler:: + ld b, BANK_TITLE + rst RSTSwitchBank + call TitleEventLoopHandlerB + rst RSTRestoreBank + jp EventLoopPostHandler + + ; Banks and jumps to the actual handler. +TitleVBlankHandler:: + ld b, BANK_TITLE + rst RSTSwitchBank + call TitleVBlankHandlerB + rst RSTRestoreBank + jp EventLoop + +DrawOption6: + ld b, BANK_OTHER + rst RSTSwitchBank + + ldh a, [hStartSpeed] + ld l, a + ldh a, [hStartSpeed+1] + ld h, a + ld a, [hl] + swap a + and a, $0F + ld b, a + ld a, TILE_0 + add a, b + ld hl, TITLE_OPTION_6+TITLE_OPTION_OFFSET+2 + ld [hl], a + + ldh a, [hStartSpeed] + ld l, a + ldh a, [hStartSpeed+1] + ld h, a + ld a, [hl] + and a, $0F + ld b, a + ld a, TILE_0 + add a, b + ld hl, TITLE_OPTION_6+TITLE_OPTION_OFFSET+3 + ld [hl], a + + ldh a, [hStartSpeed] + ld l, a + ldh a, [hStartSpeed+1] + ld h, a + inc hl + ld a, [hl] + swap a + and a, $0F + ld b, a + ld a, TILE_0 + add a, b + ld hl, TITLE_OPTION_6+TITLE_OPTION_OFFSET+0 + ld [hl], a + + ldh a, [hStartSpeed] + ld l, a + ldh a, [hStartSpeed+1] + ld h, a + inc hl + ld a, [hl] + and a, $0F + ld b, a + ld a, TILE_0 + add a, b + ld hl, TITLE_OPTION_6+TITLE_OPTION_OFFSET+1 + ld [hl], a + + jp RSTRestoreBank + + +SECTION "Title Functions Banked", ROMX, BANK[BANK_TITLE] +SwitchToTitleB: ; Turn the screen off if it's on. ldh a, [rLCDC] and LCDCF_ON @@ -38,9 +122,9 @@ SwitchToTitle:: ldh [rLCDC], a ; Load the gameplay tilemap. -: ld de, TitleScreenTilemap +: ld de, sTitleScreenTileMap ld hl, $9800 - ld bc, TitleScreenTilemapEnd - TitleScreenTilemap + ld bc, sTitleScreenTileMapEnd - sTitleScreenTileMap call UnsafeMemCopy ; Title screen easter egg. @@ -136,9 +220,8 @@ SwitchToTitle:: wait_vblank_end ret - ; Handles title screen input. -TitleEventLoopHandler:: +TitleEventLoopHandlerB: call GBCTitleProcess ; Start game? @@ -152,8 +235,10 @@ TitleEventLoopHandler:: or a, c cp a, 1 jr nz, .up - call SwitchToGameplay - jp EventLoopPostHandler + ldh a, [hSelectState] + cp a, 0 + jp z, SwitchToGameplay + jp SwitchToGameplayBig ; Change menu selection? .up @@ -165,10 +250,10 @@ TitleEventLoopHandler:: jr z, :+ dec a ld [wSelected], a - jp EventLoopPostHandler + ret : ld a, TITLE_OPTIONS-1 ld [wSelected], a - jp EventLoopPostHandler + ret .down ldh a, [hDownState] @@ -179,10 +264,10 @@ TitleEventLoopHandler:: jr z, :+ inc a ld [wSelected], a - jp EventLoopPostHandler + ret : xor a, a ld [wSelected], a - jp EventLoopPostHandler + ret .left ldh a, [hLeftState] @@ -194,7 +279,8 @@ TitleEventLoopHandler:: and 3 cp a, 3 jr nz, .right - jp DecrementOption + call DecrementOption + ret .right ldh a, [hRightState] @@ -206,10 +292,10 @@ TitleEventLoopHandler:: and 3 cp a, 3 jr nz, .done - jp IncrementOption + call IncrementOption .done - jp EventLoopPostHandler + ret ; Decrements the currently selected option. @@ -224,11 +310,11 @@ DecrementOption: dec a ld [wSwapABState], a ld [rSwapABState], a - jp EventLoopPostHandler + ret : ld a, BUTTON_MODE_COUNT-1 ld [wSwapABState], a ld [rSwapABState], a - jp EventLoopPostHandler + ret .opt1 cp a, 1 @@ -239,11 +325,11 @@ DecrementOption: dec a ld [wRNGModeState], a ld [rRNGModeState], a - jp EventLoopPostHandler + ret : ld a, RNG_MODE_COUNT-1 ld [wRNGModeState], a ld [rRNGModeState], a - jp EventLoopPostHandler + ret .opt2 cp a, 2 @@ -254,11 +340,11 @@ DecrementOption: dec a ld [wRotModeState], a ld [rRotModeState], a - jp EventLoopPostHandler + ret : ld a, ROT_MODE_COUNT-1 ld [wRotModeState], a ld [rRotModeState], a - jp EventLoopPostHandler + ret .opt3 cp a, 3 @@ -269,11 +355,11 @@ DecrementOption: dec a ld [wDropModeState], a ld [rDropModeState], a - jp EventLoopPostHandler + ret : ld a, DROP_MODE_COUNT-1 ld [wDropModeState], a ld [rDropModeState], a - jp EventLoopPostHandler + ret .opt4 cp a, 4 @@ -285,12 +371,12 @@ DecrementOption: ld [wSpeedCurveState], a ld [rSpeedCurveState], a call InitSpeedCurve - jp EventLoopPostHandler + ret : ld a, SCURVE_COUNT-1 ld [wSpeedCurveState], a ld [rSpeedCurveState], a call InitSpeedCurve - jp EventLoopPostHandler + ret .opt5 cp a, 5 @@ -301,15 +387,14 @@ DecrementOption: dec a ld [wAlways20GState], a ld [rAlways20GState], a - jp EventLoopPostHandler + ret : ld a, HIG_MODE_COUNT-1 ld [wAlways20GState], a ld [rAlways20GState], a - jp EventLoopPostHandler + ret .opt6 jr DecrementLevel - jp EventLoopPostHandler ; Decrements start level. @@ -327,7 +412,7 @@ DecrementLevel: ld a, h ldh [hStartSpeed+1], a ld [rSelectedStartLevel+1], a - jp CheckLevelRange + ret ; Increments the selected option. @@ -342,11 +427,11 @@ IncrementOption: inc a ld [wSwapABState], a ld [rSwapABState], a - jp EventLoopPostHandler + ret : xor a, a ld [wSwapABState], a ld [rSwapABState], a - jp EventLoopPostHandler + ret .opt1 cp a, 1 @@ -357,11 +442,11 @@ IncrementOption: inc a ld [wRNGModeState], a ld [rRNGModeState], a - jp EventLoopPostHandler + ret : xor a, a ld [wRNGModeState], a ld [rRNGModeState], a - jp EventLoopPostHandler + ret .opt2 cp a, 2 @@ -372,11 +457,11 @@ IncrementOption: inc a ld [wRotModeState], a ld [rRotModeState], a - jp EventLoopPostHandler + ret : xor a, a ld [wRotModeState], a ld [rRotModeState], a - jp EventLoopPostHandler + ret .opt3 cp a, 3 @@ -387,11 +472,11 @@ IncrementOption: inc a ld [wDropModeState], a ld [rDropModeState], a - jp EventLoopPostHandler + ret : xor a, a ld [wDropModeState], a ld [rDropModeState], a - jp EventLoopPostHandler + ret .opt4 cp a, 4 @@ -403,12 +488,12 @@ IncrementOption: ld [wSpeedCurveState], a ld [rSpeedCurveState], a call InitSpeedCurve - jp EventLoopPostHandler + ret : xor a, a ld [wSpeedCurveState], a ld [rSpeedCurveState], a call InitSpeedCurve - jp EventLoopPostHandler + ret .opt5 cp a, 5 @@ -419,15 +504,14 @@ IncrementOption: inc a ld [wAlways20GState], a ld [rAlways20GState], a - jp EventLoopPostHandler + ret : xor a, a ld [wAlways20GState], a ld [rAlways20GState], a - jp EventLoopPostHandler + ret .opt6 jr IncrementLevel - jp EventLoopPostHandler ; Increments start level. @@ -557,11 +641,11 @@ CheckLevelRange: ldh [hStartSpeed+1], a .notatstart - jp EventLoopPostHandler + ret ; Handles the display of the menu. -TitleVBlankHandler:: +TitleVBlankHandlerB: call ToATTR ld a, TILE_UNSELECTED @@ -707,58 +791,7 @@ TitleVBlankHandler:: ; Draw option 6. .opt6 - ldh a, [hStartSpeed] - ld l, a - ldh a, [hStartSpeed+1] - ld h, a - ld a, [hl] - swap a - and a, $0F - ld b, a - ld a, TILE_0 - add a, b - ld hl, TITLE_OPTION_6+TITLE_OPTION_OFFSET+2 - ld [hl], a - - ldh a, [hStartSpeed] - ld l, a - ldh a, [hStartSpeed+1] - ld h, a - ld a, [hl] - and a, $0F - ld b, a - ld a, TILE_0 - add a, b - ld hl, TITLE_OPTION_6+TITLE_OPTION_OFFSET+3 - ld [hl], a - - ldh a, [hStartSpeed] - ld l, a - ldh a, [hStartSpeed+1] - ld h, a - inc hl - ld a, [hl] - swap a - and a, $0F - ld b, a - ld a, TILE_0 - add a, b - ld hl, TITLE_OPTION_6+TITLE_OPTION_OFFSET+0 - ld [hl], a - - ldh a, [hStartSpeed] - ld l, a - ldh a, [hStartSpeed+1] - ld h, a - inc hl - ld a, [hl] - and a, $0F - ld b, a - ld a, TILE_0 - add a, b - ld hl, TITLE_OPTION_6+TITLE_OPTION_OFFSET+1 - ld [hl], a - jp EventLoop + jp DrawOption6 ENDC