From 84a9d4ffcfb5ac78803830dc7d25805f433fc415 Mon Sep 17 00:00:00 2001 From: AgustinGDLV <103095241+AgustinGDLV@users.noreply.github.com> Date: Wed, 24 Apr 2024 02:17:46 -0700 Subject: [PATCH] Terastallization (#4110) * wrote foundational terastal tests * implemented baseline test-only Tera functionality; modified GetBattlerType + STAB calculations, misc. changes to some moves * added tests and func. for Stellar type, more tests for Tera Blast * more tests for Stellar type, Conversion fixes, Color Change + Conversion2 future proof * implemented tera blast, expanded stellar type func., fixed tests * last set of Tera/Tera Blast tests for checklist, protean fix * implemented in-battle Terastallization, WIP stellar indicator and tera animation * fixed bad merge * expanded NUMBER_OF_MON_TYPES, cut down on TYPE_STELLAR hackiness, added Stellar type to summary * fixed type indicators * added tera logic to AI * implemented code review changes, added B_TERA_ORB_NO_COST * updated AI to calc damage with Tera when applicable; minor rework to AI gimmick handling * fixed Tera Blast split choice occuring when not Terastallized * fixed Tera Blast using Last Respects BP calcs * added tera type to TrainerMon, code review tweaks --- data/battle_scripts_1.s | 28 + graphics/battle_interface/bug_indicator.png | Bin 0 -> 4942 bytes graphics/battle_interface/dark_indicator.png | Bin 0 -> 4980 bytes .../battle_interface/dragon_indicator.png | Bin 0 -> 5015 bytes .../battle_interface/electric_indicator.png | Bin 0 -> 4876 bytes graphics/battle_interface/fairy_indicator.png | Bin 0 -> 5006 bytes .../battle_interface/fighting_indicator.png | Bin 0 -> 5009 bytes graphics/battle_interface/fire_indicator.png | Bin 0 -> 4928 bytes .../battle_interface/flying_indicator.png | Bin 0 -> 4929 bytes graphics/battle_interface/ghost_indicator.png | Bin 0 -> 4941 bytes graphics/battle_interface/grass_indicator.png | Bin 0 -> 4887 bytes .../battle_interface/ground_indicator.png | Bin 0 -> 4873 bytes graphics/battle_interface/ice_indicator.png | Bin 0 -> 4905 bytes .../battle_interface/normal_indicator.png | Bin 0 -> 4885 bytes .../battle_interface/poison_indicator.png | Bin 0 -> 4959 bytes .../battle_interface/psychic_indicator.png | Bin 0 -> 4937 bytes graphics/battle_interface/rock_indicator.png | Bin 0 -> 4933 bytes graphics/battle_interface/steel_indicator.png | Bin 0 -> 4904 bytes .../battle_interface/stellar_indicator.png | Bin 0 -> 5418 bytes graphics/battle_interface/tera_indicator.pal | 19 + graphics/battle_interface/tera_trigger.png | Bin 0 -> 7449 bytes graphics/battle_interface/water_indicator.png | Bin 0 -> 4896 bytes graphics/types/stellar.png | Bin 0 -> 5861 bytes graphics_file_rules.mk | 2 +- include/battle.h | 21 +- include/battle_controllers.h | 1 + include/battle_interface.h | 24 + include/battle_scripts.h | 2 + include/battle_terastal.h | 30 + include/battle_util.h | 2 +- include/config/battle.h | 2 + include/constants/battle.h | 3 +- include/constants/battle_move_effects.h | 1 + include/constants/battle_string_ids.h | 11 +- include/constants/pokemon.h | 3 +- include/data.h | 1 + include/test/battle.h | 2 + src/battle_ai_main.c | 38 +- src/battle_ai_util.c | 20 +- src/battle_controller_opponent.c | 12 +- src/battle_controller_player.c | 21 + src/battle_gfx_sfx_util.c | 8 + src/battle_interface.c | 19 + src/battle_main.c | 43 +- src/battle_message.c | 2 + src/battle_script_commands.c | 60 +- src/battle_terastal.c | 786 +++++++++++++++++ src/battle_util.c | 118 ++- src/data/battle_move_effects.h | 6 + src/data/moves_info.h | 6 +- src/pokemon_summary_screen.c | 5 + src/script_pokemon_util.c | 5 + test/{ => battle/gimmick}/dynamax.c | 0 test/battle/gimmick/terastal.c | 802 ++++++++++++++++++ test/battle/move_effect/tera_blast.c | 137 +++ test/test_runner_battle.c | 3 + 56 files changed, 2151 insertions(+), 92 deletions(-) create mode 100644 graphics/battle_interface/bug_indicator.png create mode 100644 graphics/battle_interface/dark_indicator.png create mode 100644 graphics/battle_interface/dragon_indicator.png create mode 100644 graphics/battle_interface/electric_indicator.png create mode 100644 graphics/battle_interface/fairy_indicator.png create mode 100644 graphics/battle_interface/fighting_indicator.png create mode 100644 graphics/battle_interface/fire_indicator.png create mode 100644 graphics/battle_interface/flying_indicator.png create mode 100644 graphics/battle_interface/ghost_indicator.png create mode 100644 graphics/battle_interface/grass_indicator.png create mode 100644 graphics/battle_interface/ground_indicator.png create mode 100644 graphics/battle_interface/ice_indicator.png create mode 100644 graphics/battle_interface/normal_indicator.png create mode 100644 graphics/battle_interface/poison_indicator.png create mode 100644 graphics/battle_interface/psychic_indicator.png create mode 100644 graphics/battle_interface/rock_indicator.png create mode 100644 graphics/battle_interface/steel_indicator.png create mode 100644 graphics/battle_interface/stellar_indicator.png create mode 100644 graphics/battle_interface/tera_indicator.pal create mode 100644 graphics/battle_interface/tera_trigger.png create mode 100644 graphics/battle_interface/water_indicator.png create mode 100644 graphics/types/stellar.png create mode 100644 include/battle_terastal.h create mode 100644 src/battle_terastal.c rename test/{ => battle/gimmick}/dynamax.c (100%) create mode 100644 test/battle/gimmick/terastal.c create mode 100644 test/battle/move_effect/tera_blast.c diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index e66f4c2a8e..5e36241c9a 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -20,6 +20,34 @@ .section script_data, "aw", %progbits +BattleScript_Terastallization:: + @ TODO: no string prints in S/V, but right now this helps with clarity + printstring STRINGID_PKMNTERASTALLIZEDINTO + @ TODO: replace this animation + playanimation BS_ATTACKER, B_ANIM_TOTEM_FLARE + waitanimation + end3 + +BattleScript_LowerAtkSpAtk:: + jumpifstat BS_EFFECT_BATTLER, CMP_GREATER_THAN, STAT_ATK, MIN_STAT_STAGE, BattleScript_LowerAtkSpAtkDoAnim + jumpifstat BS_EFFECT_BATTLER, CMP_EQUAL, STAT_SPATK, MIN_STAT_STAGE, BattleScript_LowerAtkSpAtkEnd +BattleScript_LowerAtkSpAtkDoAnim:: + setbyte sSTAT_ANIM_PLAYED, FALSE + playstatchangeanimation BS_EFFECT_BATTLER, BIT_ATK | BIT_SPATK, STAT_CHANGE_NEGATIVE + setstatchanger STAT_ATK, 1, TRUE + statbuffchange MOVE_EFFECT_AFFECTS_USER | STAT_CHANGE_ALLOW_PTR, BattleScript_LowerAtkSpAtkTrySpAtk + jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_DECREASE, BattleScript_LowerAtkSpAtkTrySpAtk + printfromtable gStatDownStringIds + waitmessage B_WAIT_TIME_LONG +BattleScript_LowerAtkSpAtkTrySpAtk:: + setstatchanger STAT_SPATK, 1, TRUE + statbuffchange MOVE_EFFECT_AFFECTS_USER | STAT_CHANGE_ALLOW_PTR, BattleScript_LowerAtkSpAtkEnd + jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_DECREASE, BattleScript_LowerAtkSpAtkEnd + printfromtable gStatDownStringIds + waitmessage B_WAIT_TIME_LONG +BattleScript_LowerAtkSpAtkEnd: + return + BattleScript_EffectTidyUp:: attackcanceler attackstring diff --git a/graphics/battle_interface/bug_indicator.png b/graphics/battle_interface/bug_indicator.png new file mode 100644 index 0000000000000000000000000000000000000000..7ab7dc6dfa37f207ea32e8655499176ff9db3b21 GIT binary patch literal 4942 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 aB@jvnC+s3S|0=@G2m*Isw?nQGDgOmy+lEd6 literal 0 HcmV?d00001 diff --git a/graphics/battle_interface/fire_indicator.png b/graphics/battle_interface/fire_indicator.png new file mode 100644 index 0000000000000000000000000000000000000000..4d3e59d543ac9e0f2b73933f1b74573a4e1b4411 GIT binary patch literal 4928 zcmeHKX;>5I77kLxve;HZyoeGbtzwxZlT20AX6>z^IdcOozd~W~vJh%UxCzF{u%X`jwzw@1B zvSpzGj&`ng6bi*r8t4}euAiIFPprZBA1!x*zR6~jmr{X3; zMWL8({4sO(y0%FUFFTybPdxT4Z{2HF8c z@dfER7#`D6zSJq>Y(G|^K(}yhm8|wAItUdO%87TE;%Ofbd{)ijJ!QBKcdXO&)RbY zw|2>0@4`E7o9>_S=`=1=ZGEo7w|AU1rjBVCcTDgAE-HdwjEYLO@^nm-1xyOgKz+~R zA@;Ff99ZTyD>ybzR_nZ$H&^YuW9@R^bJT#9EXVutJLOgli5<)He)`dCLr~j^89S}Q zG+bA=qmd(R3Y{O@?OmWK-KUvzZbrD*&0*`N>82JORVAmFM+sfAb)vkh>{)du$q33R zrRM4GA>)x%!iQfU4M?l`s&dHCzMv{vUBbEcGSiLt_-lDyWj0I4weG(@X@84Vc-B5~ zUG8G5F@YkRAM*qw#;>+p#B%fGI=aZdxkXwBCDW|hqJNH7pU>W#(ERB8Q-%Au({qQ# z?cQ3KfBfju7uliHCCxd;TfMi-XKh+Ae8jx&@mZtW@2oHPb6MyBPhIJ$a!$I>iIsDT zkcpug#~XggRWz;*ua88Ilc)aekXd5y;<@Yjh#;>y;b)xhMMOQ}T#3ATMNYPU<=Ek8 z4{_Do?}d)tdL`H{t z*`xD)#P32I9CN8F2Hz8hC(YE^@`=kg zMNR1&ujVw=6kXlnwV^cX%tP_#XGgVl+x+wznVE#+luflcVW1Au5)JPu+8q7-A5U4MA#M=cK6Q8 zwEN|Uavpkh9roEV>rAmjclHT#8qe#la(E2Cdxp;LQBdXXC#nc3pP$ zbqfxpe)%)_i^Y|C}?dG7vlW5w)(BEy<* zVQRPH7x(gSbS|;i)V;k;`->+oe*L$DjW=G!Sr_zq)ZV&A3;5<&t}e0VTATQ`-gb0q zhYwULqujdKS*)+CgDzFI*HSH62xI6*%3b)`4M5p@@7;Iuwh?4O9glLukabuIeK+ zDxnn9W^qE8Avy^jO9ZCs@rcw=IhGoa36wM+Z#$7m2msW$0i~MM2^vyp64NZWLU3;u zGiX$ci6LH0iwu!bC0adBMd%2f3Hh6dWERcajw;eCRl;z;sRIz;Nlc417<57g!)P?p zjcmGBAH#qJ0s(``Vz5{cuz<)EjR7@58ghaeq94N#Cow&tGZ0!0)r^TMv`GdrjRy48 zH~Fb`At7(!HRJ#bfDeWV)iGc?lc82K278bO|6~9%kkFrckaF;w#0bYpZIT|t{gZKx zVZvYtCHB@|m!wayl%vELcml2lrX+|8zZWt<8X|k^VWuF4Q0pvSfb91y4TS0)S?}d$ z)>z6JoCt7#i~FASo7yeLz$zp}=%>Y!%;`z}#58k!p;C(xN}=VJi*sS{;Xyne!i5ke zn+@?%0S0mSDum6$5gg%SgP^1u(tv6(+zbW4=>))Gb2uoU1EY|F!{a~*8^<7&%~3!o zU#Wm`0T)xU_=6zA^aSWiG+}U7W+)|qQm7ON8|JYg6$i#3gwInz3LHitCMIC9VGbL> zDJ)P*OgL4mSEFD#2{jsnGjy65i^5E}&{rlE(^z!oJBcg-HK>3C*aL({sWp=CRB}R% zM;K5upD>q?AY6paVR6|^K9~E>XeO>FK`)w7VJ4l0STyE^5rT36wWzsM0f0pgsu4=` zIBL-9kmSr z$+*%I2>A7@uvkJOnR7OxZNKKe#F)7F_gE zM-Y{=+@_{WRj6qSqyLR*70#NW2VX5sa(=2VKPrXNqd)oFj3bYkS}(uuC=`mFIM6Qy+=rM>D@$-~{-YiUEQ?aZ=IBCD1Ekid2r?0ebSY{a!i^+B zp%`yBXGd;mfNVwGks{baY%AmC)aBpxg-<)jo@fXQ+2lB-(#aAXQXk{{<&C$UEsdkb zO-xw*VwTY6OnuTt*Q&z8nBdL4NY0C{bb))eUaM#1TJ6}A<@mZS;OpNqTrU;=HAXo1 zb!PMJAMPmUJ+p0dmm85M$($ec!t$bYy=97`a@qDa{otr5T%h0jC)-7vn%r^a(ZE-` z&-2Ug{HuZ*msDvny4c}jq4B|lmETPml)N0d{Y5Zz!DZ9a)e()GFGX&1Yc@zH?9rz$ zE6S2@?x%4Y^!mw3CiGPp&&9HsvvQ+2s!SYwtHs7{!hpVwQ zV|e}+17Ephb`F0N*naiCJJhuE@=8161Z`N}PC<#k(?u(qL({B%2mSZA41k8-e63z) zWt9lE4Zq;u7-%(i#6p(sWP3Z+)Twv1OS7N)^}Bz+B35yE1#kfh*h zDOQs^Us*jQ=nDJZv{rZvi6~CBRBFza{#j|g(DOg!qLRwPIFZ+S zz-dSSc4ApkQEkxlN5_u};`Y|n& z=*5!E?0p&eoaYD<^^;qzi@hC#H*>*hkH{nhKy^{rlMPv@RqP}`Q(ak@R@uZhbKq!dIK4_X}Zw%=h& z*W3uVz!(a}Vks#UO2tCqyKM_LYyP4i1cARuhc68doi1`;GIDs*1?eV_?^kkzMr6cp zIW%H!G9%@&GeQQ2L_77bU6SsTG241nT+G<${=Ti&6*XtYzlwrN;mL=-Z!2$DlyA6N zcd~=BWfr>oH?~D#F222<$;^uykzPEoz&XA349pA--ag8+bx-kvC)|w(b+NIHbm>@+~0`0tjl$?AC_^} ze#6%}>uuK?bGcg!9Ul&k%pOq{m0uII`f$s|c@CEnz9RSCaP)PW9taH@%9=lETff1x zZnHyXY;W>ztaH|PiZvG>xTpVsS)ZHtt0_i9s~ zrmT#1?O8O}Pn^&yyI&t_Y`6Skyk>DIR4E}7CMr`U>jYFPScl%lr%+2I@8Ol&UKRi! zun|?mOgaNrDB!*pTAfG_KzalETMKO%_&I_@aIGp?gW)1QuGG2og^*+K?bXSeL~}ZF z48{|21yI!jugs5J28bon_ZB7!;z)(sYz4^vNK;1=AISPBHq(kZoxXtp^Y^$PX}`O03lCP$h<&pa3|X1UPJ_jLqcXavEEPaA*j|C1^a1E29xC4u{7^cpOh$)(0Y3 zLxQeE6Z=MGf|3I$0!2Mp1lx-yV-s8e#o*CoAQBov5DXcc&EgO&t{F;>@%>dA1q#YZ zD$qC_Rx9Jo3ns$(zEZJ(%AzwqNTi9VjsON=50FZ^%AozQ6hhXKR8vrp z$flM4(NoB(X}cl@;66H^S9Cyl|TF}Yz(9v?yY3^o<+O%^uo>37+B!~esFw^^aj z5CC>PGB8}gtb*SUt6siL6#j!pZyo+Y4*>eJlTYIJGhLtQ`XmNEN%?bkeWvS^82BXR z&)xNZqs!*Q=MJs}|9}kOW2W%e`)9$25JNg6Oa$(-(1llh6rp6Xbj__>*ABkbru>+< z4J(aWR|a?I&puzWDyiyRa#h)@>zr2h z>OZWXM}O-9C#!*oEB5mZ8(53Q7TC7lJvoL#+&Q_R-94wRyQQl*_FUKL?k&{C-9o>X pw_SsW9_w~?Pi~(Q7U5CtL}`L3ItXasS%M>7-oR9I*pP{ARsD5OF)E? zRMKe1yPeykwzp5Q6Zc1n5etc(oWHXr@4g@MquZiWm77~GRqyKy(OWVyjE`(9_q=|k z;r2IU^!U6im$2dEvx2=6XNfygpI@#i&+bBciev6xxpOXV%+|wSah|qiB_DPQ-a6;S z=+#G`PHHT3KiPC<`h?cG)iU;zB<|K^QDsR+vQ>)e=heHPC60`aCWOh~|1)-0$4k%N z^GjB~+ILYBIK9-b<*Ck z<3^M;6|_9Ltlf?@*7%ln4ZA`;JAWyxb{o=pIxjdT&u97R`JB_o%T7JDjB)9aPKwUv z*xhT^WnZ@ptGZhBacofFVu#b#)g^GU$aCwOuYcH56qGF}iyH5_>CuTq|C*RcWo}S= zm3Y5}2X{=;MrfYcuY`86U|T56z9rS4oVj+@RBZC>sB`7h5@z*GdfXuEa&J%E|JzGP z>KSib+dneyRim~uiro_1|DKWcvd^y6GJSb@)S+_;#|in1qZb?2xMvqwTL~{EHB4z} zqQ$H%6IN|XOS7N6%kob3#LVUZ>wG)WTG4q|hfS|y3cpR9VcFbVwMcb2lYQ~lIX_T)?i&ZW^NxiZ$1He>RylL_?3{aJ z@2!G4H{uXy%ZZe!p0V<#SOKNjOK{U^tTGc(0f z$*1#6!xBSp$ncvvi`6$Ag065L%v>jpUVPI1MmcqB*=S*M=iY}~T(VdFCQ8kBnRLVW zt&3Hg#6BGN3AuJ+cHg35@~6YA+Mu+wj&qki`4^Q1u9p)kg`e#ztRKzWHVk#3HrzG{ z?!2ZOT9kRcQ`Jj1v8M7Q9vrMw1swd)-O$} z%qnyf`L4c?EU7N4j6VO}hOEuMe}DXk8{;}Im)dhzvZI8Jd&Hx2J6x+mVCBN2ErOWN zx?{^r4lLN?8^k&O<&Tj$9vd`4y^v%6^7x$cC*|9TGc8m9y-HkGb*9X41;?$SYR!h|RB(yy# zKJt56{t})-wC{LRpZ)WI%#NG#h2Qo#+?l^&#gw|Gzg~#WblTgY=brJXTiSYI-p57X zZ0w#^`eJT&%7oe#P)$X!bACik-{_15mOBD&%b7Hq#VS%Hl1W6OH~Sdu-n?a-g@NZ~ z<5q=+2a7$^$B#>_mVGyE&01cNg53jIj%QIYUIo(SuoP+JV!?7sNA$GT-Xl}G)#=+qQZW-O@ z5Hvh=w-c*-|G_0siptkLpVoE8w`6`zu3dL#l|F zO0UX_*|A`GTMQN1oQ=6}$?+I9cHK{-wtum6o82~J4)6P7mtRIk?QpD(&bt}3q3qY1 zh0d4ar;?@DUHm2n2SOwM##}t5$a3VIyPU9)-H&`aTHKQRB)Xb*&y3Bu#oXde6Mg^X z*0<|G)Xp4g%S;sNmN&W;&Coia~YAkmR8&sdBa_4Oe-ad1h+ zsZ)A&f6vj5S5~7x(X9w)+|z-9>r8^d8YvAC;2M<|rqIX+OM?h>Zwig2y8_8rQeY!2=qf;maVg9oQA;6oE z9!F7H0fHC|1}_84OQVZLP(GiJFqjCF2?GsSpQ5HPBdpfDn;-@-{0Tj-BefK%Q9~w7 zOs+|ygmgMshu*}e(n_Uo;nn&<762cJ5z`{57XwkLkf9cON}LQp1_Sz23w;FmeL}(r zy(USA6XIk-O}P(+P~dOvwMn`Jb2(~3Erp@9JNx47?UzscRK478+DfxiY%GKDAc7t&4s1quyLDg@?7CeBiN zGf^(gMTcPypPE1UL+g$5AMl1gzvU zxiFinoi!Qz8*p-for z&EdkJ;259J<9jO=18KZP*K3rN0n-tFu^>~B4QL^AHqewoGCzMe8bcgmVv50J!3++J z@*+^afXxyB){wzu5!0T2ldTW(KYaL@6^0A}U^gHG!v)ML<_Z2$P&A=Ka>kO6$m2(wq;1s_5T zSxAH!+~wg*ullOO$%C>@w{KrN@*jQ5*4!ezB6`bNq(5)&#bfIeYcC|#p8c(!+Yb(# z=OSP;LK_&Rr_n}^HJui;oIGbBw4o$YvCTv4v6fSvr}_n40wR{g-!EeLEqqad9mCbt zCT+rn{{Gzb2|fM&D^@YqZzx=UwZ~!gq1UnD#d6%)%5#hz$A)%)x5H!C-B03yIZYy- L?O!@`QTl%Y7XWa~ literal 0 HcmV?d00001 diff --git a/graphics/battle_interface/grass_indicator.png b/graphics/battle_interface/grass_indicator.png new file mode 100644 index 0000000000000000000000000000000000000000..81fa2589d0dab5e7e803560121a84f5b761aab16 GIT binary patch literal 4887 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 literal 0 HcmV?d00001 diff --git a/graphics/battle_interface/ice_indicator.png b/graphics/battle_interface/ice_indicator.png new file mode 100644 index 0000000000000000000000000000000000000000..a40d96f01831d9941c476babde6cbf4f7c8d7bac GIT binary patch literal 4905 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 literal 0 HcmV?d00001 diff --git a/graphics/battle_interface/normal_indicator.png b/graphics/battle_interface/normal_indicator.png new file mode 100644 index 0000000000000000000000000000000000000000..029827e2962bc93f03b093045253db9155718dfc GIT binary patch literal 4885 zcmeHKXH*ku7Y-t#NKp}pl@LVHbx5xg5CfQ?QJMt>WHOn+2q|QO1nfawMa6T@ z$dP(i%l7Ak@!q2judX}fTVeINb^Ef{dtdw9Eq+-Ud2t!)VhI-ui$K`KyE-TT)AodJkbNMMVF7oA>aX z=*^)*n^ig+`oQZ66D)UK+Ss(j>-XiwMX0b!cV=bP$9p%nH=e!zD*N%JC5b7k!&{o1 zE5bzM6YYykt`=Vwm6hg?32@y!s^Zb+L)mkECWc=wf1FF-us@XXdD7R3$txhvfQROc z(7GwSPv)QCoBIdU=(Z&$oK1~C8~V^J;W5HwA@54gyy`V+Xo_U}+z^JV{ff{#N0!;g zF081V-%nz7q)6W^-v>o)9Yl*7QeP0T+iUUqt>Z}iH^XW4S=(HK)?}YdW<4pt?wjOH zjLwVLrw?Dgg);tF`|f84H(!i(j-p>6!e&|bzpVeTYX?n!%{HV&Y8L8lV-_*BL2d&L zm{eEce6H7`6IY^}$3@&Mr2fWESKJ?+yvMmnKC+xVC%2LI=BV%C5q$=bTR#wlz52wE z-VVO%+aOxO9oJe;-N|K!PBclVv~s<_ruW7qThp4^w%Cg!&mrIIbAERf^=5GcpUk&s z*H70b55E>(-16AmvE*|-zVV6P#;KILc3#S$uy?dlB5Qa0jN5zf<_1(pRb_MgCFf6f zy1aAjuI#7GOPaEmmMgRSJB&TpI>>MQoWOFs7eQg~S+{3b-wwv>Mq9<@t+dZNaa*$| zCHUpRwvmai9t2gI?q$nPKAlXAZ4^1r+rR1lF^}UPL-JEu;Zr4-b%$5&qUDT^+Ggv? zGeutYU3aCNFer0O~rku^wlQS_tw{X>T5ijIu4gSt$9J*<#C|3S6;%! zR!LN!9Hmpq3N_T`H;{OJSnQ%`W?GTP?w(h|u+o}Ri)|=HSG_-c{arN8sx%X z<1=Yn+2(h*n&lp2kF`$lYv6>LHlH{Um))O0Fj{3_%i?W+UD_po%P%|t31oHX-z3Ul{3E9sy0J1<$3Y+;c^$TlgF5moJM`= zhS0RIC3T_N*-tWH=hVzG*0yUeTW_0?zSVN8E|Z&AF!WjfkTkm+bF-`c)*pXUK96`U z`Zw(0t)X7Sr-(@X2GZxd=J)C!@PHLKb!Ve{!&Aov?P5)N{kWt}h{=qCjl(@#9ousb zgrsL`Qzy|#P0~Ld^CzkuqV6c#+U}jm8ndip$jPb)U+0^qe;HNs_zrpU#2U6b_R*bs z!4r)+F|o=;s2Sx6?eHf&u58TGoIelUJlb&MKwQc#bCU4roUF$AReHH+au7Lj00}lpo97-UH ztKu{W>aze;>Nrh|F&!B~L1Rz_FvY>Dw4auoESC6x_b^Zpi7C`ZFF^KB zmRd~ygRGxoGiZ$IbPoi$f5-jF`djWsV_+qb@V!+?oFP21w}5O|pD$A(n2c|{mCIl% zl__OGC>N$fOgbBZcvKz@qD#3f9>PVqOb*%&O02}Suo6KHPypNw12}RHiZVGY1d>ZR z42a3#!Vr(kL?AksA!pE8ECz~nhnTLxKv%*s-J>!<$p92f#$!_%Oew_WaS({frb;0x zhf9a(JhmK0>2juw3LB%5A$(tzMgfCzVhT7CrKpvWMumZJzL&pPK&HD zA(&31LI@RQ069a-rFE9;J36kCYvW-J>JX5u&2Le>rVL_AMQqjZbJb0b;`hS0kewoeOPtzWuWk1Ji6-e zFRlQfdlva6etYQZq3f3z_$A|>?&_iIml*gZggMGkN8nmd=QxSv}`ft2<@8;33c*2&fe5818>Ul~>_KYhh*2Uf^i@S0DeHEtz z9OlhJ;L}en3c(44{aeH&I3f0mok`Yph<&DcADk4S{~zq! BQG@^h literal 0 HcmV?d00001 diff --git a/graphics/battle_interface/poison_indicator.png b/graphics/battle_interface/poison_indicator.png new file mode 100644 index 0000000000000000000000000000000000000000..84806865ccef15dc1acefaf8cfbfae06892db8d0 GIT binary patch literal 4959 zcmeHKc~}$I77r?ovbchPqJ{_}LP#ciB4IH^2@nks1Qe^2nF)-Lg(Q%G%R{hM1#Nk# zC{U}?=hmuKfx4m6iUsvy6$HhC$|l+>n{_EFy$PuJz5e6(z5eHX-^|=O_nhB3=XcLN z$=x8GKhM@`k`;kKuoVXdhJ*K5(>-E1cs^=u27=+2(`3>5aM(c7Xw?cl5hLkSH5dsq z;tB%6*zhQC@xNPU*{r)0Q{iPHj=i9zUp*vBeE{L<#CIOQa(*RRCTJPN%eaZuj;?u=`Q1o>1tMDi-C75cgCfT z7evvbQsU(o0osDqWiy*^Rem4;;p-)QWxZ1dE{N~+W~ z^yHem&Q~iq-D|gjgwHdMbGlAB~mLl`LYMC`@{FnS1PUTw|AUK3cMT}y>Lfx>jlww z7VaFYnPMFI658v>el@G^x-mE|( z&SwXxY8@>}wa%d(@!25mxX&Ud>khYK1`*3YbySj<^l zBFJ{Ju$Ua=V|grxH{NBlm9NuAyMlENBvR+eAA>J`GAccDN@fY#zMh7OCAXUl_LU)6BV% zMPgmEE7y6ImB$Q;M_aat!Hm07?f7Nyk6B)-?vUu_Jt2#<0e{*>tsLbxk>$2E_GEAc zFHE>8GU4FKFySm#{Yq;JTX~>uv+TF)OP&dy+$`C*bIfvdM%}(1Usl6`?%?G%*+f?8 zrlhEV7H)T@)B2;+$9|DX7@tpMeB0VJ(W&DTMuFzahPdbl#KPcA$C0Pz{yuxk@4l5^ zop8GU+JVzlwPVfW*_Gh2?pMYS4z&-Bx9vO?Zp_kqBM;gq`q{+i$MhNxlr`obng2ZO zNm0kVYkkTwsd^*MJ>Cr+YwA#XvOY^z)UTT`C;y83n6IwMbP2yXEY=|q4E$)6+H8nHgHSbj3-TcpuW#iiV!rJr4^yiOmui3I{ z*sEE(|Lmxry0S5__r|WK9cYu)LzlBxqkHj7XSW{pnb1?dN%yp|wd3=e)3-9t#Wbx> ziYilWD(>mvJR0YDSoflD>g<#7;~&Uny4op^;>stk%ysJ}?mOqyBq_1>ClD+?!-Ybr zSSWn6t-)q4T$LjT`blc{*@B1=(X5P#c1e}eue{c8arR5^EXIMZ9pMLVQgWe1_^#yuSO#mQ{s@tM|(K2)o1JgSS`~`}47H znjxrQv3=%|k-Mg4c9m11hy~v`FuJ}wvf^RUsjMEa&a-~sMpYHqbZxkx3*~q<+cxiz zgxo)~z**rYcJXze%IV0s@I`FilGQD-`slkm;OV*f?xQDURgB&?d;3B ztYh-*s}~pE2;NlkxN526r36p>aLuFu=a3-MsB!e=GmC~ou<)v6# z)jBIP2eF9U|E06v^Jy=49*WtXug{$Wk-Fr!rxp2Y9OCPgeSJ4e3*6KDolabDc)fi1 z_E)ZDO?BjX{&(1#q{g~d!CfuUG3~mWQ0wYP*)1hBUF|5)oDbq2$dqM3V6M zqO%sGN3S_b#*VBwbNaMS)&I1({n_v_F4})Z(0 z0ezT2@bNQhU?c(4ljK+&t`d+tPoE=`a8y8!Vo7KcjS!2+gVMBEMB02Al9qt*P_mz| zm5-4R0F;;>CK;89DjnY_Ae(XdU~Cdo$t1IiK0!c^mPkoLwH6~Wy_w!LipYqk(8<15 zBp)rR;D-m!9fSa10&=`wui;av27|%d!0=XU0DMr5u!ahG)2K=%b*P0- zFG>L*g8}`mg-!;3uBhRdPMxepFi{Go(oY`>fg*41HObmUb2=zO#S$?kP}Kpi(0eZD zi6zpv7A6YfaHYm<1;~C+Q;#d&k@a3|rWtcOLjwWkZ*kw#ev`Xd8E8o){6IC5Yzj{t zC?K2s^HDW|qkQvF&O*2xRKcL2Oa!Gc6$+Fh=OS_n3zN$+uAGjd5OWBWSf$g$Dg-k@ z0dQ{|;LssV&SoQQ3WT8mf(b@2%E2fc77Jo9xe$}hU=4v-pv6H|!ihtpGC`pLiVe|$ zvVuLakEfvTw8?C`;odLzgGX1O>l z4jbZdUkxF1`7iO(-Z^5y+zll6?y}##RB3$rXU+oLgs8pGY84^ zct09LJZ55wMrTlHEDFSxK|DT_!DsTw)WKw_rak>8TOaEG@Zn=t7}5lQ-GB@<7tpJy zZ=2O1UnUCw!E10G{y`4_`lFK%;`bw6AL;ra20lpnV|9I`>w_5hAmxwM^?#$w>fLb% zQ-Oa#25^{}!8USTsNI@SJ5Ce*r{ecw_(o literal 0 HcmV?d00001 diff --git a/graphics/battle_interface/psychic_indicator.png b/graphics/battle_interface/psychic_indicator.png new file mode 100644 index 0000000000000000000000000000000000000000..c2646d0a71b53c8785459ce1c5cb68c507e66180 GIT binary patch literal 4937 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 BWw8JN literal 0 HcmV?d00001 diff --git a/graphics/battle_interface/rock_indicator.png b/graphics/battle_interface/rock_indicator.png new file mode 100644 index 0000000000000000000000000000000000000000..0329ec678cd1f66fd5d73625a319ccbd6dbcad8f GIT binary patch literal 4933 zcmeHKc~leU77r+85v@z9fG8nYktO?NEs-Szh#H9m6~!u($pi+nkqIPVp)J@`5v)=b zWvR+TU+F7VTbGJrt5vEj0#X)B1r;o!xS$WMmP-01pyGM`$8%o)bIzH}%>C~D-Ftuc zes?mt(j^g-o!p%W1j1x-WLPw~PPLp9CxG8OT|GcB;c!MwtRWgU5_Nj55>uf>L%I$n zq9#m9AecJu6viFxo$o9%yNTYk5eJ^tnfA9aE3S2%zI3;QD}E<);J?M_DTYG|JHBLO z;0io)x<>JJ%7Uawuh`X;sYxfOx^Lsu*~U-j7x~vKWx1ut zHnOflZajC7kjh+JrQMp3wl~El&f=)gEQ@x&>_9HMn%|srpChKcOt8()$O;_xBDb>s_V}l{9R0RK^wN^Et_4;`>{E2s1EYs4;O1evMS)vNkkVw8*bY{v>8a=494#7OU;@G4UDR z#n3!STvCo!Lz5X)Xv~@MXcuSnAEpB!jefQ>)4~DyiSS z_gZ#eN{~7~p4S_~e&?Jo%DwAY<^P?1Yv-SxG5$(#=(}rgwK*O2bT?e6OwVfVrd;@9c9zFe z?jI>bSv-AxT?pb^>C!l9?PmyHNjp`^5wY-}h|RK!EgnISJ^$SG^~#;ahOMD= z;-LIj&+=g1%*1x}@bIOhB|aaT-Hx?%jwDUk`E>5d?sif{@D;W$rK|nA;HuuO-LST?d2P4U-fhb24FpYq8@5B!z zBBl_KR@gTCC1Ihu> zSv)e2&S8?dD1!#U5X9qf>Eocp8r%SD5Yz$%z$qBOVe;s7Cc=fukb=u5Lns1~c}g0c ztUwVamxn4?T!=XiB1(^eu7p+Nqq0CL02IWc(-<@cLgugm0EEDDGLMG9WG9@a?zCxSWaWn$P79gAww)Sj|`(6HkrdkX3j&%CEcY|3;V7i_aZY1O5RS!N<&lPrT*eLx?6_93ui(x$OO> zAhirTAl-85QuCn^Jbhb9IZ_?B^%T`yy6oK1{FKI@QX5Y_Y~h%}VgEn~Y#eowD{%s0 z(hSRKLntnF1wwm+SR%5&VK>7zVBS8s;om^S6o-YxICLQKdz^Lmx@QxVw@1wD{C9q1 zHfK5Xn$1twdgWg+m*qc|etE^A=7d*%Vg-*-q4eObw|(O?U``N=mW1sOjnDoIKd@|u literal 0 HcmV?d00001 diff --git a/graphics/battle_interface/steel_indicator.png b/graphics/battle_interface/steel_indicator.png new file mode 100644 index 0000000000000000000000000000000000000000..e84d9fa1b0c5cdd1fb95488d342a2c79b21ab748 GIT binary patch literal 4904 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 NczgH?_I|r4^>5NoF(&{3 literal 0 HcmV?d00001 diff --git a/graphics/battle_interface/tera_indicator.pal b/graphics/battle_interface/tera_indicator.pal new file mode 100644 index 0000000000..d4ab140472 --- /dev/null +++ b/graphics/battle_interface/tera_indicator.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 0 0 +98 83 124 +207 63 109 +83 105 175 +80 144 216 +216 208 179 +254 115 121 +148 155 163 +99 188 91 +147 192 47 +255 156 85 +199 182 140 +113 206 198 +117 206 192 +245 210 55 +255 255 255 diff --git a/graphics/battle_interface/tera_trigger.png b/graphics/battle_interface/tera_trigger.png new file mode 100644 index 0000000000000000000000000000000000000000..5498ea8806cfa40dc664e5f0c8b311e84c349ce2 GIT binary patch literal 7449 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

-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 VuKLOdzo1vkpslX2mZ#zn{2zN*k%<5R literal 0 HcmV?d00001 diff --git a/graphics/battle_interface/water_indicator.png b/graphics/battle_interface/water_indicator.png new file mode 100644 index 0000000000000000000000000000000000000000..bdeb401f4abebf6b78b170c334e10aab6996efe5 GIT binary patch literal 4896 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 * 8 >= MAX_BATTLER #define TARGET_TURN_DAMAGED ((gSpecialStatuses[gBattlerTarget].physicalDmg != 0 || gSpecialStatuses[gBattlerTarget].specialDmg != 0) || (gBattleStruct->enduredDamage & gBitTable[gBattlerTarget])) #define BATTLER_TURN_DAMAGED(battlerId) ((gSpecialStatuses[battlerId].physicalDmg != 0 || gSpecialStatuses[battlerId].specialDmg != 0) || (gBattleStruct->enduredDamage & gBitTable[battler])) -#define IS_BATTLER_OF_TYPE(battlerId, type)((GetBattlerType(battlerId, 0) == type || GetBattlerType(battlerId, 1) == type || (GetBattlerType(battlerId, 2) != TYPE_MYSTERY && GetBattlerType(battlerId, 2) == type))) - -#define IS_BATTLER_TYPELESS(battlerId)(GetBattlerType(battlerId, 0) == TYPE_MYSTERY && GetBattlerType(battlerId, 1) == TYPE_MYSTERY && GetBattlerType(battlerId, 2) == TYPE_MYSTERY) +#define IS_BATTLER_OF_TYPE(battlerId, type)((GetBattlerType(battlerId, 0, FALSE) == type || GetBattlerType(battlerId, 1, FALSE) == type || (GetBattlerType(battlerId, 2, FALSE) != TYPE_MYSTERY && GetBattlerType(battlerId, 2, FALSE) == type))) +#define IS_BATTLER_OF_BASE_TYPE(battlerId, type)((GetBattlerType(battlerId, 0, TRUE) == type || GetBattlerType(battlerId, 1, TRUE) == type || (GetBattlerType(battlerId, 2, TRUE) != TYPE_MYSTERY && GetBattlerType(battlerId, 2, TRUE) == type))) +#define IS_BATTLER_TYPELESS(battlerId)(GetBattlerType(battlerId, 0, FALSE) == TYPE_MYSTERY && GetBattlerType(battlerId, 1, FALSE) == TYPE_MYSTERY && GetBattlerType(battlerId, 2, FALSE) == TYPE_MYSTERY) #define SET_BATTLER_TYPE(battlerId, type) \ { \ diff --git a/include/battle_controllers.h b/include/battle_controllers.h index 5f3bd8316e..523434d57a 100644 --- a/include/battle_controllers.h +++ b/include/battle_controllers.h @@ -101,6 +101,7 @@ enum { #define RET_MEGA_EVOLUTION (1 << 7) #define RET_ULTRA_BURST (1 << 6) #define RET_DYNAMAX (1 << 5) +#define RET_TERASTAL (1 << 4) struct UnusedControllerStruct { diff --git a/include/battle_interface.h b/include/battle_interface.h index b26205d810..f32017745f 100644 --- a/include/battle_interface.h +++ b/include/battle_interface.h @@ -57,12 +57,36 @@ enum #define TAG_DYNAMAX_TRIGGER_TILE 0xD77D #define TAG_DYNAMAX_INDICATOR_TILE 0xD77E +#define TAG_NORMAL_INDICATOR_TILE 0xD77F +#define TAG_FIGHTING_INDICATOR_TILE 0xD780 +#define TAG_FLYING_INDICATOR_TILE 0xD781 +#define TAG_POISON_INDICATOR_TILE 0xD782 +#define TAG_GROUND_INDICATOR_TILE 0xD783 +#define TAG_ROCK_INDICATOR_TILE 0xD784 +#define TAG_BUG_INDICATOR_TILE 0xD785 +#define TAG_GHOST_INDICATOR_TILE 0xD786 +#define TAG_STEEL_INDICATOR_TILE 0xD787 +// empty spot for TYPE_MYSTERY +#define TAG_FIRE_INDICATOR_TILE 0xD789 +#define TAG_WATER_INDICATOR_TILE 0xD78A +#define TAG_GRASS_INDICATOR_TILE 0xD78B +#define TAG_ELECTRIC_INDICATOR_TILE 0xD78C +#define TAG_PSYCHIC_INDICATOR_TILE 0xD78D +#define TAG_ICE_INDICATOR_TILE 0xD78E +#define TAG_DRAGON_INDICATOR_TILE 0xD78F +#define TAG_DARK_INDICATOR_TILE 0xD790 +#define TAG_FAIRY_INDICATOR_TILE 0xD791 +#define TAG_STELLAR_INDICATOR_TILE 0xD792 +#define TAG_TERA_TRIGGER_TILE 0xD793 + #define TAG_MEGA_TRIGGER_PAL 0xD777 #define TAG_MEGA_INDICATOR_PAL 0xD778 #define TAG_MISC_INDICATOR_PAL 0xD779 // Alpha, Omega, and Dynamax indicators use the same palette as each of them only uses 4 different colors. #define TAG_ZMOVE_TRIGGER_PAL 0xD77B #define TAG_BURST_TRIGGER_PAL 0xD77C #define TAG_DYNAMAX_TRIGGER_PAL 0xD77D +#define TAG_TERA_INDICATOR_PAL 0xD77E +#define TAG_TERA_TRIGGER_PAL 0xD77F enum { diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 1a7654d37b..e4e9ca4307 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -499,6 +499,8 @@ extern const u8 BattleScript_TheSwampDisappeared[]; extern const u8 BattleScript_ItemRestoreHP_Party[]; extern const u8 BattleScript_EffectPsychicNoise[]; extern const u8 BattleScript_AromaVeilProtectsRet[]; +extern const u8 BattleScript_LowerAtkSpAtk[]; +extern const u8 BattleScript_Terastallization[]; extern const u8 BattleScript_BoosterEnergyEnd2[]; // zmoves diff --git a/include/battle_terastal.h b/include/battle_terastal.h new file mode 100644 index 0000000000..078bf39079 --- /dev/null +++ b/include/battle_terastal.h @@ -0,0 +1,30 @@ +#ifndef GUARD_BATTLE_TERASTAL_H +#define GUARD_BATTLE_TERASTAL_H + +void PrepareBattlerForTera(u32 battler); +bool32 CanTerastallize(u32 battler); +u32 GetBattlerTeraType(u32 battler); +bool32 IsTerastallized(u32 battler); +void ExpendTypeStellarBoost(u32 battler, u32 type); +bool32 IsTypeStellarBoosted(u32 battler, u32 type); +uq4_12_t GetTeraMultiplier(u32 battler, u32 type); + +u16 GetTeraTypeRGB(u32 type); + +void ChangeTeraTriggerSprite(u8 spriteId, u8 animId); +void CreateTeraTriggerSprite(u8 battler, u8 palId); +bool32 IsTeraTriggerSpriteActive(void); +void HideTeraTriggerSprite(void); +void DestroyTeraTriggerSprite(void); + +void TeraIndicator_LoadSpriteGfx(void); +bool32 TeraIndicator_ShouldBeInvisible(u32 battler); +u8 TeraIndicator_GetSpriteId(u32 healthboxSpriteId); +void TeraIndicator_SetVisibilities(u32 healthboxId, bool32 invisible); +void TeraIndicator_UpdateOamPriorities(u32 healthboxId, u32 oamPriority); +void TeraIndicator_UpdateLevel(u32 healthboxId, u32 level); +void TeraIndicator_CreateSprite(u32 battler, u32 healthboxSpriteId); +void TeraIndicator_DestroySprite(u32 healthboxSpriteId); +void TeraIndicator_UpdateType(u32 battler, u32 healthboxSpriteId); + +#endif diff --git a/include/battle_util.h b/include/battle_util.h index 1fb6d6a8f7..27ecb26e82 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -250,7 +250,7 @@ bool32 AreBattlersOfOppositeGender(u32 battler1, u32 battler2); bool32 AreBattlersOfSameGender(u32 battler1, u32 battler2); u32 CalcSecondaryEffectChance(u32 battler, u32 battlerAbility, const struct AdditionalEffect *additionalEffect); bool32 MoveEffectIsGuaranteed(u32 battler, u32 battlerAbility, const struct AdditionalEffect *additionalEffect); -u8 GetBattlerType(u32 battler, u8 typeIndex); +u8 GetBattlerType(u32 battler, u8 typeIndex, bool32 ignoreTera); bool8 CanMonParticipateInSkyBattle(struct Pokemon *mon); bool8 IsMonBannedFromSkyBattles(u16 species); void RemoveBattlerType(u32 battler, u8 type); diff --git a/include/config/battle.h b/include/config/battle.h index 40d7bd1147..c47f4fedcf 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -179,6 +179,8 @@ #define B_FLAG_NO_CATCHING 0 // If this flag is set, the ability to catch wild Pokémon is disabled. #define B_FLAG_AI_VS_AI_BATTLE 0 // If this flag is set, the player's mons will be controlled by the ai next battles. #define B_FLAG_DYNAMAX_BATTLE 0 // If this flag is set, the ability to Dynamax in battle is enabled for all trainers. +#define B_FLAG_TERA_ORB_CHARGED 0 // If this flag is set, the Tera Orb is charged. It is automatically set upon healing and cleared upon Terastallizing once configured. +#define B_FLAG_TERA_ORB_NO_COST 0 // If this flag is set, the Tera Orb does not use up its charge upon Terastallization. In S/V, this occurs after an event with Terapagos. // Var Settings // To use the following features in scripting, replace the 0s with the var ID you're assigning it to. diff --git a/include/constants/battle.h b/include/constants/battle.h index 0acb857ad0..30f304144f 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -402,8 +402,9 @@ #define MOVE_EFFECT_FLORAL_HEALING 77 #define MOVE_EFFECT_SECRET_POWER 78 #define MOVE_EFFECT_PSYCHIC_NOISE 79 +#define MOVE_EFFECT_TERA_BLAST 80 -#define NUM_MOVE_EFFECTS 80 +#define NUM_MOVE_EFFECTS 81 #define MOVE_EFFECT_AFFECTS_USER 0x2000 #define MOVE_EFFECT_CERTAIN 0x4000 diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h index 0cf0862f15..b01685fbb7 100644 --- a/include/constants/battle_move_effects.h +++ b/include/constants/battle_move_effects.h @@ -350,6 +350,7 @@ enum { EFFECT_DRAGON_CHEER, EFFECT_LAST_RESPECTS, EFFECT_TIDY_UP, + EFFECT_TERA_BLAST, NUM_BATTLE_MOVE_EFFECTS, }; diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index 86b48f9d4a..dfb5c5e2d4 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -707,12 +707,13 @@ #define STRINGID_BIZARREARENACREATED 705 #define STRINGID_BIZARREAREACREATED 706 #define STRINGID_TIDYINGUPCOMPLETE 707 -#define STRINGID_BOOSTERENERGYACTIVATES 708 -#define STRINGID_FOGCREPTUP 709 -#define STRINGID_FOGISDEEP 710 -#define STRINGID_FOGLIFTED 711 +#define STRINGID_PKMNTERASTALLIZEDINTO 708 +#define STRINGID_BOOSTERENERGYACTIVATES 709 +#define STRINGID_FOGCREPTUP 710 +#define STRINGID_FOGISDEEP 711 +#define STRINGID_FOGLIFTED 712 -#define BATTLESTRINGS_COUNT 712 +#define BATTLESTRINGS_COUNT 713 // This is the string id that gBattleStringsTable starts with. // String ids before this (e.g. STRINGID_INTROMSG) are not in the table, diff --git a/include/constants/pokemon.h b/include/constants/pokemon.h index 1adefd4938..343bf1a049 100644 --- a/include/constants/pokemon.h +++ b/include/constants/pokemon.h @@ -22,7 +22,8 @@ #define TYPE_DRAGON 16 #define TYPE_DARK 17 #define TYPE_FAIRY 18 -#define NUMBER_OF_MON_TYPES 19 +#define TYPE_STELLAR 19 +#define NUMBER_OF_MON_TYPES 20 // Pokémon egg groups #define EGG_GROUP_NONE 0 diff --git a/include/data.h b/include/data.h index b4490e8cc7..0549f29c4c 100644 --- a/include/data.h +++ b/include/data.h @@ -71,6 +71,7 @@ struct TrainerMon bool8 gender:2; bool8 isShiny:1; u8 dynamaxLevel:4; + u8 teraType:5; bool8 gigantamaxFactor:1; bool8 shouldDynamax:1; bool8 shouldTerastal:1; diff --git a/include/test/battle.h b/include/test/battle.h index 62b0680486..c4bd89f91b 100644 --- a/include/test/battle.h +++ b/include/test/battle.h @@ -935,6 +935,8 @@ struct MoveContext // TODO: u8 zMove:1; u16 dynamax:1; u16 explicitDynamax:1; + u16 tera:1; + u16 explicitTera:1; u16 allowed:1; u16 explicitAllowed:1; u16 notExpected:1; // Has effect only with EXPECT_MOVE diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 3d1c7e934b..da8b3764a3 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -9,6 +9,7 @@ #include "battle_factory.h" #include "battle_setup.h" #include "battle_z_move.h" +#include "battle_terastal.h" #include "data.h" #include "debug.h" #include "event_data.h" @@ -398,6 +399,23 @@ static void SetBattlerAiData(u32 battler, struct AiLogicData *aiData) aiData->speedStats[battler] = GetBattlerTotalSpeedStatArgs(battler, ability, holdEffect); } +static void SetBattlerAiGimmickData(u32 battler, struct AiLogicData *aiData) +{ + bool32 isSecondTrainer = (GetBattlerPosition(battler) == B_POSITION_OPPONENT_RIGHT) && (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) && !BATTLE_TWO_VS_ONE_OPPONENT; + u16 trainerId = isSecondTrainer ? gTrainerBattleOpponent_B : gTrainerBattleOpponent_A; + const struct TrainerMon *party = GetTrainerPartyFromId(trainerId); + if (party != NULL) + { + aiData->shouldDynamax[battler] = CanDynamax(battler) && (party[isSecondTrainer ? gBattlerPartyIndexes[battler] - MULTI_PARTY_SIZE : gBattlerPartyIndexes[battler]].shouldDynamax); + aiData->shouldTerastal[battler] = CanTerastallize(battler) && (party[isSecondTrainer ? gBattlerPartyIndexes[battler] - MULTI_PARTY_SIZE : gBattlerPartyIndexes[battler]].shouldTerastal); + } + else + { + aiData->shouldDynamax[battler] = FALSE; + aiData->shouldTerastal[battler] = FALSE; + } +} + static u32 Ai_SetMoveAccuracy(struct AiLogicData *aiData, u32 battlerAtk, u32 battlerDef, u32 move) { u32 accuracy; @@ -467,6 +485,7 @@ void SetAiLogicDataForTurn(struct AiLogicData *aiData) continue; SetBattlerAiData(battlerAtk, aiData); + SetBattlerAiGimmickData(battlerAtk, aiData); SetBattlerAiMovesData(aiData, battlerAtk, battlersCount); } } @@ -2367,20 +2386,20 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) break; case EFFECT_SOAK: if (PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove) - || (GetBattlerType(battlerDef, 0) == TYPE_WATER - && GetBattlerType(battlerDef, 1) == TYPE_WATER - && GetBattlerType(battlerDef, 2) == TYPE_MYSTERY)) + || (GetBattlerType(battlerDef, 0, FALSE) == TYPE_WATER + && GetBattlerType(battlerDef, 1, FALSE) == TYPE_WATER + && GetBattlerType(battlerDef, 2, FALSE) == TYPE_MYSTERY)) ADJUST_SCORE(-10); // target is already water-only break; case EFFECT_THIRD_TYPE: switch (move) { case MOVE_TRICK_OR_TREAT: - if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GHOST) || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove)) + if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GHOST) || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove) || IsTerastallized(battlerDef)) ADJUST_SCORE(-10); break; case MOVE_FORESTS_CURSE: - if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GRASS) || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove)) + if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GRASS) || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove) || IsTerastallized(battlerDef)) ADJUST_SCORE(-10); break; } @@ -2514,9 +2533,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) case EFFECT_SYNCHRONOISE: //Check holding ring target or is of same type if (aiData->holdEffects[battlerDef] == HOLD_EFFECT_RING_TARGET - || IS_BATTLER_OF_TYPE(battlerDef, GetBattlerType(battlerAtk, 0)) - || IS_BATTLER_OF_TYPE(battlerDef, GetBattlerType(battlerAtk, 1)) - || IS_BATTLER_OF_TYPE(battlerDef, GetBattlerType(battlerAtk, 2))) + || DoBattlersShareType(battlerAtk, battlerDef)) break; else ADJUST_SCORE(-10); @@ -2940,9 +2957,8 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) break; case EFFECT_SOAK: if (atkPartnerAbility == ABILITY_WONDER_GUARD - && (GetBattlerType(battlerAtkPartner, 0) != TYPE_WATER - || GetBattlerType(battlerAtkPartner, 1) != TYPE_WATER - || GetBattlerType(battlerAtkPartner, 2) != TYPE_WATER)) + && IS_BATTLER_OF_TYPE(battlerAtkPartner, TYPE_WATER) + && !IsTerastallized(battlerAtkPartner)) { RETURN_SCORE_PLUS(WEAK_EFFECT); } diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 40f3da2b5a..3458830a12 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -448,14 +448,16 @@ s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectivenes s32 dmg, moveType; uq4_12_t effectivenessMultiplier; bool32 isDamageMoveUnusable = FALSE; + bool32 toggledDynamax = FALSE; + bool32 toggledTera = FALSE; struct AiLogicData *aiData = AI_DATA; SetBattlerData(battlerAtk); SetBattlerData(battlerDef); + // Temporarily enable Z-Moves for damage calcs if (considerZPower && IsViableZMove(battlerAtk, move)) { - //temporarily enable z moves for damage calcs gBattleStruct->zmove.baseMoves[battlerAtk] = move; gBattleStruct->zmove.active = TRUE; } @@ -465,6 +467,18 @@ s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectivenes if (gMovesInfo[move].effect == EFFECT_NATURE_POWER) move = GetNaturePowerMove(); + // Temporarily enable other gimmicks for damage calcs if planned + if (AI_DATA->shouldDynamax[battlerAtk]) + { + toggledDynamax = TRUE; + gBattleStruct->dynamax.dynamaxed[battlerAtk] = TRUE; + } + if (AI_DATA->shouldTerastal[battlerAtk]) + { + toggledTera = TRUE; + gBattleStruct->tera.isTerastallized[GetBattlerSide(battlerAtk)] |= gBitTable[gBattlerPartyIndexes[battlerAtk]]; + } + gBattleStruct->dynamicMoveType = 0; @@ -582,6 +596,10 @@ s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectivenes gBattleStruct->swapDamageCategory = FALSE; gBattleStruct->zmove.active = FALSE; gBattleStruct->zmove.baseMoves[battlerAtk] = MOVE_NONE; + if (toggledDynamax) + gBattleStruct->dynamax.dynamaxed[battlerAtk] = FALSE; + if (toggledTera) + gBattleStruct->tera.isTerastallized[GetBattlerSide(battlerAtk)] &= ~(gBitTable[gBattlerPartyIndexes[battlerAtk]]); return dmg; } diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c index 597d0b7c5f..1622b94e9d 100644 --- a/src/battle_controller_opponent.c +++ b/src/battle_controller_opponent.c @@ -544,13 +544,6 @@ static void OpponentHandleChooseMove(u32 battler) default: { u16 chosenMove = moveInfo->moves[chosenMoveId]; - bool32 isSecondTrainer = (GetBattlerPosition(battler) == B_POSITION_OPPONENT_RIGHT) && (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) && !BATTLE_TWO_VS_ONE_OPPONENT; - u16 trainerId = isSecondTrainer ? gTrainerBattleOpponent_B : gTrainerBattleOpponent_A; - const struct TrainerMon *party = GetTrainerPartyFromId(trainerId); - bool32 shouldDynamax = FALSE; - if (party != NULL) - shouldDynamax = party[isSecondTrainer ? gBattlerPartyIndexes[battler] - MULTI_PARTY_SIZE : gBattlerPartyIndexes[battler]].shouldDynamax; - if (GetBattlerMoveTargetType(battler, chosenMove) & (MOVE_TARGET_USER_OR_SELECTED | MOVE_TARGET_USER)) gBattlerTarget = battler; if (GetBattlerMoveTargetType(battler, chosenMove) & MOVE_TARGET_BOTH) @@ -568,8 +561,11 @@ static void OpponentHandleChooseMove(u32 battler) else if (CanUltraBurst(battler)) BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (RET_ULTRA_BURST) | (gBattlerTarget << 8)); // If opponent can Dynamax and is allowed in the partydata, do it. - else if (CanDynamax(battler) && shouldDynamax) + else if (CanDynamax(battler) && AI_DATA->shouldDynamax[battler]) BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (RET_DYNAMAX) | (gBattlerTarget << 8)); + // If opponent can Terastal and is allowed in the partydata, do it. + else if (CanTerastallize(battler) && AI_DATA->shouldTerastal[battler]) + BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (RET_TERASTAL) | (gBattlerTarget << 8)); else BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (gBattlerTarget << 8)); } diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c index eeca1899b1..10a91a1485 100644 --- a/src/battle_controller_player.c +++ b/src/battle_controller_player.c @@ -454,6 +454,8 @@ static void HandleInputChooseTarget(u32 battler) BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_ULTRA_BURST | (gMultiUsePlayerCursor << 8)); else if (gBattleStruct->dynamax.playerSelect) BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_DYNAMAX | (gMultiUsePlayerCursor << 8)); + else if (gBattleStruct->tera.playerSelect) + BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_TERASTAL | (gMultiUsePlayerCursor << 8)); else BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | (gMultiUsePlayerCursor << 8)); EndBounceEffect(gMultiUsePlayerCursor, BOUNCE_HEALTHBOX); @@ -616,6 +618,8 @@ static void HandleInputShowEntireFieldTargets(u32 battler) BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_ULTRA_BURST | (gMultiUsePlayerCursor << 8)); else if (gBattleStruct->dynamax.playerSelect) BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_DYNAMAX | (gMultiUsePlayerCursor << 8)); + else if (gBattleStruct->tera.playerSelect) + BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_TERASTAL | (gMultiUsePlayerCursor << 8)); else BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | (gMultiUsePlayerCursor << 8)); HideTriggerSprites(); @@ -648,6 +652,8 @@ static void HandleInputShowTargets(u32 battler) BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_ULTRA_BURST | (gMultiUsePlayerCursor << 8)); else if (gBattleStruct->dynamax.playerSelect) BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_DYNAMAX | (gMultiUsePlayerCursor << 8)); + else if (gBattleStruct->tera.playerSelect) + BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_TERASTAL | (gMultiUsePlayerCursor << 8)); else BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | (gMultiUsePlayerCursor << 8)); HideTriggerSprites(); @@ -772,6 +778,8 @@ static void HandleInputChooseMove(u32 battler) BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_ULTRA_BURST | (gMultiUsePlayerCursor << 8)); else if (gBattleStruct->dynamax.playerSelect) BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_DYNAMAX | (gMultiUsePlayerCursor << 8)); + else if (gBattleStruct->tera.playerSelect) + BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_TERASTAL | (gMultiUsePlayerCursor << 8)); else BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | (gMultiUsePlayerCursor << 8)); HideTriggerSprites(); @@ -810,6 +818,7 @@ static void HandleInputChooseMove(u32 battler) gBattleStruct->mega.playerSelect = FALSE; gBattleStruct->burst.playerSelect = FALSE; gBattleStruct->dynamax.playerSelect = FALSE; + gBattleStruct->tera.playerSelect = FALSE; gBattleStruct->zmove.viable = FALSE; BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, 0xFFFF); HideTriggerSprites(); @@ -918,6 +927,12 @@ static void HandleInputChooseMove(u32 battler) ChangeDynamaxTriggerSprite(gBattleStruct->dynamax.triggerSpriteId, gBattleStruct->dynamax.playerSelect); PlaySE(SE_SELECT); } + else if (CanTerastallize(battler)) + { + gBattleStruct->tera.playerSelect ^= 1; + ChangeTeraTriggerSprite(gBattleStruct->tera.triggerSpriteId, gBattleStruct->tera.playerSelect); + PlaySE(SE_SELECT); + } } } @@ -926,6 +941,7 @@ static void ReloadMoveNames(u32 battler) gBattleStruct->mega.playerSelect = FALSE; gBattleStruct->burst.playerSelect = FALSE; gBattleStruct->dynamax.playerSelect = FALSE; + gBattleStruct->tera.playerSelect = FALSE; gBattleStruct->zmove.viewing = FALSE; MoveSelectionDestroyCursorAt(battler); MoveSelectionDisplayMoveNames(battler); @@ -2059,6 +2075,7 @@ static void PlayerHandleChooseMove(u32 battler) gBattleStruct->mega.playerSelect = FALSE; gBattleStruct->burst.playerSelect = FALSE; gBattleStruct->dynamax.playerSelect = FALSE; + gBattleStruct->tera.playerSelect = FALSE; if (!IsMegaTriggerSpriteActive()) gBattleStruct->mega.triggerSpriteId = 0xFF; if (CanMegaEvolve(battler)) @@ -2073,6 +2090,10 @@ static void PlayerHandleChooseMove(u32 battler) CreateDynamaxTriggerSprite(battler, 0); if (!IsZMoveTriggerSpriteActive()) gBattleStruct->zmove.triggerSpriteId = 0xFF; + if (!IsTeraTriggerSpriteActive()) + gBattleStruct->tera.triggerSpriteId = 0xFF; + if (CanTerastallize(battler)) + CreateTeraTriggerSprite(battler, 0); GetUsableZMoves(battler, moveInfo->moves); gBattleStruct->zmove.viable = IsZMoveUsable(battler, gMoveSelectionCursor[battler]); diff --git a/src/battle_gfx_sfx_util.c b/src/battle_gfx_sfx_util.c index 09b85e7768..0a4eaa07dd 100644 --- a/src/battle_gfx_sfx_util.c +++ b/src/battle_gfx_sfx_util.c @@ -645,6 +645,13 @@ void BattleLoadMonSpriteGfx(struct Pokemon *mon, u32 battler) BlendPalette(paletteOffset, 16, 4, RGB(31, 0, 12)); CpuCopy32(gPlttBufferFaded + paletteOffset, gPlttBufferUnfaded + paletteOffset, PLTT_SIZEOF(16)); } + + // Terastallization's tint + if (IsTerastallized(battler)) + { + BlendPalette(paletteOffset, 16, 8, GetTeraTypeRGB(GetBattlerTeraType(battler))); + CpuCopy32(gPlttBufferFaded + paletteOffset, gPlttBufferUnfaded + paletteOffset, PLTT_SIZEOF(16)); + } } void BattleGfxSfxDummy2(u16 species) @@ -710,6 +717,7 @@ bool8 BattleLoadAllHealthBoxesGfx(u8 state) LoadSpritePalette(&sSpritePalettes_HealthBoxHealthBar[0]); LoadSpritePalette(&sSpritePalettes_HealthBoxHealthBar[1]); MegaIndicator_LoadSpritesGfx(); + TeraIndicator_LoadSpriteGfx(); } else if (!IsDoubleBattle()) { diff --git a/src/battle_interface.c b/src/battle_interface.c index 34cdec6e5c..6637fa4c4e 100644 --- a/src/battle_interface.c +++ b/src/battle_interface.c @@ -854,6 +854,9 @@ u8 CreateBattlerHealthboxSprites(u8 battlerId) // Create mega indicator sprite. MegaIndicator_CreateSprite(battlerId, healthboxLeftSpriteId); + // Create tera indicator sprites. + TeraIndicator_CreateSprite(battlerId, healthboxLeftSpriteId); + gBattleStruct->ballSpriteIds[0] = MAX_SPRITES; gBattleStruct->ballSpriteIds[1] = MAX_SPRITES; @@ -937,6 +940,7 @@ void SetHealthboxSpriteInvisible(u8 healthboxSpriteId) gSprites[gSprites[healthboxSpriteId].hMain_HealthBarSpriteId].invisible = TRUE; gSprites[gSprites[healthboxSpriteId].oam.affineParam].invisible = TRUE; MegaIndicator_SetVisibilities(healthboxSpriteId, TRUE); + TeraIndicator_SetVisibilities(healthboxSpriteId, TRUE); } void SetHealthboxSpriteVisible(u8 healthboxSpriteId) @@ -945,6 +949,7 @@ void SetHealthboxSpriteVisible(u8 healthboxSpriteId) gSprites[gSprites[healthboxSpriteId].hMain_HealthBarSpriteId].invisible = FALSE; gSprites[gSprites[healthboxSpriteId].oam.affineParam].invisible = FALSE; MegaIndicator_SetVisibilities(healthboxSpriteId, FALSE); + TeraIndicator_SetVisibilities(healthboxSpriteId, FALSE); } static void UpdateSpritePos(u8 spriteId, s16 x, s16 y) @@ -971,6 +976,7 @@ static void TryToggleHealboxVisibility(u32 priority, u32 healthboxLeftSpriteId, gSprites[healthboxRightSpriteId].invisible = invisible; gSprites[healthbarSpriteId].invisible = invisible; MegaIndicator_SetVisibilities(healthboxLeftSpriteId, invisible); + TeraIndicator_SetVisibilities(healthboxLeftSpriteId, invisible); } void UpdateOamPriorityInAllHealthboxes(u8 priority, bool32 hideHPBoxes) @@ -988,6 +994,7 @@ void UpdateOamPriorityInAllHealthboxes(u8 priority, bool32 hideHPBoxes) gSprites[healthbarSpriteId].oam.priority = priority; MegaIndicator_UpdateOamPriority(healthboxLeftSpriteId, priority); + TeraIndicator_UpdateOamPriorities(healthboxLeftSpriteId, priority); if (B_HIDE_HEALTHBOX_IN_ANIMS == TRUE && hideHPBoxes && IsBattlerAlive(i)) TryToggleHealboxVisibility(priority, healthboxLeftSpriteId, healthboxRightSpriteId, healthbarSpriteId); @@ -1050,6 +1057,13 @@ static void UpdateLvlInHealthbox(u8 healthboxSpriteId, u8 lvl) MegaIndicator_UpdateLevel(healthboxSpriteId, lvl); MegaIndicator_SetVisibilities(healthboxSpriteId, FALSE); } + else if (IsTerastallized(battler)) + { + objVram = ConvertIntToDecimalStringN(text, lvl, STR_CONV_MODE_LEFT_ALIGN, 3); + xPos = 5 * (3 - (objVram - (text + 2))) - 1; + TeraIndicator_UpdateLevel(healthboxSpriteId, lvl); + TeraIndicator_SetVisibilities(healthboxSpriteId, FALSE); + } else { text[0] = CHAR_EXTRA_SYMBOL; @@ -1473,6 +1487,7 @@ void HideTriggerSprites(void) HideBurstTriggerSprite(); HideZMoveTriggerSprite(); HideDynamaxTriggerSprite(); + HideTeraTriggerSprite(); } void DestroyMegaTriggerSprite(void) @@ -2532,6 +2547,10 @@ void UpdateHealthboxAttribute(u8 healthboxSpriteId, struct Pokemon *mon, u8 elem u32 battlerId = gSprites[healthboxSpriteId].hMain_Battler; s32 maxHp = GetMonData(mon, MON_DATA_MAX_HP); s32 currHp = GetMonData(mon, MON_DATA_HP); + + // This fixes a bug that should likely never happen involving switching between two Teras. + if (elementId == HEALTHBOX_ALL) + TeraIndicator_UpdateType(battlerId, healthboxSpriteId); if (GetBattlerSide(battlerId) == B_SIDE_PLAYER) { diff --git a/src/battle_main.c b/src/battle_main.c index 6290d5305c..eba4916ef4 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -590,13 +590,15 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] = //.teraShard = ITEM_FAIRY_TERA_SHARD, //.arceusForm = SPECIES_ARCEUS_FAIRY, }, - /* [TYPE_STELLAR] = { - .name = _("Stellar"), - .teraShard = ITEM_STELLAR_TERA_SHARD, + .name = _("Stellr"), + .generic = _("a STELLAR move"), + .palette = 15, + .zMove = MOVE_BREAKNECK_BLITZ, + .maxMove = MOVE_MAX_STRIKE, + // .teraShard = ITEM_STELLAR_TERA_SHARD, }, - */ }; // extra args are money and ball @@ -2325,6 +2327,11 @@ u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer u32 data = partyData[i].gigantamaxFactor; SetMonData(&party[i], MON_DATA_GIGANTAMAX_FACTOR, &data); } + if (partyData[i].teraType > 0) + { + u32 data = partyData[i].teraType; + SetMonData(&party[i], MON_DATA_TERA_TYPE, &data); + } CalculateMonStats(&party[i]); if (B_TRAINER_CLASS_POKE_BALLS >= GEN_7 && ball == -1) @@ -4608,6 +4615,7 @@ static void HandleTurnActionSelectionState(void) gBattleStruct->mega.toEvolve &= ~(gBitTable[BATTLE_PARTNER(GetBattlerPosition(battler))]); gBattleStruct->burst.toBurst &= ~(gBitTable[BATTLE_PARTNER(GetBattlerPosition(battler))]); gBattleStruct->dynamax.toDynamax &= ~(gBitTable[BATTLE_PARTNER(GetBattlerPosition(battler))]); + gBattleStruct->tera.toTera &= ~(gBitTable[BATTLE_PARTNER(GetBattlerPosition(battler))]); gBattleStruct->dynamax.usingMaxMove[BATTLE_PARTNER(GetBattlerPosition(battler))] = FALSE; gBattleStruct->zmove.toBeUsed[BATTLE_PARTNER(GetBattlerPosition(battler))] = MOVE_NONE; BtlController_EmitEndBounceEffect(battler, BUFFER_A); @@ -4697,7 +4705,7 @@ static void HandleTurnActionSelectionState(void) } // Get the chosen move position (and thus the chosen move) and target from the returned buffer. - gBattleStruct->chosenMovePositions[battler] = gBattleResources->bufferB[battler][2] & ~(RET_MEGA_EVOLUTION | RET_ULTRA_BURST | RET_DYNAMAX); + gBattleStruct->chosenMovePositions[battler] = gBattleResources->bufferB[battler][2] & ~(RET_MEGA_EVOLUTION | RET_ULTRA_BURST | RET_DYNAMAX | RET_TERASTAL); gChosenMoveByBattler[battler] = gBattleMons[battler].moves[gBattleStruct->chosenMovePositions[battler]]; gBattleStruct->moveTarget[battler] = gBattleResources->bufferB[battler][3]; @@ -4708,6 +4716,8 @@ static void HandleTurnActionSelectionState(void) gBattleStruct->burst.toBurst |= gBitTable[battler]; else if (gBattleResources->bufferB[battler][2] & RET_DYNAMAX) gBattleStruct->dynamax.toDynamax |= gBitTable[battler]; + else if (gBattleResources->bufferB[battler][2] & RET_TERASTAL) + gBattleStruct->tera.toTera |= gBitTable[battler]; // Max Move check if (ShouldUseMaxMove(battler, gChosenMoveByBattler[battler])) @@ -5325,7 +5335,8 @@ static void PopulateArrayWithBattlers(u8 *battlers) static bool32 TryDoGimmicksBeforeMoves(void) { if (!(gHitMarker & HITMARKER_RUN) - && (gBattleStruct->mega.toEvolve || gBattleStruct->burst.toBurst || gBattleStruct->dynamax.toDynamax)) + && (gBattleStruct->mega.toEvolve || gBattleStruct->burst.toBurst + || gBattleStruct->dynamax.toDynamax || gBattleStruct->tera.toTera)) { u32 i, battler; u8 order[MAX_BATTLERS_COUNT]; @@ -5334,6 +5345,17 @@ static bool32 TryDoGimmicksBeforeMoves(void) SortBattlersBySpeed(order, FALSE); for (i = 0; i < gBattlersCount; i++) { + // Tera Check + if (gBattleStruct->tera.toTera & gBitTable[order[i]]) + { + gBattlerAttacker = order[i]; + gBattleScripting.battler = gBattlerAttacker; + gBattleStruct->tera.toTera &= ~(gBitTable[gBattlerAttacker]); + PrepareBattlerForTera(gBattlerAttacker); + PREPARE_TYPE_BUFFER(gBattleTextBuff1, GetBattlerTeraType(gBattlerAttacker)); + BattleScriptExecute(BattleScript_Terastallization); + return TRUE; + } // Dynamax Check if (gBattleStruct->dynamax.toDynamax & gBitTable[order[i]]) { @@ -5996,7 +6018,9 @@ void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk) } else if (gMovesInfo[move].effect == EFFECT_REVELATION_DANCE) { - if (gBattleMons[battlerAtk].type1 != TYPE_MYSTERY) + if (IsTerastallized(battlerAtk) && GetBattlerTeraType(battlerAtk) != TYPE_STELLAR) + gBattleStruct->dynamicMoveType = GetBattlerTeraType(battlerAtk); + else if (gBattleMons[battlerAtk].type1 != TYPE_MYSTERY) gBattleStruct->dynamicMoveType = gBattleMons[battlerAtk].type1 | F_DYNAMIC_TYPE_SET; else if (gBattleMons[battlerAtk].type2 != TYPE_MYSTERY) gBattleStruct->dynamicMoveType = gBattleMons[battlerAtk].type2 | F_DYNAMIC_TYPE_SET; @@ -6038,6 +6062,10 @@ void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk) gBattleStruct->dynamicMoveType = TYPE_NORMAL | F_DYNAMIC_TYPE_SET; } } + else if (gMovesInfo[move].effect == EFFECT_TERA_BLAST && IsTerastallized(battlerAtk)) + { + gBattleStruct->dynamicMoveType = GetBattlerTeraType(battlerAtk) | F_DYNAMIC_TYPE_SET; + } attackerAbility = GetBattlerAbility(battlerAtk); @@ -6046,6 +6074,7 @@ void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk) && gMovesInfo[move].effect != EFFECT_WEATHER_BALL && gMovesInfo[move].effect != EFFECT_CHANGE_TYPE_ON_ITEM && gMovesInfo[move].effect != EFFECT_NATURAL_GIFT + && !(gMovesInfo[move].effect == EFFECT_TERA_BLAST && IsTerastallized(battlerAtk)) && ((attackerAbility == ABILITY_PIXILATE && (ateType = TYPE_FAIRY)) || (attackerAbility == ABILITY_REFRIGERATE && (ateType = TYPE_ICE)) || (attackerAbility == ABILITY_AERILATE && (ateType = TYPE_FLYING)) diff --git a/src/battle_message.c b/src/battle_message.c index 2efb99ff5f..5f7e6ca052 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -846,11 +846,13 @@ static const u8 sText_ElectroShotCharging[] = _("{B_ATK_NAME_WITH_PREFIX} absorb static const u8 sText_ItemWasUsedUp[] = _("The {B_LAST_ITEM}\nwas used up..."); static const u8 sText_AttackerLostItsType[] = _("{B_ATK_NAME_WITH_PREFIX} lost\nits {B_BUFF1} type!"); static const u8 sText_ShedItsTail[] = _("{B_ATK_NAME_WITH_PREFIX} shed its tail\nto create a decoy!"); +static const u8 sText_PkmnTerastallizedInto[] = _("{B_ATK_NAME_WITH_PREFIX} terastallized\ninto the {B_BUFF1} type!"); static const u8 sText_SupersweetAromaWafts[] = _("A supersweet aroma is wafting from\nthe syrup covering {B_ATK_NAME_WITH_PREFIX}!"); static const u8 sText_TidyingUpComplete[] = _("Tidying up complete!"); const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = { + [STRINGID_PKMNTERASTALLIZEDINTO - BATTLESTRINGS_TABLE_START] = sText_PkmnTerastallizedInto, [STRINGID_TIDYINGUPCOMPLETE - BATTLESTRINGS_TABLE_START] = sText_TidyingUpComplete, [STRINGID_SUPERSWEETAROMAWAFTS - BATTLESTRINGS_TABLE_START] = sText_SupersweetAromaWafts, [STRINGID_SHEDITSTAIL - BATTLESTRINGS_TABLE_START] = sText_ShedItsTail, diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index feb0149c68..ddcbeee78c 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1215,7 +1215,8 @@ bool32 ProteanTryChangeType(u32 battler, u32 ability, u32 move, u32 moveType) && !gDisableStructs[gBattlerAttacker].usedProteanLibero && (gBattleMons[battler].type1 != moveType || gBattleMons[battler].type2 != moveType || (gBattleMons[battler].type3 != moveType && gBattleMons[battler].type3 != TYPE_MYSTERY)) - && move != MOVE_STRUGGLE) + && move != MOVE_STRUGGLE + && !IsTerastallized(battler)) { SET_BATTLER_TYPE(battler, moveType); return TRUE; @@ -2085,12 +2086,12 @@ END: // of a move that is Super Effective against a Flying-type Pokémon. if (gBattleWeather & B_WEATHER_STRONG_WINDS) { - if ((GetBattlerType(gBattlerTarget, 0) == TYPE_FLYING - && GetTypeModifier(moveType, GetBattlerType(gBattlerTarget, 0)) >= UQ_4_12(2.0)) - || (GetBattlerType(gBattlerTarget, 1) == TYPE_FLYING - && GetTypeModifier(moveType, GetBattlerType(gBattlerTarget, 1)) >= UQ_4_12(2.0)) - || (GetBattlerType(gBattlerTarget, 2) == TYPE_FLYING - && GetTypeModifier(moveType, GetBattlerType(gBattlerTarget, 2)) >= UQ_4_12(2.0))) + if ((GetBattlerType(gBattlerTarget, 0, FALSE) == TYPE_FLYING + && GetTypeModifier(moveType, GetBattlerType(gBattlerTarget, 0, FALSE)) >= UQ_4_12(2.0)) + || (GetBattlerType(gBattlerTarget, 1, FALSE) == TYPE_FLYING + && GetTypeModifier(moveType, GetBattlerType(gBattlerTarget, 1, FALSE)) >= UQ_4_12(2.0)) + || (GetBattlerType(gBattlerTarget, 2, FALSE) == TYPE_FLYING + && GetTypeModifier(moveType, GetBattlerType(gBattlerTarget, 2, FALSE)) >= UQ_4_12(2.0))) { gBattlerAbility = gBattlerTarget; BattleScriptPushCursor(); @@ -3806,6 +3807,15 @@ void SetMoveEffect(bool32 primary, bool32 certain) gBattlescriptCurrInstr = BattleScript_EffectPsychicNoise; } break; + case MOVE_EFFECT_TERA_BLAST: + if (IsTerastallized(gEffectBattler) + && GetBattlerTeraType(gEffectBattler) == TYPE_STELLAR + && !NoAliveMonsForEitherParty()) + { + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_LowerAtkSpAtk; + } + break; } } } @@ -9648,8 +9658,9 @@ static void Cmd_various(void) case VARIOUS_TRY_SOAK: { VARIOUS_ARGS(const u8 *failInstr); - if (GetBattlerType(gBattlerTarget, 0) == gMovesInfo[gCurrentMove].type - && GetBattlerType(gBattlerTarget, 1) == gMovesInfo[gCurrentMove].type) + if ((GetBattlerType(gBattlerTarget, 0, FALSE) == gMovesInfo[gCurrentMove].type + && GetBattlerType(gBattlerTarget, 1, FALSE) == gMovesInfo[gCurrentMove].type) + || IsTerastallized(gBattlerTarget)) { gBattlescriptCurrInstr = cmd->failInstr; } @@ -9982,7 +9993,7 @@ static void Cmd_various(void) case VARIOUS_TRY_THIRD_TYPE: { VARIOUS_ARGS(const u8 *failInstr); - if (IS_BATTLER_OF_TYPE(battler, gMovesInfo[gCurrentMove].argument)) + if (IS_BATTLER_OF_TYPE(battler, gMovesInfo[gCurrentMove].argument) || IsTerastallized(battler)) { gBattlescriptCurrInstr = cmd->failInstr; } @@ -12049,6 +12060,12 @@ static void Cmd_tryconversiontypechange(void) u8 moveChecked = 0; u8 moveType = 0; + if (IsTerastallized(gBattlerAttacker)) + { + gBattlescriptCurrInstr = cmd->failInstr; + return; + } + if (B_UPDATED_CONVERSION >= GEN_6) { // Changes user's type to its first move's type @@ -12825,6 +12842,14 @@ static void Cmd_settypetorandomresistance(void) { gBattlescriptCurrInstr = cmd->failInstr; } + else if (IsTerastallized(gBattlerAttacker)) + { + gBattlescriptCurrInstr = cmd->failInstr; + } + else if (gLastHitByType[gBattlerAttacker] == TYPE_STELLAR) + { + gBattlescriptCurrInstr = cmd->failInstr; + } else { u32 i, resistTypes = 0; @@ -14812,7 +14837,7 @@ static void Cmd_settypetoterrain(void) break; } - if (!IS_BATTLER_OF_TYPE(gBattlerAttacker, terrainType)) + if (!IS_BATTLER_OF_TYPE(gBattlerAttacker, terrainType) && !IsTerastallized(gBattlerAttacker)) { SET_BATTLER_TYPE(gBattlerAttacker, terrainType); PREPARE_TYPE_BUFFER(gBattleTextBuff1, terrainType); @@ -16378,14 +16403,18 @@ void BS_TryReflectType(void) { NATIVE_ARGS(const u8 *failInstr); u16 targetBaseSpecies = GET_BASE_SPECIES_ID(gBattleMons[gBattlerTarget].species); - u8 targetType1 = GetBattlerType(gBattlerTarget, 0); - u8 targetType2 = GetBattlerType(gBattlerTarget, 1); - u8 targetType3 = GetBattlerType(gBattlerTarget, 2); + u8 targetType1 = GetBattlerType(gBattlerTarget, 0, FALSE); + u8 targetType2 = GetBattlerType(gBattlerTarget, 1, FALSE); + u8 targetType3 = GetBattlerType(gBattlerTarget, 2, FALSE); if (targetBaseSpecies == SPECIES_ARCEUS || targetBaseSpecies == SPECIES_SILVALLY) { gBattlescriptCurrInstr = cmd->failInstr; } + else if (IsTerastallized(gBattlerAttacker)) + { + gBattlescriptCurrInstr = cmd->failInstr; + } else if (IS_BATTLER_TYPELESS(gBattlerTarget)) { gBattlescriptCurrInstr = cmd->failInstr; @@ -16746,7 +16775,8 @@ void BS_AllySwitchFailChance(void) void BS_SetPhotonGeyserCategory(void) { NATIVE_ARGS(); - gBattleStruct->swapDamageCategory = (GetCategoryBasedOnStats(gBattlerAttacker) == DAMAGE_CATEGORY_PHYSICAL); + if (!(gMovesInfo[gCurrentMove].effect == EFFECT_TERA_BLAST && !IsTerastallized(gBattlerAttacker))) + gBattleStruct->swapDamageCategory = (GetCategoryBasedOnStats(gBattlerAttacker) == DAMAGE_CATEGORY_PHYSICAL); gBattlescriptCurrInstr = cmd->nextInstr; } diff --git a/src/battle_terastal.c b/src/battle_terastal.c new file mode 100644 index 0000000000..8cc03aca57 --- /dev/null +++ b/src/battle_terastal.c @@ -0,0 +1,786 @@ +#include "global.h" +#include "battle.h" +#include "battle_anim.h" +#include "battle_controllers.h" +#include "battle_interface.h" +#include "battle_terastal.h" +#include "event_data.h" +#include "item.h" +#include "palette.h" +#include "pokemon.h" +#include "sprite.h" +#include "util.h" +#include "constants/abilities.h" +#include "constants/hold_effects.h" +#include "constants/rgb.h" + +// Sets flags and variables upon a battler's Terastallization. +void PrepareBattlerForTera(u32 battler) +{ + u32 side = GetBattlerSide(battler); + struct Pokemon *party = GetBattlerParty(battler); + u32 index = gBattlerPartyIndexes[battler]; + + // Update TeraData fields. + gBattleStruct->tera.isTerastallized[side] |= gBitTable[index]; + gBattleStruct->tera.alreadyTerastallized[battler] = TRUE; + + // Remove Tera Orb charge. + if (B_FLAG_TERA_ORB_CHARGED != 0 + && B_FLAG_TERA_ORB_NO_COST != 0 + && !FlagGet(B_FLAG_TERA_ORB_NO_COST) + && side == B_SIDE_PLAYER + && !(gBattleTypeFlags & BATTLE_TYPE_DOUBLE && !IsPartnerMonFromSameTrainer(battler))) + { + FlagClear(B_FLAG_TERA_ORB_CHARGED); + } + + // Show indicator and do palette blend. + UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], &party[index], HEALTHBOX_ALL); + BlendPalette(OBJ_PLTT_ID(battler), 16, 8, GetTeraTypeRGB(GetBattlerTeraType(battler))); + CpuCopy32(gPlttBufferFaded + OBJ_PLTT_ID(battler), gPlttBufferUnfaded + OBJ_PLTT_ID(battler), PLTT_SIZEOF(16)); +} + +// Returns whether a battler can Terastallize. +bool32 CanTerastallize(u32 battler) +{ + u32 holdEffect = GetBattlerHoldEffect(battler, FALSE); + + // Check if Player has Tera Orb and has charge. + if (B_FLAG_TERA_ORB_CHARGED != 0 + && (battler == B_POSITION_PLAYER_LEFT || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && battler == B_POSITION_PLAYER_RIGHT)) + && !(CheckBagHasItem(ITEM_TERA_ORB, 1) + && FlagGet(B_FLAG_TERA_ORB_CHARGED))) + { + return FALSE; + } + + // Check if Trainer has already Terastallized. + if (gBattleStruct->tera.alreadyTerastallized[battler]) + { + return FALSE; + } + + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE + && IsPartnerMonFromSameTrainer(battler) + && (gBattleStruct->tera.alreadyTerastallized[BATTLE_PARTNER(battler)] + || (gBattleStruct->tera.toTera & gBitTable[BATTLE_PARTNER(battler)]))) + { + return FALSE; + } + + // Check if battler is holding a Z-Crystal or Mega Stone. + if (holdEffect == HOLD_EFFECT_Z_CRYSTAL || holdEffect == HOLD_EFFECT_MEGA_STONE) + { + return FALSE; + } + + // Every check passed! + return TRUE; +} + +// Returns a battler's Tera type. +u32 GetBattlerTeraType(u32 battler) +{ + struct Pokemon *mon = &GetBattlerParty(battler)[gBattlerPartyIndexes[battler]]; + return GetMonData(mon, MON_DATA_TERA_TYPE); +} + +// Returns whether a battler is terastallized. +bool32 IsTerastallized(u32 battler) +{ + return gBattleStruct->tera.isTerastallized[GetBattlerSide(battler)] & gBitTable[gBattlerPartyIndexes[battler]]; +} + + +// Uses up a type's Stellar boost. +void ExpendTypeStellarBoost(u32 battler, u32 type) +{ + if (type < 32) // avoid OOB access + gBattleStruct->tera.stellarBoostFlags[GetBattlerSide(battler)] |= gBitTable[type]; +} + +// Checks whether a type's Stellar boost has been expended. +bool32 IsTypeStellarBoosted(u32 battler, u32 type) +{ + if (type < 32) // avoid OOB access + return !(gBattleStruct->tera.stellarBoostFlags[GetBattlerSide(battler)] & gBitTable[type]); + else + return FALSE; +} + +// Returns the STAB power multiplier to use when Terastallized. +// Power multipliers from Smogon Research thread. +uq4_12_t GetTeraMultiplier(u32 battler, u32 type) +{ + u32 teraType = GetBattlerTeraType(battler); + bool32 hasAdaptability = (GetBattlerAbility(battler) == ABILITY_ADAPTABILITY); + + // Safety check. + if (!IsTerastallized(battler)) + return UQ_4_12(1.0); + + // Stellar-type checks. + if (teraType == TYPE_STELLAR) + { + bool32 shouldBoost = IsTypeStellarBoosted(battler, type); + if (IS_BATTLER_OF_BASE_TYPE(battler, type)) + { + if (shouldBoost) + return UQ_4_12(2.0); + else + return UQ_4_12(1.5); + } + else if (shouldBoost) + return UQ_4_12(1.2); + else + return UQ_4_12(1.0); + } + // Base and Tera type. + if (type == teraType && IS_BATTLER_OF_BASE_TYPE(battler, type)) + { + if (hasAdaptability) + return UQ_4_12(2.25); + else + return UQ_4_12(2.0); + } + // Base or Tera type only. + else if ((type == teraType && !IS_BATTLER_OF_BASE_TYPE(battler, type)) + || (type != teraType && IS_BATTLER_OF_BASE_TYPE(battler, type))) + { + if (hasAdaptability) + return UQ_4_12(2.0); + else + return UQ_4_12(1.5); + } + // Neither base or Tera type. + else + { + return UQ_4_12(1.0); + } +} + +// Most values pulled from the Tera type icon palette. +const u16 sTeraTypeRGBValues[NUMBER_OF_MON_TYPES] = { + [TYPE_NORMAL] = RGB_WHITE, // custom + [TYPE_FIGHTING] = RGB(26, 8, 14), + [TYPE_FLYING] = RGB(31, 26, 7), + [TYPE_POISON] = RGB(26, 10, 25), // custom + [TYPE_GROUND] = RGB(25, 23, 18), + [TYPE_ROCK] = RGB(18, 16, 8), // custom + [TYPE_BUG] = RGB(18, 24, 6), + [TYPE_GHOST] = RGB(12, 10, 16), + [TYPE_STEEL] = RGB(19, 19, 20), + [TYPE_MYSTERY] = RGB_WHITE, + [TYPE_FIRE] = RGB(31, 20, 11), + [TYPE_WATER] = RGB(10, 18, 27), + [TYPE_GRASS] = RGB(12, 24, 11), + [TYPE_ELECTRIC] = RGB(30, 26, 7), + [TYPE_PSYCHIC] = RGB(31, 14, 15), + [TYPE_ICE] = RGB(14, 26, 25), + [TYPE_DRAGON] = RGB(10, 18, 27), + [TYPE_DARK] = RGB(6, 5, 8), + [TYPE_FAIRY] = RGB(31, 15, 21), + [TYPE_STELLAR] = RGB(10, 18, 27), +}; + +u16 GetTeraTypeRGB(u32 type) +{ + return sTeraTypeRGBValues[type]; +} + +// TERASTAL TRIGGER: +static const u8 ALIGNED(4) sTeraTriggerGfx[] = INCBIN_U8("graphics/battle_interface/tera_trigger.4bpp"); +static const u16 sTeraTriggerPal[] = INCBIN_U16("graphics/battle_interface/tera_trigger.gbapal"); + +static const struct SpriteSheet sSpriteSheet_TeraTrigger = +{ + sTeraTriggerGfx, sizeof(sTeraTriggerGfx), TAG_TERA_TRIGGER_TILE +}; +static const struct SpritePalette sSpritePalette_TeraTrigger = +{ + sTeraTriggerPal, TAG_TERA_TRIGGER_PAL +}; + +static const struct OamData sOamData_TeraTrigger = +{ + .y = 0, + .affineMode = 0, + .objMode = 0, + .mosaic = 0, + .bpp = 0, + .shape = ST_OAM_SQUARE, + .x = 0, + .matrixNum = 0, + .size = 2, + .tileNum = 0, + .priority = 1, + .paletteNum = 0, + .affineParam = 0, +}; + +static const union AnimCmd sSpriteAnim_TeraTriggerOff[] = +{ + ANIMCMD_FRAME(0, 0), + ANIMCMD_END +}; + +static const union AnimCmd sSpriteAnim_TeraTriggerOn[] = +{ + ANIMCMD_FRAME(16, 0), + ANIMCMD_END +}; + +static const union AnimCmd *const sSpriteAnimTable_TeraTrigger[] = +{ + sSpriteAnim_TeraTriggerOff, + sSpriteAnim_TeraTriggerOn, +}; + +static void SpriteCb_TeraTrigger(struct Sprite *sprite); +static const struct SpriteTemplate sSpriteTemplate_TeraTrigger = +{ + .tileTag = TAG_TERA_TRIGGER_TILE, + .paletteTag = TAG_TERA_TRIGGER_PAL, + .oam = &sOamData_TeraTrigger, + .anims = sSpriteAnimTable_TeraTrigger, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraTrigger +}; + +// Tera Evolution Trigger icon functions. +void ChangeTeraTriggerSprite(u8 spriteId, u8 animId) +{ + StartSpriteAnim(&gSprites[spriteId], animId); +} + +#define SINGLES_TERA_TRIGGER_POS_X_OPTIMAL (30) +#define SINGLES_TERA_TRIGGER_POS_X_PRIORITY (31) +#define SINGLES_TERA_TRIGGER_POS_X_SLIDE (15) +#define SINGLES_TERA_TRIGGER_POS_Y_DIFF (-11) + +#define DOUBLES_TERA_TRIGGER_POS_X_OPTIMAL (30) +#define DOUBLES_TERA_TRIGGER_POS_X_PRIORITY (31) +#define DOUBLES_TERA_TRIGGER_POS_X_SLIDE (15) +#define DOUBLES_TERA_TRIGGER_POS_Y_DIFF (-4) + +#define tBattler data[0] +#define tHide data[1] + +void CreateTeraTriggerSprite(u8 battler, u8 palId) +{ + LoadSpritePalette(&sSpritePalette_TeraTrigger); + if (GetSpriteTileStartByTag(TAG_TERA_TRIGGER_TILE) == 0xFFFF) + { + LoadSpriteSheet(&sSpriteSheet_TeraTrigger); + } + if (gBattleStruct->tera.triggerSpriteId == 0xFF) + { + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + gBattleStruct->tera.triggerSpriteId = CreateSprite(&sSpriteTemplate_TeraTrigger, + gSprites[gHealthboxSpriteIds[battler]].x - DOUBLES_TERA_TRIGGER_POS_X_SLIDE, + gSprites[gHealthboxSpriteIds[battler]].y - DOUBLES_TERA_TRIGGER_POS_Y_DIFF, 0); + else + gBattleStruct->tera.triggerSpriteId = CreateSprite(&sSpriteTemplate_TeraTrigger, + gSprites[gHealthboxSpriteIds[battler]].x - SINGLES_TERA_TRIGGER_POS_X_SLIDE, + gSprites[gHealthboxSpriteIds[battler]].y - SINGLES_TERA_TRIGGER_POS_Y_DIFF, 0); + } + gSprites[gBattleStruct->tera.triggerSpriteId].tBattler = battler; + gSprites[gBattleStruct->tera.triggerSpriteId].tHide = FALSE; + + ChangeTeraTriggerSprite(gBattleStruct->tera.triggerSpriteId, palId); +} + +static void SpriteCb_TeraTrigger(struct Sprite *sprite) +{ + s32 xSlide, xPriority, xOptimal; + s32 yDiff; + + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + { + xSlide = DOUBLES_TERA_TRIGGER_POS_X_SLIDE; + xPriority = DOUBLES_TERA_TRIGGER_POS_X_PRIORITY; + xOptimal = DOUBLES_TERA_TRIGGER_POS_X_OPTIMAL; + yDiff = DOUBLES_TERA_TRIGGER_POS_Y_DIFF; + } + else + { + xSlide = SINGLES_TERA_TRIGGER_POS_X_SLIDE; + xPriority = SINGLES_TERA_TRIGGER_POS_X_PRIORITY; + xOptimal = SINGLES_TERA_TRIGGER_POS_X_OPTIMAL; + yDiff = SINGLES_TERA_TRIGGER_POS_Y_DIFF; + } + + if (sprite->tHide) + { + if (sprite->x != gSprites[gHealthboxSpriteIds[sprite->tBattler]].x - xSlide) + sprite->x++; + + if (sprite->x >= gSprites[gHealthboxSpriteIds[sprite->tBattler]].x - xPriority) + sprite->oam.priority = 2; + else + sprite->oam.priority = 1; + + sprite->y = gSprites[gHealthboxSpriteIds[sprite->tBattler]].y - yDiff; + sprite->y2 = gSprites[gHealthboxSpriteIds[sprite->tBattler]].y2 - yDiff; + if (sprite->x == gSprites[gHealthboxSpriteIds[sprite->tBattler]].x - xSlide) + DestroyTeraTriggerSprite(); + } + else + { + if (sprite->x != gSprites[gHealthboxSpriteIds[sprite->tBattler]].x - xOptimal) + sprite->x--; + + if (sprite->x >= gSprites[gHealthboxSpriteIds[sprite->tBattler]].x - xPriority) + sprite->oam.priority = 2; + else + sprite->oam.priority = 1; + + sprite->y = gSprites[gHealthboxSpriteIds[sprite->tBattler]].y - yDiff; + sprite->y2 = gSprites[gHealthboxSpriteIds[sprite->tBattler]].y2 - yDiff; + } +} + +bool32 IsTeraTriggerSpriteActive(void) +{ + if (GetSpriteTileStartByTag(TAG_TERA_TRIGGER_TILE) == 0xFFFF) + return FALSE; + else if (IndexOfSpritePaletteTag(TAG_TERA_TRIGGER_PAL) != 0xFF) + return TRUE; + else + return FALSE; +} + +void HideTeraTriggerSprite(void) +{ + if (gBattleStruct->tera.triggerSpriteId != 0xFF) + { + ChangeTeraTriggerSprite(gBattleStruct->tera.triggerSpriteId, 0); + gSprites[gBattleStruct->tera.triggerSpriteId].tHide = TRUE; + } +} + +void DestroyTeraTriggerSprite(void) +{ + FreeSpritePaletteByTag(TAG_TERA_TRIGGER_PAL); + FreeSpriteTilesByTag(TAG_TERA_TRIGGER_TILE); + if (gBattleStruct->tera.triggerSpriteId != 0xFF) + DestroySprite(&gSprites[gBattleStruct->tera.triggerSpriteId]); + gBattleStruct->tera.triggerSpriteId = 0xFF; +} + +#undef tBattler +#undef tHide + +// TERA INDICATOR: +static const u16 sTeraIndicatorPal[] = INCBIN_U16("graphics/battle_interface/tera_indicator.gbapal"); +static const u8 ALIGNED(4) sNormalIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/normal_indicator.4bpp"); +static const u8 ALIGNED(4) sFightingIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/fighting_indicator.4bpp"); +static const u8 ALIGNED(4) sFlyingIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/flying_indicator.4bpp"); +static const u8 ALIGNED(4) sPoisonIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/poison_indicator.4bpp"); +static const u8 ALIGNED(4) sGroundIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/ground_indicator.4bpp"); +static const u8 ALIGNED(4) sRockIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/rock_indicator.4bpp"); +static const u8 ALIGNED(4) sBugIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/bug_indicator.4bpp"); +static const u8 ALIGNED(4) sGhostIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/ghost_indicator.4bpp"); +static const u8 ALIGNED(4) sSteelIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/steel_indicator.4bpp"); +static const u8 ALIGNED(4) sFireIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/fire_indicator.4bpp"); +static const u8 ALIGNED(4) sWaterIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/water_indicator.4bpp"); +static const u8 ALIGNED(4) sGrassIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/grass_indicator.4bpp"); +static const u8 ALIGNED(4) sElectricIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/electric_indicator.4bpp"); +static const u8 ALIGNED(4) sPsychicIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/psychic_indicator.4bpp"); +static const u8 ALIGNED(4) sIceIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/ice_indicator.4bpp"); +static const u8 ALIGNED(4) sDragonIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/dragon_indicator.4bpp"); +static const u8 ALIGNED(4) sDarkIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/dark_indicator.4bpp"); +static const u8 ALIGNED(4) sFairyIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/fairy_indicator.4bpp"); +static const u8 ALIGNED(4) sStellarIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/stellar_indicator.4bpp"); + +static void SpriteCb_TeraIndicator(struct Sprite *sprite); +static const s8 sIndicatorPositions[][2] = +{ + [B_POSITION_PLAYER_LEFT] = {53, -9}, + [B_POSITION_OPPONENT_LEFT] = {44, -9}, + [B_POSITION_PLAYER_RIGHT] = {52, -9}, + [B_POSITION_OPPONENT_RIGHT] = {44, -9}, +}; + +static const struct SpritePalette sSpritePalette_TeraIndicator = +{ + sTeraIndicatorPal, TAG_TERA_INDICATOR_PAL +}; + +static const struct OamData sOamData_TeraIndicator = +{ + .shape = SPRITE_SHAPE(16x16), + .size = SPRITE_SIZE(16x16), + .priority = 1, +}; + +static const struct SpriteTemplate sSpriteTemplate_NormalIndicator = +{ + .tileTag = TAG_NORMAL_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_FightingIndicator = +{ + .tileTag = TAG_FIGHTING_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_FlyingIndicator = +{ + .tileTag = TAG_FLYING_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_PoisonIndicator = +{ + .tileTag = TAG_POISON_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_GroundIndicator = +{ + .tileTag = TAG_GROUND_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_RockIndicator = +{ + .tileTag = TAG_ROCK_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_BugIndicator = +{ + .tileTag = TAG_BUG_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_GhostIndicator = +{ + .tileTag = TAG_GHOST_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_SteelIndicator = +{ + .tileTag = TAG_STEEL_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_FireIndicator = +{ + .tileTag = TAG_FIRE_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_WaterIndicator = +{ + .tileTag = TAG_WATER_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_GrassIndicator = +{ + .tileTag = TAG_GRASS_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_ElectricIndicator = +{ + .tileTag = TAG_ELECTRIC_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_PsychicIndicator = +{ + .tileTag = TAG_PSYCHIC_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_IceIndicator = +{ + .tileTag = TAG_ICE_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_DragonIndicator = +{ + .tileTag = TAG_DRAGON_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_DarkIndicator = +{ + .tileTag = TAG_DARK_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_FairyIndicator = +{ + .tileTag = TAG_FAIRY_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteTemplate sSpriteTemplate_StellarIndicator = +{ + .tileTag = TAG_STELLAR_INDICATOR_TILE, + .paletteTag = TAG_TERA_INDICATOR_PAL, + .oam = &sOamData_TeraIndicator, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCb_TeraIndicator, +}; + +static const struct SpriteSheet sTeraIndicatorSpriteSheets[NUMBER_OF_MON_TYPES + 1] = +{ + {sNormalIndicatorGfx, sizeof(sNormalIndicatorGfx), TAG_NORMAL_INDICATOR_TILE}, + {sFightingIndicatorGfx, sizeof(sFightingIndicatorGfx), TAG_FIGHTING_INDICATOR_TILE}, + {sFlyingIndicatorGfx, sizeof(sFlyingIndicatorGfx), TAG_FLYING_INDICATOR_TILE}, + {sPoisonIndicatorGfx, sizeof(sPoisonIndicatorGfx), TAG_POISON_INDICATOR_TILE}, + {sGroundIndicatorGfx, sizeof(sGroundIndicatorGfx), TAG_GROUND_INDICATOR_TILE}, + {sRockIndicatorGfx, sizeof(sRockIndicatorGfx), TAG_ROCK_INDICATOR_TILE}, + {sBugIndicatorGfx, sizeof(sBugIndicatorGfx), TAG_BUG_INDICATOR_TILE}, + {sGhostIndicatorGfx, sizeof(sGhostIndicatorGfx), TAG_GHOST_INDICATOR_TILE}, + {sSteelIndicatorGfx, sizeof(sSteelIndicatorGfx), TAG_STEEL_INDICATOR_TILE}, + {sNormalIndicatorGfx, sizeof(sNormalIndicatorGfx), TAG_NORMAL_INDICATOR_TILE}, // TYPE_MYSTERY + {sFireIndicatorGfx, sizeof(sFireIndicatorGfx), TAG_FIRE_INDICATOR_TILE}, + {sWaterIndicatorGfx, sizeof(sWaterIndicatorGfx), TAG_WATER_INDICATOR_TILE}, + {sGrassIndicatorGfx, sizeof(sGrassIndicatorGfx), TAG_GRASS_INDICATOR_TILE}, + {sElectricIndicatorGfx, sizeof(sElectricIndicatorGfx), TAG_ELECTRIC_INDICATOR_TILE}, + {sPsychicIndicatorGfx, sizeof(sPsychicIndicatorGfx), TAG_PSYCHIC_INDICATOR_TILE}, + {sIceIndicatorGfx, sizeof(sIceIndicatorGfx), TAG_ICE_INDICATOR_TILE}, + {sDragonIndicatorGfx, sizeof(sDragonIndicatorGfx), TAG_DRAGON_INDICATOR_TILE}, + {sDarkIndicatorGfx, sizeof(sDarkIndicatorGfx), TAG_DARK_INDICATOR_TILE}, + {sFairyIndicatorGfx, sizeof(sFairyIndicatorGfx), TAG_FAIRY_INDICATOR_TILE}, + {sStellarIndicatorGfx, sizeof(sStellarIndicatorGfx), TAG_STELLAR_INDICATOR_TILE}, + {0} +}; + +static const struct SpriteTemplate * const sTeraIndicatorSpriteTemplates[NUMBER_OF_MON_TYPES] = +{ + [TYPE_NORMAL] = &sSpriteTemplate_NormalIndicator, + [TYPE_FIGHTING] = &sSpriteTemplate_FightingIndicator, + [TYPE_FLYING] = &sSpriteTemplate_FlyingIndicator, + [TYPE_POISON] = &sSpriteTemplate_PoisonIndicator, + [TYPE_GROUND] = &sSpriteTemplate_GroundIndicator, + [TYPE_ROCK] = &sSpriteTemplate_RockIndicator, + [TYPE_BUG] = &sSpriteTemplate_BugIndicator, + [TYPE_GHOST] = &sSpriteTemplate_GhostIndicator, + [TYPE_STEEL] = &sSpriteTemplate_SteelIndicator, + [TYPE_MYSTERY] = &sSpriteTemplate_NormalIndicator, // just in case + [TYPE_FIRE] = &sSpriteTemplate_FireIndicator, + [TYPE_WATER] = &sSpriteTemplate_WaterIndicator, + [TYPE_GRASS] = &sSpriteTemplate_GrassIndicator, + [TYPE_ELECTRIC] = &sSpriteTemplate_ElectricIndicator, + [TYPE_PSYCHIC] = &sSpriteTemplate_PsychicIndicator, + [TYPE_ICE] = &sSpriteTemplate_IceIndicator, + [TYPE_DRAGON] = &sSpriteTemplate_DragonIndicator, + [TYPE_DARK] = &sSpriteTemplate_DarkIndicator, + [TYPE_FAIRY] = &sSpriteTemplate_FairyIndicator, + [TYPE_STELLAR] = &sSpriteTemplate_StellarIndicator, +}; + +// for sprite data fields +#define tBattler data[0] +#define tType data[1] // Indicator type: tera +#define tPosX data[2] +#define tLevelXDelta data[3] // X position depends whether level has 3, 2 or 1 digit + +// data fields for healthboxMain +// oam.affineParam holds healthboxRight spriteId +#define hMain_TeraIndicatorId data[3] +#define hMain_HealthBarSpriteId data[5] +#define hMain_Battler data[6] +#define hMain_Data7 data[7] + +// data fields for healthboxRight +#define hOther_HealthBoxSpriteId data[5] + +// data fields for healthbar +#define hBar_HealthBoxSpriteId data[5] + +void TeraIndicator_LoadSpriteGfx(void) +{ + LoadSpriteSheets(sTeraIndicatorSpriteSheets); + LoadSpritePalette(&sSpritePalette_TeraIndicator); +} + +bool32 TeraIndicator_ShouldBeInvisible(u32 battler) +{ + return !IsTerastallized(battler); +} + +u8 TeraIndicator_GetSpriteId(u32 healthboxSpriteId) +{ + return gBattleStruct->tera.indicatorSpriteId[gSprites[healthboxSpriteId].hMain_Battler]; +} + +void TeraIndicator_SetVisibilities(u32 healthboxId, bool32 invisible) +{ + u8 spriteId = TeraIndicator_GetSpriteId(healthboxId); + u32 battler = gSprites[healthboxId].hMain_Battler; + + if (invisible == TRUE) + gSprites[spriteId].invisible = TRUE; + else // Try visible. + gSprites[spriteId].invisible = TeraIndicator_ShouldBeInvisible(battler); +} + +void TeraIndicator_UpdateOamPriorities(u32 healthboxId, u32 oamPriority) +{ + u8 spriteId = TeraIndicator_GetSpriteId(healthboxId); + gSprites[spriteId].oam.priority = oamPriority; +} + +void TeraIndicator_UpdateLevel(u32 healthboxId, u32 level) +{ + s16 xDelta = 0; + u8 spriteId = TeraIndicator_GetSpriteId(healthboxId); + + if (level >= 100) + xDelta -= 4; + else if (level < 10) + xDelta += 5; + + gSprites[spriteId].tLevelXDelta = xDelta; +} + +void TeraIndicator_CreateSprite(u32 battler, u32 healthboxSpriteId) +{ + u32 position; + u8 spriteId; + s16 xHealthbox = 0, y = 0; + s32 x = 0; + u32 type = GetBattlerTeraType(battler); + + position = GetBattlerPosition(battler); + GetBattlerHealthboxCoords(battler, &xHealthbox, &y); + + x = sIndicatorPositions[position][0]; + y += sIndicatorPositions[position][1]; + + spriteId = gBattleStruct->tera.indicatorSpriteId[battler] = CreateSpriteAtEnd(sTeraIndicatorSpriteTemplates[type], 0, y, 0); + gSprites[spriteId].tBattler = battler; + gSprites[spriteId].tPosX = x; + gSprites[spriteId].invisible = TRUE; +} + +void TeraIndicator_DestroySprite(u32 healthboxSpriteId) +{ + u8 spriteId = TeraIndicator_GetSpriteId(healthboxSpriteId); + DestroySprite(&gSprites[spriteId]); +} + +void TeraIndicator_UpdateType(u32 battler, u32 healthboxSpriteId) +{ + TeraIndicator_DestroySprite(healthboxSpriteId); + TeraIndicator_CreateSprite(battler, healthboxSpriteId); +} + +static void SpriteCb_TeraIndicator(struct Sprite *sprite) +{ + u32 battler = sprite->tBattler; + + sprite->x = gSprites[gHealthboxSpriteIds[battler]].x + sprite->tPosX + sprite->tLevelXDelta; + sprite->x2 = gSprites[gHealthboxSpriteIds[battler]].x2; + sprite->y2 = gSprites[gHealthboxSpriteIds[battler]].y2; +} + +#undef tBattler +#undef tType +#undef tPosX +#undef tLevelXDelta diff --git a/src/battle_util.c b/src/battle_util.c index b6fb998376..e11ddc27ea 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -707,7 +707,7 @@ void HandleAction_NothingIsFainted(void) void HandleAction_ActionFinished(void) { - u32 i, j; + u32 i, j, moveType; bool32 afterYouActive = gSpecialStatuses[gBattlerByTurnOrder[gCurrentTurnActionNumber + 1]].afterYou; *(gBattleStruct->monToSwitchIntoId + gBattlerByTurnOrder[gCurrentTurnActionNumber]) = gSelectedMonPartyId = PARTY_SIZE; gCurrentTurnActionNumber++; @@ -718,6 +718,16 @@ void HandleAction_ActionFinished(void) | HITMARKER_OBEYS | HITMARKER_WAKE_UP_CLEAR | HITMARKER_SYNCHRONISE_EFFECT | HITMARKER_CHARGING | HITMARKER_NEVER_SET | HITMARKER_IGNORE_DISGUISE); + // check if Stellar type boost should be used up + GET_MOVE_TYPE(gCurrentMove, moveType); + if (IsTerastallized(gBattlerAttacker) + && GetBattlerTeraType(gBattlerAttacker) == TYPE_STELLAR + && gMovesInfo[gCurrentMove].category != DAMAGE_CATEGORY_STATUS + && IsTypeStellarBoosted(gBattlerAttacker, moveType)) + { + ExpendTypeStellarBoost(gBattlerAttacker, moveType); + } + gCurrentMove = 0; gBattleMoveDamage = 0; gMoveResultFlags = 0; @@ -898,34 +908,35 @@ static const uq4_12_t sPercentToModifier[] = static const uq4_12_t sTypeEffectivenessTable[NUMBER_OF_MON_TYPES][NUMBER_OF_MON_TYPES] = {// Defender --> - // Attacker Normal Fighting Flying Poison Ground Rock Bug Ghost Steel Mystery Fire Water Grass Electric Psychic Ice Dragon Dark Fairy - [TYPE_NORMAL] = {______, ______, ______, ______, ______, X(0.5), ______, X(0.0), X(0.5), ______, ______, ______, ______, ______, ______, ______, ______, ______, ______}, - [TYPE_FIGHTING] = {X(2.0), ______, X(0.5), X(0.5), ______, X(2.0), X(0.5), X(0.0), X(2.0), ______, ______, ______, ______, ______, X(0.5), X(2.0), ______, X(2.0), X(0.5)}, - [TYPE_FLYING] = {______, X(2.0), ______, ______, ______, X(0.5), X(2.0), ______, X(0.5), ______, ______, ______, X(2.0), X(0.5), ______, ______, ______, ______, ______}, - [TYPE_POISON] = {______, ______, ______, X(0.5), X(0.5), X(0.5), ______, X(0.5), X(0.0), ______, ______, ______, X(2.0), ______, ______, ______, ______, ______, X(2.0)}, - [TYPE_GROUND] = {______, ______, X(0.0), X(2.0), ______, X(2.0), X(0.5), ______, X(2.0), ______, X(2.0), ______, X(0.5), X(2.0), ______, ______, ______, ______, ______}, - [TYPE_ROCK] = {______, X(0.5), X(2.0), ______, X(0.5), ______, X(2.0), ______, X(0.5), ______, X(2.0), ______, ______, ______, ______, X(2.0), ______, ______, ______}, - [TYPE_BUG] = {______, X(0.5), X(0.5), X(0.5), ______, ______, ______, X(0.5), X(0.5), ______, X(0.5), ______, X(2.0), ______, X(2.0), ______, ______, X(2.0), X(0.5)}, + // Attacker Normal Fighting Flying Poison Ground Rock Bug Ghost Steel Mystery Fire Water Grass Electric Psychic Ice Dragon Dark Fairy Stellar + [TYPE_NORMAL] = {______, ______, ______, ______, ______, X(0.5), ______, X(0.0), X(0.5), ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______}, + [TYPE_FIGHTING] = {X(2.0), ______, X(0.5), X(0.5), ______, X(2.0), X(0.5), X(0.0), X(2.0), ______, ______, ______, ______, ______, X(0.5), X(2.0), ______, X(2.0), X(0.5), ______}, + [TYPE_FLYING] = {______, X(2.0), ______, ______, ______, X(0.5), X(2.0), ______, X(0.5), ______, ______, ______, X(2.0), X(0.5), ______, ______, ______, ______, ______, ______}, + [TYPE_POISON] = {______, ______, ______, X(0.5), X(0.5), X(0.5), ______, X(0.5), X(0.0), ______, ______, ______, X(2.0), ______, ______, ______, ______, ______, X(2.0), ______}, + [TYPE_GROUND] = {______, ______, X(0.0), X(2.0), ______, X(2.0), X(0.5), ______, X(2.0), ______, X(2.0), ______, X(0.5), X(2.0), ______, ______, ______, ______, ______, ______}, + [TYPE_ROCK] = {______, X(0.5), X(2.0), ______, X(0.5), ______, X(2.0), ______, X(0.5), ______, X(2.0), ______, ______, ______, ______, X(2.0), ______, ______, ______, ______}, + [TYPE_BUG] = {______, X(0.5), X(0.5), X(0.5), ______, ______, ______, X(0.5), X(0.5), ______, X(0.5), ______, X(2.0), ______, X(2.0), ______, ______, X(2.0), X(0.5), ______}, #if B_STEEL_RESISTANCES >= GEN_6 - [TYPE_GHOST] = {X(0.0), ______, ______, ______, ______, ______, ______, X(2.0), ______, ______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), ______}, + [TYPE_GHOST] = {X(0.0), ______, ______, ______, ______, ______, ______, X(2.0), ______, ______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), ______, ______}, #else - [TYPE_GHOST] = {X(0.0), ______, ______, ______, ______, ______, ______, X(2.0), X(0.5), ______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), ______}, + [TYPE_GHOST] = {X(0.0), ______, ______, ______, ______, ______, ______, X(2.0), X(0.5), ______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), ______, ______}, #endif - [TYPE_STEEL] = {______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), ______, X(0.5), X(0.5), ______, X(0.5), ______, X(2.0), ______, ______, X(2.0)}, - [TYPE_MYSTERY] = {______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______}, - [TYPE_FIRE] = {______, ______, ______, ______, ______, X(0.5), X(2.0), ______, X(2.0), ______, X(0.5), X(0.5), X(2.0), ______, ______, X(2.0), X(0.5), ______, ______}, - [TYPE_WATER] = {______, ______, ______, ______, X(2.0), X(2.0), ______, ______, ______, ______, X(2.0), X(0.5), X(0.5), ______, ______, ______, X(0.5), ______, ______}, - [TYPE_GRASS] = {______, ______, X(0.5), X(0.5), X(2.0), X(2.0), X(0.5), ______, X(0.5), ______, X(0.5), X(2.0), X(0.5), ______, ______, ______, X(0.5), ______, ______}, - [TYPE_ELECTRIC] = {______, ______, X(2.0), ______, X(0.0), ______, ______, ______, ______, ______, ______, X(2.0), X(0.5), X(0.5), ______, ______, X(0.5), ______, ______}, - [TYPE_PSYCHIC] = {______, X(2.0), ______, X(2.0), ______, ______, ______, ______, X(0.5), ______, ______, ______, ______, ______, X(0.5), ______, ______, X(0.0), ______}, - [TYPE_ICE] = {______, ______, X(2.0), ______, X(2.0), ______, ______, ______, X(0.5), ______, X(0.5), X(0.5), X(2.0), ______, ______, X(0.5), X(2.0), ______, ______}, - [TYPE_DRAGON] = {______, ______, ______, ______, ______, ______, ______, ______, X(0.5), ______, ______, ______, ______, ______, ______, ______, X(2.0), ______, X(0.0)}, + [TYPE_STEEL] = {______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), ______, X(0.5), X(0.5), ______, X(0.5), ______, X(2.0), ______, ______, X(2.0), ______}, + [TYPE_MYSTERY] = {______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______}, + [TYPE_FIRE] = {______, ______, ______, ______, ______, X(0.5), X(2.0), ______, X(2.0), ______, X(0.5), X(0.5), X(2.0), ______, ______, X(2.0), X(0.5), ______, ______, ______}, + [TYPE_WATER] = {______, ______, ______, ______, X(2.0), X(2.0), ______, ______, ______, ______, X(2.0), X(0.5), X(0.5), ______, ______, ______, X(0.5), ______, ______, ______}, + [TYPE_GRASS] = {______, ______, X(0.5), X(0.5), X(2.0), X(2.0), X(0.5), ______, X(0.5), ______, X(0.5), X(2.0), X(0.5), ______, ______, ______, X(0.5), ______, ______, ______}, + [TYPE_ELECTRIC] = {______, ______, X(2.0), ______, X(0.0), ______, ______, ______, ______, ______, ______, X(2.0), X(0.5), X(0.5), ______, ______, X(0.5), ______, ______, ______}, + [TYPE_PSYCHIC] = {______, X(2.0), ______, X(2.0), ______, ______, ______, ______, X(0.5), ______, ______, ______, ______, ______, X(0.5), ______, ______, X(0.0), ______, ______}, + [TYPE_ICE] = {______, ______, X(2.0), ______, X(2.0), ______, ______, ______, X(0.5), ______, X(0.5), X(0.5), X(2.0), ______, ______, X(0.5), X(2.0), ______, ______, ______}, + [TYPE_DRAGON] = {______, ______, ______, ______, ______, ______, ______, ______, X(0.5), ______, ______, ______, ______, ______, ______, ______, X(2.0), ______, X(0.0), ______}, #if B_STEEL_RESISTANCES >= GEN_6 - [TYPE_DARK] = {______, X(0.5), ______, ______, ______, ______, ______, X(2.0), ______, ______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), X(0.5)}, + [TYPE_DARK] = {______, X(0.5), ______, ______, ______, ______, ______, X(2.0), ______, ______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), X(0.5), ______}, #else - [TYPE_DARK] = {______, X(0.5), ______, ______, ______, ______, ______, X(2.0), X(0.5), ______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), X(0.5)}, + [TYPE_DARK] = {______, X(0.5), ______, ______, ______, ______, ______, X(2.0), X(0.5), ______, ______, ______, ______, ______, X(2.0), ______, ______, X(0.5), X(0.5), ______}, #endif - [TYPE_FAIRY] = {______, X(2.0), ______, X(0.5), ______, ______, ______, ______, X(0.5), ______, X(0.5), ______, ______, ______, ______, ______, X(2.0), X(2.0), ______}, + [TYPE_FAIRY] = {______, X(2.0), ______, X(0.5), ______, ______, ______, ______, X(0.5), ______, X(0.5), ______, ______, ______, ______, ______, X(2.0), X(2.0), ______, ______}, + [TYPE_STELLAR] = {______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______, ______}, }; #undef ______ @@ -1276,7 +1287,7 @@ static bool32 IsBelchPreventingMove(u32 battler, u32 move) u32 TrySetCantSelectMoveBattleScript(u32 battler) { u32 limitations = 0; - u8 moveId = gBattleResources->bufferB[battler][2] & ~(RET_MEGA_EVOLUTION | RET_ULTRA_BURST | RET_DYNAMAX); + u8 moveId = gBattleResources->bufferB[battler][2] & ~(RET_MEGA_EVOLUTION | RET_ULTRA_BURST | RET_DYNAMAX | RET_TERASTAL); u32 move = gBattleMons[battler].moves[moveId]; u32 holdEffect = GetBattlerHoldEffect(battler, TRUE); u16 *choicedMove = &gBattleStruct->choicedMove[battler]; @@ -5349,6 +5360,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 && gMovesInfo[move].power != 0 && TARGET_TURN_DAMAGED && !IS_BATTLER_OF_TYPE(battler, moveType) + && moveType != TYPE_STELLAR && gBattleMons[battler].hp != 0) { SET_BATTLER_TYPE(battler, moveType); @@ -8842,6 +8854,10 @@ static inline u32 CalcMoveBasePower(u32 move, u32 battlerAtk, u32 battlerDef, u3 if (RandomPercentage(RNG_FICKLE_BEAM, 30)) basePower *= 2; break; + case EFFECT_TERA_BLAST: + if (IsTerastallized(battlerAtk) && GetBattlerTeraType(battlerAtk) == TYPE_STELLAR) + basePower = 100; + break; case EFFECT_LAST_RESPECTS: basePower += (basePower * min(100, GetBattlerSideFaintCounter(battlerAtk))); break; @@ -9210,6 +9226,19 @@ static inline u32 CalcMoveBasePowerAfterModifiers(u32 move, u32 battlerAtk, u32 modifier = uq4_12_multiply(modifier, UQ_4_12(1.1)); break; } + + // Terastallization boosts weak, non-priority, non-multi hit moves after modifiers to 60 BP. + if (IsTerastallized(battlerAtk) + && (moveType == GetBattlerTeraType(battlerAtk) + || (GetBattlerTeraType(battlerAtk) == TYPE_STELLAR && IsTypeStellarBoosted(battlerAtk, moveType))) + && uq4_12_multiply_by_int_half_down(modifier, basePower) < 60 + && gMovesInfo[move].strikeCount < 2 + && gMovesInfo[move].effect != EFFECT_MULTI_HIT + && gMovesInfo[move].priority == 0) + { + return 60; + } + return uq4_12_multiply_by_int_half_down(modifier, basePower); } @@ -9912,7 +9941,10 @@ static inline s32 DoMoveDamageCalcVars(u32 move, u32 battlerAtk, u32 battlerDef, dmg /= 100; } - DAMAGE_APPLY_MODIFIER(GetSameTypeAttackBonusModifier(battlerAtk, moveType, move, abilityAtk)); + if (IsTerastallized(battlerAtk)) + DAMAGE_APPLY_MODIFIER(GetTeraMultiplier(battlerAtk, moveType)); + else + DAMAGE_APPLY_MODIFIER(GetSameTypeAttackBonusModifier(battlerAtk, moveType, move, abilityAtk)); DAMAGE_APPLY_MODIFIER(typeEffectivenessModifier); DAMAGE_APPLY_MODIFIER(GetBurnOrFrostBiteModifier(battlerAtk, move, abilityAtk)); DAMAGE_APPLY_MODIFIER(GetZMaxMoveAgainstProtectionModifier(battlerDef, move)); @@ -10119,12 +10151,12 @@ static inline uq4_12_t CalcTypeEffectivenessMultiplierInternal(u32 move, u32 mov { u32 illusionSpecies; - MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, GetBattlerType(battlerDef, 0), battlerAtk, recordAbilities); - if (GetBattlerType(battlerDef, 1) != GetBattlerType(battlerDef, 0)) - MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, GetBattlerType(battlerDef, 1), battlerAtk, recordAbilities); - if (GetBattlerType(battlerDef, 2) != TYPE_MYSTERY && GetBattlerType(battlerDef, 2) != GetBattlerType(battlerDef, 1) - && GetBattlerType(battlerDef, 2) != GetBattlerType(battlerDef, 0)) - MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, GetBattlerType(battlerDef, 2), battlerAtk, recordAbilities); + MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, GetBattlerType(battlerDef, 0, FALSE), battlerAtk, recordAbilities); + if (GetBattlerType(battlerDef, 1, FALSE) != GetBattlerType(battlerDef, 0, FALSE)) + MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, GetBattlerType(battlerDef, 1, FALSE), battlerAtk, recordAbilities); + if (GetBattlerType(battlerDef, 2, FALSE) != TYPE_MYSTERY && GetBattlerType(battlerDef, 2, FALSE) != GetBattlerType(battlerDef, 1, FALSE) + && GetBattlerType(battlerDef, 2, FALSE) != GetBattlerType(battlerDef, 0, FALSE)) + MulByTypeEffectiveness(&modifier, move, moveType, battlerDef, GetBattlerType(battlerDef, 2, FALSE), battlerAtk, recordAbilities); if (recordAbilities && (illusionSpecies = GetIllusionMonSpecies(battlerDef))) TryNoticeIllusionInTypeEffectiveness(move, moveType, battlerAtk, battlerDef, modifier, illusionSpecies); @@ -10185,12 +10217,16 @@ uq4_12_t CalcTypeEffectivenessMultiplier(u32 move, u32 moveType, u32 battlerAtk, { uq4_12_t modifier = UQ_4_12(1.0); - if (move != MOVE_STRUGGLE && moveType != TYPE_MYSTERY) + if (move != MOVE_STRUGGLE && moveType != TYPE_MYSTERY && moveType != TYPE_STELLAR) { modifier = CalcTypeEffectivenessMultiplierInternal(move, moveType, battlerAtk, battlerDef, recordAbilities, modifier, defAbility); if (gMovesInfo[move].effect == EFFECT_TWO_TYPED_MOVE) modifier = CalcTypeEffectivenessMultiplierInternal(move, gMovesInfo[move].argument, battlerAtk, battlerDef, recordAbilities, modifier, defAbility); } + else if (moveType == TYPE_STELLAR) + { + modifier = IsTerastallized(battlerDef) ? UQ_4_12(2.0) : UQ_4_12(1.0); + } if (recordAbilities) UpdateMoveResultFlags(modifier); @@ -10640,8 +10676,8 @@ bool32 TryBattleFormChange(u32 battler, u16 method) bool32 DoBattlersShareType(u32 battler1, u32 battler2) { s32 i; - u8 types1[3] = {GetBattlerType(battler1, 0), GetBattlerType(battler1, 1), GetBattlerType(battler1, 2)}; - u8 types2[3] = {GetBattlerType(battler2, 0), GetBattlerType(battler2, 1), GetBattlerType(battler2, 2)}; + u8 types1[3] = {GetBattlerType(battler1, 0, FALSE), GetBattlerType(battler1, 1, FALSE), GetBattlerType(battler1, 2, FALSE)}; + u8 types2[3] = {GetBattlerType(battler2, 0, FALSE), GetBattlerType(battler2, 1, FALSE), GetBattlerType(battler2, 2, FALSE)}; if (types1[2] == TYPE_MYSTERY) types1[2] = types1[0]; @@ -10823,7 +10859,7 @@ static bool32 IsUnnerveAbilityOnOpposingSide(u32 battler) return FALSE; } -// Photon geyser & light that burns the sky +// Photon Geyser, Light That Burns the Sky, Tera Blast u8 GetCategoryBasedOnStats(u32 battler) { u32 attack = gBattleMons[battler].attack; @@ -11381,17 +11417,23 @@ bool8 IsMonBannedFromSkyBattles(u16 species) } } -u8 GetBattlerType(u32 battler, u8 typeIndex) +u8 GetBattlerType(u32 battler, u8 typeIndex, bool32 ignoreTera) { + u32 teraType = GetBattlerTeraType(battler); u16 types[3] = {0}; types[0] = gBattleMons[battler].type1; types[1] = gBattleMons[battler].type2; types[2] = gBattleMons[battler].type3; + // Handle Terastallization + if (IsTerastallized(battler) && teraType != TYPE_STELLAR && !ignoreTera) + return GetBattlerTeraType(battler); + // Handle Roost's Flying-type suppression if (typeIndex == 0 || typeIndex == 1) { - if (gBattleResources->flags->flags[battler] & RESOURCE_FLAG_ROOST) + if (gBattleResources->flags->flags[battler] & RESOURCE_FLAG_ROOST + && !IsTerastallized(battler)) { if (types[0] == TYPE_FLYING && types[1] == TYPE_FLYING) return B_ROOST_PURE_FLYING >= GEN_5 ? TYPE_NORMAL : TYPE_MYSTERY; @@ -11406,6 +11448,8 @@ u8 GetBattlerType(u32 battler, u8 typeIndex) void RemoveBattlerType(u32 battler, u8 type) { u32 i; + if (IsTerastallized(battler)) // don't remove type if Terastallized + return; for (i = 0; i < 3; i++) { if (*(u8 *)(&gBattleMons[battler].type1 + i) == type) diff --git a/src/data/battle_move_effects.h b/src/data/battle_move_effects.h index 52efcb1212..de8d3f9ec8 100644 --- a/src/data/battle_move_effects.h +++ b/src/data/battle_move_effects.h @@ -2230,4 +2230,10 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = .battleTvScore = 0, // TODO: Assign points .encourageEncore = TRUE, }, + + [EFFECT_TERA_BLAST] = + { + .battleScript = BattleScript_EffectPhotonGeyser, + .battleTvScore = 0, // TODO: Assign points + }, }; diff --git a/src/data/moves_info.h b/src/data/moves_info.h index 10e0c8ef28..911f2b6b82 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -18440,7 +18440,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .description = COMPOUND_STRING( "If the user's Terastallized,\n" "it hits with its Tera-type."), - .effect = EFFECT_PLACEHOLDER, // EFFECT_TERA_BLAST, + .effect = EFFECT_TERA_BLAST, .power = 80, .type = TYPE_NORMAL, .accuracy = 100, @@ -18449,6 +18449,10 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .priority = 0, .category = DAMAGE_CATEGORY_SPECIAL, .forcePressure = TRUE, + .additionalEffects = ADDITIONAL_EFFECTS({ + .moveEffect = MOVE_EFFECT_TERA_BLAST, + .self = TRUE, + }), }, [MOVE_SILK_TRAP] = diff --git a/src/pokemon_summary_screen.c b/src/pokemon_summary_screen.c index f5c2e7f6b7..6e63ce5eab 100644 --- a/src/pokemon_summary_screen.c +++ b/src/pokemon_summary_screen.c @@ -891,6 +891,10 @@ static const union AnimCmd sSpriteAnim_TypeFairy[] = { ANIMCMD_FRAME(TYPE_FAIRY * 8, 0, FALSE, FALSE), ANIMCMD_END }; +static const union AnimCmd sSpriteAnim_TypeStellar[] = { + ANIMCMD_FRAME(TYPE_STELLAR * 8, 0, FALSE, FALSE), + ANIMCMD_END +}; static const union AnimCmd sSpriteAnim_CategoryCool[] = { ANIMCMD_FRAME((CONTEST_CATEGORY_COOL + NUMBER_OF_MON_TYPES) * 8, 0, FALSE, FALSE), ANIMCMD_END @@ -931,6 +935,7 @@ static const union AnimCmd *const sSpriteAnimTable_MoveTypes[NUMBER_OF_MON_TYPES sSpriteAnim_TypeDragon, sSpriteAnim_TypeDark, sSpriteAnim_TypeFairy, + sSpriteAnim_TypeStellar, sSpriteAnim_CategoryCool, sSpriteAnim_CategoryBeauty, sSpriteAnim_CategoryCute, diff --git a/src/script_pokemon_util.c b/src/script_pokemon_util.c index 31e40fa31a..763bb3f9e6 100644 --- a/src/script_pokemon_util.c +++ b/src/script_pokemon_util.c @@ -7,6 +7,7 @@ #include "decompress.h" #include "event_data.h" #include "international_string_util.h" +#include "item.h" #include "link.h" #include "link_rfu.h" #include "main.h" @@ -38,6 +39,10 @@ void HealPlayerParty(void) HealPokemon(&gPlayerParty[i]); if (OW_PC_HEAL >= GEN_8) HealPlayerBoxes(); + + // Recharge Tera Orb, if possible. + if (B_FLAG_TERA_ORB_CHARGED != 0 && CheckBagHasItem(ITEM_TERA_ORB, 1)) + FlagSet(B_FLAG_TERA_ORB_CHARGED); } static void HealPlayerBoxes(void) diff --git a/test/dynamax.c b/test/battle/gimmick/dynamax.c similarity index 100% rename from test/dynamax.c rename to test/battle/gimmick/dynamax.c diff --git a/test/battle/gimmick/terastal.c b/test/battle/gimmick/terastal.c new file mode 100644 index 0000000000..3166e2d67c --- /dev/null +++ b/test/battle/gimmick/terastal.c @@ -0,0 +1,802 @@ +#include "global.h" +#include "test/battle.h" + +// Base Power and STAB Checks + +SINGLE_BATTLE_TEST("(TERA) Terastallizing into a different type preserves other STAB boosts", s16 damage1, s16 damage2) +{ + bool32 tera; + PARAMETRIZE { tera = FALSE; } + PARAMETRIZE { tera = TRUE; } + GIVEN { + PLAYER(SPECIES_BULBASAUR) { TeraType(TYPE_NORMAL); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_VINE_WHIP, tera: tera); } + TURN { MOVE(player, MOVE_SLUDGE_BOMB); } + } SCENE { + MESSAGE("Bulbasaur used Vine Whip!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_VINE_WHIP, player); + HP_BAR(opponent, captureDamage: &results[i].damage1); + MESSAGE("Bulbasaur used Sludge Bomb!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SLUDGE_BOMB, player); + HP_BAR(opponent, captureDamage: &results[i].damage2); + } FINALLY { + EXPECT_EQ(results[0].damage1, results[1].damage1); + EXPECT_EQ(results[0].damage2, results[1].damage2); + } +} + +SINGLE_BATTLE_TEST("(TERA) Terastallizing does not affect the power of non-STAB moves", s16 damage) +{ + bool32 tera; + PARAMETRIZE { tera = FALSE; } + PARAMETRIZE { tera = TRUE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_PSYCHIC); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_HEADBUTT, tera: tera); } + } SCENE { + MESSAGE("Wobbuffet used Headbutt!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_HEADBUTT, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + } +} + +SINGLE_BATTLE_TEST("(TERA) Terastallizing into a different type gives that type 1.5x STAB", s16 damage) +{ + bool32 tera; + PARAMETRIZE { tera = FALSE; } + PARAMETRIZE { tera = TRUE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_NORMAL); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_HEADBUTT, tera: tera); } + } SCENE { + MESSAGE("Wobbuffet used Headbutt!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_HEADBUTT, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + // The jump from no STAB to 1.5x STAB is a 1.5x boost. + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("(TERA) Terastallizing into the same type gives that type 2x STAB", s16 damage) +{ + bool32 tera; + PARAMETRIZE { tera = FALSE; } + PARAMETRIZE { tera = TRUE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_PSYCHIC); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_PSYCHIC, tera: tera); } + } SCENE { + MESSAGE("Wobbuffet used Psychic!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PSYCHIC, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + // The jump from 1.5x STAB to 2.0x STAB is a 1.33x boost. + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.33), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("(TERA) Terastallizing into a different type with Adaptability gives 2.0x STAB", s16 damage) +{ + bool32 tera; + PARAMETRIZE { tera = FALSE; } + PARAMETRIZE { tera = TRUE; } + GIVEN { + PLAYER(SPECIES_CRAWDAUNT) { Ability(ABILITY_ADAPTABILITY); TeraType(TYPE_NORMAL); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_HEADBUTT, tera: tera); } + } SCENE { + MESSAGE("Crawdaunt used Headbutt!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_HEADBUTT, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + // The jump from no STAB to 2.0x STAB is a 2.0x boost. + EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("(TERA) Terastallizing into the same type with Adaptability gives 2.25x STAB", s16 damage) +{ + bool32 tera; + PARAMETRIZE { tera = FALSE; } + PARAMETRIZE { tera = TRUE; } + GIVEN { + PLAYER(SPECIES_CRAWDAUNT) { Ability(ABILITY_ADAPTABILITY); TeraType(TYPE_WATER); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_WATER_PULSE, tera: tera); } + } SCENE { + MESSAGE("Crawdaunt used Water Pulse!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_PULSE, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + // The jump from 2x STAB to 2.25x STAB is a 1.125x boost. + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.125), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("(TERA) Terastallizing boosts moves of the same type to 60 BP", s16 damage) +{ + bool32 tera; + PARAMETRIZE { tera = FALSE; } + PARAMETRIZE { tera = TRUE; } + GIVEN { + ASSUME(gMovesInfo[MOVE_ABSORB].power == 20); + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_GRASS); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_ABSORB, tera: tera); } + } SCENE { + MESSAGE("Wobbuffet used Absorb!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ABSORB, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + // The jump from 20 BP to 90 BP (60 * 1.5x) is a 4.5x boost. + EXPECT_MUL_EQ(results[0].damage, Q_4_12(4.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("(TERA) Terastallization's 60 BP floor occurs after Technician", s16 damage) +{ + bool32 tera; + PARAMETRIZE { tera = FALSE; } + PARAMETRIZE { tera = TRUE; } + GIVEN { + ASSUME(gMovesInfo[MOVE_MEGA_DRAIN].power == 40); + PLAYER(SPECIES_MR_MIME) { Ability(ABILITY_TECHNICIAN); TeraType(TYPE_GRASS); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_MEGA_DRAIN, tera: tera); } + } SCENE { + MESSAGE("Mr. Mime used Mega Drain!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_MEGA_DRAIN, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + // This should be the same as a normal Tera boost. + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("(TERA) Terastallization's 60 BP floor occurs after Technician", s16 damage) +{ + bool32 tera; + PARAMETRIZE { tera = FALSE; } + PARAMETRIZE { tera = TRUE; } + GIVEN { + PLAYER(SPECIES_MR_MIME) { Ability(ABILITY_TECHNICIAN); TeraType(TYPE_PSYCHIC); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_STORED_POWER, tera: tera); } + } SCENE { + MESSAGE("Mr. Mime used Stored Power!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_STORED_POWER, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + // The jump from 45 BP (20 * 1.5x * 1.5x) to 120 BP (60 * 2.0x) is a 2.667x boost. + EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.667), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("(TERA) Terastallization's 60 BP floor does not apply to multi-hit moves", s16 damage) +{ + bool32 tera; + PARAMETRIZE { tera = FALSE; } + PARAMETRIZE { tera = TRUE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_NORMAL); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_FURY_SWIPES, tera: tera); } + } SCENE { + MESSAGE("Wobbuffet used Fury Swipes!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FURY_SWIPES, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("(TERA) Terastallization's 60 BP floor does not apply to priority moves", s16 damage) +{ + bool32 tera; + PARAMETRIZE { tera = FALSE; } + PARAMETRIZE { tera = TRUE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_NORMAL); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_QUICK_ATTACK, tera: tera); } + } SCENE { + MESSAGE("Wobbuffet used Quick Attack!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_ATTACK, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); + } +} + +// Defensive Type Checks + +SINGLE_BATTLE_TEST("(TERA) Terastallization changes type effectiveness", s16 damage) +{ + bool32 tera; + PARAMETRIZE { tera = FALSE; } + PARAMETRIZE { tera = TRUE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_GRASS); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE, tera: tera); MOVE(opponent, MOVE_WATER_GUN); } + } SCENE { + MESSAGE("Foe Wobbuffet used Water Gun!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, opponent); + HP_BAR(player, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("(TERA) Terastallization changes type effectiveness") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_FLYING); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE, tera: TRUE); MOVE(opponent, MOVE_EARTHQUAKE); } + } SCENE { + MESSAGE("Foe Wobbuffet used Earthquake!"); + MESSAGE("It doesn't affect Wobbuffet…"); + NOT { HP_BAR(player); } + } +} + +SINGLE_BATTLE_TEST("(TERA) Terastallization persists across switches") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_FLYING); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE, tera: TRUE); MOVE(opponent, MOVE_EARTHQUAKE); } + TURN { SWITCH(player, 1); } + TURN { SWITCH(player, 0); } + TURN { MOVE(opponent, MOVE_EARTHQUAKE); } + } SCENE { + // turn 1 + MESSAGE("Foe Wobbuffet used Earthquake!"); + MESSAGE("It doesn't affect Wobbuffet…"); + NOT { HP_BAR(player); } + // turn 4 + MESSAGE("Foe Wobbuffet used Earthquake!"); + MESSAGE("It doesn't affect Wobbuffet…"); + NOT { HP_BAR(player); } + } +} + +// Other Type Checks + +SINGLE_BATTLE_TEST("(TERA) Terastallization changes the effect of Curse") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_GHOST); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CURSE, tera: TRUE); } + } SCENE { + MESSAGE("Wobbuffet used Curse!"); + HP_BAR(player); + MESSAGE("Wobbuffet cut its own HP and laid a CURSE on Foe Wobbuffet!"); + NOT { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); } + } +} + +SINGLE_BATTLE_TEST("(TERA) Roost does not remove the user's Flying type while Terastallized") +{ + GIVEN { + PLAYER(SPECIES_ZAPDOS) { HP(1); TeraType(TYPE_FLYING); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_ROOST, tera: TRUE); MOVE(opponent, MOVE_ICE_BEAM); } + } SCENE { + MESSAGE("Zapdos used Roost!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ROOST, player); + MESSAGE("Foe Wobbuffet used Ice Beam!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ICE_BEAM, opponent); + MESSAGE("It's super effective!"); + } +} + +SINGLE_BATTLE_TEST("(TERA) Type-changing moves fail against a Terastallized Pokemon") +{ + u16 move; + PARAMETRIZE { move = MOVE_SOAK; } + PARAMETRIZE { move = MOVE_FORESTS_CURSE; } + PARAMETRIZE { move = MOVE_TRICK_OR_TREAT; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_PSYCHIC); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE, tera: TRUE); MOVE(opponent, move); } + } SCENE { + if (move != MOVE_SOAK) + NOT { ANIMATION(ANIM_TYPE_MOVE, move, opponent); } + MESSAGE("But it failed!"); + } +} + +SINGLE_BATTLE_TEST("(TERA) Reflect Type fails if used by a Terastallized Pokemon") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_PSYCHIC); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_REFLECT_TYPE, tera: TRUE); } + } SCENE { + MESSAGE("Wobbuffet used Reflect Type!"); + MESSAGE("But it failed!"); + } +} + +SINGLE_BATTLE_TEST("(TERA) Conversion fails if used by a Terastallized Pokemon") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_PSYCHIC); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CONVERSION, tera: TRUE); } + } SCENE { + MESSAGE("Wobbuffet used Conversion!"); + MESSAGE("But it failed!"); + } +} + +SINGLE_BATTLE_TEST("(TERA) Conversion2 fails if used by a Terastallized Pokemon") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_PSYCHIC); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE); } + TURN { MOVE(player, MOVE_CONVERSION_2, tera: TRUE); } + } SCENE { + MESSAGE("Wobbuffet used Conversion 2!"); + MESSAGE("But it failed!"); + } +} + +SINGLE_BATTLE_TEST("(TERA) Reflect Type copies a Terastallized Pokemon's Tera Type") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_GHOST); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_CELEBRATE, tera: TRUE); } + TURN { MOVE(opponent, MOVE_REFLECT_TYPE); } + TURN { MOVE(player, MOVE_TACKLE); } + } SCENE { + // turn 2 + MESSAGE("Foe Wobbuffet used Reflect Type!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_REFLECT_TYPE, opponent); + // turn 3 + MESSAGE("Wobbuffet used Tackle!"); + MESSAGE("It doesn't affect Foe Wobbuffet…"); + NOT { HP_BAR(opponent); } + } +} + +SINGLE_BATTLE_TEST("(TERA) Synchronoise uses a Terastallized Pokemon's Tera Type") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_GHOST); } + OPPONENT(SPECIES_WOBBUFFET) { TeraType(TYPE_GHOST); } + } WHEN { + TURN { MOVE(opponent, MOVE_SYNCHRONOISE); MOVE(player, MOVE_CELEBRATE, tera: TRUE); } + TURN { MOVE(opponent, MOVE_SYNCHRONOISE, tera: TRUE); } + } SCENE { + // turn 1 + MESSAGE("Foe Wobbuffet used Synchronoise!"); + MESSAGE("It had no effect on Wobbuffet!"); + // turn 2 + MESSAGE("Foe Wobbuffet used Synchronoise!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SYNCHRONOISE, opponent); + } +} + +SINGLE_BATTLE_TEST("(TERA) Revelation Dance uses a Terastallized Pokemon's Tera Type") +{ + GIVEN { + ASSUME(P_GEN_7_POKEMON); + PLAYER(SPECIES_ORICORIO) { TeraType(TYPE_NORMAL); } + OPPONENT(SPECIES_GENGAR); + } WHEN { + TURN { MOVE(player, MOVE_REVELATION_DANCE, tera: TRUE); } + } SCENE { + #if B_EXPANDED_MOVE_NAMES == TRUE + MESSAGE("Oricorio used Revelation Dance!"); + #else + MESSAGE("Oricorio used RvlationDnce!"); + #endif + MESSAGE("It doesn't affect Foe Gengar…"); + NOT { HP_BAR(opponent); } + } +} + +// This tests that Tera STAB modifiers depend on the user's original types, too. +SINGLE_BATTLE_TEST("(TERA) Double Shock does not remove the user's Electric type while Terastallized, and changes STAB modifier depending on when it is used") +{ + s16 damage[4]; + GIVEN { + ASSUME(gMovesInfo[MOVE_DOUBLE_SHOCK].effect == EFFECT_FAIL_IF_NOT_ARG_TYPE); + PLAYER(SPECIES_PICHU) { TeraType(TYPE_ELECTRIC); } + PLAYER(SPECIES_WOBBUFFET) + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_DOUBLE_SHOCK); MOVE(opponent, MOVE_RECOVER); } + TURN { MOVE(player, MOVE_DOUBLE_SHOCK, tera: TRUE); MOVE(opponent, MOVE_RECOVER); } + TURN { MOVE(player, MOVE_DOUBLE_SHOCK); MOVE(opponent, MOVE_RECOVER); } + TURN { SWITCH(player, 1); MOVE(opponent, MOVE_RECOVER); } + TURN { SWITCH(player, 0); MOVE(opponent, MOVE_RECOVER); } + TURN { MOVE(player, MOVE_DOUBLE_SHOCK); MOVE(opponent, MOVE_RECOVER); } + TURN { MOVE(player, MOVE_DOUBLE_SHOCK); } + } SCENE { + // turn 1 - regular STAB + MESSAGE("Pichu used Double Shock!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_SHOCK, player); + HP_BAR(opponent, captureDamage: &damage[0]); + // turn 2 - lost Electric type, gained back from Tera + MESSAGE("Pichu used Double Shock!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_SHOCK, player); + HP_BAR(opponent, captureDamage: &damage[1]); + // turn 3 - retained Electric type + MESSAGE("Pichu used Double Shock!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_SHOCK, player); + // turn 6 - original type reset, regular STAB + Tera boost + MESSAGE("Pichu used Double Shock!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_SHOCK, player); + HP_BAR(opponent, captureDamage: &damage[2]); + // turn 7 - regular STAB + Tera boost stays + MESSAGE("Pichu used Double Shock!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_SHOCK, player); + HP_BAR(opponent, captureDamage: &damage[3]); + } THEN { + EXPECT_EQ(damage[0], damage[1]); + EXPECT_MUL_EQ(damage[0], Q_4_12(1.333), damage[2]); + EXPECT_EQ(damage[2], damage[3]); + } +} + +SINGLE_BATTLE_TEST("(TERA) Transform does not copy the target's Tera Type, and if the user is Terastallized it keeps its own Tera Type") +{ + KNOWN_FAILING; // Transform seems to be bugged in tests. + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE, MOVE_TACKLE, MOVE_EARTHQUAKE); TeraType(TYPE_GHOST); } + OPPONENT(SPECIES_DITTO) { TeraType(TYPE_FLYING); } + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE, tera: TRUE); MOVE(opponent, MOVE_TRANSFORM); } + TURN { MOVE(player, MOVE_EARTHQUAKE); } + // TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_TACKLE, target: player, tera: TRUE); } + } SCENE { + // turn 2 + MESSAGE("Wobbuffet used Earthquake!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_EARTHQUAKE, player); + HP_BAR(opponent); + // turn 3 + MESSAGE("Wobbuffet used Tackle!"); + MESSAGE("It doesn't affect Ditto…"); + NOT { HP_BAR(opponent); } + } +} + +// Stellar Type checks +SINGLE_BATTLE_TEST("(TERA) Stellar type does not change the user's defensive profile", s16 damage) +{ + bool32 tera; + PARAMETRIZE { tera = FALSE; } + PARAMETRIZE { tera = TRUE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE, tera: tera); MOVE(opponent, MOVE_PSYCHIC); } + } SCENE { + MESSAGE("Foe Wobbuffet used Psychic!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PSYCHIC, opponent); + HP_BAR(player, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + } +} + +SINGLE_BATTLE_TEST("(TERA) Reflect Type copies a Stellar-type Pokemon's base type") +{ + GIVEN { + PLAYER(SPECIES_BANETTE) { TeraType(TYPE_STELLAR); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_CELEBRATE, tera: TRUE); } + TURN { MOVE(opponent, MOVE_REFLECT_TYPE); } + TURN { MOVE(player, MOVE_TACKLE); } + } SCENE { + // turn 2 + MESSAGE("Foe Wobbuffet used Reflect Type!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_REFLECT_TYPE, opponent); + // turn 3 + MESSAGE("Banette used Tackle!"); + MESSAGE("It doesn't affect Foe Wobbuffet…"); + NOT { HP_BAR(opponent); } + } +} + +SINGLE_BATTLE_TEST("(TERA) Revelation Dance uses a Stellar-type Pokemon's base type") +{ + GIVEN { + ASSUME(P_GEN_7_POKEMON); + PLAYER(SPECIES_ORICORIO_SENSU) { TeraType(TYPE_STELLAR); } + OPPONENT(SPECIES_GUMSHOOS); + } WHEN { + TURN { MOVE(player, MOVE_REVELATION_DANCE, tera: TRUE); } + } SCENE { + #if B_EXPANDED_MOVE_NAMES == TRUE + MESSAGE("Oricorio used Revelation Dance!"); + #else + MESSAGE("Oricorio used RvlationDnce!"); + #endif + MESSAGE("It doesn't affect Foe Gumshoos…"); + NOT { HP_BAR(opponent); } + } +} + +SINGLE_BATTLE_TEST("(TERA) Conversion2 fails if last hit by a Stellar-type move") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TERA_BLAST, tera: TRUE); } + TURN { MOVE(opponent, MOVE_CONVERSION_2); } + } SCENE { + // turn 1 + MESSAGE("Wobbuffet used Tera Blast!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player); + // turn 2 + MESSAGE("Foe Wobbuffet used Conversion 2!"); + MESSAGE("But it failed!"); + } +} + +SINGLE_BATTLE_TEST("(TERA) Roost does not remove Flying-type ground immunity when Terastallized into the Stellar type") +{ + GIVEN { + PLAYER(SPECIES_ZAPDOS) { HP(1); TeraType(TYPE_STELLAR); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_ROOST, tera: TRUE); MOVE(opponent, MOVE_ICE_BEAM); } + } SCENE { + MESSAGE("Zapdos used Roost!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ROOST, player); + MESSAGE("Foe Wobbuffet used Ice Beam!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ICE_BEAM, opponent); + MESSAGE("It's super effective!"); + } +} + +SINGLE_BATTLE_TEST("(TERA) Terastallizing into the Stellar-type provides a one-time 2.0x boost to STAB moves") +{ + s16 damage[3]; + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_EXTRASENSORY); } + TURN { MOVE(player, MOVE_EXTRASENSORY, tera: TRUE); } + TURN { MOVE(player, MOVE_EXTRASENSORY); } + } SCENE { + // turn 1 + MESSAGE("Wobbuffet used Extrasensory!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_EXTRASENSORY, player); + HP_BAR(opponent, captureDamage: &damage[0]); + // turn 2 + MESSAGE("Wobbuffet used Extrasensory!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_EXTRASENSORY, player); + HP_BAR(opponent, captureDamage: &damage[1]); + // turn 3 + MESSAGE("Wobbuffet used Extrasensory!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_EXTRASENSORY, player); + HP_BAR(opponent, captureDamage: &damage[2]); + } THEN { + // Extrasensory goes from a 50% boost to a 100% boost for a 1.33x total multiplier + EXPECT_MUL_EQ(damage[0], UQ_4_12(1.33), damage[1]); + EXPECT_EQ(damage[0], damage[2]); + } +} + +SINGLE_BATTLE_TEST("(TERA) Terastallizing into the Stellar-type provides a one-time 1.2x boost to non-STAB moves") +{ + s16 damage[3]; + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TAKE_DOWN); } + TURN { MOVE(player, MOVE_TAKE_DOWN, tera: TRUE); } + TURN { MOVE(player, MOVE_TAKE_DOWN); } + } SCENE { + // turn 1 + MESSAGE("Wobbuffet used Take Down!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TAKE_DOWN, player); + HP_BAR(opponent, captureDamage: &damage[0]); + // turn 2 + MESSAGE("Wobbuffet used Take Down!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TAKE_DOWN, player); + HP_BAR(opponent, captureDamage: &damage[1]); + // turn 3 + MESSAGE("Wobbuffet used Take Down!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TAKE_DOWN, player); + HP_BAR(opponent, captureDamage: &damage[2]); + } THEN { + EXPECT_MUL_EQ(damage[0], UQ_4_12(1.2), damage[1]); + EXPECT_EQ(damage[0], damage[2]); + } +} + +SINGLE_BATTLE_TEST("(TERA) Terastallizing into the Stellar type boosts all moves up to 60 BP once per type") +{ + s16 damage[4]; + GIVEN { + ASSUME(gMovesInfo[MOVE_MEGA_DRAIN].power == 40); + ASSUME(gMovesInfo[MOVE_BUBBLE].power == 40); + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_MEGA_DRAIN); } + TURN { MOVE(player, MOVE_MEGA_DRAIN, tera: TRUE); } + TURN { MOVE(player, MOVE_MEGA_DRAIN); } + TURN { MOVE(player, MOVE_BUBBLE); } + } SCENE { + // turn 1 + MESSAGE("Wobbuffet used Mega Drain!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_MEGA_DRAIN, player); + HP_BAR(opponent, captureDamage: &damage[0]); + // turn 2 + MESSAGE("Wobbuffet used Mega Drain!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_MEGA_DRAIN, player); + HP_BAR(opponent, captureDamage: &damage[1]); + // turn 3 + MESSAGE("Wobbuffet used Mega Drain!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_MEGA_DRAIN, player); + HP_BAR(opponent, captureDamage: &damage[2]); + // turn 4 + MESSAGE("Wobbuffet used Bubble!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BUBBLE, player); + HP_BAR(opponent, captureDamage: &damage[3]); + } THEN { + // The jump from 40 BP to 72 BP (60 * 1.2x) is a 1.8x boost. + EXPECT_MUL_EQ(damage[0], Q_4_12(1.8), damage[1]); + EXPECT_EQ(damage[0], damage[2]); + EXPECT_EQ(damage[1], damage[3]); + } +} + +SINGLE_BATTLE_TEST("(TERA) Protean cannot change the type of a Terastallized Pokemon") +{ + GIVEN { + PLAYER(SPECIES_GRENINJA) { Ability(ABILITY_PROTEAN); TeraType(TYPE_GRASS); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_BUBBLE, tera: TRUE); + MOVE(opponent, MOVE_EMBER); } + } SCENE { + MESSAGE("Greninja used Bubble!"); + MESSAGE("Foe Wobbuffet used Ember!"); + MESSAGE("It's super effective!"); + } +} + +SINGLE_BATTLE_TEST("(TERA) Status moves don't expend Stellar's one-time type boost") +{ + s16 damage[2]; + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_GROWL, tera: TRUE); } + TURN { MOVE(player, MOVE_TAKE_DOWN); } + TURN { MOVE(player, MOVE_TAKE_DOWN); } + } SCENE { + // turn 1 + MESSAGE("Wobbuffet used Growl!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_GROWL, player); + // turn 2 + MESSAGE("Wobbuffet used Take Down!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TAKE_DOWN, player); + HP_BAR(opponent, captureDamage: &damage[0]); + // turn 3 + MESSAGE("Wobbuffet used Take Down!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TAKE_DOWN, player); + HP_BAR(opponent, captureDamage: &damage[1]); + } THEN { + EXPECT_MUL_EQ(damage[1], UQ_4_12(1.20), damage[0]); + } +} + +SINGLE_BATTLE_TEST("(TERA) Stellar type's one-time boost factors in dynamically-typed moves") +{ + s16 damage[4]; + GIVEN { + ASSUME(gMovesInfo[MOVE_WEATHER_BALL].type == TYPE_NORMAL); + PLAYER(SPECIES_PELIPPER) { Ability(ABILITY_DRIZZLE); TeraType(TYPE_STELLAR); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_WEATHER_BALL, tera: TRUE); MOVE(opponent, MOVE_RECOVER); } + TURN { MOVE(player, MOVE_TAKE_DOWN); MOVE(opponent, MOVE_RECOVER); } + TURN { MOVE(player, MOVE_TAKE_DOWN); MOVE(opponent, MOVE_RECOVER); } + TURN { MOVE(player, MOVE_WATER_PULSE); MOVE(opponent, MOVE_RECOVER); } + TURN { MOVE(player, MOVE_WATER_PULSE); MOVE(opponent, MOVE_RECOVER); } + } SCENE { + MESSAGE("Pelipper used Weather Ball!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_WEATHER_BALL, player); + // turn 2 + MESSAGE("Pelipper used Take Down!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TAKE_DOWN, player); + HP_BAR(opponent, captureDamage: &damage[0]); + // turn 3 + MESSAGE("Pelipper used Take Down!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TAKE_DOWN, player); + HP_BAR(opponent, captureDamage: &damage[1]); + // turn 4 + MESSAGE("Pelipper used Water Pulse!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_PULSE, player); + HP_BAR(opponent, captureDamage: &damage[2]); + // turn 5 + MESSAGE("Pelipper used Water Pulse!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_PULSE, player); + HP_BAR(opponent, captureDamage: &damage[3]); + } THEN { + // Take Down should have a Normal type boost applied + EXPECT_MUL_EQ(damage[1], UQ_4_12(1.20), damage[0]); + // Water Pulse should not have a Water type boost applied + EXPECT_EQ(damage[3], damage[2]); + } +} + +SINGLE_BATTLE_TEST("(TERA) All type indicators function correctly") +{ + u32 type; + PARAMETRIZE { type = TYPE_NORMAL; } + PARAMETRIZE { type = TYPE_FIGHTING; } + PARAMETRIZE { type = TYPE_FLYING; } + PARAMETRIZE { type = TYPE_POISON; } + PARAMETRIZE { type = TYPE_GROUND; } + PARAMETRIZE { type = TYPE_ROCK; } + PARAMETRIZE { type = TYPE_BUG; } + PARAMETRIZE { type = TYPE_GHOST; } + PARAMETRIZE { type = TYPE_STEEL; } + PARAMETRIZE { type = TYPE_MYSTERY; } + PARAMETRIZE { type = TYPE_FIRE; } + PARAMETRIZE { type = TYPE_WATER; } + PARAMETRIZE { type = TYPE_GRASS; } + PARAMETRIZE { type = TYPE_ELECTRIC; } + PARAMETRIZE { type = TYPE_PSYCHIC; } + PARAMETRIZE { type = TYPE_ICE; } + PARAMETRIZE { type = TYPE_DRAGON; } + PARAMETRIZE { type = TYPE_DARK; } + PARAMETRIZE { type = TYPE_FAIRY; } + PARAMETRIZE { type = TYPE_STELLAR; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(type); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE, tera: TRUE); } + } +} diff --git a/test/battle/move_effect/tera_blast.c b/test/battle/move_effect/tera_blast.c new file mode 100644 index 0000000000..4592cf32a5 --- /dev/null +++ b/test/battle/move_effect/tera_blast.c @@ -0,0 +1,137 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_TERA_BLAST].effect == EFFECT_TERA_BLAST); +} + +SINGLE_BATTLE_TEST("Tera Blast changes from Normal-type to the user's Tera Type") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_TERA_BLAST].type == TYPE_NORMAL); + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_DARK); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TERA_BLAST, tera: TRUE); } + } SCENE { + MESSAGE("Wobbuffet used Tera Blast!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player); + MESSAGE("It's super effective!"); + } +} + +SINGLE_BATTLE_TEST("Tera Blast becomes a physical move if the user is Terastallized and has a higher Attack stat", s16 damage) +{ + bool32 tera; + PARAMETRIZE { tera = FALSE; } + PARAMETRIZE { tera = TRUE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_NORMAL); Attack(100); SpAttack(50); } + OPPONENT(SPECIES_WOBBUFFET) { Defense(200); SpDefense(200); } + } WHEN { + TURN { MOVE(player, MOVE_TERA_BLAST, tera: tera); } + } SCENE { + MESSAGE("Wobbuffet used Tera Blast!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + // Since Wobbuffett has equal defenses, Tera Blast should do 1.5x more damage + // from gaining STAB and an additional 2.0x damage from using its highest + // attacking stat. + EXPECT_MUL_EQ(results[0].damage, UQ_4_12(3.0), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Stellar-type Tera Blast lowers both offensive stats") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TERA_BLAST, tera: TRUE); } + } SCENE { + MESSAGE("Wobbuffet used Tera Blast!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Wobbuffet's Attack fell!"); + MESSAGE("Wobbuffet's Sp. Atk fell!"); + } +} + + +SINGLE_BATTLE_TEST("Stellar-type Tera Blast has 100 BP and a one-time 1.2x boost") +{ + s16 damage[3]; + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TERA_BLAST); MOVE(opponent, MOVE_RECOVER); } + TURN { MOVE(player, MOVE_TERA_BLAST, tera: TRUE); } + TURN { MOVE(player, MOVE_WORK_UP); } + TURN { MOVE(player, MOVE_TERA_BLAST); } + } SCENE { + // turn 1 + MESSAGE("Wobbuffet used Tera Blast!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player); + HP_BAR(opponent, captureDamage: &damage[0]); + // turn 2 + MESSAGE("Wobbuffet used Tera Blast!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player); + HP_BAR(opponent, captureDamage: &damage[1]); + // turn 4 + MESSAGE("Wobbuffet used Tera Blast!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player); + HP_BAR(opponent, captureDamage: &damage[2]); + } THEN { + // 80 BP to 120 BP (100 * 1.2) boost upon Terastallizing + EXPECT_MUL_EQ(damage[0], UQ_4_12(1.50), damage[1]); + // 120 BP to 100 BP after Stellar boost expended + EXPECT_MUL_EQ(damage[2], UQ_4_12(1.20), damage[1]); + } +} + +SINGLE_BATTLE_TEST("Stellar-type Tera Blast is super-effective on Stellar-type Pokemon") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); } + OPPONENT(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); } + } WHEN { + TURN { MOVE(player, MOVE_TERA_BLAST, tera: TRUE); MOVE(opponent, MOVE_CELEBRATE, tera: TRUE); } + } SCENE { + MESSAGE("Wobbuffet used Tera Blast!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player); + MESSAGE("It's super effective!"); + } +} + +SINGLE_BATTLE_TEST("Stellar-type Tera Blast activates a Stellar-type Pokemon's Weakness Policy") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { TeraType(TYPE_STELLAR); } + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_WEAKNESS_POLICY); TeraType(TYPE_NORMAL); } + } WHEN { + TURN { MOVE(player, MOVE_TERA_BLAST, tera: TRUE); MOVE(opponent, MOVE_CELEBRATE, tera: TRUE); } + } SCENE { + MESSAGE("Wobbuffet used Tera Blast!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player); + MESSAGE("It's super effective!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); + } +} + +SINGLE_BATTLE_TEST("Flying-type Tera Blast does not have its priority boosted by Gale Wings") +{ + GIVEN { + PLAYER(SPECIES_TALONFLAME) { Ability(ABILITY_GALE_WINGS); TeraType(TYPE_FLYING); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TERA_BLAST, tera: TRUE); MOVE(opponent, MOVE_QUICK_ATTACK); } + } SCENE { + MESSAGE("Foe Wobbuffet used Quick Attack!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_ATTACK, opponent); + MESSAGE("Talonflame used Tera Blast!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player); + } +} diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index b800f9099f..aaeba97479 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -2075,6 +2075,9 @@ void MoveGetIdAndSlot(s32 battlerId, struct MoveContext *ctx, u32 *moveId, u32 * if (ctx->explicitDynamax && ctx->dynamax) *moveSlot |= RET_DYNAMAX; + + if (ctx->explicitTera && ctx->tera) + *moveSlot |= RET_TERASTAL; } void Move(u32 sourceLine, struct BattlePokemon *battler, struct MoveContext ctx)