19/11/25 Master to upcoming merge (#8295)

This commit is contained in:
hedara90 2025-11-20 14:05:17 +01:00 committed by GitHub
commit 6836f1e89e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
103 changed files with 2018 additions and 1151 deletions

View File

@ -468,6 +468,16 @@
"contributions": [
"code"
]
},
{
"login": "leo60228",
"name": "leo60228",
"avatar_url": "https://avatars.githubusercontent.com/u/8355305?v=4",
"profile": "https://vriska.dev",
"contributions": [
"doc",
"data"
]
}
],
"contributorsPerLine": 7,

View File

@ -77,6 +77,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<td align="center" valign="top" width="14.28%"><a href="https://github.com/MandL27"><img src="https://avatars.githubusercontent.com/u/10366615?v=4?s=100" width="100px;" alt="MandL27"/><br /><sub><b>MandL27</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=MandL27" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/cawtds"><img src="https://avatars.githubusercontent.com/u/38510667?v=4?s=100" width="100px;" alt="cawtds"/><br /><sub><b>cawtds</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=cawtds" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/fdeblasio"><img src="https://avatars.githubusercontent.com/u/35279583?v=4?s=100" width="100px;" alt="Frank DeBlasio"/><br /><sub><b>Frank DeBlasio</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=fdeblasio" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://vriska.dev"><img src="https://avatars.githubusercontent.com/u/8355305?v=4?s=100" width="100px;" alt="leo60228"/><br /><sub><b>leo60228</b></sub></a><br /><a href="https://github.com/rh-hideout/pokeemerald-expansion/commits?author=leo60228" title="Documentation">📖</a> <a href="#data-leo60228" title="Data">🔣</a></td>
</tr>
</tbody>
<tfoot>

View File

@ -1809,7 +1809,6 @@
.macro setmoveeffect effect:req
sethword sMOVE_EFFECT, \effect
sethword sSAVED_MOVE_EFFECT, \effect
.endm
.macro sethword dst:req, value:req

View File

@ -169,7 +169,10 @@
.endm
@ Copies the value of source into destination.
.macro copyvar destination:req, source:req
.macro copyvar destination:req, source:req, warn=TRUE
.if \warn && !((\source >= VARS_START && \source <= VARS_END) || (\source >= SPECIAL_VARS_START && \source <= SPECIAL_VARS_END))
.warning "copyvar with a value that is not a VAR_ constant; did you mean setvar instead?"
.endif
.byte SCR_OP_COPYVAR
.2byte \destination
.2byte \source

View File

@ -255,23 +255,23 @@ gBattleAnimMove_Tailwind::
createvisualtask AnimTask_TranslateMonEllipticalRespectSide, 2, ANIM_ATTACKER, 24, 6, 4, 4
createvisualtask AnimTask_TraceMonBlended, 2, 0, 4, 7, 10
playsewithpan SE_M_DOUBLE_TEAM, SOUND_PAN_ATTACKER
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 10, 2304, 96, 0
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 10, 2304, 96, 1
delay 12
playsewithpan SE_M_DOUBLE_TEAM, SOUND_PAN_ATTACKER
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 90, 2048, 96, 0
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 90, 2048, 96, 1
delay 12
playsewithpan SE_M_DOUBLE_TEAM, SOUND_PAN_ATTACKER
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 50, 2560, 96, 0
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 50, 2560, 96, 1
delay 12
playsewithpan SE_M_DOUBLE_TEAM, SOUND_PAN_ATTACKER
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 20, 2304, 96, 0
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 20, 2304, 96, 1
delay 12
playsewithpan SE_M_DOUBLE_TEAM, SOUND_PAN_ATTACKER
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 70, 1984, 96, 0
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 70, 1984, 96, 1
delay 12
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 0, 2816, 96, 0
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 0, 2816, 96, 1
delay 10
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 60, 2560, 96, 0
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 60, 2560, 96, 1
waitforvisualfinish
stopsound
call UnsetHighSpeedBg
@ -285,23 +285,23 @@ gBattleAnimGeneral_Tailwind::
playsewithpan SE_M_GUST, SOUND_PAN_ATTACKER
call SetHighSpeedBg
setalpha 12, 8
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 10, 2304, 96, 0
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 10, 2304, 96, 1
delay 12
playsewithpan SE_M_DOUBLE_TEAM, SOUND_PAN_ATTACKER
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 90, 2048, 96, 0
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 90, 2048, 96, 1
delay 12
playsewithpan SE_M_DOUBLE_TEAM, SOUND_PAN_ATTACKER
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 50, 2560, 96, 0
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 50, 2560, 96, 1
delay 12
playsewithpan SE_M_DOUBLE_TEAM, SOUND_PAN_ATTACKER
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 20, 2304, 96, 0
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 20, 2304, 96, 1
delay 12
playsewithpan SE_M_DOUBLE_TEAM, SOUND_PAN_ATTACKER
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 70, 1984, 96, 0
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 70, 1984, 96, 1
delay 12
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 0, 2816, 96, 0
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 0, 2816, 96, 1
delay 10
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 60, 2560, 96, 0
createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 60, 2560, 96, 1
waitforvisualfinish
stopsound
call UnsetHighSpeedBg
@ -32027,7 +32027,8 @@ gBattleAnimGeneral_Rainbow::
createvisualtask AnimTask_BlendBattleAnimPal, 10, (F_PAL_BG | F_PAL_BATTLERS_2), 1, 6, 0, RGB_WHITE
waitforvisualfinish
delay 30
fadetobg BG_RAINBOW
goto SetRainbowBackground
AnimGeneral_RainbowContinue:
panse_adjustnone SE_M_ABSORB_2, SOUND_PAN_ATTACKER, SOUND_PAN_TARGET, +1, 0
delay 90
blendoff
@ -32035,6 +32036,14 @@ gBattleAnimGeneral_Rainbow::
waitbgfadein
clearmonbg ANIM_ATK_PARTNER
end
SetRainbowBackground:
createvisualtask AnimTask_GetAttackerSide, 2
jumprettrue SetRainbowBgOppoentSide
fadetobg BG_RAINBOW_PLAYER
goto AnimGeneral_RainbowContinue
SetRainbowBgOppoentSide:
fadetobg BG_RAINBOW_OPPONENT
goto AnimGeneral_RainbowContinue
gBattleAnimGeneral_SeaOfFire::
loadspritegfx ANIM_TAG_SMALL_EMBER

View File

@ -349,7 +349,11 @@ LilycoveCity_ContestLobby_EventScript_SetMasterContestType::
@ Functionally unused
LilycoveCity_ContestLobby_EventScript_SetDebug::
setflag FLAG_HIDE_LILYCOVE_MUSEUM_CURATOR
copyvar VAR_LILYCOVE_MUSEUM_2F_STATE, 1
#ifdef UBFIX
setvar VAR_LILYCOVE_MUSEUM_2F_STATE, 1
#else
copyvar VAR_LILYCOVE_MUSEUM_2F_STATE, 1, warn=FALSE
#endif
additem ITEM_CONTEST_PASS
setvar VAR_0x800B, 8
setvar VAR_CONTEST_RANK, CONTEST_RANK_MASTER

View File

@ -76,7 +76,11 @@ LilycoveCity_LilycoveMuseum_2F_EventScript_ShowExhibitHall::
applymovement LOCALID_PLAYER, LilycoveCity_LilycoveMuseum_2F_Movement_PlayerWalkInPlaceLeft
waitmovement 0
msgbox LilycoveCity_LilycoveMuseum_2F_Text_PleaseObtainPaintingsForExhibit, MSGBOX_SIGN
copyvar VAR_LILYCOVE_MUSEUM_2F_STATE, 1
#ifdef UBFIX
setvar VAR_LILYCOVE_MUSEUM_2F_STATE, 1
#else
copyvar VAR_LILYCOVE_MUSEUM_2F_STATE, 1, warn=FALSE
#endif
releaseall
end

View File

@ -6,7 +6,11 @@ SkyPillar_2F_MapScripts::
SkyPillar_2F_OnTransition:
call_if_lt VAR_SKY_PILLAR_STATE, 2, SkyPillar_2F_EventScript_CleanFloor
copyvar VAR_ICE_STEP_COUNT, 1
#ifdef UBFIX
setvar VAR_ICE_STEP_COUNT, 1
#else
copyvar VAR_ICE_STEP_COUNT, 1, warn=FALSE
#endif
end
SkyPillar_2F_EventScript_CleanFloor::

View File

@ -6,7 +6,11 @@ SkyPillar_4F_MapScripts::
SkyPillar_4F_OnTransition:
call_if_lt VAR_SKY_PILLAR_STATE, 2, SkyPillar_4F_EventScript_CleanFloor
copyvar VAR_ICE_STEP_COUNT, 1
#ifdef UBFIX
setvar VAR_ICE_STEP_COUNT, 1
#else
copyvar VAR_ICE_STEP_COUNT, 1, warn=FALSE
#endif
end
SkyPillar_4F_EventScript_CleanFloor::

View File

@ -3,7 +3,11 @@ CaveHole_CheckFallDownHole:
.2byte 0
CaveHole_FixCrackedGround:
#ifdef UBFIX
setvar VAR_ICE_STEP_COUNT, 1
#else
copyvar VAR_ICE_STEP_COUNT, 1, warn=FALSE
#endif
end
EventScript_FallDownHole::

View File

@ -1,19 +0,0 @@
JASC-PAL
0100
16
109 92 75
255 255 255
255 107 122
255 200 102
255 255 107
143 255 160
107 255 255
107 129 255
220 114 255
199 255 250
232 240 248
224 232 240
208 224 240
191 202 224
183 189 202
157 166 181

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1004 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 501 B

After

Width:  |  Height:  |  Size: 505 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 895 B

After

Width:  |  Height:  |  Size: 833 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 672 B

After

Width:  |  Height:  |  Size: 703 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 534 B

After

Width:  |  Height:  |  Size: 523 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 925 B

After

Width:  |  Height:  |  Size: 884 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 462 B

After

Width:  |  Height:  |  Size: 422 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1002 B

After

Width:  |  Height:  |  Size: 956 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 596 B

After

Width:  |  Height:  |  Size: 584 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 538 B

After

Width:  |  Height:  |  Size: 569 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 524 B

After

Width:  |  Height:  |  Size: 506 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 512 B

After

Width:  |  Height:  |  Size: 548 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 735 B

After

Width:  |  Height:  |  Size: 733 B

2
include/battle.h Normal file → Executable file
View File

@ -908,7 +908,7 @@ struct BattleScripting
u8 specialTrainerBattleType;
bool8 monCaught;
s32 savedDmg;
u16 savedMoveEffect; // For moves hitting multiple targets.
u16 unused_0x2c;
u16 moveEffect;
u16 unused_0x30;
u8 illusionNickHack; // To properly display nick in STRINGID_ENEMYABOUTTOSWITCHPKMN.

View File

@ -163,7 +163,7 @@ struct DamageContext
u32 randomFactor:1;
u32 updateFlags:1;
u32 isAnticipation:1;
u32 padding1:1;
u32 isSelfInflicted:1;
u32 weather:16;
u32 fixedBasePower:8;
u32 padding2:8;

View File

@ -533,8 +533,9 @@
#define BG_STEEL_BEAM_OPPONENT 78
#define BG_STEEL_BEAM_PLAYER 79
#define BG_CHLOROBLAST 80
#define BG_RAINBOW 81
#define BG_SWAMP 82
#define BG_RAINBOW_PLAYER 81
#define BG_RAINBOW_OPPONENT 82
#define BG_SWAMP 83
// table ids for general animations (sBattleAnims_General)
#define B_ANIM_STATS_CHANGE 0

View File

@ -31,7 +31,7 @@
#define sSPECIAL_TRAINER_BATTLE_TYPE (gBattleScripting + 0x26) // specialTrainerBattleType
#define sMON_CAUGHT (gBattleScripting + 0x27) // monCaught
#define sSAVED_DMG (gBattleScripting + 0x28) // savedDmg
#define sSAVED_MOVE_EFFECT (gBattleScripting + 0x2C) // savedMoveEffect
#define sUNUSED_0x2C (gBattleScripting + 0x2C) // unused_0x2c
#define sMOVE_EFFECT (gBattleScripting + 0x2E) // moveEffect
#define sUNUSED_0x30 (gBattleScripting + 0x30) // unused_0x30
#define sILLUSION_NICK_HACK (gBattleScripting + 0x32) // illusionNickHack

View File

@ -47,6 +47,7 @@ enum GenConfigTag
GEN_CONFIG_TOXIC_NEVER_MISS,
GEN_CONFIG_PARALYZE_ELECTRIC,
GEN_CONFIG_BADGE_BOOST,
GEN_CONFIG_LEAF_GUARD_PREVENTS_REST,
GEN_CONFIG_COUNT
};

View File

@ -49,7 +49,8 @@ static const u8 sGenerationalChanges[GEN_CONFIG_COUNT] =
[GEN_CONFIG_OBLIVIOUS_TAUNT] = B_OBLIVIOUS_TAUNT,
[GEN_CONFIG_TOXIC_NEVER_MISS] = B_TOXIC_NEVER_MISS,
[GEN_CONFIG_PARALYZE_ELECTRIC] = B_PARALYZE_ELECTRIC,
[GEN_CONFIG_BADGE_BOOST] = B_BADGE_BOOST
[GEN_CONFIG_BADGE_BOOST] = B_BADGE_BOOST,
[GEN_CONFIG_LEAF_GUARD_PREVENTS_REST] = B_LEAF_GUARD_PREVENTS_REST,
};
#if TESTING

View File

@ -3196,9 +3196,11 @@ extern const u32 gBattleAnimBgTilemap_Sandstorm[];
extern const u32 gBattleAnimBgImage_Sandstorm[];
// Pledge Effect field status - Rainbow
extern const u32 gBattleAnimBgImage_Rainbow[];
extern const u32 gBattleAnimBgImage_RainbowPlayer[];
extern const u32 gBattleAnimBgImage_RainbowOpponent[];
extern const u16 gBattleAnimBGPalette_Rainbow[];
extern const u32 gBattleAnimBgTilemap_Rainbow[];
extern const u32 gBattleAnimBgTilemap_RainbowPlayer[];
extern const u32 gBattleAnimBgTilemap_RainbowOpponent[];
// Pledge Effect field status - Swamp
extern const u32 gBattleAnimBgImage_Swamp[];

File diff suppressed because it is too large Load Diff

View File

