Rework switch AI and add more tests for ace pokemon flags (#8321)
This commit is contained in:
parent
c7c97531ec
commit
a4482f0ee2
@ -38,7 +38,8 @@ enum ShouldSwitchScenario
|
|||||||
enum SwitchType
|
enum SwitchType
|
||||||
{
|
{
|
||||||
SWITCH_AFTER_KO,
|
SWITCH_AFTER_KO,
|
||||||
SWITCH_MID_BATTLE,
|
SWITCH_MID_BATTLE_FORCED,
|
||||||
|
SWITCH_MID_BATTLE_OPTIONAL,
|
||||||
};
|
};
|
||||||
|
|
||||||
void GetAIPartyIndexes(u32 battlerId, s32 *firstId, s32 *lastId);
|
void GetAIPartyIndexes(u32 battlerId, s32 *firstId, s32 *lastId);
|
||||||
|
|||||||
@ -359,13 +359,13 @@ void ComputeBattlerDecisions(u32 battler)
|
|||||||
if (isAiBattler || CanAiPredictMove())
|
if (isAiBattler || CanAiPredictMove())
|
||||||
{
|
{
|
||||||
// Risky AI switches aggressively even mid battle
|
// Risky AI switches aggressively even mid battle
|
||||||
enum SwitchType switchType = (gAiThinkingStruct->aiFlags[battler] & AI_FLAG_RISKY) ? SWITCH_AFTER_KO : SWITCH_MID_BATTLE;
|
enum SwitchType switchType = (gAiThinkingStruct->aiFlags[battler] & AI_FLAG_RISKY) ? SWITCH_AFTER_KO : SWITCH_MID_BATTLE_OPTIONAL;
|
||||||
|
|
||||||
gAiLogicData->aiCalcInProgress = TRUE;
|
gAiLogicData->aiCalcInProgress = TRUE;
|
||||||
|
|
||||||
// Setup battler and prediction data
|
// Setup battler and prediction data
|
||||||
BattleAI_SetupAIData(0xF, battler);
|
BattleAI_SetupAIData(0xF, battler);
|
||||||
SetupAIPredictionData(battler, switchType);
|
SetupAIPredictionData(battler, SWITCH_MID_BATTLE_OPTIONAL);
|
||||||
|
|
||||||
// AI's own switching data
|
// AI's own switching data
|
||||||
if (isAiBattler)
|
if (isAiBattler)
|
||||||
|
|||||||
@ -1517,22 +1517,19 @@ static u32 GetBestMonDmg(struct Pokemon *party, int firstId, int lastId, u8 inva
|
|||||||
return bestMonId;
|
return bestMonId;
|
||||||
}
|
}
|
||||||
|
|
||||||
static u32 GetFirstNonInvalidMon(u32 firstId, u32 lastId, u32 invalidMons, u32 battlerIn1, u32 battlerIn2)
|
static u32 GetFirstNonInvalidMon(u32 firstId, u32 lastId, u32 invalidMons)
|
||||||
{
|
{
|
||||||
if (!IsDoubleBattle())
|
u32 chosenMonId = PARTY_SIZE;
|
||||||
return PARTY_SIZE;
|
for (u32 i = (lastId-1); i > firstId; i--)
|
||||||
|
|
||||||
if (PARTY_SIZE != gBattleStruct->monToSwitchIntoId[battlerIn1]
|
|
||||||
&& PARTY_SIZE != gBattleStruct->monToSwitchIntoId[battlerIn2])
|
|
||||||
return PARTY_SIZE;
|
|
||||||
|
|
||||||
for (u32 chosenMonId = (lastId-1); chosenMonId >= firstId; chosenMonId--)
|
|
||||||
{
|
{
|
||||||
if ((1 << (chosenMonId)) & invalidMons)
|
if (!((1 << i) & invalidMons))
|
||||||
continue;
|
{
|
||||||
return chosenMonId; // first non invalid mon found
|
// first non invalid mon found
|
||||||
|
chosenMonId = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return PARTY_SIZE;
|
return chosenMonId;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool32 IsMonGrounded(enum HoldEffect heldItemEffect, enum Ability ability, enum Type type1, enum Type type2)
|
bool32 IsMonGrounded(enum HoldEffect heldItemEffect, enum Ability ability, enum Type type1, enum Type type2)
|
||||||
@ -2125,7 +2122,7 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
|
|||||||
int batonPassId = PARTY_SIZE, typeMatchupId = PARTY_SIZE, typeMatchupEffectiveId = PARTY_SIZE, defensiveMonId = PARTY_SIZE, aceMonId = PARTY_SIZE, trapperId = PARTY_SIZE;
|
int batonPassId = PARTY_SIZE, typeMatchupId = PARTY_SIZE, typeMatchupEffectiveId = PARTY_SIZE, defensiveMonId = PARTY_SIZE, aceMonId = PARTY_SIZE, trapperId = PARTY_SIZE;
|
||||||
int i, j, aliveCount = 0, bits = 0, aceMonCount = 0;
|
int i, j, aliveCount = 0, bits = 0, aceMonCount = 0;
|
||||||
s32 defensiveMonHitKOThreshold = 3; // 3HKO threshold that candidate defensive mons must exceed
|
s32 defensiveMonHitKOThreshold = 3; // 3HKO threshold that candidate defensive mons must exceed
|
||||||
s32 playerMonHP = gBattleMons[opposingBattler].hp, maxDamageDealt = 0, damageDealt = 0;
|
s32 playerMonHP = gBattleMons[opposingBattler].hp, maxDamageDealt = 0, damageDealt = 0, monMaxDamage = 0;
|
||||||
u32 aiMove, hitsToKOAI, hitsToKOPlayer, hitsToKOAIPriority, bestPlayerMove = MOVE_NONE, bestPlayerPriorityMove = MOVE_NONE, maxHitsToKO = 0;
|
u32 aiMove, hitsToKOAI, hitsToKOPlayer, hitsToKOAIPriority, bestPlayerMove = MOVE_NONE, bestPlayerPriorityMove = MOVE_NONE, maxHitsToKO = 0;
|
||||||
u32 bestResist = UQ_4_12(2.0), bestResistEffective = UQ_4_12(2.0), typeMatchup; // 2.0 is the default "Neutral" matchup from GetBattleMonTypeMatchup
|
u32 bestResist = UQ_4_12(2.0), bestResistEffective = UQ_4_12(2.0), typeMatchup; // 2.0 is the default "Neutral" matchup from GetBattleMonTypeMatchup
|
||||||
bool32 isFreeSwitch = IsFreeSwitch(switchType, battlerIn1, opposingBattler), isSwitchinFirst, isSwitchinFirstPriority, canSwitchinWin1v1;
|
bool32 isFreeSwitch = IsFreeSwitch(switchType, battlerIn1, opposingBattler), isSwitchinFirst, isSwitchinFirstPriority, canSwitchinWin1v1;
|
||||||
@ -2167,6 +2164,8 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
|
|||||||
hitsToKOAIPriority = GetSwitchinHitsToKO(GetMaxPriorityDamagePlayerCouldDealToSwitchin(battler, opposingBattler, gAiLogicData->switchinCandidate.battleMon, &bestPlayerPriorityMove), battler);
|
hitsToKOAIPriority = GetSwitchinHitsToKO(GetMaxPriorityDamagePlayerCouldDealToSwitchin(battler, opposingBattler, gAiLogicData->switchinCandidate.battleMon, &bestPlayerPriorityMove), battler);
|
||||||
typeMatchup = GetBattleMonTypeMatchup(gBattleMons[opposingBattler], gAiLogicData->switchinCandidate.battleMon);
|
typeMatchup = GetBattleMonTypeMatchup(gBattleMons[opposingBattler], gAiLogicData->switchinCandidate.battleMon);
|
||||||
|
|
||||||
|
monMaxDamage = 0;
|
||||||
|
|
||||||
// Check through current mon's moves
|
// Check through current mon's moves
|
||||||
for (j = 0; j < MAX_MON_MOVES; j++)
|
for (j = 0; j < MAX_MON_MOVES; j++)
|
||||||
{
|
{
|
||||||
@ -2232,6 +2231,8 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
|
|||||||
&& damageDealt < playerMonHP)
|
&& damageDealt < playerMonHP)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (damageDealt > monMaxDamage)
|
||||||
|
monMaxDamage = damageDealt;
|
||||||
// Check that mon isn't one shot and set best damage mon
|
// Check that mon isn't one shot and set best damage mon
|
||||||
if (damageDealt > maxDamageDealt)
|
if (damageDealt > maxDamageDealt)
|
||||||
{
|
{
|
||||||
@ -2275,6 +2276,8 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
|
|||||||
trapperId = i;
|
trapperId = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (monMaxDamage == 0)
|
||||||
|
invalidMons |= 1u << i;
|
||||||
}
|
}
|
||||||
|
|
||||||
batonPassId = GetRandomSwitchinWithBatonPass(aliveCount, bits, firstId, lastId, i);
|
batonPassId = GetRandomSwitchinWithBatonPass(aliveCount, bits, firstId, lastId, i);
|
||||||
@ -2304,16 +2307,19 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
|
|||||||
else if (batonPassId != PARTY_SIZE) return batonPassId;
|
else if (batonPassId != PARTY_SIZE) return batonPassId;
|
||||||
else if (generic1v1MonId != PARTY_SIZE) return generic1v1MonId;
|
else if (generic1v1MonId != PARTY_SIZE) return generic1v1MonId;
|
||||||
}
|
}
|
||||||
// If ace mon is the last available Pokemon and U-Turn/Volt Switch or Eject Pack/Button was used - switch to the mon.
|
|
||||||
if (aceMonId != PARTY_SIZE && CountUsablePartyMons(battler) <= aceMonCount
|
if (switchType == SWITCH_MID_BATTLE_OPTIONAL)
|
||||||
&& (IsSwitchOutEffect(GetMoveEffect(gCurrentMove)) || gAiLogicData->ejectButtonSwitch || gAiLogicData->ejectPackSwitch))
|
return PARTY_SIZE;
|
||||||
return aceMonId;
|
|
||||||
|
|
||||||
// Fallback
|
// Fallback
|
||||||
u32 bestMonId = GetFirstNonInvalidMon(firstId, lastId, invalidMons, battlerIn1, battlerIn2);
|
u32 bestMonId = GetFirstNonInvalidMon(firstId, lastId, invalidMons);
|
||||||
if (bestMonId != PARTY_SIZE)
|
if (bestMonId != PARTY_SIZE)
|
||||||
return bestMonId;
|
return bestMonId;
|
||||||
|
|
||||||
|
// If ace mon is the last available Pokemon and U-Turn/Volt Switch or Eject Pack/Button was used - switch to the mon.
|
||||||
|
if (aceMonId != PARTY_SIZE && CountUsablePartyMons(battler) <= aceMonCount)
|
||||||
|
return aceMonId;
|
||||||
|
|
||||||
return PARTY_SIZE;
|
return PARTY_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2429,16 +2435,17 @@ u32 GetMostSuitableMonToSwitchInto(u32 battler, enum SwitchType switchType)
|
|||||||
if (bestMonId != PARTY_SIZE)
|
if (bestMonId != PARTY_SIZE)
|
||||||
return bestMonId;
|
return bestMonId;
|
||||||
|
|
||||||
// If ace mon is the last available Pokemon and U-Turn/Volt Switch or Eject Pack/Button was used - switch to the mon.
|
if (switchType == SWITCH_MID_BATTLE_OPTIONAL)
|
||||||
if (aceMonId != PARTY_SIZE && CountUsablePartyMons(battler) <= aceMonCount
|
return PARTY_SIZE;
|
||||||
&& (IsSwitchOutEffect(GetMoveEffect(gCurrentMove)) || gAiLogicData->ejectButtonSwitch || gAiLogicData->ejectPackSwitch))
|
|
||||||
return aceMonId;
|
|
||||||
|
|
||||||
// Fallback
|
// Fallback
|
||||||
bestMonId = GetFirstNonInvalidMon(firstId, lastId, invalidMons, battlerIn1, battlerIn2);
|
bestMonId = GetFirstNonInvalidMon(firstId, lastId, invalidMons);
|
||||||
if (bestMonId != PARTY_SIZE)
|
if (bestMonId != PARTY_SIZE)
|
||||||
return bestMonId;
|
return bestMonId;
|
||||||
|
|
||||||
|
if (aceMonId != PARTY_SIZE && CountUsablePartyMons(battler) <= aceMonCount)
|
||||||
|
return aceMonId;
|
||||||
|
|
||||||
return PARTY_SIZE;
|
return PARTY_SIZE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4567,7 +4567,9 @@ s32 CountUsablePartyMons(u32 battlerId)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
for (i = 0; i < PARTY_SIZE; i++)
|
s32 firstId, lastId;
|
||||||
|
GetAIPartyIndexes(battlerId, &firstId, &lastId);
|
||||||
|
for (i = firstId; i < lastId; i++)
|
||||||
{
|
{
|
||||||
if (i != battlerOnField1 && i != battlerOnField2
|
if (i != battlerOnField1 && i != battlerOnField2
|
||||||
&& GetMonData(&party[i], MON_DATA_HP) != 0
|
&& GetMonData(&party[i], MON_DATA_HP) != 0
|
||||||
|
|||||||
@ -51,8 +51,6 @@ static void OpponentHandleChoosePokemon(u32 battler);
|
|||||||
static void OpponentHandleIntroTrainerBallThrow(u32 battler);
|
static void OpponentHandleIntroTrainerBallThrow(u32 battler);
|
||||||
static void OpponentHandleDrawPartyStatusSummary(u32 battler);
|
static void OpponentHandleDrawPartyStatusSummary(u32 battler);
|
||||||
static void OpponentHandleEndLinkBattle(u32 battler);
|
static void OpponentHandleEndLinkBattle(u32 battler);
|
||||||
static u8 CountAIAliveNonEggMonsExcept(u8 slotToIgnore);
|
|
||||||
|
|
||||||
static void OpponentBufferRunCommand(u32 battler);
|
static void OpponentBufferRunCommand(u32 battler);
|
||||||
|
|
||||||
static void (*const sOpponentBufferCommands[CONTROLLER_CMDS_COUNT])(u32 battler) =
|
static void (*const sOpponentBufferCommands[CONTROLLER_CMDS_COUNT])(u32 battler) =
|
||||||
@ -527,70 +525,9 @@ static void OpponentHandleChooseItem(u32 battler)
|
|||||||
BtlController_Complete(battler);
|
BtlController_Complete(battler);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool32 IsAcePokemon(u32 chosenMonId, u32 pokemonInBattle, u32 battler)
|
|
||||||
{
|
|
||||||
return gAiThinkingStruct->aiFlags[battler] & AI_FLAG_ACE_POKEMON
|
|
||||||
&& (chosenMonId == CalculateEnemyPartyCountInSide(battler) - 1)
|
|
||||||
&& CountAIAliveNonEggMonsExcept(PARTY_SIZE) != pokemonInBattle;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool32 IsDoubleAceSlot(u32 battler, u32 partyId)
|
|
||||||
{
|
|
||||||
u32 partyCountEnd;
|
|
||||||
|
|
||||||
if (!(gAiThinkingStruct->aiFlags[battler] & AI_FLAG_DOUBLE_ACE_POKEMON))
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
partyCountEnd = CalculateEnemyPartyCountInSide(battler);
|
|
||||||
if (partyCountEnd == 0)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
if (partyId == partyCountEnd - 1)
|
|
||||||
return TRUE;
|
|
||||||
if (partyCountEnd > 1 && partyId == partyCountEnd - 2)
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool32 IsDoubleAcePokemon(u32 chosenMonId, u32 pokemonInBattle, u32 battler)
|
|
||||||
{
|
|
||||||
s32 battler1, battler2, firstId, lastId;
|
|
||||||
s32 i;
|
|
||||||
|
|
||||||
if (!IsDoubleAceSlot(battler, chosenMonId))
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
if (!IsDoubleBattle())
|
|
||||||
{
|
|
||||||
battler2 = battler1 = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
battler1 = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
|
|
||||||
battler2 = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT);
|
|
||||||
}
|
|
||||||
|
|
||||||
GetAIPartyIndexes(battler, &firstId, &lastId);
|
|
||||||
for (i = firstId; i < lastId; i++)
|
|
||||||
{
|
|
||||||
if (!IsValidForBattle(&gEnemyParty[i])
|
|
||||||
|| i == gBattlerPartyIndexes[battler1]
|
|
||||||
|| i == gBattlerPartyIndexes[battler2]
|
|
||||||
|| i == chosenMonId)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!IsAcePokemon(i, pokemonInBattle, battler) && !IsDoubleAceSlot(battler, i))
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void OpponentHandleChoosePokemon(u32 battler)
|
static void OpponentHandleChoosePokemon(u32 battler)
|
||||||
{
|
{
|
||||||
s32 chosenMonId;
|
s32 chosenMonId;
|
||||||
s32 pokemonInBattle = 1;
|
|
||||||
enum SwitchType switchType = SWITCH_AFTER_KO;
|
enum SwitchType switchType = SWITCH_AFTER_KO;
|
||||||
|
|
||||||
// Choosing Revival Blessing target
|
// Choosing Revival Blessing target
|
||||||
@ -602,7 +539,7 @@ static void OpponentHandleChoosePokemon(u32 battler)
|
|||||||
else if (gBattleStruct->AI_monToSwitchIntoId[battler] == PARTY_SIZE)
|
else if (gBattleStruct->AI_monToSwitchIntoId[battler] == PARTY_SIZE)
|
||||||
{
|
{
|
||||||
if (IsSwitchOutEffect(GetMoveEffect(gCurrentMove)) || gAiLogicData->ejectButtonSwitch || gAiLogicData->ejectPackSwitch)
|
if (IsSwitchOutEffect(GetMoveEffect(gCurrentMove)) || gAiLogicData->ejectButtonSwitch || gAiLogicData->ejectPackSwitch)
|
||||||
switchType = SWITCH_MID_BATTLE;
|
switchType = SWITCH_MID_BATTLE_FORCED;
|
||||||
|
|
||||||
// reset the AI data to consider the correct on-field state at time of switch
|
// reset the AI data to consider the correct on-field state at time of switch
|
||||||
SetBattlerAiData(GetBattlerAtPosition(B_POSITION_PLAYER_LEFT), gAiLogicData);
|
SetBattlerAiData(GetBattlerAtPosition(B_POSITION_PLAYER_LEFT), gAiLogicData);
|
||||||
@ -610,7 +547,7 @@ static void OpponentHandleChoosePokemon(u32 battler)
|
|||||||
SetBattlerAiData(GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT), gAiLogicData);
|
SetBattlerAiData(GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT), gAiLogicData);
|
||||||
|
|
||||||
chosenMonId = GetMostSuitableMonToSwitchInto(battler, switchType);
|
chosenMonId = GetMostSuitableMonToSwitchInto(battler, switchType);
|
||||||
if (chosenMonId == PARTY_SIZE)
|
if (chosenMonId == PARTY_SIZE) // Advanced logic failed so we pick the next available battler
|
||||||
{
|
{
|
||||||
s32 battler1, battler2, firstId, lastId;
|
s32 battler1, battler2, firstId, lastId;
|
||||||
|
|
||||||
@ -622,19 +559,14 @@ static void OpponentHandleChoosePokemon(u32 battler)
|
|||||||
{
|
{
|
||||||
battler1 = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
|
battler1 = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
|
||||||
battler2 = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT);
|
battler2 = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT);
|
||||||
pokemonInBattle = 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GetAIPartyIndexes(battler, &firstId, &lastId);
|
GetAIPartyIndexes(battler, &firstId, &lastId);
|
||||||
for (chosenMonId = (lastId-1); chosenMonId >= firstId; chosenMonId--)
|
for (chosenMonId = firstId; chosenMonId < lastId; chosenMonId++)
|
||||||
{
|
{
|
||||||
if (!IsValidForBattle(&gEnemyParty[chosenMonId])
|
if (IsValidForBattle(&gEnemyParty[chosenMonId])
|
||||||
|| chosenMonId == gBattlerPartyIndexes[battler1]
|
&& chosenMonId != gBattlerPartyIndexes[battler1]
|
||||||
|| chosenMonId == gBattlerPartyIndexes[battler2])
|
&& chosenMonId != gBattlerPartyIndexes[battler2])
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!IsAcePokemon(chosenMonId, pokemonInBattle, battler)
|
|
||||||
&& !IsDoubleAcePokemon(chosenMonId, pokemonInBattle, battler))
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -653,22 +585,6 @@ static void OpponentHandleChoosePokemon(u32 battler)
|
|||||||
BtlController_Complete(battler);
|
BtlController_Complete(battler);
|
||||||
}
|
}
|
||||||
|
|
||||||
static u8 CountAIAliveNonEggMonsExcept(u8 slotToIgnore)
|
|
||||||
{
|
|
||||||
u16 i, count;
|
|
||||||
|
|
||||||
for (i = 0, count = 0; i < PARTY_SIZE; i++)
|
|
||||||
{
|
|
||||||
if (i != slotToIgnore
|
|
||||||
&& IsValidForBattle(&gEnemyParty[i]))
|
|
||||||
{
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void OpponentHandleIntroTrainerBallThrow(u32 battler)
|
static void OpponentHandleIntroTrainerBallThrow(u32 battler)
|
||||||
{
|
{
|
||||||
BtlController_HandleIntroTrainerBallThrow(battler, 0, NULL, 0, Intro_TryShinyAnimShowHealthbox);
|
BtlController_HandleIntroTrainerBallThrow(battler, 0, NULL, 0, Intro_TryShinyAnimShowHealthbox);
|
||||||
|
|||||||
@ -125,3 +125,100 @@ AI_TWO_VS_ONE_BATTLE_TEST("Battler 3 has Battler 1 AI flags set correctly (2v1)"
|
|||||||
TURN { EXPECT_MOVE(opponentLeft, MOVE_EXPLOSION, target: playerLeft); EXPECT_MOVE(opponentRight, MOVE_EXPLOSION); }
|
TURN { EXPECT_MOVE(opponentLeft, MOVE_EXPLOSION, target: playerLeft); EXPECT_MOVE(opponentRight, MOVE_EXPLOSION); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AI_MULTI_BATTLE_TEST("Partner will not steal your pokemon when running out")
|
||||||
|
{
|
||||||
|
|
||||||
|
GIVEN {
|
||||||
|
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
|
||||||
|
MULTI_PLAYER(SPECIES_WOBBUFFET) { }
|
||||||
|
MULTI_PLAYER(SPECIES_WOBBUFFET) { }
|
||||||
|
MULTI_PLAYER(SPECIES_WOBBUFFET) { }
|
||||||
|
MULTI_PARTNER(SPECIES_WYNAUT) { Moves(MOVE_MEMENTO); }
|
||||||
|
MULTI_OPPONENT_A(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE); }
|
||||||
|
MULTI_OPPONENT_B(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE); }
|
||||||
|
} WHEN {
|
||||||
|
TURN {EXPECT_MOVE(playerRight, MOVE_MEMENTO, target:opponentLeft);}
|
||||||
|
TURN {}
|
||||||
|
} THEN {
|
||||||
|
EXPECT_EQ(gAbsentBattlerFlags, (1u << GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AI_MULTI_BATTLE_TEST("Partner will not steal your pokemon to delay using their ace")
|
||||||
|
{
|
||||||
|
|
||||||
|
GIVEN {
|
||||||
|
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
|
||||||
|
BATTLER_AI_FLAGS(B_POSITION_PLAYER_RIGHT, AI_FLAG_ACE_POKEMON);
|
||||||
|
MULTI_PLAYER(SPECIES_WOBBUFFET) { }
|
||||||
|
MULTI_PLAYER(SPECIES_WOBBUFFET) { }
|
||||||
|
MULTI_PLAYER(SPECIES_WOBBUFFET) { }
|
||||||
|
MULTI_PARTNER(SPECIES_WYNAUT) { Moves(MOVE_MEMENTO); }
|
||||||
|
MULTI_PARTNER(SPECIES_METAGROSS) { Moves(MOVE_CELEBRATE); }
|
||||||
|
MULTI_OPPONENT_A(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE); }
|
||||||
|
MULTI_OPPONENT_B(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE); }
|
||||||
|
} WHEN {
|
||||||
|
TURN {EXPECT_MOVE(playerRight, MOVE_MEMENTO, target:opponentLeft);}
|
||||||
|
TURN {}
|
||||||
|
} THEN {
|
||||||
|
EXPECT_EQ(SPECIES_METAGROSS, playerRight->species);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AI_MULTI_BATTLE_TEST("AI opponents do not steal their partner pokemon in multi battle to delay using their ace")
|
||||||
|
{
|
||||||
|
|
||||||
|
GIVEN {
|
||||||
|
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
|
||||||
|
BATTLER_AI_FLAGS(B_POSITION_OPPONENT_LEFT, AI_FLAG_ACE_POKEMON);
|
||||||
|
MULTI_PLAYER(SPECIES_WOBBUFFET) { }
|
||||||
|
MULTI_PARTNER(SPECIES_WOBBUFFET) { }
|
||||||
|
MULTI_OPPONENT_A(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE); HP(1);}
|
||||||
|
MULTI_OPPONENT_A(SPECIES_VENUSAUR) { Moves(MOVE_GIGA_DRAIN); }
|
||||||
|
MULTI_OPPONENT_B(SPECIES_WYNAUT) { Moves(MOVE_CELEBRATE); }
|
||||||
|
} WHEN {
|
||||||
|
TURN {MOVE(playerLeft, MOVE_TACKLE, target: opponentLeft); }
|
||||||
|
TURN {MOVE(playerLeft, MOVE_TACKLE, target: opponentLeft); }
|
||||||
|
} THEN {
|
||||||
|
EXPECT_EQ(SPECIES_VENUSAUR, opponentLeft->species);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AI_MULTI_BATTLE_TEST("AI opponents do not steal their partner pokemon in multi battle when forced out")
|
||||||
|
{
|
||||||
|
u32 item, move;
|
||||||
|
PARAMETRIZE {item = ITEM_EJECT_BUTTON; move = MOVE_TACKLE;}
|
||||||
|
PARAMETRIZE {item = ITEM_EJECT_PACK; move = MOVE_TAIL_WHIP;}
|
||||||
|
PARAMETRIZE {item = ITEM_NONE; move = MOVE_ROAR;}
|
||||||
|
GIVEN {
|
||||||
|
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
|
||||||
|
BATTLER_AI_FLAGS(B_POSITION_OPPONENT_LEFT, AI_FLAG_ACE_POKEMON);
|
||||||
|
MULTI_PLAYER(SPECIES_WOBBUFFET) { }
|
||||||
|
MULTI_PARTNER(SPECIES_WOBBUFFET) { }
|
||||||
|
MULTI_OPPONENT_A(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE); Item(item);}
|
||||||
|
MULTI_OPPONENT_A(SPECIES_VENUSAUR) { Moves(MOVE_GIGA_DRAIN); }
|
||||||
|
MULTI_OPPONENT_B(SPECIES_WYNAUT) { Moves(MOVE_CELEBRATE); }
|
||||||
|
} WHEN {
|
||||||
|
TURN {MOVE(playerLeft, move, target: opponentLeft); }
|
||||||
|
} THEN {
|
||||||
|
EXPECT_EQ(SPECIES_VENUSAUR, opponentLeft->species);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AI_MULTI_BATTLE_TEST("AI opponents do not steal their partner pokemon in multi battle when forced out 2")
|
||||||
|
{
|
||||||
|
GIVEN {
|
||||||
|
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
|
||||||
|
BATTLER_AI_FLAGS(B_POSITION_OPPONENT_LEFT, AI_FLAG_ACE_POKEMON);
|
||||||
|
MULTI_PLAYER(SPECIES_WOBBUFFET) { }
|
||||||
|
MULTI_PARTNER(SPECIES_WOBBUFFET) { }
|
||||||
|
MULTI_OPPONENT_A(SPECIES_GOLISOPOD) { Moves(MOVE_CELEBRATE); HP(101); MaxHP(200); Ability(ABILITY_EMERGENCY_EXIT);}
|
||||||
|
MULTI_OPPONENT_A(SPECIES_VENUSAUR) { Moves(MOVE_GIGA_DRAIN); }
|
||||||
|
MULTI_OPPONENT_B(SPECIES_WYNAUT) { Moves(MOVE_CELEBRATE); }
|
||||||
|
} WHEN {
|
||||||
|
TURN {MOVE(playerLeft, MOVE_TACKLE, target: opponentLeft); }
|
||||||
|
} THEN {
|
||||||
|
EXPECT_EQ(SPECIES_VENUSAUR, opponentLeft->species);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -359,7 +359,7 @@ AI_SINGLE_BATTLE_TEST("When AI switches out due to having no move that affects t
|
|||||||
OPPONENT(SPECIES_ABRA) { Moves(MOVE_TACKLE); }
|
OPPONENT(SPECIES_ABRA) { Moves(MOVE_TACKLE); }
|
||||||
OPPONENT(SPECIES_ABRA) { Moves(MOVE_TACKLE); }
|
OPPONENT(SPECIES_ABRA) { Moves(MOVE_TACKLE); }
|
||||||
} WHEN {
|
} WHEN {
|
||||||
TURN { MOVE(player, MOVE_SHADOW_BALL); EXPECT_SWITCH(opponent, 2); EXPECT_SEND_OUT(opponent, 4); }
|
TURN { MOVE(player, MOVE_SHADOW_BALL); EXPECT_SWITCH(opponent, 2); EXPECT_SEND_OUT(opponent, 0);}
|
||||||
TURN { MOVE(player, MOVE_SHADOW_BALL); EXPECT_MOVE(opponent, MOVE_TACKLE); }
|
TURN { MOVE(player, MOVE_SHADOW_BALL); EXPECT_MOVE(opponent, MOVE_TACKLE); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user