Merge branch 'RHH/upcoming' into RHH/pr/upcoming/cleanup/customTrainer

This commit is contained in:
Eduardo Quezada 2023-08-17 17:52:28 -04:00
commit d3333812dd
211 changed files with 2115 additions and 1046 deletions

View File

@ -23,8 +23,9 @@ body:
label: Version
description: What version of pokeemerald-expansion are you using as a base?
options:
- 1.5.1 (Default)
- 1.5.2 (Default)
- upcoming (Edge)
- 1.5.1
- 1.5.0
- 1.4.3
- 1.4.2

View File

@ -23,8 +23,9 @@ body:
label: Version
description: What version of pokeemerald-expansion are you using as a base?
options:
- 1.5.1 (Default)
- 1.5.2 (Default)
- upcoming (Edge)
- 1.5.1
- 1.5.0
- 1.4.3
- 1.4.2

View File

@ -23,8 +23,9 @@ body:
label: Version
description: What version of pokeemerald-expansion are you using as a base?
options:
- 1.5.1 (Default)
- 1.5.2 (Default)
- upcoming (Edge)
- 1.5.1
- 1.5.0
- 1.4.3
- 1.4.2

View File

@ -450,7 +450,7 @@ $(OBJ_DIR)/sym_ewram.ld: sym_ewram.txt
# NOTE: Based on C_DEP above, but without NODEP and KEEP_TEMPS handling.
define TEST_DEP
$1: $2 $$(shell $(SCANINC) -I include -I tools/agbcc/include -I gflib -I test $2)
$1: $2 $$(shell $(SCANINC) -I include -I tools/agbcc/include -I gflib $2)
@echo "$$(CC1) <flags> -o $$@ $$<"
@$$(CPP) $$(CPPFLAGS) $$< | $$(PREPROC) $$< charmap.txt -i | $$(CC1) $$(CFLAGS) -o - - | cat - <(echo -e ".text\n\t.align\t2, 0") | $$(AS) $$(ASFLAGS) -o $$@ -
endef

View File

@ -1364,12 +1364,6 @@
callnative BS_TrySymbiosis
.endm
@ returns TRUE or FALSE to gBattleCommunication[0]
.macro canteleport battler:req
callnative BS_CanTeleport
.byte \battler
.endm
@ returns B_SIDE_x to gBattleCommunication[0]
.macro getbattlerside battler:req
callnative BS_GetBattlerSide
@ -2184,6 +2178,11 @@
jumpifbyte CMP_COMMON_BITS, gMoveResultFlags, MOVE_RESULT_NO_EFFECT, \jumpInstr
.endm
.macro jumpifside battler:req, side:req, equalJumpInstr:req
getbattlerside \battler
jumpifbyte CMP_EQUAL, gBattleCommunication, \side, \equalJumpInstr
.endm
.macro jumpifbattletype flags:req, jumpInstr:req
jumpifword CMP_COMMON_BITS, gBattleTypeFlags, \flags, \jumpInstr
.endm

View File

@ -4023,6 +4023,8 @@ BattleScript_MoveMissedDoDamage::
.if B_CRASH_IF_TARGET_IMMUNE < GEN_4
jumpifhalfword CMP_COMMON_BITS, gMoveResultFlags, MOVE_RESULT_DOESNT_AFFECT_FOE, BattleScript_MoveEnd
.endif
moveendcase MOVEEND_PROTECT_LIKE_EFFECT @ Spiky Shield's damage happens before recoil.
jumpifhasnohp BS_ATTACKER, BattleScript_MoveEnd
printstring STRINGID_PKMNCRASHED
waitmessage B_WAIT_TIME_LONG
damagecalc
@ -5048,7 +5050,7 @@ BattleScript_EffectBatonPass::
goto BattleScript_MoveEnd
BattleScript_EffectRapidSpin::
.if B_SPEED_BUFFING_RAPID_SPIN == GEN_8
.if B_SPEED_BUFFING_RAPID_SPIN >= GEN_8
call BattleScript_EffectHit_Ret
jumpifhalfword CMP_COMMON_BITS, gMoveResultFlags, MOVE_RESULT_DOESNT_AFFECT_FOE, BattleScript_MoveEnd
setmoveeffect MOVE_EFFECT_RAPIDSPIN | MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_CERTAIN
@ -5300,15 +5302,14 @@ BattleScript_EffectHurricane:
BattleScript_EffectTeleport:
attackcanceler
attackstring
ppreduce
.if B_TELEPORT_BEHAVIOR >= GEN_7
canteleport BS_ATTACKER
jumpifbyte CMP_EQUAL, gBattleCommunication, TRUE, BattleScript_EffectTeleportNew
goto BattleScript_ButItFailed
jumpifbattletype BATTLE_TYPE_TRAINER, BattleScript_EffectBatonPass
jumpifside BS_ATTACKER, B_SIDE_PLAYER, BattleScript_EffectBatonPass
.else
jumpifbattletype BATTLE_TYPE_TRAINER, BattleScript_ButItFailed
.endif
BattleScript_EffectTeleportTryToRunAway:
ppreduce
getifcantrunfrombattle BS_ATTACKER
jumpifbyte CMP_EQUAL, gBattleCommunication, BATTLE_RUN_FORBIDDEN, BattleScript_ButItFailed
jumpifbyte CMP_EQUAL, gBattleCommunication, BATTLE_RUN_FAILURE, BattleScript_PrintAbilityMadeIneffective
@ -5319,29 +5320,6 @@ BattleScript_EffectTeleportTryToRunAway:
setoutcomeonteleport BS_ATTACKER
goto BattleScript_MoveEnd
BattleScript_EffectTeleportNew:
getbattlerside BS_ATTACKER
jumpifbyte CMP_EQUAL, gBattleCommunication, B_SIDE_OPPONENT, BattleScript_EffectTeleportTryToRunAway
attackanimation
waitanimation
openpartyscreen BS_ATTACKER, BattleScript_EffectTeleportNewEnd
switchoutabilities BS_ATTACKER
waitstate
switchhandleorder BS_ATTACKER, 2
returntoball BS_ATTACKER
getswitchedmondata BS_ATTACKER
switchindataupdate BS_ATTACKER
hpthresholds BS_ATTACKER
trytoclearprimalweather
printstring STRINGID_EMPTYSTRING3
waitmessage 1
printstring STRINGID_SWITCHINMON
switchinanim BS_ATTACKER, TRUE
waitstate
switchineffects BS_ATTACKER
BattleScript_EffectTeleportNewEnd:
goto BattleScript_MoveEnd
BattleScript_EffectBeatUp::
attackcanceler
accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE

Binary file not shown.

Before

Width:  |  Height:  |  Size: 356 B

After

Width:  |  Height:  |  Size: 290 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 385 B

After

Width:  |  Height:  |  Size: 315 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 434 B

After

Width:  |  Height:  |  Size: 356 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 380 B

After

Width:  |  Height:  |  Size: 325 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 403 B

After

Width:  |  Height:  |  Size: 326 B

View File

