diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 244976ea1e..874ebdd590 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -26,8 +26,18 @@ jobs: run: | cd docs mdbook build + - name: Check if Pages is enabled + uses: octokit/request-action@v2.x + id: check_pages + continue-on-error: true + with: + route: GET /repos/{repo}/pages + repo: ${{ github.repository }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Setup Pages uses: actions/configure-pages@v4 + if: steps.check_pages.outcome == 'success' - name: Upload artifact uses: actions/upload-pages-artifact@v3 with: @@ -35,3 +45,4 @@ jobs: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v4 + if: steps.check_pages.outcome == 'success' diff --git a/INSTALL.md b/INSTALL.md index fe8ea2e391..ed7d5bad08 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -475,6 +475,13 @@ devkitARM is now installed. Then proceed to [Choosing where to store pokeemerald Expansion (Linux)](#choosing-where-to-store-pokeemerald-expansion-linux). +### NixOS +Run the following command to start an interactive shell with the necessary packages: +```bash +nix-shell -p pkgsCross.arm-embedded.stdenv.cc git pkg-config libpng +``` +Then proceed to [Choosing where to store pokeemerald Expansion (Linux)](#choosing-where-to-store-pokeemerald-expansion-linux). + ### Other distributions _(Specific instructions for other distributions would be greatly appreciated!)_ diff --git a/Makefile b/Makefile index 74e6449236..283df7ac0f 100644 --- a/Makefile +++ b/Makefile @@ -80,7 +80,7 @@ MODERN_ELF_NAME := $(MODERN_ROM_NAME:.gba=.elf) MODERN_MAP_NAME := $(MODERN_ROM_NAME:.gba=.map) MODERN_OBJ_DIR_NAME := build/modern -SHELL := /bin/bash -o pipefail +SHELL := bash -o pipefail ELF = $(ROM:.gba=.elf) MAP = $(ROM:.gba=.map) diff --git a/asmdiff.sh b/asmdiff.sh index f5a7010747..aca670e324 100755 --- a/asmdiff.sh +++ b/asmdiff.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash if [[ -d "$DEVKITARM/bin/" ]]; then OBJDUMP_BIN="$DEVKITARM/bin/arm-none-eabi-objdump" diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 930d2103a0..21536f3de4 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -5506,7 +5506,7 @@ BattleScript_GiveExp:: BattleScript_HandleFaintedMon:: setbyte sSHIFT_SWITCHED, 0 - checkteamslost BattleScript_LinkHandleFaintedMonMultiple + checkteamslost BattleScript_HandleFaintedMonMultiple jumpifbyte CMP_NOT_EQUAL, gBattleOutcome, 0, BattleScript_FaintedMonEnd jumpifbattletype BATTLE_TYPE_TRAINER | BATTLE_TYPE_DOUBLE, BattleScript_FaintedMonTryChoose jumpifword CMP_NO_COMMON_BITS, gHitMarker, HITMARKER_PLAYER_FAINTED, BattleScript_FaintedMonTryChoose @@ -5587,13 +5587,13 @@ BattleScript_FaintedMonShiftSwitched: copybyte gBattlerTarget, sSAVED_BATTLER goto BattleScript_FaintedMonSendOutNewEnd -BattleScript_LinkHandleFaintedMonMultiple:: - openpartyscreen BS_FAINTED_LINK_MULTIPLE_1, BattleScript_LinkHandleFaintedMonMultipleStart -BattleScript_LinkHandleFaintedMonMultipleStart:: +BattleScript_HandleFaintedMonMultiple:: + openpartyscreen BS_FAINTED_MULTIPLE_1, BattleScript_HandleFaintedMonMultipleStart +BattleScript_HandleFaintedMonMultipleStart:: switchhandleorder BS_FAINTED, 0 - openpartyscreen BS_FAINTED_LINK_MULTIPLE_2, BattleScript_LinkHandleFaintedMonMultipleEnd + openpartyscreen BS_FAINTED_MULTIPLE_2, BattleScript_HandleFaintedMonMultipleEnd switchhandleorder BS_FAINTED, 0 -BattleScript_LinkHandleFaintedMonLoop:: +BattleScript_HandleFaintedMonLoop:: switchhandleorder BS_FAINTED, 3 drawpartystatussummary BS_FAINTED getswitchedmondata BS_FAINTED @@ -5605,9 +5605,10 @@ BattleScript_LinkHandleFaintedMonLoop:: hidepartystatussummary BS_FAINTED switchinanim BS_FAINTED, FALSE waitstate - switchineffects BS_FAINTED_LINK_MULTIPLE_1 - jumpifbytenotequal gBattlerFainted, gBattlersCount, BattleScript_LinkHandleFaintedMonLoop -BattleScript_LinkHandleFaintedMonMultipleEnd:: + switchineffects BS_FAINTED_MULTIPLE_1 + jumpifbytenotequal gBattlerFainted, gBattlersCount, BattleScript_HandleFaintedMonLoop +BattleScript_HandleFaintedMonMultipleEnd:: + switchineffects BS_FAINTED_MULTIPLE_2 end2 BattleScript_LocalTrainerBattleWon:: @@ -6046,6 +6047,10 @@ BattleScript_MagicRoomEnds:: waitmessage B_WAIT_TIME_LONG end2 +BattleScript_GrassyTerrainEnds:: + call BattleScript_GrassyTerrainHeals_Ret + goto BattleScript_TerrainEnds + BattleScript_TerrainEnds_Ret:: printfromtable gTerrainStringIds waitmessage B_WAIT_TIME_LONG @@ -8357,6 +8362,10 @@ BattleScript_MoveUsedPsychicTerrainPrevents:: goto BattleScript_MoveEnd BattleScript_GrassyTerrainHeals:: + call BattleScript_GrassyTerrainHeals_Ret + end2 + +BattleScript_GrassyTerrainHeals_Ret:: setbyte gBattleCommunication, 0 BattleScript_GrassyTerrainLoop: copyarraywithindex gBattlerAttacker, gBattlerByTurnOrder, gBattleCommunication, 1 @@ -8374,7 +8383,7 @@ BattleScript_GrassyTerrainLoopEnd:: bicword gHitMarker, HITMARKER_IGNORE_BIDE | HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_PASSIVE_DAMAGE jumpifword CMP_COMMON_BITS, gFieldStatuses, STATUS_FIELD_TERRAIN_PERMANENT, BattleScript_GrassyTerrainHealEnd BattleScript_GrassyTerrainHealEnd: - end2 + return BattleScript_AbilityNoSpecificStatLoss:: pause B_WAIT_TIME_SHORT @@ -8715,7 +8724,7 @@ BattleScript_SynchronizeActivates:: return BattleScript_NoItemSteal:: - pause B_WAIT_TIME_SHORT + call BattleScript_AbilityPopUpTarget printstring STRINGID_PKMNSXMADEYINEFFECTIVE waitmessage B_WAIT_TIME_LONG return diff --git a/graphics/pokemon/bounsweet/front.png b/graphics/pokemon/bounsweet/front.png index a31220dcf9..9e09d6d728 100644 Binary files a/graphics/pokemon/bounsweet/front.png and b/graphics/pokemon/bounsweet/front.png differ diff --git a/graphics/pokemon/bounsweet/normal.pal b/graphics/pokemon/bounsweet/normal.pal index 6ba3b9fb34..029ef53c85 100644 --- a/graphics/pokemon/bounsweet/normal.pal +++ b/graphics/pokemon/bounsweet/normal.pal @@ -15,5 +15,5 @@ JASC-PAL 248 136 136 88 104 96 184 192 192 -0 0 0 +248 248 248 0 0 0 diff --git a/graphics/pokemon/bounsweet/shiny.pal b/graphics/pokemon/bounsweet/shiny.pal index 14fc1f5ab3..704d72a19a 100644 --- a/graphics/pokemon/bounsweet/shiny.pal +++ b/graphics/pokemon/bounsweet/shiny.pal @@ -14,6 +14,6 @@ JASC-PAL 248 224 40 248 136 136 88 104 96 -184 192 192 -0 0 0 +200 192 128 +247 240 184 0 0 0 diff --git a/graphics/pokemon/bruxish/back.png b/graphics/pokemon/bruxish/back.png index 32aeed0b52..7969fb7197 100644 Binary files a/graphics/pokemon/bruxish/back.png and b/graphics/pokemon/bruxish/back.png differ diff --git a/graphics/pokemon/bruxish/front.png b/graphics/pokemon/bruxish/front.png index 4447621e55..ce29eb8d5f 100644 Binary files a/graphics/pokemon/bruxish/front.png and b/graphics/pokemon/bruxish/front.png differ diff --git a/graphics/pokemon/bruxish/shiny.pal b/graphics/pokemon/bruxish/shiny.pal index a609040e84..5deb432b19 100644 --- a/graphics/pokemon/bruxish/shiny.pal +++ b/graphics/pokemon/bruxish/shiny.pal @@ -10,8 +10,8 @@ JASC-PAL 120 24 24 232 56 40 136 120 104 -224 216 208 -192 176 160 +248 248 248 +200 192 176 200 160 80 232 208 136 248 248 248 diff --git a/graphics/pokemon/charjabug/anim_front.png b/graphics/pokemon/charjabug/anim_front.png index 163ab2ba2d..58d39c97d2 100644 Binary files a/graphics/pokemon/charjabug/anim_front.png and b/graphics/pokemon/charjabug/anim_front.png differ diff --git a/graphics/pokemon/charjabug/shiny.pal b/graphics/pokemon/charjabug/shiny.pal index 58974fb24f..a5ffc233bc 100644 --- a/graphics/pokemon/charjabug/shiny.pal +++ b/graphics/pokemon/charjabug/shiny.pal @@ -15,5 +15,5 @@ JASC-PAL 248 160 72 112 72 24 176 112 48 -0 0 0 -0 0 0 +8 64 88 +176 168 176 diff --git a/graphics/pokemon/lurantis/back.png b/graphics/pokemon/lurantis/back.png index a080a77f09..d133d63f7a 100644 Binary files a/graphics/pokemon/lurantis/back.png and b/graphics/pokemon/lurantis/back.png differ diff --git a/include/battle.h b/include/battle.h index 9f76fbe498..0e238e61b9 100644 --- a/include/battle.h +++ b/include/battle.h @@ -666,7 +666,11 @@ struct BattleStruct u16 abilityPreventingSwitchout; u8 hpScale; u16 synchronizeMoveEffect; - bool8 anyMonHasTransformed; + u8 anyMonHasTransformed:1; // Only used in battle_tv.c + u8 multipleSwitchInBattlers:4; // One bit per battler + u8 multipleSwitchInState:2; + u8 multipleSwitchInCursor:3; + u8 multipleSwitchInSortedBattlers[MAX_BATTLERS_COUNT]; void (*savedCallback)(void); u16 usedHeldItems[PARTY_SIZE][NUM_BATTLE_SIDES]; // For each party member and side. For harvest, recycle u16 chosenItem[MAX_BATTLERS_COUNT]; @@ -740,7 +744,7 @@ struct BattleStruct u16 moveEffect2; // For Knock Off 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) + struct LostItem itemLost[NUM_BATTLE_SIDES][PARTY_SIZE]; // Pokemon that had items consumed or stolen (two bytes per party member per side) 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 diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index ed30d2ad4e..f344eb986c 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -146,10 +146,8 @@ bool32 HasMagicCoatAffectedMove(u32 battler); bool32 HasSnatchAffectedMove(u32 battler); // status checks -bool32 AI_CanBeBurned(u32 battler, u32 ability); bool32 AI_CanGetFrostbite(u32 battler, u32 ability); bool32 AI_CanBeConfused(u32 battlerAtk, u32 battlerDef, u32 move, u32 ability); -bool32 AI_CanSleep(u32 battler, u32 ability); bool32 IsBattlerIncapacitated(u32 battler, u32 ability); bool32 AI_CanPutToSleep(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove); bool32 ShouldPoisonSelf(u32 battler, u32 ability); diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 4e5031f28e..8643f62ad9 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -269,6 +269,7 @@ extern const u8 BattleScript_WonderRoomEnds[]; extern const u8 BattleScript_MagicRoomEnds[]; extern const u8 BattleScript_TerrainEnds[]; extern const u8 BattleScript_TerrainEnds_Ret[]; +extern const u8 BattleScript_GrassyTerrainEnds[]; extern const u8 BattleScript_MudSportEnds[]; extern const u8 BattleScript_WaterSportEnds[]; extern const u8 BattleScript_SturdiedMsg[]; diff --git a/include/battle_util.h b/include/battle_util.h index faa1bbd91d..30201c167d 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -202,7 +202,7 @@ bool32 IsBattlerMegaEvolved(u32 battler); bool32 IsBattlerPrimalReverted(u32 battler); bool32 IsBattlerUltraBursted(u32 battler); u16 GetBattleFormChangeTargetSpecies(u32 battler, u16 method); -bool32 TryBattleFormChange(u32 battler, u16 method); +bool32 TryBattleFormChange(u32 battler, u32 method); bool32 DoBattlersShareType(u32 battler1, u32 battler2); bool32 CanBattlerGetOrLoseItem(u32 battler, u16 itemId); u32 GetIllusionMonSpecies(u32 battler); @@ -249,10 +249,10 @@ bool32 MoveHasChargeTurnAdditionalEffect(u32 move); bool32 CanTargetPartner(u32 battlerAtk, u32 battlerDef); bool32 TargetFullyImmuneToCurrMove(u32 BattlerAtk, u32 battlerDef); -bool32 CanSleep(u32 battler); -bool32 CanBePoisoned(u32 battlerAttacker, u32 battlerTarget); -bool32 CanBeBurned(u32 battler); -bool32 CanBeParalyzed(u32 battler); +bool32 CanBeSlept(u32 battler, u32 ability); +bool32 CanBePoisoned(u32 battlerAtk, u32 battlerDef, u32 defAbility); +bool32 CanBeBurned(u32 battler, u32 ability); +bool32 CanBeParalyzed(u32 battler, u32 ability); bool32 CanBeFrozen(u32 battler); bool32 CanGetFrostbite(u32 battler); bool32 CanBeConfused(u32 battler); diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h index 18d1108431..03173c43cf 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -63,8 +63,8 @@ #define BS_EFFECT_BATTLER 2 #define BS_FAINTED 3 #define BS_ATTACKER_WITH_PARTNER 4 // for Cmd_updatestatusicon -#define BS_FAINTED_LINK_MULTIPLE_1 5 // for openpartyscreen -#define BS_FAINTED_LINK_MULTIPLE_2 6 // for openpartyscreen +#define BS_FAINTED_MULTIPLE_1 5 // for openpartyscreen +#define BS_FAINTED_MULTIPLE_2 6 // for openpartyscreen #define BS_BATTLER_0 7 #define BS_ATTACKER_SIDE 8 // for Cmd_jumpifability #define BS_TARGET_SIDE 9 // for Cmd_jumpifability diff --git a/include/constants/species.h b/include/constants/species.h index 9009119332..bdf3bdce1f 100644 --- a/include/constants/species.h +++ b/include/constants/species.h @@ -1629,7 +1629,7 @@ #define SPECIES_URSHIFU_RAPID_STRIKE_STYLE_GIGANTAMAX 1522 #define SPECIES_MIMIKYU_TOTEM_BUSTED 1523 -#define SPECIES_EGG SPECIES_MIMIKYU_TOTEM_BUSTED + 1 +#define SPECIES_EGG (SPECIES_MIMIKYU_TOTEM_BUSTED + 1) #define NUM_SPECIES SPECIES_EGG diff --git a/include/random.h b/include/random.h index 3aeb1879d8..cb4e636e14 100644 --- a/include/random.h +++ b/include/random.h @@ -161,6 +161,7 @@ enum RandomTag RNG_ACCURACY, RNG_CONFUSION, RNG_CRITICAL_HIT, + RNG_CURSED_BODY, RNG_CUTE_CHARM, RNG_DAMAGE_MODIFIER, RNG_DIRE_CLAW, @@ -179,6 +180,7 @@ enum RandomTag RNG_METRONOME, RNG_PARALYSIS, RNG_POISON_POINT, + RNG_POISON_TOUCH, RNG_RAMPAGE_TURNS, RNG_SECONDARY_EFFECT, RNG_SECONDARY_EFFECT_2, diff --git a/include/test/battle.h b/include/test/battle.h index 6d105a3d58..ecc7a8fe7c 100644 --- a/include/test/battle.h +++ b/include/test/battle.h @@ -507,7 +507,7 @@ // or loop. #define BATTLE_TEST_STACK_SIZE 1024 #define MAX_TURNS 16 -#define MAX_QUEUED_EVENTS 25 +#define MAX_QUEUED_EVENTS 30 #define MAX_EXPECTED_ACTIONS 10 enum { BATTLE_TEST_SINGLES, BATTLE_TEST_DOUBLES, BATTLE_TEST_WILD, BATTLE_TEST_AI_SINGLES, BATTLE_TEST_AI_DOUBLES }; diff --git a/include/vs_seeker.h b/include/vs_seeker.h index 723e73bf37..d6795432b0 100644 --- a/include/vs_seeker.h +++ b/include/vs_seeker.h @@ -8,6 +8,7 @@ bool8 UpdateVsSeekerStepCounter(void); void MapResetTrainerRematches(u16 mapGroup, u16 mapNum); void ClearRematchMovementByTrainerId(void); u16 GetRematchTrainerIdVSSeeker(u16 trainerId); +bool32 IsVsSeekerEnabled(void); #define VSSEEKER_RECHARGE_STEPS 100 diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 52278b0b73..becd609477 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -1876,7 +1876,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) ADJUST_SCORE(-10); break; case EFFECT_REST: - if (!AI_CanSleep(battlerAtk, aiData->abilities[battlerAtk])) + if (!CanBeSlept(battlerAtk, aiData->abilities[battlerAtk])) ADJUST_SCORE(-10); //fallthrough case EFFECT_RESTORE_HP: @@ -3498,7 +3498,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) } break; case EFFECT_REST: - if (!(AI_CanSleep(battlerAtk, aiData->abilities[battlerAtk]))) + if (!(CanBeSlept(battlerAtk, aiData->abilities[battlerAtk]))) { break; } @@ -3978,7 +3978,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move) ADJUST_SCORE(DECENT_EFFECT); break; case HOLD_EFFECT_FLAME_ORB: - if (!ShouldBurnSelf(battlerAtk, aiData->abilities[battlerAtk]) && AI_CanBeBurned(battlerAtk, aiData->abilities[battlerDef])) + if (!ShouldBurnSelf(battlerAtk, aiData->abilities[battlerAtk]) && CanBeBurned(battlerAtk, aiData->abilities[battlerDef])) ADJUST_SCORE(DECENT_EFFECT); break; case HOLD_EFFECT_BLACK_SLUDGE: diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index 48a77a988a..6471c25ce6 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -485,7 +485,7 @@ static bool32 ShouldSwitchIfGameStatePrompt(u32 battler, bool32 emitResult) { //Yawn if (gStatuses3[battler] & STATUS3_YAWN - && AI_CanSleep(battler, monAbility) + && CanBeSlept(battler, monAbility) && gBattleMons[battler].hp > gBattleMons[battler].maxHP / 3) { switchMon = TRUE; @@ -1362,8 +1362,6 @@ static u32 GetSwitchinHazardsDamage(u32 battler, struct BattlePokemon *battleMon hazardDamage += spikesDamage; } - // Toxic Spikes - // TODO: CanBePoisoned compatibility to avoid duplicate code if ((hazardFlags & SIDE_STATUS_TOXIC_SPIKES) && (defType1 != TYPE_POISON && defType2 != TYPE_POISON && defType1 != TYPE_STEEL && defType2 != TYPE_STEEL && ability != ABILITY_IMMUNITY && ability != ABILITY_POISON_HEAL && ability != ABILITY_COMATOSE diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 83d2995b72..fe73ab3bce 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -2731,48 +2731,18 @@ bool32 IsBattlerIncapacitated(u32 battler, u32 ability) return FALSE; } -bool32 AI_CanSleep(u32 battler, u32 ability) -{ - if (ability == ABILITY_INSOMNIA - || ability == ABILITY_VITAL_SPIRIT - || ability == ABILITY_COMATOSE - || gBattleMons[battler].status1 & STATUS1_ANY - || gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD - || (gFieldStatuses & (STATUS_FIELD_MISTY_TERRAIN | STATUS_FIELD_ELECTRIC_TERRAIN)) - || IsAbilityStatusProtected(battler)) - return FALSE; - return TRUE; -} - bool32 AI_CanPutToSleep(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove) { - if (!AI_CanSleep(battlerDef, defAbility) + if (!CanBeSlept(battlerDef, defAbility) || DoesSubstituteBlockMove(battlerAtk, battlerDef, move) || PartnerMoveEffectIsStatusSameTarget(BATTLE_PARTNER(battlerAtk), battlerDef, partnerMove)) // shouldn't try to sleep mon that partner is trying to make sleep return FALSE; return TRUE; } -static bool32 AI_CanBePoisoned(u32 battlerAtk, u32 battlerDef, u32 move) -{ - u32 ability = AI_DATA->abilities[battlerDef]; - - if (!(CanPoisonType(battlerAtk, battlerDef)) - || gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_SAFEGUARD - || gBattleMons[battlerDef].status1 & STATUS1_ANY - || ability == ABILITY_IMMUNITY - || ability == ABILITY_COMATOSE - || AI_IsAbilityOnSide(battlerDef, ABILITY_PASTEL_VEIL) - || gBattleMons[battlerDef].status1 & STATUS1_ANY - || IsAbilityStatusProtected(battlerDef) - || AI_IsTerrainAffected(battlerDef, STATUS_FIELD_MISTY_TERRAIN)) - return FALSE; - return TRUE; -} - bool32 ShouldPoisonSelf(u32 battler, u32 ability) { - if (AI_CanBePoisoned(battler, battler, 0) && ( + if (CanBePoisoned(battler, battler, GetBattlerAbility(battler)) && ( ability == ABILITY_MARVEL_SCALE || ability == ABILITY_POISON_HEAL || ability == ABILITY_QUICK_FEET @@ -2787,7 +2757,7 @@ bool32 ShouldPoisonSelf(u32 battler, u32 ability) bool32 AI_CanPoison(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove) { - if (!AI_CanBePoisoned(battlerAtk, battlerDef, move) + if (!CanBePoisoned(battlerAtk, battlerDef, GetBattlerAbility(battlerDef)) || AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] == AI_EFFECTIVENESS_x0 || DoesSubstituteBlockMove(battlerAtk, battlerDef, move) || PartnerMoveEffectIsStatusSameTarget(BATTLE_PARTNER(battlerAtk), battlerDef, partnerMove)) @@ -2800,20 +2770,9 @@ bool32 AI_CanPoison(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u3 return TRUE; } -static bool32 AI_CanBeParalyzed(u32 battler, u32 ability) -{ - if (ability == ABILITY_LIMBER - || ability == ABILITY_COMATOSE - || IS_BATTLER_OF_TYPE(battler, TYPE_ELECTRIC) - || gBattleMons[battler].status1 & STATUS1_ANY - || IsAbilityStatusProtected(battler)) - return FALSE; - return TRUE; -} - bool32 AI_CanParalyze(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove) { - if (!AI_CanBeParalyzed(battlerDef, defAbility) + if (!CanBeParalyzed(battlerDef, defAbility) || AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] == AI_EFFECTIVENESS_x0 || gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_SAFEGUARD || DoesSubstituteBlockMove(battlerAtk, battlerDef, move) @@ -2847,19 +2806,6 @@ bool32 AI_CanConfuse(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 battler return TRUE; } -bool32 AI_CanBeBurned(u32 battler, u32 ability) -{ - if (ability == ABILITY_WATER_VEIL - || ability == ABILITY_WATER_BUBBLE - || ability == ABILITY_COMATOSE - || IS_BATTLER_OF_TYPE(battler, TYPE_FIRE) - || gBattleMons[battler].status1 & STATUS1_ANY - || IsAbilityStatusProtected(battler) - || gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD) - return FALSE; - return TRUE; -} - bool32 AI_CanGetFrostbite(u32 battler, u32 ability) { if (ability == ABILITY_MAGMA_ARMOR @@ -2874,7 +2820,7 @@ bool32 AI_CanGetFrostbite(u32 battler, u32 ability) bool32 ShouldBurnSelf(u32 battler, u32 ability) { - if (AI_CanBeBurned(battler, ability) && ( + if (CanBeBurned(battler, ability) && ( ability == ABILITY_QUICK_FEET || ability == ABILITY_HEATPROOF || ability == ABILITY_MAGIC_GUARD @@ -2888,7 +2834,7 @@ bool32 ShouldBurnSelf(u32 battler, u32 ability) bool32 AI_CanBurn(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 battlerAtkPartner, u32 move, u32 partnerMove) { - if (!AI_CanBeBurned(battlerDef, defAbility) + if (!CanBeBurned(battlerDef, defAbility) || AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] == AI_EFFECTIVENESS_x0 || DoesSubstituteBlockMove(battlerAtk, battlerDef, move) || PartnerMoveEffectIsStatusSameTarget(battlerAtkPartner, battlerDef, partnerMove)) diff --git a/src/battle_controller_recorded_player.c b/src/battle_controller_recorded_player.c index 53860990c4..3a9ca1ec00 100644 --- a/src/battle_controller_recorded_player.c +++ b/src/battle_controller_recorded_player.c @@ -517,7 +517,7 @@ static void RecordedPlayerHandleIntroTrainerBallThrow(u32 battler) else trainerPicId = gSaveBlock2Ptr->playerGender + TRAINER_BACK_PIC_BRENDAN; - trainerPal = gTrainerSprites[trainerPicId].palette.data; + trainerPal = gTrainerBacksprites[trainerPicId].palette.data; BtlController_HandleIntroTrainerBallThrow(battler, 0xD6F9, trainerPal, 24, Intro_TryShinyAnimShowHealthbox); } diff --git a/src/battle_dynamax.c b/src/battle_dynamax.c index 96a1aa5128..9e00486bc4 100644 --- a/src/battle_dynamax.c +++ b/src/battle_dynamax.c @@ -759,7 +759,7 @@ void BS_SetMaxMoveEffect(void) { static const u8 sSnoozeEffects[] = {TRUE, FALSE}; if (!(gStatuses3[gBattlerTarget] & STATUS3_YAWN) - && CanSleep(gBattlerTarget) + && CanBeSlept(gBattlerTarget, GetBattlerAbility(gBattlerTarget)) && RandomElement(RNG_G_MAX_SNOOZE, sSnoozeEffects)) // 50% chance of success { gStatuses3[gBattlerTarget] |= STATUS3_YAWN_TURN(2); @@ -865,7 +865,7 @@ void BS_TrySetStatus1(void) switch (status1) { case STATUS1_POISON: - if (CanBePoisoned(gBattlerAttacker, gBattlerTarget)) + if (CanBePoisoned(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerTarget))) { gBattleMons[gBattlerTarget].status1 |= STATUS1_POISON; gBattleCommunication[MULTISTRING_CHOOSER] = 0; @@ -873,7 +873,7 @@ void BS_TrySetStatus1(void) } break; case STATUS1_PARALYSIS: - if (CanBeParalyzed(gBattlerTarget)) + if (CanBeParalyzed(gBattlerTarget, GetBattlerAbility(gBattlerTarget))) { gBattleMons[gBattlerTarget].status1 |= STATUS1_PARALYSIS; gBattleCommunication[MULTISTRING_CHOOSER] = 3; @@ -881,7 +881,7 @@ void BS_TrySetStatus1(void) } break; case STATUS1_SLEEP: - if (CanSleep(gBattlerTarget)) + if (CanBeSlept(gBattlerTarget, GetBattlerAbility(gBattlerTarget))) { if (B_SLEEP_TURNS >= GEN_5) gBattleMons[gBattlerTarget].status1 |= STATUS1_SLEEP_TURN((Random() % 3) + 2); diff --git a/src/battle_main.c b/src/battle_main.c index 97123470f0..aeea09cd78 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3109,7 +3109,8 @@ static void BattleStartClearSetData(void) { gBattleStruct->usedHeldItems[i][B_SIDE_PLAYER] = 0; gBattleStruct->usedHeldItems[i][B_SIDE_OPPONENT] = 0; - gBattleStruct->itemLost[i].originalItem = GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM); + gBattleStruct->itemLost[B_SIDE_PLAYER][i].originalItem = GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM); + gBattleStruct->itemLost[B_SIDE_OPPONENT][i].originalItem = GetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM); gPartyCriticalHits[i] = 0; gBattleStruct->allowedToChangeFormInWeather[i][B_SIDE_PLAYER] = FALSE; gBattleStruct->allowedToChangeFormInWeather[i][B_SIDE_OPPONENT] = FALSE; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 4c5cea2504..63c6fdcf5c 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -2860,7 +2860,7 @@ void SetMoveEffect(bool32 primary, bool32 certain) if (i != gBattlersCount) break; - if (!CanSleep(gEffectBattler)) + if (!CanBeSlept(gEffectBattler, GetBattlerAbility(gEffectBattler))) break; cancelMultiTurnMovesResult = CancelMultiTurnMoves(gEffectBattler); @@ -2899,7 +2899,7 @@ void SetMoveEffect(bool32 primary, bool32 certain) gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STATUS_HAD_NO_EFFECT; RESET_RETURN } - if (!CanBePoisoned(gBattleScripting.battler, gEffectBattler)) + if (!CanBePoisoned(gBattleScripting.battler, gEffectBattler, GetBattlerAbility(gEffectBattler))) break; statusChanged = TRUE; @@ -2943,7 +2943,7 @@ void SetMoveEffect(bool32 primary, bool32 certain) break; } - if (!CanBeBurned(gEffectBattler)) + if (!CanBeBurned(gEffectBattler, GetBattlerAbility(gEffectBattler))) break; statusChanged = TRUE; @@ -3008,7 +3008,7 @@ void SetMoveEffect(bool32 primary, bool32 certain) } if (!CanParalyzeType(gBattleScripting.battler, gEffectBattler)) break; - if (!CanBeParalyzed(gEffectBattler)) + if (!CanBeParalyzed(gEffectBattler, GetBattlerAbility(gEffectBattler))) break; statusChanged = TRUE; @@ -3046,7 +3046,7 @@ void SetMoveEffect(bool32 primary, bool32 certain) } if (gBattleMons[gEffectBattler].status1) break; - if (CanBePoisoned(gBattleScripting.battler, gEffectBattler)) + if (CanBePoisoned(gBattleScripting.battler, gEffectBattler, GetBattlerAbility(gEffectBattler))) { // It's redundant, because at this point we know the status1 value is 0. gBattleMons[gEffectBattler].status1 &= ~STATUS1_TOXIC_POISON; @@ -3410,7 +3410,7 @@ void SetMoveEffect(bool32 primary, bool32 certain) } else if (GetBattlerAbility(gBattlerTarget) == ABILITY_STICKY_HOLD) { - BattleScriptPushCursor(); + BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_NoItemSteal; gLastUsedAbility = gBattleMons[gBattlerTarget].ability; @@ -3533,6 +3533,7 @@ void SetMoveEffect(bool32 primary, bool32 certain) case MOVE_EFFECT_SPECTRAL_THIEF: if (!NoAliveMonsForEitherParty()) { + bool32 contrary = (GetBattlerAbility(gBattlerAttacker) == ABILITY_CONTRARY); gBattleStruct->stolenStats[0] = 0; // Stats to steal. gBattleScripting.animArg1 = 0; for (i = STAT_ATK; i < NUM_BATTLE_STATS; i++) @@ -3554,16 +3555,16 @@ void SetMoveEffect(bool32 primary, bool32 certain) if (gBattleScripting.animArg1 == 0) { if (byTwo) - gBattleScripting.animArg1 = STAT_ANIM_PLUS2 + i; + gBattleScripting.animArg1 = (contrary ? STAT_ANIM_MINUS2 : STAT_ANIM_PLUS2) + i; else - gBattleScripting.animArg1 = STAT_ANIM_PLUS1 + i; + gBattleScripting.animArg1 = (contrary ? STAT_ANIM_MINUS1 : STAT_ANIM_PLUS1) + i; } else { if (byTwo) - gBattleScripting.animArg1 = STAT_ANIM_MULTIPLE_PLUS2; + gBattleScripting.animArg1 = (contrary ? STAT_ANIM_MULTIPLE_MINUS2 : STAT_ANIM_MULTIPLE_PLUS2); else - gBattleScripting.animArg1 = STAT_ANIM_MULTIPLE_PLUS1; + gBattleScripting.animArg1 = (contrary ? STAT_ANIM_MULTIPLE_MINUS1 : STAT_ANIM_MULTIPLE_PLUS1); } } } @@ -4590,7 +4591,7 @@ bool32 NoAliveMonsForEitherParty(void) return (NoAliveMonsForPlayer() || NoAliveMonsForOpponent()); } -// For battles that aren't BATTLE_TYPE_LINK or BATTLE_TYPE_RECORDED_LINK, the only thing this +// For battles that aren't BATTLE_TYPE_LINK or BATTLE_TYPE_RECORDED_LINK or trainer battles, the only thing this // command does is check whether the player has won/lost by totaling each team's HP. It then // sets gBattleOutcome accordingly, if necessary. static void Cmd_checkteamslost(void) @@ -4606,10 +4607,12 @@ static void Cmd_checkteamslost(void) if (NoAliveMonsForOpponent()) gBattleOutcome |= B_OUTCOME_WON; - // For link battles that haven't ended, count number of empty battler spots - // In link multi battles, jump to pointer if more than 1 spot empty + // Fair switching - everyone has to switch in most at the same time, without knowing which pokemon the other trainer selected. + // In vanilla Emerald this was only used for link battles, in expansion it's also used for regular trainer battles. + // For battles that haven't ended, count number of empty battler spots + // In multi battles, jump to pointer if more than 1 spot empty // In non-multi battles, jump to pointer if 1 spot is missing on both sides - if (gBattleOutcome == 0 && (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK))) + if (gBattleOutcome == 0 && (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK | BATTLE_TYPE_TRAINER))) { s32 i, emptyPlayerSpots, emptyOpponentSpots; @@ -5482,7 +5485,7 @@ static void Cmd_moveend(void) } // Not strictly a protect effect, but works the same way else if (gProtectStructs[gBattlerTarget].beakBlastCharge - && CanBeBurned(gBattlerAttacker) + && CanBeBurned(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker)) && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)) { gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE; @@ -6761,7 +6764,7 @@ static void Cmd_openpartyscreen(void) u32 i, battler = 0; const u8 *failInstr = cmd->failInstr; - if (cmd->battler == BS_FAINTED_LINK_MULTIPLE_1) + if (cmd->battler == BS_FAINTED_MULTIPLE_1) { if ((gBattleTypeFlags & BATTLE_TYPE_MULTI) || !(gBattleTypeFlags & BATTLE_TYPE_DOUBLE)) { @@ -6916,7 +6919,7 @@ static void Cmd_openpartyscreen(void) } gBattlescriptCurrInstr = cmd->nextInstr; } - else if (cmd->battler == BS_FAINTED_LINK_MULTIPLE_2) + else if (cmd->battler == BS_FAINTED_MULTIPLE_2) { if (!(gBattleTypeFlags & BATTLE_TYPE_MULTI)) { @@ -7134,13 +7137,8 @@ bool32 DoSwitchInAbilities(u32 battler) || AbilityBattleEffects(ABILITYEFFECT_TRACE2, 0, 0, 0, 0)); } -static void Cmd_switchineffects(void) +static void UpdateSentMonFlags(u32 battler) { - CMD_ARGS(u8 battler); - - s32 i; - u32 battler = GetBattlerForBattleScript(cmd->battler); - UpdateSentPokesToOpponentValue(battler); gHitMarker &= ~HITMARKER_FAINTED(battler); @@ -7148,7 +7146,11 @@ static void Cmd_switchineffects(void) if (!BattlerHasAi(battler)) gBattleStruct->appearedInBattle |= gBitTable[gBattlerPartyIndexes[battler]]; +} +static bool32 DoSwitchInEffectsForBattler(u32 battler) +{ + u32 i; // Neutralizing Gas announces itself before hazards if (gBattleMons[battler].ability == ABILITY_NEUTRALIZING_GAS && gSpecialStatuses[battler].announceNeutralizingGas == 0) { @@ -7266,7 +7268,7 @@ static void Cmd_switchineffects(void) BattleScriptPushCursor(); gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_Z_HP_TRAP; gBattlescriptCurrInstr = BattleScript_HealReplacementZMove; - return; + return TRUE; } else { @@ -7281,9 +7283,9 @@ static void Cmd_switchineffects(void) gDisableStructs[battler].truantSwitchInHack = 0; if (DoSwitchInAbilities(battler) || ItemBattleEffects(ITEMEFFECT_ON_SWITCH_IN, battler, FALSE)) - return; + return TRUE; else if (AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, battler, 0, 0, 0)) - return; + return TRUE; gDisableStructs[battler].stickyWebDone = FALSE; gDisableStructs[battler].spikesDone = FALSE; @@ -7299,22 +7301,68 @@ static void Cmd_switchineffects(void) gBattleStruct->hpOnSwitchout[GetBattlerSide(i)] = gBattleMons[i].hp; } - if (cmd->battler == BS_FAINTED_LINK_MULTIPLE_1) - { - u32 hitmarkerFaintBits = gHitMarker >> 28; - - gBattlerFainted++; - while (1) - { - if (hitmarkerFaintBits & gBitTable[gBattlerFainted] && !(gAbsentBattlerFlags & gBitTable[gBattlerFainted])) - break; - if (gBattlerFainted >= gBattlersCount) - break; - gBattlerFainted++; - } - } gBattleStruct->forcedSwitch &= ~(gBitTable[battler]); + return FALSE; + } + + return TRUE; // Effect's script plays. +} + +static void Cmd_switchineffects(void) +{ + CMD_ARGS(u8 battler); + u32 i, battler = GetBattlerForBattleScript(cmd->battler); + + switch (cmd->battler) + { + // Multiple mons fainted and are being switched-in. Their abilities/hazards will play according to speed ties. + case BS_FAINTED_MULTIPLE_1: // Saves the battlers. + gBattleStruct->multipleSwitchInBattlers |= gBitTable[battler]; + UpdateSentMonFlags(battler); + + // Increment fainted battler. + do + { + gBattlerFainted++; + if (gBattlerFainted >= gBattlersCount) + break; + if (gHitMarker & HITMARKER_FAINTED(gBattlerFainted) && !(gAbsentBattlerFlags & gBitTable[gBattlerFainted])) + break; + } while (1); + gBattlescriptCurrInstr = cmd->nextInstr; + return; + case BS_FAINTED_MULTIPLE_2: // Plays hazards/abilities. + switch (gBattleStruct->multipleSwitchInState) + { + case 0: // Sort battlers by speed + for (i = 0; i < gBattlersCount; i++) + gBattleStruct->multipleSwitchInSortedBattlers[i] = i; + SortBattlersBySpeed(gBattleStruct->multipleSwitchInSortedBattlers, FALSE); + gBattleStruct->multipleSwitchInState++; + gBattleStruct->multipleSwitchInCursor = 0; + // Loop through all available battlers + case 1: + for (; gBattleStruct->multipleSwitchInCursor < gBattlersCount; gBattleStruct->multipleSwitchInCursor++) + { + gBattlerFainted = gBattleStruct->multipleSwitchInSortedBattlers[gBattleStruct->multipleSwitchInCursor]; + if (gBattleStruct->multipleSwitchInBattlers & gBitTable[gBattlerFainted]) + { + if (DoSwitchInEffectsForBattler(gBattlerFainted)) + return; + } + } + // All battlers done, end + gBattleStruct->multipleSwitchInBattlers = 0; + gBattleStruct->multipleSwitchInState = 0; + gBattlescriptCurrInstr = cmd->nextInstr; + } + break; + default: + UpdateSentMonFlags(battler); + if (!DoSwitchInEffectsForBattler(battler)) + gBattlescriptCurrInstr = cmd->nextInstr; + break; } } @@ -7961,7 +8009,7 @@ static void BestowItem(u32 battlerAtk, u32 battlerDef) // Called by Cmd_removeitem. itemId represents the item that was removed, not being given. static bool32 TrySymbiosis(u32 battler, u32 itemId) { - if (!gBattleStruct->itemLost[gBattlerPartyIndexes[battler]].stolen + if (!gBattleStruct->itemLost[B_SIDE_PLAYER][gBattlerPartyIndexes[battler]].stolen && gBattleStruct->changedItems[battler] == ITEM_NONE && GetBattlerHoldEffect(battler, TRUE) != HOLD_EFFECT_EJECT_BUTTON && GetBattlerHoldEffect(battler, TRUE) != HOLD_EFFECT_EJECT_PACK @@ -9914,16 +9962,17 @@ static void Cmd_various(void) case VARIOUS_PSYCHO_SHIFT: { VARIOUS_ARGS(const u8 *failInstr); + u32 targetAbility = GetBattlerAbility(gBattlerTarget); // Psycho shift works - if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_POISON) && CanBePoisoned(gBattlerAttacker, gBattlerTarget)) + if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_POISON) && CanBePoisoned(gBattlerAttacker, gBattlerTarget, targetAbility)) gBattleCommunication[MULTISTRING_CHOOSER] = 0; - else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_TOXIC_POISON) && CanBePoisoned(gBattlerAttacker, gBattlerTarget)) + else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_TOXIC_POISON) && CanBePoisoned(gBattlerAttacker, gBattlerTarget, targetAbility)) gBattleCommunication[MULTISTRING_CHOOSER] = 1; - else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_BURN) && CanBeBurned(gBattlerTarget)) + else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_BURN) && CanBeBurned(gBattlerTarget, targetAbility)) gBattleCommunication[MULTISTRING_CHOOSER] = 2; - else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_PARALYSIS) && CanBeParalyzed(gBattlerTarget)) + else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_PARALYSIS) && CanBeParalyzed(gBattlerTarget, targetAbility)) gBattleCommunication[MULTISTRING_CHOOSER] = 3; - else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP) && CanSleep(gBattlerTarget)) + else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP) && CanBeSlept(gBattlerTarget, targetAbility)) gBattleCommunication[MULTISTRING_CHOOSER] = 4; else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_FROSTBITE) && CanBeFrozen(gBattlerTarget)) gBattleCommunication[MULTISTRING_CHOOSER] = 5; @@ -13383,7 +13432,16 @@ static void Cmd_handlefurycutter(void) } else { - if (gDisableStructs[gBattlerAttacker].furyCutterCounter != 5 + u32 max; + + if (B_UPDATED_MOVE_DATA >= GEN_6) + max = 3; + else if (B_UPDATED_MOVE_DATA == GEN_5) + max = 4; + else + max = 5; + + if (gDisableStructs[gBattlerAttacker].furyCutterCounter < max && gSpecialStatuses[gBattlerAttacker].parentalBondState != PARENTAL_BOND_1ST_HIT) // Don't increment counter on first hit gDisableStructs[gBattlerAttacker].furyCutterCounter++; @@ -15297,6 +15355,13 @@ static void Cmd_givecaughtmon(void) { CMD_ARGS(); + if (B_RESTORE_HELD_BATTLE_ITEMS >= GEN_9) + { + u16 lostItem = gBattleStruct->itemLost[B_SIDE_OPPONENT][gBattlerPartyIndexes[GetCatchingBattler()]].originalItem; + if (lostItem != ITEM_NONE && ItemId_GetPocket(lostItem) != POCKET_BERRIES) + SetMonData(&gEnemyParty[gBattlerPartyIndexes[GetCatchingBattler()]], MON_DATA_HELD_ITEM, &lostItem); // Restore non-berry items + } + if (GiveMonToPlayer(&gEnemyParty[gBattlerPartyIndexes[GetCatchingBattler()]]) != MON_GIVEN_TO_PARTY) { if (!ShouldShowBoxWasFullMessage()) diff --git a/src/battle_setup.c b/src/battle_setup.c index e92b32d4c9..41f2b9b502 100644 --- a/src/battle_setup.c +++ b/src/battle_setup.c @@ -1938,15 +1938,16 @@ static bool32 HasAtLeastFiveBadges(void) void IncrementRematchStepCounter(void) { #if FREE_MATCH_CALL == FALSE - if (HasAtLeastFiveBadges() - && (I_VS_SEEKER_CHARGING != 0) - && (!CheckBagHasItem(ITEM_VS_SEEKER, 1))) - { - if (gSaveBlock1Ptr->trainerRematchStepCounter >= STEP_COUNTER_MAX) - gSaveBlock1Ptr->trainerRematchStepCounter = STEP_COUNTER_MAX; - else - gSaveBlock1Ptr->trainerRematchStepCounter++; - } + if (!HasAtLeastFiveBadges()) + return; + + if (IsVsSeekerEnabled()) + return; + + if (gSaveBlock1Ptr->trainerRematchStepCounter >= STEP_COUNTER_MAX) + gSaveBlock1Ptr->trainerRematchStepCounter = STEP_COUNTER_MAX; + else + gSaveBlock1Ptr->trainerRematchStepCounter++; #endif //FREE_MATCH_CALL } diff --git a/src/battle_util.c b/src/battle_util.c index 1c9b8a4d79..99a609a513 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -932,11 +932,11 @@ u8 GetBattlerForBattleScript(u8 caseId) case BS_FAINTED: ret = gBattlerFainted; break; - case BS_FAINTED_LINK_MULTIPLE_1: + case BS_FAINTED_MULTIPLE_1: ret = gBattlerFainted; break; case BS_ATTACKER_WITH_PARTNER: - case BS_FAINTED_LINK_MULTIPLE_2: + case BS_FAINTED_MULTIPLE_2: case BS_ATTACKER_SIDE: case BS_TARGET_SIDE: case BS_PLAYER1: @@ -1691,7 +1691,10 @@ static bool32 EndTurnTerrain(u32 terrainFlag, u32 stringTableId) gFieldStatuses &= ~terrainFlag; TryToRevertMimicryAndFlags(); gBattleCommunication[MULTISTRING_CHOOSER] = stringTableId; - BattleScriptExecute(BattleScript_TerrainEnds); + if (terrainFlag & STATUS_FIELD_GRASSY_TERRAIN) + BattleScriptExecute(BattleScript_GrassyTerrainEnds); + else + BattleScriptExecute(BattleScript_TerrainEnds); return TRUE; } } @@ -3364,7 +3367,7 @@ u8 AtkCanceller_UnableToUseMove(u32 moveType) if (gBattleMons[gBattlerAttacker].status2 & STATUS2_CONFUSION) { // confusion dmg - if (RandomWeighted(RNG_CONFUSION, (B_CONFUSION_SELF_DMG_CHANCE >= GEN_7 ? 2 : 1), 1)) + if (RandomPercentage(RNG_CONFUSION, (B_CONFUSION_SELF_DMG_CHANCE >= GEN_7 ? 33 : 50))) { gBattleCommunication[MULTISTRING_CHOOSER] = TRUE; gBattlerTarget = gBattlerAttacker; @@ -5339,7 +5342,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 && !IsAbilityOnSide(gBattlerAttacker, ABILITY_AROMA_VEIL) && gBattleMons[gBattlerAttacker].pp[gChosenMovePos] != 0 && !(GetActiveGimmick(gBattlerAttacker) == GIMMICK_DYNAMAX) // TODO: Max Moves don't make contact, useless? - && (Random() % 3) == 0) + && RandomPercentage(RNG_CURSED_BODY, 30)) { gDisableStructs[gBattlerAttacker].disabledMove = gChosenMove; gDisableStructs[gBattlerAttacker].disableTimer = 4; @@ -5500,8 +5503,10 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 } break; case ABILITY_EFFECT_SPORE: + { + u32 ability = GetBattlerAbility(gBattlerAttacker); if ((!IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GRASS) || B_POWDER_GRASS < GEN_6) - && GetBattlerAbility(gBattlerAttacker) != ABILITY_OVERCOAT + && ability != ABILITY_OVERCOAT && GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_SAFETY_GOGGLES) { u32 poison, paralysis, sleep; @@ -5529,7 +5534,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 && IsBattlerAlive(gBattlerAttacker) && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && TARGET_TURN_DAMAGED - && CanSleep(gBattlerAttacker) + && CanBeSlept(gBattlerAttacker, ability) && GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_PROTECTIVE_PADS && IsMoveMakingContact(move, gBattlerAttacker)) { @@ -5541,6 +5546,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 effect++; } } + } break; case ABILITY_POISON_POINT: if (B_ABILITY_TRIGGER_CHANCE >= GEN_4 ? RandomPercentage(RNG_POISON_POINT, 30) : RandomChance(RNG_POISON_POINT, 1, 3)) @@ -5550,7 +5556,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 && IsBattlerAlive(gBattlerAttacker) && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && TARGET_TURN_DAMAGED - && CanBePoisoned(gBattlerTarget, gBattlerAttacker) + && CanBePoisoned(gBattlerTarget, gBattlerAttacker, GetBattlerAbility(gBattlerAttacker)) && GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_PROTECTIVE_PADS && IsMoveMakingContact(move, gBattlerAttacker)) { @@ -5571,7 +5577,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 && IsBattlerAlive(gBattlerAttacker) && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && TARGET_TURN_DAMAGED - && CanBeParalyzed(gBattlerAttacker) + && CanBeParalyzed(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker)) && GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_PROTECTIVE_PADS && IsMoveMakingContact(move, gBattlerAttacker)) { @@ -5591,7 +5597,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 && GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_PROTECTIVE_PADS && (IsMoveMakingContact(move, gBattlerAttacker)) && TARGET_TURN_DAMAGED - && CanBeBurned(gBattlerAttacker) + && CanBeBurned(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker)) && (B_ABILITY_TRIGGER_CHANCE >= GEN_4 ? RandomPercentage(RNG_FLAME_BODY, 30) : RandomChance(RNG_FLAME_BODY, 1, 3))) { gBattleScripting.moveEffect = MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_BURN; @@ -5808,11 +5814,11 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && IsBattlerAlive(gBattlerTarget) && !gProtectStructs[gBattlerAttacker].confusionSelfDmg - && CanBePoisoned(gBattlerAttacker, gBattlerTarget) + && CanBePoisoned(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerTarget)) && GetBattlerHoldEffect(gBattlerAttacker, TRUE) != HOLD_EFFECT_PROTECTIVE_PADS && IsMoveMakingContact(move, gBattlerAttacker) && TARGET_TURN_DAMAGED // Need to actually hit the target - && (Random() % 3) == 0) + && RandomPercentage(RNG_POISON_TOUCH, 30)) { gBattleScripting.moveEffect = MOVE_EFFECT_POISON; PREPARE_ABILITY_BUFFER(gBattleTextBuff1, gLastUsedAbility); @@ -5826,7 +5832,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && IsBattlerAlive(gBattlerTarget) && !gProtectStructs[gBattlerAttacker].confusionSelfDmg - && CanBePoisoned(gBattlerAttacker, gBattlerTarget) + && CanBePoisoned(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerTarget)) && TARGET_TURN_DAMAGED // Need to actually hit the target && RandomWeighted(RNG_TOXIC_CHAIN, 7, 3)) { @@ -6276,7 +6282,14 @@ u32 GetBattlerAbility(u32 battler) bool32 noAbilityShield = GetBattlerHoldEffectIgnoreAbility(battler, TRUE) != HOLD_EFFECT_ABILITY_SHIELD; if (gAbilitiesInfo[gBattleMons[battler].ability].cantBeSuppressed) + { + // Edge case: pokemon under the effect of gastro acid transforms into a pokemon with Comatose (Todo: verify how other unsuppressable abilities behave) + if (gBattleMons[battler].status2 & STATUS2_TRANSFORMED + && gStatuses3[battler] & STATUS3_GASTRO_ACID + && gBattleMons[battler].ability == ABILITY_COMATOSE) + return ABILITY_NONE; return gBattleMons[battler].ability; + } if (gStatuses3[battler] & STATUS3_GASTRO_ACID) return ABILITY_NONE; @@ -6289,6 +6302,7 @@ u32 GetBattlerAbility(u32 battler) if (((IsMoldBreakerTypeAbility(gBattleMons[gBattlerAttacker].ability) && !(gStatuses3[gBattlerAttacker] & STATUS3_GASTRO_ACID)) || gMovesInfo[gCurrentMove].ignoresTargetAbility) + && battler != gBattlerAttacker && gAbilitiesInfo[gBattleMons[battler].ability].breakable && noAbilityShield && gBattlerByTurnOrder[gCurrentTurnActionNumber] == gBattlerAttacker @@ -6410,83 +6424,78 @@ bool32 IsBattlerTerrainAffected(u32 battler, u32 terrainFlag) return IsBattlerGrounded(battler); } -bool32 CanSleep(u32 battler) +bool32 CanBeSlept(u32 battler, u32 ability) { - u16 ability = GetBattlerAbility(battler); if (ability == ABILITY_INSOMNIA - || ability == ABILITY_VITAL_SPIRIT - || ability == ABILITY_COMATOSE - || ability == ABILITY_PURIFYING_SALT - || gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD - || gBattleMons[battler].status1 & STATUS1_ANY - || IsAbilityOnSide(battler, ABILITY_SWEET_VEIL) - || IsAbilityStatusProtected(battler) - || IsBattlerTerrainAffected(battler, STATUS_FIELD_ELECTRIC_TERRAIN | STATUS_FIELD_MISTY_TERRAIN)) - return FALSE; - return TRUE; -} - -bool32 CanBePoisoned(u32 battlerAttacker, u32 battlerTarget) -{ - u16 ability = GetBattlerAbility(battlerTarget); - - if (!(CanPoisonType(battlerAttacker, battlerTarget)) - || gSideStatuses[GetBattlerSide(battlerTarget)] & SIDE_STATUS_SAFEGUARD - || gBattleMons[battlerTarget].status1 & STATUS1_ANY - || ability == ABILITY_IMMUNITY + || ability == ABILITY_VITAL_SPIRIT || ability == ABILITY_COMATOSE || ability == ABILITY_PURIFYING_SALT - || IsAbilityOnSide(battlerTarget, ABILITY_PASTEL_VEIL) - || IsAbilityStatusProtected(battlerTarget) - || IsBattlerTerrainAffected(battlerTarget, STATUS_FIELD_MISTY_TERRAIN)) + || gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD + || gBattleMons[battler].status1 & STATUS1_ANY + || IsAbilityOnSide(battler, ABILITY_SWEET_VEIL) + || IsAbilityStatusProtected(battler) + || IsBattlerTerrainAffected(battler, STATUS_FIELD_ELECTRIC_TERRAIN | STATUS_FIELD_MISTY_TERRAIN)) return FALSE; return TRUE; } -bool32 CanBeBurned(u32 battler) +bool32 CanBePoisoned(u32 battlerAtk, u32 battlerDef, u32 defAbility) +{ + if (!(CanPoisonType(battlerAtk, battlerDef)) + || gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_SAFEGUARD + || gBattleMons[battlerDef].status1 & STATUS1_ANY + || defAbility == ABILITY_IMMUNITY + || defAbility == ABILITY_COMATOSE + || defAbility == ABILITY_PURIFYING_SALT + || IsAbilityOnSide(battlerDef, ABILITY_PASTEL_VEIL) + || IsAbilityStatusProtected(battlerDef) + || IsBattlerTerrainAffected(battlerDef, STATUS_FIELD_MISTY_TERRAIN)) + return FALSE; + return TRUE; +} + +bool32 CanBeBurned(u32 battler, u32 ability) { - u16 ability = GetBattlerAbility(battler); if (IS_BATTLER_OF_TYPE(battler, TYPE_FIRE) + || gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD + || gBattleMons[battler].status1 & STATUS1_ANY + || ability == ABILITY_WATER_VEIL + || ability == ABILITY_WATER_BUBBLE + || ability == ABILITY_COMATOSE + || ability == ABILITY_THERMAL_EXCHANGE + || ability == ABILITY_PURIFYING_SALT + || IsAbilityStatusProtected(battler) + || IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN)) + return FALSE; + return TRUE; +} + +bool32 CanBeParalyzed(u32 battler, u32 ability) +{ + if ((B_PARALYZE_ELECTRIC >= GEN_6 && IS_BATTLER_OF_TYPE(battler, TYPE_ELECTRIC)) || gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD - || gBattleMons[battler].status1 & STATUS1_ANY - || ability == ABILITY_WATER_VEIL - || ability == ABILITY_WATER_BUBBLE + || ability == ABILITY_LIMBER || ability == ABILITY_COMATOSE - || ability == ABILITY_THERMAL_EXCHANGE || ability == ABILITY_PURIFYING_SALT + || gBattleMons[battler].status1 & STATUS1_ANY || IsAbilityStatusProtected(battler) || IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN)) return FALSE; return TRUE; } -bool32 CanBeParalyzed(u32 battler) -{ - u16 ability = GetBattlerAbility(battler); - if ((B_PARALYZE_ELECTRIC >= GEN_6 && IS_BATTLER_OF_TYPE(battler, TYPE_ELECTRIC)) - || gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD - || ability == ABILITY_LIMBER - || ability == ABILITY_COMATOSE - || ability == ABILITY_PURIFYING_SALT - || gBattleMons[battler].status1 & STATUS1_ANY - || IsAbilityStatusProtected(battler) - || IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN)) - return FALSE; - return TRUE; -} - bool32 CanBeFrozen(u32 battler) { u16 ability = GetBattlerAbility(battler); if (IS_BATTLER_OF_TYPE(battler, TYPE_ICE) - || IsBattlerWeatherAffected(battler, B_WEATHER_SUN) - || gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD - || ability == ABILITY_MAGMA_ARMOR - || ability == ABILITY_COMATOSE - || ability == ABILITY_PURIFYING_SALT - || gBattleMons[battler].status1 & STATUS1_ANY - || IsAbilityStatusProtected(battler) - || IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN)) + || IsBattlerWeatherAffected(battler, B_WEATHER_SUN) + || gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD + || ability == ABILITY_MAGMA_ARMOR + || ability == ABILITY_COMATOSE + || ability == ABILITY_PURIFYING_SALT + || gBattleMons[battler].status1 & STATUS1_ANY + || IsAbilityStatusProtected(battler) + || IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN)) return FALSE; return TRUE; } @@ -6509,8 +6518,8 @@ bool32 CanGetFrostbite(u32 battler) bool32 CanBeConfused(u32 battler) { if (GetBattlerAbility(battler) == ABILITY_OWN_TEMPO - || gBattleMons[battler].status2 & STATUS2_CONFUSION - || IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN)) + || gBattleMons[battler].status2 & STATUS2_CONFUSION + || IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN)) return FALSE; return TRUE; } @@ -8026,7 +8035,7 @@ u8 ItemBattleEffects(u8 caseID, u32 battler, bool32 moveTurn) switch (battlerHoldEffect) { case HOLD_EFFECT_TOXIC_ORB: - if (CanBePoisoned(battler, battler)) + if (CanBePoisoned(battler, battler, GetBattlerAbility(battler))) { effect = ITEM_STATUS_CHANGE; gBattleMons[battler].status1 = STATUS1_TOXIC_POISON; @@ -8035,7 +8044,7 @@ u8 ItemBattleEffects(u8 caseID, u32 battler, bool32 moveTurn) } break; case HOLD_EFFECT_FLAME_ORB: - if (CanBeBurned(battler)) + if (CanBeBurned(battler, battlerAbility)) { effect = ITEM_STATUS_CHANGE; gBattleMons[battler].status1 = STATUS1_BURN; @@ -8300,7 +8309,7 @@ u8 IsMonDisobedient(void) obedienceLevel = levelReferenced - obedienceLevel; calc = (Random() & 255); - if (calc < obedienceLevel && CanSleep(gBattlerAttacker)) + if (calc < obedienceLevel && CanBeSlept(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker))) { // try putting asleep int i; @@ -10734,12 +10743,12 @@ bool32 CanBattlerFormChange(u32 battler, u16 method) return DoesSpeciesHaveFormChangeMethod(gBattleMons[battler].species, method); } -bool32 TryBattleFormChange(u32 battler, u16 method) +bool32 TryBattleFormChange(u32 battler, u32 method) { - u8 monId = gBattlerPartyIndexes[battler]; - u8 side = GetBattlerSide(battler); + u32 monId = gBattlerPartyIndexes[battler]; + u32 side = GetBattlerSide(battler); struct Pokemon *party = GetBattlerParty(battler); - u16 targetSpecies; + u32 targetSpecies; if (!CanBattlerFormChange(battler, method)) return FALSE; @@ -10777,10 +10786,14 @@ bool32 TryBattleFormChange(u32 battler, u16 method) if (restoreSpecies) { + u32 abilityForm = gBattleMons[battler].ability; // Reverts the original species TryToSetBattleFormChangeMoves(&party[monId], method); SetMonData(&party[monId], MON_DATA_SPECIES, &gBattleStruct->changedSpecies[side][monId]); RecalcBattlerStats(battler, &party[monId]); + // Battler data is not updated with regular form's ability, not doing so could cause wrong ability activation. + if (method == FORM_CHANGE_FAINT) + gBattleMons[battler].ability = abilityForm; return TRUE; } } @@ -11064,9 +11077,9 @@ void TryRestoreHeldItems(void) for (i = 0; i < PARTY_SIZE; i++) { // Check if held items should be restored after battle based on generation - if (B_RESTORE_HELD_BATTLE_ITEMS >= GEN_9 || gBattleStruct->itemLost[i].stolen || returnNPCItems) + if (B_RESTORE_HELD_BATTLE_ITEMS >= GEN_9 || gBattleStruct->itemLost[B_SIDE_PLAYER][i].stolen || returnNPCItems) { - u16 lostItem = gBattleStruct->itemLost[i].originalItem; + u16 lostItem = gBattleStruct->itemLost[B_SIDE_PLAYER][i].originalItem; // Check if the lost item is a berry and the mon is not holding it if (ItemId_GetPocket(lostItem) == POCKET_BERRIES && GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM) != lostItem) @@ -11127,8 +11140,8 @@ void TrySaveExchangedItem(u32 battler, u16 stolenItem) if (gBattleTypeFlags & BATTLE_TYPE_TRAINER && !(gBattleTypeFlags & BATTLE_TYPE_FRONTIER) && GetBattlerSide(battler) == B_SIDE_PLAYER - && stolenItem == gBattleStruct->itemLost[gBattlerPartyIndexes[battler]].originalItem) - gBattleStruct->itemLost[gBattlerPartyIndexes[battler]].stolen = TRUE; + && stolenItem == gBattleStruct->itemLost[B_SIDE_PLAYER][gBattlerPartyIndexes[battler]].originalItem) + gBattleStruct->itemLost[B_SIDE_PLAYER][gBattlerPartyIndexes[battler]].stolen = TRUE; } bool32 IsBattlerAffectedByHazards(u32 battler, bool32 toxicSpikes) diff --git a/src/data/abilities.h b/src/data/abilities.h index 8b81057b4f..8fc7d699e1 100644 --- a/src/data/abilities.h +++ b/src/data/abilities.h @@ -2548,7 +2548,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] = #else .name = _("SuprswtSyrup"), #endif - .description = COMPOUND_STRING("Lowers the foe's Speed."), + .description = COMPOUND_STRING("Lowers the foe's Evasion."), .aiRating = 5, }, diff --git a/src/data/moves_info.h b/src/data/moves_info.h index 1e6e248381..4e27d9b397 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -15646,7 +15646,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = [MOVE_FLORAL_HEALING] = { - .name = HANDLE_EXPANDED_MOVE_NAME("FloralHealng", "Floral Healng"), + .name = HANDLE_EXPANDED_MOVE_NAME("FloralHealng", "Floral Healing"), .description = COMPOUND_STRING( "Restores an ally's HP.\n" "Heals more on grass."), @@ -18422,7 +18422,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = [MOVE_JUNGLE_HEALING] = { - .name = HANDLE_EXPANDED_MOVE_NAME("JungleHealng", "Jungle Healng"), + .name = HANDLE_EXPANDED_MOVE_NAME("JungleHealng", "Jungle Healing"), .description = COMPOUND_STRING( "Heals HP and status of\n" "itself and allies in battle."), @@ -21042,7 +21042,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = }, [MOVE_OCEANIC_OPERETTA] = { - .name = COMPOUND_STRING("Oceaning Operetta"), + .name = COMPOUND_STRING("Oceanic Operetta"), .description = sNullDescription, .effect = EFFECT_HIT, .power = 195, diff --git a/src/data/pokemon/species_info.h b/src/data/pokemon/species_info.h index eae4929a9d..9427ad2bc6 100644 --- a/src/data/pokemon/species_info.h +++ b/src/data/pokemon/species_info.h @@ -76,11 +76,7 @@ const struct SpeciesInfo gSpeciesInfo[] = .categoryName = _("Unknown"), .height = 0, .weight = 0, - .description = COMPOUND_STRING( - "This is a newly discovered Pokémon.\n" - "It is currently under investigation.\n" - "No detailed information is available\n" - "at this time."), + .description = gFallbackPokedexText, .pokemonScale = 256, .pokemonOffset = 0, .trainerScale = 256, diff --git a/src/data/pokemon/species_info/gen_7_families.h b/src/data/pokemon/species_info/gen_7_families.h index feb4fe968b..4669051d04 100644 --- a/src/data/pokemon/species_info/gen_7_families.h +++ b/src/data/pokemon/species_info/gen_7_families.h @@ -4143,6 +4143,7 @@ const struct SpeciesInfo gSpeciesInfoGen7[] = .baseSpAttack = 60, \ .baseSpDefense = 100, \ .weight = 400, \ + .description = gMiniorMeteorPokedexText, \ .frontPic = gMonFrontPic_MiniorMeteor, \ .frontPicSize = MON_COORDS_SIZE(48, 40), \ .frontPicYOffset = 14, \ diff --git a/src/data/pokemon/species_info/shared_dex_text.h b/src/data/pokemon/species_info/shared_dex_text.h index dbfef82d74..0c591bd811 100644 --- a/src/data/pokemon/species_info/shared_dex_text.h +++ b/src/data/pokemon/species_info/shared_dex_text.h @@ -1,3 +1,10 @@ +// fallback +const u8 gFallbackPokedexText[] = _( + "This is a newly discovered Pokémon.\n" + "It is currently under investigation.\n" + "No detailed information is available\n" + "at this time."); + // Gen 1 families const u8 gRaticateAlolanPokedexText[] = _( "It forms a group of Rattata, which it \n" diff --git a/src/vs_seeker.c b/src/vs_seeker.c index 63b6c6f9d5..30dcb74ce0 100644 --- a/src/vs_seeker.c +++ b/src/vs_seeker.c @@ -577,6 +577,14 @@ u16 GetRematchTrainerIdVSSeeker(u16 trainerId) return gRematchTable[tableId].trainerIds[rematchTrainerIdx]; } +bool32 IsVsSeekerEnabled(void) +{ + if (I_VS_SEEKER_CHARGING == 0) + return FALSE; + + return (CheckBagHasItem(ITEM_VS_SEEKER, 1)); +} + static bool8 ObjectEventIdIsSane(u8 objectEventId) { struct ObjectEvent *objectEvent = &gObjectEvents[objectEventId]; diff --git a/test/battle/ability/beads_of_ruin.c b/test/battle/ability/beads_of_ruin.c index a58297a58b..bb05031fe3 100644 --- a/test/battle/ability/beads_of_ruin.c +++ b/test/battle/ability/beads_of_ruin.c @@ -46,9 +46,9 @@ SINGLE_BATTLE_TEST("Beads of Ruin's message displays correctly after all battler ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, opponent); // Everyone faints. MESSAGE("Go! Chi-Yu!"); + MESSAGE("2 sent out Wobbuffet!"); ABILITY_POPUP(player, ABILITY_BEADS_OF_RUIN); MESSAGE("Chi-Yu's Beads of Ruin weakened the Sp. Def of all surrounding Pokémon!"); - MESSAGE("2 sent out Wobbuffet!"); } } diff --git a/test/battle/ability/comatose.c b/test/battle/ability/comatose.c new file mode 100644 index 0000000000..bd991c258e --- /dev/null +++ b/test/battle/ability/comatose.c @@ -0,0 +1,54 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Comatose prevents status-inducing moves") +{ + u32 move; + + PARAMETRIZE { move = MOVE_TOXIC; } + PARAMETRIZE { move = MOVE_POISONPOWDER; } + PARAMETRIZE { move = MOVE_SLEEP_POWDER; } + PARAMETRIZE { move = MOVE_THUNDER_WAVE; } + + GIVEN { + PLAYER(SPECIES_KOMALA) { Ability(ABILITY_COMATOSE); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, move); } + } SCENE { + MESSAGE("Komala is drowsing!"); + + NOT ANIMATION(ANIM_TYPE_MOVE, move, opponent); + ABILITY_POPUP(player, ABILITY_COMATOSE); + MESSAGE("It doesn't affect Komala…"); + } +} + +SINGLE_BATTLE_TEST("Comatose may be suppressed if pokemon transformed into a pokemon with Comatose ability and was under the effects of Gastro Acid") +{ + u32 move; + + PARAMETRIZE { move = MOVE_TOXIC; } + PARAMETRIZE { move = MOVE_POISONPOWDER; } + PARAMETRIZE { move = MOVE_SLEEP_POWDER; } + PARAMETRIZE { move = MOVE_THUNDER_WAVE; } + + GIVEN { + PLAYER(SPECIES_KOMALA) { Ability(ABILITY_COMATOSE); Speed(30); } + OPPONENT(SPECIES_DITTO) { Speed(20); } + } WHEN { + TURN { MOVE(player, MOVE_GASTRO_ACID); MOVE(opponent, MOVE_TRANSFORM); } + TURN { MOVE(player, move); } + } SCENE { + MESSAGE("Komala is drowsing!"); + MESSAGE("Komala used Gastro Acid!"); + MESSAGE("Foe Ditto used Transform!"); + MESSAGE("Foe Ditto transformed into Komala!"); + + ANIMATION(ANIM_TYPE_MOVE, move, player); + if (move == MOVE_POISONPOWDER) { STATUS_ICON(opponent, poison: TRUE); } + else if (move == MOVE_TOXIC) { STATUS_ICON(opponent, badPoison: TRUE); } + else if (move == MOVE_THUNDER_WAVE) { STATUS_ICON(opponent, paralysis: TRUE); } + else if (move == MOVE_SLEEP_POWDER) { STATUS_ICON(opponent, sleep: TRUE); } + } +} diff --git a/test/battle/ability/corrosion.c b/test/battle/ability/corrosion.c index 87477ddbcc..8addbd90fa 100644 --- a/test/battle/ability/corrosion.c +++ b/test/battle/ability/corrosion.c @@ -106,23 +106,122 @@ SINGLE_BATTLE_TEST("If a Poison- or Steel-type Pokémon with Corrosion holds a T SINGLE_BATTLE_TEST("If a Poison- or Steel-type Pokémon with Corrosion poisons a target with Synchronize, Synchronize will not poison Poison- or Steel-type Pokémon") { + u16 move; + PARAMETRIZE { move = MOVE_TOXIC; } + PARAMETRIZE { move = MOVE_POISON_POWDER; } GIVEN { ASSUME(gMovesInfo[MOVE_TOXIC].effect == EFFECT_TOXIC); + ASSUME(gMovesInfo[MOVE_POISON_POWDER].effect == EFFECT_POISON); PLAYER(SPECIES_SALANDIT) { Ability(ABILITY_CORROSION); } OPPONENT(SPECIES_ABRA) { Ability(ABILITY_SYNCHRONIZE); } } WHEN { - TURN { MOVE(player, MOVE_TOXIC); } + TURN { MOVE(player, move); } } SCENE { - ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC, player); + ANIMATION(ANIM_TYPE_MOVE, move, player); ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent); - STATUS_ICON(opponent, badPoison: TRUE); + if (move == MOVE_TOXIC) + STATUS_ICON(opponent, badPoison: TRUE); + else + STATUS_ICON(opponent, poison: TRUE); NONE_OF { ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, player); STATUS_ICON(player, badPoison: TRUE); + STATUS_ICON(player, poison: TRUE); } } } -TO_DO_BATTLE_TEST("Corrosion cannot bypass moves or Abilities that prevent poisoning, such as Safeguard or Immunity"); -TO_DO_BATTLE_TEST("If the Pokémon with this Ability uses Magic Coat to reflect a status move that inflicts poison, the reflected move will be able to poison Poison- or Steel-type Pokémon."); -TO_DO_BATTLE_TEST("Moves used by a Pokémon with Corrosion that are reflected by Magic Coat or Magic Bounce do not retain the ability to poison Poison- or Steel-type Pokémon.") +SINGLE_BATTLE_TEST("Corrosion cannot bypass moves that prevent poisoning such as Safeguard") +{ + u16 move; + PARAMETRIZE { move = MOVE_TOXIC; } + PARAMETRIZE { move = MOVE_POISON_POWDER; } + GIVEN { + ASSUME(gMovesInfo[MOVE_TOXIC].effect == EFFECT_TOXIC); + ASSUME(gMovesInfo[MOVE_POISON_POWDER].effect == EFFECT_POISON); + PLAYER(SPECIES_SALANDIT) { Ability(ABILITY_CORROSION); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_SAFEGUARD); MOVE(player, move); } + } SCENE { + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC, player); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent); + STATUS_ICON(opponent, badPoison: TRUE); + STATUS_ICON(opponent, poison: TRUE); + } + } +} + +SINGLE_BATTLE_TEST("Corrosion cannot bypass abilities that prevent poisoning such as Immunity") +{ + u16 move; + PARAMETRIZE { move = MOVE_TOXIC; } + PARAMETRIZE { move = MOVE_POISON_POWDER; } + GIVEN { + ASSUME(gMovesInfo[MOVE_TOXIC].effect == EFFECT_TOXIC); + ASSUME(gMovesInfo[MOVE_POISON_POWDER].effect == EFFECT_POISON); + PLAYER(SPECIES_SALANDIT) { Ability(ABILITY_CORROSION); } + OPPONENT(SPECIES_SNORLAX) { Ability(ABILITY_IMMUNITY); } + } WHEN { + TURN { MOVE(player, move); } + } SCENE { + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC, player); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent); + STATUS_ICON(opponent, badPoison: TRUE); + STATUS_ICON(opponent, poison: TRUE); + } + } +} + +SINGLE_BATTLE_TEST("Corrosion allows the Pokémon with the ability to poison a Steel or Poison-type opponent by using Magic Coat") +{ + u16 move; + PARAMETRIZE { move = MOVE_TOXIC; } + PARAMETRIZE { move = MOVE_POISON_POWDER; } + GIVEN { + ASSUME(gMovesInfo[MOVE_TOXIC].effect == EFFECT_TOXIC); + ASSUME(gMovesInfo[MOVE_POISON_POWDER].effect == EFFECT_POISON); + ASSUME(gMovesInfo[MOVE_MAGIC_COAT].effect == EFFECT_MAGIC_COAT); + PLAYER(SPECIES_SALANDIT) { Ability(ABILITY_CORROSION); } + OPPONENT(SPECIES_BELDUM); + } WHEN { + TURN { MOVE(player, MOVE_MAGIC_COAT); MOVE(opponent, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_MAGIC_COAT, player); + ANIMATION(ANIM_TYPE_MOVE, move, player); // Bounced by Magic Coat + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent); + if (move == MOVE_TOXIC) + STATUS_ICON(opponent, badPoison: TRUE); + else + STATUS_ICON(opponent, poison: TRUE); + } +} + +SINGLE_BATTLE_TEST("Corrosion's effect is lost if the move used by the Pokémon with the ability is reflected by Magic Coat") +{ + u16 move; + PARAMETRIZE { move = MOVE_TOXIC; } + PARAMETRIZE { move = MOVE_POISON_POWDER; } + GIVEN { + ASSUME(gMovesInfo[MOVE_TOXIC].effect == EFFECT_TOXIC); + ASSUME(gMovesInfo[MOVE_POISON_POWDER].effect == EFFECT_POISON); + ASSUME(gMovesInfo[MOVE_MAGIC_COAT].effect == EFFECT_MAGIC_COAT); + PLAYER(SPECIES_SALANDIT) { Ability(ABILITY_CORROSION); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_MAGIC_COAT); MOVE(player, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_MAGIC_COAT, opponent); + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, move, player); + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, player); + if (move == MOVE_TOXIC) + STATUS_ICON(opponent, badPoison: TRUE); + else + STATUS_ICON(opponent, poison: TRUE); + } + } +} diff --git a/test/battle/ability/cursed_body.c b/test/battle/ability/cursed_body.c new file mode 100644 index 0000000000..20fe659d21 --- /dev/null +++ b/test/battle/ability/cursed_body.c @@ -0,0 +1,17 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Cursed Body triggers 30% of the time") +{ + PASSES_RANDOMLY(3, 10, RNG_CURSED_BODY); + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_FRILLISH) { Ability(ABILITY_CURSED_BODY); } + } WHEN { + TURN { MOVE(player, MOVE_AQUA_JET); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_AQUA_JET, player); + ABILITY_POPUP(opponent, ABILITY_CURSED_BODY); + MESSAGE("Wobbuffet's Aqua Jet was disabled by Foe Frillish's Cursed Body!"); + } +} diff --git a/test/battle/ability/download.c b/test/battle/ability/download.c index 2d9f8f466e..9fecda400d 100644 --- a/test/battle/ability/download.c +++ b/test/battle/ability/download.c @@ -56,6 +56,7 @@ SINGLE_BATTLE_TEST("Download raises Sp.Attack if enemy has lower Sp. Def than De SINGLE_BATTLE_TEST("Download doesn't activate if target hasn't been sent out yet", s16 damagePhysical, s16 damageSpecial) { u32 ability; + PARAMETRIZE { ability = ABILITY_TRACE; } PARAMETRIZE { ability = ABILITY_DOWNLOAD; } GIVEN { @@ -73,13 +74,13 @@ SINGLE_BATTLE_TEST("Download doesn't activate if target hasn't been sent out yet // Everyone faints. SEND_IN_MESSAGE("Porygon"); - MESSAGE("2 sent out Porygon2!"); - NONE_OF { ABILITY_POPUP(player, ABILITY_DOWNLOAD); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); MESSAGE("Porygon's Download raised its Attack!"); } + MESSAGE("2 sent out Porygon2!"); + if (ability == ABILITY_DOWNLOAD) { ABILITY_POPUP(opponent, ABILITY_DOWNLOAD); diff --git a/test/battle/ability/full_metal_body.c b/test/battle/ability/full_metal_body.c new file mode 100644 index 0000000000..3b36f2d1f2 --- /dev/null +++ b/test/battle/ability/full_metal_body.c @@ -0,0 +1,4 @@ +#include "global.h" +#include "test/battle.h" + +// Tests for Full Metal Body are handled in test/battle/ability/clear_body.c diff --git a/test/battle/ability/intimidate.c b/test/battle/ability/intimidate.c index f4c5395723..d2d7bc4af7 100644 --- a/test/battle/ability/intimidate.c +++ b/test/battle/ability/intimidate.c @@ -78,23 +78,29 @@ DOUBLE_BATTLE_TEST("Intimidate doesn't activate on an empty field in a double ba // Everyone faints. SEND_IN_MESSAGE("Ekans"); - MESSAGE("2 sent out Arbok!"); - SEND_IN_MESSAGE("Abra"); - MESSAGE("2 sent out Wynaut!"); - NONE_OF { ABILITY_POPUP(playerLeft, ABILITY_INTIMIDATE); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); - MESSAGE("Ekans's Intimidate cuts Foe Arbok's attack!"); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight); - MESSAGE("Ekans's Intimidate cuts Foe Wynaut's attack!"); - + } + MESSAGE("2 sent out Arbok!"); + NONE_OF { ABILITY_POPUP(opponentLeft, ABILITY_INTIMIDATE); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); - MESSAGE("Foe Arbok's Intimidate cuts Ekans's attack!"); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); - MESSAGE("Foe Arbok's Intimidate cuts Abra's attack!"); } + SEND_IN_MESSAGE("Abra"); + MESSAGE("2 sent out Wynaut!"); + // Intimidate activates after all battlers have been brought out + ABILITY_POPUP(playerLeft, ABILITY_INTIMIDATE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); + MESSAGE("Ekans's Intimidate cuts Foe Arbok's attack!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight); + MESSAGE("Ekans's Intimidate cuts Foe Wynaut's attack!"); + + ABILITY_POPUP(opponentLeft, ABILITY_INTIMIDATE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); + MESSAGE("Foe Arbok's Intimidate cuts Ekans's attack!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); + MESSAGE("Foe Arbok's Intimidate cuts Abra's attack!"); } } @@ -211,6 +217,35 @@ SINGLE_BATTLE_TEST("Intimidate can not further lower opponents Atk stat if it is } } +DOUBLE_BATTLE_TEST("Intimidate is not going to trigger if a mon switches out through u-turn and the opposing field is empty") +{ + GIVEN { + PLAYER(SPECIES_WYNAUT); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); } + OPPONENT(SPECIES_WYNAUT) { HP(1); } + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_TREECKO); + OPPONENT(SPECIES_TORCHIC); + } WHEN { + TURN { + MOVE(opponentRight, MOVE_HEALING_WISH); + MOVE(playerLeft, MOVE_U_TURN, target: opponentLeft); + SEND_OUT(playerLeft, 2); + SEND_OUT(opponentLeft, 2); + SEND_OUT(opponentRight, 3); + } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_HEALING_WISH, opponentRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, playerLeft); + HP_BAR(opponentLeft); + MESSAGE("2 sent out Treecko!"); + MESSAGE("2 sent out Torchic!"); + NOT ABILITY_POPUP(playerLeft, ABILITY_INTIMIDATE); + } +} + SINGLE_BATTLE_TEST("Intimidate activates when it's no longer effected by Neutralizing Gas") { GIVEN { diff --git a/test/battle/ability/minds_eye.c b/test/battle/ability/minds_eye.c index 6498f9f098..7799735ee2 100644 --- a/test/battle/ability/minds_eye.c +++ b/test/battle/ability/minds_eye.c @@ -49,8 +49,8 @@ AI_SINGLE_BATTLE_TEST("AI doesn't use accuracy-lowering moves if it knows that t for (j = MOVE_NONE + 1; j < MOVES_COUNT; j++) { if (gMovesInfo[j].effect == EFFECT_ACCURACY_DOWN || gMovesInfo[j].effect == EFFECT_ACCURACY_DOWN_2) { - PARAMETRIZE{ moveAI = j; abilityAI = ABILITY_SWIFT_SWIM; } - PARAMETRIZE{ moveAI = j; abilityAI = ABILITY_MOLD_BREAKER; } + PARAMETRIZE { moveAI = j; abilityAI = ABILITY_SWIFT_SWIM; } + PARAMETRIZE { moveAI = j; abilityAI = ABILITY_MOLD_BREAKER; } } } diff --git a/test/battle/ability/own_tempo.c b/test/battle/ability/own_tempo.c index d5a6f58a72..a37bc0024a 100644 --- a/test/battle/ability/own_tempo.c +++ b/test/battle/ability/own_tempo.c @@ -56,6 +56,23 @@ SINGLE_BATTLE_TEST("Own Tempo prevents confusion from moves by the user") } } +SINGLE_BATTLE_TEST("Own Tempo is ignored by Mold Breaker") +{ + KNOWN_FAILING; // Ideally the func CanBeConfused should be split into AttackerCanBeConfused and TargetCanBeConfused or we do it in the same func but have a check for when battlerAtk == battlerDef + GIVEN { + ASSUME(gMovesInfo[MOVE_CONFUSE_RAY].effect == EFFECT_CONFUSE); + PLAYER(SPECIES_PINSIR) { Ability(ABILITY_MOLD_BREAKER); } + OPPONENT(SPECIES_SLOWPOKE) { Ability(ABILITY_OWN_TEMPO); }; + } WHEN { + TURN { MOVE(player, MOVE_CONFUSE_RAY); } + } SCENE { + NONE_OF { + ABILITY_POPUP(opponent, ABILITY_OWN_TEMPO); + MESSAGE("Foe Slowpoke's Own Tempo prevents confusion!"); + } + } +} + SINGLE_BATTLE_TEST("Own Tempo cures confusion obtained from an opponent with Mold Breaker") { KNOWN_FAILING; diff --git a/test/battle/ability/poison_touch.c b/test/battle/ability/poison_touch.c new file mode 100644 index 0000000000..b69fa20444 --- /dev/null +++ b/test/battle/ability/poison_touch.c @@ -0,0 +1,77 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Poison Touch has a 30% chance to poison when attacking with contact moves") +{ + PASSES_RANDOMLY(3, 10, RNG_POISON_TOUCH); + GIVEN { + ASSUME(gMovesInfo[MOVE_TACKLE].power > 0); + ASSUME(gMovesInfo[MOVE_TACKLE].makesContact); + PLAYER(SPECIES_GRIMER) { Ability(ABILITY_POISON_TOUCH); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TACKLE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); + ABILITY_POPUP(player, ABILITY_POISON_TOUCH); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent); + MESSAGE("Foe Wobbuffet was poisoned by Grimer's Poison Touch!"); + STATUS_ICON(opponent, poison: TRUE); + } +} + +SINGLE_BATTLE_TEST("Poison Touch only applies when using contact moves") +{ + u32 move; + + PARAMETRIZE { move = MOVE_TACKLE; } + PARAMETRIZE { move = MOVE_SWIFT; } + GIVEN { + ASSUME(gMovesInfo[MOVE_TACKLE].makesContact); + ASSUME(!gMovesInfo[MOVE_SWIFT].makesContact); + PLAYER(SPECIES_GRIMER) { Ability(ABILITY_POISON_TOUCH); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, player); + if (gMovesInfo[move].makesContact) { + ABILITY_POPUP(player, ABILITY_POISON_TOUCH); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent); + MESSAGE("Foe Wobbuffet was poisoned by Grimer's Poison Touch!"); + STATUS_ICON(opponent, poison: TRUE); + } else { + NONE_OF { + ABILITY_POPUP(player, ABILITY_POISON_TOUCH); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent); + MESSAGE("Foe Wobbuffet was poisoned by Grimer's Poison Touch!"); + STATUS_ICON(opponent, poison: TRUE); + } + } + } +} + +SINGLE_BATTLE_TEST("Poison Touch applies between multi-hit move hits") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_ARM_THRUST].effect == EFFECT_MULTI_HIT); + ASSUME(gMovesInfo[MOVE_ARM_THRUST].makesContact); + ASSUME(gItemsInfo[ITEM_PECHA_BERRY].holdEffect == HOLD_EFFECT_CURE_PSN); + PLAYER(SPECIES_GRIMER) { Ability(ABILITY_POISON_TOUCH); } + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_PECHA_BERRY); }; + } WHEN { + TURN { MOVE(player, MOVE_ARM_THRUST); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ARM_THRUST, player); + ABILITY_POPUP(player, ABILITY_POISON_TOUCH); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent); + MESSAGE("Foe Wobbuffet was poisoned by Grimer's Poison Touch!"); + STATUS_ICON(opponent, poison: TRUE); + MESSAGE("Foe Wobbuffet's Pecha Berry cured poison!"); + STATUS_ICON(opponent, poison: FALSE); + ABILITY_POPUP(player, ABILITY_POISON_TOUCH); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent); + MESSAGE("Foe Wobbuffet was poisoned by Grimer's Poison Touch!"); + STATUS_ICON(opponent, poison: TRUE); + } +} diff --git a/test/battle/ability/sticky_hold.c b/test/battle/ability/sticky_hold.c new file mode 100644 index 0000000000..2c7cc09535 --- /dev/null +++ b/test/battle/ability/sticky_hold.c @@ -0,0 +1,18 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Sticky Hold prevents item theft") +{ + GIVEN { + ASSUME(MoveHasAdditionalEffect(MOVE_THIEF, MOVE_EFFECT_STEAL_ITEM)); + PLAYER(SPECIES_URSALUNA) { Item(ITEM_NONE); } + OPPONENT(SPECIES_GASTRODON) { Ability(ABILITY_STICKY_HOLD); Item(ITEM_LIFE_ORB); } + } WHEN { + TURN { MOVE(player, MOVE_THIEF); } + } SCENE { + MESSAGE("Ursaluna used Thief!"); + ABILITY_POPUP(opponent, ABILITY_STICKY_HOLD); + MESSAGE("Foe Gastrodon's Sticky Hold made Thief ineffective!"); + } +} + diff --git a/test/battle/ability/supreme_overlord.c b/test/battle/ability/supreme_overlord.c index fcaca96685..dcaaa494a0 100644 --- a/test/battle/ability/supreme_overlord.c +++ b/test/battle/ability/supreme_overlord.c @@ -107,9 +107,9 @@ SINGLE_BATTLE_TEST("Supreme Overlord's message displays correctly after all batt ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, opponent); // Everyone faints. SEND_IN_MESSAGE("Kingambit"); + MESSAGE("2 sent out Wobbuffet!"); ABILITY_POPUP(player, ABILITY_SUPREME_OVERLORD); MESSAGE("Kingambit gained strength from the fallen!"); - MESSAGE("2 sent out Wobbuffet!"); } } diff --git a/test/battle/ability/switch_in_abilities.c b/test/battle/ability/switch_in_abilities.c new file mode 100644 index 0000000000..79cf2b2dc9 --- /dev/null +++ b/test/battle/ability/switch_in_abilities.c @@ -0,0 +1,128 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Switch-in abilities trigger in Speed Order at the battle's start - Single Battle") +{ + u32 spdPlayer, spdOpponent; + + PARAMETRIZE { spdPlayer = 5; spdOpponent = 1; } + PARAMETRIZE { spdOpponent = 5; spdPlayer = 1; } + + GIVEN { + PLAYER(SPECIES_EKANS) { Speed(spdPlayer); Ability(ABILITY_INTIMIDATE); } + OPPONENT(SPECIES_NINETALES) { Speed(spdOpponent); Ability(ABILITY_DROUGHT); } + } WHEN { + TURN { ; } + } SCENE { + if (spdPlayer > spdOpponent) { + ABILITY_POPUP(player, ABILITY_INTIMIDATE); + ABILITY_POPUP(opponent, ABILITY_DROUGHT); + } else { + ABILITY_POPUP(opponent, ABILITY_DROUGHT); + ABILITY_POPUP(player, ABILITY_INTIMIDATE); + } + } +} + +DOUBLE_BATTLE_TEST("Switch-in abilities trigger in Speed Order at the battle's start - Double Battle") +{ + u32 spdPlayer1, spdPlayer2, spdOpponent1, spdOpponent2; + + PARAMETRIZE { spdPlayer1 = 5; spdPlayer2 = 4; spdOpponent1 = 3; spdOpponent2 = 2; } + PARAMETRIZE { spdPlayer1 = 2; spdPlayer2 = 3; spdOpponent1 = 4; spdOpponent2 = 5; } + PARAMETRIZE { spdPlayer1 = 4; spdPlayer2 = 3; spdOpponent1 = 5; spdOpponent2 = 2; } + + GIVEN { + PLAYER(SPECIES_KYOGRE) { Speed(spdPlayer1); Ability(ABILITY_DRIZZLE); } + PLAYER(SPECIES_GYARADOS) { Speed(spdPlayer2); Ability(ABILITY_INTIMIDATE); } + OPPONENT(SPECIES_PORYGON2) { Speed(spdOpponent1); Ability(ABILITY_DOWNLOAD); } + OPPONENT(SPECIES_PINSIR) { Speed(spdOpponent2); Ability(ABILITY_MOLD_BREAKER); } + } WHEN { + TURN { ; } + } SCENE { + if (spdPlayer1 == 5) { + ABILITY_POPUP(playerLeft, ABILITY_DRIZZLE); + ABILITY_POPUP(playerRight, ABILITY_INTIMIDATE); + ABILITY_POPUP(opponentLeft, ABILITY_DOWNLOAD); + ABILITY_POPUP(opponentRight, ABILITY_MOLD_BREAKER); + } else if (spdOpponent2 == 5) { + ABILITY_POPUP(opponentRight, ABILITY_MOLD_BREAKER); + ABILITY_POPUP(opponentLeft, ABILITY_DOWNLOAD); + ABILITY_POPUP(playerRight, ABILITY_INTIMIDATE); + ABILITY_POPUP(playerLeft, ABILITY_DRIZZLE); + } else { + ABILITY_POPUP(opponentLeft, ABILITY_DOWNLOAD); + ABILITY_POPUP(playerLeft, ABILITY_DRIZZLE); + ABILITY_POPUP(playerRight, ABILITY_INTIMIDATE); + ABILITY_POPUP(opponentRight, ABILITY_MOLD_BREAKER); + } + } +} + +SINGLE_BATTLE_TEST("Switch-in abilities trigger in Speed Order after post-KO switch - Single Battle") +{ + u32 spdPlayer, spdOpponent; + + PARAMETRIZE { spdPlayer = 5; spdOpponent = 1; } + PARAMETRIZE { spdOpponent = 5; spdPlayer = 1; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { HP(1); Speed(1); } + PLAYER(SPECIES_EKANS) { Speed(spdPlayer); Ability(ABILITY_INTIMIDATE); } + OPPONENT(SPECIES_WOBBUFFET) { HP(1); Speed(1); } + OPPONENT(SPECIES_PORYGON2) { Speed(spdOpponent); Ability(ABILITY_DOWNLOAD); } + } WHEN { + TURN { MOVE(player, MOVE_EXPLOSION); SEND_OUT(player, 1); SEND_OUT(opponent, 1); } + TURN { ; } + } SCENE { + MESSAGE("Wobbuffet used Explosion!"); + if (spdPlayer > spdOpponent) { + ABILITY_POPUP(player, ABILITY_INTIMIDATE); + ABILITY_POPUP(opponent, ABILITY_DOWNLOAD); + } else { + ABILITY_POPUP(opponent, ABILITY_DOWNLOAD); + ABILITY_POPUP(player, ABILITY_INTIMIDATE); + } + } +} + +DOUBLE_BATTLE_TEST("Switch-in abilities trigger in Speed Order after post-KO switch - Double Battle") +{ + u32 spdPlayer1, spdPlayer2, spdOpponent1, spdOpponent2; + + PARAMETRIZE { spdPlayer1 = 5; spdPlayer2 = 4; spdOpponent1 = 3; spdOpponent2 = 2; } + PARAMETRIZE { spdPlayer1 = 2; spdPlayer2 = 3; spdOpponent1 = 4; spdOpponent2 = 5; } + PARAMETRIZE { spdPlayer1 = 4; spdPlayer2 = 3; spdOpponent1 = 5; spdOpponent2 = 2; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { HP(1); Speed(1); } + PLAYER(SPECIES_WOBBUFFET) { HP(1); Speed(1); } + PLAYER(SPECIES_TYRANITAR) { Speed(spdPlayer1); Ability(ABILITY_SAND_STREAM); } + PLAYER(SPECIES_GYARADOS) { Speed(spdPlayer2); Ability(ABILITY_INTIMIDATE); } + OPPONENT(SPECIES_WOBBUFFET) { HP(1); Speed(1); } + OPPONENT(SPECIES_WOBBUFFET) { HP(1); Speed(1); } + OPPONENT(SPECIES_WEEZING_GALARIAN) { Speed(spdOpponent1); Ability(ABILITY_MISTY_SURGE); } + OPPONENT(SPECIES_VULPIX_ALOLAN) { Speed(spdOpponent2); Ability(ABILITY_SNOW_WARNING); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_EXPLOSION); SEND_OUT(playerLeft, 2); SEND_OUT(opponentLeft, 2); SEND_OUT(playerRight, 3); SEND_OUT(opponentRight, 3); } + TURN { ; } + } SCENE { + MESSAGE("Wobbuffet used Explosion!"); + if (spdPlayer1 == 5) { + ABILITY_POPUP(playerLeft, ABILITY_SAND_STREAM); + ABILITY_POPUP(playerRight, ABILITY_INTIMIDATE); + ABILITY_POPUP(opponentLeft, ABILITY_MISTY_SURGE); + ABILITY_POPUP(opponentRight, ABILITY_SNOW_WARNING); + } else if (spdOpponent2 == 5) { + ABILITY_POPUP(opponentRight, ABILITY_SNOW_WARNING); + ABILITY_POPUP(opponentLeft, ABILITY_MISTY_SURGE); + ABILITY_POPUP(playerRight, ABILITY_INTIMIDATE); + ABILITY_POPUP(playerLeft, ABILITY_SAND_STREAM); + } else { + ABILITY_POPUP(opponentLeft, ABILITY_MISTY_SURGE); + ABILITY_POPUP(playerLeft, ABILITY_SAND_STREAM); + ABILITY_POPUP(playerRight, ABILITY_INTIMIDATE); + ABILITY_POPUP(opponentRight, ABILITY_SNOW_WARNING); + } + } +} diff --git a/test/battle/ability/sword_of_ruin.c b/test/battle/ability/sword_of_ruin.c index ae3e4c73a9..02eec69ed8 100644 --- a/test/battle/ability/sword_of_ruin.c +++ b/test/battle/ability/sword_of_ruin.c @@ -46,9 +46,9 @@ SINGLE_BATTLE_TEST("Sword of Ruin's message displays correctly after all battler ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, opponent); // Everyone faints. MESSAGE("Go! Chien-Pao!"); + MESSAGE("2 sent out Wobbuffet!"); ABILITY_POPUP(player, ABILITY_SWORD_OF_RUIN); MESSAGE("Chien-Pao's Sword of Ruin weakened the Defense of all surrounding Pokémon!"); - MESSAGE("2 sent out Wobbuffet!"); } } diff --git a/test/battle/ability/tablets_of_ruin.c b/test/battle/ability/tablets_of_ruin.c index c8d24f5d92..7862a0bb8c 100644 --- a/test/battle/ability/tablets_of_ruin.c +++ b/test/battle/ability/tablets_of_ruin.c @@ -46,9 +46,9 @@ SINGLE_BATTLE_TEST("Tablets of Ruin's message displays correctly after all battl ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, opponent); // Everyone faints. MESSAGE("Go! Wo-Chien!"); + MESSAGE("2 sent out Wobbuffet!"); ABILITY_POPUP(player, ABILITY_TABLETS_OF_RUIN); MESSAGE("Wo-Chien's Tablets of Ruin weakened the Attack of all surrounding Pokémon!"); - MESSAGE("2 sent out Wobbuffet!"); } } diff --git a/test/battle/ability/vessel_of_ruin.c b/test/battle/ability/vessel_of_ruin.c index 723c83bdf9..362b44c003 100644 --- a/test/battle/ability/vessel_of_ruin.c +++ b/test/battle/ability/vessel_of_ruin.c @@ -46,9 +46,9 @@ SINGLE_BATTLE_TEST("Vessel of Ruin's message displays correctly after all battle ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, opponent); // Everyone faints. MESSAGE("Go! Ting-Lu!"); + MESSAGE("2 sent out Wobbuffet!"); ABILITY_POPUP(player, ABILITY_VESSEL_OF_RUIN); MESSAGE("Ting-Lu's Vessel of Ruin weakened the Sp. Atk of all surrounding Pokémon!"); - MESSAGE("2 sent out Wobbuffet!"); } } diff --git a/test/battle/ability/white_smoke.c b/test/battle/ability/white_smoke.c new file mode 100644 index 0000000000..4cb1687141 --- /dev/null +++ b/test/battle/ability/white_smoke.c @@ -0,0 +1,4 @@ +#include "global.h" +#include "test/battle.h" + +// Tests for White Smoke are handled in test/battle/ability/clear_body.c diff --git a/test/battle/ability/zero_to_hero.c b/test/battle/ability/zero_to_hero.c index 1fd8e9c7e1..be72a55a7e 100644 --- a/test/battle/ability/zero_to_hero.c +++ b/test/battle/ability/zero_to_hero.c @@ -153,9 +153,9 @@ SINGLE_BATTLE_TEST("Zero to Hero's message displays correctly after all battlers ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, opponent); // Everyone faints. SEND_IN_MESSAGE("Palafin"); + MESSAGE("2 sent out Wobbuffet!"); ABILITY_POPUP(player, ABILITY_ZERO_TO_HERO); MESSAGE("Palafin underwent a heroic transformation!"); - MESSAGE("2 sent out Wobbuffet!"); } } diff --git a/test/battle/ai/ai.c b/test/battle/ai/ai.c index 48eee5c051..a19481c7ed 100644 --- a/test/battle/ai/ai.c +++ b/test/battle/ai/ai.c @@ -402,9 +402,9 @@ AI_DOUBLE_BATTLE_TEST("AI will not use Helping Hand if partner does not have any { u16 move1 = MOVE_NONE, move2 = MOVE_NONE, move3 = MOVE_NONE, move4 = MOVE_NONE; - PARAMETRIZE{ move1 = MOVE_LEER; move2 = MOVE_TOXIC; } - PARAMETRIZE{ move1 = MOVE_HELPING_HAND; move2 = MOVE_PROTECT; } - PARAMETRIZE{ move1 = MOVE_ACUPRESSURE; move2 = MOVE_DOUBLE_TEAM; move3 = MOVE_TOXIC; move4 = MOVE_PROTECT; } + PARAMETRIZE { move1 = MOVE_LEER; move2 = MOVE_TOXIC; } + PARAMETRIZE { move1 = MOVE_HELPING_HAND; move2 = MOVE_PROTECT; } + PARAMETRIZE { move1 = MOVE_ACUPRESSURE; move2 = MOVE_DOUBLE_TEAM; move3 = MOVE_TOXIC; move4 = MOVE_PROTECT; } GIVEN { AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); @@ -431,7 +431,7 @@ AI_DOUBLE_BATTLE_TEST("AI will not use a status move if partner already chose He for (j = MOVE_NONE + 1; j < MOVES_COUNT; j++) { if (gMovesInfo[j].category == DAMAGE_CATEGORY_STATUS) { - PARAMETRIZE{ statusMove = j; } + PARAMETRIZE { statusMove = j; } } } diff --git a/test/battle/ai/ai_choice.c b/test/battle/ai/ai_choice.c index a589a24d7c..c9992d2cab 100644 --- a/test/battle/ai/ai_choice.c +++ b/test/battle/ai/ai_choice.c @@ -20,8 +20,8 @@ AI_SINGLE_BATTLE_TEST("Choiced Pokémon switch out after using a status move onc for (j = 0; j < ARRAY_COUNT(choiceItems); j++) { - PARAMETRIZE{ ability = ABILITY_NONE; heldItem = choiceItems[j]; } - PARAMETRIZE{ ability = ABILITY_KLUTZ; heldItem = choiceItems[j]; } + PARAMETRIZE { ability = ABILITY_NONE; heldItem = choiceItems[j]; } + PARAMETRIZE { ability = ABILITY_KLUTZ; heldItem = choiceItems[j]; } } GIVEN { @@ -55,8 +55,8 @@ AI_SINGLE_BATTLE_TEST("Choiced Pokémon won't use stat boosting moves") for (j = 0; j < ARRAY_COUNT(choiceItems); j++) { - PARAMETRIZE{ ability = ABILITY_NONE; heldItem = choiceItems[j]; } - PARAMETRIZE{ ability = ABILITY_KLUTZ; heldItem = choiceItems[j]; } + PARAMETRIZE { ability = ABILITY_NONE; heldItem = choiceItems[j]; } + PARAMETRIZE { ability = ABILITY_KLUTZ; heldItem = choiceItems[j]; } } GIVEN { @@ -87,10 +87,10 @@ AI_SINGLE_BATTLE_TEST("Choiced Pokémon won't use status move if they are the on for (j = 0; j < ARRAY_COUNT(choiceItems); j++) { - PARAMETRIZE{ ability = ABILITY_NONE; heldItem = choiceItems[j]; isAlive = 0; } - PARAMETRIZE{ ability = ABILITY_KLUTZ; heldItem = choiceItems[j]; isAlive = 0; } - PARAMETRIZE{ ability = ABILITY_NONE; heldItem = choiceItems[j]; isAlive = 1; } - PARAMETRIZE{ ability = ABILITY_KLUTZ; heldItem = choiceItems[j]; isAlive = 1; } + PARAMETRIZE { ability = ABILITY_NONE; heldItem = choiceItems[j]; isAlive = 0; } + PARAMETRIZE { ability = ABILITY_KLUTZ; heldItem = choiceItems[j]; isAlive = 0; } + PARAMETRIZE { ability = ABILITY_NONE; heldItem = choiceItems[j]; isAlive = 1; } + PARAMETRIZE { ability = ABILITY_KLUTZ; heldItem = choiceItems[j]; isAlive = 1; } } GIVEN { @@ -122,10 +122,10 @@ AI_SINGLE_BATTLE_TEST("Choiced Pokémon won't use status move if they don't have for (j = 0; j < ARRAY_COUNT(choiceItems); j++) { - PARAMETRIZE{ ability = ABILITY_NONE; heldItem = choiceItems[j]; species = SPECIES_SWAMPERT; move = MOVE_WATERFALL; } - PARAMETRIZE{ ability = ABILITY_KLUTZ; heldItem = choiceItems[j]; species = SPECIES_SWAMPERT; move = MOVE_WATERFALL; } - PARAMETRIZE{ ability = ABILITY_NONE; heldItem = choiceItems[j]; species = SPECIES_ELEKID; move = MOVE_THUNDER_WAVE; } - PARAMETRIZE{ ability = ABILITY_KLUTZ; heldItem = choiceItems[j]; species = SPECIES_ELEKID; move = MOVE_THUNDER_WAVE; } + PARAMETRIZE { ability = ABILITY_NONE; heldItem = choiceItems[j]; species = SPECIES_SWAMPERT; move = MOVE_WATERFALL; } + PARAMETRIZE { ability = ABILITY_KLUTZ; heldItem = choiceItems[j]; species = SPECIES_SWAMPERT; move = MOVE_WATERFALL; } + PARAMETRIZE { ability = ABILITY_NONE; heldItem = choiceItems[j]; species = SPECIES_ELEKID; move = MOVE_THUNDER_WAVE; } + PARAMETRIZE { ability = ABILITY_KLUTZ; heldItem = choiceItems[j]; species = SPECIES_ELEKID; move = MOVE_THUNDER_WAVE; } } GIVEN { @@ -157,10 +157,10 @@ AI_SINGLE_BATTLE_TEST("Choiced Pokémon won't use status move if they are trappe for (j = 0; j < ARRAY_COUNT(choiceItems); j++) { - PARAMETRIZE{ aiAbility = ABILITY_NONE; heldItem = choiceItems[j]; species = SPECIES_RHYDON; playerAbility = ABILITY_LIGHTNING_ROD; } - PARAMETRIZE{ aiAbility = ABILITY_KLUTZ; heldItem = choiceItems[j]; species = SPECIES_RHYDON; playerAbility = ABILITY_LIGHTNING_ROD; } - PARAMETRIZE{ aiAbility = ABILITY_NONE; heldItem = choiceItems[j]; species = SPECIES_DUGTRIO; playerAbility = ABILITY_ARENA_TRAP; } - PARAMETRIZE{ aiAbility = ABILITY_KLUTZ; heldItem = choiceItems[j]; species = SPECIES_DUGTRIO; playerAbility = ABILITY_ARENA_TRAP; } + PARAMETRIZE { aiAbility = ABILITY_NONE; heldItem = choiceItems[j]; species = SPECIES_RHYDON; playerAbility = ABILITY_LIGHTNING_ROD; } + PARAMETRIZE { aiAbility = ABILITY_KLUTZ; heldItem = choiceItems[j]; species = SPECIES_RHYDON; playerAbility = ABILITY_LIGHTNING_ROD; } + PARAMETRIZE { aiAbility = ABILITY_NONE; heldItem = choiceItems[j]; species = SPECIES_DUGTRIO; playerAbility = ABILITY_ARENA_TRAP; } + PARAMETRIZE { aiAbility = ABILITY_KLUTZ; heldItem = choiceItems[j]; species = SPECIES_DUGTRIO; playerAbility = ABILITY_ARENA_TRAP; } } GIVEN { diff --git a/test/battle/ai/ai_flag_risky.c b/test/battle/ai/ai_flag_risky.c index 4c5d89205a..e6156de5a0 100644 --- a/test/battle/ai/ai_flag_risky.c +++ b/test/battle/ai/ai_flag_risky.c @@ -5,8 +5,8 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_RISKY: AI will blindly Mirror Coat against specia { u32 aiRiskyFlag = 0; - PARAMETRIZE{ aiRiskyFlag = 0; } - PARAMETRIZE{ aiRiskyFlag = AI_FLAG_RISKY; } + PARAMETRIZE { aiRiskyFlag = 0; } + PARAMETRIZE { aiRiskyFlag = AI_FLAG_RISKY; } GIVEN { ASSUME(gMovesInfo[MOVE_MIRROR_COAT].effect == EFFECT_MIRROR_COAT); @@ -24,8 +24,8 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_RISKY: AI will blindly Counter against physical a { u32 aiRiskyFlag = 0; - PARAMETRIZE{ aiRiskyFlag = 0; } - PARAMETRIZE{ aiRiskyFlag = AI_FLAG_RISKY; } + PARAMETRIZE { aiRiskyFlag = 0; } + PARAMETRIZE { aiRiskyFlag = AI_FLAG_RISKY; } GIVEN { ASSUME(gMovesInfo[MOVE_COUNTER].effect == EFFECT_COUNTER); @@ -43,8 +43,8 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_RISKY: AI will prioritize Revenge if slower") { u32 aiRiskyFlag = 0; - PARAMETRIZE{ aiRiskyFlag = 0; } - PARAMETRIZE{ aiRiskyFlag = AI_FLAG_RISKY; } + PARAMETRIZE { aiRiskyFlag = 0; } + PARAMETRIZE { aiRiskyFlag = AI_FLAG_RISKY; } GIVEN { ASSUME(gMovesInfo[MOVE_REVENGE].effect == EFFECT_REVENGE); @@ -60,8 +60,8 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_RISKY: Mid-battle switches prioritize offensive o { u32 aiRiskyFlag = 0; - PARAMETRIZE{ aiRiskyFlag = 0; } - PARAMETRIZE{ aiRiskyFlag = AI_FLAG_RISKY; } + PARAMETRIZE { aiRiskyFlag = 0; } + PARAMETRIZE { aiRiskyFlag = AI_FLAG_RISKY; } GIVEN { AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_MON_CHOICES | aiRiskyFlag); @@ -78,8 +78,8 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_RISKY: AI prefers high damage moves at the expens { u32 aiRiskyFlag = 0; - PARAMETRIZE{ aiRiskyFlag = 0; } - PARAMETRIZE{ aiRiskyFlag = AI_FLAG_RISKY; } + PARAMETRIZE { aiRiskyFlag = 0; } + PARAMETRIZE { aiRiskyFlag = AI_FLAG_RISKY; } GIVEN { AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiRiskyFlag); diff --git a/test/battle/ai/ai_switching.c b/test/battle/ai/ai_switching.c index 50b59f66d1..63a9c92248 100644 --- a/test/battle/ai/ai_switching.c +++ b/test/battle/ai/ai_switching.c @@ -127,9 +127,9 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: AI will not switch in a Pokemo u32 speedAlakazm; u32 aiSmartSwitchFlags = 0; - PARAMETRIZE{ speedAlakazm = 200; alakazamFirst = TRUE; } // AI will always send out Alakazan as it sees a KO with Focus Blast, even if Alakazam dies before it can get it off - PARAMETRIZE{ speedAlakazm = 200; alakazamFirst = FALSE; aiSmartSwitchFlags = AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES; } // AI_FLAG_SMART_MON_CHOICES lets AI see that Alakazam would be KO'd before it can KO, and won't switch it in - PARAMETRIZE{ speedAlakazm = 400; alakazamFirst = TRUE; aiSmartSwitchFlags = AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES; } // AI_FLAG_SMART_MON_CHOICES recognizes that Alakazam is faster and can KO, and will switch it in + PARAMETRIZE { speedAlakazm = 200; alakazamFirst = TRUE; } // AI will always send out Alakazan as it sees a KO with Focus Blast, even if Alakazam dies before it can get it off + PARAMETRIZE { speedAlakazm = 200; alakazamFirst = FALSE; aiSmartSwitchFlags = AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES; } // AI_FLAG_SMART_MON_CHOICES lets AI see that Alakazam would be KO'd before it can KO, and won't switch it in + PARAMETRIZE { speedAlakazm = 400; alakazamFirst = TRUE; aiSmartSwitchFlags = AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES; } // AI_FLAG_SMART_MON_CHOICES recognizes that Alakazam is faster and can KO, and will switch it in GIVEN { ASSUME(gMovesInfo[MOVE_PSYCHIC].category == DAMAGE_CATEGORY_SPECIAL); @@ -159,8 +159,8 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: AI considers hazard damage whe u32 aiIsSmart = 0; u32 aiSmartSwitchFlags = 0; - PARAMETRIZE{ aiIsSmart = 0; aiSmartSwitchFlags = 0; } // AI doesn't care about hazard damage resulting in Pokemon being KO'd - PARAMETRIZE{ aiIsSmart = 1; aiSmartSwitchFlags = AI_FLAG_SMART_MON_CHOICES; } // AI_FLAG_SMART_MON_CHOICES avoids being KO'd as a result of hazards damage + PARAMETRIZE { aiIsSmart = 0; aiSmartSwitchFlags = 0; } // AI doesn't care about hazard damage resulting in Pokemon being KO'd + PARAMETRIZE { aiIsSmart = 1; aiSmartSwitchFlags = AI_FLAG_SMART_MON_CHOICES; } // AI_FLAG_SMART_MON_CHOICES avoids being KO'd as a result of hazards damage GIVEN { AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiSmartSwitchFlags); @@ -181,10 +181,10 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Mid-battle switches prioritize u32 move2; u32 expectedIndex; - PARAMETRIZE{ expectedIndex = 3; move1 = MOVE_TACKLE; move2 = MOVE_TACKLE; aiSmartSwitchFlags = 0; } // When not smart, AI will only switch in a defensive mon if it has a SE move, otherwise will just default to damage - PARAMETRIZE{ expectedIndex = 1; move1 = MOVE_GIGA_DRAIN; move2 = MOVE_TACKLE; aiSmartSwitchFlags = 0; } - PARAMETRIZE{ expectedIndex = 2; move1 = MOVE_TACKLE; move2 = MOVE_TACKLE; aiSmartSwitchFlags = AI_FLAG_SMART_MON_CHOICES; } // When smart, AI will prioritize SE move, but still switch in good type matchup without SE move - PARAMETRIZE{ expectedIndex = 1; move1 = MOVE_GIGA_DRAIN; move2 = MOVE_TACKLE; aiSmartSwitchFlags = AI_FLAG_SMART_MON_CHOICES; } + PARAMETRIZE { expectedIndex = 3; move1 = MOVE_TACKLE; move2 = MOVE_TACKLE; aiSmartSwitchFlags = 0; } // When not smart, AI will only switch in a defensive mon if it has a SE move, otherwise will just default to damage + PARAMETRIZE { expectedIndex = 1; move1 = MOVE_GIGA_DRAIN; move2 = MOVE_TACKLE; aiSmartSwitchFlags = 0; } + PARAMETRIZE { expectedIndex = 2; move1 = MOVE_TACKLE; move2 = MOVE_TACKLE; aiSmartSwitchFlags = AI_FLAG_SMART_MON_CHOICES; } // When smart, AI will prioritize SE move, but still switch in good type matchup without SE move + PARAMETRIZE { expectedIndex = 1; move1 = MOVE_GIGA_DRAIN; move2 = MOVE_TACKLE; aiSmartSwitchFlags = AI_FLAG_SMART_MON_CHOICES; } GIVEN { AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | aiSmartSwitchFlags); @@ -257,8 +257,8 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will not switch out if Pokemo { u32 move1; - PARAMETRIZE{ move1 = MOVE_TACKLE; } - PARAMETRIZE{ move1 = MOVE_RAPID_SPIN; } + PARAMETRIZE { move1 = MOVE_TACKLE; } + PARAMETRIZE { move1 = MOVE_RAPID_SPIN; } GIVEN { ASSUME(gMovesInfo[MOVE_TACKLE].category == DAMAGE_CATEGORY_PHYSICAL); diff --git a/test/battle/form_change/mega_evolution.c b/test/battle/form_change/mega_evolution.c index 4a2c053057..ffab6af18b 100644 --- a/test/battle/form_change/mega_evolution.c +++ b/test/battle/form_change/mega_evolution.c @@ -153,3 +153,26 @@ SINGLE_BATTLE_TEST("Regular Mega Evolution and Fervent Wish Mega Evolution can h EXPECT_EQ(opponent->species, SPECIES_GARDEVOIR_MEGA); } } + +SINGLE_BATTLE_TEST("Mega Evolved Pokemon do not change abilities after fainting") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_CRUNCH].makesContact == TRUE); + ASSUME(gSpeciesInfo[SPECIES_GARCHOMP_MEGA].abilities[0] != ABILITY_ROUGH_SKIN); + ASSUME(gSpeciesInfo[SPECIES_GARCHOMP_MEGA].abilities[1] != ABILITY_ROUGH_SKIN); + ASSUME(gSpeciesInfo[SPECIES_GARCHOMP_MEGA].abilities[2] != ABILITY_ROUGH_SKIN); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_GARCHOMP) { Ability(ABILITY_ROUGH_SKIN); Item(ITEM_GARCHOMPITE); HP(1); } + } WHEN { + TURN { MOVE(player, MOVE_CRUNCH); MOVE(opponent, MOVE_CELEBRATE, gimmick: GIMMICK_MEGA); } + } SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CRUNCH, player); + MESSAGE("Foe Garchomp fainted!"); + NONE_OF { + ABILITY_POPUP(opponent, ABILITY_ROUGH_SKIN); + MESSAGE("Wobbuffet was hurt by Foe Garchomp's Rough Skin!"); + HP_BAR(player); + } + } +} diff --git a/test/battle/hold_effect/cure_status.c b/test/battle/hold_effect/cure_status.c index 93921ce9c1..182069aaf5 100644 --- a/test/battle/hold_effect/cure_status.c +++ b/test/battle/hold_effect/cure_status.c @@ -163,12 +163,12 @@ SINGLE_BATTLE_TEST("Berry hold effect cures status if a pokemon enters a battle" u16 status; u16 item; - PARAMETRIZE{ status = STATUS1_BURN; item = ITEM_RAWST_BERRY; } - PARAMETRIZE{ status = STATUS1_FREEZE; item = ITEM_ASPEAR_BERRY; } - PARAMETRIZE{ status = STATUS1_PARALYSIS; item = ITEM_CHERI_BERRY; } - PARAMETRIZE{ status = STATUS1_POISON; item = ITEM_PECHA_BERRY; } - PARAMETRIZE{ status = STATUS1_TOXIC_POISON; item = ITEM_PECHA_BERRY; } - PARAMETRIZE{ status = STATUS1_SLEEP; item = ITEM_CHESTO_BERRY; } + PARAMETRIZE { status = STATUS1_BURN; item = ITEM_RAWST_BERRY; } + PARAMETRIZE { status = STATUS1_FREEZE; item = ITEM_ASPEAR_BERRY; } + PARAMETRIZE { status = STATUS1_PARALYSIS; item = ITEM_CHERI_BERRY; } + PARAMETRIZE { status = STATUS1_POISON; item = ITEM_PECHA_BERRY; } + PARAMETRIZE { status = STATUS1_TOXIC_POISON; item = ITEM_PECHA_BERRY; } + PARAMETRIZE { status = STATUS1_SLEEP; item = ITEM_CHESTO_BERRY; } GIVEN { ASSUME(gItemsInfo[ITEM_RAWST_BERRY].holdEffect == HOLD_EFFECT_CURE_BRN); diff --git a/test/battle/item_effect/cure_status.c b/test/battle/item_effect/cure_status.c index f73bbae327..6c7cc2e64d 100644 --- a/test/battle/item_effect/cure_status.c +++ b/test/battle/item_effect/cure_status.c @@ -112,12 +112,12 @@ SINGLE_BATTLE_TEST("Ice Heal heals a battler from being frozen") SINGLE_BATTLE_TEST("Full Heal heals a battler from any primary status") { u16 status; - PARAMETRIZE{ status = STATUS1_BURN; } - PARAMETRIZE{ status = STATUS1_FREEZE; } - PARAMETRIZE{ status = STATUS1_PARALYSIS; } - PARAMETRIZE{ status = STATUS1_POISON; } - PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } - PARAMETRIZE{ status = STATUS1_SLEEP; } + PARAMETRIZE { status = STATUS1_BURN; } + PARAMETRIZE { status = STATUS1_FREEZE; } + PARAMETRIZE { status = STATUS1_PARALYSIS; } + PARAMETRIZE { status = STATUS1_POISON; } + PARAMETRIZE { status = STATUS1_TOXIC_POISON; } + PARAMETRIZE { status = STATUS1_SLEEP; } GIVEN { ASSUME(gItemsInfo[ITEM_FULL_HEAL].battleUsage == EFFECT_ITEM_CURE_STATUS); PLAYER(SPECIES_WOBBUFFET) { Status1(status); } @@ -134,12 +134,12 @@ SINGLE_BATTLE_TEST("Full Heal heals a battler from any primary status") SINGLE_BATTLE_TEST("Heal Powder heals a battler from any primary status") { u16 status; - PARAMETRIZE{ status = STATUS1_BURN; } - PARAMETRIZE{ status = STATUS1_FREEZE; } - PARAMETRIZE{ status = STATUS1_PARALYSIS; } - PARAMETRIZE{ status = STATUS1_POISON; } - PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } - PARAMETRIZE{ status = STATUS1_SLEEP; } + PARAMETRIZE { status = STATUS1_BURN; } + PARAMETRIZE { status = STATUS1_FREEZE; } + PARAMETRIZE { status = STATUS1_PARALYSIS; } + PARAMETRIZE { status = STATUS1_POISON; } + PARAMETRIZE { status = STATUS1_TOXIC_POISON; } + PARAMETRIZE { status = STATUS1_SLEEP; } GIVEN { ASSUME(gItemsInfo[ITEM_HEAL_POWDER].battleUsage == EFFECT_ITEM_CURE_STATUS); PLAYER(SPECIES_WOBBUFFET) { Status1(status); } @@ -156,12 +156,12 @@ SINGLE_BATTLE_TEST("Heal Powder heals a battler from any primary status") SINGLE_BATTLE_TEST("Pewter Crunchies heals a battler from any primary status") { u16 status; - PARAMETRIZE{ status = STATUS1_BURN; } - PARAMETRIZE{ status = STATUS1_FREEZE; } - PARAMETRIZE{ status = STATUS1_PARALYSIS; } - PARAMETRIZE{ status = STATUS1_POISON; } - PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } - PARAMETRIZE{ status = STATUS1_SLEEP; } + PARAMETRIZE { status = STATUS1_BURN; } + PARAMETRIZE { status = STATUS1_FREEZE; } + PARAMETRIZE { status = STATUS1_PARALYSIS; } + PARAMETRIZE { status = STATUS1_POISON; } + PARAMETRIZE { status = STATUS1_TOXIC_POISON; } + PARAMETRIZE { status = STATUS1_SLEEP; } GIVEN { ASSUME(gItemsInfo[ITEM_PEWTER_CRUNCHIES].battleUsage == EFFECT_ITEM_CURE_STATUS); PLAYER(SPECIES_WOBBUFFET) { Status1(status); } @@ -178,12 +178,12 @@ SINGLE_BATTLE_TEST("Pewter Crunchies heals a battler from any primary status") SINGLE_BATTLE_TEST("Lava Cookies heals a battler from any primary status") { u16 status; - PARAMETRIZE{ status = STATUS1_BURN; } - PARAMETRIZE{ status = STATUS1_FREEZE; } - PARAMETRIZE{ status = STATUS1_PARALYSIS; } - PARAMETRIZE{ status = STATUS1_POISON; } - PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } - PARAMETRIZE{ status = STATUS1_SLEEP; } + PARAMETRIZE { status = STATUS1_BURN; } + PARAMETRIZE { status = STATUS1_FREEZE; } + PARAMETRIZE { status = STATUS1_PARALYSIS; } + PARAMETRIZE { status = STATUS1_POISON; } + PARAMETRIZE { status = STATUS1_TOXIC_POISON; } + PARAMETRIZE { status = STATUS1_SLEEP; } GIVEN { ASSUME(gItemsInfo[ITEM_LAVA_COOKIE].battleUsage == EFFECT_ITEM_CURE_STATUS); PLAYER(SPECIES_WOBBUFFET) { Status1(status); } @@ -200,12 +200,12 @@ SINGLE_BATTLE_TEST("Lava Cookies heals a battler from any primary status") SINGLE_BATTLE_TEST("Rage Candy Bar heals a battler from any primary status") { u16 status; - PARAMETRIZE{ status = STATUS1_BURN; } - PARAMETRIZE{ status = STATUS1_FREEZE; } - PARAMETRIZE{ status = STATUS1_PARALYSIS; } - PARAMETRIZE{ status = STATUS1_POISON; } - PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } - PARAMETRIZE{ status = STATUS1_SLEEP; } + PARAMETRIZE { status = STATUS1_BURN; } + PARAMETRIZE { status = STATUS1_FREEZE; } + PARAMETRIZE { status = STATUS1_PARALYSIS; } + PARAMETRIZE { status = STATUS1_POISON; } + PARAMETRIZE { status = STATUS1_TOXIC_POISON; } + PARAMETRIZE { status = STATUS1_SLEEP; } GIVEN { ASSUME(gItemsInfo[ITEM_RAGE_CANDY_BAR].battleUsage == EFFECT_ITEM_CURE_STATUS); PLAYER(SPECIES_WOBBUFFET) { Status1(status); } @@ -222,12 +222,12 @@ SINGLE_BATTLE_TEST("Rage Candy Bar heals a battler from any primary status") SINGLE_BATTLE_TEST("Old Gateu heals a battler from any primary status") { u16 status; - PARAMETRIZE{ status = STATUS1_BURN; } - PARAMETRIZE{ status = STATUS1_FREEZE; } - PARAMETRIZE{ status = STATUS1_PARALYSIS; } - PARAMETRIZE{ status = STATUS1_POISON; } - PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } - PARAMETRIZE{ status = STATUS1_SLEEP; } + PARAMETRIZE { status = STATUS1_BURN; } + PARAMETRIZE { status = STATUS1_FREEZE; } + PARAMETRIZE { status = STATUS1_PARALYSIS; } + PARAMETRIZE { status = STATUS1_POISON; } + PARAMETRIZE { status = STATUS1_TOXIC_POISON; } + PARAMETRIZE { status = STATUS1_SLEEP; } GIVEN { ASSUME(gItemsInfo[ITEM_OLD_GATEAU].battleUsage == EFFECT_ITEM_CURE_STATUS); PLAYER(SPECIES_WOBBUFFET) { Status1(status); } @@ -244,12 +244,12 @@ SINGLE_BATTLE_TEST("Old Gateu heals a battler from any primary status") SINGLE_BATTLE_TEST("Casteliacone heals a battler from any primary status") { u16 status; - PARAMETRIZE{ status = STATUS1_BURN; } - PARAMETRIZE{ status = STATUS1_FREEZE; } - PARAMETRIZE{ status = STATUS1_PARALYSIS; } - PARAMETRIZE{ status = STATUS1_POISON; } - PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } - PARAMETRIZE{ status = STATUS1_SLEEP; } + PARAMETRIZE { status = STATUS1_BURN; } + PARAMETRIZE { status = STATUS1_FREEZE; } + PARAMETRIZE { status = STATUS1_PARALYSIS; } + PARAMETRIZE { status = STATUS1_POISON; } + PARAMETRIZE { status = STATUS1_TOXIC_POISON; } + PARAMETRIZE { status = STATUS1_SLEEP; } GIVEN { ASSUME(gItemsInfo[ITEM_CASTELIACONE].battleUsage == EFFECT_ITEM_CURE_STATUS); PLAYER(SPECIES_WOBBUFFET) { Status1(status); } @@ -266,12 +266,12 @@ SINGLE_BATTLE_TEST("Casteliacone heals a battler from any primary status") SINGLE_BATTLE_TEST("Lumiose Galette heals a battler from any primary status") { u16 status; - PARAMETRIZE{ status = STATUS1_BURN; } - PARAMETRIZE{ status = STATUS1_FREEZE; } - PARAMETRIZE{ status = STATUS1_PARALYSIS; } - PARAMETRIZE{ status = STATUS1_POISON; } - PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } - PARAMETRIZE{ status = STATUS1_SLEEP; } + PARAMETRIZE { status = STATUS1_BURN; } + PARAMETRIZE { status = STATUS1_FREEZE; } + PARAMETRIZE { status = STATUS1_PARALYSIS; } + PARAMETRIZE { status = STATUS1_POISON; } + PARAMETRIZE { status = STATUS1_TOXIC_POISON; } + PARAMETRIZE { status = STATUS1_SLEEP; } GIVEN { ASSUME(gItemsInfo[ITEM_LUMIOSE_GALETTE].battleUsage == EFFECT_ITEM_CURE_STATUS); PLAYER(SPECIES_WOBBUFFET) { Status1(status); } @@ -288,12 +288,12 @@ SINGLE_BATTLE_TEST("Lumiose Galette heals a battler from any primary status") SINGLE_BATTLE_TEST("Shalour Sable heals a battler from any primary status") { u16 status; - PARAMETRIZE{ status = STATUS1_BURN; } - PARAMETRIZE{ status = STATUS1_FREEZE; } - PARAMETRIZE{ status = STATUS1_PARALYSIS; } - PARAMETRIZE{ status = STATUS1_POISON; } - PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } - PARAMETRIZE{ status = STATUS1_SLEEP; } + PARAMETRIZE { status = STATUS1_BURN; } + PARAMETRIZE { status = STATUS1_FREEZE; } + PARAMETRIZE { status = STATUS1_PARALYSIS; } + PARAMETRIZE { status = STATUS1_POISON; } + PARAMETRIZE { status = STATUS1_TOXIC_POISON; } + PARAMETRIZE { status = STATUS1_SLEEP; } GIVEN { ASSUME(gItemsInfo[ITEM_SHALOUR_SABLE].battleUsage == EFFECT_ITEM_CURE_STATUS); PLAYER(SPECIES_WOBBUFFET) { Status1(status); } @@ -310,12 +310,12 @@ SINGLE_BATTLE_TEST("Shalour Sable heals a battler from any primary status") SINGLE_BATTLE_TEST("Big Malasada heals a battler from any primary status") { u16 status; - PARAMETRIZE{ status = STATUS1_BURN; } - PARAMETRIZE{ status = STATUS1_FREEZE; } - PARAMETRIZE{ status = STATUS1_PARALYSIS; } - PARAMETRIZE{ status = STATUS1_POISON; } - PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } - PARAMETRIZE{ status = STATUS1_SLEEP; } + PARAMETRIZE { status = STATUS1_BURN; } + PARAMETRIZE { status = STATUS1_FREEZE; } + PARAMETRIZE { status = STATUS1_PARALYSIS; } + PARAMETRIZE { status = STATUS1_POISON; } + PARAMETRIZE { status = STATUS1_TOXIC_POISON; } + PARAMETRIZE { status = STATUS1_SLEEP; } GIVEN { ASSUME(gItemsInfo[ITEM_BIG_MALASADA].battleUsage == EFFECT_ITEM_CURE_STATUS); PLAYER(SPECIES_WOBBUFFET) { Status1(status); } diff --git a/test/battle/item_effect/heal_and_cure_status.c b/test/battle/item_effect/heal_and_cure_status.c index 80719cf231..70dc8322db 100644 --- a/test/battle/item_effect/heal_and_cure_status.c +++ b/test/battle/item_effect/heal_and_cure_status.c @@ -9,13 +9,13 @@ ASSUMPTIONS SINGLE_BATTLE_TEST("Full Restore restores a battler's HP and cures any primary status") { u16 status; - PARAMETRIZE{ status = STATUS1_BURN; } - PARAMETRIZE{ status = STATUS1_FREEZE; } - PARAMETRIZE{ status = STATUS1_PARALYSIS; } - PARAMETRIZE{ status = STATUS1_POISON; } - PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } - PARAMETRIZE{ status = STATUS1_SLEEP; } - PARAMETRIZE{ status = STATUS1_NONE; } + PARAMETRIZE { status = STATUS1_BURN; } + PARAMETRIZE { status = STATUS1_FREEZE; } + PARAMETRIZE { status = STATUS1_PARALYSIS; } + PARAMETRIZE { status = STATUS1_POISON; } + PARAMETRIZE { status = STATUS1_TOXIC_POISON; } + PARAMETRIZE { status = STATUS1_SLEEP; } + PARAMETRIZE { status = STATUS1_NONE; } GIVEN { PLAYER(SPECIES_WOBBUFFET) { HP(1); MaxHP(300); Status1(status); } OPPONENT(SPECIES_WOBBUFFET); @@ -35,13 +35,13 @@ SINGLE_BATTLE_TEST("Full Restore restores a battler's HP and cures any primary s SINGLE_BATTLE_TEST("Full Restore restores a party members HP and cures any primary status") { u16 status; - PARAMETRIZE{ status = STATUS1_BURN; } - PARAMETRIZE{ status = STATUS1_FREEZE; } - PARAMETRIZE{ status = STATUS1_PARALYSIS; } - PARAMETRIZE{ status = STATUS1_POISON; } - PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } - PARAMETRIZE{ status = STATUS1_SLEEP; } - PARAMETRIZE{ status = STATUS1_NONE; } + PARAMETRIZE { status = STATUS1_BURN; } + PARAMETRIZE { status = STATUS1_FREEZE; } + PARAMETRIZE { status = STATUS1_PARALYSIS; } + PARAMETRIZE { status = STATUS1_POISON; } + PARAMETRIZE { status = STATUS1_TOXIC_POISON; } + PARAMETRIZE { status = STATUS1_SLEEP; } + PARAMETRIZE { status = STATUS1_NONE; } GIVEN { PLAYER(SPECIES_WOBBUFFET) { HP(1); MaxHP(300); Status1(status); } PLAYER(SPECIES_WYNAUT) { HP(1); MaxHP(300); Status1(status); } @@ -64,12 +64,12 @@ SINGLE_BATTLE_TEST("Full Restore restores a party members HP and cures any prima SINGLE_BATTLE_TEST("Full Restore heals a battler from any primary status") { u16 status; - PARAMETRIZE{ status = STATUS1_BURN; } - PARAMETRIZE{ status = STATUS1_FREEZE; } - PARAMETRIZE{ status = STATUS1_PARALYSIS; } - PARAMETRIZE{ status = STATUS1_POISON; } - PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } - PARAMETRIZE{ status = STATUS1_SLEEP; } + PARAMETRIZE { status = STATUS1_BURN; } + PARAMETRIZE { status = STATUS1_FREEZE; } + PARAMETRIZE { status = STATUS1_PARALYSIS; } + PARAMETRIZE { status = STATUS1_POISON; } + PARAMETRIZE { status = STATUS1_TOXIC_POISON; } + PARAMETRIZE { status = STATUS1_SLEEP; } GIVEN { PLAYER(SPECIES_WOBBUFFET) { Status1(status); } OPPONENT(SPECIES_WYNAUT); @@ -86,12 +86,12 @@ SINGLE_BATTLE_TEST("Full Restore heals a battler from any primary status") SINGLE_BATTLE_TEST("Full Restore heals a party member from any primary status") { u16 status; - PARAMETRIZE{ status = STATUS1_BURN; } - PARAMETRIZE{ status = STATUS1_FREEZE; } - PARAMETRIZE{ status = STATUS1_PARALYSIS; } - PARAMETRIZE{ status = STATUS1_POISON; } - PARAMETRIZE{ status = STATUS1_TOXIC_POISON; } - PARAMETRIZE{ status = STATUS1_SLEEP; } + PARAMETRIZE { status = STATUS1_BURN; } + PARAMETRIZE { status = STATUS1_FREEZE; } + PARAMETRIZE { status = STATUS1_PARALYSIS; } + PARAMETRIZE { status = STATUS1_POISON; } + PARAMETRIZE { status = STATUS1_TOXIC_POISON; } + PARAMETRIZE { status = STATUS1_SLEEP; } GIVEN { PLAYER(SPECIES_WOBBUFFET); PLAYER(SPECIES_WYNAUT) { Status1(status); } diff --git a/test/battle/move_effect/acrobatics.c b/test/battle/move_effect/acrobatics.c index bc34719a42..70953d0958 100644 --- a/test/battle/move_effect/acrobatics.c +++ b/test/battle/move_effect/acrobatics.c @@ -1,5 +1,48 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("Acrobatics doubles in power if the user has no held item"); -TO_DO_BATTLE_TEST("Acrobatics still doubles in power when Flying Gem is consumed"); +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_ACROBATICS].effect == EFFECT_ACROBATICS); + ASSUME(gMovesInfo[MOVE_ACROBATICS].type == TYPE_FLYING); +} + +SINGLE_BATTLE_TEST("Acrobatics doubles in power if the user has no held item", s16 damage) +{ + u16 heldItem; + PARAMETRIZE { heldItem = ITEM_POTION; } + PARAMETRIZE { heldItem = ITEM_NONE; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(heldItem); } + } WHEN { + TURN { MOVE(opponent, MOVE_ACROBATICS); } + } SCENE { + HP_BAR(player, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(2), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Acrobatics still doubles in power when Flying Gem is consumed", s16 damage) +{ + u16 heldItem; + PARAMETRIZE { heldItem = ITEM_NONE; } + PARAMETRIZE { heldItem = ITEM_FLYING_GEM; } + GIVEN { + ASSUME(I_GEM_BOOST_POWER >= GEN_6); + ASSUME(gItemsInfo[ITEM_FLYING_GEM].holdEffect == HOLD_EFFECT_GEMS); + ASSUME(gItemsInfo[ITEM_FLYING_GEM].secondaryId == TYPE_FLYING); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(heldItem); } + } WHEN { + TURN { MOVE(opponent, MOVE_ACROBATICS); } + } SCENE { + HP_BAR(player, captureDamage: &results[i].damage); + } FINALLY { + if (I_GEM_BOOST_POWER >= GEN_6) + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.3), (results[1].damage)); + else + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), (results[1].damage)); + } +} diff --git a/test/battle/move_effect/attack_accuracy_up.c b/test/battle/move_effect/attack_accuracy_up.c index 025a0cb86f..102f4d4d21 100644 --- a/test/battle/move_effect/attack_accuracy_up.c +++ b/test/battle/move_effect/attack_accuracy_up.c @@ -1,4 +1,18 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("Hone Claws increases Attack and Accuracy by one stage each"); +SINGLE_BATTLE_TEST("Hone Claws increases Attack and Accuracy by one stage each") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_HONE_CLAWS].effect == EFFECT_ATTACK_ACCURACY_UP); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_HONE_CLAWS); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_HONE_CLAWS, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Wobbuffet's Attack rose!"); + MESSAGE("Wobbuffet's accuracy rose!"); + } +} diff --git a/test/battle/move_effect/aura_wheel.c b/test/battle/move_effect/aura_wheel.c index fa4190dbdf..3d601f3583 100644 --- a/test/battle/move_effect/aura_wheel.c +++ b/test/battle/move_effect/aura_wheel.c @@ -10,8 +10,8 @@ ASSUMPTIONS SINGLE_BATTLE_TEST("Aura Wheel raises Speed; fails if the user is not Morpeko") { u16 species; - PARAMETRIZE{ species = SPECIES_WOBBUFFET; } - PARAMETRIZE{ species = SPECIES_MORPEKO; } + PARAMETRIZE { species = SPECIES_WOBBUFFET; } + PARAMETRIZE { species = SPECIES_MORPEKO; } GIVEN { PLAYER(species); OPPONENT(SPECIES_WOBBUFFET); diff --git a/test/battle/move_effect/embargo.c b/test/battle/move_effect/embargo.c index f1afba711a..11e4069d80 100644 --- a/test/battle/move_effect/embargo.c +++ b/test/battle/move_effect/embargo.c @@ -253,8 +253,8 @@ SINGLE_BATTLE_TEST("Embargo disables the effect of the Plate items on the move J { u32 heldItem; - PARAMETRIZE{ heldItem = ITEM_NONE; } - PARAMETRIZE{ heldItem = ITEM_PIXIE_PLATE; } + PARAMETRIZE { heldItem = ITEM_NONE; } + PARAMETRIZE { heldItem = ITEM_PIXIE_PLATE; } GIVEN { PLAYER(SPECIES_ARCEUS) { Item(heldItem); }; OPPONENT(SPECIES_DRAGONITE); @@ -274,8 +274,8 @@ SINGLE_BATTLE_TEST("Embargo disables the effect of the Drive items on the move T { u32 heldItem; - PARAMETRIZE{ heldItem = ITEM_NONE; } - PARAMETRIZE{ heldItem = ITEM_SHOCK_DRIVE; } + PARAMETRIZE { heldItem = ITEM_NONE; } + PARAMETRIZE { heldItem = ITEM_SHOCK_DRIVE; } GIVEN { PLAYER(SPECIES_GENESECT) { Item(heldItem); }; OPPONENT(SPECIES_GYARADOS); @@ -295,8 +295,8 @@ SINGLE_BATTLE_TEST("Embargo disables the effect of the Memory items on the move { u32 heldItem; - PARAMETRIZE{ heldItem = ITEM_NONE; } - PARAMETRIZE{ heldItem = ITEM_FIRE_MEMORY; } + PARAMETRIZE { heldItem = ITEM_NONE; } + PARAMETRIZE { heldItem = ITEM_FIRE_MEMORY; } GIVEN { PLAYER(SPECIES_SILVALLY) { Item(heldItem); }; OPPONENT(SPECIES_VENUSAUR); diff --git a/test/battle/move_effect/fury_cutter.c b/test/battle/move_effect/fury_cutter.c new file mode 100644 index 0000000000..cf3871964e --- /dev/null +++ b/test/battle/move_effect/fury_cutter.c @@ -0,0 +1,38 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_FURY_CUTTER].effect == EFFECT_FURY_CUTTER); +} + +SINGLE_BATTLE_TEST("Fury Cutter power doubles with each use, up to 160 power") +{ + s16 damage[6]; + int turn; + int maxTurns; + + if (B_UPDATED_MOVE_DATA >= GEN_6) + maxTurns = 4; + else if (B_UPDATED_MOVE_DATA == GEN_5) + maxTurns = 5; + else + maxTurns = 6; + + GIVEN { + PLAYER(SPECIES_CROBAT); + OPPONENT(SPECIES_LINOONE) { HP(900); } + } WHEN { + for (turn = 0; turn < maxTurns; turn++) + TURN { MOVE(player, MOVE_FURY_CUTTER); } + } SCENE { + for (turn = 0; turn < maxTurns; turn++) { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FURY_CUTTER, player); + HP_BAR(opponent, captureDamage: &damage[turn]); + } + } THEN { + for (turn = 1; turn < maxTurns - 1; turn++) + EXPECT_MUL_EQ(damage[turn - 1], UQ_4_12(2.0), damage[turn]); + EXPECT_EQ(damage[maxTurns - 2], damage[maxTurns - 1]); + } +} diff --git a/test/battle/move_effect/hit_escape.c b/test/battle/move_effect/hit_escape.c index dd1372a017..0c96bd4e75 100644 --- a/test/battle/move_effect/hit_escape.c +++ b/test/battle/move_effect/hit_escape.c @@ -126,6 +126,7 @@ SINGLE_BATTLE_TEST("Held items are consumed immediately after a mon switched in ABILITY_POPUP(player, ABILITY_ELECTRIC_SURGE); ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, player); HP_BAR(opponent); + ABILITY_POPUP(player, ABILITY_INTIMIDATE); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); MESSAGE("2 sent out Wynaut!"); @@ -137,7 +138,6 @@ SINGLE_BATTLE_TEST("Held items are consumed immediately after a mon switched in SINGLE_BATTLE_TEST("Held items are consumed immediately after a mon switched in by U-turn and Intimidate activates after it: opposing side") { - KNOWN_FAILING; GIVEN { PLAYER(SPECIES_TAPU_KOKO) { Ability(ABILITY_ELECTRIC_SURGE); }; PLAYER(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); } @@ -149,7 +149,7 @@ SINGLE_BATTLE_TEST("Held items are consumed immediately after a mon switched in ABILITY_POPUP(player, ABILITY_ELECTRIC_SURGE); ANIMATION(ANIM_TYPE_MOVE, MOVE_U_TURN, player); HP_BAR(opponent); - NOT ABILITY_POPUP(player, ABILITY_INTIMIDATE); + ABILITY_POPUP(player, ABILITY_INTIMIDATE); MESSAGE("2 sent out Wynaut!"); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); diff --git a/test/battle/move_effect/plasma_fists.c b/test/battle/move_effect/plasma_fists.c index ec9dd37189..bdd3ed4f41 100644 --- a/test/battle/move_effect/plasma_fists.c +++ b/test/battle/move_effect/plasma_fists.c @@ -47,20 +47,19 @@ SINGLE_BATTLE_TEST("Plasma Fists turns normal moves into electric for the remain } } -SINGLE_BATTLE_TEST("Plasma Fists type-changing effect is applied after Pixilate") +SINGLE_BATTLE_TEST("Plasma Fists type-changing effect does not override Pixilate") { GIVEN { PLAYER(SPECIES_KRABBY) { Speed(300); }; - OPPONENT(SPECIES_ALTARIA) { Speed(1); Item(ITEM_ALTARIANITE); } + OPPONENT(SPECIES_SYLVEON) { Speed(1); Ability(ABILITY_PIXILATE); } } WHEN { - TURN { MOVE(player, MOVE_PLASMA_FISTS); MOVE(opponent, MOVE_EMBER, gimmick: GIMMICK_MEGA); } + TURN { MOVE(player, MOVE_PLASMA_FISTS); MOVE(opponent, MOVE_TACKLE); } } SCENE { - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, opponent); MESSAGE("Krabby used Plasma Fists!"); ANIMATION(ANIM_TYPE_MOVE, MOVE_PLASMA_FISTS, player); MESSAGE("A deluge of ions showers the battlefield!"); - MESSAGE("Foe Altaria used Ember!"); - ANIMATION(ANIM_TYPE_MOVE, MOVE_EMBER, opponent); + MESSAGE("Foe Sylveon used Tackle!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); NOT MESSAGE("It's super effective!"); } } diff --git a/test/battle/move_effect/roost.c b/test/battle/move_effect/roost.c index 1ae84dd088..b4c0186bf0 100644 --- a/test/battle/move_effect/roost.c +++ b/test/battle/move_effect/roost.c @@ -111,24 +111,24 @@ SINGLE_BATTLE_TEST("Roost suppresses the user's Flying-typing this turn, then re SINGLE_BATTLE_TEST("Roost, if used by a Flying/Flying type, treats the user as a Normal-type (or Typeless in Gen. 4) until the end of the turn") { u32 damagingMove; - PARAMETRIZE{ damagingMove = MOVE_POUND; } - PARAMETRIZE{ damagingMove = MOVE_KARATE_CHOP; } - PARAMETRIZE{ damagingMove = MOVE_GUST; } - PARAMETRIZE{ damagingMove = MOVE_POISON_STING; } - PARAMETRIZE{ damagingMove = MOVE_EARTHQUAKE; } - PARAMETRIZE{ damagingMove = MOVE_ROCK_THROW; } - PARAMETRIZE{ damagingMove = MOVE_LEECH_LIFE; } - PARAMETRIZE{ damagingMove = MOVE_LICK; } - PARAMETRIZE{ damagingMove = MOVE_STEEL_WING; } - PARAMETRIZE{ damagingMove = MOVE_EMBER; } - PARAMETRIZE{ damagingMove = MOVE_WATER_GUN; } - PARAMETRIZE{ damagingMove = MOVE_VINE_WHIP; } - PARAMETRIZE{ damagingMove = MOVE_THUNDER_SHOCK; } - PARAMETRIZE{ damagingMove = MOVE_CONFUSION; } - PARAMETRIZE{ damagingMove = MOVE_ICE_BEAM; } - PARAMETRIZE{ damagingMove = MOVE_DRAGON_BREATH; } - PARAMETRIZE{ damagingMove = MOVE_BITE; } - PARAMETRIZE{ damagingMove = MOVE_DISARMING_VOICE; } + PARAMETRIZE { damagingMove = MOVE_POUND; } + PARAMETRIZE { damagingMove = MOVE_KARATE_CHOP; } + PARAMETRIZE { damagingMove = MOVE_GUST; } + PARAMETRIZE { damagingMove = MOVE_POISON_STING; } + PARAMETRIZE { damagingMove = MOVE_EARTHQUAKE; } + PARAMETRIZE { damagingMove = MOVE_ROCK_THROW; } + PARAMETRIZE { damagingMove = MOVE_LEECH_LIFE; } + PARAMETRIZE { damagingMove = MOVE_LICK; } + PARAMETRIZE { damagingMove = MOVE_STEEL_WING; } + PARAMETRIZE { damagingMove = MOVE_EMBER; } + PARAMETRIZE { damagingMove = MOVE_WATER_GUN; } + PARAMETRIZE { damagingMove = MOVE_VINE_WHIP; } + PARAMETRIZE { damagingMove = MOVE_THUNDER_SHOCK; } + PARAMETRIZE { damagingMove = MOVE_CONFUSION; } + PARAMETRIZE { damagingMove = MOVE_ICE_BEAM; } + PARAMETRIZE { damagingMove = MOVE_DRAGON_BREATH; } + PARAMETRIZE { damagingMove = MOVE_BITE; } + PARAMETRIZE { damagingMove = MOVE_DISARMING_VOICE; } GIVEN { ASSUME(gSpeciesInfo[SPECIES_TORNADUS].types[0] == TYPE_FLYING); @@ -179,24 +179,24 @@ SINGLE_BATTLE_TEST("Roost, if used by a Flying/Flying type, treats the user as a SINGLE_BATTLE_TEST("Roost, if used by a Mystery/Flying type, treats the user as a Mystery/Mystery type until the end of the turn") { u32 damagingMove; - PARAMETRIZE{ damagingMove = MOVE_POUND; } - PARAMETRIZE{ damagingMove = MOVE_KARATE_CHOP; } - PARAMETRIZE{ damagingMove = MOVE_GUST; } - PARAMETRIZE{ damagingMove = MOVE_POISON_STING; } - PARAMETRIZE{ damagingMove = MOVE_EARTHQUAKE; } - PARAMETRIZE{ damagingMove = MOVE_ROCK_THROW; } - PARAMETRIZE{ damagingMove = MOVE_LEECH_LIFE; } - PARAMETRIZE{ damagingMove = MOVE_LICK; } - PARAMETRIZE{ damagingMove = MOVE_STEEL_WING; } - PARAMETRIZE{ damagingMove = MOVE_EMBER; } - PARAMETRIZE{ damagingMove = MOVE_WATER_GUN; } - PARAMETRIZE{ damagingMove = MOVE_VINE_WHIP; } - PARAMETRIZE{ damagingMove = MOVE_THUNDER_SHOCK; } - PARAMETRIZE{ damagingMove = MOVE_CONFUSION; } - PARAMETRIZE{ damagingMove = MOVE_ICE_BEAM; } - PARAMETRIZE{ damagingMove = MOVE_DRAGON_BREATH; } - PARAMETRIZE{ damagingMove = MOVE_BITE; } - PARAMETRIZE{ damagingMove = MOVE_DISARMING_VOICE; } + PARAMETRIZE { damagingMove = MOVE_POUND; } + PARAMETRIZE { damagingMove = MOVE_KARATE_CHOP; } + PARAMETRIZE { damagingMove = MOVE_GUST; } + PARAMETRIZE { damagingMove = MOVE_POISON_STING; } + PARAMETRIZE { damagingMove = MOVE_EARTHQUAKE; } + PARAMETRIZE { damagingMove = MOVE_ROCK_THROW; } + PARAMETRIZE { damagingMove = MOVE_LEECH_LIFE; } + PARAMETRIZE { damagingMove = MOVE_LICK; } + PARAMETRIZE { damagingMove = MOVE_STEEL_WING; } + PARAMETRIZE { damagingMove = MOVE_EMBER; } + PARAMETRIZE { damagingMove = MOVE_WATER_GUN; } + PARAMETRIZE { damagingMove = MOVE_VINE_WHIP; } + PARAMETRIZE { damagingMove = MOVE_THUNDER_SHOCK; } + PARAMETRIZE { damagingMove = MOVE_CONFUSION; } + PARAMETRIZE { damagingMove = MOVE_ICE_BEAM; } + PARAMETRIZE { damagingMove = MOVE_DRAGON_BREATH; } + PARAMETRIZE { damagingMove = MOVE_BITE; } + PARAMETRIZE { damagingMove = MOVE_DISARMING_VOICE; } GIVEN { ASSUME(gSpeciesInfo[SPECIES_MOLTRES].types[0] == TYPE_FIRE); diff --git a/test/battle/move_effect/sticky_web.c b/test/battle/move_effect/sticky_web.c index f0017c3ca7..035568dd75 100644 --- a/test/battle/move_effect/sticky_web.c +++ b/test/battle/move_effect/sticky_web.c @@ -56,7 +56,7 @@ DOUBLE_BATTLE_TEST("Sticky Web lowers Speed by 1 in a double battle after Explos OPPONENT(SPECIES_WOBBUFFET) {HP(1); Speed(1);} OPPONENT(SPECIES_WOBBUFFET) {HP(1); Speed(1);} OPPONENT(SPECIES_WYNAUT) {Speed(10);} - OPPONENT(SPECIES_WYNAUT) {Speed(10);} + OPPONENT(SPECIES_ALAKAZAM) {Speed(100);} } WHEN { TURN { MOVE(playerRight, MOVE_STICKY_WEB); MOVE(playerLeft, MOVE_EXPLOSION); SEND_OUT(playerLeft, 2); SEND_OUT(opponentLeft, 2); SEND_OUT(opponentRight, 3); } TURN {} @@ -65,13 +65,13 @@ DOUBLE_BATTLE_TEST("Sticky Web lowers Speed by 1 in a double battle after Explos MESSAGE("A sticky web spreads out on the ground around the opposing team!"); ANIMATION(ANIM_TYPE_MOVE, MOVE_EXPLOSION, playerLeft); MESSAGE("2 sent out Wynaut!"); + MESSAGE("2 sent out Alakazam!"); + MESSAGE("Foe Alakazam was caught in a Sticky Web!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight); + MESSAGE("Foe Alakazam's Speed fell!"); MESSAGE("Foe Wynaut was caught in a Sticky Web!"); ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft); MESSAGE("Foe Wynaut's Speed fell!"); - MESSAGE("2 sent out Wynaut!"); - MESSAGE("Foe Wynaut was caught in a Sticky Web!"); - ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight); - MESSAGE("Foe Wynaut's Speed fell!"); } } diff --git a/test/battle/move_effect/strength_sap.c b/test/battle/move_effect/strength_sap.c index ef8ef3259f..4747074270 100644 --- a/test/battle/move_effect/strength_sap.c +++ b/test/battle/move_effect/strength_sap.c @@ -10,8 +10,8 @@ SINGLE_BATTLE_TEST("Strength Sap lowers Attack by 1 and restores HP based on tar { u32 atkStat = 0; - PARAMETRIZE{ atkStat = 100; } - PARAMETRIZE{ atkStat = 50; } + PARAMETRIZE { atkStat = 100; } + PARAMETRIZE { atkStat = 50; } GIVEN { PLAYER(SPECIES_WOBBUFFET) { HP(200); } @@ -35,8 +35,8 @@ SINGLE_BATTLE_TEST("Strength Sap works exactly the same when attacker is behind { u32 atkStat = 0; - PARAMETRIZE{ atkStat = 100; } - PARAMETRIZE{ atkStat = 50; } + PARAMETRIZE { atkStat = 100; } + PARAMETRIZE { atkStat = 50; } GIVEN { PLAYER(SPECIES_WOBBUFFET) { HP(200); } @@ -65,7 +65,7 @@ SINGLE_BATTLE_TEST("Strength Sap lowers Attack by 1 and restores HP based on tar for (j = 0; j <= MAX_STAT_STAGE; j++) { if (j == DEFAULT_STAT_STAGE - 1) { continue; } // Ignore -6, because Strength Sap won't work otherwise - PARAMETRIZE{ statStage = j; } + PARAMETRIZE { statStage = j; } } GIVEN { diff --git a/test/battle/move_effect/toxic.c b/test/battle/move_effect/toxic.c index 35bfaa0644..804ed56b8f 100644 --- a/test/battle/move_effect/toxic.c +++ b/test/battle/move_effect/toxic.c @@ -48,3 +48,21 @@ SINGLE_BATTLE_TEST("Toxic cannot miss if used by a Poison-type") } } } + +AI_SINGLE_BATTLE_TEST("AI avoids toxic when it can not poison target") +{ + u32 species, ability; + + PARAMETRIZE { species = SPECIES_SNORLAX; ability = ABILITY_IMMUNITY; } + PARAMETRIZE { species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; } + PARAMETRIZE { species = SPECIES_NACLI; ability = ABILITY_PURIFYING_SALT; } + PARAMETRIZE { species = SPECIES_BULBASAUR; ability = ABILITY_OVERGROW; } + + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT); + PLAYER(species) { Ability(ability); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE, MOVE_TOXIC); } + } WHEN { + TURN { SCORE_EQ(opponent, MOVE_CELEBRATE, MOVE_TOXIC); } // Both get -10 + } +} diff --git a/test/battle/move_effect/two_turns_attack.c b/test/battle/move_effect/two_turns_attack.c index 9225afb48e..4b7adc6f81 100644 --- a/test/battle/move_effect/two_turns_attack.c +++ b/test/battle/move_effect/two_turns_attack.c @@ -243,8 +243,8 @@ SINGLE_BATTLE_TEST("Solar Beam and Solar Blade can be used instantly in Sunlight SINGLE_BATTLE_TEST("Solar Beam's power is halved in Rain", s16 damage) { u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_RAIN_DANCE; } + PARAMETRIZE { move = MOVE_CELEBRATE; } + PARAMETRIZE { move = MOVE_RAIN_DANCE; } GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); @@ -261,8 +261,8 @@ SINGLE_BATTLE_TEST("Solar Beam's power is halved in Rain", s16 damage) SINGLE_BATTLE_TEST("Solar Blade's power is halved in Rain", s16 damage) { u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_RAIN_DANCE; } + PARAMETRIZE { move = MOVE_CELEBRATE; } + PARAMETRIZE { move = MOVE_RAIN_DANCE; } GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WYNAUT); @@ -279,8 +279,8 @@ SINGLE_BATTLE_TEST("Solar Blade's power is halved in Rain", s16 damage) SINGLE_BATTLE_TEST("Solar Beam's power is halved in a Sandstorm", s16 damage) { u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_SANDSTORM; } + PARAMETRIZE { move = MOVE_CELEBRATE; } + PARAMETRIZE { move = MOVE_SANDSTORM; } GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SAFETY_GOGGLES); }; @@ -297,8 +297,8 @@ SINGLE_BATTLE_TEST("Solar Beam's power is halved in a Sandstorm", s16 damage) SINGLE_BATTLE_TEST("Solar Blade's power is halved in a Sandstorm", s16 damage) { u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_SANDSTORM; } + PARAMETRIZE { move = MOVE_CELEBRATE; } + PARAMETRIZE { move = MOVE_SANDSTORM; } GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SAFETY_GOGGLES); }; @@ -315,8 +315,8 @@ SINGLE_BATTLE_TEST("Solar Blade's power is halved in a Sandstorm", s16 damage) SINGLE_BATTLE_TEST("Solar Beam's power is halved in Hail", s16 damage) { u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_HAIL; } + PARAMETRIZE { move = MOVE_CELEBRATE; } + PARAMETRIZE { move = MOVE_HAIL; } GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SAFETY_GOGGLES); }; @@ -333,8 +333,8 @@ SINGLE_BATTLE_TEST("Solar Beam's power is halved in Hail", s16 damage) SINGLE_BATTLE_TEST("Solar Blade's power is halved in Hail", s16 damage) { u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_HAIL; } + PARAMETRIZE { move = MOVE_CELEBRATE; } + PARAMETRIZE { move = MOVE_HAIL; } GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SAFETY_GOGGLES); }; @@ -351,8 +351,8 @@ SINGLE_BATTLE_TEST("Solar Blade's power is halved in Hail", s16 damage) SINGLE_BATTLE_TEST("Solar Beam's power is halved in Snow", s16 damage) { u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_SNOWSCAPE; } + PARAMETRIZE { move = MOVE_CELEBRATE; } + PARAMETRIZE { move = MOVE_SNOWSCAPE; } GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); @@ -369,8 +369,8 @@ SINGLE_BATTLE_TEST("Solar Beam's power is halved in Snow", s16 damage) SINGLE_BATTLE_TEST("Solar Blade's power is halved in Snow", s16 damage) { u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_SNOWSCAPE; } + PARAMETRIZE { move = MOVE_CELEBRATE; } + PARAMETRIZE { move = MOVE_SNOWSCAPE; } GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WYNAUT); diff --git a/test/battle/move_effect/weather_ball.c b/test/battle/move_effect/weather_ball.c index 38c902f507..1656b60d87 100644 --- a/test/battle/move_effect/weather_ball.c +++ b/test/battle/move_effect/weather_ball.c @@ -9,8 +9,8 @@ ASSUMPTIONS SINGLE_BATTLE_TEST("Weather Ball doubles its power and turns to a Fire-type move in Sunlight", s16 damage) { u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_SUNNY_DAY; } + PARAMETRIZE { move = MOVE_CELEBRATE; } + PARAMETRIZE { move = MOVE_SUNNY_DAY; } GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_MEGANIUM); @@ -27,8 +27,8 @@ SINGLE_BATTLE_TEST("Weather Ball doubles its power and turns to a Fire-type move SINGLE_BATTLE_TEST("Weather Ball doubles its power and turns to a Water-type move in Rain", s16 damage) { u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_RAIN_DANCE; } + PARAMETRIZE { move = MOVE_CELEBRATE; } + PARAMETRIZE { move = MOVE_RAIN_DANCE; } GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_ARCANINE); @@ -45,8 +45,8 @@ SINGLE_BATTLE_TEST("Weather Ball doubles its power and turns to a Water-type mov SINGLE_BATTLE_TEST("Weather Ball doubles its power and turns to a Rock-type move in a Sandstorm", s16 damage) { u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_SANDSTORM; } + PARAMETRIZE { move = MOVE_CELEBRATE; } + PARAMETRIZE { move = MOVE_SANDSTORM; } GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_MAGMAR) { Item(ITEM_SAFETY_GOGGLES); }; @@ -63,9 +63,9 @@ SINGLE_BATTLE_TEST("Weather Ball doubles its power and turns to a Rock-type move SINGLE_BATTLE_TEST("Weather Ball doubles its power and turns to an Ice-type move in Hail and Snow", s16 damage) { u16 move; - PARAMETRIZE{ move = MOVE_CELEBRATE; } - PARAMETRIZE{ move = MOVE_HAIL; } - PARAMETRIZE{ move = MOVE_SNOWSCAPE; } + PARAMETRIZE { move = MOVE_CELEBRATE; } + PARAMETRIZE { move = MOVE_HAIL; } + PARAMETRIZE { move = MOVE_SNOWSCAPE; } GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_DRAGONAIR) { Item(ITEM_SAFETY_GOGGLES); }; diff --git a/test/battle/move_effects_combined/hurricane.c b/test/battle/move_effects_combined/hurricane.c index eab843e720..61acac6649 100644 --- a/test/battle/move_effects_combined/hurricane.c +++ b/test/battle/move_effects_combined/hurricane.c @@ -32,4 +32,42 @@ SINGLE_BATTLE_TEST("Hurricane bypasses accuracy checks in Rain") NONE_OF { MESSAGE("Wobbuffet's attack missed!"); } } } -TO_DO_BATTLE_TEST("Hurricane Veil can hit airborne targets") // Fly, Bounce, Sky Drop + +SINGLE_BATTLE_TEST("Hurricane can hit airborne targets (Fly, Bounce)") +{ + u16 move; + PARAMETRIZE { move = MOVE_FLY; } + PARAMETRIZE { move = MOVE_BOUNCE; } + GIVEN { + ASSUME(gMovesInfo[MOVE_FLY].effect == EFFECT_SEMI_INVULNERABLE); + ASSUME(UNCOMPRESS_BITS(HIHALF(gMovesInfo[MOVE_FLY].argument)) == STATUS3_ON_AIR); + ASSUME(gMovesInfo[MOVE_BOUNCE].effect == EFFECT_SEMI_INVULNERABLE); + ASSUME(UNCOMPRESS_BITS(HIHALF(gMovesInfo[MOVE_BOUNCE].argument)) == STATUS3_ON_AIR); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Moves(move); } + } WHEN { + TURN { MOVE(opponent, move); MOVE(player, MOVE_HURRICANE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_HURRICANE, player); + NONE_OF { MESSAGE("Wobbuffet's attack missed!"); } + } +} + +DOUBLE_BATTLE_TEST("Hurricane can hit airborne targets (Sky Drop)") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_SKY_DROP].effect == EFFECT_SKY_DROP); + ASSUME(UNCOMPRESS_BITS(HIHALF(gMovesInfo[MOVE_SKY_DROP].argument)) == STATUS3_ON_AIR); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(playerLeft, MOVE_SKY_DROP, target: opponentLeft); MOVE(playerRight, MOVE_HURRICANE, target: playerLeft); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SKY_DROP, playerLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_HURRICANE, playerRight); + NONE_OF { MESSAGE("Wobbuffet's attack missed!"); } + } +} diff --git a/test/battle/move_flags/ignores_target_ability.c b/test/battle/move_flags/ignores_target_ability.c new file mode 100644 index 0000000000..2836f4838e --- /dev/null +++ b/test/battle/move_flags/ignores_target_ability.c @@ -0,0 +1,74 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_SUNSTEEL_STRIKE].ignoresTargetAbility); + ASSUME(gMovesInfo[MOVE_MOONGEIST_BEAM].ignoresTargetAbility); + ASSUME(gMovesInfo[MOVE_PHOTON_GEYSER].ignoresTargetAbility); +} + +SINGLE_BATTLE_TEST("ignoresTargetAbility moves do not ignore the attacker's own ability", s16 damage) +{ + u32 ability, move; + + PARAMETRIZE { move = MOVE_SUNSTEEL_STRIKE; ability = ABILITY_MAGIC_GUARD; } + PARAMETRIZE { move = MOVE_SUNSTEEL_STRIKE; ability = ABILITY_UNAWARE; } + PARAMETRIZE { move = MOVE_MOONGEIST_BEAM; ability = ABILITY_MAGIC_GUARD; } + PARAMETRIZE { move = MOVE_MOONGEIST_BEAM; ability = ABILITY_UNAWARE; } + PARAMETRIZE { move = MOVE_PHOTON_GEYSER; ability = ABILITY_MAGIC_GUARD; } + PARAMETRIZE { move = MOVE_PHOTON_GEYSER; ability = ABILITY_UNAWARE; } + + ASSUME(gAbilitiesInfo[ABILITY_UNAWARE].breakable); + ASSUME(gMovesInfo[MOVE_IRON_DEFENSE].effect == EFFECT_DEFENSE_UP_2); + ASSUME(gMovesInfo[MOVE_AMNESIA].effect == EFFECT_SPECIAL_DEFENSE_UP_2); + + GIVEN { + PLAYER(SPECIES_CLEFABLE) { Speed(1); Ability(ability); } + OPPONENT(SPECIES_ARON) { Speed(2); } + } WHEN { + if (gMovesInfo[move].category == DAMAGE_CATEGORY_PHYSICAL) + TURN { MOVE(opponent, MOVE_IRON_DEFENSE); MOVE(player, move); } + else + TURN { MOVE(opponent, MOVE_AMNESIA); MOVE(player, move); } + } SCENE { + if (gMovesInfo[move].category == DAMAGE_CATEGORY_PHYSICAL) + ANIMATION(ANIM_TYPE_MOVE, MOVE_IRON_DEFENSE, opponent); + else + ANIMATION(ANIM_TYPE_MOVE, MOVE_AMNESIA, opponent); + ANIMATION(ANIM_TYPE_MOVE, move, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, UQ_4_12(2.0), results[1].damage); + EXPECT_MUL_EQ(results[2].damage, UQ_4_12(2.0), results[3].damage); + EXPECT_MUL_EQ(results[4].damage, UQ_4_12(2.0), results[5].damage); + } +} + +SINGLE_BATTLE_TEST("ignoresTargetAbility moves do ignore target's abilities", s16 damage) +{ + u32 ability, move; + + PARAMETRIZE { move = MOVE_SUNSTEEL_STRIKE; ability = ABILITY_INNER_FOCUS; } + PARAMETRIZE { move = MOVE_SUNSTEEL_STRIKE; ability = ABILITY_MULTISCALE; } + PARAMETRIZE { move = MOVE_MOONGEIST_BEAM; ability = ABILITY_INNER_FOCUS; } + PARAMETRIZE { move = MOVE_MOONGEIST_BEAM; ability = ABILITY_MULTISCALE; } + PARAMETRIZE { move = MOVE_PHOTON_GEYSER; ability = ABILITY_INNER_FOCUS; } + PARAMETRIZE { move = MOVE_PHOTON_GEYSER; ability = ABILITY_MULTISCALE; } + + ASSUME(gAbilitiesInfo[ABILITY_MULTISCALE].breakable); + + GIVEN { + PLAYER(SPECIES_AZUMARILL); + OPPONENT(SPECIES_DRAGONITE) { Ability(ability); } + } WHEN { + TURN { MOVE(player, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + EXPECT_EQ(results[2].damage, results[3].damage); + EXPECT_EQ(results[4].damage, results[5].damage); + } +} diff --git a/test/battle/status1/burn.c b/test/battle/status1/burn.c index c49bd7a3f2..4da40589fb 100644 --- a/test/battle/status1/burn.c +++ b/test/battle/status1/burn.c @@ -35,3 +35,23 @@ SINGLE_BATTLE_TEST("Burn reduces Attack by 50%", s16 damage) EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage); } } + +AI_SINGLE_BATTLE_TEST("AI avoids Will-o-Wisp when it can not burn target") +{ + u32 species, ability; + + PARAMETRIZE { species = SPECIES_BUIZEL; ability = ABILITY_WATER_VEIL; } + PARAMETRIZE { species = SPECIES_DEWPIDER; ability = ABILITY_WATER_BUBBLE; } + PARAMETRIZE { species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; } + PARAMETRIZE { species = SPECIES_ARCTIBAX; ability = ABILITY_THERMAL_EXCHANGE; } + PARAMETRIZE { species = SPECIES_NACLI; ability = ABILITY_PURIFYING_SALT; } + PARAMETRIZE { species = SPECIES_CHARMANDER; ability = ABILITY_BLAZE; } + + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT); + PLAYER(species) { Ability(ability); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE, MOVE_WILL_O_WISP); } + } WHEN { + TURN { SCORE_EQ(opponent, MOVE_CELEBRATE, MOVE_WILL_O_WISP); } // Both get -10 + } +} diff --git a/test/battle/status1/paralysis.c b/test/battle/status1/paralysis.c index b6edaf2635..558ce4fc37 100644 --- a/test/battle/status1/paralysis.c +++ b/test/battle/status1/paralysis.c @@ -42,3 +42,21 @@ SINGLE_BATTLE_TEST("Paralysis has a 25% chance of skipping the turn") MESSAGE("Wobbuffet is paralyzed! It can't move!"); } } + +AI_SINGLE_BATTLE_TEST("AI avoids Thunder Wave when it can not paralyse target") +{ + u32 species, ability; + + PARAMETRIZE { species = SPECIES_HITMONLEE; ability = ABILITY_LIMBER; } + PARAMETRIZE { species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; } + PARAMETRIZE { species = SPECIES_NACLI; ability = ABILITY_PURIFYING_SALT; } + PARAMETRIZE { species = SPECIES_PIKACHU; ability = ABILITY_STATIC; } + + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT); + PLAYER(species) { Ability(ability); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE, MOVE_THUNDER_WAVE); } + } WHEN { + TURN { SCORE_EQ(opponent, MOVE_CELEBRATE, MOVE_THUNDER_WAVE); } // Both get -10 + } +} diff --git a/test/battle/status1/sleep.c b/test/battle/status1/sleep.c index b3dd403eb1..f42f4bd714 100644 --- a/test/battle/status1/sleep.c +++ b/test/battle/status1/sleep.c @@ -21,3 +21,21 @@ SINGLE_BATTLE_TEST("Sleep prevents the battler from using a move") MESSAGE("Wobbuffet used Celebrate!"); } } + +AI_SINGLE_BATTLE_TEST("AI avoids hypnosis when it can not put target to sleep") +{ + u32 species, ability; + + PARAMETRIZE { species = SPECIES_HOOTHOOT; ability = ABILITY_INSOMNIA; } + PARAMETRIZE { species = SPECIES_MANKEY; ability = ABILITY_VITAL_SPIRIT; } + PARAMETRIZE { species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; } + PARAMETRIZE { species = SPECIES_NACLI; ability = ABILITY_PURIFYING_SALT; } + + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT); + PLAYER(species) { Ability(ability); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE, MOVE_HYPNOSIS); } + } WHEN { + TURN { SCORE_EQ(opponent, MOVE_CELEBRATE, MOVE_HYPNOSIS); } // Both get -10 + } +} diff --git a/test/battle/status2/confusion.c b/test/battle/status2/confusion.c new file mode 100644 index 0000000000..3c86e5d555 --- /dev/null +++ b/test/battle/status2/confusion.c @@ -0,0 +1,28 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Confusion adds a 50/33% chance to hit self with 40 power") +{ + s16 damage[2]; + + ASSUME(gMovesInfo[MOVE_TACKLE].power == 40); + + PASSES_RANDOMLY(B_CONFUSION_SELF_DMG_CHANCE >= GEN_7 ? 33 : 50, 100, RNG_CONFUSION); + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Speed(1); }; + OPPONENT(SPECIES_WOBBUFFET) { Speed(2); }; + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE, WITH_RNG(RNG_DAMAGE_MODIFIER, 0)); MOVE(player, MOVE_CONFUSE_RAY); } + TURN; + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + HP_BAR(player, captureDamage: &damage[0]); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CONFUSE_RAY, player); + MESSAGE("Foe Wobbuffet became confused!"); + MESSAGE("Foe Wobbuffet is confused!"); + MESSAGE("It hurt itself in its confusion!"); + HP_BAR(opponent, captureDamage: &damage[1]); + } THEN { + EXPECT_EQ(damage[0], damage[1]); + } +} diff --git a/test/battle/terrain/grassy.c b/test/battle/terrain/grassy.c index 678352dc5e..862c9052fd 100644 --- a/test/battle/terrain/grassy.c +++ b/test/battle/terrain/grassy.c @@ -85,3 +85,27 @@ SINGLE_BATTLE_TEST("Grassy Terrain lasts for 5 turns") MESSAGE("The grass disappeared from the battlefield."); } } + +SINGLE_BATTLE_TEST("Grassy Terrain heals the pokemon on the field for the duration of the terrain, including last turn") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { HP(1); }; + } WHEN { + TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_GRASSY_TERRAIN); } + TURN {} + TURN {} + TURN {} + TURN {} + } SCENE { + MESSAGE("Foe Wobbuffet used Celebrate!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_GRASSY_TERRAIN, player); + MESSAGE("Grass grew to cover the battlefield!"); + MESSAGE("Foe Wobbuffet is healed by the grassy terrain!"); + MESSAGE("Foe Wobbuffet is healed by the grassy terrain!"); + MESSAGE("Foe Wobbuffet is healed by the grassy terrain!"); + MESSAGE("Foe Wobbuffet is healed by the grassy terrain!"); + MESSAGE("Foe Wobbuffet is healed by the grassy terrain!"); + MESSAGE("The grass disappeared from the battlefield."); + } +} diff --git a/test/battle/weather/sandstorm.c b/test/battle/weather/sandstorm.c index 5d7a6f1713..2f3f4e7ca2 100644 --- a/test/battle/weather/sandstorm.c +++ b/test/battle/weather/sandstorm.c @@ -20,8 +20,8 @@ SINGLE_BATTLE_TEST("Sandstorm deals 1/16 damage per turn") SINGLE_BATTLE_TEST("Sandstorm multiplies the special defense of Rock-types by 1.5x", s16 damage) { u16 move; - PARAMETRIZE{ move = MOVE_SANDSTORM; } - PARAMETRIZE{ move = MOVE_CELEBRATE; } + PARAMETRIZE { move = MOVE_SANDSTORM; } + PARAMETRIZE { move = MOVE_CELEBRATE; } GIVEN { ASSUME(gMovesInfo[MOVE_SWIFT].category == DAMAGE_CATEGORY_SPECIAL); PLAYER(SPECIES_WOBBUFFET) ; diff --git a/test/battle/weather/snow.c b/test/battle/weather/snow.c index 3aceaf2f85..6c084f6b7a 100644 --- a/test/battle/weather/snow.c +++ b/test/battle/weather/snow.c @@ -13,8 +13,8 @@ ASSUMPTIONS SINGLE_BATTLE_TEST("Snow multiplies the defense of Ice-types by 1.5x", s16 damage) { u16 move; - PARAMETRIZE{ move = MOVE_SNOWSCAPE; } - PARAMETRIZE{ move = MOVE_CELEBRATE; } + PARAMETRIZE { move = MOVE_SNOWSCAPE; } + PARAMETRIZE { move = MOVE_CELEBRATE; } GIVEN { PLAYER(SPECIES_WOBBUFFET); OPPONENT(SPECIES_GLALIE); diff --git a/test/species.c b/test/species.c index d4f0b4027e..d09f1e3eb9 100644 --- a/test/species.c +++ b/test/species.c @@ -1,4 +1,5 @@ #include "global.h" +#include "string_util.h" #include "test/test.h" #include "constants/form_change_types.h" @@ -138,3 +139,18 @@ TEST("No species has two evolutions that use the evolution tracker") EXPECT(evolutionTrackerEvolutions < 2); } + +extern const u8 gFallbackPokedexText[]; + +TEST("Every species has a description") +{ + u32 i; + u32 species = SPECIES_NONE; + for (i = 1; i < NUM_SPECIES; i++) + { + if (IsSpeciesEnabled(i)) + PARAMETRIZE { species = i; } + } + + EXPECT_NE(StringCompare(GetSpeciesPokedexDescription(species), gFallbackPokedexText), 0); +} diff --git a/tools/gbagfx/jasc_pal.c b/tools/gbagfx/jasc_pal.c index e5ba9c3c2f..8d4bb137d5 100644 --- a/tools/gbagfx/jasc_pal.c +++ b/tools/gbagfx/jasc_pal.c @@ -46,10 +46,14 @@ void ReadJascPaletteLine(FILE *fp, char *line) } if (c == '\n') - FATAL_ERROR("LF line endings aren't supported.\n"); + { + line[length] = 0; + + return; + } if (c == EOF) - FATAL_ERROR("Unexpected EOF. No CRLF at end of file.\n"); + FATAL_ERROR("Unexpected EOF. No LF or CRLF at end of file.\n"); if (c == 0) FATAL_ERROR("NUL character in file.\n");