Ai use most powerful move smarrter

This commit is contained in:
DizzyEggg 2019-08-27 18:29:34 +02:00
parent bfa357a608
commit 91b364e8d6
5 changed files with 129 additions and 158 deletions

View File

@ -50,7 +50,7 @@ AI_CheckBadMove:
if_move MOVE_FISSURE, AI_CBM_CheckIfNegatesType
if_move MOVE_HORN_DRILL, AI_CBM_CheckIfNegatesType
get_how_powerful_move_is
if_equal 0, AI_CheckBadMove_CheckEffect
if_equal MOVE_POWER_DISCOURAGED, AI_CheckBadMove_CheckEffect
AI_CBM_CheckIfNegatesType: @ 82DBF92
if_type_effectiveness AI_EFFECTIVENESS_x0, Score_Minus10
@ -935,11 +935,20 @@ AI_CheckIfAlreadyDeadPriorities:
score +1
end
@ The purpose is to use a move effect that hits the hardest or similar
AI_CV_DmgMove:
get_considered_move_power
if_equal 0, AI_Ret
get_how_powerful_move_is
if_equal MOVE_POWER_WEAK, Score_Minus1
end
AI_CheckViability:
if_target_is_ally AI_Ret
call_if_always_hit AI_CV_AlwaysHit
call_if_move_flag FLAG_HIGH_CRIT, AI_CV_HighCrit
call AI_CheckIfAlreadyDead
call AI_CV_DmgMove
if_effect EFFECT_HIT, AI_CV_Hit
if_effect EFFECT_SLEEP, AI_CV_Sleep
if_effect EFFECT_ABSORB, AI_CV_Absorb
@ -3218,31 +3227,32 @@ AI_CV_DragonDance_End:
AI_TryToFaint:
if_target_is_ally AI_Ret
if_can_faint AI_TryToFaint_TryToEncourageQuickAttack
if_can_faint AI_TryToFaint_Can
get_how_powerful_move_is
if_equal MOVE_NOT_MOST_POWERFUL, Score_Minus1
if_equal MOVE_POWER_DISCOURAGED, Score_Minus1
AI_TryToFaint2:
if_type_effectiveness AI_EFFECTIVENESS_x4, AI_TryToFaint_DoubleSuperEffective
goto AI_TryToFaint_InDanger
goto AI_TryToFaint_CheckIfDanger
AI_TryToFaint_DoubleSuperEffective:
if_random_less_than 80, AI_TryToFaint_InDanger
if_random_less_than 80, AI_TryToFaint_CheckIfDanger
score +2
goto AI_TryToFaint_InDanger
AI_TryToFaint_TryToEncourageQuickAttack:
if_effect EFFECT_EXPLOSION, AI_TryToFaint_InDanger
goto AI_TryToFaint_CheckIfDanger
AI_TryToFaint_Can:
if_effect EFFECT_EXPLOSION, AI_TryToFaint_CheckIfDanger
if_user_faster AI_TryToFaint_ScoreUp4
if_move_flag FLAG_HIGH_CRIT AI_TryToFaint_ScoreUp4
score +2
goto AI_TryToFaint_InDanger
goto AI_TryToFaint_CheckIfDanger
AI_TryToFaint_ScoreUp4:
score +4
AI_TryToFaint_InDanger:
if_target_faster AI_TryToFaint_End
if_ai_can_go_down AI_TryToFaint_IsInDanger
AI_TryToFaint_CheckIfDanger:
if_user_faster AI_TryToFaint_End
if_ai_can_go_down AI_TryToFaint_Danger
AI_TryToFaint_End:
end
AI_TryToFaint_IsInDanger:
AI_TryToFaint_Danger:
get_how_powerful_move_is
if_not_equal MOVE_MOST_POWERFUL, Score_Minus1
if_not_equal MOVE_POWER_BEST, Score_Minus1
score +1
goto AI_TryToFaint_End
@ -3339,10 +3349,9 @@ AI_SetupFirstTurn_SetupEffectsToEncourage:
AI_PreferStrongestMove:
if_target_is_ally AI_Ret
get_how_powerful_move_is
if_not_equal 0, AI_PreferStrongestMove_End
if_not_equal MOVE_POWER_BEST, AI_PreferStrongestMove_End
if_random_less_than 100, AI_PreferStrongestMove_End
score +2
AI_PreferStrongestMove_End:
end
@ -3354,7 +3363,6 @@ AI_Risky:
AI_Risky_RandChance:
if_random_less_than 128, AI_Risky_End
score +2
AI_Risky_End:
end
@ -3384,7 +3392,7 @@ AI_PreferBatonPass:
count_usable_party_mons AI_USER
if_equal 0, AI_PreferBatonPassEnd
get_how_powerful_move_is
if_not_equal 0, AI_PreferBatonPassEnd
if_not_equal MOVE_POWER_DISCOURAGED, AI_PreferBatonPassEnd
if_has_move_with_effect AI_USER, EFFECT_BATON_PASS, AI_PreferBatonPass_GoForBatonPass
if_random_less_than 80, AI_Risky_End
@ -3488,7 +3496,7 @@ AI_DoubleBattle:
AI_DoubleBattlePartnerHasHelpingHand:
get_how_powerful_move_is
if_not_equal 0, Score_Plus1
if_not_equal MOVE_POWER_DISCOURAGED, Score_Plus1
end
AI_DoubleBattleCheckUserStatus:
@ -3499,7 +3507,7 @@ AI_DoubleBattleCheckUserStatus2:
get_how_powerful_move_is
if_equal MOVE_POWER_DISCOURAGED, Score_Minus5
score +1
if_equal MOVE_MOST_POWERFUL, Score_Plus2
if_equal MOVE_POWER_BEST, Score_Plus2
end
AI_DoubleBattleAllHittingGroundMove:
@ -3537,7 +3545,7 @@ AI_DoubleBattleFireMove2:
AI_TryOnAlly:
get_how_powerful_move_is
if_equal 0, AI_TryStatusMoveOnAlly
if_equal MOVE_POWER_DISCOURAGED, AI_TryStatusMoveOnAlly
get_curr_move_type
if_equal TYPE_FIRE, AI_TryFireMoveOnAlly

