Merge pull request #35 from rh-hideout/master

update master
This commit is contained in:
RoamerX 2025-04-22 12:36:11 +08:00 committed by GitHub
commit 03d4b3db51
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
38 changed files with 435 additions and 127 deletions

View File

@ -1331,7 +1331,7 @@
.byte \stat
.endm
.macro jumpifoppositegenders jumpInstr:req
.macro jumpifcaptivateaffected jumpInstr:req
.byte 0xfc
.4byte \jumpInstr
.endm
@ -1367,6 +1367,10 @@
callnative BS_RestoreAttacker
.endm
.macro jumpifintimidateabilityprevented
callnative BS_JumpIfIntimidateAbilityPrevented
.endm
.macro metalburstdamagecalculator failInstr:req
callnative BS_CalcMetalBurstDmg
.4byte \failInstr
@ -1383,6 +1387,10 @@
.byte \battler
.endm
.macro tryintimidatejectpack
callnative BS_TryIntimidatEjectpack
.endm
.macro allyswitchswapbattlers
callnative BS_AllySwitchSwapBattler
.endm

View File

@ -2713,6 +2713,7 @@ BattleScript_EffectGravity::
attackstring
ppreduce
setgravity BattleScript_ButItFailed
savetarget
attackanimation
waitanimation
BattleScript_EffectGravitySuccess::
@ -2730,6 +2731,7 @@ BattleScript_GravityLoopDrop:
BattleScript_GravityLoopEnd:
moveendcase MOVEEND_TARGET_VISIBLE
jumpifnexttargetvalid BattleScript_GravityLoop
restoretarget
goto BattleScript_MoveEnd
BattleScript_EffectRoost::
@ -2743,11 +2745,13 @@ BattleScript_EffectRoost::
BattleScript_EffectCaptivate::
setstatchanger STAT_SPATK, 2, TRUE
attackcanceler
jumpifsubstituteblocks BattleScript_FailedFromAtkString
jumpifoppositegenders BattleScript_CaptivateCheckAcc
goto BattleScript_FailedFromAtkString
attackstring
ppreduce
jumpifsubstituteblocks BattleScript_ButItFailed
jumpifcaptivateaffected BattleScript_CaptivateCheckAcc
goto BattleScript_ButItFailed
BattleScript_CaptivateCheckAcc:
accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE
accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE
goto BattleScript_StatDownFromAttackString
BattleScript_EffectHealBlock::
@ -4211,6 +4215,7 @@ BattleScript_EffectPerishSong::
attackstring
ppreduce
trysetperishsong BattleScript_ButItFailed
savetarget
attackanimation
waitanimation
printstring STRINGID_FAINTINTHREE
@ -4222,6 +4227,7 @@ BattleScript_PerishSongLoop::
BattleScript_PerishSongLoopIncrement::
addbyte gBattlerTarget, 1
jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_PerishSongLoop
restoretarget
goto BattleScript_MoveEnd
BattleScript_PerishSongBlocked::
@ -7102,7 +7108,6 @@ BattleScript_MoveUsedIsAsleep::
goto BattleScript_MoveEnd
BattleScript_MoveUsedWokeUp::
bicword gHitMarker, HITMARKER_WAKE_UP_CLEAR
printfromtable gWokeUpStringIds
waitmessage B_WAIT_TIME_LONG
updatestatusicon BS_ATTACKER
@ -7730,13 +7735,7 @@ BattleScript_IntimidateLoop:
jumpiftargetally BattleScript_IntimidateLoopIncrement
jumpifabsent BS_TARGET, BattleScript_IntimidateLoopIncrement
jumpifstatus2 BS_TARGET, STATUS2_SUBSTITUTE, BattleScript_IntimidateLoopIncrement
.if B_UPDATED_INTIMIDATE >= GEN_8 @These abilties specifically prevent just intimidate, without blocking stat decreases
jumpifability BS_TARGET, ABILITY_INNER_FOCUS, BattleScript_IntimidatePrevented
jumpifability BS_TARGET, ABILITY_SCRAPPY, BattleScript_IntimidatePrevented
jumpifability BS_TARGET, ABILITY_OWN_TEMPO, BattleScript_IntimidatePrevented
jumpifability BS_TARGET, ABILITY_OBLIVIOUS, BattleScript_IntimidatePrevented
.endif
jumpifability BS_TARGET, ABILITY_GUARD_DOG, BattleScript_IntimidateInReverse
jumpifintimidateabilityprevented
BattleScript_IntimidateEffect:
copybyte sBATTLER, gBattlerAttacker
setstatchanger STAT_ATK, 1, TRUE
@ -7761,9 +7760,10 @@ BattleScript_IntimidateLoopIncrement:
destroyabilitypopup
restoretarget
pause B_WAIT_TIME_MED
tryintimidatejectpack
end3
BattleScript_IntimidatePrevented:
BattleScript_IntimidatePrevented::
copybyte sBATTLER, gBattlerTarget
call BattleScript_AbilityPopUp
printstring STRINGID_PKMNPREVENTSSTATLOSSWITH
@ -7783,7 +7783,7 @@ BattleScript_IntimidateContrary_WontIncrease:
printstring STRINGID_TARGETSTATWONTGOHIGHER
goto BattleScript_IntimidateEffect_WaitString
BattleScript_IntimidateInReverse:
BattleScript_IntimidateInReverse::
copybyte sBATTLER, gBattlerTarget
call BattleScript_AbilityPopUpTarget
pause B_WAIT_TIME_SHORT
@ -9528,7 +9528,7 @@ BattleScript_EjectButtonActivates::
printstring STRINGID_EJECTBUTTONACTIVATE
waitmessage B_WAIT_TIME_LONG
removeitem BS_SCRIPTING
undodynamax BS_SCRIPTING
undodynamax BS_SCRIPTING
makeinvisible BS_SCRIPTING
openpartyscreen BS_SCRIPTING, BattleScript_EjectButtonEnd
copybyte sSAVED_BATTLER, sBATTLER

View File

@ -183,7 +183,8 @@ struct ProtectStruct
u16 eatMirrorHerb:1;
u16 activateOpportunist:2; // 2 - to copy stats. 1 - stats copied (do not repeat). 0 - no stats to copy
u16 usedAllySwitch:1;
u16 padding:2;
u16 lashOutAffected:1;
u16 padding:1;
// End of 16-bit bitfield
u32 physicalDmg;
u32 specialDmg;

View File

@ -170,6 +170,8 @@ extern const u8 BattleScript_RainDishActivates[];
extern const u8 BattleScript_SandstreamActivates[];
extern const u8 BattleScript_ShedSkinActivates[];
extern const u8 BattleScript_IntimidateActivates[];
extern const u8 BattleScript_IntimidatePrevented[];
extern const u8 BattleScript_IntimidateInReverse[];
extern const u8 BattleScript_DroughtActivates[];
extern const u8 BattleScript_TookAttack[];
extern const u8 BattleScript_SturdyPreventsOHKO[];

View File