@ -4067,7 +4067,7 @@ static s32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move, stru
bool32 isBattle1v1 = IsBattle1v1();
bool32 hasTwoOpponents = HasTwoOpponents(battlerAtk);
bool32 hasPartner = HasPartner(battlerAtk);
bool32 moveTargetsBothOpponents = hasTwoOpponents && (gMovesInfo[move].target & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY | MOVE_TARGET_ALL_BATTLERS));
bool32 moveTargetsBothOpponents = hasTwoOpponents && (GetMoveTarget(move) & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY | MOVE_TARGET_ALL_BATTLERS));
u32 i;
// The AI should understand that while Dynamaxed, status moves function like Protect.

View File

@ -386,7 +386,7 @@ static u32 FindMonWithMoveOfEffectiveness(u32 battler, u32 opposingBattler, uq4_
for (j = 0; j < MAX_MON_MOVES; j++)
{
move = GetMonData(&party[i], MON_DATA_MOVE1 + j);
if (move != MOVE_NONE && AI_GetMoveEffectiveness(move, battler, opposingBattler) >= effectiveness && gMovesInfo[move].power != 0)
if (move != MOVE_NONE && AI_GetMoveEffectiveness(move, battler, opposingBattler) >= effectiveness && GetMovePower(move) != 0)
return SetSwitchinAndSwitch(battler, i);
}
}
@ -422,7 +422,7 @@ static bool32 ShouldSwitchIfAllMovesBad(u32 battler)
if (gAiLogicData->effectiveness[battler][opposingBattler][moveIndex] > UQ_4_12(0.0) && aiMove != MOVE_NONE
&& !CanAbilityAbsorbMove(battler, opposingBattler, gAiLogicData->abilities[opposingBattler], aiMove, GetBattleMoveType(aiMove), AI_CHECK)
&& !CanAbilityBlockMove(battler, opposingBattler, gBattleMons[battler].ability, gAiLogicData->abilities[opposingBattler], aiMove, AI_CHECK)
&& (!ALL_MOVES_BAD_STATUS_MOVES_BAD || gMovesInfo[aiMove].power != 0)) // If using ALL_MOVES_BAD_STATUS_MOVES_BAD, then need power to be non-zero
&& (!ALL_MOVES_BAD_STATUS_MOVES_BAD || GetMovePower(aiMove) != 0)) // If using ALL_MOVES_BAD_STATUS_MOVES_BAD, then need power to be non-zero
return FALSE;
}
}
@ -2418,6 +2418,9 @@ u32 GetMostSuitableMonToSwitchInto(u32 battler, enum SwitchType switchType)
if (bestMonId != PARTY_SIZE)
return bestMonId;
if (aceMonId != PARTY_SIZE && aliveCount == 0)
return aceMonId;
bestMonId = GetBestMonTypeMatchup(party, firstId, lastId, invalidMons, battler, opposingBattler);
if (bestMonId != PARTY_SIZE)
return bestMonId;

View File

