AI: Handle MOVE_UNAVAILABLE in last used moves (#8219)

This commit is contained in:
Martin Griffin 2025-11-12 21:47:50 +00:00 committed by GitHub
parent 2373f0283b
commit fe85da81fc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 21 additions and 23 deletions

View File

@ -553,7 +553,7 @@ void SetBattlerAiData(u32 battler, struct AiLogicData *aiData)
aiData->items[battler] = gBattleMons[battler].item; aiData->items[battler] = gBattleMons[battler].item;
holdEffect = aiData->holdEffects[battler] = AI_DecideHoldEffectForTurn(battler); holdEffect = aiData->holdEffects[battler] = AI_DecideHoldEffectForTurn(battler);
aiData->holdEffectParams[battler] = GetBattlerHoldEffectParam(battler); aiData->holdEffectParams[battler] = GetBattlerHoldEffectParam(battler);
aiData->lastUsedMove[battler] = gLastMoves[battler]; aiData->lastUsedMove[battler] = (gLastMoves[battler] == MOVE_UNAVAILABLE) ? MOVE_NONE : gLastMoves[battler];
aiData->hpPercents[battler] = GetHealthPercentage(battler); aiData->hpPercents[battler] = GetHealthPercentage(battler);
aiData->moveLimitations[battler] = CheckMoveLimitations(battler, 0, MOVE_LIMITATIONS_ALL); aiData->moveLimitations[battler] = CheckMoveLimitations(battler, 0, MOVE_LIMITATIONS_ALL);
aiData->speedStats[battler] = GetBattlerTotalSpeedStat(battler, ability, holdEffect); aiData->speedStats[battler] = GetBattlerTotalSpeedStat(battler, ability, holdEffect);
@ -1788,7 +1788,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
{ {
if (AI_IsFaster(battlerAtk, battlerDef, move, predictedMoveSpeedCheck, CONSIDER_PRIORITY)) // Attacker should go first if (AI_IsFaster(battlerAtk, battlerDef, move, predictedMoveSpeedCheck, CONSIDER_PRIORITY)) // Attacker should go first
{ {
if (gLastMoves[battlerDef] == MOVE_NONE || gLastMoves[battlerDef] == 0xFFFF) if (aiData->lastUsedMove[battlerDef] == MOVE_NONE)
ADJUST_SCORE(-10); // no anticipated move to disable ADJUST_SCORE(-10); // no anticipated move to disable
} }
else if (predictedMove == MOVE_NONE) else if (predictedMove == MOVE_NONE)
@ -1810,7 +1810,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
{ {
if (AI_IsFaster(battlerAtk, battlerDef, move, predictedMoveSpeedCheck, CONSIDER_PRIORITY)) // Attacker should go first if (AI_IsFaster(battlerAtk, battlerDef, move, predictedMoveSpeedCheck, CONSIDER_PRIORITY)) // Attacker should go first
{ {
if (gLastMoves[battlerDef] == MOVE_NONE || gLastMoves[battlerDef] == 0xFFFF) if (aiData->lastUsedMove[battlerDef] == MOVE_NONE)
ADJUST_SCORE(-10); // no anticipated move to encore ADJUST_SCORE(-10); // no anticipated move to encore
} }
else if (predictedMove == MOVE_NONE) else if (predictedMove == MOVE_NONE)
@ -2214,13 +2214,12 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
case EFFECT_MIMIC: case EFFECT_MIMIC:
if (AI_IsFaster(battlerAtk, battlerDef, move, predictedMoveSpeedCheck, CONSIDER_PRIORITY)) // Attacker should go first if (AI_IsFaster(battlerAtk, battlerDef, move, predictedMoveSpeedCheck, CONSIDER_PRIORITY)) // Attacker should go first
{ {
if (gLastMoves[battlerDef] == MOVE_NONE if (aiData->lastUsedMove[battlerDef] == MOVE_NONE)
|| gLastMoves[battlerDef] == 0xFFFF)
ADJUST_SCORE(-10); ADJUST_SCORE(-10);
} }
else if (predictedMove == MOVE_NONE) else if (predictedMove == MOVE_NONE)
{ {
// TODO predicted move separate from gLastMoves // TODO predicted move separate from aiData->lastUsedMove
ADJUST_SCORE(-10); ADJUST_SCORE(-10);
} }
break; break;
@ -2244,7 +2243,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
ADJUST_SCORE(-8); ADJUST_SCORE(-8);
break; break;
case EFFECT_SKETCH: case EFFECT_SKETCH:
if (gLastMoves[battlerDef] == MOVE_NONE) if (aiData->lastUsedMove[battlerDef] == MOVE_NONE)
ADJUST_SCORE(-10); ADJUST_SCORE(-10);
break; break;
case EFFECT_DESTINY_BOND: case EFFECT_DESTINY_BOND:
@ -2776,13 +2775,13 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
if (AI_IsSlower(battlerAtk, battlerDef, move, predictedMoveSpeedCheck, CONSIDER_PRIORITY)) if (AI_IsSlower(battlerAtk, battlerDef, move, predictedMoveSpeedCheck, CONSIDER_PRIORITY))
instructedMove = predictedMove; instructedMove = predictedMove;
else else
instructedMove = gLastMoves[battlerDef]; instructedMove = aiData->lastUsedMove[battlerDef];
if (instructedMove == MOVE_NONE if (instructedMove == MOVE_NONE
|| IsMoveInstructBanned(instructedMove) || IsMoveInstructBanned(instructedMove)
|| MoveHasAdditionalEffectSelf(instructedMove, MOVE_EFFECT_RECHARGE) || MoveHasAdditionalEffectSelf(instructedMove, MOVE_EFFECT_RECHARGE)
|| IsZMove(instructedMove) || IsZMove(instructedMove)
|| (gLockedMoves[battlerDef] != 0 && gLockedMoves[battlerDef] != 0xFFFF) || (gLockedMoves[battlerDef] != MOVE_NONE && gLockedMoves[battlerDef] != MOVE_UNAVAILABLE)
|| gBattleMons[battlerDef].volatiles.multipleTurns || gBattleMons[battlerDef].volatiles.multipleTurns
|| PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove)) || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove))
{ {
@ -3677,7 +3676,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
if (AI_IsFaster(battlerAtk, battlerAtkPartner, move, predictedMoveSpeedCheck, CONSIDER_PRIORITY)) if (AI_IsFaster(battlerAtk, battlerAtkPartner, move, predictedMoveSpeedCheck, CONSIDER_PRIORITY))
instructedMove = aiData->partnerMove; instructedMove = aiData->partnerMove;
else else
instructedMove = gLastMoves[battlerAtkPartner]; instructedMove = aiData->lastUsedMove[battlerAtkPartner];
if (instructedMove != MOVE_NONE if (instructedMove != MOVE_NONE
&& !IsBattleMoveStatus(instructedMove) && !IsBattleMoveStatus(instructedMove)
@ -4468,9 +4467,9 @@ static s32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move, stru
case EFFECT_MIMIC: case EFFECT_MIMIC:
if (AI_IsFaster(battlerAtk, battlerDef, move, predictedMoveSpeedCheck, CONSIDER_PRIORITY)) if (AI_IsFaster(battlerAtk, battlerDef, move, predictedMoveSpeedCheck, CONSIDER_PRIORITY))
{ {
if (gLastMoves[battlerDef] != MOVE_NONE && gLastMoves[battlerDef] != 0xFFFF if (aiData->lastUsedMove[battlerDef] != MOVE_NONE
&& (GetMoveEffect(gLastMoves[battlerDef]) != GetMoveEffect(move))) && (GetMoveEffect(aiData->lastUsedMove[battlerDef]) != GetMoveEffect(move)))
return AI_CheckViability(battlerAtk, battlerDef, gLastMoves[battlerDef], score); return AI_CheckViability(battlerAtk, battlerDef, aiData->lastUsedMove[battlerDef], score);
} }
break; break;
case EFFECT_LEECH_SEED: case EFFECT_LEECH_SEED:
@ -4537,12 +4536,11 @@ static s32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move, stru
if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX) if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
break; break;
else if (gDisableStructs[battlerDef].disableTimer == 0 else if (gDisableStructs[battlerDef].disableTimer == 0
&& (gLastMoves[battlerDef] != MOVE_NONE) && (aiData->lastUsedMove[battlerDef] != MOVE_NONE)
&& (gLastMoves[battlerDef] != 0xFFFF)
&& (B_MENTAL_HERB < GEN_5 || aiData->holdEffects[battlerDef] != HOLD_EFFECT_MENTAL_HERB) && (B_MENTAL_HERB < GEN_5 || aiData->holdEffects[battlerDef] != HOLD_EFFECT_MENTAL_HERB)
&& (AI_IsFaster(battlerAtk, battlerDef, move, predictedMoveSpeedCheck, CONSIDER_PRIORITY))) && (AI_IsFaster(battlerAtk, battlerDef, move, predictedMoveSpeedCheck, CONSIDER_PRIORITY)))
{ {
if (CanTargetMoveFaintAi(gLastMoves[battlerDef], battlerDef, battlerAtk, 1)) if (CanTargetMoveFaintAi(aiData->lastUsedMove[battlerDef], battlerDef, battlerAtk, 1))
ADJUST_SCORE(GOOD_EFFECT); // Disable move that can kill attacker ADJUST_SCORE(GOOD_EFFECT); // Disable move that can kill attacker
} }
break; break;
@ -4551,9 +4549,9 @@ static s32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move, stru
if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX) if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
break; break;
bool32 encourage = gBattleMoveEffects[GetMoveEffect(gLastMoves[battlerDef])].encourageEncore; bool32 encourage = gBattleMoveEffects[GetMoveEffect(aiData->lastUsedMove[battlerDef])].encourageEncore;
switch(GetMoveNonVolatileStatus(gLastMoves[battlerDef])) switch(GetMoveNonVolatileStatus(aiData->lastUsedMove[battlerDef]))
{ {
case MOVE_EFFECT_POISON: case MOVE_EFFECT_POISON:
case MOVE_EFFECT_PARALYSIS: case MOVE_EFFECT_PARALYSIS:
@ -4615,7 +4613,7 @@ static s32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move, stru
} }
break; break;
case EFFECT_PROTECT: case EFFECT_PROTECT:
if (predictedMove == 0xFFFF) if (predictedMove == MOVE_UNAVAILABLE)
predictedMove = MOVE_NONE; predictedMove = MOVE_NONE;
enum ProtectMethod protectMethod = GetMoveProtectMethod(move); enum ProtectMethod protectMethod = GetMoveProtectMethod(move);
switch (protectMethod) switch (protectMethod)
@ -4918,7 +4916,7 @@ static s32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move, stru
&& (!IsPowderMove(move) || IsAffectedByPowderMove(battlerDef, aiData->abilities[battlerDef], aiData->holdEffects[battlerDef]))) && (!IsPowderMove(move) || IsAffectedByPowderMove(battlerDef, aiData->abilities[battlerDef], aiData->holdEffects[battlerDef])))
// Rage Powder doesn't affect powder immunities // Rage Powder doesn't affect powder immunities
{ {
u32 predictedMoveOnPartner = gLastMoves[BATTLE_PARTNER(battlerAtk)]; u32 predictedMoveOnPartner = aiData->lastUsedMove[BATTLE_PARTNER(battlerAtk)];
if (predictedMoveOnPartner != MOVE_NONE && !IsBattleMoveStatus(predictedMoveOnPartner)) if (predictedMoveOnPartner != MOVE_NONE && !IsBattleMoveStatus(predictedMoveOnPartner))
ADJUST_SCORE(GOOD_EFFECT); ADJUST_SCORE(GOOD_EFFECT);
} }
@ -6264,7 +6262,7 @@ static s32 AI_PreferBatonPass(u32 battlerAtk, u32 battlerDef, u32 move, s32 scor
ADJUST_SCORE(DECENT_EFFECT); ADJUST_SCORE(DECENT_EFFECT);
break; break;
case EFFECT_PROTECT: case EFFECT_PROTECT:
if (GetProtectType(GetMoveProtectMethod(gLastMoves[battlerAtk])) == PROTECT_TYPE_SINGLE) if (GetProtectType(GetMoveProtectMethod(gAiLogicData->lastUsedMove[battlerAtk])) == PROTECT_TYPE_SINGLE)
ADJUST_SCORE(-2); ADJUST_SCORE(-2);
else else
ADJUST_SCORE(DECENT_EFFECT); ADJUST_SCORE(DECENT_EFFECT);

View File

@ -923,7 +923,7 @@ static bool32 FindMonWithFlagsAndSuperEffective(u32 battler, u16 flags, u32 perc
for (j = 0; j < MAX_MON_MOVES; j++) for (j = 0; j < MAX_MON_MOVES; j++)
{ {
move = GetMonData(&party[i], MON_DATA_MOVE1 + j); move = GetMonData(&party[i], MON_DATA_MOVE1 + j);
if (move == 0) if (move == MOVE_NONE)
continue; continue;
if (AI_GetMoveEffectiveness(move, battler, battlerIn1) >= UQ_4_12(2.0) && (RandomPercentage(RNG_AI_SWITCH_SE_DEFENSIVE, percentChance) || gAiLogicData->aiPredictionInProgress)) if (AI_GetMoveEffectiveness(move, battler, battlerIn1) >= UQ_4_12(2.0) && (RandomPercentage(RNG_AI_SWITCH_SE_DEFENSIVE, percentChance) || gAiLogicData->aiPredictionInProgress))

View File

@ -4136,7 +4136,7 @@ u32 GetAllyChosenMove(u32 battlerId)
if (!IsBattlerAlive(partnerBattler) || !IsAiBattlerAware(partnerBattler)) if (!IsBattlerAlive(partnerBattler) || !IsAiBattlerAware(partnerBattler))
return MOVE_NONE; return MOVE_NONE;
else if (partnerBattler > battlerId) // Battler with the lower id chooses the move first. else if (partnerBattler > battlerId) // Battler with the lower id chooses the move first.
return gLastMoves[partnerBattler]; return gAiLogicData->lastUsedMove[partnerBattler];
else else
return GetChosenMoveFromPosition(partnerBattler); return GetChosenMoveFromPosition(partnerBattler);
} }