From d8c1b55b2223ee0b040ce4abfc1ae60b34f637ab Mon Sep 17 00:00:00 2001 From: cawtds <38510667+cawtds@users.noreply.github.com> Date: Sat, 26 Apr 2025 00:21:03 +0200 Subject: [PATCH 01/23] ub fix for OOB access with gActiveBattler --- src/battle_controllers.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/battle_controllers.c b/src/battle_controllers.c index 02276a7e14..cf50d4c340 100644 --- a/src/battle_controllers.c +++ b/src/battle_controllers.c @@ -57,6 +57,11 @@ void SetUpBattleVarsAndBirchZigzagoon(void) gBattleControllerExecFlags = 0; ClearBattleAnimationVars(); ClearBattleMonForms(); +// UB: at the start of a battle CheckMoveLimitations is called with gActiveBattler +// from the previous battle, which can lead to multiple arrays being accessed out of bounds +#ifdef UBFIX + gActiveBattler = 0; +#endif BattleAI_HandleItemUseBeforeAISetup(0xF); if (gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE) From b11741855dbbf1d1589d0eb68abbfa887e72b260 Mon Sep 17 00:00:00 2001 From: cawtds <38510667+cawtds@users.noreply.github.com> Date: Fri, 2 Jan 2026 21:19:01 +0100 Subject: [PATCH 02/23] Add enum for battle script opcodes (#2222) --- asm/macros/battle_script.inc | 500 ++++++++++----------- include/constants/battle_script_commands.h | 253 +++++++++++ src/battle_script_commands.c | 498 ++++++++++---------- 3 files changed, 752 insertions(+), 499 deletions(-) diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 7209310fca..5b82e79196 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1,168 +1,168 @@ @ commands .macro attackcanceler - .byte 0x0 + .byte B_SCR_OP_ATTACKCANCELER .endm .macro accuracycheck failPtr:req, move:req - .byte 0x1 + .byte B_SCR_OP_ACCURACYCHECK .4byte \failPtr .2byte \move .endm .macro attackstring - .byte 0x2 + .byte B_SCR_OP_ATTACKSTRING .endm .macro ppreduce - .byte 0x3 + .byte B_SCR_OP_PPREDUCE .endm .macro critcalc - .byte 0x4 + .byte B_SCR_OP_CRITCALC .endm .macro damagecalc - .byte 0x5 + .byte B_SCR_OP_DAMAGECALC .endm .macro typecalc - .byte 0x6 + .byte B_SCR_OP_TYPECALC .endm .macro adjustnormaldamage - .byte 0x7 + .byte B_SCR_OP_ADJUSTNORMALDAMAGE .endm .macro adjustnormaldamage2 - .byte 0x8 + .byte B_SCR_OP_ADJUSTNORMALDAMAGE2 .endm .macro attackanimation - .byte 0x9 + .byte B_SCR_OP_ATTACKANIMATION .endm .macro waitanimation - .byte 0xa + .byte B_SCR_OP_WAITANIMATION .endm .macro healthbarupdate battler:req - .byte 0xb + .byte B_SCR_OP_HEALTHBARUPDATE .byte \battler .endm .macro datahpupdate battler:req - .byte 0xc + .byte B_SCR_OP_DATAHPUPDATE .byte \battler .endm .macro critmessage - .byte 0xd + .byte B_SCR_OP_CRITMESSAGE .endm .macro effectivenesssound - .byte 0xe + .byte B_SCR_OP_EFFECTIVENESSSOUND .endm .macro resultmessage - .byte 0xf + .byte B_SCR_OP_RESULTMESSAGE .endm .macro printstring id:req - .byte 0x10 + .byte B_SCR_OP_PRINTSTRING .2byte \id .endm .macro printselectionstring id:req - .byte 0x11 + .byte B_SCR_OP_PRINTSELECTIONSTRING .2byte \id .endm .macro waitmessage time:req - .byte 0x12 + .byte B_SCR_OP_WAITMESSAGE .2byte \time .endm .macro printfromtable ptr:req - .byte 0x13 + .byte B_SCR_OP_PRINTFROMTABLE .4byte \ptr .endm .macro printselectionstringfromtable ptr:req - .byte 0x14 + .byte B_SCR_OP_PRINTSELECTIONSTRINGFROMTABLE .4byte \ptr .endm .macro seteffectwithchance - .byte 0x15 + .byte B_SCR_OP_SETEFFECTWITHCHANCE .endm .macro seteffectprimary - .byte 0x16 + .byte B_SCR_OP_SETEFFECTPRIMARY .endm .macro seteffectsecondary - .byte 0x17 + .byte B_SCR_OP_SETEFFECTSECONDARY .endm .macro clearstatusfromeffect battler:req - .byte 0x18 + .byte B_SCR_OP_CLEARSTATUSFROMEFFECT .byte \battler .endm .macro tryfaintmon battler:req - .byte 0x19 + .byte B_SCR_OP_TRYFAINTMON .byte \battler .byte FALSE .4byte NULL .endm .macro tryfaintmon_spikes battler:req, ptr:req - .byte 0x19 + .byte B_SCR_OP_TRYFAINTMON .byte \battler .byte TRUE .4byte \ptr .endm .macro dofaintanimation battler:req - .byte 0x1a + .byte B_SCR_OP_DOFAINTANIMATION .byte \battler .endm .macro cleareffectsonfaint battler:req - .byte 0x1b + .byte B_SCR_OP_CLEAREFFECTSONFAINT .byte \battler .endm .macro jumpifstatus battler:req, status1:req, ptr:req - .byte 0x1c + .byte B_SCR_OP_JUMPIFSTATUS .byte \battler .4byte \status1 .4byte \ptr .endm .macro jumpifstatus2 battler:req, status2:req, ptr:req - .byte 0x1d + .byte B_SCR_OP_JUMPIFSTATUS2 .byte \battler .4byte \status2 .4byte \ptr .endm .macro jumpifability battler:req, ability:req, ptr:req - .byte 0x1e + .byte B_SCR_OP_JUMPIFABILITY .byte \battler .byte \ability .4byte \ptr .endm .macro jumpifsideaffecting battler:req, sidestatus:req, ptr:req - .byte 0x1f + .byte B_SCR_OP_JUMPIFSIDEAFFECTING .byte \battler .2byte \sidestatus .4byte \ptr .endm .macro jumpifstat battler:req, ifflag:req, stat:req, value:req, ptr - .byte 0x20 + .byte B_SCR_OP_JUMPIFSTAT .byte \battler .byte \ifflag .byte \stat @@ -171,7 +171,7 @@ .endm .macro jumpifstatus3condition battler:req, status3:req, condition:req, ptr:req - .byte 0x21 + .byte B_SCR_OP_JUMPIFSTATUS3CONDITION .byte \battler .4byte \status3 .byte \condition @@ -179,43 +179,43 @@ .endm .macro jumpiftype battler:req, type:req, ptr:req - .byte 0x22 + .byte B_SCR_OP_JUMPIFTYPE .byte \battler .byte \type .4byte \ptr .endm .macro getexp battler:req - .byte 0x23 + .byte B_SCR_OP_GETEXP .byte \battler .endm .macro checkteamslost ptr:req - .byte 0x24 + .byte B_SCR_OP_CHECKTEAMSLOST .4byte \ptr .endm .macro movevaluescleanup - .byte 0x25 + .byte B_SCR_OP_MOVEVALUESCLEANUP .endm .macro setmultihit value:req - .byte 0x26 + .byte B_SCR_OP_SETMULTIHIT .byte \value .endm .macro decrementmultihit value:req - .byte 0x27 + .byte B_SCR_OP_DECREMENTMULTIHIT .4byte \value .endm .macro goto ptr:req - .byte 0x28 + .byte B_SCR_OP_GOTO .4byte \ptr .endm .macro jumpifbyte ifflag:req, val:req, byte:req, ptr:req - .byte 0x29 + .byte B_SCR_OP_JUMPIFBYTE .byte \ifflag .4byte \val .byte \byte @@ -223,7 +223,7 @@ .endm .macro jumpifhalfword ifflag:req, val:req, hword:req, ptr:req - .byte 0x2a + .byte B_SCR_OP_JUMPIFHALFWORD .byte \ifflag .4byte \val .2byte \hword @@ -231,7 +231,7 @@ .endm .macro jumpifword ifflag:req, val:req, word:req, ptr:req - .byte 0x2b + .byte B_SCR_OP_JUMPIFWORD .byte \ifflag .4byte \val .4byte \word @@ -239,7 +239,7 @@ .endm .macro jumpifarrayequal val1:req, val2:req, size:req, ptr:req - .byte 0x2c + .byte B_SCR_OP_JUMPIFARRAYEQUAL .4byte \val1 .4byte \val2 .byte \size @@ -247,7 +247,7 @@ .endm .macro jumpifarraynotequal val1:req, val2:req, size:req, ptr:req - .byte 0x2d + .byte B_SCR_OP_JUMPIFARRAYNOTEQUAL .4byte \val1 .4byte \val2 .byte \size @@ -255,32 +255,32 @@ .endm .macro setbyte ptr:req, byte:req - .byte 0x2e + .byte B_SCR_OP_SETBYTE .4byte \ptr .byte \byte .endm .macro addbyte ptr:req, byte:req - .byte 0x2f + .byte B_SCR_OP_ADDBYTE .4byte \ptr .byte \byte .endm .macro subbyte ptr:req, byte:req - .byte 0x30 + .byte B_SCR_OP_SUBBYTE .4byte \ptr .byte \byte .endm .macro copyarray dest:req, src:req, size:req - .byte 0x31 + .byte B_SCR_OP_COPYARRAY .4byte \dest .4byte \src .byte \size .endm .macro copyarraywithindex dest:req, src:req, index:req, size:req - .byte 0x32 + .byte B_SCR_OP_COPYARRAYWITHINDEX .4byte \dest .4byte \src .4byte \index @@ -288,125 +288,125 @@ .endm .macro orbyte ptr:req, byte:req - .byte 0x33 + .byte B_SCR_OP_ORBYTE .4byte \ptr .byte \byte .endm .macro orhalfword ptr:req, hword:req - .byte 0x34 + .byte B_SCR_OP_ORHALFWORD .4byte \ptr .2byte \hword .endm .macro orword ptr:req, word:req - .byte 0x35 + .byte B_SCR_OP_ORWORD .4byte \ptr .4byte \word .endm .macro bicbyte ptr:req, byte:req - .byte 0x36 + .byte B_SCR_OP_BICBYTE .4byte \ptr .byte \byte .endm .macro bichalfword ptr:req, hword:req - .byte 0x37 + .byte B_SCR_OP_BICHALFWORD .4byte \ptr .2byte \hword .endm .macro bicword ptr:req, word:req - .byte 0x38 + .byte B_SCR_OP_BICWORD .4byte \ptr .4byte \word .endm .macro pause time:req - .byte 0x39 + .byte B_SCR_OP_PAUSE .2byte \time .endm .macro waitstate - .byte 0x3a + .byte B_SCR_OP_WAITSTATE .endm .macro healthbar_update battler:req - .byte 0x3b + .byte B_SCR_OP_HEALTHBAR_UPDATE .byte \battler .endm .macro return - .byte 0x3c + .byte B_SCR_OP_RETURN .endm .macro end - .byte 0x3d + .byte B_SCR_OP_END .endm .macro end2 - .byte 0x3e + .byte B_SCR_OP_END2 .endm .macro end3 - .byte 0x3f + .byte B_SCR_OP_END3 .endm .macro jumpifaffectedbyprotect ptr:req - .byte 0x40 + .byte B_SCR_OP_JUMPIFAFFECTEDBYPROTECT .4byte \ptr .endm .macro call ptr:req - .byte 0x41 + .byte B_SCR_OP_CALL .4byte \ptr .endm .macro jumpiftype2 battler:req, type:req, ptr:req - .byte 0x42 + .byte B_SCR_OP_JUMPIFTYPE2 .byte \battler .byte \type .4byte \ptr .endm .macro jumpifabilitypresent ability:req, ptr:req - .byte 0x43 + .byte B_SCR_OP_JUMPIFABILITYPRESENT .byte \ability .4byte \ptr .endm .macro endselectionscript - .byte 0x44 + .byte B_SCR_OP_ENDSELECTIONSCRIPT .endm .macro playanimation battler:req, animType:req, arg=NULL - .byte 0x45 + .byte B_SCR_OP_PLAYANIMATION .byte \battler .byte \animType .4byte \arg .endm .macro playanimation_var battler:req, animType:req, arg=NULL - .byte 0x46 + .byte B_SCR_OP_PLAYANIMATION_VAR .byte \battler .4byte \animType .4byte \arg .endm .macro setgraphicalstatchangevalues - .byte 0x47 + .byte B_SCR_OP_SETGRAPHICALSTATCHANGEVALUES .endm .macro playstatchangeanimation battler:req, stats:req, statchange:req - .byte 0x48 + .byte B_SCR_OP_PLAYSTATCHANGEANIMATION .byte \battler .byte \stats .byte \statchange .endm .macro moveend endMode:req, endState:req - .byte 0x49 + .byte B_SCR_OP_MOVEEND .byte \endMode .byte \endState .endm @@ -444,812 +444,812 @@ .endm .macro typecalc2 - .byte 0x4a + .byte B_SCR_OP_TYPECALC2 .endm .macro returnatktoball - .byte 0x4b + .byte B_SCR_OP_RETURNATKTOBALL .endm .macro getswitchedmondata battler:req - .byte 0x4c + .byte B_SCR_OP_GETSWITCHEDMONDATA .byte \battler .endm .macro switchindataupdate battler:req - .byte 0x4d + .byte B_SCR_OP_SWITCHINDATAUPDATE .byte \battler .endm .macro switchinanim battler:req, dontclearsubstitutebit:req - .byte 0x4e + .byte B_SCR_OP_SWITCHINANIM .byte \battler .byte \dontclearsubstitutebit .endm .macro jumpifcantswitch battler:req, ptr:req - .byte 0x4f + .byte B_SCR_OP_JUMPIFCANTSWITCH .byte \battler .4byte \ptr .endm .macro openpartyscreen battler:req, ptr:req - .byte 0x50 + .byte B_SCR_OP_OPENPARTYSCREEN .byte \battler .4byte \ptr .endm .macro switchhandleorder battler:req, state:req - .byte 0x51 + .byte B_SCR_OP_SWITCHHANDLEORDER .byte \battler .byte \state .endm .macro switchineffects battler:req - .byte 0x52 + .byte B_SCR_OP_SWITCHINEFFECTS .byte \battler .endm .macro trainerslidein battler:req - .byte 0x53 + .byte B_SCR_OP_TRAINERSLIDEIN .byte \battler .endm .macro playse song:req - .byte 0x54 + .byte B_SCR_OP_PLAYSE .2byte \song .endm .macro fanfare song:req - .byte 0x55 + .byte B_SCR_OP_FANFARE .2byte \song .endm .macro playfaintcry battler:req - .byte 0x56 + .byte B_SCR_OP_PLAYFAINTCRY .byte \battler .endm .macro endlinkbattle - .byte 0x57 + .byte B_SCR_OP_ENDLINKBATTLE .endm .macro returntoball battler:req - .byte 0x58 + .byte B_SCR_OP_RETURNTOBALL .byte \battler .endm .macro handlelearnnewmove learnedMovePtr:req, nothingToLearnPtr:req, isFirstMove:req - .byte 0x59 + .byte B_SCR_OP_HANDLELEARNNEWMOVE .4byte \learnedMovePtr .4byte \nothingToLearnPtr .byte \isFirstMove .endm .macro yesnoboxlearnmove forgotMovePtr:req - .byte 0x5a + .byte B_SCR_OP_YESNOBOXLEARNMOVE .4byte \forgotMovePtr .endm .macro yesnoboxstoplearningmove noPtr:req - .byte 0x5b + .byte B_SCR_OP_YESNOBOXSTOPLEARNINGMOVE .4byte \noPtr .endm .macro hitanimation battler:req - .byte 0x5c + .byte B_SCR_OP_HITANIMATION .byte \battler .endm .macro getmoneyreward - .byte 0x5d + .byte B_SCR_OP_GETMONEYREWARD .endm .macro updatebattlermoves battler:req - .byte 0x5e + .byte B_SCR_OP_UPDATEBATTLERMOVES .byte \battler .endm .macro swapattackerwithtarget - .byte 0x5f + .byte B_SCR_OP_SWAPATTACKERWITHTARGET .endm .macro incrementgamestat stat:req - .byte 0x60 + .byte B_SCR_OP_INCREMENTGAMESTAT .byte \stat .endm .macro drawpartystatussummary battler:req - .byte 0x61 + .byte B_SCR_OP_DRAWPARTYSTATUSSUMMARY .byte \battler .endm .macro hidepartystatussummary battler:req - .byte 0x62 + .byte B_SCR_OP_HIDEPARTYSTATUSSUMMARY .byte \battler .endm .macro jumptocalledmove notChosenMove:req - .byte 0x63 + .byte B_SCR_OP_JUMPTOCALLEDMOVE .byte \notChosenMove .endm .macro statusanimation battler:req - .byte 0x64 + .byte B_SCR_OP_STATUSANIMATION .byte \battler .endm .macro status2animation battler:req, status2:req - .byte 0x65 + .byte B_SCR_OP_STATUS2ANIMATION .byte \battler .4byte \status2 .endm .macro chosenstatusanimation battler:req, isStatus2:req, status:req - .byte 0x66 + .byte B_SCR_OP_CHOSENSTATUSANIMATION .byte \battler .byte \isStatus2 .4byte \status .endm .macro yesnobox - .byte 0x67 + .byte B_SCR_OP_YESNOBOX .endm .macro cancelallactions - .byte 0x68 + .byte B_SCR_OP_CANCELALLACTIONS .endm .macro adjustsetdamage - .byte 0x69 + .byte B_SCR_OP_ADJUSTSETDAMAGE .endm .macro removeitem battler:req - .byte 0x6a + .byte B_SCR_OP_REMOVEITEM .byte \battler .endm .macro atknameinbuff1 - .byte 0x6b + .byte B_SCR_OP_ATKNAMEINBUFF1 .endm .macro drawlvlupbox - .byte 0x6c + .byte B_SCR_OP_DRAWLVLUPBOX .endm .macro resetsentmonsvalue - .byte 0x6d + .byte B_SCR_OP_RESETSENTMONSVALUE .endm .macro setatktoplayer0 - .byte 0x6e + .byte B_SCR_OP_SETATKTOPLAYER0 .endm .macro makevisible battler:req - .byte 0x6f + .byte B_SCR_OP_MAKEVISIBLE .byte \battler .endm .macro recordlastability battler:req - .byte 0x70 + .byte B_SCR_OP_RECORDLASTABILITY .byte \battler .endm .macro buffermovetolearn - .byte 0x71 + .byte B_SCR_OP_BUFFERMOVETOLEARN .endm .macro jumpifplayerran ptr:req - .byte 0x72 + .byte B_SCR_OP_JUMPIFPLAYERRAN .4byte \ptr .endm .macro hpthresholds battler:req - .byte 0x73 + .byte B_SCR_OP_HPTHRESHOLDS .byte \battler .endm .macro hpthresholds2 battler:req - .byte 0x74 + .byte B_SCR_OP_HPTHRESHOLDS2 .byte \battler .endm .macro useitemonopponent - .byte 0x75 + .byte B_SCR_OP_USEITEMONOPPONENT .endm .macro various battler:req, id:req - .byte 0x76 + .byte B_SCR_OP_VARIOUS .byte \battler .byte \id .endm .macro setprotectlike - .byte 0x77 + .byte B_SCR_OP_SETPROTECTLIKE .endm .macro tryexplosion - .byte 0x78 + .byte B_SCR_OP_TRYEXPLOSION .endm .macro setatkhptozero - .byte 0x79 + .byte B_SCR_OP_SETATKHPTOZERO .endm .macro jumpifnexttargetvalid ptr:req - .byte 0x7a + .byte B_SCR_OP_JUMPIFNEXTTARGETVALID .4byte \ptr .endm .macro tryhealhalfhealth ptr:req, battler:req - .byte 0x7b + .byte B_SCR_OP_TRYHEALHALFHEALTH .4byte \ptr .byte \battler .endm .macro trymirrormove - .byte 0x7c + .byte B_SCR_OP_TRYMIRRORMOVE .endm .macro setrain - .byte 0x7d + .byte B_SCR_OP_SETRAIN .endm .macro setreflect - .byte 0x7e + .byte B_SCR_OP_SETREFLECT .endm .macro setseeded - .byte 0x7f + .byte B_SCR_OP_SETSEEDED .endm .macro manipulatedamage mode:req - .byte 0x80 + .byte B_SCR_OP_MANIPULATEDAMAGE .byte \mode .endm .macro trysetrest ptr:req - .byte 0x81 + .byte B_SCR_OP_TRYSETREST .4byte \ptr .endm .macro jumpifnotfirstturn ptr:req - .byte 0x82 + .byte B_SCR_OP_JUMPIFNOTFIRSTTURN .4byte \ptr .endm .macro nop - .byte 0x83 + .byte B_SCR_OP_NOP .endm .macro jumpifcantmakeasleep ptr:req - .byte 0x84 + .byte B_SCR_OP_JUMPIFCANTMAKEASLEEP .4byte \ptr .endm .macro stockpile - .byte 0x85 + .byte B_SCR_OP_STOCKPILE .endm .macro stockpiletobasedamage ptr:req - .byte 0x86 + .byte B_SCR_OP_STOCKPILETOBASEDAMAGE .4byte \ptr .endm .macro stockpiletohpheal ptr:req - .byte 0x87 + .byte B_SCR_OP_STOCKPILETOHPHEAL .4byte \ptr .endm .macro negativedamage - .byte 0x88 + .byte B_SCR_OP_NEGATIVEDAMAGE .endm .macro statbuffchange flags:req, jumpptr:req - .byte 0x89 + .byte B_SCR_OP_STATBUFFCHANGE .byte \flags .4byte \jumpptr .endm .macro normalisebuffs - .byte 0x8a + .byte B_SCR_OP_NORMALISEBUFFS .endm .macro setbide - .byte 0x8b + .byte B_SCR_OP_SETBIDE .endm .macro confuseifrepeatingattackends - .byte 0x8c + .byte B_SCR_OP_CONFUSEIFREPEATINGATTACKENDS .endm .macro setmultihitcounter val:req - .byte 0x8d + .byte B_SCR_OP_SETMULTIHITCOUNTER .byte \val .endm .macro initmultihitstring - .byte 0x8e + .byte B_SCR_OP_INITMULTIHITSTRING .endm .macro forcerandomswitch ptr:req - .byte 0x8f + .byte B_SCR_OP_FORCERANDOMSWITCH .4byte \ptr .endm .macro tryconversiontypechange ptr:req - .byte 0x90 + .byte B_SCR_OP_TRYCONVERSIONTYPECHANGE .4byte \ptr .endm .macro givepaydaymoney - .byte 0x91 + .byte B_SCR_OP_GIVEPAYDAYMONEY .endm .macro setlightscreen - .byte 0x92 + .byte B_SCR_OP_SETLIGHTSCREEN .endm .macro tryKO ptr:req - .byte 0x93 + .byte B_SCR_OP_TRYKO .4byte \ptr .endm .macro damagetohalftargethp - .byte 0x94 + .byte B_SCR_OP_DAMAGETOHALFTARGETHP .endm .macro setsandstorm - .byte 0x95 + .byte B_SCR_OP_SETSANDSTORM .endm .macro weatherdamage - .byte 0x96 + .byte B_SCR_OP_WEATHERDAMAGE .endm .macro tryinfatuating ptr:req - .byte 0x97 + .byte B_SCR_OP_TRYINFATUATING .4byte \ptr .endm .macro updatestatusicon battler:req - .byte 0x98 + .byte B_SCR_OP_UPDATESTATUSICON .byte \battler .endm .macro setmist - .byte 0x99 + .byte B_SCR_OP_SETMIST .endm .macro setfocusenergy - .byte 0x9a + .byte B_SCR_OP_SETFOCUSENERGY .endm .macro transformdataexecution - .byte 0x9b + .byte B_SCR_OP_TRANSFORMDATAEXECUTION .endm .macro setsubstitute - .byte 0x9c + .byte B_SCR_OP_SETSUBSTITUTE .endm .macro mimicattackcopy ptr:req - .byte 0x9d + .byte B_SCR_OP_MIMICATTACKCOPY .4byte \ptr .endm .macro metronome - .byte 0x9e + .byte B_SCR_OP_METRONOME .endm .macro dmgtolevel - .byte 0x9f + .byte B_SCR_OP_DMGTOLEVEL .endm .macro psywavedamageeffect - .byte 0xa0 + .byte B_SCR_OP_PSYWAVEDAMAGEEFFECT .endm .macro counterdamagecalculator ptr:req - .byte 0xa1 + .byte B_SCR_OP_COUNTERDAMAGECALCULATOR .4byte \ptr .endm .macro mirrorcoatdamagecalculator ptr:req - .byte 0xa2 + .byte B_SCR_OP_MIRRORCOATDAMAGECALCULATOR .4byte \ptr .endm .macro disablelastusedattack ptr:req - .byte 0xa3 + .byte B_SCR_OP_DISABLELASTUSEDATTACK .4byte \ptr .endm .macro trysetencore ptr:req - .byte 0xa4 + .byte B_SCR_OP_TRYSETENCORE .4byte \ptr .endm .macro painsplitdmgcalc ptr:req - .byte 0xa5 + .byte B_SCR_OP_PAINSPLITDMGCALC .4byte \ptr .endm .macro settypetorandomresistance ptr:req - .byte 0xa6 + .byte B_SCR_OP_SETTYPETORANDOMRESISTANCE .4byte \ptr .endm .macro setalwayshitflag - .byte 0xa7 + .byte B_SCR_OP_SETALWAYSHITFLAG .endm .macro copymovepermanently ptr:req - .byte 0xa8 + .byte B_SCR_OP_COPYMOVEPERMANENTLY .4byte \ptr .endm .macro trychoosesleeptalkmove ptr:req - .byte 0xa9 + .byte B_SCR_OP_TRYCHOOSESLEEPTALKMOVE .4byte \ptr .endm .macro setdestinybond - .byte 0xaa + .byte B_SCR_OP_SETDESTINYBOND .endm .macro trysetdestinybondtohappen - .byte 0xab + .byte B_SCR_OP_TRYSETDESTINYBONDTOHAPPEN .endm .macro remaininghptopower - .byte 0xac + .byte B_SCR_OP_REMAININGHPTOPOWER .endm .macro tryspiteppreduce ptr:req - .byte 0xad + .byte B_SCR_OP_TRYSPITEPPREDUCE .4byte \ptr .endm .macro healpartystatus - .byte 0xae + .byte B_SCR_OP_HEALPARTYSTATUS .endm .macro cursetarget ptr:req - .byte 0xaf + .byte B_SCR_OP_CURSETARGET .4byte \ptr .endm .macro trysetspikes ptr:req - .byte 0xb0 + .byte B_SCR_OP_TRYSETSPIKES .4byte \ptr .endm .macro setforesight - .byte 0xb1 + .byte B_SCR_OP_SETFORESIGHT .endm .macro trysetperishsong ptr:req - .byte 0xb2 + .byte B_SCR_OP_TRYSETPERISHSONG .4byte \ptr .endm .macro rolloutdamagecalculation - .byte 0xb3 + .byte B_SCR_OP_ROLLOUTDAMAGECALCULATION .endm .macro jumpifconfusedandstatmaxed stat:req, ptr:req - .byte 0xb4 + .byte B_SCR_OP_JUMPIFCONFUSEDANDSTATMAXED .byte \stat .4byte \ptr .endm .macro furycuttercalc - .byte 0xb5 + .byte B_SCR_OP_FURYCUTTERCALC .endm .macro friendshiptodamagecalculation - .byte 0xb6 + .byte B_SCR_OP_FRIENDSHIPTODAMAGECALCULATION .endm .macro presentdamagecalculation - .byte 0xb7 + .byte B_SCR_OP_PRESENTDAMAGECALCULATION .endm .macro setsafeguard - .byte 0xb8 + .byte B_SCR_OP_SETSAFEGUARD .endm .macro magnitudedamagecalculation - .byte 0xb9 + .byte B_SCR_OP_MAGNITUDEDAMAGECALCULATION .endm .macro jumpifnopursuitswitchdmg ptr:req - .byte 0xba + .byte B_SCR_OP_JUMPIFNOPURSUITSWITCHDMG .4byte \ptr .endm .macro setsunny - .byte 0xbb + .byte B_SCR_OP_SETSUNNY .endm .macro maxattackhalvehp ptr:req - .byte 0xbc + .byte B_SCR_OP_MAXATTACKHALVEHP .4byte \ptr .endm .macro copyfoestats ptr:req - .byte 0xbd + .byte B_SCR_OP_COPYFOESTATS .4byte \ptr .endm .macro rapidspinfree - .byte 0xbe + .byte B_SCR_OP_RAPIDSPINFREE .endm .macro setdefensecurlbit - .byte 0xbf + .byte B_SCR_OP_SETDEFENSECURLBIT .endm .macro recoverbasedonsunlight ptr:req - .byte 0xc0 + .byte B_SCR_OP_RECOVERBASEDONSUNLIGHT .4byte \ptr .endm .macro hiddenpowercalc - .byte 0xc1 + .byte B_SCR_OP_HIDDENPOWERCALC .endm .macro selectfirstvalidtarget - .byte 0xc2 + .byte B_SCR_OP_SELECTFIRSTVALIDTARGET .endm .macro trysetfutureattack ptr:req - .byte 0xc3 + .byte B_SCR_OP_TRYSETFUTUREATTACK .4byte \ptr .endm .macro trydobeatup endPtr:req, failPtr:req - .byte 0xc4 + .byte B_SCR_OP_TRYDOBEATUP .4byte \endPtr .4byte \failPtr .endm .macro setsemiinvulnerablebit - .byte 0xc5 + .byte B_SCR_OP_SETSEMIINVULNERABLEBIT .endm .macro clearsemiinvulnerablebit - .byte 0xc6 + .byte B_SCR_OP_CLEARSEMIINVULNERABLEBIT .endm .macro setminimize - .byte 0xc7 + .byte B_SCR_OP_SETMINIMIZE .endm .macro sethail - .byte 0xc8 + .byte B_SCR_OP_SETHAIL .endm .macro trymemento ptr:req - .byte 0xc9 + .byte B_SCR_OP_TRYMEMENTO .4byte \ptr .endm .macro setforcedtarget - .byte 0xca + .byte B_SCR_OP_SETFORCEDTARGET .endm .macro setcharge - .byte 0xcb + .byte B_SCR_OP_SETCHARGE .endm .macro callenvironmentattack - .byte 0xcc + .byte B_SCR_OP_CALLENVIRONMENTATTACK .endm .macro cureifburnedparalyzedorpoisoned ptr:req - .byte 0xcd + .byte B_SCR_OP_CUREIFBURNEDPARALYZEDORPOISONED .4byte \ptr .endm .macro settorment ptr:req - .byte 0xce + .byte B_SCR_OP_SETTORMENT .4byte \ptr .endm .macro jumpifnodamage ptr:req - .byte 0xcf + .byte B_SCR_OP_JUMPIFNODAMAGE .4byte \ptr .endm .macro settaunt ptr:req - .byte 0xd0 + .byte B_SCR_OP_SETTAUNT .4byte \ptr .endm .macro trysethelpinghand ptr:req - .byte 0xd1 + .byte B_SCR_OP_TRYSETHELPINGHAND .4byte \ptr .endm .macro tryswapitems ptr:req - .byte 0xd2 + .byte B_SCR_OP_TRYSWAPITEMS .4byte \ptr .endm .macro trycopyability ptr:req - .byte 0xd3 + .byte B_SCR_OP_TRYCOPYABILITY .4byte \ptr .endm .macro trywish turnNumber:req, ptr:req - .byte 0xd4 + .byte B_SCR_OP_TRYWISH .byte \turnNumber .4byte \ptr .endm .macro trysetroots ptr:req - .byte 0xd5 + .byte B_SCR_OP_TRYSETROOTS .4byte \ptr .endm .macro doubledamagedealtifdamaged - .byte 0xd6 + .byte B_SCR_OP_DOUBLEDAMAGEDEALTIFDAMAGED .endm .macro setyawn ptr:req - .byte 0xd7 + .byte B_SCR_OP_SETYAWN .4byte \ptr .endm .macro setdamagetohealthdifference ptr:req - .byte 0xd8 + .byte B_SCR_OP_SETDAMAGETOHEALTHDIFFERENCE .4byte \ptr .endm .macro scaledamagebyhealthratio - .byte 0xd9 + .byte B_SCR_OP_SCALEDAMAGEBYHEALTHRATIO .endm .macro tryswapabilities ptr:req - .byte 0xda + .byte B_SCR_OP_TRYSWAPABILITIES .4byte \ptr .endm .macro tryimprison ptr:req - .byte 0xdb + .byte B_SCR_OP_TRYIMPRISON .4byte \ptr .endm .macro trysetgrudge ptr:req - .byte 0xdc + .byte B_SCR_OP_TRYSETGRUDGE .4byte \ptr .endm .macro weightdamagecalculation - .byte 0xdd + .byte B_SCR_OP_WEIGHTDAMAGECALCULATION .endm .macro assistattackselect ptr:req - .byte 0xde + .byte B_SCR_OP_ASSISTATTACKSELECT .4byte \ptr .endm .macro trysetmagiccoat ptr:req - .byte 0xdf + .byte B_SCR_OP_TRYSETMAGICCOAT .4byte \ptr .endm .macro trysetsnatch ptr:req - .byte 0xe0 + .byte B_SCR_OP_TRYSETSNATCH .4byte \ptr .endm .macro trygetintimidatetarget ptr:req - .byte 0xe1 + .byte B_SCR_OP_TRYGETINTIMIDATETARGET .4byte \ptr .endm .macro switchoutabilities battler:req - .byte 0xe2 + .byte B_SCR_OP_SWITCHOUTABILITIES .byte \battler .endm .macro jumpifhasnohp battler:req, ptr:req - .byte 0xe3 + .byte B_SCR_OP_JUMPIFHASNOHP .byte \battler .4byte \ptr .endm .macro getsecretpowereffect - .byte 0xe4 + .byte B_SCR_OP_GETSECRETPOWEREFFECT .endm .macro pickup - .byte 0xe5 + .byte B_SCR_OP_PICKUP .endm .macro docastformchangeanimation - .byte 0xe6 + .byte B_SCR_OP_DOCASTFORMCHANGEANIMATION .endm .macro trycastformdatachange - .byte 0xe7 + .byte B_SCR_OP_TRYCASTFORMDATACHANGE .endm .macro settypebasedhalvers ptr:req - .byte 0xe8 + .byte B_SCR_OP_SETTYPEBASEDHALVERS .4byte \ptr .endm .macro setweatherballtype - .byte 0xe9 + .byte B_SCR_OP_SETWEATHERBALLTYPE .endm .macro tryrecycleitem ptr:req - .byte 0xea + .byte B_SCR_OP_TRYRECYCLEITEM .4byte \ptr .endm .macro settypetoenvironment ptr:req - .byte 0xeb + .byte B_SCR_OP_SETTYPETOENVIRONMENT .4byte \ptr .endm .macro pursuitdoubles ptr:req - .byte 0xec + .byte B_SCR_OP_PURSUITDOUBLES .4byte \ptr .endm .macro snatchsetbattlers - .byte 0xed + .byte B_SCR_OP_SNATCHSETBATTLERS .endm .macro removelightscreenreflect - .byte 0xee + .byte B_SCR_OP_REMOVELIGHTSCREENREFLECT .endm .macro handleballthrow - .byte 0xef + .byte B_SCR_OP_HANDLEBALLTHROW .endm .macro givecaughtmon - .byte 0xf0 + .byte B_SCR_OP_GIVECAUGHTMON .endm .macro trysetcaughtmondexflags ptr:req - .byte 0xf1 + .byte B_SCR_OP_TRYSETCAUGHTMONDEXFLAGS .4byte \ptr .endm .macro displaydexinfo - .byte 0xf2 + .byte B_SCR_OP_DISPLAYDEXINFO .endm .macro trygivecaughtmonnick ptr:req - .byte 0xf3 + .byte B_SCR_OP_TRYGIVECAUGHTMONNICK .4byte \ptr .endm .macro subattackerhpbydmg - .byte 0xf4 + .byte B_SCR_OP_SUBATTACKERHPBYDMG .endm .macro removeattackerstatus1 - .byte 0xf5 + .byte B_SCR_OP_REMOVEATTACKERSTATUS1 .endm .macro finishaction - .byte 0xf6 + .byte B_SCR_OP_FINISHACTION .endm .macro finishturn - .byte 0xf7 + .byte B_SCR_OP_FINISHTURN .endm .macro trainerslideout position:req - .byte 0xf8 + .byte B_SCR_OP_TRAINERSLIDEOUT .byte \position .endm diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h index c21276a4ef..ebdc9c0137 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -1,6 +1,259 @@ #ifndef GUARD_CONSTANTS_BATTLE_SCRIPT_COMMANDS_H #define GUARD_CONSTANTS_BATTLE_SCRIPT_COMMANDS_H +enum BattleScriptOpcode +{ + B_SCR_OP_ATTACKCANCELER, + B_SCR_OP_ACCURACYCHECK, + B_SCR_OP_ATTACKSTRING, + B_SCR_OP_PPREDUCE, + B_SCR_OP_CRITCALC, + B_SCR_OP_DAMAGECALC, + B_SCR_OP_TYPECALC, + B_SCR_OP_ADJUSTNORMALDAMAGE, + B_SCR_OP_ADJUSTNORMALDAMAGE2, + B_SCR_OP_ATTACKANIMATION, + B_SCR_OP_WAITANIMATION, + B_SCR_OP_HEALTHBARUPDATE, + B_SCR_OP_DATAHPUPDATE, + B_SCR_OP_CRITMESSAGE, + B_SCR_OP_EFFECTIVENESSSOUND, + B_SCR_OP_RESULTMESSAGE, + B_SCR_OP_PRINTSTRING, + B_SCR_OP_PRINTSELECTIONSTRING, + B_SCR_OP_WAITMESSAGE, + B_SCR_OP_PRINTFROMTABLE, + B_SCR_OP_PRINTSELECTIONSTRINGFROMTABLE, + B_SCR_OP_SETEFFECTWITHCHANCE, + B_SCR_OP_SETEFFECTPRIMARY, + B_SCR_OP_SETEFFECTSECONDARY, + B_SCR_OP_CLEARSTATUSFROMEFFECT, + B_SCR_OP_TRYFAINTMON, + B_SCR_OP_DOFAINTANIMATION, + B_SCR_OP_CLEAREFFECTSONFAINT, + B_SCR_OP_JUMPIFSTATUS, + B_SCR_OP_JUMPIFSTATUS2, + B_SCR_OP_JUMPIFABILITY, + B_SCR_OP_JUMPIFSIDEAFFECTING, + B_SCR_OP_JUMPIFSTAT, + B_SCR_OP_JUMPIFSTATUS3CONDITION, + B_SCR_OP_JUMPIFTYPE, + B_SCR_OP_GETEXP, + B_SCR_OP_CHECKTEAMSLOST, + B_SCR_OP_MOVEVALUESCLEANUP, + B_SCR_OP_SETMULTIHIT, + B_SCR_OP_DECREMENTMULTIHIT, + B_SCR_OP_GOTO, + B_SCR_OP_JUMPIFBYTE, + B_SCR_OP_JUMPIFHALFWORD, + B_SCR_OP_JUMPIFWORD, + B_SCR_OP_JUMPIFARRAYEQUAL, + B_SCR_OP_JUMPIFARRAYNOTEQUAL, + B_SCR_OP_SETBYTE, + B_SCR_OP_ADDBYTE, + B_SCR_OP_SUBBYTE, + B_SCR_OP_COPYARRAY, + B_SCR_OP_COPYARRAYWITHINDEX, + B_SCR_OP_ORBYTE, + B_SCR_OP_ORHALFWORD, + B_SCR_OP_ORWORD, + B_SCR_OP_BICBYTE, + B_SCR_OP_BICHALFWORD, + B_SCR_OP_BICWORD, + B_SCR_OP_PAUSE, + B_SCR_OP_WAITSTATE, + B_SCR_OP_HEALTHBAR_UPDATE, + B_SCR_OP_RETURN, + B_SCR_OP_END, + B_SCR_OP_END2, + B_SCR_OP_END3, + B_SCR_OP_JUMPIFAFFECTEDBYPROTECT, + B_SCR_OP_CALL, + B_SCR_OP_JUMPIFTYPE2, + B_SCR_OP_JUMPIFABILITYPRESENT, + B_SCR_OP_ENDSELECTIONSCRIPT, + B_SCR_OP_PLAYANIMATION, + B_SCR_OP_PLAYANIMATION_VAR, + B_SCR_OP_SETGRAPHICALSTATCHANGEVALUES, + B_SCR_OP_PLAYSTATCHANGEANIMATION, + B_SCR_OP_MOVEEND, + B_SCR_OP_TYPECALC2, + B_SCR_OP_RETURNATKTOBALL, + B_SCR_OP_GETSWITCHEDMONDATA, + B_SCR_OP_SWITCHINDATAUPDATE, + B_SCR_OP_SWITCHINANIM, + B_SCR_OP_JUMPIFCANTSWITCH, + B_SCR_OP_OPENPARTYSCREEN, + B_SCR_OP_SWITCHHANDLEORDER, + B_SCR_OP_SWITCHINEFFECTS, + B_SCR_OP_TRAINERSLIDEIN, + B_SCR_OP_PLAYSE, + B_SCR_OP_FANFARE, + B_SCR_OP_PLAYFAINTCRY, + B_SCR_OP_ENDLINKBATTLE, + B_SCR_OP_RETURNTOBALL, + B_SCR_OP_HANDLELEARNNEWMOVE, + B_SCR_OP_YESNOBOXLEARNMOVE, + B_SCR_OP_YESNOBOXSTOPLEARNINGMOVE, + B_SCR_OP_HITANIMATION, + B_SCR_OP_GETMONEYREWARD, + B_SCR_OP_UPDATEBATTLERMOVES, + B_SCR_OP_SWAPATTACKERWITHTARGET, + B_SCR_OP_INCREMENTGAMESTAT, + B_SCR_OP_DRAWPARTYSTATUSSUMMARY, + B_SCR_OP_HIDEPARTYSTATUSSUMMARY, + B_SCR_OP_JUMPTOCALLEDMOVE, + B_SCR_OP_STATUSANIMATION, + B_SCR_OP_STATUS2ANIMATION, + B_SCR_OP_CHOSENSTATUSANIMATION, + B_SCR_OP_YESNOBOX, + B_SCR_OP_CANCELALLACTIONS, + B_SCR_OP_ADJUSTSETDAMAGE, + B_SCR_OP_REMOVEITEM, + B_SCR_OP_ATKNAMEINBUFF1, + B_SCR_OP_DRAWLVLUPBOX, + B_SCR_OP_RESETSENTMONSVALUE, + B_SCR_OP_SETATKTOPLAYER0, + B_SCR_OP_MAKEVISIBLE, + B_SCR_OP_RECORDLASTABILITY, + B_SCR_OP_BUFFERMOVETOLEARN, + B_SCR_OP_JUMPIFPLAYERRAN, + B_SCR_OP_HPTHRESHOLDS, + B_SCR_OP_HPTHRESHOLDS2, + B_SCR_OP_USEITEMONOPPONENT, + B_SCR_OP_VARIOUS, + B_SCR_OP_SETPROTECTLIKE, + B_SCR_OP_TRYEXPLOSION, + B_SCR_OP_SETATKHPTOZERO, + B_SCR_OP_JUMPIFNEXTTARGETVALID, + B_SCR_OP_TRYHEALHALFHEALTH, + B_SCR_OP_TRYMIRRORMOVE, + B_SCR_OP_SETRAIN, + B_SCR_OP_SETREFLECT, + B_SCR_OP_SETSEEDED, + B_SCR_OP_MANIPULATEDAMAGE, + B_SCR_OP_TRYSETREST, + B_SCR_OP_JUMPIFNOTFIRSTTURN, + B_SCR_OP_NOP, + B_SCR_OP_JUMPIFCANTMAKEASLEEP, + B_SCR_OP_STOCKPILE, + B_SCR_OP_STOCKPILETOBASEDAMAGE, + B_SCR_OP_STOCKPILETOHPHEAL, + B_SCR_OP_NEGATIVEDAMAGE, + B_SCR_OP_STATBUFFCHANGE, + B_SCR_OP_NORMALISEBUFFS, + B_SCR_OP_SETBIDE, + B_SCR_OP_CONFUSEIFREPEATINGATTACKENDS, + B_SCR_OP_SETMULTIHITCOUNTER, + B_SCR_OP_INITMULTIHITSTRING, + B_SCR_OP_FORCERANDOMSWITCH, + B_SCR_OP_TRYCONVERSIONTYPECHANGE, + B_SCR_OP_GIVEPAYDAYMONEY, + B_SCR_OP_SETLIGHTSCREEN, + B_SCR_OP_TRYKO, + B_SCR_OP_DAMAGETOHALFTARGETHP, + B_SCR_OP_SETSANDSTORM, + B_SCR_OP_WEATHERDAMAGE, + B_SCR_OP_TRYINFATUATING, + B_SCR_OP_UPDATESTATUSICON, + B_SCR_OP_SETMIST, + B_SCR_OP_SETFOCUSENERGY, + B_SCR_OP_TRANSFORMDATAEXECUTION, + B_SCR_OP_SETSUBSTITUTE, + B_SCR_OP_MIMICATTACKCOPY, + B_SCR_OP_METRONOME, + B_SCR_OP_DMGTOLEVEL, + B_SCR_OP_PSYWAVEDAMAGEEFFECT, + B_SCR_OP_COUNTERDAMAGECALCULATOR, + B_SCR_OP_MIRRORCOATDAMAGECALCULATOR, + B_SCR_OP_DISABLELASTUSEDATTACK, + B_SCR_OP_TRYSETENCORE, + B_SCR_OP_PAINSPLITDMGCALC, + B_SCR_OP_SETTYPETORANDOMRESISTANCE, + B_SCR_OP_SETALWAYSHITFLAG, + B_SCR_OP_COPYMOVEPERMANENTLY, + B_SCR_OP_TRYCHOOSESLEEPTALKMOVE, + B_SCR_OP_SETDESTINYBOND, + B_SCR_OP_TRYSETDESTINYBONDTOHAPPEN, + B_SCR_OP_REMAININGHPTOPOWER, + B_SCR_OP_TRYSPITEPPREDUCE, + B_SCR_OP_HEALPARTYSTATUS, + B_SCR_OP_CURSETARGET, + B_SCR_OP_TRYSETSPIKES, + B_SCR_OP_SETFORESIGHT, + B_SCR_OP_TRYSETPERISHSONG, + B_SCR_OP_ROLLOUTDAMAGECALCULATION, + B_SCR_OP_JUMPIFCONFUSEDANDSTATMAXED, + B_SCR_OP_FURYCUTTERCALC, + B_SCR_OP_FRIENDSHIPTODAMAGECALCULATION, + B_SCR_OP_PRESENTDAMAGECALCULATION, + B_SCR_OP_SETSAFEGUARD, + B_SCR_OP_MAGNITUDEDAMAGECALCULATION, + B_SCR_OP_JUMPIFNOPURSUITSWITCHDMG, + B_SCR_OP_SETSUNNY, + B_SCR_OP_MAXATTACKHALVEHP, + B_SCR_OP_COPYFOESTATS, + B_SCR_OP_RAPIDSPINFREE, + B_SCR_OP_SETDEFENSECURLBIT, + B_SCR_OP_RECOVERBASEDONSUNLIGHT, + B_SCR_OP_HIDDENPOWERCALC, + B_SCR_OP_SELECTFIRSTVALIDTARGET, + B_SCR_OP_TRYSETFUTUREATTACK, + B_SCR_OP_TRYDOBEATUP, + B_SCR_OP_SETSEMIINVULNERABLEBIT, + B_SCR_OP_CLEARSEMIINVULNERABLEBIT, + B_SCR_OP_SETMINIMIZE, + B_SCR_OP_SETHAIL, + B_SCR_OP_TRYMEMENTO, + B_SCR_OP_SETFORCEDTARGET, + B_SCR_OP_SETCHARGE, + B_SCR_OP_CALLENVIRONMENTATTACK, + B_SCR_OP_CUREIFBURNEDPARALYZEDORPOISONED, + B_SCR_OP_SETTORMENT, + B_SCR_OP_JUMPIFNODAMAGE, + B_SCR_OP_SETTAUNT, + B_SCR_OP_TRYSETHELPINGHAND, + B_SCR_OP_TRYSWAPITEMS, + B_SCR_OP_TRYCOPYABILITY, + B_SCR_OP_TRYWISH, + B_SCR_OP_TRYSETROOTS, + B_SCR_OP_DOUBLEDAMAGEDEALTIFDAMAGED, + B_SCR_OP_SETYAWN, + B_SCR_OP_SETDAMAGETOHEALTHDIFFERENCE, + B_SCR_OP_SCALEDAMAGEBYHEALTHRATIO, + B_SCR_OP_TRYSWAPABILITIES, + B_SCR_OP_TRYIMPRISON, + B_SCR_OP_TRYSETGRUDGE, + B_SCR_OP_WEIGHTDAMAGECALCULATION, + B_SCR_OP_ASSISTATTACKSELECT, + B_SCR_OP_TRYSETMAGICCOAT, + B_SCR_OP_TRYSETSNATCH, + B_SCR_OP_TRYGETINTIMIDATETARGET, + B_SCR_OP_SWITCHOUTABILITIES, + B_SCR_OP_JUMPIFHASNOHP, + B_SCR_OP_GETSECRETPOWEREFFECT, + B_SCR_OP_PICKUP, + B_SCR_OP_DOCASTFORMCHANGEANIMATION, + B_SCR_OP_TRYCASTFORMDATACHANGE, + B_SCR_OP_SETTYPEBASEDHALVERS, + B_SCR_OP_SETWEATHERBALLTYPE, + B_SCR_OP_TRYRECYCLEITEM, + B_SCR_OP_SETTYPETOENVIRONMENT, + B_SCR_OP_PURSUITDOUBLES, + B_SCR_OP_SNATCHSETBATTLERS, + B_SCR_OP_REMOVELIGHTSCREENREFLECT, + B_SCR_OP_HANDLEBALLTHROW, + B_SCR_OP_GIVECAUGHTMON, + B_SCR_OP_TRYSETCAUGHTMONDEXFLAGS, + B_SCR_OP_DISPLAYDEXINFO, + B_SCR_OP_TRYGIVECAUGHTMONNICK, + B_SCR_OP_SUBATTACKERHPBYDMG, + B_SCR_OP_REMOVEATTACKERSTATUS1, + B_SCR_OP_FINISHACTION, + B_SCR_OP_FINISHTURN, + B_SCR_OP_TRAINERSLIDEOUT, +}; + // The following correspond to the struct members of BattleScripting by adding their offset #define sPAINSPLIT_HP (gBattleScripting + 0x00) // painSplitHp #define sBIDE_DMG (gBattleScripting + 0x04) // bideDmg diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index d5013b0239..bcaca27056 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -328,255 +328,255 @@ static void Cmd_trainerslideout(void); void (*const gBattleScriptingCommandsTable[])(void) = { - Cmd_attackcanceler, //0x0 - Cmd_accuracycheck, //0x1 - Cmd_attackstring, //0x2 - Cmd_ppreduce, //0x3 - Cmd_critcalc, //0x4 - Cmd_damagecalc, //0x5 - Cmd_typecalc, //0x6 - Cmd_adjustnormaldamage, //0x7 - Cmd_adjustnormaldamage2, //0x8 - Cmd_attackanimation, //0x9 - Cmd_waitanimation, //0xA - Cmd_healthbarupdate, //0xB - Cmd_datahpupdate, //0xC - Cmd_critmessage, //0xD - Cmd_effectivenesssound, //0xE - Cmd_resultmessage, //0xF - Cmd_printstring, //0x10 - Cmd_printselectionstring, //0x11 - Cmd_waitmessage, //0x12 - Cmd_printfromtable, //0x13 - Cmd_printselectionstringfromtable, //0x14 - Cmd_seteffectwithchance, //0x15 - Cmd_seteffectprimary, //0x16 - Cmd_seteffectsecondary, //0x17 - Cmd_clearstatusfromeffect, //0x18 - Cmd_tryfaintmon, //0x19 - Cmd_dofaintanimation, //0x1A - Cmd_cleareffectsonfaint, //0x1B - Cmd_jumpifstatus, //0x1C - Cmd_jumpifstatus2, //0x1D - Cmd_jumpifability, //0x1E - Cmd_jumpifsideaffecting, //0x1F - Cmd_jumpifstat, //0x20 - Cmd_jumpifstatus3condition, //0x21 - Cmd_jumpiftype, //0x22 - Cmd_getexp, //0x23 - Cmd_checkteamslost, //0x24 - Cmd_movevaluescleanup, //0x25 - Cmd_setmultihit, //0x26 - Cmd_decrementmultihit, //0x27 - Cmd_goto, //0x28 - Cmd_jumpifbyte, //0x29 - Cmd_jumpifhalfword, //0x2A - Cmd_jumpifword, //0x2B - Cmd_jumpifarrayequal, //0x2C - Cmd_jumpifarraynotequal, //0x2D - Cmd_setbyte, //0x2E - Cmd_addbyte, //0x2F - Cmd_subbyte, //0x30 - Cmd_copyarray, //0x31 - Cmd_copyarraywithindex, //0x32 - Cmd_orbyte, //0x33 - Cmd_orhalfword, //0x34 - Cmd_orword, //0x35 - Cmd_bicbyte, //0x36 - Cmd_bichalfword, //0x37 - Cmd_bicword, //0x38 - Cmd_pause, //0x39 - Cmd_waitstate, //0x3A - Cmd_healthbar_update, //0x3B - Cmd_return, //0x3C - Cmd_end, //0x3D - Cmd_end2, //0x3E - Cmd_end3, //0x3F - Cmd_jumpifaffectedbyprotect, //0x40 - Cmd_call, //0x41 - Cmd_jumpiftype2, //0x42 - Cmd_jumpifabilitypresent, //0x43 - Cmd_endselectionscript, //0x44 - Cmd_playanimation, //0x45 - Cmd_playanimation_var, //0x46 - Cmd_setgraphicalstatchangevalues, //0x47 - Cmd_playstatchangeanimation, //0x48 - Cmd_moveend, //0x49 - Cmd_typecalc2, //0x4A - Cmd_returnatktoball, //0x4B - Cmd_getswitchedmondata, //0x4C - Cmd_switchindataupdate, //0x4D - Cmd_switchinanim, //0x4E - Cmd_jumpifcantswitch, //0x4F - Cmd_openpartyscreen, //0x50 - Cmd_switchhandleorder, //0x51 - Cmd_switchineffects, //0x52 - Cmd_trainerslidein, //0x53 - Cmd_playse, //0x54 - Cmd_fanfare, //0x55 - Cmd_playfaintcry, //0x56 - Cmd_endlinkbattle, //0x57 - Cmd_returntoball, //0x58 - Cmd_handlelearnnewmove, //0x59 - Cmd_yesnoboxlearnmove, //0x5A - Cmd_yesnoboxstoplearningmove, //0x5B - Cmd_hitanimation, //0x5C - Cmd_getmoneyreward, //0x5D - Cmd_updatebattlermoves, //0x5E - Cmd_swapattackerwithtarget, //0x5F - Cmd_incrementgamestat, //0x60 - Cmd_drawpartystatussummary, //0x61 - Cmd_hidepartystatussummary, //0x62 - Cmd_jumptocalledmove, //0x63 - Cmd_statusanimation, //0x64 - Cmd_status2animation, //0x65 - Cmd_chosenstatusanimation, //0x66 - Cmd_yesnobox, //0x67 - Cmd_cancelallactions, //0x68 - Cmd_adjustsetdamage, //0x69 - Cmd_removeitem, //0x6A - Cmd_atknameinbuff1, //0x6B - Cmd_drawlvlupbox, //0x6C - Cmd_resetsentmonsvalue, //0x6D - Cmd_setatktoplayer0, //0x6E - Cmd_makevisible, //0x6F - Cmd_recordlastability, //0x70 - Cmd_buffermovetolearn, //0x71 - Cmd_jumpifplayerran, //0x72 - Cmd_hpthresholds, //0x73 - Cmd_hpthresholds2, //0x74 - Cmd_useitemonopponent, //0x75 - Cmd_various, //0x76 - Cmd_setprotectlike, //0x77 - Cmd_tryexplosion, //0x78 - Cmd_setatkhptozero, //0x79 - Cmd_jumpifnexttargetvalid, //0x7A - Cmd_tryhealhalfhealth, //0x7B - Cmd_trymirrormove, //0x7C - Cmd_setrain, //0x7D - Cmd_setreflect, //0x7E - Cmd_setseeded, //0x7F - Cmd_manipulatedamage, //0x80 - Cmd_trysetrest, //0x81 - Cmd_jumpifnotfirstturn, //0x82 - Cmd_nop, //0x83 - Cmd_jumpifcantmakeasleep, //0x84 - Cmd_stockpile, //0x85 - Cmd_stockpiletobasedamage, //0x86 - Cmd_stockpiletohpheal, //0x87 - Cmd_negativedamage, //0x88 - Cmd_statbuffchange, //0x89 - Cmd_normalisebuffs, //0x8A - Cmd_setbide, //0x8B - Cmd_confuseifrepeatingattackends, //0x8C - Cmd_setmultihitcounter, //0x8D - Cmd_initmultihitstring, //0x8E - Cmd_forcerandomswitch, //0x8F - Cmd_tryconversiontypechange, //0x90 - Cmd_givepaydaymoney, //0x91 - Cmd_setlightscreen, //0x92 - Cmd_tryKO, //0x93 - Cmd_damagetohalftargethp, //0x94 - Cmd_setsandstorm, //0x95 - Cmd_weatherdamage, //0x96 - Cmd_tryinfatuating, //0x97 - Cmd_updatestatusicon, //0x98 - Cmd_setmist, //0x99 - Cmd_setfocusenergy, //0x9A - Cmd_transformdataexecution, //0x9B - Cmd_setsubstitute, //0x9C - Cmd_mimicattackcopy, //0x9D - Cmd_metronome, //0x9E - Cmd_dmgtolevel, //0x9F - Cmd_psywavedamageeffect, //0xA0 - Cmd_counterdamagecalculator, //0xA1 - Cmd_mirrorcoatdamagecalculator, //0xA2 - Cmd_disablelastusedattack, //0xA3 - Cmd_trysetencore, //0xA4 - Cmd_painsplitdmgcalc, //0xA5 - Cmd_settypetorandomresistance, //0xA6 - Cmd_setalwayshitflag, //0xA7 - Cmd_copymovepermanently, //0xA8 - Cmd_trychoosesleeptalkmove, //0xA9 - Cmd_setdestinybond, //0xAA - Cmd_trysetdestinybondtohappen, //0xAB - Cmd_remaininghptopower, //0xAC - Cmd_tryspiteppreduce, //0xAD - Cmd_healpartystatus, //0xAE - Cmd_cursetarget, //0xAF - Cmd_trysetspikes, //0xB0 - Cmd_setforesight, //0xB1 - Cmd_trysetperishsong, //0xB2 - Cmd_rolloutdamagecalculation, //0xB3 - Cmd_jumpifconfusedandstatmaxed, //0xB4 - Cmd_furycuttercalc, //0xB5 - Cmd_friendshiptodamagecalculation, //0xB6 - Cmd_presentdamagecalculation, //0xB7 - Cmd_setsafeguard, //0xB8 - Cmd_magnitudedamagecalculation, //0xB9 - Cmd_jumpifnopursuitswitchdmg, //0xBA - Cmd_setsunny, //0xBB - Cmd_maxattackhalvehp, //0xBC - Cmd_copyfoestats, //0xBD - Cmd_rapidspinfree, //0xBE - Cmd_setdefensecurlbit, //0xBF - Cmd_recoverbasedonsunlight, //0xC0 - Cmd_hiddenpowercalc, //0xC1 - Cmd_selectfirstvalidtarget, //0xC2 - Cmd_trysetfutureattack, //0xC3 - Cmd_trydobeatup, //0xC4 - Cmd_setsemiinvulnerablebit, //0xC5 - Cmd_clearsemiinvulnerablebit, //0xC6 - Cmd_setminimize, //0xC7 - Cmd_sethail, //0xC8 - Cmd_trymemento, //0xC9 - Cmd_setforcedtarget, //0xCA - Cmd_setcharge, //0xCB - Cmd_callenvironmentattack, //0xCC - Cmd_cureifburnedparalyzedorpoisoned, //0xCD - Cmd_settorment, //0xCE - Cmd_jumpifnodamage, //0xCF - Cmd_settaunt, //0xD0 - Cmd_trysethelpinghand, //0xD1 - Cmd_tryswapitems, //0xD2 - Cmd_trycopyability, //0xD3 - Cmd_trywish, //0xD4 - Cmd_trysetroots, //0xD5 - Cmd_doubledamagedealtifdamaged, //0xD6 - Cmd_setyawn, //0xD7 - Cmd_setdamagetohealthdifference, //0xD8 - Cmd_scaledamagebyhealthratio, //0xD9 - Cmd_tryswapabilities, //0xDA - Cmd_tryimprison, //0xDB - Cmd_trysetgrudge, //0xDC - Cmd_weightdamagecalculation, //0xDD - Cmd_assistattackselect, //0xDE - Cmd_trysetmagiccoat, //0xDF - Cmd_trysetsnatch, //0xE0 - Cmd_trygetintimidatetarget, //0xE1 - Cmd_switchoutabilities, //0xE2 - Cmd_jumpifhasnohp, //0xE3 - Cmd_getsecretpowereffect, //0xE4 - Cmd_pickup, //0xE5 - Cmd_docastformchangeanimation, //0xE6 - Cmd_trycastformdatachange, //0xE7 - Cmd_settypebasedhalvers, //0xE8 - Cmd_setweatherballtype, //0xE9 - Cmd_tryrecycleitem, //0xEA - Cmd_settypetoenvironment, //0xEB - Cmd_pursuitdoubles, //0xEC - Cmd_snatchsetbattlers, //0xED - Cmd_removelightscreenreflect, //0xEE - Cmd_handleballthrow, //0xEF - Cmd_givecaughtmon, //0xF0 - Cmd_trysetcaughtmondexflags, //0xF1 - Cmd_displaydexinfo, //0xF2 - Cmd_trygivecaughtmonnick, //0xF3 - Cmd_subattackerhpbydmg, //0xF4 - Cmd_removeattackerstatus1, //0xF5 - Cmd_finishaction, //0xF6 - Cmd_finishturn, //0xF7 - Cmd_trainerslideout //0xF8 + [B_SCR_OP_ATTACKCANCELER] = Cmd_attackcanceler, //0x0 + [B_SCR_OP_ACCURACYCHECK] = Cmd_accuracycheck, //0x1 + [B_SCR_OP_ATTACKSTRING] = Cmd_attackstring, //0x2 + [B_SCR_OP_PPREDUCE] = Cmd_ppreduce, //0x3 + [B_SCR_OP_CRITCALC] = Cmd_critcalc, //0x4 + [B_SCR_OP_DAMAGECALC] = Cmd_damagecalc, //0x5 + [B_SCR_OP_TYPECALC] = Cmd_typecalc, //0x6 + [B_SCR_OP_ADJUSTNORMALDAMAGE] = Cmd_adjustnormaldamage, //0x7 + [B_SCR_OP_ADJUSTNORMALDAMAGE2] = Cmd_adjustnormaldamage2, //0x8 + [B_SCR_OP_ATTACKANIMATION] = Cmd_attackanimation, //0x9 + [B_SCR_OP_WAITANIMATION] = Cmd_waitanimation, //0xA + [B_SCR_OP_HEALTHBARUPDATE] = Cmd_healthbarupdate, //0xB + [B_SCR_OP_DATAHPUPDATE] = Cmd_datahpupdate, //0xC + [B_SCR_OP_CRITMESSAGE] = Cmd_critmessage, //0xD + [B_SCR_OP_EFFECTIVENESSSOUND] = Cmd_effectivenesssound, //0xE + [B_SCR_OP_RESULTMESSAGE] = Cmd_resultmessage, //0xF + [B_SCR_OP_PRINTSTRING] = Cmd_printstring, //0x10 + [B_SCR_OP_PRINTSELECTIONSTRING] = Cmd_printselectionstring, //0x11 + [B_SCR_OP_WAITMESSAGE] = Cmd_waitmessage, //0x12 + [B_SCR_OP_PRINTFROMTABLE] = Cmd_printfromtable, //0x13 + [B_SCR_OP_PRINTSELECTIONSTRINGFROMTABLE] = Cmd_printselectionstringfromtable, //0x14 + [B_SCR_OP_SETEFFECTWITHCHANCE] = Cmd_seteffectwithchance, //0x15 + [B_SCR_OP_SETEFFECTPRIMARY] = Cmd_seteffectprimary, //0x16 + [B_SCR_OP_SETEFFECTSECONDARY] = Cmd_seteffectsecondary, //0x17 + [B_SCR_OP_CLEARSTATUSFROMEFFECT] = Cmd_clearstatusfromeffect, //0x18 + [B_SCR_OP_TRYFAINTMON] = Cmd_tryfaintmon, //0x19 + [B_SCR_OP_DOFAINTANIMATION] = Cmd_dofaintanimation, //0x1A + [B_SCR_OP_CLEAREFFECTSONFAINT] = Cmd_cleareffectsonfaint, //0x1B + [B_SCR_OP_JUMPIFSTATUS] = Cmd_jumpifstatus, //0x1C + [B_SCR_OP_JUMPIFSTATUS2] = Cmd_jumpifstatus2, //0x1D + [B_SCR_OP_JUMPIFABILITY] = Cmd_jumpifability, //0x1E + [B_SCR_OP_JUMPIFSIDEAFFECTING] = Cmd_jumpifsideaffecting, //0x1F + [B_SCR_OP_JUMPIFSTAT] = Cmd_jumpifstat, //0x20 + [B_SCR_OP_JUMPIFSTATUS3CONDITION] = Cmd_jumpifstatus3condition, //0x21 + [B_SCR_OP_JUMPIFTYPE] = Cmd_jumpiftype, //0x22 + [B_SCR_OP_GETEXP] = Cmd_getexp, //0x23 + [B_SCR_OP_CHECKTEAMSLOST] = Cmd_checkteamslost, //0x24 + [B_SCR_OP_MOVEVALUESCLEANUP] = Cmd_movevaluescleanup, //0x25 + [B_SCR_OP_SETMULTIHIT] = Cmd_setmultihit, //0x26 + [B_SCR_OP_DECREMENTMULTIHIT] = Cmd_decrementmultihit, //0x27 + [B_SCR_OP_GOTO] = Cmd_goto, //0x28 + [B_SCR_OP_JUMPIFBYTE] = Cmd_jumpifbyte, //0x29 + [B_SCR_OP_JUMPIFHALFWORD] = Cmd_jumpifhalfword, //0x2A + [B_SCR_OP_JUMPIFWORD] = Cmd_jumpifword, //0x2B + [B_SCR_OP_JUMPIFARRAYEQUAL] = Cmd_jumpifarrayequal, //0x2C + [B_SCR_OP_JUMPIFARRAYNOTEQUAL] = Cmd_jumpifarraynotequal, //0x2D + [B_SCR_OP_SETBYTE] = Cmd_setbyte, //0x2E + [B_SCR_OP_ADDBYTE] = Cmd_addbyte, //0x2F + [B_SCR_OP_SUBBYTE] = Cmd_subbyte, //0x30 + [B_SCR_OP_COPYARRAY] = Cmd_copyarray, //0x31 + [B_SCR_OP_COPYARRAYWITHINDEX] = Cmd_copyarraywithindex, //0x32 + [B_SCR_OP_ORBYTE] = Cmd_orbyte, //0x33 + [B_SCR_OP_ORHALFWORD] = Cmd_orhalfword, //0x34 + [B_SCR_OP_ORWORD] = Cmd_orword, //0x35 + [B_SCR_OP_BICBYTE] = Cmd_bicbyte, //0x36 + [B_SCR_OP_BICHALFWORD] = Cmd_bichalfword, //0x37 + [B_SCR_OP_BICWORD] = Cmd_bicword, //0x38 + [B_SCR_OP_PAUSE] = Cmd_pause, //0x39 + [B_SCR_OP_WAITSTATE] = Cmd_waitstate, //0x3A + [B_SCR_OP_HEALTHBAR_UPDATE] = Cmd_healthbar_update, //0x3B + [B_SCR_OP_RETURN] = Cmd_return, //0x3C + [B_SCR_OP_END] = Cmd_end, //0x3D + [B_SCR_OP_END2] = Cmd_end2, //0x3E + [B_SCR_OP_END3] = Cmd_end3, //0x3F + [B_SCR_OP_JUMPIFAFFECTEDBYPROTECT] = Cmd_jumpifaffectedbyprotect, //0x40 + [B_SCR_OP_CALL] = Cmd_call, //0x41 + [B_SCR_OP_JUMPIFTYPE2] = Cmd_jumpiftype2, //0x42 + [B_SCR_OP_JUMPIFABILITYPRESENT] = Cmd_jumpifabilitypresent, //0x43 + [B_SCR_OP_ENDSELECTIONSCRIPT] = Cmd_endselectionscript, //0x44 + [B_SCR_OP_PLAYANIMATION] = Cmd_playanimation, //0x45 + [B_SCR_OP_PLAYANIMATION_VAR] = Cmd_playanimation_var, //0x46 + [B_SCR_OP_SETGRAPHICALSTATCHANGEVALUES] = Cmd_setgraphicalstatchangevalues, //0x47 + [B_SCR_OP_PLAYSTATCHANGEANIMATION] = Cmd_playstatchangeanimation, //0x48 + [B_SCR_OP_MOVEEND] = Cmd_moveend, //0x49 + [B_SCR_OP_TYPECALC2] = Cmd_typecalc2, //0x4A + [B_SCR_OP_RETURNATKTOBALL] = Cmd_returnatktoball, //0x4B + [B_SCR_OP_GETSWITCHEDMONDATA] = Cmd_getswitchedmondata, //0x4C + [B_SCR_OP_SWITCHINDATAUPDATE] = Cmd_switchindataupdate, //0x4D + [B_SCR_OP_SWITCHINANIM] = Cmd_switchinanim, //0x4E + [B_SCR_OP_JUMPIFCANTSWITCH] = Cmd_jumpifcantswitch, //0x4F + [B_SCR_OP_OPENPARTYSCREEN] = Cmd_openpartyscreen, //0x50 + [B_SCR_OP_SWITCHHANDLEORDER] = Cmd_switchhandleorder, //0x51 + [B_SCR_OP_SWITCHINEFFECTS] = Cmd_switchineffects, //0x52 + [B_SCR_OP_TRAINERSLIDEIN] = Cmd_trainerslidein, //0x53 + [B_SCR_OP_PLAYSE] = Cmd_playse, //0x54 + [B_SCR_OP_FANFARE] = Cmd_fanfare, //0x55 + [B_SCR_OP_PLAYFAINTCRY] = Cmd_playfaintcry, //0x56 + [B_SCR_OP_ENDLINKBATTLE] = Cmd_endlinkbattle, //0x57 + [B_SCR_OP_RETURNTOBALL] = Cmd_returntoball, //0x58 + [B_SCR_OP_HANDLELEARNNEWMOVE] = Cmd_handlelearnnewmove, //0x59 + [B_SCR_OP_YESNOBOXLEARNMOVE] = Cmd_yesnoboxlearnmove, //0x5A + [B_SCR_OP_YESNOBOXSTOPLEARNINGMOVE] = Cmd_yesnoboxstoplearningmove, //0x5B + [B_SCR_OP_HITANIMATION] = Cmd_hitanimation, //0x5C + [B_SCR_OP_GETMONEYREWARD] = Cmd_getmoneyreward, //0x5D + [B_SCR_OP_UPDATEBATTLERMOVES] = Cmd_updatebattlermoves, //0x5E + [B_SCR_OP_SWAPATTACKERWITHTARGET] = Cmd_swapattackerwithtarget, //0x5F + [B_SCR_OP_INCREMENTGAMESTAT] = Cmd_incrementgamestat, //0x60 + [B_SCR_OP_DRAWPARTYSTATUSSUMMARY] = Cmd_drawpartystatussummary, //0x61 + [B_SCR_OP_HIDEPARTYSTATUSSUMMARY] = Cmd_hidepartystatussummary, //0x62 + [B_SCR_OP_JUMPTOCALLEDMOVE] = Cmd_jumptocalledmove, //0x63 + [B_SCR_OP_STATUSANIMATION] = Cmd_statusanimation, //0x64 + [B_SCR_OP_STATUS2ANIMATION] = Cmd_status2animation, //0x65 + [B_SCR_OP_CHOSENSTATUSANIMATION] = Cmd_chosenstatusanimation, //0x66 + [B_SCR_OP_YESNOBOX] = Cmd_yesnobox, //0x67 + [B_SCR_OP_CANCELALLACTIONS] = Cmd_cancelallactions, //0x68 + [B_SCR_OP_ADJUSTSETDAMAGE] = Cmd_adjustsetdamage, //0x69 + [B_SCR_OP_REMOVEITEM] = Cmd_removeitem, //0x6A + [B_SCR_OP_ATKNAMEINBUFF1] = Cmd_atknameinbuff1, //0x6B + [B_SCR_OP_DRAWLVLUPBOX] = Cmd_drawlvlupbox, //0x6C + [B_SCR_OP_RESETSENTMONSVALUE] = Cmd_resetsentmonsvalue, //0x6D + [B_SCR_OP_SETATKTOPLAYER0] = Cmd_setatktoplayer0, //0x6E + [B_SCR_OP_MAKEVISIBLE] = Cmd_makevisible, //0x6F + [B_SCR_OP_RECORDLASTABILITY] = Cmd_recordlastability, //0x70 + [B_SCR_OP_BUFFERMOVETOLEARN] = Cmd_buffermovetolearn, //0x71 + [B_SCR_OP_JUMPIFPLAYERRAN] = Cmd_jumpifplayerran, //0x72 + [B_SCR_OP_HPTHRESHOLDS] = Cmd_hpthresholds, //0x73 + [B_SCR_OP_HPTHRESHOLDS2] = Cmd_hpthresholds2, //0x74 + [B_SCR_OP_USEITEMONOPPONENT] = Cmd_useitemonopponent, //0x75 + [B_SCR_OP_VARIOUS] = Cmd_various, //0x76 + [B_SCR_OP_SETPROTECTLIKE] = Cmd_setprotectlike, //0x77 + [B_SCR_OP_TRYEXPLOSION] = Cmd_tryexplosion, //0x78 + [B_SCR_OP_SETATKHPTOZERO] = Cmd_setatkhptozero, //0x79 + [B_SCR_OP_JUMPIFNEXTTARGETVALID] = Cmd_jumpifnexttargetvalid, //0x7A + [B_SCR_OP_TRYHEALHALFHEALTH] = Cmd_tryhealhalfhealth, //0x7B + [B_SCR_OP_TRYMIRRORMOVE] = Cmd_trymirrormove, //0x7C + [B_SCR_OP_SETRAIN] = Cmd_setrain, //0x7D + [B_SCR_OP_SETREFLECT] = Cmd_setreflect, //0x7E + [B_SCR_OP_SETSEEDED] = Cmd_setseeded, //0x7F + [B_SCR_OP_MANIPULATEDAMAGE] = Cmd_manipulatedamage, //0x80 + [B_SCR_OP_TRYSETREST] = Cmd_trysetrest, //0x81 + [B_SCR_OP_JUMPIFNOTFIRSTTURN] = Cmd_jumpifnotfirstturn, //0x82 + [B_SCR_OP_NOP] = Cmd_nop, //0x83 + [B_SCR_OP_JUMPIFCANTMAKEASLEEP] = Cmd_jumpifcantmakeasleep, //0x84 + [B_SCR_OP_STOCKPILE] = Cmd_stockpile, //0x85 + [B_SCR_OP_STOCKPILETOBASEDAMAGE] = Cmd_stockpiletobasedamage, //0x86 + [B_SCR_OP_STOCKPILETOHPHEAL] = Cmd_stockpiletohpheal, //0x87 + [B_SCR_OP_NEGATIVEDAMAGE] = Cmd_negativedamage, //0x88 + [B_SCR_OP_STATBUFFCHANGE] = Cmd_statbuffchange, //0x89 + [B_SCR_OP_NORMALISEBUFFS] = Cmd_normalisebuffs, //0x8A + [B_SCR_OP_SETBIDE] = Cmd_setbide, //0x8B + [B_SCR_OP_CONFUSEIFREPEATINGATTACKENDS] = Cmd_confuseifrepeatingattackends, //0x8C + [B_SCR_OP_SETMULTIHITCOUNTER] = Cmd_setmultihitcounter, //0x8D + [B_SCR_OP_INITMULTIHITSTRING] = Cmd_initmultihitstring, //0x8E + [B_SCR_OP_FORCERANDOMSWITCH] = Cmd_forcerandomswitch, //0x8F + [B_SCR_OP_TRYCONVERSIONTYPECHANGE] = Cmd_tryconversiontypechange, //0x90 + [B_SCR_OP_GIVEPAYDAYMONEY] = Cmd_givepaydaymoney, //0x91 + [B_SCR_OP_SETLIGHTSCREEN] = Cmd_setlightscreen, //0x92 + [B_SCR_OP_TRYKO] = Cmd_tryKO, //0x93 + [B_SCR_OP_DAMAGETOHALFTARGETHP] = Cmd_damagetohalftargethp, //0x94 + [B_SCR_OP_SETSANDSTORM] = Cmd_setsandstorm, //0x95 + [B_SCR_OP_WEATHERDAMAGE] = Cmd_weatherdamage, //0x96 + [B_SCR_OP_TRYINFATUATING] = Cmd_tryinfatuating, //0x97 + [B_SCR_OP_UPDATESTATUSICON] = Cmd_updatestatusicon, //0x98 + [B_SCR_OP_SETMIST] = Cmd_setmist, //0x99 + [B_SCR_OP_SETFOCUSENERGY] = Cmd_setfocusenergy, //0x9A + [B_SCR_OP_TRANSFORMDATAEXECUTION] = Cmd_transformdataexecution, //0x9B + [B_SCR_OP_SETSUBSTITUTE] = Cmd_setsubstitute, //0x9C + [B_SCR_OP_MIMICATTACKCOPY] = Cmd_mimicattackcopy, //0x9D + [B_SCR_OP_METRONOME] = Cmd_metronome, //0x9E + [B_SCR_OP_DMGTOLEVEL] = Cmd_dmgtolevel, //0x9F + [B_SCR_OP_PSYWAVEDAMAGEEFFECT] = Cmd_psywavedamageeffect, //0xA0 + [B_SCR_OP_COUNTERDAMAGECALCULATOR] = Cmd_counterdamagecalculator, //0xA1 + [B_SCR_OP_MIRRORCOATDAMAGECALCULATOR] = Cmd_mirrorcoatdamagecalculator, //0xA2 + [B_SCR_OP_DISABLELASTUSEDATTACK] = Cmd_disablelastusedattack, //0xA3 + [B_SCR_OP_TRYSETENCORE] = Cmd_trysetencore, //0xA4 + [B_SCR_OP_PAINSPLITDMGCALC] = Cmd_painsplitdmgcalc, //0xA5 + [B_SCR_OP_SETTYPETORANDOMRESISTANCE] = Cmd_settypetorandomresistance, //0xA6 + [B_SCR_OP_SETALWAYSHITFLAG] = Cmd_setalwayshitflag, //0xA7 + [B_SCR_OP_COPYMOVEPERMANENTLY] = Cmd_copymovepermanently, //0xA8 + [B_SCR_OP_TRYCHOOSESLEEPTALKMOVE] = Cmd_trychoosesleeptalkmove, //0xA9 + [B_SCR_OP_SETDESTINYBOND] = Cmd_setdestinybond, //0xAA + [B_SCR_OP_TRYSETDESTINYBONDTOHAPPEN] = Cmd_trysetdestinybondtohappen, //0xAB + [B_SCR_OP_REMAININGHPTOPOWER] = Cmd_remaininghptopower, //0xAC + [B_SCR_OP_TRYSPITEPPREDUCE] = Cmd_tryspiteppreduce, //0xAD + [B_SCR_OP_HEALPARTYSTATUS] = Cmd_healpartystatus, //0xAE + [B_SCR_OP_CURSETARGET] = Cmd_cursetarget, //0xAF + [B_SCR_OP_TRYSETSPIKES] = Cmd_trysetspikes, //0xB0 + [B_SCR_OP_SETFORESIGHT] = Cmd_setforesight, //0xB1 + [B_SCR_OP_TRYSETPERISHSONG] = Cmd_trysetperishsong, //0xB2 + [B_SCR_OP_ROLLOUTDAMAGECALCULATION] = Cmd_rolloutdamagecalculation, //0xB3 + [B_SCR_OP_JUMPIFCONFUSEDANDSTATMAXED] = Cmd_jumpifconfusedandstatmaxed, //0xB4 + [B_SCR_OP_FURYCUTTERCALC] = Cmd_furycuttercalc, //0xB5 + [B_SCR_OP_FRIENDSHIPTODAMAGECALCULATION] = Cmd_friendshiptodamagecalculation, //0xB6 + [B_SCR_OP_PRESENTDAMAGECALCULATION] = Cmd_presentdamagecalculation, //0xB7 + [B_SCR_OP_SETSAFEGUARD] = Cmd_setsafeguard, //0xB8 + [B_SCR_OP_MAGNITUDEDAMAGECALCULATION] = Cmd_magnitudedamagecalculation, //0xB9 + [B_SCR_OP_JUMPIFNOPURSUITSWITCHDMG] = Cmd_jumpifnopursuitswitchdmg, //0xBA + [B_SCR_OP_SETSUNNY] = Cmd_setsunny, //0xBB + [B_SCR_OP_MAXATTACKHALVEHP] = Cmd_maxattackhalvehp, //0xBC + [B_SCR_OP_COPYFOESTATS] = Cmd_copyfoestats, //0xBD + [B_SCR_OP_RAPIDSPINFREE] = Cmd_rapidspinfree, //0xBE + [B_SCR_OP_SETDEFENSECURLBIT] = Cmd_setdefensecurlbit, //0xBF + [B_SCR_OP_RECOVERBASEDONSUNLIGHT] = Cmd_recoverbasedonsunlight, //0xC0 + [B_SCR_OP_HIDDENPOWERCALC] = Cmd_hiddenpowercalc, //0xC1 + [B_SCR_OP_SELECTFIRSTVALIDTARGET] = Cmd_selectfirstvalidtarget, //0xC2 + [B_SCR_OP_TRYSETFUTUREATTACK] = Cmd_trysetfutureattack, //0xC3 + [B_SCR_OP_TRYDOBEATUP] = Cmd_trydobeatup, //0xC4 + [B_SCR_OP_SETSEMIINVULNERABLEBIT] = Cmd_setsemiinvulnerablebit, //0xC5 + [B_SCR_OP_CLEARSEMIINVULNERABLEBIT] = Cmd_clearsemiinvulnerablebit, //0xC6 + [B_SCR_OP_SETMINIMIZE] = Cmd_setminimize, //0xC7 + [B_SCR_OP_SETHAIL] = Cmd_sethail, //0xC8 + [B_SCR_OP_TRYMEMENTO] = Cmd_trymemento, //0xC9 + [B_SCR_OP_SETFORCEDTARGET] = Cmd_setforcedtarget, //0xCA + [B_SCR_OP_SETCHARGE] = Cmd_setcharge, //0xCB + [B_SCR_OP_CALLENVIRONMENTATTACK] = Cmd_callenvironmentattack, //0xCC + [B_SCR_OP_CUREIFBURNEDPARALYZEDORPOISONED] = Cmd_cureifburnedparalyzedorpoisoned, //0xCD + [B_SCR_OP_SETTORMENT] = Cmd_settorment, //0xCE + [B_SCR_OP_JUMPIFNODAMAGE] = Cmd_jumpifnodamage, //0xCF + [B_SCR_OP_SETTAUNT] = Cmd_settaunt, //0xD0 + [B_SCR_OP_TRYSETHELPINGHAND] = Cmd_trysethelpinghand, //0xD1 + [B_SCR_OP_TRYSWAPITEMS] = Cmd_tryswapitems, //0xD2 + [B_SCR_OP_TRYCOPYABILITY] = Cmd_trycopyability, //0xD3 + [B_SCR_OP_TRYWISH] = Cmd_trywish, //0xD4 + [B_SCR_OP_TRYSETROOTS] = Cmd_trysetroots, //0xD5 + [B_SCR_OP_DOUBLEDAMAGEDEALTIFDAMAGED] = Cmd_doubledamagedealtifdamaged, //0xD6 + [B_SCR_OP_SETYAWN] = Cmd_setyawn, //0xD7 + [B_SCR_OP_SETDAMAGETOHEALTHDIFFERENCE] = Cmd_setdamagetohealthdifference, //0xD8 + [B_SCR_OP_SCALEDAMAGEBYHEALTHRATIO] = Cmd_scaledamagebyhealthratio, //0xD9 + [B_SCR_OP_TRYSWAPABILITIES] = Cmd_tryswapabilities, //0xDA + [B_SCR_OP_TRYIMPRISON] = Cmd_tryimprison, //0xDB + [B_SCR_OP_TRYSETGRUDGE] = Cmd_trysetgrudge, //0xDC + [B_SCR_OP_WEIGHTDAMAGECALCULATION] = Cmd_weightdamagecalculation, //0xDD + [B_SCR_OP_ASSISTATTACKSELECT] = Cmd_assistattackselect, //0xDE + [B_SCR_OP_TRYSETMAGICCOAT] = Cmd_trysetmagiccoat, //0xDF + [B_SCR_OP_TRYSETSNATCH] = Cmd_trysetsnatch, //0xE0 + [B_SCR_OP_TRYGETINTIMIDATETARGET] = Cmd_trygetintimidatetarget, //0xE1 + [B_SCR_OP_SWITCHOUTABILITIES] = Cmd_switchoutabilities, //0xE2 + [B_SCR_OP_JUMPIFHASNOHP] = Cmd_jumpifhasnohp, //0xE3 + [B_SCR_OP_GETSECRETPOWEREFFECT] = Cmd_getsecretpowereffect, //0xE4 + [B_SCR_OP_PICKUP] = Cmd_pickup, //0xE5 + [B_SCR_OP_DOCASTFORMCHANGEANIMATION] = Cmd_docastformchangeanimation, //0xE6 + [B_SCR_OP_TRYCASTFORMDATACHANGE] = Cmd_trycastformdatachange, //0xE7 + [B_SCR_OP_SETTYPEBASEDHALVERS] = Cmd_settypebasedhalvers, //0xE8 + [B_SCR_OP_SETWEATHERBALLTYPE] = Cmd_setweatherballtype, //0xE9 + [B_SCR_OP_TRYRECYCLEITEM] = Cmd_tryrecycleitem, //0xEA + [B_SCR_OP_SETTYPETOENVIRONMENT] = Cmd_settypetoenvironment, //0xEB + [B_SCR_OP_PURSUITDOUBLES] = Cmd_pursuitdoubles, //0xEC + [B_SCR_OP_SNATCHSETBATTLERS] = Cmd_snatchsetbattlers, //0xED + [B_SCR_OP_REMOVELIGHTSCREENREFLECT] = Cmd_removelightscreenreflect, //0xEE + [B_SCR_OP_HANDLEBALLTHROW] = Cmd_handleballthrow, //0xEF + [B_SCR_OP_GIVECAUGHTMON] = Cmd_givecaughtmon, //0xF0 + [B_SCR_OP_TRYSETCAUGHTMONDEXFLAGS] = Cmd_trysetcaughtmondexflags, //0xF1 + [B_SCR_OP_DISPLAYDEXINFO] = Cmd_displaydexinfo, //0xF2 + [B_SCR_OP_TRYGIVECAUGHTMONNICK] = Cmd_trygivecaughtmonnick, //0xF3 + [B_SCR_OP_SUBATTACKERHPBYDMG] = Cmd_subattackerhpbydmg, //0xF4 + [B_SCR_OP_REMOVEATTACKERSTATUS1] = Cmd_removeattackerstatus1, //0xF5 + [B_SCR_OP_FINISHACTION] = Cmd_finishaction, //0xF6 + [B_SCR_OP_FINISHTURN] = Cmd_finishturn, //0xF7 + [B_SCR_OP_TRAINERSLIDEOUT] = Cmd_trainerslideout //0xF8 }; struct StatFractions From a5ee0f2727218d942d36735a83d7dc02834d8856 Mon Sep 17 00:00:00 2001 From: Bassoonian Date: Fri, 2 Jan 2026 21:59:12 +0100 Subject: [PATCH 03/23] ItemTMHMOrEvolutionStone enum (#2192) --- include/item_use.h | 7 +++++++ src/item_use.c | 6 +++--- src/party_menu.c | 4 ++-- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/include/item_use.h b/include/item_use.h index 8e28a4229c..52b07cd265 100644 --- a/include/item_use.h +++ b/include/item_use.h @@ -33,4 +33,11 @@ void Task_UseDigEscapeRopeOnField(u8 taskId); bool8 CanUseDigOrEscapeRopeOnCurMap(void); u8 CheckIfItemIsTMHMOrEvolutionStone(u16 itemId); +enum ItemTMHMOrEvolutionStone +{ + ITEM_IS_OTHER, + ITEM_IS_TM_HM, + ITEM_IS_EVOLUTION_STONE, +}; + #endif // GUARD_ITEM_USE_H diff --git a/src/item_use.c b/src/item_use.c index b7bf458a99..be09493f46 100755 --- a/src/item_use.c +++ b/src/item_use.c @@ -176,11 +176,11 @@ static void Task_CloseCantUseKeyItemMessage(u8 taskId) u8 CheckIfItemIsTMHMOrEvolutionStone(u16 itemId) { if (GetItemFieldFunc(itemId) == ItemUseOutOfBattle_TMHM) - return 1; + return ITEM_IS_TM_HM; else if (GetItemFieldFunc(itemId) == ItemUseOutOfBattle_EvolutionStone) - return 2; + return ITEM_IS_EVOLUTION_STONE; else - return 0; + return ITEM_IS_OTHER; } // Mail in the bag menu can't have a message but it can be checked (view the mail background, no message) diff --git a/src/party_menu.c b/src/party_menu.c index 7cde1a018c..1ba9051c6c 100755 --- a/src/party_menu.c +++ b/src/party_menu.c @@ -987,10 +987,10 @@ static bool8 DisplayPartyPokemonDataForMoveTutorOrEvolutionItem(u8 slot) { default: return FALSE; - case 1: // TM/HM + case ITEM_IS_TM_HM: DisplayPartyPokemonDataToTeachMove(slot, item, 0); break; - case 2: // Evolution stone + case ITEM_IS_EVOLUTION_STONE: if (!GetMonData(currentPokemon, MON_DATA_IS_EGG) && GetEvolutionTargetSpecies(currentPokemon, EVO_MODE_ITEM_CHECK, item) != SPECIES_NONE) return FALSE; DisplayPartyPokemonDescriptionData(slot, PARTYBOX_DESC_NO_USE); From edc7399104b27764a3f6846e99ec28221bcb4eb8 Mon Sep 17 00:00:00 2001 From: Estellar <137097857+estellarc@users.noreply.github.com> Date: Sat, 3 Jan 2026 21:49:12 -0300 Subject: [PATCH 04/23] `sPlayerAvatarGfxToStateFlag`structure --- src/field_player_avatar.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/field_player_avatar.c b/src/field_player_avatar.c index 90e19e27f3..3cf6523596 100644 --- a/src/field_player_avatar.c +++ b/src/field_player_avatar.c @@ -267,7 +267,11 @@ static const u8 sRSAvatarGfxIds[GENDER_COUNT] = [FEMALE] = OBJ_EVENT_GFX_LINK_RS_MAY }; -static const u8 sPlayerAvatarGfxToStateFlag[GENDER_COUNT][5][2] = +static const struct __attribute__((packed)) +{ + u8 graphicsId; + u8 playerFlag; +} sPlayerAvatarGfxToStateFlag[GENDER_COUNT][5] = { [MALE] = { @@ -1347,8 +1351,8 @@ static u8 GetPlayerAvatarStateTransitionByGraphicsId(u8 graphicsId, u8 gender) for (i = 0; i < ARRAY_COUNT(sPlayerAvatarGfxToStateFlag[0]); i++) { - if (sPlayerAvatarGfxToStateFlag[gender][i][0] == graphicsId) - return sPlayerAvatarGfxToStateFlag[gender][i][1]; + if (sPlayerAvatarGfxToStateFlag[gender][i].graphicsId == graphicsId) + return sPlayerAvatarGfxToStateFlag[gender][i].playerFlag; } return PLAYER_AVATAR_FLAG_ON_FOOT; } @@ -1360,8 +1364,8 @@ u8 GetPlayerAvatarGraphicsIdByCurrentState(void) for (i = 0; i < ARRAY_COUNT(sPlayerAvatarGfxToStateFlag[0]); i++) { - if (sPlayerAvatarGfxToStateFlag[gPlayerAvatar.gender][i][1] & flags) - return sPlayerAvatarGfxToStateFlag[gPlayerAvatar.gender][i][0]; + if (sPlayerAvatarGfxToStateFlag[gPlayerAvatar.gender][i].playerFlag & flags) + return sPlayerAvatarGfxToStateFlag[gPlayerAvatar.gender][i].graphicsId; } return 0; } From be8057540bcced5f65f1f67555a6f04db7d3c8f7 Mon Sep 17 00:00:00 2001 From: Jaizu Date: Sun, 18 Jan 2026 20:14:16 +0100 Subject: [PATCH 05/23] Use apt update before installing bin utils Seems to fix CLI actions! --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a99c99283d..c607ea6435 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -33,6 +33,7 @@ jobs: - name: Install binutils run: | + sudo apt update sudo apt install gcc-arm-none-eabi binutils-arm-none-eabi libpng-dev # build-essential and git are already installed # gcc-arm-none-eabi is only needed for the modern build From 80db2c6aec14e5ff804f76aedb2cb412bcfc3f5d Mon Sep 17 00:00:00 2001 From: Frank Date: Thu, 8 Jan 2026 21:09:44 -0500 Subject: [PATCH 06/23] Renamed building and stadium palettes to be more accurate --- .../building/{palette2.pal => gym.pal} | 0 .../building/{palette3.pal => leader.pal} | 0 .../stadium/{palette1.pal => aqua.pal} | 0 .../stadium/{palette6.pal => drake.pal} | 0 .../stadium/{palette5.pal => glacia.pal} | 0 .../stadium/{palette2.pal => magma.pal} | 0 .../stadium/{palette4.pal => phoebe.pal} | 0 .../stadium/{palette3.pal => sidney.pal} | 0 .../stadium/{palette7.pal => wallace.pal} | 0 src/data/graphics/battle_environment.h | 18 +++++++++--------- 10 files changed, 9 insertions(+), 9 deletions(-) rename graphics/battle_environment/building/{palette2.pal => gym.pal} (100%) rename graphics/battle_environment/building/{palette3.pal => leader.pal} (100%) rename graphics/battle_environment/stadium/{palette1.pal => aqua.pal} (100%) rename graphics/battle_environment/stadium/{palette6.pal => drake.pal} (100%) rename graphics/battle_environment/stadium/{palette5.pal => glacia.pal} (100%) rename graphics/battle_environment/stadium/{palette2.pal => magma.pal} (100%) rename graphics/battle_environment/stadium/{palette4.pal => phoebe.pal} (100%) rename graphics/battle_environment/stadium/{palette3.pal => sidney.pal} (100%) rename graphics/battle_environment/stadium/{palette7.pal => wallace.pal} (100%) diff --git a/graphics/battle_environment/building/palette2.pal b/graphics/battle_environment/building/gym.pal similarity index 100% rename from graphics/battle_environment/building/palette2.pal rename to graphics/battle_environment/building/gym.pal diff --git a/graphics/battle_environment/building/palette3.pal b/graphics/battle_environment/building/leader.pal similarity index 100% rename from graphics/battle_environment/building/palette3.pal rename to graphics/battle_environment/building/leader.pal diff --git a/graphics/battle_environment/stadium/palette1.pal b/graphics/battle_environment/stadium/aqua.pal similarity index 100% rename from graphics/battle_environment/stadium/palette1.pal rename to graphics/battle_environment/stadium/aqua.pal diff --git a/graphics/battle_environment/stadium/palette6.pal b/graphics/battle_environment/stadium/drake.pal similarity index 100% rename from graphics/battle_environment/stadium/palette6.pal rename to graphics/battle_environment/stadium/drake.pal diff --git a/graphics/battle_environment/stadium/palette5.pal b/graphics/battle_environment/stadium/glacia.pal similarity index 100% rename from graphics/battle_environment/stadium/palette5.pal rename to graphics/battle_environment/stadium/glacia.pal diff --git a/graphics/battle_environment/stadium/palette2.pal b/graphics/battle_environment/stadium/magma.pal similarity index 100% rename from graphics/battle_environment/stadium/palette2.pal rename to graphics/battle_environment/stadium/magma.pal diff --git a/graphics/battle_environment/stadium/palette4.pal b/graphics/battle_environment/stadium/phoebe.pal similarity index 100% rename from graphics/battle_environment/stadium/palette4.pal rename to graphics/battle_environment/stadium/phoebe.pal diff --git a/graphics/battle_environment/stadium/palette3.pal b/graphics/battle_environment/stadium/sidney.pal similarity index 100% rename from graphics/battle_environment/stadium/palette3.pal rename to graphics/battle_environment/stadium/sidney.pal diff --git a/graphics/battle_environment/stadium/palette7.pal b/graphics/battle_environment/stadium/wallace.pal similarity index 100% rename from graphics/battle_environment/stadium/palette7.pal rename to graphics/battle_environment/stadium/wallace.pal diff --git a/src/data/graphics/battle_environment.h b/src/data/graphics/battle_environment.h index d1abc026de..3d3ab733cc 100644 --- a/src/data/graphics/battle_environment.h +++ b/src/data/graphics/battle_environment.h @@ -46,15 +46,15 @@ const u32 gBattleEnvironmentPalette_Building[] = INCBIN_U32("graphics/battle_env const u32 gBattleEnvironmentPalette_Kyogre[] = INCBIN_U32("graphics/battle_environment/water/kyogre.gbapal.lz"); const u32 gBattleEnvironmentPalette_Groudon[] = INCBIN_U32("graphics/battle_environment/cave/groudon.gbapal.lz"); -const u32 gBattleEnvironmentPalette_BuildingGym[] = INCBIN_U32("graphics/battle_environment/building/palette2.gbapal.lz"); -const u32 gBattleEnvironmentPalette_BuildingLeader[] = INCBIN_U32("graphics/battle_environment/building/palette3.gbapal.lz"); -const u32 gBattleEnvironmentPalette_StadiumAqua[] = INCBIN_U32("graphics/battle_environment/stadium/palette1.gbapal.lz"); -const u32 gBattleEnvironmentPalette_StadiumMagma[] = INCBIN_U32("graphics/battle_environment/stadium/palette2.gbapal.lz"); -const u32 gBattleEnvironmentPalette_StadiumSidney[] = INCBIN_U32("graphics/battle_environment/stadium/palette3.gbapal.lz"); -const u32 gBattleEnvironmentPalette_StadiumPhoebe[] = INCBIN_U32("graphics/battle_environment/stadium/palette4.gbapal.lz"); -const u32 gBattleEnvironmentPalette_StadiumGlacia[] = INCBIN_U32("graphics/battle_environment/stadium/palette5.gbapal.lz"); -const u32 gBattleEnvironmentPalette_StadiumDrake[] = INCBIN_U32("graphics/battle_environment/stadium/palette6.gbapal.lz"); -const u32 gBattleEnvironmentPalette_StadiumWallace[] = INCBIN_U32("graphics/battle_environment/stadium/palette7.gbapal.lz"); +const u32 gBattleEnvironmentPalette_BuildingGym[] = INCBIN_U32("graphics/battle_environment/building/gym.gbapal.lz"); +const u32 gBattleEnvironmentPalette_BuildingLeader[] = INCBIN_U32("graphics/battle_environment/building/leader.gbapal.lz"); +const u32 gBattleEnvironmentPalette_StadiumAqua[] = INCBIN_U32("graphics/battle_environment/stadium/aqua.gbapal.lz"); +const u32 gBattleEnvironmentPalette_StadiumMagma[] = INCBIN_U32("graphics/battle_environment/stadium/magma.gbapal.lz"); +const u32 gBattleEnvironmentPalette_StadiumSidney[] = INCBIN_U32("graphics/battle_environment/stadium/sidney.gbapal.lz"); +const u32 gBattleEnvironmentPalette_StadiumPhoebe[] = INCBIN_U32("graphics/battle_environment/stadium/phoebe.gbapal.lz"); +const u32 gBattleEnvironmentPalette_StadiumGlacia[] = INCBIN_U32("graphics/battle_environment/stadium/glacia.gbapal.lz"); +const u32 gBattleEnvironmentPalette_StadiumDrake[] = INCBIN_U32("graphics/battle_environment/stadium/drake.gbapal.lz"); +const u32 gBattleEnvironmentPalette_StadiumWallace[] = INCBIN_U32("graphics/battle_environment/stadium/wallace.gbapal.lz"); const u32 gBattleEnvironmentPalette_Rayquaza[] = INCBIN_U32("graphics/battle_environment/sky/palette.gbapal.lz"); const u32 gBattleEnvironmentAnimTiles_TallGrass[] = INCBIN_U32("graphics/battle_environment/tall_grass/anim_tiles.4bpp.lz"); From 298bf85759f66b4068b65eba30dd1339832b8e29 Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Tue, 20 Jan 2026 13:05:28 +0100 Subject: [PATCH 07/23] Fix vivillon icy snow icon (#8970) --- src/data/graphics/pokemon.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/graphics/pokemon.h b/src/data/graphics/pokemon.h index 5ac450114f..d29f7ca79f 100644 --- a/src/data/graphics/pokemon.h +++ b/src/data/graphics/pokemon.h @@ -19386,7 +19386,7 @@ const u32 gObjectEventPic_Substitute[] = INCBIN_COMP("graphics/pokemon/question_ const u16 gMonPalette_VivillonIcySnow[] = INCBIN_U16("graphics/pokemon/vivillon/normal.gbapal"); const u32 gMonBackPic_VivillonIcySnow[] = INCBIN_U32("graphics/pokemon/vivillon/back.4bpp.smol"); const u16 gMonShinyPalette_VivillonIcySnow[] = INCBIN_U16("graphics/pokemon/vivillon/shiny.gbapal"); - const u8 gMonIcon_VivillonIcySnow[] = INCBIN_U8("graphics/pokemon/vivillon/meadow/icon.4bpp"); + const u8 gMonIcon_VivillonIcySnow[] = INCBIN_U8("graphics/pokemon/vivillon/icon.4bpp"); #if P_FOOTPRINTS const u8 gMonFootprint_Vivillon[] = INCBIN_U8("graphics/pokemon/vivillon/footprint.1bpp"); #endif //P_FOOTPRINTS From 3b241b561a851ee72054ded6e39a794719a4791b Mon Sep 17 00:00:00 2001 From: hedara90 <90hedara@gmail.com> Date: Tue, 20 Jan 2026 14:31:36 +0100 Subject: [PATCH 08/23] Move CMD_ARGS to battle_script_commands (#8974) Co-authored-by: Hedara --- include/battle.h | 20 ------------ src/battle_dynamax.c | 40 ------------------------ src/battle_script_commands.c | 60 ++++++++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 60 deletions(-) diff --git a/include/battle.h b/include/battle.h index 49d7cbb0ab..71dbb19491 100755 --- a/include/battle.h +++ b/include/battle.h @@ -27,26 +27,6 @@ #include "random.h" // for rng_value_t #include "trainer_slide.h" -// Helper for accessing command arguments and advancing gBattlescriptCurrInstr. -// -// For example accuracycheck is defined as: -// -// .macro accuracycheck failInstr:req, move:req -// .byte 0x1 -// .4byte \failInstr -// .2byte \move -// .endm -// -// Which corresponds to: -// -// CMD_ARGS(const u8 *failInstr, u16 move); -// -// The arguments can be accessed as cmd->failInstr and cmd->move. -// gBattlescriptCurrInstr = cmd->nextInstr; advances to the next instruction. -#define CMD_ARGS(...) const struct __attribute__((packed)) { u8 opcode; RECURSIVELY(R_FOR_EACH(APPEND_SEMICOLON, __VA_ARGS__)) const u8 nextInstr[0]; } *const cmd UNUSED = (const void *)gBattlescriptCurrInstr -#define VARIOUS_ARGS(...) CMD_ARGS(u8 battler, u8 id, ##__VA_ARGS__) -#define NATIVE_ARGS(...) CMD_ARGS(void (*func)(void), ##__VA_ARGS__) - // Used to exclude moves learned temporarily by Transform or Mimic #define MOVE_IS_PERMANENT(battler, moveSlot) \ (!(gBattleMons[battler].volatiles.transformed) \ diff --git a/src/battle_dynamax.c b/src/battle_dynamax.c index d5edbc0e61..c907ae696f 100644 --- a/src/battle_dynamax.c +++ b/src/battle_dynamax.c @@ -457,43 +457,3 @@ void ChooseDamageNonTypesString(enum Type type) break; } } - -// Updates Dynamax HP multipliers and healthboxes. -void BS_UpdateDynamax(void) -{ - NATIVE_ARGS(); - u32 battler = gBattleScripting.battler; - struct Pokemon *mon = GetBattlerMon(battler); - - if (!IsGigantamaxed(battler)) // RecalcBattlerStats will get called on form change. - RecalcBattlerStats(battler, mon, GetActiveGimmick(battler) == GIMMICK_DYNAMAX); - - UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], mon, HEALTHBOX_ALL); - gBattlescriptCurrInstr = cmd->nextInstr; -} - -// Goes to the jump instruction if the target is Dynamaxed. -void BS_JumpIfDynamaxed(void) -{ - NATIVE_ARGS(const u8 *jumpInstr); - if ((GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX)) - gBattlescriptCurrInstr = cmd->jumpInstr; - else - gBattlescriptCurrInstr = cmd->nextInstr; -} - -void BS_UndoDynamax(void) -{ - NATIVE_ARGS(u8 battler); - u32 battler = GetBattlerForBattleScript(cmd->battler); - - if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX) - { - UndoDynamax(battler); - gBattleScripting.battler = battler; - BattleScriptCall(BattleScript_DynamaxEnds_Ret); - return; - } - - gBattlescriptCurrInstr = cmd->nextInstr; -} diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 61c9f01ec2..97e324b25c 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -72,6 +72,26 @@ #include "load_save.h" #include "test/test_runner_battle.h" +// Helper for accessing command arguments and advancing gBattlescriptCurrInstr. +// +// For example accuracycheck is defined as: +// +// .macro accuracycheck failInstr:req, move:req +// .byte 0x1 +// .4byte \failInstr +// .2byte \move +// .endm +// +// Which corresponds to: +// +// CMD_ARGS(const u8 *failInstr, u16 move); +// +// The arguments can be accessed as cmd->failInstr and cmd->move. +// gBattlescriptCurrInstr = cmd->nextInstr; advances to the next instruction. +#define CMD_ARGS(...) const struct __attribute__((packed)) { u8 opcode; RECURSIVELY(R_FOR_EACH(APPEND_SEMICOLON, __VA_ARGS__)) const u8 nextInstr[0]; } *const cmd UNUSED = (const void *)gBattlescriptCurrInstr +#define VARIOUS_ARGS(...) CMD_ARGS(u8 battler, u8 id, ##__VA_ARGS__) +#define NATIVE_ARGS(...) CMD_ARGS(void (*func)(void), ##__VA_ARGS__) + // table to avoid ugly powing on gba (courtesy of doesnt) // this returns (i^2.5)/4 // the quarters cancel so no need to re-quadruple them in actual calculation @@ -18081,3 +18101,43 @@ void BS_TryAbsorbToxicSpikesOnFaint(void) gBattlescriptCurrInstr = cmd->nextInstr; } + +// Updates Dynamax HP multipliers and healthboxes. +void BS_UpdateDynamax(void) +{ + NATIVE_ARGS(); + u32 battler = gBattleScripting.battler; + struct Pokemon *mon = GetBattlerMon(battler); + + if (!IsGigantamaxed(battler)) // RecalcBattlerStats will get called on form change. + RecalcBattlerStats(battler, mon, GetActiveGimmick(battler) == GIMMICK_DYNAMAX); + + UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], mon, HEALTHBOX_ALL); + gBattlescriptCurrInstr = cmd->nextInstr; +} + +// Goes to the jump instruction if the target is Dynamaxed. +void BS_JumpIfDynamaxed(void) +{ + NATIVE_ARGS(const u8 *jumpInstr); + if ((GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX)) + gBattlescriptCurrInstr = cmd->jumpInstr; + else + gBattlescriptCurrInstr = cmd->nextInstr; +} + +void BS_UndoDynamax(void) +{ + NATIVE_ARGS(u8 battler); + u32 battler = GetBattlerForBattleScript(cmd->battler); + + if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX) + { + UndoDynamax(battler); + gBattleScripting.battler = battler; + BattleScriptCall(BattleScript_DynamaxEnds_Ret); + return; + } + + gBattlescriptCurrInstr = cmd->nextInstr; +} From c899479709e7608622579cb4a021f6af67780c6a Mon Sep 17 00:00:00 2001 From: GGbond Date: Wed, 21 Jan 2026 05:04:09 +0800 Subject: [PATCH 09/23] Fix the speed scoring of Tailwind for AI (#8968) --- src/battle_ai_main.c | 4 ++-- test/battle/ai/ai_doubles.c | 26 ++++++++++++++++---------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 7cbbb29e9b..e0490b6712 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -5500,9 +5500,9 @@ static s32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move, stru tailwindScore += 1; if (speed <= foe2Speed && (speed * 2) > foe2Speed) tailwindScore += 1; - if (partnerSpeed <= foe1Speed && (speed * 2) > foe1Speed) + if (partnerSpeed <= foe1Speed && (partnerSpeed * 2) > foe1Speed) tailwindScore += 1; - if (partnerSpeed <= foe1Speed && (speed * 2) > foe1Speed) + if (partnerSpeed <= foe2Speed && (partnerSpeed * 2) > foe2Speed) tailwindScore += 1; if (tailwindScore > 0) diff --git a/test/battle/ai/ai_doubles.c b/test/battle/ai/ai_doubles.c index 8edfea9ee1..3aaa0ed30a 100644 --- a/test/battle/ai/ai_doubles.c +++ b/test/battle/ai/ai_doubles.c @@ -780,27 +780,33 @@ AI_DOUBLE_BATTLE_TEST("AI does not use Helping Hand on Good as Gold ally") } } -AI_DOUBLE_BATTLE_TEST("AI uses Tailwind") +AI_DOUBLE_BATTLE_TEST("AI uses Tailwind based on speed matchups") { u32 speed1, speed2, speed3, speed4; + bool32 expectTailwind; - PARAMETRIZE { speed1 = 20; speed2 = 20; speed3 = 20; speed4 = 20; } - PARAMETRIZE { speed1 = 20; speed2 = 20; speed3 = 5; speed4 = 5; } - PARAMETRIZE { speed1 = 20; speed2 = 20; speed3 = 15; speed4 = 15; } - PARAMETRIZE { speed1 = 1; speed2 = 1; speed3 = 5; speed4 = 5; } - PARAMETRIZE { speed1 = 1; speed2 = 20; speed3 = 15; speed4 = 15; } - PARAMETRIZE { speed1 = 1; speed2 = 20; speed3 = 20; speed4 = 15; } + // All four comparisons qualify -> tailwindScore = 5 + PARAMETRIZE { speed1 = 20; speed2 = 20; speed3 = 20; speed4 = 20; expectTailwind = TRUE; } + // Only the attacker flips one foe matchup -> tailwindScore = 2 + PARAMETRIZE { speed1 = 20; speed2 = 40; speed3 = 20; speed4 = 50; expectTailwind = TRUE; } + // Only the partner flips one foe matchup -> tailwindScore = 2 + PARAMETRIZE { speed1 = 10; speed2 = 29; speed3 = 50; speed4 = 15; expectTailwind = TRUE; } + // Too slow: even after doubling, still slower than both foes -> tailwindScore = 0. + PARAMETRIZE { speed1 = 40; speed2 = 40; speed3 = 10; speed4 = 10; expectTailwind = FALSE; } + // Already faster: Tailwind doesn't improve matchups -> tailwindScore = 0. + PARAMETRIZE { speed1 = 5; speed2 = 5; speed3 = 10; speed4 = 10; expectTailwind = FALSE; } + // Boundary: speed*2 == foe speed does not count -> tailwindScore = 0. + PARAMETRIZE { speed1 = 20; speed2 = 20; speed3 = 10; speed4 = 30; expectTailwind = FALSE; } GIVEN { - ASSUME(GetMoveEffect(MOVE_AFTER_YOU) == EFFECT_AFTER_YOU); - ASSUME(GetMoveEffect(MOVE_TRICK_ROOM) == EFFECT_TRICK_ROOM); + ASSUME(GetMoveEffect(MOVE_TAILWIND) == EFFECT_TAILWIND); AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_DOUBLE_BATTLE); PLAYER(SPECIES_WOBBUFFET) { Speed(speed1); } PLAYER(SPECIES_WOBBUFFET) { Speed(speed2); } OPPONENT(SPECIES_WOBBUFFET) { Speed(speed3); Moves(MOVE_TAILWIND, MOVE_HEADBUTT); } OPPONENT(SPECIES_WOBBUFFET) { Speed(speed4); Moves(MOVE_TAILWIND, MOVE_HEADBUTT); } } WHEN { - if (speed3 > 10) + if (expectTailwind) TURN { EXPECT_MOVE(opponentLeft, MOVE_TAILWIND); } else TURN { NOT_EXPECT_MOVE(opponentLeft, MOVE_TAILWIND); } From 128beb39e119434aa0ff5aab574b85362ab9eb0f Mon Sep 17 00:00:00 2001 From: GGbond Date: Wed, 21 Jan 2026 05:06:50 +0800 Subject: [PATCH 10/23] Fixed several issues with Parting Shot (#8928) --- data/battle_scripts_1.s | 47 ++- include/config/battle.h | 1 + include/constants/generational_changes.h | 1 + src/battle_script_commands.c | 2 + test/battle/move_effect/parting_shot.c | 383 ++++++++++++++++++++++- 5 files changed, 427 insertions(+), 7 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 6b4b18f48a..b26d2905da 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -1171,24 +1171,59 @@ BattleScript_EffectSpectralThiefFromDamage: BattleScript_EffectPartingShot:: attackcanceler jumpifstat BS_TARGET, CMP_GREATER_THAN, STAT_ATK, MIN_STAT_STAGE, BattleScript_EffectPartingShotTryAtk - jumpifstat BS_TARGET, CMP_EQUAL, STAT_SPATK, MIN_STAT_STAGE, BattleScript_CantLowerMultipleStats + jumpifstat BS_TARGET, CMP_EQUAL, STAT_SPATK, MIN_STAT_STAGE, BattleScript_EffectPartingShotCantLowerMultipleStats BattleScript_EffectPartingShotTryAtk: accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE attackanimation waitanimation + setbyte sB_ANIM_TARGETS_HIT, 0 setstatchanger STAT_ATK, 1, TRUE statbuffchange BS_TARGET, STAT_CHANGE_ALLOW_PTR, BattleScript_EffectPartingShotTrySpAtk, BIT_SPATK - printfromtable gStatDownStringIds - waitmessage B_WAIT_TIME_LONG + call BattleScript_EffectPartingShotMaybePrintStat BattleScript_EffectPartingShotTrySpAtk: setstatchanger STAT_SPATK, 1, TRUE - statbuffchange BS_TARGET, STAT_CHANGE_ALLOW_PTR, BattleScript_EffectPartingShotSwitch - printfromtable gStatDownStringIds - waitmessage B_WAIT_TIME_LONG + statbuffchange BS_TARGET, STAT_CHANGE_ALLOW_PTR, BattleScript_EffectPartingShotMaybeSwitch + call BattleScript_EffectPartingShotMaybePrintStat +BattleScript_EffectPartingShotMaybeSwitch: + jumpifgenconfiglowerthan CONFIG_PARTING_SHOT_SWITCH, GEN_7, BattleScript_EffectPartingShotSwitch + jumpifbyte CMP_NOT_EQUAL, sB_ANIM_TARGETS_HIT, 0, BattleScript_EffectPartingShotSwitch + goto BattleScript_MoveEnd + BattleScript_EffectPartingShotSwitch: moveendall goto BattleScript_MoveSwitchPursuitEnd +BattleScript_EffectPartingShotCantLowerMultipleStats: + pause B_WAIT_TIME_SHORT + setmoveresultflags MOVE_RESULT_FAILED + call BattleScript_EffectPartingShotPrintWontDecrease + setbyte sB_ANIM_TARGETS_HIT, 0 + goto BattleScript_EffectPartingShotMaybeSwitch + +BattleScript_EffectPartingShotMaybePrintStat: + jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_ATTACKER_STAT_CHANGED, BattleScript_EffectPartingShotPrintStat + jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_DEFENDER_STAT_CHANGED, BattleScript_EffectPartingShotPrintStat + return + +BattleScript_EffectPartingShotPrintStat: + setbyte sB_ANIM_TARGETS_HIT, 1 + printfromtable gStatDownStringIds + waitmessage B_WAIT_TIME_LONG + return + +BattleScript_EffectPartingShotPrintWontDecrease: + jumpifability BS_TARGET, ABILITY_CONTRARY, BattleScript_EffectPartingShotPrintWontDecreaseContrary + printstring STRINGID_STATSWONTDECREASE2 + waitmessage B_WAIT_TIME_LONG + return + +BattleScript_EffectPartingShotPrintWontDecreaseContrary: + swapattackerwithtarget + printstring STRINGID_STATSWONTDECREASE2 + waitmessage B_WAIT_TIME_LONG + swapattackerwithtarget + return + BattleScript_EffectPowder:: attackcanceler accuracycheck BattleScript_MoveMissedPause, NO_ACC_CALC_CHECK_LOCK_ON diff --git a/include/config/battle.h b/include/config/battle.h index b5d11a9b3e..874817a5ab 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -96,6 +96,7 @@ #define B_CAN_SPITE_FAIL GEN_LATEST // In Gen4+, Spite can no longer fail if the foe's last move only has 1 remaining PP. #define B_CRASH_IF_TARGET_IMMUNE GEN_LATEST // In Gen4+, moves with crash damage will crash if the user attacks a target that is immune due to their typing. #define B_MEMENTO_FAIL GEN_LATEST // In Gen4+, Memento no longer fails if the target already has -6 Attack and Special Attack. Additionally, in Gen5+, it fails if there is no target, or if the target is protected or behind a Substitute. +#define B_PARTING_SHOT_SWITCH GEN_LATEST // In Gen7+, the user won't switch out if Parting Shot fails to lower the target's stats. #define B_GLARE_GHOST GEN_LATEST // In Gen4+, Glare can hit Ghost-type Pokémon normally. #define B_SKILL_SWAP GEN_LATEST // In Gen4+, Skill Swap triggers switch-in abilities after use. #define B_BRICK_BREAK GEN_LATEST // In Gen4+, you can destroy your own side's screens. In Gen 5+, screens are not removed if the target is immune. diff --git a/include/constants/generational_changes.h b/include/constants/generational_changes.h index 81ff4bece6..f8ef227c4a 100644 --- a/include/constants/generational_changes.h +++ b/include/constants/generational_changes.h @@ -89,6 +89,7 @@ F(CAN_SPITE_FAIL, canSpiteFail, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \ F(CRASH_IF_TARGET_IMMUNE, crashIfTargetImmune, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \ F(MEMENTO_FAIL, mementoFail, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \ + F(PARTING_SHOT_SWITCH, partingShotSwitch, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \ F(GLARE_GHOST, glareGhost, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \ F(SKILL_SWAP, skillSwap, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \ F(BRICK_BREAK, brickBreak, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \ diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 97e324b25c..0dee3f9320 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -10395,6 +10395,8 @@ static u32 ChangeStatBuffs(u32 battler, s8 statValue, enum Stat statId, union St } else if (battlerAbility == ABILITY_MIRROR_ARMOR && !flags.mirrorArmored && gBattlerAttacker != gBattlerTarget && battler == gBattlerTarget) { + if (GetMoveEffect(gCurrentMove) == EFFECT_PARTING_SHOT) + gBattleScripting.animTargetsHit = 1; if (flags.allowPtr) { SET_STATCHANGER(statId, GET_STAT_BUFF_VALUE(statValue) | STAT_BUFF_NEGATIVE, TRUE); diff --git a/test/battle/move_effect/parting_shot.c b/test/battle/move_effect/parting_shot.c index 4d55d09315..58c5ba5218 100644 --- a/test/battle/move_effect/parting_shot.c +++ b/test/battle/move_effect/parting_shot.c @@ -1,4 +1,385 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("TODO: Write Parting Shot (Move Effect) test titles") +ASSUMPTIONS +{ + ASSUME(GetMoveEffect(MOVE_PARTING_SHOT) == EFFECT_PARTING_SHOT); +} + +SINGLE_BATTLE_TEST("Parting Shot: Passes Substitute and switches the user out") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT, MOVE_CELEBRATE); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_SUBSTITUTE, MOVE_CELEBRATE); } + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SUBSTITUTE); } + TURN { MOVE(player, MOVE_PARTING_SHOT); MOVE(opponent, MOVE_CELEBRATE); SEND_OUT(player, 1); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, player); + SEND_IN_MESSAGE("Wynaut"); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE - 1); + EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE - 1); + EXPECT_EQ(player->species, SPECIES_WYNAUT); + } +} + +SINGLE_BATTLE_TEST("Parting Shot: Soundproof and Good as Gold block Parting Shot") +{ + u16 species, ability; + + PARAMETRIZE { species = SPECIES_EXPLOUD; ability = ABILITY_SOUNDPROOF; } + PARAMETRIZE { species = SPECIES_GHOLDENGO; ability = ABILITY_GOOD_AS_GOLD; } + + GIVEN { + ASSUME(IsSoundMove(MOVE_PARTING_SHOT)); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(species) { Ability(ability); Moves(MOVE_CELEBRATE); } + } WHEN { + TURN { MOVE(player, MOVE_PARTING_SHOT); MOVE(opponent, MOVE_CELEBRATE); } + } SCENE { + ABILITY_POPUP(opponent, ability); + if (ability == ABILITY_SOUNDPROOF) + MESSAGE("The opposing Exploud's Soundproof blocks Parting Shot!"); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, player); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(player->species, SPECIES_WOBBUFFET); + } +} + +SINGLE_BATTLE_TEST("Parting Shot: Hyper Cutter blocks Attack drop but still switches") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_KRABBY) { Ability(ABILITY_HYPER_CUTTER); } + } WHEN { + TURN { MOVE(player, MOVE_PARTING_SHOT); SEND_OUT(player, 1); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, player); + SEND_IN_MESSAGE("Wynaut"); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE - 1); + EXPECT_EQ(player->species, SPECIES_WYNAUT); + } +} + +SINGLE_BATTLE_TEST("Parting Shot: Magic Coat bounces it and switches the target out") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_MAGIC_COAT); } + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(opponent, MOVE_MAGIC_COAT); MOVE(player, MOVE_PARTING_SHOT); SEND_OUT(opponent, 1); } + } THEN { + EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE - 1); + EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE - 1); + EXPECT_EQ(opponent->species, SPECIES_WYNAUT); + } +} + +SINGLE_BATTLE_TEST("Parting Shot: Magic Bounce bounces it and switches the target out") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT); } + OPPONENT(SPECIES_ESPEON) { Ability(ABILITY_MAGIC_BOUNCE); } + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(player, MOVE_PARTING_SHOT); SEND_OUT(opponent, 1); } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_MAGIC_BOUNCE); + } THEN { + EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE - 1); + EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE - 1); + EXPECT_EQ(opponent->species, SPECIES_WYNAUT); + } +} + +SINGLE_BATTLE_TEST("Parting Shot: Mirror Armor switches the user even if reflected drops fail") +{ + u16 species, ability, item; + + PARAMETRIZE { species = SPECIES_METAGROSS; ability = ABILITY_CLEAR_BODY; item = ITEM_NONE; } + PARAMETRIZE { species = SPECIES_TORKOAL; ability = ABILITY_WHITE_SMOKE; item = ITEM_NONE; } + PARAMETRIZE { species = SPECIES_SOLGALEO; ability = ABILITY_FULL_METAL_BODY; item = ITEM_NONE; } + PARAMETRIZE { species = SPECIES_LUCARIO; ability = ABILITY_INNER_FOCUS; item = ITEM_CLEAR_AMULET; } + + GIVEN { + ASSUME(gItemsInfo[ITEM_CLEAR_AMULET].holdEffect == HOLD_EFFECT_CLEAR_AMULET); + PLAYER(species) { Ability(ability); Item(item); Moves(MOVE_PARTING_SHOT); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_CORVIKNIGHT) { Ability(ABILITY_MIRROR_ARMOR); } + } WHEN { + TURN { MOVE(player, MOVE_PARTING_SHOT); SEND_OUT(player, 1); } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_MIRROR_ARMOR); + if (ability == ABILITY_CLEAR_BODY) { + ABILITY_POPUP(player, ABILITY_CLEAR_BODY); + MESSAGE("Metagross's Clear Body prevents stat loss!"); + } else if (ability == ABILITY_WHITE_SMOKE) { + ABILITY_POPUP(player, ABILITY_WHITE_SMOKE); + MESSAGE("Torkoal's White Smoke prevents stat loss!"); + } else if (ability == ABILITY_FULL_METAL_BODY) { + ABILITY_POPUP(player, ABILITY_FULL_METAL_BODY); + MESSAGE("Solgaleo's Full Metal Body prevents stat loss!"); + } else if (item == ITEM_CLEAR_AMULET) { + MESSAGE("The effects of the Clear Amulet held by Lucario prevents its stats from being lowered!"); + } + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(player->species, SPECIES_WYNAUT); + } +} + +SINGLE_BATTLE_TEST("Parting Shot: Mirror Armor switches even if reflected stats are at minimum") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT, MOVE_SHELL_SMASH, MOVE_CELEBRATE); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_CORVIKNIGHT) { Ability(ABILITY_MIRROR_ARMOR); Moves(MOVE_TOPSY_TURVY, MOVE_CELEBRATE); } + } WHEN { + TURN { MOVE(player, MOVE_SHELL_SMASH); MOVE(opponent, MOVE_CELEBRATE); } + TURN { MOVE(player, MOVE_SHELL_SMASH); MOVE(opponent, MOVE_CELEBRATE); } + TURN { MOVE(player, MOVE_SHELL_SMASH); MOVE(opponent, MOVE_CELEBRATE); } + TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_TOPSY_TURVY); } + TURN { MOVE(player, MOVE_PARTING_SHOT); SEND_OUT(player, 1); } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_MIRROR_ARMOR); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(player->species, SPECIES_WYNAUT); + } +} + +SINGLE_BATTLE_TEST("Parting Shot: Does not switch if both stats are at minimum (Gen7+)") +{ + GIVEN { + WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_7); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT, MOVE_TOPSY_TURVY, MOVE_CELEBRATE); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_OMASTAR) { Moves(MOVE_SHELL_SMASH, MOVE_CELEBRATE); } + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); } + TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); } + TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); } + TURN { MOVE(player, MOVE_TOPSY_TURVY); MOVE(opponent, MOVE_CELEBRATE); } + TURN { MOVE(player, MOVE_PARTING_SHOT); MOVE(opponent, MOVE_CELEBRATE); } + } SCENE { + MESSAGE("The opposing Omastar's stats won't go any lower!"); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], MIN_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_SPATK], MIN_STAT_STAGE); + EXPECT_EQ(player->species, SPECIES_WOBBUFFET); + } +} + +SINGLE_BATTLE_TEST("Parting Shot: Does not switch if Contrary is at maximum stats (Gen7+)") +{ + GIVEN { + WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_7); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT, MOVE_TOPSY_TURVY, MOVE_CELEBRATE); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_INKAY) { Ability(ABILITY_CONTRARY); Moves(MOVE_SHELL_SMASH, MOVE_CELEBRATE); } + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); } + TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); } + TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); } + TURN { MOVE(player, MOVE_TOPSY_TURVY); MOVE(opponent, MOVE_CELEBRATE); } + TURN { MOVE(player, MOVE_PARTING_SHOT); MOVE(opponent, MOVE_CELEBRATE); } + } SCENE { + MESSAGE("The opposing Inkay's stats won't go any higher!"); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], MAX_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_SPATK], MAX_STAT_STAGE); + EXPECT_EQ(player->species, SPECIES_WOBBUFFET); + } +} + +SINGLE_BATTLE_TEST("Parting Shot: Stat drop prevention by abilities/items does not switch (Gen7+)") +{ + u16 species, ability, item; + + PARAMETRIZE { species = SPECIES_METAGROSS; ability = ABILITY_CLEAR_BODY; item = ITEM_NONE; } + PARAMETRIZE { species = SPECIES_TORKOAL; ability = ABILITY_WHITE_SMOKE; item = ITEM_NONE; } + PARAMETRIZE { species = SPECIES_SOLGALEO; ability = ABILITY_FULL_METAL_BODY; item = ITEM_NONE; } + PARAMETRIZE { species = SPECIES_LUCARIO; ability = ABILITY_INNER_FOCUS; item = ITEM_CLEAR_AMULET; } + + GIVEN { + WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_7); + ASSUME(gItemsInfo[ITEM_CLEAR_AMULET].holdEffect == HOLD_EFFECT_CLEAR_AMULET); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(species) { Ability(ability); Item(item); } + } WHEN { + TURN { MOVE(player, MOVE_PARTING_SHOT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, player); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(player->species, SPECIES_WOBBUFFET); + } +} + +SINGLE_BATTLE_TEST("Parting Shot: Mist prevents stat drops and does not switch (Gen7+)") +{ + GIVEN { + WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_7); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT, MOVE_CELEBRATE); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_MIST, MOVE_CELEBRATE); } + } WHEN { + TURN { MOVE(opponent, MOVE_MIST); MOVE(player, MOVE_CELEBRATE); } + TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_PARTING_SHOT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, player); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(player->species, SPECIES_WOBBUFFET); + } +} + +DOUBLE_BATTLE_TEST("Parting Shot: Flower Veil prevents stat drops and does not switch (Gen7+)") +{ + GIVEN { + ASSUME(GetSpeciesType(SPECIES_BULBASAUR, 0) == TYPE_GRASS); + WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_7); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT); } + PLAYER(SPECIES_WYNAUT); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_BULBASAUR); + OPPONENT(SPECIES_COMFEY) { Ability(ABILITY_FLOWER_VEIL); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_PARTING_SHOT, target: opponentLeft); MOVE(playerRight, MOVE_CELEBRATE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, playerLeft); + } THEN { + EXPECT_EQ(opponentLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponentLeft->statStages[STAT_SPATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(playerLeft->species, SPECIES_WOBBUFFET); + } +} + +SINGLE_BATTLE_TEST("Parting Shot: Switches if both stats are at minimum (Gen6)") +{ + GIVEN { + WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_6); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT, MOVE_TOPSY_TURVY, MOVE_CELEBRATE); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_OMASTAR) { Moves(MOVE_SHELL_SMASH, MOVE_CELEBRATE); } + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); } + TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); } + TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); } + TURN { MOVE(player, MOVE_TOPSY_TURVY); MOVE(opponent, MOVE_CELEBRATE); } + TURN { MOVE(player, MOVE_PARTING_SHOT); MOVE(opponent, MOVE_CELEBRATE); SEND_OUT(player, 1); } + } SCENE { + MESSAGE("The opposing Omastar's stats won't go any lower!"); + SEND_IN_MESSAGE("Wynaut"); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], MIN_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_SPATK], MIN_STAT_STAGE); + EXPECT_EQ(player->species, SPECIES_WYNAUT); + } +} + +SINGLE_BATTLE_TEST("Parting Shot: Switches if Contrary is at maximum stats (Gen6)") +{ + GIVEN { + WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_6); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT, MOVE_TOPSY_TURVY, MOVE_CELEBRATE); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_INKAY) { Ability(ABILITY_CONTRARY); Moves(MOVE_SHELL_SMASH, MOVE_CELEBRATE); } + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); } + TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); } + TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SHELL_SMASH); } + TURN { MOVE(player, MOVE_TOPSY_TURVY); MOVE(opponent, MOVE_CELEBRATE); } + TURN { MOVE(player, MOVE_PARTING_SHOT); MOVE(opponent, MOVE_CELEBRATE); SEND_OUT(player, 1); } + } SCENE { + MESSAGE("The opposing Inkay's stats won't go any higher!"); + SEND_IN_MESSAGE("Wynaut"); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], MAX_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_SPATK], MAX_STAT_STAGE); + EXPECT_EQ(player->species, SPECIES_WYNAUT); + } +} + +SINGLE_BATTLE_TEST("Parting Shot: Stat drop prevention by abilities/items switches (Gen6)") +{ + u16 species, ability, item; + + PARAMETRIZE { species = SPECIES_METAGROSS; ability = ABILITY_CLEAR_BODY; item = ITEM_NONE; } + PARAMETRIZE { species = SPECIES_TORKOAL; ability = ABILITY_WHITE_SMOKE; item = ITEM_NONE; } + PARAMETRIZE { species = SPECIES_SOLGALEO; ability = ABILITY_FULL_METAL_BODY; item = ITEM_NONE; } + PARAMETRIZE { species = SPECIES_LUCARIO; ability = ABILITY_INNER_FOCUS; item = ITEM_CLEAR_AMULET; } + + GIVEN { + WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_6); + ASSUME(gItemsInfo[ITEM_CLEAR_AMULET].holdEffect == HOLD_EFFECT_CLEAR_AMULET); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(species) { Ability(ability); Item(item); } + } WHEN { + TURN { MOVE(player, MOVE_PARTING_SHOT); SEND_OUT(player, 1); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, player); + SEND_IN_MESSAGE("Wynaut"); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(player->species, SPECIES_WYNAUT); + } +} + +SINGLE_BATTLE_TEST("Parting Shot: Mist prevents stat drops and switches (Gen6)") +{ + GIVEN { + WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_6); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT, MOVE_CELEBRATE); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_MIST, MOVE_CELEBRATE); } + } WHEN { + TURN { MOVE(opponent, MOVE_MIST); MOVE(player, MOVE_CELEBRATE); } + TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_PARTING_SHOT); SEND_OUT(player, 1); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, player); + SEND_IN_MESSAGE("Wynaut"); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(player->species, SPECIES_WYNAUT); + } +} + +DOUBLE_BATTLE_TEST("Parting Shot: Flower Veil prevents stat drops and switches (Gen6)") +{ + GIVEN { + WITH_CONFIG(CONFIG_PARTING_SHOT_SWITCH, GEN_6); + ASSUME(GetSpeciesType(SPECIES_BULBASAUR, 0) == TYPE_GRASS); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_PARTING_SHOT); } + PLAYER(SPECIES_WYNAUT); + PLAYER(SPECIES_PIKACHU); + OPPONENT(SPECIES_BULBASAUR); + OPPONENT(SPECIES_COMFEY) { Ability(ABILITY_FLOWER_VEIL); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_PARTING_SHOT, target: opponentLeft); MOVE(playerRight, MOVE_CELEBRATE); SEND_OUT(playerLeft, 2); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, playerLeft); + SEND_IN_MESSAGE("Pikachu"); + } THEN { + EXPECT_EQ(opponentLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponentLeft->statStages[STAT_SPATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(playerLeft->species, SPECIES_PIKACHU); + } +} From 2eef017daf82f22f8a25fdf5604bce962b97b66d Mon Sep 17 00:00:00 2001 From: FosterProgramming Date: Wed, 21 Jan 2026 01:19:29 +0100 Subject: [PATCH 11/23] Fix premier ball wrong data size (#8979) --- src/data/graphics/items.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/graphics/items.h b/src/data/graphics/items.h index e6bd7e8d67..2085c74d21 100644 --- a/src/data/graphics/items.h +++ b/src/data/graphics/items.h @@ -21,7 +21,7 @@ const u16 gItemIconPalette_UltraBall[] = INCBIN_U16("graphics/items/icon_palette const u32 gItemIcon_MasterBall[] = INCBIN_U32("graphics/items/icons/master_ball.4bpp.smol"); const u16 gItemIconPalette_MasterBall[] = INCBIN_U16("graphics/items/icon_palettes/master_ball.gbapal"); -const u32 gItemIcon_PremierBall[] = INCBIN_U16("graphics/items/icons/premier_ball.4bpp.smol"); +const u32 gItemIcon_PremierBall[] = INCBIN_U32("graphics/items/icons/premier_ball.4bpp.smol"); const u16 gItemIconPalette_PremierBall[] = INCBIN_U16("graphics/items/icon_palettes/premier_ball.gbapal"); const u32 gItemIcon_HealBall[] = INCBIN_U32("graphics/items/icons/heal_ball.4bpp.smol"); From 670bd806e536743a67f8498bb23481712a1eec6c Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Wed, 21 Jan 2026 15:50:13 +0100 Subject: [PATCH 12/23] add SabataLunar as a contributor for design (#8985) --- .all-contributorsrc | 9 +++++++++ CREDITS.md | 3 +++ 2 files changed, 12 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 11c68b7a64..a659a16bfc 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -615,6 +615,15 @@ "contributions": [ "code" ] + }, + { + "login": "SabataLunar", + "name": "SabataLunar", + "avatar_url": "https://avatars.githubusercontent.com/u/26584469?v=4", + "profile": "https://github.com/SabataLunar", + "contributions": [ + "design" + ] } ], "contributorsPerLine": 7, diff --git a/CREDITS.md b/CREDITS.md index ced3f52af8..279a523ce1 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -88,6 +88,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Josh Hufford
Josh Hufford