View File

@ -251,7 +251,7 @@ struct AI_ThinkingStruct
u32 aiFlags;
u8 aiAction;
u8 aiLogicId;
u8 simulatedRNG[4];
s32 simulatedDmg[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // attacker, target, move
struct AI_SavedBattleMon saved[4];
bool8 switchMon; // Because all available moves have no/little effect.
};

View File

@ -84,6 +84,7 @@ void BattleScriptPushCursorAndCallback(const u8* BS_ptr);
u8 ItemBattleEffects(u8 caseID, u8 battlerId, bool8 moveTurn);
void ClearFuryCutterDestinyBondGrudge(u8 battlerId);
void HandleAction_RunBattleScript(void);
u32 SetRandomTarget(u32 battlerId);
u8 GetMoveTarget(u16 move, u8 setTarget);
u8 IsMonDisobedient(void);
u32 GetBattlerHoldEffect(u8 battlerId, bool32 checkNegating);

View File

@ -31,8 +31,9 @@
// get_how_powerful_move_is
#define MOVE_POWER_DISCOURAGED 0
#define MOVE_NOT_MOST_POWERFUL 1
#define MOVE_MOST_POWERFUL 2
#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.
// script's table id to bit
#define AI_SCRIPT_CHECK_BAD_MOVE (1 << 0)

View File

