diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index c63c2de542..7664eac693 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1104,7 +1104,7 @@ .byte 0xcc .endm - .macro cureifburnedparalysedorpoisoned failInstr:req + .macro curestatuswithmove failInstr:req .byte 0xcd .4byte \failInstr .endm diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 19328b7189..502e26d5c7 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -443,7 +443,7 @@ BattleScript_EffectTakeHeart:: attackcanceler attackstring ppreduce - cureifburnedparalysedorpoisoned BattleScript_CalmMindTryToRaiseStats + curestatuswithmove BattleScript_CalmMindTryToRaiseStats attackanimation waitanimation updatestatusicon BS_ATTACKER @@ -5230,7 +5230,7 @@ BattleScript_EffectRefresh:: attackcanceler attackstring ppreduce - cureifburnedparalysedorpoisoned BattleScript_ButItFailed + curestatuswithmove BattleScript_ButItFailed attackanimation waitanimation printstring STRINGID_PKMNSTATUSNORMAL @@ -9680,6 +9680,13 @@ BattleScript_EjectPackActivates:: jumpifcantswitch BS_SCRIPTING, BattleScript_EjectButtonEnd goto BattleScript_EjectPackActivate_Ret +BattleScript_EjectPackMissesTiming:: + playanimation BS_SCRIPTING, B_ANIM_HELD_ITEM_EFFECT + printstring STRINGID_EJECTBUTTONACTIVATE + waitmessage B_WAIT_TIME_LONG + removeitem BS_SCRIPTING + return + BattleScript_DarkTypePreventsPrankster:: attackstring ppreduce diff --git a/include/battle.h b/include/battle.h index f7b41b3dce..c3c38162dc 100644 --- a/include/battle.h +++ b/include/battle.h @@ -828,6 +828,7 @@ struct BattleStruct 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 redCardActivates:1; u8 usedEjectItem; u8 usedMicleBerry; }; diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 879c2f12c3..7de4097cf2 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -419,6 +419,7 @@ extern const u8 BattleScript_EjectButtonActivates[]; extern const u8 BattleScript_EjectPackActivate_Ret[]; extern const u8 BattleScript_EjectPackActivate_End2[]; extern const u8 BattleScript_EjectPackActivates[]; +extern const u8 BattleScript_EjectPackMissesTiming[]; extern const u8 BattleScript_MentalHerbCureRet[]; extern const u8 BattleScript_MentalHerbCureEnd2[]; extern const u8 BattleScript_TerrainPreventsEnd2[]; diff --git a/include/constants/battle.h b/include/constants/battle.h index 560448159a..d337559f1b 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -121,6 +121,8 @@ #define STATUS1_PSN_ANY (STATUS1_POISON | STATUS1_TOXIC_POISON) #define STATUS1_ANY (STATUS1_SLEEP | STATUS1_POISON | STATUS1_BURN | STATUS1_FREEZE | STATUS1_PARALYSIS | STATUS1_TOXIC_POISON | STATUS1_FROSTBITE) +#define STATUS1_REFRESH (STATUS1_POISON | STATUS1_BURN | STATUS1_PARALYSIS | STATUS1_TOXIC_POISON | STATUS1_FROSTBITE) + // Volatile status ailments // These are removed after exiting the battle or switching out #define STATUS2_CONFUSION (1 << 0 | 1 << 1 | 1 << 2) diff --git a/include/test/battle.h b/include/test/battle.h index 1a7cb070de..7cf7053988 100644 --- a/include/test/battle.h +++ b/include/test/battle.h @@ -716,6 +716,7 @@ struct BattleTestRunnerState u16 observedRatio; u16 trialRatio; bool8 runRandomly:1; + bool8 didRunRandomly:1; bool8 runGiven:1; bool8 runWhen:1; bool8 runScene:1; diff --git a/map_data_rules.mk b/map_data_rules.mk index ecad6d3bd3..6712698272 100755 --- a/map_data_rules.mk +++ b/map_data_rules.mk @@ -22,12 +22,10 @@ $(DATA_ASM_BUILDDIR)/maps.o: $(DATA_ASM_SUBDIR)/maps.s $(LAYOUTS_DIR)/layouts.in $(DATA_ASM_BUILDDIR)/map_events.o: $(DATA_ASM_SUBDIR)/map_events.s $(MAPS_DIR)/events.inc $(MAP_EVENTS) $(PREPROC) $< charmap.txt | $(CPP) -I include - | $(PREPROC) -ie $< charmap.txt | $(AS) $(ASFLAGS) -o $@ -$(DATA_SRC_SUBDIR)/map_group_count.h: $(MAPS_DIR)/headers.inc ; - $(MAPS_OUTDIR)/%/header.inc $(MAPS_OUTDIR)/%/events.inc $(MAPS_OUTDIR)/%/connections.inc: $(MAPS_DIR)/%/map.json $(MAPJSON) map emerald $< $(LAYOUTS_DIR)/layouts.json $(@D) -$(MAPS_OUTDIR)/connections.inc $(MAPS_OUTDIR)/groups.inc $(MAPS_OUTDIR)/events.inc $(MAPS_OUTDIR)/headers.inc $(INCLUDECONSTS_OUTDIR)/map_groups.h: $(MAPS_DIR)/map_groups.json +$(MAPS_OUTDIR)/connections.inc $(MAPS_OUTDIR)/groups.inc $(MAPS_OUTDIR)/events.inc $(MAPS_OUTDIR)/headers.inc $(INCLUDECONSTS_OUTDIR)/map_groups.h $(DATA_SRC_SUBDIR)/map_group_count.h: $(MAPS_DIR)/map_groups.json $(MAPJSON) groups emerald $< $(MAPS_OUTDIR) $(INCLUDECONSTS_OUTDIR) $(LAYOUTS_OUTDIR)/layouts.inc $(LAYOUTS_OUTDIR)/layouts_table.inc $(INCLUDECONSTS_OUTDIR)/layouts.h: $(LAYOUTS_DIR)/layouts.json diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 9ea2920bb9..f7823f1ff7 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -543,7 +543,7 @@ static void Cmd_trymemento(void); static void Cmd_setforcedtarget(void); static void Cmd_setcharge(void); static void Cmd_callterrainattack(void); -static void Cmd_cureifburnedparalysedorpoisoned(void); +static void Cmd_curestatuswithmove(void); static void Cmd_settorment(void); static void Cmd_jumpifnodamage(void); static void Cmd_settaunt(void); @@ -802,7 +802,7 @@ void (* const gBattleScriptingCommandsTable[])(void) = Cmd_setforcedtarget, //0xCA Cmd_setcharge, //0xCB Cmd_callterrainattack, //0xCC - Cmd_cureifburnedparalysedorpoisoned, //0xCD + Cmd_curestatuswithmove, //0xCD Cmd_settorment, //0xCE Cmd_jumpifnodamage, //0xCF Cmd_settaunt, //0xD0 @@ -6357,11 +6357,19 @@ static void Cmd_moveend(void) } else // Eject Pack { - gBattlescriptCurrInstr = BattleScript_EjectPackActivates; - AI_DATA->ejectPackSwitch = TRUE; - // Are these 2 lines below needed? - gProtectStructs[battler].statFell = FALSE; - gSpecialStatuses[gBattlerAttacker].preventLifeOrbDamage = TRUE; + if (gBattleResources->flags->flags[gBattlerTarget] & RESOURCE_FLAG_EMERGENCY_EXIT) + { + gBattlescriptCurrInstr = BattleScript_EjectPackMissesTiming; + gProtectStructs[battler].statFell = FALSE; + } + else + { + gBattlescriptCurrInstr = BattleScript_EjectPackActivates; + AI_DATA->ejectPackSwitch = TRUE; + // Are these 2 lines below needed? + gProtectStructs[battler].statFell = FALSE; + gSpecialStatuses[gBattlerAttacker].preventLifeOrbDamage = TRUE; + } } break; // Only the fastest Eject item activates } @@ -6418,6 +6426,7 @@ static void Cmd_moveend(void) SaveBattlerTarget(battler); // save battler with red card gBattleScripting.battler = battler; gEffectBattler = gBattlerAttacker; + gBattleStruct->redCardActivates = TRUE; if (gMovesInfo[gCurrentMove].effect == EFFECT_HIT_ESCAPE) gBattlescriptCurrInstr = BattleScript_MoveEnd; // Prevent user switch-in selection BattleScriptPushCursor(); @@ -6524,6 +6533,11 @@ static void Cmd_moveend(void) case MOVEEND_EMERGENCY_EXIT: // Special case, because moves hitting multiple opponents stop after switching out for (i = 0; i < gBattlersCount; i++) { + if (gBattleStruct->redCardActivates) + { + gBattleResources->flags->flags[i] &= ~RESOURCE_FLAG_EMERGENCY_EXIT; + continue; + } if (gBattleResources->flags->flags[i] & RESOURCE_FLAG_EMERGENCY_EXIT) { gBattleResources->flags->flags[i] &= ~RESOURCE_FLAG_EMERGENCY_EXIT; @@ -6629,6 +6643,7 @@ static void Cmd_moveend(void) gBattleStruct->poisonPuppeteerConfusion = FALSE; gBattleStruct->fickleBeamBoosted = FALSE; gBattleStruct->distortedTypeMatchups = 0; + gBattleStruct->redCardActivates = FALSE; gBattleStruct->usedMicleBerry &= ~(1u << gBattlerAttacker); if (gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE) gBattleStruct->pledgeMove = FALSE; @@ -14110,12 +14125,17 @@ u32 GetNaturePowerMove(u32 battler) return move; } -// Refresh -static void Cmd_cureifburnedparalysedorpoisoned(void) +static void Cmd_curestatuswithmove(void) { CMD_ARGS(const u8 *failInstr); + u32 shouldHeal; - if (gBattleMons[gBattlerAttacker].status1 & (STATUS1_POISON | STATUS1_BURN | STATUS1_PARALYSIS | STATUS1_TOXIC_POISON | STATUS1_FROSTBITE)) + if (gMovesInfo[gCurrentMove].effect == EFFECT_REFRESH) + shouldHeal = gBattleMons[gBattlerAttacker].status1 & STATUS1_REFRESH; + else // Take Heart + shouldHeal = gBattleMons[gBattlerAttacker].status1 & STATUS1_ANY; + + if (shouldHeal) { gBattleMons[gBattlerAttacker].status1 = 0; gBattlescriptCurrInstr = cmd->nextInstr; diff --git a/src/battle_util.c b/src/battle_util.c index 1e5f809c52..c268c65be1 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -7123,6 +7123,24 @@ static u8 TryConsumeMirrorHerb(u32 battler, bool32 execute) return effect; } +static inline u32 TryBoosterEnergy(u32 battler) +{ + if (gBattleStruct->boosterEnergyActivates & (1u << battler) || gBattleMons[battler].status2 & STATUS2_TRANSFORMED) + return ITEM_NO_EFFECT; + + if (((GetBattlerAbility(battler) == ABILITY_PROTOSYNTHESIS) && !((gBattleWeather & B_WEATHER_SUN) && WEATHER_HAS_EFFECT)) + || ((GetBattlerAbility(battler) == ABILITY_QUARK_DRIVE) && !(gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN))) + { + PREPARE_STAT_BUFFER(gBattleTextBuff1, GetHighestStatId(battler)); + gBattlerAbility = gBattleScripting.battler = battler; + gBattleStruct->boosterEnergyActivates |= 1u << battler; + BattleScriptExecute(BattleScript_BoosterEnergyEnd2); + return ITEM_EFFECT_OTHER; + } + + return ITEM_NO_EFFECT; +} + static u32 RestoreWhiteHerbStats(u32 battler) { u32 i, effect = 0; @@ -7639,17 +7657,7 @@ u8 ItemBattleEffects(u8 caseID, u32 battler, bool32 moveTurn) effect = TryConsumeMirrorHerb(battler, TRUE); break; case HOLD_EFFECT_BOOSTER_ENERGY: - if (!(gBattleStruct->boosterEnergyActivates & (1u << battler)) - && !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED) - && (((GetBattlerAbility(battler) == ABILITY_PROTOSYNTHESIS) && !((gBattleWeather & B_WEATHER_SUN) && WEATHER_HAS_EFFECT)) - || ((GetBattlerAbility(battler) == ABILITY_QUARK_DRIVE) && !(gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN)))) - { - PREPARE_STAT_BUFFER(gBattleTextBuff1, GetHighestStatId(battler)); - gBattleScripting.battler = battler; - gBattleStruct->boosterEnergyActivates |= 1u << battler; - BattleScriptExecute(BattleScript_BoosterEnergyEnd2); - effect = ITEM_EFFECT_OTHER; - } + effect = TryBoosterEnergy(battler); break; } if (effect != 0) @@ -7907,17 +7915,7 @@ u8 ItemBattleEffects(u8 caseID, u32 battler, bool32 moveTurn) effect = TryConsumeMirrorHerb(battler, TRUE); break; case HOLD_EFFECT_BOOSTER_ENERGY: - if (!(gBattleStruct->boosterEnergyActivates & (1u << battler)) - && !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED) - && (((GetBattlerAbility(battler) == ABILITY_PROTOSYNTHESIS) && !((gBattleWeather & B_WEATHER_SUN) && WEATHER_HAS_EFFECT)) - || ((GetBattlerAbility(battler) == ABILITY_QUARK_DRIVE) && !(gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN)))) - { - PREPARE_STAT_BUFFER(gBattleTextBuff1, GetHighestStatId(battler)); - gBattlerAbility = gBattleScripting.battler = battler; - gBattleStruct->boosterEnergyActivates |= 1u << battler; - BattleScriptExecute(BattleScript_BoosterEnergyEnd2); - effect = ITEM_EFFECT_OTHER; - } + effect = TryBoosterEnergy(battler); break; } diff --git a/test/battle/hold_effect/eject_pack.c b/test/battle/hold_effect/eject_pack.c index 8d85a77062..9272a23a7c 100644 --- a/test/battle/hold_effect/eject_pack.c +++ b/test/battle/hold_effect/eject_pack.c @@ -63,3 +63,25 @@ SINGLE_BATTLE_TEST("Eject Pack is triggered by self-inflicting stat decreases") ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent); } } + +SINGLE_BATTLE_TEST("Eject Pack will miss timing to switch out user if Emergency Exit was activated on target") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_EJECT_PACK); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_GOLISOPOD) { Ability(ABILITY_EMERGENCY_EXIT); MaxHP(263); HP(133); }; + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(player, MOVE_OVERHEAT); SEND_OUT(opponent, 1); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_OVERHEAT, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + MESSAGE("Wobbuffet is switched out with the Eject Pack!"); + ABILITY_POPUP(opponent, ABILITY_EMERGENCY_EXIT); + } THEN { + EXPECT(player->species == SPECIES_WOBBUFFET); + EXPECT(player->item == ITEM_NONE); + EXPECT(opponent->species == SPECIES_WYNAUT); + } +} diff --git a/test/battle/hold_effect/red_card.c b/test/battle/hold_effect/red_card.c index e1d94b4db0..8c36400c29 100644 --- a/test/battle/hold_effect/red_card.c +++ b/test/battle/hold_effect/red_card.c @@ -380,24 +380,6 @@ SINGLE_BATTLE_TEST("Red Card does not activate if attacker's Sheer Force applied } } -SINGLE_BATTLE_TEST("Red Card activates before Emergency Exit") -{ - GIVEN { - PLAYER(SPECIES_GOLISOPOD) { MaxHP(100); HP(51); Item(ITEM_RED_CARD); } - PLAYER(SPECIES_WIMPOD); - OPPONENT(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WYNAUT); - } WHEN { - TURN { MOVE(opponent, MOVE_TACKLE); SEND_OUT(player, 1); } - } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); - MESSAGE("Golisopod held up its Red Card against the opposing Wobbuffet!"); - ABILITY_POPUP(player, ABILITY_EMERGENCY_EXIT); - SEND_IN_MESSAGE("Wimpod"); - } -} - SINGLE_BATTLE_TEST("Red Card is consumed after dragged out replacement has its Speed lowered by Sticky Web") { GIVEN { @@ -468,4 +450,21 @@ SINGLE_BATTLE_TEST("Red Card does not activate if holder is switched in mid-turn } } +SINGLE_BATTLE_TEST("Red Card prevents Emergency Exit activation when triggered") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_GOLISOPOD) { Item(ITEM_RED_CARD); Ability(ABILITY_EMERGENCY_EXIT); MaxHP(263); HP(262); }; + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SUPER_FANG); MOVE(opponent, MOVE_CELEBRATE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SUPER_FANG, player); + HP_BAR(opponent); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); + NOT ABILITY_POPUP(opponent, ABILITY_EMERGENCY_EXIT); + } +} + TO_DO_BATTLE_TEST("Red Card activates but fails if the attacker has Dynamaxed"); diff --git a/test/battle/move_effect/refresh.c b/test/battle/move_effect/refresh.c new file mode 100644 index 0000000000..7cf319f3e6 --- /dev/null +++ b/test/battle/move_effect/refresh.c @@ -0,0 +1,68 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_REFRESH].effect == EFFECT_REFRESH); +} + +SINGLE_BATTLE_TEST("Refresh cures the user of burn, frostbite, poison, and paralysis") +{ + u32 status1; + PARAMETRIZE { status1 = STATUS1_POISON; } + PARAMETRIZE { status1 = STATUS1_BURN; } + PARAMETRIZE { status1 = STATUS1_PARALYSIS; } + PARAMETRIZE { status1 = STATUS1_TOXIC_POISON; } + PARAMETRIZE { status1 = STATUS1_FROSTBITE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Status1(status1); }; + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_REFRESH); } + } SCENE { + MESSAGE("Wobbuffet's status returned to normal!"); + STATUS_ICON(player, none: TRUE); + } +} + +SINGLE_BATTLE_TEST("Refresh does not cure the user of Freeze") +{ + PASSES_RANDOMLY(20, 100, RNG_FROZEN); + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_FREEZE); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_REFRESH); } + } SCENE { + MESSAGE("Wobbuffet used Refresh!"); + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_REFRESH, player); + STATUS_ICON(player, none: TRUE); } + MESSAGE("But it failed!"); + } +} + +SINGLE_BATTLE_TEST("Refresh does not cure sleep when used by Sleep Talk") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_SPORE].effect == EFFECT_SLEEP); + ASSUME(gMovesInfo[MOVE_SLEEP_TALK].effect == EFFECT_SLEEP_TALK); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_SLEEP_TALK, MOVE_REFRESH); } + } WHEN { + TURN { MOVE(player, MOVE_SPORE); MOVE(opponent, MOVE_SLEEP_TALK); } + TURN { MOVE(player, MOVE_SPORE); MOVE(opponent, MOVE_REFRESH); } + } SCENE { + MESSAGE("Wobbuffet used Spore!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SPORE, player); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_SLP, opponent); + MESSAGE("The opposing Wobbuffet fell asleep!"); + MESSAGE("The opposing Wobbuffet used Sleep Talk!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SLEEP_TALK, opponent); + MESSAGE("The opposing Wobbuffet used Refresh!"); + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_REFRESH, player); + STATUS_ICON(player, none: TRUE); } + MESSAGE("But it failed!"); + } +} diff --git a/test/battle/move_effect/take_heart.c b/test/battle/move_effect/take_heart.c index 081815cfb8..2961725b22 100644 --- a/test/battle/move_effect/take_heart.c +++ b/test/battle/move_effect/take_heart.c @@ -40,8 +40,32 @@ SINGLE_BATTLE_TEST("Take Heart cures the user of all status conditions") STATUS_ICON(player, none: TRUE); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); } else { + STATUS_ICON(player, none: TRUE); MESSAGE("Wobbuffet's status returned to normal!"); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); } } } + +SINGLE_BATTLE_TEST("Take Heart cures sleep when used by Sleep Talk") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_SPORE].effect == EFFECT_SLEEP); + ASSUME(gMovesInfo[MOVE_SLEEP_TALK].effect == EFFECT_SLEEP_TALK); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_SLEEP_TALK, MOVE_TAKE_HEART); } + } WHEN { + TURN { MOVE(player, MOVE_SPORE); MOVE(opponent, MOVE_SLEEP_TALK); } + } SCENE { + MESSAGE("Wobbuffet used Spore!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SPORE, player); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_SLP, opponent); + MESSAGE("The opposing Wobbuffet fell asleep!"); + MESSAGE("The opposing Wobbuffet used Sleep Talk!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SLEEP_TALK, opponent); + MESSAGE("The opposing Wobbuffet used Take Heart!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TAKE_HEART, opponent); + STATUS_ICON(opponent, none: TRUE); + MESSAGE("The opposing Wobbuffet's status returned to normal!"); + } +} diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index 04db97e85d..b073ac226a 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -365,6 +365,7 @@ u32 RandomUniform(enum RandomTag tag, u32 lo, u32 hi) if (tag == STATE->rngTag) { + STATE->didRunRandomly = TRUE; u32 n = hi - lo + 1; if (STATE->trials == 1) { @@ -401,6 +402,7 @@ u32 RandomUniformExcept(enum RandomTag tag, u32 lo, u32 hi, bool32 (*reject)(u32 if (tag == STATE->rngTag) { + STATE->didRunRandomly = TRUE; if (STATE->trials == 1) { u32 n = 0, i; @@ -449,6 +451,7 @@ u32 RandomWeightedArray(enum RandomTag tag, u32 sum, u32 n, const u8 *weights) if (tag == STATE->rngTag) { + STATE->didRunRandomly = TRUE; if (STATE->trials == 1) { STATE->trials = n; @@ -522,6 +525,7 @@ const void *RandomElementArray(enum RandomTag tag, const void *array, size_t siz if (tag == STATE->rngTag) { + STATE->didRunRandomly = TRUE; if (STATE->trials == 1) { STATE->trials = count; @@ -1349,6 +1353,7 @@ static void CB2_BattleTest_NextParameter(void) else { STATE->trials = 0; + STATE->didRunRandomly = FALSE; BattleTest_Run(gTestRunnerState.test->data); } } @@ -1395,6 +1400,9 @@ static void CB2_BattleTest_NextTrial(void) } else { + if (STATE->rngTag && !STATE->didRunRandomly && STATE->expectedRatio != Q_4_12(0.0) && STATE->expectedRatio != Q_4_12(1.0)) + Test_ExitWithResult(TEST_RESULT_INVALID, SourceLine(0), ":L%s:%d: PASSES_RANDOMLY specified but no Random* call with that tag executed", gTestRunnerState.test->filename, SourceLine(0)); + // This is a tolerance of +/- ~2%. if (abs(STATE->observedRatio - STATE->expectedRatio) <= Q_4_12(0.02)) gTestRunnerState.result = TEST_RESULT_PASS;