Remove function GetMoveDamageResult (#3536)
* Remove function GetMoveDamageResult * fix test * test clean up --------- Co-authored-by: DizzyEggg <jajkodizzy@wp.pl>
This commit is contained in:
parent
b29538f23d
commit
e79583be63
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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];
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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!");
|
||||
}
|
||||
}
|
||||
|
||||
48
test/battle/ai_trytofaint.c
Normal file
48
test/battle/ai_trytofaint.c
Normal 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!");
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user