mirror of
https://github.com/rh-hideout/pokeemerald-expansion.git
synced 2026-04-26 02:14:22 -05:00
Add Dynamic Switch AI Function (#8629)
This commit is contained in:
parent
9d62b2327f
commit
0551fcf408
|
|
@ -2129,6 +2129,11 @@
|
|||
.4byte \func
|
||||
.endm
|
||||
|
||||
.macro setdynamicswitchaifunc func:req
|
||||
callnative ScriptSetDynamicAiSwitchFunc, requests_effects=1
|
||||
.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.
|
||||
@ The rest of the arguments are the stat change values to each stat.
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
- [Tutorials]()
|
||||
- [What are AI Flags?](tutorials/ai_flags.md)
|
||||
- [How to add new AI Flags](tutorials/ai_logic.md)
|
||||
- [What are Dynamic AI Functions?](tutorials/ai_dynamic_functions.md)
|
||||
- [How to add new battle script commands/macros](tutorials/how_to_battle_script_command_macro.md)
|
||||
- [How to add a new move](tutorials/how_to_new_move.md)
|
||||
- [How to add a new trainer class]()
|
||||
|
|
|
|||
27
docs/tutorials/ai_dynamic_functions.md
Normal file
27
docs/tutorials/ai_dynamic_functions.md
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
# What are Dynamic AI Functions?
|
||||
Dynamic AI functions enable AI behaviour to be controlled on a per-battle basis by being set directly ahead of a particular battle when scripting. They allow for unique move scoring or switching decisions that are not applied at scale to multiple trainers or the entire AI as a whole.
|
||||
|
||||
As such they're really useful for one-shot battle setups like boss fights or totem Pokemon or narrative tie-ins like switch happy Jugglers that would benefit from specialized AI that only applies during those specific fights.
|
||||
|
||||
There are currently two different types of dynamic AI functions, one that affects move scoring and one that affects switching.
|
||||
|
||||
# How do I use the dynamic move scoring function?
|
||||
There are a few steps involved:
|
||||
- Be sure to set `AI_FLAG_DYNAMIC_FUNC` on the trainer you'll be using your unique behaviour for
|
||||
- Write your custom AI logic. Our example for this is `AI_TagBattlePreferFoe`, and you should match its arguments and return structure in your own custom function.
|
||||
- In the script the triggers the battle, add a call to `setdynamicaifunc` specifying your function, as in:
|
||||
```
|
||||
setdynamicaifunc AI_TagBattlePreferFoe
|
||||
multi_2_vs_2 TRAINER_SIRIUS_NOVA_HYPERION_TAG, Text_NovaInsurgence_Arrival_Hyperion_Loss, TRAINER_SIRIUS_NOVA_DEIMOS_RECRUIT_TAG, Text_NovaInsurgence_Arrival_DeimosRecruit_Loss, TRAINER_SIRIUS_WHARF_TRITON_PARTNER, TRAINER_BACK_PIC_TRITON
|
||||
```
|
||||
That's it! The scoring function will be used in the battle immediately following it, and automatically cleared at the end of the battle. You can then use `setdynamicaifunc` with the same or a different AI scoring function as you see fit.
|
||||
|
||||
# How do I use the dynamic switching function?
|
||||
There are a few steps involved:
|
||||
- Write your custom AI logic. Our example for this is `ShouldSwitchDynFuncExample`, and you should match its arguments and return structure in your own custom function.
|
||||
- In the script that triggers the battle, add a call to `setdynamicswitchaifunc` specifying your function, as in:
|
||||
```
|
||||
setdynamicswitchaifunc ShouldSwitchDynFuncExample
|
||||
trainerbattle_single TRAINER_TIANA, Route102_Text_TianaIntro, Route102_Text_TianaDefeated
|
||||
```
|
||||
That's it! The switching function will be used in the battle immediately following it, and automatically cleared at the end of the battle. You can then use `setdynamicswitchaifunc` with the same or a different AI scoring function as you see fit.
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
|
||||
typedef s32 (*AiScoreFunc)(u32, u32, u32, s32);
|
||||
typedef bool32 (*AiSwitchFunc)(u32);
|
||||
|
||||
#define UNKNOWN_NO_OF_HITS UINT32_MAX
|
||||
|
||||
|
|
@ -132,8 +133,10 @@ void Ai_InitPartyStruct(void);
|
|||
void Ai_UpdateSwitchInData(u32 battler);
|
||||
void Ai_UpdateFaintData(u32 battler);
|
||||
void SetAiLogicDataForTurn(struct AiLogicData *aiData);
|
||||
void ResetDynamicAiFunc(void);
|
||||
void ResetDynamicAiFunctions(void);
|
||||
void AI_TrySwitchOrUseItem(u32 battler);
|
||||
void CalcBattlerAiMovesData(struct AiLogicData *aiData, u32 battlerAtk, u32 battlerDef, u32 weather, u32 fieldStatus);
|
||||
|
||||
extern AiSwitchFunc gDynamicAiSwitchFunc;
|
||||
|
||||
#endif // GUARD_BATTLE_AI_MAIN_H
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ enum ShouldSwitchScenario
|
|||
SHOULD_SWITCH_ATTACKING_STAT_MINUS_TWO,
|
||||
SHOULD_SWITCH_ATTACKING_STAT_MINUS_THREE_PLUS,
|
||||
SHOULD_SWITCH_ALL_SCORES_BAD,
|
||||
SHOULD_SWITCH_DYN_FUNC,
|
||||
};
|
||||
|
||||
enum SwitchType
|
||||
|
|
|
|||
|
|
@ -302,6 +302,8 @@ bool32 IsPartyFullyHealedExceptBattler(u32 battler);
|
|||
bool32 PartyHasMoveCategory(u32 battlerId, enum DamageCategory category);
|
||||
bool32 SideHasMoveCategory(u32 battlerId, enum DamageCategory category);
|
||||
void GetAIPartyIndexes(u32 battlerId, s32 *firstId, s32 *lastId);
|
||||
u32 GetActiveBattlerIds(u32 battler, u32 *battlerIn1, u32 *battlerIn2);
|
||||
bool32 IsPartyMonOnFieldOrChosenToSwitch(u32 partyIndex, u32 battlerIn1, u32 battlerIn2);
|
||||
|
||||
// score increases
|
||||
u32 IncreaseStatUpScore(u32 battlerAtk, u32 battlerDef, enum StatChange statId);
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#define SHOULD_SWITCH_ATTACKING_STAT_MINUS_TWO_PERCENTAGE 50
|
||||
#define SHOULD_SWITCH_ATTACKING_STAT_MINUS_THREE_PLUS_PERCENTAGE 100
|
||||
#define SHOULD_SWITCH_ALL_SCORES_BAD_PERCENTAGE 100
|
||||
#define SHOULD_SWITCH_DYN_FUNC_PERCENTAGE 50 // Dynamic switching function switch chance
|
||||
|
||||
// AI smart switching chances for bad statuses
|
||||
#define SHOULD_SWITCH_PERISH_SONG_PERCENTAGE 100
|
||||
|
|
|
|||
|
|
@ -194,6 +194,7 @@ enum RandomTag
|
|||
RNG_AI_SWITCH_TRAPPER,
|
||||
RNG_AI_SWITCH_FREE_TURN,
|
||||
RNG_AI_SWITCH_ALL_MOVES_BAD,
|
||||
RNG_AI_SWITCH_DYN_FUNC,
|
||||
RNG_AI_CONSERVE_TERA,
|
||||
RNG_AI_SWITCH_ALL_SCORES_BAD,
|
||||
RNG_AI_SWITCH_ABSORBING_HIDDEN_POWER,
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ static void AI_CompareDamagingMoves(u32 battlerAtk, u32 battlerDef);
|
|||
// ewram
|
||||
EWRAM_DATA const u8 *gAIScriptPtr = NULL; // Still used in contests
|
||||
EWRAM_DATA AiScoreFunc sDynamicAiFunc = NULL;
|
||||
EWRAM_DATA AiSwitchFunc gDynamicAiSwitchFunc = NULL;
|
||||
|
||||
// const rom data
|
||||
static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score);
|
||||
|
|
@ -417,10 +418,9 @@ static void SetupRandomRollsForAIMoveSelection(u32 battler)
|
|||
void AI_TrySwitchOrUseItem(u32 battler)
|
||||
{
|
||||
struct Pokemon *party;
|
||||
u8 battlerIn1, battlerIn2;
|
||||
u32 battlerIn1, battlerIn2;
|
||||
s32 firstId;
|
||||
s32 lastId; // + 1
|
||||
u8 battlerPosition = GetBattlerPosition(battler);
|
||||
party = GetBattlerParty(battler);
|
||||
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER)
|
||||
|
|
@ -434,30 +434,14 @@ void AI_TrySwitchOrUseItem(u32 battler)
|
|||
s32 monToSwitchId = gAiLogicData->mostSuitableMonId[battler];
|
||||
if (monToSwitchId == PARTY_SIZE)
|
||||
{
|
||||
if (!IsDoubleBattle())
|
||||
{
|
||||
battlerIn1 = GetBattlerAtPosition(battlerPosition);
|
||||
battlerIn2 = battlerIn1;
|
||||
}
|
||||
else
|
||||
{
|
||||
battlerIn1 = GetBattlerAtPosition(battlerPosition);
|
||||
battlerIn2 = GetBattlerAtPosition(BATTLE_PARTNER(battlerPosition));
|
||||
}
|
||||
|
||||
GetActiveBattlerIds(battler, &battlerIn1, &battlerIn2);
|
||||
GetAIPartyIndexes(battler, &firstId, &lastId);
|
||||
|
||||
for (monToSwitchId = (lastId-1); monToSwitchId >= firstId; monToSwitchId--)
|
||||
{
|
||||
if (!IsValidForBattle(&party[monToSwitchId]))
|
||||
continue;
|
||||
if (monToSwitchId == gBattlerPartyIndexes[battlerIn1])
|
||||
continue;
|
||||
if (monToSwitchId == gBattlerPartyIndexes[battlerIn2])
|
||||
continue;
|
||||
if (monToSwitchId == gBattleStruct->monToSwitchIntoId[battlerIn1])
|
||||
continue;
|
||||
if (monToSwitchId == gBattleStruct->monToSwitchIntoId[battlerIn2])
|
||||
if (IsPartyMonOnFieldOrChosenToSwitch(monToSwitchId, battlerIn1, battlerIn2))
|
||||
continue;
|
||||
if (IsAceMon(battler, monToSwitchId))
|
||||
continue;
|
||||
|
|
@ -7081,7 +7065,16 @@ void ScriptSetDynamicAiFunc(struct ScriptContext *ctx)
|
|||
sDynamicAiFunc = func;
|
||||
}
|
||||
|
||||
void ResetDynamicAiFunc(void)
|
||||
void ScriptSetDynamicAiSwitchFunc(struct ScriptContext *ctx)
|
||||
{
|
||||
Script_RequestEffects(SCREFF_V1);
|
||||
|
||||
AiSwitchFunc func = (AiSwitchFunc)ScriptReadWord(ctx);
|
||||
gDynamicAiSwitchFunc = func;
|
||||
}
|
||||
|
||||
void ResetDynamicAiFunctions(void)
|
||||
{
|
||||
sDynamicAiFunc = NULL;
|
||||
gDynamicAiSwitchFunc = NULL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -182,6 +182,8 @@ u32 GetSwitchChance(enum ShouldSwitchScenario shouldSwitchScenario)
|
|||
return SHOULD_SWITCH_ATTACKING_STAT_MINUS_THREE_PLUS_PERCENTAGE;
|
||||
case SHOULD_SWITCH_ALL_SCORES_BAD:
|
||||
return SHOULD_SWITCH_ALL_SCORES_BAD_PERCENTAGE;
|
||||
case SHOULD_SWITCH_DYN_FUNC:
|
||||
return SHOULD_SWITCH_DYN_FUNC_PERCENTAGE;
|
||||
default:
|
||||
return 100;
|
||||
}
|
||||
|
|
@ -447,7 +449,7 @@ static bool32 ShouldSwitchIfAllMovesBad(u32 battler)
|
|||
// Switch if no moves affect opponents
|
||||
if (IsDoubleBattle())
|
||||
{
|
||||
u32 opposingPartner = GetBattlerAtPosition(BATTLE_PARTNER(opposingBattler));
|
||||
u32 opposingPartner = BATTLE_PARTNER(opposingBattler);
|
||||
for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++)
|
||||
{
|
||||
aiMove = gBattleMons[battler].moves[moveIndex];
|
||||
|
|
@ -513,7 +515,7 @@ static bool32 ShouldSwitchIfWonderGuard(u32 battler)
|
|||
|
||||
static bool32 FindMonThatAbsorbsOpponentsMove(u32 battler)
|
||||
{
|
||||
u8 battlerIn1, battlerIn2;
|
||||
u32 battlerIn1, battlerIn2;
|
||||
u8 numAbsorbingAbilities = 0;
|
||||
enum Ability absorbingTypeAbilities[3]; // Array size is maximum number of absorbing abilities for a single type
|
||||
s32 firstId;
|
||||
|
|
@ -554,20 +556,6 @@ static bool32 FindMonThatAbsorbsOpponentsMove(u32 battler)
|
|||
}
|
||||
}
|
||||
|
||||
if (IsDoubleBattle())
|
||||
{
|
||||
battlerIn1 = battler;
|
||||
if (gAbsentBattlerFlags & (1u << GetPartnerBattler(battler)))
|
||||
battlerIn2 = battler;
|
||||
else
|
||||
battlerIn2 = GetPartnerBattler(battler);
|
||||
}
|
||||
else
|
||||
{
|
||||
battlerIn1 = battler;
|
||||
battlerIn2 = battler;
|
||||
}
|
||||
|
||||
// Create an array of possible absorb abilities so the AI considers all of them
|
||||
if (incomingType == TYPE_FIRE)
|
||||
{
|
||||
|
|
@ -620,10 +608,11 @@ static bool32 FindMonThatAbsorbsOpponentsMove(u32 battler)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
// Check party for mon with ability that absorbs move
|
||||
GetActiveBattlerIds(battler, &battlerIn1, &battlerIn2);
|
||||
GetAIPartyIndexes(battler, &firstId, &lastId);
|
||||
party = GetBattlerParty(battler);
|
||||
|
||||
// Check party for mon with ability that absorbs move
|
||||
for (u32 monIndex = firstId; monIndex < lastId; monIndex++)
|
||||
{
|
||||
if (!IsValidForBattle(&party[monIndex]))
|
||||
|
|
@ -955,7 +944,7 @@ static bool32 CanUseSuperEffectiveMoveAgainstOpponents(u32 battler)
|
|||
if (!IsDoubleBattle())
|
||||
return FALSE;
|
||||
|
||||
opposingBattler = GetBattlerAtPosition(BATTLE_PARTNER(opposingPosition));
|
||||
opposingBattler = BATTLE_PARTNER(opposingPosition);
|
||||
|
||||
if (!(gAbsentBattlerFlags & (1u << opposingBattler)))
|
||||
{
|
||||
|
|
@ -994,19 +983,7 @@ static bool32 FindMonWithFlagsAndSuperEffective(u32 battler, u16 flags, u32 perc
|
|||
if (IsBattleMoveStatus(gLastLandedMoves[battler]))
|
||||
return FALSE;
|
||||
|
||||
if (IsDoubleBattle())
|
||||
{
|
||||
battlerIn1 = battler;
|
||||
if (gAbsentBattlerFlags & (1u << GetPartnerBattler(battler)))
|
||||
battlerIn2 = battler;
|
||||
else
|
||||
battlerIn2 = GetPartnerBattler(battler);
|
||||
}
|
||||
else
|
||||
{
|
||||
battlerIn1 = battler;
|
||||
battlerIn2 = battler;
|
||||
}
|
||||
GetActiveBattlerIds(battler, &battlerIn1, &battlerIn2);
|
||||
|
||||
GetAIPartyIndexes(battler, &firstId, &lastId);
|
||||
party = GetBattlerParty(battler);
|
||||
|
|
@ -1020,13 +997,7 @@ static bool32 FindMonWithFlagsAndSuperEffective(u32 battler, u16 flags, u32 perc
|
|||
|
||||
if (!IsValidForBattle(&party[monIndex]))
|
||||
continue;
|
||||
if (monIndex == gBattlerPartyIndexes[battlerIn1])
|
||||
continue;
|
||||
if (monIndex == gBattlerPartyIndexes[battlerIn2])
|
||||
continue;
|
||||
if (monIndex == gBattleStruct->monToSwitchIntoId[battlerIn1])
|
||||
continue;
|
||||
if (monIndex == gBattleStruct->monToSwitchIntoId[battlerIn2])
|
||||
if (IsPartyMonOnFieldOrChosenToSwitch(monIndex, battlerIn1, battlerIn2))
|
||||
continue;
|
||||
if (IsAceMon(battler, monIndex))
|
||||
continue;
|
||||
|
|
@ -1070,20 +1041,7 @@ static bool32 CanMonSurviveHazardSwitchin(u32 battler)
|
|||
// Battler will faint to hazards, check to see if another mon can clear them
|
||||
if (hazardDamage > battlerHp)
|
||||
{
|
||||
if (IsDoubleBattle())
|
||||
{
|
||||
battlerIn1 = battler;
|
||||
if (gAbsentBattlerFlags & (1u << GetPartnerBattler(battler)))
|
||||
battlerIn2 = battler;
|
||||
else
|
||||
battlerIn2 = GetPartnerBattler(battler);
|
||||
}
|
||||
else
|
||||
{
|
||||
battlerIn1 = battler;
|
||||
battlerIn2 = battler;
|
||||
}
|
||||
|
||||
GetActiveBattlerIds(battler, &battlerIn1, &battlerIn2);
|
||||
GetAIPartyIndexes(battler, &firstId, &lastId);
|
||||
party = GetBattlerParty(battler);
|
||||
|
||||
|
|
@ -1091,13 +1049,7 @@ static bool32 CanMonSurviveHazardSwitchin(u32 battler)
|
|||
{
|
||||
if (!IsValidForBattle(&party[monIndex]))
|
||||
continue;
|
||||
if (monIndex == gBattlerPartyIndexes[battlerIn1])
|
||||
continue;
|
||||
if (monIndex == gBattlerPartyIndexes[battlerIn2])
|
||||
continue;
|
||||
if (monIndex == gBattleStruct->monToSwitchIntoId[battlerIn1])
|
||||
continue;
|
||||
if (monIndex == gBattleStruct->monToSwitchIntoId[battlerIn2])
|
||||
if (IsPartyMonOnFieldOrChosenToSwitch(monIndex, battlerIn1, battlerIn2))
|
||||
continue;
|
||||
if (IsAceMon(battler, monIndex))
|
||||
continue;
|
||||
|
|
@ -1210,6 +1162,17 @@ static bool32 ShouldSwitchIfAttackingStatsLowered(u32 battler)
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
bool32 ShouldSwitchDynFuncExample(u32 battler)
|
||||
{
|
||||
// Chance to switch if trainer class is Guitarist, perhaps thematic for Jugglers
|
||||
if (GetTrainerClassFromId(TRAINER_BATTLE_PARAM.opponentA) == TRAINER_CLASS_GUITARIST
|
||||
&& RandomPercentage(RNG_AI_SWITCH_DYN_FUNC, GetSwitchChance(SHOULD_SWITCH_DYN_FUNC)))
|
||||
{
|
||||
return SetSwitchinAndSwitch(battler, PARTY_SIZE);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bool32 ShouldSwitch(u32 battler)
|
||||
{
|
||||
u32 battlerIn1, battlerIn2;
|
||||
|
|
@ -1235,21 +1198,7 @@ bool32 ShouldSwitch(u32 battler)
|
|||
|
||||
availableToSwitch = 0;
|
||||
|
||||
if (IsDoubleBattle())
|
||||
{
|
||||
u32 partner = BATTLE_PARTNER(battler);
|
||||
battlerIn1 = battler;
|
||||
if (gAbsentBattlerFlags & (1u << partner))
|
||||
battlerIn2 = battler;
|
||||
else
|
||||
battlerIn2 = partner;
|
||||
}
|
||||
else
|
||||
{
|
||||
battlerIn1 = battler;
|
||||
battlerIn2 = battler;
|
||||
}
|
||||
|
||||
GetActiveBattlerIds(battler, &battlerIn1, &battlerIn2);
|
||||
GetAIPartyIndexes(battler, &firstId, &lastId);
|
||||
party = GetBattlerParty(battler);
|
||||
|
||||
|
|
@ -1257,13 +1206,7 @@ bool32 ShouldSwitch(u32 battler)
|
|||
{
|
||||
if (!IsValidForBattle(&party[monIndex]))
|
||||
continue;
|
||||
if (monIndex == gBattlerPartyIndexes[battlerIn1])
|
||||
continue;
|
||||
if (monIndex == gBattlerPartyIndexes[battlerIn2])
|
||||
continue;
|
||||
if (monIndex == gBattleStruct->monToSwitchIntoId[battlerIn1])
|
||||
continue;
|
||||
if (monIndex == gBattleStruct->monToSwitchIntoId[battlerIn2])
|
||||
if (IsPartyMonOnFieldOrChosenToSwitch(monIndex, battlerIn1, battlerIn2))
|
||||
continue;
|
||||
if (IsAceMon(battler, monIndex))
|
||||
continue;
|
||||
|
|
@ -1272,7 +1215,12 @@ bool32 ShouldSwitch(u32 battler)
|
|||
}
|
||||
|
||||
if (availableToSwitch == 0)
|
||||
return FALSE;
|
||||
return FALSE;
|
||||
|
||||
// custom switching logic
|
||||
// NOTE: needs to always end with `return SetSwitchinAndSwitch` or `return FALSE`
|
||||
if (gDynamicAiSwitchFunc != NULL && gDynamicAiSwitchFunc(battler)) // Create custom AI functions for specific battles via "setdynamicswitchaifunc" cmd
|
||||
return TRUE;
|
||||
|
||||
// NOTE: The sequence of the below functions matter! Do not change unless you have carefully considered the outcome.
|
||||
// Since the order is sequential, and some of these functions prompt switch to specific party members.
|
||||
|
|
@ -1393,21 +1341,7 @@ void ModifySwitchAfterMoveScoring(u32 battler)
|
|||
|
||||
availableToSwitch = 0;
|
||||
|
||||
if (IsDoubleBattle())
|
||||
{
|
||||
u32 partner = BATTLE_PARTNER(battler);
|
||||
battlerIn1 = battler;
|
||||
if (gAbsentBattlerFlags & (1u << partner))
|
||||
battlerIn2 = battler;
|
||||
else
|
||||
battlerIn2 = partner;
|
||||
}
|
||||
else
|
||||
{
|
||||
battlerIn1 = battler;
|
||||
battlerIn2 = battler;
|
||||
}
|
||||
|
||||
GetActiveBattlerIds(battler, &battlerIn1, &battlerIn2);
|
||||
GetAIPartyIndexes(battler, &firstId, &lastId);
|
||||
party = GetBattlerParty(battler);
|
||||
|
||||
|
|
@ -2096,11 +2030,7 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
|
|||
for (u32 monIndex = firstId; monIndex < lastId; monIndex++)
|
||||
{
|
||||
// Check mon validity
|
||||
if (!IsValidForBattle(&party[monIndex])
|
||||
|| gBattlerPartyIndexes[battlerIn1] == monIndex
|
||||
|| gBattlerPartyIndexes[battlerIn2] == monIndex
|
||||
|| monIndex == gBattleStruct->monToSwitchIntoId[battlerIn1]
|
||||
|| monIndex == gBattleStruct->monToSwitchIntoId[battlerIn2])
|
||||
if (!IsValidForBattle(&party[monIndex]) || IsPartyMonOnFieldOrChosenToSwitch(monIndex, battlerIn1, battlerIn2))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
@ -2330,11 +2260,7 @@ static u32 GetBestMonVanilla(struct Pokemon *party, int firstId, int lastId, u32
|
|||
for (u32 monIndex = firstId; monIndex < lastId; monIndex++)
|
||||
{
|
||||
// Check mon validity
|
||||
if (!IsValidForBattle(&party[monIndex])
|
||||
|| gBattlerPartyIndexes[battlerIn1] == monIndex
|
||||
|| gBattlerPartyIndexes[battlerIn2] == monIndex
|
||||
|| monIndex == gBattleStruct->monToSwitchIntoId[battlerIn1]
|
||||
|| monIndex == gBattleStruct->monToSwitchIntoId[battlerIn2])
|
||||
if (!IsValidForBattle(&party[monIndex]) || IsPartyMonOnFieldOrChosenToSwitch(monIndex, battlerIn1, battlerIn2))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
@ -2421,11 +2347,7 @@ static u32 GetNextMonInParty(struct Pokemon *party, int firstId, int lastId, u32
|
|||
for (u32 monIndex = firstId; monIndex < lastId; monIndex++)
|
||||
{
|
||||
// Check mon validity
|
||||
if (!IsValidForBattle(&party[monIndex])
|
||||
|| gBattlerPartyIndexes[battlerIn1] == monIndex
|
||||
|| gBattlerPartyIndexes[battlerIn2] == monIndex
|
||||
|| monIndex == gBattleStruct->monToSwitchIntoId[battlerIn1]
|
||||
|| monIndex == gBattleStruct->monToSwitchIntoId[battlerIn2])
|
||||
if (!IsValidForBattle(&party[monIndex]) || IsPartyMonOnFieldOrChosenToSwitch(monIndex, battlerIn1, battlerIn2))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
@ -2448,25 +2370,7 @@ u32 GetMostSuitableMonToSwitchInto(u32 battler, enum SwitchType switchType)
|
|||
if (gBattleTypeFlags & BATTLE_TYPE_ARENA)
|
||||
return gBattlerPartyIndexes[battler] + 1;
|
||||
|
||||
if (IsDoubleBattle())
|
||||
{
|
||||
battlerIn1 = battler;
|
||||
if (gAbsentBattlerFlags & (1u << GetPartnerBattler(battler)))
|
||||
battlerIn2 = battler;
|
||||
else
|
||||
battlerIn2 = GetPartnerBattler(battler);
|
||||
|
||||
opposingBattler = BATTLE_OPPOSITE(battlerIn1);
|
||||
if (gAbsentBattlerFlags & (1u << opposingBattler))
|
||||
opposingBattler ^= BIT_FLANK;
|
||||
}
|
||||
else
|
||||
{
|
||||
opposingBattler = GetOppositeBattler(battler);
|
||||
battlerIn1 = battler;
|
||||
battlerIn2 = battler;
|
||||
}
|
||||
|
||||
opposingBattler = GetActiveBattlerIds(battler, &battlerIn1, &battlerIn2);
|
||||
GetAIPartyIndexes(battler, &firstId, &lastId);
|
||||
party = GetBattlerParty(battler);
|
||||
|
||||
|
|
|
|||
|
|
@ -6320,3 +6320,40 @@ bool32 CanMoveBeBouncedBack(u32 battler, u32 move)
|
|||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
u32 GetActiveBattlerIds(u32 battler, u32 *battlerIn1, u32 *battlerIn2)
|
||||
{
|
||||
u32 opposingBattler = 0;
|
||||
u32 battlerPosition = GetBattlerPosition(battler);
|
||||
if (IsDoubleBattle())
|
||||
{
|
||||
*battlerIn1 = battler;
|
||||
if (gAbsentBattlerFlags & (1u << BATTLE_PARTNER(battler)))
|
||||
*battlerIn2 = battler;
|
||||
else
|
||||
*battlerIn2 = GetBattlerAtPosition(BATTLE_PARTNER(battlerPosition));
|
||||
|
||||
opposingBattler = BATTLE_OPPOSITE(*battlerIn1);
|
||||
if (gAbsentBattlerFlags & (1u << opposingBattler))
|
||||
opposingBattler ^= BIT_FLANK;
|
||||
}
|
||||
else
|
||||
{
|
||||
opposingBattler = GetBattlerAtPosition(BATTLE_OPPOSITE(battlerPosition));
|
||||
*battlerIn1 = battler;
|
||||
*battlerIn2 = battler;
|
||||
}
|
||||
|
||||
return opposingBattler;
|
||||
}
|
||||
|
||||
bool32 IsPartyMonOnFieldOrChosenToSwitch(u32 partyIndex, u32 battlerIn1, u32 battlerIn2)
|
||||
{
|
||||
if (partyIndex == gBattlerPartyIndexes[battlerIn1]
|
||||
|| partyIndex == gBattlerPartyIndexes[battlerIn2])
|
||||
return TRUE;
|
||||
if (partyIndex == gBattleStruct->monToSwitchIntoId[battlerIn1]
|
||||
|| partyIndex == gBattleStruct->monToSwitchIntoId[battlerIn2])
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1774,7 +1774,7 @@ static void FreeRestoreBattleData(void)
|
|||
FreeMonSpritesGfx();
|
||||
FreeBattleSpritesData();
|
||||
FreeBattleResources();
|
||||
ResetDynamicAiFunc();
|
||||
ResetDynamicAiFunctions();
|
||||
}
|
||||
|
||||
void CB2_QuitRecordedBattle(void)
|
||||
|
|
@ -5612,7 +5612,7 @@ static void FreeResetData_ReturnToOvOrDoEvolutions(void)
|
|||
{
|
||||
ZeroEnemyPartyMons();
|
||||
}
|
||||
ResetDynamicAiFunc();
|
||||
ResetDynamicAiFunctions();
|
||||
FreeMonSpritesGfx();
|
||||
FreeBattleResources();
|
||||
FreeBattleSpritesData();
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user