Merge branch 'master' of https://github.com/rh-hideout/pokeemerald-expansion into masterintoupcoming18jun

This commit is contained in:
Bassoonian 2024-06-18 15:27:06 +02:00
commit a24b767276
24 changed files with 819 additions and 223 deletions

View file

@ -4221,7 +4221,6 @@ Move_SMACK_DOWN::
createvisualtask AnimTask_SmokescreenImpact, 0x8, 0x400, 0x1902
fadetobg BG_IN_AIR
waitbgfadeout
createvisualtask AnimTask_StartSlidingBg, 5, 0x0, 0x0, 0x0, 0xffff
createvisualtask AnimTask_SeismicTossBgAccelerateDownAtEnd, 3
goto SeismicTossWeak

View file

@ -5828,6 +5828,7 @@ BattleScript_DoSwitchOut::
BattleScript_PursuitDmgOnSwitchOut::
pause B_WAIT_TIME_SHORT
orword gHitMarker, HITMARKER_OBEYS
attackstring
ppreduce
critcalc
@ -5845,11 +5846,12 @@ BattleScript_PursuitDmgOnSwitchOut::
resultmessage
waitmessage B_WAIT_TIME_LONG
tryfaintmon BS_TARGET
moveendfromto MOVEEND_ABILITIES, MOVEEND_CHOICE_MOVE
moveendfromto MOVEEND_ABILITIES, MOVEEND_ATTACKER_INVISIBLE @ MOVEEND_CHOICE_MOVE has to be included
jumpiffainted BS_TARGET, FALSE, BattleScript_PursuitDmgOnSwitchOutRet
setbyte sGIVEEXP_STATE, 0
getexp BS_TARGET
BattleScript_PursuitDmgOnSwitchOutRet:
bicword gHitMarker, HITMARKER_OBEYS
return
BattleScript_Pausex20::

View file

@ -313,6 +313,9 @@ MossdeepCity_SpaceCenter_2F_EventScript_DefeatedMaxieTabitha::
setobjectmovementtype LOCALID_SCIENTIST, MOVEMENT_TYPE_WANDER_AROUND
addobject LOCALID_SCIENTIST
fadescreen FADE_FROM_BLACK
#ifdef BUGFIX
releaseall
#endif
end
MossdeepCity_SpaceCenter_2F_EventScript_StevenFacePlayer::

View file

@ -203,10 +203,10 @@ u8 GetBattlerSpriteDefault_Y(u8 battlerId);
u8 GetSubstituteSpriteDefault_Y(u8 battlerId);
// battle_anim_status_effects.c
#define STAT_ANIM_PLUS1 MOVE_EFFECT_ATK_PLUS_1 - 1
#define STAT_ANIM_PLUS2 MOVE_EFFECT_ATK_PLUS_2 - 1
#define STAT_ANIM_MINUS1 MOVE_EFFECT_ATK_MINUS_1 - 1
#define STAT_ANIM_MINUS2 MOVE_EFFECT_ATK_MINUS_2 - 1
#define STAT_ANIM_PLUS1 (MOVE_EFFECT_ATK_PLUS_1 - 1)
#define STAT_ANIM_PLUS2 (MOVE_EFFECT_ATK_PLUS_2 - 1)
#define STAT_ANIM_MINUS1 (MOVE_EFFECT_ATK_MINUS_1 - 1)
#define STAT_ANIM_MINUS2 (MOVE_EFFECT_ATK_MINUS_2 - 1)
#define STAT_ANIM_MULTIPLE_PLUS1 55
#define STAT_ANIM_MULTIPLE_PLUS2 56
#define STAT_ANIM_MULTIPLE_MINUS1 57

View file

@ -600,14 +600,15 @@ struct Roamer
/*0x08*/ u16 species;
/*0x0A*/ u16 hp;
/*0x0C*/ u8 level;
/*0x0D*/ u16 status;
/*0x0F*/ u8 cool;
/*0x10*/ u8 beauty;
/*0x11*/ u8 cute;
/*0x12*/ u8 smart;
/*0x0D*/ u8 statusA;
/*0x0E*/ u8 cool;
/*0x0F*/ u8 beauty;
/*0x10*/ u8 cute;
/*0x11*/ u8 smart;
/*0x12*/ u8 tough;
/*0x13*/ bool8 active;
/*0x14*/ u8 tough;
/*0x15*/ u8 filler[0x7];
/*0x14*/ u8 statusB; // Stores frostbite
/*0x14*/ u8 filler[0x7];
};
struct RamScriptData

View file

@ -695,6 +695,7 @@ struct BattleTestData
struct BattleTestRunnerState
{
u8 battlersCount;
bool8 forceMoveAnim;
u16 parametersCount; // Valid only in BattleTest_Setup.
u16 parameters;
u16 runParameter;
@ -991,6 +992,8 @@ void SendOut(u32 sourceLine, struct BattlePokemon *, u32 partyIndex);
#define NONE_OF for (OpenQueueGroup(__LINE__, QUEUE_GROUP_NONE_OF); gBattleTestRunnerState->data.queueGroupType != QUEUE_GROUP_NONE; CloseQueueGroup(__LINE__))
#define NOT NONE_OF
#define FORCE_MOVE_ANIM(set) gBattleTestRunnerState->forceMoveAnim = (set)
#define ABILITY_POPUP(battler, ...) QueueAbility(__LINE__, battler, (struct AbilityEventContext) { __VA_ARGS__ })
#define ANIMATION(type, id, ...) QueueAnimation(__LINE__, type, id, (struct AnimationEventContext) { __VA_ARGS__ })
#define HP_BAR(battler, ...) QueueHP(__LINE__, battler, (struct HPEventContext) { R_APPEND_TRUE(__VA_ARGS__) })

View file

@ -3758,7 +3758,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
ADJUST_SCORE(GOOD_EFFECT);
break;
case EFFECT_SANDSTORM:
if (ShouldSetSandstorm(battlerAtk, aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerAtk]))
if (ShouldSetSandstorm(battlerAtk, aiData->abilities[battlerAtk], aiData->holdEffects[battlerAtk]))
{
ADJUST_SCORE(DECENT_EFFECT);
if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_SMOOTH_ROCK)

View file

@ -521,7 +521,7 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
}
else if (gMovesInfo[move].effect == EFFECT_PHOTON_GEYSER)
gBattleStruct->swapDamageCategory = (GetCategoryBasedOnStats(gBattlerAttacker) == DAMAGE_CATEGORY_PHYSICAL);
else if (move == MOVE_SHELL_SIDE_ARM && gBattleStruct->shellSideArmCategory[battlerAtk][battlerDef] == DAMAGE_CATEGORY_SPECIAL)
else if (move == MOVE_SHELL_SIDE_ARM && gBattleStruct->shellSideArmCategory[battlerAtk][battlerDef] == DAMAGE_CATEGORY_PHYSICAL)
gBattleStruct->swapDamageCategory = TRUE;
else if (gMovesInfo[move].effect == EFFECT_NATURE_POWER)
move = GetNaturePowerMove();

View file

