diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 4ce3df4d1..63ef3861b 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1597,6 +1597,11 @@ various \battler, VARIOUS_CHECK_IF_GRASSY_TERRAIN_HEALS .4byte \ptr .endm + + .macro jumpifroarfails ptr:req + various BS_ATTACKER, VARIOUS_JUMP_IF_ROAR_FAILS + .4byte \ptr + .endm @ helpful macros .macro setstatchanger stat:req, stages:req, down:req diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index e168bb36d..ab594e81d 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -2123,6 +2123,7 @@ BattleScript_EffectRoar:: attackcanceler attackstring ppreduce + jumpifroarfails BattleScript_ButItFailed jumpifability BS_TARGET, ABILITY_SUCTION_CUPS, BattleScript_AbilityPreventsPhasingOut jumpifstatus3 BS_TARGET, STATUS3_ROOTED, BattleScript_PrintMonIsRooted accuracycheck BattleScript_ButItFailed, NO_ACC_CALC_CHECK_LOCK_ON @@ -4967,18 +4968,9 @@ BattleScript_BideNoEnergyToAttack:: printstring STRINGID_PKMNUNLEASHEDENERGY waitmessage 0x40 goto BattleScript_ButItFailed - -BattleScript_SuccessForceOut:: - attackanimation - waitanimation - switchoutabilities BS_TARGET - returntoball BS_TARGET - waitstate - jumpifbattletype BATTLE_TYPE_TRAINER, BattleScript_TrainerBattleForceOut - setoutcomeonteleport BS_ATTACKER - finishaction - -BattleScript_TrainerBattleForceOut:: + +BattleScript_RoarSuccessSwitch:: + call BattleScript_RoarSuccessRet getswitchedmondata BS_TARGET switchindataupdate BS_TARGET switchinanim BS_TARGET, FALSE @@ -4986,6 +4978,19 @@ BattleScript_TrainerBattleForceOut:: printstring STRINGID_PKMNWASDRAGGEDOUT switchineffects BS_TARGET goto BattleScript_MoveEnd + +BattleScript_RoarSuccessEndBattle:: + call BattleScript_RoarSuccessRet + setoutcomeonteleport BS_ATTACKER + finishaction + +BattleScript_RoarSuccessRet: + attackanimation + waitanimation + switchoutabilities BS_TARGET + returntoball BS_TARGET + waitstate + return BattleScript_MistProtected:: pause 0x20 diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 587d866ab..098eda042 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -53,7 +53,8 @@ extern const u8 BattleScript_LeechSeedTurnDrain[]; extern const u8 BattleScript_BideStoringEnergy[]; extern const u8 BattleScript_BideAttack[]; extern const u8 BattleScript_BideNoEnergyToAttack[]; -extern const u8 BattleScript_SuccessForceOut[]; +extern const u8 BattleScript_RoarSuccessSwitch[]; +extern const u8 BattleScript_RoarSuccessEndBattle[]; extern const u8 BattleScript_MistProtected[]; extern const u8 BattleScript_RageIsBuilding[]; extern const u8 BattleScript_MoveUsedIsDisabled[]; diff --git a/include/battle_util.h b/include/battle_util.h index d1f0b7259..d056fb018 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -38,6 +38,8 @@ #define WEATHER_HAS_EFFECT ((!ABILITY_ON_FIELD(ABILITY_CLOUD_NINE) && !ABILITY_ON_FIELD(ABILITY_AIR_LOCK))) +#define IS_WHOLE_SIDE_ALIVE(battler)((IsBattlerAlive(battler) && IsBattlerAlive(BATTLE_PARTNER(battler)))) + u8 GetBattlerForBattleScript(u8 caseId); void PressurePPLose(u8 target, u8 attacker, u16 move); void PressurePPLoseOnUsingPerishSong(u8 attacker); diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h index 23fbf75b3..b79184d23 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -138,6 +138,7 @@ #define VARIOUS_SPECTRAL_THIEF 75 #define VARIOUS_GRAVITY_ON_AIRBORNE_MONS 76 #define VARIOUS_CHECK_IF_GRASSY_TERRAIN_HEALS 77 +#define VARIOUS_JUMP_IF_ROAR_FAILS 78 // atk80, dmg manipulation #define ATK80_DMG_CHANGE_SIGN 0 diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 4838fc8e2..29fb9cddc 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -6368,6 +6368,21 @@ static void atk76_various(void) switch (gBattlescriptCurrInstr[2]) { + // Roar will fail in a double wild battle when used by the player against one of the two alive wild mons. + // Also when an opposing wild mon uses it againt its partner. + case VARIOUS_JUMP_IF_ROAR_FAILS: + if (WILD_DOUBLE_BATTLE + && GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER + && GetBattlerSide(gBattlerTarget) == B_SIDE_OPPONENT + && IS_WHOLE_SIDE_ALIVE(gBattlerTarget)) + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 3); + else if (WILD_DOUBLE_BATTLE + && GetBattlerSide(gBattlerAttacker) == B_SIDE_OPPONENT + && GetBattlerSide(gBattlerTarget) == B_SIDE_OPPONENT) + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 3); + else + gBattlescriptCurrInstr += 7; + return; case VARIOUS_CHECK_IF_GRASSY_TERRAIN_HEALS: if ((gStatuses3[gActiveBattler] & (STATUS3_SEMI_INVULNERABLE)) || BATTLER_MAX_HP(gActiveBattler) @@ -8027,27 +8042,6 @@ static void atk8E_initmultihitstring(void) gBattlescriptCurrInstr++; } -static bool8 TryDoForceSwitchOut(void) -{ - if (gBattleMons[gBattlerAttacker].level >= gBattleMons[gBattlerTarget].level) - { - *(gBattleStruct->field_58 + gBattlerTarget) = gBattlerPartyIndexes[gBattlerTarget]; - } - else - { - u16 random = Random() & 0xFF; - if ((u32)((random * (gBattleMons[gBattlerAttacker].level + gBattleMons[gBattlerTarget].level) >> 8) + 1) <= (gBattleMons[gBattlerTarget].level / 4)) - { - gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); - return FALSE; - } - *(gBattleStruct->field_58 + gBattlerTarget) = gBattlerPartyIndexes[gBattlerTarget]; - } - - gBattlescriptCurrInstr = BattleScript_SuccessForceOut; - return TRUE; -} - static void atk8F_forcerandomswitch(void) { s32 i; @@ -8060,14 +8054,26 @@ static void atk8F_forcerandomswitch(void) s32 validMons = 0; s32 minNeeded = 0; - if ((gBattleTypeFlags & BATTLE_TYPE_TRAINER)) + // Swapping pokemon happens in: + // trainer battles + // wild double battles when an opposing pokemon uses it against one of the two alive player mons + // wild double battle when a player pokemon uses it against its partner + if ((gBattleTypeFlags & BATTLE_TYPE_TRAINER) + || (WILD_DOUBLE_BATTLE + && GetBattlerSide(gBattlerAttacker) == B_SIDE_OPPONENT + && GetBattlerSide(gBattlerTarget) == B_SIDE_PLAYER + && IS_WHOLE_SIDE_ALIVE(gBattlerTarget)) + || (WILD_DOUBLE_BATTLE + && GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER + && GetBattlerSide(gBattlerTarget) == B_SIDE_PLAYER) + ) { if (GetBattlerSide(gBattlerTarget) == B_SIDE_PLAYER) party = gPlayerParty; else party = gEnemyParty; - if (BATTLE_TWO_VS_ONE_OPPONENT && (gBattlerTarget & BIT_SIDE) == B_SIDE_OPPONENT) + if (BATTLE_TWO_VS_ONE_OPPONENT && GetBattlerSide(gBattlerTarget) == B_SIDE_OPPONENT) { firstMonId = 0; lastMonId = 6; @@ -8175,19 +8181,20 @@ static void atk8F_forcerandomswitch(void) } else { - if (TryDoForceSwitchOut()) + *(gBattleStruct->field_58 + gBattlerTarget) = gBattlerPartyIndexes[gBattlerTarget]; + gBattlescriptCurrInstr = BattleScript_RoarSuccessSwitch; + + do { - do - { - i = Random() % monsCount; - i += firstMonId; - } - while (i == battler2PartyId - || i == battler1PartyId - || GetMonData(&party[i], MON_DATA_SPECIES) == SPECIES_NONE - || GetMonData(&party[i], MON_DATA_IS_EGG) == TRUE - || GetMonData(&party[i], MON_DATA_HP) == 0); + i = Random() % monsCount; + i += firstMonId; } + while (i == battler2PartyId + || i == battler1PartyId + || GetMonData(&party[i], MON_DATA_SPECIES) == SPECIES_NONE + || GetMonData(&party[i], MON_DATA_IS_EGG) == TRUE + || GetMonData(&party[i], MON_DATA_HP) == 0); + *(gBattleStruct->monToSwitchIntoId + gBattlerTarget) = i; if (!IsMultiBattle()) @@ -8208,7 +8215,11 @@ static void atk8F_forcerandomswitch(void) } else { - TryDoForceSwitchOut(); + // In normal wild doubles, Roar will always fail if the user's level is less than the target's. + if (gBattleMons[gBattlerAttacker].level >= gBattleMons[gBattlerTarget].level) + gBattlescriptCurrInstr = BattleScript_RoarSuccessEndBattle; + else + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); } }