@ -196,7 +196,7 @@
#define STATUS4_SYRUP_BOMB (1 << 5)
#define STATUS4_GLAIVE_RUSH (1 << 6)
#define HITMARKER_WAKE_UP_CLEAR (1 << 4) // Cleared when waking up. Never set or checked.
#define HITMARKER_UNUSED_1 (1 << 4)
#define HITMARKER_IGNORE_BIDE (1 << 5)
#define HITMARKER_DESTINYBOND (1 << 6)
#define HITMARKER_NO_ANIMATIONS (1 << 7) // set from battleSceneOff. Never changed during battle
@ -204,16 +204,16 @@
#define HITMARKER_NO_ATTACKSTRING (1 << 9)
#define HITMARKER_ATTACKSTRING_PRINTED (1 << 10)
#define HITMARKER_NO_PPDEDUCT (1 << 11)
#define HITMARKER_SWAP_ATTACKER_TARGET (1 << 12)
#define HITMARKER_UNUSED_2 (1 << 12)
#define HITMARKER_STATUS_ABILITY_EFFECT (1 << 13)
#define HITMARKER_SYNCHRONISE_EFFECT (1 << 14)
#define HITMARKER_RUN (1 << 15)
#define HITMARKER_IGNORE_DISGUISE (1 << 16)
#define HITMARKER_DISABLE_ANIMATION (1 << 17) // disable animations during battle scripts, e.g. for Bug Bite
// 3 free spots because of change in handling of UNDERGROUND/UNDERWATER/ON AIR
#define HITMARKER_UNUSED_3 (1 << 18)
#define HITMARKER_UNABLE_TO_USE_MOVE (1 << 19)
#define HITMARKER_PASSIVE_DAMAGE (1 << 20)
#define HITMARKER_DISOBEDIENT_MOVE (1 << 21)
#define HITMARKER_UNUSED_4 (1 << 21)
#define HITMARKER_PLAYER_FAINTED (1 << 22)
#define HITMARKER_ALLOW_NO_PP (1 << 23)
#define HITMARKER_GRUDGE (1 << 24)

View File

