19/11/25 Master to upcoming merge (#8295)
@ -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,
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -1809,7 +1809,6 @@
|
||||
|
||||
.macro setmoveeffect effect:req
|
||||
sethword sMOVE_EFFECT, \effect
|
||||
sethword sSAVED_MOVE_EFFECT, \effect
|
||||
.endm
|
||||
|
||||
.macro sethword dst:req, value:req
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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::
|
||||
|
||||
@ -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::
|
||||
|
||||
@ -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::
|
||||
|
||||
@ -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
|
||||
|
Before Width: | Height: | Size: 20 KiB |
BIN
graphics/battle_anims/backgrounds/rainbow_opponent_tile.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
graphics/battle_anims/backgrounds/rainbow_player_tile.bin
Normal file
BIN
graphics/battle_anims/backgrounds/rainbow_player_tile.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1004 B |
|
Before Width: | Height: | Size: 501 B After Width: | Height: | Size: 505 B |
|
Before Width: | Height: | Size: 895 B After Width: | Height: | Size: 833 B |
|
Before Width: | Height: | Size: 672 B After Width: | Height: | Size: 703 B |
|
Before Width: | Height: | Size: 534 B After Width: | Height: | Size: 523 B |
|
Before Width: | Height: | Size: 925 B After Width: | Height: | Size: 884 B |
|
Before Width: | Height: | Size: 462 B After Width: | Height: | Size: 422 B |
|
Before Width: | Height: | Size: 1002 B After Width: | Height: | Size: 956 B |
|
Before Width: | Height: | Size: 596 B After Width: | Height: | Size: 584 B |
|
Before Width: | Height: | Size: 538 B After Width: | Height: | Size: 569 B |
|
Before Width: | Height: | Size: 524 B After Width: | Height: | Size: 506 B |
|
Before Width: | Height: | Size: 512 B After Width: | Height: | Size: 548 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 735 B After Width: | Height: | Size: 733 B |
2
include/battle.h
Normal file → Executable 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.
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
};
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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[];
|
||||
|
||||
1225
sound/song_table.inc
@ -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.
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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];
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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},
|
||||
};
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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
|
||||
{
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
//************************************
|
||||
//* *
|
||||
|
||||
@ -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 },
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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")
|
||||
{
|
||||
|
||||
@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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); }
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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); }
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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); }
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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!");
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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));
|
||||
}
|
||||
|
||||
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||