@ -4158,7 +4158,7 @@ bool32 AreMovesEquivalent(u32 battlerAtk, u32 battlerAtkPartner, u32 move, u32 p
// shared bits indicate they're meaningfully the same in some way
if (atkEffect & partnerEffect)
{
if (gMovesInfo[move].target == MOVE_TARGET_SELECTED && gMovesInfo[partnerMove].target == MOVE_TARGET_SELECTED)
if (GetMoveTarget(move) == MOVE_TARGET_SELECTED && GetMoveTarget(partnerMove) == MOVE_TARGET_SELECTED)
{
if (battlerDef == gBattleStruct->moveTarget[battlerAtkPartner])
return TRUE;
@ -4303,7 +4303,7 @@ bool32 DoesPartnerHaveSameMoveEffect(u32 battlerAtkPartner, u32 battlerDef, u32
if (GetMoveEffect(move) == GetMoveEffect(partnerMove)
&& partnerMove != MOVE_NONE)
{
if (gMovesInfo[move].target == MOVE_TARGET_SELECTED && gMovesInfo[partnerMove].target == MOVE_TARGET_SELECTED)
if (GetMoveTarget(move) == MOVE_TARGET_SELECTED && GetMoveTarget(partnerMove) == MOVE_TARGET_SELECTED)
{
return gBattleStruct->moveTarget[battlerAtkPartner] == battlerDef;
}
@ -4727,7 +4727,7 @@ bool32 IsRecycleEncouragedItem(u32 item)
static bool32 HasMoveThatChangesKOThreshold(u32 battlerId, u32 noOfHitsToFaint, u32 aiIsFaster)
{
s32 i;
s32 i, j;
u16 *moves = GetMovesArray(battlerId);
for (i = 0; i < MAX_MON_MOVES; i++)
@ -4739,16 +4739,21 @@ static bool32 HasMoveThatChangesKOThreshold(u32 battlerId, u32 noOfHitsToFaint,
if (GetMovePriority(moves[i]) > 0)
return TRUE;
switch (gMovesInfo[moves[i]].additionalEffects[i].moveEffect)
u32 additionalEffectCount = GetMoveAdditionalEffectCount(moves[i]);
for (j = 0; j < additionalEffectCount; j++)
{
case MOVE_EFFECT_SPD_MINUS_1:
case MOVE_EFFECT_SPD_MINUS_2:
{
if(aiIsFaster)
return TRUE;
}
default:
break;
const struct AdditionalEffect *additionalEffect = GetMoveAdditionalEffectById(moves[i], j);
switch (additionalEffect->moveEffect)
{
case MOVE_EFFECT_SPD_MINUS_1:
case MOVE_EFFECT_SPD_MINUS_2:
{
if(aiIsFaster)
return TRUE;
}
default:
break;
}
}
}
}

View File

@ -13,6 +13,7 @@
#include "graphics.h"
#include "main.h"
#include "malloc.h"
#include "menu.h"
#include "m4a.h"
#include "palette.h"
#include "pokemon.h"
@ -1569,10 +1570,7 @@ void LoadMoveBg(u16 bgId)
{
if (IsContest())
{
void *decompressionBuffer = Alloc(0x800);
const u32 *tilemap = gBattleAnimBackgroundTable[bgId].tilemap;
DecompressDataWithHeaderWram(tilemap, decompressionBuffer);
void *decompressionBuffer = malloc_and_decompress(gBattleAnimBackgroundTable[bgId].tilemap, NULL);
RelocateBattleBgPal(GetBattleBgPaletteNum(), decompressionBuffer, 0x100, FALSE);
DmaCopy32(3, decompressionBuffer, (void *)BG_SCREEN_ADDR(26), 0x800);
DecompressDataWithHeaderVram(gBattleAnimBackgroundTable[bgId].image, (void *)BG_SCREEN_ADDR(4));

View File

@ -5189,7 +5189,7 @@ void AnimNeedleArmSpike(struct Sprite *sprite)
{
if (gBattleAnimArgs[0] == 0)
{
if (gMovesInfo[gAnimMoveIndex].target == MOVE_TARGET_BOTH)
if (GetMoveTarget(gAnimMoveIndex) == MOVE_TARGET_BOTH)
{
SetAverageBattlerPositions(gBattleAnimAttacker, TRUE, &a, &b);
}
@ -5201,7 +5201,7 @@ void AnimNeedleArmSpike(struct Sprite *sprite)
}
else
{
if (gMovesInfo[gAnimMoveIndex].target == MOVE_TARGET_BOTH)
if (GetMoveTarget(gAnimMoveIndex) == MOVE_TARGET_BOTH)
{
SetAverageBattlerPositions(gBattleAnimTarget, TRUE, &a, &b);
}

View File

@ -9431,7 +9431,7 @@ static void SpriteCB_MaxFlutterby(struct Sprite* sprite)
{
s16 target_x;
s16 target_y;
if (gMovesInfo[gAnimMoveIndex].target == MOVE_TARGET_BOTH)
if (GetMoveTarget(gAnimMoveIndex) == MOVE_TARGET_BOTH)
{
SetAverageBattlerPositions(gBattleAnimTarget, TRUE, &target_x, &target_y);
}

View File

@ -534,13 +534,57 @@ static inline bool32 IsAcePokemon(u32 chosenMonId, u32 pokemonInBattle, u32 batt
&& CountAIAliveNonEggMonsExcept(PARTY_SIZE) != pokemonInBattle;
}
static inline bool32 IsDoubleAceSlot(u32 battler, u32 partyId)
{
u32 partyCountEnd;
if (!(gAiThinkingStruct->aiFlags[battler] & AI_FLAG_DOUBLE_ACE_POKEMON))
return FALSE;
partyCountEnd = CalculateEnemyPartyCountInSide(battler);
if (partyCountEnd == 0)
return FALSE;
if (partyId == partyCountEnd - 1)
return TRUE;
if (partyCountEnd > 1 && partyId == partyCountEnd - 2)
return TRUE;
return FALSE;
}
static inline bool32 IsDoubleAcePokemon(u32 chosenMonId, u32 pokemonInBattle, u32 battler)
{
return gAiThinkingStruct->aiFlags[battler] & AI_FLAG_DOUBLE_ACE_POKEMON
&& (chosenMonId == CalculateEnemyPartyCountInSide(battler) - 1)
&& (chosenMonId == CalculateEnemyPartyCountInSide(battler) - 2)
&& CountAIAliveNonEggMonsExcept(PARTY_SIZE) != pokemonInBattle
&& CountAIAliveNonEggMonsExcept(PARTY_SIZE-1) != pokemonInBattle;
s32 battler1, battler2, firstId, lastId;
s32 i;
if (!IsDoubleAceSlot(battler, chosenMonId))
return FALSE;
if (!IsDoubleBattle())
{
battler2 = battler1 = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
}
else
{
battler1 = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
battler2 = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT);
}
GetAIPartyIndexes(battler, &firstId, &lastId);
for (i = firstId; i < lastId; i++)
{
if (!IsValidForBattle(&gEnemyParty[i])
|| i == gBattlerPartyIndexes[battler1]
|| i == gBattlerPartyIndexes[battler2]
|| i == chosenMonId)
continue;
if (!IsAcePokemon(i, pokemonInBattle, battler) && !IsDoubleAceSlot(battler, i))
return TRUE;
}
return FALSE;
}
static void OpponentHandleChoosePokemon(u32 battler)

View File

@ -32,25 +32,9 @@ static bool32 HandleEndTurnOrder(u32 battler)
gBattleTurnCounter++;
gBattleStruct->eventState.endTurn++;
u32 i, j;
struct BattleContext ctx = {0};
for (i = 0; i < gBattlersCount; i++)
{
for (u32 i = 0; i < gBattlersCount; i++)
gBattlerByTurnOrder[i] = i;
ctx.abilities[i] = GetBattlerAbility(i);
ctx.holdEffects[i] = GetBattlerHoldEffect(i);
}
for (i = 0; i < gBattlersCount - 1; i++)
{
for (j = i + 1; j < gBattlersCount; j++)
{
ctx.battlerAtk = gBattlerByTurnOrder[i];
ctx.battlerDef = gBattlerByTurnOrder[j];
if (GetWhichBattlerFaster(&ctx, FALSE) == -1)
SwapTurnOrder(i, j);
}
}
SortBattlersBySpeed(gBattlerByTurnOrder, FALSE);
return effect;
}

View File

@ -1171,7 +1171,26 @@ static void Cmd_attackcanceler(void)
}
u32 isBounceable = MoveCanBeBouncedBack(gCurrentMove);
if (gProtectStructs[gBattlerTarget].bounceMove
bool32 bounceActive = (gProtectStructs[gBattlerTarget].bounceMove && IsBattlerAlive(gBattlerTarget));
if (!bounceActive
&& !gBattleStruct->bouncedMoveIsUsed
&& isBounceable
&& GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove) == MOVE_TARGET_OPPONENTS_FIELD)
{
u32 partner = BATTLE_PARTNER(gBattlerTarget);
if (partner < gBattlersCount
&& GetBattlerSide(partner) == GetBattlerSide(gBattlerTarget)
&& gProtectStructs[partner].bounceMove
&& IsBattlerAlive(partner))
{
gBattlerTarget = partner;
bounceActive = TRUE;
}
}
if (bounceActive
&& isBounceable
&& !gBattleStruct->bouncedMoveIsUsed)
{
@ -6309,9 +6328,10 @@ static void Cmd_moveend(void)
// Set ShellTrap to activate after the attacker's turn if target was hit by a physical move.
if (GetMoveEffect(gChosenMoveByBattler[gBattlerTarget]) == EFFECT_SHELL_TRAP
&& IsBattleMovePhysical(gCurrentMove)
&& IsBattlerTurnDamaged(gBattlerTarget)
&& gBattlerTarget != gBattlerAttacker
&& !IsBattlerAlly(gBattlerTarget, gBattlerAttacker)
&& gProtectStructs[gBattlerTarget].physicalDmg
&& gProtectStructs[gBattlerTarget].physicalBattlerId == gBattlerAttacker
&& !TestIfSheerForceAffected(gBattlerAttacker, gCurrentMove))
{
@ -6444,7 +6464,6 @@ static void Cmd_moveend(void)
gBattleStruct->moveTarget[gBattlerAttacker] = gBattlerTarget = nextTarget; // Fix for moxie spread moves
gBattleScripting.moveendState = 0;
MoveValuesCleanUp();
gBattleScripting.moveEffect = gBattleScripting.savedMoveEffect;
// Edge cases for moves that shouldn't repeat their own script
if (moveEffect == EFFECT_EXPLOSION
@ -6497,7 +6516,7 @@ static void Cmd_moveend(void)
if (!(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT)
&& !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE)
&& gMultiHitCounter
&& !(moveEffect == EFFECT_PRESENT && gBattleStruct->presentBasePower == 0)) // Silly edge case
&& !(moveEffect == EFFECT_PRESENT && gBattleStruct->presentBasePower == 0)) // Parental Bond edge case
{
gMultiHitCounter--;
if (!IsBattlerAlive(gBattlerTarget) && moveEffect != EFFECT_DRAGON_DARTS)
@ -6506,7 +6525,9 @@ static void Cmd_moveend(void)
gBattleScripting.multihitString[4]++;
if (gMultiHitCounter == 0)
{
if (GetMoveEffectArg_MoveProperty(gCurrentMove) == MOVE_EFFECT_SCALE_SHOT && !NoAliveMonsForEitherParty())
if (moveEffect == EFFECT_MULTI_HIT
&& GetMoveEffectArg_MoveProperty(gCurrentMove) == MOVE_EFFECT_SCALE_SHOT
&& !NoAliveMonsForEitherParty())
BattleScriptCall(BattleScript_ScaleShot);
else
BattleScriptCall(BattleScript_MultiHitPrintStrings);
@ -11402,17 +11423,26 @@ static void Cmd_trysetencore(void)
}
if ((IsMoveEncoreBanned(gLastMoves[gBattlerTarget]))
|| i == MAX_MON_MOVES
|| gLastMoves[gBattlerTarget] == MOVE_NONE
|| gLastMoves[gBattlerTarget] == MOVE_UNAVAILABLE)
|| gLastMoves[gBattlerTarget] == MOVE_UNAVAILABLE
|| gBattleMons[gBattlerTarget].pp[i] == 0
|| gDisableStructs[gBattlerTarget].encoredMove != MOVE_NONE
|| GetMoveEffect(gChosenMoveByBattler[gBattlerTarget]) == EFFECT_SHELL_TRAP)
{
i = MAX_MON_MOVES;
gBattlescriptCurrInstr = cmd->failInstr;
}
if (gDisableStructs[gBattlerTarget].encoredMove == MOVE_NONE
&& i != MAX_MON_MOVES && gBattleMons[gBattlerTarget].pp[i] != 0)
else
{
gDisableStructs[gBattlerTarget].encoredMove = gBattleMons[gBattlerTarget].moves[i];
gDisableStructs[gBattlerTarget].encoredMovePos = i;
// If the target's selected move is not the same as the move being Encored into,
// the target will select a random opposing target
// Redirection such as Follow Me is already covered in HandleAction_UseMove of battle_util.c
if (gDisableStructs[gBattlerTarget].encoredMove != GetChosenMoveFromPosition(gBattlerTarget))
gBattleStruct->moveTarget[gBattlerTarget] = SetRandomTarget(gBattlerTarget);
// Encore always lasts 3 turns, but we need to account for a scenario where Encore changes the move during the same turn.
if (HasBattlerActedThisTurn(gBattlerTarget))
gDisableStructs[gBattlerTarget].encoreTimer = 4;
@ -11420,10 +11450,6 @@ static void Cmd_trysetencore(void)
gDisableStructs[gBattlerTarget].encoreTimer = 3;
gBattlescriptCurrInstr = cmd->nextInstr;
}
else
{
gBattlescriptCurrInstr = cmd->failInstr;
}
}
static void Cmd_painsplitdmgcalc(void)
@ -14024,13 +14050,16 @@ static void Cmd_displaydexinfo(void)
{
CMD_ARGS();
struct Pokemon *mon = GetBattlerMon(GetCatchingBattler());
u32 caughtBattler = GetCatchingBattler();
struct Pokemon *mon = GetBattlerMon(caughtBattler);
u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL);
switch (gBattleCommunication[0])
{
case 0:
BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, RGB_BLACK);
ClearTemporarySpeciesSpriteData(caughtBattler, FALSE, FALSE);
BattleLoadMonSpriteGfx(mon, caughtBattler);
gBattleCommunication[0]++;
break;
case 1:
@ -17903,7 +17932,7 @@ void BS_JumpIfAbilityPreventsRest(void)
NATIVE_ARGS(u8 battler, const u8 *jumpInstr);
u32 battler = GetBattlerForBattleScript(cmd->battler);
u32 ability = GetBattlerAbility(battler);
if (B_LEAF_GUARD_PREVENTS_REST >= GEN_5 && IsLeafGuardProtected(battler, ability))
if (GetGenConfig(GEN_CONFIG_LEAF_GUARD_PREVENTS_REST) >= GEN_5 && IsLeafGuardProtected(battler, ability))
gBattlescriptCurrInstr = cmd->jumpInstr;
else if (IsShieldsDownProtected(battler, ability))
gBattlescriptCurrInstr = cmd->jumpInstr;

View File

@ -1279,6 +1279,7 @@ static void TrySetBattleSeminarShow(void)
ctx.isCrit = FALSE;
ctx.randomFactor = FALSE;
ctx.updateFlags = FALSE;
ctx.isSelfInflicted = FALSE;
ctx.fixedBasePower = powerOverride;
gBattleStruct->moveDamage[gBattlerTarget] = CalculateMoveDamage(&ctx);
dmgByMove[i] = gBattleStruct->moveDamage[gBattlerTarget];

View File

@ -438,7 +438,6 @@ void HandleAction_UseMove(void)
gMultiHitCounter = 0;
gBattleScripting.savedDmg = 0;
gBattleCommunication[MISS_TYPE] = 0;
gBattleScripting.savedMoveEffect = 0;
gCurrMovePos = gChosenMovePos = gBattleStruct->chosenMovePositions[gBattlerAttacker];
// choose move
@ -2076,6 +2075,7 @@ static enum MoveCanceler CancelerObedience(struct BattleContext *ctx)
dmgCtx.isCrit = FALSE;
dmgCtx.randomFactor = FALSE;
dmgCtx.updateFlags = TRUE;
dmgCtx.isSelfInflicted = TRUE;
dmgCtx.fixedBasePower = 40;
gBattleStruct->moveDamage[ctx->battlerAtk] = CalculateMoveDamage(&dmgCtx);
gBattlescriptCurrInstr = BattleScript_IgnoresAndHitsItself;
@ -2242,6 +2242,7 @@ static enum MoveCanceler CancelerConfused(struct BattleContext *ctx)
dmgCtx.isCrit = FALSE;
dmgCtx.randomFactor = FALSE;
dmgCtx.updateFlags = TRUE;
dmgCtx.isSelfInflicted = TRUE;
dmgCtx.fixedBasePower = 40;
gBattleStruct->passiveHpUpdate[ctx->battlerAtk] = CalculateMoveDamage(&dmgCtx);
gProtectStructs[ctx->battlerAtk].confusionSelfDmg = TRUE;
@ -7401,6 +7402,24 @@ static bool32 IsRuinStatusActive(u32 fieldEffect)
return FALSE;
}
static inline uq4_12_t ApplyOffensiveBadgeBoost(uq4_12_t modifier, u32 battler, u32 move)
{
if (ShouldGetStatBadgeBoost(B_FLAG_BADGE_BOOST_ATTACK, battler) && IsBattleMovePhysical(move))
modifier = uq4_12_multiply_half_down(modifier, GetBadgeBoostModifier());
if (ShouldGetStatBadgeBoost(B_FLAG_BADGE_BOOST_SPATK, battler) && IsBattleMoveSpecial(move))
modifier = uq4_12_multiply_half_down(modifier, GetBadgeBoostModifier());
return modifier;
}
static inline uq4_12_t ApplyDefensiveBadgeBoost(uq4_12_t modifier, u32 battler, u32 move)
{
if (ShouldGetStatBadgeBoost(B_FLAG_BADGE_BOOST_DEFENSE, battler) && IsBattleMovePhysical(move))
modifier = uq4_12_multiply_half_down(modifier, GetBadgeBoostModifier());
if (ShouldGetStatBadgeBoost(B_FLAG_BADGE_BOOST_SPDEF, battler) && IsBattleMoveSpecial(move))
modifier = uq4_12_multiply_half_down(modifier, GetBadgeBoostModifier());
return modifier;
}
static inline u32 CalcAttackStat(struct DamageContext *ctx)
{
u8 atkStage;
@ -7472,6 +7491,9 @@ static inline u32 CalcAttackStat(struct DamageContext *ctx)
// apply attack stat modifiers
modifier = UQ_4_12(1.0);
if (ctx->isSelfInflicted)
return uq4_12_multiply_by_int_half_down(ApplyOffensiveBadgeBoost(modifier, battlerAtk, move), atkStat);
// attacker's abilities
switch (ctx->abilityAtk)
{
@ -7672,11 +7694,7 @@ static inline u32 CalcAttackStat(struct DamageContext *ctx)
break;
}
// The offensive stats of a Player's Pokémon are boosted by x1.1 (+10%) if they have the corresponding flags set (eg. Badges)
if (ShouldGetStatBadgeBoost(B_FLAG_BADGE_BOOST_ATTACK, battlerAtk) && IsBattleMovePhysical(move))
modifier = uq4_12_multiply_half_down(modifier, GetBadgeBoostModifier());
if (ShouldGetStatBadgeBoost(B_FLAG_BADGE_BOOST_SPATK, battlerAtk) && IsBattleMoveSpecial(move))
modifier = uq4_12_multiply_half_down(modifier, GetBadgeBoostModifier());
modifier = ApplyOffensiveBadgeBoost(modifier, battlerAtk, move);
return uq4_12_multiply_by_int_half_down(modifier, atkStat);
}
@ -7761,6 +7779,9 @@ static inline u32 CalcDefenseStat(struct DamageContext *ctx)
// apply defense stat modifiers
modifier = UQ_4_12(1.0);
if (ctx->isSelfInflicted)
return uq4_12_multiply_by_int_half_down(ApplyDefensiveBadgeBoost(modifier, battlerDef, move), defStat);
// target's abilities
switch (ctx->abilityDef)
{
@ -7854,11 +7875,7 @@ static inline u32 CalcDefenseStat(struct DamageContext *ctx)
if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_ICE) && IsBattlerWeatherAffected(battlerDef, B_WEATHER_SNOW) && usesDefStat)
modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5));
// The defensive stats of a Player's Pokémon are boosted by x1.1 (+10%) if they have the corresponding flags set (eg. Badges)
if (ShouldGetStatBadgeBoost(B_FLAG_BADGE_BOOST_DEFENSE, battlerDef) && IsBattleMovePhysical(move))
modifier = uq4_12_multiply_half_down(modifier, GetBadgeBoostModifier());
if (ShouldGetStatBadgeBoost(B_FLAG_BADGE_BOOST_SPDEF, battlerDef) && IsBattleMoveSpecial(move))
modifier = uq4_12_multiply_half_down(modifier, GetBadgeBoostModifier());
modifier = ApplyDefensiveBadgeBoost(modifier, battlerDef, move);
return uq4_12_multiply_by_int_half_down(modifier, defStat);
}
@ -10310,9 +10327,9 @@ bool32 HasWeatherEffect(void)
void UpdateStallMons(void)
{
if (IsBattlerTurnDamaged(gBattlerTarget) || IsBattlerProtected(gBattlerAttacker, gBattlerTarget, gCurrentMove) || gMovesInfo[gCurrentMove].category == DAMAGE_CATEGORY_STATUS)
if (IsBattlerTurnDamaged(gBattlerTarget) || IsBattlerProtected(gBattlerAttacker, gBattlerTarget, gCurrentMove) || GetMoveCategory(gCurrentMove) == DAMAGE_CATEGORY_STATUS)
return;
if (!IsDoubleBattle() || gMovesInfo[gCurrentMove].target == MOVE_TARGET_SELECTED)
if (!IsDoubleBattle() || GetMoveTarget(gCurrentMove) == MOVE_TARGET_SELECTED)
{
enum Type moveType = GetBattleMoveType(gCurrentMove); // Probably doesn't handle dynamic move types right now
enum Ability abilityAtk = GetBattlerAbility(gBattlerAttacker);

View File

@ -2031,6 +2031,7 @@ const struct BattleAnimBackground gBattleAnimBackgroundTable[] =
[BG_STEEL_BEAM_OPPONENT] = {gBattleAnimBgImage_Highspeed, gBattleAnimBgPalette_SteelBeam, gBattleAnimBgTilemap_HighspeedOpponent},
[BG_STEEL_BEAM_PLAYER] = {gBattleAnimBgImage_Highspeed, gBattleAnimBgPalette_SteelBeam, gBattleAnimBgTilemap_HighspeedPlayer},
[BG_CHLOROBLAST] = {gBattleAnimBgImage_HydroCannon, gBattleAnimBgPalette_Chloroblast, gBattleAnimBgTilemap_HydroCannon},
[BG_RAINBOW] = {gBattleAnimBgImage_Rainbow, gBattleAnimBGPalette_Rainbow, gBattleAnimBgTilemap_Rainbow},
[BG_RAINBOW_PLAYER] = {gBattleAnimBgImage_RainbowPlayer, gBattleAnimBGPalette_Rainbow, gBattleAnimBgTilemap_RainbowPlayer},
[BG_RAINBOW_OPPONENT] = {gBattleAnimBgImage_RainbowOpponent, gBattleAnimBGPalette_Rainbow, gBattleAnimBgTilemap_RainbowOpponent},
[BG_SWAMP] = {gBattleAnimBgImage_Swamp, gBattleAnimBGPalette_Swamp, gBattleAnimBgTilemap_Swamp},
};

View File

@ -2627,37 +2627,29 @@ void UpdateLightSprite(struct Sprite *sprite)
return;
}
// Note: Don't set window registers during hardware fade!
switch (sprite->sLightType)
if (sprite->sLightType == LIGHT_TYPE_BALL)
{
default:
case LIGHT_TYPE_BALL:
if (gPaletteFade.active) // if palette fade is active, don't flicker since the timer won't be updated
{
Weather_SetBlendCoeffs(7, BASE_SHADOW_INTENSITY);
sprite->invisible = FALSE;
}
else if (gPlayerAvatar.tileTransitionState)
{
Weather_SetBlendCoeffs(7, BASE_SHADOW_INTENSITY); // As long as the second coefficient stays 12, shadows will not change
sprite->invisible = FALSE;
if (GetSpritePaletteTagByPaletteNum(sprite->oam.paletteNum) == OBJ_EVENT_PAL_TAG_LIGHT_2)
LoadSpritePaletteInSlot(&sObjectEventSpritePalettes[FindObjectEventPaletteIndexByTag(OBJ_EVENT_PAL_TAG_LIGHT)], sprite->oam.paletteNum);
}
else if ((sprite->invisible = gTimeUpdateCounter & 1))
{
Weather_SetBlendCoeffs(7, BASE_SHADOW_INTENSITY);
sprite->invisible = FALSE;
if (GetSpritePaletteTagByPaletteNum(sprite->oam.paletteNum) == OBJ_EVENT_PAL_TAG_LIGHT_2)
LoadSpritePaletteInSlot(&sObjectEventSpritePalettes[FindObjectEventPaletteIndexByTag(OBJ_EVENT_PAL_TAG_LIGHT)], sprite->oam.paletteNum);
}
break;
case LIGHT_TYPE_PKMN_CENTER_SIGN:
case LIGHT_TYPE_POKE_MART_SIGN:
Weather_SetBlendCoeffs(12, BASE_SHADOW_INTENSITY);
} else {
sprite->invisible = FALSE;
break;
}
// Note: Don't set window registers during hardware fade!
Weather_SetBlendCoeffs(7, BASE_SHADOW_INTENSITY);
}
// Spawn a light at a map coordinate

View File

@ -393,6 +393,7 @@ void SetMewAboveGrass(void)
LoadSpritePalette(&gSpritePalette_GeneralFieldEffect1);
UpdateSpritePaletteWithWeather(IndexOfSpritePaletteTag(gSpritePalette_GeneralFieldEffect1.tag), FALSE);
gSprites[mew->spriteId].subspriteTableNum = 1;
x = mew->currentCoords.x;
y = mew->currentCoords.y;

View File

@ -2125,7 +2125,6 @@ bool8 ObjectMovingOnRockStairs(struct ObjectEvent *objectEvent, u8 direction)
s16 x = objectEvent->currentCoords.x;
s16 y = objectEvent->currentCoords.y;
// TODO followers on sideways stairs
if (IsFollowerVisible() && GetFollowerObject() != NULL && (objectEvent->isPlayer || objectEvent->localId == OBJ_EVENT_ID_FOLLOWER))
return FALSE;

View File

@ -1087,6 +1087,10 @@ u32 DetermineFollowerNPCState(struct ObjectEvent *follower, u32 state, u32 direc
RETURN_STATE(MOVEMENT_ACTION_WALK_NORMAL_DOWN, direction);
// Slow stairs.
case MOVEMENT_ACTION_WALK_SLOW_STAIRS_DOWN ... MOVEMENT_ACTION_WALK_SLOW_STAIRS_RIGHT:
RETURN_STATE(MOVEMENT_ACTION_WALK_SLOW_STAIRS_DOWN, direction);
default:
return MOVEMENT_INVALID;
}

View File

@ -1673,9 +1673,11 @@ const u32 gBattleAnimSpriteGfx_WhiteShadow[] = INCBIN_U32("graphics/battle_anims
const u16 gBattleAnimSpritePal_WhiteShadow[] = INCBIN_U16("graphics/battle_anims/sprites/white_shadow.gbapal");
// Pledge Effect field status - Rainbow
const u32 gBattleAnimBgImage_Rainbow[] = INCBIN_U32("graphics/battle_anims/backgrounds/rainbow.4bpp.smol");
const u16 gBattleAnimBGPalette_Rainbow[] = INCBIN_U16("graphics/battle_anims/backgrounds/rainbow.gbapal");
const u32 gBattleAnimBgTilemap_Rainbow[] = INCBIN_U32("graphics/battle_anims/backgrounds/rainbow.bin.smolTM");
const u32 gBattleAnimBgImage_RainbowPlayer[] = INCBIN_U32("graphics/battle_anims/backgrounds/rainbow_player_tile.4bpp.smol");
const u32 gBattleAnimBgImage_RainbowOpponent[] = INCBIN_U32("graphics/battle_anims/backgrounds/rainbow_opponent_tile.4bpp.smol");
const u16 gBattleAnimBGPalette_Rainbow[] = INCBIN_U16("graphics/battle_anims/backgrounds/rainbow_player_tile.gbapal");
const u32 gBattleAnimBgTilemap_RainbowPlayer[] = INCBIN_U32("graphics/battle_anims/backgrounds/rainbow_player_tile.bin.smolTM");
const u32 gBattleAnimBgTilemap_RainbowOpponent[] = INCBIN_U32("graphics/battle_anims/backgrounds/rainbow_opponent_tile.bin.smolTM");
// Pledge Effect field status - Swamp
const u32 gBattleAnimBgImage_Swamp[] = INCBIN_U32("graphics/battle_anims/backgrounds/swampswizzle.4bpp.smol");

View File

@ -2916,8 +2916,8 @@ static s32 CompareItemsAlphabetically(enum Pocket pocketId, struct ItemSlot item
if (pocketId == POCKET_TM_HM)
{
name1 = gMovesInfo[GetTMHMMoveId(GetItemTMHMIndex(item1.itemId))].name;
name2 = gMovesInfo[GetTMHMMoveId(GetItemTMHMIndex(item2.itemId))].name;
name1 = GetMoveName(GetTMHMMoveId(GetItemTMHMIndex(item1.itemId)));
name2 = GetMoveName(GetTMHMMoveId(GetItemTMHMIndex(item2.itemId)));
}
else
{

View File

@ -1269,7 +1269,8 @@ bool32 CannotUseItemsInBattle(u16 itemId, struct Pokemon *mon)
switch (battleUsage)
{
case EFFECT_ITEM_INCREASE_STAT:
if (gBattleMons[gBattlerInMenuId].statStages[GetItemEffect(itemId)[1]] == MAX_STAT_STAGE)
u32 ability = GetBattlerAbility(gBattlerInMenuId);
if (CompareStat(gBattlerInMenuId, GetItemEffect(itemId)[1], MAX_STAT_STAGE, CMP_EQUAL, ability))
cannotUse = TRUE;
break;
case EFFECT_ITEM_SET_FOCUS_ENERGY:
@ -1308,11 +1309,12 @@ bool32 CannotUseItemsInBattle(u16 itemId, struct Pokemon *mon)
case EFFECT_ITEM_INCREASE_ALL_STATS:
{
u32 ability = GetBattlerAbility(gBattlerInMenuId);
cannotUse = TRUE;
for (i = STAT_ATK; i < NUM_STATS; i++)
{
if (CompareStat(gBattlerInMenuId, i, MAX_STAT_STAGE, CMP_EQUAL, ability))
if (!CompareStat(gBattlerInMenuId, i, MAX_STAT_STAGE, CMP_EQUAL, ability))
{
cannotUse = TRUE;
cannotUse = FALSE;
break;
}
}

View File

@ -202,7 +202,7 @@ static const struct OamData sOamData_CeilingCrumbleSmall =
static const struct SpriteTemplate sSpriteTemplate_CeilingCrumbleSmall =
{
.tileTag = TAG_CEILING_CRUMBLE,
.paletteTag = TAG_NONE,
.paletteTag = TAG_CEILING_CRUMBLE,
.oam = &sOamData_CeilingCrumbleSmall,
.anims = sAnims_CeilingCrumbleSmall,
.images = NULL,
@ -241,7 +241,7 @@ static const struct OamData sOamData_CeilingCrumbleLarge =
static const struct SpriteTemplate sSpriteTemplate_CeilingCrumbleLarge =
{
.tileTag = TAG_CEILING_CRUMBLE,
.paletteTag = TAG_NONE,
.paletteTag = TAG_CEILING_CRUMBLE,
.oam = &sOamData_CeilingCrumbleLarge,
.anims = sAnims_CeilingCrumbleLarge,
.images = NULL,
@ -420,6 +420,7 @@ static void IncrementCeilingCrumbleFinishedCount(void)
void DoMirageTowerCeilingCrumble(void)
{
LoadSpritePaletteWithTag(sMirageTowerCrumbles_Palette, TAG_CEILING_CRUMBLE);
LoadSpriteSheets(sCeilingCrumbleSpriteSheets);
CreateCeilingCrumbleSprites();
CreateTask(WaitCeilingCrumble, 8);
@ -454,17 +455,12 @@ static void CreateCeilingCrumbleSprites(void)
{
spriteId = CreateSprite(&sSpriteTemplate_CeilingCrumbleLarge, sCeilingCrumblePositions[i][0] + 120, sCeilingCrumblePositions[i][1], 8);
gSprites[spriteId].oam.priority = 0;
// These sprites use color index 11 from the player's sprite palette. This probably wasn't intentional.
// The palettes for Brendan and May have different shades of green at this index, so the color of these sprites changes
// depending on the player's gender (and neither shade of green particularly fits a crumbling yellow/brown ceiling).
gSprites[spriteId].oam.paletteNum = PALSLOT_PLAYER;
gSprites[spriteId].sIndex = i;
}
for (i = 0; i < ARRAY_COUNT(sCeilingCrumblePositions); i++)
{
spriteId = CreateSprite(&sSpriteTemplate_CeilingCrumbleSmall, sCeilingCrumblePositions[i][0] + 115, sCeilingCrumblePositions[i][1] - 3, 8);
gSprites[spriteId].oam.priority = 0;
gSprites[spriteId].oam.paletteNum = PALSLOT_PLAYER;
gSprites[spriteId].sIndex = i;
}
}

View File

@ -6392,8 +6392,9 @@ static void SwapFusionMonMoves(struct Pokemon *mon, const u16 moveTable[][2], u3
{
if (move == moveTable[j][oldMoveIndex])
{
u32 pp = GetMovePP(moveTable[j][newMoveIndex]);
SetMonData(mon, MON_DATA_MOVE1 + i, &moveTable[j][newMoveIndex]);
SetMonData(mon, MON_DATA_PP1 + i, &gMovesInfo[moveTable[j][newMoveIndex]].pp);
SetMonData(mon, MON_DATA_PP1 + i, &pp);
}
}
}

View File

@ -4089,6 +4089,12 @@ static void UNUSED HighlightScreenSelectBarItem(u8 selectedScreen, u16 unused)
#define tPersonalityLo data[14]
#define tPersonalityHi data[15]
// Types palettes need to be loaded at a different slot than anticipated by gTypesInfo
// to avoid overlapping with caught mon sprite palette slot
// Normal type info palette slots: 13, 14 and 15
// Caught mon palette slot: 15
#define TYPE_INFO_PALETTE_NUM_OFFSET -1
void Task_DisplayCaughtMonDexPageHGSS(u8 taskId)
{
u8 spriteId;
@ -4343,7 +4349,7 @@ static void SetTypeIconPosAndPal(u8 typeId, u8 x, u8 y, u8 spriteArrayId)
sprite = &gSprites[sPokedexView->typeIconSpriteIds[spriteArrayId]];
StartSpriteAnim(sprite, typeId);
if (typeId < NUMBER_OF_MON_TYPES)
sprite->oam.paletteNum = gTypesInfo[typeId].palette;
sprite->oam.paletteNum = gTypesInfo[typeId].palette + TYPE_INFO_PALETTE_NUM_OFFSET;
else
sprite->oam.paletteNum = gContestCategoryInfo[typeId - NUMBER_OF_MON_TYPES].palette;
sprite->x = x + 16;
@ -4386,7 +4392,8 @@ static void CreateTypeIconSprites(void)
u8 i;
LoadCompressedSpriteSheet(&gSpriteSheet_MoveTypes);
LoadPalette(gMoveTypes_Pal, 0x1D0, 0x60);
u32 paletteNum = gTypesInfo[TYPE_NORMAL].palette + TYPE_INFO_PALETTE_NUM_OFFSET;
LoadPalette(gMoveTypes_Pal, OBJ_PLTT_ID(paletteNum), 3 * PLTT_SIZE_4BPP);
for (i = 0; i < 2; i++)
{
if (sPokedexView->typeIconSpriteIds[i] == 0xFF)
@ -4566,6 +4573,7 @@ static u16 CreateSizeScreenTrainerPic(u16 species, s16 x, s16 y, s8 paletteSlot)
return CreateTrainerPicSprite(species, TRUE, x, y, paletteSlot, TAG_NONE);
}
#undef TYPE_INFO_PALETTE_NUM_OFFSET
//************************************
//* *

View File

@ -34,6 +34,7 @@ static void Task_Fanfare(u8 taskId);
static void CreateFanfareTask(void);
static void RestoreBGMVolumeAfterPokemonCry(void);
// The 1st argument in the table is the length of the fanfare, measured in frames. This is calculated by taking the duration of the midi file, multiplying by 59.72750056960583, and rounding up to the next nearest integer.
static const struct Fanfare sFanfares[] = {
[FANFARE_LEVEL_UP] = { MUS_LEVEL_UP, 80 },
[FANFARE_OBTAIN_ITEM] = { MUS_OBTAIN_ITEM, 160 },

View File

@ -493,7 +493,7 @@ static u8 CheckTrainer(u8 objectEventId)
if (GetTrainerFlagFromScriptPointer(trainerBattlePtr))
{
//If there is a rematch, we want to trigger the approach sequence
if (GetRematchFromScriptPointer(trainerBattlePtr))
if (I_VS_SEEKER_CHARGING && GetRematchFromScriptPointer(trainerBattlePtr))
{
trainerBattlePtr = NULL;
numTrainers = 0xFF;

View File

@ -5,62 +5,62 @@ DOUBLE_BATTLE_TEST("Aura Break inverts Fairy Aura's effect")
{
s16 damage[3];
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_XERNEAS) { Ability(ABILITY_FAIRY_AURA); }
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_ZYGARDE_50) { Ability(ABILITY_AURA_BREAK); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_PLAY_ROUGH, target:opponentLeft); }
TURN { MOVE(playerLeft, MOVE_PLAY_ROUGH, target:opponentLeft); SWITCH(playerRight, 2); }
TURN { MOVE(playerLeft, MOVE_PLAY_ROUGH, target:opponentLeft); SWITCH(opponentRight, 2); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[0]);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_XERNEAS) { Ability(ABILITY_FAIRY_AURA); }
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_ZYGARDE_50) { Ability(ABILITY_AURA_BREAK); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_PLAY_ROUGH, target:opponentLeft); }
TURN { MOVE(playerLeft, MOVE_PLAY_ROUGH, target:opponentLeft); SWITCH(playerRight, 2); }
TURN { MOVE(playerLeft, MOVE_PLAY_ROUGH, target:opponentLeft); SWITCH(opponentRight, 2); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[0]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[1]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[1]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[2]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[2]);
} THEN {
EXPECT_MUL_EQ(damage[0], UQ_4_12(1.33), damage[1]);
EXPECT_MUL_EQ(damage[0], UQ_4_12(0.75), damage[2]);
}
} THEN {
EXPECT_MUL_EQ(damage[0], UQ_4_12(1.33), damage[1]);
EXPECT_MUL_EQ(damage[0], UQ_4_12(0.75), damage[2]);
}
}
DOUBLE_BATTLE_TEST("Aura Break inverts Dark Aura's effect")
{
s16 damage[3];
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_YVELTAL) { Ability(ABILITY_DARK_AURA); }
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_ZYGARDE_50) { Ability(ABILITY_AURA_BREAK); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_BITE, target:opponentLeft); }
TURN { MOVE(playerLeft, MOVE_BITE, target:opponentLeft); SWITCH(playerRight, 2); }
TURN { MOVE(playerLeft, MOVE_BITE, target:opponentLeft); SWITCH(opponentRight, 2); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[0]);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_YVELTAL) { Ability(ABILITY_DARK_AURA); }
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_ZYGARDE_50) { Ability(ABILITY_AURA_BREAK); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_BITE, target:opponentLeft); }
TURN { MOVE(playerLeft, MOVE_BITE, target:opponentLeft); SWITCH(playerRight, 2); }
TURN { MOVE(playerLeft, MOVE_BITE, target:opponentLeft); SWITCH(opponentRight, 2); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[0]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[1]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[1]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[2]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[2]);
} THEN {
EXPECT_MUL_EQ(damage[0], UQ_4_12(1.33), damage[1]);
EXPECT_MUL_EQ(damage[0], UQ_4_12(0.75), damage[2]);
}
} THEN {
EXPECT_MUL_EQ(damage[0], UQ_4_12(1.33), damage[1]);
EXPECT_MUL_EQ(damage[0], UQ_4_12(0.75), damage[2]);
}
}
DOUBLE_BATTLE_TEST("Aura Break ignores Mold Breaker abilities")
@ -73,43 +73,43 @@ DOUBLE_BATTLE_TEST("Aura Break ignores Mold Breaker abilities")
PARAMETRIZE { species = SPECIES_ZEKROM, ability = ABILITY_TERAVOLT; }
PARAMETRIZE { species = SPECIES_RESHIRAM, ability = ABILITY_TURBOBLAZE; }
GIVEN {
PLAYER(species) { Ability(ability); Level(50); }
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_YVELTAL) { Ability(ABILITY_DARK_AURA); }
PLAYER(SPECIES_XERNEAS) { Ability(ABILITY_FAIRY_AURA); }
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_ZYGARDE_50) { Ability(ABILITY_AURA_BREAK); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_BITE, target: opponentLeft); }
TURN { MOVE(playerLeft, MOVE_PLAY_ROUGH, target: opponentLeft); }
TURN { MOVE(playerLeft, MOVE_BITE, target: opponentLeft); SWITCH(playerRight, 2); }
TURN { MOVE(playerLeft, MOVE_PLAY_ROUGH, target: opponentLeft); SWITCH(playerRight, 3); }
TURN { MOVE(playerLeft, MOVE_BITE, target: opponentLeft); SWITCH(playerRight, 2); SWITCH(opponentRight, 2); }
TURN { MOVE(playerLeft, MOVE_PLAY_ROUGH, target: opponentLeft); SWITCH(playerRight, 3); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[0]);
GIVEN {
PLAYER(species) { Ability(ability); Level(50); }
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_YVELTAL) { Ability(ABILITY_DARK_AURA); }
PLAYER(SPECIES_XERNEAS) { Ability(ABILITY_FAIRY_AURA); }
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_ZYGARDE_50) { Ability(ABILITY_AURA_BREAK); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_BITE, target: opponentLeft); }
TURN { MOVE(playerLeft, MOVE_PLAY_ROUGH, target: opponentLeft); }
TURN { MOVE(playerLeft, MOVE_BITE, target: opponentLeft); SWITCH(playerRight, 2); }
TURN { MOVE(playerLeft, MOVE_PLAY_ROUGH, target: opponentLeft); SWITCH(playerRight, 3); }
TURN { MOVE(playerLeft, MOVE_BITE, target: opponentLeft); SWITCH(playerRight, 2); SWITCH(opponentRight, 2); }
TURN { MOVE(playerLeft, MOVE_PLAY_ROUGH, target: opponentLeft); SWITCH(playerRight, 3); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[0]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[1]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[1]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[2]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[2]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[3]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[3]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[4]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[4]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[5]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[5]);
} THEN {
EXPECT_MUL_EQ(damage[0], UQ_4_12(1.33), damage[2]);
EXPECT_MUL_EQ(damage[0], UQ_4_12(0.75), damage[4]);
EXPECT_MUL_EQ(damage[1], UQ_4_12(1.33), damage[3]);
EXPECT_MUL_EQ(damage[1], UQ_4_12(0.75), damage[5]);
}
EXPECT_MUL_EQ(damage[0], UQ_4_12(1.33), damage[2]);
EXPECT_MUL_EQ(damage[0], UQ_4_12(0.75), damage[4]);
EXPECT_MUL_EQ(damage[1], UQ_4_12(1.33), damage[3]);
EXPECT_MUL_EQ(damage[1], UQ_4_12(0.75), damage[5]);
}
}

