diff --git a/include/battle.h b/include/battle.h index addf654d913c..92da6ad05efb 100644 --- a/include/battle.h +++ b/include/battle.h @@ -196,10 +196,10 @@ struct SpecialStatus u8 statLowered:1; u8 lightningRodRedirected:1; u8 restoredBattlerSprite: 1; - u8 traced:1; u8 faintedHasReplacement:1; u8 focusBanded:1; u8 focusSashed:1; + u8 unused:1; // End of byte u8 sturdied:1; u8 stormDrainRedirected:1; diff --git a/include/battle_util.h b/include/battle_util.h index fc1fdce99653..644a112d7dab 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -29,17 +29,15 @@ #define ABILITYEFFECT_IMMUNITY 6 #define ABILITYEFFECT_SYNCHRONIZE 7 #define ABILITYEFFECT_ATK_SYNCHRONIZE 8 -#define ABILITYEFFECT_TRACE1 9 -#define ABILITYEFFECT_TRACE2 10 -#define ABILITYEFFECT_MOVE_END_OTHER 11 -#define ABILITYEFFECT_NEUTRALIZINGGAS 12 -#define ABILITYEFFECT_FIELD_SPORT 13 // Only used if B_SPORT_TURNS >= GEN_6 -#define ABILITYEFFECT_ON_WEATHER 14 -#define ABILITYEFFECT_ON_TERRAIN 15 -#define ABILITYEFFECT_SWITCH_IN_TERRAIN 16 -#define ABILITYEFFECT_SWITCH_IN_WEATHER 17 -#define ABILITYEFFECT_OPPORTUNIST 18 -#define ABILITYEFFECT_SWITCH_IN_STATUSES 19 +#define ABILITYEFFECT_MOVE_END_OTHER 9 +#define ABILITYEFFECT_NEUTRALIZINGGAS 10 +#define ABILITYEFFECT_FIELD_SPORT 11 // Only used if B_SPORT_TURNS >= GEN_6 +#define ABILITYEFFECT_ON_WEATHER 12 +#define ABILITYEFFECT_ON_TERRAIN 13 +#define ABILITYEFFECT_SWITCH_IN_TERRAIN 14 +#define ABILITYEFFECT_SWITCH_IN_WEATHER 15 +#define ABILITYEFFECT_OPPORTUNIST 16 +#define ABILITYEFFECT_SWITCH_IN_STATUSES 17 // Special cases #define ABILITYEFFECT_MUD_SPORT 252 // Only used if B_SPORT_TURNS >= GEN_6 #define ABILITYEFFECT_WATER_SPORT 253 // Only used if B_SPORT_TURNS >= GEN_6 diff --git a/src/battle_main.c b/src/battle_main.c index f5d6487740ab..0e16ffc11eff 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -4116,8 +4116,6 @@ static void TryDoEventsBeforeFirstTurn(void) if (AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, gBattlerAttacker, 0, 0, 0) != 0) return; } - if (AbilityBattleEffects(ABILITYEFFECT_TRACE1, 0, 0, 0, 0) != 0) - return; // Check all switch in items having effect from the fastest mon to slowest. while (gBattleStruct->switchInItemsCounter < gBattlersCount) { diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index cf140e1b86de..77b18eafd380 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -7140,8 +7140,7 @@ bool32 DoSwitchInAbilities(u32 battler) return (TryPrimalReversion(battler) || AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, battler, 0, 0, 0) || (gBattleWeather & B_WEATHER_ANY && WEATHER_HAS_EFFECT && AbilityBattleEffects(ABILITYEFFECT_ON_WEATHER, battler, 0, 0, 0)) - || (gFieldStatuses & STATUS_FIELD_TERRAIN_ANY && AbilityBattleEffects(ABILITYEFFECT_ON_TERRAIN, battler, 0, 0, 0)) - || AbilityBattleEffects(ABILITYEFFECT_TRACE2, 0, 0, 0, 0)); + || (gFieldStatuses & STATUS_FIELD_TERRAIN_ANY && AbilityBattleEffects(ABILITYEFFECT_ON_TERRAIN, battler, 0, 0, 0))); } static void UpdateSentMonFlags(u32 battler) @@ -7289,6 +7288,14 @@ static bool32 DoSwitchInEffectsForBattler(u32 battler) gDisableStructs[battler].truantSwitchInHack = 0; + for (i = 0; i < gBattlersCount; i++) + { + if (i != battler + && GetBattlerAbility(i) == ABILITY_TRACE + && AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, i, 0, 0, 0)) + return TRUE; + } + if (DoSwitchInAbilities(battler) || ItemBattleEffects(ITEMEFFECT_ON_SWITCH_IN, battler, FALSE)) return TRUE; else if (AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, battler, 0, 0, 0)) @@ -9226,7 +9233,6 @@ static void Cmd_various(void) case VARIOUS_RESET_SWITCH_IN_ABILITY_BITS: { VARIOUS_ARGS(); - gSpecialStatuses[battler].traced = FALSE; gSpecialStatuses[battler].switchInAbilityDone = FALSE; break; } @@ -9459,7 +9465,6 @@ static void Cmd_various(void) gBattlescriptCurrInstr = cmd->nextInstr; AbilityBattleEffects(ABILITYEFFECT_NEUTRALIZINGGAS, battler, 0, 0, 0); AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, battler, 0, 0, 0); - AbilityBattleEffects(ABILITYEFFECT_TRACE2, battler, 0, 0, 0); AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, battler, 0, 0, 0); return; } diff --git a/src/battle_util.c b/src/battle_util.c index cd2333d4af7c..755b75f6be49 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -4247,6 +4247,50 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 gBattleScripting.battler = battler; switch (gLastUsedAbility) { + case ABILITY_TRACE: + { + u32 chosenTarget; + u32 target1; + u32 target2; + + if (gSpecialStatuses[battler].switchInAbilityDone) + break; + if (gBattleResources->flags->flags[battler] & RESOURCE_FLAG_TRACED) + break; + + side = (BATTLE_OPPOSITE(GetBattlerPosition(battler))) & BIT_SIDE; + target1 = GetBattlerAtPosition(side); + target2 = GetBattlerAtPosition(side + BIT_FLANK); + gSpecialStatuses[battler].switchInAbilityDone = TRUE; + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + { + if (!gAbilitiesInfo[gBattleMons[target1].ability].cantBeTraced && gBattleMons[target1].hp != 0 + && !gAbilitiesInfo[gBattleMons[target2].ability].cantBeTraced && gBattleMons[target2].hp != 0) + chosenTarget = GetBattlerAtPosition((RandomPercentage(RNG_TRACE, 50) * 2) | side), effect++; + else if (!gAbilitiesInfo[gBattleMons[target1].ability].cantBeTraced && gBattleMons[target1].hp != 0) + chosenTarget = target1, effect++; + else if (!gAbilitiesInfo[gBattleMons[target2].ability].cantBeTraced && gBattleMons[target2].hp != 0) + chosenTarget = target2, effect++; + } + else + { + if (!gAbilitiesInfo[gBattleMons[target1].ability].cantBeTraced && gBattleMons[target1].hp != 0) + chosenTarget = target1, effect++; + } + + if (effect != 0) + { + BattleScriptPushCursorAndCallback(BattleScript_TraceActivatesEnd3); + gBattleResources->flags->flags[battler] &= ~RESOURCE_FLAG_TRACED; + gBattleStruct->tracedAbility[battler] = gLastUsedAbility = gBattleMons[chosenTarget].ability; + RecordAbilityBattle(chosenTarget, gLastUsedAbility); // Record the opposing battler has this ability + battler = gBattlerAbility = gBattleScripting.battler = battler; + + PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, chosenTarget, gBattlerPartyIndexes[chosenTarget]) + PREPARE_ABILITY_BUFFER(gBattleTextBuff2, gLastUsedAbility) + } + } + break; case ABILITY_IMPOSTER: if (IsBattlerAlive(BATTLE_OPPOSITE(battler)) && !(gBattleMons[BATTLE_OPPOSITE(battler)].status2 & (STATUS2_TRANSFORMED | STATUS2_SUBSTITUTE)) @@ -4584,13 +4628,6 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 effect++; } break; - case ABILITY_TRACE: - if (!(gSpecialStatuses[battler].traced)) - { - gBattleResources->flags->flags[battler] |= RESOURCE_FLAG_TRACED; - gSpecialStatuses[battler].traced = TRUE; - } - break; case ABILITY_CLOUD_NINE: case ABILITY_AIR_LOCK: if (!gSpecialStatuses[battler].switchInAbilityDone) @@ -5981,48 +6018,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 } } break; - case ABILITYEFFECT_TRACE1: - case ABILITYEFFECT_TRACE2: - for (i = 0; i < gBattlersCount; i++) - { - if (gBattleMons[i].ability == ABILITY_TRACE && (gBattleResources->flags->flags[i] & RESOURCE_FLAG_TRACED)) - { - u32 chosenTarget; - u32 side = (BATTLE_OPPOSITE(GetBattlerPosition(i))) & BIT_SIDE; // side of the opposing Pokémon - u32 target1 = GetBattlerAtPosition(side); - u32 target2 = GetBattlerAtPosition(side + BIT_FLANK); - - if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) - { - if (!gAbilitiesInfo[gBattleMons[target1].ability].cantBeTraced && gBattleMons[target1].hp != 0 - && !gAbilitiesInfo[gBattleMons[target2].ability].cantBeTraced && gBattleMons[target2].hp != 0) - chosenTarget = GetBattlerAtPosition((RandomPercentage(RNG_TRACE, 50) * 2) | side), effect++; - else if (!gAbilitiesInfo[gBattleMons[target1].ability].cantBeTraced && gBattleMons[target1].hp != 0) - chosenTarget = target1, effect++; - else if (!gAbilitiesInfo[gBattleMons[target2].ability].cantBeTraced && gBattleMons[target2].hp != 0) - chosenTarget = target2, effect++; - } - else - { - if (!gAbilitiesInfo[gBattleMons[target1].ability].cantBeTraced && gBattleMons[target1].hp != 0) - chosenTarget = target1, effect++; - } - if (effect != 0) - { - BattleScriptPushCursorAndCallback(BattleScript_TraceActivatesEnd3); - gBattleResources->flags->flags[i] &= ~RESOURCE_FLAG_TRACED; - gBattleStruct->tracedAbility[i] = gLastUsedAbility = gBattleMons[chosenTarget].ability; - RecordAbilityBattle(chosenTarget, gLastUsedAbility); // Record the opposing battler has this ability - battler = gBattlerAbility = gBattleScripting.battler = i; - - PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, chosenTarget, gBattlerPartyIndexes[chosenTarget]) - PREPARE_ABILITY_BUFFER(gBattleTextBuff2, gLastUsedAbility) - break; - } - } - } - break; case ABILITYEFFECT_NEUTRALIZINGGAS: // Prints message only. separate from ABILITYEFFECT_ON_SWITCHIN bc activates before entry hazards for (i = 0; i < gBattlersCount; i++) diff --git a/test/battle/ability/trace.c b/test/battle/ability/trace.c index bb72f38860bf..acc49bcf1398 100644 --- a/test/battle/ability/trace.c +++ b/test/battle/ability/trace.c @@ -79,3 +79,20 @@ SINGLE_BATTLE_TEST("Trace will copy an opponent's ability whenever it has the ch MESSAGE("Ralts TRACED Foe Torchic's Blaze!"); } } + +DOUBLE_BATTLE_TEST("Trace respects the turn order") +{ + GIVEN { + PLAYER(SPECIES_DEOXYS_SPEED) { Speed(40); Ability(ABILITY_PRESSURE); } + PLAYER(SPECIES_GARDEVOIR) { Speed(20); Ability(ABILITY_TRACE); } + OPPONENT(SPECIES_HIPPOWDON) { Speed(10); Ability(ABILITY_SAND_STREAM); } + OPPONENT(SPECIES_DEOXYS_SPEED) { Speed(30); Ability(ABILITY_PRESSURE); } + } WHEN { + TURN { } + } SCENE { + ABILITY_POPUP(playerLeft, ABILITY_PRESSURE); + ABILITY_POPUP(opponentRight, ABILITY_PRESSURE); + ABILITY_POPUP(playerRight, ABILITY_TRACE); + ABILITY_POPUP(opponentLeft, ABILITY_SAND_STREAM); + } +}