Remove function GetMoveDamageResult (#3536)

* Remove function GetMoveDamageResult

* fix test

* test clean up

---------

Co-authored-by: DizzyEggg <jajkodizzy@wp.pl>
This commit is contained in:
Alex 2023-11-09 20:53:14 +01:00 committed by GitHub
parent b29538f23d
commit e79583be63
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 186 additions and 223 deletions

View File

@ -301,7 +301,6 @@ struct AiLogicData
u8 hpPercents[MAX_BATTLERS_COUNT];
u16 partnerMove;
u16 speedStats[MAX_BATTLERS_COUNT]; // Speed stats for all battles, calculated only once, same way as damages
u8 moveDmgResult[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // MOVE_POWER defines for GetMoveDamageResult ; attacker, target, moveIndex
s32 simulatedDmg[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // attacker, target, moveIndex
u8 effectiveness[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // attacker, target, moveIndex
u8 moveAccuracy[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // attacker, target, moveIndex

View File

@ -91,8 +91,6 @@ bool32 AI_IsDamagedByRecoil(u32 battler);
u32 GetNoOfHitsToKO(u32 dmg, s32 hp);
u32 GetNoOfHitsToKOBattlerDmg(u32 dmg, u32 battlerDef);
u32 GetNoOfHitsToKOBattler(u32 battlerAtk, u32 battlerDef, u32 moveIndex);
void SetMovesDamageResults(u32 battlerAtk, u16 *moves);
u32 GetMoveDamageResult(u32 battlerAtk, u32 battlerDef, u32 moveIndex);
u32 GetCurrDamageHpPercent(u32 battlerAtk, u32 battlerDef);
uq4_12_t AI_GetTypeEffectiveness(u32 move, u32 battlerAtk, u32 battlerDef);
u32 AI_GetMoveEffectiveness(u32 move, u32 battlerAtk, u32 battlerDef);

View File

@ -24,12 +24,6 @@
#define AI_EFFECTIVENESS_x0_125 1
#define AI_EFFECTIVENESS_x0 0
// get_how_powerful_move_is
#define MOVE_POWER_OTHER 0
#define MOVE_POWER_BEST 1
#define MOVE_POWER_GOOD 2 // Similar dmg range with best.
#define MOVE_POWER_WEAK 3 // Significantly lower than best and good.
// AI Flags. Most run specific functions to update score, new flags are used for internal logic in other scripts
#define AI_FLAG_CHECK_BAD_MOVE (1 << 0)
#define AI_FLAG_TRY_TO_FAINT (1 << 1)

View File

@ -388,7 +388,6 @@ static void SetBattlerAiMovesData(struct AiLogicData *aiData, u32 battlerAtk, u3
aiData->effectiveness[battlerAtk][battlerDef][i] = effectiveness;
}
}
SetMovesDamageResults(battlerAtk, moves);
}
void SetAiLogicDataForTurn(struct AiLogicData *aiData)
@ -2667,36 +2666,25 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
static s32 AI_TryToFaint(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
{
u32 movesetIndex = AI_THINKING_STRUCT->movesetIndex;
bool32 aiFaster;
if (IS_TARGETING_PARTNER(battlerAtk, battlerDef))
return score;
if (gBattleMoves[move].power == 0)
return score; // can't make anything faint with no power
return score; // can't make anything faint with no power
aiFaster = AI_STRIKES_FIRST(battlerAtk, battlerDef, move);
if (CanIndexMoveFaintTarget(battlerAtk, battlerDef, movesetIndex, 0) && gBattleMoves[move].effect != EFFECT_EXPLOSION)
{
// this move can faint the target
if (aiFaster || GetMovePriority(battlerAtk, move) > 0)
ADJUST_SCORE(5); // we go first or we're using priority move
if (AI_STRIKES_FIRST(battlerAtk, battlerDef, move))
ADJUST_SCORE(5); // Move hits first and can faint the target
else
ADJUST_SCORE(4);
ADJUST_SCORE(4); // Faints target but slower
}
else
else if (CanTargetFaintAi(battlerDef, battlerAtk)
&& GetWhichBattlerFaster(battlerAtk, battlerDef, TRUE) != AI_IS_FASTER
&& GetMovePriority(battlerAtk, move) > 0)
{
if (GetMoveDamageResult(battlerAtk, battlerDef, movesetIndex) == MOVE_POWER_OTHER)
ADJUST_SCORE(-1);
}
//AI_TryToFaint_CheckIfDanger
if (!aiFaster && CanTargetFaintAi(battlerDef, battlerAtk))
{ // AI_TryToFaint_Danger
if (GetMoveDamageResult(battlerAtk, battlerDef, movesetIndex) != MOVE_POWER_BEST)
ADJUST_SCORE(-1);
else
ADJUST_SCORE(1);
ADJUST_SCORE(2); // If slower and target can kill
}
return score;
@ -2826,113 +2814,110 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
// check specific target
if (IS_TARGETING_PARTNER(battlerAtk, battlerDef))
{
if (GetMoveDamageResult(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex) == MOVE_POWER_OTHER)
// partner ability checks
if (!partnerProtecting && moveTarget != MOVE_TARGET_BOTH && !DoesBattlerIgnoreAbilityChecks(aiData->abilities[battlerAtk], move))
{
// partner ability checks
if (!partnerProtecting && moveTarget != MOVE_TARGET_BOTH && !DoesBattlerIgnoreAbilityChecks(aiData->abilities[battlerAtk], move))
switch (atkPartnerAbility)
{
switch (atkPartnerAbility)
case ABILITY_VOLT_ABSORB:
if (!(AI_THINKING_STRUCT->aiFlags & AI_FLAG_HP_AWARE))
{
case ABILITY_VOLT_ABSORB:
if (!(AI_THINKING_STRUCT->aiFlags & AI_FLAG_HP_AWARE))
{
RETURN_SCORE_MINUS(10);
}
break; // handled in AI_HPAware
case ABILITY_MOTOR_DRIVE:
if (moveType == TYPE_ELECTRIC && BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_SPEED))
{
RETURN_SCORE_PLUS(1);
}
break;
case ABILITY_LIGHTNING_ROD:
if (moveType == TYPE_ELECTRIC
&& HasMoveWithSplit(battlerAtkPartner, SPLIT_SPECIAL)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_SPATK))
{
RETURN_SCORE_PLUS(1);
}
break;
case ABILITY_WATER_ABSORB:
case ABILITY_DRY_SKIN:
if (!(AI_THINKING_STRUCT->aiFlags & AI_FLAG_HP_AWARE))
{
RETURN_SCORE_MINUS(10);
}
break; // handled in AI_HPAware
case ABILITY_STORM_DRAIN:
if (moveType == TYPE_WATER
&& HasMoveWithSplit(battlerAtkPartner, SPLIT_SPECIAL)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_SPATK))
{
RETURN_SCORE_PLUS(1);
}
break;
case ABILITY_WATER_COMPACTION:
if (moveType == TYPE_WATER && GetMoveDamageResult(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex) == MOVE_POWER_WEAK)
{
RETURN_SCORE_PLUS(1); // only mon with this ability is weak to water so only make it okay if we do very little damage
}
RETURN_SCORE_MINUS(10);
break;
case ABILITY_FLASH_FIRE:
if (moveType == TYPE_FIRE
&& HasMoveWithType(battlerAtkPartner, TYPE_FIRE)
&& !(gBattleResources->flags->flags[battlerAtkPartner] & RESOURCE_FLAG_FLASH_FIRE))
{
RETURN_SCORE_PLUS(1);
}
break;
case ABILITY_SAP_SIPPER:
if (moveType == TYPE_GRASS
&& HasMoveWithSplit(battlerAtkPartner, SPLIT_PHYSICAL)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_ATK))
{
RETURN_SCORE_PLUS(1);
}
break;
case ABILITY_JUSTIFIED:
if (moveType == TYPE_DARK
&& !IS_MOVE_STATUS(move)
&& HasMoveWithSplit(battlerAtkPartner, SPLIT_PHYSICAL)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_ATK)
&& !CanIndexMoveFaintTarget(battlerAtk, battlerAtkPartner, AI_THINKING_STRUCT->movesetIndex, 1))
{
RETURN_SCORE_PLUS(1);
}
break;
case ABILITY_RATTLED:
if (!IS_MOVE_STATUS(move)
&& (moveType == TYPE_DARK || moveType == TYPE_GHOST || moveType == TYPE_BUG)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_SPEED)
&& !CanIndexMoveFaintTarget(battlerAtk, battlerAtkPartner, AI_THINKING_STRUCT->movesetIndex, 1))
{
RETURN_SCORE_PLUS(1);
}
break;
case ABILITY_CONTRARY:
if (IsStatLoweringEffect(effect))
{
RETURN_SCORE_PLUS(2);
}
break;
case ABILITY_DEFIANT:
if (IsStatLoweringEffect(effect)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_ATK))
{
RETURN_SCORE_PLUS(1);
}
break;
case ABILITY_COMPETITIVE:
if (IsStatLoweringEffect(effect)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_SPATK))
{
RETURN_SCORE_PLUS(1);
}
break;
}
} // ability checks
} // move power check
break; // handled in AI_HPAware
case ABILITY_MOTOR_DRIVE:
if (moveType == TYPE_ELECTRIC && BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_SPEED))
{
RETURN_SCORE_PLUS(1);
}
break;
case ABILITY_LIGHTNING_ROD:
if (moveType == TYPE_ELECTRIC
&& HasMoveWithSplit(battlerAtkPartner, SPLIT_SPECIAL)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_SPATK))
{
RETURN_SCORE_PLUS(1);
}
break;
case ABILITY_WATER_ABSORB:
case ABILITY_DRY_SKIN:
if (!(AI_THINKING_STRUCT->aiFlags & AI_FLAG_HP_AWARE))
{
RETURN_SCORE_MINUS(10);
}
break; // handled in AI_HPAware
case ABILITY_STORM_DRAIN:
if (moveType == TYPE_WATER
&& HasMoveWithSplit(battlerAtkPartner, SPLIT_SPECIAL)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_SPATK))
{
RETURN_SCORE_PLUS(1);
}
break;
case ABILITY_WATER_COMPACTION:
if (moveType == TYPE_WATER && GetNoOfHitsToKOBattler(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex) >= 4)
{
RETURN_SCORE_PLUS(1); // only mon with this ability is weak to water so only make it okay if we do very little damage
}
RETURN_SCORE_MINUS(10);
break;
case ABILITY_FLASH_FIRE:
if (moveType == TYPE_FIRE
&& HasMoveWithType(battlerAtkPartner, TYPE_FIRE)
&& !(gBattleResources->flags->flags[battlerAtkPartner] & RESOURCE_FLAG_FLASH_FIRE))
{
RETURN_SCORE_PLUS(1);
}
break;
case ABILITY_SAP_SIPPER:
if (moveType == TYPE_GRASS
&& HasMoveWithSplit(battlerAtkPartner, SPLIT_PHYSICAL)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_ATK))
{
RETURN_SCORE_PLUS(1);
}
break;
case ABILITY_JUSTIFIED:
if (moveType == TYPE_DARK
&& !IS_MOVE_STATUS(move)
&& HasMoveWithSplit(battlerAtkPartner, SPLIT_PHYSICAL)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_ATK)
&& !CanIndexMoveFaintTarget(battlerAtk, battlerAtkPartner, AI_THINKING_STRUCT->movesetIndex, 1))
{
RETURN_SCORE_PLUS(1);
}
break;
case ABILITY_RATTLED:
if (!IS_MOVE_STATUS(move)
&& (moveType == TYPE_DARK || moveType == TYPE_GHOST || moveType == TYPE_BUG)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_SPEED)
&& !CanIndexMoveFaintTarget(battlerAtk, battlerAtkPartner, AI_THINKING_STRUCT->movesetIndex, 1))
{
RETURN_SCORE_PLUS(1);
}
break;
case ABILITY_CONTRARY:
if (IsStatLoweringEffect(effect))
{
RETURN_SCORE_PLUS(2);
}
break;
case ABILITY_DEFIANT:
if (IsStatLoweringEffect(effect)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_ATK))
{
RETURN_SCORE_PLUS(1);
}
break;
case ABILITY_COMPETITIVE:
if (IsStatLoweringEffect(effect)
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_SPATK))
{
RETURN_SCORE_PLUS(1);
}
break;
}
} // ability checks
// attacker move effects specifically targeting partner
if (!partnerProtecting)
@ -4832,7 +4817,7 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u32 move, s32 score
{
if (gDisableStructs[battlerDef].tauntTimer != 0)
ADJUST_SCORE(1); // target must use damaging move
if (GetMoveDamageResult(battlerDef, battlerAtk, predictedMoveSlot) >= MOVE_POWER_GOOD && GetBattleMoveSplit(predictedMove) == SPLIT_PHYSICAL)
if (GetNoOfHitsToKOBattler(battlerDef, battlerAtk, predictedMoveSlot) >= 2 && GetBattleMoveSplit(predictedMove) == SPLIT_PHYSICAL)
ADJUST_SCORE(3);
}
break;
@ -4841,7 +4826,7 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u32 move, s32 score
{
if (gDisableStructs[battlerDef].tauntTimer != 0)
ADJUST_SCORE(1); // target must use damaging move
if (GetMoveDamageResult(battlerDef, battlerAtk, predictedMoveSlot) >= MOVE_POWER_GOOD && GetBattleMoveSplit(predictedMove) == SPLIT_SPECIAL)
if (GetNoOfHitsToKOBattler(battlerDef, battlerAtk, predictedMoveSlot) >= 2 && GetBattleMoveSplit(predictedMove) == SPLIT_SPECIAL)
ADJUST_SCORE(3);
}
break;
@ -5056,7 +5041,9 @@ static s32 AI_PreferStrongestMove(u32 battlerAtk, u32 battlerDef, u32 move, s32
if (IS_TARGETING_PARTNER(battlerAtk, battlerDef))
return score;
if (GetMoveDamageResult(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex) == MOVE_POWER_BEST)
if (GetNoOfHitsToKOBattler(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex) == 1)
ADJUST_SCORE(3);
else if (GetNoOfHitsToKOBattler(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex) == 2)
ADJUST_SCORE(2);
return score;
@ -5069,7 +5056,7 @@ static s32 AI_PreferBatonPass(u32 battlerAtk, u32 battlerDef, u32 move, s32 scor
if (IS_TARGETING_PARTNER(battlerAtk, battlerDef)
|| CountUsablePartyMons(battlerAtk) == 0
|| GetMoveDamageResult(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex) != MOVE_POWER_OTHER
|| gBattleMoves[move].power != 0
|| !HasMoveEffect(battlerAtk, EFFECT_BATON_PASS)
|| IsBattlerTrapped(battlerAtk, TRUE))
return score;

View File

@ -1040,90 +1040,6 @@ u32 GetNoOfHitsToKOBattler(u32 battlerAtk, u32 battlerDef, u32 moveIndex)
return GetNoOfHitsToKOBattlerDmg(AI_DATA->simulatedDmg[battlerAtk][battlerDef][moveIndex], battlerDef);
}
void SetMovesDamageResults(u32 battlerAtk, u16 *moves)
{
s32 i, j, battlerDef, bestId, currId, hp, result;
s32 moveDmgs[MAX_MON_MOVES];
bool32 isNotConsidered[MAX_MON_MOVES];
for (i = 0; i < MAX_MON_MOVES; i++)
{
u32 move = moves[i];
if (move == MOVE_NONE || move == MOVE_UNAVAILABLE || gBattleMoves[move].power == 0)
isNotConsidered[i] = TRUE;
else
isNotConsidered[i] = FALSE;
}
for (i = 0; i < MAX_MON_MOVES; i++)
{
for (battlerDef = 0; battlerDef < MAX_BATTLERS_COUNT; battlerDef++)
{
if (battlerDef == battlerAtk)
continue;
if (isNotConsidered[i])
{
result = MOVE_POWER_OTHER; // Move has a power of 0/1
}
else
{
// Check all other moves and calculate their power
for (j = 0; j < MAX_MON_MOVES; j++)
{
if (!isNotConsidered[j])
moveDmgs[j] = AI_DATA->simulatedDmg[battlerAtk][battlerDef][j];
else
moveDmgs[j] = 0;
}
hp = gBattleMons[battlerDef].hp + (20 * gBattleMons[battlerDef].hp / 100); // 20 % add to make sure the battler is always fainted
// If a move can faint battler, it doesn't matter how much damage it does
for (j = 0; j < MAX_MON_MOVES; j++)
{
if (moveDmgs[j] > hp)
moveDmgs[j] = hp;
}
// Find move which deals most damage, in case of a tie prioritize one with better effect.
for (bestId = 0, j = 1; j < MAX_MON_MOVES; j++)
{
if (moveDmgs[j] > moveDmgs[bestId])
bestId = j;
if (moveDmgs[j] == moveDmgs[bestId])
{
switch (AI_WhichMoveBetter(gBattleMons[battlerAtk].moves[bestId], gBattleMons[battlerAtk].moves[j], battlerAtk, battlerDef, GetNoOfHitsToKO(moveDmgs[j], hp)))
{
case 2:
if (Random() & 1)
break;
case 1:
bestId = j;
break;
}
}
}
currId = i;
if (currId == bestId)
result = MOVE_POWER_BEST;
else if ((moveDmgs[currId] >= hp || moveDmgs[bestId] < hp) // If current move can faint as well, or if neither can
&& GetNoOfHitsToKO(moveDmgs[currId], hp) - GetNoOfHitsToKO(moveDmgs[bestId], hp) <= 2 // Consider a move weak if it needs to be used at least 2 times more to faint the target, compared to the best move.
&& AI_WhichMoveBetter(gBattleMons[battlerAtk].moves[bestId], gBattleMons[battlerAtk].moves[currId], battlerAtk, battlerDef, GetNoOfHitsToKO(moveDmgs[currId], hp)) != 0)
result = MOVE_POWER_GOOD;
else
result = MOVE_POWER_WEAK;
}
AI_DATA->moveDmgResult[battlerAtk][battlerDef][i] = result;
}
}
}
u32 GetMoveDamageResult(u32 battlerAtk, u32 battlerDef, u32 moveIndex)
{
return AI_DATA->moveDmgResult[battlerAtk][battlerDef][moveIndex];
}
u32 GetCurrDamageHpPercent(u32 battlerAtk, u32 battlerDef)
{
int bestDmg = AI_DATA->simulatedDmg[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex];

View File

@ -223,8 +223,7 @@ AI_SINGLE_BATTLE_TEST("AI prefers a weaker move over a one with a downside effec
TURN { EXPECT_MOVE(opponent, expectedMove); SEND_OUT(player, 1); }
break;
}
}
SCENE {
} SCENE {
MESSAGE("Wobbuffet fainted!");
}
}
@ -248,7 +247,7 @@ AI_SINGLE_BATTLE_TEST("AI can choose a status move that boosts the attack by two
{
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_WOBBUFFET) { HP(250); };
PLAYER(SPECIES_WOBBUFFET) { HP(278); };
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_KANGASKHAN) { Moves(MOVE_STRENGTH, MOVE_HORN_ATTACK, MOVE_SWORDS_DANCE); }
} WHEN {

View File

@ -140,6 +140,28 @@ AI_SINGLE_BATTLE_TEST("AI sees increased base power of Spit Up")
TURN { EXPECT_MOVE(opponent, MOVE_STOCKPILE); }
TURN { EXPECT_MOVE(opponent, MOVE_SPIT_UP); }
} SCENE {
// MESSAGE("Wobbuffet fainted!");
MESSAGE("Wobbuffet fainted!");
}
}
AI_SINGLE_BATTLE_TEST("AI can choose Counter or Mirror Coat if the predicted move split is correct and user doesn't faint")
{
u16 playerMove = MOVE_NONE, opponentMove = MOVE_NONE;
PARAMETRIZE { playerMove = MOVE_STRENGTH; opponentMove = MOVE_COUNTER; }
PARAMETRIZE { playerMove = MOVE_POWER_GEM; opponentMove = MOVE_MIRROR_COAT; }
GIVEN {
ASSUME(gBattleMoves[MOVE_COUNTER].effect == EFFECT_COUNTER);
ASSUME(gBattleMoves[MOVE_MIRROR_COAT].effect == EFFECT_MIRROR_COAT);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_WOBBUFFET) { Speed(1); }
OPPONENT(SPECIES_WOBBUFFET) { HP(102); Speed(100); Moves(opponentMove, MOVE_STRENGTH); }
} WHEN {
TURN { MOVE(player, playerMove); EXPECT_MOVE(opponent, MOVE_STRENGTH); }
TURN { MOVE(player, playerMove); EXPECT_MOVE(opponent, opponentMove); }
TURN { MOVE(player, playerMove); EXPECT_MOVE(opponent, MOVE_STRENGTH); }
} SCENE {
MESSAGE("Foe Wobbuffet fainted!");
}
}

