diff --git a/.gitignore b/.gitignore index af9009dd01..ba80433cf7 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,4 @@ prefabs.json *.sym *.js src/data/map_group_count.h +tools/trainerproc/trainerproc diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 24ce3405eb..28648814d8 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -6326,6 +6326,21 @@ BattleScript_DmgHazardsOnTargetFainted:: moveendall goto BattleScript_HandleFaintedMon +BattleScript_DmgHazardsOnBattlerScripting:: + orword gHitMarker, HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_PASSIVE_DAMAGE + healthbarupdate BS_SCRIPTING + datahpupdate BS_SCRIPTING + call BattleScript_PrintHurtByDmgHazards + tryfaintmon BS_SCRIPTING + tryfaintmon_spikes BS_SCRIPTING, BattleScript_DmgHazardsOnBattlerScriptingFainted + return + +BattleScript_DmgHazardsOnBattlerScriptingFainted:: + setbyte sGIVEEXP_STATE, 0 + getexp BS_SCRIPTING + moveendall + goto BattleScript_HandleFaintedMon + BattleScript_DmgHazardsOnFaintedBattler:: orword gHitMarker, HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_PASSIVE_DAMAGE healthbarupdate BS_FAINTED @@ -6667,6 +6682,7 @@ BattleScript_DoFutureAttackResult: checkteamslost BattleScript_FutureAttackEnd BattleScript_FutureAttackEnd:: moveendcase MOVEEND_RAGE + moveendcase MOVEEND_ABILITIES moveendfromto MOVEEND_ITEM_EFFECTS_ALL, MOVEEND_UPDATE_LAST_MOVES setbyte gMoveResultFlags, 0 end2 diff --git a/graphics/pokemon/tatsugiri/droopy/back.png b/graphics/pokemon/tatsugiri/droopy/back.png index 6d998537f2..381ae4278e 100644 Binary files a/graphics/pokemon/tatsugiri/droopy/back.png and b/graphics/pokemon/tatsugiri/droopy/back.png differ diff --git a/include/battle.h b/include/battle.h index cdcb1b1742..b5ecf08335 100644 --- a/include/battle.h +++ b/include/battle.h @@ -178,7 +178,6 @@ struct ProtectStruct u32 flinchImmobility:1; u32 notFirstStrike:1; u32 palaceUnableToUseMove:1; - u32 usesBouncedMove:1; u32 usedHealBlockedMove:1; u32 usedGravityPreventedMove:1; u32 powderSelfDmg:1; @@ -771,9 +770,10 @@ struct BattleStruct u8 quickClawBattlerId; struct LostItem itemLost[PARTY_SIZE]; // Player's team that had items consumed or stolen (two bytes per party member) u8 forcedSwitch:4; // For each battler + u8 additionalEffectsCounter:4; // A counter for the additionalEffects applied by the current move in Cmd_setadditionaleffects u8 blunderPolicy:1; // should blunder policy activate u8 swapDamageCategory:1; // Photon Geyser, Shell Side Arm, Light That Burns the Sky - u8 additionalEffectsCounter:4; // A counter for the additionalEffects applied by the current move in Cmd_setadditionaleffects + u8 bouncedMoveIsUsed:1; u8 ballSpriteIds[2]; // item gfx, window gfx u8 appearedInBattle; // Bitfield to track which Pokemon appeared in battle. Used for Burmy's form change u8 skyDropTargets[MAX_BATTLERS_COUNT]; // For Sky Drop, to account for if multiple Pokemon use Sky Drop in a double battle. diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 7ab27aa117..fae2e09e30 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -80,6 +80,7 @@ extern const u8 BattleScript_EncoredNoMore[]; extern const u8 BattleScript_DestinyBondTakesLife[]; extern const u8 BattleScript_DmgHazardsOnAttacker[]; extern const u8 BattleScript_DmgHazardsOnTarget[]; +extern const u8 BattleScript_DmgHazardsOnBattlerScripting[]; extern const u8 BattleScript_DmgHazardsOnFaintedBattler[]; extern const u8 BattleScript_PerishSongTakesLife[]; extern const u8 BattleScript_PerishSongCountGoesDown[]; diff --git a/include/constants/abilities.h b/include/constants/abilities.h index 3cced68bb1..8568206137 100644 --- a/include/constants/abilities.h +++ b/include/constants/abilities.h @@ -326,10 +326,10 @@ #define ABILITY_MYCELIUM_MIGHT 298 #define ABILITY_HOSPITALITY 299 #define ABILITY_MINDS_EYE 300 -#define ABILITY_EMBODY_ASPECT_TEAL 301 -#define ABILITY_EMBODY_ASPECT_HEARTHFLAME 302 -#define ABILITY_EMBODY_ASPECT_WELLSPRING 303 -#define ABILITY_EMBODY_ASPECT_CORNERSTONE 304 +#define ABILITY_EMBODY_ASPECT_TEAL_MASK 301 +#define ABILITY_EMBODY_ASPECT_HEARTHFLAME_MASK 302 +#define ABILITY_EMBODY_ASPECT_WELLSPRING_MASK 303 +#define ABILITY_EMBODY_ASPECT_CORNERSTONE_MASK 304 #define ABILITY_TOXIC_CHAIN 305 #define ABILITY_SUPERSWEET_SYRUP 306 #define ABILITY_TERA_SHIFT 307 diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index f08251e2a5..273fb2c998 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -1535,7 +1535,7 @@ static u32 GetSwitchinHitsToKO(s32 damageTaken, u32 battler) s32 currentHP = startingHP; // No damage being dealt - if ((damageTaken + statusDamage + recurringDamage < recurringHealing) || damageTaken + statusDamage + recurringDamage == 0) + if ((damageTaken + statusDamage + recurringDamage <= recurringHealing) || damageTaken + statusDamage + recurringDamage == 0) return startingHP; // Mon fainted to hazards diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 3458830a12..28ae094c08 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -366,7 +366,7 @@ static inline s32 LowestRollDmg(s32 dmg) return dmg; } -bool32 IsDamageMoveUsable(u32 move, u32 battlerAtk, u32 battlerDef) +bool32 IsDamageMoveUnusable(u32 move, u32 battlerAtk, u32 battlerDef) { s32 moveType; struct AiLogicData *aiData = AI_DATA; @@ -438,6 +438,10 @@ bool32 IsDamageMoveUsable(u32 move, u32 battlerAtk, u32 battlerDef) if (!IS_BATTLER_OF_TYPE(battlerAtk, gMovesInfo[move].argument)) return TRUE; break; + case EFFECT_HIT_SET_REMOVE_TERRAIN: + if (!(gFieldStatuses & STATUS_FIELD_TERRAIN_ANY)) + return TRUE; + break; } return FALSE; @@ -486,7 +490,7 @@ s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectivenes GET_MOVE_TYPE(move, moveType); if (gMovesInfo[move].power) - isDamageMoveUnusable = IsDamageMoveUsable(move, battlerAtk, battlerDef); + isDamageMoveUnusable = IsDamageMoveUnusable(move, battlerAtk, battlerDef); effectivenessMultiplier = CalcTypeEffectivenessMultiplier(move, moveType, battlerAtk, battlerDef, aiData->abilities[battlerDef], FALSE); if (gMovesInfo[move].power && !isDamageMoveUnusable) diff --git a/src/battle_interface.c b/src/battle_interface.c index b4a975c1b8..4ae7726d14 100644 --- a/src/battle_interface.c +++ b/src/battle_interface.c @@ -3144,6 +3144,7 @@ static void PrintBattlerOnAbilityPopUp(u8 battlerId, u8 spriteId1, u8 spriteId2) static void PrintAbilityOnAbilityPopUp(u32 ability, u8 spriteId1, u8 spriteId2) { + ClearAbilityName(spriteId1, spriteId2); PrintOnAbilityPopUp(gAbilitiesInfo[ability].name, (void*)(OBJ_VRAM0) + (gSprites[spriteId1].oam.tileNum * 32) + 256, (void*)(OBJ_VRAM0) + (gSprites[spriteId2].oam.tileNum * 32) + 256, @@ -3350,7 +3351,6 @@ void UpdateAbilityPopup(u8 battlerId) u8 spriteId2 = gBattleStruct->abilityPopUpSpriteIds[battlerId][1]; u16 ability = (gBattleScripting.abilityPopupOverwrite != 0) ? gBattleScripting.abilityPopupOverwrite : gBattleMons[battlerId].ability; - ClearAbilityName(spriteId1, spriteId2); PrintAbilityOnAbilityPopUp(ability, spriteId1, spriteId2); RestoreOverwrittenPixels((void*)(OBJ_VRAM0) + (gSprites[spriteId1].oam.tileNum * 32)); } diff --git a/src/battle_main.c b/src/battle_main.c index b24facd6fd..8385572bc4 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3618,7 +3618,6 @@ const u8* FaintClearSetData(u32 battler) gProtectStructs[battler].flinchImmobility = FALSE; gProtectStructs[battler].notFirstStrike = FALSE; gProtectStructs[battler].usedHealBlockedMove = FALSE; - gProtectStructs[battler].usesBouncedMove = FALSE; gProtectStructs[battler].usedGravityPreventedMove = FALSE; gProtectStructs[battler].usedThroatChopPreventedMove = FALSE; gProtectStructs[battler].statRaised = FALSE; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 405deafaf3..889a4b8af8 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1378,9 +1378,9 @@ static void Cmd_attackcanceler(void) if (gProtectStructs[gBattlerTarget].bounceMove && gMovesInfo[gCurrentMove].magicCoatAffected - && !gProtectStructs[gBattlerAttacker].usesBouncedMove) + && !gBattleStruct->bouncedMoveIsUsed) { - gProtectStructs[gBattlerTarget].usesBouncedMove = TRUE; + gBattleStruct->bouncedMoveIsUsed = TRUE; gBattleCommunication[MULTISTRING_CHOOSER] = 0; // Edge case for bouncing a powder move against a grass type pokemon. SetAtkCancellerForCalledMove(); @@ -1397,18 +1397,33 @@ static void Cmd_attackcanceler(void) } return; } - else if (GetBattlerAbility(gBattlerTarget) == ABILITY_MAGIC_BOUNCE - && gMovesInfo[gCurrentMove].magicCoatAffected - && !gProtectStructs[gBattlerAttacker].usesBouncedMove) + else if (gMovesInfo[gCurrentMove].magicCoatAffected && !gBattleStruct->bouncedMoveIsUsed) { - gProtectStructs[gBattlerTarget].usesBouncedMove = TRUE; - gBattleCommunication[MULTISTRING_CHOOSER] = 1; - // Edge case for bouncing a powder move against a grass type pokemon. - SetAtkCancellerForCalledMove(); - BattleScriptPushCursor(); - gBattlescriptCurrInstr = BattleScript_MagicCoatBounce; - gBattlerAbility = gBattlerTarget; - return; + u32 battler = gBattlerTarget; + + if (GetBattlerAbility(gBattlerTarget) == ABILITY_MAGIC_BOUNCE) + { + battler = gBattlerTarget; + gBattleStruct->bouncedMoveIsUsed = TRUE; + } + else if (IsDoubleBattle() + && gMovesInfo[gCurrentMove].target == MOVE_TARGET_OPPONENTS_FIELD + && GetBattlerAbility(BATTLE_PARTNER(gBattlerTarget)) == ABILITY_MAGIC_BOUNCE) + { + gBattlerTarget = battler = BATTLE_PARTNER(gBattlerTarget); + gBattleStruct->bouncedMoveIsUsed = TRUE; + } + + if (gBattleStruct->bouncedMoveIsUsed) + { + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + // Edge case for bouncing a powder move against a grass type pokemon. + SetAtkCancellerForCalledMove(); + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_MagicCoatBounce; + gBattlerAbility = battler; + return; + } } // Z-moves and Max Moves bypass protection, but deal reduced damage (factored in AccumulateOtherModifiers) @@ -4423,7 +4438,15 @@ static void Cmd_getexp(void) gBattleMoveDamage += GetSoftLevelCapExpValue(gPlayerParty[*expMonId].level, gBattleStruct->expShareExpValue);; } - ApplyExperienceMultipliers(&gBattleMoveDamage, *expMonId, gBattlerFainted); + if (EXP_CAP_HARD && gBattleMoveDamage != 0) + { + u32 growthRate = gSpeciesInfo[GetMonData(&gPlayerParty[*expMonId], MON_DATA_SPECIES)].growthRate; + if (gExperienceTables[growthRate][GetCurrentLevelCap()] < gExperienceTables[growthRate][GetMonData(&gPlayerParty[*expMonId], MON_DATA_LEVEL)] + gBattleMoveDamage) + gBattleMoveDamage = gExperienceTables[growthRate][GetCurrentLevelCap()]; + } + + if (!EXP_CAP_HARD || gBattleMoveDamage != 0) // Edge case for hard level caps. Prevents mons from getting 1 exp + ApplyExperienceMultipliers(&gBattleMoveDamage, *expMonId, gBattlerFainted); if (IsTradedMon(&gPlayerParty[*expMonId])) { @@ -5930,9 +5953,10 @@ static void Cmd_moveend(void) return; } // Check if the move used was actually a bounced move. If so, we need to go back to the original attacker and make sure, its move hits all 2 or 3 pokemon. - else if (gProtectStructs[gBattlerAttacker].usesBouncedMove) + else if (gBattleStruct->bouncedMoveIsUsed) { u8 originalBounceTarget = gBattlerAttacker; + gBattleStruct->bouncedMoveIsUsed = FALSE; gBattlerAttacker = gBattleStruct->attackerBeforeBounce; gBattleStruct->targetsDone[gBattlerAttacker] |= gBitTable[originalBounceTarget]; gBattleStruct->targetsDone[originalBounceTarget] = 0; @@ -6058,8 +6082,6 @@ static void Cmd_moveend(void) for (i = 0; i < gBattlersCount; i++) { u32 holdEffect; - if (i == gBattlerAttacker) - continue; holdEffect = GetBattlerHoldEffect(i, TRUE); if (holdEffect == HOLD_EFFECT_EJECT_BUTTON) ejectButtonBattlers |= gBitTable[i]; @@ -6075,7 +6097,7 @@ static void Cmd_moveend(void) { u32 battler = battlers[i]; - if (ejectButtonBattlers & gBitTable[battler]) + if (battler != gBattlerAttacker && ejectButtonBattlers & gBitTable[battler]) { if (TestIfSheerForceAffected(gBattlerAttacker, gCurrentMove)) // Apparently Sheer Force blocks Eject Button, but not Eject Pack continue; @@ -6233,7 +6255,7 @@ static void Cmd_moveend(void) if (!(gBattleStruct->lastMoveFailed & gBitTable[gBattlerAttacker] || (!gSpecialStatuses[gBattlerAttacker].dancerUsedMove - && gProtectStructs[gBattlerAttacker].usesBouncedMove))) + && gBattleStruct->bouncedMoveIsUsed))) { // Dance move succeeds // Set target for other Dancer mons; set bit so that mon cannot activate Dancer off of its own move if (!gSpecialStatuses[gBattlerAttacker].dancerUsedMove) @@ -6328,7 +6350,6 @@ static void Cmd_moveend(void) CancelMultiTurnMoves(gBattlerAttacker); // Cancel it gBattleStruct->targetsDone[gBattlerAttacker] = 0; - gProtectStructs[gBattlerAttacker].usesBouncedMove = FALSE; gProtectStructs[gBattlerAttacker].targetAffected = FALSE; gProtectStructs[gBattlerAttacker].shellTrap = FALSE; gBattleStruct->ateBoost[gBattlerAttacker] = 0; @@ -6345,6 +6366,7 @@ static void Cmd_moveend(void) gBattleStruct->hitSwitchTargetFailed = FALSE; gBattleStruct->isAtkCancelerForCalledMove = FALSE; gBattleStruct->swapDamageCategory = FALSE; + gBattleStruct->bouncedMoveIsUsed = FALSE; gBattleStruct->enduredDamage = 0; gBattleStruct->additionalEffectsCounter = 0; gBattleStruct->poisonPuppeteerConfusion = FALSE; @@ -7062,6 +7084,8 @@ static void SetDmgHazardsBattlescript(u8 battler, u8 multistringId) gBattlescriptCurrInstr = BattleScript_DmgHazardsOnTarget; else if (gBattlescriptCurrInstr[1] == BS_ATTACKER) gBattlescriptCurrInstr = BattleScript_DmgHazardsOnAttacker; + else if (gBattlescriptCurrInstr[1] == BS_SCRIPTING) + gBattlescriptCurrInstr = BattleScript_DmgHazardsOnBattlerScripting; else gBattlescriptCurrInstr = BattleScript_DmgHazardsOnFaintedBattler; } @@ -16035,6 +16059,7 @@ void ApplyExperienceMultipliers(s32 *expAmount, u8 expGetterMonId, u8 faintedBat value *= sExperienceScalingFactors[(faintedLevel * 2) + 10]; value /= sExperienceScalingFactors[faintedLevel + expGetterLevel + 10]; + *expAmount = value + 1; } } diff --git a/src/battle_util.c b/src/battle_util.c index 6a3f43f9d7..a0b5435bc9 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -3519,10 +3519,7 @@ u8 AtkCanceller_UnableToUseMove(u32 moveType) if (effect != 0) gBattlescriptCurrInstr = BattleScript_PowderMoveNoEffect; } - if (gProtectStructs[gBattlerAttacker].usesBouncedMove) // Edge case for bouncing a powder move against a grass type pokemon. - gBattleStruct->atkCancellerTracker = CANCELLER_END; - else - gBattleStruct->atkCancellerTracker++; + gBattleStruct->atkCancellerTracker++; break; case CANCELLER_POWDER_STATUS: if (gBattleMons[gBattlerAttacker].status2 & STATUS2_POWDER) @@ -4775,7 +4772,10 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 case ABILITY_HOSPITALITY: partner = BATTLE_PARTNER(battler); - if (!gSpecialStatuses[battler].switchInAbilityDone && IsDoubleBattle() && gBattleMons[partner].hp < gBattleMons[partner].maxHP) + if (!gSpecialStatuses[battler].switchInAbilityDone + && IsDoubleBattle() + && gBattleMons[partner].hp < gBattleMons[partner].maxHP + && IsBattlerAlive(partner)) { gBattlerTarget = partner; gBattlerAttacker = battler; @@ -4785,22 +4785,22 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 effect++; } break; - case ABILITY_EMBODY_ASPECT_TEAL: - case ABILITY_EMBODY_ASPECT_HEARTHFLAME: - case ABILITY_EMBODY_ASPECT_WELLSPRING: - case ABILITY_EMBODY_ASPECT_CORNERSTONE: + case ABILITY_EMBODY_ASPECT_TEAL_MASK: + case ABILITY_EMBODY_ASPECT_HEARTHFLAME_MASK: + case ABILITY_EMBODY_ASPECT_WELLSPRING_MASK: + case ABILITY_EMBODY_ASPECT_CORNERSTONE_MASK: if (!gSpecialStatuses[battler].switchInAbilityDone) { - u32 stat = STAT_SPATK; + u32 stat; - if (gLastUsedAbility == ABILITY_EMBODY_ASPECT_TEAL) - stat = STAT_SPATK; - else if (gLastUsedAbility == ABILITY_EMBODY_ASPECT_HEARTHFLAME) + if (gLastUsedAbility == ABILITY_EMBODY_ASPECT_HEARTHFLAME_MASK) stat = STAT_ATK; - else if (gLastUsedAbility == ABILITY_EMBODY_ASPECT_WELLSPRING) + else if (gLastUsedAbility == ABILITY_EMBODY_ASPECT_WELLSPRING_MASK) stat = STAT_SPDEF; - else if (gLastUsedAbility == ABILITY_EMBODY_ASPECT_CORNERSTONE) + else if (gLastUsedAbility == ABILITY_EMBODY_ASPECT_CORNERSTONE_MASK) stat = STAT_DEF; + else //ABILITY_EMBODY_ASPECT_TEAL_MASK + stat = STAT_SPEED; if (CompareStat(battler, stat, MAX_STAT_STAGE, CMP_EQUAL)) break; diff --git a/src/data/abilities.h b/src/data/abilities.h index 8c26934d9a..a65d4e8c24 100644 --- a/src/data/abilities.h +++ b/src/data/abilities.h @@ -1276,6 +1276,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] = .name = _("Fur Coat"), .description = COMPOUND_STRING("Raises Defense."), .aiRating = 7, + .breakable = TRUE, }, [ABILITY_MAGICIAN] = @@ -2472,7 +2473,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] = .breakable = TRUE, }, - [ABILITY_EMBODY_ASPECT_TEAL] = + [ABILITY_EMBODY_ASPECT_TEAL_MASK] = { #if B_EXPANDED_ABILITY_NAMES == TRUE .name = _("Embody Aspect"), @@ -2487,7 +2488,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] = .failsOnImposter = TRUE, }, - [ABILITY_EMBODY_ASPECT_HEARTHFLAME] = + [ABILITY_EMBODY_ASPECT_HEARTHFLAME_MASK] = { #if B_EXPANDED_ABILITY_NAMES == TRUE .name = _("Embody Aspect"), @@ -2502,7 +2503,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] = .failsOnImposter = TRUE, }, - [ABILITY_EMBODY_ASPECT_WELLSPRING] = + [ABILITY_EMBODY_ASPECT_WELLSPRING_MASK] = { #if B_EXPANDED_ABILITY_NAMES == TRUE .name = _("Embody Aspect"), @@ -2517,7 +2518,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] = .failsOnImposter = TRUE, }, - [ABILITY_EMBODY_ASPECT_CORNERSTONE] = + [ABILITY_EMBODY_ASPECT_CORNERSTONE_MASK] = { #if B_EXPANDED_ABILITY_NAMES == TRUE .name = _("Embody Aspect"), diff --git a/src/data/pokemon/species_info/gen_9_families.h b/src/data/pokemon/species_info/gen_9_families.h index b334c59110..d272120cf9 100644 --- a/src/data/pokemon/species_info/gen_9_families.h +++ b/src/data/pokemon/species_info/gen_9_families.h @@ -6312,10 +6312,10 @@ const struct SpeciesInfo gSpeciesInfoGen9[] = [SPECIES_OGERPON_HEARTHFLAME_MASK] = OGERPON_SPECIES_INFO(HearthflameMask, TYPE_FIRE, ABILITY_MOLD_BREAKER, BODY_COLOR_RED, 0), [SPECIES_OGERPON_CORNERSTONE_MASK] = OGERPON_SPECIES_INFO(CornerstoneMask, TYPE_ROCK, ABILITY_STURDY, BODY_COLOR_GRAY, 0), #if P_TERA_FORMS - [SPECIES_OGERPON_TEAL_MASK_TERA] = OGERPON_SPECIES_INFO(TealMask, TYPE_GRASS, ABILITY_EMBODY_ASPECT_TEAL, BODY_COLOR_GREEN, 1), - [SPECIES_OGERPON_WELLSPRING_MASK_TERA] = OGERPON_SPECIES_INFO(WellspringMask, TYPE_WATER, ABILITY_EMBODY_ASPECT_WELLSPRING, BODY_COLOR_BLUE, 0), - [SPECIES_OGERPON_HEARTHFLAME_MASK_TERA] = OGERPON_SPECIES_INFO(HearthflameMask, TYPE_FIRE, ABILITY_EMBODY_ASPECT_HEARTHFLAME, BODY_COLOR_RED, 0), - [SPECIES_OGERPON_CORNERSTONE_MASK_TERA] = OGERPON_SPECIES_INFO(CornerstoneMask, TYPE_ROCK, ABILITY_EMBODY_ASPECT_CORNERSTONE, BODY_COLOR_GRAY, 0), + [SPECIES_OGERPON_TEAL_MASK_TERA] = OGERPON_SPECIES_INFO(TealMask, TYPE_GRASS, ABILITY_EMBODY_ASPECT_TEAL_MASK, BODY_COLOR_GREEN, 1), + [SPECIES_OGERPON_WELLSPRING_MASK_TERA] = OGERPON_SPECIES_INFO(WellspringMask, TYPE_WATER, ABILITY_EMBODY_ASPECT_WELLSPRING_MASK, BODY_COLOR_BLUE, 0), + [SPECIES_OGERPON_HEARTHFLAME_MASK_TERA] = OGERPON_SPECIES_INFO(HearthflameMask, TYPE_FIRE, ABILITY_EMBODY_ASPECT_HEARTHFLAME_MASK, BODY_COLOR_RED, 0), + [SPECIES_OGERPON_CORNERSTONE_MASK_TERA] = OGERPON_SPECIES_INFO(CornerstoneMask, TYPE_ROCK, ABILITY_EMBODY_ASPECT_CORNERSTONE_MASK, BODY_COLOR_GRAY, 0), #endif //P_TERA_FORMS #endif //P_FAMILY_OGERPON diff --git a/src/pokedex_plus_hgss.c b/src/pokedex_plus_hgss.c index 5297a2a733..45b7800564 100644 --- a/src/pokedex_plus_hgss.c +++ b/src/pokedex_plus_hgss.c @@ -386,7 +386,7 @@ struct PokemonStats u8 eggGroup1; u8 eggGroup2; u8 eggCycles; - u16 expYield; + u16 expYield; u8 friendship; u16 ability0; u16 ability1; diff --git a/src/pokemon.c b/src/pokemon.c index 7c2b749f12..9e0fbe6848 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -3534,8 +3534,17 @@ bool8 PokemonUseItemEffects(struct Pokemon *mon, u16 item, u8 partyIndex, u8 mov { u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL); dataUnsigned = sExpCandyExperienceTable[param - 1] + GetMonData(mon, MON_DATA_EXP, NULL); - if (dataUnsigned > gExperienceTables[gSpeciesInfo[species].growthRate][MAX_LEVEL]) + + if (B_RARE_CANDY_CAP && EXP_CAP_HARD) + { + u32 currentLevelCap = GetCurrentLevelCap(); + if (dataUnsigned > gExperienceTables[gSpeciesInfo[species].growthRate][currentLevelCap]) + dataUnsigned = gExperienceTables[gSpeciesInfo[species].growthRate][currentLevelCap]; + } + else if (dataUnsigned > gExperienceTables[gSpeciesInfo[species].growthRate][MAX_LEVEL]) + { dataUnsigned = gExperienceTables[gSpeciesInfo[species].growthRate][MAX_LEVEL]; + } } if (dataUnsigned != 0) // Failsafe diff --git a/test/battle/ability/color_change.c b/test/battle/ability/color_change.c new file mode 100644 index 0000000000..b7828bb978 --- /dev/null +++ b/test/battle/ability/color_change.c @@ -0,0 +1,148 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Color Change changes the type of a Pokemon being hit by a move if the type of the move and the Pokemon are different") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_COLOR_CHANGE); } + } WHEN { + TURN { MOVE(player, MOVE_TACKLE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); + ABILITY_POPUP(opponent, ABILITY_COLOR_CHANGE); + MESSAGE("Foe Wobbuffet's Color Change made it the Normal type!"); + } +} + +SINGLE_BATTLE_TEST("Color Change does not change the type when hit by a move that's the same type as itself") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_COLOR_CHANGE); } + } WHEN { + TURN { MOVE(player, MOVE_PSYCHO_CUT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PSYCHO_CUT, player); + NONE_OF { + ABILITY_POPUP(opponent, ABILITY_COLOR_CHANGE); + MESSAGE("Foe Wobbuffet's Color Change made it the Normal type!"); + } + } +} + +SINGLE_BATTLE_TEST("Color Change does not change the type of a dual-type Pokemon when hit by a move that shares its primary type") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_XATU) { Ability(ABILITY_COLOR_CHANGE); } + } WHEN { + TURN { MOVE(player, MOVE_PSYCHO_CUT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PSYCHO_CUT, player); + NONE_OF { + ABILITY_POPUP(opponent, ABILITY_COLOR_CHANGE); + MESSAGE("Foe Xatu's Color Change made it the Psychic type!"); + } + } +} + +SINGLE_BATTLE_TEST("Color Change does not change the type of a dual-type Pokemon when hit by a move that shares its secondary type") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_SLOWBRO) { Ability(ABILITY_COLOR_CHANGE); } + } WHEN { + TURN { MOVE(player, MOVE_PSYCHO_CUT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PSYCHO_CUT, player); + NONE_OF { + ABILITY_POPUP(opponent, ABILITY_COLOR_CHANGE); + MESSAGE("Foe Slowbro's Color Change made it the Psychic type!"); + } + } +} + +SINGLE_BATTLE_TEST("Color Change changes the user to Electric type if hit by a move while the opponent is under the effect of Electrify") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_COLOR_CHANGE); } + } WHEN { + TURN { MOVE(opponent, MOVE_ELECTRIFY); MOVE(player, MOVE_PSYCHO_CUT); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_PSYCHO_CUT, player); + ABILITY_POPUP(opponent, ABILITY_COLOR_CHANGE); + MESSAGE("Foe Wobbuffet's Color Change made it the Electr type!"); + } +} + +SINGLE_BATTLE_TEST("Color Change changes the type when a Pokemon is hit by Future Sight") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_SNORLAX) { Ability(ABILITY_COLOR_CHANGE); } + } WHEN { + TURN { MOVE(player, MOVE_FUTURE_SIGHT); } + TURN { } + TURN { } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FUTURE_SIGHT, player); + MESSAGE("Foe Snorlax took the Future Sight attack!"); + ABILITY_POPUP(opponent, ABILITY_COLOR_CHANGE); + MESSAGE("Foe Snorlax's Color Change made it the Psychc type!"); + } +} + +SINGLE_BATTLE_TEST("Color Change changes the type when a Pokemon is hit by Doom Desire") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_COLOR_CHANGE); } + } WHEN { + TURN { MOVE(player, MOVE_DOOM_DESIRE); } + TURN { } + TURN { } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOOM_DESIRE, player); + MESSAGE("Foe Wobbuffet took the Doom Desire attack!"); + ABILITY_POPUP(opponent, ABILITY_COLOR_CHANGE); + MESSAGE("Foe Wobbuffet's Color Change made it the Steel type!"); + } +} + +SINGLE_BATTLE_TEST("Color Change changes the type to Electric when a Pokemon is hit by a forseen attack under the effect of Electrify") +{ + KNOWN_FAILING; // #4471. + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_BLASTOISE) { Ability(ABILITY_COLOR_CHANGE); } + } WHEN { + TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_FUTURE_SIGHT); } + TURN { } + TURN { MOVE(opponent, MOVE_ELECTRIFY); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FUTURE_SIGHT, player); + MESSAGE("Foe Blastoise took the Future Sight attack!"); + MESSAGE("It's super effective!"); + ABILITY_POPUP(opponent, ABILITY_COLOR_CHANGE); + MESSAGE("Foe Blastoise's Color Change made it the Electr type!"); + } +} + +SINGLE_BATTLE_TEST("Color Change changes the type to Normal when a Pokemon is hit by a forseen attack under the effect of Normalize") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_NORMALIZE); } + OPPONENT(SPECIES_BLASTOISE) { Ability(ABILITY_COLOR_CHANGE); } + } WHEN { + TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_FUTURE_SIGHT); } + TURN { } + TURN { } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FUTURE_SIGHT, player); + MESSAGE("Foe Blastoise took the Future Sight attack!"); + ABILITY_POPUP(opponent, ABILITY_COLOR_CHANGE); + MESSAGE("Foe Blastoise's Color Change made it the Normal type!"); + } +} diff --git a/test/battle/ability/embody_aspect.c b/test/battle/ability/embody_aspect.c index 8b37dd1a74..728ba8cb94 100644 --- a/test/battle/ability/embody_aspect.c +++ b/test/battle/ability/embody_aspect.c @@ -2,14 +2,14 @@ #include "test/battle.h" -SINGLE_BATTLE_TEST("Embodoy Aspect raises a stat depending on the users form by one stage") +SINGLE_BATTLE_TEST("Embody Aspect raises a stat depending on the users form by one stage") { u16 species, ability; - PARAMETRIZE { species = SPECIES_OGERPON_TEAL_MASK_TERA; ability = ABILITY_EMBODY_ASPECT_TEAL; } - PARAMETRIZE { species = SPECIES_OGERPON_HEARTHFLAME_MASK_TERA; ability = ABILITY_EMBODY_ASPECT_HEARTHFLAME; } - PARAMETRIZE { species = SPECIES_OGERPON_WELLSPRING_MASK_TERA; ability = ABILITY_EMBODY_ASPECT_WELLSPRING; } - PARAMETRIZE { species = SPECIES_OGERPON_CORNERSTONE_MASK_TERA; ability = ABILITY_EMBODY_ASPECT_CORNERSTONE; } + PARAMETRIZE { species = SPECIES_OGERPON_TEAL_MASK_TERA; ability = ABILITY_EMBODY_ASPECT_TEAL_MASK; } + PARAMETRIZE { species = SPECIES_OGERPON_HEARTHFLAME_MASK_TERA; ability = ABILITY_EMBODY_ASPECT_HEARTHFLAME_MASK; } + PARAMETRIZE { species = SPECIES_OGERPON_WELLSPRING_MASK_TERA; ability = ABILITY_EMBODY_ASPECT_WELLSPRING_MASK; } + PARAMETRIZE { species = SPECIES_OGERPON_CORNERSTONE_MASK_TERA; ability = ABILITY_EMBODY_ASPECT_CORNERSTONE_MASK; } GIVEN { PLAYER(SPECIES_WOBBUFFET); @@ -19,32 +19,32 @@ SINGLE_BATTLE_TEST("Embodoy Aspect raises a stat depending on the users form by } SCENE { ABILITY_POPUP(opponent, ability); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); - if (ability == ABILITY_EMBODY_ASPECT_TEAL) - MESSAGE("Foe Ogerpon's Embody Aspect raised its Sp. Atk!"); - else if (ability == ABILITY_EMBODY_ASPECT_HEARTHFLAME) + if (ability == ABILITY_EMBODY_ASPECT_TEAL_MASK) + MESSAGE("Foe Ogerpon's Embody Aspect raised its Speed!"); + else if (ability == ABILITY_EMBODY_ASPECT_HEARTHFLAME_MASK) MESSAGE("Foe Ogerpon's Embody Aspect raised its Attack!"); - else if (ability == ABILITY_EMBODY_ASPECT_WELLSPRING) + else if (ability == ABILITY_EMBODY_ASPECT_WELLSPRING_MASK) MESSAGE("Foe Ogerpon's Embody Aspect raised its Sp. Def!"); - else if (ability == ABILITY_EMBODY_ASPECT_CORNERSTONE) + else if (ability == ABILITY_EMBODY_ASPECT_CORNERSTONE_MASK) MESSAGE("Foe Ogerpon's Embody Aspect raised its Defense!"); } THEN { - if (ability == ABILITY_EMBODY_ASPECT_TEAL) - EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1); - else if (ability == ABILITY_EMBODY_ASPECT_HEARTHFLAME) + if (ability == ABILITY_EMBODY_ASPECT_TEAL_MASK) + EXPECT_EQ(opponent->statStages[STAT_SPEED], DEFAULT_STAT_STAGE + 1); + else if (ability == ABILITY_EMBODY_ASPECT_HEARTHFLAME_MASK) EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1); - else if (ability == ABILITY_EMBODY_ASPECT_WELLSPRING) + else if (ability == ABILITY_EMBODY_ASPECT_WELLSPRING_MASK) EXPECT_EQ(opponent->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE + 1); - else if (ability == ABILITY_EMBODY_ASPECT_CORNERSTONE) + else if (ability == ABILITY_EMBODY_ASPECT_CORNERSTONE_MASK) EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 1); } } -SINGLE_BATTLE_TEST("Embodoy Aspect activates when it's no longer effected by Neutralizing Gas") +SINGLE_BATTLE_TEST("Embody Aspect activates when it's no longer effected by Neutralizing Gas") { GIVEN { PLAYER(SPECIES_WEEZING) { Ability(ABILITY_NEUTRALIZING_GAS); } PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_OGERPON_TEAL_MASK_TERA) { Ability(ABILITY_EMBODY_ASPECT_TEAL); } + OPPONENT(SPECIES_OGERPON_TEAL_MASK_TERA) { Ability(ABILITY_EMBODY_ASPECT_TEAL_MASK); } } WHEN { TURN { SWITCH(player, 1); } } SCENE { @@ -52,8 +52,8 @@ SINGLE_BATTLE_TEST("Embodoy Aspect activates when it's no longer effected by Neu MESSAGE("Neutralizing Gas filled the area!"); MESSAGE("Weezing, that's enough! Come back!"); MESSAGE("The effects of Neutralizing Gas wore off!"); - ABILITY_POPUP(opponent, ABILITY_EMBODY_ASPECT_TEAL); + ABILITY_POPUP(opponent, ABILITY_EMBODY_ASPECT_TEAL_MASK); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); - MESSAGE("Foe Ogerpon's Embody Aspect raised its Sp. Atk!"); + MESSAGE("Foe Ogerpon's Embody Aspect raised its Speed!"); } } diff --git a/test/battle/ability/hospitality.c b/test/battle/ability/hospitality.c index dc81faf7de..8f494ba4c6 100644 --- a/test/battle/ability/hospitality.c +++ b/test/battle/ability/hospitality.c @@ -68,3 +68,24 @@ DOUBLE_BATTLE_TEST("Hospitality ignores Substitute") MESSAGE("Wobbuffet drank down all the matcha that Poltchageist made!"); } } + +DOUBLE_BATTLE_TEST("Hospitality does not trigger if there is no ally on the field") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { HP(1); } + PLAYER(SPECIES_WOBBUFFET) { HP(1); } + PLAYER(SPECIES_POLTCHAGEIST) { Ability(ABILITY_HOSPITALITY); } + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponentLeft, MOVE_BLIZZARD); SEND_OUT(playerLeft, 2); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_BLIZZARD, opponentLeft); + HP_BAR(playerLeft); + MESSAGE("Wobbuffet fainted!"); + HP_BAR(playerRight); + MESSAGE("Wobbuffet fainted!"); + MESSAGE("Go! Ptchageist!"); + NOT ABILITY_POPUP(playerLeft, ABILITY_HOSPITALITY); + } +} diff --git a/test/battle/ability/magic_bounce.c b/test/battle/ability/magic_bounce.c index 95eaf382e6..8d583b154d 100644 --- a/test/battle/ability/magic_bounce.c +++ b/test/battle/ability/magic_bounce.c @@ -81,3 +81,54 @@ DOUBLE_BATTLE_TEST("Magic Bounce bounces back moves hitting both foes at two foe MESSAGE("Foe Wynaut's Defense fell!"); } } + +DOUBLE_BATTLE_TEST("Magic Bounce bounces back moves hitting foes field") +{ + u32 battlerOne, battlerTwo, abilityBattlerOne, abilityBattlerTwo; + + PARAMETRIZE { battlerOne = SPECIES_NATU; abilityBattlerOne = ABILITY_MAGIC_BOUNCE; + battlerTwo = SPECIES_ESPEON; abilityBattlerTwo = ABILITY_SYNCHRONIZE; } + PARAMETRIZE { battlerOne = SPECIES_NATU; abilityBattlerOne = ABILITY_KEEN_EYE; + battlerTwo = SPECIES_ESPEON; abilityBattlerTwo = ABILITY_MAGIC_BOUNCE; } + + GIVEN { + ASSUME(gMovesInfo[MOVE_STEALTH_ROCK].target == MOVE_TARGET_OPPONENTS_FIELD); + PLAYER(SPECIES_ABRA); + PLAYER(SPECIES_KADABRA); + OPPONENT(battlerOne) { Ability(abilityBattlerOne); } + OPPONENT(battlerTwo) { Ability(abilityBattlerTwo); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_STEALTH_ROCK); } + } SCENE { + if (abilityBattlerOne == ABILITY_MAGIC_BOUNCE) + ABILITY_POPUP(opponentLeft, ABILITY_MAGIC_BOUNCE); + else + ABILITY_POPUP(opponentRight, ABILITY_MAGIC_BOUNCE); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_STEALTH_ROCK, playerLeft); + if (abilityBattlerOne == ABILITY_MAGIC_BOUNCE) { + MESSAGE("Abra's Stealth Rock was bounced back by Foe Natu's Magic Bounce!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_STEALTH_ROCK, opponentLeft); + } else { + MESSAGE("Abra's Stealth Rock was bounced back by Foe Espeon's Magic Bounce!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_STEALTH_ROCK, opponentRight); + } + } +} + +SINGLE_BATTLE_TEST("Magic Bounce bounced back status moves can not be bounced back by Magic Bounce") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_TOXIC].effect == EFFECT_TOXIC); + PLAYER(SPECIES_ESPEON) { Ability(ABILITY_MAGIC_BOUNCE); } + OPPONENT(SPECIES_ESPEON) { Ability(ABILITY_MAGIC_BOUNCE); } + } WHEN { + TURN { MOVE(player, MOVE_TOXIC); } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_MAGIC_BOUNCE); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC, player); + MESSAGE("Espeon's Toxic was bounced back by Foe Espeon's Magic Bounce!"); + NOT ABILITY_POPUP(player, ABILITY_MAGIC_BOUNCE); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC, opponent); + STATUS_ICON(player, badPoison: TRUE); + } +} diff --git a/test/battle/ai.c b/test/battle/ai.c index 0597925356..0d890da466 100644 --- a/test/battle/ai.c +++ b/test/battle/ai.c @@ -548,6 +548,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Number of hits to KO calculati AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Number of hits to KO calculation checks whether incoming damage is zero to avoid an infinite loop") { GIVEN { + ASSUME(gItemsInfo[ITEM_LEFTOVERS].holdEffect == HOLD_EFFECT_LEFTOVERS); AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES); PLAYER(SPECIES_BULBASAUR) { Level(5); Moves(MOVE_SWORDS_DANCE, MOVE_WHIRLWIND, MOVE_SAND_ATTACK, MOVE_TAIL_WHIP); } // Scenario courtesy of Duke, who triggered the bug in the first place @@ -561,6 +562,23 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Number of hits to KO calculati } } +AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Avoid infinite loop if damage taken is equal to recurring healing") +{ + GIVEN { + ASSUME(gItemsInfo[ITEM_LEFTOVERS].holdEffect == HOLD_EFFECT_LEFTOVERS); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES); + PLAYER(SPECIES_MEOWTH_GALARIAN) { Level(100); Moves(MOVE_GROWL, MOVE_FAKE_OUT, MOVE_HONE_CLAWS); } + // Scenario courtesy of Duke, who triggered the bug in the first place + OPPONENT(SPECIES_MEOWTH_GALARIAN) { Level(5); Moves(MOVE_GROWL, MOVE_FAKE_OUT, MOVE_HONE_CLAWS); } + OPPONENT(SPECIES_GEODUDE) { Level(5); Moves(MOVE_DOUBLE_EDGE); } + OPPONENT(SPECIES_GEODUDE) { Level(5); Moves(MOVE_DOUBLE_EDGE); } + OPPONENT(SPECIES_NOSEPASS) { Level(5); Moves(MOVE_DOUBLE_EDGE); } + OPPONENT(SPECIES_HOUNDSTONE) { Level(5); Moves(MOVE_NIGHT_SHADE, MOVE_BODY_PRESS, MOVE_WILL_O_WISP, MOVE_PROTECT); Item(ITEM_LEFTOVERS); } + } WHEN { + TURN { MOVE(player, MOVE_FAKE_OUT); EXPECT_MOVES(opponent, MOVE_FAKE_OUT); } + } +} + AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: AI will not switch in a Pokemon which is slower and gets 1HKOed after fainting") { bool32 alakazamFirst; diff --git a/test/battle/hold_effect/eject_pack.c b/test/battle/hold_effect/eject_pack.c index 1a54d5a2e9..6757b11db9 100644 --- a/test/battle/hold_effect/eject_pack.c +++ b/test/battle/hold_effect/eject_pack.c @@ -22,6 +22,7 @@ SINGLE_BATTLE_TEST("Eject Pack does not cause the new pokemon to lose hp due to MESSAGE("Wobbuffet is switched out with the Eject Pack!"); MESSAGE("Go! Wynaut!"); NOT MESSAGE("Wynaut was hurt by its Life Orb!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent); } } @@ -41,3 +42,24 @@ SINGLE_BATTLE_TEST("Eject Pack does not activate if there are no pokemon left to } } } + +SINGLE_BATTLE_TEST("Eject Pack triggers on the correct pokemon") +{ + GIVEN { + ASSUME(gItemsInfo[ITEM_LIFE_ORB].holdEffect == HOLD_EFFECT_LIFE_ORB); + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_EJECT_PACK); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_EJECT_PACK); } + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(player, MOVE_OVERHEAT); SEND_OUT(player, 1); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_OVERHEAT, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + MESSAGE("Wobbuffet is switched out with the Eject Pack!"); + MESSAGE("Go! Wynaut!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent); + } +} diff --git a/test/battle/move_effect/stealth_rock.c b/test/battle/move_effect/stealth_rock.c index 23a773f8e8..f8c7a6369e 100644 --- a/test/battle/move_effect/stealth_rock.c +++ b/test/battle/move_effect/stealth_rock.c @@ -30,3 +30,46 @@ SINGLE_BATTLE_TEST("Stealth Rock damage on switch in based on typing") HP_BAR(opponent, damage: maxHP / divisor); } } + +SINGLE_BATTLE_TEST("Stealth Rock damages the correct pokemon when Eject Button is triggered") +{ + GIVEN { + PLAYER(SPECIES_METAPOD) { Item(ITEM_EJECT_BUTTON); } + PLAYER(SPECIES_METAPOD); + OPPONENT(SPECIES_JOLTEON); + } WHEN { + TURN { MOVE(opponent, MOVE_STEALTH_ROCK); MOVE(player, MOVE_HARDEN); } + TURN { MOVE(opponent, MOVE_QUICK_ATTACK); MOVE(player, MOVE_HARDEN); SEND_OUT(player, 1); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_STEALTH_ROCK, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_HARDEN, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_ATTACK, opponent); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_HARDEN, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + MESSAGE("Go! Metapod!"); + HP_BAR(player); + } THEN { + EXPECT_EQ(opponent->hp, opponent->maxHP); + } +} + +DOUBLE_BATTLE_TEST("Stealth Rock damages the correct pokemon when Eject Button is triggered in double battle") +{ + GIVEN { + PLAYER(SPECIES_METAPOD) { Item(ITEM_EJECT_BUTTON); } + PLAYER(SPECIES_METAPOD) { Item(ITEM_EJECT_BUTTON); } + PLAYER(SPECIES_METAPOD); + OPPONENT(SPECIES_JOLTEON); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponentLeft, MOVE_STEALTH_ROCK); MOVE(opponentRight, MOVE_TACKLE, target: playerLeft); SEND_OUT(playerLeft, 2); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_STEALTH_ROCK, opponentLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponentRight); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft); + MESSAGE("Go! Metapod!"); + HP_BAR(playerLeft); + } THEN { + EXPECT_EQ(opponentLeft->hp, opponentLeft->maxHP); + } +}