From f61a0f6a30b667ceef00669f38a487edb8096779 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Sat, 21 Dec 2024 19:45:50 +0100 Subject: [PATCH] Sheer Force fix and move effect cleanup (#5812) --- asm/macros/battle_script.inc | 10 - data/battle_scripts_1.s | 56 +- include/battle_scripts.h | 14 +- include/constants/battle.h | 10 +- include/constants/battle_move_effects.h | 7 - include/pokemon.h | 10 +- src/battle_ai_main.c | 11 +- src/battle_script_commands.c | 170 ++-- src/battle_util.c | 5 + src/data/battle_move_effects.h | 42 - src/data/moves_info.h | 62 +- test/battle/ability/sheer_force.c | 949 +++++++++++++++++- test/battle/move_effect/salt_cure.c | 2 +- test/battle/move_effect_secondary/haze.c | 32 + .../ion_deluge.c} | 22 +- .../battle/move_effect_secondary/leech_seed.c | 37 + .../move_effect_secondary/light_screen.c | 32 + test/battle/move_effect_secondary/reflect.c | 32 + 18 files changed, 1266 insertions(+), 237 deletions(-) create mode 100644 test/battle/move_effect_secondary/haze.c rename test/battle/{move_effect/plasma_fists.c => move_effect_secondary/ion_deluge.c} (80%) create mode 100644 test/battle/move_effect_secondary/leech_seed.c create mode 100644 test/battle/move_effect_secondary/light_screen.c create mode 100644 test/battle/move_effect_secondary/reflect.c diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 7ae4403ff9..fbc724a81e 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1425,11 +1425,6 @@ callnative BS_TryRevertWeatherForm .endm - .macro applysaltcure battler:req - callnative BS_ApplySaltCure - .byte \battler - .endm - .macro trysetoctolock battler:req, failInstr:req callnative BS_TrySetOctolock .byte \battler @@ -2214,11 +2209,6 @@ .4byte \jumpInstr .endm - .macro eeriespellppreduce failInstr:req - various BS_TARGET, VARIOUS_EERIE_SPELL_PP_REDUCE - .4byte \failInstr - .endm - .macro jumpifteamhealthy battler:req, jumpInstr:req various \battler, VARIOUS_JUMP_IF_TEAM_HEALTHY .4byte \jumpInstr diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 7359b4469a..d94ba2363b 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -396,16 +396,10 @@ BattleScript_EffectHit_Pledge:: tryfaintmon BS_TARGET return -BattleScript_EffectSaltCure:: - call BattleScript_EffectHit_Ret - tryfaintmon BS_TARGET - jumpiffainted BS_TARGET, TRUE, BattleScript_EffectSaltCure_End - jumpifsubstituteblocks BattleScript_EffectSaltCure_End - applysaltcure BS_TARGET +BattleScript_MoveEffectSaltCure:: printstring STRINGID_TARGETISBEINGSALTCURED waitmessage B_WAIT_TIME_LONG -BattleScript_EffectSaltCure_End: - goto BattleScript_MoveEnd + return BattleScript_SaltCureExtraDamage:: playanimation BS_TARGET, B_ANIM_SALT_CURE_DAMAGE, NULL @@ -949,13 +943,10 @@ BattleScript_HyperspaceFuryRemoveProtect:: waitmessage B_WAIT_TIME_LONG return -BattleScript_EffectPlasmaFists:: - call BattleScript_EffectHit_Ret - tryfaintmon BS_TARGET - orword gFieldStatuses, STATUS_FIELD_ION_DELUGE +BattleScript_MoveEffectIonDeluge:: printstring STRINGID_IONDELUGEON waitmessage B_WAIT_TIME_LONG - goto BattleScript_MoveEnd + return BattleScript_EffectSparklySwirl:: call BattleScript_EffectHit_Ret @@ -966,41 +957,25 @@ BattleScript_EffectSparklySwirl:: waitstate goto BattleScript_MoveEnd -BattleScript_EffectFreezyFrost:: - call BattleScript_EffectHit_Ret - tryfaintmon BS_TARGET - normalisebuffs +BattleScript_MoveEffectHaze:: printstring STRINGID_STATCHANGESGONE waitmessage B_WAIT_TIME_LONG - goto BattleScript_MoveEnd + return -BattleScript_EffectSappySeed:: - jumpifstatus3 BS_TARGET, STATUS3_LEECHSEED, BattleScript_EffectHit - call BattleScript_EffectHit_Ret - tryfaintmon BS_TARGET - jumpifhasnohp BS_TARGET, BattleScript_MoveEnd - setseeded - printfromtable gLeechSeedStringIds +BattleScript_MoveEffectLeechSeed:: + printstring STRINGID_PKMNSEEDED waitmessage B_WAIT_TIME_LONG goto BattleScript_MoveEnd -BattleScript_EffectBaddyBad:: - jumpifsideaffecting BS_ATTACKER, SIDE_STATUS_REFLECT, BattleScript_EffectHit - call BattleScript_EffectHit_Ret - tryfaintmon BS_TARGET - setreflect +BattleScript_MoveEffectReflect:: printfromtable gReflectLightScreenSafeguardStringIds waitmessage B_WAIT_TIME_LONG - goto BattleScript_MoveEnd + return -BattleScript_EffectGlitzyGlow:: - jumpifsideaffecting BS_ATTACKER, SIDE_STATUS_LIGHTSCREEN, BattleScript_EffectHit - call BattleScript_EffectHit_Ret - tryfaintmon BS_TARGET - setlightscreen +BattleScript_MoveEffectLightScreen:: printfromtable gReflectLightScreenSafeguardStringIds waitmessage B_WAIT_TIME_LONG - goto BattleScript_MoveEnd + return BattleScript_EffectStuffCheeks:: attackcanceler @@ -4065,13 +4040,10 @@ BattleScript_EffectDestinyBond:: waitmessage B_WAIT_TIME_LONG goto BattleScript_MoveEnd -BattleScript_EffectEerieSpell:: - call BattleScript_EffectHit_Ret - tryfaintmon BS_TARGET - eeriespellppreduce BattleScript_MoveEnd +BattleScript_MoveEffectEerieSpell:: printstring STRINGID_PKMNREDUCEDPP waitmessage B_WAIT_TIME_LONG - goto BattleScript_MoveEnd + return BattleScript_EffectSpite:: attackcanceler diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 3c58b28e1e..158dfb5dc3 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -804,18 +804,18 @@ extern const u8 BattleScript_EffectGeomancy[]; extern const u8 BattleScript_EffectFairyLock[]; extern const u8 BattleScript_EffectAllySwitch[]; extern const u8 BattleScript_EffectRelicSong[]; -extern const u8 BattleScript_EffectEerieSpell[]; +extern const u8 BattleScript_MoveEffectEerieSpell[]; extern const u8 BattleScript_EffectJungleHealing[]; extern const u8 BattleScript_EffectCoaching[]; extern const u8 BattleScript_EffectDecorate[]; extern const u8 BattleScript_EffectRecoilHP25[]; extern const u8 BattleScript_EffectStuffCheeks[]; -extern const u8 BattleScript_EffectGlitzyGlow[]; -extern const u8 BattleScript_EffectBaddyBad[]; -extern const u8 BattleScript_EffectSappySeed[]; -extern const u8 BattleScript_EffectFreezyFrost[]; +extern const u8 BattleScript_MoveEffectLightScreen[]; +extern const u8 BattleScript_MoveEffectReflect[]; +extern const u8 BattleScript_MoveEffectLeechSeed[]; +extern const u8 BattleScript_MoveEffectHaze[]; extern const u8 BattleScript_EffectSparklySwirl[]; -extern const u8 BattleScript_EffectPlasmaFists[]; +extern const u8 BattleScript_MoveEffectIonDeluge[]; extern const u8 BattleScript_EffectHyperspaceFury[]; extern const u8 BattleScript_EffectAuraWheel[]; extern const u8 BattleScript_EffectPhotonGeyser[]; @@ -838,7 +838,7 @@ extern const u8 BattleScript_EffectRevivalBlessing[]; extern const u8 BattleScript_EffectSnow[]; extern const u8 BattleScript_EffectTakeHeart[]; extern const u8 BattleScript_EffectCorrosiveGas[]; -extern const u8 BattleScript_EffectSaltCure[]; +extern const u8 BattleScript_MoveEffectSaltCure[]; extern const u8 BattleScript_EffectChillyReception[]; extern const u8 BattleScript_EffectMaxMove[]; extern const u8 BattleScript_EffectGlaiveRush[]; diff --git a/include/constants/battle.h b/include/constants/battle.h index ed19a72d9c..d9c4e1301c 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -405,8 +405,16 @@ #define MOVE_EFFECT_PSYCHIC_NOISE 78 #define MOVE_EFFECT_TERA_BLAST 79 #define MOVE_EFFECT_ORDER_UP 80 +#define MOVE_EFFECT_ION_DELUGE 81 +#define MOVE_EFFECT_AROMATHERAPY 82 // No functionality yet +#define MOVE_EFFECT_HAZE 83 +#define MOVE_EFFECT_LEECH_SEED 84 +#define MOVE_EFFECT_REFLECT 85 +#define MOVE_EFFECT_LIGHT_SCREEN 86 +#define MOVE_EFFECT_SALT_CURE 87 +#define MOVE_EFFECT_EERIE_SPELL 88 -#define NUM_MOVE_EFFECTS 81 +#define NUM_MOVE_EFFECTS 88 #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 b1472d0280..d0e4839bce 100644 --- a/include/constants/battle_move_effects.h +++ b/include/constants/battle_move_effects.h @@ -282,7 +282,6 @@ enum { EFFECT_ALLY_SWITCH, EFFECT_RELIC_SONG, EFFECT_BODY_PRESS, - EFFECT_EERIE_SPELL, EFFECT_JUNGLE_HEALING, EFFECT_COACHING, EFFECT_LASH_OUT, @@ -293,12 +292,7 @@ enum { EFFECT_RECOIL_HP_25, EFFECT_STUFF_CHEEKS, EFFECT_GRAV_APPLE, - EFFECT_GLITZY_GLOW, - EFFECT_BADDY_BAD, - EFFECT_SAPPY_SEED, - EFFECT_FREEZY_FROST, EFFECT_SPARKLY_SWIRL, - EFFECT_PLASMA_FISTS, EFFECT_HYPERSPACE_FURY, EFFECT_AURA_WHEEL, EFFECT_PHOTON_GEYSER, @@ -331,7 +325,6 @@ enum { EFFECT_COLLISION_COURSE, EFFECT_CORROSIVE_GAS, EFFECT_POPULATION_BOMB, - EFFECT_SALT_CURE, EFFECT_CHILLY_RECEPTION, EFFECT_MAX_MOVE, EFFECT_GLAIVE_RUSH, diff --git a/include/pokemon.h b/include/pokemon.h index 8a618f5fe1..069290b363 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -564,8 +564,12 @@ struct MoveInfo #define EFFECTS_ARR(...) (const struct AdditionalEffect[]) {__VA_ARGS__} #define ADDITIONAL_EFFECTS(...) EFFECTS_ARR( __VA_ARGS__ ), .numAdditionalEffects = ARRAY_COUNT(EFFECTS_ARR( __VA_ARGS__ )) -// Just a hack to make a move boosted by Sheer Force despite having no secondary effects affected -#define SHEER_FORCE_HACK { .moveEffect = 0, .chance = 100, } +enum SheerForceBoost +{ + SHEER_FORCE_AUTO_BOOST, // This is the default state when a move has a move effect with a chance + SHEER_FORCE_BOOST, // If a move effect doesn't have an effect with a chance this can force a boost + SHEER_FORCE_NO_BOOST, // Prevents a Sheer Force boost +}; struct AdditionalEffect { @@ -573,6 +577,8 @@ struct AdditionalEffect u8 self:1; u8 onlyIfTargetRaisedStats:1; u8 onChargeTurnOnly:1; + u8 sheerForceBoost:2; // Handles edge cases for Sheer Force + u8 padding:3; u8 chance; // 0% = effect certain, primary effect }; diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index d245be8cd5..538b0e4732 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -2511,8 +2511,6 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) ADJUST_SCORE(-4); break; //TODO - //case EFFECT_PLASMA_FISTS: - //break; //case EFFECT_SHELL_TRAP: //break; //case EFFECT_BEAK_BLAST: @@ -4416,10 +4414,6 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) || gBattleMons[BATTLE_PARTNER(battlerAtk)].status1 & STATUS1_ANY) ADJUST_SCORE(GOOD_EFFECT); break; - case EFFECT_SALT_CURE: - if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_WATER) || IS_BATTLER_OF_TYPE(battlerDef, TYPE_STEEL)) - ADJUST_SCORE(DECENT_EFFECT); - break; } // move effect checks // check move additional effects that are likely to happen @@ -4664,6 +4658,11 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) if (!HasMoveWithAdditionalEffect(battlerDef, MOVE_EFFECT_RAPID_SPIN) && ShouldTrap(battlerAtk, battlerDef, move)) ADJUST_SCORE(BEST_EFFECT); break; + case MOVE_EFFECT_SALT_CURE: + if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_WATER) || IS_BATTLER_OF_TYPE(battlerDef, TYPE_STEEL)) + ADJUST_SCORE(DECENT_EFFECT); + break; + } } } diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 244aab3ed0..40f9db64c6 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -2904,7 +2904,8 @@ void SetMoveEffect(bool32 primary, bool32 certain) bool32 statusChanged = FALSE; bool32 mirrorArmorReflected = (GetBattlerAbility(gBattlerTarget) == ABILITY_MIRROR_ARMOR); u32 flags = 0; - u16 battlerAbility; + u32 battlerAbility; + u32 side; bool8 activateAfterFaint = FALSE; // NULL move effect @@ -3992,6 +3993,117 @@ void SetMoveEffect(bool32 primary, bool32 certain) } } break; + case MOVE_EFFECT_ION_DELUGE: + if (!(gFieldStatuses & STATUS_FIELD_ION_DELUGE)) + { + gFieldStatuses |= STATUS_FIELD_ION_DELUGE; + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_MoveEffectIonDeluge; + } + break; + // TODO: The moves aromatherapy and heal bell need a refactor first + // case MOVE_EFFECT_AROMATHERAPY: + // break; + case MOVE_EFFECT_HAZE: + for (i = 0; i < gBattlersCount; i++) + TryResetBattlerStatChanges(i); + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_MoveEffectHaze; + break; + case MOVE_EFFECT_LEECH_SEED: + if (!IS_BATTLER_OF_TYPE(gBattlerTarget, TYPE_GRASS) && !(gStatuses3[gBattlerTarget] & STATUS3_LEECHSEED)) + { + gStatuses3[gBattlerTarget] |= gBattlerAttacker; + gStatuses3[gBattlerTarget] |= STATUS3_LEECHSEED; + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_MoveEffectLeechSeed; + } + break; + case MOVE_EFFECT_REFLECT: + side = GetBattlerSide(gBattlerAttacker); + if (!(gSideStatuses[side] & SIDE_STATUS_REFLECT)) + { + gSideStatuses[side] |= SIDE_STATUS_REFLECT; + if (GetBattlerHoldEffect(gBattlerAttacker, TRUE) == HOLD_EFFECT_LIGHT_CLAY) + gSideTimers[side].reflectTimer = 8; + else + gSideTimers[side].reflectTimer = 5; + gSideTimers[side].reflectBattlerId = gBattlerAttacker; + + if (IsDoubleBattle() && CountAliveMonsInBattle(BATTLE_ALIVE_SIDE, gBattlerAttacker) == 2) + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SET_REFLECT_DOUBLE; + else + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SET_REFLECT_SINGLE; + + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_MoveEffectReflect; + } + break; + case MOVE_EFFECT_LIGHT_SCREEN: + side = GetBattlerSide(gBattlerAttacker); + if (!(gSideStatuses[side] & SIDE_STATUS_LIGHTSCREEN)) + { + gSideStatuses[side] |= SIDE_STATUS_LIGHTSCREEN; + if (GetBattlerHoldEffect(gBattlerAttacker, TRUE) == HOLD_EFFECT_LIGHT_CLAY) + gSideTimers[side].lightscreenTimer = 8; + else + gSideTimers[side].lightscreenTimer = 5; + gSideTimers[side].lightscreenBattlerId = gBattlerAttacker; + + if (IsDoubleBattle() && CountAliveMonsInBattle(BATTLE_ALIVE_SIDE, gBattlerAttacker) == 2) + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SET_LIGHTSCREEN_DOUBLE; + else + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SET_LIGHTSCREEN_SINGLE; + + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_MoveEffectLightScreen; + } + break; + case MOVE_EFFECT_SALT_CURE: + if (!(gStatuses4[gBattlerTarget] & STATUS4_SALT_CURE)) + { + gStatuses4[gBattlerTarget] |= STATUS4_SALT_CURE; + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_MoveEffectSaltCure; + } + break; + case MOVE_EFFECT_EERIE_SPELL: + if (gLastMoves[gBattlerTarget] != MOVE_NONE && gLastMoves[gBattlerTarget] != 0xFFFF) + { + u32 i; + + for (i = 0; i < MAX_MON_MOVES; i++) + { + if (gLastMoves[gBattlerTarget] == gBattleMons[gBattlerTarget].moves[i]) + break; + } + + if (i != MAX_MON_MOVES && gBattleMons[gBattlerTarget].pp[i] != 0) + { + u32 ppToDeduct = 3; + + if (gBattleMons[gBattlerTarget].pp[i] < ppToDeduct) + ppToDeduct = gBattleMons[gBattlerTarget].pp[i]; + + PREPARE_MOVE_BUFFER(gBattleTextBuff1, gLastMoves[gBattlerTarget]) + ConvertIntToDecimalStringN(gBattleTextBuff2, ppToDeduct, STR_CONV_MODE_LEFT_ALIGN, 1); + PREPARE_BYTE_NUMBER_BUFFER(gBattleTextBuff2, 1, ppToDeduct) + gBattleMons[gBattlerTarget].pp[i] -= ppToDeduct; + if (!(gDisableStructs[gBattlerTarget].mimickedMoves & (1u << i)) + && !(gBattleMons[gBattlerTarget].status2 & STATUS2_TRANSFORMED)) + { + BtlController_EmitSetMonData(gBattlerTarget, BUFFER_A, REQUEST_PPMOVE1_BATTLE + i, 0, sizeof(gBattleMons[gBattlerTarget].pp[i]), &gBattleMons[gBattlerTarget].pp[i]); + MarkBattlerForControllerExec(gBattlerTarget); + } + + if (gBattleMons[gBattlerTarget].pp[i] == 0 && gBattleStruct->skyDropTargets[gBattlerTarget] == 0xFF) + CancelMultiTurnMoves(gBattlerTarget); + + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_MoveEffectEerieSpell; + } + } + break; } } } @@ -10459,53 +10571,6 @@ static void Cmd_various(void) MarkBattlerForControllerExec(battler); break; } - case VARIOUS_EERIE_SPELL_PP_REDUCE: - { - VARIOUS_ARGS(const u8 *failInstr); - if (gLastMoves[battler] != 0 && gLastMoves[battler] != 0xFFFF) - { - s32 i; - - for (i = 0; i < MAX_MON_MOVES; i++) - { - if (gLastMoves[battler] == gBattleMons[battler].moves[i]) - break; - } - - if (i != MAX_MON_MOVES && gBattleMons[battler].pp[i] != 0) - { - s32 ppToDeduct = 3; - - if (gBattleMons[battler].pp[i] < ppToDeduct) - ppToDeduct = gBattleMons[battler].pp[i]; - - PREPARE_MOVE_BUFFER(gBattleTextBuff1, gLastMoves[battler]) - ConvertIntToDecimalStringN(gBattleTextBuff2, ppToDeduct, STR_CONV_MODE_LEFT_ALIGN, 1); - PREPARE_BYTE_NUMBER_BUFFER(gBattleTextBuff2, 1, ppToDeduct) - gBattleMons[battler].pp[i] -= ppToDeduct; - if (!(gDisableStructs[battler].mimickedMoves & (1u << i)) - && !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED)) - { - BtlController_EmitSetMonData(battler, BUFFER_A, REQUEST_PPMOVE1_BATTLE + i, 0, sizeof(gBattleMons[battler].pp[i]), &gBattleMons[battler].pp[i]); - MarkBattlerForControllerExec(battler); - } - - if (gBattleMons[battler].pp[i] == 0 && gBattleStruct->skyDropTargets[battler] == 0xFF) - CancelMultiTurnMoves(battler); - - gBattlescriptCurrInstr = cmd->nextInstr; // continue - } - else - { - gBattlescriptCurrInstr = cmd->failInstr; // cant reduce pp - } - } - else - { - gBattlescriptCurrInstr = cmd->failInstr; // cant reduce pp - } - return; - } case VARIOUS_JUMP_IF_TEAM_HEALTHY: { VARIOUS_ARGS(const u8 *jumpInstr); @@ -16539,15 +16604,6 @@ void BS_JumpIfElectricAbilityAffected(void) gBattlescriptCurrInstr = cmd->nextInstr; } -void BS_ApplySaltCure(void) -{ - NATIVE_ARGS(u8 battler); - - u8 battler = GetBattlerForBattleScript(cmd->battler); - gStatuses4[battler] |= STATUS4_SALT_CURE; - gBattlescriptCurrInstr = cmd->nextInstr; -} - void BS_JumpIfArgument(void) { NATIVE_ARGS(u8 argument, const u8 *jumpInstr); diff --git a/src/battle_util.c b/src/battle_util.c index 45be169dfd..1e0341f337 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -11783,8 +11783,13 @@ bool32 MoveIsAffectedBySheerForce(u32 move) u32 i; for (i = 0; i < gMovesInfo[move].numAdditionalEffects; i++) { + if (gMovesInfo[move].additionalEffects[i].sheerForceBoost == SHEER_FORCE_NO_BOOST) + continue; + if (gMovesInfo[move].additionalEffects[i].chance > 0) return TRUE; + if (gMovesInfo[move].additionalEffects[i].sheerForceBoost == SHEER_FORCE_BOOST) + return TRUE; } return FALSE; } diff --git a/src/data/battle_move_effects.h b/src/data/battle_move_effects.h index b5b3e539c8..ae9e831e4a 100644 --- a/src/data/battle_move_effects.h +++ b/src/data/battle_move_effects.h @@ -1811,12 +1811,6 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = .battleTvScore = 0, // TODO: Assign points }, - [EFFECT_EERIE_SPELL] = - { - .battleScript = BattleScript_EffectEerieSpell, - .battleTvScore = 0, // TODO: Assign points - }, - [EFFECT_JUNGLE_HEALING] = { .battleScript = BattleScript_EffectJungleHealing, @@ -1880,42 +1874,12 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = .battleTvScore = 0, // TODO: Assign points }, - [EFFECT_GLITZY_GLOW] = - { - .battleScript = BattleScript_EffectGlitzyGlow, - .battleTvScore = 0, // TODO: Assign points - }, - - [EFFECT_BADDY_BAD] = - { - .battleScript = BattleScript_EffectBaddyBad, - .battleTvScore = 0, // TODO: Assign points - }, - - [EFFECT_SAPPY_SEED] = - { - .battleScript = BattleScript_EffectSappySeed, - .battleTvScore = 0, // TODO: Assign points - }, - - [EFFECT_FREEZY_FROST] = - { - .battleScript = BattleScript_EffectFreezyFrost, - .battleTvScore = 0, // TODO: Assign points - }, - [EFFECT_SPARKLY_SWIRL] = { .battleScript = BattleScript_EffectSparklySwirl, .battleTvScore = 0, // TODO: Assign points }, - [EFFECT_PLASMA_FISTS] = - { - .battleScript = BattleScript_EffectPlasmaFists, - .battleTvScore = 0, // TODO: Assign points - }, - [EFFECT_HYPERSPACE_FURY] = { .battleScript = BattleScript_EffectHyperspaceFury, @@ -2113,12 +2077,6 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = .battleTvScore = 0, // TODO: Assign points }, - [EFFECT_SALT_CURE] = - { - .battleScript = BattleScript_EffectSaltCure, - .battleTvScore = 0, // TODO: Assign points - }, - [EFFECT_CHILLY_RECEPTION] = { .battleScript = BattleScript_EffectChillyReception, diff --git a/src/data/moves_info.h b/src/data/moves_info.h index 180d0d74ac..4ff3b71f4c 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -16757,7 +16757,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .description = COMPOUND_STRING( "Hits with electrical fists.\n" "Normal moves turn Electric."), - .effect = EFFECT_PLASMA_FISTS, + .effect = EFFECT_HIT, .power = 100, .type = TYPE_ELECTRIC, .accuracy = 100, @@ -16772,6 +16772,11 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .contestCategory = CONTEST_CATEGORY_COOL, .contestComboStarterId = 0, .contestComboMoves = {0}, + .additionalEffects = ADDITIONAL_EFFECTS({ + .moveEffect = MOVE_EFFECT_ION_DELUGE, + .chance = 100, + .sheerForceBoost = SHEER_FORCE_NO_BOOST, + }), .battleAnimScript = gBattleAnimMove_PlasmaFists, }, @@ -16820,6 +16825,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .additionalEffects = ADDITIONAL_EFFECTS({ .moveEffect = MOVE_EFFECT_EVS_PLUS_1, .chance = 100, + .sheerForceBoost = SHEER_FORCE_NO_BOOST, }), #endif .battleAnimScript = gBattleAnimMove_ZippyZap, @@ -16869,6 +16875,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .additionalEffects = ADDITIONAL_EFFECTS({ .moveEffect = MOVE_EFFECT_FLINCH, .chance = 30, + .sheerForceBoost = SHEER_FORCE_NO_BOOST, }), .battleAnimScript = gBattleAnimMove_FloatyFall, }, @@ -16936,6 +16943,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .additionalEffects = ADDITIONAL_EFFECTS({ .moveEffect = MOVE_EFFECT_PARALYSIS, .chance = 100, + .sheerForceBoost = SHEER_FORCE_NO_BOOST, }), .battleAnimScript = gBattleAnimMove_BuzzyBuzz, }, @@ -16961,6 +16969,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .additionalEffects = ADDITIONAL_EFFECTS({ .moveEffect = MOVE_EFFECT_BURN, .chance = 100, + .sheerForceBoost = SHEER_FORCE_NO_BOOST, }), .battleAnimScript = gBattleAnimMove_SizzlySlide, }, @@ -16971,7 +16980,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .description = COMPOUND_STRING( "Telekinetic force that sets\n" "wall, lowering Sp. Atk damage."), - .effect = EFFECT_GLITZY_GLOW, + .effect = EFFECT_HIT, .power = B_UPDATED_MOVE_DATA >= GEN_8 ? 80 : 90, .type = TYPE_PSYCHIC, .accuracy = B_UPDATED_MOVE_DATA >= GEN_8 ? 95 : 100, @@ -16981,6 +16990,11 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .category = DAMAGE_CATEGORY_SPECIAL, .mirrorMoveBanned = B_UPDATED_MOVE_FLAGS < GEN_8, .metronomeBanned = TRUE, + .additionalEffects = ADDITIONAL_EFFECTS({ + .moveEffect = MOVE_EFFECT_LIGHT_SCREEN, + .chance = 100, + .sheerForceBoost = SHEER_FORCE_NO_BOOST, + }), .battleAnimScript = gBattleAnimMove_GlitzyGlow, }, @@ -16990,7 +17004,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .description = COMPOUND_STRING( "Acting badly, attacks. Sets\n" "wall, lowering Attack damage."), - .effect = EFFECT_BADDY_BAD, + .effect = EFFECT_HIT, .power = B_UPDATED_MOVE_DATA >= GEN_8 ? 80 : 90, .type = TYPE_DARK, .accuracy = B_UPDATED_MOVE_DATA >= GEN_8 ? 95 : 100, @@ -17000,6 +17014,11 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .category = DAMAGE_CATEGORY_SPECIAL, .mirrorMoveBanned = B_UPDATED_MOVE_FLAGS < GEN_8, .metronomeBanned = TRUE, + .additionalEffects = ADDITIONAL_EFFECTS({ + .moveEffect = MOVE_EFFECT_REFLECT, + .chance = 100, + .sheerForceBoost = SHEER_FORCE_NO_BOOST, + }), .battleAnimScript = gBattleAnimMove_BaddyBad, }, @@ -17009,7 +17028,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .description = COMPOUND_STRING( "Giant stalk scatters seeds\n" "that drain HP every turn."), - .effect = EFFECT_SAPPY_SEED, + .effect = EFFECT_HIT, .power = B_UPDATED_MOVE_DATA >= GEN_8 ? 100 : 90, .type = TYPE_GRASS, .accuracy = B_UPDATED_MOVE_DATA >= GEN_8 ? 90 : 100, @@ -17020,6 +17039,11 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .mirrorMoveBanned = B_UPDATED_MOVE_FLAGS < GEN_8, .magicCoatAffected = TRUE, .metronomeBanned = TRUE, + .additionalEffects = ADDITIONAL_EFFECTS({ + .moveEffect = MOVE_EFFECT_LEECH_SEED, + .chance = 100, + .sheerForceBoost = SHEER_FORCE_NO_BOOST, + }), .battleAnimScript = gBattleAnimMove_SappySeed, }, @@ -17029,7 +17053,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .description = COMPOUND_STRING( "Crystal from cold haze hits.\n" "Eliminates all stat changes."), - .effect = EFFECT_FREEZY_FROST, + .effect = EFFECT_HIT, .power = B_UPDATED_MOVE_DATA >= GEN_8 ? 100 : 90, .type = TYPE_ICE, .accuracy = B_UPDATED_MOVE_DATA >= GEN_8 ? 90 : 100, @@ -17039,6 +17063,11 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .category = DAMAGE_CATEGORY_SPECIAL, .mirrorMoveBanned = B_UPDATED_MOVE_FLAGS < GEN_8, .metronomeBanned = TRUE, + .additionalEffects = ADDITIONAL_EFFECTS({ + .moveEffect = MOVE_EFFECT_HAZE, + .chance = 100, + .sheerForceBoost = SHEER_FORCE_NO_BOOST, + }), .battleAnimScript = gBattleAnimMove_FreezyFrost, }, @@ -17048,7 +17077,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .description = COMPOUND_STRING( "Wrap foe with whirlwind of\n" "scent. Heals party's status."), - .effect = EFFECT_SPARKLY_SWIRL, + .effect = EFFECT_SPARKLY_SWIRL, // Temprorary .power = B_UPDATED_MOVE_DATA >= GEN_8 ? 120 : 90, .type = TYPE_FAIRY, .accuracy = B_UPDATED_MOVE_DATA >= GEN_8 ? 85 : 100, @@ -17058,6 +17087,11 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .category = DAMAGE_CATEGORY_SPECIAL, .mirrorMoveBanned = B_UPDATED_MOVE_FLAGS < GEN_8, .metronomeBanned = TRUE, + // .additionalEffects = ADDITIONAL_EFFECTS({ + // .moveEffect = 0, // MOVE_EFFECT_AROMATHERAPY, Added 0 for Sheer Force boost + // .chance = 100, + // .sheerForceBoost = SHEER_FORCE_NO_BOOST, + // }), .battleAnimScript = gBattleAnimMove_SparklySwirl, }, @@ -18682,7 +18716,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .description = COMPOUND_STRING( "Attacks with psychic power.\n" "Foe's last move has 3 PP cut."), - .effect = EFFECT_EERIE_SPELL, + .effect = EFFECT_HIT, .power = 80, .type = TYPE_PSYCHIC, .accuracy = 100, @@ -18696,6 +18730,10 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .contestCategory = CONTEST_CATEGORY_SMART, .contestComboStarterId = 0, .contestComboMoves = {0}, + .additionalEffects = ADDITIONAL_EFFECTS({ + .moveEffect = MOVE_EFFECT_EERIE_SPELL, + .chance = 100, + }), .battleAnimScript = gBattleAnimMove_EerieSpell, }, @@ -19497,7 +19535,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .description = COMPOUND_STRING( "Hurts foe every turn. Double\n" "damage to Steel and Water."), - .effect = EFFECT_SALT_CURE, + .effect = EFFECT_HIT, .power = 40, .type = TYPE_ROCK, .accuracy = 100, @@ -19506,6 +19544,10 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .priority = 0, .category = DAMAGE_CATEGORY_PHYSICAL, .metronomeBanned = TRUE, + .additionalEffects = ADDITIONAL_EFFECTS({ + .moveEffect = MOVE_EFFECT_SALT_CURE, + .chance = 100, + }), .battleAnimScript = gBattleAnimMove_SaltCure, }, @@ -20402,7 +20444,8 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .moveEffect = MOVE_EFFECT_SP_ATK_PLUS_1, .self = TRUE, .onChargeTurnOnly = TRUE, - }, SHEER_FORCE_HACK), + .sheerForceBoost = SHEER_FORCE_BOOST, + }), .battleAnimScript = gBattleAnimMove_ElectroShot, }, @@ -20676,6 +20719,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .additionalEffects = ADDITIONAL_EFFECTS({ .moveEffect = MOVE_EFFECT_TOXIC, .chance = 50, + .sheerForceBoost = SHEER_FORCE_BOOST, }), .battleAnimScript = gBattleAnimMove_MalignantChain, }, diff --git a/test/battle/ability/sheer_force.c b/test/battle/ability/sheer_force.c index 97dee48a31..e06e56c2c5 100644 --- a/test/battle/ability/sheer_force.c +++ b/test/battle/ability/sheer_force.c @@ -7,64 +7,909 @@ ASSUMPTIONS ASSUME(MoveIsAffectedBySheerForce(MOVE_ELECTRO_SHOT) == TRUE); } -SINGLE_BATTLE_TEST("Sheer Force boosts power, but removes secondary effects of moves", s16 damage) +SINGLE_BATTLE_TEST("Sheer Force doesn't boost Magnitude", s16 damage) { - s32 j; - u32 ability = 0, move = 0; - - for (j = 1; j < MOVES_COUNT; j++) - { - if (MoveIsAffectedBySheerForce(j) - //&& gMovesInfo[j].effect != EFFECT_ORDER_UP - && gMovesInfo[j].effect != EFFECT_AURA_WHEEL - && gMovesInfo[j].effect != EFFECT_PLACEHOLDER) - { - PARAMETRIZE { ability = ABILITY_ANGER_POINT; move = j; } - PARAMETRIZE { ability = ABILITY_SHEER_FORCE; move = j; } - } - } - + u16 ability = 0; + PARAMETRIZE { ability = ABILITY_SHEER_FORCE; } + PARAMETRIZE { ability = ABILITY_ANGER_POINT; } GIVEN { - PLAYER(SPECIES_TAUROS) { Ability(ability); Status1(move == MOVE_SNORE ? STATUS1_SLEEP : STATUS1_NONE); } + PLAYER(SPECIES_TAUROS) { Ability(ability); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { - if (move == MOVE_ALLURING_VOICE || move == MOVE_BURNING_JEALOUSY) // Alluring Voice requires the target to boost stats to have an effect - TURN { MOVE(opponent, MOVE_AGILITY); MOVE(player, move); } - else if (move == MOVE_UPPER_HAND) // Upper Hand requires the target to be using a damaging priority move - TURN { MOVE(opponent, MOVE_QUICK_ATTACK); MOVE(player, move); } - else - TURN { MOVE(player, move); } - if (gMovesInfo[move].effect == EFFECT_TWO_TURNS_ATTACK || gMovesInfo[move].effect == EFFECT_SEMI_INVULNERABLE) { - TURN { SKIP_TURN(player); } - TURN { ; } - } + TURN { MOVE(player, MOVE_MAGNITUDE); } } SCENE { - ANIMATION(ANIM_TYPE_MOVE, move, player); HP_BAR(opponent, captureDamage: &results[i].damage); - if (ability == ABILITY_SHEER_FORCE) { - NONE_OF { - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); - STATUS_ICON(opponent, STATUS1_FREEZE); - STATUS_ICON(opponent, STATUS1_POISON); - STATUS_ICON(opponent, STATUS1_BURN); - STATUS_ICON(opponent, STATUS1_TOXIC_POISON); - STATUS_ICON(opponent, STATUS1_PARALYSIS); - MESSAGE("Wobbuffet is confused!"); - MESSAGE("Wobbuffet flinched and couldn't move!"); - } - // Volt Tackle/Flare Blitz edge case: recoil happens, but target isn't statused - if (gMovesInfo[move].recoil > 0) - { - HP_BAR(player); - MESSAGE("Tauros was damaged by the recoil!"); - } - } } FINALLY { - s32 j; - for (j = 0; j < gBattleTestRunnerState->parametersCount; j+=2) - { - EXPECT_GT(results[j+1].damage, results[j].damage); - } + EXPECT_EQ(results[0].damage, results[1].damage); + EXPECT_NE(results[0].damage, 0); + } +} +SINGLE_BATTLE_TEST("Sheer Force doesn't boost Eruption", s16 damage) +{ + u16 ability = 0; + PARAMETRIZE { ability = ABILITY_SHEER_FORCE; } + PARAMETRIZE { ability = ABILITY_ANGER_POINT; } + GIVEN { + PLAYER(SPECIES_TAUROS) { Ability(ability); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_PRESENT); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + EXPECT_NE(results[0].damage, 0); + } +} +SINGLE_BATTLE_TEST("Sheer Force doesn't boost Water Spout", s16 damage) +{ + u16 ability = 0; + PARAMETRIZE { ability = ABILITY_SHEER_FORCE; } + PARAMETRIZE { ability = ABILITY_ANGER_POINT; } + GIVEN { + PLAYER(SPECIES_TAUROS) { Ability(ability); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_PRESENT); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + EXPECT_NE(results[0].damage, 0); + } +} +SINGLE_BATTLE_TEST("Sheer Force doesn't boost Present", s16 damage) +{ + u16 ability = 0; + PARAMETRIZE { ability = ABILITY_SHEER_FORCE; } + PARAMETRIZE { ability = ABILITY_ANGER_POINT; } + GIVEN { + PLAYER(SPECIES_TAUROS) { Ability(ability); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_PRESENT); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + EXPECT_NE(results[0].damage, 0); + } +} +SINGLE_BATTLE_TEST("Sheer Force doesn't boost Psywave", s16 damage) +{ + u16 ability = 0; + PARAMETRIZE { ability = ABILITY_SHEER_FORCE; } + PARAMETRIZE { ability = ABILITY_ANGER_POINT; } + GIVEN { + PLAYER(SPECIES_TAUROS) { Ability(ability); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_PSYWAVE); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + EXPECT_NE(results[0].damage, 0); + } +} +SINGLE_BATTLE_TEST("Sheer Force doesn't boost Round", s16 damage) +{ + u16 ability = 0; + PARAMETRIZE { ability = ABILITY_SHEER_FORCE; } + PARAMETRIZE { ability = ABILITY_ANGER_POINT; } + GIVEN { + PLAYER(SPECIES_TAUROS) { Ability(ability); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_ROUND); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + EXPECT_NE(results[0].damage, 0); + } +} +SINGLE_BATTLE_TEST("Sheer Force doesn't boost Gyro Ball", s16 damage) +{ + u16 ability = 0; + PARAMETRIZE { ability = ABILITY_SHEER_FORCE; } + PARAMETRIZE { ability = ABILITY_ANGER_POINT; } + GIVEN { + PLAYER(SPECIES_TAUROS) { Ability(ability); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_GYRO_BALL); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + EXPECT_NE(results[0].damage, 0); + } +} +SINGLE_BATTLE_TEST("Sheer Force doesn't boost Electro Ball", s16 damage) +{ + u16 ability = 0; + PARAMETRIZE { ability = ABILITY_SHEER_FORCE; } + PARAMETRIZE { ability = ABILITY_ANGER_POINT; } + GIVEN { + PLAYER(SPECIES_TAUROS) { Ability(ability); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_ELECTRO_BALL); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + EXPECT_NE(results[0].damage, 0); + } +} +SINGLE_BATTLE_TEST("Sheer Force doesn't boost Dragon Energy", s16 damage) +{ + u16 ability = 0; + PARAMETRIZE { ability = ABILITY_SHEER_FORCE; } + PARAMETRIZE { ability = ABILITY_ANGER_POINT; } + GIVEN { + PLAYER(SPECIES_TAUROS) { Ability(ability); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_DRAGON_ENERGY); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + EXPECT_NE(results[0].damage, 0); + } +} +SINGLE_BATTLE_TEST("Sheer Force doesn't boost Belch", s16 damage) +{ + u16 ability = 0; + PARAMETRIZE { ability = ABILITY_SHEER_FORCE; } + PARAMETRIZE { ability = ABILITY_ANGER_POINT; } + GIVEN { + PLAYER(SPECIES_TAUROS) { Ability(ability); HP(1); Item(ITEM_SITRUS_BERRY); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_BELCH); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + EXPECT_NE(results[0].damage, 0); + } +} +SINGLE_BATTLE_TEST("Sheer Force doesn't boost Shell Trap", s16 damage) +{ + u16 ability = 0; + PARAMETRIZE { ability = ABILITY_SHEER_FORCE; } + PARAMETRIZE { ability = ABILITY_ANGER_POINT; } + GIVEN { + PLAYER(SPECIES_TAUROS) { Ability(ability); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SHELL_TRAP); MOVE(opponent, MOVE_TACKLE); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + EXPECT_NE(results[0].damage, 0); + } +} +SINGLE_BATTLE_TEST("Sheer Force doesn't boost Burn Up", s16 damage) +{ + u16 ability = 0; + PARAMETRIZE { ability = ABILITY_SHEER_FORCE; } + PARAMETRIZE { ability = ABILITY_ZEN_MODE; } + GIVEN { + PLAYER(SPECIES_DARMANITAN) { Ability(ability); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_BURN_UP); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + EXPECT_NE(results[0].damage, 0); + } +} +SINGLE_BATTLE_TEST("Sheer Force doesn't boost Double Shock", s16 damage) +{ + u16 move = 0; + PARAMETRIZE { move = MOVE_SKILL_SWAP; } + PARAMETRIZE { move = MOVE_CELEBRATE; } + GIVEN { + PLAYER(SPECIES_PIKACHU); + OPPONENT(SPECIES_TAUROS) { Ability(ABILITY_SHEER_FORCE); }; + } WHEN { + TURN { MOVE(opponent, move); MOVE(player, MOVE_DOUBLE_SHOCK); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + EXPECT_NE(results[0].damage, 0); + } +} +SINGLE_BATTLE_TEST("Sheer Force doesn't boost Steel Roller", s16 damage) +{ + u16 ability = 0; + PARAMETRIZE { ability = ABILITY_SHEER_FORCE; } + PARAMETRIZE { ability = ABILITY_ANGER_POINT; } + GIVEN { + PLAYER(SPECIES_TAUROS) { Ability(ability); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_GRASSY_TERRAIN); MOVE(player, MOVE_STEEL_ROLLER); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + EXPECT_NE(results[0].damage, 0); + } +} +SINGLE_BATTLE_TEST("Sheer Force doesn't boost Synchronoise", s16 damage) +{ + u16 ability = 0; + PARAMETRIZE { ability = ABILITY_SHEER_FORCE; } + PARAMETRIZE { ability = ABILITY_ANGER_POINT; } + GIVEN { + PLAYER(SPECIES_TAUROS) { Ability(ability); HP(1); Item(ITEM_SITRUS_BERRY); } + OPPONENT(SPECIES_CHANSEY); + } WHEN { + TURN { MOVE(player, MOVE_SYNCHRONOISE); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + EXPECT_NE(results[0].damage, 0); + } +} +SINGLE_BATTLE_TEST("Sheer Force doesn't boost Aura Wheel", s16 damage) +{ + u16 move = 0; + PARAMETRIZE { move = MOVE_SKILL_SWAP; } + PARAMETRIZE { move = MOVE_CELEBRATE; } + GIVEN { + PLAYER(SPECIES_MORPEKO); + OPPONENT(SPECIES_TAUROS) { Ability(ABILITY_SHEER_FORCE); }; + } WHEN { + TURN { MOVE(opponent, move); MOVE(player, MOVE_AURA_WHEEL); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + EXPECT_NE(results[0].damage, 0); + } +} +SINGLE_BATTLE_TEST("Sheer Force doesn't boost Hyperspace Fury", s16 damage) +{ + u16 move = 0; + PARAMETRIZE { move = MOVE_SKILL_SWAP; } + PARAMETRIZE { move = MOVE_CELEBRATE; } + GIVEN { + PLAYER(SPECIES_HOOPA_UNBOUND); + OPPONENT(SPECIES_TAUROS) { Ability(ABILITY_SHEER_FORCE); }; + } WHEN { + TURN { MOVE(opponent, move); MOVE(player, MOVE_HYPERSPACE_FURY); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + EXPECT_NE(results[0].damage, 0); + } +} +SINGLE_BATTLE_TEST("Sheer Force doesn't boost Bolt Beak", s16 damage) +{ + u16 ability = 0; + PARAMETRIZE { ability = ABILITY_SHEER_FORCE; } + PARAMETRIZE { ability = ABILITY_ANGER_POINT; } + GIVEN { + PLAYER(SPECIES_TAUROS) { Ability(ability); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_BOLT_BEAK); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + EXPECT_NE(results[0].damage, 0); + } +} +SINGLE_BATTLE_TEST("Sheer Force doesn't boost Fishious Rend", s16 damage) +{ + u16 ability = 0; + PARAMETRIZE { ability = ABILITY_SHEER_FORCE; } + PARAMETRIZE { ability = ABILITY_ANGER_POINT; } + GIVEN { + PLAYER(SPECIES_TAUROS) { Ability(ability); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_FISHIOUS_REND); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + EXPECT_NE(results[0].damage, 0); + } +} +SINGLE_BATTLE_TEST("Sheer Force doesn't boost Comeuppance", s16 damage) +{ + u16 ability = 0; + PARAMETRIZE { ability = ABILITY_SHEER_FORCE; } + PARAMETRIZE { ability = ABILITY_ANGER_POINT; } + GIVEN { + PLAYER(SPECIES_TAUROS) { Ability(ability); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE); MOVE(player, MOVE_COMEUPPANCE); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + EXPECT_NE(results[0].damage, 0); + } +} +SINGLE_BATTLE_TEST("Sheer Force doesn't boost Comeuppance", s16 damage) +{ + u16 ability = 0; + PARAMETRIZE { ability = ABILITY_SHEER_FORCE; } + PARAMETRIZE { ability = ABILITY_ANGER_POINT; } + GIVEN { + PLAYER(SPECIES_TAUROS) { Ability(ability); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_PAYBACK); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + EXPECT_NE(results[0].damage, 0); + } +} + +static inline bool32 IgnoreMoveForSheerForceBoost(u32 move) +{ + switch (move) { + case MOVE_PSYWAVE: // Just skip Psywve + case MOVE_PRESENT: // And Present... + case MOVE_MAGNITUDE: // And Magnitude... + case MOVE_ERUPTION: // And Eruption... + case MOVE_WATER_SPOUT: + case MOVE_GYRO_BALL: + case MOVE_SYNCHRONOISE: + case MOVE_ELECTRO_BALL: + case MOVE_ROUND: + case MOVE_BELCH: + case MOVE_HYPERSPACE_FURY: + case MOVE_BURN_UP: + case MOVE_SHELL_TRAP: + case MOVE_BOLT_BEAK: + case MOVE_FISHIOUS_REND: + case MOVE_AURA_WHEEL: + case MOVE_STEEL_ROLLER: + case MOVE_DRAGON_ENERGY: + case MOVE_DOUBLE_SHOCK: + case MOVE_COMEUPPANCE: + case MOVE_UPPER_HAND: // Bugged? + case MOVE_GLITZY_GLOW: // Light Screen Move Effect seems to be bugged + case MOVE_PAYBACK: + return TRUE; + } + return FALSE; +} + +static inline bool32 IsMoveSheerForceBoosted(u32 move) +{ + switch (move) { + case MOVE_AIR_SLASH: + case MOVE_ANCIENT_POWER: + case MOVE_ASTONISH: + case MOVE_BITE: + case MOVE_BLIZZARD: + case MOVE_BODY_SLAM: + case MOVE_BOUNCE: + case MOVE_BREAKING_SWIPE: + case MOVE_BUBBLE: + case MOVE_BUBBLE_BEAM: + case MOVE_BUG_BUZZ: + case MOVE_BULLDOZE: + case MOVE_BURNING_JEALOUSY: + case MOVE_CHARGE_BEAM: + case MOVE_CHILLING_WATER: + case MOVE_CONFUSION: + case MOVE_CRUNCH: + case MOVE_CRUSH_CLAW: + case MOVE_DARK_PULSE: + case MOVE_DRAGON_RUSH: + case MOVE_DRAGON_BREATH: + case MOVE_DYNAMIC_PUNCH: + case MOVE_EARTH_POWER: + case MOVE_EMBER: + case MOVE_ESPER_WING: + case MOVE_EXTRASENSORY: + case MOVE_FAKE_OUT: + case MOVE_FIRE_BLAST: + case MOVE_FIRE_FANG: + case MOVE_FIRE_PUNCH: + case MOVE_FLAME_CHARGE: + case MOVE_FLAME_WHEEL: + case MOVE_FLAMETHROWER: + case MOVE_FLARE_BLITZ: + case MOVE_FLASH_CANNON: + case MOVE_FOCUS_BLAST: + case MOVE_FORCE_PALM: + case MOVE_GUNK_SHOT: + case MOVE_HEADBUTT: + case MOVE_HEAT_WAVE: + case MOVE_HURRICANE: + case MOVE_ICE_BEAM: + case MOVE_ICE_FANG: + case MOVE_ICE_PUNCH: + case MOVE_ICICLE_CRASH: + case MOVE_ICY_WIND: + case MOVE_IRON_HEAD: + case MOVE_IRON_TAIL: + case MOVE_LAVA_PLUME: + case MOVE_LIQUIDATION: + case MOVE_LOW_SWEEP: + case MOVE_METAL_CLAW: + case MOVE_MUD_BOMB: + case MOVE_MUDDY_WATER: + case MOVE_MUD_SHOT: + case MOVE_MUD_SLAP: + case MOVE_MYSTICAL_FIRE: + case MOVE_PLAY_ROUGH: + case MOVE_POISON_FANG: + case MOVE_POISON_JAB: + case MOVE_POISON_STING: + case MOVE_POISON_TAIL: + case MOVE_POUNCE: + case MOVE_POWER_UP_PUNCH: + case MOVE_PSYBEAM: + case MOVE_PSYCHIC: + case MOVE_RAZOR_SHELL: + case MOVE_ROCK_CLIMB: + case MOVE_ROCK_SLIDE: + case MOVE_ROCK_SMASH: + case MOVE_ROCK_TOMB: + case MOVE_SANDSEAR_STORM: + case MOVE_SCALD: + case MOVE_SCORCHING_SANDS: + case MOVE_SECRET_POWER: + case MOVE_SHADOW_BALL: + case MOVE_SIGNAL_BEAM: + case MOVE_SKY_ATTACK: + case MOVE_SLUDGE_BOMB: + case MOVE_SLUDGE_WAVE: + case MOVE_SNARL: + case MOVE_SNORE: + case MOVE_STEEL_WING: + case MOVE_STOMP: + case MOVE_STONE_AXE: + case MOVE_STRUGGLE_BUG: + case MOVE_THROAT_CHOP: + case MOVE_THUNDER: + case MOVE_THUNDER_FANG: + case MOVE_THUNDERBOLT: + case MOVE_THUNDER_PUNCH: + case MOVE_TRAILBLAZE: + case MOVE_TWISTER: + case MOVE_UPPER_HAND: + case MOVE_WATER_PULSE: + case MOVE_WATERFALL: + case MOVE_ZAP_CANNON: + case MOVE_ZEN_HEADBUTT: + case MOVE_ACID: + case MOVE_ACID_SPRAY: + case MOVE_ALLURING_VOICE: + case MOVE_ANCHOR_SHOT: + case MOVE_APPLE_ACID: + case MOVE_AQUA_STEP: + case MOVE_AURA_WHEEL: + case MOVE_AURORA_BEAM: + case MOVE_AXE_KICK: + case MOVE_BARB_BARRAGE: + case MOVE_BITTER_MALICE: + case MOVE_BLAZE_KICK: + case MOVE_BLAZING_TORQUE: + case MOVE_BLEAKWIND_STORM: + case MOVE_BLUE_FLARE: + case MOVE_BOLT_STRIKE: + case MOVE_BONE_CLUB: + case MOVE_CEASELESS_EDGE: + case MOVE_CHATTER: + case MOVE_CLANGOROUS_SOULBLAZE: + case MOVE_COMBAT_TORQUE: + case MOVE_CONSTRICT: + case MOVE_CROSS_POISON: + case MOVE_DIAMOND_STORM: + case MOVE_DIRE_CLAW: + case MOVE_DISCHARGE: + case MOVE_DIZZY_PUNCH: + case MOVE_DOUBLE_IRON_BASH: + case MOVE_DRUM_BEATING: + case MOVE_EERIE_SPELL: + case MOVE_ELECTROWEB: + case MOVE_ENERGY_BALL: + case MOVE_FIERY_DANCE: + case MOVE_FIERY_WRATH: + case MOVE_FREEZING_GLARE: + case MOVE_FIRE_LASH: + case MOVE_FREEZE_DRY: + case MOVE_FREEZE_SHOCK: + case MOVE_GENESIS_SUPERNOVA: + case MOVE_GLACIATE: + case MOVE_GRAV_APPLE: + case MOVE_HEART_STAMP: + case MOVE_HYPER_FANG: + case MOVE_ICE_BURN: + case MOVE_INFERNAL_PARADE: + case MOVE_INFERNO: + case MOVE_LEAF_TORNADO: + case MOVE_LICK: + case MOVE_LUMINA_CRASH: + case MOVE_LUNGE: + case MOVE_LUSTER_PURGE: + case MOVE_MAGICAL_TORQUE: + case MOVE_MALIGNANT_CHAIN: + case MOVE_MATCHA_GOTCHA: + case MOVE_METEOR_MASH: + case MOVE_MIRROR_SHOT: + case MOVE_MIST_BALL: + case MOVE_MOONBLAST: + case MOVE_MORTAL_SPIN: + case MOVE_MOUNTAIN_GALE: + case MOVE_MYSTICAL_POWER: + case MOVE_NEEDLE_ARM: + case MOVE_NIGHT_DAZE: + case MOVE_NOXIOUS_TORQUE: + case MOVE_NUZZLE: + case MOVE_OCTAZOOKA: + case MOVE_OMINOUS_WIND: + case MOVE_ORDER_UP: + case MOVE_POWDER_SNOW: + case MOVE_PSYSHIELD_BASH: + case MOVE_PYRO_BALL: + case MOVE_RAPID_SPIN: + case MOVE_RELIC_SONG: + case MOVE_ROLLING_KICK: + case MOVE_SACRED_FIRE: + case MOVE_SALT_CURE: + case MOVE_SEARING_SHOT: + case MOVE_SEED_FLARE: + case MOVE_SHADOW_BONE: + case MOVE_SHELL_SIDE_ARM: + case MOVE_SILVER_WIND: + case MOVE_SKITTER_SMACK: + case MOVE_SLUDGE: + case MOVE_SMOG: + case MOVE_SPARK: + case MOVE_SPARKLING_ARIA: + case MOVE_SPIRIT_BREAK: + case MOVE_SPIRIT_SHACKLE: + case MOVE_SPLISHY_SPLASH: + case MOVE_SPRINGTIDE_STORM: + case MOVE_STEAM_ERUPTION: + case MOVE_STEAMROLLER: + case MOVE_STOKED_SPARKSURFER: + case MOVE_STRANGE_STEAM: + case MOVE_SYRUP_BOMB: + case MOVE_THUNDER_SHOCK: + case MOVE_THUNDEROUS_KICK: + case MOVE_TORCH_SONG: + case MOVE_TRI_ATTACK: + case MOVE_TRIPLE_ARROWS: + case MOVE_TROP_KICK: + case MOVE_TWINEEDLE: + case MOVE_VOLT_TACKLE: + case MOVE_WICKED_TORQUE: + case MOVE_WILDBOLT_STORM: + case MOVE_ZING_ZAP: + case MOVE_ELECTRO_SHOT: + case MOVE_PSYCHIC_NOISE: + return TRUE; + } + return FALSE; +} + +// Test split into four parts that handles ~1/4 of all moves each +DOUBLE_BATTLE_TEST("Sheer Force only boosts the damage of moves it's supposed to boost 1") +{ + s16 damage1, damage2; + u32 move = 0; + for (u32 j = 1; j < MOVES_COUNT; j += 4) + if (gMovesInfo[j].category != DAMAGE_CATEGORY_STATUS && !IgnoreMoveForSheerForceBoost(j)) + PARAMETRIZE { move = j; } + GIVEN { + PLAYER(SPECIES_STEELIX) { Ability(ABILITY_SHEER_FORCE); Item(ITEM_BLUK_BERRY); } + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_TELEPATHY); Level(100); Item(ITEM_BLUK_BERRY); } + OPPONENT(SPECIES_STEELIX) { Ability(ABILITY_STURDY); Item(ITEM_BLUK_BERRY); } + OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_TELEPATHY); Level(100); Item(ITEM_BLUK_BERRY); } + } WHEN { + if (move == MOVE_ALLURING_VOICE || move == MOVE_BURNING_JEALOUSY) // Alluring Voice requires the target to boost stats to have an effect + TURN { MOVE(opponentRight, MOVE_AGILITY); MOVE(playerRight, MOVE_AGILITY); MOVE(playerLeft, move, target: opponentRight); MOVE(opponentLeft, move, target: playerRight); } + else if (move == MOVE_UPPER_HAND) // Upper Hand requires the target to be using a damaging priority move + TURN { MOVE(opponentRight, MOVE_QUICK_ATTACK, target: playerLeft); MOVE(playerRight, move, target: opponentLeft); MOVE(playerLeft, move, target: opponentRight); MOVE(opponentLeft, move, target: playerRight); } + else if (move == MOVE_COUNTER || move == MOVE_UPPER_HAND) + TURN { MOVE(opponentRight, MOVE_QUICK_ATTACK, target: playerLeft); + MOVE(playerRight, MOVE_QUICK_ATTACK, target: opponentLeft); + MOVE(playerLeft, move, target: opponentRight); + MOVE(opponentLeft, move, target: playerRight); } + else if (move == MOVE_MIRROR_COAT || move == MOVE_METAL_BURST) + TURN { MOVE(opponentRight, MOVE_WATER_GUN, target: playerLeft); MOVE(playerRight, MOVE_WATER_GUN, target: opponentLeft); MOVE(playerLeft, move, target: opponentRight); MOVE(opponentLeft, move, target: playerRight); } + else if (move == MOVE_SUCKER_PUNCH || move == MOVE_THUNDERCLAP) + TURN { MOVE(opponentRight, MOVE_TACKLE, target: playerLeft); MOVE(playerRight, MOVE_TACKLE, target: opponentLeft); MOVE(playerLeft, move, target: opponentRight); MOVE(opponentLeft, move, target: playerRight); } + else if (move == MOVE_DREAM_EATER) + { + TURN { MOVE(playerLeft, MOVE_HYPNOSIS, target: opponentRight); MOVE(opponentLeft, MOVE_HYPNOSIS, target: playerRight); } + TURN { MOVE(playerLeft, move, target: opponentRight); MOVE(opponentLeft, move, target: playerRight); } + } + else if (move == MOVE_SNORE) + { + TURN { MOVE(opponentRight, MOVE_HYPNOSIS, target: playerLeft); MOVE(playerRight, MOVE_HYPNOSIS, target: opponentLeft); MOVE(playerLeft, move, target: opponentRight); MOVE(opponentLeft, move, target: playerRight); } + } + else if (move == MOVE_SPIT_UP || move == MOVE_LAST_RESORT) + { + TURN { MOVE(playerLeft, MOVE_STOCKPILE); MOVE(opponentLeft, MOVE_STOCKPILE); } + TURN { MOVE(playerLeft, move, target: opponentRight); MOVE(opponentLeft, move, target: playerRight); } + } + else + TURN { MOVE(playerLeft, move, target: opponentRight); MOVE(opponentLeft, move, target: playerRight); } + if (gMovesInfo[move].effect == EFFECT_TWO_TURNS_ATTACK || gMovesInfo[move].effect == EFFECT_SEMI_INVULNERABLE || gMovesInfo[move].effect == EFFECT_SOLAR_BEAM || gMovesInfo[move].effect == EFFECT_SKY_DROP) + { + TURN { SKIP_TURN(playerLeft); SKIP_TURN(opponentLeft); } + TURN { ; } + } + if (gMovesInfo[move].effect == EFFECT_FUTURE_SIGHT) + { + TURN { ; } + TURN { ; } + } + if (gMovesInfo[move].effect == EFFECT_BIDE) + { + TURN { MOVE(opponentRight, MOVE_WATER_GUN, target: playerLeft); MOVE(playerRight, MOVE_WATER_GUN, target: opponentLeft); SKIP_TURN(playerLeft); SKIP_TURN(opponentLeft); } + TURN { SKIP_TURN(playerLeft); SKIP_TURN(opponentLeft); } + } + } SCENE { + if (gMovesInfo[move].effect != EFFECT_FUTURE_SIGHT) + { + HP_BAR(opponentRight, captureDamage: &damage1); + HP_BAR(playerRight, captureDamage: &damage2); + } + else + { + HP_BAR(playerRight, captureDamage: &damage2); + HP_BAR(opponentRight, captureDamage: &damage1); + } + } THEN { + if (IsMoveSheerForceBoosted(move)) + EXPECT_GT(damage1, damage2); + else + EXPECT_EQ(damage2, damage1); + } +} +DOUBLE_BATTLE_TEST("Sheer Force only boosts the damage of moves it's supposed to boost 2") +{ + s16 damage1, damage2; + u32 move = 0; + for (u32 j = 2; j < MOVES_COUNT; j += 4) + if (gMovesInfo[j].category != DAMAGE_CATEGORY_STATUS && !IgnoreMoveForSheerForceBoost(j)) + PARAMETRIZE { move = j; } + GIVEN { + PLAYER(SPECIES_STEELIX) { Ability(ABILITY_SHEER_FORCE); Item(ITEM_BLUK_BERRY); } + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_TELEPATHY); Level(100); Item(ITEM_BLUK_BERRY); } + OPPONENT(SPECIES_STEELIX) { Ability(ABILITY_STURDY); Item(ITEM_BLUK_BERRY); } + OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_TELEPATHY); Level(100); Item(ITEM_BLUK_BERRY); } + } WHEN { + if (move == MOVE_ALLURING_VOICE || move == MOVE_BURNING_JEALOUSY) // Alluring Voice requires the target to boost stats to have an effect + TURN { MOVE(opponentRight, MOVE_AGILITY); MOVE(playerRight, MOVE_AGILITY); MOVE(playerLeft, move, target: opponentRight); MOVE(opponentLeft, move, target: playerRight); } + else if (move == MOVE_UPPER_HAND) // Upper Hand requires the target to be using a damaging priority move + TURN { MOVE(opponentRight, MOVE_QUICK_ATTACK, target: playerLeft); MOVE(playerRight, move, target: opponentLeft); MOVE(playerLeft, move, target: opponentRight); MOVE(opponentLeft, move, target: playerRight); } + else if (move == MOVE_COUNTER || move == MOVE_UPPER_HAND) + TURN { MOVE(opponentRight, MOVE_QUICK_ATTACK, target: playerLeft); + MOVE(playerRight, MOVE_QUICK_ATTACK, target: opponentLeft); + MOVE(playerLeft, move, target: opponentRight); + MOVE(opponentLeft, move, target: playerRight); } + else if (move == MOVE_MIRROR_COAT || move == MOVE_METAL_BURST) + TURN { MOVE(opponentRight, MOVE_WATER_GUN, target: playerLeft); MOVE(playerRight, MOVE_WATER_GUN, target: opponentLeft); MOVE(playerLeft, move, target: opponentRight); MOVE(opponentLeft, move, target: playerRight); } + else if (move == MOVE_SUCKER_PUNCH || move == MOVE_THUNDERCLAP) + TURN { MOVE(opponentRight, MOVE_TACKLE, target: playerLeft); MOVE(playerRight, MOVE_TACKLE, target: opponentLeft); MOVE(playerLeft, move, target: opponentRight); MOVE(opponentLeft, move, target: playerRight); } + else if (move == MOVE_DREAM_EATER) + { + TURN { MOVE(playerLeft, MOVE_HYPNOSIS, target: opponentRight); MOVE(opponentLeft, MOVE_HYPNOSIS, target: playerRight); } + TURN { MOVE(playerLeft, move, target: opponentRight); MOVE(opponentLeft, move, target: playerRight); } + } + else if (move == MOVE_SNORE) + { + TURN { MOVE(opponentRight, MOVE_HYPNOSIS, target: playerLeft); MOVE(playerRight, MOVE_HYPNOSIS, target: opponentLeft); MOVE(playerLeft, move, target: opponentRight); MOVE(opponentLeft, move, target: playerRight); } + } + else if (move == MOVE_SPIT_UP || move == MOVE_LAST_RESORT) + { + TURN { MOVE(playerLeft, MOVE_STOCKPILE); MOVE(opponentLeft, MOVE_STOCKPILE); } + TURN { MOVE(playerLeft, move, target: opponentRight); MOVE(opponentLeft, move, target: playerRight); } + } + else + TURN { MOVE(playerLeft, move, target: opponentRight); MOVE(opponentLeft, move, target: playerRight); } + if (gMovesInfo[move].effect == EFFECT_TWO_TURNS_ATTACK || gMovesInfo[move].effect == EFFECT_SEMI_INVULNERABLE || gMovesInfo[move].effect == EFFECT_SOLAR_BEAM || gMovesInfo[move].effect == EFFECT_SKY_DROP) + { + TURN { SKIP_TURN(playerLeft); SKIP_TURN(opponentLeft); } + TURN { ; } + } + if (gMovesInfo[move].effect == EFFECT_FUTURE_SIGHT) + { + TURN { ; } + TURN { ; } + } + if (gMovesInfo[move].effect == EFFECT_BIDE) + { + TURN { MOVE(opponentRight, MOVE_WATER_GUN, target: playerLeft); MOVE(playerRight, MOVE_WATER_GUN, target: opponentLeft); SKIP_TURN(playerLeft); SKIP_TURN(opponentLeft); } + TURN { SKIP_TURN(playerLeft); SKIP_TURN(opponentLeft); } + } + } SCENE { + if (gMovesInfo[move].effect != EFFECT_FUTURE_SIGHT) + { + HP_BAR(opponentRight, captureDamage: &damage1); + HP_BAR(playerRight, captureDamage: &damage2); + } + else + { + HP_BAR(playerRight, captureDamage: &damage2); + HP_BAR(opponentRight, captureDamage: &damage1); + } + } THEN { + if (IsMoveSheerForceBoosted(move)) + EXPECT_GT(damage1, damage2); + else + EXPECT_EQ(damage2, damage1); + } +} +DOUBLE_BATTLE_TEST("Sheer Force only boosts the damage of moves it's supposed to boost 3") +{ + s16 damage1, damage2; + u32 move = 0; + for (u32 j = 3; j < MOVES_COUNT; j += 4) + if (gMovesInfo[j].category != DAMAGE_CATEGORY_STATUS && !IgnoreMoveForSheerForceBoost(j)) + PARAMETRIZE { move = j; } + GIVEN { + PLAYER(SPECIES_STEELIX) { Ability(ABILITY_SHEER_FORCE); Item(ITEM_BLUK_BERRY); } + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_TELEPATHY); Level(100); Item(ITEM_BLUK_BERRY); } + OPPONENT(SPECIES_STEELIX) { Ability(ABILITY_STURDY); Item(ITEM_BLUK_BERRY); } + OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_TELEPATHY); Level(100); Item(ITEM_BLUK_BERRY); } + } WHEN { + if (move == MOVE_ALLURING_VOICE || move == MOVE_BURNING_JEALOUSY) // Alluring Voice requires the target to boost stats to have an effect + TURN { MOVE(opponentRight, MOVE_AGILITY); MOVE(playerRight, MOVE_AGILITY); MOVE(playerLeft, move, target: opponentRight); MOVE(opponentLeft, move, target: playerRight); } + else if (move == MOVE_UPPER_HAND) // Upper Hand requires the target to be using a damaging priority move + TURN { MOVE(opponentRight, MOVE_QUICK_ATTACK, target: playerLeft); MOVE(playerRight, move, target: opponentLeft); MOVE(playerLeft, move, target: opponentRight); MOVE(opponentLeft, move, target: playerRight); } + else if (move == MOVE_COUNTER || move == MOVE_UPPER_HAND) + TURN { MOVE(opponentRight, MOVE_QUICK_ATTACK, target: playerLeft); + MOVE(playerRight, MOVE_QUICK_ATTACK, target: opponentLeft); + MOVE(playerLeft, move, target: opponentRight); + MOVE(opponentLeft, move, target: playerRight); } + else if (move == MOVE_MIRROR_COAT || move == MOVE_METAL_BURST) + TURN { MOVE(opponentRight, MOVE_WATER_GUN, target: playerLeft); MOVE(playerRight, MOVE_WATER_GUN, target: opponentLeft); MOVE(playerLeft, move, target: opponentRight); MOVE(opponentLeft, move, target: playerRight); } + else if (move == MOVE_SUCKER_PUNCH || move == MOVE_THUNDERCLAP) + TURN { MOVE(opponentRight, MOVE_TACKLE, target: playerLeft); MOVE(playerRight, MOVE_TACKLE, target: opponentLeft); MOVE(playerLeft, move, target: opponentRight); MOVE(opponentLeft, move, target: playerRight); } + else if (move == MOVE_DREAM_EATER) + { + TURN { MOVE(playerLeft, MOVE_HYPNOSIS, target: opponentRight); MOVE(opponentLeft, MOVE_HYPNOSIS, target: playerRight); } + TURN { MOVE(playerLeft, move, target: opponentRight); MOVE(opponentLeft, move, target: playerRight); } + } + else if (move == MOVE_SNORE) + { + TURN { MOVE(opponentRight, MOVE_HYPNOSIS, target: playerLeft); MOVE(playerRight, MOVE_HYPNOSIS, target: opponentLeft); MOVE(playerLeft, move, target: opponentRight); MOVE(opponentLeft, move, target: playerRight); } + } + else if (move == MOVE_SPIT_UP || move == MOVE_LAST_RESORT) + { + TURN { MOVE(playerLeft, MOVE_STOCKPILE); MOVE(opponentLeft, MOVE_STOCKPILE); } + TURN { MOVE(playerLeft, move, target: opponentRight); MOVE(opponentLeft, move, target: playerRight); } + } + else + TURN { MOVE(playerLeft, move, target: opponentRight); MOVE(opponentLeft, move, target: playerRight); } + if (gMovesInfo[move].effect == EFFECT_TWO_TURNS_ATTACK || gMovesInfo[move].effect == EFFECT_SEMI_INVULNERABLE || gMovesInfo[move].effect == EFFECT_SOLAR_BEAM || gMovesInfo[move].effect == EFFECT_SKY_DROP) + { + TURN { SKIP_TURN(playerLeft); SKIP_TURN(opponentLeft); } + TURN { ; } + } + if (gMovesInfo[move].effect == EFFECT_FUTURE_SIGHT) + { + TURN { ; } + TURN { ; } + } + if (gMovesInfo[move].effect == EFFECT_BIDE) + { + TURN { MOVE(opponentRight, MOVE_WATER_GUN, target: playerLeft); MOVE(playerRight, MOVE_WATER_GUN, target: opponentLeft); SKIP_TURN(playerLeft); SKIP_TURN(opponentLeft); } + TURN { SKIP_TURN(playerLeft); SKIP_TURN(opponentLeft); } + } + } SCENE { + if (gMovesInfo[move].effect != EFFECT_FUTURE_SIGHT) + { + HP_BAR(opponentRight, captureDamage: &damage1); + HP_BAR(playerRight, captureDamage: &damage2); + } + else + { + HP_BAR(playerRight, captureDamage: &damage2); + HP_BAR(opponentRight, captureDamage: &damage1); + } + } THEN { + if (IsMoveSheerForceBoosted(move)) + EXPECT_GT(damage1, damage2); + else + EXPECT_EQ(damage2, damage1); + } +} +DOUBLE_BATTLE_TEST("Sheer Force only boosts the damage of moves it's supposed to boost 4") +{ + s16 damage1, damage2; + u32 move = 0; + for (u32 j = 4; j < MOVES_COUNT; j += 4) + { + if (gMovesInfo[j].category != DAMAGE_CATEGORY_STATUS && !IgnoreMoveForSheerForceBoost(j)) + PARAMETRIZE { move = j; } + } + GIVEN { + PLAYER(SPECIES_STEELIX) { Ability(ABILITY_SHEER_FORCE); Item(ITEM_BLUK_BERRY); } + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_TELEPATHY); Level(100); Item(ITEM_BLUK_BERRY); } + OPPONENT(SPECIES_STEELIX) { Ability(ABILITY_STURDY); Item(ITEM_BLUK_BERRY); } + OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_TELEPATHY); Level(100); Item(ITEM_BLUK_BERRY); } + } WHEN { + if (move == MOVE_ALLURING_VOICE || move == MOVE_BURNING_JEALOUSY) // Alluring Voice requires the target to boost stats to have an effect + TURN { MOVE(opponentRight, MOVE_AGILITY); MOVE(playerRight, MOVE_AGILITY); MOVE(playerLeft, move, target: opponentRight); MOVE(opponentLeft, move, target: playerRight); } + else if (move == MOVE_UPPER_HAND) // Upper Hand requires the target to be using a damaging priority move + TURN { MOVE(opponentRight, MOVE_QUICK_ATTACK, target: playerLeft); MOVE(playerRight, move, target: opponentLeft); MOVE(playerLeft, move, target: opponentRight); MOVE(opponentLeft, move, target: playerRight); } + else if (move == MOVE_COUNTER || move == MOVE_UPPER_HAND) + TURN { MOVE(opponentRight, MOVE_QUICK_ATTACK, target: playerLeft); + MOVE(playerRight, MOVE_QUICK_ATTACK, target: opponentLeft); + MOVE(playerLeft, move, target: opponentRight); + MOVE(opponentLeft, move, target: playerRight); } + else if (move == MOVE_MIRROR_COAT || move == MOVE_METAL_BURST) + TURN { MOVE(opponentRight, MOVE_WATER_GUN, target: playerLeft); MOVE(playerRight, MOVE_WATER_GUN, target: opponentLeft); MOVE(playerLeft, move, target: opponentRight); MOVE(opponentLeft, move, target: playerRight); } + else if (move == MOVE_SUCKER_PUNCH || move == MOVE_THUNDERCLAP) + TURN { MOVE(opponentRight, MOVE_TACKLE, target: playerLeft); MOVE(playerRight, MOVE_TACKLE, target: opponentLeft); MOVE(playerLeft, move, target: opponentRight); MOVE(opponentLeft, move, target: playerRight); } + else if (move == MOVE_DREAM_EATER) + { + TURN { MOVE(playerLeft, MOVE_HYPNOSIS, target: opponentRight); MOVE(opponentLeft, MOVE_HYPNOSIS, target: playerRight); } + TURN { MOVE(playerLeft, move, target: opponentRight); MOVE(opponentLeft, move, target: playerRight); } + } + else if (move == MOVE_SNORE) + { + TURN { MOVE(opponentRight, MOVE_HYPNOSIS, target: playerLeft); MOVE(playerRight, MOVE_HYPNOSIS, target: opponentLeft); MOVE(playerLeft, move, target: opponentRight); MOVE(opponentLeft, move, target: playerRight); } + } + else if (move == MOVE_SPIT_UP || move == MOVE_LAST_RESORT) + { + TURN { MOVE(playerLeft, MOVE_STOCKPILE); MOVE(opponentLeft, MOVE_STOCKPILE); } + TURN { MOVE(playerLeft, move, target: opponentRight); MOVE(opponentLeft, move, target: playerRight); } + } + else + TURN { MOVE(playerLeft, move, target: opponentRight); MOVE(opponentLeft, move, target: playerRight); } + if (gMovesInfo[move].effect == EFFECT_TWO_TURNS_ATTACK || gMovesInfo[move].effect == EFFECT_SEMI_INVULNERABLE || gMovesInfo[move].effect == EFFECT_SOLAR_BEAM || gMovesInfo[move].effect == EFFECT_SKY_DROP) + { + TURN { SKIP_TURN(playerLeft); SKIP_TURN(opponentLeft); } + TURN { ; } + } + if (gMovesInfo[move].effect == EFFECT_FUTURE_SIGHT) + { + TURN { ; } + TURN { ; } + } + if (gMovesInfo[move].effect == EFFECT_BIDE) + { + TURN { MOVE(opponentRight, MOVE_WATER_GUN, target: playerLeft); MOVE(playerRight, MOVE_WATER_GUN, target: opponentLeft); SKIP_TURN(playerLeft); SKIP_TURN(opponentLeft); } + TURN { SKIP_TURN(playerLeft); SKIP_TURN(opponentLeft); } + } + } SCENE { + if (gMovesInfo[move].effect != EFFECT_FUTURE_SIGHT) + { + HP_BAR(opponentRight, captureDamage: &damage1); + HP_BAR(playerRight, captureDamage: &damage2); + } + else + { + HP_BAR(playerRight, captureDamage: &damage2); + HP_BAR(opponentRight, captureDamage: &damage1); + } + } THEN { + if (IsMoveSheerForceBoosted(move)) + EXPECT_GT(damage1, damage2); + else + EXPECT_EQ(damage2, damage1); } } diff --git a/test/battle/move_effect/salt_cure.c b/test/battle/move_effect/salt_cure.c index 495a7e8c80..afe811da50 100644 --- a/test/battle/move_effect/salt_cure.c +++ b/test/battle/move_effect/salt_cure.c @@ -3,7 +3,7 @@ ASSUMPTIONS { - ASSUME(gMovesInfo[MOVE_SALT_CURE].effect == EFFECT_SALT_CURE); + ASSUME(MoveHasAdditionalEffect(MOVE_SALT_CURE, MOVE_EFFECT_SALT_CURE) == TRUE); } SINGLE_BATTLE_TEST("Salt Cure inflicts 1/8 of the target's maximum HP as damage per turn") diff --git a/test/battle/move_effect_secondary/haze.c b/test/battle/move_effect_secondary/haze.c new file mode 100644 index 0000000000..c3831f0768 --- /dev/null +++ b/test/battle/move_effect_secondary/haze.c @@ -0,0 +1,32 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(MoveHasAdditionalEffect(MOVE_FREEZY_FROST, MOVE_EFFECT_HAZE) == TRUE); +} + +SINGLE_BATTLE_TEST("Freeze Frost restores stat changes when it was succesful") +{ + bool32 moveSuccess; + PARAMETRIZE { moveSuccess = FALSE; } + PARAMETRIZE { moveSuccess = TRUE; } + + GIVEN { + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_FREEZY_FROST, hit: moveSuccess); } + } SCENE { + if (moveSuccess == TRUE) + { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FREEZY_FROST, player); + MESSAGE("All stat changes were eliminated!"); + } else { + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FREEZY_FROST, player); + MESSAGE("All stat changes were eliminated!"); + } + } + } +} diff --git a/test/battle/move_effect/plasma_fists.c b/test/battle/move_effect_secondary/ion_deluge.c similarity index 80% rename from test/battle/move_effect/plasma_fists.c rename to test/battle/move_effect_secondary/ion_deluge.c index 93c8869026..efc66903bc 100644 --- a/test/battle/move_effect/plasma_fists.c +++ b/test/battle/move_effect_secondary/ion_deluge.c @@ -3,7 +3,7 @@ ASSUMPTIONS { - ASSUME(gMovesInfo[MOVE_PLASMA_FISTS].effect == EFFECT_PLASMA_FISTS); + ASSUME(MoveHasAdditionalEffect(MOVE_PLASMA_FISTS, MOVE_EFFECT_ION_DELUGE) == TRUE); } SINGLE_BATTLE_TEST("Ion Duldge turns normal moves into electric for the remainder of the current turn") @@ -47,6 +47,26 @@ SINGLE_BATTLE_TEST("Plasma Fists turns normal moves into electric for the remain } } +SINGLE_BATTLE_TEST("Plasma Fists does not set up Ion Deluge if it does not connect") +{ + GIVEN { + ASSUME(gSpeciesInfo[SPECIES_PHANPY].types[0] == TYPE_GROUND || gSpeciesInfo[SPECIES_PHANPY].types[1] == TYPE_GROUND); + PLAYER(SPECIES_KRABBY); + OPPONENT(SPECIES_PHANPY); + } WHEN { + TURN { MOVE(player, MOVE_PLASMA_FISTS); MOVE(opponent, MOVE_TACKLE); } + } SCENE { + MESSAGE("Krabby used Plasma Fists!"); + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PLASMA_FISTS, player); + MESSAGE("A deluge of ions showers the battlefield!"); + } + MESSAGE("The opposing Phanpy used Tackle!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + NOT MESSAGE("It's super effective!"); + } +} + SINGLE_BATTLE_TEST("Plasma Fists type-changing effect does not override Pixilate") { GIVEN { diff --git a/test/battle/move_effect_secondary/leech_seed.c b/test/battle/move_effect_secondary/leech_seed.c new file mode 100644 index 0000000000..c5a8db57cc --- /dev/null +++ b/test/battle/move_effect_secondary/leech_seed.c @@ -0,0 +1,37 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(MoveHasAdditionalEffect(MOVE_SAPPY_SEED, MOVE_EFFECT_LEECH_SEED) == TRUE); +} + +SINGLE_BATTLE_TEST("Sappy Seed can seed the target") +{ + GIVEN { + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SAPPY_SEED); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SAPPY_SEED, player); + MESSAGE("The opposing Wobbuffet was seeded!"); + MESSAGE("The opposing Wobbuffet's health is sapped by Leech Seed!"); + } +} + +SINGLE_BATTLE_TEST("Sappy Seed is not going to seed the target if it fails") +{ + GIVEN { + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SAPPY_SEED, hit: FALSE); } + } SCENE { + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SAPPY_SEED, player); + MESSAGE("The opposing Wobbuffet was seeded!"); + MESSAGE("The opposing Wobbuffet's health is sapped by Leech Seed!"); + } + } +} diff --git a/test/battle/move_effect_secondary/light_screen.c b/test/battle/move_effect_secondary/light_screen.c new file mode 100644 index 0000000000..244e469893 --- /dev/null +++ b/test/battle/move_effect_secondary/light_screen.c @@ -0,0 +1,32 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(MoveHasAdditionalEffect(MOVE_GLITZY_GLOW, MOVE_EFFECT_LIGHT_SCREEN) == TRUE); +} + +SINGLE_BATTLE_TEST("Glitzy Glow sets up Light Screen when it was succesful") +{ + bool32 moveSuccess; + PARAMETRIZE { moveSuccess = FALSE; } + PARAMETRIZE { moveSuccess = TRUE; } + + GIVEN { + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_GLITZY_GLOW, hit: moveSuccess); } + } SCENE { + if (moveSuccess == TRUE) + { + ANIMATION(ANIM_TYPE_MOVE, MOVE_GLITZY_GLOW, player); + MESSAGE("Light Screen made your team stronger against special moves!"); + } else { + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_GLITZY_GLOW, player); + MESSAGE("Light Screen made your team stronger against special moves!"); + } + } + } +} diff --git a/test/battle/move_effect_secondary/reflect.c b/test/battle/move_effect_secondary/reflect.c new file mode 100644 index 0000000000..6a0dda06d8 --- /dev/null +++ b/test/battle/move_effect_secondary/reflect.c @@ -0,0 +1,32 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(MoveHasAdditionalEffect(MOVE_BADDY_BAD, MOVE_EFFECT_REFLECT) == TRUE); +} + +SINGLE_BATTLE_TEST("Baddy Bad sets up Reflect when it was succesful") +{ + bool32 moveSuccess; + PARAMETRIZE { moveSuccess = FALSE; } + PARAMETRIZE { moveSuccess = TRUE; } + + GIVEN { + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_BADDY_BAD, hit: moveSuccess); } + } SCENE { + if (moveSuccess == TRUE) + { + ANIMATION(ANIM_TYPE_MOVE, MOVE_BADDY_BAD, player); + MESSAGE("Reflect made your team stronger against physical moves!"); + } else { + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_BADDY_BAD, player); + MESSAGE("Reflect made your team stronger against physical moves!"); + } + } + } +}