diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/battle_ai_script_commands.c | 2022 |
1 files changed, 2022 insertions, 0 deletions
diff --git a/src/battle_ai_script_commands.c b/src/battle_ai_script_commands.c new file mode 100644 index 000000000..0ce6b6c35 --- /dev/null +++ b/src/battle_ai_script_commands.c @@ -0,0 +1,2022 @@ +#include "global.h" +#include "battle.h" +#include "item.h" +#include "pokemon.h" +#include "constants/species.h" +#include "constants/abilities.h" +#include "constants/battle_ai.h" + +extern u16 Random(void); +extern void sub_80C7164(void); + +#define AI_ACTION_DONE 0x0001 +#define AI_ACTION_FLEE 0x0002 +#define AI_ACTION_WATCH 0x0004 +#define AI_ACTION_DO_NOT_ATTACK 0x0008 +#define AI_ACTION_UNK5 0x0010 +#define AI_ACTION_UNK6 0x0020 +#define AI_ACTION_UNK7 0x0040 +#define AI_ACTION_UNK8 0x0080 + +#define AI_THINKING_STRUCT ((struct AI_ThinkingStruct *)(gBattleResources->ai)) +#define BATTLE_HISTORY ((struct BattleHistory *)(gBattleResources->battleHistory)) + +// AI states +enum +{ + AIState_SettingUp, + AIState_Processing, + AIState_FinishedProcessing, + AIState_DoNotProcess +}; + +extern const u8 *gAIScriptPtr; +extern u8 *BattleAIs[]; +extern u16 gLastUsedMove[]; + +typedef void (*BattleAICmdFunc)(void); + +extern const BattleAICmdFunc sBattleAICmdTable[]; + +// TODO: move these +extern u8 sBattler_AI; +extern const u32 gBitTable[]; // util.h +extern u32 gStatuses3[]; // battle_2.h +extern u16 gSideAffecting[2]; +extern const struct BattleMove gBattleMoves[]; +extern u16 gBattlerPartyIndexes[]; +extern u16 gDynamicBasePower; +extern u8 gMoveResultFlags; +extern u8 gCritMultiplier; +extern u16 gCurrentMove; +extern s32 gBattleMoveDamage; + +void BattleAI_SetupAIData(void); +void BattleAI_DoAIProcessing(void); +void AIStackPushVar(const u8 *ptr); +bool8 AIStackPop(void); + +void BattleAI_HandleItemUseBeforeAISetup(void) +{ + s32 i; + u8 *data = (u8 *)BATTLE_HISTORY; + + for (i = 0; i < sizeof(struct BattleHistory); i++) + data[i] = 0; + + // Items are allowed to use in ONLY trainer battles. + // TODO: Use proper flags + if ((gBattleTypeFlags & 0x8) + && (gTrainerBattleOpponent_A != 0x400) + && !(gBattleTypeFlags & 0x80982) + ) + { + for (i = 0; i < 4; i++) + { + if (gTrainers[gTrainerBattleOpponent_A].items[i] != 0) + { + BATTLE_HISTORY->trainerItems[BATTLE_HISTORY->itemsNo] = gTrainers[gTrainerBattleOpponent_A].items[i]; + BATTLE_HISTORY->itemsNo++; + } + } + } + + BattleAI_SetupAIData(); +} + +void BattleAI_SetupAIData(void) +{ + s32 i; + u8 *data = (u8 *)AI_THINKING_STRUCT; + u8 moveLimitations; + + // Clear AI data. + for (i = 0; i < sizeof(struct AI_ThinkingStruct); i++) + data[i] = 0; + + for (i = 0; i < 4; i++) + AI_THINKING_STRUCT->score[i] = 100; + + moveLimitations = CheckMoveLimitations(gActiveBattler, 0, 0xFF); + + // Ignore moves that aren't possible to use. + for (i = 0; i < 4; i++) + { + 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) + { + gBattlerTarget = (Random() & BIT_FLANK); + + if (gAbsentBattlerFlags & gBitTable[gBattlerTarget]) + gBattlerTarget ^= BIT_FLANK; + } + // There's only one choice in single battles. + else + { + gBattlerTarget = sBattler_AI ^ BIT_SIDE; + } + + // Choose proper trainer ai scripts. + // Fire Red, why all the returns?!? + if (gBattleTypeFlags & BATTLE_TYPE_SAFARI) // _080C6E84 + { + AI_THINKING_STRUCT->aiFlags = AI_SCRIPT_SAFARI; + return; + } + else if (gBattleTypeFlags & BATTLE_TYPE_ROAMER) // _080C6EAC + { + AI_THINKING_STRUCT->aiFlags = AI_SCRIPT_ROAMING; + return; + } + else if (!(gBattleTypeFlags & (0x80900)) && (gTrainerBattleOpponent_A != 0x400)) // _080C6ECC + { + if(gBattleTypeFlags & (0x80 << 10)) + { + AI_THINKING_STRUCT->aiFlags = 1; + return; + } + else if(gBattleTypeFlags & (0x80 << 11)) + { + AI_THINKING_STRUCT->aiFlags = 7; + return; + } + } + else + { + AI_THINKING_STRUCT->aiFlags = 7; + return; + } + AI_THINKING_STRUCT->aiFlags = gTrainers[gTrainerBattleOpponent_A].aiFlags; +} + +u8 BattleAI_GetAIActionToUse(void) +{ + u8 currentMoveArray[MAX_MON_MOVES]; + u8 consideredMoveArray[MAX_MON_MOVES]; + u8 numOfBestMoves; + s32 i; + + sub_80C7164(); + while (AI_THINKING_STRUCT->aiFlags != 0) + { + if (AI_THINKING_STRUCT->aiFlags & 1) + { + AI_THINKING_STRUCT->aiState = AIState_SettingUp; + BattleAI_DoAIProcessing(); + } + AI_THINKING_STRUCT->aiFlags >>= 1; + AI_THINKING_STRUCT->aiLogicId++; + AI_THINKING_STRUCT->movesetIndex = 0; + } + + // special flee or watch cases for safari. + if (AI_THINKING_STRUCT->aiAction & (AI_ACTION_FLEE)) // flee + return 4; + if (AI_THINKING_STRUCT->aiAction & (AI_ACTION_WATCH)) // watch + return 5; + + numOfBestMoves = 1; + currentMoveArray[0] = AI_THINKING_STRUCT->score[0]; + consideredMoveArray[0] = 0; + + for (i = 1; i < MAX_MON_MOVES; i++) + { + if (currentMoveArray[0] < AI_THINKING_STRUCT->score[i]) + { + numOfBestMoves = 1; + currentMoveArray[0] = AI_THINKING_STRUCT->score[i]; + consideredMoveArray[0] = i; + } + if (currentMoveArray[0] == AI_THINKING_STRUCT->score[i]) + { + currentMoveArray[numOfBestMoves] = AI_THINKING_STRUCT->score[i]; + consideredMoveArray[numOfBestMoves++] = i; + } + } + + return consideredMoveArray[Random() % numOfBestMoves]; // break any ties that exist. +} + +void BattleAI_DoAIProcessing(void) +{ + while (AI_THINKING_STRUCT->aiState != AIState_FinishedProcessing) + { + switch (AI_THINKING_STRUCT->aiState) + { + case AIState_DoNotProcess: //Needed to match. + break; + case AIState_SettingUp: + gAIScriptPtr = BattleAIs[AI_THINKING_STRUCT->aiLogicId]; // set the AI ptr. + if (gBattleMons[sBattler_AI].pp[AI_THINKING_STRUCT->movesetIndex] == 0) + { + AI_THINKING_STRUCT->moveConsidered = 0; // don't consider a move you have 0 PP for, idiot. + } + else + { + AI_THINKING_STRUCT->moveConsidered = gBattleMons[sBattler_AI].moves[AI_THINKING_STRUCT->movesetIndex]; + } + AI_THINKING_STRUCT->aiState++; + break; + case AIState_Processing: + if (AI_THINKING_STRUCT->moveConsidered != 0) + sBattleAICmdTable[*gAIScriptPtr](); // run AI command. + else + { + AI_THINKING_STRUCT->score[AI_THINKING_STRUCT->movesetIndex] = 0; // definitely do not consider any move that has 0 PP. + AI_THINKING_STRUCT->aiAction |= AI_ACTION_DONE; + } + if (AI_THINKING_STRUCT->aiAction & AI_ACTION_DONE) + { + AI_THINKING_STRUCT->movesetIndex++; + if (AI_THINKING_STRUCT->movesetIndex < MAX_MON_MOVES && (AI_THINKING_STRUCT->aiAction & AI_ACTION_DO_NOT_ATTACK) == 0) + AI_THINKING_STRUCT->aiState = AIState_SettingUp; // as long as their are more moves to process, keep setting this to setup state. + else + AI_THINKING_STRUCT->aiState++; // done processing. + AI_THINKING_STRUCT->aiAction &= (AI_ACTION_FLEE | AI_ACTION_WATCH | AI_ACTION_DO_NOT_ATTACK | + AI_ACTION_UNK5 | AI_ACTION_UNK6 | AI_ACTION_UNK7 | AI_ACTION_UNK8); // disable AI_ACTION_DONE. + } + break; + } + } +} + +void sub_80C7164(void) +{ + s32 i; + + for (i = 0; i < 8; i++) + { + if (BATTLE_HISTORY->usedMoves[gBattlerTarget >> 1][i] == 0) + { + BATTLE_HISTORY->usedMoves[gBattlerTarget >> 1][i] = gLastUsedMove[gBattlerTarget]; + return; + } + } +} + +void sub_80C71A8(u8 a) +{ + s32 i; + + for (i = 0; i < 8; i++) + BATTLE_HISTORY->usedMoves[a / 2][i] = 0; +} + +void sub_80C71D0(u8 a, u8 b) +{ + if (GetBankSide(a) == 0) + BATTLE_HISTORY->abilities[GetBankIdentity(a) & 1] = b; +} + +void sub_80C7208(u8 a, u8 b) +{ + if (GetBankSide(a) == 0) + BATTLE_HISTORY->itemEffects[GetBankIdentity(a) & 1] = b; +} + +static void BattleAICmd_if_random_less_than(void) +{ + if (Random() % 256 < gAIScriptPtr[1]) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 2); + else + gAIScriptPtr += 6; +} + +static void BattleAICmd_if_random_greater_than(void) +{ + if (Random() % 256 > gAIScriptPtr[1]) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 2); + else + gAIScriptPtr += 6; +} + +static void BattleAICmd_if_random_equal(void) +{ + if (Random() % 256 == gAIScriptPtr[1]) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 2); + else + gAIScriptPtr += 6; +} + +static void BattleAICmd_if_random_not_equal(void) +{ + if (Random() % 256 != gAIScriptPtr[1]) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 2); + else + gAIScriptPtr += 6; +} + +static void BattleAICmd_score(void) +{ + AI_THINKING_STRUCT->score[AI_THINKING_STRUCT->movesetIndex] += gAIScriptPtr[1]; // add the result to the array of the move consider's score. + + if (AI_THINKING_STRUCT->score[AI_THINKING_STRUCT->movesetIndex] < 0) // if the score is negative, flatten it to 0. + AI_THINKING_STRUCT->score[AI_THINKING_STRUCT->movesetIndex] = 0; + + gAIScriptPtr += 2; // AI return. +} + +enum { + TARGET, + USER +}; + +static void BattleAICmd_if_hp_less_than(void) +{ + u16 index; + + if (gAIScriptPtr[1] == USER) + index = sBattler_AI; + else + index = gBattlerTarget; + + if ((u32)(100 * gBattleMons[index].hp / gBattleMons[index].maxHP) < gAIScriptPtr[2]) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 3); + else + gAIScriptPtr += 7; +} + +static void BattleAICmd_if_hp_more_than(void) +{ + u16 index; + + if (gAIScriptPtr[1] == USER) + index = sBattler_AI; + else + index = gBattlerTarget; + + if ((u32)(100 * gBattleMons[index].hp / gBattleMons[index].maxHP) > gAIScriptPtr[2]) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 3); + else + gAIScriptPtr += 7; +} + +static void BattleAICmd_if_hp_equal(void) +{ + u16 index; + + if (gAIScriptPtr[1] == USER) + index = sBattler_AI; + else + index = gBattlerTarget; + + if ((u32)(100 * gBattleMons[index].hp / gBattleMons[index].maxHP) == gAIScriptPtr[2]) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 3); + else + gAIScriptPtr += 7; +} + +static void BattleAICmd_if_hp_not_equal(void) +{ + u16 index; + + if (gAIScriptPtr[1] == USER) + index = sBattler_AI; + else + index = gBattlerTarget; + + if ((u32)(100 * gBattleMons[index].hp / gBattleMons[index].maxHP) != gAIScriptPtr[2]) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 3); + else + gAIScriptPtr += 7; +} + +static void BattleAICmd_if_status(void) +{ + u16 index; + u32 arg; + + if (gAIScriptPtr[1] == USER) + index = sBattler_AI; + else + index = gBattlerTarget; + + arg = T1_READ_32(gAIScriptPtr + 2); + + if ((gBattleMons[index].status1 & arg) != 0) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 6); + else + gAIScriptPtr += 10; +} + +static void BattleAICmd_if_not_status(void) +{ + u16 index; + u32 arg; + + if (gAIScriptPtr[1] == USER) + index = sBattler_AI; + else + index = gBattlerTarget; + + arg = T1_READ_32(gAIScriptPtr + 2); + + if ((gBattleMons[index].status1 & arg) == 0) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 6); + else + gAIScriptPtr += 10; +} + +static void BattleAICmd_if_status2(void) +{ + u16 index; + u32 arg; + + if (gAIScriptPtr[1] == USER) + index = sBattler_AI; + else + index = gBattlerTarget; + + arg = T1_READ_32(gAIScriptPtr + 2); + + if ((gBattleMons[index].status2 & arg) != 0) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 6); + else + gAIScriptPtr += 10; +} + +static void BattleAICmd_if_not_status2(void) +{ + u16 index; + u32 arg; + + if (gAIScriptPtr[1] == USER) + index = sBattler_AI; + else + index = gBattlerTarget; + + arg = T1_READ_32(gAIScriptPtr + 2); + + if ((gBattleMons[index].status2 & arg) == 0) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 6); + else + gAIScriptPtr += 10; +} + +static void BattleAICmd_if_status3(void) +{ + u16 index; + u32 arg; + + if (gAIScriptPtr[1] == USER) + index = sBattler_AI; + else + index = gBattlerTarget; + + arg = T1_READ_32(gAIScriptPtr + 2); + + if ((gStatuses3[index] & arg) != 0) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 6); + else + gAIScriptPtr += 10; +} + +static void BattleAICmd_if_not_status3(void) +{ + u16 index; + u32 arg; + + if (gAIScriptPtr[1] == USER) + index = sBattler_AI; + else + index = gBattlerTarget; + + arg = T1_READ_32(gAIScriptPtr + 2); + + if ((gStatuses3[index] & arg) == 0) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 6); + else + gAIScriptPtr += 10; +} + +static void BattleAICmd_if_status4(void) +{ + u16 index; + u32 arg1, arg2; + + if (gAIScriptPtr[1] == USER) + index = sBattler_AI; + else + index = gBattlerTarget; + + arg1 = GetBankIdentity(index) & 1; + arg2 = T1_READ_32(gAIScriptPtr + 2); + + if ((gSideAffecting[arg1] & arg2) != 0) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 6); + else + gAIScriptPtr += 10; +} + +static void BattleAICmd_if_not_status4(void) +{ + u16 index; + u32 arg1, arg2; + + if (gAIScriptPtr[1] == USER) + index = sBattler_AI; + else + index = gBattlerTarget; + + arg1 = GetBankIdentity(index) & 1; + arg2 = T1_READ_32(gAIScriptPtr + 2); + + if ((gSideAffecting[arg1] & arg2) == 0) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 6); + else + gAIScriptPtr += 10; +} + +static void BattleAICmd_if_less_than(void) +{ + if (AI_THINKING_STRUCT->funcResult < gAIScriptPtr[1]) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 2); + else + gAIScriptPtr += 6; +} + +static void BattleAICmd_if_more_than(void) +{ + if (AI_THINKING_STRUCT->funcResult > gAIScriptPtr[1]) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 2); + else + gAIScriptPtr += 6; +} + +static void BattleAICmd_if_equal(void) +{ + if (AI_THINKING_STRUCT->funcResult == gAIScriptPtr[1]) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 2); + else + gAIScriptPtr += 6; +} + +static void BattleAICmd_if_not_equal(void) +{ + if (AI_THINKING_STRUCT->funcResult != gAIScriptPtr[1]) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 2); + else + gAIScriptPtr += 6; +} + +static void BattleAICmd_if_less_than_32(void) +{ + u8 *temp = T1_READ_PTR(gAIScriptPtr + 1); + + if (AI_THINKING_STRUCT->funcResult < *temp) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 5); + else + gAIScriptPtr += 9; +} + +static void BattleAICmd_if_more_than_32(void) +{ + u8 *temp = T1_READ_PTR(gAIScriptPtr + 1); + + if (AI_THINKING_STRUCT->funcResult > *temp) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 5); + else + gAIScriptPtr += 9; +} + +static void BattleAICmd_if_equal_32(void) +{ + u8 *temp = T1_READ_PTR(gAIScriptPtr + 1); + + if (AI_THINKING_STRUCT->funcResult == *temp) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 5); + else + gAIScriptPtr += 9; +} + +static void BattleAICmd_if_not_equal_32(void) +{ + u8 *temp = T1_READ_PTR(gAIScriptPtr + 1); + + if (AI_THINKING_STRUCT->funcResult != *temp) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 5); + else + gAIScriptPtr += 9; +} + +static void BattleAICmd_if_move(void) +{ + u16 move = T1_READ_16(gAIScriptPtr + 1); + + if (AI_THINKING_STRUCT->moveConsidered == move) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 3); + else + gAIScriptPtr += 7; +} + +static void BattleAICmd_if_not_move(void) +{ + u16 move = T1_READ_16(gAIScriptPtr + 1); + + if (AI_THINKING_STRUCT->moveConsidered != move) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 3); + else + gAIScriptPtr += 7; +} + +static void BattleAICmd_if_in_bytes(void) +{ + u8 *ptr = T1_READ_PTR(gAIScriptPtr + 1); + + while (*ptr != 0xFF) + { + if (AI_THINKING_STRUCT->funcResult == *ptr) + { + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 5); + return; + } + ptr++; + } + gAIScriptPtr += 9; +} + +static void BattleAICmd_if_not_in_bytes(void) +{ + u8 *ptr = T1_READ_PTR(gAIScriptPtr + 1); + + while (*ptr != 0xFF) + { + if (AI_THINKING_STRUCT->funcResult == *ptr) + { + gAIScriptPtr += 9; + return; + } + ptr++; + } + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 5); +} + +static void BattleAICmd_if_in_words(void) +{ + u16 *ptr = (u16 *)T1_READ_PTR(gAIScriptPtr + 1); + + while (*ptr != 0xFFFF) + { + if (AI_THINKING_STRUCT->funcResult == *ptr) + { + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 5); + return; + } + ptr++; + } + gAIScriptPtr += 9; +} + +static void BattleAICmd_if_not_in_words(void) +{ + u16 *ptr = (u16 *)T1_READ_PTR(gAIScriptPtr + 1); + + while (*ptr != 0xFFFF) + { + if (AI_THINKING_STRUCT->funcResult == *ptr) + { + gAIScriptPtr += 9; + return; + } + ptr++; + } + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 5); +} + +static void BattleAICmd_if_user_can_damage(void) +{ + s32 i; + + for (i = 0; i < MAX_MON_MOVES; i++) + { + if (gBattleMons[sBattler_AI].moves[i] != 0 + && gBattleMoves[gBattleMons[sBattler_AI].moves[i]].power != 0) + break; + } + if (i == MAX_MON_MOVES) + gAIScriptPtr += 5; + else + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 1); +} + +static void BattleAICmd_if_user_cant_damage(void) +{ + s32 i; + + for (i = 0; i < MAX_MON_MOVES; i++) + { + if (gBattleMons[sBattler_AI].moves[i] != 0 + && gBattleMoves[gBattleMons[sBattler_AI].moves[i]].power != 0) + break; + } + if (i != MAX_MON_MOVES) + gAIScriptPtr += 5; + else + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 1); +} + +static void BattleAICmd_get_turn_count(void) +{ + AI_THINKING_STRUCT->funcResult = gBattleResults.battleTurnCounter; + gAIScriptPtr += 1; +} + +static void BattleAICmd_get_type(void) +{ + switch (gAIScriptPtr[1]) + { + case 1: // player primary type + AI_THINKING_STRUCT->funcResult = gBattleMons[sBattler_AI].type1; + break; + case 0: // enemy primary type + AI_THINKING_STRUCT->funcResult = gBattleMons[gBattlerTarget].type1; + break; + case 3: // player secondary type + AI_THINKING_STRUCT->funcResult = gBattleMons[sBattler_AI].type2; + break; + case 2: // enemy secondary type + AI_THINKING_STRUCT->funcResult = gBattleMons[gBattlerTarget].type2; + break; + case 4: // type of move being pointed to + AI_THINKING_STRUCT->funcResult = gBattleMoves[AI_THINKING_STRUCT->moveConsidered].type; + break; + } + gAIScriptPtr += 2; +} + +static void BattleAICmd_get_move_power(void) +{ + AI_THINKING_STRUCT->funcResult = gBattleMoves[AI_THINKING_STRUCT->moveConsidered].power; + gAIScriptPtr += 1; +} + +// still a nonmatching +#ifdef NONMATCHING +void BattleAICmd_is_most_powerful_move(void) +{ + int i, j; + s32 damages[MAX_MON_MOVES]; + + for (i = 0; sDiscouragedPowerfulMoveEffects[i] != 0xFFFF; i++) + if (gBattleMoves[AI_THINKING_STRUCT->moveConsidered].effect == sDiscouragedPowerfulMoveEffects[i]) + break; + + if (gBattleMoves[AI_THINKING_STRUCT->moveConsidered].power > 1 + && sDiscouragedPowerfulMoveEffects[i] == 0xFFFF) + { + gDynamicBasePower = 0; + eDynamicMoveType = 0; + eDmgMultiplier = 1; + gMoveResultFlags = 0; + gCritMultiplier = 1; + + for (i = 0; i < MAX_MON_MOVES; i++) + { + for (j = 0; sDiscouragedPowerfulMoveEffects[j] != 0xFFFF; j++) + { // _08108276 + if (gBattleMoves[gBattleMons[gBankAttacker].moves[i]].effect == sDiscouragedPowerfulMoveEffects[j]) + break; + } + + // _081082BA + if (gBattleMons[gBankAttacker].moves[i] + && sDiscouragedPowerfulMoveEffects[j] == 0xFFFF + && gBattleMoves[gBattleMons[gBankAttacker].moves[i]].power > 1) + { + gCurrentMove = gBattleMons[gBankAttacker].moves[i]; + AI_CalcDmg(gBankAttacker, gBankTarget); + TypeCalc(gCurrentMove, gBankAttacker, gBankTarget); + damages[i] = (gBattleMoveDamage * AI_THINKING_STRUCT->simulatedRNG[i]) / 100; + + if (damages[i] == 0) // moves always do at least 1 damage. + damages[i] = 1; + } + else + { + damages[i] = 0; + } + } + + for (i = 0; i < MAX_MON_MOVES; i++) + if (damages[i] > damages[AI_THINKING_STRUCT->movesetIndex]) + break; + + if (i == MAX_MON_MOVES) + AI_THINKING_STRUCT->funcResult = 2; + else + AI_THINKING_STRUCT->funcResult = 1; + } + else + { + AI_THINKING_STRUCT->funcResult = 0; + } + + gAIScriptPtr += 1; +} +#else +NAKED +void BattleAICmd_is_most_powerful_move(void) +{ + asm(".syntax unified\n\ + push {r4-r7,lr}\n\ + mov r7, r10\n\ + mov r6, r9\n\ + mov r5, r8\n\ + push {r5-r7}\n\ + sub sp, 0x14\n\ + movs r3, 0\n\ + ldr r0, _080C80A4 @ =gUnknown_83F571C\n\ + ldrh r1, [r0]\n\ + ldr r5, _080C80A8 @ =0x0000ffff\n\ + ldr r6, _080C80AC @ =gBattleMoves\n\ + ldr r2, _080C80B0 @ =gBattleResources\n\ + cmp r1, r5\n\ + beq _080C7FA2\n\ + ldr r0, [r2]\n\ + ldr r0, [r0, 0x14]\n\ + ldrh r1, [r0, 0x2]\n\ + lsls r0, r1, 1\n\ + adds r0, r1\n\ + lsls r0, 2\n\ + adds r0, r6\n\ + ldrb r4, [r0]\n\ + ldr r1, _080C80A4 @ =gUnknown_83F571C\n\ +_080C7F92:\n\ + ldrh r0, [r1]\n\ + cmp r4, r0\n\ + beq _080C7FA2\n\ + adds r1, 0x2\n\ + adds r3, 0x1\n\ + ldrh r0, [r1]\n\ + cmp r0, r5\n\ + bne _080C7F92\n\ +_080C7FA2:\n\ + ldr r0, [r2]\n\ + ldr r0, [r0, 0x14]\n\ + ldrh r1, [r0, 0x2]\n\ + lsls r0, r1, 1\n\ + adds r0, r1\n\ + lsls r0, 2\n\ + adds r0, r6\n\ + ldrb r0, [r0, 0x1]\n\ + cmp r0, 0x1\n\ + bhi _080C7FB8\n\ + b _080C8142\n\ +_080C7FB8:\n\ + lsls r0, r3, 1\n\ + ldr r1, _080C80A4 @ =gUnknown_83F571C\n\ + adds r0, r1\n\ + ldrh r3, [r0]\n\ + ldr r0, _080C80A8 @ =0x0000ffff\n\ + cmp r3, r0\n\ + beq _080C7FC8\n\ + b _080C8142\n\ +_080C7FC8:\n\ + ldr r0, _080C80B4 @ =gDynamicBasePower\n\ + movs r1, 0\n\ + strh r1, [r0]\n\ + ldr r0, _080C80B8 @ =gBattleStruct\n\ + ldr r0, [r0]\n\ + strb r1, [r0, 0x13]\n\ + ldr r0, _080C80BC @ =gBattleScripting\n\ + movs r2, 0x1\n\ + strb r2, [r0, 0xE]\n\ + ldr r0, _080C80C0 @ =gMoveResultFlags\n\ + strb r1, [r0]\n\ + ldr r0, _080C80C4 @ =gCritMultiplier\n\ + strb r2, [r0]\n\ + movs r6, 0\n\ + mov r9, r3\n\ + ldr r2, _080C80A4 @ =gUnknown_83F571C\n\ + ldrh r2, [r2]\n\ + str r2, [sp, 0x10]\n\ +_080C7FEC:\n\ + movs r3, 0\n\ + ldr r5, _080C80C8 @ =gBattleMons\n\ + lsls r4, r6, 1\n\ + ldr r7, _080C80CC @ =sBattler_AI\n\ + lsls r0, r6, 2\n\ + mov r8, r0\n\ + adds r1, r6, 0x1\n\ + mov r10, r1\n\ + ldr r2, [sp, 0x10]\n\ + cmp r2, r9\n\ + beq _080C8030\n\ + ldr r2, _080C80AC @ =gBattleMoves\n\ + ldrb r1, [r7]\n\ + movs r0, 0x58\n\ + muls r0, r1\n\ + adds r0, r4, r0\n\ + adds r1, r5, 0\n\ + adds r1, 0xC\n\ + adds r0, r1\n\ + ldrh r1, [r0]\n\ + lsls r0, r1, 1\n\ + adds r0, r1\n\ + lsls r0, 2\n\ + adds r0, r2\n\ + ldrb r2, [r0]\n\ + ldr r1, _080C80A4 @ =gUnknown_83F571C\n\ +_080C8020:\n\ + ldrh r0, [r1]\n\ + cmp r2, r0\n\ + beq _080C8030\n\ + adds r1, 0x2\n\ + adds r3, 0x1\n\ + ldrh r0, [r1]\n\ + cmp r0, r9\n\ + bne _080C8020\n\ +_080C8030:\n\ + ldrb r1, [r7]\n\ + movs r0, 0x58\n\ + muls r0, r1\n\ + adds r0, r4, r0\n\ + adds r1, r5, 0\n\ + adds r1, 0xC\n\ + adds r1, r0, r1\n\ + ldrh r0, [r1]\n\ + cmp r0, 0\n\ + beq _080C80DC\n\ + lsls r0, r3, 1\n\ + ldr r2, _080C80A4 @ =gUnknown_83F571C\n\ + adds r0, r2\n\ + ldrh r0, [r0]\n\ + cmp r0, r9\n\ + bne _080C80DC\n\ + ldr r0, _080C80AC @ =gBattleMoves\n\ + ldrh r2, [r1]\n\ + lsls r1, r2, 1\n\ + adds r1, r2\n\ + lsls r1, 2\n\ + adds r1, r0\n\ + ldrb r0, [r1, 0x1]\n\ + cmp r0, 0x1\n\ + bls _080C80DC\n\ + ldr r5, _080C80D0 @ =gCurrentMove\n\ + strh r2, [r5]\n\ + ldrb r0, [r7]\n\ + ldr r4, _080C80D4 @ =gBattlerTarget\n\ + ldrb r1, [r4]\n\ + bl AI_CalcDmg\n\ + ldrh r0, [r5]\n\ + ldrb r1, [r7]\n\ + ldrb r2, [r4]\n\ + bl TypeCalc\n\ + mov r4, sp\n\ + add r4, r8\n\ + ldr r2, _080C80D8 @ =gBattleMoveDamage\n\ + ldr r0, _080C80B0 @ =gBattleResources\n\ + ldr r0, [r0]\n\ + ldr r0, [r0, 0x14]\n\ + adds r0, 0x18\n\ + adds r0, r6\n\ + ldrb r1, [r0]\n\ + ldr r0, [r2]\n\ + muls r0, r1\n\ + movs r1, 0x64\n\ + bl __divsi3\n\ + str r0, [r4]\n\ + cmp r0, 0\n\ + bne _080C80E4\n\ + movs r0, 0x1\n\ + str r0, [r4]\n\ + b _080C80E4\n\ + .align 2, 0\n\ +_080C80A4: .4byte gUnknown_83F571C\n\ +_080C80A8: .4byte 0x0000ffff\n\ +_080C80AC: .4byte gBattleMoves\n\ +_080C80B0: .4byte gBattleResources\n\ +_080C80B4: .4byte gDynamicBasePower\n\ +_080C80B8: .4byte gBattleStruct\n\ +_080C80BC: .4byte gBattleScripting\n\ +_080C80C0: .4byte gMoveResultFlags\n\ +_080C80C4: .4byte gCritMultiplier\n\ +_080C80C8: .4byte gBattleMons\n\ +_080C80CC: .4byte sBattler_AI\n\ +_080C80D0: .4byte gCurrentMove\n\ +_080C80D4: .4byte gBattlerTarget\n\ +_080C80D8: .4byte gBattleMoveDamage\n\ +_080C80DC:\n\ + mov r1, sp\n\ + add r1, r8\n\ + movs r0, 0\n\ + str r0, [r1]\n\ +_080C80E4:\n\ + mov r6, r10\n\ + cmp r6, 0x3\n\ + bgt _080C80EC\n\ + b _080C7FEC\n\ +_080C80EC:\n\ + movs r6, 0\n\ + ldr r2, _080C8130 @ =gBattleResources\n\ + ldr r0, [r2]\n\ + ldr r0, [r0, 0x14]\n\ + ldrb r0, [r0, 0x1]\n\ + lsls r0, 2\n\ + add r0, sp\n\ + ldr r1, [sp]\n\ + ldr r0, [r0]\n\ + ldr r5, _080C8134 @ =gAIScriptPtr\n\ + cmp r1, r0\n\ + bgt _080C8122\n\ + adds r4, r2, 0\n\ + mov r3, sp\n\ +_080C8108:\n\ + adds r3, 0x4\n\ + adds r6, 0x1\n\ + cmp r6, 0x3\n\ + bgt _080C8122\n\ + ldr r0, [r4]\n\ + ldr r0, [r0, 0x14]\n\ + ldrb r0, [r0, 0x1]\n\ + lsls r0, 2\n\ + add r0, sp\n\ + ldr r1, [r3]\n\ + ldr r0, [r0]\n\ + cmp r1, r0\n\ + ble _080C8108\n\ +_080C8122:\n\ + cmp r6, 0x4\n\ + bne _080C8138\n\ + ldr r0, [r2]\n\ + ldr r1, [r0, 0x14]\n\ + movs r0, 0x2\n\ + str r0, [r1, 0x8]\n\ + b _080C814C\n\ + .align 2, 0\n\ +_080C8130: .4byte gBattleResources\n\ +_080C8134: .4byte gAIScriptPtr\n\ +_080C8138:\n\ + ldr r0, [r2]\n\ + ldr r1, [r0, 0x14]\n\ + movs r0, 0x1\n\ + str r0, [r1, 0x8]\n\ + b _080C814C\n\ +_080C8142:\n\ + ldr r0, [r2]\n\ + ldr r1, [r0, 0x14]\n\ + movs r0, 0\n\ + str r0, [r1, 0x8]\n\ + ldr r5, _080C8164 @ =gAIScriptPtr\n\ +_080C814C:\n\ + ldr r0, [r5]\n\ + adds r0, 0x1\n\ + str r0, [r5]\n\ + add sp, 0x14\n\ + pop {r3-r5}\n\ + mov r8, r3\n\ + mov r9, r4\n\ + mov r10, r5\n\ + pop {r4-r7}\n\ + pop {r0}\n\ + bx r0\n\ + .align 2, 0\n\ +_080C8164: .4byte gAIScriptPtr\n\ + .syntax divided\n"); +} +#endif + +static void BattleAICmd_get_move(void) +{ + if (gAIScriptPtr[1] == USER) + AI_THINKING_STRUCT->funcResult = gLastUsedMove[sBattler_AI]; + else + AI_THINKING_STRUCT->funcResult = gLastUsedMove[gBattlerTarget]; + + gAIScriptPtr += 2; +} + +static void BattleAICmd_if_arg_equal(void) +{ + if (gAIScriptPtr[1] == AI_THINKING_STRUCT->funcResult) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 2); + else + gAIScriptPtr += 6; +} + +static void BattleAICmd_if_arg_not_equal(void) +{ + if (gAIScriptPtr[1] != AI_THINKING_STRUCT->funcResult) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 2); + else + gAIScriptPtr += 6; +} + +static void BattleAICmd_if_would_go_first(void) +{ + if (GetWhoStrikesFirst(sBattler_AI, gBattlerTarget, TRUE) == gAIScriptPtr[1]) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 2); + else + gAIScriptPtr += 6; +} + +static void BattleAICmd_if_would_not_go_first(void) +{ + if (GetWhoStrikesFirst(sBattler_AI, gBattlerTarget, TRUE) != gAIScriptPtr[1]) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 2); + else + gAIScriptPtr += 6; +} + +static void BattleAICmd_nullsub_2A(void) +{ +} + +static void BattleAICmd_nullsub_2B(void) +{ +} + +static void BattleAICmd_count_alive_pokemon(void) +{ + struct Pokemon *party; + int i; + u8 index; + u8 var, var2; + + AI_THINKING_STRUCT->funcResult = 0; + + if (gAIScriptPtr[1] == USER) + index = sBattler_AI; + else + index = gBattlerTarget; + + if (GetBankSide(index) == 0) + party = gPlayerParty; + else + party = gEnemyParty; + + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + { + u32 status; + var = gBattlerPartyIndexes[index]; + status = GetBankIdentity(index) ^ 2; + var2 = gBattlerPartyIndexes[GetBankByIdentity(status)]; + } + else + { + var = gBattlerPartyIndexes[index]; + var2 = gBattlerPartyIndexes[index]; + } + + for (i = 0; i < 6; i++) + { + if (i != var && i != var2 + && GetMonData(&party[i], MON_DATA_HP) != 0 + && GetMonData(&party[i], MON_DATA_SPECIES2) != SPECIES_NONE + && GetMonData(&party[i], MON_DATA_SPECIES2) != SPECIES_EGG) + { + AI_THINKING_STRUCT->funcResult++; + } + } + + gAIScriptPtr += 2; +} + +static void BattleAICmd_get_considered_move(void) +{ + AI_THINKING_STRUCT->funcResult = AI_THINKING_STRUCT->moveConsidered; + gAIScriptPtr += 1; +} + +static void BattleAICmd_get_considered_move_effect(void) +{ + AI_THINKING_STRUCT->funcResult = gBattleMoves[AI_THINKING_STRUCT->moveConsidered].effect; + gAIScriptPtr += 1; +} + +static void BattleAICmd_get_ability(void) +{ + u8 index; + + if (gAIScriptPtr[1] == USER) + index = sBattler_AI; + else + index = gBattlerTarget; + + if (GetBankSide(index) == TARGET) + { + u16 side = GetBankIdentity(index) & 1; + + if (BATTLE_HISTORY->abilities[side] != 0) + { + AI_THINKING_STRUCT->funcResult = BATTLE_HISTORY->abilities[side]; + gAIScriptPtr += 2; + return; + } + + // abilities that prevent fleeing. + if (gBattleMons[index].ability == ABILITY_SHADOW_TAG + || gBattleMons[index].ability == ABILITY_MAGNET_PULL + || gBattleMons[index].ability == ABILITY_ARENA_TRAP) + { + AI_THINKING_STRUCT->funcResult = gBattleMons[index].ability; + gAIScriptPtr += 2; + return; + } + + if (gBaseStats[gBattleMons[index].species].ability1 != ABILITY_NONE) + { + if (gBaseStats[gBattleMons[index].species].ability2 != ABILITY_NONE) + { + // AI has no knowledge of opponent, so it guesses which ability. + if (Random() % 2) + { + AI_THINKING_STRUCT->funcResult = gBaseStats[gBattleMons[index].species].ability1; + } + else + { + AI_THINKING_STRUCT->funcResult = gBaseStats[gBattleMons[index].species].ability2; + } + } + else + { + AI_THINKING_STRUCT->funcResult = gBaseStats[gBattleMons[index].species].ability1; // it's definitely ability 1. + } + } + else + { + AI_THINKING_STRUCT->funcResult = gBaseStats[gBattleMons[index].species].ability2; // AI cant actually reach this part since every mon has at least 1 ability. + } + } + else + { + // The AI knows its own ability. + AI_THINKING_STRUCT->funcResult = gBattleMons[index].ability; + } + gAIScriptPtr += 2; +} + +static void BattleAICmd_get_highest_type_effectiveness(void) +{ + s32 i; + u8 *dynamicMoveType; + + gDynamicBasePower = 0; + dynamicMoveType = &gBattleStruct->dynamicMoveType; + *dynamicMoveType = 0; + gBattleScripting.dmgMultiplier = 1; + gMoveResultFlags = 0; + gCritMultiplier = 1; + AI_THINKING_STRUCT->funcResult = 0; + + for (i = 0; i < 4; i++) + { + gBattleMoveDamage = 40; + gCurrentMove = gBattleMons[sBattler_AI].moves[i]; + + if (gCurrentMove != 0) + { + TypeCalc(gCurrentMove, sBattler_AI, gBattlerTarget); + + if (gBattleMoveDamage == 120) // Super effective STAB. + gBattleMoveDamage = AI_EFFECTIVENESS_x2; + if (gBattleMoveDamage == 240) + gBattleMoveDamage = AI_EFFECTIVENESS_x4; + if (gBattleMoveDamage == 30) // Not very effective STAB. + gBattleMoveDamage = AI_EFFECTIVENESS_x0_5; + if (gBattleMoveDamage == 15) + gBattleMoveDamage = AI_EFFECTIVENESS_x0_25; + + if (gMoveResultFlags & MOVE_RESULT_DOESNT_AFFECT_FOE) + gBattleMoveDamage = AI_EFFECTIVENESS_x0; + + if (AI_THINKING_STRUCT->funcResult < gBattleMoveDamage) + AI_THINKING_STRUCT->funcResult = gBattleMoveDamage; + } + } + + gAIScriptPtr += 1; +} + +static void BattleAICmd_if_type_effectiveness(void) +{ + u8 damageVar; + + gDynamicBasePower = 0; + gBattleStruct->dynamicMoveType = 0; + gBattleScripting.dmgMultiplier = 1; + gMoveResultFlags = 0; + gCritMultiplier = 1; + + gBattleMoveDamage = AI_EFFECTIVENESS_x1; + gCurrentMove = AI_THINKING_STRUCT->moveConsidered; + + TypeCalc(gCurrentMove, sBattler_AI, gBattlerTarget); + + if (gBattleMoveDamage == 120) // Super effective STAB. + gBattleMoveDamage = AI_EFFECTIVENESS_x2; + if (gBattleMoveDamage == 240) + gBattleMoveDamage = AI_EFFECTIVENESS_x4; + if (gBattleMoveDamage == 30) // Not very effective STAB. + gBattleMoveDamage = AI_EFFECTIVENESS_x0_5; + if (gBattleMoveDamage == 15) + gBattleMoveDamage = AI_EFFECTIVENESS_x0_25; + + if (gMoveResultFlags & MOVE_RESULT_DOESNT_AFFECT_FOE) + gBattleMoveDamage = AI_EFFECTIVENESS_x0; + + // Store gBattleMoveDamage in a u8 variable because gAIScriptPtr[1] is a u8. + damageVar = gBattleMoveDamage; + + if (damageVar == gAIScriptPtr[1]) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 2); + else + gAIScriptPtr += 6; +} + +static void BattleAICmd_nullsub_32(void) +{ +} + +static void BattleAICmd_nullsub_33(void) +{ +} + +static void BattleAICmd_if_status_in_party(void) +{ + struct Pokemon *party; + struct Pokemon *partyPtr; + int i; + u32 statusToCompareTo; + + // for whatever reason, game freak put the party pointer into 2 variables instead of 1. it's possible at some point the switch encompassed the whole function and used each respective variable creating largely duplicate code. + switch (gAIScriptPtr[1]) + { + case 1: + party = partyPtr = gEnemyParty; + break; + default: + party = partyPtr = gPlayerParty; + break; + } + + statusToCompareTo = T1_READ_32(gAIScriptPtr + 2); + + for (i = 0; i < 6; i++) + { + u16 species = GetMonData(&party[i], MON_DATA_SPECIES); + u16 hp = GetMonData(&party[i], MON_DATA_HP); + u32 status = GetMonData(&party[i], MON_DATA_STATUS); + + if (species != SPECIES_NONE && species != SPECIES_EGG && hp != 0 && status == statusToCompareTo) + { + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 6); // WHAT. why is this being merged into the above switch + return; + } + } + + gAIScriptPtr += 10; +} + +// bugged, doesnt return properly. also unused +static void BattleAICmd_if_status_not_in_party(void) +{ + struct Pokemon *party; + struct Pokemon *partyPtr; + int i; + u32 statusToCompareTo; + + switch (gAIScriptPtr[1]) + { + case 1: + party = partyPtr = gEnemyParty; + break; + default: + party = partyPtr = gPlayerParty; + break; + } + + statusToCompareTo = T1_READ_32(gAIScriptPtr + 2); + + for (i = 0; i < 6; i++) + { + u16 species = GetMonData(&party[i], MON_DATA_SPECIES); + u16 hp = GetMonData(&party[i], MON_DATA_HP); + u32 status = GetMonData(&party[i], MON_DATA_STATUS); + + // everytime the status is found, the AI's logic jumps further and further past its intended destination. this results in a broken AI macro and is probably why it is unused. + if (species != SPECIES_NONE && species != SPECIES_EGG && hp != 0 && status == statusToCompareTo) + gAIScriptPtr += 10; // doesnt return? + } + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 6); +} + +enum +{ + WEATHER_TYPE_SUN, + WEATHER_TYPE_RAIN, + WEATHER_TYPE_SANDSTORM, + WEATHER_TYPE_HAIL, +}; + +extern u16 gBattleWeather; + +static void BattleAICmd_get_weather(void) +{ + if (gBattleWeather & WEATHER_RAIN_ANY) + AI_THINKING_STRUCT->funcResult = WEATHER_TYPE_RAIN; + if (gBattleWeather & WEATHER_SANDSTORM_ANY) + AI_THINKING_STRUCT->funcResult = WEATHER_TYPE_SANDSTORM; + if (gBattleWeather & WEATHER_SUN_ANY) + AI_THINKING_STRUCT->funcResult = WEATHER_TYPE_SUN; + if (gBattleWeather & WEATHER_HAIL) + AI_THINKING_STRUCT->funcResult = WEATHER_TYPE_HAIL; + + gAIScriptPtr += 1; +} + +static void BattleAICmd_if_effect(void) +{ + if (gBattleMoves[AI_THINKING_STRUCT->moveConsidered].effect == gAIScriptPtr[1]) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 2); + else + gAIScriptPtr += 6; +} + +static void BattleAICmd_if_not_effect(void) +{ + if (gBattleMoves[AI_THINKING_STRUCT->moveConsidered].effect != gAIScriptPtr[1]) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 2); + else + gAIScriptPtr += 6; +} + +static void BattleAICmd_if_stat_level_less_than(void) +{ + u32 party; + + if (gAIScriptPtr[1] == USER) + party = sBattler_AI; + else + party = gBattlerTarget; + + if (gBattleMons[party].statStages[gAIScriptPtr[2]] < gAIScriptPtr[3]) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 4); + else + gAIScriptPtr += 8; +} + +static void BattleAICmd_if_stat_level_more_than(void) +{ + u32 party; + + if (gAIScriptPtr[1] == USER) + party = sBattler_AI; + else + party = gBattlerTarget; + + if (gBattleMons[party].statStages[gAIScriptPtr[2]] > gAIScriptPtr[3]) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 4); + else + gAIScriptPtr += 8; +} + +static void BattleAICmd_if_stat_level_equal(void) +{ + u32 party; + + if (gAIScriptPtr[1] == USER) + party = sBattler_AI; + else + party = gBattlerTarget; + + if (gBattleMons[party].statStages[gAIScriptPtr[2]] == gAIScriptPtr[3]) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 4); + else + gAIScriptPtr += 8; +} + +static void BattleAICmd_if_stat_level_not_equal(void) +{ + u32 party; + + if (gAIScriptPtr[1] == USER) + party = sBattler_AI; + else + party = gBattlerTarget; + + if (gBattleMons[party].statStages[gAIScriptPtr[2]] != gAIScriptPtr[3]) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 4); + else + gAIScriptPtr += 8; +} + +static void BattleAICmd_if_can_faint(void) +{ + if (gBattleMoves[AI_THINKING_STRUCT->moveConsidered].power < 2) + { + gAIScriptPtr += 5; + return; + } + + gDynamicBasePower = 0; + gBattleStruct->dynamicMoveType = 0; + gBattleScripting.dmgMultiplier = 1; + gMoveResultFlags = 0; + gCritMultiplier = 1; + gCurrentMove = AI_THINKING_STRUCT->moveConsidered; + AI_CalcDmg(sBattler_AI, gBattlerTarget); + TypeCalc(gCurrentMove, sBattler_AI, gBattlerTarget); + + gBattleMoveDamage = gBattleMoveDamage * AI_THINKING_STRUCT->simulatedRNG[AI_THINKING_STRUCT->movesetIndex] / 100; + + // Moves always do at least 1 damage. + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + + if (gBattleMons[gBattlerTarget].hp <= gBattleMoveDamage) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 1); + else + gAIScriptPtr += 5; +} + +static void BattleAICmd_if_cant_faint(void) +{ + if (gBattleMoves[AI_THINKING_STRUCT->moveConsidered].power < 2) + { + gAIScriptPtr += 5; + return; + } + + gDynamicBasePower = 0; + gBattleStruct->dynamicMoveType = 0; + gBattleScripting.dmgMultiplier = 1; + gMoveResultFlags = 0; + gCritMultiplier = 1; + gCurrentMove = AI_THINKING_STRUCT->moveConsidered; + AI_CalcDmg(sBattler_AI, gBattlerTarget); + TypeCalc(gCurrentMove, sBattler_AI, gBattlerTarget); + + gBattleMoveDamage = gBattleMoveDamage * AI_THINKING_STRUCT->simulatedRNG[AI_THINKING_STRUCT->movesetIndex] / 100; + + // This macro is missing the damage 0 = 1 assumption. + + if (gBattleMons[gBattlerTarget].hp > gBattleMoveDamage) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 1); + else + gAIScriptPtr += 5; +} + +static void BattleAICmd_if_has_move(void) +{ + int i; + u16 *temp_ptr = (u16 *)(gAIScriptPtr + 2); + + switch (gAIScriptPtr[1]) + { + case 1: + case 3: + for (i = 0; i < MAX_MON_MOVES; i++) + { + if (gBattleMons[sBattler_AI].moves[i] == *temp_ptr) + break; + } + if (i == MAX_MON_MOVES) + gAIScriptPtr += 8; + else + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 4); + break; + case 0: + case 2: + for (i = 0; i < 8; i++) + { + if (BATTLE_HISTORY->usedMoves[gBattlerTarget >> 1][i] == *temp_ptr) + break; + } + if (i == 8) + gAIScriptPtr += 8; + else + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 4); + break; + } +} + +static void BattleAICmd_if_dont_have_move(void) +{ + int i; + u16 *temp_ptr = (u16 *)(gAIScriptPtr + 2); + + switch (gAIScriptPtr[1]) + { + case 1: + case 3: + for (i = 0; i < MAX_MON_MOVES; i++) + { + if (gBattleMons[sBattler_AI].moves[i] == *temp_ptr) + break; + } + if (i != MAX_MON_MOVES) + gAIScriptPtr += 8; + else + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 4); + break; + case 0: + case 2: + for (i = 0; i < 8; i++) + { + if (BATTLE_HISTORY->usedMoves[gBattlerTarget >> 1][i] == *temp_ptr) + break; + } + if (i != 8) + gAIScriptPtr += 8; + else + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 4); + break; + } +} + +static void BattleAICmd_if_move_effect(void) +{ + int i; + + switch (gAIScriptPtr[1]) + { + case 1: + case 3: + for (i = 0; i < MAX_MON_MOVES; i++) + { + if (gBattleMons[sBattler_AI].moves[i] != 0 && gBattleMoves[gBattleMons[sBattler_AI].moves[i]].effect == gAIScriptPtr[2]) + break; + } + if (i != MAX_MON_MOVES) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 3); + else + gAIScriptPtr += 7; + break; + case 0: + case 2: + for (i = 0; i < 8; i++) + { + if (gBattleMons[sBattler_AI].moves[i] != 0 && gBattleMoves[BATTLE_HISTORY->usedMoves[gBattlerTarget >> 1][i]].effect == gAIScriptPtr[2]) + break; + } + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 3); + } +} + +static void BattleAICmd_if_not_move_effect(void) +{ + int i; + + switch (gAIScriptPtr[1]) + { + case 1: + case 3: + for (i = 0; i < MAX_MON_MOVES; i++) + { + if (gBattleMons[sBattler_AI].moves[i] != 0 && gBattleMoves[gBattleMons[sBattler_AI].moves[i]].effect == gAIScriptPtr[2]) + break; + } + if (i != MAX_MON_MOVES) + gAIScriptPtr += 7; + else + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 3); + break; + case 0: + case 2: + for (i = 0; i < 8; i++) + { + if (BATTLE_HISTORY->usedMoves[gBattlerTarget >> 1][i] != 0 && gBattleMoves[BATTLE_HISTORY->usedMoves[gBattlerTarget >> 1][i]].effect == gAIScriptPtr[2]) + break; + } + gAIScriptPtr += 7; + } +} + +static void BattleAICmd_if_last_move_did_damage(void) +{ + u8 index; + + if (gAIScriptPtr[1] == USER) + index = sBattler_AI; + else + index = gBattlerTarget; + + if (gAIScriptPtr[2] == 0) + { + if (gDisableStructs[index].disabledMove == 0) + { + gAIScriptPtr += 7; + return; + } + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 3); + return; + } + else if (gAIScriptPtr[2] != 1) // ignore the macro if its not 0 or 1. + { + gAIScriptPtr += 7; + return; + } + else if (gDisableStructs[index].encoredMove != 0) + { + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 3); + return; + } + gAIScriptPtr += 7; +} + +static void BattleAICmd_if_encored(void) +{ + switch (gAIScriptPtr[1]) + { + case 0: // _08109348 + if (gDisableStructs[gActiveBattler].disabledMove == AI_THINKING_STRUCT->moveConsidered) + { + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 2); + return; + } + gAIScriptPtr += 6; + return; + case 1: // _08109370 + if (gDisableStructs[gActiveBattler].encoredMove == AI_THINKING_STRUCT->moveConsidered) + { + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 2); + return; + } + gAIScriptPtr += 6; + return; + default: + gAIScriptPtr += 6; + return; + } +} + +static void BattleAICmd_flee(void) +{ + AI_THINKING_STRUCT->aiAction |= (AI_ACTION_DONE | AI_ACTION_FLEE | AI_ACTION_DO_NOT_ATTACK); // what matters is AI_ACTION_FLEE being enabled. +} + +// FRLG safari command +static void BattleAICmd_frlg_safari(void) +{ + u8 var; + + if(gBattleStruct->safariGoNearCounter) + { + var = gBattleStruct->safariEscapeFactor * 2; + if(var > 20) + var = 20; + } + else if(gBattleStruct->safariPkblThrowCounter != 0) // _080C91DC + { + var = gBattleStruct->safariEscapeFactor / 4; + if(var == 0) + var = 1; + } + else + var = gBattleStruct->safariEscapeFactor; + var *= 5; + if((u8)(Random() % 100) < var) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 1); + else + gAIScriptPtr += 5; +} + +static void BattleAICmd_watch(void) +{ + AI_THINKING_STRUCT->aiAction |= (AI_ACTION_DONE | AI_ACTION_WATCH | AI_ACTION_DO_NOT_ATTACK); // what matters is AI_ACTION_WATCH being enabled. +} + +static void BattleAICmd_get_hold_effect(void) +{ + u8 index; + u16 side; + + if (gAIScriptPtr[1] == USER) + index = sBattler_AI; + else + index = gBattlerTarget; + + if (GetBankSide(index) == 0) + { + side = (GetBankIdentity(index) & 1); + AI_THINKING_STRUCT->funcResult = BATTLE_HISTORY->itemEffects[side]; + } + else + AI_THINKING_STRUCT->funcResult = ItemId_GetHoldEffect(gBattleMons[index].item); + + gAIScriptPtr += 2; +} + +static void BattleAICmd_get_gender(void) +{ + u8 index; + + if (gAIScriptPtr[1] == USER) + index = sBattler_AI; + else + index = gBattlerTarget; + + AI_THINKING_STRUCT->funcResult = GetGenderFromSpeciesAndPersonality(gBattleMons[index].species, gBattleMons[index].personality); + + gAIScriptPtr += 2; +} + +static void BattleAICmd_is_first_turn(void) +{ + u8 index; + + if (gAIScriptPtr[1] == USER) + index = sBattler_AI; + else + index = gBattlerTarget; + + AI_THINKING_STRUCT->funcResult = gDisableStructs[index].isFirstTurn; + + gAIScriptPtr += 2; +} + +static void BattleAICmd_get_stockpile_count(void) +{ + u8 index; + + if (gAIScriptPtr[1] == USER) + index = sBattler_AI; + else + index = gBattlerTarget; + + AI_THINKING_STRUCT->funcResult = gDisableStructs[index].stockpileCounter; + + gAIScriptPtr += 2; +} + +static void BattleAICmd_is_double_battle(void) +{ + AI_THINKING_STRUCT->funcResult = gBattleTypeFlags & BATTLE_TYPE_DOUBLE; + + gAIScriptPtr += 1; +} + +static void BattleAICmd_get_used_held_item(void) +{ + u8 battlerId; + + if (gAIScriptPtr[1] == AI_USER) + battlerId = sBattler_AI; + else + battlerId = gBattlerTarget; + + // This is likely a leftover from Ruby's code and its ugly ewram access. + #ifdef NONMATCHING + AI_THINKING_STRUCT->funcResult = gBattleStruct->usedHeldItems[battlerId]; + #else + AI_THINKING_STRUCT->funcResult = *(u8*)((u8*)(gBattleStruct) + offsetof(struct BattleStruct, usedHeldItems) + (battlerId * 2)); + #endif // NONMATCHING + + gAIScriptPtr += 2; +} + +static void BattleAICmd_get_move_type_from_result(void) +{ + AI_THINKING_STRUCT->funcResult = gBattleMoves[AI_THINKING_STRUCT->funcResult].type; + + gAIScriptPtr += 1; +} + +static void BattleAICmd_get_move_power_from_result(void) +{ + AI_THINKING_STRUCT->funcResult = gBattleMoves[AI_THINKING_STRUCT->funcResult].power; + + gAIScriptPtr += 1; +} + +static void BattleAICmd_get_move_effect_from_result(void) +{ + AI_THINKING_STRUCT->funcResult = gBattleMoves[AI_THINKING_STRUCT->funcResult].effect; + + gAIScriptPtr += 1; +} + +static void BattleAICmd_get_protect_count(void) +{ + u8 index; + + if (gAIScriptPtr[1] == USER) + index = sBattler_AI; + else + index = gBattlerTarget; + + AI_THINKING_STRUCT->funcResult = gDisableStructs[index].protectUses; + + gAIScriptPtr += 2; +} + +static void BattleAICmd_nullsub_52(void) +{ +} + +static void BattleAICmd_nullsub_53(void) +{ +} + +static void BattleAICmd_nullsub_54(void) +{ +} + +static void BattleAICmd_nullsub_55(void) +{ +} + +static void BattleAICmd_nullsub_56(void) +{ +} + +static void BattleAICmd_nullsub_57(void) +{ +} + +static void BattleAICmd_call(void) +{ + AIStackPushVar(gAIScriptPtr + 5); + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 1); +} + +static void BattleAICmd_jump(void) +{ + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 1); +} + +static void BattleAICmd_end(void) +{ + if (AIStackPop() == FALSE) + AI_THINKING_STRUCT->aiAction |= AI_ACTION_DONE; +} + +static void BattleAICmd_if_level_compare(void) +{ + switch (gAIScriptPtr[1]) + { + case 0: // greater than + if (gBattleMons[sBattler_AI].level > gBattleMons[gBattlerTarget].level) + { + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 2); + return; + } + gAIScriptPtr += 6; + return; + case 1: // less than + if (gBattleMons[sBattler_AI].level < gBattleMons[gBattlerTarget].level) + { + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 2); + return; + } + gAIScriptPtr += 6; + return; + case 2: // equal + if (gBattleMons[sBattler_AI].level == gBattleMons[gBattlerTarget].level) + { + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 2); + return; + } + gAIScriptPtr += 6; + return; + } +} + +static void BattleAICmd_if_taunted(void) +{ + if (gDisableStructs[gBattlerTarget].tauntTimer1 != 0) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 1); + else + gAIScriptPtr += 5; +} + +static void BattleAICmd_if_not_taunted(void) +{ + if (gDisableStructs[gBattlerTarget].tauntTimer1 == 0) + gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 1); + else + gAIScriptPtr += 5; +} + +void AIStackPushVar(const u8 *var) +{ + gBattleResources->AI_ScriptsStack->ptr[gBattleResources->AI_ScriptsStack->size++] = var; +} + +// unused +void AIStackPushVar_cursor(void) +{ + gBattleResources->AI_ScriptsStack->ptr[gBattleResources->AI_ScriptsStack->size++] = gAIScriptPtr; +} + +bool8 AIStackPop(void) +{ + if (gBattleResources->AI_ScriptsStack->size != 0) + { + --gBattleResources->AI_ScriptsStack->size; + gAIScriptPtr = gBattleResources->AI_ScriptsStack->ptr[gBattleResources->AI_ScriptsStack->size]; + return TRUE; + } + else + return FALSE; +} |