@ -137,7 +137,7 @@ struct MoveInfo
const u8 *battleAnimScript;
};
extern const struct MoveInfo gMovesInfo[];
extern const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL];
extern const u8 gNotDoneYetDescription[];
extern const struct BattleMoveEffect gBattleMoveEffects[];
@ -154,19 +154,19 @@ static inline const u8 *GetMoveName(u32 moveId)
return gMovesInfo[SanitizeMoveId(moveId)].name;
}
static inline const u8 *GetMoveDescription(u32 moveId)
{
moveId = SanitizeMoveId(moveId);
if (gMovesInfo[moveId].effect == EFFECT_PLACEHOLDER)
return gNotDoneYetDescription;
return gMovesInfo[moveId].description;
}
static inline u32 GetMoveEffect(u32 moveId)
{
return gMovesInfo[SanitizeMoveId(moveId)].effect;
}
static inline const u8 *GetMoveDescription(u32 moveId)
{
moveId = SanitizeMoveId(moveId);
if (GetMoveEffect(moveId) == EFFECT_PLACEHOLDER)
return gNotDoneYetDescription;
return gMovesInfo[moveId].description;
}
static inline u32 GetMoveType(u32 moveId)
{
return gMovesInfo[SanitizeMoveId(moveId)].type;
@ -437,6 +437,11 @@ static inline bool32 IsMoveSketchBanned(u32 moveId)
return gMovesInfo[SanitizeMoveId(moveId)].sketchBanned;
}
static inline bool32 IsValidApprenticeMove(u32 moveId)
{
return gMovesInfo[SanitizeMoveId(moveId)].validApprenticeMove;
}
static inline u32 GetMoveTwoTurnAttackStringId(u32 moveId)
{
return gMovesInfo[SanitizeMoveId(moveId)].argument.twoTurnAttack.stringId;
@ -529,12 +534,12 @@ static inline const u8 *GetMoveAnimationScript(u32 moveId)
static inline const u8 *GetMoveBattleScript(u32 moveId)
{
moveId = SanitizeMoveId(moveId);
if (gBattleMoveEffects[gMovesInfo[moveId].effect].battleScript == NULL)
if (gBattleMoveEffects[GetMoveEffect(moveId)].battleScript == NULL)
{
DebugPrintfLevel(MGBA_LOG_WARN, "No effect for moveId=%u", moveId);
return gBattleMoveEffects[EFFECT_PLACEHOLDER].battleScript;
}
return gBattleMoveEffects[gMovesInfo[moveId].effect].battleScript;
return gBattleMoveEffects[GetMoveEffect(moveId)].battleScript;
}
#endif // GUARD_MOVES_H

View File

@ -411,7 +411,7 @@ static u16 GetRandomAlternateMove(u8 monId)
if (TrySetMove(monId, moveId))
{
if (gMovesInfo[moveId].validApprenticeMove)
if (IsValidApprenticeMove(moveId))
break;
i++;
}

View File

@ -1465,7 +1465,7 @@ bool32 IsConfusionMoveEffect(u32 moveEffect)
bool32 IsHazardMove(u32 move)
{
// Hazard setting moves like Stealth Rock, Spikes, etc.
u32 i, moveEffect = gMovesInfo[move].effect;
u32 i, moveEffect = GetMoveEffect(move);
switch (moveEffect)
{
case EFFECT_SPIKES:
@ -1491,7 +1491,7 @@ bool32 IsHazardMove(u32 move)
bool32 IsHazardClearingMove(u32 move)
{
// Hazard clearing effects like Rapid Spin, Tidy Up, etc.
u32 i, moveEffect = gMovesInfo[move].effect;
u32 i, moveEffect = GetMoveEffect(move);
switch (moveEffect)
{
case EFFECT_RAPID_SPIN:

View File

@ -3751,6 +3751,7 @@ static void TryDoEventsBeforeFirstTurn(void)
{
for (i = 0; i < gBattlersCount; i++)
{
gBattleStruct->monToSwitchIntoId[i] = PARTY_SIZE; // Included here because switches can happen before during set ups (eg. eject pack)
struct Pokemon *party = GetBattlerParty(i);
struct Pokemon *mon = &party[gBattlerPartyIndexes[i]];
if (!IsBattlerAlive(i) || gBattleMons[i].species == SPECIES_NONE || GetMonData(mon, MON_DATA_IS_EGG))
@ -4846,7 +4847,6 @@ s8 GetBattleMovePriority(u32 battler, u16 move)
return priority;
}
// Function for AI with variables provided as arguments to speed the computation time
s32 GetWhichBattlerFasterArgs(u32 battler1, u32 battler2, bool32 ignoreChosenMoves, u32 ability1, u32 ability2,
u32 holdEffectBattler1, u32 holdEffectBattler2, u32 speedBattler1, u32 speedBattler2, s32 priority1, s32 priority2)
{
@ -5106,6 +5106,9 @@ static void TurnValuesCleanUp(bool8 var0)
gProtectStructs[i].spikyShielded = FALSE;
gProtectStructs[i].kingsShielded = FALSE;
gProtectStructs[i].banefulBunkered = FALSE;
gProtectStructs[i].obstructed = FALSE;
gProtectStructs[i].silkTrapped = FALSE;
gProtectStructs[i].burningBulwarked = FALSE;
gProtectStructs[i].quash = FALSE;
gProtectStructs[i].usedCustapBerry = FALSE;
gProtectStructs[i].quickDraw = FALSE;
@ -5135,6 +5138,7 @@ static void TurnValuesCleanUp(bool8 var0)
gSpecialStatuses[i].parentalBondState = PARENTAL_BOND_OFF;
gBattleStruct->battlerState[i].usedEjectItem = FALSE;
gProtectStructs[i].lashOutAffected = FALSE;
}
gSideStatuses[B_SIDE_PLAYER] &= ~(SIDE_STATUS_QUICK_GUARD | SIDE_STATUS_WIDE_GUARD | SIDE_STATUS_CRAFTY_SHIELD | SIDE_STATUS_MAT_BLOCK);

View File

@ -599,7 +599,7 @@ static void Cmd_trainerslideout(void);
static void Cmd_settelekinesis(void);
static void Cmd_swapstatstages(void);
static void Cmd_averagestats(void);
static void Cmd_jumpifoppositegenders(void);
static void Cmd_jumpifcaptivateaffected(void);
static void Cmd_unused(void);
static void Cmd_tryworryseed(void);
static void Cmd_callnative(void);
@ -836,8 +836,8 @@ void (* const gBattleScriptingCommandsTable[])(void) =
Cmd_jumpifhasnohp, //0xE3
Cmd_jumpifnotcurrentmoveargtype, //0xE4
Cmd_pickup, //0xE5
Cmd_unused_0xE6, //0xE6
Cmd_unused_0xE7, //0xE7
Cmd_unused_0xE6, //0xE6
Cmd_unused_0xE7, //0xE7
Cmd_settypebasedhalvers, //0xE8
Cmd_jumpifsubstituteblocks, //0xE9
Cmd_tryrecycleitem, //0xEA
@ -858,7 +858,7 @@ void (* const gBattleScriptingCommandsTable[])(void) =
Cmd_settelekinesis, //0xF9
Cmd_swapstatstages, //0xFA
Cmd_averagestats, //0xFB
Cmd_jumpifoppositegenders, //0xFC
Cmd_jumpifcaptivateaffected, //0xFC
Cmd_unused, //0xFD
Cmd_tryworryseed, //0xFE
Cmd_callnative, //0xFF
@ -1198,7 +1198,7 @@ static void Cmd_attackcanceler(void)
if (AbilityBattleEffects(ABILITYEFFECT_MOVES_BLOCK, gBattlerTarget, 0, 0, 0))
return;
if (gMovesInfo[gCurrentMove].effect == EFFECT_PARALYZE && AbilityBattleEffects(ABILITYEFFECT_ABSORBING, gBattlerTarget, 0, 0, gCurrentMove))
if (GetMoveEffect(gCurrentMove) == EFFECT_PARALYZE && AbilityBattleEffects(ABILITYEFFECT_ABSORBING, gBattlerTarget, 0, 0, gCurrentMove))
return;
if (IsMovePowderBlocked(gBattlerAttacker, gBattlerTarget, gCurrentMove))
return;
@ -2423,7 +2423,7 @@ static void Cmd_attackanimation(void)
}
// handle special move animations
if (gMovesInfo[gCurrentMove].effect == EFFECT_EXPANDING_FORCE && moveTarget & MOVE_TARGET_BOTH && CountAliveMonsInBattle(BATTLE_ALIVE_SIDE, BATTLE_OPPOSITE(gBattlerAttacker) > 1))
if (GetMoveEffect(gCurrentMove) == EFFECT_EXPANDING_FORCE && moveTarget & MOVE_TARGET_BOTH && CountAliveMonsInBattle(BATTLE_ALIVE_SIDE, BATTLE_OPPOSITE(gBattlerAttacker) > 1))
gBattleScripting.animTurn = 1;
if (!(moveResultFlags & MOVE_RESULT_NO_EFFECT))
@ -6475,7 +6475,7 @@ static void Cmd_moveend(void)
gBattleScripting.moveendState++;
break;
}
else if (gMovesInfo[gCurrentMove].effect == EFFECT_RECOIL_IF_MISS
else if (GetMoveEffect(gCurrentMove) == EFFECT_RECOIL_IF_MISS
&& (!IsBattlerTurnDamaged(gBattlerTarget) || gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT)
&& !gBattleStruct->noTargetPresent
&& IsBattlerAlive(gBattlerAttacker))
@ -6708,6 +6708,7 @@ static void Cmd_moveend(void)
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerAlive(gBattlerTarget)
&& IsBattlerAlive(gBattlerAttacker)
&& !(gStatuses3[BATTLE_PARTNER(gBattlerTarget)] & STATUS3_COMMANDER)
&& gSpecialStatuses[gBattlerAttacker].parentalBondState != PARENTAL_BOND_1ST_HIT)
{
u32 targetAbility = GetBattlerAbility(gBattlerTarget);
@ -6841,12 +6842,13 @@ static void Cmd_moveend(void)
}
}
if (gHitMarker & HITMARKER_SWAP_ATTACKER_TARGET)
// After swapattackerwithtarget is used for snatch the correct battlers have to be restored so data is stored correctly
if (gBattleStruct->snatchedMoveIsUsed)
{
u8 temp;
u32 temp;
SWAP(gBattlerAttacker, gBattlerTarget, temp);
gHitMarker &= ~HITMARKER_SWAP_ATTACKER_TARGET;
}
if (!gSpecialStatuses[gBattlerAttacker].dancerUsedMove)
{
gDisableStructs[gBattlerAttacker].usedMoves |= 1u << gCurrMovePos;
@ -7147,6 +7149,9 @@ static void Cmd_moveend(void)
if (numEjectPackBattlers > 1)
SortBattlersBySpeed(battlers, FALSE);
for (i = 0; i < gBattlersCount; i++)
gProtectStructs[i].statFell = FALSE; // restore for every possible eject pack battler
for (i = 0; i < gBattlersCount; i++)
{
u32 battler = battlers[i];
@ -7165,7 +7170,6 @@ static void Cmd_moveend(void)
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_EjectPackActivates;
AI_DATA->ejectPackSwitch = TRUE;
gProtectStructs[battler].statFell = FALSE;
break; // Only the fastest Eject item activates
}
}
@ -8175,6 +8179,7 @@ static bool32 DoSwitchInEffectsForBattler(u32 battler)
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWITCHIN_NEUTRALIZING_GAS;
gSpecialStatuses[battler].announceNeutralizingGas = TRUE;
gDisableStructs[battler].neutralizingGas = TRUE;
gBattlerAbility = battler;
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_SwitchInAbilityMsgRet;
@ -8826,14 +8831,8 @@ static void Cmd_swapattackerwithtarget(void)
{
CMD_ARGS();
u8 temp;
u32 temp;
SWAP(gBattlerAttacker, gBattlerTarget, temp);
if (gHitMarker & HITMARKER_SWAP_ATTACKER_TARGET)
gHitMarker &= ~HITMARKER_SWAP_ATTACKER_TARGET;
else
gHitMarker |= HITMARKER_SWAP_ATTACKER_TARGET;
gBattlescriptCurrInstr = cmd->nextInstr;
}
@ -12592,7 +12591,8 @@ static u32 ChangeStatBuffs(s8 statValue, u32 statId, u32 flags, const u8 *BS_ptr
}
else
{
gProtectStructs[battler].statFell = TRUE; // Eject pack, lash out
gProtectStructs[battler].statFell = TRUE;
gProtectStructs[battler].lashOutAffected = TRUE;
gBattleCommunication[MULTISTRING_CHOOSER] = (gBattlerTarget == battler); // B_MSG_ATTACKER_STAT_FELL or B_MSG_DEFENDER_STAT_FELL
}
}
@ -16669,14 +16669,24 @@ static void Cmd_averagestats(void)
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_jumpifoppositegenders(void)
static void Cmd_jumpifcaptivateaffected(void)
{
CMD_ARGS(const u8 *jumpInstr);
if (AreBattlersOfOppositeGender(gBattlerAttacker, gBattlerTarget))
if (GetBattlerAbility(gBattlerTarget) == ABILITY_OBLIVIOUS)
{
gBattlescriptCurrInstr = BattleScript_NotAffectedAbilityPopUp;
gLastUsedAbility = ABILITY_OBLIVIOUS;
RecordAbilityBattle(gBattlerTarget, ABILITY_OBLIVIOUS);
}
else if (AreBattlersOfOppositeGender(gBattlerAttacker, gBattlerTarget))
{
gBattlescriptCurrInstr = cmd->jumpInstr;
}
else
{
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
static void Cmd_unused(void)
@ -18805,3 +18815,87 @@ void BS_SetSteelsurge(void)
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
void BS_JumpIfIntimidateAbilityPrevented(void)
{
NATIVE_ARGS();
u32 hasAbility = FALSE;
u32 ability = GetBattlerAbility(gBattlerTarget);
switch (ability)
{
case ABILITY_INNER_FOCUS:
case ABILITY_SCRAPPY:
case ABILITY_OWN_TEMPO:
case ABILITY_OBLIVIOUS:
if (B_UPDATED_INTIMIDATE >= GEN_8)
{
hasAbility = TRUE;
gBattlescriptCurrInstr = BattleScript_IntimidatePrevented;
}
else
{
gBattlescriptCurrInstr = cmd->nextInstr;
}
break;
case ABILITY_GUARD_DOG:
hasAbility = TRUE;
gBattlescriptCurrInstr = BattleScript_IntimidateInReverse;
break;
default:
gBattlescriptCurrInstr = cmd->nextInstr;
break;
}
if (hasAbility)
{
gLastUsedAbility = ability;
gBattlerAbility = gBattlerTarget;
RecordAbilityBattle(gBattlerTarget, gLastUsedAbility);
}
}
void BS_TryIntimidatEjectpack(void)
{
NATIVE_ARGS();
u32 affectedBattler = 0xFF;
u32 battler = BATTLE_OPPOSITE(gBattlerAttacker);
u32 partnerBattler = BATTLE_PARTNER(battler);
bool32 ejectPackBattler = CanEjectPackTrigger(gBattlerAttacker, battler, MOVE_NONE);
bool32 ejectPackPartnerBattler = CanEjectPackTrigger(gBattlerAttacker, partnerBattler, MOVE_NONE);
if (ejectPackBattler && ejectPackPartnerBattler)
{
u32 battlerSpeed = GetBattlerTotalSpeedStat(battler);
u32 partnerbattlerSpeed = GetBattlerTotalSpeedStat(partnerBattler);
if (battlerSpeed >= partnerbattlerSpeed)
affectedBattler = battler;
else
affectedBattler = partnerBattler;
}
else if (ejectPackBattler)
{
affectedBattler = battler;
}
else if (ejectPackPartnerBattler)
{
affectedBattler = partnerBattler;
}
gBattlescriptCurrInstr = cmd->nextInstr;
if (affectedBattler != 0xFF)
{
gProtectStructs[battler].statFell = FALSE;
gProtectStructs[partnerBattler].statFell = FALSE;
AI_DATA->ejectPackSwitch = TRUE;
gBattleScripting.battler = affectedBattler;
gLastUsedItem = gBattleMons[affectedBattler].item;
RecordItemEffectBattle(affectedBattler, HOLD_EFFECT_EJECT_PACK);
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_EjectPackActivate_Ret;
}
}

View File

@ -787,7 +787,7 @@ void HandleAction_NothingIsFainted(void)
gCurrentActionFuncId = gActionsByTurnOrder[gCurrentTurnActionNumber];
gHitMarker &= ~(HITMARKER_DESTINYBOND | HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_ATTACKSTRING_PRINTED
| HITMARKER_NO_PPDEDUCT | HITMARKER_STATUS_ABILITY_EFFECT | HITMARKER_PASSIVE_DAMAGE
| HITMARKER_OBEYS | HITMARKER_WAKE_UP_CLEAR | HITMARKER_SYNCHRONISE_EFFECT
| HITMARKER_OBEYS | HITMARKER_SYNCHRONISE_EFFECT
| HITMARKER_CHARGING | HITMARKER_NEVER_SET);
}
@ -801,7 +801,7 @@ void HandleAction_ActionFinished(void)
SpecialStatusesClear();
gHitMarker &= ~(HITMARKER_DESTINYBOND | HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_ATTACKSTRING_PRINTED
| HITMARKER_NO_PPDEDUCT | HITMARKER_STATUS_ABILITY_EFFECT | HITMARKER_PASSIVE_DAMAGE
| HITMARKER_OBEYS | HITMARKER_WAKE_UP_CLEAR | HITMARKER_SYNCHRONISE_EFFECT
| HITMARKER_OBEYS | HITMARKER_SYNCHRONISE_EFFECT
| HITMARKER_CHARGING | HITMARKER_NEVER_SET | HITMARKER_IGNORE_DISGUISE);
// check if Stellar type boost should be used up
@ -3309,7 +3309,6 @@ static void CancellerObedience(u32 *effect)
SetAtkCancellerForCalledMove();
gBattlescriptCurrInstr = BattleScript_IgnoresAndUsesRandomMove;
gBattlerTarget = GetBattleMoveTarget(gCalledMove, NO_TARGET_OVERRIDE);
gHitMarker |= HITMARKER_DISOBEDIENT_MOVE;
gHitMarker |= HITMARKER_OBEYS;
break;
}
@ -5363,6 +5362,8 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
case ABILITY_COMMANDER:
partner = BATTLE_PARTNER(battler);
if (!gSpecialStatuses[battler].switchInAbilityDone
&& IsBattlerAlive(partner)
&& IsBattlerAlive(battler)
&& gBattleStruct->commanderActive[partner] == SPECIES_NONE
&& gBattleMons[partner].species == SPECIES_DONDOZO
&& GET_BASE_SPECIES_ID(GetMonData(GetPartyBattlerData(battler), MON_DATA_SPECIES)) == SPECIES_TATSUGIRI)
@ -7233,6 +7234,9 @@ static u32 ItemHealHp(u32 battler, u32 itemId, enum ItemCaseId caseID, bool32 pe
static bool32 UnnerveOn(u32 battler, u32 itemId)
{
if (gBattleScripting.overrideBerryRequirements > 0) // Berries that aren't eaten naturally ignore unnerve
return FALSE;
if (ItemId_GetPocket(itemId) == POCKET_BERRIES && IsUnnerveAbilityOnOpposingSide(battler))
return TRUE;
return FALSE;
@ -8448,9 +8452,6 @@ u32 ItemBattleEffects(enum ItemCaseId caseID, u32 battler, bool32 moveTurn)
gBattlescriptCurrInstr = BattleScript_WhiteHerbRet;
}
break;
case HOLD_EFFECT_EJECT_PACK:
effect = TryEjectPack(battler, ITEMEFFECT_ON_SWITCH_IN);
break;
}
break;
}
@ -9228,7 +9229,7 @@ static inline u32 CalcMoveBasePower(struct DamageCalculationData *damageCalcData
basePower *= 2;
break;
case EFFECT_LASH_OUT:
if (gProtectStructs[battlerAtk].statFell)
if (gProtectStructs[battlerAtk].lashOutAffected)
basePower *= 2;
break;
case EFFECT_EXPLOSION:

View File

@ -5,7 +5,7 @@ SINGLE_BATTLE_TEST("Color Change changes the type of a Pokemon being hit by a mo
{
GIVEN {
ASSUME(gSpeciesInfo[SPECIES_KECLEON].types[0] != TYPE_PSYCHIC && gSpeciesInfo[SPECIES_KECLEON].types[1] != TYPE_PSYCHIC);
ASSUME(gMovesInfo[MOVE_PSYWAVE].type == TYPE_PSYCHIC);
ASSUME(GetMoveType(MOVE_PSYWAVE) == TYPE_PSYCHIC);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_KECLEON) { Ability(ABILITY_COLOR_CHANGE); }
} WHEN {
@ -21,7 +21,7 @@ SINGLE_BATTLE_TEST("Color Change does not change the type when hit by a move tha
{
GIVEN {
ASSUME(gSpeciesInfo[SPECIES_KECLEON].types[0] == TYPE_NORMAL || gSpeciesInfo[SPECIES_KECLEON].types[1] == TYPE_NORMAL);
ASSUME(gMovesInfo[MOVE_TACKLE].type == TYPE_NORMAL);
ASSUME(GetMoveType(MOVE_TACKLE) == TYPE_NORMAL);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_KECLEON) { Ability(ABILITY_COLOR_CHANGE); }
} WHEN {

View File

@ -421,3 +421,37 @@ DOUBLE_BATTLE_TEST("Commander Tatsugiri does not get hit by Dragon Darts when co
NOT HP_BAR(playerLeft);
}
}
DOUBLE_BATTLE_TEST("Commander will not activate if Dondozo fainted right before Tatsugiri came in")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_DONDOZO) { HP(1); }
PLAYER(SPECIES_TATSUGIRI) { Ability(ABILITY_COMMANDER); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponentRight, MOVE_TACKLE, target: playerRight); MOVE(playerLeft, MOVE_SHED_TAIL); SEND_OUT(playerLeft, 2); SEND_OUT(playerRight, 3); }
} SCENE {
NOT ABILITY_POPUP(playerLeft, ABILITY_COMMANDER);
}
}
DOUBLE_BATTLE_TEST("Commander prevent Dondozo from switch out by Dragon Tail")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_DRAGON_TAIL) == EFFECT_HIT_SWITCH_TARGET);
PLAYER(SPECIES_DONDOZO);
PLAYER(SPECIES_TATSUGIRI) { Ability(ABILITY_COMMANDER); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponentLeft, MOVE_DRAGON_TAIL, target: playerLeft); }
} SCENE {
ABILITY_POPUP(playerRight, ABILITY_COMMANDER);
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_TAIL, opponentLeft);
NOT MESSAGE("Wobbuffet was dragged out!");
}
}