@ -212,6 +212,7 @@ struct SideTimer
u8 toxicSpikesAmount;
u8 stealthRockAmount;
u8 stickyWebAmount;
u8 stickyWebBattlerId;
u8 stickyWebBattlerSide; // Used for Court Change
u8 auroraVeilTimer;
u8 auroraVeilBattlerId;
@ -642,7 +643,6 @@ struct BattleStruct
u8 forcedSwitch:4; // For each battler
u8 switchInAbilityPostponed:4; // To not activate against an empty field, each bit for battler
u8 ballSpriteIds[2]; // item gfx, window gfx
u8 stickyWebUser;
u8 appearedInBattle; // Bitfield to track which Pokemon appeared in battle. Used for Burmy's form change
u8 skyDropTargets[MAX_BATTLERS_COUNT]; // For Sky Drop, to account for if multiple Pokemon use Sky Drop in a double battle.
// When using a move which hits multiple opponents which is then bounced by a target, we need to make sure, the move hits both opponents, the one with bounce, and the one without.

View File

@ -209,14 +209,12 @@ void BufferStatChange(u8 battlerId, u8 statId, u8 stringId);
bool32 BlocksPrankster(u16 move, u8 battlerPrankster, u8 battlerDef, bool32 checkTarget);
u16 GetUsedHeldItem(u8 battler);
bool32 IsBattlerWeatherAffected(u8 battlerId, u32 weatherFlags);
u32 ApplyWeatherDamageMultiplier(u8 battlerAtk, u16 move, u8 moveType, u32 dmg, u16 holdEffectAtk, u16 holdEffectDef);
u32 GetBattlerMoveTargetType(u8 battlerId, u16 move);
bool32 CanTargetBattler(u8 battlerAtk, u8 battlerDef, u16 move);
bool8 IsMoveAffectedByParentalBond(u16 move, u8 battlerId);
void CopyMonLevelAndBaseStatsToBattleMon(u32 battler, struct Pokemon *mon);
void CopyMonAbilityAndTypesToBattleMon(u32 battler, struct Pokemon *mon);
void RecalcBattlerStats(u32 battler, struct Pokemon *mon);
void MulModifier(u16 *modifier, u16 val);
bool32 IsAlly(u32 battlerAtk, u32 battlerDef);
// Ability checks
@ -245,9 +243,4 @@ u8 GetBattlerGender(u8 battlerId);
bool8 AreBattlersOfOppositeGender(u8 battler1, u8 battler2);
u32 CalcSecondaryEffectChance(u8 battlerId, u8 secondaryEffectChance);
static inline u32 ApplyModifier(uq4_12_t modifier, u32 val)
{
return UQ_4_12_TO_INT((modifier * val) + UQ_4_12_ROUND);
}
#endif // GUARD_BATTLE_UTIL_H

View File

@ -52,10 +52,30 @@ static inline uq4_12_t uq4_12_multiply(uq4_12_t a, uq4_12_t b)
return (product + UQ_4_12_ROUND) >> UQ_4_12_SHIFT;
}
static inline uq4_12_t uq4_12_multiply_half_down(uq4_12_t a, uq4_12_t b)
{
u32 product = (u32) a * b;
return (product + UQ_4_12_ROUND - 1) >> UQ_4_12_SHIFT;
}
static inline uq4_12_t uq4_12_divide(uq4_12_t dividend, uq4_12_t divisor)
{
if (divisor == UQ_4_12(0.0)) return UQ_4_12(0);
return (dividend << UQ_4_12_SHIFT) / divisor;
}
// Multiplies value by the UQ_4_12 number modifier.
// Returns an integer, rounded to nearest (rounding down on n.5)
static inline u32 uq4_12_multiply_by_int_half_down(uq4_12_t modifier, u32 value)
{
return UQ_4_12_TO_INT((modifier * value) + UQ_4_12_ROUND - 1);
}
// Multiplies value by the UQ_4_12 number modifier.
// Returns an integer, rounded to nearest (rounding up on n.5)
static inline u32 uq4_12_multiply_by_int_half_up(uq4_12_t modifier, u32 value)
{
return UQ_4_12_TO_INT((modifier * value) + UQ_4_12_ROUND);
}
#endif // FPMATH_H_

View File

@ -447,13 +447,13 @@
#ifndef GUARD_TEST_BATTLE_H
#define GUARD_TEST_BATTLE_H
#include "global.h"
#include "battle.h"
#include "battle_anim.h"
#include "data.h"
#include "item.h"
#include "random.h"
#include "recorded_battle.h"
#include "test.h"
#include "util.h"
#include "constants/abilities.h"
#include "constants/battle_anim.h"
@ -462,6 +462,7 @@
#include "constants/items.h"
#include "constants/moves.h"
#include "constants/species.h"
#include "test/test.h"
// NOTE: If the stack is too small the test runner will probably crash
// or loop.

View File

