Skip to content

Commit

Permalink
Fixes Trace turn order (rh-hideout#4941)
Browse files Browse the repository at this point in the history
* Fixes Trace turn order

* Update battle_script_commands.c

* Update test/battle/ability/trace.c

---------

Co-authored-by: Bassoonian <iasperbassoonian@gmail.com>
  • Loading branch information
AlexOn1ine and Bassoonian authored Jul 10, 2024
1 parent 0613af5 commit 7b6ff1b
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 66 deletions.
2 changes: 1 addition & 1 deletion include/battle.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
20 changes: 9 additions & 11 deletions include/battle_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 0 additions & 2 deletions src/battle_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
13 changes: 9 additions & 4 deletions src/battle_script_commands.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand Down
92 changes: 44 additions & 48 deletions src/battle_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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++)
Expand Down
17 changes: 17 additions & 0 deletions test/battle/ability/trace.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

0 comments on commit 7b6ff1b

Please sign in to comment.