View File

@ -0,0 +1,48 @@
#include "global.h"
#include "test/battle.h"
#include "battle_ai_util.h"
AI_SINGLE_BATTLE_TEST("AI prefers priority moves if it's slower and can kill target")
{
GIVEN {
ASSUME(gBattleMoves[MOVE_QUICK_ATTACK].priority == 1);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_WOBBUFFET) { HP(1); Speed(100); }
PLAYER(SPECIES_WOBBUFFET) { Speed(100); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(1); Moves(MOVE_QUICK_ATTACK, MOVE_STRENGTH); }
} WHEN {
TURN { EXPECT_MOVE(opponent, MOVE_QUICK_ATTACK); SEND_OUT(player, 1); }
} SCENE {
MESSAGE("Wobbuffet fainted!");
}
}
AI_SINGLE_BATTLE_TEST("AI will choose a random move if it's faster and can kill target")
{
GIVEN {
ASSUME(gBattleMoves[MOVE_QUICK_ATTACK].priority == 1);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_WOBBUFFET) { HP(1); Speed(1); }
PLAYER(SPECIES_WOBBUFFET) { Speed(1); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(100); Moves(MOVE_QUICK_ATTACK, MOVE_STRENGTH); }
} WHEN {
TURN { EXPECT_MOVES(opponent, MOVE_QUICK_ATTACK, MOVE_STRENGTH); SEND_OUT(player, 1); }
} SCENE {
MESSAGE("Wobbuffet fainted!");
}
}
AI_SINGLE_BATTLE_TEST("AI will choose a priority move if it is slower then the target and will be killed")
{
GIVEN {
ASSUME(gBattleMoves[MOVE_QUICK_ATTACK].priority == 1);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_WOBBUFFET) { Speed(100); }
OPPONENT(SPECIES_WOBBUFFET) { HP(60); Speed(1); Moves(MOVE_QUICK_ATTACK, MOVE_STRENGTH); }
} WHEN {
TURN { MOVE(player, MOVE_STRENGTH); EXPECT_MOVE(opponent, MOVE_STRENGTH); }
TURN { MOVE(player, MOVE_STRENGTH); EXPECT_MOVE(opponent, MOVE_QUICK_ATTACK); }
} SCENE {
MESSAGE("Foe Wobbuffet fainted!");
}
}