@ -18,6 +18,7 @@
#include "sprite.h"
#include "task.h"
#include "test_runner.h"
#include "test/battle.h"
#include "constants/battle_anim.h"
#include "constants/moves.h"
@ -239,6 +240,9 @@ void LaunchBattleAnimation(u32 animType, u32 animId)
TestRunner_Battle_RecordAnimation(animType, animId);
// Play Transform and Ally Switch even in Headless as these move animations also change mon data.
if (gTestRunnerHeadless
#if TESTING // Because gBattleTestRunnerState is not seen outside of test env.
&& !gBattleTestRunnerState->forceMoveAnim
#endif // TESTING
&& !(animType == ANIM_TYPE_MOVE && (animId == MOVE_TRANSFORM || animId == MOVE_ALLY_SWITCH)))
{
gAnimScriptCallback = Nop;
@ -446,7 +450,7 @@ static u8 GetBattleAnimMoveTargets(u8 battlerArgIndex, u8 *targets)
u32 i;
u32 ignoredTgt = gBattlerAttacker;
u32 target = GetBattlerMoveTargetType(gBattleAnimAttacker, gAnimMoveIndex);
switch (battlerAnimId)
{
case ANIM_ATTACKER:
@ -458,7 +462,7 @@ static u8 GetBattleAnimMoveTargets(u8 battlerArgIndex, u8 *targets)
ignoredTgt = gBattlerAttacker;
break;
}
switch (target)
{
case MOVE_TARGET_FOES_AND_ALLY:

View file

@ -1961,7 +1961,7 @@ static void Cmd_damagecalc(void)
u8 moveType;
GET_MOVE_TYPE(gCurrentMove, moveType);
if (gBattleStruct->shellSideArmCategory[gBattlerAttacker][gBattlerTarget] == DAMAGE_CATEGORY_SPECIAL && gCurrentMove == MOVE_SHELL_SIDE_ARM)
if (gBattleStruct->shellSideArmCategory[gBattlerAttacker][gBattlerTarget] == DAMAGE_CATEGORY_PHYSICAL && gCurrentMove == MOVE_SHELL_SIDE_ARM)
gBattleStruct->swapDamageCategory = TRUE;
gBattleMoveDamage = CalculateMoveDamage(gCurrentMove, gBattlerAttacker, gBattlerTarget, moveType, 0, gIsCriticalHit, TRUE, TRUE);
gBattlescriptCurrInstr = cmd->nextInstr;
@ -5389,7 +5389,6 @@ static void Cmd_moveend(void)
bool32 effect = FALSE;
u32 moveType = 0;
u32 holdEffectAtk = 0;
u16 *choicedMoveAtk = NULL;
u32 endMode, endState;
u32 originallyUsedMove;
@ -5402,7 +5401,6 @@ static void Cmd_moveend(void)
endState = cmd->endState;
holdEffectAtk = GetBattlerHoldEffect(gBattlerAttacker, TRUE);
choicedMoveAtk = &gBattleStruct->choicedMove[gBattlerAttacker];
GET_MOVE_TYPE(gCurrentMove, moveType);
do
@ -5612,29 +5610,34 @@ static void Cmd_moveend(void)
gBattleScripting.moveendState++;
break;
case MOVEEND_CHOICE_MOVE: // update choice band move
if (gHitMarker & HITMARKER_OBEYS
&& (HOLD_EFFECT_CHOICE(holdEffectAtk) || GetBattlerAbility(gBattlerAttacker) == ABILITY_GORILLA_TACTICS)
&& gChosenMove != MOVE_STRUGGLE
&& (*choicedMoveAtk == MOVE_NONE || *choicedMoveAtk == MOVE_UNAVAILABLE))
{
if ((gMovesInfo[gChosenMove].effect == EFFECT_BATON_PASS
|| gMovesInfo[gChosenMove].effect == EFFECT_HEALING_WISH)
&& !(gMoveResultFlags & MOVE_RESULT_FAILED))
u16 *choicedMoveAtk = &gBattleStruct->choicedMove[gBattlerAttacker];
if (gHitMarker & HITMARKER_OBEYS
&& (HOLD_EFFECT_CHOICE(holdEffectAtk) || GetBattlerAbility(gBattlerAttacker) == ABILITY_GORILLA_TACTICS)
&& gChosenMove != MOVE_STRUGGLE
&& (*choicedMoveAtk == MOVE_NONE || *choicedMoveAtk == MOVE_UNAVAILABLE))
{
gBattleScripting.moveendState++;
break;
if ((gMovesInfo[gChosenMove].effect == EFFECT_BATON_PASS
|| gMovesInfo[gChosenMove].effect == EFFECT_HEALING_WISH)
&& !(gMoveResultFlags & MOVE_RESULT_FAILED))
{
gBattleScripting.moveendState++;
break;
}
*choicedMoveAtk = gChosenMove;
}
*choicedMoveAtk = gChosenMove;
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (gBattleMons[gBattlerAttacker].moves[i] == *choicedMoveAtk)
break;
}
if (i == MAX_MON_MOVES)
{
*choicedMoveAtk = MOVE_NONE;
}
gBattleScripting.moveendState++;
break;
}
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (gBattleMons[gBattlerAttacker].moves[i] == *choicedMoveAtk)
break;
}
if (i == MAX_MON_MOVES)
*choicedMoveAtk = MOVE_NONE;
gBattleScripting.moveendState++;
break;
case MOVEEND_CHANGED_ITEMS: // changed held items
for (i = 0; i < gBattlersCount; i++)
{
@ -12825,7 +12828,11 @@ static void Cmd_trysetencore(void)
{
gDisableStructs[gBattlerTarget].encoredMove = gBattleMons[gBattlerTarget].moves[i];
gDisableStructs[gBattlerTarget].encoredMovePos = i;
gDisableStructs[gBattlerTarget].encoreTimer = 3;
// Encore always lasts 3 turns, but we need to account for a scenario where Encore changes the move during the same turn.
if (GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget))
gDisableStructs[gBattlerTarget].encoreTimer = 4;
else
gDisableStructs[gBattlerTarget].encoreTimer = 3;
gBattlescriptCurrInstr = cmd->nextInstr;
}
else
@ -13543,7 +13550,7 @@ static void Cmd_jumpifnopursuitswitchdmg(void)
gActionsByTurnOrder[i] = B_ACTION_TRY_FINISH;
}
gCurrentMove = gChosenMoveByBattler[gBattlerTarget];
gCurrentMove = gChosenMove = gChosenMoveByBattler[gBattlerTarget];
gCurrMovePos = gChosenMovePos = *(gBattleStruct->chosenMovePositions + gBattlerTarget);
gBattlescriptCurrInstr = cmd->nextInstr;
gBattleScripting.animTurn = 1;
@ -16437,9 +16444,9 @@ void BS_SetRemoveTerrain(void)
}
else
{
u16 atkHoldEffect = GetBattlerHoldEffect(gBattlerAttacker, TRUE);
u32 atkHoldEffect = GetBattlerHoldEffect(gBattlerAttacker, TRUE);
gFieldStatuses &= ~STATUS_FIELD_TERRAIN_ANY;
gFieldStatuses &= ~(STATUS_FIELD_TERRAIN_ANY | STATUS_FIELD_TERRAIN_PERMANENT);
gFieldStatuses |= statusFlag;
gFieldTimers.terrainTimer = (atkHoldEffect == HOLD_EFFECT_TERRAIN_EXTENDER) ? 8 : 5;
gBattlescriptCurrInstr = cmd->nextInstr;
@ -16537,7 +16544,8 @@ void BS_TryRelicSong(void)
{
NATIVE_ARGS();
if (GetBattlerAbility(gBattlerAttacker) != ABILITY_SHEER_FORCE && !(gBattleMons[gBattlerAttacker].status2 & STATUS2_TRANSFORMED))
if (GetBattlerAbility(gBattlerAttacker) != ABILITY_SHEER_FORCE && !(gBattleMons[gBattlerAttacker].status2 & STATUS2_TRANSFORMED)
&& (gBattleMons[gBattlerAttacker].species == SPECIES_MELOETTA_ARIA || gBattleMons[gBattlerAttacker].species == SPECIES_MELOETTA_PIROUETTE))
{
if (gBattleMons[gBattlerAttacker].species == SPECIES_MELOETTA_ARIA)
gBattleMons[gBattlerAttacker].species = SPECIES_MELOETTA_PIROUETTE;
@ -16548,7 +16556,9 @@ void BS_TryRelicSong(void)
gBattlescriptCurrInstr = BattleScript_AttackerFormChangeMoveEffect;
}
else
{
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
void BS_SetPledge(void)

View file

@ -3883,7 +3883,7 @@ static bool32 TryChangeBattleTerrain(u32 battler, u32 statusFlag, u8 *timer)
{
if ((!(gFieldStatuses & statusFlag) && (!gBattleStruct->isSkyBattle)))
{
gFieldStatuses &= ~(STATUS_FIELD_MISTY_TERRAIN | STATUS_FIELD_GRASSY_TERRAIN | STATUS_FIELD_ELECTRIC_TERRAIN | STATUS_FIELD_PSYCHIC_TERRAIN);
gFieldStatuses &= ~(STATUS_FIELD_TERRAIN_ANY | STATUS_FIELD_TERRAIN_PERMANENT);
gFieldStatuses |= statusFlag;
gDisableStructs[battler].terrainAbilityDone = FALSE;
@ -6558,7 +6558,7 @@ static u8 StatRaiseBerry(u32 battler, u32 itemId, u32 statId, bool32 end2)
else
SET_STATCHANGER(statId, 1, FALSE);
gBattleScripting.animArg1 = 14 + statId;
gBattleScripting.animArg1 = STAT_ANIM_PLUS1 + statId;
gBattleScripting.animArg2 = 0;
if (end2)
@ -6609,7 +6609,7 @@ static u8 RandomStatRaiseBerry(u32 battler, u32 itemId, bool32 end2)
else
SET_STATCHANGER(i + 1, 2, FALSE);
gBattleScripting.animArg1 = 0x21 + i + 6;
gBattleScripting.animArg1 = STAT_ANIM_PLUS2 + i + 1;
gBattleScripting.animArg2 = 0;
if (end2)
{
@ -6686,7 +6686,7 @@ static u8 DamagedStatBoostBerryEffect(u32 battler, u8 statId, u8 category)
SET_STATCHANGER(statId, 1, FALSE);
gBattleScripting.battler = battler;
gBattleScripting.animArg1 = 14 + statId;
gBattleScripting.animArg1 = STAT_ANIM_PLUS1 + statId;
gBattleScripting.animArg2 = 0;
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_BerryStatRaiseRet;
@ -6703,7 +6703,7 @@ u8 TryHandleSeed(u32 battler, u32 terrainFlag, u8 statId, u16 itemId, bool32 exe
gLastUsedItem = itemId; // For surge abilities
gEffectBattler = gBattleScripting.battler = battler;
SET_STATCHANGER(statId, 1, FALSE);
gBattleScripting.animArg1 = 14 + statId;
gBattleScripting.animArg1 = STAT_ANIM_PLUS1 + statId;
gBattleScripting.animArg2 = 0;
if (execute)
{
@ -7111,7 +7111,7 @@ static u8 ItemEffectMoveEnd(u32 battler, u16 holdEffect)
}
SET_STATCHANGER(STAT_ATK, 2, FALSE);
gBattleScripting.animArg1 = 14 + STAT_ATK;
gBattleScripting.animArg1 = STAT_ANIM_PLUS1 + STAT_ATK;
gBattleScripting.animArg2 = 0;
BattleScriptPushCursorAndCallback(BattleScript_BerserkGeneRet);
@ -7393,7 +7393,7 @@ u8 ItemBattleEffects(u8 caseID, u32 battler, bool32 moveTurn)
}
SET_STATCHANGER(STAT_ATK, 2, FALSE);
gBattleScripting.animArg1 = 14 + STAT_ATK;
gBattleScripting.animArg1 = STAT_ANIM_PLUS1 + STAT_ATK;
gBattleScripting.animArg2 = 0;
BattleScriptPushCursorAndCallback(BattleScript_BerserkGeneRet);
@ -7657,7 +7657,7 @@ u8 ItemBattleEffects(u8 caseID, u32 battler, bool32 moveTurn)
}
SET_STATCHANGER(STAT_ATK, 2, FALSE);
gBattleScripting.animArg1 = 14 + STAT_ATK;
gBattleScripting.animArg1 = STAT_ANIM_PLUS1 + STAT_ATK;
gBattleScripting.animArg2 = 0;
BattleScriptPushCursorAndCallback(BattleScript_BerserkGeneRet);
@ -8343,7 +8343,7 @@ bool32 IsMoveMakingContact(u32 move, u32 battlerAtk)
if (!gMovesInfo[move].makesContact)
{
if (move == MOVE_SHELL_SIDE_ARM && gBattleStruct->shellSideArmCategory[battlerAtk][gBattlerTarget] == DAMAGE_CATEGORY_SPECIAL)
if (move == MOVE_SHELL_SIDE_ARM && gBattleStruct->shellSideArmCategory[battlerAtk][gBattlerTarget] == DAMAGE_CATEGORY_PHYSICAL)
return TRUE;
else
return FALSE;
@ -8383,7 +8383,7 @@ bool32 IsBattlerProtected(u32 battlerAtk, u32 battlerDef, u32 move)
// Protective Pads doesn't stop Unseen Fist from bypassing Protect effects, so IsMoveMakingContact() isn't used here.
// This means extra logic is needed to handle Shell Side Arm.
if (GetBattlerAbility(gBattlerAttacker) == ABILITY_UNSEEN_FIST
&& (gMovesInfo[move].makesContact || (move == MOVE_SHELL_SIDE_ARM && gBattleStruct->shellSideArmCategory[battlerAtk][battlerDef] == DAMAGE_CATEGORY_SPECIAL))
&& (gMovesInfo[move].makesContact || (move == MOVE_SHELL_SIDE_ARM && gBattleStruct->shellSideArmCategory[battlerAtk][battlerDef] == DAMAGE_CATEGORY_PHYSICAL))
&& !gProtectStructs[battlerDef].maxGuarded) // Max Guard cannot be bypassed by Unseen Fist
return FALSE;
else if (gMovesInfo[move].ignoresProtect)
@ -9316,7 +9316,11 @@ static inline u32 CalcAttackStat(u32 move, u32 battlerAtk, u32 battlerDef, u32 m
if (IS_MOVE_PHYSICAL(move))
{
atkStat = gBattleMons[battlerAtk].defense;
atkStage = gBattleMons[battlerAtk].statStages[STAT_DEF];
// Edge case: Body Press used during Wonder Room. For some reason, it still uses Defense over Sp.Def, but uses Sp.Def stat changes
if (gFieldStatuses & STATUS_FIELD_WONDER_ROOM)
atkStage = gBattleMons[battlerAtk].statStages[STAT_SPDEF];
else
atkStage = gBattleMons[battlerAtk].statStages[STAT_DEF];
}
else
{
@ -11222,7 +11226,7 @@ bool32 TryRoomService(u32 battler)
BufferStatChange(battler, STAT_SPEED, STRINGID_STATFELL);
gEffectBattler = gBattleScripting.battler = battler;
SET_STATCHANGER(STAT_SPEED, 1, TRUE);
gBattleScripting.animArg1 = 14 + STAT_SPEED;
gBattleScripting.animArg1 = STAT_ANIM_PLUS1 + STAT_SPEED;
gBattleScripting.animArg2 = 0;
gLastUsedItem = gBattleMons[battler].item;
return TRUE;
@ -11608,7 +11612,9 @@ void SetShellSideArmCategory(void)
special = ((((2 * gBattleMons[battlerAtk].level / 5 + 2) * gMovesInfo[MOVE_SHELL_SIDE_ARM].power * attackerSpAtkStat) / targetSpDefStat) / 50);
if (((physical > special) || (physical == special && RandomPercentage(RNG_SHELL_SIDE_ARM, 50))))
if ((physical > special) || (physical == special && RandomPercentage(RNG_SHELL_SIDE_ARM, 50)))
gBattleStruct->shellSideArmCategory[battlerAtk][battlerDef] = DAMAGE_CATEGORY_PHYSICAL;
else
gBattleStruct->shellSideArmCategory[battlerAtk][battlerDef] = DAMAGE_CATEGORY_SPECIAL;
}
}

View file

@ -1040,12 +1040,14 @@ static u16 DetermineEggSpeciesAndParentSlots(struct DayCare *daycare, u8 *parent
eggSpecies = SPECIES_ILLUMISE;
else if (eggSpecies == SPECIES_MANAPHY)
eggSpecies = SPECIES_PHIONE;
else if (eggSpecies == SPECIES_SINISTEA_ANTIQUE)
eggSpecies = SPECIES_SINISTEA_PHONY;
else if (GET_BASE_SPECIES_ID(eggSpecies) == SPECIES_ROTOM)
eggSpecies = SPECIES_ROTOM;
else if (GET_BASE_SPECIES_ID(eggSpecies) == SPECIES_FURFROU)
eggSpecies = SPECIES_FURFROU;
else if (eggSpecies == SPECIES_SINISTEA_ANTIQUE)
eggSpecies = SPECIES_SINISTEA_PHONY;
else if (eggSpecies == SPECIES_POLTCHAGEIST_ARTISAN)
eggSpecies = SPECIES_POLTCHAGEIST_COUNTERFEIT;
// To avoid single-stage Totem Pokémon to breed more of themselves.
else if (eggSpecies == SPECIES_MIMIKYU_TOTEM_DISGUISED)
eggSpecies = SPECIES_MIMIKYU_DISGUISED;

View file

@ -105,7 +105,8 @@ static void CreateInitialRoamerMon(u8 index, u16 species, u8 level)
ROAMER(index)->personality = GetMonData(&gEnemyParty[0], MON_DATA_PERSONALITY);
ROAMER(index)->species = species;
ROAMER(index)->level = level;
ROAMER(index)->status = 0;
ROAMER(index)->statusA = 0;
ROAMER(index)->statusB = 0;
ROAMER(index)->hp = GetMonData(&gEnemyParty[0], MON_DATA_MAX_HP);
ROAMER(index)->cool = GetMonData(&gEnemyParty[0], MON_DATA_COOL);
ROAMER(index)->beauty = GetMonData(&gEnemyParty[0], MON_DATA_BEAUTY);
@ -238,18 +239,13 @@ bool8 IsRoamerAt(u32 roamerIndex, u8 mapGroup, u8 mapNum)
void CreateRoamerMonInstance(u32 roamerIndex)
{
u32 status;
u32 status = ROAMER(roamerIndex)->statusA + (ROAMER(roamerIndex)->statusB << 8);
struct Pokemon *mon = &gEnemyParty[0];
ZeroEnemyPartyMons();
CreateMonWithIVsPersonality(mon, ROAMER(roamerIndex)->species, ROAMER(roamerIndex)->level, ROAMER(roamerIndex)->ivs, ROAMER(roamerIndex)->personality);
// The roamer's status field is u16, but SetMonData expects status to be u32, so will set the roamer's status
// using the status field and the following 3 bytes (cool, beauty, and cute).
#ifdef BUGFIX
status = ROAMER(roamerIndex)->status;
SetMonData(mon, MON_DATA_STATUS, &status);
#else
SetMonData(mon, MON_DATA_STATUS, &ROAMER->status);
#endif
SetMonData(mon, MON_DATA_HP, &ROAMER(roamerIndex)->hp);
SetMonData(mon, MON_DATA_COOL, &ROAMER(roamerIndex)->cool);
SetMonData(mon, MON_DATA_BEAUTY, &ROAMER(roamerIndex)->beauty);
@ -276,8 +272,11 @@ bool8 TryStartRoamerEncounter(void)
void UpdateRoamerHPStatus(struct Pokemon *mon)
{
u32 status = GetMonData(mon, MON_DATA_STATUS);
ROAMER(gEncounteredRoamerIndex)->hp = GetMonData(mon, MON_DATA_HP);
ROAMER(gEncounteredRoamerIndex)->status = GetMonData(mon, MON_DATA_STATUS);
ROAMER(gEncounteredRoamerIndex)->statusA = status;
ROAMER(gEncounteredRoamerIndex)->statusB = status >> 8;
RoamerMoveToOtherLocationSet(gEncounteredRoamerIndex);
}

View file

@ -0,0 +1,73 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Anger Point raises Attack stage to maximum after receiving a critical hit")
{
ASSUME(gMovesInfo[MOVE_FROST_BREATH].alwaysCriticalHit);
GIVEN {
PLAYER(SPECIES_PRIMEAPE) { Ability(ABILITY_ANGER_POINT); }
OPPONENT(SPECIES_SNORUNT);
} WHEN {
TURN { MOVE(opponent, MOVE_FROST_BREATH); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_FROST_BREATH, opponent);
MESSAGE("A critical hit!");
ABILITY_POPUP(player, ABILITY_ANGER_POINT);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
MESSAGE("Primeape's Anger Point maxed its Attack!");
} THEN {
EXPECT_EQ(player->statStages[STAT_ATK], MAX_STAT_STAGE);
}
}
SINGLE_BATTLE_TEST("Anger Point does not trigger when already at maximum Attack stage")
{
ASSUME(gMovesInfo[MOVE_FROST_BREATH].alwaysCriticalHit);
ASSUME(gMovesInfo[MOVE_BELLY_DRUM].effect == EFFECT_BELLY_DRUM);
GIVEN {
PLAYER(SPECIES_PRIMEAPE) { Ability(ABILITY_ANGER_POINT); Speed(2); }
OPPONENT(SPECIES_SNORUNT) { Speed(1); }
} WHEN {
TURN { MOVE(player, MOVE_BELLY_DRUM); MOVE(opponent, MOVE_FROST_BREATH); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BELLY_DRUM, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
MESSAGE("Primeape cut its own HP and maximized ATTACK!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_FROST_BREATH, opponent);
MESSAGE("A critical hit!");
NONE_OF {
ABILITY_POPUP(player, ABILITY_ANGER_POINT);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
MESSAGE("Primeape's Anger Point maxed its Attack!");
}
} THEN {
EXPECT_EQ(player->statStages[STAT_ATK], MAX_STAT_STAGE);
}
}
SINGLE_BATTLE_TEST("Anger Point does not trigger when a substitute takes the hit")
{
ASSUME(gMovesInfo[MOVE_FROST_BREATH].alwaysCriticalHit);
ASSUME(gMovesInfo[MOVE_SUBSTITUTE].effect == EFFECT_SUBSTITUTE);
GIVEN {
PLAYER(SPECIES_PRIMEAPE) { Ability(ABILITY_ANGER_POINT); Speed(2); }
OPPONENT(SPECIES_SNORUNT) { Speed(1); }
} WHEN {
TURN { MOVE(player, MOVE_SUBSTITUTE); MOVE(opponent, MOVE_FROST_BREATH); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, player);
MESSAGE("Primeape made a SUBSTITUTE!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_FROST_BREATH, opponent);
MESSAGE("A critical hit!");
NONE_OF {
ABILITY_POPUP(player, ABILITY_ANGER_POINT);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
MESSAGE("Primeape's Anger Point maxed its Attack!");
}
} THEN {
EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
}
}

View file

@ -1,15 +1,19 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Clear Body prevents intimidate")
SINGLE_BATTLE_TEST("Clear Body, Full Metal Body, and White Smoke prevent intimidate")
{
s16 turnOneHit;
s16 turnTwoHit;
u32 species, ability;
PARAMETRIZE{ species = SPECIES_METANG; ability = ABILITY_CLEAR_BODY; }
PARAMETRIZE{ species = SPECIES_SOLGALEO; ability = ABILITY_FULL_METAL_BODY; }
PARAMETRIZE{ species = SPECIES_TORKOAL; ability = ABILITY_WHITE_SMOKE; }
GIVEN {
PLAYER(SPECIES_EKANS) { Ability(ABILITY_SHED_SKIN); };
PLAYER(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); };
OPPONENT(SPECIES_BELDUM) { Ability(ABILITY_CLEAR_BODY); };
OPPONENT(species) { Ability(ability); };
} WHEN {
TURN { MOVE(opponent, MOVE_TACKLE); }
TURN { SWITCH(player, 1); MOVE(opponent, MOVE_TACKLE); }
@ -20,24 +24,38 @@ SINGLE_BATTLE_TEST("Clear Body prevents intimidate")
NONE_OF {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
}
ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY);
MESSAGE("Foe Beldum's Clear Body prevents stat loss!");
ABILITY_POPUP(opponent, ability);
if (ability == ABILITY_FULL_METAL_BODY)
MESSAGE("Foe Solgaleo's Full Metal Body prevents stat loss!");
else if (ability == ABILITY_WHITE_SMOKE)
MESSAGE("Foe Torkoal's White Smoke prevents stat loss!");
else
MESSAGE("Foe Metang's Clear Body prevents stat loss!");
HP_BAR(player, captureDamage: &turnTwoHit);
} THEN {
EXPECT_EQ(turnOneHit, turnTwoHit);
}
}
SINGLE_BATTLE_TEST("Clear Body prevents stat stage reduction from moves")
SINGLE_BATTLE_TEST("Clear Body, Full Metal Body, and White Smoke prevent stat stage reduction from moves")
{
u16 move;
PARAMETRIZE{ move = MOVE_GROWL; }
PARAMETRIZE{ move = MOVE_LEER; }
PARAMETRIZE{ move = MOVE_CONFIDE; }
PARAMETRIZE{ move = MOVE_FAKE_TEARS; }
PARAMETRIZE{ move = MOVE_SCARY_FACE; }
PARAMETRIZE{ move = MOVE_SWEET_SCENT; }
PARAMETRIZE{ move = MOVE_SAND_ATTACK; }
u16 move = MOVE_NONE;
u32 j, species = SPECIES_NONE, ability = ABILITY_NONE;
static const u16 statReductionMoves[] = {
MOVE_GROWL,
MOVE_LEER,
MOVE_CONFIDE,
MOVE_FAKE_TEARS,
MOVE_SCARY_FACE,
MOVE_SWEET_SCENT,
MOVE_SAND_ATTACK,
};
for (j = 0; j < ARRAY_COUNT(statReductionMoves); j++)
{
PARAMETRIZE{ species = SPECIES_METANG; ability = ABILITY_CLEAR_BODY; move = statReductionMoves[j]; }
PARAMETRIZE{ species = SPECIES_SOLGALEO; ability = ABILITY_FULL_METAL_BODY; move = statReductionMoves[j]; }
PARAMETRIZE{ species = SPECIES_TORKOAL; ability = ABILITY_WHITE_SMOKE; move = statReductionMoves[j]; }
}
GIVEN {
ASSUME(gMovesInfo[MOVE_GROWL].effect == EFFECT_ATTACK_DOWN);
@ -48,7 +66,7 @@ SINGLE_BATTLE_TEST("Clear Body prevents stat stage reduction from moves")
ASSUME(gMovesInfo[MOVE_SWEET_SCENT].effect == (B_UPDATED_MOVE_DATA >= GEN_6 ? EFFECT_EVASION_DOWN_2 : EFFECT_EVASION_DOWN));
ASSUME(gMovesInfo[MOVE_SAND_ATTACK].effect == EFFECT_ACCURACY_DOWN);
PLAYER(SPECIES_WOBBUFFET)
OPPONENT(SPECIES_BELDUM) { Ability(ABILITY_CLEAR_BODY); }
OPPONENT(species) { Ability(ability); }
} WHEN {
TURN { MOVE(player, move); }
} SCENE {
@ -56,18 +74,27 @@ SINGLE_BATTLE_TEST("Clear Body prevents stat stage reduction from moves")
ANIMATION(ANIM_TYPE_MOVE, move, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
}
ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY);
MESSAGE("Foe Beldum's Clear Body prevents stat loss!");
ABILITY_POPUP(opponent, ability);
if (ability == ABILITY_FULL_METAL_BODY)
MESSAGE("Foe Solgaleo's Full Metal Body prevents stat loss!");
else if (ability == ABILITY_WHITE_SMOKE)
MESSAGE("Foe Torkoal's White Smoke prevents stat loss!");
else
MESSAGE("Foe Metang's Clear Body prevents stat loss!");
}
}
SINGLE_BATTLE_TEST("Clear Body prevents Sticky Web")
SINGLE_BATTLE_TEST("Clear Body, Full Metal Body, and White Smoke prevent Sticky Web effect on switchin")
{
u32 species, ability;
PARAMETRIZE{ species = SPECIES_METANG; ability = ABILITY_CLEAR_BODY; }
PARAMETRIZE{ species = SPECIES_SOLGALEO; ability = ABILITY_FULL_METAL_BODY; }
PARAMETRIZE{ species = SPECIES_TORKOAL; ability = ABILITY_WHITE_SMOKE; }
GIVEN {
ASSUME(gMovesInfo[MOVE_STICKY_WEB].effect == EFFECT_STICKY_WEB);
PLAYER(SPECIES_WOBBUFFET)
OPPONENT(SPECIES_WOBBUFFET)
OPPONENT(SPECIES_BELDUM) { Ability(ABILITY_CLEAR_BODY); }
OPPONENT(species) { Ability(ability); }
} WHEN {
TURN { MOVE(player, MOVE_STICKY_WEB); }
TURN { SWITCH(opponent, 1); }
@ -75,32 +102,43 @@ SINGLE_BATTLE_TEST("Clear Body prevents Sticky Web")
NONE_OF {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
}
ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY);
MESSAGE("Foe Beldum's Clear Body prevents stat loss!");
ABILITY_POPUP(opponent, ability);
if (ability == ABILITY_FULL_METAL_BODY)
MESSAGE("Foe Solgaleo's Full Metal Body prevents stat loss!");
else if (ability == ABILITY_WHITE_SMOKE)
MESSAGE("Foe Torkoal's White Smoke prevents stat loss!");
else
MESSAGE("Foe Metang's Clear Body prevents stat loss!");
}
}
SINGLE_BATTLE_TEST("Clear Body doesn't prevent stat stage reduction from moves used by the user")
SINGLE_BATTLE_TEST("Clear Body, Full Metal Body, and White Smoke don't prevent stat stage reduction from moves used by the user")
{
u32 species, ability;
PARAMETRIZE{ species = SPECIES_METANG; ability = ABILITY_CLEAR_BODY; }
PARAMETRIZE{ species = SPECIES_SOLGALEO; ability = ABILITY_FULL_METAL_BODY; }
PARAMETRIZE{ species = SPECIES_TORKOAL; ability = ABILITY_WHITE_SMOKE; }
GIVEN {
ASSUME(MoveHasAdditionalEffectSelf(MOVE_SUPERPOWER, MOVE_EFFECT_ATK_DEF_DOWN) == TRUE);
PLAYER(SPECIES_WOBBUFFET)
OPPONENT(SPECIES_BELDUM) { Ability(ABILITY_CLEAR_BODY); }
OPPONENT(species) { Ability(ability); }
} WHEN {
TURN { MOVE(opponent, MOVE_SUPERPOWER); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUPERPOWER, opponent);
NONE_OF {
ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY);
MESSAGE("Foe Beldum's Clear Body prevents stat loss!");
ABILITY_POPUP(opponent, ability);
MESSAGE("Foe Solgaleo's Full Metal Body prevents stat loss!");
MESSAGE("Foe Torkoal's White Smoke prevents stat loss!");
MESSAGE("Foe Metang's Clear Body prevents stat loss!");
}
}
}
SINGLE_BATTLE_TEST("Mold Breaker, Teravolt, and Turboblaze ignore Clear Body")
SINGLE_BATTLE_TEST("Mold Breaker, Teravolt, and Turboblaze ignore Clear Body and White Smoke, but not Full Metal Body")
{
u32 j, k;
u16 ability = ABILITY_NONE;
u32 j, k, species = SPECIES_NONE, ability = ABILITY_NONE;
u16 breakerAbility = ABILITY_NONE;
u16 move = ABILITY_NONE;
static const u16 breakerAbilities[] = {
ABILITY_MOLD_BREAKER,
@ -121,7 +159,9 @@ SINGLE_BATTLE_TEST("Mold Breaker, Teravolt, and Turboblaze ignore Clear Body")
{
for (k = 0; k < ARRAY_COUNT(breakerAbilities); k++)
{
PARAMETRIZE{ move = statReductionMoves[j]; ability = breakerAbilities[k]; }
PARAMETRIZE{ species = SPECIES_METANG; ability = ABILITY_CLEAR_BODY; move = statReductionMoves[j]; breakerAbility = breakerAbilities[k]; }
PARAMETRIZE{ species = SPECIES_SOLGALEO; ability = ABILITY_FULL_METAL_BODY; move = statReductionMoves[j]; breakerAbility = breakerAbilities[k]; }
PARAMETRIZE{ species = SPECIES_TORKOAL; ability = ABILITY_WHITE_SMOKE; move = statReductionMoves[j]; breakerAbility = breakerAbilities[k]; }
}
}
@ -133,146 +173,242 @@ SINGLE_BATTLE_TEST("Mold Breaker, Teravolt, and Turboblaze ignore Clear Body")
ASSUME(gMovesInfo[MOVE_SCARY_FACE].effect == EFFECT_SPEED_DOWN_2);
ASSUME(gMovesInfo[MOVE_SWEET_SCENT].effect == (B_UPDATED_MOVE_DATA >= GEN_6 ? EFFECT_EVASION_DOWN_2 : EFFECT_EVASION_DOWN));
ASSUME(gMovesInfo[MOVE_SAND_ATTACK].effect == EFFECT_ACCURACY_DOWN);
PLAYER(SPECIES_WOBBUFFET) { Ability(ability); }
OPPONENT(SPECIES_BELDUM) { Ability(ABILITY_CLEAR_BODY); }
PLAYER(SPECIES_WOBBUFFET) { Ability(breakerAbility); }
OPPONENT(species) { Ability(ability); }
} WHEN {
TURN { MOVE(player, move); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, move, player);
NONE_OF {
ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY);
MESSAGE("Foe Beldum's Clear Body prevents stat loss!");
if (ability == ABILITY_FULL_METAL_BODY){ // Full Metal Body can't be ignored by breaker abilities
NOT ANIMATION(ANIM_TYPE_MOVE, move, player);
ABILITY_POPUP(opponent, ability);
MESSAGE("Foe Solgaleo's Full Metal Body prevents stat loss!");
}
else{
ANIMATION(ANIM_TYPE_MOVE, move, player);
NONE_OF {
ABILITY_POPUP(opponent, ability);
MESSAGE("Foe Solgaleo's Full Metal Body prevents stat loss!");
MESSAGE("Foe Torkoal's White Smoke prevents stat loss!");
MESSAGE("Foe Metang's Clear Body prevents stat loss!");
}
}
}
}
SINGLE_BATTLE_TEST("Clear Body doesn't prevent Speed reduction from Iron Ball")
SINGLE_BATTLE_TEST("Clear Body, Full Metal Body, and White Smoke don't prevent Speed reduction from Iron Ball")
{
u16 heldItem;
PARAMETRIZE{ heldItem = ITEM_NONE; }
PARAMETRIZE{ heldItem = ITEM_IRON_BALL; }
u32 j, species = SPECIES_NONE, ability = ABILITY_NONE;
u16 heldItem = ITEM_NONE;
static const u16 heldItems[] = {
ITEM_NONE,
ITEM_IRON_BALL,
};
for (j = 0; j < ARRAY_COUNT(heldItems); j++)
{
PARAMETRIZE{ species = SPECIES_METANG; ability = ABILITY_CLEAR_BODY; heldItem = heldItems[j]; }
PARAMETRIZE{ species = SPECIES_SOLGALEO; ability = ABILITY_FULL_METAL_BODY; heldItem = heldItems[j]; }
PARAMETRIZE{ species = SPECIES_TORKOAL; ability = ABILITY_WHITE_SMOKE; heldItem = heldItems[j]; }
}
GIVEN {
ASSUME(gItemsInfo[ITEM_IRON_BALL].holdEffect == HOLD_EFFECT_IRON_BALL);
PLAYER(SPECIES_WOBBUFFET) { Speed(4); }
OPPONENT(SPECIES_BELDUM) { Speed(6); Ability(ABILITY_CLEAR_BODY); Item(heldItem); }
OPPONENT(species) { Speed(6); Ability(ability); Item(heldItem); }
} WHEN {
TURN { }
} SCENE {
NOT ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY);
NOT ABILITY_POPUP(opponent, ability);
if (heldItem == ITEM_IRON_BALL) {
MESSAGE("Wobbuffet used Celebrate!");
MESSAGE("Foe Beldum used Celebrate!");
if (ability == ABILITY_FULL_METAL_BODY)
MESSAGE("Foe Solgaleo used Celebrate!");
else if (ability == ABILITY_WHITE_SMOKE)
MESSAGE("Foe Torkoal used Celebrate!");
else
MESSAGE("Foe Metang used Celebrate!");
} else {
MESSAGE("Foe Beldum used Celebrate!");
if (ability == ABILITY_FULL_METAL_BODY)
MESSAGE("Foe Solgaleo used Celebrate!");
else if (ability == ABILITY_WHITE_SMOKE)
MESSAGE("Foe Torkoal used Celebrate!");
else
MESSAGE("Foe Metang used Celebrate!");
MESSAGE("Wobbuffet used Celebrate!");
}
}
}
SINGLE_BATTLE_TEST("Clear Body doesn't prevent Speed reduction from paralysis")
SINGLE_BATTLE_TEST("Clear Body, Full Metal Body, and White Smoke don't prevent Speed reduction from paralysis")
{
u32 species, ability;
PARAMETRIZE{ species = SPECIES_METANG; ability = ABILITY_CLEAR_BODY; }
PARAMETRIZE{ species = SPECIES_SOLGALEO; ability = ABILITY_FULL_METAL_BODY; }
PARAMETRIZE{ species = SPECIES_TORKOAL; ability = ABILITY_WHITE_SMOKE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Speed(4); }
OPPONENT(SPECIES_BELDUM) { Speed(6); Ability(ABILITY_CLEAR_BODY); }
OPPONENT(species) { Speed(6); Ability(ability); }
} WHEN {
TURN { MOVE(player, MOVE_THUNDER_WAVE); }
TURN { MOVE(player, MOVE_THUNDER_WAVE); }
} SCENE {
MESSAGE("Foe Beldum used Celebrate!");
if (ability == ABILITY_FULL_METAL_BODY)
MESSAGE("Foe Solgaleo used Celebrate!");
else if (ability == ABILITY_WHITE_SMOKE)
MESSAGE("Foe Torkoal used Celebrate!");
else
MESSAGE("Foe Metang used Celebrate!");
MESSAGE("Wobbuffet used Thunder Wave!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDER_WAVE, player);
NOT ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY);
NOT ABILITY_POPUP(opponent, ability);
MESSAGE("Wobbuffet used Thunder Wave!");
ONE_OF {
MESSAGE("Foe Beldum used Celebrate!");
MESSAGE("Foe Beldum is paralyzed! It can't move!");
MESSAGE("Foe Metang used Celebrate!");
MESSAGE("Foe Metang is paralyzed! It can't move!");
MESSAGE("Foe Solgaleo used Celebrate!");
MESSAGE("Foe Solgaleo is paralyzed! It can't move!");
MESSAGE("Foe Torkoal used Celebrate!");
MESSAGE("Foe Torkoal is paralyzed! It can't move!");
}
}
}
SINGLE_BATTLE_TEST("Clear Body doesn't prevent Attack reduction from burn", s16 damage)
SINGLE_BATTLE_TEST("Clear Body, Full Metal Body, and White Smoke don't prevent Attack reduction from burn", s16 damage)
{
bool32 burned;
PARAMETRIZE{ burned = FALSE; }
PARAMETRIZE{ burned = TRUE; }
bool32 burned = FALSE;
u32 species, ability;
PARAMETRIZE{ species = SPECIES_METANG; ability = ABILITY_CLEAR_BODY; burned = FALSE; }
PARAMETRIZE{ species = SPECIES_METANG; ability = ABILITY_CLEAR_BODY; burned = TRUE; }
PARAMETRIZE{ species = SPECIES_SOLGALEO; ability = ABILITY_FULL_METAL_BODY; burned = FALSE; }
PARAMETRIZE{ species = SPECIES_SOLGALEO; ability = ABILITY_FULL_METAL_BODY; burned = TRUE; }
PARAMETRIZE{ species = SPECIES_TORKOAL; ability = ABILITY_WHITE_SMOKE; burned = FALSE; }
PARAMETRIZE{ species = SPECIES_TORKOAL; ability = ABILITY_WHITE_SMOKE; burned = TRUE; }
GIVEN {
ASSUME(gMovesInfo[MOVE_TACKLE].category == DAMAGE_CATEGORY_PHYSICAL);
PLAYER(SPECIES_WOBBUFFET)
OPPONENT(SPECIES_BELDUM) { Ability(ABILITY_CLEAR_BODY); if (burned) Status1(STATUS1_BURN); }
OPPONENT(species) { Ability(ability); if (burned) Status1(STATUS1_BURN); }
} WHEN {
TURN { MOVE(opponent, MOVE_TACKLE); }
} SCENE {
NOT ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY);
NOT ABILITY_POPUP(opponent, ability);
HP_BAR(player, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.5), results[1].damage);
}
}
SINGLE_BATTLE_TEST("Clear Body doesn't prevent receiving negative stat changes from Baton Pass")
SINGLE_BATTLE_TEST("Clear Body, Full Metal Body, and White Smoke don't prevent receiving negative stat changes from Baton Pass")
{
u32 species, ability;
PARAMETRIZE{ species = SPECIES_METANG; ability = ABILITY_CLEAR_BODY; }
PARAMETRIZE{ species = SPECIES_SOLGALEO; ability = ABILITY_FULL_METAL_BODY; }
PARAMETRIZE{ species = SPECIES_TORKOAL; ability = ABILITY_WHITE_SMOKE; }
GIVEN {
ASSUME(gMovesInfo[MOVE_SCARY_FACE].effect == EFFECT_SPEED_DOWN_2);
ASSUME(gMovesInfo[MOVE_BATON_PASS].effect == EFFECT_BATON_PASS);
PLAYER(SPECIES_WOBBUFFET) { Speed(4); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(3); }
OPPONENT(SPECIES_BELDUM) { Speed(6); Ability(ABILITY_CLEAR_BODY); }
OPPONENT(species) { Speed(6); Ability(ability); }
} WHEN {
TURN { MOVE(player, MOVE_SCARY_FACE); MOVE(opponent, MOVE_BATON_PASS); SEND_OUT(opponent, 1); }
TURN { MOVE(player, MOVE_SCARY_FACE); }
} SCENE {
MESSAGE("Wobbuffet used Scary Face!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCARY_FACE, player);
ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY);
MESSAGE("Foe Beldum used Celebrate!");
ABILITY_POPUP(opponent, ability);
if (ability == ABILITY_FULL_METAL_BODY)
MESSAGE("Foe Solgaleo used Celebrate!");
else if (ability == ABILITY_WHITE_SMOKE)
MESSAGE("Foe Torkoal used Celebrate!");
else
MESSAGE("Foe Metang used Celebrate!");
}
}
SINGLE_BATTLE_TEST("Clear Body doesn't prevent Topsy-Turvy")
SINGLE_BATTLE_TEST("Clear Body, Full Metal Body, and White Smoke don't prevent Topsy-Turvy")
{
u32 species, ability;
PARAMETRIZE{ species = SPECIES_METANG; ability = ABILITY_CLEAR_BODY; }
PARAMETRIZE{ species = SPECIES_SOLGALEO; ability = ABILITY_FULL_METAL_BODY; }
PARAMETRIZE{ species = SPECIES_TORKOAL; ability = ABILITY_WHITE_SMOKE; }
GIVEN {
ASSUME(gMovesInfo[MOVE_TOPSY_TURVY].effect == EFFECT_TOPSY_TURVY);
ASSUME(gMovesInfo[MOVE_SCARY_FACE].effect == EFFECT_SPEED_DOWN_2);
ASSUME(gMovesInfo[MOVE_BATON_PASS].effect == EFFECT_BATON_PASS);
PLAYER(SPECIES_WOBBUFFET) { Speed(4); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(3); }
OPPONENT(SPECIES_BELDUM) { Speed(6); Ability(ABILITY_CLEAR_BODY); }
OPPONENT(species) { Speed(6); Ability(ability); }
} WHEN {
TURN { MOVE(player, MOVE_SCARY_FACE); MOVE(opponent, MOVE_BATON_PASS); SEND_OUT(opponent, 1); }
TURN { MOVE(player, MOVE_TOPSY_TURVY); }
TURN { MOVE(player, MOVE_SCARY_FACE); }
} SCENE {
MESSAGE("Wobbuffet used Topsy-Turvy!");
NOT ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY);
NOT ABILITY_POPUP(opponent, ability);
ANIMATION(ANIM_TYPE_MOVE, MOVE_TOPSY_TURVY, player);
MESSAGE("Foe Beldum used Celebrate!");
MESSAGE("Foe Beldum used Celebrate!");
if (ability == ABILITY_FULL_METAL_BODY) {
MESSAGE("Foe Solgaleo used Celebrate!");
MESSAGE("Foe Solgaleo used Celebrate!");
}
else if (ability == ABILITY_WHITE_SMOKE) {
MESSAGE("Foe Torkoal used Celebrate!");
MESSAGE("Foe Torkoal used Celebrate!");
}
else {
MESSAGE("Foe Metang used Celebrate!");
MESSAGE("Foe Metang used Celebrate!");
}
MESSAGE("Wobbuffet used Scary Face!");
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SCARY_FACE, player);
ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY);
ABILITY_POPUP(opponent, ability);
}
}
SINGLE_BATTLE_TEST("Clear Body doesn't prevent Spectral Thief from resetting positive stat changes")
SINGLE_BATTLE_TEST("Clear Body, Full Metal Body, and White Smoke don't prevent Spectral Thief from resetting positive stat changes")
{
u32 species, ability;
PARAMETRIZE{ species = SPECIES_METANG; ability = ABILITY_CLEAR_BODY; }
PARAMETRIZE{ species = SPECIES_SOLGALEO; ability = ABILITY_FULL_METAL_BODY; }
PARAMETRIZE{ species = SPECIES_TORKOAL; ability = ABILITY_WHITE_SMOKE; }
GIVEN {
ASSUME(MoveHasAdditionalEffect(MOVE_SPECTRAL_THIEF, MOVE_EFFECT_SPECTRAL_THIEF) == TRUE);
ASSUME(gMovesInfo[MOVE_AGILITY].effect == EFFECT_SPEED_UP_2);
PLAYER(SPECIES_WOBBUFFET) { Speed(4); }
OPPONENT(SPECIES_METANG) { Speed(5); Ability(ABILITY_CLEAR_BODY); }
OPPONENT(species) { Speed(5); Ability(ability); }
} WHEN {
TURN{ MOVE(opponent, MOVE_AGILITY); }
TURN{ MOVE(player, MOVE_SPECTRAL_THIEF); }
TURN{ }
} SCENE {
MESSAGE("Foe Metang used Agility!");
if (ability == ABILITY_FULL_METAL_BODY)
MESSAGE("Foe Solgaleo used Agility!");
else if (ability == ABILITY_WHITE_SMOKE)
MESSAGE("Foe Torkoal used Agility!");
else
MESSAGE("Foe Metang used Agility!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_AGILITY, opponent);
MESSAGE("Wobbuffet used Celebrate!");
MESSAGE("Foe Metang used Celebrate!");
if (ability == ABILITY_FULL_METAL_BODY)
MESSAGE("Foe Solgaleo used Celebrate!");
else if (ability == ABILITY_WHITE_SMOKE)
MESSAGE("Foe Torkoal used Celebrate!");
else
MESSAGE("Foe Metang used Celebrate!");
MESSAGE("Wobbuffet used Spectral Thief!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPECTRAL_THIEF, player);
NOT ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY);
NOT ABILITY_POPUP(opponent, ability);
MESSAGE("Wobbuffet used Celebrate!");
MESSAGE("Foe Metang used Celebrate!");
if (ability == ABILITY_FULL_METAL_BODY)
MESSAGE("Foe Solgaleo used Celebrate!");
else if (ability == ABILITY_WHITE_SMOKE)
MESSAGE("Foe Torkoal used Celebrate!");
else
MESSAGE("Foe Metang used Celebrate!");
}
}

View file

@ -1,38 +0,0 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Full Metal Body prevents intimidate")
{
s16 turnOneHit;
s16 turnTwoHit;
GIVEN {
PLAYER(SPECIES_EKANS) { Ability(ABILITY_SHED_SKIN); };
PLAYER(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); };
OPPONENT(SPECIES_SOLGALEO) { Ability(ABILITY_FULL_METAL_BODY); };
} WHEN {
TURN { MOVE(opponent, MOVE_TACKLE); }
TURN { SWITCH(player, 1); MOVE(opponent, MOVE_TACKLE); }
} SCENE {
HP_BAR(player, captureDamage: &turnOneHit);
ABILITY_POPUP(player, ABILITY_INTIMIDATE);
NONE_OF { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); }
ABILITY_POPUP(opponent, ABILITY_FULL_METAL_BODY);
MESSAGE("Foe Solgaleo's Full Metal Body prevents stat loss!");
HP_BAR(player, captureDamage: &turnTwoHit);
} THEN {
EXPECT_EQ(turnOneHit, turnTwoHit);
}
}
TO_DO_BATTLE_TEST("Full Metal Body prevents stat stage reduction from moves"); // Growl, Leer, Confide, Fake Tears, Scary Face, Sweet Scent, Sand Attack (Attack, Defense, Sp. Attack, Sp. Defense, Speed, Evasion, Accuracy
TO_DO_BATTLE_TEST("Full Metal Body prevents Sticky Web");
TO_DO_BATTLE_TEST("Full Metal Body doesn't prevent stat stage reduction from moves used by the user"); // e.g. Superpower
TO_DO_BATTLE_TEST("Full Metal Body doesn't prevent Speed reduction from Iron Ball");
TO_DO_BATTLE_TEST("Full Metal Body doesn't prevent Speed reduction from paralysis");
TO_DO_BATTLE_TEST("Full Metal Body doesn't prevent Attack reduction from burn");
TO_DO_BATTLE_TEST("Full Metal Body doesn't prevent receiving negative stat changes from Baton Pass");
TO_DO_BATTLE_TEST("Full Metal Body doesn't prevent Topsy-Turvy");
TO_DO_BATTLE_TEST("Full Metal Body doesn't prevent Spectral Thief from resetting positive stat changes");
TO_DO_BATTLE_TEST("Full Metal Body is ignored by Mold Breaker");