View File

@ -104,7 +104,7 @@ DOUBLE_BATTLE_TEST("Intimidate doesn't activate on an empty field in a double ba
}
}
SINGLE_BATTLE_TEST("Intimidate and Eject Button force the opponent to Attack")
SINGLE_BATTLE_TEST("Intimidate and Eject Button don't force the opponent to Attack")
{
GIVEN {
ASSUME(gItemsInfo[ITEM_EJECT_BUTTON].holdEffect == HOLD_EFFECT_EJECT_BUTTON);
@ -365,6 +365,7 @@ DOUBLE_BATTLE_TEST("Intimidate will correctly decrease the attack of the second
} SCENE {
ABILITY_POPUP(opponentLeft, ABILITY_INTIMIDATE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft);
ABILITY_POPUP(playerLeft, ABILITY_PROTOSYNTHESIS);
@ -372,14 +373,13 @@ DOUBLE_BATTLE_TEST("Intimidate will correctly decrease the attack of the second
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight);
}
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
}
}
SINGLE_BATTLE_TEST("Intimdate does not lose timing after mega evolution and switch out by a hit escape move")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_U_TURN].effect == EFFECT_HIT_ESCAPE);
ASSUME(GetMoveEffect(MOVE_U_TURN) == EFFECT_HIT_ESCAPE);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_MANECTRIC) { Item(ITEM_MANECTITE); }
OPPONENT(SPECIES_ARBOK) { Ability(ABILITY_INTIMIDATE); }
@ -390,3 +390,48 @@ SINGLE_BATTLE_TEST("Intimdate does not lose timing after mega evolution and swit
ABILITY_POPUP(opponent, ABILITY_INTIMIDATE);
}
}
DOUBLE_BATTLE_TEST("Intimidate drop down both opposing atk before eject pack has the chance to activate")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_EJECT_PACK); }
PLAYER(SPECIES_WYNAUT);
PLAYER(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); }
OPPONENT(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); }
} WHEN {
TURN { SWITCH(opponentLeft, 2); SEND_OUT(playerLeft, 2); }
} SCENE {
ABILITY_POPUP(opponentLeft, ABILITY_INTIMIDATE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft);
ABILITY_POPUP(playerLeft, ABILITY_INTIMIDATE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight);
}
}
DOUBLE_BATTLE_TEST("Intimidate will not miss timing for competitive")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_EJECT_PACK); }
PLAYER(SPECIES_MILOTIC) { Ability(ABILITY_COMPETITIVE); }
PLAYER(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); }
OPPONENT(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); }
} WHEN {
TURN { SWITCH(opponentLeft, 2); SEND_OUT(playerLeft, 2); }
} SCENE {
ABILITY_POPUP(opponentLeft, ABILITY_INTIMIDATE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
ABILITY_POPUP(playerRight, ABILITY_COMPETITIVE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft);
ABILITY_POPUP(playerLeft, ABILITY_INTIMIDATE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight);
}
}

