Fix frame counter in multi battles

This commit is contained in:
AlexOn1ine 2026-03-21 10:28:21 +01:00
parent c47809abcd
commit 991f11bec1
5 changed files with 51 additions and 57 deletions

View File

@ -210,7 +210,6 @@ struct AiLogicData
enum Ability abilities[MAX_BATTLERS_COUNT];
enum Item items[MAX_BATTLERS_COUNT];
enum HoldEffect holdEffects[MAX_BATTLERS_COUNT];
u8 holdEffectParams[MAX_BATTLERS_COUNT];
enum Move lastUsedMove[MAX_BATTLERS_COUNT];
u8 hpPercents[MAX_BATTLERS_COUNT];
enum Move partnerMove;
@ -223,6 +222,7 @@ struct AiLogicData
u8 mostSuitableMonId[MAX_BATTLERS_COUNT]; // Stores result of GetMostSuitableMonToSwitchInto, which decides which generic mon the AI would switch into if they decide to switch. This can be overruled by specific mons found in ShouldSwitch; the final resulting mon is stored in AI_monToSwitchIntoId.
enum Move predictedMove[MAX_BATTLERS_COUNT];
u8 resistBerryAffected[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // Tracks whether currently calc'd move is affected by a resist berry into given target
u8 turnOrder[MAX_BATTLERS_COUNT];
// Flags
u32 weatherHasEffect:1; // The same as HasWeatherEffect(). Stored here, so it's called only once.

View File

@ -121,8 +121,6 @@ struct BattleContext
enum HoldEffect holdEffectAtk;
enum HoldEffect holdEffectDef;
u8 aiTurnOrder[MAX_BATTLERS_COUNT];
// Flags
u32 isCrit:1;
u32 randomFactor:1;

View File

@ -643,7 +643,6 @@ void SetBattlerAiData(enum BattlerId battler, struct AiLogicData *aiData)
ability = aiData->abilities[battler] = AI_DecideKnownAbilityForTurn(battler);
aiData->items[battler] = gBattleMons[battler].item;
holdEffect = aiData->holdEffects[battler] = AI_DecideHoldEffectForTurn(battler);
aiData->holdEffectParams[battler] = GetBattlerHoldEffectParam(battler);
aiData->lastUsedMove[battler] = (gLastMoves[battler] == MOVE_UNAVAILABLE) ? MOVE_NONE : gLastMoves[battler];
aiData->hpPercents[battler] = GetHealthPercentage(battler);
aiData->moveLimitations[battler] = CheckMoveLimitations(battler, 0, MOVE_LIMITATIONS_ALL);
@ -678,6 +677,22 @@ static u32 Ai_SetMoveAccuracy(struct AiLogicData *aiData, enum BattlerId battler
}
#undef BYPASSES_ACCURACY_CALC
static void SetBattlerTurnOrder(u8 *aiTurnOrder)
{
for (u32 i = 0; i < gBattlersCount; i++)
{
for (u32 j = 0; j < gBattlersCount; j++)
{
if (AI_WhoStrikesFirst(aiTurnOrder[i], aiTurnOrder[j], MOVE_NONE, MOVE_NONE, DONT_CONSIDER_PRIORITY) == AI_IS_FASTER)
{
u32 temp = aiTurnOrder[i];
aiTurnOrder[i] = aiTurnOrder[j];
aiTurnOrder[j] = temp;
}
}
}
}
void CalcBattlerAiMovesData(struct AiLogicData *aiData, enum BattlerId battlerAtk, enum BattlerId battlerDef, u32 weather, u32 fieldStatus)
{
enum Move move;
@ -723,56 +738,59 @@ static void SetBattlerAiMovesData(struct AiLogicData *aiData, enum BattlerId bat
void SetAiLogicDataForTurn(struct AiLogicData *aiData)
{
u32 battlersCount, weather;
memset(aiData, 0, sizeof(struct AiLogicData));
gAiBattleData->aiUsingGimmick = 0;
if (!(gBattleTypeFlags & BATTLE_TYPE_HAS_AI) && !IsWildMonSmart())
return;
gAiLogicData->aiCalcInProgress = TRUE;
if (DEBUG_AI_DELAY_TIMER)
AIDebugTimerStart();
aiData->weatherHasEffect = HasWeatherEffect();
weather = AI_GetWeather();
u32 weather = AI_GetWeather();
// get/assume all battler data and simulate AI damage
battlersCount = gBattlersCount;
u32 battlersCount = gBattlersCount;
for (enum BattlerId battlerAtk = 0; battlerAtk < battlersCount; battlerAtk++)
for (enum BattlerId battler = 0; battler < battlersCount; battler++)
{
if (!IsBattlerAlive(battlerAtk))
if (!IsBattlerAlive(battler))
continue;
SetBattlerAiData(battlerAtk, aiData);
SetBattlerAiData(battler, aiData);
}
for (enum BattlerId battlerAtk = 0; battlerAtk < battlersCount; battlerAtk++)
for (enum BattlerId battler = 0; battler < battlersCount; battler++)
aiData->turnOrder[battler] = battler;
SetBattlerTurnOrder(aiData->turnOrder);
for (enum BattlerId battler = 0; battler < battlersCount; battler++)
{
if (!IsBattlerAlive(battlerAtk))
if (!IsBattlerAlive(battler))
continue;
SetBattlerAiMovesData(aiData, battlerAtk, battlersCount, weather);
SetBattlerAiMovesData(aiData, battler, battlersCount, weather);
aiData->turnOrder[battler] = battler;
}
for (enum BattlerId battlerAtk = 0; battlerAtk < battlersCount; battlerAtk++)
for (enum BattlerId battler = 0; battler < battlersCount; battler++)
{
// Prediction limited to player side but can be expanded to read partners move in the future
if (!IsOnPlayerSide(battlerAtk) || !CanAiPredictMove(battlerAtk))
if (!IsOnPlayerSide(battler) || !CanAiPredictMove(battler))
continue;
// This can potentially be cleaned up more
BattleAI_SetupAIData(0xF, battlerAtk);
u32 chosenMoveIndex = ChooseMoveOrAction(battlerAtk);
gAiLogicData->predictedMove[battlerAtk] = gBattleMons[battlerAtk].moves[chosenMoveIndex];
BattleAI_SetupAIData(0xF, battler);
u32 chosenMoveIndex = ChooseMoveOrAction(battler);
gAiLogicData->predictedMove[battler] = gBattleMons[battler].moves[chosenMoveIndex];
aiData->predictingMove = RandomPercentage(RNG_AI_PREDICT_MOVE, PREDICT_MOVE_CHANCE);
}
if (DEBUG_AI_DELAY_TIMER)
AIDebugTimerEnd();
gAiLogicData->aiCalcInProgress = FALSE;
}
@ -1272,7 +1290,7 @@ static s32 AI_CheckBadMove(enum BattlerId battlerAtk, enum BattlerId battlerDef,
{
ADJUST_SCORE(-5);
}
// check non-user target
if (moveTarget != TARGET_USER)
{
@ -5068,7 +5086,7 @@ static s32 AI_CalcMoveEffectScore(enum BattlerId battlerAtk, enum BattlerId batt
&& AI_WhoStrikesFirst(battlerAtk, battlerDef, MOVE_FAKE_OUT, MOVE_FAKE_OUT, CONSIDER_PRIORITY) == AI_IS_FASTER)
|| (HasMove(battlerDefPartner, MOVE_FAKE_OUT) && gBattleStruct->battlerState[battlerDefPartner].isFirstTurn
&& AI_WhoStrikesFirst(battlerAtk, battlerDefPartner, MOVE_FAKE_OUT, MOVE_FAKE_OUT, CONSIDER_PRIORITY) == AI_IS_FASTER))
{
{
ADJUST_SCORE(FAST_KILL + 2);
}
// If ally has KO on target's partner, but target can fast KO ally (checking move and priority combinations for everything likely gets a bit complicated)
@ -5091,7 +5109,7 @@ static s32 AI_CalcMoveEffectScore(enum BattlerId battlerAtk, enum BattlerId batt
}
}
// If ally has slow KO with their chosen move, user sees no KOs while outspeeding (checking move and priority combinations for everything likely gets a bit complicated)
else if (hasPartner
else if (hasPartner
&& (atkPartnerKoDef && AI_WhoStrikesFirst(battlerAtkPartner, battlerDef, gBattleMons[battlerAtkPartner].moves[gAiBattleData->chosenMoveIndex[battlerAtkPartner]], predictedMoveSpeedCheck, CONSIDER_PRIORITY) == AI_IS_SLOWER)
&& !(atkKoDef && AI_WhoStrikesFirst(battlerAtk, battlerDef, move, predictedMoveSpeedCheck, DONT_CONSIDER_PRIORITY) == AI_IS_FASTER)
&& !(atkKoDefPartner && AI_WhoStrikesFirst(battlerAtk, battlerDefPartner, move, MOVE_SCRATCH, DONT_CONSIDER_PRIORITY) == AI_IS_FASTER))
@ -5099,7 +5117,7 @@ static s32 AI_CalcMoveEffectScore(enum BattlerId battlerAtk, enum BattlerId batt
ADJUST_SCORE(GOOD_EFFECT);
}
else
{
{
ADJUST_SCORE(DECENT_EFFECT);
}
}

View File

@ -23,7 +23,6 @@
#include "constants/moves.h"
#include "constants/items.h"
static void AI_SetBattlerTurnOrder(u8 *aiTurnOrder);
static u32 GetAIEffectGroup(enum BattleMoveEffects effect);
static u32 GetAIEffectGroupFromMove(enum BattlerId battler, enum Move move);
@ -959,7 +958,6 @@ struct SimulatedDamage AI_CalcDamage(enum Move move, enum BattlerId battlerAtk,
ctx.abilityDef = AI_GetMoldBreakerSanitizedAbility(battlerAtk, ctx.abilityAtk, aiData->abilities[battlerDef], ctx.holdEffectDef, move);
ctx.isCrit = ShouldCalcCritDamage(&ctx);
ctx.typeEffectivenessModifier = CalcTypeEffectivenessMultiplier(&ctx);
AI_SetBattlerTurnOrder(ctx.aiTurnOrder);
u32 movePower = GetMovePower(move);
@ -2188,7 +2186,7 @@ bool32 ShouldTryOHKO(enum BattlerId battlerAtk, enum BattlerId battlerDef, enum
u32 accuracy = gAiLogicData->moveAccuracy[battlerAtk][battlerDef][gAiThinkingStruct->movesetIndex];
gPotentialItemEffectBattler = battlerDef;
if (holdEffect == HOLD_EFFECT_FOCUS_BAND && (Random() % 100) < gAiLogicData->holdEffectParams[battlerDef])
if (holdEffect == HOLD_EFFECT_FOCUS_BAND && (Random() % 100) < GetBattlerHoldEffectParam(battlerDef))
return FALSE; //probabilistically speaking, focus band should activate so dont OHKO
else if (holdEffect == HOLD_EFFECT_FOCUS_SASH && AI_BattlerAtMaxHp(battlerDef))
return FALSE;
@ -6501,23 +6499,3 @@ bool32 IsPartyMonPlannedToBeSwitchedInByPartner(u32 partyIndex, enum BattlerId b
return TRUE;
return FALSE;
}
static void AI_SetBattlerTurnOrder(u8 *aiTurnOrder)
{
for (u32 battler = 0; battler < gBattlersCount; battler++)
aiTurnOrder[battler] = battler;
for (u32 i = 0; i < gBattlersCount; i++)
{
for (u32 j = 0; j < gBattlersCount; j++)
{
if (AI_WhoStrikesFirst(aiTurnOrder[i], aiTurnOrder[j], MOVE_NONE, MOVE_NONE, DONT_CONSIDER_PRIORITY) == AI_IS_FASTER)
{
u32 temp = aiTurnOrder[i];
aiTurnOrder[i] = aiTurnOrder[j];
aiTurnOrder[j] = temp;
}
}
}
}

View File

@ -1113,25 +1113,25 @@ bool32 IsLastMonToMove(enum BattlerId battler)
return TRUE;
}
static u32 GetAiTurnOrder(u8 *aiTurnOrder, enum BattlerId battler)
static u32 GetAiTurnOrder(enum BattlerId battler)
{
for (u32 i = 0; i < gBattlersCount; i++)
{
if (aiTurnOrder[i] == battler)
if (gAiLogicData->turnOrder[i] == battler)
return i;
}
return 0;
}
static bool32 Ai_AttackerMovesAfterTarget(struct BattleContext *ctx)
static bool32 Ai_AttackerMovesAfterTarget(enum BattlerId battlerAtk, enum BattlerId battlerDef)
{
return GetAiTurnOrder(ctx->aiTurnOrder, ctx->battlerAtk) > GetAiTurnOrder(ctx->aiTurnOrder, ctx->battlerDef);
return GetAiTurnOrder(battlerAtk) > GetAiTurnOrder(battlerDef);
}
static bool32 Ai_AttackerMovesLast(struct BattleContext *ctx)
static bool32 Ai_AttackerMovesLast(enum BattlerId battlerAtk)
{
u32 numAliveBattlers = 0;
u32 battlerTurnOrder = GetAiTurnOrder(ctx->aiTurnOrder, ctx->battlerAtk);
u32 battlerTurnOrder = GetAiTurnOrder(battlerAtk);
for (enum BattlerId battler = B_BATTLER_0; battler < gBattlersCount; battler++)
{
@ -6344,7 +6344,7 @@ static inline u32 CalcMoveBasePower(struct BattleContext *ctx)
case EFFECT_PAYBACK:
if (ctx->aiCalc)
{
if (Ai_AttackerMovesAfterTarget(ctx))
if (Ai_AttackerMovesAfterTarget(battlerAtk, battlerDef))
basePower *= 2;
}
else if (HasBattlerActedThisTurn(battlerDef)
@ -6356,7 +6356,7 @@ static inline u32 CalcMoveBasePower(struct BattleContext *ctx)
case EFFECT_BOLT_BEAK:
if (ctx->aiCalc)
{
if (!Ai_AttackerMovesAfterTarget(ctx))
if (!Ai_AttackerMovesAfterTarget(battlerAtk, battlerDef))
basePower *= 2;
}
else if (!HasBattlerActedThisTurn(battlerDef)
@ -6567,7 +6567,7 @@ static inline u32 CalcMoveBasePowerAfterModifiers(struct BattleContext *ctx)
if (ctx->aiCalc)
{
if (Ai_AttackerMovesLast(ctx))
if (Ai_AttackerMovesLast(battlerAtk))
modifier = uq4_12_multiply(modifier, UQ_4_12(1.3));
}
else if (IsLastMonToMove(battlerAtk))