121
test/battle/ability/moxie.c Normal file
View file

@ -0,0 +1,121 @@
#include "global.h"
#include "test/battle.h"
DOUBLE_BATTLE_TEST("Moxie raises Attack by one stage after directly causing a Pokemon to faint")
{
ASSUME(gMovesInfo[MOVE_EARTHQUAKE].target == MOVE_TARGET_FOES_AND_ALLY);
GIVEN {
PLAYER(SPECIES_SALAMENCE) { Ability(ABILITY_MOXIE); }
PLAYER(SPECIES_SNORUNT) { HP(1); }
OPPONENT(SPECIES_GLALIE) { HP(1); }
OPPONENT(SPECIES_ABRA) { HP(1); }
OPPONENT(SPECIES_ABRA);
} WHEN {
TURN { MOVE(playerLeft, MOVE_EARTHQUAKE); SEND_OUT(opponentLeft, 2); }
} SCENE {
int i;
ANIMATION(ANIM_TYPE_MOVE, MOVE_EARTHQUAKE, playerLeft);
for (i = 0; i < 3; i++) {
ONE_OF {
MESSAGE("Snorunt fainted!");
MESSAGE("Foe Glalie fainted!");
MESSAGE("Foe Abra fainted!");
}
ABILITY_POPUP(playerLeft, ABILITY_MOXIE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
MESSAGE("Salamence's Moxie raised its Attack!");
}
} THEN {
EXPECT_EQ(playerLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 3);
}
}
DOUBLE_BATTLE_TEST("Moxie does not trigger if Pokemon faint to indirect damage or damage from other Pokemon")
{
GIVEN {
PLAYER(SPECIES_SALAMENCE) { Ability(ABILITY_MOXIE); }
PLAYER(SPECIES_SNORUNT) { HP(1); Status1(STATUS1_POISON); }
OPPONENT(SPECIES_GLALIE) { HP(1); Status1(STATUS1_BURN); }
OPPONENT(SPECIES_ABRA) { HP(1); }
OPPONENT(SPECIES_ABRA);
} WHEN {
TURN { MOVE(playerRight, MOVE_QUICK_ATTACK, target: opponentRight); SEND_OUT(opponentLeft, 2); }
} SCENE {
int i;
ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_ATTACK, playerRight);
for (i = 0; i < 3; i++) {
ONE_OF {
MESSAGE("Snorunt fainted!");
MESSAGE("Foe Glalie fainted!");
MESSAGE("Foe Abra fainted!");
}
NONE_OF {
ABILITY_POPUP(playerLeft, ABILITY_MOXIE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
MESSAGE("Salamence's Moxie raised its Attack!");
}
}
} THEN {
EXPECT_EQ(playerLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
}
}
SINGLE_BATTLE_TEST("Moxie does not trigger when already at maximum Attack stage")
{
ASSUME(gMovesInfo[MOVE_BELLY_DRUM].effect == EFFECT_BELLY_DRUM);
GIVEN {
PLAYER(SPECIES_SALAMENCE) { Ability(ABILITY_MOXIE); }
OPPONENT(SPECIES_SNORUNT) { HP(1); }
OPPONENT(SPECIES_SNORUNT);
} WHEN {
TURN { MOVE(player, MOVE_BELLY_DRUM); }
TURN { MOVE(player, MOVE_QUICK_ATTACK); SEND_OUT(opponent, 1); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BELLY_DRUM, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
MESSAGE("Salamence cut its own HP and maximized ATTACK!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_ATTACK, player);
MESSAGE("Foe Snorunt fainted!");
NONE_OF {
ABILITY_POPUP(player, ABILITY_MOXIE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
MESSAGE("Salamence's Moxie raised its Attack!");
}
} THEN {
EXPECT_EQ(player->statStages[STAT_ATK], MAX_STAT_STAGE);
}
}
DOUBLE_BATTLE_TEST("Moxie does not increase damage done by the same move that causes another Pokemon to faint")
{
s16 damage[2];
ASSUME(gMovesInfo[MOVE_EARTHQUAKE].target == MOVE_TARGET_FOES_AND_ALLY);
KNOWN_FAILING; // Requires simultaneous damage implementation
GIVEN {
PLAYER(SPECIES_SALAMENCE) { Ability(ABILITY_MOXIE); }
PLAYER(SPECIES_ABRA) { HP(1); }
OPPONENT(SPECIES_GLALIE);
OPPONENT(SPECIES_GLALIE);
OPPONENT(SPECIES_ABRA);
} WHEN {
TURN { MOVE(playerLeft, MOVE_EARTHQUAKE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_EARTHQUAKE, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damage[0]);
HP_BAR(playerRight);
MESSAGE("Abra fainted!");
ABILITY_POPUP(playerLeft, ABILITY_MOXIE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
MESSAGE("Salamence's Moxie raised its Attack!");
HP_BAR(opponentRight, captureDamage: &damage[1]);
} THEN {
EXPECT_EQ(playerLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(damage[0], damage[1]);
}
}

View file

@ -1,39 +0,0 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("White Smoke prevents intimidate")
{
s16 turnOneHit;
s16 turnTwoHit;
GIVEN {
PLAYER(SPECIES_EKANS) { Ability(ABILITY_SHED_SKIN); };
PLAYER(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); };
OPPONENT(SPECIES_TORKOAL) { Ability(ABILITY_WHITE_SMOKE); };
} WHEN {
TURN { MOVE(opponent, MOVE_TACKLE); }
TURN { SWITCH(player, 1); MOVE(opponent, MOVE_TACKLE); }
} SCENE {
HP_BAR(player, captureDamage: &turnOneHit);
ABILITY_POPUP(player, ABILITY_INTIMIDATE);
NONE_OF { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); }
ABILITY_POPUP(opponent, ABILITY_WHITE_SMOKE);
MESSAGE("Foe Torkoal's White Smoke prevents stat loss!");
HP_BAR(player, captureDamage: &turnTwoHit);
} THEN {
EXPECT_EQ(turnOneHit, turnTwoHit);
}
}
TO_DO_BATTLE_TEST("White Smoke prevents stat stage reduction from moves"); // Growl, Leer, Confide, Fake Tears, Scary Face, Sweet Scent, Sand Attack (Attack, Defense, Sp. Attack, Sp. Defense, Speed, Evasion, Accuracy
TO_DO_BATTLE_TEST("White Smoke prevents Sticky Web");
TO_DO_BATTLE_TEST("White Smoke doesn't prevent stat stage reduction from moves used by the user"); // e.g. Superpower
TO_DO_BATTLE_TEST("White Smoke doesn't prevent Speed reduction from Iron Ball");
TO_DO_BATTLE_TEST("White Smoke doesn't prevent Speed reduction from paralysis");
TO_DO_BATTLE_TEST("White Smoke doesn't prevent Attack reduction from burn");
TO_DO_BATTLE_TEST("White Smoke doesn't prevent receiving negative stat changes from Baton Pass");
TO_DO_BATTLE_TEST("White Smoke doesn't prevent Topsy-Turvy");
TO_DO_BATTLE_TEST("White Smoke doesn't prevent Spectral Thief from resetting positive stat changes");
TO_DO_BATTLE_TEST("White Smoke is ignored by Mold Breaker");

View file

@ -0,0 +1,25 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Move Animation Test: Smack Down works when used 15 times in a row")
{
u16 j, nTurns = 15;
FORCE_MOVE_ANIM(TRUE);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
for (j = 0; j < nTurns; j++)
{
TURN { MOVE(player, MOVE_SMACK_DOWN); MOVE(opponent, MOVE_HELPING_HAND); } // Helping Hand, so there's no anim on the opponent's side.
}
} SCENE {
for (j = 0; j < nTurns; j++)
{
ANIMATION(ANIM_TYPE_MOVE, MOVE_SMACK_DOWN, player);
}
} THEN {
FORCE_MOVE_ANIM(FALSE);
}
}

View file

@ -17,7 +17,6 @@ SINGLE_BATTLE_TEST("Body Press uses physical defense stat of target", s16 damage
GIVEN {
ASSUME(gMovesInfo[MOVE_DRILL_PECK].power == gMovesInfo[MOVE_BODY_PRESS].power);
ASSUME(gMovesInfo[MOVE_CHARM].effect == EFFECT_ATTACK_DOWN_2);
ASSUME(gMovesInfo[MOVE_CHARM].effect == EFFECT_ATTACK_DOWN_2);
PLAYER(SPECIES_MEW);
OPPONENT(SPECIES_SHELLDER);
} WHEN {
@ -30,8 +29,95 @@ SINGLE_BATTLE_TEST("Body Press uses physical defense stat of target", s16 damage
}
}
TO_DO_BATTLE_TEST("Body Press's damage depends on the user's base Defense instead of its base Attack");
TO_DO_BATTLE_TEST("Body Press's damage depends on the user's Defense stat stages");
SINGLE_BATTLE_TEST("Body Press's damage depends on the user's base Defense instead of its base Attack", s16 damage)
{
u32 def, atk;
PARAMETRIZE { def = 150; atk = 179; } // Atk is higher
PARAMETRIZE { atk = 150; def = 179; } // Atk is lower
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Attack(atk); Defense(def); }
} WHEN {
TURN { MOVE(opponent, MOVE_BODY_PRESS); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BODY_PRESS, opponent);
HP_BAR(player, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_GT(results[1].damage, results[0].damage);
}
}
SINGLE_BATTLE_TEST("Body Press's damage depends on the user's Defense and not Attack stat stages", s16 damage)
{
u32 move;
PARAMETRIZE { move = MOVE_IRON_DEFENSE; }
PARAMETRIZE { move = MOVE_SWORDS_DANCE; }
PARAMETRIZE { move = MOVE_CELEBRATE; } // Nothing, stats are default
GIVEN {
ASSUME(gMovesInfo[MOVE_IRON_DEFENSE].effect == EFFECT_DEFENSE_UP_2);
ASSUME(gMovesInfo[MOVE_SWORDS_DANCE].effect == EFFECT_ATTACK_UP_2);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Attack(150); Defense(150); }
} WHEN {
TURN { MOVE(opponent, move); }
TURN { MOVE(opponent, MOVE_BODY_PRESS); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BODY_PRESS, opponent);
HP_BAR(player, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_GT(results[0].damage, results[1].damage);
EXPECT_EQ(results[1].damage, results[2].damage);
}
}
SINGLE_BATTLE_TEST("Body Press uses Defense Stat even in Wonder Room", s16 damage)
{
u32 move;
PARAMETRIZE { move = MOVE_WONDER_ROOM; }
PARAMETRIZE { move = MOVE_CELEBRATE; }
GIVEN {
ASSUME(gMovesInfo[MOVE_WONDER_ROOM].effect == EFFECT_WONDER_ROOM);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { SpDefense(50); Defense(150); }
} WHEN {
TURN { MOVE(opponent, move); }
TURN { MOVE(opponent, MOVE_BODY_PRESS); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BODY_PRESS, opponent);
HP_BAR(player, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_EQ(results[0].damage, results[1].damage);
}
}
SINGLE_BATTLE_TEST("Body Press uses Special Defense stat Stages in Wonder Room", s16 damage)
{
u32 move;
PARAMETRIZE { move = MOVE_IRON_DEFENSE; }
PARAMETRIZE { move = MOVE_AMNESIA; }
PARAMETRIZE { move = MOVE_CELEBRATE; } // Nothing, stats are default
GIVEN {
ASSUME(gMovesInfo[MOVE_IRON_DEFENSE].effect == EFFECT_DEFENSE_UP_2);
ASSUME(gMovesInfo[MOVE_AMNESIA].effect == EFFECT_SPECIAL_DEFENSE_UP_2);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { SpDefense(150); Defense(150); }
} WHEN {
TURN { MOVE(opponent, move); MOVE(player, MOVE_WONDER_ROOM); }
TURN { MOVE(opponent, MOVE_BODY_PRESS); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BODY_PRESS, opponent);
HP_BAR(player, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_GT(results[1].damage, results[0].damage);
EXPECT_EQ(results[0].damage, results[2].damage);
}
}
// Could be split into multiple tests or maybe to separate files based on the modifier?
TO_DO_BATTLE_TEST("Body Press's damage is influenced by all other Attack modifiers that are not stat stages");

View file

@ -6,40 +6,90 @@ ASSUMPTIONS
ASSUME(gMovesInfo[MOVE_ENCORE].effect == EFFECT_ENCORE);
}
SINGLE_BATTLE_TEST("Encore forces consecutive move uses for 2 turns for player")
SINGLE_BATTLE_TEST("Encore forces consecutive move uses for 3 turns for player: Encore used before move")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET) { Speed(10); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(20); }
} WHEN {
TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_CELEBRATE); }
TURN { MOVE(opponent, MOVE_ENCORE); MOVE(player, MOVE_CELEBRATE); }
// TURN { FORCED_MOVE(player); }
TURN { FORCED_MOVE(player); }
TURN { FORCED_MOVE(player); }
TURN { MOVE(player, MOVE_SPLASH); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_ENCORE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPLASH, player);
}
}
SINGLE_BATTLE_TEST("Encore forces consecutive move uses for 3 turns for player: Encore used after move")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Speed(20); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(10); }
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_ENCORE); }
TURN { FORCED_MOVE(player); }
TURN { FORCED_MOVE(player); }
TURN { FORCED_MOVE(player); }
TURN { MOVE(player, MOVE_SPLASH); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_ENCORE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPLASH, player);
}
}
SINGLE_BATTLE_TEST("Encore forces consecutive move uses for 2 turns for opponent")
SINGLE_BATTLE_TEST("Encore forces consecutive move uses for 3 turns for opponent: Encore used before move")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET) { Speed(20); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(10); }
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_CELEBRATE); }
TURN { MOVE(player, MOVE_ENCORE); MOVE(opponent, MOVE_CELEBRATE); }
// TURN { FORCED_MOVE(opponent); }
TURN { FORCED_MOVE(opponent); }
TURN { FORCED_MOVE(opponent); }
TURN { MOVE(opponent, MOVE_SPLASH); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_ENCORE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPLASH, opponent);
}
}
SINGLE_BATTLE_TEST("Encore forces consecutive move uses for 3 turns for opponent: Encore used after move")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Speed(10); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(20); }
} WHEN {
TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_ENCORE); }
TURN { FORCED_MOVE(opponent); }
TURN { FORCED_MOVE(opponent); }
TURN { FORCED_MOVE(opponent); }
TURN { MOVE(opponent, MOVE_SPLASH); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_ENCORE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPLASH, opponent);
}
}