View File

@ -35,7 +35,21 @@ SINGLE_BATTLE_TEST("Bad Dreams causes the sleeping enemy Pokemon to lose 1/8 of
}
}
TO_DO_BATTLE_TEST("Bad Dreams affects Pokémon with Comatose")
SINGLE_BATTLE_TEST("Bad Dreams causes Pokémon with Comatose to lose 1/8 of HP")
{
GIVEN {
PLAYER(SPECIES_DARKRAI);
OPPONENT(SPECIES_KOMALA) { Ability(ABILITY_COMATOSE); }
} WHEN {
TURN {;}
} SCENE {
ABILITY_POPUP(player, ABILITY_BAD_DREAMS);
MESSAGE("The opposing Komala is tormented!");
HP_BAR(opponent);
} THEN {
EXPECT_EQ(opponent->hp, opponent->maxHP - opponent->maxHP / 8);
}
}
DOUBLE_BATTLE_TEST("Bad Dreams does not activate if only the partner Pokemon is sleeping")
{

View File

@ -1,4 +1,108 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("TODO: Write Dark Aura (Ability) test titles")
DOUBLE_BATTLE_TEST("Dark Aura increases the power of all Dark-type attacks by 33%")
{
s16 damage[8];
GIVEN {
PLAYER(SPECIES_YVELTAL) { Ability(ABILITY_DARK_AURA); }
PLAYER(SPECIES_LINOONE);
PLAYER(SPECIES_LINOONE);
OPPONENT(SPECIES_LINOONE);
OPPONENT(SPECIES_LINOONE);
} WHEN {
TURN { MOVE(playerRight, MOVE_SKILL_SWAP, target: playerLeft); }
TURN { SWITCH(playerLeft, 2); }
TURN {
MOVE(playerLeft, MOVE_BITE, target:opponentLeft, secondaryEffect:FALSE);
MOVE(playerRight, MOVE_BITE, target:opponentRight, secondaryEffect:FALSE);
MOVE(opponentLeft, MOVE_BITE, target:playerLeft, secondaryEffect:FALSE);
MOVE(opponentRight, MOVE_BITE, target:playerRight, secondaryEffect:FALSE);
}
TURN { MOVE(opponentLeft, MOVE_GASTRO_ACID, target:playerRight); }
TURN {
MOVE(playerLeft, MOVE_BITE, target:opponentLeft, secondaryEffect:FALSE);
MOVE(playerRight, MOVE_BITE, target:opponentRight, secondaryEffect:FALSE);
MOVE(opponentLeft, MOVE_BITE, target:playerLeft, secondaryEffect:FALSE);
MOVE(opponentRight, MOVE_BITE, target:playerRight, secondaryEffect:FALSE);
}
} SCENE {
// Turn 1
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[0]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, playerRight);
HP_BAR(opponentRight, captureDamage: &damage[1]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, opponentLeft);
HP_BAR(playerLeft, captureDamage: &damage[2]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, opponentRight);
HP_BAR(playerRight, captureDamage: &damage[3]);
// Turn 2
ANIMATION(ANIM_TYPE_MOVE, MOVE_GASTRO_ACID, opponentLeft);
// Turn 3
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[4]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, playerRight);
HP_BAR(opponentRight, captureDamage: &damage[5]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, opponentLeft);
HP_BAR(playerLeft, captureDamage: &damage[6]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, opponentRight);
HP_BAR(playerRight, captureDamage: &damage[7]);
} THEN {
EXPECT_MUL_EQ(damage[4], UQ_4_12(1.33), damage[0]);
EXPECT_MUL_EQ(damage[5], UQ_4_12(1.33), damage[1]);
EXPECT_MUL_EQ(damage[6], UQ_4_12(1.33), damage[2]);
EXPECT_MUL_EQ(damage[7], UQ_4_12(1.33), damage[3]);
}
}
DOUBLE_BATTLE_TEST("Dark Aura's effect doesn't stack multiple times")
{
s16 damage[6];
GIVEN {
PLAYER(SPECIES_YVELTAL) { Ability(ABILITY_DARK_AURA); }
PLAYER(SPECIES_WOBBUFFET) { HP(9999); MaxHP(9999); }
PLAYER(SPECIES_YVELTAL) { Ability(ABILITY_DARK_AURA); }
OPPONENT(SPECIES_WOBBUFFET) { HP(9999); MaxHP(9999); }
OPPONENT(SPECIES_WOBBUFFET) { HP(9999); MaxHP(9999); }
} WHEN {
TURN {
MOVE(playerLeft, MOVE_BITE, target:opponentLeft, secondaryEffect:FALSE);
MOVE(opponentLeft, MOVE_BITE, target:playerLeft, secondaryEffect:FALSE);
MOVE(opponentRight, MOVE_BITE, target:playerLeft, secondaryEffect:FALSE);
}
TURN { SWITCH(playerRight, 2); }
TURN {
MOVE(playerLeft, MOVE_BITE, target:opponentLeft, secondaryEffect:FALSE);
MOVE(opponentLeft, MOVE_BITE, target:playerLeft, secondaryEffect:FALSE);
MOVE(opponentRight, MOVE_BITE, target:playerLeft, secondaryEffect:FALSE);
}
} SCENE {
// Turn 1
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[0]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, opponentLeft);
HP_BAR(playerLeft, captureDamage: &damage[1]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, opponentRight);
HP_BAR(playerLeft, captureDamage: &damage[2]);
// Turn 2
SWITCH_OUT_MESSAGE("Wobbuffet");
SEND_IN_MESSAGE("Yveltal");
// Turn 3
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[3]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, opponentLeft);
HP_BAR(playerLeft, captureDamage: &damage[4]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, opponentRight);
HP_BAR(playerLeft, captureDamage: &damage[5]);
} THEN {
EXPECT_EQ(damage[3], damage[0]);
EXPECT_EQ(damage[4], damage[1]);
EXPECT_EQ(damage[5], damage[2]);
}
}