💻 Kasenn
Kasenn

💻 + + SabataLunar
SabataLunar

🎨 + From 530812419121a4d54b8a829e3039b42555f70e30 Mon Sep 17 00:00:00 2001 From: GGbond Date: Fri, 23 Jan 2026 00:36:07 +0800 Subject: [PATCH 13/23] Align Crafty Shield with wiki and refine IsAllyProtectingFromMove (#8990) --- src/battle_ai_util.c | 57 +++++++++------- src/battle_util.c | 24 ++++++- test/battle/move_effect/protect.c | 109 ++++++++++++++++++++++++++---- 3 files changed, 146 insertions(+), 44 deletions(-) diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 21e7376cb7..70683fc420 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -1960,40 +1960,45 @@ bool32 IsAllyProtectingFromMove(u32 battlerAtk, u32 attackerMove, u32 allyMove) { return FALSE; } - else + + enum ProtectMethod protectMethod = GetMoveProtectMethod(allyMove); + + switch (protectMethod) { - enum ProtectMethod protectMethod = GetMoveProtectMethod(allyMove); - - if (protectMethod == PROTECT_QUICK_GUARD) + case PROTECT_CRAFTY_SHIELD: + if (!IsBattleMoveStatus(attackerMove)) { - u32 priority = GetBattleMovePriority(battlerAtk, gAiLogicData->abilities[battlerAtk], attackerMove); - return (priority > 0); + return FALSE; } - - if (IsBattleMoveStatus(attackerMove)) + else if (GetMoveEffect(attackerMove) == EFFECT_HOLD_HANDS) { - switch (protectMethod) - { - case PROTECT_NORMAL: - case PROTECT_CRAFTY_SHIELD: - case PROTECT_MAX_GUARD: - case PROTECT_WIDE_GUARD: - return TRUE; - - default: - return FALSE; - } + return TRUE; } else { - switch (protectMethod) - { - case PROTECT_CRAFTY_SHIELD: - return FALSE; - default: - return TRUE; - } + u32 moveTarget = GetBattlerMoveTargetType(battlerAtk, attackerMove); + return (GetBattlerSide(battlerAtk) != GetBattlerSide(BATTLE_PARTNER(battlerAtk)) + && moveTarget != MOVE_TARGET_OPPONENTS_FIELD + && moveTarget != MOVE_TARGET_ALL_BATTLERS); } + case PROTECT_WIDE_GUARD: + return IsSpreadMove(GetBattlerMoveTargetType(battlerAtk, attackerMove)); + case PROTECT_NORMAL: + case PROTECT_SPIKY_SHIELD: + case PROTECT_MAX_GUARD: + case PROTECT_BANEFUL_BUNKER: + case PROTECT_BURNING_BULWARK: + return TRUE; + case PROTECT_OBSTRUCT: + case PROTECT_SILK_TRAP: + case PROTECT_KINGS_SHIELD: + return !IsBattleMoveStatus(attackerMove); + case PROTECT_QUICK_GUARD: + return (GetChosenMovePriority(battlerAtk, gAiLogicData->abilities[battlerAtk]) > 0); + case PROTECT_MAT_BLOCK: + return !IsBattleMoveStatus(attackerMove); + default: + return FALSE; } } diff --git a/src/battle_util.c b/src/battle_util.c index b660c174ce..c209573d30 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -6635,6 +6635,26 @@ static inline bool32 IsSideProtected(u32 battler, enum ProtectMethod method) || gProtectStructs[BATTLE_PARTNER(battler)].protected == method; } +static bool32 IsCraftyShieldProtected(u32 battlerAtk, u32 battlerDef, u32 move) +{ + if (!IsBattleMoveStatus(move)) + return FALSE; + + if (!IsSideProtected(battlerDef, PROTECT_CRAFTY_SHIELD)) + return FALSE; + + if (GetMoveEffect(move) == EFFECT_HOLD_HANDS) + return TRUE; + + u32 moveTarget = GetBattlerMoveTargetType(battlerAtk, move); + if (!IsBattlerAlly(battlerAtk, battlerDef) + && moveTarget != MOVE_TARGET_OPPONENTS_FIELD + && moveTarget != MOVE_TARGET_ALL_BATTLERS) + return TRUE; + + return FALSE; +} + bool32 IsBattlerProtected(u32 battlerAtk, u32 battlerDef, u32 move) { if (gProtectStructs[battlerDef].protected == PROTECT_NONE @@ -6652,9 +6672,7 @@ bool32 IsBattlerProtected(u32 battlerAtk, u32 battlerDef, u32 move) bool32 isProtected = FALSE; - if (IsSideProtected(battlerDef, PROTECT_CRAFTY_SHIELD) - && IsBattleMoveStatus(move) - && GetMoveEffect(move) != EFFECT_COACHING) + if (IsCraftyShieldProtected(battlerAtk, battlerDef, move)) isProtected = TRUE; else if (MoveIgnoresProtect(move)) isProtected = FALSE; diff --git a/test/battle/move_effect/protect.c b/test/battle/move_effect/protect.c index 85546f2064..0f486c6405 100644 --- a/test/battle/move_effect/protect.c +++ b/test/battle/move_effect/protect.c @@ -528,7 +528,7 @@ DOUBLE_BATTLE_TEST("Protect: Quick Guard can not fail on consecutive turns (Gen6 } } -DOUBLE_BATTLE_TEST("Protect: Crafty Shield protects self and ally from status moves") +DOUBLE_BATTLE_TEST("Crafty Shield protects self and ally from opposing status moves") { u16 move = MOVE_NONE; struct BattlePokemon *targetOpponent = NULL; @@ -569,6 +569,72 @@ DOUBLE_BATTLE_TEST("Protect: Crafty Shield protects self and ally from status mo } } +DOUBLE_BATTLE_TEST("Crafty Shield does not protect against status moves used on the user's side") +{ + u32 move; + + PARAMETRIZE { move = MOVE_AROMATHERAPY; } + PARAMETRIZE { move = MOVE_ACUPRESSURE; } + + GIVEN { + ASSUME(GetMoveEffect(MOVE_AROMATHERAPY) == EFFECT_HEAL_BELL); + ASSUME(GetMoveEffect(MOVE_ACUPRESSURE) == EFFECT_ACUPRESSURE); + PLAYER(SPECIES_WOBBUFFET) { Speed(5); } + PLAYER(SPECIES_WOBBUFFET) { Speed(5); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(10); } + OPPONENT(SPECIES_WYNAUT) { Speed(5); Status1(STATUS1_BURN); } + } WHEN { + TURN { + MOVE(opponentLeft, MOVE_CRAFTY_SHIELD); + if (move == MOVE_ACUPRESSURE) + MOVE(opponentRight, move, target: opponentLeft); + else + MOVE(opponentRight, move); + } + TURN {} + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CRAFTY_SHIELD, opponentLeft); + if (move == MOVE_ACUPRESSURE) { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ACUPRESSURE, opponentRight); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); + } else { + ANIMATION(ANIM_TYPE_MOVE, MOVE_AROMATHERAPY, opponentRight); + STATUS_ICON(opponentRight, none: TRUE); + } + } +} + +DOUBLE_BATTLE_TEST("Crafty Shield does not protect against entry hazard moves") +{ + u32 move; + + PARAMETRIZE { move = MOVE_SPIKES; } + PARAMETRIZE { move = MOVE_STEALTH_ROCK; } + PARAMETRIZE { move = MOVE_TOXIC_SPIKES; } + PARAMETRIZE { move = MOVE_STICKY_WEB; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponentLeft, MOVE_CRAFTY_SHIELD); MOVE(playerLeft, move, target: opponentLeft); } + TURN {} + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CRAFTY_SHIELD, opponentLeft); + if (move == MOVE_SPIKES) { + MESSAGE("Spikes were scattered on the ground all around the opposing team!"); + } else if (move == MOVE_TOXIC_SPIKES) { + MESSAGE("Poison spikes were scattered on the ground all around the opposing team!"); + } else if (move == MOVE_STEALTH_ROCK) { + MESSAGE("Pointed stones float in the air around the opposing team!"); + } else { + MESSAGE("A sticky web has been laid out on the ground around the opposing team!"); + } + } +} + SINGLE_BATTLE_TEST("Protect: Protect does not block Confide or Decorate") { u32 move; @@ -618,6 +684,11 @@ DOUBLE_BATTLE_TEST("Crafty Shield protects self and ally from Confide and Decora DOUBLE_BATTLE_TEST("Crafty Shield does not protect against moves that target all battlers") { + u32 move; + + PARAMETRIZE { move = MOVE_FLOWER_SHIELD; } + PARAMETRIZE { move = MOVE_PERISH_SONG; } + GIVEN { ASSUME(GetSpeciesType(SPECIES_TANGELA, 0) == TYPE_GRASS); ASSUME(GetSpeciesType(SPECIES_TANGROWTH, 0) == TYPE_GRASS); @@ -628,21 +699,29 @@ DOUBLE_BATTLE_TEST("Crafty Shield does not protect against moves that target all OPPONENT(SPECIES_SUNKERN); OPPONENT(SPECIES_SUNFLORA); } WHEN { - TURN { MOVE(opponentLeft, MOVE_CRAFTY_SHIELD); MOVE(opponentRight, MOVE_CELEBRATE); MOVE(playerLeft, MOVE_FLOWER_SHIELD); MOVE(playerRight, MOVE_CELEBRATE); } + TURN { MOVE(opponentLeft, MOVE_CRAFTY_SHIELD); MOVE(opponentRight, MOVE_CELEBRATE); MOVE(playerLeft, move); MOVE(playerRight, MOVE_CELEBRATE); } } SCENE { - MESSAGE("Tangela used Flower Shield!"); - ANIMATION(ANIM_TYPE_MOVE, MOVE_FLOWER_SHIELD, playerLeft); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); - MESSAGE("Tangela's Defense rose!"); - ANIMATION(ANIM_TYPE_MOVE, MOVE_FLOWER_SHIELD, playerLeft); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); - MESSAGE("The opposing Sunkern's Defense rose!"); - ANIMATION(ANIM_TYPE_MOVE, MOVE_FLOWER_SHIELD, playerLeft); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); - MESSAGE("Tangrowth's Defense rose!"); - ANIMATION(ANIM_TYPE_MOVE, MOVE_FLOWER_SHIELD, playerLeft); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight); - MESSAGE("The opposing Sunflora's Defense rose!"); + if (move == MOVE_FLOWER_SHIELD) { + MESSAGE("Tangela used Flower Shield!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FLOWER_SHIELD, playerLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); + MESSAGE("Tangela's Defense rose!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FLOWER_SHIELD, playerLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); + MESSAGE("The opposing Sunkern's Defense rose!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FLOWER_SHIELD, playerLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); + MESSAGE("Tangrowth's Defense rose!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FLOWER_SHIELD, playerLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight); + MESSAGE("The opposing Sunflora's Defense rose!"); + } else { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PERISH_SONG, playerLeft); + NONE_OF { + MESSAGE("The opposing Sunkern protected itself!"); + MESSAGE("The opposing Sunflora protected itself!"); + } + } } } From 7722ebf47048d07342e9f767254a56043af4e1d4 Mon Sep 17 00:00:00 2001 From: PhallenTree <168426989+PhallenTree@users.noreply.github.com> Date: Fri, 23 Jan 2026 22:28:22 +0000 Subject: [PATCH 14/23] Fixes Hyperspace Fury not breaking protection (#8999) --- src/battle_script_commands.c | 2 +- src/data/moves_info.h | 4 +- test/battle/move_effect/hyperspace_fury.c | 4 - .../move_effects_combined/hyperspace_fury.c | 97 +++++++++++++++++++ 4 files changed, 101 insertions(+), 6 deletions(-) delete mode 100644 test/battle/move_effect/hyperspace_fury.c create mode 100644 test/battle/move_effects_combined/hyperspace_fury.c diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 0dee3f9320..83d665a06d 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -3471,7 +3471,7 @@ void SetMoveEffect(u32 battler, u32 effectBattler, enum MoveEffect moveEffect, c if (i) { BattleScriptPush(battleScript); - if (gCurrentMove == MOVE_HYPERSPACE_FURY) + if (GetMoveEffect(gCurrentMove) == EFFECT_HYPERSPACE_FURY) gBattlescriptCurrInstr = BattleScript_HyperspaceFuryRemoveProtect; else gBattlescriptCurrInstr = BattleScript_MoveEffectFeint; diff --git a/src/data/moves_info.h b/src/data/moves_info.h index 312a2a2e00..31db4e424f 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -15981,7 +15981,9 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .metronomeBanned = TRUE, .sketchBanned = (B_SKETCH_BANS >= GEN_9), .additionalEffects = ADDITIONAL_EFFECTS({ - // Feint move effect handled in script as it goes before animation + .moveEffect = MOVE_EFFECT_FEINT, // TODO: Is this supposed to happen before the attack animation? + }, + { .moveEffect = MOVE_EFFECT_DEF_MINUS_1, .self = TRUE, }), diff --git a/test/battle/move_effect/hyperspace_fury.c b/test/battle/move_effect/hyperspace_fury.c deleted file mode 100644 index 080758c94b..0000000000 --- a/test/battle/move_effect/hyperspace_fury.c +++ /dev/null @@ -1,4 +0,0 @@ -#include "global.h" -#include "test/battle.h" - -TO_DO_BATTLE_TEST("TODO: Write Hyperspace Fury (Move Effect) test titles") diff --git a/test/battle/move_effects_combined/hyperspace_fury.c b/test/battle/move_effects_combined/hyperspace_fury.c new file mode 100644 index 0000000000..5c0a865c2b --- /dev/null +++ b/test/battle/move_effects_combined/hyperspace_fury.c @@ -0,0 +1,97 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(GetMoveEffect(MOVE_HYPERSPACE_FURY) == EFFECT_HYPERSPACE_FURY); + ASSUME(MoveHasAdditionalEffect(MOVE_HYPERSPACE_FURY, MOVE_EFFECT_FEINT)); + ASSUME(MoveHasAdditionalEffectSelf(MOVE_HYPERSPACE_FURY, MOVE_EFFECT_DEF_MINUS_1)); + ASSUME(GetMoveEffect(MOVE_PROTECT) == EFFECT_PROTECT); +} + +SINGLE_BATTLE_TEST("Hyperspace Fury fails if used by a Pokémon other than Hoopa Unbound") +{ + u32 species; + PARAMETRIZE { species = SPECIES_WOBBUFFET; } + PARAMETRIZE { species = SPECIES_HOOPA_CONFINED; } + PARAMETRIZE { species = SPECIES_HOOPA_UNBOUND; } + + GIVEN { + PLAYER(species); + OPPONENT(SPECIES_REGIROCK); + } WHEN { + TURN { MOVE(player, MOVE_HYPERSPACE_FURY); } + } SCENE { + switch (species) + { + case SPECIES_HOOPA_UNBOUND: + ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPERSPACE_FURY, player); + break; + case SPECIES_HOOPA_CONFINED: + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPERSPACE_FURY, player); + MESSAGE("But Hoopa can't use it the way it is now!"); + break; + case SPECIES_WOBBUFFET: + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPERSPACE_FURY, player); + MESSAGE("But Wobbuffet can't use the move!"); + break; + default: + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPERSPACE_FURY, player); + break; + } + } +} + +DOUBLE_BATTLE_TEST("Hyperspace Fury hits the target through Protect and breaks it") +{ + GIVEN { + PLAYER(SPECIES_HOOPA_UNBOUND); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_REGIROCK); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(opponentLeft, MOVE_PROTECT); MOVE(playerLeft, MOVE_HYPERSPACE_FURY, target: opponentLeft); MOVE(playerRight, MOVE_SCRATCH, target: opponentLeft); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PROTECT, opponentLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPERSPACE_FURY, playerLeft); + HP_BAR(opponentLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, playerRight); + HP_BAR(opponentLeft); + } +} + +SINGLE_BATTLE_TEST("Hyperspace Fury lowers the user's Defense by 1 stage after hitting the target") +{ + GIVEN { + PLAYER(SPECIES_HOOPA_UNBOUND); + OPPONENT(SPECIES_REGIROCK); + } WHEN { + TURN { MOVE(player, MOVE_HYPERSPACE_FURY); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPERSPACE_FURY, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + } THEN { + EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE - 1); + } +} + +DOUBLE_BATTLE_TEST("Hyperspace Fury breaks protection and lowers the user's Defense by 1 stage") +{ + GIVEN { + PLAYER(SPECIES_HOOPA_UNBOUND); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_REGIROCK); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(opponentLeft, MOVE_PROTECT); MOVE(playerLeft, MOVE_HYPERSPACE_FURY, target: opponentLeft); MOVE(playerRight, MOVE_SCRATCH, target: opponentLeft); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PROTECT, opponentLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPERSPACE_FURY, playerLeft); + HP_BAR(opponentLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, playerRight); + HP_BAR(opponentLeft); + } THEN { + EXPECT_EQ(playerLeft->statStages[STAT_DEF], DEFAULT_STAT_STAGE - 1); + } +} From e871913e22322f66f59763fc8d0d3f78d35f9620 Mon Sep 17 00:00:00 2001 From: GGbond Date: Sat, 24 Jan 2026 15:45:19 +0800 Subject: [PATCH 15/23] Fix Magnetic Flux target handling in singles (#9007) --- data/battle_scripts_1.s | 6 ++++ test/battle/ai/ai_doubles.c | 26 ++++++++++++++ test/battle/move_effect/gear_up.c | 46 ++++++++++++++++++++----- test/battle/move_effect/magnetic_flux.c | 46 ++++++++++++++++++++----- 4 files changed, 108 insertions(+), 16 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index b26d2905da..7e9e0549c0 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -1259,6 +1259,7 @@ BattleScript_EffectAromaticMistWontGoHigher: BattleScript_EffectMagneticFlux:: attackcanceler + savetarget setbyte gBattleCommunication, 0 BattleScript_EffectMagneticFluxStart: jumpifability BS_TARGET, ABILITY_MINUS, BattleScript_EffectMagneticFluxCheckStats @@ -1287,13 +1288,16 @@ BattleScript_EffectMagneticFluxTrySpDef: waitmessage B_WAIT_TIME_LONG BattleScript_EffectMagneticFluxLoop: jumpifbytenotequal gBattlerTarget, gBattlerAttacker, BattleScript_EffectMagneticFluxEnd + jumpifnoally BS_ATTACKER, BattleScript_EffectMagneticFluxEnd setallytonexttarget BattleScript_EffectMagneticFluxStart BattleScript_EffectMagneticFluxEnd: + restoretarget jumpifbyte CMP_NOT_EQUAL, gBattleCommunication, 0, BattleScript_MoveEnd goto BattleScript_ButItFailed BattleScript_EffectGearUp:: attackcanceler + savetarget setbyte gBattleCommunication, 0 BattleScript_EffectGearUpStart: jumpifability BS_TARGET, ABILITY_MINUS, BattleScript_EffectGearUpCheckStats @@ -1322,8 +1326,10 @@ BattleScript_EffectGearUpTrySpAtk: waitmessage B_WAIT_TIME_LONG BattleScript_EffectGearUpLoop: jumpifbytenotequal gBattlerTarget, gBattlerAttacker, BattleScript_EffectGearUpEnd + jumpifnoally BS_ATTACKER, BattleScript_EffectGearUpEnd setallytonexttarget BattleScript_EffectGearUpStart BattleScript_EffectGearUpEnd: + restoretarget jumpifbyte CMP_NOT_EQUAL, gBattleCommunication, 0, BattleScript_MoveEnd goto BattleScript_ButItFailed diff --git a/test/battle/ai/ai_doubles.c b/test/battle/ai/ai_doubles.c index 3aaa0ed30a..955c82b9f9 100644 --- a/test/battle/ai/ai_doubles.c +++ b/test/battle/ai/ai_doubles.c @@ -880,3 +880,29 @@ AI_DOUBLE_BATTLE_TEST("AI prefers to Fake Out the opponent vulnerable to flinchi TURN { EXPECT_MOVE(opponentLeft, MOVE_FAKE_OUT, target:playerRight); } } } + +AI_DOUBLE_BATTLE_TEST("AI uses Gear Up") +{ + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); } + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); } + OPPONENT(SPECIES_KLINKLANG) { Ability(ABILITY_PLUS); Moves(MOVE_GEAR_UP, MOVE_WATER_GUN, MOVE_POUND); } + OPPONENT(SPECIES_KLINKLANG) { Ability(ABILITY_PLUS); Moves(MOVE_GEAR_UP, MOVE_WATER_GUN, MOVE_POUND); } + } WHEN { + TURN { EXPECT_MOVE(opponentLeft, MOVE_GEAR_UP); } + } +} + +AI_DOUBLE_BATTLE_TEST("AI uses Magnetic Flux") +{ + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); } + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); } + OPPONENT(SPECIES_KLINK) { Ability(ABILITY_PLUS); Moves(MOVE_MAGNETIC_FLUX, MOVE_POUND); } + OPPONENT(SPECIES_KLINK) { Ability(ABILITY_PLUS); Moves(MOVE_MAGNETIC_FLUX, MOVE_POUND); } + } WHEN { + TURN { EXPECT_MOVE(opponentLeft, MOVE_MAGNETIC_FLUX); } + } +} diff --git a/test/battle/move_effect/gear_up.c b/test/battle/move_effect/gear_up.c index 9a9bdab79c..9cac73d694 100644 --- a/test/battle/move_effect/gear_up.c +++ b/test/battle/move_effect/gear_up.c @@ -1,17 +1,47 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("Gear Up increases the Attack and Sp. Attack of the user and allies if they have Plus or Minus") +ASSUMPTIONS +{ + ASSUME(GetMoveEffect(MOVE_GEAR_UP) == EFFECT_GEAR_UP); +} -AI_DOUBLE_BATTLE_TEST("AI uses Gear Up") +SINGLE_BATTLE_TEST("Gear Up raises Attack and Sp. Attack of the user with Plus/Minus in singles") { GIVEN { - AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT); - PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); } - PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); } - OPPONENT(SPECIES_KLINKLANG) { Ability(ABILITY_PLUS); Moves(MOVE_GEAR_UP, MOVE_WATER_GUN, MOVE_POUND); } - OPPONENT(SPECIES_KLINKLANG) { Ability(ABILITY_PLUS); Moves(MOVE_GEAR_UP, MOVE_WATER_GUN, MOVE_POUND); } + PLAYER(SPECIES_PLUSLE) { Ability(ABILITY_PLUS); } + OPPONENT(SPECIES_MINUN) { Ability(ABILITY_MINUS); } } WHEN { - TURN { EXPECT_MOVE(opponentLeft, MOVE_GEAR_UP); } + TURN { MOVE(player, MOVE_GEAR_UP); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_GEAR_UP, player); + } THEN { + EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE); + } +} + +DOUBLE_BATTLE_TEST("Gear Up raises Attack and Sp. Attack of all Plus/Minus allies in doubles") +{ + GIVEN { + PLAYER(SPECIES_PLUSLE) { Ability(ABILITY_PLUS); } + PLAYER(SPECIES_MINUN) { Ability(ABILITY_MINUS); } + OPPONENT(SPECIES_PLUSLE) { Ability(ABILITY_PLUS); } + OPPONENT(SPECIES_MINUN) { Ability(ABILITY_MINUS); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_GEAR_UP); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_GEAR_UP, playerLeft); + } THEN { + EXPECT_EQ(playerLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(playerLeft->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(playerRight->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(playerRight->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(opponentLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponentLeft->statStages[STAT_SPATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponentRight->statStages[STAT_ATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponentRight->statStages[STAT_SPATK], DEFAULT_STAT_STAGE); } } diff --git a/test/battle/move_effect/magnetic_flux.c b/test/battle/move_effect/magnetic_flux.c index 7bbcd52754..ac969a9dd2 100644 --- a/test/battle/move_effect/magnetic_flux.c +++ b/test/battle/move_effect/magnetic_flux.c @@ -1,17 +1,47 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("TODO: Write Magnetic Flux (Move Effect) test titles") +ASSUMPTIONS +{ + ASSUME(GetMoveEffect(MOVE_MAGNETIC_FLUX) == EFFECT_MAGNETIC_FLUX); +} -AI_DOUBLE_BATTLE_TEST("AI uses Magnetic Flux") +SINGLE_BATTLE_TEST("Magnetic Flux raises Defense and Sp. Defense of the user with Plus/Minus in singles") { GIVEN { - AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT); - PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); } - PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_POUND, MOVE_CELEBRATE); } - OPPONENT(SPECIES_KLINK) { Ability(ABILITY_PLUS); Moves(MOVE_MAGNETIC_FLUX, MOVE_POUND); } - OPPONENT(SPECIES_KLINK) { Ability(ABILITY_PLUS); Moves(MOVE_MAGNETIC_FLUX, MOVE_POUND); } + PLAYER(SPECIES_PLUSLE) { Ability(ABILITY_PLUS); } + OPPONENT(SPECIES_MINUN) { Ability(ABILITY_MINUS); } } WHEN { - TURN { EXPECT_MOVE(opponentLeft, MOVE_MAGNETIC_FLUX); } + TURN { MOVE(player, MOVE_MAGNETIC_FLUX); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_MAGNETIC_FLUX, player); + } THEN { + EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(player->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE); + } +} + +DOUBLE_BATTLE_TEST("Magnetic Flux raises Defense and Sp. Defense of all Plus/Minus allies in doubles") +{ + GIVEN { + PLAYER(SPECIES_PLUSLE) { Ability(ABILITY_PLUS); } + PLAYER(SPECIES_MINUN) { Ability(ABILITY_MINUS); } + OPPONENT(SPECIES_PLUSLE) { Ability(ABILITY_PLUS); } + OPPONENT(SPECIES_MINUN) { Ability(ABILITY_MINUS); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_MAGNETIC_FLUX); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_MAGNETIC_FLUX, playerLeft); + } THEN { + EXPECT_EQ(playerLeft->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(playerLeft->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(playerRight->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(playerRight->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(opponentLeft->statStages[STAT_DEF], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponentLeft->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponentRight->statStages[STAT_DEF], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponentRight->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE); } } From 89da62b02d05a62e90aef0ca100a655522e4a09e Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Sun, 25 Jan 2026 06:12:41 -0300 Subject: [PATCH 16/23] Remove trailing whitespace - Master (#9011) --- .../ISSUE_TEMPLATE/01_battle_engine_bugs.yaml | 8 +-- .../ISSUE_TEMPLATE/02_battle_ai_issues.yaml | 8 +-- .../ISSUE_TEMPLATE/03_feature_requests.yaml | 4 +- .github/ISSUE_TEMPLATE/04_other_errors.yaml | 8 +-- CONTRIBUTING.md | 12 ++-- README.md | 4 +- asm/macros/m4a.inc | 2 +- audio_rules.mk | 6 +- data/battle_anim_scripts.s | 24 +++---- docs/STYLEGUIDE.md | 4 +- docs/changelogs/1.12.x/1.12.2.md | 12 ++-- docs/changelogs/1.14.x/1.14.0.md | 2 +- docs/changelogs/1.9.x/1.9.2.md | 2 +- docs/team_procedures/expansion_versions.md | 4 +- docs/team_procedures/merge_checklist.md | 4 +- docs/team_procedures/schedule.md | 10 +-- docs/team_procedures/scope.md | 2 +- docs/tutorials/ai_flags.md | 4 +- docs/tutorials/dns.md | 2 +- docs/tutorials/how_to_code_entry.md | 8 +-- docs/tutorials/how_to_follower_npc.md | 12 ++-- docs/tutorials/how_to_new_pokemon.md | 2 +- docs/tutorials/how_to_new_trainer_slide.md | 12 ++-- docs/tutorials/how_to_testing_system.md | 2 +- .../how_to_time_of_day_encounters.md | 4 +- docs/tutorials/vs_seeker.md | 14 ++-- include/constants/weather.h | 2 +- include/gametypes.h | 64 +++++++++---------- .../add_time_based_encounters.py | 2 +- src/battle_anim_ice.c | 4 +- src/battle_anim_rock.c | 4 +- src/battle_anim_water.c | 56 ++++++++-------- src/battle_controller_recorded_player.c | 2 +- src/battle_transition.c | 16 ++--- src/datetime.c | 2 +- src/field_player_avatar.c | 6 +- src/field_region_map.c | 2 +- src/follower_npc.c | 16 ++--- src/frontier_pass.c | 2 +- src/naming_screen.c | 4 +- src/pokemon_sprite_visualizer.c | 2 +- src/pokemon_summary_screen.c | 4 +- src/pokenav_region_map.c | 4 +- src/scrcmd.c | 6 +- src/type_icons.c | 3 +- test/battle/ai/ai_multi.c | 2 +- test/battle/ai/can_use_all_moves.c | 2 +- 47 files changed, 189 insertions(+), 192 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml b/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml index a8e9c9326a..0c918c334b 100644 --- a/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml +++ b/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml @@ -7,7 +7,7 @@ body: value: | Please fill in all fields with as many details as possible. Once your bug is posted, make sure you and your collaborators are added to `CREDITS.md` by [tagging the bot on GitHub](https://github.com/rh-hideout/pokeemerald-expansion/wiki/CREDITS.md-Frequently-Asked-Questions). EVERY contribution matters, even reporting bugs! - - type: textarea + - type: textarea id: description attributes: label: Description @@ -24,13 +24,13 @@ body: description: | What exact steps can somebody else follow in order to recreate the issue on their own? placeholder: | - Provide as much context as possible as to what was done to create the issue. + Provide as much context as possible as to what was done to create the issue. validations: required: true - type: textarea - id: media + id: media attributes: - label: Images / Video + label: Images / Video description: | Do you have images or videos to show the problem happen? placeholder: | diff --git a/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml b/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml index e6762925b4..b172a867aa 100644 --- a/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml +++ b/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml @@ -24,13 +24,13 @@ body: description: | What exact steps can somebody else follow in order to recreate the issue on their own? placeholder: | - Provide as much context as possible as to what was done to create the issue. + Provide as much context as possible as to what was done to create the issue. validations: - required: true + required: true - type: textarea - id: media + id: media attributes: - label: Images / Video + label: Images / Video description: | Do you have images or videos to show the problem happen? placeholder: | diff --git a/.github/ISSUE_TEMPLATE/03_feature_requests.yaml b/.github/ISSUE_TEMPLATE/03_feature_requests.yaml index 347e782660..e58227b112 100644 --- a/.github/ISSUE_TEMPLATE/03_feature_requests.yaml +++ b/.github/ISSUE_TEMPLATE/03_feature_requests.yaml @@ -15,13 +15,13 @@ body: description: | What is the current behavior? What behavior would you expect your feature request to provide? What other information can you provide to help your feature get implemented? placeholder: | - Provide as much context as possible. + Provide as much context as possible. validations: required: true - type: textarea id: media attributes: - label: Images / Video + label: Images / Video description: | Have other projects or games solved this problem? Do you have images or video to show this happening? placeholder: | diff --git a/.github/ISSUE_TEMPLATE/04_other_errors.yaml b/.github/ISSUE_TEMPLATE/04_other_errors.yaml index 8fa4e94564..c69e2c1052 100644 --- a/.github/ISSUE_TEMPLATE/04_other_errors.yaml +++ b/.github/ISSUE_TEMPLATE/04_other_errors.yaml @@ -24,13 +24,13 @@ body: description: | What exact steps can somebody else follow in order to recreate the issue on their own? placeholder: | - Provide as much context as possible as to what was done to create the issue. + Provide as much context as possible as to what was done to create the issue. validations: - required: false + required: false - type: textarea - id: media + id: media attributes: - label: Images / Video + label: Images / Video description: | Do you have images or videos to show the problem happen? placeholder: | diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 49517cfff3..06c302c4c8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,7 +6,7 @@ All contributions are encouraged and valued. Please make sure to read the releva ## Bug Reports -We use [GitHub](https://github.com/rh-hideout/pokeemerald-expansion/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen+label%3Abug) issues to track bugs. +We use [GitHub](https://github.com/rh-hideout/pokeemerald-expansion/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen+label%3Abug) issues to track bugs. ### What should I do before making a bug report? @@ -15,7 +15,7 @@ We use [GitHub](https://github.com/rh-hideout/pokeemerald-expansion/issues?q=sor ### How do I submit a bug report? -If you run into an issue with the project, open an [issue](https://github.com/rh-hideout/pokeemerald-expansion/issues/new). +If you run into an issue with the project, open an [issue](https://github.com/rh-hideout/pokeemerald-expansion/issues/new). The best bug reports have enough information that we won't have to contact you for more information. We welcome all efforts to improve pokeemerald-expansion, but would be very grateful if you completed as much of the checklist as possible in your bug report. This will help other contributiors fix your issue. @@ -31,7 +31,7 @@ The best bug reports have enough information that we won't have to contact you f This section guides you through submitting a feature request for pokeemerald-expansion, **including completely new features and minor improvements to existing functionality**. Following these guidelines will help maintainers and the community to understand your suggestion and find related suggestions. -- We use [GitHub](https://github.com/rh-hideout/pokeemerald-expansion/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen+label%3Afeature-request) issues to track feature requests. +- We use [GitHub](https://github.com/rh-hideout/pokeemerald-expansion/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen+label%3Afeature-request) issues to track feature requests. ### What should I do before making a feature request? @@ -44,7 +44,7 @@ This section guides you through submitting a feature request for pokeemerald-exp ### How do I submit a feature request? -To request a feature to be added to the project, open a [feature request](https://github.com/rh-hideout/pokeemerald-expansion/issues/new). +To request a feature to be added to the project, open a [feature request](https://github.com/rh-hideout/pokeemerald-expansion/issues/new). ### What happens after I submit a feature request? @@ -108,11 +108,11 @@ git push --set-upstream origin newFeature ``` #### 7. Open Pull Request -Once your work is complete and pushed to the branch on Github, you can open a [pull request from your branch](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork), targeting the branch you've chosen from `pokeemerald-expansion`. Please fill out the pull request description as completely as possible. +Once your work is complete and pushed to the branch on Github, you can open a [pull request from your branch](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork), targeting the branch you've chosen from `pokeemerald-expansion`. Please fill out the pull request description as completely as possible. ### What happens after I submit a pull request? -A maintainer will then assign themselves as a reviewer of your pull request, and may provide feedback in the form of a PR review. +A maintainer will then assign themselves as a reviewer of your pull request, and may provide feedback in the form of a PR review. Contributors are responsible for responding to and updating their branch by addressing the feedback in the review. Contributors are also responsible for making sure the branch passes the checklist at all times. diff --git a/README.md b/README.md index c74b22fa29..c9a538c49a 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ -**`pokeemerald-expansion`** is a GBA ROM hack base that equips developers with a comprehensive toolkit for creating Pokémon ROM hacks. **`pokeemerald-expansion`** is built on top of [pret's `pokeemerald`](https://github.com/pret/pokeemerald) decompilation project. **It is not a playable Pokémon game on its own.** +**`pokeemerald-expansion`** is a GBA ROM hack base that equips developers with a comprehensive toolkit for creating Pokémon ROM hacks. **`pokeemerald-expansion`** is built on top of [pret's `pokeemerald`](https://github.com/pret/pokeemerald) decompilation project. **It is not a playable Pokémon game on its own.** # [Features](FEATURES.md) @@ -30,7 +30,7 @@ Please consider [crediting all contributors](CREDITS.md) involved in the project # [Getting Started](INSTALL.md) -❗❗ **Important**: Do not use GitHub's "Download Zip" option as it will not include commit history. This is necessary if you want to update or merge other feature branches. +❗❗ **Important**: Do not use GitHub's "Download Zip" option as it will not include commit history. This is necessary if you want to update or merge other feature branches. If you're new to git and GitHub, [Team Aqua's Asset Repo](https://github.com/Pawkkie/Team-Aquas-Asset-Repo/) has a [guide to forking and cloning the repository](https://github.com/Pawkkie/Team-Aquas-Asset-Repo/wiki/The-Basics-of-GitHub). Then you can follow one of the following guides: diff --git a/asm/macros/m4a.inc b/asm/macros/m4a.inc index 0169a3c033..f405206aca 100644 --- a/asm/macros/m4a.inc +++ b/asm/macros/m4a.inc @@ -15,7 +15,7 @@ .macro voice_group label:req, starting_note .align 2 .ifb \starting_note - .global voicegroup_\label + .global voicegroup_\label voicegroup_\label: .else .set voicegroup_\label, . - \starting_note * 0xC diff --git a/audio_rules.mk b/audio_rules.mk index 70d4466409..808f51a61c 100644 --- a/audio_rules.mk +++ b/audio_rules.mk @@ -9,7 +9,7 @@ SOUND_BIN_DIR := sound # Needs to recompile for B_NUM_LOW_HEALTH_BEEPS in battle.h EXPANSION_BATTLE_CONFIG := include/config/battle.h -SPECIAL_OUTDIRS := $(MID_ASM_DIR) $(CRY_BIN_DIR) +SPECIAL_OUTDIRS := $(MID_ASM_DIR) $(CRY_BIN_DIR) SPECIAL_OUTDIRS += $(SOUND_BIN_DIR) $(SOUND_BIN_DIR)/direct_sound_samples/phonemes $(SOUND_BIN_DIR)/direct_sound_samples/cries $(shell mkdir -p $(SPECIAL_OUTDIRS) ) @@ -25,11 +25,11 @@ $(CRY_BIN_DIR)/%.bin: $(CRY_SUBDIR)/%.wav $(WAV2AGB) -b -c -l 1 --no-pad $< $@ # Uncompressed cries -$(CRY_BIN_DIR)/uncomp_%.bin: $(CRY_SUBDIR)/uncomp_%.aif +$(CRY_BIN_DIR)/uncomp_%.bin: $(CRY_SUBDIR)/uncomp_%.aif $(AIF) $< $@ # Uncompressed sounds -$(SOUND_BIN_DIR)/%.bin: sound/%.wav +$(SOUND_BIN_DIR)/%.bin: sound/%.wav $(WAV2AGB) -b $< $@ # For each line in midi.cfg, we do some trickery to convert it into a make rule for the `.mid` file described on the line diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index 7344b2d3e6..220346b67a 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -1984,15 +1984,15 @@ gBattleAnimMove_IceShard:: setalpha 12, 8 loadspritegfx ANIM_TAG_ICE_CRYSTALS loadspritegfx ANIM_TAG_IMPACT - createsprite gIceShardSpriteTemplate, ANIM_ATTACKER, 2, 3, 0, 30 + createsprite gIceShardSpriteTemplate, ANIM_ATTACKER, 2, 3, 0, 30 delay 1 createsprite gIceShardSpriteTemplate, ANIM_ATTACKER, 2, 3, 0, -20 delay 1 - createsprite gIceShardSpriteTemplate, ANIM_ATTACKER, 2, 3, 0, 30 + createsprite gIceShardSpriteTemplate, ANIM_ATTACKER, 2, 3, 0, 30 delay 1 createsprite gIceShardSpriteTemplate, ANIM_ATTACKER, 2, 3, 0, -20 delay 1 - createsprite gIceShardSpriteTemplate, ANIM_ATTACKER, 2, 3, 0, 30 + createsprite gIceShardSpriteTemplate, ANIM_ATTACKER, 2, 3, 0, 30 delay 4 createsprite gSimplePaletteBlendSpriteTemplate, ANIM_ATTACKER, 2, F_PAL_TARGET, -31, 0, 7, RGB(0, 20, 31) createvisualtask AnimTask_ShakeMon2, 2, ANIM_TARGET, 2, 0, 25, 1 @@ -19861,36 +19861,36 @@ TeraBlastBug: waitforvisualfinish monbg ANIM_DEF_PARTNER call SetBugBg - createsprite gMaxFlutterbyButterflySpriteTemplate, ANIM_ATTACKER, 2, 3, 0, 30 + createsprite gMaxFlutterbyButterflySpriteTemplate, ANIM_ATTACKER, 2, 3, 0, 30 delay 1 createsprite gMaxFlutterbyButterflySpriteTemplate, ANIM_ATTACKER, 2, 3, 0, -20 delay 1 createvisualtask AnimTask_ShakeMon, 5, ANIM_TARGET, 3, 0, 50, 1 - createsprite gMaxFlutterbyButterflySpriteTemplate, ANIM_ATTACKER, 2, 3, 0, 30 + createsprite gMaxFlutterbyButterflySpriteTemplate, ANIM_ATTACKER, 2, 3, 0, 30 delay 1 createsprite gMaxFlutterbyButterflySpriteTemplate, ANIM_ATTACKER, 2, 3, 0, -20 delay 1 - createsprite gMaxFlutterbyButterflySpriteTemplate, ANIM_ATTACKER, 2, 3, 0, 30 + createsprite gMaxFlutterbyButterflySpriteTemplate, ANIM_ATTACKER, 2, 3, 0, 30 delay 1 createsprite gMaxFlutterbyButterflySpriteTemplate, ANIM_ATTACKER, 2, 3, 0, -20 delay 1 - createsprite gMaxFlutterbyButterflySpriteTemplate, ANIM_ATTACKER, 2, 3, 0, 30 + createsprite gMaxFlutterbyButterflySpriteTemplate, ANIM_ATTACKER, 2, 3, 0, 30 delay 1 createsprite gMaxFlutterbyButterflySpriteTemplate, ANIM_ATTACKER, 2, 3, 0, -20 delay 1 - createsprite gMaxFlutterbyButterflySpriteTemplate, ANIM_ATTACKER, 2, 3, 0, 30 + createsprite gMaxFlutterbyButterflySpriteTemplate, ANIM_ATTACKER, 2, 3, 0, 30 delay 1 createsprite gMaxFlutterbyButterflySpriteTemplate, ANIM_ATTACKER, 2, 3, 0, -20 delay 1 - createsprite gMaxFlutterbyButterflySpriteTemplate, ANIM_ATTACKER, 2, 3, 0, 30 + createsprite gMaxFlutterbyButterflySpriteTemplate, ANIM_ATTACKER, 2, 3, 0, 30 delay 1 createsprite gMaxFlutterbyButterflySpriteTemplate, ANIM_ATTACKER, 2, 3, 0, -20 delay 1 - createsprite gMaxFlutterbyButterflySpriteTemplate, ANIM_ATTACKER, 2, 3, 0, 30 + createsprite gMaxFlutterbyButterflySpriteTemplate, ANIM_ATTACKER, 2, 3, 0, 30 delay 1 createsprite gMaxFlutterbyButterflySpriteTemplate, ANIM_ATTACKER, 2, 3, 0, -20 delay 1 - createsprite gMaxFlutterbyButterflySpriteTemplate, ANIM_ATTACKER, 2, 3, 0, 30 + createsprite gMaxFlutterbyButterflySpriteTemplate, ANIM_ATTACKER, 2, 3, 0, 30 delay 1 createsprite gMaxFlutterbyButterflySpriteTemplate, ANIM_ATTACKER, 2, 3, 0, -20 createvisualtask AnimTask_ShakeMon, 2, ANIM_TARGET, 0, 3, 50, 1 @@ -20994,7 +20994,7 @@ OrderUpStretchy: playsewithpan SE_M_DOUBLE_SLAP, SOUND_PAN_TARGET waitforvisualfinish end - + gBattleAnimMove_MatchaGotcha:: loadspritegfx ANIM_TAG_POISON_BUBBLE playsewithpan SE_M_SWIFT, SOUND_PAN_ATTACKER diff --git a/docs/STYLEGUIDE.md b/docs/STYLEGUIDE.md index dc99be6b1b..9535ce4fe3 100644 --- a/docs/STYLEGUIDE.md +++ b/docs/STYLEGUIDE.md @@ -64,7 +64,7 @@ When describing a system/component in-depth, use block comment syntax. ``` When briefly describing a function or block of code, use a single-line comments -placed on its own line. +placed on its own line. There should be a single space directly to the right of `//`. ```c @@ -404,7 +404,7 @@ All other configs should be off. ### Save Philosophy -Until [save migration](https://discord.com/channels/419213663107416084/1108733346864963746) is implemented, branches will only merged in if they do not forcefully break existing game saves. +Until [save migration](https://discord.com/channels/419213663107416084/1108733346864963746) is implemented, branches will only merged in if they do not forcefully break existing game saves. When `pokemeerald-expansion` gets to a point where new functionality will require that we break saves, we will merge as many [save-breaking features](https://discord.com/channels/419213663107416084/1202774957776441427) together as possible, and increment the major version number of the project. diff --git a/docs/changelogs/1.12.x/1.12.2.md b/docs/changelogs/1.12.x/1.12.2.md index 9f1b210638..229eff250b 100644 --- a/docs/changelogs/1.12.x/1.12.2.md +++ b/docs/changelogs/1.12.x/1.12.2.md @@ -53,8 +53,8 @@ * Fixed text width for a lot of forms in HGSS Dex by @AsparagusEduardo in [#7035](https://github.com/rh-hideout/pokeemerald-expansion/pull/7035) * Fixes Roamers not saving shininess by @i0brendan0 in [#7185](https://github.com/rh-hideout/pokeemerald-expansion/pull/7185) * [FIX] Prevent caught Pokémon loss in NPC partner battles by @J2M2 in [#7177](https://github.com/rh-hideout/pokeemerald-expansion/pull/7177) - - + + ## ⚔️ Battle General ⚔️ ### Changed @@ -72,8 +72,8 @@ * Chloroblast causing recoil damage even if the move fails to connect by @LinathanZel in #7007 - - + + * [DRAFT] Fix Normalize not boosting Normal type moves if they were already Normal type by @i0brendan0 in [#7060](https://github.com/rh-hideout/pokeemerald-expansion/pull/7060) * Fixes freeze during a 1v2 double battle by @AlexOn1ine in [#7075](https://github.com/rh-hideout/pokeemerald-expansion/pull/7075) * Fixes Pursuit potentially causing both battlers to switch into the same mon by @PhallenTree in [#7084](https://github.com/rh-hideout/pokeemerald-expansion/pull/7084) @@ -93,8 +93,8 @@ ## 🤹 Moves 🤹 ### Changed * Fix ScaryFace anim for Bitter Malice by @TLM-PsIQ in [#6476](https://github.com/rh-hideout/pokeemerald-expansion/pull/6476) - - + + ### Fixed * Fix savage spin out spider web template by @ghoulslash in [#7137](https://github.com/rh-hideout/pokeemerald-expansion/pull/7137) diff --git a/docs/changelogs/1.14.x/1.14.0.md b/docs/changelogs/1.14.x/1.14.0.md index 3a75d35e6e..3e38d0a30a 100644 --- a/docs/changelogs/1.14.x/1.14.0.md +++ b/docs/changelogs/1.14.x/1.14.0.md @@ -32,7 +32,7 @@ * Add new actions to Debug Menu by @FosterProgramming in [#7837](https://github.com/rh-hideout/pokeemerald-expansion/pull/7837) - Adds an action to change a Pokemon ability in the party side of the Debug Menu - Adds an action to set the friendship of a Pokemon in the party side of the Debug Menu - + * Display TM/HM's move name in the debug menu by @estellarc in [#7994](https://github.com/rh-hideout/pokeemerald-expansion/pull/7994) ### Changed diff --git a/docs/changelogs/1.9.x/1.9.2.md b/docs/changelogs/1.9.x/1.9.2.md index 4d95fc95f9..18e9f63468 100644 --- a/docs/changelogs/1.9.x/1.9.2.md +++ b/docs/changelogs/1.9.x/1.9.2.md @@ -121,7 +121,7 @@ * Fixed message for switch out moves by @kittenchilly in [#5258](https://github.com/rh-hideout/pokeemerald-expansion/pull/5258) * Fixed Ice Fang's descriptions using the opposite of what they're supposed to do based on `B_USE_FROSTBITE` by @laserXdolphin in [#5273](https://github.com/rh-hideout/pokeemerald-expansion/pull/5273) * Fixes to Instruct by @PhallenTree in [#5262](https://github.com/rh-hideout/pokeemerald-expansion/pull/5262) - * Fixed Instruct bypassing AtkCanceler checks (Instruct allowed the target to act while asleep, flinched, etc.) and its interaction with First Turn Only moves (Fake Out, First Impression, Mat Block). + * Fixed Instruct bypassing AtkCanceler checks (Instruct allowed the target to act while asleep, flinched, etc.) and its interaction with First Turn Only moves (Fake Out, First Impression, Mat Block). * Fixed Instruct's animation using the attacker and target of the called move. * Fixed Scale Shot's effect not activating if the opponent fainted before all hits finished by @AlexOn1ine in [#5292](https://github.com/rh-hideout/pokeemerald-expansion/pull/5292) * Fixed Round not preserving turn order for non-Round users if there's a switch out at the beginning of the turn by @AlexOn1ine in [#5292](https://github.com/rh-hideout/pokeemerald-expansion/pull/5292) diff --git a/docs/team_procedures/expansion_versions.md b/docs/team_procedures/expansion_versions.md index 5c5cfabf00..4d328c796e 100644 --- a/docs/team_procedures/expansion_versions.md +++ b/docs/team_procedures/expansion_versions.md @@ -4,7 +4,7 @@ ## 1.- Autogenerating a changelog for the `master` branch. *Requires Write access to the repo.* - + If the changelog you're making is for a minor version (Eg. 1.3.0), make sure to sync the `upcoming` branch with `master` before starting. Keep in mind that if there are unreleased changes in `master`, they should be made into a patch version released alongside minor version. - Go to https://github.com/rh-hideout/pokeemerald-expansion/releases. - Press the option "Draft a new release". @@ -178,7 +178,7 @@ With this, the repo is ready again to receive new PRs. - Requires role to post in #announcements channel. - [Team Aqua's Hideout](https://discord.gg/team-aqua-s-hideout-976252009114140682) - Requires role to post in #romhacking-updates channel. - - [What a Hack!](https://discord.gg/whack-a-hack-292436944670162955) + - [What a Hack!](https://discord.gg/whack-a-hack-292436944670162955) - Announcements are done in Spanish, but not the changelogs themselves. - Requires role to ping "Decompilaciones" role. - [pret](https://discord.gg/R4c3FA95dP) diff --git a/docs/team_procedures/merge_checklist.md b/docs/team_procedures/merge_checklist.md index 53e8d7683f..fb47bd5a25 100644 --- a/docs/team_procedures/merge_checklist.md +++ b/docs/team_procedures/merge_checklist.md @@ -18,7 +18,7 @@ Contributors are asked to make sure tests pass locally, but maintainers should a ## Have you verified that the functionality works in game without any problems? If functionality cannot be verified with an automated test, proof of an in game test is required. Do not be afraid to reach out to the contributor or the community to make sure something works in game as it should. -## If the branch ports behavior from another Pokémon game, have you verified that the behavior functions as faithfully as possible? +## If the branch ports behavior from another Pokémon game, have you verified that the behavior functions as faithfully as possible? We have always tried to make sure we can mimic the original functionality as closely as possible so as to avoid confusion with users and players. Do not be afraid to ask the contributor / community for proof if you cannot personally verify. ## If the branch is a popular feature within the community with an established feature branch, is this using that established branch as a base? @@ -28,7 +28,7 @@ There are situations where this should and should not happen, and should be disc Not everything needs a migration script - if you're unsure, start a discussion. ## Should new functionality introduced by this branch be gated behind a config? -We don't have a strict definition of when configs should be used, but you can start with +We don't have a strict definition of when configs should be used, but you can start with > Why SHOULDN'T this be a config? diff --git a/docs/team_procedures/schedule.md b/docs/team_procedures/schedule.md index 520c4af000..6c948a8b3a 100644 --- a/docs/team_procedures/schedule.md +++ b/docs/team_procedures/schedule.md @@ -13,10 +13,10 @@ PRs with the Github label [`type: big feature`](https://github.com/rh-hideout/po ### Merge Freeze (14 days to the next Minor Release) Pull Requests that **DO NOT** have one of the following Github labels: -- [`bugfix`](https://github.com/rh-hideout/pokeemerald-expansion/issues?q=sort%3Aupdated-desc+label%3Abugfix) -- [`type: cleanup`](https://github.com/rh-hideout/pokeemerald-expansion/issues?q=sort%3Aupdated-desc+label%3A%22type%3A+cleanup%22+) -- [`type: credits`](https://github.com/rh-hideout/pokeemerald-expansion/issues?q=sort%3Aupdated-desc+label%3A%22type%3A+credits%22+) -- [`type: documentation`](https://github.com/rh-hideout/pokeemerald-expansion/issues?q=sort%3Aupdated-desc+label%3A%22type%3A+documentation%22+) +- [`bugfix`](https://github.com/rh-hideout/pokeemerald-expansion/issues?q=sort%3Aupdated-desc+label%3Abugfix) +- [`type: cleanup`](https://github.com/rh-hideout/pokeemerald-expansion/issues?q=sort%3Aupdated-desc+label%3A%22type%3A+cleanup%22+) +- [`type: credits`](https://github.com/rh-hideout/pokeemerald-expansion/issues?q=sort%3Aupdated-desc+label%3A%22type%3A+credits%22+) +- [`type: documentation`](https://github.com/rh-hideout/pokeemerald-expansion/issues?q=sort%3Aupdated-desc+label%3A%22type%3A+documentation%22+) - [`category: battle-tests`](https://github.com/rh-hideout/pokeemerald-expansion/issues?q=sort%3Aupdated-desc+is%3Aopen+label%3A%22category%3A+battle-tests%22) will NOT be merged until after the next Minor Release. @@ -36,7 +36,7 @@ will NOT be merged until after the next Minor Release. ## What is a "Big Feature"? * If the original owner of the PR thinks a feature should be labeled a Big Feature, it is, no questions asked * If a reviewer thinks a PR is a Big Feature, then it is -* If the two disagree, it can be discussed in a PR thread, and can ultimately be resolved with a Maintainer vote. +* If the two disagree, it can be discussed in a PR thread, and can ultimately be resolved with a Maintainer vote. ### How To Identify a Big Feature * **Big diffs**: It's easy for something to go unnoticed in review when it's a tiny part of a massive diff. diff --git a/docs/team_procedures/scope.md b/docs/team_procedures/scope.md index f64ecc399a..0f90117dae 100644 --- a/docs/team_procedures/scope.md +++ b/docs/team_procedures/scope.md @@ -25,7 +25,7 @@ A pull request meets the scope criteria if: - Has appeared in a Showdown supported title - Is mechanically / functionally unique from existing items (ie. not Relic Crown, Silver Leaf), with the exception of items with in-battle functionality (ie. Lumiose Gallette) - Do not ONLY exist for story related purpose (ie. Jade Orb) -5. **SS Gimmicks**: Adds Gimmicks that have appeared in a Showdown-supported title (Dynamax, Mega Evolution, etc.) +5. **SS Gimmicks**: Adds Gimmicks that have appeared in a Showdown-supported title (Dynamax, Mega Evolution, etc.) 6. **SS Battle Types**: Adds Special Battle Types that have appeared in a Showdown-supported title (Triple battles, etc.) 7. **SS Battle Mechanics**: Adds mechanical battle changes that have appeared in a Showdown-supported title, and allow developers to choose which generation suits them where applicable 8. **Battle AI Behaviour**: Improvements towards the capability of a human competitive player, and unique or interesting behaviours otherwise diff --git a/docs/tutorials/ai_flags.md b/docs/tutorials/ai_flags.md index 2c13cee8bd..5f58ef1ff6 100644 --- a/docs/tutorials/ai_flags.md +++ b/docs/tutorials/ai_flags.md @@ -29,13 +29,13 @@ This section lists all of expansion’s AI Flags and briefly describes the effec ## Composite AI Flags -Expansion has a few "composite" AI flags. This means that these flags have no unique functionality themselves, and can instead be thought of as groups of other flags that are all enabled when this flag is enabled. The idea behind these flags is that if you don't care to manage the detailed behaviour of a particular trainer, you can use these as a baseline instead, and expansion will keep them updated for you. +Expansion has a few "composite" AI flags. This means that these flags have no unique functionality themselves, and can instead be thought of as groups of other flags that are all enabled when this flag is enabled. The idea behind these flags is that if you don't care to manage the detailed behaviour of a particular trainer, you can use these as a baseline instead, and expansion will keep them updated for you. `AI_FLAG_BASIC_TRAINER` is expansion's version of generic, normal AI behaviour. It includes `AI_FLAG_CHECK_BAD_MOVE` (don't use bad moves), `AI_FLAG_TRY_TO_FAINT` (faint the player where possible), and `AI_FLAG_CHECK_VIABILITY` (choose the most effective move to use in the current context). Trainers with this flag will still be smarter than they are in vanilla as there have been dramatic improvements made to move selection, but not incredibly so. Trainers with this flag should feel like normal trainers. In general we recommend these three flags be used in all cases, unless you specifically want a trainer who makes obvious mistakes in battle. `AI_FLAG_SMART_TRAINER` is expansion's version of a "smart AI". It includes everything in `AI_FLAG_BASIC_TRAINER` along with `AI_FLAG_SMART_SWITCHING` (make smart decisions about when to switch), `AI_FLAG_SMART_MON_CHOICES` (make smart decisions about what mon to send in after a switch / KO), `AI_FLAG_OMNISCIENT` (awareness of what moves, items, and abilities the player's mons have to better inform decisions), and `AI_FLAG_SMART_TERA` (make smart decisions about when to terastalize). Expansion will keep this updated to represent the most objectively intelligent behaviour our flags are capable of producing. -`AI_FLAG_PREDICTION` will enable all of the prediction flags at once, so the AI can perform as well as possible. It is best paired with the flags in `AI_FLAG_SMART_TRAINER` for optimal behaviour. This currently includes `AI_FLAG_PREDICT_SWITCH` and `AI_FLAG_PREDICT_INCOMING_MON`, but will likely be expanded in the future. +`AI_FLAG_PREDICTION` will enable all of the prediction flags at once, so the AI can perform as well as possible. It is best paired with the flags in `AI_FLAG_SMART_TRAINER` for optimal behaviour. This currently includes `AI_FLAG_PREDICT_SWITCH` and `AI_FLAG_PREDICT_INCOMING_MON`, but will likely be expanded in the future. Expansion has LOADS of flags, which will be covered in the rest of this guide. If you don't want to engage with detailed trainer AI tuning though, you can just use these two composite flags, and trust that expansion will keep their contents updated to always represent the most standard and the smartest behaviour we can. diff --git a/docs/tutorials/dns.md b/docs/tutorials/dns.md index 6fdd1c1d77..498d62caa5 100644 --- a/docs/tutorials/dns.md +++ b/docs/tutorials/dns.md @@ -12,7 +12,7 @@ If you intend to use vanilla maps and have not already edited them, revert commi If you _have_ edited vanilla maps, the merge conflicts from reverting that commit will cause problems. If you are using vanilla maps, manually copy some of the tileset changes, `.pal`, and `.pla` files in your branch, and begin rebuilding your metatiles to have windows use the palettes that have a `.pla` for light blending the correct color slots. [Triple-layer metatiles](https://github.com/pret/pokeemerald/wiki/Triple-layer-metatiles) are highly recommended. -WARNING: [As per issue #7034](https://github.com/rh-hideout/pokeemerald-expansion/issues/7034) if you follow this tutorial reverting the previously mentioned commit to use the updated palettes in the Hoenn maps and *after* that you try to follow the [Triple-layer metatiles tutorial](https://github.com/pret/pokeemerald/wiki/Triple-layer-metatiles), you'll encounter an issue when running the script to update old tilesets to support triple-layer metatiles. +WARNING: [As per issue #7034](https://github.com/rh-hideout/pokeemerald-expansion/issues/7034) if you follow this tutorial reverting the previously mentioned commit to use the updated palettes in the Hoenn maps and *after* that you try to follow the [Triple-layer metatiles tutorial](https://github.com/pret/pokeemerald/wiki/Triple-layer-metatiles), you'll encounter an issue when running the script to update old tilesets to support triple-layer metatiles. Follow the band-aid fix proposed in that issue after this tutorial but before following the triple-layer metatiles tutorial if you want everythign to work properly with light-blended palettes and triple-layer metatiles together. You will also want to add the lighting object events from that commit. diff --git a/docs/tutorials/how_to_code_entry.md b/docs/tutorials/how_to_code_entry.md index 51fe0d6f7d..8558d60a9c 100644 --- a/docs/tutorials/how_to_code_entry.md +++ b/docs/tutorials/how_to_code_entry.md @@ -293,7 +293,7 @@ MysteryGift_EventScript_Celebi:: end ``` -Walking through this, it's clear we'll need some more scripting. We first check if Celebi's corresponding Mystery Gift flag has been set, and if it has, we need to tell the player they've already redeemed it and can't again. If they haven't though, we get ourselves setup for the givemon, do the givemon, and set the mystery gift flag. Then we need soem more generic handling to prompt nicknaming and some fanfare. +Walking through this, it's clear we'll need some more scripting. We first check if Celebi's corresponding Mystery Gift flag has been set, and if it has, we need to tell the player they've already redeemed it and can't again. If they haven't though, we get ourselves setup for the givemon, do the givemon, and set the mystery gift flag. Then we need soem more generic handling to prompt nicknaming and some fanfare. Two things, then; an event script to handle the case where a mystery gift mon has already been redeemed, and an event script to handle when a mystery gift mon has successfully been received. @@ -327,13 +327,13 @@ Almost done! Just need to handle the specific nicknaming scripts, and then add a MysteryGift_EventScript_NicknamePartyMon:: msgbox gText_NicknameThisPokemon, MSGBOX_YESNO goto_if_eq VAR_RESULT, NO, MysteryGift_EventScript_Exit - call Common_EventScript_GetGiftMonPartySlot - call Common_EventScript_NameReceivedPartyMon + call Common_EventScript_GetGiftMonPartySlot + call Common_EventScript_NameReceivedPartyMon goto MysteryGift_EventScript_Exit end MysteryGift_EventScript_NicknamePCMon:: - msgbox gText_NicknameThisPokemon, MSGBOX_YESNO + msgbox gText_NicknameThisPokemon, MSGBOX_YESNO goto_if_eq VAR_RESULT, NO, MysteryGift_EventScript_TransferredToPC call Common_EventScript_NameReceivedBoxMon call Common_EventScript_TransferredToPC diff --git a/docs/tutorials/how_to_follower_npc.md b/docs/tutorials/how_to_follower_npc.md index fbd0087859..87bf31ceb9 100644 --- a/docs/tutorials/how_to_follower_npc.md +++ b/docs/tutorials/how_to_follower_npc.md @@ -1,5 +1,5 @@ # How to Use Follower NPCs -*Written by Bivurnum* +*Written by Bivurnum* *gif by ghoulslash* ![follower-npc](img/follower_npc/follower-npc.gif) @@ -11,22 +11,22 @@ The configs for follower NPCs can be found in [include/config/follower_npc.h](ht * `FNPC_FLAG_HEAL_AFTER_FOLLOWER_BATTLE`: The player's party can be automatically healed after every partner battle. Set it to a flag to toggle it on/off with scripts, or set it to `FNPC_ALWAYS` to have it happen every time. * `FNPC_FLAG_PARTNER_WILD_BATTLES`: The battle partner can join the player for wild battles. Set it to a flag to toggle it on/off with scripts, or set it to `FNPC_ALWAYS` to have it happen every time. * `FNPC_NPC_FOLLOWER_WILD_BATTLE_VS_2`: Wild battles with a battle partner default to two wild Pokémon appearing. You can set this to `FALSE` to make only one wild Pokémon appear. -* `FNPC_NPC_FOLLOWER_PARTY_PREVIEW`: By default, a preview of the player's and partner's teams will be shown at the start of every trainer battle. Set this to `FALSE` to disable this feature. +* `FNPC_NPC_FOLLOWER_PARTY_PREVIEW`: By default, a preview of the player's and partner's teams will be shown at the start of every trainer battle. Set this to `FALSE` to disable this feature. * `FNPC_FACE_NPC_FOLLOWER_ON_DOOR_EXIT`: If `TRUE` the player will turn to face the follower when they exit a doorway. * `FNPC_NPC_FOLLOWER_SHOW_AFTER_LEAVE_ROUTE`: If `TRUE` the follower will walk out of the player automatically after using Fly, Teleport, or Escape Rope. ## Set the Follower The `setfollowernpc` macro will turn the specified object into an NPC follower. It requires the object id, the [follower flags](#follower-flags), and optionally a custom script and a [battle partner](#battle-partner). If you do not include a custom script name (or you set it to `0`), the NPC follower will default to their normal interaction script. If there is a follower Pokémon present, it will be returned to its Pokeball until the NPC follower is destroyed. -Here's an example: -`setfollowernpc 3, FNPC_ALL, MyScript_Eventscript_CustomFollowerScript, PARTNER_STEVEN` +Here's an example: +`setfollowernpc 3, FNPC_ALL, MyScript_Eventscript_CustomFollowerScript, PARTNER_STEVEN` This would turn object number 3 on the current map into an NPC follower, give them access to all following behaviors, run that custom script when the player interacts with them, and adds the Steven battle partner to the follower ([more on this later](#battle-partner)). The object ***MUST*** have an event flag or the NPC follower will not be created! ## Create a Follower -The `createfollowernpc` macro will create a new follower without needing to convert an existing NPC. It works similarly to `setfollowernpc`, but instead of providing an object id, you give it a GFX id. For example, if you wanted to create a follower with the May sprite, you could do something like this: -`createfollowernpc OBJ_EVENT_GFX_RIVAL_MAY_NORMAL, FNPC_ALL, EventScript_MayFollow` +The `createfollowernpc` macro will create a new follower without needing to convert an existing NPC. It works similarly to `setfollowernpc`, but instead of providing an object id, you give it a GFX id. For example, if you wanted to create a follower with the May sprite, you could do something like this: +`createfollowernpc OBJ_EVENT_GFX_RIVAL_MAY_NORMAL, FNPC_ALL, EventScript_MayFollow` The created follower NPC will initially be invisible until the player takes a step. ## Follower Flags diff --git a/docs/tutorials/how_to_new_pokemon.md b/docs/tutorials/how_to_new_pokemon.md index f042160573..ba1872889e 100644 --- a/docs/tutorials/how_to_new_pokemon.md +++ b/docs/tutorials/how_to_new_pokemon.md @@ -907,7 +907,7 @@ _NOTE: At the top of this file, you will probably see this warning:_ // DO NOT MODIFY THIS FILE! It is auto-generated from tools/learnset_helpers/teachable.py` // ``` -From version 1.9 onwards, pokeemerald-expansion includes a tool called the learnset helper, which aims to automate the generation of valid teachable moves. At the time of writing, this tool only supports generating TM and Tutor learnsets. However, in the future it may be expanded to deal with level up learnsets and egg moves. +From version 1.9 onwards, pokeemerald-expansion includes a tool called the learnset helper, which aims to automate the generation of valid teachable moves. At the time of writing, this tool only supports generating TM and Tutor learnsets. However, in the future it may be expanded to deal with level up learnsets and egg moves. Ignore the warning shown above the first time you're adding your teachable moves (as otherwise the compiler will complain about the array not existing), but in the future (if you're using the learnset helper) simply edit what teachable moves your Pokémon can learn in one of the JSON files found in `tools/learnset_helpers/porymoves_files`. It doesn't really matter which one you add your new Pokémon to, as the tool pulls from all of the files in this folder. diff --git a/docs/tutorials/how_to_new_trainer_slide.md b/docs/tutorials/how_to_new_trainer_slide.md index 49c3e80f3e..6dfab9b5ec 100644 --- a/docs/tutorials/how_to_new_trainer_slide.md +++ b/docs/tutorials/how_to_new_trainer_slide.md @@ -47,7 +47,7 @@ enum TrainerSlideType { TRAINER_SLIDE_BEFORE_FIRST_TURN, TRAINER_SLIDE_PLAYER_LANDS_FIRST_CRITICAL_HIT, -+ TRAINER_SLIDE_ENEMY_LANDS_FIRST_CRITICAL_HIT, // Each Trainer Slide has a unqiue id. ++ TRAINER_SLIDE_ENEMY_LANDS_FIRST_CRITICAL_HIT, // Each Trainer Slide has a unqiue id. TRAINER_SLIDE_PLAYER_LANDS_FIRST_SUPER_EFFECTIVE_HIT, TRAINER_SLIDE_PLAYER_LANDS_FIRST_STAB_MOVE, TRAINER_SLIDE_PLAYER_LANDS_FIRST_DOWN, @@ -82,7 +82,7 @@ bool32 IsTrainerSlideInitialized(enum TrainerSlideType); ```diff return IsTrainerSlideInitialized(slideId); } - + +static bool32 ShouldRunTrainerSlideEnemyLandsFirstCriticalHit(enum TrainerSlideType slideId) +{ + return IsTrainerSlideInitialized(slideId); @@ -110,7 +110,7 @@ The function that determines if a Slide should play has different function for m ```diff InitalizeTrainerSlide(slideId); } - + +void TryInitializeTrainerSlideEnemyLandsFirstCriticalHit(u32 target) +{ + enum TrainerSlideType slideId = TRAINER_SLIDE_ENEMY_LANDS_FIRST_CRITICAL_HIT; @@ -149,10 +149,10 @@ In `BattleTurnPassed`, most Trainer Slides are checked to see if they should run ```diff { PrepareStringBattle(STRINGID_CRITICALHIT, gBattlerAttacker); - + + TryInitializeTrainerSlideEnemyLandsFirstCriticalHit(gBattlerTarget); TryInitializeTrainerSlidePlayerLandsFirstCriticalHit(gBattlerTarget); - + gBattleCommunication[MSG_DISPLAY] = 1; ``` @@ -162,7 +162,7 @@ The actual usage of `TryInitializeTrainerSlideEnemyLandsFirstCriticalHit` is add ```diff } } - + +SINGLE_BATTLE_TEST("Trainer Slide: Enemy Lands First Critical Hit") +{ + gBattleTestRunnerState->data.recordedBattle.opponentA = TRAINER_SLIDE_ENEMY_LANDS_FIRST_CRITICAL_HIT; diff --git a/docs/tutorials/how_to_testing_system.md b/docs/tutorials/how_to_testing_system.md index 70ee6c4033..e72788938b 100644 --- a/docs/tutorials/how_to_testing_system.md +++ b/docs/tutorials/how_to_testing_system.md @@ -292,7 +292,7 @@ Adds the species to the player's, player partner's, opponent A's, or opponent B' Pokemon can be customised as per the guidance for `PLAYER(species)` and `OPPONENT(species)`. The functions assign the Pokémon to the party of the trainer at `B_POSITION_PLAYER_LEFT`, `B_POSITION_PLAYER_RIGHT`, `B_POSITION_OPPONENT_LEFT`, and `B_POSITION_OPPONENT_RIGHT`, respectively. `MULTI_PLAYER(species)` and `MULTI_OPPONENT_A(species)` set Pokémon starting at party index 0, while `MULTI_PARTNER(species)` and `MULTI_OPPONENT_B(species)` set Pokémon starting at party index 3. -For `ONE_VS_TWO` tests, `MULTI_PLAYER(species)` must be used for all player-side Pokémon, and for `TWO_VS_ONE` tests, `MULTI_OPPONENT_A(species)` must be used for all opponent-side Pokémon. +For `ONE_VS_TWO` tests, `MULTI_PLAYER(species)` must be used for all player-side Pokémon, and for `TWO_VS_ONE` tests, `MULTI_OPPONENT_A(species)` must be used for all opponent-side Pokémon. All `MULTI_PLAYER(species)` Pokémon must be set before any `MULTI_PARTNER(species)` Pokémon, and all `MULTI_OPPONENT_A(species)` must be set before any `MULTI_OPPONENT_B(species)` Pokémon, else Pokémon will be set in the incorrect parties in the test. **Note where a side in a test has two trainers, the test setup manages the assigning of correct multi-party orders, therefore when using functions such as SEND_OUT, Player and Opponent A Pokémon may be referenced using indexes 0, 1, and 2, and Player's Partner and Opponent B Pokémon may be referenced using indexes 3, 4, and 5.** diff --git a/docs/tutorials/how_to_time_of_day_encounters.md b/docs/tutorials/how_to_time_of_day_encounters.md index 85cd2ac09a..05bddc1e7d 100644 --- a/docs/tutorials/how_to_time_of_day_encounters.md +++ b/docs/tutorials/how_to_time_of_day_encounters.md @@ -10,7 +10,7 @@ - [So what are the `#define` options in `overworld.h`?](#so-what-are-the-define-options-in-overworldh) - [Examples](#examples) -## What is the Time-Based Encounters feature? +## What is the Time-Based Encounters feature? Time-Based Encounters lets you pick which Pokémon appear based on the in-game clock, per route! Gen 2 had this feature, and Gen 4 brought it back- for instance, in Sinnoh's Route 201 you have a higher chance of catching a Bidoof than a Starly at night. @@ -810,4 +810,4 @@ python3 migration_scripts/add_time_based_encounters.py --copy }, ] ``` -As you can see, the group `gRoute101` and all its encounters were copied into groups that correspond with the four vanilla times of day (Morning/Day/Evening/Night). \ No newline at end of file +As you can see, the group `gRoute101` and all its encounters were copied into groups that correspond with the four vanilla times of day (Morning/Day/Evening/Night). diff --git a/docs/tutorials/vs_seeker.md b/docs/tutorials/vs_seeker.md index 136465a5b4..90a5f3c81d 100644 --- a/docs/tutorials/vs_seeker.md +++ b/docs/tutorials/vs_seeker.md @@ -1,13 +1,13 @@ -# `pokemerald-expansion` Vs. Seeker +# `pokemerald-expansion` Vs. Seeker ## What is the Vs. Seeker? -The Vs. Seeker is a Key Item that is used to battle Trainers that the player has battled previously. +The Vs. Seeker is a Key Item that is used to battle Trainers that the player has battled previously. -When used, the Vs. Seeker sends out a signal that allows the player to find other Trainers who want a rematch. This signal affects all Trainers that are on-screen. Once used on Trainers that can be rematched, the device cannot be used again until it is charged. The player does this by walking a specific number of steps. The effect on the Trainers wears off if they are battled, the player leaves the area, or the player walks a specific number of steps. If the player attempts to use the Vs. Seeker when it is not fully charged, the player will be told how many steps remain until it is. After the player uses the Vs. Seeker, some Trainers may have their team changed from their first battle. +When used, the Vs. Seeker sends out a signal that allows the player to find other Trainers who want a rematch. This signal affects all Trainers that are on-screen. Once used on Trainers that can be rematched, the device cannot be used again until it is charged. The player does this by walking a specific number of steps. The effect on the Trainers wears off if they are battled, the player leaves the area, or the player walks a specific number of steps. If the player attempts to use the Vs. Seeker when it is not fully charged, the player will be told how many steps remain until it is. After the player uses the Vs. Seeker, some Trainers may have their team changed from their first battle. ## How is the Vs. Seeker enabled? ### Users -Vs. Seeker functionality is enabled by setting `I_VS_SEEKER_CHARGING` to `TRUE`. +Vs. Seeker functionality is enabled by setting `I_VS_SEEKER_CHARGING` to `TRUE`. ### Players `ITEM_VS_SEEKER` can only be used outside of battle. It can be used from the bag or registered to be used from the field. @@ -26,7 +26,7 @@ When `I_VS_SEEKER_CHARGING` is enabled, the Match Call does not function at all. ## How does the Vs. Seeker choose a Trainer? -When the Vs. Seeker is successfully used, every Trainer on screen is individually queried. There is a 31% chance that the Trainer will want a rematch. +When the Vs. Seeker is successfully used, every Trainer on screen is individually queried. There is a 31% chance that the Trainer will want a rematch. Objects listed in `regularTrainersOnLand` or `regularTrainersInWater` are considered Land/Water objects. | Status | Is Land/Water Object | Emote | New Movement Type | @@ -34,7 +34,7 @@ Objects listed in `regularTrainersOnLand` or `regularTrainersInWater` are consid | Wants Rematch | Yes | `MOVEMENT_ACTION_EMOTE_DOUBLE_EXCL_MARK` | `MOVEMENT_TYPE_COUNTER_CLOCKWISE` | | Wants Rematch | No | `MOVEMENT_ACTION_EMOTE_DOUBLE_EXCL_MARK` | `MOVEMENT_TYPE_FACE_DOWN` | | Does Not Want Rematch | - | `MOVEMENT_ACTION_EMOTE_X` | none | -| Has Not Been Fought | - | `MOVEMENT_ACTION_EMOTE_EXCLAMATION_MARK` | none | +| Has Not Been Fought | - | `MOVEMENT_ACTION_EMOTE_EXCLAMATION_MARK` | none | ### Rematch Table @@ -58,7 +58,7 @@ The game determines which version of the Trainer you'll fight next by following No extra work is required. With the exception of Wally, Gym Leaders and Elite Four, all of the rematchable Trainers in Emerald will work with the Vs. Seeker without any changes. ### New Trainers #### Party / `gRematchTable` -Each of the rematches for the Trainer must be defined as seperate Trainers in `src/data/trainers.party` and `include/constants/opponents`. For example, `TRAINER_CALVIN_1` also has `TRAINER_CALVIN_2`,`TRAINER_CALVIN_3`,`TRAINER_CALVIN_4`, and `TRAINER_CALVIN_5`. +Each of the rematches for the Trainer must be defined as seperate Trainers in `src/data/trainers.party` and `include/constants/opponents`. For example, `TRAINER_CALVIN_1` also has `TRAINER_CALVIN_2`,`TRAINER_CALVIN_3`,`TRAINER_CALVIN_4`, and `TRAINER_CALVIN_5`. Once all of those constants and parties are defined, a new row must be added to `gRematchTable` (located in in `src/battle_setup.c`). The row header should be a rematch ID, which can be added in `include/constants/rematches.h`. The row contents must be the five constants created for the new parties, with the lat argument being the constant of the map (`include/constants/map_groups.h`) where the Trainer is placed. diff --git a/include/constants/weather.h b/include/constants/weather.h index f2e41b3ab7..d79e8f1717 100644 --- a/include/constants/weather.h +++ b/include/constants/weather.h @@ -19,7 +19,7 @@ #define WEATHER_ABNORMAL 15 // The alternating weather during Groudon/Kyogre conflict #define WEATHER_ROUTE119_CYCLE 20 #define WEATHER_ROUTE123_CYCLE 21 -#define WEATHER_FOG 22 // Aggregate of WEATHER_FOG_HORIZONTAL and WEATHER_FOG_DIAGONAL +#define WEATHER_FOG 22 // Aggregate of WEATHER_FOG_HORIZONTAL and WEATHER_FOG_DIAGONAL #define WEATHER_COUNT 23 // These are used in maps' coord_weather_event entries. diff --git a/include/gametypes.h b/include/gametypes.h index 242fc9d9db..97e0533baf 100644 --- a/include/gametypes.h +++ b/include/gametypes.h @@ -4,63 +4,63 @@ #include "gba/types.h" // -// This header includes typedefs for fields that commonly appear throughout +// This header includes typedefs for fields that commonly appear throughout // the codebase, and which ROM hacks might benefit from being able to widen. // // These typedefs include the underlying type in their name for two reasons: // -// - Game Freak wasn't fully consistent about field widths throughout -// their codebase. For example, when Region Map Sections are persistently -// stored in savedata, they're stored as 8-bit values; but much of the +// - Game Freak wasn't fully consistent about field widths throughout +// their codebase. For example, when Region Map Sections are persistently +// stored in savedata, they're stored as 8-bit values; but much of the // codebase handles them as 16-bit values. // -// - Although Pokemon Emerald doesn't come close to maxing out RAM, it *does* -// use nearly all of its EEPROM. That is: the vanilla game uses 96% of the -// flash memory available for storing players' save files, leaving 2172 -// bytes to spare within each of the game's two save files (primary and -// backup). These spare bytes are not contiguous: SaveBlock1 can only grow -// by 84 bytes, and SaveBlock2 can only grow by 120 bytes, with the rest +// - Although Pokemon Emerald doesn't come close to maxing out RAM, it *does* +// use nearly all of its EEPROM. That is: the vanilla game uses 96% of the +// flash memory available for storing players' save files, leaving 2172 +// bytes to spare within each of the game's two save files (primary and +// backup). These spare bytes are not contiguous: SaveBlock1 can only grow +// by 84 bytes, and SaveBlock2 can only grow by 120 bytes, with the rest // of the free space located after the player's PC-boxed Pokemon. // -// With so little flash memory to spare, keeping track of how much space -// you're using is vital -- and so is arranging struct members to minimize -// compiler-inserted padding. It's easier to deal with this when you can +// With so little flash memory to spare, keeping track of how much space +// you're using is vital -- and so is arranging struct members to minimize +// compiler-inserted padding. It's easier to deal with this when you can // see these types' widths at a glance. // -// Accordingly, this file generally doesn't contain just single types, but -// rather families of types. For example, Region Map Sections are saved as -// u8s within the player's save file, but are sometimes handled as u16s or -// even s16s and ints; and so there are multiple typedefs for Map Sections -// corresponding to each of these underlying types, and each typedef has a +// Accordingly, this file generally doesn't contain just single types, but +// rather families of types. For example, Region Map Sections are saved as +// u8s within the player's save file, but are sometimes handled as u16s or +// even s16s and ints; and so there are multiple typedefs for Map Sections +// corresponding to each of these underlying types, and each typedef has a // name which indicates the underlying type. // -// For a given family of typedefs, the smallest one should be considered -// the "real" or "canonical" type. Continuing with Map Sections as our -// example, the smallest type is an 8-bit integer, and so any values that -// can't fit in an 8-bit integer will be truncated and lost at some point -// within the codebase. Therefore mapsec_u8_t is the "canonical" type for -// Map Sections, and the larger typedefs just exist to describe situations +// For a given family of typedefs, the smallest one should be considered +// the "real" or "canonical" type. Continuing with Map Sections as our +// example, the smallest type is an 8-bit integer, and so any values that +// can't fit in an 8-bit integer will be truncated and lost at some point +// within the codebase. Therefore mapsec_u8_t is the "canonical" type for +// Map Sections, and the larger typedefs just exist to describe situations // where the game handles Map Sections inconsistently with that "canon." // -// Map Sections are named areas that can appear in the region map. Each -// individual map can be assigned to a Map Section as appropriate. The +// Map Sections are named areas that can appear in the region map. Each +// individual map can be assigned to a Map Section as appropriate. The // possible values are in constants/region_map_sections.h. // -// If you choose to widen Map Sections, be aware that Met Locations (below) +// If you choose to widen Map Sections, be aware that Met Locations (below) // are based on Map Sections and will also be widened. typedef u8 mapsec_u8_t; typedef u16 mapsec_u16_t; typedef s16 mapsec_s16_t; typedef s32 mapsec_s32_t; -// Met Locations for caught Pokemon use the same values as Map Sections, +// Met Locations for caught Pokemon use the same values as Map Sections, // except that 0xFD, 0xFE, and 0xFF have special meanings. // -// Because this value appears inside every Pokemon's data, widening it will -// consume a lot more space within flash memory. The space usage will be -// greater than you expect due to how Pokemon substructs are laid out; you -// would have to rearrange the substructs' contents in order to minimize +// Because this value appears inside every Pokemon's data, widening it will +// consume a lot more space within flash memory. The space usage will be +// greater than you expect due to how Pokemon substructs are laid out; you +// would have to rearrange the substructs' contents in order to minimize // how much more space a wider Met Location would consume. typedef mapsec_u8_t metloc_u8_t; diff --git a/migration_scripts/add_time_based_encounters.py b/migration_scripts/add_time_based_encounters.py index b036e46e41..4c802fb44c 100644 --- a/migration_scripts/add_time_based_encounters.py +++ b/migration_scripts/add_time_based_encounters.py @@ -56,7 +56,7 @@ def GetWildEncounterFile(): if tempSuffix in map["base_label"]: editMap = False break - else: + else: editMap = True if editMap: diff --git a/src/battle_anim_ice.c b/src/battle_anim_ice.c index 5b8c599c84..65ff6f9f29 100644 --- a/src/battle_anim_ice.c +++ b/src/battle_anim_ice.c @@ -1599,8 +1599,8 @@ static void AnimHailBegin(struct Sprite *sprite) sprite->data[0] = spriteId; if (spriteId != MAX_SPRITES) { - // The sprite template we're using is shared amongst a few other - // places, which make the sprite flicker. That's not what we want + // The sprite template we're using is shared amongst a few other + // places, which make the sprite flicker. That's not what we want // here, though. Override the callback. gSprites[sprite->data[0]].callback = AnimHailContinue; gSprites[sprite->data[0]].sOwnerTaskId = sprite->sOwnerTaskId; diff --git a/src/battle_anim_rock.c b/src/battle_anim_rock.c index 23b244957c..03880a74f1 100644 --- a/src/battle_anim_rock.c +++ b/src/battle_anim_rock.c @@ -626,10 +626,10 @@ static void AnimTask_LoadSandstormBackground_Step(u8 taskId) #define sFractionalY data[4] // 256ths of a pixel #define sMirroredX data[5] // init'd from gBattleAnimArgs[3] -// The fields named "velocity" are arguably more like "acceleration," +// The fields named "velocity" are arguably more like "acceleration," // and the fields named "fractional" are arguably more like "velocity." // -// ...is what I WOULD say if the "fractional" fields weren't AND'd with +// ...is what I WOULD say if the "fractional" fields weren't AND'd with // 0xFF after every frame. void AnimFlyingSandCrescent(struct Sprite *sprite) diff --git a/src/battle_anim_water.c b/src/battle_anim_water.c index 5674cd5cc7..a8c1647bda 100644 --- a/src/battle_anim_water.c +++ b/src/battle_anim_water.c @@ -1443,20 +1443,20 @@ static void AnimSmallWaterOrb(struct Sprite *sprite) } } -#define tRainState data[0] -#define tWaterSpoutPower data[1] -#define tDropTaskDelay data[2] -#define tDropInitialXPos data[4] -#define tDropXRange data[5] -#define tDropEndYPos data[6] -#define tDropXPos data[7] -#define tSineTableIndex data[8] -#define tCurrentDropSprites data[9] -#define tDropHasHit data[10] -#define tCreatedDropSprites data[11] -#define tMaxDropSprites data[12] -#define tShakeTasksCreated data[13] -#define tDropInitialYPos data[14] +#define tRainState data[0] +#define tWaterSpoutPower data[1] +#define tDropTaskDelay data[2] +#define tDropInitialXPos data[4] +#define tDropXRange data[5] +#define tDropEndYPos data[6] +#define tDropXPos data[7] +#define tSineTableIndex data[8] +#define tCurrentDropSprites data[9] +#define tDropHasHit data[10] +#define tCreatedDropSprites data[11] +#define tMaxDropSprites data[12] +#define tShakeTasksCreated data[13] +#define tDropInitialYPos data[14] void AnimTask_BrineRain(u8 taskId) { @@ -1634,20 +1634,20 @@ static void AnimWaterSpoutRainHit(struct Sprite *sprite) } } -#undef tRainState -#undef tWaterSpoutPower -#undef tDropTaskDelay -#undef tDropInitialXPos -#undef tDropXRange -#undef tDropEndYPos -#undef tDropXPos -#undef tSineTableIndex -#undef tCurrentDropSprites -#undef tDropHasHit -#undef tCreatedDropSprites -#undef tMaxDropSprites -#undef tShakeTasksCreated -#undef tDropInitialYPos +#undef tRainState +#undef tWaterSpoutPower +#undef tDropTaskDelay +#undef tDropInitialXPos +#undef tDropXRange +#undef tDropEndYPos +#undef tDropXPos +#undef tSineTableIndex +#undef tCurrentDropSprites +#undef tDropHasHit +#undef tCreatedDropSprites +#undef tMaxDropSprites +#undef tShakeTasksCreated +#undef tDropInitialYPos void AnimTask_WaterSport(u8 taskId) { diff --git a/src/battle_controller_recorded_player.c b/src/battle_controller_recorded_player.c index 51bfa5327b..8547981eac 100644 --- a/src/battle_controller_recorded_player.c +++ b/src/battle_controller_recorded_player.c @@ -286,7 +286,7 @@ static void RecordedPlayerHandleDrawTrainerPic(u32 battler) xPos = 80; yPos = (8 - gTrainerBacksprites[trainerPicId].coordinates.size) * 4 + 80; } - else + else { if (gBattleTypeFlags & BATTLE_TYPE_RECORDED_LINK) { diff --git a/src/battle_transition.c b/src/battle_transition.c index ffb826664d..ed9f86072a 100644 --- a/src/battle_transition.c +++ b/src/battle_transition.c @@ -2426,7 +2426,7 @@ static bool8 Mugshot_WaitPlayerSlide(struct Task *task) sTransitionData->BG0HOFS_Lower -= 8; sTransitionData->BG0HOFS_Upper += 8; - if (gPartnerTrainerId != TRAINER_PARTNER(PARTNER_NONE) && gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) + if (gPartnerTrainerId != TRAINER_PARTNER(PARTNER_NONE) && gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) { if (IsTrainerPicSlideDone(task->tPartnerSpriteId)) { @@ -2615,11 +2615,11 @@ static void Mugshots_CreateTrainerPics(struct Task *task) 0, NULL); gReservedSpritePaletteCount = 12; - if (gPartnerTrainerId != TRAINER_PARTNER(PARTNER_NONE) && gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) + if (gPartnerTrainerId != TRAINER_PARTNER(PARTNER_NONE) && gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) { - task->tPartnerSpriteId = CreateTrainerSprite(partnerPicId, - DISPLAY_WIDTH + 240, - 106, + task->tPartnerSpriteId = CreateTrainerSprite(partnerPicId, + DISPLAY_WIDTH + 240, + 106, 0, NULL); partnerSprite = &gSprites[task->tPartnerSpriteId]; partnerSprite->callback = SpriteCB_MugshotTrainerPicPartner; @@ -2631,9 +2631,9 @@ static void Mugshots_CreateTrainerPics(struct Task *task) SetOamMatrixRotationScaling(partnerSprite->oam.matrixNum, -512, 512, 0); } - task->tPlayerSpriteId = CreateTrainerSprite(PlayerGenderToFrontTrainerPicId(gSaveBlock2Ptr->playerGender), - DISPLAY_WIDTH + 32, - 106, + task->tPlayerSpriteId = CreateTrainerSprite(PlayerGenderToFrontTrainerPicId(gSaveBlock2Ptr->playerGender), + DISPLAY_WIDTH + 32, + 106, 0, NULL); opponentSpriteA = &gSprites[task->tOpponentSpriteAId]; diff --git a/src/datetime.c b/src/datetime.c index fbbb3ea6b7..51e009b3bb 100644 --- a/src/datetime.c +++ b/src/datetime.c @@ -3,7 +3,7 @@ #include "rtc.h" -const struct DateTime gGen3Epoch = +const struct DateTime gGen3Epoch = { .year = 2000, .month = MONTH_JAN, diff --git a/src/field_player_avatar.c b/src/field_player_avatar.c index 5111e0ece3..56fe5868e5 100644 --- a/src/field_player_avatar.c +++ b/src/field_player_avatar.c @@ -845,10 +845,10 @@ static void PlayerNotOnBikeMoving(u8 direction, u16 heldKeys) } if (!(gPlayerAvatar.flags & PLAYER_AVATAR_FLAG_UNDERWATER) - && (heldKeys & B_BUTTON) + && (heldKeys & B_BUTTON) && FlagGet(FLAG_SYS_B_DASH) - && IsRunningDisallowed(gObjectEvents[gPlayerAvatar.objectEventId].currentMetatileBehavior) == 0 - && !FollowerNPCComingThroughDoor() + && IsRunningDisallowed(gObjectEvents[gPlayerAvatar.objectEventId].currentMetatileBehavior) == 0 + && !FollowerNPCComingThroughDoor() && (I_ORAS_DOWSING_FLAG == 0 || (I_ORAS_DOWSING_FLAG != 0 && !FlagGet(I_ORAS_DOWSING_FLAG)))) { if (ObjectMovingOnRockStairs(&gObjectEvents[gPlayerAvatar.objectEventId], direction)) diff --git a/src/field_region_map.c b/src/field_region_map.c index 966d2d1327..f12422cc01 100644 --- a/src/field_region_map.c +++ b/src/field_region_map.c @@ -187,7 +187,7 @@ static void FieldUpdateRegionMap(void) sFieldRegionMapHandler->state++; break; case MAP_INPUT_R_BUTTON: - if (sFieldRegionMapHandler->regionMap.mapSecType == MAPSECTYPE_CITY_CANFLY + if (sFieldRegionMapHandler->regionMap.mapSecType == MAPSECTYPE_CITY_CANFLY && FlagGet(OW_FLAG_POKE_RIDER) && Overworld_MapTypeAllowsTeleportAndFly(gMapHeader.mapType) == TRUE) { PlaySE(SE_SELECT); diff --git a/src/follower_npc.c b/src/follower_npc.c index 3675a99bd3..cf04b5c660 100644 --- a/src/follower_npc.c +++ b/src/follower_npc.c @@ -284,7 +284,7 @@ static bool32 FollowerNPCHasRunningFrames(void) static bool32 IsStateMovement(u32 state) { - switch (state) + switch (state) { case MOVEMENT_ACTION_FACE_DOWN: case MOVEMENT_ACTION_FACE_UP: @@ -509,7 +509,7 @@ static void SetSurfJump(void) SetUpSurfBlobFieldEffect(follower); // Adjust surf head spawn location infront of follower. - switch (direction) + switch (direction) { case DIR_SOUTH: gFieldEffectArguments[1]++; // effect_y @@ -813,7 +813,7 @@ void CreateFollowerNPC(u32 gfx, u32 followerFlags, const u8 *scriptPtr) struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; struct ObjectEvent *follower; - struct ObjectEventTemplate npc = + struct ObjectEventTemplate npc = { .localId = OBJ_EVENT_ID_NPC_FOLLOWER, .graphicsId = gfx, @@ -929,7 +929,7 @@ u32 DetermineFollowerNPCState(struct ObjectEvent *follower, u32 state, u32 direc break; } - switch (state) + switch (state) { case MOVEMENT_ACTION_WALK_SLOW_DOWN ... MOVEMENT_ACTION_WALK_SLOW_RIGHT: // Slow walk. @@ -1286,7 +1286,7 @@ void NPCFollow(struct ObjectEvent *npc, u32 state, bool32 ignoreScriptActive) ObjectEventSetHeldMovement(follower, newState); PlayerLogCoordinates(player); - switch (newState) + switch (newState) { case MOVEMENT_ACTION_JUMP_2_DOWN ... MOVEMENT_ACTION_JUMP_2_RIGHT: case MOVEMENT_ACTION_JUMP_DOWN ... MOVEMENT_ACTION_JUMP_RIGHT: @@ -1305,7 +1305,7 @@ void CreateFollowerNPCAvatar(void) return; struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId]; - struct ObjectEventTemplate clone = + struct ObjectEventTemplate clone = { .localId = OBJ_EVENT_ID_NPC_FOLLOWER, .graphicsId = GetFollowerNPCSprite(), @@ -1830,7 +1830,7 @@ void ScriptFaceFollowerNPC(struct ScriptContext *ctx) followerDirection = playerDirection; //Flip direction. - switch (playerDirection) + switch (playerDirection) { case DIR_NORTH: playerDirection = DIR_SOUTH; @@ -1851,7 +1851,7 @@ void ScriptFaceFollowerNPC(struct ScriptContext *ctx) } } -static const u8 *const FollowerNPCHideMovementsSpeedTable[][4] = +static const u8 *const FollowerNPCHideMovementsSpeedTable[][4] = { [DIR_SOUTH] = {Common_Movement_WalkDownSlow, Common_Movement_WalkDown, Common_Movement_WalkDownFast, Common_Movement_WalkDownFaster}, [DIR_NORTH] = {Common_Movement_WalkUpSlow, Common_Movement_WalkUp, Common_Movement_WalkUpFast, Common_Movement_WalkUpFaster}, diff --git a/src/frontier_pass.c b/src/frontier_pass.c index a81d236157..361e8d0d47 100644 --- a/src/frontier_pass.c +++ b/src/frontier_pass.c @@ -606,7 +606,7 @@ static void LeaveFrontierPass(void) static u32 AllocateFrontierPassData(MainCallback callback) { - // This variable is a MAPSEC initially, but is recycled as a + // This variable is a MAPSEC initially, but is recycled as a // bare integer near the end of the function. mapsec_u8_t i; diff --git a/src/naming_screen.c b/src/naming_screen.c index 157f6868fd..b297be3db3 100644 --- a/src/naming_screen.c +++ b/src/naming_screen.c @@ -2152,7 +2152,7 @@ static const struct NamingScreenTemplate sWaldaWordsScreenTemplate = }; static const u8 sText_EnterCode[] = _("Enter code:"); -static const struct NamingScreenTemplate sCodeScreenTemplate = +static const struct NamingScreenTemplate sCodeScreenTemplate = { .copyExistingString = FALSE, .maxChars = CODE_NAME_LENGTH, @@ -2620,5 +2620,3 @@ static const struct SpritePalette sSpritePalettes[] = {gNamingScreenMenu_Pal[4], PALTAG_OK_BUTTON}, {} }; - - diff --git a/src/pokemon_sprite_visualizer.c b/src/pokemon_sprite_visualizer.c index d4a17d7a69..680e6603c0 100644 --- a/src/pokemon_sprite_visualizer.c +++ b/src/pokemon_sprite_visualizer.c @@ -1859,7 +1859,7 @@ static void HandleInput_PokemonSpriteVisualizer(u8 taskId) SetArrowInvisibility(data); PrintInstructionsOnWindow(data); UpdateMonAnimNames(taskId); - + if (data->followerspriteId != 0) gSprites[data->followerspriteId].invisible = FALSE; } diff --git a/src/pokemon_summary_screen.c b/src/pokemon_summary_screen.c index 4c0b5bdaca..c930626546 100644 --- a/src/pokemon_summary_screen.c +++ b/src/pokemon_summary_screen.c @@ -2115,7 +2115,7 @@ static void Task_ChangeSummaryMon(u8 taskId) } if (P_SUMMARY_SCREEN_MOVE_RELEARNER - && (sMonSummaryScreen->currPageIndex == PSS_PAGE_BATTLE_MOVES + && (sMonSummaryScreen->currPageIndex == PSS_PAGE_BATTLE_MOVES || sMonSummaryScreen->currPageIndex == PSS_PAGE_CONTEST_MOVES)) { gMoveRelearnerState = MOVE_RELEARNER_LEVEL_UP_MOVES; @@ -2288,7 +2288,7 @@ static void ChangePage(u8 taskId, s8 delta) TryUpdateRelearnType(TRY_SET_UPDATE); } - // to prevent nothing showing + // to prevent nothing showing if (currPageIndex >= PSS_PAGE_BATTLE_MOVES && !sMonSummaryScreen->hasRelearnableMoves) TryUpdateRelearnType(TRY_SET_UPDATE); else diff --git a/src/pokenav_region_map.c b/src/pokenav_region_map.c index 633ecdb056..c597d1aba2 100755 --- a/src/pokenav_region_map.c +++ b/src/pokenav_region_map.c @@ -223,7 +223,7 @@ static u32 HandleRegionMapInput(struct Pokenav_RegionMapMenu *state) state->callback = GetExitRegionMapMenuId; return POKENAV_MAP_FUNC_EXIT; case MAP_INPUT_R_BUTTON: - if (regionMap->mapSecType == MAPSECTYPE_CITY_CANFLY && FlagGet(OW_FLAG_POKE_RIDER) + if (regionMap->mapSecType == MAPSECTYPE_CITY_CANFLY && FlagGet(OW_FLAG_POKE_RIDER) && Overworld_MapTypeAllowsTeleportAndFly(gMapHeader.mapType) == TRUE) return POKENAV_MAP_FUNC_FLY; } @@ -775,7 +775,7 @@ void UpdateRegionMapHelpBarText(void) { struct RegionMap* regionMap = GetSubstructPtr(POKENAV_SUBSTRUCT_REGION_MAP); - if (regionMap->mapSecType == MAPSECTYPE_CITY_CANFLY && FlagGet(OW_FLAG_POKE_RIDER) + if (regionMap->mapSecType == MAPSECTYPE_CITY_CANFLY && FlagGet(OW_FLAG_POKE_RIDER) && Overworld_MapTypeAllowsTeleportAndFly(gMapHeader.mapType) == TRUE) { if (IsRegionMapZoomed()) diff --git a/src/scrcmd.c b/src/scrcmd.c index cf1fb1ab67..e05190c18d 100644 --- a/src/scrcmd.c +++ b/src/scrcmd.c @@ -1293,7 +1293,7 @@ bool8 ScrCmd_applymovement(struct ScriptContext *ctx) Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); // When applying script movements to follower, it may have frozen animation that must be cleared - if ((localId == OBJ_EVENT_ID_FOLLOWER && (objEvent = GetFollowerObject()) && objEvent->frozen) + if ((localId == OBJ_EVENT_ID_FOLLOWER && (objEvent = GetFollowerObject()) && objEvent->frozen) || ((objEvent = &gObjectEvents[GetObjectEventIdByLocalId(localId)]) && IS_OW_MON_OBJ(objEvent))) { ClearObjectEventMovement(objEvent, &gSprites[objEvent->spriteId]); @@ -1507,8 +1507,8 @@ bool8 ScrCmd_resetobjectsubpriority(struct ScriptContext *ctx) bool8 ScrCmd_faceplayer(struct ScriptContext *ctx) { Script_RequestEffects(SCREFF_V1 | SCREFF_HARDWARE); - if (PlayerHasFollowerNPC() - && gObjectEvents[GetFollowerNPCObjectId()].invisible == FALSE + if (PlayerHasFollowerNPC() + && gObjectEvents[GetFollowerNPCObjectId()].invisible == FALSE && gSelectedObjectEvent == GetFollowerNPCObjectId()) { struct ObjectEvent *npcFollower = &gObjectEvents[GetFollowerNPCObjectId()]; diff --git a/src/type_icons.c b/src/type_icons.c index 237c2c9a4f..0fdcb7b3f8 100644 --- a/src/type_icons.c +++ b/src/type_icons.c @@ -243,7 +243,7 @@ void LoadTypeIcons(u32 battler) struct Pokemon* mon = GetBattlerMon(battler); u32 species = GetMonData(mon, MON_DATA_SPECIES, NULL); - if (B_SHOW_TYPES == SHOW_TYPES_NEVER + if (B_SHOW_TYPES == SHOW_TYPES_NEVER || (B_SHOW_TYPES == SHOW_TYPES_SEEN && !GetSetPokedexFlag(SpeciesToNationalPokedexNum(species), FLAG_GET_SEEN))) return; @@ -566,4 +566,3 @@ static s32 GetTypeIconBounceMovement(s32 originalY, u32 position) struct Sprite *healthbox = &gSprites[gHealthboxSpriteIds[GetBattlerAtPosition(position)]]; return originalY + healthbox->y2; } - diff --git a/test/battle/ai/ai_multi.c b/test/battle/ai/ai_multi.c index 3fcc02d4c3..ae07207ae8 100644 --- a/test/battle/ai/ai_multi.c +++ b/test/battle/ai/ai_multi.c @@ -234,7 +234,7 @@ AI_MULTI_BATTLE_TEST("Pollen Puff: AI correctly scores moves with EFFECT_HIT_ENE MULTI_OPPONENT_A(SPECIES_WOBBUFFET) { Speed(1); HP(50); Moves(MOVE_POLLEN_PUFF); } MULTI_OPPONENT_B(SPECIES_WOBBUFFET) { Speed(1); HP(50); Moves(MOVE_POLLEN_PUFF); } } WHEN { - TURN { + TURN { // Targeting ally SCORE_EQ_VAL(opponentLeft, MOVE_POLLEN_PUFF, AI_SCORE_DEFAULT + WEAK_EFFECT, target:opponentRight); SCORE_EQ_VAL(playerRight, MOVE_POLLEN_PUFF, AI_SCORE_DEFAULT + WEAK_EFFECT, target:playerLeft); diff --git a/test/battle/ai/can_use_all_moves.c b/test/battle/ai/can_use_all_moves.c index b9333978cf..89fea68178 100644 --- a/test/battle/ai/can_use_all_moves.c +++ b/test/battle/ai/can_use_all_moves.c @@ -546,7 +546,7 @@ AI_DOUBLE_BATTLE_TEST("AI can use all moves, 601-700") case EFFECT_AURORA_VEIL: case EFFECT_GEAR_UP: case EFFECT_MAGNETIC_FLUX: - + // Skipped on purpose. case EFFECT_PROTECT: case EFFECT_NON_VOLATILE_STATUS: From 12970602ce50da94814cadade5ee5d54b0d0e7cc Mon Sep 17 00:00:00 2001 From: khbsd Date: Sun, 25 Jan 2026 03:15:01 -0600 Subject: [PATCH 17/23] fix: IsDoubleBattle() return value (#9012) --- include/battle.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/battle.h b/include/battle.h index 71dbb19491..6c528866cf 100755 --- a/include/battle.h +++ b/include/battle.h @@ -1210,7 +1210,7 @@ static inline struct PartyState *GetBattlerPartyState(u32 battler) static inline bool32 IsDoubleBattle(void) { - return (gBattleTypeFlags & BATTLE_TYPE_MORE_THAN_TWO_BATTLERS); + return !!(gBattleTypeFlags & BATTLE_TYPE_MORE_THAN_TWO_BATTLERS); } static inline bool32 IsSpreadMove(u32 moveTarget) From 1cbbb304a9576c5e665348cd2b48dbff2a98a9a4 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 25 Jan 2026 18:48:04 +0100 Subject: [PATCH 18/23] add PacFire as a contributor for design (#9017) Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> --- .all-contributorsrc | 9 +++++++++ CREDITS.md | 1 + 2 files changed, 10 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index a659a16bfc..ee9a9c49f7 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -624,6 +624,15 @@ "contributions": [ "design" ] + }, + { + "login": "PacFire", + "name": "PacFire", + "avatar_url": "https://avatars.githubusercontent.com/u/108960850?v=4", + "profile": "https://github.com/PacFire", + "contributions": [ + "design" + ] } ], "contributorsPerLine": 7, diff --git a/CREDITS.md b/CREDITS.md index 279a523ce1..10786eff5d 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -90,6 +90,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d SabataLunar
SabataLunar

🎨 + PacFire
PacFire

🎨 From 30db62d4b74017a0380f0f55d6a6103cb3d79dfb Mon Sep 17 00:00:00 2001 From: grintoul <166724814+grintoul1@users.noreply.github.com> Date: Sun, 25 Jan 2026 23:17:12 +0000 Subject: [PATCH 19/23] Remove player party shiny bit setting in CreateFrontierBrainPokemon (#9022) --- src/frontier_util.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/frontier_util.c b/src/frontier_util.c index 50f0c67167..e821f5ba2e 100644 --- a/src/frontier_util.c +++ b/src/frontier_util.c @@ -2612,8 +2612,6 @@ void CreateFrontierBrainPokemon(void) friendship = 0; } SetMonData(&gEnemyParty[monPartyId], MON_DATA_FRIENDSHIP, &friendship); - j = FALSE; - SetMonData(&gPlayerParty[MULTI_PARTY_SIZE + i], MON_DATA_IS_SHINY, &j); CalculateMonStats(&gEnemyParty[monPartyId]); monPartyId++; } From 21492e0f23dbee9ecf1f65af2dd921fed773fbc7 Mon Sep 17 00:00:00 2001 From: GGbond Date: Mon, 26 Jan 2026 21:48:39 +0800 Subject: [PATCH 20/23] Fix Foresight/Miracle Eye repeated-use behavior (#9025) --- data/battle_scripts_1.s | 7 ++ include/config/battle.h | 2 + include/constants/generational_changes.h | 2 + test/battle/move_effect/foresight.c | 117 ++++++++++++++++++++++- test/battle/move_effect/miracle_eye.c | 104 +++++++++++++++++++- 5 files changed, 227 insertions(+), 5 deletions(-) diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 7e9e0549c0..7007410e15 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -2357,6 +2357,9 @@ BattleScript_TryTailwindAbilitiesLoop_WindPower: BattleScript_EffectMiracleEye:: attackcanceler accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE + jumpifgenconfiglowerthan CONFIG_MIRACLE_EYE_FAIL, GEN_5, BattleScript_MiracleEyeSet + jumpifvolatile BS_TARGET, VOLATILE_MIRACLE_EYE, BattleScript_ButItFailed +BattleScript_MiracleEyeSet: setvolatile BS_TARGET, VOLATILE_MIRACLE_EYE goto BattleScript_IdentifiedFoe @@ -3479,7 +3482,11 @@ BattleScript_EffectSpikes:: BattleScript_EffectForesight:: attackcanceler accuracycheck BattleScript_ButItFailed, NO_ACC_CALC_CHECK_LOCK_ON + jumpifgenconfiglowerthan CONFIG_FORESIGHT_FAIL, GEN_3, BattleScript_ForesightFailCheck + jumpifgenconfiglowerthan CONFIG_FORESIGHT_FAIL, GEN_5, BattleScript_ForesightSet +BattleScript_ForesightFailCheck: jumpifvolatile BS_TARGET, VOLATILE_FORESIGHT, BattleScript_ButItFailed +BattleScript_ForesightSet: setvolatile BS_TARGET, VOLATILE_FORESIGHT BattleScript_IdentifiedFoe: attackanimation diff --git a/include/config/battle.h b/include/config/battle.h index 874817a5ab..9d2814cb85 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -128,6 +128,8 @@ #define B_AFTER_YOU_TURN_ORDER GEN_LATEST // In Gen8+, After You doesn't fail if the turn order wouldn't change after use. #define B_QUASH_TURN_ORDER GEN_LATEST // In Gen8+, Quash-affected battlers move according to speed order. Before Gen8, Quash-affected battlers move in the order they were affected by Quash. #define B_DESTINY_BOND_FAIL GEN_LATEST // In Gen7+, Destiny Bond fails if used repeatedly. +#define B_FORESIGHT_FAIL GEN_LATEST // In Gen2 and Gen5+, Foresight fails if used against a target already under its effect. +#define B_MIRACLE_EYE_FAIL GEN_LATEST // In Gen5+, Miracle Eye fails if used against a target already under its effect. #define B_PURSUIT_TARGET GEN_LATEST // In Gen4+, Pursuit attacks a switching opponent even if they weren't targeting them. Before Gen4, Pursuit only attacks a switching opponent that it originally targeted. #define B_SKIP_RECHARGE GEN_LATEST // In Gen1, recharging moves such as Hyper Beam skip the recharge if the target gets KO'd #define B_ENCORE_TARGET GEN_LATEST // In Gen5+, encored moves are allowed to choose a target diff --git a/include/constants/generational_changes.h b/include/constants/generational_changes.h index f8ef227c4a..ad700a6ea2 100644 --- a/include/constants/generational_changes.h +++ b/include/constants/generational_changes.h @@ -119,6 +119,8 @@ F(AFTER_YOU_TURN_ORDER, afterYouTurnOrder, (u32, GEN_COUNT - 1)) \ F(QUASH_TURN_ORDER, quashTurnOrder, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \ F(DESTINY_BOND_FAIL, destinyBondFail, (u32, GEN_COUNT - 1)) \ + F(FORESIGHT_FAIL, foresightFail, (u32, GEN_COUNT - 1)) \ + F(MIRACLE_EYE_FAIL, miracleEyeFail, (u32, GEN_COUNT - 1)) \ F(PURSUIT_TARGET, pursuitTarget, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \ F(SKIP_RECHARGE, skipRecharge, (u32, GEN_COUNT - 1)) /* TODO: use in tests */ \ F(ENCORE_TARGET, encoreTarget, (u32, GEN_COUNT - 1)) \ diff --git a/test/battle/move_effect/foresight.c b/test/battle/move_effect/foresight.c index 59209030d0..9879774bab 100644 --- a/test/battle/move_effect/foresight.c +++ b/test/battle/move_effect/foresight.c @@ -1,14 +1,123 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("Foresight removes Ghost's type immunity to Normal and Fighting types") +ASSUMPTIONS +{ + ASSUME(GetMoveEffect(MOVE_FORESIGHT) == EFFECT_FORESIGHT); +} + +SINGLE_BATTLE_TEST("Foresight removes Ghost's type immunity to Normal and Fighting types") +{ + GIVEN { + ASSUME(GetMoveType(MOVE_SCRATCH) == TYPE_NORMAL); + ASSUME(GetMoveType(MOVE_LOW_KICK) == TYPE_FIGHTING); + ASSUME(GetSpeciesType(SPECIES_GENGAR, 0) == TYPE_GHOST); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_FORESIGHT, MOVE_SCRATCH, MOVE_LOW_KICK); } + OPPONENT(SPECIES_GENGAR) { Moves(MOVE_SPLASH); } + } WHEN { + TURN { MOVE(player, MOVE_FORESIGHT); MOVE(opponent, MOVE_SPLASH); } + TURN { MOVE(player, MOVE_SCRATCH); MOVE(opponent, MOVE_SPLASH); } + TURN { MOVE(player, MOVE_LOW_KICK); MOVE(opponent, MOVE_SPLASH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESIGHT, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player); + HP_BAR(opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_LOW_KICK, player); + HP_BAR(opponent); + } +} + +SINGLE_BATTLE_TEST("Foresight always hits unless the target is semi-invulnerable") +{ + bool32 semiInvulnerable = FALSE; + PARAMETRIZE { semiInvulnerable = FALSE; } + PARAMETRIZE { semiInvulnerable = TRUE; } + + GIVEN { + ASSUME(GetMoveEffect(MOVE_DOUBLE_TEAM) == EFFECT_EVASION_UP); + ASSUME(GetMoveEffect(MOVE_FLY) == EFFECT_SEMI_INVULNERABLE); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_FORESIGHT, MOVE_SPLASH); Speed(10); } + OPPONENT(SPECIES_SQUAWKABILLY) { Moves(MOVE_DOUBLE_TEAM, MOVE_FLY); Speed(20); } + } WHEN { + if (semiInvulnerable) + TURN { MOVE(player, MOVE_FORESIGHT); MOVE(opponent, MOVE_FLY); } + else + TURN { MOVE(player, MOVE_FORESIGHT); MOVE(opponent, MOVE_DOUBLE_TEAM); } + + if (semiInvulnerable) + TURN { MOVE(player, MOVE_SPLASH); SKIP_TURN(opponent); } + } SCENE { + if (semiInvulnerable) { + MESSAGE("The opposing Squawkabilly avoided the attack!"); + } else { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_TEAM, opponent); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESIGHT, player); + } + } +} + +SINGLE_BATTLE_TEST("Foresight causes moves against the target to ignore positive evasion stat stages") +{ + PASSES_RANDOMLY(100, 100, RNG_ACCURACY); + GIVEN { + ASSUME(GetMoveEffect(MOVE_DOUBLE_TEAM) == EFFECT_EVASION_UP); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_FORESIGHT, MOVE_SCRATCH); Speed(10); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_DOUBLE_TEAM, MOVE_SPLASH); Speed(20); } + } WHEN { + TURN { MOVE(player, MOVE_FORESIGHT); MOVE(opponent, MOVE_DOUBLE_TEAM); } + TURN { MOVE(player, MOVE_SCRATCH); MOVE(opponent, MOVE_SPLASH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_TEAM, opponent); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESIGHT, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player); + HP_BAR(opponent); + } +} + +SINGLE_BATTLE_TEST("Foresight fails if the target is already under its effect (Gen 2 and Gen5+)") +{ + u32 genConfig = GEN_2; + PARAMETRIZE { genConfig = GEN_2; } + PARAMETRIZE { genConfig = GEN_5; } + GIVEN { + WITH_CONFIG(CONFIG_FORESIGHT_FAIL, genConfig); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_FORESIGHT); } + TURN { MOVE(player, MOVE_FORESIGHT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESIGHT, player); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESIGHT, player); + MESSAGE("But it failed!"); + } +} + +SINGLE_BATTLE_TEST("Foresight doesn't fail if the target is already under its effect (Gen 3-4)") +{ + u32 genConfig = GEN_3; + PARAMETRIZE { genConfig = GEN_3; } + PARAMETRIZE { genConfig = GEN_4; } + GIVEN { + WITH_CONFIG(CONFIG_FORESIGHT_FAIL, genConfig); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_FORESIGHT); } + TURN { MOVE(player, MOVE_FORESIGHT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESIGHT, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FORESIGHT, player); + NOT MESSAGE("But it failed!"); + } +} + TO_DO_BATTLE_TEST("Foresight causes accuracy/evasion stat changes only between the user/target when the user's accuracy stage is less than the target's evasion stage (Gen 2)") TO_DO_BATTLE_TEST("Foresight causes all moves against the target to ignore evasion stat changes (Gen 3)") TO_DO_BATTLE_TEST("Foresight causes all moves against the target to ignore only positive evasion stat changes (Gen 4+)") // Eg. Doesn't ignore Sweet Scent TO_DO_BATTLE_TEST("Foresight doesn't cause moves used against the target to always hit (Gen 2-3)") TO_DO_BATTLE_TEST("Foresight causes moves used against the target to always hit (Gen 4+)") -TO_DO_BATTLE_TEST("Foresight does not make moves hit semi-invulnerable targets") -TO_DO_BATTLE_TEST("Foresight fails if the target is already under its effect (Gen 2 and Gen5+)") -TO_DO_BATTLE_TEST("Foresight doesn't fail if the target is already under its effect (Gen 3-4)") TO_DO_BATTLE_TEST("Baton Pass passes Foresight's effect (Gen 2)"); TO_DO_BATTLE_TEST("Baton Pass doesn't pass Foresight's effect (Gen 3+)"); diff --git a/test/battle/move_effect/miracle_eye.c b/test/battle/move_effect/miracle_eye.c index 784323f1cc..4f819036a5 100644 --- a/test/battle/move_effect/miracle_eye.c +++ b/test/battle/move_effect/miracle_eye.c @@ -1,4 +1,106 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("TODO: Write Miracle Eye (Move Effect) test titles") +ASSUMPTIONS +{ + ASSUME(GetMoveEffect(MOVE_MIRACLE_EYE) == EFFECT_MIRACLE_EYE); +} + +SINGLE_BATTLE_TEST("Miracle Eye removes Dark-type immunity to Psychic-type moves") +{ + GIVEN { + ASSUME(GetMoveType(MOVE_PSYCHIC) == TYPE_PSYCHIC); + ASSUME(GetSpeciesType(SPECIES_UMBREON, 0) == TYPE_DARK); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_MIRACLE_EYE, MOVE_PSYCHIC); } + OPPONENT(SPECIES_UMBREON) { Moves(MOVE_SPLASH); } + } WHEN { + TURN { MOVE(player, MOVE_PSYCHIC); MOVE(opponent, MOVE_SPLASH); } + TURN { MOVE(player, MOVE_MIRACLE_EYE); MOVE(opponent, MOVE_SPLASH); } + TURN { MOVE(player, MOVE_PSYCHIC); MOVE(opponent, MOVE_SPLASH); } + } SCENE { + NOT HP_BAR(opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRACLE_EYE, player); + HP_BAR(opponent); + } +} + +SINGLE_BATTLE_TEST("Miracle Eye always hits unless the target is semi-invulnerable") +{ + bool32 semiInvulnerable = FALSE; + PARAMETRIZE { semiInvulnerable = FALSE; } + PARAMETRIZE { semiInvulnerable = TRUE; } + + GIVEN { + ASSUME(GetMoveEffect(MOVE_DOUBLE_TEAM) == EFFECT_EVASION_UP); + ASSUME(GetMoveEffect(MOVE_FLY) == EFFECT_SEMI_INVULNERABLE); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_MIRACLE_EYE, MOVE_SPLASH); Speed(10); } + OPPONENT(SPECIES_SQUAWKABILLY) { Moves(MOVE_DOUBLE_TEAM, MOVE_FLY); Speed(20); } + } WHEN { + if (semiInvulnerable) + TURN { MOVE(player, MOVE_MIRACLE_EYE); MOVE(opponent, MOVE_FLY); } + else + TURN { MOVE(player, MOVE_MIRACLE_EYE); MOVE(opponent, MOVE_DOUBLE_TEAM); } + + if (semiInvulnerable) + TURN { MOVE(player, MOVE_SPLASH); SKIP_TURN(opponent); } + } SCENE { + if (semiInvulnerable) { + MESSAGE("Wobbuffet's attack missed!"); + } else { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_TEAM, opponent); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRACLE_EYE, player); + } + } +} + +SINGLE_BATTLE_TEST("Miracle Eye causes moves against the target to ignore positive evasion stat stages") +{ + PASSES_RANDOMLY(100, 100, RNG_ACCURACY); + GIVEN { + ASSUME(GetMoveEffect(MOVE_DOUBLE_TEAM) == EFFECT_EVASION_UP); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_MIRACLE_EYE, MOVE_SCRATCH); Speed(10); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_DOUBLE_TEAM, MOVE_SPLASH); Speed(20); } + } WHEN { + TURN { MOVE(player, MOVE_MIRACLE_EYE); MOVE(opponent, MOVE_DOUBLE_TEAM); } + TURN { MOVE(player, MOVE_SCRATCH); MOVE(opponent, MOVE_SPLASH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_TEAM, opponent); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRACLE_EYE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player); + HP_BAR(opponent); + } +} + +SINGLE_BATTLE_TEST("Miracle Eye fails if the target is already affected by Miracle Eye (Gen5+)") +{ + GIVEN { + WITH_CONFIG(CONFIG_MIRACLE_EYE_FAIL, GEN_5); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_MIRACLE_EYE); } + TURN { MOVE(player, MOVE_MIRACLE_EYE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRACLE_EYE, player); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRACLE_EYE, player); + MESSAGE("But it failed!"); + } +} + +SINGLE_BATTLE_TEST("Miracle Eye does not fail if the target is already affected by Miracle Eye (Gen4)") +{ + GIVEN { + WITH_CONFIG(CONFIG_MIRACLE_EYE_FAIL, GEN_4); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_MIRACLE_EYE); } + TURN { MOVE(player, MOVE_MIRACLE_EYE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRACLE_EYE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRACLE_EYE, player); + NOT MESSAGE("But it failed!"); + } +} From b27eb839edb4d6fd51b26abfbde5c8db87d86ba4 Mon Sep 17 00:00:00 2001 From: GGbond Date: Mon, 26 Jan 2026 21:51:55 +0800 Subject: [PATCH 21/23] Fix Crafty Shield consecutive-use failure (#9023) --- src/battle_script_commands.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 83d665a06d..58362adda5 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -9753,7 +9753,8 @@ static void Cmd_setprotectlike(void) if ((sProtectSuccessRates[gDisableStructs[gBattlerAttacker].protectUses] >= RandomUniform(RNG_PROTECT_FAIL, 0, USHRT_MAX) && notLastTurn) || (protectMethod == PROTECT_WIDE_GUARD && GetConfig(CONFIG_WIDE_GUARD) >= GEN_6) - || (protectMethod == PROTECT_QUICK_GUARD && GetConfig(CONFIG_QUICK_GUARD) >= GEN_6)) + || (protectMethod == PROTECT_QUICK_GUARD && GetConfig(CONFIG_QUICK_GUARD) >= GEN_6) + || (protectMethod == PROTECT_CRAFTY_SHIELD)) { if (GetMoveEffect(gCurrentMove) == EFFECT_ENDURE) { From cb697f5d7dca6a2ba3c78ba6c9e114ef80f26fe6 Mon Sep 17 00:00:00 2001 From: PhallenTree <168426989+PhallenTree@users.noreply.github.com> Date: Mon, 26 Jan 2026 17:16:01 +0000 Subject: [PATCH 22/23] Fixes Beak Blast burning attacker when charging 2 turn move (#9026) --- src/battle_script_commands.c | 2 +- test/battle/move_effect/beak_blast.c | 30 +++++++- test/battle/move_effect/protect.c | 108 +++++++++++++++++++++++++++ 3 files changed, 138 insertions(+), 2 deletions(-) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 58362adda5..1421eaf9e6 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -6173,7 +6173,7 @@ static void Cmd_moveend(void) // Not strictly a protect effect, but works the same way if (IsBattlerUsingBeakBlast(gBattlerTarget) && CanBeBurned(gBattlerAttacker, gBattlerAttacker, GetBattlerAbility(gBattlerAttacker)) - && !(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT)) + && IsBattlerTurnDamaged(gBattlerTarget)) { gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE; gBattleMons[gBattlerAttacker].status1 = STATUS1_BURN; diff --git a/test/battle/move_effect/beak_blast.c b/test/battle/move_effect/beak_blast.c index a837bda712..4cbea8a596 100644 --- a/test/battle/move_effect/beak_blast.c +++ b/test/battle/move_effect/beak_blast.c @@ -87,7 +87,6 @@ SINGLE_BATTLE_TEST("Beak Blast burns only when contact moves are used") OPPONENT(SPECIES_WOBBUFFET); } WHEN { TURN { MOVE(opponent, move); MOVE(player, MOVE_BEAK_BLAST); } - TURN {} } SCENE { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_BEAK_BLAST_SETUP, player); MESSAGE("Wobbuffet started heating up its beak!"); @@ -112,6 +111,35 @@ SINGLE_BATTLE_TEST("Beak Blast burns only when contact moves are used") } } +SINGLE_BATTLE_TEST("Beak Blast doesn't burn when charging a two turn move") +{ + u32 move; + PARAMETRIZE { move = MOVE_BOUNCE; } + PARAMETRIZE { move = MOVE_DIG; } + + GIVEN { + ASSUME(MoveMakesContact(MOVE_BOUNCE)); + ASSUME(MoveMakesContact(MOVE_DIG)); + ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_BOUNCE)].twoTurnEffect); + ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_DIG)].twoTurnEffect); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, move); MOVE(player, MOVE_BEAK_BLAST); } + } SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_BEAK_BLAST_SETUP, player); + MESSAGE("Wobbuffet started heating up its beak!"); + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + + NONE_OF { + HP_BAR(player); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_BRN, opponent); + MESSAGE("The opposing Wobbuffet was burned!"); + STATUS_ICON(opponent, burn: TRUE); + } + } +} + SINGLE_BATTLE_TEST("Beak Blast doesn't burn fire types") { GIVEN { diff --git a/test/battle/move_effect/protect.c b/test/battle/move_effect/protect.c index 0f486c6405..930f6d829a 100644 --- a/test/battle/move_effect/protect.c +++ b/test/battle/move_effect/protect.c @@ -125,6 +125,36 @@ SINGLE_BATTLE_TEST("Protect: King's Shield, Silk Trap and Obstruct protect from } } +SINGLE_BATTLE_TEST("Protect: King's Shield, Silk Trap and Obstruct don't lower stats when charging a two turn move") +{ + u32 move, protectMove; + PARAMETRIZE { move = MOVE_BOUNCE; protectMove = MOVE_KINGS_SHIELD; } + PARAMETRIZE { move = MOVE_DIG; protectMove = MOVE_KINGS_SHIELD; } + PARAMETRIZE { move = MOVE_BOUNCE; protectMove = MOVE_SILK_TRAP; } + PARAMETRIZE { move = MOVE_DIG; protectMove = MOVE_SILK_TRAP; } + PARAMETRIZE { move = MOVE_BOUNCE; protectMove = MOVE_OBSTRUCT; } + PARAMETRIZE { move = MOVE_DIG; protectMove = MOVE_OBSTRUCT; } + + GIVEN { + ASSUME(MoveMakesContact(MOVE_BOUNCE)); + ASSUME(MoveMakesContact(MOVE_DIG)); + ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_BOUNCE)].twoTurnEffect); + ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_DIG)].twoTurnEffect); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, protectMove); MOVE(opponent, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, protectMove, player); + + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + NONE_OF { + HP_BAR(player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + } + } +} + SINGLE_BATTLE_TEST("Protect: Spiky Shield does 1/8 dmg of max hp of attackers making contact and may faint them") { u16 usedMove = MOVE_NONE; @@ -162,6 +192,32 @@ SINGLE_BATTLE_TEST("Protect: Spiky Shield does 1/8 dmg of max hp of attackers ma } } +SINGLE_BATTLE_TEST("Protect: Spiky Shield doesn't hurt attacker when charging a two turn move") +{ + u32 move; + PARAMETRIZE { move = MOVE_BOUNCE; } + PARAMETRIZE { move = MOVE_DIG; } + + GIVEN { + ASSUME(MoveMakesContact(MOVE_BOUNCE)); + ASSUME(MoveMakesContact(MOVE_DIG)); + ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_BOUNCE)].twoTurnEffect); + ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_DIG)].twoTurnEffect); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SPIKY_SHIELD); MOVE(opponent, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SPIKY_SHIELD, player); + + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + NONE_OF { + HP_BAR(player); + HP_BAR(opponent); + } + } +} + SINGLE_BATTLE_TEST("Protect: Baneful Bunker poisons Pokémon for moves making contact") { u16 usedMove = MOVE_NONE; @@ -214,6 +270,32 @@ SINGLE_BATTLE_TEST("Protect: Baneful Bunker can't poison Pokémon if they are al } } +SINGLE_BATTLE_TEST("Protect: Baneful Bunker doesn't poison attacker when charging a two turn move") +{ + u32 move; + PARAMETRIZE { move = MOVE_BOUNCE; } + PARAMETRIZE { move = MOVE_DIG; } + + GIVEN { + ASSUME(MoveMakesContact(MOVE_BOUNCE)); + ASSUME(MoveMakesContact(MOVE_DIG)); + ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_BOUNCE)].twoTurnEffect); + ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_DIG)].twoTurnEffect); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_BANEFUL_BUNKER); MOVE(opponent, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_BANEFUL_BUNKER, player); + + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + NONE_OF { + HP_BAR(player); + STATUS_ICON(opponent, STATUS1_POISON); + } + } +} + SINGLE_BATTLE_TEST("Protect: Burning Bulwark burns Pokémon for moves making contact") { u16 usedMove = MOVE_NONE; @@ -266,6 +348,32 @@ SINGLE_BATTLE_TEST("Protect: Burning Bulwark can't burn Pokémon if they are alr } } +SINGLE_BATTLE_TEST("Protect: Burning Bulwark doesn't burn attacker when charging a two turn move") +{ + u32 move; + PARAMETRIZE { move = MOVE_BOUNCE; } + PARAMETRIZE { move = MOVE_DIG; } + + GIVEN { + ASSUME(MoveMakesContact(MOVE_BOUNCE)); + ASSUME(MoveMakesContact(MOVE_DIG)); + ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_BOUNCE)].twoTurnEffect); + ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_DIG)].twoTurnEffect); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_BURNING_BULWARK); MOVE(opponent, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_BURNING_BULWARK, player); + + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + NONE_OF { + HP_BAR(player); + STATUS_ICON(opponent, STATUS1_BURN); + } + } +} + SINGLE_BATTLE_TEST("Protect: Recoil damage is not applied if target was protected") { u32 j, k; From 433f55157ce569b84952baf367ed09af121db015 Mon Sep 17 00:00:00 2001 From: psf <77138753+pkmnsnfrn@users.noreply.github.com> Date: Mon, 26 Jan 2026 10:19:55 -0800 Subject: [PATCH 23/23] fix: updated rulings about big feature label (#9027) --- docs/team_procedures/schedule.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/team_procedures/schedule.md b/docs/team_procedures/schedule.md index 6c948a8b3a..efcff17b26 100644 --- a/docs/team_procedures/schedule.md +++ b/docs/team_procedures/schedule.md @@ -34,9 +34,9 @@ will NOT be merged until after the next Minor Release. --- ## What is a "Big Feature"? -* If the original owner of the PR thinks a feature should be labeled a Big Feature, it is, no questions asked -* If a reviewer thinks a PR is a Big Feature, then it is -* If the two disagree, it can be discussed in a PR thread, and can ultimately be resolved with a Maintainer vote. +* If any maintainer thinks a PR is a Big Feature, then it is, no question asked +* If there is disagreement, it can be discussed in a PR thread, and can ultimately be resolved with a Maintainer vote. +* If you believe your PR should have this feature, please let a maintainer know. ### How To Identify a Big Feature * **Big diffs**: It's easy for something to go unnoticed in review when it's a tiny part of a massive diff.