@ -151,7 +151,7 @@ static void InitSinglePlayerBtlControllers(void)
gBattlerPartyIndexes[0] = 0;
gBattlerPartyIndexes[1] = 0;
if (BATTLE_TWO_VS_ONE_OPPONENT)
if (BATTLE_TWO_VS_ONE_OPPONENT || WILD_DOUBLE_BATTLE)
{
gBattlerPartyIndexes[2] = 3;
gBattlerPartyIndexes[3] = 1;

View File

@ -1409,11 +1409,10 @@ bool32 IsMegaTriggerSpriteActive(void)
void HideMegaTriggerSprite(void)
{
if (gBattleStruct->mega.triggerSpriteId != 0xFF)
{
if (gBattleStruct->mega.triggerSpriteId >= MAX_SPRITES)
return;
ChangeMegaTriggerSprite(gBattleStruct->mega.triggerSpriteId, 0);
gSprites[gBattleStruct->mega.triggerSpriteId].tHide = TRUE;
}
}
void HideTriggerSprites(void)

View File

@ -3113,7 +3113,10 @@ static void BattleStartClearSetData(void)
gBattleStruct->mega.triggerSpriteId = 0xFF;
gBattleStruct->stickyWebUser = 0xFF;
for (i = 0; i < ARRAY_COUNT(gSideTimers); i++)
{
gSideTimers[i].stickyWebBattlerId = 0xFF;
}
gBattleStruct->appearedInBattle = 0;
gBattleStruct->beatUpSlot = 0;
@ -3219,8 +3222,12 @@ void SwitchInClearSetData(void)
gBattleStruct->lastMoveFailed &= ~(gBitTable[gActiveBattler]);
gBattleStruct->palaceFlags &= ~(gBitTable[gActiveBattler]);
if (gActiveBattler == gBattleStruct->stickyWebUser)
gBattleStruct->stickyWebUser = 0xFF; // Switched into sticky web user slot so reset it
for (i = 0; i < ARRAY_COUNT(gSideTimers); i++)
{
// Switched into sticky web user slot, so reset stored battler ID
if (gSideTimers[i].stickyWebBattlerId == gActiveBattler)
gSideTimers[i].stickyWebBattlerId = 0xFF;
}
for (i = 0; i < gBattlersCount; i++)
{
@ -3333,8 +3340,12 @@ void FaintClearSetData(void)
gBattleStruct->palaceFlags &= ~(gBitTable[gActiveBattler]);
if (gActiveBattler == gBattleStruct->stickyWebUser)
gBattleStruct->stickyWebUser = 0xFF; // User of sticky web fainted, so reset the stored battler ID
for (i = 0; i < ARRAY_COUNT(gSideTimers); i++)
{
// User of sticky web fainted, so reset the stored battler ID
if (gSideTimers[i].stickyWebBattlerId == gActiveBattler)
gSideTimers[i].stickyWebBattlerId = 0xFF;
}
for (i = 0; i < gBattlersCount; i++)
{
@ -4527,6 +4538,10 @@ static void HandleTurnActionSelectionState(void)
{
// if we choose to throw a ball with our second mon, skip the action of the first
// (if we have chosen throw ball with first, second's is already skipped)
// if throwing a ball in a wild battle with an in-game partner, skip partner's turn when throwing a ball
if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER)
gChosenActionByBattler[GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT)] = B_ACTION_NOTHING_FAINTED;
else
gChosenActionByBattler[GetBattlerAtPosition(B_POSITION_PLAYER_LEFT)] = B_ACTION_NOTHING_FAINTED;
}

View File

@ -142,8 +142,8 @@ static const u8 sText_PkmnRaisedSpDefALittle[] = _("{B_ATK_PREFIX2}'s {B_CURRENT
static const u8 sText_PkmnRaisedDef[] = _("{B_ATK_PREFIX2}'s {B_CURRENT_MOVE}\nraised DEFENSE!");
static const u8 sText_PkmnRaisedDefALittle[] = _("{B_ATK_PREFIX2}'s {B_CURRENT_MOVE}\nraised DEFENSE a little!");
static const u8 sText_PkmnCoveredByVeil[] = _("{B_ATK_PREFIX2}'s party is covered\nby a veil!");
static const u8 sText_PkmnUsedSafeguard[] = _("{B_DEF_NAME_WITH_PREFIX}'s party is protected\nby SAFEGUARD!");
static const u8 sText_PkmnSafeguardExpired[] = _("{B_ATK_PREFIX3}'s party is no longer\nprotected by SAFEGUARD!");
static const u8 sText_PkmnUsedSafeguard[] = _("{B_DEF_NAME_WITH_PREFIX}'s party is protected\nby Safeguard!");
static const u8 sText_PkmnSafeguardExpired[] = _("{B_ATK_PREFIX3}'s party is no longer\nprotected by Safeguard!");
static const u8 sText_PkmnWentToSleep[] = _("{B_ATK_NAME_WITH_PREFIX} went\nto sleep!");
static const u8 sText_PkmnSleptHealthy[] = _("{B_ATK_NAME_WITH_PREFIX} slept and\nbecame healthy!");
static const u8 sText_PkmnWhippedWhirlwind[] = _("{B_ATK_NAME_WITH_PREFIX} whipped\nup a whirlwind!");
@ -2700,7 +2700,7 @@ void BufferStringBattle(u16 stringID)
{
if (gBattleTypeFlags & BATTLE_TYPE_LEGENDARY)
stringPtr = sText_LegendaryPkmnAppeared;
else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && IsValidForBattle(&gEnemyParty[gBattlerPartyIndexes[BATTLE_PARTNER(gActiveBattler)]])) // interesting, looks like they had something planned for wild double battles
else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && IsValidForBattle(&gEnemyParty[gBattlerPartyIndexes[GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT)]]))
stringPtr = sText_TwoWildPkmnAppeared;
else if (gBattleTypeFlags & BATTLE_TYPE_WALLY_TUTORIAL)
stringPtr = sText_WildPkmnAppearedPause;

View File

