From a027c98cd9b0acb7bb1da219f1d0a87910d6ea27 Mon Sep 17 00:00:00 2001 From: cawtds <38510667+cawtds@users.noreply.github.com> Date: Mon, 6 Jan 2025 23:08:33 +0100 Subject: [PATCH] update player controller --- charmap.txt | 1 + graphics/types/battle_icons1.pal | 15 + graphics/types/battle_icons1.png | Bin 0 -> 354 bytes graphics/types/battle_icons2.pal | 15 + graphics/types/battle_icons2.png | Bin 0 -> 372 bytes graphics/types/bug.png | Bin 0 -> 204 bytes graphics/types/contest_beauty.png | Bin 0 -> 224 bytes graphics/types/contest_cool.png | Bin 0 -> 213 bytes graphics/types/contest_cute.png | Bin 0 -> 214 bytes graphics/types/contest_smart.png | Bin 0 -> 223 bytes graphics/types/contest_tough.png | Bin 0 -> 217 bytes graphics/types/dark.png | Bin 0 -> 219 bytes graphics/types/dragon.png | Bin 0 -> 223 bytes graphics/types/electric.png | Bin 0 -> 227 bytes graphics/types/fairy.png | Bin 0 -> 324 bytes graphics/types/fight.png | Bin 0 -> 227 bytes graphics/types/fire.png | Bin 0 -> 216 bytes graphics/types/flying.png | Bin 0 -> 229 bytes graphics/types/ghost.png | Bin 0 -> 233 bytes graphics/types/grass.png | Bin 0 -> 230 bytes graphics/types/ground.png | Bin 0 -> 231 bytes graphics/types/ice.png | Bin 0 -> 208 bytes graphics/types/move_types_1.pal | 19 + graphics/types/move_types_2.pal | 19 + graphics/types/move_types_3.pal | 19 + graphics/types/mystery.png | Bin 0 -> 215 bytes graphics/types/none.png | Bin 0 -> 215 bytes graphics/types/normal.png | Bin 0 -> 221 bytes graphics/types/poison.png | Bin 0 -> 236 bytes graphics/types/psychic.png | Bin 0 -> 234 bytes graphics/types/rock.png | Bin 0 -> 220 bytes graphics/types/steel.png | Bin 0 -> 221 bytes graphics/types/stellar.png | Bin 0 -> 5861 bytes graphics/types/water.png | Bin 0 -> 218 bytes include/battle_controllers.h | 14 +- include/battle_main.h | 1 + include/graphics.h | 5 + include/item.h | 5 +- include/item_menu.h | 2 +- include/line_break.h | 33 + include/party_menu.h | 2 +- include/pokemon.h | 1 + include/type_icons.h | 18 + src/battle_controller_link_opponent.c | 2 +- src/battle_controller_link_partner.c | 2 +- src/battle_controller_oak_old_man.c | 4 +- src/battle_controller_opponent.c | 2 +- src/battle_controller_player.c | 711 ++++++++++++++-------- src/battle_controller_player_partner.c | 2 +- src/battle_controller_pokedude.c | 2 +- src/battle_controller_recorded_opponent.c | 2 +- src/battle_controller_recorded_player.c | 2 +- src/battle_controller_safari.c | 2 +- src/data/items.h | 5 + src/graphics.c | 5 + src/item.c | 5 + src/item_menu.c | 10 +- src/line_break.c | 281 +++++++++ src/party_menu.c | 12 +- src/pokemon.c | 8 + src/tm_case.c | 2 +- src/type_icons.c | 552 +++++++++++++++++ 62 files changed, 1508 insertions(+), 272 deletions(-) create mode 100644 graphics/types/battle_icons1.pal create mode 100644 graphics/types/battle_icons1.png create mode 100644 graphics/types/battle_icons2.pal create mode 100644 graphics/types/battle_icons2.png create mode 100644 graphics/types/bug.png create mode 100644 graphics/types/contest_beauty.png create mode 100644 graphics/types/contest_cool.png create mode 100644 graphics/types/contest_cute.png create mode 100644 graphics/types/contest_smart.png create mode 100644 graphics/types/contest_tough.png create mode 100644 graphics/types/dark.png create mode 100644 graphics/types/dragon.png create mode 100644 graphics/types/electric.png create mode 100644 graphics/types/fairy.png create mode 100644 graphics/types/fight.png create mode 100644 graphics/types/fire.png create mode 100644 graphics/types/flying.png create mode 100644 graphics/types/ghost.png create mode 100644 graphics/types/grass.png create mode 100644 graphics/types/ground.png create mode 100644 graphics/types/ice.png create mode 100644 graphics/types/move_types_1.pal create mode 100644 graphics/types/move_types_2.pal create mode 100644 graphics/types/move_types_3.pal create mode 100644 graphics/types/mystery.png create mode 100644 graphics/types/none.png create mode 100644 graphics/types/normal.png create mode 100644 graphics/types/poison.png create mode 100644 graphics/types/psychic.png create mode 100644 graphics/types/rock.png create mode 100644 graphics/types/steel.png create mode 100644 graphics/types/stellar.png create mode 100644 graphics/types/water.png create mode 100644 include/line_break.h create mode 100644 include/type_icons.h create mode 100644 src/line_break.c create mode 100644 src/type_icons.c diff --git a/charmap.txt b/charmap.txt index 1c0367e7c..71e7a3aec 100644 --- a/charmap.txt +++ b/charmap.txt @@ -45,6 +45,7 @@ SUPER_ER = 2C LV = 34 '=' = 35 ';' = 36 +V_D_ARROW = 38 '¿' = 51 '¡' = 52 PK = 53 diff --git a/graphics/types/battle_icons1.pal b/graphics/types/battle_icons1.pal new file mode 100644 index 000000000..972dfd02a --- /dev/null +++ b/graphics/types/battle_icons1.pal @@ -0,0 +1,15 @@ +JASC-PAL +0100 +12 +120 152 128 +49 49 49 +255 255 255 +164 82 57 +172 189 32 +184 160 90 +213 180 90 +98 98 180 +180 90 164 +172 164 148 +172 172 197 +152 172 246 diff --git a/graphics/types/battle_icons1.png b/graphics/types/battle_icons1.png new file mode 100644 index 0000000000000000000000000000000000000000..c47b07f1171a309220fe403e7ed17b544dce61b2 GIT binary patch literal 354 zcmeAS@N?(olHy`uVBq!ia0vp^96-E)gBeJ&&AV#_q!^2X+?^QKos)S9f0+Ah^6AeDr5qSvozYT0U{!x$`s_r#^L~O? zW`;68@OaH&_v*B4*$Iv5&s|*w(yuXeG{_swkC``BYN3?2$6ECzR%;a2T$7x3-fpK2 zw`)VQ%vH99|B6*k<|Py^@Yg%OV literal 0 HcmV?d00001 diff --git a/graphics/types/battle_icons2.pal b/graphics/types/battle_icons2.pal new file mode 100644 index 000000000..bddf49ad9 --- /dev/null +++ b/graphics/types/battle_icons2.pal @@ -0,0 +1,15 @@ +JASC-PAL +0100 +12 +120 152 128 +49 49 49 +255 255 255 +240 82 48 +120 200 80 +248 197 49 +80 120 136 +123 98 230 +248 115 164 +57 156 255 +90 205 230 +246 180 246 diff --git a/graphics/types/battle_icons2.png b/graphics/types/battle_icons2.png new file mode 100644 index 0000000000000000000000000000000000000000..44aa4f89547f478a290231112e03bd84f350a08e GIT binary patch literal 372 zcmeAS@N?(olHy`uVBq!ia0vp^96-E)gBeJ&&AV#_q!^2X+?^QKos)S9%Lb!>Ej>Uc_m{DK*PN@0NW zNPrqp{F$eVV~9oX*+~b5njCnX4YfG_{QrMTEphh)*Y7EA%lmU~y*2PL&)#)cl~Kxp z!IERaWLAYID;xWC%9j?$eC*AdA1ZV};4{aCCrl!1c&{l7|6f;|Grv9M#7Uhdjs6y$ zGhY@M>?)gc>-e`yFYyM(vV;e(KVEcyW|W>Ye|kyVjwuaK7z8*qp3hmScV6;#`s8yB zY&Rw}_zHyodQqSi96QT++j%9)ga_;kyc`1Lo)&ZKm)+^(*_kc*uP*H+Qv<_C?~dro zBk$AS%{>(|{aMf3&E9MR8$NG+Qmi1Wad_8vzv-E=vp%1;t6yfmv6;^}dj^LH(8mm( Lu6{1-oD!M!|EvlSxcrApWUL6l>}7QTwVPC|9^8=`D;KbP0l+XkK`G`ub literal 0 HcmV?d00001 diff --git a/graphics/types/contest_beauty.png b/graphics/types/contest_beauty.png new file mode 100644 index 0000000000000000000000000000000000000000..b52fe3ca47a7eeab2f51191b2899c95ab3b3395e GIT binary patch literal 224 zcmeAS@N?(olHy`uVBq!ia0vp^3P3Et!VDx^^%AmxltF+`h%1mjd-VUBDc_2t=44Iz zHs{{GtN-75SqA-&>i&Op%ao*~C5}t(HSe7=Wl3{&@&Et-cbFcu1?tK6ba4!kn3~$Z zk?*hqkIUYav*8m(%QODlA3pos-s1YgiCQO^bqXIv9?MDn?X&02T{XUJ?rTAD(R@pn zaEUmEH8$-(z`Qu7zeM}QANJ`@3oV+uA4zzHRHuL9J8{lRp=#|n8J^F3Z=dV@{PDMb Y;MP{&cNw)hKx-L1UHx3vIVCg!0Nf>D6aWAK literal 0 HcmV?d00001 diff --git a/graphics/types/contest_cool.png b/graphics/types/contest_cool.png new file mode 100644 index 0000000000000000000000000000000000000000..f0ca72323009e51795bd13a6f23e53f22d291b8b GIT binary patch literal 213 zcmeAS@N?(olHy`uVBq!ia0vp^3P3Et!VDx^^%AmxltF+`h%1o(f7S3?v*A%g&1Xlm z_AasP&NBS}zk1D@>U;N&7Dst`Ia==Bdljgxxw`oO|NkCw+!8=ef~SjPh{V*^^9MN_ z40u`}c5ttdmU!R!;@|0%hLq-TQLR~3AM9CvIVz}Ty*_ruL3nxEqh;J8F+K~+n3ZIn zS2!GEsAYUDpRrl!!=pxx6$>t9QU(A3 literal 0 HcmV?d00001 diff --git a/graphics/types/contest_cute.png b/graphics/types/contest_cute.png new file mode 100644 index 0000000000000000000000000000000000000000..eeb86c431be05f1289f2322bd99691e3ee7992db GIT binary patch literal 214 zcmeAS@N?(olHy`uVBq!ia0vp^3P3Et!VDx^^%AmxltF+`h%1mjd-VUBDc_2t=44Iz zHs{{GtN-75SqA-&>i&Op%ao*~C5}t(HSe7=Wl3{&@&Et-cbFcu1?oxkba4!kn3~#e z$k*V&!IJs)X+~j0R(fWQr0Ac7k3Kw{&r5u6=m_-sxF;^2%gz1qfHun_4wo0|57rel zFU^Skvb3k+sc56)fuIutg%J_`Dc;6rxmh=Z_;;K Mr>mdKI;Vst01C=i{r~^~ literal 0 HcmV?d00001 diff --git a/graphics/types/contest_smart.png b/graphics/types/contest_smart.png new file mode 100644 index 0000000000000000000000000000000000000000..7659cc4f291c987aa09c8c80dbb435e32da30535 GIT binary patch literal 223 zcmeAS@N?(olHy`uVBq!ia0vp^3P3Et!VDx^^%AmxltF+`h%1mj`ah}qY*19QW3lCb zFUu)=m;7I|SK;0>!|EvlSxcrApWUL6l>}7QTwVPC|9^8=`D;KU;N&7Dst`Ia==Bdljgxxw`oO|NkCw+!8=eil>WXh{V*?{*Am1 z4jitTJ*KT2t-~%IvfWY?C)U7wKscG_N7wuh94ri4k7wR^c;E<6#EV0{JwG}Hw3eL` z6MXdU9@9#W@bgZ;bA-OU4GU;N&7Dst`Ia==Bdljgxxw`oO|NkCw+!8=enx~6nh{V*?en-9r z1s;}alaH6>di4!W1<(IlEO&}uOj~G!1t+ghL{vvr+fBoZhx(@Ocr3^{M@5mN%b|x$ z+n42bbmt|PCf5VLPfmPy>(j`Z9)86=w0z^zMXo;%`qkgd*dufN{daSYrOPZ7g)~9V OXYh3Ob6Mw<&;$U(mr!|EvlSxcrApWUL6l>}7QTwVPC|9^8=`D;KBD<6WO`n?!TA6JF87~ T$xb~ppsfs^u6{1-oD!MU;N&7Dst`Ia==Bdljgxxw`oO|NkCw+!8=eo~Mgrh{V*?ervu4 z1p$}shSx-n@vU3Z`u)PG&*xaDNx!lBp&2HZA`~}+x8slG`2%|HTxTZLc@(WrXgGaG z!$(shE4Num^v*)oFpW7sT@FTPntu`7m=u3h`}po(=Faj9PF(2umzZtiZ&U>cv7h@-A}f$@5a1I6GVbir|7)gvD~_6zHRao!d-tyXf9GWx^gpWm|IsZ|l9HA< zF1gpdcgmC{&DF*K|Nq}%de9cAhcU_9-G!lpRn`N@$?$Y>45^rt+JBI@!GObcr_}WC zS&sznl`N{|^g;(q*d+2d1&6s;k!3 zmWe;D>Xeo|TC~LJ$ZowOOS*kJjg5t$YaL2_ZgJm0p85Ct-|S|ScKG!D$~y$Klfl!~ K&t;ucLK6T1m2+kQ literal 0 HcmV?d00001 diff --git a/graphics/types/fight.png b/graphics/types/fight.png new file mode 100644 index 0000000000000000000000000000000000000000..95ff5a9281fbcf044378cad52b4cec504b3278a5 GIT binary patch literal 227 zcmeAS@N?(olHy`uVBq!ia0vp^3P3Et!VDx^^%AmxltF+`h%1o(f7S3?v*A%g&1Xlm z_AasP&NBS}zk1D@>U;N&7Dst`Ia==Bdljgxxw`oO|NkCw+!8=eo~Mgrh{V*?{w@$d%)&aF8=PJhG%j1V!lTag zpw1elC5L<`OrQ2RwmU#lyqZN^QEsapyJYv8e1mz7f4Bc- Xy_t}fF3EorU;N&7Dst`Ia==Bdljgxxw`oO|NkCw+!8=evZsq0uvPhP+h`Gl^I3W_mBngYE(*$E4KnXNxIbjO?tx1GfxvW&|_ zu{1iYQuySf&M!;2{GXT$>V>(^pVY#r@i&Op%ao*~C5}t(HSe7=Wl3{&@&Et-cbFcu1?nmAba4!kn3{V2 zB5#8N56cB9@c{D;|C%rUo&J*HI&V~TQqqCn3J(u1budYb+O4ytaQfT|j*YvyX77v? zozCFQ+`9UZ&3$L?7MiwA_z^}XJfCDVfCpsWl5JcjFhUREcc!|EvlSxcrApWUL6l>}7QTwVPC|9^8=`D;KwA0~qr)qP`8Q0&@2E?5s@Jo)7@v;2@#4WDPn(Oa4o<9dQaN@= z&s(G!Au{3K198WSjF#4_&J_!7C;qE;k%;$v=#iM%CvtYyt%_SZ=|0`DTk9|W(%kcZ b-G0UeU*jtTL#(a>Eobm_^>bP0l+XkK9Ryj= literal 0 HcmV?d00001 diff --git a/graphics/types/ground.png b/graphics/types/ground.png new file mode 100644 index 0000000000000000000000000000000000000000..d3f6787b29a0a580e5e184a1f3ce4522ac832ac0 GIT binary patch literal 231 zcmeAS@N?(olHy`uVBq!ia0vp^3P3Et!VDx^^%AmxltF+`h%1o(f7S3?v*A%g&1Xlm z_AasP&NBS}zk1D@>U;N&7Dst`Ia==Bdljgxxw`oO|NkCw+!8=ek*AAeh{V*?enY+% z10I&`yME6oE%_*}Az;dSsrJU#U+jFxOjwphIei~i26+Ea&3dA@Dtv>>Yo65Q?st;}q-I~dHFL=x`E5O~ax literal 0 HcmV?d00001 diff --git a/graphics/types/ice.png b/graphics/types/ice.png new file mode 100644 index 0000000000000000000000000000000000000000..5ff2f69aa7845da1ad97254ac48f73a06e2b06ee GIT binary patch literal 208 zcmeAS@N?(olHy`uVBq!ia0vp^3P3Et!VDx^^%AmxltF+`h%1mjd-VUBDc_2t=44Iz zHs{{GtN-75SqA-&>i&Op%ao*~C5}t(HSe7=Wl3{&@&Et-cbFcu1?q|Rba4!kn3~$J zDAW+Z;o@o5-|oBdMWwErvS|K>4z||!w~x%e$=DIKYpcQKL#>TF9tu8^Dy%))a+%>b zi}RHUTH6!cl@mf0!jI@q+CIbW)3ll!anC3ITD;-!zWofFkL8{BubkZmw1&ac)z4*} HQ$iB}#qe1+ literal 0 HcmV?d00001 diff --git a/graphics/types/move_types_1.pal b/graphics/types/move_types_1.pal new file mode 100644 index 000000000..951aa69cb --- /dev/null +++ b/graphics/types/move_types_1.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 0 0 +255 213 49 +246 131 49 +197 49 41 +230 197 106 +189 164 57 +139 106 49 +255 255 123 +172 172 123 +222 222 197 +115 90 74 +74 65 57 +189 189 213 +0 0 0 +131 123 115 +255 255 255 diff --git a/graphics/types/move_types_2.pal b/graphics/types/move_types_2.pal new file mode 100644 index 000000000..08dcea47c --- /dev/null +++ b/graphics/types/move_types_2.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 0 0 +205 197 255 +172 148 246 +115 90 156 +106 148 246 +156 222 222 +213 255 238 +74 57 82 +255 90 139 +255 197 180 +148 98 98 +164 65 164 +222 131 189 +148 148 164 +131 123 115 +255 255 255 diff --git a/graphics/types/move_types_3.pal b/graphics/types/move_types_3.pal new file mode 100644 index 000000000..4db37235b --- /dev/null +++ b/graphics/types/move_types_3.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 0 0 +197 255 98 +123 205 82 +90 131 65 +115 57 255 +74 57 148 +189 164 255 +172 189 32 +222 230 49 +123 148 16 +106 164 148 +115 205 180 +32 106 98 +0 0 0 +131 123 115 +255 255 255 diff --git a/graphics/types/mystery.png b/graphics/types/mystery.png new file mode 100644 index 0000000000000000000000000000000000000000..a2ea01d556a3d6b1a638c078c3dc702e1013b9bf GIT binary patch literal 215 zcmeAS@N?(olHy`uVBq!ia0vp^3P3Et!VDx^^%AmxltF+`h%1mj`ah}qY*19QW3lCb zFUu)=m;7I|SK;0>!|EvlSxcrApWUL6l>}7QTwVPC|9^8=`D;K&o zHRe)O$(h6=HMycCA;*6STa|sT!|EvlSxcrApWUL6l>}7QTwVPC|9^8=`D;K&o zHRe)O$(h6=HMycCA;*6STa|sTU;N&7Dst`Ia==Bdljgxxw`oO|NkCw+!8=ehNp{Th{V*?enY+n z0}j{7uTQQz%9IwH%i&Op%ao*~C5}t(HSe7=Wl3{&@&Et-cbFcu1?nmHba4!kn3~#e z$j7Y6!*X}&$>hti6Wf>G;JBo2?-*tOV9D1+Z_ORjiIH*!Y@3b0-#>JGUqpqppM+C~ z;H#Ne+8y78tdo4Gx$AkK!D5#cEz3imaDEdx`&U08>Ac@n*^GlX)!DMS82L6C21{RW kx4r%=b$jl^!k>Q`Q%$$bxLT}o4(J31Pgg&ebxsLQ0L;8&#sB~S literal 0 HcmV?d00001 diff --git a/graphics/types/psychic.png b/graphics/types/psychic.png new file mode 100644 index 0000000000000000000000000000000000000000..975d1fb553b1abdb287a6b63b496049766c62680 GIT binary patch literal 234 zcmeAS@N?(olHy`uVBq!ia0vp^3P3Et!VDx^^%AmxltF+`h%1mjd-VUBDc_2t=44Iz zHs{{GtN-75SqA-&>i&Op%ao*~C5}t(HSe7=Wl3{&@&Et-cbFcu1?nmFba4!kn3~#e z$mit9;gb3F$zs-stn^G9$*Tp-*ACuz^IcSBPl{SX*^@6fcHESe7C+2*IBU(DzP>9H zUTAGwF=^eD4lj=%9OpX=lzJnigcWaiDgF@VsNq%@y382gqa#|jm4$KH^2?L`{>Xp3 i^?mi+`_kw4zqhyLnHSNt>8U)>0Sun5elF{r5}E*RCSx=J literal 0 HcmV?d00001 diff --git a/graphics/types/rock.png b/graphics/types/rock.png new file mode 100644 index 0000000000000000000000000000000000000000..3793597f94d29e898af980f3f64a74e3d97b6b27 GIT binary patch literal 220 zcmeAS@N?(olHy`uVBq!ia0vp^3P3Et!VDx^^%AmxltF+`h%1o(f7S3?v*A%g&1Xlm z_AasP&NBS}zk1D@>U;N&7Dst`Ia==Bdljgxxw`oO|NkCw+!8=ex~Gd{h{V*?enY+n z0}fZop65juq&t!eFU~GsYW`zt*5$TeY)e7%6VnzkN((ywHIov&)xsZpYW{Evfum5 Q9iWvAp00i_>zopr0Cz%Hc>n+a literal 0 HcmV?d00001 diff --git a/graphics/types/steel.png b/graphics/types/steel.png new file mode 100644 index 0000000000000000000000000000000000000000..29748639c5d345adfdba463631daf0462f309394 GIT binary patch literal 221 zcmeAS@N?(olHy`uVBq!ia0vp^3P3Et!VDx^^%AmxltF+`h%1o(f7S3?v*A%g&1Xlm z_AasP&NBS}zk1D@>U;N&7Dst`Ia==Bdljgxxw`oO|NkCw+!8=ehNp{Th{V*?en+kb z2M*V}+g`rOZPpXvc`RzxP@^w!f62;(W$g-fKUOFyU%s{RM3_~XCbQ-gDY>44?4~{) zf%zwbPA}^(SjNGn7*m)I%Fdbd!FC@yYKt=+|Tnq?;aPYHL7qu zI0OPwr8(HSfp-LW&Q+KVUU#7bf*}wk!zd3Qi5o+T5Q+pGZYY3|L<#`}Amef%5ZS}C zJ|PjKWca7^o{btRutlgfx5XT#s}q;%BTg=({e5tq^h3MXDCEXfL^gKn<6HPtRf=cI z+*Z*tlZ9io8gsk}!(o&CTmPE(UOY9XSKWj8wEYcq^}$D|=f@};M&53Xf{{0Ap|)sb zj1%u{R9gGTljqK{==2+?;dd1Z`yKZUQdS&{iQ*49Ea@20erVOx*G;o|`zR%IQ`6+| z#j0rA+%=i``wV0yH(DS=mcJBUaSbzHng)A_)OFq;?I^65ZlxYAf**5@EVIc?>xn_W z%$|2=sK0SkwyneYW8svBPI=-ng5!0+iHAMCajv(W-bJ5Ht??V_+>oSiS3I>!Q!NX~>)leP_n<}~~^!5%x4yNt%w3d=c<+!y_}*rftG%k3%Dl#=jkO!|XxR?^+04YvYGBJCI# z8Z+jUgQC~GJ*pEIGOKb==ec^OXK_$|=_fJ?C)t0dZz5g@3jVXPMC**Ns4>gEJlht1 zNgEyeq+p4b>7@)e(+n?%b3PkzxovfHztD$nr3d`EzwfZIj(A<@e>?Z!MkRIK`uU4h z^&>M}jp;7jx&dEYC|*l<9+z0y8TJb zE2K_|3ma{gS?UI#51gYsaYZU44R*XY2`Y>&hb4IJoD1aetC!9+sdW&vE-!x(J>(i8tp5a7BDg%EQ9l0s&leA@=_*EZ=(5*brtv=Tt z(5<0=wKX(g?CplFIXP?R;89Zr4(f~tgsjjh}$9}?QPfA zJgeC0t$CU!%M=IQK+&zvuw`n#HC&3H|L-pp*BvIZJ~}!6@oBbi<|^xGYWt*$dR%bB zfaLiST?p)b_x;f0tnvEQR(=bc`>IR*?S{)7TVc08D95R~3RJa{yWFH}cMKKJd8)y# zdIjz16Lv7J2HV9496sz7qt$sZ#S{IPeqvy5w=ljFFQ)2i(Fm7|XBjIwA8v6!w$f<5 znqX7W$b_o!;9oZkOwZnf9zgoOE7?0gIr|OSj{ZVDy32z-PE`3|F!W2sVaghwSd#OL z;Hh1cZc^6ZDGSF#3+_BNcIdO)Qu|TApe8DARM|YIN&i&>ET(!wc(-=;X2bZ!`g*;r zX4jjJ`-D~VPUqjf-)61$sK8XEv8FOYCtv`!L#Pqi7TZ*1p0BidTPF_3i@(}=WOtXU zIz=$YBBD3SRwqEWa=-7%E74(@HQaGCRI%pG$ak-;E}c|8>e04ruF4YSn|^e~@OR$N z*4z2??TGe?ozyuPID4OyQ&YWqceBC1Ymosh&yrtP zgjP3^+cZidlSgFD=gGQtFA82!o3l!~6xUlgJl^{{qR!%NyCGltZur5-IO=$MW-jOK zr?qp(VUV;+H7z(6q8fZx$ba!XJBqc5sgI3WMc%ZA?bg z0Kb+RyJ-BKa_5He&X(i3d-Y=TbV=3sCXPV~My!pQPmdf5y?bupPLXBmr2tJ1apLtY zao2s%9y9pzmiK|-;A+%r8U8r%<75$fVr35n=G14did~d7bm^YDf5VwiQ_%C}R&l-Y zLOuin-Nm)Fb)nhXe)&>@-^c7Nsn!lPD|B}EoLXzYOwDUvSiQ>;v-pJgbdL)#?X~{7 zo=WMCQ#mx-wBFv|DzptIe2b_R9BQ|02T1$3~Wy>0K=4dPzZHV}gB(7p?58S<1B05U8Gt++P!)JLB zJbF!fi}QjrFzoeSWI>o$gJ;_x#@iU!{Ie%2oI-SPW{Z*)?CJ~hcH+(Kg6=!uQcsK& z#BHk|GOk~eenw$+vZ6=MD$&HFZG%@Xa<+=9>G0Ltn-_FU>Of~*S#g}x(Q~uBiwvE( zUAez)*t@^7(fH->2efTwMP1yUIB;O;Tj)W{%17yF-L%CYxXK@_2Dh&6$x!^r-~NVp zQP3pLwt;Z_TZdJJLfBsI-=e$vx;DJtGJf&YgX>0r_KR*c7-6O0mPUsQZe4un>!>UN z56xf;m;hSF6M|bC2*hf+Ovqq`01^Zf2;}muk#DbDMIhm5d4!@Bj$|A>)Pe#Z;L!a+;S4KFgaiNW?Tm5@Lq0 zjEo_~;LunMkB9lzLM*Y507+&7`d16F2e<>oxB+58xQGSVM*w`u;%_O~tgrUMa8c-V zI&2mO2nBc`R1A8>edlrwjqdW*LM}ldmnWRI0>%CgDdBSd5$n6yaU8{+0JT z^q1VH!61rGr`icv;qvfkcGgI_e=1wR;ieNz`pqOkT4n-ufu`C=G z%OIO$zoBpzalxu&gnk>9oQe%nu?ZZa1&ho?Q5a+via^3qPz)BpMwt@{L<)<+?Nmp@Qjv(lX?g3KC4W zgV|7RMF2x05P1j$q1H%w2nf05bb%wRJ`alnR}3N|_p6n0NB%xK)cUftRMzI5CCWROi6ud=l;uZVF89YhlB&=<&eoJ0)xy(k>yVu zGC;6k0vsYZmfy3B1ssW#Ap%wgf|-KZfF(4Y4dS;MF-^Y5OM?KpQdm3zg~g-rcn_R8 z)tpQv5|NmhWHItN{Uuu~%zx=&H4XS?5diHzw}FcbxK?4lE><&o$tC#br(8dzzz>0cR@YCten^2I0{^V8|CwCye|~oWKDYytfn zorgVmubpB>H@W%kSShAVt)*mKJuy{u718|+7o9OT_(X=dsSFX!1YtX(#ZyyLBzvPa z@I_hZ;46ke=FFF$P)Js`4%nz9q0#M?Ud+)E>6&*R!NGra`4m!7Oz)UTVeJ1K&nxyLeIQNs|DC_*T9A~>FvL`CyA zQmwS=RP#}KLdepOOY?({oq*YG2v1~&)*2>EZY~1y7(5@F`i&Op%ao*~C5}t(HSe7=Wl3{&@&Et-cbFcu1?oxlba4!kn3~#e z$ag@2!!>f(w~(@{d~g3+8`S4BC~fpys4w*Rvi}5)&379ouM@IZq2l9_oGN;C-5!_r z8%{{wk{_q~Fmc&%X+c8THTFlda%VD$sK7Y7O%{D!K S#lt`g89ZJ6T-G@yGywoXgbufferA[battler][0] < NELEMS(sPlayerBufferCommands)) + if (gBattleResources->bufferA[battler][0] < ARRAY_COUNT(sPlayerBufferCommands)) sPlayerBufferCommands[gBattleResources->bufferA[battler][0]](battler); else PlayerBufferExecCompleted(battler); @@ -240,6 +253,11 @@ static void HandleInputChooseAction(u32 battler) DoBounceEffect(battler, BOUNCE_HEALTHBOX, 7, 1); DoBounceEffect(battler, BOUNCE_MON, 7, 1); + if (JOY_REPT(DPAD_ANY) && gSaveBlock2Ptr->optionsButtonMode == OPTIONS_BUTTON_MODE_L_EQUALS_A) + gPlayerDpadHoldFrames++; + else + gPlayerDpadHoldFrames = 0; + if (B_LAST_USED_BALL == TRUE && B_LAST_USED_BALL_CYCLE == TRUE) { if (!gLastUsedBallMenuPresent) @@ -305,16 +323,16 @@ static void HandleInputChooseAction(u32 battler) switch (gActionSelectionCursor[battler]) { - case 0: + case 0: // Top left BtlController_EmitTwoReturnValues(battler, BUFFER_B, B_ACTION_USE_MOVE, 0); break; - case 1: + case 1: // Top right BtlController_EmitTwoReturnValues(battler, BUFFER_B, B_ACTION_USE_ITEM, 0); break; - case 2: + case 2: // Bottom left BtlController_EmitTwoReturnValues(battler, BUFFER_B, B_ACTION_SWITCH, 0); break; - case 3: + case 3: // Bottom right BtlController_EmitTwoReturnValues(battler, BUFFER_B, B_ACTION_RUN, 0); break; } @@ -360,16 +378,16 @@ static void HandleInputChooseAction(u32 battler) ActionSelectionCreateCursorAt(gActionSelectionCursor[battler], 0); } } - else if (JOY_NEW(B_BUTTON)) + else if (JOY_NEW(B_BUTTON) || gPlayerDpadHoldFrames > 59) { - if ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + if (IsDoubleBattle() && GetBattlerPosition(battler) == B_POSITION_PLAYER_RIGHT - && !(gAbsentBattlerFlags & gBitTable[GetBattlerAtPosition(B_POSITION_PLAYER_LEFT)]) + && !(gAbsentBattlerFlags & (1u << GetBattlerAtPosition(B_POSITION_PLAYER_LEFT))) && !(gBattleTypeFlags & BATTLE_TYPE_MULTI)) { - if (gBattleResources->bufferA[battler][1] == B_ACTION_USE_ITEM) + // Return item to bag if partner had selected one (if consumable). + if (gBattleResources->bufferA[battler][1] == B_ACTION_USE_ITEM && ItemId_GetConsumability(itemId)) { - // Return item to bag if partner had selected one. AddBagItem(itemId, 1); } PlaySE(SE_SELECT); @@ -406,7 +424,7 @@ static void HandleInputChooseAction(u32 battler) } } -static void HandleInputChooseTarget(u32 battler) +void HandleInputChooseTarget(u32 battler) { s32 i; static const u8 identities[MAX_BATTLERS_COUNT] = {B_POSITION_PLAYER_LEFT, B_POSITION_PLAYER_RIGHT, B_POSITION_OPPONENT_RIGHT, B_POSITION_OPPONENT_LEFT}; @@ -420,6 +438,11 @@ static void HandleInputChooseTarget(u32 battler) EndBounceEffect(i, BOUNCE_HEALTHBOX); } + if (JOY_HELD(DPAD_ANY) && gSaveBlock2Ptr->optionsButtonMode == OPTIONS_BUTTON_MODE_L_EQUALS_A) + gPlayerDpadHoldFrames++; + else + gPlayerDpadHoldFrames = 0; + if (JOY_NEW(A_BUTTON)) { PlaySE(SE_SELECT); @@ -433,7 +456,7 @@ static void HandleInputChooseTarget(u32 battler) HideGimmickTriggerSprite(); PlayerBufferExecCompleted(battler); } - else if (JOY_NEW(B_BUTTON)) + else if (JOY_NEW(B_BUTTON) || gPlayerDpadHoldFrames > 59) { PlaySE(SE_SELECT); gSprites[gBattlerSpriteIds[gMultiUsePlayerCursor]].callback = SpriteCB_HideAsMoveTarget; @@ -485,7 +508,7 @@ static void HandleInputChooseTarget(u32 battler) break; } - if (gAbsentBattlerFlags & gBitTable[gMultiUsePlayerCursor] + if (gAbsentBattlerFlags & (1u << gMultiUsePlayerCursor) || !CanTargetBattler(battler, gMultiUsePlayerCursor, move)) i = 0; } while (i == 0); @@ -535,7 +558,7 @@ static void HandleInputChooseTarget(u32 battler) break; } - if (gAbsentBattlerFlags & gBitTable[gMultiUsePlayerCursor] + if (gAbsentBattlerFlags & (1u << gMultiUsePlayerCursor) || !CanTargetBattler(battler, gMultiUsePlayerCursor, move)) i = 0; } while (i == 0); @@ -571,7 +594,7 @@ static void HideShownTargets(u32 battler) } } -static void HandleInputShowEntireFieldTargets(u32 battler) +void HandleInputShowEntireFieldTargets(u32 battler) { if (JOY_HELD(DPAD_ANY) && gSaveBlock2Ptr->optionsButtonMode == OPTIONS_BUTTON_MODE_L_EQUALS_A) gPlayerDpadHoldFrames++; @@ -599,7 +622,7 @@ static void HandleInputShowEntireFieldTargets(u32 battler) } } -static void HandleInputShowTargets(u32 battler) +void HandleInputShowTargets(u32 battler) { if (JOY_HELD(DPAD_ANY) && gSaveBlock2Ptr->optionsButtonMode == OPTIONS_BUTTON_MODE_L_EQUALS_A) gPlayerDpadHoldFrames++; @@ -657,13 +680,13 @@ void HandleInputChooseMove(u32 battler) if (gBattleStruct->zmove.viewing) { gBattleStruct->zmove.viewing = FALSE; - if (gMovesInfo[moveInfo->moves[gMoveSelectionCursor[battler]]].category != DAMAGE_CATEGORY_STATUS) + if (GetMoveCategory(moveInfo->moves[gMoveSelectionCursor[battler]]) != DAMAGE_CATEGORY_STATUS) moveTarget = MOVE_TARGET_SELECTED; //damaging z moves always have selected target } // Status moves turn into Max Guard when Dynamaxed, targets user. if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX || IsGimmickSelected(battler, GIMMICK_DYNAMAX)) - moveTarget = gMovesInfo[GetMaxMove(battler, moveInfo->moves[gMoveSelectionCursor[battler]])].target; + moveTarget = GetMoveTarget(GetMaxMove(battler, moveInfo->moves[gMoveSelectionCursor[battler]])); if (moveTarget & MOVE_TARGET_USER) gMultiUsePlayerCursor = battler; @@ -731,7 +754,7 @@ void HandleInputChooseMove(u32 battler) if (moveTarget & (MOVE_TARGET_USER | MOVE_TARGET_USER_OR_SELECTED)) gMultiUsePlayerCursor = battler; - else if (gAbsentBattlerFlags & gBitTable[GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT)]) + else if (gAbsentBattlerFlags & (1u << GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT))) gMultiUsePlayerCursor = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); else gMultiUsePlayerCursor = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); @@ -865,7 +888,7 @@ void HandleInputChooseMove(u32 battler) } else if (JOY_NEW(START_BUTTON)) { - if (gBattleStruct->gimmick.usableGimmick[battler] != GIMMICK_NONE) + if (gBattleStruct->gimmick.usableGimmick[battler] != GIMMICK_NONE && !HasTrainerUsedGimmick(battler, gBattleStruct->gimmick.usableGimmick[battler])) { gBattleStruct->gimmick.playerSelect ^= 1; ReloadMoveNames(battler); @@ -875,7 +898,6 @@ void HandleInputChooseMove(u32 battler) } } - static void ReloadMoveNames(u32 battler) { if (gBattleStruct->zmove.viable && !gBattleStruct->zmove.viewing) @@ -894,14 +916,65 @@ static void ReloadMoveNames(u32 battler) } } -static void HandleMoveSwitching(u32 battler) +static u32 UNUSED HandleMoveInputUnused(u32 battler) { - u8 perMovePPBonuses[4]; + u32 var = 0; + + if (JOY_NEW(A_BUTTON)) + { + PlaySE(SE_SELECT); + var = 1; + } + if (JOY_NEW(B_BUTTON)) + { + PlaySE(SE_SELECT); + gBattle_BG0_X = 0; + gBattle_BG0_Y = DISPLAY_HEIGHT * 2; + var = 0xFF; + } + if (JOY_NEW(DPAD_LEFT) && gMoveSelectionCursor[battler] & 1) + { + MoveSelectionDestroyCursorAt(gMoveSelectionCursor[battler]); + gMoveSelectionCursor[battler] ^= 1; + PlaySE(SE_SELECT); + MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0); + } + if (JOY_NEW(DPAD_RIGHT) && !(gMoveSelectionCursor[battler] & 1) + && (gMoveSelectionCursor[battler] ^ 1) < gNumberOfMovesToChoose) + { + MoveSelectionDestroyCursorAt(gMoveSelectionCursor[battler]); + gMoveSelectionCursor[battler] ^= 1; + PlaySE(SE_SELECT); + MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0); + } + if (JOY_NEW(DPAD_UP) && gMoveSelectionCursor[battler] & 2) + { + MoveSelectionDestroyCursorAt(gMoveSelectionCursor[battler]); + gMoveSelectionCursor[battler] ^= 2; + PlaySE(SE_SELECT); + MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0); + } + if (JOY_NEW(DPAD_DOWN) && !(gMoveSelectionCursor[battler] & 2) + && (gMoveSelectionCursor[battler] ^ 2) < gNumberOfMovesToChoose) + { + MoveSelectionDestroyCursorAt(gMoveSelectionCursor[battler]); + gMoveSelectionCursor[battler] ^= 2; + PlaySE(SE_SELECT); + MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0); + } + + return var; +} + +void HandleMoveSwitching(u32 battler) +{ + u8 perMovePPBonuses[MAX_MON_MOVES]; struct ChooseMoveStruct moveStruct; u8 totalPPBonuses; if (JOY_NEW(A_BUTTON | SELECT_BUTTON)) { + struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleResources->bufferA[battler][4]); PlaySE(SE_SELECT); if (gMoveSelectionCursor[battler] != gMultiUsePlayerCursor) @@ -913,75 +986,89 @@ static void HandleMoveSwitching(u32 battler) i = moveInfo->moves[gMoveSelectionCursor[battler]]; moveInfo->moves[gMoveSelectionCursor[battler]] = moveInfo->moves[gMultiUsePlayerCursor]; moveInfo->moves[gMultiUsePlayerCursor] = i; + i = moveInfo->currentPp[gMoveSelectionCursor[battler]]; moveInfo->currentPp[gMoveSelectionCursor[battler]] = moveInfo->currentPp[gMultiUsePlayerCursor]; moveInfo->currentPp[gMultiUsePlayerCursor] = i; + i = moveInfo->maxPp[gMoveSelectionCursor[battler]]; moveInfo->maxPp[gMoveSelectionCursor[battler]] = moveInfo->maxPp[gMultiUsePlayerCursor]; moveInfo->maxPp[gMultiUsePlayerCursor] = i; - if (gDisableStructs[battler].mimickedMoves & gBitTable[gMoveSelectionCursor[battler]]) + + if (gDisableStructs[battler].mimickedMoves & (1u << gMoveSelectionCursor[battler])) { - gDisableStructs[battler].mimickedMoves &= (~gBitTable[gMoveSelectionCursor[battler]]); - gDisableStructs[battler].mimickedMoves |= gBitTable[gMultiUsePlayerCursor]; + gDisableStructs[battler].mimickedMoves &= ~(1u << gMoveSelectionCursor[battler]); + gDisableStructs[battler].mimickedMoves |= 1u << gMultiUsePlayerCursor; } + MoveSelectionDisplayMoveNames(battler); - for (i = 0; i < MAX_MON_MOVES; ++i) + + for (i = 0; i < MAX_MON_MOVES; i++) perMovePPBonuses[i] = (gBattleMons[battler].ppBonuses & (3 << (i * 2))) >> (i * 2); + totalPPBonuses = perMovePPBonuses[gMoveSelectionCursor[battler]]; perMovePPBonuses[gMoveSelectionCursor[battler]] = perMovePPBonuses[gMultiUsePlayerCursor]; perMovePPBonuses[gMultiUsePlayerCursor] = totalPPBonuses; + totalPPBonuses = 0; - for (i = 0; i < MAX_MON_MOVES; ++i) + for (i = 0; i < MAX_MON_MOVES; i++) totalPPBonuses |= perMovePPBonuses[i] << (i * 2); gBattleMons[battler].ppBonuses = totalPPBonuses; - for (i = 0; i < MAX_MON_MOVES; ++i) + + for (i = 0; i < MAX_MON_MOVES; i++) { gBattleMons[battler].moves[i] = moveInfo->moves[i]; gBattleMons[battler].pp[i] = moveInfo->currentPp[i]; } + if (!(gBattleMons[battler].status2 & STATUS2_TRANSFORMED)) { - for (i = 0; i < MAX_MON_MOVES; ++i) + for (i = 0; i < MAX_MON_MOVES; i++) { moveStruct.moves[i] = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battler]], MON_DATA_MOVE1 + i); moveStruct.currentPp[i] = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battler]], MON_DATA_PP1 + i); } totalPPBonuses = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battler]], MON_DATA_PP_BONUSES); - for (i = 0; i < MAX_MON_MOVES; ++i) + for (i = 0; i < MAX_MON_MOVES; i++) perMovePPBonuses[i] = (totalPPBonuses & (3 << (i * 2))) >> (i * 2); + i = moveStruct.moves[gMoveSelectionCursor[battler]]; moveStruct.moves[gMoveSelectionCursor[battler]] = moveStruct.moves[gMultiUsePlayerCursor]; moveStruct.moves[gMultiUsePlayerCursor] = i; + i = moveStruct.currentPp[gMoveSelectionCursor[battler]]; moveStruct.currentPp[gMoveSelectionCursor[battler]] = moveStruct.currentPp[gMultiUsePlayerCursor]; moveStruct.currentPp[gMultiUsePlayerCursor] = i; + totalPPBonuses = perMovePPBonuses[gMoveSelectionCursor[battler]]; perMovePPBonuses[gMoveSelectionCursor[battler]] = perMovePPBonuses[gMultiUsePlayerCursor]; perMovePPBonuses[gMultiUsePlayerCursor] = totalPPBonuses; + totalPPBonuses = 0; - for (i = 0; i < MAX_MON_MOVES; ++i) + for (i = 0; i < MAX_MON_MOVES; i++) totalPPBonuses |= perMovePPBonuses[i] << (i * 2); - for (i = 0; i < MAX_MON_MOVES; ++i) + + for (i = 0; i < MAX_MON_MOVES; i++) { SetMonData(&gPlayerParty[gBattlerPartyIndexes[battler]], MON_DATA_MOVE1 + i, &moveStruct.moves[i]); SetMonData(&gPlayerParty[gBattlerPartyIndexes[battler]], MON_DATA_PP1 + i, &moveStruct.currentPp[i]); } + SetMonData(&gPlayerParty[gBattlerPartyIndexes[battler]], MON_DATA_PP_BONUSES, &totalPPBonuses); } } - if (gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE) - gBattlerControllerFuncs[battler] = OakOldManHandleInputChooseMove; - else - gBattlerControllerFuncs[battler] = HandleInputChooseMove; + + gBattlerControllerFuncs[battler] = HandleInputChooseMove; gMoveSelectionCursor[battler] = gMultiUsePlayerCursor; MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0); - MoveSelectionDisplayPpString(); + MoveSelectionDisplayPpString(battler); MoveSelectionDisplayPpNumber(battler); MoveSelectionDisplayMoveType(battler); + AssignUsableZMoves(battler, moveInfo->moves); } - if (JOY_NEW(B_BUTTON)) + else if (JOY_NEW(B_BUTTON | SELECT_BUTTON)) { PlaySE(SE_SELECT); MoveSelectionDestroyCursorAt(gMultiUsePlayerCursor); @@ -990,11 +1077,11 @@ static void HandleMoveSwitching(u32 battler) gBattlerControllerFuncs[battler] = OakOldManHandleInputChooseMove; else gBattlerControllerFuncs[battler] = HandleInputChooseMove; - MoveSelectionDisplayPpString(); + MoveSelectionDisplayPpString(battler); MoveSelectionDisplayPpNumber(battler); MoveSelectionDisplayMoveType(battler); } - if (JOY_NEW(DPAD_LEFT)) + else if (JOY_NEW(DPAD_LEFT)) { if (gMultiUsePlayerCursor & 1) { @@ -1002,15 +1089,17 @@ static void HandleMoveSwitching(u32 battler) MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 29); else MoveSelectionDestroyCursorAt(gMultiUsePlayerCursor); + gMultiUsePlayerCursor ^= 1; PlaySE(SE_SELECT); + if (gMultiUsePlayerCursor == gMoveSelectionCursor[battler]) MoveSelectionCreateCursorAt(gMultiUsePlayerCursor, 0); else MoveSelectionCreateCursorAt(gMultiUsePlayerCursor, 27); } } - if (JOY_NEW(DPAD_RIGHT)) + else if (JOY_NEW(DPAD_RIGHT)) { if (!(gMultiUsePlayerCursor & 1) && (gMultiUsePlayerCursor ^ 1) < gNumberOfMovesToChoose) { @@ -1018,15 +1107,17 @@ static void HandleMoveSwitching(u32 battler) MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 29); else MoveSelectionDestroyCursorAt(gMultiUsePlayerCursor); + gMultiUsePlayerCursor ^= 1; PlaySE(SE_SELECT); + if (gMultiUsePlayerCursor == gMoveSelectionCursor[battler]) MoveSelectionCreateCursorAt(gMultiUsePlayerCursor, 0); else MoveSelectionCreateCursorAt(gMultiUsePlayerCursor, 27); } } - if (JOY_NEW(DPAD_UP)) + else if (JOY_NEW(DPAD_UP)) { if (gMultiUsePlayerCursor & 2) { @@ -1034,15 +1125,17 @@ static void HandleMoveSwitching(u32 battler) MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 29); else MoveSelectionDestroyCursorAt(gMultiUsePlayerCursor); + gMultiUsePlayerCursor ^= 2; PlaySE(SE_SELECT); + if (gMultiUsePlayerCursor == gMoveSelectionCursor[battler]) MoveSelectionCreateCursorAt(gMultiUsePlayerCursor, 0); else MoveSelectionCreateCursorAt(gMultiUsePlayerCursor, 27); } } - if (JOY_NEW(DPAD_DOWN)) + else if (JOY_NEW(DPAD_DOWN)) { if (!(gMultiUsePlayerCursor & 2) && (gMultiUsePlayerCursor ^ 2) < gNumberOfMovesToChoose) { @@ -1050,8 +1143,10 @@ static void HandleMoveSwitching(u32 battler) MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 29); else MoveSelectionDestroyCursorAt(gMultiUsePlayerCursor); + gMultiUsePlayerCursor ^= 2; PlaySE(SE_SELECT); + if (gMultiUsePlayerCursor == gMoveSelectionCursor[battler]) MoveSelectionCreateCursorAt(gMultiUsePlayerCursor, 0); else @@ -1064,36 +1159,44 @@ static void SetLinkBattleEndCallbacks(u32 battler) { if (gWirelessCommType == 0) { - if (!gReceivedRemoteLinkPlayers) + if (gReceivedRemoteLinkPlayers == 0) { m4aSongNumStop(SE_LOW_HEALTH); - gMain.inBattle = 0; + gMain.inBattle = FALSE; gMain.callback1 = gPreBattleCallback1; SetMainCallback2(CB2_InitEndLinkBattle); FreeAllWindowBuffers(); } } - else if (IsLinkTaskFinished()) + else { - m4aSongNumStop(SE_LOW_HEALTH); - gMain.inBattle = 0; - gMain.callback1 = gPreBattleCallback1; - SetMainCallback2(CB2_InitEndLinkBattle); - FreeAllWindowBuffers(); + if (IsLinkTaskFinished()) + { + m4aSongNumStop(SE_LOW_HEALTH); + gMain.inBattle = FALSE; + gMain.callback1 = gPreBattleCallback1; + SetMainCallback2(CB2_InitEndLinkBattle); + FreeAllWindowBuffers(); + } } } +// Despite handling link battles separately, this is only ever used by link battles void SetBattleEndCallbacks(u32 battler) { if (!gPaletteFade.active) { if (gBattleTypeFlags & BATTLE_TYPE_LINK) { - if (gWirelessCommType == 0) - SetCloseLinkCallback(); - else - SetLinkStandbyCallback(); - gBattlerControllerFuncs[battler] = SetLinkBattleEndCallbacks; + if (IsLinkTaskFinished()) + { + if (gWirelessCommType == 0) + SetCloseLinkCallback(); + else + SetLinkStandbyCallback(); + + gBattlerControllerFuncs[battler] = SetLinkBattleEndCallbacks; + } } else { @@ -1242,14 +1345,21 @@ static void Intro_TryShinyAnimShowHealthbox(u32 battler) static void SwitchIn_CleanShinyAnimShowSubstitute(u32 battler) { if (gSprites[gHealthboxSpriteIds[battler]].callback == SpriteCallbackDummy - && gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim) + && gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim + && gSprites[gBattlerSpriteIds[battler]].callback == SpriteCallbackDummy) { + CopyBattleSpriteInvisibility(battler); + + // Reset shiny anim (even if it didn't occur) gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim = FALSE; gBattleSpritesDataPtr->healthBoxesData[battler].finishedShinyMonAnim = FALSE; FreeSpriteTilesByTag(ANIM_TAG_GOLD_STARS); FreeSpritePaletteByTag(ANIM_TAG_GOLD_STARS); + + // Check if Substitute should be shown if (gBattleSpritesDataPtr->battlerData[battler].behindSubstitute) InitAndLaunchSpecialAnimation(battler, battler, battler, B_ANIM_MON_TO_SUBSTITUTE); + gBattlerControllerFuncs[battler] = SwitchIn_HandleSoundAndEnd; } } @@ -1257,7 +1367,7 @@ static void SwitchIn_CleanShinyAnimShowSubstitute(u32 battler) static void SwitchIn_HandleSoundAndEnd(u32 battler) { if (!gBattleSpritesDataPtr->healthBoxesData[battler].specialAnimActive - && !IsCryPlayingOrClearCrySongs()) + && !IsCryPlayingOrClearCrySongs()) { m4aMPlayVolumeControl(&gMPlayInfo_BGM, TRACKS_ALL, 0x100); HandleLowHpMusicChange(&gPlayerParty[gBattlerPartyIndexes[battler]], battler); @@ -1267,19 +1377,19 @@ static void SwitchIn_HandleSoundAndEnd(u32 battler) static void SwitchIn_TryShinyAnimShowHealthbox(u32 battler) { + // Start shiny animation if applicable if (!gBattleSpritesDataPtr->healthBoxesData[battler].triedShinyMonAnim - && !gBattleSpritesDataPtr->healthBoxesData[battler].ballAnimActive) + && !gBattleSpritesDataPtr->healthBoxesData[battler].ballAnimActive) TryShinyAnimation(battler, &gPlayerParty[gBattlerPartyIndexes[battler]]); + + // Wait for ball anim, then show healthbox if (gSprites[gBattleControllerData[battler]].callback == SpriteCallbackDummy - && !(gBattleSpritesDataPtr->healthBoxesData[battler].ballAnimActive)) + && !gBattleSpritesDataPtr->healthBoxesData[battler].ballAnimActive) { DestroySprite(&gSprites[gBattleControllerData[battler]]); - UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], - &gPlayerParty[gBattlerPartyIndexes[battler]], - HEALTHBOX_ALL); + UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], &gPlayerParty[gBattlerPartyIndexes[battler]], HEALTHBOX_ALL); StartHealthboxSlideIn(battler); SetHealthboxSpriteVisible(gHealthboxSpriteIds[battler]); - CopyBattleSpriteInvisibility(battler); gBattlerControllerFuncs[battler] = SwitchIn_CleanShinyAnimShowSubstitute; } } @@ -1293,17 +1403,11 @@ void Task_PlayerController_RestoreBgmAfterCry(u8 taskId) } } -void CompleteOnInactiveTextPrinter(u32 battler) -{ - if (!IsTextPrinterActive(0)) - PlayerBufferExecCompleted(battler); -} - -#define tExpTask_monId data[0] -#define tExpTask_battler data[2] -#define tExpTask_gainedExp_1 data[3] -#define tExpTask_gainedExp_2 data[4] -#define tExpTask_frames data[10] +#define tExpTask_monId data[0] +#define tExpTask_battler data[2] +#define tExpTask_gainedExp_1 data[3] +#define tExpTask_gainedExp_2 data[4] // Stored as two half-words containing a word. +#define tExpTask_frames data[10] static s32 GetTaskExpValue(u8 taskId) { @@ -1346,7 +1450,7 @@ static void Task_GiveExpToMon(u8 taskId) && (monId == gBattlerPartyIndexes[battler] || monId == gBattlerPartyIndexes[BATTLE_PARTNER(battler)])) gTasks[taskId].func = Task_LaunchLvlUpAnim; else - gTasks[taskId].func = DestroyExpTaskAndCompleteOnInactiveTextPrinter; + gTasks[taskId].func = Task_SetControllerToWaitForString; } else { @@ -1457,15 +1561,17 @@ static void Task_UpdateLvlInHealthbox(u8 taskId) if (!gBattleSpritesDataPtr->healthBoxesData[battler].specialAnimActive) { u8 monIndex = gTasks[taskId].tExpTask_monId; + if (IsDoubleBattle() == TRUE && monIndex == gBattlerPartyIndexes[BATTLE_PARTNER(battler)]) UpdateHealthboxAttribute(gHealthboxSpriteIds[BATTLE_PARTNER(battler)], &gPlayerParty[monIndex], HEALTHBOX_ALL); else UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], &gPlayerParty[monIndex], HEALTHBOX_ALL); - gTasks[taskId].func = DestroyExpTaskAndCompleteOnInactiveTextPrinter; + + gTasks[taskId].func = Task_SetControllerToWaitForString; } } -static void DestroyExpTaskAndCompleteOnInactiveTextPrinter(u8 taskId) +static void Task_SetControllerToWaitForString(u8 taskId) { s32 battlerId = gTasks[taskId].tExpTask_battler; @@ -1572,7 +1678,7 @@ static void OpenPartyMenuToChooseMon(u32 battler) caseId = gTasks[gBattleControllerData[battler]].data[0]; DestroyTask(gBattleControllerData[battler]); FreeAllWindowBuffers(); - OpenPartyMenuInTutorialBattle(caseId); + OpenPartyMenuInBattle(caseId); } } @@ -1581,11 +1687,13 @@ static void WaitForMonSelection(u32 battler) if (gMain.callback2 == BattleMainCB2 && !gPaletteFade.active) { if (gPartyMenuUseExitCallback == TRUE) - BtlController_EmitChosenMonReturnValue(battler, 1, gSelectedMonPartyId, gBattlePartyCurrentOrder); + BtlController_EmitChosenMonReturnValue(battler, BUFFER_B, gSelectedMonPartyId, gBattlePartyCurrentOrder); else - BtlController_EmitChosenMonReturnValue(battler, 1, 6, NULL); + BtlController_EmitChosenMonReturnValue(battler, BUFFER_B, PARTY_SIZE, NULL); + if ((gBattleResources->bufferA[battler][1] & 0xF) == 1) PrintLinkStandbyMsg(); + PlayerBufferExecCompleted(battler); } } @@ -1605,7 +1713,43 @@ static void CompleteWhenChoseItem(u32 battler) { if (gMain.callback2 == BattleMainCB2 && !gPaletteFade.active) { - BtlController_EmitOneReturnValue(battler, 1, gSpecialVar_ItemId); + BtlController_EmitOneReturnValue(battler, BUFFER_B, gSpecialVar_ItemId); + PlayerBufferExecCompleted(battler); + } +} + +static void PlayerHandleYesNoInput(u32 battler) +{ + if (JOY_NEW(DPAD_UP) && gMultiUsePlayerCursor != 0) + { + PlaySE(SE_SELECT); + BattleDestroyYesNoCursorAt(gMultiUsePlayerCursor); + gMultiUsePlayerCursor = 0; + BattleCreateYesNoCursorAt(0); + } + if (JOY_NEW(DPAD_DOWN) && gMultiUsePlayerCursor == 0) + { + PlaySE(SE_SELECT); + BattleDestroyYesNoCursorAt(gMultiUsePlayerCursor); + gMultiUsePlayerCursor = 1; + BattleCreateYesNoCursorAt(1); + } + if (JOY_NEW(A_BUTTON)) + { + HandleBattleWindow(YESNOBOX_X_Y, WINDOW_CLEAR); + PlaySE(SE_SELECT); + + if (gMultiUsePlayerCursor != 0) + BtlController_EmitTwoReturnValues(battler, BUFFER_B, 0xE, 0); + else + BtlController_EmitTwoReturnValues(battler, BUFFER_B, 0xD, 0); + + PlayerBufferExecCompleted(battler); + } + if (JOY_NEW(B_BUTTON)) + { + HandleBattleWindow(YESNOBOX_X_Y, WINDOW_CLEAR); + PlaySE(SE_SELECT); PlayerBufferExecCompleted(battler); } } @@ -1630,7 +1774,7 @@ static void MoveSelectionDisplayMoveNames(u32 battler) } } -static void MoveSelectionDisplayPpString(void) +static void MoveSelectionDisplayPpString(u32 battler) { StringCopy(gDisplayedStringBattle, gText_MoveInterfacePP); BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_PP); @@ -1643,11 +1787,13 @@ static void MoveSelectionDisplayPpNumber(u32 battler) if (gBattleResources->bufferA[battler][2] == TRUE) // check if we didn't want to display pp number return; + SetPpNumbersPaletteInMoveSelection(battler); moveInfo = (struct ChooseMoveStruct *)(&gBattleResources->bufferA[battler][4]); txtPtr = ConvertIntToDecimalStringN(gDisplayedStringBattle, moveInfo->currentPp[gMoveSelectionCursor[battler]], STR_CONV_MODE_RIGHT_ALIGN, 2); - *txtPtr = CHAR_SLASH; - ConvertIntToDecimalStringN(++txtPtr, moveInfo->maxPp[gMoveSelectionCursor[battler]], STR_CONV_MODE_RIGHT_ALIGN, 2); + *(txtPtr)++ = CHAR_SLASH; + ConvertIntToDecimalStringN(txtPtr, moveInfo->maxPp[gMoveSelectionCursor[battler]], STR_CONV_MODE_RIGHT_ALIGN, 2); + BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_PP_REMAINING); } @@ -1658,13 +1804,12 @@ static void MoveSelectionDisplayMoveType(u32 battler) u32 speciesId; struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleResources->bufferA[battler][4]); txtPtr = StringCopy(gDisplayedStringBattle, gText_MoveInterfaceType); - type = gMovesInfo[moveInfo->moves[gMoveSelectionCursor[battler]]].type; + type = GetMoveType(moveInfo->moves[gMoveSelectionCursor[battler]]); if (moveInfo->moves[gMoveSelectionCursor[battler]] == MOVE_TERA_BLAST) { if (IsGimmickSelected(battler, GIMMICK_TERA) || GetActiveGimmick(battler) == GIMMICK_TERA) type = GetBattlerTeraType(battler); - end = StringCopy(txtPtr, gTypesInfo[type].name); } else if (moveInfo->moves[gMoveSelectionCursor[battler]] == MOVE_IVY_CUDGEL) { @@ -1674,26 +1819,24 @@ static void MoveSelectionDisplayMoveType(u32 battler) || speciesId == SPECIES_OGERPON_HEARTHFLAME || speciesId == SPECIES_OGERPON_HEARTHFLAME_TERA || speciesId == SPECIES_OGERPON_CORNERSTONE || speciesId == SPECIES_OGERPON_CORNERSTONE_TERA) type = gBattleMons[battler].types[1]; - end = StringCopy(txtPtr, gTypesInfo[type].name); } - // Max Guard is a Normal-type move - else if (gMovesInfo[moveInfo->moves[gMoveSelectionCursor[battler]]].category == DAMAGE_CATEGORY_STATUS + else if (GetMoveCategory(moveInfo->moves[gMoveSelectionCursor[battler]]) == DAMAGE_CATEGORY_STATUS && (GetActiveGimmick(battler) == GIMMICK_DYNAMAX || IsGimmickSelected(battler, GIMMICK_DYNAMAX))) { - type = TYPE_NORMAL; - end = StringCopy(txtPtr, gTypesInfo[type].name); + type = TYPE_NORMAL; // Max Guard is always a Normal-type move } else if (moveInfo->moves[gMoveSelectionCursor[battler]] == MOVE_TERA_STARSTORM) { if (gBattleMons[battler].species == SPECIES_TERAPAGOS_STELLAR || (IsGimmickSelected(battler, GIMMICK_TERA) && gBattleMons[battler].species == SPECIES_TERAPAGOS_TERASTAL)) type = TYPE_STELLAR; - end = StringCopy(txtPtr, gTypesInfo[type].name); } - else + else if (P_SHOW_DYNAMIC_TYPES) // Non-vanilla changes to battle UI showing dynamic types { - end = StringCopy(txtPtr, gTypesInfo[type].name); + struct Pokemon *mon = &gPlayerParty[gBattlerPartyIndexes[battler]]; + type = CheckDynamicMoveType(mon, moveInfo->moves[gMoveSelectionCursor[battler]], battler); } + end = StringCopy(txtPtr, gTypesInfo[type].name); PrependFontIdToFit(txtPtr, end, FONT_NORMAL, WindowWidthPx(B_WIN_MOVE_TYPE) - 25); BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_MOVE_TYPE); @@ -1703,9 +1846,8 @@ static void MoveSelectionDisplayMoveDescription(u32 battler) { struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct*)(&gBattleResources->bufferA[battler][4]); u16 move = moveInfo->moves[gMoveSelectionCursor[battler]]; - u16 pwr = gMovesInfo[move].power; - u16 acc = gMovesInfo[move].accuracy; - u8 cat = gMovesInfo[move].category; + u16 pwr = GetMovePower(move); + u16 acc = GetMoveAccuracy(move); u8 pwr_num[3], acc_num[3]; u8 cat_desc[7] = _("CAT: "); @@ -1733,26 +1875,23 @@ static void MoveSelectionDisplayMoveDescription(u32 battler) StringAppend(gDisplayedStringBattle, acc_desc); StringAppend(gDisplayedStringBattle, acc_num); StringAppend(gDisplayedStringBattle, gText_NewLine); - if (gMovesInfo[move].effect == EFFECT_PLACEHOLDER) - StringAppend(gDisplayedStringBattle, gNotDoneYetDescription); - else - StringAppend(gDisplayedStringBattle, gMovesInfo[move].description); + StringAppend(gDisplayedStringBattle, GetMoveDescription(move)); BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_MOVE_DESCRIPTION); if (gCategoryIconSpriteId == 0xFF) gCategoryIconSpriteId = CreateSprite(&gSpriteTemplate_CategoryIcons, 38, 64, 1); - StartSpriteAnim(&gSprites[gCategoryIconSpriteId], cat); + StartSpriteAnim(&gSprites[gCategoryIconSpriteId], GetBattleMoveCategory(move)); CopyWindowToVram(B_WIN_MOVE_DESCRIPTION, COPYWIN_FULL); } -void MoveSelectionCreateCursorAt(u8 cursorPosition, u8 arg1) +void MoveSelectionCreateCursorAt(u8 cursorPosition, u8 baseTileNum) { u16 src[2]; + src[0] = baseTileNum + 1; + src[1] = baseTileNum + 2; - src[0] = arg1 + 1; - src[1] = arg1 + 2; CopyToBgTilemapBufferRect_ChangePalette(0, src, 9 * (cursorPosition & 1) + 1, 55 + (cursorPosition & 2), 1, 2, 0x11); CopyBgTilemapBufferToVram(0); } @@ -1760,19 +1899,19 @@ void MoveSelectionCreateCursorAt(u8 cursorPosition, u8 arg1) void MoveSelectionDestroyCursorAt(u8 cursorPosition) { u16 src[2]; - src[0] = 32; src[1] = 32; + CopyToBgTilemapBufferRect_ChangePalette(0, src, 9 * (cursorPosition & 1) + 1, 55 + (cursorPosition & 2), 1, 2, 0x11); CopyBgTilemapBufferToVram(0); } -void ActionSelectionCreateCursorAt(u8 cursorPosition, u8 arg1) +void ActionSelectionCreateCursorAt(u8 cursorPosition, u8 baseTileNum) { u16 src[2]; - src[0] = 1; src[1] = 2; + CopyToBgTilemapBufferRect_ChangePalette(0, src, 7 * (cursorPosition & 1) + 16, 35 + (cursorPosition & 2), 1, 2, 0x11); CopyBgTilemapBufferToVram(0); } @@ -1780,19 +1919,19 @@ void ActionSelectionCreateCursorAt(u8 cursorPosition, u8 arg1) void ActionSelectionDestroyCursorAt(u8 cursorPosition) { u16 src[2]; - src[0] = 32; src[1] = 32; + CopyToBgTilemapBufferRect_ChangePalette(0, src, 7 * (cursorPosition & 1) + 16, 35 + (cursorPosition & 2), 1, 2, 0x11); CopyBgTilemapBufferToVram(0); } -void SetCB2ToReshowScreenAfterMenu(void) +void CB2_SetUpReshowBattleScreenAfterMenu(void) { SetMainCallback2(ReshowBattleScreenAfterMenu); } -void SetCB2ToReshowScreenAfterMenu2(void) +void CB2_SetUpReshowBattleScreenAfterMenu2(void) { SetMainCallback2(ReshowBattleScreenAfterMenu); } @@ -1848,6 +1987,9 @@ static u32 PlayerGetTrainerBackPicId(void) return trainerPicId; } +// In emerald it's possible to have a tag battle in the battle frontier facilities with AI +// which use the front sprite for both the player and the partner as opposed to any other battles (including the one with Steven) +// that use an animated back pic. static void PlayerHandleDrawTrainerPic(u32 battler) { bool32 isFrontPic; @@ -1922,17 +2064,28 @@ void PlayerHandleBallThrowAnim(u32 battler) static void PlayerHandlePause(u32 battler) { - u8 var = gBattleResources->bufferA[battler][1]; + u8 timer = gBattleResources->bufferA[battler][1]; + + while (timer != 0) + timer--; - while (var) - --var; PlayerBufferExecCompleted(battler); } +static void PlayerHandleMoveAnimation(u32 battler) +{ + BtlController_HandleMoveAnimation(battler); +} + +static void PlayerHandlePrintString(u32 battler) +{ + BtlController_HandlePrintString(battler); +} + static void PlayerHandlePrintSelectionString(u32 battler) { if (GetBattlerSide(battler) == B_SIDE_PLAYER) - BtlController_HandlePrintString(battler); + PlayerHandlePrintString(battler); else PlayerBufferExecCompleted(battler); } @@ -1974,43 +2127,116 @@ static void PlayerHandleChooseAction(u32 battler) ActionSelectionCreateCursorAt(gActionSelectionCursor[battler], 0); PREPARE_MON_NICK_BUFFER(gBattleTextBuff1, battler, gBattlerPartyIndexes[battler]); BattleStringExpandPlaceholdersToDisplayedString(gText_WhatWillPkmnDo); - BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_ACTION_PROMPT); + BreakStringAutomatic(gDisplayedStringBattle, WindowWidthPx(B_WIN_ACTION_PROMPT), 2, FONT_NORMAL); + + if (B_SHOW_PARTNER_TARGET && gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && IsBattlerAlive(B_POSITION_PLAYER_RIGHT)) + { + StringCopy(gStringVar1, COMPOUND_STRING("Partner will use:\n")); + u32 move = gBattleMons[B_POSITION_PLAYER_RIGHT].moves[*(gBattleStruct->chosenMovePositions + B_POSITION_PLAYER_RIGHT)]; + StringAppend(gStringVar1, GetMoveName(move)); + u32 moveTarget = GetBattlerMoveTargetType(B_POSITION_PLAYER_RIGHT, move); + if (moveTarget == MOVE_TARGET_SELECTED) + { + if (gBattleStruct->aiChosenTarget[B_POSITION_PLAYER_RIGHT] == B_POSITION_OPPONENT_LEFT) + StringAppend(gStringVar1, COMPOUND_STRING(" -{UP_ARROW}")); + else if (gBattleStruct->aiChosenTarget[B_POSITION_PLAYER_RIGHT] == B_POSITION_OPPONENT_RIGHT) + StringAppend(gStringVar1, COMPOUND_STRING(" {UP_ARROW}-")); + else if (gBattleStruct->aiChosenTarget[B_POSITION_PLAYER_RIGHT] == B_POSITION_PLAYER_LEFT) + StringAppend(gStringVar1, COMPOUND_STRING(" {DOWN_ARROW}-")); + else if (gBattleStruct->aiChosenTarget[B_POSITION_PLAYER_RIGHT] == B_POSITION_PLAYER_RIGHT) + StringAppend(gStringVar1, COMPOUND_STRING(" {DOWN_ARROW}-")); + } + else if (moveTarget == MOVE_TARGET_BOTH) + { + StringAppend(gStringVar1, COMPOUND_STRING(" {UP_ARROW}{UP_ARROW}")); + } + else if (moveTarget == MOVE_TARGET_FOES_AND_ALLY) + { + StringAppend(gStringVar1, COMPOUND_STRING(" {V_D_ARROW}{UP_ARROW}")); + } + else if (moveTarget == MOVE_TARGET_ALL_BATTLERS) + { + StringAppend(gStringVar1, COMPOUND_STRING(" {V_D_ARROW}{V_D_ARROW}")); + } + BattlePutTextOnWindow(gStringVar1, B_WIN_ACTION_PROMPT); + } + else + { + BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_ACTION_PROMPT); + } } -static void HandleChooseMoveAfterDma3(u32 battler) +static void PlayerHandleYesNoBox(u32 battler) +{ + if (GetBattlerSide(battler) == B_SIDE_PLAYER) + { + HandleBattleWindow(YESNOBOX_X_Y, 0); + BattlePutTextOnWindow(gText_BattleYesNoChoice, B_WIN_YESNO); + gMultiUsePlayerCursor = 1; + BattleCreateYesNoCursorAt(1); + gBattlerControllerFuncs[battler] = PlayerHandleYesNoInput; + } + else + { + PlayerBufferExecCompleted(battler); + } +} + +void HandleChooseMoveAfterDma3(u32 battler) { if (!IsDma3ManagerBusyWithBgCopy()) { gBattle_BG0_X = 0; - gBattle_BG0_Y = 320; + gBattle_BG0_Y = DISPLAY_HEIGHT * 2; gBattlerControllerFuncs[battler] = HandleInputChooseMove; } } -static void PlayerHandleChooseMove(u32 battler) +// arenaMindPoints is used here as a placeholder for a timer. + +// static void PlayerChooseMoveInBattlePalace(u32 battler) +// { +// if (--*(gBattleStruct->arenaMindPoints + battler) == 0) +// { +// gBattlePalaceMoveSelectionRngValue = gRngValue; +// BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, ChooseMoveAndTargetInBattlePalace(battler)); +// PlayerBufferExecCompleted(battler); +// } +// } + +void PlayerHandleChooseMove(u32 battler) { - struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleResources->bufferA[battler][4]); + if (gBattleTypeFlags & BATTLE_TYPE_PALACE) + { + // *(gBattleStruct->arenaMindPoints + battler) = 8; + // gBattlerControllerFuncs[battler] = PlayerChooseMoveInBattlePalace; + } + else + { + struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleResources->bufferA[battler][4]); - InitMoveSelectionsVarsAndStrings(battler); - gBattleStruct->gimmick.playerSelect = FALSE; + InitMoveSelectionsVarsAndStrings(battler); + gBattleStruct->gimmick.playerSelect = FALSE; - AssignUsableZMoves(battler, moveInfo->moves); - gBattleStruct->zmove.viable = (gBattleStruct->zmove.possibleZMoves[battler] & gBitTable[gMoveSelectionCursor[battler]]) != 0; + AssignUsableZMoves(battler, moveInfo->moves); + gBattleStruct->zmove.viable = (gBattleStruct->zmove.possibleZMoves[battler] & (1u << gMoveSelectionCursor[battler])) != 0; - if (!IsGimmickTriggerSpriteActive()) - gBattleStruct->gimmick.triggerSpriteId = 0xFF; - if (!(gBattleStruct->gimmick.usableGimmick[battler] == GIMMICK_Z_MOVE && !gBattleStruct->zmove.viable)) - CreateGimmickTriggerSprite(battler); + if (!IsGimmickTriggerSpriteActive()) + gBattleStruct->gimmick.triggerSpriteId = 0xFF; + if (!(gBattleStruct->gimmick.usableGimmick[battler] == GIMMICK_Z_MOVE && !gBattleStruct->zmove.viable)) + CreateGimmickTriggerSprite(battler); - gBattlerControllerFuncs[battler] = HandleChooseMoveAfterDma3; + gBattlerControllerFuncs[battler] = HandleChooseMoveAfterDma3; + } } void InitMoveSelectionsVarsAndStrings(u32 battler) { + LoadTypeIcons(battler); MoveSelectionDisplayMoveNames(battler); gMultiUsePlayerCursor = 0xFF; MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0); - MoveSelectionDisplayPpString(); + MoveSelectionDisplayPpString(battler); MoveSelectionDisplayPpNumber(battler); MoveSelectionDisplayMoveType(battler); } @@ -2034,14 +2260,23 @@ static void PlayerHandleChoosePokemon(u32 battler) for (i = 0; i < ARRAY_COUNT(gBattlePartyCurrentOrder); i++) gBattlePartyCurrentOrder[i] = gBattleResources->bufferA[battler][4 + i]; - gBattleControllerData[battler] = CreateTask(TaskDummy, 0xFF); - gTasks[gBattleControllerData[battler]].data[0] = gBattleResources->bufferA[battler][1] & 0xF; - *(&gBattleStruct->battlerPreventingSwitchout) = gBattleResources->bufferA[battler][1] >> 4; - *(&gBattleStruct->prevSelectedPartySlot) = gBattleResources->bufferA[battler][2]; - *(&gBattleStruct->abilityPreventingSwitchout) = (gBattleResources->bufferA[battler][3] & 0xFF) | (gBattleResources->bufferA[battler][7] << 8); - BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 0x10, RGB_BLACK); - gBattlerControllerFuncs[battler] = OpenPartyMenuToChooseMon; - gBattlerInMenuId = battler; + if (gBattleTypeFlags & BATTLE_TYPE_ARENA && (gBattleResources->bufferA[battler][1] & 0xF) != PARTY_ACTION_CANT_SWITCH + && (gBattleResources->bufferA[battler][1] & 0xF) != PARTY_ACTION_CHOOSE_FAINTED_MON) + { + // BtlController_EmitChosenMonReturnValue(battler, BUFFER_B, gBattlerPartyIndexes[battler] + 1, gBattlePartyCurrentOrder); + // PlayerBufferExecCompleted(battler); + } + else + { + gBattleControllerData[battler] = CreateTask(TaskDummy, 0xFF); + gTasks[gBattleControllerData[battler]].data[0] = gBattleResources->bufferA[battler][1] & 0xF; + *(&gBattleStruct->battlerPreventingSwitchout) = gBattleResources->bufferA[battler][1] >> 4; + *(&gBattleStruct->prevSelectedPartySlot) = gBattleResources->bufferA[battler][2]; + *(&gBattleStruct->abilityPreventingSwitchout) = (gBattleResources->bufferA[battler][3] & 0xFF) | (gBattleResources->bufferA[battler][7] << 8); + BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 0x10, RGB_BLACK); + gBattlerControllerFuncs[battler] = OpenPartyMenuToChooseMon; + gBattlerInMenuId = battler; + } } static void PlayerHandleCmd23(u32 battler) @@ -2056,7 +2291,6 @@ static void PlayerHandleHealthBarUpdate(u32 battler) BtlController_HandleHealthBarUpdate(battler, TRUE); } -// consider moving to battle_controller, shared with battle_controller_pokedude void PlayerHandleExpUpdate(u32 battler) { u8 monId = gBattleResources->bufferA[battler][1]; @@ -2096,16 +2330,16 @@ static void PlayerHandleStatusXor(u32 battler) static void PlayerHandleDMA3Transfer(u32 battler) { u32 dstArg = gBattleResources->bufferA[battler][1] - | (gBattleResources->bufferA[battler][2] << 8) - | (gBattleResources->bufferA[battler][3] << 16) - | (gBattleResources->bufferA[battler][4] << 24); + | (gBattleResources->bufferA[battler][2] << 8) + | (gBattleResources->bufferA[battler][3] << 16) + | (gBattleResources->bufferA[battler][4] << 24); u16 sizeArg = gBattleResources->bufferA[battler][5] | (gBattleResources->bufferA[battler][6] << 8); const u8 *src = &gBattleResources->bufferA[battler][7]; u8 *dst = (u8 *)(dstArg); u32 size = sizeArg; - while (TRUE) + while (1) { if (size <= 0x1000) { @@ -2156,19 +2390,8 @@ static void PlayerHandleIntroTrainerBallThrow(u32 battler) BtlController_HandleIntroTrainerBallThrow(battler, 0xD6F8, trainerPal, 31, Intro_TryShinyAnimShowHealthbox, PlayerThrowBall_StartAnimLinearTranslation); } -void SpriteCB_FreePlayerSpriteLoadMonSpriteOld(struct Sprite *sprite) -{ - u8 battlerId = sprite->data[5]; - - FreeSpriteOamMatrix(sprite); - FreeSpritePaletteByTag(GetSpritePaletteTagByPaletteNum(sprite->oam.paletteNum)); - DestroySprite(sprite); - BattleLoadMonSpriteGfx(&gPlayerParty[gBattlerPartyIndexes[battlerId]], battlerId); - StartSpriteAnim(&gSprites[gBattlerSpriteIds[battlerId]], 0); -} - static void PlayerHandleDrawPartyStatusSummary(u32 battler) -{ +{ BtlController_HandleDrawPartyStatusSummary(battler, B_SIDE_PLAYER, TRUE); } @@ -2223,7 +2446,9 @@ static void PlayerHandleResetActionMoveSelection(u32 battler) static void PlayerHandleEndLinkBattle(u32 battler) { + // RecordedBattle_RecordAllBattlerData(&gBattleResources->bufferA[battler][4]); gBattleOutcome = gBattleResources->bufferA[battler][1]; + // gSaveBlock2Ptr->frontier.disableRecordBattle = gBattleResources->bufferA[battler][2]; FadeOutMapMusic(5); BeginFastPaletteFade(3); PlayerBufferExecCompleted(battler); diff --git a/src/battle_controller_player_partner.c b/src/battle_controller_player_partner.c index f5775beed..de3dd0659 100644 --- a/src/battle_controller_player_partner.c +++ b/src/battle_controller_player_partner.c @@ -70,7 +70,7 @@ static void (*const sPlayerPartnerBufferCommands[CONTROLLER_CMDS_COUNT])(u32 bat [CONTROLLER_PRINTSTRING] = BtlController_HandlePrintString, [CONTROLLER_PRINTSTRINGPLAYERONLY] = BtlController_Empty, [CONTROLLER_CHOOSEACTION] = PlayerPartnerHandleChooseAction, - [CONTROLLER_UNKNOWNYESNOBOX] = BtlController_Empty, + [CONTROLLER_YESNOBOX] = BtlController_Empty, [CONTROLLER_CHOOSEMOVE] = PlayerPartnerHandleChooseMove, [CONTROLLER_OPENBAG] = BtlController_Empty, [CONTROLLER_CHOOSEPOKEMON] = PlayerPartnerHandleChoosePokemon, diff --git a/src/battle_controller_pokedude.c b/src/battle_controller_pokedude.c index 850ccc269..9eb6ff936 100644 --- a/src/battle_controller_pokedude.c +++ b/src/battle_controller_pokedude.c @@ -95,7 +95,7 @@ static void (*const sPokedudeBufferCommands[CONTROLLER_CMDS_COUNT])(u32 battler) [CONTROLLER_PRINTSTRING] = BtlController_HandlePrintString, // done [CONTROLLER_PRINTSTRINGPLAYERONLY] = PokedudeHandlePrintSelectionString, // done [CONTROLLER_CHOOSEACTION] = PokedudeHandleChooseAction, // done - [CONTROLLER_UNKNOWNYESNOBOX] = BtlController_Empty, // done + [CONTROLLER_YESNOBOX] = BtlController_Empty, // done [CONTROLLER_CHOOSEMOVE] = PokedudeHandleChooseMove, // done [CONTROLLER_OPENBAG] = PokedudeHandleChooseItem, // done [CONTROLLER_CHOOSEPOKEMON] = PokedudeHandleChoosePokemon, // done diff --git a/src/battle_controller_recorded_opponent.c b/src/battle_controller_recorded_opponent.c index da36608cf..c3450e63b 100644 --- a/src/battle_controller_recorded_opponent.c +++ b/src/battle_controller_recorded_opponent.c @@ -74,7 +74,7 @@ static void (*const sRecordedOpponentBufferCommands[CONTROLLER_CMDS_COUNT])(u32 [CONTROLLER_PRINTSTRING] = RecordedOpponentHandlePrintString, [CONTROLLER_PRINTSTRINGPLAYERONLY] = BtlController_Empty, [CONTROLLER_CHOOSEACTION] = RecordedOpponentHandleChooseAction, - [CONTROLLER_UNKNOWNYESNOBOX] = BtlController_Empty, + [CONTROLLER_YESNOBOX] = BtlController_Empty, [CONTROLLER_CHOOSEMOVE] = RecordedOpponentHandleChooseMove, [CONTROLLER_OPENBAG] = RecordedOpponentHandleChooseItem, [CONTROLLER_CHOOSEPOKEMON] = RecordedOpponentHandleChoosePokemon, diff --git a/src/battle_controller_recorded_player.c b/src/battle_controller_recorded_player.c index e49a9e7e8..2852b30c8 100644 --- a/src/battle_controller_recorded_player.c +++ b/src/battle_controller_recorded_player.c @@ -72,7 +72,7 @@ static void (*const sRecordedPlayerBufferCommands[CONTROLLER_CMDS_COUNT])(u32 ba [CONTROLLER_PRINTSTRING] = RecordedPlayerHandlePrintString, [CONTROLLER_PRINTSTRINGPLAYERONLY] = BtlController_Empty, [CONTROLLER_CHOOSEACTION] = RecordedPlayerHandleChooseAction, - [CONTROLLER_UNKNOWNYESNOBOX] = BtlController_Empty, + [CONTROLLER_YESNOBOX] = BtlController_Empty, [CONTROLLER_CHOOSEMOVE] = RecordedPlayerHandleChooseMove, [CONTROLLER_OPENBAG] = RecordedPlayerHandleChooseItem, [CONTROLLER_CHOOSEPOKEMON] = RecordedPlayerHandleChoosePokemon, diff --git a/src/battle_controller_safari.c b/src/battle_controller_safari.c index 141cfe7dd..24488746f 100644 --- a/src/battle_controller_safari.c +++ b/src/battle_controller_safari.c @@ -52,7 +52,7 @@ static void (*const sSafariBufferCommands[CONTROLLER_CMDS_COUNT])(u32 battler) = [CONTROLLER_PRINTSTRING] = BtlController_HandlePrintString, // done [CONTROLLER_PRINTSTRINGPLAYERONLY] = SafariHandlePrintSelectionString, // done [CONTROLLER_CHOOSEACTION] = SafariHandleChooseAction, // done - [CONTROLLER_UNKNOWNYESNOBOX] = BtlController_Empty, // done + [CONTROLLER_YESNOBOX] = BtlController_Empty, // done [CONTROLLER_CHOOSEMOVE] = BtlController_Empty, // done [CONTROLLER_OPENBAG] = SafariHandleChooseItem, // done [CONTROLLER_CHOOSEPOKEMON] = BtlController_Empty, // done diff --git a/src/data/items.h b/src/data/items.h index e8531ab06..9a7110891 100644 --- a/src/data/items.h +++ b/src/data/items.h @@ -2052,6 +2052,7 @@ const struct Item gItemsInfo[] = "A glass flute that " "awakens\nsleeping " "Pokémon."), + .notConsumed = TRUE, .pocket = POCKET_ITEMS, .type = ITEM_USE_PARTY_MENU, .fieldUseFunc = ItemUseOutOfBattle_Medicine, @@ -2070,6 +2071,7 @@ const struct Item gItemsInfo[] = "A glass flute that " "snaps Pokémon\n" "out of confusion."), + .notConsumed = TRUE, .pocket = POCKET_ITEMS, .type = ITEM_USE_PARTY_MENU, .fieldUseFunc = ItemUseOutOfBattle_CannotUse, @@ -2088,6 +2090,7 @@ const struct Item gItemsInfo[] = "A glass flute that " "snaps Pokémon\n" "out of attraction."), + .notConsumed = TRUE, .pocket = POCKET_ITEMS, .type = ITEM_USE_PARTY_MENU, .fieldUseFunc = ItemUseOutOfBattle_CannotUse, @@ -2109,6 +2112,7 @@ const struct Item gItemsInfo[] = "A glass flute that " "keeps away\nwild " "Pokémon."), + .notConsumed = TRUE, .pocket = POCKET_ITEMS, .type = ITEM_USE_PARTY_MENU, .fieldUseFunc = ItemUseOutOfBattle_BlackWhiteFlute, @@ -2125,6 +2129,7 @@ const struct Item gItemsInfo[] = .description = COMPOUND_STRING( "A glass flute that " "lures wild\nPokémon."), + .notConsumed = TRUE, .pocket = POCKET_ITEMS, .type = ITEM_USE_PARTY_MENU, .fieldUseFunc = ItemUseOutOfBattle_BlackWhiteFlute, diff --git a/src/graphics.c b/src/graphics.c index 3fc34e902..cf9a8a587 100644 --- a/src/graphics.c +++ b/src/graphics.c @@ -1937,3 +1937,8 @@ const u32 gBattleAnimBgPalette_ZMoveMountain[] = INCBIN_U32("graphics/battle_ani const u32 gBattleAnimBgTilemap_ZMoveMountain[] = INCBIN_U32("graphics/battle_anims/backgrounds/zmove_mountain.bin.lz"); const u32 gBattleAnimBgPalette_SteelBeam[] = INCBIN_U32("graphics/battle_anims/backgrounds/steel_beam.gbapal.lz"); + +const u32 gBattleIcons_Gfx1[] = INCBIN_U32("graphics/types/battle_icons1.4bpp.lz"); +const u32 gBattleIcons_Gfx2[] = INCBIN_U32("graphics/types/battle_icons2.4bpp.lz"); +const u32 gBattleIcons_Pal1[] = INCBIN_U32("graphics/types/battle_icons1.gbapal.lz"); +const u32 gBattleIcons_Pal2[] = INCBIN_U32("graphics/types/battle_icons2.gbapal.lz"); diff --git a/src/item.c b/src/item.c index 337309a23..8b86828b3 100644 --- a/src/item.c +++ b/src/item.c @@ -662,6 +662,11 @@ u8 ItemId_GetImportance(u16 itemId) return gItemsInfo[SanitizeItemId(itemId)].importance; } +u8 ItemId_GetConsumability(u16 itemId) +{ + return !gItemsInfo[SanitizeItemId(itemId)].notConsumed; +} + u8 ItemId_GetPocket(u16 itemId) { return gItemsInfo[SanitizeItemId(itemId)].pocket; diff --git a/src/item_menu.c b/src/item_menu.c index 29fd9f057..7c566ba28 100644 --- a/src/item_menu.c +++ b/src/item_menu.c @@ -351,7 +351,7 @@ void CB2_BagMenuFromStartMenu(void) void CB2_BagMenuFromBattle(void) { - GoToBagMenu(ITEMMENULOCATION_BATTLE, OPEN_BAG_LAST, SetCB2ToReshowScreenAfterMenu2); + GoToBagMenu(ITEMMENULOCATION_BATTLE, OPEN_BAG_LAST, CB2_SetUpReshowBattleScreenAfterMenu2); } static void CB2_BagMenuRun(void) @@ -949,7 +949,7 @@ void Bag_BeginCloseWin0Animation(void) gBagMenuState.bagOpen = FALSE; } -void CB2_SetUpReshowBattleScreenAfterMenu(void) +void SetBagOpenFalse(void) { gBagMenuState.bagOpen = FALSE; } @@ -2101,7 +2101,7 @@ void InitOldManBag(void) BackUpPlayerBag(); AddBagItem(ITEM_POTION, 1); AddBagItem(ITEM_POKE_BALL, 1); - GoToBagMenu(ITEMMENULOCATION_OLD_MAN, OPEN_BAG_ITEMS, SetCB2ToReshowScreenAfterMenu2); + GoToBagMenu(ITEMMENULOCATION_OLD_MAN, OPEN_BAG_ITEMS, CB2_SetUpReshowBattleScreenAfterMenu2); } #define tFrameCounter data[8] @@ -2183,11 +2183,11 @@ void InitPokedudeBag(u8 a0) location = a0; break; case ITEMMENULOCATION_TTVSCR_STATUS: - cb2 = SetCB2ToReshowScreenAfterMenu2; + cb2 = CB2_SetUpReshowBattleScreenAfterMenu2; location = ITEMMENULOCATION_TTVSCR_STATUS; break; case ITEMMENULOCATION_TTVSCR_CATCHING: - cb2 = SetCB2ToReshowScreenAfterMenu2; + cb2 = CB2_SetUpReshowBattleScreenAfterMenu2; location = ITEMMENULOCATION_TTVSCR_CATCHING; break; } diff --git a/src/line_break.c b/src/line_break.c new file mode 100644 index 000000000..b8888f501 --- /dev/null +++ b/src/line_break.c @@ -0,0 +1,281 @@ +#include "global.h" +#include "line_break.h" +#include "text.h" +#include "malloc.h" + +void StripLineBreaks(u8 *src) +{ + u32 currIndex = 0; + while (src[currIndex] != EOS) + { + if (src[currIndex] == CHAR_PROMPT_SCROLL || src[currIndex] == CHAR_NEWLINE) + src[currIndex] = CHAR_SPACE; + currIndex++; + } +} + +void BreakStringAutomatic(u8 *src, u32 maxWidth, u32 screenLines, u8 fontId) +{ + u32 currIndex = 0; + u8 *currSrc = src; + while (src[currIndex] != EOS) + { + if (src[currIndex] == CHAR_PROMPT_CLEAR) + { + u8 replacedChar = src[currIndex + 1]; + src[currIndex + 1] = EOS; + BreakSubStringAutomatic(currSrc, maxWidth, screenLines, fontId); + src[currIndex + 1] = replacedChar; + currSrc = &src[currIndex + 1]; + } + currIndex++; + } + BreakSubStringAutomatic(currSrc, maxWidth, screenLines, fontId); +} + +void BreakSubStringAutomatic(u8 *src, u32 maxWidth, u32 screenLines, u8 fontId) +{ + // If the string already has line breaks, don't interfere with them + if (StringHasManualBreaks(src)) + return; + // Sanity check + if (src[0] == EOS) + return; + u32 numChars = 1; + u32 numWords = 1; + u32 currWordIndex = 0; + u32 currWordLength = 1; + bool32 isPrevCharSplitting = FALSE; + bool32 isCurrCharSplitting; + // Get numbers of chars in string and count words + while (src[numChars] != EOS) + { + isCurrCharSplitting = IsWordSplittingChar(src, numChars); + if (isCurrCharSplitting && !isPrevCharSplitting) + numWords++; + isPrevCharSplitting = isCurrCharSplitting; + numChars++; + } + // Allocate enough space for word data + struct StringWord *allWords = Alloc(numWords*sizeof(struct StringWord)); + + allWords[currWordIndex].startIndex = 0; + allWords[currWordIndex].width = 0; + isPrevCharSplitting = FALSE; + // Fill in word begin index and lengths + for (u32 i = 1; i < numChars; i++) + { + isCurrCharSplitting = IsWordSplittingChar(src, i); + if (isCurrCharSplitting && !isPrevCharSplitting) + { + allWords[currWordIndex].length = currWordLength; + currWordIndex++; + currWordLength = 0; + } + else if (!isCurrCharSplitting && isPrevCharSplitting) + { + allWords[currWordIndex].startIndex = i; + allWords[currWordIndex].width = 0; + currWordLength++; + } + else + { + currWordLength++; + } + isPrevCharSplitting = isCurrCharSplitting; + } + allWords[currWordIndex].length = currWordLength; + + // Fill in individual word widths + for (u32 i = 0; i < numWords; i++) + { + for (u32 j = 0; j < allWords[i].length; j++) + allWords[i].width += GetGlyphWidth(src[allWords[i].startIndex + j], FALSE, fontId); + } + + // Step 1: Does it all fit one one line? Then no break + // Step 2: Try to split across minimum number of lines + u32 spaceWidth = GetGlyphWidth(0, FALSE, fontId); + u32 totalWidth = allWords[0].width; + // Calculate total widths without any line breaks + for (u32 i = 1; i < numWords; i++) + totalWidth += allWords[i].width + spaceWidth; + + // If it doesn't fit on 1 line, do fancy line break calculation + // NOTE: Currently the line break calculation isn't fancy + if (totalWidth > maxWidth) + { + // Figure out how many lines are needed with naive method + u32 currLineWidth = 0; + u32 totalLines = 1; + bool32 shouldTryAgain; + for (currWordIndex = 0; currWordIndex < numWords; currWordIndex++) + { + if (currLineWidth + allWords[currWordIndex].length > maxWidth) + { + totalLines++; + currLineWidth = allWords[currWordIndex].width; + } + else + { + currLineWidth += allWords[currWordIndex].width + spaceWidth; + } + } + // LINE LAYOUT STARTS HERE + struct StringLine *stringLines; + do + { + shouldTryAgain = FALSE; + u16 targetLineWidth = totalWidth/totalLines; + stringLines = Alloc(totalLines*sizeof(struct StringLine)); + for (u32 lineIndex = 0; lineIndex < totalLines; lineIndex++) + { + stringLines[lineIndex].numWords = 0; + stringLines[lineIndex].spaceWidth = spaceWidth; + stringLines[lineIndex].extraSpaceWidth = 0; + } + currWordIndex = 0; + u16 currLineIndex = 0; + stringLines[currLineIndex].words = &allWords[currWordIndex]; + stringLines[currLineIndex].numWords = 1; + currLineWidth = allWords[currWordIndex].width; + currWordIndex++; + while (currWordIndex < numWords) + { + if (currLineWidth + spaceWidth + allWords[currWordIndex].width > maxWidth) + { + // go to next line + currLineIndex++; + if (currLineIndex == totalLines) + { + totalLines++; + Free(stringLines); + shouldTryAgain = TRUE; + break; + } + stringLines[currLineIndex].words = &allWords[currWordIndex]; + stringLines[currLineIndex].numWords = 1; + currLineWidth = allWords[currWordIndex].width; + currWordIndex++; + } + else if (currLineWidth > targetLineWidth) + { + // go to next line + currLineIndex++; + if (currLineIndex == totalLines) + { + totalLines++; + Free(stringLines); + shouldTryAgain = TRUE; + break; + } + stringLines[currLineIndex].words = &allWords[currWordIndex]; + stringLines[currLineIndex].numWords = 1; + currLineWidth = allWords[currWordIndex].width; + currWordIndex++; + } + else + { + // continue on current line + // add word and space width + currLineWidth += spaceWidth + allWords[currWordIndex].width; + stringLines[currLineIndex].numWords++; + currWordIndex++; + } + } + } while (shouldTryAgain); + //u32 currBadness = GetStringBadness(stringLines, totalLines, maxWidth); + BuildNewString(stringLines, totalLines, screenLines, src); + Free(stringLines); + } + + Free(allWords); +} + +// Only allow word splitting on allowed chars +bool32 IsWordSplittingChar(const u8 *src, u32 index) +{ + switch (src[index]) + { + case CHAR_SPACE: + return TRUE; + default: + return FALSE; + } +} + +// Badness calculation +// unfilled lines scale linerarly +// jagged lines scales by the square +// runts scale linearly +// numbers not final +// ISN'T ACTUALLY USED RIGHT NOW +u32 GetStringBadness(struct StringLine *stringLines, u32 numLines, u32 maxWidth) +{ + u32 badness = 0; + u32 *lineWidths = Alloc(numLines*4); + u32 widestWidth = 0; + for (u32 i = 0; i < numLines; i++) + { + lineWidths[i] = 0; + for (u32 j = 0; j < stringLines[i].numWords; j++) + lineWidths[i] += stringLines[i].words[j].width; + lineWidths[i] += (stringLines[i].numWords-1)*stringLines[i].spaceWidth; + if (lineWidths[i] > widestWidth) + widestWidth = lineWidths[i]; + if (stringLines[i].numWords == 1) + badness += BADNESS_RUNT; + } + for (u32 i = 0; i < numLines; i++) + { + u32 extraSpaceWidth = 0; + if (lineWidths[i] != widestWidth) + { + // Not the best way to do this, ideally a line should be allowed to get longer than current widest + // line. But then the widest line has to be recalculated. + while (lineWidths[i] + (extraSpaceWidth + 1) * (stringLines[i].numWords - 1) < widestWidth && extraSpaceWidth < MAX_SPACE_WIDTH) + extraSpaceWidth++; + lineWidths[i] += extraSpaceWidth*(stringLines[i].numWords-1); + } + badness += (maxWidth - lineWidths[i]) * BADNESS_UNFILLED; + u32 baseBadness = (widestWidth - lineWidths[i]) * BADNESS_JAGGED; + badness += baseBadness*baseBadness; + stringLines[i].extraSpaceWidth = extraSpaceWidth; + } + Free(lineWidths); + return badness; +} + +// Build the new string from the data stored in the StringLine structs +void BuildNewString(struct StringLine *stringLines, u32 numLines, u32 maxLines, u8 *str) +{ + u32 srcCharIndex = 0; + for (u32 lineIndex = 0; lineIndex < numLines; lineIndex++) + { + srcCharIndex += stringLines[lineIndex].words[0].length; + for (u32 wordIndex = 1; wordIndex < stringLines[lineIndex].numWords; wordIndex++) + // Add length of word and a space + srcCharIndex += stringLines[lineIndex].words[wordIndex].length + 1; + if (lineIndex + 1 < numLines) + { + // Add the appropriate line break depending on line number + if (lineIndex >= maxLines - 1 && numLines > maxLines) + str[srcCharIndex] = CHAR_PROMPT_SCROLL; + else + str[srcCharIndex] = CHAR_NEWLINE; + srcCharIndex++; + } + } +} + +bool32 StringHasManualBreaks(u8 *src) +{ + u32 charIndex = 0; + while (src[charIndex] != EOS) + { + if (src[charIndex] == CHAR_PROMPT_SCROLL || src[charIndex] == CHAR_NEWLINE) + return TRUE; + charIndex++; + } + return FALSE; +} diff --git a/src/party_menu.c b/src/party_menu.c index e11ddbf3a..3e8c277f6 100644 --- a/src/party_menu.c +++ b/src/party_menu.c @@ -6562,7 +6562,7 @@ static u8 GetPartyLayoutFromBattleType(void) return PARTY_LAYOUT_DOUBLE; } -void OpenPartyMenuInTutorialBattle(u8 partyAction) +void OpenPartyMenuInBattle(u8 partyAction) { if (!BtlCtrl_OakOldMan_TestState2Flag(FIRST_BATTLE_MSG_FLAG_PARTY_MENU) && (gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE)) { @@ -6572,7 +6572,7 @@ void OpenPartyMenuInTutorialBattle(u8 partyAction) FALSE, PARTY_MSG_NONE, Task_FirstBattleEnterParty_WaitFadeIn, - SetCB2ToReshowScreenAfterMenu); + CB2_SetUpReshowBattleScreenAfterMenu); BtlCtrl_OakOldMan_SetState2Flag(FIRST_BATTLE_MSG_FLAG_PARTY_MENU); } else @@ -6583,7 +6583,7 @@ void OpenPartyMenuInTutorialBattle(u8 partyAction) FALSE, PARTY_MSG_CHOOSE_MON, Task_HandleChooseMonInput, - SetCB2ToReshowScreenAfterMenu); + CB2_SetUpReshowBattleScreenAfterMenu); } ReshowBattleScreenDummy(); UpdatePartyToBattleOrder(); @@ -6591,7 +6591,7 @@ void OpenPartyMenuInTutorialBattle(u8 partyAction) void Pokedude_OpenPartyMenuInBattle(void) { - InitPartyMenu(PARTY_MENU_TYPE_IN_BATTLE, GetPartyLayoutFromBattleType(), PARTY_ACTION_CHOOSE_MON, FALSE, PARTY_MSG_CHOOSE_MON, Task_PartyMenu_Pokedude, SetCB2ToReshowScreenAfterMenu); + InitPartyMenu(PARTY_MENU_TYPE_IN_BATTLE, GetPartyLayoutFromBattleType(), PARTY_ACTION_CHOOSE_MON, FALSE, PARTY_MSG_CHOOSE_MON, Task_PartyMenu_Pokedude, CB2_SetUpReshowBattleScreenAfterMenu); ReshowBattleScreenDummy(); UpdatePartyToBattleOrder(); } @@ -6948,8 +6948,8 @@ static void UpdatePartyToFieldOrder(void) static void CB2_SetUpExitToBattleScreen(void) { - CB2_SetUpReshowBattleScreenAfterMenu(); - SetMainCallback2(SetCB2ToReshowScreenAfterMenu); + SetBagOpenFalse(); + SetMainCallback2(CB2_SetUpReshowBattleScreenAfterMenu); } void ShowPartyMenuToShowcaseMultiBattleParty(void) diff --git a/src/pokemon.c b/src/pokemon.c index 46000d437..dfc4be449 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -6561,3 +6561,11 @@ void UpdateDaysPassedSinceFormChange(u16 days) } } } + +u32 CheckDynamicMoveType(struct Pokemon *mon, u32 move, u32 battler) +{ + u32 moveType = GetDynamicMoveType(mon, move, battler, NULL); + if (moveType != TYPE_NONE) + return moveType; + return GetMoveType(move); +} diff --git a/src/tm_case.c b/src/tm_case.c index 2ebe80203..3a6f384de 100644 --- a/src/tm_case.c +++ b/src/tm_case.c @@ -1466,7 +1466,7 @@ static void Task_Pokedude_Run(u8 taskId) sTMCaseStaticResources.scrollOffset = sPokedudeBagBackup->scrollOffset; Free(sPokedudeBagBackup); CpuFastCopy(gPlttBufferFaded, gPlttBufferUnfaded, PLTT_SIZE); - CB2_SetUpReshowBattleScreenAfterMenu(); + SetBagOpenFalse(); BeginNormalPaletteFade(PALETTES_ALL, -2, 0, 16, 0); tPokedudeState++; } diff --git a/src/type_icons.c b/src/type_icons.c new file mode 100644 index 000000000..bd940ec84 --- /dev/null +++ b/src/type_icons.c @@ -0,0 +1,552 @@ +#include "global.h" +#include "battle.h" +#include "battle_anim.h" +#include "battle_controllers.h" +#include "battle_gimmick.h" +#include "decompress.h" +#include "graphics.h" +#include "pokedex.h" +#include "sprite.h" +#include "type_icons.h" + +static void LoadTypeSpritesAndPalettes(void); +static void LoadTypeIconsPerBattler(u32, u32); + +static bool32 UseDoubleBattleCoords(u32); + +static u32 GetMonPublicType(u32, u32); +static bool32 ShouldHideUncaughtType(u32); +static u32 GetMonDefensiveTeraType(struct Pokemon *, struct Pokemon*, u32, u32, u32, u32); +static u32 IsIllusionActiveAndTypeUnchanged(struct Pokemon*, u32, u32); + +static void CreateSpriteFromType(u32, bool32, u32[], u32, u32); +static bool32 ShouldSkipSecondType(u32[], u32); +static void SetTypeIconXY(s32*, s32*, u32, bool32, u32); + +static void CreateSpriteAndSetTypeSpriteAttributes(u32, u32 x, u32 y, u32, u32, bool32); +static bool32 ShouldFlipTypeIcon(bool32, u32, u32); + +static void SpriteCB_TypeIcon(struct Sprite*); +static void DestroyTypeIcon(struct Sprite*); +static void FreeAllTypeIconResources(void); +static bool32 ShouldHideTypeIcon(u32); +static s32 GetTypeIconHideMovement(bool32, u32); +static s32 GetTypeIconSlideMovement(bool32, u32, s32); +static s32 GetTypeIconBounceMovement(s32, u32); + +const struct Coords16 sTypeIconPositions[][2] = +{ + [B_POSITION_PLAYER_LEFT] = + { + [FALSE] = {221, 86}, + [TRUE] = {144, 71}, + }, + [B_POSITION_OPPONENT_LEFT] = + { + [FALSE] = {20, 26}, + [TRUE] = {97, 14}, + }, + [B_POSITION_PLAYER_RIGHT] = + { + [TRUE] = {156, 96}, + }, + [B_POSITION_OPPONENT_RIGHT] = + { + [TRUE] = {85, 39}, + }, +}; + +const union AnimCmd sSpriteAnim_TypeIcon_Normal[] = +{ + ANIMCMD_FRAME(TYPE_ICON_1_FRAME(TYPE_NORMAL), 0), + ANIMCMD_END +}; +const union AnimCmd sSpriteAnim_TypeIcon_Fighting[] = +{ + ANIMCMD_FRAME(TYPE_ICON_1_FRAME(TYPE_FIGHTING), 0), + ANIMCMD_END +}; +const union AnimCmd sSpriteAnim_TypeIcon_Flying[] = +{ + ANIMCMD_FRAME(TYPE_ICON_1_FRAME(TYPE_FLYING), 0), + ANIMCMD_END +}; +const union AnimCmd sSpriteAnim_TypeIcon_Poison[] = +{ + ANIMCMD_FRAME(TYPE_ICON_1_FRAME(TYPE_POISON), 0), + ANIMCMD_END +}; +const union AnimCmd sSpriteAnim_TypeIcon_Ground[] = +{ + ANIMCMD_FRAME(TYPE_ICON_1_FRAME(TYPE_GROUND), 0), + ANIMCMD_END +}; +const union AnimCmd sSpriteAnim_TypeIcon_Rock[] = +{ + ANIMCMD_FRAME(TYPE_ICON_1_FRAME(TYPE_ROCK), 0), + ANIMCMD_END +}; +const union AnimCmd sSpriteAnim_TypeIcon_Bug[] = +{ + ANIMCMD_FRAME(TYPE_ICON_1_FRAME(TYPE_BUG), 0), + ANIMCMD_END +}; +const union AnimCmd sSpriteAnim_TypeIcon_Ghost[] = +{ + ANIMCMD_FRAME(TYPE_ICON_1_FRAME(TYPE_GHOST), 0), + ANIMCMD_END +}; +const union AnimCmd sSpriteAnim_TypeIcon_Steel[] = +{ + ANIMCMD_FRAME(TYPE_ICON_1_FRAME(TYPE_STEEL), 0), + ANIMCMD_END +}; +const union AnimCmd sSpriteAnim_TypeIcon_Mystery[] = +{ + ANIMCMD_FRAME(TYPE_ICON_1_FRAME(TYPE_MYSTERY), 0), + ANIMCMD_END +}; + +const union AnimCmd sSpriteAnim_TypeIcon_Fire[] = +{ + ANIMCMD_FRAME(TYPE_ICON_2_FRAME(TYPE_FIRE), 0), + ANIMCMD_END +}; +const union AnimCmd sSpriteAnim_TypeIcon_Water[] = +{ + ANIMCMD_FRAME(TYPE_ICON_2_FRAME(TYPE_WATER), 0), + ANIMCMD_END +}; +const union AnimCmd sSpriteAnim_TypeIcon_Grass[] = +{ + ANIMCMD_FRAME(TYPE_ICON_2_FRAME(TYPE_GRASS), 0), + ANIMCMD_END +}; +const union AnimCmd sSpriteAnim_TypeIcon_Electric[] = +{ + ANIMCMD_FRAME(TYPE_ICON_2_FRAME(TYPE_ELECTRIC), 0), + ANIMCMD_END +}; +const union AnimCmd sSpriteAnim_TypeIcon_Psychic[] = +{ + ANIMCMD_FRAME(TYPE_ICON_2_FRAME(TYPE_PSYCHIC), 0), + ANIMCMD_END +}; +const union AnimCmd sSpriteAnim_TypeIcon_Ice[] = +{ + ANIMCMD_FRAME(TYPE_ICON_2_FRAME(TYPE_ICE), 0), + ANIMCMD_END +}; +const union AnimCmd sSpriteAnim_TypeIcon_Dragon[] = +{ + ANIMCMD_FRAME(TYPE_ICON_2_FRAME(TYPE_DRAGON), 0), + ANIMCMD_END +}; +const union AnimCmd sSpriteAnim_TypeIcon_Dark[] = +{ + ANIMCMD_FRAME(TYPE_ICON_2_FRAME(TYPE_DARK), 0), + ANIMCMD_END +}; +const union AnimCmd sSpriteAnim_TypeIcon_Fairy[] = +{ + ANIMCMD_FRAME(TYPE_ICON_2_FRAME(TYPE_FAIRY), 0), + ANIMCMD_END +}; + +const union AnimCmd *const sSpriteAnimTable_TypeIcons[] = +{ + [TYPE_NONE] = sSpriteAnim_TypeIcon_Mystery, + [TYPE_NORMAL] = sSpriteAnim_TypeIcon_Normal, + [TYPE_FIGHTING] = sSpriteAnim_TypeIcon_Fighting, + [TYPE_FLYING] = sSpriteAnim_TypeIcon_Flying, + [TYPE_POISON] = sSpriteAnim_TypeIcon_Poison, + [TYPE_GROUND] = sSpriteAnim_TypeIcon_Ground, + [TYPE_ROCK] = sSpriteAnim_TypeIcon_Rock, + [TYPE_BUG] = sSpriteAnim_TypeIcon_Bug, + [TYPE_GHOST] = sSpriteAnim_TypeIcon_Ghost, + [TYPE_STEEL] = sSpriteAnim_TypeIcon_Steel, + [TYPE_MYSTERY] = sSpriteAnim_TypeIcon_Mystery, + [TYPE_FIRE] = sSpriteAnim_TypeIcon_Fire, + [TYPE_WATER] = sSpriteAnim_TypeIcon_Water, + [TYPE_GRASS] = sSpriteAnim_TypeIcon_Grass, + [TYPE_ELECTRIC] = sSpriteAnim_TypeIcon_Electric, + [TYPE_PSYCHIC] = sSpriteAnim_TypeIcon_Psychic, + [TYPE_ICE] = sSpriteAnim_TypeIcon_Ice, + [TYPE_DRAGON] = sSpriteAnim_TypeIcon_Dragon, + [TYPE_DARK] = sSpriteAnim_TypeIcon_Dark, + [TYPE_FAIRY] = sSpriteAnim_TypeIcon_Fairy, + [TYPE_STELLAR] = sSpriteAnim_TypeIcon_Mystery, +}; + +const struct CompressedSpritePalette sTypeIconPal1 = +{ + .data = gBattleIcons_Pal1, + .tag = TYPE_ICON_TAG +}; + +const struct CompressedSpritePalette sTypeIconPal2 = +{ + .data = gBattleIcons_Pal2, + .tag = TYPE_ICON_TAG_2 +}; + +const struct OamData sOamData_TypeIcons = +{ + .affineMode = ST_OAM_AFFINE_OFF, + .objMode = ST_OAM_OBJ_NORMAL, + .shape = SPRITE_SHAPE(8x16), + .size = SPRITE_SIZE(8x16), + .priority = 1, +}; + +const struct CompressedSpriteSheet sSpriteSheet_TypeIcons2 = +{ + .data = gBattleIcons_Gfx2, + .size = (8*16) * 9, + .tag = TYPE_ICON_TAG_2, +}; + +const struct CompressedSpriteSheet sSpriteSheet_TypeIcons1 = +{ + .data = gBattleIcons_Gfx1, + .size = (8*16) * 10, + .tag = TYPE_ICON_TAG, +}; + +const struct SpriteTemplate sSpriteTemplate_TypeIcons1 = +{ + .tileTag = TYPE_ICON_TAG, + .paletteTag = TYPE_ICON_TAG, + .oam = &sOamData_TypeIcons, + .anims = sSpriteAnimTable_TypeIcons, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCB_TypeIcon +}; + +const struct SpriteTemplate sSpriteTemplate_TypeIcons2 = +{ + .tileTag = TYPE_ICON_TAG_2, + .paletteTag = TYPE_ICON_TAG_2, + .oam = &sOamData_TypeIcons, + .anims = sSpriteAnimTable_TypeIcons, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCB_TypeIcon +}; + +void LoadTypeIcons(u32 battler) +{ + u32 position; + + if (B_SHOW_TYPES == SHOW_TYPES_NEVER) + return; + + LoadTypeSpritesAndPalettes(); + + for (position = 0; position < gBattlersCount; ++position) + LoadTypeIconsPerBattler(battler, position); +} + +static void LoadTypeSpritesAndPalettes(void) +{ + if (IndexOfSpritePaletteTag(TYPE_ICON_TAG) != UCHAR_MAX) + return; + + LoadCompressedSpriteSheet(&sSpriteSheet_TypeIcons1); + LoadCompressedSpriteSheet(&sSpriteSheet_TypeIcons2); + LoadCompressedSpritePalette(&sTypeIconPal1); + LoadCompressedSpritePalette(&sTypeIconPal2); +} + +static void LoadTypeIconsPerBattler(u32 battler, u32 position) +{ + u32 typeNum, types[2]; + u32 battlerId = GetBattlerAtPosition(position); + bool32 useDoubleBattleCoords = UseDoubleBattleCoords(battlerId); + + if (!IsBattlerAlive(battlerId)) + return; + + for (typeNum = 0; typeNum < 2; ++typeNum) + types[typeNum] = GetMonPublicType(battlerId, typeNum); + + for (typeNum = 0; typeNum < 2; ++typeNum) + CreateSpriteFromType(position, useDoubleBattleCoords, types, typeNum, battler); +} + +static bool32 UseDoubleBattleCoords(u32 position) +{ + if (!IsDoubleBattle()) + return FALSE; + + if ((position == B_POSITION_PLAYER_LEFT) && (gBattleMons[B_POSITION_PLAYER_RIGHT].species == SPECIES_NONE)) + return FALSE; + + if ((position == B_POSITION_OPPONENT_LEFT) && (gBattleMons[B_POSITION_OPPONENT_RIGHT].species == SPECIES_NONE)) + return FALSE; + + return TRUE; +} + +static u32 GetMonPublicType(u32 battlerId, u32 typeNum) +{ + struct Pokemon* mon = GetPartyBattlerData(battlerId); + u32 monSpecies = GetMonData(mon,MON_DATA_SPECIES,NULL); + struct Pokemon* monIllusion; + u32 illusionSpecies; + + if (ShouldHideUncaughtType(monSpecies)) + return TYPE_MYSTERY; + + monIllusion = GetIllusionMonPtr(battlerId); + illusionSpecies = GetMonData(monIllusion,MON_DATA_SPECIES,NULL); + + if (GetActiveGimmick(battlerId) == GIMMICK_TERA) + return GetMonDefensiveTeraType(mon,monIllusion,battlerId,typeNum,illusionSpecies,monSpecies); + + if (IsIllusionActiveAndTypeUnchanged(monIllusion,monSpecies, battlerId)) + return gSpeciesInfo[illusionSpecies].types[typeNum]; + + return gBattleMons[battlerId].types[typeNum]; +} + +static bool32 ShouldHideUncaughtType(u32 species) +{ + if (B_SHOW_TYPES != SHOW_TYPES_CAUGHT) + return FALSE; + + if (GetSetPokedexFlag(SpeciesToNationalDexNum(species),FLAG_GET_CAUGHT)) + return FALSE; + + return TRUE; +} + +static u32 GetMonDefensiveTeraType(struct Pokemon * mon, struct Pokemon* monIllusion, u32 battlerId, u32 typeNum, u32 illusionSpecies, u32 monSpecies) +{ + u32 teraType = GetBattlerTeraType(battlerId); + u32 targetSpecies; + + if (teraType != TYPE_STELLAR) + return teraType; + + targetSpecies = (monIllusion != NULL) ? illusionSpecies : monSpecies; + + return gSpeciesInfo[targetSpecies].types[typeNum]; +} + +static u32 IsIllusionActiveAndTypeUnchanged(struct Pokemon* monIllusion, u32 monSpecies, u32 battlerId) +{ + u32 typeNum; + + if (monIllusion == NULL) + return FALSE; + + for (typeNum = 0; typeNum < 2; typeNum++) + if (gSpeciesInfo[monSpecies].types[typeNum] != gBattleMons[battlerId].types[typeNum]) + return FALSE; + + return TRUE; +} + +static void CreateSpriteFromType(u32 position, bool32 useDoubleBattleCoords, u32 types[], u32 typeNum, u32 battler) +{ + s32 x = 0, y = 0; + + if (ShouldSkipSecondType(types, typeNum)) + return; + + SetTypeIconXY(&x, &y, position, useDoubleBattleCoords, typeNum); + + CreateSpriteAndSetTypeSpriteAttributes(types[typeNum], x, y, position, battler, useDoubleBattleCoords); +} + +static bool32 ShouldSkipSecondType(u32 types[], u32 typeNum) +{ + if (!typeNum) + return FALSE; + + if (types[0] != types[1]) + return FALSE; + + return TRUE; +} + +static void SetTypeIconXY(s32* x, s32* y, u32 position, bool32 useDoubleBattleCoords, u32 typeNum) +{ + *x = sTypeIconPositions[position][useDoubleBattleCoords].x; + *y = sTypeIconPositions[position][useDoubleBattleCoords].y + (11 * typeNum); +} + +static void CreateSpriteAndSetTypeSpriteAttributes(u32 type, u32 x, u32 y, u32 position, u32 battler, bool32 useDoubleBattleCoords) +{ + struct Sprite* sprite; + const struct SpriteTemplate* spriteTemplate = gTypesInfo[type].useSecondTypeIconPalette ? &sSpriteTemplate_TypeIcons2 : &sSpriteTemplate_TypeIcons1; + u32 spriteId = CreateSpriteAtEnd(spriteTemplate, x, y, UCHAR_MAX); + + if (spriteId == MAX_SPRITES) + return; + + sprite = &gSprites[spriteId]; + sprite->tMonPosition = position; + sprite->tBattlerId = battler; + sprite->tVerticalPosition = y; + + sprite->hFlip = ShouldFlipTypeIcon(useDoubleBattleCoords, position, type); + + StartSpriteAnim(sprite, type); +} + +static bool32 ShouldFlipTypeIcon(bool32 useDoubleBattleCoords, u32 position, u32 typeId) +{ + bool32 side = (useDoubleBattleCoords) ? B_SIDE_OPPONENT : B_SIDE_PLAYER; + + if (GetBattlerSide(GetBattlerAtPosition(position)) != side) + return FALSE; + + return !gTypesInfo[typeId].isSpecialCaseType; +} + +static void SpriteCB_TypeIcon(struct Sprite* sprite) +{ + u32 position = sprite->tMonPosition; + u32 battlerId = sprite->tBattlerId; + bool32 useDoubleBattleCoords = UseDoubleBattleCoords(GetBattlerAtPosition(position)); + + if (sprite->tHideIconTimer == NUM_FRAMES_HIDE_TYPE_ICON) + { + DestroyTypeIcon(sprite); + return; + } + + if (ShouldHideTypeIcon(battlerId)) + { + sprite->x += GetTypeIconHideMovement(useDoubleBattleCoords, position); + ++sprite->tHideIconTimer; + return; + } + + sprite->x += GetTypeIconSlideMovement(useDoubleBattleCoords,position, sprite->x); + sprite->y = GetTypeIconBounceMovement(sprite->tVerticalPosition,position); +} + +static const u32 typeIconTags[] = +{ + TYPE_ICON_TAG, + TYPE_ICON_TAG_2 +}; + +static void DestroyTypeIcon(struct Sprite* sprite) +{ + u32 spriteId, tag; + + DestroySpriteAndFreeResources(sprite); + + for (spriteId = 0; spriteId < MAX_SPRITES; ++spriteId) + { + if (!gSprites[spriteId].inUse) + continue; + + for (tag = 0; tag < 2; tag++) + { + if (gSprites[spriteId].template->paletteTag == typeIconTags[tag]) + return; + + if (gSprites[spriteId].template->tileTag == typeIconTags[tag]) + return; + } + } + + FreeAllTypeIconResources(); +} + +static void FreeAllTypeIconResources(void) +{ + u32 tag; + + for (tag = 0; tag < 2; tag++) + { + FreeSpriteTilesByTag(typeIconTags[tag]); + FreeSpritePaletteByTag(typeIconTags[tag]); + } +} + +static void (* const sShowTypesControllerFuncs[])(u32 battler) = +{ + PlayerHandleChooseMove, + HandleChooseMoveAfterDma3, + HandleInputChooseTarget, + HandleInputShowTargets, + HandleInputShowEntireFieldTargets, + HandleMoveSwitching, + HandleInputChooseMove, +}; + + +static bool32 ShouldHideTypeIcon(u32 battlerId) +{ + u32 funcIndex; + + for (funcIndex = 0; funcIndex < ARRAY_COUNT(sShowTypesControllerFuncs); funcIndex++) + if (gBattlerControllerFuncs[battlerId] == sShowTypesControllerFuncs[funcIndex]) + return FALSE; + + return TRUE; +} + +static s32 GetTypeIconHideMovement(bool32 useDoubleBattleCoords, u32 position) +{ + if (useDoubleBattleCoords) + { + if (position == B_POSITION_PLAYER_LEFT || position == B_POSITION_PLAYER_RIGHT) + return 1; + else + return -1; + } + + if (position == B_POSITION_PLAYER_LEFT) + return -1; + else + return 1; +} + +static s32 GetTypeIconSlideMovement(bool32 useDoubleBattleCoords, u32 position, s32 xPos) +{ + if (useDoubleBattleCoords) + { + switch (position) + { + case B_POSITION_PLAYER_LEFT: + case B_POSITION_PLAYER_RIGHT: + if (xPos > sTypeIconPositions[position][useDoubleBattleCoords].x - 10) + return -1; + break; + default: + case B_POSITION_OPPONENT_LEFT: + case B_POSITION_OPPONENT_RIGHT: + if (xPos < sTypeIconPositions[position][useDoubleBattleCoords].x + 10) + return 1; + break; + } + return 0; + } + + if (position == B_POSITION_PLAYER_LEFT) + { + if (xPos < sTypeIconPositions[position][useDoubleBattleCoords].x + 10) + return 1; + } + else + { + if (xPos > sTypeIconPositions[position][useDoubleBattleCoords].x - 10) + return -1; + } + return 0; +} + +static s32 GetTypeIconBounceMovement(s32 originalY, u32 position) +{ + struct Sprite* healthbox = &gSprites[gHealthboxSpriteIds[GetBattlerAtPosition(position)]]; + return originalY + healthbox->y2; +} +