View File

@ -1,4 +1,108 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("TODO: Write Fairy Aura (Ability) test titles")
DOUBLE_BATTLE_TEST("Fairy Aura increases the power of all Fairy-type attacks by 33%")
{
s16 damage[8];
GIVEN {
PLAYER(SPECIES_XERNEAS) { Ability(ABILITY_FAIRY_AURA); }
PLAYER(SPECIES_LINOONE);
PLAYER(SPECIES_LINOONE);
OPPONENT(SPECIES_LINOONE);
OPPONENT(SPECIES_LINOONE);
} WHEN {
TURN { MOVE(playerRight, MOVE_SKILL_SWAP, target: playerLeft); }
TURN { SWITCH(playerLeft, 2); }
TURN {
MOVE(playerLeft, MOVE_PLAY_ROUGH, target:opponentLeft, secondaryEffect:FALSE);
MOVE(playerRight, MOVE_PLAY_ROUGH, target:opponentRight, secondaryEffect:FALSE);
MOVE(opponentLeft, MOVE_PLAY_ROUGH, target:playerLeft, secondaryEffect:FALSE);
MOVE(opponentRight, MOVE_PLAY_ROUGH, target:playerRight, secondaryEffect:FALSE);
}
TURN { MOVE(opponentLeft, MOVE_GASTRO_ACID, target:playerRight); }
TURN {
MOVE(playerLeft, MOVE_PLAY_ROUGH, target:opponentLeft, secondaryEffect:FALSE);
MOVE(playerRight, MOVE_PLAY_ROUGH, target:opponentRight, secondaryEffect:FALSE);
MOVE(opponentLeft, MOVE_PLAY_ROUGH, target:playerLeft, secondaryEffect:FALSE);
MOVE(opponentRight, MOVE_PLAY_ROUGH, target:playerRight, secondaryEffect:FALSE);
}
} SCENE {
// Turn 1
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[0]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, playerRight);
HP_BAR(opponentRight, captureDamage: &damage[1]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, opponentLeft);
HP_BAR(playerLeft, captureDamage: &damage[2]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, opponentRight);
HP_BAR(playerRight, captureDamage: &damage[3]);
// Turn 2
ANIMATION(ANIM_TYPE_MOVE, MOVE_GASTRO_ACID, opponentLeft);
// Turn 3
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[4]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, playerRight);
HP_BAR(opponentRight, captureDamage: &damage[5]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, opponentLeft);
HP_BAR(playerLeft, captureDamage: &damage[6]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, opponentRight);
HP_BAR(playerRight, captureDamage: &damage[7]);
} THEN {
EXPECT_MUL_EQ(damage[4], UQ_4_12(1.33), damage[0]);
EXPECT_MUL_EQ(damage[5], UQ_4_12(1.33), damage[1]);
EXPECT_MUL_EQ(damage[6], UQ_4_12(1.33), damage[2]);
EXPECT_MUL_EQ(damage[7], UQ_4_12(1.33), damage[3]);
}
}
DOUBLE_BATTLE_TEST("Fairy Aura's effect doesn't stack multiple times")
{
s16 damage[6];
GIVEN {
PLAYER(SPECIES_XERNEAS) { Ability(ABILITY_FAIRY_AURA); }
PLAYER(SPECIES_WOBBUFFET) { HP(9999); MaxHP(9999); }
PLAYER(SPECIES_XERNEAS) { Ability(ABILITY_FAIRY_AURA); }
OPPONENT(SPECIES_WOBBUFFET) { HP(9999); MaxHP(9999); }
OPPONENT(SPECIES_WOBBUFFET) { HP(9999); MaxHP(9999); }
} WHEN {
TURN {
MOVE(playerLeft, MOVE_PLAY_ROUGH, target:opponentLeft, secondaryEffect:FALSE);
MOVE(opponentLeft, MOVE_PLAY_ROUGH, target:playerLeft, secondaryEffect:FALSE);
MOVE(opponentRight, MOVE_PLAY_ROUGH, target:playerLeft, secondaryEffect:FALSE);
}
TURN { SWITCH(playerRight, 2); }
TURN {
MOVE(playerLeft, MOVE_PLAY_ROUGH, target:opponentLeft, secondaryEffect:FALSE);
MOVE(opponentLeft, MOVE_PLAY_ROUGH, target:playerLeft, secondaryEffect:FALSE);
MOVE(opponentRight, MOVE_PLAY_ROUGH, target:playerLeft, secondaryEffect:FALSE);
}
} SCENE {
// Turn 1
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[0]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, opponentLeft);
HP_BAR(playerLeft, captureDamage: &damage[1]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, opponentRight);
HP_BAR(playerLeft, captureDamage: &damage[2]);
// Turn 2
SWITCH_OUT_MESSAGE("Wobbuffet");
SEND_IN_MESSAGE("Xerneas");
// Turn 3
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[3]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, opponentLeft);
HP_BAR(playerLeft, captureDamage: &damage[4]);
ANIMATION(ANIM_TYPE_MOVE, MOVE_PLAY_ROUGH, opponentRight);
HP_BAR(playerLeft, captureDamage: &damage[5]);
} THEN {
EXPECT_EQ(damage[3], damage[0]);
EXPECT_EQ(damage[4], damage[1]);
EXPECT_EQ(damage[5], damage[2]);
}
}

View File