@ -1414,7 +1414,7 @@ static void Cmd_attackcanceler(void)
return;
}
// Z-moves and Max Moves bypass protection, but deal reduced damage (factored in CalcFinalDmg)
// Z-moves and Max Moves bypass protection, but deal reduced damage (factored in AccumulateOtherModifiers)
if (gBattleStruct->zmove.active && IS_BATTLER_PROTECTED(gBattlerTarget))
{
BattleScriptPush(cmd->nextInstr);
@ -8518,8 +8518,9 @@ static bool32 IsTeatimeAffected(u32 battlerId)
#define UPDATE_COURTCHANGED_BATTLER(structField)\
{ \
sideTimerPlayer->structField ^= BIT_SIDE; \
sideTimerOpp->structField ^= BIT_SIDE; \
temp = sideTimerPlayer->structField; \
sideTimerPlayer->structField = BATTLE_OPPOSITE(sideTimerOpp->structField); \
sideTimerOpp->structField = BATTLE_OPPOSITE(temp); \
} \
static bool32 CourtChangeSwapSideStatuses(void)
@ -8554,9 +8555,7 @@ static bool32 CourtChangeSwapSideStatuses(void)
UPDATE_COURTCHANGED_BATTLER(auroraVeilBattlerId);
UPDATE_COURTCHANGED_BATTLER(tailwindBattlerId);
UPDATE_COURTCHANGED_BATTLER(luckyChantBattlerId);
// For Mirror Armor only
gBattleStruct->stickyWebUser = gBattlerAttacker;
UPDATE_COURTCHANGED_BATTLER(stickyWebBattlerId);
// Track which side originally set the Sticky Web
SWAP(sideTimerPlayer->stickyWebBattlerSide, sideTimerOpp->stickyWebBattlerSide, temp);
@ -8596,33 +8595,6 @@ static void HandleScriptMegaPrimal(u32 caseId, u32 battlerId, bool32 isMega)
}
}
static bool32 CanTeleport(u8 battlerId)
{
struct Pokemon *party = GetBattlerParty(battlerId);
u32 species, count, i;
for (i = 0; i < PARTY_SIZE; i++)
{
species = GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG);
if (species != SPECIES_NONE && species != SPECIES_EGG && GetMonData(&party[i], MON_DATA_HP) != 0)
count++;
}
switch (GetBattlerSide(battlerId))
{
case B_SIDE_OPPONENT:
if (count == 1 || gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
return FALSE;
break;
case B_SIDE_PLAYER:
if (count == 1 || (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && count <= 2))
return FALSE;
break;
}
return TRUE;
}
// Return True if the order was changed, and false if the order was not changed(for example because the target would move after the attacker anyway).
static bool32 ChangeOrderTargetAfterAttacker(void)
{
@ -10571,8 +10543,8 @@ static void Cmd_various(void)
// If Pokémon which set up Sticky Web is not on the field, no Pokémon have their Speed lowered."
gBattlerAttacker = gBattlerTarget; // Initialize 'fail' condition
SET_STATCHANGER(STAT_SPEED, 1, TRUE);
if (gBattleStruct->stickyWebUser != 0xFF)
gBattlerAttacker = gBattleStruct->stickyWebUser;
if (gSideTimers[GetBattlerSide(gActiveBattler)].stickyWebBattlerId != 0xFF)
gBattlerAttacker = gSideTimers[GetBattlerSide(gActiveBattler)].stickyWebBattlerId;
break;
}
case VARIOUS_CUT_1_3_HP_RAISE_STATS:
@ -13640,7 +13612,7 @@ static void Cmd_jumpifnopursuitswitchdmg(void)
&& !(gBattleMons[gBattlerTarget].status1 & (STATUS1_SLEEP | STATUS1_FREEZE))
&& gBattleMons[gBattlerAttacker].hp
&& !gDisableStructs[gBattlerTarget].truantCounter
&& gChosenMoveByBattler[gBattlerTarget] == MOVE_PURSUIT)
&& gBattleMoves[gChosenMoveByBattler[gBattlerTarget]].effect == EFFECT_PURSUIT)
{
s32 i;
@ -13650,7 +13622,7 @@ static void Cmd_jumpifnopursuitswitchdmg(void)
gActionsByTurnOrder[i] = B_ACTION_TRY_FINISH;
}
gCurrentMove = MOVE_PURSUIT;
gCurrentMove = gChosenMoveByBattler[gBattlerTarget];
gCurrMovePos = gChosenMovePos = *(gBattleStruct->chosenMovePositions + gBattlerTarget);
gBattlescriptCurrInstr = cmd->nextInstr;
gBattleScripting.animTurn = 1;
@ -13834,9 +13806,9 @@ static void Cmd_setstickyweb(void)
else
{
gSideStatuses[targetSide] |= SIDE_STATUS_STICKY_WEB;
gSideTimers[targetSide].stickyWebBattlerId = gBattlerAttacker; // For Mirror Armor
gSideTimers[targetSide].stickyWebBattlerSide = GetBattlerSide(gBattlerAttacker); // For Court Change/Defiant - set this to the user's side
gSideTimers[targetSide].stickyWebAmount = 1;
gBattleStruct->stickyWebUser = gBattlerAttacker; // For Mirror Armor
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
@ -15070,10 +15042,10 @@ static void Cmd_pursuitdoubles(void)
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE
&& !(gAbsentBattlerFlags & gBitTable[gActiveBattler])
&& gChosenActionByBattler[gActiveBattler] == B_ACTION_USE_MOVE
&& gChosenMoveByBattler[gActiveBattler] == MOVE_PURSUIT)
&& gBattleMoves[gChosenMoveByBattler[gActiveBattler]].effect == EFFECT_PURSUIT)
{
gActionsByTurnOrder[gActiveBattler] = B_ACTION_TRY_FINISH;
gCurrentMove = MOVE_PURSUIT;
gCurrentMove = gChosenMoveByBattler[gActiveBattler];
gBattlescriptCurrInstr = cmd->nextInstr;
gBattleScripting.animTurn = 1;
gBattleScripting.savedBattler = gBattlerAttacker;
@ -16101,13 +16073,6 @@ void BS_GetBattlerSide(void)
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_CanTeleport(void)
{
NATIVE_ARGS(u8 battler);
gBattleCommunication[0] = CanTeleport(cmd->battler);
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_TrySymbiosis(void)
{
NATIVE_ARGS();

File diff suppressed because it is too large Load Diff

View File

@ -159,7 +159,6 @@ void QueueZMove(u8 battlerId, u16 baseMove)
bool32 IsViableZMove(u8 battlerId, u16 move)
{
struct Pokemon *mon;
struct MegaEvolutionData *mega = &(((struct ChooseMoveStruct *)(&gBattleResources->bufferA[gActiveBattler][4]))->mega);
u8 battlerPosition = GetBattlerPosition(battlerId);
u8 partnerPosition = GetBattlerPosition(BATTLE_PARTNER(battlerId));
u32 item;
@ -185,15 +184,6 @@ bool32 IsViableZMove(u8 battlerId, u16 move)
if ((GetBattlerPosition(battlerId) == B_POSITION_PLAYER_LEFT || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && GetBattlerPosition(battlerId) == B_POSITION_PLAYER_RIGHT)) && !CheckBagHasItem(ITEM_Z_POWER_RING, 1))
return FALSE;
if (mega->alreadyEvolved[battlerPosition])
return FALSE; // Trainer has mega evolved
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
{
if (IsPartnerMonFromSameTrainer(battlerId) && (mega->alreadyEvolved[partnerPosition] || (mega->toEvolve & gBitTable[BATTLE_PARTNER(battlerId)])))
return FALSE; // Partner has mega evolved or is about to mega evolve
}
if (item == ITEM_ENIGMA_BERRY_E_READER)
return FALSE; // HoldEffect = gEnigmaBerries[battlerId].holdEffect;
else
@ -350,9 +340,12 @@ bool32 IsZMoveTriggerSpriteActive(void)
void HideZMoveTriggerSprite(void)
{
struct Sprite *sprite = &gSprites[gBattleStruct->zmove.triggerSpriteId];
sprite->tHide = TRUE;
struct Sprite *sprite;
gBattleStruct->zmove.viable = FALSE;
if (gBattleStruct->zmove.triggerSpriteId >= MAX_SPRITES)
return;
sprite = &gSprites[gBattleStruct->zmove.triggerSpriteId];
sprite->tHide = TRUE;
}
static void ShowZMoveTriggerSprite(u8 battlerId)

View File

@ -9942,7 +9942,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] =
.priority = 0,
.split = SPLIT_SPECIAL,
.zMoveEffect = Z_EFFECT_NONE,
.soundMove = TRUE,
.sheerForceBoost = TRUE,
.thawsUser = TRUE,
.metronomeBanned = TRUE,
},

View File

@ -6,7 +6,7 @@
#define EVO_HELD_ITEM_FIELD_FUNC ItemUseOutOfBattle_CannotUse
#endif
#if I_GEM_BOOST_POWER >= GEN_5
#if I_GEM_BOOST_POWER >= GEN_6
#define GEM_BOOST_PARAM 30
#else
#define GEM_BOOST_PARAM 50

0
src/script_pokemon_util.c Executable file → Normal file
View File

View File

@ -1,20 +0,0 @@
#include "global.h"
#include "test_battle.h"
SINGLE_BATTLE_TEST("Swarm boosts Bug-type moves in a pinch", s16 damage)
{
u16 hp;
PARAMETRIZE { hp = 99; }
PARAMETRIZE { hp = 33; }
GIVEN {
ASSUME(gBattleMoves[MOVE_BUG_BITE].type == TYPE_BUG);
PLAYER(SPECIES_LEDYBA) { Ability(ABILITY_SWARM); MaxHP(99); HP(hp); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_BUG_BITE); }
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage);
}
}

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
ASSUMPTIONS
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
ASSUMPTIONS
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Blaze boosts Fire-type moves in a pinch", s16 damage)
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Clear Body prevents intimidate")
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Cloud Nine prevents weather effects")
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Compound Eyes raises accuracy")
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
ASSUMPTIONS
{
@ -26,7 +26,7 @@ SINGLE_BATTLE_TEST("Contrary raises Attack when Intimidated", s16 damage)
HP_BAR(player, captureDamage: &results[i].damage);
}
FINALLY {
EXPECT_MUL_EQ(results[1].damage, Q_4_12(2.125), results[0].damage);
EXPECT_MUL_EQ(results[1].damage, Q_4_12(2.25), results[0].damage);
}
}

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Cute Charm inflicts infatuation on contact")
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Damp prevents explosion-like moves from enemies")
{

View File

@ -1,72 +1,12 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(gBattleMoves[MOVE_EMBER].power != 0);
ASSUME(gBattleMoves[MOVE_EMBER].type == TYPE_FIRE);
ASSUME(gBattleMoves[MOVE_WATER_GUN].power != 0);
ASSUME(gBattleMoves[MOVE_WATER_GUN].type == TYPE_WATER);
}
SINGLE_BATTLE_TEST("Primordial Sea blocks damaging Fire-type moves")
{
GIVEN {
PLAYER(SPECIES_KYOGRE) {Item(ITEM_BLUE_ORB);}
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_EMBER); }
TURN { MOVE(opponent, MOVE_EMBER); }
} SCENE {
MESSAGE("Foe Wobbuffet used Ember!");
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_EMBER, opponent);
MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!");
NOT HP_BAR(player);
MESSAGE("Foe Wobbuffet used Ember!");
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_EMBER, opponent);
MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!");
NOT HP_BAR(player);
} THEN {
EXPECT_EQ(player->hp, player->maxHP);
}
}
DOUBLE_BATTLE_TEST("Primordial Sea blocks damaging Fire-type moves and prints the message only once with moves hitting multiple targets")
{
GIVEN {
ASSUME(gBattleMoves[MOVE_ERUPTION].power != 0);
ASSUME(gBattleMoves[MOVE_ERUPTION].type == TYPE_FIRE);
ASSUME(gBattleMoves[MOVE_ERUPTION].target == MOVE_TARGET_BOTH);
PLAYER(SPECIES_KYOGRE) {Item(ITEM_BLUE_ORB); {Speed(5);}}
PLAYER(SPECIES_WOBBUFFET) {Speed(5);}
OPPONENT(SPECIES_WOBBUFFET) {Speed(10);}
OPPONENT(SPECIES_WOBBUFFET) {Speed(8);}
} WHEN {
TURN { MOVE(opponentLeft, MOVE_ERUPTION); }
} SCENE {
MESSAGE("Foe Wobbuffet used Eruption!");
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_ERUPTION, opponentLeft);
MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!");
NOT MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!");
} THEN {
EXPECT_EQ(playerLeft->hp, playerLeft->maxHP);
EXPECT_EQ(playerRight->hp, playerRight->maxHP);
}
}
SINGLE_BATTLE_TEST("Primordial Sea does not block a move if pokemon is asleep and uses a Fire-type move") // Sleep/confusion/paralysis all happen before the check for primal weather
{
GIVEN {
PLAYER(SPECIES_KYOGRE) {Item(ITEM_BLUE_ORB);}
OPPONENT(SPECIES_WOBBUFFET) {Status1(STATUS1_SLEEP);}
} WHEN {
TURN { MOVE(opponent, MOVE_EMBER); }
} SCENE {
NOT MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!");
MESSAGE("Foe Wobbuffet is fast asleep.");
}
}
SINGLE_BATTLE_TEST("Desolate Land blocks damaging Water-type moves")
{
GIVEN {

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
ASSUMPTIONS
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Drizzle summons rain", s16 damage)
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Dry Skin causes 1/8th Max HP damage in Sun")
{
@ -36,15 +36,24 @@ SINGLE_BATTLE_TEST("Dry Skin increases damage taken from Fire-type moves by 25%"
PARAMETRIZE { ability = ABILITY_DRY_SKIN; }
GIVEN {
ASSUME(gBattleMoves[MOVE_EMBER].type == TYPE_FIRE);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_PARASECT) { Ability(ability); }
ASSUME(gBattleMoves[MOVE_EMBER].power == 40);
ASSUME(gSpeciesInfo[SPECIES_PARASECT].types[0] == TYPE_BUG);
ASSUME(gSpeciesInfo[SPECIES_PARASECT].types[1] == TYPE_GRASS);
ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[0] == TYPE_PSYCHIC);
ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[1] == TYPE_PSYCHIC);
PLAYER(SPECIES_WOBBUFFET) { SpAttack(71); }
OPPONENT(SPECIES_PARASECT) { Ability(ability); SpDefense(165); }
} WHEN {
TURN { MOVE(player, MOVE_EMBER); }
} SCENE {
MESSAGE("Wobbuffet used Ember!");
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.25), results[1].damage);
// Due to numerics related to rounding on each applied multiplier,
// the ability effect doesn't manifest as a 25% damage increase, but as a ~31% damage increase in this case.
// Values obtained from https://calc.pokemonshowdown.com (Neutral nature and 0 IVs on both sides)
EXPECT_EQ(results[0].damage, 52);
EXPECT_EQ(results[1].damage, 68);
}
}

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Electromorphosis sets up Charge when hit by any move")
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Flame Body inflicts burn on contact")
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Flower Gift transforms Cherrim in harsh sunlight")
{

View File

@ -0,0 +1,66 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(gBattleMoves[MOVE_TACKLE].makesContact);
ASSUME(gBattleMoves[MOVE_EMBER].type == TYPE_FIRE);
ASSUME(gBattleMoves[MOVE_TACKLE].makesContact);
ASSUME(gBattleMoves[MOVE_FIRE_PUNCH].makesContact);
ASSUME(gBattleMoves[MOVE_FIRE_PUNCH].type == TYPE_FIRE);
ASSUME(P_GEN_7_POKEMON == TRUE);
}
SINGLE_BATTLE_TEST("Fluffy halves damage taken from moves that make direct contact", s16 damage)
{
u32 ability;
PARAMETRIZE { ability = ABILITY_KLUTZ; }
PARAMETRIZE { ability = ABILITY_FLUFFY; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_STUFFUL) { Ability(ability); }
} WHEN {
TURN { MOVE(player, MOVE_TACKLE); }
} SCENE {
MESSAGE("Wobbuffet used Tackle!");
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, UQ_4_12(0.5), results[1].damage);
}
}
SINGLE_BATTLE_TEST("Fluffy doubles damage taken from fire type moves", s16 damage)
{
u32 ability;
PARAMETRIZE { ability = ABILITY_KLUTZ; }
PARAMETRIZE { ability = ABILITY_FLUFFY; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_STUFFUL) { Ability(ability); }
} WHEN {
TURN { MOVE(player, MOVE_EMBER); }
} SCENE {
MESSAGE("Wobbuffet used Ember!");
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, UQ_4_12(2.0), results[1].damage);
}
}
SINGLE_BATTLE_TEST("Fluffy does not alter damage of fire-type moves that make direct contact", s16 damage)
{
u32 ability;
PARAMETRIZE { ability = ABILITY_KLUTZ; }
PARAMETRIZE { ability = ABILITY_FLUFFY; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_STUFFUL) { Ability(ability); }
} WHEN {
TURN { MOVE(player, MOVE_FIRE_PUNCH); }
} SCENE {
MESSAGE("Wobbuffet used Fire Punch!");
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_EQ(results[0].damage, results[1].damage);
}
}

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Forecast transforms Castform in weather from an opponent's move")
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Full Metal Body prevents intimidate")
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Hunger Switch switches Morpeko's forms at the end of the turn")
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Hydration cures non-volatile Status conditions if it is raining")
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Hyper Cutter prevents intimidate")
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Ice Body prevents damage from hail")
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Immunity prevents Poison Sting poison")
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Inner Focus prevents intimidate")
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Insomnia prevents sleep")
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
ASSUMPTIONS
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Leaf Guard prevents non-volatile status conditions in sun")
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Limber prevents paralysis")
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Magic Bounce bounces back status moves")

