Refactor synchronize and cure berry timing (#9446)
Some checks are pending
CI / build (push) Waiting to run
CI / docs_validate (push) Waiting to run
CI / allcontributors (push) Waiting to run

This commit is contained in:
Alex 2026-03-07 16:38:53 +01:00 committed by GitHub
parent 138a8f90c6
commit 0c20d91508
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 183 additions and 180 deletions

View File

@ -218,20 +218,6 @@
.4byte \jumpInstr
.endm
.macro movevaluescleanup
.byte B_SCR_OP_MOVEVALUESCLEANUP
.endm
.macro setmultihit value:req
.byte B_SCR_OP_SETMULTIHIT
.byte \value
.endm
.macro decrementmultihit loopInstr:req
.byte B_SCR_OP_DECREMENTMULTIHIT
.4byte \loopInstr
.endm
.macro goto instr:req
.byte B_SCR_OP_GOTO
.4byte \instr
@ -1214,6 +1200,10 @@
.4byte \failInstr
.endm
.macro trysynchronize
.byte B_SCR_OP_TRY_SYNCHRONIZE
.endm
.macro callnative func:req
.byte B_SCR_OP_CALLNATIVE
.4byte \func

View File

@ -415,7 +415,6 @@ BattleScript_EffectTeatime::
checkteatimetargets BattleScript_ButItFailed
attackanimation
waitanimation
movevaluescleanup
goto BattleScript_EffectTeatimeGetTarget
BattleScript_EffectTeatimeNextTarget:
jumpifnoberry BS_TARGET, BattleScript_EffectTeatimeGetTarget
@ -1136,7 +1135,6 @@ BattleScript_FlowerShieldCheckNextTarget:
BattleScript_FlowerShieldLoopStart:
selectfirstvalidtarget
BattleScript_FlowerShieldLoop:
movevaluescleanup
jumpifvolatile BS_TARGET, VOLATILE_SEMI_INVULNERABLE, BattleScript_FlowerShieldMoveTargetEnd
jumpiftype BS_TARGET, TYPE_GRASS, BattleScript_FlowerShieldLoop2
goto BattleScript_FlowerShieldMoveTargetEnd
@ -2119,7 +2117,6 @@ BattleScript_EffectGravitySuccess::
savetarget
selectfirstvalidtarget
BattleScript_GravityLoop:
movevaluescleanup
jumpfifsemiinvulnerable BS_TARGET, STATE_ON_AIR, BattleScript_GravityLoopDrop
jumpifvolatile BS_TARGET, VOLATILE_MAGNET_RISE, BattleScript_GravityLoopDrop
jumpifvolatile BS_TARGET, VOLATILE_TELEKINESIS, BattleScript_GravityLoopDrop
@ -5828,6 +5825,8 @@ BattleScript_UpdateEffectStatusIconRet::
updatestatusicon BS_EFFECT_BATTLER
waitstate
trytriggerstatusform
trysynchronize
tryactivateitem BS_EFFECT_BATTLER, ACTIVATION_ON_STATUS_CHANGE
flushtextbox
return

View File

@ -595,13 +595,12 @@ struct BattleStruct
u32 savedBattleTypeFlags;
u16 abilityPreventingSwitchout;
u8 hpScale;
u16 synchronizeMoveEffect;
u8 anyMonHasTransformed:1; // Only used in battle_tv.c
u8 sleepClauseNotBlocked:1;
u8 isSkyBattle:1;
u8 unableToUseMove:1; // for the current action only, to check if the battler failed to act at end turn use the DisableStruct member
u8 triAttackBurn:1;
u8 unused:3;
enum SynchronizeState synchronizeState:3;
void (*savedCallback)(void);
u16 chosenItem[MAX_BATTLERS_COUNT];
u16 choicedMove[MAX_BATTLERS_COUNT];

View File

@ -5,6 +5,5 @@
enum CancelerResult DoAttackCanceler(void);
enum MoveEndResult DoMoveEnd(enum MoveEndState endMode, enum MoveEndState endState);
void MoveValuesCleanUp(void);
#endif // GUARD_BATTLE_MOVE_RESOLUTION_H

View File

@ -46,8 +46,6 @@ enum AbilityEffect
ABILITYEFFECT_COLOR_CHANGE, // Color Change / Berserk / Anger Shell
ABILITYEFFECT_MOVE_END,
ABILITYEFFECT_IMMUNITY,
ABILITYEFFECT_SYNCHRONIZE,
ABILITYEFFECT_ATK_SYNCHRONIZE,
ABILITYEFFECT_FORM_CHANGE_ON_HIT,
ABILITYEFFECT_DANCER,
ABILITYEFFECT_MOVE_END_FOES_FAINTED, // Moxie-like abilities / Battle Bond / Magician

View File

@ -83,13 +83,11 @@ enum MoveEndState
MOVEEND_PROTECT_LIKE_EFFECT,
MOVEEND_ABSORB,
MOVEEND_RAGE,
MOVEEND_SYNCHRONIZE_TARGET,
MOVEEND_ABILITIES,
MOVEEND_FORM_CHANGE_ON_HIT, // Disguise / Gulp Missile
MOVEEND_ABILITIES_ATTACKER,
MOVEEND_QUEUE_DANCER,
MOVEEND_STATUS_IMMUNITY_ABILITIES, // TODO: Do berries come before????
MOVEEND_SYNCHRONIZE_ATTACKER,
MOVEEND_ATTACKER_INVISIBLE,
MOVEEND_ATTACKER_VISIBLE,
MOVEEND_TARGET_VISIBLE,

View File

@ -39,9 +39,6 @@ enum BattleScriptOpcode
B_SCR_OP_JUMPBASEDONTYPE,
B_SCR_OP_GETEXP,
B_SCR_OP_CHECKTEAMSLOST,
B_SCR_OP_MOVEVALUESCLEANUP,
B_SCR_OP_SETMULTIHIT,
B_SCR_OP_DECREMENTMULTIHIT,
B_SCR_OP_GOTO,
B_SCR_OP_JUMPIFBYTE,
B_SCR_OP_JUMPIFHALFWORD,
@ -228,6 +225,7 @@ enum BattleScriptOpcode
B_SCR_OP_JUMPIFCAPTIVATEAFFECTED,
B_SCR_OP_SETNONVOLATILESTATUS,
B_SCR_OP_TRYOVERWRITEABILITY,
B_SCR_OP_TRY_SYNCHRONIZE,
// Expansion users, please don't use any of the unused commands.
// They are reserved for expansion usage.
@ -262,6 +260,8 @@ enum BattleScriptOpcode
B_SCR_OP_UNUSED_28,
B_SCR_OP_UNUSED_29,
B_SCR_OP_UNUSED_30,
B_SCR_OP_UNUSED_31,
B_SCR_OP_UNUSED_32,
B_SCR_OP_CALLNATIVE,
};
@ -430,4 +430,13 @@ enum FlungItem
FLUNG_ITEM_REMOVED,
};
enum SynchronizeState
{
SYNCH_STATE_NONE,
SYNCH_STATE_START,
SYNCH_STATE_SET_STATUS,
SYNCH_STATE_SHOW_ABILITY_POPUP,
SYNCH_STATE_END,
};
#endif // GUARD_CONSTANTS_BATTLE_SCRIPT_COMMANDS_H

