diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index b23992d332..d9a7ca5fed 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -2461,6 +2461,11 @@ .byte \battler .endm + .macro trysynchronoise jumpInstr:req + callnative BS_TrySynchronoise + .4byte \jumpInstr + .endm + .macro setallytonexttarget jumpInstr:req jumpifbyte CMP_GREATER_THAN, gBattlerTarget, 0x1, 1f addbyte gBattlerTarget, 0x2 diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index a57ce67c25..6f20b90345 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -1622,42 +1622,20 @@ BattleScript_EffectPsychoShiftCanWork: BattleScript_EffectSynchronoise:: attackcanceler attackstring + pause B_WAIT_TIME_MED ppreduce - selectfirstvalidtarget -BattleScript_SynchronoiseLoop: - movevaluescleanup - jumpifcantusesynchronoise BattleScript_SynchronoiseNoEffect - accuracycheck BattleScript_SynchronoiseMissed, ACC_CURR_MOVE - critcalc - damagecalc - adjustdamage - attackanimation - waitanimation - effectivenesssound - hitanimation BS_TARGET - waitstate - healthbarupdate BS_TARGET - datahpupdate BS_TARGET - critmessage - waitmessage B_WAIT_TIME_LONG - resultmessage - waitmessage B_WAIT_TIME_LONG + trysynchronoise BattleScript_MoveEnd + accuracycheck BattleScript_ButItFailed, ACC_CURR_MOVE + goto BattleScript_HitFromCritCalc + +BattleScript_ItDoesntAffectFoe:: + savetarget + copybyte gBattlerTarget, sBATTLER + printstring STRINGID_ITDOESNTAFFECT + waitmessage B_WAIT_TIME_SHORT flushtextbox - tryfaintmon BS_TARGET -BattleScript_SynchronoiseMoveTargetEnd: - moveendto MOVEEND_NEXT_TARGET - jumpifnexttargetvalid BattleScript_SynchronoiseLoop - end -BattleScript_SynchronoiseMissed: - pause B_WAIT_TIME_SHORT - resultmessage - waitmessage B_WAIT_TIME_LONG - goto BattleScript_SynchronoiseMoveTargetEnd -BattleScript_SynchronoiseNoEffect: - pause B_WAIT_TIME_SHORT - printstring STRINGID_NOEFFECTONTARGET - waitmessage B_WAIT_TIME_LONG - goto BattleScript_SynchronoiseMoveTargetEnd + restoretarget + return BattleScript_MoveEffectSmackDown:: printstring STRINGID_FELLSTRAIGHTDOWN diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 525d3aefe4..dba2ad49d0 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -526,7 +526,6 @@ extern const u8 BattleScript_TeraShellDistortingTypeMatchups[]; extern const u8 BattleScript_TeraFormChange[]; extern const u8 BattleScript_SleepClausePreventsEnd[]; extern const u8 BattleScript_PowerConstruct[]; - extern const u8 BattleScript_AbilityProtectsDoesntAffect[]; extern const u8 BattleScript_ImmunityProtected[]; extern const u8 BattleScript_SafeguardProtected[]; @@ -538,6 +537,7 @@ extern const u8 BattleScript_AlreadyPoisoned[]; extern const u8 BattleScript_AlreadyParalyzed[]; extern const u8 BattleScript_AlreadyBurned[]; extern const u8 BattleScript_PrintAbilityMadeIneffective[]; +extern const u8 BattleScript_ItDoesntAffectFoe[]; // zmoves extern const u8 BattleScript_ZMoveActivateDamaging[]; diff --git a/include/constants/battle.h b/include/constants/battle.h index e785498489..9064eb3863 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -345,6 +345,7 @@ enum TypeSideHazard #define MOVE_RESULT_FOE_HUNG_ON (1 << 7) #define MOVE_RESULT_STURDIED (1 << 8) #define MOVE_RESULT_FOE_ENDURED_AFFECTION (1 << 9) +#define MOVE_RESULT_SYNCHRONOISE_AFFECTED (1 << 10) #define MOVE_RESULT_NO_EFFECT (MOVE_RESULT_MISSED | MOVE_RESULT_DOESNT_AFFECT_FOE | MOVE_RESULT_FAILED) enum BattleWeather diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index ff4104b19e..32c2111488 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -1242,22 +1242,24 @@ static void Cmd_attackcanceler(void) u32 abilityDef = GetBattlerAbility(gBattlerTarget); - if (CanAbilityBlockMove(gBattlerAttacker, - gBattlerTarget, - GetBattlerAbility(gBattlerAttacker), - abilityDef, - gCurrentMove, - ABILITY_RUN_SCRIPT)) + if (CanAbilityBlockMove( + gBattlerAttacker, + gBattlerTarget, + GetBattlerAbility(gBattlerAttacker), + abilityDef, + gCurrentMove, + ABILITY_RUN_SCRIPT)) return; if (GetMoveNonVolatileStatus(gCurrentMove) == MOVE_EFFECT_PARALYSIS) { - if (CanAbilityAbsorbMove(gBattlerAttacker, - gBattlerTarget, - abilityDef, - gCurrentMove, - GetBattleMoveType(gCurrentMove), - ABILITY_RUN_SCRIPT)) + if (CanAbilityAbsorbMove( + gBattlerAttacker, + gBattlerTarget, + abilityDef, + gCurrentMove, + GetBattleMoveType(gCurrentMove), + ABILITY_RUN_SCRIPT)) return; } @@ -2418,6 +2420,7 @@ static bool32 ProcessPreAttackAnimationFuncs(void) if (IsDoubleSpreadMove()) { u32 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove); + if (!gBattleStruct->printedStrongWindsWeakenedAttack) { for (u32 battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) @@ -5957,9 +5960,10 @@ static u32 GetNextTarget(u32 moveTarget, bool32 excludeCurrent) u32 battler; for (battler = 0; battler < MAX_BATTLERS_COUNT; battler++) { - if (battler != gBattlerAttacker - && !(excludeCurrent && battler == gBattlerTarget) - && IsBattlerAlive(battler) + if (battler == gBattlerAttacker || !IsBattlerAlive(battler)) + continue; + + if (!(excludeCurrent && battler == gBattlerTarget) && !gBattleStruct->battlerState[gBattlerAttacker].targetsDone[battler] && (!IsBattlerAlly(battler, gBattlerAttacker) || moveTarget == MOVE_TARGET_FOES_AND_ALLY)) break; @@ -6665,8 +6669,9 @@ static void Cmd_moveend(void) MoveValuesCleanUp(); gBattleScripting.moveEffect = gBattleScripting.savedMoveEffect; - if (moveEffect == EFFECT_EXPLOSION || moveEffect == EFFECT_MISTY_EXPLOSION) - BattleScriptPush(gBattleMoveEffects[EFFECT_HIT].battleScript); // Edge case for Explosion not changing targets + if (moveEffect == EFFECT_EXPLOSION || moveEffect == EFFECT_MISTY_EXPLOSION // Edge case for Explosion not changing targets + || moveEffect == EFFECT_SYNCHRONOISE) // So we don't go back to the Synchronoise script + BattleScriptPush(gBattleMoveEffects[EFFECT_HIT].battleScript); else BattleScriptPush(GetMoveBattleScript(gCurrentMove)); gBattlescriptCurrInstr = BattleScript_FlushMessageBox; @@ -17907,7 +17912,6 @@ void BS_JumpIfSleepClause(void) void BS_FickleBeamDamageCalculation(void) { NATIVE_ARGS(); - gBattleStruct->fickleBeamBoosted = FALSE; if (RandomPercentage(RNG_FICKLE_BEAM, 30)) { @@ -18671,3 +18675,36 @@ void BS_TryActivateAbilityShield(void) BattleScriptCall(BattleScript_AbilityShieldProtects); } } + +void BS_TrySynchronoise(void) +{ + NATIVE_ARGS(const u8 *jumpInstr); + bool32 atleastOneSharedType = FALSE; + + for (u32 battlerDef = 0; battlerDef < gBattlersCount; battlerDef++) + { + if (gBattleStruct->moveResultFlags[battlerDef] & MOVE_RESULT_SYNCHRONOISE_AFFECTED + || gBattlerAttacker == battlerDef + || !IsBattlerAlive(battlerDef)) + continue; + + if (DoBattlersShareType(gBattlerAttacker, battlerDef)) + { + atleastOneSharedType = TRUE; + continue; + } + + if (!DoBattlersShareType(gBattlerAttacker, battlerDef)) + { + gBattleScripting.battler = battlerDef; + gBattleStruct->moveResultFlags[battlerDef] |= MOVE_RESULT_NO_EFFECT | MOVE_RESULT_SYNCHRONOISE_AFFECTED; + BattleScriptCall(BattleScript_ItDoesntAffectFoe); + return; + } + } + + if (atleastOneSharedType) + gBattlescriptCurrInstr = cmd->nextInstr; + else + gBattlescriptCurrInstr = cmd->jumpInstr; +} diff --git a/src/battle_util.c b/src/battle_util.c index 76786d1f44..e40020e11f 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -2513,6 +2513,7 @@ static enum MoveCanceller CancellerMultiTargetMoves(void) if (gBattlerAttacker == battlerDef || !IsBattlerAlive(battlerDef) + || (GetMoveEffect(gCurrentMove) == EFFECT_SYNCHRONOISE && !DoBattlersShareType(gBattlerAttacker, battlerDef)) || (moveTarget == MOVE_TARGET_BOTH && gBattlerAttacker == BATTLE_PARTNER(battlerDef)) || IsBattlerProtected(gBattlerAttacker, battlerDef, gCurrentMove)) // Missing Invulnerable check { @@ -7914,7 +7915,7 @@ static inline u32 IsFieldMudSportAffected(u32 moveType) return TRUE; } } - + return FALSE; } diff --git a/test/battle/gimmick/terastal.c b/test/battle/gimmick/terastal.c index 7ce10a5942..441487c002 100644 --- a/test/battle/gimmick/terastal.c +++ b/test/battle/gimmick/terastal.c @@ -385,7 +385,7 @@ SINGLE_BATTLE_TEST("(TERA) Synchronoise uses a Terastallized Pokemon's Tera Type } SCENE { // turn 1 MESSAGE("The opposing Wobbuffet used Synchronoise!"); - MESSAGE("It won't have any effect on Wobbuffet!"); + MESSAGE("It doesn't affect Wobbuffet…"); // turn 2 MESSAGE("The opposing Wobbuffet used Synchronoise!"); ANIMATION(ANIM_TYPE_MOVE, MOVE_SYNCHRONOISE, opponent); diff --git a/test/battle/move_effect/synchronoise.c b/test/battle/move_effect/synchronoise.c index 9b68ccc63a..45b4f125ea 100644 --- a/test/battle/move_effect/synchronoise.c +++ b/test/battle/move_effect/synchronoise.c @@ -1,4 +1,74 @@ #include "global.h" #include "test/battle.h" +DOUBLE_BATTLE_TEST("Synchronoise hits all Pokemon that share a type with the attacker") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(playerLeft, MOVE_SYNCHRONOISE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SYNCHRONOISE, playerLeft); + HP_BAR(opponentLeft); + HP_BAR(playerRight); + HP_BAR(opponentRight); + } +} + +DOUBLE_BATTLE_TEST("Synchronoise will fail if there is no corresponding typing on the field") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_BULBASAUR); + OPPONENT(SPECIES_BULBASAUR); + OPPONENT(SPECIES_BULBASAUR); + } WHEN { + TURN { MOVE(playerLeft, MOVE_SYNCHRONOISE); } + } SCENE { + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SYNCHRONOISE, playerLeft); + MESSAGE("Wobbuffet used Synchronoise!"); + MESSAGE("It doesn't affect the opposing Bulbasaur…"); + MESSAGE("It doesn't affect Bulbasaur…"); + MESSAGE("It doesn't affect the opposing Bulbasaur…"); + NOT MESSAGE("But it failed!"); + } +} + +DOUBLE_BATTLE_TEST("Synchronoise will hit if there is at least one target") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_BULBASAUR); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_BULBASAUR); + } WHEN { + TURN { MOVE(playerLeft, MOVE_SYNCHRONOISE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SYNCHRONOISE, playerLeft); + HP_BAR(opponentLeft); + NONE_OF { + HP_BAR(playerRight); + HP_BAR(opponentRight); + MESSAGE("But it failed!"); + } + } +} + +DOUBLE_BATTLE_TEST("Synchronoise will fail if the corresponding typing mon protects") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_BULBASAUR); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_BULBASAUR); + } WHEN { + TURN { MOVE(opponentLeft, MOVE_PROTECT); MOVE(playerLeft, MOVE_SYNCHRONOISE); } + } SCENE { + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SYNCHRONOISE, playerLeft); + } +} + TO_DO_BATTLE_TEST("TODO: Write Synchronoise (Move Effect) test titles")