View File

@ -0,0 +1,202 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(P_GEN_8_POKEMON == TRUE);
}
SINGLE_BATTLE_TEST("Mirror Armor lowers a stat of the attacking pokemon")
{
u16 move, statId;
PARAMETRIZE { move = MOVE_LEER; statId = STAT_DEF; }
PARAMETRIZE { move = MOVE_GROWL; statId = STAT_ATK; }
PARAMETRIZE { move = MOVE_SWEET_SCENT; statId = STAT_EVASION; }
PARAMETRIZE { move = MOVE_SAND_ATTACK; statId = STAT_ACC; }
PARAMETRIZE { move = MOVE_CONFIDE; statId = STAT_SPATK; }
PARAMETRIZE { move = MOVE_FAKE_TEARS; statId = STAT_SPDEF; }
GIVEN {
PLAYER(SPECIES_CORVIKNIGHT) {Ability(ABILITY_MIRROR_ARMOR);}
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(opponent, move); }
} SCENE {
ABILITY_POPUP(player, ABILITY_MIRROR_ARMOR);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
switch (statId)
{
case STAT_DEF:
MESSAGE("Foe Wynaut's Defense fell!");
break;
case STAT_ATK:
MESSAGE("Foe Wynaut's Attack fell!");
break;
case STAT_EVASION:
MESSAGE("Foe Wynaut's evasiveness harshly fell!");
break;
case STAT_ACC:
MESSAGE("Foe Wynaut's accuracy fell!");
break;
case STAT_SPATK:
MESSAGE("Foe Wynaut's Sp. Atk fell!");
break;
case STAT_SPDEF:
MESSAGE("Foe Wynaut's Sp. Def harshly fell!");
break;
}
} THEN {
EXPECT_EQ(player->statStages[statId], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponent->statStages[statId], (statId == STAT_SPDEF || statId == STAT_EVASION) ? DEFAULT_STAT_STAGE - 2 : DEFAULT_STAT_STAGE - 1);
}
}
SINGLE_BATTLE_TEST("Mirror Armor triggers even if the attacking Pokemon also has Mirror Armor ability")
{
GIVEN {
PLAYER(SPECIES_CORVIKNIGHT) { Ability(ABILITY_MIRROR_ARMOR); }
OPPONENT(SPECIES_CORVIKNIGHT) { Ability(ABILITY_MIRROR_ARMOR); }
} WHEN {
TURN { MOVE(opponent, MOVE_LEER); }
} SCENE {
MESSAGE("Foe Corviknigh used Leer!");
ABILITY_POPUP(player, ABILITY_MIRROR_ARMOR);
NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
MESSAGE("Foe Corviknigh's Defense fell!");
} THEN {
EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE - 1);
}
}
SINGLE_BATTLE_TEST("Mirror Armor doesn't lower the stats of an attacking Pokemon with the Clear Body ability")
{
GIVEN {
PLAYER(SPECIES_CORVIKNIGHT) { Ability(ABILITY_MIRROR_ARMOR); }
OPPONENT(SPECIES_WYNAUT) { Ability(ABILITY_CLEAR_BODY); }
} WHEN {
TURN { MOVE(opponent, MOVE_LEER); }
} SCENE {
MESSAGE("Foe Wynaut used Leer!");
ABILITY_POPUP(player, ABILITY_MIRROR_ARMOR);
ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY);
MESSAGE("Foe Wynaut's Clear Body prevents stat loss!");
} THEN {
EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE);
}
}
SINGLE_BATTLE_TEST("Mirror Armor lowers the Attack of Pokemon with Intimidate")
{
GIVEN {
PLAYER(SPECIES_CORVIKNIGHT) { Ability(ABILITY_MIRROR_ARMOR); }
OPPONENT(SPECIES_GYARADOS) { Ability(ABILITY_INTIMIDATE); }
} WHEN {
TURN {}
} SCENE {
ABILITY_POPUP(opponent, ABILITY_INTIMIDATE);
ABILITY_POPUP(player, ABILITY_MIRROR_ARMOR);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
MESSAGE("Foe Gyarados's Attack fell!");
} THEN {
EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE - 1);
}
}
// Unsure whether this should or should not fail, as Showdown has conflicting information. Needs testing in gen8 games.
SINGLE_BATTLE_TEST("Mirror Armor doesn't lower the stats of an attacking Pokemon behind Substitute")
{
KNOWN_FAILING;
GIVEN {
PLAYER(SPECIES_CORVIKNIGHT) { Ability(ABILITY_MIRROR_ARMOR); }
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(opponent, MOVE_SUBSTITUTE); }
TURN { MOVE(opponent, MOVE_LEER); }
} SCENE {
MESSAGE("Foe Wynaut used Substitute!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, opponent);
MESSAGE("Foe Wynaut used Leer!");
ABILITY_POPUP(player, ABILITY_MIRROR_ARMOR);
NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
} THEN {
EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE);
}
}
SINGLE_BATTLE_TEST("Mirror Armor raises the stat of an attacking Pokemon with Contrary")
{
GIVEN {
PLAYER(SPECIES_CORVIKNIGHT) {Ability(ABILITY_MIRROR_ARMOR);}
OPPONENT(SPECIES_SHUCKLE) {Ability(ABILITY_CONTRARY);}
} WHEN {
TURN { MOVE(opponent, MOVE_LEER); }
} SCENE {
MESSAGE("Foe Shuckle used Leer!");
ABILITY_POPUP(player, ABILITY_MIRROR_ARMOR);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
MESSAGE("Foe Shuckle's Defense rose!");
} THEN {
EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 1);
}
}
SINGLE_BATTLE_TEST("Mirror Armor doesn't lower the stat of the attacking Pokemon if it is already at -6")
{
GIVEN {
PLAYER(SPECIES_CORVIKNIGHT) {Ability(ABILITY_MIRROR_ARMOR);}
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(player, MOVE_SCREECH); }
TURN { MOVE(player, MOVE_SCREECH); }
TURN { MOVE(player, MOVE_SCREECH); }
TURN { MOVE(opponent, MOVE_LEER); }
} SCENE {
MESSAGE("Corviknigh used Screech!");
MESSAGE("Corviknigh used Screech!");
MESSAGE("Corviknigh used Screech!");
MESSAGE("Foe Wynaut used Leer!");
ABILITY_POPUP(player, ABILITY_MIRROR_ARMOR);
NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
MESSAGE("Foe Wynaut's Defense won't go lower!");
} THEN {
EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE);
EXPECT_EQ(opponent->statStages[STAT_DEF], MIN_STAT_STAGE);
}
}
// This behaviour needs to be verified in the actual games. Currently it's written to follow Showdown's logic.
DOUBLE_BATTLE_TEST("Mirror Armor lowers Speed of the partner Pokemon after Court Change was used by the opponent after it set up Sticky Web")
{
KNOWN_FAILING;
GIVEN {
ASSUME(gBattleMoves[MOVE_STICKY_WEB].effect == EFFECT_STICKY_WEB);
ASSUME(gBattleMoves[MOVE_COURT_CHANGE].effect == EFFECT_COURT_CHANGE);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_CORVIKNIGHT) {Ability(ABILITY_MIRROR_ARMOR); Item(ITEM_IRON_BALL); }
OPPONENT(SPECIES_WYNAUT);
OPPONENT(SPECIES_WYNAUT);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(playerLeft, MOVE_STICKY_WEB); }
TURN { MOVE(opponentLeft, MOVE_COURT_CHANGE); }
TURN { SWITCH(playerRight, 2);}
TURN { }
} SCENE {
MESSAGE("Wobbuffet used Sticky Web!");
MESSAGE("Foe Wynaut used Court Change!");
MESSAGE("Foe Wynaut swapped the battle effects affecting each side!");
MESSAGE("Go! Corviknigh!");
MESSAGE("Corviknigh was caught in a Sticky Web!");
ABILITY_POPUP(playerRight, ABILITY_MIRROR_ARMOR);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
MESSAGE("Wobbuffet's Speed fell!");
}
}

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Oblivious prevents Infatuation")
{

View File

@ -0,0 +1,21 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Overcoat blocks powder and spore moves")
{
GIVEN {
ASSUME(gBattleMoves[MOVE_STUN_SPORE].powderMove);
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_PINECO) { Ability(ABILITY_OVERCOAT); }
} WHEN {
TURN { MOVE(player, MOVE_STUN_SPORE); }
} SCENE {
ABILITY_POPUP(opponent, ABILITY_OVERCOAT);
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_STUN_SPORE, player);
MESSAGE("It doesn't affect Foe Pineco…");
}
}
TO_DO_BATTLE_TEST("Overcoat blocks damage from hail");
TO_DO_BATTLE_TEST("Overcoat blocks damage from sandstorm");
TO_DO_BATTLE_TEST("Overcoat blocks Effect Spore's effect");

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Overgrow boosts Grass-type moves in a pinch", s16 damage)
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Own Tempo prevents intimidate")
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Pastel Veil prevents Poison Sting poison")
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Poison Point inflicts poison on contact")
{

View File

@ -0,0 +1,66 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(gBattleMoves[MOVE_EMBER].power != 0);
ASSUME(gBattleMoves[MOVE_EMBER].type == TYPE_FIRE);
}
SINGLE_BATTLE_TEST("Primordial Sea blocks damaging Fire-type moves")
{
GIVEN {
PLAYER(SPECIES_KYOGRE) {Item(ITEM_BLUE_ORB);}
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_EMBER); }
TURN { MOVE(opponent, MOVE_EMBER); }
} SCENE {
MESSAGE("Foe Wobbuffet used Ember!");
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_EMBER, opponent);
MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!");
NOT HP_BAR(player);
MESSAGE("Foe Wobbuffet used Ember!");
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_EMBER, opponent);
MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!");
NOT HP_BAR(player);
} THEN {
EXPECT_EQ(player->hp, player->maxHP);
}
}
DOUBLE_BATTLE_TEST("Primordial Sea blocks damaging Fire-type moves and prints the message only once with moves hitting multiple targets")
{
GIVEN {
ASSUME(gBattleMoves[MOVE_ERUPTION].power != 0);
ASSUME(gBattleMoves[MOVE_ERUPTION].type == TYPE_FIRE);
ASSUME(gBattleMoves[MOVE_ERUPTION].target == MOVE_TARGET_BOTH);
PLAYER(SPECIES_KYOGRE) {Item(ITEM_BLUE_ORB); {Speed(5);}}
PLAYER(SPECIES_WOBBUFFET) {Speed(5);}
OPPONENT(SPECIES_WOBBUFFET) {Speed(10);}
OPPONENT(SPECIES_WOBBUFFET) {Speed(8);}
} WHEN {
TURN { MOVE(opponentLeft, MOVE_ERUPTION); }
} SCENE {
MESSAGE("Foe Wobbuffet used Eruption!");
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_ERUPTION, opponentLeft);
MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!");
NOT MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!");
} THEN {
EXPECT_EQ(playerLeft->hp, playerLeft->maxHP);
EXPECT_EQ(playerRight->hp, playerRight->maxHP);
}
}
SINGLE_BATTLE_TEST("Primordial Sea does not block a move if pokemon is asleep and uses a Fire-type move") // Sleep/confusion/paralysis all happen before the check for primal weather
{
GIVEN {
PLAYER(SPECIES_KYOGRE) {Item(ITEM_BLUE_ORB);}
OPPONENT(SPECIES_WOBBUFFET) {Status1(STATUS1_SLEEP);}
} WHEN {
TURN { MOVE(opponent, MOVE_EMBER); }
} SCENE {
NOT MESSAGE("The Fire-type attack fizzled out\nin the heavy rain!");
MESSAGE("Foe Wobbuffet is fast asleep.");
}
}

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
ASSUMPTIONS
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
ASSUMPTIONS
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Rain Dish recovers 1/16th of Max HP in Rain")
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
ASSUMPTIONS
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Sand Veil prevents damage from sandstorm")
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Schooling switches Level 20+ Wishiwashi's form when HP is 25-percent or less at the end of the turn")
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Scrappy prevents intimidate")
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Snow Cloak prevents damage from hail")
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
#if B_SNOW_WARNING < GEN_9
SINGLE_BATTLE_TEST("Snow Warning summons hail")

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Speed Boost gradually boosts Speed")
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
#define STAMINA_STAT_RAISE(target, msg) \
{ \

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Static inflicts paralysis on contact")
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Stench has a 10% chance to flinch")
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Sturdy prevents OHKO moves")
{

View File

@ -0,0 +1,28 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Swarm boosts Bug-type moves in a pinch", s16 damage)
{
u16 hp;
PARAMETRIZE { hp = 99; }
PARAMETRIZE { hp = 33; }
GIVEN {
ASSUME(gBattleMoves[MOVE_BUG_BITE].type == TYPE_BUG);
ASSUME(gBattleMoves[MOVE_BUG_BITE].power == 60);
ASSUME(gSpeciesInfo[SPECIES_LEDYBA].types[0] == TYPE_BUG);
ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[0] == TYPE_PSYCHIC);
ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[1] == TYPE_PSYCHIC);
PLAYER(SPECIES_LEDYBA) { Ability(ABILITY_SWARM); MaxHP(99); HP(hp); Attack(45); }
OPPONENT(SPECIES_WOBBUFFET) { Defense(121); }
} WHEN {
TURN { MOVE(player, MOVE_BUG_BITE); }
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
// Due to numerics related to rounding on each applied multiplier,
// the 50% move power increase doesn't manifest as a 50% damage increase, but as a 44% damage increase in this case.
// Values obtained from https://calc.pokemonshowdown.com (Neutral nature and 0 IVs on both sides)
EXPECT_EQ(results[0].damage, 50);
EXPECT_EQ(results[1].damage, 72);
}
}

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
ASSUMPTIONS
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
ASSUMPTIONS
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Torrent boosts Water-type moves in a pinch", s16 damage)
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
ASSUMPTIONS
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Volt Absorb heals 25% when hit by electric type moves")
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Water Absorb heals 25% when hit by water type moves")
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("White Smoke prevents intimidate")
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
ASSUMPTIONS
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Zen Mode switches Darmanitan's form when HP is half or less at the end of the turn")
{

View File

@ -0,0 +1,78 @@
#include "global.h"
#include "test/battle.h"
// From https://bulbapedia.bulbagarden.net/wiki/Damage#Example
SINGLE_BATTLE_TEST("Damage calculation matches Gen5+")
{
s16 dmg;
s16 expectedDamage;
PARAMETRIZE { expectedDamage = 196; }
PARAMETRIZE { expectedDamage = 192; }
PARAMETRIZE { expectedDamage = 192; }
PARAMETRIZE { expectedDamage = 192; }
PARAMETRIZE { expectedDamage = 184; }
PARAMETRIZE { expectedDamage = 184; }
PARAMETRIZE { expectedDamage = 184; }
PARAMETRIZE { expectedDamage = 180; }
PARAMETRIZE { expectedDamage = 180; }
PARAMETRIZE { expectedDamage = 180; }
PARAMETRIZE { expectedDamage = 172; }
PARAMETRIZE { expectedDamage = 172; }
PARAMETRIZE { expectedDamage = 172; }
PARAMETRIZE { expectedDamage = 168; }
PARAMETRIZE { expectedDamage = 168; }
PARAMETRIZE { expectedDamage = 168; }
GIVEN {
PLAYER(SPECIES_GLACEON) { Level(75); Attack(123); }
OPPONENT(SPECIES_GARCHOMP) { Defense(163); }
} WHEN {
TURN {
MOVE(player, MOVE_ICE_FANG, WITH_RNG(RNG_DAMAGE_MODIFIER, i));
}
}
SCENE{
MESSAGE("Glaceon used Ice Fang!");
HP_BAR(opponent, captureDamage: &dmg);
}
THEN{
EXPECT_EQ(expectedDamage, dmg);
}
}
SINGLE_BATTLE_TEST("Damage calculation matches Gen5+ (Muscle Band, crit)")
{
s16 dmg;
s16 expectedDamage;
PARAMETRIZE { expectedDamage = 324; }
PARAMETRIZE { expectedDamage = 316; }
PARAMETRIZE { expectedDamage = 312; }
PARAMETRIZE { expectedDamage = 312; }
PARAMETRIZE { expectedDamage = 304; }
PARAMETRIZE { expectedDamage = 304; }
PARAMETRIZE { expectedDamage = 300; }
PARAMETRIZE { expectedDamage = 300; }
PARAMETRIZE { expectedDamage = 292; }
PARAMETRIZE { expectedDamage = 292; }
PARAMETRIZE { expectedDamage = 288; }
PARAMETRIZE { expectedDamage = 288; }
PARAMETRIZE { expectedDamage = 280; }
PARAMETRIZE { expectedDamage = 276; }
PARAMETRIZE { expectedDamage = 276; }
PARAMETRIZE { expectedDamage = 268; }
GIVEN {
PLAYER(SPECIES_GLACEON) { Level(75); Attack(123); Item(ITEM_MUSCLE_BAND); }
OPPONENT(SPECIES_GARCHOMP) { Defense(163); }
} WHEN {
TURN {
MOVE(player, MOVE_ICE_FANG, WITH_RNG(RNG_DAMAGE_MODIFIER, i), criticalHit: TRUE);
}
}
SCENE{
MESSAGE("Glaceon used Ice Fang!");
HP_BAR(opponent, captureDamage: &dmg);
}
THEN{
EXPECT_EQ(expectedDamage, dmg);
}
}

View File

@ -0,0 +1,23 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Aegislash reverts to Shield Form upon switching out")
{
GIVEN {
ASSUME(P_GEN_6_POKEMON == TRUE);
PLAYER(SPECIES_AEGISLASH);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_TACKLE); }
TURN { SWITCH(player, 1); }
TURN { SWITCH(player, 0); }
} SCENE {
ABILITY_POPUP(player, ABILITY_STANCE_CHANGE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player);
MESSAGE("Aegislash used Tackle!");
MESSAGE("Foe Wobbuffet used Celebrate!");
} THEN {
EXPECT_EQ(player->species, SPECIES_AEGISLASH);
}
}

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Xerneas changes into Active Form upon battle start")
{
@ -79,41 +79,3 @@ SINGLE_BATTLE_TEST("Zamazenta's Iron Head becomes Behemoth Bash upon form change
EXPECT_EQ(player->moves[0], MOVE_BEHEMOTH_BASH);
}
}
SINGLE_BATTLE_TEST("Aegislash reverts to Shield Form upon switching out")
{
GIVEN {
ASSUME(P_GEN_6_POKEMON == TRUE);
PLAYER(SPECIES_AEGISLASH);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_TACKLE); }
TURN { SWITCH(player, 1); }
TURN { SWITCH(player, 0); }
} SCENE {
ABILITY_POPUP(player, ABILITY_STANCE_CHANGE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player);
MESSAGE("Aegislash used Tackle!");
MESSAGE("Foe Wobbuffet used Celebrate!");
} THEN {
EXPECT_EQ(player->species, SPECIES_AEGISLASH);
}
}
SINGLE_BATTLE_TEST("Aegislash reverts to Shield Form upon fainting")
{
GIVEN {
ASSUME(P_GEN_6_POKEMON == TRUE);
PLAYER(SPECIES_AEGISLASH) { HP(1); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_GUST); SEND_OUT(player, 1); }
} SCENE {
MESSAGE("Foe Wobbuffet used Gust!");
MESSAGE("Aegislash fainted!");
} THEN {
EXPECT_EQ(GetMonData(&PLAYER_PARTY[0], MON_DATA_SPECIES), SPECIES_AEGISLASH);
}
}

