mirror of
https://github.com/rh-hideout/pokeemerald-expansion.git
synced 2026-03-21 18:04:50 -05:00
30/11/25 master to upcoming merge
This commit is contained in:
commit
15d9342fe5
|
|
@ -32,6 +32,7 @@ Distributions with instructions:
|
|||
- [Debian](docs/install/linux/DEBIAN.md)
|
||||
- [Arch Linux](docs/install/linux/ARCH_LINUX.md)
|
||||
- [NixOS](docs/install/linux/NIXOS.md)
|
||||
- [Fedora](docs/install/linux/FEDORA.md)
|
||||
|
||||
Other distributions have to infer what to do from [general instructions](docs/install/linux/OTHERS.md).
|
||||
|
||||
|
|
|
|||
|
|
@ -1733,10 +1733,6 @@
|
|||
callnative BS_WaitFanfare
|
||||
.endm
|
||||
|
||||
.macro setbeakblast
|
||||
callnative BS_SetBeakBlast
|
||||
.endm
|
||||
|
||||
.macro cantarshotwork failInstr:req
|
||||
callnative BS_CanTarShotWork
|
||||
.4byte \failInstr
|
||||
|
|
|
|||
|
|
@ -139,7 +139,11 @@ BattleScript_EffectShedTail::
|
|||
waitmessage B_WAIT_TIME_LONG
|
||||
moveendto MOVEEND_ATTACKER_VISIBLE
|
||||
moveendfrom MOVEEND_TARGET_VISIBLE
|
||||
goto BattleScript_MoveSwitchOpenPartyScreen
|
||||
call BattleScript_MoveSwitchOpenPartyScreenReturnWithNoAnim
|
||||
switchinanim BS_ATTACKER, FALSE, TRUE
|
||||
waitstate
|
||||
switchineffects BS_ATTACKER
|
||||
end
|
||||
|
||||
BattleScript_EffectPsychicNoise::
|
||||
printstring STRINGID_PKMNPREVENTEDFROMHEALING
|
||||
|
|
@ -282,6 +286,14 @@ BattleScript_MoveSwitch:
|
|||
printstring STRINGID_PKMNWENTBACK
|
||||
waitmessage B_WAIT_TIME_SHORT
|
||||
BattleScript_MoveSwitchOpenPartyScreen::
|
||||
call BattleScript_MoveSwitchOpenPartyScreenReturnWithNoAnim
|
||||
switchinanim BS_ATTACKER, FALSE, FALSE
|
||||
waitstate
|
||||
switchineffects BS_ATTACKER
|
||||
BattleScript_MoveSwitchEnd:
|
||||
end
|
||||
|
||||
BattleScript_MoveSwitchOpenPartyScreenReturnWithNoAnim:
|
||||
openpartyscreen BS_ATTACKER, BattleScript_MoveSwitchEnd
|
||||
waitstate
|
||||
returntoball BS_ATTACKER, FALSE
|
||||
|
|
@ -294,11 +306,7 @@ BattleScript_MoveSwitchOpenPartyScreen::
|
|||
printstring STRINGID_EMPTYSTRING3
|
||||
waitmessage 1
|
||||
printstring STRINGID_SWITCHINMON
|
||||
switchinanim BS_ATTACKER, FALSE, TRUE
|
||||
waitstate
|
||||
switchineffects BS_ATTACKER
|
||||
BattleScript_MoveSwitchEnd:
|
||||
end
|
||||
return
|
||||
|
||||
BattleScript_EffectPledge::
|
||||
attackcanceler
|
||||
|
|
@ -612,7 +620,6 @@ BattleScript_EffectCourtChange::
|
|||
goto BattleScript_MoveEnd
|
||||
|
||||
BattleScript_BeakBlastSetUp::
|
||||
setbeakblast
|
||||
flushtextbox
|
||||
playanimation BS_ATTACKER, B_ANIM_BEAK_BLAST_SETUP, NULL
|
||||
printstring STRINGID_HEATUPBEAK
|
||||
|
|
|
|||
6
docs/install/linux/FEDORA.md
Normal file
6
docs/install/linux/FEDORA.md
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# Fedora instructions
|
||||
## Installing dependencies
|
||||
Open a terminal and run the following command from the command line:
|
||||
```console
|
||||
sudo dnf install gcc g++ arm-none-eabi-binutils-cs arm-none-eabi-gcc-cs arm-none-eabi-newlib git libpng-devel python3
|
||||
```
|
||||
|
|
@ -161,18 +161,17 @@ struct ProtectStruct
|
|||
u32 disableEjectPack:1;
|
||||
u32 pranksterElevated:1;
|
||||
u32 quickDraw:1;
|
||||
u32 beakBlastCharge:1;
|
||||
u32 quash:1;
|
||||
u32 shellTrap:1;
|
||||
u32 eatMirrorHerb:1;
|
||||
u32 activateOpportunist:2; // 2 - to copy stats. 1 - stats copied (do not repeat). 0 - no stats to copy
|
||||
u16 usedAllySwitch:1;
|
||||
u16 lashOutAffected:1;
|
||||
// End of 32-bit bitfield
|
||||
u16 helpingHand:3;
|
||||
u16 lashOutAffected:1;
|
||||
u16 assuranceDoubled:1;
|
||||
u16 myceliumMight:1;
|
||||
u16 padding:10;
|
||||
u16 padding:11;
|
||||
// End of 16-bit bitfield
|
||||
u16 physicalDmg;
|
||||
u16 specialDmg;
|
||||
|
|
@ -697,7 +696,7 @@ struct BattleStruct
|
|||
u8 fickleBeamBoosted:1;
|
||||
u8 poisonPuppeteerConfusion:1;
|
||||
u8 toxicChainPriority:1; // If Toxic Chain will trigger on target, all other non volatiles will be blocked
|
||||
u8 padding1:1;
|
||||
u8 moldBreakerActive:1;
|
||||
u16 startingStatusTimer;
|
||||
struct BattleTvMovePoints tvMovePoints;
|
||||
struct BattleTv tv;
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ enum MoveSuccessOrder
|
|||
CANCELER_TAUNTED,
|
||||
CANCELER_IMPRISONED,
|
||||
CANCELER_CONFUSED,
|
||||
CANCELER_PARALYSED,
|
||||
CANCELER_PARALYZED,
|
||||
CANCELER_INFATUATION,
|
||||
CANCELER_BIDE,
|
||||
CANCELER_Z_MOVES,
|
||||
|
|
|
|||
|
|
@ -2247,7 +2247,7 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
|
|||
|
||||
// Check if current mon can revenge kill in some capacity
|
||||
// If AI mon can one shot
|
||||
if (damageDealt > playerMonHP)
|
||||
if (damageDealt >= playerMonHP)
|
||||
{
|
||||
if (canSwitchinWin1v1)
|
||||
{
|
||||
|
|
@ -2259,7 +2259,7 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
|
|||
}
|
||||
|
||||
// If AI mon can two shot
|
||||
if (damageDealt > playerMonHP / 2)
|
||||
if (damageDealt >= (playerMonHP / 2 + playerMonHP % 2)) // Modulo to handle odd numbers in non-decimal division
|
||||
{
|
||||
if (canSwitchinWin1v1)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -198,46 +198,36 @@ static void CompleteOnBattlerSpritePosX_0(u32 battler)
|
|||
|
||||
static u16 GetPrevBall(u16 ballId)
|
||||
{
|
||||
u16 ballPrev;
|
||||
s32 i, j;
|
||||
CompactItemsInBagPocket(POCKET_POKE_BALLS);
|
||||
for (i = 0; i < gBagPockets[POCKET_POKE_BALLS].capacity; i++)
|
||||
s32 i;
|
||||
s32 index = ItemIdToBallId(ballId);
|
||||
u32 newBall = 0;
|
||||
for (i = 0; i < POKEBALL_COUNT; i++)
|
||||
{
|
||||
if (ballId == GetBagItemId(POCKET_POKE_BALLS, i))
|
||||
{
|
||||
if (i <= 0)
|
||||
{
|
||||
for (j = gBagPockets[POCKET_POKE_BALLS].capacity - 1; j >= 0; j--)
|
||||
{
|
||||
ballPrev = GetBagItemId(POCKET_POKE_BALLS, j);
|
||||
if (ballPrev != ITEM_NONE)
|
||||
return ballPrev;
|
||||
}
|
||||
}
|
||||
i--;
|
||||
break;
|
||||
}
|
||||
index--;
|
||||
if (index == -1)
|
||||
index = POKEBALL_COUNT - 1;
|
||||
newBall = gBallItemIds[index];
|
||||
if (CheckBagHasItem(newBall, 1))
|
||||
return newBall;
|
||||
}
|
||||
return GetBagItemId(POCKET_POKE_BALLS, i);
|
||||
return ballId;
|
||||
}
|
||||
|
||||
static u32 GetNextBall(u32 ballId)
|
||||
{
|
||||
u32 ballNext = ITEM_NONE;
|
||||
s32 i;
|
||||
CompactItemsInBagPocket(POCKET_POKE_BALLS);
|
||||
for (i = 1; i < gBagPockets[POCKET_POKE_BALLS].capacity; i++)
|
||||
s32 index = ItemIdToBallId(ballId);
|
||||
u32 newBall = 0;
|
||||
for (i = 0; i < POKEBALL_COUNT; i++)
|
||||
{
|
||||
if (ballId == GetBagItemId(POCKET_POKE_BALLS, i-1))
|
||||
{
|
||||
ballNext = GetBagItemId(POCKET_POKE_BALLS, i);
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
if (index == POKEBALL_COUNT)
|
||||
index = 0;
|
||||
newBall = gBallItemIds[index];
|
||||
if (CheckBagHasItem(newBall, 1))
|
||||
return newBall;
|
||||
}
|
||||
if (ballNext == ITEM_NONE)
|
||||
return GetBagItemId(POCKET_POKE_BALLS, 0); // Zeroth slot
|
||||
else
|
||||
return ballNext;
|
||||
return ballId;
|
||||
}
|
||||
|
||||
static void HandleInputChooseAction(u32 battler)
|
||||
|
|
|
|||
|
|
@ -3263,7 +3263,6 @@ void SwitchInClearSetData(u32 battler, struct Volatiles *volatilesCopy)
|
|||
}
|
||||
|
||||
gBattleStruct->choicedMove[battler] = MOVE_NONE;
|
||||
gCurrentMove = MOVE_NONE;
|
||||
gBattleStruct->eventState.arenaTurn = 0xFF;
|
||||
|
||||
// Restore struct member so replacement does not miss timing
|
||||
|
|
|
|||
|
|
@ -1106,6 +1106,15 @@ bool32 EmergencyExitCanBeTriggered(u32 battler)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
static inline bool32 IsBattlerUsingBeakBlast(u32 battler)
|
||||
{
|
||||
if (gChosenActionByBattler[battler] != B_ACTION_USE_MOVE)
|
||||
return FALSE;
|
||||
if (GetMoveEffect(gChosenMoveByBattler[battler]) != EFFECT_BEAK_BLAST)
|
||||
return FALSE;
|
||||
return !HasBattlerActedThisTurn(battler);
|
||||
}
|
||||
|
||||
static void Cmd_attackcanceler(void)
|
||||
{
|
||||
CMD_ARGS();
|
||||
|
|
@ -1315,7 +1324,8 @@ static void Cmd_attackcanceler(void)
|
|||
gBattleCommunication[MISS_TYPE] = B_MSG_PROTECTED;
|
||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
}
|
||||
else if (gProtectStructs[gBattlerTarget].beakBlastCharge && !CanBattlerAvoidContactEffects(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerAttacker), GetBattlerHoldEffect(gBattlerAttacker), gCurrentMove))
|
||||
else if (IsBattlerUsingBeakBlast(gBattlerTarget)
|
||||
&& !CanBattlerAvoidContactEffects(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerAttacker), GetBattlerHoldEffect(gBattlerAttacker), gCurrentMove))
|
||||
{
|
||||
gProtectStructs[gBattlerAttacker].touchedProtectLike = TRUE;
|
||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
|
|
@ -6116,7 +6126,7 @@ static void Cmd_moveend(void)
|
|||
}
|
||||
|
||||
// Not strictly a protect effect, but works the same way
|
||||
if (gProtectStructs[gBattlerTarget].beakBlastCharge
|
||||
if (IsBattlerUsingBeakBlast(gBattlerTarget)
|
||||
&& CanBeBurned(gBattlerAttacker, gBattlerAttacker, GetBattlerAbility(gBattlerAttacker))
|
||||
&& !(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT))
|
||||
{
|
||||
|
|
@ -6404,8 +6414,9 @@ static void Cmd_moveend(void)
|
|||
gBattleStruct->lastMoveTarget[gBattlerAttacker] = gBattlerTarget;
|
||||
}
|
||||
enum BattleMoveEffects originalEffect = GetMoveEffect(originallyUsedMove);
|
||||
if (!(gAbsentBattlerFlags & (1u << gBattlerAttacker))
|
||||
&& originalEffect != EFFECT_BATON_PASS && originalEffect != EFFECT_HEALING_WISH)
|
||||
if (IsBattlerAlive(gBattlerAttacker)
|
||||
&& originalEffect != EFFECT_BATON_PASS
|
||||
&& originalEffect != EFFECT_HEALING_WISH)
|
||||
{
|
||||
if (gHitMarker & HITMARKER_OBEYS)
|
||||
{
|
||||
|
|
@ -6979,25 +6990,17 @@ static void Cmd_moveend(void)
|
|||
gBattleScripting.moveendState++;
|
||||
break;
|
||||
case MOVEEND_THIRD_MOVE_BLOCK:
|
||||
if (gChosenMove == MOVE_UNAVAILABLE)
|
||||
{
|
||||
gBattleScripting.moveendState++;
|
||||
break;
|
||||
}
|
||||
|
||||
// Special case for Steel Roller since it has to check the chosen move
|
||||
if (GetMoveEffect(gChosenMove) == EFFECT_STEEL_ROLLER && IsBattlerTurnDamaged(gBattlerTarget))
|
||||
{
|
||||
BattleScriptCall(BattleScript_RemoveTerrain);
|
||||
effect = TRUE;
|
||||
gBattleScripting.moveendState++;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (moveEffect)
|
||||
{
|
||||
case EFFECT_STEEL_ROLLER:
|
||||
if (gFieldStatuses & STATUS_FIELD_TERRAIN_ANY && IsBattlerTurnDamaged(gBattlerTarget))
|
||||
{
|
||||
BattleScriptCall(BattleScript_RemoveTerrain);
|
||||
effect = TRUE;
|
||||
}
|
||||
case EFFECT_ICE_SPINNER:
|
||||
if (gFieldStatuses & STATUS_FIELD_TERRAIN_ANY
|
||||
&& gLastPrintedMoves[gBattlerAttacker] == gCurrentMove
|
||||
&& IsBattlerAlive(gBattlerAttacker)
|
||||
&& IsBattlerTurnDamaged(gBattlerTarget))
|
||||
{
|
||||
|
|
@ -7070,6 +7073,7 @@ static void Cmd_moveend(void)
|
|||
gProtectStructs[gBattlerAttacker].shellTrap = FALSE;
|
||||
gBattleStruct->battlerState[gBattlerAttacker].ateBoost = FALSE;
|
||||
gBattleScripting.moveEffect = MOVE_EFFECT_NONE;
|
||||
gBattleStruct->moldBreakerActive = FALSE;
|
||||
gBattleStruct->swapDamageCategory = FALSE;
|
||||
gBattleStruct->categoryOverride = FALSE;
|
||||
gBattleStruct->additionalEffectsCounter = 0;
|
||||
|
|
@ -7113,11 +7117,7 @@ static void Cmd_moveend(void)
|
|||
gBattleScripting.moveendState++;
|
||||
break;
|
||||
case MOVEEND_DANCER:
|
||||
if (gCurrentMove == MOVE_NONE)
|
||||
originallyUsedMove = gChosenMove; // Fallback to chosen move in case attacker is switched out in the middle of an attack resolution (eg red card)
|
||||
else
|
||||
originallyUsedMove = gCurrentMove;
|
||||
if (IsDanceMove(originallyUsedMove) && !gBattleStruct->snatchedMoveIsUsed)
|
||||
if (IsDanceMove(gCurrentMove) && !gBattleStruct->snatchedMoveIsUsed)
|
||||
{
|
||||
u32 battler, nextDancer = 0;
|
||||
bool32 hasDancerTriggered = FALSE;
|
||||
|
|
@ -7151,7 +7151,7 @@ static void Cmd_moveend(void)
|
|||
nextDancer = battler | 0x4;
|
||||
}
|
||||
}
|
||||
if (nextDancer && AbilityBattleEffects(ABILITYEFFECT_MOVE_END_OTHER, nextDancer & 0x3, 0, 0, originallyUsedMove))
|
||||
if (nextDancer && AbilityBattleEffects(ABILITYEFFECT_MOVE_END_OTHER, nextDancer & 0x3, 0, 0, gCurrentMove))
|
||||
effect = TRUE;
|
||||
}
|
||||
}
|
||||
|
|
@ -15975,13 +15975,6 @@ void BS_WaitFanfare(void)
|
|||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
}
|
||||
|
||||
void BS_SetBeakBlast(void)
|
||||
{
|
||||
NATIVE_ARGS();
|
||||
gProtectStructs[gBattlerAttacker].beakBlastCharge = TRUE;
|
||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
}
|
||||
|
||||
void BS_RemoveTerrain(void)
|
||||
{
|
||||
NATIVE_ARGS();
|
||||
|
|
|
|||
|
|
@ -1305,6 +1305,7 @@ static void CB2_EndTrainerBattle(void)
|
|||
{
|
||||
HandleBattleVariantEndParty();
|
||||
|
||||
gIsDebugBattle = FALSE;
|
||||
if (FollowerNPCIsBattlePartner())
|
||||
{
|
||||
RestorePartyAfterFollowerNPCBattle();
|
||||
|
|
|
|||
|
|
@ -1265,6 +1265,7 @@ static void TrySetBattleSeminarShow(void)
|
|||
|
||||
dmgByMove[gMoveSelectionCursor[gBattlerAttacker]] = gBattleStruct->moveDamage[gBattlerTarget]; // TODO: Not sure
|
||||
currMoveSaved = gCurrentMove;
|
||||
u16 storedMoveResultFlags = gBattleStruct->moveResultFlags[gBattlerTarget];
|
||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
gCurrentMove = gBattleMons[gBattlerAttacker].moves[i];
|
||||
|
|
@ -1281,8 +1282,7 @@ static void TrySetBattleSeminarShow(void)
|
|||
ctx.updateFlags = FALSE;
|
||||
ctx.isSelfInflicted = FALSE;
|
||||
ctx.fixedBasePower = powerOverride;
|
||||
gBattleStruct->moveDamage[gBattlerTarget] = CalculateMoveDamage(&ctx);
|
||||
dmgByMove[i] = gBattleStruct->moveDamage[gBattlerTarget];
|
||||
dmgByMove[i] = CalculateMoveDamage(&ctx);
|
||||
if (dmgByMove[i] == 0 && !(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT))
|
||||
dmgByMove[i] = 1;
|
||||
}
|
||||
|
|
@ -1313,8 +1313,8 @@ static void TrySetBattleSeminarShow(void)
|
|||
}
|
||||
}
|
||||
|
||||
gBattleStruct->moveDamage[gBattlerTarget] = dmgByMove[gMoveSelectionCursor[gBattlerAttacker]];
|
||||
gCurrentMove = currMoveSaved;
|
||||
gBattleStruct->moveResultFlags[gBattlerTarget] = storedMoveResultFlags;
|
||||
}
|
||||
|
||||
static bool8 ShouldCalculateDamage(u16 move, s32 *dmg, u16 *powerOverride)
|
||||
|
|
|
|||
|
|
@ -533,6 +533,9 @@ void HandleAction_UseMove(void)
|
|||
gCurrentMove = gChosenMove = GetMaxMove(gBattlerAttacker, gCurrentMove);
|
||||
}
|
||||
|
||||
if (IsMoldBreakerTypeAbility(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker)) || MoveIgnoresTargetAbility(gCurrentMove))
|
||||
gBattleStruct->moldBreakerActive = TRUE;
|
||||
|
||||
moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove);
|
||||
|
||||
if (!HandleMoveTargetRedirection())
|
||||
|
|
@ -958,12 +961,13 @@ void HandleAction_ActionFinished(void)
|
|||
gHitMarker &= ~(HITMARKER_OBEYS);
|
||||
|
||||
ClearDamageCalcResults();
|
||||
gCurrentMove = 0;
|
||||
gCurrentMove = MOVE_NONE;
|
||||
gBattleScripting.animTurn = 0;
|
||||
gBattleScripting.animTargetsHit = 0;
|
||||
gBattleStruct->dynamicMoveType = 0;
|
||||
gBattleStruct->bouncedMoveIsUsed = FALSE;
|
||||
gBattleStruct->snatchedMoveIsUsed = FALSE;
|
||||
gBattleStruct->moldBreakerActive = FALSE;
|
||||
gBattleScripting.moveendState = 0;
|
||||
gBattleCommunication[3] = 0;
|
||||
gBattleCommunication[4] = 0;
|
||||
|
|
@ -2297,7 +2301,7 @@ static enum MoveCanceler CancelerConfused(struct BattleContext *ctx)
|
|||
return MOVE_STEP_SUCCESS;
|
||||
}
|
||||
|
||||
static enum MoveCanceler CancelerParalysed(struct BattleContext *ctx)
|
||||
static enum MoveCanceler CancelerParalyzed(struct BattleContext *ctx)
|
||||
{
|
||||
if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_PARALYSIS
|
||||
&& !(B_MAGIC_GUARD == GEN_4 && IsAbilityAndRecord(ctx->battlerAtk, ctx->abilities[ctx->battlerAtk], ABILITY_MAGIC_GUARD))
|
||||
|
|
@ -2996,7 +3000,7 @@ static enum MoveCanceler (*const sMoveSuccessOrderCancelers[])(struct BattleCont
|
|||
[CANCELER_TAUNTED] = CancelerTaunted,
|
||||
[CANCELER_IMPRISONED] = CancelerImprisoned,
|
||||
[CANCELER_CONFUSED] = CancelerConfused,
|
||||
[CANCELER_PARALYSED] = CancelerParalysed,
|
||||
[CANCELER_PARALYZED] = CancelerParalyzed,
|
||||
[CANCELER_INFATUATION] = CancelerInfatuation,
|
||||
[CANCELER_BIDE] = CancelerBide,
|
||||
[CANCELER_Z_MOVES] = CancelerZMoves,
|
||||
|
|
@ -5680,17 +5684,11 @@ bool32 IsMoldBreakerTypeAbility(u32 battler, enum Ability ability)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
static inline bool32 CanBreakThroughAbility(u32 battlerAtk, u32 battlerDef, enum Ability ability, u32 hasAbilityShield, u32 ignoreMoldBreaker)
|
||||
static inline bool32 CanBreakThroughAbility(u32 battlerAtk, u32 battlerDef, u32 hasAbilityShield, u32 ignoreMoldBreaker)
|
||||
{
|
||||
if (hasAbilityShield || ignoreMoldBreaker)
|
||||
if (hasAbilityShield || ignoreMoldBreaker || battlerDef == battlerAtk)
|
||||
return FALSE;
|
||||
|
||||
return ((IsMoldBreakerTypeAbility(battlerAtk, ability) || MoveIgnoresTargetAbility(gCurrentMove))
|
||||
&& battlerDef != battlerAtk
|
||||
&& gAbilitiesInfo[gBattleMons[battlerDef].ability].breakable
|
||||
&& gBattlerByTurnOrder[gCurrentTurnActionNumber] == battlerAtk
|
||||
&& gActionsByTurnOrder[gCurrentTurnActionNumber] == B_ACTION_USE_MOVE
|
||||
&& gCurrentTurnActionNumber < gBattlersCount);
|
||||
return gBattleStruct->moldBreakerActive && gAbilitiesInfo[gBattleMons[battlerDef].ability].breakable;
|
||||
}
|
||||
|
||||
u32 GetBattlerAbilityNoAbilityShield(u32 battler)
|
||||
|
|
@ -5721,7 +5719,7 @@ u32 GetBattlerAbilityInternal(u32 battler, u32 ignoreMoldBreaker, u32 noAbilityS
|
|||
&& gBattleMons[battler].ability == ABILITY_COMATOSE)
|
||||
return ABILITY_NONE;
|
||||
|
||||
if (CanBreakThroughAbility(gBattlerAttacker, battler, gBattleMons[gBattlerAttacker].ability, hasAbilityShield, ignoreMoldBreaker))
|
||||
if (CanBreakThroughAbility(gBattlerAttacker, battler, hasAbilityShield, ignoreMoldBreaker))
|
||||
return ABILITY_NONE;
|
||||
|
||||
return gBattleMons[battler].ability;
|
||||
|
|
@ -5735,7 +5733,7 @@ u32 GetBattlerAbilityInternal(u32 battler, u32 ignoreMoldBreaker, u32 noAbilityS
|
|||
&& (gBattleMons[battler].ability != ABILITY_NEUTRALIZING_GAS || gBattleMons[battler].volatiles.gastroAcid))
|
||||
return ABILITY_NONE;
|
||||
|
||||
if (CanBreakThroughAbility(gBattlerAttacker, battler, gBattleMons[gBattlerAttacker].ability, hasAbilityShield, ignoreMoldBreaker))
|
||||
if (CanBreakThroughAbility(gBattlerAttacker, battler, hasAbilityShield, ignoreMoldBreaker))
|
||||
return ABILITY_NONE;
|
||||
|
||||
return gBattleMons[battler].ability;
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
|
|||
{
|
||||
.battleScript = BattleScript_EffectNonVolatileStatus,
|
||||
.battleTvScore = 0, // Handled within the battle TV functions
|
||||
.encourageEncore = TRUE,
|
||||
},
|
||||
|
||||
[EFFECT_ABSORB] =
|
||||
|
|
|
|||
|
|
@ -18680,8 +18680,8 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
|
|||
{
|
||||
.name = COMPOUND_STRING("Lash Out"),
|
||||
.description = COMPOUND_STRING(
|
||||
"If stats lowered during this\n"
|
||||
"turn, power is doubled."),
|
||||
"If user's stats were lowered\n"
|
||||
"this turn, power is doubled."),
|
||||
.effect = EFFECT_LASH_OUT,
|
||||
.power = 75,
|
||||
.type = TYPE_DARK,
|
||||
|
|
|
|||
|
|
@ -35,8 +35,8 @@ static void UpdateFeetInFlowingWaterFieldEffect(struct Sprite *);
|
|||
static void UpdateAshFieldEffect_Wait(struct Sprite *);
|
||||
static void UpdateAshFieldEffect_Show(struct Sprite *);
|
||||
static void UpdateAshFieldEffect_End(struct Sprite *);
|
||||
static void SynchroniseSurfAnim(struct ObjectEvent *, struct Sprite *);
|
||||
static void SynchroniseSurfPosition(struct ObjectEvent *, struct Sprite *);
|
||||
static void SynchronizeSurfAnim(struct ObjectEvent *, struct Sprite *);
|
||||
static void SynchronizeSurfPosition(struct ObjectEvent *, struct Sprite *);
|
||||
static void UpdateBobbingEffect(struct ObjectEvent *, struct Sprite *, struct Sprite *);
|
||||
static void SpriteCB_UnderwaterSurfBlob(struct Sprite *);
|
||||
static u32 ShowDisguiseFieldEffect(u8, u8, u8);
|
||||
|
|
@ -1248,13 +1248,13 @@ void UpdateSurfBlobFieldEffect(struct Sprite *sprite)
|
|||
{
|
||||
struct ObjectEvent *playerObj = &gObjectEvents[sprite->sPlayerObjId];
|
||||
struct Sprite *playerSprite = &gSprites[playerObj->spriteId];
|
||||
SynchroniseSurfAnim(playerObj, sprite);
|
||||
SynchroniseSurfPosition(playerObj, sprite);
|
||||
SynchronizeSurfAnim(playerObj, sprite);
|
||||
SynchronizeSurfPosition(playerObj, sprite);
|
||||
UpdateBobbingEffect(playerObj, playerSprite, sprite);
|
||||
sprite->oam.priority = playerSprite->oam.priority;
|
||||
}
|
||||
|
||||
static void SynchroniseSurfAnim(struct ObjectEvent *playerObj, struct Sprite *sprite)
|
||||
static void SynchronizeSurfAnim(struct ObjectEvent *playerObj, struct Sprite *sprite)
|
||||
{
|
||||
// Indexes into sAnimTable_SurfBlob
|
||||
u8 surfBlobDirectionAnims[] = {
|
||||
|
|
@ -1273,7 +1273,7 @@ static void SynchroniseSurfAnim(struct ObjectEvent *playerObj, struct Sprite *sp
|
|||
StartSpriteAnimIfDifferent(sprite, surfBlobDirectionAnims[playerObj->movementDirection]);
|
||||
}
|
||||
|
||||
void SynchroniseSurfPosition(struct ObjectEvent *playerObj, struct Sprite *sprite)
|
||||
void SynchronizeSurfPosition(struct ObjectEvent *playerObj, struct Sprite *sprite)
|
||||
{
|
||||
u8 i;
|
||||
s16 x = playerObj->currentCoords.x;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,10 @@
|
|||
#include "sound.h"
|
||||
#include "constants/songs.h"
|
||||
|
||||
// GF cast Task data to ListMenu in many places, which effectively puts
|
||||
// an upper bound on sizeof(struct ListMenu).
|
||||
STATIC_ASSERT(sizeof(struct ListMenu) <= sizeof(((struct Task *)NULL)->data), ListMenuTooLargeForTaskData);
|
||||
|
||||
// Cursors after this point are created using a sprite with their own task.
|
||||
// This allows them to have idle animations. Cursors prior to this are simply printed text.
|
||||
#define CURSOR_OBJECT_START CURSOR_RED_OUTLINE
|
||||
|
|
|
|||
|
|
@ -860,8 +860,8 @@ void LoadMapFromCameraTransition(u8 mapGroup, u8 mapNum)
|
|||
TryUpdateRandomTrainerRematches(mapGroup, mapNum);
|
||||
#endif //FREE_MATCH_CALL
|
||||
|
||||
if (I_VS_SEEKER_CHARGING != 0)
|
||||
MapResetTrainerRematches(mapGroup, mapNum);
|
||||
if (I_VS_SEEKER_CHARGING != 0)
|
||||
MapResetTrainerRematches(mapGroup, mapNum);
|
||||
|
||||
DoTimeBasedEvents();
|
||||
SetSavedWeatherFromCurrMapHeader();
|
||||
|
|
@ -926,8 +926,8 @@ static void LoadMapFromWarp(bool32 a1)
|
|||
TryUpdateRandomTrainerRematches(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum);
|
||||
#endif //FREE_MATCH_CALL
|
||||
|
||||
if (I_VS_SEEKER_CHARGING != 0)
|
||||
MapResetTrainerRematches(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum);
|
||||
if (I_VS_SEEKER_CHARGING != 0)
|
||||
MapResetTrainerRematches(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum);
|
||||
|
||||
if (a1 != TRUE)
|
||||
DoTimeBasedEvents();
|
||||
|
|
|
|||
|
|
@ -353,3 +353,45 @@ SINGLE_BATTLE_TEST("Neutralizing Gas only displays exiting message for the last
|
|||
NOT MESSAGE("The effects of the neutralizing gas wore off!");
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Neutralizing Gas is active for the duration of a Spread Move even if Neutralizing Gas is no longer on the field")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(GetMoveTarget(MOVE_ORIGIN_PULSE) == MOVE_TARGET_BOTH);
|
||||
PLAYER(SPECIES_WEEZING) { HP(1); Ability(ABILITY_NEUTRALIZING_GAS); }
|
||||
PLAYER(SPECIES_GOLEM) { Ability(ABILITY_STURDY); }
|
||||
OPPONENT(SPECIES_BASCULEGION) { Ability(ABILITY_MOLD_BREAKER); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponentLeft, MOVE_ORIGIN_PULSE); }
|
||||
} SCENE {
|
||||
ABILITY_POPUP(playerLeft, ABILITY_NEUTRALIZING_GAS);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ORIGIN_PULSE, opponentLeft);
|
||||
HP_BAR(playerLeft);
|
||||
HP_BAR(playerRight);
|
||||
MESSAGE("Weezing fainted!");
|
||||
MESSAGE("Golem fainted!");
|
||||
NOT ABILITY_POPUP(playerRight, ABILITY_STURDY);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Neutralizing Gas is active until the last Dragon Darts hit even if Neutralizing Gas is no longer on the field")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(GetMoveEffect(MOVE_DRAGON_DARTS) == EFFECT_DRAGON_DARTS);
|
||||
PLAYER(SPECIES_WEEZING) { HP(1); Ability(ABILITY_NEUTRALIZING_GAS); }
|
||||
PLAYER(SPECIES_GOLEM) { HP(2); MaxHP(2); Ability(ABILITY_STURDY); }
|
||||
OPPONENT(SPECIES_BASCULEGION) { Ability(ABILITY_MOLD_BREAKER); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponentLeft, MOVE_DRAGON_DARTS, target: playerLeft); }
|
||||
} SCENE {
|
||||
ABILITY_POPUP(playerLeft, ABILITY_NEUTRALIZING_GAS);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_DARTS, opponentLeft);
|
||||
HP_BAR(playerLeft);
|
||||
MESSAGE("Weezing fainted!");
|
||||
HP_BAR(playerRight);
|
||||
NOT MESSAGE("Golem fainted!");
|
||||
ABILITY_POPUP(playerRight, ABILITY_STURDY);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -487,7 +487,6 @@ AI_DOUBLE_BATTLE_TEST("AI treats an ally's redirection ability appropriately (ge
|
|||
|
||||
AI_DOUBLE_BATTLE_TEST("AI recognizes Volt Absorb received from Trace")
|
||||
{
|
||||
KNOWN_FAILING; // MGriffin's PR that switched two turn charging moves in AI tests broke this test, waiting on a fix
|
||||
GIVEN {
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
|
||||
PLAYER(SPECIES_MAGNETON);
|
||||
|
|
@ -495,7 +494,7 @@ AI_DOUBLE_BATTLE_TEST("AI recognizes Volt Absorb received from Trace")
|
|||
OPPONENT(SPECIES_JOLTEON) { Ability(ABILITY_VOLT_ABSORB); Moves(MOVE_THUNDER_WAVE, MOVE_THUNDERSHOCK, MOVE_WATER_GUN); }
|
||||
OPPONENT(SPECIES_JOLTEON) { Ability(ABILITY_VOLT_ABSORB); Moves(MOVE_THUNDER_WAVE, MOVE_THUNDERSHOCK, MOVE_WATER_GUN); }
|
||||
} WHEN {
|
||||
TURN { NOT_EXPECT_MOVE(opponentLeft, MOVE_THUNDERSHOCK); NOT_EXPECT_MOVE(opponentLeft, MOVE_THUNDER_WAVE); NOT_EXPECT_MOVE(opponentRight, MOVE_THUNDER_WAVE); }
|
||||
TURN { NOT_EXPECT_MOVES(opponentLeft, MOVE_THUNDERSHOCK, MOVE_THUNDER_WAVE); NOT_EXPECT_MOVE(opponentRight, MOVE_THUNDER_WAVE); }
|
||||
} THEN {
|
||||
EXPECT(gAiLogicData->abilities[B_POSITION_PLAYER_RIGHT] == ABILITY_VOLT_ABSORB);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1704,3 +1704,23 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: AI will consider choice-locked
|
|||
TURN { MOVE(player, MOVE_MIGHTY_CLEAVE); EXPECT_MOVE(opponent, MOVE_TACKLE); item == ITEM_NONE ? EXPECT_SEND_OUT(opponent, 1) : EXPECT_SEND_OUT(opponent, 2); }
|
||||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: AI considers both meeting and exceeding KO thresholds correctly")
|
||||
{
|
||||
u32 hp;
|
||||
PARAMETRIZE { hp = 40; }
|
||||
PARAMETRIZE { hp = 80; }
|
||||
PARAMETRIZE { hp = 79; }
|
||||
PARAMETRIZE { hp = 81; }
|
||||
GIVEN {
|
||||
ASSUME(GetMoveEffect(MOVE_DRAGON_RAGE) == EFFECT_FIXED_HP_DAMAGE);
|
||||
ASSUME(GetMoveFixedHPDamage(MOVE_DRAGON_RAGE) == 40);
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_OMNISCIENT);
|
||||
PLAYER(SPECIES_ZIGZAGOON) { Speed(5); HP(hp); Moves(MOVE_PROTECT, MOVE_TACKLE); }
|
||||
OPPONENT(SPECIES_ZIGZAGOON) { Speed(6); Moves(MOVE_EXPLOSION); }
|
||||
OPPONENT(SPECIES_ZIGZAGOON) { Speed(6); Moves(MOVE_DRAGON_RAGE); }
|
||||
OPPONENT(SPECIES_BELDUM) { Speed(4); Moves(MOVE_TACKLE); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_PROTECT); EXPECT_MOVE(opponent, MOVE_EXPLOSION); hp > 80 ? EXPECT_SEND_OUT(opponent, 2) : EXPECT_SEND_OUT(opponent, 1); }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ SINGLE_BATTLE_TEST("Beak Blast burns only when contact moves are used")
|
|||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_BEAK_BLAST); MOVE(opponent, move); }
|
||||
TURN { MOVE(opponent, move); MOVE(player, MOVE_BEAK_BLAST); }
|
||||
TURN {}
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_BEAK_BLAST_SETUP, player);
|
||||
|
|
@ -115,11 +115,11 @@ SINGLE_BATTLE_TEST("Beak Blast burns only when contact moves are used")
|
|||
SINGLE_BATTLE_TEST("Beak Blast doesn't burn fire types")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gSpeciesInfo[SPECIES_ARCANINE].types[0] == TYPE_FIRE || gSpeciesInfo[SPECIES_ARCANINE].types[1]);
|
||||
ASSUME(gSpeciesInfo[SPECIES_ARCANINE].types[0] == TYPE_FIRE || gSpeciesInfo[SPECIES_ARCANINE].types[1] == TYPE_FIRE);
|
||||
PLAYER(SPECIES_ARCANINE);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_BEAK_BLAST); MOVE(player, MOVE_SCRATCH); }
|
||||
TURN { MOVE(player, MOVE_SCRATCH); MOVE(opponent, MOVE_BEAK_BLAST); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player);
|
||||
NOT STATUS_ICON(player, burn: TRUE);
|
||||
|
|
@ -127,6 +127,20 @@ SINGLE_BATTLE_TEST("Beak Blast doesn't burn fire types")
|
|||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Beak Blast doesn't burn after being used")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(GetMovePriority(MOVE_COUNTER) < GetMovePriority(MOVE_BEAK_BLAST));
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_BEAK_BLAST); MOVE(player, MOVE_COUNTER); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_BEAK_BLAST, opponent);
|
||||
NOT STATUS_ICON(player, burn: TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
TO_DO_BATTLE_TEST("Beak Blast's charging message is shown regardless if it would've missed");
|
||||
TO_DO_BATTLE_TEST("Beak Blast fails if it's forced by Encore after choosing a different move");
|
||||
TO_DO_BATTLE_TEST("Bulletproof is immune to Beak Blast but not to the burn it causes");
|
||||
|
|
|
|||
|
|
@ -80,8 +80,35 @@ SINGLE_BATTLE_TEST("Fling fails for Pokémon with Klutz ability")
|
|||
}
|
||||
}
|
||||
|
||||
TO_DO_BATTLE_TEST("Fling fails if the item changes the Pokémon's form")
|
||||
TO_DO_BATTLE_TEST("Fling works if the item changes a Pokémon's form but not the one holding it") //Eg. non-matching Mega Stones
|
||||
SINGLE_BATTLE_TEST("Fling fails if the item changes the Pokémon's form")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_GIRATINA_ORIGIN) { Item(ITEM_GRISEOUS_CORE); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_FLING); }
|
||||
} SCENE {
|
||||
MESSAGE("But it failed!");
|
||||
} THEN {
|
||||
EXPECT(player->item == ITEM_GRISEOUS_CORE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Fling works if the item changes a Pokémon's form but not the one holding it")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_VENUSAUR) { Item(ITEM_BLASTOISINITE); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_FLING); }
|
||||
} SCENE {
|
||||
NOT MESSAGE("But it failed!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_FLING, player);
|
||||
HP_BAR(opponent);
|
||||
} THEN {
|
||||
EXPECT(player->item == ITEM_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Fling's thrown item can be regained with Recycle")
|
||||
{
|
||||
|
|
@ -478,25 +505,46 @@ SINGLE_BATTLE_TEST("Fling deals damage based on items fling power")
|
|||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Flinging a Mental Herb does not trigger the item if the target doesn't have anything that's cured by Mental Herb")
|
||||
SINGLE_BATTLE_TEST("Fling deals damage based on a TM's move power")
|
||||
{
|
||||
s16 damage[2];
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_MENTAL_HERB); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
ASSUME(GetMovePower(MOVE_EARTHQUAKE) == GetMovePower(MOVE_EGG_BOMB));
|
||||
ASSUME(!IsSpeciesOfType(SPECIES_WOBBUFFET, TYPE_DARK));
|
||||
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_TM_EARTHQUAKE); }
|
||||
OPPONENT(SPECIES_HIPPOWDON);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_FLING); }
|
||||
TURN { MOVE(player, MOVE_EGG_BOMB); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_FLING, player);
|
||||
NONE_OF {
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
|
||||
MESSAGE("The opposing Wobbuffet got over its infatuation!");
|
||||
MESSAGE("The opposing Wobbuffet's Taunt wore off!");
|
||||
MESSAGE("The opposing Wobbuffet ended its encore!");
|
||||
MESSAGE("The opposing Wobbuffet is no longer tormented!");
|
||||
MESSAGE("The opposing Wobbuffet's move is no longer disabled!");
|
||||
MESSAGE("The opposing Wobbuffet is cured of its heal block!");
|
||||
}
|
||||
HP_BAR(opponent, captureDamage: &damage[0]);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_EGG_BOMB, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[1]);
|
||||
} THEN {
|
||||
EXPECT_EQ(damage[0], damage[1]);
|
||||
}
|
||||
}
|
||||
|
||||
TO_DO_BATTLE_TEST("Fling deals damage based on a TM's move power")
|
||||
SINGLE_BATTLE_TEST("Fling deals damage based on a TM's move power")
|
||||
{
|
||||
s16 damage[2];
|
||||
|
||||
GIVEN {
|
||||
ASSUME(GetMovePower(MOVE_EARTHQUAKE) == GetMovePower(MOVE_EGG_BOMB));
|
||||
ASSUME(!IsSpeciesOfType(SPECIES_WOBBUFFET, TYPE_DARK));
|
||||
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_TM_EARTHQUAKE); }
|
||||
OPPONENT(SPECIES_HIPPOWDON);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_FLING); }
|
||||
TURN { MOVE(player, MOVE_EGG_BOMB); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_FLING, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[0]);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_EGG_BOMB, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[1]);
|
||||
} THEN {
|
||||
EXPECT_EQ(damage[0], damage[1]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ SINGLE_BATTLE_TEST("Roar switches the target with a random non-fainted replaceme
|
|||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROAR, player);
|
||||
MESSAGE("The opposing Bulbasaur was dragged out!");
|
||||
} THEN {
|
||||
EXPECT_EQ(gLastUsedMove, MOVE_ROAR);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user