Merge branch 'RHH/upcoming' into RHH/pr/upcoming/cleanup/customTrainer
@ -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
|
||||
|
@ -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
|
||||
|
3
.github/ISSUE_TEMPLATE/04_other_errors.yaml
vendored
@ -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
|
||||
|
2
Makefile
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Before Width: | Height: | Size: 356 B After Width: | Height: | Size: 290 B |
Before Width: | Height: | Size: 385 B After Width: | Height: | Size: 315 B |
Before Width: | Height: | Size: 434 B After Width: | Height: | Size: 356 B |
Before Width: | Height: | Size: 380 B After Width: | Height: | Size: 325 B |
Before Width: | Height: | Size: 403 B After Width: | Height: | Size: 326 B |
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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_
|
||||
|
@ -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.
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
},
|
||||
|
@ -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
@ -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);
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
@ -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)
|
||||
{
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Clear Body prevents intimidate")
|
||||
{
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Cloud Nine prevents weather effects")
|
||||
{
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Compound Eyes raises accuracy")
|
||||
{
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Cute Charm inflicts infatuation on contact")
|
||||
{
|
@ -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")
|
||||
{
|
@ -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 {
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Drizzle summons rain", s16 damage)
|
||||
{
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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")
|
||||
{
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Flame Body inflicts burn on contact")
|
||||
{
|
@ -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")
|
||||
{
|
66
test/battle/ability/fluffy.c
Normal 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);
|
||||
}
|
||||
}
|
@ -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")
|
||||
{
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Full Metal Body prevents intimidate")
|
||||
{
|
@ -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")
|
||||
{
|
@ -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")
|
||||
{
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Hyper Cutter prevents intimidate")
|
||||
{
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Ice Body prevents damage from hail")
|
||||
{
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Immunity prevents Poison Sting poison")
|
||||
{
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Inner Focus prevents intimidate")
|
||||
{
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Insomnia prevents sleep")
|
||||
{
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
@ -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")
|
||||
{
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Limber prevents paralysis")
|
||||
{
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
|
||||
SINGLE_BATTLE_TEST("Magic Bounce bounces back status moves")
|
202
test/battle/ability/mirror_armor.c
Normal 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!");
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Oblivious prevents Infatuation")
|
||||
{
|
21
test/battle/ability/overcoat.c
Normal 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");
|
@ -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)
|
||||
{
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Own Tempo prevents intimidate")
|
||||
{
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Pastel Veil prevents Poison Sting poison")
|
||||
{
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Poison Point inflicts poison on contact")
|
||||
{
|
66
test/battle/ability/primordial_sea.c
Normal 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.");
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
@ -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")
|
||||
{
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Sand Veil prevents damage from sandstorm")
|
||||
{
|
@ -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")
|
||||
{
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Scrappy prevents intimidate")
|
||||
{
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Snow Cloak prevents damage from hail")
|
||||
{
|
@ -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")
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Speed Boost gradually boosts Speed")
|
||||
{
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
#define STAMINA_STAT_RAISE(target, msg) \
|
||||
{ \
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Static inflicts paralysis on contact")
|
||||
{
|
@ -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")
|
||||
{
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Sturdy prevents OHKO moves")
|
||||
{
|
28
test/battle/ability/swarm.c
Normal 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);
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
@ -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)
|
||||
{
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
@ -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")
|
||||
{
|
@ -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")
|
||||
{
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("White Smoke prevents intimidate")
|
||||
{
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
@ -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")
|
||||
{
|
78
test/battle/damage_formula.c
Normal 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);
|
||||
}
|
||||
}
|
23
test/battle/form_change/battle_switch.c
Normal 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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
19
test/battle/form_change/faint.c
Normal 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);
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Venusaur can Mega Evolve holding Venusaurite")
|
||||
{
|
@ -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")
|
||||
{
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
@ -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!");
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
@ -1,5 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|