diff --git a/.all-contributorsrc b/.all-contributorsrc index aa13a3864d..a54e1caefd 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -441,6 +441,15 @@ "contributions": [ "code" ] + }, + { + "login": "MandL27", + "name": "MandL27", + "avatar_url": "https://avatars.githubusercontent.com/u/10366615?v=4", + "profile": "https://github.com/MandL27", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7, diff --git a/CREDITS.md b/CREDITS.md index d3d46d0361..547b5bd52d 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -74,6 +74,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d ghostyboyy97
ghostyboyy97

💻 Marky
Marky

💻 + MandL27
MandL27

💻 diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 01b25762cd..cecd0c0be8 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -2536,6 +2536,7 @@ BattleScript_CantMakeAsleep:: BattleScript_EffectAbsorbLiquidOoze:: call BattleScript_AbilityPopUpTarget + jumpifability BS_ATTACKER, ABILITY_MAGIC_GUARD, BattleScript_EffectAbsorbRet goto BattleScript_EffectAbsorb BattleScript_EffectAbsorb:: @@ -2544,6 +2545,7 @@ BattleScript_EffectAbsorb:: printfromtable gAbsorbDrainStringIds waitmessage B_WAIT_TIME_LONG tryfaintmon BS_ATTACKER +BattleScript_EffectAbsorbRet: return BattleScript_EffectExplosion:: @@ -4945,10 +4947,12 @@ BattleScript_LeechSeedTurnDrainLiquidOoze:: copybyte gBattlerAbility, gBattlerAttacker call BattleScript_AbilityPopUp copybyte gBattlerAttacker, gBattlerTarget @ needed to get liquid ooze message correct + jumpifability BS_TARGET, ABILITY_MAGIC_GUARD, BattleScript_LeechSeedTurnDrainHealBlockEnd2 goto BattleScript_LeechSeedTurnDrainGainHp BattleScript_LeechSeedTurnDrainHealBlock:: call BattleScript_LeechSeedTurnDrain +BattleScript_LeechSeedTurnDrainHealBlockEnd2: end2 BattleScript_LeechSeedTurnDrainRecovery:: diff --git a/graphics/pokemon/kingler/overworld.png b/graphics/pokemon/kingler/overworld.png index 135f79b326..c67ce3a53a 100644 Binary files a/graphics/pokemon/kingler/overworld.png and b/graphics/pokemon/kingler/overworld.png differ diff --git a/graphics/pokemon/krabby/overworld.png b/graphics/pokemon/krabby/overworld.png index abe66ceb6f..f712b6f6ff 100644 Binary files a/graphics/pokemon/krabby/overworld.png and b/graphics/pokemon/krabby/overworld.png differ diff --git a/include/battle_gimmick.h b/include/battle_gimmick.h index 42b8c7255a..84b1d6b26a 100644 --- a/include/battle_gimmick.h +++ b/include/battle_gimmick.h @@ -35,6 +35,7 @@ void SetGimmickAsActivated(u32 battler, enum Gimmick gimmick); void ChangeGimmickTriggerSprite(u32 spriteId, u32 animId); void CreateGimmickTriggerSprite(u32 battler); bool32 IsGimmickTriggerSpriteActive(void); +bool32 IsGimmickTriggerSpriteMatchingBattler(u32 battler); void HideGimmickTriggerSprite(void); void DestroyGimmickTriggerSprite(void); diff --git a/include/battle_util.h b/include/battle_util.h index cc65a79bd5..f6d92b6b11 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -423,6 +423,7 @@ bool32 CanMoveSkipAccuracyCalc(u32 battlerAtk, u32 battlerDef, enum Ability abil u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, enum Ability atkAbility, enum Ability defAbility, enum HoldEffect atkHoldEffect, enum HoldEffect defHoldEffect); bool32 IsSemiInvulnerable(u32 battler, enum SemiInvulnerableExclusion excludeCommander); bool32 BreaksThroughSemiInvulnerablity(u32 battler, u32 move); +bool32 HasPartnerTrainer(u32 battler); u32 GetNaturePowerMove(u32 battler); u32 GetNaturePowerMove(u32 battler); void RemoveAbilityFlags(u32 battler); diff --git a/include/config/summary_screen.h b/include/config/summary_screen.h index 4b5f434cb3..07bae63533 100644 --- a/include/config/summary_screen.h +++ b/include/config/summary_screen.h @@ -8,6 +8,7 @@ #define P_SUMMARY_SCREEN_RENAME TRUE // If TRUE, an option to change Pokémon nicknames replaces the cancel prompt on the summary screen info page. #define P_SUMMARY_SCREEN_IV_EV_INFO FALSE // If TRUE, will allow player to cycle through the Stats, IVs, and EVs in the summary screen skills page. #define P_SUMMARY_SCREEN_IV_EV_BOX_ONLY FALSE // If TRUE, will allow player to cycle through the Stats, IVs, and EVs in the summary screen skills page, but only in the PC storage box. +#define P_SUMMARY_SCREEN_IV_HYPERTRAIN TRUE // If TRUE, stats that have been hyper trained will show as 31/S when viewing them in the summary screen #define P_SUMMARY_SCREEN_IV_EV_TILESET FALSE // If TRUE, loads an alternate tileset to allow changing the "STATS" label in the summary screen skills page. Note: if it's still loading the alternate tileset after changing this and recompiling, you may need a `make clean` before compilation. #define P_SUMMARY_SCREEN_IV_EV_VALUES FALSE // If TRUE, will show the actual IV value instead of the letter grade. /* diff --git a/include/constants/battle.h b/include/constants/battle.h index 12f34a396b..380903acdb 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -103,6 +103,7 @@ enum BattleSide #define BATTLE_TWO_VS_ONE_OPPONENT ((gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && TRAINER_BATTLE_PARAM.opponentB == 0xFFFF)) #define BATTLE_TYPE_HAS_AI (BATTLE_TYPE_TRAINER | BATTLE_TYPE_FIRST_BATTLE | BATTLE_TYPE_SAFARI | BATTLE_TYPE_ROAMER | BATTLE_TYPE_INGAME_PARTNER) #define BATTLE_TYPE_MORE_THAN_TWO_BATTLERS (BATTLE_TYPE_DOUBLE | BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER | BATTLE_TYPE_TWO_OPPONENTS) +#define BATTLE_TYPE_PLAYER_HAS_PARTNER (BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER | BATTLE_TYPE_TOWER_LINK_MULTI) // Multibattle test composite flags #define BATTLE_MULTI_TEST (BATTLE_TYPE_IS_MASTER | BATTLE_TYPE_TRAINER | BATTLE_TYPE_INGAME_PARTNER | BATTLE_TYPE_MULTI | BATTLE_TYPE_TWO_OPPONENTS) diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index e30ba186af..0b9c9ed3e1 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -1119,7 +1119,7 @@ bool32 ShouldSwitch(u32 battler) if (IsDoubleBattle()) { - u32 partner = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerAtPosition(battler))); + u32 partner = BATTLE_PARTNER(battler); battlerIn1 = battler; if (gAbsentBattlerFlags & (1u << partner)) battlerIn2 = battler; @@ -1270,7 +1270,7 @@ void ModifySwitchAfterMoveScoring(u32 battler) if (IsDoubleBattle()) { - u32 partner = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerAtPosition(battler))); + u32 partner = BATTLE_PARTNER(battler); battlerIn1 = battler; if (gAbsentBattlerFlags & (1u << partner)) battlerIn2 = battler; @@ -1318,7 +1318,7 @@ bool32 IsSwitchinValid(u32 battler) // Edge case: See if partner already chose to switch into the same mon if (IsDoubleBattle()) { - u32 partner = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerAtPosition(battler))); + u32 partner = BATTLE_PARTNER(battler); if (gBattleStruct->AI_monToSwitchIntoId[battler] == PARTY_SIZE) // Generic switch { if ((gAiLogicData->shouldSwitch & (1u << partner)) && gAiLogicData->monToSwitchInId[partner] == gAiLogicData->mostSuitableMonId[battler]) diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 202e9fd041..008bef4812 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -891,7 +891,7 @@ static inline bool32 ShouldCalcCritDamage(u32 battlerAtk, u32 battlerDef, u32 mo struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, uq4_12_t *typeEffectiveness, enum AIConsiderGimmick considerGimmickAtk, enum AIConsiderGimmick considerGimmickDef, u32 weather) { - struct SimulatedDamage simDamage; + struct SimulatedDamage simDamage = {0}; enum BattleMoveEffects moveEffect = GetMoveEffect(move); bool32 isDamageMoveUnusable = FALSE; bool32 toggledGimmickAtk = FALSE; @@ -924,7 +924,7 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u gBattleStruct->magnitudeBasePower = 70; gBattleStruct->presentBasePower = 80; - struct DamageContext ctx; + struct DamageContext ctx = {0}; ctx.battlerAtk = battlerAtk; ctx.battlerDef = battlerDef; ctx.move = move; diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c index 1d63466ef8..d3076417f4 100644 --- a/src/battle_controller_player.c +++ b/src/battle_controller_player.c @@ -455,6 +455,12 @@ void HandleInputChooseTarget(u32 battler) PlaySE(SE_SELECT); gSprites[gBattlerSpriteIds[gMultiUsePlayerCursor]].callback = SpriteCB_HideAsMoveTarget; gBattlerControllerFuncs[battler] = HandleInputChooseMove; + if (gBattleStruct->gimmick.playerSelect == 1 && gBattleStruct->gimmick.usableGimmick[battler] == GIMMICK_Z_MOVE) + { + gBattleStruct->gimmick.playerSelect = 0; + gBattleStruct->zmove.viewing = TRUE; + ReloadMoveNames(battler); + } DoBounceEffect(battler, BOUNCE_HEALTHBOX, 7, 1); DoBounceEffect(battler, BOUNCE_MON, 7, 1); EndBounceEffect(gMultiUsePlayerCursor, BOUNCE_HEALTHBOX); @@ -2100,6 +2106,8 @@ void PlayerHandleChooseMove(u32 battler) if (!IsGimmickTriggerSpriteActive()) gBattleStruct->gimmick.triggerSpriteId = 0xFF; + else if (!IsGimmickTriggerSpriteMatchingBattler(battler)) + DestroyGimmickTriggerSprite(); if (!(gBattleStruct->gimmick.usableGimmick[battler] == GIMMICK_Z_MOVE && !gBattleStruct->zmove.viable)) CreateGimmickTriggerSprite(battler); diff --git a/src/battle_end_turn.c b/src/battle_end_turn.c index 50072c2313..2aafa6624d 100644 --- a/src/battle_end_turn.c +++ b/src/battle_end_turn.c @@ -298,13 +298,12 @@ static bool32 HandleEndTurnEmergencyExit(u32 battler) && IsBattlerAlive(battler) && (CanBattlerSwitch(battler) || !(gBattleTypeFlags & BATTLE_TYPE_TRAINER)) && !(gBattleTypeFlags & BATTLE_TYPE_ARENA) - && CountUsablePartyMons(battler) > 0 && gBattleMons[battler].volatiles.semiInvulnerable != STATE_SKY_DROP) // Not currently held by Sky Drop { gBattlerAbility = battler; gLastUsedAbility = ability; - if (gBattleTypeFlags & BATTLE_TYPE_TRAINER || IsOnPlayerSide(battler)) + if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) BattleScriptExecute(BattleScript_EmergencyExitEnd2); else BattleScriptExecute(BattleScript_EmergencyExitWildEnd2); diff --git a/src/battle_gimmick.c b/src/battle_gimmick.c index 76ea80a4f6..38d60d4365 100644 --- a/src/battle_gimmick.c +++ b/src/battle_gimmick.c @@ -178,6 +178,13 @@ bool32 IsGimmickTriggerSpriteActive(void) return FALSE; } +bool32 IsGimmickTriggerSpriteMatchingBattler(u32 battler) +{ + if (battler == gSprites[gBattleStruct->gimmick.triggerSpriteId].tBattler) + return TRUE; + return FALSE; +} + void HideGimmickTriggerSprite(void) { if (gBattleStruct->gimmick.triggerSpriteId != 0xFF) diff --git a/src/battle_main.c b/src/battle_main.c index 685b1a9f15..a57155753d 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3237,6 +3237,9 @@ void SwitchInClearSetData(u32 battler, struct Volatiles *volatilesCopy) gBattleStruct->battlerState[battler].stompingTantrumTimer = 0; gBattleStruct->palaceFlags &= ~(1u << battler); gBattleStruct->battlerState[battler].canPickupItem = FALSE; + gBattleStruct->hazardsCounter = 0; + gDisableStructs[battler].hazardsDone = FALSE; + gSpecialStatuses[battler].switchInItemDone = FALSE; ClearPursuitValuesIfSet(battler); diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 3052b45a53..3ec480f9a2 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1098,7 +1098,6 @@ bool32 EmergencyExitCanBeTriggered(u32 battler) && HadMoreThanHalfHpNowDoesnt(battler) && (CanBattlerSwitch(battler) || !(gBattleTypeFlags & BATTLE_TYPE_TRAINER)) && !(gBattleTypeFlags & BATTLE_TYPE_ARENA) - && CountUsablePartyMons(battler) > 0 && gBattleMons[battler].volatiles.semiInvulnerable != STATE_SKY_DROP) return TRUE; @@ -6696,7 +6695,7 @@ static void Cmd_moveend(void) effect = TRUE; gBattleScripting.battler = battler; - if (gBattleTypeFlags & BATTLE_TYPE_TRAINER || IsOnPlayerSide(battler)) + if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) BattleScriptCall(BattleScript_EmergencyExit); else BattleScriptCall(BattleScript_EmergencyExitWild); @@ -15386,9 +15385,7 @@ void BS_TryAllySwitch(void) { NATIVE_ARGS(const u8 *failInstr); - if (!IsBattlerAlive(BATTLE_PARTNER(gBattlerAttacker)) - || (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER && gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) - || (GetBattlerSide(gBattlerAttacker) == B_SIDE_OPPONENT && gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS)) + if (!IsBattlerAlive(BATTLE_PARTNER(gBattlerAttacker)) || HasPartnerTrainer(gBattlerAttacker)) { gBattlescriptCurrInstr = cmd->failInstr; } @@ -16831,11 +16828,14 @@ void BS_ArenaJudgmentWindow(void) gBattlescriptCurrInstr = cmd->nextInstr; } -static void SetArenMonLostValues(u32 battler) +static void SetArenMonLostValues(u32 battler, u32 side) { gBattleMons[battler].hp = 0; gHitMarker |= HITMARKER_FAINTED(battler); - gBattleStruct->arenaLostOpponentMons |= 1u << gBattlerPartyIndexes[battler]; + if (side == B_SIDE_PLAYER) + gBattleStruct->arenaLostPlayerMons |= 1u << gBattlerPartyIndexes[battler]; + else + gBattleStruct->arenaLostOpponentMons |= 1u << gBattlerPartyIndexes[battler]; gDisableStructs[battler].truantSwitchInHack = TRUE; } @@ -16844,22 +16844,22 @@ static void SetArenMonLostValues(u32 battler) void BS_ArenaOpponentMonLost(void) { NATIVE_ARGS(); - SetArenMonLostValues(opponentMon); + SetArenMonLostValues(opponentMon, B_SIDE_OPPONENT); gBattlescriptCurrInstr = cmd->nextInstr; } void BS_ArenaPlayerMonLost(void) { NATIVE_ARGS(); - SetArenMonLostValues(playerMon); + SetArenMonLostValues(playerMon, B_SIDE_PLAYER); gBattlescriptCurrInstr = cmd->nextInstr; } void BS_ArenaBothMonsLost(void) { NATIVE_ARGS(); - SetArenMonLostValues(playerMon); - SetArenMonLostValues(opponentMon); + SetArenMonLostValues(playerMon, B_SIDE_PLAYER); + SetArenMonLostValues(opponentMon, B_SIDE_OPPONENT); gBattlescriptCurrInstr = cmd->nextInstr; } #undef playerMon diff --git a/src/battle_tower.c b/src/battle_tower.c index 0f31106142..d589cb757a 100644 --- a/src/battle_tower.c +++ b/src/battle_tower.c @@ -1990,7 +1990,7 @@ void DoSpecialTrainerBattle(void) gBattleTypeFlags |= BATTLE_TYPE_DOUBLE | BATTLE_TYPE_INGAME_PARTNER | BATTLE_TYPE_MULTI | BATTLE_TYPE_TWO_OPPONENTS; break; case FRONTIER_MODE_LINK_MULTIS: - gBattleTypeFlags |= BATTLE_TYPE_DOUBLE | BATTLE_TYPE_LINK | BATTLE_TYPE_MULTI | BATTLE_TYPE_TOWER_LINK_MULTI; + gBattleTypeFlags |= BATTLE_TYPE_DOUBLE | BATTLE_TYPE_LINK | BATTLE_TYPE_MULTI | BATTLE_TYPE_TOWER_LINK_MULTI | BATTLE_TYPE_TWO_OPPONENTS; FillFrontierTrainersParties(FRONTIER_MULTI_PARTY_SIZE); break; } diff --git a/src/battle_util.c b/src/battle_util.c index 187ce52955..648f6a9e76 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -10771,6 +10771,15 @@ bool32 BreaksThroughSemiInvulnerablity(u32 battler, u32 move) return FALSE; } +bool32 HasPartnerTrainer(u32 battler) +{ + if ((GetBattlerSide(battler) == B_SIDE_PLAYER && gBattleTypeFlags & BATTLE_TYPE_PLAYER_HAS_PARTNER) + || (GetBattlerSide(battler) == B_SIDE_OPPONENT && gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS)) + return TRUE; + else + return FALSE; +} + static u32 GetMirrorMoveMove(void) { s32 i, validMovesCount; diff --git a/src/data/pokemon/species_info/gen_1_families.h b/src/data/pokemon/species_info/gen_1_families.h index a661147349..f42bf0f445 100644 --- a/src/data/pokemon/species_info/gen_1_families.h +++ b/src/data/pokemon/species_info/gen_1_families.h @@ -12522,7 +12522,7 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = SIZE_32x32, SHADOW_SIZE_M, TRACKS_FOOT, - sAnimTable_Following, + sAnimTable_Following_Asym, gOverworldPalette_Krabby, gShinyOverworldPalette_Krabby ) @@ -12592,7 +12592,7 @@ const struct SpeciesInfo gSpeciesInfoGen1[] = SIZE_32x32, SHADOW_SIZE_M, TRACKS_FOOT, - sAnimTable_Following, + sAnimTable_Following_Asym, gOverworldPalette_Kingler, gShinyOverworldPalette_Kingler ) diff --git a/src/field_specials.c b/src/field_specials.c index 183abbd35c..dde236c911 100644 --- a/src/field_specials.c +++ b/src/field_specials.c @@ -22,12 +22,13 @@ #include "item.h" #include "item_icon.h" #include "link.h" -#include "load_save.h" #include "list_menu.h" +#include "load_save.h" #include "main.h" -#include "mystery_gift.h" #include "match_call.h" #include "menu.h" +#include "metatile_behavior.h" +#include "mystery_gift.h" #include "overworld.h" #include "party_menu.h" #include "pokeblock.h" @@ -985,7 +986,7 @@ void FieldShowRegionMap(void) static bool32 IsBuildingPCTile(u32 tileId) { - return gMapHeader.mapLayout->primaryTileset == &gTileset_Building && (tileId == METATILE_Building_PC_On || tileId == METATILE_Building_PC_Off); + return (MetatileBehavior_IsPC(UNPACK_BEHAVIOR(GetMetatileAttributesById(tileId)))); } static bool32 IsPlayerHousePCTile(u32 tileId) diff --git a/src/pokemon_summary_screen.c b/src/pokemon_summary_screen.c index f5b61f9cc7..5223c1d35c 100644 --- a/src/pokemon_summary_screen.c +++ b/src/pokemon_summary_screen.c @@ -333,6 +333,7 @@ static const u8 *GetLetterGrade(u32 stat); static u8 AddWindowFromTemplateList(const struct WindowTemplate *template, u8 templateId); static u8 IncrementSkillsStatsMode(u8 mode); static void ClearStatLabel(u32 length, u32 statsCoordX, u32 statsCoordY); +u32 GetAdjustedIvData(struct Pokemon *mon, u32 stat); static const struct BgTemplate sBgTemplates[] = { @@ -1172,6 +1173,13 @@ static void DestroyCategoryIcon(void) sMonSummaryScreen->categoryIconSpriteId = 0xFF; } +u32 GetAdjustedIvData(struct Pokemon *mon, u32 stat) +{ + if (P_SUMMARY_SCREEN_IV_HYPERTRAIN && GetMonData(mon, MON_DATA_HYPER_TRAINED_HP + stat)) + return MAX_PER_STAT_IVS; + return GetMonData(mon, MON_DATA_HP_IV + stat); +} + void ShowPokemonSummaryScreen(u8 mode, void *mons, u8 monIndex, u8 maxMonIndex, void (*callback)(void)) { sMonSummaryScreen = AllocZeroed(sizeof(*sMonSummaryScreen)); @@ -1863,12 +1871,12 @@ void ExtractMonSkillStatsData(struct Pokemon *mon, struct PokeSummary *sum) void ExtractMonSkillIvData(struct Pokemon *mon, struct PokeSummary *sum) { - sum->currentHP = GetMonData(mon, MON_DATA_HP_IV); - sum->atk = GetMonData(mon, MON_DATA_ATK_IV); - sum->def = GetMonData(mon, MON_DATA_DEF_IV); - sum->spatk = GetMonData(mon, MON_DATA_SPATK_IV); - sum->spdef = GetMonData(mon, MON_DATA_SPDEF_IV); - sum->speed = GetMonData(mon, MON_DATA_SPEED_IV); + sum->currentHP = GetAdjustedIvData(mon, STAT_HP); + sum->atk = GetAdjustedIvData(mon, STAT_ATK); + sum->def = GetAdjustedIvData(mon, STAT_DEF); + sum->spatk = GetAdjustedIvData(mon, STAT_SPATK); + sum->spdef = GetAdjustedIvData(mon, STAT_SPDEF); + sum->speed = GetAdjustedIvData(mon, STAT_SPEED); } void ExtractMonSkillEvData(struct Pokemon *mon, struct PokeSummary *sum) diff --git a/test/battle/ability/emergency_exit.c b/test/battle/ability/emergency_exit.c index 5027d929ec..1dcd0be21b 100644 --- a/test/battle/ability/emergency_exit.c +++ b/test/battle/ability/emergency_exit.c @@ -108,3 +108,69 @@ SINGLE_BATTLE_TEST("Emergency Exit activates when taking residual damage and fal ABILITY_POPUP(opponent, ABILITY_EMERGENCY_EXIT); } } + +WILD_BATTLE_TEST("Emergency Exit makes the pokemon flee during wild battle") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_GOLISOPOD) { Ability(ABILITY_EMERGENCY_EXIT); MaxHP(263); HP(262); }; + } WHEN { + TURN { MOVE(player, MOVE_SUPER_FANG);} + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SUPER_FANG, player); + HP_BAR(opponent); + ABILITY_POPUP(opponent, ABILITY_EMERGENCY_EXIT); + } THEN { + EXPECT_EQ(gBattleOutcome, B_OUTCOME_MON_TELEPORTED); + } +} + +WILD_BATTLE_TEST("Emergency Exit activates when taking residual damage and falling under 50% max-hp (wild battle)") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_GOLISOPOD) { Ability(ABILITY_EMERGENCY_EXIT); MaxHP(263); HP(134); Status1(STATUS1_BURN); }; + } WHEN { + TURN { } + } SCENE { + HP_BAR(opponent); + ABILITY_POPUP(opponent, ABILITY_EMERGENCY_EXIT); + } THEN { + EXPECT_EQ(gBattleOutcome, B_OUTCOME_MON_TELEPORTED); + } +} + +WILD_BATTLE_TEST("Emergency Exit makes the player ran during wild battle") +{ + GIVEN { + PLAYER(SPECIES_GOLISOPOD) { Ability(ABILITY_EMERGENCY_EXIT); MaxHP(263); HP(262); }; + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_SUPER_FANG);} + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SUPER_FANG, opponent); + HP_BAR(player); + ABILITY_POPUP(player, ABILITY_EMERGENCY_EXIT); + } THEN { + EXPECT_EQ(gBattleOutcome, B_OUTCOME_PLAYER_TELEPORTED); + } +} + +WILD_BATTLE_TEST("Emergency Exit activates when taking residual damage and falling under 50% max-hp (wild battle player side)") +{ + GIVEN { + PLAYER(SPECIES_GOLISOPOD) { Ability(ABILITY_EMERGENCY_EXIT); MaxHP(263); HP(134); }; + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_SANDSTORM);} + } SCENE { + HP_BAR(player); + ABILITY_POPUP(player, ABILITY_EMERGENCY_EXIT); + } THEN { + EXPECT_EQ(gBattleOutcome, B_OUTCOME_PLAYER_TELEPORTED); + } +} diff --git a/test/battle/ability/liquid_ooze.c b/test/battle/ability/liquid_ooze.c index 3af8cc102f..a9e5fb4b90 100644 --- a/test/battle/ability/liquid_ooze.c +++ b/test/battle/ability/liquid_ooze.c @@ -187,3 +187,34 @@ SINGLE_BATTLE_TEST("Liquid Ooze does not cause Dream Eater users to lose HP inst EXPECT_LT(damage, 0); // Negative damage = Heal } } + +SINGLE_BATTLE_TEST("Liquid Ooze HP loss from Absorb is blocked by Magic Guard") +{ + GIVEN { + PLAYER(SPECIES_CLEFFA) { Ability(ABILITY_MAGIC_GUARD); } + OPPONENT(SPECIES_TENTACOOL) { Ability(ABILITY_LIQUID_OOZE); } + } WHEN { + TURN { MOVE(player, MOVE_ABSORB); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ABSORB, player); + HP_BAR(opponent); + NONE_OF { + HP_BAR(player); + MESSAGE("Wobbuffet sucked up the liquid ooze!"); + } + } +} + +SINGLE_BATTLE_TEST("Liquid Ooze HP loss from Leech Seed is blocked by Magic Guard") +{ + GIVEN { + PLAYER(SPECIES_CLEFFA) { Ability(ABILITY_MAGIC_GUARD); } + OPPONENT(SPECIES_TENTACOOL) { Ability(ABILITY_LIQUID_OOZE); } + } WHEN { + TURN { MOVE(player, MOVE_LEECH_SEED); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_LEECH_SEED, player); + HP_BAR(opponent); + NOT HP_BAR(player); + } +} diff --git a/test/battle/hazards.c b/test/battle/hazards.c index 9c974bb7b1..17ecb41f5c 100644 --- a/test/battle/hazards.c +++ b/test/battle/hazards.c @@ -38,3 +38,26 @@ SINGLE_BATTLE_TEST("Hazards are applied based on order of set up") EXPECT_EQ(gBattleStruct->hazardsQueue[0][5], HAZARDS_NONE); } } + +SINGLE_BATTLE_TEST("Hazards are applied correctly after a battler faints") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_FINAL_GAMBIT) == EFFECT_FINAL_GAMBIT); + PLAYER(SPECIES_WYNAUT); + PLAYER(SPECIES_WOBBUFFET) { HP(1); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_STEALTH_ROCK); + MOVE(player, MOVE_FINAL_GAMBIT); + SEND_OUT(player, 1); + SEND_OUT(player, 2); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_STEALTH_ROCK, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FINAL_GAMBIT, player); + MESSAGE("Wynaut fainted!"); + MESSAGE("Pointed stones dug into Wobbuffet!"); + MESSAGE("Wobbuffet fainted!"); + MESSAGE("Pointed stones dug into Wynaut!"); + } +}