View File

@ -51,7 +51,7 @@ DOUBLE_BATTLE_TEST("Neutralizing Gas prevents ally's switch-in ability from acti
DOUBLE_BATTLE_TEST("Neutralizing Gas ignores all battlers' ability effects")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_SURF].target == MOVE_TARGET_FOES_AND_ALLY);
ASSUME(GetMoveTarget(MOVE_SURF) == MOVE_TARGET_FOES_AND_ALLY);
PLAYER(SPECIES_WEEZING) { Ability(ABILITY_NEUTRALIZING_GAS); }
PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_TELEPATHY); }
OPPONENT(SPECIES_LANTURN) { Ability(ABILITY_WATER_ABSORB); }
@ -87,7 +87,7 @@ SINGLE_BATTLE_TEST("Neutralizing Gas ignores multipliers from attacker's ability
PARAMETRIZE { ability = ABILITY_NEUTRALIZING_GAS; }
PARAMETRIZE { ability = ABILITY_LEVITATE; }
GIVEN {
ASSUME(gMovesInfo[MOVE_TACKLE].category == DAMAGE_CATEGORY_PHYSICAL);
ASSUME(GetMoveCategory(MOVE_TACKLE) == DAMAGE_CATEGORY_PHYSICAL);
PLAYER(SPECIES_WEEZING) { Ability(ability); }
OPPONENT(SPECIES_AZUMARILL) { Ability(ABILITY_HUGE_POWER); }
} WHEN {
@ -106,8 +106,8 @@ SINGLE_BATTLE_TEST("Neutralizing Gas ignores multipliers from target's ability",
PARAMETRIZE { ability = ABILITY_NEUTRALIZING_GAS; }
PARAMETRIZE { ability = ABILITY_LEVITATE; }
GIVEN {
ASSUME(gMovesInfo[MOVE_TACKLE].makesContact == TRUE);
ASSUME(gMovesInfo[MOVE_TACKLE].type == TYPE_NORMAL);
ASSUME(MoveMakesContact(MOVE_TACKLE) == TRUE);
ASSUME(GetMoveType(MOVE_TACKLE) == TYPE_NORMAL);
PLAYER(SPECIES_WEEZING) { Ability(ability); }
OPPONENT(SPECIES_BEWEAR) { Ability(ABILITY_FLUFFY); }
} WHEN {
@ -146,7 +146,7 @@ DOUBLE_BATTLE_TEST("Neutralizing Gas ignores multipliers from ally's ability", s
PARAMETRIZE { ability = ABILITY_NEUTRALIZING_GAS; }
PARAMETRIZE { ability = ABILITY_LEVITATE; }
GIVEN {
ASSUME(gMovesInfo[MOVE_TACKLE].category == DAMAGE_CATEGORY_PHYSICAL);
ASSUME(GetMoveCategory(MOVE_TACKLE) == DAMAGE_CATEGORY_PHYSICAL);
PLAYER(SPECIES_WEEZING) { Ability(ability); }
PLAYER(SPECIES_WO_CHIEN) { Ability(ABILITY_TABLETS_OF_RUIN); }
OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_TELEPATHY); }

View File

@ -23,7 +23,7 @@ SINGLE_BATTLE_TEST("Oblivious prevents Captivate")
PLAYER(SPECIES_SLOWPOKE) { Ability(ABILITY_OBLIVIOUS); Gender(MON_MALE); }
OPPONENT(SPECIES_WOBBUFFET) { Gender(MON_FEMALE); }
} WHEN {
TURN { MOVE(opponent, MOVE_ATTRACT); }
TURN { MOVE(opponent, MOVE_CAPTIVATE); }
} SCENE {
ABILITY_POPUP(player, ABILITY_OBLIVIOUS);
NONE_OF { ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); }

View File

@ -84,7 +84,6 @@ SINGLE_BATTLE_TEST("Teraform Zero cannot be copied")
DOUBLE_BATTLE_TEST("Teraform Zero shouldn't cause Neutralizing Gas to show it's popup when trying to activate")
{
KNOWN_FAILING; // #5010
GIVEN {
PLAYER(SPECIES_TERAPAGOS_TERASTAL);
PLAYER(SPECIES_ABSOL) {Ability(ABILITY_PRESSURE); }
@ -95,8 +94,8 @@ DOUBLE_BATTLE_TEST("Teraform Zero shouldn't cause Neutralizing Gas to show it's
TURN { SWITCH(playerRight, 2); MOVE(playerLeft, MOVE_CELEBRATE, gimmick: GIMMICK_TERA); }
} SCENE {
MESSAGE("Terapagos is storing energy!");
MESSAGE("Terapagos terastalized into the Stellar type!");
MESSAGE("Terapagos terastallized into the Stellar type!");
NOT ABILITY_POPUP(playerRight, ABILITY_NEUTRALIZING_GAS);
MESSAGE("Terapagos used Celebreate!");
MESSAGE("Terapagos used Celebrate!");
}
}

View File

@ -4,7 +4,7 @@
SINGLE_BATTLE_TEST("Thermal Exchange makes Will-O-Wisp fail")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_WILL_O_WISP].effect == EFFECT_WILL_O_WISP);
ASSUME(GetMoveEffect(MOVE_WILL_O_WISP) == EFFECT_WILL_O_WISP);
PLAYER(SPECIES_BAXCALIBUR) { Ability(ABILITY_THERMAL_EXCHANGE); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
@ -37,8 +37,8 @@ SINGLE_BATTLE_TEST("Thermal Exchange prevents the user from getting burned when
SINGLE_BATTLE_TEST("Thermal Exchange cures burns when acquired")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_WILL_O_WISP].effect == EFFECT_WILL_O_WISP);
ASSUME(gMovesInfo[MOVE_SKILL_SWAP].effect == EFFECT_SKILL_SWAP);
ASSUME(GetMoveEffect(MOVE_WILL_O_WISP) == EFFECT_WILL_O_WISP);
ASSUME(GetMoveEffect(MOVE_SKILL_SWAP) == EFFECT_SKILL_SWAP);
PLAYER(SPECIES_BAXCALIBUR) { Ability(ABILITY_THERMAL_EXCHANGE); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
@ -56,7 +56,7 @@ SINGLE_BATTLE_TEST("Thermal Exchange cures burns when acquired")
SINGLE_BATTLE_TEST("Thermal Exchange burn prevention can be bypassed with Mold Breaker but is cured after")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_WILL_O_WISP].effect == EFFECT_WILL_O_WISP);
ASSUME(GetMoveEffect(MOVE_WILL_O_WISP) == EFFECT_WILL_O_WISP);
PLAYER(SPECIES_BAXCALIBUR) { Ability(ABILITY_THERMAL_EXCHANGE); }
OPPONENT(SPECIES_RAMPARDOS) { Ability(ABILITY_MOLD_BREAKER); }
} WHEN {
@ -73,7 +73,7 @@ SINGLE_BATTLE_TEST("Thermal Exchange burn prevention can be bypassed with Mold B
SINGLE_BATTLE_TEST("Thermal Exchange boosts attack if hit by a damaging fire type move")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_EMBER].type == TYPE_FIRE);
ASSUME(GetMoveType(MOVE_EMBER) == TYPE_FIRE);
PLAYER(SPECIES_BAXCALIBUR) { Ability(ABILITY_THERMAL_EXCHANGE); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {

View File

@ -835,8 +835,8 @@ AI_SINGLE_BATTLE_TEST("AI stays choice locked into moves in spite of the player'
AI_SINGLE_BATTLE_TEST("AI won't use Sucker Punch if it expects a move of the same priority bracket and the opponent is faster")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_QUICK_ATTACK].priority == 1);
ASSUME(gMovesInfo[MOVE_SUCKER_PUNCH].priority == 1);
ASSUME(GetMovePriority(MOVE_QUICK_ATTACK) == 1);
ASSUME(GetMovePriority(MOVE_SUCKER_PUNCH) == 1);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_WOBBUFFET) { Speed(300); Moves(MOVE_QUICK_ATTACK); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(100); Moves(MOVE_SUCKER_PUNCH, MOVE_TACKLE); }