View File

@ -2244,15 +2244,6 @@ static enum MoveEndResult MoveEndRage(void)
return result;
}
static enum MoveEndResult MoveEndSynchronizeTarget(void)
{
enum MoveEndResult result = MOVEEND_RESULT_CONTINUE;
if (AbilityBattleEffects(ABILITYEFFECT_SYNCHRONIZE, gBattlerTarget, 0, 0, TRUE))
result = MOVEEND_RESULT_RUN_SCRIPT;
gBattleScripting.moveendState++;
return result;
}
static enum MoveEndResult MoveEndAbilities(void)
{
enum MoveEndResult result = MOVEEND_RESULT_CONTINUE;
@ -2330,17 +2321,6 @@ static enum MoveEndResult MoveEndStatusImmunityAbilities(void)
return result;
}
static enum MoveEndResult MoveEndSynchronizeAttacker(void)
{
enum MoveEndResult result = MOVEEND_RESULT_CONTINUE;
if (AbilityBattleEffects(ABILITYEFFECT_ATK_SYNCHRONIZE, gBattlerAttacker, 0, 0, TRUE))
result = MOVEEND_RESULT_RUN_SCRIPT;
gBattleScripting.moveendState++;
return result;
}
static enum MoveEndResult MoveEndAttackerInvisible(void)
{
enum MoveEndResult result = MOVEEND_RESULT_CONTINUE;
@ -2726,7 +2706,6 @@ static enum MoveEndResult MoveEndNextTarget(void)
BattleScriptPush(GetMoveBattleScript(gCurrentMove));
gBattlescriptCurrInstr = BattleScript_FlushMessageBox;
gBattleScripting.moveendState = 0;
MoveValuesCleanUp();
return MOVEEND_RESULT_BREAK;
}
}
@ -2738,7 +2717,6 @@ static enum MoveEndResult MoveEndNextTarget(void)
{
gBattleStruct->moveTarget[gBattlerAttacker] = gBattlerTarget = nextTarget; // Fix for moxie spread moves
gBattleScripting.moveendState = 0;
MoveValuesCleanUp();
BattleScriptPush(GetMoveBattleScript(gCurrentMove));
gBattlescriptCurrInstr = BattleScript_FlushMessageBox;
return MOVEEND_RESULT_BREAK;
@ -2760,7 +2738,6 @@ static enum MoveEndResult MoveEndNextTarget(void)
gBattleScripting.moveendState = 0;
gBattleScripting.animTurn = 0;
gBattleScripting.animTargetsHit = 0;
MoveValuesCleanUp();
BattleScriptPush(GetMoveBattleScript(gCurrentMove));
gBattlescriptCurrInstr = BattleScript_FlushMessageBox;
return MOVEEND_RESULT_BREAK;
@ -2829,7 +2806,6 @@ static enum MoveEndResult MoveEndMultihitMove(void)
gBattleScripting.animTargetsHit = 0;
gBattleScripting.moveendState = 0;
gSpecialStatuses[gBattlerAttacker].multiHitOn = TRUE;
MoveValuesCleanUp();
BattleScriptPush(GetMoveBattleScript(gCurrentMove));
gBattlescriptCurrInstr = BattleScript_FlushMessageBox;
return MOVEEND_RESULT_BREAK;
@ -3800,13 +3776,11 @@ static enum MoveEndResult (*const sMoveEndHandlers[])(void) =
[MOVEEND_PROTECT_LIKE_EFFECT] = MoveEndProtectLikeEffect,
[MOVEEND_ABSORB] = MoveEndAbsorb,
[MOVEEND_RAGE] = MoveEndRage,
[MOVEEND_SYNCHRONIZE_TARGET] = MoveEndSynchronizeTarget,
[MOVEEND_ABILITIES] = MoveEndAbilities,
[MOVEEND_FORM_CHANGE_ON_HIT] = MoveEndFormChangeOnHit,
[MOVEEND_ABILITIES_ATTACKER] = MoveEndAbilitiesAttacker,
[MOVEEND_QUEUE_DANCER] = MoveEndQueueDancer,
[MOVEEND_STATUS_IMMUNITY_ABILITIES] = MoveEndStatusImmunityAbilities,
[MOVEEND_SYNCHRONIZE_ATTACKER] = MoveEndSynchronizeAttacker,
[MOVEEND_ATTACKER_INVISIBLE] = MoveEndAttackerInvisible,
[MOVEEND_ATTACKER_VISIBLE] = MoveEndAttackerVisible,
[MOVEEND_TARGET_VISIBLE] = MoveEndTargetVisible,
@ -3949,14 +3923,6 @@ static inline bool32 IsBattlerUsingBeakBlast(enum BattlerId battler)
return !HasBattlerActedThisTurn(battler);
}
// Is there any point in still doing this?
void MoveValuesCleanUp(void)
{
gBattleScripting.moveEffect = MOVE_EFFECT_NONE;
gBattleStruct->synchronizeMoveEffect = MOVE_EFFECT_NONE;
gBattleCommunication[MISS_TYPE] = 0;
}
static void RequestNonVolatileChange(enum BattlerId battlerAtk)
{
BtlController_EmitSetMonData(

View File

@ -389,9 +389,6 @@ static void Cmd_jumpifstatignorecontrary(void);
static void Cmd_jumpbasedontype(void);
static void Cmd_getexp(void);
static void Cmd_checkteamslost(void);
static void Cmd_movevaluescleanup(void);
static void Cmd_setmultihit(void);
static void Cmd_decrementmultihit(void);
static void Cmd_goto(void);
static void Cmd_jumpifbyte(void);
static void Cmd_jumpifhalfword(void);
@ -578,6 +575,7 @@ static void Cmd_averagestats(void);
static void Cmd_jumpifcaptivateaffected(void);
static void Cmd_setnonvolatilestatus(void);
static void Cmd_tryoverwriteability(void);
static void Cmd_trysynchronize(void);
static void Cmd_dummy(void);
static void Cmd_callnative(void);
@ -619,9 +617,6 @@ void (*const gBattleScriptingCommandsTable[])(void) =
[B_SCR_OP_JUMPBASEDONTYPE] = Cmd_jumpbasedontype,
[B_SCR_OP_GETEXP] = Cmd_getexp,
[B_SCR_OP_CHECKTEAMSLOST] = Cmd_checkteamslost,
[B_SCR_OP_MOVEVALUESCLEANUP] = Cmd_movevaluescleanup,
[B_SCR_OP_SETMULTIHIT] = Cmd_setmultihit,
[B_SCR_OP_DECREMENTMULTIHIT] = Cmd_decrementmultihit,
[B_SCR_OP_GOTO] = Cmd_goto,
[B_SCR_OP_JUMPIFBYTE] = Cmd_jumpifbyte,
[B_SCR_OP_JUMPIFHALFWORD] = Cmd_jumpifhalfword,
@ -808,6 +803,7 @@ void (*const gBattleScriptingCommandsTable[])(void) =
[B_SCR_OP_JUMPIFCAPTIVATEAFFECTED] = Cmd_jumpifcaptivateaffected,
[B_SCR_OP_SETNONVOLATILESTATUS] = Cmd_setnonvolatilestatus,
[B_SCR_OP_TRYOVERWRITEABILITY] = Cmd_tryoverwriteability,
[B_SCR_OP_TRY_SYNCHRONIZE] = Cmd_trysynchronize,
[B_SCR_OP_UNUSED_1] = Cmd_dummy,
[B_SCR_OP_UNUSED_2] = Cmd_dummy,
[B_SCR_OP_UNUSED_3] = Cmd_dummy,
@ -838,6 +834,8 @@ void (*const gBattleScriptingCommandsTable[])(void) =
[B_SCR_OP_UNUSED_28] = Cmd_dummy,
[B_SCR_OP_UNUSED_29] = Cmd_dummy,
[B_SCR_OP_UNUSED_30] = Cmd_dummy,
[B_SCR_OP_UNUSED_31] = Cmd_dummy,
[B_SCR_OP_UNUSED_32] = Cmd_dummy,
[B_SCR_OP_CALLNATIVE] = Cmd_callnative,
};
@ -2285,7 +2283,41 @@ static inline bool32 TrySetLightScreen(enum BattlerId battler)
return FALSE;
}
static void SetNonVolatileStatus(enum BattlerId effectBattler, enum MoveEffect effect, const u8 *battleScript, enum StatusTrigger trigger)
static void TrySynchronizeActivation(enum BattlerId battlerAtk, enum BattlerId effectBattler, enum MoveEffect effect)
{
if (battlerAtk == effectBattler || gBattleStruct->synchronizeState == SYNCH_STATE_SET_STATUS)
return;
enum Ability effectAbility = GetBattlerAbility(effectBattler);
if (effectAbility != ABILITY_SYNCHRONIZE)
return;
if (effect == MOVE_EFFECT_POISON
|| effect == MOVE_EFFECT_TOXIC
|| effect == MOVE_EFFECT_PARALYSIS
|| effect == MOVE_EFFECT_BURN)
{
if (CanSetNonVolatileStatus(
effectBattler,
battlerAtk,
effectAbility,
GetBattlerAbility(battlerAtk),
effect,
CHECK_TRIGGER))
{
gBattleStruct->synchronizeState = SYNCH_STATE_START;
gBattlerAbility = effectBattler;
gBattleScripting.savedBattler = battlerAtk;
}
else
{
gBattleStruct->synchronizeState = SYNCH_STATE_SHOW_ABILITY_POPUP;
gBattlerAbility = effectBattler;
}
}
}
static void SetNonVolatileStatus(enum BattlerId battlerAtk, enum BattlerId effectBattler, enum MoveEffect effect, const u8 *battleScript, enum StatusTrigger trigger)
{
gEffectBattler = effectBattler;
@ -2350,12 +2382,7 @@ static void SetNonVolatileStatus(enum BattlerId effectBattler, enum MoveEffect e
gBattleScripting.moveEffect = MOVE_EFFECT_NONE;
// for synchronize
if (effect == MOVE_EFFECT_POISON
|| effect == MOVE_EFFECT_TOXIC
|| effect == MOVE_EFFECT_PARALYSIS
|| effect == MOVE_EFFECT_BURN)
gBattleStruct->synchronizeMoveEffect = effect;
TrySynchronizeActivation(battlerAtk, effectBattler, effect);
if (effect == MOVE_EFFECT_POISON || effect == MOVE_EFFECT_TOXIC)
gBattleStruct->poisonPuppeteerConfusion = TRUE;
@ -2483,7 +2510,7 @@ void SetMoveEffect(enum BattlerId battlerAtk, enum BattlerId effectBattler, enum
moveEffect,
CHECK_TRIGGER))
{
SetNonVolatileStatus(gEffectBattler, moveEffect, battleScript, TRIGGER_ON_MOVE);
SetNonVolatileStatus(gBattlerAttacker, gEffectBattler, moveEffect, battleScript, TRIGGER_ON_MOVE);
}
else
{
@ -4621,32 +4648,6 @@ static void Cmd_checkteamslost(void)
}
}
static void Cmd_movevaluescleanup(void)
{
CMD_ARGS();
MoveValuesCleanUp();
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_setmultihit(void)
{
CMD_ARGS(u8 value);
gMultiHitCounter = cmd->value;
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_decrementmultihit(void)
{
CMD_ARGS(const u8 *loopInstr);
if (--gMultiHitCounter == 0)
gBattlescriptCurrInstr = cmd->nextInstr;
else
gBattlescriptCurrInstr = cmd->loopInstr;
}
static void Cmd_goto(void)
{
CMD_ARGS(const u8 *instr);
@ -9380,21 +9381,17 @@ static void Cmd_tryactivateitem(void)
{
case ACTIVATION_ON_USABLE_AGAIN:
case ACTIVATION_ON_PICK_UP:
if (ItemBattleEffects(battler, 0, GetBattlerHoldEffect(battler), IsForceTriggerItemActivation))
return;
ItemBattleEffects(battler, 0, GetBattlerHoldEffect(battler), IsForceTriggerItemActivation);
break;
case ACTIVATION_ON_HARVEST:
gLastUsedItem = gBattleMons[battler].item;
if (ItemBattleEffects(battler, 0, GetBattlerHoldEffect(battler), IsOnBerryActivation))
return;
ItemBattleEffects(battler, 0, GetBattlerHoldEffect(battler), IsOnBerryActivation);
break;
case ACTIVATION_ON_HP_THRESHOLD:
if (ItemBattleEffects(battler, 0, GetBattlerHoldEffect(battler), IsOnHpThresholdActivation))
return;
ItemBattleEffects(battler, 0, GetBattlerHoldEffect(battler), IsOnHpThresholdActivation);
break;
case ACTIVATION_ON_STATUS_CHANGE:
if (ItemBattleEffects(battler, 0, GetBattlerHoldEffect(battler), IsOnStatusChangeActivation))
return;
ItemBattleEffects(battler, 0, GetBattlerHoldEffect(battler), IsOnStatusChangeActivation);
break;
}
}
@ -11540,13 +11537,13 @@ static void Cmd_setnonvolatilestatus(void)
if (gBattleScripting.moveEffect >= MOVE_EFFECT_CONFUSION)
SetMoveEffect(gBattleScripting.battler, gEffectBattler, gBattleScripting.moveEffect, cmd->nextInstr, EFFECT_PRIMARY);
else
SetNonVolatileStatus(gEffectBattler, gBattleScripting.moveEffect, cmd->nextInstr, TRIGGER_ON_ABILITY);
SetNonVolatileStatus(gBattleScripting.battler, gEffectBattler, gBattleScripting.moveEffect, cmd->nextInstr, TRIGGER_ON_ABILITY);
break;
case TRIGGER_ON_MOVE:
SetNonVolatileStatus(gBattlerTarget, GetMoveNonVolatileStatus(gCurrentMove), cmd->nextInstr, TRIGGER_ON_MOVE);
SetNonVolatileStatus(gBattlerAttacker, gBattlerTarget, GetMoveNonVolatileStatus(gCurrentMove), cmd->nextInstr, TRIGGER_ON_MOVE);
break;
case TRIGGER_ON_PROTECT:
SetNonVolatileStatus(gBattlerAttacker, gBattleScripting.moveEffect, cmd->nextInstr, TRIGGER_ON_PROTECT);
SetNonVolatileStatus(gBattlerTarget, gBattlerAttacker, gBattleScripting.moveEffect, cmd->nextInstr, TRIGGER_ON_PROTECT);
break;
}
}
@ -11578,6 +11575,63 @@ static void Cmd_tryoverwriteability(void)
}
}
static u32 GetMoveEffectFromStatus(enum BattlerId battler)
{
switch (gBattleMons[battler].status1)
{
case STATUS1_POISON:
return MOVE_EFFECT_POISON;
case STATUS1_TOXIC_POISON:
return MOVE_EFFECT_TOXIC;
case STATUS1_PARALYSIS:
return MOVE_EFFECT_PARALYSIS;
case STATUS1_BURN:
return MOVE_EFFECT_BURN;
default:
return MOVE_EFFECT_NONE;
}
}
static void Cmd_trysynchronize(void)
{
CMD_ARGS();
enum MoveEffect synchStatus = MOVE_EFFECT_NONE;
switch (gBattleStruct->synchronizeState)
{
case SYNCH_STATE_NONE:
gBattlescriptCurrInstr = cmd->nextInstr;
break;
case SYNCH_STATE_START:
synchStatus = GetMoveEffectFromStatus(gBattlerAbility);
RecordAbilityBattle(gBattlerAbility, ABILITY_SYNCHRONIZE);
if (GetConfig(B_SYNCHRONIZE_TOXIC) < GEN_5 && synchStatus == MOVE_EFFECT_TOXIC)
synchStatus = MOVE_EFFECT_POISON;
gBattleScripting.battler = gBattlerAbility;
gEffectBattler = gBattleScripting.savedBattler;
gBattleScripting.moveEffect = synchStatus;
gBattleStruct->synchronizeState = SYNCH_STATE_SET_STATUS;
PREPARE_ABILITY_BUFFER(gBattleTextBuff1, ABILITY_SYNCHRONIZE);
BattleScriptCall(BattleScript_SynchronizeActivates);
break;
case SYNCH_STATE_SHOW_ABILITY_POPUP: // Synchronize ability pop up still shows up even if status fails
gBattleStruct->synchronizeState = SYNCH_STATE_END;
BattleScriptCall(BattleScript_AbilityPopUp);
break;
case SYNCH_STATE_SET_STATUS: // Extra step to skip trysynchronize for battler the status is inflicted on, so gEffectBattler isn't assigned to early
gBattleStruct->synchronizeState = SYNCH_STATE_END;
gBattlescriptCurrInstr = cmd->nextInstr;
break;
case SYNCH_STATE_END:
gBattleStruct->synchronizeState = SYNCH_STATE_NONE;
gEffectBattler = gBattlerAbility; // Restore effect battler that was previously set to the synchronize battler
gBattlescriptCurrInstr = cmd->nextInstr;
break;
}
}
static void Cmd_dummy(void)
{
}
@ -13053,7 +13107,7 @@ void BS_SwapStats(void)
static void TrySetParalysis(const u8 *nextInstr, const u8 *failInstr)
{
if (CanBeParalyzed(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerTarget)))
SetNonVolatileStatus(gBattlerTarget, MOVE_EFFECT_PARALYSIS, nextInstr, TRIGGER_ON_MOVE);
SetNonVolatileStatus(gBattlerAttacker, gBattlerTarget, MOVE_EFFECT_PARALYSIS, nextInstr, TRIGGER_ON_MOVE);
else
gBattlescriptCurrInstr = failInstr;
}
@ -13061,7 +13115,7 @@ static void TrySetParalysis(const u8 *nextInstr, const u8 *failInstr)
static void TrySetPoison(const u8 *nextInstr, const u8 *failInstr)
{
if (CanBePoisoned(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerAttacker), GetBattlerAbility(gBattlerTarget)))
SetNonVolatileStatus(gBattlerTarget, MOVE_EFFECT_POISON, nextInstr, TRIGGER_ON_MOVE);
SetNonVolatileStatus(gBattlerAttacker, gBattlerTarget, MOVE_EFFECT_POISON, nextInstr, TRIGGER_ON_MOVE);
else
gBattlescriptCurrInstr = failInstr;
}
@ -13069,7 +13123,7 @@ static void TrySetPoison(const u8 *nextInstr, const u8 *failInstr)
static void TrySetSleep(const u8 *nextInstr, const u8 *failInstr)
{
if (CanBeSlept(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerTarget), BLOCKED_BY_SLEEP_CLAUSE))
SetNonVolatileStatus(gBattlerTarget, MOVE_EFFECT_SLEEP, nextInstr, TRIGGER_ON_MOVE);
SetNonVolatileStatus(gBattlerAttacker, gBattlerTarget, MOVE_EFFECT_SLEEP, nextInstr, TRIGGER_ON_MOVE);
else
gBattlescriptCurrInstr = failInstr;
}

