diff --git a/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml b/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml index 7a04bf329..55d4fada4 100644 --- a/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml +++ b/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml @@ -23,8 +23,9 @@ body: label: Version description: What version of pokeemerald-expansion are you using as a base? options: - - 1.5.0 (Default) + - 1.5.1 (Default) - upcoming (Edge) + - 1.5.0 - 1.4.3 - 1.4.2 - 1.4.1 diff --git a/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml b/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml index ef03b5547..8c28b3942 100644 --- a/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml +++ b/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml @@ -23,8 +23,9 @@ body: label: Version description: What version of pokeemerald-expansion are you using as a base? options: - - 1.5.0 (Default) + - 1.5.1 (Default) - upcoming (Edge) + - 1.5.0 - 1.4.3 - 1.4.2 - 1.4.1 diff --git a/.github/ISSUE_TEMPLATE/04_other_errors.yaml b/.github/ISSUE_TEMPLATE/04_other_errors.yaml index a1ff7e0c4..6f11d5b9a 100644 --- a/.github/ISSUE_TEMPLATE/04_other_errors.yaml +++ b/.github/ISSUE_TEMPLATE/04_other_errors.yaml @@ -23,8 +23,9 @@ body: label: Version description: What version of pokeemerald-expansion are you using as a base? options: - - 1.5.0 (Default) + - 1.5.1 (Default) - upcoming (Edge) + - 1.5.0 - 1.4.3 - 1.4.2 - 1.4.1 diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 3c0950dc3..ec6958d94 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -6634,9 +6634,6 @@ BattleScript_DoSwitchOut:: hidepartystatussummary BS_ATTACKER switchinanim BS_ATTACKER, FALSE waitstate - jumpifcantreverttoprimal BattleScript_DoSwitchOut2 - call BattleScript_PrimalReversionRet -BattleScript_DoSwitchOut2: switchineffects BS_ATTACKER moveendcase MOVEEND_STATUS_IMMUNITY_ABILITIES moveendcase MOVEEND_MIRROR_MOVE @@ -7791,17 +7788,12 @@ BattleScript_WishMegaEvolution:: goto BattleScript_MegaEvolutionAfterString BattleScript_PrimalReversion:: - printstring STRINGID_EMPTYSTRING3 - waitmessage 1 - setbyte gIsCriticalHit, 0 - handleprimalreversion BS_ATTACKER, 0 - handleprimalreversion BS_ATTACKER, 1 - playanimation BS_ATTACKER, B_ANIM_PRIMAL_REVERSION - waitanimation - handleprimalreversion BS_ATTACKER, 2 - printstring STRINGID_PKMNREVERTEDTOPRIMAL - waitmessage B_WAIT_TIME_LONG - switchinabilities BS_ATTACKER + call BattleScript_PrimalReversionRet + end2 + +BattleScript_PrimalReversionRestoreAttacker:: + call BattleScript_PrimalReversionRet + copybyte gBattlerAttacker, sSAVED_BATTLER end2 BattleScript_PrimalReversionRet:: @@ -7815,6 +7807,7 @@ BattleScript_PrimalReversionRet:: handleprimalreversion BS_ATTACKER, 2 printstring STRINGID_PKMNREVERTEDTOPRIMAL waitmessage B_WAIT_TIME_LONG + switchinabilities BS_ATTACKER return BattleScript_AttackerFormChange:: diff --git a/include/battle.h b/include/battle.h index 5acde903a..00893d105 100644 --- a/include/battle.h +++ b/include/battle.h @@ -634,7 +634,7 @@ struct BattleStruct bool8 friskedAbility; // If identifies two mons, show the ability pop-up only once. u8 sameMoveTurns[MAX_BATTLERS_COUNT]; // For Metronome, number of times the same moves has been SUCCESFULLY used. u16 moveEffect2; // For Knock Off - u16 changedSpecies[PARTY_SIZE]; // For Zygarde or future forms when multiple mons can change into the same pokemon. + u16 changedSpecies[NUM_BATTLE_SIDES][PARTY_SIZE]; // For forms when multiple mons can change into the same pokemon. u8 quickClawBattlerId; struct LostItem itemLost[PARTY_SIZE]; // Player's team that had items consumed or stolen (two bytes per party member) u8 blunderPolicy:1; // should blunder policy activate diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 8996abd71..f34f80139 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -418,6 +418,7 @@ extern const u8 BattleScript_AttackWeakenedByStrongWinds[]; extern const u8 BattleScript_BlockedByPrimalWeatherEnd3[]; extern const u8 BattleScript_BlockedByPrimalWeatherRet[]; extern const u8 BattleScript_PrimalReversion[]; +extern const u8 BattleScript_PrimalReversionRestoreAttacker[]; extern const u8 BattleScript_HyperspaceFuryRemoveProtect[]; extern const u8 BattleScript_SelectingNotAllowedMoveGorillaTactics[]; extern const u8 BattleScript_SelectingNotAllowedMoveGorillaTacticsInPalace[]; diff --git a/include/battle_util.h b/include/battle_util.h index 0431d7ba4..7ae9577b7 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -142,6 +142,7 @@ u8 AtkCanceller_UnableToUseMove2(void); bool8 HasNoMonsToSwitch(u8 battlerId, u8 r1, u8 r2); bool32 TryChangeBattleWeather(u8 battler, u32 weatherEnumId, bool32 viaAbility); u8 AbilityBattleEffects(u8 caseID, u8 battlerId, u16 ability, u8 special, u16 moveArg); +bool32 TryPrimalReversion(u8 battlerId); bool32 IsNeutralizingGasOnField(void); u32 GetBattlerAbility(u8 battlerId); u32 IsAbilityOnSide(u32 battlerId, u32 ability); @@ -208,6 +209,7 @@ void BufferStatChange(u8 battlerId, u8 statId, u8 stringId); bool32 BlocksPrankster(u16 move, u8 battlerPrankster, u8 battlerDef, bool32 checkTarget); u16 GetUsedHeldItem(u8 battler); bool32 IsBattlerWeatherAffected(u8 battlerId, u32 weatherFlags); +u32 ApplyWeatherDamageMultiplier(u8 battlerAtk, u16 move, u8 moveType, u32 dmg, u16 holdEffectAtk, u16 holdEffectDef); u32 GetBattlerMoveTargetType(u8 battlerId, u16 move); bool32 CanTargetBattler(u8 battlerAtk, u8 battlerDef, u16 move); bool8 IsMoveAffectedByParentalBond(u16 move, u8 battlerId); diff --git a/include/config/item.h b/include/config/item.h index 1e174b5ff..ad205f003 100644 --- a/include/config/item.h +++ b/include/config/item.h @@ -10,6 +10,7 @@ #define I_VITAMIN_EV_CAP GEN_LATEST // In Gen8+, the Vitamins no longer have a cap of 100 EV per stat. #define I_BERRY_EV_JUMP GEN_LATEST // In Gen4 only, EV-lowering Berries lower a stat's EV to 100 if it is above 100. #define I_GRISEOUS_ORB_FORM_CHANGE GEN_LATEST // In Gen9+, the Griseous Orb no longer changes Giratina's form when held. +#define I_GEM_BOOST_POWER GEN_LATEST // In Gen5+, the Gem boost power was reduced from 50% to 30%. #define I_USE_EVO_HELD_ITEMS_FROM_BAG FALSE // If TRUE, items such as Razor Claw or Electirizer will be usable from the bag to evolve a Pokémon just like in LA. // TM config diff --git a/src/battle_main.c b/src/battle_main.c index 2c191512d..65fc757a7 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3875,14 +3875,9 @@ static void TryDoEventsBeforeFirstTurn(void) while (gBattleStruct->switchInAbilitiesCounter < gBattlersCount) { gBattlerAttacker = gBattlerByTurnOrder[gBattleStruct->switchInAbilitiesCounter++]; - - // Primal Reversion - if (GetBattlerHoldEffect(gBattlerAttacker, TRUE) == HOLD_EFFECT_PRIMAL_ORB - && GetBattleFormChangeTargetSpecies(gBattlerAttacker, FORM_CHANGE_BATTLE_PRIMAL_REVERSION) != SPECIES_NONE) - { - BattleScriptExecute(BattleScript_PrimalReversion); + + if (TryPrimalReversion(gBattlerAttacker)) return; - } if (AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, gBattlerAttacker, 0, 0, 0) != 0) return; } @@ -5454,7 +5449,8 @@ static void HandleEndTurn_FinishBattle(void) changedForm = TryFormChange(i, B_SIDE_PLAYER, FORM_CHANGE_END_BATTLE); // Clear original species field - gBattleStruct->changedSpecies[i] = SPECIES_NONE; + gBattleStruct->changedSpecies[B_SIDE_PLAYER][i] = SPECIES_NONE; + gBattleStruct->changedSpecies[B_SIDE_OPPONENT][i] = SPECIES_NONE; #if B_RECALCULATE_STATS >= GEN_5 // Recalculate the stats of every party member before the end diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 1c6a2e677..710c2982e 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -6826,7 +6826,8 @@ static void SetDmgHazardsBattlescript(u8 battlerId, u8 multistringId) bool32 DoSwitchInAbilitiesItems(u32 battlerId) { - return (AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, battlerId, 0, 0, 0) + return (TryPrimalReversion(battlerId) + || AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, battlerId, 0, 0, 0) || (gBattleWeather & B_WEATHER_ANY && WEATHER_HAS_EFFECT && AbilityBattleEffects(ABILITYEFFECT_ON_WEATHER, battlerId, 0, 0, 0)) || (gFieldStatuses & STATUS_FIELD_TERRAIN_ANY && AbilityBattleEffects(ABILITYEFFECT_ON_TERRAIN, battlerId, 0, 0, 0)) || ItemBattleEffects(ITEMEFFECT_ON_SWITCH_IN, battlerId, FALSE) @@ -10434,7 +10435,7 @@ static void Cmd_various(void) { gBattleStruct->battleBondTransformed[GET_BATTLER_SIDE2(gBattlerAttacker)] |= gBitTable[gBattlerPartyIndexes[gBattlerAttacker]]; PREPARE_SPECIES_BUFFER(gBattleTextBuff1, gBattleMons[gBattlerAttacker].species); - gBattleStruct->changedSpecies[gBattlerPartyIndexes[gBattlerAttacker]] = gBattleMons[gBattlerAttacker].species; + gBattleStruct->changedSpecies[GET_BATTLER_SIDE2(gBattlerAttacker)][gBattlerPartyIndexes[gBattlerAttacker]] = gBattleMons[gBattlerAttacker].species; gBattleMons[gBattlerAttacker].species = SPECIES_GRENINJA_ASH; BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_BattleBondActivatesOnMoveEndAttacker; diff --git a/src/battle_util.c b/src/battle_util.c index e7687b6aa..792eb9711 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -6040,6 +6040,27 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move return effect; } +bool32 TryPrimalReversion(u8 battlerId) +{ + if (GetBattlerHoldEffect(battlerId, FALSE) == HOLD_EFFECT_PRIMAL_ORB + && GetBattleFormChangeTargetSpecies(battlerId, FORM_CHANGE_BATTLE_PRIMAL_REVERSION) != SPECIES_NONE) + { + if (gBattlerAttacker == battlerId) + { + BattleScriptExecute(BattleScript_PrimalReversion); + } + else + { + // edge case for scenarios like a switch-in after activated eject button + gBattleScripting.savedBattler = gBattlerAttacker; + gBattlerAttacker = battlerId; + BattleScriptExecute(BattleScript_PrimalReversionRestoreAttacker); + } + return TRUE; + } + return FALSE; +} + bool32 IsNeutralizingGasBannedAbility(u32 ability) { switch (ability) @@ -8784,14 +8805,16 @@ static u32 CalcMoveBasePowerAfterModifiers(u16 move, u8 battlerAtk, u8 battlerDe case ABILITY_PROTOSYNTHESIS: { u8 atkHighestStat = GetHighestStatId(battlerAtk); - if (gBattleWeather & B_WEATHER_SUN && WEATHER_HAS_EFFECT && (atkHighestStat == STAT_ATK || atkHighestStat == STAT_SPATK)) + if (gBattleWeather & B_WEATHER_SUN && WEATHER_HAS_EFFECT + && ((IS_MOVE_PHYSICAL(move) && atkHighestStat == STAT_ATK) || (IS_MOVE_SPECIAL(move) && atkHighestStat == STAT_SPATK))) modifier = uq4_12_multiply(modifier, UQ_4_12(1.3)); } break; case ABILITY_QUARK_DRIVE: { u8 atkHighestStat = GetHighestStatId(battlerAtk); - if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN && (atkHighestStat == STAT_ATK || atkHighestStat == STAT_SPATK)) + if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN + && ((IS_MOVE_PHYSICAL(move) && atkHighestStat == STAT_ATK) || (IS_MOVE_SPECIAL(move) && atkHighestStat == STAT_SPATK))) modifier = uq4_12_multiply(modifier, UQ_4_12(1.3)); } break; @@ -8882,14 +8905,16 @@ static u32 CalcMoveBasePowerAfterModifiers(u16 move, u8 battlerAtk, u8 battlerDe case ABILITY_PROTOSYNTHESIS: { u8 defHighestStat = GetHighestStatId(battlerDef); - if (gBattleWeather & B_WEATHER_SUN && WEATHER_HAS_EFFECT && (defHighestStat == STAT_DEF || defHighestStat == STAT_SPDEF)) + if (gBattleWeather & B_WEATHER_SUN && WEATHER_HAS_EFFECT + && ((IS_MOVE_PHYSICAL(move) && defHighestStat == STAT_DEF) || (IS_MOVE_SPECIAL(move) && defHighestStat == STAT_SPDEF))) modifier = uq4_12_multiply(modifier, UQ_4_12(0.7)); } break; case ABILITY_QUARK_DRIVE: { u8 defHighestStat = GetHighestStatId(battlerDef); - if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN && (defHighestStat == STAT_DEF || defHighestStat == STAT_SPDEF)) + if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN + && ((IS_MOVE_PHYSICAL(move) && defHighestStat == STAT_DEF) || (IS_MOVE_SPECIAL(move) && defHighestStat == STAT_SPDEF))) modifier = uq4_12_multiply(modifier, UQ_4_12(0.7)); } break; @@ -8933,10 +8958,6 @@ static u32 CalcMoveBasePowerAfterModifiers(u16 move, u8 battlerAtk, u8 battlerDe #endif modifier = uq4_12_multiply(modifier, holdEffectModifier); break; - case HOLD_EFFECT_GEMS: - if (gSpecialStatuses[battlerAtk].gemBoost && gBattleMons[battlerAtk].item) - modifier = uq4_12_multiply(modifier, UQ_4_12(1.0) + sPercentToModifier[gSpecialStatuses[battlerAtk].gemParam]); - break; case HOLD_EFFECT_BUG_POWER: case HOLD_EFFECT_STEEL_POWER: case HOLD_EFFECT_GROUND_POWER: @@ -9027,6 +9048,8 @@ static u32 CalcMoveBasePowerAfterModifiers(u16 move, u8 battlerAtk, u8 battlerDe // various effects if (gProtectStructs[battlerAtk].helpingHand) modifier = uq4_12_multiply(modifier, UQ_4_12(1.5)); + if (gSpecialStatuses[battlerAtk].gemBoost) + modifier = uq4_12_multiply(modifier, UQ_4_12(1.0) + sPercentToModifier[gSpecialStatuses[battlerAtk].gemParam]); if (gStatuses3[battlerAtk] & STATUS3_CHARGED_UP && moveType == TYPE_ELECTRIC) modifier = uq4_12_multiply(modifier, UQ_4_12(2.0)); if (gStatuses3[battlerAtk] & STATUS3_ME_FIRST) @@ -9423,6 +9446,8 @@ static u32 CalcFinalDmg(u32 dmg, u16 move, u8 battlerAtk, u8 battlerDef, u8 move u32 defSide = GET_BATTLER_SIDE(battlerDef); uq4_12_t finalModifier = UQ_4_12(1.0); u16 itemDef = gBattleMons[battlerDef].item; + u16 holdEffectAtk = GetBattlerHoldEffect(battlerAtk, TRUE); + u16 holdEffectDef = GetBattlerHoldEffect(battlerDef, TRUE); // check multiple targets in double battle if (GetMoveTargetCount(move, battlerAtk, battlerDef) >= 2) @@ -9452,28 +9477,15 @@ static u32 CalcFinalDmg(u32 dmg, u16 move, u8 battlerAtk, u8 battlerDef, u8 move dmg = ApplyModifier(UQ_4_12(0.5), dmg); // check frostbite - if (gBattleMons[battlerAtk].status1 & STATUS1_FROSTBITE && !IS_MOVE_PHYSICAL(move) + if (gBattleMons[battlerAtk].status1 & STATUS1_FROSTBITE && IS_MOVE_SPECIAL(move) #if B_BURN_FACADE_DMG >= GEN_6 && gBattleMoves[move].effect != EFFECT_FACADE #endif && abilityAtk != ABILITY_GUTS) dmg = ApplyModifier(UQ_4_12(0.5), dmg); - // check sunny/rain weather - if (IsBattlerWeatherAffected(battlerAtk, B_WEATHER_RAIN)) - { - if (moveType == TYPE_FIRE) - dmg = ApplyModifier(UQ_4_12(0.5), dmg); - else if (moveType == TYPE_WATER) - dmg = ApplyModifier(UQ_4_12(1.5), dmg); - } - else if (IsBattlerWeatherAffected(battlerAtk, B_WEATHER_SUN)) - { - if (moveType == TYPE_FIRE || gBattleMoves[move].effect == EFFECT_HYDRO_STEAM) - dmg = ApplyModifier(UQ_4_12(1.5), dmg); - else if (moveType == TYPE_WATER) - dmg = ApplyModifier(UQ_4_12(0.5), dmg); - } + // check weather + dmg = ApplyWeatherDamageMultiplier(battlerAtk, move, moveType, dmg, holdEffectAtk, holdEffectDef); // check stab if (IS_BATTLER_OF_TYPE(battlerAtk, moveType) && move != MOVE_STRUGGLE && move != MOVE_NONE) @@ -9562,7 +9574,7 @@ static u32 CalcFinalDmg(u32 dmg, u16 move, u8 battlerAtk, u8 battlerDef, u8 move } // attacker's hold effect - switch (GetBattlerHoldEffect(battlerAtk, TRUE)) + switch (holdEffectAtk) { case HOLD_EFFECT_METRONOME: percentBoost = min((gBattleStruct->sameMoveTurns[battlerAtk] * GetBattlerHoldEffectParam(battlerAtk)), 100); @@ -9578,7 +9590,7 @@ static u32 CalcFinalDmg(u32 dmg, u16 move, u8 battlerAtk, u8 battlerDef, u8 move } // target's hold effect - switch (GetBattlerHoldEffect(battlerDef, TRUE)) + switch (holdEffectDef) { // berries reducing dmg case HOLD_EFFECT_RESIST_BERRY: @@ -10152,8 +10164,8 @@ bool32 TryBattleFormChange(u8 battlerId, u16 method) if (targetSpecies != SPECIES_NONE) { // Saves the original species on the first form change for the player. - if (side == B_SIDE_PLAYER && gBattleStruct->changedSpecies[monId] == SPECIES_NONE) - gBattleStruct->changedSpecies[monId] = gBattleMons[battlerId].species; + if (gBattleStruct->changedSpecies[side][monId] == SPECIES_NONE) + gBattleStruct->changedSpecies[side][monId] = gBattleMons[battlerId].species; TryToSetBattleFormChangeMoves(&party[monId], method); SetMonData(&party[monId], MON_DATA_SPECIES, &targetSpecies); @@ -10161,7 +10173,7 @@ bool32 TryBattleFormChange(u8 battlerId, u16 method) RecalcBattlerStats(battlerId, &party[monId]); return TRUE; } - else if (gBattleStruct->changedSpecies[monId] != SPECIES_NONE) + else if (gBattleStruct->changedSpecies[side][monId] != SPECIES_NONE) { bool8 restoreSpecies = FALSE; @@ -10177,7 +10189,7 @@ bool32 TryBattleFormChange(u8 battlerId, u16 method) { // Reverts the original species TryToSetBattleFormChangeMoves(&party[monId], method); - SetMonData(&party[monId], MON_DATA_SPECIES, &gBattleStruct->changedSpecies[monId]); + SetMonData(&party[monId], MON_DATA_SPECIES, &gBattleStruct->changedSpecies[side][monId]); RecalcBattlerStats(battlerId, &party[monId]); return TRUE; } @@ -10765,6 +10777,34 @@ bool32 IsBattlerWeatherAffected(u8 battlerId, u32 weatherFlags) return FALSE; } +// Utility Umbrella holders take normal damage from what would be rain- and sun-weakened attacks. +u32 ApplyWeatherDamageMultiplier(u8 battlerAtk, u16 move, u8 moveType, u32 dmg, u16 holdEffectAtk, u16 holdEffectDef) +{ + if (WEATHER_HAS_EFFECT) + { + if (gBattleMoves[move].effect == EFFECT_HYDRO_STEAM && (gBattleWeather & B_WEATHER_SUN) && holdEffectAtk != HOLD_EFFECT_UTILITY_UMBRELLA) + dmg = ApplyModifier(UQ_4_12(1.5), dmg); + else if (holdEffectDef != HOLD_EFFECT_UTILITY_UMBRELLA) + { + if (gBattleWeather & B_WEATHER_RAIN) + { + if (moveType == TYPE_FIRE) + dmg = ApplyModifier(UQ_4_12(0.5), dmg); + else if (moveType == TYPE_WATER) + dmg = ApplyModifier(UQ_4_12(1.5), dmg); + } + else if (gBattleWeather & B_WEATHER_SUN) + { + if (moveType == TYPE_FIRE) + dmg = ApplyModifier(UQ_4_12(1.5), dmg); + else if (moveType == TYPE_WATER) + dmg = ApplyModifier(UQ_4_12(0.5), dmg); + } + } + } + return dmg; +} + // Gets move target before redirection effects etc. are applied // Possible return values are defined in battle.h following MOVE_TARGET_SELECTED u32 GetBattlerMoveTargetType(u8 battlerId, u16 move) diff --git a/src/data/items.h b/src/data/items.h index a309925aa..802cd325d 100644 --- a/src/data/items.h +++ b/src/data/items.h @@ -6,6 +6,12 @@ #define EVO_HELD_ITEM_FIELD_FUNC ItemUseOutOfBattle_CannotUse #endif +#if I_GEM_BOOST_POWER >= GEN_5 + #define GEM_BOOST_PARAM 30 +#else + #define GEM_BOOST_PARAM 50 +#endif + const struct Item gItems[] = { [ITEM_NONE] = @@ -4416,7 +4422,7 @@ const struct Item gItems[] = .itemId = ITEM_NORMAL_GEM, .price = 4000, .holdEffect = HOLD_EFFECT_GEMS, - .holdEffectParam = 30, + .holdEffectParam = GEM_BOOST_PARAM, .description = sNormalGemDesc, .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, @@ -4430,7 +4436,7 @@ const struct Item gItems[] = .itemId = ITEM_FIRE_GEM, .price = 4000, .holdEffect = HOLD_EFFECT_GEMS, - .holdEffectParam = 30, + .holdEffectParam = GEM_BOOST_PARAM, .description = sFireGemDesc, .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, @@ -4444,7 +4450,7 @@ const struct Item gItems[] = .itemId = ITEM_WATER_GEM, .price = 4000, .holdEffect = HOLD_EFFECT_GEMS, - .holdEffectParam = 30, + .holdEffectParam = GEM_BOOST_PARAM, .description = sWaterGemDesc, .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, @@ -4458,7 +4464,7 @@ const struct Item gItems[] = .itemId = ITEM_ELECTRIC_GEM, .price = 4000, .holdEffect = HOLD_EFFECT_GEMS, - .holdEffectParam = 30, + .holdEffectParam = GEM_BOOST_PARAM, .description = sElectricGemDesc, .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, @@ -4472,7 +4478,7 @@ const struct Item gItems[] = .itemId = ITEM_GRASS_GEM, .price = 4000, .holdEffect = HOLD_EFFECT_GEMS, - .holdEffectParam = 30, + .holdEffectParam = GEM_BOOST_PARAM, .description = sGrassGemDesc, .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, @@ -4486,7 +4492,7 @@ const struct Item gItems[] = .itemId = ITEM_ICE_GEM, .price = 4000, .holdEffect = HOLD_EFFECT_GEMS, - .holdEffectParam = 30, + .holdEffectParam = GEM_BOOST_PARAM, .description = sIceGemDesc, .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, @@ -4500,7 +4506,7 @@ const struct Item gItems[] = .itemId = ITEM_FIGHTING_GEM, .price = 4000, .holdEffect = HOLD_EFFECT_GEMS, - .holdEffectParam = 30, + .holdEffectParam = GEM_BOOST_PARAM, .description = sFightingGemDesc, .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, @@ -4514,7 +4520,7 @@ const struct Item gItems[] = .itemId = ITEM_POISON_GEM, .price = 4000, .holdEffect = HOLD_EFFECT_GEMS, - .holdEffectParam = 30, + .holdEffectParam = GEM_BOOST_PARAM, .description = sPoisonGemDesc, .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, @@ -4528,7 +4534,7 @@ const struct Item gItems[] = .itemId = ITEM_GROUND_GEM, .price = 4000, .holdEffect = HOLD_EFFECT_GEMS, - .holdEffectParam = 30, + .holdEffectParam = GEM_BOOST_PARAM, .description = sGroundGemDesc, .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, @@ -4542,7 +4548,7 @@ const struct Item gItems[] = .itemId = ITEM_FLYING_GEM, .price = 4000, .holdEffect = HOLD_EFFECT_GEMS, - .holdEffectParam = 30, + .holdEffectParam = GEM_BOOST_PARAM, .description = sFlyingGemDesc, .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, @@ -4556,7 +4562,7 @@ const struct Item gItems[] = .itemId = ITEM_PSYCHIC_GEM, .price = 4000, .holdEffect = HOLD_EFFECT_GEMS, - .holdEffectParam = 30, + .holdEffectParam = GEM_BOOST_PARAM, .description = sPsychicGemDesc, .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, @@ -4570,7 +4576,7 @@ const struct Item gItems[] = .itemId = ITEM_BUG_GEM, .price = 4000, .holdEffect = HOLD_EFFECT_GEMS, - .holdEffectParam = 30, + .holdEffectParam = GEM_BOOST_PARAM, .description = sBugGemDesc, .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, @@ -4584,7 +4590,7 @@ const struct Item gItems[] = .itemId = ITEM_ROCK_GEM, .price = 4000, .holdEffect = HOLD_EFFECT_GEMS, - .holdEffectParam = 30, + .holdEffectParam = GEM_BOOST_PARAM, .description = sRockGemDesc, .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, @@ -4598,7 +4604,7 @@ const struct Item gItems[] = .itemId = ITEM_GHOST_GEM, .price = 4000, .holdEffect = HOLD_EFFECT_GEMS, - .holdEffectParam = 30, + .holdEffectParam = GEM_BOOST_PARAM, .description = sGhostGemDesc, .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, @@ -4612,7 +4618,7 @@ const struct Item gItems[] = .itemId = ITEM_DRAGON_GEM, .price = 4000, .holdEffect = HOLD_EFFECT_GEMS, - .holdEffectParam = 30, + .holdEffectParam = GEM_BOOST_PARAM, .description = sDragonGemDesc, .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, @@ -4626,7 +4632,7 @@ const struct Item gItems[] = .itemId = ITEM_DARK_GEM, .price = 4000, .holdEffect = HOLD_EFFECT_GEMS, - .holdEffectParam = 30, + .holdEffectParam = GEM_BOOST_PARAM, .description = sDarkGemDesc, .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, @@ -4640,7 +4646,7 @@ const struct Item gItems[] = .itemId = ITEM_STEEL_GEM, .price = 4000, .holdEffect = HOLD_EFFECT_GEMS, - .holdEffectParam = 30, + .holdEffectParam = GEM_BOOST_PARAM, .description = sSteelGemDesc, .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, @@ -4654,7 +4660,7 @@ const struct Item gItems[] = .itemId = ITEM_FAIRY_GEM, .price = 4000, .holdEffect = HOLD_EFFECT_GEMS, - .holdEffectParam = 30, + .holdEffectParam = GEM_BOOST_PARAM, .description = sFairyGemDesc, .pocket = POCKET_ITEMS, .type = ITEM_USE_BAG_MENU, diff --git a/src/pokemon.c b/src/pokemon.c index 881eeab8b..4347d7a0e 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -8549,7 +8549,7 @@ bool32 TryFormChange(u32 monId, u32 side, u16 method) targetSpecies = GetFormChangeTargetSpecies(&party[monId], method, 0); if (targetSpecies == SPECIES_NONE && gBattleStruct != NULL) - targetSpecies = gBattleStruct->changedSpecies[monId]; + targetSpecies = gBattleStruct->changedSpecies[side][monId]; if (targetSpecies != SPECIES_NONE) { diff --git a/test/ability_protosynthesis.c b/test/ability_protosynthesis.c new file mode 100644 index 000000000..9f794e00a --- /dev/null +++ b/test/ability_protosynthesis.c @@ -0,0 +1,87 @@ +#include "global.h" +#include "test_battle.h" + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_TACKLE].split == SPLIT_PHYSICAL); + ASSUME(gBattleMoves[MOVE_ROUND].split == SPLIT_SPECIAL); +} + +SINGLE_BATTLE_TEST("Protosynthesis boosts the highest stat") +{ + GIVEN { + PLAYER(SPECIES_ABRA) { Ability(ABILITY_PROTOSYNTHESIS); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SUNNY_DAY); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, player); + ABILITY_POPUP(player, ABILITY_PROTOSYNTHESIS); + MESSAGE("The harsh sunlight activated Abra's Protosynthesis!"); + MESSAGE("Abra's Sp. Atk was heightened!"); + } +} + +SINGLE_BATTLE_TEST("Protosynthesis boosts either Attack or Special Attack, not both") +{ + u16 species; + u32 move; + u16 damage[2]; + + PARAMETRIZE { species = SPECIES_BELLSPROUT; move = MOVE_TACKLE; } + PARAMETRIZE { species = SPECIES_BELLSPROUT; move = MOVE_ROUND; } + + PARAMETRIZE { species = SPECIES_ABRA; move = MOVE_TACKLE; } + PARAMETRIZE { species = SPECIES_ABRA; move = MOVE_ROUND; } + + GIVEN { + PLAYER(species) { Ability(ABILITY_PROTOSYNTHESIS); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, move); } + TURN { MOVE(opponent, MOVE_SUNNY_DAY); MOVE(player, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, player); + HP_BAR(opponent, captureDamage: &damage[0]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, opponent); + ANIMATION(ANIM_TYPE_MOVE, move, player); + HP_BAR(opponent, captureDamage: &damage[1]); + } THEN { + if ((move == MOVE_TACKLE && species == SPECIES_BELLSPROUT) || (move == MOVE_ROUND && species == SPECIES_ABRA)) + EXPECT_MUL_EQ(damage[0], Q_4_12(1.3), damage[1]); + else + EXPECT_EQ(damage[0], damage[1]); + } +} + +SINGLE_BATTLE_TEST("Protosynthesis either boosts Defense or Special Defense, not both") +{ + u16 species; + u32 move; + u16 damage[2]; + + PARAMETRIZE { species = SPECIES_ONIX; move = MOVE_TACKLE; } + PARAMETRIZE { species = SPECIES_ONIX; move = MOVE_ROUND; } + + PARAMETRIZE { species = SPECIES_BLASTOISE; move = MOVE_TACKLE; } + PARAMETRIZE { species = SPECIES_BLASTOISE; move = MOVE_ROUND; } + + GIVEN { + PLAYER(species) { Ability(ABILITY_PROTOSYNTHESIS); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, move); } + TURN { MOVE(player, MOVE_SUNNY_DAY); MOVE(opponent, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + HP_BAR(player, captureDamage: &damage[0]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, player); + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + HP_BAR(player, captureDamage: &damage[1]); + } THEN { + if ((move == MOVE_TACKLE && species == SPECIES_ONIX) || (move == MOVE_ROUND && species == SPECIES_BLASTOISE)) + EXPECT_MUL_EQ(damage[0], Q_4_12(0.7), damage[1]); + else + EXPECT_EQ(damage[0], damage[1]); + } +} diff --git a/test/ability_quark_drive.c b/test/ability_quark_drive.c new file mode 100644 index 000000000..b004c760c --- /dev/null +++ b/test/ability_quark_drive.c @@ -0,0 +1,87 @@ +#include "global.h" +#include "test_battle.h" + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_TACKLE].split == SPLIT_PHYSICAL); + ASSUME(gBattleMoves[MOVE_ROUND].split == SPLIT_SPECIAL); +} + +SINGLE_BATTLE_TEST("Quark Drive boosts the highest stat") +{ + GIVEN { + PLAYER(SPECIES_ABRA) { Ability(ABILITY_QUARK_DRIVE); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_ELECTRIC_TERRAIN); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRIC_TERRAIN, player); + ABILITY_POPUP(player, ABILITY_QUARK_DRIVE); + MESSAGE("The Electric Terrain activated Abra's Quark Drive!"); + MESSAGE("Abra's Sp. Atk was heightened!"); + } +} + +SINGLE_BATTLE_TEST("Quark Drive boosts either Attack or Special Attack, not both") +{ + u16 species; + u32 move; + u16 damage[2]; + + PARAMETRIZE { species = SPECIES_BELLSPROUT; move = MOVE_TACKLE; } + PARAMETRIZE { species = SPECIES_BELLSPROUT; move = MOVE_ROUND; } + + PARAMETRIZE { species = SPECIES_ABRA; move = MOVE_TACKLE; } + PARAMETRIZE { species = SPECIES_ABRA; move = MOVE_ROUND; } + + GIVEN { + PLAYER(species) { Ability(ABILITY_QUARK_DRIVE); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, move); } + TURN { MOVE(opponent, MOVE_ELECTRIC_TERRAIN); MOVE(player, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, player); + HP_BAR(opponent, captureDamage: &damage[0]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRIC_TERRAIN, opponent); + ANIMATION(ANIM_TYPE_MOVE, move, player); + HP_BAR(opponent, captureDamage: &damage[1]); + } THEN { + if ((move == MOVE_TACKLE && species == SPECIES_BELLSPROUT) || (move == MOVE_ROUND && species == SPECIES_ABRA)) + EXPECT_MUL_EQ(damage[0], Q_4_12(1.3), damage[1]); + else + EXPECT_EQ(damage[0], damage[1]); + } +} + +SINGLE_BATTLE_TEST("Quark Drive either boosts Defense or Special Defense, not both") +{ + u16 species; + u32 move; + u16 damage[2]; + + PARAMETRIZE { species = SPECIES_ONIX; move = MOVE_TACKLE; } + PARAMETRIZE { species = SPECIES_ONIX; move = MOVE_ROUND; } + + PARAMETRIZE { species = SPECIES_BLASTOISE; move = MOVE_TACKLE; } + PARAMETRIZE { species = SPECIES_BLASTOISE; move = MOVE_ROUND; } + + GIVEN { + PLAYER(species) { Ability(ABILITY_QUARK_DRIVE); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, move); } + TURN { MOVE(player, MOVE_ELECTRIC_TERRAIN); MOVE(opponent, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + HP_BAR(player, captureDamage: &damage[0]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRIC_TERRAIN, player); + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + HP_BAR(player, captureDamage: &damage[1]); + } THEN { + if ((move == MOVE_TACKLE && species == SPECIES_ONIX) || (move == MOVE_ROUND && species == SPECIES_BLASTOISE)) + EXPECT_MUL_EQ(damage[0], Q_4_12(0.7), damage[1]); + else + EXPECT_EQ(damage[0], damage[1]); + } +} diff --git a/test/hold_effect_gems.c b/test/hold_effect_gems.c new file mode 100644 index 000000000..9a90b81f9 --- /dev/null +++ b/test/hold_effect_gems.c @@ -0,0 +1,89 @@ +#include "global.h" +#include "test_battle.h" + +ASSUMPTIONS +{ + ASSUME(gItems[ITEM_NORMAL_GEM].holdEffect == HOLD_EFFECT_GEMS); +} + +SINGLE_BATTLE_TEST("Gem is consumed when it corresponds to the type of a move") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_NORMAL_GEM); }; + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_EMBER); } + TURN { MOVE(player, MOVE_TACKLE); } + } SCENE { + NONE_OF { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + MESSAGE("Fire Gem strengthened Wobbuffet's power!"); + } + ANIMATION(ANIM_TYPE_MOVE, MOVE_EMBER, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + MESSAGE("Normal Gem strengthened Wobbuffet's power!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); + } +} + +SINGLE_BATTLE_TEST("Gem boost is only applied once") +{ + s16 boostedHit; + s16 normalHit; + + GIVEN { + ASSUME(I_GEM_BOOST_POWER >= GEN_5); + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_NORMAL_GEM); }; + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TACKLE); } + TURN { MOVE(player, MOVE_TACKLE); } + } SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + MESSAGE("Normal Gem strengthened Wobbuffet's power!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); + HP_BAR(opponent, captureDamage: &boostedHit); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); + HP_BAR(opponent, captureDamage: &normalHit); + } THEN { + EXPECT_MUL_EQ(normalHit, Q_4_12(1.3), boostedHit); + } +} + +SINGLE_BATTLE_TEST("Gem modifier is used for all hits of Multi Hit Moves") +{ + s16 firstHit; + s16 secondHit; + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_NORMAL_GEM); }; + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { + MOVE(player, MOVE_DOUBLE_HIT); + } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_HIT, player); + HP_BAR(opponent, captureDamage: &firstHit); + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_HIT, player); + HP_BAR(opponent, captureDamage: &secondHit); + } THEN { + EXPECT_EQ(firstHit, secondHit); + } +} + +SINGLE_BATTLE_TEST("Gem is consumed if the move type is changed") +{ + GIVEN { + PLAYER(SPECIES_DELCATTY) { Ability(ABILITY_NORMALIZE); Item(ITEM_NORMAL_GEM); }; + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { + MOVE(player, MOVE_FEINT_ATTACK); + } + } SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + MESSAGE("Normal Gem strengthened Delcatty's power!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FEINT_ATTACK, player); + } +} diff --git a/test/hold_effect_utility_umbrella.c b/test/hold_effect_utility_umbrella.c new file mode 100644 index 000000000..6716ee72f --- /dev/null +++ b/test/hold_effect_utility_umbrella.c @@ -0,0 +1,54 @@ +#include "global.h" +#include "test_battle.h" + +// Please add Utility Umbrella interactions with move, item and ability effects on their respective files. +ASSUMPTIONS +{ + ASSUME(gItems[ITEM_UTILITY_UMBRELLA].holdEffect == HOLD_EFFECT_UTILITY_UMBRELLA); + ASSUME(gBattleMoves[MOVE_EMBER].type == TYPE_FIRE); + ASSUME(gBattleMoves[MOVE_WATER_GUN].type == TYPE_WATER); +} + +SINGLE_BATTLE_TEST("Utility Umbrella blocks Sun damage modifiers", s16 damage) +{ + u16 setupMove, attackingMove, heldItem; + PARAMETRIZE { setupMove = MOVE_SUNNY_DAY; attackingMove = MOVE_EMBER; heldItem = ITEM_UTILITY_UMBRELLA; } + PARAMETRIZE { setupMove = MOVE_SUNNY_DAY; attackingMove = MOVE_EMBER; heldItem = ITEM_NONE; } + PARAMETRIZE { setupMove = MOVE_SUNNY_DAY; attackingMove = MOVE_WATER_GUN; heldItem = ITEM_UTILITY_UMBRELLA; } + PARAMETRIZE { setupMove = MOVE_SUNNY_DAY; attackingMove = MOVE_WATER_GUN; heldItem = ITEM_NONE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(heldItem); }; + } WHEN { + TURN { MOVE(opponent, setupMove); } + TURN { MOVE(player, attackingMove); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, attackingMove, player); + 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[2].damage, Q_4_12(0.5), results[3].damage); + } +} + +SINGLE_BATTLE_TEST("Utility Umbrella blocks Rain damage modifiers", s16 damage) +{ + u16 setupMove, attackingMove, heldItem; + PARAMETRIZE { setupMove = MOVE_RAIN_DANCE; attackingMove = MOVE_EMBER; heldItem = ITEM_UTILITY_UMBRELLA; } + PARAMETRIZE { setupMove = MOVE_RAIN_DANCE; attackingMove = MOVE_EMBER; heldItem = ITEM_NONE; } + PARAMETRIZE { setupMove = MOVE_RAIN_DANCE; attackingMove = MOVE_WATER_GUN; heldItem = ITEM_UTILITY_UMBRELLA; } + PARAMETRIZE { setupMove = MOVE_RAIN_DANCE; attackingMove = MOVE_WATER_GUN; heldItem = ITEM_NONE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(heldItem); }; + } WHEN { + TURN { MOVE(opponent, setupMove); } + TURN { MOVE(player, attackingMove); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, attackingMove, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); + EXPECT_MUL_EQ(results[2].damage, Q_4_12(1.5), results[3].damage); + } +} diff --git a/test/move_effect_hydro_steam.c b/test/move_effect_hydro_steam.c new file mode 100644 index 000000000..487449159 --- /dev/null +++ b/test/move_effect_hydro_steam.c @@ -0,0 +1,50 @@ +#include "global.h" +#include "test_battle.h" + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_HYDRO_STEAM].effect == EFFECT_HYDRO_STEAM); +} + +SINGLE_BATTLE_TEST("Hydro Steam deals 1.5x damage under both Sunlight and Rain", s16 damage) +{ + u16 setupMove; + PARAMETRIZE { setupMove = MOVE_CELEBRATE; } + PARAMETRIZE { setupMove = MOVE_SUNNY_DAY; } + PARAMETRIZE { setupMove = MOVE_RAIN_DANCE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, setupMove); } + TURN { MOVE(player, MOVE_HYDRO_STEAM); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_HYDRO_STEAM, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[2].damage); + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Hydro Steam is affected by Utility Umbrella", s16 damage) +{ + u32 itemPlayer; + u32 itemOpponent; + PARAMETRIZE { itemPlayer = ITEM_UTILITY_UMBRELLA; itemOpponent = ITEM_NONE; } + PARAMETRIZE { itemPlayer = ITEM_NONE; itemOpponent = ITEM_UTILITY_UMBRELLA; } + PARAMETRIZE { itemPlayer = ITEM_UTILITY_UMBRELLA; itemOpponent = ITEM_UTILITY_UMBRELLA; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(itemPlayer); }; + OPPONENT(SPECIES_WOBBUFFET) {Item(itemOpponent); }; + } WHEN { + TURN { MOVE(player, MOVE_SUNNY_DAY); } + TURN { MOVE(player, MOVE_HYDRO_STEAM); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_HYDRO_STEAM, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[2].damage, Q_4_12(1.5), results[1].damage); + EXPECT_MUL_EQ(results[2].damage, Q_4_12(0.5), results[0].damage); + } +} diff --git a/test/primal_reversion.c b/test/primal_reversion.c new file mode 100644 index 000000000..f888d17f7 --- /dev/null +++ b/test/primal_reversion.c @@ -0,0 +1,216 @@ +#include "global.h" +#include "test_battle.h" + +SINGLE_BATTLE_TEST("Primal reversion happens for Groudon only when holding Red Orb") +{ + u16 heldItem; + PARAMETRIZE { heldItem = ITEM_NONE;} + PARAMETRIZE { heldItem = ITEM_RED_ORB;} + PARAMETRIZE { heldItem = ITEM_BLUE_ORB;} + GIVEN { + PLAYER(SPECIES_GROUDON) { Item(heldItem); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CELEBRATE); } + } SCENE { + if (heldItem == ITEM_RED_ORB) { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_PRIMAL_REVERSION, player); + MESSAGE("Groudon's Primal Reversion! It reverted to its primal form!"); + } + else { + NONE_OF { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_PRIMAL_REVERSION, player); + MESSAGE("Groudon's Primal Reversion! It reverted to its primal form!"); + } + } + } THEN { + if (heldItem == ITEM_RED_ORB) { + EXPECT_EQ(player->species, SPECIES_GROUDON_PRIMAL); + } + else { + EXPECT_EQ(player->species, SPECIES_GROUDON); + } + } +} + +SINGLE_BATTLE_TEST("Primal reversion happens for Kyogre only when holding Blue Orb") +{ + u16 heldItem; + PARAMETRIZE { heldItem = ITEM_NONE;} + PARAMETRIZE { heldItem = ITEM_RED_ORB;} + PARAMETRIZE { heldItem = ITEM_BLUE_ORB;} + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_KYOGRE) { Item(heldItem); } + } WHEN { + TURN { MOVE(opponent, MOVE_CELEBRATE); } + } SCENE { + if (heldItem == ITEM_BLUE_ORB) { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_PRIMAL_REVERSION, opponent); + MESSAGE("Foe Kyogre's Primal Reversion! It reverted to its primal form!"); + } + else { + NONE_OF { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_PRIMAL_REVERSION, opponent); + MESSAGE("Foe Kyogre's Primal Reversion! It reverted to its primal form!"); + } + } + } THEN { + if (heldItem == ITEM_BLUE_ORB) { + EXPECT_EQ(opponent->species, SPECIES_KYOGRE_PRIMAL); + } + else { + EXPECT_EQ(opponent->species, SPECIES_KYOGRE); + } + } +} + +DOUBLE_BATTLE_TEST("Primal reversion's order is determined by Speed - opponent faster") +{ + GIVEN { + PLAYER(SPECIES_KYOGRE) { Item(ITEM_BLUE_ORB); Speed(5); }; + PLAYER(SPECIES_GROUDON) { Item(ITEM_RED_ORB); Speed(15); }; + OPPONENT(SPECIES_GROUDON) { Item(ITEM_RED_ORB); Speed(10); } + OPPONENT(SPECIES_KYOGRE) { Item(ITEM_BLUE_ORB); Speed(20); } + } WHEN { + TURN { MOVE(opponentLeft, MOVE_CELEBRATE); } + } SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_PRIMAL_REVERSION, opponentRight); + MESSAGE("Foe Kyogre's Primal Reversion! It reverted to its primal form!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_PRIMAL_REVERSION, playerRight); + MESSAGE("Groudon's Primal Reversion! It reverted to its primal form!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_PRIMAL_REVERSION, opponentLeft); + MESSAGE("Foe Groudon's Primal Reversion! It reverted to its primal form!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_PRIMAL_REVERSION, playerLeft); + MESSAGE("Kyogre's Primal Reversion! It reverted to its primal form!"); + } THEN { + EXPECT_EQ(playerLeft->species, SPECIES_KYOGRE_PRIMAL); + EXPECT_EQ(opponentLeft->species, SPECIES_GROUDON_PRIMAL); + EXPECT_EQ(opponentRight->species, SPECIES_KYOGRE_PRIMAL); + EXPECT_EQ(playerRight->species, SPECIES_GROUDON_PRIMAL); + } +} + +DOUBLE_BATTLE_TEST("Primal reversion's order is determined by Speed - player faster") +{ + GIVEN { + PLAYER(SPECIES_KYOGRE) { Item(ITEM_BLUE_ORB); Speed(20); }; + PLAYER(SPECIES_GROUDON) { Item(ITEM_RED_ORB); Speed(30); }; + OPPONENT(SPECIES_GROUDON) { Item(ITEM_RED_ORB); Speed(10); } + OPPONENT(SPECIES_KYOGRE) { Item(ITEM_BLUE_ORB); Speed(2); } + } WHEN { + TURN { MOVE(opponentLeft, MOVE_CELEBRATE); } + } SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_PRIMAL_REVERSION, playerRight); + MESSAGE("Groudon's Primal Reversion! It reverted to its primal form!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_PRIMAL_REVERSION, playerLeft); + MESSAGE("Kyogre's Primal Reversion! It reverted to its primal form!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_PRIMAL_REVERSION, opponentLeft); + MESSAGE("Foe Groudon's Primal Reversion! It reverted to its primal form!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_PRIMAL_REVERSION, opponentRight); + MESSAGE("Foe Kyogre's Primal Reversion! It reverted to its primal form!"); + } THEN { + EXPECT_EQ(playerLeft->species, SPECIES_KYOGRE_PRIMAL); + EXPECT_EQ(opponentLeft->species, SPECIES_GROUDON_PRIMAL); + EXPECT_EQ(opponentRight->species, SPECIES_KYOGRE_PRIMAL); + EXPECT_EQ(playerRight->species, SPECIES_GROUDON_PRIMAL); + } +} + +SINGLE_BATTLE_TEST("Primal reversion happens after a mon is sent out after a mon is fainted") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_TACKLE].power != 0); + PLAYER(SPECIES_WOBBUFFET) {HP(1); } + PLAYER(SPECIES_GROUDON) { Item(ITEM_RED_ORB); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE); SEND_OUT(player, 1); } + TURN { MOVE(opponent, MOVE_TACKLE); } + } SCENE { + MESSAGE("Wobbuffet fainted!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_PRIMAL_REVERSION, player); + MESSAGE("Groudon's Primal Reversion! It reverted to its primal form!"); + } THEN { + EXPECT_EQ(player->species, SPECIES_GROUDON_PRIMAL); + } +} + +SINGLE_BATTLE_TEST("Primal reversion happens after a mon is switched in") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_GROUDON) { Item(ITEM_RED_ORB); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { SWITCH(player, 1); MOVE(opponent, MOVE_CELEBRATE); } + TURN { MOVE(opponent, MOVE_CELEBRATE); } + } SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_PRIMAL_REVERSION, player); + MESSAGE("Groudon's Primal Reversion! It reverted to its primal form!"); + } THEN { + EXPECT_EQ(player->species, SPECIES_GROUDON_PRIMAL); + } +} + +SINGLE_BATTLE_TEST("Primal reversion happens after a switch-in caused by Eject Button") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_TACKLE].power != 0); + ASSUME(gItems[ITEM_EJECT_BUTTON].holdEffect == HOLD_EFFECT_EJECT_BUTTON); + PLAYER(SPECIES_WOBBUFFET) {Item(ITEM_EJECT_BUTTON); } + PLAYER(SPECIES_GROUDON) { Item(ITEM_RED_ORB); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE); SEND_OUT(player, 1); } + TURN { MOVE(opponent, MOVE_TACKLE); } + } SCENE { + MESSAGE("Wobbuffet is switched out with the Eject Button!"); + MESSAGE("Go! Groudon!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_PRIMAL_REVERSION, player); + MESSAGE("Groudon's Primal Reversion! It reverted to its primal form!"); + } THEN { + EXPECT_EQ(player->species, SPECIES_GROUDON_PRIMAL); + } +} + +SINGLE_BATTLE_TEST("Primal reversion happens after a switch-in caused by Red Card") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_TACKLE].power != 0); + ASSUME(gItems[ITEM_RED_CARD].holdEffect == HOLD_EFFECT_RED_CARD); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_GROUDON) { Item(ITEM_RED_ORB); } + OPPONENT(SPECIES_WOBBUFFET) {Item(ITEM_RED_CARD); } + } WHEN { + TURN { MOVE(player, MOVE_TACKLE); } + } SCENE { + MESSAGE("Foe Wobbuffet held up its Red Card against Wobbuffet!"); + MESSAGE("Groudon was dragged out!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_PRIMAL_REVERSION, player); + MESSAGE("Groudon's Primal Reversion! It reverted to its primal form!"); + } THEN { + EXPECT_EQ(player->species, SPECIES_GROUDON_PRIMAL); + } +} + +SINGLE_BATTLE_TEST("Primal reversion happens after the entry hazards damage") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_SPIKES].effect == EFFECT_SPIKES); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_GROUDON) { Item(ITEM_RED_ORB); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_SPIKES); } + TURN { MOVE(opponent, MOVE_SPIKES); SWITCH(player, 1);} + } SCENE { + MESSAGE("Go! Groudon!"); + HP_BAR(player); + MESSAGE("Groudon is hurt by spikes!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_PRIMAL_REVERSION, player); + MESSAGE("Groudon's Primal Reversion! It reverted to its primal form!"); + } THEN { + EXPECT_EQ(player->species, SPECIES_GROUDON_PRIMAL); + } +} diff --git a/test/weather_rain.c b/test/weather_rain.c new file mode 100644 index 000000000..b99681495 --- /dev/null +++ b/test/weather_rain.c @@ -0,0 +1,47 @@ +#include "global.h" +#include "test_battle.h" + +// Please add Rain interactions with move, item and ability effects on their respective files. +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_EMBER].type == TYPE_FIRE); + ASSUME(gBattleMoves[MOVE_WATER_GUN].type == TYPE_WATER); +} + +SINGLE_BATTLE_TEST("Rain multiplies the power of Fire-type moves by 0.5x", s16 damage) +{ + u32 setupMove; + PARAMETRIZE { setupMove = MOVE_CELEBRATE; } + PARAMETRIZE { setupMove = MOVE_RAIN_DANCE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, setupMove); } + 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(0.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Rain multiplies the power of Water-type moves by 1.5x", s16 damage) +{ + u32 setupMove; + PARAMETRIZE { setupMove = MOVE_CELEBRATE; } + PARAMETRIZE { setupMove = MOVE_RAIN_DANCE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, setupMove); } + 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(1.5), results[1].damage); + } +} diff --git a/test/weather_sunlight.c b/test/weather_sunlight.c new file mode 100644 index 000000000..beba0e9b6 --- /dev/null +++ b/test/weather_sunlight.c @@ -0,0 +1,47 @@ +#include "global.h" +#include "test_battle.h" + +// Please add Sunlight interactions with move, item and ability effects on their respective files. +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_EMBER].type == TYPE_FIRE); + ASSUME(gBattleMoves[MOVE_WATER_GUN].type == TYPE_WATER); +} + +SINGLE_BATTLE_TEST("Sunlight multiplies the power of Fire-type moves by 1.5x", s16 damage) +{ + u32 setupMove; + PARAMETRIZE { setupMove = MOVE_CELEBRATE; } + PARAMETRIZE { setupMove = MOVE_SUNNY_DAY; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, setupMove); } + 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("Sunlight multiplies the power of Water-type moves by 0.5x", s16 damage) +{ + u32 setupMove; + PARAMETRIZE { setupMove = MOVE_CELEBRATE; } + PARAMETRIZE { setupMove = MOVE_SUNNY_DAY; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, setupMove); } + 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); + } +}