@ -9,7 +9,7 @@ SINGLE_BATTLE_TEST("Filter reduces damage to Super Effective moves by 0.75", s16
GIVEN {
ASSUME(gSpeciesInfo[SPECIES_MR_MIME].types[0] == TYPE_PSYCHIC);
ASSUME(gSpeciesInfo[SPECIES_MR_MIME].types[1] == TYPE_FAIRY);
ASSUME(gMovesInfo[MOVE_POISON_JAB].type == TYPE_POISON);
ASSUME(GetMoveType(MOVE_POISON_JAB) == TYPE_POISON);
ASSUME(gTypeEffectivenessTable[TYPE_POISON][TYPE_FAIRY] > UQ_4_12(1.0));
ASSUME(gTypeEffectivenessTable[TYPE_POISON][TYPE_PSYCHIC] == UQ_4_12(1.0));
PLAYER(SPECIES_MR_MIME) { Ability(ability); }

View File

@ -1,4 +1,21 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("TODO: Write Flare Boost (Ability) test titles")
SINGLE_BATTLE_TEST("Flare Boost increases Sp. Attack by 50% when the Pokémon is burned", s16 damage)
{
u32 status1;
PARAMETRIZE { status1 = STATUS1_NONE; }
PARAMETRIZE { status1 = STATUS1_BURN; }
GIVEN {
ASSUME(GetMoveCategory(MOVE_SWIFT) == DAMAGE_CATEGORY_SPECIAL);
PLAYER(SPECIES_DRIFBLIM) { Ability(ABILITY_FLARE_BOOST); Status1(status1); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SWIFT); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SWIFT, player);
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage);
}
}

View File

@ -1,4 +1,44 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("TODO: Write Fur Coat (Ability) test titles")
ASSUMPTIONS
{
ASSUME(GetMoveCategory(MOVE_SCRATCH) == DAMAGE_CATEGORY_PHYSICAL);
}
SINGLE_BATTLE_TEST("Fur Coat doubles Defense", s16 damage)
{
u32 ability;
PARAMETRIZE { ability = ABILITY_FUR_COAT; }
PARAMETRIZE { ability = ABILITY_RATTLED; }
GIVEN {
PLAYER(SPECIES_PERSIAN_ALOLA) { Ability(ability); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_SCRATCH); }
} SCENE {
HP_BAR(player, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[1].damage);
}
}
SINGLE_BATTLE_TEST("Fur Coat has no effect on self-inflicted confusion damage", s16 damage)
{
u32 ability;
PARAMETRIZE { ability = ABILITY_FUR_COAT; }
PARAMETRIZE { ability = ABILITY_RATTLED; }
GIVEN {
PLAYER(SPECIES_PERSIAN_ALOLA) { Ability(ability); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_CONFUSE_RAY); MOVE(player, MOVE_POUND, WITH_RNG(RNG_CONFUSION, TRUE)); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_CONFUSE_RAY, opponent);
HP_BAR(player, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_EQ(results[0].damage, results[1].damage);
}
}

View File

@ -1,4 +1,26 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("TODO: Write Iron Fist (Ability) test titles")
SINGLE_BATTLE_TEST("Iron Fist increases the power of punching moves by 20%", s16 damage)
{
u32 move, ability;
PARAMETRIZE { move = MOVE_BULLET_PUNCH; ability = ABILITY_IRON_FIST; }
PARAMETRIZE { move = MOVE_BULLET_PUNCH; ability = ABILITY_BLAZE; }
PARAMETRIZE { move = MOVE_SCRATCH; ability = ABILITY_IRON_FIST; }
PARAMETRIZE { move = MOVE_SCRATCH; ability = ABILITY_BLAZE; }
GIVEN {
ASSUME(IsPunchingMove(MOVE_BULLET_PUNCH));
ASSUME(!IsPunchingMove(MOVE_SCRATCH));
ASSUME(GetMovePower(MOVE_BULLET_PUNCH) == GetMovePower(MOVE_SCRATCH));
PLAYER(SPECIES_CHIMCHAR) { Ability(ability); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, move); }
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[1].damage, Q_4_12(1.2), results[0].damage); // Iron Fist affects punching moves
EXPECT_EQ(results[2].damage, results[3].damage); // Iron Fist does not affect non-punching moves
}
}

View File

@ -31,7 +31,41 @@ SINGLE_BATTLE_TEST("Leaf Guard prevents non-volatile status conditions in sun")
}
}
TO_DO_BATTLE_TEST("Leaf Guard doesn't prevent non-volatile status conditions if Cloud Nine/Air Lock is on the field");
SINGLE_BATTLE_TEST("Leaf Guard doesn't prevent non-volatile status conditions if Cloud Nine/Air Lock is on the field")
{
u32 move, species, ability;
u16 status;
PARAMETRIZE { move = MOVE_WILL_O_WISP; status = STATUS1_BURN; species = SPECIES_GOLDUCK; ability = ABILITY_CLOUD_NINE; }
PARAMETRIZE { move = MOVE_HYPNOSIS; status = STATUS1_SLEEP; species = SPECIES_GOLDUCK; ability = ABILITY_CLOUD_NINE; }
PARAMETRIZE { move = MOVE_THUNDER_WAVE; status = STATUS1_PARALYSIS; species = SPECIES_GOLDUCK; ability = ABILITY_CLOUD_NINE; }
PARAMETRIZE { move = MOVE_TOXIC; status = STATUS1_TOXIC_POISON; species = SPECIES_GOLDUCK; ability = ABILITY_CLOUD_NINE; }
PARAMETRIZE { move = MOVE_WILL_O_WISP; status = STATUS1_BURN; species = SPECIES_RAYQUAZA; ability = ABILITY_AIR_LOCK; }
PARAMETRIZE { move = MOVE_HYPNOSIS; status = STATUS1_SLEEP; species = SPECIES_RAYQUAZA; ability = ABILITY_AIR_LOCK; }
PARAMETRIZE { move = MOVE_THUNDER_WAVE; status = STATUS1_PARALYSIS; species = SPECIES_RAYQUAZA; ability = ABILITY_AIR_LOCK; }
PARAMETRIZE { move = MOVE_TOXIC; status = STATUS1_TOXIC_POISON; species = SPECIES_RAYQUAZA; ability = ABILITY_AIR_LOCK; }
// PARAMETRIZE { move = MOVE_POWDER_SNOW; status = STATUS1_FREEZE; } // Pointless since you can't freeze in sunlight anyway
GIVEN {
ASSUME(GetMoveEffect(MOVE_WILL_O_WISP) == EFFECT_NON_VOLATILE_STATUS);
ASSUME(GetMoveNonVolatileStatus(MOVE_WILL_O_WISP) == MOVE_EFFECT_BURN);
ASSUME(GetMoveEffect(MOVE_HYPNOSIS) == EFFECT_NON_VOLATILE_STATUS);
ASSUME(GetMoveNonVolatileStatus(MOVE_HYPNOSIS) == MOVE_EFFECT_SLEEP);
ASSUME(GetMoveEffect(MOVE_THUNDER_WAVE) == EFFECT_NON_VOLATILE_STATUS);
ASSUME(GetMoveNonVolatileStatus(MOVE_THUNDER_WAVE) == MOVE_EFFECT_PARALYSIS);
ASSUME(GetMoveEffect(MOVE_TOXIC) == EFFECT_NON_VOLATILE_STATUS);
ASSUME(GetMoveNonVolatileStatus(MOVE_TOXIC) == MOVE_EFFECT_TOXIC);
PLAYER(SPECIES_LEAFEON) { Ability(ABILITY_LEAF_GUARD); }
OPPONENT(species) { Ability(ability); }
} WHEN {
TURN { MOVE(player, MOVE_SUNNY_DAY); MOVE(opponent, move); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
NONE_OF {
ABILITY_POPUP(player, ABILITY_LEAF_GUARD);
MESSAGE("It doesn't affect Leafeon…");
}
STATUS_ICON(player, status);
}
}
SINGLE_BATTLE_TEST("Leaf Guard prevents status conditions from Flame Orb and Toxic Orb")
{
@ -50,29 +84,82 @@ SINGLE_BATTLE_TEST("Leaf Guard prevents status conditions from Flame Orb and Tox
NONE_OF { MESSAGE("Leafeon was burned!"); STATUS_ICON(player, burn: TRUE); }
}
else {
NONE_OF { MESSAGE("Leafeon was badly poisoned!"); STATUS_ICON(player, poison: TRUE); }
NONE_OF { MESSAGE("Leafeon was badly poisoned!"); STATUS_ICON(player, badPoison: TRUE); }
}
}
}
TO_DO_BATTLE_TEST("Leaf Guard doesn't prevent status conditions from Flame Orb and Toxic Orb if Cloud Nine/Air Lock is on the field");
SINGLE_BATTLE_TEST("Leaf Guard prevents Rest during sun")
SINGLE_BATTLE_TEST("Leaf Guard doesn't prevent status conditions from Flame Orb and Toxic Orb if Cloud Nine/Air Lock is on the field")
{
u32 item, species, ability;
PARAMETRIZE { item = ITEM_FLAME_ORB; species = SPECIES_GOLDUCK; ability = ABILITY_CLOUD_NINE; }
PARAMETRIZE { item = ITEM_TOXIC_ORB; species = SPECIES_GOLDUCK; ability = ABILITY_CLOUD_NINE; }
PARAMETRIZE { item = ITEM_FLAME_ORB; species = SPECIES_RAYQUAZA; ability = ABILITY_AIR_LOCK; }
PARAMETRIZE { item = ITEM_TOXIC_ORB; species = SPECIES_RAYQUAZA; ability = ABILITY_AIR_LOCK; }
GIVEN {
ASSUME(B_LEAF_GUARD_PREVENTS_REST >= GEN_5);
ASSUME(gItemsInfo[ITEM_FLAME_ORB].holdEffect == HOLD_EFFECT_FLAME_ORB);
ASSUME(gItemsInfo[ITEM_TOXIC_ORB].holdEffect == HOLD_EFFECT_TOXIC_ORB);
PLAYER(SPECIES_LEAFEON) { Ability(ABILITY_LEAF_GUARD); Item(item); }
OPPONENT(species) { Ability(ability); }
} WHEN {
TURN { MOVE(player, MOVE_SUNNY_DAY); }
} SCENE {
if (item == ITEM_FLAME_ORB) {
MESSAGE("Leafeon was burned!");
STATUS_ICON(player, burn: TRUE);
}
else {
MESSAGE("Leafeon was badly poisoned!");
STATUS_ICON(player, badPoison: TRUE);
}
}
}
SINGLE_BATTLE_TEST("Leaf Guard prevents Rest during sun (Gen 5+)")
{
u32 gen;
PARAMETRIZE { gen = GEN_4; }
PARAMETRIZE { gen = GEN_5; }
GIVEN {
WITH_CONFIG(GEN_CONFIG_LEAF_GUARD_PREVENTS_REST, gen);
ASSUME(GetMoveEffect(MOVE_REST) == EFFECT_REST);
PLAYER(SPECIES_LEAFEON) { Ability(ABILITY_LEAF_GUARD); HP(100); MaxHP(200); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_SUNNY_DAY); MOVE(player, MOVE_REST); }
} SCENE {
MESSAGE("But it failed!");
NONE_OF {
if (gen >= GEN_5) {
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_REST, player);
STATUS_ICON(player, sleep: TRUE);
HP_BAR(player);
}
}
else {
STATUS_ICON(player, sleep: TRUE);
ANIMATION(ANIM_TYPE_MOVE, MOVE_REST, player);
HP_BAR(player);
}
}
}
TO_DO_BATTLE_TEST("Leaf Guard doesn't prevent Rest if Cloud Nine/Air Lock is on the field");
SINGLE_BATTLE_TEST("Leaf Guard doesn't prevent Rest if Cloud Nine/Air Lock is on the field")
{
u32 species, ability;
PARAMETRIZE { species = SPECIES_GOLDUCK; ability = ABILITY_CLOUD_NINE; }
PARAMETRIZE { species = SPECIES_GOLDUCK; ability = ABILITY_CLOUD_NINE; }
PARAMETRIZE { species = SPECIES_RAYQUAZA; ability = ABILITY_AIR_LOCK; }
PARAMETRIZE { species = SPECIES_RAYQUAZA; ability = ABILITY_AIR_LOCK; }
GIVEN {
WITH_CONFIG(GEN_CONFIG_LEAF_GUARD_PREVENTS_REST, GEN_5);
ASSUME(GetMoveEffect(MOVE_REST) == EFFECT_REST);
PLAYER(SPECIES_LEAFEON) { Ability(ABILITY_LEAF_GUARD); HP(100); MaxHP(200); }
OPPONENT(species) { Ability(ability); }
} WHEN {
TURN { MOVE(opponent, MOVE_SUNNY_DAY); MOVE(player, MOVE_REST); }
} SCENE {
STATUS_ICON(player, sleep: TRUE);
ANIMATION(ANIM_TYPE_MOVE, MOVE_REST, player);
HP_BAR(player);
}
}

View File

@ -1,4 +1,4 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("TODO: Write Libero (Ability) test titles")
// Tests for Libero are handled in test/battle/ability/protean.c

View File

@ -42,6 +42,7 @@ SINGLE_BATTLE_TEST("Magic Bounce bounces back powder moves")
SINGLE_BATTLE_TEST("Magic Bounce cannot bounce back powder moves against Grass Types")
{
GIVEN {
WITH_CONFIG(GEN_CONFIG_POWDER_GRASS, GEN_6);
ASSUME(IsPowderMove(MOVE_STUN_SPORE));
ASSUME(GetSpeciesType(SPECIES_ODDISH, 0) == TYPE_GRASS);
PLAYER(SPECIES_ODDISH);

View File

@ -1,4 +1,22 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("TODO: Write Mold Breaker (Ability) test titles")
SINGLE_BATTLE_TEST("Mold Breaker cancels damage reduction from Ice Scales", s16 damage)
{
u16 ability;
PARAMETRIZE { ability = ABILITY_SHADOW_TAG; }
PARAMETRIZE { ability = ABILITY_MOLD_BREAKER; }
GIVEN {
ASSUME(GetMoveCategory(MOVE_PSYCHIC) == DAMAGE_CATEGORY_SPECIAL);
PLAYER(SPECIES_WOBBUFFET) { Ability(ability); }
OPPONENT(SPECIES_FROSMOTH) { Ability(ABILITY_ICE_SCALES); }
} WHEN {
TURN { MOVE(player, MOVE_PSYCHIC); }
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[1].damage, UQ_4_12(0.5), results[0].damage);
}
}
TO_DO_BATTLE_TEST("TODO: Write more Mold Breaker (Ability) test titles")

View File

@ -71,17 +71,29 @@ DOUBLE_BATTLE_TEST("Overcoat blocks damage from hail")
}
}
SINGLE_BATTLE_TEST("Overcoat blocks Effect Spore's effect")
SINGLE_BATTLE_TEST("Overcoat blocks Effect Spore's effect (Gen6+)")
{
u32 config;
PARAMETRIZE { config = GEN_5; }
PARAMETRIZE { config = GEN_6; }
GIVEN {
WITH_CONFIG(GEN_CONFIG_POWDER_OVERCOAT, config);
PLAYER(SPECIES_PINECO) {Ability(ABILITY_OVERCOAT);}
OPPONENT(SPECIES_SHROOMISH) {Ability(ABILITY_EFFECT_SPORE);}
} WHEN {
TURN { MOVE(player, MOVE_TACKLE, WITH_RNG(RNG_EFFECT_SPORE, 1)); }
} SCENE {
MESSAGE("Pineco used Tackle!");
NOT ABILITY_POPUP(opponent, ABILITY_EFFECT_SPORE);
if (config == GEN_6) {
NOT ABILITY_POPUP(opponent, ABILITY_EFFECT_SPORE);
}
else {
ABILITY_POPUP(opponent, ABILITY_EFFECT_SPORE);
}
} THEN {
EXPECT_EQ(player->status1, 0);
if (config == GEN_6)
EXPECT_EQ(player->status1, 0);
else
EXPECT_NE(player->status1, 0);
}
}