View File

@ -0,0 +1,19 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Aegislash reverts to Shield Form upon fainting")
{
GIVEN {
ASSUME(P_GEN_6_POKEMON == TRUE);
PLAYER(SPECIES_AEGISLASH) { HP(1); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_GUST); SEND_OUT(player, 1); }
} SCENE {
MESSAGE("Foe Wobbuffet used Gust!");
MESSAGE("Aegislash fainted!");
} THEN {
EXPECT_EQ(GetMonData(&PLAYER_PARTY[0], MON_DATA_SPECIES), SPECIES_AEGISLASH);
}
}

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Venusaur can Mega Evolve holding Venusaurite")
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Primal reversion happens for Groudon only when holding Red Orb")
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
ASSUMPTIONS
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
ASSUMPTIONS
{
@ -112,7 +112,7 @@ SINGLE_BATTLE_TEST("Berserk Gene does not confuse when Safeguard is active")
} SCENE {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
MESSAGE("Using Berserk Gene, the Attack of Wobbuffet sharply rose!");
MESSAGE("Wobbuffet's party is protected by SAFEGUARD!");
MESSAGE("Wobbuffet's party is protected by Safeguard!");
NOT MESSAGE("Wobbuffet became confused!");
}
}

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
ASSUMPTIONS
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
ASSUMPTIONS
{

View File

@ -1,5 +1,5 @@
#include "global.h"
#include "test_battle.h"
#include "test/battle.h"
ASSUMPTIONS
{

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