View File

@ -160,3 +160,74 @@ SINGLE_BATTLE_TEST("Eject Pack will miss timing to switch out user if Eject Butt
EXPECT(opponent->species == SPECIES_WYNAUT);
}
}
DOUBLE_BATTLE_TEST("Eject Pack: Only the fastest Eject Pack will activate after intimidate")
{
u32 speed;
PARAMETRIZE { speed = 1; }
PARAMETRIZE { speed = 11; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Speed(10); Item(ITEM_EJECT_PACK); }
PLAYER(SPECIES_WYNAUT) { Speed(speed); Item(ITEM_EJECT_PACK); }
PLAYER(SPECIES_WOBBUFFET) { Speed(3); }
OPPONENT(SPECIES_WYNAUT) { Speed(4); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(5); }
OPPONENT(SPECIES_EKANS) { Speed(6); Ability(ABILITY_INTIMIDATE); }
} WHEN {
TURN {
SWITCH(opponentLeft, 2);
if (speed == 11)
SEND_OUT(playerRight, 2);
else
SEND_OUT(playerLeft, 2);
}
} SCENE {
ABILITY_POPUP(opponentLeft, ABILITY_INTIMIDATE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
if (speed == 11) {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerRight);
NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft);
} else {
NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerRight);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft);
}
}
}
DOUBLE_BATTLE_TEST("Eject Pack: Only the fastest Eject Pack will activate after a move stat drop")
{
u32 speed;
PARAMETRIZE { speed = 1; }
PARAMETRIZE { speed = 11; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Speed(10); Item(ITEM_EJECT_PACK); }
PLAYER(SPECIES_WYNAUT) { Speed(speed); Item(ITEM_EJECT_PACK); }
PLAYER(SPECIES_WOBBUFFET) { Speed(3); }
OPPONENT(SPECIES_WYNAUT) { Speed(4); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(5); }
} WHEN {
TURN {
MOVE(opponentLeft, MOVE_BUBBLE);
if (speed == 11)
SEND_OUT(playerRight, 2);
else
SEND_OUT(playerLeft, 2);
}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BUBBLE, opponentLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
if (speed == 11) {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerRight);
NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft);
} else {
NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerRight);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft);
}
}
}

View File

