Dynamic AI Functions (#4908)

* dynamic ai func

* add AI_TagBattlePreferFoe as an example

* Update src/battle_ai_main.c

* Update src/battle_ai_main.c

---------

Co-authored-by: ghoulslash <pokevoyager0@gmail.com>
Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com>
This commit is contained in:
ghoulslash 2024-07-09 07:50:48 -04:00 committed by GitHub
parent 49e93799d1
commit e1d8ef8190
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 63 additions and 1 deletions

View File

@ -2094,6 +2094,11 @@
setvar VAR_0x8006, \item
special CreateEnemyEventMon
.endm
.macro setdynamicaifunc func:req
callnative ScriptSetDynamicAiFunc
.4byte \func
.endm
@ Set up a totem boost for the next battle.
@ 'battler' is the position of the mon you want to gain a boost. see B_POSITION_xx in include/constants/battle.h.

View File

@ -1,6 +1,9 @@
#ifndef GUARD_BATTLE_AI_MAIN_H
#define GUARD_BATTLE_AI_MAIN_H
typedef s32 (*AiScoreFunc)(u32, u32, u32, s32);
#define UNKNOWN_NO_OF_HITS UINT32_MAX
// return vals for BattleAI_ChooseMoveOrAction
@ -101,6 +104,7 @@ void Ai_InitPartyStruct(void);
void Ai_UpdateSwitchInData(u32 battler);
void Ai_UpdateFaintData(u32 battler);
void SetAiLogicDataForTurn(struct AiLogicData *aiData);
void ResetDynamicAiFunc(void);
extern u8 sBattler_AI;

View File

@ -51,6 +51,7 @@
#define AI_FLAG_COUNT 20
// 'other' ai logic flags
#define AI_FLAG_DYNAMIC_FUNC (1 << 28) // Create custom AI functions for specific battles via "setdynamicaifunc" cmd
#define AI_FLAG_ROAMING (1 << 29)
#define AI_FLAG_SAFARI (1 << 30)
#define AI_FLAG_FIRST_BATTLE (1 << 31)

View File

@ -18,6 +18,7 @@
#include "random.h"
#include "recorded_battle.h"
#include "util.h"
#include "script.h"
#include "constants/abilities.h"
#include "constants/battle_ai.h"
#include "constants/battle_move_effects.h"
@ -39,6 +40,7 @@ static bool32 IsPinchBerryItemEffect(u32 holdEffect);
// ewram
EWRAM_DATA const u8 *gAIScriptPtr = NULL; // Still used in contests
EWRAM_DATA u8 sBattler_AI = 0;
EWRAM_DATA AiScoreFunc sDynamicAiFunc = NULL;
// const rom data
static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score);
@ -54,6 +56,7 @@ static s32 AI_Safari(u32 battlerAtk, u32 battlerDef, u32 move, s32 score);
static s32 AI_FirstBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score);
static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score);
static s32 AI_PowerfulStatus(u32 battlerAtk, u32 battlerDef, u32 move, s32 score);
static s32 AI_DynamicFunc(u32 battlerAtk, u32 battlerDef, u32 move, s32 score);
static s32 (*const sBattleAiFuncTable[])(u32, u32, u32, s32) =
@ -86,7 +89,7 @@ static s32 (*const sBattleAiFuncTable[])(u32, u32, u32, s32) =
[25] = NULL, // Unused
[26] = NULL, // Unused
[27] = NULL, // Unused
[28] = NULL, // Unused
[28] = AI_DynamicFunc, // AI_FLAG_DYNAMIC_FUNC
[29] = AI_Roaming, // AI_FLAG_ROAMING
[30] = AI_Safari, // AI_FLAG_SAFARI
[31] = AI_FirstBattle, // AI_FLAG_FIRST_BATTLE
@ -179,6 +182,9 @@ static u32 GetAiFlags(u16 trainerId)
// Automatically includes AI_FLAG_SMART_MON_CHOICES to improve smart switching
if (flags & AI_FLAG_SMART_SWITCHING)
flags |= AI_FLAG_SMART_MON_CHOICES;
if (sDynamicAiFunc != NULL)
flags |= AI_FLAG_DYNAMIC_FUNC;
return flags;
}
@ -5335,3 +5341,47 @@ static s32 AI_FirstBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
return score;
}
// Dynamic AI Functions
// For specific battle scenarios
// Example - prefer attacking opposite foe in a tag battle
s32 AI_TagBattlePreferFoe(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
{
if (!(gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER))
{
/* not a partner battle */
return score;
}
else if (!IsBattlerAlive(BATTLE_OPPOSITE(battlerAtk)) || !IsBattlerAlive(BATTLE_PARTNER(BATTLE_OPPOSITE(battlerAtk))))
{
/* partner is defeated so attack normally */
return score;
}
else if (battlerDef == BATTLE_OPPOSITE(battlerAtk))
{
/* attacking along the diagonal */
ADJUST_SCORE(-20);
}
return score;
}
static s32 AI_DynamicFunc(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
{
if (sDynamicAiFunc != NULL)
score = sDynamicAiFunc(battlerAtk, battlerDef, move, score);
return score;
}
void ScriptSetDynamicAiFunc(struct ScriptContext *ctx)
{
AiScoreFunc func = (AiScoreFunc)ScriptReadWord(ctx);
sDynamicAiFunc = func;
}
void ResetDynamicAiFunc(void)
{
sDynamicAiFunc = NULL;
}

View File

@ -1766,6 +1766,7 @@ static void FreeRestoreBattleData(void)
FreeMonSpritesGfx();
FreeBattleSpritesData();
FreeBattleResources();
ResetDynamicAiFunc();
}
void CB2_QuitRecordedBattle(void)
@ -5530,6 +5531,7 @@ static void FreeResetData_ReturnToOvOrDoEvolutions(void)
if (gBattleStruct != NULL && !(gBattleTypeFlags & BATTLE_TYPE_LINK))
{
ZeroEnemyPartyMons();
ResetDynamicAiFunc();
FreeMonSpritesGfx();
FreeBattleResources();
FreeBattleSpritesData();