View file

@ -25,4 +25,25 @@ SINGLE_BATTLE_TEST("Pursuited mon correctly switches out after it got hit and ac
}
}
// Checked so that Pursuit has only 1 PP and it forces the player to use Struggle.
SINGLE_BATTLE_TEST("Pursuit becomes a locked move after being used on switch-out while holding a Choice Item")
{
GIVEN {
ASSUME(gItemsInfo[ITEM_CHOICE_BAND].holdEffect == HOLD_EFFECT_CHOICE_BAND);
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_CHOICE_BAND); MovesWithPP({MOVE_PURSUIT, 1}, {MOVE_CELEBRATE, 10}, {MOVE_WATER_GUN, 10}, {MOVE_TACKLE, 10}); }
OPPONENT(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { SWITCH(opponent, 1); MOVE(player, MOVE_PURSUIT); }
TURN { FORCED_MOVE(player); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_PURSUIT, player);
HP_BAR(opponent);
MESSAGE("2 sent out Wobbuffet!");
MESSAGE("Wobbuffet used Struggle!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, player);
}
}
TO_DO_BATTLE_TEST("Baton Pass doesn't cause Pursuit to increase its power or priority");

View file

@ -85,6 +85,25 @@ SINGLE_BATTLE_TEST("Relic Song transforms Meloetta if used successfully")
}
}
SINGLE_BATTLE_TEST("Relic Song does not transform Pokemon other than Meloetta")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_RELIC_SONG); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_RELIC_SONG, player);
HP_BAR(opponent);
NONE_OF {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player);
MESSAGE("Wobbuffet transformed!");
}
} THEN {
EXPECT_EQ(player->species, SPECIES_WOBBUFFET);
}
}
SINGLE_BATTLE_TEST("Relic Song transforms Meloetta twice if used successfully")
{
GIVEN {

View file

@ -0,0 +1,113 @@
#include "global.h"
#include "event_data.h"
#include "test/battle.h"
#if B_VAR_STARTING_STATUS != 0
SINGLE_BATTLE_TEST("B_VAR_STARTING_STATUS starts a chosen terrain at the beginning of battle and lasts infinitely long")
{
u16 terrain;
PARAMETRIZE { terrain = STARTING_STATUS_GRASSY_TERRAIN; }
PARAMETRIZE { terrain = STARTING_STATUS_PSYCHIC_TERRAIN; }
PARAMETRIZE { terrain = STARTING_STATUS_MISTY_TERRAIN; }
PARAMETRIZE { terrain = STARTING_STATUS_ELECTRIC_TERRAIN; }
VarSet(B_VAR_STARTING_STATUS, terrain);
VarSet(B_VAR_STARTING_STATUS_TIMER, 0);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
// More than 5 turns
TURN { ; }
TURN { ; }
TURN { ; }
TURN { ; }
TURN { ; }
TURN { ; }
TURN { ; }
} SCENE {
switch (terrain) {
case STARTING_STATUS_GRASSY_TERRAIN:
MESSAGE("Grass grew to cover the battlefield!");
break;
case STARTING_STATUS_PSYCHIC_TERRAIN:
MESSAGE("The battlefield got weird!");
break;
case STARTING_STATUS_MISTY_TERRAIN:
MESSAGE("Mist swirled about the battlefield!");
break;
case STARTING_STATUS_ELECTRIC_TERRAIN:
MESSAGE("An electric current runs across the battlefield!");
break;
}
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_RESTORE_BG);
NONE_OF {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_RESTORE_BG);
MESSAGE("The weirdness disappeared from the battlefield.");
MESSAGE("The electricity disappeared from the battlefield.");
MESSAGE("The mist disappeared from the battlefield.");
MESSAGE("The grass disappeared from the battlefield.");
}
} THEN {
VarSet(B_VAR_STARTING_STATUS, 0);
}
}
SINGLE_BATTLE_TEST("Terrain started after the one which started the battle lasts only 5 turns")
{
bool32 viaMove;
PARAMETRIZE { viaMove = TRUE; }
PARAMETRIZE { viaMove = FALSE; }
VarSet(B_VAR_STARTING_STATUS, STARTING_STATUS_ELECTRIC_TERRAIN);
VarSet(B_VAR_STARTING_STATUS_TIMER, 0);
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Ability(viaMove == TRUE ? ABILITY_SHADOW_TAG : ABILITY_GRASSY_SURGE); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
// More than 5 turns
TURN { MOVE(player, viaMove == TRUE ? MOVE_GRASSY_TERRAIN : MOVE_CELEBRATE); }
TURN { ; }
TURN { ; }
TURN { ; }
TURN { ; }
TURN { ; }
TURN { ; }
} SCENE {
// Electric Terrain at battle's start
MESSAGE("An electric current runs across the battlefield!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_RESTORE_BG);
// Player uses Grassy Terrain
if (viaMove) {
MESSAGE("Wobbuffet used GrssyTerrain!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_GRASSY_TERRAIN, player);
MESSAGE("Grass grew to cover the battlefield!");
} else {
ABILITY_POPUP(player, ABILITY_GRASSY_SURGE);
MESSAGE("Grass grew to cover the battlefield!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_RESTORE_BG);
}
// 5 turns
MESSAGE("Wobbuffet used Celebrate!");
MESSAGE("Foe Wobbuffet used Celebrate!");
MESSAGE("Wobbuffet used Celebrate!");
MESSAGE("Foe Wobbuffet used Celebrate!");
MESSAGE("Wobbuffet used Celebrate!");
MESSAGE("Foe Wobbuffet used Celebrate!");
MESSAGE("The grass disappeared from the battlefield.");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_RESTORE_BG);
} THEN {
VarSet(B_VAR_STARTING_STATUS, 0);
}
}
#endif // B_VAR_STARTING_STATUS