@ -208,7 +208,7 @@ DOUBLE_BATTLE_TEST("Shell Bell heals accumulated damage for spread moves")
const u16 maxHp = 200;
const u16 initHp = 1;
GIVEN {
ASSUME(gMovesInfo[MOVE_DISCHARGE].target == MOVE_TARGET_FOES_AND_ALLY);
ASSUME(GetMoveTarget(MOVE_DISCHARGE) == MOVE_TARGET_FOES_AND_ALLY);
PLAYER(SPECIES_ARIADOS) { MaxHP(maxHp); HP(initHp); Item(ITEM_SHELL_BELL); }
PLAYER(SPECIES_WOBBUFFET) {}
OPPONENT(SPECIES_GYARADOS) {}

View File

@ -77,9 +77,9 @@ SINGLE_BATTLE_TEST("Dream Eater works on targets with Comatose")
SINGLE_BATTLE_TEST("Dream Eater fails if the target is behind a Substitute (Gen 1-4)")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_YAWN].effect == EFFECT_YAWN);
ASSUME(gMovesInfo[MOVE_SUBSTITUTE].effect == EFFECT_SUBSTITUTE);
ASSUME(!gMovesInfo[MOVE_DREAM_EATER].ignoresSubstitute);
ASSUME(GetMoveEffect(MOVE_YAWN) == EFFECT_YAWN);
ASSUME(GetMoveEffect(MOVE_SUBSTITUTE) == EFFECT_SUBSTITUTE);
ASSUME(!MoveIgnoresSubstitute(MOVE_DREAM_EATER));
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
@ -97,8 +97,8 @@ SINGLE_BATTLE_TEST("Dream Eater works if the target is behind a Substitute (Gen
s16 damage;
s16 healed;
GIVEN {
ASSUME(gMovesInfo[MOVE_YAWN].effect == EFFECT_YAWN);
ASSUME(gMovesInfo[MOVE_SUBSTITUTE].effect == EFFECT_SUBSTITUTE);
ASSUME(GetMoveEffect(MOVE_YAWN) == EFFECT_YAWN);
ASSUME(GetMoveEffect(MOVE_SUBSTITUTE) == EFFECT_SUBSTITUTE);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { HP(1); }
} WHEN {

View File

@ -3,8 +3,8 @@
ASSUMPTIONS
{
ASSUME(gMovesInfo[MOVE_FOLLOW_ME].effect == EFFECT_FOLLOW_ME);
ASSUME(gMovesInfo[MOVE_SPOTLIGHT].effect == EFFECT_FOLLOW_ME);
ASSUME(GetMoveEffect(MOVE_FOLLOW_ME) == EFFECT_FOLLOW_ME);
ASSUME(GetMoveEffect(MOVE_SPOTLIGHT) == EFFECT_FOLLOW_ME);
}
DOUBLE_BATTLE_TEST("Follow Me redirects single target moves used by opponents to user")

View File

@ -249,8 +249,8 @@ DOUBLE_BATTLE_TEST("Instructed move will be redirected by Follow Me after instru
PARAMETRIZE { moveTarget = opponentLeft; }
PARAMETRIZE { moveTarget = opponentRight; }
GIVEN {
ASSUME(gMovesInfo[MOVE_FOLLOW_ME].effect == EFFECT_FOLLOW_ME);
ASSUME(gMovesInfo[MOVE_SKILL_SWAP].effect == EFFECT_SKILL_SWAP);
ASSUME(GetMoveEffect(MOVE_FOLLOW_ME) == EFFECT_FOLLOW_ME);
ASSUME(GetMoveEffect(MOVE_SKILL_SWAP) == EFFECT_SKILL_SWAP);
PLAYER(SPECIES_DURALUDON) { Ability(ABILITY_STALWART); }
PLAYER(SPECIES_DURALUDON) { Ability(ABILITY_STALWART); }
OPPONENT(SPECIES_WOBBUFFET);
@ -279,9 +279,9 @@ DOUBLE_BATTLE_TEST("Instructed move will be redirected by Rage Powder after inst
PARAMETRIZE { moveTarget = opponentLeft; }
PARAMETRIZE { moveTarget = opponentRight; }
GIVEN {
ASSUME(gMovesInfo[MOVE_RAGE_POWDER].effect == EFFECT_FOLLOW_ME);
ASSUME(gMovesInfo[MOVE_RAGE_POWDER].powderMove == TRUE);
ASSUME(gMovesInfo[MOVE_SOAK].effect == EFFECT_SOAK);
ASSUME(GetMoveEffect(MOVE_RAGE_POWDER) == EFFECT_FOLLOW_ME);
ASSUME(IsPowderMove(MOVE_RAGE_POWDER) == TRUE);
ASSUME(GetMoveEffect(MOVE_SOAK) == EFFECT_SOAK);
PLAYER(SPECIES_TREECKO);
PLAYER(SPECIES_SCEPTILE);
OPPONENT(SPECIES_WOBBUFFET);

View File

@ -3,7 +3,7 @@
ASSUMPTIONS
{
ASSUME(gMovesInfo[MOVE_LASER_FOCUS].effect == EFFECT_LASER_FOCUS);
ASSUME(GetMoveEffect(MOVE_LASER_FOCUS) == EFFECT_LASER_FOCUS);
}
SINGLE_BATTLE_TEST("Laser Focus causes the user's move used on the next turn to result in a Critical Hit")

View File

@ -102,7 +102,7 @@ SINGLE_BATTLE_TEST("Recoil if miss: Jump Kick's recoil happens after Spiky Shiel
SINGLE_BATTLE_TEST("Recoil if miss: Jump Kick recoil happens after Spiky Shield damage")
{
GIVEN {
ASSUME(!gMovesInfo[MOVE_JUMP_KICK].ignoresProtect);
ASSUME(!MoveIgnoresProtect(MOVE_JUMP_KICK));
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {

View File

@ -3,7 +3,7 @@
ASSUMPTIONS
{
ASSUME(gMovesInfo[MOVE_ROTOTILLER].effect == EFFECT_ROTOTILLER);
ASSUME(GetMoveEffect(MOVE_ROTOTILLER) == EFFECT_ROTOTILLER);
}
DOUBLE_BATTLE_TEST("Rototiller boosts Attack and Special Attack of all Grass types on the field")
@ -55,7 +55,7 @@ SINGLE_BATTLE_TEST("Rototiller doesn't affect pokemon that are semi-invulnerable
{
GIVEN {
ASSUME(gSpeciesInfo[SPECIES_TANGELA].types[0] == TYPE_GRASS);
ASSUME(gMovesInfo[MOVE_DIG].effect == EFFECT_SEMI_INVULNERABLE);
ASSUME(GetMoveEffect(MOVE_DIG) == EFFECT_SEMI_INVULNERABLE);
PLAYER(SPECIES_TANGELA);
OPPONENT(SPECIES_TANGELA);
} WHEN {
@ -79,7 +79,7 @@ SINGLE_BATTLE_TEST("Rototiller fails if the only valid target is semi-invulnerab
ASSUME(gSpeciesInfo[SPECIES_TANGELA].types[0] == TYPE_GRASS);
ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[0] != TYPE_GRASS);
ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].types[1] != TYPE_GRASS);
ASSUME(gMovesInfo[MOVE_DIG].effect == EFFECT_SEMI_INVULNERABLE);
ASSUME(GetMoveEffect(MOVE_DIG) == EFFECT_SEMI_INVULNERABLE);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_TANGELA);
} WHEN {

View File

@ -94,8 +94,8 @@ SINGLE_BATTLE_TEST("Sleep Talk can use moves while choiced into Sleep Talk")
SINGLE_BATTLE_TEST("Sleep Talk fails if user is taunted")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_TAUNT].effect == EFFECT_TAUNT);
ASSUME(gMovesInfo[MOVE_SLEEP_TALK].category == DAMAGE_CATEGORY_STATUS);
ASSUME(GetMoveEffect(MOVE_TAUNT) == EFFECT_TAUNT);
ASSUME(GetMoveCategory(MOVE_SLEEP_TALK) == DAMAGE_CATEGORY_STATUS);
PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP); Moves(MOVE_SLEEP_TALK, MOVE_TACKLE, MOVE_FLY, MOVE_DIG); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
@ -113,7 +113,7 @@ DOUBLE_BATTLE_TEST("Sleep Talk calls move and that move may be redirected by Lig
{
PASSES_RANDOMLY(1, 2, RNG_RANDOM_TARGET);
GIVEN {
ASSUME(gMovesInfo[MOVE_SPARK].type == TYPE_ELECTRIC);
ASSUME(GetMoveType(MOVE_SPARK) == TYPE_ELECTRIC);
PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP); Moves(MOVE_SLEEP_TALK, MOVE_SPARK, MOVE_FLY, MOVE_DIG); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
@ -132,7 +132,7 @@ DOUBLE_BATTLE_TEST("Sleep Talk calls move and that move may be redirected by Sto
{
PASSES_RANDOMLY(1, 2, RNG_RANDOM_TARGET);
GIVEN {
ASSUME(gMovesInfo[MOVE_WATER_GUN].type == TYPE_WATER);
ASSUME(GetMoveType(MOVE_WATER_GUN) == TYPE_WATER);
PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP); Moves(MOVE_SLEEP_TALK, MOVE_WATER_GUN, MOVE_FLY, MOVE_DIG); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);

View File

@ -3,7 +3,7 @@
ASSUMPTIONS
{
ASSUME(gMovesInfo[MOVE_SNORE].effect == EFFECT_SNORE);
ASSUME(GetMoveEffect(MOVE_SNORE) == EFFECT_SNORE);
}
SINGLE_BATTLE_TEST("Snore fails if not asleep")
@ -47,7 +47,7 @@ SINGLE_BATTLE_TEST("Snore fails if user is throat chopped")
{
GIVEN {
ASSUME(MoveHasAdditionalEffect(MOVE_THROAT_CHOP, MOVE_EFFECT_THROAT_CHOP));
ASSUME(gMovesInfo[MOVE_SNORE].soundMove == TRUE);
ASSUME(IsSoundMove(MOVE_SNORE) == TRUE);
PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {

View File

@ -8,8 +8,8 @@ SINGLE_BATTLE_TEST("Spectral Thief steals opponents boost before attacking", s16
PARAMETRIZE { move = MOVE_SWORDS_DANCE; }
GIVEN {
ASSUME(gMovesInfo[MOVE_SWORDS_DANCE].effect == EFFECT_ATTACK_UP_2);
ASSUME(gMovesInfo[MOVE_SPECTRAL_THIEF].effect == EFFECT_SPECTRAL_THIEF);
ASSUME(GetMoveEffect(MOVE_SWORDS_DANCE) == EFFECT_ATTACK_UP_2);
ASSUME(GetMoveEffect(MOVE_SPECTRAL_THIEF) == EFFECT_SPECTRAL_THIEF);
PLAYER(SPECIES_REGIROCK);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
@ -32,8 +32,8 @@ SINGLE_BATTLE_TEST("Spectral Thief steals opponents boost before attacking", s16
SINGLE_BATTLE_TEST("Spectral Thief can't steal opponent's boost if target is immune")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_SWORDS_DANCE].effect == EFFECT_ATTACK_UP_2);
ASSUME(gMovesInfo[MOVE_SPECTRAL_THIEF].effect == EFFECT_SPECTRAL_THIEF);
ASSUME(GetMoveEffect(MOVE_SWORDS_DANCE) == EFFECT_ATTACK_UP_2);
ASSUME(GetMoveEffect(MOVE_SPECTRAL_THIEF) == EFFECT_SPECTRAL_THIEF);
PLAYER(SPECIES_MEOWTH);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {

View File

@ -133,3 +133,18 @@ SINGLE_BATTLE_TEST("Tanga Berry activates before Bug Bite")
EXPECT_EQ(player->item, ITEM_NONE);
}
}
SINGLE_BATTLE_TEST("Bug Bite ignores Unnerve")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(1); }
OPPONENT(SPECIES_TYRANITAR) { Ability(ABILITY_UNNERVE); Item(ITEM_ORAN_BERRY); }
} WHEN {
TURN { MOVE(player, MOVE_BUG_BITE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BUG_BITE, player);
HP_BAR(player);
} THEN {
EXPECT_EQ(opponent->item, ITEM_NONE);
}
}

View File

@ -16,7 +16,10 @@ SINGLE_BATTLE_TEST("Freeze Frost restores stat changes when it was succesful")
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_FREEZY_FROST, hit: moveSuccess); }
TURN {
MOVE(opponent, MOVE_SAND_ATTACK);
MOVE(player, MOVE_FREEZY_FROST, hit: moveSuccess);
}
} SCENE {
if (moveSuccess == TRUE)
{

View File

@ -26,7 +26,10 @@ SINGLE_BATTLE_TEST("Sappy Seed is not going to seed the target if it fails")
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SAPPY_SEED, hit: FALSE); }
TURN {
MOVE(opponent, MOVE_SAND_ATTACK);
MOVE(player, MOVE_SAPPY_SEED, hit: FALSE);
}
} SCENE {
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SAPPY_SEED, player);

View File

@ -16,7 +16,10 @@ SINGLE_BATTLE_TEST("Glitzy Glow sets up Light Screen when it was succesful")
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_GLITZY_GLOW, hit: moveSuccess); }
TURN {
MOVE(opponent, MOVE_SAND_ATTACK);
MOVE(player, MOVE_GLITZY_GLOW, hit: moveSuccess);
}
} SCENE {
if (moveSuccess == TRUE)
{

View File

@ -16,7 +16,10 @@ SINGLE_BATTLE_TEST("Baddy Bad sets up Reflect when it was succesful")
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_BADDY_BAD, hit: moveSuccess); }
TURN {
MOVE(opponent, MOVE_SAND_ATTACK);
MOVE(player, MOVE_BADDY_BAD, hit: moveSuccess);
}
} SCENE {
if (moveSuccess == TRUE)
{

View File

@ -87,7 +87,7 @@ SINGLE_BATTLE_TEST("Flare Blitz deals 33% of recoil damage to the user and can b
SINGLE_BATTLE_TEST("Flare Blitz is absorbed by Flash Fire and no recoil damage is dealt")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_FLARE_BLITZ].recoil > 0);
ASSUME(GetMoveRecoil(MOVE_FLARE_BLITZ) > 0);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_VULPIX) { Ability(ABILITY_FLASH_FIRE); };
} WHEN {

View File

@ -61,3 +61,20 @@ SINGLE_BATTLE_TEST("Freeze isn't thawed if opponent is asleep during thawing att
}
}
}
SINGLE_BATTLE_TEST("Freeze isn't thawed if opponent is asleep during thawing attack when using Scald")
{
PASSES_RANDOMLY(80, 100, RNG_FROZEN);
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_FREEZE); }
OPPONENT(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP); }
} WHEN {
TURN { MOVE(opponent, MOVE_SCALD); MOVE(player, MOVE_CELEBRATE); }
} SCENE {
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCALD, opponent);
MESSAGE("Wobbuffet thawed out!");
STATUS_ICON(player, none: TRUE);
}
}
}

