diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 5982716bc9..d45487e644 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1351,7 +1351,6 @@ .4byte \func .endm -@ callnative macros .macro savetarget callnative BS_SaveTarget .endm @@ -1727,6 +1726,11 @@ .4byte \failInstr .endm + .macro jumpifcommanderactive jumpInstr:req + callnative BS_JumpIfCommanderActive + .4byte \jumpInstr + .endm + @ various command changed to more readable macros .macro cancelmultiturnmoves battler:req various \battler, VARIOUS_CANCEL_MULTI_TURN_MOVES diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 16168ca124..05eb64d15a 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -3265,6 +3265,7 @@ BattleScript_EffectRoar:: attackstring ppreduce jumpifroarfails BattleScript_ButItFailed + jumpifcommanderactive BattleScript_ButItFailed jumpifability BS_TARGET, ABILITY_GUARD_DOG, BattleScript_ButItFailed jumpifability BS_TARGET, ABILITY_SUCTION_CUPS, BattleScript_AbilityPreventsPhasingOut jumpifstatus3 BS_TARGET, STATUS3_ROOTED, BattleScript_PrintMonIsRooted @@ -8008,11 +8009,52 @@ BattleScript_CostarActivates:: BattleScript_ZeroToHeroActivates:: pause B_WAIT_TIME_SHORT - call BattleScript_AbilityPopUp + call BattleScript_AbilityPopUpScripting printstring STRINGID_ZEROTOHEROTRANSFORMATION waitmessage B_WAIT_TIME_LONG end3 +BattleScript_CommanderActivates:: + pause B_WAIT_TIME_SHORT + call BattleScript_AbilityPopUpScripting + printstring STRINGID_COMMANDERACTIVATES + waitmessage B_WAIT_TIME_LONG +BattleScript_CommanderAtkIncrease: + setbyte sSTAT_ANIM_PLAYED, FALSE + playstatchangeanimation BS_ATTACKER, BIT_ATK | BIT_DEF | BIT_SPATK | BIT_SPDEF | BIT_SPEED, STAT_CHANGE_BY_TWO + setstatchanger STAT_ATK, 2, FALSE + statbuffchange MOVE_EFFECT_AFFECTS_USER | STAT_CHANGE_ALLOW_PTR, BattleScript_CommanderDefIncrease + jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_INCREASE, BattleScript_CommanderDefIncrease + printfromtable gStatUpStringIds + waitmessage B_WAIT_TIME_LONG +BattleScript_CommanderDefIncrease: + setstatchanger STAT_DEF, 2, FALSE + statbuffchange MOVE_EFFECT_AFFECTS_USER | STAT_CHANGE_ALLOW_PTR, BattleScript_CommanderSpAtkIncrease + jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_INCREASE, BattleScript_CommanderSpAtkIncrease + printfromtable gStatUpStringIds + waitmessage B_WAIT_TIME_LONG +BattleScript_CommanderSpAtkIncrease: + setstatchanger STAT_SPATK, 2, FALSE + statbuffchange MOVE_EFFECT_AFFECTS_USER | STAT_CHANGE_ALLOW_PTR, BattleScript_CommanderSpDefIncrease + jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_INCREASE, BattleScript_CommanderSpDefIncrease + printfromtable gStatUpStringIds + waitmessage B_WAIT_TIME_LONG +BattleScript_CommanderSpDefIncrease: + setstatchanger STAT_SPDEF, 2, FALSE + statbuffchange MOVE_EFFECT_AFFECTS_USER | STAT_CHANGE_ALLOW_PTR, BattleScript_CommanderSpeedIncrease + jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_INCREASE, BattleScript_CommanderSpeedIncrease + printfromtable gStatUpStringIds + waitmessage B_WAIT_TIME_LONG +BattleScript_CommanderSpeedIncrease: + setstatchanger STAT_SPEED, 2, FALSE + statbuffchange MOVE_EFFECT_AFFECTS_USER | STAT_CHANGE_ALLOW_PTR, BattleScript_CommanderEnd + jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_INCREASE, BattleScript_CommanderEnd + printfromtable gStatUpStringIds + waitmessage B_WAIT_TIME_LONG +BattleScript_CommanderEnd: + restoreattacker + end3 + BattleScript_HospitalityActivates:: pause B_WAIT_TIME_SHORT call BattleScript_AbilityPopUp @@ -9533,6 +9575,14 @@ BattleScript_StickyBarbTransfer:: removeitem BS_TARGET return +BattleScript_RedCardActivationNoSwitch:: + playanimation BS_SCRIPTING, B_ANIM_HELD_ITEM_EFFECT + printstring STRINGID_REDCARDACTIVATE + waitmessage B_WAIT_TIME_LONG + removeitem BS_SCRIPTING + restoretarget + return + BattleScript_RedCardActivates:: playanimation BS_SCRIPTING, B_ANIM_HELD_ITEM_EFFECT printstring STRINGID_REDCARDACTIVATE diff --git a/include/battle.h b/include/battle.h index f1628ddd9b..16df7c500a 100644 --- a/include/battle.h +++ b/include/battle.h @@ -201,10 +201,13 @@ struct ProtectStruct u16 eatMirrorHerb:1; u16 activateOpportunist:2; // 2 - to copy stats. 1 - stats copied (do not repeat). 0 - no stats to copy u16 usedAllySwitch:1; + u16 padding:2; + // End of 16-bit bitfield u32 physicalDmg; u32 specialDmg; u8 physicalBattlerId; u8 specialBattlerId; + }; struct SpecialStatus @@ -818,9 +821,13 @@ struct BattleStruct u8 boosterEnergyActivates; u8 distortedTypeMatchups; u8 categoryOverride; // for Z-Moves and Max Moves + u8 commandingDondozo; + u16 commanderActive[NUM_BATTLE_SIDES]; u32 stellarBoostFlags[NUM_BATTLE_SIDES]; // stored as a bitfield of flags for all types for each side u8 fickleBeamBoosted:1; u8 obedienceResult:3; + u8 padding:4; + u8 usedEjectItem; u8 usedMicleBerry; }; diff --git a/include/battle_script_commands.h b/include/battle_script_commands.h index 4f1593d4d8..7326647d41 100644 --- a/include/battle_script_commands.h +++ b/include/battle_script_commands.h @@ -57,6 +57,8 @@ bool32 IsMoveNotAllowedInSkyBattles(u32 move); bool32 DoSwitchInAbilities(u32 battlerId); u8 GetFirstFaintedPartyIndex(u8 battlerId); bool32 IsMoveAffectedByParentalBond(u32 move, u32 battler); +void SaveBattlerTarget(u32 battler); +void SaveBattlerAttacker(u32 battler); extern void (* const gBattleScriptingCommandsTable[])(void); extern const struct StatFractions gAccuracyStageRatios[]; diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 84c1b70cf4..9e2fb84485 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -412,6 +412,7 @@ extern const u8 BattleScript_BattlerGotOverItsInfatuation[]; extern const u8 BattleScript_Pickpocket[]; extern const u8 BattleScript_StickyBarbTransfer[]; extern const u8 BattleScript_AttackerItemStatRaise[]; +extern const u8 BattleScript_RedCardActivationNoSwitch[]; extern const u8 BattleScript_RedCardActivates[]; extern const u8 BattleScript_EjectButtonActivates[]; extern const u8 BattleScript_EjectPackActivate_Ret[]; @@ -477,6 +478,7 @@ extern const u8 BattleScript_CudChewActivates[]; extern const u8 BattleScript_SupremeOverlordActivates[]; extern const u8 BattleScript_CostarActivates[]; extern const u8 BattleScript_ZeroToHeroActivates[]; +extern const u8 BattleScript_CommanderActivates[]; extern const u8 BattleScript_HospitalityActivates[]; extern const u8 BattleScript_ToxicDebrisActivates[]; extern const u8 BattleScript_EarthEaterActivates[]; diff --git a/include/constants/battle.h b/include/constants/battle.h index f9800ef4b3..560448159a 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -166,7 +166,7 @@ #define STATUS3_YAWN_TURN(num) (((num) << 11) & STATUS3_YAWN) #define STATUS3_IMPRISONED_OTHERS (1 << 13) #define STATUS3_GRUDGE (1 << 14) -#define STATUS3___UNUSED (1 << 15) +#define STATUS3_COMMANDER (1 << 15) #define STATUS3_GASTRO_ACID (1 << 16) #define STATUS3_EMBARGO (1 << 17) #define STATUS3_UNDERWATER (1 << 18) @@ -183,7 +183,8 @@ #define STATUS3_LASER_FOCUS (1 << 29) #define STATUS3_POWER_TRICK (1 << 30) #define STATUS3_SKY_DROPPED (1 << 31) // Target of Sky Drop -#define STATUS3_SEMI_INVULNERABLE (STATUS3_UNDERGROUND | STATUS3_ON_AIR | STATUS3_UNDERWATER | STATUS3_PHANTOM_FORCE) +#define STATUS3_SEMI_INVULNERABLE_NO_COMMANDER (STATUS3_UNDERGROUND | STATUS3_ON_AIR | STATUS3_UNDERWATER | STATUS3_PHANTOM_FORCE) // Exception for Transform / Imposter +#define STATUS3_SEMI_INVULNERABLE (STATUS3_SEMI_INVULNERABLE_NO_COMMANDER | STATUS3_COMMANDER) #define STATUS4_ELECTRIFIED (1 << 0) #define STATUS4_MUD_SPORT (1 << 1) // Only used if B_SPORT_TURNS < GEN_6 @@ -401,8 +402,9 @@ #define MOVE_EFFECT_SECRET_POWER 77 #define MOVE_EFFECT_PSYCHIC_NOISE 78 #define MOVE_EFFECT_TERA_BLAST 79 +#define MOVE_EFFECT_ORDER_UP 80 -#define NUM_MOVE_EFFECTS 80 +#define NUM_MOVE_EFFECTS 81 #define MOVE_EFFECT_AFFECTS_USER 0x2000 #define MOVE_EFFECT_CERTAIN 0x4000 diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h index 5da278110f..770e773f80 100644 --- a/include/constants/battle_move_effects.h +++ b/include/constants/battle_move_effects.h @@ -354,6 +354,7 @@ enum { EFFECT_DRAGON_DARTS, EFFECT_GUARDIAN_OF_ALOLA, EFFECT_SHELL_SIDE_ARM, + EFFECT_ORDER_UP, NUM_BATTLE_MOVE_EFFECTS, }; diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index 9fbb70a223..08c80248fe 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -712,8 +712,9 @@ #define STRINGID_FOGLIFTED 710 #define STRINGID_PKMNMADESHELLGLEAM 711 #define STRINGID_FICKLEBEAMDOUBLED 712 +#define STRINGID_COMMANDERACTIVATES 713 -#define BATTLESTRINGS_COUNT 713 +#define BATTLESTRINGS_COUNT 714 // This is the string id that gBattleStringsTable starts with. // String ids before this (e.g. STRINGID_INTROMSG) are not in the table, diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index acc13a7378..e686b52071 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -835,6 +835,9 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) if (IsTwoTurnNotSemiInvulnerableMove(battlerAtk, move) && CanTargetFaintAi(battlerDef, battlerAtk)) RETURN_SCORE_MINUS(10); + if (gBattleStruct->commandingDondozo & (1u << battlerDef)) + RETURN_SCORE_MINUS(20); + // check if negates type switch (effectiveness) { diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 14f5c44ffc..05dbbe916a 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -412,6 +412,9 @@ bool32 IsDamageMoveUnusable(u32 move, u32 battlerAtk, u32 battlerDef) if (battlerDef == BATTLE_PARTNER(battlerAtk)) battlerDefAbility = aiData->abilities[battlerDef]; + if (gBattleStruct->commandingDondozo & (1u << battlerDef)) + return TRUE; + switch (battlerDefAbility) { case ABILITY_LIGHTNING_ROD: @@ -1508,6 +1511,8 @@ bool32 IsSemiInvulnerable(u32 battlerDef, u32 move) { if (gStatuses3[battlerDef] & STATUS3_PHANTOM_FORCE) return TRUE; + else if (gBattleStruct->commandingDondozo & (1u << battlerDef)) + return TRUE; else if (!gMovesInfo[move].damagesAirborne && gStatuses3[battlerDef] & STATUS3_ON_AIR) return TRUE; else if (!gMovesInfo[move].damagesUnderwater && gStatuses3[battlerDef] & STATUS3_UNDERWATER) diff --git a/src/battle_main.c b/src/battle_main.c index 92e1618fc7..d30d7a927f 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3343,6 +3343,16 @@ const u8* FaintClearSetData(u32 battler) gBattleStruct->palaceFlags &= ~(1u << battler); gBattleStruct->boosterEnergyActivates &= ~(1u << battler); + if (gBattleStruct->commanderActive[battler] != SPECIES_NONE) + { + u32 partner = BATTLE_PARTNER(battler); + if (IsBattlerAlive(partner)) + { + BtlController_EmitSpriteInvisibility(partner, BUFFER_A, FALSE); + MarkBattlerForControllerExec(partner); + } + } + for (i = 0; i < ARRAY_COUNT(gSideTimers); i++) { // User of sticky web fainted, so reset the stored battler ID @@ -4194,7 +4204,7 @@ static void HandleTurnActionSelectionState(void) || gBattleStruct->absentBattlerFlags & (1u << GetBattlerAtPosition(BATTLE_PARTNER(position))) || gBattleCommunication[GetBattlerAtPosition(BATTLE_PARTNER(position))] == STATE_WAIT_ACTION_CONFIRMED) { - if (gBattleStruct->absentBattlerFlags & (1u << battler)) + if ((gBattleStruct->absentBattlerFlags & (1u << battler)) || (gBattleStruct->commandingDondozo & (1u << battler))) { gChosenActionByBattler[battler] = B_ACTION_NOTHING_FAINTED; if (!(gBattleTypeFlags & BATTLE_TYPE_MULTI)) @@ -5116,6 +5126,9 @@ static void TurnValuesCleanUp(bool8 var0) if (gDisableStructs[i].substituteHP == 0) gBattleMons[i].status2 &= ~STATUS2_SUBSTITUTE; + if (!(gStatuses3[i] & STATUS3_COMMANDER)) + gBattleStruct->commandingDondozo &= ~(1u << i); + gSpecialStatuses[i].parentalBondState = PARENTAL_BOND_OFF; } @@ -5124,6 +5137,7 @@ static void TurnValuesCleanUp(bool8 var0) gSideTimers[B_SIDE_PLAYER].followmeTimer = 0; gSideTimers[B_SIDE_OPPONENT].followmeTimer = 0; + gBattleStruct->usedEjectItem = 0; gBattleStruct->pledgeMove = FALSE; // combined pledge move may not have been used due to a canceller } diff --git a/src/battle_message.c b/src/battle_message.c index 727091cec6..d90113b5ea 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -830,7 +830,8 @@ static const u8 sText_TargetIsBeingSaltCured[] = _("{B_DEF_NAME_WITH_PREFIX} is static const u8 sText_TargetIsHurtBySaltCure[] = _("{B_DEF_NAME_WITH_PREFIX} is hurt by {B_BUFF1}!"); static const u8 sText_TargetCoveredInStickyCandySyrup[] = _("{B_DEF_NAME_WITH_PREFIX} got covered\nin sticky syrup!"); static const u8 sText_PkmnTellChillingReceptionJoke[] = _("{B_ATK_NAME_WITH_PREFIX} is preparing to tell a\nchillingly bad joke!"); -static const u8 sText_ZeroToHeroTransformation[] = _("{B_ATK_NAME_WITH_PREFIX} underwent a heroic\ntransformation!"); +static const u8 sText_ZeroToHeroTransformation[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} underwent a heroic\ntransformation!"); +static const u8 sText_CommanderActivates[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} was swallowed by Dondozo\nand became Dondozo's commander!"); static const u8 sText_TheTwoMovesBecomeOne[] = _("The two moves become one!\nIt's a combined move!{PAUSE 16}"); static const u8 sText_ARainbowAppearedOnSide[] = _("A rainbow appeared in the sky\non {B_ATK_TEAM2} team's side!"); static const u8 sText_TheRainbowDisappeared[] = _("The rainbow on {B_ATK_TEAM2}\nside disappeared!"); @@ -869,6 +870,7 @@ const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = [STRINGID_ARAINBOWAPPEAREDONSIDE - BATTLESTRINGS_TABLE_START] = sText_ARainbowAppearedOnSide, [STRINGID_THETWOMOVESBECOMEONE - BATTLESTRINGS_TABLE_START] = sText_TheTwoMovesBecomeOne, [STRINGID_ZEROTOHEROTRANSFORMATION - BATTLESTRINGS_TABLE_START] = sText_ZeroToHeroTransformation, + [STRINGID_COMMANDERACTIVATES - BATTLESTRINGS_TABLE_START] = sText_CommanderActivates, [STRINGID_PKMNTELLCHILLINGRECEPTIONJOKE - BATTLESTRINGS_TABLE_START] = sText_PkmnTellChillingReceptionJoke, [STRINGID_MOVEBLOCKEDBYDYNAMAX - BATTLESTRINGS_TABLE_START] = sText_MoveBlockedByDynamax, [STRINGID_TARGETISHURTBYSALTCURE - BATTLESTRINGS_TABLE_START] = sText_TargetIsHurtBySaltCure, diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index ec5d2f61a0..897bf1dab3 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -337,8 +337,6 @@ static bool8 CanBurnHitThaw(u16 move); static u32 GetNextTarget(u32 moveTarget, bool32 excludeCurrent); static void TryUpdateEvolutionTracker(u32 evolutionMethod, u32 upAmount, u16 usedMove); static void AccuracyCheck(bool32 recalcDragonDarts, const u8 *nextInstr, const u8 *failInstr, u16 move); -static void SaveBattlerAttacker(u32 battler); -static void SaveBattlerTarget(u32 battler); static void Cmd_attackcanceler(void); static void Cmd_accuracycheck(void); @@ -1197,6 +1195,13 @@ static void Cmd_attackcanceler(void) u16 attackerAbility = GetBattlerAbility(gBattlerAttacker); u32 moveType = GetMoveType(gCurrentMove); + if (gBattleStruct->usedEjectItem & (1u << gBattlerAttacker)) + { + gBattleStruct->usedEjectItem = 0; + gCurrentActionFuncId = B_ACTION_TRY_FINISH; + return; + } + // Weight-based moves are blocked by Dynamax. if ((GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX) && IsMoveBlockedByDynamax(gCurrentMove)) { @@ -1516,14 +1521,17 @@ static bool32 AccuracyCalcHelper(u16 move) return TRUE; } // If the attacker has the ability No Guard and they aren't targeting a Pokemon involved in a Sky Drop with the move Sky Drop, move hits. - else if (GetBattlerAbility(gBattlerAttacker) == ABILITY_NO_GUARD && (gMovesInfo[move].effect != EFFECT_SKY_DROP || gBattleStruct->skyDropTargets[gBattlerTarget] == 0xFF)) + else if (GetBattlerAbility(gBattlerAttacker) == ABILITY_NO_GUARD + && !(gStatuses3[gBattlerTarget] & STATUS3_COMMANDER) + && (gMovesInfo[move].effect != EFFECT_SKY_DROP || gBattleStruct->skyDropTargets[gBattlerTarget] == 0xFF)) { if (!JumpIfMoveFailed(7, move)) RecordAbilityBattle(gBattlerAttacker, ABILITY_NO_GUARD); return TRUE; } // If the target has the ability No Guard and they aren't involved in a Sky Drop or the current move isn't Sky Drop, move hits. - else if (GetBattlerAbility(gBattlerTarget) == ABILITY_NO_GUARD && (gMovesInfo[move].effect != EFFECT_SKY_DROP || gBattleStruct->skyDropTargets[gBattlerTarget] == 0xFF)) + else if (GetBattlerAbility(gBattlerTarget) == ABILITY_NO_GUARD + && (gMovesInfo[move].effect != EFFECT_SKY_DROP || gBattleStruct->skyDropTargets[gBattlerTarget] == 0xFF)) { if (!JumpIfMoveFailed(7, move)) RecordAbilityBattle(gBattlerTarget, ABILITY_NO_GUARD); @@ -1531,8 +1539,8 @@ static bool32 AccuracyCalcHelper(u16 move) } // If the target is under the effects of Telekinesis, and the move isn't a OH-KO move, move hits. else if (gStatuses3[gBattlerTarget] & STATUS3_TELEKINESIS - && !(gStatuses3[gBattlerTarget] & STATUS3_SEMI_INVULNERABLE) - && gMovesInfo[move].effect != EFFECT_OHKO) + && !(gStatuses3[gBattlerTarget] & STATUS3_SEMI_INVULNERABLE) + && gMovesInfo[move].effect != EFFECT_OHKO) { JumpIfMoveFailed(7, move); return TRUE; @@ -1544,10 +1552,11 @@ static bool32 AccuracyCalcHelper(u16 move) return TRUE; } - if ((gStatuses3[gBattlerTarget] & STATUS3_PHANTOM_FORCE) - || ((gStatuses3[gBattlerTarget] & STATUS3_ON_AIR) && !(gMovesInfo[move].damagesAirborne || gMovesInfo[move].damagesAirborneDoubleDamage)) - || ((gStatuses3[gBattlerTarget] & STATUS3_UNDERGROUND) && !gMovesInfo[move].damagesUnderground) - || ((gStatuses3[gBattlerTarget] & STATUS3_UNDERWATER) && !gMovesInfo[move].damagesUnderwater)) + if ((gStatuses3[gBattlerTarget] & STATUS3_COMMANDER) + || (gStatuses3[gBattlerTarget] & STATUS3_PHANTOM_FORCE) + || ((gStatuses3[gBattlerTarget] & STATUS3_ON_AIR) && !(gMovesInfo[move].damagesAirborne || gMovesInfo[move].damagesAirborneDoubleDamage)) + || ((gStatuses3[gBattlerTarget] & STATUS3_UNDERGROUND) && !gMovesInfo[move].damagesUnderground) + || ((gStatuses3[gBattlerTarget] & STATUS3_UNDERWATER) && !gMovesInfo[move].damagesUnderwater)) { gMoveResultFlags |= MOVE_RESULT_MISSED; JumpIfMoveFailed(7, move); @@ -2943,9 +2952,10 @@ void SetMoveEffect(bool32 primary, bool32 certain) INCREMENT_RESET_RETURN if (!(gHitMarker & HITMARKER_STATUS_ABILITY_EFFECT) - && TestIfSheerForceAffected(gBattlerAttacker, gCurrentMove) - && !primary - && gBattleScripting.moveEffect != MOVE_EFFECT_CHARGING) + && TestIfSheerForceAffected(gBattlerAttacker, gCurrentMove) + && !(gMovesInfo[gCurrentMove].effect == EFFECT_ORDER_UP && gBattleStruct->commanderActive[gBattlerAttacker]) + && !primary + && gBattleScripting.moveEffect != MOVE_EFFECT_CHARGING) INCREMENT_RESET_RETURN if (!IsBattlerAlive(gEffectBattler) && !activateAfterFaint) @@ -3919,6 +3929,43 @@ void SetMoveEffect(bool32 primary, bool32 certain) gBattlescriptCurrInstr = BattleScript_LowerAtkSpAtk; } break; + case MOVE_EFFECT_ORDER_UP: + { + u32 stat = 0; + bool32 commanderAffected = TRUE; + switch (gBattleStruct->commanderActive[gEffectBattler]) + { + case SPECIES_TATSUGIRI_CURLY: + stat = STAT_ATK; + break; + case SPECIES_TATSUGIRI_DROOPY: + stat = STAT_DEF; + break; + case SPECIES_TATSUGIRI_STRETCHY: + stat = STAT_SPEED; + break; + default: + commanderAffected = FALSE; + break; + } + if (!commanderAffected + || NoAliveMonsForEitherParty() + || ChangeStatBuffs(SET_STAT_BUFF_VALUE(1), + stat, + affectsUser | STAT_CHANGE_UPDATE_MOVE_EFFECT, + 0) == STAT_CHANGE_DIDNT_WORK) + { + gBattlescriptCurrInstr++; + } + else + { + gBattleScripting.animArg1 = gBattleScripting.moveEffect & ~(MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_CERTAIN); + gBattleScripting.animArg2 = 0; + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_StatUp; + } + } + break; } } } @@ -5482,17 +5529,17 @@ static bool32 TryKnockOffBattleScript(u32 battlerDef) static u32 GetNextTarget(u32 moveTarget, bool32 excludeCurrent) { - u32 i; - for (i = 0; i < MAX_BATTLERS_COUNT; i++) + u32 battler; + for (battler = 0; battler < MAX_BATTLERS_COUNT; battler++) { - if (i != gBattlerAttacker - && !(excludeCurrent && i == gBattlerTarget) - && IsBattlerAlive(i) - && !(gBattleStruct->targetsDone[gBattlerAttacker] & (1u << i)) - && (GetBattlerSide(i) != GetBattlerSide(gBattlerAttacker) || moveTarget == MOVE_TARGET_FOES_AND_ALLY)) - break; + if (battler != gBattlerAttacker + && !(excludeCurrent && battler == gBattlerTarget) + && IsBattlerAlive(battler) + && !(gBattleStruct->targetsDone[gBattlerAttacker] & (1u << battler)) + && (GetBattlerSide(battler) != GetBattlerSide(gBattlerAttacker) || moveTarget == MOVE_TARGET_FOES_AND_ALLY)) + break; } - return i; + return battler; } static void Cmd_moveend(void) @@ -6245,6 +6292,7 @@ static void Cmd_moveend(void) gBattlescriptCurrInstr = BattleScript_MoveEnd; // Prevent user switch-in selection effect = TRUE; BattleScriptPushCursor(); + gBattleStruct->usedEjectItem |= 1u << battler; if (ejectButtonBattlers & (1u << battler)) { gBattlescriptCurrInstr = BattleScript_EjectButtonActivates; @@ -6314,8 +6362,15 @@ static void Cmd_moveend(void) if (gMovesInfo[gCurrentMove].effect == EFFECT_HIT_ESCAPE) gBattlescriptCurrInstr = BattleScript_MoveEnd; // Prevent user switch-in selection BattleScriptPushCursor(); - gBattlescriptCurrInstr = BattleScript_RedCardActivates; - gSpecialStatuses[gBattlerAttacker].preventLifeOrbDamage = TRUE; + if (gBattleStruct->commanderActive[gBattlerAttacker] != SPECIES_NONE) + { + gBattlescriptCurrInstr = BattleScript_RedCardActivationNoSwitch; + } + else + { + gBattlescriptCurrInstr = BattleScript_RedCardActivates; + gSpecialStatuses[gBattlerAttacker].preventLifeOrbDamage = TRUE; + } effect = TRUE; break; // Only fastest red card activates } @@ -6478,8 +6533,6 @@ static void Cmd_moveend(void) && (gBattleMons[gBattlerAttacker].status2 & STATUS2_LOCK_CONFUSE) != STATUS2_LOCK_CONFUSE_TURN(1)) // And won't end this turn CancelMultiTurnMoves(gBattlerAttacker); // Cancel it - - if (gBattleStruct->savedAttackerCount > 0) { // #if TESTING @@ -6525,6 +6578,18 @@ static void Cmd_moveend(void) if (B_CHARGE <= GEN_8 || moveType == TYPE_ELECTRIC) gStatuses3[gBattlerAttacker] &= ~(STATUS3_CHARGED_UP); memset(gQueuedStatBoosts, 0, sizeof(gQueuedStatBoosts)); + + for (i = 0; i < gBattlersCount; i++) + { + if (gBattleStruct->commanderActive[i] != SPECIES_NONE && !IsBattlerAlive(i)) + { + u32 partner = BATTLE_PARTNER(i); + gBattleStruct->commanderActive[i] = SPECIES_NONE; + if (IsBattlerAlive(partner)) + gStatuses3[partner] &= ~STATUS3_COMMANDER; + } + } + gBattleScripting.moveendState++; break; case MOVEEND_COUNT: @@ -7425,6 +7490,7 @@ static bool32 DoSwitchInEffectsForBattler(u32 battler) switch (GetBattlerAbility(i)) { case ABILITY_TRACE: + case ABILITY_COMMANDER: if (AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, i, 0, 0, 0)) return TRUE; break; @@ -12567,7 +12633,7 @@ static void Cmd_transformdataexecution(void) gBattlescriptCurrInstr = cmd->nextInstr; if (gBattleMons[gBattlerTarget].status2 & STATUS2_TRANSFORMED || gBattleStruct->illusion[gBattlerTarget].on - || gStatuses3[gBattlerTarget] & STATUS3_SEMI_INVULNERABLE) + || gStatuses3[gBattlerTarget] & STATUS3_SEMI_INVULNERABLE_NO_COMMANDER) { gMoveResultFlags |= MOVE_RESULT_FAILED; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TRANSFORM_FAILED; @@ -13357,7 +13423,8 @@ static void Cmd_trysetperishsong(void) { if (gStatuses3[i] & STATUS3_PERISH_SONG || GetBattlerAbility(i) == ABILITY_SOUNDPROOF - || BlocksPrankster(gCurrentMove, gBattlerAttacker, i, TRUE)) + || BlocksPrankster(gCurrentMove, gBattlerAttacker, i, TRUE) + || gStatuses3[i] & STATUS3_COMMANDER) { notAffectedCount++; } @@ -15767,7 +15834,7 @@ static void Cmd_callnative(void) // Callnative Funcs -static void SaveBattlerTarget(u32 battler) +void SaveBattlerTarget(u32 battler) { if (gBattleStruct->savedTargetCount < NELEMS(gBattleStruct->savedBattlerTarget)) gBattleStruct->savedBattlerTarget[gBattleStruct->savedTargetCount++] = battler; @@ -15775,7 +15842,7 @@ static void SaveBattlerTarget(u32 battler) DebugPrintfLevel(MGBA_LOG_WARN, "Attempting to exceed savedBattlerTarget array size!"); } -static void SaveBattlerAttacker(u32 battler) +void SaveBattlerAttacker(u32 battler) { if (gBattleStruct->savedAttackerCount < NELEMS(gBattleStruct->savedBattlerAttacker)) gBattleStruct->savedBattlerAttacker[gBattleStruct->savedAttackerCount++] = battler; @@ -17344,3 +17411,15 @@ void BS_TryRevivalBlessing(void) MarkBattlerForControllerExec(gBattlerAttacker); } } + +void BS_JumpIfCommanderActive(void) +{ + NATIVE_ARGS(const u8 *jumpInstr); + + if (gBattleStruct->commanderActive[gBattlerTarget] != SPECIES_NONE) + gBattlescriptCurrInstr = cmd->jumpInstr; + else if (gStatuses3[gBattlerTarget] & STATUS3_COMMANDER) + gBattlescriptCurrInstr = cmd->jumpInstr; + else + gBattlescriptCurrInstr = cmd->nextInstr; +} diff --git a/src/battle_util.c b/src/battle_util.c index e0e5fe2485..e32e21cf2a 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -130,7 +130,9 @@ void HandleAction_UseMove(void) u16 moveTarget; gBattlerAttacker = gBattlerByTurnOrder[gCurrentTurnActionNumber]; - if (gBattleStruct->absentBattlerFlags & (1u << gBattlerAttacker) || !IsBattlerAlive(gBattlerAttacker)) + if (gBattleStruct->absentBattlerFlags & (1u << gBattlerAttacker) + || gBattleStruct->commandingDondozo & (1u << gBattlerAttacker) + || !IsBattlerAlive(gBattlerAttacker)) { gCurrentActionFuncId = B_ACTION_FINISHED; return; @@ -4113,7 +4115,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 u32 moveType, move; u32 side; u32 i, j; - u32 partner; + u32 partner = 0; struct Pokemon *mon; if (gBattleTypeFlags & BATTLE_TYPE_SAFARI) @@ -4404,7 +4406,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 && !(gBattleMons[BATTLE_OPPOSITE(battler)].status2 & (STATUS2_TRANSFORMED | STATUS2_SUBSTITUTE)) && !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED) && !(gBattleStruct->illusion[BATTLE_OPPOSITE(battler)].on) - && !(gStatuses3[BATTLE_OPPOSITE(battler)] & STATUS3_SEMI_INVULNERABLE)) + && !(gStatuses3[BATTLE_OPPOSITE(battler)] & STATUS3_SEMI_INVULNERABLE_NO_COMMANDER)) { gBattlerAttacker = battler; gBattlerTarget = BATTLE_OPPOSITE(battler); @@ -4907,7 +4909,6 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 && !(gBattleStruct->transformZeroToHero[side] & (1u << gBattlerPartyIndexes[battler]))) { gSpecialStatuses[battler].switchInAbilityDone = TRUE; - gBattlerAttacker = battler; gBattleStruct->transformZeroToHero[side] |= 1u << gBattlerPartyIndexes[battler]; BattleScriptPushCursorAndCallback(BattleScript_ZeroToHeroActivates); effect++; @@ -4980,6 +4981,28 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 effect++; } break; + case ABILITY_COMMANDER: + partner = BATTLE_PARTNER(battler); + if (!gSpecialStatuses[battler].switchInAbilityDone + && gBattleStruct->commanderActive[partner] == SPECIES_NONE + && gBattleMons[partner].species == SPECIES_DONDOZO + && GET_BASE_SPECIES_ID(GetMonData(GetPartyBattlerData(battler), MON_DATA_SPECIES)) == SPECIES_TATSUGIRI) + { + SaveBattlerAttacker(gBattlerAttacker); + gSpecialStatuses[battler].switchInAbilityDone = TRUE; + gBattlerAttacker = partner; + gBattleStruct->commandingDondozo |= 1u << battler; + gBattleStruct->commanderActive[partner] = gBattleMons[battler].species; + gStatuses3[battler] |= STATUS3_COMMANDER; + if (gBattleMons[battler].status2 & STATUS2_CONFUSION + && !(gStatuses4[battler] & STATUS4_INFINITE_CONFUSION)) + gBattleMons[battler].status2 -= STATUS2_CONFUSION_TURN(1); + BtlController_EmitSpriteInvisibility(battler, BUFFER_A, TRUE); + MarkBattlerForControllerExec(battler); + BattleScriptPushCursorAndCallback(BattleScript_CommanderActivates); + effect++; + } + break; } break; case ABILITYEFFECT_ENDTURN: @@ -6519,7 +6542,9 @@ u32 IsAbilityPreventingEscape(u32 battler) bool32 CanBattlerEscape(u32 battler) // no ability check { - if (GetBattlerHoldEffect(battler, TRUE) == HOLD_EFFECT_SHED_SHELL) + if (gBattleStruct->commanderActive[battler] != SPECIES_NONE) + return FALSE; + else if (GetBattlerHoldEffect(battler, TRUE) == HOLD_EFFECT_SHED_SHELL) return TRUE; else if (B_GHOSTS_ESCAPE >= GEN_6 && IS_BATTLER_OF_TYPE(battler, TYPE_GHOST)) return TRUE; diff --git a/src/data/abilities.h b/src/data/abilities.h index 7c67f2da17..30a34ffe90 100644 --- a/src/data/abilities.h +++ b/src/data/abilities.h @@ -2141,6 +2141,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] = .cantBeSwapped = TRUE, .cantBeTraced = TRUE, .cantBeSuppressed = TRUE, + .cantBeOverwritten = TRUE, }, [ABILITY_ELECTROMORPHOSIS] = diff --git a/src/data/battle_move_effects.h b/src/data/battle_move_effects.h index ead7cee6f1..776563bbec 100644 --- a/src/data/battle_move_effects.h +++ b/src/data/battle_move_effects.h @@ -2255,4 +2255,10 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = .battleScript = BattleScript_EffectHit, .battleTvScore = 0, // TODO: Assign points }, + + [EFFECT_ORDER_UP] = + { + .battleScript = BattleScript_EffectHit, + .battleTvScore = 0, // TODO: Assign points + }, }; diff --git a/src/data/moves_info.h b/src/data/moves_info.h index 74e12c4e7a..0d754f479d 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -19325,7 +19325,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .description = COMPOUND_STRING( "Boosts a user's stats\n" "depending on Tatsugiri."), - .effect = EFFECT_PLACEHOLDER, // EFFECT_ORDER_UP + .effect = EFFECT_ORDER_UP, .power = 80, .type = TYPE_DRAGON, .accuracy = 100, @@ -19335,6 +19335,11 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .category = DAMAGE_CATEGORY_PHYSICAL, .mirrorMoveBanned = TRUE, .metronomeBanned = TRUE, + .additionalEffects = ADDITIONAL_EFFECTS({ + .moveEffect = MOVE_EFFECT_ORDER_UP, + .self = TRUE, + .chance = 100, + }), .battleAnimScript = gBattleAnimMove_OrderUp, }, diff --git a/test/battle/ability/commander.c b/test/battle/ability/commander.c new file mode 100644 index 0000000000..af02f971a3 --- /dev/null +++ b/test/battle/ability/commander.c @@ -0,0 +1,423 @@ +#include "global.h" +#include "test/battle.h" + +DOUBLE_BATTLE_TEST("Commander will activate once Dondozo switches in") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_TATSUGIRI) { Ability(ABILITY_COMMANDER); } + PLAYER(SPECIES_DONDOZO); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { SWITCH(playerLeft, 2); } + } SCENE { + ABILITY_POPUP(playerRight, ABILITY_COMMANDER); + MESSAGE("Tatsugiri was swallowed by Dondozo and became Dondozo's commander!"); + } +} + +DOUBLE_BATTLE_TEST("Commander increases all stats by 2 stages once it is triggered") +{ + GIVEN { + PLAYER(SPECIES_TATSUGIRI) { Ability(ABILITY_COMMANDER); } + PLAYER(SPECIES_DONDOZO); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { } + } SCENE { + ABILITY_POPUP(playerLeft, ABILITY_COMMANDER); + MESSAGE("Tatsugiri was swallowed by Dondozo and became Dondozo's commander!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); + MESSAGE("Dondozo's Attack sharply rose!"); + MESSAGE("Dondozo's Defense sharply rose!"); + MESSAGE("Dondozo's Sp. Atk sharply rose!"); + MESSAGE("Dondozo's Sp. Def sharply rose!"); + MESSAGE("Dondozo's Speed sharply rose!"); + } +} + +DOUBLE_BATTLE_TEST("Commander Tatsugiri avoids moves targetted towards it") +{ + GIVEN { + PLAYER(SPECIES_TATSUGIRI) { Ability(ABILITY_COMMANDER); } + PLAYER(SPECIES_DONDOZO); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponentLeft, MOVE_TACKLE, target: playerLeft); MOVE(opponentRight, MOVE_POUND, target: playerRight); } + } SCENE { + ABILITY_POPUP(playerLeft, ABILITY_COMMANDER); + MESSAGE("Tatsugiri was swallowed by Dondozo and became Dondozo's commander!"); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponentLeft); + MESSAGE("Foe Wobbuffet's attack missed!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_POUND, opponentRight); + } +} + +DOUBLE_BATTLE_TEST("Commander Tatsugiri will still take residual damage from a field effect while inside Dondozo") +{ + GIVEN { + PLAYER(SPECIES_TATSUGIRI) { Ability(ABILITY_COMMANDER); } + PLAYER(SPECIES_DONDOZO); + OPPONENT(SPECIES_TYRANITAR) { Ability(ABILITY_SAND_STREAM); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { } + } SCENE { + ABILITY_POPUP(playerLeft, ABILITY_COMMANDER); + MESSAGE("Tatsugiri was swallowed by Dondozo and became Dondozo's commander!"); + ABILITY_POPUP(opponentLeft, ABILITY_SAND_STREAM); + MESSAGE("Dondozo is buffeted by the sandstorm!"); + MESSAGE("Tatsugiri is buffeted by the sandstorm!"); + MESSAGE("Foe Wobbuffet is buffeted by the sandstorm!"); + } +} + +DOUBLE_BATTLE_TEST("Commander Tatsugiri will still take poison damage if while inside Dondozo") +{ + GIVEN { + PLAYER(SPECIES_TATSUGIRI) { Ability(ABILITY_COMMANDER); Status1(STATUS1_POISON); } + PLAYER(SPECIES_DONDOZO); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { } + } SCENE { + ABILITY_POPUP(playerLeft, ABILITY_COMMANDER); + MESSAGE("Tatsugiri was swallowed by Dondozo and became Dondozo's commander!"); + MESSAGE("Tatsugiri is hurt by poison!"); + } +} + +DOUBLE_BATTLE_TEST("Commander Tatsugiri still avoids moves even when the attacker has No Guard") +{ + GIVEN { + PLAYER(SPECIES_TATSUGIRI) { Ability(ABILITY_COMMANDER); } + PLAYER(SPECIES_DONDOZO); + OPPONENT(SPECIES_MACHAMP) { Ability(ABILITY_NO_GUARD); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponentLeft, MOVE_TACKLE, target: playerLeft); } + } SCENE { + ABILITY_POPUP(playerLeft, ABILITY_COMMANDER); + MESSAGE("Tatsugiri was swallowed by Dondozo and became Dondozo's commander!"); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponentLeft); + MESSAGE("Foe Machamp's attack missed!"); + } +} + +DOUBLE_BATTLE_TEST("Commander cannot affect a Dondozo that was previously affected by Commander until it faints and revived") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_DONDOZO); + PLAYER(SPECIES_TATSUGIRI) { HP(1); Ability(ABILITY_COMMANDER); Status1(STATUS1_POISON); } + PLAYER(SPECIES_TATSUGIRI) { Ability(ABILITY_COMMANDER); } + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(playerRight, MOVE_CELEBRATE); SWITCH(playerLeft, 2); SEND_OUT(playerLeft, 3); } + } SCENE { + ABILITY_POPUP(playerLeft, ABILITY_COMMANDER); + MESSAGE("Tatsugiri was swallowed by Dondozo and became Dondozo's commander!"); + MESSAGE("Tatsugiri is hurt by poison!"); + NONE_OF { + ABILITY_POPUP(playerLeft, ABILITY_COMMANDER); + MESSAGE("Tatsugiri was swallowed by Dondozo and became Dondozo's commander!"); + } + } +} + +DOUBLE_BATTLE_TEST("Commander prevents Whirlwind from working against Dondozo or Tatsugiri while it's active") +{ + GIVEN { + PLAYER(SPECIES_TATSUGIRI) { Ability(ABILITY_COMMANDER); } + PLAYER(SPECIES_DONDOZO); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponentLeft, MOVE_WHIRLWIND, target: playerLeft); } + TURN { MOVE(opponentRight, MOVE_WHIRLWIND, target: playerRight); } + } SCENE { + ABILITY_POPUP(playerLeft, ABILITY_COMMANDER); + MESSAGE("Tatsugiri was swallowed by Dondozo and became Dondozo's commander!"); + MESSAGE("Foe Wobbuffet used Whirlwind!"); + MESSAGE("But it failed!"); + MESSAGE("Foe Wobbuffet used Whirlwind!"); + MESSAGE("But it failed!"); + } +} + +DOUBLE_BATTLE_TEST("Commander prevents Red Card from working while Commander is active") +{ + GIVEN { + PLAYER(SPECIES_TATSUGIRI) { Ability(ABILITY_COMMANDER); } + PLAYER(SPECIES_DONDOZO); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_RED_CARD); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(playerRight, MOVE_TACKLE, target: opponentLeft); } + } SCENE { + ABILITY_POPUP(playerLeft, ABILITY_COMMANDER); + MESSAGE("Tatsugiri was swallowed by Dondozo and became Dondozo's commander!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponentLeft); + } THEN { + EXPECT(opponentLeft->item == ITEM_NONE); + EXPECT(playerRight->species == SPECIES_DONDOZO); + } + +} + +DOUBLE_BATTLE_TEST("Commander Tatsugiri is not damaged by a double target move if Dondozo faints") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_SURF].target == MOVE_TARGET_FOES_AND_ALLY); + PLAYER(SPECIES_DONDOZO) { HP(1); }; + PLAYER(SPECIES_TATSUGIRI) { Ability(ABILITY_COMMANDER); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(opponentLeft, MOVE_SURF); SEND_OUT(playerLeft, 2); } + } SCENE { + ABILITY_POPUP(playerRight, ABILITY_COMMANDER); + MESSAGE("Tatsugiri was swallowed by Dondozo and became Dondozo's commander!"); + HP_BAR(playerLeft); + MESSAGE("Dondozo fainted!"); + NOT HP_BAR(playerRight); + HP_BAR(opponentRight); + } +} + +DOUBLE_BATTLE_TEST("Commander Tatsugiri takes no damage from multi-target damaging moves") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_TATSUGIRI) { Ability(ABILITY_COMMANDER); } + PLAYER(SPECIES_DONDOZO); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(opponentLeft, MOVE_SURF); MOVE(opponentRight, MOVE_SURF); SWITCH(playerLeft, 2); } + } SCENE { + ABILITY_POPUP(playerRight, ABILITY_COMMANDER); + MESSAGE("Tatsugiri was swallowed by Dondozo and became Dondozo's commander!"); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_SURF, opponentLeft); + HP_BAR(playerLeft); + NOT HP_BAR(playerRight); + HP_BAR(opponentRight); + + ANIMATION(ANIM_TYPE_MOVE, MOVE_SURF, opponentRight); + HP_BAR(playerLeft); + HP_BAR(opponentLeft); + NOT HP_BAR(playerRight); + } +} + +DOUBLE_BATTLE_TEST("Commander doesn't prevent Transform from working on a Commander Tatsugiri") +{ + GIVEN { + PLAYER(SPECIES_DONDOZO); + PLAYER(SPECIES_TATSUGIRI) { Ability(ABILITY_COMMANDER); } + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponentRight, MOVE_TRANSFORM, target: playerRight); } + } SCENE { + ABILITY_POPUP(playerRight, ABILITY_COMMANDER); + MESSAGE("Tatsugiri was swallowed by Dondozo and became Dondozo's commander!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TRANSFORM, opponentRight); + } +} + +DOUBLE_BATTLE_TEST("Commander doesn't prevent Imposter from working on a Commander Tatsugiri") +{ + GIVEN { + PLAYER(SPECIES_DONDOZO); + PLAYER(SPECIES_TATSUGIRI) { Ability(ABILITY_COMMANDER); } + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_DITTO) { Ability(ABILITY_IMPOSTER); } + } WHEN { + TURN { } + TURN { SWITCH(opponentRight, 2); } + } SCENE { + ABILITY_POPUP(playerRight, ABILITY_COMMANDER); + MESSAGE("Tatsugiri was swallowed by Dondozo and became Dondozo's commander!"); + ABILITY_POPUP(opponentRight, ABILITY_IMPOSTER); + MESSAGE("Foe Ditto transformed into Tatsugiri using Imposter!"); + } +} + +DOUBLE_BATTLE_TEST("Commander Tatsugiri is still affected by Haze while controlling Dondozo") +{ + GIVEN { + PLAYER(SPECIES_DONDOZO); + PLAYER(SPECIES_TATSUGIRI) { Ability(ABILITY_COMMANDER); } + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(opponentLeft, MOVE_PERISH_SONG); } + TURN {} + TURN {} + TURN {} + } SCENE { + ABILITY_POPUP(playerRight, ABILITY_COMMANDER); + MESSAGE("Tatsugiri was swallowed by Dondozo and became Dondozo's commander!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PERISH_SONG, opponentLeft); + MESSAGE("All affected POKéMON will faint in three turns!"); + MESSAGE("Dondozo's PERISH count fell to 0!"); + MESSAGE("Dondozo fainted!"); + MESSAGE("Foe Wobbuffet's PERISH count fell to 0!"); + MESSAGE("Foe Wobbuffet fainted!"); + NONE_OF { + MESSAGE("Tatsugiri's PERISH count fell to 0!"); + MESSAGE("Tatsugiri fainted!"); + } + MESSAGE("Foe Wynaut's PERISH count fell to 0!"); + MESSAGE("Foe Wynaut fainted!"); + } +} + +DOUBLE_BATTLE_TEST("Commander Tatsugiri is still affected by Haze while controlling Dondozo") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_TATSUGIRI) { Ability(ABILITY_COMMANDER); } + PLAYER(SPECIES_DONDOZO); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(playerRight, MOVE_SWORDS_DANCE); } + TURN { SWITCH(playerLeft, 2); MOVE(opponentRight, MOVE_HAZE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SWORDS_DANCE, playerRight); + ABILITY_POPUP(playerRight, ABILITY_COMMANDER); + MESSAGE("Tatsugiri was swallowed by Dondozo and became Dondozo's commander!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_HAZE, opponentRight); + } THEN { + EXPECT_EQ(playerRight->statStages[STAT_ATK], DEFAULT_STAT_STAGE); + } +} + +DOUBLE_BATTLE_TEST("Commander Attacker is kept (Dondozo Left Slot)") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_SURF].target == MOVE_TARGET_FOES_AND_ALLY); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_TATSUGIRI) { Ability(ABILITY_COMMANDER); } + PLAYER(SPECIES_DONDOZO); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponentRight, MOVE_TACKLE, target: opponentLeft); } + TURN { SWITCH(playerLeft, 2); MOVE(opponentLeft, MOVE_SURF); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponentRight); + ABILITY_POPUP(playerRight, ABILITY_COMMANDER); + MESSAGE("Tatsugiri was swallowed by Dondozo and became Dondozo's commander!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SURF, opponentLeft); + HP_BAR(playerLeft); + MESSAGE("Foe Wobbuffet's attack missed!"); + HP_BAR(opponentRight); + } +} + +DOUBLE_BATTLE_TEST("Commander Attacker is kept (Dondozo Right Slot)") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_SURF].target == MOVE_TARGET_FOES_AND_ALLY); + PLAYER(SPECIES_TATSUGIRI) { Ability(ABILITY_COMMANDER); } + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_DONDOZO); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponentRight, MOVE_TACKLE, target: opponentLeft); } + TURN { SWITCH(playerRight, 2); MOVE(opponentLeft, MOVE_SURF); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponentRight); + ABILITY_POPUP(playerLeft, ABILITY_COMMANDER); + MESSAGE("Tatsugiri was swallowed by Dondozo and became Dondozo's commander!"); + MESSAGE("Foe Wobbuffet's attack missed!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SURF, opponentLeft); + HP_BAR(playerRight); + HP_BAR(opponentRight); + } +} + +DOUBLE_BATTLE_TEST("Commander Tatsugiri does not attack if Dondozo faints the same turn it's switched in") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_TATSUGIRI) { Ability(ABILITY_COMMANDER); } + PLAYER(SPECIES_DONDOZO) { HP(1); } + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { + SWITCH(playerLeft, 2); + MOVE(opponentLeft, MOVE_TACKLE, target: playerLeft); + MOVE(opponentRight, MOVE_TACKLE, target: playerRight); + MOVE(playerRight, MOVE_CELEBRATE); + SEND_OUT(playerLeft, 0); + } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponentLeft); + HP_BAR(playerLeft); + MESSAGE("Dondozo fainted!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponentRight); + HP_BAR(playerRight); + NOT MESSAGE("Tatsugiri used Celebrate!"); + } +} + +DOUBLE_BATTLE_TEST("Commander Tatsugiri does not get hit by Dragon Darts when a commanded Dondozo faints") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_DRAGON_DARTS].effect == EFFECT_DRAGON_DARTS); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_DONDOZO) { HP(1); } + PLAYER(SPECIES_TATSUGIRI) { Ability(ABILITY_COMMANDER); } + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { SWITCH(playerLeft, 2); MOVE(opponentRight, MOVE_DRAGON_DARTS, target: playerRight); SEND_OUT(playerRight, 0); } + } SCENE { + MESSAGE("Tatsugiri was swallowed by Dondozo and became Dondozo's commander!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DARTS, opponentRight); + MESSAGE("Dondozo fainted!"); + NOT HP_BAR(playerLeft); + } +} + +DOUBLE_BATTLE_TEST("Commander Tatsugiri does not get hit by Dragon Darts when commanding Dondozo") +{ + bool32 targetPlayerRight; + PARAMETRIZE { targetPlayerRight = TRUE; } + PARAMETRIZE { targetPlayerRight = FALSE; } + GIVEN { + ASSUME(gMovesInfo[MOVE_DRAGON_DARTS].effect == EFFECT_DRAGON_DARTS); + PLAYER(SPECIES_TATSUGIRI) { Ability(ABILITY_COMMANDER); } + PLAYER(SPECIES_DONDOZO); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + if (targetPlayerRight == TRUE) + TURN { MOVE(opponentRight, MOVE_DRAGON_DARTS, target: playerRight); } + else + TURN { MOVE(opponentRight, MOVE_DRAGON_DARTS, target: playerLeft); } + } SCENE { + MESSAGE("Tatsugiri was swallowed by Dondozo and became Dondozo's commander!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DARTS, opponentRight); + HP_BAR(playerRight); + NOT HP_BAR(playerLeft); + HP_BAR(playerRight); + NOT HP_BAR(playerLeft); + } +} diff --git a/test/battle/hold_effect/eject_button.c b/test/battle/hold_effect/eject_button.c index da358c2aa9..f53a270d82 100644 --- a/test/battle/hold_effect/eject_button.c +++ b/test/battle/hold_effect/eject_button.c @@ -208,3 +208,27 @@ SINGLE_BATTLE_TEST("Eject Button is not triggered after High Jump Kick crash dam } } } + +DOUBLE_BATTLE_TEST("Eject Button activation will not trigger an attack from the incoming mon") +{ + GIVEN { + PLAYER(SPECIES_TATSUGIRI) { Speed(10); Ability(ABILITY_COMMANDER); } + PLAYER(SPECIES_WOBBUFFET) { Speed(100); Item(ITEM_EJECT_BUTTON); } + PLAYER(SPECIES_DONDOZO) { Speed(20); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(50); Item(ITEM_EJECT_PACK); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(10); } + OPPONENT(SPECIES_WYNAUT) { Speed(1); } + } WHEN { + TURN { MOVE(opponentRight, MOVE_MAKE_IT_RAIN); SEND_OUT(playerRight, 2); SEND_OUT(opponentRight, 2); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_MAKE_IT_RAIN, opponentRight); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerRight); + MESSAGE("Wobbuffet is switched out with the Eject Button!"); + ABILITY_POPUP(playerLeft, ABILITY_COMMANDER); + MESSAGE("Tatsugiri was swallowed by Dondozo and became Dondozo's commander!"); + NONE_OF { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponentLeft); + MESSAGE("Wobbuffet is switched out with the Eject Pack!"); + } + } +} diff --git a/test/battle/move_effect_secondary/order_up.c b/test/battle/move_effect_secondary/order_up.c new file mode 100644 index 0000000000..ea1980062c --- /dev/null +++ b/test/battle/move_effect_secondary/order_up.c @@ -0,0 +1,172 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_ORDER_UP].additionalEffects[0].moveEffect == MOVE_EFFECT_ORDER_UP); +} + +DOUBLE_BATTLE_TEST("Order Up increases a stat based on Tatsugiri's form") +{ + u32 species = 0; + PARAMETRIZE { species = SPECIES_TATSUGIRI_CURLY; } + PARAMETRIZE { species = SPECIES_TATSUGIRI_DROOPY; } + PARAMETRIZE { species = SPECIES_TATSUGIRI_STRETCHY; } + + GIVEN { + PLAYER(species) { Ability(ABILITY_COMMANDER); } + PLAYER(SPECIES_DONDOZO); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_VOLBEAT) { Ability(ABILITY_PRANKSTER); }; + } WHEN { + TURN { MOVE(opponentRight, MOVE_HAZE); MOVE(playerRight, MOVE_ORDER_UP, target: opponentLeft); } + } SCENE { + ABILITY_POPUP(playerLeft, ABILITY_COMMANDER); + MESSAGE("Tatsugiri was swallowed by Dondozo and became Dondozo's commander!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_HAZE, opponentRight); // Remove previous stat boosts + ANIMATION(ANIM_TYPE_MOVE, MOVE_ORDER_UP, playerRight); + switch (species) + { + case SPECIES_TATSUGIRI_CURLY: + MESSAGE("Dondozo's Attack rose!"); + break; + case SPECIES_TATSUGIRI_DROOPY: + MESSAGE("Dondozo's Defense rose!"); + break; + case SPECIES_TATSUGIRI_STRETCHY: + MESSAGE("Dondozo's Speed rose!"); + break; + } + } THEN { + switch (species) + { + case SPECIES_TATSUGIRI_CURLY: + EXPECT_EQ(playerRight->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1); + break; + case SPECIES_TATSUGIRI_DROOPY: + EXPECT_EQ(playerRight->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 1); + break; + case SPECIES_TATSUGIRI_STRETCHY: + EXPECT_EQ(playerRight->statStages[STAT_SPEED], DEFAULT_STAT_STAGE + 1); + break; + } + } +} + +DOUBLE_BATTLE_TEST("Order Up increases a stat based on Tatsugiri's form even if Tatsugiri fainted inside Dondozo") +{ + u32 species = 0; + PARAMETRIZE { species = SPECIES_TATSUGIRI_CURLY; } + PARAMETRIZE { species = SPECIES_TATSUGIRI_DROOPY; } + PARAMETRIZE { species = SPECIES_TATSUGIRI_STRETCHY; } + + GIVEN { + PLAYER(species) { HP(1); Status1(STATUS1_POISON); Ability(ABILITY_COMMANDER); } + PLAYER(SPECIES_DONDOZO); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_VOLBEAT) { Ability(ABILITY_PRANKSTER); }; + } WHEN { + TURN { } + TURN { MOVE(opponentRight, MOVE_HAZE); MOVE(playerRight, MOVE_ORDER_UP, target: opponentLeft); } + } SCENE { + ABILITY_POPUP(playerLeft, ABILITY_COMMANDER); + MESSAGE("Tatsugiri was swallowed by Dondozo and became Dondozo's commander!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); + MESSAGE("Tatsugiri is hurt by poison!"); + MESSAGE("Tatsugiri fainted!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_HAZE, opponentRight); // Remove previous stat boosts + ANIMATION(ANIM_TYPE_MOVE, MOVE_ORDER_UP, playerRight); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); + switch (species) + { + case SPECIES_TATSUGIRI_CURLY: + MESSAGE("Dondozo's Attack rose!"); + break; + case SPECIES_TATSUGIRI_DROOPY: + MESSAGE("Dondozo's Defense rose!"); + break; + case SPECIES_TATSUGIRI_STRETCHY: + MESSAGE("Dondozo's Speed rose!"); + break; + } + } THEN { + switch (species) + { + case SPECIES_TATSUGIRI_CURLY: + EXPECT_EQ(playerRight->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1); + break; + case SPECIES_TATSUGIRI_DROOPY: + EXPECT_EQ(playerRight->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 1); + break; + case SPECIES_TATSUGIRI_STRETCHY: + EXPECT_EQ(playerRight->statStages[STAT_SPEED], DEFAULT_STAT_STAGE + 1); + break; + } + } +} + +DOUBLE_BATTLE_TEST("Order up does not boosts any stats if Dondozo is not affected by Commander") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_DONDOZO); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(playerRight, MOVE_ORDER_UP, target: opponentLeft); } + } SCENE { + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); + } +} + +DOUBLE_BATTLE_TEST("Order Up is boosted by Sheer Force without removing the stat boosting effect") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_ENTRAINMENT].effect == EFFECT_ENTRAINMENT); + PLAYER(SPECIES_DONDOZO) { Speed(10); } + PLAYER(SPECIES_TATSUGIRI_CURLY) { Speed(9); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(8); } + OPPONENT(SPECIES_TAUROS) { Speed(21); Ability(ABILITY_SHEER_FORCE); } + } WHEN { + TURN { MOVE(opponentRight, MOVE_ENTRAINMENT, target: playerLeft); MOVE(playerLeft, MOVE_ORDER_UP, target: opponentLeft); } + } SCENE { + MESSAGE("Foe Tauros used Entrainment!"); + MESSAGE("Dondozo acquired Sheer Force!"); + MESSAGE("Dondozo used Order Up!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); + } +} +DOUBLE_BATTLE_TEST("Order Up is always boosted by Sheer Force", s16 damage) +{ + u32 move; + u32 ability; + PARAMETRIZE(move = MOVE_CELEBRATE, ability = ABILITY_STORM_DRAIN); + PARAMETRIZE(move = MOVE_ENTRAINMENT, ability = ABILITY_STORM_DRAIN); + PARAMETRIZE(move = MOVE_ENTRAINMENT, ability = ABILITY_COMMANDER); + + GIVEN { + ASSUME(gMovesInfo[MOVE_HAZE].effect == EFFECT_HAZE); + ASSUME(gMovesInfo[MOVE_ENTRAINMENT].effect == EFFECT_ENTRAINMENT); + PLAYER(SPECIES_DONDOZO) { Speed(10); } + PLAYER(SPECIES_TATSUGIRI_CURLY) { Speed(9); Ability(ability); } + OPPONENT(SPECIES_TAUROS) { Speed(21); Ability(ABILITY_SHEER_FORCE); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(22); } + } WHEN { + TURN { MOVE(opponentRight, MOVE_HAZE); + MOVE(opponentLeft, move, target: playerLeft); + MOVE(playerLeft, MOVE_ORDER_UP, target: opponentRight); } + } SCENE { + MESSAGE("Foe Wobbuffet used Haze!"); + if (move == MOVE_ENTRAINMENT) + { + MESSAGE("Foe Tauros used Entrainment!"); + MESSAGE("Dondozo acquired Sheer Force!"); + } + MESSAGE("Dondozo used Order Up!"); + HP_BAR(opponentRight, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, UQ_4_12(1.3), results[1].damage); + EXPECT_MUL_EQ(results[0].damage, UQ_4_12(1.3), results[2].damage); + } +}