@ -14,6 +14,7 @@
#include "constants/abilities.h"
#include "constants/battle_ai.h"
#include "constants/battle_move_effects.h"
#include "constants/hold_effects.h"
#include "constants/moves.h"
#include "constants/species.h"
@ -55,6 +56,7 @@ static void BattleAI_DoAIProcessing(void);
static void AIStackPushVar(const u8 *);
static bool8 AIStackPop(void);
static s32 CountUsablePartyMons(u8 battlerId);
static s32 AI_GetAbility(u32 battlerId, bool32 guess);
static void BattleAICmd_if_random_less_than(void);
static void BattleAICmd_if_random_greater_than(void);
@ -360,7 +362,7 @@ void BattleAI_SetupFlags(void)
void BattleAI_SetupAIData(u8 defaultScoreMoves)
{
s32 i;
s32 i, move, dmg;
u8 moveLimitations;
// Clear AI data but preserve the flags.
@ -386,25 +388,32 @@ void BattleAI_SetupAIData(u8 defaultScoreMoves)
{
if (gBitTable[i] & moveLimitations)
AI_THINKING_STRUCT->score[i] = 0;
AI_THINKING_STRUCT->simulatedRNG[i] = 100 - (Random() % 16);
}
gBattleResources->AI_ScriptsStack->size = 0;
sBattler_AI = gActiveBattler;
// Decide a random target battlerId in doubles.
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
// Simulate dmg for all AI moves against all opposing targets
for (gBattlerTarget = 0; gBattlerTarget < gBattlersCount; gBattlerTarget++)
{
gBattlerTarget = (Random() & BIT_FLANK) + (GetBattlerSide(gActiveBattler) ^ BIT_SIDE);
if (gAbsentBattlerFlags & gBitTable[gBattlerTarget])
gBattlerTarget ^= BIT_FLANK;
}
// There's only one choice in single battles.
else
{
gBattlerTarget = sBattler_AI ^ BIT_SIDE;
if (GET_BATTLER_SIDE2(sBattler_AI) == GET_BATTLER_SIDE2(gBattlerTarget))
continue;
for (i = 0; i < MAX_MON_MOVES; i++)
{
dmg = 0;
move = gBattleMons[sBattler_AI].moves[i];
if (gBattleMoves[move].power != 0 && !(moveLimitations & gBitTable[i]))
{
dmg = AI_CalcDamage(move, sBattler_AI, gBattlerTarget) * (100 - (Random() % 16)) / 100;
if (dmg == 0)
dmg = 1;
}
AI_THINKING_STRUCT->simulatedDmg[sBattler_AI][gBattlerTarget][i] = dmg;
}
}
gBattlerTarget = SetRandomTarget(sBattler_AI);
}
u8 BattleAI_ChooseMoveOrAction(void)
@ -1322,9 +1331,38 @@ static void BattleAICmd_get_considered_move_power(void)
gAIScriptPtr += 1;
}
// Checks if the move dealing less damage does not have worse effects.
static bool32 CompareTwoMoves(u32 bestMove, u32 goodMove)
{
s32 defAbility = AI_GetAbility(gBattlerTarget, FALSE);
// Check if physical moves hurt.
if (GetBattlerHoldEffect(sBattler_AI, TRUE) != HOLD_EFFECT_PROTECTIVE_PADS
&& (BATTLE_HISTORY->itemEffects[gBattlerTarget] == HOLD_EFFECT_ROCKY_HELMET
|| defAbility == ABILITY_IRON_BARBS || defAbility == ABILITY_ROUGH_SKIN))
{
if (IS_MOVE_PHYSICAL(goodMove) && !IS_MOVE_PHYSICAL(bestMove))
return FALSE;
}
// Check recoil
if (GetBattlerAbility(sBattler_AI) != ABILITY_ROCK_HEAD)
{
if (gBattleMoves[goodMove].effect == EFFECT_RECOIL && gBattleMoves[bestMove].effect != EFFECT_RECOIL)
return FALSE;
}
// Check recharge
if (gBattleMoves[goodMove].effect == EFFECT_RECHARGE && gBattleMoves[bestMove].effect != EFFECT_RECHARGE)
return FALSE;
// Check additional effect.
if (gBattleMoves[bestMove].effect != 0 && gBattleMoves[goodMove].effect == 0)
return FALSE;
return TRUE;
}
static void BattleAICmd_get_how_powerful_move_is(void)
{
s32 i, checkedMove;
s32 i, checkedMove, bestId, currId, hp;
s32 moveDmgs[4];
for (i = 0; sDiscouragedPowerfulMoveEffects[i] != 0xFFFF; i++)
@ -1333,12 +1371,9 @@ static void BattleAICmd_get_how_powerful_move_is(void)
break;
}
if (gBattleMoves[AI_THINKING_STRUCT->moveConsidered].power > 1
if (gBattleMoves[AI_THINKING_STRUCT->moveConsidered].power != 0
&& sDiscouragedPowerfulMoveEffects[i] == 0xFFFF)
{
*(&gBattleStruct->dynamicMoveType) = 0;
gMoveResultFlags = 0;
for (checkedMove = 0; checkedMove < MAX_MON_MOVES; checkedMove++)
{
for (i = 0; sDiscouragedPowerfulMoveEffects[i] != 0xFFFF; i++)
@ -1349,13 +1384,9 @@ static void BattleAICmd_get_how_powerful_move_is(void)
if (gBattleMons[sBattler_AI].moves[checkedMove] != MOVE_NONE
&& sDiscouragedPowerfulMoveEffects[i] == 0xFFFF
&& gBattleMoves[gBattleMons[sBattler_AI].moves[checkedMove]].power > 1)
&& gBattleMoves[gBattleMons[sBattler_AI].moves[checkedMove]].power != 0)
{
gCurrentMove = gBattleMons[sBattler_AI].moves[checkedMove];
moveDmgs[checkedMove] = AI_CalcDamage(gCurrentMove, sBattler_AI, gBattlerTarget);
moveDmgs[checkedMove] = moveDmgs[checkedMove] * AI_THINKING_STRUCT->simulatedRNG[checkedMove] / 100;
if (moveDmgs[checkedMove] == 0)
moveDmgs[checkedMove] = 1;
moveDmgs[checkedMove] = AI_THINKING_STRUCT->simulatedDmg[sBattler_AI][gBattlerTarget][checkedMove];
}
else
{
@ -1363,16 +1394,22 @@ static void BattleAICmd_get_how_powerful_move_is(void)
}
}
for (checkedMove = 0; checkedMove < MAX_MON_MOVES; checkedMove++)
for (bestId = 0, i = 0; i < MAX_MON_MOVES; i++)
{
if (moveDmgs[checkedMove] > moveDmgs[AI_THINKING_STRUCT->movesetIndex])
break;
if (moveDmgs[i] > moveDmgs[bestId])
bestId = i;
}
if (checkedMove == MAX_MON_MOVES)
AI_THINKING_STRUCT->funcResult = MOVE_MOST_POWERFUL; // Is the most powerful.
currId = AI_THINKING_STRUCT->movesetIndex;
hp = gBattleMons[gBattlerTarget].hp;
if (currId == bestId)
AI_THINKING_STRUCT->funcResult = MOVE_POWER_BEST;
// Compare percentage difference.
else if ((moveDmgs[bestId] * 100 / hp) - (moveDmgs[currId] * 100 / hp) <= 10
&& CompareTwoMoves(gBattleMons[sBattler_AI].moves[bestId], gBattleMons[sBattler_AI].moves[currId]))
AI_THINKING_STRUCT->funcResult = MOVE_POWER_GOOD;
else
AI_THINKING_STRUCT->funcResult = MOVE_NOT_MOST_POWERFUL; // Not the most powerful.
AI_THINKING_STRUCT->funcResult = MOVE_POWER_WEAK;
}
else
{
@ -1516,109 +1553,49 @@ static void BattleAICmd_get_considered_move_effect(void)
gAIScriptPtr += 1;
}
static void BattleAICmd_get_ability(void)
static s32 AI_GetAbility(u32 battlerId, bool32 guess)
{
u8 battlerId = BattleAI_GetWantedBattler(gAIScriptPtr[1]);
// The AI knows its own ability.
if (IsBattlerAIControlled)
return gBattleMons[battlerId].ability;
if (!IsBattlerAIControlled(battlerId))
if (BATTLE_HISTORY->abilities[battlerId] != 0)
return BATTLE_HISTORY->abilities[battlerId];
// Abilities that prevent fleeing.
if (gBattleMons[battlerId].ability == ABILITY_SHADOW_TAG
|| gBattleMons[battlerId].ability == ABILITY_MAGNET_PULL
|| gBattleMons[battlerId].ability == ABILITY_ARENA_TRAP)
return gBattleMons[battlerId].ability;
if (gBaseStats[gBattleMons[battlerId].species].abilities[0] != ABILITY_NONE)
{
if (BATTLE_HISTORY->abilities[battlerId] != 0)
if (gBaseStats[gBattleMons[battlerId].species].abilities[1] != ABILITY_NONE)
{
AI_THINKING_STRUCT->funcResult = BATTLE_HISTORY->abilities[battlerId];
gAIScriptPtr += 2;
return;
}
// abilities that prevent fleeing.
if (gBattleMons[battlerId].ability == ABILITY_SHADOW_TAG
|| gBattleMons[battlerId].ability == ABILITY_MAGNET_PULL
|| gBattleMons[battlerId].ability == ABILITY_ARENA_TRAP)
{
AI_THINKING_STRUCT->funcResult = gBattleMons[battlerId].ability;
gAIScriptPtr += 2;
return;
}
if (gBaseStats[gBattleMons[battlerId].species].abilities[0] != ABILITY_NONE)
{
if (gBaseStats[gBattleMons[battlerId].species].abilities[1] != ABILITY_NONE)
{
// AI has no knowledge of opponent, so it guesses which ability.
if (Random() & 1)
AI_THINKING_STRUCT->funcResult = gBaseStats[gBattleMons[battlerId].species].abilities[0];
else
AI_THINKING_STRUCT->funcResult = gBaseStats[gBattleMons[battlerId].species].abilities[1];
}
else
{
AI_THINKING_STRUCT->funcResult = gBaseStats[gBattleMons[battlerId].species].abilities[0]; // It's definitely ability 1.
}
// AI has no knowledge of opponent, so it guesses which ability.
if (guess)
return gBaseStats[gBattleMons[battlerId].species].abilities[Random() & 1];
}
else
{
AI_THINKING_STRUCT->funcResult = gBaseStats[gBattleMons[battlerId].species].abilities[1]; // AI can't actually reach this part since no pokemon has ability 2 and no ability 1.
return gBaseStats[gBattleMons[battlerId].species].abilities[0]; // It's definitely ability 1.
}
}
else
{
// The AI knows its own ability.
AI_THINKING_STRUCT->funcResult = gBattleMons[battlerId].ability;
}
return -1; // Unknown.
}
static void BattleAICmd_get_ability(void)
{
AI_THINKING_STRUCT->funcResult = AI_GetAbility(BattleAI_GetWantedBattler(gAIScriptPtr[1]), TRUE);
gAIScriptPtr += 2;
}
static void BattleAICmd_check_ability(void)
{
u32 battlerId = BattleAI_GetWantedBattler(gAIScriptPtr[1]);
u32 ability = gAIScriptPtr[2];
u32 ability = AI_GetAbility(battlerId, FALSE);
if (!IsBattlerAIControlled(battlerId))
{
if (BATTLE_HISTORY->abilities[battlerId] != ABILITY_NONE)
{
ability = BATTLE_HISTORY->abilities[battlerId];
AI_THINKING_STRUCT->funcResult = ability;
}
// Abilities that prevent fleeing.
else if (gBattleMons[battlerId].ability == ABILITY_SHADOW_TAG
|| gBattleMons[battlerId].ability == ABILITY_MAGNET_PULL
|| gBattleMons[battlerId].ability == ABILITY_ARENA_TRAP)
{
ability = gBattleMons[battlerId].ability;
}
else if (gBaseStats[gBattleMons[battlerId].species].abilities[0] != ABILITY_NONE)
{
if (gBaseStats[gBattleMons[battlerId].species].abilities[1] != ABILITY_NONE)
{
u8 abilityDummyVariable = ability; // Needed to match.
if (gBaseStats[gBattleMons[battlerId].species].abilities[0] != abilityDummyVariable
&& gBaseStats[gBattleMons[battlerId].species].abilities[1] != abilityDummyVariable)
{
ability = gBaseStats[gBattleMons[battlerId].species].abilities[0];
}
else
{
ability = ABILITY_NONE;
}
}
else
{
ability = gBaseStats[gBattleMons[battlerId].species].abilities[0];
}
}
else
{
ability = gBaseStats[gBattleMons[battlerId].species].abilities[1]; // AI can't actually reach this part since no pokemon has ability 2 and no ability 1.
}
}
else
{
// The AI knows its own or partner's ability.
ability = gBattleMons[battlerId].ability;
}
if (ability == 0)
if (ability == -1)
AI_THINKING_STRUCT->funcResult = 2; // Unable to answer.
else if (ability == gAIScriptPtr[2])
AI_THINKING_STRUCT->funcResult = 1; // Pokemon has the ability we wanted to check.
@ -1878,15 +1855,7 @@ static void BattleAICmd_if_can_faint(void)
return;
}
gBattleStruct->dynamicMoveType = 0;
gMoveResultFlags = 0;
dmg = AI_CalcDamage(AI_THINKING_STRUCT->moveConsidered, sBattler_AI, gBattlerTarget);
dmg = dmg * AI_THINKING_STRUCT->simulatedRNG[AI_THINKING_STRUCT->movesetIndex] / 100;
// Moves always do at least 1 damage.
if (dmg == 0)
dmg = 1;
dmg = AI_THINKING_STRUCT->simulatedDmg[sBattler_AI][gBattlerTarget][AI_THINKING_STRUCT->movesetIndex];
if (gBattleMons[gBattlerTarget].hp <= dmg)
gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 1);
else
@ -1903,15 +1872,7 @@ static void BattleAICmd_if_cant_faint(void)
return;
}
gBattleStruct->dynamicMoveType = 0;
gMoveResultFlags = 0;
dmg = AI_CalcDamage(AI_THINKING_STRUCT->moveConsidered, sBattler_AI, gBattlerTarget);
dmg = dmg * AI_THINKING_STRUCT->simulatedRNG[AI_THINKING_STRUCT->movesetIndex] / 100;
// Moves always do at least 1 damage.
if (dmg == 0)
dmg = 1;
dmg = AI_THINKING_STRUCT->simulatedDmg[sBattler_AI][gBattlerTarget][AI_THINKING_STRUCT->movesetIndex];
if (gBattleMons[gBattlerTarget].hp > dmg)
gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 1);
else