View File

@ -354,6 +354,22 @@ SINGLE_BATTLE_TEST("Parental Bond does not trigger on two turn attacks")
}
}
SINGLE_BATTLE_TEST("Parental Bond does not trigger Scale Shot effect on Drain Punch")
{
GIVEN {
PLAYER(SPECIES_KANGASKHAN) { Item(ITEM_KANGASKHANITE); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_DRAIN_PUNCH, gimmick: GIMMICK_MEGA); MOVE(opponent, MOVE_CELEBRATE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAIN_PUNCH, player);
NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
} THEN {
EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE);
EXPECT_EQ(player->statStages[STAT_SPEED], DEFAULT_STAT_STAGE);
}
}
TO_DO_BATTLE_TEST("Parental Bond tests");
// Temporary TODO: Convert Bulbapedia description into tests.

View File

@ -9,7 +9,7 @@ SINGLE_BATTLE_TEST("Prism Armor reduces damage to Super Effective moves by 0.75"
GIVEN {
ASSUME(gSpeciesInfo[SPECIES_NECROZMA].types[0] == TYPE_PSYCHIC);
ASSUME(gSpeciesInfo[SPECIES_NECROZMA].types[1] == TYPE_PSYCHIC);
ASSUME(gMovesInfo[MOVE_DARK_PULSE].type == TYPE_DARK);
ASSUME(GetMoveType(MOVE_DARK_PULSE) == TYPE_DARK);
ASSUME(gTypeEffectivenessTable[TYPE_POISON][TYPE_FAIRY] > UQ_4_12(1.0));
ASSUME(gTypeEffectivenessTable[TYPE_POISON][TYPE_PSYCHIC] == UQ_4_12(1.0));
PLAYER(SPECIES_NECROZMA);

View File

@ -1,12 +1,15 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Protean changes the type of the user to the move used every time (Gen6-8)")
SINGLE_BATTLE_TEST("Protean/Libero changes the type of the user to the move used every time (Gen6-8)")
{
u32 ability, species;
PARAMETRIZE { ability = ABILITY_PROTEAN; species = SPECIES_KECLEON; }
PARAMETRIZE { ability = ABILITY_LIBERO; species = SPECIES_RABOOT; }
GIVEN {
WITH_CONFIG(GEN_PROTEAN_LIBERO, GEN_6);
PLAYER(SPECIES_REGIROCK);
OPPONENT(SPECIES_KECLEON) { Ability(ABILITY_PROTEAN); }
OPPONENT(species) { Ability(ability); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_WATER_GUN); }
@ -15,24 +18,36 @@ SINGLE_BATTLE_TEST("Protean changes the type of the user to the move used every
TURN { SWITCH(opponent, 0); }
TURN { MOVE(opponent, MOVE_WATER_GUN); }
} SCENE {
ABILITY_POPUP(opponent, ABILITY_PROTEAN);
MESSAGE("The opposing Kecleon transformed into the Water type!");
ABILITY_POPUP(opponent, ability);
if (species == SPECIES_KECLEON)
MESSAGE("The opposing Kecleon transformed into the Water type!");
else
MESSAGE("The opposing Raboot transformed into the Water type!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, opponent);
ABILITY_POPUP(opponent, ABILITY_PROTEAN);
MESSAGE("The opposing Kecleon transformed into the Normal type!");
ABILITY_POPUP(opponent, ability);
if (species == SPECIES_KECLEON)
MESSAGE("The opposing Kecleon transformed into the Normal type!");
else
MESSAGE("The opposing Raboot transformed into the Normal type!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent);
ABILITY_POPUP(opponent, ABILITY_PROTEAN);
MESSAGE("The opposing Kecleon transformed into the Water type!");
ABILITY_POPUP(opponent, ability);
if (species == SPECIES_KECLEON)
MESSAGE("The opposing Kecleon transformed into the Water type!");
else
MESSAGE("The opposing Raboot transformed into the Water type!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, opponent);
}
}
SINGLE_BATTLE_TEST("Protean changes the type of the user only once per switch in (Gen9+)")
SINGLE_BATTLE_TEST("Protean/Libero changes the type of the user only once per switch in (Gen9+)")
{
u32 ability, species;
PARAMETRIZE { ability = ABILITY_PROTEAN; species = SPECIES_KECLEON; }
PARAMETRIZE { ability = ABILITY_LIBERO; species = SPECIES_RABOOT; }
GIVEN {
WITH_CONFIG(GEN_PROTEAN_LIBERO, GEN_9);
PLAYER(SPECIES_REGIROCK);
OPPONENT(SPECIES_KECLEON) { Ability(ABILITY_PROTEAN); }
OPPONENT(species) { Ability(ability); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_WATER_GUN); }
@ -41,31 +56,42 @@ SINGLE_BATTLE_TEST("Protean changes the type of the user only once per switch in
TURN { SWITCH(opponent, 0); }
TURN { MOVE(opponent, MOVE_WATER_GUN); }
} SCENE {
ABILITY_POPUP(opponent, ABILITY_PROTEAN);
MESSAGE("The opposing Kecleon transformed into the Water type!");
ABILITY_POPUP(opponent, ability);
if (species == SPECIES_KECLEON)
MESSAGE("The opposing Kecleon transformed into the Water type!");
else
MESSAGE("The opposing Raboot transformed into the Water type!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, opponent);
NONE_OF {
ABILITY_POPUP(opponent, ABILITY_PROTEAN);
ABILITY_POPUP(opponent, ability);
MESSAGE("The opposing Kecleon transformed into the Normal type!");
MESSAGE("The opposing Raboot transformed into the Normal type!");
}
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent);
ABILITY_POPUP(opponent, ABILITY_PROTEAN);
MESSAGE("The opposing Kecleon transformed into the Water type!");
ABILITY_POPUP(opponent, ability);
if (species == SPECIES_KECLEON)
MESSAGE("The opposing Kecleon transformed into the Water type!");
else
MESSAGE("The opposing Raboot transformed into the Water type!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, opponent);
}
}
SINGLE_BATTLE_TEST("Protean does not change the user's type when using Struggle")
SINGLE_BATTLE_TEST("Protean/Libero does not change the user's type when using Struggle")
{
u32 ability, species;
PARAMETRIZE { ability = ABILITY_PROTEAN; species = SPECIES_GRENINJA; }
PARAMETRIZE { ability = ABILITY_LIBERO; species = SPECIES_RABOOT; }
GIVEN {
PLAYER(SPECIES_REGIROCK);
OPPONENT(SPECIES_GRENINJA) { Ability(ABILITY_PROTEAN); }
OPPONENT(species) { Ability(ability); }
} WHEN {
TURN { MOVE(opponent, MOVE_STRUGGLE); }
} SCENE {
NONE_OF {
ABILITY_POPUP(opponent, ABILITY_PROTEAN);
ABILITY_POPUP(opponent, ability);
MESSAGE("The opposing Greninja transformed into the Normal type!");
MESSAGE("The opposing Raboot transformed into the Normal type!");
}
ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, opponent);
}

View File

@ -1,7 +1,7 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Sharpness increases the power of slicing moves", s16 damage)
SINGLE_BATTLE_TEST("Sharpness increases the power of slicing moves by 50%", s16 damage)
{
u32 move;
enum Ability ability;

View File

@ -9,7 +9,7 @@ SINGLE_BATTLE_TEST("Solid Rock reduces damage to Super Effective moves by 0.75",
GIVEN {
ASSUME(gSpeciesInfo[SPECIES_CARRACOSTA].types[0] == TYPE_WATER);
ASSUME(gSpeciesInfo[SPECIES_CARRACOSTA].types[1] == TYPE_ROCK);
ASSUME(gMovesInfo[MOVE_CLOSE_COMBAT].type == TYPE_FIGHTING);
ASSUME(GetMoveType(MOVE_CLOSE_COMBAT) == TYPE_FIGHTING);
ASSUME(gTypeEffectivenessTable[TYPE_FIGHTING][TYPE_ROCK] > UQ_4_12(1.0));
ASSUME(gTypeEffectivenessTable[TYPE_FIGHTING][TYPE_WATER] == UQ_4_12(1.0));
PLAYER(SPECIES_CARRACOSTA) { Ability(ability); }

View File

@ -1,4 +1,23 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("TODO: Write Toxic Boost (Ability) test titles")
SINGLE_BATTLE_TEST("Toxic Boost increases Attack by 50% when the Pokémon is poisoned", s16 damage)
{
u32 status1;
PARAMETRIZE { status1 = STATUS1_NONE; }
PARAMETRIZE { status1 = STATUS1_POISON; }
PARAMETRIZE { status1 = STATUS1_TOXIC_POISON; }
GIVEN {
ASSUME(GetMoveCategory(MOVE_SCRATCH) == DAMAGE_CATEGORY_PHYSICAL);
PLAYER(SPECIES_ZANGOOSE) { Ability(ABILITY_TOXIC_BOOST); Status1(status1); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SCRATCH); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player);
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage);
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[2].damage);
}
}

View File

@ -695,7 +695,7 @@ AI_SINGLE_BATTLE_TEST("AI won't use thawing moves if target is frozen unless it
ASSUME(GetMoveType(MOVE_EMBER) == TYPE_FIRE);
ASSUME(GetMoveCategory(MOVE_TACKLE) == DAMAGE_CATEGORY_PHYSICAL);
ASSUME(GetMoveCategory(MOVE_WATER_GUN) == DAMAGE_CATEGORY_SPECIAL);
ASSUME(gMovesInfo[MOVE_SCALD].thawsUser == TRUE);
ASSUME(MoveThawsUser(MOVE_SCALD) == TRUE);
AI_FLAGS(aiFlags | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_TACKLE); Status1(status); }
OPPONENT(SPECIES_VULPIX) { Moves(MOVE_TACKLE, aiMove); }

View File

@ -94,3 +94,35 @@ AI_DOUBLE_BATTLE_TEST("AI_FLAG_DOUBLE_ACE_POKEMON: Ace mons won't be switched in
TURN { EXPECT_SWITCH(opponentLeft, 2); }
}
}
AI_DOUBLE_BATTLE_TEST("AI_FLAG_DOUBLE_ACE_POKEMON: sends out Ace mons when no other options remain mid-battle")
{
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_SMART_SWITCHING | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_DOUBLE_ACE_POKEMON);
PLAYER(SPECIES_WOBBUFFET) { Level(50); Speed(200); Moves(MOVE_THUNDERBOLT, MOVE_CELEBRATE); SpAttack(200); }
PLAYER(SPECIES_WOBBUFFET) { Level(50); Speed(150); Moves(MOVE_THUNDERBOLT, MOVE_CELEBRATE); SpAttack(200); }
OPPONENT(SPECIES_ZIGZAGOON) { Level(5); HP(1); Speed(1); Moves(MOVE_SPLASH); }
OPPONENT(SPECIES_POOCHYENA) { Level(5); HP(1); Speed(1); Moves(MOVE_SPLASH); }
// Aces
OPPONENT(SPECIES_MIGHTYENA) { Level(50); Speed(10); Moves(MOVE_CRUNCH); }
OPPONENT(SPECIES_GENGAR) { Level(50); Speed(10); Moves(MOVE_SPLASH); }
} WHEN {
TURN {
MOVE(playerLeft, MOVE_THUNDERBOLT, target: opponentLeft);
MOVE(playerRight, MOVE_CELEBRATE);
EXPECT_MOVE(opponentLeft, MOVE_SPLASH);
EXPECT_MOVE(opponentRight, MOVE_SPLASH);
EXPECT_SEND_OUT(opponentLeft, 3);
}
TURN {
MOVE(playerLeft, MOVE_CELEBRATE);
MOVE(playerRight, MOVE_THUNDERBOLT, target: opponentRight);
EXPECT_MOVE(opponentLeft, MOVE_SPLASH);
EXPECT_MOVE(opponentRight, MOVE_SPLASH);
EXPECT_SEND_OUT(opponentRight, 2);
}
}
}

View File

@ -29,6 +29,30 @@ DOUBLE_BATTLE_TEST("End Turn Effects: First Event Block is executed correctly (d
}
}
DOUBLE_BATTLE_TEST("End Turn Effects: Effects are applied by Speed Order")
{
GIVEN {
PLAYER(SPECIES_WYNAUT) { MaxHP(200); HP(100); Speed(3); }
PLAYER(SPECIES_RILLABOOM) { MaxHP(200); HP(100); Speed(1); Ability(ABILITY_GRASSY_SURGE); }
OPPONENT(SPECIES_MEWTWO) { MaxHP(200); HP(100); Speed(2); }
OPPONENT(SPECIES_WOBBUFFET) { MaxHP(200); HP(100); Speed(4); }
} WHEN {
TURN {
MOVE(opponentLeft, MOVE_FAKE_OUT, target: playerLeft);
MOVE(playerRight, MOVE_FAKE_OUT, target: opponentRight);
}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_FAKE_OUT, opponentLeft);
HP_BAR(playerLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_FAKE_OUT, playerRight);
HP_BAR(opponentRight);
HP_BAR(opponentRight);
HP_BAR(playerLeft);
HP_BAR(opponentLeft);
HP_BAR(playerRight);
}
}
MULTI_BATTLE_TEST("End Turn Effects: First Event Block is executed correctly (multibattle)")
{
@ -115,4 +139,3 @@ ONE_VS_TWO_BATTLE_TEST("End Turn Effects: First Event Block is executed correctl
EXPECT_GT(damage, 0);
}
}

View File

@ -639,17 +639,21 @@ SINGLE_BATTLE_TEST("(TERA) Terastallizing into the Stellar type boosts all moves
}
}
SINGLE_BATTLE_TEST("(TERA) Protean cannot change the type of a Terastallized Pokemon")
SINGLE_BATTLE_TEST("(TERA) Protean/Libero cannot change the type of a Terastallized Pokemon")
{
u32 ability, species;
PARAMETRIZE { ability = ABILITY_PROTEAN; species = SPECIES_GRENINJA; }
PARAMETRIZE { ability = ABILITY_LIBERO; species = SPECIES_RABOOT; }
GIVEN {
PLAYER(SPECIES_GRENINJA) { Ability(ABILITY_PROTEAN); TeraType(TYPE_GRASS); }
PLAYER(species) { Ability(ability); TeraType(TYPE_GRASS); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_BUBBLE, gimmick: GIMMICK_TERA);
MOVE(opponent, MOVE_EMBER); }
} SCENE {
MESSAGE("Greninja used Bubble!");
MESSAGE("The opposing Wobbuffet used Ember!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_TERA_ACTIVATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BUBBLE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_EMBER, opponent);
MESSAGE("It's super effective!");
}
}

