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
|
||||
{
|
||||
SWITCH_AFTER_KO,
|
||||
SWITCH_MID_BATTLE,
|
||||
SWITCH_MID_BATTLE_FORCED,
|
||||
SWITCH_MID_BATTLE_OPTIONAL,
|
||||
};
|
||||
|
||||
void GetAIPartyIndexes(u32 battlerId, s32 *firstId, s32 *lastId);
|
||||
|
||||
@ -359,13 +359,13 @@ void ComputeBattlerDecisions(u32 battler)
|
||||
if (isAiBattler || CanAiPredictMove())
|
||||
{
|
||||
// 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;
|
||||
|
||||
// Setup battler and prediction data
|
||||
BattleAI_SetupAIData(0xF, battler);
|
||||
SetupAIPredictionData(battler, switchType);
|
||||
SetupAIPredictionData(battler, SWITCH_MID_BATTLE_OPTIONAL);
|
||||
|
||||
// AI's own switching data
|
||||
if (isAiBattler)
|
||||
|
||||
@ -1517,22 +1517,19 @@ static u32 GetBestMonDmg(struct Pokemon *party, int firstId, int lastId, u8 inva
|
||||
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())
|
||||
return PARTY_SIZE;
|
||||
|
||||
if (PARTY_SIZE != gBattleStruct->monToSwitchIntoId[battlerIn1]
|
||||
&& PARTY_SIZE != gBattleStruct->monToSwitchIntoId[battlerIn2])
|
||||
return PARTY_SIZE;
|
||||
|
||||
for (u32 chosenMonId = (lastId-1); chosenMonId >= firstId; chosenMonId--)
|
||||
u32 chosenMonId = PARTY_SIZE;
|
||||
for (u32 i = (lastId-1); i > firstId; i--)
|
||||
{
|
||||
if ((1 << (chosenMonId)) & invalidMons)
|
||||
continue;
|
||||
return chosenMonId; // first non invalid mon found
|
||||
if (!((1 << i) & invalidMons))
|
||||
{
|
||||
// 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)
|
||||
@ -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 i, j, aliveCount = 0, bits = 0, aceMonCount = 0;
|
||||
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 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;
|
||||
@ -2167,6 +2164,8 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
|
||||
hitsToKOAIPriority = GetSwitchinHitsToKO(GetMaxPriorityDamagePlayerCouldDealToSwitchin(battler, opposingBattler, gAiLogicData->switchinCandidate.battleMon, &bestPlayerPriorityMove), battler);
|
||||
typeMatchup = GetBattleMonTypeMatchup(gBattleMons[opposingBattler], gAiLogicData->switchinCandidate.battleMon);
|
||||
|
||||
monMaxDamage = 0;
|
||||
|
||||
// Check through current mon's moves
|
||||
for (j = 0; j < MAX_MON_MOVES; j++)
|
||||
{
|
||||
@ -2232,6 +2231,8 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
|
||||
&& damageDealt < playerMonHP)
|
||||
continue;
|
||||
|
||||
if (damageDealt > monMaxDamage)
|
||||
monMaxDamage = damageDealt;
|
||||
// Check that mon isn't one shot and set best damage mon
|
||||
if (damageDealt > maxDamageDealt)
|
||||
{
|
||||
@ -2275,6 +2276,8 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
|
||||
trapperId = i;
|
||||
}
|
||||
}
|
||||
if (monMaxDamage == 0)
|
||||
invalidMons |= 1u << 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 (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
|
||||
&& (IsSwitchOutEffect(GetMoveEffect(gCurrentMove)) || gAiLogicData->ejectButtonSwitch || gAiLogicData->ejectPackSwitch))
|
||||
return aceMonId;
|
||||
|
||||
if (switchType == SWITCH_MID_BATTLE_OPTIONAL)
|
||||
return PARTY_SIZE;
|
||||
|
||||
// Fallback
|
||||
u32 bestMonId = GetFirstNonInvalidMon(firstId, lastId, invalidMons, battlerIn1, battlerIn2);
|
||||
u32 bestMonId = GetFirstNonInvalidMon(firstId, lastId, invalidMons);
|
||||
if (bestMonId != PARTY_SIZE)
|
||||
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;
|
||||
}
|
||||
|
||||
@ -2429,16 +2435,17 @@ u32 GetMostSuitableMonToSwitchInto(u32 battler, enum SwitchType switchType)
|
||||
if (bestMonId != PARTY_SIZE)
|
||||
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
|
||||
&& (IsSwitchOutEffect(GetMoveEffect(gCurrentMove)) || gAiLogicData->ejectButtonSwitch || gAiLogicData->ejectPackSwitch))
|
||||
return aceMonId;
|
||||
if (switchType == SWITCH_MID_BATTLE_OPTIONAL)
|
||||
return PARTY_SIZE;
|
||||
|
||||
// Fallback
|
||||
bestMonId = GetFirstNonInvalidMon(firstId, lastId, invalidMons, battlerIn1, battlerIn2);
|
||||
bestMonId = GetFirstNonInvalidMon(firstId, lastId, invalidMons);
|
||||
if (bestMonId != PARTY_SIZE)
|
||||
return bestMonId;
|
||||
|
||||
if (aceMonId != PARTY_SIZE && CountUsablePartyMons(battler) <= aceMonCount)
|
||||
return aceMonId;
|
||||
|
||||
return PARTY_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
@ -4567,7 +4567,9 @@ s32 CountUsablePartyMons(u32 battlerId)
|
||||
}
|
||||
|
||||
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
|
||||
&& GetMonData(&party[i], MON_DATA_HP) != 0
|
||||
|
||||
@ -51,8 +51,6 @@ static void OpponentHandleChoosePokemon(u32 battler);
|
||||
static void OpponentHandleIntroTrainerBallThrow(u32 battler);
|
||||
static void OpponentHandleDrawPartyStatusSummary(u32 battler);
|
||||
static void OpponentHandleEndLinkBattle(u32 battler);
|
||||
static u8 CountAIAliveNonEggMonsExcept(u8 slotToIgnore);
|
||||
|
||||
static void OpponentBufferRunCommand(u32 battler);
|
||||
|
||||
static void (*const sOpponentBufferCommands[CONTROLLER_CMDS_COUNT])(u32 battler) =
|
||||
@ -527,70 +525,9 @@ static void OpponentHandleChooseItem(u32 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)
|
||||
{
|
||||
s32 chosenMonId;
|
||||
s32 pokemonInBattle = 1;
|
||||
enum SwitchType switchType = SWITCH_AFTER_KO;
|
||||
|
||||
// Choosing Revival Blessing target
|
||||
@ -602,7 +539,7 @@ static void OpponentHandleChoosePokemon(u32 battler)
|
||||
else if (gBattleStruct->AI_monToSwitchIntoId[battler] == PARTY_SIZE)
|
||||
{
|
||||
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
|
||||
SetBattlerAiData(GetBattlerAtPosition(B_POSITION_PLAYER_LEFT), gAiLogicData);
|
||||
@ -610,7 +547,7 @@ static void OpponentHandleChoosePokemon(u32 battler)
|
||||
SetBattlerAiData(GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT), gAiLogicData);
|
||||
|
||||
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;
|
||||
|
||||
@ -622,19 +559,14 @@ static void OpponentHandleChoosePokemon(u32 battler)
|
||||
{
|
||||
battler1 = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
|
||||
battler2 = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT);
|
||||
pokemonInBattle = 2;
|
||||
}
|
||||
|
||||
GetAIPartyIndexes(battler, &firstId, &lastId);
|
||||
for (chosenMonId = (lastId-1); chosenMonId >= firstId; chosenMonId--)
|
||||
for (chosenMonId = firstId; chosenMonId < lastId; chosenMonId++)
|
||||
{
|
||||
if (!IsValidForBattle(&gEnemyParty[chosenMonId])
|
||||
|| chosenMonId == gBattlerPartyIndexes[battler1]
|
||||
|| chosenMonId == gBattlerPartyIndexes[battler2])
|
||||
continue;
|
||||
|
||||
if (!IsAcePokemon(chosenMonId, pokemonInBattle, battler)
|
||||
&& !IsDoubleAcePokemon(chosenMonId, pokemonInBattle, battler))
|
||||
if (IsValidForBattle(&gEnemyParty[chosenMonId])
|
||||
&& chosenMonId != gBattlerPartyIndexes[battler1]
|
||||
&& chosenMonId != gBattlerPartyIndexes[battler2])
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -653,22 +585,6 @@ static void OpponentHandleChoosePokemon(u32 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)
|
||||
{
|
||||
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); }
|
||||
}
|
||||
}
|
||||
|
||||
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); }
|
||||
} 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); }
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user