View File

@ -2034,16 +2034,16 @@ s32 MoveGetTarget(s32 battlerId, u32 moveId, struct MoveContext *ctx, u32 source
}
else
{
const struct MoveInfo *move = &gMovesInfo[moveId];
if (move->target == MOVE_TARGET_RANDOM
|| move->target == MOVE_TARGET_BOTH
|| move->target == MOVE_TARGET_DEPENDS
|| move->target == MOVE_TARGET_FOES_AND_ALLY
|| move->target == MOVE_TARGET_OPPONENTS_FIELD)
u32 moveTarget = GetMoveTarget(moveId);
if (moveTarget == MOVE_TARGET_RANDOM
|| moveTarget == MOVE_TARGET_BOTH
|| moveTarget == MOVE_TARGET_DEPENDS
|| moveTarget == MOVE_TARGET_FOES_AND_ALLY
|| moveTarget == MOVE_TARGET_OPPONENTS_FIELD)
{
target = BATTLE_OPPOSITE(battlerId);
}
else if (move->target == MOVE_TARGET_SELECTED || move->target == MOVE_TARGET_OPPONENT)
else if (moveTarget == MOVE_TARGET_SELECTED || moveTarget == MOVE_TARGET_OPPONENT)
{
// In AI Doubles not specified target allows any target for EXPECT_MOVE.
if (GetBattleTest()->type != BATTLE_TEST_AI_DOUBLES)
@ -2053,11 +2053,11 @@ s32 MoveGetTarget(s32 battlerId, u32 moveId, struct MoveContext *ctx, u32 source
target = BATTLE_OPPOSITE(battlerId);
}
else if (move->target == MOVE_TARGET_USER || move->target == MOVE_TARGET_ALL_BATTLERS)
else if (moveTarget == MOVE_TARGET_USER || moveTarget == MOVE_TARGET_ALL_BATTLERS)
{
target = battlerId;
}
else if (move->target == MOVE_TARGET_ALLY)
else if (moveTarget == MOVE_TARGET_ALLY)
{
target = BATTLE_PARTNER(battlerId);
}