View File

@ -34,6 +34,7 @@ SINGLE_BATTLE_TEST("Life Orb activates if it hits a Substitute")
SINGLE_BATTLE_TEST("Life Orb does not activate if using status move on a Substitute")
{
GIVEN {
ASSUME(MoveIgnoresSubstitute(MOVE_GROWL));
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_LIFE_ORB); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {

File diff suppressed because it is too large Load Diff

View File

@ -170,7 +170,7 @@ DOUBLE_BATTLE_TEST("Ally Switch - move fails if the target was ally which change
DOUBLE_BATTLE_TEST("Ally Switch doesn't make self-targeting status moves fail")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_HARDEN].target == MOVE_TARGET_USER);
ASSUME(GetMoveTarget(MOVE_HARDEN) == MOVE_TARGET_USER);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);

View File

@ -1,4 +1,25 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("Brine's power doubles if the target is at 50% or below max HP");
SINGLE_BATTLE_TEST("Brine's power doubles if the target is at 50% or below max HP", s16 damage)
{
bool32 halfHP;
PARAMETRIZE { halfHP = FALSE; }
PARAMETRIZE { halfHP = TRUE; }
GIVEN {
ASSUME(GetMoveEffect(MOVE_BRINE) == EFFECT_BRINE);
PLAYER(SPECIES_SQUIRTLE);
OPPONENT(SPECIES_BLISSEY){
if (halfHP) {
HP((GetMonData(&OPPONENT_PARTY[0], MON_DATA_MAX_HP) / 2) - 1);
}
}
} WHEN {
TURN { MOVE(player, MOVE_BRINE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BRINE, player);
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[1].damage);
}
}

View File

@ -37,16 +37,19 @@ SINGLE_BATTLE_TEST("Curse cuts the user's HP in half when used by Ghost-types")
}
}
SINGLE_BATTLE_TEST("Curse applies to the user if used with Protean")
SINGLE_BATTLE_TEST("Curse applies to the user if used with Protean/Libero")
{
u32 ability, species;
PARAMETRIZE { ability = ABILITY_PROTEAN; species = SPECIES_KECLEON; }
PARAMETRIZE { ability = ABILITY_LIBERO; species = SPECIES_RABOOT; }
GIVEN {
PLAYER(SPECIES_KECLEON) { Ability(ABILITY_PROTEAN); }
PLAYER(species) { Ability(ability); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_CURSE, target: player); }
} SCENE {
s32 playerMaxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP);
ABILITY_POPUP(player, ABILITY_PROTEAN);
ABILITY_POPUP(player, ability);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CURSE, player);
HP_BAR(player, damage: playerMaxHP / 2);
HP_BAR(player, damage: playerMaxHP / 4);

View File

@ -3,8 +3,8 @@
ASSUMPTIONS
{
ASSUME(gMovesInfo[MOVE_HEAL_BELL].effect == EFFECT_HEAL_BELL);
ASSUME(gMovesInfo[MOVE_AROMATHERAPY].effect == EFFECT_HEAL_BELL);
ASSUME(GetMoveEffect(MOVE_HEAL_BELL) == EFFECT_HEAL_BELL);
ASSUME(GetMoveEffect(MOVE_AROMATHERAPY) == EFFECT_HEAL_BELL);
ASSUME(MoveHasAdditionalEffect(MOVE_SPARKLY_SWIRL, MOVE_EFFECT_AROMATHERAPY));
}

View File

@ -32,3 +32,32 @@ SINGLE_BATTLE_TEST("Magic Coat prints the correct message when bouncing back a m
STATUS_ICON(opponent, sleep: TRUE);
}
}
DOUBLE_BATTLE_TEST("Magic Coat reflects hazards regardless of the user's position")
{
struct BattlePokemon *coatUser = NULL;
PARAMETRIZE { coatUser = playerLeft; }
PARAMETRIZE { coatUser = playerRight; }
ASSUME(GetMoveEffect(MOVE_SPIKES) == EFFECT_SPIKES);
ASSUME(GetMoveEffect(MOVE_STEALTH_ROCK) == EFFECT_STEALTH_ROCK);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(coatUser, MOVE_MAGIC_COAT); MOVE(opponentRight, MOVE_STEALTH_ROCK); MOVE(opponentLeft, MOVE_SPIKES); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_MAGIC_COAT, coatUser);
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_STEALTH_ROCK, opponentRight);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPIKES, opponentLeft);
}
} THEN {
EXPECT(!IsHazardOnSide(B_SIDE_PLAYER, HAZARDS_STEALTH_ROCK));
EXPECT(!IsHazardOnSide(B_SIDE_PLAYER, HAZARDS_SPIKES));
EXPECT(IsHazardOnSide(B_SIDE_OPPONENT, HAZARDS_STEALTH_ROCK));
EXPECT(IsHazardOnSide(B_SIDE_OPPONENT, HAZARDS_SPIKES));
}
}

View File

@ -25,6 +25,7 @@ SINGLE_BATTLE_TEST("Metronome picks a random move")
SINGLE_BATTLE_TEST("Metronome's called powder move fails against Grass Types")
{
GIVEN {
WITH_CONFIG(GEN_CONFIG_POWDER_GRASS, GEN_6);
ASSUME(IsPowderMove(MOVE_POISON_POWDER));
ASSUME(GetSpeciesType(SPECIES_TANGELA, 0) == TYPE_GRASS);
ASSUME(GetMoveEffect(MOVE_POISON_POWDER) == EFFECT_NON_VOLATILE_STATUS);

View File

@ -41,6 +41,7 @@ SINGLE_BATTLE_TEST("Mirror Move fails if no move was used before")
SINGLE_BATTLE_TEST("Mirror Move's called powder move fails against Grass Types")
{
GIVEN {
WITH_CONFIG(GEN_CONFIG_POWDER_GRASS, GEN_6);
ASSUME(IsPowderMove(MOVE_STUN_SPORE));
ASSUME(GetSpeciesType(SPECIES_ODDISH, 0) == TYPE_GRASS);
ASSUME(GetMoveEffect(MOVE_STUN_SPORE) == EFFECT_NON_VOLATILE_STATUS);

View File

@ -168,7 +168,7 @@ SINGLE_BATTLE_TEST("Powder fails if the target is Grass type (Gen6+)")
SINGLE_BATTLE_TEST("Powder fails if the target has Overcoat (Gen6+)")
{
GIVEN {
WITH_CONFIG(GEN_CONFIG_POWDER_GRASS, GEN_6);
WITH_CONFIG(GEN_CONFIG_POWDER_OVERCOAT, GEN_6);
PLAYER(SPECIES_FORRETRESS) { Ability(ABILITY_OVERCOAT); }
OPPONENT(SPECIES_VIVILLON);
} WHEN {
@ -223,17 +223,20 @@ DOUBLE_BATTLE_TEST("Powder still blocks the target's Fire type moves even if it
}
}
SINGLE_BATTLE_TEST("Powder prevents Protean from changing its user to Fire type")
SINGLE_BATTLE_TEST("Powder prevents Protean/Libero from changing its user to Fire type")
{
u32 ability, species;
PARAMETRIZE { ability = ABILITY_PROTEAN; species = SPECIES_GRENINJA; }
PARAMETRIZE { ability = ABILITY_LIBERO; species = SPECIES_RABOOT; }
GIVEN {
PLAYER(SPECIES_GRENINJA) { Ability(ABILITY_PROTEAN); }
PLAYER(species) { Ability(ability); }
OPPONENT(SPECIES_VIVILLON);
} WHEN {
TURN { MOVE(opponent, MOVE_POWDER); MOVE(player, MOVE_EMBER); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_POWDER, opponent);
NONE_OF {
ABILITY_POPUP(player, ABILITY_PROTEAN);
ABILITY_POPUP(player, ability);
ANIMATION(ANIM_TYPE_MOVE, MOVE_EMBER, player);
HP_BAR(opponent);
}

View File

@ -120,6 +120,8 @@ SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves against semi-in
PARAMETRIZE { move = MOVE_SOLAR_BEAM; shouldWork = FALSE;}
PARAMETRIZE { move = MOVE_FLY; shouldWork = TRUE;}
GIVEN {
WITH_CONFIG(GEN_CONFIG_TOXIC_NEVER_MISS, GEN_6);
ASSUME(IsSpeciesOfType(SPECIES_SHROODLE, TYPE_POISON));
PLAYER(SPECIES_SHROODLE) { Ability(ABILITY_PRANKSTER); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {

View File

@ -35,22 +35,6 @@ SINGLE_BATTLE_TEST("Rest fails if the user is at full HP")
}
}
SINGLE_BATTLE_TEST("Rest fails if the user is protected by Leaf Guard")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_SUNNY_DAY) == EFFECT_SUNNY_DAY);
ASSUME(B_LEAF_GUARD_PREVENTS_REST >= GEN_5);
PLAYER(SPECIES_CHIKORITA) { Ability(ABILITY_LEAF_GUARD); HP(1); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_SUNNY_DAY); MOVE(player, MOVE_REST); }
} SCENE {
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_REST, player);
} THEN {
EXPECT(!(player->status1 & STATUS1_SLEEP));
}
}
SINGLE_BATTLE_TEST("Rest fails if the user is protected by Shields Down")
{
GIVEN {

View File

@ -1,6 +1,46 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("Return's power increases the higher friendship of the user is")
ASSUMPTIONS
{
ASSUME(GetMoveEffect(MOVE_RETURN) == EFFECT_RETURN);
}
SINGLE_BATTLE_TEST("Return's power increases the higher friendship of the user is", s16 damage)
{
u32 friendship;
PARAMETRIZE { friendship = 0; }
PARAMETRIZE { friendship = 100; }
PARAMETRIZE { friendship = 200; }
PARAMETRIZE { friendship = 255; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Friendship(friendship); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_RETURN); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_RETURN, player);
HP_BAR(opponent, captureDamage: &results[i].damage);
} THEN {
if (i > 0)
EXPECT_GT(results[i].damage, results[i-1].damage);
}
}
TO_DO_BATTLE_TEST("Return does 0 damage at min Friendship (Gen2)")
TO_DO_BATTLE_TEST("Return does 1 damage at min Friendship (Gen3+)")
SINGLE_BATTLE_TEST("Return does 1 damage at min Friendship (Gen3+)")
{
s16 damage;
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Friendship(0); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_RETURN); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_RETURN, player);
HP_BAR(opponent, captureDamage: &damage);
} THEN {
EXPECT_EQ(damage, 1);
}
}

View File

@ -198,3 +198,84 @@ DOUBLE_BATTLE_TEST("Shell Trap targets correctly if one of the opponents has fai
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerLeft);
}
}
SINGLE_BATTLE_TEST("Shell Trap activates if user is hit with a physical move but does no damage")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_FALSE_SWIPE) == EFFECT_FALSE_SWIPE);
PLAYER(SPECIES_WOBBUFFET) { HP(1); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SHELL_TRAP); MOVE(opponent, MOVE_FALSE_SWIPE); }
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_SHELL_TRAP_SETUP, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_FALSE_SWIPE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_TRAP, player);
HP_BAR(opponent);
}
}
SINGLE_BATTLE_TEST("Encore fails if target has active Shell Trap waiting")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_ENCORE) == EFFECT_ENCORE);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE); }
TURN { MOVE(player, MOVE_SHELL_TRAP); MOVE(opponent, MOVE_ENCORE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent);
MESSAGE("Wobbuffet set a shell trap!");
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ENCORE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_TRAP, player);
}
}
}
SINGLE_BATTLE_TEST("Shell Trap fails if an other -3 or lower priority Move is used")
{
GIVEN {
ASSUME(GetMovePriority(MOVE_DRAGON_TAIL) <= -3);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN {
MOVE(player, MOVE_SHELL_TRAP);
MOVE(opponent, MOVE_DRAGON_TAIL);
}
} SCENE {
MESSAGE("Wobbuffet set a shell trap!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_TAIL, opponent);
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_TRAP, player);
}
}
DOUBLE_BATTLE_TEST("Shell Trap does not trigger when hit into Substitute")
{
GIVEN {
ASSUME(GetMoveCategory(MOVE_DOUBLE_EDGE) == DAMAGE_CATEGORY_PHYSICAL);
PLAYER(SPECIES_WYNAUT);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_SNORLAX);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(playerLeft, MOVE_SUBSTITUTE); }
TURN {
MOVE(playerLeft, MOVE_SHELL_TRAP);
MOVE(opponentLeft, MOVE_DOUBLE_EDGE, target: playerLeft);
MOVE(opponentRight, MOVE_SCRATCH, target: playerLeft);
}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, playerLeft);
MESSAGE("Wynaut set a shell trap!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_EDGE, opponentLeft);
MESSAGE("Wynaut's substitute faded!");
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_TRAP, playerLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponentRight);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_TRAP, playerLeft);
}
}

View File

@ -1,4 +1,12 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("TODO: Write Flying Press (Move Effect) test titles")
TO_DO_BATTLE_TEST("Flying Press does both Fighting and Flying-type for type effectiveness")
TO_DO_BATTLE_TEST("Flying-type Pokémon don't receive STAB on Flying Press")
TO_DO_BATTLE_TEST("Sky Plate doesn't boost Flying Press' power") // Check Fist Plate for comparison
TO_DO_BATTLE_TEST("Sharp Beak doesn't boost Flying Press' power") // Check Black Belt for comparison
TO_DO_BATTLE_TEST("Flying Gem doesn't trigger when using Flying Press") // Check Fighting Gem for comparison
TO_DO_BATTLE_TEST("Coba Berry doesn't trigger when the user is attacked by Flying Press")
TO_DO_BATTLE_TEST("Flying Press triggers Chople Berry, even when it wouldn't be super effective with regular Fighting-type moves")
TO_DO_BATTLE_TEST("Flying Press under Electrify does both Electric and Flying-type for type effectiveness") // Check Electric 1/4 effectiveness
TO_DO_BATTLE_TEST("Flying Press under Normalize does both Normal and Flying-type for type effectiveness") // Check Rock/Steel 1/4 effectiveness

Some files were not shown because too many files have changed in this diff Show More