diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/battle_ai_script_commands.c | 239 | ||||
-rw-r--r-- | src/battle_ai_switch_items.c | 676 | ||||
-rw-r--r-- | src/battle_anim_mon_movement.c | 366 | ||||
-rw-r--r-- | src/battle_controller_link_opponent.c | 1686 | ||||
-rw-r--r-- | src/battle_controller_link_partner.c | 1599 | ||||
-rw-r--r-- | src/battle_controller_opponent.c | 1788 | ||||
-rw-r--r-- | src/battle_controller_player.c | 2969 | ||||
-rw-r--r-- | src/battle_controller_safari.c | 677 | ||||
-rw-r--r-- | src/battle_controllers.c | 1170 | ||||
-rw-r--r-- | src/battle_gfx_sfx_util.c | 1056 | ||||
-rw-r--r-- | src/battle_script_commands.c | 9824 | ||||
-rw-r--r-- | src/battle_util.c | 3200 | ||||
-rw-r--r-- | src/battle_util2.c | 102 | ||||
-rw-r--r-- | src/data/items.json | 2 | ||||
-rw-r--r-- | src/item_pc.c | 2 | ||||
-rw-r--r-- | src/mevent.c | 8 | ||||
-rw-r--r-- | src/mevent_server_helpers.c | 6 | ||||
-rw-r--r-- | src/pokemon.c | 87 | ||||
-rw-r--r-- | src/quest_log.c | 2 | ||||
-rw-r--r-- | src/quest_log_battle.c | 2 | ||||
-rw-r--r-- | src/reshow_battle_screen.c | 322 | ||||
-rw-r--r-- | src/save.c | 8 | ||||
-rw-r--r-- | src/scrcmd.c | 2 | ||||
-rw-r--r-- | src/teachy_tv.c | 2 | ||||
-rw-r--r-- | src/tm_case.c | 2 | ||||
-rw-r--r-- | src/trainer_pokemon_sprites.c | 4 | ||||
-rw-r--r-- | src/trainer_tower.c | 10 | ||||
-rw-r--r-- | src/window.c | 2 |
28 files changed, 25497 insertions, 316 deletions
diff --git a/src/battle_ai_script_commands.c b/src/battle_ai_script_commands.c index 1650f1056..c57825b4f 100644 --- a/src/battle_ai_script_commands.c +++ b/src/battle_ai_script_commands.c @@ -1,16 +1,17 @@ #include "global.h" #include "battle.h" +#include "battle_main.h" +#include "util.h" #include "item.h" +#include "random.h" #include "pokemon.h" +#include "battle_ai_script_commands.h" #include "constants/species.h" #include "constants/abilities.h" #include "constants/battle_ai.h" #include "constants/battle_move_effects.h" #include "constants/moves.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 @@ -20,8 +21,8 @@ extern void sub_80C7164(void); #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)) +#define AI_THINKING_STRUCT (gBattleResources->ai) +#define BATTLE_HISTORY (gBattleResources->battleHistory) // AI states enum @@ -32,9 +33,16 @@ enum AIState_DoNotProcess }; +/* +gAIScriptPtr is a pointer to the next battle AI cmd command to read. +when a command finishes processing, gAIScriptPtr is incremented by +the number of bytes that the current command had reserved for arguments +in order to read the next command correctly. refer to battle_ai_scripts.s for the +AI scripts. +*/ + extern const u8 *gAIScriptPtr; -extern u8 *BattleAIs[]; -extern u16 gLastUsedMove[]; +extern u8 *gBattleAI_ScriptsTable[]; static void BattleAICmd_if_random_less_than(void); static void BattleAICmd_if_random_greater_than(void); @@ -131,6 +139,11 @@ static void BattleAICmd_if_level_compare(void); static void BattleAICmd_if_taunted(void); static void BattleAICmd_if_not_taunted(void); +static void RecordLastUsedMoveByTarget(void); +static void BattleAI_DoAIProcessing(void); +static void AIStackPushVar(const u8 *ptr); +static bool8 AIStackPop(void); + typedef void (*BattleAICmdFunc)(void); static const BattleAICmdFunc sBattleAICmdTable[] = @@ -248,23 +261,6 @@ static const u16 sDiscouragedPowerfulMoveEffects[] = 0xFFFF }; -// 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 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; @@ -275,9 +271,9 @@ void BattleAI_HandleItemUseBeforeAISetup(void) // Items are allowed to use in ONLY trainer battles. // TODO: Use proper flags - if ((gBattleTypeFlags & 0x8) + if ((gBattleTypeFlags & BATTLE_TYPE_TRAINER) && (gTrainerBattleOpponent_A != 0x400) - && !(gBattleTypeFlags & 0x80982) + && !(gBattleTypeFlags & (BATTLE_TYPE_TRAINER_TOWER | BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_BATTLE_TOWER | BATTLE_TYPE_SAFARI | BATTLE_TYPE_LINK)) ) { for (i = 0; i < 4; i++) @@ -318,7 +314,7 @@ void BattleAI_SetupAIData(void) } gBattleResources->AI_ScriptsStack->size = 0; - sBattler_AI = gActiveBattler; + gBattlerAttacker = gActiveBattler; // Decide a random target battlerId in doubles. if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) @@ -331,7 +327,7 @@ void BattleAI_SetupAIData(void) // There's only one choice in single battles. else { - gBattlerTarget = sBattler_AI ^ BIT_SIDE; + gBattlerTarget = gBattlerAttacker ^ BIT_SIDE; } // Choose proper trainer ai scripts. @@ -348,12 +344,12 @@ void BattleAI_SetupAIData(void) } else if (!(gBattleTypeFlags & (0x80900)) && (gTrainerBattleOpponent_A != 0x400)) // _080C6ECC { - if(gBattleTypeFlags & (0x80 << 10)) + if (gBattleTypeFlags & (0x80 << 10)) { AI_THINKING_STRUCT->aiFlags = 1; return; } - else if(gBattleTypeFlags & (0x80 << 11)) + else if (gBattleTypeFlags & (0x80 << 11)) { AI_THINKING_STRUCT->aiFlags = 7; return; @@ -367,14 +363,14 @@ void BattleAI_SetupAIData(void) AI_THINKING_STRUCT->aiFlags = gTrainers[gTrainerBattleOpponent_A].aiFlags; } -u8 BattleAI_GetAIActionToUse(void) +u8 BattleAI_ChooseMoveOrAction(void) { u8 currentMoveArray[MAX_MON_MOVES]; u8 consideredMoveArray[MAX_MON_MOVES]; u8 numOfBestMoves; s32 i; - sub_80C7164(); + RecordLastUsedMoveByTarget(); while (AI_THINKING_STRUCT->aiFlags != 0) { if (AI_THINKING_STRUCT->aiFlags & 1) @@ -415,7 +411,7 @@ u8 BattleAI_GetAIActionToUse(void) return consideredMoveArray[Random() % numOfBestMoves]; // break any ties that exist. } -void BattleAI_DoAIProcessing(void) +static void BattleAI_DoAIProcessing(void) { while (AI_THINKING_STRUCT->aiState != AIState_FinishedProcessing) { @@ -424,14 +420,14 @@ void BattleAI_DoAIProcessing(void) 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) + gAIScriptPtr = gBattleAI_ScriptsTable[AI_THINKING_STRUCT->aiLogicId]; // set the AI ptr. + if (gBattleMons[gBattlerAttacker].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->moveConsidered = gBattleMons[gBattlerAttacker].moves[AI_THINKING_STRUCT->movesetIndex]; } AI_THINKING_STRUCT->aiState++; break; @@ -458,7 +454,7 @@ void BattleAI_DoAIProcessing(void) } } -void sub_80C7164(void) +static void RecordLastUsedMoveByTarget(void) { s32 i; @@ -466,30 +462,31 @@ void sub_80C7164(void) { if (BATTLE_HISTORY->usedMoves[gBattlerTarget >> 1][i] == 0) { - BATTLE_HISTORY->usedMoves[gBattlerTarget >> 1][i] = gLastUsedMove[gBattlerTarget]; + BATTLE_HISTORY->usedMoves[gBattlerTarget >> 1][i] = gLastMoves[gBattlerTarget]; return; } } } -void sub_80C71A8(u8 a) +// not used +static void ClearBattlerMoveHistory(u8 battlerId) { s32 i; for (i = 0; i < 8; i++) - BATTLE_HISTORY->usedMoves[a / 2][i] = 0; + BATTLE_HISTORY->usedMoves[battlerId / 2][i] = MOVE_NONE; } -void sub_80C71D0(u8 a, u8 b) +void RecordAbilityBattle(u8 battlerId, u8 abilityId) { - if (GetBattlerSide(a) == 0) - BATTLE_HISTORY->abilities[GetBattlerPosition(a) & 1] = b; + if (GetBattlerSide(battlerId) == 0) + BATTLE_HISTORY->abilities[GetBattlerPosition(battlerId) & 1] = abilityId; } -void sub_80C7208(u8 a, u8 b) +void RecordItemEffectBattle(u8 battlerId, u8 itemEffect) { - if (GetBattlerSide(a) == 0) - BATTLE_HISTORY->itemEffects[GetBattlerPosition(a) & 1] = b; + if (GetBattlerSide(battlerId) == 0) + BATTLE_HISTORY->itemEffects[GetBattlerPosition(battlerId) & 1] = itemEffect; } static void BattleAICmd_if_random_less_than(void) @@ -544,7 +541,7 @@ static void BattleAICmd_if_hp_less_than(void) u16 index; if (gAIScriptPtr[1] == USER) - index = sBattler_AI; + index = gBattlerAttacker; else index = gBattlerTarget; @@ -559,7 +556,7 @@ static void BattleAICmd_if_hp_more_than(void) u16 index; if (gAIScriptPtr[1] == USER) - index = sBattler_AI; + index = gBattlerAttacker; else index = gBattlerTarget; @@ -574,7 +571,7 @@ static void BattleAICmd_if_hp_equal(void) u16 index; if (gAIScriptPtr[1] == USER) - index = sBattler_AI; + index = gBattlerAttacker; else index = gBattlerTarget; @@ -589,7 +586,7 @@ static void BattleAICmd_if_hp_not_equal(void) u16 index; if (gAIScriptPtr[1] == USER) - index = sBattler_AI; + index = gBattlerAttacker; else index = gBattlerTarget; @@ -605,7 +602,7 @@ static void BattleAICmd_if_status(void) u32 arg; if (gAIScriptPtr[1] == USER) - index = sBattler_AI; + index = gBattlerAttacker; else index = gBattlerTarget; @@ -623,7 +620,7 @@ static void BattleAICmd_if_not_status(void) u32 arg; if (gAIScriptPtr[1] == USER) - index = sBattler_AI; + index = gBattlerAttacker; else index = gBattlerTarget; @@ -641,7 +638,7 @@ static void BattleAICmd_if_status2(void) u32 arg; if (gAIScriptPtr[1] == USER) - index = sBattler_AI; + index = gBattlerAttacker; else index = gBattlerTarget; @@ -659,7 +656,7 @@ static void BattleAICmd_if_not_status2(void) u32 arg; if (gAIScriptPtr[1] == USER) - index = sBattler_AI; + index = gBattlerAttacker; else index = gBattlerTarget; @@ -677,7 +674,7 @@ static void BattleAICmd_if_status3(void) u32 arg; if (gAIScriptPtr[1] == USER) - index = sBattler_AI; + index = gBattlerAttacker; else index = gBattlerTarget; @@ -695,7 +692,7 @@ static void BattleAICmd_if_not_status3(void) u32 arg; if (gAIScriptPtr[1] == USER) - index = sBattler_AI; + index = gBattlerAttacker; else index = gBattlerTarget; @@ -713,14 +710,14 @@ static void BattleAICmd_if_status4(void) u32 arg1, arg2; if (gAIScriptPtr[1] == USER) - index = sBattler_AI; + index = gBattlerAttacker; else index = gBattlerTarget; arg1 = GetBattlerPosition(index) & 1; arg2 = T1_READ_32(gAIScriptPtr + 2); - if ((gSideAffecting[arg1] & arg2) != 0) + if ((gSideStatuses[arg1] & arg2) != 0) gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 6); else gAIScriptPtr += 10; @@ -732,14 +729,14 @@ static void BattleAICmd_if_not_status4(void) u32 arg1, arg2; if (gAIScriptPtr[1] == USER) - index = sBattler_AI; + index = gBattlerAttacker; else index = gBattlerTarget; arg1 = GetBattlerPosition(index) & 1; arg2 = T1_READ_32(gAIScriptPtr + 2); - if ((gSideAffecting[arg1] & arg2) == 0) + if ((gSideStatuses[arg1] & arg2) == 0) gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 6); else gAIScriptPtr += 10; @@ -907,8 +904,8 @@ static void BattleAICmd_if_user_can_damage(void) for (i = 0; i < MAX_MON_MOVES; i++) { - if (gBattleMons[sBattler_AI].moves[i] != 0 - && gBattleMoves[gBattleMons[sBattler_AI].moves[i]].power != 0) + if (gBattleMons[gBattlerAttacker].moves[i] != 0 + && gBattleMoves[gBattleMons[gBattlerAttacker].moves[i]].power != 0) break; } if (i == MAX_MON_MOVES) @@ -923,8 +920,8 @@ static void BattleAICmd_if_user_cant_damage(void) for (i = 0; i < MAX_MON_MOVES; i++) { - if (gBattleMons[sBattler_AI].moves[i] != 0 - && gBattleMoves[gBattleMons[sBattler_AI].moves[i]].power != 0) + if (gBattleMons[gBattlerAttacker].moves[i] != 0 + && gBattleMoves[gBattleMons[gBattlerAttacker].moves[i]].power != 0) break; } if (i != MAX_MON_MOVES) @@ -944,13 +941,13 @@ static void BattleAICmd_get_type(void) switch (gAIScriptPtr[1]) { case 1: // player primary type - AI_THINKING_STRUCT->funcResult = gBattleMons[sBattler_AI].type1; + AI_THINKING_STRUCT->funcResult = gBattleMons[gBattlerAttacker].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; + AI_THINKING_STRUCT->funcResult = gBattleMons[gBattlerAttacker].type2; break; case 2: // enemy secondary type AI_THINKING_STRUCT->funcResult = gBattleMons[gBattlerTarget].type2; @@ -992,17 +989,17 @@ static void BattleAICmd_is_most_powerful_move(void) { for (i = 0; sDiscouragedPowerfulMoveEffects[i] != 0xFFFF; i++) { - if (gBattleMoves[gBattleMons[sBattler_AI].moves[checkedMove]].effect == sDiscouragedPowerfulMoveEffects[i]) + if (gBattleMoves[gBattleMons[gBattlerAttacker].moves[checkedMove]].effect == sDiscouragedPowerfulMoveEffects[i]) break; } - if (gBattleMons[sBattler_AI].moves[checkedMove] != MOVE_NONE + if (gBattleMons[gBattlerAttacker].moves[checkedMove] != MOVE_NONE && sDiscouragedPowerfulMoveEffects[i] == 0xFFFF - && gBattleMoves[gBattleMons[sBattler_AI].moves[checkedMove]].power > 1) + && gBattleMoves[gBattleMons[gBattlerAttacker].moves[checkedMove]].power > 1) { - gCurrentMove = gBattleMons[sBattler_AI].moves[checkedMove]; - AI_CalcDmg(sBattler_AI, gBattlerTarget); - TypeCalc(gCurrentMove, sBattler_AI, gBattlerTarget); + gCurrentMove = gBattleMons[gBattlerAttacker].moves[checkedMove]; + AI_CalcDmg(gBattlerAttacker, gBattlerTarget); + TypeCalc(gCurrentMove, gBattlerAttacker, gBattlerTarget); moveDmgs[checkedMove] = gBattleMoveDamage * AI_THINKING_STRUCT->simulatedRNG[checkedMove] / 100; if (moveDmgs[checkedMove] == 0) moveDmgs[checkedMove] = 1; @@ -1035,9 +1032,9 @@ static void BattleAICmd_is_most_powerful_move(void) static void BattleAICmd_get_move(void) { if (gAIScriptPtr[1] == USER) - AI_THINKING_STRUCT->funcResult = gLastUsedMove[sBattler_AI]; + AI_THINKING_STRUCT->funcResult = gLastMoves[gBattlerAttacker]; else - AI_THINKING_STRUCT->funcResult = gLastUsedMove[gBattlerTarget]; + AI_THINKING_STRUCT->funcResult = gLastMoves[gBattlerTarget]; gAIScriptPtr += 2; } @@ -1060,7 +1057,7 @@ static void BattleAICmd_if_arg_not_equal(void) static void BattleAICmd_if_would_go_first(void) { - if (GetWhoStrikesFirst(sBattler_AI, gBattlerTarget, TRUE) == gAIScriptPtr[1]) + if (GetWhoStrikesFirst(gBattlerAttacker, gBattlerTarget, TRUE) == gAIScriptPtr[1]) gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 2); else gAIScriptPtr += 6; @@ -1068,7 +1065,7 @@ static void BattleAICmd_if_would_go_first(void) static void BattleAICmd_if_would_not_go_first(void) { - if (GetWhoStrikesFirst(sBattler_AI, gBattlerTarget, TRUE) != gAIScriptPtr[1]) + if (GetWhoStrikesFirst(gBattlerAttacker, gBattlerTarget, TRUE) != gAIScriptPtr[1]) gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 2); else gAIScriptPtr += 6; @@ -1092,7 +1089,7 @@ static void BattleAICmd_count_alive_pokemon(void) AI_THINKING_STRUCT->funcResult = 0; if (gAIScriptPtr[1] == USER) - index = sBattler_AI; + index = gBattlerAttacker; else index = gBattlerTarget; @@ -1145,7 +1142,7 @@ static void BattleAICmd_get_ability(void) u8 index; if (gAIScriptPtr[1] == USER) - index = sBattler_AI; + index = gBattlerAttacker; else index = gBattlerTarget; @@ -1218,11 +1215,11 @@ static void BattleAICmd_get_highest_possible_damage(void) for (i = 0; i < 4; i++) { gBattleMoveDamage = 40; - gCurrentMove = gBattleMons[sBattler_AI].moves[i]; + gCurrentMove = gBattleMons[gBattlerAttacker].moves[i]; if (gCurrentMove != 0) { - TypeCalc(gCurrentMove, sBattler_AI, gBattlerTarget); + TypeCalc(gCurrentMove, gBattlerAttacker, gBattlerTarget); if (gBattleMoveDamage == 120) // Super effective STAB. gBattleMoveDamage = AI_EFFECTIVENESS_x2; @@ -1257,7 +1254,7 @@ static void BattleAICmd_if_type_effectiveness(void) gBattleMoveDamage = AI_EFFECTIVENESS_x1; gCurrentMove = AI_THINKING_STRUCT->moveConsidered; - TypeCalc(gCurrentMove, sBattler_AI, gBattlerTarget); + TypeCalc(gCurrentMove, gBattlerAttacker, gBattlerTarget); if (gBattleMoveDamage == 120) // Super effective STAB. gBattleMoveDamage = AI_EFFECTIVENESS_x2; @@ -1373,7 +1370,7 @@ static void BattleAICmd_get_weather(void) AI_THINKING_STRUCT->funcResult = WEATHER_TYPE_RAIN; if (gBattleWeather & WEATHER_SANDSTORM_ANY) AI_THINKING_STRUCT->funcResult = WEATHER_TYPE_SANDSTORM; - if (gBattleWeather & WEATHER_SUNNY_ANY) + if (gBattleWeather & WEATHER_SUN_ANY) AI_THINKING_STRUCT->funcResult = WEATHER_TYPE_SUNNY; if (gBattleWeather & WEATHER_HAIL) AI_THINKING_STRUCT->funcResult = WEATHER_TYPE_HAIL; @@ -1402,7 +1399,7 @@ static void BattleAICmd_if_stat_level_less_than(void) u32 party; if (gAIScriptPtr[1] == USER) - party = sBattler_AI; + party = gBattlerAttacker; else party = gBattlerTarget; @@ -1417,7 +1414,7 @@ static void BattleAICmd_if_stat_level_more_than(void) u32 party; if (gAIScriptPtr[1] == USER) - party = sBattler_AI; + party = gBattlerAttacker; else party = gBattlerTarget; @@ -1432,7 +1429,7 @@ static void BattleAICmd_if_stat_level_equal(void) u32 party; if (gAIScriptPtr[1] == USER) - party = sBattler_AI; + party = gBattlerAttacker; else party = gBattlerTarget; @@ -1447,7 +1444,7 @@ static void BattleAICmd_if_stat_level_not_equal(void) u32 party; if (gAIScriptPtr[1] == USER) - party = sBattler_AI; + party = gBattlerAttacker; else party = gBattlerTarget; @@ -1471,8 +1468,8 @@ static void BattleAICmd_if_can_faint(void) gMoveResultFlags = 0; gCritMultiplier = 1; gCurrentMove = AI_THINKING_STRUCT->moveConsidered; - AI_CalcDmg(sBattler_AI, gBattlerTarget); - TypeCalc(gCurrentMove, sBattler_AI, gBattlerTarget); + AI_CalcDmg(gBattlerAttacker, gBattlerTarget); + TypeCalc(gCurrentMove, gBattlerAttacker, gBattlerTarget); gBattleMoveDamage = gBattleMoveDamage * AI_THINKING_STRUCT->simulatedRNG[AI_THINKING_STRUCT->movesetIndex] / 100; @@ -1500,8 +1497,8 @@ static void BattleAICmd_if_cant_faint(void) gMoveResultFlags = 0; gCritMultiplier = 1; gCurrentMove = AI_THINKING_STRUCT->moveConsidered; - AI_CalcDmg(sBattler_AI, gBattlerTarget); - TypeCalc(gCurrentMove, sBattler_AI, gBattlerTarget); + AI_CalcDmg(gBattlerAttacker, gBattlerTarget); + TypeCalc(gCurrentMove, gBattlerAttacker, gBattlerTarget); gBattleMoveDamage = gBattleMoveDamage * AI_THINKING_STRUCT->simulatedRNG[AI_THINKING_STRUCT->movesetIndex] / 100; @@ -1524,7 +1521,7 @@ static void BattleAICmd_if_has_move(void) case 3: for (i = 0; i < MAX_MON_MOVES; i++) { - if (gBattleMons[sBattler_AI].moves[i] == *temp_ptr) + if (gBattleMons[gBattlerAttacker].moves[i] == *temp_ptr) break; } if (i == MAX_MON_MOVES) @@ -1558,7 +1555,7 @@ static void BattleAICmd_if_dont_have_move(void) case 3: for (i = 0; i < MAX_MON_MOVES; i++) { - if (gBattleMons[sBattler_AI].moves[i] == *temp_ptr) + if (gBattleMons[gBattlerAttacker].moves[i] == *temp_ptr) break; } if (i != MAX_MON_MOVES) @@ -1591,7 +1588,7 @@ static void BattleAICmd_if_move_effect(void) 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]) + if (gBattleMons[gBattlerAttacker].moves[i] != 0 && gBattleMoves[gBattleMons[gBattlerAttacker].moves[i]].effect == gAIScriptPtr[2]) break; } if (i != MAX_MON_MOVES) @@ -1603,7 +1600,7 @@ static void BattleAICmd_if_move_effect(void) 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]) + if (gBattleMons[gBattlerAttacker].moves[i] != 0 && gBattleMoves[BATTLE_HISTORY->usedMoves[gBattlerTarget >> 1][i]].effect == gAIScriptPtr[2]) break; } gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 3); @@ -1620,7 +1617,7 @@ static void BattleAICmd_if_not_move_effect(void) 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]) + if (gBattleMons[gBattlerAttacker].moves[i] != 0 && gBattleMoves[gBattleMons[gBattlerAttacker].moves[i]].effect == gAIScriptPtr[2]) break; } if (i != MAX_MON_MOVES) @@ -1644,13 +1641,13 @@ static void BattleAICmd_if_last_move_did_damage(void) u8 index; if (gAIScriptPtr[1] == USER) - index = sBattler_AI; + index = gBattlerAttacker; else index = gBattlerTarget; if (gAIScriptPtr[2] == 0) { - if (gDisableStructs[index].disabledMove == 0) + if (gDisableStructs[index].disabledMove == MOVE_NONE) { gAIScriptPtr += 7; return; @@ -1663,7 +1660,7 @@ static void BattleAICmd_if_last_move_did_damage(void) gAIScriptPtr += 7; return; } - else if (gDisableStructs[index].encoredMove != 0) + else if (gDisableStructs[index].encoredMove != MOVE_NONE) { gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 3); return; @@ -1707,22 +1704,22 @@ static void BattleAICmd_frlg_safari(void) { u8 var; - if(gBattleStruct->safariGoNearCounter) + if (gBattleStruct->safariGoNearCounter) { var = gBattleStruct->safariEscapeFactor * 2; - if(var > 20) + if (var > 20) var = 20; } - else if(gBattleStruct->safariPkblThrowCounter != 0) // _080C91DC + else if (gBattleStruct->safariPkblThrowCounter != 0) // _080C91DC { var = gBattleStruct->safariEscapeFactor / 4; - if(var == 0) + if (var == 0) var = 1; } else var = gBattleStruct->safariEscapeFactor; var *= 5; - if((u8)(Random() % 100) < var) + if ((u8)(Random() % 100) < var) gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 1); else gAIScriptPtr += 5; @@ -1739,7 +1736,7 @@ static void BattleAICmd_get_hold_effect(void) u16 side; if (gAIScriptPtr[1] == USER) - index = sBattler_AI; + index = gBattlerAttacker; else index = gBattlerTarget; @@ -1759,7 +1756,7 @@ static void BattleAICmd_get_gender(void) u8 index; if (gAIScriptPtr[1] == USER) - index = sBattler_AI; + index = gBattlerAttacker; else index = gBattlerTarget; @@ -1773,7 +1770,7 @@ static void BattleAICmd_is_first_turn(void) u8 index; if (gAIScriptPtr[1] == USER) - index = sBattler_AI; + index = gBattlerAttacker; else index = gBattlerTarget; @@ -1787,7 +1784,7 @@ static void BattleAICmd_get_stockpile_count(void) u8 index; if (gAIScriptPtr[1] == USER) - index = sBattler_AI; + index = gBattlerAttacker; else index = gBattlerTarget; @@ -1808,17 +1805,11 @@ static void BattleAICmd_get_used_held_item(void) u8 battlerId; if (gAIScriptPtr[1] == AI_USER) - battlerId = sBattler_AI; + battlerId = gBattlerAttacker; 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 - + AI_THINKING_STRUCT->funcResult = ((u8 *)gBattleStruct->usedHeldItems)[battlerId * 2]; gAIScriptPtr += 2; } @@ -1848,7 +1839,7 @@ static void BattleAICmd_get_protect_count(void) u8 index; if (gAIScriptPtr[1] == USER) - index = sBattler_AI; + index = gBattlerAttacker; else index = gBattlerTarget; @@ -1903,7 +1894,7 @@ static void BattleAICmd_if_level_compare(void) switch (gAIScriptPtr[1]) { case 0: // greater than - if (gBattleMons[sBattler_AI].level > gBattleMons[gBattlerTarget].level) + if (gBattleMons[gBattlerAttacker].level > gBattleMons[gBattlerTarget].level) { gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 2); return; @@ -1911,7 +1902,7 @@ static void BattleAICmd_if_level_compare(void) gAIScriptPtr += 6; return; case 1: // less than - if (gBattleMons[sBattler_AI].level < gBattleMons[gBattlerTarget].level) + if (gBattleMons[gBattlerAttacker].level < gBattleMons[gBattlerTarget].level) { gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 2); return; @@ -1919,7 +1910,7 @@ static void BattleAICmd_if_level_compare(void) gAIScriptPtr += 6; return; case 2: // equal - if (gBattleMons[sBattler_AI].level == gBattleMons[gBattlerTarget].level) + if (gBattleMons[gBattlerAttacker].level == gBattleMons[gBattlerTarget].level) { gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 2); return; @@ -1931,7 +1922,7 @@ static void BattleAICmd_if_level_compare(void) static void BattleAICmd_if_taunted(void) { - if (gDisableStructs[gBattlerTarget].tauntTimer1 != 0) + if (gDisableStructs[gBattlerTarget].tauntTimer != 0) gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 1); else gAIScriptPtr += 5; @@ -1939,24 +1930,24 @@ static void BattleAICmd_if_taunted(void) static void BattleAICmd_if_not_taunted(void) { - if (gDisableStructs[gBattlerTarget].tauntTimer1 == 0) + if (gDisableStructs[gBattlerTarget].tauntTimer == 0) gAIScriptPtr = T1_READ_PTR(gAIScriptPtr + 1); else gAIScriptPtr += 5; } -void AIStackPushVar(const u8 *var) +static void AIStackPushVar(const u8 *var) { gBattleResources->AI_ScriptsStack->ptr[gBattleResources->AI_ScriptsStack->size++] = var; } // unused -void AIStackPushVar_cursor(void) +static void AIStackPushVar_cursor(void) { gBattleResources->AI_ScriptsStack->ptr[gBattleResources->AI_ScriptsStack->size++] = gAIScriptPtr; } -bool8 AIStackPop(void) +static bool8 AIStackPop(void) { if (gBattleResources->AI_ScriptsStack->size != 0) { diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c new file mode 100644 index 000000000..5d642bda9 --- /dev/null +++ b/src/battle_ai_switch_items.c @@ -0,0 +1,676 @@ +#include "global.h" +#include "battle.h" +#include "battle_anim.h" +#include "battle_controllers.h" +#include "pokemon.h" +#include "random.h" +#include "util.h" +#include "constants/abilities.h" +#include "constants/item_effects.h" +#include "constants/items.h" +#include "constants/moves.h" +#include "constants/species.h" +#include "constants/pokemon.h" + +static bool8 HasSuperEffectiveMoveAgainstOpponents(bool8 noRng); +static bool8 FindMonWithFlagsAndSuperEffective(u8 flags, u8 moduloPercent); +static bool8 ShouldUseItem(void); + +static bool8 ShouldSwitchIfPerishSong(void) +{ + if (gStatuses3[gActiveBattler] & STATUS3_PERISH_SONG + && gDisableStructs[gActiveBattler].perishSongTimer == 0) + { + *(gBattleStruct->AI_monToSwitchIntoId + (GetBattlerPosition(gActiveBattler) >> 1)) = PARTY_SIZE; + BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0); + return TRUE; + } + else + { + return FALSE; + } +} + +static bool8 ShouldSwitchIfWonderGuard(void) +{ + u8 opposingBattler; + u8 moveFlags; + s32 i, j; + u16 move; + + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + return FALSE; + if (gBattleMons[GetBattlerAtPosition(B_POSITION_PLAYER_LEFT)].ability == ABILITY_WONDER_GUARD) + { + // Check if Pokemon has a super effective move. + for (opposingBattler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT), i = 0; i < MAX_MON_MOVES; ++i) + { + move = gBattleMons[gActiveBattler].moves[i]; + if (move == MOVE_NONE) + continue; + moveFlags = AI_TypeCalc(move, gBattleMons[opposingBattler].species, gBattleMons[opposingBattler].ability); + if (moveFlags & MOVE_RESULT_SUPER_EFFECTIVE) + return FALSE; + } + // Find a Pokemon in the party that has a super effective move. + for (i = 0; i < PARTY_SIZE; ++i) + { + if (GetMonData(&gEnemyParty[i], MON_DATA_HP) == 0 + || GetMonData(&gEnemyParty[i], MON_DATA_SPECIES2) == SPECIES_NONE + || GetMonData(&gEnemyParty[i], MON_DATA_SPECIES2) == SPECIES_EGG + || i == gBattlerPartyIndexes[gActiveBattler]) + continue; + GetMonData(&gEnemyParty[i], MON_DATA_SPECIES); // Unused return value. + GetMonData(&gEnemyParty[i], MON_DATA_ABILITY_NUM); // Unused return value. + for (opposingBattler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT), j = 0; j < MAX_MON_MOVES; ++j) + { + move = GetMonData(&gEnemyParty[i], MON_DATA_MOVE1 + j); + if (move == MOVE_NONE) + continue; + moveFlags = AI_TypeCalc(move, gBattleMons[opposingBattler].species, gBattleMons[opposingBattler].ability); + if (moveFlags & MOVE_RESULT_SUPER_EFFECTIVE && Random() % 3 < 2) + { + // We found a mon. + *(gBattleStruct->AI_monToSwitchIntoId + (GetBattlerPosition(gActiveBattler) >> 1)) = i; + BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0); + return TRUE; + } + } + } + } + return FALSE; // There is not a single Pokemon in the party that has a super effective move against a mon with Wonder Guard. +} + +static bool8 FindMonThatAbsorbsOpponentsMove(void) +{ + u8 battlerIn1, battlerIn2; + u8 absorbingTypeAbility; + s32 i; + + if ((HasSuperEffectiveMoveAgainstOpponents(TRUE) && Random() % 3) + || (gLastLandedMoves[gActiveBattler] == MOVE_NONE)) + return FALSE; + if (gLastLandedMoves[gActiveBattler] == 0xFFFF + || gBattleMoves[gLastLandedMoves[gActiveBattler]].power == 0) + return FALSE; + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + { + battlerIn1 = gActiveBattler; + if (gAbsentBattlerFlags & gBitTable[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gActiveBattler)))]) + battlerIn2 = gActiveBattler; + else + battlerIn2 = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gActiveBattler))); + } + else + { + battlerIn1 = gActiveBattler; + battlerIn2 = gActiveBattler; + } + if (gBattleMoves[gLastLandedMoves[gActiveBattler]].type == TYPE_FIRE) + absorbingTypeAbility = ABILITY_FLASH_FIRE; + else if (gBattleMoves[gLastLandedMoves[gActiveBattler]].type == TYPE_WATER) + absorbingTypeAbility = ABILITY_WATER_ABSORB; + else if (gBattleMoves[gLastLandedMoves[gActiveBattler]].type == TYPE_ELECTRIC) + absorbingTypeAbility = ABILITY_VOLT_ABSORB; + else + return FALSE; + if (gBattleMons[gActiveBattler].ability == absorbingTypeAbility) + return FALSE; + for (i = 0; i < PARTY_SIZE; ++i) + { + u16 species; + u8 monAbility; + + if ((GetMonData(&gEnemyParty[i], MON_DATA_HP) == 0) + || (GetMonData(&gEnemyParty[i], MON_DATA_SPECIES2) == SPECIES_NONE) + || (GetMonData(&gEnemyParty[i], MON_DATA_SPECIES2) == SPECIES_EGG) + || (i == gBattlerPartyIndexes[battlerIn1]) + || (i == gBattlerPartyIndexes[battlerIn2]) + || (i == *(gBattleStruct->monToSwitchIntoId + battlerIn1)) + || (i == *(gBattleStruct->monToSwitchIntoId + battlerIn2))) + continue; + species = GetMonData(&gEnemyParty[i], MON_DATA_SPECIES); + if (GetMonData(&gEnemyParty[i], MON_DATA_ABILITY_NUM) != ABILITY_NONE) + monAbility = gBaseStats[species].abilities[1]; + else + monAbility = gBaseStats[species].abilities[0]; + if (absorbingTypeAbility == monAbility && Random() & 1) + { + // we found a mon + *(gBattleStruct->AI_monToSwitchIntoId + (GetBattlerPosition(gActiveBattler) >> 1)) = i; + BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0); + return TRUE; + } + } + return FALSE; +} + +static bool8 ShouldSwitchIfNaturalCure(void) +{ + if (!(gBattleMons[gActiveBattler].status1 & STATUS1_SLEEP) + || (gBattleMons[gActiveBattler].ability != ABILITY_NATURAL_CURE) + || (gBattleMons[gActiveBattler].hp < gBattleMons[gActiveBattler].maxHP / 2)) + return FALSE; + if ((gLastLandedMoves[gActiveBattler] == MOVE_NONE || gLastLandedMoves[gActiveBattler] == 0xFFFF) && Random() & 1) + { + *(gBattleStruct->AI_monToSwitchIntoId + (GetBattlerPosition(gActiveBattler) >> 1)) = PARTY_SIZE; + BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0); + return TRUE; + } + else if (gBattleMoves[gLastLandedMoves[gActiveBattler]].power == 0 && Random() & 1) + { + *(gBattleStruct->AI_monToSwitchIntoId + (GetBattlerPosition(gActiveBattler) >> 1)) = PARTY_SIZE; + BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0); + return TRUE; + } + if (FindMonWithFlagsAndSuperEffective(MOVE_RESULT_DOESNT_AFFECT_FOE, 1) + || FindMonWithFlagsAndSuperEffective(MOVE_RESULT_NOT_VERY_EFFECTIVE, 1)) + return TRUE; + if (Random() & 1) + { + *(gBattleStruct->AI_monToSwitchIntoId + (GetBattlerPosition(gActiveBattler) >> 1)) = PARTY_SIZE; + BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0); + return TRUE; + } + return FALSE; +} + +static bool8 HasSuperEffectiveMoveAgainstOpponents(bool8 noRng) +{ + u8 opposingBattler; + s32 i; + u8 moveFlags; + u16 move; + + opposingBattler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); + if (!(gAbsentBattlerFlags & gBitTable[opposingBattler])) + { + for (i = 0; i < MAX_MON_MOVES; ++i) + { + move = gBattleMons[gActiveBattler].moves[i]; + if (move == MOVE_NONE) + continue; + + moveFlags = AI_TypeCalc(move, gBattleMons[opposingBattler].species, gBattleMons[opposingBattler].ability); + if (moveFlags & MOVE_RESULT_SUPER_EFFECTIVE) + { + if (noRng || (Random() % 10)) + return TRUE; + } + } + } + if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE)) + return FALSE; + opposingBattler = GetBattlerAtPosition(BATTLE_PARTNER(B_POSITION_PLAYER_LEFT)); + if (!(gAbsentBattlerFlags & gBitTable[opposingBattler])) + { + for (i = 0; i < MAX_MON_MOVES; ++i) + { + move = gBattleMons[gActiveBattler].moves[i]; + if (move == MOVE_NONE) + continue; + moveFlags = AI_TypeCalc(move, gBattleMons[opposingBattler].species, gBattleMons[opposingBattler].ability); + if (moveFlags & MOVE_RESULT_SUPER_EFFECTIVE) + { + if (noRng) + return TRUE; + if (Random() % 10 != 0) + return TRUE; + } + } + } + return FALSE; +} + +static bool8 AreStatsRaised(void) +{ + u8 buffedStatsValue = 0; + s32 i; + + for (i = 0; i < NUM_BATTLE_STATS; ++i) + { + if (gBattleMons[gActiveBattler].statStages[i] > 6) + buffedStatsValue += gBattleMons[gActiveBattler].statStages[i] - 6; + } + return (buffedStatsValue > 3); +} + +static bool8 FindMonWithFlagsAndSuperEffective(u8 flags, u8 moduloPercent) +{ + u8 battlerIn1, battlerIn2; + s32 i, j; + u16 move; + u8 moveFlags; + + if (gLastLandedMoves[gActiveBattler] == 0) + return FALSE; + if ((gLastLandedMoves[gActiveBattler] == 0xFFFF) + || (gLastHitBy[gActiveBattler] == 0xFF) + || (gBattleMoves[gLastLandedMoves[gActiveBattler]].power == 0)) + return FALSE; + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + { + battlerIn1 = gActiveBattler; + if (gAbsentBattlerFlags & gBitTable[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gActiveBattler)))]) + battlerIn2 = gActiveBattler; + else + battlerIn2 = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gActiveBattler))); + } + else + { + battlerIn1 = gActiveBattler; + battlerIn2 = gActiveBattler; + } + for (i = 0; i < PARTY_SIZE; ++i) + { + u16 species; + u8 monAbility; + + if ((GetMonData(&gEnemyParty[i], MON_DATA_HP) == 0) + || (GetMonData(&gEnemyParty[i], MON_DATA_SPECIES2) == SPECIES_NONE) + || (GetMonData(&gEnemyParty[i], MON_DATA_SPECIES2) == SPECIES_EGG) + || (i == gBattlerPartyIndexes[battlerIn1]) + || (i == gBattlerPartyIndexes[battlerIn2]) + || (i == *(gBattleStruct->monToSwitchIntoId + battlerIn1)) + || (i == *(gBattleStruct->monToSwitchIntoId + battlerIn2))) + continue; + species = GetMonData(&gEnemyParty[i], MON_DATA_SPECIES); + if (GetMonData(&gEnemyParty[i], MON_DATA_ABILITY_NUM) != ABILITY_NONE) + monAbility = gBaseStats[species].abilities[1]; + else + monAbility = gBaseStats[species].abilities[0]; + moveFlags = AI_TypeCalc(gLastLandedMoves[gActiveBattler], species, monAbility); + if (moveFlags & flags) + { + battlerIn1 = gLastHitBy[gActiveBattler]; + for (j = 0; j < MAX_MON_MOVES; ++j) + { + move = GetMonData(&gEnemyParty[i], MON_DATA_MOVE1 + j); + if (move == MOVE_NONE) + continue; + moveFlags = AI_TypeCalc(move, gBattleMons[battlerIn1].species, gBattleMons[battlerIn1].ability); + if (moveFlags & MOVE_RESULT_SUPER_EFFECTIVE && Random() % moduloPercent == 0) + { + *(gBattleStruct->AI_monToSwitchIntoId + (GetBattlerPosition(gActiveBattler) >> 1)) = i; + BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0); + return TRUE; + } + } + } + } + return FALSE; +} + +static bool8 ShouldSwitch(void) +{ + u8 battlerIn1, battlerIn2; + s32 i; + s32 availableToSwitch; + + if ((gBattleMons[gActiveBattler].status2 & (STATUS2_WRAPPED | STATUS2_ESCAPE_PREVENTION)) + || (gStatuses3[gActiveBattler] & STATUS3_ROOTED) + || AbilityBattleEffects(ABILITYEFFECT_CHECK_OTHER_SIDE, gActiveBattler, ABILITY_SHADOW_TAG, 0, 0) + || AbilityBattleEffects(ABILITYEFFECT_CHECK_OTHER_SIDE, gActiveBattler, ABILITY_ARENA_TRAP, 0, 0)) + return FALSE; // misses the flying or levitate check + if (AbilityBattleEffects(ABILITYEFFECT_FIELD_SPORT, 0, ABILITY_MAGNET_PULL, 0, 0)) + if ((gBattleMons[gActiveBattler].type1 == TYPE_STEEL) || (gBattleMons[gActiveBattler].type2 == TYPE_STEEL)) + return FALSE; + availableToSwitch = 0; + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + { + battlerIn1 = gActiveBattler; + if (gAbsentBattlerFlags & gBitTable[GetBattlerAtPosition(GetBattlerPosition(gActiveBattler) ^ BIT_FLANK)]) + battlerIn2 = gActiveBattler; + else + battlerIn2 = GetBattlerAtPosition(GetBattlerPosition(gActiveBattler) ^ BIT_FLANK); + } + else + { + battlerIn2 = gActiveBattler; + battlerIn1 = gActiveBattler; + } + for (i = 0; i < PARTY_SIZE; ++i) + { + if ((GetMonData(&gEnemyParty[i], MON_DATA_HP) == 0) + || (GetMonData(&gEnemyParty[i], MON_DATA_SPECIES2) == SPECIES_NONE) + || (GetMonData(&gEnemyParty[i], MON_DATA_SPECIES2) == SPECIES_EGG) + || (i == gBattlerPartyIndexes[battlerIn1]) + || (i == gBattlerPartyIndexes[battlerIn2]) + || (i == *(gBattleStruct->monToSwitchIntoId + battlerIn1)) + || (i == *(gBattleStruct->monToSwitchIntoId + battlerIn2))) + continue; + ++availableToSwitch; + } + if (!availableToSwitch) + return FALSE; + if (ShouldSwitchIfPerishSong() + || ShouldSwitchIfWonderGuard() + || FindMonThatAbsorbsOpponentsMove() + || ShouldSwitchIfNaturalCure()) + return TRUE; + if (HasSuperEffectiveMoveAgainstOpponents(FALSE) + || AreStatsRaised()) + return FALSE; + if (FindMonWithFlagsAndSuperEffective(MOVE_RESULT_DOESNT_AFFECT_FOE, 2) + || FindMonWithFlagsAndSuperEffective(MOVE_RESULT_NOT_VERY_EFFECTIVE, 3)) + return TRUE; + return FALSE; +} + +void AI_TrySwitchOrUseItem(void) +{ + u8 battlerIn1, battlerIn2; + + if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) + { + if (ShouldSwitch()) + { + if (*(gBattleStruct->AI_monToSwitchIntoId + (GetBattlerPosition(gActiveBattler) >> 1)) == 6) + { + s32 monToSwitchId = GetMostSuitableMonToSwitchInto(); + if (monToSwitchId == 6) + { + if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE)) + { + battlerIn1 = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); + battlerIn2 = battlerIn1; + } + else + { + battlerIn1 = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); + battlerIn2 = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); + } + for (monToSwitchId = 0; monToSwitchId < PARTY_SIZE; ++monToSwitchId) + { + if ((!GetMonData(&gEnemyParty[monToSwitchId], MON_DATA_HP) == 0) + && (monToSwitchId != gBattlerPartyIndexes[battlerIn1]) + && (monToSwitchId != gBattlerPartyIndexes[battlerIn2]) + && (monToSwitchId != *(gBattleStruct->monToSwitchIntoId + battlerIn1)) + && (monToSwitchId != *(gBattleStruct->monToSwitchIntoId + battlerIn2))) + break; + } + } + *(gBattleStruct->AI_monToSwitchIntoId + (GetBattlerPosition(gActiveBattler) >> 1)) = monToSwitchId; + } + *(gBattleStruct->monToSwitchIntoId + gActiveBattler) = *(gBattleStruct->AI_monToSwitchIntoId + (GetBattlerPosition(gActiveBattler) >> 1)); + return; + } + else if (ShouldUseItem()) + { + return; + } + } + BtlController_EmitTwoReturnValues(1, B_ACTION_USE_MOVE, (gActiveBattler ^ BIT_SIDE) << 8); +} + +static void ModulateByTypeEffectiveness(u8 atkType, u8 defType1, u8 defType2, u8 *var) +{ + s32 i = 0; + + while (TYPE_EFFECT_ATK_TYPE(i) != TYPE_ENDTABLE) + { + if (TYPE_EFFECT_ATK_TYPE(i) == TYPE_FORESIGHT) + { + i += 3; + continue; + } + else if (TYPE_EFFECT_ATK_TYPE(i) == atkType) + { + // Check type1. + if (TYPE_EFFECT_DEF_TYPE(i) == defType1) + *var = (*var * TYPE_EFFECT_MULTIPLIER(i)) / 10; + // Check type2. + if (TYPE_EFFECT_DEF_TYPE(i) == defType2 && defType1 != defType2) + *var = (*var * TYPE_EFFECT_MULTIPLIER(i)) / 10; + } + i += 3; + } +} + +u8 GetMostSuitableMonToSwitchInto(void) +{ + u8 opposingBattler; + u8 bestDmg; // Note : should be changed to u32 for obvious reasons. + u8 bestMonId; + u8 battlerIn1, battlerIn2; + s32 i, j; + u8 invalidMons; + u16 move; + + if (*(gBattleStruct->monToSwitchIntoId + gActiveBattler) != PARTY_SIZE) + return *(gBattleStruct->monToSwitchIntoId + gActiveBattler); + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + { + battlerIn1 = gActiveBattler; + if (gAbsentBattlerFlags & gBitTable[GetBattlerAtPosition(GetBattlerPosition(gActiveBattler) ^ BIT_FLANK)]) + battlerIn2 = gActiveBattler; + else + battlerIn2 = GetBattlerAtPosition(GetBattlerPosition(gActiveBattler) ^ BIT_FLANK); + // UB: It considers the opponent only player's side even though it can battle alongside player. + opposingBattler = Random() & BIT_FLANK; + if (gAbsentBattlerFlags & gBitTable[opposingBattler]) + opposingBattler ^= BIT_FLANK; + } + else + { + opposingBattler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); + battlerIn1 = gActiveBattler; + battlerIn2 = gActiveBattler; + } + invalidMons = 0; + while (invalidMons != 0x3F) // All mons are invalid. + { + bestDmg = 0; + bestMonId = 6; + // Find the mon whose type is the most suitable offensively. + for (i = 0; i < PARTY_SIZE; ++i) + { + u16 species = GetMonData(&gEnemyParty[i], MON_DATA_SPECIES); + if (species != SPECIES_NONE + && GetMonData(&gEnemyParty[i], MON_DATA_HP) != 0 + && !(gBitTable[i] & invalidMons) + && gBattlerPartyIndexes[battlerIn1] != i + && gBattlerPartyIndexes[battlerIn2] != i + && i != *(gBattleStruct->monToSwitchIntoId + battlerIn1) + && i != *(gBattleStruct->monToSwitchIntoId + battlerIn2)) + { + u8 type1 = gBaseStats[species].type1; + u8 type2 = gBaseStats[species].type2; + u8 typeDmg = 10; + ModulateByTypeEffectiveness(gBattleMons[opposingBattler].type1, type1, type2, &typeDmg); + ModulateByTypeEffectiveness(gBattleMons[opposingBattler].type2, type1, type2, &typeDmg); + if (bestDmg < typeDmg) + { + bestDmg = typeDmg; + bestMonId = i; + } + } + else + { + invalidMons |= gBitTable[i]; + } + } + // Ok, we know the mon has the right typing but does it have at least one super effective move? + if (bestMonId != PARTY_SIZE) + { + for (i = 0; i < MAX_MON_MOVES; ++i) + { + move = GetMonData(&gEnemyParty[bestMonId], MON_DATA_MOVE1 + i); + if (move != MOVE_NONE && TypeCalc(move, gActiveBattler, opposingBattler) & MOVE_RESULT_SUPER_EFFECTIVE) + break; + } + if (i != MAX_MON_MOVES) + return bestMonId; // Has both the typing and at least one super effective move. + + invalidMons |= gBitTable[bestMonId]; // Sorry buddy, we want something better. + } + else + { + invalidMons = 0x3F; // No viable mon to switch. + } + } + gDynamicBasePower = 0; + gBattleStruct->dynamicMoveType = 0; + gBattleScripting.dmgMultiplier = 1; + gMoveResultFlags = 0; + gCritMultiplier = 1; + bestDmg = 0; + bestMonId = 6; + // If we couldn't find the best mon in terms of typing, find the one that deals most damage. + for (i = 0; i < PARTY_SIZE; ++i) + { + if (((u16)(GetMonData(&gEnemyParty[i], MON_DATA_SPECIES)) == SPECIES_NONE) + || (GetMonData(&gEnemyParty[i], MON_DATA_HP) == 0) + || (gBattlerPartyIndexes[battlerIn1] == i) + || (gBattlerPartyIndexes[battlerIn2] == i) + || (i == *(gBattleStruct->monToSwitchIntoId + battlerIn1)) + || (i == *(gBattleStruct->monToSwitchIntoId + battlerIn2))) + continue; + for (j = 0; j < MAX_MON_MOVES; ++j) + { + move = GetMonData(&gEnemyParty[i], MON_DATA_MOVE1 + j); + gBattleMoveDamage = 0; + if (move != MOVE_NONE && gBattleMoves[move].power != 1) + { + AI_CalcDmg(gActiveBattler, opposingBattler); + TypeCalc(move, gActiveBattler, opposingBattler); + } + if (bestDmg < gBattleMoveDamage) + { + bestDmg = gBattleMoveDamage; + bestMonId = i; + } + } + } + return bestMonId; +} + +static u8 GetAI_ItemType(u8 itemId, const u8 *itemEffect) // NOTE: should take u16 as item Id argument +{ + if (itemId == ITEM_FULL_RESTORE) + return AI_ITEM_FULL_RESTORE; + else if (itemEffect[4] & ITEM4_HEAL_HP) + return AI_ITEM_HEAL_HP; + else if (itemEffect[3] & ITEM3_STATUS_ALL) + return AI_ITEM_CURE_CONDITION; + else if (itemEffect[0] & (ITEM0_HIGH_CRIT | ITEM0_X_ATTACK) || itemEffect[1] != 0 || itemEffect[2] != 0) + return AI_ITEM_X_STAT; + else if (itemEffect[3] & ITEM3_MIST) + return AI_ITEM_GUARD_SPECS; + else + return AI_ITEM_NOT_RECOGNIZABLE; +} + +static bool8 ShouldUseItem(void) +{ + s32 i; + u8 validMons = 0; + bool8 shouldUse = FALSE; + + for (i = 0; i < PARTY_SIZE; ++i) + if (GetMonData(&gEnemyParty[i], MON_DATA_HP) != 0 + && GetMonData(&gEnemyParty[i], MON_DATA_SPECIES2) != SPECIES_NONE + && GetMonData(&gEnemyParty[i], MON_DATA_SPECIES2) != SPECIES_EGG) + ++validMons; + for (i = 0; i < MAX_MON_MOVES; ++i) + { + u16 item; + const u8 *itemEffects; + u8 paramOffset; + u8 battlerSide; + + if (i && validMons > (gBattleResources->battleHistory->itemsNo - i) + 1) + continue; + item = gBattleResources->battleHistory->trainerItems[i]; + if (item == ITEM_NONE || gItemEffectTable[item - ITEM_POTION] == NULL) + continue; + if (item == ITEM_ENIGMA_BERRY) + itemEffects = gSaveBlock1Ptr->enigmaBerry.itemEffect; + else + itemEffects = gItemEffectTable[item - ITEM_POTION]; + *(gBattleStruct->AI_itemType + gActiveBattler / 2) = GetAI_ItemType(item, itemEffects); + switch (*(gBattleStruct->AI_itemType + gActiveBattler / 2)) + { + case AI_ITEM_FULL_RESTORE: + if (gBattleMons[gActiveBattler].hp >= gBattleMons[gActiveBattler].maxHP / 4) + break; + if (gBattleMons[gActiveBattler].hp == 0) + break; + shouldUse = TRUE; + break; + case AI_ITEM_HEAL_HP: + paramOffset = GetItemEffectParamOffset(item, 4, 4); + if (paramOffset == 0 || gBattleMons[gActiveBattler].hp == 0) + break; + if (gBattleMons[gActiveBattler].hp < gBattleMons[gActiveBattler].maxHP / 4 || gBattleMons[gActiveBattler].maxHP - gBattleMons[gActiveBattler].hp > itemEffects[paramOffset]) + shouldUse = TRUE; + break; + case AI_ITEM_CURE_CONDITION: + *(gBattleStruct->AI_itemFlags + gActiveBattler / 2) = 0; + if (itemEffects[3] & ITEM3_SLEEP && gBattleMons[gActiveBattler].status1 & STATUS1_SLEEP) + { + *(gBattleStruct->AI_itemFlags + gActiveBattler / 2) |= 0x20; + shouldUse = TRUE; + } + if (itemEffects[3] & ITEM3_POISON && (gBattleMons[gActiveBattler].status1 & STATUS1_POISON || gBattleMons[gActiveBattler].status1 & STATUS1_TOXIC_POISON)) + { + *(gBattleStruct->AI_itemFlags + gActiveBattler / 2) |= 0x10; + shouldUse = TRUE; + } + if (itemEffects[3] & ITEM3_BURN && gBattleMons[gActiveBattler].status1 & STATUS1_BURN) + { + *(gBattleStruct->AI_itemFlags + gActiveBattler / 2) |= 0x8; + shouldUse = TRUE; + } + if (itemEffects[3] & ITEM3_FREEZE && gBattleMons[gActiveBattler].status1 & STATUS1_FREEZE) + { + *(gBattleStruct->AI_itemFlags + gActiveBattler / 2) |= 0x4; + shouldUse = TRUE; + } + if (itemEffects[3] & ITEM3_PARALYSIS && gBattleMons[gActiveBattler].status1 & STATUS1_PARALYSIS) + { + *(gBattleStruct->AI_itemFlags + gActiveBattler / 2) |= 0x2; + shouldUse = TRUE; + } + if (itemEffects[3] & ITEM3_CONFUSION && gBattleMons[gActiveBattler].status2 & STATUS2_CONFUSION) + { + *(gBattleStruct->AI_itemFlags + gActiveBattler / 2) |= 0x1; + shouldUse = TRUE; + } + break; + case AI_ITEM_X_STAT: + *(gBattleStruct->AI_itemFlags + gActiveBattler / 2) = 0; + if (!gDisableStructs[gActiveBattler].isFirstTurn) + break; + if (itemEffects[0] & ITEM0_X_ATTACK) + *(gBattleStruct->AI_itemFlags + gActiveBattler / 2) |= 0x1; + if (itemEffects[1] & ITEM1_X_DEFEND) + *(gBattleStruct->AI_itemFlags + gActiveBattler / 2) |= 0x2; + if (itemEffects[1] & ITEM1_X_SPEED) + *(gBattleStruct->AI_itemFlags + gActiveBattler / 2) |= 0x4; + if (itemEffects[2] & ITEM2_X_SPATK) + *(gBattleStruct->AI_itemFlags + gActiveBattler / 2) |= 0x8; + if (itemEffects[2] & ITEM2_X_ACCURACY) + *(gBattleStruct->AI_itemFlags + gActiveBattler / 2) |= 0x20; + if (itemEffects[0] & ITEM0_HIGH_CRIT) + *(gBattleStruct->AI_itemFlags + gActiveBattler / 2) |= 0x80; + shouldUse = TRUE; + break; + case AI_ITEM_GUARD_SPECS: + battlerSide = GetBattlerSide(gActiveBattler); + if (gDisableStructs[gActiveBattler].isFirstTurn && gSideTimers[battlerSide].mistTimer == 0) + shouldUse = TRUE; + break; + case AI_ITEM_NOT_RECOGNIZABLE: + return FALSE; + } + if (shouldUse) + { + BtlController_EmitTwoReturnValues(1, B_ACTION_USE_ITEM, 0); + *(gBattleStruct->chosenItem + (gActiveBattler / 2) * 2) = item; + gBattleResources->battleHistory->trainerItems[i] = 0; + return shouldUse; + } + } + return FALSE; +} diff --git a/src/battle_anim_mon_movement.c b/src/battle_anim_mon_movement.c index 27e6ef28c..88fbce6da 100644 --- a/src/battle_anim_mon_movement.c +++ b/src/battle_anim_mon_movement.c @@ -6,43 +6,91 @@ #define abs(x) ((x) < 0 ? -(x) : (x)) -void sub_8098A6C(u8 taskId); -void sub_8098C08(u8 taskId); -void sub_8098D54(u8 taskId); -void sub_8098EF0(u8 taskId); -void sub_8099004(u8 taskId); -void sub_80990AC(struct Sprite * sprite); -void sub_8099120(struct Sprite * sprite); -void sub_8099144(struct Sprite * sprite); -void sub_8099190(struct Sprite * sprite); -void sub_80991B4(struct Sprite * sprite); -void sub_8099270(struct Sprite * sprite); -void sub_80992E0(struct Sprite * sprite); -void sub_8099394(struct Sprite * sprite); -void sub_809946C(struct Sprite * sprite); -void sub_8099530(u8 taskId); -void sub_8099594(u8 taskId); -void sub_80996B8(u8 taskId); -void sub_8099788(u8 taskId); -void sub_8099908(u8 taskId); -void sub_8099B54(u8 taskId); -void sub_8099CB8(u8 taskId); - -const struct SpriteTemplate gUnknown_83D4E4C[] = { - { - 0, 0, &gDummyOamData, gDummySpriteAnimTable, NULL, gDummySpriteAffineAnimTable, sub_80990AC - }, { - 0, 0, &gDummyOamData, gDummySpriteAnimTable, NULL, gDummySpriteAffineAnimTable, sub_8099144 - }, { - 0, 0, &gDummyOamData, gDummySpriteAnimTable, NULL, gDummySpriteAffineAnimTable, sub_80991B4 - }, { - 0, 0, &gDummyOamData, gDummySpriteAnimTable, NULL, gDummySpriteAffineAnimTable, sub_80992E0 - }, { - 0, 0, &gDummyOamData, gDummySpriteAnimTable, NULL, gDummySpriteAffineAnimTable, sub_8099394 - } +static void AnimTask_ShakeMonStep(u8 taskId); +static void AnimTask_ShakeMon2Step(u8 taskId); +static void AnimTask_ShakeMonInPlaceStep(u8 taskId); +static void AnimTask_ShakeAndSinkMonStep(u8 taskId); +static void AnimTask_TranslateMonEllipticalStep(u8 taskId); +static void DoHorizontalLunge(struct Sprite * sprite); +static void ReverseHorizontalLungeDirection(struct Sprite * sprite); +static void DoVerticalDip(struct Sprite * sprite); +static void ReverseVerticalDipDirection(struct Sprite * sprite); +static void SlideMonToOriginalPos(struct Sprite * sprite); +static void SlideMonToOriginalPosStep(struct Sprite * sprite); +static void SlideMonToOffset(struct Sprite * sprite); +static void sub_8099394(struct Sprite * sprite); +static void sub_809946C(struct Sprite * sprite); +static void AnimTask_WindUpLungePart1(u8 taskId); +static void AnimTask_WindUpLungePart2(u8 taskId); +static void sub_80996B8(u8 taskId); +static void AnimTask_SwayMonStep(u8 taskId); +static void AnimTask_ScaleMonAndRestoreStep(u8 taskId); +static void sub_8099B54(u8 taskId); +static void sub_8099CB8(u8 taskId); + +const struct SpriteTemplate gHorizontalLungeSpriteTemplate = +{ + .tileTag = 0, + .paletteTag = 0, + .oam = &gDummyOamData, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = DoHorizontalLunge, +}; + +const struct SpriteTemplate gVerticalDipSpriteTemplate = +{ + .tileTag = 0, + .paletteTag = 0, + .oam = &gDummyOamData, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = DoVerticalDip, +}; + +const struct SpriteTemplate gSlideMonToOriginalPosSpriteTemplate = +{ + .tileTag = 0, + .paletteTag = 0, + .oam = &gDummyOamData, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SlideMonToOriginalPos, +}; + +const struct SpriteTemplate gSlideMonToOffsetSpriteTemplate = +{ + .tileTag = 0, + .paletteTag = 0, + .oam = &gDummyOamData, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SlideMonToOffset, +}; + +const struct SpriteTemplate gUnknown_83D4EB4 = +{ + .tileTag = 0, + .paletteTag = 0, + .oam = &gDummyOamData, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = sub_8099394, }; -void sub_80989F8(u8 taskId) +// Task to facilitate simple shaking of a pokemon's picture in battle. +// The shaking alternates between the original position and the target position. +// arg 0: anim battler +// arg 1: x pixel offset +// arg 2: y pixel offset +// arg 3: num times to shake +// arg 4: frame delay +void AnimTask_ShakeMon(u8 taskId) { u8 spriteId = GetAnimBankSpriteId(gBattleAnimArgs[0]); @@ -58,12 +106,12 @@ void sub_80989F8(u8 taskId) gTasks[taskId].data[3] = gBattleAnimArgs[4]; gTasks[taskId].data[4] = gBattleAnimArgs[1]; gTasks[taskId].data[5] = gBattleAnimArgs[2]; - gTasks[taskId].func = sub_8098A6C; + gTasks[taskId].func = AnimTask_ShakeMonStep; gTasks[taskId].func(taskId); } } -void sub_8098A6C(u8 taskId) +static void AnimTask_ShakeMonStep(u8 taskId) { if (gTasks[taskId].data[3] == 0) { @@ -87,7 +135,14 @@ void sub_8098A6C(u8 taskId) gTasks[taskId].data[3]--; } -void sub_8098B1C(u8 taskId) +// Task to facilitate simple shaking of a pokemon's picture in battle. +// The shaking alternates between the positive and negative versions of the specified pixel offsets. +// arg 0: anim battler +// arg 1: x pixel offset +// arg 2: y pixel offset +// arg 3: num times to shake +// arg 4: frame delay +void AnimTask_ShakeMon2(u8 taskId) { bool8 abort = FALSE; u8 spriteId; @@ -116,7 +171,7 @@ void sub_8098B1C(u8 taskId) battlerId = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); break; } - if (!sub_8072DF0(battlerId)) + if (!IsBattlerSpriteVisible(battlerId)) abort = TRUE; spriteId = gBattlerSpriteIds[battlerId]; } @@ -134,12 +189,12 @@ void sub_8098B1C(u8 taskId) gTasks[taskId].data[3] = gBattleAnimArgs[4]; gTasks[taskId].data[4] = gBattleAnimArgs[1]; gTasks[taskId].data[5] = gBattleAnimArgs[2]; - gTasks[taskId].func = sub_8098C08; + gTasks[taskId].func = AnimTask_ShakeMon2Step; gTasks[taskId].func(taskId); } } -void sub_8098C08(u8 taskId) +static void AnimTask_ShakeMon2Step(u8 taskId) { if (gTasks[taskId].data[3] == 0) { @@ -163,7 +218,15 @@ void sub_8098C08(u8 taskId) gTasks[taskId].data[3]--; } -void sub_8098CD0(u8 taskId) +// Task to facilitate simple shaking of a pokemon's picture in battle. +// The shaking alternates between the positive and negative versions of the specified pixel offsets +// with respect to the current location of the mon's picture. +// arg 0: battler +// arg 1: x offset +// arg 2: y offset +// arg 3: num shakes +// arg 4: delay +void AnimTask_ShakeMonInPlace(u8 taskId) { u8 spriteId = GetAnimBankSpriteId(gBattleAnimArgs[0]); @@ -180,12 +243,12 @@ void sub_8098CD0(u8 taskId) gTasks[taskId].data[4] = gBattleAnimArgs[4]; gTasks[taskId].data[5] = gBattleAnimArgs[1] * 2; gTasks[taskId].data[6] = gBattleAnimArgs[2] * 2; - gTasks[taskId].func = sub_8098D54; + gTasks[taskId].func = AnimTask_ShakeMonInPlaceStep; gTasks[taskId].func(taskId); } } -void sub_8098D54(u8 taskId) +static void AnimTask_ShakeMonInPlaceStep(u8 taskId) { if (gTasks[taskId].data[3] == 0) { @@ -219,7 +282,13 @@ void sub_8098D54(u8 taskId) gTasks[taskId].data[3]--; } -void sub_8098E90(u8 taskId) +// Shakes a mon bg horizontally and moves it downward linearly. +// arg 0: battler +// arg 1: x offset +// arg 2: frame delay between each movement +// arg 3: downward speed (subpixel) +// arg 4: duration +void AnimTask_ShakeAndSinkMon(u8 taskId) { u8 spriteId = GetAnimBankSpriteId(gBattleAnimArgs[0]); @@ -229,11 +298,11 @@ void sub_8098E90(u8 taskId) gTasks[taskId].data[2] = gBattleAnimArgs[2]; gTasks[taskId].data[3] = gBattleAnimArgs[3]; gTasks[taskId].data[4] = gBattleAnimArgs[4]; - gTasks[taskId].func = sub_8098EF0; + gTasks[taskId].func = AnimTask_ShakeAndSinkMonStep; gTasks[taskId].func(taskId); } -void sub_8098EF0(u8 taskId) +static void AnimTask_ShakeAndSinkMonStep(u8 taskId) { u8 spriteId = gTasks[taskId].data[0]; s16 data1 = gTasks[taskId].data[1]; @@ -251,25 +320,32 @@ void sub_8098EF0(u8 taskId) DestroyAnimVisualTask(taskId); } -void sub_8098F84(u8 taskId) +// Moves a mon bg picture along an elliptical path that begins +// and ends at the mon's origin location. +// arg 0: battler +// arg 1: ellipse width +// arg 2: ellipse height +// arg 3: num loops +// arg 4: speed (valid values are 0-5) +void AnimTask_TranslateMonElliptical(u8 taskId) { - u8 r6 = 1; + u8 wavePeriod = 1; u8 i; u8 spriteId = GetAnimBankSpriteId(gBattleAnimArgs[0]); if (gBattleAnimArgs[4] > 5) gBattleAnimArgs[4] = 5; for (i = 0; i < gBattleAnimArgs[4]; i++) - r6 *= 2; + wavePeriod *= 2; gTasks[taskId].data[0] = spriteId; gTasks[taskId].data[1] = gBattleAnimArgs[1]; gTasks[taskId].data[2] = gBattleAnimArgs[2]; gTasks[taskId].data[3] = gBattleAnimArgs[3]; - gTasks[taskId].data[4] = r6; - gTasks[taskId].func = sub_8099004; + gTasks[taskId].data[4] = wavePeriod; + gTasks[taskId].func = AnimTask_TranslateMonEllipticalStep; gTasks[taskId].func(taskId); } -void sub_8099004(u8 taskId) +static void AnimTask_TranslateMonEllipticalStep(u8 taskId) { u8 spriteId = gTasks[taskId].data[0]; gSprites[spriteId].pos2.x = Sin(gTasks[taskId].data[5], gTasks[taskId].data[1]); @@ -287,14 +363,26 @@ void sub_8099004(u8 taskId) } } -void sub_809907C(u8 taskId) +// Moves a mon bg picture along an elliptical path that begins +// and ends at the mon's origin location. Reverses the direction +// of the path if it's not on the player's side of the battle. +// arg 0: battler +// arg 1: ellipse width +// arg 2: ellipse height +// arg 3: num loops +// arg 4: speed (valid values are 0-5) +void AnimTask_TranslateMonEllipticalRespectSide(u8 taskId) { if (GetBattlerSide(gBattleAnimAttacker) != B_SIDE_PLAYER) gBattleAnimArgs[1] = -gBattleAnimArgs[1]; - sub_8098F84(taskId); + AnimTask_TranslateMonElliptical(taskId); } -void sub_80990AC(struct Sprite * sprite) +// Performs a simple horizontal lunge, where the mon moves +// horizontally, and then moves back in the opposite direction. +// arg 0: duration of single lunge direction +// arg 1: x pixel delta that is applied each frame +static void DoHorizontalLunge(struct Sprite * sprite) { sprite->invisible = TRUE; if (GetBattlerSide(gBattleAnimAttacker) != B_SIDE_PLAYER) @@ -305,19 +393,24 @@ void sub_80990AC(struct Sprite * sprite) sprite->data[2] = 0; sprite->data[3] = gBattlerSpriteIds[gBattleAnimAttacker]; sprite->data[4] = gBattleAnimArgs[0]; - StoreSpriteCallbackInData6(sprite, sub_8099120); - sprite->callback = sub_8074DC4; + StoreSpriteCallbackInData6(sprite, ReverseHorizontalLungeDirection); + sprite->callback = TranslateMonSpriteLinear; } -void sub_8099120(struct Sprite * sprite) +static void ReverseHorizontalLungeDirection(struct Sprite * sprite) { sprite->data[0] = sprite->data[4]; sprite->data[1] = -sprite->data[1]; - sprite->callback = sub_8074DC4; + sprite->callback = TranslateMonSpriteLinear; StoreSpriteCallbackInData6(sprite, DestroyAnimSprite); } -void sub_8099144(struct Sprite * sprite) +// Performs a simple vertical dipping motion, where moves vertically, and then +// moves back in the opposite direction. +// arg 0: duration of single dip direction +// arg 1: y pixel delta that is applied each frame +// arg 2: battler +static void DoVerticalDip(struct Sprite * sprite) { u8 spriteId; sprite->invisible = TRUE; @@ -327,19 +420,24 @@ void sub_8099144(struct Sprite * sprite) sprite->data[2] = gBattleAnimArgs[1]; sprite->data[3] = spriteId; sprite->data[4] = gBattleAnimArgs[0]; - StoreSpriteCallbackInData6(sprite, sub_8099190); - sprite->callback = sub_8074DC4; + StoreSpriteCallbackInData6(sprite, ReverseVerticalDipDirection); + sprite->callback = TranslateMonSpriteLinear; } -void sub_8099190(struct Sprite * sprite) +static void ReverseVerticalDipDirection(struct Sprite * sprite) { sprite->data[0] = sprite->data[4]; sprite->data[2] = -sprite->data[2]; - sprite->callback = sub_8074DC4; + sprite->callback = TranslateMonSpriteLinear; StoreSpriteCallbackInData6(sprite, DestroyAnimSprite); } -void sub_80991B4(struct Sprite * sprite) +// Linearly slides a mon's bg picture back to its original sprite position. +// The sprite parameter is a dummy sprite used for facilitating the movement with its callback. +// arg 0: 1 = target or 0 = attacker +// arg 1: direction (0 = horizontal and vertical, 1 = horizontal only, 2 = vertical only) +// arg 2: duration +static void SlideMonToOriginalPos(struct Sprite * sprite) { u8 spriteId; if (gBattleAnimArgs[0] == 0) @@ -351,7 +449,7 @@ void sub_80991B4(struct Sprite * sprite) sprite->data[2] = gSprites[spriteId].pos1.x; sprite->data[3] = gSprites[spriteId].pos1.y + gSprites[spriteId].pos2.y; sprite->data[4] = gSprites[spriteId].pos1.y; - sub_80754B8(sprite); + InitSpriteDataForLinearTranslation(sprite); sprite->data[3] = 0; sprite->data[4] = 0; sprite->data[5] = gSprites[spriteId].pos2.x; @@ -363,10 +461,10 @@ void sub_80991B4(struct Sprite * sprite) sprite->data[1] = 0; sprite->data[7] = gBattleAnimArgs[1]; sprite->data[7] |= spriteId << 8; - sprite->callback = sub_8099270; + sprite->callback = SlideMonToOriginalPosStep; } -void sub_8099270(struct Sprite * sprite) +static void SlideMonToOriginalPosStep(struct Sprite * sprite) { u8 data7 = sprite->data[7]; struct Sprite *otherSprite = &gSprites[sprite->data[7] >> 8]; @@ -388,7 +486,15 @@ void sub_8099270(struct Sprite * sprite) } } -void sub_80992E0(struct Sprite * sprite) +// Linearly translates a mon to a target offset. The horizontal offset +// is mirrored for the opponent's pokemon, and the vertical offset +// is only mirrored if arg 3 is set to 1. +// arg 0: 0 = attacker, 1 = target +// arg 1: target x pixel offset +// arg 2: target y pixel offset +// arg 3: mirror vertical translation for opposite battle side +// arg 4: duration +static void SlideMonToOffset(struct Sprite * sprite) { u8 battlerId; u8 spriteId; @@ -408,16 +514,16 @@ void sub_80992E0(struct Sprite * sprite) sprite->data[2] = gSprites[spriteId].pos1.x + gBattleAnimArgs[1]; sprite->data[3] = gSprites[spriteId].pos1.y; sprite->data[4] = gSprites[spriteId].pos1.y + gBattleAnimArgs[2]; - sub_80754B8(sprite); + InitSpriteDataForLinearTranslation(sprite); sprite->data[3] = 0; sprite->data[4] = 0; sprite->data[5] = spriteId; sprite->invisible = TRUE; StoreSpriteCallbackInData6(sprite, DestroyAnimSprite); - sprite->callback = sub_8074E14; + sprite->callback = TranslateMonSpriteLinearFixedPoint; } -void sub_8099394(struct Sprite * sprite) +static void sub_8099394(struct Sprite * sprite) { u8 battlerId; u8 spriteId; @@ -438,7 +544,7 @@ void sub_8099394(struct Sprite * sprite) sprite->data[2] = sprite->data[1] + gBattleAnimArgs[1]; sprite->data[3] = gSprites[spriteId].pos1.y + gSprites[spriteId].pos2.y; sprite->data[4] = sprite->data[3] + gBattleAnimArgs[2]; - sub_80754B8(sprite); + InitSpriteDataForLinearTranslation(sprite); sprite->data[3] = gSprites[spriteId].pos2.x << 8; sprite->data[4] = gSprites[spriteId].pos2.y << 8; sprite->data[5] = spriteId; @@ -447,19 +553,29 @@ void sub_8099394(struct Sprite * sprite) StoreSpriteCallbackInData6(sprite, DestroyAnimSprite); else StoreSpriteCallbackInData6(sprite, sub_809946C); - sprite->callback = sub_8074E14; + sprite->callback = TranslateMonSpriteLinearFixedPoint; } -void sub_809946C(struct Sprite * sprite) +static void sub_809946C(struct Sprite * sprite) { gSprites[sprite->data[5]].pos2.x = 0; gSprites[sprite->data[5]].pos2.y = 0; DestroyAnimSprite(sprite); } -void sub_809949C(u8 taskId) +// Task to facilitate a two-part translation animation, in which the sprite +// is first translated in an arc to one position. Then, it "lunges" to a target +// x offset. Used in TAKE_DOWN, for example. +// arg 0: anim bank +// arg 1: horizontal speed (subpixel) +// arg 2: wave amplitude +// arg 3: first duration +// arg 4: delay before starting lunge +// arg 5: target x offset for lunge +// arg 6: lunge duration +void AnimTask_WindUpLunge(u8 taskId) { - u16 r7 = 0x8000 / gBattleAnimArgs[3]; + u16 wavePeriod = 0x8000 / gBattleAnimArgs[3]; if (GetBattlerSide(gBattleAnimAttacker) != B_SIDE_PLAYER) { gBattleAnimArgs[1] = -gBattleAnimArgs[1]; @@ -472,11 +588,11 @@ void sub_809949C(u8 taskId) gTasks[taskId].data[4] = gBattleAnimArgs[4]; gTasks[taskId].data[5] = gBattleAnimArgs[5] * 256 / gBattleAnimArgs[6]; gTasks[taskId].data[6] = gBattleAnimArgs[6]; - gTasks[taskId].data[7] = r7; - gTasks[taskId].func = sub_8099530; + gTasks[taskId].data[7] = wavePeriod; + gTasks[taskId].func = AnimTask_WindUpLungePart1; } -void sub_8099530(u8 taskId) +static void AnimTask_WindUpLungePart1(u8 taskId) { u8 spriteId = gTasks[taskId].data[0]; gTasks[taskId].data[11] += gTasks[taskId].data[1]; @@ -484,10 +600,10 @@ void sub_8099530(u8 taskId) gSprites[spriteId].pos2.y = Sin((u8)(gTasks[taskId].data[10] >> 8), gTasks[taskId].data[2]); gTasks[taskId].data[10] += gTasks[taskId].data[7]; if (--gTasks[taskId].data[3] == 0) - gTasks[taskId].func = sub_8099594; + gTasks[taskId].func = AnimTask_WindUpLungePart2; } -void sub_8099594(u8 taskId) +static void AnimTask_WindUpLungePart2(u8 taskId) { u8 spriteId; @@ -514,7 +630,7 @@ void sub_80995FC(u8 taskId) spriteId = GetAnimBankSpriteId(gBattleAnimArgs[0]); break; case 2: - if (!sub_8072DF0(gBattleAnimAttacker ^ BIT_FLANK)) + if (!IsBattlerSpriteVisible(gBattleAnimAttacker ^ BIT_FLANK)) { DestroyAnimVisualTask(taskId); return; @@ -522,7 +638,7 @@ void sub_80995FC(u8 taskId) spriteId = gBattlerSpriteIds[gBattleAnimAttacker ^ BIT_FLANK]; break; case 3: - if (!sub_8072DF0(gBattleAnimTarget ^ BIT_FLANK)) + if (!IsBattlerSpriteVisible(gBattleAnimTarget ^ BIT_FLANK)) { DestroyAnimVisualTask(taskId); return; @@ -541,7 +657,7 @@ void sub_80995FC(u8 taskId) gTasks[taskId].func = sub_80996B8; } -void sub_80996B8(u8 taskId) +static void sub_80996B8(u8 taskId) { u8 spriteId = gTasks[taskId].data[0]; gSprites[spriteId].pos2.x += gTasks[taskId].data[1]; @@ -549,7 +665,15 @@ void sub_80996B8(u8 taskId) DestroyAnimVisualTask(taskId); } -void sub_8099704(u8 taskId) +// Task that facilitates translating the mon bg picture back and forth +// in a swaying motion (uses Sine wave). It can sway either horizontally +// or vertically, but not both. +// arg 0: direction (0 = horizontal, 1 = vertical) +// arg 1: wave amplitude +// arg 2: wave period +// arg 3: num sways +// arg 4: which mon (0 = attacker, 1 = target) +void AnimTask_SwayMon(u8 taskId) { u8 spriteId; if (GetBattlerSide(gBattleAnimAttacker) != B_SIDE_PLAYER) @@ -565,32 +689,32 @@ void sub_8099704(u8 taskId) else gTasks[taskId].data[5] = gBattleAnimTarget; gTasks[taskId].data[12] = 1; - gTasks[taskId].func = sub_8099788; + gTasks[taskId].func = AnimTask_SwayMonStep; } -void sub_8099788(u8 taskId) +static void AnimTask_SwayMonStep(u8 taskId) { u8 spriteId; - u32 r8; - s16 r5; - u16 tmp; + u32 waveIndex; + s16 sineValue; + u16 sineIndex; spriteId = gTasks[taskId].data[4]; - tmp = gTasks[taskId].data[10] + gTasks[taskId].data[2]; - gTasks[taskId].data[10] = tmp; - r8 = tmp >> 8; - r5 = Sin(r8, gTasks[taskId].data[1]); + sineIndex = gTasks[taskId].data[10] + gTasks[taskId].data[2]; + gTasks[taskId].data[10] = sineIndex; + waveIndex = sineIndex >> 8; + sineValue = Sin(waveIndex, gTasks[taskId].data[1]); if (gTasks[taskId].data[0] == 0) { - gSprites[spriteId].pos2.x = r5; + gSprites[spriteId].pos2.x = sineValue; } else if (GetBattlerSide(gTasks[taskId].data[5]) == B_SIDE_PLAYER) { - gSprites[spriteId].pos2.y = abs(r5); + gSprites[spriteId].pos2.y = abs(sineValue); } else - gSprites[spriteId].pos2.y = -abs(r5); - if ((r8 > 0x7F && gTasks[taskId].data[11] == 0 && gTasks[taskId].data[12] == 1) || (r8 < 0x7F && gTasks[taskId].data[11] == 1 && gTasks[taskId].data[12] == 0)) + gSprites[spriteId].pos2.y = -abs(sineValue); + if ((waveIndex > 0x7F && gTasks[taskId].data[11] == 0 && gTasks[taskId].data[12] == 1) || (waveIndex < 0x7F && gTasks[taskId].data[11] == 1 && gTasks[taskId].data[12] == 0)) { gTasks[taskId].data[11] ^= 1; gTasks[taskId].data[12] ^= 1; @@ -603,10 +727,16 @@ void sub_8099788(u8 taskId) } } -void sub_80998B0(u8 taskId) +// Scales a mon's sprite, and then scales back to its original dimensions. +// arg 0: x scale delta +// arg 1: y scale delta +// arg 2: duration +// arg 3: anim bank +// arg 4: sprite object mode +void AnimTask_ScaleMonAndRestore(u8 taskId) { u8 spriteId = GetAnimBankSpriteId(gBattleAnimArgs[3]); - sub_80758E0(spriteId, gBattleAnimArgs[4]); + PrepareBattlerSpriteForRotScale(spriteId, gBattleAnimArgs[4]); gTasks[taskId].data[0] = gBattleAnimArgs[0]; gTasks[taskId].data[1] = gBattleAnimArgs[1]; gTasks[taskId].data[2] = gBattleAnimArgs[2]; @@ -614,16 +744,16 @@ void sub_80998B0(u8 taskId) gTasks[taskId].data[4] = spriteId; gTasks[taskId].data[10] = 0x100; gTasks[taskId].data[11] = 0x100; - gTasks[taskId].func = sub_8099908; + gTasks[taskId].func = AnimTask_ScaleMonAndRestoreStep; } -void sub_8099908(u8 taskId) +static void AnimTask_ScaleMonAndRestoreStep(u8 taskId) { u8 spriteId; gTasks[taskId].data[10] += gTasks[taskId].data[0]; gTasks[taskId].data[11] += gTasks[taskId].data[1]; spriteId = gTasks[taskId].data[4]; - obj_id_set_rotscale(spriteId, gTasks[taskId].data[10], gTasks[taskId].data[11], 0); + SetSpriteRotScale(spriteId, gTasks[taskId].data[10], gTasks[taskId].data[11], 0); if (--gTasks[taskId].data[2] == 0) { if (gTasks[taskId].data[3] > 0) @@ -635,7 +765,7 @@ void sub_8099908(u8 taskId) } else { - sub_8075980(spriteId); + ResetSpriteRotScale(spriteId); DestroyAnimVisualTask(taskId); } } @@ -644,7 +774,7 @@ void sub_8099908(u8 taskId) void sub_8099980(u8 taskId) { u8 spriteId = GetAnimBankSpriteId(gBattleAnimArgs[2]); - sub_80758E0(spriteId, 0); + PrepareBattlerSpriteForRotScale(spriteId, 0); gTasks[taskId].data[1] = 0; gTasks[taskId].data[2] = gBattleAnimArgs[0]; if (gBattleAnimArgs[3] != 1) @@ -654,7 +784,7 @@ void sub_8099980(u8 taskId) gTasks[taskId].data[4] = gBattleAnimArgs[1]; gTasks[taskId].data[5] = spriteId; gTasks[taskId].data[6] = gBattleAnimArgs[3]; - if (sub_8073788()) + if (IsContest()) gTasks[taskId].data[7] = 1; else { @@ -663,7 +793,7 @@ void sub_8099980(u8 taskId) else gTasks[taskId].data[7] = GetBattlerSide(gBattleAnimTarget) == B_SIDE_PLAYER ? 1 : 0; } - if (gTasks[taskId].data[7] && !sub_8073788()) + if (gTasks[taskId].data[7] && !IsContest()) { s16 tmp; tmp = gTasks[taskId].data[3]; @@ -677,7 +807,7 @@ void sub_8099980(u8 taskId) void sub_8099A78(u8 taskId) { u8 spriteId = GetAnimBankSpriteId(gBattleAnimArgs[2]); - sub_80758E0(spriteId, 0); + PrepareBattlerSpriteForRotScale(spriteId, 0); gTasks[taskId].data[1] = 0; gTasks[taskId].data[2] = gBattleAnimArgs[0]; if (gBattleAnimArgs[2] == 0) @@ -709,11 +839,10 @@ void sub_8099A78(u8 taskId) gTasks[taskId].func = sub_8099B54; } -void sub_8099B54(u8 taskId) +static void sub_8099B54(u8 taskId) { - s16 tmp; gTasks[taskId].data[3] += gTasks[taskId].data[4]; - obj_id_set_rotscale(gTasks[taskId].data[5], 0x100, 0x100, gTasks[taskId].data[3]); + SetSpriteRotScale(gTasks[taskId].data[5], 0x100, 0x100, gTasks[taskId].data[3]); if (gTasks[taskId].data[7]) sub_80759DC(gTasks[taskId].data[5]); if (++gTasks[taskId].data[1] >= gTasks[taskId].data[2]) @@ -721,7 +850,7 @@ void sub_8099B54(u8 taskId) switch (gTasks[taskId].data[6]) { case 1: - sub_8075980(gTasks[taskId].data[5]); + ResetSpriteRotScale(gTasks[taskId].data[5]); // fallthrough case 0: default: @@ -729,8 +858,7 @@ void sub_8099B54(u8 taskId) break; case 2: gTasks[taskId].data[1] = 0; - tmp = gTasks[taskId].data[4]; - gTasks[taskId].data[4] = -tmp; + gTasks[taskId].data[4] *= -1; gTasks[taskId].data[6] = 1; break; } @@ -741,7 +869,7 @@ void sub_8099BD4(u8 taskId) { if (gBattleAnimArgs[0] == 0) { - gTasks[taskId].data[15] = gUnknown_2037EEC / 12; + gTasks[taskId].data[15] = gAnimMovePower / 12; if (gTasks[taskId].data[15] < 1) gTasks[taskId].data[15] = 1; if (gTasks[taskId].data[15] > 16) @@ -749,7 +877,7 @@ void sub_8099BD4(u8 taskId) } else { - gTasks[taskId].data[15] = gUnknown_2037EE8 / 12; + gTasks[taskId].data[15] = gAnimMoveDmg / 12; if (gTasks[taskId].data[15] < 1) gTasks[taskId].data[15] = 1; if (gTasks[taskId].data[15] > 16) @@ -769,7 +897,7 @@ void sub_8099BD4(u8 taskId) gTasks[taskId].func = sub_8099CB8; } -void sub_8099CB8(u8 taskId) +static void sub_8099CB8(u8 taskId) { struct Task *task = &gTasks[taskId]; if (++task->data[0] > task->data[1]) diff --git a/src/battle_controller_link_opponent.c b/src/battle_controller_link_opponent.c new file mode 100644 index 000000000..7df93e170 --- /dev/null +++ b/src/battle_controller_link_opponent.c @@ -0,0 +1,1686 @@ +#include "global.h" +#include "bg.h" +#include "data.h" +#include "link.h" +#include "main.h" +#include "m4a.h" +#include "palette.h" +#include "pokeball.h" +#include "pokemon.h" +#include "reshow_battle_screen.h" +#include "sound.h" +#include "string_util.h" +#include "task.h" +#include "text.h" +#include "util.h" +#include "window.h" +#include "battle.h" +#include "battle_ai_script_commands.h" +#include "battle_anim.h" +#include "battle_controllers.h" +#include "battle_interface.h" +#include "battle_message.h" +#include "battle_setup.h" +#include "battle_tower.h" +#include "constants/battle_anim.h" +#include "constants/songs.h" +#include "constants/facility_trainer_classes.h" + +static void LinkOpponentHandleGetMonData(void); +static void LinkOpponentHandleGetRawMonData(void); +static void LinkOpponentHandleSetMonData(void); +static void LinkOpponentHandleSetRawMonData(void); +static void LinkOpponentHandleLoadMonSprite(void); +static void LinkOpponentHandleSwitchInAnim(void); +static void LinkOpponentHandleReturnMonToBall(void); +static void LinkOpponentHandleDrawTrainerPic(void); +static void LinkOpponentHandleTrainerSlide(void); +static void LinkOpponentHandleTrainerSlideBack(void); +static void LinkOpponentHandleFaintAnimation(void); +static void LinkOpponentHandlePaletteFade(void); +static void LinkOpponentHandleSuccessBallThrowAnim(void); +static void LinkOpponentHandleBallThrowAnim(void); +static void LinkOpponentHandlePause(void); +static void LinkOpponentHandleMoveAnimation(void); +static void LinkOpponentHandlePrintString(void); +static void LinkOpponentHandlePrintSelectionString(void); +static void LinkOpponentHandleChooseAction(void); +static void LinkOpponentHandleUnknownYesNoBox(void); +static void LinkOpponentHandleChooseMove(void); +static void LinkOpponentHandleChooseItem(void); +static void LinkOpponentHandleChoosePokemon(void); +static void LinkOpponentHandleCmd23(void); +static void LinkOpponentHandleHealthBarUpdate(void); +static void LinkOpponentHandleExpUpdate(void); +static void LinkOpponentHandleStatusIconUpdate(void); +static void LinkOpponentHandleStatusAnimation(void); +static void LinkOpponentHandleStatusXor(void); +static void LinkOpponentHandleDataTransfer(void); +static void LinkOpponentHandleDMA3Transfer(void); +static void LinkOpponentHandlePlayBGM(void); +static void LinkOpponentHandleCmd32(void); +static void LinkOpponentHandleTwoReturnValues(void); +static void LinkOpponentHandleChosenMonReturnValue(void); +static void LinkOpponentHandleOneReturnValue(void); +static void LinkOpponentHandleOneReturnValue_Duplicate(void); +static void LinkOpponentHandleCmd37(void); +static void LinkOpponentHandleCmd38(void); +static void LinkOpponentHandleCmd39(void); +static void LinkOpponentHandleCmd40(void); +static void LinkOpponentHandleHitAnimation(void); +static void LinkOpponentHandleCmd42(void); +static void LinkOpponentHandlePlaySE(void); +static void LinkOpponentHandlePlayFanfare(void); +static void LinkOpponentHandleFaintingCry(void); +static void LinkOpponentHandleIntroSlide(void); +static void LinkOpponentHandleIntroTrainerBallThrow(void); +static void LinkOpponentHandleDrawPartyStatusSummary(void); +static void LinkOpponentHandleHidePartyStatusSummary(void); +static void LinkOpponentHandleEndBounceEffect(void); +static void LinkOpponentHandleSpriteInvisibility(void); +static void LinkOpponentHandleBattleAnimation(void); +static void LinkOpponentHandleLinkStandbyMsg(void); +static void LinkOpponentHandleResetActionMoveSelection(void); +static void LinkOpponentHandleCmd55(void); +static void LinkOpponentCmdEnd(void); + +static void LinkOpponentBufferRunCommand(void); +static void LinkOpponentBufferExecCompleted(void); +static u32 CopyLinkOpponentMonData(u8 monId, u8 *dst); +static void SetLinkOpponentMonData(u8 monId); +static void DoSwitchOutAnimation(void); +static void LinkOpponentDoMoveAnimation(void); +static void sub_803AEDC(void); +static void sub_803C550(u8 battlerId, bool8 dontClearSubstituteBit); +static void sub_803D564(u8 taskId); +static void sub_803D648(struct Sprite *sprite); +static void sub_803D790(void); + +static void (*const sLinkOpponentBufferCommands[CONTROLLER_CMDS_COUNT])(void) = +{ + LinkOpponentHandleGetMonData, + LinkOpponentHandleGetRawMonData, + LinkOpponentHandleSetMonData, + LinkOpponentHandleSetRawMonData, + LinkOpponentHandleLoadMonSprite, + LinkOpponentHandleSwitchInAnim, + LinkOpponentHandleReturnMonToBall, + LinkOpponentHandleDrawTrainerPic, + LinkOpponentHandleTrainerSlide, + LinkOpponentHandleTrainerSlideBack, + LinkOpponentHandleFaintAnimation, + LinkOpponentHandlePaletteFade, + LinkOpponentHandleSuccessBallThrowAnim, + LinkOpponentHandleBallThrowAnim, + LinkOpponentHandlePause, + LinkOpponentHandleMoveAnimation, + LinkOpponentHandlePrintString, + LinkOpponentHandlePrintSelectionString, + LinkOpponentHandleChooseAction, + LinkOpponentHandleUnknownYesNoBox, + LinkOpponentHandleChooseMove, + LinkOpponentHandleChooseItem, + LinkOpponentHandleChoosePokemon, + LinkOpponentHandleCmd23, + LinkOpponentHandleHealthBarUpdate, + LinkOpponentHandleExpUpdate, + LinkOpponentHandleStatusIconUpdate, + LinkOpponentHandleStatusAnimation, + LinkOpponentHandleStatusXor, + LinkOpponentHandleDataTransfer, + LinkOpponentHandleDMA3Transfer, + LinkOpponentHandlePlayBGM, + LinkOpponentHandleCmd32, + LinkOpponentHandleTwoReturnValues, + LinkOpponentHandleChosenMonReturnValue, + LinkOpponentHandleOneReturnValue, + LinkOpponentHandleOneReturnValue_Duplicate, + LinkOpponentHandleCmd37, + LinkOpponentHandleCmd38, + LinkOpponentHandleCmd39, + LinkOpponentHandleCmd40, + LinkOpponentHandleHitAnimation, + LinkOpponentHandleCmd42, + LinkOpponentHandlePlaySE, + LinkOpponentHandlePlayFanfare, + LinkOpponentHandleFaintingCry, + LinkOpponentHandleIntroSlide, + LinkOpponentHandleIntroTrainerBallThrow, + LinkOpponentHandleDrawPartyStatusSummary, + LinkOpponentHandleHidePartyStatusSummary, + LinkOpponentHandleEndBounceEffect, + LinkOpponentHandleSpriteInvisibility, + LinkOpponentHandleBattleAnimation, + LinkOpponentHandleLinkStandbyMsg, + LinkOpponentHandleResetActionMoveSelection, + LinkOpponentHandleCmd55, + LinkOpponentCmdEnd +}; + + +static void nullsub_19(void) +{ +} + +void SetControllerToLinkOpponent(void) +{ + gBattlerControllerFuncs[gActiveBattler] = LinkOpponentBufferRunCommand; +} + +static void LinkOpponentBufferRunCommand(void) +{ + if (gBattleControllerExecFlags & gBitTable[gActiveBattler]) + { + if (gBattleBufferA[gActiveBattler][0] < NELEMS(sLinkOpponentBufferCommands)) + sLinkOpponentBufferCommands[gBattleBufferA[gActiveBattler][0]](); + else + LinkOpponentBufferExecCompleted(); + } +} + +static void CompleteOnBattlerSpriteCallbackDummy(void) +{ + if (gSprites[gBattlerSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy) + LinkOpponentBufferExecCompleted(); +} + +static void sub_803A70C(void) +{ + if (gSprites[gBattlerSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy) + { + FreeTrainerFrontPicPaletteAndTile(gSprites[gBattlerSpriteIds[gActiveBattler]].oam.affineParam); + gSprites[gBattlerSpriteIds[gActiveBattler]].oam.tileNum = gSprites[gBattlerSpriteIds[gActiveBattler]].data[5]; + FreeSpriteOamMatrix(&gSprites[gBattlerSpriteIds[gActiveBattler]]); + DestroySprite(&gSprites[gBattlerSpriteIds[gActiveBattler]]); + LinkOpponentBufferExecCompleted(); + } +} + +static void sub_803A79C(void) +{ + if (--gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_9 == 0xFF) + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_9 = 0; + LinkOpponentBufferExecCompleted(); + } +} + +static void sub_803A7E4(void) +{ + bool8 var = FALSE; + + if (!IsDoubleBattle() || ((IsDoubleBattle() && (gBattleTypeFlags & BATTLE_TYPE_MULTI)))) + { + if (gSprites[gHealthboxSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy) + var = TRUE; + } + else if (gSprites[gHealthboxSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy + && gSprites[gHealthboxSpriteIds[gActiveBattler ^ BIT_FLANK]].callback == gSprites[gHealthboxSpriteIds[gActiveBattler]].callback) + { + var = TRUE; + } + if (IsCryPlayingOrClearCrySongs()) + var = FALSE; + if (var) + { + if (GetBattlerPosition(gActiveBattler) == B_POSITION_OPPONENT_LEFT) + { + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_1_x1 + || !gBattleSpritesDataPtr->healthBoxesData[gActiveBattler ^ BIT_FLANK].field_1_x1) + return; + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].flag_x80 = 0; + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_1_x1 = 0; + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler ^ BIT_FLANK].flag_x80 = 0; + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler ^ BIT_FLANK].field_1_x1 = 0; + FreeSpriteTilesByTag(0x27F9); + FreeSpritePaletteByTag(0x27F9); + } + if (gBattleTypeFlags & BATTLE_TYPE_MULTI) + { + if (GetBattlerPosition(gActiveBattler) == B_POSITION_OPPONENT_LEFT) + m4aMPlayContinue(&gMPlayInfo_BGM); + } + else + { + m4aMPlayVolumeControl(&gMPlayInfo_BGM, 0xFFFF, 256); + } + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_9 = 3; + gBattlerControllerFuncs[gActiveBattler] = sub_803A79C; + } +} + +static void sub_803A9CC(void) +{ + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].ballAnimActive + && !gBattleSpritesDataPtr->healthBoxesData[gActiveBattler ^ BIT_FLANK].ballAnimActive) + { + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].flag_x80) + sub_80F1720(gActiveBattler, &gEnemyParty[gBattlerPartyIndexes[gActiveBattler]]); + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler ^ BIT_FLANK].flag_x80) + sub_80F1720(gActiveBattler ^ BIT_FLANK, &gEnemyParty[gBattlerPartyIndexes[gActiveBattler ^ BIT_FLANK]]); + if (gBattleTypeFlags & BATTLE_TYPE_MULTI && GetBattlerPosition(gActiveBattler) == 3) + { + if (++gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_9 == 1) + return; + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_9 = 0; + } + if (IsDoubleBattle() && !(gBattleTypeFlags & BATTLE_TYPE_MULTI)) + { + DestroySprite(&gSprites[gUnknown_3004FFC[gActiveBattler ^ BIT_FLANK]]); + UpdateHealthboxAttribute(gHealthboxSpriteIds[gActiveBattler ^ BIT_FLANK], + &gEnemyParty[gBattlerPartyIndexes[gActiveBattler ^ BIT_FLANK]], + HEALTHBOX_ALL); + sub_804BD94(gActiveBattler ^ BIT_FLANK); + SetHealthboxSpriteVisible(gHealthboxSpriteIds[gActiveBattler ^ BIT_FLANK]); + SetBattlerShadowSpriteCallback(gActiveBattler ^ BIT_FLANK, GetMonData(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler ^ BIT_FLANK]], MON_DATA_SPECIES)); + } + DestroySprite(&gSprites[gUnknown_3004FFC[gActiveBattler]]); + UpdateHealthboxAttribute(gHealthboxSpriteIds[gActiveBattler], + &gEnemyParty[gBattlerPartyIndexes[gActiveBattler]], + HEALTHBOX_ALL); + sub_804BD94(gActiveBattler); + SetHealthboxSpriteVisible(gHealthboxSpriteIds[gActiveBattler]); + SetBattlerShadowSpriteCallback(gActiveBattler, GetMonData(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_SPECIES)); + gBattleSpritesDataPtr->animationData->field_9_x1 = 0; + gBattlerControllerFuncs[gActiveBattler] = sub_803A7E4; + } +} + +static void sub_803AC14(void) +{ + if (gSprites[gBattlerSpriteIds[gActiveBattler]].animEnded == TRUE + && gSprites[gBattlerSpriteIds[gActiveBattler]].pos2.x == 0) + { + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].flag_x80) + { + sub_80F1720(gActiveBattler, &gEnemyParty[gBattlerPartyIndexes[gActiveBattler]]); + } + else if (gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_1_x1) + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].flag_x80 = 0; + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_1_x1 = 0; + FreeSpriteTilesByTag(0x27F9); + FreeSpritePaletteByTag(0x27F9); + LinkOpponentBufferExecCompleted(); + } + } +} + +static void CompleteOnHealthbarDone(void) +{ + s16 hpValue = MoveBattleBar(gActiveBattler, gHealthboxSpriteIds[gActiveBattler], HEALTH_BAR, 0); + + SetHealthboxSpriteVisible(gHealthboxSpriteIds[gActiveBattler]); + if (hpValue != -1) + UpdateHpTextInHealthbox(gHealthboxSpriteIds[gActiveBattler], hpValue, HP_CURRENT); + else + LinkOpponentBufferExecCompleted(); +} + +static void sub_803AD20(void) +{ + if (!gSprites[gBattlerSpriteIds[gActiveBattler]].inUse) + { + SetHealthboxSpriteInvisible(gHealthboxSpriteIds[gActiveBattler]); + LinkOpponentBufferExecCompleted(); + } +} + +static void sub_803AD64(void) +{ + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].specialAnimActive) + { + FreeSpriteOamMatrix(&gSprites[gBattlerSpriteIds[gActiveBattler]]); + DestroySprite(&gSprites[gBattlerSpriteIds[gActiveBattler]]); + HideBattlerShadowSprite(gActiveBattler); + SetHealthboxSpriteInvisible(gHealthboxSpriteIds[gActiveBattler]); + LinkOpponentBufferExecCompleted(); + } +} + +static void CompleteOnInactiveTextPrinter(void) +{ + if (!IsTextPrinterActive(0)) + LinkOpponentBufferExecCompleted(); +} + +static void DoHitAnimBlinkSpriteEffect(void) +{ + u8 spriteId = gBattlerSpriteIds[gActiveBattler]; + + if (gSprites[spriteId].data[1] == 32) + { + gSprites[spriteId].data[1] = 0; + gSprites[spriteId].invisible = FALSE; + gDoingBattleAnim = FALSE; + LinkOpponentBufferExecCompleted(); + } + else + { + if (!(gSprites[spriteId].data[1] % 4)) + gSprites[spriteId].invisible ^= 1; + ++gSprites[spriteId].data[1]; + } +} + +static void sub_803AE6C(void) +{ + if (gSprites[gHealthboxSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy) + { + if (gBattleSpritesDataPtr->battlerData[gActiveBattler].behindSubstitute) + InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, gActiveBattler, B_ANIM_MON_TO_SUBSTITUTE); + gBattlerControllerFuncs[gActiveBattler] = sub_803AEDC; + } +} + +static void sub_803AEDC(void) +{ + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].specialAnimActive && !IsCryPlayingOrClearCrySongs()) + { + m4aMPlayVolumeControl(&gMPlayInfo_BGM, 0xFFFF, 0x100); + LinkOpponentBufferExecCompleted(); + } +} + +static void sub_803AF28(void) +{ + if (gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_1_x1) + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].flag_x80 = 0; + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_1_x1 = 0; + FreeSpriteTilesByTag(0x27F9); + FreeSpritePaletteByTag(0x27F9); + StartSpriteAnim(&gSprites[gBattlerSpriteIds[gActiveBattler]], 0); + UpdateHealthboxAttribute(gHealthboxSpriteIds[gActiveBattler], &gEnemyParty[gBattlerPartyIndexes[gActiveBattler]], HEALTHBOX_ALL); + sub_804BD94(gActiveBattler); + SetHealthboxSpriteVisible(gHealthboxSpriteIds[gActiveBattler]); + CopyBattleSpriteInvisibility(gActiveBattler); + gBattlerControllerFuncs[gActiveBattler] = sub_803AE6C; + } +} + +static void sub_803AFFC(void) +{ + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].ballAnimActive + && !gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].flag_x80) + sub_80F1720(gActiveBattler, &gEnemyParty[gBattlerPartyIndexes[gActiveBattler]]); + + if (gSprites[gUnknown_3004FFC[gActiveBattler]].callback == SpriteCallbackDummy + && !gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].ballAnimActive) + { + DestroySprite(&gSprites[gUnknown_3004FFC[gActiveBattler]]); + SetBattlerShadowSpriteCallback(gActiveBattler, GetMonData(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_SPECIES)); + gBattlerControllerFuncs[gActiveBattler] = sub_803AF28; + } +} + +static void CompleteOnFinishedStatusAnimation(void) +{ + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].statusAnimActive) + LinkOpponentBufferExecCompleted(); +} + +static void CompleteOnFinishedBattleAnimation(void) +{ + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animFromTableActive) + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentBufferExecCompleted(void) +{ + gBattlerControllerFuncs[gActiveBattler] = LinkOpponentBufferRunCommand; + if (gBattleTypeFlags & BATTLE_TYPE_LINK) + { + u8 playerId = GetMultiplayerId(); + + PrepareBufferDataTransferLink(2, 4, &playerId); + gBattleBufferA[gActiveBattler][0] = CONTROLLER_TERMINATOR_NOP; + } + else + { + gBattleControllerExecFlags &= ~gBitTable[gActiveBattler]; + } +} + +static void LinkOpponentHandleGetMonData(void) +{ + u8 monData[sizeof(struct Pokemon) * 2 + 56]; // this allows to get full data of two pokemon, trying to get more will result in overwriting data + u32 size = 0; + u8 monToCheck; + s32 i; + + if (!gBattleBufferA[gActiveBattler][2]) + { + size += CopyLinkOpponentMonData(gBattlerPartyIndexes[gActiveBattler], monData); + } + else + { + monToCheck = gBattleBufferA[gActiveBattler][2]; + for (i = 0; i < PARTY_SIZE; ++i) + { + if (monToCheck & 1) + size += CopyLinkOpponentMonData(i, monData + size); + monToCheck >>= 1; + } + } + BtlController_EmitDataTransfer(1, size, monData); + LinkOpponentBufferExecCompleted(); +} + +static u32 CopyLinkOpponentMonData(u8 monId, u8 *dst) +{ + struct BattlePokemon battleMon; + struct MovePpInfo moveData; + u8 nickname[20]; + u8 *src; + s16 data16; + u32 data32; + s32 size = 0; + + switch (gBattleBufferA[gActiveBattler][1]) + { + case REQUEST_ALL_BATTLE: + battleMon.species = GetMonData(&gEnemyParty[monId], MON_DATA_SPECIES); + battleMon.item = GetMonData(&gEnemyParty[monId], MON_DATA_HELD_ITEM); + for (size = 0; size < MAX_MON_MOVES; ++size) + { + battleMon.moves[size] = GetMonData(&gEnemyParty[monId], MON_DATA_MOVE1 + size); + battleMon.pp[size] = GetMonData(&gEnemyParty[monId], MON_DATA_PP1 + size); + } + battleMon.ppBonuses = GetMonData(&gEnemyParty[monId], MON_DATA_PP_BONUSES); + battleMon.friendship = GetMonData(&gEnemyParty[monId], MON_DATA_FRIENDSHIP); + battleMon.experience = GetMonData(&gEnemyParty[monId], MON_DATA_EXP); + battleMon.hpIV = GetMonData(&gEnemyParty[monId], MON_DATA_HP_IV); + battleMon.attackIV = GetMonData(&gEnemyParty[monId], MON_DATA_ATK_IV); + battleMon.defenseIV = GetMonData(&gEnemyParty[monId], MON_DATA_DEF_IV); + battleMon.speedIV = GetMonData(&gEnemyParty[monId], MON_DATA_SPEED_IV); + battleMon.spAttackIV = GetMonData(&gEnemyParty[monId], MON_DATA_SPATK_IV); + battleMon.spDefenseIV = GetMonData(&gEnemyParty[monId], MON_DATA_SPDEF_IV); + battleMon.personality = GetMonData(&gEnemyParty[monId], MON_DATA_PERSONALITY); + battleMon.status1 = GetMonData(&gEnemyParty[monId], MON_DATA_STATUS); + battleMon.level = GetMonData(&gEnemyParty[monId], MON_DATA_LEVEL); + battleMon.hp = GetMonData(&gEnemyParty[monId], MON_DATA_HP); + battleMon.maxHP = GetMonData(&gEnemyParty[monId], MON_DATA_MAX_HP); + battleMon.attack = GetMonData(&gEnemyParty[monId], MON_DATA_ATK); + battleMon.defense = GetMonData(&gEnemyParty[monId], MON_DATA_DEF); + battleMon.speed = GetMonData(&gEnemyParty[monId], MON_DATA_SPEED); + battleMon.spAttack = GetMonData(&gEnemyParty[monId], MON_DATA_SPATK); + battleMon.spDefense = GetMonData(&gEnemyParty[monId], MON_DATA_SPDEF); + battleMon.isEgg = GetMonData(&gEnemyParty[monId], MON_DATA_IS_EGG); + battleMon.abilityNum = GetMonData(&gEnemyParty[monId], MON_DATA_ABILITY_NUM); + battleMon.otId = GetMonData(&gEnemyParty[monId], MON_DATA_OT_ID); + GetMonData(&gEnemyParty[monId], MON_DATA_NICKNAME, nickname); + StringCopy10(battleMon.nickname, nickname); + GetMonData(&gEnemyParty[monId], MON_DATA_OT_NAME, battleMon.otName); + src = (u8 *)&battleMon; + for (size = 0; size < sizeof(battleMon); ++size) + dst[size] = src[size]; + break; + case REQUEST_SPECIES_BATTLE: + data16 = GetMonData(&gEnemyParty[monId], MON_DATA_SPECIES); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_HELDITEM_BATTLE: + data16 = GetMonData(&gEnemyParty[monId], MON_DATA_HELD_ITEM); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_MOVES_PP_BATTLE: + for (size = 0; size < MAX_MON_MOVES; ++size) + { + moveData.moves[size] = GetMonData(&gEnemyParty[monId], MON_DATA_MOVE1 + size); + moveData.pp[size] = GetMonData(&gEnemyParty[monId], MON_DATA_PP1 + size); + } + moveData.ppBonuses = GetMonData(&gEnemyParty[monId], MON_DATA_PP_BONUSES); + src = (u8 *)(&moveData); + for (size = 0; size < sizeof(moveData); ++size) + dst[size] = src[size]; + break; + case REQUEST_MOVE1_BATTLE: + case REQUEST_MOVE2_BATTLE: + case REQUEST_MOVE3_BATTLE: + case REQUEST_MOVE4_BATTLE: + data16 = GetMonData(&gEnemyParty[monId], MON_DATA_MOVE1 + gBattleBufferA[gActiveBattler][1] - REQUEST_MOVE1_BATTLE); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_PP_DATA_BATTLE: + for (size = 0; size < MAX_MON_MOVES; ++size) + dst[size] = GetMonData(&gEnemyParty[monId], MON_DATA_PP1 + size); + dst[size] = GetMonData(&gEnemyParty[monId], MON_DATA_PP_BONUSES); + ++size; + break; + case REQUEST_PPMOVE1_BATTLE: + case REQUEST_PPMOVE2_BATTLE: + case REQUEST_PPMOVE3_BATTLE: + case REQUEST_PPMOVE4_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_PP1 + gBattleBufferA[gActiveBattler][1] - REQUEST_PPMOVE1_BATTLE); + size = 1; + break; + case REQUEST_OTID_BATTLE: + data32 = GetMonData(&gEnemyParty[monId], MON_DATA_OT_ID); + dst[0] = (data32 & 0x000000FF); + dst[1] = (data32 & 0x0000FF00) >> 8; + dst[2] = (data32 & 0x00FF0000) >> 16; + size = 3; + break; + case REQUEST_EXP_BATTLE: + data32 = GetMonData(&gEnemyParty[monId], MON_DATA_EXP); + dst[0] = (data32 & 0x000000FF); + dst[1] = (data32 & 0x0000FF00) >> 8; + dst[2] = (data32 & 0x00FF0000) >> 16; + size = 3; + break; + case REQUEST_HP_EV_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_HP_EV); + size = 1; + break; + case REQUEST_ATK_EV_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_ATK_EV); + size = 1; + break; + case REQUEST_DEF_EV_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_DEF_EV); + size = 1; + break; + case REQUEST_SPEED_EV_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_SPEED_EV); + size = 1; + break; + case REQUEST_SPATK_EV_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_SPATK_EV); + size = 1; + break; + case REQUEST_SPDEF_EV_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_SPDEF_EV); + size = 1; + break; + case REQUEST_FRIENDSHIP_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_FRIENDSHIP); + size = 1; + break; + case REQUEST_POKERUS_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_POKERUS); + size = 1; + break; + case REQUEST_MET_LOCATION_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_MET_LOCATION); + size = 1; + break; + case REQUEST_MET_LEVEL_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_MET_LEVEL); + size = 1; + break; + case REQUEST_MET_GAME_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_MET_GAME); + size = 1; + break; + case REQUEST_POKEBALL_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_POKEBALL); + size = 1; + break; + case REQUEST_ALL_IVS_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_HP_IV); + dst[1] = GetMonData(&gEnemyParty[monId], MON_DATA_ATK_IV); + dst[2] = GetMonData(&gEnemyParty[monId], MON_DATA_DEF_IV); + dst[3] = GetMonData(&gEnemyParty[monId], MON_DATA_SPEED_IV); + dst[4] = GetMonData(&gEnemyParty[monId], MON_DATA_SPATK_IV); + dst[5] = GetMonData(&gEnemyParty[monId], MON_DATA_SPDEF_IV); + size = 6; + break; + case REQUEST_HP_IV_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_HP_IV); + size = 1; + break; + case REQUEST_ATK_IV_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_ATK_IV); + size = 1; + break; + case REQUEST_DEF_IV_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_DEF_IV); + size = 1; + break; + case REQUEST_SPEED_IV_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_SPEED_IV); + size = 1; + break; + case REQUEST_SPATK_IV_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_SPATK_IV); + size = 1; + break; + case REQUEST_SPDEF_IV_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_SPDEF_IV); + size = 1; + break; + case REQUEST_PERSONALITY_BATTLE: + data32 = GetMonData(&gEnemyParty[monId], MON_DATA_PERSONALITY); + dst[0] = (data32 & 0x000000FF); + dst[1] = (data32 & 0x0000FF00) >> 8; + dst[2] = (data32 & 0x00FF0000) >> 16; + dst[3] = (data32 & 0xFF000000) >> 24; + size = 4; + break; + case REQUEST_CHECKSUM_BATTLE: + data16 = GetMonData(&gEnemyParty[monId], MON_DATA_CHECKSUM); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_STATUS_BATTLE: + data32 = GetMonData(&gEnemyParty[monId], MON_DATA_STATUS); + dst[0] = (data32 & 0x000000FF); + dst[1] = (data32 & 0x0000FF00) >> 8; + dst[2] = (data32 & 0x00FF0000) >> 16; + dst[3] = (data32 & 0xFF000000) >> 24; + size = 4; + break; + case REQUEST_LEVEL_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_LEVEL); + size = 1; + break; + case REQUEST_HP_BATTLE: + data16 = GetMonData(&gEnemyParty[monId], MON_DATA_HP); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_MAX_HP_BATTLE: + data16 = GetMonData(&gEnemyParty[monId], MON_DATA_MAX_HP); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_ATK_BATTLE: + data16 = GetMonData(&gEnemyParty[monId], MON_DATA_ATK); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_DEF_BATTLE: + data16 = GetMonData(&gEnemyParty[monId], MON_DATA_DEF); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_SPEED_BATTLE: + data16 = GetMonData(&gEnemyParty[monId], MON_DATA_SPEED); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_SPATK_BATTLE: + data16 = GetMonData(&gEnemyParty[monId], MON_DATA_SPATK); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_SPDEF_BATTLE: + data16 = GetMonData(&gEnemyParty[monId], MON_DATA_SPDEF); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_COOL_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_COOL); + size = 1; + break; + case REQUEST_BEAUTY_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_BEAUTY); + size = 1; + break; + case REQUEST_CUTE_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_CUTE); + size = 1; + break; + case REQUEST_SMART_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_SMART); + size = 1; + break; + case REQUEST_TOUGH_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_TOUGH); + size = 1; + break; + case REQUEST_SHEEN_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_SHEEN); + size = 1; + break; + case REQUEST_COOL_RIBBON_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_COOL_RIBBON); + size = 1; + break; + case REQUEST_BEAUTY_RIBBON_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_BEAUTY_RIBBON); + size = 1; + break; + case REQUEST_CUTE_RIBBON_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_CUTE_RIBBON); + size = 1; + break; + case REQUEST_SMART_RIBBON_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_SMART_RIBBON); + size = 1; + break; + case REQUEST_TOUGH_RIBBON_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_TOUGH_RIBBON); + size = 1; + break; + } + return size; +} + +static void LinkOpponentHandleGetRawMonData(void) +{ + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentHandleSetMonData(void) +{ + u8 monToCheck; + u8 i; + + if (!gBattleBufferA[gActiveBattler][2]) + { + SetLinkOpponentMonData(gBattlerPartyIndexes[gActiveBattler]); + } + else + { + monToCheck = gBattleBufferA[gActiveBattler][2]; + for (i = 0; i < PARTY_SIZE; ++i) + { + if (monToCheck & 1) + SetLinkOpponentMonData(i); + monToCheck >>= 1; + } + } + LinkOpponentBufferExecCompleted(); +} + +static void SetLinkOpponentMonData(u8 monId) +{ + struct BattlePokemon *battlePokemon = (struct BattlePokemon *)&gBattleBufferA[gActiveBattler][3]; + struct MovePpInfo *moveData = (struct MovePpInfo *)&gBattleBufferA[gActiveBattler][3]; + s32 i; + + switch (gBattleBufferA[gActiveBattler][1]) + { + case REQUEST_ALL_BATTLE: + { + u8 iv; + + SetMonData(&gEnemyParty[monId], MON_DATA_SPECIES, &battlePokemon->species); + SetMonData(&gEnemyParty[monId], MON_DATA_HELD_ITEM, &battlePokemon->item); + for (i = 0; i < MAX_MON_MOVES; ++i) + { + SetMonData(&gEnemyParty[monId], MON_DATA_MOVE1 + i, &battlePokemon->moves[i]); + SetMonData(&gEnemyParty[monId], MON_DATA_PP1 + i, &battlePokemon->pp[i]); + } + SetMonData(&gEnemyParty[monId], MON_DATA_PP_BONUSES, &battlePokemon->ppBonuses); + SetMonData(&gEnemyParty[monId], MON_DATA_FRIENDSHIP, &battlePokemon->friendship); + SetMonData(&gEnemyParty[monId], MON_DATA_EXP, &battlePokemon->experience); + iv = battlePokemon->hpIV; + SetMonData(&gEnemyParty[monId], MON_DATA_HP_IV, &iv); + iv = battlePokemon->attackIV; + SetMonData(&gEnemyParty[monId], MON_DATA_ATK_IV, &iv); + iv = battlePokemon->defenseIV; + SetMonData(&gEnemyParty[monId], MON_DATA_DEF_IV, &iv); + iv = battlePokemon->speedIV; + SetMonData(&gEnemyParty[monId], MON_DATA_SPEED_IV, &iv); + iv = battlePokemon->spAttackIV; + SetMonData(&gEnemyParty[monId], MON_DATA_SPATK_IV, &iv); + iv = battlePokemon->spDefenseIV; + SetMonData(&gEnemyParty[monId], MON_DATA_SPDEF_IV, &iv); + SetMonData(&gEnemyParty[monId], MON_DATA_PERSONALITY, &battlePokemon->personality); + SetMonData(&gEnemyParty[monId], MON_DATA_STATUS, &battlePokemon->status1); + SetMonData(&gEnemyParty[monId], MON_DATA_LEVEL, &battlePokemon->level); + SetMonData(&gEnemyParty[monId], MON_DATA_HP, &battlePokemon->hp); + SetMonData(&gEnemyParty[monId], MON_DATA_MAX_HP, &battlePokemon->maxHP); + SetMonData(&gEnemyParty[monId], MON_DATA_ATK, &battlePokemon->attack); + SetMonData(&gEnemyParty[monId], MON_DATA_DEF, &battlePokemon->defense); + SetMonData(&gEnemyParty[monId], MON_DATA_SPEED, &battlePokemon->speed); + SetMonData(&gEnemyParty[monId], MON_DATA_SPATK, &battlePokemon->spAttack); + SetMonData(&gEnemyParty[monId], MON_DATA_SPDEF, &battlePokemon->spDefense); + } + break; + case REQUEST_SPECIES_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_SPECIES, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_HELDITEM_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_HELD_ITEM, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_MOVES_PP_BATTLE: + for (i = 0; i < MAX_MON_MOVES; ++i) + { + SetMonData(&gEnemyParty[monId], MON_DATA_MOVE1 + i, &moveData->moves[i]); + SetMonData(&gEnemyParty[monId], MON_DATA_PP1 + i, &moveData->pp[i]); + } + SetMonData(&gEnemyParty[monId], MON_DATA_PP_BONUSES, &moveData->ppBonuses); + break; + case REQUEST_MOVE1_BATTLE: + case REQUEST_MOVE2_BATTLE: + case REQUEST_MOVE3_BATTLE: + case REQUEST_MOVE4_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_MOVE1 + gBattleBufferA[gActiveBattler][1] - REQUEST_MOVE1_BATTLE, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_PP_DATA_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_PP1, &gBattleBufferA[gActiveBattler][3]); + SetMonData(&gEnemyParty[monId], MON_DATA_PP2, &gBattleBufferA[gActiveBattler][4]); + SetMonData(&gEnemyParty[monId], MON_DATA_PP3, &gBattleBufferA[gActiveBattler][5]); + SetMonData(&gEnemyParty[monId], MON_DATA_PP4, &gBattleBufferA[gActiveBattler][6]); + SetMonData(&gEnemyParty[monId], MON_DATA_PP_BONUSES, &gBattleBufferA[gActiveBattler][7]); + break; + case REQUEST_PPMOVE1_BATTLE: + case REQUEST_PPMOVE2_BATTLE: + case REQUEST_PPMOVE3_BATTLE: + case REQUEST_PPMOVE4_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_PP1 + gBattleBufferA[gActiveBattler][1] - REQUEST_PPMOVE1_BATTLE, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_OTID_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_OT_ID, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_EXP_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_EXP, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_HP_EV_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_HP_EV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_ATK_EV_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_ATK_EV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_DEF_EV_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_DEF_EV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SPEED_EV_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_SPEED_EV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SPATK_EV_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_SPATK_EV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SPDEF_EV_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_SPDEF_EV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_FRIENDSHIP_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_FRIENDSHIP, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_POKERUS_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_POKERUS, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_MET_LOCATION_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_MET_LOCATION, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_MET_LEVEL_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_MET_LEVEL, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_MET_GAME_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_MET_GAME, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_POKEBALL_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_POKEBALL, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_ALL_IVS_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_HP_IV, &gBattleBufferA[gActiveBattler][3]); + SetMonData(&gEnemyParty[monId], MON_DATA_ATK_IV, &gBattleBufferA[gActiveBattler][4]); + SetMonData(&gEnemyParty[monId], MON_DATA_DEF_IV, &gBattleBufferA[gActiveBattler][5]); + SetMonData(&gEnemyParty[monId], MON_DATA_SPEED_IV, &gBattleBufferA[gActiveBattler][6]); + SetMonData(&gEnemyParty[monId], MON_DATA_SPATK_IV, &gBattleBufferA[gActiveBattler][7]); + SetMonData(&gEnemyParty[monId], MON_DATA_SPDEF_IV, &gBattleBufferA[gActiveBattler][8]); + break; + case REQUEST_HP_IV_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_HP_IV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_ATK_IV_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_ATK_IV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_DEF_IV_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_DEF_IV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SPEED_IV_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_SPEED_IV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SPATK_IV_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_SPATK_IV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SPDEF_IV_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_SPDEF_IV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_PERSONALITY_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_PERSONALITY, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_CHECKSUM_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_CHECKSUM, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_STATUS_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_STATUS, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_LEVEL_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_LEVEL, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_HP_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_HP, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_MAX_HP_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_MAX_HP, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_ATK_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_ATK, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_DEF_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_DEF, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SPEED_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_SPEED, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SPATK_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_SPATK, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SPDEF_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_SPDEF, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_COOL_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_COOL, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_BEAUTY_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_BEAUTY, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_CUTE_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_CUTE, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SMART_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_SMART, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_TOUGH_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_TOUGH, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SHEEN_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_SHEEN, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_COOL_RIBBON_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_COOL_RIBBON, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_BEAUTY_RIBBON_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_BEAUTY_RIBBON, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_CUTE_RIBBON_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_CUTE_RIBBON, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SMART_RIBBON_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_SMART_RIBBON, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_TOUGH_RIBBON_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_TOUGH_RIBBON, &gBattleBufferA[gActiveBattler][3]); + break; + } +} + +static void LinkOpponentHandleSetRawMonData(void) +{ + u8 *dst = (u8 *)&gEnemyParty[gBattlerPartyIndexes[gActiveBattler]] + gBattleBufferA[gActiveBattler][1]; + u8 i; + + for (i = 0; i < gBattleBufferA[gActiveBattler][2]; ++i) + dst[i] = gBattleBufferA[gActiveBattler][3 + i]; + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentHandleLoadMonSprite(void) +{ + u16 species = GetMonData(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_SPECIES); + + BattleLoadOpponentMonSpriteGfx(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler]], gActiveBattler); + SetMultiuseSpriteTemplateToPokemon(species, GetBattlerPosition(gActiveBattler)); + gBattlerSpriteIds[gActiveBattler] = CreateSprite(&gMultiuseSpriteTemplate, + GetBattlerSpriteCoord(gActiveBattler, 2), + GetBattlerSpriteDefault_Y(gActiveBattler), + GetBattlerSpriteSubpriority(gActiveBattler)); + gSprites[gBattlerSpriteIds[gActiveBattler]].pos2.x = -240; + gSprites[gBattlerSpriteIds[gActiveBattler]].data[0] = gActiveBattler; + gSprites[gBattlerSpriteIds[gActiveBattler]].oam.paletteNum = gActiveBattler; + StartSpriteAnim(&gSprites[gBattlerSpriteIds[gActiveBattler]], gBattleMonForms[gActiveBattler]); + SetBattlerShadowSpriteCallback(gActiveBattler, GetMonData(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_SPECIES)); + gBattlerControllerFuncs[gActiveBattler] = sub_803AC14; +} + +static void LinkOpponentHandleSwitchInAnim(void) +{ + gBattlerPartyIndexes[gActiveBattler] = gBattleBufferA[gActiveBattler][1]; + sub_803C550(gActiveBattler, gBattleBufferA[gActiveBattler][2]); + gBattlerControllerFuncs[gActiveBattler] = sub_803AFFC; +} + +static void sub_803C550(u8 battlerId, bool8 dontClearSubstituteBit) +{ + u16 species; + + ClearTemporarySpeciesSpriteData(battlerId, dontClearSubstituteBit); + gBattlerPartyIndexes[battlerId] = gBattleBufferA[battlerId][1]; + species = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerId]], MON_DATA_SPECIES); + gUnknown_3004FFC[battlerId] = CreateInvisibleSpriteWithCallback(sub_8033E3C); + BattleLoadOpponentMonSpriteGfx(&gEnemyParty[gBattlerPartyIndexes[battlerId]], battlerId); + SetMultiuseSpriteTemplateToPokemon(species, GetBattlerPosition(battlerId)); + gBattlerSpriteIds[battlerId] = CreateSprite(&gMultiuseSpriteTemplate, + GetBattlerSpriteCoord(battlerId, 2), + GetBattlerSpriteDefault_Y(battlerId), + GetBattlerSpriteSubpriority(battlerId)); + gSprites[gUnknown_3004FFC[battlerId]].data[1] = gBattlerSpriteIds[battlerId]; + gSprites[gBattlerSpriteIds[battlerId]].data[0] = battlerId; + gSprites[gBattlerSpriteIds[battlerId]].data[2] = species; + gSprites[gBattlerSpriteIds[battlerId]].oam.paletteNum = battlerId; + StartSpriteAnim(&gSprites[gBattlerSpriteIds[battlerId]], gBattleMonForms[battlerId]); + gSprites[gBattlerSpriteIds[battlerId]].invisible = TRUE; + gSprites[gBattlerSpriteIds[battlerId]].callback = SpriteCallbackDummy; + gSprites[gUnknown_3004FFC[battlerId]].data[0] = DoPokeballSendOutAnimation(0, POKEBALL_OPPONENT_SENDOUT); +} + +static void LinkOpponentHandleReturnMonToBall(void) +{ + if (!gBattleBufferA[gActiveBattler][1]) + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 0; + gBattlerControllerFuncs[gActiveBattler] = DoSwitchOutAnimation; + } + else + { + FreeSpriteOamMatrix(&gSprites[gBattlerSpriteIds[gActiveBattler]]); + DestroySprite(&gSprites[gBattlerSpriteIds[gActiveBattler]]); + HideBattlerShadowSprite(gActiveBattler); + SetHealthboxSpriteInvisible(gHealthboxSpriteIds[gActiveBattler]); + LinkOpponentBufferExecCompleted(); + } +} + +static void DoSwitchOutAnimation(void) +{ + switch (gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState) + { + case 0: + if (gBattleSpritesDataPtr->battlerData[gActiveBattler].behindSubstitute) + InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, gActiveBattler, B_ANIM_SUBSTITUTE_TO_MON); + + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 1; + break; + case 1: + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].specialAnimActive) + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 0; + InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, gActiveBattler, B_ANIM_SWITCH_OUT_OPPONENT_MON); + gBattlerControllerFuncs[gActiveBattler] = sub_803AD64; + } + break; + } +} + +static void LinkOpponentHandleDrawTrainerPic(void) +{ + s16 xPos; + u32 trainerPicId; + + if (gBattleTypeFlags & BATTLE_TYPE_MULTI) + { + if (GetBattlerPosition(gActiveBattler) & BIT_FLANK) // second mon + xPos = 152; + else // first mon + xPos = 200; + if ((gLinkPlayers[GetBattlerMultiplayerId(gActiveBattler)].version & 0xFF) == VERSION_RUBY + || (gLinkPlayers[GetBattlerMultiplayerId(gActiveBattler)].version & 0xFF) == VERSION_SAPPHIRE + || (gLinkPlayers[GetBattlerMultiplayerId(gActiveBattler)].version & 0xFF) == VERSION_EMERALD) + { + if (gLinkPlayers[GetBattlerMultiplayerId(gActiveBattler)].gender != MALE) + trainerPicId = gFacilityClassToPicIndex[FACILITY_CLASS_PKMN_TRAINER_5]; + else + trainerPicId = gFacilityClassToPicIndex[FACILITY_CLASS_PKMN_TRAINER_2]; + } + else if (gLinkPlayers[GetBattlerMultiplayerId(gActiveBattler)].gender != MALE) + { + trainerPicId = gFacilityClassToPicIndex[FACILITY_CLASS_PLAYER_4]; + } + else + { + trainerPicId = gFacilityClassToPicIndex[FACILITY_CLASS_PLAYER_3]; + } + } + else + { + xPos = 176; + if (gTrainerBattleOpponent_A == TRAINER_OPPONENT_C00) + { + trainerPicId = sub_80447AC(); + } + else if ((gLinkPlayers[GetMultiplayerId() ^ BIT_SIDE].version & 0xFF) == VERSION_RUBY + || (gLinkPlayers[GetMultiplayerId() ^ BIT_SIDE].version & 0xFF) == VERSION_SAPPHIRE + || (gLinkPlayers[GetMultiplayerId() ^ BIT_SIDE].version & 0xFF) == VERSION_EMERALD) + { + if (gLinkPlayers[GetMultiplayerId() ^ BIT_SIDE].gender != MALE) + trainerPicId = gFacilityClassToPicIndex[FACILITY_CLASS_PKMN_TRAINER_5]; + else + trainerPicId = gFacilityClassToPicIndex[FACILITY_CLASS_PKMN_TRAINER_2]; + } + else if (gLinkPlayers[GetMultiplayerId() ^ BIT_SIDE].gender != MALE) + { + trainerPicId = gFacilityClassToPicIndex[FACILITY_CLASS_PLAYER_4]; + } + else + { + trainerPicId = gFacilityClassToPicIndex[FACILITY_CLASS_PLAYER_3]; + } + } + DecompressTrainerFrontPic(trainerPicId, gActiveBattler); + SetMultiuseSpriteTemplateToTrainerBack(trainerPicId, GetBattlerPosition(gActiveBattler)); + gBattlerSpriteIds[gActiveBattler] = CreateSprite(&gMultiuseSpriteTemplate, + xPos, + (8 - gTrainerFrontPicCoords[trainerPicId].size) * 4 + 40, + GetBattlerSpriteSubpriority(gActiveBattler)); + gSprites[gBattlerSpriteIds[gActiveBattler]].pos2.x = -240; + gSprites[gBattlerSpriteIds[gActiveBattler]].data[0] = 2; + gSprites[gBattlerSpriteIds[gActiveBattler]].oam.paletteNum = IndexOfSpritePaletteTag(gTrainerFrontPicPaletteTable[trainerPicId].tag); + gSprites[gBattlerSpriteIds[gActiveBattler]].data[5] = gSprites[gBattlerSpriteIds[gActiveBattler]].oam.tileNum; + gSprites[gBattlerSpriteIds[gActiveBattler]].oam.tileNum = GetSpriteTileStartByTag(gTrainerFrontPicTable[trainerPicId].tag); + gSprites[gBattlerSpriteIds[gActiveBattler]].oam.affineParam = trainerPicId; + gSprites[gBattlerSpriteIds[gActiveBattler]].callback = sub_8033EEC; + gBattlerControllerFuncs[gActiveBattler] = CompleteOnBattlerSpriteCallbackDummy; +} + +static void LinkOpponentHandleTrainerSlide(void) +{ + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentHandleTrainerSlideBack(void) +{ + SetSpritePrimaryCoordsFromSecondaryCoords(&gSprites[gBattlerSpriteIds[gActiveBattler]]); + gSprites[gBattlerSpriteIds[gActiveBattler]].data[0] = 35; + gSprites[gBattlerSpriteIds[gActiveBattler]].data[2] = 280; + gSprites[gBattlerSpriteIds[gActiveBattler]].data[4] = gSprites[gBattlerSpriteIds[gActiveBattler]].pos1.y; + gSprites[gBattlerSpriteIds[gActiveBattler]].callback = StartAnimLinearTranslation; + StoreSpriteCallbackInData6(&gSprites[gBattlerSpriteIds[gActiveBattler]], SpriteCallbackDummy); + gBattlerControllerFuncs[gActiveBattler] = sub_803A70C; +} + +static void LinkOpponentHandleFaintAnimation(void) +{ + if (gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState == 0) + { + if (gBattleSpritesDataPtr->battlerData[gActiveBattler].behindSubstitute) + InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, gActiveBattler, B_ANIM_SUBSTITUTE_TO_MON); + ++gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState; + } + else + { + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].specialAnimActive) + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 0; + PlaySE12WithPanning(SE_POKE_DEAD, SOUND_PAN_TARGET); + gSprites[gBattlerSpriteIds[gActiveBattler]].callback = SpriteCB_FaintOpponentMon; + gBattlerControllerFuncs[gActiveBattler] = sub_803AD20; + } + } +} + +static void LinkOpponentHandlePaletteFade(void) +{ + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentHandleSuccessBallThrowAnim(void) +{ + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentHandleBallThrowAnim(void) +{ + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentHandlePause(void) +{ + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentHandleMoveAnimation(void) +{ + if (!mplay_80342A4(gActiveBattler)) + { + u16 move = gBattleBufferA[gActiveBattler][1] | (gBattleBufferA[gActiveBattler][2] << 8); + + gAnimMoveTurn = gBattleBufferA[gActiveBattler][3]; + gAnimMovePower = gBattleBufferA[gActiveBattler][4] | (gBattleBufferA[gActiveBattler][5] << 8); + gAnimMoveDmg = gBattleBufferA[gActiveBattler][6] | (gBattleBufferA[gActiveBattler][7] << 8) | (gBattleBufferA[gActiveBattler][8] << 16) | (gBattleBufferA[gActiveBattler][9] << 24); + gAnimFriendship = gBattleBufferA[gActiveBattler][10]; + gWeatherMoveAnim = gBattleBufferA[gActiveBattler][12] | (gBattleBufferA[gActiveBattler][13] << 8); + gAnimDisableStructPtr = (struct DisableStruct *)&gBattleBufferA[gActiveBattler][16]; + gTransformedPersonalities[gActiveBattler] = gAnimDisableStructPtr->transformedMonPersonality; + if (IsMoveWithoutAnimation(move, gAnimMoveTurn)) // always returns FALSE + { + LinkOpponentBufferExecCompleted(); + } + else + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 0; + gBattlerControllerFuncs[gActiveBattler] = LinkOpponentDoMoveAnimation; + } + } +} + +static void LinkOpponentDoMoveAnimation(void) +{ + u16 move = gBattleBufferA[gActiveBattler][1] | (gBattleBufferA[gActiveBattler][2] << 8); + u8 multihit = gBattleBufferA[gActiveBattler][11]; + + switch (gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState) + { + case 0: + if (gBattleSpritesDataPtr->battlerData[gActiveBattler].behindSubstitute + && !gBattleSpritesDataPtr->battlerData[gActiveBattler].flag_x8) + { + gBattleSpritesDataPtr->battlerData[gActiveBattler].flag_x8 = 1; + InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, gActiveBattler, B_ANIM_SUBSTITUTE_TO_MON); + } + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 1; + break; + case 1: + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].specialAnimActive) + { + sub_8035450(0); + DoMoveAnim(move); + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 2; + } + break; + case 2: + gAnimScriptCallback(); + if (!gAnimScriptActive) + { + sub_8035450(1); + if (gBattleSpritesDataPtr->battlerData[gActiveBattler].behindSubstitute && multihit < 2) + { + InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, gActiveBattler, B_ANIM_MON_TO_SUBSTITUTE); + gBattleSpritesDataPtr->battlerData[gActiveBattler].flag_x8 = 0; + } + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 3; + } + break; + case 3: + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].specialAnimActive) + { + CopyAllBattleSpritesInvisibilities(); + TrySetBehindSubstituteSpriteBit(gActiveBattler, gBattleBufferA[gActiveBattler][1] | (gBattleBufferA[gActiveBattler][2] << 8)); + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 0; + LinkOpponentBufferExecCompleted(); + } + break; + } +} + +static void LinkOpponentHandlePrintString(void) +{ + u16 *stringId; + + gBattle_BG0_X = 0; + gBattle_BG0_Y = 0; + stringId = (u16 *)(&gBattleBufferA[gActiveBattler][2]); + BufferStringBattle(*stringId); + if (sub_80D89B0(*stringId)) + BattlePutTextOnWindow(gDisplayedStringBattle, 0x40); + else + BattlePutTextOnWindow(gDisplayedStringBattle, 0); + gBattlerControllerFuncs[gActiveBattler] = CompleteOnInactiveTextPrinter; +} + +static void LinkOpponentHandlePrintSelectionString(void) +{ + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentHandleChooseAction(void) +{ + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentHandleUnknownYesNoBox(void) +{ + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentHandleChooseMove(void) +{ + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentHandleChooseItem(void) +{ + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentHandleChoosePokemon(void) +{ + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentHandleCmd23(void) +{ + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentHandleHealthBarUpdate(void) +{ + s16 hpVal; + + LoadBattleBarGfx(0); + hpVal = gBattleBufferA[gActiveBattler][2] | (gBattleBufferA[gActiveBattler][3] << 8); + if (hpVal != INSTANT_HP_BAR_DROP) + { + u32 maxHP = GetMonData(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_MAX_HP); + u32 curHP = GetMonData(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_HP); + + SetBattleBarStruct(gActiveBattler, gHealthboxSpriteIds[gActiveBattler], maxHP, curHP, hpVal); + } + else + { + u32 maxHP = GetMonData(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_MAX_HP); + + SetBattleBarStruct(gActiveBattler, gHealthboxSpriteIds[gActiveBattler], maxHP, 0, hpVal); + } + gBattlerControllerFuncs[gActiveBattler] = CompleteOnHealthbarDone; +} + +static void LinkOpponentHandleExpUpdate(void) +{ + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentHandleStatusIconUpdate(void) +{ + if (!mplay_80342A4(gActiveBattler)) + { + u8 battlerId; + + UpdateHealthboxAttribute(gHealthboxSpriteIds[gActiveBattler], &gEnemyParty[gBattlerPartyIndexes[gActiveBattler]], HEALTHBOX_STATUS_ICON); + battlerId = gActiveBattler; + gBattleSpritesDataPtr->healthBoxesData[battlerId].statusAnimActive = 0; + gBattlerControllerFuncs[gActiveBattler] = CompleteOnFinishedStatusAnimation; + } +} + +static void LinkOpponentHandleStatusAnimation(void) +{ + if (!mplay_80342A4(gActiveBattler)) + { + InitAndLaunchChosenStatusAnimation(gBattleBufferA[gActiveBattler][1], + gBattleBufferA[gActiveBattler][2] | (gBattleBufferA[gActiveBattler][3] << 8) | (gBattleBufferA[gActiveBattler][4] << 16) | (gBattleBufferA[gActiveBattler][5] << 24)); + gBattlerControllerFuncs[gActiveBattler] = CompleteOnFinishedStatusAnimation; + } +} + +static void LinkOpponentHandleStatusXor(void) +{ + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentHandleDataTransfer(void) +{ + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentHandleDMA3Transfer(void) +{ + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentHandlePlayBGM(void) +{ + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentHandleCmd32(void) +{ + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentHandleTwoReturnValues(void) +{ + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentHandleChosenMonReturnValue(void) +{ + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentHandleOneReturnValue(void) +{ + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentHandleOneReturnValue_Duplicate(void) +{ + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentHandleCmd37(void) +{ + gUnknown_2022870.field_0 = 0; + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentHandleCmd38(void) +{ + gUnknown_2022870.field_0 = gBattleBufferA[gActiveBattler][1]; + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentHandleCmd39(void) +{ + gUnknown_2022870.flag_x80 = 0; + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentHandleCmd40(void) +{ + gUnknown_2022870.flag_x80 ^= 1; + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentHandleHitAnimation(void) +{ + if (gSprites[gBattlerSpriteIds[gActiveBattler]].invisible == TRUE) + { + LinkOpponentBufferExecCompleted(); + } + else + { + gDoingBattleAnim = TRUE; + gSprites[gBattlerSpriteIds[gActiveBattler]].data[1] = 0; + DoHitAnimHealthboxEffect(gActiveBattler); + gBattlerControllerFuncs[gActiveBattler] = DoHitAnimBlinkSpriteEffect; + } +} + +static void LinkOpponentHandleCmd42(void) +{ + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentHandlePlaySE(void) +{ + s8 pan; + + if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER) + pan = SOUND_PAN_ATTACKER; + else + pan = SOUND_PAN_TARGET; + PlaySE12WithPanning(gBattleBufferA[gActiveBattler][1] | (gBattleBufferA[gActiveBattler][2] << 8), pan); + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentHandlePlayFanfare(void) +{ + PlayFanfare(gBattleBufferA[gActiveBattler][1] | (gBattleBufferA[gActiveBattler][2] << 8)); + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentHandleFaintingCry(void) +{ + u16 species = GetMonData(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_SPECIES); + + PlayCry3(species, 25, 5); + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentHandleIntroSlide(void) +{ + HandleIntroSlide(gBattleBufferA[gActiveBattler][1]); + gIntroSlideFlags |= 1; + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentHandleIntroTrainerBallThrow(void) +{ + u8 paletteNum, taskId; + + SetSpritePrimaryCoordsFromSecondaryCoords(&gSprites[gBattlerSpriteIds[gActiveBattler]]); + gSprites[gBattlerSpriteIds[gActiveBattler]].data[0] = 35; + gSprites[gBattlerSpriteIds[gActiveBattler]].data[2] = 280; + gSprites[gBattlerSpriteIds[gActiveBattler]].data[4] = gSprites[gBattlerSpriteIds[gActiveBattler]].pos1.y; + gSprites[gBattlerSpriteIds[gActiveBattler]].callback = StartAnimLinearTranslation; + StoreSpriteCallbackInData6(&gSprites[gBattlerSpriteIds[gActiveBattler]], sub_803D648); + taskId = CreateTask(sub_803D564, 5); + gTasks[taskId].data[0] = gActiveBattler; + if (gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].partyStatusSummaryShown) + gTasks[gBattlerStatusSummaryTaskId[gActiveBattler]].func = Task_HidePartyStatusSummary; + gBattleSpritesDataPtr->animationData->field_9_x1 = 1; + gBattlerControllerFuncs[gActiveBattler] = nullsub_19; +} + +static void sub_803D564(u8 taskId) +{ + u8 savedActiveBank = gActiveBattler; + + gActiveBattler = gTasks[taskId].data[0]; + if (!IsDoubleBattle() || (gBattleTypeFlags & BATTLE_TYPE_MULTI)) + { + gBattleBufferA[gActiveBattler][1] = gBattlerPartyIndexes[gActiveBattler]; + sub_803C550(gActiveBattler, FALSE); + } + else + { + gBattleBufferA[gActiveBattler][1] = gBattlerPartyIndexes[gActiveBattler]; + sub_803C550(gActiveBattler, FALSE); + gActiveBattler = BATTLE_PARTNER(gActiveBattler); + gBattleBufferA[gActiveBattler][1] = gBattlerPartyIndexes[gActiveBattler]; + sub_803C550(gActiveBattler, FALSE); + gActiveBattler = BATTLE_PARTNER(gActiveBattler); + } + gBattlerControllerFuncs[gActiveBattler] = sub_803A9CC; + gActiveBattler = savedActiveBank; + DestroyTask(taskId); +} + +static void sub_803D648(struct Sprite *sprite) +{ + FreeTrainerFrontPicPaletteAndTile(sprite->oam.affineParam); + sprite->oam.tileNum = sprite->data[5]; + FreeSpriteOamMatrix(sprite); + DestroySprite(sprite); +} + +static void LinkOpponentHandleDrawPartyStatusSummary(void) +{ + if (gBattleBufferA[gActiveBattler][1] && GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER) + { + LinkOpponentBufferExecCompleted(); + } + else + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].partyStatusSummaryShown = 1; + + if (gBattleBufferA[gActiveBattler][2]) + { + if (gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_1_x1E < 2) + { + ++gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_1_x1E; + return; + } + else + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_1_x1E = 0; + } + } + gBattlerStatusSummaryTaskId[gActiveBattler] = CreatePartyStatusSummarySprites(gActiveBattler, (struct HpAndStatus *)&gBattleBufferA[gActiveBattler][4], gBattleBufferA[gActiveBattler][1], gBattleBufferA[gActiveBattler][2]); + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_5 = 0; + if (gBattleBufferA[gActiveBattler][2]) + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_5 = 0x5D; + gBattlerControllerFuncs[gActiveBattler] = sub_803D790; + } +} + +static void sub_803D790(void) +{ + if (gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_5++ > 0x5C) + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_5 = 0; + LinkOpponentBufferExecCompleted(); + } +} + +static void LinkOpponentHandleHidePartyStatusSummary(void) +{ + if (gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].partyStatusSummaryShown) + gTasks[gBattlerStatusSummaryTaskId[gActiveBattler]].func = Task_HidePartyStatusSummary; + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentHandleEndBounceEffect(void) +{ + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentHandleSpriteInvisibility(void) +{ + if (IsBattlerSpritePresent(gActiveBattler)) + { + gSprites[gBattlerSpriteIds[gActiveBattler]].invisible = gBattleBufferA[gActiveBattler][1]; + CopyBattleSpriteInvisibility(gActiveBattler); + } + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentHandleBattleAnimation(void) +{ + if (!mplay_80342A4(gActiveBattler)) + { + u8 animationId = gBattleBufferA[gActiveBattler][1]; + u16 argument = gBattleBufferA[gActiveBattler][2] | (gBattleBufferA[gActiveBattler][3] << 8); + + if (TryHandleLaunchBattleTableAnimation(gActiveBattler, gActiveBattler, gActiveBattler, animationId, argument)) + LinkOpponentBufferExecCompleted(); + else + gBattlerControllerFuncs[gActiveBattler] = CompleteOnFinishedBattleAnimation; + } +} + +static void LinkOpponentHandleLinkStandbyMsg(void) +{ + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentHandleResetActionMoveSelection(void) +{ + LinkOpponentBufferExecCompleted(); +} + +static void LinkOpponentHandleCmd55(void) +{ + if (gBattleBufferA[gActiveBattler][1] == B_OUTCOME_DREW) + gBattleOutcome = gBattleBufferA[gActiveBattler][1]; + else + gBattleOutcome = gBattleBufferA[gActiveBattler][1] ^ B_OUTCOME_DREW; + FadeOutMapMusic(5); + BeginFastPaletteFade(3); + LinkOpponentBufferExecCompleted(); + gBattlerControllerFuncs[gActiveBattler] = sub_802F6A8; +} + +static void LinkOpponentCmdEnd(void) +{ +} diff --git a/src/battle_controller_link_partner.c b/src/battle_controller_link_partner.c new file mode 100644 index 000000000..96130f049 --- /dev/null +++ b/src/battle_controller_link_partner.c @@ -0,0 +1,1599 @@ +#include "global.h" +#include "bg.h" +#include "data.h" +#include "link.h" +#include "main.h" +#include "m4a.h" +#include "palette.h" +#include "pokeball.h" +#include "pokemon.h" +#include "sound.h" +#include "string_util.h" +#include "task.h" +#include "text.h" +#include "util.h" +#include "window.h" +#include "battle.h" +#include "battle_ai_script_commands.h" +#include "battle_anim.h" +#include "battle_controllers.h" +#include "battle_interface.h" +#include "battle_message.h" +#include "battle_setup.h" +#include "battle_tower.h" +#include "reshow_battle_screen.h" +#include "constants/battle_anim.h" +#include "constants/songs.h" + +static void LinkPartnerHandleGetMonData(void); +static void LinkPartnerHandleGetRawMonData(void); +static void LinkPartnerHandleSetMonData(void); +static void LinkPartnerHandleSetRawMonData(void); +static void LinkPartnerHandleLoadMonSprite(void); +static void LinkPartnerHandleSwitchInAnim(void); +static void LinkPartnerHandleReturnMonToBall(void); +static void LinkPartnerHandleDrawTrainerPic(void); +static void LinkPartnerHandleTrainerSlide(void); +static void LinkPartnerHandleTrainerSlideBack(void); +static void LinkPartnerHandleFaintAnimation(void); +static void LinkPartnerHandlePaletteFade(void); +static void LinkPartnerHandleSuccessBallThrowAnim(void); +static void LinkPartnerHandleBallThrowAnim(void); +static void LinkPartnerHandlePause(void); +static void LinkPartnerHandleMoveAnimation(void); +static void LinkPartnerHandlePrintString(void); +static void LinkPartnerHandlePrintSelectionString(void); +static void LinkPartnerHandleChooseAction(void); +static void LinkPartnerHandleUnknownYesNoBox(void); +static void LinkPartnerHandleChooseMove(void); +static void LinkPartnerHandleChooseItem(void); +static void LinkPartnerHandleChoosePokemon(void); +static void LinkPartnerHandleCmd23(void); +static void LinkPartnerHandleHealthBarUpdate(void); +static void LinkPartnerHandleExpUpdate(void); +static void LinkPartnerHandleStatusIconUpdate(void); +static void LinkPartnerHandleStatusAnimation(void); +static void LinkPartnerHandleStatusXor(void); +static void LinkPartnerHandleDataTransfer(void); +static void LinkPartnerHandleDMA3Transfer(void); +static void LinkPartnerHandlePlayBGM(void); +static void LinkPartnerHandleCmd32(void); +static void LinkPartnerHandleTwoReturnValues(void); +static void LinkPartnerHandleChosenMonReturnValue(void); +static void LinkPartnerHandleOneReturnValue(void); +static void LinkPartnerHandleOneReturnValue_Duplicate(void); +static void LinkPartnerHandleCmd37(void); +static void LinkPartnerHandleCmd38(void); +static void LinkPartnerHandleCmd39(void); +static void LinkPartnerHandleCmd40(void); +static void LinkPartnerHandleHitAnimation(void); +static void LinkPartnerHandleCmd42(void); +static void LinkPartnerHandlePlaySE(void); +static void LinkPartnerHandlePlayFanfare(void); +static void LinkPartnerHandleFaintingCry(void); +static void LinkPartnerHandleIntroSlide(void); +static void LinkPartnerHandleIntroTrainerBallThrow(void); +static void LinkPartnerHandleDrawPartyStatusSummary(void); +static void LinkPartnerHandleHidePartyStatusSummary(void); +static void LinkPartnerHandleEndBounceEffect(void); +static void LinkPartnerHandleSpriteInvisibility(void); +static void LinkPartnerHandleBattleAnimation(void); +static void LinkPartnerHandleLinkStandbyMsg(void); +static void LinkPartnerHandleResetActionMoveSelection(void); +static void LinkPartnerHandleCmd55(void); +static void LinkPartnerCmdEnd(void); + +static void LinkPartnerBufferRunCommand(void); +static void LinkPartnerBufferExecCompleted(void); +static void sub_80D481C(void); +static u32 CopyLinkPartnerMonData(u8 monId, u8 *dst); +static void SetLinkPartnerMonData(u8 monId); +static void sub_80D5F40(u8 battlerId, bool8 dontClearSubstituteBit); +static void DoSwitchOutAnimation(void); +static void LinkPartnerDoMoveAnimation(void); +static void sub_80D6ED0(u8 taskId); +static void sub_80D70A0(void); + +static void (*const sLinkPartnerBufferCommands[CONTROLLER_CMDS_COUNT])(void) = +{ + LinkPartnerHandleGetMonData, + LinkPartnerHandleGetRawMonData, + LinkPartnerHandleSetMonData, + LinkPartnerHandleSetRawMonData, + LinkPartnerHandleLoadMonSprite, + LinkPartnerHandleSwitchInAnim, + LinkPartnerHandleReturnMonToBall, + LinkPartnerHandleDrawTrainerPic, + LinkPartnerHandleTrainerSlide, + LinkPartnerHandleTrainerSlideBack, + LinkPartnerHandleFaintAnimation, + LinkPartnerHandlePaletteFade, + LinkPartnerHandleSuccessBallThrowAnim, + LinkPartnerHandleBallThrowAnim, + LinkPartnerHandlePause, + LinkPartnerHandleMoveAnimation, + LinkPartnerHandlePrintString, + LinkPartnerHandlePrintSelectionString, + LinkPartnerHandleChooseAction, + LinkPartnerHandleUnknownYesNoBox, + LinkPartnerHandleChooseMove, + LinkPartnerHandleChooseItem, + LinkPartnerHandleChoosePokemon, + LinkPartnerHandleCmd23, + LinkPartnerHandleHealthBarUpdate, + LinkPartnerHandleExpUpdate, + LinkPartnerHandleStatusIconUpdate, + LinkPartnerHandleStatusAnimation, + LinkPartnerHandleStatusXor, + LinkPartnerHandleDataTransfer, + LinkPartnerHandleDMA3Transfer, + LinkPartnerHandlePlayBGM, + LinkPartnerHandleCmd32, + LinkPartnerHandleTwoReturnValues, + LinkPartnerHandleChosenMonReturnValue, + LinkPartnerHandleOneReturnValue, + LinkPartnerHandleOneReturnValue_Duplicate, + LinkPartnerHandleCmd37, + LinkPartnerHandleCmd38, + LinkPartnerHandleCmd39, + LinkPartnerHandleCmd40, + LinkPartnerHandleHitAnimation, + LinkPartnerHandleCmd42, + LinkPartnerHandlePlaySE, + LinkPartnerHandlePlayFanfare, + LinkPartnerHandleFaintingCry, + LinkPartnerHandleIntroSlide, + LinkPartnerHandleIntroTrainerBallThrow, + LinkPartnerHandleDrawPartyStatusSummary, + LinkPartnerHandleHidePartyStatusSummary, + LinkPartnerHandleEndBounceEffect, + LinkPartnerHandleSpriteInvisibility, + LinkPartnerHandleBattleAnimation, + LinkPartnerHandleLinkStandbyMsg, + LinkPartnerHandleResetActionMoveSelection, + LinkPartnerHandleCmd55, + LinkPartnerCmdEnd +}; + +static void nullsub_77(void) +{ +} + +void SetControllerToLinkPartner(void) +{ + gBattlerControllerFuncs[gActiveBattler] = LinkPartnerBufferRunCommand; +} + +static void LinkPartnerBufferRunCommand(void) +{ + if (gBattleControllerExecFlags & gBitTable[gActiveBattler]) + { + if (gBattleBufferA[gActiveBattler][0] < NELEMS(sLinkPartnerBufferCommands)) + sLinkPartnerBufferCommands[gBattleBufferA[gActiveBattler][0]](); + else + LinkPartnerBufferExecCompleted(); + } +} + +static void CompleteOnBattlerSpriteCallbackDummy(void) +{ + if (gSprites[gBattlerSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy) + LinkPartnerBufferExecCompleted(); +} + +static void sub_80D42A8(void) +{ + if (gSprites[gBattlerSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy) + { + nullsub_16(0); + FreeSpriteOamMatrix(&gSprites[gBattlerSpriteIds[gActiveBattler]]); + DestroySprite(&gSprites[gBattlerSpriteIds[gActiveBattler]]); + LinkPartnerBufferExecCompleted(); + } +} + +static void sub_80D4310(void) +{ + if (--gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_9 == 0xFF) + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_9 = 0; + LinkPartnerBufferExecCompleted(); + } +} + +static void sub_80D4358(void) +{ + bool32 var = FALSE; + + if (!IsDoubleBattle() || (IsDoubleBattle() && (gBattleTypeFlags & BATTLE_TYPE_MULTI))) + { + if (gSprites[gHealthboxSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy) + var = TRUE; + } + else if (gSprites[gHealthboxSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy + && gSprites[gHealthboxSpriteIds[gActiveBattler ^ BIT_FLANK]].callback == SpriteCallbackDummy) + { + var = TRUE; + } + if (IsCryPlayingOrClearCrySongs()) + var = FALSE; + if (var) + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_9 = 3; + gBattlerControllerFuncs[gActiveBattler] = sub_80D4310; + } +} + +static void sub_80D443C(void) +{ + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].ballAnimActive + && !gBattleSpritesDataPtr->healthBoxesData[gActiveBattler ^ BIT_FLANK].ballAnimActive) + { + if (++gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_9 != 1) + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_9 = 0; + if (IsDoubleBattle() && !(gBattleTypeFlags & BATTLE_TYPE_MULTI)) + { + DestroySprite(&gSprites[gUnknown_3004FFC[gActiveBattler ^ BIT_FLANK]]); + UpdateHealthboxAttribute(gHealthboxSpriteIds[gActiveBattler ^ BIT_FLANK], + &gPlayerParty[gBattlerPartyIndexes[gActiveBattler ^ BIT_FLANK]], + HEALTHBOX_ALL); + sub_804BD94(gActiveBattler ^ BIT_FLANK); + SetHealthboxSpriteVisible(gHealthboxSpriteIds[gActiveBattler ^ BIT_FLANK]); + } + DestroySprite(&gSprites[gUnknown_3004FFC[gActiveBattler]]); + UpdateHealthboxAttribute(gHealthboxSpriteIds[gActiveBattler], + &gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], + HEALTHBOX_ALL); + sub_804BD94(gActiveBattler); + SetHealthboxSpriteVisible(gHealthboxSpriteIds[gActiveBattler]); + gBattleSpritesDataPtr->animationData->field_9_x1 = 0; + gBattlerControllerFuncs[gActiveBattler] = sub_80D4358; + } + } +} + +static void sub_80D4590(void) +{ + if (gSprites[gBattlerSpriteIds[gActiveBattler]].animEnded && gSprites[gBattlerSpriteIds[gActiveBattler]].pos2.x == 0) + LinkPartnerBufferExecCompleted(); +} + +static void CompleteOnHealthbarDone(void) +{ + s16 hpValue = MoveBattleBar(gActiveBattler, gHealthboxSpriteIds[gActiveBattler], HEALTH_BAR, 0); + + SetHealthboxSpriteVisible(gHealthboxSpriteIds[gActiveBattler]); + if (hpValue != -1) + { + UpdateHpTextInHealthbox(gHealthboxSpriteIds[gActiveBattler], hpValue, HP_CURRENT); + } + else + { + HandleLowHpMusicChange(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], gActiveBattler); + LinkPartnerBufferExecCompleted(); + } +} + +static void sub_80D4640(void) +{ + if (gSprites[gBattlerSpriteIds[gActiveBattler]].pos1.y + gSprites[gBattlerSpriteIds[gActiveBattler]].pos2.y > DISPLAY_HEIGHT) + { + FreeOamMatrix(gSprites[gBattlerSpriteIds[gActiveBattler]].oam.matrixNum); + DestroySprite(&gSprites[gBattlerSpriteIds[gActiveBattler]]); + SetHealthboxSpriteInvisible(gHealthboxSpriteIds[gActiveBattler]); + LinkPartnerBufferExecCompleted(); + } +} + +static void sub_80D46A8(void) +{ + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].specialAnimActive) + { + FreeSpriteOamMatrix(&gSprites[gBattlerSpriteIds[gActiveBattler]]); + DestroySprite(&gSprites[gBattlerSpriteIds[gActiveBattler]]); + SetHealthboxSpriteInvisible(gHealthboxSpriteIds[gActiveBattler]); + LinkPartnerBufferExecCompleted(); + } +} + +static void CompleteOnInactiveTextPrinter(void) +{ + if (!IsTextPrinterActive(0)) + LinkPartnerBufferExecCompleted(); +} + +static void DoHitAnimBlinkSpriteEffect(void) +{ + u8 spriteId = gBattlerSpriteIds[gActiveBattler]; + + if (gSprites[spriteId].data[1] == 32) + { + gSprites[spriteId].data[1] = 0; + gSprites[spriteId].invisible = FALSE; + gDoingBattleAnim = FALSE; + LinkPartnerBufferExecCompleted(); + } + else + { + if (!(gSprites[spriteId].data[1] % 4)) + gSprites[spriteId].invisible ^= 1; + ++gSprites[spriteId].data[1]; + } +} + +static void sub_80D47AC(void) +{ + if (gSprites[gHealthboxSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy) + { + if (gBattleSpritesDataPtr->battlerData[gActiveBattler].behindSubstitute) + InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, gActiveBattler, B_ANIM_MON_TO_SUBSTITUTE); + gBattlerControllerFuncs[gActiveBattler] = sub_80D481C; + } +} + +static void sub_80D481C(void) +{ + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].specialAnimActive) + LinkPartnerBufferExecCompleted(); +} + +static void sub_80D484C(void) +{ + if (gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_1_x1) + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].flag_x80 = 0; + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_1_x1 = 0; + FreeSpriteTilesByTag(0x27F9); + FreeSpritePaletteByTag(0x27F9); + CreateTask(c3_0802FDF4, 10); + HandleLowHpMusicChange(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], gActiveBattler); + StartSpriteAnim(&gSprites[gBattlerSpriteIds[gActiveBattler]], 0); + UpdateHealthboxAttribute(gHealthboxSpriteIds[gActiveBattler], &gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], HEALTHBOX_ALL); + sub_804BD94(gActiveBattler); + SetHealthboxSpriteVisible(gHealthboxSpriteIds[gActiveBattler]); + CopyBattleSpriteInvisibility(gActiveBattler); + gBattlerControllerFuncs[gActiveBattler] = sub_80D47AC; + } +} + +static void sub_80D4944(void) +{ + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].flag_x80 + && !gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].ballAnimActive) + sub_80F1720(gActiveBattler, &gPlayerParty[gBattlerPartyIndexes[gActiveBattler]]); + if (gSprites[gUnknown_3004FFC[gActiveBattler]].callback == SpriteCallbackDummy + && !gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].ballAnimActive) + { + DestroySprite(&gSprites[gUnknown_3004FFC[gActiveBattler]]); + gBattlerControllerFuncs[gActiveBattler] = sub_80D484C; + } +} + +static void LinkPartnerBufferExecCompleted(void) +{ + gBattlerControllerFuncs[gActiveBattler] = LinkPartnerBufferRunCommand; + if (gBattleTypeFlags & BATTLE_TYPE_LINK) + { + u8 playerId = GetMultiplayerId(); + + PrepareBufferDataTransferLink(2, 4, &playerId); + gBattleBufferA[gActiveBattler][0] = CONTROLLER_TERMINATOR_NOP; + } + else + { + gBattleControllerExecFlags &= ~gBitTable[gActiveBattler]; + } +} + +static void CompleteOnFinishedStatusAnimation(void) +{ + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].statusAnimActive) + LinkPartnerBufferExecCompleted(); +} + +static void CompleteOnFinishedBattleAnimation(void) +{ + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animFromTableActive) + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandleGetMonData(void) +{ + u8 monData[sizeof(struct Pokemon) * 2 + 56]; // this allows to get full data of two pokemon, trying to get more will result in overwriting data + u32 size = 0; + u8 monToCheck; + s32 i; + + if (!gBattleBufferA[gActiveBattler][2]) + { + size += CopyLinkPartnerMonData(gBattlerPartyIndexes[gActiveBattler], monData); + } + else + { + monToCheck = gBattleBufferA[gActiveBattler][2]; + for (i = 0; i < PARTY_SIZE; ++i) + { + if (monToCheck & 1) + size += CopyLinkPartnerMonData(i, monData + size); + monToCheck >>= 1; + } + } + BtlController_EmitDataTransfer(1, size, monData); + LinkPartnerBufferExecCompleted(); +} + +static u32 CopyLinkPartnerMonData(u8 monId, u8 *dst) +{ + struct BattlePokemon battleMon; + struct MovePpInfo moveData; + u8 nickname[20]; + u8 *src; + s16 data16; + u32 data32; + s32 size = 0; + + switch (gBattleBufferA[gActiveBattler][1]) + { + case REQUEST_ALL_BATTLE: + battleMon.species = GetMonData(&gPlayerParty[monId], MON_DATA_SPECIES); + battleMon.item = GetMonData(&gPlayerParty[monId], MON_DATA_HELD_ITEM); + for (size = 0; size < MAX_MON_MOVES; ++size) + { + battleMon.moves[size] = GetMonData(&gPlayerParty[monId], MON_DATA_MOVE1 + size); + battleMon.pp[size] = GetMonData(&gPlayerParty[monId], MON_DATA_PP1 + size); + } + battleMon.ppBonuses = GetMonData(&gPlayerParty[monId], MON_DATA_PP_BONUSES); + battleMon.friendship = GetMonData(&gPlayerParty[monId], MON_DATA_FRIENDSHIP); + battleMon.experience = GetMonData(&gPlayerParty[monId], MON_DATA_EXP); + battleMon.hpIV = GetMonData(&gPlayerParty[monId], MON_DATA_HP_IV); + battleMon.attackIV = GetMonData(&gPlayerParty[monId], MON_DATA_ATK_IV); + battleMon.defenseIV = GetMonData(&gPlayerParty[monId], MON_DATA_DEF_IV); + battleMon.speedIV = GetMonData(&gPlayerParty[monId], MON_DATA_SPEED_IV); + battleMon.spAttackIV = GetMonData(&gPlayerParty[monId], MON_DATA_SPATK_IV); + battleMon.spDefenseIV = GetMonData(&gPlayerParty[monId], MON_DATA_SPDEF_IV); + battleMon.personality = GetMonData(&gPlayerParty[monId], MON_DATA_PERSONALITY); + battleMon.status1 = GetMonData(&gPlayerParty[monId], MON_DATA_STATUS); + battleMon.level = GetMonData(&gPlayerParty[monId], MON_DATA_LEVEL); + battleMon.hp = GetMonData(&gPlayerParty[monId], MON_DATA_HP); + battleMon.maxHP = GetMonData(&gPlayerParty[monId], MON_DATA_MAX_HP); + battleMon.attack = GetMonData(&gPlayerParty[monId], MON_DATA_ATK); + battleMon.defense = GetMonData(&gPlayerParty[monId], MON_DATA_DEF); + battleMon.speed = GetMonData(&gPlayerParty[monId], MON_DATA_SPEED); + battleMon.spAttack = GetMonData(&gPlayerParty[monId], MON_DATA_SPATK); + battleMon.spDefense = GetMonData(&gPlayerParty[monId], MON_DATA_SPDEF); + battleMon.isEgg = GetMonData(&gPlayerParty[monId], MON_DATA_IS_EGG); + battleMon.abilityNum = GetMonData(&gPlayerParty[monId], MON_DATA_ABILITY_NUM); + battleMon.otId = GetMonData(&gPlayerParty[monId], MON_DATA_OT_ID); + GetMonData(&gPlayerParty[monId], MON_DATA_NICKNAME, nickname); + StringCopy10(battleMon.nickname, nickname); + GetMonData(&gPlayerParty[monId], MON_DATA_OT_NAME, battleMon.otName); + src = (u8 *)&battleMon; + for (size = 0; size < sizeof(battleMon); ++size) + dst[size] = src[size]; + break; + case REQUEST_SPECIES_BATTLE: + data16 = GetMonData(&gPlayerParty[monId], MON_DATA_SPECIES); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_HELDITEM_BATTLE: + data16 = GetMonData(&gPlayerParty[monId], MON_DATA_HELD_ITEM); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_MOVES_PP_BATTLE: + for (size = 0; size < MAX_MON_MOVES; ++size) + { + moveData.moves[size] = GetMonData(&gPlayerParty[monId], MON_DATA_MOVE1 + size); + moveData.pp[size] = GetMonData(&gPlayerParty[monId], MON_DATA_PP1 + size); + } + moveData.ppBonuses = GetMonData(&gPlayerParty[monId], MON_DATA_PP_BONUSES); + src = (u8 *)(&moveData); + for (size = 0; size < sizeof(moveData); ++size) + dst[size] = src[size]; + break; + case REQUEST_MOVE1_BATTLE: + case REQUEST_MOVE2_BATTLE: + case REQUEST_MOVE3_BATTLE: + case REQUEST_MOVE4_BATTLE: + data16 = GetMonData(&gPlayerParty[monId], MON_DATA_MOVE1 + gBattleBufferA[gActiveBattler][1] - REQUEST_MOVE1_BATTLE); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_PP_DATA_BATTLE: + for (size = 0; size < MAX_MON_MOVES; ++size) + dst[size] = GetMonData(&gPlayerParty[monId], MON_DATA_PP1 + size); + dst[size] = GetMonData(&gPlayerParty[monId], MON_DATA_PP_BONUSES); + ++size; + break; + case REQUEST_PPMOVE1_BATTLE: + case REQUEST_PPMOVE2_BATTLE: + case REQUEST_PPMOVE3_BATTLE: + case REQUEST_PPMOVE4_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_PP1 + gBattleBufferA[gActiveBattler][1] - REQUEST_PPMOVE1_BATTLE); + size = 1; + break; + case REQUEST_OTID_BATTLE: + data32 = GetMonData(&gPlayerParty[monId], MON_DATA_OT_ID); + dst[0] = (data32 & 0x000000FF); + dst[1] = (data32 & 0x0000FF00) >> 8; + dst[2] = (data32 & 0x00FF0000) >> 16; + size = 3; + break; + case REQUEST_EXP_BATTLE: + data32 = GetMonData(&gPlayerParty[monId], MON_DATA_EXP); + dst[0] = (data32 & 0x000000FF); + dst[1] = (data32 & 0x0000FF00) >> 8; + dst[2] = (data32 & 0x00FF0000) >> 16; + size = 3; + break; + case REQUEST_HP_EV_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_HP_EV); + size = 1; + break; + case REQUEST_ATK_EV_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_ATK_EV); + size = 1; + break; + case REQUEST_DEF_EV_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_DEF_EV); + size = 1; + break; + case REQUEST_SPEED_EV_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_SPEED_EV); + size = 1; + break; + case REQUEST_SPATK_EV_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_SPATK_EV); + size = 1; + break; + case REQUEST_SPDEF_EV_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_SPDEF_EV); + size = 1; + break; + case REQUEST_FRIENDSHIP_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_FRIENDSHIP); + size = 1; + break; + case REQUEST_POKERUS_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_POKERUS); + size = 1; + break; + case REQUEST_MET_LOCATION_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_MET_LOCATION); + size = 1; + break; + case REQUEST_MET_LEVEL_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_MET_LEVEL); + size = 1; + break; + case REQUEST_MET_GAME_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_MET_GAME); + size = 1; + break; + case REQUEST_POKEBALL_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_POKEBALL); + size = 1; + break; + case REQUEST_ALL_IVS_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_HP_IV); + dst[1] = GetMonData(&gPlayerParty[monId], MON_DATA_ATK_IV); + dst[2] = GetMonData(&gPlayerParty[monId], MON_DATA_DEF_IV); + dst[3] = GetMonData(&gPlayerParty[monId], MON_DATA_SPEED_IV); + dst[4] = GetMonData(&gPlayerParty[monId], MON_DATA_SPATK_IV); + dst[5] = GetMonData(&gPlayerParty[monId], MON_DATA_SPDEF_IV); + size = 6; + break; + case REQUEST_HP_IV_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_HP_IV); + size = 1; + break; + case REQUEST_ATK_IV_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_ATK_IV); + size = 1; + break; + case REQUEST_DEF_IV_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_DEF_IV); + size = 1; + break; + case REQUEST_SPEED_IV_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_SPEED_IV); + size = 1; + break; + case REQUEST_SPATK_IV_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_SPATK_IV); + size = 1; + break; + case REQUEST_SPDEF_IV_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_SPDEF_IV); + size = 1; + break; + case REQUEST_PERSONALITY_BATTLE: + data32 = GetMonData(&gPlayerParty[monId], MON_DATA_PERSONALITY); + dst[0] = (data32 & 0x000000FF); + dst[1] = (data32 & 0x0000FF00) >> 8; + dst[2] = (data32 & 0x00FF0000) >> 16; + dst[3] = (data32 & 0xFF000000) >> 24; + size = 4; + break; + case REQUEST_CHECKSUM_BATTLE: + data16 = GetMonData(&gPlayerParty[monId], MON_DATA_CHECKSUM); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_STATUS_BATTLE: + data32 = GetMonData(&gPlayerParty[monId], MON_DATA_STATUS); + dst[0] = (data32 & 0x000000FF); + dst[1] = (data32 & 0x0000FF00) >> 8; + dst[2] = (data32 & 0x00FF0000) >> 16; + dst[3] = (data32 & 0xFF000000) >> 24; + size = 4; + break; + case REQUEST_LEVEL_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_LEVEL); + size = 1; + break; + case REQUEST_HP_BATTLE: + data16 = GetMonData(&gPlayerParty[monId], MON_DATA_HP); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_MAX_HP_BATTLE: + data16 = GetMonData(&gPlayerParty[monId], MON_DATA_MAX_HP); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_ATK_BATTLE: + data16 = GetMonData(&gPlayerParty[monId], MON_DATA_ATK); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_DEF_BATTLE: + data16 = GetMonData(&gPlayerParty[monId], MON_DATA_DEF); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_SPEED_BATTLE: + data16 = GetMonData(&gPlayerParty[monId], MON_DATA_SPEED); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_SPATK_BATTLE: + data16 = GetMonData(&gPlayerParty[monId], MON_DATA_SPATK); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_SPDEF_BATTLE: + data16 = GetMonData(&gPlayerParty[monId], MON_DATA_SPDEF); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_COOL_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_COOL); + size = 1; + break; + case REQUEST_BEAUTY_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_BEAUTY); + size = 1; + break; + case REQUEST_CUTE_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_CUTE); + size = 1; + break; + case REQUEST_SMART_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_SMART); + size = 1; + break; + case REQUEST_TOUGH_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_TOUGH); + size = 1; + break; + case REQUEST_SHEEN_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_SHEEN); + size = 1; + break; + case REQUEST_COOL_RIBBON_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_COOL_RIBBON); + size = 1; + break; + case REQUEST_BEAUTY_RIBBON_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_BEAUTY_RIBBON); + size = 1; + break; + case REQUEST_CUTE_RIBBON_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_CUTE_RIBBON); + size = 1; + break; + case REQUEST_SMART_RIBBON_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_SMART_RIBBON); + size = 1; + break; + case REQUEST_TOUGH_RIBBON_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_TOUGH_RIBBON); + size = 1; + break; + } + return size; +} + +static void LinkPartnerHandleGetRawMonData(void) +{ + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandleSetMonData(void) +{ + u8 monToCheck; + u8 i; + + if (!gBattleBufferA[gActiveBattler][2]) + { + SetLinkPartnerMonData(gBattlerPartyIndexes[gActiveBattler]); + } + else + { + monToCheck = gBattleBufferA[gActiveBattler][2]; + for (i = 0; i < PARTY_SIZE; ++i) + { + if (monToCheck & 1) + SetLinkPartnerMonData(i); + monToCheck >>= 1; + } + } + LinkPartnerBufferExecCompleted(); +} + +static void SetLinkPartnerMonData(u8 monId) +{ + struct BattlePokemon *battlePokemon = (struct BattlePokemon *)&gBattleBufferA[gActiveBattler][3]; + struct MovePpInfo *moveData = (struct MovePpInfo *)&gBattleBufferA[gActiveBattler][3]; + s32 i; + + switch (gBattleBufferA[gActiveBattler][1]) + { + case REQUEST_ALL_BATTLE: + { + u8 iv; + + SetMonData(&gPlayerParty[monId], MON_DATA_SPECIES, &battlePokemon->species); + SetMonData(&gPlayerParty[monId], MON_DATA_HELD_ITEM, &battlePokemon->item); + for (i = 0; i < MAX_MON_MOVES; ++i) + { + SetMonData(&gPlayerParty[monId], MON_DATA_MOVE1 + i, &battlePokemon->moves[i]); + SetMonData(&gPlayerParty[monId], MON_DATA_PP1 + i, &battlePokemon->pp[i]); + } + SetMonData(&gPlayerParty[monId], MON_DATA_PP_BONUSES, &battlePokemon->ppBonuses); + SetMonData(&gPlayerParty[monId], MON_DATA_FRIENDSHIP, &battlePokemon->friendship); + SetMonData(&gPlayerParty[monId], MON_DATA_EXP, &battlePokemon->experience); + iv = battlePokemon->hpIV; + SetMonData(&gPlayerParty[monId], MON_DATA_HP_IV, &iv); + iv = battlePokemon->attackIV; + SetMonData(&gPlayerParty[monId], MON_DATA_ATK_IV, &iv); + iv = battlePokemon->defenseIV; + SetMonData(&gPlayerParty[monId], MON_DATA_DEF_IV, &iv); + iv = battlePokemon->speedIV; + SetMonData(&gPlayerParty[monId], MON_DATA_SPEED_IV, &iv); + iv = battlePokemon->spAttackIV; + SetMonData(&gPlayerParty[monId], MON_DATA_SPATK_IV, &iv); + iv = battlePokemon->spDefenseIV; + SetMonData(&gPlayerParty[monId], MON_DATA_SPDEF_IV, &iv); + SetMonData(&gPlayerParty[monId], MON_DATA_PERSONALITY, &battlePokemon->personality); + SetMonData(&gPlayerParty[monId], MON_DATA_STATUS, &battlePokemon->status1); + SetMonData(&gPlayerParty[monId], MON_DATA_LEVEL, &battlePokemon->level); + SetMonData(&gPlayerParty[monId], MON_DATA_HP, &battlePokemon->hp); + SetMonData(&gPlayerParty[monId], MON_DATA_MAX_HP, &battlePokemon->maxHP); + SetMonData(&gPlayerParty[monId], MON_DATA_ATK, &battlePokemon->attack); + SetMonData(&gPlayerParty[monId], MON_DATA_DEF, &battlePokemon->defense); + SetMonData(&gPlayerParty[monId], MON_DATA_SPEED, &battlePokemon->speed); + SetMonData(&gPlayerParty[monId], MON_DATA_SPATK, &battlePokemon->spAttack); + SetMonData(&gPlayerParty[monId], MON_DATA_SPDEF, &battlePokemon->spDefense); + } + break; + case REQUEST_SPECIES_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_SPECIES, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_HELDITEM_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_HELD_ITEM, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_MOVES_PP_BATTLE: + for (i = 0; i < MAX_MON_MOVES; ++i) + { + SetMonData(&gPlayerParty[monId], MON_DATA_MOVE1 + i, &moveData->moves[i]); + SetMonData(&gPlayerParty[monId], MON_DATA_PP1 + i, &moveData->pp[i]); + } + SetMonData(&gPlayerParty[monId], MON_DATA_PP_BONUSES, &moveData->ppBonuses); + break; + case REQUEST_MOVE1_BATTLE: + case REQUEST_MOVE2_BATTLE: + case REQUEST_MOVE3_BATTLE: + case REQUEST_MOVE4_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_MOVE1 + gBattleBufferA[gActiveBattler][1] - REQUEST_MOVE1_BATTLE, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_PP_DATA_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_PP1, &gBattleBufferA[gActiveBattler][3]); + SetMonData(&gPlayerParty[monId], MON_DATA_PP2, &gBattleBufferA[gActiveBattler][4]); + SetMonData(&gPlayerParty[monId], MON_DATA_PP3, &gBattleBufferA[gActiveBattler][5]); + SetMonData(&gPlayerParty[monId], MON_DATA_PP4, &gBattleBufferA[gActiveBattler][6]); + SetMonData(&gPlayerParty[monId], MON_DATA_PP_BONUSES, &gBattleBufferA[gActiveBattler][7]); + break; + case REQUEST_PPMOVE1_BATTLE: + case REQUEST_PPMOVE2_BATTLE: + case REQUEST_PPMOVE3_BATTLE: + case REQUEST_PPMOVE4_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_PP1 + gBattleBufferA[gActiveBattler][1] - REQUEST_PPMOVE1_BATTLE, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_OTID_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_OT_ID, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_EXP_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_EXP, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_HP_EV_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_HP_EV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_ATK_EV_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_ATK_EV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_DEF_EV_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_DEF_EV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SPEED_EV_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_SPEED_EV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SPATK_EV_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_SPATK_EV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SPDEF_EV_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_SPDEF_EV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_FRIENDSHIP_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_FRIENDSHIP, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_POKERUS_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_POKERUS, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_MET_LOCATION_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_MET_LOCATION, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_MET_LEVEL_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_MET_LEVEL, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_MET_GAME_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_MET_GAME, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_POKEBALL_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_POKEBALL, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_ALL_IVS_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_HP_IV, &gBattleBufferA[gActiveBattler][3]); + SetMonData(&gPlayerParty[monId], MON_DATA_ATK_IV, &gBattleBufferA[gActiveBattler][4]); + SetMonData(&gPlayerParty[monId], MON_DATA_DEF_IV, &gBattleBufferA[gActiveBattler][5]); + SetMonData(&gPlayerParty[monId], MON_DATA_SPEED_IV, &gBattleBufferA[gActiveBattler][6]); + SetMonData(&gPlayerParty[monId], MON_DATA_SPATK_IV, &gBattleBufferA[gActiveBattler][7]); + SetMonData(&gPlayerParty[monId], MON_DATA_SPDEF_IV, &gBattleBufferA[gActiveBattler][8]); + break; + case REQUEST_HP_IV_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_HP_IV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_ATK_IV_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_ATK_IV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_DEF_IV_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_DEF_IV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SPEED_IV_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_SPEED_IV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SPATK_IV_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_SPATK_IV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SPDEF_IV_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_SPDEF_IV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_PERSONALITY_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_PERSONALITY, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_CHECKSUM_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_CHECKSUM, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_STATUS_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_STATUS, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_LEVEL_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_LEVEL, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_HP_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_HP, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_MAX_HP_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_MAX_HP, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_ATK_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_ATK, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_DEF_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_DEF, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SPEED_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_SPEED, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SPATK_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_SPATK, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SPDEF_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_SPDEF, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_COOL_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_COOL, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_BEAUTY_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_BEAUTY, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_CUTE_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_CUTE, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SMART_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_SMART, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_TOUGH_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_TOUGH, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SHEEN_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_SHEEN, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_COOL_RIBBON_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_COOL_RIBBON, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_BEAUTY_RIBBON_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_BEAUTY_RIBBON, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_CUTE_RIBBON_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_CUTE_RIBBON, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SMART_RIBBON_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_SMART_RIBBON, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_TOUGH_RIBBON_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_TOUGH_RIBBON, &gBattleBufferA[gActiveBattler][3]); + break; + } + HandleLowHpMusicChange(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], gActiveBattler); +} + +static void LinkPartnerHandleSetRawMonData(void) +{ + u8 *dst = (u8 *)&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]] + gBattleBufferA[gActiveBattler][1]; + u8 i; + + for (i = 0; i < gBattleBufferA[gActiveBattler][2]; ++i) + dst[i] = gBattleBufferA[gActiveBattler][3 + i]; + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandleLoadMonSprite(void) +{ + u16 species; + + BattleLoadPlayerMonSpriteGfx(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], gActiveBattler); + species = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_SPECIES); + SetMultiuseSpriteTemplateToPokemon(species, GetBattlerPosition(gActiveBattler)); + gBattlerSpriteIds[gActiveBattler] = CreateSprite(&gMultiuseSpriteTemplate, + GetBattlerSpriteCoord(gActiveBattler, 2), + GetBattlerSpriteDefault_Y(gActiveBattler), + GetBattlerSpriteSubpriority(gActiveBattler)); + gSprites[gBattlerSpriteIds[gActiveBattler]].pos2.x = -240; + gSprites[gBattlerSpriteIds[gActiveBattler]].data[0] = gActiveBattler; + gSprites[gBattlerSpriteIds[gActiveBattler]].oam.paletteNum = gActiveBattler; + StartSpriteAnim(&gSprites[gBattlerSpriteIds[gActiveBattler]], gBattleMonForms[gActiveBattler]); + gBattlerControllerFuncs[gActiveBattler] = sub_80D4590; +} + +static void LinkPartnerHandleSwitchInAnim(void) +{ + ClearTemporarySpeciesSpriteData(gActiveBattler, gBattleBufferA[gActiveBattler][2]); + gBattlerPartyIndexes[gActiveBattler] = gBattleBufferA[gActiveBattler][1]; + BattleLoadPlayerMonSpriteGfx(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], gActiveBattler); + sub_80D5F40(gActiveBattler, gBattleBufferA[gActiveBattler][2]); + gBattlerControllerFuncs[gActiveBattler] = sub_80D4944; +} + +static void sub_80D5F40(u8 battlerId, bool8 dontClearSubstituteBit) +{ + u16 species; + + ClearTemporarySpeciesSpriteData(battlerId, dontClearSubstituteBit); + gBattlerPartyIndexes[battlerId] = gBattleBufferA[battlerId][1]; + species = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerId]], MON_DATA_SPECIES); + gUnknown_3004FFC[battlerId] = CreateInvisibleSpriteWithCallback(sub_8033E3C); + SetMultiuseSpriteTemplateToPokemon(species, GetBattlerPosition(battlerId)); + gBattlerSpriteIds[battlerId] = CreateSprite(&gMultiuseSpriteTemplate, + GetBattlerSpriteCoord(battlerId, 2), + GetBattlerSpriteDefault_Y(battlerId), + GetBattlerSpriteSubpriority(battlerId)); + gSprites[gUnknown_3004FFC[battlerId]].data[1] = gBattlerSpriteIds[battlerId]; + gSprites[gBattlerSpriteIds[battlerId]].data[0] = battlerId; + gSprites[gBattlerSpriteIds[battlerId]].data[2] = species; + gSprites[gBattlerSpriteIds[battlerId]].oam.paletteNum = battlerId; + StartSpriteAnim(&gSprites[gBattlerSpriteIds[battlerId]], gBattleMonForms[battlerId]); + gSprites[gBattlerSpriteIds[battlerId]].invisible = TRUE; + gSprites[gBattlerSpriteIds[battlerId]].callback = SpriteCallbackDummy; + gSprites[gUnknown_3004FFC[battlerId]].data[0] = DoPokeballSendOutAnimation(0, POKEBALL_PLAYER_SENDOUT); +} + +static void LinkPartnerHandleReturnMonToBall(void) +{ + if (!gBattleBufferA[gActiveBattler][1]) + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 0; + gBattlerControllerFuncs[gActiveBattler] = DoSwitchOutAnimation; + } + else + { + FreeSpriteOamMatrix(&gSprites[gBattlerSpriteIds[gActiveBattler]]); + DestroySprite(&gSprites[gBattlerSpriteIds[gActiveBattler]]); + SetHealthboxSpriteInvisible(gHealthboxSpriteIds[gActiveBattler]); + LinkPartnerBufferExecCompleted(); + } +} + +static void DoSwitchOutAnimation(void) +{ + switch (gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState) + { + case 0: + if (gBattleSpritesDataPtr->battlerData[gActiveBattler].behindSubstitute) + InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, gActiveBattler, B_ANIM_SUBSTITUTE_TO_MON); + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 1; + break; + case 1: + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].specialAnimActive) + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 0; + InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, gActiveBattler, B_ANIM_SWITCH_OUT_PLAYER_MON); + gBattlerControllerFuncs[gActiveBattler] = sub_80D46A8; + } + break; + } +} + +static void LinkPartnerHandleDrawTrainerPic(void) +{ + s16 xPos; + u32 trainerPicId; + + if ((GetBattlerPosition(gActiveBattler) & BIT_FLANK) != B_FLANK_LEFT) // Second mon, on the right. + xPos = 90; + else // First mon, on the left. + xPos = 32; + if ((gLinkPlayers[GetBattlerMultiplayerId(gActiveBattler)].version & 0xFF) == VERSION_RUBY + || (gLinkPlayers[GetBattlerMultiplayerId(gActiveBattler)].version & 0xFF) == VERSION_SAPPHIRE + || (gLinkPlayers[GetBattlerMultiplayerId(gActiveBattler)].version & 0xFF) == VERSION_EMERALD) + trainerPicId = gLinkPlayers[GetBattlerMultiplayerId(gActiveBattler)].gender + 2; + else + trainerPicId = gLinkPlayers[GetBattlerMultiplayerId(gActiveBattler)].gender + 0; + DecompressTrainerBackPalette(trainerPicId, gActiveBattler); + SetMultiuseSpriteTemplateToTrainerBack(trainerPicId, GetBattlerPosition(gActiveBattler)); + gBattlerSpriteIds[gActiveBattler] = CreateSprite(&gMultiuseSpriteTemplate, xPos, (8 - gTrainerBackPicCoords[trainerPicId].size) * 4 + 80, GetBattlerSpriteSubpriority(gActiveBattler)); + gSprites[gBattlerSpriteIds[gActiveBattler]].oam.paletteNum = gActiveBattler; + gSprites[gBattlerSpriteIds[gActiveBattler]].pos2.x = 240; + gSprites[gBattlerSpriteIds[gActiveBattler]].data[0] = -2; + gSprites[gBattlerSpriteIds[gActiveBattler]].callback = sub_8033EEC; + gBattlerControllerFuncs[gActiveBattler] = CompleteOnBattlerSpriteCallbackDummy; +} + +static void LinkPartnerHandleTrainerSlide(void) +{ + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandleTrainerSlideBack(void) +{ + SetSpritePrimaryCoordsFromSecondaryCoords(&gSprites[gBattlerSpriteIds[gActiveBattler]]); + gSprites[gBattlerSpriteIds[gActiveBattler]].data[0] = 35; + gSprites[gBattlerSpriteIds[gActiveBattler]].data[2] = -40; + gSprites[gBattlerSpriteIds[gActiveBattler]].data[4] = gSprites[gBattlerSpriteIds[gActiveBattler]].pos1.y; + gSprites[gBattlerSpriteIds[gActiveBattler]].callback = StartAnimLinearTranslation; + StoreSpriteCallbackInData6(&gSprites[gBattlerSpriteIds[gActiveBattler]], SpriteCallbackDummy); + gBattlerControllerFuncs[gActiveBattler] = sub_80D42A8; +} + +static void LinkPartnerHandleFaintAnimation(void) +{ + if (gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState == 0) + { + if (gBattleSpritesDataPtr->battlerData[gActiveBattler].behindSubstitute) + InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, gActiveBattler, B_ANIM_SUBSTITUTE_TO_MON); + ++gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState; + } + else + { + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].specialAnimActive) + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 0; + HandleLowHpMusicChange(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], gActiveBattler); + PlaySE12WithPanning(SE_POKE_DEAD, SOUND_PAN_ATTACKER); + gSprites[gBattlerSpriteIds[gActiveBattler]].data[1] = 0; + gSprites[gBattlerSpriteIds[gActiveBattler]].data[2] = 5; + gSprites[gBattlerSpriteIds[gActiveBattler]].callback = sub_8012110; + gBattlerControllerFuncs[gActiveBattler] = sub_80D4640; + } + } +} + +static void LinkPartnerHandlePaletteFade(void) +{ + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandleSuccessBallThrowAnim(void) +{ + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandleBallThrowAnim(void) +{ + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandlePause(void) +{ + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandleMoveAnimation(void) +{ + if (!mplay_80342A4(gActiveBattler)) + { + u16 move = gBattleBufferA[gActiveBattler][1] | (gBattleBufferA[gActiveBattler][2] << 8); + + gAnimMoveTurn = gBattleBufferA[gActiveBattler][3]; + gAnimMovePower = gBattleBufferA[gActiveBattler][4] | (gBattleBufferA[gActiveBattler][5] << 8); + gAnimMoveDmg = gBattleBufferA[gActiveBattler][6] | (gBattleBufferA[gActiveBattler][7] << 8) | (gBattleBufferA[gActiveBattler][8] << 16) | (gBattleBufferA[gActiveBattler][9] << 24); + gAnimFriendship = gBattleBufferA[gActiveBattler][10]; + gWeatherMoveAnim = gBattleBufferA[gActiveBattler][12] | (gBattleBufferA[gActiveBattler][13] << 8); + gAnimDisableStructPtr = (struct DisableStruct *)&gBattleBufferA[gActiveBattler][16]; + gTransformedPersonalities[gActiveBattler] = gAnimDisableStructPtr->transformedMonPersonality; + if (IsMoveWithoutAnimation(move, gAnimMoveTurn)) // always returns FALSE + { + LinkPartnerBufferExecCompleted(); + } + else + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 0; + gBattlerControllerFuncs[gActiveBattler] = LinkPartnerDoMoveAnimation; + } + } +} + +static void LinkPartnerDoMoveAnimation(void) +{ + u16 move = gBattleBufferA[gActiveBattler][1] | (gBattleBufferA[gActiveBattler][2] << 8); + u8 multihit = gBattleBufferA[gActiveBattler][11]; + + switch (gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState) + { + case 0: + if (gBattleSpritesDataPtr->battlerData[gActiveBattler].behindSubstitute + && !gBattleSpritesDataPtr->battlerData[gActiveBattler].flag_x8) + { + gBattleSpritesDataPtr->battlerData[gActiveBattler].flag_x8 = 1; + InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, gActiveBattler, B_ANIM_SUBSTITUTE_TO_MON); + } + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 1; + break; + case 1: + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].specialAnimActive) + { + sub_8035450(0); + DoMoveAnim(move); + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 2; + } + break; + case 2: + gAnimScriptCallback(); + if (!gAnimScriptActive) + { + sub_8035450(1); + if (gBattleSpritesDataPtr->battlerData[gActiveBattler].behindSubstitute && multihit < 2) + { + InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, gActiveBattler, B_ANIM_MON_TO_SUBSTITUTE); + gBattleSpritesDataPtr->battlerData[gActiveBattler].flag_x8 = 0; + } + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 3; + } + break; + case 3: + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].specialAnimActive) + { + CopyAllBattleSpritesInvisibilities(); + TrySetBehindSubstituteSpriteBit(gActiveBattler, gBattleBufferA[gActiveBattler][1] | (gBattleBufferA[gActiveBattler][2] << 8)); + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 0; + LinkPartnerBufferExecCompleted(); + } + break; + } +} + +static void LinkPartnerHandlePrintString(void) +{ + u16 *stringId; + + gBattle_BG0_X = 0; + gBattle_BG0_Y = 0; + stringId = (u16 *)(&gBattleBufferA[gActiveBattler][2]); + BufferStringBattle(*stringId); + if (sub_80D89B0(*stringId)) + BattlePutTextOnWindow(gDisplayedStringBattle, 0x40); + else + BattlePutTextOnWindow(gDisplayedStringBattle, 0); + gBattlerControllerFuncs[gActiveBattler] = CompleteOnInactiveTextPrinter; +} + +static void LinkPartnerHandlePrintSelectionString(void) +{ + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandleChooseAction(void) +{ + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandleUnknownYesNoBox(void) +{ + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandleChooseMove(void) +{ + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandleChooseItem(void) +{ + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandleChoosePokemon(void) +{ + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandleCmd23(void) +{ + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandleHealthBarUpdate(void) +{ + s16 hpVal; + + LoadBattleBarGfx(0); + hpVal = gBattleBufferA[gActiveBattler][2] | (gBattleBufferA[gActiveBattler][3] << 8); + if (hpVal != INSTANT_HP_BAR_DROP) + { + u32 maxHP = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_MAX_HP); + u32 curHP = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_HP); + + SetBattleBarStruct(gActiveBattler, gHealthboxSpriteIds[gActiveBattler], maxHP, curHP, hpVal); + } + else + { + u32 maxHP = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_MAX_HP); + + SetBattleBarStruct(gActiveBattler, gHealthboxSpriteIds[gActiveBattler], maxHP, 0, hpVal); + } + gBattlerControllerFuncs[gActiveBattler] = CompleteOnHealthbarDone; +} + +static void LinkPartnerHandleExpUpdate(void) +{ + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandleStatusIconUpdate(void) +{ + if (!mplay_80342A4(gActiveBattler)) + { + u8 battlerId; + + UpdateHealthboxAttribute(gHealthboxSpriteIds[gActiveBattler], &gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], HEALTHBOX_STATUS_ICON); + battlerId = gActiveBattler; + gBattleSpritesDataPtr->healthBoxesData[battlerId].statusAnimActive = 0; + gBattlerControllerFuncs[gActiveBattler] = CompleteOnFinishedStatusAnimation; + } +} + +static void LinkPartnerHandleStatusAnimation(void) +{ + if (!mplay_80342A4(gActiveBattler)) + { + InitAndLaunchChosenStatusAnimation(gBattleBufferA[gActiveBattler][1], + gBattleBufferA[gActiveBattler][2] | (gBattleBufferA[gActiveBattler][3] << 8) | (gBattleBufferA[gActiveBattler][4] << 16) | (gBattleBufferA[gActiveBattler][5] << 24)); + gBattlerControllerFuncs[gActiveBattler] = CompleteOnFinishedStatusAnimation; + } +} + +static void LinkPartnerHandleStatusXor(void) +{ + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandleDataTransfer(void) +{ + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandleDMA3Transfer(void) +{ + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandlePlayBGM(void) +{ + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandleCmd32(void) +{ + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandleTwoReturnValues(void) +{ + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandleChosenMonReturnValue(void) +{ + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandleOneReturnValue(void) +{ + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandleOneReturnValue_Duplicate(void) +{ + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandleCmd37(void) +{ + gUnknown_2022870.field_0 = 0; + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandleCmd38(void) +{ + gUnknown_2022870.field_0 = gBattleBufferA[gActiveBattler][1]; + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandleCmd39(void) +{ + gUnknown_2022870.flag_x80 = 0; + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandleCmd40(void) +{ + gUnknown_2022870.flag_x80 ^= 1; + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandleHitAnimation(void) +{ + if (gSprites[gBattlerSpriteIds[gActiveBattler]].invisible == TRUE) + { + LinkPartnerBufferExecCompleted(); + } + else + { + gDoingBattleAnim = TRUE; + gSprites[gBattlerSpriteIds[gActiveBattler]].data[1] = 0; + DoHitAnimHealthboxEffect(gActiveBattler); + gBattlerControllerFuncs[gActiveBattler] = DoHitAnimBlinkSpriteEffect; + } +} + +static void LinkPartnerHandleCmd42(void) +{ + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandlePlaySE(void) +{ + s8 pan; + + if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER) + pan = SOUND_PAN_ATTACKER; + else + pan = SOUND_PAN_TARGET; + PlaySE12WithPanning(gBattleBufferA[gActiveBattler][1] | (gBattleBufferA[gActiveBattler][2] << 8), pan); + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandlePlayFanfare(void) +{ + PlayFanfare(gBattleBufferA[gActiveBattler][1] | (gBattleBufferA[gActiveBattler][2] << 8)); + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandleFaintingCry(void) +{ + u16 species = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_SPECIES); + + PlayCry3(species, -25, 5); + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandleIntroSlide(void) +{ + HandleIntroSlide(gBattleBufferA[gActiveBattler][1]); + gIntroSlideFlags |= 1; + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandleIntroTrainerBallThrow(void) +{ + u8 paletteNum; + u8 taskId; + u32 trainerPicId; + + SetSpritePrimaryCoordsFromSecondaryCoords(&gSprites[gBattlerSpriteIds[gActiveBattler]]); + gSprites[gBattlerSpriteIds[gActiveBattler]].data[0] = 50; + gSprites[gBattlerSpriteIds[gActiveBattler]].data[2] = -40; + gSprites[gBattlerSpriteIds[gActiveBattler]].data[4] = gSprites[gBattlerSpriteIds[gActiveBattler]].pos1.y; + gSprites[gBattlerSpriteIds[gActiveBattler]].callback = StartAnimLinearTranslation; + gSprites[gBattlerSpriteIds[gActiveBattler]].data[5] = gActiveBattler; + StoreSpriteCallbackInData6(&gSprites[gBattlerSpriteIds[gActiveBattler]], sub_80335F8); + StartSpriteAnim(&gSprites[gBattlerSpriteIds[gActiveBattler]], 1); + paletteNum = AllocSpritePalette(0xD6F9); + if ((gLinkPlayers[GetBattlerMultiplayerId(gActiveBattler)].version & 0xFF) == VERSION_RUBY + || (gLinkPlayers[GetBattlerMultiplayerId(gActiveBattler)].version & 0xFF) == VERSION_SAPPHIRE + || (gLinkPlayers[GetBattlerMultiplayerId(gActiveBattler)].version & 0xFF) == VERSION_EMERALD) + trainerPicId = gLinkPlayers[GetBattlerMultiplayerId(gActiveBattler)].gender + 2; + else + trainerPicId = gLinkPlayers[GetBattlerMultiplayerId(gActiveBattler)].gender + 0; + LoadCompressedPalette(gTrainerBackPicPaletteTable[trainerPicId].data, 0x100 + paletteNum * 16, 32); + gSprites[gBattlerSpriteIds[gActiveBattler]].oam.paletteNum = paletteNum; + taskId = CreateTask(sub_80D6ED0, 5); + gTasks[taskId].data[0] = gActiveBattler; + if (gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].partyStatusSummaryShown) + gTasks[gBattlerStatusSummaryTaskId[gActiveBattler]].func = Task_HidePartyStatusSummary; + gBattleSpritesDataPtr->animationData->field_9_x1 = 1; + gBattlerControllerFuncs[gActiveBattler] = nullsub_77; +} + +static void sub_80D6ED0(u8 taskId) +{ + if (gTasks[taskId].data[1] < 24) + { + ++gTasks[taskId].data[1]; + } + else + { + u8 savedActiveBattler = gActiveBattler; + + gActiveBattler = gTasks[taskId].data[0]; + if (!IsDoubleBattle() || (gBattleTypeFlags & BATTLE_TYPE_MULTI)) + { + gBattleBufferA[gActiveBattler][1] = gBattlerPartyIndexes[gActiveBattler]; + sub_80D5F40(gActiveBattler, FALSE); + } + else + { + gBattleBufferA[gActiveBattler][1] = gBattlerPartyIndexes[gActiveBattler]; + sub_80D5F40(gActiveBattler, FALSE); + gActiveBattler ^= BIT_FLANK; + gBattleBufferA[gActiveBattler][1] = gBattlerPartyIndexes[gActiveBattler]; + BattleLoadPlayerMonSpriteGfx(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], gActiveBattler); + sub_80D5F40(gActiveBattler, FALSE); + gActiveBattler ^= BIT_FLANK; + } + gBattlerControllerFuncs[gActiveBattler] = sub_80D443C; + gActiveBattler = savedActiveBattler; + DestroyTask(taskId); + } +} + +static void LinkPartnerHandleDrawPartyStatusSummary(void) +{ + if (gBattleBufferA[gActiveBattler][1] && GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER) + { + LinkPartnerBufferExecCompleted(); + } + else + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].partyStatusSummaryShown = 1; + gBattlerStatusSummaryTaskId[gActiveBattler] = CreatePartyStatusSummarySprites(gActiveBattler, (struct HpAndStatus *)&gBattleBufferA[gActiveBattler][4], gBattleBufferA[gActiveBattler][1], gBattleBufferA[gActiveBattler][2]); + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_5 = 0; + + if (gBattleBufferA[gActiveBattler][2]) + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_5 = 0x5D; + + gBattlerControllerFuncs[gActiveBattler] = sub_80D70A0; + } +} + +static void sub_80D70A0(void) +{ + if (gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_5++ > 0x5C) + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_5 = 0; + LinkPartnerBufferExecCompleted(); + } +} + +static void LinkPartnerHandleHidePartyStatusSummary(void) +{ + if (gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].partyStatusSummaryShown) + gTasks[gBattlerStatusSummaryTaskId[gActiveBattler]].func = Task_HidePartyStatusSummary; + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandleEndBounceEffect(void) +{ + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandleSpriteInvisibility(void) +{ + if (IsBattlerSpritePresent(gActiveBattler)) + { + gSprites[gBattlerSpriteIds[gActiveBattler]].invisible = gBattleBufferA[gActiveBattler][1]; + CopyBattleSpriteInvisibility(gActiveBattler); + } + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandleBattleAnimation(void) +{ + if (!mplay_80342A4(gActiveBattler)) + { + u8 animationId = gBattleBufferA[gActiveBattler][1]; + u16 argument = gBattleBufferA[gActiveBattler][2] | (gBattleBufferA[gActiveBattler][3] << 8); + + if (TryHandleLaunchBattleTableAnimation(gActiveBattler, gActiveBattler, gActiveBattler, animationId, argument)) + LinkPartnerBufferExecCompleted(); + else + gBattlerControllerFuncs[gActiveBattler] = CompleteOnFinishedBattleAnimation; + } +} + +static void LinkPartnerHandleLinkStandbyMsg(void) +{ + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandleResetActionMoveSelection(void) +{ + LinkPartnerBufferExecCompleted(); +} + +static void LinkPartnerHandleCmd55(void) +{ + gBattleOutcome = gBattleBufferA[gActiveBattler][1]; + FadeOutMapMusic(5); + BeginFastPaletteFade(3); + LinkPartnerBufferExecCompleted(); + gBattlerControllerFuncs[gActiveBattler] = sub_802F6A8; +} + +static void LinkPartnerCmdEnd(void) +{ +} diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c new file mode 100644 index 000000000..794b83b73 --- /dev/null +++ b/src/battle_controller_opponent.c @@ -0,0 +1,1788 @@ +#include "global.h" +#include "bg.h" +#include "data.h" +#include "item.h" +#include "link.h" +#include "main.h" +#include "m4a.h" +#include "task.h" +#include "text.h" +#include "util.h" +#include "window.h" +#include "palette.h" +#include "pokeball.h" +#include "pokemon.h" +#include "random.h" +#include "sound.h" +#include "string_util.h" +#include "battle.h" +#include "battle_anim.h" +#include "battle_controllers.h" +#include "battle_message.h" +#include "battle_interface.h" +#include "battle_setup.h" +#include "battle_tower.h" +#include "battle_gfx_sfx_util.h" +#include "battle_ai_script_commands.h" +#include "battle_ai_switch_items.h" +#include "trainer_tower.h" +#include "reshow_battle_screen.h" +#include "constants/battle_anim.h" +#include "constants/items.h" +#include "constants/moves.h" +#include "constants/songs.h" + +static void OpponentHandleGetMonData(void); +static void OpponentHandleGetRawMonData(void); +static void OpponentHandleSetMonData(void); +static void OpponentHandleSetRawMonData(void); +static void OpponentHandleLoadMonSprite(void); +static void OpponentHandleSwitchInAnim(void); +static void OpponentHandleReturnMonToBall(void); +static void OpponentHandleDrawTrainerPic(void); +static void OpponentHandleTrainerSlide(void); +static void OpponentHandleTrainerSlideBack(void); +static void OpponentHandleFaintAnimation(void); +static void OpponentHandlePaletteFade(void); +static void OpponentHandleSuccessBallThrowAnim(void); +static void OpponentHandleBallThrow(void); +static void OpponentHandlePause(void); +static void OpponentHandleMoveAnimation(void); +static void OpponentHandlePrintString(void); +static void OpponentHandlePrintSelectionString(void); +static void OpponentHandleChooseAction(void); +static void OpponentHandleUnknownYesNoBox(void); +static void OpponentHandleChooseMove(void); +static void OpponentHandleChooseItem(void); +static void OpponentHandleChoosePokemon(void); +static void OpponentHandleCmd23(void); +static void OpponentHandleHealthBarUpdate(void); +static void OpponentHandleExpUpdate(void); +static void OpponentHandleStatusIconUpdate(void); +static void OpponentHandleStatusAnimation(void); +static void OpponentHandleStatusXor(void); +static void OpponentHandleDataTransfer(void); +static void OpponentHandleDMA3Transfer(void); +static void OpponentHandlePlayBGM(void); +static void OpponentHandleCmd32(void); +static void OpponentHandleTwoReturnValues(void); +static void OpponentHandleChosenMonReturnValue(void); +static void OpponentHandleOneReturnValue(void); +static void OpponentHandleOneReturnValue_Duplicate(void); +static void OpponentHandleCmd37(void); +static void OpponentHandleCmd38(void); +static void OpponentHandleCmd39(void); +static void OpponentHandleCmd40(void); +static void OpponentHandleHitAnimation(void); +static void OpponentHandleCmd42(void); +static void OpponentHandlePlaySE(void); +static void OpponentHandlePlayFanfare(void); +static void OpponentHandleFaintingCry(void); +static void OpponentHandleIntroSlide(void); +static void OpponentHandleIntroTrainerBallThrow(void); +static void OpponentHandleDrawPartyStatusSummary(void); +static void OpponentHandleHidePartyStatusSummary(void); +static void OpponentHandleEndBounceEffect(void); +static void OpponentHandleSpriteInvisibility(void); +static void OpponentHandleBattleAnimation(void); +static void OpponentHandleLinkStandbyMsg(void); +static void OpponentHandleResetActionMoveSelection(void); +static void OpponentHandleCmd55(void); +static void OpponentCmdEnd(void); + +static void OpponentBufferRunCommand(void); +static u32 GetOpponentMonData(u8 monId, u8 *dst); +static void SetOpponentMonData(u8 monId); +static void DoSwitchOutAnimation(void); +static void OpponentDoMoveAnimation(void); +static void sub_80362E8(void); +static void sub_8037A28(u8 battlerId, bool8 dontClearSubstituteBit); +static void sub_8038DC4(u8 taskId); +static void sub_8038D90(struct Sprite *sprite); +static void sub_8038FBC(void); + +static void (*const sOpponentBufferCommands[CONTROLLER_CMDS_COUNT])(void) = +{ + OpponentHandleGetMonData, + OpponentHandleGetRawMonData, + OpponentHandleSetMonData, + OpponentHandleSetRawMonData, + OpponentHandleLoadMonSprite, + OpponentHandleSwitchInAnim, + OpponentHandleReturnMonToBall, + OpponentHandleDrawTrainerPic, + OpponentHandleTrainerSlide, + OpponentHandleTrainerSlideBack, + OpponentHandleFaintAnimation, + OpponentHandlePaletteFade, + OpponentHandleSuccessBallThrowAnim, + OpponentHandleBallThrow, + OpponentHandlePause, + OpponentHandleMoveAnimation, + OpponentHandlePrintString, + OpponentHandlePrintSelectionString, + OpponentHandleChooseAction, + OpponentHandleUnknownYesNoBox, + OpponentHandleChooseMove, + OpponentHandleChooseItem, + OpponentHandleChoosePokemon, + OpponentHandleCmd23, + OpponentHandleHealthBarUpdate, + OpponentHandleExpUpdate, + OpponentHandleStatusIconUpdate, + OpponentHandleStatusAnimation, + OpponentHandleStatusXor, + OpponentHandleDataTransfer, + OpponentHandleDMA3Transfer, + OpponentHandlePlayBGM, + OpponentHandleCmd32, + OpponentHandleTwoReturnValues, + OpponentHandleChosenMonReturnValue, + OpponentHandleOneReturnValue, + OpponentHandleOneReturnValue_Duplicate, + OpponentHandleCmd37, + OpponentHandleCmd38, + OpponentHandleCmd39, + OpponentHandleCmd40, + OpponentHandleHitAnimation, + OpponentHandleCmd42, + OpponentHandlePlaySE, + OpponentHandlePlayFanfare, + OpponentHandleFaintingCry, + OpponentHandleIntroSlide, + OpponentHandleIntroTrainerBallThrow, + OpponentHandleDrawPartyStatusSummary, + OpponentHandleHidePartyStatusSummary, + OpponentHandleEndBounceEffect, + OpponentHandleSpriteInvisibility, + OpponentHandleBattleAnimation, + OpponentHandleLinkStandbyMsg, + OpponentHandleResetActionMoveSelection, + OpponentHandleCmd55, + OpponentCmdEnd +}; + +// not used +static const u8 gUnknown_8250B18[] = { 0xB0, 0xB0, 0xC8, 0x98, 0x28, 0x28, 0x28, 0x20 }; + +static void nullsub_17(void) +{ +} + +void SetControllerToOpponent(void) +{ + gBattlerControllerFuncs[gActiveBattler] = OpponentBufferRunCommand; +} + +static void OpponentBufferRunCommand(void) +{ + if (gBattleControllerExecFlags & gBitTable[gActiveBattler]) + { + if (gBattleBufferA[gActiveBattler][0] < NELEMS(sOpponentBufferCommands)) + sOpponentBufferCommands[gBattleBufferA[gActiveBattler][0]](); + else + OpponentBufferExecCompleted(); + } +} + +static void CompleteOnBattlerSpriteCallbackDummy(void) +{ + if (gSprites[gBattlerSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy) + OpponentBufferExecCompleted(); +} + +static void CompleteOnBattlerSpriteCallbackDummy2(void) +{ + if (gSprites[gBattlerSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy) + OpponentBufferExecCompleted(); +} + +static void sub_8035B58(void) +{ + if (gSprites[gBattlerSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy) + { + FreeTrainerFrontPicPaletteAndTile(gSprites[gBattlerSpriteIds[gActiveBattler]].oam.affineParam); + gSprites[gBattlerSpriteIds[gActiveBattler]].oam.tileNum = gSprites[gBattlerSpriteIds[gActiveBattler]].data[5]; + FreeSpriteOamMatrix(&gSprites[gBattlerSpriteIds[gActiveBattler]]); + DestroySprite(&gSprites[gBattlerSpriteIds[gActiveBattler]]); + OpponentBufferExecCompleted(); + } +} + +static void sub_8035BE8(void) +{ + if (--gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_9 == 0xFF) + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_9 = 0; + OpponentBufferExecCompleted(); + } +} + +static void sub_8035C30(void) +{ + bool8 var = FALSE; + + if (!IsDoubleBattle() || ((IsDoubleBattle() && (gBattleTypeFlags & BATTLE_TYPE_MULTI)))) + { + if (gSprites[gHealthboxSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy) + var = TRUE; + } + else if (gSprites[gHealthboxSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy + && gSprites[gHealthboxSpriteIds[gActiveBattler ^ BIT_FLANK]].callback == gSprites[gHealthboxSpriteIds[gActiveBattler]].callback) + { + var = TRUE; + } + if (IsCryPlayingOrClearCrySongs()) + var = FALSE; + if (var && gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_1_x1 && gBattleSpritesDataPtr->healthBoxesData[gActiveBattler ^ BIT_FLANK].field_1_x1) + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].flag_x80 = 0; + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_1_x1 = 0; + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler ^ BIT_FLANK].flag_x80 = 0; + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler ^ BIT_FLANK].field_1_x1 = 0; + FreeSpriteTilesByTag(0x27F9); + FreeSpritePaletteByTag(0x27F9); + if (gBattleTypeFlags & BATTLE_TYPE_MULTI) + m4aMPlayContinue(&gMPlayInfo_BGM); + else + m4aMPlayVolumeControl(&gMPlayInfo_BGM, 0xFFFF, 256); + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_9 = 3; + gBattlerControllerFuncs[gActiveBattler] = sub_8035BE8; + } +} + +static void sub_8035DF0(void) +{ + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].flag_x80 && !gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].ballAnimActive) + sub_80F1720(gActiveBattler, &gEnemyParty[gBattlerPartyIndexes[gActiveBattler]]); + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler ^ BIT_FLANK].flag_x80 && !gBattleSpritesDataPtr->healthBoxesData[gActiveBattler ^ BIT_FLANK].ballAnimActive) + sub_80F1720(gActiveBattler ^ BIT_FLANK, &gEnemyParty[gBattlerPartyIndexes[gActiveBattler ^ BIT_FLANK]]); + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].ballAnimActive && !gBattleSpritesDataPtr->healthBoxesData[gActiveBattler ^ BIT_FLANK].ballAnimActive) + { + if (IsDoubleBattle() && !(gBattleTypeFlags & BATTLE_TYPE_MULTI)) + { + DestroySprite(&gSprites[gUnknown_3004FFC[gActiveBattler ^ BIT_FLANK]]); + UpdateHealthboxAttribute(gHealthboxSpriteIds[gActiveBattler ^ BIT_FLANK], + &gEnemyParty[gBattlerPartyIndexes[gActiveBattler ^ BIT_FLANK]], + HEALTHBOX_ALL); + sub_804BD94(gActiveBattler ^ BIT_FLANK); + SetHealthboxSpriteVisible(gHealthboxSpriteIds[gActiveBattler ^ BIT_FLANK]); + SetBattlerShadowSpriteCallback(gActiveBattler ^ BIT_FLANK, GetMonData(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler ^ BIT_FLANK]], MON_DATA_SPECIES)); + } + DestroySprite(&gSprites[gUnknown_3004FFC[gActiveBattler]]); + UpdateHealthboxAttribute(gHealthboxSpriteIds[gActiveBattler], + &gEnemyParty[gBattlerPartyIndexes[gActiveBattler]], + HEALTHBOX_ALL); + sub_804BD94(gActiveBattler); + SetHealthboxSpriteVisible(gHealthboxSpriteIds[gActiveBattler]); + SetBattlerShadowSpriteCallback(gActiveBattler, GetMonData(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_SPECIES)); + gBattleSpritesDataPtr->animationData->field_9_x1 = 0; + gBattlerControllerFuncs[gActiveBattler] = sub_8035C30; + } +} + +static void sub_8035FE8(void) +{ + if (gSprites[gBattlerSpriteIds[gActiveBattler]].animEnded == TRUE + && gSprites[gBattlerSpriteIds[gActiveBattler]].pos2.x == 0) + { + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].flag_x80) + { + sub_80F1720(gActiveBattler, &gEnemyParty[gBattlerPartyIndexes[gActiveBattler]]); + } + else if (gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_1_x1) + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].flag_x80 = 0; + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_1_x1 = 0; + FreeSpriteTilesByTag(0x27F9); + FreeSpritePaletteByTag(0x27F9); + OpponentBufferExecCompleted(); + } + } +} + +static void CompleteOnHealthbarDone(void) +{ + s16 hpValue = MoveBattleBar(gActiveBattler, gHealthboxSpriteIds[gActiveBattler], HEALTH_BAR, 0); + + SetHealthboxSpriteVisible(gHealthboxSpriteIds[gActiveBattler]); + if (hpValue != -1) + { + UpdateHpTextInHealthbox(gHealthboxSpriteIds[gActiveBattler], hpValue, HP_CURRENT); + } + else + { + if (!sub_80EB2E0(1) && (gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE)) + { + sub_80EB2F4(1); + gBattlerControllerFuncs[gActiveBattler] = sub_80E8570; + } + else + { + OpponentBufferExecCompleted(); + } + } +} + +static void sub_803612C(void) +{ + if (!gSprites[gBattlerSpriteIds[gActiveBattler]].inUse) + { + SetHealthboxSpriteInvisible(gHealthboxSpriteIds[gActiveBattler]); + OpponentBufferExecCompleted(); + } +} + +static void sub_8036170(void) +{ + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].specialAnimActive) + { + FreeSpriteOamMatrix(&gSprites[gBattlerSpriteIds[gActiveBattler]]); + DestroySprite(&gSprites[gBattlerSpriteIds[gActiveBattler]]); + HideBattlerShadowSprite(gActiveBattler); + SetHealthboxSpriteInvisible(gHealthboxSpriteIds[gActiveBattler]); + OpponentBufferExecCompleted(); + } +} + +static void CompleteOnInactiveTextPrinter(void) +{ + if (!IsTextPrinterActive(0)) + OpponentBufferExecCompleted(); +} + +static void DoHitAnimBlinkSpriteEffect(void) +{ + u8 spriteId = gBattlerSpriteIds[gActiveBattler]; + + if (gSprites[spriteId].data[1] == 32) + { + gSprites[spriteId].data[1] = 0; + gSprites[spriteId].invisible = FALSE; + gDoingBattleAnim = FALSE; + OpponentBufferExecCompleted(); + } + else + { + if ((gSprites[spriteId].data[1] % 4) == 0) + gSprites[spriteId].invisible ^= 1; + ++gSprites[spriteId].data[1]; + } +} + +static void sub_8036278(void) +{ + if (gSprites[gHealthboxSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy) + { + if (gBattleSpritesDataPtr->battlerData[gActiveBattler].behindSubstitute) + InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, gActiveBattler, B_ANIM_MON_TO_SUBSTITUTE); + gBattlerControllerFuncs[gActiveBattler] = sub_80362E8; + } +} + +static void sub_80362E8(void) +{ + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].specialAnimActive && !IsCryPlayingOrClearCrySongs()) + { + m4aMPlayVolumeControl(&gMPlayInfo_BGM, 0xFFFF, 0x100); + OpponentBufferExecCompleted(); + } +} + +static void sub_8036334(void) +{ + if (gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_1_x1) + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].flag_x80 = 0; + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_1_x1 = 0; + FreeSpriteTilesByTag(0x27F9); + FreeSpritePaletteByTag(0x27F9); + StartSpriteAnim(&gSprites[gBattlerSpriteIds[gActiveBattler]], 0); + UpdateHealthboxAttribute(gHealthboxSpriteIds[gActiveBattler], &gEnemyParty[gBattlerPartyIndexes[gActiveBattler]], HEALTHBOX_ALL); + sub_804BD94(gActiveBattler); + SetHealthboxSpriteVisible(gHealthboxSpriteIds[gActiveBattler]); + CopyBattleSpriteInvisibility(gActiveBattler); + gBattlerControllerFuncs[gActiveBattler] = sub_8036278; + } +} + +static void sub_8036408(void) +{ + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].flag_x80 && !gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].ballAnimActive) + sub_80F1720(gActiveBattler, &gEnemyParty[gBattlerPartyIndexes[gActiveBattler]]); + if (gSprites[gUnknown_3004FFC[gActiveBattler]].callback == SpriteCallbackDummy + && !gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].ballAnimActive) + { + DestroySprite(&gSprites[gUnknown_3004FFC[gActiveBattler]]); + SetBattlerShadowSpriteCallback(gActiveBattler, GetMonData(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_SPECIES)); + gBattlerControllerFuncs[gActiveBattler] = sub_8036334; + } +} + +static void CompleteOnFinishedStatusAnimation(void) +{ + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].statusAnimActive) + OpponentBufferExecCompleted(); +} + +static void CompleteOnFinishedBattleAnimation(void) +{ + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animFromTableActive) + OpponentBufferExecCompleted(); +} + +void OpponentBufferExecCompleted(void) +{ + gBattlerControllerFuncs[gActiveBattler] = OpponentBufferRunCommand; + gBattleControllerExecFlags &= ~gBitTable[gActiveBattler]; +} + +static void OpponentHandleGetMonData(void) +{ + u8 monData[sizeof(struct Pokemon) * 2 + 56]; // this allows to get full data of two pokemon, trying to get more will result in overwriting data + u32 size = 0; + u8 monToCheck; + s32 i; + + if (!gBattleBufferA[gActiveBattler][2]) + { + size += GetOpponentMonData(gBattlerPartyIndexes[gActiveBattler], monData); + } + else + { + monToCheck = gBattleBufferA[gActiveBattler][2]; + for (i = 0; i < PARTY_SIZE; ++i) + { + if (monToCheck & 1) + size += GetOpponentMonData(i, monData + size); + monToCheck >>= 1; + } + } + BtlController_EmitDataTransfer(1, size, monData); + OpponentBufferExecCompleted(); +} + +static u32 GetOpponentMonData(u8 monId, u8 *dst) +{ + struct BattlePokemon battleMon; + struct MovePpInfo moveData; + u8 nickname[20]; + u8 *src; + s16 data16; + u32 data32; + s32 size = 0; + + switch (gBattleBufferA[gActiveBattler][1]) + { + case REQUEST_ALL_BATTLE: + battleMon.species = GetMonData(&gEnemyParty[monId], MON_DATA_SPECIES); + battleMon.item = GetMonData(&gEnemyParty[monId], MON_DATA_HELD_ITEM); + for (size = 0; size < MAX_MON_MOVES; ++size) + { + battleMon.moves[size] = GetMonData(&gEnemyParty[monId], MON_DATA_MOVE1 + size); + battleMon.pp[size] = GetMonData(&gEnemyParty[monId], MON_DATA_PP1 + size); + } + battleMon.ppBonuses = GetMonData(&gEnemyParty[monId], MON_DATA_PP_BONUSES); + battleMon.friendship = GetMonData(&gEnemyParty[monId], MON_DATA_FRIENDSHIP); + battleMon.experience = GetMonData(&gEnemyParty[monId], MON_DATA_EXP); + battleMon.hpIV = GetMonData(&gEnemyParty[monId], MON_DATA_HP_IV); + battleMon.attackIV = GetMonData(&gEnemyParty[monId], MON_DATA_ATK_IV); + battleMon.defenseIV = GetMonData(&gEnemyParty[monId], MON_DATA_DEF_IV); + battleMon.speedIV = GetMonData(&gEnemyParty[monId], MON_DATA_SPEED_IV); + battleMon.spAttackIV = GetMonData(&gEnemyParty[monId], MON_DATA_SPATK_IV); + battleMon.spDefenseIV = GetMonData(&gEnemyParty[monId], MON_DATA_SPDEF_IV); + battleMon.personality = GetMonData(&gEnemyParty[monId], MON_DATA_PERSONALITY); + battleMon.status1 = GetMonData(&gEnemyParty[monId], MON_DATA_STATUS); + battleMon.level = GetMonData(&gEnemyParty[monId], MON_DATA_LEVEL); + battleMon.hp = GetMonData(&gEnemyParty[monId], MON_DATA_HP); + battleMon.maxHP = GetMonData(&gEnemyParty[monId], MON_DATA_MAX_HP); + battleMon.attack = GetMonData(&gEnemyParty[monId], MON_DATA_ATK); + battleMon.defense = GetMonData(&gEnemyParty[monId], MON_DATA_DEF); + battleMon.speed = GetMonData(&gEnemyParty[monId], MON_DATA_SPEED); + battleMon.spAttack = GetMonData(&gEnemyParty[monId], MON_DATA_SPATK); + battleMon.spDefense = GetMonData(&gEnemyParty[monId], MON_DATA_SPDEF); + battleMon.isEgg = GetMonData(&gEnemyParty[monId], MON_DATA_IS_EGG); + battleMon.abilityNum = GetMonData(&gEnemyParty[monId], MON_DATA_ABILITY_NUM); + battleMon.otId = GetMonData(&gEnemyParty[monId], MON_DATA_OT_ID); + GetMonData(&gEnemyParty[monId], MON_DATA_NICKNAME, nickname); + StringCopy10(battleMon.nickname, nickname); + GetMonData(&gEnemyParty[monId], MON_DATA_OT_NAME, battleMon.otName); + src = (u8 *)&battleMon; + for (size = 0; size < sizeof(battleMon); ++size) + dst[size] = src[size]; + break; + case REQUEST_SPECIES_BATTLE: + data16 = GetMonData(&gEnemyParty[monId], MON_DATA_SPECIES); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_HELDITEM_BATTLE: + data16 = GetMonData(&gEnemyParty[monId], MON_DATA_HELD_ITEM); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_MOVES_PP_BATTLE: + for (size = 0; size < MAX_MON_MOVES; ++size) + { + moveData.moves[size] = GetMonData(&gEnemyParty[monId], MON_DATA_MOVE1 + size); + moveData.pp[size] = GetMonData(&gEnemyParty[monId], MON_DATA_PP1 + size); + } + moveData.ppBonuses = GetMonData(&gEnemyParty[monId], MON_DATA_PP_BONUSES); + src = (u8 *)(&moveData); + for (size = 0; size < sizeof(moveData); ++size) + dst[size] = src[size]; + break; + case REQUEST_MOVE1_BATTLE: + case REQUEST_MOVE2_BATTLE: + case REQUEST_MOVE3_BATTLE: + case REQUEST_MOVE4_BATTLE: + data16 = GetMonData(&gEnemyParty[monId], MON_DATA_MOVE1 + gBattleBufferA[gActiveBattler][1] - REQUEST_MOVE1_BATTLE); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_PP_DATA_BATTLE: + for (size = 0; size < MAX_MON_MOVES; ++size) + dst[size] = GetMonData(&gEnemyParty[monId], MON_DATA_PP1 + size); + dst[size] = GetMonData(&gEnemyParty[monId], MON_DATA_PP_BONUSES); + ++size; + break; + case REQUEST_PPMOVE1_BATTLE: + case REQUEST_PPMOVE2_BATTLE: + case REQUEST_PPMOVE3_BATTLE: + case REQUEST_PPMOVE4_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_PP1 + gBattleBufferA[gActiveBattler][1] - REQUEST_PPMOVE1_BATTLE); + size = 1; + break; + case REQUEST_OTID_BATTLE: + data32 = GetMonData(&gEnemyParty[monId], MON_DATA_OT_ID); + dst[0] = (data32 & 0x000000FF); + dst[1] = (data32 & 0x0000FF00) >> 8; + dst[2] = (data32 & 0x00FF0000) >> 16; + size = 3; + break; + case REQUEST_EXP_BATTLE: + data32 = GetMonData(&gEnemyParty[monId], MON_DATA_EXP); + dst[0] = (data32 & 0x000000FF); + dst[1] = (data32 & 0x0000FF00) >> 8; + dst[2] = (data32 & 0x00FF0000) >> 16; + size = 3; + break; + case REQUEST_HP_EV_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_HP_EV); + size = 1; + break; + case REQUEST_ATK_EV_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_ATK_EV); + size = 1; + break; + case REQUEST_DEF_EV_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_DEF_EV); + size = 1; + break; + case REQUEST_SPEED_EV_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_SPEED_EV); + size = 1; + break; + case REQUEST_SPATK_EV_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_SPATK_EV); + size = 1; + break; + case REQUEST_SPDEF_EV_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_SPDEF_EV); + size = 1; + break; + case REQUEST_FRIENDSHIP_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_FRIENDSHIP); + size = 1; + break; + case REQUEST_POKERUS_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_POKERUS); + size = 1; + break; + case REQUEST_MET_LOCATION_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_MET_LOCATION); + size = 1; + break; + case REQUEST_MET_LEVEL_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_MET_LEVEL); + size = 1; + break; + case REQUEST_MET_GAME_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_MET_GAME); + size = 1; + break; + case REQUEST_POKEBALL_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_POKEBALL); + size = 1; + break; + case REQUEST_ALL_IVS_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_HP_IV); + dst[1] = GetMonData(&gEnemyParty[monId], MON_DATA_ATK_IV); + dst[2] = GetMonData(&gEnemyParty[monId], MON_DATA_DEF_IV); + dst[3] = GetMonData(&gEnemyParty[monId], MON_DATA_SPEED_IV); + dst[4] = GetMonData(&gEnemyParty[monId], MON_DATA_SPATK_IV); + dst[5] = GetMonData(&gEnemyParty[monId], MON_DATA_SPDEF_IV); + size = 6; + break; + case REQUEST_HP_IV_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_HP_IV); + size = 1; + break; + case REQUEST_ATK_IV_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_ATK_IV); + size = 1; + break; + case REQUEST_DEF_IV_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_DEF_IV); + size = 1; + break; + case REQUEST_SPEED_IV_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_SPEED_IV); + size = 1; + break; + case REQUEST_SPATK_IV_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_SPATK_IV); + size = 1; + break; + case REQUEST_SPDEF_IV_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_SPDEF_IV); + size = 1; + break; + case REQUEST_PERSONALITY_BATTLE: + data32 = GetMonData(&gEnemyParty[monId], MON_DATA_PERSONALITY); + dst[0] = (data32 & 0x000000FF); + dst[1] = (data32 & 0x0000FF00) >> 8; + dst[2] = (data32 & 0x00FF0000) >> 16; + dst[3] = (data32 & 0xFF000000) >> 24; + size = 4; + break; + case REQUEST_CHECKSUM_BATTLE: + data16 = GetMonData(&gEnemyParty[monId], MON_DATA_CHECKSUM); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_STATUS_BATTLE: + data32 = GetMonData(&gEnemyParty[monId], MON_DATA_STATUS); + dst[0] = (data32 & 0x000000FF); + dst[1] = (data32 & 0x0000FF00) >> 8; + dst[2] = (data32 & 0x00FF0000) >> 16; + dst[3] = (data32 & 0xFF000000) >> 24; + size = 4; + break; + case REQUEST_LEVEL_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_LEVEL); + size = 1; + break; + case REQUEST_HP_BATTLE: + data16 = GetMonData(&gEnemyParty[monId], MON_DATA_HP); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_MAX_HP_BATTLE: + data16 = GetMonData(&gEnemyParty[monId], MON_DATA_MAX_HP); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_ATK_BATTLE: + data16 = GetMonData(&gEnemyParty[monId], MON_DATA_ATK); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_DEF_BATTLE: + data16 = GetMonData(&gEnemyParty[monId], MON_DATA_DEF); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_SPEED_BATTLE: + data16 = GetMonData(&gEnemyParty[monId], MON_DATA_SPEED); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_SPATK_BATTLE: + data16 = GetMonData(&gEnemyParty[monId], MON_DATA_SPATK); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_SPDEF_BATTLE: + data16 = GetMonData(&gEnemyParty[monId], MON_DATA_SPDEF); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_COOL_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_COOL); + size = 1; + break; + case REQUEST_BEAUTY_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_BEAUTY); + size = 1; + break; + case REQUEST_CUTE_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_CUTE); + size = 1; + break; + case REQUEST_SMART_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_SMART); + size = 1; + break; + case REQUEST_TOUGH_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_TOUGH); + size = 1; + break; + case REQUEST_SHEEN_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_SHEEN); + size = 1; + break; + case REQUEST_COOL_RIBBON_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_COOL_RIBBON); + size = 1; + break; + case REQUEST_BEAUTY_RIBBON_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_BEAUTY_RIBBON); + size = 1; + break; + case REQUEST_CUTE_RIBBON_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_CUTE_RIBBON); + size = 1; + break; + case REQUEST_SMART_RIBBON_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_SMART_RIBBON); + size = 1; + break; + case REQUEST_TOUGH_RIBBON_BATTLE: + dst[0] = GetMonData(&gEnemyParty[monId], MON_DATA_TOUGH_RIBBON); + size = 1; + break; + } + return size; +} + +static void OpponentHandleGetRawMonData(void) +{ + struct BattlePokemon battleMon; + u8 *src = (u8 *)&gEnemyParty[gBattlerPartyIndexes[gActiveBattler]] + gBattleBufferA[gActiveBattler][1]; + u8 *dst = (u8 *)&battleMon + gBattleBufferA[gActiveBattler][1]; + u8 i; + + for (i = 0; i < gBattleBufferA[gActiveBattler][2]; ++i) + dst[i] = src[i]; + BtlController_EmitDataTransfer(1, gBattleBufferA[gActiveBattler][2], dst); + OpponentBufferExecCompleted(); +} + +static void OpponentHandleSetMonData(void) +{ + u8 monToCheck; + u8 i; + + if (gBattleBufferA[gActiveBattler][2] == 0) + { + SetOpponentMonData(gBattlerPartyIndexes[gActiveBattler]); + } + else + { + monToCheck = gBattleBufferA[gActiveBattler][2]; + for (i = 0; i < PARTY_SIZE; ++i) + { + if (monToCheck & 1) + SetOpponentMonData(i); + monToCheck >>= 1; + } + } + OpponentBufferExecCompleted(); +} + +static void SetOpponentMonData(u8 monId) +{ + struct BattlePokemon *battlePokemon = (struct BattlePokemon *)&gBattleBufferA[gActiveBattler][3]; + struct MovePpInfo *moveData = (struct MovePpInfo *)&gBattleBufferA[gActiveBattler][3]; + s32 i; + + switch (gBattleBufferA[gActiveBattler][1]) + { + case REQUEST_ALL_BATTLE: + { + u8 iv; + + SetMonData(&gEnemyParty[monId], MON_DATA_SPECIES, &battlePokemon->species); + SetMonData(&gEnemyParty[monId], MON_DATA_HELD_ITEM, &battlePokemon->item); + for (i = 0; i < MAX_MON_MOVES; ++i) + { + SetMonData(&gEnemyParty[monId], MON_DATA_MOVE1 + i, &battlePokemon->moves[i]); + SetMonData(&gEnemyParty[monId], MON_DATA_PP1 + i, &battlePokemon->pp[i]); + } + SetMonData(&gEnemyParty[monId], MON_DATA_PP_BONUSES, &battlePokemon->ppBonuses); + SetMonData(&gEnemyParty[monId], MON_DATA_FRIENDSHIP, &battlePokemon->friendship); + SetMonData(&gEnemyParty[monId], MON_DATA_EXP, &battlePokemon->experience); + iv = battlePokemon->hpIV; + SetMonData(&gEnemyParty[monId], MON_DATA_HP_IV, &iv); + iv = battlePokemon->attackIV; + SetMonData(&gEnemyParty[monId], MON_DATA_ATK_IV, &iv); + iv = battlePokemon->defenseIV; + SetMonData(&gEnemyParty[monId], MON_DATA_DEF_IV, &iv); + iv = battlePokemon->speedIV; + SetMonData(&gEnemyParty[monId], MON_DATA_SPEED_IV, &iv); + iv = battlePokemon->spAttackIV; + SetMonData(&gEnemyParty[monId], MON_DATA_SPATK_IV, &iv); + iv = battlePokemon->spDefenseIV; + SetMonData(&gEnemyParty[monId], MON_DATA_SPDEF_IV, &iv); + SetMonData(&gEnemyParty[monId], MON_DATA_PERSONALITY, &battlePokemon->personality); + SetMonData(&gEnemyParty[monId], MON_DATA_STATUS, &battlePokemon->status1); + SetMonData(&gEnemyParty[monId], MON_DATA_LEVEL, &battlePokemon->level); + SetMonData(&gEnemyParty[monId], MON_DATA_HP, &battlePokemon->hp); + SetMonData(&gEnemyParty[monId], MON_DATA_MAX_HP, &battlePokemon->maxHP); + SetMonData(&gEnemyParty[monId], MON_DATA_ATK, &battlePokemon->attack); + SetMonData(&gEnemyParty[monId], MON_DATA_DEF, &battlePokemon->defense); + SetMonData(&gEnemyParty[monId], MON_DATA_SPEED, &battlePokemon->speed); + SetMonData(&gEnemyParty[monId], MON_DATA_SPATK, &battlePokemon->spAttack); + SetMonData(&gEnemyParty[monId], MON_DATA_SPDEF, &battlePokemon->spDefense); + } + break; + case REQUEST_SPECIES_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_SPECIES, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_HELDITEM_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_HELD_ITEM, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_MOVES_PP_BATTLE: + for (i = 0; i < MAX_MON_MOVES; ++i) + { + SetMonData(&gEnemyParty[monId], MON_DATA_MOVE1 + i, &moveData->moves[i]); + SetMonData(&gEnemyParty[monId], MON_DATA_PP1 + i, &moveData->pp[i]); + } + SetMonData(&gEnemyParty[monId], MON_DATA_PP_BONUSES, &moveData->ppBonuses); + break; + case REQUEST_MOVE1_BATTLE: + case REQUEST_MOVE2_BATTLE: + case REQUEST_MOVE3_BATTLE: + case REQUEST_MOVE4_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_MOVE1 + gBattleBufferA[gActiveBattler][1] - REQUEST_MOVE1_BATTLE, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_PP_DATA_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_PP1, &gBattleBufferA[gActiveBattler][3]); + SetMonData(&gEnemyParty[monId], MON_DATA_PP2, &gBattleBufferA[gActiveBattler][4]); + SetMonData(&gEnemyParty[monId], MON_DATA_PP3, &gBattleBufferA[gActiveBattler][5]); + SetMonData(&gEnemyParty[monId], MON_DATA_PP4, &gBattleBufferA[gActiveBattler][6]); + SetMonData(&gEnemyParty[monId], MON_DATA_PP_BONUSES, &gBattleBufferA[gActiveBattler][7]); + break; + case REQUEST_PPMOVE1_BATTLE: + case REQUEST_PPMOVE2_BATTLE: + case REQUEST_PPMOVE3_BATTLE: + case REQUEST_PPMOVE4_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_PP1 + gBattleBufferA[gActiveBattler][1] - REQUEST_PPMOVE1_BATTLE, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_OTID_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_OT_ID, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_EXP_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_EXP, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_HP_EV_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_HP_EV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_ATK_EV_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_ATK_EV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_DEF_EV_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_DEF_EV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SPEED_EV_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_SPEED_EV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SPATK_EV_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_SPATK_EV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SPDEF_EV_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_SPDEF_EV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_FRIENDSHIP_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_FRIENDSHIP, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_POKERUS_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_POKERUS, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_MET_LOCATION_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_MET_LOCATION, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_MET_LEVEL_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_MET_LEVEL, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_MET_GAME_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_MET_GAME, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_POKEBALL_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_POKEBALL, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_ALL_IVS_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_HP_IV, &gBattleBufferA[gActiveBattler][3]); + SetMonData(&gEnemyParty[monId], MON_DATA_ATK_IV, &gBattleBufferA[gActiveBattler][4]); + SetMonData(&gEnemyParty[monId], MON_DATA_DEF_IV, &gBattleBufferA[gActiveBattler][5]); + SetMonData(&gEnemyParty[monId], MON_DATA_SPEED_IV, &gBattleBufferA[gActiveBattler][6]); + SetMonData(&gEnemyParty[monId], MON_DATA_SPATK_IV, &gBattleBufferA[gActiveBattler][7]); + SetMonData(&gEnemyParty[monId], MON_DATA_SPDEF_IV, &gBattleBufferA[gActiveBattler][8]); + break; + case REQUEST_HP_IV_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_HP_IV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_ATK_IV_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_ATK_IV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_DEF_IV_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_DEF_IV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SPEED_IV_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_SPEED_IV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SPATK_IV_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_SPATK_IV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SPDEF_IV_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_SPDEF_IV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_PERSONALITY_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_PERSONALITY, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_CHECKSUM_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_CHECKSUM, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_STATUS_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_STATUS, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_LEVEL_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_LEVEL, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_HP_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_HP, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_MAX_HP_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_MAX_HP, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_ATK_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_ATK, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_DEF_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_DEF, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SPEED_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_SPEED, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SPATK_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_SPATK, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SPDEF_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_SPDEF, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_COOL_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_COOL, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_BEAUTY_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_BEAUTY, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_CUTE_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_CUTE, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SMART_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_SMART, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_TOUGH_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_TOUGH, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SHEEN_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_SHEEN, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_COOL_RIBBON_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_COOL_RIBBON, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_BEAUTY_RIBBON_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_BEAUTY_RIBBON, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_CUTE_RIBBON_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_CUTE_RIBBON, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SMART_RIBBON_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_SMART_RIBBON, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_TOUGH_RIBBON_BATTLE: + SetMonData(&gEnemyParty[monId], MON_DATA_TOUGH_RIBBON, &gBattleBufferA[gActiveBattler][3]); + break; + } +} + +static void OpponentHandleSetRawMonData(void) +{ + u8 *dst = (u8 *)&gEnemyParty[gBattlerPartyIndexes[gActiveBattler]] + gBattleBufferA[gActiveBattler][1]; + u8 i; + + for (i = 0; i < gBattleBufferA[gActiveBattler][2]; ++i) + dst[i] = gBattleBufferA[gActiveBattler][3 + i]; + OpponentBufferExecCompleted(); +} + +static void OpponentHandleLoadMonSprite(void) +{ + u16 species = GetMonData(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_SPECIES); + u32 y; + + if (gBattleTypeFlags & BATTLE_TYPE_GHOST) + { + DecompressGhostFrontPic(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler]], gActiveBattler); + y = GetGhostSpriteDefault_Y(gActiveBattler); + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].flag_x80 = 1; + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_1_x1 = 1; + } + else + { + BattleLoadOpponentMonSpriteGfx(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler]], gActiveBattler); + y = GetBattlerSpriteDefault_Y(gActiveBattler); + } + SetMultiuseSpriteTemplateToPokemon(species, GetBattlerPosition(gActiveBattler)); + gBattlerSpriteIds[gActiveBattler] = CreateSprite(&gMultiuseSpriteTemplate, + GetBattlerSpriteCoord(gActiveBattler, 2), + y, + GetBattlerSpriteSubpriority(gActiveBattler)); + gSprites[gBattlerSpriteIds[gActiveBattler]].pos2.x = -240; + gSprites[gBattlerSpriteIds[gActiveBattler]].data[0] = gActiveBattler; + gSprites[gBattlerSpriteIds[gActiveBattler]].data[2] = species; + gSprites[gBattlerSpriteIds[gActiveBattler]].oam.paletteNum = gActiveBattler; + StartSpriteAnim(&gSprites[gBattlerSpriteIds[gActiveBattler]], gBattleMonForms[gActiveBattler]); + if (!(gBattleTypeFlags & BATTLE_TYPE_GHOST)) + SetBattlerShadowSpriteCallback(gActiveBattler, GetMonData(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_SPECIES)); + gBattlerControllerFuncs[gActiveBattler] = sub_8035FE8; +} + +static void OpponentHandleSwitchInAnim(void) +{ + *(gBattleStruct->monToSwitchIntoId + gActiveBattler) = 6; + gBattlerPartyIndexes[gActiveBattler] = gBattleBufferA[gActiveBattler][1]; + sub_8037A28(gActiveBattler, gBattleBufferA[gActiveBattler][2]); + gBattlerControllerFuncs[gActiveBattler] = sub_8036408; +} + +static void sub_8037A28(u8 battlerId, bool8 dontClearSubstituteBit) +{ + u16 species; + + ClearTemporarySpeciesSpriteData(battlerId, dontClearSubstituteBit); + gBattlerPartyIndexes[battlerId] = gBattleBufferA[battlerId][1]; + species = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerId]], MON_DATA_SPECIES); + gUnknown_3004FFC[battlerId] = CreateInvisibleSpriteWithCallback(sub_8033E3C); + BattleLoadOpponentMonSpriteGfx(&gEnemyParty[gBattlerPartyIndexes[battlerId]], battlerId); + SetMultiuseSpriteTemplateToPokemon(species, GetBattlerPosition(battlerId)); + gBattlerSpriteIds[battlerId] = CreateSprite(&gMultiuseSpriteTemplate, + GetBattlerSpriteCoord(battlerId, 2), + GetBattlerSpriteDefault_Y(battlerId), + GetBattlerSpriteSubpriority(battlerId)); + gSprites[gBattlerSpriteIds[battlerId]].data[0] = battlerId; + gSprites[gBattlerSpriteIds[battlerId]].data[2] = species; + gSprites[gUnknown_3004FFC[battlerId]].data[1] = gBattlerSpriteIds[battlerId]; + gSprites[gBattlerSpriteIds[battlerId]].oam.paletteNum = battlerId; + StartSpriteAnim(&gSprites[gBattlerSpriteIds[battlerId]], gBattleMonForms[battlerId]); + gSprites[gBattlerSpriteIds[battlerId]].invisible = TRUE; + gSprites[gBattlerSpriteIds[battlerId]].callback = SpriteCallbackDummy; + gSprites[gUnknown_3004FFC[battlerId]].data[0] = DoPokeballSendOutAnimation(0, POKEBALL_OPPONENT_SENDOUT); +} + +static void OpponentHandleReturnMonToBall(void) +{ + if (!gBattleBufferA[gActiveBattler][1]) + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 0; + gBattlerControllerFuncs[gActiveBattler] = DoSwitchOutAnimation; + } + else + { + FreeSpriteOamMatrix(&gSprites[gBattlerSpriteIds[gActiveBattler]]); + DestroySprite(&gSprites[gBattlerSpriteIds[gActiveBattler]]); + HideBattlerShadowSprite(gActiveBattler); + SetHealthboxSpriteInvisible(gHealthboxSpriteIds[gActiveBattler]); + OpponentBufferExecCompleted(); + } +} + +static void DoSwitchOutAnimation(void) +{ + switch (gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState) + { + case 0: + if (gBattleSpritesDataPtr->battlerData[gActiveBattler].behindSubstitute) + InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, gActiveBattler, B_ANIM_SUBSTITUTE_TO_MON); + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 1; + break; + case 1: + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].specialAnimActive) + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 0; + InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, gActiveBattler, B_ANIM_SWITCH_OUT_OPPONENT_MON); + gBattlerControllerFuncs[gActiveBattler] = sub_8036170; + } + break; + } +} + +static void OpponentHandleDrawTrainerPic(void) +{ + u32 trainerPicId; + + if (gTrainerBattleOpponent_A == 0x400) + trainerPicId = GetSecretBaseTrainerPicIndex(); + else if (gBattleTypeFlags & BATTLE_TYPE_BATTLE_TOWER) + trainerPicId = GetBattleTowerTrainerFrontSpriteId(); + else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_TOWER) + trainerPicId = GetTrainerTowerTrainerFrontSpriteId(); + else if (gBattleTypeFlags & BATTLE_TYPE_EREADER_TRAINER) + trainerPicId = GetEreaderTrainerFrontSpriteId(); + else + trainerPicId = gTrainers[gTrainerBattleOpponent_A].trainerPic; + DecompressTrainerFrontPic(trainerPicId, gActiveBattler); + SetMultiuseSpriteTemplateToTrainerBack(trainerPicId, GetBattlerPosition(gActiveBattler)); + gBattlerSpriteIds[gActiveBattler] = CreateSprite(&gMultiuseSpriteTemplate, + 176, + (8 - gTrainerFrontPicCoords[trainerPicId].size) * 4 + 40, + GetBattlerSpriteSubpriority(gActiveBattler)); + gSprites[gBattlerSpriteIds[gActiveBattler]].pos2.x = -240; + gSprites[gBattlerSpriteIds[gActiveBattler]].data[0] = 2; + gSprites[gBattlerSpriteIds[gActiveBattler]].oam.paletteNum = IndexOfSpritePaletteTag(gTrainerFrontPicPaletteTable[trainerPicId].tag); + gSprites[gBattlerSpriteIds[gActiveBattler]].data[5] = gSprites[gBattlerSpriteIds[gActiveBattler]].oam.tileNum; + gSprites[gBattlerSpriteIds[gActiveBattler]].oam.tileNum = GetSpriteTileStartByTag(gTrainerFrontPicTable[trainerPicId].tag); + gSprites[gBattlerSpriteIds[gActiveBattler]].oam.affineParam = trainerPicId; + gSprites[gBattlerSpriteIds[gActiveBattler]].callback = sub_8033EEC; + gBattlerControllerFuncs[gActiveBattler] = CompleteOnBattlerSpriteCallbackDummy; +} + +static void OpponentHandleTrainerSlide(void) +{ + u32 trainerPicId; + + if (gTrainerBattleOpponent_A == 0x400) + trainerPicId = GetSecretBaseTrainerPicIndex(); + else if (gBattleTypeFlags & BATTLE_TYPE_BATTLE_TOWER) + trainerPicId = GetBattleTowerTrainerFrontSpriteId(); + else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_TOWER) + trainerPicId = GetTrainerTowerTrainerFrontSpriteId(); + else if (gBattleTypeFlags & BATTLE_TYPE_EREADER_TRAINER) + trainerPicId = GetEreaderTrainerFrontSpriteId(); + else + trainerPicId = gTrainers[gTrainerBattleOpponent_A].trainerPic; + DecompressTrainerFrontPic(trainerPicId, gActiveBattler); + SetMultiuseSpriteTemplateToTrainerBack(trainerPicId, GetBattlerPosition(gActiveBattler)); + gBattlerSpriteIds[gActiveBattler] = CreateSprite(&gMultiuseSpriteTemplate, + 176, + (8 - gTrainerFrontPicCoords[trainerPicId].size) * 4 + 40, + 30); + gSprites[gBattlerSpriteIds[gActiveBattler]].pos2.x = 96; + gSprites[gBattlerSpriteIds[gActiveBattler]].pos1.x += 32; + gSprites[gBattlerSpriteIds[gActiveBattler]].data[0] = -2; + gSprites[gBattlerSpriteIds[gActiveBattler]].oam.paletteNum = IndexOfSpritePaletteTag(gTrainerFrontPicPaletteTable[trainerPicId].tag); + gSprites[gBattlerSpriteIds[gActiveBattler]].data[5] = gSprites[gBattlerSpriteIds[gActiveBattler]].oam.tileNum; + gSprites[gBattlerSpriteIds[gActiveBattler]].oam.tileNum = GetSpriteTileStartByTag(gTrainerFrontPicTable[trainerPicId].tag); + gSprites[gBattlerSpriteIds[gActiveBattler]].oam.affineParam = trainerPicId; + gSprites[gBattlerSpriteIds[gActiveBattler]].callback = sub_8033EEC; + gBattlerControllerFuncs[gActiveBattler] = CompleteOnBattlerSpriteCallbackDummy2; +} + +static void OpponentHandleTrainerSlideBack(void) +{ + SetSpritePrimaryCoordsFromSecondaryCoords(&gSprites[gBattlerSpriteIds[gActiveBattler]]); + gSprites[gBattlerSpriteIds[gActiveBattler]].data[0] = 35; + gSprites[gBattlerSpriteIds[gActiveBattler]].data[2] = 280; + gSprites[gBattlerSpriteIds[gActiveBattler]].data[4] = gSprites[gBattlerSpriteIds[gActiveBattler]].pos1.y; + gSprites[gBattlerSpriteIds[gActiveBattler]].callback = StartAnimLinearTranslation; + StoreSpriteCallbackInData6(&gSprites[gBattlerSpriteIds[gActiveBattler]], SpriteCallbackDummy); + gBattlerControllerFuncs[gActiveBattler] = sub_8035B58; +} + +static void OpponentHandleFaintAnimation(void) +{ + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState) + { + if (gBattleSpritesDataPtr->battlerData[gActiveBattler].behindSubstitute) + InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, gActiveBattler, B_ANIM_SUBSTITUTE_TO_MON); + ++gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState; + } + else + { + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].specialAnimActive) + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 0; + PlaySE12WithPanning(SE_POKE_DEAD, SOUND_PAN_TARGET); + gSprites[gBattlerSpriteIds[gActiveBattler]].callback = SpriteCB_FaintOpponentMon; + gBattlerControllerFuncs[gActiveBattler] = sub_803612C; + } + } +} + +static void OpponentHandlePaletteFade(void) +{ + OpponentBufferExecCompleted(); +} + +static void OpponentHandleSuccessBallThrowAnim(void) +{ + OpponentBufferExecCompleted(); +} + +static void OpponentHandleBallThrow(void) +{ + OpponentBufferExecCompleted(); +} + +static void OpponentHandlePause(void) +{ + OpponentBufferExecCompleted(); +} + +static void OpponentHandleMoveAnimation(void) +{ + if (!mplay_80342A4(gActiveBattler)) + { + u16 move = gBattleBufferA[gActiveBattler][1] | (gBattleBufferA[gActiveBattler][2] << 8); + + gAnimMoveTurn = gBattleBufferA[gActiveBattler][3]; + gAnimMovePower = gBattleBufferA[gActiveBattler][4] | (gBattleBufferA[gActiveBattler][5] << 8); + gAnimMoveDmg = gBattleBufferA[gActiveBattler][6] | (gBattleBufferA[gActiveBattler][7] << 8) | (gBattleBufferA[gActiveBattler][8] << 16) | (gBattleBufferA[gActiveBattler][9] << 24); + gAnimFriendship = gBattleBufferA[gActiveBattler][10]; + gWeatherMoveAnim = gBattleBufferA[gActiveBattler][12] | (gBattleBufferA[gActiveBattler][13] << 8); + gAnimDisableStructPtr = (struct DisableStruct *)&gBattleBufferA[gActiveBattler][16]; + gTransformedPersonalities[gActiveBattler] = gAnimDisableStructPtr->transformedMonPersonality; + if (IsMoveWithoutAnimation(move, gAnimMoveTurn)) // always returns FALSE + { + OpponentBufferExecCompleted(); + } + else + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 0; + gBattlerControllerFuncs[gActiveBattler] = OpponentDoMoveAnimation; + } + } +} + +static void OpponentDoMoveAnimation(void) +{ + u16 move = gBattleBufferA[gActiveBattler][1] | (gBattleBufferA[gActiveBattler][2] << 8); + u8 multihit = gBattleBufferA[gActiveBattler][11]; + + switch (gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState) + { + case 0: + if (gBattleSpritesDataPtr->battlerData[gActiveBattler].behindSubstitute + && !gBattleSpritesDataPtr->battlerData[gActiveBattler].flag_x8) + { + gBattleSpritesDataPtr->battlerData[gActiveBattler].flag_x8 = 1; + InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, gActiveBattler, B_ANIM_SUBSTITUTE_TO_MON); + } + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 1; + break; + case 1: + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].specialAnimActive) + { + sub_8035450(0); + DoMoveAnim(move); + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 2; + } + break; + case 2: + gAnimScriptCallback(); + if (!gAnimScriptActive) + { + sub_8035450(1); + if (gBattleSpritesDataPtr->battlerData[gActiveBattler].behindSubstitute && multihit < 2) + { + InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, gActiveBattler, B_ANIM_MON_TO_SUBSTITUTE); + gBattleSpritesDataPtr->battlerData[gActiveBattler].flag_x8 = 0; + } + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 3; + } + break; + case 3: + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].specialAnimActive) + { + CopyAllBattleSpritesInvisibilities(); + TrySetBehindSubstituteSpriteBit(gActiveBattler, gBattleBufferA[gActiveBattler][1] | (gBattleBufferA[gActiveBattler][2] << 8)); + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 0; + OpponentBufferExecCompleted(); + } + break; + } +} + +static void OpponentHandlePrintString(void) +{ + u16 *stringId; + + gBattle_BG0_X = 0; + gBattle_BG0_Y = 0; + stringId = (u16 *)(&gBattleBufferA[gActiveBattler][2]); + BufferStringBattle(*stringId); + if (sub_80D89B0(*stringId)) + BattlePutTextOnWindow(gDisplayedStringBattle, 0x40); + else + BattlePutTextOnWindow(gDisplayedStringBattle, 0); + if (gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE) + { + switch (*stringId) + { + case 0x17F: + gBattlerControllerFuncs[gActiveBattler] = sub_80E85C0; + return; + case 0xE3: + gBattlerControllerFuncs[gActiveBattler] = sub_80E8598; + return; + } + } + gBattlerControllerFuncs[gActiveBattler] = CompleteOnInactiveTextPrinter; +} + +static void OpponentHandlePrintSelectionString(void) +{ + OpponentBufferExecCompleted(); +} + +static void OpponentHandleChooseAction(void) +{ + AI_TrySwitchOrUseItem(); + OpponentBufferExecCompleted(); +} + +static void OpponentHandleUnknownYesNoBox(void) +{ + OpponentBufferExecCompleted(); +} + +static void OpponentHandleChooseMove(void) +{ + u8 chosenMoveId; + struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleBufferA[gActiveBattler][4]); + + if (gBattleTypeFlags & (BATTLE_TYPE_TRAINER | BATTLE_TYPE_FIRST_BATTLE | BATTLE_TYPE_SAFARI | BATTLE_TYPE_ROAMER)) + { + + BattleAI_SetupAIData(); + chosenMoveId = BattleAI_ChooseMoveOrAction(); + + switch (chosenMoveId) + { + case AI_CHOICE_WATCH: + BtlController_EmitTwoReturnValues(1, B_ACTION_SAFARI_WATCH_CAREFULLY, 0); + break; + case AI_CHOICE_FLEE: + BtlController_EmitTwoReturnValues(1, B_ACTION_RUN, 0); + break; + default: + if (gBattleMoves[moveInfo->moves[chosenMoveId]].target & (MOVE_TARGET_USER_OR_SELECTED | MOVE_TARGET_USER)) + gBattlerTarget = gActiveBattler; + if (gBattleMoves[moveInfo->moves[chosenMoveId]].target & MOVE_TARGET_BOTH) + { + gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); + if (gAbsentBattlerFlags & gBitTable[gBattlerTarget]) + gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT); + } + BtlController_EmitTwoReturnValues(1, 10, (chosenMoveId) | (gBattlerTarget << 8)); + break; + } + OpponentBufferExecCompleted(); + } + else + { + u16 move; + + do + { + chosenMoveId = Random() & 3; + move = moveInfo->moves[chosenMoveId]; + } + while (move == MOVE_NONE); + if (gBattleMoves[move].target & (MOVE_TARGET_USER_OR_SELECTED | MOVE_TARGET_USER)) + BtlController_EmitTwoReturnValues(1, 10, (chosenMoveId) | (gActiveBattler << 8)); + else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + BtlController_EmitTwoReturnValues(1, 10, (chosenMoveId) | (GetBattlerAtPosition(Random() & 2) << 8)); + else + BtlController_EmitTwoReturnValues(1, 10, (chosenMoveId) | (GetBattlerAtPosition(B_POSITION_PLAYER_LEFT) << 8)); + + OpponentBufferExecCompleted(); + } +} + +static void OpponentHandleChooseItem(void) +{ + BtlController_EmitOneReturnValue(1, *(gBattleStruct->chosenItem + (gActiveBattler / 2) * 2)); + OpponentBufferExecCompleted(); +} + +static void OpponentHandleChoosePokemon(void) +{ + s32 chosenMonId; + + if (*(gBattleStruct->AI_monToSwitchIntoId + (GetBattlerPosition(gActiveBattler) >> 1)) == PARTY_SIZE) + { + chosenMonId = GetMostSuitableMonToSwitchInto(); + + if (chosenMonId == PARTY_SIZE) + { + s32 battler1, battler2; + + if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE)) + { + battler2 = battler1 = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); + } + else + { + battler1 = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); + battler2 = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); + } + for (chosenMonId = 0; chosenMonId < PARTY_SIZE; ++chosenMonId) + if (GetMonData(&gEnemyParty[chosenMonId], MON_DATA_HP) != 0 + && chosenMonId != gBattlerPartyIndexes[battler1] + && chosenMonId != gBattlerPartyIndexes[battler2]) + break; + } + } + else + { + chosenMonId = *(gBattleStruct->AI_monToSwitchIntoId + (GetBattlerPosition(gActiveBattler) >> 1)); + *(gBattleStruct->AI_monToSwitchIntoId + (GetBattlerPosition(gActiveBattler) >> 1)) = PARTY_SIZE; + } + *(gBattleStruct->monToSwitchIntoId + gActiveBattler) = chosenMonId; + BtlController_EmitChosenMonReturnValue(1, chosenMonId, NULL); + OpponentBufferExecCompleted(); +} + +static void OpponentHandleCmd23(void) +{ + OpponentBufferExecCompleted(); +} + +static void OpponentHandleHealthBarUpdate(void) +{ + s16 hpVal; + + LoadBattleBarGfx(0); + hpVal = (gBattleBufferA[gActiveBattler][3] << 8) | gBattleBufferA[gActiveBattler][2]; + + if (hpVal != INSTANT_HP_BAR_DROP) + { + u32 maxHP = GetMonData(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_MAX_HP); + u32 curHP = GetMonData(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_HP); + + SetBattleBarStruct(gActiveBattler, gHealthboxSpriteIds[gActiveBattler], maxHP, curHP, hpVal); + } + else + { + u32 maxHP = GetMonData(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_MAX_HP); + + SetBattleBarStruct(gActiveBattler, gHealthboxSpriteIds[gActiveBattler], maxHP, 0, hpVal); + } + gBattlerControllerFuncs[gActiveBattler] = CompleteOnHealthbarDone; +} + +static void OpponentHandleExpUpdate(void) +{ + OpponentBufferExecCompleted(); +} + +static void OpponentHandleStatusIconUpdate(void) +{ + if (!mplay_80342A4(gActiveBattler)) + { + u8 battlerId; + + UpdateHealthboxAttribute(gHealthboxSpriteIds[gActiveBattler], &gEnemyParty[gBattlerPartyIndexes[gActiveBattler]], HEALTHBOX_STATUS_ICON); + battlerId = gActiveBattler; + gBattleSpritesDataPtr->healthBoxesData[battlerId].statusAnimActive = 0; + gBattlerControllerFuncs[gActiveBattler] = CompleteOnFinishedStatusAnimation; + } +} + +static void OpponentHandleStatusAnimation(void) +{ + if (!mplay_80342A4(gActiveBattler)) + { + InitAndLaunchChosenStatusAnimation(gBattleBufferA[gActiveBattler][1], + gBattleBufferA[gActiveBattler][2] | (gBattleBufferA[gActiveBattler][3] << 8) | (gBattleBufferA[gActiveBattler][4] << 16) | (gBattleBufferA[gActiveBattler][5] << 24)); + gBattlerControllerFuncs[gActiveBattler] = CompleteOnFinishedStatusAnimation; + } +} + +static void OpponentHandleStatusXor(void) +{ + OpponentBufferExecCompleted(); +} + +static void OpponentHandleDataTransfer(void) +{ + OpponentBufferExecCompleted(); +} + +static void OpponentHandleDMA3Transfer(void) +{ + OpponentBufferExecCompleted(); +} + +static void OpponentHandlePlayBGM(void) +{ + OpponentBufferExecCompleted(); +} + +static void OpponentHandleCmd32(void) +{ + OpponentBufferExecCompleted(); +} + +static void OpponentHandleTwoReturnValues(void) +{ + OpponentBufferExecCompleted(); +} + +static void OpponentHandleChosenMonReturnValue(void) +{ + OpponentBufferExecCompleted(); +} + +static void OpponentHandleOneReturnValue(void) +{ + OpponentBufferExecCompleted(); +} + +static void OpponentHandleOneReturnValue_Duplicate(void) +{ + OpponentBufferExecCompleted(); +} + +static void OpponentHandleCmd37(void) +{ + gUnknown_2022870.field_0 = 0; + OpponentBufferExecCompleted(); +} + +static void OpponentHandleCmd38(void) +{ + gUnknown_2022870.field_0 = gBattleBufferA[gActiveBattler][1]; + OpponentBufferExecCompleted(); +} + +static void OpponentHandleCmd39(void) +{ + gUnknown_2022870.flag_x80 = 0; + OpponentBufferExecCompleted(); +} + +static void OpponentHandleCmd40(void) +{ + gUnknown_2022870.flag_x80 ^= 1; + OpponentBufferExecCompleted(); +} + +static void OpponentHandleHitAnimation(void) +{ + if (gSprites[gBattlerSpriteIds[gActiveBattler]].invisible == TRUE) + { + OpponentBufferExecCompleted(); + } + else + { + gDoingBattleAnim = TRUE; + gSprites[gBattlerSpriteIds[gActiveBattler]].data[1] = 0; + DoHitAnimHealthboxEffect(gActiveBattler); + gBattlerControllerFuncs[gActiveBattler] = DoHitAnimBlinkSpriteEffect; + } +} + +static void OpponentHandleCmd42(void) +{ + OpponentBufferExecCompleted(); +} + +static void OpponentHandlePlaySE(void) +{ + s8 pan; + + if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER) + pan = SOUND_PAN_ATTACKER; + else + pan = SOUND_PAN_TARGET; + PlaySE12WithPanning(gBattleBufferA[gActiveBattler][1] | (gBattleBufferA[gActiveBattler][2] << 8), pan); + OpponentBufferExecCompleted(); +} + +static void OpponentHandlePlayFanfare(void) +{ + PlayFanfare(gBattleBufferA[gActiveBattler][1] | (gBattleBufferA[gActiveBattler][2] << 8)); + OpponentBufferExecCompleted(); +} + +static void OpponentHandleFaintingCry(void) +{ + u16 species = GetMonData(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_SPECIES); + + PlayCry3(species, 25, 5); + OpponentBufferExecCompleted(); +} + +static void OpponentHandleIntroSlide(void) +{ + HandleIntroSlide(gBattleBufferA[gActiveBattler][1]); + gIntroSlideFlags |= 1; + OpponentBufferExecCompleted(); +} + +static void OpponentHandleIntroTrainerBallThrow(void) +{ + u8 paletteNum; + u8 taskId; + + SetSpritePrimaryCoordsFromSecondaryCoords(&gSprites[gBattlerSpriteIds[gActiveBattler]]); + gSprites[gBattlerSpriteIds[gActiveBattler]].data[0] = 35; + gSprites[gBattlerSpriteIds[gActiveBattler]].data[2] = 280; + gSprites[gBattlerSpriteIds[gActiveBattler]].data[4] = gSprites[gBattlerSpriteIds[gActiveBattler]].pos1.y; + gSprites[gBattlerSpriteIds[gActiveBattler]].callback = StartAnimLinearTranslation; + StoreSpriteCallbackInData6(&gSprites[gBattlerSpriteIds[gActiveBattler]], sub_8038D90); + taskId = CreateTask(sub_8038DC4, 5); + gTasks[taskId].data[0] = gActiveBattler; + if (gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].partyStatusSummaryShown) + gTasks[gBattlerStatusSummaryTaskId[gActiveBattler]].func = Task_HidePartyStatusSummary; + gBattleSpritesDataPtr->animationData->field_9_x1 = 1; + gBattlerControllerFuncs[gActiveBattler] = nullsub_17; +} + +static void sub_8038D90(struct Sprite *sprite) +{ + FreeTrainerFrontPicPaletteAndTile(sprite->oam.affineParam); + sprite->oam.tileNum = sprite->data[5]; + FreeSpriteOamMatrix(sprite); + DestroySprite(sprite); +} + +static void sub_8038DC4(u8 taskId) +{ + u8 savedActiveBattler = gActiveBattler; + + gActiveBattler = gTasks[taskId].data[0]; + if (!IsDoubleBattle() || (gBattleTypeFlags & BATTLE_TYPE_MULTI)) + { + gBattleBufferA[gActiveBattler][1] = gBattlerPartyIndexes[gActiveBattler]; + sub_8037A28(gActiveBattler, FALSE); + } + else + { + gBattleBufferA[gActiveBattler][1] = gBattlerPartyIndexes[gActiveBattler]; + sub_8037A28(gActiveBattler, FALSE); + gActiveBattler ^= BIT_FLANK; + gBattleBufferA[gActiveBattler][1] = gBattlerPartyIndexes[gActiveBattler]; + sub_8037A28(gActiveBattler, FALSE); + gActiveBattler ^= BIT_FLANK; + } + gBattlerControllerFuncs[gActiveBattler] = sub_8035DF0; + gActiveBattler = savedActiveBattler; + DestroyTask(taskId); +} + +static void OpponentHandleDrawPartyStatusSummary(void) +{ + if (gBattleBufferA[gActiveBattler][1] && GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER) + { + OpponentBufferExecCompleted(); + } + else + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].partyStatusSummaryShown = 1; + if (gBattleBufferA[gActiveBattler][2]) + { + if (gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_1_x1E <= 1) + { + ++gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_1_x1E; + return; + } + else + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_1_x1E = 0; + } + } + gBattlerStatusSummaryTaskId[gActiveBattler] = CreatePartyStatusSummarySprites(gActiveBattler, + (struct HpAndStatus *)&gBattleBufferA[gActiveBattler][4], + gBattleBufferA[gActiveBattler][1], + gBattleBufferA[gActiveBattler][2]); + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_5 = 0; + if (gBattleBufferA[gActiveBattler][2]) + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_5 = 0x5D; + gBattlerControllerFuncs[gActiveBattler] = sub_8038FBC; + } +} + +static void sub_8038FBC(void) +{ + if (gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_5++ > 0x5C) + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_5 = 0; + OpponentBufferExecCompleted(); + } +} + +static void OpponentHandleHidePartyStatusSummary(void) +{ + if (gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].partyStatusSummaryShown) + gTasks[gBattlerStatusSummaryTaskId[gActiveBattler]].func = Task_HidePartyStatusSummary; + OpponentBufferExecCompleted(); +} + +static void OpponentHandleEndBounceEffect(void) +{ + OpponentBufferExecCompleted(); +} + +static void OpponentHandleSpriteInvisibility(void) +{ + if (IsBattlerSpritePresent(gActiveBattler)) + { + gSprites[gBattlerSpriteIds[gActiveBattler]].invisible = gBattleBufferA[gActiveBattler][1]; + CopyBattleSpriteInvisibility(gActiveBattler); + } + OpponentBufferExecCompleted(); +} + +static void OpponentHandleBattleAnimation(void) +{ + if (!mplay_80342A4(gActiveBattler)) + { + u8 animationId = gBattleBufferA[gActiveBattler][1]; + u16 argument = gBattleBufferA[gActiveBattler][2] | (gBattleBufferA[gActiveBattler][3] << 8); + + if (TryHandleLaunchBattleTableAnimation(gActiveBattler, gActiveBattler, gActiveBattler, animationId, argument)) + OpponentBufferExecCompleted(); + else + gBattlerControllerFuncs[gActiveBattler] = CompleteOnFinishedBattleAnimation; + } +} + +static void OpponentHandleLinkStandbyMsg(void) +{ + OpponentBufferExecCompleted(); +} + +static void OpponentHandleResetActionMoveSelection(void) +{ + OpponentBufferExecCompleted(); +} + +static void OpponentHandleCmd55(void) +{ + if (gBattleTypeFlags & BATTLE_TYPE_LINK && !(gBattleTypeFlags & BATTLE_TYPE_IS_MASTER)) + { + gMain.inBattle = 0; + gMain.callback1 = gPreBattleCallback1; + SetMainCallback2(gMain.savedCallback); + } + OpponentBufferExecCompleted(); +} + +static void OpponentCmdEnd(void) +{ +} diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c new file mode 100644 index 000000000..b0cd92c2e --- /dev/null +++ b/src/battle_controller_player.c @@ -0,0 +1,2969 @@ +#include "global.h" +#include "bg.h" +#include "data.h" +#include "item.h" +#include "item_menu.h" +#include "link.h" +#include "main.h" +#include "m4a.h" +#include "palette.h" +#include "party_menu.h" +#include "pokeball.h" +#include "pokemon.h" +#include "random.h" +#include "sound.h" +#include "strings.h" +#include "string_util.h" +#include "pokemon_special_anim.h" +#include "task.h" +#include "text.h" +#include "util.h" +#include "window.h" +#include "battle.h" +#include "battle_anim.h" +#include "battle_controllers.h" +#include "battle_interface.h" +#include "battle_message.h" +#include "battle_setup.h" +#include "battle_script_commands.h" +#include "reshow_battle_screen.h" +#include "constants/battle_anim.h" +#include "constants/items.h" +#include "constants/moves.h" +#include "constants/songs.h" + +static void PlayerHandleGetMonData(void); +static void PlayerHandleSetMonData(void); +static void PlayerHandleSetRawMonData(void); +static void PlayerHandleLoadMonSprite(void); +static void PlayerHandleSwitchInAnim(void); +static void PlayerHandleReturnMonToBall(void); +static void PlayerHandleDrawTrainerPic(void); +static void PlayerHandleTrainerSlide(void); +static void PlayerHandleTrainerSlideBack(void); +static void PlayerHandleFaintAnimation(void); +static void PlayerHandlePaletteFade(void); +static void PlayerHandleSuccessBallThrowAnim(void); +static void PlayerHandleBallThrowAnim(void); +static void PlayerHandlePause(void); +static void PlayerHandleMoveAnimation(void); +static void PlayerHandlePrintString(void); +static void PlayerHandlePrintSelectionString(void); +static void PlayerHandleChooseAction(void); +static void PlayerHandleUnknownYesNoBox(void); +static void PlayerHandleChooseMove(void); +static void PlayerHandleChooseItem(void); +static void PlayerHandleChoosePokemon(void); +static void PlayerHandleCmd23(void); +static void PlayerHandleHealthBarUpdate(void); +static void PlayerHandleExpUpdate(void); +static void PlayerHandleStatusIconUpdate(void); +static void PlayerHandleStatusAnimation(void); +static void PlayerHandleStatusXor(void); +static void PlayerHandleDataTransfer(void); +static void PlayerHandleDMA3Transfer(void); +static void PlayerHandlePlayBGM(void); +static void PlayerHandleCmd32(void); +static void PlayerHandleTwoReturnValues(void); +static void PlayerHandleChosenMonReturnValue(void); +static void PlayerHandleOneReturnValue(void); +static void PlayerHandleOneReturnValue_Duplicate(void); +static void PlayerHandleCmd37(void); +static void PlayerHandleCmd38(void); +static void PlayerHandleCmd39(void); +static void PlayerHandleCmd40(void); +static void PlayerHandleHitAnimation(void); +static void PlayerHandleCmd42(void); +static void PlayerHandlePlaySE(void); +static void PlayerHandlePlayFanfare(void); +static void PlayerHandleFaintingCry(void); +static void PlayerHandleIntroSlide(void); +static void PlayerHandleIntroTrainerBallThrow(void); +static void PlayerHandleDrawPartyStatusSummary(void); +static void PlayerHandleHidePartyStatusSummary(void); +static void PlayerHandleEndBounceEffect(void); +static void PlayerHandleSpriteInvisibility(void); +static void PlayerHandleBattleAnimation(void); +static void PlayerHandleLinkStandbyMsg(void); +static void PlayerHandleResetActionMoveSelection(void); +static void PlayerHandleCmd55(void); +static void PlayerCmdEnd(void); + +static void PlayerBufferRunCommand(void); +static void HandleInputChooseTarget(void); +static void MoveSelectionDisplayPpNumber(void); +static void MoveSelectionDisplayPpString(void); +static void MoveSelectionDisplayMoveType(void); +static void MoveSelectionDisplayMoveNames(void); +static void HandleMoveSwitching(void); +static void WaitForMonSelection(void); +static void CompleteWhenChoseItem(void); +static void Task_LaunchLvlUpAnim(u8 taskId); +static void Task_PrepareToGiveExpWithExpBar(u8 taskId); +static void DestroyExpTaskAndCompleteOnInactiveTextPrinter(u8 taskId); +static void Task_UpdateLvlInHealthbox(u8 taskId); +static void PrintLinkStandbyMsg(void); +static u32 CopyPlayerMonData(u8 monId, u8 *dst); +static void SetPlayerMonData(u8 monId); +static void DoSwitchOutAnimation(void); +static void PlayerDoMoveAnimation(void); +static void task05_08033660(u8 taskId); +static void sub_8033AC8(void); +static void sub_802FCAC(void); +static void sub_80300F4(u8 taskId); +static void sub_80303A8(u8 taskId); +static void sub_8031FF4(u8 battlerId, bool8 dontClearSubstituteBit); +static void sub_8033830(void); + +static void (*const sPlayerBufferCommands[CONTROLLER_CMDS_COUNT])(void) = +{ + PlayerHandleGetMonData, + PlayerHandleGetRawMonData, + PlayerHandleSetMonData, + PlayerHandleSetRawMonData, + PlayerHandleLoadMonSprite, + PlayerHandleSwitchInAnim, + PlayerHandleReturnMonToBall, + PlayerHandleDrawTrainerPic, + PlayerHandleTrainerSlide, + PlayerHandleTrainerSlideBack, + PlayerHandleFaintAnimation, + PlayerHandlePaletteFade, + PlayerHandleSuccessBallThrowAnim, + PlayerHandleBallThrowAnim, + PlayerHandlePause, + PlayerHandleMoveAnimation, + PlayerHandlePrintString, + PlayerHandlePrintSelectionString, + PlayerHandleChooseAction, + PlayerHandleUnknownYesNoBox, + PlayerHandleChooseMove, + PlayerHandleChooseItem, + PlayerHandleChoosePokemon, + PlayerHandleCmd23, + PlayerHandleHealthBarUpdate, + PlayerHandleExpUpdate, + PlayerHandleStatusIconUpdate, + PlayerHandleStatusAnimation, + PlayerHandleStatusXor, + PlayerHandleDataTransfer, + PlayerHandleDMA3Transfer, + PlayerHandlePlayBGM, + PlayerHandleCmd32, + PlayerHandleTwoReturnValues, + PlayerHandleChosenMonReturnValue, + PlayerHandleOneReturnValue, + PlayerHandleOneReturnValue_Duplicate, + PlayerHandleCmd37, + PlayerHandleCmd38, + PlayerHandleCmd39, + PlayerHandleCmd40, + PlayerHandleHitAnimation, + PlayerHandleCmd42, + PlayerHandlePlaySE, + PlayerHandlePlayFanfare, + PlayerHandleFaintingCry, + PlayerHandleIntroSlide, + PlayerHandleIntroTrainerBallThrow, + PlayerHandleDrawPartyStatusSummary, + PlayerHandleHidePartyStatusSummary, + PlayerHandleEndBounceEffect, + PlayerHandleSpriteInvisibility, + PlayerHandleBattleAnimation, + PlayerHandleLinkStandbyMsg, + PlayerHandleResetActionMoveSelection, + PlayerHandleCmd55, + PlayerCmdEnd, +}; + +static const u8 sTargetIdentities[] = { B_POSITION_PLAYER_LEFT, B_POSITION_PLAYER_RIGHT, B_POSITION_OPPONENT_RIGHT, B_POSITION_OPPONENT_LEFT }; + +// not used +static const u8 gUnknown_8250984[] = { 0x48, 0x48, 0x20, 0x5a, 0x50, 0x50, 0x50, 0x58 }; + +void nullsub_13(void) +{ +} + +void SetControllerToPlayer(void) +{ + gBattlerControllerFuncs[gActiveBattler] = PlayerBufferRunCommand; + gDoingBattleAnim = FALSE; +} + +static void PlayerBufferExecCompleted(void) +{ + gBattlerControllerFuncs[gActiveBattler] = PlayerBufferRunCommand; + if (gBattleTypeFlags & BATTLE_TYPE_LINK) + { + u8 playerId = GetMultiplayerId(); + + PrepareBufferDataTransferLink(2, 4, &playerId); + gBattleBufferA[gActiveBattler][0] = CONTROLLER_TERMINATOR_NOP; + } + else + { + gBattleControllerExecFlags &= ~gBitTable[gActiveBattler]; + } +} + +static void PlayerBufferRunCommand(void) +{ + if (gBattleControllerExecFlags & gBitTable[gActiveBattler]) + { + if (gBattleBufferA[gActiveBattler][0] < NELEMS(sPlayerBufferCommands)) + sPlayerBufferCommands[gBattleBufferA[gActiveBattler][0]](); + else + PlayerBufferExecCompleted(); + } +} + +static void CompleteOnBattlerSpritePosX_0(void) +{ + if (gSprites[gBattlerSpriteIds[gActiveBattler]].pos2.x == 0) + PlayerBufferExecCompleted(); +} + +static void HandleInputChooseAction(void) +{ + u16 itemId = gBattleBufferA[gActiveBattler][2] | (gBattleBufferA[gActiveBattler][3] << 8); + + DoBounceEffect(gActiveBattler, BOUNCE_HEALTHBOX, 7, 1); + DoBounceEffect(gActiveBattler, BOUNCE_MON, 7, 1); + if (JOY_NEW(A_BUTTON)) + { + PlaySE(SE_SELECT); + + switch (gActionSelectionCursor[gActiveBattler]) + { + case 0: + BtlController_EmitTwoReturnValues(1, B_ACTION_USE_MOVE, 0); + break; + case 1: + BtlController_EmitTwoReturnValues(1, B_ACTION_USE_ITEM, 0); + break; + case 2: + BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0); + break; + case 3: + BtlController_EmitTwoReturnValues(1, B_ACTION_RUN, 0); + break; + } + PlayerBufferExecCompleted(); + } + else if (JOY_NEW(DPAD_LEFT)) + { + if (gActionSelectionCursor[gActiveBattler] & 1) // if is B_ACTION_USE_ITEM or B_ACTION_RUN + { + PlaySE(SE_SELECT); + ActionSelectionDestroyCursorAt(gActionSelectionCursor[gActiveBattler]); + gActionSelectionCursor[gActiveBattler] ^= 1; + ActionSelectionCreateCursorAt(gActionSelectionCursor[gActiveBattler], 0); + } + } + else if (JOY_NEW(DPAD_RIGHT)) + { + if (!(gActionSelectionCursor[gActiveBattler] & 1)) // if is B_ACTION_USE_MOVE or B_ACTION_SWITCH + { + PlaySE(SE_SELECT); + ActionSelectionDestroyCursorAt(gActionSelectionCursor[gActiveBattler]); + gActionSelectionCursor[gActiveBattler] ^= 1; + ActionSelectionCreateCursorAt(gActionSelectionCursor[gActiveBattler], 0); + } + } + else if (JOY_NEW(DPAD_UP)) + { + if (gActionSelectionCursor[gActiveBattler] & 2) // if is B_ACTION_SWITCH or B_ACTION_RUN + { + PlaySE(SE_SELECT); + ActionSelectionDestroyCursorAt(gActionSelectionCursor[gActiveBattler]); + gActionSelectionCursor[gActiveBattler] ^= 2; + ActionSelectionCreateCursorAt(gActionSelectionCursor[gActiveBattler], 0); + } + } + else if (JOY_NEW(DPAD_DOWN)) + { + if (!(gActionSelectionCursor[gActiveBattler] & 2)) // if is B_ACTION_USE_MOVE or B_ACTION_USE_ITEM + { + PlaySE(SE_SELECT); + ActionSelectionDestroyCursorAt(gActionSelectionCursor[gActiveBattler]); + gActionSelectionCursor[gActiveBattler] ^= 2; + ActionSelectionCreateCursorAt(gActionSelectionCursor[gActiveBattler], 0); + } + } + else if (JOY_NEW(B_BUTTON)) + { + if ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + && GetBattlerPosition(gActiveBattler) == B_POSITION_PLAYER_RIGHT + && !(gAbsentBattlerFlags & gBitTable[GetBattlerAtPosition(B_POSITION_PLAYER_LEFT)]) + && !(gBattleTypeFlags & BATTLE_TYPE_MULTI)) + { + if (gBattleBufferA[gActiveBattler][1] == B_ACTION_USE_ITEM) + { + // Add item to bag if it is a ball + if (itemId <= ITEM_PREMIER_BALL) + AddBagItem(itemId, 1); + else + return; + } + PlaySE(SE_SELECT); + BtlController_EmitTwoReturnValues(1, B_ACTION_CANCEL_PARTNER, 0); + PlayerBufferExecCompleted(); + } + } + else if (JOY_NEW(START_BUTTON)) + { + SwapHpBarsWithHpText(); + } +} + +static void sub_802E640(void) +{ + EndBounceEffect(gActiveBattler, BOUNCE_HEALTHBOX); + EndBounceEffect(gActiveBattler, BOUNCE_MON); + gBattlerControllerFuncs[gActiveBattler] = HandleInputChooseTarget; +} + +static void HandleInputChooseTarget(void) +{ + s32 i; + u8 identities[4]; + + memcpy(identities, sTargetIdentities, NELEMS(sTargetIdentities)); + DoBounceEffect(gMultiUsePlayerCursor, BOUNCE_HEALTHBOX, 15, 1); + i = 0; + if (gBattlersCount != 0) + { + do + { + if (i != gMultiUsePlayerCursor) + EndBounceEffect(i, BOUNCE_HEALTHBOX); + ++i; + } + while (i < gBattlersCount); + } + if (JOY_NEW(A_BUTTON)) + { + PlaySE(SE_SELECT); + gSprites[gBattlerSpriteIds[gMultiUsePlayerCursor]].callback = sub_8012098; + BtlController_EmitTwoReturnValues(1, 10, gMoveSelectionCursor[gActiveBattler] | (gMultiUsePlayerCursor << 8)); + EndBounceEffect(gMultiUsePlayerCursor, BOUNCE_HEALTHBOX); + PlayerBufferExecCompleted(); + } + else if (JOY_NEW(B_BUTTON)) + { + PlaySE(SE_SELECT); + gSprites[gBattlerSpriteIds[gMultiUsePlayerCursor]].callback = sub_8012098; + gBattlerControllerFuncs[gActiveBattler] = HandleInputChooseMove; + DoBounceEffect(gActiveBattler, BOUNCE_HEALTHBOX, 7, 1); + DoBounceEffect(gActiveBattler, BOUNCE_MON, 7, 1); + EndBounceEffect(gMultiUsePlayerCursor, BOUNCE_HEALTHBOX); + } + else if (JOY_NEW(DPAD_LEFT | DPAD_UP)) + { + PlaySE(SE_SELECT); + gSprites[gBattlerSpriteIds[gMultiUsePlayerCursor]].callback = sub_8012098; + + do + { + u8 currSelIdentity = GetBattlerPosition(gMultiUsePlayerCursor); + + for (i = 0; i < MAX_BATTLERS_COUNT; ++i) + if (currSelIdentity == identities[i]) + break; + do + { + if (--i < 0) + i = MAX_BATTLERS_COUNT; // UB: array out of range + gMultiUsePlayerCursor = GetBattlerAtPosition(identities[i]); + } + while (gMultiUsePlayerCursor == gBattlersCount); + i = 0; + switch (GetBattlerPosition(gMultiUsePlayerCursor)) + { + case B_POSITION_PLAYER_LEFT: + case B_POSITION_PLAYER_RIGHT: + if (gActiveBattler != gMultiUsePlayerCursor) + ++i; + else if (gBattleMoves[GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_MOVE1 + gMoveSelectionCursor[gActiveBattler])].target & MOVE_TARGET_USER_OR_SELECTED) + ++i; + break; + case B_POSITION_OPPONENT_LEFT: + case B_POSITION_OPPONENT_RIGHT: + ++i; + break; + } + if (gAbsentBattlerFlags & gBitTable[gMultiUsePlayerCursor]) + i = 0; + } + while (i == 0); + gSprites[gBattlerSpriteIds[gMultiUsePlayerCursor]].callback = sub_8012044; + } + else if (JOY_NEW(DPAD_RIGHT | DPAD_DOWN)) + { + PlaySE(SE_SELECT); + gSprites[gBattlerSpriteIds[gMultiUsePlayerCursor]].callback = sub_8012098; + + do + { + u8 currSelIdentity = GetBattlerPosition(gMultiUsePlayerCursor); + + for (i = 0; i < MAX_BATTLERS_COUNT; ++i) + if (currSelIdentity == identities[i]) + break; + do + { + if (++i > 3) + i = 0; + gMultiUsePlayerCursor = GetBattlerAtPosition(identities[i]); + } + while (gMultiUsePlayerCursor == gBattlersCount); + i = 0; + switch (GetBattlerPosition(gMultiUsePlayerCursor)) + { + case B_POSITION_PLAYER_LEFT: + case B_POSITION_PLAYER_RIGHT: + if (gActiveBattler != gMultiUsePlayerCursor) + ++i; + else if (gBattleMoves[GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_MOVE1 + gMoveSelectionCursor[gActiveBattler])].target & MOVE_TARGET_USER_OR_SELECTED) + ++i; + break; + case B_POSITION_OPPONENT_LEFT: + case B_POSITION_OPPONENT_RIGHT: + ++i; + break; + } + if (gAbsentBattlerFlags & gBitTable[gMultiUsePlayerCursor]) + i = 0; + } + while (i == 0); + gSprites[gBattlerSpriteIds[gMultiUsePlayerCursor]].callback = sub_8012044; + } +} + +void HandleInputChooseMove(void) +{ + bool32 canSelectTarget = FALSE; + struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleBufferA[gActiveBattler][4]); + + sub_8033AC8(); + if (JOY_NEW(A_BUTTON)) + { + u8 moveTarget; + + PlaySE(SE_SELECT); + if (moveInfo->moves[gMoveSelectionCursor[gActiveBattler]] == MOVE_CURSE) + { + if (moveInfo->monType1 != TYPE_GHOST && moveInfo->monType2 != TYPE_GHOST) + moveTarget = MOVE_TARGET_USER; + else + moveTarget = MOVE_TARGET_SELECTED; + } + else + { + moveTarget = gBattleMoves[moveInfo->moves[gMoveSelectionCursor[gActiveBattler]]].target; + } + + if (moveTarget & MOVE_TARGET_USER) + gMultiUsePlayerCursor = gActiveBattler; + else + gMultiUsePlayerCursor = GetBattlerAtPosition((GetBattlerPosition(gActiveBattler) & BIT_SIDE) ^ BIT_SIDE); + + if (!gBattleBufferA[gActiveBattler][1]) // not a double battle + { + if (moveTarget & MOVE_TARGET_USER_OR_SELECTED && !gBattleBufferA[gActiveBattler][2]) + ++canSelectTarget; + } + else // double battle + { + if (!(moveTarget & (MOVE_TARGET_RANDOM | MOVE_TARGET_BOTH | MOVE_TARGET_DEPENDS | MOVE_TARGET_FOES_AND_ALLY | MOVE_TARGET_OPPONENTS_FIELD | MOVE_TARGET_USER))) + ++canSelectTarget; // either selected or user + if (moveInfo->currentPp[gMoveSelectionCursor[gActiveBattler]] == 0) + { + canSelectTarget = FALSE; + } + else if (!(moveTarget & (MOVE_TARGET_USER | MOVE_TARGET_USER_OR_SELECTED)) && CountAliveMonsInBattle(BATTLE_ALIVE_EXCEPT_ACTIVE) <= 1) + { + gMultiUsePlayerCursor = GetDefaultMoveTarget(gActiveBattler); + canSelectTarget = FALSE; + } + } + ResetPaletteFadeControl(); + BeginNormalPaletteFade(0xF0000, 0, 0, 0, RGB_WHITE); + if (!canSelectTarget) + { + BtlController_EmitTwoReturnValues(1, 10, gMoveSelectionCursor[gActiveBattler] | (gMultiUsePlayerCursor << 8)); + PlayerBufferExecCompleted(); + } + else + { + gBattlerControllerFuncs[gActiveBattler] = HandleInputChooseTarget; + if (moveTarget & (MOVE_TARGET_USER | MOVE_TARGET_USER_OR_SELECTED)) + gMultiUsePlayerCursor = gActiveBattler; + else if (gAbsentBattlerFlags & gBitTable[GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT)]) + gMultiUsePlayerCursor = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); + else + gMultiUsePlayerCursor = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); + gSprites[gBattlerSpriteIds[gMultiUsePlayerCursor]].callback = sub_8012044; + } + } + else if (JOY_NEW(B_BUTTON)) + { + PlaySE(SE_SELECT); + BtlController_EmitTwoReturnValues(1, 10, 0xFFFF); + PlayerBufferExecCompleted(); + ResetPaletteFadeControl(); + BeginNormalPaletteFade(0xF0000, 0, 0, 0, RGB_WHITE); + } + else if (JOY_NEW(DPAD_LEFT)) + { + if (gMoveSelectionCursor[gActiveBattler] & 1) + { + MoveSelectionDestroyCursorAt(gMoveSelectionCursor[gActiveBattler]); + gMoveSelectionCursor[gActiveBattler] ^= 1; + PlaySE(SE_SELECT); + MoveSelectionCreateCursorAt(gMoveSelectionCursor[gActiveBattler], 0); + MoveSelectionDisplayPpNumber(); + MoveSelectionDisplayMoveType(); + BeginNormalPaletteFade(0xF0000, 0, 0, 0, RGB_WHITE); + } + } + else if (JOY_NEW(DPAD_RIGHT)) + { + if (!(gMoveSelectionCursor[gActiveBattler] & 1) + && (gMoveSelectionCursor[gActiveBattler] ^ 1) < gNumberOfMovesToChoose) + { + MoveSelectionDestroyCursorAt(gMoveSelectionCursor[gActiveBattler]); + gMoveSelectionCursor[gActiveBattler] ^= 1; + PlaySE(SE_SELECT); + MoveSelectionCreateCursorAt(gMoveSelectionCursor[gActiveBattler], 0); + MoveSelectionDisplayPpNumber(); + MoveSelectionDisplayMoveType(); + BeginNormalPaletteFade(0xF0000, 0, 0, 0, RGB_WHITE); + } + } + else if (JOY_NEW(DPAD_UP)) + { + if (gMoveSelectionCursor[gActiveBattler] & 2) + { + MoveSelectionDestroyCursorAt(gMoveSelectionCursor[gActiveBattler]); + gMoveSelectionCursor[gActiveBattler] ^= 2; + PlaySE(SE_SELECT); + MoveSelectionCreateCursorAt(gMoveSelectionCursor[gActiveBattler], 0); + MoveSelectionDisplayPpNumber(); + MoveSelectionDisplayMoveType(); + BeginNormalPaletteFade(0xF0000, 0, 0, 0, RGB_WHITE); + } + } + else if (JOY_NEW(DPAD_DOWN)) + { + if (!(gMoveSelectionCursor[gActiveBattler] & 2) + && (gMoveSelectionCursor[gActiveBattler] ^ 2) < gNumberOfMovesToChoose) + { + MoveSelectionDestroyCursorAt(gMoveSelectionCursor[gActiveBattler]); + gMoveSelectionCursor[gActiveBattler] ^= 2; + PlaySE(SE_SELECT); + MoveSelectionCreateCursorAt(gMoveSelectionCursor[gActiveBattler], 0); + MoveSelectionDisplayPpNumber(); + MoveSelectionDisplayMoveType(); + BeginNormalPaletteFade(0xF0000, 0, 0, 0, RGB_WHITE); + } + } + else if (JOY_NEW(SELECT_BUTTON)) + { + if (gNumberOfMovesToChoose > 1 && !(gBattleTypeFlags & BATTLE_TYPE_LINK)) + { + MoveSelectionCreateCursorAt(gMoveSelectionCursor[gActiveBattler], 29); + if (gMoveSelectionCursor[gActiveBattler] != 0) + gMultiUsePlayerCursor = 0; + else + gMultiUsePlayerCursor = gMoveSelectionCursor[gActiveBattler] + 1; + MoveSelectionCreateCursorAt(gMultiUsePlayerCursor, 27); + BattlePutTextOnWindow(gText_BattleSwitchWhich, 0xB); + gBattlerControllerFuncs[gActiveBattler] = HandleMoveSwitching; + } + } +} + +// not used +static u32 sub_802EDDC(void) +{ + u32 var = 0; + + if (JOY_NEW(A_BUTTON)) + { + PlaySE(SE_SELECT); + var = 1; + } + if (JOY_NEW(B_BUTTON)) + { + PlaySE(SE_SELECT); + gBattle_BG0_X = 0; + gBattle_BG0_Y = 0x140; + var = 0xFF; + } + if (JOY_NEW(DPAD_LEFT) && gMoveSelectionCursor[gActiveBattler] & 1) + { + MoveSelectionDestroyCursorAt(gMoveSelectionCursor[gActiveBattler]); + gMoveSelectionCursor[gActiveBattler] ^= 1; + PlaySE(SE_SELECT); + MoveSelectionCreateCursorAt(gMoveSelectionCursor[gActiveBattler], 0); + } + if (JOY_NEW(DPAD_RIGHT) && !(gMoveSelectionCursor[gActiveBattler] & 1) + && (gMoveSelectionCursor[gActiveBattler] ^ 1) < gNumberOfMovesToChoose) + { + MoveSelectionDestroyCursorAt(gMoveSelectionCursor[gActiveBattler]); + gMoveSelectionCursor[gActiveBattler] ^= 1; + PlaySE(SE_SELECT); + MoveSelectionCreateCursorAt(gMoveSelectionCursor[gActiveBattler], 0); + } + if (JOY_NEW(DPAD_UP) && gMoveSelectionCursor[gActiveBattler] & 2) + { + MoveSelectionDestroyCursorAt(gMoveSelectionCursor[gActiveBattler]); + gMoveSelectionCursor[gActiveBattler] ^= 2; + PlaySE(SE_SELECT); + MoveSelectionCreateCursorAt(gMoveSelectionCursor[gActiveBattler], 0); + } + if (JOY_NEW(DPAD_DOWN) && !(gMoveSelectionCursor[gActiveBattler] & 2) + && (gMoveSelectionCursor[gActiveBattler] ^ 2) < gNumberOfMovesToChoose) + { + MoveSelectionDestroyCursorAt(gMoveSelectionCursor[gActiveBattler]); + gMoveSelectionCursor[gActiveBattler] ^= 2; + PlaySE(SE_SELECT); + MoveSelectionCreateCursorAt(gMoveSelectionCursor[gActiveBattler], 0); + } + return var; +} + +static void HandleMoveSwitching(void) +{ + u8 perMovePPBonuses[4]; + struct ChooseMoveStruct moveStruct; + u8 totalPPBonuses; + + if (JOY_NEW(A_BUTTON | SELECT_BUTTON)) + { + PlaySE(SE_SELECT); + + if (gMoveSelectionCursor[gActiveBattler] != gMultiUsePlayerCursor) + { + struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleBufferA[gActiveBattler][4]); + s32 i; + + // swap moves and pp + i = moveInfo->moves[gMoveSelectionCursor[gActiveBattler]]; + moveInfo->moves[gMoveSelectionCursor[gActiveBattler]] = moveInfo->moves[gMultiUsePlayerCursor]; + moveInfo->moves[gMultiUsePlayerCursor] = i; + i = moveInfo->currentPp[gMoveSelectionCursor[gActiveBattler]]; + moveInfo->currentPp[gMoveSelectionCursor[gActiveBattler]] = moveInfo->currentPp[gMultiUsePlayerCursor]; + moveInfo->currentPp[gMultiUsePlayerCursor] = i; + i = moveInfo->maxPp[gMoveSelectionCursor[gActiveBattler]]; + moveInfo->maxPp[gMoveSelectionCursor[gActiveBattler]] = moveInfo->maxPp[gMultiUsePlayerCursor]; + moveInfo->maxPp[gMultiUsePlayerCursor] = i; + if (gDisableStructs[gActiveBattler].mimickedMoves & gBitTable[gMoveSelectionCursor[gActiveBattler]]) + { + gDisableStructs[gActiveBattler].mimickedMoves &= (~gBitTable[gMoveSelectionCursor[gActiveBattler]]); + gDisableStructs[gActiveBattler].mimickedMoves |= gBitTable[gMultiUsePlayerCursor]; + } + MoveSelectionDisplayMoveNames(); + for (i = 0; i < MAX_MON_MOVES; ++i) + perMovePPBonuses[i] = (gBattleMons[gActiveBattler].ppBonuses & (3 << (i * 2))) >> (i * 2); + totalPPBonuses = perMovePPBonuses[gMoveSelectionCursor[gActiveBattler]]; + perMovePPBonuses[gMoveSelectionCursor[gActiveBattler]] = perMovePPBonuses[gMultiUsePlayerCursor]; + perMovePPBonuses[gMultiUsePlayerCursor] = totalPPBonuses; + totalPPBonuses = 0; + for (i = 0; i < MAX_MON_MOVES; ++i) + totalPPBonuses |= perMovePPBonuses[i] << (i * 2); + + gBattleMons[gActiveBattler].ppBonuses = totalPPBonuses; + for (i = 0; i < MAX_MON_MOVES; ++i) + { + gBattleMons[gActiveBattler].moves[i] = moveInfo->moves[i]; + gBattleMons[gActiveBattler].pp[i] = moveInfo->currentPp[i]; + } + if (!(gBattleMons[gActiveBattler].status2 & STATUS2_TRANSFORMED)) + { + for (i = 0; i < MAX_MON_MOVES; ++i) + { + moveStruct.moves[i] = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_MOVE1 + i); + moveStruct.currentPp[i] = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_PP1 + i); + } + + totalPPBonuses = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_PP_BONUSES); + for (i = 0; i < MAX_MON_MOVES; ++i) + perMovePPBonuses[i] = (totalPPBonuses & (3 << (i * 2))) >> (i * 2); + i = moveStruct.moves[gMoveSelectionCursor[gActiveBattler]]; + moveStruct.moves[gMoveSelectionCursor[gActiveBattler]] = moveStruct.moves[gMultiUsePlayerCursor]; + moveStruct.moves[gMultiUsePlayerCursor] = i; + i = moveStruct.currentPp[gMoveSelectionCursor[gActiveBattler]]; + moveStruct.currentPp[gMoveSelectionCursor[gActiveBattler]] = moveStruct.currentPp[gMultiUsePlayerCursor]; + moveStruct.currentPp[gMultiUsePlayerCursor] = i; + totalPPBonuses = perMovePPBonuses[gMoveSelectionCursor[gActiveBattler]]; + perMovePPBonuses[gMoveSelectionCursor[gActiveBattler]] = perMovePPBonuses[gMultiUsePlayerCursor]; + perMovePPBonuses[gMultiUsePlayerCursor] = totalPPBonuses; + totalPPBonuses = 0; + for (i = 0; i < MAX_MON_MOVES; ++i) + totalPPBonuses |= perMovePPBonuses[i] << (i * 2); + for (i = 0; i < MAX_MON_MOVES; ++i) + { + SetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_MOVE1 + i, &moveStruct.moves[i]); + SetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_PP1 + i, &moveStruct.currentPp[i]); + } + SetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_PP_BONUSES, &totalPPBonuses); + } + } + if (gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE) + gBattlerControllerFuncs[gActiveBattler] = sub_80E7988; + else + gBattlerControllerFuncs[gActiveBattler] = HandleInputChooseMove; + gMoveSelectionCursor[gActiveBattler] = gMultiUsePlayerCursor; + MoveSelectionCreateCursorAt(gMoveSelectionCursor[gActiveBattler], 0); + MoveSelectionDisplayPpString(); + MoveSelectionDisplayPpNumber(); + MoveSelectionDisplayMoveType(); + } + if (JOY_NEW(B_BUTTON)) + { + PlaySE(SE_SELECT); + MoveSelectionDestroyCursorAt(gMultiUsePlayerCursor); + MoveSelectionCreateCursorAt(gMoveSelectionCursor[gActiveBattler], 0); + if (gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE) + gBattlerControllerFuncs[gActiveBattler] = sub_80E7988; + else + gBattlerControllerFuncs[gActiveBattler] = HandleInputChooseMove; + MoveSelectionDisplayPpString(); + MoveSelectionDisplayPpNumber(); + MoveSelectionDisplayMoveType(); + } + if (JOY_NEW(DPAD_LEFT)) + { + if (gMultiUsePlayerCursor & 1) + { + if (gMultiUsePlayerCursor == gMoveSelectionCursor[gActiveBattler]) + MoveSelectionCreateCursorAt(gMoveSelectionCursor[gActiveBattler], 29); + else + MoveSelectionDestroyCursorAt(gMultiUsePlayerCursor); + gMultiUsePlayerCursor ^= 1; + PlaySE(SE_SELECT); + if (gMultiUsePlayerCursor == gMoveSelectionCursor[gActiveBattler]) + MoveSelectionCreateCursorAt(gMultiUsePlayerCursor, 0); + else + MoveSelectionCreateCursorAt(gMultiUsePlayerCursor, 27); + } + } + if (JOY_NEW(DPAD_RIGHT)) + { + if (!(gMultiUsePlayerCursor & 1) && (gMultiUsePlayerCursor ^ 1) < gNumberOfMovesToChoose) + { + if (gMultiUsePlayerCursor == gMoveSelectionCursor[gActiveBattler]) + MoveSelectionCreateCursorAt(gMoveSelectionCursor[gActiveBattler], 29); + else + MoveSelectionDestroyCursorAt(gMultiUsePlayerCursor); + gMultiUsePlayerCursor ^= 1; + PlaySE(SE_SELECT); + if (gMultiUsePlayerCursor == gMoveSelectionCursor[gActiveBattler]) + MoveSelectionCreateCursorAt(gMultiUsePlayerCursor, 0); + else + MoveSelectionCreateCursorAt(gMultiUsePlayerCursor, 27); + } + } + if (JOY_NEW(DPAD_UP)) + { + if (gMultiUsePlayerCursor & 2) + { + if (gMultiUsePlayerCursor == gMoveSelectionCursor[gActiveBattler]) + MoveSelectionCreateCursorAt(gMoveSelectionCursor[gActiveBattler], 29); + else + MoveSelectionDestroyCursorAt(gMultiUsePlayerCursor); + gMultiUsePlayerCursor ^= 2; + PlaySE(SE_SELECT); + if (gMultiUsePlayerCursor == gMoveSelectionCursor[gActiveBattler]) + MoveSelectionCreateCursorAt(gMultiUsePlayerCursor, 0); + else + MoveSelectionCreateCursorAt(gMultiUsePlayerCursor, 27); + } + } + if (JOY_NEW(DPAD_DOWN)) + { + if (!(gMultiUsePlayerCursor & 2) && (gMultiUsePlayerCursor ^ 2) < gNumberOfMovesToChoose) + { + if (gMultiUsePlayerCursor == gMoveSelectionCursor[gActiveBattler]) + MoveSelectionCreateCursorAt(gMoveSelectionCursor[gActiveBattler], 29); + else + MoveSelectionDestroyCursorAt(gMultiUsePlayerCursor); + gMultiUsePlayerCursor ^= 2; + PlaySE(SE_SELECT); + if (gMultiUsePlayerCursor == gMoveSelectionCursor[gActiveBattler]) + MoveSelectionCreateCursorAt(gMultiUsePlayerCursor, 0); + else + MoveSelectionCreateCursorAt(gMultiUsePlayerCursor, 27); + } + } +} + +static void sub_802F610(void) +{ + if (gWirelessCommType == 0) + { + if (gReceivedRemoteLinkPlayers == 0) + { + m4aSongNumStop(SE_T_OOAME); + gMain.inBattle = 0; + gMain.callback1 = gPreBattleCallback1; + SetMainCallback2(sub_8011A1C); + FreeAllWindowBuffers(); + } + } + else if (IsLinkTaskFinished()) + { + m4aSongNumStop(SE_T_OOAME); + gMain.inBattle = 0; + gMain.callback1 = gPreBattleCallback1; + SetMainCallback2(sub_8011A1C); + FreeAllWindowBuffers(); + } +} + +void sub_802F6A8(void) +{ + if (!gPaletteFade.active) + { + if (gBattleTypeFlags & BATTLE_TYPE_LINK) + { + if (gWirelessCommType == 0) + sub_800AAC0(); + else + sub_800AB9C(); + gBattlerControllerFuncs[gActiveBattler] = sub_802F610; + } + else + { + m4aSongNumStop(SE_T_OOAME); + gMain.inBattle = 0; + gMain.callback1 = gPreBattleCallback1; + SetMainCallback2(gMain.savedCallback); + } + } +} + +static void CompleteOnBattlerSpriteCallbackDummy(void) +{ + if (gSprites[gBattlerSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy) + PlayerBufferExecCompleted(); +} + +static void CompleteOnBattlerSpriteCallbackDummy2(void) +{ + if (gSprites[gBattlerSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy) + PlayerBufferExecCompleted(); +} + +static void sub_802F7A0(void) +{ + if (gSprites[gBattlerSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy) + { + nullsub_16(gSaveBlock2Ptr->playerGender); + FreeSpriteOamMatrix(&gSprites[gBattlerSpriteIds[gActiveBattler]]); + DestroySprite(&gSprites[gBattlerSpriteIds[gActiveBattler]]); + PlayerBufferExecCompleted(); + } +} + +static void sub_802F810(void) +{ + if (--gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_9 == 0xFF) + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_9 = 0; + PlayerBufferExecCompleted(); + } +} + +static void sub_802F858(void) +{ + bool8 var = FALSE; + + if (!IsDoubleBattle() || (IsDoubleBattle() && (gBattleTypeFlags & BATTLE_TYPE_MULTI))) + { + if (gSprites[gHealthboxSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy) + var = TRUE; + } + else + { + if (gSprites[gHealthboxSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy + && gSprites[gHealthboxSpriteIds[gActiveBattler ^ BIT_FLANK]].callback == SpriteCallbackDummy) + var = TRUE; + } + if (IsCryPlayingOrClearCrySongs()) + var = FALSE; + if (var && gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_1_x1 + && gBattleSpritesDataPtr->healthBoxesData[gActiveBattler ^ BIT_FLANK].field_1_x1) + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].flag_x80 = 0; + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_1_x1 = 0; + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler ^ BIT_FLANK].flag_x80 = 0; + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler ^ BIT_FLANK].field_1_x1 = 0; + FreeSpriteTilesByTag(0x27F9); + FreeSpritePaletteByTag(0x27F9); + if (gBattleTypeFlags & BATTLE_TYPE_MULTI) + m4aMPlayContinue(&gMPlayInfo_BGM); + else + m4aMPlayVolumeControl(&gMPlayInfo_BGM, 0xFFFF, 256); + HandleLowHpMusicChange(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], gActiveBattler); + if (IsDoubleBattle()) + HandleLowHpMusicChange(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler ^ BIT_FLANK]], gActiveBattler ^ BIT_FLANK); + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_9 = 3; + gBattlerControllerFuncs[gActiveBattler] = sub_802F810; + } +} + +static void sub_802FA58(void) +{ + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].ballAnimActive && !gBattleSpritesDataPtr->healthBoxesData[gActiveBattler ^ BIT_FLANK].ballAnimActive) + { + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].flag_x80) + sub_80F1720(gActiveBattler, &gPlayerParty[gBattlerPartyIndexes[gActiveBattler]]); + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler ^ BIT_FLANK].flag_x80) + sub_80F1720(gActiveBattler ^ BIT_FLANK, &gPlayerParty[gBattlerPartyIndexes[gActiveBattler ^ BIT_FLANK]]); + if (IsDoubleBattle() && !(gBattleTypeFlags & BATTLE_TYPE_MULTI)) + { + DestroySprite(&gSprites[gUnknown_3004FFC[gActiveBattler ^ BIT_FLANK]]); + UpdateHealthboxAttribute(gHealthboxSpriteIds[gActiveBattler ^ BIT_FLANK], + &gPlayerParty[gBattlerPartyIndexes[gActiveBattler ^ BIT_FLANK]], + HEALTHBOX_ALL); + sub_804BD94(gActiveBattler ^ BIT_FLANK); + SetHealthboxSpriteVisible(gHealthboxSpriteIds[gActiveBattler ^ BIT_FLANK]); + } + DestroySprite(&gSprites[gUnknown_3004FFC[gActiveBattler]]); + UpdateHealthboxAttribute(gHealthboxSpriteIds[gActiveBattler], + &gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], + HEALTHBOX_ALL); + sub_804BD94(gActiveBattler); + SetHealthboxSpriteVisible(gHealthboxSpriteIds[gActiveBattler]); + gBattleSpritesDataPtr->animationData->field_9_x1 = 0; + gBattlerControllerFuncs[gActiveBattler] = sub_802F858; + } +} + +static void sub_802FBF4(void) +{ + if (gSprites[gHealthboxSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy + && gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_1_x1) + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].flag_x80 = 0; + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_1_x1 = 0; + FreeSpriteTilesByTag(0x27F9); + FreeSpritePaletteByTag(0x27F9); + if (gBattleSpritesDataPtr->battlerData[gActiveBattler].behindSubstitute) + InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, gActiveBattler, B_ANIM_MON_TO_SUBSTITUTE); + gBattlerControllerFuncs[gActiveBattler] = sub_802FCAC; + } +} + +static void sub_802FCAC(void) +{ + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].specialAnimActive + && !IsCryPlayingOrClearCrySongs()) + { + m4aMPlayVolumeControl(&gMPlayInfo_BGM, 0xFFFF, 0x100); + HandleLowHpMusicChange(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], gActiveBattler); + PlayerBufferExecCompleted(); + } +} + +static void sub_802FD18(void) +{ + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].flag_x80 + && !gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].ballAnimActive) + sub_80F1720(gActiveBattler, &gPlayerParty[gBattlerPartyIndexes[gActiveBattler]]); + if (gSprites[gUnknown_3004FFC[gActiveBattler]].callback == SpriteCallbackDummy + && !(gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].ballAnimActive)) + { + DestroySprite(&gSprites[gUnknown_3004FFC[gActiveBattler]]); + UpdateHealthboxAttribute(gHealthboxSpriteIds[gActiveBattler], + &gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], + HEALTHBOX_ALL); + sub_804BD94(gActiveBattler); + SetHealthboxSpriteVisible(gHealthboxSpriteIds[gActiveBattler]); + CopyBattleSpriteInvisibility(gActiveBattler); + gBattlerControllerFuncs[gActiveBattler] = sub_802FBF4; + } +} + +void c3_0802FDF4(u8 taskId) +{ + if (!IsCryPlayingOrClearCrySongs()) + { + m4aMPlayVolumeControl(&gMPlayInfo_BGM, 0xFFFF, 0x100); + DestroyTask(taskId); + } +} + +static void CompleteOnHealthbarDone(void) +{ + s16 hpValue = MoveBattleBar(gActiveBattler, gHealthboxSpriteIds[gActiveBattler], HEALTH_BAR, 0); + + SetHealthboxSpriteVisible(gHealthboxSpriteIds[gActiveBattler]); + + if (hpValue != -1) + { + UpdateHpTextInHealthbox(gHealthboxSpriteIds[gActiveBattler], hpValue, HP_CURRENT); + } + else + { + HandleLowHpMusicChange(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], gActiveBattler); + PlayerBufferExecCompleted(); + } +} + +void CompleteOnInactiveTextPrinter(void) +{ + if (!IsTextPrinterActive(0)) + PlayerBufferExecCompleted(); +} + +#define tExpTask_monId data[0] +#define tExpTask_gainedExp data[1] +#define tExpTask_battler data[2] +#define tExpTask_frames data[10] +// TODO: document other used fields + +static void Task_GiveExpToMon(u8 taskId) +{ + u32 monId = (u8)(gTasks[taskId].tExpTask_monId); + u8 battlerId = gTasks[taskId].tExpTask_battler; + s16 gainedExp = gTasks[taskId].tExpTask_gainedExp; + + if (IsDoubleBattle() == TRUE || monId != gBattlerPartyIndexes[battlerId]) // Give exp without moving the expbar. + { + struct Pokemon *mon = &gPlayerParty[monId]; + u16 species = GetMonData(mon, MON_DATA_SPECIES); + u8 level = GetMonData(mon, MON_DATA_LEVEL); + u32 currExp = GetMonData(mon, MON_DATA_EXP); + u32 nextLvlExp = gExperienceTables[gBaseStats[species].growthRate][level + 1]; + + if (currExp + gainedExp >= nextLvlExp) + { + u8 savedActiveBattler; + + SetMonData(mon, MON_DATA_EXP, &nextLvlExp); + CalculateMonStats(mon); + gainedExp -= nextLvlExp - currExp; + savedActiveBattler = gActiveBattler; + gActiveBattler = battlerId; + BtlController_EmitTwoReturnValues(1, RET_VALUE_LEVELED_UP, gainedExp); + gActiveBattler = savedActiveBattler; + if (IsDoubleBattle() == TRUE + && ((u16)(monId) == gBattlerPartyIndexes[battlerId] || (u16)(monId) == gBattlerPartyIndexes[battlerId ^ BIT_FLANK])) + gTasks[taskId].func = Task_LaunchLvlUpAnim; + else + gTasks[taskId].func = DestroyExpTaskAndCompleteOnInactiveTextPrinter; + } + else + { + currExp += gainedExp; + SetMonData(mon, MON_DATA_EXP, &currExp); + gBattlerControllerFuncs[battlerId] = CompleteOnInactiveTextPrinter; + DestroyTask(taskId); + } + } + else + { + gTasks[taskId].func = Task_PrepareToGiveExpWithExpBar; + } +} + +static void Task_PrepareToGiveExpWithExpBar(u8 taskId) +{ + u8 monIndex = gTasks[taskId].tExpTask_monId; + s32 gainedExp = gTasks[taskId].tExpTask_gainedExp; + u8 battlerId = gTasks[taskId].tExpTask_battler; + struct Pokemon *mon = &gPlayerParty[monIndex]; + u8 level = GetMonData(mon, MON_DATA_LEVEL); + u16 species = GetMonData(mon, MON_DATA_SPECIES); + u32 exp = GetMonData(mon, MON_DATA_EXP); + u32 currLvlExp = gExperienceTables[gBaseStats[species].growthRate][level]; + u32 expToNextLvl; + + exp -= currLvlExp; + expToNextLvl = gExperienceTables[gBaseStats[species].growthRate][level + 1] - currLvlExp; + SetBattleBarStruct(battlerId, gHealthboxSpriteIds[battlerId], expToNextLvl, exp, -gainedExp); + PlaySE(SE_U); + gTasks[taskId].func = sub_80300F4; +} + +static void sub_80300F4(u8 taskId) +{ + if (gTasks[taskId].tExpTask_frames < 13) + { + ++gTasks[taskId].tExpTask_frames; + } + else + { + u8 monId = gTasks[taskId].tExpTask_monId; + s16 gainedExp = gTasks[taskId].tExpTask_gainedExp; + u8 battlerId = gTasks[taskId].tExpTask_battler; + s16 newExpPoints; + + newExpPoints = MoveBattleBar(battlerId, gHealthboxSpriteIds[battlerId], EXP_BAR, 0); + SetHealthboxSpriteVisible(gHealthboxSpriteIds[battlerId]); + if (newExpPoints == -1) // The bar has been filled with given exp points. + { + u8 level; + s32 currExp; + u16 species; + s32 expOnNextLvl; + + m4aSongNumStop(SE_U); + level = GetMonData(&gPlayerParty[monId], MON_DATA_LEVEL); + currExp = GetMonData(&gPlayerParty[monId], MON_DATA_EXP); + species = GetMonData(&gPlayerParty[monId], MON_DATA_SPECIES); + expOnNextLvl = gExperienceTables[gBaseStats[species].growthRate][level + 1]; + if (currExp + gainedExp >= expOnNextLvl) + { + u8 savedActiveBattler; + + SetMonData(&gPlayerParty[monId], MON_DATA_EXP, &expOnNextLvl); + CalculateMonStats(&gPlayerParty[monId]); + gainedExp -= expOnNextLvl - currExp; + savedActiveBattler = gActiveBattler; + gActiveBattler = battlerId; + BtlController_EmitTwoReturnValues(1, RET_VALUE_LEVELED_UP, gainedExp); + gActiveBattler = savedActiveBattler; + gTasks[taskId].func = Task_LaunchLvlUpAnim; + } + else + { + currExp += gainedExp; + SetMonData(&gPlayerParty[monId], MON_DATA_EXP, &currExp); + gBattlerControllerFuncs[battlerId] = CompleteOnInactiveTextPrinter; + DestroyTask(taskId); + } + } + } +} + +static void Task_LaunchLvlUpAnim(u8 taskId) +{ + u8 battlerId = gTasks[taskId].tExpTask_battler; + u8 monIndex = gTasks[taskId].tExpTask_monId; + + if (IsDoubleBattle() == TRUE && monIndex == gBattlerPartyIndexes[battlerId ^ BIT_FLANK]) + battlerId ^= BIT_FLANK; + InitAndLaunchSpecialAnimation(battlerId, battlerId, battlerId, B_ANIM_LVL_UP); + gTasks[taskId].func = Task_UpdateLvlInHealthbox; +} + +static void Task_UpdateLvlInHealthbox(u8 taskId) +{ + u8 battlerId = gTasks[taskId].tExpTask_battler; + + if (!gBattleSpritesDataPtr->healthBoxesData[battlerId].specialAnimActive) + { + u8 monIndex = gTasks[taskId].tExpTask_monId; + + GetMonData(&gPlayerParty[monIndex], MON_DATA_LEVEL); // Unused return value. + if (IsDoubleBattle() == TRUE && monIndex == gBattlerPartyIndexes[battlerId ^ BIT_FLANK]) + UpdateHealthboxAttribute(gHealthboxSpriteIds[battlerId ^ BIT_FLANK], &gPlayerParty[monIndex], HEALTHBOX_ALL); + else + UpdateHealthboxAttribute(gHealthboxSpriteIds[battlerId], &gPlayerParty[monIndex], HEALTHBOX_ALL); + gTasks[taskId].func = DestroyExpTaskAndCompleteOnInactiveTextPrinter; + } +} + +static void DestroyExpTaskAndCompleteOnInactiveTextPrinter(u8 taskId) +{ + u8 monIndex; + s32 battlerId = gTasks[taskId].tExpTask_battler; + + if (IsBattlerSpriteVisible((u8)battlerId) == TRUE) + { + gTasks[taskId].func = sub_80303A8; + gTasks[taskId].data[15] = 0; + } + else + { + gBattlerControllerFuncs[battlerId] = CompleteOnInactiveTextPrinter; + DestroyTask(taskId); + } +} + +static void sub_80303A8(u8 taskId) +{ + s16 *data = gTasks[taskId].data; + u8 battlerId = tExpTask_battler; + u16 v5 = sub_80768B0(battlerId); + bool32 v6 = ((v5 ^ BIT_SIDE)) != B_SIDE_PLAYER; + struct Sprite *sprite = &gSprites[gBattlerSpriteIds[battlerId]]; + + switch (data[15]) + { + case 0: + if (!IsTextPrinterActive(0)) + { + if (!v6) + { + data[14] = gBattle_BG1_X; + data[13] = gBattle_BG1_Y; + gBattle_BG1_X = -(sprite->pos1.x + sprite->pos2.x) + 32; + gBattle_BG1_Y = -(sprite->pos1.y + sprite->pos2.y) + 32; + } + else + { + data[14] = gBattle_BG2_X; + data[13] = gBattle_BG2_Y; + gBattle_BG2_X = -(sprite->pos1.x + sprite->pos2.x) + 32; + gBattle_BG2_Y = -(sprite->pos1.y + sprite->pos2.y) + 32; + } + ++data[15]; + } + break; + case 1: + { + u32 battlerIdAlt = battlerId; + bool32 v6Alt = v6; + + sub_8072E48(battlerIdAlt, v6Alt); + } + ++data[15]; + break; + case 2: + PlaySE(SE_T_KAMI2); + if (IsMonGettingExpSentOut()) + sub_811E5B8(sprite->pos1.x + sprite->pos2.x, + sprite->pos1.y + sprite->pos2.y, + 10000, + 10000, + 1, + 0); + ++data[15]; + break; + case 3: + if (!sub_811E680()) + { + sprite->invisible = FALSE; + ++data[15]; + } + break; + case 5: + sub_8073128(v6); + ++data[15]; + break; + case 4: + ++data[15]; + break; + case 6: + if (!v6) + { + gBattle_BG1_X = data[14]; + gBattle_BG1_Y = data[13]; + } + else + { + gBattle_BG2_X = data[14]; + gBattle_BG2_Y = data[13]; + } + gBattlerControllerFuncs[battlerId] = CompleteOnInactiveTextPrinter; + DestroyTask(taskId); + break; + } +} + +static void sub_8030538(void) +{ + if (gSprites[gBattlerSpriteIds[gActiveBattler]].pos1.y + gSprites[gBattlerSpriteIds[gActiveBattler]].pos2.y > DISPLAY_HEIGHT) + { + FreeOamMatrix(gSprites[gBattlerSpriteIds[gActiveBattler]].oam.matrixNum); + DestroySprite(&gSprites[gBattlerSpriteIds[gActiveBattler]]); + SetHealthboxSpriteInvisible(gHealthboxSpriteIds[gActiveBattler]); + PlayerBufferExecCompleted(); + } +} + +static void sub_80305A0(void) +{ + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].specialAnimActive) + { + FreeSpriteOamMatrix(&gSprites[gBattlerSpriteIds[gActiveBattler]]); + DestroySprite(&gSprites[gBattlerSpriteIds[gActiveBattler]]); + SetHealthboxSpriteInvisible(gHealthboxSpriteIds[gActiveBattler]); + PlayerBufferExecCompleted(); + } +} + +static void CompleteOnInactiveTextPrinter2(void) +{ + if (!IsTextPrinterActive(0)) + PlayerBufferExecCompleted(); +} + +static void OpenPartyMenuToChooseMon(void) +{ + if (!gPaletteFade.active) + { + u8 caseId; + + gBattlerControllerFuncs[gActiveBattler] = WaitForMonSelection; + caseId = gTasks[gUnknown_3004FFC[gActiveBattler]].data[0]; + DestroyTask(gUnknown_3004FFC[gActiveBattler]); + FreeAllWindowBuffers(); + OpenPartyMenuInBattle(caseId); + } +} + +static void WaitForMonSelection(void) +{ + if (gMain.callback2 == BattleMainCB2 && !gPaletteFade.active) + { + if (gUnknown_203B0C0 == 1) + BtlController_EmitChosenMonReturnValue(1, gUnknown_203B0C1, gUnknown_203B0DC); + else + BtlController_EmitChosenMonReturnValue(1, 6, NULL); + + if ((gBattleBufferA[gActiveBattler][1] & 0xF) == 1) + PrintLinkStandbyMsg(); + PlayerBufferExecCompleted(); + } +} + +static void OpenBagAndChooseItem(void) +{ + if (!gPaletteFade.active) + { + gBattlerControllerFuncs[gActiveBattler] = CompleteWhenChoseItem; + nullsub_44(); + FreeAllWindowBuffers(); + sub_8107ECC(); + } +} + +static void CompleteWhenChoseItem(void) +{ + if (gMain.callback2 == BattleMainCB2 && !gPaletteFade.active) + { + BtlController_EmitOneReturnValue(1, gSpecialVar_ItemId); + PlayerBufferExecCompleted(); + } +} + +static void CompleteOnSpecialAnimDone(void) +{ + if (!gDoingBattleAnim || !gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].specialAnimActive) + PlayerBufferExecCompleted(); +} + +static void DoHitAnimBlinkSpriteEffect(void) +{ + u8 spriteId = gBattlerSpriteIds[gActiveBattler]; + + if (gSprites[spriteId].data[1] == 32) + { + gSprites[spriteId].data[1] = 0; + gSprites[spriteId].invisible = FALSE; + gDoingBattleAnim = FALSE; + PlayerBufferExecCompleted(); + } + else + { + if ((gSprites[spriteId].data[1] % 4) == 0) + gSprites[spriteId].invisible ^= 1; + ++gSprites[spriteId].data[1]; + } +} + +static void MoveSelectionDisplayMoveNames(void) +{ + s32 i; + struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleBufferA[gActiveBattler][4]); + gNumberOfMovesToChoose = 0; + + for (i = 0; i < MAX_MON_MOVES; ++i) + { + MoveSelectionDestroyCursorAt(i); + StringCopy(gDisplayedStringBattle, gUnknown_83FE770); + StringAppend(gDisplayedStringBattle, gMoveNames[moveInfo->moves[i]]); + BattlePutTextOnWindow(gDisplayedStringBattle, i + 3); + if (moveInfo->moves[i] != MOVE_NONE) + ++gNumberOfMovesToChoose; + } +} + +static void MoveSelectionDisplayPpString(void) +{ + StringCopy(gDisplayedStringBattle, gText_MoveInterfacePP); + BattlePutTextOnWindow(gDisplayedStringBattle, 7); +} + +static void MoveSelectionDisplayPpNumber(void) +{ + u8 *txtPtr; + struct ChooseMoveStruct *moveInfo; + + if (gBattleBufferA[gActiveBattler][2] == TRUE) // check if we didn't want to display pp number + return; + SetPpNumbersPaletteInMoveSelection(); + moveInfo = (struct ChooseMoveStruct *)(&gBattleBufferA[gActiveBattler][4]); + txtPtr = ConvertIntToDecimalStringN(gDisplayedStringBattle, moveInfo->currentPp[gMoveSelectionCursor[gActiveBattler]], STR_CONV_MODE_RIGHT_ALIGN, 2); + *txtPtr = CHAR_SLASH; + ConvertIntToDecimalStringN(++txtPtr, moveInfo->maxPp[gMoveSelectionCursor[gActiveBattler]], STR_CONV_MODE_RIGHT_ALIGN, 2); + BattlePutTextOnWindow(gDisplayedStringBattle, 9); +} + +static void MoveSelectionDisplayMoveType(void) +{ + u8 *txtPtr; + struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleBufferA[gActiveBattler][4]); + + txtPtr = StringCopy(gDisplayedStringBattle, gText_MoveInterfaceType); + *txtPtr++ = EXT_CTRL_CODE_BEGIN; + *txtPtr++ = 6; + *txtPtr++ = 1; + txtPtr = StringCopy(txtPtr, gUnknown_83FE770); + StringCopy(txtPtr, gTypeNames[gBattleMoves[moveInfo->moves[gMoveSelectionCursor[gActiveBattler]]].type]); + BattlePutTextOnWindow(gDisplayedStringBattle, 8); +} + +void MoveSelectionCreateCursorAt(u8 cursorPosition, u8 arg1) +{ + u16 src[2]; + + src[0] = arg1 + 1; + src[1] = arg1 + 2; + CopyToBgTilemapBufferRect_ChangePalette(0, src, 9 * (cursorPosition & 1) + 1, 55 + (cursorPosition & 2), 1, 2, 0x11); + CopyBgTilemapBufferToVram(0); +} + +void MoveSelectionDestroyCursorAt(u8 cursorPosition) +{ + u16 src[2]; + + src[0] = 32; + src[1] = 32; + CopyToBgTilemapBufferRect_ChangePalette(0, src, 9 * (cursorPosition & 1) + 1, 55 + (cursorPosition & 2), 1, 2, 0x11); + CopyBgTilemapBufferToVram(0); +} + +void ActionSelectionCreateCursorAt(u8 cursorPosition, u8 arg1) +{ + u16 src[2]; + + src[0] = 1; + src[1] = 2; + CopyToBgTilemapBufferRect_ChangePalette(0, src, 7 * (cursorPosition & 1) + 16, 35 + (cursorPosition & 2), 1, 2, 0x11); + CopyBgTilemapBufferToVram(0); +} + +void ActionSelectionDestroyCursorAt(u8 cursorPosition) +{ + u16 src[2]; + + src[0] = 32; + src[1] = 32; + CopyToBgTilemapBufferRect_ChangePalette(0, src, 7 * (cursorPosition & 1) + 16, 35 + (cursorPosition & 2), 1, 2, 0x11); + CopyBgTilemapBufferToVram(0); +} + +void SetCB2ToReshowScreenAfterMenu(void) +{ + SetMainCallback2(ReshowBattleScreenAfterMenu); +} + +void SetCB2ToReshowScreenAfterMenu2(void) +{ + SetMainCallback2(ReshowBattleScreenAfterMenu); +} + +static void CompleteOnFinishedStatusAnimation(void) +{ + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].statusAnimActive) + PlayerBufferExecCompleted(); +} + +static void CompleteOnFinishedBattleAnimation(void) +{ + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animFromTableActive) + PlayerBufferExecCompleted(); +} + +static void PrintLinkStandbyMsg(void) +{ + if (gBattleTypeFlags & BATTLE_TYPE_LINK) + { + gBattle_BG0_X = 0; + gBattle_BG0_Y = 0; + BattlePutTextOnWindow(gText_LinkStandby, 0); + } +} + +static void PlayerHandleGetMonData(void) +{ + u8 monData[sizeof(struct Pokemon) * 2 + 56]; // this allows to get full data of two pokemon, trying to get more will result in overwriting data + u32 size = 0; + u8 monToCheck; + s32 i; + + if (gBattleBufferA[gActiveBattler][2] == 0) + { + size += CopyPlayerMonData(gBattlerPartyIndexes[gActiveBattler], monData); + } + else + { + monToCheck = gBattleBufferA[gActiveBattler][2]; + for (i = 0; i < PARTY_SIZE; ++i) + { + if (monToCheck & 1) + size += CopyPlayerMonData(i, monData + size); + monToCheck >>= 1; + } + } + BtlController_EmitDataTransfer(1, size, monData); + PlayerBufferExecCompleted(); +} + +static u32 CopyPlayerMonData(u8 monId, u8 *dst) +{ + struct BattlePokemon battleMon; + struct MovePpInfo moveData; + u8 nickname[20]; + u8 *src; + s16 data16; + u32 data32; + s32 size = 0; + + switch (gBattleBufferA[gActiveBattler][1]) + { + case REQUEST_ALL_BATTLE: + battleMon.species = GetMonData(&gPlayerParty[monId], MON_DATA_SPECIES); + battleMon.item = GetMonData(&gPlayerParty[monId], MON_DATA_HELD_ITEM); + for (size = 0; size < MAX_MON_MOVES; ++size) + { + battleMon.moves[size] = GetMonData(&gPlayerParty[monId], MON_DATA_MOVE1 + size); + battleMon.pp[size] = GetMonData(&gPlayerParty[monId], MON_DATA_PP1 + size); + } + battleMon.ppBonuses = GetMonData(&gPlayerParty[monId], MON_DATA_PP_BONUSES); + battleMon.friendship = GetMonData(&gPlayerParty[monId], MON_DATA_FRIENDSHIP); + battleMon.experience = GetMonData(&gPlayerParty[monId], MON_DATA_EXP); + battleMon.hpIV = GetMonData(&gPlayerParty[monId], MON_DATA_HP_IV); + battleMon.attackIV = GetMonData(&gPlayerParty[monId], MON_DATA_ATK_IV); + battleMon.defenseIV = GetMonData(&gPlayerParty[monId], MON_DATA_DEF_IV); + battleMon.speedIV = GetMonData(&gPlayerParty[monId], MON_DATA_SPEED_IV); + battleMon.spAttackIV = GetMonData(&gPlayerParty[monId], MON_DATA_SPATK_IV); + battleMon.spDefenseIV = GetMonData(&gPlayerParty[monId], MON_DATA_SPDEF_IV); + battleMon.personality = GetMonData(&gPlayerParty[monId], MON_DATA_PERSONALITY); + battleMon.status1 = GetMonData(&gPlayerParty[monId], MON_DATA_STATUS); + battleMon.level = GetMonData(&gPlayerParty[monId], MON_DATA_LEVEL); + battleMon.hp = GetMonData(&gPlayerParty[monId], MON_DATA_HP); + battleMon.maxHP = GetMonData(&gPlayerParty[monId], MON_DATA_MAX_HP); + battleMon.attack = GetMonData(&gPlayerParty[monId], MON_DATA_ATK); + battleMon.defense = GetMonData(&gPlayerParty[monId], MON_DATA_DEF); + battleMon.speed = GetMonData(&gPlayerParty[monId], MON_DATA_SPEED); + battleMon.spAttack = GetMonData(&gPlayerParty[monId], MON_DATA_SPATK); + battleMon.spDefense = GetMonData(&gPlayerParty[monId], MON_DATA_SPDEF); + battleMon.isEgg = GetMonData(&gPlayerParty[monId], MON_DATA_IS_EGG); + battleMon.abilityNum = GetMonData(&gPlayerParty[monId], MON_DATA_ABILITY_NUM); + battleMon.otId = GetMonData(&gPlayerParty[monId], MON_DATA_OT_ID); + GetMonData(&gPlayerParty[monId], MON_DATA_NICKNAME, nickname); + StringCopy10(battleMon.nickname, nickname); + GetMonData(&gPlayerParty[monId], MON_DATA_OT_NAME, battleMon.otName); + src = (u8 *)&battleMon; + for (size = 0; size < sizeof(battleMon); ++size) + dst[size] = src[size]; + break; + case REQUEST_SPECIES_BATTLE: + data16 = GetMonData(&gPlayerParty[monId], MON_DATA_SPECIES); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_HELDITEM_BATTLE: + data16 = GetMonData(&gPlayerParty[monId], MON_DATA_HELD_ITEM); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_MOVES_PP_BATTLE: + for (size = 0; size < MAX_MON_MOVES; ++size) + { + moveData.moves[size] = GetMonData(&gPlayerParty[monId], MON_DATA_MOVE1 + size); + moveData.pp[size] = GetMonData(&gPlayerParty[monId], MON_DATA_PP1 + size); + } + moveData.ppBonuses = GetMonData(&gPlayerParty[monId], MON_DATA_PP_BONUSES); + src = (u8 *)(&moveData); + for (size = 0; size < sizeof(moveData); ++size) + dst[size] = src[size]; + break; + case REQUEST_MOVE1_BATTLE: + case REQUEST_MOVE2_BATTLE: + case REQUEST_MOVE3_BATTLE: + case REQUEST_MOVE4_BATTLE: + data16 = GetMonData(&gPlayerParty[monId], MON_DATA_MOVE1 + gBattleBufferA[gActiveBattler][1] - REQUEST_MOVE1_BATTLE); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_PP_DATA_BATTLE: + for (size = 0; size < MAX_MON_MOVES; ++size) + dst[size] = GetMonData(&gPlayerParty[monId], MON_DATA_PP1 + size); + dst[size] = GetMonData(&gPlayerParty[monId], MON_DATA_PP_BONUSES); + ++size; + break; + case REQUEST_PPMOVE1_BATTLE: + case REQUEST_PPMOVE2_BATTLE: + case REQUEST_PPMOVE3_BATTLE: + case REQUEST_PPMOVE4_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_PP1 + gBattleBufferA[gActiveBattler][1] - REQUEST_PPMOVE1_BATTLE); + size = 1; + break; + case REQUEST_OTID_BATTLE: + data32 = GetMonData(&gPlayerParty[monId], MON_DATA_OT_ID); + dst[0] = (data32 & 0x000000FF); + dst[1] = (data32 & 0x0000FF00) >> 8; + dst[2] = (data32 & 0x00FF0000) >> 16; + size = 3; + break; + case REQUEST_EXP_BATTLE: + data32 = GetMonData(&gPlayerParty[monId], MON_DATA_EXP); + dst[0] = (data32 & 0x000000FF); + dst[1] = (data32 & 0x0000FF00) >> 8; + dst[2] = (data32 & 0x00FF0000) >> 16; + size = 3; + break; + case REQUEST_HP_EV_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_HP_EV); + size = 1; + break; + case REQUEST_ATK_EV_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_ATK_EV); + size = 1; + break; + case REQUEST_DEF_EV_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_DEF_EV); + size = 1; + break; + case REQUEST_SPEED_EV_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_SPEED_EV); + size = 1; + break; + case REQUEST_SPATK_EV_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_SPATK_EV); + size = 1; + break; + case REQUEST_SPDEF_EV_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_SPDEF_EV); + size = 1; + break; + case REQUEST_FRIENDSHIP_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_FRIENDSHIP); + size = 1; + break; + case REQUEST_POKERUS_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_POKERUS); + size = 1; + break; + case REQUEST_MET_LOCATION_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_MET_LOCATION); + size = 1; + break; + case REQUEST_MET_LEVEL_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_MET_LEVEL); + size = 1; + break; + case REQUEST_MET_GAME_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_MET_GAME); + size = 1; + break; + case REQUEST_POKEBALL_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_POKEBALL); + size = 1; + break; + case REQUEST_ALL_IVS_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_HP_IV); + dst[1] = GetMonData(&gPlayerParty[monId], MON_DATA_ATK_IV); + dst[2] = GetMonData(&gPlayerParty[monId], MON_DATA_DEF_IV); + dst[3] = GetMonData(&gPlayerParty[monId], MON_DATA_SPEED_IV); + dst[4] = GetMonData(&gPlayerParty[monId], MON_DATA_SPATK_IV); + dst[5] = GetMonData(&gPlayerParty[monId], MON_DATA_SPDEF_IV); + size = 6; + break; + case REQUEST_HP_IV_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_HP_IV); + size = 1; + break; + case REQUEST_ATK_IV_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_ATK_IV); + size = 1; + break; + case REQUEST_DEF_IV_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_DEF_IV); + size = 1; + break; + case REQUEST_SPEED_IV_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_SPEED_IV); + size = 1; + break; + case REQUEST_SPATK_IV_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_SPATK_IV); + size = 1; + break; + case REQUEST_SPDEF_IV_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_SPDEF_IV); + size = 1; + break; + case REQUEST_PERSONALITY_BATTLE: + data32 = GetMonData(&gPlayerParty[monId], MON_DATA_PERSONALITY); + dst[0] = (data32 & 0x000000FF); + dst[1] = (data32 & 0x0000FF00) >> 8; + dst[2] = (data32 & 0x00FF0000) >> 16; + dst[3] = (data32 & 0xFF000000) >> 24; + size = 4; + break; + case REQUEST_CHECKSUM_BATTLE: + data16 = GetMonData(&gPlayerParty[monId], MON_DATA_CHECKSUM); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_STATUS_BATTLE: + data32 = GetMonData(&gPlayerParty[monId], MON_DATA_STATUS); + dst[0] = (data32 & 0x000000FF); + dst[1] = (data32 & 0x0000FF00) >> 8; + dst[2] = (data32 & 0x00FF0000) >> 16; + dst[3] = (data32 & 0xFF000000) >> 24; + size = 4; + break; + case REQUEST_LEVEL_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_LEVEL); + size = 1; + break; + case REQUEST_HP_BATTLE: + data16 = GetMonData(&gPlayerParty[monId], MON_DATA_HP); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_MAX_HP_BATTLE: + data16 = GetMonData(&gPlayerParty[monId], MON_DATA_MAX_HP); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_ATK_BATTLE: + data16 = GetMonData(&gPlayerParty[monId], MON_DATA_ATK); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_DEF_BATTLE: + data16 = GetMonData(&gPlayerParty[monId], MON_DATA_DEF); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_SPEED_BATTLE: + data16 = GetMonData(&gPlayerParty[monId], MON_DATA_SPEED); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_SPATK_BATTLE: + data16 = GetMonData(&gPlayerParty[monId], MON_DATA_SPATK); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_SPDEF_BATTLE: + data16 = GetMonData(&gPlayerParty[monId], MON_DATA_SPDEF); + dst[0] = data16; + dst[1] = data16 >> 8; + size = 2; + break; + case REQUEST_COOL_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_COOL); + size = 1; + break; + case REQUEST_BEAUTY_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_BEAUTY); + size = 1; + break; + case REQUEST_CUTE_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_CUTE); + size = 1; + break; + case REQUEST_SMART_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_SMART); + size = 1; + break; + case REQUEST_TOUGH_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_TOUGH); + size = 1; + break; + case REQUEST_SHEEN_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_SHEEN); + size = 1; + break; + case REQUEST_COOL_RIBBON_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_COOL_RIBBON); + size = 1; + break; + case REQUEST_BEAUTY_RIBBON_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_BEAUTY_RIBBON); + size = 1; + break; + case REQUEST_CUTE_RIBBON_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_CUTE_RIBBON); + size = 1; + break; + case REQUEST_SMART_RIBBON_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_SMART_RIBBON); + size = 1; + break; + case REQUEST_TOUGH_RIBBON_BATTLE: + dst[0] = GetMonData(&gPlayerParty[monId], MON_DATA_TOUGH_RIBBON); + size = 1; + break; + } + return size; +} + +void PlayerHandleGetRawMonData(void) +{ + struct BattlePokemon battleMon; + u8 *src = (u8 *)&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]] + gBattleBufferA[gActiveBattler][1]; + u8 *dst = (u8 *)&battleMon + gBattleBufferA[gActiveBattler][1]; + u8 i; + + for (i = 0; i < gBattleBufferA[gActiveBattler][2]; ++i) + dst[i] = src[i]; + + BtlController_EmitDataTransfer(1, gBattleBufferA[gActiveBattler][2], dst); + PlayerBufferExecCompleted(); +} + +static void PlayerHandleSetMonData(void) +{ + u8 monToCheck; + u8 i; + + if (gBattleBufferA[gActiveBattler][2] == 0) + { + SetPlayerMonData(gBattlerPartyIndexes[gActiveBattler]); + } + else + { + monToCheck = gBattleBufferA[gActiveBattler][2]; + for (i = 0; i < PARTY_SIZE; ++i) + { + if (monToCheck & 1) + SetPlayerMonData(i); + monToCheck >>= 1; + } + } + PlayerBufferExecCompleted(); +} + +static void SetPlayerMonData(u8 monId) +{ + struct BattlePokemon *battlePokemon = (struct BattlePokemon *)&gBattleBufferA[gActiveBattler][3]; + struct MovePpInfo *moveData = (struct MovePpInfo *)&gBattleBufferA[gActiveBattler][3]; + s32 i; + + switch (gBattleBufferA[gActiveBattler][1]) + { + case REQUEST_ALL_BATTLE: + { + u8 iv; + + SetMonData(&gPlayerParty[monId], MON_DATA_SPECIES, &battlePokemon->species); + SetMonData(&gPlayerParty[monId], MON_DATA_HELD_ITEM, &battlePokemon->item); + for (i = 0; i < MAX_MON_MOVES; ++i) + { + SetMonData(&gPlayerParty[monId], MON_DATA_MOVE1 + i, &battlePokemon->moves[i]); + SetMonData(&gPlayerParty[monId], MON_DATA_PP1 + i, &battlePokemon->pp[i]); + } + SetMonData(&gPlayerParty[monId], MON_DATA_PP_BONUSES, &battlePokemon->ppBonuses); + SetMonData(&gPlayerParty[monId], MON_DATA_FRIENDSHIP, &battlePokemon->friendship); + SetMonData(&gPlayerParty[monId], MON_DATA_EXP, &battlePokemon->experience); + iv = battlePokemon->hpIV; + SetMonData(&gPlayerParty[monId], MON_DATA_HP_IV, &iv); + iv = battlePokemon->attackIV; + SetMonData(&gPlayerParty[monId], MON_DATA_ATK_IV, &iv); + iv = battlePokemon->defenseIV; + SetMonData(&gPlayerParty[monId], MON_DATA_DEF_IV, &iv); + iv = battlePokemon->speedIV; + SetMonData(&gPlayerParty[monId], MON_DATA_SPEED_IV, &iv); + iv = battlePokemon->spAttackIV; + SetMonData(&gPlayerParty[monId], MON_DATA_SPATK_IV, &iv); + iv = battlePokemon->spDefenseIV; + SetMonData(&gPlayerParty[monId], MON_DATA_SPDEF_IV, &iv); + SetMonData(&gPlayerParty[monId], MON_DATA_PERSONALITY, &battlePokemon->personality); + SetMonData(&gPlayerParty[monId], MON_DATA_STATUS, &battlePokemon->status1); + SetMonData(&gPlayerParty[monId], MON_DATA_LEVEL, &battlePokemon->level); + SetMonData(&gPlayerParty[monId], MON_DATA_HP, &battlePokemon->hp); + SetMonData(&gPlayerParty[monId], MON_DATA_MAX_HP, &battlePokemon->maxHP); + SetMonData(&gPlayerParty[monId], MON_DATA_ATK, &battlePokemon->attack); + SetMonData(&gPlayerParty[monId], MON_DATA_DEF, &battlePokemon->defense); + SetMonData(&gPlayerParty[monId], MON_DATA_SPEED, &battlePokemon->speed); + SetMonData(&gPlayerParty[monId], MON_DATA_SPATK, &battlePokemon->spAttack); + SetMonData(&gPlayerParty[monId], MON_DATA_SPDEF, &battlePokemon->spDefense); + } + break; + case REQUEST_SPECIES_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_SPECIES, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_HELDITEM_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_HELD_ITEM, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_MOVES_PP_BATTLE: + for (i = 0; i < MAX_MON_MOVES; ++i) + { + SetMonData(&gPlayerParty[monId], MON_DATA_MOVE1 + i, &moveData->moves[i]); + SetMonData(&gPlayerParty[monId], MON_DATA_PP1 + i, &moveData->pp[i]); + } + SetMonData(&gPlayerParty[monId], MON_DATA_PP_BONUSES, &moveData->ppBonuses); + break; + case REQUEST_MOVE1_BATTLE: + case REQUEST_MOVE2_BATTLE: + case REQUEST_MOVE3_BATTLE: + case REQUEST_MOVE4_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_MOVE1 + gBattleBufferA[gActiveBattler][1] - REQUEST_MOVE1_BATTLE, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_PP_DATA_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_PP1, &gBattleBufferA[gActiveBattler][3]); + SetMonData(&gPlayerParty[monId], MON_DATA_PP2, &gBattleBufferA[gActiveBattler][4]); + SetMonData(&gPlayerParty[monId], MON_DATA_PP3, &gBattleBufferA[gActiveBattler][5]); + SetMonData(&gPlayerParty[monId], MON_DATA_PP4, &gBattleBufferA[gActiveBattler][6]); + SetMonData(&gPlayerParty[monId], MON_DATA_PP_BONUSES, &gBattleBufferA[gActiveBattler][7]); + break; + case REQUEST_PPMOVE1_BATTLE: + case REQUEST_PPMOVE2_BATTLE: + case REQUEST_PPMOVE3_BATTLE: + case REQUEST_PPMOVE4_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_PP1 + gBattleBufferA[gActiveBattler][1] - REQUEST_PPMOVE1_BATTLE, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_OTID_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_OT_ID, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_EXP_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_EXP, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_HP_EV_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_HP_EV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_ATK_EV_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_ATK_EV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_DEF_EV_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_DEF_EV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SPEED_EV_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_SPEED_EV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SPATK_EV_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_SPATK_EV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SPDEF_EV_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_SPDEF_EV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_FRIENDSHIP_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_FRIENDSHIP, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_POKERUS_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_POKERUS, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_MET_LOCATION_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_MET_LOCATION, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_MET_LEVEL_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_MET_LEVEL, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_MET_GAME_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_MET_GAME, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_POKEBALL_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_POKEBALL, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_ALL_IVS_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_HP_IV, &gBattleBufferA[gActiveBattler][3]); + SetMonData(&gPlayerParty[monId], MON_DATA_ATK_IV, &gBattleBufferA[gActiveBattler][4]); + SetMonData(&gPlayerParty[monId], MON_DATA_DEF_IV, &gBattleBufferA[gActiveBattler][5]); + SetMonData(&gPlayerParty[monId], MON_DATA_SPEED_IV, &gBattleBufferA[gActiveBattler][6]); + SetMonData(&gPlayerParty[monId], MON_DATA_SPATK_IV, &gBattleBufferA[gActiveBattler][7]); + SetMonData(&gPlayerParty[monId], MON_DATA_SPDEF_IV, &gBattleBufferA[gActiveBattler][8]); + break; + case REQUEST_HP_IV_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_HP_IV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_ATK_IV_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_ATK_IV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_DEF_IV_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_DEF_IV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SPEED_IV_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_SPEED_IV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SPATK_IV_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_SPATK_IV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SPDEF_IV_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_SPDEF_IV, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_PERSONALITY_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_PERSONALITY, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_CHECKSUM_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_CHECKSUM, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_STATUS_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_STATUS, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_LEVEL_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_LEVEL, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_HP_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_HP, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_MAX_HP_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_MAX_HP, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_ATK_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_ATK, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_DEF_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_DEF, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SPEED_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_SPEED, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SPATK_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_SPATK, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SPDEF_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_SPDEF, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_COOL_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_COOL, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_BEAUTY_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_BEAUTY, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_CUTE_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_CUTE, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SMART_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_SMART, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_TOUGH_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_TOUGH, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SHEEN_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_SHEEN, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_COOL_RIBBON_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_COOL_RIBBON, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_BEAUTY_RIBBON_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_BEAUTY_RIBBON, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_CUTE_RIBBON_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_CUTE_RIBBON, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_SMART_RIBBON_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_SMART_RIBBON, &gBattleBufferA[gActiveBattler][3]); + break; + case REQUEST_TOUGH_RIBBON_BATTLE: + SetMonData(&gPlayerParty[monId], MON_DATA_TOUGH_RIBBON, &gBattleBufferA[gActiveBattler][3]); + break; + } + HandleLowHpMusicChange(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], gActiveBattler); +} + +static void PlayerHandleSetRawMonData(void) +{ + u8 *dst = (u8 *)&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]] + gBattleBufferA[gActiveBattler][1]; + u8 i; + + for (i = 0; i < gBattleBufferA[gActiveBattler][2]; ++i) + dst[i] = gBattleBufferA[gActiveBattler][3 + i]; + PlayerBufferExecCompleted(); +} + +static void PlayerHandleLoadMonSprite(void) +{ + BattleLoadPlayerMonSpriteGfx(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], gActiveBattler); + gSprites[gBattlerSpriteIds[gActiveBattler]].oam.paletteNum = gActiveBattler; + gBattlerControllerFuncs[gActiveBattler] = CompleteOnBattlerSpritePosX_0; +} + +static void PlayerHandleSwitchInAnim(void) +{ + ClearTemporarySpeciesSpriteData(gActiveBattler, gBattleBufferA[gActiveBattler][2]); + gBattlerPartyIndexes[gActiveBattler] = gBattleBufferA[gActiveBattler][1]; + BattleLoadPlayerMonSpriteGfx(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], gActiveBattler); + gActionSelectionCursor[gActiveBattler] = 0; + gMoveSelectionCursor[gActiveBattler] = 0; + sub_8031FF4(gActiveBattler, gBattleBufferA[gActiveBattler][2]); + gBattlerControllerFuncs[gActiveBattler] = sub_802FD18; +} + +static void sub_8031FF4(u8 battlerId, bool8 dontClearSubstituteBit) +{ + u16 species; + + ClearTemporarySpeciesSpriteData(battlerId, dontClearSubstituteBit); + gBattlerPartyIndexes[battlerId] = gBattleBufferA[battlerId][1]; + species = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerId]], MON_DATA_SPECIES); + gUnknown_3004FFC[battlerId] = CreateInvisibleSpriteWithCallback(sub_8033E3C); + SetMultiuseSpriteTemplateToPokemon(species, GetBattlerPosition(battlerId)); + gBattlerSpriteIds[battlerId] = CreateSprite(&gMultiuseSpriteTemplate, + GetBattlerSpriteCoord(battlerId, 2), + GetBattlerSpriteDefault_Y(battlerId), + GetBattlerSpriteSubpriority(battlerId)); + gSprites[gUnknown_3004FFC[battlerId]].data[1] = gBattlerSpriteIds[battlerId]; + gSprites[gBattlerSpriteIds[battlerId]].data[0] = battlerId; + gSprites[gBattlerSpriteIds[battlerId]].data[2] = species; + gSprites[gBattlerSpriteIds[battlerId]].oam.paletteNum = battlerId; + StartSpriteAnim(&gSprites[gBattlerSpriteIds[battlerId]], gBattleMonForms[battlerId]); + gSprites[gBattlerSpriteIds[battlerId]].invisible = TRUE; + gSprites[gBattlerSpriteIds[battlerId]].callback = SpriteCallbackDummy; + gSprites[gUnknown_3004FFC[battlerId]].data[0] = DoPokeballSendOutAnimation(0, POKEBALL_PLAYER_SENDOUT); +} + +static void PlayerHandleReturnMonToBall(void) +{ + if (gBattleBufferA[gActiveBattler][1] == 0) + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 0; + gBattlerControllerFuncs[gActiveBattler] = DoSwitchOutAnimation; + } + else + { + FreeSpriteOamMatrix(&gSprites[gBattlerSpriteIds[gActiveBattler]]); + DestroySprite(&gSprites[gBattlerSpriteIds[gActiveBattler]]); + SetHealthboxSpriteInvisible(gHealthboxSpriteIds[gActiveBattler]); + PlayerBufferExecCompleted(); + } +} + +static void DoSwitchOutAnimation(void) +{ + switch (gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState) + { + case 0: + if (gBattleSpritesDataPtr->battlerData[gActiveBattler].behindSubstitute) + InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, gActiveBattler, B_ANIM_SUBSTITUTE_TO_MON); + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 1; + break; + case 1: + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].specialAnimActive) + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 0; + InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, gActiveBattler, B_ANIM_SWITCH_OUT_PLAYER_MON); + gBattlerControllerFuncs[gActiveBattler] = sub_80305A0; + } + break; + } +} + +static void PlayerHandleDrawTrainerPic(void) +{ + s16 xPos; + u32 trainerPicId; + + if (gBattleTypeFlags & BATTLE_TYPE_MULTI) + { + if ((GetBattlerPosition(gActiveBattler) & BIT_FLANK) != B_FLANK_LEFT) // Second mon, on the right. + xPos = 90; + else // First mon, on the left. + xPos = 32; + + } + else + { + xPos = 80; + } + if (gBattleTypeFlags & BATTLE_TYPE_LINK) + { + if ((gLinkPlayers[GetMultiplayerId()].version & 0xFF) == VERSION_RUBY + || (gLinkPlayers[GetMultiplayerId()].version & 0xFF) == VERSION_SAPPHIRE + || (gLinkPlayers[GetMultiplayerId()].version & 0xFF) == VERSION_EMERALD) + trainerPicId = gLinkPlayers[GetMultiplayerId()].gender + 2; + else + trainerPicId = gLinkPlayers[GetMultiplayerId()].gender + 0; + } + else + { + trainerPicId = gSaveBlock2Ptr->playerGender + 0; + } + DecompressTrainerBackPalette(trainerPicId, gActiveBattler); + SetMultiuseSpriteTemplateToTrainerBack(trainerPicId, GetBattlerPosition(gActiveBattler)); + gBattlerSpriteIds[gActiveBattler] = CreateSprite(&gMultiuseSpriteTemplate, + xPos, + (8 - gTrainerBackPicCoords[trainerPicId].size) * 4 + 80, + GetBattlerSpriteSubpriority(gActiveBattler)); + gSprites[gBattlerSpriteIds[gActiveBattler]].oam.paletteNum = gActiveBattler; + gSprites[gBattlerSpriteIds[gActiveBattler]].pos2.x = 240; + gSprites[gBattlerSpriteIds[gActiveBattler]].data[0] = -2; + gSprites[gBattlerSpriteIds[gActiveBattler]].callback = sub_8033EEC; + gBattlerControllerFuncs[gActiveBattler] = CompleteOnBattlerSpriteCallbackDummy; +} + +static void PlayerHandleTrainerSlide(void) +{ + u32 trainerPicId; + + if (gBattleTypeFlags & BATTLE_TYPE_LINK) + { + if ((gLinkPlayers[GetMultiplayerId()].version & 0xFF) == VERSION_RUBY + || (gLinkPlayers[GetMultiplayerId()].version & 0xFF) == VERSION_SAPPHIRE + || (gLinkPlayers[GetMultiplayerId()].version & 0xFF) == VERSION_EMERALD) + trainerPicId = gLinkPlayers[GetMultiplayerId()].gender + 2; + else + trainerPicId = gLinkPlayers[GetMultiplayerId()].gender + 0; + } + else + { + trainerPicId = gSaveBlock2Ptr->playerGender + 0; + } + DecompressTrainerBackPalette(trainerPicId, gActiveBattler); + SetMultiuseSpriteTemplateToTrainerBack(trainerPicId, GetBattlerPosition(gActiveBattler)); + gBattlerSpriteIds[gActiveBattler] = CreateSprite(&gMultiuseSpriteTemplate, + 80, + (8 - gTrainerBackPicCoords[trainerPicId].size) * 4 + 80, + 30); + gSprites[gBattlerSpriteIds[gActiveBattler]].oam.paletteNum = gActiveBattler; + gSprites[gBattlerSpriteIds[gActiveBattler]].pos2.x = -96; + gSprites[gBattlerSpriteIds[gActiveBattler]].data[0] = 2; + gSprites[gBattlerSpriteIds[gActiveBattler]].callback = sub_8033EEC; + gBattlerControllerFuncs[gActiveBattler] = CompleteOnBattlerSpriteCallbackDummy2; +} + +static void PlayerHandleTrainerSlideBack(void) +{ + SetSpritePrimaryCoordsFromSecondaryCoords(&gSprites[gBattlerSpriteIds[gActiveBattler]]); + gSprites[gBattlerSpriteIds[gActiveBattler]].data[0] = 50; + gSprites[gBattlerSpriteIds[gActiveBattler]].data[2] = -40; + gSprites[gBattlerSpriteIds[gActiveBattler]].data[4] = gSprites[gBattlerSpriteIds[gActiveBattler]].pos1.y; + gSprites[gBattlerSpriteIds[gActiveBattler]].callback = StartAnimLinearTranslation; + StoreSpriteCallbackInData6(&gSprites[gBattlerSpriteIds[gActiveBattler]], SpriteCallbackDummy); + StartSpriteAnim(&gSprites[gBattlerSpriteIds[gActiveBattler]], 1); + gBattlerControllerFuncs[gActiveBattler] = sub_802F7A0; +} + +static void PlayerHandleFaintAnimation(void) +{ + if (gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState == 0) + { + if (gBattleSpritesDataPtr->battlerData[gActiveBattler].behindSubstitute) + InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, gActiveBattler, B_ANIM_SUBSTITUTE_TO_MON); + ++gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState; + } + else + { + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].specialAnimActive) + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 0; + HandleLowHpMusicChange(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], gActiveBattler); + PlaySE12WithPanning(SE_POKE_DEAD, SOUND_PAN_ATTACKER); + gSprites[gBattlerSpriteIds[gActiveBattler]].data[1] = 0; + gSprites[gBattlerSpriteIds[gActiveBattler]].data[2] = 5; + gSprites[gBattlerSpriteIds[gActiveBattler]].callback = sub_8012110; + gBattlerControllerFuncs[gActiveBattler] = sub_8030538; + } + } +} + +static void PlayerHandlePaletteFade(void) +{ + BeginNormalPaletteFade(0xFFFFFFFF, 2, 0, 16, RGB_BLACK); + PlayerBufferExecCompleted(); +} + +static void PlayerHandleSuccessBallThrowAnim(void) +{ + gBattleSpritesDataPtr->animationData->ballThrowCaseId = BALL_3_SHAKES_SUCCESS; + gDoingBattleAnim = TRUE; + InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT), B_ANIM_BALL_THROW); + gBattlerControllerFuncs[gActiveBattler] = CompleteOnSpecialAnimDone; +} + +static void PlayerHandleBallThrowAnim(void) +{ + u8 ballThrowCaseId = gBattleBufferA[gActiveBattler][1]; + + gBattleSpritesDataPtr->animationData->ballThrowCaseId = ballThrowCaseId; + gDoingBattleAnim = TRUE; + InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT), B_ANIM_BALL_THROW); + gBattlerControllerFuncs[gActiveBattler] = CompleteOnSpecialAnimDone; +} + +static void PlayerHandlePause(void) +{ + u8 var = gBattleBufferA[gActiveBattler][1]; + + while (var) + --var; + PlayerBufferExecCompleted(); +} + +static void PlayerHandleMoveAnimation(void) +{ + if (!mplay_80342A4(gActiveBattler)) + { + u16 move = gBattleBufferA[gActiveBattler][1] | (gBattleBufferA[gActiveBattler][2] << 8); + + gAnimMoveTurn = gBattleBufferA[gActiveBattler][3]; + gAnimMovePower = gBattleBufferA[gActiveBattler][4] | (gBattleBufferA[gActiveBattler][5] << 8); + gAnimMoveDmg = gBattleBufferA[gActiveBattler][6] | (gBattleBufferA[gActiveBattler][7] << 8) | (gBattleBufferA[gActiveBattler][8] << 16) | (gBattleBufferA[gActiveBattler][9] << 24); + gAnimFriendship = gBattleBufferA[gActiveBattler][10]; + gWeatherMoveAnim = gBattleBufferA[gActiveBattler][12] | (gBattleBufferA[gActiveBattler][13] << 8); + gAnimDisableStructPtr = (struct DisableStruct *)&gBattleBufferA[gActiveBattler][16]; + gTransformedPersonalities[gActiveBattler] = gAnimDisableStructPtr->transformedMonPersonality; + if (IsMoveWithoutAnimation(move, gAnimMoveTurn)) // Always returns FALSE. + { + PlayerBufferExecCompleted(); + } + else + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 0; + gBattlerControllerFuncs[gActiveBattler] = PlayerDoMoveAnimation; + } + } +} + +static void PlayerDoMoveAnimation(void) +{ + u16 move = gBattleBufferA[gActiveBattler][1] | (gBattleBufferA[gActiveBattler][2] << 8); + u8 multihit = gBattleBufferA[gActiveBattler][11]; + + switch (gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState) + { + case 0: + if (gBattleSpritesDataPtr->battlerData[gActiveBattler].behindSubstitute + && !gBattleSpritesDataPtr->battlerData[gActiveBattler].flag_x8) + { + gBattleSpritesDataPtr->battlerData[gActiveBattler].flag_x8 = 1; + InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, gActiveBattler, B_ANIM_SUBSTITUTE_TO_MON); + } + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 1; + break; + case 1: + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].specialAnimActive) + { + sub_8035450(0); + DoMoveAnim(move); + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 2; + } + break; + case 2: + gAnimScriptCallback(); + if (!gAnimScriptActive) + { + sub_8035450(1); + if (gBattleSpritesDataPtr->battlerData[gActiveBattler].behindSubstitute && multihit < 2) + { + InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, gActiveBattler, B_ANIM_MON_TO_SUBSTITUTE); + gBattleSpritesDataPtr->battlerData[gActiveBattler].flag_x8 = 0; + } + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 3; + } + break; + case 3: + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].specialAnimActive) + { + CopyAllBattleSpritesInvisibilities(); + TrySetBehindSubstituteSpriteBit(gActiveBattler, gBattleBufferA[gActiveBattler][1] | (gBattleBufferA[gActiveBattler][2] << 8)); + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animationState = 0; + PlayerBufferExecCompleted(); + } + break; + } +} + +static void PlayerHandlePrintString(void) +{ + u16 *stringId; + + gBattle_BG0_X = 0; + gBattle_BG0_Y = 0; + stringId = (u16 *)(&gBattleBufferA[gActiveBattler][2]); + BufferStringBattle(*stringId); + if (sub_80D89B0(*stringId)) + BattlePutTextOnWindow(gDisplayedStringBattle, 64); + else + BattlePutTextOnWindow(gDisplayedStringBattle, 0); + gBattlerControllerFuncs[gActiveBattler] = CompleteOnInactiveTextPrinter2; +} + +static void PlayerHandlePrintSelectionString(void) +{ + if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER) + PlayerHandlePrintString(); + else + PlayerBufferExecCompleted(); +} + +static void HandleChooseActionAfterDma3(void) +{ + if (!IsDma3ManagerBusyWithBgCopy()) + { + gBattle_BG0_X = 0; + gBattle_BG0_Y = 160; + gBattlerControllerFuncs[gActiveBattler] = HandleInputChooseAction; + } +} + +static void PlayerHandleChooseAction(void) +{ + s32 i; + + gBattlerControllerFuncs[gActiveBattler] = HandleChooseActionAfterDma3; + BattlePutTextOnWindow(gUnknown_83FDA4C, 0); + BattlePutTextOnWindow(gUnknown_83FE725, 2); + for (i = 0; i < 4; ++i) + ActionSelectionDestroyCursorAt(i); + ActionSelectionCreateCursorAt(gActionSelectionCursor[gActiveBattler], 0); + BattleStringExpandPlaceholdersToDisplayedString(gText_WhatWillPkmnDo); + BattlePutTextOnWindow(gDisplayedStringBattle, 1); +} + +static void PlayerHandleUnknownYesNoBox(void) +{ +} + +static void HandleChooseMoveAfterDma3(void) +{ + if (!IsDma3ManagerBusyWithBgCopy()) + { + gBattle_BG0_X = 0; + gBattle_BG0_Y = 320; + gBattlerControllerFuncs[gActiveBattler] = HandleInputChooseMove; + } +} + +static void PlayerHandleChooseMove(void) +{ + InitMoveSelectionsVarsAndStrings(); + gBattlerControllerFuncs[gActiveBattler] = HandleChooseMoveAfterDma3; +} + +void InitMoveSelectionsVarsAndStrings(void) +{ + MoveSelectionDisplayMoveNames(); + gMultiUsePlayerCursor = 0xFF; + MoveSelectionCreateCursorAt(gMoveSelectionCursor[gActiveBattler], 0); + MoveSelectionDisplayPpString(); + MoveSelectionDisplayPpNumber(); + MoveSelectionDisplayMoveType(); +} + +static void PlayerHandleChooseItem(void) +{ + s32 i; + + BeginNormalPaletteFade(0xFFFFFFFF, 0, 0, 0x10, RGB_BLACK); + gBattlerControllerFuncs[gActiveBattler] = OpenBagAndChooseItem; + gBattlerInMenuId = gActiveBattler; + for (i = 0; i < 3; ++i) + gUnknown_203B0DC[i] = gBattleBufferA[gActiveBattler][1 + i]; +} + +static void PlayerHandleChoosePokemon(void) +{ + s32 i; + + gUnknown_3004FFC[gActiveBattler] = CreateTask(TaskDummy, 0xFF); + gTasks[gUnknown_3004FFC[gActiveBattler]].data[0] = gBattleBufferA[gActiveBattler][1] & 0xF; + *(&gBattleStruct->battlerPreventingSwitchout) = gBattleBufferA[gActiveBattler][1] >> 4; + *(&gBattleStruct->field_8B) = gBattleBufferA[gActiveBattler][2]; + *(&gBattleStruct->abilityPreventingSwitchout) = gBattleBufferA[gActiveBattler][3]; + for (i = 0; i < 3; ++i) + gUnknown_203B0DC[i] = gBattleBufferA[gActiveBattler][4 + i]; + BeginNormalPaletteFade(0xFFFFFFFF, 0, 0, 0x10, RGB_BLACK); + gBattlerControllerFuncs[gActiveBattler] = OpenPartyMenuToChooseMon; + gBattlerInMenuId = gActiveBattler; +} + +static void PlayerHandleCmd23(void) +{ + BattleStopLowHpSound(); + BeginNormalPaletteFade(0xFFFFFFFF, 2, 0, 16, RGB_BLACK); + PlayerBufferExecCompleted(); +} + +static void PlayerHandleHealthBarUpdate(void) +{ + s16 hpVal; + + LoadBattleBarGfx(0); + hpVal = gBattleBufferA[gActiveBattler][2] | (gBattleBufferA[gActiveBattler][3] << 8); + if (hpVal != INSTANT_HP_BAR_DROP) + { + u32 maxHP = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_MAX_HP); + u32 curHP = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_HP); + + SetBattleBarStruct(gActiveBattler, gHealthboxSpriteIds[gActiveBattler], maxHP, curHP, hpVal); + } + else + { + u32 maxHP = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_MAX_HP); + + SetBattleBarStruct(gActiveBattler, gHealthboxSpriteIds[gActiveBattler], maxHP, 0, hpVal); + UpdateHpTextInHealthbox(gHealthboxSpriteIds[gActiveBattler], 0, HP_CURRENT); + } + gBattlerControllerFuncs[gActiveBattler] = CompleteOnHealthbarDone; +} + +static void PlayerHandleExpUpdate(void) +{ + u8 monId = gBattleBufferA[gActiveBattler][1]; + + if (GetMonData(&gPlayerParty[monId], MON_DATA_LEVEL) >= MAX_LEVEL) + { + PlayerBufferExecCompleted(); + } + else + { + s16 expPointsToGive; + u8 taskId; + + LoadBattleBarGfx(1); + GetMonData(&gPlayerParty[monId], MON_DATA_SPECIES); // Unused return value. + expPointsToGive = T1_READ_16(&gBattleBufferA[gActiveBattler][2]); + taskId = CreateTask(Task_GiveExpToMon, 10); + gTasks[taskId].tExpTask_monId = monId; + gTasks[taskId].tExpTask_gainedExp = expPointsToGive; + gTasks[taskId].tExpTask_battler = gActiveBattler; + gBattlerControllerFuncs[gActiveBattler] = nullsub_13; + } +} + +static void PlayerHandleStatusIconUpdate(void) +{ + if (!mplay_80342A4(gActiveBattler)) + { + u8 battlerId; + + UpdateHealthboxAttribute(gHealthboxSpriteIds[gActiveBattler], &gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], HEALTHBOX_STATUS_ICON); + battlerId = gActiveBattler; + gBattleSpritesDataPtr->healthBoxesData[battlerId].statusAnimActive = 0; + gBattlerControllerFuncs[gActiveBattler] = CompleteOnFinishedStatusAnimation; + } +} + +static void PlayerHandleStatusAnimation(void) +{ + if (!mplay_80342A4(gActiveBattler)) + { + InitAndLaunchChosenStatusAnimation(gBattleBufferA[gActiveBattler][1], + gBattleBufferA[gActiveBattler][2] | (gBattleBufferA[gActiveBattler][3] << 8) | (gBattleBufferA[gActiveBattler][4] << 16) | (gBattleBufferA[gActiveBattler][5] << 24)); + gBattlerControllerFuncs[gActiveBattler] = CompleteOnFinishedStatusAnimation; + } +} + +static void PlayerHandleStatusXor(void) +{ + u8 val = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_STATUS) ^ gBattleBufferA[gActiveBattler][1]; + + SetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_STATUS, &val); + PlayerBufferExecCompleted(); +} + +static void PlayerHandleDataTransfer(void) +{ + PlayerBufferExecCompleted(); +} + +static void PlayerHandleDMA3Transfer(void) +{ + u32 dstArg = gBattleBufferA[gActiveBattler][1] + | (gBattleBufferA[gActiveBattler][2] << 8) + | (gBattleBufferA[gActiveBattler][3] << 16) + | (gBattleBufferA[gActiveBattler][4] << 24); + u16 sizeArg = gBattleBufferA[gActiveBattler][5] | (gBattleBufferA[gActiveBattler][6] << 8); + const u8 *src = &gBattleBufferA[gActiveBattler][7]; + u8 *dst = (u8 *)(dstArg); + u32 size = sizeArg; + + while (TRUE) + { + if (size <= 0x1000) + { + DmaCopy16(3, src, dst, size); + break; + } + DmaCopy16(3, src, dst, 0x1000); + src += 0x1000; + dst += 0x1000; + size -= 0x1000; + } + PlayerBufferExecCompleted(); +} + +static void PlayerHandlePlayBGM(void) +{ + PlayBGM(gBattleBufferA[gActiveBattler][1] | (gBattleBufferA[gActiveBattler][2] << 8)); + PlayerBufferExecCompleted(); +} + +static void PlayerHandleCmd32(void) +{ + PlayerBufferExecCompleted(); +} + +static void PlayerHandleTwoReturnValues(void) +{ + BtlController_EmitTwoReturnValues(1, 0, 0); + PlayerBufferExecCompleted(); +} + +static void PlayerHandleChosenMonReturnValue(void) +{ + BtlController_EmitChosenMonReturnValue(1, 0, NULL); + PlayerBufferExecCompleted(); +} + +static void PlayerHandleOneReturnValue(void) +{ + BtlController_EmitOneReturnValue(1, 0); + PlayerBufferExecCompleted(); +} + +static void PlayerHandleOneReturnValue_Duplicate(void) +{ + BtlController_EmitOneReturnValue_Duplicate(1, 0); + PlayerBufferExecCompleted(); +} + +static void PlayerHandleCmd37(void) +{ + gUnknown_2022870.field_0 = 0; + PlayerBufferExecCompleted(); +} + +static void PlayerHandleCmd38(void) +{ + gUnknown_2022870.field_0 = gBattleBufferA[gActiveBattler][1]; + PlayerBufferExecCompleted(); +} + +static void PlayerHandleCmd39(void) +{ + gUnknown_2022870.flag_x80 = 0; + PlayerBufferExecCompleted(); +} + +static void PlayerHandleCmd40(void) +{ + gUnknown_2022870.flag_x80 ^= 1; + PlayerBufferExecCompleted(); +} + +static void PlayerHandleHitAnimation(void) +{ + if (gSprites[gBattlerSpriteIds[gActiveBattler]].invisible == TRUE) + { + PlayerBufferExecCompleted(); + } + else + { + gDoingBattleAnim = TRUE; + gSprites[gBattlerSpriteIds[gActiveBattler]].data[1] = 0; + DoHitAnimHealthboxEffect(gActiveBattler); + gBattlerControllerFuncs[gActiveBattler] = DoHitAnimBlinkSpriteEffect; + } +} + +static void PlayerHandleCmd42(void) +{ + PlayerBufferExecCompleted(); +} + +static void PlayerHandlePlaySE(void) +{ + s8 pan; + + if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER) + pan = SOUND_PAN_ATTACKER; + else + pan = SOUND_PAN_TARGET; + PlaySE12WithPanning(gBattleBufferA[gActiveBattler][1] | (gBattleBufferA[gActiveBattler][2] << 8), pan); + PlayerBufferExecCompleted(); +} + +static void PlayerHandlePlayFanfare(void) +{ + PlayFanfare(gBattleBufferA[gActiveBattler][1] | (gBattleBufferA[gActiveBattler][2] << 8)); + PlayerBufferExecCompleted(); +} + +static void PlayerHandleFaintingCry(void) +{ + PlayCry3(GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_SPECIES), -25, 5); + PlayerBufferExecCompleted(); +} + +static void PlayerHandleIntroSlide(void) +{ + HandleIntroSlide(gBattleBufferA[gActiveBattler][1]); + gIntroSlideFlags |= 1; + PlayerBufferExecCompleted(); +} + +static void PlayerHandleIntroTrainerBallThrow(void) +{ + u8 paletteNum; + u8 taskId; + + SetSpritePrimaryCoordsFromSecondaryCoords(&gSprites[gBattlerSpriteIds[gActiveBattler]]); + gSprites[gBattlerSpriteIds[gActiveBattler]].data[0] = 50; + gSprites[gBattlerSpriteIds[gActiveBattler]].data[2] = -40; + gSprites[gBattlerSpriteIds[gActiveBattler]].data[4] = gSprites[gBattlerSpriteIds[gActiveBattler]].pos1.y; + gSprites[gBattlerSpriteIds[gActiveBattler]].callback = sub_80755B8; + gSprites[gBattlerSpriteIds[gActiveBattler]].data[5] = gActiveBattler; + StoreSpriteCallbackInData6(&gSprites[gBattlerSpriteIds[gActiveBattler]], sub_80335F8); + StartSpriteAnim(&gSprites[gBattlerSpriteIds[gActiveBattler]], 1); + paletteNum = AllocSpritePalette(0xD6F8); + LoadCompressedPalette(gTrainerBackPicPaletteTable[gSaveBlock2Ptr->playerGender].data, 0x100 + paletteNum * 16, 32); + gSprites[gBattlerSpriteIds[gActiveBattler]].oam.paletteNum = paletteNum; + taskId = CreateTask(task05_08033660, 5); + gTasks[taskId].data[0] = gActiveBattler; + if (gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].partyStatusSummaryShown) + gTasks[gBattlerStatusSummaryTaskId[gActiveBattler]].func = Task_HidePartyStatusSummary; + gBattleSpritesDataPtr->animationData->field_9_x1 = 1; + gBattlerControllerFuncs[gActiveBattler] = nullsub_13; +} + +void sub_80335F8(struct Sprite *sprite) +{ + u8 battlerId = sprite->data[5]; + + FreeSpriteOamMatrix(sprite); + FreeSpritePaletteByTag(GetSpritePaletteTagByPaletteNum(sprite->oam.paletteNum)); + DestroySprite(sprite); + BattleLoadPlayerMonSpriteGfx(&gPlayerParty[gBattlerPartyIndexes[battlerId]], battlerId); + StartSpriteAnim(&gSprites[gBattlerSpriteIds[battlerId]], 0); +} + +static void task05_08033660(u8 taskId) +{ + if (gTasks[taskId].data[1] < 31) + { + ++gTasks[taskId].data[1]; + } + else + { + u8 savedActiveBattler = gActiveBattler; + + gActiveBattler = gTasks[taskId].data[0]; + if (!IsDoubleBattle() || (gBattleTypeFlags & BATTLE_TYPE_MULTI)) + { + gBattleBufferA[gActiveBattler][1] = gBattlerPartyIndexes[gActiveBattler]; + sub_8031FF4(gActiveBattler, FALSE); + } + else + { + gBattleBufferA[gActiveBattler][1] = gBattlerPartyIndexes[gActiveBattler]; + sub_8031FF4(gActiveBattler, FALSE); + gActiveBattler ^= BIT_FLANK; + gBattleBufferA[gActiveBattler][1] = gBattlerPartyIndexes[gActiveBattler]; + BattleLoadPlayerMonSpriteGfx(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], gActiveBattler); + sub_8031FF4(gActiveBattler, FALSE); + gActiveBattler ^= BIT_FLANK; + } + gBattlerControllerFuncs[gActiveBattler] = sub_802FA58; + gActiveBattler = savedActiveBattler; + DestroyTask(taskId); + } +} + +static void PlayerHandleDrawPartyStatusSummary(void) +{ + if (gBattleBufferA[gActiveBattler][1] && GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER) + { + PlayerBufferExecCompleted(); + } + else + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].partyStatusSummaryShown = 1; + gBattlerStatusSummaryTaskId[gActiveBattler] = CreatePartyStatusSummarySprites(gActiveBattler, (struct HpAndStatus *)&gBattleBufferA[gActiveBattler][4], gBattleBufferA[gActiveBattler][1], gBattleBufferA[gActiveBattler][2]); + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_5 = 0; + if (gBattleBufferA[gActiveBattler][2] != 0) + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_5 = 0x5D; + gBattlerControllerFuncs[gActiveBattler] = sub_8033830; + } +} + +static void sub_8033830(void) +{ + if (gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_5++ > 0x5C) + { + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_5 = 0; + PlayerBufferExecCompleted(); + } +} + +static void PlayerHandleHidePartyStatusSummary(void) +{ + if (gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].partyStatusSummaryShown) + gTasks[gBattlerStatusSummaryTaskId[gActiveBattler]].func = Task_HidePartyStatusSummary; + PlayerBufferExecCompleted(); +} + +static void PlayerHandleEndBounceEffect(void) +{ + EndBounceEffect(gActiveBattler, BOUNCE_HEALTHBOX); + EndBounceEffect(gActiveBattler, BOUNCE_MON); + PlayerBufferExecCompleted(); +} + +static void PlayerHandleSpriteInvisibility(void) +{ + if (IsBattlerSpritePresent(gActiveBattler)) + { + gSprites[gBattlerSpriteIds[gActiveBattler]].invisible = gBattleBufferA[gActiveBattler][1]; + CopyBattleSpriteInvisibility(gActiveBattler); + } + PlayerBufferExecCompleted(); +} + +static void PlayerHandleBattleAnimation(void) +{ + if (!mplay_80342A4(gActiveBattler)) + { + u8 animationId = gBattleBufferA[gActiveBattler][1]; + u16 argument = gBattleBufferA[gActiveBattler][2] | (gBattleBufferA[gActiveBattler][3] << 8); + + if (TryHandleLaunchBattleTableAnimation(gActiveBattler, gActiveBattler, gActiveBattler, animationId, argument)) + PlayerBufferExecCompleted(); + else + gBattlerControllerFuncs[gActiveBattler] = CompleteOnFinishedBattleAnimation; + } +} + +static void PlayerHandleLinkStandbyMsg(void) +{ + switch (gBattleBufferA[gActiveBattler][1]) + { + case 0: + PrintLinkStandbyMsg(); + // fall through + case 1: + EndBounceEffect(gActiveBattler, BOUNCE_HEALTHBOX); + EndBounceEffect(gActiveBattler, BOUNCE_MON); + break; + case 2: + PrintLinkStandbyMsg(); + break; + } + PlayerBufferExecCompleted(); +} + +static void PlayerHandleResetActionMoveSelection(void) +{ + switch (gBattleBufferA[gActiveBattler][1]) + { + case RESET_ACTION_MOVE_SELECTION: + gActionSelectionCursor[gActiveBattler] = 0; + gMoveSelectionCursor[gActiveBattler] = 0; + break; + case RESET_ACTION_SELECTION: + gActionSelectionCursor[gActiveBattler] = 0; + break; + case RESET_MOVE_SELECTION: + gMoveSelectionCursor[gActiveBattler] = 0; + break; + } + PlayerBufferExecCompleted(); +} + +static void PlayerHandleCmd55(void) +{ + gBattleOutcome = gBattleBufferA[gActiveBattler][1]; + FadeOutMapMusic(5); + BeginFastPaletteFade(3); + PlayerBufferExecCompleted(); + gBattlerControllerFuncs[gActiveBattler] = sub_802F6A8; +} + +static void PlayerCmdEnd(void) +{ +} + +static void sub_8033AC8(void) +{ + u32 bitMask = 0; + u8 startY = 0; + + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + { + u8 moveTarget; + struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleBufferA[gActiveBattler][4]); + u16 move = moveInfo->moves[gMoveSelectionCursor[gActiveBattler]]; + + if (move == MOVE_CURSE) + { + if (moveInfo->monType1 != TYPE_GHOST && moveInfo->monType2 != TYPE_GHOST) + moveTarget = MOVE_TARGET_USER; + else + moveTarget = MOVE_TARGET_SELECTED; + } + else + { + moveTarget = gBattleMoves[moveInfo->moves[gMoveSelectionCursor[gActiveBattler]]].target; + } + switch (moveTarget) + { + case MOVE_TARGET_SELECTED: + case MOVE_TARGET_DEPENDS: + case MOVE_TARGET_USER_OR_SELECTED: + case MOVE_TARGET_RANDOM: + bitMask = 0xF0000; + startY = 0; + break; + case MOVE_TARGET_BOTH: + case MOVE_TARGET_OPPONENTS_FIELD: + bitMask = (gBitTable[GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT)] + | gBitTable[GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT)]) << 16; + startY = 8; + break; + case MOVE_TARGET_USER: + switch (move) + { + case MOVE_HAZE: + case MOVE_SANDSTORM: + case MOVE_PERISH_SONG: + case MOVE_RAIN_DANCE: + case MOVE_SUNNY_DAY: + case MOVE_HAIL: + case MOVE_MUD_SPORT: + case MOVE_WATER_SPORT: + bitMask = 0xF0000; + break; + case MOVE_SAFEGUARD: + case MOVE_REFLECT: + case MOVE_LIGHT_SCREEN: + case MOVE_MIST: + case MOVE_HEAL_BELL: + case MOVE_AROMATHERAPY: + bitMask = (gBitTable[GetBattlerAtPosition(B_POSITION_PLAYER_LEFT)] + | gBitTable[GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT)]) << 16; + break; + case MOVE_HELPING_HAND: + bitMask = (gBitTable[GetBattlerAtPosition(GetBattlerPosition(gActiveBattler) ^ BIT_FLANK)]) << 16; + break; + default: + bitMask = (gBitTable[gActiveBattler]) << 16; + break; + } + startY = 8; + break; + case MOVE_TARGET_FOES_AND_ALLY: + bitMask = (gBitTable[GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT)] + | gBitTable[GetBattlerAtPosition(GetBattlerPosition(gActiveBattler) ^ BIT_FLANK)] + | gBitTable[GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT)]) << 16; + startY = 8; + break; + } + BeginNormalPaletteFade(bitMask, 8, startY, 0, RGB_WHITE); + } +} diff --git a/src/battle_controller_safari.c b/src/battle_controller_safari.c new file mode 100644 index 000000000..4b5153c7c --- /dev/null +++ b/src/battle_controller_safari.c @@ -0,0 +1,677 @@ +#include "global.h" +#include "battle.h" +#include "battle_anim.h" +#include "battle_controllers.h" +#include "battle_interface.h" +#include "battle_message.h" +#include "bg.h" +#include "data.h" +#include "item_menu.h" +#include "link.h" +#include "main.h" +#include "m4a.h" +#include "palette.h" +#include "pokeball.h" +#include "pokemon.h" +#include "reshow_battle_screen.h" +#include "sound.h" +#include "task.h" +#include "text.h" +#include "util.h" +#include "window.h" +#include "strings.h" +#include "constants/songs.h" +#include "constants/battle_anim.h" + +static void SafariHandleGetMonData(void); +static void SafariHandleGetRawMonData(void); +static void SafariHandleSetMonData(void); +static void SafariHandleSetRawMonData(void); +static void SafariHandleLoadMonSprite(void); +static void SafariHandleSwitchInAnim(void); +static void SafariHandleReturnMonToBall(void); +static void SafariHandleDrawTrainerPic(void); +static void SafariHandleTrainerSlide(void); +static void SafariHandleTrainerSlideBack(void); +static void SafariHandleFaintAnimation(void); +static void SafariHandlePaletteFade(void); +static void SafariHandleSuccessBallThrowAnim(void); +static void SafariHandleBallThrowAnim(void); +static void SafariHandlePause(void); +static void SafariHandleMoveAnimation(void); +static void SafariHandlePrintString(void); +static void SafariHandlePrintSelectionString(void); +static void SafariHandleChooseAction(void); +static void SafariHandleUnknownYesNoBox(void); +static void SafariHandleChooseMove(void); +static void SafariHandleChooseItem(void); +static void SafariHandleChoosePokemon(void); +static void SafariHandleCmd23(void); +static void SafariHandleHealthBarUpdate(void); +static void SafariHandleExpUpdate(void); +static void SafariHandleStatusIconUpdate(void); +static void SafariHandleStatusAnimation(void); +static void SafariHandleStatusXor(void); +static void SafariHandleDataTransfer(void); +static void SafariHandleDMA3Transfer(void); +static void SafariHandlePlayBGM(void); +static void SafariHandleCmd32(void); +static void SafariHandleTwoReturnValues(void); +static void SafariHandleChosenMonReturnValue(void); +static void SafariHandleOneReturnValue(void); +static void SafariHandleOneReturnValue_Duplicate(void); +static void SafariHandleCmd37(void); +static void SafariHandleCmd38(void); +static void SafariHandleCmd39(void); +static void SafariHandleCmd40(void); +static void SafariHandleHitAnimation(void); +static void SafariHandleCmd42(void); +static void SafariHandlePlaySE(void); +static void SafariHandlePlayFanfareOrBGM(void); +static void SafariHandleFaintingCry(void); +static void SafariHandleIntroSlide(void); +static void SafariHandleIntroTrainerBallThrow(void); +static void SafariHandleDrawPartyStatusSummary(void); +static void SafariHandleHidePartyStatusSummary(void); +static void SafariHandleEndBounceEffect(void); +static void SafariHandleSpriteInvisibility(void); +static void SafariHandleBattleAnimation(void); +static void SafariHandleLinkStandbyMsg(void); +static void SafariHandleResetActionMoveSelection(void); +static void SafariHandleCmd55(void); +static void SafariCmdEnd(void); + +static void SafariBufferRunCommand(void); +static void SafariBufferExecCompleted(void); +static void CompleteWhenChosePokeblock(void); + +static void (*const sSafariBufferCommands[CONTROLLER_CMDS_COUNT])(void) = +{ + SafariHandleGetMonData, + SafariHandleGetRawMonData, + SafariHandleSetMonData, + SafariHandleSetRawMonData, + SafariHandleLoadMonSprite, + SafariHandleSwitchInAnim, + SafariHandleReturnMonToBall, + SafariHandleDrawTrainerPic, + SafariHandleTrainerSlide, + SafariHandleTrainerSlideBack, + SafariHandleFaintAnimation, + SafariHandlePaletteFade, + SafariHandleSuccessBallThrowAnim, + SafariHandleBallThrowAnim, + SafariHandlePause, + SafariHandleMoveAnimation, + SafariHandlePrintString, + SafariHandlePrintSelectionString, + SafariHandleChooseAction, + SafariHandleUnknownYesNoBox, + SafariHandleChooseMove, + SafariHandleChooseItem, + SafariHandleChoosePokemon, + SafariHandleCmd23, + SafariHandleHealthBarUpdate, + SafariHandleExpUpdate, + SafariHandleStatusIconUpdate, + SafariHandleStatusAnimation, + SafariHandleStatusXor, + SafariHandleDataTransfer, + SafariHandleDMA3Transfer, + SafariHandlePlayBGM, + SafariHandleCmd32, + SafariHandleTwoReturnValues, + SafariHandleChosenMonReturnValue, + SafariHandleOneReturnValue, + SafariHandleOneReturnValue_Duplicate, + SafariHandleCmd37, + SafariHandleCmd38, + SafariHandleCmd39, + SafariHandleCmd40, + SafariHandleHitAnimation, + SafariHandleCmd42, + SafariHandlePlaySE, + SafariHandlePlayFanfareOrBGM, + SafariHandleFaintingCry, + SafariHandleIntroSlide, + SafariHandleIntroTrainerBallThrow, + SafariHandleDrawPartyStatusSummary, + SafariHandleHidePartyStatusSummary, + SafariHandleEndBounceEffect, + SafariHandleSpriteInvisibility, + SafariHandleBattleAnimation, + SafariHandleLinkStandbyMsg, + SafariHandleResetActionMoveSelection, + SafariHandleCmd55, + SafariCmdEnd, +}; + +// not used +static void SpriteCB_Null4(struct Sprite *sprite) +{ +} + +void SetControllerToSafari(void) +{ + gBattlerControllerFuncs[gActiveBattler] = SafariBufferRunCommand; +} + +static void SafariBufferRunCommand(void) +{ + if (gBattleControllerExecFlags & gBitTable[gActiveBattler]) + { + if (gBattleBufferA[gActiveBattler][0] < NELEMS(sSafariBufferCommands)) + sSafariBufferCommands[gBattleBufferA[gActiveBattler][0]](); + else + SafariBufferExecCompleted(); + } +} + +static void HandleInputChooseAction(void) +{ + if (JOY_NEW(A_BUTTON)) + { + PlaySE(SE_SELECT); + + switch (gActionSelectionCursor[gActiveBattler]) + { + case 0: + BtlController_EmitTwoReturnValues(1, B_ACTION_SAFARI_BALL, 0); + break; + case 1: + BtlController_EmitTwoReturnValues(1, B_ACTION_SAFARI_POKEBLOCK, 0); + break; + case 2: + BtlController_EmitTwoReturnValues(1, B_ACTION_SAFARI_GO_NEAR, 0); + break; + case 3: + BtlController_EmitTwoReturnValues(1, B_ACTION_SAFARI_RUN, 0); + break; + } + SafariBufferExecCompleted(); + } + else if (JOY_NEW(DPAD_LEFT)) + { + if (gActionSelectionCursor[gActiveBattler] & 1) + { + PlaySE(SE_SELECT); + ActionSelectionDestroyCursorAt(gActionSelectionCursor[gActiveBattler]); + gActionSelectionCursor[gActiveBattler] ^= 1; + ActionSelectionCreateCursorAt(gActionSelectionCursor[gActiveBattler], 0); + } + } + else if (JOY_NEW(DPAD_RIGHT)) + { + if (!(gActionSelectionCursor[gActiveBattler] & 1)) + { + PlaySE(SE_SELECT); + ActionSelectionDestroyCursorAt(gActionSelectionCursor[gActiveBattler]); + gActionSelectionCursor[gActiveBattler] ^= 1; + ActionSelectionCreateCursorAt(gActionSelectionCursor[gActiveBattler], 0); + } + } + else if (JOY_NEW(DPAD_UP)) + { + if (gActionSelectionCursor[gActiveBattler] & 2) + { + PlaySE(SE_SELECT); + ActionSelectionDestroyCursorAt(gActionSelectionCursor[gActiveBattler]); + gActionSelectionCursor[gActiveBattler] ^= 2; + ActionSelectionCreateCursorAt(gActionSelectionCursor[gActiveBattler], 0); + } + } + else if (JOY_NEW(DPAD_DOWN)) + { + if (!(gActionSelectionCursor[gActiveBattler] & 2)) + { + PlaySE(SE_SELECT); + ActionSelectionDestroyCursorAt(gActionSelectionCursor[gActiveBattler]); + gActionSelectionCursor[gActiveBattler] ^= 2; + ActionSelectionCreateCursorAt(gActionSelectionCursor[gActiveBattler], 0); + } + } +} + +static void CompleteOnBattlerSpriteCallbackDummy(void) +{ + if (gSprites[gBattlerSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy) + SafariBufferExecCompleted(); +} + +static void CompleteOnInactiveTextPrinter(void) +{ + if (!IsTextPrinterActive(0)) + SafariBufferExecCompleted(); +} + +static void CompleteOnHealthboxSpriteCallbackDummy(void) +{ + if (gSprites[gHealthboxSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy) + SafariBufferExecCompleted(); +} + +static void sub_80DD7B0(void) +{ + if (!gPaletteFade.active) + { + gMain.inBattle = FALSE; + gMain.callback1 = gPreBattleCallback1; + SetMainCallback2(gMain.savedCallback); + } +} + +static void CompleteOnSpecialAnimDone(void) +{ + if (!gDoingBattleAnim || !gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].specialAnimActive) + SafariBufferExecCompleted(); +} + +static void SafariOpenPokeblockCase(void) +{ + if (!gPaletteFade.active) + gBattlerControllerFuncs[gActiveBattler] = CompleteWhenChosePokeblock; +} + +static void CompleteWhenChosePokeblock(void) +{ + if (gMain.callback2 == BattleMainCB2 && !gPaletteFade.active) + { + BtlController_EmitOneReturnValue(1, gSpecialVar_ItemId); + SafariBufferExecCompleted(); + } +} + +static void CompleteOnFinishedBattleAnimation(void) +{ + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animFromTableActive) + SafariBufferExecCompleted(); +} + +static void SafariBufferExecCompleted(void) +{ + gBattlerControllerFuncs[gActiveBattler] = SafariBufferRunCommand; + if (gBattleTypeFlags & BATTLE_TYPE_LINK) + { + u8 playerId = GetMultiplayerId(); + + PrepareBufferDataTransferLink(2, 4, &playerId); + gBattleBufferA[gActiveBattler][0] = CONTROLLER_TERMINATOR_NOP; + } + else + { + gBattleControllerExecFlags &= ~gBitTable[gActiveBattler]; + } +} + +// not used +static void CompleteOnFinishedStatusAnimation(void) +{ + if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].statusAnimActive) + SafariBufferExecCompleted(); +} + +static void SafariHandleGetMonData(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandleGetRawMonData(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandleSetMonData(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandleSetRawMonData(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandleLoadMonSprite(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandleSwitchInAnim(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandleReturnMonToBall(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandleDrawTrainerPic(void) +{ + DecompressTrainerBackPalette(gSaveBlock2Ptr->playerGender, gActiveBattler); + SetMultiuseSpriteTemplateToTrainerBack(gSaveBlock2Ptr->playerGender, GetBattlerPosition(gActiveBattler)); + gBattlerSpriteIds[gActiveBattler] = CreateSprite(&gMultiuseSpriteTemplate, + 80, + (8 - gTrainerBackPicCoords[gSaveBlock2Ptr->playerGender].size) * 4 + 80, + 30); + gSprites[gBattlerSpriteIds[gActiveBattler]].oam.paletteNum = gActiveBattler; + gSprites[gBattlerSpriteIds[gActiveBattler]].pos2.x = 240; + gSprites[gBattlerSpriteIds[gActiveBattler]].data[0] = -2; + gSprites[gBattlerSpriteIds[gActiveBattler]].callback = sub_8033EEC; + gBattlerControllerFuncs[gActiveBattler] = CompleteOnBattlerSpriteCallbackDummy; +} + +static void SafariHandleTrainerSlide(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandleTrainerSlideBack(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandleFaintAnimation(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandlePaletteFade(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandleSuccessBallThrowAnim(void) +{ + gBattleSpritesDataPtr->animationData->ballThrowCaseId = BALL_3_SHAKES_SUCCESS; + gDoingBattleAnim = TRUE; + InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT), B_ANIM_SAFARI_BALL_THROW); + gBattlerControllerFuncs[gActiveBattler] = CompleteOnSpecialAnimDone; +} + +static void SafariHandleBallThrowAnim(void) +{ + u8 ballThrowCaseId = gBattleBufferA[gActiveBattler][1]; + + gBattleSpritesDataPtr->animationData->ballThrowCaseId = ballThrowCaseId; + gDoingBattleAnim = TRUE; + InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT), B_ANIM_SAFARI_BALL_THROW); + gBattlerControllerFuncs[gActiveBattler] = CompleteOnSpecialAnimDone; +} + +static void SafariHandlePause(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandleMoveAnimation(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandlePrintString(void) +{ + u16 *stringId; + + gBattle_BG0_X = 0; + gBattle_BG0_Y = 0; + stringId = (u16 *)(&gBattleBufferA[gActiveBattler][2]); + BufferStringBattle(*stringId); + if (sub_80D89B0(*stringId)) + BattlePutTextOnWindow(gDisplayedStringBattle, 0x40); + else + BattlePutTextOnWindow(gDisplayedStringBattle, 0); + gBattlerControllerFuncs[gActiveBattler] = CompleteOnInactiveTextPrinter; +} + +static void SafariHandlePrintSelectionString(void) +{ + if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER) + SafariHandlePrintString(); + else + SafariBufferExecCompleted(); +} + +static void HandleChooseActionAfterDma3(void) +{ + if (!IsDma3ManagerBusyWithBgCopy()) + { + gBattle_BG0_X = 0; + gBattle_BG0_Y = 160; + gBattlerControllerFuncs[gActiveBattler] = HandleInputChooseAction; + } +} + +static void SafariHandleChooseAction(void) +{ + s32 i; + + gBattlerControllerFuncs[gActiveBattler] = HandleChooseActionAfterDma3; + BattlePutTextOnWindow(gUnknown_83FDA4C, 0); + BattlePutTextOnWindow(gUnknown_83FE747, 2); + for (i = 0; i < 4; ++i) + ActionSelectionDestroyCursorAt(i); + ActionSelectionCreateCursorAt(gActionSelectionCursor[gActiveBattler], 0); + BattleStringExpandPlaceholdersToDisplayedString(gUnknown_83FE6E6); + BattlePutTextOnWindow(gDisplayedStringBattle, 1); +} + +static void SafariHandleUnknownYesNoBox(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandleChooseMove(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandleChooseItem(void) +{ + s32 i; + + BeginNormalPaletteFade(0xFFFFFFFF, 0, 0, 0x10, RGB_BLACK); + gBattlerControllerFuncs[gActiveBattler] = SafariOpenPokeblockCase; + gBattlerInMenuId = gActiveBattler; +} + +static void SafariHandleChoosePokemon(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandleCmd23(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandleHealthBarUpdate(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandleExpUpdate(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandleStatusIconUpdate(void) +{ + UpdateHealthboxAttribute(gHealthboxSpriteIds[gActiveBattler], &gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], HEALTHBOX_SAFARI_BALLS_TEXT); + SafariBufferExecCompleted(); +} + +static void SafariHandleStatusAnimation(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandleStatusXor(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandleDataTransfer(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandleDMA3Transfer(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandlePlayBGM(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandleCmd32(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandleTwoReturnValues(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandleChosenMonReturnValue(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandleOneReturnValue(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandleOneReturnValue_Duplicate(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandleCmd37(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandleCmd38(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandleCmd39(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandleCmd40(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandleHitAnimation(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandleCmd42(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandlePlaySE(void) +{ + s8 pan; + + if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER) + pan = SOUND_PAN_ATTACKER; + else + pan = SOUND_PAN_TARGET; + PlaySE12WithPanning(gBattleBufferA[gActiveBattler][1] | (gBattleBufferA[gActiveBattler][2] << 8), pan); + SafariBufferExecCompleted(); +} + +static void SafariHandlePlayFanfareOrBGM(void) +{ + PlayFanfare(gBattleBufferA[gActiveBattler][1] | (gBattleBufferA[gActiveBattler][2] << 8)); + SafariBufferExecCompleted(); +} + +static void SafariHandleFaintingCry(void) +{ + u16 species = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_SPECIES); + + PlayCry1(species, 25); + SafariBufferExecCompleted(); +} + +static void SafariHandleIntroSlide(void) +{ + HandleIntroSlide(gBattleBufferA[gActiveBattler][1]); + gIntroSlideFlags |= 1; + SafariBufferExecCompleted(); +} + +static void SafariHandleIntroTrainerBallThrow(void) +{ + UpdateHealthboxAttribute(gHealthboxSpriteIds[gActiveBattler], &gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], HEALTHBOX_SAFARI_ALL_TEXT); + sub_804BD94(gActiveBattler); + SetHealthboxSpriteVisible(gHealthboxSpriteIds[gActiveBattler]); + gBattlerControllerFuncs[gActiveBattler] = CompleteOnHealthboxSpriteCallbackDummy; +} + +static void SafariHandleDrawPartyStatusSummary(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandleHidePartyStatusSummary(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandleEndBounceEffect(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandleSpriteInvisibility(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandleBattleAnimation(void) +{ + u8 animationId = gBattleBufferA[gActiveBattler][1]; + u16 argument = gBattleBufferA[gActiveBattler][2] | (gBattleBufferA[gActiveBattler][3] << 8); + + if (TryHandleLaunchBattleTableAnimation(gActiveBattler, gActiveBattler, gActiveBattler, animationId, argument)) + SafariBufferExecCompleted(); + else + gBattlerControllerFuncs[gActiveBattler] = CompleteOnFinishedBattleAnimation; +} + +static void SafariHandleLinkStandbyMsg(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandleResetActionMoveSelection(void) +{ + SafariBufferExecCompleted(); +} + +static void SafariHandleCmd55(void) +{ + gBattleOutcome = gBattleBufferA[gActiveBattler][1]; + FadeOutMapMusic(5); + BeginFastPaletteFade(3); + SafariBufferExecCompleted(); + if ((gBattleTypeFlags & BATTLE_TYPE_LINK) && !(gBattleTypeFlags & BATTLE_TYPE_IS_MASTER)) + gBattlerControllerFuncs[gActiveBattler] = sub_80DD7B0; +} + +static void SafariCmdEnd(void) +{ +} diff --git a/src/battle_controllers.c b/src/battle_controllers.c new file mode 100644 index 000000000..544af1006 --- /dev/null +++ b/src/battle_controllers.c @@ -0,0 +1,1170 @@ +#include "global.h" +#include "battle.h" +#include "battle_main.h" +#include "battle_ai_script_commands.h" +#include "battle_anim.h" +#include "battle_util.h" +#include "battle_controllers.h" +#include "battle_message.h" +#include "link.h" +#include "link_rfu.h" +#include "cable_club.h" +#include "party_menu.h" +#include "pokemon.h" +#include "task.h" +#include "util.h" +#include "constants/abilities.h" +#include "constants/species.h" +#include "constants/battle.h" + +static EWRAM_DATA u8 sLinkSendTaskId = 0; +static EWRAM_DATA u8 sLinkReceiveTaskId = 0; +static EWRAM_DATA u8 gUnknown_202286E = 0; +EWRAM_DATA struct UnusedControllerStruct gUnknown_2022870 = {0}; +static EWRAM_DATA u8 sBattleBuffersTransferData[0x100] = {0}; + +static void CreateTasksForSendRecvLinkBuffers(void); +static void InitLinkBtlControllers(void); +static void InitSinglePlayerBtlControllers(void); +static void SetBattlePartyIds(void); +static void Task_HandleSendLinkBuffersData(u8 taskId); +static void Task_HandleCopyReceivedLinkBuffersData(u8 taskId); + +void HandleLinkBattleSetup(void) +{ + if (gBattleTypeFlags & BATTLE_TYPE_LINK) + { + if (gWirelessCommType) + sub_800B1F4(); + if (!gReceivedRemoteLinkPlayers) + OpenLink(); + CreateTask(sub_8081A90, 0); + CreateTasksForSendRecvLinkBuffers(); + } +} + +void SetUpBattleVars(void) +{ + s32 i; + + gBattleMainFunc = nullsub_12; + for (i = 0; i < MAX_BATTLERS_COUNT; ++i) + { + gBattlerControllerFuncs[i] = nullsub_13; + gBattlerPositions[i] = 0xFF; + gActionSelectionCursor[i] = 0; + gMoveSelectionCursor[i] = 0; + } + HandleLinkBattleSetup(); + gBattleControllerExecFlags = 0; + ClearBattleAnimationVars(); + ClearBattleMonForms(); + BattleAI_HandleItemUseBeforeAISetup(); + gUnknown_2022B54 = 0; + gUnknown_2023DDC = 0; +} + +void sub_800D30C(void) +{ + s32 i; + + if (gBattleTypeFlags & BATTLE_TYPE_LINK) + InitLinkBtlControllers(); + else + InitSinglePlayerBtlControllers(); + SetBattlePartyIds(); + if (!(gBattleTypeFlags & BATTLE_TYPE_MULTI)) + for (i = 0; i < gBattlersCount; ++i) + sub_8127DA8(i, 0); +} + +static void InitSinglePlayerBtlControllers(void) +{ + if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE)) + { + gBattleMainFunc = BeginBattleIntro; + if (gBattleTypeFlags & BATTLE_TYPE_POKEDUDE) + { + gBattlerControllerFuncs[0] = SetControllerToPokedude; + gBattlerPositions[0] = B_POSITION_PLAYER_LEFT; + gBattlerControllerFuncs[1] = SetControllerToPokedude; + gBattlerPositions[1] = B_POSITION_OPPONENT_LEFT; + gBattlersCount = 2; + } + else + { + if (gBattleTypeFlags & BATTLE_TYPE_SAFARI) + gBattlerControllerFuncs[0] = SetControllerToSafari; + else if (gBattleTypeFlags & (BATTLE_TYPE_OLDMAN_TUTORIAL | BATTLE_TYPE_FIRST_BATTLE)) + gBattlerControllerFuncs[0] = SetControllerToOakOrOldman; + else + gBattlerControllerFuncs[0] = SetControllerToPlayer; + gBattlerPositions[0] = B_POSITION_PLAYER_LEFT; + gBattlerControllerFuncs[1] = SetControllerToOpponent; + gBattlerPositions[1] = B_POSITION_OPPONENT_LEFT; + gBattlersCount = 2; + } + } + else + { + gBattleMainFunc = BeginBattleIntro; + if (gBattleTypeFlags & BATTLE_TYPE_POKEDUDE) + { + gBattlerControllerFuncs[0] = SetControllerToPokedude; + gBattlerPositions[0] = B_POSITION_PLAYER_LEFT; + gBattlerControllerFuncs[1] = SetControllerToPokedude; + gBattlerPositions[1] = B_POSITION_OPPONENT_LEFT; + gBattlerControllerFuncs[2] = SetControllerToPokedude; + gBattlerPositions[2] = B_POSITION_PLAYER_RIGHT; + gBattlerControllerFuncs[3] = SetControllerToPokedude; + gBattlerPositions[3] = B_POSITION_OPPONENT_RIGHT; + gBattlersCount = MAX_BATTLERS_COUNT; + } + else + { + gBattlerControllerFuncs[0] = SetControllerToPlayer; + gBattlerPositions[0] = B_POSITION_PLAYER_LEFT; + gBattlerControllerFuncs[1] = SetControllerToOpponent; + gBattlerPositions[1] = B_POSITION_OPPONENT_LEFT; + gBattlerControllerFuncs[2] = SetControllerToPlayer; + gBattlerPositions[2] = B_POSITION_PLAYER_RIGHT; + gBattlerControllerFuncs[3] = SetControllerToOpponent; + gBattlerPositions[3] = B_POSITION_OPPONENT_RIGHT; + gBattlersCount = MAX_BATTLERS_COUNT; + } + } +} + +static void InitLinkBtlControllers(void) +{ + s32 i; + u8 multiplayerId; + + if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE)) + { + if (gBattleTypeFlags & BATTLE_TYPE_IS_MASTER) + { + gBattleMainFunc = BeginBattleIntro; + gBattlerControllerFuncs[0] = SetControllerToPlayer; + gBattlerPositions[0] = B_POSITION_PLAYER_LEFT; + gBattlerControllerFuncs[1] = SetControllerToLinkOpponent; + gBattlerPositions[1] = B_POSITION_OPPONENT_LEFT; + gBattlersCount = 2; + } + else + { + gBattlerControllerFuncs[1] = SetControllerToPlayer; + gBattlerPositions[1] = B_POSITION_PLAYER_LEFT; + gBattlerControllerFuncs[0] = SetControllerToLinkOpponent; + gBattlerPositions[0] = B_POSITION_OPPONENT_LEFT; + gBattlersCount = 2; + } + } + else if (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)) + { + if (gBattleTypeFlags & BATTLE_TYPE_IS_MASTER) + { + gBattleMainFunc = BeginBattleIntro; + gBattlerControllerFuncs[0] = SetControllerToPlayer; + gBattlerPositions[0] = B_POSITION_PLAYER_LEFT; + gBattlerControllerFuncs[1] = SetControllerToLinkOpponent; + gBattlerPositions[1] = B_POSITION_OPPONENT_LEFT; + gBattlerControllerFuncs[2] = SetControllerToPlayer; + gBattlerPositions[2] = B_POSITION_PLAYER_RIGHT; + gBattlerControllerFuncs[3] = SetControllerToLinkOpponent; + gBattlerPositions[3] = B_POSITION_OPPONENT_RIGHT; + gBattlersCount = MAX_BATTLERS_COUNT; + } + else + { + gBattlerControllerFuncs[1] = SetControllerToPlayer; + gBattlerPositions[1] = B_POSITION_PLAYER_LEFT; + gBattlerControllerFuncs[0] = SetControllerToLinkOpponent; + gBattlerPositions[0] = B_POSITION_OPPONENT_LEFT; + gBattlerControllerFuncs[3] = SetControllerToPlayer; + gBattlerPositions[3] = B_POSITION_PLAYER_RIGHT; + gBattlerControllerFuncs[2] = SetControllerToLinkOpponent; + gBattlerPositions[2] = B_POSITION_OPPONENT_RIGHT; + gBattlersCount = MAX_BATTLERS_COUNT; + } + } + else + { + multiplayerId = GetMultiplayerId(); + if (gBattleTypeFlags & BATTLE_TYPE_IS_MASTER) + gBattleMainFunc = BeginBattleIntro; + for (i = 0; i < MAX_BATTLERS_COUNT; ++i) + { + switch (gLinkPlayers[i].id) + { + case 0: + case 3: + sub_8127DA8(gLinkPlayers[i].id, 0); + break; + case 1: + case 2: + sub_8127DA8(gLinkPlayers[i].id, 1); + break; + } + if (i == multiplayerId) + { + gBattlerControllerFuncs[gLinkPlayers[i].id] = SetControllerToPlayer; + switch (gLinkPlayers[i].id) + { + case 0: + case 3: + gBattlerPositions[gLinkPlayers[i].id] = B_POSITION_PLAYER_LEFT; + gBattlerPartyIndexes[gLinkPlayers[i].id] = B_POSITION_PLAYER_LEFT; + break; + case 1: + case 2: + gBattlerPositions[gLinkPlayers[i].id] = B_POSITION_PLAYER_RIGHT; + gBattlerPartyIndexes[gLinkPlayers[i].id] = 3; + break; + } + } + else + { + if ((!(gLinkPlayers[i].id & 1) && !(gLinkPlayers[multiplayerId].id & 1)) || ((gLinkPlayers[i].id & 1) && (gLinkPlayers[multiplayerId].id & 1))) + { + gBattlerControllerFuncs[gLinkPlayers[i].id] = SetControllerToLinkPartner; + switch (gLinkPlayers[i].id) + { + case 0: + case 3: + gBattlerPositions[gLinkPlayers[i].id] = B_POSITION_PLAYER_LEFT; + gBattlerPartyIndexes[gLinkPlayers[i].id] = B_POSITION_PLAYER_LEFT; + break; + case 1: + case 2: + gBattlerPositions[gLinkPlayers[i].id] = B_POSITION_PLAYER_RIGHT; + gBattlerPartyIndexes[gLinkPlayers[i].id] = B_POSITION_OPPONENT_RIGHT; + break; + } + } + else + { + gBattlerControllerFuncs[gLinkPlayers[i].id] = SetControllerToLinkOpponent; + switch (gLinkPlayers[i].id) + { + case 0: + case 3: + gBattlerPositions[gLinkPlayers[i].id] = B_POSITION_OPPONENT_LEFT; + gBattlerPartyIndexes[gLinkPlayers[i].id] = B_POSITION_PLAYER_LEFT; + break; + case 1: + case 2: + gBattlerPositions[gLinkPlayers[i].id] = B_POSITION_OPPONENT_RIGHT; + gBattlerPartyIndexes[gLinkPlayers[i].id] = B_POSITION_OPPONENT_RIGHT; + break; + } + } + } + } + gBattlersCount = MAX_BATTLERS_COUNT; + } +} + +static void SetBattlePartyIds(void) +{ + s32 i, j; + + if (!(gBattleTypeFlags & BATTLE_TYPE_MULTI)) + { + for (i = 0; i < gBattlersCount; ++i) + { + for (j = 0; j < PARTY_SIZE; ++j) + { + if (i < 2) + { + if (GET_BATTLER_SIDE2(i) == B_SIDE_PLAYER) + { + if (GetMonData(&gPlayerParty[j], MON_DATA_HP) != 0 + && GetMonData(&gPlayerParty[j], MON_DATA_SPECIES2) != SPECIES_NONE + && GetMonData(&gPlayerParty[j], MON_DATA_SPECIES2) != SPECIES_EGG + && !GetMonData(&gPlayerParty[j], MON_DATA_IS_EGG)) + { + gBattlerPartyIndexes[i] = j; + break; + } + } + else + { + if (GetMonData(&gEnemyParty[j], MON_DATA_HP) != 0 + && GetMonData(&gEnemyParty[j], MON_DATA_SPECIES2) != SPECIES_NONE + && GetMonData(&gEnemyParty[j], MON_DATA_SPECIES2) != SPECIES_EGG + && !GetMonData(&gEnemyParty[j], MON_DATA_IS_EGG)) + { + gBattlerPartyIndexes[i] = j; + break; + } + } + } + else + { + if (GET_BATTLER_SIDE2(i) == B_SIDE_PLAYER) + { + if (GetMonData(&gPlayerParty[j], MON_DATA_HP) != 0 + && GetMonData(&gPlayerParty[j], MON_DATA_SPECIES) != SPECIES_NONE // Probably a typo by Game Freak. The rest use SPECIES2. + && GetMonData(&gPlayerParty[j], MON_DATA_SPECIES2) != SPECIES_EGG + && !GetMonData(&gPlayerParty[j], MON_DATA_IS_EGG) + && gBattlerPartyIndexes[i - 2] != j) + { + gBattlerPartyIndexes[i] = j; + break; + } + } + else + { + if (GetMonData(&gEnemyParty[j], MON_DATA_HP) != 0 + && GetMonData(&gEnemyParty[j], MON_DATA_SPECIES2) != SPECIES_NONE + && GetMonData(&gEnemyParty[j], MON_DATA_SPECIES2) != SPECIES_EGG + && !GetMonData(&gEnemyParty[j], MON_DATA_IS_EGG) + && gBattlerPartyIndexes[i - 2] != j) + { + gBattlerPartyIndexes[i] = j; + break; + } + } + } + } + } + } +} + +static void PrepareBufferDataTransfer(u8 bufferId, u8 *data, u16 size) +{ + s32 i; + + if (gBattleTypeFlags & BATTLE_TYPE_LINK) + { + PrepareBufferDataTransferLink(bufferId, size, data); + } + else + { + switch (bufferId) + { + case 0: + for (i = 0; i < size; ++data, ++i) + gBattleBufferA[gActiveBattler][i] = *data; + break; + case 1: + for (i = 0; i < size; ++data, ++i) + gBattleBufferB[gActiveBattler][i] = *data; + break; + } + } +} + +static void CreateTasksForSendRecvLinkBuffers(void) +{ + sLinkSendTaskId = CreateTask(Task_HandleSendLinkBuffersData, 0); + gTasks[sLinkSendTaskId].data[11] = 0; + gTasks[sLinkSendTaskId].data[12] = 0; + gTasks[sLinkSendTaskId].data[13] = 0; + gTasks[sLinkSendTaskId].data[14] = 0; + gTasks[sLinkSendTaskId].data[15] = 0; + sLinkReceiveTaskId = CreateTask(Task_HandleCopyReceivedLinkBuffersData, 0); + gTasks[sLinkReceiveTaskId].data[12] = 0; + gTasks[sLinkReceiveTaskId].data[13] = 0; + gTasks[sLinkReceiveTaskId].data[14] = 0; + gTasks[sLinkReceiveTaskId].data[15] = 0; + gUnknown_202286E = 0; +} + +enum +{ + LINK_BUFF_BUFFER_ID, + LINK_BUFF_ACTIVE_BATTLER, + LINK_BUFF_ATTACKER, + LINK_BUFF_TARGET, + LINK_BUFF_SIZE_LO, + LINK_BUFF_SIZE_HI, + LINK_BUFF_ABSENT_BATTLER_FLAGS, + LINK_BUFF_EFFECT_BATTLER, + LINK_BUFF_DATA +}; + +void PrepareBufferDataTransferLink(u8 bufferId, u16 size, u8 *data) +{ + s32 alignedSize, i; + + alignedSize = size - size % 4 + 4; + if (gTasks[sLinkSendTaskId].data[14] + alignedSize + LINK_BUFF_DATA + 1 > BATTLE_BUFFER_LINK_SIZE) + { + gTasks[sLinkSendTaskId].data[12] = gTasks[sLinkSendTaskId].data[14]; + gTasks[sLinkSendTaskId].data[14] = 0; + } + gLinkBattleSendBuffer[gTasks[sLinkSendTaskId].data[14] + LINK_BUFF_BUFFER_ID] = bufferId; + gLinkBattleSendBuffer[gTasks[sLinkSendTaskId].data[14] + LINK_BUFF_ACTIVE_BATTLER] = gActiveBattler; + gLinkBattleSendBuffer[gTasks[sLinkSendTaskId].data[14] + LINK_BUFF_ATTACKER] = gBattlerAttacker; + gLinkBattleSendBuffer[gTasks[sLinkSendTaskId].data[14] + LINK_BUFF_TARGET] = gBattlerTarget; + gLinkBattleSendBuffer[gTasks[sLinkSendTaskId].data[14] + LINK_BUFF_SIZE_LO] = alignedSize; + gLinkBattleSendBuffer[gTasks[sLinkSendTaskId].data[14] + LINK_BUFF_SIZE_HI] = (alignedSize & 0x0000FF00) >> 8; + gLinkBattleSendBuffer[gTasks[sLinkSendTaskId].data[14] + LINK_BUFF_ABSENT_BATTLER_FLAGS] = gAbsentBattlerFlags; + gLinkBattleSendBuffer[gTasks[sLinkSendTaskId].data[14] + LINK_BUFF_EFFECT_BATTLER] = gEffectBattler; + for (i = 0; i < size; ++i) + gLinkBattleSendBuffer[gTasks[sLinkSendTaskId].data[14] + LINK_BUFF_DATA + i] = data[i]; + gTasks[sLinkSendTaskId].data[14] = gTasks[sLinkSendTaskId].data[14] + alignedSize + LINK_BUFF_DATA; +} + +static void Task_HandleSendLinkBuffersData(u8 taskId) +{ + u16 blockSize; + s32 playerCount; + + switch (gTasks[taskId].data[11]) + { + case 0: + gTasks[taskId].data[10] = 100; + ++gTasks[taskId].data[11]; + break; + case 1: + if (!--gTasks[taskId].data[10]) + { + ++gTasks[taskId].data[11]; + if (gReceivedRemoteLinkPlayers) + gTasks[taskId].data[11] = 3; + } + break; + case 2: + playerCount = GetLinkPlayerCount_2(); + if ((gBattleTypeFlags & BATTLE_TYPE_MULTI && playerCount > 3) || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && playerCount > 1)) + { + if (IsLinkMaster()) + { + CheckShouldAdvanceLinkState(); + ++gTasks[taskId].data[11]; + } + else + { + ++gTasks[taskId].data[11]; + } + } + break; + case 3: + if (gTasks[taskId].data[15] != gTasks[taskId].data[14]) + { + if (!gTasks[taskId].data[13]) + { + if (gTasks[taskId].data[15] > gTasks[taskId].data[14] + && gTasks[taskId].data[15] == gTasks[taskId].data[12]) + { + gTasks[taskId].data[12] = 0; + gTasks[taskId].data[15] = 0; + } + blockSize = (gLinkBattleSendBuffer[gTasks[taskId].data[15] + LINK_BUFF_SIZE_LO] | (gLinkBattleSendBuffer[gTasks[taskId].data[15] + LINK_BUFF_SIZE_HI] << 8)) + LINK_BUFF_DATA; + SendBlock(bitmask_all_link_players_but_self(), &gLinkBattleSendBuffer[gTasks[taskId].data[15]], blockSize); + ++gTasks[taskId].data[11]; + } + else + { + --gTasks[taskId].data[13]; + break; + } + } + break; + case 4: + if (IsLinkTaskFinished()) + { + blockSize = gLinkBattleSendBuffer[gTasks[taskId].data[15] + LINK_BUFF_SIZE_LO] | (gLinkBattleSendBuffer[gTasks[taskId].data[15] + LINK_BUFF_SIZE_HI] << 8); + gTasks[taskId].data[13] = 5; + gTasks[taskId].data[15] = gTasks[taskId].data[15] + blockSize + LINK_BUFF_DATA; + gTasks[taskId].data[11] = 3; + } + break; + case 5: + if (!--gTasks[taskId].data[13]) + { + gTasks[taskId].data[13] = 5; + gTasks[taskId].data[11] = 3; + } + break; + } +} + +void sub_800DD28(void) +{ + u8 i; + s32 j; + u8 *recvBuffer; + + if (gReceivedRemoteLinkPlayers && (gBattleTypeFlags & BATTLE_TYPE_20) && (gLinkPlayers[0].linkType == 0x2211)) + { + sub_80FBB4C(); + for (i = 0; i < GetLinkPlayerCount(); ++i) + { + if (GetBlockReceivedStatus() & gBitTable[i]) + { + ResetBlockReceivedFlag(i); + recvBuffer = (u8 *)gBlockRecvBuffer[i]; + { + u8 *dest, *src; + u16 r6 = gBlockRecvBuffer[i][2]; + + if (gTasks[sLinkReceiveTaskId].data[14] + 9 + r6 > 0x1000) + { + gTasks[sLinkReceiveTaskId].data[12] = gTasks[sLinkReceiveTaskId].data[14]; + gTasks[sLinkReceiveTaskId].data[14] = 0; + } + dest = &gLinkBattleRecvBuffer[gTasks[sLinkReceiveTaskId].data[14]]; + src = recvBuffer; + for (j = 0; j < r6 + 8; ++j) + dest[j] = src[j]; + gTasks[sLinkReceiveTaskId].data[14] = gTasks[sLinkReceiveTaskId].data[14] + r6 + 8; + } + } + } + } +} + +static void Task_HandleCopyReceivedLinkBuffersData(u8 taskId) +{ + u16 blockSize; + u8 battlerId; + u8 var; + + if (gTasks[taskId].data[15] != gTasks[taskId].data[14]) + { + if (gTasks[taskId].data[15] > gTasks[taskId].data[14] + && gTasks[taskId].data[15] == gTasks[taskId].data[12]) + { + gTasks[taskId].data[12] = 0; + gTasks[taskId].data[15] = 0; + } + battlerId = gLinkBattleRecvBuffer[gTasks[taskId].data[15] + LINK_BUFF_ACTIVE_BATTLER]; + blockSize = gLinkBattleRecvBuffer[gTasks[taskId].data[15] + LINK_BUFF_SIZE_LO] | (gLinkBattleRecvBuffer[gTasks[taskId].data[15] + LINK_BUFF_SIZE_HI] << 8); + switch (gLinkBattleRecvBuffer[gTasks[taskId].data[15] + 0]) + { + case 0: + if (gBattleControllerExecFlags & gBitTable[battlerId]) + return; + memcpy(gBattleBufferA[battlerId], &gLinkBattleRecvBuffer[gTasks[taskId].data[15] + LINK_BUFF_DATA], blockSize); + sub_8017298(battlerId); + if (!(gBattleTypeFlags & BATTLE_TYPE_IS_MASTER)) + { + gBattlerAttacker = gLinkBattleRecvBuffer[gTasks[taskId].data[15] + LINK_BUFF_ATTACKER]; + gBattlerTarget = gLinkBattleRecvBuffer[gTasks[taskId].data[15] + LINK_BUFF_TARGET]; + gAbsentBattlerFlags = gLinkBattleRecvBuffer[gTasks[taskId].data[15] + LINK_BUFF_ABSENT_BATTLER_FLAGS]; + gEffectBattler = gLinkBattleRecvBuffer[gTasks[taskId].data[15] + LINK_BUFF_EFFECT_BATTLER]; + } + break; + case 1: + memcpy(gBattleBufferB[battlerId], &gLinkBattleRecvBuffer[gTasks[taskId].data[15] + LINK_BUFF_DATA], blockSize); + break; + case 2: + var = gLinkBattleRecvBuffer[gTasks[taskId].data[15] + LINK_BUFF_DATA]; + gBattleControllerExecFlags &= ~(gBitTable[battlerId] << (var * 4)); + break; + } + gTasks[taskId].data[15] = gTasks[taskId].data[15] + blockSize + LINK_BUFF_DATA; + } +} + +void BtlController_EmitGetMonData(u8 bufferId, u8 requestId, u8 monToCheck) +{ + sBattleBuffersTransferData[0] = CONTROLLER_GETMONDATA; + sBattleBuffersTransferData[1] = requestId; + sBattleBuffersTransferData[2] = monToCheck; + sBattleBuffersTransferData[3] = 0; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); +} + +// not used +static void BtlController_EmitGetRawMonData(u8 bufferId, u8 monId, u8 bytes) +{ + sBattleBuffersTransferData[0] = CONTROLLER_GETRAWMONDATA; + sBattleBuffersTransferData[1] = monId; + sBattleBuffersTransferData[2] = bytes; + sBattleBuffersTransferData[3] = 0; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); +} + +void BtlController_EmitSetMonData(u8 bufferId, u8 requestId, u8 monToCheck, u8 bytes, void *data) +{ + s32 i; + + sBattleBuffersTransferData[0] = CONTROLLER_SETMONDATA; + sBattleBuffersTransferData[1] = requestId; + sBattleBuffersTransferData[2] = monToCheck; + for (i = 0; i < bytes; ++i) + sBattleBuffersTransferData[3 + i] = *(u8 *)(data++); + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 3 + bytes); +} + +// not used +static void BtlController_EmitSetRawMonData(u8 bufferId, u8 monId, u8 bytes, void *data) +{ + s32 i; + + sBattleBuffersTransferData[0] = CONTROLLER_SETRAWMONDATA; + sBattleBuffersTransferData[1] = monId; + sBattleBuffersTransferData[2] = bytes; + for (i = 0; i < bytes; ++i) + sBattleBuffersTransferData[3 + i] = *(u8 *)(data++); + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, bytes + 3); +} + +void BtlController_EmitLoadMonSprite(u8 bufferId) +{ + sBattleBuffersTransferData[0] = CONTROLLER_LOADMONSPRITE; + sBattleBuffersTransferData[1] = CONTROLLER_LOADMONSPRITE; + sBattleBuffersTransferData[2] = CONTROLLER_LOADMONSPRITE; + sBattleBuffersTransferData[3] = CONTROLLER_LOADMONSPRITE; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); +} + +void BtlController_EmitSwitchInAnim(u8 bufferId, u8 partyId, bool8 dontClearSubstituteBit) +{ + sBattleBuffersTransferData[0] = CONTROLLER_SWITCHINANIM; + sBattleBuffersTransferData[1] = partyId; + sBattleBuffersTransferData[2] = dontClearSubstituteBit; + sBattleBuffersTransferData[3] = 5; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); +} + +void BtlController_EmitReturnMonToBall(u8 bufferId, u8 arg1) +{ + sBattleBuffersTransferData[0] = CONTROLLER_RETURNMONTOBALL; + sBattleBuffersTransferData[1] = arg1; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 2); +} + +void BtlController_EmitDrawTrainerPic(u8 bufferId) +{ + sBattleBuffersTransferData[0] = CONTROLLER_DRAWTRAINERPIC; + sBattleBuffersTransferData[1] = CONTROLLER_DRAWTRAINERPIC; + sBattleBuffersTransferData[2] = CONTROLLER_DRAWTRAINERPIC; + sBattleBuffersTransferData[3] = CONTROLLER_DRAWTRAINERPIC; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); +} + +void BtlController_EmitTrainerSlide(u8 bufferId) +{ + sBattleBuffersTransferData[0] = CONTROLLER_TRAINERSLIDE; + sBattleBuffersTransferData[1] = CONTROLLER_TRAINERSLIDE; + sBattleBuffersTransferData[2] = CONTROLLER_TRAINERSLIDE; + sBattleBuffersTransferData[3] = CONTROLLER_TRAINERSLIDE; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); +} + +void BtlController_EmitTrainerSlideBack(u8 bufferId) +{ + sBattleBuffersTransferData[0] = CONTROLLER_TRAINERSLIDEBACK; + sBattleBuffersTransferData[1] = CONTROLLER_TRAINERSLIDEBACK; + sBattleBuffersTransferData[2] = CONTROLLER_TRAINERSLIDEBACK; + sBattleBuffersTransferData[3] = CONTROLLER_TRAINERSLIDEBACK; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); +} + +void BtlController_EmitFaintAnimation(u8 bufferId) +{ + sBattleBuffersTransferData[0] = CONTROLLER_FAINTANIMATION; + sBattleBuffersTransferData[1] = CONTROLLER_FAINTANIMATION; + sBattleBuffersTransferData[2] = CONTROLLER_FAINTANIMATION; + sBattleBuffersTransferData[3] = CONTROLLER_FAINTANIMATION; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); +} + +// not used +static void BtlController_EmitPaletteFade(u8 bufferId) +{ + sBattleBuffersTransferData[0] = CONTROLLER_PALETTEFADE; + sBattleBuffersTransferData[1] = CONTROLLER_PALETTEFADE; + sBattleBuffersTransferData[2] = CONTROLLER_PALETTEFADE; + sBattleBuffersTransferData[3] = CONTROLLER_PALETTEFADE; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); +} + +// not used +static void BtlController_EmitSuccessBallThrowAnim(u8 bufferId) +{ + sBattleBuffersTransferData[0] = CONTROLLER_SUCCESSBALLTHROWANIM; + sBattleBuffersTransferData[1] = CONTROLLER_SUCCESSBALLTHROWANIM; + sBattleBuffersTransferData[2] = CONTROLLER_SUCCESSBALLTHROWANIM; + sBattleBuffersTransferData[3] = CONTROLLER_SUCCESSBALLTHROWANIM; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); +} + +void BtlController_EmitBallThrowAnim(u8 bufferId, u8 caseId) +{ + sBattleBuffersTransferData[0] = CONTROLLER_BALLTHROWANIM; + sBattleBuffersTransferData[1] = caseId; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 2); +} + +// not used +static void BtlController_EmitPause(u8 bufferId, u8 toWait, void *data) +{ + s32 i; + + sBattleBuffersTransferData[0] = CONTROLLER_PAUSE; + sBattleBuffersTransferData[1] = toWait; + for (i = 0; i < toWait * 3; ++i) + sBattleBuffersTransferData[2 + i] = *(u8 *)(data++); + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, toWait * 3 + 2); +} + +void BtlController_EmitMoveAnimation(u8 bufferId, u16 move, u8 turnOfMove, u16 movePower, s32 dmg, u8 friendship, struct DisableStruct *disableStructPtr) +{ + sBattleBuffersTransferData[0] = CONTROLLER_MOVEANIMATION; + sBattleBuffersTransferData[1] = move; + sBattleBuffersTransferData[2] = (move & 0xFF00) >> 8; + sBattleBuffersTransferData[3] = turnOfMove; + sBattleBuffersTransferData[4] = movePower; + sBattleBuffersTransferData[5] = (movePower & 0xFF00) >> 8; + sBattleBuffersTransferData[6] = dmg; + sBattleBuffersTransferData[7] = (dmg & 0x0000FF00) >> 8; + sBattleBuffersTransferData[8] = (dmg & 0x00FF0000) >> 16; + sBattleBuffersTransferData[9] = (dmg & 0xFF000000) >> 24; + sBattleBuffersTransferData[10] = friendship; + sBattleBuffersTransferData[11] = gMultiHitCounter; // multihit in pokeem + if (WEATHER_HAS_EFFECT2) + { + sBattleBuffersTransferData[12] = gBattleWeather; + sBattleBuffersTransferData[13] = (gBattleWeather & 0xFF00) >> 8; + } + else + { + sBattleBuffersTransferData[12] = 0; + sBattleBuffersTransferData[13] = 0; + } + sBattleBuffersTransferData[14] = 0; + sBattleBuffersTransferData[15] = 0; + memcpy(&sBattleBuffersTransferData[16], disableStructPtr, sizeof(struct DisableStruct)); + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 16 + sizeof(struct DisableStruct)); +} + +void BtlController_EmitPrintString(u8 bufferId, u16 stringID) +{ + s32 i; + struct BattleMsgData *stringInfo; + + sBattleBuffersTransferData[0] = CONTROLLER_PRINTSTRING; + sBattleBuffersTransferData[1] = gBattleOutcome; + sBattleBuffersTransferData[2] = stringID; + sBattleBuffersTransferData[3] = (stringID & 0xFF00) >> 8; + stringInfo = (struct BattleMsgData *)(&sBattleBuffersTransferData[4]); + stringInfo->currentMove = gCurrentMove; + stringInfo->originallyUsedMove = gChosenMove; + stringInfo->lastItem = gLastUsedItem; + stringInfo->lastAbility = gLastUsedAbility; + stringInfo->scrActive = gBattleScripting.battler; + stringInfo->unk1605E = gBattleStruct->field_52; + stringInfo->hpScale = gBattleStruct->hpScale; + stringInfo->itemEffectBattler = gPotentialItemEffectBattler; + stringInfo->moveType = gBattleMoves[gCurrentMove].type; + for (i = 0; i < MAX_BATTLERS_COUNT; ++i) + stringInfo->abilities[i] = gBattleMons[i].ability; + for (i = 0; i < TEXT_BUFF_ARRAY_COUNT; ++i) + { + stringInfo->textBuffs[0][i] = gBattleTextBuff1[i]; + stringInfo->textBuffs[1][i] = gBattleTextBuff2[i]; + stringInfo->textBuffs[2][i] = gBattleTextBuff3[i]; + } + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, sizeof(struct BattleMsgData) + 4); +} + +void BtlController_EmitPrintSelectionString(u8 bufferId, u16 stringID) +{ + s32 i; + struct BattleMsgData *stringInfo; + + sBattleBuffersTransferData[0] = CONTROLLER_PRINTSTRINGPLAYERONLY; + sBattleBuffersTransferData[1] = CONTROLLER_PRINTSTRINGPLAYERONLY; + sBattleBuffersTransferData[2] = stringID; + sBattleBuffersTransferData[3] = (stringID & 0xFF00) >> 8; + stringInfo = (struct BattleMsgData*)(&sBattleBuffersTransferData[4]); + stringInfo->currentMove = gCurrentMove; + stringInfo->originallyUsedMove = gChosenMove; + stringInfo->lastItem = gLastUsedItem; + stringInfo->lastAbility = gLastUsedAbility; + stringInfo->scrActive = gBattleScripting.battler; + stringInfo->unk1605E = gBattleStruct->field_52; + for (i = 0; i < MAX_BATTLERS_COUNT; ++i) + stringInfo->abilities[i] = gBattleMons[i].ability; + for (i = 0; i < TEXT_BUFF_ARRAY_COUNT; ++i) + { + stringInfo->textBuffs[0][i] = gBattleTextBuff1[i]; + stringInfo->textBuffs[1][i] = gBattleTextBuff2[i]; + stringInfo->textBuffs[2][i] = gBattleTextBuff3[i]; + } + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, sizeof(struct BattleMsgData) + 4); +} + +void BtlController_EmitChooseAction(u8 bufferId, u8 arg1, u16 arg2) +{ + sBattleBuffersTransferData[0] = CONTROLLER_CHOOSEACTION; + sBattleBuffersTransferData[1] = arg1; + sBattleBuffersTransferData[2] = arg2; + sBattleBuffersTransferData[3] = (arg2 & 0xFF00) >> 8; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); +} + +// not used +static void BtlController_EmitUnknownYesNoBox(u8 bufferId, u32 arg1) // TODO: Does the function name make sense for pokefirered? +{ + sBattleBuffersTransferData[0] = CONTROLLER_UNKNOWNYESNOBOX; + sBattleBuffersTransferData[1] = arg1; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 2); +} + +void BtlController_EmitChooseMove(u8 bufferId, bool8 isDoubleBattle, bool8 NoPpNumber, struct ChooseMoveStruct *movePpData) +{ + s32 i; + + sBattleBuffersTransferData[0] = CONTROLLER_CHOOSEMOVE; + sBattleBuffersTransferData[1] = isDoubleBattle; + sBattleBuffersTransferData[2] = NoPpNumber; + sBattleBuffersTransferData[3] = 0; + for (i = 0; i < sizeof(*movePpData); ++i) + sBattleBuffersTransferData[4 + i] = *((u8 *)(movePpData) + i); + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, sizeof(*movePpData) + 4); +} + +void BtlController_EmitChooseItem(u8 bufferId, u8 *arg1) +{ + s32 i; + + sBattleBuffersTransferData[0] = CONTROLLER_OPENBAG; + for (i = 0; i < 3; ++i) + sBattleBuffersTransferData[1 + i] = arg1[i]; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); +} + +void BtlController_EmitChoosePokemon(u8 bufferId, u8 caseId, u8 arg2, u8 abilityId, u8 *arg4) +{ + s32 i; + + sBattleBuffersTransferData[0] = CONTROLLER_CHOOSEPOKEMON; + sBattleBuffersTransferData[1] = caseId; + sBattleBuffersTransferData[2] = arg2; + sBattleBuffersTransferData[3] = abilityId; + for (i = 0; i < 3; ++i) + sBattleBuffersTransferData[4 + i] = arg4[i]; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 8); // Only 7 bytes were written. +} + +// not used +static void BtlController_EmitCmd23(u8 bufferId) +{ + sBattleBuffersTransferData[0] = CONTROLLER_23; + sBattleBuffersTransferData[1] = CONTROLLER_23; + sBattleBuffersTransferData[2] = CONTROLLER_23; + sBattleBuffersTransferData[3] = CONTROLLER_23; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); +} + +void BtlController_EmitHealthBarUpdate(u8 bufferId, u16 hpValue) +{ + sBattleBuffersTransferData[0] = CONTROLLER_HEALTHBARUPDATE; + sBattleBuffersTransferData[1] = 0; + sBattleBuffersTransferData[2] = (s16)hpValue; + sBattleBuffersTransferData[3] = ((s16)hpValue & 0xFF00) >> 8; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); +} + +void BtlController_EmitExpUpdate(u8 bufferId, u8 partyId, u16 expPoints) +{ + sBattleBuffersTransferData[0] = CONTROLLER_EXPUPDATE; + sBattleBuffersTransferData[1] = partyId; + sBattleBuffersTransferData[2] = (s16)expPoints; + sBattleBuffersTransferData[3] = ((s16)expPoints & 0xFF00) >> 8; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); +} + +void BtlController_EmitStatusIconUpdate(u8 bufferId, u32 status1, u32 status2) +{ + sBattleBuffersTransferData[0] = CONTROLLER_STATUSICONUPDATE; + sBattleBuffersTransferData[1] = status1; + sBattleBuffersTransferData[2] = (status1 & 0x0000FF00) >> 8; + sBattleBuffersTransferData[3] = (status1 & 0x00FF0000) >> 16; + sBattleBuffersTransferData[4] = (status1 & 0xFF000000) >> 24; + sBattleBuffersTransferData[5] = status2; + sBattleBuffersTransferData[6] = (status2 & 0x0000FF00) >> 8; + sBattleBuffersTransferData[7] = (status2 & 0x00FF0000) >> 16; + sBattleBuffersTransferData[8] = (status2 & 0xFF000000) >> 24; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 9); +} + +void BtlController_EmitStatusAnimation(u8 bufferId, bool8 status2, u32 status) +{ + sBattleBuffersTransferData[0] = CONTROLLER_STATUSANIMATION; + sBattleBuffersTransferData[1] = status2; + sBattleBuffersTransferData[2] = status; + sBattleBuffersTransferData[3] = (status & 0x0000FF00) >> 8; + sBattleBuffersTransferData[4] = (status & 0x00FF0000) >> 16; + sBattleBuffersTransferData[5] = (status & 0xFF000000) >> 24; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 6); +} + +// not used +static void BtlController_EmitStatusXor(u8 bufferId, u8 b) +{ + sBattleBuffersTransferData[0] = CONTROLLER_STATUSXOR; + sBattleBuffersTransferData[1] = b; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 2); +} + +void BtlController_EmitDataTransfer(u8 bufferId, u16 size, void *data) +{ + s32 i; + + sBattleBuffersTransferData[0] = CONTROLLER_DATATRANSFER; + sBattleBuffersTransferData[1] = CONTROLLER_DATATRANSFER; + sBattleBuffersTransferData[2] = size; + sBattleBuffersTransferData[3] = (size & 0xFF00) >> 8; + for (i = 0; i < size; ++i) + sBattleBuffersTransferData[4 + i] = *(u8 *)(data++); + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, size + 4); +} + +// not used +static void BtlController_EmitDMA3Transfer(u8 bufferId, void *dst, u16 size, void *data) +{ + s32 i; + + sBattleBuffersTransferData[0] = CONTROLLER_DMA3TRANSFER; + sBattleBuffersTransferData[1] = (u32)(dst); + sBattleBuffersTransferData[2] = ((u32)(dst) & 0x0000FF00) >> 8; + sBattleBuffersTransferData[3] = ((u32)(dst) & 0x00FF0000) >> 16; + sBattleBuffersTransferData[4] = ((u32)(dst) & 0xFF000000) >> 24; + sBattleBuffersTransferData[5] = size; + sBattleBuffersTransferData[6] = (size & 0xFF00) >> 8; + for (i = 0; i < size; ++i) + sBattleBuffersTransferData[7 + i] = *(u8 *)(data++); + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, size + 7); +} + +// not used +static void BtlController_EmitPlayBGM(u8 bufferId, u16 songId, void *unusedDumbDataParameter) +{ + s32 i; + + sBattleBuffersTransferData[0] = CONTROLLER_31; + sBattleBuffersTransferData[1] = songId; + sBattleBuffersTransferData[2] = (songId & 0xFF00) >> 8; + for (i = 0; i < songId; ++i) + sBattleBuffersTransferData[3 + i] = *(u8 *)(unusedDumbDataParameter++); + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, songId + 3); +} + +// not used +static void BtlController_EmitCmd32(u8 bufferId, u16 size, void *data) +{ + s32 i; + + sBattleBuffersTransferData[0] = CONTROLLER_32; + sBattleBuffersTransferData[1] = size; + sBattleBuffersTransferData[2] = (size & 0xFF00) >> 8; + for (i = 0; i < size; ++i) + sBattleBuffersTransferData[3 + i] = *(u8 *)(data++); + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, size + 3); +} + +void BtlController_EmitTwoReturnValues(u8 bufferId, u8 arg1, u16 arg2) +{ + sBattleBuffersTransferData[0] = CONTROLLER_TWORETURNVALUES; + sBattleBuffersTransferData[1] = arg1; + sBattleBuffersTransferData[2] = arg2; + sBattleBuffersTransferData[3] = (arg2 & 0xFF00) >> 8; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); +} + +void BtlController_EmitChosenMonReturnValue(u8 bufferId, u8 b, u8 *c) +{ + s32 i; + + sBattleBuffersTransferData[0] = CONTROLLER_CHOSENMONRETURNVALUE; + sBattleBuffersTransferData[1] = b; + for (i = 0; i < 3; ++i) + sBattleBuffersTransferData[2 + i] = c[i]; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 5); +} + +void BtlController_EmitOneReturnValue(u8 bufferId, u16 arg1) +{ + sBattleBuffersTransferData[0] = CONTROLLER_ONERETURNVALUE; + sBattleBuffersTransferData[1] = arg1; + sBattleBuffersTransferData[2] = (arg1 & 0xFF00) >> 8; + sBattleBuffersTransferData[3] = 0; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); +} + +void BtlController_EmitOneReturnValue_Duplicate(u8 bufferId, u16 b) +{ + sBattleBuffersTransferData[0] = CONTROLLER_ONERETURNVALUE_DUPLICATE; + sBattleBuffersTransferData[1] = b; + sBattleBuffersTransferData[2] = (b & 0xFF00) >> 8; + sBattleBuffersTransferData[3] = 0; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); +} + +// not used +static void BtlController_EmitCmd37(u8 bufferId) +{ + sBattleBuffersTransferData[0] = CONTROLLER_37; + sBattleBuffersTransferData[1] = CONTROLLER_37; + sBattleBuffersTransferData[2] = CONTROLLER_37; + sBattleBuffersTransferData[3] = CONTROLLER_37; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); +} + +// not used +static void BtlController_EmitCmd38(u8 bufferId, u8 b) +{ + sBattleBuffersTransferData[0] = CONTROLLER_38; + sBattleBuffersTransferData[1] = b; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 2); +} + +// not used +static void BtlController_EmitCmd39(u8 bufferId) +{ + sBattleBuffersTransferData[0] = CONTROLLER_39; + sBattleBuffersTransferData[1] = CONTROLLER_39; + sBattleBuffersTransferData[2] = CONTROLLER_39; + sBattleBuffersTransferData[3] = CONTROLLER_39; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); +} + +// not used +static void BtlController_EmitCmd40(u8 bufferId) +{ + sBattleBuffersTransferData[0] = CONTROLLER_40; + sBattleBuffersTransferData[1] = CONTROLLER_40; + sBattleBuffersTransferData[2] = CONTROLLER_40; + sBattleBuffersTransferData[3] = CONTROLLER_40; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); +} + +void BtlController_EmitHitAnimation(u8 bufferId) +{ + sBattleBuffersTransferData[0] = CONTROLLER_HITANIMATION; + sBattleBuffersTransferData[1] = CONTROLLER_HITANIMATION; + sBattleBuffersTransferData[2] = CONTROLLER_HITANIMATION; + sBattleBuffersTransferData[3] = CONTROLLER_HITANIMATION; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); +} + +void BtlController_EmitCmd42(u8 bufferId) +{ + sBattleBuffersTransferData[0] = CONTROLLER_42; + sBattleBuffersTransferData[1] = CONTROLLER_42; + sBattleBuffersTransferData[2] = CONTROLLER_42; + sBattleBuffersTransferData[3] = CONTROLLER_42; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); +} + +void BtlController_EmitPlaySE(u8 bufferId, u16 songId) +{ + sBattleBuffersTransferData[0] = CONTROLLER_EFFECTIVENESSSOUND; + sBattleBuffersTransferData[1] = songId; + sBattleBuffersTransferData[2] = (songId & 0xFF00) >> 8; + sBattleBuffersTransferData[3] = 0; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); +} + +void BtlController_EmitPlayFanfare(u8 bufferId, u16 songId) +{ + sBattleBuffersTransferData[0] = CONTROLLER_PLAYFANFAREORBGM; + sBattleBuffersTransferData[1] = songId; + sBattleBuffersTransferData[2] = (songId & 0xFF00) >> 8; + sBattleBuffersTransferData[3] = 0; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); +} + +void BtlController_EmitFaintingCry(u8 bufferId) +{ + sBattleBuffersTransferData[0] = CONTROLLER_FAINTINGCRY; + sBattleBuffersTransferData[1] = CONTROLLER_FAINTINGCRY; + sBattleBuffersTransferData[2] = CONTROLLER_FAINTINGCRY; + sBattleBuffersTransferData[3] = CONTROLLER_FAINTINGCRY; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); +} + +void BtlController_EmitIntroSlide(u8 bufferId, u8 terrainId) +{ + sBattleBuffersTransferData[0] = CONTROLLER_INTROSLIDE; + sBattleBuffersTransferData[1] = terrainId; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 2); +} + +void BtlController_EmitIntroTrainerBallThrow(u8 bufferId) +{ + sBattleBuffersTransferData[0] = CONTROLLER_INTROTRAINERBALLTHROW; + sBattleBuffersTransferData[1] = CONTROLLER_INTROTRAINERBALLTHROW; + sBattleBuffersTransferData[2] = CONTROLLER_INTROTRAINERBALLTHROW; + sBattleBuffersTransferData[3] = CONTROLLER_INTROTRAINERBALLTHROW; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); +} + +void BtlController_EmitDrawPartyStatusSummary(u8 bufferId, struct HpAndStatus* hpAndStatus, u8 arg2) +{ + s32 i; + + sBattleBuffersTransferData[0] = CONTROLLER_DRAWPARTYSTATUSSUMMARY; + sBattleBuffersTransferData[1] = arg2 & 0x7F; + sBattleBuffersTransferData[2] = (arg2 & 0x80) >> 7; + sBattleBuffersTransferData[3] = CONTROLLER_DRAWPARTYSTATUSSUMMARY; + for (i = 0; i < (s32)(sizeof(struct HpAndStatus) * PARTY_SIZE); ++i) + sBattleBuffersTransferData[4 + i] = *(i + (u8 *)(hpAndStatus)); + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, sizeof(struct HpAndStatus) * PARTY_SIZE + 4); +} + +void BtlController_EmitHidePartyStatusSummary(u8 bufferId) +{ + sBattleBuffersTransferData[0] = CONTROLLER_HIDEPARTYSTATUSSUMMARY; + sBattleBuffersTransferData[1] = CONTROLLER_HIDEPARTYSTATUSSUMMARY; + sBattleBuffersTransferData[2] = CONTROLLER_HIDEPARTYSTATUSSUMMARY; + sBattleBuffersTransferData[3] = CONTROLLER_HIDEPARTYSTATUSSUMMARY; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); +} + +void BtlController_EmitEndBounceEffect(u8 bufferId) +{ + sBattleBuffersTransferData[0] = CONTROLLER_ENDBOUNCE; + sBattleBuffersTransferData[1] = CONTROLLER_ENDBOUNCE; + sBattleBuffersTransferData[2] = CONTROLLER_ENDBOUNCE; + sBattleBuffersTransferData[3] = CONTROLLER_ENDBOUNCE; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); +} + +void BtlController_EmitSpriteInvisibility(u8 bufferId, bool8 isInvisible) +{ + sBattleBuffersTransferData[0] = CONTROLLER_SPRITEINVISIBILITY; + sBattleBuffersTransferData[1] = isInvisible; + sBattleBuffersTransferData[2] = CONTROLLER_SPRITEINVISIBILITY; + sBattleBuffersTransferData[3] = CONTROLLER_SPRITEINVISIBILITY; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); +} + +void BtlController_EmitBattleAnimation(u8 bufferId, u8 animationId, u16 argument) +{ + sBattleBuffersTransferData[0] = CONTROLLER_BATTLEANIMATION; + sBattleBuffersTransferData[1] = animationId; + sBattleBuffersTransferData[2] = argument; + sBattleBuffersTransferData[3] = (argument & 0xFF00) >> 8; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 4); +} + +void BtlController_EmitLinkStandbyMsg(u8 bufferId, u8 arg1) +{ + sBattleBuffersTransferData[0] = CONTROLLER_LINKSTANDBYMSG; + sBattleBuffersTransferData[1] = arg1; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 2); +} + +void BtlController_EmitResetActionMoveSelection(u8 bufferId, u8 caseId) +{ + sBattleBuffersTransferData[0] = CONTROLLER_RESETACTIONMOVESELECTION; + sBattleBuffersTransferData[1] = caseId; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 2); +} + +void BtlController_EmitCmd55(u8 bufferId, u8 battleOutcome) +{ + sBattleBuffersTransferData[0] = CONTROLLER_55; + sBattleBuffersTransferData[1] = battleOutcome; + PrepareBufferDataTransfer(bufferId, sBattleBuffersTransferData, 2); +} diff --git a/src/battle_gfx_sfx_util.c b/src/battle_gfx_sfx_util.c new file mode 100644 index 000000000..a47baea99 --- /dev/null +++ b/src/battle_gfx_sfx_util.c @@ -0,0 +1,1056 @@ +#include "global.h" +#include "main.h" +#include "m4a.h" +#include "task.h" +#include "malloc.h" +#include "graphics.h" +#include "sound.h" +#include "decompress.h" +#include "palette.h" +#include "sprite.h" +#include "data.h" +#include "util.h" +#include "party_menu.h" +#include "battle.h" +#include "battle_main.h" +#include "battle_controllers.h" +#include "battle_ai_script_commands.h" +#include "battle_anim.h" +#include "battle_interface.h" +#include "constants/battle_anim.h" +#include "constants/species.h" +#include "constants/moves.h" +#include "constants/songs.h" + +static bool8 ShouldAnimBeDoneRegardlessOfSubsitute(u8 animId); +static void Task_ClearBitWhenBattleTableAnimDone(u8 taskId); +static void Task_ClearBitWhenSpecialAnimDone(u8 taskId); +static void ClearSpritesBattlerHealthboxAnimData(void); + +static const struct CompressedSpriteSheet sSpriteSheet_SinglesPlayerHealthbox = +{ + .data = gHealthboxSinglesPlayerGfx, + .size = 0x1000, + .tag = TAG_HEALTHBOX_PLAYER1_TILE, +}; + +static const struct CompressedSpriteSheet sSpriteSheet_SinglesOpponentHealthbox = +{ + .data = gHealthboxSinglesOpponentGfx, + .size = 0x1000, + .tag = TAG_HEALTHBOX_OPPONENT1_TILE, +}; + +static const struct CompressedSpriteSheet sSpriteSheets_DoublesPlayerHealthbox[2] = +{ + { + .data = gHealthboxDoublesPlayerGfx, + .size = 0x800, + .tag = TAG_HEALTHBOX_PLAYER1_TILE, + }, + { + .data = gHealthboxDoublesPlayerGfx, + .size = 0x800, + .tag = TAG_HEALTHBOX_PLAYER2_TILE, + }, +}; + +static const struct CompressedSpriteSheet sSpriteSheets_DoublesOpponentHealthbox[2] = +{ + { + .data = gHealthboxDoublesOpponentGfx, + .size = 0x800, + .tag = TAG_HEALTHBOX_OPPONENT1_TILE, + }, + { + .data = gHealthboxDoublesOpponentGfx, + .size = 0x800, + .tag = TAG_HEALTHBOX_OPPONENT2_TILE, + }, +}; + +static const struct CompressedSpriteSheet sSpriteSheet_SafariHealthbox = +{ + .data = gHealthboxSafariGfx, + .size = 0x1000, + .tag = TAG_HEALTHBOX_SAFARI_TILE, +}; + +static const struct CompressedSpriteSheet sSpriteSheets_HealthBar[MAX_BATTLERS_COUNT] = +{ + { + .data = gBlankGfxCompressed, + .size = 0x100, + .tag = TAG_HEALTHBAR_PLAYER1_TILE, + }, + { + .data = gBlankGfxCompressed, + .size = 0x120, + .tag = TAG_HEALTHBAR_OPPONENT1_TILE, + }, + { + .data = gBlankGfxCompressed, + .size = 0x100, + .tag = TAG_HEALTHBAR_PLAYER2_TILE, + }, + { + .data = gBlankGfxCompressed, + .size = 0x120, + .tag = TAG_HEALTHBAR_OPPONENT2_TILE, + }, +}; + +static const struct SpritePalette sSpritePalettes_HealthBoxHealthBar[2] = +{ + { + .data = gBattleInterface_BallStatusBarPal, + .tag = TAG_HEALTHBOX_PAL, + }, + { + .data = gBattleInterface_BallDisplayPal, + .tag = TAG_HEALTHBAR_PAL, + }, +}; + +void AllocateBattleSpritesData(void) +{ + gBattleSpritesDataPtr = AllocZeroed(sizeof(struct BattleSpriteData)); + gBattleSpritesDataPtr->battlerData = AllocZeroed(sizeof(struct BattleSpriteInfo) * MAX_BATTLERS_COUNT); + gBattleSpritesDataPtr->healthBoxesData = AllocZeroed(sizeof(struct BattleHealthboxInfo) * MAX_BATTLERS_COUNT); + gBattleSpritesDataPtr->animationData = AllocZeroed(sizeof(struct BattleAnimationInfo)); + gBattleSpritesDataPtr->battleBars = AllocZeroed(sizeof(struct BattleBarInfo) * MAX_BATTLERS_COUNT); +} + +void FreeBattleSpritesData(void) +{ + if (gBattleSpritesDataPtr) + { + FREE_AND_SET_NULL(gBattleSpritesDataPtr->battleBars); + FREE_AND_SET_NULL(gBattleSpritesDataPtr->animationData); + FREE_AND_SET_NULL(gBattleSpritesDataPtr->healthBoxesData); + FREE_AND_SET_NULL(gBattleSpritesDataPtr->battlerData); + FREE_AND_SET_NULL(gBattleSpritesDataPtr); + } +} + +void sub_8033E3C(struct Sprite *sprite) +{ + u8 spriteId = sprite->data[1]; + + if (!gSprites[spriteId].affineAnimEnded) + return; + if (gSprites[spriteId].invisible) + return; + if (gSprites[spriteId].animPaused) + gSprites[spriteId].animPaused = 0; + else if (gSprites[spriteId].animEnded) + { + gSprites[spriteId].callback = sub_8012100; + StartSpriteAffineAnim(&gSprites[spriteId], 0); + sprite->callback = SpriteCallbackDummy; + } +} + +// not used +static void sub_8033EB0(struct Sprite *sprite, bool8 arg1) +{ + sprite->animPaused = 1; + sprite->callback = SpriteCallbackDummy; + if (!arg1) + StartSpriteAffineAnim(sprite, 1); + else + StartSpriteAffineAnim(sprite, 1); + AnimateSprite(sprite); +} + +void sub_8033EEC(struct Sprite *sprite) +{ + if (!(gIntroSlideFlags & 1)) + { + sprite->pos2.x += sprite->data[0]; + if (sprite->pos2.x == 0) + sprite->callback = SpriteCallbackDummy; + } +} + +void InitAndLaunchChosenStatusAnimation(bool8 isStatus2, u32 status) +{ + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].statusAnimActive = 1; + if (!isStatus2) + { + if (status == STATUS1_FREEZE) + LaunchStatusAnimation(gActiveBattler, B_ANIM_STATUS_FRZ); + else if (status == STATUS1_POISON || status & STATUS1_TOXIC_POISON) + LaunchStatusAnimation(gActiveBattler, B_ANIM_STATUS_PSN); + else if (status == STATUS1_BURN) + LaunchStatusAnimation(gActiveBattler, B_ANIM_STATUS_BRN); + else if (status & STATUS1_SLEEP) + LaunchStatusAnimation(gActiveBattler, B_ANIM_STATUS_SLP); + else if (status == STATUS1_PARALYSIS) + LaunchStatusAnimation(gActiveBattler, B_ANIM_STATUS_PRZ); + else // no animation + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].statusAnimActive = 0; + } + else + { + if (status & STATUS2_INFATUATION) + LaunchStatusAnimation(gActiveBattler, B_ANIM_STATUS_INFATUATION); + else if (status & STATUS2_CONFUSION) + LaunchStatusAnimation(gActiveBattler, B_ANIM_STATUS_CONFUSION); + else if (status & STATUS2_CURSED) + LaunchStatusAnimation(gActiveBattler, B_ANIM_STATUS_CURSED); + else if (status & STATUS2_NIGHTMARE) + LaunchStatusAnimation(gActiveBattler, B_ANIM_STATUS_NIGHTMARE); + else if (status & STATUS2_WRAPPED) + LaunchStatusAnimation(gActiveBattler, B_ANIM_STATUS_WRAPPED); // this animation doesn't actually exist + else // no animation + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].statusAnimActive = 0; + } +} + +#define tBattlerId data[0] + +bool8 TryHandleLaunchBattleTableAnimation(u8 activeBattler, u8 atkBattler, u8 defBattler, u8 tableId, u16 argument) +{ + u8 taskId; + + if (tableId == B_ANIM_CASTFORM_CHANGE && (argument & 0x80)) + { + gBattleMonForms[activeBattler] = (argument & ~(0x80)); + return TRUE; + } + else if (gBattleSpritesDataPtr->battlerData[activeBattler].behindSubstitute + && !ShouldAnimBeDoneRegardlessOfSubsitute(tableId)) + { + return TRUE; + } + else if (gBattleSpritesDataPtr->battlerData[activeBattler].behindSubstitute + && tableId == B_ANIM_SUBSTITUTE_FADE + && gSprites[gBattlerSpriteIds[activeBattler]].invisible) + { + LoadBattleMonGfxAndAnimate(activeBattler, TRUE, gBattlerSpriteIds[activeBattler]); + ClearBehindSubstituteBit(activeBattler); + return TRUE; + } + gBattleAnimAttacker = atkBattler; + gBattleAnimTarget = defBattler; + gBattleSpritesDataPtr->animationData->animArg = argument; + LaunchBattleAnimation(gBattleAnims_General, tableId, FALSE); + taskId = CreateTask(Task_ClearBitWhenBattleTableAnimDone, 10); + gTasks[taskId].tBattlerId = activeBattler; + gBattleSpritesDataPtr->healthBoxesData[gTasks[taskId].tBattlerId].animFromTableActive = 1; + return FALSE; +} + +static void Task_ClearBitWhenBattleTableAnimDone(u8 taskId) +{ + gAnimScriptCallback(); + if (!gAnimScriptActive) + { + gBattleSpritesDataPtr->healthBoxesData[gTasks[taskId].tBattlerId].animFromTableActive = 0; + DestroyTask(taskId); + } +} + +static bool8 ShouldAnimBeDoneRegardlessOfSubsitute(u8 animId) +{ + switch (animId) + { + case B_ANIM_SUBSTITUTE_FADE: + case B_ANIM_RAIN_CONTINUES: + case B_ANIM_SUN_CONTINUES: + case B_ANIM_SANDSTORM_CONTINUES: + case B_ANIM_HAIL_CONTINUES: + case B_ANIM_SNATCH_MOVE: + return TRUE; + default: + return FALSE; + } +} + +void InitAndLaunchSpecialAnimation(u8 activeBattler, u8 atkBattler, u8 defBattler, u8 tableId) +{ + u8 taskId; + + gBattleAnimAttacker = atkBattler; + gBattleAnimTarget = defBattler; + LaunchBattleAnimation(gBattleAnims_Special, tableId, FALSE); + taskId = CreateTask(Task_ClearBitWhenSpecialAnimDone, 10); + gTasks[taskId].tBattlerId = activeBattler; + gBattleSpritesDataPtr->healthBoxesData[gTasks[taskId].tBattlerId].specialAnimActive = 1; +} + +static void Task_ClearBitWhenSpecialAnimDone(u8 taskId) +{ + gAnimScriptCallback(); + if (!gAnimScriptActive) + { + gBattleSpritesDataPtr->healthBoxesData[gTasks[taskId].tBattlerId].specialAnimActive = 0; + DestroyTask(taskId); + } +} + +bool8 IsMoveWithoutAnimation(u16 moveId, u8 animationTurn) +{ + return FALSE; +} + +bool8 mplay_80342A4(u8 battlerId) +{ + u8 zero = 0; + + if (IsSEPlaying()) + { + ++gBattleSpritesDataPtr->healthBoxesData[battlerId].field_8; + if (gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].field_8 < 30) + return TRUE; + m4aMPlayStop(&gMPlayInfo_SE1); + m4aMPlayStop(&gMPlayInfo_SE2); + } + if (zero == 0) + { + gBattleSpritesDataPtr->healthBoxesData[battlerId].field_8 = 0; + return FALSE; + } + else + { + return TRUE; + } +} + +void BattleLoadOpponentMonSpriteGfx(struct Pokemon *mon, u8 battlerId) +{ + u32 monsPersonality, currentPersonality, otId; + u16 species; + u8 position; + u16 paletteOffset; + const void *lzPaletteData; + void *buffer; + + monsPersonality = GetMonData(mon, MON_DATA_PERSONALITY); + if (gBattleSpritesDataPtr->battlerData[battlerId].transformSpecies == SPECIES_NONE) + { + species = GetMonData(mon, MON_DATA_SPECIES); + currentPersonality = monsPersonality; + } + else + { + species = gBattleSpritesDataPtr->battlerData[battlerId].transformSpecies; + currentPersonality = gTransformedPersonalities[battlerId]; + } + otId = GetMonData(mon, MON_DATA_OT_ID); + position = GetBattlerPosition(battlerId); + HandleLoadSpecialPokePic_DontHandleDeoxys(&gMonFrontPicTable[species], + gMonSpritesGfxPtr->sprites[position], + species, currentPersonality); + paletteOffset = 0x100 + battlerId * 16; + if (gBattleSpritesDataPtr->battlerData[battlerId].transformSpecies == SPECIES_NONE) + lzPaletteData = GetMonFrontSpritePal(mon); + else + lzPaletteData = GetMonSpritePalFromSpeciesAndPersonality(species, otId, monsPersonality); + buffer = AllocZeroed(0x400); + LZDecompressWram(lzPaletteData, buffer); + LoadPalette(buffer, paletteOffset, 0x20); + LoadPalette(buffer, 0x80 + battlerId * 16, 0x20); + Free(buffer); + if (species == SPECIES_CASTFORM) + { + paletteOffset = 0x100 + battlerId * 16; + LZDecompressWram(lzPaletteData, gBattleStruct->castformPalette[0]); + LoadPalette(gBattleStruct->castformPalette[gBattleMonForms[battlerId]], paletteOffset, 0x20); + } + // transform's pink color + if (gBattleSpritesDataPtr->battlerData[battlerId].transformSpecies != SPECIES_NONE) + { + BlendPalette(paletteOffset, 16, 6, RGB_WHITE); + CpuCopy32(gPlttBufferFaded + paletteOffset, gPlttBufferUnfaded + paletteOffset, 32); + } +} + +void BattleLoadPlayerMonSpriteGfx(struct Pokemon *mon, u8 battlerId) +{ + u32 monsPersonality, currentPersonality, otId; + u16 species; + u8 position; + u16 paletteOffset; + const void *lzPaletteData; + void *buffer; + + monsPersonality = GetMonData(mon, MON_DATA_PERSONALITY); + if (gBattleSpritesDataPtr->battlerData[battlerId].transformSpecies == SPECIES_NONE) + { + species = GetMonData(mon, MON_DATA_SPECIES); + currentPersonality = monsPersonality; + } + else + { + species = gBattleSpritesDataPtr->battlerData[battlerId].transformSpecies; + currentPersonality = gTransformedPersonalities[battlerId]; + } + otId = GetMonData(mon, MON_DATA_OT_ID); + position = GetBattlerPosition(battlerId); + if (sub_804455C(1, battlerId) == 1 || gBattleSpritesDataPtr->battlerData[battlerId].transformSpecies != SPECIES_NONE) + HandleLoadSpecialPokePic_DontHandleDeoxys(&gMonBackPicTable[species], + gMonSpritesGfxPtr->sprites[position], + species, currentPersonality); + else + HandleLoadSpecialPokePic(&gMonBackPicTable[species], + gMonSpritesGfxPtr->sprites[position], + species, currentPersonality); + paletteOffset = 0x100 + battlerId * 16; + if (gBattleSpritesDataPtr->battlerData[battlerId].transformSpecies == SPECIES_NONE) + lzPaletteData = GetMonFrontSpritePal(mon); + else + lzPaletteData = GetMonSpritePalFromSpeciesAndPersonality(species, otId, monsPersonality); + buffer = AllocZeroed(0x400); + LZDecompressWram(lzPaletteData, buffer); + LoadPalette(buffer, paletteOffset, 0x20); + LoadPalette(buffer, 0x80 + battlerId * 16, 0x20); + Free(buffer); + if (species == SPECIES_CASTFORM) + { + paletteOffset = 0x100 + battlerId * 16; + LZDecompressWram(lzPaletteData, gBattleStruct->castformPalette[0]); + LoadPalette(gBattleStruct->castformPalette[gBattleMonForms[battlerId]], paletteOffset, 0x20); + } + // transform's pink color + if (gBattleSpritesDataPtr->battlerData[battlerId].transformSpecies != SPECIES_NONE) + { + BlendPalette(paletteOffset, 16, 6, RGB_WHITE); + CpuCopy32(gPlttBufferFaded + paletteOffset, gPlttBufferUnfaded + paletteOffset, 32); + } +} + +void DecompressGhostFrontPic(struct Pokemon *unused, u8 battlerId) +{ + u16 palOffset; + void *buffer; + u8 position = GetBattlerPosition(battlerId); + + LZ77UnCompWram(gGhostFrontPic, gMonSpritesGfxPtr->sprites[position]); + palOffset = 0x100 + 16 * battlerId; + buffer = AllocZeroed(0x400); + LZDecompressWram(gGhostPalette, buffer); + LoadPalette(buffer, palOffset, 0x20); + LoadPalette(buffer, 0x80 + 16 * battlerId, 0x20); + Free(buffer); +} + +void DecompressTrainerFrontPic(u16 frontPicId, u8 battlerId) +{ + struct SpriteSheet sheet; + u8 position = GetBattlerPosition(battlerId); + + DecompressPicFromTable(&gTrainerFrontPicTable[frontPicId], gMonSpritesGfxPtr->sprites[position], SPECIES_NONE); + sheet.data = gMonSpritesGfxPtr->sprites[position]; + sheet.size = gTrainerFrontPicTable[frontPicId].size; + sheet.tag = gTrainerFrontPicTable[frontPicId].tag; + LoadSpriteSheet(&sheet); + LoadCompressedSpritePaletteUsingHeap(&gTrainerFrontPicPaletteTable[frontPicId]); +} + +void DecompressTrainerBackPalette(u16 index, u8 palette) +{ + LoadCompressedPalette(gTrainerBackPicPaletteTable[index].data, (palette + 16) * 16, 0x20); +} + +void nullsub_16(u8 a1) +{ +} + +void FreeTrainerFrontPicPaletteAndTile(u16 frontPicId) +{ + FreeSpritePaletteByTag(gTrainerFrontPicPaletteTable[frontPicId].tag); + FreeSpriteTilesByTag(gTrainerFrontPicTable[frontPicId].tag); +} + +// not used +static void BattleLoadAllHealthBoxesGfxAtOnce(void) +{ + u8 numberOfBattlers = 0; + u8 i; + + LoadSpritePalette(&sSpritePalettes_HealthBoxHealthBar[0]); + LoadSpritePalette(&sSpritePalettes_HealthBoxHealthBar[1]); + if (!IsDoubleBattle()) + { + LoadCompressedSpriteSheetUsingHeap(&sSpriteSheet_SinglesPlayerHealthbox); + LoadCompressedSpriteSheetUsingHeap(&sSpriteSheet_SinglesOpponentHealthbox); + numberOfBattlers = 2; + } + else + { + LoadCompressedSpriteSheetUsingHeap(&sSpriteSheets_DoublesPlayerHealthbox[0]); + LoadCompressedSpriteSheetUsingHeap(&sSpriteSheets_DoublesPlayerHealthbox[1]); + LoadCompressedSpriteSheetUsingHeap(&sSpriteSheets_DoublesOpponentHealthbox[0]); + LoadCompressedSpriteSheetUsingHeap(&sSpriteSheets_DoublesOpponentHealthbox[1]); + numberOfBattlers = MAX_BATTLERS_COUNT; + } + for (i = 0; i < numberOfBattlers; ++i) + LoadCompressedSpriteSheetUsingHeap(&sSpriteSheets_HealthBar[gBattlerPositions[i]]); +} + +bool8 BattleLoadAllHealthBoxesGfx(u8 state) +{ + bool8 retVal = FALSE; + + if (state) + { + if (state == 1) + { + LoadSpritePalette(&sSpritePalettes_HealthBoxHealthBar[0]); + LoadSpritePalette(&sSpritePalettes_HealthBoxHealthBar[1]); + } + else if (!IsDoubleBattle()) + { + if (state == 2) + { + if (gBattleTypeFlags & BATTLE_TYPE_SAFARI) + LoadCompressedSpriteSheetUsingHeap(&sSpriteSheet_SafariHealthbox); + else + LoadCompressedSpriteSheetUsingHeap(&sSpriteSheet_SinglesPlayerHealthbox); + } + else if (state == 3) + LoadCompressedSpriteSheetUsingHeap(&sSpriteSheet_SinglesOpponentHealthbox); + else if (state == 4) + LoadCompressedSpriteSheetUsingHeap(&sSpriteSheets_HealthBar[gBattlerPositions[0]]); + else if (state == 5) + LoadCompressedSpriteSheetUsingHeap(&sSpriteSheets_HealthBar[gBattlerPositions[1]]); + else + retVal = TRUE; + } + else + { + if (state == 2) + LoadCompressedSpriteSheetUsingHeap(&sSpriteSheets_DoublesPlayerHealthbox[0]); + else if (state == 3) + LoadCompressedSpriteSheetUsingHeap(&sSpriteSheets_DoublesPlayerHealthbox[1]); + else if (state == 4) + LoadCompressedSpriteSheetUsingHeap(&sSpriteSheets_DoublesOpponentHealthbox[0]); + else if (state == 5) + LoadCompressedSpriteSheetUsingHeap(&sSpriteSheets_DoublesOpponentHealthbox[1]); + else if (state == 6) + LoadCompressedSpriteSheetUsingHeap(&sSpriteSheets_HealthBar[gBattlerPositions[0]]); + else if (state == 7) + LoadCompressedSpriteSheetUsingHeap(&sSpriteSheets_HealthBar[gBattlerPositions[1]]); + else if (state == 8) + LoadCompressedSpriteSheetUsingHeap(&sSpriteSheets_HealthBar[gBattlerPositions[2]]); + else if (state == 9) + LoadCompressedSpriteSheetUsingHeap(&sSpriteSheets_HealthBar[gBattlerPositions[3]]); + else + retVal = TRUE; + } + } + return retVal; +} + +void LoadBattleBarGfx(u8 arg0) +{ + LZDecompressWram(gFile_graphics_interface_hp_numbers, gMonSpritesGfxPtr->barFontGfx); +} + +bool8 BattleInitAllSprites(u8 *state, u8 *battlerId) +{ + bool8 retVal = FALSE; + + switch (*state) + { + case 0: + ClearSpritesBattlerHealthboxAnimData(); + ++*state; + break; + case 1: + if (!BattleLoadAllHealthBoxesGfx(*battlerId)) + { + ++*battlerId; + } + else + { + *battlerId = 0; + ++*state; + } + break; + case 2: + ++*state; + break; + case 3: + if ((gBattleTypeFlags & BATTLE_TYPE_SAFARI) && *battlerId == 0) + gHealthboxSpriteIds[*battlerId] = CreateSafariPlayerHealthboxSprites(); + else + gHealthboxSpriteIds[*battlerId] = CreateBattlerHealthboxSprites(*battlerId); + + ++*battlerId; + if (*battlerId == gBattlersCount) + { + *battlerId = 0; + ++*state; + } + break; + case 4: + InitBattlerHealthboxCoords(*battlerId); + if (gBattlerPositions[*battlerId] <= 1) + DummyBattleInterfaceFunc(gHealthboxSpriteIds[*battlerId], FALSE); + else + DummyBattleInterfaceFunc(gHealthboxSpriteIds[*battlerId], TRUE); + + ++*battlerId; + if (*battlerId == gBattlersCount) + { + *battlerId = 0; + ++*state; + } + break; + case 5: + if (GetBattlerSide(*battlerId) == B_SIDE_PLAYER) + { + if (!(gBattleTypeFlags & BATTLE_TYPE_SAFARI)) + UpdateHealthboxAttribute(gHealthboxSpriteIds[*battlerId], &gPlayerParty[gBattlerPartyIndexes[*battlerId]], HEALTHBOX_ALL); + } + else + { + UpdateHealthboxAttribute(gHealthboxSpriteIds[*battlerId], &gEnemyParty[gBattlerPartyIndexes[*battlerId]], HEALTHBOX_ALL); + } + SetHealthboxSpriteInvisible(gHealthboxSpriteIds[*battlerId]); + ++*battlerId; + if (*battlerId == gBattlersCount) + { + *battlerId = 0; + ++*state; + } + break; + case 6: + LoadAndCreateEnemyShadowSprites(); + sub_8127CAC(); + retVal = TRUE; + break; + } + return retVal; +} + +void ClearSpritesHealthboxAnimData(void) +{ + memset(gBattleSpritesDataPtr->healthBoxesData, 0, sizeof(struct BattleHealthboxInfo) * MAX_BATTLERS_COUNT); + memset(gBattleSpritesDataPtr->animationData, 0, sizeof(struct BattleAnimationInfo)); +} + +static void ClearSpritesBattlerHealthboxAnimData(void) +{ + ClearSpritesHealthboxAnimData(); + memset(gBattleSpritesDataPtr->battlerData, 0, sizeof(struct BattleSpriteInfo) * MAX_BATTLERS_COUNT); +} + +void CopyAllBattleSpritesInvisibilities(void) +{ + s32 i; + + for (i = 0; i < gBattlersCount; ++i) + gBattleSpritesDataPtr->battlerData[i].invisible = gSprites[gBattlerSpriteIds[i]].invisible; +} + +void CopyBattleSpriteInvisibility(u8 battlerId) +{ + gBattleSpritesDataPtr->battlerData[battlerId].invisible = gSprites[gBattlerSpriteIds[battlerId]].invisible; +} + +void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, u8 notTransform) +{ + u16 paletteOffset, targetSpecies; + u32 personalityValue; + u32 otId; + u8 position; + const u32 *lzPaletteData; + void *buffer; + + //TODO: notTransform is bool8 in pokeem. Document it with a more reasonable name here. + if (notTransform == 255) + { + const void *src; + void *dst; + + position = GetBattlerPosition(battlerAtk); + targetSpecies = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerAtk]], MON_DATA_SPECIES); + personalityValue = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerAtk]], MON_DATA_PERSONALITY); + otId = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerAtk]], MON_DATA_OT_ID); + HandleLoadSpecialPokePic_DontHandleDeoxys(&gMonFrontPicTable[targetSpecies], + gMonSpritesGfxPtr->sprites[position], + targetSpecies, + personalityValue); + src = gMonSpritesGfxPtr->sprites[position]; + dst = (void *)(VRAM + 0x10000 + gSprites[gBattlerSpriteIds[battlerAtk]].oam.tileNum * 32); + DmaCopy32(3, src, dst, 0x800); + paletteOffset = 0x100 + battlerAtk * 16; + lzPaletteData = GetMonSpritePalFromSpeciesAndPersonality(targetSpecies, otId, personalityValue); + buffer = AllocZeroed(0x400); + LZDecompressWram(lzPaletteData, buffer); + LoadPalette(buffer, paletteOffset, 32); + Free(buffer); + gSprites[gBattlerSpriteIds[battlerAtk]].pos1.y = GetBattlerSpriteDefault_Y(battlerAtk); + StartSpriteAnim(&gSprites[gBattlerSpriteIds[battlerAtk]], gBattleMonForms[battlerAtk]); + SetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerAtk]], MON_DATA_NICKNAME, gSpeciesNames[targetSpecies]); + sub_80496C0(gHealthboxSpriteIds[battlerAtk], &gEnemyParty[gBattlerPartyIndexes[battlerAtk]]); + sub_804981C(gHealthboxSpriteIds[battlerAtk], 1); + } + else if (notTransform) + { + StartSpriteAnim(&gSprites[gBattlerSpriteIds[battlerAtk]], gBattleSpritesDataPtr->animationData->animArg); + paletteOffset = 0x100 + battlerAtk * 16; + LoadPalette(gBattleStruct->castformPalette[gBattleSpritesDataPtr->animationData->animArg], paletteOffset, 32); + gBattleMonForms[battlerAtk] = gBattleSpritesDataPtr->animationData->animArg; + if (gBattleSpritesDataPtr->battlerData[battlerAtk].transformSpecies != SPECIES_NONE) + { + BlendPalette(paletteOffset, 16, 6, RGB_WHITE); + CpuCopy32(gPlttBufferFaded + paletteOffset, gPlttBufferUnfaded + paletteOffset, 32); + } + gSprites[gBattlerSpriteIds[battlerAtk]].pos1.y = GetBattlerSpriteDefault_Y(battlerAtk); + } + else + { + const void *src; + void *dst; + + position = GetBattlerPosition(battlerAtk); + if (GetBattlerSide(battlerDef) == B_SIDE_OPPONENT) + targetSpecies = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerDef]], MON_DATA_SPECIES); + else + targetSpecies = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerDef]], MON_DATA_SPECIES); + if (GetBattlerSide(battlerAtk) == B_SIDE_PLAYER) + { + personalityValue = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerAtk]], MON_DATA_PERSONALITY); + otId = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerAtk]], MON_DATA_OT_ID); + + HandleLoadSpecialPokePic_DontHandleDeoxys(&gMonBackPicTable[targetSpecies], + gMonSpritesGfxPtr->sprites[position], + targetSpecies, + gTransformedPersonalities[battlerAtk]); + } + else + { + personalityValue = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerAtk]], MON_DATA_PERSONALITY); + otId = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerAtk]], MON_DATA_OT_ID); + + HandleLoadSpecialPokePic_DontHandleDeoxys(&gMonFrontPicTable[targetSpecies], + gMonSpritesGfxPtr->sprites[position], + targetSpecies, + gTransformedPersonalities[battlerAtk]); + } + src = gMonSpritesGfxPtr->sprites[position]; + dst = (void *)(VRAM + 0x10000 + gSprites[gBattlerSpriteIds[battlerAtk]].oam.tileNum * 32); + DmaCopy32(3, src, dst, 0x800); + paletteOffset = 0x100 + battlerAtk * 16; + lzPaletteData = GetMonSpritePalFromSpeciesAndPersonality(targetSpecies, otId, personalityValue); + buffer = AllocZeroed(0x400); + LZDecompressWram(lzPaletteData, buffer); + LoadPalette(buffer, paletteOffset, 32); + Free(buffer); + if (targetSpecies == SPECIES_CASTFORM) + { + LZDecompressWram(lzPaletteData, gBattleStruct->castformPalette[0]); + LoadPalette(gBattleStruct->castformPalette[0] + gBattleMonForms[battlerDef] * 16, paletteOffset, 32); + } + BlendPalette(paletteOffset, 16, 6, RGB_WHITE); + CpuCopy32(gPlttBufferFaded + paletteOffset, gPlttBufferUnfaded + paletteOffset, 32); + gBattleSpritesDataPtr->battlerData[battlerAtk].transformSpecies = targetSpecies; + gBattleMonForms[battlerAtk] = gBattleMonForms[battlerDef]; + gSprites[gBattlerSpriteIds[battlerAtk]].pos1.y = GetBattlerSpriteDefault_Y(battlerAtk); + StartSpriteAnim(&gSprites[gBattlerSpriteIds[battlerAtk]], gBattleMonForms[battlerAtk]); + } +} + +void BattleLoadSubstituteOrMonSpriteGfx(u8 battlerId, bool8 loadMonSprite) +{ + u8 position; + s32 i; + u32 palOffset; + + if (!loadMonSprite) + { + position = GetBattlerPosition(battlerId); + if (GetBattlerSide(battlerId) != B_SIDE_PLAYER) + LZDecompressVram(gSubstituteDollGfx, gMonSpritesGfxPtr->sprites[position]); + else + LZDecompressVram(gSubstituteDollTilemap, gMonSpritesGfxPtr->sprites[position]); + for (i = 1; i < 4; ++i) + { + u8 (*ptr)[4][0x800] = gMonSpritesGfxPtr->sprites[position]; + + ++ptr; + --ptr; + DmaCopy32Defvars(3, (*ptr)[0], (*ptr)[i], 0x800); + } + palOffset = (battlerId * 16) + 0x100; + LoadCompressedPalette(gSubstituteDollPal, palOffset, 32); + } + else + { + if (GetBattlerSide(battlerId) != B_SIDE_PLAYER) + BattleLoadOpponentMonSpriteGfx(&gEnemyParty[gBattlerPartyIndexes[battlerId]], battlerId); + else + BattleLoadPlayerMonSpriteGfx(&gPlayerParty[gBattlerPartyIndexes[battlerId]], battlerId); + } +} + +void LoadBattleMonGfxAndAnimate(u8 battlerId, bool8 loadMonSprite, u8 spriteId) +{ + BattleLoadSubstituteOrMonSpriteGfx(battlerId, loadMonSprite); + StartSpriteAnim(&gSprites[spriteId], gBattleMonForms[battlerId]); + if (!loadMonSprite) + gSprites[spriteId].pos1.y = GetSubstituteSpriteDefault_Y(battlerId); + else + gSprites[spriteId].pos1.y = GetBattlerSpriteDefault_Y(battlerId); +} + +void TrySetBehindSubstituteSpriteBit(u8 battlerId, u16 move) +{ + if (move == MOVE_SUBSTITUTE) + gBattleSpritesDataPtr->battlerData[battlerId].behindSubstitute = 1; +} + +void ClearBehindSubstituteBit(u8 battlerId) +{ + gBattleSpritesDataPtr->battlerData[battlerId].behindSubstitute = 0; +} + +void HandleLowHpMusicChange(struct Pokemon *mon, u8 battlerId) +{ + u16 hp = GetMonData(mon, MON_DATA_HP); + u16 maxHP = GetMonData(mon, MON_DATA_MAX_HP); + + if (GetHPBarLevel(hp, maxHP) == HP_BAR_RED) + { + if (!gBattleSpritesDataPtr->battlerData[battlerId].lowHpSong) + { + if (!gBattleSpritesDataPtr->battlerData[battlerId ^ BIT_FLANK].lowHpSong) + PlaySE(SE_T_OOAME); + gBattleSpritesDataPtr->battlerData[battlerId].lowHpSong = 1; + } + } + else + { + gBattleSpritesDataPtr->battlerData[battlerId].lowHpSong = 0; + if (!IsDoubleBattle()) + m4aSongNumStop(SE_T_OOAME); + else if (IsDoubleBattle() && !gBattleSpritesDataPtr->battlerData[battlerId ^ BIT_FLANK].lowHpSong) + m4aSongNumStop(SE_T_OOAME); + } +} + +void BattleStopLowHpSound(void) +{ + u8 playerBattler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); + + gBattleSpritesDataPtr->battlerData[playerBattler].lowHpSong = 0; + if (IsDoubleBattle()) + gBattleSpritesDataPtr->battlerData[playerBattler ^ BIT_FLANK].lowHpSong = 0; + m4aSongNumStop(SE_T_OOAME); +} + +// not used +static u8 GetMonHPBarLevel(struct Pokemon *mon) +{ + u16 hp = GetMonData(mon, MON_DATA_HP); + u16 maxHP = GetMonData(mon, MON_DATA_MAX_HP); + + return GetHPBarLevel(hp, maxHP); +} + +void HandleBattleLowHpMusicChange(void) +{ + if (gMain.inBattle) + { + u8 playerBattler1 = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); + u8 playerBattler2 = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT); + u8 battler1PartyId = pokemon_order_func(gBattlerPartyIndexes[playerBattler1]); + u8 battler2PartyId = pokemon_order_func(gBattlerPartyIndexes[playerBattler2]); + + if (GetMonData(&gPlayerParty[battler1PartyId], MON_DATA_HP) != 0) + HandleLowHpMusicChange(&gPlayerParty[battler1PartyId], playerBattler1); + if (IsDoubleBattle() && GetMonData(&gPlayerParty[battler2PartyId], MON_DATA_HP) != 0) + HandleLowHpMusicChange(&gPlayerParty[battler2PartyId], playerBattler2); + } +} + +void sub_8035450(u8 affineMode) +{ + s32 i; + + for (i = 0; i < gBattlersCount; ++i) + { + if (IsBattlerSpritePresent(i)) + { + gSprites[gBattlerSpriteIds[i]].oam.affineMode = affineMode; + if (affineMode == ST_OAM_AFFINE_OFF) + { + gBattleSpritesDataPtr->healthBoxesData[i].matrixNum = gSprites[gBattlerSpriteIds[i]].oam.matrixNum; + gSprites[gBattlerSpriteIds[i]].oam.matrixNum = 0; + } + else + { + gSprites[gBattlerSpriteIds[i]].oam.matrixNum = gBattleSpritesDataPtr->healthBoxesData[i].matrixNum; + } + } + } +} + +void LoadAndCreateEnemyShadowSprites(void) +{ + u8 battlerId; + + LoadCompressedSpriteSheetUsingHeap(&gSpriteSheet_EnemyShadow); + battlerId = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); + gBattleSpritesDataPtr->healthBoxesData[battlerId].shadowSpriteId = CreateSprite(&gSpriteTemplate_EnemyShadow, GetBattlerSpriteCoord(battlerId, 0), GetBattlerSpriteCoord(battlerId, 1) + 29, 0xC8); + gSprites[gBattleSpritesDataPtr->healthBoxesData[battlerId].shadowSpriteId].data[0] = battlerId; + if (IsDoubleBattle()) + { + battlerId = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); + gBattleSpritesDataPtr->healthBoxesData[battlerId].shadowSpriteId = CreateSprite(&gSpriteTemplate_EnemyShadow, GetBattlerSpriteCoord(battlerId, 0), GetBattlerSpriteCoord(battlerId, 1) + 29, 0xC8); + gSprites[gBattleSpritesDataPtr->healthBoxesData[battlerId].shadowSpriteId].data[0] = battlerId; + } +} + +static void SpriteCB_EnemyShadow(struct Sprite *shadowSprite) +{ + bool8 invisible = FALSE; + u8 battlerId = shadowSprite->tBattlerId; + struct Sprite *battlerSprite = &gSprites[gBattlerSpriteIds[battlerId]]; + + if (!battlerSprite->inUse || !IsBattlerSpritePresent(battlerId)) + { + shadowSprite->callback = SpriteCB_SetInvisible; + return; + } + if (gAnimScriptActive || battlerSprite->invisible) + invisible = TRUE; + else if (gBattleSpritesDataPtr->battlerData[battlerId].transformSpecies != SPECIES_NONE + && gEnemyMonElevation[gBattleSpritesDataPtr->battlerData[battlerId].transformSpecies] == 0) + invisible = TRUE; + if (gBattleSpritesDataPtr->battlerData[battlerId].behindSubstitute) + invisible = TRUE; + shadowSprite->pos1.x = battlerSprite->pos1.x; + shadowSprite->pos2.x = battlerSprite->pos2.x; + shadowSprite->invisible = invisible; +} + +void SpriteCB_SetInvisible(struct Sprite *sprite) +{ + sprite->invisible = TRUE; +} + +void SetBattlerShadowSpriteCallback(u8 battlerId, u16 species) +{ + // The player's shadow is never seen. + if (GetBattlerSide(battlerId) == B_SIDE_PLAYER) + return; + + if (gBattleSpritesDataPtr->battlerData[battlerId].transformSpecies != SPECIES_NONE) + species = gBattleSpritesDataPtr->battlerData[battlerId].transformSpecies; + + if (gEnemyMonElevation[species] != 0) + gSprites[gBattleSpritesDataPtr->healthBoxesData[battlerId].shadowSpriteId].callback = SpriteCB_EnemyShadow; + else + gSprites[gBattleSpritesDataPtr->healthBoxesData[battlerId].shadowSpriteId].callback = SpriteCB_SetInvisible; +} + +void HideBattlerShadowSprite(u8 battlerId) +{ + gSprites[gBattleSpritesDataPtr->healthBoxesData[battlerId].shadowSpriteId].callback = SpriteCB_SetInvisible; +} + +void sub_80357C8(void) +{ + u16 *vramPtr = (u16 *)(VRAM + 0x240); + s32 i; + s32 j; + + for (i = 0; i < 9; ++i) + { + for (j = 0; j < 16; ++vramPtr, ++j) + { + if (!(*vramPtr & 0xF000)) + *vramPtr |= 0xF000; + if (!(*vramPtr & 0x0F00)) + *vramPtr |= 0x0F00; + if (!(*vramPtr & 0x00F0)) + *vramPtr |= 0x00F0; + if (!(*vramPtr & 0x000F)) + *vramPtr |= 0x000F; + } + } + vramPtr = (u16 *)(VRAM + 0x600); + for (i = 0; i < 18; ++i) + { + for (j = 0; j < 16; ++vramPtr, ++j) + { + if (!(*vramPtr & 0xF000)) + *vramPtr |= 0x6000; + if (!(*vramPtr & 0x0F00)) + *vramPtr |= 0x0600; + if (!(*vramPtr & 0x00F0)) + *vramPtr |= 0x0060; + if (!(*vramPtr & 0x000F)) + *vramPtr |= 0x0006; + } + } +} + +void ClearTemporarySpeciesSpriteData(u8 battlerId, bool8 dontClearSubstitute) +{ + gBattleSpritesDataPtr->battlerData[battlerId].transformSpecies = SPECIES_NONE; + gBattleMonForms[battlerId] = 0; + if (!dontClearSubstitute) + ClearBehindSubstituteBit(battlerId); +} + +void AllocateMonSpritesGfx(void) +{ + u8 i = 0, j; + + gMonSpritesGfxPtr = NULL; + gMonSpritesGfxPtr = AllocZeroed(sizeof(*gMonSpritesGfxPtr)); + gMonSpritesGfxPtr->firstDecompressed = AllocZeroed(0x8000); + for (i = 0; i < MAX_BATTLERS_COUNT; ++i) + { + gMonSpritesGfxPtr->sprites[i] = gMonSpritesGfxPtr->firstDecompressed + (i * 0x2000); + *(gMonSpritesGfxPtr->templates + i) = gUnknown_825DEF0[i]; + + for (j = 0; j < 4; ++j) + { + gMonSpritesGfxPtr->field_74[i][j].data = gMonSpritesGfxPtr->sprites[i] + (j * 0x800); + gMonSpritesGfxPtr->field_74[i][j].size = 0x800; + } + + gMonSpritesGfxPtr->templates[i].images = gMonSpritesGfxPtr->field_74[i]; + } + gMonSpritesGfxPtr->barFontGfx = AllocZeroed(0x1000); +} + +void FreeMonSpritesGfx(void) +{ + if (gMonSpritesGfxPtr == NULL) + return; + if (gMonSpritesGfxPtr->field_17C != NULL) + FREE_AND_SET_NULL(gMonSpritesGfxPtr->field_17C); + if (gMonSpritesGfxPtr->field_178 != NULL) + FREE_AND_SET_NULL(gMonSpritesGfxPtr->field_178); + FREE_AND_SET_NULL(gMonSpritesGfxPtr->barFontGfx); + FREE_AND_SET_NULL(gMonSpritesGfxPtr->firstDecompressed); + gMonSpritesGfxPtr->sprites[0] = NULL; + gMonSpritesGfxPtr->sprites[1] = NULL; + gMonSpritesGfxPtr->sprites[2] = NULL; + gMonSpritesGfxPtr->sprites[3] = NULL; + FREE_AND_SET_NULL(gMonSpritesGfxPtr); +} + +bool32 ShouldPlayNormalPokeCry(struct Pokemon *mon) +{ + s16 hp, maxHP; + s32 barLevel; + + if (GetMonData(mon, MON_DATA_STATUS) & (STATUS1_ANY | STATUS1_TOXIC_COUNTER)) + return FALSE; + hp = GetMonData(mon, MON_DATA_HP); + maxHP = GetMonData(mon, MON_DATA_MAX_HP); + barLevel = GetHPBarLevel(hp, maxHP); + if (barLevel <= HP_BAR_YELLOW) + return FALSE; + return TRUE; +} diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c new file mode 100644 index 000000000..f84ff4d4a --- /dev/null +++ b/src/battle_script_commands.c @@ -0,0 +1,9824 @@ +#include "global.h" +#include "item.h" +#include "util.h" +#include "pokemon.h" +#include "random.h" +#include "data.h" +#include "text.h" +#include "sound.h" +#include "pokedex.h" +#include "window.h" +#include "main.h" +#include "palette.h" +#include "money.h" +#include "bg.h" +#include "string_util.h" +#include "pokemon_icon.h" +#include "m4a.h" +#include "mail.h" +#include "event_data.h" +#include "strings.h" +#include "pokemon_special_anim.h" +#include "pokemon_storage_system.h" +#include "pokemon_summary_screen.h" +#include "task.h" +#include "naming_screen.h" +#include "overworld.h" +#include "party_menu.h" +#include "trainer_pokemon_sprites.h" +#include "field_specials.h" +#include "battle.h" +#include "battle_message.h" +#include "battle_anim.h" +#include "battle_setup.h" +#include "battle_ai_script_commands.h" +#include "battle_scripts.h" +#include "battle_string_ids.h" +#include "reshow_battle_screen.h" +#include "battle_controllers.h" +#include "battle_interface.h" +#include "constants/battle_anim.h" +#include "constants/battle_move_effects.h" +#include "constants/battle_script_commands.h" +#include "constants/items.h" +#include "constants/hold_effects.h" +#include "constants/songs.h" +#include "constants/species.h" +#include "constants/moves.h" +#include "constants/abilities.h" +#include "constants/pokemon.h" +#include "constants/trainers.h" + +#define DEFENDER_IS_PROTECTED ((gProtectStructs[gBattlerTarget].protected) && (gBattleMoves[gCurrentMove].flags & FLAG_PROTECT_AFFECTED)) + +extern const u8 *const gBattleScriptsForMoveEffects[]; + +static bool8 IsTwoTurnsMove(u16 move); +static void TrySetDestinyBondToHappen(void); +static u8 AttacksThisTurn(u8 battlerId, u16 move); // Note: returns 1 if it's a charging turn, otherwise 2. +static void CheckWonderGuardAndLevitate(void); +static u8 ChangeStatBuffs(s8 statValue, u8 statId, u8, const u8 *BS_ptr); +static void sub_8026480(void); +static bool8 sub_80264D0(void); +static void DrawLevelUpWindow1(void); +static void DrawLevelUpWindow2(void); +static bool8 sub_8026648(void); +static void PutMonIconOnLvlUpBox(void); +static void PutLevelAndGenderOnLvlUpBox(void); + +static void SpriteCB_MonIconOnLvlUpBox(struct Sprite *sprite); + +static void atk00_attackcanceler(void); +static void atk01_accuracycheck(void); +static void atk02_attackstring(void); +static void atk03_ppreduce(void); +static void atk04_critcalc(void); +static void atk05_damagecalc(void); +static void atk06_typecalc(void); +static void atk07_adjustnormaldamage(void); +static void atk08_adjustnormaldamage2(void); +static void atk09_attackanimation(void); +static void atk0A_waitanimation(void); +static void atk0B_healthbarupdate(void); +static void atk0C_datahpupdate(void); +static void atk0D_critmessage(void); +static void atk0E_effectivenesssound(void); +static void atk0F_resultmessage(void); +static void atk10_printstring(void); +static void atk11_printselectionstring(void); +static void atk12_waitmessage(void); +static void atk13_printfromtable(void); +static void atk14_printselectionstringfromtable(void); +static void atk15_seteffectwithchance(void); +static void atk16_seteffectprimary(void); +static void atk17_seteffectsecondary(void); +static void atk18_clearstatusfromeffect(void); +static void atk19_tryfaintmon(void); +static void atk1A_dofaintanimation(void); +static void atk1B_cleareffectsonfaint(void); +static void atk1C_jumpifstatus(void); +static void atk1D_jumpifstatus2(void); +static void atk1E_jumpifability(void); +static void atk1F_jumpifsideaffecting(void); +static void atk20_jumpifstat(void); +static void atk21_jumpifstatus3condition(void); +static void atk22_jumpiftype(void); +static void atk23_getexp(void); +static void atk24(void); +static void atk25_movevaluescleanup(void); +static void atk26_setmultihit(void); +static void atk27_decrementmultihit(void); +static void atk28_goto(void); +static void atk29_jumpifbyte(void); +static void atk2A_jumpifhalfword(void); +static void atk2B_jumpifword(void); +static void atk2C_jumpifarrayequal(void); +static void atk2D_jumpifarraynotequal(void); +static void atk2E_setbyte(void); +static void atk2F_addbyte(void); +static void atk30_subbyte(void); +static void atk31_copyarray(void); +static void atk32_copyarraywithindex(void); +static void atk33_orbyte(void); +static void atk34_orhalfword(void); +static void atk35_orword(void); +static void atk36_bicbyte(void); +static void atk37_bichalfword(void); +static void atk38_bicword(void); +static void atk39_pause(void); +static void atk3A_waitstate(void); +static void atk3B_healthbar_update(void); +static void atk3C_return(void); +static void atk3D_end(void); +static void atk3E_end2(void); +static void atk3F_end3(void); +static void atk40_jumpifaffectedbyprotect(void); +static void atk41_call(void); +static void atk42_jumpiftype2(void); +static void atk43_jumpifabilitypresent(void); +static void atk44_endselectionscript(void); +static void atk45_playanimation(void); +static void atk46_playanimation2(void); +static void atk47_setgraphicalstatchangevalues(void); +static void atk48_playstatchangeanimation(void); +static void atk49_moveend(void); +static void atk4A_typecalc2(void); +static void atk4B_returnatktoball(void); +static void atk4C_getswitchedmondata(void); +static void atk4D_switchindataupdate(void); +static void atk4E_switchinanim(void); +static void atk4F_jumpifcantswitch(void); +static void atk50_openpartyscreen(void); +static void atk51_switchhandleorder(void); +static void atk52_switchineffects(void); +static void atk53_trainerslidein(void); +static void atk54_playse(void); +static void atk55_fanfare(void); +static void atk56_playfaintcry(void); +static void atk57(void); +static void atk58_returntoball(void); +static void atk59_handlelearnnewmove(void); +static void atk5A_yesnoboxlearnmove(void); +static void atk5B_yesnoboxstoplearningmove(void); +static void atk5C_hitanimation(void); +static void atk5D_getmoneyreward(void); +static void atk5E(void); +static void atk5F_swapattackerwithtarget(void); +static void atk60_incrementgamestat(void); +static void atk61_drawpartystatussummary(void); +static void atk62_hidepartystatussummary(void); +static void atk63_jumptocalledmove(void); +static void atk64_statusanimation(void); +static void atk65_status2animation(void); +static void atk66_chosenstatusanimation(void); +static void atk67_yesnobox(void); +static void atk68_cancelallactions(void); +static void atk69_adjustsetdamage(void); +static void atk6A_removeitem(void); +static void atk6B_atknameinbuff1(void); +static void atk6C_drawlvlupbox(void); +static void atk6D_resetsentmonsvalue(void); +static void atk6E_setatktoplayer0(void); +static void atk6F_makevisible(void); +static void atk70_recordlastability(void); +static void atk71_buffermovetolearn(void); +static void atk72_jumpifplayerran(void); +static void atk73_hpthresholds(void); +static void atk74_hpthresholds2(void); +static void atk75_useitemonopponent(void); +static void atk76_various(void); +static void atk77_setprotectlike(void); +static void atk78_faintifabilitynotdamp(void); +static void atk79_setatkhptozero(void); +static void atk7A_jumpifnexttargetvalid(void); +static void atk7B_tryhealhalfhealth(void); +static void atk7C_trymirrormove(void); +static void atk7D_setrain(void); +static void atk7E_setreflect(void); +static void atk7F_setseeded(void); +static void atk80_manipulatedamage(void); +static void atk81_trysetrest(void); +static void atk82_jumpifnotfirstturn(void); +static void atk83_nop(void); +static void atk84_jumpifcantmakeasleep(void); +static void atk85_stockpile(void); +static void atk86_stockpiletobasedamage(void); +static void atk87_stockpiletohpheal(void); +static void atk88_negativedamage(void); +static void atk89_statbuffchange(void); +static void atk8A_normalisebuffs(void); +static void atk8B_setbide(void); +static void atk8C_confuseifrepeatingattackends(void); +static void atk8D_setmultihitcounter(void); +static void atk8E_initmultihitstring(void); +static void atk8F_forcerandomswitch(void); +static void atk90_tryconversiontypechange(void); +static void atk91_givepaydaymoney(void); +static void atk92_setlightscreen(void); +static void atk93_tryKO(void); +static void atk94_damagetohalftargethp(void); +static void atk95_setsandstorm(void); +static void atk96_weatherdamage(void); +static void atk97_tryinfatuating(void); +static void atk98_updatestatusicon(void); +static void atk99_setmist(void); +static void atk9A_setfocusenergy(void); +static void atk9B_transformdataexecution(void); +static void atk9C_setsubstitute(void); +static void atk9D_mimicattackcopy(void); +static void atk9E_metronome(void); +static void atk9F_dmgtolevel(void); +static void atkA0_psywavedamageeffect(void); +static void atkA1_counterdamagecalculator(void); +static void atkA2_mirrorcoatdamagecalculator(void); +static void atkA3_disablelastusedattack(void); +static void atkA4_trysetencore(void); +static void atkA5_painsplitdmgcalc(void); +static void atkA6_settypetorandomresistance(void); +static void atkA7_setalwayshitflag(void); +static void atkA8_copymovepermanently(void); +static void atkA9_trychoosesleeptalkmove(void); +static void atkAA_setdestinybond(void); +static void atkAB_trysetdestinybondtohappen(void); +static void atkAC_remaininghptopower(void); +static void atkAD_tryspiteppreduce(void); +static void atkAE_healpartystatus(void); +static void atkAF_cursetarget(void); +static void atkB0_trysetspikes(void); +static void atkB1_setforesight(void); +static void atkB2_trysetperishsong(void); +static void atkB3_rolloutdamagecalculation(void); +static void atkB4_jumpifconfusedandstatmaxed(void); +static void atkB5_furycuttercalc(void); +static void atkB6_happinesstodamagecalculation(void); +static void atkB7_presentdamagecalculation(void); +static void atkB8_setsafeguard(void); +static void atkB9_magnitudedamagecalculation(void); +static void atkBA_jumpifnopursuitswitchdmg(void); +static void atkBB_setsunny(void); +static void atkBC_maxattackhalvehp(void); +static void atkBD_copyfoestats(void); +static void atkBE_rapidspinfree(void); +static void atkBF_setdefensecurlbit(void); +static void atkC0_recoverbasedonsunlight(void); +static void atkC1_hiddenpowercalc(void); +static void atkC2_selectfirstvalidtarget(void); +static void atkC3_trysetfutureattack(void); +static void atkC4_trydobeatup(void); +static void atkC5_setsemiinvulnerablebit(void); +static void atkC6_clearsemiinvulnerablebit(void); +static void atkC7_setminimize(void); +static void atkC8_sethail(void); +static void atkC9_jumpifattackandspecialattackcannotfall(void); +static void atkCA_setforcedtarget(void); +static void atkCB_setcharge(void); +static void atkCC_callterrainattack(void); +static void atkCD_cureifburnedparalysedorpoisoned(void); +static void atkCE_settorment(void); +static void atkCF_jumpifnodamage(void); +static void atkD0_settaunt(void); +static void atkD1_trysethelpinghand(void); +static void atkD2_tryswapitems(void); +static void atkD3_trycopyability(void); +static void atkD4_trywish(void); +static void atkD5_trysetroots(void); +static void atkD6_doubledamagedealtifdamaged(void); +static void atkD7_setyawn(void); +static void atkD8_setdamagetohealthdifference(void); +static void atkD9_scaledamagebyhealthratio(void); +static void atkDA_tryswapabilities(void); +static void atkDB_tryimprison(void); +static void atkDC_trysetgrudge(void); +static void atkDD_weightdamagecalculation(void); +static void atkDE_assistattackselect(void); +static void atkDF_trysetmagiccoat(void); +static void atkE0_trysetsnatch(void); +static void atkE1_trygetintimidatetarget(void); +static void atkE2_switchoutabilities(void); +static void atkE3_jumpifhasnohp(void); +static void atkE4_getsecretpowereffect(void); +static void atkE5_pickup(void); +static void atkE6_docastformchangeanimation(void); +static void atkE7_trycastformdatachange(void); +static void atkE8_settypebasedhalvers(void); +static void atkE9_setweatherballtype(void); +static void atkEA_tryrecycleitem(void); +static void atkEB_settypetoterrain(void); +static void atkEC_pursuitrelated(void); +static void atkED_snatchsetbattlers(void); +static void atkEE_removelightscreenreflect(void); +static void atkEF_handleballthrow(void); +static void atkF0_givecaughtmon(void); +static void atkF1_trysetcaughtmondexflags(void); +static void atkF2_displaydexinfo(void); +static void atkF3_trygivecaughtmonnick(void); +static void atkF4_subattackerhpbydmg(void); +static void atkF5_removeattackerstatus1(void); +static void atkF6_finishaction(void); +static void atkF7_finishturn(void); + +void (* const gBattleScriptingCommandsTable[])(void) = +{ + atk00_attackcanceler, + atk01_accuracycheck, + atk02_attackstring, + atk03_ppreduce, + atk04_critcalc, + atk05_damagecalc, + atk06_typecalc, + atk07_adjustnormaldamage, + atk08_adjustnormaldamage2, + atk09_attackanimation, + atk0A_waitanimation, + atk0B_healthbarupdate, + atk0C_datahpupdate, + atk0D_critmessage, + atk0E_effectivenesssound, + atk0F_resultmessage, + atk10_printstring, + atk11_printselectionstring, + atk12_waitmessage, + atk13_printfromtable, + atk14_printselectionstringfromtable, + atk15_seteffectwithchance, + atk16_seteffectprimary, + atk17_seteffectsecondary, + atk18_clearstatusfromeffect, + atk19_tryfaintmon, + atk1A_dofaintanimation, + atk1B_cleareffectsonfaint, + atk1C_jumpifstatus, + atk1D_jumpifstatus2, + atk1E_jumpifability, + atk1F_jumpifsideaffecting, + atk20_jumpifstat, + atk21_jumpifstatus3condition, + atk22_jumpiftype, + atk23_getexp, + atk24, + atk25_movevaluescleanup, + atk26_setmultihit, + atk27_decrementmultihit, + atk28_goto, + atk29_jumpifbyte, + atk2A_jumpifhalfword, + atk2B_jumpifword, + atk2C_jumpifarrayequal, + atk2D_jumpifarraynotequal, + atk2E_setbyte, + atk2F_addbyte, + atk30_subbyte, + atk31_copyarray, + atk32_copyarraywithindex, + atk33_orbyte, + atk34_orhalfword, + atk35_orword, + atk36_bicbyte, + atk37_bichalfword, + atk38_bicword, + atk39_pause, + atk3A_waitstate, + atk3B_healthbar_update, + atk3C_return, + atk3D_end, + atk3E_end2, + atk3F_end3, + atk40_jumpifaffectedbyprotect, + atk41_call, + atk42_jumpiftype2, + atk43_jumpifabilitypresent, + atk44_endselectionscript, + atk45_playanimation, + atk46_playanimation2, + atk47_setgraphicalstatchangevalues, + atk48_playstatchangeanimation, + atk49_moveend, + atk4A_typecalc2, + atk4B_returnatktoball, + atk4C_getswitchedmondata, + atk4D_switchindataupdate, + atk4E_switchinanim, + atk4F_jumpifcantswitch, + atk50_openpartyscreen, + atk51_switchhandleorder, + atk52_switchineffects, + atk53_trainerslidein, + atk54_playse, + atk55_fanfare, + atk56_playfaintcry, + atk57, + atk58_returntoball, + atk59_handlelearnnewmove, + atk5A_yesnoboxlearnmove, + atk5B_yesnoboxstoplearningmove, + atk5C_hitanimation, + atk5D_getmoneyreward, + atk5E, + atk5F_swapattackerwithtarget, + atk60_incrementgamestat, + atk61_drawpartystatussummary, + atk62_hidepartystatussummary, + atk63_jumptocalledmove, + atk64_statusanimation, + atk65_status2animation, + atk66_chosenstatusanimation, + atk67_yesnobox, + atk68_cancelallactions, + atk69_adjustsetdamage, + atk6A_removeitem, + atk6B_atknameinbuff1, + atk6C_drawlvlupbox, + atk6D_resetsentmonsvalue, + atk6E_setatktoplayer0, + atk6F_makevisible, + atk70_recordlastability, + atk71_buffermovetolearn, + atk72_jumpifplayerran, + atk73_hpthresholds, + atk74_hpthresholds2, + atk75_useitemonopponent, + atk76_various, + atk77_setprotectlike, + atk78_faintifabilitynotdamp, + atk79_setatkhptozero, + atk7A_jumpifnexttargetvalid, + atk7B_tryhealhalfhealth, + atk7C_trymirrormove, + atk7D_setrain, + atk7E_setreflect, + atk7F_setseeded, + atk80_manipulatedamage, + atk81_trysetrest, + atk82_jumpifnotfirstturn, + atk83_nop, + atk84_jumpifcantmakeasleep, + atk85_stockpile, + atk86_stockpiletobasedamage, + atk87_stockpiletohpheal, + atk88_negativedamage, + atk89_statbuffchange, + atk8A_normalisebuffs, + atk8B_setbide, + atk8C_confuseifrepeatingattackends, + atk8D_setmultihitcounter, + atk8E_initmultihitstring, + atk8F_forcerandomswitch, + atk90_tryconversiontypechange, + atk91_givepaydaymoney, + atk92_setlightscreen, + atk93_tryKO, + atk94_damagetohalftargethp, + atk95_setsandstorm, + atk96_weatherdamage, + atk97_tryinfatuating, + atk98_updatestatusicon, + atk99_setmist, + atk9A_setfocusenergy, + atk9B_transformdataexecution, + atk9C_setsubstitute, + atk9D_mimicattackcopy, + atk9E_metronome, + atk9F_dmgtolevel, + atkA0_psywavedamageeffect, + atkA1_counterdamagecalculator, + atkA2_mirrorcoatdamagecalculator, + atkA3_disablelastusedattack, + atkA4_trysetencore, + atkA5_painsplitdmgcalc, + atkA6_settypetorandomresistance, + atkA7_setalwayshitflag, + atkA8_copymovepermanently, + atkA9_trychoosesleeptalkmove, + atkAA_setdestinybond, + atkAB_trysetdestinybondtohappen, + atkAC_remaininghptopower, + atkAD_tryspiteppreduce, + atkAE_healpartystatus, + atkAF_cursetarget, + atkB0_trysetspikes, + atkB1_setforesight, + atkB2_trysetperishsong, + atkB3_rolloutdamagecalculation, + atkB4_jumpifconfusedandstatmaxed, + atkB5_furycuttercalc, + atkB6_happinesstodamagecalculation, + atkB7_presentdamagecalculation, + atkB8_setsafeguard, + atkB9_magnitudedamagecalculation, + atkBA_jumpifnopursuitswitchdmg, + atkBB_setsunny, + atkBC_maxattackhalvehp, + atkBD_copyfoestats, + atkBE_rapidspinfree, + atkBF_setdefensecurlbit, + atkC0_recoverbasedonsunlight, + atkC1_hiddenpowercalc, + atkC2_selectfirstvalidtarget, + atkC3_trysetfutureattack, + atkC4_trydobeatup, + atkC5_setsemiinvulnerablebit, + atkC6_clearsemiinvulnerablebit, + atkC7_setminimize, + atkC8_sethail, + atkC9_jumpifattackandspecialattackcannotfall, + atkCA_setforcedtarget, + atkCB_setcharge, + atkCC_callterrainattack, + atkCD_cureifburnedparalysedorpoisoned, + atkCE_settorment, + atkCF_jumpifnodamage, + atkD0_settaunt, + atkD1_trysethelpinghand, + atkD2_tryswapitems, + atkD3_trycopyability, + atkD4_trywish, + atkD5_trysetroots, + atkD6_doubledamagedealtifdamaged, + atkD7_setyawn, + atkD8_setdamagetohealthdifference, + atkD9_scaledamagebyhealthratio, + atkDA_tryswapabilities, + atkDB_tryimprison, + atkDC_trysetgrudge, + atkDD_weightdamagecalculation, + atkDE_assistattackselect, + atkDF_trysetmagiccoat, + atkE0_trysetsnatch, + atkE1_trygetintimidatetarget, + atkE2_switchoutabilities, + atkE3_jumpifhasnohp, + atkE4_getsecretpowereffect, + atkE5_pickup, + atkE6_docastformchangeanimation, + atkE7_trycastformdatachange, + atkE8_settypebasedhalvers, + atkE9_setweatherballtype, + atkEA_tryrecycleitem, + atkEB_settypetoterrain, + atkEC_pursuitrelated, + atkED_snatchsetbattlers, + atkEE_removelightscreenreflect, + atkEF_handleballthrow, + atkF0_givecaughtmon, + atkF1_trysetcaughtmondexflags, + atkF2_displaydexinfo, + atkF3_trygivecaughtmonnick, + atkF4_subattackerhpbydmg, + atkF5_removeattackerstatus1, + atkF6_finishaction, + atkF7_finishturn, +}; + +struct StatFractions +{ + u8 dividend; + u8 divisor; +}; + +static const struct StatFractions sAccuracyStageRatios[] = +{ + { 33, 100 }, // -6 + { 36, 100 }, // -5 + { 43, 100 }, // -4 + { 50, 100 }, // -3 + { 60, 100 }, // -2 + { 75, 100 }, // -1 + { 1, 1 }, // 0 + { 133, 100 }, // +1 + { 166, 100 }, // +2 + { 2, 1 }, // +3 + { 233, 100 }, // +4 + { 133, 50 }, // +5 + { 3, 1 }, // +6 +}; + +// The chance is 1/N for each stage. +static const u16 sCriticalHitChance[] = { 16, 8, 4, 3, 2 }; + +static const u32 sStatusFlagsForMoveEffects[] = +{ + 0x00000000, + STATUS1_SLEEP, + STATUS1_POISON, + STATUS1_BURN, + STATUS1_FREEZE, + STATUS1_PARALYSIS, + STATUS1_TOXIC_POISON, + STATUS2_CONFUSION, + STATUS2_FLINCHED, + 0x00000000, + STATUS2_UPROAR, + 0x00000000, + STATUS2_MULTIPLETURNS, + STATUS2_WRAPPED, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + STATUS2_RECHARGE, + 0x00000000, + 0x00000000, + STATUS2_ESCAPE_PREVENTION, + STATUS2_NIGHTMARE, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + STATUS2_LOCK_CONFUSE, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000, + 0x00000000 +}; + +static const u8 *const sMoveEffectBS_Ptrs[] = +{ + [0] = BattleScript_MoveEffectSleep, + [MOVE_EFFECT_SLEEP] = BattleScript_MoveEffectSleep, + [MOVE_EFFECT_POISON] = BattleScript_MoveEffectPoison, + [MOVE_EFFECT_BURN] = BattleScript_MoveEffectBurn, + [MOVE_EFFECT_FREEZE] = BattleScript_MoveEffectFreeze, + [MOVE_EFFECT_PARALYSIS] = BattleScript_MoveEffectParalysis, + [MOVE_EFFECT_TOXIC] = BattleScript_MoveEffectToxic, + [MOVE_EFFECT_CONFUSION] = BattleScript_MoveEffectConfusion, + [MOVE_EFFECT_FLINCH] = BattleScript_MoveEffectSleep, + [MOVE_EFFECT_TRI_ATTACK] = BattleScript_MoveEffectSleep, + [MOVE_EFFECT_UPROAR] = BattleScript_MoveEffectUproar, + [MOVE_EFFECT_PAYDAY] = BattleScript_MoveEffectPayDay, + [MOVE_EFFECT_CHARGING] = BattleScript_MoveEffectSleep, + [MOVE_EFFECT_WRAP] = BattleScript_MoveEffectWrap, + [MOVE_EFFECT_RECOIL_25] = BattleScript_MoveEffectRecoil, + [MOVE_EFFECT_ATK_PLUS_1] = BattleScript_MoveEffectSleep, + [MOVE_EFFECT_DEF_PLUS_1] = BattleScript_MoveEffectSleep, + [MOVE_EFFECT_SPD_PLUS_1] = BattleScript_MoveEffectSleep, + [MOVE_EFFECT_SP_ATK_PLUS_1] = BattleScript_MoveEffectSleep, + [MOVE_EFFECT_SP_DEF_PLUS_1] = BattleScript_MoveEffectSleep, + [MOVE_EFFECT_ACC_PLUS_1] = BattleScript_MoveEffectSleep, + [MOVE_EFFECT_EVS_PLUS_1] = BattleScript_MoveEffectSleep, + [MOVE_EFFECT_ATK_MINUS_1] = BattleScript_MoveEffectSleep, + [MOVE_EFFECT_DEF_MINUS_1] = BattleScript_MoveEffectSleep, + [MOVE_EFFECT_SPD_MINUS_1] = BattleScript_MoveEffectSleep, + [MOVE_EFFECT_SP_ATK_MINUS_1] = BattleScript_MoveEffectSleep, + [MOVE_EFFECT_SP_DEF_MINUS_1] = BattleScript_MoveEffectSleep, + [MOVE_EFFECT_ACC_MINUS_1] = BattleScript_MoveEffectSleep, + [MOVE_EFFECT_EVS_MINUS_1] = BattleScript_MoveEffectSleep, + [MOVE_EFFECT_RECHARGE] = BattleScript_MoveEffectSleep, + [MOVE_EFFECT_RAGE] = BattleScript_MoveEffectSleep, + [MOVE_EFFECT_STEAL_ITEM] = BattleScript_MoveEffectSleep, + [MOVE_EFFECT_PREVENT_ESCAPE] = BattleScript_MoveEffectSleep, + [MOVE_EFFECT_NIGHTMARE] = BattleScript_MoveEffectSleep, + [MOVE_EFFECT_ALL_STATS_UP] = BattleScript_MoveEffectSleep, + [MOVE_EFFECT_RAPIDSPIN] = BattleScript_MoveEffectSleep, + [MOVE_EFFECT_REMOVE_PARALYSIS] = BattleScript_MoveEffectSleep, + [MOVE_EFFECT_ATK_DEF_DOWN] = BattleScript_MoveEffectSleep, + [MOVE_EFFECT_RECOIL_33] = BattleScript_MoveEffectRecoil, +}; + +// not used +static const struct WindowTemplate sUnusedWinTemplate = +{ + .bg = 0, + .tilemapLeft = 1, + .tilemapTop = 3, + .width = 7, + .height = 15, + .paletteNum = 31, + .baseBlock = 0x3F, +}; + +static const u16 gUnknown_82506D0[] = INCBIN_U16("graphics/battle_interface/unk_battlebox.gbapal"); +static const u32 gUnknown_82506F0[] = INCBIN_U32("graphics/battle_interface/unk_battlebox.4bpp.lz"); + +// not used +static const u8 sRubyLevelUpStatBoxStats[] = +{ + MON_DATA_MAX_HP, MON_DATA_SPATK, MON_DATA_ATK, + MON_DATA_SPDEF, MON_DATA_DEF, MON_DATA_SPEED +}; + +static const struct OamData sOamData_MonIconOnLvlUpBox = +{ + .y = 0, + .affineMode = 0, + .objMode = 0, + .mosaic = 0, + .bpp = 0, + .shape = SPRITE_SHAPE(32x32), + .x = 0, + .matrixNum = 0, + .size = SPRITE_SIZE(32x32), + .tileNum = 0, + .priority = 0, + .paletteNum = 0, + .affineParam = 0, +}; + +#define MON_ICON_LVLUP_BOX_TAG 0xD75A + +static const struct SpriteTemplate sSpriteTemplate_MonIconOnLvlUpBox = +{ + .tileTag = MON_ICON_LVLUP_BOX_TAG, + .paletteTag = MON_ICON_LVLUP_BOX_TAG, + .oam = &sOamData_MonIconOnLvlUpBox, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = SpriteCB_MonIconOnLvlUpBox +}; + +static const u16 sProtectSuccessRates[] = +{ + USHRT_MAX, + USHRT_MAX / 2, + USHRT_MAX / 4, + USHRT_MAX / 8 +}; + +#define MIMIC_FORBIDDEN_END 0xFFFE +#define METRONOME_FORBIDDEN_END 0xFFFF +#define ASSIST_FORBIDDEN_END 0xFFFF + +static const u16 sMovesForbiddenToCopy[] = +{ + MOVE_METRONOME, + MOVE_STRUGGLE, + MOVE_SKETCH, + MOVE_MIMIC, + MIMIC_FORBIDDEN_END, + MOVE_COUNTER, + MOVE_MIRROR_COAT, + MOVE_PROTECT, + MOVE_DETECT, + MOVE_ENDURE, + MOVE_DESTINY_BOND, + MOVE_SLEEP_TALK, + MOVE_THIEF, + MOVE_FOLLOW_ME, + MOVE_SNATCH, + MOVE_HELPING_HAND, + MOVE_COVET, + MOVE_TRICK, + MOVE_FOCUS_PUNCH, + METRONOME_FORBIDDEN_END +}; + +static const u8 sFlailHpScaleToPowerTable[] = +{ + 1, 200, + 4, 150, + 9, 100, + 16, 80, + 32, 40, + 48, 20 +}; + +static const u16 sNaturePowerMoves[] = +{ + MOVE_STUN_SPORE, + MOVE_RAZOR_LEAF, + MOVE_EARTHQUAKE, + MOVE_HYDRO_PUMP, + MOVE_SURF, + MOVE_BUBBLE_BEAM, + MOVE_ROCK_SLIDE, + MOVE_SHADOW_BALL, + MOVE_SWIFT, + MOVE_SWIFT +}; + +static const u16 sWeightToDamageTable[] = +{ + 100, 20, + 250, 40, + 500, 60, + 1000, 80, + 2000, 100, + 0xFFFF, 0xFFFF +}; + +struct PickupItem +{ + u16 itemId; + u8 chance; +}; + +static const struct PickupItem sPickupItems[] = +{ + { ITEM_ORAN_BERRY, 15 }, + { ITEM_CHERI_BERRY, 25 }, + { ITEM_CHESTO_BERRY, 35 }, + { ITEM_PECHA_BERRY, 45 }, + { ITEM_RAWST_BERRY, 55 }, + { ITEM_ASPEAR_BERRY, 65 }, + { ITEM_PERSIM_BERRY, 75 }, + { ITEM_TM10, 80 }, + { ITEM_PP_UP, 85 }, + { ITEM_RARE_CANDY, 90 }, + { ITEM_NUGGET, 95 }, + { ITEM_SPELON_BERRY, 96 }, + { ITEM_PAMTRE_BERRY, 97 }, + { ITEM_WATMEL_BERRY, 98 }, + { ITEM_DURIN_BERRY, 99 }, + { ITEM_BELUE_BERRY, 1 }, + +}; + +static const u8 sTerrainToType[] = +{ + TYPE_GRASS, // tall grass + TYPE_GRASS, // long grass + TYPE_GROUND, // sand + TYPE_WATER, // underwater + TYPE_WATER, // water + TYPE_WATER, // pond water + TYPE_ROCK, // rock + TYPE_ROCK, // cave + TYPE_NORMAL, // building + TYPE_NORMAL, // plain +}; + +static const u8 sBallCatchBonuses[] = +{ + 20, 15, 10, 15 // Ultra, Great, Poke, Safari +}; + +// not used +static const u32 gUnknown_8250898 = 0xFF7EAE60; + +static void atk00_attackcanceler(void) +{ + s32 i; + + if (gBattleOutcome) + { + gCurrentActionFuncId = B_ACTION_FINISHED; + return; + } + if (gBattleMons[gBattlerAttacker].hp == 0 && !(gHitMarker & HITMARKER_NO_ATTACKSTRING)) + { + gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; + gBattlescriptCurrInstr = BattleScript_MoveEnd; + return; + } + if (AtkCanceller_UnableToUseMove()) + return; + if (AbilityBattleEffects(ABILITYEFFECT_MOVES_BLOCK, gBattlerTarget, 0, 0, 0)) + return; + if (!gBattleMons[gBattlerAttacker].pp[gCurrMovePos] && gCurrentMove != MOVE_STRUGGLE && !(gHitMarker & (HITMARKER_x800000 | HITMARKER_NO_ATTACKSTRING)) + && !(gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS)) + { + gBattlescriptCurrInstr = BattleScript_NoPPForMove; + gMoveResultFlags |= MOVE_RESULT_MISSED; + return; + } + gHitMarker &= ~(HITMARKER_x800000); + if (!(gHitMarker & HITMARKER_OBEYS) + && !(gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS)) + { + i = IsMonDisobedient(); + switch (i) + { + case 0: + break; + case 2: + gHitMarker |= HITMARKER_OBEYS; + return; + default: + gMoveResultFlags |= MOVE_RESULT_MISSED; + return; + } + } + gHitMarker |= HITMARKER_OBEYS; + if (gProtectStructs[gBattlerTarget].bounceMove && gBattleMoves[gCurrentMove].flags & FLAG_MAGICCOAT_AFFECTED) + { + PressurePPLose(gBattlerAttacker, gBattlerTarget, MOVE_MAGIC_COAT); + gProtectStructs[gBattlerTarget].bounceMove = FALSE; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_MagicCoatBounce; + return; + } + for (i = 0; i < gBattlersCount; ++i) + { + if ((gProtectStructs[gBattlerByTurnOrder[i]].stealMove) && gBattleMoves[gCurrentMove].flags & FLAG_SNATCH_AFFECTED) + { + PressurePPLose(gBattlerAttacker, gBattlerByTurnOrder[i], MOVE_SNATCH); + gProtectStructs[gBattlerByTurnOrder[i]].stealMove = FALSE; + gBattleScripting.battler = gBattlerByTurnOrder[i]; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_SnatchedMove; + return; + } + } + if (gSpecialStatuses[gBattlerTarget].lightningRodRedirected) + { + gSpecialStatuses[gBattlerTarget].lightningRodRedirected = FALSE; + gLastUsedAbility = ABILITY_LIGHTNING_ROD; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_TookAttack; + RecordAbilityBattle(gBattlerTarget, gLastUsedAbility); + } + else if (DEFENDER_IS_PROTECTED + && (gCurrentMove != MOVE_CURSE || IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GHOST)) + && ((!IsTwoTurnsMove(gCurrentMove) || (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS)))) + { + CancelMultiTurnMoves(gBattlerAttacker); + gMoveResultFlags |= MOVE_RESULT_MISSED; + gLastLandedMoves[gBattlerTarget] = 0; + gLastHitByType[gBattlerTarget] = 0; + gBattleCommunication[6] = 1; + ++gBattlescriptCurrInstr; + } + else + { + ++gBattlescriptCurrInstr; + } +} + +static void JumpIfMoveFailed(u8 adder, u16 move) +{ + const u8 *BS_ptr = gBattlescriptCurrInstr + adder; + + if (gMoveResultFlags & MOVE_RESULT_NO_EFFECT) + { + gLastLandedMoves[gBattlerTarget] = 0; + gLastHitByType[gBattlerTarget] = 0; + BS_ptr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } + else + { + TrySetDestinyBondToHappen(); + if (AbilityBattleEffects(ABILITYEFFECT_ABSORBING, gBattlerTarget, 0, 0, move)) + return; + } + gBattlescriptCurrInstr = BS_ptr; +} + +static void atk40_jumpifaffectedbyprotect(void) +{ + if (DEFENDER_IS_PROTECTED) + { + gMoveResultFlags |= MOVE_RESULT_MISSED; + JumpIfMoveFailed(5, 0); + gBattleCommunication[6] = 1; + } + else + { + gBattlescriptCurrInstr += 5; + } +} + +static bool8 JumpIfMoveAffectedByProtect(u16 move) +{ + bool8 affected = FALSE; + + if (DEFENDER_IS_PROTECTED) + { + gMoveResultFlags |= MOVE_RESULT_MISSED; + JumpIfMoveFailed(7, move); + gBattleCommunication[6] = 1; + affected = TRUE; + } + return affected; +} + +static bool8 AccuracyCalcHelper(u16 move) +{ + if (gStatuses3[gBattlerTarget] & STATUS3_ALWAYS_HITS && gDisableStructs[gBattlerTarget].battlerWithSureHit == gBattlerAttacker) + { + JumpIfMoveFailed(7, move); + return TRUE; + } + if (!(gHitMarker & HITMARKER_IGNORE_ON_AIR) && gStatuses3[gBattlerTarget] & STATUS3_ON_AIR) + { + gMoveResultFlags |= MOVE_RESULT_MISSED; + JumpIfMoveFailed(7, move); + return TRUE; + } + gHitMarker &= ~HITMARKER_IGNORE_ON_AIR; + if (!(gHitMarker & HITMARKER_IGNORE_UNDERGROUND) && gStatuses3[gBattlerTarget] & STATUS3_UNDERGROUND) + { + gMoveResultFlags |= MOVE_RESULT_MISSED; + JumpIfMoveFailed(7, move); + return TRUE; + } + gHitMarker &= ~HITMARKER_IGNORE_UNDERGROUND; + if (!(gHitMarker & HITMARKER_IGNORE_UNDERWATER) && gStatuses3[gBattlerTarget] & STATUS3_UNDERWATER) + { + gMoveResultFlags |= MOVE_RESULT_MISSED; + JumpIfMoveFailed(7, move); + return TRUE; + } + gHitMarker &= ~HITMARKER_IGNORE_UNDERWATER; + if ((WEATHER_HAS_EFFECT && (gBattleWeather & WEATHER_RAIN_ANY) && gBattleMoves[move].effect == EFFECT_THUNDER) + || (gBattleMoves[move].effect == EFFECT_ALWAYS_HIT || gBattleMoves[move].effect == EFFECT_VITAL_THROW)) + { + JumpIfMoveFailed(7, move); + return TRUE; + } + return FALSE; +} + +static void atk01_accuracycheck(void) +{ + u16 move = T2_READ_16(gBattlescriptCurrInstr + 5); + + if ((gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE + && !sub_80EB2E0(1) + && gBattleMoves[move].power != 0 + && GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER) + || (gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE + && !sub_80EB2E0(2) + && gBattleMoves[move].power == 0 + && GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER) + || (gBattleTypeFlags & BATTLE_TYPE_POKEDUDE)) + { + JumpIfMoveFailed(7, move); + return; + } + if (move == NO_ACC_CALC || move == NO_ACC_CALC_CHECK_LOCK_ON) + { + if (gStatuses3[gBattlerTarget] & STATUS3_ALWAYS_HITS && move == NO_ACC_CALC_CHECK_LOCK_ON && gDisableStructs[gBattlerTarget].battlerWithSureHit == gBattlerAttacker) + gBattlescriptCurrInstr += 7; + else if (gStatuses3[gBattlerTarget] & (STATUS3_ON_AIR | STATUS3_UNDERGROUND | STATUS3_UNDERWATER)) + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + else if (!JumpIfMoveAffectedByProtect(0)) + gBattlescriptCurrInstr += 7; + } + else + { + u8 type, moveAcc, holdEffect, param; + s8 buff; + u16 calc; + + if (move == MOVE_NONE) + move = gCurrentMove; + GET_MOVE_TYPE(move, type); + if (JumpIfMoveAffectedByProtect(move) || AccuracyCalcHelper(move)) + return; + if (gBattleMons[gBattlerTarget].status2 & STATUS2_FORESIGHT) + { + u8 acc = gBattleMons[gBattlerAttacker].statStages[STAT_ACC]; + + buff = acc; + } + else + { + u8 acc = gBattleMons[gBattlerAttacker].statStages[STAT_ACC]; + + buff = acc + 6 - gBattleMons[gBattlerTarget].statStages[STAT_EVASION]; + } + if (buff < 0) + buff = 0; + if (buff > 0xC) + buff = 0xC; + moveAcc = gBattleMoves[move].accuracy; + // check Thunder on sunny weather + if (WEATHER_HAS_EFFECT && gBattleWeather & WEATHER_SUN_ANY && gBattleMoves[move].effect == EFFECT_THUNDER) + moveAcc = 50; + calc = sAccuracyStageRatios[buff].dividend * moveAcc; + calc /= sAccuracyStageRatios[buff].divisor; + if (gBattleMons[gBattlerAttacker].ability == ABILITY_COMPOUND_EYES) + calc = (calc * 130) / 100; // 1.3 compound eyes boost + if (WEATHER_HAS_EFFECT && gBattleMons[gBattlerTarget].ability == ABILITY_SAND_VEIL && gBattleWeather & WEATHER_SANDSTORM_ANY) + calc = (calc * 80) / 100; // 1.2 sand veil loss + if (gBattleMons[gBattlerAttacker].ability == ABILITY_HUSTLE && IS_TYPE_PHYSICAL(type)) + calc = (calc * 80) / 100; // 1.2 hustle loss + if (gBattleMons[gBattlerTarget].item == ITEM_ENIGMA_BERRY) + { + holdEffect = gEnigmaBerries[gBattlerTarget].holdEffect; + param = gEnigmaBerries[gBattlerTarget].holdEffectParam; + } + else + { + holdEffect = ItemId_GetHoldEffect(gBattleMons[gBattlerTarget].item); + param = ItemId_GetHoldEffectParam(gBattleMons[gBattlerTarget].item); + } + gPotentialItemEffectBattler = gBattlerTarget; + + if (holdEffect == HOLD_EFFECT_EVASION_UP) + calc = (calc * (100 - param)) / 100; + // final calculation + if ((Random() % 100 + 1) > calc) + { + gMoveResultFlags |= MOVE_RESULT_MISSED; + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE + && (gBattleMoves[move].target == MOVE_TARGET_BOTH || gBattleMoves[move].target == MOVE_TARGET_FOES_AND_ALLY)) + gBattleCommunication[6] = 2; + else + gBattleCommunication[6] = 0; + CheckWonderGuardAndLevitate(); + } + JumpIfMoveFailed(7, move); + } +} + +static void atk02_attackstring(void) +{ + if (!gBattleControllerExecFlags) + { + if (!(gHitMarker & (HITMARKER_NO_ATTACKSTRING | HITMARKER_ATTACKSTRING_PRINTED))) + { + PrepareStringBattle(STRINGID_USEDMOVE, gBattlerAttacker); + gHitMarker |= HITMARKER_ATTACKSTRING_PRINTED; + } + ++gBattlescriptCurrInstr; + gBattleCommunication[MSG_DISPLAY] = 0; + } +} + + static void atk03_ppreduce(void) +{ + s32 ppToDeduct = 1; + + if (!gBattleControllerExecFlags) + { + if (!gSpecialStatuses[gBattlerAttacker].ppNotAffectedByPressure) + { + switch (gBattleMoves[gCurrentMove].target) + { + case MOVE_TARGET_FOES_AND_ALLY: + ppToDeduct += AbilityBattleEffects(ABILITYEFFECT_COUNT_ON_FIELD, gBattlerAttacker, ABILITY_PRESSURE, 0, 0); + break; + case MOVE_TARGET_BOTH: + case MOVE_TARGET_OPPONENTS_FIELD: + ppToDeduct += AbilityBattleEffects(ABILITYEFFECT_COUNT_OTHER_SIDE, gBattlerAttacker, ABILITY_PRESSURE, 0, 0); + break; + default: + if (gBattlerAttacker != gBattlerTarget && gBattleMons[gBattlerTarget].ability == ABILITY_PRESSURE) + ++ppToDeduct; + break; + } + } + if (!(gHitMarker & (HITMARKER_NO_PPDEDUCT | HITMARKER_NO_ATTACKSTRING)) && gBattleMons[gBattlerAttacker].pp[gCurrMovePos]) + { + gProtectStructs[gBattlerAttacker].notFirstStrike = 1; + + if (gBattleMons[gBattlerAttacker].pp[gCurrMovePos] > ppToDeduct) + gBattleMons[gBattlerAttacker].pp[gCurrMovePos] -= ppToDeduct; + else + gBattleMons[gBattlerAttacker].pp[gCurrMovePos] = 0; + + if (!(gBattleMons[gBattlerAttacker].status2 & STATUS2_TRANSFORMED) + && !((gDisableStructs[gBattlerAttacker].mimickedMoves) & gBitTable[gCurrMovePos])) + { + gActiveBattler = gBattlerAttacker; + BtlController_EmitSetMonData(0, REQUEST_PPMOVE1_BATTLE + gCurrMovePos, 0, 1, &gBattleMons[gBattlerAttacker].pp[gCurrMovePos]); + MarkBattlerForControllerExec(gBattlerAttacker); + } + } + gHitMarker &= ~(HITMARKER_NO_PPDEDUCT); + ++gBattlescriptCurrInstr; + } +} + +static void atk04_critcalc(void) +{ + u8 holdEffect; + u16 item, critChance; + + item = gBattleMons[gBattlerAttacker].item; + if (item == ITEM_ENIGMA_BERRY) + holdEffect = gEnigmaBerries[gBattlerAttacker].holdEffect; + else + holdEffect = ItemId_GetHoldEffect(item); + gPotentialItemEffectBattler = gBattlerAttacker; + critChance = 2 * ((gBattleMons[gBattlerAttacker].status2 & STATUS2_FOCUS_ENERGY) != 0) + + (gBattleMoves[gCurrentMove].effect == EFFECT_HIGH_CRITICAL) + + (gBattleMoves[gCurrentMove].effect == EFFECT_SKY_ATTACK) + + (gBattleMoves[gCurrentMove].effect == EFFECT_BLAZE_KICK) + + (gBattleMoves[gCurrentMove].effect == EFFECT_POISON_TAIL) + + (holdEffect == HOLD_EFFECT_SCOPE_LENS) + + 2 * (holdEffect == HOLD_EFFECT_LUCKY_PUNCH && gBattleMons[gBattlerAttacker].species == SPECIES_CHANSEY) + + 2 * (holdEffect == HOLD_EFFECT_STICK && gBattleMons[gBattlerAttacker].species == SPECIES_FARFETCHD); + if (critChance >= NELEMS(sCriticalHitChance)) + critChance = NELEMS(sCriticalHitChance) - 1; + if ((gBattleMons[gBattlerTarget].ability != ABILITY_BATTLE_ARMOR && gBattleMons[gBattlerTarget].ability != ABILITY_SHELL_ARMOR) + && !(gStatuses3[gBattlerAttacker] & STATUS3_CANT_SCORE_A_CRIT) + && !(gBattleTypeFlags & BATTLE_TYPE_OLDMAN_TUTORIAL) + && !(Random() % sCriticalHitChance[critChance]) + && (!(gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE) || sub_80EB2E0(1)) + && !(gBattleTypeFlags & BATTLE_TYPE_POKEDUDE)) + gCritMultiplier = 2; + else + gCritMultiplier = 1; + ++gBattlescriptCurrInstr; +} + +static void atk05_damagecalc(void) +{ + u16 sideStatus = gSideStatuses[GET_BATTLER_SIDE(gBattlerTarget)]; + + gBattleMoveDamage = CalculateBaseDamage(&gBattleMons[gBattlerAttacker], + &gBattleMons[gBattlerTarget], + gCurrentMove, + sideStatus, + gDynamicBasePower, + gBattleStruct->dynamicMoveType, + gBattlerAttacker, + gBattlerTarget); + gBattleMoveDamage = gBattleMoveDamage * gCritMultiplier * gBattleScripting.dmgMultiplier; + if (gStatuses3[gBattlerAttacker] & STATUS3_CHARGED_UP && gBattleMoves[gCurrentMove].type == TYPE_ELECTRIC) + gBattleMoveDamage *= 2; + if (gProtectStructs[gBattlerAttacker].helpingHand) + gBattleMoveDamage = gBattleMoveDamage * 15 / 10; + ++gBattlescriptCurrInstr; +} + +void AI_CalcDmg(u8 attacker, u8 defender) +{ + u16 sideStatus = gSideStatuses[GET_BATTLER_SIDE(defender)]; + + gBattleMoveDamage = CalculateBaseDamage(&gBattleMons[attacker], + &gBattleMons[defender], + gCurrentMove, + sideStatus, + gDynamicBasePower, + gBattleStruct->dynamicMoveType, + attacker, + defender); + gDynamicBasePower = 0; + gBattleMoveDamage = gBattleMoveDamage * gCritMultiplier * gBattleScripting.dmgMultiplier; + if (gStatuses3[attacker] & STATUS3_CHARGED_UP && gBattleMoves[gCurrentMove].type == TYPE_ELECTRIC) + gBattleMoveDamage *= 2; + if (gProtectStructs[attacker].helpingHand) + gBattleMoveDamage = gBattleMoveDamage * 15 / 10; +} + +void ModulateDmgByType(u8 multiplier) +{ + gBattleMoveDamage = gBattleMoveDamage * multiplier / 10; + if (gBattleMoveDamage == 0 && multiplier) + gBattleMoveDamage = 1; + switch (multiplier) + { + case TYPE_MUL_NO_EFFECT: + gMoveResultFlags |= MOVE_RESULT_DOESNT_AFFECT_FOE; + gMoveResultFlags &= ~MOVE_RESULT_NOT_VERY_EFFECTIVE; + gMoveResultFlags &= ~MOVE_RESULT_SUPER_EFFECTIVE; + break; + case TYPE_MUL_NOT_EFFECTIVE: + if (gBattleMoves[gCurrentMove].power && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)) + { + if (gMoveResultFlags & MOVE_RESULT_SUPER_EFFECTIVE) + gMoveResultFlags &= ~MOVE_RESULT_SUPER_EFFECTIVE; + else + gMoveResultFlags |= MOVE_RESULT_NOT_VERY_EFFECTIVE; + } + break; + case TYPE_MUL_SUPER_EFFECTIVE: + if (gBattleMoves[gCurrentMove].power && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)) + { + if (gMoveResultFlags & MOVE_RESULT_NOT_VERY_EFFECTIVE) + gMoveResultFlags &= ~MOVE_RESULT_NOT_VERY_EFFECTIVE; + else + gMoveResultFlags |= MOVE_RESULT_SUPER_EFFECTIVE; + } + break; + } +} + +static void atk06_typecalc(void) +{ + s32 i = 0; + u8 moveType; + + if (gCurrentMove == MOVE_STRUGGLE) + { + ++gBattlescriptCurrInstr; + return; + } + GET_MOVE_TYPE(gCurrentMove, moveType); + // check stab + if (IS_BATTLER_OF_TYPE(gBattlerAttacker, moveType)) + { + gBattleMoveDamage = gBattleMoveDamage * 15; + gBattleMoveDamage = gBattleMoveDamage / 10; + } + + if (gBattleMons[gBattlerTarget].ability == ABILITY_LEVITATE && moveType == TYPE_GROUND) + { + gLastUsedAbility = gBattleMons[gBattlerTarget].ability; + gMoveResultFlags |= (MOVE_RESULT_MISSED | MOVE_RESULT_DOESNT_AFFECT_FOE); + gLastLandedMoves[gBattlerTarget] = 0; + gLastHitByType[gBattlerTarget] = 0; + gBattleCommunication[6] = moveType; + RecordAbilityBattle(gBattlerTarget, gLastUsedAbility); + } + else + { + while (TYPE_EFFECT_ATK_TYPE(i) != TYPE_ENDTABLE) + { + if (TYPE_EFFECT_ATK_TYPE(i) == TYPE_FORESIGHT) + { + if (gBattleMons[gBattlerTarget].status2 & STATUS2_FORESIGHT) + break; + i += 3; + continue; + } + else if (TYPE_EFFECT_ATK_TYPE(i) == moveType) + { + // check type1 + if (TYPE_EFFECT_DEF_TYPE(i) == gBattleMons[gBattlerTarget].type1) + ModulateDmgByType(TYPE_EFFECT_MULTIPLIER(i)); + // check type2 + if (TYPE_EFFECT_DEF_TYPE(i) == gBattleMons[gBattlerTarget].type2 && + gBattleMons[gBattlerTarget].type1 != gBattleMons[gBattlerTarget].type2) + ModulateDmgByType(TYPE_EFFECT_MULTIPLIER(i)); + } + i += 3; + } + } + if (gBattleMons[gBattlerTarget].ability == ABILITY_WONDER_GUARD && AttacksThisTurn(gBattlerAttacker, gCurrentMove) == 2 + && (!(gMoveResultFlags & MOVE_RESULT_SUPER_EFFECTIVE) || ((gMoveResultFlags & (MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE)) == (MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE))) + && gBattleMoves[gCurrentMove].power) + { + gLastUsedAbility = ABILITY_WONDER_GUARD; + gMoveResultFlags |= MOVE_RESULT_MISSED; + gLastLandedMoves[gBattlerTarget] = 0; + gLastHitByType[gBattlerTarget] = 0; + gBattleCommunication[6] = 3; + RecordAbilityBattle(gBattlerTarget, gLastUsedAbility); + } + if (gMoveResultFlags & MOVE_RESULT_DOESNT_AFFECT_FOE) + gProtectStructs[gBattlerAttacker].targetNotAffected = 1; + ++gBattlescriptCurrInstr; +} + +static void CheckWonderGuardAndLevitate(void) +{ + u8 flags = 0; + s32 i = 0; + u8 moveType; + + if (gCurrentMove == MOVE_STRUGGLE || !gBattleMoves[gCurrentMove].power) + return; + GET_MOVE_TYPE(gCurrentMove, moveType); + if (gBattleMons[gBattlerTarget].ability == ABILITY_LEVITATE && moveType == TYPE_GROUND) + { + gLastUsedAbility = ABILITY_LEVITATE; + gBattleCommunication[6] = moveType; + RecordAbilityBattle(gBattlerTarget, ABILITY_LEVITATE); + return; + } + while (TYPE_EFFECT_ATK_TYPE(i) != TYPE_ENDTABLE) + { + if (TYPE_EFFECT_ATK_TYPE(i) == TYPE_FORESIGHT) + { + if (gBattleMons[gBattlerTarget].status2 & STATUS2_FORESIGHT) + break; + i += 3; + continue; + } + if (TYPE_EFFECT_ATK_TYPE(i) == moveType) + { + // check no effect + if (TYPE_EFFECT_DEF_TYPE(i) == gBattleMons[gBattlerTarget].type1 + && TYPE_EFFECT_MULTIPLIER(i) == TYPE_MUL_NO_EFFECT) + { + gMoveResultFlags |= MOVE_RESULT_DOESNT_AFFECT_FOE; + gProtectStructs[gBattlerAttacker].targetNotAffected = 1; + } + if (TYPE_EFFECT_DEF_TYPE(i) == gBattleMons[gBattlerTarget].type2 && + gBattleMons[gBattlerTarget].type1 != gBattleMons[gBattlerTarget].type2 && + TYPE_EFFECT_MULTIPLIER(i) == TYPE_MUL_NO_EFFECT) + { + gMoveResultFlags |= MOVE_RESULT_DOESNT_AFFECT_FOE; + gProtectStructs[gBattlerAttacker].targetNotAffected = 1; + } + // check super effective + if (TYPE_EFFECT_DEF_TYPE(i) == gBattleMons[gBattlerTarget].type1 && TYPE_EFFECT_MULTIPLIER(i) == 20) + flags |= 1; + if (TYPE_EFFECT_DEF_TYPE(i) == gBattleMons[gBattlerTarget].type2 + && gBattleMons[gBattlerTarget].type1 != gBattleMons[gBattlerTarget].type2 + && TYPE_EFFECT_MULTIPLIER(i) == TYPE_MUL_SUPER_EFFECTIVE) + flags |= 1; + // check not very effective + if (TYPE_EFFECT_DEF_TYPE(i) == gBattleMons[gBattlerTarget].type1 && TYPE_EFFECT_MULTIPLIER(i) == 5) + flags |= 2; + if (TYPE_EFFECT_DEF_TYPE(i) == gBattleMons[gBattlerTarget].type2 + && gBattleMons[gBattlerTarget].type1 != gBattleMons[gBattlerTarget].type2 + && TYPE_EFFECT_MULTIPLIER(i) == TYPE_MUL_NOT_EFFECTIVE) + flags |= 2; + } + i += 3; + } + if (gBattleMons[gBattlerTarget].ability == ABILITY_WONDER_GUARD && AttacksThisTurn(gBattlerAttacker, gCurrentMove) == 2) + { + if (((flags & 2) || !(flags & 1)) && gBattleMoves[gCurrentMove].power) + { + gLastUsedAbility = ABILITY_WONDER_GUARD; + gBattleCommunication[6] = 3; + RecordAbilityBattle(gBattlerTarget, ABILITY_WONDER_GUARD); + } + } +} + +// same as ModulateDmgByType except different arguments +static void ModulateDmgByType2(u8 multiplier, u16 move, u8 *flags) +{ + gBattleMoveDamage = gBattleMoveDamage * multiplier / 10; + if (gBattleMoveDamage == 0 && multiplier != 0) + gBattleMoveDamage = 1; + switch (multiplier) + { + case TYPE_MUL_NO_EFFECT: + *flags |= MOVE_RESULT_DOESNT_AFFECT_FOE; + *flags &= ~MOVE_RESULT_NOT_VERY_EFFECTIVE; + *flags &= ~MOVE_RESULT_SUPER_EFFECTIVE; + break; + case TYPE_MUL_NOT_EFFECTIVE: + if (gBattleMoves[move].power && !(*flags & MOVE_RESULT_NO_EFFECT)) + { + if (*flags & MOVE_RESULT_SUPER_EFFECTIVE) + *flags &= ~MOVE_RESULT_SUPER_EFFECTIVE; + else + *flags |= MOVE_RESULT_NOT_VERY_EFFECTIVE; + } + break; + case TYPE_MUL_SUPER_EFFECTIVE: + if (gBattleMoves[move].power && !(*flags & MOVE_RESULT_NO_EFFECT)) + { + if (*flags & MOVE_RESULT_NOT_VERY_EFFECTIVE) + *flags &= ~MOVE_RESULT_NOT_VERY_EFFECTIVE; + else + *flags |= MOVE_RESULT_SUPER_EFFECTIVE; + } + break; + } +} + +u8 TypeCalc(u16 move, u8 attacker, u8 defender) +{ + s32 i = 0; + u8 flags = 0; + u8 moveType; + + if (move == MOVE_STRUGGLE) + return 0; + moveType = gBattleMoves[move].type; + // check stab + if (IS_BATTLER_OF_TYPE(attacker, moveType)) + { + gBattleMoveDamage = gBattleMoveDamage * 15; + gBattleMoveDamage = gBattleMoveDamage / 10; + } + + if (gBattleMons[defender].ability == ABILITY_LEVITATE && moveType == TYPE_GROUND) + { + flags |= (MOVE_RESULT_MISSED | MOVE_RESULT_DOESNT_AFFECT_FOE); + } + else + { + while (TYPE_EFFECT_ATK_TYPE(i) != TYPE_ENDTABLE) + { + if (TYPE_EFFECT_ATK_TYPE(i) == TYPE_FORESIGHT) + { + if (gBattleMons[defender].status2 & STATUS2_FORESIGHT) + break; + i += 3; + continue; + } + + else if (TYPE_EFFECT_ATK_TYPE(i) == moveType) + { + // check type1 + if (TYPE_EFFECT_DEF_TYPE(i) == gBattleMons[defender].type1) + ModulateDmgByType2(TYPE_EFFECT_MULTIPLIER(i), move, &flags); + // check type2 + if (TYPE_EFFECT_DEF_TYPE(i) == gBattleMons[defender].type2 && + gBattleMons[defender].type1 != gBattleMons[defender].type2) + ModulateDmgByType2(TYPE_EFFECT_MULTIPLIER(i), move, &flags); + } + i += 3; + } + } + if (gBattleMons[defender].ability == ABILITY_WONDER_GUARD + && !(flags & MOVE_RESULT_MISSED) + && AttacksThisTurn(attacker, move) == 2 + && (!(flags & MOVE_RESULT_SUPER_EFFECTIVE) || ((flags & (MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE)) == (MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE))) + && gBattleMoves[move].power) + flags |= MOVE_RESULT_MISSED; + return flags; +} + +u8 AI_TypeCalc(u16 move, u16 targetSpecies, u8 targetAbility) +{ + s32 i = 0; + u8 flags = 0; + u8 type1 = gBaseStats[targetSpecies].type1, type2 = gBaseStats[targetSpecies].type2; + u8 moveType; + + if (move == MOVE_STRUGGLE) + return 0; + moveType = gBattleMoves[move].type; + if (targetAbility == ABILITY_LEVITATE && moveType == TYPE_GROUND) + { + flags = MOVE_RESULT_MISSED | MOVE_RESULT_DOESNT_AFFECT_FOE; + } + else + { + while (TYPE_EFFECT_ATK_TYPE(i) != TYPE_ENDTABLE) + { + if (TYPE_EFFECT_ATK_TYPE(i) == TYPE_FORESIGHT) + { + i += 3; + continue; + } + if (TYPE_EFFECT_ATK_TYPE(i) == moveType) + { + // check type1 + if (TYPE_EFFECT_DEF_TYPE(i) == type1) + ModulateDmgByType2(TYPE_EFFECT_MULTIPLIER(i), move, &flags); + // check type2 + if (TYPE_EFFECT_DEF_TYPE(i) == type2 && type1 != type2) + ModulateDmgByType2(TYPE_EFFECT_MULTIPLIER(i), move, &flags); + } + i += 3; + } + } + if (targetAbility == ABILITY_WONDER_GUARD + && (!(flags & MOVE_RESULT_SUPER_EFFECTIVE) || ((flags & (MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE)) == (MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE))) + && gBattleMoves[move].power) + flags |= MOVE_RESULT_DOESNT_AFFECT_FOE; + return flags; +} + +static inline void ApplyRandomDmgMultiplier(void) +{ + u16 rand = Random(); + u16 randPercent = 100 - (rand % 16); + + if (gBattleMoveDamage != 0) + { + gBattleMoveDamage *= randPercent; + gBattleMoveDamage /= 100; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + } +} + +static void Unused_ApplyRandomDmgMultiplier(void) +{ + ApplyRandomDmgMultiplier(); +} + +static void atk07_adjustnormaldamage(void) +{ + u8 holdEffect, param; + + ApplyRandomDmgMultiplier(); + if (gBattleMons[gBattlerTarget].item == ITEM_ENIGMA_BERRY) + { + holdEffect = gEnigmaBerries[gBattlerTarget].holdEffect; + param = gEnigmaBerries[gBattlerTarget].holdEffectParam; + } + else + { + holdEffect = ItemId_GetHoldEffect(gBattleMons[gBattlerTarget].item); + param = ItemId_GetHoldEffectParam(gBattleMons[gBattlerTarget].item); + } + gPotentialItemEffectBattler = gBattlerTarget; + if (holdEffect == HOLD_EFFECT_FOCUS_BAND && (Random() % 100) < param) + { + RecordItemEffectBattle(gBattlerTarget, holdEffect); + gSpecialStatuses[gBattlerTarget].focusBanded = 1; + } + if (!(gBattleMons[gBattlerTarget].status2 & STATUS2_SUBSTITUTE) + && (gBattleMoves[gCurrentMove].effect == EFFECT_FALSE_SWIPE || gProtectStructs[gBattlerTarget].endured || gSpecialStatuses[gBattlerTarget].focusBanded) + && gBattleMons[gBattlerTarget].hp <= gBattleMoveDamage) + { + gBattleMoveDamage = gBattleMons[gBattlerTarget].hp - 1; + if (gProtectStructs[gBattlerTarget].endured) + { + gMoveResultFlags |= MOVE_RESULT_FOE_ENDURED; + } + else if (gSpecialStatuses[gBattlerTarget].focusBanded) + { + gMoveResultFlags |= MOVE_RESULT_FOE_HUNG_ON; + gLastUsedItem = gBattleMons[gBattlerTarget].item; + } + } + ++gBattlescriptCurrInstr; +} + +// The same as 0x7 except it doesn't check for false swipe move effect. +static void atk08_adjustnormaldamage2(void) +{ + u8 holdEffect, param; + + ApplyRandomDmgMultiplier(); + if (gBattleMons[gBattlerTarget].item == ITEM_ENIGMA_BERRY) + { + holdEffect = gEnigmaBerries[gBattlerTarget].holdEffect; + param = gEnigmaBerries[gBattlerTarget].holdEffectParam; + } + else + { + holdEffect = ItemId_GetHoldEffect(gBattleMons[gBattlerTarget].item); + param = ItemId_GetHoldEffectParam(gBattleMons[gBattlerTarget].item); + } + gPotentialItemEffectBattler = gBattlerTarget; + if (holdEffect == HOLD_EFFECT_FOCUS_BAND && (Random() % 100) < param) + { + RecordItemEffectBattle(gBattlerTarget, holdEffect); + gSpecialStatuses[gBattlerTarget].focusBanded = 1; + } + if (!(gBattleMons[gBattlerTarget].status2 & STATUS2_SUBSTITUTE) + && (gProtectStructs[gBattlerTarget].endured || gSpecialStatuses[gBattlerTarget].focusBanded) + && gBattleMons[gBattlerTarget].hp <= gBattleMoveDamage) + { + gBattleMoveDamage = gBattleMons[gBattlerTarget].hp - 1; + if (gProtectStructs[gBattlerTarget].endured) + { + gMoveResultFlags |= MOVE_RESULT_FOE_ENDURED; + } + else if (gSpecialStatuses[gBattlerTarget].focusBanded) + { + gMoveResultFlags |= MOVE_RESULT_FOE_HUNG_ON; + gLastUsedItem = gBattleMons[gBattlerTarget].item; + } + } + ++gBattlescriptCurrInstr; +} + +static void atk09_attackanimation(void) +{ + if (!gBattleControllerExecFlags) + { + if ((gHitMarker & HITMARKER_NO_ANIMATIONS) && (gCurrentMove != MOVE_TRANSFORM && gCurrentMove != MOVE_SUBSTITUTE)) + { + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_Pausex20; + ++gBattleScripting.animTurn; + ++gBattleScripting.animTargetsHit; + } + else + { + if ((gBattleMoves[gCurrentMove].target & MOVE_TARGET_BOTH + || gBattleMoves[gCurrentMove].target & MOVE_TARGET_FOES_AND_ALLY + || gBattleMoves[gCurrentMove].target & MOVE_TARGET_DEPENDS) + && gBattleScripting.animTargetsHit) + { + ++gBattlescriptCurrInstr; + return; + } + if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)) + { + gActiveBattler = gBattlerAttacker; + BtlController_EmitMoveAnimation(0, gCurrentMove, gBattleScripting.animTurn, gBattleMovePower, gBattleMoveDamage, gBattleMons[gBattlerAttacker].friendship, &gDisableStructs[gBattlerAttacker]); + ++gBattleScripting.animTurn; + ++gBattleScripting.animTargetsHit; + MarkBattlerForControllerExec(gBattlerAttacker); + ++gBattlescriptCurrInstr; + } + else + { + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_Pausex20; + } + } + } +} + +static void atk0A_waitanimation(void) +{ + if (!gBattleControllerExecFlags) + ++gBattlescriptCurrInstr; +} + +static void atk0B_healthbarupdate(void) +{ + if (!gBattleControllerExecFlags) + { + if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)) + { + gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + + if (gBattleMons[gActiveBattler].status2 & STATUS2_SUBSTITUTE && gDisableStructs[gActiveBattler].substituteHP && !(gHitMarker & HITMARKER_IGNORE_SUBSTITUTE)) + { + PrepareStringBattle(STRINGID_SUBSTITUTEDAMAGED, gActiveBattler); + } + else + { + s16 healthValue; + s32 currDmg = gBattleMoveDamage; + s32 maxPossibleDmgValue = 10000; // not present in R/S, ensures that huge damage values don't change sign + + if (currDmg <= maxPossibleDmgValue) + healthValue = currDmg; + else + healthValue = maxPossibleDmgValue; + BtlController_EmitHealthBarUpdate(0, healthValue); + MarkBattlerForControllerExec(gActiveBattler); + if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER && gBattleMoveDamage > 0) + gBattleResults.playerMonWasDamaged = TRUE; + } + } + gBattlescriptCurrInstr += 2; + } +} + +static void atk0C_datahpupdate(void) +{ + u32 moveType; + + if (!gBattleControllerExecFlags) + { + if (gBattleStruct->dynamicMoveType == 0) + moveType = gBattleMoves[gCurrentMove].type; + else if (!(gBattleStruct->dynamicMoveType & 0x40)) + moveType = gBattleStruct->dynamicMoveType & 0x3F; + else + moveType = gBattleMoves[gCurrentMove].type; + if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)) + { + gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + if (gBattleMons[gActiveBattler].status2 & STATUS2_SUBSTITUTE && gDisableStructs[gActiveBattler].substituteHP && !(gHitMarker & HITMARKER_IGNORE_SUBSTITUTE)) + { + if (gDisableStructs[gActiveBattler].substituteHP >= gBattleMoveDamage) + { + if (gSpecialStatuses[gActiveBattler].dmg == 0) + gSpecialStatuses[gActiveBattler].dmg = gBattleMoveDamage; + gDisableStructs[gActiveBattler].substituteHP -= gBattleMoveDamage; + gHpDealt = gBattleMoveDamage; + } + else + { + if (gSpecialStatuses[gActiveBattler].dmg == 0) + gSpecialStatuses[gActiveBattler].dmg = gDisableStructs[gActiveBattler].substituteHP; + gHpDealt = gDisableStructs[gActiveBattler].substituteHP; + gDisableStructs[gActiveBattler].substituteHP = 0; + } + // check substitute fading + if (gDisableStructs[gActiveBattler].substituteHP == 0) + { + gBattlescriptCurrInstr += 2; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_SubstituteFade; + return; + } + } + else + { + gHitMarker &= ~(HITMARKER_IGNORE_SUBSTITUTE); + if (gBattleMoveDamage < 0) // hp goes up + { + gBattleMons[gActiveBattler].hp -= gBattleMoveDamage; + if (gBattleMons[gActiveBattler].hp > gBattleMons[gActiveBattler].maxHP) + gBattleMons[gActiveBattler].hp = gBattleMons[gActiveBattler].maxHP; + + } + else // hp goes down + { + if (gHitMarker & HITMARKER_x20) + { + gHitMarker &= ~(HITMARKER_x20); + } + else + { + gTakenDmg[gActiveBattler] += gBattleMoveDamage; + if (gBattlescriptCurrInstr[1] == BS_TARGET) + gTakenDmgByBattler[gActiveBattler] = gBattlerAttacker; + else + gTakenDmgByBattler[gActiveBattler] = gBattlerTarget; + } + + if (gBattleMons[gActiveBattler].hp > gBattleMoveDamage) + { + gBattleMons[gActiveBattler].hp -= gBattleMoveDamage; + gHpDealt = gBattleMoveDamage; + } + else + { + gHpDealt = gBattleMons[gActiveBattler].hp; + gBattleMons[gActiveBattler].hp = 0; + } + if (!gSpecialStatuses[gActiveBattler].dmg && !(gHitMarker & HITMARKER_x100000)) + gSpecialStatuses[gActiveBattler].dmg = gHpDealt; + if (IS_TYPE_PHYSICAL(moveType) && !(gHitMarker & HITMARKER_x100000) && gCurrentMove != MOVE_PAIN_SPLIT) + { + gProtectStructs[gActiveBattler].physicalDmg = gHpDealt; + gSpecialStatuses[gActiveBattler].physicalDmg = gHpDealt; + if (gBattlescriptCurrInstr[1] == BS_TARGET) + { + gProtectStructs[gActiveBattler].physicalBattlerId = gBattlerAttacker; + gSpecialStatuses[gActiveBattler].physicalBattlerId = gBattlerAttacker; + } + else + { + gProtectStructs[gActiveBattler].physicalBattlerId = gBattlerTarget; + gSpecialStatuses[gActiveBattler].physicalBattlerId = gBattlerTarget; + } + } + else if (!IS_TYPE_PHYSICAL(moveType) && !(gHitMarker & HITMARKER_x100000)) + { + gProtectStructs[gActiveBattler].specialDmg = gHpDealt; + gSpecialStatuses[gActiveBattler].specialDmg = gHpDealt; + if (gBattlescriptCurrInstr[1] == BS_TARGET) + { + gProtectStructs[gActiveBattler].specialBattlerId = gBattlerAttacker; + gSpecialStatuses[gActiveBattler].specialBattlerId = gBattlerAttacker; + } + else + { + gProtectStructs[gActiveBattler].specialBattlerId = gBattlerTarget; + gSpecialStatuses[gActiveBattler].specialBattlerId = gBattlerTarget; + } + } + } + gHitMarker &= ~(HITMARKER_x100000); + BtlController_EmitSetMonData(0, REQUEST_HP_BATTLE, 0, 2, &gBattleMons[gActiveBattler].hp); + MarkBattlerForControllerExec(gActiveBattler); + } + } + else + { + gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + if (gSpecialStatuses[gActiveBattler].dmg == 0) + gSpecialStatuses[gActiveBattler].dmg = 0xFFFF; + } + gBattlescriptCurrInstr += 2; + } +} + +static void atk0D_critmessage(void) +{ + if (!gBattleControllerExecFlags) + { + if (gCritMultiplier == 2 && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)) + { + PrepareStringBattle(STRINGID_CRITICALHIT, gBattlerAttacker); + gBattleCommunication[MSG_DISPLAY] = 1; + } + ++gBattlescriptCurrInstr; + } +} + +static void atk0E_effectivenesssound(void) +{ + if (!gBattleControllerExecFlags) + { + gActiveBattler = gBattlerTarget; + if (!(gMoveResultFlags & MOVE_RESULT_MISSED)) + { + switch (gMoveResultFlags & (u8)(~(MOVE_RESULT_MISSED))) + { + case MOVE_RESULT_SUPER_EFFECTIVE: + BtlController_EmitPlaySE(0, SE_KOUKA_H); + MarkBattlerForControllerExec(gActiveBattler); + break; + case MOVE_RESULT_NOT_VERY_EFFECTIVE: + BtlController_EmitPlaySE(0, SE_KOUKA_L); + MarkBattlerForControllerExec(gActiveBattler); + break; + case MOVE_RESULT_DOESNT_AFFECT_FOE: + case MOVE_RESULT_FAILED: + // no sound + break; + case MOVE_RESULT_FOE_ENDURED: + case MOVE_RESULT_ONE_HIT_KO: + case MOVE_RESULT_FOE_HUNG_ON: + default: + if (gMoveResultFlags & MOVE_RESULT_SUPER_EFFECTIVE) + { + BtlController_EmitPlaySE(0, SE_KOUKA_H); + MarkBattlerForControllerExec(gActiveBattler); + } + else if (gMoveResultFlags & MOVE_RESULT_NOT_VERY_EFFECTIVE) + { + BtlController_EmitPlaySE(0, SE_KOUKA_L); + MarkBattlerForControllerExec(gActiveBattler); + } + else if (!(gMoveResultFlags & (MOVE_RESULT_DOESNT_AFFECT_FOE | MOVE_RESULT_FAILED))) + { + BtlController_EmitPlaySE(0, SE_KOUKA_M); + MarkBattlerForControllerExec(gActiveBattler); + } + break; + } + } + ++gBattlescriptCurrInstr; + } +} + +static void atk0F_resultmessage(void) +{ + u32 stringId = 0; + + if (!gBattleControllerExecFlags) + { + if (gMoveResultFlags & MOVE_RESULT_MISSED && (!(gMoveResultFlags & MOVE_RESULT_DOESNT_AFFECT_FOE) || gBattleCommunication[6] > 2)) + { + stringId = gMissStringIds[gBattleCommunication[6]]; + gBattleCommunication[MSG_DISPLAY] = 1; + } + else + { + gBattleCommunication[MSG_DISPLAY] = 1; + switch (gMoveResultFlags & (u8)(~(MOVE_RESULT_MISSED))) + { + case MOVE_RESULT_SUPER_EFFECTIVE: + stringId = STRINGID_SUPEREFFECTIVE; + break; + case MOVE_RESULT_NOT_VERY_EFFECTIVE: + stringId = STRINGID_NOTVERYEFFECTIVE; + break; + case MOVE_RESULT_ONE_HIT_KO: + stringId = STRINGID_ONEHITKO; + break; + case MOVE_RESULT_FOE_ENDURED: + stringId = STRINGID_PKMNENDUREDHIT; + break; + case MOVE_RESULT_FAILED: + stringId = STRINGID_BUTITFAILED; + break; + case MOVE_RESULT_DOESNT_AFFECT_FOE: + stringId = STRINGID_ITDOESNTAFFECT; + break; + case MOVE_RESULT_FOE_HUNG_ON: + gLastUsedItem = gBattleMons[gBattlerTarget].item; + gPotentialItemEffectBattler = gBattlerTarget; + gMoveResultFlags &= ~(MOVE_RESULT_FOE_ENDURED | MOVE_RESULT_FOE_HUNG_ON); + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_HangedOnMsg; + return; + default: + if (gMoveResultFlags & MOVE_RESULT_DOESNT_AFFECT_FOE) + { + stringId = STRINGID_ITDOESNTAFFECT; + } + else if (gMoveResultFlags & MOVE_RESULT_ONE_HIT_KO) + { + gMoveResultFlags &= ~(MOVE_RESULT_ONE_HIT_KO); + gMoveResultFlags &= ~(MOVE_RESULT_SUPER_EFFECTIVE); + gMoveResultFlags &= ~(MOVE_RESULT_NOT_VERY_EFFECTIVE); + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_OneHitKOMsg; + return; + } + else if (gMoveResultFlags & MOVE_RESULT_FOE_ENDURED) + { + gMoveResultFlags &= ~(MOVE_RESULT_FOE_ENDURED | MOVE_RESULT_FOE_HUNG_ON); + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_EnduredMsg; + return; + } + else if (gMoveResultFlags & MOVE_RESULT_FOE_HUNG_ON) + { + gLastUsedItem = gBattleMons[gBattlerTarget].item; + gPotentialItemEffectBattler = gBattlerTarget; + gMoveResultFlags &= ~(MOVE_RESULT_FOE_ENDURED | MOVE_RESULT_FOE_HUNG_ON); + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_HangedOnMsg; + return; + } + else if (gMoveResultFlags & MOVE_RESULT_FAILED) + { + stringId = STRINGID_BUTITFAILED; + } + else + { + gBattleCommunication[MSG_DISPLAY] = 0; + } + } + } + if (stringId) + PrepareStringBattle(stringId, gBattlerAttacker); + ++gBattlescriptCurrInstr; + } +} + +static void atk10_printstring(void) +{ + if (!gBattleControllerExecFlags) + { + u16 var = T2_READ_16(gBattlescriptCurrInstr + 1); + + PrepareStringBattle(var, gBattlerAttacker); + gBattlescriptCurrInstr += 3; + gBattleCommunication[MSG_DISPLAY] = 1; + } +} + +static void atk11_printselectionstring(void) +{ + gActiveBattler = gBattlerAttacker; + BtlController_EmitPrintSelectionString(0, T2_READ_16(gBattlescriptCurrInstr + 1)); + MarkBattlerForControllerExec(gActiveBattler); + gBattlescriptCurrInstr += 3; + gBattleCommunication[MSG_DISPLAY] = 1; +} + +static void atk12_waitmessage(void) +{ + if (!gBattleControllerExecFlags) + { + if (!gBattleCommunication[MSG_DISPLAY]) + { + gBattlescriptCurrInstr += 3; + } + else + { + u16 toWait = T2_READ_16(gBattlescriptCurrInstr + 1); + + if (++gPauseCounterBattle >= toWait) + { + gPauseCounterBattle = 0; + gBattlescriptCurrInstr += 3; + gBattleCommunication[MSG_DISPLAY] = 0; + } + } + } +} + +static void atk13_printfromtable(void) +{ + if (!gBattleControllerExecFlags) + { + const u16 *ptr = (const u16 *) T1_READ_PTR(gBattlescriptCurrInstr + 1); + + ptr += gBattleCommunication[MULTISTRING_CHOOSER]; + PrepareStringBattle(*ptr, gBattlerAttacker); + gBattlescriptCurrInstr += 5; + gBattleCommunication[MSG_DISPLAY] = 1; + } +} + +static void atk14_printselectionstringfromtable(void) +{ + if (!gBattleControllerExecFlags) + { + const u16 *ptr = (const u16 *) T1_READ_PTR(gBattlescriptCurrInstr + 1); + + ptr += gBattleCommunication[MULTISTRING_CHOOSER]; + gActiveBattler = gBattlerAttacker; + BtlController_EmitPrintSelectionString(0, *ptr); + MarkBattlerForControllerExec(gActiveBattler); + gBattlescriptCurrInstr += 5; + gBattleCommunication[MSG_DISPLAY] = 1; + } +} + +u8 GetBattlerTurnOrderNum(u8 battlerId) +{ + s32 i; + + for (i = 0; i < gBattlersCount && gBattlerByTurnOrder[i] != battlerId; ++i); + return i; +} + +void SetMoveEffect(bool8 primary, u8 certain) +{ + bool32 statusChanged = FALSE; + u8 affectsUser = 0; // 0x40 otherwise + bool32 noSunCanFreeze = TRUE; + + if (gBattleCommunication[MOVE_EFFECT_BYTE] & MOVE_EFFECT_AFFECTS_USER) + { + gEffectBattler = gBattlerAttacker; // battlerId that effects get applied on + gBattleCommunication[MOVE_EFFECT_BYTE] &= ~(MOVE_EFFECT_AFFECTS_USER); + affectsUser = MOVE_EFFECT_AFFECTS_USER; + gBattleScripting.battler = gBattlerTarget; // theoretically the attacker + } + else + { + gEffectBattler = gBattlerTarget; + gBattleScripting.battler = gBattlerAttacker; + } + if (gBattleTypeFlags & BATTLE_TYPE_POKEDUDE + && gBattleCommunication[MOVE_EFFECT_BYTE] != 1 + && GetBattlerSide(gEffectBattler) == B_SIDE_OPPONENT) + { + ++gBattlescriptCurrInstr; + return; + } + if (gBattleMons[gEffectBattler].ability == ABILITY_SHIELD_DUST + && !(gHitMarker & HITMARKER_IGNORE_SAFEGUARD) + && !primary + && gBattleCommunication[MOVE_EFFECT_BYTE] <= 9) + { + ++gBattlescriptCurrInstr; + return; + } + if (gSideStatuses[GET_BATTLER_SIDE(gEffectBattler)] & SIDE_STATUS_SAFEGUARD + && !(gHitMarker & HITMARKER_IGNORE_SAFEGUARD) + && !primary + && gBattleCommunication[MOVE_EFFECT_BYTE] <= 7) + { + ++gBattlescriptCurrInstr; + return; + } + if (gBattleMons[gEffectBattler].hp == 0 + && gBattleCommunication[MOVE_EFFECT_BYTE] != MOVE_EFFECT_PAYDAY + && gBattleCommunication[MOVE_EFFECT_BYTE] != MOVE_EFFECT_STEAL_ITEM) + { + ++gBattlescriptCurrInstr; + return; + } + if (gBattleMons[gEffectBattler].status2 & STATUS2_SUBSTITUTE + && affectsUser != MOVE_EFFECT_AFFECTS_USER) + { + ++gBattlescriptCurrInstr; + return; + } + if (gBattleCommunication[MOVE_EFFECT_BYTE] <= 6) // status change + { + switch (sStatusFlagsForMoveEffects[gBattleCommunication[MOVE_EFFECT_BYTE]]) + { + case STATUS1_SLEEP: + // check active uproar + if (gBattleMons[gEffectBattler].ability != ABILITY_SOUNDPROOF) + for (gActiveBattler = 0; + gActiveBattler < gBattlersCount && !(gBattleMons[gActiveBattler].status2 & STATUS2_UPROAR); + ++gActiveBattler); + else + gActiveBattler = gBattlersCount; + if (gBattleMons[gEffectBattler].status1) + break; + if (gActiveBattler != gBattlersCount) + break; + if (gBattleMons[gEffectBattler].ability == ABILITY_VITAL_SPIRIT) + break; + if (gBattleMons[gEffectBattler].ability == ABILITY_INSOMNIA) + break; + CancelMultiTurnMoves(gEffectBattler); + statusChanged = TRUE; + break; + case STATUS1_POISON: + if (gBattleMons[gEffectBattler].ability == ABILITY_IMMUNITY + && (primary == TRUE || certain == MOVE_EFFECT_CERTAIN)) + { + gLastUsedAbility = ABILITY_IMMUNITY; + RecordAbilityBattle(gEffectBattler, ABILITY_IMMUNITY); + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_PSNPrevention; + if (gHitMarker & HITMARKER_IGNORE_SAFEGUARD) + { + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + gHitMarker &= ~(HITMARKER_IGNORE_SAFEGUARD); + } + else + { + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + } + return; + } + if ((IS_BATTLER_OF_TYPE(gEffectBattler, TYPE_POISON) || IS_BATTLER_OF_TYPE(gEffectBattler, TYPE_STEEL)) + && (gHitMarker & HITMARKER_IGNORE_SAFEGUARD) + && (primary == TRUE || certain == MOVE_EFFECT_CERTAIN)) + { + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_PSNPrevention; + gBattleCommunication[MULTISTRING_CHOOSER] = 2; + return; + } + if (IS_BATTLER_OF_TYPE(gEffectBattler, TYPE_POISON)) + break; + if (IS_BATTLER_OF_TYPE(gEffectBattler, TYPE_STEEL)) + break; + if (gBattleMons[gEffectBattler].status1) + break; + if (gBattleMons[gEffectBattler].ability == ABILITY_IMMUNITY) + break; + statusChanged = TRUE; + break; + case STATUS1_BURN: + if (gBattleMons[gEffectBattler].ability == ABILITY_WATER_VEIL + && (primary == TRUE || certain == MOVE_EFFECT_CERTAIN)) + { + gLastUsedAbility = ABILITY_WATER_VEIL; + RecordAbilityBattle(gEffectBattler, ABILITY_WATER_VEIL); + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_BRNPrevention; + if (gHitMarker & HITMARKER_IGNORE_SAFEGUARD) + { + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + gHitMarker &= ~(HITMARKER_IGNORE_SAFEGUARD); + } + else + { + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + } + return; + } + if (IS_BATTLER_OF_TYPE(gEffectBattler, TYPE_FIRE) + && (gHitMarker & HITMARKER_IGNORE_SAFEGUARD) + && (primary == TRUE || certain == MOVE_EFFECT_CERTAIN)) + { + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_BRNPrevention; + gBattleCommunication[MULTISTRING_CHOOSER] = 2; + return; + } + if (IS_BATTLER_OF_TYPE(gEffectBattler, TYPE_FIRE)) + break; + if (gBattleMons[gEffectBattler].ability == ABILITY_WATER_VEIL) + break; + if (gBattleMons[gEffectBattler].status1) + break; + statusChanged = TRUE; + break; + case STATUS1_FREEZE: + if (WEATHER_HAS_EFFECT && gBattleWeather & WEATHER_SUN_ANY) + noSunCanFreeze = FALSE; + if (IS_BATTLER_OF_TYPE(gEffectBattler, TYPE_ICE)) + break; + if (gBattleMons[gEffectBattler].status1) + break; + if (noSunCanFreeze == 0) + break; + if (gBattleMons[gEffectBattler].ability == ABILITY_MAGMA_ARMOR) + break; + CancelMultiTurnMoves(gEffectBattler); + statusChanged = TRUE; + break; + case STATUS1_PARALYSIS: + if (gBattleMons[gEffectBattler].ability == ABILITY_LIMBER) + { + if (primary == TRUE || certain == MOVE_EFFECT_CERTAIN) + { + gLastUsedAbility = ABILITY_LIMBER; + RecordAbilityBattle(gEffectBattler, ABILITY_LIMBER); + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_PRLZPrevention; + if (gHitMarker & HITMARKER_IGNORE_SAFEGUARD) + { + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + gHitMarker &= ~(HITMARKER_IGNORE_SAFEGUARD); + } + else + { + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + } + return; + } + else + break; + } + if (gBattleMons[gEffectBattler].status1) + break; + statusChanged = TRUE; + break; + case STATUS1_TOXIC_POISON: + if (gBattleMons[gEffectBattler].ability == ABILITY_IMMUNITY && (primary == TRUE || certain == MOVE_EFFECT_CERTAIN)) + { + gLastUsedAbility = ABILITY_IMMUNITY; + RecordAbilityBattle(gEffectBattler, ABILITY_IMMUNITY); + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_PSNPrevention; + if (gHitMarker & HITMARKER_IGNORE_SAFEGUARD) + { + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + gHitMarker &= ~(HITMARKER_IGNORE_SAFEGUARD); + } + else + { + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + } + return; + } + if ((IS_BATTLER_OF_TYPE(gEffectBattler, TYPE_POISON) || IS_BATTLER_OF_TYPE(gEffectBattler, TYPE_STEEL)) + && (gHitMarker & HITMARKER_IGNORE_SAFEGUARD) + && (primary == TRUE || certain == MOVE_EFFECT_CERTAIN)) + { + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_PSNPrevention; + gBattleCommunication[MULTISTRING_CHOOSER] = 2; + return; + } + if (gBattleMons[gEffectBattler].status1) + break; + if (!IS_BATTLER_OF_TYPE(gEffectBattler, TYPE_POISON) && !IS_BATTLER_OF_TYPE(gEffectBattler, TYPE_STEEL)) + { + if (gBattleMons[gEffectBattler].ability == ABILITY_IMMUNITY) + break; + // It's redundant, because at this point we know the status1 value is 0. + gBattleMons[gEffectBattler].status1 &= ~(STATUS1_TOXIC_POISON); + gBattleMons[gEffectBattler].status1 &= ~(STATUS1_POISON); + statusChanged = TRUE; + break; + } + else + { + gMoveResultFlags |= MOVE_RESULT_DOESNT_AFFECT_FOE; + } + break; + } + if (statusChanged == TRUE) + { + BattleScriptPush(gBattlescriptCurrInstr + 1); + if (sStatusFlagsForMoveEffects[gBattleCommunication[MOVE_EFFECT_BYTE]] == STATUS1_SLEEP) + gBattleMons[gEffectBattler].status1 |= ((Random() & 3) + 2); + else + gBattleMons[gEffectBattler].status1 |= sStatusFlagsForMoveEffects[gBattleCommunication[MOVE_EFFECT_BYTE]]; + gBattlescriptCurrInstr = sMoveEffectBS_Ptrs[gBattleCommunication[MOVE_EFFECT_BYTE]]; + gActiveBattler = gEffectBattler; + BtlController_EmitSetMonData(0, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[gEffectBattler].status1); + MarkBattlerForControllerExec(gActiveBattler); + if (gHitMarker & HITMARKER_IGNORE_SAFEGUARD) + { + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + gHitMarker &= ~(HITMARKER_IGNORE_SAFEGUARD); + } + else + { + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + } + // for synchronize + if (gBattleCommunication[MOVE_EFFECT_BYTE] == MOVE_EFFECT_POISON + || gBattleCommunication[MOVE_EFFECT_BYTE] == MOVE_EFFECT_TOXIC + || gBattleCommunication[MOVE_EFFECT_BYTE] == MOVE_EFFECT_PARALYSIS + || gBattleCommunication[MOVE_EFFECT_BYTE] == MOVE_EFFECT_BURN) + { + u8 *synchronizeEffect = &gBattleStruct->synchronizeMoveEffect; + *synchronizeEffect = gBattleCommunication[MOVE_EFFECT_BYTE]; + gHitMarker |= HITMARKER_SYNCHRONISE_EFFECT; + } + } + else if (statusChanged == FALSE) + { + ++gBattlescriptCurrInstr; + } + return; + } + else + { + if (gBattleMons[gEffectBattler].status2 & sStatusFlagsForMoveEffects[gBattleCommunication[MOVE_EFFECT_BYTE]]) + { + ++gBattlescriptCurrInstr; + } + else + { + u8 side; + + switch (gBattleCommunication[MOVE_EFFECT_BYTE]) + { + case MOVE_EFFECT_CONFUSION: + if (gBattleMons[gEffectBattler].ability == ABILITY_OWN_TEMPO + || gBattleMons[gEffectBattler].status2 & STATUS2_CONFUSION) + { + ++gBattlescriptCurrInstr; + } + else + { + gBattleMons[gEffectBattler].status2 |= (((Random()) % 0x4)) + 2; + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = sMoveEffectBS_Ptrs[gBattleCommunication[MOVE_EFFECT_BYTE]]; + } + break; + case MOVE_EFFECT_FLINCH: + if (gBattleMons[gEffectBattler].ability == ABILITY_INNER_FOCUS) + { + if (primary == TRUE || certain == MOVE_EFFECT_CERTAIN) + { + gLastUsedAbility = ABILITY_INNER_FOCUS; + RecordAbilityBattle(gEffectBattler, ABILITY_INNER_FOCUS); + gBattlescriptCurrInstr = BattleScript_FlinchPrevention; + } + else + { + ++gBattlescriptCurrInstr; + } + } + else + { + if (GetBattlerTurnOrderNum(gEffectBattler) > gCurrentTurnActionNumber) + gBattleMons[gEffectBattler].status2 |= sStatusFlagsForMoveEffects[gBattleCommunication[MOVE_EFFECT_BYTE]]; + ++gBattlescriptCurrInstr; + } + break; + case MOVE_EFFECT_UPROAR: + if (!(gBattleMons[gEffectBattler].status2 & STATUS2_UPROAR)) + { + gBattleMons[gEffectBattler].status2 |= STATUS2_MULTIPLETURNS; + gLockedMoves[gEffectBattler] = gCurrentMove; + gBattleMons[gEffectBattler].status2 |= ((Random() & 3) + 2) << 4; + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = sMoveEffectBS_Ptrs[gBattleCommunication[MOVE_EFFECT_BYTE]]; + } + else + { + ++gBattlescriptCurrInstr; + } + break; + case MOVE_EFFECT_PAYDAY: + if (GET_BATTLER_SIDE(gBattlerAttacker) == B_SIDE_PLAYER) + { + u16 PayDay = gPaydayMoney; + gPaydayMoney += (gBattleMons[gBattlerAttacker].level * 5); + if (PayDay > gPaydayMoney) + gPaydayMoney = 0xFFFF; + } + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = sMoveEffectBS_Ptrs[gBattleCommunication[MOVE_EFFECT_BYTE]]; + break; + case MOVE_EFFECT_TRI_ATTACK: + if (gBattleMons[gEffectBattler].status1) + { + ++gBattlescriptCurrInstr; + } + else + { + gBattleCommunication[MOVE_EFFECT_BYTE] = Random() % 3 + 3; + SetMoveEffect(FALSE, 0); + } + break; + case MOVE_EFFECT_CHARGING: + gBattleMons[gEffectBattler].status2 |= STATUS2_MULTIPLETURNS; + gLockedMoves[gEffectBattler] = gCurrentMove; + gProtectStructs[gEffectBattler].chargingTurn = 1; + ++gBattlescriptCurrInstr; + break; + case MOVE_EFFECT_WRAP: + if (gBattleMons[gEffectBattler].status2 & STATUS2_WRAPPED) + { + ++gBattlescriptCurrInstr; + } + else + { + gBattleMons[gEffectBattler].status2 |= ((Random() & 3) + 3) << 0xD; + *(gBattleStruct->wrappedMove + gEffectBattler * 2 + 0) = gCurrentMove; + *(gBattleStruct->wrappedMove + gEffectBattler * 2 + 1) = gCurrentMove >> 8; + *(gBattleStruct->wrappedBy + gEffectBattler) = gBattlerAttacker; + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = sMoveEffectBS_Ptrs[gBattleCommunication[MOVE_EFFECT_BYTE]]; + for (gBattleCommunication[MULTISTRING_CHOOSER] = 0; ; ++gBattleCommunication[MULTISTRING_CHOOSER]) + { + if (gBattleCommunication[MULTISTRING_CHOOSER] > 4 || gTrappingMoves[gBattleCommunication[MULTISTRING_CHOOSER]] == gCurrentMove) + break; + } + } + break; + case MOVE_EFFECT_RECOIL_25: // 25% recoil + gBattleMoveDamage = (gHpDealt) / 4; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = sMoveEffectBS_Ptrs[gBattleCommunication[MOVE_EFFECT_BYTE]]; + break; + case MOVE_EFFECT_ATK_PLUS_1: + case MOVE_EFFECT_DEF_PLUS_1: + case MOVE_EFFECT_SPD_PLUS_1: + case MOVE_EFFECT_SP_ATK_PLUS_1: + case MOVE_EFFECT_SP_DEF_PLUS_1: + case MOVE_EFFECT_ACC_PLUS_1: + case MOVE_EFFECT_EVS_PLUS_1: + if (ChangeStatBuffs(SET_STAT_BUFF_VALUE(1), + gBattleCommunication[MOVE_EFFECT_BYTE] - MOVE_EFFECT_ATK_PLUS_1 + 1, + affectsUser, + NULL)) + { + ++gBattlescriptCurrInstr; + } + else + { + gBattleScripting.animArg1 = gBattleCommunication[MOVE_EFFECT_BYTE] & ~(MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_CERTAIN); + gBattleScripting.animArg2 = 0; + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_StatUp; + } + break; + case MOVE_EFFECT_ATK_MINUS_1: + case MOVE_EFFECT_DEF_MINUS_1: + case MOVE_EFFECT_SPD_MINUS_1: + case MOVE_EFFECT_SP_ATK_MINUS_1: + case MOVE_EFFECT_SP_DEF_MINUS_1: + case MOVE_EFFECT_ACC_MINUS_1: + case MOVE_EFFECT_EVS_MINUS_1: + if (ChangeStatBuffs(SET_STAT_BUFF_VALUE(1) | STAT_BUFF_NEGATIVE, + gBattleCommunication[MOVE_EFFECT_BYTE] - MOVE_EFFECT_ATK_MINUS_1 + 1, + affectsUser, + NULL)) + { + ++gBattlescriptCurrInstr; + } + else + { + gBattleScripting.animArg1 = gBattleCommunication[MOVE_EFFECT_BYTE] & ~(MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_CERTAIN); + gBattleScripting.animArg2 = 0; + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_StatDown; + } + break; + case MOVE_EFFECT_ATK_PLUS_2: + case MOVE_EFFECT_DEF_PLUS_2: + case MOVE_EFFECT_SPD_PLUS_2: + case MOVE_EFFECT_SP_ATK_PLUS_2: + case MOVE_EFFECT_SP_DEF_PLUS_2: + case MOVE_EFFECT_ACC_PLUS_2: + case MOVE_EFFECT_EVS_PLUS_2: + if (ChangeStatBuffs(SET_STAT_BUFF_VALUE(2), + gBattleCommunication[MOVE_EFFECT_BYTE] - MOVE_EFFECT_ATK_PLUS_2 + 1, + affectsUser, + NULL)) + { + ++gBattlescriptCurrInstr; + } + else + { + gBattleScripting.animArg1 = gBattleCommunication[MOVE_EFFECT_BYTE] & ~(MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_CERTAIN); + gBattleScripting.animArg2 = 0; + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_StatUp; + } + break; + case MOVE_EFFECT_ATK_MINUS_2: + case MOVE_EFFECT_DEF_MINUS_2: + case MOVE_EFFECT_SPD_MINUS_2: + case MOVE_EFFECT_SP_ATK_MINUS_2: + case MOVE_EFFECT_SP_DEF_MINUS_2: + case MOVE_EFFECT_ACC_MINUS_2: + case MOVE_EFFECT_EVS_MINUS_2: + if (ChangeStatBuffs(SET_STAT_BUFF_VALUE(2) | STAT_BUFF_NEGATIVE, + gBattleCommunication[MOVE_EFFECT_BYTE] - MOVE_EFFECT_ATK_MINUS_2 + 1, + affectsUser, + NULL)) + { + ++gBattlescriptCurrInstr; + } + else + { + gBattleScripting.animArg1 = gBattleCommunication[MOVE_EFFECT_BYTE] & ~(MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_CERTAIN); + gBattleScripting.animArg2 = 0; + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_StatDown; + } + break; + case MOVE_EFFECT_RECHARGE: + gBattleMons[gEffectBattler].status2 |= STATUS2_RECHARGE; + gDisableStructs[gEffectBattler].rechargeTimer = 2; + gLockedMoves[gEffectBattler] = gCurrentMove; + ++gBattlescriptCurrInstr; + break; + case MOVE_EFFECT_RAGE: + gBattleMons[gBattlerAttacker].status2 |= STATUS2_RAGE; + ++gBattlescriptCurrInstr; + break; + case MOVE_EFFECT_STEAL_ITEM: + { + if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_TOWER) + { + ++gBattlescriptCurrInstr; + break; + } + side = GetBattlerSide(gBattlerAttacker); + if (GetBattlerSide(gBattlerAttacker) == B_SIDE_OPPONENT + && !(gBattleTypeFlags & + (BATTLE_TYPE_EREADER_TRAINER + | BATTLE_TYPE_BATTLE_TOWER + | BATTLE_TYPE_LINK)) + && gTrainerBattleOpponent_A != 0x400) + { + ++gBattlescriptCurrInstr; + } + else if (!(gBattleTypeFlags & + (BATTLE_TYPE_EREADER_TRAINER + | BATTLE_TYPE_BATTLE_TOWER + | BATTLE_TYPE_LINK)) + && gTrainerBattleOpponent_A != 0x400 + && (gWishFutureKnock.knockedOffMons[side] & gBitTable[gBattlerPartyIndexes[gBattlerAttacker]])) + { + ++gBattlescriptCurrInstr; + } + else if (gBattleMons[gBattlerTarget].item + && gBattleMons[gBattlerTarget].ability == ABILITY_STICKY_HOLD) + { + gBattlescriptCurrInstr = BattleScript_StickyHoldActivates; + gLastUsedAbility = gBattleMons[gBattlerTarget].ability; + RecordAbilityBattle(gBattlerTarget, gLastUsedAbility); + } + else if (gBattleMons[gBattlerAttacker].item != ITEM_NONE + || gBattleMons[gBattlerTarget].item == ITEM_ENIGMA_BERRY + || IS_ITEM_MAIL(gBattleMons[gBattlerTarget].item) + || gBattleMons[gBattlerTarget].item == ITEM_NONE) + { + ++gBattlescriptCurrInstr; + } + else + { + u16 *changedItem = &gBattleStruct->changedItems[gBattlerAttacker]; + gLastUsedItem = *changedItem = gBattleMons[gBattlerTarget].item; + gBattleMons[gBattlerTarget].item = ITEM_NONE; + gActiveBattler = gBattlerAttacker; + BtlController_EmitSetMonData(0, REQUEST_HELDITEM_BATTLE, 0, 2, &gLastUsedItem); + MarkBattlerForControllerExec(gBattlerAttacker); + gActiveBattler = gBattlerTarget; + BtlController_EmitSetMonData(0, REQUEST_HELDITEM_BATTLE, 0, 2, &gBattleMons[gBattlerTarget].item); + MarkBattlerForControllerExec(gBattlerTarget); + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_ItemSteal; + *(u8 *)((u8 *)(&gBattleStruct->choicedMove[gBattlerTarget]) + 0) = 0; + *(u8 *)((u8 *)(&gBattleStruct->choicedMove[gBattlerTarget]) + 1) = 0; + } + } + break; + case MOVE_EFFECT_PREVENT_ESCAPE: + gBattleMons[gBattlerTarget].status2 |= STATUS2_ESCAPE_PREVENTION; + gDisableStructs[gBattlerTarget].battlerPreventingEscape = gBattlerAttacker; + ++gBattlescriptCurrInstr; + break; + case MOVE_EFFECT_NIGHTMARE: + gBattleMons[gBattlerTarget].status2 |= STATUS2_NIGHTMARE; + ++gBattlescriptCurrInstr; + break; + case MOVE_EFFECT_ALL_STATS_UP: + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_AllStatsUp; + break; + case MOVE_EFFECT_RAPIDSPIN: + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_RapidSpinAway; + break; + case MOVE_EFFECT_REMOVE_PARALYSIS: // Smelling salts + if (!(gBattleMons[gBattlerTarget].status1 & STATUS1_PARALYSIS)) + { + ++gBattlescriptCurrInstr; + } + else + { + gBattleMons[gBattlerTarget].status1 &= ~(STATUS1_PARALYSIS); + gActiveBattler = gBattlerTarget; + BtlController_EmitSetMonData(0, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[gActiveBattler].status1); + MarkBattlerForControllerExec(gActiveBattler); + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_TargetPRLZHeal; + } + break; + case MOVE_EFFECT_ATK_DEF_DOWN: // SuperPower + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_AtkDefDown; + break; + case MOVE_EFFECT_RECOIL_33: // Double Edge + gBattleMoveDamage = gHpDealt / 3; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = sMoveEffectBS_Ptrs[gBattleCommunication[MOVE_EFFECT_BYTE]]; + break; + case MOVE_EFFECT_THRASH: + if (gBattleMons[gEffectBattler].status2 & STATUS2_LOCK_CONFUSE) + { + ++gBattlescriptCurrInstr; + } + else + { + gBattleMons[gEffectBattler].status2 |= STATUS2_MULTIPLETURNS; + gLockedMoves[gEffectBattler] = gCurrentMove; + gBattleMons[gEffectBattler].status2 |= (((Random() & 1) + 2) << 0xA); + } + break; + case MOVE_EFFECT_KNOCK_OFF: + if (gBattleMons[gEffectBattler].ability == ABILITY_STICKY_HOLD) + { + if (gBattleMons[gEffectBattler].item == ITEM_NONE) + { + ++gBattlescriptCurrInstr; + } + else + { + gLastUsedAbility = ABILITY_STICKY_HOLD; + gBattlescriptCurrInstr = BattleScript_StickyHoldActivates; + RecordAbilityBattle(gEffectBattler, ABILITY_STICKY_HOLD); + } + break; + } + if (gBattleMons[gEffectBattler].item) + { + side = GetBattlerSide(gEffectBattler); + gLastUsedItem = gBattleMons[gEffectBattler].item; + gBattleMons[gEffectBattler].item = ITEM_NONE; + gWishFutureKnock.knockedOffMons[side] |= gBitTable[gBattlerPartyIndexes[gEffectBattler]]; + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_KnockedOff; + *(u8 *)((u8 *)(&gBattleStruct->choicedMove[gEffectBattler]) + 0) = 0; + *(u8 *)((u8 *)(&gBattleStruct->choicedMove[gEffectBattler]) + 1) = 0; + } + else + { + ++gBattlescriptCurrInstr; + } + break; + case MOVE_EFFECT_SP_ATK_TWO_DOWN: // Overheat + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_SAtkDown2; + break; + } + } + } +} + +static void atk15_seteffectwithchance(void) +{ + u32 percentChance; + + if (gBattleMons[gBattlerAttacker].ability == ABILITY_SERENE_GRACE) + percentChance = gBattleMoves[gCurrentMove].secondaryEffectChance * 2; + else + percentChance = gBattleMoves[gCurrentMove].secondaryEffectChance; + if (gBattleCommunication[MOVE_EFFECT_BYTE] & MOVE_EFFECT_CERTAIN + && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)) + { + gBattleCommunication[MOVE_EFFECT_BYTE] &= ~(MOVE_EFFECT_CERTAIN); + SetMoveEffect(0, MOVE_EFFECT_CERTAIN); + } + else if (Random() % 100 <= percentChance + && gBattleCommunication[MOVE_EFFECT_BYTE] + && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)) + { + if (percentChance >= 100) + SetMoveEffect(0, MOVE_EFFECT_CERTAIN); + else + SetMoveEffect(0, 0); + } + else + { + ++gBattlescriptCurrInstr; + } + gBattleCommunication[MOVE_EFFECT_BYTE] = 0; + gBattleScripting.multihitMoveEffect = 0; +} + +static void atk16_seteffectprimary(void) +{ + SetMoveEffect(TRUE, 0); +} + +static void atk17_seteffectsecondary(void) +{ + SetMoveEffect(FALSE, 0); +} + +static void atk18_clearstatusfromeffect(void) +{ + gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + + if (gBattleCommunication[MOVE_EFFECT_BYTE] <= MOVE_EFFECT_TOXIC) + gBattleMons[gActiveBattler].status1 &= (~sStatusFlagsForMoveEffects[gBattleCommunication[MOVE_EFFECT_BYTE]]); + else + gBattleMons[gActiveBattler].status2 &= (~sStatusFlagsForMoveEffects[gBattleCommunication[MOVE_EFFECT_BYTE]]); + gBattleCommunication[MOVE_EFFECT_BYTE] = 0; + gBattlescriptCurrInstr += 2; + gBattleScripting.multihitMoveEffect = 0; +} + +static void atk19_tryfaintmon(void) +{ + const u8 *BS_ptr; + + if (gBattlescriptCurrInstr[2] != 0) + { + gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + if (gHitMarker & HITMARKER_FAINTED(gActiveBattler)) + { + BS_ptr = T1_READ_PTR(gBattlescriptCurrInstr + 3); + BattleScriptPop(); + gBattlescriptCurrInstr = BS_ptr; + gSideStatuses[GetBattlerSide(gActiveBattler)] &= ~(SIDE_STATUS_SPIKES_DAMAGED); + } + else + { + gBattlescriptCurrInstr += 7; + } + } + else + { + u8 battlerId; + + if (gBattlescriptCurrInstr[1] == BS_ATTACKER) + { + gActiveBattler = gBattlerAttacker; + battlerId = gBattlerTarget; + BS_ptr = BattleScript_FaintAttacker; + } + else + { + gActiveBattler = gBattlerTarget; + battlerId = gBattlerAttacker; + BS_ptr = BattleScript_FaintTarget; + } + if (!(gAbsentBattlerFlags & gBitTable[gActiveBattler]) + && gBattleMons[gActiveBattler].hp == 0) + { + gHitMarker |= HITMARKER_FAINTED(gActiveBattler); + BattleScriptPush(gBattlescriptCurrInstr + 7); + gBattlescriptCurrInstr = BS_ptr; + if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER) + { + gHitMarker |= HITMARKER_x400000; + if (gBattleResults.playerFaintCounter < 0xFF) + ++gBattleResults.playerFaintCounter; + AdjustFriendshipOnBattleFaint(gActiveBattler); + } + else + { + if (gBattleResults.opponentFaintCounter < 0xFF) + ++gBattleResults.opponentFaintCounter; + gBattleResults.lastOpponentSpecies = GetMonData(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_SPECIES); + *(u8 *)(&gBattleStruct->field_182) = gBattlerAttacker; + } + if ((gHitMarker & HITMARKER_DESTINYBOND) && gBattleMons[gBattlerAttacker].hp != 0) + { + gHitMarker &= ~(HITMARKER_DESTINYBOND); + BattleScriptPush(gBattlescriptCurrInstr); + gBattleMoveDamage = gBattleMons[battlerId].hp; + gBattlescriptCurrInstr = BattleScript_DestinyBondTakesLife; + } + if ((gStatuses3[gBattlerTarget] & STATUS3_GRUDGE) + && !(gHitMarker & HITMARKER_GRUDGE) + && GetBattlerSide(gBattlerAttacker) != GetBattlerSide(gBattlerTarget) + && gBattleMons[gBattlerAttacker].hp != 0 + && gCurrentMove != MOVE_STRUGGLE) + { + u8 moveIndex = *(gBattleStruct->chosenMovePositions + gBattlerAttacker); + + gBattleMons[gBattlerAttacker].pp[moveIndex] = 0; + BattleScriptPush(gBattlescriptCurrInstr); + gBattlescriptCurrInstr = BattleScript_GrudgeTakesPp; + gActiveBattler = gBattlerAttacker; + BtlController_EmitSetMonData(0, moveIndex + REQUEST_PPMOVE1_BATTLE, 0, 1, &gBattleMons[gActiveBattler].pp[moveIndex]); + MarkBattlerForControllerExec(gActiveBattler); + PREPARE_MOVE_BUFFER(gBattleTextBuff1, gBattleMons[gBattlerAttacker].moves[moveIndex]) + } + } + else + { + gBattlescriptCurrInstr += 7; + } + } +} + +static void atk1A_dofaintanimation(void) +{ + if (!gBattleControllerExecFlags) + { + gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + BtlController_EmitFaintAnimation(0); + MarkBattlerForControllerExec(gActiveBattler); + gBattlescriptCurrInstr += 2; + } +} + +static void atk1B_cleareffectsonfaint(void) +{ + if (!gBattleControllerExecFlags) + { + gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + gBattleMons[gActiveBattler].status1 = 0; + BtlController_EmitSetMonData(0, REQUEST_STATUS_BATTLE, 0, 0x4, &gBattleMons[gActiveBattler].status1); + MarkBattlerForControllerExec(gActiveBattler); + FaintClearSetData(); // Effects like attractions, trapping, etc. + gBattlescriptCurrInstr += 2; + } +} + +static void atk1C_jumpifstatus(void) +{ + u8 battlerId = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + u32 flags = T2_READ_32(gBattlescriptCurrInstr + 2); + const u8 *jumpPtr = T2_READ_PTR(gBattlescriptCurrInstr + 6); + + if (gBattleMons[battlerId].status1 & flags && gBattleMons[battlerId].hp != 0) + gBattlescriptCurrInstr = jumpPtr; + else + gBattlescriptCurrInstr += 10; +} + +static void atk1D_jumpifstatus2(void) +{ + u8 battlerId = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + u32 flags = T2_READ_32(gBattlescriptCurrInstr + 2); + const u8 *jumpPtr = T2_READ_PTR(gBattlescriptCurrInstr + 6); + + if (gBattleMons[battlerId].status2 & flags && gBattleMons[battlerId].hp != 0) + gBattlescriptCurrInstr = jumpPtr; + else + gBattlescriptCurrInstr += 10; +} + +static void atk1E_jumpifability(void) +{ + u8 battlerId; + u8 ability = gBattlescriptCurrInstr[2]; + const u8 *jumpPtr = T2_READ_PTR(gBattlescriptCurrInstr + 3); + + if (gBattlescriptCurrInstr[1] == BS_ATTACKER_SIDE) + { + battlerId = AbilityBattleEffects(ABILITYEFFECT_CHECK_BATTLER_SIDE, gBattlerAttacker, ability, 0, 0); + if (battlerId) + { + gLastUsedAbility = ability; + gBattlescriptCurrInstr = jumpPtr; + RecordAbilityBattle(battlerId - 1, gLastUsedAbility); + gBattleScripting.battlerWithAbility = battlerId - 1; + } + else + gBattlescriptCurrInstr += 7; + } + else if (gBattlescriptCurrInstr[1] == BS_NOT_ATTACKER_SIDE) + { + battlerId = AbilityBattleEffects(ABILITYEFFECT_CHECK_OTHER_SIDE, gBattlerAttacker, ability, 0, 0); + if (battlerId) + { + gLastUsedAbility = ability; + gBattlescriptCurrInstr = jumpPtr; + RecordAbilityBattle(battlerId - 1, gLastUsedAbility); + gBattleScripting.battlerWithAbility = battlerId - 1; + } + else + gBattlescriptCurrInstr += 7; + } + else + { + battlerId = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + if (gBattleMons[battlerId].ability == ability) + { + gLastUsedAbility = ability; + gBattlescriptCurrInstr = jumpPtr; + RecordAbilityBattle(battlerId, gLastUsedAbility); + gBattleScripting.battlerWithAbility = battlerId; + } + else + gBattlescriptCurrInstr += 7; + } +} + +static void atk1F_jumpifsideaffecting(void) +{ + u8 side; + u16 flags; + const u8 *jumpPtr; + + if (gBattlescriptCurrInstr[1] == BS_ATTACKER) + side = GET_BATTLER_SIDE(gBattlerAttacker); + else + side = GET_BATTLER_SIDE(gBattlerTarget); + + flags = T2_READ_16(gBattlescriptCurrInstr + 2); + jumpPtr = T2_READ_PTR(gBattlescriptCurrInstr + 4); + + if (gSideStatuses[side] & flags) + gBattlescriptCurrInstr = jumpPtr; + else + gBattlescriptCurrInstr += 8; +} + +static void atk20_jumpifstat(void) +{ + u8 ret = 0; + u8 battlerId = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + u8 value = gBattleMons[battlerId].statStages[gBattlescriptCurrInstr[3]]; + + switch (gBattlescriptCurrInstr[2]) + { + case CMP_EQUAL: + if (value == gBattlescriptCurrInstr[4]) + ++ret; + break; + case CMP_NOT_EQUAL: + if (value != gBattlescriptCurrInstr[4]) + ++ret; + break; + case CMP_GREATER_THAN: + if (value > gBattlescriptCurrInstr[4]) + ++ret; + break; + case CMP_LESS_THAN: + if (value < gBattlescriptCurrInstr[4]) + ++ret; + break; + case CMP_COMMON_BITS: + if (value & gBattlescriptCurrInstr[4]) + ++ret; + break; + case CMP_NO_COMMON_BITS: + if (!(value & gBattlescriptCurrInstr[4])) + ++ret; + break; + } + if (ret) + gBattlescriptCurrInstr = T2_READ_PTR(gBattlescriptCurrInstr + 5); + else + gBattlescriptCurrInstr += 9; +} + +static void atk21_jumpifstatus3condition(void) +{ + u32 flags; + const u8 *jumpPtr; + + gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + flags = T2_READ_32(gBattlescriptCurrInstr + 2); + jumpPtr = T2_READ_PTR(gBattlescriptCurrInstr + 7); + if (gBattlescriptCurrInstr[6]) + { + if (gStatuses3[gActiveBattler] & flags) + gBattlescriptCurrInstr += 11; + else + gBattlescriptCurrInstr = jumpPtr; + } + else + { + if (gStatuses3[gActiveBattler] & flags) + gBattlescriptCurrInstr = jumpPtr; + else + gBattlescriptCurrInstr += 11; + } +} + +static void atk22_jumpiftype(void) +{ + u8 battlerId = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + u8 type = gBattlescriptCurrInstr[2]; + const u8 *jumpPtr = T2_READ_PTR(gBattlescriptCurrInstr + 3); + + if (IS_BATTLER_OF_TYPE(battlerId, type)) + gBattlescriptCurrInstr = jumpPtr; + else + gBattlescriptCurrInstr += 7; +} + +static void atk23_getexp(void) +{ + u16 item; + s32 i; // also used as stringId + u8 holdEffect; + s32 sentIn; + s32 viaExpShare = 0; + u16 *exp = &gBattleStruct->expValue; + + gBattlerFainted = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + sentIn = gSentPokesToOpponent[(gBattlerFainted & 2) >> 1]; + switch (gBattleScripting.atk23_state) + { + case 0: // check if should receive exp at all + if (GetBattlerSide(gBattlerFainted) != B_SIDE_OPPONENT + || (gBattleTypeFlags & + (BATTLE_TYPE_LINK + | BATTLE_TYPE_TRAINER_TOWER + | BATTLE_TYPE_BATTLE_TOWER + | BATTLE_TYPE_SAFARI + | BATTLE_TYPE_EREADER_TRAINER))) + { + gBattleScripting.atk23_state = 6; // goto last case + } + else + { + ++gBattleScripting.atk23_state; + gBattleStruct->givenExpMons |= gBitTable[gBattlerPartyIndexes[gBattlerFainted]]; + } + break; + case 1: // calculate experience points to redistribute + { + u16 calculatedExp; + s32 viaSentIn; + + for (viaSentIn = 0, i = 0; i < PARTY_SIZE; ++i) + { + if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES) != SPECIES_NONE && GetMonData(&gPlayerParty[i], MON_DATA_HP) != 0) + { + if (gBitTable[i] & sentIn) + ++viaSentIn; + item = GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM); + if (item == ITEM_ENIGMA_BERRY) + holdEffect = gSaveBlock1Ptr->enigmaBerry.holdEffect; + else + holdEffect = ItemId_GetHoldEffect(item); + if (holdEffect == HOLD_EFFECT_EXP_SHARE) + ++viaExpShare; + } + } + calculatedExp = gBaseStats[gBattleMons[gBattlerFainted].species].expYield * gBattleMons[gBattlerFainted].level / 7; + if (viaExpShare) // at least one mon is getting exp via exp share + { + *exp = calculatedExp / 2 / viaSentIn; + if (*exp == 0) + *exp = 1; + gExpShareExp = calculatedExp / 2 / viaExpShare; + if (gExpShareExp == 0) + gExpShareExp = 1; + } + else + { + *exp = calculatedExp / viaSentIn; + if (*exp == 0) + *exp = 1; + gExpShareExp = 0; + } + ++gBattleScripting.atk23_state; + gBattleStruct->expGetterMonId = 0; + gBattleStruct->sentInPokes = sentIn; + } + // fall through + case 2: // set exp value to the poke in expgetter_id and print message + if (!gBattleControllerExecFlags) + { + item = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_HELD_ITEM); + if (item == ITEM_ENIGMA_BERRY) + holdEffect = gSaveBlock1Ptr->enigmaBerry.holdEffect; + else + holdEffect = ItemId_GetHoldEffect(item); + if (holdEffect != HOLD_EFFECT_EXP_SHARE && !(gBattleStruct->sentInPokes & 1)) + { + *(&gBattleStruct->sentInPokes) >>= 1; + gBattleScripting.atk23_state = 5; + gBattleMoveDamage = 0; // used for exp + } + else if (GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_LEVEL) == MAX_LEVEL) + { + *(&gBattleStruct->sentInPokes) >>= 1; + gBattleScripting.atk23_state = 5; + gBattleMoveDamage = 0; // used for exp + } + else + { + // music change in wild battle after fainting a poke + if (!(gBattleTypeFlags & (BATTLE_TYPE_TRAINER | BATTLE_TYPE_POKEDUDE)) + && gBattleMons[0].hp + && !gBattleStruct->wildVictorySong) + { + BattleStopLowHpSound(); + PlayBGM(BGM_FRLG_KACHI_WILD_POKEMON); + ++gBattleStruct->wildVictorySong; + } + if (GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_HP)) + { + if (gBattleStruct->sentInPokes & 1) + gBattleMoveDamage = *exp; + else + gBattleMoveDamage = 0; + if (holdEffect == HOLD_EFFECT_EXP_SHARE) + gBattleMoveDamage += gExpShareExp; + if (holdEffect == HOLD_EFFECT_LUCKY_EGG) + gBattleMoveDamage = (gBattleMoveDamage * 150) / 100; + if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) + gBattleMoveDamage = (gBattleMoveDamage * 150) / 100; + if (IsTradedMon(&gPlayerParty[gBattleStruct->expGetterMonId]) + && !(gBattleTypeFlags & BATTLE_TYPE_POKEDUDE)) + { + gBattleMoveDamage = (gBattleMoveDamage * 150) / 100; + i = STRINGID_ABOOSTED; + } + else + { + i = STRINGID_EMPTYSTRING4; + } + // get exp getter battlerId + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + { + if (!(gBattlerPartyIndexes[2] != gBattleStruct->expGetterMonId) && !(gAbsentBattlerFlags & gBitTable[2])) + gBattleStruct->expGetterBattlerId = 2; + else + { + if (!(gAbsentBattlerFlags & gBitTable[0])) + gBattleStruct->expGetterBattlerId = 0; + else + gBattleStruct->expGetterBattlerId = 2; + } + } + else + { + gBattleStruct->expGetterBattlerId = 0; + } + PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, gBattleStruct->expGetterBattlerId, gBattleStruct->expGetterMonId); + // buffer 'gained' or 'gained a boosted' + PREPARE_STRING_BUFFER(gBattleTextBuff2, i); + PREPARE_WORD_NUMBER_BUFFER(gBattleTextBuff3, 5, gBattleMoveDamage); + PrepareStringBattle(STRINGID_PKMNGAINEDEXP, gBattleStruct->expGetterBattlerId); + MonGainEVs(&gPlayerParty[gBattleStruct->expGetterMonId], gBattleMons[gBattlerFainted].species); + } + gBattleStruct->sentInPokes >>= 1; + ++gBattleScripting.atk23_state; + } + } + break; + case 3: // Set stats and give exp + if (!gBattleControllerExecFlags) + { + gBattleBufferB[gBattleStruct->expGetterBattlerId][0] = 0; + if (GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_HP) && GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_LEVEL) != MAX_LEVEL) + { + gBattleResources->beforeLvlUp->stats[STAT_HP] = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_MAX_HP); + gBattleResources->beforeLvlUp->stats[STAT_ATK] = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_ATK); + gBattleResources->beforeLvlUp->stats[STAT_DEF] = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_DEF); + gBattleResources->beforeLvlUp->stats[STAT_SPEED] = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_SPEED); + gBattleResources->beforeLvlUp->stats[STAT_SPATK] = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_SPATK); + gBattleResources->beforeLvlUp->stats[STAT_SPDEF] = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_SPDEF); + gActiveBattler = gBattleStruct->expGetterBattlerId; + BtlController_EmitExpUpdate(0, gBattleStruct->expGetterMonId, gBattleMoveDamage); + MarkBattlerForControllerExec(gActiveBattler); + } + ++gBattleScripting.atk23_state; + } + break; + case 4: // lvl up if necessary + if (!gBattleControllerExecFlags) + { + gActiveBattler = gBattleStruct->expGetterBattlerId; + if (gBattleBufferB[gActiveBattler][0] == CONTROLLER_TWORETURNVALUES && gBattleBufferB[gActiveBattler][1] == RET_VALUE_LEVELED_UP) + { + if (gBattleTypeFlags & BATTLE_TYPE_TRAINER && gBattlerPartyIndexes[gActiveBattler] == gBattleStruct->expGetterMonId) + HandleLowHpMusicChange(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], gActiveBattler); + PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, gActiveBattler, gBattleStruct->expGetterMonId); + PREPARE_BYTE_NUMBER_BUFFER(gBattleTextBuff2, 3, GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_LEVEL)); + BattleScriptPushCursor(); + gLeveledUpInBattle |= gBitTable[gBattleStruct->expGetterMonId]; + gBattlescriptCurrInstr = BattleScript_LevelUp; + gBattleMoveDamage = (gBattleBufferB[gActiveBattler][2] | (gBattleBufferB[gActiveBattler][3] << 8)); + AdjustFriendship(&gPlayerParty[gBattleStruct->expGetterMonId], 0); + // update battle mon structure after level up + if (gBattlerPartyIndexes[0] == gBattleStruct->expGetterMonId && gBattleMons[0].hp) + { + gBattleMons[0].level = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_LEVEL); + gBattleMons[0].hp = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_HP); + gBattleMons[0].maxHP = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_MAX_HP); + gBattleMons[0].attack = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_ATK); + gBattleMons[0].defense = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_DEF); + // Why is this duplicated? + gBattleMons[0].speed = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_SPEED); + gBattleMons[0].speed = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_SPEED); + gBattleMons[0].spAttack = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_SPATK); + gBattleMons[0].spDefense = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_SPDEF); + } + // What is else if? + if (gBattlerPartyIndexes[2] == gBattleStruct->expGetterMonId && gBattleMons[2].hp && (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)) + { + gBattleMons[2].level = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_LEVEL); + gBattleMons[2].hp = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_HP); + gBattleMons[2].maxHP = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_MAX_HP); + gBattleMons[2].attack = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_ATK); + gBattleMons[2].defense = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_DEF); + // Duplicated again, but this time there's no Sp Defense + gBattleMons[2].speed = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_SPEED); + gBattleMons[2].speed = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_SPEED); + gBattleMons[2].spAttack = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_SPATK); + } + gBattleScripting.atk23_state = 5; + } + else + { + gBattleMoveDamage = 0; + gBattleScripting.atk23_state = 5; + } + } + break; + case 5: // looper increment + if (gBattleMoveDamage) // there is exp to give, goto case 3 that gives exp + { + gBattleScripting.atk23_state = 3; + } + else + { + ++gBattleStruct->expGetterMonId; + if (gBattleStruct->expGetterMonId <= 5) + gBattleScripting.atk23_state = 2; // loop again + else + gBattleScripting.atk23_state = 6; // we're done + } + break; + case 6: // increment instruction + if (!gBattleControllerExecFlags) + { + // not sure why gf clears the item and ability here + gBattleMons[gBattlerFainted].item = ITEM_NONE; + gBattleMons[gBattlerFainted].ability = ABILITY_NONE; + gBattlescriptCurrInstr += 2; + } + break; + } +} + +static void atk24(void) +{ + u16 HP_count = 0; + s32 i; + + if (!gBattleControllerExecFlags) + { + for (i = 0; i < PARTY_SIZE; ++i) + if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES) && !GetMonData(&gPlayerParty[i], MON_DATA_IS_EGG)) + HP_count += GetMonData(&gPlayerParty[i], MON_DATA_HP); + if (HP_count == 0) + gBattleOutcome |= B_OUTCOME_LOST; + for (HP_count = 0, i = 0; i < PARTY_SIZE; ++i) + if (GetMonData(&gEnemyParty[i], MON_DATA_SPECIES) && !GetMonData(&gEnemyParty[i], MON_DATA_IS_EGG)) + HP_count += GetMonData(&gEnemyParty[i], MON_DATA_HP); + if (HP_count == 0) + gBattleOutcome |= B_OUTCOME_WON; + if (gBattleOutcome == 0 && (gBattleTypeFlags & BATTLE_TYPE_LINK)) + { + s32 foundPlayer; + s32 foundOpponent; + + for (foundPlayer = 0, i = 0; i < gBattlersCount; i += 2) + { + u32 *ptr = &gHitMarker; + u32 hitMarkerUnk = 0x10000000; + + ++i; + --i; + if ((hitMarkerUnk << i) & *ptr && !gSpecialStatuses[i].flag40) + ++foundPlayer; + } + for (foundOpponent = 0, i = 1; i < gBattlersCount; i += 2) + { + u32 *ptr = &gHitMarker; + u32 hitMarkerUnk = 0x10000000; + + { + u8 match; + + ++match; + --match; + } + if ((hitMarkerUnk << i) & *ptr && !gSpecialStatuses[i].flag40) + ++foundOpponent; + } + if (gBattleTypeFlags & BATTLE_TYPE_MULTI) + { + if (foundOpponent + foundPlayer > 1) + gBattlescriptCurrInstr = T2_READ_PTR(gBattlescriptCurrInstr + 1); + else + gBattlescriptCurrInstr += 5; + } + else + { + if (foundOpponent != 0 && foundPlayer != 0) + gBattlescriptCurrInstr = T2_READ_PTR(gBattlescriptCurrInstr + 1); + else + gBattlescriptCurrInstr += 5; + } + } + else + { + gBattlescriptCurrInstr += 5; + } + } +} + +static void MoveValuesCleanUp(void) +{ + gMoveResultFlags = 0; + gBattleScripting.dmgMultiplier = 1; + gCritMultiplier = 1; + gBattleCommunication[MOVE_EFFECT_BYTE] = 0; + gBattleCommunication[6] = 0; + gHitMarker &= ~(HITMARKER_DESTINYBOND); + gHitMarker &= ~(HITMARKER_SYNCHRONISE_EFFECT); +} + +static void atk25_movevaluescleanup(void) +{ + MoveValuesCleanUp(); + gBattlescriptCurrInstr += 1; +} + +static void atk26_setmultihit(void) +{ + gMultiHitCounter = gBattlescriptCurrInstr[1]; + gBattlescriptCurrInstr += 2; +} + +static void atk27_decrementmultihit(void) +{ + if (--gMultiHitCounter == 0) + gBattlescriptCurrInstr += 5; + else + gBattlescriptCurrInstr = T2_READ_PTR(gBattlescriptCurrInstr + 1); +} + +static void atk28_goto(void) +{ + gBattlescriptCurrInstr = T2_READ_PTR(gBattlescriptCurrInstr + 1); +} + +static void atk29_jumpifbyte(void) +{ + u8 caseID = gBattlescriptCurrInstr[1]; + const u8 *memByte = T2_READ_PTR(gBattlescriptCurrInstr + 2); + u8 value = gBattlescriptCurrInstr[6]; + const u8 *jumpPtr = T2_READ_PTR(gBattlescriptCurrInstr + 7); + + gBattlescriptCurrInstr += 11; + + switch (caseID) + { + case CMP_EQUAL: + if (*memByte == value) + gBattlescriptCurrInstr = jumpPtr; + break; + case CMP_NOT_EQUAL: + if (*memByte != value) + gBattlescriptCurrInstr = jumpPtr; + break; + case CMP_GREATER_THAN: + if (*memByte > value) + gBattlescriptCurrInstr = jumpPtr; + break; + case CMP_LESS_THAN: + if (*memByte < value) + gBattlescriptCurrInstr = jumpPtr; + break; + case CMP_COMMON_BITS: + if (*memByte & value) + gBattlescriptCurrInstr = jumpPtr; + break; + case CMP_NO_COMMON_BITS: + if (!(*memByte & value)) + gBattlescriptCurrInstr = jumpPtr; + break; + } +} + +static void atk2A_jumpifhalfword(void) +{ + u8 caseID = gBattlescriptCurrInstr[1]; + const u16 *memHword = T2_READ_PTR(gBattlescriptCurrInstr + 2); + u16 value = T2_READ_16(gBattlescriptCurrInstr + 6); + const u8 *jumpPtr = T2_READ_PTR(gBattlescriptCurrInstr + 8); + + gBattlescriptCurrInstr += 12; + switch (caseID) + { + case CMP_EQUAL: + if (*memHword == value) + gBattlescriptCurrInstr = jumpPtr; + break; + case CMP_NOT_EQUAL: + if (*memHword != value) + gBattlescriptCurrInstr = jumpPtr; + break; + case CMP_GREATER_THAN: + if (*memHword > value) + gBattlescriptCurrInstr = jumpPtr; + break; + case CMP_LESS_THAN: + if (*memHword < value) + gBattlescriptCurrInstr = jumpPtr; + break; + case CMP_COMMON_BITS: + if (*memHword & value) + gBattlescriptCurrInstr = jumpPtr; + break; + case CMP_NO_COMMON_BITS: + if (!(*memHword & value)) + gBattlescriptCurrInstr = jumpPtr; + break; + } +} + +static void atk2B_jumpifword(void) +{ + u8 caseID = gBattlescriptCurrInstr[1]; + const u32 *memWord = T2_READ_PTR(gBattlescriptCurrInstr + 2); + u32 value = T1_READ_32(gBattlescriptCurrInstr + 6); + const u8 *jumpPtr = T2_READ_PTR(gBattlescriptCurrInstr + 10); + + gBattlescriptCurrInstr += 14; + switch (caseID) + { + case CMP_EQUAL: + if (*memWord == value) + gBattlescriptCurrInstr = jumpPtr; + break; + case CMP_NOT_EQUAL: + if (*memWord != value) + gBattlescriptCurrInstr = jumpPtr; + break; + case CMP_GREATER_THAN: + if (*memWord > value) + gBattlescriptCurrInstr = jumpPtr; + break; + case CMP_LESS_THAN: + if (*memWord < value) + gBattlescriptCurrInstr = jumpPtr; + break; + case CMP_COMMON_BITS: + if (*memWord & value) + gBattlescriptCurrInstr = jumpPtr; + break; + case CMP_NO_COMMON_BITS: + if (!(*memWord & value)) + gBattlescriptCurrInstr = jumpPtr; + break; + } +} + +static void atk2C_jumpifarrayequal(void) +{ + const u8 *mem1 = T2_READ_PTR(gBattlescriptCurrInstr + 1); + const u8 *mem2 = T2_READ_PTR(gBattlescriptCurrInstr + 5); + u32 size = gBattlescriptCurrInstr[9]; + const u8 *jumpPtr = T2_READ_PTR(gBattlescriptCurrInstr + 10); + u8 i; + + for (i = 0; i < size; ++i) + { + if (*mem1 != *mem2) + { + gBattlescriptCurrInstr += 14; + break; + } + ++mem1, ++mem2; + } + if (i == size) + gBattlescriptCurrInstr = jumpPtr; +} + +static void atk2D_jumpifarraynotequal(void) +{ + u8 equalBytes = 0; + const u8 *mem1 = T2_READ_PTR(gBattlescriptCurrInstr + 1); + const u8 *mem2 = T2_READ_PTR(gBattlescriptCurrInstr + 5); + u32 size = gBattlescriptCurrInstr[9]; + const u8 *jumpPtr = T2_READ_PTR(gBattlescriptCurrInstr + 10); + u8 i; + + for (i = 0; i < size; ++i) + { + if (*mem1 == *mem2) + ++equalBytes; + ++mem1, ++mem2; + } + if (equalBytes != size) + gBattlescriptCurrInstr = jumpPtr; + else + gBattlescriptCurrInstr += 14; +} + +static void atk2E_setbyte(void) +{ + u8 *memByte = T2_READ_PTR(gBattlescriptCurrInstr + 1); + + *memByte = gBattlescriptCurrInstr[5]; + gBattlescriptCurrInstr += 6; +} + +static void atk2F_addbyte(void) +{ + u8 *memByte = T2_READ_PTR(gBattlescriptCurrInstr + 1); + + *memByte += gBattlescriptCurrInstr[5]; + gBattlescriptCurrInstr += 6; +} + +static void atk30_subbyte(void) +{ + u8 *memByte = T2_READ_PTR(gBattlescriptCurrInstr + 1); + + *memByte -= gBattlescriptCurrInstr[5]; + gBattlescriptCurrInstr += 6; +} + +static void atk31_copyarray(void) +{ + u8 *dest = T2_READ_PTR(gBattlescriptCurrInstr + 1); + const u8 *src = T2_READ_PTR(gBattlescriptCurrInstr + 5); + s32 size = gBattlescriptCurrInstr[9]; + s32 i; + + for (i = 0; i < size; ++i) + { + dest[i] = src[i]; + } + gBattlescriptCurrInstr += 10; +} + +static void atk32_copyarraywithindex(void) +{ + u8 *dest = T2_READ_PTR(gBattlescriptCurrInstr + 1); + const u8 *src = T2_READ_PTR(gBattlescriptCurrInstr + 5); + const u8 *index = T2_READ_PTR(gBattlescriptCurrInstr + 9); + s32 size = gBattlescriptCurrInstr[13]; + s32 i; + + for (i = 0; i < size; ++i) + { + dest[i] = src[i + *index]; + } + gBattlescriptCurrInstr += 14; +} + +static void atk33_orbyte(void) +{ + u8 *memByte = T2_READ_PTR(gBattlescriptCurrInstr + 1); + *memByte |= gBattlescriptCurrInstr[5]; + gBattlescriptCurrInstr += 6; +} + +static void atk34_orhalfword(void) +{ + u16 *memHword = T2_READ_PTR(gBattlescriptCurrInstr + 1); + u16 val = T2_READ_16(gBattlescriptCurrInstr + 5); + + *memHword |= val; + gBattlescriptCurrInstr += 7; +} + +static void atk35_orword(void) +{ + u32 *memWord = T2_READ_PTR(gBattlescriptCurrInstr + 1); + u32 val = T2_READ_32(gBattlescriptCurrInstr + 5); + + *memWord |= val; + gBattlescriptCurrInstr += 9; +} + +static void atk36_bicbyte(void) +{ + u8 *memByte = T2_READ_PTR(gBattlescriptCurrInstr + 1); + + *memByte &= ~(gBattlescriptCurrInstr[5]); + gBattlescriptCurrInstr += 6; +} + +static void atk37_bichalfword(void) +{ + u16 *memHword = T2_READ_PTR(gBattlescriptCurrInstr + 1); + u16 val = T2_READ_16(gBattlescriptCurrInstr + 5); + + *memHword &= ~val; + gBattlescriptCurrInstr += 7; +} + +static void atk38_bicword(void) +{ + u32 *memWord = T2_READ_PTR(gBattlescriptCurrInstr + 1); + u32 val = T2_READ_32(gBattlescriptCurrInstr + 5); + + *memWord &= ~val; + gBattlescriptCurrInstr += 9; +} + +static void atk39_pause(void) +{ + if (!gBattleControllerExecFlags) + { + u16 value = T2_READ_16(gBattlescriptCurrInstr + 1); + + if (++gPauseCounterBattle >= value) + { + gPauseCounterBattle = 0; + gBattlescriptCurrInstr += 3; + } + } +} + +static void atk3A_waitstate(void) +{ + if (!gBattleControllerExecFlags) + ++gBattlescriptCurrInstr; +} + +static void atk3B_healthbar_update(void) +{ + if (gBattlescriptCurrInstr[1] == BS_TARGET) + gActiveBattler = gBattlerTarget; + else + gActiveBattler = gBattlerAttacker; + BtlController_EmitHealthBarUpdate(0, gBattleMoveDamage); + MarkBattlerForControllerExec(gActiveBattler); + gBattlescriptCurrInstr += 2; +} + +static void atk3C_return(void) +{ + BattleScriptPop(); +} + +static void atk3D_end(void) +{ + gMoveResultFlags = 0; + gActiveBattler = 0; + gCurrentActionFuncId = B_ACTION_TRY_FINISH; +} + +static void atk3E_end2(void) +{ + gActiveBattler = 0; + gCurrentActionFuncId = B_ACTION_TRY_FINISH; +} + +static void atk3F_end3(void) // pops the main function stack +{ + BattleScriptPop(); + if (gBattleResources->battleCallbackStack->size != 0) + gBattleResources->battleCallbackStack->size--; + gBattleMainFunc = gBattleResources->battleCallbackStack->function[gBattleResources->battleCallbackStack->size]; +} + +static void atk41_call(void) +{ + BattleScriptPush(gBattlescriptCurrInstr + 5); + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); +} + +static void atk42_jumpiftype2(void) +{ + u8 battlerId = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + + if (gBattlescriptCurrInstr[2] == gBattleMons[battlerId].type1 || gBattlescriptCurrInstr[2] == gBattleMons[battlerId].type2) + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 3); + else + gBattlescriptCurrInstr += 7; +} + +static void atk43_jumpifabilitypresent(void) +{ + if (AbilityBattleEffects(ABILITYEFFECT_CHECK_ON_FIELD, 0, gBattlescriptCurrInstr[1], 0, 0)) + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 2); + else + gBattlescriptCurrInstr += 6; +} + +static void atk44_endselectionscript(void) +{ + *(gBattlerAttacker + gBattleStruct->selectionScriptFinished) = TRUE; +} + +static void atk45_playanimation(void) +{ + const u16 *argumentPtr; + + gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + argumentPtr = T2_READ_PTR(gBattlescriptCurrInstr + 3); + if (gBattlescriptCurrInstr[2] == B_ANIM_STATS_CHANGE + || gBattlescriptCurrInstr[2] == B_ANIM_SNATCH_MOVE + || gBattlescriptCurrInstr[2] == B_ANIM_SUBSTITUTE_FADE + || gBattlescriptCurrInstr[2] == B_ANIM_x19) + { + BtlController_EmitBattleAnimation(0, gBattlescriptCurrInstr[2], *argumentPtr); + MarkBattlerForControllerExec(gActiveBattler); + gBattlescriptCurrInstr += 7; + } + else if (gHitMarker & HITMARKER_NO_ANIMATIONS) + { + BattleScriptPush(gBattlescriptCurrInstr + 7); + gBattlescriptCurrInstr = BattleScript_Pausex20; + } + else if (gBattlescriptCurrInstr[2] == B_ANIM_RAIN_CONTINUES + || gBattlescriptCurrInstr[2] == B_ANIM_SUN_CONTINUES + || gBattlescriptCurrInstr[2] == B_ANIM_SANDSTORM_CONTINUES + || gBattlescriptCurrInstr[2] == B_ANIM_HAIL_CONTINUES) + { + BtlController_EmitBattleAnimation(0, gBattlescriptCurrInstr[2], *argumentPtr); + MarkBattlerForControllerExec(gActiveBattler); + gBattlescriptCurrInstr += 7; + } + else if (gStatuses3[gActiveBattler] & STATUS3_SEMI_INVULNERABLE) + { + gBattlescriptCurrInstr += 7; + } + else + { + BtlController_EmitBattleAnimation(0, gBattlescriptCurrInstr[2], *argumentPtr); + MarkBattlerForControllerExec(gActiveBattler); + gBattlescriptCurrInstr += 7; + } +} + +static void atk46_playanimation2(void) // animation Id is stored in the first pointer +{ + const u16 *argumentPtr; + const u8 *animationIdPtr; + + gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + animationIdPtr = T2_READ_PTR(gBattlescriptCurrInstr + 2); + argumentPtr = T2_READ_PTR(gBattlescriptCurrInstr + 6); + if (*animationIdPtr == B_ANIM_STATS_CHANGE + || *animationIdPtr == B_ANIM_SNATCH_MOVE + || *animationIdPtr == B_ANIM_SUBSTITUTE_FADE) + { + BtlController_EmitBattleAnimation(0, *animationIdPtr, *argumentPtr); + MarkBattlerForControllerExec(gActiveBattler); + gBattlescriptCurrInstr += 10; + } + else if (gHitMarker & HITMARKER_NO_ANIMATIONS) + { + gBattlescriptCurrInstr += 10; + } + else if (*animationIdPtr == B_ANIM_RAIN_CONTINUES + || *animationIdPtr == B_ANIM_SUN_CONTINUES + || *animationIdPtr == B_ANIM_SANDSTORM_CONTINUES + || *animationIdPtr == B_ANIM_HAIL_CONTINUES) + { + BtlController_EmitBattleAnimation(0, *animationIdPtr, *argumentPtr); + MarkBattlerForControllerExec(gActiveBattler); + gBattlescriptCurrInstr += 10; + } + else if (gStatuses3[gActiveBattler] & STATUS3_SEMI_INVULNERABLE) + { + gBattlescriptCurrInstr += 10; + } + else + { + BtlController_EmitBattleAnimation(0, *animationIdPtr, *argumentPtr); + MarkBattlerForControllerExec(gActiveBattler); + gBattlescriptCurrInstr += 10; + } +} + +static void atk47_setgraphicalstatchangevalues(void) +{ + u8 value = 0; + + switch (GET_STAT_BUFF_VALUE2(gBattleScripting.statChanger)) + { + case SET_STAT_BUFF_VALUE(1): // +1 + value = STAT_ANIM_PLUS1; + break; + case SET_STAT_BUFF_VALUE(2): // +2 + value = STAT_ANIM_PLUS2; + break; + case SET_STAT_BUFF_VALUE(1) | STAT_BUFF_NEGATIVE: // -1 + value = STAT_ANIM_MINUS1; + break; + case SET_STAT_BUFF_VALUE(2) | STAT_BUFF_NEGATIVE: // -2 + value = STAT_ANIM_MINUS2; + break; + } + gBattleScripting.animArg1 = GET_STAT_BUFF_ID(gBattleScripting.statChanger) + value - 1; + gBattleScripting.animArg2 = 0; + ++gBattlescriptCurrInstr; +} + +static void atk48_playstatchangeanimation(void) +{ + u32 currStat = 0; + u16 statAnimId = 0; + s32 changeableStatsCount = 0; + u8 statsToCheck = 0; + + gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + statsToCheck = gBattlescriptCurrInstr[2]; + if (gBattlescriptCurrInstr[3] & ATK48_STAT_NEGATIVE) // goes down + { + s16 startingStatAnimId; + if (gBattlescriptCurrInstr[3] & ATK48_STAT_BY_TWO) + startingStatAnimId = STAT_ANIM_MINUS2 - 1; + else + startingStatAnimId = STAT_ANIM_MINUS1 - 1; + + while (statsToCheck != 0) + { + if (statsToCheck & 1) + { + if (gBattlescriptCurrInstr[3] & ATK48_DONT_CHECK_LOWER) + { + if (gBattleMons[gActiveBattler].statStages[currStat] > 0) + { + statAnimId = startingStatAnimId + currStat; + ++changeableStatsCount; + } + } + else if (!gSideTimers[GET_BATTLER_SIDE(gActiveBattler)].mistTimer + && gBattleMons[gActiveBattler].ability != ABILITY_CLEAR_BODY + && gBattleMons[gActiveBattler].ability != ABILITY_WHITE_SMOKE + && !(gBattleMons[gActiveBattler].ability == ABILITY_KEEN_EYE && currStat == STAT_ACC) + && !(gBattleMons[gActiveBattler].ability == ABILITY_HYPER_CUTTER && currStat == STAT_ATK)) + { + if (gBattleMons[gActiveBattler].statStages[currStat] > 0) + { + statAnimId = startingStatAnimId + currStat; + ++changeableStatsCount; + } + } + } + statsToCheck >>= 1; + ++currStat; + } + + if (changeableStatsCount > 1) // more than one stat, so the color is gray + { + if (gBattlescriptCurrInstr[3] & ATK48_STAT_BY_TWO) + statAnimId = STAT_ANIM_MULTIPLE_MINUS2; + else + statAnimId = STAT_ANIM_MULTIPLE_MINUS1; + } + } + else // goes up + { + s16 startingStatAnimId; + if (gBattlescriptCurrInstr[3] & ATK48_STAT_BY_TWO) + startingStatAnimId = STAT_ANIM_PLUS2 - 1; + else + startingStatAnimId = STAT_ANIM_PLUS1 - 1; + + while (statsToCheck != 0) + { + if (statsToCheck & 1 && gBattleMons[gActiveBattler].statStages[currStat] < 0xC) + { + statAnimId = startingStatAnimId + currStat; + ++changeableStatsCount; + } + statsToCheck >>= 1; + ++currStat; + } + if (changeableStatsCount > 1) // more than one stat, so the color is gray + { + if (gBattlescriptCurrInstr[3] & ATK48_STAT_BY_TWO) + statAnimId = STAT_ANIM_MULTIPLE_PLUS2; + else + statAnimId = STAT_ANIM_MULTIPLE_PLUS1; + } + } + if (gBattlescriptCurrInstr[3] & ATK48_ONLY_MULTIPLE && changeableStatsCount < 2) + { + gBattlescriptCurrInstr += 4; + } + else if (changeableStatsCount != 0 && !gBattleScripting.statAnimPlayed) + { + BtlController_EmitBattleAnimation(0, B_ANIM_STATS_CHANGE, statAnimId); + MarkBattlerForControllerExec(gActiveBattler); + if (gBattlescriptCurrInstr[3] & ATK48_ONLY_MULTIPLE && changeableStatsCount > 1) + gBattleScripting.statAnimPlayed = TRUE; + gBattlescriptCurrInstr += 4; + } + else + { + gBattlescriptCurrInstr += 4; + } +} + +static void atk49_moveend(void) +{ + s32 i; + bool32 effect = FALSE; + u8 moveType = 0; + u8 holdEffectAtk = 0; + u16 *choicedMoveAtk = NULL; + u8 arg1, arg2; + u16 originallyUsedMove; + + if (gChosenMove == 0xFFFF) + originallyUsedMove = MOVE_NONE; + else + originallyUsedMove = gChosenMove; + arg1 = gBattlescriptCurrInstr[1]; + arg2 = gBattlescriptCurrInstr[2]; + if (gBattleMons[gBattlerAttacker].item == ITEM_ENIGMA_BERRY) + holdEffectAtk = gEnigmaBerries[gBattlerAttacker].holdEffect; + else + holdEffectAtk = ItemId_GetHoldEffect(gBattleMons[gBattlerAttacker].item); + choicedMoveAtk = &gBattleStruct->choicedMove[gBattlerAttacker]; + GET_MOVE_TYPE(gCurrentMove, moveType); + do + { + switch (gBattleScripting.atk49_state) + { + case ATK49_RAGE: // rage check + if (gBattleMons[gBattlerTarget].status2 & STATUS2_RAGE + && gBattleMons[gBattlerTarget].hp != 0 + && gBattlerAttacker != gBattlerTarget + && GetBattlerSide(gBattlerAttacker) != GetBattlerSide(gBattlerTarget) + && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) + && TARGET_TURN_DAMAGED + && gBattleMoves[gCurrentMove].power + && gBattleMons[gBattlerTarget].statStages[STAT_ATK] <= 0xB) + { + ++gBattleMons[gBattlerTarget].statStages[STAT_ATK]; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_RageIsBuilding; + effect = TRUE; + } + ++gBattleScripting.atk49_state; + break; + case ATK49_DEFROST: // defrosting check + if (gBattleMons[gBattlerTarget].status1 & STATUS1_FREEZE + && gBattleMons[gBattlerTarget].hp != 0 + && gBattlerAttacker != gBattlerTarget + && gSpecialStatuses[gBattlerTarget].specialDmg + && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) + && moveType == TYPE_FIRE) + { + gBattleMons[gBattlerTarget].status1 &= ~(STATUS1_FREEZE); + gActiveBattler = gBattlerTarget; + BtlController_EmitSetMonData(0, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[gBattlerTarget].status1); + MarkBattlerForControllerExec(gActiveBattler); + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_DefrostedViaFireMove; + effect = TRUE; + } + ++gBattleScripting.atk49_state; + break; + case ATK49_SYNCHRONIZE_TARGET: // target synchronize + if (AbilityBattleEffects(ABILITYEFFECT_SYNCHRONIZE, gBattlerTarget, 0, 0, 0)) + effect = TRUE; + ++gBattleScripting.atk49_state; + break; + case ATK49_MOVE_END_ABILITIES: // Such as abilities activating on contact(Poison Spore, Rough Skin, etc.). + if (AbilityBattleEffects(ABILITYEFFECT_MOVE_END, gBattlerTarget, 0, 0, 0)) + effect = TRUE; + ++gBattleScripting.atk49_state; + break; + case ATK49_STATUS_IMMUNITY_ABILITIES: // status immunities + if (AbilityBattleEffects(ABILITYEFFECT_IMMUNITY, 0, 0, 0, 0)) + effect = TRUE; // it loops through all battlers, so we increment after its done with all battlers + else + ++gBattleScripting.atk49_state; + break; + case ATK49_SYNCHRONIZE_ATTACKER: // attacker synchronize + if (AbilityBattleEffects(ABILITYEFFECT_ATK_SYNCHRONIZE, gBattlerAttacker, 0, 0, 0)) + effect = TRUE; + ++gBattleScripting.atk49_state; + break; + case ATK49_CHOICE_MOVE: // update choice band move + if (gHitMarker & HITMARKER_OBEYS + && holdEffectAtk == HOLD_EFFECT_CHOICE_BAND + && gChosenMove != MOVE_STRUGGLE + && (*choicedMoveAtk == 0 || *choicedMoveAtk == 0xFFFF)) + { + if (gChosenMove == MOVE_BATON_PASS && !(gMoveResultFlags & MOVE_RESULT_FAILED)) + { + ++gBattleScripting.atk49_state; + break; + } + *choicedMoveAtk = gChosenMove; + } + for (i = 0; i < MAX_MON_MOVES; ++i) + { + if (gBattleMons[gBattlerAttacker].moves[i] == *choicedMoveAtk) + break; + } + if (i == MAX_MON_MOVES) + *choicedMoveAtk = 0; + ++gBattleScripting.atk49_state; + break; + case ATK49_CHANGED_ITEMS: // changed held items + for (i = 0; i < gBattlersCount; ++i) + { + u16 *changedItem = &gBattleStruct->changedItems[i]; + + if (*changedItem != 0) + { + gBattleMons[i].item = *changedItem; + *changedItem = 0; + } + } + ++gBattleScripting.atk49_state; + break; + case ATK49_ITEM_EFFECTS_ALL: // item effects for all battlers + if (ItemBattleEffects(ITEMEFFECT_MOVE_END, 0, FALSE)) + effect = TRUE; + else + ++gBattleScripting.atk49_state; + break; + case ATK49_KINGSROCK_SHELLBELL: // king's rock and shell bell + if (ItemBattleEffects(ITEMEFFECT_KINGSROCK_SHELLBELL, 0, FALSE)) + effect = TRUE; + ++gBattleScripting.atk49_state; + break; + case ATK49_ATTACKER_INVISIBLE: // make attacker sprite invisible + if (gStatuses3[gBattlerAttacker] & (STATUS3_SEMI_INVULNERABLE) + && gHitMarker & HITMARKER_NO_ANIMATIONS) + { + gActiveBattler = gBattlerAttacker; + BtlController_EmitSpriteInvisibility(0, TRUE); + MarkBattlerForControllerExec(gActiveBattler); + ++gBattleScripting.atk49_state; + return; + } + ++gBattleScripting.atk49_state; + break; + case ATK49_ATTACKER_VISIBLE: // make attacker sprite visible + if (gMoveResultFlags & MOVE_RESULT_NO_EFFECT + || !(gStatuses3[gBattlerAttacker] & (STATUS3_SEMI_INVULNERABLE)) + || WasUnableToUseMove(gBattlerAttacker)) + { + gActiveBattler = gBattlerAttacker; + BtlController_EmitSpriteInvisibility(0, FALSE); + MarkBattlerForControllerExec(gActiveBattler); + gStatuses3[gBattlerAttacker] &= ~(STATUS3_SEMI_INVULNERABLE); + gSpecialStatuses[gBattlerAttacker].restoredBattlerSprite = 1; + ++gBattleScripting.atk49_state; + return; + } + ++gBattleScripting.atk49_state; + break; + case ATK49_TARGET_VISIBLE: // make target sprite visible + if (!gSpecialStatuses[gBattlerTarget].restoredBattlerSprite + && gBattlerTarget < gBattlersCount + && !(gStatuses3[gBattlerTarget] & STATUS3_SEMI_INVULNERABLE)) + { + gActiveBattler = gBattlerTarget; + BtlController_EmitSpriteInvisibility(0, FALSE); + MarkBattlerForControllerExec(gActiveBattler); + gStatuses3[gBattlerTarget] &= ~(STATUS3_SEMI_INVULNERABLE); + ++gBattleScripting.atk49_state; + return; + } + ++gBattleScripting.atk49_state; + break; + case ATK49_SUBSTITUTE: // update substitute + for (i = 0; i < gBattlersCount; ++i) + { + if (gDisableStructs[i].substituteHP == 0) + gBattleMons[i].status2 &= ~(STATUS2_SUBSTITUTE); + } + ++gBattleScripting.atk49_state; + break; + case ATK49_UPDATE_LAST_MOVES: + if (gHitMarker & HITMARKER_SWAP_ATTACKER_TARGET) + { + gActiveBattler = gBattlerAttacker; + gBattlerAttacker = gBattlerTarget; + gBattlerTarget = gActiveBattler; + gHitMarker &= ~(HITMARKER_SWAP_ATTACKER_TARGET); + } + if (gHitMarker & HITMARKER_ATTACKSTRING_PRINTED) + { + gLastPrintedMoves[gBattlerAttacker] = gChosenMove; + } + if (!(gAbsentBattlerFlags & gBitTable[gBattlerAttacker]) + && !(gBattleStruct->field_91 & gBitTable[gBattlerAttacker]) + && gBattleMoves[originallyUsedMove].effect != EFFECT_BATON_PASS) + { + if (gHitMarker & HITMARKER_OBEYS) + { + gLastMoves[gBattlerAttacker] = gChosenMove; + gLastResultingMoves[gBattlerAttacker] = gCurrentMove; + } + else + { + gLastMoves[gBattlerAttacker] = 0xFFFF; + gLastResultingMoves[gBattlerAttacker] = 0xFFFF; + } + + if (!(gHitMarker & HITMARKER_FAINTED(gBattlerTarget))) + gLastHitBy[gBattlerTarget] = gBattlerAttacker; + + if (gHitMarker & HITMARKER_OBEYS && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)) + { + if (gChosenMove == 0xFFFF) + { + gLastLandedMoves[gBattlerTarget] = gChosenMove; + } + else + { + gLastLandedMoves[gBattlerTarget] = gCurrentMove; + GET_MOVE_TYPE(gCurrentMove, gLastHitByType[gBattlerTarget]); + } + } + else + { + gLastLandedMoves[gBattlerTarget] = 0xFFFF; + } + } + ++gBattleScripting.atk49_state; + break; + case ATK49_MIRROR_MOVE: // mirror move + if (!(gAbsentBattlerFlags & gBitTable[gBattlerAttacker]) + && !(gBattleStruct->field_91 & gBitTable[gBattlerAttacker]) + && gBattleMoves[originallyUsedMove].flags & FLAG_MIRROR_MOVE_AFFECTED + && gHitMarker & HITMARKER_OBEYS + && gBattlerAttacker != gBattlerTarget + && !(gHitMarker & HITMARKER_FAINTED(gBattlerTarget)) + && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)) + { + u8 target, attacker; + + *(gBattleStruct->lastTakenMove + gBattlerTarget * 2 + 0) = gChosenMove; + *(gBattleStruct->lastTakenMove + gBattlerTarget * 2 + 1) = gChosenMove >> 8; + target = gBattlerTarget; + attacker = gBattlerAttacker; + *(attacker * 2 + target * 8 + (gBattleStruct->lastTakenMoveFrom) + 0) = gChosenMove; + target = gBattlerTarget; + attacker = gBattlerAttacker; + *(attacker * 2 + target * 8 + (gBattleStruct->lastTakenMoveFrom) + 1) = gChosenMove >> 8; + } + ++gBattleScripting.atk49_state; + break; + case ATK49_NEXT_TARGET: // For moves hitting two opposing Pokemon. + if (!(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE) + && gBattleTypeFlags & BATTLE_TYPE_DOUBLE + && !gProtectStructs[gBattlerAttacker].chargingTurn + && gBattleMoves[gCurrentMove].target == MOVE_TARGET_BOTH + && !(gHitMarker & HITMARKER_NO_ATTACKSTRING)) + { + u8 battlerId = GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gBattlerTarget))); + if (gBattleMons[battlerId].hp != 0) + { + gBattlerTarget = battlerId; + gHitMarker |= HITMARKER_NO_ATTACKSTRING; + gBattleScripting.atk49_state = 0; + MoveValuesCleanUp(); + BattleScriptPush(gBattleScriptsForMoveEffects[gBattleMoves[gCurrentMove].effect]); + gBattlescriptCurrInstr = BattleScript_FlushMessageBox; + return; + } + else + { + gHitMarker |= HITMARKER_NO_ATTACKSTRING; + } + } + ++gBattleScripting.atk49_state; + break; + case ATK49_COUNT: + break; + } + if (arg1 == 1 && effect == FALSE) + gBattleScripting.atk49_state = ATK49_COUNT; + if (arg1 == 2 && arg2 == gBattleScripting.atk49_state) + gBattleScripting.atk49_state = ATK49_COUNT; + } + while (gBattleScripting.atk49_state != ATK49_COUNT && effect == FALSE); + if (gBattleScripting.atk49_state == ATK49_COUNT && effect == FALSE) + gBattlescriptCurrInstr += 3; +} + +static void atk4A_typecalc2(void) +{ + u8 flags = 0; + s32 i = 0; + u8 moveType = gBattleMoves[gCurrentMove].type; + + if (gBattleMons[gBattlerTarget].ability == ABILITY_LEVITATE && moveType == TYPE_GROUND) + { + gLastUsedAbility = gBattleMons[gBattlerTarget].ability; + gMoveResultFlags |= (MOVE_RESULT_MISSED | MOVE_RESULT_DOESNT_AFFECT_FOE); + gLastLandedMoves[gBattlerTarget] = 0; + gBattleCommunication[6] = moveType; + RecordAbilityBattle(gBattlerTarget, gLastUsedAbility); + } + else + { + while (TYPE_EFFECT_ATK_TYPE(i) != TYPE_ENDTABLE) + { + if (TYPE_EFFECT_ATK_TYPE(i) == TYPE_FORESIGHT) + { + if (gBattleMons[gBattlerTarget].status2 & STATUS2_FORESIGHT) + { + break; + } + else + { + i += 3; + continue; + } + } + if (TYPE_EFFECT_ATK_TYPE(i) == moveType) + { + // check type1 + if (TYPE_EFFECT_DEF_TYPE(i) == gBattleMons[gBattlerTarget].type1) + { + if (TYPE_EFFECT_MULTIPLIER(i) == TYPE_MUL_NO_EFFECT) + { + gMoveResultFlags |= MOVE_RESULT_DOESNT_AFFECT_FOE; + break; + } + if (TYPE_EFFECT_MULTIPLIER(i) == TYPE_MUL_NOT_EFFECTIVE) + { + flags |= MOVE_RESULT_NOT_VERY_EFFECTIVE; + } + if (TYPE_EFFECT_MULTIPLIER(i) == TYPE_MUL_SUPER_EFFECTIVE) + { + flags |= MOVE_RESULT_SUPER_EFFECTIVE; + } + } + // check type2 + if (TYPE_EFFECT_DEF_TYPE(i) == gBattleMons[gBattlerTarget].type2) + { + if (gBattleMons[gBattlerTarget].type1 != gBattleMons[gBattlerTarget].type2 + && TYPE_EFFECT_MULTIPLIER(i) == TYPE_MUL_NO_EFFECT) + { + gMoveResultFlags |= MOVE_RESULT_DOESNT_AFFECT_FOE; + break; + } + if (TYPE_EFFECT_DEF_TYPE(i) == gBattleMons[gBattlerTarget].type2 + && gBattleMons[gBattlerTarget].type1 != gBattleMons[gBattlerTarget].type2 + && TYPE_EFFECT_MULTIPLIER(i) == TYPE_MUL_NOT_EFFECTIVE) + { + flags |= MOVE_RESULT_NOT_VERY_EFFECTIVE; + } + if (TYPE_EFFECT_DEF_TYPE(i) == gBattleMons[gBattlerTarget].type2 + && gBattleMons[gBattlerTarget].type1 != gBattleMons[gBattlerTarget].type2 + && TYPE_EFFECT_MULTIPLIER(i) == TYPE_MUL_SUPER_EFFECTIVE) + { + flags |= MOVE_RESULT_SUPER_EFFECTIVE; + } + } + } + i += 3; + } + } + if (gBattleMons[gBattlerTarget].ability == ABILITY_WONDER_GUARD + && !(flags & MOVE_RESULT_NO_EFFECT) + && AttacksThisTurn(gBattlerAttacker, gCurrentMove) == 2 + && (!(flags & MOVE_RESULT_SUPER_EFFECTIVE) || ((flags & (MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE)) == (MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE))) + && gBattleMoves[gCurrentMove].power) + { + gLastUsedAbility = ABILITY_WONDER_GUARD; + gMoveResultFlags |= MOVE_RESULT_MISSED; + gLastLandedMoves[gBattlerTarget] = 0; + gBattleCommunication[6] = 3; + RecordAbilityBattle(gBattlerTarget, gLastUsedAbility); + } + if (gMoveResultFlags & MOVE_RESULT_DOESNT_AFFECT_FOE) + gProtectStructs[gBattlerAttacker].targetNotAffected = 1; + ++gBattlescriptCurrInstr; +} + +static void atk4B_returnatktoball(void) +{ + gActiveBattler = gBattlerAttacker; + if (!(gHitMarker & HITMARKER_FAINTED(gActiveBattler))) + { + BtlController_EmitReturnMonToBall(0, 0); + MarkBattlerForControllerExec(gActiveBattler); + } + ++gBattlescriptCurrInstr; +} + +static void atk4C_getswitchedmondata(void) +{ + if (!gBattleControllerExecFlags) + { + gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + gBattlerPartyIndexes[gActiveBattler] = *(gBattleStruct->monToSwitchIntoId + gActiveBattler); + BtlController_EmitGetMonData(0, REQUEST_ALL_BATTLE, gBitTable[gBattlerPartyIndexes[gActiveBattler]]); + MarkBattlerForControllerExec(gActiveBattler); + gBattlescriptCurrInstr += 2; + } +} + +static void atk4D_switchindataupdate(void) +{ + struct BattlePokemon oldData; + s32 i; + u8 *monData; + + if (!gBattleControllerExecFlags) + { + gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + oldData = gBattleMons[gActiveBattler]; + monData = (u8 *)(&gBattleMons[gActiveBattler]); + for (i = 0; i < sizeof(struct BattlePokemon); ++i) + { + monData[i] = gBattleBufferB[gActiveBattler][4 + i]; + } + gBattleMons[gActiveBattler].type1 = gBaseStats[gBattleMons[gActiveBattler].species].type1; + gBattleMons[gActiveBattler].type2 = gBaseStats[gBattleMons[gActiveBattler].species].type2; + gBattleMons[gActiveBattler].ability = GetAbilityBySpecies(gBattleMons[gActiveBattler].species, gBattleMons[gActiveBattler].abilityNum); + // check knocked off item + i = GetBattlerSide(gActiveBattler); + if (gWishFutureKnock.knockedOffMons[i] & gBitTable[gBattlerPartyIndexes[gActiveBattler]]) + gBattleMons[gActiveBattler].item = 0; + if (gBattleMoves[gCurrentMove].effect == EFFECT_BATON_PASS) + { + for (i = 0; i < NUM_BATTLE_STATS; ++i) + { + gBattleMons[gActiveBattler].statStages[i] = oldData.statStages[i]; + } + gBattleMons[gActiveBattler].status2 = oldData.status2; + } + SwitchInClearSetData(); + gBattleScripting.battler = gActiveBattler; + PREPARE_MON_NICK_BUFFER(gBattleTextBuff1, gActiveBattler, gBattlerPartyIndexes[gActiveBattler]); + gBattlescriptCurrInstr += 2; + } +} + +static void atk4E_switchinanim(void) +{ + if (!gBattleControllerExecFlags) + { + gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + if (GetBattlerSide(gActiveBattler) == B_SIDE_OPPONENT + && !(gBattleTypeFlags & + (BATTLE_TYPE_LINK + | BATTLE_TYPE_LEGENDARY + | BATTLE_TYPE_OLDMAN_TUTORIAL + | BATTLE_TYPE_POKEDUDE + | BATTLE_TYPE_EREADER_TRAINER + | BATTLE_TYPE_GHOST))) + HandleSetPokedexFlag(SpeciesToNationalPokedexNum(gBattleMons[gActiveBattler].species), FLAG_SET_SEEN, gBattleMons[gActiveBattler].personality); + gAbsentBattlerFlags &= ~(gBitTable[gActiveBattler]); + BtlController_EmitSwitchInAnim(0, gBattlerPartyIndexes[gActiveBattler], gBattlescriptCurrInstr[2]); + MarkBattlerForControllerExec(gActiveBattler); + gBattlescriptCurrInstr += 3; + } +} + +static void atk4F_jumpifcantswitch(void) +{ + s32 i; + s32 lastMonId; + struct Pokemon *party; + + gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1] & ~(ATK4F_DONT_CHECK_STATUSES)); + if (!(gBattlescriptCurrInstr[1] & ATK4F_DONT_CHECK_STATUSES) + && ((gBattleMons[gActiveBattler].status2 & (STATUS2_WRAPPED | STATUS2_ESCAPE_PREVENTION)) + || (gStatuses3[gActiveBattler] & STATUS3_ROOTED))) + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 2); + } + else if (gBattleTypeFlags & BATTLE_TYPE_MULTI) + { + if (GetBattlerSide(gActiveBattler) == B_SIDE_OPPONENT) + party = gEnemyParty; + else + party = gPlayerParty; + + i = 0; + if (GetLinkTrainerFlankId(GetBattlerMultiplayerId(gActiveBattler)) == TRUE) + i = 3; + for (lastMonId = i + 3; i < lastMonId; ++i) + { + if (GetMonData(&party[i], MON_DATA_SPECIES) != SPECIES_NONE + && !GetMonData(&party[i], MON_DATA_IS_EGG) + && GetMonData(&party[i], MON_DATA_HP) != 0 + && gBattlerPartyIndexes[gActiveBattler] != i) + break; + } + if (i == lastMonId) + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 2); + else + gBattlescriptCurrInstr += 6; + } + else + { + u8 battlerIn1, battlerIn2; + + if (GetBattlerSide(gActiveBattler) == B_SIDE_OPPONENT) + { + battlerIn1 = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + battlerIn2 = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); + else + battlerIn2 = battlerIn1; + party = gEnemyParty; + } + else + { + battlerIn1 = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + battlerIn2 = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT); + else + battlerIn2 = battlerIn1; + party = gPlayerParty; + } + for (i = 0; i < PARTY_SIZE; ++i) + { + if (GetMonData(&party[i], MON_DATA_HP) != 0 + && GetMonData(&party[i], MON_DATA_SPECIES) != SPECIES_NONE + && !GetMonData(&party[i], MON_DATA_IS_EGG) + && i != gBattlerPartyIndexes[battlerIn1] + && i != gBattlerPartyIndexes[battlerIn2]) + break; + } + if (i == 6) + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 2); + else + gBattlescriptCurrInstr += 6; + } +} + +static void sub_8024398(u8 arg0) +{ + *(gBattleStruct->field_58 + gActiveBattler) = gBattlerPartyIndexes[gActiveBattler]; + BtlController_EmitChoosePokemon(0, PARTY_MUST_CHOOSE_MON, arg0, 0, gBattleStruct->field_60[gActiveBattler]); + MarkBattlerForControllerExec(gActiveBattler); +} + +static void atk50_openpartyscreen(void) +{ + u32 flags; + u8 hitmarkerFaintBits; + u8 battlerId; + const u8 *jumpPtr; + + battlerId = 0; + flags = 0; + jumpPtr = T1_READ_PTR(gBattlescriptCurrInstr + 2); + if (gBattlescriptCurrInstr[1] == 5) + { + if ((gBattleTypeFlags & (BATTLE_TYPE_DOUBLE | BATTLE_TYPE_MULTI)) != BATTLE_TYPE_DOUBLE) + { + for (gActiveBattler = 0; gActiveBattler < gBattlersCount; ++gActiveBattler) + { + if (gHitMarker & HITMARKER_FAINTED(gActiveBattler)) + { + if (HasNoMonsToSwitch(gActiveBattler, 6, 6)) + { + gAbsentBattlerFlags |= gBitTable[gActiveBattler]; + gHitMarker &= ~(HITMARKER_FAINTED(gActiveBattler)); + BtlController_EmitLinkStandbyMsg(0, 2); + MarkBattlerForControllerExec(gActiveBattler); + } + else if (!gSpecialStatuses[gActiveBattler].flag40) + { + sub_8024398(6); + gSpecialStatuses[gActiveBattler].flag40 = 1; + } + } + else + { + BtlController_EmitLinkStandbyMsg(0, 2); + MarkBattlerForControllerExec(gActiveBattler); + } + } + } + else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + { + u8 flag40_0, flag40_1, flag40_2, flag40_3; + + hitmarkerFaintBits = gHitMarker >> 0x1C; + if (gBitTable[0] & hitmarkerFaintBits) + { + gActiveBattler = 0; + if (HasNoMonsToSwitch(0, 6, 6)) + { + gAbsentBattlerFlags |= gBitTable[gActiveBattler]; + gHitMarker &= ~(HITMARKER_FAINTED(gActiveBattler)); + BtlController_EmitCmd42(0); + MarkBattlerForControllerExec(gActiveBattler); + } + else if (!gSpecialStatuses[gActiveBattler].flag40) + { + sub_8024398(gBattleStruct->monToSwitchIntoId[2]); + gSpecialStatuses[gActiveBattler].flag40 = 1; + } + else + { + BtlController_EmitLinkStandbyMsg(0, 2); + MarkBattlerForControllerExec(gActiveBattler); + flags |= 1; + } + } + if (gBitTable[2] & hitmarkerFaintBits && !(gBitTable[0] & hitmarkerFaintBits)) + { + gActiveBattler = 2; + if (HasNoMonsToSwitch(2, 6, 6)) + { + gAbsentBattlerFlags |= gBitTable[gActiveBattler]; + gHitMarker &= ~(HITMARKER_FAINTED(gActiveBattler)); + BtlController_EmitCmd42(0); + MarkBattlerForControllerExec(gActiveBattler); + } + else if (!gSpecialStatuses[gActiveBattler].flag40) + { + sub_8024398(gBattleStruct->monToSwitchIntoId[0]); + gSpecialStatuses[gActiveBattler].flag40 = 1; + } + else if (!(flags & 1)) + { + BtlController_EmitLinkStandbyMsg(0, 2); + MarkBattlerForControllerExec(gActiveBattler); + } + } + if (gBitTable[1] & hitmarkerFaintBits) + { + gActiveBattler = 1; + if (HasNoMonsToSwitch(1, 6, 6)) + { + gAbsentBattlerFlags |= gBitTable[gActiveBattler]; + gHitMarker &= ~(HITMARKER_FAINTED(gActiveBattler)); + BtlController_EmitCmd42(0); + MarkBattlerForControllerExec(gActiveBattler); + } + else if (!gSpecialStatuses[gActiveBattler].flag40) + { + sub_8024398(gBattleStruct->monToSwitchIntoId[3]); + gSpecialStatuses[gActiveBattler].flag40 = 1; + } + else + { + BtlController_EmitLinkStandbyMsg(0, 2); + MarkBattlerForControllerExec(gActiveBattler); + flags |= 2; + } + } + if (gBitTable[3] & hitmarkerFaintBits && !(gBitTable[1] & hitmarkerFaintBits)) + { + gActiveBattler = 3; + if (HasNoMonsToSwitch(3, 6, 6)) + { + gAbsentBattlerFlags |= gBitTable[gActiveBattler]; + gHitMarker &= ~(HITMARKER_FAINTED(gActiveBattler)); + BtlController_EmitCmd42(0); + MarkBattlerForControllerExec(gActiveBattler); + } + else if (!gSpecialStatuses[gActiveBattler].flag40) + { + sub_8024398(gBattleStruct->monToSwitchIntoId[1]); + gSpecialStatuses[gActiveBattler].flag40 = 1; + } + else if (!(flags & 2)) + { + BtlController_EmitLinkStandbyMsg(0, 2); + MarkBattlerForControllerExec(gActiveBattler); + } + } + flag40_0 = gSpecialStatuses[0].flag40; + if (!flag40_0) + { + flag40_2 = gSpecialStatuses[2].flag40; + if (!flag40_2 && hitmarkerFaintBits != 0) + { + if (gAbsentBattlerFlags & gBitTable[0]) + gActiveBattler = 2; + else + gActiveBattler = 0; + + BtlController_EmitLinkStandbyMsg(0, 2); + MarkBattlerForControllerExec(gActiveBattler); + } + + } + flag40_1 = gSpecialStatuses[1].flag40; + if (!flag40_1) + { + flag40_3 = gSpecialStatuses[3].flag40; + if (!flag40_3 && hitmarkerFaintBits != 0) + { + if (gAbsentBattlerFlags & gBitTable[1]) + gActiveBattler = 3; + else + gActiveBattler = 1; + BtlController_EmitLinkStandbyMsg(0, 2); + MarkBattlerForControllerExec(gActiveBattler); + } + } + } + gBattlescriptCurrInstr += 6; + } + else if (gBattlescriptCurrInstr[1] == 6) + { + if (!(gBattleTypeFlags & BATTLE_TYPE_MULTI)) + { + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + { + hitmarkerFaintBits = gHitMarker >> 0x1C; + if (gBitTable[2] & hitmarkerFaintBits && gBitTable[0] & hitmarkerFaintBits) + { + gActiveBattler = 2; + if (HasNoMonsToSwitch(2, gBattleBufferB[0][1], 6)) + { + gAbsentBattlerFlags |= gBitTable[gActiveBattler]; + gHitMarker &= ~(HITMARKER_FAINTED(gActiveBattler)); + BtlController_EmitCmd42(0); + MarkBattlerForControllerExec(gActiveBattler); + } + else if (!gSpecialStatuses[gActiveBattler].flag40) + { + sub_8024398(gBattleStruct->monToSwitchIntoId[0]); + gSpecialStatuses[gActiveBattler].flag40 = 1; + } + } + if (gBitTable[3] & hitmarkerFaintBits && hitmarkerFaintBits & gBitTable[1]) + { + gActiveBattler = 3; + if (HasNoMonsToSwitch(3, gBattleBufferB[1][1], 6)) + { + gAbsentBattlerFlags |= gBitTable[gActiveBattler]; + gHitMarker &= ~(HITMARKER_FAINTED(gActiveBattler)); + BtlController_EmitCmd42(0); + MarkBattlerForControllerExec(gActiveBattler); + } + else if (!gSpecialStatuses[gActiveBattler].flag40) + { + sub_8024398(gBattleStruct->monToSwitchIntoId[1]); + gSpecialStatuses[gActiveBattler].flag40 = 1; + } + } + gBattlescriptCurrInstr += 6; + } + else + { + gBattlescriptCurrInstr += 6; + } + } + else + { + gBattlescriptCurrInstr += 6; + } + + hitmarkerFaintBits = gHitMarker >> 0x1C; + + gBattlerFainted = 0; + while (1) + { + if (gBitTable[gBattlerFainted] & hitmarkerFaintBits) + break; + if (gBattlerFainted >= gBattlersCount) + break; + ++gBattlerFainted; + } + + if (gBattlerFainted == gBattlersCount) + gBattlescriptCurrInstr = jumpPtr; + } + else + { + if (gBattlescriptCurrInstr[1] & 0x80) + hitmarkerFaintBits = PARTY_CHOOSE_MON; // Used here as the caseId for the EmitChoose function. + else + hitmarkerFaintBits = PARTY_MUST_CHOOSE_MON; + battlerId = GetBattlerForBattleScript(gBattlescriptCurrInstr[1] & ~(0x80)); + if (gSpecialStatuses[battlerId].flag40) + { + gBattlescriptCurrInstr += 6; + } + else if (HasNoMonsToSwitch(battlerId, 6, 6)) + { + gActiveBattler = battlerId; + gAbsentBattlerFlags |= gBitTable[gActiveBattler]; + gHitMarker &= ~(HITMARKER_FAINTED(gActiveBattler)); + gBattlescriptCurrInstr = jumpPtr; + } + else + { + gActiveBattler = battlerId; + *(gBattleStruct->field_58 + gActiveBattler) = gBattlerPartyIndexes[gActiveBattler]; + BtlController_EmitChoosePokemon(0, hitmarkerFaintBits, *(gBattleStruct->monToSwitchIntoId + (gActiveBattler ^ 2)), 0, gBattleStruct->field_60[gActiveBattler]); + MarkBattlerForControllerExec(gActiveBattler); + gBattlescriptCurrInstr += 6; + if (GetBattlerPosition(gActiveBattler) == B_POSITION_PLAYER_LEFT && gBattleResults.playerSwitchesCounter < 0xFF) + ++gBattleResults.playerSwitchesCounter; + + if (gBattleTypeFlags & BATTLE_TYPE_MULTI) + { + for (gActiveBattler = 0; gActiveBattler < gBattlersCount; ++gActiveBattler) + { + if (gActiveBattler != battlerId) + { + BtlController_EmitLinkStandbyMsg(0, 2); + MarkBattlerForControllerExec(gActiveBattler); + } + } + } + else + { + gActiveBattler = GetBattlerAtPosition(GetBattlerPosition(battlerId) ^ BIT_SIDE); + if (gAbsentBattlerFlags & gBitTable[gActiveBattler]) + gActiveBattler ^= BIT_FLANK; + BtlController_EmitLinkStandbyMsg(0, 2); + MarkBattlerForControllerExec(gActiveBattler); + } + } + } +} + +static void atk51_switchhandleorder(void) +{ + s32 i; + + if (!gBattleControllerExecFlags) + { + gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + switch (gBattlescriptCurrInstr[2]) + { + case 0: + for (i = 0; i < gBattlersCount; ++i) + if (gBattleBufferB[i][0] == 0x22) + *(gBattleStruct->monToSwitchIntoId + i) = gBattleBufferB[i][1]; + break; + case 1: + if (!(gBattleTypeFlags & BATTLE_TYPE_MULTI)) + sub_8013F6C(gActiveBattler); + break; + case 2: + gBattleCommunication[0] = gBattleBufferB[gActiveBattler][1]; + *(gBattleStruct->monToSwitchIntoId + gActiveBattler) = gBattleBufferB[gActiveBattler][1]; + + if (gBattleTypeFlags & BATTLE_TYPE_MULTI) + { + *(gActiveBattler * 3 + (u8 *)(gBattleStruct->field_60) + 0) &= 0xF; + *(gActiveBattler * 3 + (u8 *)(gBattleStruct->field_60) + 0) |= (gBattleBufferB[gActiveBattler][2] & 0xF0); + *(gActiveBattler * 3 + (u8 *)(gBattleStruct->field_60) + 1) = gBattleBufferB[gActiveBattler][3]; + *((gActiveBattler ^ BIT_FLANK) * 3 + (u8 *)(gBattleStruct->field_60) + 0) &= (0xF0); + *((gActiveBattler ^ BIT_FLANK) * 3 + (u8 *)(gBattleStruct->field_60) + 0) |= (gBattleBufferB[gActiveBattler][2] & 0xF0) >> 4; + *((gActiveBattler ^ BIT_FLANK) * 3 + (u8 *)(gBattleStruct->field_60) + 2) = gBattleBufferB[gActiveBattler][3]; + } + else + { + sub_8013F6C(gActiveBattler); + } + PREPARE_SPECIES_BUFFER(gBattleTextBuff1, gBattleMons[gBattlerAttacker].species) + PREPARE_MON_NICK_BUFFER(gBattleTextBuff2, gActiveBattler, gBattleBufferB[gActiveBattler][1]) + break; + } + gBattlescriptCurrInstr += 3; + } +} + +static void atk52_switchineffects(void) +{ + s32 i; + + gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + sub_80174B8(gActiveBattler); + gHitMarker &= ~(HITMARKER_FAINTED(gActiveBattler)); + gSpecialStatuses[gActiveBattler].flag40 = 0; + if (!(gSideStatuses[GetBattlerSide(gActiveBattler)] & SIDE_STATUS_SPIKES_DAMAGED) + && (gSideStatuses[GetBattlerSide(gActiveBattler)] & SIDE_STATUS_SPIKES) + && !IS_BATTLER_OF_TYPE(gActiveBattler, TYPE_FLYING) + && gBattleMons[gActiveBattler].ability != ABILITY_LEVITATE) + { + u8 spikesDmg; + + gSideStatuses[GetBattlerSide(gActiveBattler)] |= SIDE_STATUS_SPIKES_DAMAGED; + spikesDmg = (5 - gSideTimers[GetBattlerSide(gActiveBattler)].spikesAmount) * 2; + gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP / (spikesDmg); + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + gBattleScripting.battler = gActiveBattler; + BattleScriptPushCursor(); + if (gBattlescriptCurrInstr[1] == BS_TARGET) + gBattlescriptCurrInstr = BattleScript_SpikesOnTarget; + else if (gBattlescriptCurrInstr[1] == BS_ATTACKER) + gBattlescriptCurrInstr = BattleScript_SpikesOnAttacker; + else + gBattlescriptCurrInstr = BattleScript_SpikesOnFaintedBattler; + } + else + { + if (gBattleMons[gActiveBattler].ability == ABILITY_TRUANT) + gDisableStructs[gActiveBattler].truantCounter = 1; + if (!AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, gActiveBattler, 0, 0, 0) + && !ItemBattleEffects(ITEMEFFECT_ON_SWITCH_IN, gActiveBattler, FALSE)) + { + gSideStatuses[GetBattlerSide(gActiveBattler)] &= ~(SIDE_STATUS_SPIKES_DAMAGED); + + for (i = 0; i < gBattlersCount; ++i) + { + if (gBattlerByTurnOrder[i] == gActiveBattler) + gActionsByTurnOrder[i] = B_ACTION_CANCEL_PARTNER; + } + for (i = 0; i < gBattlersCount; ++i) + { + u16 *hpOnSwitchout = &gBattleStruct->hpOnSwitchout[GetBattlerSide(i)]; + *hpOnSwitchout = gBattleMons[i].hp; + } + + if (gBattlescriptCurrInstr[1] == 5) + { + u32 hitmarkerFaintBits = gHitMarker >> 0x1C; + + ++gBattlerFainted; + while (TRUE) + { + if (hitmarkerFaintBits & gBitTable[gBattlerFainted] && !(gAbsentBattlerFlags & gBitTable[gBattlerFainted])) + break; + if (gBattlerFainted >= gBattlersCount) + break; + ++gBattlerFainted; + } + } + gBattlescriptCurrInstr += 2; + } + } +} + +static void atk53_trainerslidein(void) +{ + if (!gBattlescriptCurrInstr[1]) + gActiveBattler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); + else + gActiveBattler = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); + BtlController_EmitTrainerSlide(0); + MarkBattlerForControllerExec(gActiveBattler); + gBattlescriptCurrInstr += 2; +} + +static void atk54_playse(void) +{ + gActiveBattler = gBattlerAttacker; + BtlController_EmitPlaySE(0, T2_READ_16(gBattlescriptCurrInstr + 1)); + MarkBattlerForControllerExec(gActiveBattler); + gBattlescriptCurrInstr += 3; +} + +static void atk55_fanfare(void) +{ + gActiveBattler = gBattlerAttacker; + BtlController_EmitPlayFanfare(0, T2_READ_16(gBattlescriptCurrInstr + 1)); + MarkBattlerForControllerExec(gActiveBattler); + gBattlescriptCurrInstr += 3; +} + +static void atk56_playfaintcry(void) +{ + gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + BtlController_EmitFaintingCry(0); + MarkBattlerForControllerExec(gActiveBattler); + gBattlescriptCurrInstr += 2; +} + +static void atk57(void) +{ + gActiveBattler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); + BtlController_EmitCmd55(0, gBattleOutcome); + MarkBattlerForControllerExec(gActiveBattler); + gBattlescriptCurrInstr += 1; +} + +static void atk58_returntoball(void) +{ + gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + BtlController_EmitReturnMonToBall(0, 1); + MarkBattlerForControllerExec(gActiveBattler); + gBattlescriptCurrInstr += 2; +} + +static void atk59_handlelearnnewmove(void) +{ + const u8 *jumpPtr1 = T1_READ_PTR(gBattlescriptCurrInstr + 1); + const u8 *jumpPtr2 = T1_READ_PTR(gBattlescriptCurrInstr + 5); + u16 ret = MonTryLearningNewMove(&gPlayerParty[gBattleStruct->expGetterMonId], gBattlescriptCurrInstr[9]); + + while (ret == 0xFFFE) + ret = MonTryLearningNewMove(&gPlayerParty[gBattleStruct->expGetterMonId], 0); + if (ret == 0) + { + gBattlescriptCurrInstr = jumpPtr2; + } + else if (ret == 0xFFFF) + { + gBattlescriptCurrInstr += 10; + } + else + { + gActiveBattler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); + if (gBattlerPartyIndexes[gActiveBattler] == gBattleStruct->expGetterMonId + && !(gBattleMons[gActiveBattler].status2 & STATUS2_TRANSFORMED)) + { + GiveMoveToBattleMon(&gBattleMons[gActiveBattler], ret); + } + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + { + gActiveBattler = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT); + if (gBattlerPartyIndexes[gActiveBattler] == gBattleStruct->expGetterMonId + && !(gBattleMons[gActiveBattler].status2 & STATUS2_TRANSFORMED)) + { + GiveMoveToBattleMon(&gBattleMons[gActiveBattler], ret); + } + } + gBattlescriptCurrInstr = jumpPtr1; + } +} + +static void atk5A_yesnoboxlearnmove(void) +{ + gActiveBattler = 0; + + switch (gBattleScripting.learnMoveState) + { + case 0: + HandleBattleWindow(0x17, 8, 0x1D, 0xD, 0); + BattlePutTextOnWindow(gText_BattleYesNoChoice, 0xE); + ++gBattleScripting.learnMoveState; + gBattleCommunication[CURSOR_POSITION] = 0; + BattleCreateYesNoCursorAt(); + break; + case 1: + if (JOY_NEW(DPAD_UP) && gBattleCommunication[CURSOR_POSITION] != 0) + { + PlaySE(SE_SELECT); + BattleDestroyYesNoCursorAt(); + gBattleCommunication[CURSOR_POSITION] = 0; + BattleCreateYesNoCursorAt(); + } + if (JOY_NEW(DPAD_DOWN) && gBattleCommunication[CURSOR_POSITION] == 0) + { + PlaySE(SE_SELECT); + BattleDestroyYesNoCursorAt(); + gBattleCommunication[CURSOR_POSITION] = 1; + BattleCreateYesNoCursorAt(); + } + if (JOY_NEW(A_BUTTON)) + { + PlaySE(SE_SELECT); + if (gBattleCommunication[1] == 0) + { + HandleBattleWindow(0x17, 0x8, 0x1D, 0xD, WINDOW_CLEAR); + BeginNormalPaletteFade(0xFFFFFFFF, 0, 0, 0x10, RGB_BLACK); + ++gBattleScripting.learnMoveState; + } + else + { + gBattleScripting.learnMoveState = 4; + } + } + else if (JOY_NEW(B_BUTTON)) + { + PlaySE(SE_SELECT); + gBattleScripting.learnMoveState = 4; + } + break; + case 2: + if (!gPaletteFade.active) + { + FreeAllWindowBuffers(); + ShowSelectMovePokemonSummaryScreen(gPlayerParty, gBattleStruct->expGetterMonId, gPlayerPartyCount - 1, ReshowBattleScreenAfterMenu, gMoveToLearn); + ++gBattleScripting.learnMoveState; + } + break; + case 3: + if (!gPaletteFade.active && gMain.callback2 == BattleMainCB2) + { + u8 movePosition = sub_8138B2C(); + + if (movePosition == 4) + { + gBattleScripting.learnMoveState = 4; + } + else + { + u16 moveId = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_MOVE1 + movePosition); + + if (IsHMMove2(moveId)) + { + PrepareStringBattle(STRINGID_HMMOVESCANTBEFORGOTTEN, gActiveBattler); + gBattleScripting.learnMoveState = 5; + } + else + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + PREPARE_MOVE_BUFFER(gBattleTextBuff2, moveId) + RemoveMonPPBonus(&gPlayerParty[gBattleStruct->expGetterMonId], movePosition); + SetMonMoveSlot(&gPlayerParty[gBattleStruct->expGetterMonId], gMoveToLearn, movePosition); + if (gBattlerPartyIndexes[0] == gBattleStruct->expGetterMonId + && !(gBattleMons[0].status2 & STATUS2_TRANSFORMED) + && !(gDisableStructs[0].mimickedMoves & gBitTable[movePosition])) + { + RemoveBattleMonPPBonus(&gBattleMons[0], movePosition); + SetBattleMonMoveSlot(&gBattleMons[0], gMoveToLearn, movePosition); + } + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE + && gBattlerPartyIndexes[2] == gBattleStruct->expGetterMonId + && !(gBattleMons[2].status2 & STATUS2_TRANSFORMED) + && !(gDisableStructs[2].mimickedMoves & gBitTable[movePosition])) + { + RemoveBattleMonPPBonus(&gBattleMons[2], movePosition); + SetBattleMonMoveSlot(&gBattleMons[2], gMoveToLearn, movePosition); + } + } + } + } + break; + case 4: + HandleBattleWindow(0x17, 8, 0x1D, 0xD, WINDOW_CLEAR); + gBattlescriptCurrInstr += 5; + break; + case 5: + if (!gBattleControllerExecFlags) + { + gBattleScripting.learnMoveState = 2; + } + break; + } +} + +static void atk5B_yesnoboxstoplearningmove(void) +{ + switch (gBattleScripting.learnMoveState) + { + case 0: + HandleBattleWindow(0x17, 8, 0x1D, 0xD, 0); + BattlePutTextOnWindow(gText_BattleYesNoChoice, 0xE); + ++gBattleScripting.learnMoveState; + gBattleCommunication[CURSOR_POSITION] = 0; + BattleCreateYesNoCursorAt(); + break; + case 1: + if (JOY_NEW(DPAD_UP) && gBattleCommunication[CURSOR_POSITION] != 0) + { + PlaySE(SE_SELECT); + BattleDestroyYesNoCursorAt(); + gBattleCommunication[CURSOR_POSITION] = 0; + BattleCreateYesNoCursorAt(); + } + if (JOY_NEW(DPAD_DOWN) && gBattleCommunication[CURSOR_POSITION] == 0) + { + PlaySE(SE_SELECT); + BattleDestroyYesNoCursorAt(); + gBattleCommunication[CURSOR_POSITION] = 1; + BattleCreateYesNoCursorAt(); + } + if (JOY_NEW(A_BUTTON)) + { + PlaySE(SE_SELECT); + + if (gBattleCommunication[1] != 0) + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + else + gBattlescriptCurrInstr += 5; + HandleBattleWindow(0x17, 0x8, 0x1D, 0xD, WINDOW_CLEAR); + } + else if (JOY_NEW(B_BUTTON)) + { + PlaySE(SE_SELECT); + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + HandleBattleWindow(0x17, 0x8, 0x1D, 0xD, WINDOW_CLEAR); + } + break; + } +} + +static void atk5C_hitanimation(void) +{ + gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + if (gMoveResultFlags & MOVE_RESULT_NO_EFFECT) + { + gBattlescriptCurrInstr += 2; + } + else if (!(gHitMarker & HITMARKER_IGNORE_SUBSTITUTE) + || !(gBattleMons[gActiveBattler].status2 & STATUS2_SUBSTITUTE) + || gDisableStructs[gActiveBattler].substituteHP == 0) + { + BtlController_EmitHitAnimation(0); + MarkBattlerForControllerExec(gActiveBattler); + gBattlescriptCurrInstr += 2; + } + else + { + gBattlescriptCurrInstr += 2; + } +} + +#ifdef NONMATCHING +static void atk5D_getmoneyreward(void) +{ + u32 i = 0; + u32 lastMonLevel = 0; + u32 moneyReward = 0; + u32 value; + + // The whole function is using wrong registers. + if (gBattleOutcome == B_OUTCOME_WON) + { + if (gTrainerBattleOpponent_A == 0x400) + { + moneyReward = 20 * gBattleResources->secretBase->party.levels[0] * gBattleStruct->moneyMultiplier; + } + else + { + switch (gTrainers[gTrainerBattleOpponent_A].partyFlags) + { + case 0: + { + const struct TrainerMonNoItemDefaultMoves *party = gTrainers[gTrainerBattleOpponent_A].party.NoItemDefaultMoves; + + lastMonLevel = party[gTrainers[gTrainerBattleOpponent_A].partySize - 1].lvl; + } + break; + case F_TRAINER_PARTY_CUSTOM_MOVESET: + { + const struct TrainerMonNoItemCustomMoves *party = gTrainers[gTrainerBattleOpponent_A].party.NoItemCustomMoves; + + lastMonLevel = party[gTrainers[gTrainerBattleOpponent_A].partySize - 1].lvl; + } + break; + case F_TRAINER_PARTY_HELD_ITEM: + { + const struct TrainerMonItemDefaultMoves *party = gTrainers[gTrainerBattleOpponent_A].party.ItemDefaultMoves; + + lastMonLevel = party[gTrainers[gTrainerBattleOpponent_A].partySize - 1].lvl; + } + break; + case F_TRAINER_PARTY_CUSTOM_MOVESET | F_TRAINER_PARTY_HELD_ITEM: + { + const struct TrainerMonItemCustomMoves *party = gTrainers[gTrainerBattleOpponent_A].party.ItemCustomMoves; + + lastMonLevel = party[gTrainers[gTrainerBattleOpponent_A].partySize - 1].lvl; + } + break; + } + for (; gTrainerMoneyTable[i].classId != 0xFF; ++i) + { + if (gTrainerMoneyTable[i].classId == gTrainers[gTrainerBattleOpponent_A].trainerClass) + break; + } + moneyReward = 4 * lastMonLevel; + moneyReward *= gBattleStruct->moneyMultiplier; + value = gTrainerMoneyTable[i].value; + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + moneyReward *= (value << 1); + else + moneyReward *= value; + } + AddMoney(&gSaveBlock1Ptr->money, moneyReward); + } + else + { + moneyReward = sub_8054C04(); + } + PREPARE_WORD_NUMBER_BUFFER(gBattleTextBuff1, 5, moneyReward); + if (moneyReward) + gBattlescriptCurrInstr += 5; + else + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); +} +#else +NAKED +static void atk5D_getmoneyreward(void) +{ + asm_unified("\n\ + push {r4-r7,lr}\n\ + mov r7, r8\n\ + push {r7}\n\ + movs r6, 0\n\ + movs r5, 0\n\ + ldr r0, _080258F0 @ =gBattleOutcome\n\ + ldrb r0, [r0]\n\ + cmp r0, 0x1\n\ + beq _080258C0\n\ + b _080259FA\n\ + _080258C0:\n\ + ldr r0, _080258F4 @ =gTrainerBattleOpponent_A\n\ + ldrh r2, [r0]\n\ + movs r1, 0x80\n\ + lsls r1, 3\n\ + cmp r2, r1\n\ + bne _08025904\n\ + ldr r0, _080258F8 @ =gBattleResources\n\ + ldr r0, [r0]\n\ + ldr r0, [r0]\n\ + adds r0, 0x94\n\ + ldrb r2, [r0]\n\ + ldr r0, _080258FC @ =gBattleStruct\n\ + ldr r0, [r0]\n\ + adds r0, 0x4A\n\ + ldrb r1, [r0]\n\ + lsls r0, r1, 2\n\ + adds r0, r1\n\ + lsls r0, 2\n\ + adds r4, r2, 0\n\ + muls r4, r0\n\ + ldr r0, _08025900 @ =gSaveBlock1Ptr\n\ + mov r8, r0\n\ + b _080259E8\n\ + .align 2, 0\n\ + _080258F0: .4byte gBattleOutcome\n\ + _080258F4: .4byte gTrainerBattleOpponent_A\n\ + _080258F8: .4byte gBattleResources\n\ + _080258FC: .4byte gBattleStruct\n\ + _08025900: .4byte gSaveBlock1Ptr\n\ + _08025904:\n\ + ldr r2, _08025920 @ =gTrainers\n\ + ldrh r1, [r0]\n\ + lsls r0, r1, 2\n\ + adds r0, r1\n\ + lsls r3, r0, 3\n\ + adds r4, r3, r2\n\ + ldrb r1, [r4]\n\ + cmp r1, 0x1\n\ + beq _0802595A\n\ + cmp r1, 0x1\n\ + bgt _08025924\n\ + cmp r1, 0\n\ + beq _0802592E\n\ + b _08025970\n\ + .align 2, 0\n\ + _08025920: .4byte gTrainers\n\ + _08025924:\n\ + cmp r1, 0x2\n\ + beq _08025944\n\ + cmp r1, 0x3\n\ + beq _0802595A\n\ + b _08025970\n\ + _0802592E:\n\ + adds r0, r2, 0\n\ + adds r0, 0x24\n\ + adds r0, r3, r0\n\ + ldr r1, [r0]\n\ + adds r0, r4, 0\n\ + adds r0, 0x20\n\ + ldrb r0, [r0]\n\ + lsls r0, 3\n\ + adds r0, r1\n\ + subs r0, 0x8\n\ + b _0802596E\n\ + _08025944:\n\ + adds r0, r2, 0\n\ + adds r0, 0x24\n\ + adds r0, r3, r0\n\ + ldr r1, [r0]\n\ + adds r0, r4, 0\n\ + adds r0, 0x20\n\ + ldrb r0, [r0]\n\ + lsls r0, 3\n\ + adds r0, r1\n\ + subs r0, 0x8\n\ + b _0802596E\n\ + _0802595A:\n\ + adds r0, r2, 0\n\ + adds r0, 0x24\n\ + adds r0, r3, r0\n\ + ldr r1, [r0]\n\ + adds r0, r4, 0\n\ + adds r0, 0x20\n\ + ldrb r0, [r0]\n\ + lsls r0, 4\n\ + adds r0, r1\n\ + subs r0, 0x10\n\ + _0802596E:\n\ + ldrb r5, [r0, 0x2]\n\ + _08025970:\n\ + ldr r0, _080259CC @ =gTrainerMoneyTable\n\ + lsls r1, r6, 2\n\ + adds r3, r1, r0\n\ + ldrb r1, [r3]\n\ + ldr r7, _080259D0 @ =gBattleStruct\n\ + mov r12, r0\n\ + lsls r4, r5, 2\n\ + ldr r5, _080259D4 @ =gBattleTypeFlags\n\ + ldr r0, _080259D8 @ =gSaveBlock1Ptr\n\ + mov r8, r0\n\ + cmp r1, 0xFF\n\ + beq _080259AA\n\ + ldr r2, _080259DC @ =gTrainers\n\ + ldr r0, _080259E0 @ =gTrainerBattleOpponent_A\n\ + ldrh r1, [r0]\n\ + lsls r0, r1, 2\n\ + adds r0, r1\n\ + lsls r0, 3\n\ + adds r0, r2\n\ + ldrb r2, [r0, 0x1]\n\ + adds r1, r3, 0\n\ + _0802599A:\n\ + ldrb r0, [r1]\n\ + cmp r0, r2\n\ + beq _080259AA\n\ + adds r1, 0x4\n\ + adds r6, 0x1\n\ + ldrb r0, [r1]\n\ + cmp r0, 0xFF\n\ + bne _0802599A\n\ + _080259AA:\n\ + ldr r0, [r7]\n\ + adds r0, 0x4A\n\ + ldrb r0, [r0]\n\ + adds r3, r4, 0\n\ + muls r3, r0\n\ + lsls r0, r6, 2\n\ + add r0, r12\n\ + ldrb r2, [r0, 0x1]\n\ + ldr r0, [r5]\n\ + movs r1, 0x1\n\ + ands r0, r1\n\ + cmp r0, 0\n\ + beq _080259E4\n\ + lsls r0, r2, 1\n\ + adds r4, r3, 0\n\ + muls r4, r0\n\ + b _080259E8\n\ + .align 2, 0\n\ + _080259CC: .4byte gTrainerMoneyTable\n\ + _080259D0: .4byte gBattleStruct\n\ + _080259D4: .4byte gBattleTypeFlags\n\ + _080259D8: .4byte gSaveBlock1Ptr\n\ + _080259DC: .4byte gTrainers\n\ + _080259E0: .4byte gTrainerBattleOpponent_A\n\ + _080259E4:\n\ + adds r4, r3, 0\n\ + muls r4, r2\n\ + _080259E8:\n\ + mov r1, r8\n\ + ldr r0, [r1]\n\ + movs r1, 0xA4\n\ + lsls r1, 2\n\ + adds r0, r1\n\ + adds r1, r4, 0\n\ + bl AddMoney\n\ + b _08025A00\n\ + _080259FA:\n\ + bl sub_8054C04\n\ + adds r4, r0, 0\n\ + _08025A00:\n\ + ldr r1, _08025A40 @ =gBattleTextBuff1\n\ + movs r0, 0xFD\n\ + strb r0, [r1]\n\ + movs r0, 0x1\n\ + strb r0, [r1, 0x1]\n\ + movs r0, 0x4\n\ + strb r0, [r1, 0x2]\n\ + movs r0, 0x5\n\ + strb r0, [r1, 0x3]\n\ + strb r4, [r1, 0x4]\n\ + movs r0, 0xFF\n\ + lsls r0, 8\n\ + ands r0, r4\n\ + lsrs r0, 8\n\ + strb r0, [r1, 0x5]\n\ + movs r0, 0xFF\n\ + lsls r0, 16\n\ + ands r0, r4\n\ + lsrs r0, 16\n\ + strb r0, [r1, 0x6]\n\ + lsrs r0, r4, 24\n\ + strb r0, [r1, 0x7]\n\ + movs r0, 0xFF\n\ + strb r0, [r1, 0x8]\n\ + cmp r4, 0\n\ + beq _08025A48\n\ + ldr r1, _08025A44 @ =gBattlescriptCurrInstr\n\ + ldr r0, [r1]\n\ + adds r0, 0x5\n\ + str r0, [r1]\n\ + b _08025A62\n\ + .align 2, 0\n\ + _08025A40: .4byte gBattleTextBuff1\n\ + _08025A44: .4byte gBattlescriptCurrInstr\n\ + _08025A48:\n\ + ldr r3, _08025A6C @ =gBattlescriptCurrInstr\n\ + ldr r2, [r3]\n\ + ldrb r1, [r2, 0x1]\n\ + ldrb r0, [r2, 0x2]\n\ + lsls r0, 8\n\ + orrs r1, r0\n\ + ldrb r0, [r2, 0x3]\n\ + lsls r0, 16\n\ + orrs r1, r0\n\ + ldrb r0, [r2, 0x4]\n\ + lsls r0, 24\n\ + orrs r1, r0\n\ + str r1, [r3]\n\ + _08025A62:\n\ + pop {r3}\n\ + mov r8, r3\n\ + pop {r4-r7}\n\ + pop {r0}\n\ + bx r0\n\ + .align 2, 0\n\ + _08025A6C: .4byte gBattlescriptCurrInstr\n\ + "); +} +#endif + +static void atk5E(void) +{ + gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + + switch (gBattleCommunication[0]) + { + case 0: + BtlController_EmitGetMonData(0, REQUEST_ALL_BATTLE, 0); + MarkBattlerForControllerExec(gActiveBattler); + ++gBattleCommunication[0]; + break; + case 1: + if (!gBattleControllerExecFlags) + { + s32 i; + struct BattlePokemon *bufferPoke = (struct BattlePokemon *) &gBattleBufferB[gActiveBattler][4]; + + for (i = 0; i < MAX_MON_MOVES; ++i) + { + gBattleMons[gActiveBattler].moves[i] = bufferPoke->moves[i]; + gBattleMons[gActiveBattler].pp[i] = bufferPoke->pp[i]; + } + gBattlescriptCurrInstr += 2; + } + break; + } +} + +static void atk5F_swapattackerwithtarget(void) +{ + gActiveBattler = gBattlerAttacker; + gBattlerAttacker = gBattlerTarget; + gBattlerTarget = gActiveBattler; + if (gHitMarker & HITMARKER_SWAP_ATTACKER_TARGET) + gHitMarker &= ~(HITMARKER_SWAP_ATTACKER_TARGET); + else + gHitMarker |= HITMARKER_SWAP_ATTACKER_TARGET; + ++gBattlescriptCurrInstr; +} + +static void atk60_incrementgamestat(void) +{ + if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER) + IncrementGameStat(gBattlescriptCurrInstr[1]); + gBattlescriptCurrInstr += 2; +} + +static void atk61_drawpartystatussummary(void) +{ + s32 i; + struct Pokemon *party; + struct HpAndStatus hpStatuses[PARTY_SIZE]; + + if (!gBattleControllerExecFlags) + { + gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + + if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER) + party = gPlayerParty; + else + party = gEnemyParty; + + for (i = 0; i < PARTY_SIZE; ++i) + { + if (GetMonData(&party[i], MON_DATA_SPECIES2) == SPECIES_NONE + || GetMonData(&party[i], MON_DATA_SPECIES2) == SPECIES_EGG) + { + hpStatuses[i].hp = 0xFFFF; + hpStatuses[i].status = 0; + } + else + { + hpStatuses[i].hp = GetMonData(&party[i], MON_DATA_HP); + hpStatuses[i].status = GetMonData(&party[i], MON_DATA_STATUS); + } + } + BtlController_EmitDrawPartyStatusSummary(0, hpStatuses, 1); + MarkBattlerForControllerExec(gActiveBattler); + gBattlescriptCurrInstr += 2; + } +} + +static void atk62_hidepartystatussummary(void) +{ + gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + BtlController_EmitHidePartyStatusSummary(0); + MarkBattlerForControllerExec(gActiveBattler); + gBattlescriptCurrInstr += 2; +} + +static void atk63_jumptocalledmove(void) +{ + if (gBattlescriptCurrInstr[1]) + gCurrentMove = gCalledMove; + else + gChosenMove = gCurrentMove = gCalledMove; + gBattlescriptCurrInstr = gBattleScriptsForMoveEffects[gBattleMoves[gCurrentMove].effect]; +} + +static void atk64_statusanimation(void) +{ + if (!gBattleControllerExecFlags) + { + gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + if (!(gStatuses3[gActiveBattler] & STATUS3_SEMI_INVULNERABLE) + && gDisableStructs[gActiveBattler].substituteHP == 0 + && !(gHitMarker & HITMARKER_NO_ANIMATIONS)) + { + BtlController_EmitStatusAnimation(0, FALSE, gBattleMons[gActiveBattler].status1); + MarkBattlerForControllerExec(gActiveBattler); + } + gBattlescriptCurrInstr += 2; + } +} + +static void atk65_status2animation(void) +{ + u32 wantedToAnimate; + + if (!gBattleControllerExecFlags) + { + gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + wantedToAnimate = T1_READ_32(gBattlescriptCurrInstr + 2); + if (!(gStatuses3[gActiveBattler] & STATUS3_SEMI_INVULNERABLE) + && gDisableStructs[gActiveBattler].substituteHP == 0 + && !(gHitMarker & HITMARKER_NO_ANIMATIONS)) + { + BtlController_EmitStatusAnimation(0, TRUE, gBattleMons[gActiveBattler].status2 & wantedToAnimate); + MarkBattlerForControllerExec(gActiveBattler); + } + gBattlescriptCurrInstr += 6; + } +} + +static void atk66_chosenstatusanimation(void) +{ + u32 wantedStatus; + + if (!gBattleControllerExecFlags) + { + gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + wantedStatus = T1_READ_32(gBattlescriptCurrInstr + 3); + if (!(gStatuses3[gActiveBattler] & STATUS3_SEMI_INVULNERABLE) + && gDisableStructs[gActiveBattler].substituteHP == 0 + && !(gHitMarker & HITMARKER_NO_ANIMATIONS)) + { + BtlController_EmitStatusAnimation(0, gBattlescriptCurrInstr[2], wantedStatus); + MarkBattlerForControllerExec(gActiveBattler); + } + gBattlescriptCurrInstr += 7; + } +} + +static void atk67_yesnobox(void) +{ + switch (gBattleCommunication[0]) + { + case 0: + HandleBattleWindow(0x17, 8, 0x1D, 0xD, 0); + BattlePutTextOnWindow(gText_BattleYesNoChoice, 0xE); + ++gBattleCommunication[0]; + gBattleCommunication[CURSOR_POSITION] = 0; + BattleCreateYesNoCursorAt(); + break; + case 1: + if (JOY_NEW(DPAD_UP) && gBattleCommunication[CURSOR_POSITION] != 0) + { + PlaySE(SE_SELECT); + BattleDestroyYesNoCursorAt(); + gBattleCommunication[CURSOR_POSITION] = 0; + BattleCreateYesNoCursorAt(); + } + if (JOY_NEW(DPAD_DOWN) && gBattleCommunication[CURSOR_POSITION] == 0) + { + PlaySE(SE_SELECT); + BattleDestroyYesNoCursorAt(); + gBattleCommunication[CURSOR_POSITION] = 1; + BattleCreateYesNoCursorAt(); + } + if (JOY_NEW(B_BUTTON)) + { + gBattleCommunication[CURSOR_POSITION] = 1; + PlaySE(SE_SELECT); + HandleBattleWindow(0x17, 8, 0x1D, 0xD, WINDOW_CLEAR); + ++gBattlescriptCurrInstr; + } + else if (JOY_NEW(A_BUTTON)) + { + PlaySE(SE_SELECT); + HandleBattleWindow(0x17, 8, 0x1D, 0xD, WINDOW_CLEAR); + ++gBattlescriptCurrInstr; + } + break; + } +} + +static void atk68_cancelallactions(void) +{ + s32 i; + + for (i = 0; i < gBattlersCount; ++i) + gActionsByTurnOrder[i] = B_ACTION_CANCEL_PARTNER; + ++gBattlescriptCurrInstr; +} + +// The same as 0x7, except there's no random damage multiplier. +static void atk69_adjustsetdamage(void) +{ + u8 holdEffect, param; + + if (gBattleMons[gBattlerTarget].item == ITEM_ENIGMA_BERRY) + { + holdEffect = gEnigmaBerries[gBattlerTarget].holdEffect; + param = gEnigmaBerries[gBattlerTarget].holdEffectParam; + } + else + { + holdEffect = ItemId_GetHoldEffect(gBattleMons[gBattlerTarget].item); + param = ItemId_GetHoldEffectParam(gBattleMons[gBattlerTarget].item); + } + gPotentialItemEffectBattler = gBattlerTarget; + if (holdEffect == HOLD_EFFECT_FOCUS_BAND && (Random() % 100) < param) + { + RecordItemEffectBattle(gBattlerTarget, holdEffect); + gSpecialStatuses[gBattlerTarget].focusBanded = 1; + } + if (!(gBattleMons[gBattlerTarget].status2 & STATUS2_SUBSTITUTE) + && (gBattleMoves[gCurrentMove].effect == EFFECT_FALSE_SWIPE || gProtectStructs[gBattlerTarget].endured || gSpecialStatuses[gBattlerTarget].focusBanded) + && gBattleMons[gBattlerTarget].hp <= gBattleMoveDamage) + { + gBattleMoveDamage = gBattleMons[gBattlerTarget].hp - 1; + if (gProtectStructs[gBattlerTarget].endured) + { + gMoveResultFlags |= MOVE_RESULT_FOE_ENDURED; + } + else if (gSpecialStatuses[gBattlerTarget].focusBanded) + { + gMoveResultFlags |= MOVE_RESULT_FOE_HUNG_ON; + gLastUsedItem = gBattleMons[gBattlerTarget].item; + } + } + ++gBattlescriptCurrInstr; +} + +static void atk6A_removeitem(void) +{ + u16 *usedHeldItem; + + gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + usedHeldItem = &gBattleStruct->usedHeldItems[gActiveBattler]; + *usedHeldItem = gBattleMons[gActiveBattler].item; + gBattleMons[gActiveBattler].item = ITEM_NONE; + BtlController_EmitSetMonData(0, REQUEST_HELDITEM_BATTLE, 0, 2, &gBattleMons[gActiveBattler].item); + MarkBattlerForControllerExec(gActiveBattler); + gBattlescriptCurrInstr += 2; +} + +static void atk6B_atknameinbuff1(void) +{ + PREPARE_MON_NICK_BUFFER(gBattleTextBuff1, gBattlerAttacker, gBattlerPartyIndexes[gBattlerAttacker]) + ++gBattlescriptCurrInstr; +} + +static void atk6C_drawlvlupbox(void) +{ + if (gBattleScripting.atk6C_state == 0) + { + if (IsMonGettingExpSentOut()) + gBattleScripting.atk6C_state = 3; + else + gBattleScripting.atk6C_state = 1; + } + + switch (gBattleScripting.atk6C_state) + { + case 1: + gBattle_BG2_Y = 0x60; + SetBgAttribute(2, BG_ATTR_PRIORITY, 0); + ShowBg(2); + sub_8026480(); + gBattleScripting.atk6C_state = 2; + break; + case 2: + if (!sub_80264D0()) + gBattleScripting.atk6C_state = 3; + break; + case 3: + gBattle_BG1_X = 0; + gBattle_BG1_Y = 0x100; + SetBgAttribute(0, BG_ATTR_PRIORITY, 1); + SetBgAttribute(1, BG_ATTR_PRIORITY, 0); + ShowBg(0); + ShowBg(1); + HandleBattleWindow(18, 7, 0x1D, 0x13, WINDOW_x80); + gBattleScripting.atk6C_state = 4; + break; + case 4: + DrawLevelUpWindow1(); + PutWindowTilemap(12); + CopyWindowToVram(12, 3); + ++gBattleScripting.atk6C_state; + break; + case 5: + case 7: + if (!IsDma3ManagerBusyWithBgCopy()) + { + gBattle_BG1_Y = 0; + ++gBattleScripting.atk6C_state; + } + break; + case 6: + if (gMain.newKeys) + { + PlaySE(SE_SELECT); + DrawLevelUpWindow2(); + CopyWindowToVram(12, 2); + ++gBattleScripting.atk6C_state; + } + break; + case 8: + if (gMain.newKeys) + { + PlaySE(SE_SELECT); + HandleBattleWindow(18, 7, 0x1D, 0x13, WINDOW_x80 | WINDOW_CLEAR); + ++gBattleScripting.atk6C_state; + } + break; + case 9: + if (!sub_8026648()) + { + ClearWindowTilemap(13); + CopyWindowToVram(13, 1); + ClearWindowTilemap(12); + CopyWindowToVram(12, 1); + SetBgAttribute(2, BG_ATTR_PRIORITY, 2); + ShowBg(2); + gBattleScripting.atk6C_state = 10; + } + break; + case 10: + if (!IsDma3ManagerBusyWithBgCopy()) + { + SetBgAttribute(0, BG_ATTR_PRIORITY, 0); + SetBgAttribute(1, BG_ATTR_PRIORITY, 1); + ShowBg(0); + ShowBg(1); + ++gBattlescriptCurrInstr; + } + break; + } +} + +static void DrawLevelUpWindow1(void) +{ + u16 currStats[NUM_STATS]; + + GetMonLevelUpWindowStats(&gPlayerParty[gBattleStruct->expGetterMonId], currStats); + DrawLevelUpWindowPg1(12, gBattleResources->beforeLvlUp->stats, currStats, 0xE, 0xD, 0xF); +} + +static void DrawLevelUpWindow2(void) +{ + u16 currStats[NUM_STATS]; + + GetMonLevelUpWindowStats(&gPlayerParty[gBattleStruct->expGetterMonId], currStats); + DrawLevelUpWindowPg2(12, currStats, 0xE, 0xD, 0xF); +} + +static void sub_8026480(void) +{ + gBattle_BG2_Y = 0; + gBattle_BG2_X = 0x1A0; + LoadPalette(gUnknown_82506D0, 0x60, 0x20); + CopyToWindowPixelBuffer(13, gUnknown_82506F0, 0, 0); + PutWindowTilemap(13); + CopyWindowToVram(13, 3); + PutMonIconOnLvlUpBox(); +} + +static bool8 sub_80264D0(void) +{ + if (IsDma3ManagerBusyWithBgCopy()) + return TRUE; + if (gBattle_BG2_X == 0x200) + return FALSE; + if (gBattle_BG2_X == 0x1A0) + PutLevelAndGenderOnLvlUpBox(); + gBattle_BG2_X += 8; + if (gBattle_BG2_X >= 0x200) + gBattle_BG2_X = 0x200; + return (gBattle_BG2_X != 0x200); +} + +static void PutLevelAndGenderOnLvlUpBox(void) +{ + u16 monLevel; + u8 monGender; + struct TextPrinterTemplate printerTemplate; + u8 *txtPtr; + u8 *txtPtr2; + + monLevel = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_LEVEL); + monGender = GetMonGender(&gPlayerParty[gBattleStruct->expGetterMonId]); + GetMonNickname(&gPlayerParty[gBattleStruct->expGetterMonId], gStringVar4); + printerTemplate.currentChar = gStringVar4; + printerTemplate.windowId = 13; + printerTemplate.fontId = 0; + printerTemplate.x = 32; + printerTemplate.y = 0; + printerTemplate.currentX = 32; + printerTemplate.currentY = 0; + printerTemplate.letterSpacing = 0; + printerTemplate.lineSpacing = 0; + printerTemplate.unk = 0; + printerTemplate.fgColor = TEXT_COLOR_WHITE; + printerTemplate.bgColor = TEXT_COLOR_TRANSPARENT; + printerTemplate.shadowColor = TEXT_COLOR_DARK_GREY; + AddTextPrinter(&printerTemplate, 0xFF, NULL); + txtPtr = gStringVar4; + gStringVar4[0] = 0xF9; + *++txtPtr = 5; + *++txtPtr = 0; + txtPtr2 = txtPtr + 1; + txtPtr = ConvertIntToDecimalStringN(++txtPtr, monLevel, STR_CONV_MODE_LEFT_ALIGN, 3); + txtPtr = StringFill(txtPtr, 0, 5); + txtPtr = txtPtr2 + 4; + if (monGender != MON_GENDERLESS) + { + if (monGender == MON_MALE) + { + txtPtr = WriteColorChangeControlCode(txtPtr, 0, 0xC); + txtPtr = WriteColorChangeControlCode(txtPtr, 1, 0xD); + *(txtPtr++) = CHAR_MALE; + } + else + { + txtPtr = WriteColorChangeControlCode(txtPtr, 0, 0xE); + txtPtr = WriteColorChangeControlCode(txtPtr, 1, 0xF); + *(txtPtr++) = CHAR_FEMALE; + } + *(txtPtr++) = EOS; + } + printerTemplate.y = 10; + printerTemplate.currentY = 10; + AddTextPrinter(&printerTemplate, 0xFF, NULL); + CopyWindowToVram(13, 2); +} + +static bool8 sub_8026648(void) +{ + if (gBattle_BG2_X == 0x1A0) + return FALSE; + if (gBattle_BG2_X - 16 < 0x1A0) + gBattle_BG2_X = 0x1A0; + else + gBattle_BG2_X -= 16; + return (gBattle_BG2_X != 0x1A0); +} + +#define sDestroy data[0] +#define sSavedLvlUpBoxXPosition data[1] + +static void PutMonIconOnLvlUpBox(void) +{ + u8 spriteId; + const u16 *iconPal; + struct SpriteSheet iconSheet; + struct SpritePalette iconPalSheet; + u16 species = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_SPECIES); + u32 personality = GetMonData(&gPlayerParty[gBattleStruct->expGetterMonId], MON_DATA_PERSONALITY); + const u8 *iconPtr = GetMonIconPtr(species, personality, 1); + + iconSheet.data = iconPtr; + iconSheet.size = 0x200; + iconSheet.tag = MON_ICON_LVLUP_BOX_TAG; + iconPal = GetValidMonIconPalettePtr(species); + iconPalSheet.data = iconPal; + iconPalSheet.tag = MON_ICON_LVLUP_BOX_TAG; + LoadSpriteSheet(&iconSheet); + LoadSpritePalette(&iconPalSheet); + spriteId = CreateSprite(&sSpriteTemplate_MonIconOnLvlUpBox, 256, 10, 0); + gSprites[spriteId].sDestroy = FALSE; + gSprites[spriteId].sSavedLvlUpBoxXPosition = gBattle_BG2_X; +} + +static void SpriteCB_MonIconOnLvlUpBox(struct Sprite* sprite) +{ + sprite->pos2.x = sprite->sSavedLvlUpBoxXPosition - gBattle_BG2_X; + if (sprite->pos2.x != 0) + { + sprite->sDestroy = TRUE; + } + else if (sprite->sDestroy) + { + DestroySprite(sprite); + FreeSpriteTilesByTag(MON_ICON_LVLUP_BOX_TAG); + FreeSpritePaletteByTag(MON_ICON_LVLUP_BOX_TAG); + } +} + +bool32 IsMonGettingExpSentOut(void) +{ + if (gBattlerPartyIndexes[0] == gBattleStruct->expGetterMonId) + return TRUE; + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && gBattlerPartyIndexes[2] == gBattleStruct->expGetterMonId) + return TRUE; + return FALSE; +} + +static void atk6D_resetsentmonsvalue(void) +{ + ResetSentPokesToOpponentValue(); + ++gBattlescriptCurrInstr; +} + +static void atk6E_setatktoplayer0(void) +{ + gBattlerAttacker = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); + ++gBattlescriptCurrInstr; +} + +static void atk6F_makevisible(void) +{ + gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + BtlController_EmitSpriteInvisibility(0, FALSE); + MarkBattlerForControllerExec(gActiveBattler); + + gBattlescriptCurrInstr += 2; +} + +static void atk70_recordlastability(void) +{ + gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + RecordAbilityBattle(gActiveBattler, gLastUsedAbility); + gBattlescriptCurrInstr += 1; // UB: Should be + 2, one byte for command and one byte for battlerId argument. +} + +void BufferMoveToLearnIntoBattleTextBuff2(void) +{ + PREPARE_MOVE_BUFFER(gBattleTextBuff2, gMoveToLearn); +} + +static void atk71_buffermovetolearn(void) +{ + BufferMoveToLearnIntoBattleTextBuff2(); + ++gBattlescriptCurrInstr; +} + +static void atk72_jumpifplayerran(void) +{ + if (TryRunFromBattle(gBattlerFainted)) + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + else + gBattlescriptCurrInstr += 5; +} + +static void atk73_hpthresholds(void) +{ + u8 opposingBattler; + s32 result; + + if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE)) + { + gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + opposingBattler = gActiveBattler ^ BIT_SIDE; + + result = gBattleMons[opposingBattler].hp * 100 / gBattleMons[opposingBattler].maxHP; + if (result == 0) + result = 1; + if (result > 69 || !gBattleMons[opposingBattler].hp) + gBattleStruct->hpScale = 0; + else if (result > 39) + gBattleStruct->hpScale = 1; + else if (result > 9) + gBattleStruct->hpScale = 2; + else + gBattleStruct->hpScale = 3; + } + gBattlescriptCurrInstr += 2; +} + +static void atk74_hpthresholds2(void) +{ + u8 opposingBattler; + s32 result; + u8 hpSwitchout; + + if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE)) + { + gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + opposingBattler = gActiveBattler ^ BIT_SIDE; + hpSwitchout = *(gBattleStruct->hpOnSwitchout + GetBattlerSide(opposingBattler)); + result = (hpSwitchout - gBattleMons[opposingBattler].hp) * 100 / hpSwitchout; + + if (gBattleMons[opposingBattler].hp >= hpSwitchout) + gBattleStruct->hpScale = 0; + else if (result <= 29) + gBattleStruct->hpScale = 1; + else if (result <= 69) + gBattleStruct->hpScale = 2; + else + gBattleStruct->hpScale = 3; + } + gBattlescriptCurrInstr += 2; +} + +static void atk75_useitemonopponent(void) +{ + gBattlerInMenuId = gBattlerAttacker; + PokemonUseItemEffects(&gEnemyParty[gBattlerPartyIndexes[gBattlerAttacker]], gLastUsedItem, gBattlerPartyIndexes[gBattlerAttacker], 0, 1); + ++gBattlescriptCurrInstr; +} + +static void atk76_various(void) +{ + u8 side; + s32 i; + u32 monToCheck, status; + u16 species; + u8 abilityNum; + + gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + + switch (gBattlescriptCurrInstr[2]) + { + case VARIOUS_CANCEL_MULTI_TURN_MOVES: + CancelMultiTurnMoves(gActiveBattler); + break; + case VARIOUS_SET_MAGIC_COAT_TARGET: + gBattlerAttacker = gBattlerTarget; + side = GetBattlerSide(gBattlerAttacker) ^ BIT_SIDE; + if (gSideTimers[side].followmeTimer != 0 && gBattleMons[gSideTimers[side].followmeTarget].hp != 0) + gBattlerTarget = gSideTimers[side].followmeTarget; + else + gBattlerTarget = gActiveBattler; + break; + case VARIOUS_IS_RUNNING_IMPOSSIBLE: + gBattleCommunication[0] = IsRunningFromBattleImpossible(); + break; + case VARIOUS_GET_MOVE_TARGET: + gBattlerTarget = GetMoveTarget(gCurrentMove, 0); + break; + case VARIOUS_CASE_4: + if (gHitMarker & HITMARKER_FAINTED(gActiveBattler)) + gBattleCommunication[0] = 1; + else + gBattleCommunication[0] = 0; + break; + case VARIOUS_RESET_INTIMIDATE_TRACE_BITS: + gSpecialStatuses[gActiveBattler].intimidatedMon = 0; + gSpecialStatuses[gActiveBattler].traced = 0; + break; + case VARIOUS_UPDATE_CHOICE_MOVE_ON_LVL_UP: + if (gBattlerPartyIndexes[0] == gBattleStruct->expGetterMonId || gBattlerPartyIndexes[2] == gBattleStruct->expGetterMonId) + { + u16 *choicedMove; + + if (gBattlerPartyIndexes[0] == gBattleStruct->expGetterMonId) + gActiveBattler = 0; + else + gActiveBattler = 2; + choicedMove = &gBattleStruct->choicedMove[gActiveBattler]; + for (i = 0; i < MAX_MON_MOVES; ++i) + { + if (gBattleMons[gActiveBattler].moves[i] == *choicedMove) + break; + } + if (i == MAX_MON_MOVES) + *choicedMove = MOVE_NONE; + } + break; + case VARIOUS_CASE_7: + if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_DOUBLE)) + && gBattleTypeFlags & BATTLE_TYPE_TRAINER + && gBattleMons[0].hp != 0 + && gBattleMons[1].hp != 0) + gHitMarker &= ~(HITMARKER_x400000); + break; + case VARIOUS_CASE_8: + i = 0; // redundant + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + gActiveBattler = 1; + for (i = 0; gActiveBattler < MAX_BATTLERS_COUNT; gActiveBattler += 2) + { + if (gActiveBattler < gBattlersCount && gBattleMons[gActiveBattler].hp != 0) + gBattleCommunication[MULTISTRING_CHOOSER] |= gBitTable[i]; + ++i; + } + break; + case VARIOUS_RETURN_OPPONENT_MON1: + gActiveBattler = 1; + if (gBattleMons[gActiveBattler].hp != 0) + { + BtlController_EmitReturnMonToBall(0, 0); + MarkBattlerForControllerExec(gActiveBattler); + } + break; + case VARIOUS_RETURN_OPPONENT_MON2: + if (gBattlersCount > 3) + { + gActiveBattler = 3; + if (gBattleMons[gActiveBattler].hp != 0) + { + BtlController_EmitReturnMonToBall(0, 0); + MarkBattlerForControllerExec(gActiveBattler); + } + } + break; + case VARIOUS_CASE_11: + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + monToCheck = 0; + for (i = 0; i < gBattlersCount; ++i) + { + if (gBattleMons[i].ability != ABILITY_SOUNDPROOF) + { + gBattleMons[i].status1 &= ~STATUS1_SLEEP; + gBattleMons[i].status2 &= ~STATUS2_NIGHTMARE; + + } + } + for (i = 0; i < PARTY_SIZE; ++i) + { + species = GetMonData(&gPlayerParty[i], MON_DATA_SPECIES2); + abilityNum = GetMonData(&gPlayerParty[i], MON_DATA_ABILITY_NUM); + status = GetMonData(&gPlayerParty[i], MON_DATA_STATUS); + if (species != SPECIES_NONE + && species != SPECIES_EGG + && status & AILMENT_FNT + && GetAbilityBySpecies(species, abilityNum) != ABILITY_SOUNDPROOF) + monToCheck |= (1 << i); + } + if (monToCheck) + { + gActiveBattler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); + status = 0; + BtlController_EmitSetMonData(0, REQUEST_STATUS_BATTLE, monToCheck, 4, &status); + MarkBattlerForControllerExec(gActiveBattler); + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + } + monToCheck = 0; + for (i = 0; i < PARTY_SIZE; ++i) + { + species = GetMonData(&gEnemyParty[i], MON_DATA_SPECIES2); + abilityNum = GetMonData(&gEnemyParty[i], MON_DATA_ABILITY_NUM); + status = GetMonData(&gEnemyParty[i], MON_DATA_STATUS); + + if (species != SPECIES_NONE + && species != SPECIES_EGG + && status & AILMENT_FNT + && GetAbilityBySpecies(species, abilityNum) != ABILITY_SOUNDPROOF) + monToCheck |= (1 << i); + } + if (monToCheck) + { + gActiveBattler = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); + status = 0; + BtlController_EmitSetMonData(0, REQUEST_STATUS_BATTLE, monToCheck, 4, &status); + MarkBattlerForControllerExec(gActiveBattler); + gBattleCommunication[5] = 1; + } + break; + case VARIOUS_CASE_12: + if (!IsFanfareTaskInactive()) + return; + break; + } + gBattlescriptCurrInstr += 3; +} + +static void atk77_setprotectlike(void) +{ + bool8 notLastTurn = TRUE; + u16 lastMove = gLastResultingMoves[gBattlerAttacker]; + + if (lastMove != MOVE_PROTECT && lastMove != MOVE_DETECT && lastMove != MOVE_ENDURE) + gDisableStructs[gBattlerAttacker].protectUses = 0; + if (gCurrentTurnActionNumber == (gBattlersCount - 1)) + notLastTurn = FALSE; + if (sProtectSuccessRates[gDisableStructs[gBattlerAttacker].protectUses] >= Random() && notLastTurn) + { + if (gBattleMoves[gCurrentMove].effect == EFFECT_PROTECT) + { + gProtectStructs[gBattlerAttacker].protected = 1; + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + } + if (gBattleMoves[gCurrentMove].effect == EFFECT_ENDURE) + { + gProtectStructs[gBattlerAttacker].endured = 1; + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + } + ++gDisableStructs[gBattlerAttacker].protectUses; + } + else + { + gDisableStructs[gBattlerAttacker].protectUses = 0; + gBattleCommunication[MULTISTRING_CHOOSER] = 2; + gMoveResultFlags |= MOVE_RESULT_MISSED; + } + ++gBattlescriptCurrInstr; +} + +static void atk78_faintifabilitynotdamp(void) +{ + if (!gBattleControllerExecFlags) + { + for (gBattlerTarget = 0; gBattlerTarget < gBattlersCount; ++gBattlerTarget) + if (gBattleMons[gBattlerTarget].ability == ABILITY_DAMP) + break; + if (gBattlerTarget == gBattlersCount) + { + gActiveBattler = gBattlerAttacker; + gBattleMoveDamage = gBattleMons[gActiveBattler].hp; + BtlController_EmitHealthBarUpdate(0, INSTANT_HP_BAR_DROP); + MarkBattlerForControllerExec(gActiveBattler); + ++gBattlescriptCurrInstr; + + for (gBattlerTarget = 0; gBattlerTarget < gBattlersCount; ++gBattlerTarget) + if (gBattlerTarget != gBattlerAttacker && !(gAbsentBattlerFlags & gBitTable[gBattlerTarget])) + break; + } + else + { + gLastUsedAbility = ABILITY_DAMP; + RecordAbilityBattle(gBattlerTarget, gBattleMons[gBattlerTarget].ability); + gBattlescriptCurrInstr = BattleScript_DampStopsExplosion; + } + } +} + +static void atk79_setatkhptozero(void) +{ + if (!gBattleControllerExecFlags) + { + gActiveBattler = gBattlerAttacker; + gBattleMons[gActiveBattler].hp = 0; + BtlController_EmitSetMonData(0, REQUEST_HP_BATTLE, 0, 2, &gBattleMons[gActiveBattler].hp); + MarkBattlerForControllerExec(gActiveBattler); + ++gBattlescriptCurrInstr; + } +} + +static void atk7A_jumpifnexttargetvalid(void) +{ + const u8 *jumpPtr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + { + for (++gBattlerTarget; ; ++gBattlerTarget) + if (gBattlerTarget != gBattlerAttacker && !(gAbsentBattlerFlags & gBitTable[gBattlerTarget])) + break; + if (gBattlerTarget >= gBattlersCount) + gBattlescriptCurrInstr += 5; + else + gBattlescriptCurrInstr = jumpPtr; + } + else + { + gBattlescriptCurrInstr += 5; + } +} + +static void atk7B_tryhealhalfhealth(void) +{ + const u8 *failPtr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + + if (gBattlescriptCurrInstr[5] == BS_ATTACKER) + gBattlerTarget = gBattlerAttacker; + gBattleMoveDamage = gBattleMons[gBattlerTarget].maxHP / 2; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + gBattleMoveDamage *= -1; + if (gBattleMons[gBattlerTarget].hp == gBattleMons[gBattlerTarget].maxHP) + gBattlescriptCurrInstr = failPtr; + else + gBattlescriptCurrInstr += 6; +} + +#ifdef NONMATCHING +static void atk7C_trymirrormove(void) +{ + s32 validMovesCount; + s32 i; + u16 move; + u16 movesArray[4]; + + // incorrect pointer load sequence + // and incorrect layout in data pools + for (i = 0; i < 3; ++i) + movesArray[i] = 0; + for (validMovesCount = 0, i = 0; i < gBattlersCount; ++i) + { + + if (i != gBattlerAttacker) + { + move = *(i * 2 + gBattlerAttacker * 8 + gBattleStruct->lastTakenMoveFrom + 0) + | (*(i * 2 + gBattlerAttacker * 8 + gBattleStruct->lastTakenMoveFrom + 1) << 8); + if (move != MOVE_NONE && move != 0xFFFF) + movesArray[validMovesCount++] = move; + } + } + move = *(gBattleStruct->lastTakenMove + gBattlerAttacker * 2 + 0) + | (*(gBattleStruct->lastTakenMove + gBattlerAttacker * 2 + 1) << 8); + if (move != MOVE_NONE && move != 0xFFFF) + { + gHitMarker &= ~(HITMARKER_ATTACKSTRING_PRINTED); + gCurrentMove = move; + gBattlerTarget = GetMoveTarget(gCurrentMove, 0); + gBattlescriptCurrInstr = gBattleScriptsForMoveEffects[gBattleMoves[gCurrentMove].effect]; + } + else if (validMovesCount) + { + gHitMarker &= ~(HITMARKER_ATTACKSTRING_PRINTED); + i = Random() % validMovesCount; + gCurrentMove = movesArray[i]; + gBattlerTarget = GetMoveTarget(gCurrentMove, 0); + gBattlescriptCurrInstr = gBattleScriptsForMoveEffects[gBattleMoves[gCurrentMove].effect]; + } + else + { + gSpecialStatuses[gBattlerAttacker].ppNotAffectedByPressure = TRUE; + ++gBattlescriptCurrInstr; + } +} +#else +NAKED +static void atk7C_trymirrormove(void) +{ + asm_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, 0x8\n\ + ldr r2, _080273E0 @ =gBattlersCount\n\ + ldr r0, _080273E4 @ =gBattleStruct\n\ + mov r10, r0\n\ + movs r1, 0\n\ + add r0, sp, 0x4\n\ + _08027356:\n\ + strh r1, [r0]\n\ + subs r0, 0x2\n\ + cmp r0, sp\n\ + bge _08027356\n\ + movs r1, 0\n\ + mov r8, r1\n\ + movs r5, 0\n\ + ldrb r1, [r2]\n\ + cmp r5, r1\n\ + bge _080273AC\n\ + ldr r0, _080273E8 @ =gBattlerAttacker\n\ + ldrb r6, [r0]\n\ + ldr r2, _080273EC @ =0x0000ffff\n\ + mov r9, r2\n\ + lsls r4, r6, 3\n\ + mov r2, sp\n\ + ldr r0, _080273E4 @ =gBattleStruct\n\ + mov r12, r0\n\ + adds r7, r1, 0\n\ + _0802737C:\n\ + cmp r5, r6\n\ + beq _080273A4\n\ + mov r1, r12\n\ + ldr r0, [r1]\n\ + adds r0, r4, r0\n\ + adds r1, r0, 0\n\ + adds r1, 0xE0\n\ + ldrb r3, [r1]\n\ + adds r0, 0xE1\n\ + ldrb r0, [r0]\n\ + lsls r0, 8\n\ + orrs r3, r0\n\ + cmp r3, 0\n\ + beq _080273A4\n\ + cmp r3, r9\n\ + beq _080273A4\n\ + strh r3, [r2]\n\ + adds r2, 0x2\n\ + movs r0, 0x1\n\ + add r8, r0\n\ + _080273A4:\n\ + adds r4, 0x2\n\ + adds r5, 0x1\n\ + cmp r5, r7\n\ + blt _0802737C\n\ + _080273AC:\n\ + ldr r1, _080273E8 @ =gBattlerAttacker\n\ + ldrb r0, [r1]\n\ + mov r2, r10\n\ + ldr r1, [r2]\n\ + lsls r0, 1\n\ + adds r0, r1\n\ + adds r1, r0, 0\n\ + adds r1, 0x98\n\ + ldrb r3, [r1]\n\ + adds r0, 0x99\n\ + ldrb r0, [r0]\n\ + lsls r0, 8\n\ + orrs r3, r0\n\ + cmp r3, 0\n\ + beq _080273FC\n\ + ldr r0, _080273EC @ =0x0000ffff\n\ + cmp r3, r0\n\ + beq _080273FC\n\ + ldr r2, _080273F0 @ =gHitMarker\n\ + ldr r0, [r2]\n\ + ldr r1, _080273F4 @ =0xfffffbff\n\ + ands r0, r1\n\ + str r0, [r2]\n\ + ldr r4, _080273F8 @ =gCurrentMove\n\ + strh r3, [r4]\n\ + b _08027426\n\ + .align 2, 0\n\ + _080273E0: .4byte gBattlersCount\n\ + _080273E4: .4byte gBattleStruct\n\ + _080273E8: .4byte gBattlerAttacker\n\ + _080273EC: .4byte 0x0000ffff\n\ + _080273F0: .4byte gHitMarker\n\ + _080273F4: .4byte 0xfffffbff\n\ + _080273F8: .4byte gCurrentMove\n\ + _080273FC:\n\ + mov r0, r8\n\ + cmp r0, 0\n\ + beq _0802746C\n\ + ldr r2, _08027450 @ =gHitMarker\n\ + ldr r0, [r2]\n\ + ldr r1, _08027454 @ =0xfffffbff\n\ + ands r0, r1\n\ + str r0, [r2]\n\ + bl Random\n\ + lsls r0, 16\n\ + lsrs r0, 16\n\ + mov r1, r8\n\ + bl __modsi3\n\ + adds r5, r0, 0\n\ + ldr r4, _08027458 @ =gCurrentMove\n\ + lsls r0, r5, 1\n\ + add r0, sp\n\ + ldrh r0, [r0]\n\ + strh r0, [r4]\n\ + _08027426:\n\ + ldrh r0, [r4]\n\ + movs r1, 0\n\ + bl GetMoveTarget\n\ + ldr r1, _0802745C @ =gBattlerTarget\n\ + strb r0, [r1]\n\ + ldr r5, _08027460 @ =gBattlescriptCurrInstr\n\ + ldr r3, _08027464 @ =gBattleScriptsForMoveEffects\n\ + ldr r2, _08027468 @ =gBattleMoves\n\ + ldrh r1, [r4]\n\ + lsls r0, r1, 1\n\ + adds r0, r1\n\ + lsls r0, 2\n\ + adds r0, r2\n\ + ldrb r0, [r0]\n\ + lsls r0, 2\n\ + adds r0, r3\n\ + ldr r0, [r0]\n\ + str r0, [r5]\n\ + b _0802748A\n\ + .align 2, 0\n\ + _08027450: .4byte gHitMarker\n\ + _08027454: .4byte 0xfffffbff\n\ + _08027458: .4byte gCurrentMove\n\ + _0802745C: .4byte gBattlerTarget\n\ + _08027460: .4byte gBattlescriptCurrInstr\n\ + _08027464: .4byte gBattleScriptsForMoveEffects\n\ + _08027468: .4byte gBattleMoves\n\ + _0802746C:\n\ + ldr r2, _0802749C @ =gSpecialStatuses\n\ + ldr r0, _080274A0 @ =gBattlerAttacker\n\ + ldrb r1, [r0]\n\ + lsls r0, r1, 2\n\ + adds r0, r1\n\ + lsls r0, 2\n\ + adds r0, r2\n\ + ldrb r1, [r0]\n\ + movs r2, 0x20\n\ + orrs r1, r2\n\ + strb r1, [r0]\n\ + ldr r1, _080274A4 @ =gBattlescriptCurrInstr\n\ + ldr r0, [r1]\n\ + adds r0, 0x1\n\ + str r0, [r1]\n\ + _0802748A:\n\ + add sp, 0x8\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\ + _0802749C: .4byte gSpecialStatuses\n\ + _080274A0: .4byte gBattlerAttacker\n\ + _080274A4: .4byte gBattlescriptCurrInstr\n\ + "); +} +#endif + +static void atk7D_setrain(void) +{ + if (gBattleWeather & WEATHER_RAIN_ANY) + { + gMoveResultFlags |= MOVE_RESULT_MISSED; + gBattleCommunication[MULTISTRING_CHOOSER] = 2; + } + else + { + gBattleWeather = WEATHER_RAIN_TEMPORARY; + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + gWishFutureKnock.weatherDuration = 5; + } + ++gBattlescriptCurrInstr; +} + +static void atk7E_setreflect(void) +{ + if (gSideStatuses[GET_BATTLER_SIDE(gBattlerAttacker)] & SIDE_STATUS_REFLECT) + { + gMoveResultFlags |= MOVE_RESULT_MISSED; + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + } + else + { + gSideStatuses[GET_BATTLER_SIDE(gBattlerAttacker)] |= SIDE_STATUS_REFLECT; + gSideTimers[GET_BATTLER_SIDE(gBattlerAttacker)].reflectTimer = 5; + gSideTimers[GET_BATTLER_SIDE(gBattlerAttacker)].reflectBattlerId = gBattlerAttacker; + + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && CountAliveMonsInBattle(BATTLE_ALIVE_ATK_SIDE) == 2) + gBattleCommunication[MULTISTRING_CHOOSER] = 2; + else + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + } + ++gBattlescriptCurrInstr; +} + +static void atk7F_setseeded(void) +{ + if (gMoveResultFlags & MOVE_RESULT_NO_EFFECT || gStatuses3[gBattlerTarget] & STATUS3_LEECHSEED) + { + gMoveResultFlags |= MOVE_RESULT_MISSED; + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + } + else if (IS_BATTLER_OF_TYPE(gBattlerTarget, TYPE_GRASS)) + { + gMoveResultFlags |= MOVE_RESULT_MISSED; + gBattleCommunication[MULTISTRING_CHOOSER] = 2; + } + else + { + gStatuses3[gBattlerTarget] |= gBattlerAttacker; + gStatuses3[gBattlerTarget] |= STATUS3_LEECHSEED; + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + } + ++gBattlescriptCurrInstr; +} + +static void atk80_manipulatedamage(void) +{ + switch (gBattlescriptCurrInstr[1]) + { + case ATK80_DMG_CHANGE_SIGN: + gBattleMoveDamage *= -1; + break; + case ATK80_DMG_HALF_BY_TWO_NOT_MORE_THAN_HALF_MAX_HP: + gBattleMoveDamage /= 2; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + if ((gBattleMons[gBattlerTarget].maxHP / 2) < gBattleMoveDamage) + gBattleMoveDamage = gBattleMons[gBattlerTarget].maxHP / 2; + break; + case ATK80_DMG_DOUBLED: + gBattleMoveDamage *= 2; + break; + } + gBattlescriptCurrInstr += 2; +} + +static void atk81_trysetrest(void) +{ + const u8 *failJump = T1_READ_PTR(gBattlescriptCurrInstr + 1); + + gActiveBattler = gBattlerTarget = gBattlerAttacker; + gBattleMoveDamage = gBattleMons[gBattlerTarget].maxHP * (-1); + if (gBattleMons[gBattlerTarget].hp == gBattleMons[gBattlerTarget].maxHP) + { + gBattlescriptCurrInstr = failJump; + } + else + { + if (gBattleMons[gBattlerTarget].status1 & ((u8)(~STATUS1_SLEEP))) + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + else + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + gBattleMons[gBattlerTarget].status1 = 3; + BtlController_EmitSetMonData(0, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[gActiveBattler].status1); + MarkBattlerForControllerExec(gActiveBattler); + gBattlescriptCurrInstr += 5; + } +} + +static void atk82_jumpifnotfirstturn(void) +{ + const u8 *failJump = T1_READ_PTR(gBattlescriptCurrInstr + 1); + + if (gDisableStructs[gBattlerAttacker].isFirstTurn) + gBattlescriptCurrInstr += 5; + else + gBattlescriptCurrInstr = failJump; +} + +static void atk83_nop(void) +{ + ++gBattlescriptCurrInstr; +} + +bool8 UproarWakeUpCheck(u8 battlerId) +{ + s32 i; + + for (i = 0; i < gBattlersCount; ++i) + { + if (!(gBattleMons[i].status2 & STATUS2_UPROAR) + || gBattleMons[battlerId].ability == ABILITY_SOUNDPROOF) + continue; + gBattleScripting.battler = i; + + if (gBattlerTarget == 0xFF) + gBattlerTarget = i; + else if (gBattlerTarget == i) + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + else + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + break; + } + if (i == gBattlersCount) + return FALSE; + else + return TRUE; +} + +static void atk84_jumpifcantmakeasleep(void) +{ + const u8 *jumpPtr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + + if (UproarWakeUpCheck(gBattlerTarget)) + { + gBattlescriptCurrInstr = jumpPtr; + } + else if (gBattleMons[gBattlerTarget].ability == ABILITY_INSOMNIA + || gBattleMons[gBattlerTarget].ability == ABILITY_VITAL_SPIRIT) + { + gLastUsedAbility = gBattleMons[gBattlerTarget].ability; + gBattleCommunication[MULTISTRING_CHOOSER] = 2; + gBattlescriptCurrInstr = jumpPtr; + RecordAbilityBattle(gBattlerTarget, gLastUsedAbility); + } + else + { + gBattlescriptCurrInstr += 5; + } +} + +static void atk85_stockpile(void) +{ + if (gDisableStructs[gBattlerAttacker].stockpileCounter == 3) + { + gMoveResultFlags |= MOVE_RESULT_MISSED; + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + } + else + { + ++gDisableStructs[gBattlerAttacker].stockpileCounter; + PREPARE_BYTE_NUMBER_BUFFER(gBattleTextBuff1, 1, gDisableStructs[gBattlerAttacker].stockpileCounter) + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + } + ++gBattlescriptCurrInstr; +} + +static void atk86_stockpiletobasedamage(void) +{ + const u8 *jumpPtr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + + if (gDisableStructs[gBattlerAttacker].stockpileCounter == 0) + { + gBattlescriptCurrInstr = jumpPtr; + } + else + { + if (gBattleCommunication[6] != 1) + { + gBattleMoveDamage = CalculateBaseDamage(&gBattleMons[gBattlerAttacker], &gBattleMons[gBattlerTarget], gCurrentMove, + gSideStatuses[GET_BATTLER_SIDE(gBattlerTarget)], 0, + 0, gBattlerAttacker, gBattlerTarget) + * gDisableStructs[gBattlerAttacker].stockpileCounter; + gBattleScripting.animTurn = gDisableStructs[gBattlerAttacker].stockpileCounter; + + if (gProtectStructs[gBattlerAttacker].helpingHand) + gBattleMoveDamage = gBattleMoveDamage * 15 / 10; + } + gDisableStructs[gBattlerAttacker].stockpileCounter = 0; + gBattlescriptCurrInstr += 5; + } +} + +static void atk87_stockpiletohpheal(void) +{ + const u8 *jumpPtr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + + if (gDisableStructs[gBattlerAttacker].stockpileCounter == 0) + { + gBattlescriptCurrInstr = jumpPtr; + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + } + else if (gBattleMons[gBattlerAttacker].maxHP == gBattleMons[gBattlerAttacker].hp) + { + gDisableStructs[gBattlerAttacker].stockpileCounter = 0; + gBattlescriptCurrInstr = jumpPtr; + gBattlerTarget = gBattlerAttacker; + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + } + else + { + gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / (1 << (3 - gDisableStructs[gBattlerAttacker].stockpileCounter)); + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + gBattleMoveDamage *= -1; + gBattleScripting.animTurn = gDisableStructs[gBattlerAttacker].stockpileCounter; + gDisableStructs[gBattlerAttacker].stockpileCounter = 0; + gBattlescriptCurrInstr += 5; + gBattlerTarget = gBattlerAttacker; + } +} + +static void atk88_negativedamage(void) +{ + gBattleMoveDamage = -(gHpDealt / 2); + if (gBattleMoveDamage == 0) + gBattleMoveDamage = -1; + ++gBattlescriptCurrInstr; +} + +#define STAT_CHANGE_WORKED 0 +#define STAT_CHANGE_DIDNT_WORK 1 + +static u8 ChangeStatBuffs(s8 statValue, u8 statId, u8 flags, const u8 *BS_ptr) +{ + bool8 certain = FALSE; + bool8 notProtectAffected = FALSE; + u32 index; + + if (flags & MOVE_EFFECT_AFFECTS_USER) + gActiveBattler = gBattlerAttacker; + else + gActiveBattler = gBattlerTarget; + flags &= ~(MOVE_EFFECT_AFFECTS_USER); + if (flags & MOVE_EFFECT_CERTAIN) + ++certain; + flags &= ~(MOVE_EFFECT_CERTAIN); + if (flags & STAT_CHANGE_NOT_PROTECT_AFFECTED) + ++notProtectAffected; + flags &= ~(STAT_CHANGE_NOT_PROTECT_AFFECTED); + PREPARE_STAT_BUFFER(gBattleTextBuff1, statId) + if (statValue <= -1) // Stat decrease. + { + if (gSideTimers[GET_BATTLER_SIDE(gActiveBattler)].mistTimer + && !certain && gCurrentMove != MOVE_CURSE) + { + if (flags == STAT_CHANGE_BS_PTR) + { + if (gSpecialStatuses[gActiveBattler].statLowered) + { + gBattlescriptCurrInstr = BS_ptr; + } + else + { + BattleScriptPush(BS_ptr); + gBattleScripting.battler = gActiveBattler; + gBattlescriptCurrInstr = BattleScript_MistProtected; + gSpecialStatuses[gActiveBattler].statLowered = 1; + } + } + return STAT_CHANGE_DIDNT_WORK; + } + else if (gCurrentMove != MOVE_CURSE + && notProtectAffected != TRUE + && JumpIfMoveAffectedByProtect(0)) + { + gBattlescriptCurrInstr = BattleScript_ButItFailed; + return STAT_CHANGE_DIDNT_WORK; + } + else if ((gBattleMons[gActiveBattler].ability == ABILITY_CLEAR_BODY + || gBattleMons[gActiveBattler].ability == ABILITY_WHITE_SMOKE) + && !certain + && gCurrentMove != MOVE_CURSE) + { + if (flags == STAT_CHANGE_BS_PTR) + { + if (gSpecialStatuses[gActiveBattler].statLowered) + { + gBattlescriptCurrInstr = BS_ptr; + } + else + { + BattleScriptPush(BS_ptr); + gBattleScripting.battler = gActiveBattler; + gBattlescriptCurrInstr = BattleScript_AbilityNoStatLoss; + gLastUsedAbility = gBattleMons[gActiveBattler].ability; + RecordAbilityBattle(gActiveBattler, gLastUsedAbility); + gSpecialStatuses[gActiveBattler].statLowered = 1; + } + } + return STAT_CHANGE_DIDNT_WORK; + } + else if (gBattleMons[gActiveBattler].ability == ABILITY_KEEN_EYE + && !certain && statId == STAT_ACC) + { + if (flags == STAT_CHANGE_BS_PTR) + { + BattleScriptPush(BS_ptr); + gBattleScripting.battler = gActiveBattler; + gBattlescriptCurrInstr = BattleScript_AbilityNoSpecificStatLoss; + gLastUsedAbility = gBattleMons[gActiveBattler].ability; + RecordAbilityBattle(gActiveBattler, gLastUsedAbility); + } + return STAT_CHANGE_DIDNT_WORK; + } + else if (gBattleMons[gActiveBattler].ability == ABILITY_HYPER_CUTTER + && !certain && statId == STAT_ATK) + { + if (flags == STAT_CHANGE_BS_PTR) + { + BattleScriptPush(BS_ptr); + gBattleScripting.battler = gActiveBattler; + gBattlescriptCurrInstr = BattleScript_AbilityNoSpecificStatLoss; + gLastUsedAbility = gBattleMons[gActiveBattler].ability; + RecordAbilityBattle(gActiveBattler, gLastUsedAbility); + } + return STAT_CHANGE_DIDNT_WORK; + } + else if (gBattleMons[gActiveBattler].ability == ABILITY_SHIELD_DUST && !flags) + { + return STAT_CHANGE_DIDNT_WORK; + } + else // try to decrease + { + statValue = -GET_STAT_BUFF_VALUE(statValue); + gBattleTextBuff2[0] = B_BUFF_PLACEHOLDER_BEGIN; + index = 1; + if (statValue == -2) + { + gBattleTextBuff2[1] = B_BUFF_STRING; + gBattleTextBuff2[2] = STRINGID_STATHARSHLY; + gBattleTextBuff2[3] = STRINGID_STATHARSHLY >> 8; + index = 4; + } + gBattleTextBuff2[index++] = B_BUFF_STRING; + gBattleTextBuff2[index++] = STRINGID_STATFELL; + gBattleTextBuff2[index++] = STRINGID_STATFELL >> 8; + gBattleTextBuff2[index] = B_BUFF_EOS; + if (gBattleMons[gActiveBattler].statStages[statId] == 0) + gBattleCommunication[MULTISTRING_CHOOSER] = 2; + else + gBattleCommunication[MULTISTRING_CHOOSER] = (gBattlerTarget == gActiveBattler); + } + } + else // stat increase + { + statValue = GET_STAT_BUFF_VALUE(statValue); + gBattleTextBuff2[0] = B_BUFF_PLACEHOLDER_BEGIN; + index = 1; + if (statValue == 2) + { + gBattleTextBuff2[1] = B_BUFF_STRING; + gBattleTextBuff2[2] = STRINGID_STATSHARPLY; + gBattleTextBuff2[3] = STRINGID_STATSHARPLY >> 8; + index = 4; + } + gBattleTextBuff2[index++] = B_BUFF_STRING; + gBattleTextBuff2[index++] = STRINGID_STATROSE; + gBattleTextBuff2[index++] = STRINGID_STATROSE >> 8; + gBattleTextBuff2[index] = B_BUFF_EOS; + if (gBattleMons[gActiveBattler].statStages[statId] == 0xC) + gBattleCommunication[MULTISTRING_CHOOSER] = 2; + else + gBattleCommunication[MULTISTRING_CHOOSER] = (gBattlerTarget == gActiveBattler); + } + gBattleMons[gActiveBattler].statStages[statId] += statValue; + if (gBattleMons[gActiveBattler].statStages[statId] < 0) + gBattleMons[gActiveBattler].statStages[statId] = 0; + if (gBattleMons[gActiveBattler].statStages[statId] > 0xC) + gBattleMons[gActiveBattler].statStages[statId] = 0xC; + if (gBattleCommunication[MULTISTRING_CHOOSER] == 2 && flags & STAT_CHANGE_BS_PTR) + gMoveResultFlags |= MOVE_RESULT_MISSED; + if (gBattleCommunication[MULTISTRING_CHOOSER] == 2 && !(flags & STAT_CHANGE_BS_PTR)) + return STAT_CHANGE_DIDNT_WORK; + return STAT_CHANGE_WORKED; +} + +static void atk89_statbuffchange(void) +{ + const u8 *jumpPtr = T1_READ_PTR(gBattlescriptCurrInstr + 2); + if (ChangeStatBuffs(gBattleScripting.statChanger & 0xF0, GET_STAT_BUFF_ID(gBattleScripting.statChanger), gBattlescriptCurrInstr[1], jumpPtr) == STAT_CHANGE_WORKED) + gBattlescriptCurrInstr += 6; +} + +static void atk8A_normalisebuffs(void) // haze +{ + s32 i, j; + + for (i = 0; i < gBattlersCount; ++i) + for (j = 0; j < NUM_BATTLE_STATS; ++j) + gBattleMons[i].statStages[j] = 6; + ++gBattlescriptCurrInstr; +} + +static void atk8B_setbide(void) +{ + gBattleMons[gBattlerAttacker].status2 |= STATUS2_MULTIPLETURNS; + gLockedMoves[gBattlerAttacker] = gCurrentMove; + gTakenDmg[gBattlerAttacker] = 0; + gBattleMons[gBattlerAttacker].status2 |= (STATUS2_BIDE - 0x100); // 2 turns + ++gBattlescriptCurrInstr; +} + +static void atk8C_confuseifrepeatingattackends(void) +{ + if (!(gBattleMons[gBattlerAttacker].status2 & STATUS2_LOCK_CONFUSE)) + gBattleCommunication[MOVE_EFFECT_BYTE] = (MOVE_EFFECT_THRASH | MOVE_EFFECT_AFFECTS_USER); + ++gBattlescriptCurrInstr; +} + +static void atk8D_setmultihitcounter(void) +{ + if (gBattlescriptCurrInstr[1]) + { + gMultiHitCounter = gBattlescriptCurrInstr[1]; + } + else + { + gMultiHitCounter = Random() & 3; + if (gMultiHitCounter > 1) + gMultiHitCounter = (Random() & 3) + 2; + else + gMultiHitCounter += 2; + } + gBattlescriptCurrInstr += 2; +} + +static void atk8E_initmultihitstring(void) +{ + PREPARE_BYTE_NUMBER_BUFFER(gBattleScripting.multihitString, 1, 0) + ++gBattlescriptCurrInstr; +} + +static bool8 TryDoForceSwitchOut(void) +{ + if (gBattleMons[gBattlerAttacker].level >= gBattleMons[gBattlerTarget].level) + { + *(gBattleStruct->field_58 + gBattlerTarget) = gBattlerPartyIndexes[gBattlerTarget]; + } + else + { + u16 random = Random() & 0xFF; + + if ((u32)((random * (gBattleMons[gBattlerAttacker].level + gBattleMons[gBattlerTarget].level) >> 8) + 1) <= (gBattleMons[gBattlerTarget].level / 4)) + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + return FALSE; + } + *(gBattleStruct->field_58 + gBattlerTarget) = gBattlerPartyIndexes[gBattlerTarget]; + } + gBattlescriptCurrInstr = BattleScript_SuccessForceOut; + return TRUE; +} + +#define MON_CAN_BATTLE(mon) (((GetMonData(mon, MON_DATA_SPECIES) && GetMonData(mon, MON_DATA_IS_EGG) != TRUE && GetMonData(mon, MON_DATA_HP)))) + +static void atk8F_forcerandomswitch(void) +{ + if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) + { + u8 i; + struct Pokemon *party; + u8 valid; + u8 val; + + if (!GetBattlerSide(gBattlerTarget)) + party = gPlayerParty; + else + party = gEnemyParty; + if (gBattleTypeFlags & BATTLE_TYPE_MULTI) + { + valid = 0; + val = 0; + if (GetLinkTrainerFlankId(GetBattlerMultiplayerId(gBattlerTarget)) == 1) + val = 3; + for (i = val; i < val + 3; ++i) + { + if (GetMonData(&party[i], MON_DATA_SPECIES) != SPECIES_NONE + && !GetMonData(&party[i], MON_DATA_IS_EGG) + && GetMonData(&party[i], MON_DATA_HP) != 0) + ++valid; + } + } + else + { + valid = 0; + for (i = 0; i < 6; ++i) + { + if (GetMonData(&party[i], MON_DATA_SPECIES) != SPECIES_NONE + && !GetMonData(&party[i], MON_DATA_IS_EGG) + && GetMonData(&party[i], MON_DATA_HP) != 0) + ++valid; + } + } + + if ((valid < 2 && (gBattleTypeFlags & (BATTLE_TYPE_DOUBLE | BATTLE_TYPE_MULTI)) != BATTLE_TYPE_DOUBLE) + || (valid < 3 && (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) && !(gBattleTypeFlags & BATTLE_TYPE_MULTI))) + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } + else if (TryDoForceSwitchOut()) + { + if (gBattleTypeFlags & BATTLE_TYPE_MULTI) + { + do + { + val = Random() % 3; + if (GetLinkTrainerFlankId(GetBattlerMultiplayerId(gBattlerTarget)) == 1) + i = val + 3; + else + i = val; + } + while (i == gBattlerPartyIndexes[gBattlerTarget] + || i == gBattlerPartyIndexes[gBattlerTarget ^ 2] + || !MON_CAN_BATTLE(&party[i])); + } + else + { + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + { + do + { + i = Random() % 6; + } + while (i == gBattlerPartyIndexes[gBattlerTarget] + || i == gBattlerPartyIndexes[gBattlerTarget ^ 2] + || !MON_CAN_BATTLE(&party[i])); + } + else + { + do + { + i = Random() % 6; + } + while (i == gBattlerPartyIndexes[gBattlerTarget] + || !MON_CAN_BATTLE(&party[i])); + } + } + *(gBattleStruct->monToSwitchIntoId + gBattlerTarget) = i; + if (!IsMultiBattle()) + sub_8013F6C(gBattlerTarget); + sub_8127EC4(gBattlerTarget, i, 0); + sub_8127EC4(gBattlerTarget ^ 2, i, 1); + } + } + else + { + TryDoForceSwitchOut(); + } +} + +static void atk90_tryconversiontypechange(void) // randomly changes user's type to one of its moves' type +{ + u8 validMoves = 0; + u8 moveChecked; + u8 moveType; + + while (validMoves < MAX_MON_MOVES) + { + if (gBattleMons[gBattlerAttacker].moves[validMoves] == MOVE_NONE) + break; + ++validMoves; + } + for (moveChecked = 0; moveChecked < validMoves; ++moveChecked) + { + moveType = gBattleMoves[gBattleMons[gBattlerAttacker].moves[moveChecked]].type; + if (moveType == TYPE_MYSTERY) + { + if (IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GHOST)) + moveType = TYPE_GHOST; + else + moveType = TYPE_NORMAL; + } + if (moveType != gBattleMons[gBattlerAttacker].type1 + && moveType != gBattleMons[gBattlerAttacker].type2) + { + break; + } + } + if (moveChecked == validMoves) + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } + else + { + do + { + while ((moveChecked = Random() & 3) >= validMoves); + moveType = gBattleMoves[gBattleMons[gBattlerAttacker].moves[moveChecked]].type; + if (moveType == TYPE_MYSTERY) + { + if (IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GHOST)) + moveType = TYPE_GHOST; + else + moveType = TYPE_NORMAL; + } + } + while (moveType == gBattleMons[gBattlerAttacker].type1 + || moveType == gBattleMons[gBattlerAttacker].type2); + SET_BATTLER_TYPE(gBattlerAttacker, moveType); + PREPARE_TYPE_BUFFER(gBattleTextBuff1, moveType); + gBattlescriptCurrInstr += 5; + } +} + +static void atk91_givepaydaymoney(void) +{ + if (!(gBattleTypeFlags & BATTLE_TYPE_LINK) && gPaydayMoney != 0) + { + u32 bonusMoney = gPaydayMoney * gBattleStruct->moneyMultiplier; + + AddMoney(&gSaveBlock1Ptr->money, bonusMoney); + PREPARE_HWORD_NUMBER_BUFFER(gBattleTextBuff1, 5, bonusMoney) + BattleScriptPush(gBattlescriptCurrInstr + 1); + gBattlescriptCurrInstr = BattleScript_PrintPayDayMoneyString; + } + else + { + ++gBattlescriptCurrInstr; + } +} + +static void atk92_setlightscreen(void) +{ + if (gSideStatuses[GET_BATTLER_SIDE(gBattlerAttacker)] & SIDE_STATUS_LIGHTSCREEN) + { + gMoveResultFlags |= MOVE_RESULT_MISSED; + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + } + else + { + gSideStatuses[GET_BATTLER_SIDE(gBattlerAttacker)] |= SIDE_STATUS_LIGHTSCREEN; + gSideTimers[GET_BATTLER_SIDE(gBattlerAttacker)].lightscreenTimer = 5; + gSideTimers[GET_BATTLER_SIDE(gBattlerAttacker)].lightscreenBattlerId = gBattlerAttacker; + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && CountAliveMonsInBattle(BATTLE_ALIVE_ATK_SIDE) == 2) + gBattleCommunication[MULTISTRING_CHOOSER] = 4; + else + gBattleCommunication[MULTISTRING_CHOOSER] = 3; + } + ++gBattlescriptCurrInstr; +} + +static void atk93_tryKO(void) +{ + u8 holdEffect, param; + + if (gBattleMons[gBattlerTarget].item == ITEM_ENIGMA_BERRY) + { + holdEffect = gEnigmaBerries[gBattlerTarget].holdEffect; + param = gEnigmaBerries[gBattlerTarget].holdEffectParam; + } + else + { + holdEffect = ItemId_GetHoldEffect(gBattleMons[gBattlerTarget].item); + param = ItemId_GetHoldEffectParam(gBattleMons[gBattlerTarget].item); + } + gPotentialItemEffectBattler = gBattlerTarget; + if (holdEffect == HOLD_EFFECT_FOCUS_BAND && (Random() % 100) < param) + { + RecordItemEffectBattle(gBattlerTarget, HOLD_EFFECT_FOCUS_BAND); + gSpecialStatuses[gBattlerTarget].focusBanded = 1; + } + + if (gBattleMons[gBattlerTarget].ability == ABILITY_STURDY) + { + gMoveResultFlags |= MOVE_RESULT_MISSED; + gLastUsedAbility = ABILITY_STURDY; + gBattlescriptCurrInstr = BattleScript_SturdyPreventsOHKO; + RecordAbilityBattle(gBattlerTarget, ABILITY_STURDY); + } + else + { + u16 chance; + + if (!(gStatuses3[gBattlerTarget] & STATUS3_ALWAYS_HITS)) + { + chance = gBattleMoves[gCurrentMove].accuracy + (gBattleMons[gBattlerAttacker].level - gBattleMons[gBattlerTarget].level); + if (Random() % 100 + 1 < chance && gBattleMons[gBattlerAttacker].level >= gBattleMons[gBattlerTarget].level) + chance = TRUE; + else + chance = FALSE; + } + else if (gDisableStructs[gBattlerTarget].battlerWithSureHit == gBattlerAttacker + && gBattleMons[gBattlerAttacker].level >= gBattleMons[gBattlerTarget].level) + { + chance = TRUE; + } + else + { + chance = gBattleMoves[gCurrentMove].accuracy + (gBattleMons[gBattlerAttacker].level - gBattleMons[gBattlerTarget].level); + if (Random() % 100 + 1 < chance && gBattleMons[gBattlerAttacker].level >= gBattleMons[gBattlerTarget].level) + chance = TRUE; + else + chance = FALSE; + } + if (chance) + { + if (gProtectStructs[gBattlerTarget].endured) + { + gBattleMoveDamage = gBattleMons[gBattlerTarget].hp - 1; + gMoveResultFlags |= MOVE_RESULT_FOE_ENDURED; + } + else if (gSpecialStatuses[gBattlerTarget].focusBanded) + { + gBattleMoveDamage = gBattleMons[gBattlerTarget].hp - 1; + gMoveResultFlags |= MOVE_RESULT_FOE_HUNG_ON; + gLastUsedItem = gBattleMons[gBattlerTarget].item; + } + else + { + gBattleMoveDamage = gBattleMons[gBattlerTarget].hp; + gMoveResultFlags |= MOVE_RESULT_ONE_HIT_KO; + } + gBattlescriptCurrInstr += 5; + } + else + { + gMoveResultFlags |= MOVE_RESULT_MISSED; + if (gBattleMons[gBattlerAttacker].level >= gBattleMons[gBattlerTarget].level) + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + else + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } + } +} + +static void atk94_damagetohalftargethp(void) // super fang +{ + gBattleMoveDamage = gBattleMons[gBattlerTarget].hp / 2; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + ++gBattlescriptCurrInstr; +} + +static void atk95_setsandstorm(void) +{ + if (gBattleWeather & WEATHER_SANDSTORM_ANY) + { + gMoveResultFlags |= MOVE_RESULT_MISSED; + gBattleCommunication[MULTISTRING_CHOOSER] = 2; + } + else + { + gBattleWeather = WEATHER_SANDSTORM_TEMPORARY; + gBattleCommunication[MULTISTRING_CHOOSER] = 3; + gWishFutureKnock.weatherDuration = 5; + } + ++gBattlescriptCurrInstr; +} + +static void atk96_weatherdamage(void) +{ + if (((gBattleTypeFlags & (BATTLE_TYPE_LEGENDARY | BATTLE_TYPE_GHOST)) == BATTLE_TYPE_GHOST) + && (GetBattlerSide(gBattlerAttacker) == B_SIDE_OPPONENT)) + { + gBattleMoveDamage = 0; + ++gBattlescriptCurrInstr; + return; + } + if (WEATHER_HAS_EFFECT) + { + if (gBattleWeather & WEATHER_SANDSTORM_ANY) + { + if (gBattleMons[gBattlerAttacker].type1 != TYPE_ROCK + && gBattleMons[gBattlerAttacker].type1 != TYPE_STEEL + && gBattleMons[gBattlerAttacker].type1 != TYPE_GROUND + && gBattleMons[gBattlerAttacker].type2 != TYPE_ROCK + && gBattleMons[gBattlerAttacker].type2 != TYPE_STEEL + && gBattleMons[gBattlerAttacker].type2 != TYPE_GROUND + && gBattleMons[gBattlerAttacker].ability != ABILITY_SAND_VEIL + && !(gStatuses3[gBattlerAttacker] & STATUS3_UNDERGROUND) + && !(gStatuses3[gBattlerAttacker] & STATUS3_UNDERWATER)) + { + gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / 16; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + } + else + { + gBattleMoveDamage = 0; + } + } + if (gBattleWeather & WEATHER_HAIL) + { + if (!IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_ICE) + && !(gStatuses3[gBattlerAttacker] & STATUS3_UNDERGROUND) + && !(gStatuses3[gBattlerAttacker] & STATUS3_UNDERWATER)) + { + gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / 16; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + } + else + { + gBattleMoveDamage = 0; + } + } + } + else + { + gBattleMoveDamage = 0; + } + if (gAbsentBattlerFlags & gBitTable[gBattlerAttacker]) + gBattleMoveDamage = 0; + ++gBattlescriptCurrInstr; +} + +static void atk97_tryinfatuating(void) +{ + struct Pokemon *monAttacker, *monTarget; + u16 speciesAttacker, speciesTarget; + u32 personalityAttacker, personalityTarget; + + if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER) + monAttacker = &gPlayerParty[gBattlerPartyIndexes[gBattlerAttacker]]; + else + monAttacker = &gEnemyParty[gBattlerPartyIndexes[gBattlerAttacker]]; + if (GetBattlerSide(gBattlerTarget) == B_SIDE_PLAYER) + monTarget = &gPlayerParty[gBattlerPartyIndexes[gBattlerTarget]]; + else + monTarget = &gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]]; + speciesAttacker = GetMonData(monAttacker, MON_DATA_SPECIES); + personalityAttacker = GetMonData(monAttacker, MON_DATA_PERSONALITY); + speciesTarget = GetMonData(monTarget, MON_DATA_SPECIES); + personalityTarget = GetMonData(monTarget, MON_DATA_PERSONALITY); + if (gBattleMons[gBattlerTarget].ability == ABILITY_OBLIVIOUS) + { + gBattlescriptCurrInstr = BattleScript_ObliviousPreventsAttraction; + gLastUsedAbility = ABILITY_OBLIVIOUS; + RecordAbilityBattle(gBattlerTarget, ABILITY_OBLIVIOUS); + } + else + { + if (GetGenderFromSpeciesAndPersonality(speciesAttacker, personalityAttacker) == GetGenderFromSpeciesAndPersonality(speciesTarget, personalityTarget) + || gBattleMons[gBattlerTarget].status2 & STATUS2_INFATUATION + || GetGenderFromSpeciesAndPersonality(speciesAttacker, personalityAttacker) == MON_GENDERLESS + || GetGenderFromSpeciesAndPersonality(speciesTarget, personalityTarget) == MON_GENDERLESS) + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } + else + { + gBattleMons[gBattlerTarget].status2 |= STATUS2_INFATUATED_WITH(gBattlerAttacker); + gBattlescriptCurrInstr += 5; + } + } +} + +static void atk98_updatestatusicon(void) +{ + if (!gBattleControllerExecFlags) + { + if (gBattlescriptCurrInstr[1] == BS_PLAYER2) + { + for (gActiveBattler = gBattleControllerExecFlags; gActiveBattler < gBattlersCount; ++gActiveBattler) + { + if (!(gAbsentBattlerFlags & gBitTable[gActiveBattler])) + { + BtlController_EmitStatusIconUpdate(0, gBattleMons[gActiveBattler].status1, gBattleMons[gActiveBattler].status2); + MarkBattlerForControllerExec(gActiveBattler); + } + } + gBattlescriptCurrInstr += 2; + } + else if (gBattlescriptCurrInstr[1] == BS_ATTACKER_WITH_PARTNER) + { + gActiveBattler = gBattlerAttacker; + if (!(gAbsentBattlerFlags & gBitTable[gActiveBattler])) + { + BtlController_EmitStatusIconUpdate(0, gBattleMons[gActiveBattler].status1, gBattleMons[gActiveBattler].status2); + MarkBattlerForControllerExec(gActiveBattler); + } + if ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE)) + { + gActiveBattler = GetBattlerAtPosition(GetBattlerPosition(gBattlerAttacker) ^ BIT_FLANK); + if (!(gAbsentBattlerFlags & gBitTable[gActiveBattler])) + { + BtlController_EmitStatusIconUpdate(0, gBattleMons[gActiveBattler].status1, gBattleMons[gActiveBattler].status2); + MarkBattlerForControllerExec(gActiveBattler); + } + } + gBattlescriptCurrInstr += 2; + } + else + { + + gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + BtlController_EmitStatusIconUpdate(0, gBattleMons[gActiveBattler].status1, gBattleMons[gActiveBattler].status2); + MarkBattlerForControllerExec(gActiveBattler); + gBattlescriptCurrInstr += 2; + } + } +} + +static void atk99_setmist(void) +{ + if (gSideTimers[GET_BATTLER_SIDE(gBattlerAttacker)].mistTimer) + { + gMoveResultFlags |= MOVE_RESULT_FAILED; + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + } + else + { + gSideTimers[GET_BATTLER_SIDE(gBattlerAttacker)].mistTimer = 5; + gSideTimers[GET_BATTLER_SIDE(gBattlerAttacker)].mistBattlerId = gBattlerAttacker; + gSideStatuses[GET_BATTLER_SIDE(gBattlerAttacker)] |= SIDE_STATUS_MIST; + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + } + ++gBattlescriptCurrInstr; +} + +static void atk9A_setfocusenergy(void) +{ + if (gBattleMons[gBattlerAttacker].status2 & STATUS2_FOCUS_ENERGY) + { + gMoveResultFlags |= MOVE_RESULT_FAILED; + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + } + else + { + gBattleMons[gBattlerAttacker].status2 |= STATUS2_FOCUS_ENERGY; + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + } + ++gBattlescriptCurrInstr; +} + +static void atk9B_transformdataexecution(void) +{ + gChosenMove = 0xFFFF; + ++gBattlescriptCurrInstr; + if (gBattleMons[gBattlerTarget].status2 & STATUS2_TRANSFORMED + || gStatuses3[gBattlerTarget] & STATUS3_SEMI_INVULNERABLE) + { + gMoveResultFlags |= MOVE_RESULT_FAILED; + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + } + else + { + s32 i; + u8 *battleMonAttacker, *battleMonTarget; + + gBattleMons[gBattlerAttacker].status2 |= STATUS2_TRANSFORMED; + gDisableStructs[gBattlerAttacker].disabledMove = MOVE_NONE; + gDisableStructs[gBattlerAttacker].disableTimer = 0; + gDisableStructs[gBattlerAttacker].transformedMonPersonality = gBattleMons[gBattlerTarget].personality; + gDisableStructs[gBattlerAttacker].mimickedMoves = 0; + PREPARE_SPECIES_BUFFER(gBattleTextBuff1, gBattleMons[gBattlerTarget].species) + battleMonAttacker = (u8 *)(&gBattleMons[gBattlerAttacker]); + battleMonTarget = (u8 *)(&gBattleMons[gBattlerTarget]); + for (i = 0; i < offsetof(struct BattlePokemon, pp); ++i) + battleMonAttacker[i] = battleMonTarget[i]; + for (i = 0; i < MAX_MON_MOVES; ++i) + { + if (gBattleMoves[gBattleMons[gBattlerAttacker].moves[i]].pp < 5) + gBattleMons[gBattlerAttacker].pp[i] = gBattleMoves[gBattleMons[gBattlerAttacker].moves[i]].pp; + else + gBattleMons[gBattlerAttacker].pp[i] = 5; + } + gActiveBattler = gBattlerAttacker; + BtlController_EmitResetActionMoveSelection(0, RESET_MOVE_SELECTION); + MarkBattlerForControllerExec(gActiveBattler); + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + } +} + +static void atk9C_setsubstitute(void) +{ + u32 hp = gBattleMons[gBattlerAttacker].maxHP / 4; + + if (gBattleMons[gBattlerAttacker].maxHP / 4 == 0) + hp = 1; + if (gBattleMons[gBattlerAttacker].hp <= hp) + { + gBattleMoveDamage = 0; + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + } + else + { + gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / 4; // one bit value will only work for pokemon which max hp can go to 1020(which is more than possible in games) + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + gBattleMons[gBattlerAttacker].status2 |= STATUS2_SUBSTITUTE; + gBattleMons[gBattlerAttacker].status2 &= ~(STATUS2_WRAPPED); + gDisableStructs[gBattlerAttacker].substituteHP = gBattleMoveDamage; + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + gHitMarker |= HITMARKER_IGNORE_SUBSTITUTE; + } + ++gBattlescriptCurrInstr; +} + +static bool8 IsMoveUncopyableByMimic(u16 move) +{ + s32 i; + + for (i = 0; sMovesForbiddenToCopy[i] != MIMIC_FORBIDDEN_END && sMovesForbiddenToCopy[i] != move; ++i); + return (sMovesForbiddenToCopy[i] != MIMIC_FORBIDDEN_END); +} + +static void atk9D_mimicattackcopy(void) +{ + gChosenMove = 0xFFFF; + if (IsMoveUncopyableByMimic(gLastMoves[gBattlerTarget]) + || gBattleMons[gBattlerAttacker].status2 & STATUS2_TRANSFORMED + || gLastMoves[gBattlerTarget] == 0 + || gLastMoves[gBattlerTarget] == 0xFFFF) + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } + else + { + s32 i; + + for (i = 0; i < MAX_MON_MOVES; ++i) + { + if (gBattleMons[gBattlerAttacker].moves[i] == gLastMoves[gBattlerTarget]) + break; + } + if (i == MAX_MON_MOVES) + { + gBattleMons[gBattlerAttacker].moves[gCurrMovePos] = gLastMoves[gBattlerTarget]; + if (gBattleMoves[gLastMoves[gBattlerTarget]].pp < 5) + gBattleMons[gBattlerAttacker].pp[gCurrMovePos] = gBattleMoves[gLastMoves[gBattlerTarget]].pp; + else + gBattleMons[gBattlerAttacker].pp[gCurrMovePos] = 5; + PREPARE_MOVE_BUFFER(gBattleTextBuff1, gLastMoves[gBattlerTarget]) + gDisableStructs[gBattlerAttacker].mimickedMoves |= gBitTable[gCurrMovePos]; + gBattlescriptCurrInstr += 5; + } + else + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } + } +} + +static void atk9E_metronome(void) +{ + while (1) + { + s32 i; + + gCurrentMove = (Random() & 0x1FF) + 1; + if (gCurrentMove >= MOVES_COUNT) + continue; + for (i = 0; i < MAX_MON_MOVES; ++i); // redundant + i = -1; + while (1) + { + ++i; + if (sMovesForbiddenToCopy[i] == gCurrentMove) + break; + if (sMovesForbiddenToCopy[i] == METRONOME_FORBIDDEN_END) + break; + } + if (sMovesForbiddenToCopy[i] == METRONOME_FORBIDDEN_END) + { + gHitMarker &= ~(HITMARKER_ATTACKSTRING_PRINTED); + gBattlescriptCurrInstr = gBattleScriptsForMoveEffects[gBattleMoves[gCurrentMove].effect]; + gBattlerTarget = GetMoveTarget(gCurrentMove, 0); + return; + } + } +} + +static void atk9F_dmgtolevel(void) +{ + gBattleMoveDamage = gBattleMons[gBattlerAttacker].level; + ++gBattlescriptCurrInstr; +} + +static void atkA0_psywavedamageeffect(void) +{ + s32 randDamage; + + while ((randDamage = (Random() & 0xF)) > 10); + randDamage *= 10; + gBattleMoveDamage = gBattleMons[gBattlerAttacker].level * (randDamage + 50) / 100; + ++gBattlescriptCurrInstr; +} + +static void atkA1_counterdamagecalculator(void) +{ + u8 sideAttacker = GetBattlerSide(gBattlerAttacker); + u8 sideTarget = GetBattlerSide(gProtectStructs[gBattlerAttacker].physicalBattlerId); + + if (gProtectStructs[gBattlerAttacker].physicalDmg + && sideAttacker != sideTarget + && gBattleMons[gProtectStructs[gBattlerAttacker].physicalBattlerId].hp) + { + gBattleMoveDamage = gProtectStructs[gBattlerAttacker].physicalDmg * 2; + + if (gSideTimers[sideTarget].followmeTimer && gBattleMons[gSideTimers[sideTarget].followmeTarget].hp) + gBattlerTarget = gSideTimers[sideTarget].followmeTarget; + else + gBattlerTarget = gProtectStructs[gBattlerAttacker].physicalBattlerId; + + gBattlescriptCurrInstr += 5; + } + else + { + gSpecialStatuses[gBattlerAttacker].ppNotAffectedByPressure = 1; + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } +} + +// a copy of atkA1 with the physical -> special field changes +static void atkA2_mirrorcoatdamagecalculator(void) +{ + u8 sideAttacker = GetBattlerSide(gBattlerAttacker); + u8 sideTarget = GetBattlerSide(gProtectStructs[gBattlerAttacker].specialBattlerId); + + if (gProtectStructs[gBattlerAttacker].specialDmg && sideAttacker != sideTarget && gBattleMons[gProtectStructs[gBattlerAttacker].specialBattlerId].hp) + { + gBattleMoveDamage = gProtectStructs[gBattlerAttacker].specialDmg * 2; + if (gSideTimers[sideTarget].followmeTimer && gBattleMons[gSideTimers[sideTarget].followmeTarget].hp) + gBattlerTarget = gSideTimers[sideTarget].followmeTarget; + else + gBattlerTarget = gProtectStructs[gBattlerAttacker].specialBattlerId; + gBattlescriptCurrInstr += 5; + } + else + { + gSpecialStatuses[gBattlerAttacker].ppNotAffectedByPressure = 1; + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } +} + +static void atkA3_disablelastusedattack(void) +{ + s32 i; + + for (i = 0; i < MAX_MON_MOVES; ++i) + { + if (gBattleMons[gBattlerTarget].moves[i] == gLastMoves[gBattlerTarget]) + break; + } + if (gDisableStructs[gBattlerTarget].disabledMove == MOVE_NONE + && i != MAX_MON_MOVES && gBattleMons[gBattlerTarget].pp[i] != 0) + { + PREPARE_MOVE_BUFFER(gBattleTextBuff1, gBattleMons[gBattlerTarget].moves[i]) + gDisableStructs[gBattlerTarget].disabledMove = gBattleMons[gBattlerTarget].moves[i]; + gDisableStructs[gBattlerTarget].disableTimer = (Random() & 3) + 2; + gDisableStructs[gBattlerTarget].disableTimerStartValue = gDisableStructs[gBattlerTarget].disableTimer; // used to save the random amount of turns? + gBattlescriptCurrInstr += 5; + } + else + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } +} + +static void atkA4_trysetencore(void) +{ + s32 i; + + for (i = 0; i < MAX_MON_MOVES; ++i) + if (gBattleMons[gBattlerTarget].moves[i] == gLastMoves[gBattlerTarget]) + break; + if (gLastMoves[gBattlerTarget] == MOVE_STRUGGLE + || gLastMoves[gBattlerTarget] == MOVE_ENCORE + || gLastMoves[gBattlerTarget] == MOVE_MIRROR_MOVE) + i = 4; + if (gDisableStructs[gBattlerTarget].encoredMove == MOVE_NONE + && i != 4 + && gBattleMons[gBattlerTarget].pp[i] != 0) + { + gDisableStructs[gBattlerTarget].encoredMove = gBattleMons[gBattlerTarget].moves[i]; + gDisableStructs[gBattlerTarget].encoredMovePos = i; + gDisableStructs[gBattlerTarget].encoreTimer = (Random() & 3) + 3; + gDisableStructs[gBattlerTarget].encoreTimerStartValue = gDisableStructs[gBattlerTarget].encoreTimer; + gBattlescriptCurrInstr += 5; + } + else + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } +} + +static void atkA5_painsplitdmgcalc(void) +{ + if (!(gBattleMons[gBattlerTarget].status2 & STATUS2_SUBSTITUTE)) + { + s32 hpDiff = (gBattleMons[gBattlerAttacker].hp + gBattleMons[gBattlerTarget].hp) / 2; + s32 painSplitHp = gBattleMoveDamage = gBattleMons[gBattlerTarget].hp - hpDiff; + u8 *storeLoc = (void *)(&gBattleScripting.painSplitHp); + + storeLoc[0] = (painSplitHp); + storeLoc[1] = (painSplitHp & 0x0000FF00) >> 8; + storeLoc[2] = (painSplitHp & 0x00FF0000) >> 16; + storeLoc[3] = (painSplitHp & 0xFF000000) >> 24; + gBattleMoveDamage = gBattleMons[gBattlerAttacker].hp - hpDiff; + gSpecialStatuses[gBattlerTarget].dmg = 0xFFFF; + gBattlescriptCurrInstr += 5; + } + else + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } +} + +static void atkA6_settypetorandomresistance(void) // conversion 2 +{ + if (gLastLandedMoves[gBattlerAttacker] == MOVE_NONE + || gLastLandedMoves[gBattlerAttacker] == 0xFFFF) + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } + else if (IsTwoTurnsMove(gLastLandedMoves[gBattlerAttacker]) + && gBattleMons[gLastHitBy[gBattlerAttacker]].status2 & STATUS2_MULTIPLETURNS) + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } + else + { + s32 i, j, rands; + + for (rands = 0; rands < 1000; ++rands) + { + while (((i = (Random() & 0x7F)) > sizeof(gTypeEffectiveness) / 3)); + i *= 3; + if (TYPE_EFFECT_ATK_TYPE(i) == gLastHitByType[gBattlerAttacker] + && TYPE_EFFECT_MULTIPLIER(i) <= TYPE_MUL_NOT_EFFECTIVE + && !IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_EFFECT_DEF_TYPE(i))) + { + SET_BATTLER_TYPE(gBattlerAttacker, TYPE_EFFECT_DEF_TYPE(i)); + PREPARE_TYPE_BUFFER(gBattleTextBuff1, TYPE_EFFECT_DEF_TYPE(i)); + + gBattlescriptCurrInstr += 5; + return; + } + } + for (j = 0, rands = 0; rands < sizeof(gTypeEffectiveness); j += 3, rands += 3) + { + switch (TYPE_EFFECT_ATK_TYPE(j)) + { + case TYPE_ENDTABLE: + case TYPE_FORESIGHT: + break; + default: + if (TYPE_EFFECT_ATK_TYPE(j) == gLastHitByType[gBattlerAttacker] + && TYPE_EFFECT_MULTIPLIER(j) <= 5 + && !IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_EFFECT_DEF_TYPE(i))) + { + SET_BATTLER_TYPE(gBattlerAttacker, TYPE_EFFECT_DEF_TYPE(rands)); + PREPARE_TYPE_BUFFER(gBattleTextBuff1, TYPE_EFFECT_DEF_TYPE(rands)) + + gBattlescriptCurrInstr += 5; + return; + } + break; + } + } + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } +} + +static void atkA7_setalwayshitflag(void) +{ + gStatuses3[gBattlerTarget] &= ~(STATUS3_ALWAYS_HITS); + gStatuses3[gBattlerTarget] |= 0x10; + gDisableStructs[gBattlerTarget].battlerWithSureHit = gBattlerAttacker; + ++gBattlescriptCurrInstr; +} + +static void atkA8_copymovepermanently(void) // sketch +{ + gChosenMove = 0xFFFF; + if (!(gBattleMons[gBattlerAttacker].status2 & STATUS2_TRANSFORMED) + && gLastPrintedMoves[gBattlerTarget] != MOVE_STRUGGLE + && gLastPrintedMoves[gBattlerTarget] != 0 + && gLastPrintedMoves[gBattlerTarget] != 0xFFFF + && gLastPrintedMoves[gBattlerTarget] != MOVE_SKETCH) + { + s32 i; + + for (i = 0; i < MAX_MON_MOVES; ++i) + if (gBattleMons[gBattlerAttacker].moves[i] != MOVE_SKETCH && gBattleMons[gBattlerAttacker].moves[i] == gLastPrintedMoves[gBattlerTarget]) + break; + if (i != MAX_MON_MOVES) + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } + else // sketch worked + { + struct MovePpInfo movePpData; + + gBattleMons[gBattlerAttacker].moves[gCurrMovePos] = gLastPrintedMoves[gBattlerTarget]; + gBattleMons[gBattlerAttacker].pp[gCurrMovePos] = gBattleMoves[gLastPrintedMoves[gBattlerTarget]].pp; + gActiveBattler = gBattlerAttacker; + for (i = 0; i < MAX_MON_MOVES; ++i) + { + movePpData.moves[i] = gBattleMons[gBattlerAttacker].moves[i]; + movePpData.pp[i] = gBattleMons[gBattlerAttacker].pp[i]; + } + movePpData.ppBonuses = gBattleMons[gBattlerAttacker].ppBonuses; + BtlController_EmitSetMonData(0, REQUEST_MOVES_PP_BATTLE, 0, sizeof(struct MovePpInfo), &movePpData); + MarkBattlerForControllerExec(gActiveBattler); + PREPARE_MOVE_BUFFER(gBattleTextBuff1, gLastPrintedMoves[gBattlerTarget]) + gBattlescriptCurrInstr += 5; + } + } + else + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } +} + +static bool8 IsTwoTurnsMove(u16 move) +{ + if (gBattleMoves[move].effect == EFFECT_SKULL_BASH + || gBattleMoves[move].effect == EFFECT_RAZOR_WIND + || gBattleMoves[move].effect == EFFECT_SKY_ATTACK + || gBattleMoves[move].effect == EFFECT_SOLARBEAM + || gBattleMoves[move].effect == EFFECT_SEMI_INVULNERABLE + || gBattleMoves[move].effect == EFFECT_BIDE) + return TRUE; + else + return FALSE; +} + +static bool8 IsInvalidForSleepTalkOrAssist(u16 move) +{ + if (move == MOVE_NONE || move == MOVE_SLEEP_TALK || move == MOVE_ASSIST + || move == MOVE_MIRROR_MOVE || move == MOVE_METRONOME) + return TRUE; + else + return FALSE; +} + +static u8 AttacksThisTurn(u8 battlerId, u16 move) // Note: returns 1 if it's a charging turn, otherwise 2 +{ + // first argument is unused + if (gBattleMoves[move].effect == EFFECT_SOLARBEAM + && (gBattleWeather & WEATHER_SUN_ANY)) + return 2; + if (gBattleMoves[move].effect == EFFECT_SKULL_BASH + || gBattleMoves[move].effect == EFFECT_RAZOR_WIND + || gBattleMoves[move].effect == EFFECT_SKY_ATTACK + || gBattleMoves[move].effect == EFFECT_SOLARBEAM + || gBattleMoves[move].effect == EFFECT_SEMI_INVULNERABLE + || gBattleMoves[move].effect == EFFECT_BIDE) + if ((gHitMarker & HITMARKER_CHARGING)) + return 1; + return 2; +} + +static void atkA9_trychoosesleeptalkmove(void) +{ + s32 i; + u8 unusableMovesBits = 0; + + for (i = 0; i < MAX_MON_MOVES; ++i) + { + if (IsInvalidForSleepTalkOrAssist(gBattleMons[gBattlerAttacker].moves[i]) + || gBattleMons[gBattlerAttacker].moves[i] == MOVE_FOCUS_PUNCH + || gBattleMons[gBattlerAttacker].moves[i] == MOVE_UPROAR + || IsTwoTurnsMove(gBattleMons[gBattlerAttacker].moves[i])) + { + unusableMovesBits |= gBitTable[i]; + } + } + unusableMovesBits = CheckMoveLimitations(gBattlerAttacker, unusableMovesBits, ~(MOVE_LIMITATION_PP)); + if (unusableMovesBits == 0xF) // all 4 moves cannot be chosen + { + gBattlescriptCurrInstr += 5; + } + else // at least one move can be chosen + { + u32 movePosition; + + do + movePosition = Random() & 3; + while ((gBitTable[movePosition] & unusableMovesBits)); + gCalledMove = gBattleMons[gBattlerAttacker].moves[movePosition]; + gCurrMovePos = movePosition; + gHitMarker &= ~(HITMARKER_ATTACKSTRING_PRINTED); + gBattlerTarget = GetMoveTarget(gCalledMove, 0); + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } +} + +static void atkAA_setdestinybond(void) +{ + gBattleMons[gBattlerAttacker].status2 |= STATUS2_DESTINY_BOND; + ++gBattlescriptCurrInstr; +} + +static void TrySetDestinyBondToHappen(void) +{ + u8 sideAttacker = GetBattlerSide(gBattlerAttacker); + u8 sideTarget = GetBattlerSide(gBattlerTarget); + + if (gBattleMons[gBattlerTarget].status2 & STATUS2_DESTINY_BOND + && sideAttacker != sideTarget + && !(gHitMarker & HITMARKER_GRUDGE)) + gHitMarker |= HITMARKER_DESTINYBOND; +} + +static void atkAB_trysetdestinybondtohappen(void) +{ + TrySetDestinyBondToHappen(); + ++gBattlescriptCurrInstr; +} + +static void atkAC_remaininghptopower(void) +{ + s32 i; + s32 hpFraction = GetScaledHPFraction(gBattleMons[gBattlerAttacker].hp, gBattleMons[gBattlerAttacker].maxHP, 48); + + for (i = 0; i < (s32)sizeof(sFlailHpScaleToPowerTable); i += 2) + { + if (hpFraction <= sFlailHpScaleToPowerTable[i]) + break; + } + gDynamicBasePower = sFlailHpScaleToPowerTable[i + 1]; + ++gBattlescriptCurrInstr; +} + +static void atkAD_tryspiteppreduce(void) +{ + if (gLastMoves[gBattlerTarget] != MOVE_NONE && gLastMoves[gBattlerTarget] != 0xFFFF) + { + s32 i; + + for (i = 0; i < MAX_MON_MOVES; ++i) + if (gLastMoves[gBattlerTarget] == gBattleMons[gBattlerTarget].moves[i]) + break; + if (i != MAX_MON_MOVES && gBattleMons[gBattlerTarget].pp[i] > 1) + { + s32 ppToDeduct = (Random() & 3) + 2; + + if (gBattleMons[gBattlerTarget].pp[i] < ppToDeduct) + ppToDeduct = gBattleMons[gBattlerTarget].pp[i]; + PREPARE_MOVE_BUFFER(gBattleTextBuff1, gLastMoves[gBattlerTarget]) + ConvertIntToDecimalStringN(gBattleTextBuff2, ppToDeduct, 0, 1); + PREPARE_BYTE_NUMBER_BUFFER(gBattleTextBuff2, 1, ppToDeduct) + gBattleMons[gBattlerTarget].pp[i] -= ppToDeduct; + gActiveBattler = gBattlerTarget; + if (!(gDisableStructs[gActiveBattler].mimickedMoves & gBitTable[i]) + && !(gBattleMons[gActiveBattler].status2 & STATUS2_TRANSFORMED)) + { + BtlController_EmitSetMonData(0, REQUEST_PPMOVE1_BATTLE + i, 0, 1, &gBattleMons[gActiveBattler].pp[i]); + MarkBattlerForControllerExec(gActiveBattler); + } + gBattlescriptCurrInstr += 5; + if (gBattleMons[gBattlerTarget].pp[i] == 0) + CancelMultiTurnMoves(gBattlerTarget); + } + else + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } + } + else + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } +} + +static void atkAE_healpartystatus(void) +{ + u32 zero = 0; + u8 toHeal = 0; + + if (gCurrentMove == MOVE_HEAL_BELL) + { + struct Pokemon *party; + s32 i; + + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER) + party = gPlayerParty; + else + party = gEnemyParty; + if (gBattleMons[gBattlerAttacker].ability != ABILITY_SOUNDPROOF) + { + gBattleMons[gBattlerAttacker].status1 = 0; + gBattleMons[gBattlerAttacker].status2 &= ~(STATUS2_NIGHTMARE); + } + else + { + RecordAbilityBattle(gBattlerAttacker, gBattleMons[gBattlerAttacker].ability); + gBattleCommunication[MULTISTRING_CHOOSER] |= 1; + } + gActiveBattler = gBattleScripting.battler = GetBattlerAtPosition(GetBattlerPosition(gBattlerAttacker) ^ BIT_FLANK); + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE + && !(gAbsentBattlerFlags & gBitTable[gActiveBattler])) + { + if (gBattleMons[gActiveBattler].ability != ABILITY_SOUNDPROOF) + { + gBattleMons[gActiveBattler].status1 = 0; + gBattleMons[gActiveBattler].status2 &= ~(STATUS2_NIGHTMARE); + } + else + { + RecordAbilityBattle(gActiveBattler, gBattleMons[gActiveBattler].ability); + gBattleCommunication[MULTISTRING_CHOOSER] |= 2; + } + } + for (i = 0; i < PARTY_SIZE; ++i) + { + u16 species = GetMonData(&party[i], MON_DATA_SPECIES2); + u8 abilityNum = GetMonData(&party[i], MON_DATA_ABILITY_NUM); + + if (species != SPECIES_NONE && species != SPECIES_EGG) + { + u8 ability; + + if (gBattlerPartyIndexes[gBattlerAttacker] == i) + ability = gBattleMons[gBattlerAttacker].ability; + else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE + && gBattlerPartyIndexes[gActiveBattler] == i + && !(gAbsentBattlerFlags & gBitTable[gActiveBattler])) + ability = gBattleMons[gActiveBattler].ability; + else + ability = GetAbilityBySpecies(species, abilityNum); + if (ability != ABILITY_SOUNDPROOF) + toHeal |= (1 << i); + } + } + } + else // Aromatherapy + { + gBattleCommunication[MULTISTRING_CHOOSER] = 4; + toHeal = 0x3F; + gBattleMons[gBattlerAttacker].status1 = 0; + gBattleMons[gBattlerAttacker].status2 &= ~(STATUS2_NIGHTMARE); + gActiveBattler = GetBattlerAtPosition(GetBattlerPosition(gBattlerAttacker) ^ BIT_FLANK); + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE + && !(gAbsentBattlerFlags & gBitTable[gActiveBattler])) + { + gBattleMons[gActiveBattler].status1 = 0; + gBattleMons[gActiveBattler].status2 &= ~(STATUS2_NIGHTMARE); + } + + } + if (toHeal) + { + gActiveBattler = gBattlerAttacker; + BtlController_EmitSetMonData(0, REQUEST_STATUS_BATTLE, toHeal, 4, &zero); + MarkBattlerForControllerExec(gActiveBattler); + } + ++gBattlescriptCurrInstr; +} + +static void atkAF_cursetarget(void) +{ + if (gBattleMons[gBattlerTarget].status2 & STATUS2_CURSED) + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } + else + { + gBattleMons[gBattlerTarget].status2 |= STATUS2_CURSED; + gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / 2; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + gBattlescriptCurrInstr += 5; + } +} + +static void atkB0_trysetspikes(void) +{ + u8 targetSide = GetBattlerSide(gBattlerAttacker) ^ BIT_SIDE; + + if (gSideTimers[targetSide].spikesAmount == 3) + { + gSpecialStatuses[gBattlerAttacker].ppNotAffectedByPressure = 1; + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } + else + { + gSideStatuses[targetSide] |= SIDE_STATUS_SPIKES; + ++gSideTimers[targetSide].spikesAmount; + gBattlescriptCurrInstr += 5; + } +} + +static void atkB1_setforesight(void) +{ + gBattleMons[gBattlerTarget].status2 |= STATUS2_FORESIGHT; + ++gBattlescriptCurrInstr; +} + +static void atkB2_trysetperishsong(void) +{ + s32 i; + s32 notAffectedCount = 0; + + for (i = 0; i < gBattlersCount; ++i) + { + if (gStatuses3[i] & STATUS3_PERISH_SONG + || gBattleMons[i].ability == ABILITY_SOUNDPROOF) + { + ++notAffectedCount; + } + else + { + gStatuses3[i] |= STATUS3_PERISH_SONG; + gDisableStructs[i].perishSongTimer = 3; + gDisableStructs[i].perishSongTimerStartValue = 3; + } + } + PressurePPLoseOnUsingPerishSong(gBattlerAttacker); + if (notAffectedCount == gBattlersCount) + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + else + gBattlescriptCurrInstr += 5; +} + +static void atkB3_rolloutdamagecalculation(void) +{ + if (gMoveResultFlags & MOVE_RESULT_NO_EFFECT) + { + CancelMultiTurnMoves(gBattlerAttacker); + gBattlescriptCurrInstr = BattleScript_MoveMissedPause; + } + else + { + s32 i; + + if (!(gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS)) // first hit + { + gDisableStructs[gBattlerAttacker].rolloutTimer = 5; + gDisableStructs[gBattlerAttacker].rolloutTimerStartValue = 5; + gBattleMons[gBattlerAttacker].status2 |= STATUS2_MULTIPLETURNS; + gLockedMoves[gBattlerAttacker] = gCurrentMove; + } + if (--gDisableStructs[gBattlerAttacker].rolloutTimer == 0) // last hit + gBattleMons[gBattlerAttacker].status2 &= ~(STATUS2_MULTIPLETURNS); + gDynamicBasePower = gBattleMoves[gCurrentMove].power; + for (i = 1; i < (5 - gDisableStructs[gBattlerAttacker].rolloutTimer); ++i) + gDynamicBasePower *= 2; + if (gBattleMons[gBattlerAttacker].status2 & STATUS2_DEFENSE_CURL) + gDynamicBasePower *= 2; + ++gBattlescriptCurrInstr; + } +} + +static void atkB4_jumpifconfusedandstatmaxed(void) +{ + if (gBattleMons[gBattlerTarget].status2 & STATUS2_CONFUSION + && gBattleMons[gBattlerTarget].statStages[gBattlescriptCurrInstr[1]] == 12) + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 2); + else + gBattlescriptCurrInstr += 6; +} + +static void atkB5_furycuttercalc(void) +{ + if (gMoveResultFlags & MOVE_RESULT_NO_EFFECT) + { + gDisableStructs[gBattlerAttacker].furyCutterCounter = 0; + gBattlescriptCurrInstr = BattleScript_MoveMissedPause; + } + else + { + s32 i; + + if (gDisableStructs[gBattlerAttacker].furyCutterCounter != 5) + ++gDisableStructs[gBattlerAttacker].furyCutterCounter; + gDynamicBasePower = gBattleMoves[gCurrentMove].power; + + for (i = 1; i < gDisableStructs[gBattlerAttacker].furyCutterCounter; ++i) + gDynamicBasePower *= 2; + ++gBattlescriptCurrInstr; + } +} + +static void atkB6_happinesstodamagecalculation(void) +{ + if (gBattleMoves[gCurrentMove].effect == EFFECT_RETURN) + gDynamicBasePower = 10 * (gBattleMons[gBattlerAttacker].friendship) / 25; + else // EFFECT_FRUSTRATION + gDynamicBasePower = 10 * (255 - gBattleMons[gBattlerAttacker].friendship) / 25; + ++gBattlescriptCurrInstr; +} + +static void atkB7_presentdamagecalculation(void) +{ + s32 rand = Random() & 0xFF; + + if (rand < 102) + { + gDynamicBasePower = 40; + } + else if (rand < 178) + { + gDynamicBasePower = 80; + } + else if (rand < 204) + { + gDynamicBasePower = 120; + } + else + { + gBattleMoveDamage = gBattleMons[gBattlerTarget].maxHP / 4; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + gBattleMoveDamage *= -1; + } + if (rand < 204) + { + gBattlescriptCurrInstr = BattleScript_HitFromCritCalc; + } + else if (gBattleMons[gBattlerTarget].maxHP == gBattleMons[gBattlerTarget].hp) + { + gBattlescriptCurrInstr = BattleScript_AlreadyAtFullHp; + } + else + { + gMoveResultFlags &= ~(MOVE_RESULT_DOESNT_AFFECT_FOE); + gBattlescriptCurrInstr = BattleScript_PresentHealTarget; + } +} + +static void atkB8_setsafeguard(void) +{ + if (gSideStatuses[GET_BATTLER_SIDE(gBattlerAttacker)] & SIDE_STATUS_SAFEGUARD) + { + gMoveResultFlags |= MOVE_RESULT_MISSED; + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + } + else + { + gSideStatuses[GET_BATTLER_SIDE(gBattlerAttacker)] |= SIDE_STATUS_SAFEGUARD; + gSideTimers[GET_BATTLER_SIDE(gBattlerAttacker)].safeguardTimer = 5; + gSideTimers[GET_BATTLER_SIDE(gBattlerAttacker)].safeguardBattlerId = gBattlerAttacker; + gBattleCommunication[MULTISTRING_CHOOSER] = 5; + } + ++gBattlescriptCurrInstr; +} + +static void atkB9_magnitudedamagecalculation(void) +{ + s32 magnitude = Random() % 100; + + if (magnitude < 5) + { + gDynamicBasePower = 10; + magnitude = 4; + } + else if (magnitude < 15) + { + gDynamicBasePower = 30; + magnitude = 5; + } + else if (magnitude < 35) + { + gDynamicBasePower = 50; + magnitude = 6; + } + else if (magnitude < 65) + { + gDynamicBasePower = 70; + magnitude = 7; + } + else if (magnitude < 85) + { + gDynamicBasePower = 90; + magnitude = 8; + } + else if (magnitude < 95) + { + gDynamicBasePower = 110; + magnitude = 9; + } + else + { + gDynamicBasePower = 150; + magnitude = 10; + } + PREPARE_BYTE_NUMBER_BUFFER(gBattleTextBuff1, 2, magnitude) + for (gBattlerTarget = 0; gBattlerTarget < gBattlersCount; ++gBattlerTarget) + if (gBattlerTarget != gBattlerAttacker && !(gAbsentBattlerFlags & gBitTable[gBattlerTarget])) // a valid target was found + break; + ++gBattlescriptCurrInstr; +} + +static void atkBA_jumpifnopursuitswitchdmg(void) +{ + if (gMultiHitCounter == 1) + { + if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER) + gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); + else + gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); + } + else + { + if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER) + gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); + else + gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT); + } + if (gChosenActionByBattler[gBattlerTarget] == B_ACTION_USE_MOVE + && gBattlerAttacker == *(gBattleStruct->moveTarget + gBattlerTarget) + && !(gBattleMons[gBattlerTarget].status1 & (STATUS1_SLEEP | STATUS1_FREEZE)) + && gBattleMons[gBattlerAttacker].hp + && !gDisableStructs[gBattlerTarget].truantCounter + && gChosenMoveByBattler[gBattlerTarget] == MOVE_PURSUIT) + { + s32 i; + + for (i = 0; i < gBattlersCount; ++i) + if (gBattlerByTurnOrder[i] == gBattlerTarget) + gActionsByTurnOrder[i] = 11; + gCurrentMove = MOVE_PURSUIT; + gCurrMovePos = gChosenMovePos = *(gBattleStruct->chosenMovePositions + gBattlerTarget); + gBattlescriptCurrInstr += 5; + gBattleScripting.animTurn = 1; + gHitMarker &= ~(HITMARKER_ATTACKSTRING_PRINTED); + } + else + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } +} + +static void atkBB_setsunny(void) +{ + if (gBattleWeather & WEATHER_SUN_ANY) + { + gMoveResultFlags |= MOVE_RESULT_MISSED; + gBattleCommunication[MULTISTRING_CHOOSER] = 2; + } + else + { + gBattleWeather = WEATHER_SUN_TEMPORARY; + gBattleCommunication[MULTISTRING_CHOOSER] = 4; + gWishFutureKnock.weatherDuration = 5; + } + ++gBattlescriptCurrInstr; +} + +static void atkBC_maxattackhalvehp(void) // belly drum +{ + u32 halfHp = gBattleMons[gBattlerAttacker].maxHP / 2; + + if (!(gBattleMons[gBattlerAttacker].maxHP / 2)) + halfHp = 1; + if (gBattleMons[gBattlerAttacker].statStages[STAT_ATK] < 12 + && gBattleMons[gBattlerAttacker].hp > halfHp) + { + gBattleMons[gBattlerAttacker].statStages[STAT_ATK] = 12; + gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / 2; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + gBattlescriptCurrInstr += 5; + } + else + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } +} + +static void atkBD_copyfoestats(void) // psych up +{ + s32 i; + + for (i = 0; i < NUM_BATTLE_STATS; ++i) + gBattleMons[gBattlerAttacker].statStages[i] = gBattleMons[gBattlerTarget].statStages[i]; + gBattlescriptCurrInstr += 5; // Has an unused jump ptr(possibly for a failed attempt) parameter. +} + +static void atkBE_rapidspinfree(void) +{ + if (gBattleMons[gBattlerAttacker].status2 & STATUS2_WRAPPED) + { + gBattleScripting.battler = gBattlerTarget; + gBattleMons[gBattlerAttacker].status2 &= ~(STATUS2_WRAPPED); + gBattlerTarget = *(gBattleStruct->wrappedBy + gBattlerAttacker); + gBattleTextBuff1[0] = B_BUFF_PLACEHOLDER_BEGIN; + gBattleTextBuff1[1] = B_BUFF_MOVE; + gBattleTextBuff1[2] = *(gBattleStruct->wrappedMove + gBattlerAttacker * 2 + 0); + gBattleTextBuff1[3] = *(gBattleStruct->wrappedMove + gBattlerAttacker * 2 + 1); + gBattleTextBuff1[4] = B_BUFF_EOS; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_WrapFree; + } + else if (gStatuses3[gBattlerAttacker] & STATUS3_LEECHSEED) + { + gStatuses3[gBattlerAttacker] &= ~(STATUS3_LEECHSEED); + gStatuses3[gBattlerAttacker] &= ~(STATUS3_LEECHSEED_BATTLER); + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_LeechSeedFree; + } + else if (gSideStatuses[GetBattlerSide(gBattlerAttacker)] & SIDE_STATUS_SPIKES) + { + gSideStatuses[GetBattlerSide(gBattlerAttacker)] &= ~(SIDE_STATUS_SPIKES); + gSideTimers[GetBattlerSide(gBattlerAttacker)].spikesAmount = 0; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_SpikesFree; + } + else + { + ++gBattlescriptCurrInstr; + } +} + +static void atkBF_setdefensecurlbit(void) +{ + gBattleMons[gBattlerAttacker].status2 |= STATUS2_DEFENSE_CURL; + ++gBattlescriptCurrInstr; +} + +static void atkC0_recoverbasedonsunlight(void) +{ + gBattlerTarget = gBattlerAttacker; + if (gBattleMons[gBattlerAttacker].hp != gBattleMons[gBattlerAttacker].maxHP) + { + if (gBattleWeather == 0 || !WEATHER_HAS_EFFECT) + gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / 2; + else if (gBattleWeather & WEATHER_SUN_ANY) + gBattleMoveDamage = 20 * gBattleMons[gBattlerAttacker].maxHP / 30; + else // not sunny weather + gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / 4; + + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + gBattleMoveDamage *= -1; + + gBattlescriptCurrInstr += 5; + } + else + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } +} + +static void atkC1_hiddenpowercalc(void) +{ + s32 powerBits, typeBits; + + powerBits = ((gBattleMons[gBattlerAttacker].hpIV & 2) >> 1) + | ((gBattleMons[gBattlerAttacker].attackIV & 2) << 0) + | ((gBattleMons[gBattlerAttacker].defenseIV & 2) << 1) + | ((gBattleMons[gBattlerAttacker].speedIV & 2) << 2) + | ((gBattleMons[gBattlerAttacker].spAttackIV & 2) << 3) + | ((gBattleMons[gBattlerAttacker].spDefenseIV & 2) << 4); + typeBits = ((gBattleMons[gBattlerAttacker].hpIV & 1) << 0) + | ((gBattleMons[gBattlerAttacker].attackIV & 1) << 1) + | ((gBattleMons[gBattlerAttacker].defenseIV & 1) << 2) + | ((gBattleMons[gBattlerAttacker].speedIV & 1) << 3) + | ((gBattleMons[gBattlerAttacker].spAttackIV & 1) << 4) + | ((gBattleMons[gBattlerAttacker].spDefenseIV & 1) << 5); + gDynamicBasePower = (40 * powerBits) / 63 + 30; + gBattleStruct->dynamicMoveType = (15 * typeBits) / 63 + 1; + if (gBattleStruct->dynamicMoveType >= TYPE_MYSTERY) + ++gBattleStruct->dynamicMoveType; + gBattleStruct->dynamicMoveType |= 0xC0; + ++gBattlescriptCurrInstr; +} + +static void atkC2_selectfirstvalidtarget(void) +{ + for (gBattlerTarget = 0; gBattlerTarget < gBattlersCount; ++gBattlerTarget) + if (gBattlerTarget != gBattlerAttacker && !(gAbsentBattlerFlags & gBitTable[gBattlerTarget])) + break; + ++gBattlescriptCurrInstr; +} + +static void atkC3_trysetfutureattack(void) +{ + if (gWishFutureKnock.futureSightCounter[gBattlerTarget] != 0) + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } + else + { + gWishFutureKnock.futureSightMove[gBattlerTarget] = gCurrentMove; + gWishFutureKnock.futureSightAttacker[gBattlerTarget] = gBattlerAttacker; + gWishFutureKnock.futureSightCounter[gBattlerTarget] = 3; + gWishFutureKnock.futureSightDmg[gBattlerTarget] = CalculateBaseDamage(&gBattleMons[gBattlerAttacker], + &gBattleMons[gBattlerTarget], + gCurrentMove, + gSideStatuses[GET_BATTLER_SIDE(gBattlerTarget)], + 0, + 0, + gBattlerAttacker, + gBattlerTarget); + if (gProtectStructs[gBattlerAttacker].helpingHand) + gWishFutureKnock.futureSightDmg[gBattlerTarget] = gWishFutureKnock.futureSightDmg[gBattlerTarget] * 15 / 10; + if (gCurrentMove == MOVE_DOOM_DESIRE) + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + else + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + gBattlescriptCurrInstr += 5; + } +} + +static void atkC4_trydobeatup(void) +{ + struct Pokemon *party; + + if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER) + party = gPlayerParty; + else + party = gEnemyParty; + if (gBattleMons[gBattlerTarget].hp == 0) + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } + else + { + u8 beforeLoop = gBattleCommunication[0]; + + for (;gBattleCommunication[0] < 6; ++gBattleCommunication[0]) + { + if (GetMonData(&party[gBattleCommunication[0]], MON_DATA_HP) + && GetMonData(&party[gBattleCommunication[0]], MON_DATA_SPECIES2) + && GetMonData(&party[gBattleCommunication[0]], MON_DATA_SPECIES2) != SPECIES_EGG + && !GetMonData(&party[gBattleCommunication[0]], MON_DATA_STATUS)) + break; + } + if (gBattleCommunication[0] < 6) + { + PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, gBattlerAttacker, gBattleCommunication[0]) + gBattlescriptCurrInstr += 9; + gBattleMoveDamage = gBaseStats[GetMonData(&party[gBattleCommunication[0]], MON_DATA_SPECIES)].baseAttack; + gBattleMoveDamage *= gBattleMoves[gCurrentMove].power; + gBattleMoveDamage *= (GetMonData(&party[gBattleCommunication[0]], MON_DATA_LEVEL) * 2 / 5 + 2); + gBattleMoveDamage /= gBaseStats[gBattleMons[gBattlerTarget].species].baseDefense; + gBattleMoveDamage = (gBattleMoveDamage / 50) + 2; + if (gProtectStructs[gBattlerAttacker].helpingHand) + gBattleMoveDamage = gBattleMoveDamage * 15 / 10; + ++gBattleCommunication[0]; + } + else if (beforeLoop != 0) + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + else + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 5); + } +} + +static void atkC5_setsemiinvulnerablebit(void) +{ + switch (gCurrentMove) + { + case MOVE_FLY: + case MOVE_BOUNCE: + gStatuses3[gBattlerAttacker] |= STATUS3_ON_AIR; + break; + case MOVE_DIG: + gStatuses3[gBattlerAttacker] |= STATUS3_UNDERGROUND; + break; + case MOVE_DIVE: + gStatuses3[gBattlerAttacker] |= STATUS3_UNDERWATER; + break; + } + ++gBattlescriptCurrInstr; +} + +static void atkC6_clearsemiinvulnerablebit(void) +{ + switch (gCurrentMove) + { + case MOVE_FLY: + case MOVE_BOUNCE: + gStatuses3[gBattlerAttacker] &= ~STATUS3_ON_AIR; + break; + case MOVE_DIG: + gStatuses3[gBattlerAttacker] &= ~STATUS3_UNDERGROUND; + break; + case MOVE_DIVE: + gStatuses3[gBattlerAttacker] &= ~STATUS3_UNDERWATER; + break; + } + ++gBattlescriptCurrInstr; +} + +static void atkC7_setminimize(void) +{ + if (gHitMarker & HITMARKER_OBEYS) + gStatuses3[gBattlerAttacker] |= STATUS3_MINIMIZED; + ++gBattlescriptCurrInstr; +} + +static void atkC8_sethail(void) +{ + if (gBattleWeather & WEATHER_HAIL_ANY) + { + gMoveResultFlags |= MOVE_RESULT_MISSED; + gBattleCommunication[MULTISTRING_CHOOSER] = 2; + } + else + { + gBattleWeather = WEATHER_HAIL; + gBattleCommunication[MULTISTRING_CHOOSER] = 5; + gWishFutureKnock.weatherDuration = 5; + } + ++gBattlescriptCurrInstr; +} + +static void atkC9_jumpifattackandspecialattackcannotfall(void) // memento +{ + if (gBattleMons[gBattlerTarget].statStages[STAT_ATK] == 0 + && gBattleMons[gBattlerTarget].statStages[STAT_SPATK] == 0 + && gBattleCommunication[6] != 1) + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } + else + { + gActiveBattler = gBattlerAttacker; + gBattleMoveDamage = gBattleMons[gActiveBattler].hp; + BtlController_EmitHealthBarUpdate(0, INSTANT_HP_BAR_DROP); + MarkBattlerForControllerExec(gActiveBattler); + gBattlescriptCurrInstr += 5; + } +} + +static void atkCA_setforcedtarget(void) // follow me +{ + gSideTimers[GetBattlerSide(gBattlerAttacker)].followmeTimer = 1; + gSideTimers[GetBattlerSide(gBattlerAttacker)].followmeTarget = gBattlerAttacker; + ++gBattlescriptCurrInstr; +} + +static void atkCB_setcharge(void) +{ + gStatuses3[gBattlerAttacker] |= STATUS3_CHARGED_UP; + gDisableStructs[gBattlerAttacker].chargeTimer = 2; + gDisableStructs[gBattlerAttacker].chargeTimerStartValue = 2; + ++gBattlescriptCurrInstr; +} + +static void atkCC_callterrainattack(void) // nature power +{ + gHitMarker &= ~(HITMARKER_ATTACKSTRING_PRINTED); + gCurrentMove = sNaturePowerMoves[gBattleTerrain]; + gBattlerTarget = GetMoveTarget(gCurrentMove, 0); + BattleScriptPush(gBattleScriptsForMoveEffects[gBattleMoves[gCurrentMove].effect]); + ++gBattlescriptCurrInstr; +} + +static void atkCD_cureifburnedparalysedorpoisoned(void) // refresh +{ + if (gBattleMons[gBattlerAttacker].status1 & (STATUS1_POISON | STATUS1_BURN | STATUS1_PARALYSIS | STATUS1_TOXIC_POISON)) + { + gBattleMons[gBattlerAttacker].status1 = 0; + gBattlescriptCurrInstr += 5; + gActiveBattler = gBattlerAttacker; + BtlController_EmitSetMonData(0, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[gActiveBattler].status1); + MarkBattlerForControllerExec(gActiveBattler); + } + else + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } +} + +static void atkCE_settorment(void) +{ + if (gBattleMons[gBattlerTarget].status2 & STATUS2_TORMENT) + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } + else + { + gBattleMons[gBattlerTarget].status2 |= STATUS2_TORMENT; + gBattlescriptCurrInstr += 5; + } +} + +static void atkCF_jumpifnodamage(void) +{ + if (gProtectStructs[gBattlerAttacker].physicalDmg || gProtectStructs[gBattlerAttacker].specialDmg) + gBattlescriptCurrInstr += 5; + else + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); +} + +static void atkD0_settaunt(void) +{ + if (gDisableStructs[gBattlerTarget].tauntTimer == 0) + { + gDisableStructs[gBattlerTarget].tauntTimer = 2; + gDisableStructs[gBattlerTarget].tauntTimer2 = 2; + gBattlescriptCurrInstr += 5; + } + else + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } +} + +static void atkD1_trysethelpinghand(void) +{ + gBattlerTarget = GetBattlerAtPosition(GetBattlerPosition(gBattlerAttacker) ^ BIT_FLANK); + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE + && !(gAbsentBattlerFlags & gBitTable[gBattlerTarget]) + && !gProtectStructs[gBattlerAttacker].helpingHand + && !gProtectStructs[gBattlerTarget].helpingHand) + { + gProtectStructs[gBattlerTarget].helpingHand = 1; + gBattlescriptCurrInstr += 5; + } + else + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } +} + +static void atkD2_tryswapitems(void) // trick +{ + // opponent can't swap items with player in regular battles + if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_TOWER + || (GetBattlerSide(gBattlerAttacker) == B_SIDE_OPPONENT + && !(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_BATTLE_TOWER | BATTLE_TYPE_EREADER_TRAINER)) + && gTrainerBattleOpponent_A != 0x400)) + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } + else + { + u8 sideAttacker = GetBattlerSide(gBattlerAttacker); + u8 sideTarget = GetBattlerSide(gBattlerTarget); + + // you can't swap items if they were knocked off in regular battles + if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_BATTLE_TOWER | BATTLE_TYPE_EREADER_TRAINER)) + && gTrainerBattleOpponent_A != 0x400 + && (gWishFutureKnock.knockedOffMons[sideAttacker] & gBitTable[gBattlerPartyIndexes[gBattlerAttacker]] + || gWishFutureKnock.knockedOffMons[sideTarget] & gBitTable[gBattlerPartyIndexes[gBattlerTarget]])) + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } + // can't swap if two pokemon don't have an item + // or if either of them is an enigma berry or a mail + else if ((gBattleMons[gBattlerAttacker].item == 0 && gBattleMons[gBattlerTarget].item == 0) + || gBattleMons[gBattlerAttacker].item == ITEM_ENIGMA_BERRY + || gBattleMons[gBattlerTarget].item == ITEM_ENIGMA_BERRY + || IS_ITEM_MAIL(gBattleMons[gBattlerAttacker].item) + || IS_ITEM_MAIL(gBattleMons[gBattlerTarget].item)) + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } + // check if ability prevents swapping + else if (gBattleMons[gBattlerTarget].ability == ABILITY_STICKY_HOLD) + { + gBattlescriptCurrInstr = BattleScript_StickyHoldActivates; + gLastUsedAbility = gBattleMons[gBattlerTarget].ability; + RecordAbilityBattle(gBattlerTarget, gLastUsedAbility); + } + // took a while, but all checks passed and items can be safely swapped + else + { + u16 oldItemAtk, *newItemAtk; + + newItemAtk = &gBattleStruct->changedItems[gBattlerAttacker]; + oldItemAtk = gBattleMons[gBattlerAttacker].item; + *newItemAtk = gBattleMons[gBattlerTarget].item; + gBattleMons[gBattlerAttacker].item = 0; + gBattleMons[gBattlerTarget].item = oldItemAtk; + gActiveBattler = gBattlerAttacker; + BtlController_EmitSetMonData(0, REQUEST_HELDITEM_BATTLE, 0, 2, newItemAtk); + MarkBattlerForControllerExec(gBattlerAttacker); + gActiveBattler = gBattlerTarget; + BtlController_EmitSetMonData(0, REQUEST_HELDITEM_BATTLE, 0, 2, &gBattleMons[gBattlerTarget].item); + MarkBattlerForControllerExec(gBattlerTarget); + *(u8 *)((u8 *)(&gBattleStruct->choicedMove[gBattlerTarget]) + 0) = 0; + *(u8 *)((u8 *)(&gBattleStruct->choicedMove[gBattlerTarget]) + 1) = 0; + + *(u8 *)((u8 *)(&gBattleStruct->choicedMove[gBattlerAttacker]) + 0) = 0; + *(u8 *)((u8 *)(&gBattleStruct->choicedMove[gBattlerAttacker]) + 1) = 0; + gBattlescriptCurrInstr += 5; + PREPARE_ITEM_BUFFER(gBattleTextBuff1, *newItemAtk) + PREPARE_ITEM_BUFFER(gBattleTextBuff2, oldItemAtk) + if (oldItemAtk != ITEM_NONE && *newItemAtk != ITEM_NONE) + gBattleCommunication[MULTISTRING_CHOOSER] = 2; // attacker's item -> <- target's item + else if (oldItemAtk == ITEM_NONE && *newItemAtk != ITEM_NONE) + gBattleCommunication[MULTISTRING_CHOOSER] = 0; // nothing -> <- target's item + else + gBattleCommunication[MULTISTRING_CHOOSER] = 1; // attacker's item -> <- nothing + } + } +} + +static void atkD3_trycopyability(void) // role play +{ + if (gBattleMons[gBattlerTarget].ability != ABILITY_NONE && gBattleMons[gBattlerTarget].ability != ABILITY_WONDER_GUARD) + { + gBattleMons[gBattlerAttacker].ability = gBattleMons[gBattlerTarget].ability; + gLastUsedAbility = gBattleMons[gBattlerTarget].ability; + gBattlescriptCurrInstr += 5; + } + else + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } +} + +static void atkD4_trywish(void) +{ + switch (gBattlescriptCurrInstr[1]) + { + case 0: // use wish + if (gWishFutureKnock.wishCounter[gBattlerAttacker] == 0) + { + gWishFutureKnock.wishCounter[gBattlerAttacker] = 2; + gWishFutureKnock.wishMonId[gBattlerAttacker] = gBattlerPartyIndexes[gBattlerAttacker]; + gBattlescriptCurrInstr += 6; + } + else + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 2); + } + break; + case 1: // heal effect + PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, gBattlerTarget, gWishFutureKnock.wishMonId[gBattlerTarget]) + gBattleMoveDamage = gBattleMons[gBattlerTarget].maxHP / 2; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + gBattleMoveDamage *= -1; + if (gBattleMons[gBattlerTarget].hp == gBattleMons[gBattlerTarget].maxHP) + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 2); + else + gBattlescriptCurrInstr += 6; + break; + } +} + +static void atkD5_trysetroots(void) // ingrain +{ + if (gStatuses3[gBattlerAttacker] & STATUS3_ROOTED) + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } + else + { + gStatuses3[gBattlerAttacker] |= STATUS3_ROOTED; + gBattlescriptCurrInstr += 5; + } +} + +static void atkD6_doubledamagedealtifdamaged(void) +{ + if ((gProtectStructs[gBattlerAttacker].physicalDmg != 0 + && gProtectStructs[gBattlerAttacker].physicalBattlerId == gBattlerTarget) + || (gProtectStructs[gBattlerAttacker].specialDmg != 0 + && gProtectStructs[gBattlerAttacker].specialBattlerId == gBattlerTarget)) + { + gBattleScripting.dmgMultiplier = 2; + } + ++gBattlescriptCurrInstr; +} + +static void atkD7_setyawn(void) +{ + if (gStatuses3[gBattlerTarget] & STATUS3_YAWN + || gBattleMons[gBattlerTarget].status1 & STATUS1_ANY) + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } + else + { + gStatuses3[gBattlerTarget] |= 0x1000; + gBattlescriptCurrInstr += 5; + } +} + +static void atkD8_setdamagetohealthdifference(void) +{ + if (gBattleMons[gBattlerTarget].hp <= gBattleMons[gBattlerAttacker].hp) + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } + else + { + gBattleMoveDamage = gBattleMons[gBattlerTarget].hp - gBattleMons[gBattlerAttacker].hp; + gBattlescriptCurrInstr += 5; + } +} + +static void atkD9_scaledamagebyhealthratio(void) +{ + if (gDynamicBasePower == 0) + { + u8 power = gBattleMoves[gCurrentMove].power; + + gDynamicBasePower = gBattleMons[gBattlerAttacker].hp * power / gBattleMons[gBattlerAttacker].maxHP; + if (gDynamicBasePower == 0) + gDynamicBasePower = 1; + } + ++gBattlescriptCurrInstr; +} + +static void atkDA_tryswapabilities(void) // skill swap +{ + if ((gBattleMons[gBattlerAttacker].ability == 0 + && gBattleMons[gBattlerTarget].ability == 0) + || gBattleMons[gBattlerAttacker].ability == ABILITY_WONDER_GUARD + || gBattleMons[gBattlerTarget].ability == ABILITY_WONDER_GUARD + || gMoveResultFlags & MOVE_RESULT_NO_EFFECT) + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } + else + { + u8 abilityAtk = gBattleMons[gBattlerAttacker].ability; + + gBattleMons[gBattlerAttacker].ability = gBattleMons[gBattlerTarget].ability; + gBattleMons[gBattlerTarget].ability = abilityAtk; + + gBattlescriptCurrInstr += 5; + } +} + +static void atkDB_tryimprison(void) +{ + if ((gStatuses3[gBattlerAttacker] & STATUS3_IMPRISONED_OTHERS)) + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } + else + { + u8 battlerId, sideAttacker; + + sideAttacker = GetBattlerSide(gBattlerAttacker); + PressurePPLoseOnUsingImprison(gBattlerAttacker); + for (battlerId = 0; battlerId < gBattlersCount; ++battlerId) + { + if (sideAttacker != GetBattlerSide(battlerId)) + { + s32 attackerMoveId; + + for (attackerMoveId = 0; attackerMoveId < MAX_MON_MOVES; ++attackerMoveId) + { + s32 i; + + for (i = 0; i < MAX_MON_MOVES; ++i) + { + if (gBattleMons[gBattlerAttacker].moves[attackerMoveId] == gBattleMons[battlerId].moves[i] + && gBattleMons[gBattlerAttacker].moves[attackerMoveId] != MOVE_NONE) + break; + } + if (i != MAX_MON_MOVES) + break; + } + if (attackerMoveId != MAX_MON_MOVES) + { + gStatuses3[gBattlerAttacker] |= STATUS3_IMPRISONED_OTHERS; + gBattlescriptCurrInstr += 5; + break; + } + } + } + if (battlerId == gBattlersCount) // In Generation 3 games, Imprison fails if the user doesn't share any moves with any of the foes + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } +} + +static void atkDC_trysetgrudge(void) +{ + if (gStatuses3[gBattlerAttacker] & STATUS3_GRUDGE) + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } + else + { + gStatuses3[gBattlerAttacker] |= STATUS3_GRUDGE; + gBattlescriptCurrInstr += 5; + } +} + +static void atkDD_weightdamagecalculation(void) +{ + s32 i; + + for (i = 0; sWeightToDamageTable[i] != 0xFFFF; i += 2) + { + if (sWeightToDamageTable[i] > GetPokedexHeightWeight(SpeciesToNationalPokedexNum(gBattleMons[gBattlerTarget].species), 1)) + break; + } + if (sWeightToDamageTable[i] != 0xFFFF) + gDynamicBasePower = sWeightToDamageTable[i + 1]; + else + gDynamicBasePower = 120; + ++gBattlescriptCurrInstr; +} + +static void atkDE_assistattackselect(void) +{ + s32 chooseableMovesNo = 0; + struct Pokemon *party; + s32 monId, moveId; + u16 *movesArray = gBattleStruct->assistPossibleMoves; + + if (GET_BATTLER_SIDE(gBattlerAttacker) != B_SIDE_PLAYER) + party = gEnemyParty; + else + party = gPlayerParty; + for (monId = 0; monId < PARTY_SIZE; ++monId) + { + if (monId == gBattlerPartyIndexes[gBattlerAttacker] + || GetMonData(&party[monId], MON_DATA_SPECIES2) == SPECIES_NONE + || GetMonData(&party[monId], MON_DATA_SPECIES2) == SPECIES_EGG) + continue; + for (moveId = 0; moveId < MAX_MON_MOVES; ++moveId) + { + s32 i = 0; + u16 move = GetMonData(&party[monId], MON_DATA_MOVE1 + moveId); + + if (IsInvalidForSleepTalkOrAssist(move)) + continue; + for (; sMovesForbiddenToCopy[i] != ASSIST_FORBIDDEN_END && move != sMovesForbiddenToCopy[i]; ++i); + if (sMovesForbiddenToCopy[i] != ASSIST_FORBIDDEN_END || move == MOVE_NONE) + continue; + movesArray[chooseableMovesNo] = move; + ++chooseableMovesNo; + } + } + if (chooseableMovesNo) + { + gHitMarker &= ~(HITMARKER_ATTACKSTRING_PRINTED); + gCalledMove = movesArray[((Random() & 0xFF) * chooseableMovesNo) >> 8]; + gBattlerTarget = GetMoveTarget(gCalledMove, 0); + gBattlescriptCurrInstr += 5; + } + else + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } +} + +static void atkDF_trysetmagiccoat(void) +{ + gBattlerTarget = gBattlerAttacker; + gSpecialStatuses[gBattlerAttacker].ppNotAffectedByPressure = 1; + if (gCurrentTurnActionNumber == gBattlersCount - 1) // moves last turn + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } + else + { + gProtectStructs[gBattlerAttacker].bounceMove = 1; + gBattlescriptCurrInstr += 5; + } +} + +static void atkE0_trysetsnatch(void) // snatch +{ + gSpecialStatuses[gBattlerAttacker].ppNotAffectedByPressure = 1; + if (gCurrentTurnActionNumber == gBattlersCount - 1) // moves last turn + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } + else + { + gProtectStructs[gBattlerAttacker].stealMove = 1; + gBattlescriptCurrInstr += 5; + } +} + +static void atkE1_trygetintimidatetarget(void) +{ + u8 side; + + gBattleScripting.battler = gBattleStruct->intimidateBattler; + side = GetBattlerSide(gBattleScripting.battler); + PREPARE_ABILITY_BUFFER(gBattleTextBuff1, gBattleMons[gBattleScripting.battler].ability) + for (;gBattlerTarget < gBattlersCount; ++gBattlerTarget) + if (GetBattlerSide(gBattlerTarget) != side && !(gAbsentBattlerFlags & gBitTable[gBattlerTarget])) + break; + if (gBattlerTarget >= gBattlersCount) + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + else + gBattlescriptCurrInstr += 5; +} + +static void atkE2_switchoutabilities(void) +{ + gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + switch (gBattleMons[gActiveBattler].ability) + { + case ABILITY_NATURAL_CURE: + gBattleMons[gActiveBattler].status1 = 0; + BtlController_EmitSetMonData(0, REQUEST_STATUS_BATTLE, gBitTable[*(gBattleStruct->field_58 + gActiveBattler)], 4, &gBattleMons[gActiveBattler].status1); + MarkBattlerForControllerExec(gActiveBattler); + break; + } + gBattlescriptCurrInstr += 2; +} + +static void atkE3_jumpifhasnohp(void) +{ + gActiveBattler = GetBattlerForBattleScript(gBattlescriptCurrInstr[1]); + + if (gBattleMons[gActiveBattler].hp == 0) + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 2); + else + gBattlescriptCurrInstr += 6; +} + +static void atkE4_getsecretpowereffect(void) +{ + switch (gBattleTerrain) + { + case BATTLE_TERRAIN_GRASS: + gBattleCommunication[MOVE_EFFECT_BYTE] = MOVE_EFFECT_POISON; + break; + case BATTLE_TERRAIN_LONG_GRASS: + gBattleCommunication[MOVE_EFFECT_BYTE] = MOVE_EFFECT_SLEEP; + break; + case BATTLE_TERRAIN_SAND: + gBattleCommunication[MOVE_EFFECT_BYTE] = MOVE_EFFECT_ACC_MINUS_1; + break; + case BATTLE_TERRAIN_UNDERWATER: + gBattleCommunication[MOVE_EFFECT_BYTE] = MOVE_EFFECT_DEF_MINUS_1; + break; + case BATTLE_TERRAIN_WATER: + gBattleCommunication[MOVE_EFFECT_BYTE] = MOVE_EFFECT_ATK_MINUS_1; + break; + case BATTLE_TERRAIN_POND: + gBattleCommunication[MOVE_EFFECT_BYTE] = MOVE_EFFECT_SPD_MINUS_1; + break; + case BATTLE_TERRAIN_MOUNTAIN: + gBattleCommunication[MOVE_EFFECT_BYTE] = MOVE_EFFECT_CONFUSION; + break; + case BATTLE_TERRAIN_CAVE: + gBattleCommunication[MOVE_EFFECT_BYTE] = MOVE_EFFECT_FLINCH; + break; + default: + gBattleCommunication[MOVE_EFFECT_BYTE] = MOVE_EFFECT_PARALYSIS; + break; + } + ++gBattlescriptCurrInstr; +} + +static void atkE5_pickup(void) +{ + s32 i; + u32 j; + u16 species, heldItem; + u32 ability; + + for (i = 0; i < PARTY_SIZE; ++i) + { + species = GetMonData(&gPlayerParty[i], MON_DATA_SPECIES2); + heldItem = GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM); + if (GetMonData(&gPlayerParty[i], MON_DATA_ABILITY_NUM) != ABILITY_NONE) + ability = gBaseStats[species].abilities[1]; + else + ability = gBaseStats[species].abilities[0]; + if (ability == ABILITY_PICKUP && species != SPECIES_NONE && species != SPECIES_EGG && heldItem == ITEM_NONE && !(Random() % 10)) + { + s32 random = Random() % 100; + + for (j = 0; j < 15; ++j) + if (sPickupItems[j].chance > random) + break; + SetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM, &sPickupItems[j]); + } + } + ++gBattlescriptCurrInstr; +} + +static void atkE6_docastformchangeanimation(void) +{ + gActiveBattler = gBattleScripting.battler; + if (gBattleMons[gActiveBattler].status2 & STATUS2_SUBSTITUTE) + *(&gBattleStruct->formToChangeInto) |= 0x80; + BtlController_EmitBattleAnimation(0, B_ANIM_CASTFORM_CHANGE, gBattleStruct->formToChangeInto); + MarkBattlerForControllerExec(gActiveBattler); + ++gBattlescriptCurrInstr; +} + +static void atkE7_trycastformdatachange(void) +{ + u8 form; + + ++gBattlescriptCurrInstr; + form = CastformDataTypeChange(gBattleScripting.battler); + if (form) + { + BattleScriptPushCursorAndCallback(BattleScript_CastformChange); + *(&gBattleStruct->formToChangeInto) = form - 1; + } +} + +static void atkE8_settypebasedhalvers(void) // water and mud sport +{ + bool8 worked = FALSE; + + if (gBattleMoves[gCurrentMove].effect == EFFECT_MUD_SPORT) + { + if (!(gStatuses3[gBattlerAttacker] & STATUS3_MUDSPORT)) + { + gStatuses3[gBattlerAttacker] |= STATUS3_MUDSPORT; + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + worked = TRUE; + } + } + else // water sport + { + if (!(gStatuses3[gBattlerAttacker] & STATUS3_WATERSPORT)) + { + gStatuses3[gBattlerAttacker] |= STATUS3_WATERSPORT; + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + worked = TRUE; + } + } + if (worked) + gBattlescriptCurrInstr += 5; + else + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); +} + +static void atkE9_setweatherballtype(void) +{ + if (WEATHER_HAS_EFFECT) + { + if (gBattleWeather & WEATHER_ANY) + gBattleScripting.dmgMultiplier = 2; + if (gBattleWeather & WEATHER_RAIN_ANY) + *(&gBattleStruct->dynamicMoveType) = TYPE_WATER | 0x80; + else if (gBattleWeather & WEATHER_SANDSTORM_ANY) + *(&gBattleStruct->dynamicMoveType) = TYPE_ROCK | 0x80; + else if (gBattleWeather & WEATHER_SUN_ANY) + *(&gBattleStruct->dynamicMoveType) = TYPE_FIRE | 0x80; + else if (gBattleWeather & WEATHER_HAIL_ANY) + *(&gBattleStruct->dynamicMoveType) = TYPE_ICE | 0x80; + else + *(&gBattleStruct->dynamicMoveType) = TYPE_NORMAL | 0x80; + } + ++gBattlescriptCurrInstr; +} + +static void atkEA_tryrecycleitem(void) +{ + u16 *usedHeldItem; + + gActiveBattler = gBattlerAttacker; + usedHeldItem = &gBattleStruct->usedHeldItems[gActiveBattler]; + if (*usedHeldItem != ITEM_NONE && gBattleMons[gActiveBattler].item == ITEM_NONE) + { + gLastUsedItem = *usedHeldItem; + *usedHeldItem = ITEM_NONE; + gBattleMons[gActiveBattler].item = gLastUsedItem; + BtlController_EmitSetMonData(0, REQUEST_HELDITEM_BATTLE, 0, 2, &gBattleMons[gActiveBattler].item); + MarkBattlerForControllerExec(gActiveBattler); + gBattlescriptCurrInstr += 5; + } + else + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } +} + +static void atkEB_settypetoterrain(void) +{ + if (!IS_BATTLER_OF_TYPE(gBattlerAttacker, sTerrainToType[gBattleTerrain])) + { + SET_BATTLER_TYPE(gBattlerAttacker, sTerrainToType[gBattleTerrain]); + PREPARE_TYPE_BUFFER(gBattleTextBuff1, sTerrainToType[gBattleTerrain]); + gBattlescriptCurrInstr += 5; + } + else + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } +} + +static void atkEC_pursuitrelated(void) +{ + gActiveBattler = GetBattlerAtPosition(GetBattlerPosition(gBattlerAttacker) ^ BIT_FLANK); + + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE + && !(gAbsentBattlerFlags & gBitTable[gActiveBattler]) + && gChosenActionByBattler[gActiveBattler] == 0 + && gChosenMoveByBattler[gActiveBattler] == MOVE_PURSUIT) + { + gActionsByTurnOrder[gActiveBattler] = 11; + gCurrentMove = MOVE_PURSUIT; + gBattlescriptCurrInstr += 5; + gBattleScripting.animTurn = 1; + gBattleScripting.field_20 = gBattlerAttacker; + gBattlerAttacker = gActiveBattler; + } + else + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } +} + +static void atkED_snatchsetbattlers(void) +{ + gEffectBattler = gBattlerAttacker; + if (gBattlerAttacker == gBattlerTarget) + gBattlerAttacker = gBattlerTarget = gBattleScripting.battler; + else + gBattlerTarget = gBattleScripting.battler; + gBattleScripting.battler = gEffectBattler; + ++gBattlescriptCurrInstr; +} + +static void atkEE_removelightscreenreflect(void) // brick break +{ + u8 opposingSide = GetBattlerSide(gBattlerAttacker) ^ BIT_SIDE; + + if (gSideTimers[opposingSide].reflectTimer || gSideTimers[opposingSide].lightscreenTimer) + { + gSideStatuses[opposingSide] &= ~(SIDE_STATUS_REFLECT); + gSideStatuses[opposingSide] &= ~(SIDE_STATUS_LIGHTSCREEN); + gSideTimers[opposingSide].reflectTimer = 0; + gSideTimers[opposingSide].lightscreenTimer = 0; + gBattleScripting.animTurn = 1; + gBattleScripting.animTargetsHit = 1; + } + else + { + gBattleScripting.animTurn = 0; + gBattleScripting.animTargetsHit = 0; + } + ++gBattlescriptCurrInstr; +} + +static void atkEF_handleballthrow(void) +{ + u8 ballMultiplier = 0; + + if (!gBattleControllerExecFlags) + { + gActiveBattler = gBattlerAttacker; + gBattlerTarget = gBattlerAttacker ^ BIT_SIDE; + if (gBattleTypeFlags & BATTLE_TYPE_GHOST) + { + BtlController_EmitBallThrowAnim(0, BALL_GHOST_DODGE); + MarkBattlerForControllerExec(gActiveBattler); + gBattlescriptCurrInstr = BattleScript_GhostBallDodge; + } + else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) + { + BtlController_EmitBallThrowAnim(0, BALL_TRAINER_BLOCK); + MarkBattlerForControllerExec(gActiveBattler); + gBattlescriptCurrInstr = BattleScript_TrainerBallBlock; + } + else if (gBattleTypeFlags & (BATTLE_TYPE_POKEDUDE | BATTLE_TYPE_OLDMAN_TUTORIAL)) + { + BtlController_EmitBallThrowAnim(0, BALL_3_SHAKES_SUCCESS); + MarkBattlerForControllerExec(gActiveBattler); + gBattlescriptCurrInstr = gUnknown_81D9A88; + } + else + { + u32 odds; + u8 catchRate; + + if (gLastUsedItem == ITEM_SAFARI_BALL) + catchRate = gBattleStruct->safariCatchFactor * 1275 / 100; + else + catchRate = gBaseStats[gBattleMons[gBattlerTarget].species].catchRate; + + if (gLastUsedItem > ITEM_SAFARI_BALL) + { + switch (gLastUsedItem) + { + case ITEM_NET_BALL: + if (IS_BATTLER_OF_TYPE(gBattlerTarget, TYPE_WATER) || IS_BATTLER_OF_TYPE(gBattlerTarget, TYPE_BUG)) + ballMultiplier = 30; + else + ballMultiplier = 10; + break; + case ITEM_DIVE_BALL: + if (GetCurrentMapType() == MAP_TYPE_UNDERWATER) + ballMultiplier = 35; + else + ballMultiplier = 10; + break; + case ITEM_NEST_BALL: + if (gBattleMons[gBattlerTarget].level < 40) + { + ballMultiplier = 40 - gBattleMons[gBattlerTarget].level; + if (ballMultiplier <= 9) + ballMultiplier = 10; + } + else + { + ballMultiplier = 10; + } + break; + case ITEM_REPEAT_BALL: + if (GetSetPokedexFlag(SpeciesToNationalPokedexNum(gBattleMons[gBattlerTarget].species), FLAG_GET_CAUGHT)) + ballMultiplier = 30; + else + ballMultiplier = 10; + break; + case ITEM_TIMER_BALL: + ballMultiplier = gBattleResults.battleTurnCounter + 10; + if (ballMultiplier > 40) + ballMultiplier = 40; + break; + case ITEM_LUXURY_BALL: + case ITEM_PREMIER_BALL: + ballMultiplier = 10; + break; + } + } + else + ballMultiplier = sBallCatchBonuses[gLastUsedItem - 2]; + odds = (catchRate * ballMultiplier / 10) + * (gBattleMons[gBattlerTarget].maxHP * 3 - gBattleMons[gBattlerTarget].hp * 2) + / (3 * gBattleMons[gBattlerTarget].maxHP); + if (gBattleMons[gBattlerTarget].status1 & (STATUS1_SLEEP | STATUS1_FREEZE)) + odds *= 2; + if (gBattleMons[gBattlerTarget].status1 & (STATUS1_POISON | STATUS1_BURN | STATUS1_PARALYSIS | STATUS1_TOXIC_POISON)) + odds = (odds * 15) / 10; + if (gLastUsedItem != ITEM_SAFARI_BALL) + { + if (gLastUsedItem == ITEM_MASTER_BALL) + { + gBattleResults.usedMasterBall = TRUE; + } + else + { + if (gBattleResults.catchAttempts[gLastUsedItem - ITEM_ULTRA_BALL] < 0xFF) + gBattleResults.catchAttempts[gLastUsedItem - ITEM_ULTRA_BALL]++; + } + } + if (odds > 254) // mon caught + { + BtlController_EmitBallThrowAnim(0, BALL_3_SHAKES_SUCCESS); + MarkBattlerForControllerExec(gActiveBattler); + gBattlescriptCurrInstr = BattleScript_SuccessBallThrow; + SetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]], MON_DATA_POKEBALL, &gLastUsedItem); + if (CalculatePlayerPartyCount() == 6) + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + else + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + } + else // mon may be caught, calculate shakes + { + u8 shakes; + + odds = Sqrt(Sqrt(16711680 / odds)); + odds = 1048560 / odds; + for (shakes = 0; shakes < 4 && Random() < odds; ++shakes); + if (gLastUsedItem == ITEM_MASTER_BALL) + shakes = BALL_3_SHAKES_SUCCESS; // why calculate the shakes before that check? + BtlController_EmitBallThrowAnim(0, shakes); + MarkBattlerForControllerExec(gActiveBattler); + if (shakes == BALL_3_SHAKES_SUCCESS) // mon caught, copy of the code above + { + gBattlescriptCurrInstr = BattleScript_SuccessBallThrow; + SetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]], MON_DATA_POKEBALL, &gLastUsedItem); + if (CalculatePlayerPartyCount() == 6) + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + else + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + } + else // not caught + { + gBattleCommunication[MULTISTRING_CHOOSER] = shakes; + gBattlescriptCurrInstr = BattleScript_ShakeBallThrow; + } + } + } + } +} + +static void atkF0_givecaughtmon(void) +{ + if (GiveMonToPlayer(&gEnemyParty[gBattlerPartyIndexes[gBattlerAttacker ^ BIT_SIDE]]) != MON_GIVEN_TO_PARTY) + { + if (!sub_80CC7B4()) + { + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + StringCopy(gStringVar1, GetBoxNamePtr(VarGet(VAR_0x4037))); + GetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerAttacker ^ BIT_SIDE]], MON_DATA_NICKNAME, gStringVar2); + } + else + { + StringCopy(gStringVar1, GetBoxNamePtr(VarGet(VAR_0x4037))); + GetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerAttacker ^ BIT_SIDE]], MON_DATA_NICKNAME, gStringVar2); + StringCopy(gStringVar3, GetBoxNamePtr(get_unknown_box_id())); + gBattleCommunication[MULTISTRING_CHOOSER] = 2; + } + if (FlagGet(FLAG_SYS_NOT_SOMEONES_PC)) + ++gBattleCommunication[MULTISTRING_CHOOSER]; + } + gBattleResults.caughtMonSpecies = gBattleMons[gBattlerAttacker ^ BIT_SIDE].species; + GetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerAttacker ^ BIT_SIDE]], MON_DATA_NICKNAME, gBattleResults.caughtMonNick); + ++gBattlescriptCurrInstr; +} + +static void atkF1_trysetcaughtmondexflags(void) +{ + u16 species = GetMonData(&gEnemyParty[0], MON_DATA_SPECIES, NULL); + u32 personality = GetMonData(&gEnemyParty[0], MON_DATA_PERSONALITY, NULL); + + if (GetSetPokedexFlag(SpeciesToNationalPokedexNum(species), FLAG_GET_CAUGHT)) + { + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } + else + { + HandleSetPokedexFlag(SpeciesToNationalPokedexNum(species), FLAG_SET_CAUGHT, personality); + gBattlescriptCurrInstr += 5; + } +} + +static void atkF2_displaydexinfo(void) +{ + u16 species = GetMonData(&gEnemyParty[0], MON_DATA_SPECIES, NULL); + + switch (gBattleCommunication[0]) + { + case 0: + BeginNormalPaletteFade(0xFFFFFFFF, 0, 0, 0x10, RGB_WHITE); + ++gBattleCommunication[0]; + break; + case 1: + if (!gPaletteFade.active) + { + FreeAllWindowBuffers(); + gBattleCommunication[TASK_ID] = sub_8106B60(species); + ++gBattleCommunication[0]; + } + break; + case 2: + if (!gPaletteFade.active + && gMain.callback2 == BattleMainCB2 + && !gTasks[gBattleCommunication[TASK_ID]].isActive) + { + CpuFill32(0, (void *)VRAM, VRAM_SIZE); + SetVBlankCallback(VBlankCB_Battle); + ++gBattleCommunication[0]; + } + break; + case 3: + sub_800F34C(); + LoadBattleTextboxAndBackground(); + gBattle_BG3_X = 0x100; + ++gBattleCommunication[0]; + break; + case 4: + if (!IsDma3ManagerBusyWithBgCopy()) + { + CreateMonPicSprite_HandleDeoxys(species, + gBattleMons[B_POSITION_OPPONENT_LEFT].otId, + gBattleMons[B_POSITION_OPPONENT_LEFT].personality, + TRUE, + 120, + 64, + 0, + 0xFFFF); + CpuFill32(0, gPlttBufferFaded, BG_PLTT_SIZE); + BeginNormalPaletteFade(0x1FFFF, 0, 0x10, 0, RGB_BLACK); + ShowBg(0); + ShowBg(3); + ++gBattleCommunication[0]; + } + break; + case 5: + if (!gPaletteFade.active) + ++gBattlescriptCurrInstr; + break; + } +} + +void HandleBattleWindow(u8 xStart, u8 yStart, u8 xEnd, u8 yEnd, u8 flags) +{ + s32 destY, destX; + u16 var = 0; + + for (destY = yStart; destY <= yEnd; ++destY) + { + for (destX = xStart; destX <= xEnd; ++destX) + { + if (destY == yStart) + { + if (destX == xStart) + var = 0x1022; + else if (destX == xEnd) + var = 0x1024; + else + var = 0x1023; + } + else if (destY == yEnd) + { + if (destX == xStart) + var = 0x1028; + else if (destX == xEnd) + var = 0x102A; + else + var = 0x1029; + } + else + { + if (destX == xStart) + var = 0x1025; + else if (destX == xEnd) + var = 0x1027; + else + var = 0x1026; + } + if (flags & WINDOW_CLEAR) + var = 0; + if (flags & WINDOW_x80) + CopyToBgTilemapBufferRect_ChangePalette(1, &var, destX, destY, 1, 1, 0x11); + else + CopyToBgTilemapBufferRect_ChangePalette(0, &var, destX, destY, 1, 1, 0x11); + } + } + CopyBgTilemapBufferToVram(1); +} + +void BattleCreateYesNoCursorAt(void) +{ + u16 src[2]; + + src[0] = 1; + src[1] = 2; + CopyToBgTilemapBufferRect_ChangePalette(0, src, 0x18, 9 + (2 * gBattleCommunication[1]), 1, 2, 0x11); + CopyBgTilemapBufferToVram(0); +} + +void BattleDestroyYesNoCursorAt(void) +{ + u16 src[2]; + + src[0] = 32; + src[1] = 32; + CopyToBgTilemapBufferRect_ChangePalette(0, src, 0x18, 9 + (2 * gBattleCommunication[1]), 1, 2, 0x11); + CopyBgTilemapBufferToVram(0); +} + +static void atkF3_trygivecaughtmonnick(void) +{ + switch (gBattleCommunication[MULTIUSE_STATE]) + { + case 0: + HandleBattleWindow(0x17, 8, 0x1D, 0xD, 0); + BattlePutTextOnWindow(gText_BattleYesNoChoice, 0xE); + ++gBattleCommunication[MULTIUSE_STATE]; + gBattleCommunication[CURSOR_POSITION] = 0; + BattleCreateYesNoCursorAt(); + break; + case 1: + if (JOY_NEW(DPAD_UP) && gBattleCommunication[CURSOR_POSITION] != 0) + { + PlaySE(SE_SELECT); + BattleDestroyYesNoCursorAt(); + gBattleCommunication[CURSOR_POSITION] = 0; + BattleCreateYesNoCursorAt(); + } + if (JOY_NEW(DPAD_DOWN) && gBattleCommunication[CURSOR_POSITION] == 0) + { + PlaySE(SE_SELECT); + BattleDestroyYesNoCursorAt(); + gBattleCommunication[CURSOR_POSITION] = 1; + BattleCreateYesNoCursorAt(); + } + if (JOY_NEW(A_BUTTON)) + { + PlaySE(SE_SELECT); + if (gBattleCommunication[CURSOR_POSITION] == 0) + { + ++gBattleCommunication[MULTIUSE_STATE]; + BeginFastPaletteFade(3); + } + else + { + gBattleCommunication[MULTIUSE_STATE] = 4; + } + } + else if (JOY_NEW(B_BUTTON)) + { + PlaySE(SE_SELECT); + gBattleCommunication[MULTIUSE_STATE] = 4; + } + break; + case 2: + if (!gPaletteFade.active) + { + GetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerAttacker ^ BIT_SIDE]], MON_DATA_NICKNAME, gBattleStruct->caughtMonNick); + FreeAllWindowBuffers(); + DoNamingScreen(NAMING_SCREEN_CAUGHT_MON, gBattleStruct->caughtMonNick, + GetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerAttacker ^ BIT_SIDE]], MON_DATA_SPECIES), + GetMonGender(&gEnemyParty[gBattlerPartyIndexes[gBattlerAttacker ^ BIT_SIDE]]), + GetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerAttacker ^ BIT_SIDE]], MON_DATA_PERSONALITY, NULL), + BattleMainCB2); + ++gBattleCommunication[MULTIUSE_STATE]; + } + break; + case 3: + if (gMain.callback2 == BattleMainCB2 && !gPaletteFade.active) + { + SetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerAttacker ^ BIT_SIDE]], MON_DATA_NICKNAME, gBattleStruct->caughtMonNick); + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + } + break; + case 4: + if (CalculatePlayerPartyCount() == PARTY_SIZE) + gBattlescriptCurrInstr += 5; + else + gBattlescriptCurrInstr = T1_READ_PTR(gBattlescriptCurrInstr + 1); + break; + } +} + +static void atkF4_subattackerhpbydmg(void) +{ + gBattleMons[gBattlerAttacker].hp -= gBattleMoveDamage; + ++gBattlescriptCurrInstr; +} + +static void atkF5_removeattackerstatus1(void) +{ + gBattleMons[gBattlerAttacker].status1 = 0; + ++gBattlescriptCurrInstr; +} + +static void atkF6_finishaction(void) +{ + gCurrentActionFuncId = B_ACTION_FINISHED; +} + +static void atkF7_finishturn(void) +{ + gCurrentActionFuncId = B_ACTION_FINISHED; + gCurrentTurnActionNumber = gBattlersCount; +} diff --git a/src/battle_util.c b/src/battle_util.c new file mode 100644 index 000000000..b8360758c --- /dev/null +++ b/src/battle_util.c @@ -0,0 +1,3200 @@ +#include "global.h" +#include "item.h" +#include "text.h" +#include "util.h" +#include "link.h" +#include "berry.h" +#include "random.h" +#include "pokemon.h" +#include "string_util.h" +#include "field_weather.h" +#include "event_data.h" +#include "battle.h" +#include "battle_anim.h" +#include "battle_scripts.h" +#include "battle_message.h" +#include "constants/battle_anim.h" +#include "battle_controllers.h" +#include "battle_string_ids.h" +#include "battle_ai_script_commands.h" +#include "constants/battle.h" +#include "constants/moves.h" +#include "constants/items.h" +#include "constants/flags.h" +#include "constants/species.h" +#include "constants/weather.h" +#include "constants/abilities.h" +#include "constants/pokemon.h" +#include "constants/hold_effects.h" +#include "constants/battle_move_effects.h" +#include "constants/battle_script_commands.h" + +static const u16 sSoundMovesTable[] = +{ + MOVE_GROWL, MOVE_ROAR, MOVE_SING, MOVE_SUPERSONIC, MOVE_SCREECH, MOVE_SNORE, + MOVE_UPROAR, MOVE_METAL_SOUND, MOVE_GRASS_WHISTLE, MOVE_HYPER_VOICE, 0xFFFF +}; + +u8 GetBattlerForBattleScript(u8 caseId) +{ + u32 ret = 0; + + switch (caseId) + { + case BS_TARGET: + ret = gBattlerTarget; + break; + case BS_ATTACKER: + ret = gBattlerAttacker; + break; + case BS_EFFECT_BATTLER: + ret = gEffectBattler; + break; + case BS_BATTLER_0: + ret = 0; + break; + case BS_SCRIPTING: + ret = gBattleScripting.battler; + break; + case BS_FAINTED: + ret = gBattlerFainted; + break; + case 5: + ret = gBattlerFainted; + break; + case BS_PLAYER1: + ret = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); + break; + case BS_OPPONENT1: + ret = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); + break; + case 4: + case 6: + case 8: + case 9: + break; + } + return ret; +} + +void PressurePPLose(u8 target, u8 attacker, u16 move) +{ + s32 i; + + if (gBattleMons[target].ability == ABILITY_PRESSURE) + { + for (i = 0; i < MAX_MON_MOVES && gBattleMons[attacker].moves[i] != move; ++i); + if (i != MAX_MON_MOVES) + { + if (gBattleMons[attacker].pp[i]) + --gBattleMons[attacker].pp[i]; + if (!(gBattleMons[attacker].status2 & STATUS2_TRANSFORMED) + && !(gDisableStructs[attacker].mimickedMoves & gBitTable[i])) + { + gActiveBattler = attacker; + BtlController_EmitSetMonData(0, REQUEST_PPMOVE1_BATTLE + i, 0, 1, &gBattleMons[gActiveBattler].pp[i]); + MarkBattlerForControllerExec(gActiveBattler); + } + } + } +} + +void PressurePPLoseOnUsingImprison(u8 attacker) +{ + s32 i, j; + s32 imprisonPos = 4; + u8 atkSide = GetBattlerSide(attacker); + + for (i = 0; i < gBattlersCount; ++i) + { + if (atkSide != GetBattlerSide(i) && gBattleMons[i].ability == ABILITY_PRESSURE) + { + for (j = 0; j < MAX_MON_MOVES && gBattleMons[attacker].moves[j] != MOVE_IMPRISON; ++j); + if (j != MAX_MON_MOVES) + { + imprisonPos = j; + if (gBattleMons[attacker].pp[j]) + --gBattleMons[attacker].pp[j]; + } + } + } + if (imprisonPos != 4 + && !(gBattleMons[attacker].status2 & STATUS2_TRANSFORMED) + && !(gDisableStructs[attacker].mimickedMoves & gBitTable[imprisonPos])) + { + gActiveBattler = attacker; + BtlController_EmitSetMonData(0, REQUEST_PPMOVE1_BATTLE + imprisonPos, 0, 1, &gBattleMons[gActiveBattler].pp[imprisonPos]); + MarkBattlerForControllerExec(gActiveBattler); + } +} + +void PressurePPLoseOnUsingPerishSong(u8 attacker) +{ + s32 i, j; + s32 perishSongPos = 4; + + for (i = 0; i < gBattlersCount; ++i) + { + if (gBattleMons[i].ability == ABILITY_PRESSURE && i != attacker) + { + for (j = 0; j < MAX_MON_MOVES && gBattleMons[attacker].moves[j] != MOVE_PERISH_SONG; ++j); + if (j != MAX_MON_MOVES) + { + perishSongPos = j; + if (gBattleMons[attacker].pp[j]) + --gBattleMons[attacker].pp[j]; + } + } + } + if (perishSongPos != MAX_MON_MOVES + && !(gBattleMons[attacker].status2 & STATUS2_TRANSFORMED) + && !(gDisableStructs[attacker].mimickedMoves & gBitTable[perishSongPos])) + { + gActiveBattler = attacker; + BtlController_EmitSetMonData(0, REQUEST_PPMOVE1_BATTLE + perishSongPos, 0, 1, &gBattleMons[gActiveBattler].pp[perishSongPos]); + MarkBattlerForControllerExec(gActiveBattler); + } +} + +void MarkAllBattlersForControllerExec(void) +{ + s32 i; + + if (gBattleTypeFlags & BATTLE_TYPE_LINK) + for (i = 0; i < gBattlersCount; ++i) + gBattleControllerExecFlags |= gBitTable[i] << 0x1C; + else + for (i = 0; i < gBattlersCount; ++i) + gBattleControllerExecFlags |= gBitTable[i]; +} + +void MarkBattlerForControllerExec(u8 battlerId) +{ + if (gBattleTypeFlags & BATTLE_TYPE_LINK) + gBattleControllerExecFlags |= gBitTable[battlerId] << 0x1C; + else + gBattleControllerExecFlags |= gBitTable[battlerId]; +} + +void sub_8017298(u8 arg0) +{ + s32 i; + + for (i = 0; i < GetLinkPlayerCount(); ++i) + gBattleControllerExecFlags |= gBitTable[arg0] << (i << 2); + gBattleControllerExecFlags &= ~(0x10000000 << arg0); +} + +void CancelMultiTurnMoves(u8 battler) +{ + gBattleMons[battler].status2 &= ~(STATUS2_MULTIPLETURNS); + gBattleMons[battler].status2 &= ~(STATUS2_LOCK_CONFUSE); + gBattleMons[battler].status2 &= ~(STATUS2_UPROAR); + gBattleMons[battler].status2 &= ~(STATUS2_BIDE); + gStatuses3[battler] &= ~(STATUS3_SEMI_INVULNERABLE); + gDisableStructs[battler].rolloutTimer = 0; + gDisableStructs[battler].furyCutterCounter = 0; +} + +bool8 WasUnableToUseMove(u8 battler) +{ + if (gProtectStructs[battler].prlzImmobility + || gProtectStructs[battler].targetNotAffected + || gProtectStructs[battler].usedImprisonedMove + || gProtectStructs[battler].loveImmobility + || gProtectStructs[battler].usedDisabledMove + || gProtectStructs[battler].usedTauntedMove + || gProtectStructs[battler].flag2Unknown + || gProtectStructs[battler].flinchImmobility + || gProtectStructs[battler].confusionSelfDmg) + return TRUE; + else + return FALSE; +} + +void PrepareStringBattle(u16 stringId, u8 battler) +{ + gActiveBattler = battler; + BtlController_EmitPrintString(0, stringId); + MarkBattlerForControllerExec(gActiveBattler); +} + +void ResetSentPokesToOpponentValue(void) +{ + s32 i; + u32 bits = 0; + + gSentPokesToOpponent[0] = 0; + gSentPokesToOpponent[1] = 0; + for (i = 0; i < gBattlersCount; i += 2) + bits |= gBitTable[gBattlerPartyIndexes[i]]; + for (i = 1; i < gBattlersCount; i += 2) + gSentPokesToOpponent[(i & BIT_FLANK) >> 1] = bits; +} + +void sub_8017434(u8 battler) +{ + s32 i = 0; + u32 bits = 0; + + if (GetBattlerSide(battler) == B_SIDE_OPPONENT) + { + u8 flank = ((battler & BIT_FLANK) >> 1); + + gSentPokesToOpponent[flank] = 0; + for (i = 0; i < gBattlersCount; i += 2) + if (!(gAbsentBattlerFlags & gBitTable[i])) + bits |= gBitTable[gBattlerPartyIndexes[i]]; + gSentPokesToOpponent[flank] = bits; + } +} + +void sub_80174B8(u8 battler) +{ + if (GetBattlerSide(battler) == B_SIDE_OPPONENT) + { + sub_8017434(battler); + } + else + { + s32 i; + + for (i = 1; i < gBattlersCount; ++i) + gSentPokesToOpponent[(i & BIT_FLANK) >> 1] |= gBitTable[gBattlerPartyIndexes[battler]]; + } +} + +void BattleScriptPush(const u8 *bsPtr) +{ + gBattleResources->battleScriptsStack->ptr[gBattleResources->battleScriptsStack->size++] = bsPtr; +} + +void BattleScriptPushCursor(void) +{ + gBattleResources->battleScriptsStack->ptr[gBattleResources->battleScriptsStack->size++] = gBattlescriptCurrInstr; +} + +void BattleScriptPop(void) +{ + gBattlescriptCurrInstr = gBattleResources->battleScriptsStack->ptr[--gBattleResources->battleScriptsStack->size]; +} + +u8 TrySetCantSelectMoveBattleScript(void) +{ + u8 holdEffect; + u8 limitations = 0; + u16 move = gBattleMons[gActiveBattler].moves[gBattleBufferB[gActiveBattler][2]]; + u16 *choicedMove = &gBattleStruct->choicedMove[gActiveBattler]; + + if (gDisableStructs[gActiveBattler].disabledMove == move && move != MOVE_NONE) + { + gBattleScripting.battler = gActiveBattler; + gCurrentMove = move; + gSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingDisabledMove; + limitations = 1; + } + if (move == gLastMoves[gActiveBattler] && move != MOVE_STRUGGLE && (gBattleMons[gActiveBattler].status2 & STATUS2_TORMENT)) + { + CancelMultiTurnMoves(gActiveBattler); + gSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingTormentedMove; + ++limitations; + } + if (gDisableStructs[gActiveBattler].tauntTimer && !gBattleMoves[move].power) + { + gCurrentMove = move; + gSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingNotAllowedMoveTaunt; + ++limitations; + } + if (GetImprisonedMovesCount(gActiveBattler, move)) + { + gCurrentMove = move; + gSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingImprisonedMove; + ++limitations; + } + if (gBattleMons[gActiveBattler].item == ITEM_ENIGMA_BERRY) + holdEffect = gEnigmaBerries[gActiveBattler].holdEffect; + else + holdEffect = ItemId_GetHoldEffect(gBattleMons[gActiveBattler].item); + gPotentialItemEffectBattler = gActiveBattler; + if (holdEffect == HOLD_EFFECT_CHOICE_BAND && *choicedMove && *choicedMove != 0xFFFF && *choicedMove != move) + { + gCurrentMove = *choicedMove; + gLastUsedItem = gBattleMons[gActiveBattler].item; + gSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingNotAllowedMoveChoiceItem; + ++limitations; + } + if (!gBattleMons[gActiveBattler].pp[gBattleBufferB[gActiveBattler][2]]) + { + gSelectionBattleScripts[gActiveBattler] = BattleScript_SelectingMoveWithNoPP; + ++limitations; + } + return limitations; +} + +u8 CheckMoveLimitations(u8 battlerId, u8 unusableMoves, u8 check) +{ + u8 holdEffect; + u16 *choicedMove = &gBattleStruct->choicedMove[battlerId]; + s32 i; + + if (gBattleMons[battlerId].item == ITEM_ENIGMA_BERRY) + holdEffect = gEnigmaBerries[battlerId].holdEffect; + else + holdEffect = ItemId_GetHoldEffect(gBattleMons[battlerId].item); + gPotentialItemEffectBattler = battlerId; + + for (i = 0; i < MAX_MON_MOVES; ++i) + { + if (gBattleMons[battlerId].moves[i] == 0 && check & MOVE_LIMITATION_ZEROMOVE) + unusableMoves |= gBitTable[i]; + if (gBattleMons[battlerId].pp[i] == 0 && check & MOVE_LIMITATION_PP) + unusableMoves |= gBitTable[i]; + if (gBattleMons[battlerId].moves[i] == gDisableStructs[battlerId].disabledMove && check & MOVE_LIMITATION_DISABLED) + unusableMoves |= gBitTable[i]; + if (gBattleMons[battlerId].moves[i] == gLastMoves[battlerId] && check & MOVE_LIMITATION_TORMENTED && gBattleMons[battlerId].status2 & STATUS2_TORMENT) + unusableMoves |= gBitTable[i]; + if (gDisableStructs[battlerId].tauntTimer && check & MOVE_LIMITATION_TAUNT && gBattleMoves[gBattleMons[battlerId].moves[i]].power == 0) + unusableMoves |= gBitTable[i]; + if (GetImprisonedMovesCount(battlerId, gBattleMons[battlerId].moves[i]) && check & MOVE_LIMITATION_IMPRISON) + unusableMoves |= gBitTable[i]; + if (gDisableStructs[battlerId].encoreTimer && gDisableStructs[battlerId].encoredMove != gBattleMons[battlerId].moves[i]) + unusableMoves |= gBitTable[i]; + if (holdEffect == HOLD_EFFECT_CHOICE_BAND && *choicedMove != 0 && *choicedMove != 0xFFFF && *choicedMove != gBattleMons[battlerId].moves[i]) + unusableMoves |= gBitTable[i]; + } + return unusableMoves; +} + +bool8 AreAllMovesUnusable(void) +{ + u8 unusable = CheckMoveLimitations(gActiveBattler, 0, 0xFF); + + if (unusable == 0xF) // All moves are unusable. + { + gProtectStructs[gActiveBattler].noValidMoves = 1; + gSelectionBattleScripts[gActiveBattler] = BattleScript_NoMovesLeft; + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + gBattleBufferB[gActiveBattler][3] = GetBattlerAtPosition((BATTLE_OPPOSITE(GetBattlerPosition(gActiveBattler))) | (Random() & 2)); + else + gBattleBufferB[gActiveBattler][3] = GetBattlerAtPosition(BATTLE_OPPOSITE(GetBattlerPosition(gActiveBattler))); + } + else + { + gProtectStructs[gActiveBattler].noValidMoves = 0; + } + return (unusable == 0xF); +} + +u8 GetImprisonedMovesCount(u8 battlerId, u16 move) +{ + s32 i; + u8 imprisonedMoves = 0; + u8 battlerSide = GetBattlerSide(battlerId); + + for (i = 0; i < gBattlersCount; ++i) + { + if (battlerSide != GetBattlerSide(i) && gStatuses3[i] & STATUS3_IMPRISONED_OTHERS) + { + s32 j; + + for (j = 0; j < MAX_MON_MOVES && move != gBattleMons[i].moves[j]; ++j); + if (j < MAX_MON_MOVES) + ++imprisonedMoves; + } + } + return imprisonedMoves; +} + +enum +{ + ENDTURN_ORDER, + ENDTURN_REFLECT, + ENDTURN_LIGHT_SCREEN, + ENDTURN_MIST, + ENDTURN_SAFEGUARD, + ENDTURN_WISH, + ENDTURN_RAIN, + ENDTURN_SANDSTORM, + ENDTURN_SUN, + ENDTURN_HAIL, + ENDTURN_FIELD_COUNT, +}; + +u8 DoFieldEndTurnEffects(void) +{ + u8 effect = 0; + s32 i; + + for (gBattlerAttacker = 0; gBattlerAttacker < gBattlersCount && gAbsentBattlerFlags & gBitTable[gBattlerAttacker]; ++gBattlerAttacker); + for (gBattlerTarget = 0; gBattlerTarget < gBattlersCount && gAbsentBattlerFlags & gBitTable[gBattlerTarget]; ++gBattlerTarget); + do + { + u8 side; + + switch (gBattleStruct->turnCountersTracker) + { + case ENDTURN_ORDER: + for (i = 0; i < gBattlersCount; ++i) + gBattlerByTurnOrder[i] = i; + for (i = 0; i < gBattlersCount - 1; ++i) + { + s32 j; + + for (j = i + 1; j < gBattlersCount; ++j) + if (GetWhoStrikesFirst(gBattlerByTurnOrder[i], gBattlerByTurnOrder[j], 0)) + SwapTurnOrder(i, j); + } + { + u8 *var = &gBattleStruct->turnCountersTracker; + + ++*var; + gBattleStruct->turnSideTracker = 0; + } + // fall through + case ENDTURN_REFLECT: + while (gBattleStruct->turnSideTracker < 2) + { + side = gBattleStruct->turnSideTracker; + gActiveBattler = gBattlerAttacker = gSideTimers[side].reflectBattlerId; + if (gSideStatuses[side] & SIDE_STATUS_REFLECT) + { + if (--gSideTimers[side].reflectTimer == 0) + { + gSideStatuses[side] &= ~SIDE_STATUS_REFLECT; + BattleScriptExecute(BattleScript_SideStatusWoreOff); + PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_REFLECT); + ++effect; + } + } + ++gBattleStruct->turnSideTracker; + if (effect) + break; + } + if (!effect) + { + ++gBattleStruct->turnCountersTracker; + gBattleStruct->turnSideTracker = 0; + } + break; + case ENDTURN_LIGHT_SCREEN: + while (gBattleStruct->turnSideTracker < 2) + { + side = gBattleStruct->turnSideTracker; + gActiveBattler = gBattlerAttacker = gSideTimers[side].lightscreenBattlerId; + if (gSideStatuses[side] & SIDE_STATUS_LIGHTSCREEN) + { + if (--gSideTimers[side].lightscreenTimer == 0) + { + gSideStatuses[side] &= ~SIDE_STATUS_LIGHTSCREEN; + BattleScriptExecute(BattleScript_SideStatusWoreOff); + gBattleCommunication[MULTISTRING_CHOOSER] = side; + PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_LIGHT_SCREEN); + ++effect; + } + } + ++gBattleStruct->turnSideTracker; + if (effect) + break; + } + if (!effect) + { + ++gBattleStruct->turnCountersTracker; + gBattleStruct->turnSideTracker = 0; + } + break; + case ENDTURN_MIST: + while (gBattleStruct->turnSideTracker < 2) + { + side = gBattleStruct->turnSideTracker; + gActiveBattler = gBattlerAttacker = gSideTimers[side].mistBattlerId; + if (gSideTimers[side].mistTimer != 0 && --gSideTimers[side].mistTimer == 0) + { + gSideStatuses[side] &= ~SIDE_STATUS_MIST; + BattleScriptExecute(BattleScript_SideStatusWoreOff); + gBattleCommunication[MULTISTRING_CHOOSER] = side; + PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_MIST); + ++effect; + } + ++gBattleStruct->turnSideTracker; + if (effect) + break; + } + if (!effect) + { + ++gBattleStruct->turnCountersTracker; + gBattleStruct->turnSideTracker = 0; + } + break; + case ENDTURN_SAFEGUARD: + while (gBattleStruct->turnSideTracker < 2) + { + side = gBattleStruct->turnSideTracker; + gActiveBattler = gBattlerAttacker = gSideTimers[side].safeguardBattlerId; + if (gSideStatuses[side] & SIDE_STATUS_SAFEGUARD) + { + if (--gSideTimers[side].safeguardTimer == 0) + { + gSideStatuses[side] &= ~SIDE_STATUS_SAFEGUARD; + BattleScriptExecute(BattleScript_SafeguardEnds); + ++effect; + } + } + ++gBattleStruct->turnSideTracker; + if (effect) + break; + } + if (!effect) + { + ++gBattleStruct->turnCountersTracker; + gBattleStruct->turnSideTracker = 0; + } + break; + case ENDTURN_WISH: + while (gBattleStruct->turnSideTracker < gBattlersCount) + { + gActiveBattler = gBattlerByTurnOrder[gBattleStruct->turnSideTracker]; + if (gWishFutureKnock.wishCounter[gActiveBattler] != 0 + && --gWishFutureKnock.wishCounter[gActiveBattler] == 0 + && gBattleMons[gActiveBattler].hp != 0) + { + gBattlerTarget = gActiveBattler; + BattleScriptExecute(BattleScript_WishComesTrue); + ++effect; + } + ++gBattleStruct->turnSideTracker; + if (effect) + break; + } + if (!effect) + ++gBattleStruct->turnCountersTracker; + break; + case ENDTURN_RAIN: + if (gBattleWeather & WEATHER_RAIN_ANY) + { + if (!(gBattleWeather & WEATHER_RAIN_PERMANENT)) + { + if (--gWishFutureKnock.weatherDuration == 0) + { + gBattleWeather &= ~WEATHER_RAIN_TEMPORARY; + gBattleWeather &= ~WEATHER_RAIN_DOWNPOUR; + gBattleCommunication[MULTISTRING_CHOOSER] = 2; + } + else if (gBattleWeather & WEATHER_RAIN_DOWNPOUR) + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + else + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + } + else if (gBattleWeather & WEATHER_RAIN_DOWNPOUR) + { + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + } + else + { + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + } + BattleScriptExecute(BattleScript_RainContinuesOrEnds); + ++effect; + } + ++gBattleStruct->turnCountersTracker; + break; + case ENDTURN_SANDSTORM: + if (gBattleWeather & WEATHER_SANDSTORM_ANY) + { + if (!(gBattleWeather & WEATHER_SANDSTORM_PERMANENT) && --gWishFutureKnock.weatherDuration == 0) + { + gBattleWeather &= ~WEATHER_SANDSTORM_TEMPORARY; + gBattlescriptCurrInstr = BattleScript_SandStormHailEnds; + } + else + { + gBattlescriptCurrInstr = BattleScript_DamagingWeatherContinues; + } + gBattleScripting.animArg1 = B_ANIM_SANDSTORM_CONTINUES; + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + BattleScriptExecute(gBattlescriptCurrInstr); + ++effect; + } + ++gBattleStruct->turnCountersTracker; + break; + case ENDTURN_SUN: + if (gBattleWeather & WEATHER_SUN_ANY) + { + if (!(gBattleWeather & WEATHER_SUN_PERMANENT) && --gWishFutureKnock.weatherDuration == 0) + { + gBattleWeather &= ~WEATHER_SUN_TEMPORARY; + gBattlescriptCurrInstr = BattleScript_SunlightFaded; + } + else + { + gBattlescriptCurrInstr = BattleScript_SunlightContinues; + } + BattleScriptExecute(gBattlescriptCurrInstr); + ++effect; + } + ++gBattleStruct->turnCountersTracker; + break; + case ENDTURN_HAIL: + if (gBattleWeather & WEATHER_HAIL_ANY) + { + if (--gWishFutureKnock.weatherDuration == 0) + { + gBattleWeather &= ~WEATHER_HAIL; + gBattlescriptCurrInstr = BattleScript_SandStormHailEnds; + } + else + { + gBattlescriptCurrInstr = BattleScript_DamagingWeatherContinues; + } + gBattleScripting.animArg1 = B_ANIM_HAIL_CONTINUES; + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + BattleScriptExecute(gBattlescriptCurrInstr); + ++effect; + } + ++gBattleStruct->turnCountersTracker; + break; + case ENDTURN_FIELD_COUNT: + ++effect; + break; + } + } while (!effect); + return (gBattleMainFunc != BattleTurnPassed); +} + +enum +{ + ENDTURN_INGRAIN, + ENDTURN_ABILITIES, + ENDTURN_ITEMS1, + ENDTURN_LEECH_SEED, + ENDTURN_POISON, + ENDTURN_BAD_POISON, + ENDTURN_BURN, + ENDTURN_NIGHTMARES, + ENDTURN_CURSE, + ENDTURN_WRAP, + ENDTURN_UPROAR, + ENDTURN_THRASH, + ENDTURN_DISABLE, + ENDTURN_ENCORE, + ENDTURN_LOCK_ON, + ENDTURN_CHARGE, + ENDTURN_TAUNT, + ENDTURN_YAWN, + ENDTURN_ITEMS2, + ENDTURN_BATTLER_COUNT +}; + +u8 DoBattlerEndTurnEffects(void) +{ + u8 effect = 0; + + gHitMarker |= (HITMARKER_GRUDGE | HITMARKER_x20); + while (gBattleStruct->turnEffectsBattlerId < gBattlersCount && gBattleStruct->turnEffectsTracker <= ENDTURN_BATTLER_COUNT) + { + gActiveBattler = gBattlerAttacker = gBattlerByTurnOrder[gBattleStruct->turnEffectsBattlerId]; + if (gAbsentBattlerFlags & gBitTable[gActiveBattler]) + { + ++gBattleStruct->turnEffectsBattlerId; + } + else + { + switch (gBattleStruct->turnEffectsTracker) + { + case ENDTURN_INGRAIN: // ingrain + if ((gStatuses3[gActiveBattler] & STATUS3_ROOTED) + && gBattleMons[gActiveBattler].hp != gBattleMons[gActiveBattler].maxHP + && gBattleMons[gActiveBattler].hp != 0) + { + gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP / 16; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + gBattleMoveDamage *= -1; + BattleScriptExecute(BattleScript_IngrainTurnHeal); + ++effect; + } + ++gBattleStruct->turnEffectsTracker; + break; + case ENDTURN_ABILITIES: // end turn abilities + if (AbilityBattleEffects(ABILITYEFFECT_ENDTURN, gActiveBattler, 0, 0, 0)) + ++effect; + ++gBattleStruct->turnEffectsTracker; + break; + case ENDTURN_ITEMS1: // item effects + if (ItemBattleEffects(1, gActiveBattler, FALSE)) + ++effect; + ++gBattleStruct->turnEffectsTracker; + break; + case ENDTURN_ITEMS2: // item effects again + if (ItemBattleEffects(1, gActiveBattler, TRUE)) + ++effect; + ++gBattleStruct->turnEffectsTracker; + break; + case ENDTURN_LEECH_SEED: // leech seed + if ((gStatuses3[gActiveBattler] & STATUS3_LEECHSEED) + && gBattleMons[gStatuses3[gActiveBattler] & STATUS3_LEECHSEED_BATTLER].hp != 0 + && gBattleMons[gActiveBattler].hp != 0) + { + gBattlerTarget = gStatuses3[gActiveBattler] & STATUS3_LEECHSEED_BATTLER; // Notice gBattlerTarget is actually the HP receiver. + gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP / 8; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + gBattleScripting.animArg1 = gBattlerTarget; + gBattleScripting.animArg2 = gBattlerAttacker; + BattleScriptExecute(BattleScript_LeechSeedTurnDrain); + ++effect; + } + ++gBattleStruct->turnEffectsTracker; + break; + case ENDTURN_POISON: // poison + if ((gBattleMons[gActiveBattler].status1 & STATUS1_POISON) && gBattleMons[gActiveBattler].hp != 0) + { + gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP / 8; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + BattleScriptExecute(BattleScript_PoisonTurnDmg); + ++effect; + } + ++gBattleStruct->turnEffectsTracker; + break; + case ENDTURN_BAD_POISON: // toxic poison + if ((gBattleMons[gActiveBattler].status1 & STATUS1_TOXIC_POISON) && gBattleMons[gActiveBattler].hp != 0) + { + gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP / 16; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + if ((gBattleMons[gActiveBattler].status1 & 0xF00) != 0xF00) // not 16 turns + gBattleMons[gActiveBattler].status1 += 0x100; + gBattleMoveDamage *= (gBattleMons[gActiveBattler].status1 & 0xF00) >> 8; + BattleScriptExecute(BattleScript_PoisonTurnDmg); + ++effect; + } + ++gBattleStruct->turnEffectsTracker; + break; + case ENDTURN_BURN: // burn + if ((gBattleMons[gActiveBattler].status1 & STATUS1_BURN) && gBattleMons[gActiveBattler].hp != 0) + { + gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP / 8; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + BattleScriptExecute(BattleScript_BurnTurnDmg); + ++effect; + } + ++gBattleStruct->turnEffectsTracker; + break; + case ENDTURN_NIGHTMARES: // spooky nightmares + if ((gBattleMons[gActiveBattler].status2 & STATUS2_NIGHTMARE) && gBattleMons[gActiveBattler].hp != 0) + { + // R/S does not perform this sleep check, which causes the nightmare effect to + // persist even after the affected Pokemon has been awakened by Shed Skin. + if (gBattleMons[gActiveBattler].status1 & STATUS1_SLEEP) + { + gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP / 4; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + BattleScriptExecute(BattleScript_NightmareTurnDmg); + ++effect; + } + else + { + gBattleMons[gActiveBattler].status2 &= ~STATUS2_NIGHTMARE; + } + } + ++gBattleStruct->turnEffectsTracker; + break; + case ENDTURN_CURSE: // curse + if ((gBattleMons[gActiveBattler].status2 & STATUS2_CURSED) && gBattleMons[gActiveBattler].hp != 0) + { + gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP / 4; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + BattleScriptExecute(BattleScript_CurseTurnDmg); + ++effect; + } + ++gBattleStruct->turnEffectsTracker; + break; + case ENDTURN_WRAP: // wrap + if ((gBattleMons[gActiveBattler].status2 & STATUS2_WRAPPED) && gBattleMons[gActiveBattler].hp != 0) + { + gBattleMons[gActiveBattler].status2 -= 0x2000; + if (gBattleMons[gActiveBattler].status2 & STATUS2_WRAPPED) // damaged by wrap + { + gBattleScripting.animArg1 = *(gBattleStruct->wrappedMove + gActiveBattler * 2 + 0); + gBattleScripting.animArg2 = *(gBattleStruct->wrappedMove + gActiveBattler * 2 + 1); + gBattleTextBuff1[0] = B_BUFF_PLACEHOLDER_BEGIN; + gBattleTextBuff1[1] = B_BUFF_MOVE; + gBattleTextBuff1[2] = *(gBattleStruct->wrappedMove + gActiveBattler * 2 + 0); + gBattleTextBuff1[3] = *(gBattleStruct->wrappedMove + gActiveBattler * 2 + 1); + gBattleTextBuff1[4] = EOS; + gBattlescriptCurrInstr = BattleScript_WrapTurnDmg; + gBattleMoveDamage = gBattleMons[gActiveBattler].maxHP / 16; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + } + else // broke free + { + gBattleTextBuff1[0] = B_BUFF_PLACEHOLDER_BEGIN; + gBattleTextBuff1[1] = B_BUFF_MOVE; + gBattleTextBuff1[2] = *(gBattleStruct->wrappedMove + gActiveBattler * 2 + 0); + gBattleTextBuff1[3] = *(gBattleStruct->wrappedMove + gActiveBattler * 2 + 1); + gBattleTextBuff1[4] = EOS; + gBattlescriptCurrInstr = BattleScript_WrapEnds; + } + BattleScriptExecute(gBattlescriptCurrInstr); + ++effect; + } + ++gBattleStruct->turnEffectsTracker; + break; + case ENDTURN_UPROAR: // uproar + if (gBattleMons[gActiveBattler].status2 & STATUS2_UPROAR) + { + for (gBattlerAttacker = 0; gBattlerAttacker < gBattlersCount; ++gBattlerAttacker) + { + if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP) + && gBattleMons[gBattlerAttacker].ability != ABILITY_SOUNDPROOF) + { + gBattleMons[gBattlerAttacker].status1 &= ~(STATUS1_SLEEP); + gBattleMons[gBattlerAttacker].status2 &= ~(STATUS2_NIGHTMARE); + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + BattleScriptExecute(BattleScript_MonWokeUpInUproar); + gActiveBattler = gBattlerAttacker; + BtlController_EmitSetMonData(0, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[gActiveBattler].status1); + MarkBattlerForControllerExec(gActiveBattler); + break; + } + } + if (gBattlerAttacker != gBattlersCount) + { + effect = 2; // a pokemon was awaken + break; + } + else + { + gBattlerAttacker = gActiveBattler; + gBattleMons[gActiveBattler].status2 -= 0x10; // uproar timer goes down + if (WasUnableToUseMove(gActiveBattler)) + { + CancelMultiTurnMoves(gActiveBattler); + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + } + else if (gBattleMons[gActiveBattler].status2 & STATUS2_UPROAR) + { + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + gBattleMons[gActiveBattler].status2 |= STATUS2_MULTIPLETURNS; + } + else + { + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + CancelMultiTurnMoves(gActiveBattler); + } + BattleScriptExecute(BattleScript_PrintUproarOverTurns); + effect = 1; + } + } + if (effect != 2) + ++gBattleStruct->turnEffectsTracker; + break; + case ENDTURN_THRASH: // thrash + if (gBattleMons[gActiveBattler].status2 & STATUS2_LOCK_CONFUSE) + { + gBattleMons[gActiveBattler].status2 -= 0x400; + if (WasUnableToUseMove(gActiveBattler)) + CancelMultiTurnMoves(gActiveBattler); + else if (!(gBattleMons[gActiveBattler].status2 & STATUS2_LOCK_CONFUSE) + && (gBattleMons[gActiveBattler].status2 & STATUS2_MULTIPLETURNS)) + { + gBattleMons[gActiveBattler].status2 &= ~(STATUS2_MULTIPLETURNS); + if (!(gBattleMons[gActiveBattler].status2 & STATUS2_CONFUSION)) + { + gBattleCommunication[MOVE_EFFECT_BYTE] = MOVE_EFFECT_CONFUSION | MOVE_EFFECT_AFFECTS_USER; + SetMoveEffect(1, 0); + if (gBattleMons[gActiveBattler].status2 & STATUS2_CONFUSION) + BattleScriptExecute(BattleScript_ThrashConfuses); + ++effect; + } + } + } + ++gBattleStruct->turnEffectsTracker; + break; + case ENDTURN_DISABLE: // disable + if (gDisableStructs[gActiveBattler].disableTimer != 0) + { + s32 i; + + for (i = 0; i < MAX_MON_MOVES; ++i) + { + if (gDisableStructs[gActiveBattler].disabledMove == gBattleMons[gActiveBattler].moves[i]) + break; + } + if (i == MAX_MON_MOVES) // pokemon does not have the disabled move anymore + { + gDisableStructs[gActiveBattler].disabledMove = MOVE_NONE; + gDisableStructs[gActiveBattler].disableTimer = 0; + } + else if (--gDisableStructs[gActiveBattler].disableTimer == 0) // disable ends + { + gDisableStructs[gActiveBattler].disabledMove = MOVE_NONE; + BattleScriptExecute(BattleScript_DisabledNoMore); + ++effect; + } + } + ++gBattleStruct->turnEffectsTracker; + break; + case ENDTURN_ENCORE: // encore + if (gDisableStructs[gActiveBattler].encoreTimer != 0) + { + if (gBattleMons[gActiveBattler].moves[gDisableStructs[gActiveBattler].encoredMovePos] != gDisableStructs[gActiveBattler].encoredMove) // pokemon does not have the encored move anymore + { + gDisableStructs[gActiveBattler].encoredMove = MOVE_NONE; + gDisableStructs[gActiveBattler].encoreTimer = 0; + } + else if (--gDisableStructs[gActiveBattler].encoreTimer == 0 + || gBattleMons[gActiveBattler].pp[gDisableStructs[gActiveBattler].encoredMovePos] == 0) + { + gDisableStructs[gActiveBattler].encoredMove = MOVE_NONE; + gDisableStructs[gActiveBattler].encoreTimer = 0; + BattleScriptExecute(BattleScript_EncoredNoMore); + ++effect; + } + } + ++gBattleStruct->turnEffectsTracker; + break; + case ENDTURN_LOCK_ON: // lock-on decrement + if (gStatuses3[gActiveBattler] & STATUS3_ALWAYS_HITS) + gStatuses3[gActiveBattler] -= 0x8; + ++gBattleStruct->turnEffectsTracker; + break; + case ENDTURN_CHARGE: // charge + if (gDisableStructs[gActiveBattler].chargeTimer && --gDisableStructs[gActiveBattler].chargeTimer == 0) + gStatuses3[gActiveBattler] &= ~STATUS3_CHARGED_UP; + ++gBattleStruct->turnEffectsTracker; + break; + case ENDTURN_TAUNT: // taunt + if (gDisableStructs[gActiveBattler].tauntTimer) + --gDisableStructs[gActiveBattler].tauntTimer; + ++gBattleStruct->turnEffectsTracker; + break; + case ENDTURN_YAWN: // yawn + if (gStatuses3[gActiveBattler] & STATUS3_YAWN) + { + gStatuses3[gActiveBattler] -= 0x800; + if (!(gStatuses3[gActiveBattler] & STATUS3_YAWN) && !(gBattleMons[gActiveBattler].status1 & STATUS1_ANY) + && gBattleMons[gActiveBattler].ability != ABILITY_VITAL_SPIRIT + && gBattleMons[gActiveBattler].ability != ABILITY_INSOMNIA && !UproarWakeUpCheck(gActiveBattler)) + { + CancelMultiTurnMoves(gActiveBattler); + gBattleMons[gActiveBattler].status1 |= (Random() & 3) + 2; + BtlController_EmitSetMonData(0, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[gActiveBattler].status1); + MarkBattlerForControllerExec(gActiveBattler); + gEffectBattler = gActiveBattler; + BattleScriptExecute(BattleScript_YawnMakesAsleep); + ++effect; + } + } + ++gBattleStruct->turnEffectsTracker; + break; + case ENDTURN_BATTLER_COUNT: // done + gBattleStruct->turnEffectsTracker = 0; + ++gBattleStruct->turnEffectsBattlerId; + break; + } + if (effect) + return effect; + } + } + gHitMarker &= ~(HITMARKER_GRUDGE | HITMARKER_x20); + return 0; +} + +bool8 HandleWishPerishSongOnTurnEnd(void) +{ + gHitMarker |= (HITMARKER_GRUDGE | HITMARKER_x20); + switch (gBattleStruct->wishPerishSongState) + { + case 0: + while (gBattleStruct->wishPerishSongBattlerId < gBattlersCount) + { + gActiveBattler = gBattleStruct->wishPerishSongBattlerId; + if (gAbsentBattlerFlags & gBitTable[gActiveBattler]) + { + ++gBattleStruct->wishPerishSongBattlerId; + continue; + } + ++gBattleStruct->wishPerishSongBattlerId; + if (gWishFutureKnock.futureSightCounter[gActiveBattler] != 0 + && --gWishFutureKnock.futureSightCounter[gActiveBattler] == 0 + && gBattleMons[gActiveBattler].hp != 0) + { + if (gWishFutureKnock.futureSightMove[gActiveBattler] == MOVE_FUTURE_SIGHT) + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + else + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + PREPARE_MOVE_BUFFER(gBattleTextBuff1, gWishFutureKnock.futureSightMove[gActiveBattler]); + gBattlerTarget = gActiveBattler; + gBattlerAttacker = gWishFutureKnock.futureSightAttacker[gActiveBattler]; + gBattleMoveDamage = gWishFutureKnock.futureSightDmg[gActiveBattler]; + gSpecialStatuses[gBattlerTarget].dmg = 0xFFFF; + BattleScriptExecute(BattleScript_MonTookFutureAttack); + return TRUE; + } + } + { + u8 *state = &gBattleStruct->wishPerishSongState; + + *state = 1; + gBattleStruct->wishPerishSongBattlerId = 0; + } + // fall through + case 1: + while (gBattleStruct->wishPerishSongBattlerId < gBattlersCount) + { + gActiveBattler = gBattlerAttacker = gBattlerByTurnOrder[gBattleStruct->wishPerishSongBattlerId]; + if (gAbsentBattlerFlags & gBitTable[gActiveBattler]) + { + ++gBattleStruct->wishPerishSongBattlerId; + continue; + } + ++gBattleStruct->wishPerishSongBattlerId; + if (gStatuses3[gActiveBattler] & STATUS3_PERISH_SONG) + { + PREPARE_BYTE_NUMBER_BUFFER(gBattleTextBuff1, 1, gDisableStructs[gActiveBattler].perishSongTimer); + if (gDisableStructs[gActiveBattler].perishSongTimer == 0) + { + gStatuses3[gActiveBattler] &= ~STATUS3_PERISH_SONG; + gBattleMoveDamage = gBattleMons[gActiveBattler].hp; + gBattlescriptCurrInstr = BattleScript_PerishSongTakesLife; + } + else + { + --gDisableStructs[gActiveBattler].perishSongTimer; + gBattlescriptCurrInstr = BattleScript_PerishSongCountGoesDown; + } + BattleScriptExecute(gBattlescriptCurrInstr); + return TRUE; + } + } + break; + } + gHitMarker &= ~(HITMARKER_GRUDGE | HITMARKER_x20); + return FALSE; +} + +#define FAINTED_ACTIONS_MAX_CASE 7 + +bool8 HandleFaintedMonActions(void) +{ + if (gBattleTypeFlags & BATTLE_TYPE_SAFARI) + return FALSE; + do + { + s32 i; + switch (gBattleStruct->faintedActionsState) + { + case 0: + gBattleStruct->faintedActionsBattlerId = 0; + ++gBattleStruct->faintedActionsState; + for (i = 0; i < gBattlersCount; ++i) + { + if (gAbsentBattlerFlags & gBitTable[i] && !HasNoMonsToSwitch(i, 6, 6)) + gAbsentBattlerFlags &= ~(gBitTable[i]); + } + // fall through + case 1: + do + { + gBattlerFainted = gBattlerTarget = gBattleStruct->faintedActionsBattlerId; + if (gBattleMons[gBattleStruct->faintedActionsBattlerId].hp == 0 + && !(gBattleStruct->givenExpMons & gBitTable[gBattlerPartyIndexes[gBattleStruct->faintedActionsBattlerId]]) + && !(gAbsentBattlerFlags & gBitTable[gBattleStruct->faintedActionsBattlerId])) + { + BattleScriptExecute(BattleScript_GiveExp); + gBattleStruct->faintedActionsState = 2; + return TRUE; + } + } while (++gBattleStruct->faintedActionsBattlerId != gBattlersCount); + gBattleStruct->faintedActionsState = 3; + break; + case 2: + sub_8017434(gBattlerFainted); + if (++gBattleStruct->faintedActionsBattlerId == gBattlersCount) + gBattleStruct->faintedActionsState = 3; + else + gBattleStruct->faintedActionsState = 1; + break; + case 3: + gBattleStruct->faintedActionsBattlerId = 0; + ++gBattleStruct->faintedActionsState; + // fall through + case 4: + do + { + gBattlerFainted = gBattlerTarget = gBattleStruct->faintedActionsBattlerId; + if (gBattleMons[gBattleStruct->faintedActionsBattlerId].hp == 0 + && !(gAbsentBattlerFlags & gBitTable[gBattleStruct->faintedActionsBattlerId])) + { + BattleScriptExecute(BattleScript_HandleFaintedMon); + gBattleStruct->faintedActionsState = 5; + return TRUE; + } + } while (++gBattleStruct->faintedActionsBattlerId != gBattlersCount); + gBattleStruct->faintedActionsState = 6; + break; + case 5: + if (++gBattleStruct->faintedActionsBattlerId == gBattlersCount) + gBattleStruct->faintedActionsState = 6; + else + gBattleStruct->faintedActionsState = 4; + break; + case 6: + if (AbilityBattleEffects(ABILITYEFFECT_INTIMIDATE1, 0, 0, 0, 0) || AbilityBattleEffects(ABILITYEFFECT_TRACE, 0, 0, 0, 0) || ItemBattleEffects(1, 0, TRUE) || AbilityBattleEffects(ABILITYEFFECT_FORECAST, 0, 0, 0, 0)) + return TRUE; + ++gBattleStruct->faintedActionsState; + break; + case FAINTED_ACTIONS_MAX_CASE: + break; + } + } while (gBattleStruct->faintedActionsState != FAINTED_ACTIONS_MAX_CASE); + return FALSE; +} + +void TryClearRageStatuses(void) +{ + s32 i; + + for (i = 0; i < gBattlersCount; ++i) + if ((gBattleMons[i].status2 & STATUS2_RAGE) && gChosenMoveByBattler[i] != MOVE_RAGE) + gBattleMons[i].status2 &= ~(STATUS2_RAGE); +} + +enum +{ + CANCELLER_FLAGS, + CANCELLER_ASLEEP, + CANCELLER_FROZEN, + CANCELLER_TRUANT, + CANCELLER_RECHARGE, + CANCELLER_FLINCH, + CANCELLER_DISABLED, + CANCELLER_TAUNTED, + CANCELLER_IMPRISONED, + CANCELLER_CONFUSED, + CANCELLER_PARALYSED, + CANCELLER_GHOST, + CANCELLER_IN_LOVE, + CANCELLER_BIDE, + CANCELLER_THAW, + CANCELLER_END, +}; + +u8 AtkCanceller_UnableToUseMove(void) +{ + u8 effect = 0; + s32 *bideDmg = &gBattleScripting.bideDmg; + + do + { + switch (gBattleStruct->atkCancellerTracker) + { + case CANCELLER_FLAGS: // flags clear + gBattleMons[gBattlerAttacker].status2 &= ~(STATUS2_DESTINY_BOND); + gStatuses3[gBattlerAttacker] &= ~(STATUS3_GRUDGE); + ++gBattleStruct->atkCancellerTracker; + break; + case CANCELLER_ASLEEP: // check being asleep + if (gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP) + { + if (UproarWakeUpCheck(gBattlerAttacker)) + { + gBattleMons[gBattlerAttacker].status1 &= ~(STATUS1_SLEEP); + gBattleMons[gBattlerAttacker].status2 &= ~(STATUS2_NIGHTMARE); + BattleScriptPushCursor(); + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + gBattlescriptCurrInstr = BattleScript_MoveUsedWokeUp; + effect = 2; + } + else + { + u8 toSub; + + if (gBattleMons[gBattlerAttacker].ability == ABILITY_EARLY_BIRD) + toSub = 2; + else + toSub = 1; + if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP) < toSub) + gBattleMons[gBattlerAttacker].status1 &= ~(STATUS1_SLEEP); + else + gBattleMons[gBattlerAttacker].status1 -= toSub; + if (gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP) + { + if (gCurrentMove != MOVE_SNORE && gCurrentMove != MOVE_SLEEP_TALK) + { + gBattlescriptCurrInstr = BattleScript_MoveUsedIsAsleep; + gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; + effect = 2; + } + } + else + { + gBattleMons[gBattlerAttacker].status2 &= ~(STATUS2_NIGHTMARE); + BattleScriptPushCursor(); + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + gBattlescriptCurrInstr = BattleScript_MoveUsedWokeUp; + effect = 2; + } + } + } + ++gBattleStruct->atkCancellerTracker; + break; + case CANCELLER_FROZEN: // check being frozen + if (gBattleMons[gBattlerAttacker].status1 & STATUS1_FREEZE) + { + if (Random() % 5) + { + if (gBattleMoves[gCurrentMove].effect != EFFECT_THAW_HIT) // unfreezing via a move effect happens in case 13 + { + gBattlescriptCurrInstr = BattleScript_MoveUsedIsFrozen; + gHitMarker |= HITMARKER_NO_ATTACKSTRING; + } + else + { + ++gBattleStruct->atkCancellerTracker; + break; + } + } + else // unfreeze + { + gBattleMons[gBattlerAttacker].status1 &= ~(STATUS1_FREEZE); + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_MoveUsedUnfroze; + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + } + effect = 2; + } + ++gBattleStruct->atkCancellerTracker; + break; + case CANCELLER_TRUANT: // truant + if (gBattleMons[gBattlerAttacker].ability == ABILITY_TRUANT && gDisableStructs[gBattlerAttacker].truantCounter) + { + CancelMultiTurnMoves(gBattlerAttacker); + gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + gBattlescriptCurrInstr = BattleScript_MoveUsedLoafingAround; + gMoveResultFlags |= MOVE_RESULT_MISSED; + effect = 1; + } + ++gBattleStruct->atkCancellerTracker; + break; + case CANCELLER_RECHARGE: // recharge + if (gBattleMons[gBattlerAttacker].status2 & STATUS2_RECHARGE) + { + gBattleMons[gBattlerAttacker].status2 &= ~(STATUS2_RECHARGE); + gDisableStructs[gBattlerAttacker].rechargeTimer = 0; + CancelMultiTurnMoves(gBattlerAttacker); + gBattlescriptCurrInstr = BattleScript_MoveUsedMustRecharge; + gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; + effect = 1; + } + ++gBattleStruct->atkCancellerTracker; + break; + case CANCELLER_FLINCH: // flinch + if (gBattleMons[gBattlerAttacker].status2 & STATUS2_FLINCHED) + { + gBattleMons[gBattlerAttacker].status2 &= ~(STATUS2_FLINCHED); + gProtectStructs[gBattlerAttacker].flinchImmobility = 1; + CancelMultiTurnMoves(gBattlerAttacker); + gBattlescriptCurrInstr = BattleScript_MoveUsedFlinched; + gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; + effect = 1; + } + ++gBattleStruct->atkCancellerTracker; + break; + case CANCELLER_DISABLED: // disabled move + if (gDisableStructs[gBattlerAttacker].disabledMove == gCurrentMove && gDisableStructs[gBattlerAttacker].disabledMove != MOVE_NONE) + { + gProtectStructs[gBattlerAttacker].usedDisabledMove = 1; + gBattleScripting.battler = gBattlerAttacker; + CancelMultiTurnMoves(gBattlerAttacker); + gBattlescriptCurrInstr = BattleScript_MoveUsedIsDisabled; + gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; + effect = 1; + } + ++gBattleStruct->atkCancellerTracker; + break; + case CANCELLER_TAUNTED: // taunt + if (gDisableStructs[gBattlerAttacker].tauntTimer && gBattleMoves[gCurrentMove].power == 0) + { + gProtectStructs[gBattlerAttacker].usedTauntedMove = 1; + CancelMultiTurnMoves(gBattlerAttacker); + gBattlescriptCurrInstr = BattleScript_MoveUsedIsTaunted; + gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; + effect = 1; + } + ++gBattleStruct->atkCancellerTracker; + break; + case CANCELLER_IMPRISONED: // imprisoned + if (GetImprisonedMovesCount(gBattlerAttacker, gCurrentMove)) + { + gProtectStructs[gBattlerAttacker].usedImprisonedMove = 1; + CancelMultiTurnMoves(gBattlerAttacker); + gBattlescriptCurrInstr = BattleScript_MoveUsedIsImprisoned; + gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; + effect = 1; + } + ++gBattleStruct->atkCancellerTracker; + break; + case CANCELLER_CONFUSED: // confusion + if (gBattleMons[gBattlerAttacker].status2 & STATUS2_CONFUSION) + { + --gBattleMons[gBattlerAttacker].status2; + if (gBattleMons[gBattlerAttacker].status2 & STATUS2_CONFUSION) + { + if (Random() & 1) + { + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + BattleScriptPushCursor(); + } + else // confusion dmg + { + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + gBattlerTarget = gBattlerAttacker; + gBattleMoveDamage = CalculateBaseDamage(&gBattleMons[gBattlerAttacker], &gBattleMons[gBattlerAttacker], MOVE_POUND, 0, 40, 0, gBattlerAttacker, gBattlerAttacker); + gProtectStructs[gBattlerAttacker].confusionSelfDmg = 1; + gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; + } + gBattlescriptCurrInstr = BattleScript_MoveUsedIsConfused; + } + else // snapped out of confusion + { + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_MoveUsedIsConfusedNoMore; + } + effect = 1; + } + ++gBattleStruct->atkCancellerTracker; + break; + case CANCELLER_PARALYSED: // paralysis + if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_PARALYSIS) && (Random() % 4) == 0) + { + gProtectStructs[gBattlerAttacker].prlzImmobility = 1; + gBattlescriptCurrInstr = BattleScript_MoveUsedIsParalyzed; + gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; + effect = 1; + } + ++gBattleStruct->atkCancellerTracker; + break; + case CANCELLER_GHOST: // GHOST in pokemon tower + if ((gBattleTypeFlags & (BATTLE_TYPE_GHOST | BATTLE_TYPE_LEGENDARY)) == BATTLE_TYPE_GHOST) + { + if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER) + gBattlescriptCurrInstr = gUnknown_81D9180; + else + gBattlescriptCurrInstr = gUnknown_81D9192; + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + effect = 1; + } + ++gBattleStruct->atkCancellerTracker; + break; + case CANCELLER_IN_LOVE: // infatuation + if (gBattleMons[gBattlerAttacker].status2 & STATUS2_INFATUATION) + { + gBattleScripting.battler = CountTrailingZeroBits((gBattleMons[gBattlerAttacker].status2 & STATUS2_INFATUATION) >> 0x10); + if (Random() & 1) + { + BattleScriptPushCursor(); + } + else + { + BattleScriptPush(BattleScript_MoveUsedIsInLoveCantAttack); + gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; + gProtectStructs[gBattlerAttacker].loveImmobility = 1; + CancelMultiTurnMoves(gBattlerAttacker); + } + gBattlescriptCurrInstr = BattleScript_MoveUsedIsInLove; + effect = 1; + } + ++gBattleStruct->atkCancellerTracker; + break; + case CANCELLER_BIDE: // bide + if (gBattleMons[gBattlerAttacker].status2 & STATUS2_BIDE) + { + gBattleMons[gBattlerAttacker].status2 -= 0x100; + if (gBattleMons[gBattlerAttacker].status2 & STATUS2_BIDE) + { + gBattlescriptCurrInstr = BattleScript_BideStoringEnergy; + } + else + { + if (gTakenDmg[gBattlerAttacker]) + { + gCurrentMove = MOVE_BIDE; + *bideDmg = gTakenDmg[gBattlerAttacker] * 2; + gBattlerTarget = gTakenDmgByBattler[gBattlerAttacker]; + if (gAbsentBattlerFlags & gBitTable[gBattlerTarget]) + gBattlerTarget = GetMoveTarget(MOVE_BIDE, 1); + gBattlescriptCurrInstr = BattleScript_BideAttack; + } + else + { + gBattlescriptCurrInstr = BattleScript_BideNoEnergyToAttack; + } + } + effect = 1; + } + ++gBattleStruct->atkCancellerTracker; + break; + case CANCELLER_THAW: // move thawing + if (gBattleMons[gBattlerAttacker].status1 & STATUS1_FREEZE) + { + if (gBattleMoves[gCurrentMove].effect == EFFECT_THAW_HIT) + { + gBattleMons[gBattlerAttacker].status1 &= ~(STATUS1_FREEZE); + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_MoveUsedUnfroze; + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + } + effect = 2; + } + ++gBattleStruct->atkCancellerTracker; + break; + case CANCELLER_END: + break; + } + + } while (gBattleStruct->atkCancellerTracker != CANCELLER_END && !effect); + if (effect == 2) + { + gActiveBattler = gBattlerAttacker; + BtlController_EmitSetMonData(0, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[gActiveBattler].status1); + MarkBattlerForControllerExec(gActiveBattler); + } + return effect; +} + +bool8 HasNoMonsToSwitch(u8 battler, u8 partyIdBattlerOn1, u8 partyIdBattlerOn2) +{ + u8 playerId, flankId; + struct Pokemon *party; + s32 i; + + if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE)) + { + return FALSE; + } + else + { + if (gBattleTypeFlags & BATTLE_TYPE_MULTI) + { + playerId = GetBattlerMultiplayerId(battler); + if (GetBattlerSide(battler) == B_SIDE_PLAYER) + party = gPlayerParty; + else + party = gEnemyParty; + flankId = GetLinkTrainerFlankId(playerId); + for (i = flankId * 3; i < flankId * 3 + 3; ++i) + { + if (GetMonData(&party[i], MON_DATA_HP) != 0 + && GetMonData(&party[i], MON_DATA_SPECIES2) != SPECIES_NONE + && GetMonData(&party[i], MON_DATA_SPECIES2) != SPECIES_EGG) + break; + } + return (i == flankId * 3 + 3); + } + else + { + if (GetBattlerSide(battler) == B_SIDE_OPPONENT) + { + playerId = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); + flankId = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); + party = gEnemyParty; + } + else + { + playerId = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); + flankId = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT); + party = gPlayerParty; + } + if (partyIdBattlerOn1 == PARTY_SIZE) + partyIdBattlerOn1 = gBattlerPartyIndexes[playerId]; + if (partyIdBattlerOn2 == PARTY_SIZE) + partyIdBattlerOn2 = gBattlerPartyIndexes[flankId]; + for (i = 0; i < PARTY_SIZE; ++i) + { + if (GetMonData(&party[i], MON_DATA_HP) != 0 + && GetMonData(&party[i], MON_DATA_SPECIES2) != SPECIES_NONE + && GetMonData(&party[i], MON_DATA_SPECIES2) != SPECIES_EGG + && i != partyIdBattlerOn1 + && i != partyIdBattlerOn2 + && i != *(gBattleStruct->monToSwitchIntoId + playerId) + && i != flankId[gBattleStruct->monToSwitchIntoId]) + break; + } + return (i == PARTY_SIZE); + } + } +} + +enum +{ + CASTFORM_NO_CHANGE, + CASTFORM_TO_NORMAL, + CASTFORM_TO_FIRE, + CASTFORM_TO_WATER, + CASTFORM_TO_ICE, +}; + +u8 CastformDataTypeChange(u8 battler) +{ + u8 formChange = 0; + if (gBattleMons[battler].species != SPECIES_CASTFORM || gBattleMons[battler].ability != ABILITY_FORECAST || gBattleMons[battler].hp == 0) + return CASTFORM_NO_CHANGE; + if (!WEATHER_HAS_EFFECT && !IS_BATTLER_OF_TYPE(battler, TYPE_NORMAL)) + { + SET_BATTLER_TYPE(battler, TYPE_NORMAL); + return CASTFORM_TO_NORMAL; + } + if (!WEATHER_HAS_EFFECT) + return CASTFORM_NO_CHANGE; + if (!(gBattleWeather & (WEATHER_RAIN_ANY | WEATHER_SUN_ANY | WEATHER_HAIL_ANY)) && !IS_BATTLER_OF_TYPE(battler, TYPE_NORMAL)) + { + SET_BATTLER_TYPE(battler, TYPE_NORMAL); + formChange = CASTFORM_TO_NORMAL; + } + if (gBattleWeather & WEATHER_SUN_ANY && !IS_BATTLER_OF_TYPE(battler, TYPE_FIRE)) + { + SET_BATTLER_TYPE(battler, TYPE_FIRE); + formChange = CASTFORM_TO_FIRE; + } + if (gBattleWeather & WEATHER_RAIN_ANY && !IS_BATTLER_OF_TYPE(battler, TYPE_WATER)) + { + SET_BATTLER_TYPE(battler, TYPE_WATER); + formChange = CASTFORM_TO_WATER; + } + if (gBattleWeather & WEATHER_HAIL_ANY && !IS_BATTLER_OF_TYPE(battler, TYPE_ICE)) + { + SET_BATTLER_TYPE(battler, TYPE_ICE); + formChange = CASTFORM_TO_ICE; + } + return formChange; +} + +u8 AbilityBattleEffects(u8 caseID, u8 battler, u8 ability, u8 special, u16 moveArg) +{ + u8 effect = 0; + struct Pokemon *pokeAtk; + struct Pokemon *pokeDef; + u16 speciesAtk; + u16 speciesDef; + u32 pidAtk; + u32 pidDef; + + if (gBattlerAttacker >= gBattlersCount) + gBattlerAttacker = battler; + if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER) + pokeAtk = &gPlayerParty[gBattlerPartyIndexes[gBattlerAttacker]]; + else + pokeAtk = &gEnemyParty[gBattlerPartyIndexes[gBattlerAttacker]]; + if (gBattlerTarget >= gBattlersCount) + gBattlerTarget = battler; + if (GetBattlerSide(gBattlerTarget) == B_SIDE_PLAYER) + pokeDef = &gPlayerParty[gBattlerPartyIndexes[gBattlerTarget]]; + else + pokeDef = &gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]]; + speciesAtk = GetMonData(pokeAtk, MON_DATA_SPECIES); + pidAtk = GetMonData(pokeAtk, MON_DATA_PERSONALITY); + speciesDef = GetMonData(pokeDef, MON_DATA_SPECIES); + pidDef = GetMonData(pokeDef, MON_DATA_PERSONALITY); + if (!(gBattleTypeFlags & BATTLE_TYPE_SAFARI)) // Why isn't that check done at the beginning? + { + u8 moveType; + s32 i; + u8 side; + u8 target1; + + if (special) + gLastUsedAbility = special; + else + gLastUsedAbility = gBattleMons[battler].ability; + if (!moveArg) + moveArg = gCurrentMove; + GET_MOVE_TYPE(moveArg, moveType); + if ((gBattleTypeFlags & (BATTLE_TYPE_GHOST | BATTLE_TYPE_LEGENDARY)) == BATTLE_TYPE_GHOST + && (gLastUsedAbility == ABILITY_INTIMIDATE || gLastUsedAbility == ABILITY_TRACE)) + return effect; + switch (caseID) + { + case ABILITYEFFECT_ON_SWITCHIN: // 0 + if (gBattlerAttacker >= gBattlersCount) + gBattlerAttacker = battler; + switch (gLastUsedAbility) + { + case ABILITYEFFECT_SWITCH_IN_WEATHER: + switch (GetCurrentWeather()) + { + case WEATHER_RAIN: + case WEATHER_RAIN_THUNDERSTORM: + case WEATHER_DOWNPOUR: + if (!(gBattleWeather & WEATHER_RAIN_ANY)) + { + gBattleWeather = (WEATHER_RAIN_TEMPORARY | WEATHER_RAIN_PERMANENT); + gBattleScripting.animArg1 = B_ANIM_RAIN_CONTINUES; + gBattleScripting.battler = battler; + ++effect; + } + break; + case WEATHER_SANDSTORM: + if (!(gBattleWeather & WEATHER_SANDSTORM_ANY)) + { + gBattleWeather = (WEATHER_SANDSTORM_PERMANENT | WEATHER_SANDSTORM_TEMPORARY); + gBattleScripting.animArg1 = B_ANIM_SANDSTORM_CONTINUES; + gBattleScripting.battler = battler; + ++effect; + } + break; + case WEATHER_DROUGHT: + if (!(gBattleWeather & WEATHER_SUN_ANY)) + { + gBattleWeather = (WEATHER_SUN_PERMANENT | WEATHER_SUN_TEMPORARY); + gBattleScripting.animArg1 = B_ANIM_SUN_CONTINUES; + gBattleScripting.battler = battler; + ++effect; + } + break; + } + if (effect) + { + gBattleCommunication[MULTISTRING_CHOOSER] = GetCurrentWeather(); + BattleScriptPushCursorAndCallback(BattleScript_OverworldWeatherStarts); + } + break; + case ABILITY_DRIZZLE: + if (!(gBattleWeather & WEATHER_RAIN_PERMANENT)) + { + gBattleWeather = (WEATHER_RAIN_PERMANENT | WEATHER_RAIN_TEMPORARY); + BattleScriptPushCursorAndCallback(BattleScript_DrizzleActivates); + gBattleScripting.battler = battler; + ++effect; + } + break; + case ABILITY_SAND_STREAM: + if (!(gBattleWeather & WEATHER_SANDSTORM_PERMANENT)) + { + gBattleWeather = (WEATHER_SANDSTORM_PERMANENT | WEATHER_SANDSTORM_TEMPORARY); + BattleScriptPushCursorAndCallback(BattleScript_SandstreamActivates); + gBattleScripting.battler = battler; + ++effect; + } + break; + case ABILITY_DROUGHT: + if (!(gBattleWeather & WEATHER_SUN_PERMANENT)) + { + gBattleWeather = (WEATHER_SUN_PERMANENT | WEATHER_SUN_TEMPORARY); + BattleScriptPushCursorAndCallback(BattleScript_DroughtActivates); + gBattleScripting.battler = battler; + ++effect; + } + break; + case ABILITY_INTIMIDATE: + if (!(gSpecialStatuses[battler].intimidatedMon)) + { + gStatuses3[battler] |= STATUS3_INTIMIDATE_POKES; + gSpecialStatuses[battler].intimidatedMon = 1; + } + break; + case ABILITY_FORECAST: + effect = CastformDataTypeChange(battler); + if (effect != 0) + { + BattleScriptPushCursorAndCallback(BattleScript_CastformChange); + gBattleScripting.battler = battler; + *(&gBattleStruct->formToChangeInto) = effect - 1; + } + break; + case ABILITY_TRACE: + if (!(gSpecialStatuses[battler].traced)) + { + gStatuses3[battler] |= STATUS3_TRACE; + gSpecialStatuses[battler].traced = 1; + } + break; + case ABILITY_CLOUD_NINE: + case ABILITY_AIR_LOCK: + { + for (target1 = 0; target1 < gBattlersCount; ++target1) + { + effect = CastformDataTypeChange(target1); + if (effect != 0) + { + BattleScriptPushCursorAndCallback(BattleScript_CastformChange); + gBattleScripting.battler = target1; + *(&gBattleStruct->formToChangeInto) = effect - 1; + break; + } + } + } + break; + } + break; + case ABILITYEFFECT_ENDTURN: // 1 + if (gBattleMons[battler].hp != 0) + { + gBattlerAttacker = battler; + switch (gLastUsedAbility) + { + case ABILITY_RAIN_DISH: + if (WEATHER_HAS_EFFECT && (gBattleWeather & WEATHER_RAIN_ANY) + && gBattleMons[battler].maxHP > gBattleMons[battler].hp) + { + gLastUsedAbility = ABILITY_RAIN_DISH; // why + BattleScriptPushCursorAndCallback(BattleScript_RainDishActivates); + gBattleMoveDamage = gBattleMons[battler].maxHP / 16; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + gBattleMoveDamage *= -1; + ++effect; + } + break; + case ABILITY_SHED_SKIN: + if ((gBattleMons[battler].status1 & STATUS1_ANY) && (Random() % 3) == 0) + { + if (gBattleMons[battler].status1 & (STATUS1_POISON | STATUS1_TOXIC_POISON)) + StringCopy(gBattleTextBuff1, gStatusConditionString_PoisonJpn); + if (gBattleMons[battler].status1 & STATUS1_SLEEP) + StringCopy(gBattleTextBuff1, gStatusConditionString_SleepJpn); + if (gBattleMons[battler].status1 & STATUS1_PARALYSIS) + StringCopy(gBattleTextBuff1, gStatusConditionString_ParalysisJpn); + if (gBattleMons[battler].status1 & STATUS1_BURN) + StringCopy(gBattleTextBuff1, gStatusConditionString_BurnJpn); + if (gBattleMons[battler].status1 & STATUS1_FREEZE) + StringCopy(gBattleTextBuff1, gStatusConditionString_IceJpn); + gBattleMons[battler].status1 = 0; + gBattleMons[battler].status2 &= ~(STATUS2_NIGHTMARE); // fix nightmare glitch + gBattleScripting.battler = gActiveBattler = battler; + BattleScriptPushCursorAndCallback(BattleScript_ShedSkinActivates); + BtlController_EmitSetMonData(0, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[battler].status1); + MarkBattlerForControllerExec(gActiveBattler); + ++effect; + } + break; + case ABILITY_SPEED_BOOST: + if (gBattleMons[battler].statStages[STAT_SPEED] < 0xC && gDisableStructs[battler].isFirstTurn != 2) + { + ++gBattleMons[battler].statStages[STAT_SPEED]; + gBattleScripting.animArg1 = 0x11; + gBattleScripting.animArg2 = 0; + BattleScriptPushCursorAndCallback(BattleScript_SpeedBoostActivates); + gBattleScripting.battler = battler; + ++effect; + } + break; + case ABILITY_TRUANT: + gDisableStructs[gBattlerAttacker].truantCounter ^= 1; + break; + } + } + break; + case ABILITYEFFECT_MOVES_BLOCK: // 2 + if (gLastUsedAbility == ABILITY_SOUNDPROOF) + { + for (i = 0; sSoundMovesTable[i] != 0xFFFF; ++i) + if (sSoundMovesTable[i] == moveArg) + break; + if (sSoundMovesTable[i] != 0xFFFF) + { + if (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS) + gHitMarker |= HITMARKER_NO_PPDEDUCT; + gBattlescriptCurrInstr = BattleScript_SoundproofProtected; + effect = 1; + } + } + break; + case ABILITYEFFECT_ABSORBING: // 3 + if (moveArg) + { + switch (gLastUsedAbility) + { + case ABILITY_VOLT_ABSORB: + if (moveType == TYPE_ELECTRIC && gBattleMoves[moveArg].power != 0) + { + if (gProtectStructs[gBattlerAttacker].notFirstStrike) + gBattlescriptCurrInstr = BattleScript_MoveHPDrain; + else + gBattlescriptCurrInstr = BattleScript_MoveHPDrain_PPLoss; + effect = 1; + } + break; + case ABILITY_WATER_ABSORB: + if (moveType == TYPE_WATER && gBattleMoves[moveArg].power != 0) + { + if (gProtectStructs[gBattlerAttacker].notFirstStrike) + gBattlescriptCurrInstr = BattleScript_MoveHPDrain; + else + gBattlescriptCurrInstr = BattleScript_MoveHPDrain_PPLoss; + effect = 1; + } + break; + case ABILITY_FLASH_FIRE: + if (moveType == TYPE_FIRE && !(gBattleMons[battler].status1 & STATUS1_FREEZE)) + { + if (!(gBattleResources->flags->flags[battler] & RESOURCE_FLAG_FLASH_FIRE)) + { + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + if (gProtectStructs[gBattlerAttacker].notFirstStrike) + gBattlescriptCurrInstr = BattleScript_FlashFireBoost; + else + gBattlescriptCurrInstr = BattleScript_FlashFireBoost_PPLoss; + gBattleResources->flags->flags[battler] |= RESOURCE_FLAG_FLASH_FIRE; + effect = 2; + } + else + { + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + if (gProtectStructs[gBattlerAttacker].notFirstStrike) + gBattlescriptCurrInstr = BattleScript_FlashFireBoost; + else + gBattlescriptCurrInstr = BattleScript_FlashFireBoost_PPLoss; + effect = 2; + } + } + break; + } + if (effect == 1) + { + if (gBattleMons[battler].maxHP == gBattleMons[battler].hp) + { + if ((gProtectStructs[gBattlerAttacker].notFirstStrike)) + gBattlescriptCurrInstr = BattleScript_MonMadeMoveUseless; + else + gBattlescriptCurrInstr = BattleScript_MonMadeMoveUseless_PPLoss; + } + else + { + gBattleMoveDamage = gBattleMons[battler].maxHP / 4; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + gBattleMoveDamage *= -1; + } + } + } + break; + case ABILITYEFFECT_MOVE_END: // Think contact abilities. + switch (gLastUsedAbility) + { + case ABILITY_COLOR_CHANGE: + if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) + && moveArg != MOVE_STRUGGLE + && gBattleMoves[moveArg].power != 0 + && TARGET_TURN_DAMAGED + && !IS_BATTLER_OF_TYPE(battler, moveType) + && gBattleMons[battler].hp != 0) + { + SET_BATTLER_TYPE(battler, moveType); + PREPARE_TYPE_BUFFER(gBattleTextBuff1, moveType); + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_ColorChangeActivates; + ++effect; + } + break; + case ABILITY_ROUGH_SKIN: + if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) + && gBattleMons[gBattlerAttacker].hp != 0 + && !gProtectStructs[gBattlerAttacker].confusionSelfDmg + && TARGET_TURN_DAMAGED + && (gBattleMoves[moveArg].flags & FLAG_MAKES_CONTACT)) + { + gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / 16; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_RoughSkinActivates; + ++effect; + } + break; + case ABILITY_EFFECT_SPORE: + if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) + && gBattleMons[gBattlerAttacker].hp != 0 + && !gProtectStructs[gBattlerAttacker].confusionSelfDmg + && TARGET_TURN_DAMAGED + && (gBattleMoves[moveArg].flags & FLAG_MAKES_CONTACT) + && (Random() % 10) == 0) + { + do + gBattleCommunication[MOVE_EFFECT_BYTE] = Random() & 3; + while (gBattleCommunication[MOVE_EFFECT_BYTE] == 0); + + if (gBattleCommunication[MOVE_EFFECT_BYTE] == MOVE_EFFECT_BURN) + gBattleCommunication[MOVE_EFFECT_BYTE] += 2; // 5 MOVE_EFFECT_PARALYSIS + gBattleCommunication[MOVE_EFFECT_BYTE] += MOVE_EFFECT_AFFECTS_USER; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_ApplySecondaryEffect; + gHitMarker |= HITMARKER_IGNORE_SAFEGUARD; + ++effect; + } + break; + case ABILITY_POISON_POINT: + if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) + && gBattleMons[gBattlerAttacker].hp != 0 + && !gProtectStructs[gBattlerAttacker].confusionSelfDmg + && TARGET_TURN_DAMAGED + && (gBattleMoves[moveArg].flags & FLAG_MAKES_CONTACT) + && (Random() % 3) == 0) + { + gBattleCommunication[MOVE_EFFECT_BYTE] = MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_POISON; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_ApplySecondaryEffect; + gHitMarker |= HITMARKER_IGNORE_SAFEGUARD; + ++effect; + } + break; + case ABILITY_STATIC: + if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) + && gBattleMons[gBattlerAttacker].hp != 0 + && !gProtectStructs[gBattlerAttacker].confusionSelfDmg + && TARGET_TURN_DAMAGED + && (gBattleMoves[moveArg].flags & FLAG_MAKES_CONTACT) + && (Random() % 3) == 0) + { + gBattleCommunication[MOVE_EFFECT_BYTE] = MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_PARALYSIS; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_ApplySecondaryEffect; + gHitMarker |= HITMARKER_IGNORE_SAFEGUARD; + ++effect; + } + break; + case ABILITY_FLAME_BODY: + if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) + && gBattleMons[gBattlerAttacker].hp != 0 + && !gProtectStructs[gBattlerAttacker].confusionSelfDmg + && (gBattleMoves[moveArg].flags & FLAG_MAKES_CONTACT) + && TARGET_TURN_DAMAGED + && (Random() % 3) == 0) + { + gBattleCommunication[MOVE_EFFECT_BYTE] = MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_BURN; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_ApplySecondaryEffect; + gHitMarker |= HITMARKER_IGNORE_SAFEGUARD; + ++effect; + } + break; + case ABILITY_CUTE_CHARM: + if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) + && gBattleMons[gBattlerAttacker].hp != 0 + && !gProtectStructs[gBattlerAttacker].confusionSelfDmg + && (gBattleMoves[moveArg].flags & FLAG_MAKES_CONTACT) + && TARGET_TURN_DAMAGED + && gBattleMons[gBattlerTarget].hp != 0 + && (Random() % 3) == 0 + && gBattleMons[gBattlerAttacker].ability != ABILITY_OBLIVIOUS + && GetGenderFromSpeciesAndPersonality(speciesAtk, pidAtk) != GetGenderFromSpeciesAndPersonality(speciesDef, pidDef) + && !(gBattleMons[gBattlerAttacker].status2 & STATUS2_INFATUATION) + && GetGenderFromSpeciesAndPersonality(speciesAtk, pidAtk) != MON_GENDERLESS + && GetGenderFromSpeciesAndPersonality(speciesDef, pidDef) != MON_GENDERLESS) + { + gBattleMons[gBattlerAttacker].status2 |= STATUS2_INFATUATED_WITH(gBattlerTarget); + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_CuteCharmActivates; + ++effect; + } + break; + } + break; + case ABILITYEFFECT_IMMUNITY: // 5 + for (battler = 0; battler < gBattlersCount; ++battler) + { + switch (gBattleMons[battler].ability) + { + case ABILITY_IMMUNITY: + if (gBattleMons[battler].status1 & (STATUS1_POISON | STATUS1_TOXIC_POISON | STATUS1_TOXIC_COUNTER)) + { + StringCopy(gBattleTextBuff1, gStatusConditionString_PoisonJpn); + effect = 1; + } + break; + case ABILITY_OWN_TEMPO: + if (gBattleMons[battler].status2 & STATUS2_CONFUSION) + { + StringCopy(gBattleTextBuff1, gStatusConditionString_ConfusionJpn); + effect = 2; + } + break; + case ABILITY_LIMBER: + if (gBattleMons[battler].status1 & STATUS1_PARALYSIS) + { + StringCopy(gBattleTextBuff1, gStatusConditionString_ParalysisJpn); + effect = 1; + } + break; + case ABILITY_INSOMNIA: + case ABILITY_VITAL_SPIRIT: + if (gBattleMons[battler].status1 & STATUS1_SLEEP) + { + gBattleMons[battler].status2 &= ~(STATUS2_NIGHTMARE); + StringCopy(gBattleTextBuff1, gStatusConditionString_SleepJpn); + effect = 1; + } + break; + case ABILITY_WATER_VEIL: + if (gBattleMons[battler].status1 & STATUS1_BURN) + { + StringCopy(gBattleTextBuff1, gStatusConditionString_BurnJpn); + effect = 1; + } + break; + case ABILITY_MAGMA_ARMOR: + if (gBattleMons[battler].status1 & STATUS1_FREEZE) + { + StringCopy(gBattleTextBuff1, gStatusConditionString_IceJpn); + effect = 1; + } + break; + case ABILITY_OBLIVIOUS: + if (gBattleMons[battler].status2 & STATUS2_INFATUATION) + { + StringCopy(gBattleTextBuff1, gStatusConditionString_LoveJpn); + effect = 3; + } + break; + } + if (effect) + { + switch (effect) + { + case 1: // status cleared + gBattleMons[battler].status1 = 0; + break; + case 2: // get rid of confusion + gBattleMons[battler].status2 &= ~(STATUS2_CONFUSION); + break; + case 3: // get rid of infatuation + gBattleMons[battler].status2 &= ~(STATUS2_INFATUATION); + break; + } + + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_AbilityCuredStatus; + gBattleScripting.battler = battler; + gActiveBattler = battler; + BtlController_EmitSetMonData(0, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[gActiveBattler].status1); + MarkBattlerForControllerExec(gActiveBattler); + return effect; + } + } + break; + case ABILITYEFFECT_FORECAST: // 6 + for (battler = 0; battler < gBattlersCount; ++battler) + { + if (gBattleMons[battler].ability == ABILITY_FORECAST) + { + effect = CastformDataTypeChange(battler); + if (effect) + { + BattleScriptPushCursorAndCallback(BattleScript_CastformChange); + gBattleScripting.battler = battler; + *(&gBattleStruct->formToChangeInto) = effect - 1; + return effect; + } + } + } + break; + case ABILITYEFFECT_SYNCHRONIZE: // 7 + if (gLastUsedAbility == ABILITY_SYNCHRONIZE && (gHitMarker & HITMARKER_SYNCHRONISE_EFFECT)) + { + gHitMarker &= ~(HITMARKER_SYNCHRONISE_EFFECT); + gBattleStruct->synchronizeMoveEffect &= ~(MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_CERTAIN); + if (gBattleStruct->synchronizeMoveEffect == MOVE_EFFECT_TOXIC) + gBattleStruct->synchronizeMoveEffect = MOVE_EFFECT_POISON; + gBattleCommunication[MOVE_EFFECT_BYTE] = gBattleStruct->synchronizeMoveEffect + MOVE_EFFECT_AFFECTS_USER; + gBattleScripting.battler = gBattlerTarget; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_SynchronizeActivates; + gHitMarker |= HITMARKER_IGNORE_SAFEGUARD; + ++effect; + } + break; + case ABILITYEFFECT_ATK_SYNCHRONIZE: // 8 + if (gLastUsedAbility == ABILITY_SYNCHRONIZE && (gHitMarker & HITMARKER_SYNCHRONISE_EFFECT)) + { + gHitMarker &= ~(HITMARKER_SYNCHRONISE_EFFECT); + gBattleStruct->synchronizeMoveEffect &= ~(MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_CERTAIN); + if (gBattleStruct->synchronizeMoveEffect == MOVE_EFFECT_TOXIC) + gBattleStruct->synchronizeMoveEffect = MOVE_EFFECT_POISON; + gBattleCommunication[MOVE_EFFECT_BYTE] = gBattleStruct->synchronizeMoveEffect; + gBattleScripting.battler = gBattlerAttacker; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_SynchronizeActivates; + gHitMarker |= HITMARKER_IGNORE_SAFEGUARD; + ++effect; + } + break; + case ABILITYEFFECT_INTIMIDATE1: // 9 + for (i = 0; i < gBattlersCount; ++i) + { + if (gBattleMons[i].ability == ABILITY_INTIMIDATE && gStatuses3[i] & STATUS3_INTIMIDATE_POKES) + { + gLastUsedAbility = ABILITY_INTIMIDATE; + gStatuses3[i] &= ~(STATUS3_INTIMIDATE_POKES); + BattleScriptPushCursorAndCallback(BattleScript_IntimidateActivatesEnd3); + gBattleStruct->intimidateBattler = i; + ++effect; + break; + } + } + break; + case ABILITYEFFECT_TRACE: // 11 + for (i = 0; i < gBattlersCount; ++i) + { + if (gBattleMons[i].ability == ABILITY_TRACE && (gStatuses3[i] & STATUS3_TRACE)) + { + u8 target2; + + side = (GetBattlerPosition(i) ^ BIT_SIDE) & BIT_SIDE; // side of the opposing pokemon + target1 = GetBattlerAtPosition(side); + target2 = GetBattlerAtPosition(side + BIT_FLANK); + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + { + if (gBattleMons[target1].ability != 0 && gBattleMons[target1].hp != 0 + && gBattleMons[target2].ability != 0 && gBattleMons[target2].hp != 0) + { + gActiveBattler = GetBattlerAtPosition(((Random() & 1) * 2) | side); + gBattleMons[i].ability = gBattleMons[gActiveBattler].ability; + gLastUsedAbility = gBattleMons[gActiveBattler].ability; + ++effect; + } + else if (gBattleMons[target1].ability != 0 && gBattleMons[target1].hp != 0) + { + gActiveBattler = target1; + gBattleMons[i].ability = gBattleMons[gActiveBattler].ability; + gLastUsedAbility = gBattleMons[gActiveBattler].ability; + ++effect; + } + else if (gBattleMons[target2].ability != 0 && gBattleMons[target2].hp != 0) + { + gActiveBattler = target2; + gBattleMons[i].ability = gBattleMons[gActiveBattler].ability; + gLastUsedAbility = gBattleMons[gActiveBattler].ability; + ++effect; + } + } + else + { + gActiveBattler = target1; + if (gBattleMons[target1].ability && gBattleMons[target1].hp) + { + gBattleMons[i].ability = gBattleMons[target1].ability; + gLastUsedAbility = gBattleMons[target1].ability; + ++effect; + } + } + if (effect) + { + BattleScriptPushCursorAndCallback(BattleScript_TraceActivates); + gStatuses3[i] &= ~(STATUS3_TRACE); + gBattleScripting.battler = i; + PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, gActiveBattler, gBattlerPartyIndexes[gActiveBattler]) + PREPARE_ABILITY_BUFFER(gBattleTextBuff2, gLastUsedAbility) + break; + } + } + } + break; + case ABILITYEFFECT_INTIMIDATE2: // 10 + for (i = 0; i < gBattlersCount; ++i) + { + if (gBattleMons[i].ability == ABILITY_INTIMIDATE && (gStatuses3[i] & STATUS3_INTIMIDATE_POKES)) + { + gLastUsedAbility = ABILITY_INTIMIDATE; + gStatuses3[i] &= ~(STATUS3_INTIMIDATE_POKES); + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_IntimidateActivates; + gBattleStruct->intimidateBattler = i; + ++effect; + break; + } + } + break; + case ABILITYEFFECT_CHECK_OTHER_SIDE: // 12 + side = GetBattlerSide(battler); + for (i = 0; i < gBattlersCount; ++i) + { + if (GetBattlerSide(i) != side && gBattleMons[i].ability == ability) + { + gLastUsedAbility = ability; + effect = i + 1; + } + } + break; + case ABILITYEFFECT_CHECK_BATTLER_SIDE: // 13 + side = GetBattlerSide(battler); + for (i = 0; i < gBattlersCount; ++i) + { + if (GetBattlerSide(i) == side && gBattleMons[i].ability == ability) + { + gLastUsedAbility = ability; + effect = i + 1; + } + } + break; + case ABILITYEFFECT_FIELD_SPORT: // 14 + switch (gLastUsedAbility) + { + case 0xFD: + for (i = 0; i < gBattlersCount; ++i) + if (gStatuses3[i] & STATUS3_MUDSPORT) + effect = i + 1; + break; + case 0xFE: + for (i = 0; i < gBattlersCount; ++i) + if (gStatuses3[i] & STATUS3_WATERSPORT) + effect = i + 1; + break; + default: + for (i = 0; i < gBattlersCount; ++i) + { + if (gBattleMons[i].ability == ability) + { + gLastUsedAbility = ability; + effect = i + 1; + } + } + break; + } + break; + case ABILITYEFFECT_CHECK_ON_FIELD: // 19 + for (i = 0; i < gBattlersCount; ++i) + { + if (gBattleMons[i].ability == ability && gBattleMons[i].hp != 0) + { + gLastUsedAbility = ability; + effect = i + 1; + } + } + break; + case ABILITYEFFECT_CHECK_FIELD_EXCEPT_BATTLER: // 15 + side = GetBattlerSide(battler); + for (i = 0; i < gBattlersCount; ++i) + { + if (GetBattlerSide(i) != side && gBattleMons[i].ability == ability) + { + gLastUsedAbility = ability; + effect = i + 1; + break; + } + } + if (!effect) + { + for (i = 0; i < gBattlersCount; ++i) + { + if (gBattleMons[i].ability == ability && GetBattlerSide(i) == side && i != battler) + { + gLastUsedAbility = ability; + effect = i + 1; + } + } + } + break; + case ABILITYEFFECT_COUNT_OTHER_SIDE: // 16 + side = GetBattlerSide(battler); + for (i = 0; i < gBattlersCount; ++i) + { + if (GetBattlerSide(i) != side && gBattleMons[i].ability == ability) + { + gLastUsedAbility = ability; + ++effect; + } + } + break; + case ABILITYEFFECT_COUNT_BATTLER_SIDE: // 17 + side = GetBattlerSide(battler); + for (i = 0; i < gBattlersCount; ++i) + { + if (GetBattlerSide(i) == side && gBattleMons[i].ability == ability) + { + gLastUsedAbility = ability; + ++effect; + } + } + break; + case ABILITYEFFECT_COUNT_ON_FIELD: // 18 + for (i = 0; i < gBattlersCount; ++i) + { + if (gBattleMons[i].ability == ability && i != battler) + { + gLastUsedAbility = ability; + ++effect; + } + } + break; + } + if (effect && caseID < ABILITYEFFECT_CHECK_OTHER_SIDE && gLastUsedAbility != 0xFF) + RecordAbilityBattle(battler, gLastUsedAbility); + } + return effect; +} + +void BattleScriptExecute(const u8 *BS_ptr) +{ + gBattlescriptCurrInstr = BS_ptr; + gBattleResources->battleCallbackStack->function[gBattleResources->battleCallbackStack->size++] = gBattleMainFunc; + gBattleMainFunc = RunBattleScriptCommands_PopCallbacksStack; + gCurrentActionFuncId = 0; +} + +void BattleScriptPushCursorAndCallback(const u8 *BS_ptr) +{ + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BS_ptr; + gBattleResources->battleCallbackStack->function[gBattleResources->battleCallbackStack->size++] = gBattleMainFunc; + gBattleMainFunc = RunBattleScriptCommands; +} + +enum +{ + ITEM_NO_EFFECT, + ITEM_STATUS_CHANGE, + ITEM_EFFECT_OTHER, + ITEM_PP_CHANGE, + ITEM_HP_CHANGE, + ITEM_STATS_CHANGE, +}; + +u8 ItemBattleEffects(u8 caseID, u8 battlerId, bool8 moveTurn) +{ + int i = 0; + u8 effect = ITEM_NO_EFFECT; + u8 changedPP = 0; + u8 battlerHoldEffect, atkHoldEffect, defHoldEffect; + u8 battlerHoldEffectParam, atkHoldEffectParam, defHoldEffectParam; + u16 atkItem, defItem; + + gLastUsedItem = gBattleMons[battlerId].item; + if (gLastUsedItem == ITEM_ENIGMA_BERRY) + { + battlerHoldEffect = gEnigmaBerries[battlerId].holdEffect; + battlerHoldEffectParam = gEnigmaBerries[battlerId].holdEffectParam; + } + else + { + battlerHoldEffect = ItemId_GetHoldEffect(gLastUsedItem); + battlerHoldEffectParam = ItemId_GetHoldEffectParam(gLastUsedItem); + } + + atkItem = gBattleMons[gBattlerAttacker].item; + if (atkItem == ITEM_ENIGMA_BERRY) + { + atkHoldEffect = gEnigmaBerries[gBattlerAttacker].holdEffect; + atkHoldEffectParam = gEnigmaBerries[gBattlerAttacker].holdEffectParam; + } + else + { + atkHoldEffect = ItemId_GetHoldEffect(atkItem); + atkHoldEffectParam = ItemId_GetHoldEffectParam(atkItem); + } + + // def variables are unused + defItem = gBattleMons[gBattlerTarget].item; + if (defItem == ITEM_ENIGMA_BERRY) + { + defHoldEffect = gEnigmaBerries[gBattlerTarget].holdEffect; + defHoldEffectParam = gEnigmaBerries[gBattlerTarget].holdEffectParam; + } + else + { + defHoldEffect = ItemId_GetHoldEffect(defItem); + defHoldEffectParam = ItemId_GetHoldEffectParam(defItem); + } + switch (caseID) + { + case ITEMEFFECT_ON_SWITCH_IN: + switch (battlerHoldEffect) + { + case HOLD_EFFECT_DOUBLE_PRIZE: + gBattleStruct->moneyMultiplier = 2; + break; + case HOLD_EFFECT_RESTORE_STATS: + for (i = 0; i < NUM_BATTLE_STATS; ++i) + { + if (gBattleMons[battlerId].statStages[i] < 6) + { + gBattleMons[battlerId].statStages[i] = 6; + effect = ITEM_STATS_CHANGE; + } + } + if (effect) + { + gBattleScripting.battler = battlerId; + gPotentialItemEffectBattler = battlerId; + gActiveBattler = gBattlerAttacker = battlerId; + BattleScriptExecute(BattleScript_WhiteHerbEnd2); + } + break; + } + break; + case 1: + if (gBattleMons[battlerId].hp) + { + switch (battlerHoldEffect) + { + case HOLD_EFFECT_RESTORE_HP: + if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / 2 && !moveTurn) + { + gBattleMoveDamage = battlerHoldEffectParam; + if (gBattleMons[battlerId].hp + battlerHoldEffectParam > gBattleMons[battlerId].maxHP) + gBattleMoveDamage = gBattleMons[battlerId].maxHP - gBattleMons[battlerId].hp; + gBattleMoveDamage *= -1; + BattleScriptExecute(BattleScript_ItemHealHP_RemoveItem); + effect = 4; + } + break; + case HOLD_EFFECT_RESTORE_PP: + if (!moveTurn) + { + struct Pokemon *mon; + u8 ppBonuses; + u16 move; + + if (GetBattlerSide(battlerId) == B_SIDE_PLAYER) + mon = &gPlayerParty[gBattlerPartyIndexes[battlerId]]; + else + mon = &gEnemyParty[gBattlerPartyIndexes[battlerId]]; + for (i = 0; i < MAX_MON_MOVES; ++i) + { + move = GetMonData(mon, MON_DATA_MOVE1 + i); + changedPP = GetMonData(mon, MON_DATA_PP1 + i); + ppBonuses = GetMonData(mon, MON_DATA_PP_BONUSES); + if (move && changedPP == 0) + break; + } + if (i != MAX_MON_MOVES) + { + u8 maxPP = CalculatePPWithBonus(move, ppBonuses, i); + if (changedPP + battlerHoldEffectParam > maxPP) + changedPP = maxPP; + else + changedPP = changedPP + battlerHoldEffectParam; + PREPARE_MOVE_BUFFER(gBattleTextBuff1, move); + BattleScriptExecute(BattleScript_BerryPPHealEnd2); + BtlController_EmitSetMonData(0, i + REQUEST_PPMOVE1_BATTLE, 0, 1, &changedPP); + MarkBattlerForControllerExec(gActiveBattler); + effect = ITEM_PP_CHANGE; + } + } + break; + case HOLD_EFFECT_RESTORE_STATS: + for (i = 0; i < NUM_BATTLE_STATS; ++i) + { + if (gBattleMons[battlerId].statStages[i] < 6) + { + gBattleMons[battlerId].statStages[i] = 6; + effect = ITEM_STATS_CHANGE; + } + } + if (effect) + { + gBattleScripting.battler = battlerId; + gPotentialItemEffectBattler = battlerId; + gActiveBattler = gBattlerAttacker = battlerId; + BattleScriptExecute(BattleScript_WhiteHerbEnd2); + } + break; + case HOLD_EFFECT_LEFTOVERS: + if (gBattleMons[battlerId].hp < gBattleMons[battlerId].maxHP && !moveTurn) + { + gBattleMoveDamage = gBattleMons[battlerId].maxHP / 16; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + if (gBattleMons[battlerId].hp + gBattleMoveDamage > gBattleMons[battlerId].maxHP) + gBattleMoveDamage = gBattleMons[battlerId].maxHP - gBattleMons[battlerId].hp; + gBattleMoveDamage *= -1; + BattleScriptExecute(BattleScript_ItemHealHP_End2); + effect = ITEM_HP_CHANGE; + RecordItemEffectBattle(battlerId, battlerHoldEffect); + } + break; + case HOLD_EFFECT_CONFUSE_SPICY: + if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / 2 && !moveTurn) + { + PREPARE_FLAVOR_BUFFER(gBattleTextBuff1, FLAVOR_SPICY); + gBattleMoveDamage = gBattleMons[battlerId].maxHP / battlerHoldEffectParam; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + if (gBattleMons[battlerId].hp + gBattleMoveDamage > gBattleMons[battlerId].maxHP) + gBattleMoveDamage = gBattleMons[battlerId].maxHP - gBattleMons[battlerId].hp; + gBattleMoveDamage *= -1; + if (GetFlavorRelationByPersonality(gBattleMons[battlerId].personality, FLAVOR_SPICY) < 0) + BattleScriptExecute(BattleScript_BerryConfuseHealEnd2); + else + BattleScriptExecute(BattleScript_ItemHealHP_RemoveItem); + effect = ITEM_HP_CHANGE; + } + break; + case HOLD_EFFECT_CONFUSE_DRY: + if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / 2 && !moveTurn) + { + PREPARE_FLAVOR_BUFFER(gBattleTextBuff1, FLAVOR_DRY); + gBattleMoveDamage = gBattleMons[battlerId].maxHP / battlerHoldEffectParam; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + if (gBattleMons[battlerId].hp + gBattleMoveDamage > gBattleMons[battlerId].maxHP) + gBattleMoveDamage = gBattleMons[battlerId].maxHP - gBattleMons[battlerId].hp; + gBattleMoveDamage *= -1; + if (GetFlavorRelationByPersonality(gBattleMons[battlerId].personality, FLAVOR_DRY) < 0) + BattleScriptExecute(BattleScript_BerryConfuseHealEnd2); + else + BattleScriptExecute(BattleScript_ItemHealHP_RemoveItem); + effect = ITEM_HP_CHANGE; + } + break; + case HOLD_EFFECT_CONFUSE_SWEET: + if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / 2 && !moveTurn) + { + PREPARE_FLAVOR_BUFFER(gBattleTextBuff1, FLAVOR_SWEET); + gBattleMoveDamage = gBattleMons[battlerId].maxHP / battlerHoldEffectParam; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + if (gBattleMons[battlerId].hp + gBattleMoveDamage > gBattleMons[battlerId].maxHP) + gBattleMoveDamage = gBattleMons[battlerId].maxHP - gBattleMons[battlerId].hp; + gBattleMoveDamage *= -1; + if (GetFlavorRelationByPersonality(gBattleMons[battlerId].personality, FLAVOR_SWEET) < 0) + BattleScriptExecute(BattleScript_BerryConfuseHealEnd2); + else + BattleScriptExecute(BattleScript_ItemHealHP_RemoveItem); + effect = ITEM_HP_CHANGE; + } + break; + case HOLD_EFFECT_CONFUSE_BITTER: + if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / 2 && !moveTurn) + { + PREPARE_FLAVOR_BUFFER(gBattleTextBuff1, FLAVOR_BITTER); + gBattleMoveDamage = gBattleMons[battlerId].maxHP / battlerHoldEffectParam; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + if (gBattleMons[battlerId].hp + gBattleMoveDamage > gBattleMons[battlerId].maxHP) + gBattleMoveDamage = gBattleMons[battlerId].maxHP - gBattleMons[battlerId].hp; + gBattleMoveDamage *= -1; + if (GetFlavorRelationByPersonality(gBattleMons[battlerId].personality, FLAVOR_BITTER) < 0) + BattleScriptExecute(BattleScript_BerryConfuseHealEnd2); + else + BattleScriptExecute(BattleScript_ItemHealHP_RemoveItem); + effect = ITEM_HP_CHANGE; + } + break; + case HOLD_EFFECT_CONFUSE_SOUR: + if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / 2 && !moveTurn) + { + PREPARE_FLAVOR_BUFFER(gBattleTextBuff1, FLAVOR_SOUR); + gBattleMoveDamage = gBattleMons[battlerId].maxHP / battlerHoldEffectParam; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + if (gBattleMons[battlerId].hp + gBattleMoveDamage > gBattleMons[battlerId].maxHP) + gBattleMoveDamage = gBattleMons[battlerId].maxHP - gBattleMons[battlerId].hp; + gBattleMoveDamage *= -1; + if (GetFlavorRelationByPersonality(gBattleMons[battlerId].personality, FLAVOR_SOUR) < 0) + BattleScriptExecute(BattleScript_BerryConfuseHealEnd2); + else + BattleScriptExecute(BattleScript_ItemHealHP_RemoveItem); + effect = ITEM_HP_CHANGE; + } + break; + case HOLD_EFFECT_ATTACK_UP: + if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / battlerHoldEffectParam && !moveTurn && gBattleMons[battlerId].statStages[STAT_ATK] < 0xC) + { + PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_ATK); + PREPARE_STRING_BUFFER(gBattleTextBuff2, STRINGID_STATROSE); + gEffectBattler = battlerId; + SET_STATCHANGER(STAT_ATK, 1, FALSE); + gBattleScripting.animArg1 = 0xE + STAT_ATK; + gBattleScripting.animArg2 = 0; + BattleScriptExecute(BattleScript_BerryStatRaiseEnd2); + effect = ITEM_STATS_CHANGE; + } + break; + case HOLD_EFFECT_DEFENSE_UP: + if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / battlerHoldEffectParam && !moveTurn && gBattleMons[battlerId].statStages[STAT_DEF] < 0xC) + { + PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_DEF); + gEffectBattler = battlerId; + SET_STATCHANGER(STAT_DEF, 1, FALSE); + gBattleScripting.animArg1 = 0xE + STAT_DEF; + gBattleScripting.animArg2 = 0; + BattleScriptExecute(BattleScript_BerryStatRaiseEnd2); + effect = ITEM_STATS_CHANGE; + } + break; + case HOLD_EFFECT_SPEED_UP: + if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / battlerHoldEffectParam && !moveTurn && gBattleMons[battlerId].statStages[STAT_SPEED] < 0xC) + { + PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_SPEED); + gEffectBattler = battlerId; + SET_STATCHANGER(STAT_SPEED, 1, FALSE); + gBattleScripting.animArg1 = 0xE + STAT_SPEED; + gBattleScripting.animArg2 = 0; + BattleScriptExecute(BattleScript_BerryStatRaiseEnd2); + effect = ITEM_STATS_CHANGE; + } + break; + case HOLD_EFFECT_SP_ATTACK_UP: + if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / battlerHoldEffectParam && !moveTurn && gBattleMons[battlerId].statStages[STAT_SPATK] < 0xC) + { + PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_SPATK); + gEffectBattler = battlerId; + SET_STATCHANGER(STAT_SPATK, 1, FALSE); + gBattleScripting.animArg1 = 0xE + STAT_SPATK; + gBattleScripting.animArg2 = 0; + BattleScriptExecute(BattleScript_BerryStatRaiseEnd2); + effect = ITEM_STATS_CHANGE; + } + break; + case HOLD_EFFECT_SP_DEFENSE_UP: + if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / battlerHoldEffectParam && !moveTurn && gBattleMons[battlerId].statStages[STAT_SPDEF] < 0xC) + { + PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_SPDEF); + gEffectBattler = battlerId; + SET_STATCHANGER(STAT_SPDEF, 1, FALSE); + gBattleScripting.animArg1 = 0xE + STAT_SPDEF; + gBattleScripting.animArg2 = 0; + BattleScriptExecute(BattleScript_BerryStatRaiseEnd2); + effect = ITEM_STATS_CHANGE; + } + break; + case HOLD_EFFECT_CRITICAL_UP: + if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / battlerHoldEffectParam && !moveTurn && !(gBattleMons[battlerId].status2 & STATUS2_FOCUS_ENERGY)) + { + gBattleMons[battlerId].status2 |= STATUS2_FOCUS_ENERGY; + BattleScriptExecute(BattleScript_BerryFocusEnergyEnd2); + effect = ITEM_EFFECT_OTHER; + } + break; + case HOLD_EFFECT_RANDOM_STAT_UP: + if (!moveTurn && gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / battlerHoldEffectParam) + { + for (i = 0; i < 5 && gBattleMons[battlerId].statStages[STAT_ATK + i] >= 0xC; ++i); + if (i != 5) + { + do + i = Random() % 5; + while (gBattleMons[battlerId].statStages[STAT_ATK + i] == 0xC); + PREPARE_STAT_BUFFER(gBattleTextBuff1, i + 1); + gBattleTextBuff2[0] = B_BUFF_PLACEHOLDER_BEGIN; + gBattleTextBuff2[1] = B_BUFF_STRING; + gBattleTextBuff2[2] = STRINGID_STATSHARPLY; + gBattleTextBuff2[3] = STRINGID_STATSHARPLY >> 8; + gBattleTextBuff2[4] = B_BUFF_STRING; + gBattleTextBuff2[5] = STRINGID_STATROSE; + gBattleTextBuff2[6] = STRINGID_STATROSE >> 8; + gBattleTextBuff2[7] = EOS; + gEffectBattler = battlerId; + SET_STATCHANGER(i + 1, 2, FALSE); + gBattleScripting.animArg1 = 0x21 + i + 6; + gBattleScripting.animArg2 = 0; + BattleScriptExecute(BattleScript_BerryStatRaiseEnd2); + effect = ITEM_STATS_CHANGE; + } + } + break; + case HOLD_EFFECT_CURE_PAR: + if (gBattleMons[battlerId].status1 & STATUS1_PARALYSIS) + { + gBattleMons[battlerId].status1 &= ~(STATUS1_PARALYSIS); + BattleScriptExecute(BattleScript_BerryCurePrlzEnd2); + effect = ITEM_STATUS_CHANGE; + } + break; + case HOLD_EFFECT_CURE_PSN: + if (gBattleMons[battlerId].status1 & STATUS1_PSN_ANY) + { + gBattleMons[battlerId].status1 &= ~(STATUS1_PSN_ANY | STATUS1_TOXIC_COUNTER); + BattleScriptExecute(BattleScript_BerryCurePsnEnd2); + effect = ITEM_STATUS_CHANGE; + } + break; + case HOLD_EFFECT_CURE_BRN: + if (gBattleMons[battlerId].status1 & STATUS1_BURN) + { + gBattleMons[battlerId].status1 &= ~(STATUS1_BURN); + BattleScriptExecute(BattleScript_BerryCureBrnEnd2); + effect = ITEM_STATUS_CHANGE; + } + break; + case HOLD_EFFECT_CURE_FRZ: + if (gBattleMons[battlerId].status1 & STATUS1_FREEZE) + { + gBattleMons[battlerId].status1 &= ~(STATUS1_FREEZE); + BattleScriptExecute(BattleScript_BerryCureFrzEnd2); + effect = ITEM_STATUS_CHANGE; + } + break; + case HOLD_EFFECT_CURE_SLP: + if (gBattleMons[battlerId].status1 & STATUS1_SLEEP) + { + gBattleMons[battlerId].status1 &= ~(STATUS1_SLEEP); + gBattleMons[battlerId].status2 &= ~(STATUS2_NIGHTMARE); + BattleScriptExecute(BattleScript_BerryCureSlpEnd2); + effect = ITEM_STATUS_CHANGE; + } + break; + case HOLD_EFFECT_CURE_CONFUSION: + if (gBattleMons[battlerId].status2 & STATUS2_CONFUSION) + { + gBattleMons[battlerId].status2 &= ~(STATUS2_CONFUSION); + BattleScriptExecute(BattleScript_BerryCureConfusionEnd2); + effect = ITEM_EFFECT_OTHER; + } + break; + case HOLD_EFFECT_CURE_STATUS: + if (gBattleMons[battlerId].status1 & STATUS1_ANY || gBattleMons[battlerId].status2 & STATUS2_CONFUSION) + { + i = 0; + if (gBattleMons[battlerId].status1 & STATUS1_PSN_ANY) + { + StringCopy(gBattleTextBuff1, gStatusConditionString_PoisonJpn); + ++i; + } + if (gBattleMons[battlerId].status1 & STATUS1_SLEEP) + { + gBattleMons[battlerId].status2 &= ~(STATUS2_NIGHTMARE); + StringCopy(gBattleTextBuff1, gStatusConditionString_SleepJpn); + ++i; + } + if (gBattleMons[battlerId].status1 & STATUS1_PARALYSIS) + { + StringCopy(gBattleTextBuff1, gStatusConditionString_ParalysisJpn); + ++i; + } + if (gBattleMons[battlerId].status1 & STATUS1_BURN) + { + StringCopy(gBattleTextBuff1, gStatusConditionString_BurnJpn); + ++i; + } + if (gBattleMons[battlerId].status1 & STATUS1_FREEZE) + { + StringCopy(gBattleTextBuff1, gStatusConditionString_IceJpn); + ++i; + } + if (gBattleMons[battlerId].status2 & STATUS2_CONFUSION) + { + StringCopy(gBattleTextBuff1, gStatusConditionString_ConfusionJpn); + ++i; + } + if (!(i > 1)) + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + else + gBattleCommunication[MULTISTRING_CHOOSER] = 1; + gBattleMons[battlerId].status1 = 0; + gBattleMons[battlerId].status2 &= ~(STATUS2_CONFUSION); + BattleScriptExecute(BattleScript_BerryCureChosenStatusEnd2); + effect = ITEM_STATUS_CHANGE; + } + break; + case HOLD_EFFECT_CURE_ATTRACT: + if (gBattleMons[battlerId].status2 & STATUS2_INFATUATION) + { + gBattleMons[battlerId].status2 &= ~(STATUS2_INFATUATION); + StringCopy(gBattleTextBuff1, gStatusConditionString_LoveJpn); + BattleScriptExecute(BattleScript_BerryCureChosenStatusEnd2); + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + effect = ITEM_EFFECT_OTHER; + } + break; + } + if (effect) + { + gBattleScripting.battler = battlerId; + gPotentialItemEffectBattler = battlerId; + gActiveBattler = gBattlerAttacker = battlerId; + switch (effect) + { + case ITEM_STATUS_CHANGE: + BtlController_EmitSetMonData(0, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[battlerId].status1); + MarkBattlerForControllerExec(gActiveBattler); + break; + case ITEM_PP_CHANGE: + if (!(gBattleMons[battlerId].status2 & STATUS2_TRANSFORMED) && !(gDisableStructs[battlerId].mimickedMoves & gBitTable[i])) + gBattleMons[battlerId].pp[i] = changedPP; + break; + } + } + } + break; + case 2: + break; + case ITEMEFFECT_MOVE_END: + for (battlerId = 0; battlerId < gBattlersCount; ++battlerId) + { + gLastUsedItem = gBattleMons[battlerId].item; + if (gBattleMons[battlerId].item == ITEM_ENIGMA_BERRY) + { + battlerHoldEffect = gEnigmaBerries[battlerId].holdEffect; + battlerHoldEffectParam = gEnigmaBerries[battlerId].holdEffectParam; + } + else + { + battlerHoldEffect = ItemId_GetHoldEffect(gLastUsedItem); + battlerHoldEffectParam = ItemId_GetHoldEffectParam(gLastUsedItem); + } + switch (battlerHoldEffect) + { + case HOLD_EFFECT_CURE_PAR: + if (gBattleMons[battlerId].status1 & STATUS1_PARALYSIS) + { + gBattleMons[battlerId].status1 &= ~(STATUS1_PARALYSIS); + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_BerryCureParRet; + effect = ITEM_STATUS_CHANGE; + } + break; + case HOLD_EFFECT_CURE_PSN: + if (gBattleMons[battlerId].status1 & STATUS1_PSN_ANY) + { + gBattleMons[battlerId].status1 &= ~(STATUS1_PSN_ANY | STATUS1_TOXIC_COUNTER); + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_BerryCurePsnRet; + effect = ITEM_STATUS_CHANGE; + } + break; + case HOLD_EFFECT_CURE_BRN: + if (gBattleMons[battlerId].status1 & STATUS1_BURN) + { + gBattleMons[battlerId].status1 &= ~(STATUS1_BURN); + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_BerryCureBrnRet; + effect = ITEM_STATUS_CHANGE; + } + break; + case HOLD_EFFECT_CURE_FRZ: + if (gBattleMons[battlerId].status1 & STATUS1_FREEZE) + { + gBattleMons[battlerId].status1 &= ~(STATUS1_FREEZE); + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_BerryCureFrzRet; + effect = ITEM_STATUS_CHANGE; + } + break; + case HOLD_EFFECT_CURE_SLP: + if (gBattleMons[battlerId].status1 & STATUS1_SLEEP) + { + gBattleMons[battlerId].status1 &= ~(STATUS1_SLEEP); + gBattleMons[battlerId].status2 &= ~(STATUS2_NIGHTMARE); + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_BerryCureSlpRet; + effect = ITEM_STATUS_CHANGE; + } + break; + case HOLD_EFFECT_CURE_CONFUSION: + if (gBattleMons[battlerId].status2 & STATUS2_CONFUSION) + { + gBattleMons[battlerId].status2 &= ~(STATUS2_CONFUSION); + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_BerryCureConfusionRet; + effect = ITEM_EFFECT_OTHER; + } + break; + case HOLD_EFFECT_CURE_ATTRACT: + if (gBattleMons[battlerId].status2 & STATUS2_INFATUATION) + { + gBattleMons[battlerId].status2 &= ~(STATUS2_INFATUATION); + StringCopy(gBattleTextBuff1, gStatusConditionString_LoveJpn); + BattleScriptPushCursor(); + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + gBattlescriptCurrInstr = BattleScript_BerryCureChosenStatusRet; + effect = ITEM_EFFECT_OTHER; + } + break; + case HOLD_EFFECT_CURE_STATUS: + if (gBattleMons[battlerId].status1 & STATUS1_ANY || gBattleMons[battlerId].status2 & STATUS2_CONFUSION) + { + if (gBattleMons[battlerId].status1 & STATUS1_PSN_ANY) + { + StringCopy(gBattleTextBuff1, gStatusConditionString_PoisonJpn); + } + if (gBattleMons[battlerId].status1 & STATUS1_SLEEP) + { + gBattleMons[battlerId].status2 &= ~(STATUS2_NIGHTMARE); + StringCopy(gBattleTextBuff1, gStatusConditionString_SleepJpn); + } + if (gBattleMons[battlerId].status1 & STATUS1_PARALYSIS) + { + StringCopy(gBattleTextBuff1, gStatusConditionString_ParalysisJpn); + } + if (gBattleMons[battlerId].status1 & STATUS1_BURN) + { + StringCopy(gBattleTextBuff1, gStatusConditionString_BurnJpn); + } + if (gBattleMons[battlerId].status1 & STATUS1_FREEZE) + { + StringCopy(gBattleTextBuff1, gStatusConditionString_IceJpn); + } + if (gBattleMons[battlerId].status2 & STATUS2_CONFUSION) + { + StringCopy(gBattleTextBuff1, gStatusConditionString_ConfusionJpn); + } + gBattleMons[battlerId].status1 = 0; + gBattleMons[battlerId].status2 &= ~(STATUS2_CONFUSION); + BattleScriptPushCursor(); + gBattleCommunication[MULTISTRING_CHOOSER] = 0; + gBattlescriptCurrInstr = BattleScript_BerryCureChosenStatusRet; + effect = ITEM_STATUS_CHANGE; + } + break; + case HOLD_EFFECT_RESTORE_STATS: + for (i = 0; i < NUM_BATTLE_STATS; ++i) + { + if (gBattleMons[battlerId].statStages[i] < 6) + { + gBattleMons[battlerId].statStages[i] = 6; + effect = ITEM_STATS_CHANGE; + } + } + if (effect) + { + gBattleScripting.battler = battlerId; + gPotentialItemEffectBattler = battlerId; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_WhiteHerbRet; + return effect; + } + break; + } + if (effect) + { + gBattleScripting.battler = battlerId; + gPotentialItemEffectBattler = battlerId; + gActiveBattler = battlerId; + BtlController_EmitSetMonData(0, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[gActiveBattler].status1); + MarkBattlerForControllerExec(gActiveBattler); + break; + } + } + break; + case ITEMEFFECT_KINGSROCK_SHELLBELL: + if (gBattleMoveDamage) + { + switch (atkHoldEffect) + { + case HOLD_EFFECT_FLINCH: + if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) + && TARGET_TURN_DAMAGED + && (Random() % 100) < battlerHoldEffectParam + && gBattleMoves[gCurrentMove].flags & FLAG_KINGSROCK_AFFECTED + && gBattleMons[gBattlerTarget].hp) + { + gBattleCommunication[MOVE_EFFECT_BYTE] = MOVE_EFFECT_FLINCH; + BattleScriptPushCursor(); + SetMoveEffect(0, 0); + BattleScriptPop(); + } + break; + case HOLD_EFFECT_SHELL_BELL: + if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) + && gSpecialStatuses[gBattlerTarget].dmg != 0 + && gSpecialStatuses[gBattlerTarget].dmg != 0xFFFF + && gBattlerAttacker != gBattlerTarget + && gBattleMons[gBattlerAttacker].hp != gBattleMons[gBattlerAttacker].maxHP + && gBattleMons[gBattlerAttacker].hp != 0) + { + gLastUsedItem = atkItem; + gPotentialItemEffectBattler = gBattlerAttacker; + gBattleScripting.battler = gBattlerAttacker; + gBattleMoveDamage = (gSpecialStatuses[gBattlerTarget].dmg / atkHoldEffectParam) * -1; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = -1; + gSpecialStatuses[gBattlerTarget].dmg = 0; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_ItemHealHP_Ret; + ++effect; + } + break; + } + } + break; + } + return effect; +} + +void ClearFuryCutterDestinyBondGrudge(u8 battlerId) +{ + gDisableStructs[battlerId].furyCutterCounter = 0; + gBattleMons[battlerId].status2 &= ~(STATUS2_DESTINY_BOND); + gStatuses3[battlerId] &= ~(STATUS3_GRUDGE); +} + +void HandleAction_RunBattleScript(void) // identical to RunBattleScriptCommands +{ + if (!gBattleControllerExecFlags) + gBattleScriptingCommandsTable[*gBattlescriptCurrInstr](); +} + +u8 GetMoveTarget(u16 move, u8 setTarget) +{ + u8 targetBattler = 0; + u8 moveTarget; + u8 side; + + if (setTarget) + moveTarget = setTarget - 1; + else + moveTarget = gBattleMoves[move].target; + switch (moveTarget) + { + case MOVE_TARGET_SELECTED: + side = GetBattlerSide(gBattlerAttacker) ^ BIT_SIDE; + if (gSideTimers[side].followmeTimer && gBattleMons[gSideTimers[side].followmeTarget].hp) + targetBattler = gSideTimers[side].followmeTarget; + else + { + side = GetBattlerSide(gBattlerAttacker); + do + { + targetBattler = Random() % gBattlersCount; + } while (targetBattler == gBattlerAttacker || side == GetBattlerSide(targetBattler) || gAbsentBattlerFlags & gBitTable[targetBattler]); + if (gBattleMoves[move].type == TYPE_ELECTRIC + && AbilityBattleEffects(ABILITYEFFECT_COUNT_OTHER_SIDE, gBattlerAttacker, ABILITY_LIGHTNING_ROD, 0, 0) + && gBattleMons[targetBattler].ability != ABILITY_LIGHTNING_ROD) + { + targetBattler ^= BIT_FLANK; + RecordAbilityBattle(targetBattler, gBattleMons[targetBattler].ability); + gSpecialStatuses[targetBattler].lightningRodRedirected = 1; + } + } + break; + case MOVE_TARGET_DEPENDS: + case MOVE_TARGET_BOTH: + case MOVE_TARGET_FOES_AND_ALLY: + case MOVE_TARGET_OPPONENTS_FIELD: + targetBattler = GetBattlerAtPosition((GetBattlerPosition(gBattlerAttacker) & BIT_SIDE) ^ BIT_SIDE); + if (gAbsentBattlerFlags & gBitTable[targetBattler]) + targetBattler ^= BIT_FLANK; + break; + case MOVE_TARGET_RANDOM: + side = GetBattlerSide(gBattlerAttacker) ^ BIT_SIDE; + if (gSideTimers[side].followmeTimer && gBattleMons[gSideTimers[side].followmeTarget].hp) + targetBattler = gSideTimers[side].followmeTarget; + else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && moveTarget & MOVE_TARGET_RANDOM) + { + if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER) + { + if (Random() & 1) + targetBattler = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); + else + targetBattler = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); + } + else + { + if (Random() & 1) + targetBattler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); + else + targetBattler = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT); + } + if (gAbsentBattlerFlags & gBitTable[targetBattler]) + targetBattler ^= BIT_FLANK; + } + else + targetBattler = GetBattlerAtPosition((GetBattlerPosition(gBattlerAttacker) & BIT_SIDE) ^ BIT_SIDE); + break; + case MOVE_TARGET_USER_OR_SELECTED: + case MOVE_TARGET_USER: + targetBattler = gBattlerAttacker; + break; + } + *(gBattleStruct->moveTarget + gBattlerAttacker) = targetBattler; + return targetBattler; +} + +static bool32 HasObedientBitSet(u8 battlerId) +{ + if (GetBattlerSide(battlerId) == B_SIDE_OPPONENT + || (GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerId]], MON_DATA_SPECIES, NULL) != SPECIES_DEOXYS + && GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerId]], MON_DATA_SPECIES, NULL) != SPECIES_MEW)) + return TRUE; + return GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerId]], MON_DATA_OBEDIENCE, NULL); +} + +u8 IsMonDisobedient(void) +{ + s32 rnd; + s32 calc; + u8 obedienceLevel = 0; + + if ((gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_POKEDUDE)) || GetBattlerSide(gBattlerAttacker) == B_SIDE_OPPONENT) + return 0; + if (HasObedientBitSet(gBattlerAttacker)) // only if species is Mew or Deoxys + { + if (!IsOtherTrainer(gBattleMons[gBattlerAttacker].otId, gBattleMons[gBattlerAttacker].otName) || FlagGet(FLAG_0x827)) + return 0; + obedienceLevel = 10; + if (FlagGet(FLAG_0x821)) + obedienceLevel = 30; + if (FlagGet(FLAG_0x823)) + obedienceLevel = 50; + if (FlagGet(FLAG_0x825)) + obedienceLevel = 70; + } + if (gBattleMons[gBattlerAttacker].level <= obedienceLevel) + return 0; + rnd = (Random() & 255); + calc = (gBattleMons[gBattlerAttacker].level + obedienceLevel) * rnd >> 8; + if (calc < obedienceLevel) + return 0; + // is not obedient + if (gCurrentMove == MOVE_RAGE) + gBattleMons[gBattlerAttacker].status2 &= ~(STATUS2_RAGE); + if (gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP && (gCurrentMove == MOVE_SNORE || gCurrentMove == MOVE_SLEEP_TALK)) + { + gBattlescriptCurrInstr = BattleScript_IgnoresWhileAsleep; + return 1; + } + rnd = (Random() & 255); + calc = (gBattleMons[gBattlerAttacker].level + obedienceLevel) * rnd >> 8; + if (calc < obedienceLevel && gCurrentMove != MOVE_FOCUS_PUNCH) // Additional check for focus punch in FR + { + calc = CheckMoveLimitations(gBattlerAttacker, gBitTable[gCurrMovePos], 0xFF); + if (calc == 0xF) // all moves cannot be used + { + gBattleCommunication[MULTISTRING_CHOOSER] = Random() & 3; + gBattlescriptCurrInstr = BattleScript_MoveUsedLoafingAround; + return 1; + } + else // use a random move + { + do + gCurrMovePos = gChosenMovePos = Random() & 3; + while (gBitTable[gCurrMovePos] & calc); + gCalledMove = gBattleMons[gBattlerAttacker].moves[gCurrMovePos]; + gBattlescriptCurrInstr = BattleScript_IgnoresAndUsesRandomMove; + gBattlerTarget = GetMoveTarget(gCalledMove, 0); + gHitMarker |= HITMARKER_x200000; + return 2; + } + } + else + { + obedienceLevel = gBattleMons[gBattlerAttacker].level - obedienceLevel; + calc = (Random() & 255); + if (calc < obedienceLevel && !(gBattleMons[gBattlerAttacker].status1 & STATUS1_ANY) && gBattleMons[gBattlerAttacker].ability != ABILITY_VITAL_SPIRIT && gBattleMons[gBattlerAttacker].ability != ABILITY_INSOMNIA) + { + // try putting asleep + int i; + + for (i = 0; i < gBattlersCount; ++i) + if (gBattleMons[i].status2 & STATUS2_UPROAR) + break; + if (i == gBattlersCount) + { + gBattlescriptCurrInstr = BattleScript_IgnoresAndFallsAsleep; + return 1; + } + } + calc -= obedienceLevel; + if (calc < obedienceLevel) + { + gBattleMoveDamage = CalculateBaseDamage(&gBattleMons[gBattlerAttacker], &gBattleMons[gBattlerAttacker], MOVE_POUND, 0, 40, 0, gBattlerAttacker, gBattlerAttacker); + gBattlerTarget = gBattlerAttacker; + gBattlescriptCurrInstr = BattleScript_IgnoresAndHitsItself; + gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE; + return 2; + } + else + { + gBattleCommunication[MULTISTRING_CHOOSER] = Random() & 3; + gBattlescriptCurrInstr = BattleScript_MoveUsedLoafingAround; + return 1; + } + } +} diff --git a/src/battle_util2.c b/src/battle_util2.c new file mode 100644 index 000000000..6a3f3525b --- /dev/null +++ b/src/battle_util2.c @@ -0,0 +1,102 @@ +#include "global.h" +#include "bg.h" +#include "battle.h" +#include "pokemon.h" +#include "malloc.h" +#include "trainer_tower.h" +#include "battle_util2.h" + +void AllocateBattleResources(void) +{ + if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_TOWER) + InitTrainerTowerBattleStruct(); + if (gBattleTypeFlags & BATTLE_TYPE_POKEDUDE) + { + void **ptr = gUnknown_3005EE0; + s32 i = 3; + + do + *ptr++ = AllocZeroed(8); + while (--i >= 0); + } + gBattleStruct = AllocZeroed(sizeof(*gBattleStruct)); + gBattleResources = AllocZeroed(sizeof(*gBattleResources)); + gBattleResources->secretBase = AllocZeroed(sizeof(*gBattleResources->secretBase)); + gBattleResources->flags = AllocZeroed(sizeof(*gBattleResources->flags)); + gBattleResources->battleScriptsStack = AllocZeroed(sizeof(*gBattleResources->battleScriptsStack)); + gBattleResources->battleCallbackStack = AllocZeroed(sizeof(*gBattleResources->battleCallbackStack)); + gBattleResources->beforeLvlUp = AllocZeroed(sizeof(*gBattleResources->beforeLvlUp)); + gBattleResources->ai = AllocZeroed(sizeof(*gBattleResources->ai)); + gBattleResources->battleHistory = AllocZeroed(sizeof(*gBattleResources->battleHistory)); + gBattleResources->AI_ScriptsStack = AllocZeroed(sizeof(*gBattleResources->AI_ScriptsStack)); + gLinkBattleSendBuffer = AllocZeroed(BATTLE_BUFFER_LINK_SIZE); + gLinkBattleRecvBuffer = AllocZeroed(BATTLE_BUFFER_LINK_SIZE); + gUnknown_2022BB8 = AllocZeroed(0x2000); + gUnknown_2022BBC = AllocZeroed(0x1000); + SetBgTilemapBuffer(1, gUnknown_2022BBC); + SetBgTilemapBuffer(2, gUnknown_2022BBC); +} + +void FreeBattleResources(void) +{ + if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_TOWER) + FreeTrainerTowerBattleStruct(); + if (gBattleTypeFlags & BATTLE_TYPE_POKEDUDE) + { + void **ptr = gUnknown_3005EE0; + void *nullPtr = NULL; + s32 i = 3; + + do + { + Free(*ptr); + *ptr++ = nullPtr; + } + while (--i >= 0); + } + if (gBattleResources != NULL) + { + FREE_AND_SET_NULL(gBattleStruct); + FREE_AND_SET_NULL(gBattleResources->secretBase); + FREE_AND_SET_NULL(gBattleResources->flags); + FREE_AND_SET_NULL(gBattleResources->battleScriptsStack); + FREE_AND_SET_NULL(gBattleResources->battleCallbackStack); + FREE_AND_SET_NULL(gBattleResources->beforeLvlUp); + FREE_AND_SET_NULL(gBattleResources->ai); + FREE_AND_SET_NULL(gBattleResources->battleHistory); + FREE_AND_SET_NULL(gBattleResources->AI_ScriptsStack); + FREE_AND_SET_NULL(gBattleResources); + FREE_AND_SET_NULL(gLinkBattleSendBuffer); + FREE_AND_SET_NULL(gLinkBattleRecvBuffer); + FREE_AND_SET_NULL(gUnknown_2022BB8); + FREE_AND_SET_NULL(gUnknown_2022BBC); + } +} + +void AdjustFriendshipOnBattleFaint(u8 battlerId) +{ + u8 opposingBattlerId, opposingBattlerId2; + + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + { + opposingBattlerId = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); + opposingBattlerId2 = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); + if (gBattleMons[opposingBattlerId2].level > gBattleMons[opposingBattlerId].level) + opposingBattlerId = opposingBattlerId2; + } + else + { + opposingBattlerId = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); + } + if (gBattleMons[opposingBattlerId].level > gBattleMons[battlerId].level) + { + if (gBattleMons[opposingBattlerId].level - gBattleMons[battlerId].level > 29) + AdjustFriendship(&gPlayerParty[gBattlerPartyIndexes[battlerId]], 9); + else + AdjustFriendship(&gPlayerParty[gBattlerPartyIndexes[battlerId]], 7); + } + else + { + AdjustFriendship(&gPlayerParty[gBattlerPartyIndexes[battlerId]], 7); + } +} diff --git a/src/data/items.json b/src/data/items.json index b976e4c3e..48d614a80 100644 --- a/src/data/items.json +++ b/src/data/items.json @@ -6059,4 +6059,4 @@ "secondaryId": 0 } ] -}
\ No newline at end of file +} diff --git a/src/item_pc.c b/src/item_pc.c index 3b60a5bee..24f6afed2 100644 --- a/src/item_pc.c +++ b/src/item_pc.c @@ -1,6 +1,6 @@ #include "global.h" #include "bg.h" -#include "data2.h" +#include "data.h" #include "decompress.h" #include "gpu_regs.h" #include "graphics.h" diff --git a/src/mevent.c b/src/mevent.c index fc0c1f2d4..d0d7f59aa 100644 --- a/src/mevent.c +++ b/src/mevent.c @@ -134,7 +134,7 @@ static void ResetTTDataBuffer(void) { memset(gDecompressionBuffer, 0, 0x2000); gLinkType = 0x5502; - sub_8009804(); + OpenLink(); SetSuppressLinkErrorMessage(TRUE); } @@ -156,7 +156,7 @@ bool32 sub_81436EC(void) static bool32 IsEReaderConnectionSane(void) { - if (sub_800AA48() && GetLinkPlayerCount_2() == 2) + if (IsLinkMaster() && GetLinkPlayerCount_2() == 2) return TRUE; return FALSE; } @@ -171,7 +171,7 @@ u32 sub_8143770(u8 * r4, u16 * r5) switch (*r4) { case 0: - if (sub_800AA48() && GetLinkPlayerCount_2() > 1) + if (IsLinkMaster() && GetLinkPlayerCount_2() > 1) { *r4 = 1; ; @@ -193,7 +193,7 @@ u32 sub_8143770(u8 * r4, u16 * r5) if (GetLinkPlayerCount_2() == 2) { PlaySE(SE_TOY_G); - sub_800A5BC(); + CheckShouldAdvanceLinkState(); *r5 = 0; *r4 = 3; } diff --git a/src/mevent_server_helpers.c b/src/mevent_server_helpers.c index 35efa68c8..6df5207f0 100644 --- a/src/mevent_server_helpers.c +++ b/src/mevent_server_helpers.c @@ -155,7 +155,7 @@ static bool32 mevent_send_func(struct mevent_srv_sub * svr) switch (svr->seqno) { case 0: - if (sub_800A4BC()) + if (IsLinkTaskFinished()) { header.ident = svr->sendIdent; header.size = svr->sendSize; @@ -167,7 +167,7 @@ static bool32 mevent_send_func(struct mevent_srv_sub * svr) } break; case 1: - if (sub_800A4BC()) + if (IsLinkTaskFinished()) { if (mevent_has_received(svr->sendPlayerNo)) { @@ -189,7 +189,7 @@ static bool32 mevent_send_func(struct mevent_srv_sub * svr) } break; case 2: - if (sub_800A4BC()) + if (IsLinkTaskFinished()) { if (CalcCRC16WithTable(svr->sendBfr, svr->sendSize) != svr->sendCRC) sub_80FA190(); diff --git a/src/pokemon.c b/src/pokemon.c index 6f1df249f..e476ab456 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -5,21 +5,20 @@ #include "random.h" #include "main.h" #include "text.h" -#include "data2.h" +#include "data.h" #include "string_util.h" #include "battle.h" -#include "battle_2.h" +#include "battle_main.h" #include "item.h" #include "event_data.h" #include "util.h" #include "pokemon_storage_system.h" -#include "data2.h" +#include "data.h" #include "battle_gfx_sfx_util.h" #include "battle_controllers.h" #include "evolution_scene.h" #include "battle_message.h" #include "battle_util.h" -#include "battle_ai_script_commands.h" #include "link.h" #include "m4a.h" #include "sound.h" @@ -1792,7 +1791,7 @@ void CreateBoxMon(struct BoxPokemon *boxMon, u16 species, u8 level, u8 fixedIV, if (gBaseStats[species].abilities[1]) { value = personality & 1; - SetBoxMonData(boxMon, MON_DATA_ALT_ABILITY, &value); + SetBoxMonData(boxMon, MON_DATA_ABILITY_NUM, &value); } GiveBoxMonInitialMoveset(boxMon); @@ -1941,8 +1940,8 @@ void CreateBattleTowerMon(struct Pokemon *mon, struct BattleTowerPokemon *src) SetMonData(mon, MON_DATA_SPEED_EV, &src->speedEV); SetMonData(mon, MON_DATA_SPATK_EV, &src->spAttackEV); SetMonData(mon, MON_DATA_SPDEF_EV, &src->spDefenseEV); - value = src->altAbility; - SetMonData(mon, MON_DATA_ALT_ABILITY, &value); + value = src->abilityNum; + SetMonData(mon, MON_DATA_ABILITY_NUM, &value); value = src->hpIV; SetMonData(mon, MON_DATA_HP_IV, &value); value = src->attackIV; @@ -1998,7 +1997,7 @@ void sub_803E23C(struct Pokemon *mon, struct BattleTowerPokemon *dest) dest->speedIV = GetMonData(mon, MON_DATA_SPEED_IV, NULL); dest->spAttackIV = GetMonData(mon, MON_DATA_SPATK_IV, NULL); dest->spDefenseIV = GetMonData(mon, MON_DATA_SPDEF_IV, NULL); - dest->altAbility = GetMonData(mon, MON_DATA_ALT_ABILITY, NULL); + dest->abilityNum = GetMonData(mon, MON_DATA_ABILITY_NUM, NULL); dest->personality = GetMonData(mon, MON_DATA_PERSONALITY, NULL); GetMonData(mon, MON_DATA_NICKNAME, dest->nickname); } @@ -2553,7 +2552,7 @@ s32 CalculateBaseDamage(struct BattlePokemon *attacker, struct BattlePokemon *de damage /= 2; // sunny - if (gBattleWeather & WEATHER_SUNNY_ANY) + if (gBattleWeather & WEATHER_SUN_ANY) { switch (type) { @@ -2568,7 +2567,7 @@ s32 CalculateBaseDamage(struct BattlePokemon *attacker, struct BattlePokemon *de } // flash fire triggered - if ((gBattleResources->flags->flags[battlerIdAtk] & UNKNOWN_FLAG_FLASH_FIRE) && type == TYPE_FIRE) + if ((gBattleResources->flags->flags[battlerIdAtk] & RESOURCE_FLAG_FLASH_FIRE) && type == TYPE_FIRE) damage = (15 * damage) / 10; } @@ -2592,7 +2591,7 @@ u8 CountAliveMonsInBattle(u8 caseId) case BATTLE_ALIVE_ATK_SIDE: for (i = 0; i < 4; i++) { - if (GetBattlerSide(i) == GetBattlerSide(sBattler_AI) && !(gAbsentBattlerFlags & gBitTable[i])) + if (GetBattlerSide(i) == GetBattlerSide(gBattlerAttacker) && !(gAbsentBattlerFlags & gBitTable[i])) retVal++; } break; @@ -3100,8 +3099,8 @@ u32 GetBoxMonData(struct BoxPokemon *boxMon, s32 field, u8 *data) case MON_DATA_IS_EGG: retVal = substruct3->isEgg; break; - case MON_DATA_ALT_ABILITY: - retVal = substruct3->altAbility; + case MON_DATA_ABILITY_NUM: + retVal = substruct3->abilityNum; break; case MON_DATA_COOL_RIBBON: retVal = substruct3->coolRibbon; @@ -3503,8 +3502,8 @@ void SetBoxMonData(struct BoxPokemon *boxMon, s32 field, const void *dataArg) else boxMon->isEgg = 0; break; - case MON_DATA_ALT_ABILITY: - SET8(substruct3->altAbility); + case MON_DATA_ABILITY_NUM: + SET8(substruct3->abilityNum); break; case MON_DATA_COOL_RIBBON: SET8(substruct3->coolRibbon); @@ -3699,9 +3698,9 @@ u8 GetMonsStateToDoubles(void) return (aliveCount > 1) ? PLAYER_HAS_TWO_USABLE_MONS : PLAYER_HAS_ONE_USABLE_MON; } -u8 GetAbilityBySpecies(u16 species, bool8 altAbility) +u8 GetAbilityBySpecies(u16 species, bool8 abilityNum) { - if (altAbility) + if (abilityNum) gLastUsedAbility = gBaseStats[species].abilities[1]; else gLastUsedAbility = gBaseStats[species].abilities[0]; @@ -3712,8 +3711,8 @@ u8 GetAbilityBySpecies(u16 species, bool8 altAbility) u8 GetMonAbility(struct Pokemon *mon) { u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL); - u8 altAbility = GetMonData(mon, MON_DATA_ALT_ABILITY, NULL); - return GetAbilityBySpecies(species, altAbility); + u8 abilityNum = GetMonData(mon, MON_DATA_ABILITY_NUM, NULL); + return GetAbilityBySpecies(species, abilityNum); } static void CreateSecretBaseEnemyParty(struct SecretBaseRecord *secretBaseRecord) @@ -3859,11 +3858,11 @@ static void CopyPlayerPartyMonToBattleData(u8 battlerId, u8 partyIndex) gBattleMons[battlerId].spAttack = GetMonData(&gPlayerParty[partyIndex], MON_DATA_SPATK, NULL); gBattleMons[battlerId].spDefense = GetMonData(&gPlayerParty[partyIndex], MON_DATA_SPDEF, NULL); gBattleMons[battlerId].isEgg = GetMonData(&gPlayerParty[partyIndex], MON_DATA_IS_EGG, NULL); - gBattleMons[battlerId].altAbility = GetMonData(&gPlayerParty[partyIndex], MON_DATA_ALT_ABILITY, NULL); + gBattleMons[battlerId].abilityNum = GetMonData(&gPlayerParty[partyIndex], MON_DATA_ABILITY_NUM, NULL); gBattleMons[battlerId].otId = GetMonData(&gPlayerParty[partyIndex], MON_DATA_OT_ID, NULL); gBattleMons[battlerId].type1 = gBaseStats[gBattleMons[battlerId].species].type1; gBattleMons[battlerId].type2 = gBaseStats[gBattleMons[battlerId].species].type2; - gBattleMons[battlerId].ability = GetAbilityBySpecies(gBattleMons[battlerId].species, gBattleMons[battlerId].altAbility); + gBattleMons[battlerId].ability = GetAbilityBySpecies(gBattleMons[battlerId].species, gBattleMons[battlerId].abilityNum); GetMonData(&gPlayerParty[partyIndex], MON_DATA_NICKNAME, nickname); StringCopy10(gBattleMons[battlerId].nickname, nickname); GetMonData(&gPlayerParty[partyIndex], MON_DATA_OT_NAME, gBattleMons[battlerId].otName); @@ -4118,14 +4117,14 @@ bool8 PokemonUseItemEffects(struct Pokemon *pkmn, u16 item, u8 partyIndex, u8 mo { gAbsentBattlerFlags &= ~gBitTable[sp34]; CopyPlayerPartyMonToBattleData(sp34, pokemon_order_func(gBattlerPartyIndexes[sp34])); - if (GetBattlerSide(gActiveBattler) == 0 && gBattleResults.unk4 < 255) - gBattleResults.unk4++; + if (GetBattlerSide(gActiveBattler) == 0 && gBattleResults.numRevivesUsed < 255) + gBattleResults.numRevivesUsed++; } else { gAbsentBattlerFlags &= ~gBitTable[gActiveBattler ^ 2]; - if (GetBattlerSide(gActiveBattler) == 0 && gBattleResults.unk4 < 255) - gBattleResults.unk4++; + if (GetBattlerSide(gActiveBattler) == 0 && gBattleResults.numRevivesUsed < 255) + gBattleResults.numRevivesUsed++; } } } @@ -4165,13 +4164,13 @@ bool8 PokemonUseItemEffects(struct Pokemon *pkmn, u16 item, u8 partyIndex, u8 mo gBattleMons[sp34].hp = data; if (!(r10 & 0x10) && GetBattlerSide(gActiveBattler) == 0) { - if (gBattleResults.unk3 < 255) - gBattleResults.unk3++; + if (gBattleResults.numHealingItemsUsed < 255) + gBattleResults.numHealingItemsUsed++; // I have to re-use this variable to match. r5 = gActiveBattler; gActiveBattler = sp34; BtlController_EmitGetMonData(0, 0, 0); - MarkBufferBankForExecution(gActiveBattler); + MarkBattlerForControllerExec(gActiveBattler); gActiveBattler = r5; } } @@ -4205,7 +4204,7 @@ bool8 PokemonUseItemEffects(struct Pokemon *pkmn, u16 item, u8 partyIndex, u8 mo SetMonData(pkmn, MON_DATA_PP1 + r5, &data); if (gMain.inBattle && sp34 != 4 && !(gBattleMons[sp34].status2 & 0x200000) - && !(gDisableStructs[sp34].unk18_b & gBitTable[r5])) + && !(gDisableStructs[sp34].mimickedMoves & gBitTable[r5])) gBattleMons[sp34].pp[r5] = data; retVal = FALSE; } @@ -4230,7 +4229,7 @@ bool8 PokemonUseItemEffects(struct Pokemon *pkmn, u16 item, u8 partyIndex, u8 mo SetMonData(pkmn, MON_DATA_PP1 + moveIndex, &data); if (gMain.inBattle && sp34 != 4 && !(gBattleMons[sp34].status2 & 0x200000) - && !(gDisableStructs[sp34].unk18_b & gBitTable[moveIndex])) + && !(gDisableStructs[sp34].mimickedMoves & gBitTable[moveIndex])) gBattleMons[sp34].pp[moveIndex] = data; retVal = FALSE; } @@ -4710,7 +4709,7 @@ bool8 PokemonUseItemEffects2(struct Pokemon *pkmn, u16 item, u8 partyIndex, u8 m r5 = gActiveBattler; gActiveBattler = sp34; BtlController_EmitGetMonData(0, 0, 0); - MarkBufferBankForExecution(gActiveBattler); + MarkBattlerForControllerExec(gActiveBattler); gActiveBattler = r5; } } @@ -6106,7 +6105,7 @@ const u8 *Battle_PrintStatBoosterEffectMessage(u16 itemId) } else { - sBattler_AI = gBattlerInMenuId; + gBattlerAttacker = gBattlerInMenuId; BattleStringExpandPlaceholdersToDisplayedString(BattleText_GetPumped); } } @@ -6114,7 +6113,7 @@ const u8 *Battle_PrintStatBoosterEffectMessage(u16 itemId) if (itemEffect[3] & 0x80) { - sBattler_AI = gBattlerInMenuId; + gBattlerAttacker = gBattlerInMenuId; BattleStringExpandPlaceholdersToDisplayedString(BattleText_MistShroud); } @@ -6466,9 +6465,9 @@ bool8 sub_80435E0(void) return retVal; } -bool8 GetLinkTrainerFlankId(u8 linkPlayerId) +bool16 GetLinkTrainerFlankId(u8 linkPlayerId) { - bool8 retVal = FALSE; + bool16 retVal = FALSE; switch (gLinkPlayers[linkPlayerId].id) { case 0: @@ -6483,7 +6482,7 @@ bool8 GetLinkTrainerFlankId(u8 linkPlayerId) return retVal; } -s32 GetBankMultiplayerId(u16 a1) +s32 GetBattlerMultiplayerId(u16 a1) { s32 id; for (id = 0; id < MAX_LINK_PLAYERS; id++) @@ -6962,10 +6961,10 @@ const u32 *GetMonFrontSpritePal(struct Pokemon *mon) u16 species = GetMonData(mon, MON_DATA_SPECIES2, 0); u32 otId = GetMonData(mon, MON_DATA_OT_ID, 0); u32 personality = GetMonData(mon, MON_DATA_PERSONALITY, 0); - return GetFrontSpritePalFromSpeciesAndPersonality(species, otId, personality); + return GetMonSpritePalFromSpeciesAndPersonality(species, otId, personality); } -const u32 *GetFrontSpritePalFromSpeciesAndPersonality(u16 species, u32 otId, u32 personality) +const u32 *GetMonSpritePalFromSpeciesAndPersonality(u16 species, u32 otId, u32 personality) { u32 shinyValue; @@ -7075,15 +7074,9 @@ void BoxMonRestorePP(struct BoxPokemon *boxMon) } } -// SetMonPreventsSwitchingString -void sub_8044348(void) +void SetMonPreventsSwitchingString(void) { -#ifdef NONMATCHING - gLastUsedAbility = gBattleStruct -> abilityPreventingSwitchout; // fixed from the original -#else - gLastUsedAbility = ((u8 *) gBattleStruct)[0xac]; // huh? why is this wrong? -#endif - + gLastUsedAbility = gBattleStruct -> abilityPreventingSwitchout; gBattleTextBuff1[0] = B_BUFF_PLACEHOLDER_BEGIN; gBattleTextBuff1[1] = B_BUFF_MON_NICK_WITH_PREFIX; gBattleTextBuff1[2] = gBattleStruct->battlerPreventingSwitchout; @@ -7140,7 +7133,7 @@ static bool8 IsShinyOtIdPersonality(u32 otId, u32 personality) u8 *GetTrainerPartnerName(void) { u8 id = GetMultiplayerId(); - return gLinkPlayers[GetBankMultiplayerId(gLinkPlayers[id].id ^ 2)].name; + return gLinkPlayers[GetBattlerMultiplayerId(gLinkPlayers[id].id ^ 2)].name; } u8 GetPlayerPartyHighestLevel(void) diff --git a/src/quest_log.c b/src/quest_log.c index 7a3084d8c..6083e9348 100644 --- a/src/quest_log.c +++ b/src/quest_log.c @@ -1,7 +1,7 @@ #include "global.h" #include "constants/species.h" #include "constants/items.h" -#include "data2.h" +#include "data.h" #include "malloc.h" #include "main.h" #include "task.h" diff --git a/src/quest_log_battle.c b/src/quest_log_battle.c index 3dfc2eb57..2687e4ea5 100644 --- a/src/quest_log_battle.c +++ b/src/quest_log_battle.c @@ -26,7 +26,7 @@ void sub_812C334(s32 *, s32 *); void sub_812BFDC(void) { - if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_WALLY_TUTORIAL | BATTLE_TYPE_POKEDUDE)) && (gBattleOutcome == B_OUTCOME_WON || gBattleOutcome == B_OUTCOME_CAUGHT)) + if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_OLDMAN_TUTORIAL | BATTLE_TYPE_POKEDUDE)) && (gBattleOutcome == B_OUTCOME_WON || gBattleOutcome == B_OUTCOME_CAUGHT)) { struct QuestLogStruct_TrainerBattleRecord * questLogTrainerBattleRecord = Alloc(sizeof(struct QuestLogStruct_TrainerBattleRecord)); struct QuestLogStruct_WildBattleRecord * questLogWildBattleRecord = Alloc(sizeof(struct QuestLogStruct_WildBattleRecord)); diff --git a/src/reshow_battle_screen.c b/src/reshow_battle_screen.c new file mode 100644 index 000000000..6dd743176 --- /dev/null +++ b/src/reshow_battle_screen.c @@ -0,0 +1,322 @@ +#include "global.h" +#include "bg.h" +#include "palette.h" +#include "pokemon.h" +#include "main.h" +#include "link.h" +#include "data.h" +#include "sprite.h" +#include "text.h" +#include "gpu_regs.h" +#include "scanline_effect.h" +#include "help_system.h" +#include "battle.h" +#include "battle_interface.h" +#include "battle_anim.h" +#include "battle_controllers.h" +#include "reshow_battle_screen.h" +#include "constants/species.h" + +static void CB2_ReshowBattleScreenAfterMenu(void); +static void sub_8077AAC(void); +static bool8 LoadBattlerSpriteGfx(u8 battlerId); +static void CreateBattlerSprite(u8 battlerId); +static void CreateHealthboxSprite(u8 battlerId); + +void nullsub_44(void) +{ +} + +void ReshowBattleScreenAfterMenu(void) +{ + gPaletteFade.bufferTransferDisabled = 1; + SetHBlankCallback(NULL); + SetGpuReg(REG_OFFSET_MOSAIC, 0); + gBattleScripting.reshowMainState = 0; + gBattleScripting.reshowHelperState = 0; + if (!(gBattleTypeFlags & BATTLE_TYPE_LINK)) + { + if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) + { + if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) + HelpSystem_SetSomeVariable2(0x19); + else + HelpSystem_SetSomeVariable2(0x18); + } + else if (gBattleTypeFlags & BATTLE_TYPE_SAFARI) + { + HelpSystem_SetSomeVariable2(0x1A); + } + else + { + HelpSystem_SetSomeVariable2(0x17); + } + } + SetMainCallback2(CB2_ReshowBattleScreenAfterMenu); +} + +static void CB2_ReshowBattleScreenAfterMenu(void) +{ + u8 opponentBattler; + u16 species; + + switch (gBattleScripting.reshowMainState) + { + case 0: + ResetSpriteData(); + break; + case 1: + SetVBlankCallback(NULL); + ScanlineEffect_Clear(); + sub_800F324(); + SetBgAttribute(1, BG_ATTR_CHARBASEINDEX, 0); + SetBgAttribute(2, BG_ATTR_CHARBASEINDEX, 0); + ShowBg(0); + ShowBg(1); + ShowBg(2); + ShowBg(3); + ResetPaletteFade(); + gBattle_BG0_X = 0; + gBattle_BG0_Y = 0; + gBattle_BG1_X = 0; + gBattle_BG1_Y = 0; + gBattle_BG2_X = 0; + gBattle_BG2_Y = 0; + gBattle_BG3_X = 0; + gBattle_BG3_Y = 0; + break; + case 2: + CpuFastFill(0, (void *)VRAM, VRAM_SIZE); + break; + case 3: + LoadBattleTextboxAndBackground(); + break; + case 4: + FreeAllSpritePalettes(); + gReservedSpritePaletteCount = 4; + break; + case 5: + ClearSpritesHealthboxAnimData(); + break; + case 6: + if (BattleLoadAllHealthBoxesGfx(gBattleScripting.reshowHelperState)) + { + gBattleScripting.reshowHelperState = 0; + } + else + { + ++gBattleScripting.reshowHelperState; + --gBattleScripting.reshowMainState; + } + break; + case 7: + if (!LoadBattlerSpriteGfx(0)) + --gBattleScripting.reshowMainState; + break; + case 8: + if (!LoadBattlerSpriteGfx(1)) + --gBattleScripting.reshowMainState; + break; + case 9: + if (!LoadBattlerSpriteGfx(2)) + --gBattleScripting.reshowMainState; + break; + case 0xA: + if (!LoadBattlerSpriteGfx(3)) + --gBattleScripting.reshowMainState; + break; + case 0xB: + CreateBattlerSprite(0); + break; + case 0xC: + CreateBattlerSprite(1); + break; + case 0xD: + CreateBattlerSprite(2); + break; + case 0xE: + CreateBattlerSprite(3); + break; + case 0xF: + CreateHealthboxSprite(0); + break; + case 0x10: + CreateHealthboxSprite(1); + break; + case 0x11: + CreateHealthboxSprite(2); + break; + case 0x12: + CreateHealthboxSprite(3); + break; + case 0x13: + LoadAndCreateEnemyShadowSprites(); + opponentBattler = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); + species = GetMonData(&gEnemyParty[gBattlerPartyIndexes[opponentBattler]], MON_DATA_SPECIES); + SetBattlerShadowSpriteCallback(opponentBattler, species); + if (IsDoubleBattle()) + { + opponentBattler = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); + species = GetMonData(&gEnemyParty[gBattlerPartyIndexes[opponentBattler]], MON_DATA_SPECIES); + SetBattlerShadowSpriteCallback(opponentBattler, species); + } + ActionSelectionCreateCursorAt(gActionSelectionCursor[gBattlerInMenuId], 0); + if (gWirelessCommType && gReceivedRemoteLinkPlayers) + { + LoadWirelessStatusIndicatorSpriteGfx(); + CreateWirelessStatusIndicatorSprite(0, 0); + } + break; + case 0x14: + SetVBlankCallback(VBlankCB_Battle); + sub_8077AAC(); + BeginHardwarePaletteFade(0xFF, 0, 0x10, 0, 1); + gPaletteFade.bufferTransferDisabled = 0; + SetMainCallback2(BattleMainCB2); + sub_80357C8(); + break; + default: + break; + } + ++gBattleScripting.reshowMainState; +} + +static void sub_8077AAC(void) +{ + EnableInterrupts(INTR_FLAG_VBLANK); + SetGpuReg(REG_OFFSET_BLDCNT, 0); + SetGpuReg(REG_OFFSET_BLDALPHA, 0); + SetGpuReg(REG_OFFSET_BLDY, 0); + SetGpuReg(REG_OFFSET_WININ, 0x3F); + SetGpuReg(REG_OFFSET_WINOUT, 0x3F); + SetGpuReg(REG_OFFSET_WIN0H, 0); + SetGpuReg(REG_OFFSET_WIN0V, 0); + SetGpuReg(REG_OFFSET_WIN1H, 0); + SetGpuReg(REG_OFFSET_WIN1V, 0); + SetGpuRegBits(REG_OFFSET_DISPCNT, DISPCNT_OBJ_1D_MAP | DISPCNT_OBJ_ON | DISPCNT_WIN0_ON | DISPCNT_OBJWIN_ON); +} + +static bool8 LoadBattlerSpriteGfx(u8 battler) +{ + if (battler < gBattlersCount) + { + if (GetBattlerSide(battler) != B_SIDE_PLAYER) + { + if ((gBattleTypeFlags & (BATTLE_TYPE_LEGENDARY | BATTLE_TYPE_GHOST)) == BATTLE_TYPE_GHOST) + DecompressGhostFrontPic(&gEnemyParty[gBattlerPartyIndexes[battler]], battler); + else if (!gBattleSpritesDataPtr->battlerData[battler].behindSubstitute) + BattleLoadOpponentMonSpriteGfx(&gEnemyParty[gBattlerPartyIndexes[battler]], battler); + else + BattleLoadSubstituteOrMonSpriteGfx(battler, FALSE); + } + else if (gBattleTypeFlags & BATTLE_TYPE_SAFARI && battler == B_POSITION_PLAYER_LEFT) // Should be checking position, not battler. + DecompressTrainerBackPalette(gSaveBlock2Ptr->playerGender, battler); + else if (gBattleTypeFlags & BATTLE_TYPE_OLDMAN_TUTORIAL && battler == B_POSITION_PLAYER_LEFT) // Should be checking position, not battler. + DecompressTrainerBackPalette(5, battler); + else if (!gBattleSpritesDataPtr->battlerData[battler].behindSubstitute) + BattleLoadPlayerMonSpriteGfx(&gPlayerParty[gBattlerPartyIndexes[battler]], battler); + else + BattleLoadSubstituteOrMonSpriteGfx(battler, FALSE); + gBattleScripting.reshowHelperState = 0; + } + return TRUE; +} + +static void CreateBattlerSprite(u8 battler) +{ + if (battler < gBattlersCount) + { + u8 posY; + + if ((gBattleTypeFlags & (BATTLE_TYPE_LEGENDARY | BATTLE_TYPE_GHOST)) == BATTLE_TYPE_GHOST) + posY = GetGhostSpriteDefault_Y(battler); + else if (gBattleSpritesDataPtr->battlerData[battler].behindSubstitute) + posY = GetSubstituteSpriteDefault_Y(battler); + else + posY = GetBattlerSpriteDefault_Y(battler); + if (GetBattlerSide(battler) != B_SIDE_PLAYER) + { + if (GetMonData(&gEnemyParty[gBattlerPartyIndexes[battler]], MON_DATA_HP) == 0) + return; + SetMultiuseSpriteTemplateToPokemon(GetMonData(&gEnemyParty[gBattlerPartyIndexes[battler]], MON_DATA_SPECIES), GetBattlerPosition(battler)); + gBattlerSpriteIds[battler] = CreateSprite(&gMultiuseSpriteTemplate, GetBattlerSpriteCoord(battler, 2), posY, GetBattlerSpriteSubpriority(battler)); + gSprites[gBattlerSpriteIds[battler]].oam.paletteNum = battler; + gSprites[gBattlerSpriteIds[battler]].callback = SpriteCallbackDummy; + gSprites[gBattlerSpriteIds[battler]].data[0] = battler; + gSprites[gBattlerSpriteIds[battler]].data[2] = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battler]], MON_DATA_SPECIES); + StartSpriteAnim(&gSprites[gBattlerSpriteIds[battler]], gBattleMonForms[battler]); + } + else if (gBattleTypeFlags & BATTLE_TYPE_SAFARI && battler == B_POSITION_PLAYER_LEFT) + { + SetMultiuseSpriteTemplateToTrainerBack(gSaveBlock2Ptr->playerGender, GetBattlerPosition(B_POSITION_PLAYER_LEFT)); + gBattlerSpriteIds[battler] = CreateSprite(&gMultiuseSpriteTemplate, 0x50, + (8 - gTrainerBackPicCoords[gSaveBlock2Ptr->playerGender].size) * 4 + 80, + GetBattlerSpriteSubpriority(0)); + gSprites[gBattlerSpriteIds[battler]].oam.paletteNum = battler; + gSprites[gBattlerSpriteIds[battler]].callback = SpriteCallbackDummy; + gSprites[gBattlerSpriteIds[battler]].data[0] = battler; + } + else if (gBattleTypeFlags & BATTLE_TYPE_OLDMAN_TUTORIAL && battler == B_POSITION_PLAYER_LEFT) + { + SetMultiuseSpriteTemplateToTrainerBack(5, GetBattlerPosition(0)); + gBattlerSpriteIds[battler] = CreateSprite(&gMultiuseSpriteTemplate, 0x50, + (8 - gTrainerBackPicCoords[5].size) * 4 + 80, + GetBattlerSpriteSubpriority(0)); + gSprites[gBattlerSpriteIds[battler]].oam.paletteNum = battler; + gSprites[gBattlerSpriteIds[battler]].callback = SpriteCallbackDummy; + gSprites[gBattlerSpriteIds[battler]].data[0] = battler; + } + else if (GetMonData(&gPlayerParty[gBattlerPartyIndexes[battler]], MON_DATA_HP) == 0) + { + return; + } + else + { + SetMultiuseSpriteTemplateToPokemon(GetMonData(&gPlayerParty[gBattlerPartyIndexes[battler]], MON_DATA_SPECIES), GetBattlerPosition(battler)); + gBattlerSpriteIds[battler] = CreateSprite(&gMultiuseSpriteTemplate, GetBattlerSpriteCoord(battler, 2), posY, GetBattlerSpriteSubpriority(battler)); + gSprites[gBattlerSpriteIds[battler]].oam.paletteNum = battler; + gSprites[gBattlerSpriteIds[battler]].callback = SpriteCallbackDummy; + gSprites[gBattlerSpriteIds[battler]].data[0] = battler; + gSprites[gBattlerSpriteIds[battler]].data[2] = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battler]], MON_DATA_SPECIES); + StartSpriteAnim(&gSprites[gBattlerSpriteIds[battler]], gBattleMonForms[battler]); + } + gSprites[gBattlerSpriteIds[battler]].invisible = gBattleSpritesDataPtr->battlerData[battler].invisible; + } +} + +static void CreateHealthboxSprite(u8 battler) +{ + if (battler < gBattlersCount) + { + u8 healthboxSpriteId; + + if (gBattleTypeFlags & BATTLE_TYPE_SAFARI && battler == B_POSITION_PLAYER_LEFT) + healthboxSpriteId = CreateSafariPlayerHealthboxSprites(); + else if (gBattleTypeFlags & BATTLE_TYPE_OLDMAN_TUTORIAL && battler == B_POSITION_PLAYER_LEFT) + return; + else + healthboxSpriteId = CreateBattlerHealthboxSprites(battler); + gHealthboxSpriteIds[battler] = healthboxSpriteId; + InitBattlerHealthboxCoords(battler); + SetHealthboxSpriteVisible(healthboxSpriteId); + if (GetBattlerSide(battler) != B_SIDE_PLAYER) + UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], &gEnemyParty[gBattlerPartyIndexes[battler]], HEALTHBOX_ALL); + else if (gBattleTypeFlags & BATTLE_TYPE_SAFARI) + UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], &gPlayerParty[gBattlerPartyIndexes[battler]], HEALTHBOX_SAFARI_ALL_TEXT); + else + UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], &gPlayerParty[gBattlerPartyIndexes[battler]], HEALTHBOX_ALL); + if (GetBattlerPosition(battler) == B_POSITION_OPPONENT_RIGHT || GetBattlerPosition(battler) == B_POSITION_PLAYER_RIGHT) + DummyBattleInterfaceFunc(gHealthboxSpriteIds[battler], TRUE); + else + DummyBattleInterfaceFunc(gHealthboxSpriteIds[battler], FALSE); + if (GetBattlerSide(battler) != B_SIDE_PLAYER) + { + if (GetMonData(&gEnemyParty[gBattlerPartyIndexes[battler]], MON_DATA_HP) == 0) + SetHealthboxSpriteInvisible(healthboxSpriteId); + } + else if (!(gBattleTypeFlags & BATTLE_TYPE_SAFARI) && GetMonData(&gPlayerParty[gBattlerPartyIndexes[battler]], MON_DATA_HP) == 0) + { + SetHealthboxSpriteInvisible(healthboxSpriteId); + } + } +} diff --git a/src/save.c b/src/save.c index d3d31a22e..9f580477b 100644 --- a/src/save.c +++ b/src/save.c @@ -68,7 +68,7 @@ const struct SaveSectionOffsets gSaveSectionOffsets[] = extern void DoSaveFailedScreen(u8 saveType); // save_failed_screen extern void sub_800AB9C(void); // link -extern bool8 sub_800A4BC(void); // link +extern bool8 IsLinkTaskFinished(void); // link extern void save_serialize_map(void); // fieldmap extern void sub_804C1C0(void); // load_save extern void sav2_gender2_inplace_and_xFE(void); // load_save @@ -865,7 +865,7 @@ void sub_80DA634(u8 taskId) gTasks[taskId].data[0] = 2; break; case 2: - if (sub_800A4BC()) + if (IsLinkTaskFinished()) { save_serialize_map(); gTasks[taskId].data[0] = 3; @@ -899,7 +899,7 @@ void sub_80DA634(u8 taskId) gTasks[taskId].data[0] = 8; break; case 8: - if (sub_800A4BC()) + if (IsLinkTaskFinished()) { sub_80DA434(); gTasks[taskId].data[0] = 9; @@ -910,7 +910,7 @@ void sub_80DA634(u8 taskId) gTasks[taskId].data[0] = 10; break; case 10: - if (sub_800A4BC()) + if (IsLinkTaskFinished()) gTasks[taskId].data[0]++; break; case 11: diff --git a/src/scrcmd.c b/src/scrcmd.c index cef1a1eb7..bcf8c231d 100644 --- a/src/scrcmd.c +++ b/src/scrcmd.c @@ -25,7 +25,7 @@ #include "start_menu.h" #include "script_menu.h" #include "string_util.h" -#include "data2.h" +#include "data.h" #include "field_specials.h" #include "constants/items.h" #include "script_pokemon_util_80A0058.h" diff --git a/src/teachy_tv.c b/src/teachy_tv.c index 3594c79a2..fa687fbef 100644 --- a/src/teachy_tv.c +++ b/src/teachy_tv.c @@ -23,7 +23,7 @@ #include "event_data.h" #include "load_save.h" #include "battle_transition.h" -#include "battle_2.h" +#include "battle_main.h" #include "battle.h" #include "global.fieldmap.h" #include "teachy_tv.h" diff --git a/src/tm_case.c b/src/tm_case.c index 2031234af..b9c89b6b3 100644 --- a/src/tm_case.c +++ b/src/tm_case.c @@ -21,7 +21,7 @@ #include "pokemon_storage_system.h" #include "string_util.h" #include "party_menu.h" -#include "data2.h" +#include "data.h" #include "scanline_effect.h" #include "sound.h" #include "strings.h" diff --git a/src/trainer_pokemon_sprites.c b/src/trainer_pokemon_sprites.c index 25a8d4515..5f49173dc 100644 --- a/src/trainer_pokemon_sprites.c +++ b/src/trainer_pokemon_sprites.c @@ -104,7 +104,7 @@ void LoadPicPaletteByTagOrSlot(u16 species, u32 otId, u32 personality, u8 palett if (paletteTag == 0xFFFF) { sCreatingSpriteTemplate.paletteTag = 0xFFFF; - LoadCompressedPalette(GetFrontSpritePalFromSpeciesAndPersonality(species, otId, personality), 0x100 + paletteSlot * 0x10, 0x20); + LoadCompressedPalette(GetMonSpritePalFromSpeciesAndPersonality(species, otId, personality), 0x100 + paletteSlot * 0x10, 0x20); } else { @@ -130,7 +130,7 @@ void LoadPicPaletteByTagOrSlot(u16 species, u32 otId, u32 personality, u8 palett void LoadPicPaletteBySlot(u16 species, u32 otId, u32 personality, u8 paletteSlot, bool8 isTrainer) { if (!isTrainer) - LoadCompressedPalette(GetFrontSpritePalFromSpeciesAndPersonality(species, otId, personality), paletteSlot * 0x10, 0x20); + LoadCompressedPalette(GetMonSpritePalFromSpeciesAndPersonality(species, otId, personality), paletteSlot * 0x10, 0x20); else LoadCompressedPalette(gTrainerFrontPicPaletteTable[species].data, paletteSlot * 0x10, 0x20); } diff --git a/src/trainer_tower.c b/src/trainer_tower.c index 7194d2271..da3cf6dda 100644 --- a/src/trainer_tower.c +++ b/src/trainer_tower.c @@ -17,7 +17,7 @@ #include "battle_setup.h" #include "battle_transition.h" #include "battle.h" -#include "battle_2.h" +#include "battle_main.h" #include "overworld.h" #include "item.h" #include "window.h" @@ -626,12 +626,12 @@ void sub_815DA28(u8 * dest) StringCopyN(dest, gUnknown_203F45C->unk_00, 11); } -u8 sub_815DA3C(void) +u8 GetTrainerTowerTrainerFrontSpriteId(void) { return gFacilityClassToPicIndex[gUnknown_203F45C->unk_3D]; } -void sub_815DA54(void) +void InitTrainerTowerBattleStruct(void) { u16 r10; s32 r9; @@ -660,7 +660,7 @@ void sub_815DA54(void) sub_815DD2C(); } -void sub_815DBDC(void) +void FreeTrainerTowerBattleStruct(void) { Free(gUnknown_203F45C); gUnknown_203F45C = NULL; @@ -1122,7 +1122,7 @@ void sub_815E124(u8 taskId) void sub_815E160(void) { - gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_FACTORY; + gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_TRAINER_TOWER; if (gUnknown_203F458->unk_0004.trainers[gUnknown_203F458->unk_0000].unk_002 == 1) gBattleTypeFlags |= BATTLE_TYPE_DOUBLE; gTrainerBattleOpponent_A = 0; diff --git a/src/window.c b/src/window.c index 5ccd05f35..4aaa6d9b4 100644 --- a/src/window.c +++ b/src/window.c @@ -364,7 +364,7 @@ void FillWindowPixelRect(u8 windowId, u8 fillValue, u16 x, u16 y, u16 width, u16 FillBitmapRect4Bit(&pixelRect, x, y, width, height, fillValue); } -void CopyToWindowPixelBuffer(u8 windowId, const u8 *src, u16 size, u16 tileOffset) +void CopyToWindowPixelBuffer(u8 windowId, const void *src, u16 size, u16 tileOffset) { if (size != 0) CpuCopy16(src, gWindows[windowId].tileData + (0x20 * tileOffset), size); |