diff --git a/include/battle_script_commands.h b/include/battle_script_commands.h index 7326647d419d..32d8fc02388e 100644 --- a/include/battle_script_commands.h +++ b/include/battle_script_commands.h @@ -22,8 +22,7 @@ struct PickupItem u8 percentage[10]; }; -s32 CalcCritChanceStageArgs(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility, u32 abilityAtk, u32 abilityDef, u32 holdEffectAtk); -s32 CalcCritChanceStage(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility); +s32 CalcCritChanceStage(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility, u32 abilityAtk, u32 abilityDef, u32 holdEffectAtk); s32 GetCritHitOdds(s32 critChanceIndex); u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u32 defAbility, u32 atkHoldEffect, u32 defHoldEffect); u8 GetBattlerTurnOrderNum(u8 battlerId); diff --git a/include/config/battle.h b/include/config/battle.h index 226f649a339e..34e022f5eba6 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -89,6 +89,7 @@ #define B_CHARGE_SPDEF_RAISE GEN_LATEST // In Gen5+, Charge raises the user's Special Defense by 1 stage. #define B_MINIMIZE_EVASION GEN_LATEST // In Gen5+, Minimize raises evasion by 2 stages instead of 1. #define B_GROWTH_STAT_RAISE GEN_LATEST // In Gen5+, Growth raises Attack in addition to Special Attack by 1 stage each. Under the effects of the sun, it raises them by 2 stages each instead. +#define B_FOCUS_ENERGY_CRIT_RATIO GEN_LATEST // In Gen3+, Focus Energy increases critical hit ratio by 2 instead of 1. // Other move settings #define B_INCINERATE_GEMS GEN_LATEST // In Gen6+, Incinerate can destroy Gems. diff --git a/include/constants/generational_changes.h b/include/constants/generational_changes.h index 557b34b6537e..b4c5db43b7a4 100644 --- a/include/constants/generational_changes.h +++ b/include/constants/generational_changes.h @@ -3,6 +3,12 @@ enum GenConfigTag { + GEN_CONFIG_CRIT_CHANCE, + GEN_CONFIG_CRIT_MULTIPLIER, + GEN_CONFIG_FOCUS_ENERGY_CRIT_RATIO, + GEN_CONFIG_PARALYSIS_SPEED, + GEN_CONFIG_CONFUSION_SELF_DMG_CHANCE, + GEN_CONFIG_MULTI_HIT_CHANCE, GEN_CONFIG_GALE_WINGS, GEN_CONFIG_COUNT }; diff --git a/include/generational_changes.h b/include/generational_changes.h index 5a726007c36e..fa404845b099 100644 --- a/include/generational_changes.h +++ b/include/generational_changes.h @@ -6,7 +6,13 @@ static const u8 sGenerationalChanges[GEN_CONFIG_COUNT] = { - [GEN_CONFIG_GALE_WINGS] = B_GALE_WINGS, + [GEN_CONFIG_CRIT_CHANCE] = B_CRIT_CHANCE, + [GEN_CONFIG_CRIT_MULTIPLIER] = B_CRIT_MULTIPLIER, + [GEN_CONFIG_FOCUS_ENERGY_CRIT_RATIO] = B_FOCUS_ENERGY_CRIT_RATIO, + [GEN_CONFIG_PARALYSIS_SPEED] = B_PARALYSIS_SPEED, + [GEN_CONFIG_CONFUSION_SELF_DMG_CHANCE] = B_CONFUSION_SELF_DMG_CHANCE, + [GEN_CONFIG_MULTI_HIT_CHANCE] = B_MULTI_HIT_CHANCE, + [GEN_CONFIG_GALE_WINGS] = B_GALE_WINGS, }; #if TESTING diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index ac6f214134de..e440091183b1 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -676,7 +676,7 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u damageCalcData.randomFactor = FALSE; damageCalcData.updateFlags = FALSE; - critChanceIndex = CalcCritChanceStageArgs(battlerAtk, battlerDef, move, FALSE, aiData->abilities[battlerAtk], aiData->abilities[battlerDef], aiData->holdEffects[battlerAtk]); + critChanceIndex = CalcCritChanceStage(battlerAtk, battlerDef, move, FALSE, aiData->abilities[battlerAtk], aiData->abilities[battlerDef], aiData->holdEffects[battlerAtk]); if (critChanceIndex > 1) // Consider crit damage only if a move has at least +2 crit chance { damageCalcData.isCrit = FALSE; diff --git a/src/battle_main.c b/src/battle_main.c index 43bc13277ba1..d5db2a072a77 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -4823,7 +4823,7 @@ u32 GetBattlerTotalSpeedStatArgs(u32 battler, u32 ability, u32 holdEffect) // paralysis drop if (gBattleMons[battler].status1 & STATUS1_PARALYSIS && ability != ABILITY_QUICK_FEET) - speed /= B_PARALYSIS_SPEED >= GEN_7 ? 2 : 4; + speed /= GetGenConfig(GEN_CONFIG_PARALYSIS_SPEED) >= GEN_7 ? 2 : 4; if (gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SWAMP) speed /= 4; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index bd4451f7d727..6cc8162a7124 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -6,8 +6,6 @@ #include "battle_ai_util.h" #include "battle_scripts.h" #include "battle_z_move.h" -#include "constants/moves.h" -#include "constants/abilities.h" #include "item.h" #include "util.h" #include "pokemon.h" @@ -47,6 +45,7 @@ #include "pokenav.h" #include "menu_specialized.h" #include "data.h" +#include "generational_changes.h" #include "constants/abilities.h" #include "constants/battle_anim.h" #include "constants/battle_move_effects.h" @@ -1819,16 +1818,19 @@ static void Cmd_ppreduce(void) } // The chance is 1/N for each stage. -static const u32 sGen7CriticalHitOdds[] = {24, 8, 2, 1, 1}; -static const u32 sGen6CriticalHitOdds[] = {16, 8, 2, 1, 1}; -static const u32 sCriticalHitOdds[] = {16, 8, 4, 3, 2}; // Gens 2,3,4,5 +static const u32 sGen7CriticalHitOdds[] = {24, 8, 2, 1, 1}; // 1/X +static const u32 sGen6CriticalHitOdds[] = {16, 8, 2, 1, 1}; // 1/X +static const u32 sCriticalHitOdds[] = {16, 8, 4, 3, 2}; // 1/X, Gens 3,4,5 +static const u32 sGen2CriticalHitOdds[] = {17, 32, 64, 85, 128}; // X/256 static inline u32 GetCriticalHitOdds(u32 critChance) { - if (B_CRIT_CHANCE >= GEN_7) + if (GetGenConfig(GEN_CONFIG_CRIT_CHANCE) >= GEN_7) return sGen7CriticalHitOdds[critChance]; - if (B_CRIT_CHANCE == GEN_6) + if (GetGenConfig(GEN_CONFIG_CRIT_CHANCE) == GEN_6) return sGen6CriticalHitOdds[critChance]; + if (GetGenConfig(GEN_CONFIG_CRIT_CHANCE) == GEN_2) + return sGen2CriticalHitOdds[critChance]; return sCriticalHitOdds[critChance]; } @@ -1870,7 +1872,7 @@ static inline u32 GetHoldEffectCritChanceIncrease(u32 battler, u32 holdEffect) #define CRITICAL_HIT_BLOCKED -1 #define CRITICAL_HIT_ALWAYS -2 -s32 CalcCritChanceStageArgs(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility, u32 abilityAtk, u32 abilityDef, u32 holdEffectAtk) +s32 CalcCritChanceStage(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility, u32 abilityAtk, u32 abilityDef, u32 holdEffectAtk) { s32 critChance = 0; @@ -1913,75 +1915,51 @@ s32 CalcCritChanceStageArgs(u32 battlerAtk, u32 battlerDef, u32 move, bool32 rec return critChance; } -#undef CRITICAL_HIT_BLOCKED -#undef CRITICAL_HIT_ALWAYS - -s32 CalcCritChanceStage(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility) -{ - u32 abilityAtk = GetBattlerAbility(gBattlerAttacker); - u32 abilityDef = GetBattlerAbility(gBattlerTarget); - u32 holdEffectAtk = GetBattlerHoldEffect(battlerAtk, TRUE); - return CalcCritChanceStageArgs(battlerAtk, battlerDef, move, recordAbility, abilityAtk, abilityDef, holdEffectAtk); -} // Bulbapedia: https://bulbapedia.bulbagarden.net/wiki/Critical_hit#Generation_I // Crit chance = Threshold / 256, Threshold maximum of 255 // Threshold = Base Speed / 2 // High crit move = 8 * (Base Speed / 2) // Focus Energy = 4 * (Base Speed / 2) -s32 CalcCritChanceStageGen1(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility) +s32 CalcCritChanceStageGen1(u32 battlerAtk, u32 battlerDef, u32 move, bool32 recordAbility, u32 abilityAtk, u32 abilityDef, u32 holdEffectAtk) { - // Vanilla - u32 focusEnergyScaler = 4; - u32 highCritRatioScaler = 8; - - // Not vanilla - u32 superLuckScaler = 4; - u32 scopeLensScaler = 4; - u32 luckyPunchScaler = 8; - u32 farfetchdLeekScaler = 8; - s32 critChance = 0; s32 moveCritStage = gMovesInfo[gCurrentMove].criticalHitStage; s32 bonusCritStage = gBattleStruct->bonusCritStages[battlerAtk]; // G-Max Chi Strike - u32 abilityAtk = GetBattlerAbility(battlerAtk); - u32 abilityDef = GetBattlerAbility(battlerDef); - u32 holdEffectAtk = GetBattlerHoldEffect(battlerAtk, TRUE); + u32 holdEffectCritStage = GetHoldEffectCritChanceIncrease(battlerAtk, holdEffectAtk); u16 baseSpeed = gSpeciesInfo[gBattleMons[battlerAtk].species].baseSpeed; critChance = baseSpeed / 2; // Crit scaling if (moveCritStage > 0) - critChance = critChance * highCritRatioScaler * moveCritStage; + critChance *= 8 * moveCritStage; if (bonusCritStage > 0) - critChance = critChance * bonusCritStage; + critChance *= bonusCritStage; - if ((gBattleMons[battlerAtk].status2 & STATUS2_FOCUS_ENERGY_ANY) != 0) - critChance = critChance * focusEnergyScaler; + if (gBattleMons[battlerAtk].status2 & STATUS2_FOCUS_ENERGY) + critChance *= 4; + else if (gBattleMons[battlerAtk].status2 & STATUS2_DRAGON_CHEER) + critChance *= 2; - if (holdEffectAtk == HOLD_EFFECT_SCOPE_LENS) - critChance = critChance * scopeLensScaler; - else if (holdEffectAtk == HOLD_EFFECT_LUCKY_PUNCH && gBattleMons[battlerAtk].species == SPECIES_CHANSEY) - critChance = critChance * luckyPunchScaler; - else if (IsBattlerLeekAffected(battlerAtk, holdEffectAtk)) - critChance = critChance * farfetchdLeekScaler; + if (holdEffectCritStage > 0) + critChance *= 4 * holdEffectCritStage; if (abilityAtk == ABILITY_SUPER_LUCK) - critChance = critChance * superLuckScaler; + critChance *= 4; if (critChance > 255) critChance = 255; // Prevented crits if (gSideStatuses[battlerDef] & SIDE_STATUS_LUCKY_CHANT) - critChance = -1; + critChance = CRITICAL_HIT_BLOCKED; else if (abilityDef == ABILITY_BATTLE_ARMOR || abilityDef == ABILITY_SHELL_ARMOR) { if (recordAbility) RecordAbilityBattle(battlerDef, abilityDef); - critChance = -1; + critChance = CRITICAL_HIT_BLOCKED; } // Guaranteed crits @@ -1989,7 +1967,7 @@ s32 CalcCritChanceStageGen1(u32 battlerAtk, u32 battlerDef, u32 move, bool32 rec || gMovesInfo[move].alwaysCriticalHit == TRUE || (abilityAtk == ABILITY_MERCILESS && gBattleMons[battlerDef].status1 & STATUS1_PSN_ANY)) { - critChance = -2; + critChance = CRITICAL_HIT_ALWAYS; } return critChance; @@ -2002,6 +1980,8 @@ s32 GetCritHitOdds(s32 critChanceIndex) else return GetCriticalHitOdds(critChanceIndex); } +#undef CRITICAL_HIT_BLOCKED +#undef CRITICAL_HIT_ALWAYS static void Cmd_critcalc(void) { @@ -2009,11 +1989,14 @@ static void Cmd_critcalc(void) u16 partySlot; s32 critChance; + u32 abilityAtk = GetBattlerAbility(gBattlerAttacker); + u32 abilityDef = GetBattlerAbility(gBattlerTarget); + u32 holdEffectAtk = GetBattlerHoldEffect(gBattlerAttacker, TRUE); - if (B_CRIT_CHANCE == GEN_1) - critChance = CalcCritChanceStageGen1(gBattlerAttacker, gBattlerTarget, gCurrentMove, TRUE); + if (GetGenConfig(GEN_CONFIG_CRIT_CHANCE) == GEN_1) + critChance = CalcCritChanceStageGen1(gBattlerAttacker, gBattlerTarget, gCurrentMove, TRUE, abilityAtk, abilityDef, holdEffectAtk); else - critChance = CalcCritChanceStage(gBattlerAttacker, gBattlerTarget, gCurrentMove, TRUE); + critChance = CalcCritChanceStage(gBattlerAttacker, gBattlerTarget, gCurrentMove, TRUE, abilityAtk, abilityDef, holdEffectAtk); gPotentialItemEffectBattler = gBattlerAttacker; @@ -2025,14 +2008,10 @@ static void Cmd_critcalc(void) gIsCriticalHit = TRUE; else { - if (B_CRIT_CHANCE == GEN_1) - { - u8 critRoll = RandomUniform(RNG_CRITICAL_HIT, 1, 256); - if (critRoll <= critChance) - gIsCriticalHit = 1; - else - gIsCriticalHit = 0; - } + if (GetGenConfig(GEN_CONFIG_CRIT_CHANCE) == GEN_1) + gIsCriticalHit = RandomChance(RNG_CRITICAL_HIT, critChance, 256); + else if (GetGenConfig(GEN_CONFIG_CRIT_CHANCE) == GEN_2) + gIsCriticalHit = RandomChance(RNG_CRITICAL_HIT, GetCriticalHitOdds(critChance), 256); else gIsCriticalHit = RandomChance(RNG_CRITICAL_HIT, 1, GetCriticalHitOdds(critChance)); } @@ -12603,7 +12582,10 @@ static void Cmd_setfocusenergy(void) } else { - gBattleMons[battler].status2 |= STATUS2_FOCUS_ENERGY; + if (GetGenConfig(GEN_CONFIG_FOCUS_ENERGY_CRIT_RATIO) >= GEN_3) + gBattleMons[battler].status2 |= STATUS2_FOCUS_ENERGY; + else + gBattleMons[battler].status2 |= STATUS2_DRAGON_CHEER; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_GETTING_PUMPED; } gBattlescriptCurrInstr = cmd->nextInstr; diff --git a/src/battle_util.c b/src/battle_util.c index 4dc9c9b45e90..e381abb1c9d7 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -9,6 +9,7 @@ #include "battle_setup.h" #include "battle_z_move.h" #include "battle_gimmick.h" +#include "generational_changes.h" #include "party_menu.h" #include "pokemon.h" #include "international_string_util.h" @@ -3473,7 +3474,7 @@ u8 AtkCanceller_UnableToUseMove(u32 moveType) if (gBattleMons[gBattlerAttacker].status2 & STATUS2_CONFUSION) { // confusion dmg - if (RandomPercentage(RNG_CONFUSION, (B_CONFUSION_SELF_DMG_CHANCE >= GEN_7 ? 33 : 50))) + if (RandomPercentage(RNG_CONFUSION, (GetGenConfig(GEN_CONFIG_CONFUSION_SELF_DMG_CHANCE) >= GEN_7 ? 33 : 50))) { gBattleCommunication[MULTISTRING_CHOOSER] = TRUE; gBattlerTarget = gBattlerAttacker; @@ -10091,7 +10092,7 @@ static inline uq4_12_t GetBurnOrFrostBiteModifier(struct DamageCalculationData * static inline uq4_12_t GetCriticalModifier(bool32 isCrit) { if (isCrit) - return B_CRIT_MULTIPLIER >= GEN_6 ? UQ_4_12(1.5) : UQ_4_12(2.0); + return GetGenConfig(GEN_CONFIG_CRIT_MULTIPLIER) >= GEN_6 ? UQ_4_12(1.5) : UQ_4_12(2.0); return UQ_4_12(1.0); } @@ -11677,7 +11678,7 @@ static void SetRandomMultiHitCounter() { if (GetBattlerHoldEffect(gBattlerAttacker, TRUE) == HOLD_EFFECT_LOADED_DICE) gMultiHitCounter = RandomUniform(RNG_LOADED_DICE, 4, 5); - else if (B_MULTI_HIT_CHANCE >= GEN_5) + else if (GetGenConfig(GEN_CONFIG_MULTI_HIT_CHANCE) >= GEN_5) gMultiHitCounter = RandomWeighted(RNG_HITS, 0, 0, 7, 7, 3, 3); // 35%: 2 hits, 35%: 3 hits, 15% 4 hits, 15% 5 hits. else gMultiHitCounter = RandomWeighted(RNG_HITS, 0, 0, 3, 3, 1, 1); // 37.5%: 2 hits, 37.5%: 3 hits, 12.5% 4 hits, 12.5% 5 hits. diff --git a/test/battle/ability/battle_armor.c b/test/battle/ability/battle_armor.c new file mode 100644 index 000000000000..9424d57e5997 --- /dev/null +++ b/test/battle/ability/battle_armor.c @@ -0,0 +1,49 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Battle Armor and Shell Armor block critical hits") +{ + u32 species; + u32 ability; + + PARAMETRIZE { species = SPECIES_KINGLER; ability = ABILITY_SHELL_ARMOR; } + PARAMETRIZE { species = SPECIES_ARMALDO; ability = ABILITY_BATTLE_ARMOR; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(species) { Ability(ability); } + } WHEN { + TURN { MOVE(player, MOVE_TACKLE, criticalHit: TRUE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); + NOT MESSAGE("A critical hit!"); + } +} + +SINGLE_BATTLE_TEST("Mold Breaker, Teravolt and Turboblaze ignore Battle Armor and Shell Armor") +{ + u32 j; + u32 species1, species2, ability1, ability2; + static const u32 breakerData[][2] = + { + {SPECIES_PINSIR, ABILITY_MOLD_BREAKER}, + {SPECIES_ZEKROM, ABILITY_TERAVOLT}, + {SPECIES_RESHIRAM, ABILITY_TURBOBLAZE}, + }; + + for (j = 0; j < ARRAY_COUNT(breakerData); j++) + { + PARAMETRIZE { species1 = breakerData[j][0]; ability1 = breakerData[j][1]; species2 = SPECIES_KINGLER; ability2 = ABILITY_SHELL_ARMOR; } + PARAMETRIZE { species1 = breakerData[j][0]; ability1 = breakerData[j][1]; species2 = SPECIES_ARMALDO; ability2 = ABILITY_BATTLE_ARMOR; } + } + + GIVEN { + PLAYER(species1) { Ability(ability1); } + OPPONENT(species2) { Ability(ability2); } + } WHEN { + TURN { MOVE(player, MOVE_TACKLE, criticalHit: TRUE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); + MESSAGE("A critical hit!"); + } +} diff --git a/test/battle/ability/inner_focus.c b/test/battle/ability/inner_focus.c index 5a470b742f89..509b46d17606 100644 --- a/test/battle/ability/inner_focus.c +++ b/test/battle/ability/inner_focus.c @@ -43,7 +43,7 @@ SINGLE_BATTLE_TEST("Inner Focus prevents flinching") } } -SINGLE_BATTLE_TEST("Inner Focus is ignored by Mold Breaker") +SINGLE_BATTLE_TEST("Mold Breaker ignores Inner Focus") { GIVEN { PLAYER(SPECIES_PINSIR) { Ability(ABILITY_MOLD_BREAKER); }; diff --git a/test/battle/ability/merciless.c b/test/battle/ability/merciless.c new file mode 100644 index 000000000000..fc73842dcf79 --- /dev/null +++ b/test/battle/ability/merciless.c @@ -0,0 +1,16 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Merciless causes a move to result in a critical hit if the target is poisoned") +{ + PASSES_RANDOMLY(1, 1, RNG_CRITICAL_HIT); + GIVEN { + PLAYER(SPECIES_MAREANIE) { Ability(ABILITY_MERCILESS); } + OPPONENT(SPECIES_WOBBUFFET) { Status1(STATUS1_POISON); } + } WHEN { + TURN { MOVE(player, MOVE_TACKLE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); + MESSAGE("A critical hit!"); + } +} diff --git a/test/battle/ability/own_tempo.c b/test/battle/ability/own_tempo.c index 4b3c42053b77..1f3f49c0060c 100644 --- a/test/battle/ability/own_tempo.c +++ b/test/battle/ability/own_tempo.c @@ -56,7 +56,7 @@ SINGLE_BATTLE_TEST("Own Tempo prevents confusion from moves by the user") } } -SINGLE_BATTLE_TEST("Own Tempo is ignored by Mold Breaker") +SINGLE_BATTLE_TEST("Mold Breaker ignores Own Tempo") { KNOWN_FAILING; // Ideally the func CanBeConfused should be split into AttackerCanBeConfused and TargetCanBeConfused or we do it in the same func but have a check for when battlerAtk == battlerDef GIVEN { @@ -73,7 +73,7 @@ SINGLE_BATTLE_TEST("Own Tempo is ignored by Mold Breaker") } } -SINGLE_BATTLE_TEST("Own Tempo cures confusion obtained from an opponent with Mold Breaker") +SINGLE_BATTLE_TEST("Mold Breaker does not prevent Own Tempo from curing confusion right after") { KNOWN_FAILING; GIVEN { diff --git a/test/battle/ability/parental_bond.c b/test/battle/ability/parental_bond.c index a1614a8ffc32..e84977c91e23 100644 --- a/test/battle/ability/parental_bond.c +++ b/test/battle/ability/parental_bond.c @@ -131,12 +131,15 @@ SINGLE_BATTLE_TEST("Parental Bond-converted moves only hit once on Lightning Rod } } -SINGLE_BATTLE_TEST("Parental Bond has no affect on multi hit moves and they still hit twice 35% of the time") +SINGLE_BATTLE_TEST("Parental Bond has no affect on multi hit moves and they still hit twice 37.5/35% of the time") { - PASSES_RANDOMLY(35, 100, RNG_HITS); + u32 genConfig, passes, trials; + PARAMETRIZE { genConfig = GEN_4; passes = 3; trials = 8; } // 37.5% + PARAMETRIZE { genConfig = GEN_5; passes = 7; trials = 20; } // 35% + PASSES_RANDOMLY(passes, trials, RNG_HITS); GIVEN { - ASSUME(B_MULTI_HIT_CHANCE >= GEN_5); + WITH_CONFIG(GEN_CONFIG_MULTI_HIT_CHANCE, genConfig); ASSUME(gMovesInfo[MOVE_COMET_PUNCH].category != DAMAGE_CATEGORY_STATUS); ASSUME(gMovesInfo[MOVE_COMET_PUNCH].effect == EFFECT_MULTI_HIT); PLAYER(SPECIES_KANGASKHAN) { Item(ITEM_KANGASKHANITE); } @@ -157,12 +160,15 @@ SINGLE_BATTLE_TEST("Parental Bond has no affect on multi hit moves and they stil } } -SINGLE_BATTLE_TEST("Parental Bond has no affect on multi hit moves and they still hit thrice 35% of the time") +SINGLE_BATTLE_TEST("Parental Bond has no affect on multi hit moves and they still hit thrice 37.5/35% of the time") { - PASSES_RANDOMLY(35, 100, RNG_HITS); + u32 genConfig, passes, trials; + PARAMETRIZE { genConfig = GEN_4; passes = 3; trials = 8; } // 37.5% + PARAMETRIZE { genConfig = GEN_5; passes = 7; trials = 20; } // 35% + PASSES_RANDOMLY(passes, trials, RNG_HITS); GIVEN { - ASSUME(B_MULTI_HIT_CHANCE >= GEN_5); + WITH_CONFIG(GEN_CONFIG_MULTI_HIT_CHANCE, genConfig); ASSUME(gMovesInfo[MOVE_COMET_PUNCH].category != DAMAGE_CATEGORY_STATUS); ASSUME(gMovesInfo[MOVE_COMET_PUNCH].effect == EFFECT_MULTI_HIT); PLAYER(SPECIES_KANGASKHAN) { Item(ITEM_KANGASKHANITE); } @@ -184,12 +190,15 @@ SINGLE_BATTLE_TEST("Parental Bond has no affect on multi hit moves and they stil } } -SINGLE_BATTLE_TEST("Parental Bond has no affect on multi hit moves and they still hit four times 15% of the time") +SINGLE_BATTLE_TEST("Parental Bond has no affect on multi hit moves and they still hit four times 12.5/15% of the time") { - PASSES_RANDOMLY(15, 100, RNG_HITS); + u32 genConfig, passes, trials; + PARAMETRIZE { genConfig = GEN_4; passes = 1; trials = 8; } // 12.5% + PARAMETRIZE { genConfig = GEN_5; passes = 3; trials = 20; } // 15% + PASSES_RANDOMLY(passes, trials, RNG_HITS); GIVEN { - ASSUME(B_MULTI_HIT_CHANCE >= GEN_5); + WITH_CONFIG(GEN_CONFIG_MULTI_HIT_CHANCE, genConfig); ASSUME(gMovesInfo[MOVE_COMET_PUNCH].category != DAMAGE_CATEGORY_STATUS); ASSUME(gMovesInfo[MOVE_COMET_PUNCH].effect == EFFECT_MULTI_HIT); PLAYER(SPECIES_KANGASKHAN) { Item(ITEM_KANGASKHANITE); } @@ -212,12 +221,15 @@ SINGLE_BATTLE_TEST("Parental Bond has no affect on multi hit moves and they stil } } -SINGLE_BATTLE_TEST("Parental Bond has no affect on multi hit moves and they still hit five times 15% of the time") +SINGLE_BATTLE_TEST("Parental Bond has no affect on multi hit moves and they still hit five times 12.5/15% of the time") { - PASSES_RANDOMLY(15, 100, RNG_HITS); + u32 genConfig, passes, trials; + PARAMETRIZE { genConfig = GEN_4; passes = 1; trials = 8; } // 12.5% + PARAMETRIZE { genConfig = GEN_5; passes = 3; trials = 20; } // 15% + PASSES_RANDOMLY(passes, trials, RNG_HITS); GIVEN { - ASSUME(B_MULTI_HIT_CHANCE >= GEN_5); + WITH_CONFIG(GEN_CONFIG_MULTI_HIT_CHANCE, genConfig); ASSUME(gMovesInfo[MOVE_COMET_PUNCH].category != DAMAGE_CATEGORY_STATUS); ASSUME(gMovesInfo[MOVE_COMET_PUNCH].effect == EFFECT_MULTI_HIT); PLAYER(SPECIES_KANGASKHAN) { Item(ITEM_KANGASKHANITE); } diff --git a/test/battle/ability/shell_armor.c b/test/battle/ability/shell_armor.c new file mode 100644 index 000000000000..7d97b0a069b4 --- /dev/null +++ b/test/battle/ability/shell_armor.c @@ -0,0 +1,4 @@ +#include "global.h" +#include "test/battle.h" + +// Tests for Shell Armor are handled in test/battle/ability/battle_armor.c diff --git a/test/battle/ability/super_luck.c b/test/battle/ability/super_luck.c new file mode 100644 index 000000000000..39fd20ce168d --- /dev/null +++ b/test/battle/ability/super_luck.c @@ -0,0 +1,25 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Super Luck increases the critical hit ratio by 1 stage") +{ + u32 j, genConfig = 0, passes = 0, trials = 0; + + PARAMETRIZE { genConfig = GEN_1; passes = 5; trials = 32; } // ~15.6% with Togepi's base speed + for (j = GEN_2; j <= GEN_9; j++) + PARAMETRIZE { genConfig = j; passes = 1; trials = 8; } // 12.5% + PASSES_RANDOMLY(passes, trials, RNG_CRITICAL_HIT); + GIVEN { + ASSUME(gSpeciesInfo[SPECIES_TOGEPI].baseSpeed == 20); + WITH_CONFIG(GEN_CONFIG_CRIT_CHANCE, genConfig); + PLAYER(SPECIES_TOGEPI) { Ability(ABILITY_SUPER_LUCK); }; + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TACKLE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); + MESSAGE("A critical hit!"); + } +} + +TO_DO_BATTLE_TEST("Super Luck increases the chances of wild Pokémon holding items (Gen8+)"); diff --git a/test/battle/crit_chance.c b/test/battle/crit_chance.c index 7287266e985b..0ff0634539b0 100644 --- a/test/battle/crit_chance.c +++ b/test/battle/crit_chance.c @@ -1,200 +1,41 @@ #include "global.h" #include "test/battle.h" -SINGLE_BATTLE_TEST("Crit Chance: Side effected by Lucky Chant blocks critical hits") +SINGLE_BATTLE_TEST("Critical hits without modifiers occur at different rates by generation") { - GIVEN { - ASSUME(gMovesInfo[MOVE_LUCKY_CHANT].effect == EFFECT_LUCKY_CHANT); - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { MOVE(opponent, MOVE_LUCKY_CHANT); MOVE(player, MOVE_TACKLE, criticalHit: TRUE); } - } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); - NOT MESSAGE("A critical hit!"); - } -} - -SINGLE_BATTLE_TEST("Crit Chance: Battle Armor and Shell Armor block critical hits") -{ - u32 species; - u32 ability; - - PARAMETRIZE { species = SPECIES_KINGLER; ability = ABILITY_SHELL_ARMOR; } - PARAMETRIZE { species = SPECIES_ARMALDO; ability = ABILITY_BATTLE_ARMOR; } + u32 genConfig, passes, trials; + PARAMETRIZE { genConfig = GEN_1; passes = 1; trials = 16; } // 6.25% with Wobbuffet's base speed + PARAMETRIZE { genConfig = GEN_2; passes = 17; trials = 256; } // ~6.64% + for (u32 j = GEN_3; j <= GEN_6; j++) + PARAMETRIZE { genConfig = j; passes = 1, trials = 16; } // 6.25% + for (u32 j = GEN_7; j <= GEN_9; j++) + PARAMETRIZE { genConfig = j; passes = 1, trials = 24; } // ~4.17% + PASSES_RANDOMLY(passes, trials, RNG_CRITICAL_HIT); GIVEN { - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(species) { Ability(ability); } - } WHEN { - TURN { MOVE(player, MOVE_TACKLE, criticalHit: TRUE); } - } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); - NOT MESSAGE("A critical hit!"); - } -} - -SINGLE_BATTLE_TEST("Crit Chance: Flag ignoresTargetAbility ignores Battle Armor and Shell Armor") -{ - u32 species; - u32 ability; - - PARAMETRIZE { species = SPECIES_KINGLER; ability = ABILITY_SHELL_ARMOR; } - PARAMETRIZE { species = SPECIES_ARMALDO; ability = ABILITY_BATTLE_ARMOR; } - - GIVEN { - ASSUME(gMovesInfo[MOVE_SUNSTEEL_STRIKE].ignoresTargetAbility == TRUE); - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(species) { Ability(ability); } - } WHEN { - TURN { MOVE(player, MOVE_SUNSTEEL_STRIKE, criticalHit: TRUE); } - } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNSTEEL_STRIKE, player); - MESSAGE("A critical hit!"); - } -} - -SINGLE_BATTLE_TEST("Crit Chance: Mold Breaker, Teravolt and Turboblaze ignore Battle Armor and Shell Armor") -{ - u32 j; - static const u32 pokemonPlayer[][2] = - { - {SPECIES_PINSIR, ABILITY_MOLD_BREAKER}, - {SPECIES_ZEKROM, ABILITY_TERAVOLT}, - {SPECIES_KYUREM_WHITE, ABILITY_TURBOBLAZE}, - }; - - u32 speciesPlayer; - u32 abilityPlayer; - u32 speciesOpponent; - u32 abilityOpponent; - - for (j = 0; j < ARRAY_COUNT(pokemonPlayer); j++) - { - PARAMETRIZE { - speciesPlayer = pokemonPlayer[j][0]; - abilityPlayer = pokemonPlayer[j][1]; - speciesOpponent = SPECIES_KINGLER; - abilityOpponent = ABILITY_SHELL_ARMOR; - } - - PARAMETRIZE { - speciesPlayer = pokemonPlayer[j][0]; - abilityPlayer = pokemonPlayer[j][1]; - speciesOpponent = SPECIES_ARMALDO; - abilityOpponent = ABILITY_BATTLE_ARMOR; - } - } - - GIVEN { - PLAYER(speciesPlayer) { Ability(abilityPlayer); } - OPPONENT(speciesOpponent) { Ability(abilityOpponent); } - } WHEN { - TURN { MOVE(player, MOVE_TACKLE, criticalHit: TRUE); } - } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); - MESSAGE("A critical hit!"); - } -} - -SINGLE_BATTLE_TEST("Crit Chance: User effected by Laser Focus causes moves to result in a critical hit") -{ - GIVEN { - ASSUME(gMovesInfo[MOVE_LASER_FOCUS].effect == EFFECT_LASER_FOCUS); - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { MOVE(player, MOVE_LASER_FOCUS); } - TURN { MOVE(player, MOVE_TACKLE); } - } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_LASER_FOCUS, player); - ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); - MESSAGE("A critical hit!"); - } -} - -SINGLE_BATTLE_TEST("Crit Chance: If the target is poisoned the ability Merciless causes a move to result in a critical hit") -{ - GIVEN { - PLAYER(SPECIES_MAREANIE) { Ability(ABILITY_MERCILESS); } - OPPONENT(SPECIES_WOBBUFFET) { Status1(STATUS1_POISON); } - } WHEN { - TURN { MOVE(player, MOVE_TACKLE); } - } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); - MESSAGE("A critical hit!"); - } -} - -SINGLE_BATTLE_TEST("Crit Chance: Focus Energy increases the user's critical hit ratio by two stage") -{ - PASSES_RANDOMLY(1, 2, RNG_CRITICAL_HIT); - GIVEN { - ASSUME(B_CRIT_CHANCE >= GEN_7); - ASSUME(gMovesInfo[MOVE_FOCUS_ENERGY].effect == EFFECT_FOCUS_ENERGY); + WITH_CONFIG(GEN_CONFIG_CRIT_CHANCE, genConfig); + ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].baseSpeed == 33); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); } WHEN { - TURN { MOVE(player, MOVE_FOCUS_ENERGY); } - TURN { MOVE(player, MOVE_TACKLE); } - } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_FOCUS_ENERGY, player); - ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); - MESSAGE("A critical hit!"); - } -} - -SINGLE_BATTLE_TEST("Crit Chance: High crit rate increases the critical hit ratio by one stage") -{ - PASSES_RANDOMLY(1, 8, RNG_CRITICAL_HIT); - GIVEN { - ASSUME(B_CRIT_CHANCE >= GEN_7); - ASSUME(gMovesInfo[MOVE_SLASH].criticalHitStage == 1); - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { MOVE(player, MOVE_SLASH); } - } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_SLASH, player); - MESSAGE("A critical hit!"); - } -} - -SINGLE_BATTLE_TEST("Crit Chance: Super Luck increases the critical hit ratio by one stage") -{ - PASSES_RANDOMLY(1, 8, RNG_CRITICAL_HIT); - GIVEN { - ASSUME(B_CRIT_CHANCE >= GEN_7); - PLAYER(SPECIES_TOGEPI) { Ability(ABILITY_SUPER_LUCK); }; - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { MOVE(player, MOVE_TACKLE); } - } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); - MESSAGE("A critical hit!"); - } -} - -SINGLE_BATTLE_TEST("Crit Chance: Scope Lens increases the critical hit ratio by one stage") -{ - PASSES_RANDOMLY(1, 8, RNG_CRITICAL_HIT); - GIVEN { - ASSUME(B_CRIT_CHANCE >= GEN_7); - ASSUME(gItemsInfo[ITEM_SCOPE_LENS].holdEffect == HOLD_EFFECT_SCOPE_LENS); - PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_SCOPE_LENS); }; - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { MOVE(player, MOVE_TACKLE); } + TURN { MOVE(player, MOVE_SCRATCH); } } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); MESSAGE("A critical hit!"); } } -SINGLE_BATTLE_TEST("Crit Chance: High crit rate, Super Luck and Scope Lens cause the move to result in a critical hit") +SINGLE_BATTLE_TEST("Crit Chance: Raising critical hit rate to 3 guarantees a critical hit (Gen 6+)") { + u32 genConfig = 0, passes, trials; + PARAMETRIZE { genConfig = GEN_1; passes = 255; trials = 256; } // ~99.6% + PARAMETRIZE { genConfig = GEN_2; passes = 85; trials = 256; } // ~33.2% + for (u32 j = GEN_3; j <= GEN_5; j++) + PARAMETRIZE { genConfig = j; passes = 1, trials = 3; } // ~33.3% + for (u32 j = GEN_6; j <= GEN_9; j++) + PARAMETRIZE { genConfig = j; passes = 1, trials = 1; } // 100% + PASSES_RANDOMLY(passes, trials, RNG_CRITICAL_HIT); GIVEN { - ASSUME(B_CRIT_CHANCE >= GEN_7); + WITH_CONFIG(GEN_CONFIG_CRIT_CHANCE, genConfig); ASSUME(gMovesInfo[MOVE_SLASH].criticalHitStage == 1); ASSUME(gItemsInfo[ITEM_SCOPE_LENS].holdEffect == HOLD_EFFECT_SCOPE_LENS); PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_SUPER_LUCK); Item(ITEM_SCOPE_LENS); }; @@ -206,68 +47,3 @@ SINGLE_BATTLE_TEST("Crit Chance: High crit rate, Super Luck and Scope Lens cause MESSAGE("A critical hit!"); } } - -SINGLE_BATTLE_TEST("Crit Chance: Signature items Leek and Lucky Punch increase the critical hit ratio by 2 stages") -{ - u32 species; - u32 item; - - PASSES_RANDOMLY(1, 2, RNG_CRITICAL_HIT); - - PARAMETRIZE { species = SPECIES_FARFETCHD; item = ITEM_LEEK; } - PARAMETRIZE { species = SPECIES_FARFETCHD_GALAR; item = ITEM_LEEK; } - PARAMETRIZE { species = SPECIES_SIRFETCHD; item = ITEM_LEEK; } - PARAMETRIZE { species = SPECIES_CHANSEY; item = ITEM_LUCKY_PUNCH; } - - GIVEN { - ASSUME(B_CRIT_CHANCE >= GEN_7); - ASSUME(gItemsInfo[ITEM_LEEK].holdEffect == HOLD_EFFECT_LEEK); - ASSUME(gItemsInfo[ITEM_LUCKY_PUNCH].holdEffect == HOLD_EFFECT_LUCKY_PUNCH); - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(species) { Item(item); } - } WHEN { - TURN { MOVE(opponent, MOVE_TACKLE); } - } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); - MESSAGE("A critical hit!"); - } -} - -SINGLE_BATTLE_TEST("Crit Chance: Dire Hit increases a battler's critical hit chance by 2 stages") -{ - PASSES_RANDOMLY(1, 2, RNG_CRITICAL_HIT); - GIVEN { - ASSUME(B_CRIT_CHANCE >= GEN_7); - ASSUME(gItemsInfo[ITEM_DIRE_HIT].battleUsage == EFFECT_ITEM_SET_FOCUS_ENERGY); - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { USE_ITEM(player, ITEM_DIRE_HIT, partyIndex: 0); } - TURN { MOVE(player, MOVE_SCRATCH); } - } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_FOCUS_ENERGY, player); - MESSAGE("Wobbuffet used the Dire Hit to get pumped!"); - MESSAGE("Wobbuffet used Scratch!"); - MESSAGE("A critical hit!"); - } -} - -SINGLE_BATTLE_TEST("Crit Chance: Focus Energy increases critical hit ratio by two") -{ - PASSES_RANDOMLY(8, 8, RNG_CRITICAL_HIT); - GIVEN { - ASSUME(B_CRIT_CHANCE >= GEN_7); - ASSUME(gMovesInfo[MOVE_SLASH].criticalHitStage == 1); - ASSUME(gMovesInfo[MOVE_FOCUS_ENERGY].effect == EFFECT_FOCUS_ENERGY); - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { MOVE(player, MOVE_FOCUS_ENERGY); } - TURN { MOVE(player, MOVE_SLASH); } - } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_FOCUS_ENERGY, player); - MESSAGE("Wobbuffet is getting pumped!"); - ANIMATION(ANIM_TYPE_MOVE, MOVE_SLASH, player); - MESSAGE("A critical hit!"); - } -} diff --git a/test/battle/gimmick/dynamax.c b/test/battle/gimmick/dynamax.c index 089587c71deb..6a753a92af03 100644 --- a/test/battle/gimmick/dynamax.c +++ b/test/battle/gimmick/dynamax.c @@ -1421,7 +1421,7 @@ DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Centiferno traps both opponents in Fire Spin } } -DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Chi Strike boosts allies' crit chance") +DOUBLE_BATTLE_TEST("(DYNAMAX) G-Max Chi Strike boosts allies' crit chance by 1 stage") { u32 j; GIVEN { diff --git a/test/battle/gimmick/zmove.c b/test/battle/gimmick/zmove.c index eb44184e5aed..e0716f949514 100644 --- a/test/battle/gimmick/zmove.c +++ b/test/battle/gimmick/zmove.c @@ -518,10 +518,17 @@ SINGLE_BATTLE_TEST("(Z-MOVE) Light That Burns the Sky uses the battler's highest SINGLE_BATTLE_TEST("(Z-MOVE) 10,000,000 Volt Thunderbolt has an increased critical hit ratio") { - PASSES_RANDOMLY(1, 2, RNG_CRITICAL_HIT); - GIVEN { - ASSUME(B_CRIT_CHANCE >= GEN_6); + u32 genConfig, chance; + PARAMETRIZE { genConfig = GEN_1; chance = 1; } + for (u32 j = GEN_2; j <= GEN_5; j++) + PARAMETRIZE { genConfig = j; chance = 4; } + for (u32 j = GEN_6; j <= GEN_9; j++) + PARAMETRIZE { genConfig = j; chance = 2; } + PASSES_RANDOMLY(1, chance, RNG_CRITICAL_HIT); + GIVEN { + WITH_CONFIG(GEN_CONFIG_CRIT_CHANCE, genConfig); ASSUME(gMovesInfo[MOVE_10_000_000_VOLT_THUNDERBOLT].criticalHitStage == 2); + ASSUME(gSpeciesInfo[SPECIES_PIKACHU_PARTNER].baseSpeed == 90); PLAYER(SPECIES_PIKACHU_PARTNER) { Item(ITEM_PIKASHUNIUM_Z); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { diff --git a/test/battle/hold_effect/critical_hit_up.c b/test/battle/hold_effect/critical_hit_up.c index c23f29773aea..7689d183f0da 100644 --- a/test/battle/hold_effect/critical_hit_up.c +++ b/test/battle/hold_effect/critical_hit_up.c @@ -48,12 +48,17 @@ SINGLE_BATTLE_TEST("Lansat Berry raises the holder's critical-hit-ratio by two s } } -SINGLE_BATTLE_TEST("Lansat Berry raises the holder's critical-hit-ratio by two stages") +SINGLE_BATTLE_TEST("Lansat Berry raises the holder's critical-hit-ratio by 2 stages") { - PASSES_RANDOMLY(1, 2, RNG_CRITICAL_HIT); + u32 genConfig = 0, chance; + for (u32 j = GEN_1; j <= GEN_5; j++) + PARAMETRIZE { genConfig = j; chance = 4; } // 25% + for (u32 j = GEN_6; j <= GEN_9; j++) + PARAMETRIZE { genConfig = j; chance = 2; } // 50% + PASSES_RANDOMLY(1, chance, RNG_CRITICAL_HIT); GIVEN { + WITH_CONFIG(GEN_CONFIG_CRIT_CHANCE, genConfig); ASSUME(gMovesInfo[MOVE_TACKLE].criticalHitStage == 0); - ASSUME(B_CRIT_CHANCE >= GEN_6); PLAYER(SPECIES_WOBBUFFET) { MaxHP(160); HP(80); Item(ITEM_LANSAT_BERRY); } OPPONENT(SPECIES_WOBBUFFET); } WHEN { diff --git a/test/battle/hold_effect/leek.c b/test/battle/hold_effect/leek.c new file mode 100644 index 000000000000..57cc9f1697d3 --- /dev/null +++ b/test/battle/hold_effect/leek.c @@ -0,0 +1,36 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Leek increases critical hit ratio by 2 stages for the Farfetch'd Family") +{ + u32 species, genConfig, passes, trials; + + PARAMETRIZE { genConfig = GEN_1; passes = 15; trials = 16; species = SPECIES_FARFETCHD; } // ~93.8% with Farfetch'd's base speed + PARAMETRIZE { genConfig = GEN_1; passes = 27; trials = 32; species = SPECIES_FARFETCHD_GALAR; } // ~84.4% with Galarian Farfetch'd's base speed + PARAMETRIZE { genConfig = GEN_1; passes = 1; trials = 1; species = SPECIES_SIRFETCHD; } // 100% with Sirfetch'd's base speed + for (u32 j = GEN_2; j <= GEN_5; j++) { + PARAMETRIZE { genConfig = j; passes = 1; trials = 4; species = SPECIES_FARFETCHD; } // 25% + PARAMETRIZE { genConfig = j; passes = 1; trials = 4; species = SPECIES_FARFETCHD_GALAR; } // 25% + PARAMETRIZE { genConfig = j; passes = 1; trials = 4; species = SPECIES_SIRFETCHD; } // 25% + } + for (u32 j = GEN_6; j <= GEN_9; j++) { + PARAMETRIZE { genConfig = j; passes = 1; trials = 2; species = SPECIES_FARFETCHD; } // 50% + PARAMETRIZE { genConfig = j; passes = 1; trials = 2; species = SPECIES_FARFETCHD_GALAR; } // 50% + PARAMETRIZE { genConfig = j; passes = 1; trials = 2; species = SPECIES_SIRFETCHD; } // 50% + } + PASSES_RANDOMLY(passes, trials, RNG_CRITICAL_HIT); + GIVEN { + WITH_CONFIG(GEN_CONFIG_CRIT_CHANCE, genConfig); + ASSUME(gSpeciesInfo[SPECIES_FARFETCHD].baseSpeed == 60); + ASSUME(gSpeciesInfo[SPECIES_FARFETCHD_GALAR].baseSpeed == 55); + ASSUME(gSpeciesInfo[SPECIES_SIRFETCHD].baseSpeed == 65); + ASSUME(gItemsInfo[ITEM_LEEK].holdEffect == HOLD_EFFECT_LEEK); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(species) { Item(ITEM_LEEK); } + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + MESSAGE("A critical hit!"); + } +} diff --git a/test/battle/hold_effect/luck_punch.c b/test/battle/hold_effect/luck_punch.c new file mode 100644 index 000000000000..056723eb3661 --- /dev/null +++ b/test/battle/hold_effect/luck_punch.c @@ -0,0 +1,25 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Lucky Punch increases critical hit ratio by 2 stages for Chansey") +{ + u32 genConfig, passes, trials; + PARAMETRIZE { genConfig = GEN_1; passes = 25; trials = 32; } // ~78.1% with Chansey's base speed + for (u32 j = GEN_2; j <= GEN_5; j++) + PARAMETRIZE { genConfig = j; passes = 1; trials = 4; } // 25% + for (u32 j = GEN_6; j <= GEN_9; j++) + PARAMETRIZE { genConfig = j; passes = 1; trials = 2; } // 50% + PASSES_RANDOMLY(passes, trials, RNG_CRITICAL_HIT); + GIVEN { + WITH_CONFIG(GEN_CONFIG_CRIT_CHANCE, genConfig); + ASSUME(gItemsInfo[ITEM_LUCKY_PUNCH].holdEffect == HOLD_EFFECT_LUCKY_PUNCH); + ASSUME(gSpeciesInfo[SPECIES_CHANSEY].baseSpeed == 50); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_CHANSEY) { Item(ITEM_LUCKY_PUNCH); } + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + MESSAGE("A critical hit!"); + } +} diff --git a/test/battle/hold_effect/scope_lens.c b/test/battle/hold_effect/scope_lens.c new file mode 100644 index 000000000000..6914025ce7f1 --- /dev/null +++ b/test/battle/hold_effect/scope_lens.c @@ -0,0 +1,23 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Scope Lens increases the critical hit ratio by 1 stage") +{ + u32 genConfig = 0, passes, trials; + PARAMETRIZE { genConfig = GEN_1; passes = 1; trials = 4; } // 25% with Wobbuffet's base speed + for (u32 j = GEN_2; j <= GEN_9; j++) + PARAMETRIZE { genConfig = j; passes = 1; trials = 8; } // 12.5% + PASSES_RANDOMLY(passes, trials, RNG_CRITICAL_HIT); + GIVEN { + WITH_CONFIG(GEN_CONFIG_CRIT_CHANCE, genConfig); + ASSUME(gItemsInfo[ITEM_SCOPE_LENS].holdEffect == HOLD_EFFECT_SCOPE_LENS); + ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].baseSpeed == 33); + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_SCOPE_LENS); }; + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TACKLE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); + MESSAGE("A critical hit!"); + } +} diff --git a/test/battle/item_effect/dire_hit.c b/test/battle/item_effect/dire_hit.c new file mode 100644 index 000000000000..10b9a28a6026 --- /dev/null +++ b/test/battle/item_effect/dire_hit.c @@ -0,0 +1,26 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Dire Hit increases a battler's critical hit chance by 2 stages") +{ + u32 genConfig = 0, chance; + for (u32 j = GEN_1; j <= GEN_5; j++) + PARAMETRIZE { genConfig = j; chance = 4; } // 25% + for (u32 j = GEN_6; j <= GEN_9; j++) + PARAMETRIZE { genConfig = j; chance = 2; } // 50% + PASSES_RANDOMLY(1, chance, RNG_CRITICAL_HIT); + GIVEN { + WITH_CONFIG(GEN_CONFIG_CRIT_CHANCE, genConfig); + ASSUME(gItemsInfo[ITEM_DIRE_HIT].battleUsage == EFFECT_ITEM_SET_FOCUS_ENERGY); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { USE_ITEM(player, ITEM_DIRE_HIT, partyIndex: 0); } + TURN { MOVE(player, MOVE_SCRATCH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FOCUS_ENERGY, player); + MESSAGE("Wobbuffet used the Dire Hit to get pumped!"); + MESSAGE("Wobbuffet used Scratch!"); + MESSAGE("A critical hit!"); + } +} diff --git a/test/battle/move.c b/test/battle/move.c index 9ee37391ec72..ab2f5157bc5b 100644 --- a/test/battle/move.c +++ b/test/battle/move.c @@ -134,42 +134,15 @@ DOUBLE_BATTLE_TEST("Turn order is determined randomly if priority and Speed tie } } -SINGLE_BATTLE_TEST("Critical hits occur at a 1/24 rate") -{ - PASSES_RANDOMLY(1, 24, RNG_CRITICAL_HIT); - GIVEN { - ASSUME(B_CRIT_CHANCE >= GEN_7); - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { MOVE(player, MOVE_SCRATCH); } - } SCENE { - MESSAGE("A critical hit!"); - } -} - -SINGLE_BATTLE_TEST("Slash's critical hits occur at a 1/8 rate") -{ - PASSES_RANDOMLY(1, 8, RNG_CRITICAL_HIT); - GIVEN { - ASSUME(B_CRIT_CHANCE >= GEN_7); - ASSUME(gMovesInfo[MOVE_SLASH].criticalHitStage == 1); - PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_WOBBUFFET); - } WHEN { - TURN { MOVE(player, MOVE_SLASH); } - } SCENE { - MESSAGE("A critical hit!"); - } -} - -SINGLE_BATTLE_TEST("Critical hits deal 50% more damage", s16 damage) +SINGLE_BATTLE_TEST("Critical hits deal 100% (Gen 1-5) or 50% (Gen 6+) more damage", s16 damage) { bool32 criticalHit; - PARAMETRIZE { criticalHit = FALSE; } - PARAMETRIZE { criticalHit = TRUE; } + u32 genConfig; + PARAMETRIZE { criticalHit = FALSE; genConfig = GEN_5; } + PARAMETRIZE { criticalHit = TRUE; genConfig = GEN_5; } + PARAMETRIZE { criticalHit = TRUE; genConfig = GEN_6; } GIVEN { - ASSUME(B_CRIT_MULTIPLIER >= GEN_6); + WITH_CONFIG(GEN_CONFIG_CRIT_MULTIPLIER, genConfig); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); } WHEN { @@ -177,7 +150,8 @@ SINGLE_BATTLE_TEST("Critical hits deal 50% more damage", s16 damage) } SCENE { HP_BAR(opponent, captureDamage: &results[i].damage); } FINALLY { - EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); + EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[1].damage); + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[2].damage); } } diff --git a/test/battle/move_effect/dragon_cheer.c b/test/battle/move_effect/dragon_cheer.c index e02dd84dd283..34a7f0b05e8b 100644 --- a/test/battle/move_effect/dragon_cheer.c +++ b/test/battle/move_effect/dragon_cheer.c @@ -1,10 +1,14 @@ #include "global.h" #include "test/battle.h" +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_DRAGON_CHEER].effect == EFFECT_DRAGON_CHEER); +} + SINGLE_BATTLE_TEST("Dragon Cheer fails in a single battle") { GIVEN { - ASSUME(gMovesInfo[MOVE_DRAGON_CHEER].effect == EFFECT_DRAGON_CHEER); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); } WHEN { @@ -14,43 +18,68 @@ SINGLE_BATTLE_TEST("Dragon Cheer fails in a single battle") } } -DOUBLE_BATTLE_TEST("Dragon Cheer increases critical hit ratio by one on non-Dragon types") +DOUBLE_BATTLE_TEST("Dragon Cheer increases critical hit ratio by 1 on non-Dragon types") { - PASSES_RANDOMLY(1, 8, RNG_CRITICAL_HIT); + bool32 useDragonCheer = 0; + u32 genConfig = 0, chance = 0; + for (u32 j = GEN_1; j <= GEN_9; j++) { + PARAMETRIZE { genConfig = j; useDragonCheer = FALSE; chance = j >= GEN_7 ? 24 : 16; } // 6.25% with Wobbuffet's base speed + PARAMETRIZE { genConfig = j; useDragonCheer = TRUE; chance = 8; } // 12.5% with Wobbuffet's base speed + } + PASSES_RANDOMLY(1, chance, RNG_CRITICAL_HIT); GIVEN { - ASSUME(B_CRIT_CHANCE >= GEN_7); + WITH_CONFIG(GEN_CONFIG_CRIT_CHANCE, genConfig); ASSUME(gMovesInfo[MOVE_TACKLE].criticalHitStage == 0); - ASSUME(gMovesInfo[MOVE_DRAGON_CHEER].effect == EFFECT_DRAGON_CHEER); PLAYER(SPECIES_WOBBUFFET); - PLAYER(SPECIES_WYNAUT); + PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); } WHEN { - TURN { MOVE(playerLeft, MOVE_DRAGON_CHEER, target: playerRight); MOVE(playerRight, MOVE_TACKLE, target: opponentLeft); } + TURN { + if (useDragonCheer) + MOVE(playerLeft, MOVE_DRAGON_CHEER, target: playerRight); + MOVE(playerRight, MOVE_TACKLE, target: opponentLeft); + } } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_CHEER, playerLeft); - MESSAGE("Wynaut is getting pumped!"); + if (useDragonCheer) { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_CHEER, playerLeft); + MESSAGE("Wobbuffet is getting pumped!"); + } ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, playerRight); MESSAGE("A critical hit!"); } } -DOUBLE_BATTLE_TEST("Dragon Cheer increases critical hit ratio by two on Dragon types") +DOUBLE_BATTLE_TEST("Dragon Cheer increases critical hit ratio by 2 on Dragon types") { - PASSES_RANDOMLY(1, 2, RNG_CRITICAL_HIT); + bool32 useDragonCheer; + u32 genConfig, passes, trials; + PARAMETRIZE { genConfig = GEN_1; useDragonCheer = FALSE; passes = 25; trials = 256; } // ~9.77% with Dratini's base speed + PARAMETRIZE { genConfig = GEN_1; useDragonCheer = TRUE; passes = 25; trials = 64; } // ~39.06% with Dratini's base speed + for (u32 j = GEN_2; j <= GEN_9; j++) { + PARAMETRIZE { genConfig = j; useDragonCheer = FALSE; passes = 1; trials = j >= GEN_7 ? 24 : 16; } // ~4.16%/6.25% + PARAMETRIZE { genConfig = j; useDragonCheer = TRUE; passes = 1; trials = j >= GEN_6 ? 2 : 4; } // 50%/25% + } + PASSES_RANDOMLY(passes, trials, RNG_CRITICAL_HIT); GIVEN { - ASSUME(B_CRIT_CHANCE >= GEN_7); + WITH_CONFIG(GEN_CONFIG_CRIT_CHANCE, genConfig); ASSUME(gMovesInfo[MOVE_TACKLE].criticalHitStage == 0); - ASSUME(gMovesInfo[MOVE_DRAGON_CHEER].effect == EFFECT_DRAGON_CHEER); + ASSUME(gSpeciesInfo[SPECIES_DRATINI].baseSpeed == 50); PLAYER(SPECIES_WOBBUFFET); PLAYER(SPECIES_DRATINI); OPPONENT(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); } WHEN { - TURN { MOVE(playerLeft, MOVE_DRAGON_CHEER, target: playerRight); MOVE(playerRight, MOVE_TACKLE, target: opponentLeft); } + TURN { + if (useDragonCheer) + MOVE(playerLeft, MOVE_DRAGON_CHEER, target: playerRight); + MOVE(playerRight, MOVE_TACKLE, target: opponentLeft); + } } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_CHEER, playerLeft); - MESSAGE("Dratini is getting pumped!"); + if (useDragonCheer) { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_CHEER, playerLeft); + MESSAGE("Dratini is getting pumped!"); + } ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, playerRight); MESSAGE("A critical hit!"); } @@ -61,7 +90,6 @@ DOUBLE_BATTLE_TEST("Dragon Cheer fails if critical hit stage was already increas GIVEN { ASSUME(gMovesInfo[MOVE_SLASH].criticalHitStage == 1); ASSUME(gMovesInfo[MOVE_FOCUS_ENERGY].effect == EFFECT_FOCUS_ENERGY); - ASSUME(gMovesInfo[MOVE_DRAGON_CHEER].effect == EFFECT_DRAGON_CHEER); PLAYER(SPECIES_WOBBUFFET); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); diff --git a/test/battle/move_effect/focus_energy.c b/test/battle/move_effect/focus_energy.c new file mode 100644 index 000000000000..151a259cfdf4 --- /dev/null +++ b/test/battle/move_effect/focus_energy.c @@ -0,0 +1,41 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_FOCUS_ENERGY].effect == EFFECT_FOCUS_ENERGY); +} + +SINGLE_BATTLE_TEST("Focus Energy increases the user's critical hit ratio by 1 stage (Gen 1-2) or 2 stages (Gen 3+)") +{ + bool32 useFocusEnergy = 0; + u32 genConfig = 0, chance = 0; + for (u32 j = GEN_1; j <= GEN_9; j++) { + PARAMETRIZE { genConfig = j; useFocusEnergy = FALSE; chance = j >= GEN_7 ? 24 : 16; } // ~4.16%/6.25% with Wobbuffet's base speed + PARAMETRIZE { genConfig = j; useFocusEnergy = TRUE; + if (j >= GEN_6) + chance = 2; // 50% / 25% + else if (j >= GEN_3) + chance = 4; // 25% + else + chance = 8; // 12.5% with Wobbuffet's base speed + } + } + PASSES_RANDOMLY(1, chance, RNG_CRITICAL_HIT); + GIVEN { + WITH_CONFIG(GEN_CONFIG_CRIT_CHANCE, genConfig); + WITH_CONFIG(GEN_CONFIG_FOCUS_ENERGY_CRIT_RATIO, genConfig); + ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].baseSpeed == 33); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + if (useFocusEnergy) + TURN { MOVE(player, MOVE_FOCUS_ENERGY); } + TURN { MOVE(player, MOVE_TACKLE); } + } SCENE { + if (useFocusEnergy) + ANIMATION(ANIM_TYPE_MOVE, MOVE_FOCUS_ENERGY, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); + MESSAGE("A critical hit!"); + } +} diff --git a/test/battle/move_effect/laser_focus.c b/test/battle/move_effect/laser_focus.c new file mode 100644 index 000000000000..2f61c99ef880 --- /dev/null +++ b/test/battle/move_effect/laser_focus.c @@ -0,0 +1,23 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_LASER_FOCUS].effect == EFFECT_LASER_FOCUS); +} + +SINGLE_BATTLE_TEST("Laser Focus causes the user's move used on the next turn to result in a Critical Hit") +{ + PASSES_RANDOMLY(1, 1, RNG_CRITICAL_HIT); + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_LASER_FOCUS); } + TURN { MOVE(player, MOVE_TACKLE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_LASER_FOCUS, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); + MESSAGE("A critical hit!"); + } +} diff --git a/test/battle/move_effect/lucky_chant.c b/test/battle/move_effect/lucky_chant.c new file mode 100644 index 000000000000..31e9dce25402 --- /dev/null +++ b/test/battle/move_effect/lucky_chant.c @@ -0,0 +1,16 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Lucky Chant prevents critical hits on the user's side") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_LUCKY_CHANT].effect == EFFECT_LUCKY_CHANT); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_LUCKY_CHANT); MOVE(player, MOVE_TACKLE, criticalHit: TRUE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); + NOT MESSAGE("A critical hit!"); + } +} diff --git a/test/battle/move_effect/multi_hit.c b/test/battle/move_effect/multi_hit.c index 802c4f455eef..9309f97ed9f2 100644 --- a/test/battle/move_effect/multi_hit.c +++ b/test/battle/move_effect/multi_hit.c @@ -25,12 +25,15 @@ SINGLE_BATTLE_TEST("Multi hit Moves hit the maximum amount with Skill Link") } } -SINGLE_BATTLE_TEST("Multi hit Moves hit twice 35% of the time") +SINGLE_BATTLE_TEST("Multi hit Moves hit twice 37.5/35% of the time") { - PASSES_RANDOMLY(35, 100, RNG_HITS); + u32 genConfig, passes, trials; + PARAMETRIZE { genConfig = GEN_4; passes = 3; trials = 8; } + PARAMETRIZE { genConfig = GEN_5; passes = 7; trials = 20; } + PASSES_RANDOMLY(passes, trials, RNG_HITS); GIVEN { - ASSUME(B_MULTI_HIT_CHANCE >= GEN_5); + WITH_CONFIG(GEN_CONFIG_MULTI_HIT_CHANCE, genConfig); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); } WHEN { @@ -42,12 +45,15 @@ SINGLE_BATTLE_TEST("Multi hit Moves hit twice 35% of the time") } } -SINGLE_BATTLE_TEST("Multi hit Moves hit thrice 35% of the time") +SINGLE_BATTLE_TEST("Multi hit Moves hit thrice 37.5/35% of the time") { - PASSES_RANDOMLY(35, 100, RNG_HITS); + u32 genConfig, passes, trials; + PARAMETRIZE { genConfig = GEN_4; passes = 3; trials = 8; } + PARAMETRIZE { genConfig = GEN_5; passes = 7; trials = 20; } + PASSES_RANDOMLY(passes, trials, RNG_HITS); GIVEN { - ASSUME(B_MULTI_HIT_CHANCE >= GEN_5); + WITH_CONFIG(GEN_CONFIG_MULTI_HIT_CHANCE, genConfig); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); } WHEN { @@ -60,12 +66,15 @@ SINGLE_BATTLE_TEST("Multi hit Moves hit thrice 35% of the time") } } -SINGLE_BATTLE_TEST("Multi hit Moves hit four times 15% of the time") +SINGLE_BATTLE_TEST("Multi hit Moves hit four times 12.5/15% of the time") { - PASSES_RANDOMLY(15, 100, RNG_HITS); + u32 genConfig, passes, trials; + PARAMETRIZE { genConfig = GEN_4; passes = 1; trials = 8; } + PARAMETRIZE { genConfig = GEN_5; passes = 3; trials = 20; } + PASSES_RANDOMLY(passes, trials, RNG_HITS); GIVEN { - ASSUME(B_MULTI_HIT_CHANCE >= GEN_5); + WITH_CONFIG(GEN_CONFIG_MULTI_HIT_CHANCE, genConfig); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); } WHEN { @@ -79,12 +88,15 @@ SINGLE_BATTLE_TEST("Multi hit Moves hit four times 15% of the time") } } -SINGLE_BATTLE_TEST("Multi hit Moves hit five times 15% of the time") +SINGLE_BATTLE_TEST("Multi hit Moves hit five times 12.5/15% of the time") { - PASSES_RANDOMLY(15, 100, RNG_HITS); + u32 genConfig, passes, trials; + PARAMETRIZE { genConfig = GEN_4; passes = 1; trials = 8; } + PARAMETRIZE { genConfig = GEN_5; passes = 3; trials = 20; } + PASSES_RANDOMLY(passes, trials, RNG_HITS); GIVEN { - ASSUME(B_MULTI_HIT_CHANCE >= GEN_5); + WITH_CONFIG(GEN_CONFIG_MULTI_HIT_CHANCE, genConfig); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); } WHEN { diff --git a/test/battle/move_effects_combined/triple_arrows.c b/test/battle/move_effects_combined/triple_arrows.c index ad7878fdc93e..d0b514fd19d9 100644 --- a/test/battle/move_effects_combined/triple_arrows.c +++ b/test/battle/move_effects_combined/triple_arrows.c @@ -44,11 +44,17 @@ SINGLE_BATTLE_TEST("Triple Arrows makes the foe flinch 30% of the time") } } -SINGLE_BATTLE_TEST("Triple Arrows lands a critical hit") +SINGLE_BATTLE_TEST("Triple Arrows has an increased critical hit ratio") { - PASSES_RANDOMLY(1, 8, RNG_CRITICAL_HIT); + u32 j, genConfig = 0, passes = 0, trials = 0; + + PARAMETRIZE { genConfig = GEN_1; passes = 1; trials = 2; } // 50% with Wobbuffet's base speed + for (j = GEN_2; j <= GEN_9; j++) { + PARAMETRIZE { genConfig = GEN_2; passes = 1; trials = 8; } + } + PASSES_RANDOMLY(passes, trials, RNG_CRITICAL_HIT); GIVEN { - ASSUME(B_CRIT_CHANCE >= GEN_7); + WITH_CONFIG(GEN_CONFIG_CRIT_CHANCE, genConfig); ASSUME(gMovesInfo[MOVE_TRIPLE_ARROWS].criticalHitStage == 1); PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); diff --git a/test/battle/move_flags/critical_hit_stage.c b/test/battle/move_flags/critical_hit_stage.c new file mode 100644 index 000000000000..8660da37148f --- /dev/null +++ b/test/battle/move_flags/critical_hit_stage.c @@ -0,0 +1,24 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("criticalHitStage set to 1 increases critical hits occur at a 1/8 rate (Gen 2+) or x8 times more likely (Gen 1)") +{ + u32 j, genConfig = 0, passes = 0, trials = 0; + + PARAMETRIZE { genConfig = GEN_1; passes = 1; trials = 2; } // 50% with Wobbuffet's base speed + for (j = GEN_2; j <= GEN_9; j++) { + PARAMETRIZE { genConfig = GEN_2; passes = 1; trials = 8; } + } + PASSES_RANDOMLY(passes, trials, RNG_CRITICAL_HIT); + GIVEN { + WITH_CONFIG(GEN_CONFIG_CRIT_CHANCE, genConfig); + ASSUME(gMovesInfo[MOVE_SLASH].criticalHitStage == 1); + ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].baseSpeed == 33); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SLASH); } + } SCENE { + MESSAGE("A critical hit!"); + } +} diff --git a/test/battle/move_flags/ignores_target_ability.c b/test/battle/move_flags/ignores_target_ability.c index 2836f4838ec0..e5cd476641f6 100644 --- a/test/battle/move_flags/ignores_target_ability.c +++ b/test/battle/move_flags/ignores_target_ability.c @@ -72,3 +72,23 @@ SINGLE_BATTLE_TEST("ignoresTargetAbility moves do ignore target's abilities", s1 EXPECT_EQ(results[4].damage, results[5].damage); } } + +SINGLE_BATTLE_TEST("ignoresTargetAbility allows Pokémon with Battle Armor and Shell Armor to receive critical hits") +{ + u32 species; + u32 ability; + + PARAMETRIZE { species = SPECIES_KINGLER; ability = ABILITY_SHELL_ARMOR; } + PARAMETRIZE { species = SPECIES_ARMALDO; ability = ABILITY_BATTLE_ARMOR; } + + GIVEN { + ASSUME(gMovesInfo[MOVE_SUNSTEEL_STRIKE].ignoresTargetAbility == TRUE); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(species) { Ability(ability); } + } WHEN { + TURN { MOVE(player, MOVE_SUNSTEEL_STRIKE, criticalHit: TRUE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNSTEEL_STRIKE, player); + MESSAGE("A critical hit!"); + } +} diff --git a/test/battle/status1/paralysis.c b/test/battle/status1/paralysis.c index 85a784764ef3..4caeb3c50903 100644 --- a/test/battle/status1/paralysis.c +++ b/test/battle/status1/paralysis.c @@ -1,14 +1,16 @@ #include "global.h" #include "test/battle.h" -SINGLE_BATTLE_TEST("Paralysis reduces Speed by 50%") +SINGLE_BATTLE_TEST("Paralysis reduces Speed by 50% (Gen 7+) or 75% (Gen 1-6)") { - u16 playerSpeed; + u32 playerSpeed, genConfig; bool32 playerFirst; - PARAMETRIZE { playerSpeed = 98; playerFirst = FALSE; } - PARAMETRIZE { playerSpeed = 102; playerFirst = TRUE; } + PARAMETRIZE { playerSpeed = 196; playerFirst = FALSE; genConfig = GEN_6; } + PARAMETRIZE { playerSpeed = 204; playerFirst = TRUE; genConfig = GEN_6; } + PARAMETRIZE { playerSpeed = 98; playerFirst = FALSE; genConfig = GEN_7; } + PARAMETRIZE { playerSpeed = 102; playerFirst = TRUE; genConfig = GEN_7; } GIVEN { - ASSUME(B_PARALYSIS_SPEED >= GEN_7); + WITH_CONFIG(GEN_CONFIG_PARALYSIS_SPEED, genConfig); PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_PARALYSIS); Speed(playerSpeed); } OPPONENT(SPECIES_WOBBUFFET) { Speed(50); } } WHEN { diff --git a/test/battle/status2/confusion.c b/test/battle/status2/confusion.c index 4115123b3c11..9c7524cdec9e 100644 --- a/test/battle/status2/confusion.c +++ b/test/battle/status2/confusion.c @@ -4,11 +4,14 @@ SINGLE_BATTLE_TEST("Confusion adds a 50/33% chance to hit self with 40 power") { s16 damage[2]; + u32 genConfig, pctChance; - ASSUME(gMovesInfo[MOVE_TACKLE].power == 40); - - PASSES_RANDOMLY(B_CONFUSION_SELF_DMG_CHANCE >= GEN_7 ? 33 : 50, 100, RNG_CONFUSION); + PARAMETRIZE { genConfig = GEN_6; pctChance = 50; } + PARAMETRIZE { genConfig = GEN_7; pctChance = 33; } + PASSES_RANDOMLY(pctChance, 100, RNG_CONFUSION); GIVEN { + WITH_CONFIG(GEN_CONFIG_CONFUSION_SELF_DMG_CHANCE, genConfig); + ASSUME(gMovesInfo[MOVE_TACKLE].power == 40); PLAYER(SPECIES_WOBBUFFET) { Speed(1); }; OPPONENT(SPECIES_WOBBUFFET) { Speed(2); }; } WHEN { @@ -29,8 +32,13 @@ SINGLE_BATTLE_TEST("Confusion adds a 50/33% chance to hit self with 40 power") SINGLE_BATTLE_TEST("Confusion self hit does not consume Gems") { - PASSES_RANDOMLY(B_CONFUSION_SELF_DMG_CHANCE >= GEN_7 ? 33 : 50, 100, RNG_CONFUSION); + u32 genConfig, pctChance; + + PARAMETRIZE { genConfig = GEN_6; pctChance = 50; } + PARAMETRIZE { genConfig = GEN_7; pctChance = 33; } + PASSES_RANDOMLY(pctChance, 100, RNG_CONFUSION); GIVEN { + WITH_CONFIG(GEN_CONFIG_CONFUSION_SELF_DMG_CHANCE, genConfig); PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_NORMAL_GEM); }; OPPONENT(SPECIES_WOBBUFFET); } WHEN {