View File

@ -897,7 +897,6 @@ void HandleAction_NothingIsFainted(void)
{
gCurrentTurnActionNumber++;
gCurrentActionFuncId = gActionsByTurnOrder[gCurrentTurnActionNumber];
gBattleStruct->synchronizeMoveEffect = MOVE_EFFECT_NONE;
}
void HandleAction_ActionFinished(void)
@ -920,7 +919,6 @@ void HandleAction_ActionFinished(void)
gBattleCommunication[3] = 0;
gBattleCommunication[4] = 0;
gBattleResources->battleScriptsStack->size = 0;
gBattleStruct->synchronizeMoveEffect = MOVE_EFFECT_NONE;
if (GetConfig(B_RECALC_TURN_AFTER_ACTIONS) >= GEN_8 && !afterYouActive && !gBattleStruct->pledgeMove && !IsPursuitTargetSet())
{
@ -4650,69 +4648,6 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, enum BattlerId battler, enum
if (effect)
return effect;
break;
case ABILITYEFFECT_SYNCHRONIZE:
if (gLastUsedAbility == ABILITY_SYNCHRONIZE && gBattleStruct->synchronizeMoveEffect != MOVE_EFFECT_NONE)
{
gBattleScripting.battler = gBattlerAbility = gBattlerTarget;
RecordAbilityBattle(gBattlerTarget, ABILITY_SYNCHRONIZE);
if (GetConfig(B_SYNCHRONIZE_TOXIC) < GEN_5 && gBattleStruct->synchronizeMoveEffect == MOVE_EFFECT_TOXIC)
gBattleStruct->synchronizeMoveEffect = MOVE_EFFECT_POISON;
if (CanSetNonVolatileStatus(
gBattlerTarget,
gBattlerAttacker,
gLastUsedAbility,
GetBattlerAbility(gBattlerAttacker),
gBattleStruct->synchronizeMoveEffect,
CHECK_TRIGGER))
{
gEffectBattler = gBattlerAttacker;
gBattleScripting.moveEffect = gBattleStruct->synchronizeMoveEffect;
PREPARE_ABILITY_BUFFER(gBattleTextBuff1, ABILITY_SYNCHRONIZE);
BattleScriptCall(BattleScript_SynchronizeActivates);
effect++;
}
else // Synchronize ability pop up still shows up even if status fails
{
BattleScriptCall(BattleScript_AbilityPopUp);
}
gBattleStruct->synchronizeMoveEffect = MOVE_EFFECT_NONE;
}
break;
case ABILITYEFFECT_ATK_SYNCHRONIZE:
if (gLastUsedAbility == ABILITY_SYNCHRONIZE && gBattleStruct->synchronizeMoveEffect != MOVE_EFFECT_NONE)
{
gBattleScripting.battler = gBattlerAbility = gBattlerAttacker;
RecordAbilityBattle(gBattlerAttacker, ABILITY_SYNCHRONIZE);
if (GetConfig(B_SYNCHRONIZE_TOXIC) < GEN_5 && gBattleStruct->synchronizeMoveEffect == MOVE_EFFECT_TOXIC)
gBattleStruct->synchronizeMoveEffect = MOVE_EFFECT_POISON;
if (CanSetNonVolatileStatus(
gBattlerAttacker,
gBattlerTarget,
gLastUsedAbility,
GetBattlerAbility(gBattlerAttacker),
gBattleStruct->synchronizeMoveEffect,
CHECK_TRIGGER))
{
if (gBattleStruct->synchronizeMoveEffect == MOVE_EFFECT_TOXIC)
gBattleStruct->synchronizeMoveEffect = MOVE_EFFECT_POISON;
gEffectBattler = gBattlerTarget;
gBattleScripting.moveEffect = gBattleStruct->synchronizeMoveEffect;
PREPARE_ABILITY_BUFFER(gBattleTextBuff1, ABILITY_SYNCHRONIZE);
BattleScriptCall(BattleScript_SynchronizeActivates);
effect++;
}
else // Synchronize ability pop up still shows up even if status fails
{
BattleScriptCall(BattleScript_AbilityPopUp);
}
gBattleStruct->synchronizeMoveEffect = MOVE_EFFECT_NONE;
}
break;
case ABILITYEFFECT_TERA_SHIFT:
if (TryBattleFormChange(battler, FORM_CHANGE_BATTLE_SWITCH_IN, ability))
{

View File

@ -73,3 +73,59 @@ SINGLE_BATTLE_TEST("Synchronize will mirror back static activation")
STATUS_ICON(player, paralysis: TRUE);
}
}
DOUBLE_BATTLE_TEST("Synchronize will trigger on both targets")
{
GIVEN {
ASSUME(MoveHasAdditionalEffect(MOVE_MORTAL_SPIN, MOVE_EFFECT_POISON));
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_LUM_BERRY); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_ABRA) { Item(ITEM_LUM_BERRY); Ability(ABILITY_SYNCHRONIZE); }
OPPONENT(SPECIES_ABRA) { Item(ITEM_LUM_BERRY); Ability(ABILITY_SYNCHRONIZE); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_MORTAL_SPIN); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_MORTAL_SPIN, playerLeft);
HP_BAR(opponentLeft);
HP_BAR(opponentRight);
ABILITY_POPUP(opponentLeft, ABILITY_SYNCHRONIZE);
ABILITY_POPUP(opponentRight, ABILITY_SYNCHRONIZE);
} THEN {
EXPECT_EQ(gBattleMons[B_BATTLER_0].status1, STATUS1_POISON);
EXPECT_EQ(gBattleMons[B_BATTLER_1].status1, STATUS1_NONE);
EXPECT_EQ(gBattleMons[B_BATTLER_3].status1, STATUS1_NONE);
}
}
SINGLE_BATTLE_TEST("Synchronize can trigger again during the same attack if user cured it's status")
{
GIVEN {
ASSUME(MoveHasAdditionalEffect(MOVE_MORTAL_SPIN, MOVE_EFFECT_POISON));
ASSUME(MoveMakesContact(MOVE_MORTAL_SPIN));
PLAYER(SPECIES_SEISMITOAD) { Ability(ABILITY_POISON_TOUCH); Item(ITEM_LUM_BERRY); }
OPPONENT(SPECIES_ABRA) { Ability(ABILITY_SYNCHRONIZE); Item(ITEM_LUM_BERRY); }
} WHEN {
TURN { MOVE(player, MOVE_MORTAL_SPIN); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_MORTAL_SPIN, player);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent);
STATUS_ICON(opponent, poison: TRUE);
ABILITY_POPUP(opponent, ABILITY_SYNCHRONIZE);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, player);
STATUS_ICON(player, poison: TRUE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
STATUS_ICON(player, poison: FALSE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
STATUS_ICON(opponent, poison: FALSE);
ABILITY_POPUP(player, ABILITY_POISON_TOUCH);
STATUS_ICON(opponent, poison: TRUE);
ABILITY_POPUP(opponent, ABILITY_SYNCHRONIZE);
STATUS_ICON(player, poison: TRUE);
}
}