mirror of
https://github.com/rh-hideout/pokeemerald-expansion.git
synced 2026-03-21 18:04:50 -05:00
Adds CancelerSetTargets and improves Pre Attack Effects (#9178)
This commit is contained in:
parent
5aed923f0d
commit
cf18d086b6
|
|
@ -557,7 +557,6 @@ BattleScript_SkyDropFlyingAlreadyConfused:
|
|||
|
||||
BattleScript_EffectFling::
|
||||
attackcanceler
|
||||
setlastuseditem BS_ATTACKER
|
||||
accuracycheck BattleScript_FlingMissed
|
||||
pause B_WAIT_TIME_SHORT
|
||||
printstring STRINGID_PKMNFLUNG
|
||||
|
|
@ -2239,6 +2238,7 @@ BattleScript_EffectHit::
|
|||
attackcanceler
|
||||
BattleScript_HitFromAccCheck::
|
||||
accuracycheck BattleScript_MoveMissedPause
|
||||
copybyte gEffectBattler, gBattlerAttacker
|
||||
setpreattackadditionaleffect
|
||||
BattleScript_HitFromDamageCalc::
|
||||
damagecalc
|
||||
|
|
@ -2249,6 +2249,7 @@ BattleScript_MoveEnd::
|
|||
|
||||
BattleScript_EffectHit_RetFromAccCheck::
|
||||
accuracycheck BattleScript_MoveMissedPause
|
||||
copybyte gEffectBattler, gBattlerAttacker
|
||||
setpreattackadditionaleffect
|
||||
damagecalc
|
||||
BattleScript_Hit_RetFromAtkAnimation::
|
||||
|
|
|
|||
|
|
@ -700,8 +700,8 @@ struct BattleStruct
|
|||
u16 flingItem;
|
||||
u8 incrementEchoedVoice:1;
|
||||
u8 echoedVoiceCounter:3;
|
||||
u8 preAttackAnimPlayed:1;
|
||||
u8 padding4:1;
|
||||
u8 attackAnimPlayed:1;
|
||||
u8 preAttackEffectHappened:1;
|
||||
u8 magicCoatActive:1;
|
||||
u8 magicBounceActive:1;
|
||||
u8 moveBouncer;
|
||||
|
|
|
|||
|
|
@ -181,7 +181,6 @@ bool32 EndOrContinueWeather(void);
|
|||
enum DamageCategory GetReflectDamageMoveDamageCategory(enum BattlerId battler, enum Move move);
|
||||
bool32 IsUnnerveBlocked(enum BattlerId battler, enum Item itemId);
|
||||
bool32 IsAffectedByFollowMe(enum BattlerId battlerAtk, enum BattleSide defSide, enum Move move);
|
||||
void DetermineTarget(enum MoveTarget moveTarget, bool32 overwriteTarget);
|
||||
void HandleAction_UseMove(void);
|
||||
void HandleAction_Switch(void);
|
||||
void HandleAction_UseItem(void);
|
||||
|
|
|
|||
|
|
@ -40,19 +40,21 @@ enum CancelerState
|
|||
CANCELER_GHOST,
|
||||
CANCELER_PARALYZED,
|
||||
CANCELER_INFATUATION,
|
||||
CANCELER_BIDE,
|
||||
CANCELER_Z_MOVES,
|
||||
CANCELER_CHOICE_LOCK,
|
||||
CANCELER_CALLSUBMOVE,
|
||||
CANCELER_THAW,
|
||||
CANCELER_STANCE_CHANGE_2,
|
||||
CANCELER_ATTACKSTRING,
|
||||
CANCELER_SET_TARGETS,
|
||||
CANCELER_PPDEDUCTION,
|
||||
CANCELER_MOVE_SPECIFIC_MESSAGE,
|
||||
CANCELER_SKY_BATTLE,
|
||||
CANCELER_WEATHER_PRIMAL,
|
||||
CANCELER_FOCUS_PRE_GEN5,
|
||||
CANCELER_BIDE,
|
||||
CANCELER_MOVE_FAILURE,
|
||||
CANCELER_MOVE_EFFECT_FAILURE_TARGET,
|
||||
CANCELER_POWDER_STATUS,
|
||||
CANCELER_PRIORITY_BLOCK,
|
||||
CANCELER_PROTEAN,
|
||||
|
|
|
|||
|
|
@ -452,37 +452,6 @@ static enum CancelerResult CancelerInfatuation(struct BattleContext *ctx)
|
|||
return CANCELER_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static enum CancelerResult CancelerBide(struct BattleContext *ctx)
|
||||
{
|
||||
if (gBattleMons[ctx->battlerAtk].volatiles.bideTurns)
|
||||
{
|
||||
if (--gBattleMons[ctx->battlerAtk].volatiles.bideTurns)
|
||||
{
|
||||
gBattlescriptCurrInstr = BattleScript_BideStoringEnergy;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is removed in FRLG and Emerald for some reason
|
||||
//gBattleMons[gBattlerAttacker].volatiles.multipleTurns = FALSE;
|
||||
if (gBideDmg[ctx->battlerAtk])
|
||||
{
|
||||
gCurrentMove = MOVE_BIDE;
|
||||
gBattlerTarget = gBideTarget[ctx->battlerAtk];
|
||||
if (!IsBattlerAlive(ctx->battlerDef))
|
||||
gBattlerTarget = GetBattleMoveTarget(MOVE_BIDE, TARGET_SELECTED);
|
||||
gBattlescriptCurrInstr = BattleScript_BideAttack;
|
||||
return CANCELER_RESULT_BREAK; // Jumps to a different script but no failure
|
||||
}
|
||||
else
|
||||
{
|
||||
gBattlescriptCurrInstr = BattleScript_BideNoEnergyToAttack;
|
||||
return CANCELER_RESULT_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return CANCELER_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static enum CancelerResult CancelerZMoves(struct BattleContext *ctx)
|
||||
{
|
||||
if (GetActiveGimmick(ctx->battlerAtk) == GIMMICK_Z_MOVE)
|
||||
|
|
@ -532,7 +501,7 @@ static enum CancelerResult CancelerCallSubmove(struct BattleContext *ctx)
|
|||
const u8 *battleScript = NULL;
|
||||
battleScript = BattleScript_SubmoveAttackstring;
|
||||
|
||||
switch(GetMoveEffect(ctx->move))
|
||||
switch (GetMoveEffect(ctx->move))
|
||||
{
|
||||
case EFFECT_MIRROR_MOVE:
|
||||
calledMove = GetMirrorMoveMove();
|
||||
|
|
@ -578,6 +547,8 @@ static enum CancelerResult CancelerCallSubmove(struct BattleContext *ctx)
|
|||
|
||||
gBattleStruct->submoveAnnouncement = SUBMOVE_SUCCESS;
|
||||
gCalledMove = calledMove;
|
||||
if (GetBattlerMoveTargetType(ctx->battlerAtk, ctx->move) == TARGET_DEPENDS) // originally using a move without a set target
|
||||
gBattlerTarget = GetBattleMoveTarget(calledMove, TARGET_NONE);
|
||||
BattleScriptCall(battleScript);
|
||||
return CANCELER_RESULT_BREAK;
|
||||
}
|
||||
|
|
@ -643,10 +614,314 @@ static enum CancelerResult CancelerAttackstring(struct BattleContext *ctx)
|
|||
return CANCELER_RESULT_BREAK;
|
||||
}
|
||||
|
||||
#define checkFailure TRUE
|
||||
#define skipFailure FALSE
|
||||
static bool32 IsSingleTarget(enum BattlerId battlerAtk, enum BattlerId battlerDef)
|
||||
{
|
||||
if (battlerDef != gBattlerTarget)
|
||||
return skipFailure;
|
||||
return checkFailure;
|
||||
}
|
||||
|
||||
static bool32 IsSmartTarget(enum BattlerId battlerAtk, enum BattlerId battlerDef)
|
||||
{
|
||||
if (!IsBattlerAlly(gBattlerTarget, battlerDef) || battlerAtk == battlerDef)
|
||||
return skipFailure;
|
||||
return checkFailure;
|
||||
}
|
||||
|
||||
static bool32 IsTargetingBothFoes(enum BattlerId battlerAtk, enum BattlerId battlerDef)
|
||||
{
|
||||
if (battlerDef == BATTLE_PARTNER(battlerAtk) || battlerAtk == battlerDef)
|
||||
{
|
||||
// Because of Magic Bounce and Magic Coat we don't want to set MOVE_RESULT_NO_EFFECT
|
||||
if (GetMoveCategory(gCurrentMove) != DAMAGE_CATEGORY_STATUS)
|
||||
gBattleStruct->moveResultFlags[battlerDef] = MOVE_RESULT_NO_EFFECT;
|
||||
return skipFailure;
|
||||
}
|
||||
return checkFailure;
|
||||
}
|
||||
|
||||
static bool32 IsTargetingSelf(enum BattlerId battlerAtk, enum BattlerId battlerDef)
|
||||
{
|
||||
return skipFailure;
|
||||
}
|
||||
|
||||
static bool32 IsTargetingAlly(enum BattlerId battlerAtk, enum BattlerId battlerDef)
|
||||
{
|
||||
if (battlerDef != BATTLE_PARTNER(battlerAtk))
|
||||
{
|
||||
gBattleStruct->moveResultFlags[battlerDef] = MOVE_RESULT_NO_EFFECT;
|
||||
return skipFailure;
|
||||
}
|
||||
return checkFailure;
|
||||
}
|
||||
|
||||
static bool32 IsTargetingSelfAndAlly(enum BattlerId battlerAtk, enum BattlerId battlerDef)
|
||||
{
|
||||
if (battlerDef != BATTLE_PARTNER(battlerAtk))
|
||||
{
|
||||
if (battlerDef != battlerAtk) // Don't set result flags for user
|
||||
gBattleStruct->moveResultFlags[battlerDef] = MOVE_RESULT_NO_EFFECT;
|
||||
return skipFailure;
|
||||
}
|
||||
return checkFailure;
|
||||
}
|
||||
|
||||
static bool32 IsTargetingSelfOrAlly(enum BattlerId battlerAtk, enum BattlerId battlerDef)
|
||||
{
|
||||
if (battlerDef == battlerAtk)
|
||||
return skipFailure;
|
||||
|
||||
if (battlerDef != BATTLE_PARTNER(battlerAtk))
|
||||
{
|
||||
gBattleStruct->moveResultFlags[battlerDef] = MOVE_RESULT_NO_EFFECT;
|
||||
return skipFailure;
|
||||
}
|
||||
|
||||
return checkFailure;
|
||||
}
|
||||
|
||||
static bool32 IsTargetingFoesAndAlly(enum BattlerId battlerAtk, enum BattlerId battlerDef)
|
||||
{
|
||||
if (battlerAtk == battlerDef)
|
||||
return skipFailure; // Don't set result flags for user
|
||||
return checkFailure;
|
||||
}
|
||||
|
||||
static bool32 IsTargetingField(enum BattlerId battlerAtk, enum BattlerId battlerDef)
|
||||
{
|
||||
return skipFailure;
|
||||
}
|
||||
|
||||
static bool32 IsTargetingOpponentsField(enum BattlerId battlerAtk, enum BattlerId battlerDef)
|
||||
{
|
||||
if (IsBattlerAlly(battlerDef, BATTLE_OPPOSITE(battlerAtk)))
|
||||
return checkFailure;
|
||||
return skipFailure;
|
||||
}
|
||||
|
||||
static bool32 IsTargetingAllBattlers(enum BattlerId battlerAtk, enum BattlerId battlerDef)
|
||||
{
|
||||
return checkFailure;
|
||||
}
|
||||
|
||||
// ShouldCheckFailureOnTarget
|
||||
static bool32 (*const sShouldCheckTargetMoveFailure[])(enum BattlerId battlerAtk, enum BattlerId battlerDef) =
|
||||
{
|
||||
[TARGET_NONE] = IsTargetingField,
|
||||
[TARGET_SELECTED] = IsSingleTarget,
|
||||
[TARGET_DEPENDS] = IsSingleTarget,
|
||||
[TARGET_OPPONENT] = IsSingleTarget,
|
||||
[TARGET_RANDOM] = IsSingleTarget,
|
||||
[TARGET_BOTH] = IsTargetingBothFoes,
|
||||
[TARGET_USER] = IsTargetingSelf,
|
||||
[TARGET_SMART] = IsSmartTarget,
|
||||
[TARGET_ALLY] = IsTargetingAlly,
|
||||
[TARGET_USER_AND_ALLY] = IsTargetingSelfAndAlly,
|
||||
[TARGET_USER_OR_ALLY] = IsTargetingSelfOrAlly,
|
||||
[TARGET_FOES_AND_ALLY] = IsTargetingFoesAndAlly,
|
||||
[TARGET_FIELD] = IsTargetingField,
|
||||
[TARGET_OPPONENTS_FIELD] = IsTargetingOpponentsField,
|
||||
[TARGET_ALL_BATTLERS] = IsTargetingAllBattlers,
|
||||
};
|
||||
|
||||
static bool32 ShouldCheckTargetMoveFailure(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, enum MoveTarget moveTarget)
|
||||
{
|
||||
// For Bounced moves
|
||||
if (IsBattlerUnaffectedByMove(battlerDef))
|
||||
return skipFailure;
|
||||
|
||||
return sShouldCheckTargetMoveFailure[moveTarget](battlerAtk, battlerDef);
|
||||
}
|
||||
#undef checkFailure
|
||||
#undef skipFailure
|
||||
|
||||
static inline bool32 IsSmartTargetMoveConsecutiveHit(enum BattlerId battlerAtk, enum Move move)
|
||||
{
|
||||
if (GetBattlerMoveTargetType(battlerAtk, move) != TARGET_SMART)
|
||||
return FALSE;
|
||||
|
||||
if (gMultiHitCounter < GetMoveStrikeCount(move))
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bool32 IsAffectedByFollowMe(enum BattlerId battlerAtk, enum BattleSide defSide, enum Move move)
|
||||
{
|
||||
enum Ability ability = GetBattlerAbility(battlerAtk);
|
||||
enum BattleMoveEffects effect = GetMoveEffect(move);
|
||||
|
||||
if (gSideTimers[defSide].followmeTimer == 0
|
||||
|| (!IsBattlerAlive(gSideTimers[defSide].followmeTarget) && !IsSmartTargetMoveConsecutiveHit(battlerAtk, move))
|
||||
|| effect == EFFECT_SNIPE_SHOT
|
||||
|| effect == EFFECT_SKY_DROP
|
||||
|| IsAbilityAndRecord(battlerAtk, ability, ABILITY_PROPELLER_TAIL)
|
||||
|| IsAbilityAndRecord(battlerAtk, ability, ABILITY_STALWART))
|
||||
return FALSE;
|
||||
|
||||
if (effect == EFFECT_PURSUIT && IsPursuitTargetSet())
|
||||
return FALSE;
|
||||
|
||||
if (gSideTimers[defSide].followmePowder && !IsAffectedByPowderMove(battlerAtk, ability, GetBattlerHoldEffect(battlerAtk)))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static bool32 HandleMoveTargetRedirection(enum MoveTarget moveTarget)
|
||||
{
|
||||
u32 redirectorOrderNum = MAX_BATTLERS_COUNT;
|
||||
enum BattleMoveEffects moveEffect = GetMoveEffect(gCurrentMove);
|
||||
enum BattleSide side = BATTLE_OPPOSITE(GetBattlerSide(gBattlerAttacker));
|
||||
|
||||
if (moveEffect == EFFECT_REFLECT_DAMAGE)
|
||||
{
|
||||
enum DamageCategory reflectCategory = GetReflectDamageMoveDamageCategory(gBattlerAttacker, gCurrentMove);
|
||||
|
||||
if (reflectCategory == DAMAGE_CATEGORY_PHYSICAL)
|
||||
gBattlerTarget = gBattleStruct->moveTarget[gBattlerAttacker] = gProtectStructs[gBattlerAttacker].physicalBattlerId;
|
||||
else
|
||||
gBattlerTarget = gBattleStruct->moveTarget[gBattlerAttacker] = gProtectStructs[gBattlerAttacker].specialBattlerId;
|
||||
}
|
||||
|
||||
if (IsAffectedByFollowMe(gBattlerAttacker, side, gCurrentMove)
|
||||
&& (moveTarget == TARGET_SELECTED || moveTarget == TARGET_SMART || moveEffect == EFFECT_REFLECT_DAMAGE)
|
||||
&& !IsBattlerAlly(gBattlerAttacker, gSideTimers[side].followmeTarget))
|
||||
{
|
||||
gBattleStruct->moveTarget[gBattlerAttacker] = gBattlerTarget = gSideTimers[side].followmeTarget; // follow me moxie fix
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
enum Type moveType = GetBattleMoveType(gCurrentMove);
|
||||
enum Ability ability = GetBattlerAbility(gBattlerTarget);
|
||||
bool32 currTargetCantAbsorb = ((ability != ABILITY_LIGHTNING_ROD && moveType == TYPE_ELECTRIC)
|
||||
|| (ability != ABILITY_STORM_DRAIN && moveType == TYPE_WATER));
|
||||
|
||||
if (currTargetCantAbsorb
|
||||
&& IsDoubleBattle()
|
||||
&& gSideTimers[side].followmeTimer == 0
|
||||
&& moveTarget != TARGET_USER
|
||||
&& moveTarget != TARGET_ALL_BATTLERS
|
||||
&& moveTarget != TARGET_FIELD
|
||||
&& moveEffect != EFFECT_TEATIME
|
||||
&& moveEffect != EFFECT_SNIPE_SHOT
|
||||
&& moveEffect != EFFECT_PLEDGE)
|
||||
{
|
||||
// Find first battler that redirects the move (in turn order)
|
||||
enum Ability abilityAtk = GetBattlerAbility(gBattlerAttacker);
|
||||
enum BattlerId battler;
|
||||
for (battler = 0; battler < gBattlersCount; battler++)
|
||||
{
|
||||
ability = GetBattlerAbility(battler);
|
||||
if ((B_REDIRECT_ABILITY_ALLIES >= GEN_4 || !IsBattlerAlly(gBattlerAttacker, battler))
|
||||
&& battler != gBattlerAttacker
|
||||
&& gBattlerTarget != battler
|
||||
&& ((ability == ABILITY_LIGHTNING_ROD && moveType == TYPE_ELECTRIC)
|
||||
|| (ability == ABILITY_STORM_DRAIN && moveType == TYPE_WATER))
|
||||
&& GetBattlerTurnOrderNum(battler) < redirectorOrderNum
|
||||
&& !IsAbilityAndRecord(gBattlerAttacker, abilityAtk, ABILITY_PROPELLER_TAIL)
|
||||
&& !IsAbilityAndRecord(gBattlerAttacker, abilityAtk, ABILITY_STALWART))
|
||||
{
|
||||
redirectorOrderNum = GetBattlerTurnOrderNum(battler);
|
||||
}
|
||||
}
|
||||
if (redirectorOrderNum != MAX_BATTLERS_COUNT)
|
||||
{
|
||||
enum Ability battlerAbility;
|
||||
battler = gBattlerByTurnOrder[redirectorOrderNum];
|
||||
battlerAbility = GetBattlerAbility(battler);
|
||||
RecordAbilityBattle(battler, battlerAbility);
|
||||
gSpecialStatuses[battler].abilityRedirected = TRUE;
|
||||
gBattlerTarget = battler;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static bool32 WasOriginalTargetAlly(enum MoveTarget moveTarget)
|
||||
{
|
||||
if (!gProtectStructs[BATTLE_PARTNER(gBattlerAttacker)].usedAllySwitch)
|
||||
return FALSE;
|
||||
|
||||
if ((moveTarget == TARGET_ALLY || moveTarget == TARGET_USER_OR_ALLY) && gBattlerAttacker == gBattlerTarget)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static enum CancelerResult CancelerSetTargets(struct BattleContext *ctx)
|
||||
{
|
||||
enum MoveTarget moveTarget = GetBattlerMoveTargetType(ctx->battlerAtk, ctx->move);
|
||||
|
||||
if (!HandleMoveTargetRedirection(moveTarget))
|
||||
{
|
||||
if (IsDoubleBattle() && moveTarget == TARGET_RANDOM)
|
||||
{
|
||||
gBattlerTarget = SetRandomTarget(gBattlerAttacker);
|
||||
if (gAbsentBattlerFlags & (1u << gBattlerTarget)
|
||||
&& !IsBattlerAlly(gBattlerAttacker, gBattlerTarget))
|
||||
{
|
||||
gBattlerTarget = GetPartnerBattler(gBattlerTarget);
|
||||
}
|
||||
}
|
||||
else if (moveTarget == TARGET_ALLY && !IsBattlerAlly(gBattlerTarget, gBattlerAttacker))
|
||||
{
|
||||
gBattlerTarget = BATTLE_PARTNER(gBattlerAttacker);
|
||||
}
|
||||
else if (IsDoubleBattle() && moveTarget == TARGET_FOES_AND_ALLY)
|
||||
{
|
||||
for (gBattlerTarget = 0; gBattlerTarget < gBattlersCount; gBattlerTarget++)
|
||||
{
|
||||
if (gBattlerTarget == gBattlerAttacker)
|
||||
continue;
|
||||
if (IsBattlerAlive(gBattlerTarget))
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (moveTarget == TARGET_USER || moveTarget == TARGET_USER_AND_ALLY)
|
||||
{
|
||||
gBattlerTarget = gBattlerAttacker;
|
||||
}
|
||||
else if (!IsBattlerAlive(gBattlerTarget)
|
||||
&& moveTarget != TARGET_OPPONENTS_FIELD
|
||||
&& IsDoubleBattle()
|
||||
&& (!IsBattlerAlly(gBattlerAttacker, gBattlerTarget)))
|
||||
{
|
||||
gBattlerTarget = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gBattlerTarget)));
|
||||
}
|
||||
}
|
||||
|
||||
while (gBattleStruct->eventState.atkCancelerBattler < gBattlersCount)
|
||||
{
|
||||
enum BattlerId battlerDef = gBattleStruct->eventState.atkCancelerBattler++;
|
||||
|
||||
if (!ShouldCheckTargetMoveFailure(ctx->battlerAtk, battlerDef, ctx->move, moveTarget))
|
||||
gBattleStruct->battlerState[ctx->battlerAtk].targetsDone[battlerDef] = TRUE;
|
||||
}
|
||||
gBattleStruct->eventState.atkCancelerBattler = 0;
|
||||
|
||||
if (IsBattlerAlly(gBattlerAttacker, gBattlerTarget) && !IsBattlerAlive(gBattlerTarget))
|
||||
{
|
||||
gBattlescriptCurrInstr = BattleScript_ButItFailed;
|
||||
return CANCELER_RESULT_FAILURE;
|
||||
}
|
||||
else if (WasOriginalTargetAlly(moveTarget))
|
||||
{
|
||||
gBattlescriptCurrInstr = BattleScript_ButItFailed;
|
||||
return CANCELER_RESULT_FAILURE;
|
||||
}
|
||||
|
||||
return CANCELER_RESULT_BREAK; // update ctx->battlerDef
|
||||
}
|
||||
|
||||
static enum CancelerResult CancelerPPDeduction(struct BattleContext *ctx)
|
||||
{
|
||||
if (gBattleMons[ctx->battlerAtk].volatiles.multipleTurns
|
||||
|| gSpecialStatuses[ctx->battlerAtk].dancerUsedMove
|
||||
|| gBattleStruct->bouncedMoveIsUsed
|
||||
|| ctx->move == MOVE_STRUGGLE)
|
||||
return CANCELER_RESULT_SUCCESS;
|
||||
|
||||
|
|
@ -711,13 +986,11 @@ static enum CancelerResult CancelerPPDeduction(struct BattleContext *ctx)
|
|||
else
|
||||
{
|
||||
gBattleStruct->submoveAnnouncement = SUBMOVE_NO_EFFECT;
|
||||
gBattlerTarget = GetBattleMoveTarget(ctx->move, TARGET_NONE);
|
||||
gBattleScripting.animTurn = 0;
|
||||
gBattleScripting.animTargetsHit = 0;
|
||||
|
||||
// Possibly better to just move type setting and redirection to attackcanceler as a new case at this point
|
||||
SetTypeBeforeUsingMove(ctx->move, ctx->battlerAtk);
|
||||
DetermineTarget(moveTarget, FALSE);
|
||||
ClearDamageCalcResults();
|
||||
gBattlescriptCurrInstr = GetMoveBattleScript(ctx->move);
|
||||
return CANCELER_RESULT_BREAK;
|
||||
|
|
@ -767,12 +1040,61 @@ static enum CancelerResult CancelerWeatherPrimal(struct BattleContext *ctx)
|
|||
return result;
|
||||
}
|
||||
|
||||
static bool32 ShouldSkipFailureCheckOnBattler(enum BattlerId battlerAtk, enum BattlerId battlerDef, bool32 checkResultFlag)
|
||||
{
|
||||
if (gBattleStruct->battlerState[battlerAtk].targetsDone[battlerDef])
|
||||
return TRUE;
|
||||
if (checkResultFlag && gBattleStruct->moveResultFlags[battlerDef] & MOVE_RESULT_NO_EFFECT)
|
||||
return TRUE;
|
||||
if (GetConfig(CONFIG_CHECK_USER_FAILURE) >= GEN_5 && battlerAtk == battlerDef)
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static enum CancelerResult CancelerBide(struct BattleContext *ctx)
|
||||
{
|
||||
if (gBattleMons[ctx->battlerAtk].volatiles.bideTurns)
|
||||
{
|
||||
if (--gBattleMons[ctx->battlerAtk].volatiles.bideTurns)
|
||||
{
|
||||
gBattlescriptCurrInstr = BattleScript_BideStoringEnergy;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gBideDmg[ctx->battlerAtk])
|
||||
{
|
||||
gCurrentMove = MOVE_BIDE;
|
||||
gBattlerTarget = gBideTarget[ctx->battlerAtk];
|
||||
if (!IsBattlerAlive(gBattlerTarget))
|
||||
gBattlerTarget = GetBattleMoveTarget(MOVE_BIDE, TARGET_SELECTED);
|
||||
gBattlescriptCurrInstr = BattleScript_BideAttack;
|
||||
}
|
||||
else
|
||||
{
|
||||
gBattlescriptCurrInstr = BattleScript_BideNoEnergyToAttack;
|
||||
return CANCELER_RESULT_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return CANCELER_RESULT_BREAK; // Jumps to a different script but no failure
|
||||
}
|
||||
|
||||
static enum CancelerResult CancelerMoveFailure(struct BattleContext *ctx)
|
||||
{
|
||||
const u8 *battleScript = NULL;
|
||||
|
||||
switch (GetMoveEffect(ctx->move))
|
||||
{
|
||||
case EFFECT_FLING:
|
||||
if (!CanFling(ctx->battlerAtk))
|
||||
battleScript = BattleScript_ButItFailed;
|
||||
else // set fling item
|
||||
gBattleStruct->flingItem = gLastUsedItem = gBattleMons[ctx->battlerAtk].item;
|
||||
break;
|
||||
case EFFECT_POLTERGEIST:
|
||||
if (gFieldStatuses & STATUS_FIELD_MAGIC_ROOM)
|
||||
battleScript = BattleScript_ButItFailed;
|
||||
break;
|
||||
case EFFECT_FAIL_IF_NOT_ARG_TYPE:
|
||||
if (!IS_BATTLER_OF_TYPE(ctx->battlerAtk, GetMoveArgType(ctx->move)))
|
||||
battleScript = BattleScript_ButItFailed;
|
||||
|
|
@ -821,32 +1143,16 @@ static enum CancelerResult CancelerMoveFailure(struct BattleContext *ctx)
|
|||
if (!gBattleStruct->battlerState[ctx->battlerAtk].isFirstTurn || gSpecialStatuses[ctx->battlerAtk].instructedChosenTarget)
|
||||
battleScript = BattleScript_ButItFailed;
|
||||
break;
|
||||
case EFFECT_FLING:
|
||||
if (!CanFling(ctx->battlerAtk))
|
||||
battleScript = BattleScript_ButItFailed;
|
||||
else if (!IsBattlerAlive(ctx->battlerDef)) // Edge case for removing a mon's item when there is no target available after using Fling.
|
||||
battleScript = BattleScript_FlingFailConsumeItem;
|
||||
break;
|
||||
case EFFECT_FOLLOW_ME:
|
||||
if (B_UPDATED_MOVE_DATA >= GEN_8 && !(gBattleTypeFlags & BATTLE_TYPE_DOUBLE))
|
||||
battleScript = BattleScript_ButItFailed;
|
||||
break;
|
||||
case EFFECT_FUTURE_SIGHT:
|
||||
if (gBattleStruct->futureSight[ctx->battlerDef].counter > 0)
|
||||
battleScript = BattleScript_ButItFailed;
|
||||
break;
|
||||
case EFFECT_LAST_RESORT:
|
||||
if (!CanUseLastResort(ctx->battlerAtk))
|
||||
battleScript = BattleScript_ButItFailed;
|
||||
break;
|
||||
case EFFECT_NO_RETREAT:
|
||||
if (gBattleMons[ctx->battlerDef].volatiles.noRetreat)
|
||||
battleScript = BattleScript_ButItFailed;
|
||||
break;
|
||||
case EFFECT_POLTERGEIST:
|
||||
if (gBattleMons[ctx->battlerDef].item == ITEM_NONE
|
||||
|| gFieldStatuses & STATUS_FIELD_MAGIC_ROOM
|
||||
|| ctx->abilityDef == ABILITY_KLUTZ)
|
||||
if (gBattleMons[ctx->battlerAtk].volatiles.noRetreat)
|
||||
battleScript = BattleScript_ButItFailed;
|
||||
break;
|
||||
case EFFECT_PROTECT:
|
||||
|
|
@ -886,21 +1192,6 @@ static enum CancelerResult CancelerMoveFailure(struct BattleContext *ctx)
|
|||
|| ctx->abilityAtk == ABILITY_PURIFYING_SALT)
|
||||
battleScript = BattleScript_InsomniaProtects;
|
||||
break;
|
||||
case EFFECT_SUCKER_PUNCH:
|
||||
if (HasBattlerActedThisTurn(ctx->battlerDef)
|
||||
|| (IsBattleMoveStatus(GetBattlerChosenMove(ctx->battlerDef)) && !gProtectStructs[ctx->battlerDef].noValidMoves))
|
||||
battleScript = BattleScript_ButItFailed;
|
||||
break;
|
||||
case EFFECT_UPPER_HAND:
|
||||
{
|
||||
s32 prio = GetChosenMovePriority(ctx->battlerDef, GetBattlerAbility(ctx->battlerDef));
|
||||
if (prio < 1 || prio > 3 // Fails if priority is less than 1 or greater than 3, if target already moved, or if using a status
|
||||
|| HasBattlerActedThisTurn(ctx->battlerDef)
|
||||
|| gChosenMoveByBattler[ctx->battlerDef] == MOVE_NONE
|
||||
|| IsBattleMoveStatus(gChosenMoveByBattler[ctx->battlerDef]))
|
||||
battleScript = BattleScript_ButItFailed;
|
||||
break;
|
||||
}
|
||||
case EFFECT_SNORE:
|
||||
if (!(gBattleMons[ctx->battlerAtk].status1 & STATUS1_SLEEP)
|
||||
&& ctx->abilityAtk != ABILITY_COMATOSE)
|
||||
|
|
@ -926,11 +1217,6 @@ static enum CancelerResult CancelerMoveFailure(struct BattleContext *ctx)
|
|||
case EFFECT_TELEPORT:
|
||||
// TODO: follow up: Can't make sense of teleport logic
|
||||
break;
|
||||
case EFFECT_LOW_KICK:
|
||||
case EFFECT_HEAT_CRASH:
|
||||
if (GetActiveGimmick(ctx->battlerDef) == GIMMICK_DYNAMAX)
|
||||
battleScript = BattleScript_MoveBlockedByDynamax;
|
||||
break;
|
||||
case EFFECT_NATURAL_GIFT:
|
||||
if (GetItemPocket(gBattleMons[ctx->battlerAtk].item) != POCKET_BERRIES
|
||||
|| gFieldStatuses & STATUS_FIELD_MAGIC_ROOM
|
||||
|
|
@ -951,6 +1237,113 @@ static enum CancelerResult CancelerMoveFailure(struct BattleContext *ctx)
|
|||
return CANCELER_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static enum CancelerResult CancelerMoveEffectFailureTarget(struct BattleContext *ctx)
|
||||
{
|
||||
const u8 *battleScript = NULL;
|
||||
u32 numAffectedTargets = 0;
|
||||
|
||||
while (gBattleStruct->eventState.atkCancelerBattler < gBattlersCount)
|
||||
{
|
||||
enum BattlerId battlerDef = gBattleStruct->eventState.atkCancelerBattler++;
|
||||
|
||||
if (ShouldSkipFailureCheckOnBattler(ctx->battlerAtk, battlerDef, TRUE))
|
||||
continue;
|
||||
|
||||
switch (GetMoveEffect(ctx->move))
|
||||
{
|
||||
case EFFECT_FLING:
|
||||
if (!IsBattlerAlive(battlerDef)) // Edge case for removing a mon's item when there is no target available after using Fling.
|
||||
{
|
||||
battleScript = BattleScript_FlingFailConsumeItem;
|
||||
}
|
||||
else
|
||||
{
|
||||
numAffectedTargets++;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case EFFECT_FUTURE_SIGHT:
|
||||
if (gBattleStruct->futureSight[battlerDef].counter > 0)
|
||||
{
|
||||
battleScript = BattleScript_ButItFailed;
|
||||
}
|
||||
else
|
||||
{
|
||||
numAffectedTargets++;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case EFFECT_POLTERGEIST:
|
||||
if (gBattleMons[battlerDef].item == ITEM_NONE
|
||||
|| GetBattlerAbility(battlerDef) == ABILITY_KLUTZ)
|
||||
{
|
||||
battleScript = BattleScript_ButItFailed;
|
||||
}
|
||||
else
|
||||
{
|
||||
numAffectedTargets++;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case EFFECT_SUCKER_PUNCH:
|
||||
if (HasBattlerActedThisTurn(battlerDef)
|
||||
|| (IsBattleMoveStatus(GetBattlerChosenMove(battlerDef)) && !gProtectStructs[battlerDef].noValidMoves))
|
||||
{
|
||||
battleScript = BattleScript_ButItFailed;
|
||||
}
|
||||
else
|
||||
{
|
||||
numAffectedTargets++;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case EFFECT_UPPER_HAND:
|
||||
{
|
||||
s32 prio = GetChosenMovePriority(battlerDef, GetBattlerAbility(battlerDef));
|
||||
if (prio < 1 || prio > 3 // Fails if priority is less than 1 or greater than 3, if target already moved, or if using a status
|
||||
|| HasBattlerActedThisTurn(battlerDef)
|
||||
|| gChosenMoveByBattler[battlerDef] == MOVE_NONE
|
||||
|| IsBattleMoveStatus(gChosenMoveByBattler[battlerDef]))
|
||||
{
|
||||
battleScript = BattleScript_ButItFailed;
|
||||
}
|
||||
else
|
||||
{
|
||||
numAffectedTargets++;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EFFECT_LOW_KICK:
|
||||
case EFFECT_HEAT_CRASH:
|
||||
if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
|
||||
{
|
||||
battleScript = BattleScript_MoveBlockedByDynamax;
|
||||
}
|
||||
else
|
||||
{
|
||||
numAffectedTargets++;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
gBattleStruct->moveResultFlags[battlerDef] = MOVE_RESULT_FAILED;
|
||||
}
|
||||
|
||||
gBattleStruct->eventState.atkCancelerBattler = 0;
|
||||
|
||||
if (battleScript != NULL && numAffectedTargets == 0)
|
||||
{
|
||||
gBattlescriptCurrInstr = battleScript;
|
||||
return CANCELER_RESULT_FAILURE;
|
||||
}
|
||||
|
||||
return CANCELER_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
static enum CancelerResult CancelerPowderStatus(struct BattleContext *ctx)
|
||||
{
|
||||
if (TryActivatePowderStatus(ctx->move))
|
||||
|
|
@ -983,18 +1376,19 @@ static enum CancelerResult CancelerPriorityBlock(struct BattleContext *ctx)
|
|||
{
|
||||
bool32 effect = FALSE;
|
||||
s32 priority = GetChosenMovePriority(ctx->battlerAtk, ctx->abilityAtk);
|
||||
enum MoveTarget moveTarget = GetBattlerMoveTargetType(ctx->battlerAtk, ctx->move);
|
||||
|
||||
if (priority <= 0)
|
||||
if (priority <= 0 || moveTarget == TARGET_FIELD || moveTarget == TARGET_OPPONENTS_FIELD)
|
||||
return CANCELER_RESULT_SUCCESS;
|
||||
|
||||
enum BattlerId battler;
|
||||
enum Ability ability = ABILITY_NONE; // ability of battler who is blocking
|
||||
bool32 isSpreadMove = IsSpreadMove(GetBattlerMoveTargetType(ctx->battlerAtk, ctx->move));
|
||||
for (battler = 0; battler < gBattlersCount; battler++)
|
||||
{
|
||||
if (!IsBattlerAlive(battler) || IsBattlerAlly(ctx->battlerAtk, battler))
|
||||
continue;
|
||||
if (!isSpreadMove && !IsBattlerAlly(battler, ctx->battlerDef))
|
||||
if (ShouldSkipFailureCheckOnBattler(ctx->battlerAtk, battler, TRUE)
|
||||
&& (!IsDoubleBattle() || ShouldSkipFailureCheckOnBattler(ctx->battlerAtk, BATTLE_PARTNER(battler), TRUE))) // either battler or partner is affected
|
||||
continue;
|
||||
|
||||
ability = GetBattlerAbility(battler);
|
||||
|
|
@ -1050,7 +1444,7 @@ static enum CancelerResult CancelerExplosion(struct BattleContext *ctx)
|
|||
{
|
||||
// KO user of Explosion; for Final Gambit doesn't happen if target is immune or if it missed
|
||||
if (IsExplosionMove(ctx->move)
|
||||
&& (GetMoveEffect(ctx->move) != EFFECT_FINAL_GAMBIT || !IsBattlerUnaffectedByMove(ctx->battlerDef)))
|
||||
&& (GetMoveEffect(ctx->move) != EFFECT_FINAL_GAMBIT || IsAnyTargetAffected()))
|
||||
{
|
||||
BattleScriptCall(BattleScript_Explosion);
|
||||
return CANCELER_RESULT_BREAK;
|
||||
|
|
@ -1211,129 +1605,6 @@ static enum CancelerResult CancelerTookAttack(struct BattleContext *ctx)
|
|||
return CANCELER_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
#define checkFailure FALSE
|
||||
#define skipFailure TRUE
|
||||
static bool32 IsSingleTarget(enum BattlerId battlerAtk, enum BattlerId battlerDef)
|
||||
{
|
||||
if (battlerDef != gBattlerTarget)
|
||||
return skipFailure;
|
||||
return checkFailure;
|
||||
}
|
||||
|
||||
static bool32 IsSmartTarget(enum BattlerId battlerAtk, enum BattlerId battlerDef)
|
||||
{
|
||||
if (battlerAtk == BATTLE_PARTNER(battlerDef))
|
||||
return skipFailure;
|
||||
return checkFailure;
|
||||
}
|
||||
|
||||
static bool32 IsTargetingBothFoes(enum BattlerId battlerAtk, enum BattlerId battlerDef)
|
||||
{
|
||||
if (battlerDef == BATTLE_PARTNER(battlerAtk) || battlerAtk == battlerDef)
|
||||
{
|
||||
// Because of Magic Bounce and Magic Coat we don't want to set MOVE_RESULT_NO_EFFECT
|
||||
if (GetMoveCategory(gCurrentMove) != DAMAGE_CATEGORY_STATUS)
|
||||
gBattleStruct->moveResultFlags[battlerDef] = MOVE_RESULT_NO_EFFECT;
|
||||
return skipFailure;
|
||||
}
|
||||
return checkFailure;
|
||||
}
|
||||
|
||||
static bool32 IsTargetingSelf(enum BattlerId battlerAtk, enum BattlerId battlerDef)
|
||||
{
|
||||
return skipFailure;
|
||||
}
|
||||
|
||||
static bool32 IsTargetingAlly(enum BattlerId battlerAtk, enum BattlerId battlerDef)
|
||||
{
|
||||
if (battlerDef != BATTLE_PARTNER(battlerAtk))
|
||||
{
|
||||
gBattleStruct->moveResultFlags[battlerDef] = MOVE_RESULT_NO_EFFECT;
|
||||
return skipFailure;
|
||||
}
|
||||
return checkFailure;
|
||||
}
|
||||
|
||||
static bool32 IsTargetingSelfAndAlly(enum BattlerId battlerAtk, enum BattlerId battlerDef)
|
||||
{
|
||||
if (battlerDef != BATTLE_PARTNER(battlerAtk))
|
||||
{
|
||||
if (battlerDef != battlerAtk) // Don't set result flags for user
|
||||
gBattleStruct->moveResultFlags[battlerDef] = MOVE_RESULT_NO_EFFECT;
|
||||
return skipFailure;
|
||||
}
|
||||
return checkFailure;
|
||||
}
|
||||
|
||||
static bool32 IsTargetingSelfOrAlly(enum BattlerId battlerAtk, enum BattlerId battlerDef)
|
||||
{
|
||||
if (battlerDef == battlerAtk)
|
||||
return skipFailure;
|
||||
|
||||
if (battlerDef != BATTLE_PARTNER(battlerAtk))
|
||||
{
|
||||
gBattleStruct->moveResultFlags[battlerDef] = MOVE_RESULT_NO_EFFECT;
|
||||
return skipFailure;
|
||||
}
|
||||
|
||||
return checkFailure;
|
||||
}
|
||||
|
||||
static bool32 IsTargetingFoesAndAlly(enum BattlerId battlerAtk, enum BattlerId battlerDef)
|
||||
{
|
||||
if (battlerAtk == battlerDef)
|
||||
return skipFailure; // Don't set result flags for user
|
||||
return checkFailure;
|
||||
}
|
||||
|
||||
static bool32 IsTargetingField(enum BattlerId battlerAtk, enum BattlerId battlerDef)
|
||||
{
|
||||
return skipFailure;
|
||||
}
|
||||
|
||||
static bool32 IsTargetingOpponentsField(enum BattlerId battlerAtk, enum BattlerId battlerDef)
|
||||
{
|
||||
return checkFailure; // Bounce failure only
|
||||
}
|
||||
|
||||
static bool32 IsTargetingAllBattlers(enum BattlerId battlerAtk, enum BattlerId battlerDef)
|
||||
{
|
||||
if (GetConfig(CONFIG_CHECK_USER_FAILURE) >= GEN_5 && battlerAtk == battlerDef)
|
||||
return skipFailure;
|
||||
return checkFailure;
|
||||
}
|
||||
|
||||
static bool32 (*const sShouldCheckTargetMoveFailure[])(enum BattlerId battlerAtk, enum BattlerId battlerDef) =
|
||||
{
|
||||
[TARGET_NONE] = IsTargetingField,
|
||||
[TARGET_SELECTED] = IsSingleTarget,
|
||||
[TARGET_DEPENDS] = IsSingleTarget,
|
||||
[TARGET_OPPONENT] = IsSingleTarget,
|
||||
[TARGET_RANDOM] = IsSingleTarget,
|
||||
[TARGET_BOTH] = IsTargetingBothFoes,
|
||||
[TARGET_USER] = IsTargetingSelf,
|
||||
[TARGET_SMART] = IsSmartTarget,
|
||||
[TARGET_ALLY] = IsTargetingAlly,
|
||||
[TARGET_USER_AND_ALLY] = IsTargetingSelfAndAlly,
|
||||
[TARGET_USER_OR_ALLY] = IsTargetingSelfOrAlly,
|
||||
[TARGET_FOES_AND_ALLY] = IsTargetingFoesAndAlly,
|
||||
[TARGET_FIELD] = IsTargetingField,
|
||||
[TARGET_OPPONENTS_FIELD] = IsTargetingOpponentsField,
|
||||
[TARGET_ALL_BATTLERS] = IsTargetingAllBattlers,
|
||||
};
|
||||
|
||||
static bool32 ShouldCheckTargetMoveFailure(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum Move move, enum MoveTarget moveTarget)
|
||||
{
|
||||
// For Bounced moves
|
||||
if (IsBattlerUnaffectedByMove(battlerDef))
|
||||
return skipFailure;
|
||||
|
||||
return sShouldCheckTargetMoveFailure[moveTarget](battlerAtk, battlerDef);
|
||||
}
|
||||
#undef checkFailure
|
||||
#undef skipFailure
|
||||
|
||||
|
||||
static enum CancelerResult CancelerTargetFailure(struct BattleContext *ctx)
|
||||
{
|
||||
bool32 targetAvoidedAttack = FALSE;
|
||||
|
|
@ -1347,7 +1618,7 @@ static enum CancelerResult CancelerTargetFailure(struct BattleContext *ctx)
|
|||
{
|
||||
ctx->battlerDef = gBattleStruct->eventState.atkCancelerBattler++;
|
||||
|
||||
if (ShouldCheckTargetMoveFailure(ctx->battlerAtk, ctx->battlerDef, ctx->move, moveTarget))
|
||||
if (ShouldSkipFailureCheckOnBattler(ctx->battlerAtk, ctx->battlerDef, FALSE))
|
||||
continue;
|
||||
|
||||
ctx->abilityDef = GetBattlerAbility(ctx->battlerDef);
|
||||
|
|
@ -1360,7 +1631,12 @@ static enum CancelerResult CancelerTargetFailure(struct BattleContext *ctx)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!IsBattlerAlive(ctx->battlerDef))
|
||||
if (IsBattlerUnaffectedByMove(ctx->battlerDef)) // immune but targeted
|
||||
{
|
||||
BattleScriptCall(BattleScript_DoesntAffectScripting);
|
||||
targetAvoidedAttack = TRUE;
|
||||
}
|
||||
else if (!IsBattlerAlive(ctx->battlerDef))
|
||||
{
|
||||
gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_FAILED;
|
||||
continue;
|
||||
|
|
@ -1392,7 +1668,6 @@ static enum CancelerResult CancelerTargetFailure(struct BattleContext *ctx)
|
|||
}
|
||||
else if (CanMoveBeBlockedByTarget(ctx, movePriority))
|
||||
{
|
||||
|
||||
gBattleStruct->moveResultFlags[ctx->battlerDef] |= MOVE_RESULT_MISSED;
|
||||
targetAvoidedAttack = TRUE;
|
||||
}
|
||||
|
|
@ -1464,6 +1739,9 @@ static enum CancelerResult CancelerNotFullyProtected(struct BattleContext *ctx)
|
|||
{
|
||||
enum BattlerId battlerDef = gBattleStruct->eventState.atkCancelerBattler++;
|
||||
|
||||
if (ShouldSkipFailureCheckOnBattler(ctx->battlerAtk, battlerDef, TRUE))
|
||||
continue;
|
||||
|
||||
if (CantFullyProtectFromMove(battlerDef))
|
||||
{
|
||||
BattleScriptCall(BattleScript_CouldntFullyProtect);
|
||||
|
|
@ -1615,18 +1893,20 @@ static enum CancelerResult (*const sMoveSuccessOrderCancelers[])(struct BattleCo
|
|||
[CANCELER_GHOST] = CancelerGhost,
|
||||
[CANCELER_PARALYZED] = CancelerParalyzed,
|
||||
[CANCELER_INFATUATION] = CancelerInfatuation,
|
||||
[CANCELER_BIDE] = CancelerBide,
|
||||
[CANCELER_Z_MOVES] = CancelerZMoves,
|
||||
[CANCELER_CHOICE_LOCK] = CancelerChoiceLock,
|
||||
[CANCELER_CALLSUBMOVE] = CancelerCallSubmove,
|
||||
[CANCELER_THAW] = CancelerThaw,
|
||||
[CANCELER_STANCE_CHANGE_2] = CancelerStanceChangeTwo,
|
||||
[CANCELER_ATTACKSTRING] = CancelerAttackstring,
|
||||
[CANCELER_SET_TARGETS] = CancelerSetTargets,
|
||||
[CANCELER_PPDEDUCTION] = CancelerPPDeduction,
|
||||
[CANCELER_SKY_BATTLE] = CancelerSkyBattle,
|
||||
[CANCELER_WEATHER_PRIMAL] = CancelerWeatherPrimal,
|
||||
[CANCELER_FOCUS_PRE_GEN5] = CancelerFocusPreGen5,
|
||||
[CANCELER_BIDE] = CancelerBide,
|
||||
[CANCELER_MOVE_FAILURE] = CancelerMoveFailure,
|
||||
[CANCELER_MOVE_EFFECT_FAILURE_TARGET] = CancelerMoveEffectFailureTarget,
|
||||
[CANCELER_POWDER_STATUS] = CancelerPowderStatus,
|
||||
[CANCELER_PRIORITY_BLOCK] = CancelerPriorityBlock,
|
||||
[CANCELER_PROTEAN] = CancelerProtean,
|
||||
|
|
@ -2102,6 +2382,7 @@ static enum MoveEndResult MoveEndFaintBlock(void)
|
|||
break;
|
||||
case FAINT_BLOCK_CHECK_TARGET_FAINTED: // Stop if target already ran the block / is alive or absent
|
||||
if (IsBattlerAlive(gBattlerTarget)
|
||||
|| (gAbsentBattlerFlags & 1u << gBattlerTarget)
|
||||
|| gBattleStruct->battlerState[gBattlerTarget].fainted)
|
||||
{
|
||||
gBattleScripting.moveendState++;
|
||||
|
|
@ -2452,6 +2733,7 @@ static enum MoveEndResult MoveEndMultihitMove(void)
|
|||
&& gMultiHitCounter)
|
||||
{
|
||||
enum MoveTarget target = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove);
|
||||
gBattleStruct->preAttackEffectHappened = FALSE;
|
||||
gMultiHitCounter--;
|
||||
if (!IsBattlerAlive(gBattlerTarget) && target != TARGET_SMART)
|
||||
gMultiHitCounter = 0;
|
||||
|
|
@ -3273,7 +3555,7 @@ static bool32 ShouldSetStompingTantrumTimer(void)
|
|||
if (!IsDoubleSpreadMove())
|
||||
return gBattleStruct->moveResultFlags[gBattlerTarget] & (MOVE_RESULT_FAILED | MOVE_RESULT_DOESNT_AFFECT_FOE);
|
||||
|
||||
for (u32 battlerDef = 0; battlerDef < gBattlersCount; battlerDef++)
|
||||
for (enum BattlerId battlerDef = 0; battlerDef < gBattlersCount; battlerDef++)
|
||||
{
|
||||
if (gBattlerAttacker == battlerDef)
|
||||
continue;
|
||||
|
|
@ -3322,6 +3604,7 @@ static enum MoveEndResult MoveEndClearBits(void)
|
|||
gBattleStruct->fickleBeamBoosted = FALSE;
|
||||
gBattleStruct->battlerState[gBattlerAttacker].usedMicleBerry = FALSE;
|
||||
gBattleStruct->toxicChainPriority = FALSE;
|
||||
gBattleStruct->flingItem = ITEM_NONE;
|
||||
if (gBattleStruct->unableToUseMove)
|
||||
gBattleStruct->pledgeMove = FALSE;
|
||||
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE)
|
||||
|
|
|
|||
|
|
@ -352,6 +352,7 @@ static void AccuracyCheck(bool32 recalcDragonDarts, const u8 *nextInstr, const u
|
|||
static void ResetValuesForCalledMove(void);
|
||||
static bool32 CanAbilityShieldActivateForBattler(enum BattlerId battler);
|
||||
static void PlayAnimation(enum BattlerId battler, u8 animId, const u16 *argPtr, const u8 *nextInstr);
|
||||
static u32 GetPossibleNextTarget(u32 currTarget);
|
||||
|
||||
static void Cmd_attackcanceler(void);
|
||||
static void Cmd_accuracycheck(void);
|
||||
|
|
@ -939,6 +940,7 @@ bool32 ProteanTryChangeType(enum BattlerId battler, enum Ability ability, enum M
|
|||
{
|
||||
if ((ability == ABILITY_PROTEAN || ability == ABILITY_LIBERO)
|
||||
&& !gBattleMons[gBattlerAttacker].volatiles.usedProteanLibero
|
||||
&& !gBattleStruct->bouncedMoveIsUsed
|
||||
&& (gBattleMons[battler].types[0] != moveType || gBattleMons[battler].types[1] != moveType
|
||||
|| (gBattleMons[battler].types[2] != moveType && gBattleMons[battler].types[2] != TYPE_MYSTERY))
|
||||
&& move != MOVE_STRUGGLE
|
||||
|
|
@ -1561,7 +1563,7 @@ static void Cmd_attackanimation(void)
|
|||
moveResultFlags = UpdateEffectivenessResultFlagsForDoubleSpreadMoves(gBattleStruct->moveResultFlags[gBattlerTarget]);
|
||||
|
||||
bool32 isAnimDisabled = (gHitMarker & (HITMARKER_NO_ANIMATIONS | HITMARKER_DISABLE_ANIMATION)
|
||||
|| gBattleStruct->preAttackAnimPlayed);
|
||||
|| gBattleStruct->attackAnimPlayed);
|
||||
if (isAnimDisabled
|
||||
&& effect != EFFECT_TRANSFORM
|
||||
&& effect != EFFECT_SUBSTITUTE
|
||||
|
|
@ -3493,7 +3495,7 @@ void SetMoveEffect(enum BattlerId battlerAtk, enum BattlerId effectBattler, enum
|
|||
gSideStatuses[i] &= ~SIDE_STATUS_SCREEN_ANY;
|
||||
gBattleScripting.animTurn = 1;
|
||||
gBattleScripting.animTargetsHit = 1;
|
||||
gBattleStruct->preAttackAnimPlayed = TRUE; // The whole brick break animation is covered by the move so don't play twice
|
||||
gBattleStruct->attackAnimPlayed = TRUE; // The whole brick break animation is covered by the move so don't play twice
|
||||
BattleScriptPush(battleScript);
|
||||
gBattlescriptCurrInstr = BattleScript_BreakScreens;
|
||||
}
|
||||
|
|
@ -3547,7 +3549,6 @@ void SetMoveEffect(enum BattlerId battlerAtk, enum BattlerId effectBattler, enum
|
|||
|
||||
if (gBattleStruct->stolenStats[0] != 0)
|
||||
{
|
||||
gBattleStruct->preAttackAnimPlayed = FALSE; // if set by the previous move effect, reset it for final Spectral Thief anim
|
||||
BattleScriptPush(battleScript);
|
||||
gBattlescriptCurrInstr = BattleScript_StealStats;
|
||||
}
|
||||
|
|
@ -3584,27 +3585,54 @@ static void Cmd_setpreattackadditionaleffect(void)
|
|||
{
|
||||
CMD_ARGS();
|
||||
|
||||
u32 numAdditionalEffects = GetMoveAdditionalEffectCount(gCurrentMove);
|
||||
if (numAdditionalEffects > gBattleStruct->additionalEffectsCounter)
|
||||
if (gBattleStruct->preAttackEffectHappened)
|
||||
{
|
||||
const struct AdditionalEffect *additionalEffect = GetMoveAdditionalEffectById(gCurrentMove, gBattleStruct->additionalEffectsCounter);
|
||||
gBattleStruct->additionalEffectsCounter++;
|
||||
|
||||
if (!additionalEffect->preAttackEffect)
|
||||
return;
|
||||
|
||||
SetMoveEffect(
|
||||
gBattlerAttacker,
|
||||
additionalEffect->self ? gBattlerAttacker : gBattlerTarget,
|
||||
additionalEffect->moveEffect,
|
||||
gBattlescriptCurrInstr,
|
||||
EFFECT_PRIMARY
|
||||
);
|
||||
|
||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
return;
|
||||
}
|
||||
|
||||
while (gEffectBattler != MAX_BATTLERS_COUNT)
|
||||
{
|
||||
u32 numAdditionalEffects = GetMoveAdditionalEffectCount(gCurrentMove);
|
||||
if (numAdditionalEffects > gBattleStruct->additionalEffectsCounter)
|
||||
{
|
||||
const struct AdditionalEffect *additionalEffect = GetMoveAdditionalEffectById(gCurrentMove, gBattleStruct->additionalEffectsCounter);
|
||||
gBattleStruct->additionalEffectsCounter++;
|
||||
|
||||
if (!additionalEffect->preAttackEffect)
|
||||
return;
|
||||
|
||||
if ((gEffectBattler == gBattlerAttacker) != additionalEffect->self)
|
||||
return;
|
||||
|
||||
u32 percentChance = CalcSecondaryEffectChance(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker), additionalEffect);
|
||||
|
||||
// Activate effect if it's primary (chance == 0) or if RNGesus says so
|
||||
if ((percentChance == 0) || RandomPercentage(RNG_SECONDARY_EFFECT + gBattleStruct->additionalEffectsCounter, percentChance))
|
||||
{
|
||||
gBattleCommunication[MULTISTRING_CHOOSER] = *((u8 *) &additionalEffect->multistring);
|
||||
|
||||
enum SetMoveEffectFlags flags = NO_FLAGS;
|
||||
if (percentChance == 0) flags |= EFFECT_PRIMARY;
|
||||
if (percentChance >= 100) flags |= EFFECT_CERTAIN;
|
||||
|
||||
SetMoveEffect(
|
||||
gBattlerAttacker,
|
||||
gEffectBattler,
|
||||
additionalEffect->moveEffect,
|
||||
gBattlescriptCurrInstr,
|
||||
flags
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
gEffectBattler = GetPossibleNextTarget(gEffectBattler);
|
||||
gBattleStruct->additionalEffectsCounter = 0;
|
||||
}
|
||||
|
||||
gEffectBattler = gBattlerAttacker;
|
||||
gBattleStruct->additionalEffectsCounter = 0;
|
||||
gBattleStruct->preAttackEffectHappened = TRUE;
|
||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
}
|
||||
|
||||
|
|
@ -3681,13 +3709,11 @@ static void Cmd_setadditionaleffects(void)
|
|||
gBattlescriptCurrInstr,
|
||||
flags
|
||||
);
|
||||
|
||||
gBattleStruct->additionalEffectsCounter++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
gBattleStruct->additionalEffectsCounter++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -6263,7 +6289,6 @@ static void ResetValuesForCalledMove(void)
|
|||
gBattleScripting.animTurn = 0;
|
||||
gBattleScripting.animTargetsHit = 0;
|
||||
SetTypeBeforeUsingMove(gCurrentMove, gBattlerAttacker);
|
||||
DetermineTarget(GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove), FALSE);
|
||||
ClearDamageCalcResults();
|
||||
}
|
||||
|
||||
|
|
@ -6316,8 +6341,9 @@ static void Cmd_futuresighttargetfailure(void)
|
|||
}
|
||||
#undef DONE_TARGET_FAILURE
|
||||
|
||||
static u32 GetPossibleNextTarget(void)
|
||||
static u32 GetPossibleNextTarget(u32 currTarget)
|
||||
{
|
||||
u32 i = 0;
|
||||
const u8 targetOrder[MAX_BATTLERS_COUNT] = {
|
||||
gBattlerAttacker,
|
||||
BATTLE_PARTNER(gBattlerAttacker),
|
||||
|
|
@ -6325,9 +6351,20 @@ static u32 GetPossibleNextTarget(void)
|
|||
RIGHT_FOE(gBattlerAttacker),
|
||||
};
|
||||
|
||||
for (u32 i = 0; i < MAX_BATTLERS_COUNT; i++)
|
||||
// currTarget allows for a starting point without relying on values for previous targets being set
|
||||
if (currTarget != MAX_BATTLERS_COUNT)
|
||||
{
|
||||
enum BattlerId battler = targetOrder[i];
|
||||
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
|
||||
{
|
||||
if (targetOrder[i] == currTarget)
|
||||
break;
|
||||
}
|
||||
i++; // next target after finding currTarget
|
||||
}
|
||||
|
||||
while (i < MAX_BATTLERS_COUNT)
|
||||
{
|
||||
enum BattlerId battler = targetOrder[i++];
|
||||
|
||||
if (!IsBattlerAlive(battler))
|
||||
continue;
|
||||
|
|
@ -6344,7 +6381,7 @@ static void Cmd_getpossiblenexttarget(void)
|
|||
{
|
||||
CMD_ARGS(const u8 *jumpInstr);
|
||||
|
||||
u32 nextTarget = GetPossibleNextTarget();
|
||||
u32 nextTarget = GetPossibleNextTarget(MAX_BATTLERS_COUNT);
|
||||
if (nextTarget != MAX_BATTLERS_COUNT)
|
||||
{
|
||||
gBattleStruct->moveTarget[gBattlerAttacker] = gBattlerTarget = nextTarget;
|
||||
|
|
@ -6451,19 +6488,16 @@ static void Cmd_removeitem(void)
|
|||
{
|
||||
CMD_ARGS(u8 battler);
|
||||
|
||||
enum BattlerId battler;
|
||||
enum Item itemId = 0;
|
||||
enum BattlerId battler = GetBattlerForBattleScript(cmd->battler);
|
||||
enum Item itemId = gBattleMons[battler].item;
|
||||
|
||||
if (gBattleScripting.overrideBerryRequirements)
|
||||
if (gBattleScripting.overrideBerryRequirements || itemId == ITEM_NONE)
|
||||
{
|
||||
// bug bite / pluck - don't remove current item
|
||||
// bug bite / pluck / no item - don't remove current item
|
||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
return;
|
||||
}
|
||||
|
||||
battler = GetBattlerForBattleScript(cmd->battler);
|
||||
itemId = gBattleMons[battler].item;
|
||||
|
||||
// Popped Air Balloon cannot be restored by any means.
|
||||
// Corroded items cannot be restored either.
|
||||
if (GetBattlerHoldEffect(battler) != HOLD_EFFECT_AIR_BALLOON
|
||||
|
|
@ -12627,9 +12661,8 @@ void BS_SetMagicCoatTarget(void)
|
|||
gBattleStruct->attackerBeforeBounce = gBattleScripting.battler = gBattlerAttacker;
|
||||
gBattlerAttacker = gBattlerTarget;
|
||||
gBattlerTarget = gBattleStruct->attackerBeforeBounce;
|
||||
DetermineTarget(GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove), FALSE);
|
||||
ClearDamageCalcResults();
|
||||
gBattleStruct->eventState.atkCanceler = CANCELER_TARGET_FAILURE;
|
||||
gBattleStruct->eventState.atkCanceler = CANCELER_SET_TARGETS;
|
||||
gBattleStruct->eventState.atkCancelerBattler = 0;
|
||||
|
||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
|
|
@ -13381,7 +13414,6 @@ void BS_SetLastUsedItem(void)
|
|||
{
|
||||
NATIVE_ARGS(u8 battler);
|
||||
gLastUsedItem = gBattleMons[GetBattlerForBattleScript(cmd->battler)].item;
|
||||
gBattleStruct->flingItem = gLastUsedItem;
|
||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -368,175 +368,9 @@ static bool32 IsUnnerveAbilityOnOpposingSide(enum BattlerId battler)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
static inline bool32 IsDragonDartsSecondHit(enum BattlerId battlerAtk, enum Move move)
|
||||
{
|
||||
if (GetBattlerMoveTargetType(battlerAtk, move) != TARGET_SMART)
|
||||
return FALSE;
|
||||
|
||||
if (gMultiHitCounter < GetMoveStrikeCount(move))
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bool32 IsAffectedByFollowMe(enum BattlerId battlerAtk, enum BattleSide defSide, enum Move move)
|
||||
{
|
||||
enum Ability ability = GetBattlerAbility(battlerAtk);
|
||||
enum BattleMoveEffects effect = GetMoveEffect(move);
|
||||
|
||||
if (gSideTimers[defSide].followmeTimer == 0
|
||||
|| (!IsBattlerAlive(gSideTimers[defSide].followmeTarget) && !IsDragonDartsSecondHit(battlerAtk, move))
|
||||
|| effect == EFFECT_SNIPE_SHOT
|
||||
|| effect == EFFECT_SKY_DROP
|
||||
|| IsAbilityAndRecord(battlerAtk, ability, ABILITY_PROPELLER_TAIL)
|
||||
|| IsAbilityAndRecord(battlerAtk, ability, ABILITY_STALWART))
|
||||
return FALSE;
|
||||
|
||||
if (effect == EFFECT_PURSUIT && IsPursuitTargetSet())
|
||||
return FALSE;
|
||||
|
||||
if (gSideTimers[defSide].followmePowder && !IsAffectedByPowderMove(battlerAtk, ability, GetBattlerHoldEffect(battlerAtk)))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static bool32 HandleMoveTargetRedirection(void)
|
||||
{
|
||||
u32 redirectorOrderNum = MAX_BATTLERS_COUNT;
|
||||
enum MoveTarget moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove);
|
||||
enum BattleMoveEffects moveEffect = GetMoveEffect(gCurrentMove);
|
||||
enum BattleSide side = BATTLE_OPPOSITE(GetBattlerSide(gBattlerAttacker));
|
||||
|
||||
if (moveEffect == EFFECT_REFLECT_DAMAGE)
|
||||
{
|
||||
enum DamageCategory reflectCategory = GetReflectDamageMoveDamageCategory(gBattlerAttacker, gCurrentMove);
|
||||
|
||||
if (reflectCategory == DAMAGE_CATEGORY_PHYSICAL)
|
||||
gBattleStruct->moveTarget[gBattlerAttacker] = gProtectStructs[gBattlerAttacker].physicalBattlerId;
|
||||
else
|
||||
gBattleStruct->moveTarget[gBattlerAttacker] = gProtectStructs[gBattlerAttacker].specialBattlerId;
|
||||
}
|
||||
|
||||
if (IsAffectedByFollowMe(gBattlerAttacker, side, gCurrentMove)
|
||||
&& (moveTarget == TARGET_SELECTED || moveTarget == TARGET_SMART || moveEffect == EFFECT_REFLECT_DAMAGE)
|
||||
&& !IsBattlerAlly(gBattlerAttacker, gSideTimers[side].followmeTarget))
|
||||
{
|
||||
gBattleStruct->moveTarget[gBattlerAttacker] = gBattlerTarget = gSideTimers[side].followmeTarget; // follow me moxie fix
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
enum Type moveType = GetBattleMoveType(gCurrentMove);
|
||||
enum Ability ability = GetBattlerAbility(gBattleStruct->moveTarget[gBattlerAttacker]);
|
||||
bool32 currTargetCantAbsorb = ((ability != ABILITY_LIGHTNING_ROD && moveType == TYPE_ELECTRIC)
|
||||
|| (ability != ABILITY_STORM_DRAIN && moveType == TYPE_WATER));
|
||||
|
||||
if (currTargetCantAbsorb
|
||||
&& IsDoubleBattle()
|
||||
&& gSideTimers[side].followmeTimer == 0
|
||||
&& moveTarget != TARGET_USER
|
||||
&& moveTarget != TARGET_ALL_BATTLERS
|
||||
&& moveTarget != TARGET_FIELD)
|
||||
{
|
||||
// Find first battler that redirects the move (in turn order)
|
||||
enum Ability abilityAtk = GetBattlerAbility(gBattlerAttacker);
|
||||
enum BattlerId battler;
|
||||
for (battler = 0; battler < gBattlersCount; battler++)
|
||||
{
|
||||
ability = GetBattlerAbility(battler);
|
||||
if ((B_REDIRECT_ABILITY_ALLIES >= GEN_4 || !IsBattlerAlly(gBattlerAttacker, battler))
|
||||
&& battler != gBattlerAttacker
|
||||
&& gBattleStruct->moveTarget[gBattlerAttacker] != battler
|
||||
&& ((ability == ABILITY_LIGHTNING_ROD && moveType == TYPE_ELECTRIC)
|
||||
|| (ability == ABILITY_STORM_DRAIN && moveType == TYPE_WATER))
|
||||
&& GetBattlerTurnOrderNum(battler) < redirectorOrderNum
|
||||
&& moveEffect != EFFECT_SNIPE_SHOT
|
||||
&& moveEffect != EFFECT_PLEDGE
|
||||
&& !IsAbilityAndRecord(gBattlerAttacker, abilityAtk, ABILITY_PROPELLER_TAIL)
|
||||
&& !IsAbilityAndRecord(gBattlerAttacker, abilityAtk, ABILITY_STALWART))
|
||||
{
|
||||
redirectorOrderNum = GetBattlerTurnOrderNum(battler);
|
||||
}
|
||||
}
|
||||
if (redirectorOrderNum != MAX_BATTLERS_COUNT && moveEffect != EFFECT_TEATIME)
|
||||
{
|
||||
enum Ability battlerAbility;
|
||||
battler = gBattlerByTurnOrder[redirectorOrderNum];
|
||||
battlerAbility = GetBattlerAbility(battler);
|
||||
RecordAbilityBattle(battler, battlerAbility);
|
||||
gSpecialStatuses[battler].abilityRedirected = TRUE;
|
||||
gBattlerTarget = battler;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void DetermineTarget(enum MoveTarget moveTarget, bool32 overwriteTarget)
|
||||
{
|
||||
if (HandleMoveTargetRedirection())
|
||||
return;
|
||||
|
||||
if (IsDoubleBattle() && moveTarget == TARGET_RANDOM)
|
||||
{
|
||||
gBattlerTarget = SetRandomTarget(gBattlerAttacker);
|
||||
if (gAbsentBattlerFlags & (1u << gBattlerTarget)
|
||||
&& !IsBattlerAlly(gBattlerAttacker, gBattlerTarget))
|
||||
{
|
||||
gBattlerTarget = GetPartnerBattler(gBattlerTarget);
|
||||
}
|
||||
}
|
||||
else if (moveTarget == TARGET_ALLY)
|
||||
{
|
||||
if (IsBattlerAlive(BATTLE_PARTNER(gBattlerAttacker)) && !gProtectStructs[BATTLE_PARTNER(gBattlerAttacker)].usedAllySwitch)
|
||||
gBattlerTarget = BATTLE_PARTNER(gBattlerAttacker);
|
||||
else
|
||||
gBattlerTarget = gBattlerAttacker;
|
||||
}
|
||||
else if (IsDoubleBattle() && moveTarget == TARGET_FOES_AND_ALLY)
|
||||
{
|
||||
for (gBattlerTarget = 0; gBattlerTarget < gBattlersCount; gBattlerTarget++)
|
||||
{
|
||||
if (gBattlerTarget == gBattlerAttacker)
|
||||
continue;
|
||||
if (IsBattlerAlive(gBattlerTarget))
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (moveTarget == TARGET_USER || moveTarget == TARGET_USER_AND_ALLY)
|
||||
{
|
||||
gBattlerTarget = gBattlerAttacker;
|
||||
}
|
||||
else if (overwriteTarget)
|
||||
{
|
||||
gBattlerTarget = gBattleStruct->moveTarget[gBattlerAttacker];
|
||||
if (!IsBattlerAlive(gBattlerTarget)
|
||||
&& moveTarget != TARGET_OPPONENTS_FIELD
|
||||
&& IsDoubleBattle()
|
||||
&& (!IsBattlerAlly(gBattlerAttacker, gBattlerTarget)))
|
||||
{
|
||||
gBattlerTarget = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gBattlerTarget)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool32 WasOriginalTargetAlly(enum MoveTarget target)
|
||||
{
|
||||
if (!gProtectStructs[BATTLE_PARTNER(gBattlerAttacker)].usedAllySwitch)
|
||||
return FALSE;
|
||||
|
||||
if ((target == TARGET_ALLY || target == TARGET_USER_OR_ALLY) && gBattlerAttacker == gBattlerTarget)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Functions
|
||||
void HandleAction_UseMove(void)
|
||||
{
|
||||
u32 i;
|
||||
enum MoveTarget moveTarget;
|
||||
|
||||
gBattlerAttacker = gBattlerByTurnOrder[gCurrentTurnActionNumber];
|
||||
if (gAbsentBattlerFlags & 1u << gBattlerAttacker
|
||||
|| gBattleStruct->battlerState[gBattlerAttacker].commandingDondozo
|
||||
|
|
@ -617,10 +451,10 @@ void HandleAction_UseMove(void)
|
|||
ClearDamageCalcResults();
|
||||
gMultiHitCounter = 0;
|
||||
gBattleCommunication[MISS_TYPE] = 0;
|
||||
|
||||
moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove);
|
||||
DetermineTarget(moveTarget, TRUE);
|
||||
|
||||
gBattlerTarget = gBattleStruct->moveTarget[gBattlerAttacker];
|
||||
// Avoid assert errors before failure script is played (for ally targeting moves)
|
||||
if (!IsDoubleBattle() && gBattlerTarget >= gBattlersCount)
|
||||
gBattlerTarget = BATTLE_PARTNER(gBattlerTarget);
|
||||
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_PALACE && gProtectStructs[gBattlerAttacker].palaceUnableToUseMove)
|
||||
{
|
||||
|
|
@ -642,14 +476,6 @@ void HandleAction_UseMove(void)
|
|||
gBattlescriptCurrInstr = BattleScript_MoveUsedLoafingAround;
|
||||
}
|
||||
}
|
||||
else if (IsBattlerAlly(gBattlerAttacker, gBattlerTarget) && !IsBattlerAlive(gBattlerTarget))
|
||||
{
|
||||
gBattlescriptCurrInstr = BattleScript_FailedFromAtkCanceler;
|
||||
}
|
||||
else if (WasOriginalTargetAlly(moveTarget))
|
||||
{
|
||||
gBattlescriptCurrInstr = BattleScript_FailedFromAtkCanceler;
|
||||
}
|
||||
else
|
||||
{
|
||||
gBattlescriptCurrInstr = GetMoveBattleScript(gCurrentMove);
|
||||
|
|
@ -658,10 +484,10 @@ void HandleAction_UseMove(void)
|
|||
if (gBattleTypeFlags & BATTLE_TYPE_ARENA)
|
||||
BattleArena_AddMindPoints(gBattlerAttacker);
|
||||
|
||||
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
|
||||
for (enum BattlerId battler = 0; battler < gBattlersCount; battler++)
|
||||
{
|
||||
gBattleStruct->battlerState[i].wasAboveHalfHp = gBattleMons[i].hp > gBattleMons[i].maxHP / 2;
|
||||
gBattleMons[i].volatiles.activateDancer = FALSE;
|
||||
gBattleStruct->battlerState[battler].wasAboveHalfHp = gBattleMons[battler].hp > gBattleMons[battler].maxHP / 2;
|
||||
gBattleMons[battler].volatiles.activateDancer = FALSE;
|
||||
}
|
||||
|
||||
gCurrentActionFuncId = B_ACTION_EXEC_SCRIPT;
|
||||
|
|
@ -5760,7 +5586,6 @@ u32 GetBattleMoveTarget(enum Move move, enum MoveTarget moveTarget)
|
|||
{
|
||||
u32 targetBattler = 0;
|
||||
enum BattleSide side;
|
||||
enum Type moveType = GetBattleMoveType(move);
|
||||
|
||||
if (moveTarget == TARGET_NONE)
|
||||
moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, move);
|
||||
|
|
@ -5770,45 +5595,12 @@ u32 GetBattleMoveTarget(enum Move move, enum MoveTarget moveTarget)
|
|||
case TARGET_SELECTED:
|
||||
case TARGET_SMART:
|
||||
case TARGET_OPPONENT:
|
||||
case TARGET_RANDOM:
|
||||
side = BATTLE_OPPOSITE(GetBattlerSide(gBattlerAttacker));
|
||||
if (IsAffectedByFollowMe(gBattlerAttacker, side, move))
|
||||
{
|
||||
targetBattler = gSideTimers[side].followmeTarget;
|
||||
}
|
||||
else
|
||||
{
|
||||
enum Ability battlerAbilityOnField = 0;
|
||||
|
||||
targetBattler = SetRandomTarget(gBattlerAttacker);
|
||||
if (moveType == TYPE_ELECTRIC && GetBattlerAbility(targetBattler) != ABILITY_LIGHTNING_ROD)
|
||||
{
|
||||
if (B_REDIRECT_ABILITY_ALLIES >= GEN_4)
|
||||
battlerAbilityOnField = IsAbilityOnField(ABILITY_LIGHTNING_ROD);
|
||||
else
|
||||
battlerAbilityOnField = IsAbilityOnOpposingSide(targetBattler, ABILITY_LIGHTNING_ROD);
|
||||
|
||||
if (battlerAbilityOnField > 0 && (battlerAbilityOnField - 1) != gBattlerAttacker)
|
||||
{
|
||||
targetBattler = battlerAbilityOnField - 1;
|
||||
RecordAbilityBattle(targetBattler, gBattleMons[targetBattler].ability);
|
||||
gSpecialStatuses[targetBattler].abilityRedirected = TRUE;
|
||||
}
|
||||
}
|
||||
else if (moveType == TYPE_WATER && GetBattlerAbility(targetBattler) != ABILITY_STORM_DRAIN)
|
||||
{
|
||||
if (B_REDIRECT_ABILITY_ALLIES >= GEN_4)
|
||||
battlerAbilityOnField = IsAbilityOnField(ABILITY_STORM_DRAIN);
|
||||
else
|
||||
battlerAbilityOnField = IsAbilityOnOpposingSide(targetBattler, ABILITY_STORM_DRAIN);
|
||||
|
||||
if (battlerAbilityOnField > 0 && (battlerAbilityOnField - 1) != gBattlerAttacker)
|
||||
{
|
||||
targetBattler = battlerAbilityOnField - 1;
|
||||
RecordAbilityBattle(targetBattler, gBattleMons[targetBattler].ability);
|
||||
gSpecialStatuses[targetBattler].abilityRedirected = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TARGET_DEPENDS:
|
||||
case TARGET_BOTH:
|
||||
|
|
@ -5820,15 +5612,6 @@ u32 GetBattleMoveTarget(enum Move move, enum MoveTarget moveTarget)
|
|||
case TARGET_OPPONENTS_FIELD:
|
||||
targetBattler = GetOpposingSideBattler(gBattlerAttacker);
|
||||
break;
|
||||
case TARGET_RANDOM:
|
||||
side = BATTLE_OPPOSITE(GetBattlerSide(gBattlerAttacker));
|
||||
if (IsAffectedByFollowMe(gBattlerAttacker, side, move))
|
||||
targetBattler = gSideTimers[side].followmeTarget;
|
||||
else if (IsDoubleBattle())
|
||||
targetBattler = SetRandomTarget(gBattlerAttacker);
|
||||
else
|
||||
targetBattler = GetOpposingSideBattler(gBattlerAttacker);
|
||||
break;
|
||||
case TARGET_USER:
|
||||
default:
|
||||
targetBattler = gBattlerAttacker;
|
||||
|
|
@ -10077,7 +9860,8 @@ void ClearDamageCalcResults(void)
|
|||
gBattleStruct->printedStrongWindsWeakenedAttack = FALSE;
|
||||
gBattleStruct->numSpreadTargets = 0;
|
||||
gBattleStruct->unableToUseMove = FALSE;
|
||||
gBattleStruct->preAttackAnimPlayed = FALSE;
|
||||
gBattleStruct->attackAnimPlayed = FALSE;
|
||||
gBattleStruct->preAttackEffectHappened = FALSE;
|
||||
gBattleScripting.savedDmg = 0;
|
||||
if (gCurrentMove != MOVE_NONE)
|
||||
gBattleStruct->moldBreakerActive = IsMoldBreakerTypeAbility(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker)) || MoveIgnoresTargetAbility(gCurrentMove);
|
||||
|
|
|
|||
|
|
@ -6809,6 +6809,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
|
|||
.additionalEffects = ADDITIONAL_EFFECTS({
|
||||
.moveEffect = MOVE_EFFECT_BEAT_UP_MESSAGE,
|
||||
.preAttackEffect = TRUE,
|
||||
.self = TRUE,
|
||||
}),
|
||||
.contestEffect = C_UPDATED_MOVE_EFFECTS >= GEN_6 ? CONTEST_EFFECT_BETTER_WITH_GOOD_CONDITION : CONTEST_EFFECT_BADLY_STARTLE_MONS_WITH_GOOD_APPEALS,
|
||||
.contestCategory = CONTEST_CATEGORY_SMART,
|
||||
|
|
|
|||
|
|
@ -122,8 +122,7 @@ SINGLE_BATTLE_TEST("Dazzling, Queenly Majesty and Armor Tail prevent Protean act
|
|||
}
|
||||
}
|
||||
|
||||
// Listed on Bulbapedia as "Moves that target all Pokémon (except Perish Song, Flower Shield, and Rototiller),"
|
||||
// Despite the fact that there's only 2 remaining moves from that list, being Haze and Teatime
|
||||
// Moves that target the field don't get blocked
|
||||
SINGLE_BATTLE_TEST("Dazzling, Queenly Majesty and Armor Tail do not block Haze")
|
||||
{
|
||||
u32 species;
|
||||
|
|
@ -135,6 +134,7 @@ SINGLE_BATTLE_TEST("Dazzling, Queenly Majesty and Armor Tail do not block Haze")
|
|||
|
||||
GIVEN {
|
||||
ASSUME(GetMoveEffect(MOVE_HAZE) == EFFECT_HAZE);
|
||||
ASSUME(GetMoveTarget(MOVE_HAZE) == TARGET_FIELD);
|
||||
PLAYER(SPECIES_MURKROW) { Ability(ABILITY_PRANKSTER); }
|
||||
OPPONENT(species) { Ability(ability); }
|
||||
} WHEN {
|
||||
|
|
@ -143,14 +143,15 @@ SINGLE_BATTLE_TEST("Dazzling, Queenly Majesty and Armor Tail do not block Haze")
|
|||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SWORDS_DANCE, player);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_HAZE, player);
|
||||
NOT ABILITY_POPUP(opponent, ability);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_HAZE, player);
|
||||
} THEN {
|
||||
EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Dazzling, Queenly Majesty and Armor Tail do not block Teatime")
|
||||
// Moves that target all battlers are blocked
|
||||
SINGLE_BATTLE_TEST("Dazzling, Queenly Majesty and Armor Tail on opponents block Teatime")
|
||||
{
|
||||
u32 species;
|
||||
enum Ability ability;
|
||||
|
|
@ -161,17 +162,18 @@ SINGLE_BATTLE_TEST("Dazzling, Queenly Majesty and Armor Tail do not block Teatim
|
|||
|
||||
GIVEN {
|
||||
ASSUME(GetMoveEffect(MOVE_TEATIME) == EFFECT_TEATIME);
|
||||
ASSUME(GetMoveTarget(MOVE_TEATIME) == TARGET_ALL_BATTLERS);
|
||||
ASSUME(GetItemHoldEffect(ITEM_ORAN_BERRY) == HOLD_EFFECT_RESTORE_HP);
|
||||
PLAYER(SPECIES_MURKROW) { Ability(ABILITY_PRANKSTER); Item(ITEM_ORAN_BERRY); HP(75); MaxHP(100); }
|
||||
OPPONENT(species) { Ability(ability); Item(ITEM_ORAN_BERRY); HP(75); MaxHP(100); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TEATIME); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TEATIME, player);
|
||||
NOT ABILITY_POPUP(opponent, ability);
|
||||
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_TEATIME, player);
|
||||
ABILITY_POPUP(opponent, ability);
|
||||
} THEN {
|
||||
EXPECT_EQ(player->item, ITEM_NONE);
|
||||
EXPECT_EQ(opponent->item, ITEM_NONE);
|
||||
EXPECT_NE(player->item, ITEM_NONE);
|
||||
EXPECT_NE(opponent->item, ITEM_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -74,10 +74,10 @@ DOUBLE_BATTLE_TEST("Magic Coat reflects hazards regardless of the user's positio
|
|||
struct BattlePokemon *coatUser = NULL;
|
||||
PARAMETRIZE { coatUser = playerLeft; }
|
||||
PARAMETRIZE { coatUser = playerRight; }
|
||||
ASSUME(GetMoveEffect(MOVE_SPIKES) == EFFECT_SPIKES);
|
||||
ASSUME(GetMoveEffect(MOVE_STEALTH_ROCK) == EFFECT_STEALTH_ROCK);
|
||||
|
||||
GIVEN {
|
||||
ASSUME(GetMoveEffect(MOVE_SPIKES) == EFFECT_SPIKES);
|
||||
ASSUME(GetMoveEffect(MOVE_STEALTH_ROCK) == EFFECT_STEALTH_ROCK);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
|
|
@ -97,3 +97,33 @@ DOUBLE_BATTLE_TEST("Magic Coat reflects hazards regardless of the user's positio
|
|||
EXPECT(IsHazardOnSide(B_SIDE_OPPONENT, HAZARDS_SPIKES));
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Magic Coat reflection doesn't activate Protean/Libero")
|
||||
{
|
||||
u32 species, ability;
|
||||
|
||||
PARAMETRIZE { species = SPECIES_GRENINJA; ability = ABILITY_PROTEAN; }
|
||||
PARAMETRIZE { species = SPECIES_CINDERACE; ability = ABILITY_LIBERO; }
|
||||
|
||||
GIVEN {
|
||||
ASSUME(GetMoveEffect(MOVE_SPIKES) == EFFECT_SPIKES);
|
||||
ASSUME(GetMoveType(MOVE_MAGIC_COAT) == TYPE_PSYCHIC);
|
||||
ASSUME(GetMoveType(MOVE_SPIKES) != TYPE_PSYCHIC);
|
||||
PLAYER(species) { Ability(ability); }
|
||||
OPPONENT(SPECIES_ZIGZAGOON);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_MAGIC_COAT); MOVE(opponent, MOVE_SPIKES); }
|
||||
} SCENE {
|
||||
ABILITY_POPUP(player, ability);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_MAGIC_COAT, player);
|
||||
ONE_OF {
|
||||
MESSAGE("Greninja bounced the Spikes back!");
|
||||
MESSAGE("Cinderace bounced the Spikes back!");
|
||||
}
|
||||
NOT ABILITY_POPUP(player, ability);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPIKES, player);
|
||||
} THEN {
|
||||
EXPECT_EQ(player->types[0], TYPE_PSYCHIC);
|
||||
EXPECT_EQ(player->types[1], TYPE_PSYCHIC);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user