Merge branch '_RHH/master' into _RHH/upcoming

# Conflicts:
#	src/battle_util.c
#	src/data/pokemon/species_info/gen_7_families.h
#	test/battle/ability/download.c
#	test/battle/ability/intimidate.c
#	test/battle/ability/supreme_overlord.c
#	test/battle/ability/zero_to_hero.c
#	test/battle/ai/ai.c
#	test/battle/move_effect/plasma_fists.c
This commit is contained in:
Eduardo Quezada 2024-07-05 14:25:25 -04:00
commit fcdc9ed65a
88 changed files with 1405 additions and 507 deletions

View File

@ -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'

View File

@ -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!)_

View File

@ -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)

View File

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
if [[ -d "$DEVKITARM/bin/" ]]; then
OBJDUMP_BIN="$DEVKITARM/bin/arm-none-eabi-objdump"

View File

@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 451 B

After

Width:  |  Height:  |  Size: 388 B

View File

@ -15,5 +15,5 @@ JASC-PAL
248 136 136
88 104 96
184 192 192
0 0 0
248 248 248
0 0 0

View File

@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 708 B

After

Width:  |  Height:  |  Size: 620 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 666 B

After

Width:  |  Height:  |  Size: 571 B

View File

@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 837 B

After

Width:  |  Height:  |  Size: 816 B

View File

@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 898 B

After

Width:  |  Height:  |  Size: 820 B

View File

@ -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

View File

@ -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);

View File

@ -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[];

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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,

View File

@ -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 };

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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))

View File

@ -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);
}

View File

@ -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);

View File

@ -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;

View File

@ -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())

View File

@ -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
}

View File

@ -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)

View File

@ -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,
},

View File

@ -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,

View File

@ -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,

View File

@ -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, \

View File

@ -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"

View File

@ -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];

View File

@ -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!");
}
}

View File

@ -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); }
}
}

View File

@ -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);
}
}
}

View File

@ -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!");
}
}

View File

@ -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);

View File

@ -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

View File

@ -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 {

View File

@ -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; }
}
}

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

@ -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);
}
}
}

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

@ -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!");
}
}

View File

@ -0,0 +1,4 @@
#include "global.h"
#include "test/battle.h"
// Tests for White Smoke are handled in test/battle/ability/clear_body.c

View File

@ -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!");
}
}

View File

@ -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; }
}
}

View File

@ -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 {

View File

@ -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);

View File

@ -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);

View File

@ -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);
}
}
}

View File

@ -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);

View File

@ -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); }

View File

@ -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); }

View File

@ -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));
}
}

View File

@ -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!");
}
}

View File

@ -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);

View File

@ -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);

View File

@ -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]);
}
}

View File

@ -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);

View File

@ -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!");
}
}

View File

@ -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);

View File

@ -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!");
}
}

View File

@ -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 {

View File

@ -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
}
}

View File

@ -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);

View File

@ -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); };

View File

@ -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!"); }
}
}

View File

@ -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);
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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]);
}
}

View File

@ -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.");
}
}

View File

@ -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) ;

View File

@ -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);

View File

@ -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);
}

View File

@ -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");