From ab24eaec108a2da0292b9817c19e561295d4e622 Mon Sep 17 00:00:00 2001 From: luuma <31407427+luuma@users.noreply.github.com> Date: Sun, 12 Apr 2026 12:09:04 +0100 Subject: [PATCH] Mega Sol and Dragonize (#9735) Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com> --- include/battle_util.h | 1 + src/battle_ai_util.c | 1 + src/battle_anim_effects_3.c | 15 +- src/battle_main.c | 30 ++- src/battle_move_resolution.c | 2 +- src/battle_script_commands.c | 19 +- src/battle_util.c | 76 ++++-- src/data/abilities.h | 4 +- .../pokemon/species_info/gen_2_families.h | 4 +- test/battle/ability/dragonize.c | 239 ++++++++++++++++++ test/battle/ability/mega_sol.c | 233 +++++++++++++++++ test/battle/ai/ai_switching.c | 2 +- test/battle/move_effect/growth.c | 32 ++- 13 files changed, 591 insertions(+), 67 deletions(-) create mode 100644 test/battle/ability/dragonize.c create mode 100644 test/battle/ability/mega_sol.c diff --git a/include/battle_util.h b/include/battle_util.h index 7cd7aae23e..7d0354be6a 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -297,6 +297,7 @@ bool32 BlocksPrankster(enum Move move, enum BattlerId battlerPrankster, enum Bat bool32 PickupHasValidTarget(enum BattlerId battler); bool32 CantPickupItem(u32 battler); u32 GetWeather(void); +u32 GetAttackerWeather(enum HoldEffect holdEffect, enum Ability ability, u32 weather); bool32 IsBattlerWeatherAffected(enum HoldEffect holdEffect, u32 weather, u32 weatherFlags); enum MoveTarget GetBattlerMoveTargetType(enum BattlerId battler, enum Move move); bool32 CanTargetBattler(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move); diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 360fab8752..bb1d02d484 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -944,6 +944,7 @@ struct SimulatedDamage AI_CalcDamage(enum Move move, enum BattlerId battlerAtk, ctx.isCrit = ShouldCalcCritDamage(&ctx); ctx.typeEffectivenessModifier = CalcTypeEffectivenessMultiplier(&ctx); + u32 movePower = GetMovePower(move); if (movePower && !IsDamageMoveUnusable(&ctx)) diff --git a/src/battle_anim_effects_3.c b/src/battle_anim_effects_3.c index 45dafc8a62..798506f8be 100644 --- a/src/battle_anim_effects_3.c +++ b/src/battle_anim_effects_3.c @@ -5670,20 +5670,19 @@ static void AnimRecycle_Step(struct Sprite *sprite) void AnimTask_GetWeather(u8 taskId) { - bool32 utilityUmbrellaAffected = GetBattlerHoldEffect(gBattleAnimAttacker) == HOLD_EFFECT_UTILITY_UMBRELLA; - + u32 weather = GetAttackerWeather(GetBattlerHoldEffect(gBattleAnimAttacker), GetBattlerAbility(gBattleAnimAttacker), gWeatherMoveAnim); gBattleAnimArgs[ARG_RET_ID] = ANIM_WEATHER_NONE; - if (gWeatherMoveAnim & B_WEATHER_SUN && !utilityUmbrellaAffected) + if (weather & B_WEATHER_SUN) gBattleAnimArgs[ARG_RET_ID] = ANIM_WEATHER_SUN; - else if (gWeatherMoveAnim & B_WEATHER_RAIN && !utilityUmbrellaAffected) + else if (weather & B_WEATHER_RAIN) gBattleAnimArgs[ARG_RET_ID] = ANIM_WEATHER_RAIN; - else if (gWeatherMoveAnim & B_WEATHER_SANDSTORM) + else if (weather & B_WEATHER_SANDSTORM) gBattleAnimArgs[ARG_RET_ID] = ANIM_WEATHER_SANDSTORM; - else if (gWeatherMoveAnim & B_WEATHER_HAIL) + else if (weather & B_WEATHER_HAIL) gBattleAnimArgs[ARG_RET_ID] = ANIM_WEATHER_HAIL; - else if (gWeatherMoveAnim & B_WEATHER_SNOW) + else if (weather & B_WEATHER_SNOW) gBattleAnimArgs[ARG_RET_ID] = ANIM_WEATHER_SNOW; - else if (gWeatherMoveAnim & B_WEATHER_FOG) + else if (weather & B_WEATHER_FOG) gBattleAnimArgs[ARG_RET_ID] = ANIM_WEATHER_FOG; DestroyAnimVisualTask(taskId); diff --git a/src/battle_main.c b/src/battle_main.c index 46dd38139c..8dda584308 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3339,7 +3339,6 @@ void FaintClearSetData(enum BattlerId battler) gBattleStruct->palaceFlags &= ~(1u << battler); if (battler == gBattlerAttacker) gBattleStruct->moldBreakerActive = FALSE; - ClearPursuitValuesIfSet(battler); if (gBattleStruct->battlerState[battler].commanderSpecies != SPECIES_NONE) @@ -5767,6 +5766,9 @@ enum Type TrySetAteType(enum Move move, enum BattlerId battlerAtk, enum Ability case ABILITY_GALVANIZE: ateType = TYPE_ELECTRIC; break; + case ABILITY_DRAGONIZE: + ateType = TYPE_DRAGON; + break; default: ateType = TYPE_NONE; break; @@ -5816,22 +5818,22 @@ enum Type GetDynamicMoveType(struct Pokemon *mon, enum Move move, enum BattlerId case EFFECT_WEATHER_BALL: if (state == MON_IN_BATTLE) { - if (HasWeatherEffect()) - { - if (gBattleWeather & B_WEATHER_RAIN && holdEffect != HOLD_EFFECT_UTILITY_UMBRELLA) - return TYPE_WATER; - else if (gBattleWeather & B_WEATHER_SANDSTORM) - return TYPE_ROCK; - else if (gBattleWeather & B_WEATHER_SUN && holdEffect != HOLD_EFFECT_UTILITY_UMBRELLA) - return TYPE_FIRE; - else if (gBattleWeather & B_WEATHER_ICY_ANY) - return TYPE_ICE; - else - return moveType; - } + u32 weather = GetAttackerWeather(holdEffect, ability, GetWeather()); + if (weather & B_WEATHER_SUN) + return TYPE_FIRE; + else if (weather & B_WEATHER_RAIN) + return TYPE_WATER; + else if (weather & B_WEATHER_SANDSTORM) + return TYPE_ROCK; + else if (weather & B_WEATHER_ICY_ANY) + return TYPE_ICE; + else + return moveType; } else { + if (ability == ABILITY_MEGA_SOL) + return TYPE_FIRE; switch (gWeatherPtr->currWeather) { case WEATHER_DROUGHT: diff --git a/src/battle_move_resolution.c b/src/battle_move_resolution.c index 43601cc362..f928c782b6 100644 --- a/src/battle_move_resolution.c +++ b/src/battle_move_resolution.c @@ -1504,7 +1504,7 @@ static bool32 CanTwoTurnMoveFireThisTurn(struct BattleCalcValues *cv) { if (gBattleMoveEffects[GetMoveEffect(cv->move)].semiInvulnerableEffect || GetMoveEffect(cv->move) == EFFECT_GEOMANCY - || !IsBattlerWeatherAffected(cv->holdEffects[cv->battlerAtk], GetWeather(), GetMoveTwoTurnAttackWeather(cv->move))) + || !(GetAttackerWeather(cv->holdEffects[cv->battlerAtk], cv->abilities[cv->battlerAtk], GetWeather()) & GetMoveTwoTurnAttackWeather(cv->move))) return FALSE; return TRUE; } diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index e4b13d2093..9618fb17e2 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1273,7 +1273,6 @@ static void Cmd_damagecalc(void) ctx.fieldStatuses = gFieldStatuses; ctx.randomFactor = TRUE; ctx.updateFlags = TRUE; - if (IsSpreadMove(GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove))) { for (enum BattlerId battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) @@ -9369,19 +9368,20 @@ static void Cmd_recoverbasedonsunlight(void) if (gBattleMons[gBattlerAttacker].hp != gBattleMons[gBattlerAttacker].maxHP) { s32 recoverAmount = 0; + u32 weather = GetAttackerWeather(GetBattlerHoldEffect(gBattlerAttacker), GetBattlerAbility(gBattlerAttacker), GetWeather()); if (GetMoveEffect(gCurrentMove) == EFFECT_SHORE_UP) { - if (HasWeatherEffect() && gBattleWeather & B_WEATHER_SANDSTORM) + if (weather & B_WEATHER_SANDSTORM) recoverAmount = 20 * GetNonDynamaxMaxHP(gBattlerAttacker) / 30; else recoverAmount = GetNonDynamaxMaxHP(gBattlerAttacker) / 2; } else if (GetConfig(B_TIME_OF_DAY_HEALING_MOVES) != GEN_2) { - if (!(gBattleWeather & B_WEATHER_ANY) || !HasWeatherEffect() || GetBattlerHoldEffect(gBattlerAttacker) == HOLD_EFFECT_UTILITY_UMBRELLA) - recoverAmount = GetNonDynamaxMaxHP(gBattlerAttacker) / 2; - else if (gBattleWeather & B_WEATHER_SUN) + if (weather & B_WEATHER_SUN) recoverAmount = 20 * GetNonDynamaxMaxHP(gBattlerAttacker) / 30; + else if (!(gBattleWeather & B_WEATHER_ANY) || !HasWeatherEffect() || GetBattlerHoldEffect(gBattlerAttacker) == HOLD_EFFECT_UTILITY_UMBRELLA) + recoverAmount = GetNonDynamaxMaxHP(gBattlerAttacker) / 2; else // not sunny weather recoverAmount = GetNonDynamaxMaxHP(gBattlerAttacker) / 4; } @@ -9409,11 +9409,10 @@ static void Cmd_recoverbasedonsunlight(void) healingModifier = 1; break; } - - if (!(gBattleWeather & B_WEATHER_ANY) || !HasWeatherEffect() || GetBattlerHoldEffect(gBattlerAttacker) == HOLD_EFFECT_UTILITY_UMBRELLA) - recoverAmount = healingModifier * GetNonDynamaxMaxHP(gBattlerAttacker) / 4; - else if (gBattleWeather & B_WEATHER_SUN) + if (weather & B_WEATHER_SUN) recoverAmount = healingModifier * GetNonDynamaxMaxHP(gBattlerAttacker) / 2; + else if (!(gBattleWeather & B_WEATHER_ANY) || !HasWeatherEffect() || GetBattlerHoldEffect(gBattlerAttacker) == HOLD_EFFECT_UTILITY_UMBRELLA) + recoverAmount = healingModifier * GetNonDynamaxMaxHP(gBattlerAttacker) / 4; else // not sunny weather recoverAmount = healingModifier * GetNonDynamaxMaxHP(gBattlerAttacker) / 8; @@ -14712,7 +14711,7 @@ void BS_JumpIfWeatherAffected(void) { NATIVE_ARGS(u16 flags, const u8 *jumpInstr); u32 weather = cmd->flags; - if (IsBattlerWeatherAffected(GetBattlerHoldEffect(gBattlerAttacker), GetWeather(), weather)) + if (GetAttackerWeather(GetBattlerHoldEffect(gBattlerAttacker), GetBattlerAbility(gBattlerAttacker), GetWeather()) & weather) gBattlescriptCurrInstr = cmd->jumpInstr; else gBattlescriptCurrInstr = cmd->nextInstr; diff --git a/src/battle_util.c b/src/battle_util.c index 45ea902dd2..2aad8dd849 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -6113,7 +6113,7 @@ static inline u32 CalcMoveBasePower(struct DamageContext *ctx) basePower *= 2; break; case EFFECT_WEATHER_BALL: - if (ctx->weather & B_WEATHER_ANY) + if (GetAttackerWeather(ctx->holdEffectAtk, ctx->abilityAtk, ctx->weather) & B_WEATHER_ANY) basePower *= 2; break; case EFFECT_PURSUIT: @@ -6345,8 +6345,9 @@ static inline u32 CalcMoveBasePowerAfterModifiers(struct DamageContext *ctx) break; } case EFFECT_SOLAR_BEAM: - if ((GetConfig(B_SANDSTORM_SOLAR_BEAM) >= GEN_3 && IsBattlerWeatherAffected(ctx->holdEffectAtk, ctx->weather, B_WEATHER_LOW_LIGHT)) - || IsBattlerWeatherAffected(ctx->holdEffectAtk, ctx->weather, (B_WEATHER_RAIN | B_WEATHER_ICY_ANY | B_WEATHER_FOG))) // Excludes Sandstorm + u32 weather = GetAttackerWeather(ctx->holdEffectAtk, ctx->abilityAtk, ctx->weather); + if ((GetConfig(B_SANDSTORM_SOLAR_BEAM) >= GEN_3 && weather & B_WEATHER_LOW_LIGHT) + || weather & (B_WEATHER_RAIN | B_WEATHER_ICY_ANY | B_WEATHER_FOG)) // Excludes Sandstorm modifier = uq4_12_multiply(modifier, UQ_4_12(0.5)); break; case EFFECT_STOMPING_TANTRUM: @@ -6479,6 +6480,10 @@ static inline u32 CalcMoveBasePowerAfterModifiers(struct DamageContext *ctx) if (moveType == TYPE_FLYING && gBattleStruct->battlerState[battlerAtk].ateBoost) modifier = uq4_12_multiply(modifier, UQ_4_12(GetConfig(B_ATE_MULTIPLIER) >= GEN_7 ? 1.2 : 1.3)); break; + case ABILITY_DRAGONIZE: + if (moveType == TYPE_DRAGON && gBattleStruct->battlerState[battlerAtk].ateBoost) + modifier = uq4_12_multiply(modifier, UQ_4_12(GetConfig(B_ATE_MULTIPLIER) >= GEN_7 ? 1.2 : 1.3)); + break; case ABILITY_NORMALIZE: if (moveType == TYPE_NORMAL && gBattleStruct->battlerState[battlerAtk].ateBoost && GetConfig(B_ATE_MULTIPLIER) >= GEN_7) modifier = uq4_12_multiply(modifier, UQ_4_12(1.2)); @@ -6970,7 +6975,6 @@ static inline u32 CalcDefenseStat(struct DamageContext *ctx) enum BattlerId battlerDef = ctx->battlerDef; enum Move move = ctx->move; enum BattleMoveEffects moveEffect = GetMoveEffect(move); - def = gBattleMons[battlerDef].defense; spDef = gBattleMons[battlerDef].spDefense; @@ -7054,7 +7058,7 @@ static inline u32 CalcDefenseStat(struct DamageContext *ctx) } break; case ABILITY_FLOWER_GIFT: - if (gBattleMons[battlerDef].species == SPECIES_CHERRIM_SUNSHINE && IsBattlerWeatherAffected(GetBattlerHoldEffect(battlerDef), GetWeather(), B_WEATHER_SUN) && !usesDefStat) + if (gBattleMons[battlerDef].species == SPECIES_CHERRIM_SUNSHINE && IsBattlerWeatherAffected(ctx->holdEffectDef, ctx->weather, B_WEATHER_SUN) && !usesDefStat) modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5)); break; case ABILITY_PROTOSYNTHESIS: @@ -7137,11 +7141,16 @@ static inline u32 CalcDefenseStat(struct DamageContext *ctx) } // sandstorm sp.def boost for rock types - if (GetConfig(B_SANDSTORM_SPDEF_BOOST) >= GEN_4 && IS_BATTLER_OF_TYPE(battlerDef, TYPE_ROCK) && IsBattlerWeatherAffected(ctx->holdEffectDef, ctx->weather, B_WEATHER_SANDSTORM) && !usesDefStat) + if (GetConfig(B_SANDSTORM_SPDEF_BOOST) >= GEN_4 + && IS_BATTLER_OF_TYPE(battlerDef, TYPE_ROCK) + && GetAttackerWeather(ctx->holdEffectAtk, ctx->abilityAtk, ctx->weather) & B_WEATHER_SANDSTORM + && !usesDefStat) modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5)); // snow def boost for ice types - if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_ICE) && IsBattlerWeatherAffected(ctx->holdEffectDef, ctx->weather, B_WEATHER_SNOW) && usesDefStat) - modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5)); + if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_ICE) + && GetAttackerWeather(ctx->holdEffectAtk, ctx->abilityAtk, ctx->weather) & B_WEATHER_SNOW + && usesDefStat) + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5)); modifier = ApplyDefensiveBadgeBoost(modifier, battlerDef, move); @@ -7187,25 +7196,28 @@ static inline uq4_12_t GetSameTypeAttackBonusModifier(struct DamageContext *ctx) // Utility Umbrella holders take normal damage from what would be rain- and sun-weakened attacks. static uq4_12_t GetWeatherDamageModifier(struct DamageContext *ctx) { - if (ctx->weather == B_WEATHER_NONE) - return UQ_4_12(1.0); - if (GetMoveEffect(ctx->move) == EFFECT_HYDRO_STEAM && (ctx->weather & B_WEATHER_SUN) && ctx->holdEffectAtk != HOLD_EFFECT_UTILITY_UMBRELLA) + u32 attackerWeather = GetAttackerWeather(ctx->holdEffectAtk, ctx->abilityAtk, ctx->weather); + if ((attackerWeather | ctx->weather) == B_WEATHER_NONE) + return UQ_4_12(1.0);// This early exit helps limit AI thinking time + if (GetMoveEffect(ctx->move) == EFFECT_HYDRO_STEAM && (attackerWeather & B_WEATHER_SUN)) return UQ_4_12(1.5); if (ctx->holdEffectDef == HOLD_EFFECT_UTILITY_UMBRELLA) return UQ_4_12(1.0); - if (ctx->weather & B_WEATHER_RAIN) - { - if (ctx->moveType != TYPE_FIRE && ctx->moveType != TYPE_WATER) - return UQ_4_12(1.0); - return (ctx->moveType == TYPE_FIRE) ? UQ_4_12(0.5) : UQ_4_12(1.5); - } - if (ctx->weather & B_WEATHER_SUN) + if (ctx->weather & B_WEATHER_SUN || attackerWeather & B_WEATHER_SUN) // called because utility umbrella is only active on the defender for this calc. { if (ctx->moveType != TYPE_FIRE && ctx->moveType != TYPE_WATER) return UQ_4_12(1.0); return (ctx->moveType == TYPE_WATER) ? UQ_4_12(0.5) : UQ_4_12(1.5); } + + if (ctx->weather & B_WEATHER_RAIN || attackerWeather & B_WEATHER_RAIN) + { + if (ctx->moveType != TYPE_FIRE && ctx->moveType != TYPE_WATER) + return UQ_4_12(1.0); + return (ctx->moveType == TYPE_FIRE) ? UQ_4_12(0.5) : UQ_4_12(1.5); + } + return UQ_4_12(1.0); } @@ -9289,12 +9301,24 @@ u32 GetWeather(void) return gBattleWeather; } -bool32 IsBattlerWeatherAffected(enum HoldEffect holdEffect, u32 weather, u32 weatherFlags) +u32 GetAttackerWeather(enum HoldEffect holdEffect, enum Ability ability, u32 weather) { - if (weather == B_WEATHER_NONE || !(gBattleWeather & weatherFlags)) + if (ability == ABILITY_MEGA_SOL) + return B_WEATHER_SUN; + + if (holdEffect == HOLD_EFFECT_UTILITY_UMBRELLA) + return weather & ~(B_WEATHER_SUN | B_WEATHER_RAIN); // This was assumed not to block mega sol, like cloud nine doesn't. + + return weather; +} + + +bool32 IsBattlerWeatherAffected(enum HoldEffect holdEffect, u32 weather, u32 weatherFlags)// note: should probably be turned into something like "getbattlerweather", returning weather flags +{ + if (weather & (B_WEATHER_SUN | B_WEATHER_RAIN) && holdEffect == HOLD_EFFECT_UTILITY_UMBRELLA) return FALSE; - if (weather & (B_WEATHER_SUN | B_WEATHER_RAIN) && holdEffect == HOLD_EFFECT_UTILITY_UMBRELLA) + if (weather == B_WEATHER_NONE || !(gBattleWeather & weatherFlags)) return FALSE; return TRUE; @@ -10175,7 +10199,7 @@ bool32 CanMoveSkipAccuracyCalc(enum BattlerId battlerAtk, enum BattlerId battler if (!effect && HasWeatherEffect()) { - if (MoveAlwaysHitsInRain(move) && IsBattlerWeatherAffected(GetBattlerHoldEffect(battlerDef), GetWeather(), B_WEATHER_RAIN)) + if (MoveAlwaysHitsInRain(move) && IsBattlerWeatherAffected(GetBattlerHoldEffect(battlerDef), gBattleWeather, B_WEATHER_RAIN))// Check mega sol interaction then update to GetAttackerWeather. effect = TRUE; else if ((gBattleWeather & B_WEATHER_ICY_ANY) && MoveAlwaysHitsInHailSnow(move)) effect = TRUE; @@ -10196,7 +10220,6 @@ u32 GetTotalAccuracy(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum s8 buff, accStage, evasionStage; u32 atkParam = GetBattlerHoldEffectParam(battlerAtk); u32 defParam = GetBattlerHoldEffectParam(battlerDef); - gPotentialItemEffectBattler = battlerDef; accStage = gBattleMons[battlerAtk].statStages[STAT_ACC]; evasionStage = gBattleMons[battlerDef].statStages[STAT_EVASION]; @@ -10219,8 +10242,9 @@ u32 GetTotalAccuracy(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum buff = MAX_STAT_STAGE; moveAcc = GetMoveAccuracy(move); + // Check Thunder and Hurricane on sunny weather. - if (IsBattlerWeatherAffected(defHoldEffect, GetWeather(), B_WEATHER_SUN) && MoveHas50AccuracyInSun(move)) + if (IsBattlerWeatherAffected(defHoldEffect, GetWeather(), B_WEATHER_SUN) && MoveHas50AccuracyInSun(move)) moveAcc = 50; // Check Wonder Skin. if (defAbility == ABILITY_WONDER_SKIN && IsBattleMoveStatus(move) && moveAcc > 50) @@ -10250,11 +10274,11 @@ u32 GetTotalAccuracy(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum switch (defAbility) { case ABILITY_SAND_VEIL: - if (gBattleWeather & B_WEATHER_SANDSTORM && HasWeatherEffect()) + if (GetAttackerWeather(atkHoldEffect, atkAbility, GetWeather()) & B_WEATHER_SANDSTORM) calc = (calc * 80) / 100; // 1.2 sand veil loss break; case ABILITY_SNOW_CLOAK: - if ((gBattleWeather & B_WEATHER_ICY_ANY) && HasWeatherEffect()) + if (GetAttackerWeather(atkHoldEffect, atkAbility, GetWeather()) & B_WEATHER_ICY_ANY) calc = (calc * 80) / 100; // 1.2 snow cloak loss break; case ABILITY_TANGLED_FEET: diff --git a/src/data/abilities.h b/src/data/abilities.h index f6a4b054ca..8451ce198b 100644 --- a/src/data/abilities.h +++ b/src/data/abilities.h @@ -2423,7 +2423,7 @@ const struct AbilityInfo gAbilitiesInfo[ABILITIES_COUNT] = [ABILITY_DRAGONIZE] = { .name = _("Dragonize"), - .description = COMPOUND_STRING("Unimplemented."), + .description = COMPOUND_STRING("Normal moves turn Dragon."), }, [ABILITY_313] = @@ -2441,7 +2441,7 @@ const struct AbilityInfo gAbilitiesInfo[ABILITIES_COUNT] = [ABILITY_MEGA_SOL] = { .name = _("Mega Sol"), - .description = COMPOUND_STRING("Unimplemented."), + .description = COMPOUND_STRING("Acts like under sun."), }, [ABILITY_316] = diff --git a/src/data/pokemon/species_info/gen_2_families.h b/src/data/pokemon/species_info/gen_2_families.h index f2841d5dcc..d8d2ff9116 100644 --- a/src/data/pokemon/species_info/gen_2_families.h +++ b/src/data/pokemon/species_info/gen_2_families.h @@ -165,7 +165,7 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_MONSTER, EGG_GROUP_GRASS), - .abilities = { ABILITY_OVERGROW, ABILITY_NONE, ABILITY_LEAF_GUARD }, + .abilities = { ABILITY_MEGA_SOL, ABILITY_NONE, ABILITY_MEGA_SOL }, .bodyColor = BODY_COLOR_GREEN, .speciesName = _("Meganium"), .cryId = CRY_MEGANIUM, @@ -814,7 +814,7 @@ const struct SpeciesInfo gSpeciesInfoGen2[] = .friendship = STANDARD_FRIENDSHIP, .growthRate = GROWTH_MEDIUM_SLOW, .eggGroups = MON_EGG_GROUPS(EGG_GROUP_MONSTER, EGG_GROUP_WATER_1), - .abilities = { ABILITY_DRAGONIZE, ABILITY_DRAGONIZE, ABILITY_DRAGONIZE }, + .abilities = { ABILITY_DRAGONIZE, ABILITY_NONE, ABILITY_DRAGONIZE }, .bodyColor = BODY_COLOR_BLUE, .speciesName = _("Feraligatr"), #if P_MODIFIED_MEGA_CRIES diff --git a/test/battle/ability/dragonize.c b/test/battle/ability/dragonize.c new file mode 100644 index 0000000000..0909d8fe0f --- /dev/null +++ b/test/battle/ability/dragonize.c @@ -0,0 +1,239 @@ +#include "global.h" +#include "test/battle.h" +#include "berry.h" + +ASSUMPTIONS +{ + ASSUME(GetMoveType(MOVE_SCRATCH) == TYPE_NORMAL); + ASSUME(GetMovePower(MOVE_SCRATCH) > 0); +} + +SINGLE_BATTLE_TEST("Dragonize turns a Normal-type move into a dragon-type move") +{ + GIVEN { + PLAYER(SPECIES_DRUDDIGON); + OPPONENT(SPECIES_FERALIGATR_MEGA) { Ability(ABILITY_DRAGONIZE); } + } WHEN { + TURN { MOVE(opponent, MOVE_SCRATCH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent); + MESSAGE("It's super effective!"); + } +} + +SINGLE_BATTLE_TEST("Dragonize boosts power of affected moves by 20% (Gen7+) or 30% (Gen1-6)", s16 damage) +{ + enum Ability ability; + u32 genConfig; + PARAMETRIZE { ability = ABILITY_NONE; genConfig = GEN_7; } + PARAMETRIZE { ability = ABILITY_NONE; genConfig = GEN_6; } + PARAMETRIZE { ability = ABILITY_DRAGONIZE; genConfig = GEN_7; } + PARAMETRIZE { ability = ABILITY_DRAGONIZE; genConfig = GEN_6; } + + GIVEN { + WITH_CONFIG(B_ATE_MULTIPLIER, genConfig); + PLAYER(SPECIES_DRUDDIGON) { Ability(ability); Moves(MOVE_TACKLE); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TACKLE); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + if (genConfig >= GEN_7) + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.8), results[2].damage); // STAB + ate + else + EXPECT_MUL_EQ(results[1].damage, Q_4_12(1.95), results[3].damage); // STAB + ate + } +} + +SINGLE_BATTLE_TEST("Dragonize doesn't affect Weather Ball's type", s16 damage) +{ + enum Move move; + enum Ability ability; + PARAMETRIZE { move = MOVE_CELEBRATE; ability = ABILITY_NONE; } + PARAMETRIZE { move = MOVE_SUNNY_DAY; ability = ABILITY_NONE; } + PARAMETRIZE { move = MOVE_CELEBRATE; ability = ABILITY_DRAGONIZE; } + PARAMETRIZE { move = MOVE_SUNNY_DAY; ability = ABILITY_DRAGONIZE; } + GIVEN { + ASSUME(GetMoveEffect(MOVE_WEATHER_BALL) == EFFECT_WEATHER_BALL); + ASSUME(GetSpeciesType(SPECIES_PINSIR, 0) == TYPE_BUG); + PLAYER(SPECIES_DRUDDIGON) { Ability(ability); } + OPPONENT(SPECIES_PINSIR); + } WHEN { + TURN { MOVE(player, move); } + TURN { MOVE(player, MOVE_WEATHER_BALL); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + if (move == MOVE_SUNNY_DAY) + MESSAGE("It's super effective!"); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(6.0), results[1].damage); // double base power + type effectiveness + sun 50% boost + EXPECT_MUL_EQ(results[2].damage, Q_4_12(6.0), results[3].damage); // double base power + type effectiveness + sun 50% boost + EXPECT_MUL_EQ(results[2].damage, Q_4_12(1.0), results[0].damage); // identical test + EXPECT_EQ(results[1].damage, results[3].damage); + } +} + +SINGLE_BATTLE_TEST("Dragonize doesn't affect Natural Gift's type") +{ + enum Ability ability; + PARAMETRIZE { ability = ABILITY_NONE; } + PARAMETRIZE { ability = ABILITY_DRAGONIZE; } + GIVEN { + ASSUME(GetMoveEffect(MOVE_NATURAL_GIFT) == EFFECT_NATURAL_GIFT); + ASSUME(gBerries[ItemIdToBerryType(ITEM_ORAN_BERRY)].naturalGiftType == TYPE_POISON); + ASSUME(GetSpeciesType(SPECIES_BELDUM, 0) == TYPE_STEEL); + PLAYER(SPECIES_FERALIGATR_MEGA) { Ability(ability); Item(ITEM_ORAN_BERRY); } + OPPONENT(SPECIES_BELDUM); + } WHEN { + TURN { MOVE(player, MOVE_NATURAL_GIFT); } + } SCENE { + NOT { ANIMATION(ANIM_TYPE_MOVE, MOVE_NATURAL_GIFT, player); } + MESSAGE("It doesn't affect the opposing Beldum…"); + } +} + +SINGLE_BATTLE_TEST("Dragonize doesn't affect Judgment / Techno Blast / Multi-Attack's type") +{ + enum Move move; + enum Item item; + PARAMETRIZE { move = MOVE_JUDGMENT; item = ITEM_ZAP_PLATE; } + PARAMETRIZE { move = MOVE_TECHNO_BLAST; item = ITEM_SHOCK_DRIVE; } + PARAMETRIZE { move = MOVE_MULTI_ATTACK; item = ITEM_ELECTRIC_MEMORY; } + GIVEN { + ASSUME(GetMoveEffect(MOVE_JUDGMENT) == EFFECT_CHANGE_TYPE_ON_ITEM); + ASSUME(GetMoveEffect(MOVE_TECHNO_BLAST) == EFFECT_CHANGE_TYPE_ON_ITEM); + ASSUME(GetMoveEffect(MOVE_MULTI_ATTACK) == EFFECT_CHANGE_TYPE_ON_ITEM); + ASSUME(gItemsInfo[ITEM_ZAP_PLATE].holdEffect == HOLD_EFFECT_PLATE); + ASSUME(gItemsInfo[ITEM_ZAP_PLATE].secondaryId == TYPE_ELECTRIC); + ASSUME(gItemsInfo[ITEM_SHOCK_DRIVE].holdEffect == HOLD_EFFECT_DRIVE); + ASSUME(gItemsInfo[ITEM_SHOCK_DRIVE].secondaryId == TYPE_ELECTRIC); + ASSUME(gItemsInfo[ITEM_ELECTRIC_MEMORY].holdEffect == HOLD_EFFECT_MEMORY); + ASSUME(gItemsInfo[ITEM_ELECTRIC_MEMORY].secondaryId == TYPE_ELECTRIC); + ASSUME(GetSpeciesType(SPECIES_DIGLETT, 0) == TYPE_GROUND); + PLAYER(SPECIES_FERALIGATR_MEGA) { Ability(ABILITY_DRAGONIZE); Item(item); } + OPPONENT(SPECIES_DIGLETT); + } WHEN { + TURN { MOVE(player, move); } + } SCENE { + NOT { ANIMATION(ANIM_TYPE_MOVE, move, player); } + MESSAGE("It doesn't affect the opposing Diglett…"); + } +} + +SINGLE_BATTLE_TEST("Dragonize doesn't affect Hidden Power's type") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_HIDDEN_POWER) == EFFECT_HIDDEN_POWER); + ASSUME(gTypesInfo[TYPE_ELECTRIC].isHiddenPowerType == TRUE); + ASSUME(GetSpeciesType(SPECIES_DIGLETT, 0) == TYPE_GROUND); + PLAYER(SPECIES_FERALIGATR_MEGA) { Ability(ABILITY_DRAGONIZE); HPIV(31); AttackIV(31); DefenseIV(31); SpAttackIV(30); SpDefenseIV(31); SpeedIV(31); } // HP Electric + OPPONENT(SPECIES_DIGLETT); + } WHEN { + TURN { MOVE(player, MOVE_HIDDEN_POWER); } + } SCENE { + NOT { ANIMATION(ANIM_TYPE_MOVE, MOVE_HIDDEN_POWER, player); } + MESSAGE("It doesn't affect the opposing Diglett…"); + } +} + +SINGLE_BATTLE_TEST("Dragonize doesn't override Electrify") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_ELECTRIFY) == EFFECT_ELECTRIFY); + ASSUME(GetSpeciesType(SPECIES_SANDSHREW, 0) == TYPE_GROUND || GetSpeciesType(SPECIES_SANDSHREW, 1) == TYPE_GROUND); + PLAYER(SPECIES_FERALIGATR_MEGA) { Ability(ABILITY_DRAGONIZE); } + OPPONENT(SPECIES_SANDSHREW); + } WHEN { + TURN { MOVE(opponent, MOVE_ELECTRIFY); MOVE(player, MOVE_SCRATCH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRIFY, opponent); + NOT { ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player); } + MESSAGE("It doesn't affect the opposing Sandshrew…"); + } +} + +SINGLE_BATTLE_TEST("Dragonize overrides Ion Deluge") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_ION_DELUGE) == EFFECT_ION_DELUGE); + ASSUME(GetSpeciesType(SPECIES_DRUDDIGON, 0) == TYPE_DRAGON || GetSpeciesType(SPECIES_DRUDDIGON, 1) == TYPE_DRAGON); + PLAYER(SPECIES_FERALIGATR_MEGA) { Ability(ABILITY_DRAGONIZE); } + OPPONENT(SPECIES_DRUDDIGON); + } WHEN { + TURN { MOVE(opponent, MOVE_ION_DELUGE); MOVE(player, MOVE_SCRATCH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ION_DELUGE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player); + MESSAGE("It's super effective!"); + } +} + +SINGLE_BATTLE_TEST("Dragonize changes Tera Blast's type when not Terastallized") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_TERA_BLAST) == EFFECT_TERA_BLAST); + ASSUME(GetMoveType(MOVE_TERA_BLAST) == TYPE_NORMAL); + ASSUME(GetSpeciesType(SPECIES_CUFANT, 0) == TYPE_STEEL || GetSpeciesType(SPECIES_CUFANT, 1) == TYPE_STEEL); + PLAYER(SPECIES_FERALIGATR_MEGA) { Ability(ABILITY_DRAGONIZE); } + OPPONENT(SPECIES_CUFANT); + } WHEN { + TURN { MOVE(player, MOVE_TERA_BLAST); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player); + MESSAGE("It's not very effective…"); + } +} + +SINGLE_BATTLE_TEST("Dragonize doesn't change Tera Blast's type when Terastallized") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_TERA_BLAST) == EFFECT_TERA_BLAST); + ASSUME(GetMoveType(MOVE_TERA_BLAST) == TYPE_NORMAL); + ASSUME(GetSpeciesType(SPECIES_MISDREAVUS, 0) == TYPE_GHOST); + PLAYER(SPECIES_FERALIGATR_MEGA) { Ability(ABILITY_DRAGONIZE); TeraType(TYPE_NORMAL); } + OPPONENT(SPECIES_MISDREAVUS); + } WHEN { + TURN { MOVE(player, MOVE_TERA_BLAST, gimmick: GIMMICK_TERA); } + } SCENE { + NOT { ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player); } + MESSAGE("It doesn't affect the opposing Misdreavus…"); + } +} + +SINGLE_BATTLE_TEST("Dragonize doesn't affect Terrain Pulse's type") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_TERRAIN_PULSE) == EFFECT_TERRAIN_PULSE); + ASSUME(GetMoveType(MOVE_TERRAIN_PULSE) == TYPE_NORMAL); + ASSUME(GetSpeciesType(SPECIES_SANDSHREW, 0) == TYPE_GROUND || GetSpeciesType(SPECIES_SANDSHREW, 1) == TYPE_GROUND); + PLAYER(SPECIES_FERALIGATR_MEGA) { Ability(ABILITY_DRAGONIZE); } + OPPONENT(SPECIES_SANDSHREW); + } WHEN { + TURN { MOVE(opponent, MOVE_ELECTRIC_TERRAIN); MOVE(player, MOVE_CELEBRATE); } + TURN { MOVE(player, MOVE_TERRAIN_PULSE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRIC_TERRAIN, opponent); + NOT { ANIMATION(ANIM_TYPE_MOVE, MOVE_TERRAIN_PULSE, player); } + MESSAGE("It doesn't affect the opposing Sandshrew…"); + } +} + +SINGLE_BATTLE_TEST("Dragonize doesn't affect damaging Z-Move types") +{ + GIVEN { + ASSUME(GetMoveType(MOVE_SCRATCH) == TYPE_NORMAL); + ASSUME(GetSpeciesType(SPECIES_BAGON, 0) == TYPE_DRAGON || GetSpeciesType(SPECIES_BAGON, 1) == TYPE_DRAGON); + PLAYER(SPECIES_FERALIGATR_MEGA) { Ability(ABILITY_DRAGONIZE); Item(ITEM_NORMALIUM_Z); } + OPPONENT(SPECIES_BAGON); + } WHEN { + TURN { MOVE(player, MOVE_SCRATCH, gimmick: GIMMICK_Z_MOVE); } + } SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BREAKNECK_BLITZ, player); + NOT { MESSAGE("It's super effective!"); } + } +} + +TO_DO_BATTLE_TEST("Dragonize doesn't affect Max Strike's type"); +TO_DO_BATTLE_TEST("Confirm behavioural match with other -ate abilities");// we assume that it behaves like Pixilate. diff --git a/test/battle/ability/mega_sol.c b/test/battle/ability/mega_sol.c new file mode 100644 index 0000000000..8e8452313a --- /dev/null +++ b/test/battle/ability/mega_sol.c @@ -0,0 +1,233 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Mega Sol multiplies the power of Fire-type moves by 1.5x", s16 damage) +{ + ASSUME(GetMoveType(MOVE_EMBER) == TYPE_FIRE); + + enum Ability ability; + PARAMETRIZE { ability = ABILITY_OVERGROW;} + PARAMETRIZE { ability = ABILITY_MEGA_SOL;} + GIVEN { + PLAYER(SPECIES_MEGANIUM) { Ability(ability);} + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_EMBER); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_EMBER, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Mega Sol halves the power of the user's Water-type moves", s16 damage) +{ + + enum Ability ability; + PARAMETRIZE { ability = ABILITY_OVERGROW;} + PARAMETRIZE { ability = ABILITY_MEGA_SOL;} + + GIVEN { + ASSUME(GetMoveType(MOVE_WATER_GUN) == TYPE_WATER); + PLAYER(SPECIES_MEGANIUM_MEGA) { Ability(ability);} + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_WATER_GUN); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Weather Ball doubles its power and turns to a Fire-type move if user has Mega Sol", s16 damage) +{ + enum Ability ability; + PARAMETRIZE { ability = ABILITY_OVERGROW;} + PARAMETRIZE { ability = ABILITY_MEGA_SOL;} + + GIVEN { + ASSUME(GetMoveEffect(MOVE_WEATHER_BALL) == EFFECT_WEATHER_BALL); + PLAYER(SPECIES_MEGANIUM_MEGA) { Ability(ability);} + OPPONENT(SPECIES_PINSIR){HP(9999); MaxHP(9999);} + } WHEN { + TURN { MOVE(player, MOVE_WEATHER_BALL); } + } SCENE { + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(6.0), results[1].damage); // double base power + type effectiveness + sun 50% boost + } +} + +SINGLE_BATTLE_TEST("Synthesis recovers 2/3 of the user's max HP if user has Mega Sol (Gen3+)") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_SYNTHESIS) == EFFECT_SYNTHESIS); + WITH_CONFIG(B_TIME_OF_DAY_HEALING_MOVES, GEN_3); + PLAYER(SPECIES_MEGANIUM) { HP(1); MaxHP(300); Ability(ABILITY_MEGA_SOL); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SYNTHESIS); } + } SCENE { + HP_BAR(player, damage: -(300 / 1.5)); + } +} + +SINGLE_BATTLE_TEST("Mega Sol ignores Sandstorm's solarbeam power reduction, and its rock defense boost", s16 damage) +{ + enum Ability ability; + PARAMETRIZE { ability = ABILITY_OVERGROW;} + PARAMETRIZE { ability = ABILITY_MEGA_SOL;} + + GIVEN { + ASSUME(GetMoveEffect(MOVE_SOLARBEAM) == EFFECT_SOLAR_BEAM); + ASSUME(GetMoveType(MOVE_SOLARBEAM) == TYPE_GRASS); + PLAYER(SPECIES_MEGANIUM_MEGA) { Ability(ability);} + OPPONENT(SPECIES_BASTIODON) { Ability(ABILITY_SAND_STREAM);} + } WHEN { + TURN {} + TURN { MOVE(player, MOVE_SOLAR_BEAM); } + if (ability == ABILITY_OVERGROW) { + TURN { SKIP_TURN(player); } + } + } SCENE { + HP_BAR(player); // checking sandstorm occurred + ANIMATION(ANIM_TYPE_MOVE, MOVE_SOLAR_BEAM, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(3), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Mega Sol doesn't trigger the foe's Leaf Guard", s16 damage) +{ + enum Move move; + PARAMETRIZE { move = MOVE_CELEBRATE;} + PARAMETRIZE { move = MOVE_SUNNY_DAY;} + + GIVEN { + WITH_CONFIG(B_SANDSTORM_SOLAR_BEAM, GEN_3); + ASSUME(GetMoveEffect(MOVE_WILL_O_WISP) == EFFECT_NON_VOLATILE_STATUS); + ASSUME(GetMoveNonVolatileStatus(MOVE_WILL_O_WISP) == MOVE_EFFECT_BURN); + PLAYER(SPECIES_MEGANIUM_MEGA) { Ability(ABILITY_MEGA_SOL);} + OPPONENT(SPECIES_LEAFEON) { Ability(ABILITY_LEAF_GUARD);} + } WHEN { + TURN { MOVE(player, move); } + TURN { MOVE(player, MOVE_WILL_O_WISP); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, player); + if (move == MOVE_CELEBRATE) { + ANIMATION(ANIM_TYPE_MOVE, MOVE_WILL_O_WISP, player); + STATUS_ICON(opponent, STATUS1_BURN); + } + else { + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_WILL_O_WISP, player); + ABILITY_POPUP(opponent, ABILITY_LEAF_GUARD); + MESSAGE("It doesn't affect the opposing Leafeon…"); + NOT STATUS_ICON(opponent, STATUS1_BURN); + } + } +} + + +SINGLE_BATTLE_TEST("Mega Sol ignores Cloud Nine", s16 damage) +{ + ASSUME(GetMoveType(MOVE_EMBER) == TYPE_FIRE); + + enum Ability ability; + PARAMETRIZE { ability = ABILITY_OVERGROW;} + PARAMETRIZE { ability = ABILITY_MEGA_SOL;} + GIVEN { + PLAYER(SPECIES_MEGANIUM_MEGA) { Ability(ability);} + OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_CLOUD_NINE); HP(9999); } + } WHEN { + TURN { MOVE(player, MOVE_EMBER); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_EMBER, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Solar Beam does not need a charging turn if user has Mega Sol") +{ + enum Ability ability; + + PARAMETRIZE { ability = ABILITY_MEGA_SOL; } + PARAMETRIZE { ability = ABILITY_OVERGROW; } + + GIVEN { + ASSUME(GetMoveEffect(MOVE_SOLARBEAM) == EFFECT_SOLAR_BEAM); + ASSUME(GetMoveType(MOVE_SOLARBEAM) == TYPE_GRASS); + PLAYER(SPECIES_MEGANIUM) { Ability(ability); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SOLAR_BEAM); } + if (ability == ABILITY_OVERGROW) { + TURN { SKIP_TURN(player); } + } + } SCENE { + if (ability == ABILITY_OVERGROW) { + MESSAGE("Meganium used Solar Beam!"); + NOT HP_BAR(opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent); + } + else { + MESSAGE("Meganium used Solar Beam!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SOLAR_BEAM, player); + HP_BAR(opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent); + } + } +} + +SINGLE_BATTLE_TEST("Growth increases Attack and Sp. Atk by 2 stages under Mega Sol (Gen 5+)") +{ + GIVEN { + WITH_CONFIG(B_GROWTH_STAT_RAISE, GEN_1); + ASSUME(GetMoveEffect(MOVE_GROWTH) == EFFECT_GROWTH); + PLAYER(SPECIES_MEGANIUM_MEGA) { Ability(ABILITY_MEGA_SOL); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_GROWTH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_GROWTH, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + } THEN { + EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 2); + EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 2); + } +} + +SINGLE_BATTLE_TEST("Mega Sol ignores Sand Veil") +{ + PASSES_RANDOMLY(5, 5, RNG_ACCURACY); + GIVEN { + ASSUME(GetMoveAccuracy(MOVE_POUND) == 100); + PLAYER(SPECIES_SANDSHREW) { Ability(ABILITY_SAND_VEIL); } + OPPONENT(SPECIES_MEGANIUM_MEGA) { Ability(ABILITY_MEGA_SOL); } + } WHEN { + TURN { MOVE(opponent, MOVE_SANDSTORM); } + TURN { MOVE(opponent, MOVE_POUND); } + } SCENE { + HP_BAR(player); + } +} + +SINGLE_BATTLE_TEST("Mega Sol ignores Snow Cloak") +{ + PASSES_RANDOMLY(5, 5, RNG_ACCURACY); + GIVEN { + ASSUME(GetMoveAccuracy(MOVE_POUND) == 100); + PLAYER(SPECIES_GLACEON) { Ability(ABILITY_SNOW_CLOAK); } + OPPONENT(SPECIES_MEGANIUM_MEGA) { Ability(ABILITY_MEGA_SOL); } + } WHEN { + TURN { MOVE(player, MOVE_HAIL); } + TURN { MOVE(opponent, MOVE_SCRATCH); } + } SCENE { + HP_BAR(player); + } +} diff --git a/test/battle/ai/ai_switching.c b/test/battle/ai/ai_switching.c index 35fe954ea0..f1ae1bf9b8 100644 --- a/test/battle/ai/ai_switching.c +++ b/test/battle/ai/ai_switching.c @@ -2064,7 +2064,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: AI sees Echoed Voice damage co AI_SINGLE_BATTLE_TEST("AI_SMART_MON_CHOICES: AI sees its own weather setting ability when considering switchin candidates") { - enum Ability ability = ABILITY_NONE; + enum Ability ability; PARAMETRIZE { ability = ABILITY_WATER_ABSORB; } PARAMETRIZE { ability = ABILITY_DRIZZLE; } GIVEN { diff --git a/test/battle/move_effect/growth.c b/test/battle/move_effect/growth.c index a9dda9cfd5..d3890404ce 100644 --- a/test/battle/move_effect/growth.c +++ b/test/battle/move_effect/growth.c @@ -1,6 +1,32 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("Growth increases Sp. Atk and Sp. Def by 1 stage (Gen 1)") // Equivalent to raising Special -TO_DO_BATTLE_TEST("Growth increases Sp. Atk by 1 stage (Gen 2-4)") -TO_DO_BATTLE_TEST("Growth increases Attack and Sp. Atk by 1 stage or 2 stages under Sun (Gen 5+)") +SINGLE_BATTLE_TEST("Growth increases Attack and Sp. Atk by 1 stage or 2 stages under Sun (Gen 5+)") +{ + enum Move move; + PARAMETRIZE { move = MOVE_CELEBRATE;} + PARAMETRIZE { move = MOVE_SUNNY_DAY;} + + GIVEN { + WITH_CONFIG(B_GROWTH_STAT_RAISE, GEN_5); + ASSUME(GetMoveEffect(MOVE_GROWTH) == EFFECT_GROWTH); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, move); } + TURN { MOVE(player, MOVE_GROWTH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_GROWTH, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + } THEN { + if (move == MOVE_CELEBRATE) { + EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1); + } + else { + EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 2); + EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 2); + } + } +} +