summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSatoMew <SatoMew@users.noreply.github.com>2019-10-08 22:46:32 +0100
committerSatoMew <SatoMew@users.noreply.github.com>2019-10-08 22:46:32 +0100
commit2c254082de042dde46cee9d2da2d9bb1bc340905 (patch)
treeaf99bcac2c6fece9523b12dbabe4128ec26f6fc9 /src
parent78558b07b0983b6b8b06085933fdf66b801658e9 (diff)
parent2a7205dec677c98d087cb8ba191370de464c8bf0 (diff)
Merge branch 'master' of https://github.com/pret/pokefirered
Diffstat (limited to 'src')
-rw-r--r--src/bag.c6
-rw-r--r--src/battle_ai_script_commands.c239
-rw-r--r--src/battle_ai_switch_items.c676
-rw-r--r--src/battle_anim_mon_movement.c366
-rw-r--r--src/battle_controller_link_opponent.c1686
-rw-r--r--src/battle_controller_link_partner.c1599
-rw-r--r--src/battle_controller_opponent.c1788
-rw-r--r--src/battle_controller_player.c2969
-rw-r--r--src/battle_controller_safari.c677
-rw-r--r--src/battle_controllers.c1170
-rw-r--r--src/battle_gfx_sfx_util.c1056
-rw-r--r--src/battle_records.c26
-rw-r--r--src/battle_script_commands.c9824
-rw-r--r--src/battle_util.c3200
-rw-r--r--src/battle_util2.c96
-rw-r--r--src/berry_pouch.c1528
-rw-r--r--src/berry_powder.c24
-rw-r--r--src/braille_text.c86
-rw-r--r--src/buy_menu_helpers.c22
-rw-r--r--src/credits.c2168
-rw-r--r--src/data/items.json2
-rw-r--r--src/daycare.c344
-rw-r--r--src/diploma.c8
-rw-r--r--src/ereader_helpers.c14
-rw-r--r--src/fame_checker.c20
-rw-r--r--src/field_fadetransition.c965
-rw-r--r--src/fieldmap.c2
-rw-r--r--src/help_system.c14
-rw-r--r--src/intro.c2
-rw-r--r--src/item.c2
-rw-r--r--src/item_pc.c6
-rw-r--r--src/item_use.c10
-rw-r--r--src/itemfinder.c659
-rw-r--r--src/link.c2259
-rw-r--r--src/list_menu.c18
-rw-r--r--src/mailbox_pc.c143
-rw-r--r--src/map_preview_screen.c10
-rw-r--r--src/menu.c27
-rw-r--r--src/menu2.c339
-rw-r--r--src/menu_helpers.c2
-rw-r--r--src/mevent.c16
-rw-r--r--src/mevent_8145654.c24
-rw-r--r--src/mevent_server.c2
-rw-r--r--src/mevent_server_helpers.c6
-rw-r--r--src/mystery_event_script.c2
-rw-r--r--src/mystery_gift_menu.c24
-rw-r--r--src/new_game.c161
-rw-r--r--src/oak_speech.c30
-rw-r--r--src/palette.c989
-rw-r--r--src/pokedex_area_markers.c238
-rw-r--r--src/pokemon.c1706
-rw-r--r--src/prof_pc.c2
-rw-r--r--src/quest_log.c18
-rw-r--r--src/quest_log_battle.c2
-rw-r--r--src/reshow_battle_screen.c322
-rw-r--r--src/save.c8
-rw-r--r--src/scrcmd.c16
-rw-r--r--src/seagallop.c2
-rw-r--r--src/slot_machine.c10
-rw-r--r--src/sound.c4
-rw-r--r--src/string_util.c4
-rw-r--r--src/teachy_tv.c2
-rw-r--r--src/text.c1184
-rw-r--r--src/text_printer.c62
-rw-r--r--src/tm_case.c8
-rw-r--r--src/trainer_pokemon_sprites.c6
-rw-r--r--src/trainer_tower.c20
-rw-r--r--src/wild_pokemon_area.c308
-rw-r--r--src/window.c2
-rw-r--r--src/wireless_communication_status_screen.c34
70 files changed, 35954 insertions, 3310 deletions
diff --git a/src/bag.c b/src/bag.c
index a63f57702..c30bc626d 100644
--- a/src/bag.c
+++ b/src/bag.c
@@ -14,7 +14,7 @@
extern const u8 gText_DepositItem[];
const u16 gUnknown_8453098[] = INCBIN_U16("data/bag/bag_window_pal.gbapal");
-const struct TextColor gUnknown_84530B8[] = {
+const u8 gUnknown_84530B8[][3] = {
{0, 1, 2},
{0, 2, 3},
{0, 3, 2},
@@ -229,13 +229,13 @@ void sub_810B858(void)
void sub_810B8F0(u8 windowId, u8 fontId, const u8 * str, u8 x, u8 y, u8 letterSpacing, u8 lineSpacing, s8 speed, u8 colorIdx)
{
- AddTextPrinterParameterized4(windowId, fontId, x, y, letterSpacing, lineSpacing, &gUnknown_84530B8[colorIdx], speed, str);
+ AddTextPrinterParameterized4(windowId, fontId, x, y, letterSpacing, lineSpacing, gUnknown_84530B8[colorIdx], speed, str);
}
void sub_810B958(const u8 * str)
{
u32 x = 0x48 - GetStringWidth(1, str, 0);
- AddTextPrinterParameterized3(2, 1, x / 2, 1, &gUnknown_84530B8[0], 0, str);
+ AddTextPrinterParameterized3(2, 1, x / 2, 1, gUnknown_84530B8[0], 0, str);
}
void sub_810B994(void)
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_records.c b/src/battle_records.c
index 79f29eb8a..38889ca67 100644
--- a/src/battle_records.c
+++ b/src/battle_records.c
@@ -58,7 +58,7 @@ static const struct WindowTemplate sWindowTemplates[] = {
}, DUMMY_WIN_TEMPLATE
};
-static const struct TextColor sTextColor = {
+static const u8 sTextColor[3] = {
0, 2, 3
};
@@ -279,7 +279,7 @@ static void ResetBGPos(void)
ChangeBgY(3, 0, 0);
}
-static void InitLinkBattleRecord(struct LinkBattleRecord * record)
+static void ClearLinkBattleRecord(struct LinkBattleRecord *record)
{
CpuFill16(0, record, sizeof(*record));
record->name[0] = EOS;
@@ -289,12 +289,12 @@ static void InitLinkBattleRecord(struct LinkBattleRecord * record)
record->draws = 0;
}
-static void InitLinkBattleRecords_(struct LinkBattleRecords * records)
+static void ClearLinkBattleRecords(struct LinkBattleRecords *records)
{
s32 i;
for (i = 0; i < LINK_B_RECORDS_COUNT; i++)
- InitLinkBattleRecord(&records->entries[i]);
+ ClearLinkBattleRecord(&records->entries[i]);
SetGameStat(GAME_STAT_LINK_BATTLE_WINS, 0);
SetGameStat(GAME_STAT_LINK_BATTLE_LOSSES, 0);
SetGameStat(GAME_STAT_LINK_BATTLE_DRAWS, 0);
@@ -404,7 +404,7 @@ static void AddOpponentLinkBattleRecord(struct LinkBattleRecords * records, cons
{
i = LINK_B_RECORDS_COUNT - 1;
record = &records->entries[LINK_B_RECORDS_COUNT - 1];
- InitLinkBattleRecord(record);
+ ClearLinkBattleRecord(record);
StringCopyN(record->name, namebuf, OT_NAME_LENGTH);
record->trainerId = trainerId;
}
@@ -412,9 +412,9 @@ static void AddOpponentLinkBattleRecord(struct LinkBattleRecords * records, cons
SortLinkBattleRecords(records);
}
-void InitLinkBattleRecords(void)
+void ClearPlayerLinkBattleRecords(void)
{
- InitLinkBattleRecords_(&gSaveBlock2Ptr->linkBattleRecords);
+ ClearLinkBattleRecords(&gSaveBlock2Ptr->linkBattleRecords);
}
static void IncTrainerCardWinCount(s32 battlerId)
@@ -494,7 +494,7 @@ static void PrintTotalRecord(struct LinkBattleRecords * records)
}
StringExpandPlaceholders(gStringVar4, gString_BattleRecords_TotalRecord);
- AddTextPrinterParameterized4(0, 2, 12, 24, 0, 2, &sTextColor, 0, gStringVar4);
+ AddTextPrinterParameterized4(0, 2, 12, 24, 0, 2, sTextColor, 0, gStringVar4);
}
static void PrintOpponentBattleRecord(struct LinkBattleRecord * record, u8 y)
@@ -504,7 +504,7 @@ static void PrintOpponentBattleRecord(struct LinkBattleRecord * record, u8 y)
if (record->wins == 0 && record->losses == 0 && record->draws == 0)
{
- AddTextPrinterParameterized4(0, 2, 0, y, 0, 2, &sTextColor, 0, gString_BattleRecords_7Dashes);
+ AddTextPrinterParameterized4(0, 2, 0, y, 0, 2, sTextColor, 0, gString_BattleRecords_7Dashes);
for (i = 0; i < 3; i++)
{
if (i == 0)
@@ -513,7 +513,7 @@ static void PrintOpponentBattleRecord(struct LinkBattleRecord * record, u8 y)
x = 0x84;
else
x = 0xB4;
- AddTextPrinterParameterized4(0, 2, x, y, 0, 2, &sTextColor, 0, gString_BattleRecords_4Dashes);
+ AddTextPrinterParameterized4(0, 2, x, y, 0, 2, sTextColor, 0, gString_BattleRecords_4Dashes);
}
}
else
@@ -541,7 +541,7 @@ static void PrintOpponentBattleRecord(struct LinkBattleRecord * record, u8 y)
x = 0xB4;
ConvertIntToDecimalStringN(gStringVar1, record->draws, STR_CONV_MODE_RIGHT_ALIGN, 4);
}
- AddTextPrinterParameterized4(0, 2, x, y, 0, 2, &sTextColor, 0, gStringVar1);
+ AddTextPrinterParameterized4(0, 2, x, y, 0, 2, sTextColor, 0, gStringVar1);
}
}
}
@@ -554,9 +554,9 @@ static void PrintBattleRecords(void)
FillWindowPixelRect(0, PIXEL_FILL(0), 0, 0, 0xD8, 0x90);
StringExpandPlaceholders(gStringVar4, gString_BattleRecords_PlayersBattleResults);
left = 0xD0 - GetStringWidth(2, gStringVar4, -1);
- AddTextPrinterParameterized4(0, 2, left / 2, 4, 0, 2, &sTextColor, 0, gStringVar4);
+ AddTextPrinterParameterized4(0, 2, left / 2, 4, 0, 2, sTextColor, 0, gStringVar4);
PrintTotalRecord(&gSaveBlock2Ptr->linkBattleRecords);
- AddTextPrinterParameterized4(0, 2, 0x54, 0x30, 0, 2, &sTextColor, 0, gString_BattleRecords_ColumnHeaders);
+ AddTextPrinterParameterized4(0, 2, 0x54, 0x30, 0, 2, sTextColor, 0, gString_BattleRecords_ColumnHeaders);
for (i = 0; i < LINK_B_RECORDS_COUNT; i++)
PrintOpponentBattleRecord(&gSaveBlock2Ptr->linkBattleRecords.entries[i], 0x3D + 14 * i);
CommitWindow(0);
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..d940d8508
--- /dev/null
+++ b/src/battle_util2.c
@@ -0,0 +1,96 @@
+#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)
+ {
+ s32 i;
+
+ for (i = 0; i < 4; ++i)
+ gUnknown_3005EE0[i] = AllocZeroed(8);
+ }
+ 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)
+ {
+ s32 i;
+
+ for (i = 0; i < 4; ++i)
+ {
+ FREE_AND_SET_NULL(gUnknown_3005EE0[i]);
+ }
+ }
+ 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/berry_pouch.c b/src/berry_pouch.c
new file mode 100644
index 000000000..525249514
--- /dev/null
+++ b/src/berry_pouch.c
@@ -0,0 +1,1528 @@
+#include "global.h"
+#include "malloc.h"
+#include "bg.h"
+#include "decompress.h"
+#include "gpu_regs.h"
+#include "palette.h"
+#include "text.h"
+#include "berry_pouch.h"
+#include "item_menu.h"
+#include "menu_helpers.h"
+#include "new_menu_helpers.h"
+#include "scanline_effect.h"
+#include "item_menu_icons.h"
+#include "list_menu.h"
+#include "graphics.h"
+#include "berry.h"
+#include "item.h"
+#include "item_use.h"
+#include "party_menu.h"
+#include "text_window.h"
+#include "strings.h"
+#include "string_util.h"
+#include "sound.h"
+#include "link.h"
+#include "money.h"
+#include "shop.h"
+#include "menu.h"
+#include "menu_indicators.h"
+#include "pokemon_storage_system.h"
+#include "constants/items.h"
+#include "constants/songs.h"
+
+struct BerryPouchStruct_203F36C
+{
+ void (*exitCallback)(void);
+ u16 indicatorOffset;
+ u8 indicatorTaskId;
+ u8 listMenuNumItems;
+ u8 listMenuMaxShowed;
+ u8 itemMenuIconId;
+ u8 ALIGNED(4) bg1TilemapBuffer[BG_SCREEN_SIZE];
+ s16 data[4];
+};
+
+struct BerryPouchStruct_203F370
+{
+ void (*savedCallback)(void);
+ u8 type;
+ u8 allowSelect;
+ u8 unused_06;
+ u16 listMenuSelectedRow;
+ u16 listMenuScrollOffset;
+};
+
+enum
+{
+ BP_ACTION_USE = 0,
+ BP_ACTION_TOSS,
+ BP_ACTION_GIVE,
+ BP_ACTION_EXIT,
+ BP_ACTION_DUMMY
+};
+
+static EWRAM_DATA struct BerryPouchStruct_203F36C *sResources = NULL;
+static EWRAM_DATA struct BerryPouchStruct_203F370 sStaticCnt = {};
+static EWRAM_DATA struct ListMenuItem *sListMenuItems = NULL;
+static EWRAM_DATA u8 * sListMenuStrbuf = NULL;
+static EWRAM_DATA const u8 * sContextMenuOptions = NULL;
+static EWRAM_DATA u8 sContextMenuNumOptions = 0;
+static ALIGNED(4) EWRAM_DATA u8 sVariableWindowIds[14] = {};
+static ALIGNED(4) EWRAM_DATA u8 sBerryPouchSpriteId = 0;
+
+static void CB2_InitBerryPouch(void);
+static bool8 RunBerryPouchInit(void);
+static void AbortBerryPouchLoading(void);
+static void Task_AbortBerryPouchLoading_WaitFade(u8 taskId);
+static void BerryPouchInitBgs(void);
+static bool8 BerryPouchLoadGfx(void);
+static bool8 AllocateListMenuBuffers(void);
+static void SetUpListMenuTemplate(void);
+static void GetBerryNameAndIndexForMenu(u8 * dest, u16 itemId);
+static void BerryPouchMoveCursorFunc(s32 itemIndex, bool8 onInit, struct ListMenu *list);
+static void BerryPouchItemPrintFunc(u8 windowId, s32 itemId, u8 y);
+static void BerryPouchSetArrowCursorAt(u8 y, u8 colorIdx);
+static void PrintSelectedBerryDescription(s32 itemIndex);
+static void CreateScrollIndicatorArrows_BerryPouchList(void);
+static void PrintBerryPouchHeaderCentered(void);
+static void SanitizeListMenuSelectionParams(void);
+static void UpdateListMenuScrollOffset(void);
+static void BerryPouch_DestroyResources(void);
+static void Task_BerryPouchFadeToExitCallback(u8 taskId);
+static void SortAndCountBerries(void);
+static void Task_BerryPouchMain(u8 taskId);
+static void Task_NormalContextMenu(u8 taskId);
+static void Task_NormalContextMenu_HandleInput(u8 taskId);
+static void Task_BerryPouch_Use(u8 taskId);
+static void Task_BerryPouch_Toss(u8 taskId);
+static void Task_AskTossMultiple(u8 taskId);
+static void Task_TossNo(u8 taskId);
+static void Task_Toss_SelectMultiple(u8 taskId);
+static void Task_TossYes(u8 taskId);
+static void Task_WaitButtonThenTossBerries(u8 taskId);
+static void Task_BerryPouch_Give(u8 taskId);
+static void Task_Give_PrintThereIsNoPokemon(u8 taskId);
+static void Task_WaitButtonBeforeDialogueWindowDestruction(u8 taskId);
+static void Task_BerryPouch_Exit(u8 taskId);
+static void Task_ContextMenu_FromPartyGiveMenu(u8 taskId);
+static void Task_ContextMenu_FromPokemonPC(u8 taskId);
+static void Task_ContextMenu_Sell(u8 taskId);
+static void Task_AskSellMultiple(u8 taskId);
+static void Task_SellMultiple_CreateYesNoMenu(u8 taskId);
+static void Task_SellNo(u8 taskId);
+static void Task_Sell_PrintSelectMultipleUI(u8 taskId);
+static void SellMultiple_UpdateSellPriceDisplay(s32 price);
+static void Task_Sell_SelectMultiple(u8 taskId);
+static void Task_SellYes(u8 taskId);
+static void Task_SellBerries_PlaySfxAndRemoveBerries(u8 taskId);
+static void Task_SellBerries_WaitButton(u8 taskId);
+static void BerryPouchInitWindows(void);
+static void BerryPouchPrint(u8 windowId, u8 fontId, const u8 * str, u8 x, u8 y, u8 letterSpacing, u8 lineSpacing, u8 speed, u8 colorIdx);
+static u8 GetOrCreateVariableWindow(u8 winIdx);
+static void DestroyVariableWindow(u8 winIdx);
+static void TryDestroyVariableWindow(u8 winIdx);
+static u8 GetVariableWindowId(u8 winIdx);
+static void CreateYesNoMenuWin3(u8 taskId, const struct YesNoFuncTable *ptrs);
+static void CreateYesNoMenuWin4(u8 taskId, const struct YesNoFuncTable *ptrs);
+static void PrintMoneyInWin2(void);
+static void CreateBerryPouchSprite(void);
+static void StartBerryPouchSpriteWobbleAnim(void);
+static void SpriteCB_BerryPouchWaitWobbleAnim(struct Sprite *sprite);
+
+static const struct BgTemplate sBgTemplates[] = {
+ {
+ .bg = 0,
+ .charBaseIndex = 0,
+ .mapBaseIndex = 31,
+ .screenSize = 0,
+ .paletteMode = 0,
+ .priority = 1,
+ .baseTile = 0x000
+ }, {
+ .bg = 1,
+ .charBaseIndex = 3,
+ .mapBaseIndex = 30,
+ .screenSize = 0,
+ .paletteMode = 0,
+ .priority = 2,
+ .baseTile = 0x000
+ }, {
+ .bg = 2,
+ .charBaseIndex = 0,
+ .mapBaseIndex = 29,
+ .screenSize = 0,
+ .paletteMode = 0,
+ .priority = 0,
+ .baseTile = 0x000
+ }
+};
+
+static const TaskFunc sBerryPouchContextMenuTasks[] = {
+ Task_NormalContextMenu,
+ Task_ContextMenu_FromPartyGiveMenu,
+ Task_ContextMenu_Sell,
+ Task_ContextMenu_FromPokemonPC,
+ Task_NormalContextMenu
+};
+
+static const struct YesNoFuncTable sYesNoFuncs_Toss = {
+ .yesFunc = Task_TossYes,
+ .noFunc = Task_TossNo
+};
+
+static const struct YesNoFuncTable sYesNoFuncs_Sell = {
+ .yesFunc = Task_SellYes,
+ .noFunc = Task_SellNo
+};
+
+static const struct MenuAction sContextMenuActions[] = {
+ {gOtherText_Use, Task_BerryPouch_Use},
+ {gOtherText_Toss, Task_BerryPouch_Toss},
+ {gOtherText_Give, Task_BerryPouch_Give},
+ {gOtherText_Exit, Task_BerryPouch_Exit},
+ {gString_Dummy, NULL}
+};
+
+static const u8 sOptions_UseGiveTossExit[] = {
+ BP_ACTION_USE,
+ BP_ACTION_GIVE,
+ BP_ACTION_TOSS,
+ BP_ACTION_EXIT
+};
+
+static const u8 sOptions_GiveExit[] = {
+ BP_ACTION_GIVE,
+ BP_ACTION_EXIT,
+ BP_ACTION_DUMMY,
+ BP_ACTION_DUMMY
+};
+
+static const u8 sOptions_Exit[] = {
+ BP_ACTION_EXIT,
+ BP_ACTION_DUMMY,
+ BP_ACTION_DUMMY,
+ BP_ACTION_DUMMY
+};
+
+static const u8 sOptions_UseToss_Exit[] = {
+ BP_ACTION_USE,
+ BP_ACTION_TOSS,
+ BP_ACTION_EXIT,
+ BP_ACTION_DUMMY
+};
+
+static const u8 sText_Space[] = _(" ");
+
+static const struct WindowTemplate sWindowTemplates_Main[] = {
+ {
+ .bg = 0,
+ .tilemapLeft = 11,
+ .tilemapTop = 1,
+ .width = 18,
+ .height = 14,
+ .paletteNum = 15,
+ .baseBlock = 0x027
+ }, {
+ .bg = 0,
+ .tilemapLeft = 5,
+ .tilemapTop = 16,
+ .width = 25,
+ .height = 4,
+ .paletteNum = 15,
+ .baseBlock = 0x123
+ }, {
+ .bg = 2,
+ .tilemapLeft = 1,
+ .tilemapTop = 1,
+ .width = 9,
+ .height = 2,
+ .paletteNum = 15,
+ .baseBlock = 0x187
+ }, DUMMY_WIN_TEMPLATE
+};
+
+static const struct WindowTemplate sWindowTemplates_Variable[] = {
+ {
+ .bg = 0x02,
+ .tilemapLeft = 0x18,
+ .tilemapTop = 0x0f,
+ .width = 0x05,
+ .height = 0x04,
+ .paletteNum = 0x0f,
+ .baseBlock = 0x1d1
+ }, {
+ .bg = 0x02,
+ .tilemapLeft = 0x11,
+ .tilemapTop = 0x09,
+ .width = 0x0c,
+ .height = 0x04,
+ .paletteNum = 0x0f,
+ .baseBlock = 0x1d1
+ }, {
+ .bg = 0x02,
+ .tilemapLeft = 0x01,
+ .tilemapTop = 0x01,
+ .width = 0x08,
+ .height = 0x03,
+ .paletteNum = 0x0c,
+ .baseBlock = 0x201
+ }, {
+ .bg = 0x02,
+ .tilemapLeft = 0x17,
+ .tilemapTop = 0x0f,
+ .width = 0x06,
+ .height = 0x04,
+ .paletteNum = 0x0f,
+ .baseBlock = 0x219
+ }, {
+ .bg = 0x02,
+ .tilemapLeft = 0x15,
+ .tilemapTop = 0x09,
+ .width = 0x06,
+ .height = 0x04,
+ .paletteNum = 0x0f,
+ .baseBlock = 0x219
+ }, {
+ .bg = 0x02,
+ .tilemapLeft = 0x02,
+ .tilemapTop = 0x0f,
+ .width = 0x1a,
+ .height = 0x04,
+ .paletteNum = 0x0f,
+ .baseBlock = 0x231
+ }, {
+ .bg = 0x02,
+ .tilemapLeft = 0x06,
+ .tilemapTop = 0x0f,
+ .width = 0x0e,
+ .height = 0x04,
+ .paletteNum = 0x0c,
+ .baseBlock = 0x231
+ }, {
+ .bg = 0x02,
+ .tilemapLeft = 0x06,
+ .tilemapTop = 0x0f,
+ .width = 0x0f,
+ .height = 0x04,
+ .paletteNum = 0x0c,
+ .baseBlock = 0x269
+ }, {
+ .bg = 0x02,
+ .tilemapLeft = 0x06,
+ .tilemapTop = 0x0f,
+ .width = 0x10,
+ .height = 0x04,
+ .paletteNum = 0x0c,
+ .baseBlock = 0x2a5
+ }, {
+ .bg = 0x02,
+ .tilemapLeft = 0x06,
+ .tilemapTop = 0x0f,
+ .width = 0x17,
+ .height = 0x04,
+ .paletteNum = 0x0c,
+ .baseBlock = 0x2e5
+ }, {
+ .bg = 0x02,
+ .tilemapLeft = 0x16,
+ .tilemapTop = 0x11,
+ .width = 0x07,
+ .height = 0x02,
+ .paletteNum = 0x0f,
+ .baseBlock = 0x199
+ }, {
+ .bg = 0x02,
+ .tilemapLeft = 0x16,
+ .tilemapTop = 0x0f,
+ .width = 0x07,
+ .height = 0x04,
+ .paletteNum = 0x0f,
+ .baseBlock = 0x199
+ }, {
+ .bg = 0x02,
+ .tilemapLeft = 0x16,
+ .tilemapTop = 0x0d,
+ .width = 0x07,
+ .height = 0x06,
+ .paletteNum = 0x0f,
+ .baseBlock = 0x199
+ }, {
+ .bg = 0x02,
+ .tilemapLeft = 0x16,
+ .tilemapTop = 0x0b,
+ .width = 0x07,
+ .height = 0x08,
+ .paletteNum = 0x0f,
+ .baseBlock = 0x199
+ }
+};
+
+static const u8 sTextColors[][3] = {
+ { 0, 1, 2 },
+ { 0, 2, 3 },
+ { 0, 3, 2 }
+};
+
+static const struct OamData sOamData = {
+ .affineMode = ST_OAM_AFFINE_NORMAL,
+ .shape = ST_OAM_SQUARE,
+ .size = ST_OAM_SIZE_3,
+ .priority = 1
+};
+
+static const union AnimCmd sSpriteAnim_Dummy[] = {
+ ANIMCMD_FRAME(0, 0),
+ ANIMCMD_END
+};
+
+static const union AnimCmd *const sSpriteAnimTable[] = {
+ sSpriteAnim_Dummy
+};
+
+static const union AffineAnimCmd sSpriteAffineAnim_Static[] = {
+ AFFINEANIMCMD_FRAME(0x100, 0x100, 0, 0),
+ AFFINEANIMCMD_END
+};
+
+static const union AffineAnimCmd sSpriteAffineAnim_Wobble[] = {
+ AFFINEANIMCMD_FRAME(0, 0, -2, 2),
+ AFFINEANIMCMD_FRAME(0, 0, 2, 4),
+ AFFINEANIMCMD_FRAME(0, 0, -2, 4),
+ AFFINEANIMCMD_FRAME(0, 0, 2, 2),
+ AFFINEANIMCMD_END
+};
+
+static const union AffineAnimCmd *const sSpriteAffineAnimTable[] = {
+ sSpriteAffineAnim_Static,
+ sSpriteAffineAnim_Wobble
+};
+
+static const struct CompressedSpriteSheet sBerryPouchSpriteSheet = {
+ gBerryPouchSpriteTiles, 0x800, 100
+};
+
+static const struct CompressedSpritePalette sBerryPouchSpritePal = {
+ gBerryPouchSpritePalette, 100
+};
+
+static const struct SpriteTemplate sSpriteTemplate_BerryPouch = {
+ 100, 100, &sOamData, sSpriteAnimTable, NULL, sSpriteAffineAnimTable, SpriteCallbackDummy
+};
+
+void InitBerryPouch(u8 type, void (*savedCallback)(void), u8 allowSelect)
+{
+ u8 i;
+
+ sResources = Alloc(sizeof(struct BerryPouchStruct_203F36C));
+ if (sResources == NULL)
+ {
+ SetMainCallback2(savedCallback);
+ }
+ else
+ {
+ if (type != BERRYPOUCH_NA)
+ sStaticCnt.type = type;
+ if (allowSelect != 0xFF)
+ sStaticCnt.allowSelect = allowSelect;
+ if (savedCallback != NULL)
+ sStaticCnt.savedCallback = savedCallback;
+ sResources->exitCallback = NULL;
+ sResources->itemMenuIconId = 0;
+ sResources->indicatorTaskId = 0xFF;
+ for (i = 0; i < 4; i++)
+ sResources->data[i] = 0;
+ gTextFlags.autoScroll = FALSE;
+ gSpecialVar_ItemId = ITEM_NONE;
+ SetMainCallback2(CB2_InitBerryPouch);
+ }
+}
+
+static void CB2_BerryPouchIdle(void)
+{
+ RunTasks();
+ AnimateSprites();
+ BuildOamBuffer();
+ DoScheduledBgTilemapCopiesToVram();
+ UpdatePaletteFade();
+}
+
+static void VBlankCB_BerryPouchIdle(void)
+{
+ LoadOam();
+ ProcessSpriteCopyRequests();
+ TransferPlttBuffer();
+}
+
+static void CB2_InitBerryPouch(void)
+{
+ while (1)
+ {
+ if (sub_80BF72C() == TRUE)
+ break;
+ if (RunBerryPouchInit() == TRUE)
+ break;
+ if (MenuHelpers_LinkSomething() == TRUE)
+ break;
+ }
+}
+
+static bool8 RunBerryPouchInit(void)
+{
+ u8 taskId;
+
+ switch (gMain.state)
+ {
+ case 0:
+ SetVBlankHBlankCallbacksToNull();
+ ClearScheduledBgCopiesToVram();
+ gMain.state++;
+ break;
+ case 1:
+ ScanlineEffect_Stop();
+ gMain.state++;
+ break;
+ case 2:
+ FreeAllSpritePalettes();
+ gMain.state++;
+ break;
+ case 3:
+ ResetPaletteFade();
+ gMain.state++;
+ break;
+ case 4:
+ ResetSpriteData();
+ gMain.state++;
+ break;
+ case 5:
+ ResetItemMenuIconState();
+ gMain.state++;
+ break;
+ case 6:
+ if (!MenuHelpers_LinkSomething())
+ ResetTasks();
+ gMain.state++;
+ break;
+ case 7:
+ BerryPouchInitBgs();
+ sResources->data[0] = 0;
+ gMain.state++;
+ break;
+ case 8:
+ if (BerryPouchLoadGfx())
+ gMain.state++;
+ break;
+ case 9:
+ BerryPouchInitWindows();
+ gMain.state++;
+ break;
+ case 10:
+ SortAndCountBerries();
+ SanitizeListMenuSelectionParams();
+ UpdateListMenuScrollOffset();
+ gMain.state++;
+ break;
+ case 11:
+ if (!AllocateListMenuBuffers())
+ {
+ AbortBerryPouchLoading();
+ return TRUE;
+ }
+ gMain.state++;
+ break;
+ case 12:
+ SetUpListMenuTemplate();
+ gMain.state++;
+ break;
+ case 13:
+ PrintBerryPouchHeaderCentered();
+ gMain.state++;
+ break;
+ case 14:
+ taskId = CreateTask(Task_BerryPouchMain, 0);
+ gTasks[taskId].data[0] = ListMenuInit(&gMultiuseListMenuTemplate, sStaticCnt.listMenuScrollOffset, sStaticCnt.listMenuSelectedRow);
+ gTasks[taskId].data[8] = 0;
+ gMain.state++;
+ break;
+ case 15:
+ CreateBerryPouchSprite();
+ gMain.state++;
+ break;
+ case 16:
+ CreateScrollIndicatorArrows_BerryPouchList();
+ gMain.state++;
+ break;
+ case 17:
+ BlendPalettes(0xFFFFFFFF, 16, RGB_BLACK);
+ gMain.state++;
+ break;
+ case 18:
+ BeginNormalPaletteFade(0xFFFFFFFF, -2, 16, 0, RGB_BLACK);
+ gMain.state++;
+ break;
+ default:
+ SetVBlankCallback(VBlankCB_BerryPouchIdle);
+ SetMainCallback2(CB2_BerryPouchIdle);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void AbortBerryPouchLoading(void)
+{
+ BeginNormalPaletteFade(0xFFFFFFFF, -2, 0, 16, RGB_BLACK);
+ CreateTask(Task_AbortBerryPouchLoading_WaitFade, 0);
+ SetVBlankCallback(VBlankCB_BerryPouchIdle);
+ SetMainCallback2(CB2_BerryPouchIdle);
+}
+
+static void Task_AbortBerryPouchLoading_WaitFade(u8 taskId)
+{
+ if (!gPaletteFade.active)
+ {
+ SetMainCallback2(sStaticCnt.savedCallback);
+ BerryPouch_DestroyResources();
+ DestroyTask(taskId);
+ }
+}
+
+static void BerryPouchInitBgs(void)
+{
+ ResetAllBgsCoordinatesAndBgCntRegs();
+ memset(sResources->bg1TilemapBuffer, 0, BG_SCREEN_SIZE);
+ ResetBgsAndClearDma3BusyFlags(FALSE);
+ InitBgsFromTemplates(0, sBgTemplates, NELEMS(sBgTemplates));
+ SetBgTilemapBuffer(1, sResources->bg1TilemapBuffer);
+ ScheduleBgCopyTilemapToVram(1);
+ SetGpuReg(REG_OFFSET_BLDCNT, 0);
+ SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_MODE_0 | DISPCNT_OBJ_1D_MAP | DISPCNT_OBJ_ON);
+ ShowBg(0);
+ ShowBg(1);
+ ShowBg(2);
+}
+
+static bool8 BerryPouchLoadGfx(void)
+{
+ switch (sResources->data[0])
+ {
+ case 0:
+ ResetTempTileDataBuffers();
+ DecompressAndCopyTileDataToVram(1, gBerryPouchBgGfx, 0, 0, 0);
+ sResources->data[0]++;
+ break;
+ case 1:
+ if (FreeTempTileDataBuffersIfPossible() != TRUE)
+ {
+ LZDecompressWram(gBerryPouchBg1Tilemap, sResources->bg1TilemapBuffer);
+ sResources->data[0]++;
+ }
+ break;
+ case 2:
+ LoadCompressedPalette(gBerryPouchBgPals, 0, 0x60);
+ if (gSaveBlock2Ptr->playerGender != MALE)
+ LoadCompressedPalette(gBerryPouchBgPal0FemaleOverride, 0, 0x20);
+ sResources->data[0]++;
+ break;
+ case 3:
+ LoadCompressedSpriteSheet(&sBerryPouchSpriteSheet);
+ sResources->data[0]++;
+ break;
+ default:
+ LoadCompressedSpritePalette(&sBerryPouchSpritePal);
+ sResources->data[0] = 0;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static bool8 AllocateListMenuBuffers(void)
+{
+ sListMenuItems = Alloc(NUM_BERRIES * sizeof(struct ListMenuItem));
+ if (sListMenuItems == NULL)
+ return FALSE;
+ sListMenuStrbuf = Alloc(sResources->listMenuNumItems * 27);
+ if (sListMenuStrbuf == NULL)
+ return FALSE;
+ return TRUE;
+}
+
+static void SetUpListMenuTemplate(void)
+{
+ u16 i;
+ struct BagPocket *pocket = &gBagPockets[POCKET_BERRY_POUCH - 1];
+ for (i = 0; i < sResources->listMenuNumItems; i++)
+ {
+ GetBerryNameAndIndexForMenu(&sListMenuStrbuf[i * 27], pocket->itemSlots[i].itemId);
+ sListMenuItems[i].label = &sListMenuStrbuf[i * 27];
+ sListMenuItems[i].index = i;
+ }
+ sListMenuItems[i].label = gText_Close;
+ sListMenuItems[i].index = i;
+ gMultiuseListMenuTemplate.items = sListMenuItems;
+ if (sStaticCnt.type != BERRYPOUCH_FROMBERRYCRUSH)
+ gMultiuseListMenuTemplate.totalItems = sResources->listMenuNumItems + 1;
+ else
+ gMultiuseListMenuTemplate.totalItems = sResources->listMenuNumItems;
+ gMultiuseListMenuTemplate.windowId = 0;
+ gMultiuseListMenuTemplate.header_X = 0;
+ gMultiuseListMenuTemplate.item_X = 9;
+ gMultiuseListMenuTemplate.cursor_X = 1;
+ gMultiuseListMenuTemplate.lettersSpacing = 0;
+ gMultiuseListMenuTemplate.itemVerticalPadding = 2;
+ gMultiuseListMenuTemplate.upText_Y = 2;
+ gMultiuseListMenuTemplate.maxShowed = sResources->listMenuMaxShowed;
+ gMultiuseListMenuTemplate.fontId = 2;
+ gMultiuseListMenuTemplate.cursorPal = 2;
+ gMultiuseListMenuTemplate.fillValue = 0;
+ gMultiuseListMenuTemplate.cursorShadowPal = 3;
+ gMultiuseListMenuTemplate.moveCursorFunc = BerryPouchMoveCursorFunc;
+ gMultiuseListMenuTemplate.itemPrintFunc = BerryPouchItemPrintFunc;
+ gMultiuseListMenuTemplate.cursorKind = 0;
+ gMultiuseListMenuTemplate.scrollMultiple = 0;
+}
+
+static void GetBerryNameAndIndexForMenu(u8 * dest, u16 itemId)
+{
+ StringCopy(gStringVar4, gText_FontSize0);
+ StringAppend(gStringVar4, gOtherText_UnkF9_08_Clear_01);
+ ConvertIntToDecimalStringN(gStringVar1, itemId - FIRST_BERRY_INDEX + 1, STR_CONV_MODE_LEADING_ZEROS, 2);
+ StringAppend(gStringVar4, gStringVar1);
+ CopyItemName(itemId, gStringVar1);
+ StringAppend(gStringVar4, sText_Space);
+ StringAppend(gStringVar4, gText_FontSize2);
+ StringAppend(gStringVar4, gStringVar1);
+ StringCopy(dest, gStringVar4);
+}
+
+static void CopySelectedListMenuItemName(s16 itemIdx, u8 * dest)
+{
+ StringCopy(dest, &sListMenuStrbuf[itemIdx * 27]);
+}
+
+static void BerryPouchMoveCursorFunc(s32 itemIndex, bool8 onInit, struct ListMenu *list)
+{
+ if (onInit != TRUE)
+ {
+ PlaySE(SE_W287B);
+ StartBerryPouchSpriteWobbleAnim();
+ }
+ DestroyItemMenuIcon(sResources->itemMenuIconId ^ 1);
+ if (sResources->listMenuNumItems != itemIndex)
+ sub_80989A0(BagGetItemIdByPocketPosition(POCKET_BERRY_POUCH, itemIndex), sResources->itemMenuIconId);
+ else
+ sub_80989A0(ITEM_N_A, sResources->itemMenuIconId);
+ sResources->itemMenuIconId ^= 1;
+ PrintSelectedBerryDescription(itemIndex);
+}
+
+static void BerryPouchItemPrintFunc(u8 windowId, s32 itemId, u8 y)
+{
+ u16 unused;
+ u16 itemQuantity;
+ if (itemId != -2 && sResources->listMenuNumItems != itemId)
+ {
+ unused = BagGetItemIdByPocketPosition(POCKET_BERRY_POUCH, itemId);
+ itemQuantity = BagGetQuantityByPocketPosition(POCKET_BERRY_POUCH, itemId);
+ ConvertIntToDecimalStringN(gStringVar1, itemQuantity, STR_CONV_MODE_RIGHT_ALIGN, 3);
+ StringExpandPlaceholders(gStringVar4, gText_TimesStrVar1);
+ BerryPouchPrint(windowId, 0, gStringVar4, 110, y, 0, 0, 0xFF, 1);
+ }
+}
+
+static void BerryPouchSetArrowCursorFromListMenu(u8 taskId, u8 colorIdx)
+{
+ BerryPouchSetArrowCursorAt(ListMenuGetYCoordForPrintingArrowCursor(taskId), colorIdx);
+}
+
+static void BerryPouchSetArrowCursorAt(u8 y, u8 colorIdx)
+{
+ u8 width;
+ u8 height;
+ if (colorIdx == 0xFF)
+ {
+ width = GetMenuCursorDimensionByFont(2, 0);
+ height = GetMenuCursorDimensionByFont(2, 1);
+ FillWindowPixelRect(0, 0, 1, y, width, height);
+ CopyWindowToVram(0, 2);
+ }
+ else
+ {
+ BerryPouchPrint(0, 2, gFameCheckerText_ListMenuCursor, 1, y, 0, 0, 0, colorIdx);
+ }
+}
+
+static void PrintSelectedBerryDescription(s32 itemIdx)
+{
+ const u8 * str;
+ if (itemIdx != sResources->listMenuNumItems)
+ str = ItemId_GetDescription(BagGetItemIdByPocketPosition(POCKET_BERRY_POUCH, itemIdx));
+ else
+ str = gText_TheBerryPouchWillBePutAway;
+ FillWindowPixelBuffer(1, PIXEL_FILL(0));
+ BerryPouchPrint(1, 2, str, 0, 2, 2, 0, 0, 0);
+}
+
+static void SetDescriptionWindowBorderPalette(s32 pal)
+{
+ SetBgRectPal(1, 0, 16, 30, 4, pal + 1);
+ ScheduleBgCopyTilemapToVram(1);
+}
+
+static void CreateScrollIndicatorArrows_BerryPouchList(void)
+{
+ if (sStaticCnt.type != BERRYPOUCH_FROMBERRYCRUSH)
+ sResources->indicatorTaskId = AddScrollIndicatorArrowPairParameterized(2, 160, 8, 120, sResources->listMenuNumItems - sResources->listMenuMaxShowed + 1, 110, 110, &sStaticCnt.listMenuScrollOffset);
+ else
+ sResources->indicatorTaskId = AddScrollIndicatorArrowPairParameterized(2, 160, 8, 120, sResources->listMenuNumItems - sResources->listMenuMaxShowed, 110, 110, &sStaticCnt.listMenuScrollOffset);
+}
+
+static void CreateScrollIndicatorArrows_TossQuantity(void)
+{
+ sResources->indicatorOffset = 1;
+ sResources->indicatorTaskId = AddScrollIndicatorArrowPairParameterized(2, 212, 120, 152, 2, 110, 110, &sResources->indicatorOffset);
+}
+
+static void CreateScrollIndicatorArrows_SellQuantity(void)
+{
+ sResources->indicatorOffset = 1;
+ sResources->indicatorTaskId = AddScrollIndicatorArrowPairParameterized(2, 152, 72, 104, 2, 110, 110, &sResources->indicatorOffset);
+}
+
+static void DestroyScrollIndicatorArrows(void)
+{
+ if (sResources->indicatorTaskId != 0xFF)
+ {
+ RemoveScrollIndicatorArrowPair(sResources->indicatorTaskId);
+ sResources->indicatorTaskId = 0xFF;
+ }
+}
+
+static void PrintBerryPouchHeaderCentered(void)
+{
+ u32 slack = 72 - GetStringWidth(1, gText_BerryPouch, 0);
+ BerryPouchPrint(2, 1, gText_BerryPouch, slack / 2, 1, 0, 0, 0, 0);
+}
+
+void BerryPouch_CursorResetToTop(void)
+{
+ sStaticCnt.listMenuSelectedRow = 0;
+ sStaticCnt.listMenuScrollOffset = 0;
+}
+
+static void SanitizeListMenuSelectionParams(void)
+{
+ s32 r2;
+ if (sStaticCnt.type != BERRYPOUCH_FROMBERRYCRUSH)
+ r2 = sResources->listMenuNumItems + 1;
+ else
+ r2 = sResources->listMenuNumItems;
+ if (sStaticCnt.listMenuScrollOffset != 0 && sStaticCnt.listMenuScrollOffset + sResources->listMenuMaxShowed > r2)
+ sStaticCnt.listMenuScrollOffset = r2 - sResources->listMenuMaxShowed;
+ if (sStaticCnt.listMenuScrollOffset + sStaticCnt.listMenuSelectedRow >= r2)
+ {
+ if (r2 == 0 || r2 == 1)
+ sStaticCnt.listMenuSelectedRow = 0;
+ else
+ sStaticCnt.listMenuSelectedRow = r2 - 1;
+ }
+}
+
+static void UpdateListMenuScrollOffset(void)
+{
+ u8 lim;
+ u8 i;
+ if (sStaticCnt.type != BERRYPOUCH_FROMBERRYCRUSH)
+ lim = sResources->listMenuNumItems + 1;
+ else
+ lim = sResources->listMenuNumItems;
+ if (sStaticCnt.listMenuSelectedRow > 4)
+ {
+ for (i = 0; i <= sStaticCnt.listMenuSelectedRow - 4; sStaticCnt.listMenuSelectedRow--, sStaticCnt.listMenuScrollOffset++, i++)
+ {
+ if (sStaticCnt.listMenuScrollOffset + sResources->listMenuMaxShowed == lim)
+ break;
+ }
+ }
+}
+
+static void BerryPouch_DestroyResources(void)
+{
+ if (sResources != NULL)
+ Free(sResources);
+ if (sListMenuItems != NULL)
+ Free(sListMenuItems);
+ if (sListMenuStrbuf != NULL)
+ Free(sListMenuStrbuf);
+ FreeAllWindowBuffers();
+}
+
+void BerryPouch_StartFadeToExitCallback(u8 taskId)
+{
+ BeginNormalPaletteFade(0xFFFFFFFF, -2, 0, 16, RGB_BLACK);
+ gTasks[taskId].func = Task_BerryPouchFadeToExitCallback;
+}
+
+static void Task_BerryPouchFadeToExitCallback(u8 taskId)
+{
+ s16 * data = gTasks[taskId].data;
+ if (!gPaletteFade.active)
+ {
+ DestroyListMenuTask(data[0], &sStaticCnt.listMenuScrollOffset, &sStaticCnt.listMenuSelectedRow);
+ if (sResources->exitCallback != NULL)
+ SetMainCallback2(sResources->exitCallback);
+ else
+ SetMainCallback2(sStaticCnt.savedCallback);
+ DestroyScrollIndicatorArrows();
+ BerryPouch_DestroyResources();
+ DestroyTask(taskId);
+ }
+}
+
+static void SortAndCountBerries(void)
+{
+ u16 i;
+ u32 r2;
+ struct BagPocket *pocket = &gBagPockets[POCKET_BERRY_POUCH - 1];
+ SortAndCompactBagPocket(pocket);
+ sResources->listMenuNumItems = 0;
+ for (i = 0; i < pocket->capacity; i++)
+ {
+ if (pocket->itemSlots[i].itemId == ITEM_NONE)
+ break;
+ sResources->listMenuNumItems++;
+ }
+ if (sStaticCnt.type != BERRYPOUCH_FROMBERRYCRUSH)
+ r2 = sResources->listMenuNumItems + 1;
+ else
+ r2 = sResources->listMenuNumItems;
+ if (r2 > 7)
+ sResources->listMenuMaxShowed = 7;
+ else
+ sResources->listMenuMaxShowed = r2;
+}
+
+void BerryPouch_SetExitCallback(void (*callback)(void))
+{
+ sResources->exitCallback = callback;
+}
+
+void InitTossQuantitySelectUI(u8 taskId, const u8 * str)
+{
+ s16 * data = gTasks[taskId].data;
+ u8 windowId = GetOrCreateVariableWindow(8);
+ u8 windowId2;
+ CopySelectedListMenuItemName(data[1], gStringVar1);
+ StringExpandPlaceholders(gStringVar4, str);
+ BerryPouchPrint(windowId, 2, gStringVar4, 0, 2, 1, 2, 0, 1);
+ windowId2 = GetOrCreateVariableWindow(0);
+ ConvertIntToDecimalStringN(gStringVar1, 1, STR_CONV_MODE_LEADING_ZEROS, 3);
+ StringExpandPlaceholders(gStringVar4, gText_TimesStrVar1);
+ BerryPouchPrint(windowId2, 0, gStringVar4, 4, 10, 1, 0, 0, 1);
+}
+
+static void PrintxQuantityOnWindow(u8 whichWindow, s16 quantity, u8 ndigits)
+{
+ u8 windowId = GetVariableWindowId(whichWindow);
+ FillWindowPixelBuffer(windowId, PIXEL_FILL(1));
+ ConvertIntToDecimalStringN(gStringVar1, quantity, STR_CONV_MODE_LEADING_ZEROS, ndigits);
+ StringExpandPlaceholders(gStringVar4, gText_TimesStrVar1);
+ BerryPouchPrint(windowId, 0, gStringVar4, 4, 10, 1, 0, 0, 1);
+}
+
+static void Task_BerryPouchMain(u8 taskId)
+{
+ s16 * data = gTasks[taskId].data;
+ s32 menuInput;
+ if (!gPaletteFade.active && sub_80BF72C() != TRUE)
+ {
+ menuInput = ListMenu_ProcessInput(data[0]);
+ ListMenuGetScrollAndRow(data[0], &sStaticCnt.listMenuScrollOffset, &sStaticCnt.listMenuSelectedRow);
+ if (JOY_NEW(SELECT_BUTTON) && sStaticCnt.allowSelect == 1)
+ {
+ PlaySE(SE_SELECT);
+ gSpecialVar_ItemId = 0;
+ BerryPouch_StartFadeToExitCallback(taskId);
+ }
+ else
+ {
+ switch (menuInput)
+ {
+ case -1:
+ return;
+ case -2:
+ if (sStaticCnt.type != BERRYPOUCH_FROMBERRYCRUSH)
+ {
+ PlaySE(SE_SELECT);
+ gSpecialVar_ItemId = 0;
+ BerryPouch_StartFadeToExitCallback(taskId);
+ }
+ break;
+ default:
+ PlaySE(SE_SELECT);
+ if (sStaticCnt.type == BERRYPOUCH_FROMBERRYCRUSH)
+ {
+ gSpecialVar_ItemId = BagGetItemIdByPocketPosition(POCKET_BERRY_POUCH, menuInput);
+ BerryPouch_StartFadeToExitCallback(taskId);
+ }
+ else if (menuInput == sResources->listMenuNumItems)
+ {
+ gSpecialVar_ItemId = 0;
+ BerryPouch_StartFadeToExitCallback(taskId);
+ }
+ else
+ {
+ DestroyScrollIndicatorArrows();
+ SetDescriptionWindowBorderPalette(1);
+ BerryPouchSetArrowCursorFromListMenu(data[0], 2);
+ data[1] = menuInput;
+ data[2] = BagGetQuantityByPocketPosition(POCKET_BERRY_POUCH, menuInput);
+ gSpecialVar_ItemId = BagGetItemIdByPocketPosition(POCKET_BERRY_POUCH, menuInput);
+ gTasks[taskId].func = sBerryPouchContextMenuTasks[sStaticCnt.type];
+ }
+ break;
+ }
+ }
+ }
+}
+
+static void Task_CleanUpAndReturnToMain(u8 taskId)
+{
+ SetDescriptionWindowBorderPalette(0);
+ CreateScrollIndicatorArrows_BerryPouchList();
+ gTasks[taskId].func = Task_BerryPouchMain;
+}
+
+static void CreateNormalContextMenu(u8 taskId)
+{
+ s16 * data = gTasks[taskId].data;
+ u8 windowId;
+ u8 windowId2;
+
+ if (sStaticCnt.type == BERRYPOUCH_FROMBATTLE)
+ {
+ sContextMenuOptions = sOptions_UseToss_Exit;
+ sContextMenuNumOptions = 3;
+ }
+ else if (MenuHelpers_LinkSomething() == TRUE || InUnionRoom() == TRUE)
+ {
+ if (!itemid_link_can_give_berry(gSpecialVar_ItemId))
+ {
+ sContextMenuOptions = sOptions_Exit;
+ sContextMenuNumOptions = 1;
+ }
+ else
+ {
+ sContextMenuOptions = sOptions_GiveExit;
+ sContextMenuNumOptions = 2;
+ }
+ }
+ else
+ {
+ sContextMenuOptions = sOptions_UseGiveTossExit;
+ sContextMenuNumOptions = 4;
+ }
+ windowId = GetOrCreateVariableWindow(sContextMenuNumOptions + 9);
+ AddItemMenuActionTextPrinters(windowId, 2, GetMenuCursorDimensionByFont(2, 0), 2, GetFontAttribute(2, FONTATTR_LETTER_SPACING), GetFontAttribute(2, FONTATTR_MAX_LETTER_HEIGHT) + 2, sContextMenuNumOptions, sContextMenuActions, sContextMenuOptions);
+ Menu_InitCursor(windowId, 2, 0, 2, GetFontAttribute(2, FONTATTR_MAX_LETTER_HEIGHT) + 2, sContextMenuNumOptions, 0);
+ windowId2 = GetOrCreateVariableWindow(6);
+ CopySelectedListMenuItemName(data[1], gStringVar1);
+ StringExpandPlaceholders(gStringVar4, gOtherText_StrVar1);
+ BerryPouchPrint(windowId2, 2, gStringVar4, 0, 2, 1, 2, 0, 1);
+}
+
+static void Task_NormalContextMenu(u8 taskId)
+{
+ CreateNormalContextMenu(taskId);
+ gTasks[taskId].func = Task_NormalContextMenu_HandleInput;
+}
+
+static void Task_NormalContextMenu_HandleInput(u8 taskId)
+{
+ s8 input;
+ if (sub_80BF72C() != TRUE)
+ {
+ input = Menu_ProcessInputNoWrapAround();
+ switch (input)
+ {
+ case -2:
+ break;
+ case -1:
+ PlaySE(SE_SELECT);
+ sContextMenuActions[BP_ACTION_EXIT].func.void_u8(taskId);
+ break;
+ default:
+ PlaySE(SE_SELECT);
+ sContextMenuActions[sContextMenuOptions[input]].func.void_u8(taskId);
+ break;
+ }
+ }
+}
+
+static void Task_BerryPouch_Use(u8 taskId)
+{
+ DestroyVariableWindow(sContextMenuNumOptions + 9);
+ DestroyVariableWindow(6);
+ PutWindowTilemap(0);
+ PutWindowTilemap(1);
+ ScheduleBgCopyTilemapToVram(0);
+ ScheduleBgCopyTilemapToVram(2);
+ if (sStaticCnt.type == BERRYPOUCH_FROMBATTLE)
+ {
+ if (ItemId_GetBattleFunc(gSpecialVar_ItemId) == NULL)
+ FieldUseFunc_OakStopsYou(taskId);
+ else
+ ItemId_GetBattleFunc(gSpecialVar_ItemId)(taskId);
+ }
+ else if (CalculatePlayerPartyCount() == 0 && ItemId_GetType(gSpecialVar_ItemId) == 1)
+ Task_Give_PrintThereIsNoPokemon(taskId);
+ else
+ ItemId_GetFieldFunc(gSpecialVar_ItemId)(taskId);
+}
+
+static void Task_BerryPouch_Toss(u8 taskId)
+{
+ s16 * data = gTasks[taskId].data;
+ ClearWindowTilemap(GetVariableWindowId(sContextMenuNumOptions + 9));
+ ClearWindowTilemap(GetVariableWindowId(6));
+ DestroyVariableWindow(sContextMenuNumOptions + 9);
+ DestroyVariableWindow(6);
+ PutWindowTilemap(0);
+ data[8] = 1;
+ if (data[2] == 1)
+ Task_AskTossMultiple(taskId);
+ else
+ {
+ InitTossQuantitySelectUI(taskId, gText_TossOutHowManyStrVar1s);
+ CreateScrollIndicatorArrows_TossQuantity();
+ gTasks[taskId].func = Task_Toss_SelectMultiple;
+ }
+}
+
+static void Task_AskTossMultiple(u8 taskId)
+{
+ s16 * data = gTasks[taskId].data;
+ ConvertIntToDecimalStringN(gStringVar2, data[8], STR_CONV_MODE_LEFT_ALIGN, 3);
+ StringExpandPlaceholders(gStringVar4, gText_ThrowAwayStrVar2OfThisItemQM);
+ BerryPouchPrint(GetOrCreateVariableWindow(7), 2, gStringVar4, 0, 2, 1, 2, 0, 1);
+ CreateYesNoMenuWin3(taskId, &sYesNoFuncs_Toss);
+}
+
+static void Task_TossNo(u8 taskId)
+{
+ s16 * data = gTasks[taskId].data;
+ DestroyVariableWindow(7);
+ PutWindowTilemap(1);
+ PutWindowTilemap(0);
+ ScheduleBgCopyTilemapToVram(0);
+ ScheduleBgCopyTilemapToVram(2);
+ BerryPouchSetArrowCursorFromListMenu(data[0], 1);
+ Task_CleanUpAndReturnToMain(taskId);
+}
+
+static void Task_Toss_SelectMultiple(u8 taskId)
+{
+ s16 * data = gTasks[taskId].data;
+ if (AdjustQuantityAccordingToDPadInput(&data[8], data[2]) == TRUE)
+ PrintxQuantityOnWindow(0, data[8], 3);
+ else if (JOY_NEW(A_BUTTON))
+ {
+ PlaySE(SE_SELECT);
+ ClearWindowTilemap(GetVariableWindowId(8));
+ DestroyVariableWindow(8);
+ DestroyVariableWindow(0);
+ ScheduleBgCopyTilemapToVram(0);
+ ScheduleBgCopyTilemapToVram(2);
+ DestroyScrollIndicatorArrows();
+ Task_AskTossMultiple(taskId);
+ }
+ else if (JOY_NEW(B_BUTTON))
+ {
+ PlaySE(SE_SELECT);
+ DestroyVariableWindow(8);
+ DestroyVariableWindow(0);
+ PutWindowTilemap(0);
+ PutWindowTilemap(1);
+ ScheduleBgCopyTilemapToVram(0);
+ ScheduleBgCopyTilemapToVram(2);
+ BerryPouchSetArrowCursorFromListMenu(data[0], 1);
+ DestroyScrollIndicatorArrows();
+ Task_CleanUpAndReturnToMain(taskId);
+ }
+}
+
+static void Task_TossYes(u8 taskId)
+{
+ s16 * data = gTasks[taskId].data;
+ DestroyVariableWindow(7);
+ CopySelectedListMenuItemName(data[1], gStringVar1);
+ ConvertIntToDecimalStringN(gStringVar2, data[8], STR_CONV_MODE_LEFT_ALIGN, 3);
+ StringExpandPlaceholders(gStringVar4, gText_ThrewAwayStrVar2StrVar1s);
+ BerryPouchPrint(GetOrCreateVariableWindow(9), 2, gStringVar4, 0, 2, 1, 2, 0, 1);
+ gTasks[taskId].func = Task_WaitButtonThenTossBerries;
+}
+
+static void Task_WaitButtonThenTossBerries(u8 taskId)
+{
+ s16 * data = gTasks[taskId].data;
+ if (JOY_NEW(A_BUTTON) || JOY_NEW(B_BUTTON))
+ {
+ PlaySE(SE_SELECT);
+ RemoveBagItem(gSpecialVar_ItemId, data[8]);
+ DestroyVariableWindow(9);
+ DestroyListMenuTask(data[0], &sStaticCnt.listMenuScrollOffset, &sStaticCnt.listMenuSelectedRow);
+ SortAndCountBerries();
+ SanitizeListMenuSelectionParams();
+ SetUpListMenuTemplate();
+ data[0] = ListMenuInit(&gMultiuseListMenuTemplate, sStaticCnt.listMenuScrollOffset, sStaticCnt.listMenuSelectedRow);
+ PutWindowTilemap(1);
+ ScheduleBgCopyTilemapToVram(0);
+ BerryPouchSetArrowCursorFromListMenu(data[0], 1);
+ Task_CleanUpAndReturnToMain(taskId);
+ }
+}
+
+static void Task_BerryPouch_Give(u8 taskId)
+{
+ DestroyVariableWindow(sContextMenuNumOptions + 9);
+ DestroyVariableWindow(6);
+ PutWindowTilemap(0);
+ PutWindowTilemap(1);
+ ScheduleBgCopyTilemapToVram(0);
+ ScheduleBgCopyTilemapToVram(2);
+ if (CalculatePlayerPartyCount() == 0)
+ Task_Give_PrintThereIsNoPokemon(taskId);
+ else
+ {
+ sResources->exitCallback = sub_8126EDC;
+ gTasks[taskId].func = BerryPouch_StartFadeToExitCallback;
+ }
+}
+
+static void Task_Give_PrintThereIsNoPokemon(u8 taskId)
+{
+ DisplayItemMessageInBerryPouch(taskId, 2, gText_ThereIsNoPokemon, Task_WaitButtonBeforeDialogueWindowDestruction);
+}
+
+static void Task_WaitButtonBeforeDialogueWindowDestruction(u8 taskId)
+{
+ if (JOY_NEW(A_BUTTON))
+ {
+ PlaySE(SE_SELECT);
+ Task_BerryPouch_DestroyDialogueWindowAndRefreshListMenu(taskId);
+ }
+}
+
+void Task_BerryPouch_DestroyDialogueWindowAndRefreshListMenu(u8 taskId)
+{
+ s16 * data = gTasks[taskId].data;
+ TryDestroyVariableWindow(5);
+ DestroyListMenuTask(data[0], &sStaticCnt.listMenuScrollOffset, &sStaticCnt.listMenuSelectedRow);
+ SortAndCountBerries();
+ SanitizeListMenuSelectionParams();
+ SetUpListMenuTemplate();
+ data[0] = ListMenuInit(&gMultiuseListMenuTemplate, sStaticCnt.listMenuScrollOffset, sStaticCnt.listMenuSelectedRow);
+ ScheduleBgCopyTilemapToVram(0);
+ BerryPouchSetArrowCursorFromListMenu(data[0], 1);
+ Task_CleanUpAndReturnToMain(taskId);
+}
+
+static void Task_BerryPouch_Exit(u8 taskId)
+{
+ DestroyVariableWindow(sContextMenuNumOptions + 9);
+ DestroyVariableWindow(6);
+ PutWindowTilemap(0);
+ PutWindowTilemap(1);
+ ScheduleBgCopyTilemapToVram(0);
+ ScheduleBgCopyTilemapToVram(2);
+ BerryPouchSetArrowCursorFromListMenu(gTasks[taskId].data[0], 1);
+ Task_CleanUpAndReturnToMain(taskId);
+}
+
+static void Task_ContextMenu_FromPartyGiveMenu(u8 taskId)
+{
+ s16 * data = gTasks[taskId].data;
+ u16 itemId = BagGetItemIdByPocketPosition(POCKET_BERRY_POUCH, data[1]);
+ if (!itemid_link_can_give_berry(itemId))
+ {
+ CopyItemName(itemId, gStringVar1);
+ StringExpandPlaceholders(gStringVar4, gText_TheStrVar1CantBeHeldHere);
+ DisplayItemMessageInBerryPouch(taskId, 2, gStringVar4, Task_WaitButtonBeforeDialogueWindowDestruction);
+ }
+ else
+ {
+ sResources->exitCallback = c2_8123744;
+ gTasks[taskId].func = BerryPouch_StartFadeToExitCallback;
+ }
+}
+
+static void Task_ContextMenu_FromPokemonPC(u8 taskId)
+{
+ sResources->exitCallback = sub_808CE60;
+ gTasks[taskId].func = BerryPouch_StartFadeToExitCallback;
+}
+
+static void Task_ContextMenu_Sell(u8 taskId)
+{
+ s16 * data = gTasks[taskId].data;
+ if (itemid_get_market_price(gSpecialVar_ItemId) == 0)
+ {
+ CopyItemName(gSpecialVar_ItemId, gStringVar1);
+ StringExpandPlaceholders(gStringVar4, gText_OhNoICantBuyThat);
+ DisplayItemMessageInBerryPouch(taskId, sub_80BF8E4(), gStringVar4, Task_BerryPouch_DestroyDialogueWindowAndRefreshListMenu);
+ }
+ else
+ {
+ data[8] = 1;
+ if (data[2] == 1)
+ {
+ PrintMoneyInWin2();
+ Task_AskSellMultiple(taskId);
+ }
+ else
+ {
+ if (data[2] > 99)
+ data[2] = 99;
+ CopyItemName(gSpecialVar_ItemId, gStringVar1);
+ StringExpandPlaceholders(gStringVar4, gText_HowManyWouldYouLikeToSell);
+ DisplayItemMessageInBerryPouch(taskId, sub_80BF8E4(), gStringVar4, Task_Sell_PrintSelectMultipleUI);
+ }
+ }
+}
+
+static void Task_AskSellMultiple(u8 taskId)
+{
+ s16 * data = gTasks[taskId].data;
+ ConvertIntToDecimalStringN(gStringVar3, itemid_get_market_price(BagGetItemIdByPocketPosition(POCKET_BERRY_POUCH, data[1])) / 2 * data[8], STR_CONV_MODE_LEFT_ALIGN, 6);
+ StringExpandPlaceholders(gStringVar4, gText_ICanPayThisMuch_WouldThatBeOkay);
+ DisplayItemMessageInBerryPouch(taskId, sub_80BF8E4(), gStringVar4, Task_SellMultiple_CreateYesNoMenu);
+}
+
+static void Task_SellMultiple_CreateYesNoMenu(u8 taskId)
+{
+ CreateYesNoMenuWin4(taskId, &sYesNoFuncs_Sell);
+}
+
+static void Task_SellNo(u8 taskId)
+{
+ s16 * data = gTasks[taskId].data;
+ DestroyVariableWindow(2);
+ TryDestroyVariableWindow(5);
+ PutWindowTilemap(2);
+ PutWindowTilemap(0);
+ PutWindowTilemap(1);
+ ScheduleBgCopyTilemapToVram(0);
+ BerryPouchSetArrowCursorFromListMenu(data[0], 1);
+ Task_CleanUpAndReturnToMain(taskId);
+}
+
+static void Task_Sell_PrintSelectMultipleUI(u8 taskId)
+{
+ s16 * data = gTasks[taskId].data;
+ u8 windowId = GetOrCreateVariableWindow(1);
+ ConvertIntToDecimalStringN(gStringVar1, 1, STR_CONV_MODE_LEADING_ZEROS, 2);
+ StringExpandPlaceholders(gStringVar4, gText_TimesStrVar1);
+ BerryPouchPrint(windowId, 0, gStringVar4, 4, 10, 1, 0, 0xFF, 1);
+ SellMultiple_UpdateSellPriceDisplay(itemid_get_market_price(BagGetItemIdByPocketPosition(POCKET_BERRY_POUCH, data[1])) / 2 * data[8]);
+ PrintMoneyInWin2();
+ CreateScrollIndicatorArrows_SellQuantity();
+ gTasks[taskId].func = Task_Sell_SelectMultiple;
+}
+
+static void SellMultiple_UpdateSellPriceDisplay(s32 price)
+{
+ PrintMoneyAmount(GetVariableWindowId(1), 56, 10, price, 0);
+}
+
+static void Task_Sell_SelectMultiple(u8 taskId)
+{
+ s16 * data = gTasks[taskId].data;
+ if (AdjustQuantityAccordingToDPadInput(&data[8], data[2]) == TRUE)
+ {
+ PrintxQuantityOnWindow(1, data[8], 2);
+ SellMultiple_UpdateSellPriceDisplay(itemid_get_market_price(BagGetItemIdByPocketPosition(POCKET_BERRY_POUCH, data[1])) / 2 * data[8]);
+ }
+ else if (JOY_NEW(A_BUTTON))
+ {
+ PlaySE(SE_SELECT);
+ DestroyVariableWindow(1);
+ PutWindowTilemap(0);
+ ScheduleBgCopyTilemapToVram(0);
+ DestroyScrollIndicatorArrows();
+ Task_AskSellMultiple(taskId);
+ }
+ else if (JOY_NEW(B_BUTTON))
+ {
+ PlaySE(SE_SELECT);
+ DestroyVariableWindow(1);
+ DestroyVariableWindow(2);
+ TryDestroyVariableWindow(5);
+ PutWindowTilemap(2);
+ PutWindowTilemap(0);
+ PutWindowTilemap(1);
+ ScheduleBgCopyTilemapToVram(0);
+ DestroyScrollIndicatorArrows();
+ BerryPouchSetArrowCursorFromListMenu(data[0], 1);
+ Task_CleanUpAndReturnToMain(taskId);
+ }
+}
+
+static void Task_SellYes(u8 taskId)
+{
+ s16 * data = gTasks[taskId].data;
+ PutWindowTilemap(0);
+ ScheduleBgCopyTilemapToVram(0);
+ CopyItemName(gSpecialVar_ItemId, gStringVar1);
+ ConvertIntToDecimalStringN(gStringVar3, itemid_get_market_price(BagGetItemIdByPocketPosition(POCKET_BERRY_POUCH, data[1])) / 2 * data[8], STR_CONV_MODE_LEFT_ALIGN, 6);
+ StringExpandPlaceholders(gStringVar4, gText_TurnedOverItemsWorthYen);
+ DisplayItemMessageInBerryPouch(taskId, 2, gStringVar4, Task_SellBerries_PlaySfxAndRemoveBerries);
+}
+
+static void Task_SellBerries_PlaySfxAndRemoveBerries(u8 taskId)
+{
+ s16 * data = gTasks[taskId].data;
+ PlaySE(SE_CASHIER);
+ RemoveBagItem(gSpecialVar_ItemId, data[8]);
+ AddMoney(&gSaveBlock1Ptr->money, itemid_get_market_price(gSpecialVar_ItemId) / 2 * data[8]);
+ sub_809C09C(gSpecialVar_ItemId, data[8], 2);
+ DestroyListMenuTask(data[0], &sStaticCnt.listMenuScrollOffset, &sStaticCnt.listMenuSelectedRow);
+ SortAndCountBerries();
+ SanitizeListMenuSelectionParams();
+ SetUpListMenuTemplate();
+ data[0] = ListMenuInit(&gMultiuseListMenuTemplate, sStaticCnt.listMenuScrollOffset, sStaticCnt.listMenuSelectedRow);
+ BerryPouchSetArrowCursorFromListMenu(data[0], 2);
+ PrintMoneyAmountInMoneyBox(GetVariableWindowId(2), GetMoney(&gSaveBlock1Ptr->money), 0);
+ gTasks[taskId].func = Task_SellBerries_WaitButton;
+}
+
+static void Task_SellBerries_WaitButton(u8 taskId)
+{
+ if (JOY_NEW(A_BUTTON) || JOY_NEW(B_BUTTON))
+ {
+ PlaySE(SE_SELECT);
+ DestroyVariableWindow(2);
+ PutWindowTilemap(2);
+ Task_BerryPouch_DestroyDialogueWindowAndRefreshListMenu(taskId);
+ }
+}
+
+static void BerryPouchInitWindows(void)
+{
+ u8 i;
+ InitWindows(sWindowTemplates_Main);
+ DeactivateAllTextPrinters();
+ TextWindow_SetUserSelectedFrame(0, 0x001, 0xE0);
+ TextWindow_LoadResourcesStdFrame0(0, 0x013, 0xD0);
+ TextWindow_SetStdFrame0_WithPal(0, 0x00A, 0xC0);
+ LoadPalette(gTMCaseMainWindowPalette, 0xF0, 0x20);
+ for (i = 0; i < 3; i++)
+ FillWindowPixelBuffer(i, PIXEL_FILL(0));
+ PutWindowTilemap(0);
+ PutWindowTilemap(1);
+ PutWindowTilemap(2);
+ ScheduleBgCopyTilemapToVram(0);
+ ScheduleBgCopyTilemapToVram(2);
+ for (i = 0; i < 14; i++)
+ sVariableWindowIds[i] = 0xFF;
+}
+
+static void BerryPouchPrint(u8 windowId, u8 fontId, const u8 * str, u8 x, u8 y, u8 letterSpacing, u8 lineSpacing, u8 speed, u8 colorIdx)
+{
+ AddTextPrinterParameterized4(windowId, fontId, x, y, letterSpacing, lineSpacing, sTextColors[colorIdx], speed, str);
+}
+
+static u8 GetOrCreateVariableWindow(u8 winIdx)
+{
+ u8 retval = sVariableWindowIds[winIdx];
+ if (retval == 0xFF)
+ {
+ sVariableWindowIds[winIdx] = AddWindow(&sWindowTemplates_Variable[winIdx]);
+ if (winIdx == 2 || winIdx == 6 || winIdx == 7 || winIdx == 8 || winIdx == 9)
+ DrawStdFrameWithCustomTileAndPalette(sVariableWindowIds[winIdx], FALSE, 0x00A, 0xC);
+ else
+ DrawStdFrameWithCustomTileAndPalette(sVariableWindowIds[winIdx], FALSE, 0x001, 0xE);
+ ScheduleBgCopyTilemapToVram(2);
+ retval = sVariableWindowIds[winIdx];
+ }
+ return retval;
+}
+
+static void VariableWindowSetAltFrameTileAndPalette(u8 winIdx)
+{
+ DrawStdFrameWithCustomTileAndPalette(sVariableWindowIds[winIdx], FALSE, 0x001, 0xE);
+}
+
+static void DestroyVariableWindow(u8 winIdx)
+{
+ ClearStdWindowAndFrameToTransparent(sVariableWindowIds[winIdx], FALSE);
+ ClearWindowTilemap(sVariableWindowIds[winIdx]);
+ RemoveWindow(sVariableWindowIds[winIdx]);
+ ScheduleBgCopyTilemapToVram(2);
+ sVariableWindowIds[winIdx] = 0xFF;
+}
+
+static void TryDestroyVariableWindow(u8 winIdx)
+{
+ if (sVariableWindowIds[winIdx] != 0xFF)
+ {
+ ClearDialogWindowAndFrameToTransparent(sVariableWindowIds[winIdx], FALSE);
+ ClearWindowTilemap(sVariableWindowIds[winIdx]);
+ RemoveWindow(sVariableWindowIds[winIdx]);
+ PutWindowTilemap(1);
+ ScheduleBgCopyTilemapToVram(0);
+ ScheduleBgCopyTilemapToVram(2);
+ sVariableWindowIds[winIdx] = 0xFF;
+ }
+}
+
+static u8 GetVariableWindowId(u8 winIdx)
+{
+ return sVariableWindowIds[winIdx];
+}
+
+void DisplayItemMessageInBerryPouch(u8 taskId, u8 fontId, const u8 * str, TaskFunc followUpFunc)
+{
+ if (sVariableWindowIds[5] == 0xFF)
+ sVariableWindowIds[5] = AddWindow(&sWindowTemplates_Variable[5]);
+ DisplayMessageAndContinueTask(taskId, sVariableWindowIds[5], 0x013, 0xD, fontId, GetTextSpeedSetting(), str, followUpFunc);
+ ScheduleBgCopyTilemapToVram(2);
+}
+
+static void CreateYesNoMenuWin3(u8 taskId, const struct YesNoFuncTable *ptrs)
+{
+ CreateYesNoMenuWithCallbacks(taskId, &sWindowTemplates_Variable[3], 2, 0, 2, 0x001, 0xE, ptrs);
+}
+
+static void CreateYesNoMenuWin4(u8 taskId, const struct YesNoFuncTable *ptrs)
+{
+ CreateYesNoMenuWithCallbacks(taskId, &sWindowTemplates_Variable[4], 2, 0, 2, 0x001, 0xE, ptrs);
+}
+
+static void PrintMoneyInWin2(void)
+{
+ PrintMoneyAmountInMoneyBoxWithBorder(GetOrCreateVariableWindow(2), 0x00A, 0xC, GetMoney(&gSaveBlock1Ptr->money));
+}
+
+static void CreateBerryPouchSprite(void)
+{
+ sBerryPouchSpriteId = CreateSprite(&sSpriteTemplate_BerryPouch, 40, 76, 0);
+}
+
+static void StartBerryPouchSpriteWobbleAnim(void)
+{
+ struct Sprite *sprite = &gSprites[sBerryPouchSpriteId];
+ if (sprite->affineAnimEnded)
+ {
+ StartSpriteAffineAnim(sprite, 1);
+ sprite->callback = SpriteCB_BerryPouchWaitWobbleAnim;
+ }
+}
+
+static void SpriteCB_BerryPouchWaitWobbleAnim(struct Sprite *sprite)
+{
+ if (sprite->affineAnimEnded)
+ {
+ StartSpriteAffineAnim(sprite, 0);
+ sprite->callback = SpriteCallbackDummy;
+ }
+}
diff --git a/src/berry_powder.c b/src/berry_powder.c
index 9b3bbc17f..992e82522 100644
--- a/src/berry_powder.c
+++ b/src/berry_powder.c
@@ -11,12 +11,12 @@
EWRAM_DATA u8 gUnknown_203F464 = 0;
-u32 sub_815EE3C(u32 * a0)
+u32 DecryptBerryPowder(u32 * a0)
{
return *a0 ^ gSaveBlock2Ptr->encryptionKey;
}
-void sub_815EE54(u32 * a0, u32 a1)
+void SetBerryPowder(u32 * a0, u32 a1)
{
*a0 = gSaveBlock2Ptr->encryptionKey ^ a1;
}
@@ -28,7 +28,7 @@ void sub_815EE6C(u32 a0)
bool8 sub_815EE88(u32 a0)
{
- if (sub_815EE3C(&gSaveBlock2Ptr->berryCrush.berryPowderAmount) < a0)
+ if (DecryptBerryPowder(&gSaveBlock2Ptr->berryCrush.berryPowderAmount) < a0)
return FALSE;
else
return TRUE;
@@ -36,7 +36,7 @@ bool8 sub_815EE88(u32 a0)
bool8 sub_815EEB0(void)
{
- if (sub_815EE3C(&gSaveBlock2Ptr->berryCrush.berryPowderAmount) < gSpecialVar_0x8004)
+ if (DecryptBerryPowder(&gSaveBlock2Ptr->berryCrush.berryPowderAmount) < gSpecialVar_0x8004)
return FALSE;
else
return TRUE;
@@ -45,15 +45,15 @@ bool8 sub_815EEB0(void)
bool8 sub_815EEE0(u32 a0)
{
u32 * ptr = &gSaveBlock2Ptr->berryCrush.berryPowderAmount;
- u32 amount = sub_815EE3C(ptr) + a0;
+ u32 amount = DecryptBerryPowder(ptr) + a0;
if (amount > 99999)
{
- sub_815EE54(ptr, 99999);
+ SetBerryPowder(ptr, 99999);
return FALSE;
}
else
{
- sub_815EE54(ptr, amount);
+ SetBerryPowder(ptr, amount);
return TRUE;
}
}
@@ -65,8 +65,8 @@ bool8 sub_815EF20(u32 a0)
return FALSE;
else
{
- u32 amount = sub_815EE3C(ptr);
- sub_815EE54(ptr, amount - a0);
+ u32 amount = DecryptBerryPowder(ptr);
+ SetBerryPowder(ptr, amount - a0);
return TRUE;
}
}
@@ -78,15 +78,15 @@ bool8 sub_815EF5C(void)
return FALSE;
else
{
- u32 amount = sub_815EE3C(ptr);
- sub_815EE54(ptr, amount - gSpecialVar_0x8004);
+ u32 amount = DecryptBerryPowder(ptr);
+ SetBerryPowder(ptr, amount - gSpecialVar_0x8004);
return TRUE;
}
}
u32 GetBerryPowder(void)
{
- return sub_815EE3C(&gSaveBlock2Ptr->berryCrush.berryPowderAmount);
+ return DecryptBerryPowder(&gSaveBlock2Ptr->berryCrush.berryPowderAmount);
}
void sub_815EFBC(u8 windowId, u32 powder, u8 x, u8 y, u8 speed)
diff --git a/src/braille_text.c b/src/braille_text.c
index 047fcab08..c95cf64b2 100644
--- a/src/braille_text.c
+++ b/src/braille_text.c
@@ -17,20 +17,20 @@ u16 Font6Func(struct TextPrinter *textPrinter)
u16 char_;
struct TextPrinterSubStruct *sub;
- sub = &textPrinter->sub_union.sub;
+ sub = &textPrinter->subUnion.sub;
switch (textPrinter->state)
{
case 0:
- if (gMain.heldKeys & (A_BUTTON | B_BUTTON) && sub->font_type_upper)
+ if (gMain.heldKeys & (A_BUTTON | B_BUTTON) && sub->hasPrintBeenSpedUp)
{
textPrinter->delayCounter = 0;
}
- if (textPrinter->delayCounter && textPrinter->text_speed)
+ if (textPrinter->delayCounter && textPrinter->textSpeed)
{
textPrinter->delayCounter --;
if (gTextFlags.canABSpeedUpPrint && gMain.newKeys & (A_BUTTON | B_BUTTON))
{
- sub->font_type_upper = TRUE;
+ sub->hasPrintBeenSpedUp = TRUE;
textPrinter->delayCounter = 0;
}
return 3;
@@ -41,62 +41,62 @@ u16 Font6Func(struct TextPrinter *textPrinter)
}
else
{
- textPrinter->delayCounter = textPrinter->text_speed;
+ textPrinter->delayCounter = textPrinter->textSpeed;
}
- char_ = *textPrinter->subPrinter.currentChar++;
+ char_ = *textPrinter->printerTemplate.currentChar++;
switch (char_)
{
case EOS:
return 1;
case CHAR_NEWLINE:
- textPrinter->subPrinter.currentX = textPrinter->subPrinter.x;
- textPrinter->subPrinter.currentY += gFonts[textPrinter->subPrinter.fontId].maxLetterHeight + textPrinter->subPrinter.lineSpacing;
+ textPrinter->printerTemplate.currentX = textPrinter->printerTemplate.x;
+ textPrinter->printerTemplate.currentY += gFonts[textPrinter->printerTemplate.fontId].maxLetterHeight + textPrinter->printerTemplate.lineSpacing;
return 2;
case PLACEHOLDER_BEGIN:
- textPrinter->subPrinter.currentChar++;
+ textPrinter->printerTemplate.currentChar++;
return 2;
case EXT_CTRL_CODE_BEGIN:
- char_ = *textPrinter->subPrinter.currentChar++;
+ char_ = *textPrinter->printerTemplate.currentChar++;
switch (char_)
{
case 1:
- textPrinter->subPrinter.fgColor = *textPrinter->subPrinter.currentChar++;
- GenerateFontHalfRowLookupTable(textPrinter->subPrinter.fgColor, textPrinter->subPrinter.bgColor, textPrinter->subPrinter.shadowColor);
+ textPrinter->printerTemplate.fgColor = *textPrinter->printerTemplate.currentChar++;
+ GenerateFontHalfRowLookupTable(textPrinter->printerTemplate.fgColor, textPrinter->printerTemplate.bgColor, textPrinter->printerTemplate.shadowColor);
return 2;
case 2:
- textPrinter->subPrinter.bgColor = *textPrinter->subPrinter.currentChar++;
- GenerateFontHalfRowLookupTable(textPrinter->subPrinter.fgColor, textPrinter->subPrinter.bgColor, textPrinter->subPrinter.shadowColor);
+ textPrinter->printerTemplate.bgColor = *textPrinter->printerTemplate.currentChar++;
+ GenerateFontHalfRowLookupTable(textPrinter->printerTemplate.fgColor, textPrinter->printerTemplate.bgColor, textPrinter->printerTemplate.shadowColor);
return 2;
case 3:
- textPrinter->subPrinter.shadowColor = *textPrinter->subPrinter.currentChar++;
- GenerateFontHalfRowLookupTable(textPrinter->subPrinter.fgColor, textPrinter->subPrinter.bgColor, textPrinter->subPrinter.shadowColor);
+ textPrinter->printerTemplate.shadowColor = *textPrinter->printerTemplate.currentChar++;
+ GenerateFontHalfRowLookupTable(textPrinter->printerTemplate.fgColor, textPrinter->printerTemplate.bgColor, textPrinter->printerTemplate.shadowColor);
return 2;
case 4:
- textPrinter->subPrinter.fgColor = *textPrinter->subPrinter.currentChar;
- textPrinter->subPrinter.bgColor = *++textPrinter->subPrinter.currentChar;
- textPrinter->subPrinter.shadowColor = *++textPrinter->subPrinter.currentChar;
- textPrinter->subPrinter.currentChar++;
+ textPrinter->printerTemplate.fgColor = *textPrinter->printerTemplate.currentChar;
+ textPrinter->printerTemplate.bgColor = *++textPrinter->printerTemplate.currentChar;
+ textPrinter->printerTemplate.shadowColor = *++textPrinter->printerTemplate.currentChar;
+ textPrinter->printerTemplate.currentChar++;
- GenerateFontHalfRowLookupTable(textPrinter->subPrinter.fgColor, textPrinter->subPrinter.bgColor, textPrinter->subPrinter.shadowColor);
+ GenerateFontHalfRowLookupTable(textPrinter->printerTemplate.fgColor, textPrinter->printerTemplate.bgColor, textPrinter->printerTemplate.shadowColor);
return 2;
case 5:
- textPrinter->subPrinter.currentChar++;
+ textPrinter->printerTemplate.currentChar++;
return 2;
case 6:
- sub->font_type = *textPrinter->subPrinter.currentChar;
- textPrinter->subPrinter.currentChar++;
+ sub->glyphId = *textPrinter->printerTemplate.currentChar;
+ textPrinter->printerTemplate.currentChar++;
return 2;
case 7:
return 2;
case 8:
- textPrinter->delayCounter = *textPrinter->subPrinter.currentChar++;
+ textPrinter->delayCounter = *textPrinter->printerTemplate.currentChar++;
textPrinter->state = 6;
return 2;
case 9:
textPrinter->state = 1;
if (gTextFlags.autoScroll)
{
- sub->frames_visible_counter = 0;
+ sub->autoScrollDelay = 0;
}
return 3;
case 10:
@@ -104,19 +104,19 @@ u16 Font6Func(struct TextPrinter *textPrinter)
return 3;
case 11:
case 16:
- textPrinter->subPrinter.currentChar += 2;
+ textPrinter->printerTemplate.currentChar += 2;
return 2;
case 12:
- char_ = *++textPrinter->subPrinter.currentChar;
+ char_ = *++textPrinter->printerTemplate.currentChar;
break;
case 13:
- textPrinter->subPrinter.currentX = textPrinter->subPrinter.x + *textPrinter->subPrinter.currentChar++;
+ textPrinter->printerTemplate.currentX = textPrinter->printerTemplate.x + *textPrinter->printerTemplate.currentChar++;
return 2;
case 14:
- textPrinter->subPrinter.currentY = textPrinter->subPrinter.y + *textPrinter->subPrinter.currentChar++;
+ textPrinter->printerTemplate.currentY = textPrinter->printerTemplate.y + *textPrinter->printerTemplate.currentChar++;
return 2;
case 15:
- FillWindowPixelBuffer(textPrinter->subPrinter.windowId, PIXEL_FILL(textPrinter->subPrinter.bgColor));
+ FillWindowPixelBuffer(textPrinter->printerTemplate.windowId, PIXEL_FILL(textPrinter->printerTemplate.bgColor));
return 2;
}
break;
@@ -129,15 +129,15 @@ u16 Font6Func(struct TextPrinter *textPrinter)
TextPrinterInitDownArrowCounters(textPrinter);
return 3;
case 0xF9:
- char_ = *textPrinter->subPrinter.currentChar++| 0x100;
+ char_ = *textPrinter->printerTemplate.currentChar++| 0x100;
break;
case 0xF8:
- textPrinter->subPrinter.currentChar++;
+ textPrinter->printerTemplate.currentChar++;
return 0;
}
DecompressGlyphFont6(char_);
CopyGlyphToWindow(textPrinter);
- textPrinter->subPrinter.currentX += gGlyphInfo[0x80] + textPrinter->subPrinter.letterSpacing;
+ textPrinter->printerTemplate.currentX += gGlyphInfo[0x80] + textPrinter->printerTemplate.letterSpacing;
return 0;
case 1:
if (TextPrinterWait(textPrinter))
@@ -148,9 +148,9 @@ u16 Font6Func(struct TextPrinter *textPrinter)
case 2:
if (TextPrinterWaitWithDownArrow(textPrinter))
{
- FillWindowPixelBuffer(textPrinter->subPrinter.windowId, PIXEL_FILL(textPrinter->subPrinter.bgColor));
- textPrinter->subPrinter.currentX = textPrinter->subPrinter.x;
- textPrinter->subPrinter.currentY = textPrinter->subPrinter.y;
+ FillWindowPixelBuffer(textPrinter->printerTemplate.windowId, PIXEL_FILL(textPrinter->printerTemplate.bgColor));
+ textPrinter->printerTemplate.currentX = textPrinter->printerTemplate.x;
+ textPrinter->printerTemplate.currentY = textPrinter->printerTemplate.y;
textPrinter->state = 0;
}
return 3;
@@ -158,8 +158,8 @@ u16 Font6Func(struct TextPrinter *textPrinter)
if (TextPrinterWaitWithDownArrow(textPrinter))
{
TextPrinterClearDownArrow(textPrinter);
- textPrinter->scrollDistance = gFonts[textPrinter->subPrinter.fontId].maxLetterHeight + textPrinter->subPrinter.lineSpacing;
- textPrinter->subPrinter.currentX = textPrinter->subPrinter.x;
+ textPrinter->scrollDistance = gFonts[textPrinter->printerTemplate.fontId].maxLetterHeight + textPrinter->printerTemplate.lineSpacing;
+ textPrinter->printerTemplate.currentX = textPrinter->printerTemplate.x;
textPrinter->state = 4;
}
return 3;
@@ -168,15 +168,15 @@ u16 Font6Func(struct TextPrinter *textPrinter)
{
if (textPrinter->scrollDistance < gUnknown_846FB08[gSaveBlock2Ptr->optionsTextSpeed])
{
- ScrollWindow(textPrinter->subPrinter.windowId, 0, textPrinter->scrollDistance, PIXEL_FILL(textPrinter->subPrinter.bgColor));
+ ScrollWindow(textPrinter->printerTemplate.windowId, 0, textPrinter->scrollDistance, PIXEL_FILL(textPrinter->printerTemplate.bgColor));
textPrinter->scrollDistance = 0;
}
else
{
- ScrollWindow(textPrinter->subPrinter.windowId, 0, gUnknown_846FB08[gSaveBlock2Ptr->optionsTextSpeed], PIXEL_FILL(textPrinter->subPrinter.bgColor));
+ ScrollWindow(textPrinter->printerTemplate.windowId, 0, gUnknown_846FB08[gSaveBlock2Ptr->optionsTextSpeed], PIXEL_FILL(textPrinter->printerTemplate.bgColor));
textPrinter->scrollDistance -= gUnknown_846FB08[gSaveBlock2Ptr->optionsTextSpeed];
}
- CopyWindowToVram(textPrinter->subPrinter.windowId, 2);
+ CopyWindowToVram(textPrinter->printerTemplate.windowId, 2);
}
else
{
@@ -216,7 +216,7 @@ static void DecompressGlyphFont6(u16 glyph)
gGlyphInfo[0x81] = 0x10;
}
-u32 GetGlyphWidthFont6(u16 font_type, bool32 isJapanese)
+s32 GetGlyphWidthFont6(u16 font_type, bool32 isJapanese)
{
return 0x10;
}
diff --git a/src/buy_menu_helpers.c b/src/buy_menu_helpers.c
index 4cdf90e4e..cf8072977 100644
--- a/src/buy_menu_helpers.c
+++ b/src/buy_menu_helpers.c
@@ -148,23 +148,11 @@ static const struct WindowTemplate sShopBuyMenuYesNoWindowTemplate =
.baseBlock = 0xC1,
};
-static const struct TextColor sShopBuyMenuTextColors[] =
+static const u8 sShopBuyMenuTextColors[][3] =
{
- {
- .fgColor = 0,
- .bgColor = 1,
- .shadowColor = 2,
- },
- {
- .fgColor = 0,
- .bgColor = 2,
- .shadowColor = 3,
- },
- {
- .fgColor = 0,
- .bgColor = 3,
- .shadowColor = 2,
- },
+ {0, 1, 2},
+ {0, 2, 3},
+ {0, 3, 2}
};
void BuyMenuInitWindows(bool32 isSellingTM)
@@ -191,7 +179,7 @@ void BuyMenuDrawMoneyBox(void)
void BuyMenuPrint(u8 windowId, u8 font, const u8 *text, u8 x, u8 y, u8 letterSpacing, u8 lineSpacing, s8 speed, u8 color)
{
- AddTextPrinterParameterized4(windowId, font, x, y, letterSpacing, lineSpacing, &sShopBuyMenuTextColors[color], speed, text);
+ AddTextPrinterParameterized4(windowId, font, x, y, letterSpacing, lineSpacing, sShopBuyMenuTextColors[color], speed, text);
}
void BuyMenuDisplayMessage(u8 taskId, const u8 *text, TaskFunc callback)
diff --git a/src/credits.c b/src/credits.c
new file mode 100644
index 000000000..f47cc79c4
--- /dev/null
+++ b/src/credits.c
@@ -0,0 +1,2168 @@
+#include "global.h"
+#include "malloc.h"
+#include "bg.h"
+#include "palette.h"
+#include "gpu_regs.h"
+#include "task.h"
+#include "overworld.h"
+#include "event_data.h"
+#include "window.h"
+#include "new_menu_helpers.h"
+#include "decompress.h"
+#include "graphics.h"
+#include "strings.h"
+#include "menu.h"
+#include "field_weather.h"
+#include "trainer_pokemon_sprites.h"
+#include "sound.h"
+#include "constants/species.h"
+#include "constants/maps.h"
+
+#if defined(FIRERED)
+#define TITLE_TEXT gString_PokemonFireRed_Staff
+asm(".set TITLE_TEXT, gString_PokemonFireRed_Staff");
+#elif defined(LEAFGREEN)
+#define TITLE_TEXT gString_PokemonLeafGreen_Staff
+asm(".set TITLE_TEXT, gString_PokemonLeafGreen_Staff");
+#endif
+
+enum CreditsSceneIdx
+{
+ CREDITSSCENE_INIT_WIN0 = 0,
+ CREDITSSCENE_SETUP_DARKEN_EFFECT,
+ CREDITSSCENE_OPEN_WIN0,
+ CREDITSSCENE_LOAD_PLAYER_SPRITE_AT_INDIGO,
+ CREDITSSCENE_PRINT_TITLE_STAFF,
+ CREDITSSCENE_WAIT_TITLE_STAFF,
+ CREDITSSCENE_EXEC_CMD,
+ CREDITSSCENE_PRINT_ADDPRINTER1,
+ CREDITSSCENE_PRINT_ADDPRINTER2,
+ CREDITSSCENE_PRINT_DELAY,
+ CREDITSSCENE_MAPNEXT_DESTROYWINDOW,
+ CREDITSSCENE_MAPNEXT_LOADMAP,
+ CREDITSSCENE_MAP_LOADMAP_CREATESPRITES,
+ CREDITSSCENE_MON_DESTROY_ASSETS,
+ CREDITSSCENE_MON_SHOW,
+ CREDITSSCENE_THEEND_DESTROY_ASSETS,
+ CREDITSSCENE_THEEND_SHOW,
+ CREDITSSCENE_WAITBUTTON,
+ CREDITSSCENE_TERMINATE,
+};
+
+enum CreditsScrCmd
+{
+ CREDITSSCRCMD_PRINT = 0,
+ CREDITSSCRCMD_MAPNEXT,
+ CREDITSSCRCMD_MAP,
+ CREDITSSCRCMD_MON,
+ CREDITSSCRCMD_THEENDGFX,
+ CREDITSSCRCMD_WAITBUTTON
+};
+
+enum CreditsMon
+{
+ CREDITSMON_CHARIZARD = 0,
+ CREDITSMON_VENUSAUR,
+ CREDITSMON_BLASTOISE,
+ CREDITSMON_PIKACHU
+};
+
+enum CreditsClosingText
+{
+ CREDITSCLOSING_ALLRIGHTSRESERVED = 0,
+ CREDITSCLOSING_THEEND
+};
+
+enum CreditsString
+{
+ CREDITS_STRING_DIRECTOR = 0,
+ CREDITS_STRING_ART_DIRECTOR_BATTLE_DIRECTOR,
+ CREDITS_STRING_PROGRAM_LEADER_PLANNING_LEADER_GRAPHIC_DESIGN_LEADER,
+ CREDITS_STRING_PROGRAMMERS,
+ CREDITS_STRING_SYSTEM_PROGRAMMERS,
+ CREDITS_STRING_GRAPHIC_DESIGNERS,
+ CREDITS_STRING_GRAPHIC_DESIGNERS_2,
+ CREDITS_STRING_MUSIC_COMPOSITION,
+ CREDITS_STRING_SOUND_EFFECTS,
+ CREDITS_STRING_GAME_DESIGNERS,
+ CREDITS_STRING_GAME_DESIGNERS_2,
+ CREDITS_STRING_GAME_SCENARIO,
+ CREDITS_STRING_SCRIPT_DESIGNER_MAP_DESIGNER,
+ CREDITS_STRING_PARAMETRIC_DESIGNERS,
+ CREDITS_STRING_POKEDEX_TEXT,
+ CREDITS_STRING_POKEMON_DESIGNERS,
+ CREDITS_STRING_POKEMON_DESIGNERS_2,
+ CREDITS_STRING_POKEMON_DESIGNERS_3,
+ CREDITS_STRING_SUPPORTING_PROGRAMMERS,
+ CREDITS_STRING_NCL_PRODUCT_TESTING,
+ CREDITS_STRING_SPECIAL_THANKS,
+ CREDITS_STRING_SPECIAL_THANKS_2,
+ CREDITS_STRING_SPECIAL_THANKS_3,
+ CREDITS_STRING_BRAILLE_CODE_CHECK,
+ CREDITS_STRING_INFORMATION_SUPERVISORS,
+ CREDITS_STRING_COORDINATORS,
+ CREDITS_STRING_TASK_MANAGERS,
+ CREDITS_STRING_PRODUCERS,
+ CREDITS_STRING_EXECUTIVE_DIRECTOR,
+ CREDITS_STRING_EXECUTIVE_PRODUCER,
+ CREDITS_STRING_EXECUTIVE_PRODUCER_2,
+ CREDITS_STRING_ENGLISH_VERSION_COORDINATORS,
+ CREDITS_STRING_TRANSLATOR_TEXT_EDITOR,
+ CREDITS_STRING_PROGRAMMERS_2,
+ CREDITS_STRING_ENVIRONMENT_TOOL_PROGRAMMERS,
+ CREDITS_STRING_NOA_PRODUCT_TESTING,
+ CREDITS_STRING_BRAILLE_CODE_CHECK_2,
+ CREDITS_STRING_BRAILLE_CODE_CHECK_3,
+ CREDITS_STRING_SPECIAL_THANKS_4,
+ CREDITS_STRING_SPECIAL_THANKS_5,
+ CREDITS_STRING_BRAILLE_CODE_CHECK_4,
+ CREDITS_STRING_GRAPHIC_DESIGNER,
+ CREDITS_STRING_DUMMY
+};
+
+enum CreditsMap
+{
+ CREDITS_MAP_ROUTE23 = 0,
+ CREDITS_MAP_VIRIDIAN_CITY,
+ CREDITS_MAP_PEWTER_CITY,
+ CREDITS_MAP_CERULEAN_CITY,
+ CREDITS_MAP_ROUTE25,
+ CREDITS_MAP_VERMILION_CITY,
+ CREDITS_MAP_ROUTE10,
+ CREDITS_MAP_CELADON_CITY,
+ CREDITS_MAP_SAFFRON_CITY_DUPLICATE,
+ CREDITS_MAP_ROUTE17,
+ CREDITS_MAP_FUCHSIA_CITY,
+ CREDITS_MAP_CINNABAR_ISLAND,
+ CREDITS_MAP_ROUTE21_NORTH
+};
+
+struct CreditsResources
+{
+ u8 mainseqno;
+ u8 subseqno;
+ u8 taskId;
+ u16 timer;
+ u16 scrcmdidx;
+ u8 canSpeedThrough;
+ u8 whichMon;
+ u8 windowId;
+ bool8 windowIsActive;
+ u16 creditsMonTimer;
+ u16 unk_0E;
+ u8 filler_10[12];
+ u8 ovwldseqno;
+ u8 unk_1D;
+};
+
+struct CreditsScrcmd
+{
+ u8 cmd;
+ u8 param;
+ u16 duration;
+};
+
+struct CreditsTextHeader
+{
+ const u8 * unk_0;
+ const u8 * unk_4;
+ bool8 unk_8;
+};
+
+struct CompressedGraphicsHeader
+{
+ const u8 * tiles;
+ const u8 * map;
+ const u16 * palette;
+};
+
+struct CreditsTaskData
+{
+ u8 spriteMoveCmd;
+ u8 playerSpriteId;
+ u16 playerTilesTag;
+ u16 field_04;
+ u8 groundSpriteId;
+ u16 groundTilesTag;
+ u16 field_0A;
+};
+
+static EWRAM_DATA struct CreditsResources * sCreditsMgr = NULL;
+
+static void CB2_Credits(void);
+static s32 RollCredits(void);
+static bool32 DoCreditsMonScene(void);
+static bool32 DoCopyrightOrTheEndGfxScene(void);
+static void DestroyPlayerOrRivalSprite(void);
+static void LoadPlayerOrRivalSprite(u8 a0);
+
+static const struct BgTemplate sBgTemplates_MonSceneOrTheEnd[] = {
+ {
+ .bg = 0,
+ .charBaseIndex = 0,
+ .mapBaseIndex = 15,
+ .screenSize = 0,
+ .paletteMode = FALSE,
+ .priority = 0,
+ .baseTile = 0x0
+ }, {
+ .bg = 1,
+ .charBaseIndex = 2,
+ .mapBaseIndex = 23,
+ .screenSize = 0,
+ .paletteMode = FALSE,
+ .priority = 1,
+ .baseTile = 0x0
+ }, {
+ .bg = 2,
+ .charBaseIndex = 3,
+ .mapBaseIndex = 31,
+ .screenSize = 1,
+ .paletteMode = TRUE,
+ .priority = 2,
+ .baseTile = 0x0
+ }
+};
+
+static const struct WindowTemplate sWindowTemplates_Charizard[] = {
+ {
+ .bg = 0x00,
+ .tilemapLeft = 0x0b,
+ .tilemapTop = 0x06,
+ .width = 0x08,
+ .height = 0x08,
+ .paletteNum = 0x0a,
+ .baseBlock = 0x0008
+ }, {
+ .bg = 0x00,
+ .tilemapLeft = 0x0a,
+ .tilemapTop = 0x05,
+ .width = 0x0a,
+ .height = 0x0a,
+ .paletteNum = 0x0a,
+ .baseBlock = 0x0048
+ }, {
+ .bg = 0x00,
+ .tilemapLeft = 0x09,
+ .tilemapTop = 0x03,
+ .width = 0x0c,
+ .height = 0x0d,
+ .paletteNum = 0x0a,
+ .baseBlock = 0x00ac
+ }, DUMMY_WIN_TEMPLATE
+};
+
+static const struct WindowTemplate sWindowTemplates_Venusaur[] = {
+ {
+ .bg = 0x00,
+ .tilemapLeft = 0x0b,
+ .tilemapTop = 0x06,
+ .width = 0x08,
+ .height = 0x08,
+ .paletteNum = 0x0a,
+ .baseBlock = 0x0008
+ }, {
+ .bg = 0x00,
+ .tilemapLeft = 0x0a,
+ .tilemapTop = 0x05,
+ .width = 0x0a,
+ .height = 0x0a,
+ .paletteNum = 0x0a,
+ .baseBlock = 0x0048
+ }, {
+ .bg = 0x00,
+ .tilemapLeft = 0x09,
+ .tilemapTop = 0x05,
+ .width = 0x0c,
+ .height = 0x0a,
+ .paletteNum = 0x0a,
+ .baseBlock = 0x00ac
+ }, DUMMY_WIN_TEMPLATE
+};
+
+static const struct WindowTemplate sWindowTemplates_Blastoise[] = {
+ {
+ .bg = 0x00,
+ .tilemapLeft = 0x0b,
+ .tilemapTop = 0x06,
+ .width = 0x08,
+ .height = 0x08,
+ .paletteNum = 0x0a,
+ .baseBlock = 0x0008
+ }, {
+ .bg = 0x00,
+ .tilemapLeft = 0x0a,
+ .tilemapTop = 0x05,
+ .width = 0x0a,
+ .height = 0x0a,
+ .paletteNum = 0x0a,
+ .baseBlock = 0x0048
+ }, {
+ .bg = 0x00,
+ .tilemapLeft = 0x0a,
+ .tilemapTop = 0x04,
+ .width = 0x0a,
+ .height = 0x0c,
+ .paletteNum = 0x0a,
+ .baseBlock = 0x00ac
+ }, DUMMY_WIN_TEMPLATE
+};
+
+static const struct WindowTemplate sWindowTemplates_Pikachu[] = {
+ {
+ .bg = 0x00,
+ .tilemapLeft = 0x0b,
+ .tilemapTop = 0x06,
+ .width = 0x08,
+ .height = 0x08,
+ .paletteNum = 0x0a,
+ .baseBlock = 0x0008
+ }, {
+ .bg = 0x00,
+ .tilemapLeft = 0x0a,
+ .tilemapTop = 0x05,
+ .width = 0x0a,
+ .height = 0x0a,
+ .paletteNum = 0x0a,
+ .baseBlock = 0x0048
+ }, {
+ .bg = 0x00,
+ .tilemapLeft = 0x09,
+ .tilemapTop = 0x04,
+ .width = 0x0c,
+ .height = 0x0c,
+ .paletteNum = 0x0a,
+ .baseBlock = 0x00ac
+ }, DUMMY_WIN_TEMPLATE
+};
+
+static const u16 sPalette_OneBlackThenAllWhite[] = INCBIN_U16("data/credits/unk_840C630.gbapal");
+static const u32 sAffineCircleGfx[] = INCBIN_U32("data/credits/unk_840C650.8bpp.lz");
+static const u32 sAffineCircleMap[] = INCBIN_U32("data/credits/unk_840CA54.bin.lz");
+static const u32 sWindow1Map_Charizard[] = INCBIN_U32("data/credits/unk_840CB8C.bin.lz");
+static const u32 sWindow2Map_Charizard[] = INCBIN_U32("data/credits/unk_840D228.bin.lz");
+static const u32 sUnusedTilemap[] = INCBIN_U32("data/credits/unk_840DC0C.bin.lz");
+static const u32 sWindow1Map_Venusaur[] = INCBIN_U32("data/credits/unk_840E158.bin.lz");
+static const u32 sWindow2Map_Venusaur[] = INCBIN_U32("data/credits/unk_840E904.bin.lz");
+static const u32 sWindow1Map_Blastoise[] = INCBIN_U32("data/credits/unk_840F240.bin.lz");
+static const u32 sWindow2Map_Blastoise[] = INCBIN_U32("data/credits/unk_840F944.bin.lz");
+static const u32 sWindow1Map_Pikachu[] = INCBIN_U32("data/credits/unk_8410198.bin.lz");
+static const u32 sWindow2Map_Pikachu[] = INCBIN_U32("data/credits/unk_84105B4.bin.lz");
+
+static const u32 filler_8410AFC = 0xF0;
+
+static const u16 sTheEndGfxPal[] = INCBIN_U16("data/credits/unk_8410B20.gbapal");
+static const u8 sTheEndGfxTiles[] = INCBIN_U8("data/credits/unk_8410B20.4bpp.lz");
+static const u8 sTheEndGfxMap[] = INCBIN_U8("data/credits/unk_8410B20.bin.lz");
+
+static const struct CompressedGraphicsHeader sCopyrightOrTheEndGfxHeaders[] = {
+ {
+ .tiles = gCreditsAllRightsReservedGfxTiles,
+ .map = gCreditsAllRightsReservedGfxMap,
+ .palette = gCreditsAllRightsReservedGfxPal
+ }, {
+ .tiles = sTheEndGfxTiles,
+ .map = sTheEndGfxMap,
+ .palette = sTheEndGfxPal
+ }
+};
+
+#define CREDITS_PRINT(text, duration) { CREDITSSCRCMD_PRINT, CREDITS_STRING_##text, duration }
+#define CREDITS_MAPNEXT(map, duration) { CREDITSSCRCMD_MAPNEXT, CREDITS_MAP_##map, duration }
+#define CREDITS_MAP(map, duration) { CREDITSSCRCMD_MAP, CREDITS_MAP_##map, duration }
+#define CREDITS_MON(mon) { CREDITSSCRCMD_MON, CREDITSMON_##mon }
+#define CREDITS_THEENDGFX(idx, duration) { CREDITSSCRCMD_THEENDGFX, CREDITSCLOSING_##idx, duration }
+#define CREDITS_WAITBUTTON(time) { CREDITSSCRCMD_WAITBUTTON, .duration = time }
+
+static const struct CreditsScrcmd sCreditsScript[] = {
+ CREDITS_MAPNEXT(ROUTE23, 16),
+ CREDITS_PRINT(DIRECTOR, 300),
+ CREDITS_PRINT(ART_DIRECTOR_BATTLE_DIRECTOR, 300),
+ CREDITS_PRINT(PROGRAM_LEADER_PLANNING_LEADER_GRAPHIC_DESIGN_LEADER, 300),
+ CREDITS_PRINT(DUMMY, 60),
+ CREDITS_MAPNEXT(VIRIDIAN_CITY, 0),
+ CREDITS_PRINT(PROGRAMMERS, 211),
+ CREDITS_PRINT(SYSTEM_PROGRAMMERS, 211),
+ CREDITS_PRINT(GRAPHIC_DESIGNERS, 211),
+ CREDITS_MAPNEXT(PEWTER_CITY, 0),
+ CREDITS_PRINT(GRAPHIC_DESIGNERS_2, 211),
+ CREDITS_PRINT(MUSIC_COMPOSITION, 210),
+ CREDITS_PRINT(SOUND_EFFECTS, 210),
+ CREDITS_MON(CHARIZARD),
+ CREDITS_MAP(CERULEAN_CITY, 16),
+ CREDITS_PRINT(GAME_DESIGNERS, 211),
+ CREDITS_PRINT(GAME_DESIGNERS_2, 210),
+ CREDITS_PRINT(GAME_SCENARIO, 210),
+ CREDITS_MAPNEXT(ROUTE25, 16),
+ CREDITS_PRINT(SCRIPT_DESIGNER_MAP_DESIGNER, 211),
+ CREDITS_PRINT(PARAMETRIC_DESIGNERS, 210),
+ CREDITS_PRINT(POKEDEX_TEXT, 210),
+ CREDITS_MAPNEXT(VERMILION_CITY, 16),
+ CREDITS_PRINT(POKEMON_DESIGNERS, 211),
+ CREDITS_PRINT(POKEMON_DESIGNERS_2, 210),
+ CREDITS_PRINT(POKEMON_DESIGNERS_3, 210),
+ CREDITS_MON(VENUSAUR),
+ CREDITS_MAP(ROUTE10, 16),
+ CREDITS_PRINT(SUPPORTING_PROGRAMMERS, 211),
+ CREDITS_PRINT(NCL_PRODUCT_TESTING, 210),
+ CREDITS_PRINT(SPECIAL_THANKS, 210),
+ CREDITS_MAPNEXT(CELADON_CITY, 16),
+ CREDITS_PRINT(SPECIAL_THANKS_2, 221),
+ CREDITS_PRINT(SPECIAL_THANKS_3, 221),
+ CREDITS_PRINT(BRAILLE_CODE_CHECK, 221),
+ CREDITS_PRINT(INFORMATION_SUPERVISORS, 221),
+ CREDITS_PRINT(DUMMY, 51),
+ CREDITS_MAPNEXT(SAFFRON_CITY_DUPLICATE, 16),
+ CREDITS_PRINT(COORDINATORS, 211),
+ CREDITS_PRINT(TASK_MANAGERS, 210),
+ CREDITS_PRINT(PRODUCERS, 210),
+ CREDITS_MON(BLASTOISE),
+ CREDITS_MAP(ROUTE17, 16),
+ CREDITS_PRINT(EXECUTIVE_DIRECTOR, 331),
+ CREDITS_PRINT(EXECUTIVE_PRODUCER, 331),
+ CREDITS_PRINT(EXECUTIVE_PRODUCER_2, 331),
+ CREDITS_MAPNEXT(FUCHSIA_CITY, 16),
+ CREDITS_PRINT(ENGLISH_VERSION_COORDINATORS, 221),
+ CREDITS_PRINT(TRANSLATOR_TEXT_EDITOR, 221),
+ CREDITS_PRINT(PROGRAMMERS_2, 221),
+ CREDITS_PRINT(ENVIRONMENT_TOOL_PROGRAMMERS, 221),
+ CREDITS_PRINT(DUMMY, 52),
+ CREDITS_MAPNEXT(CINNABAR_ISLAND, 16),
+ CREDITS_PRINT(GRAPHIC_DESIGNER, 210),
+ CREDITS_PRINT(NOA_PRODUCT_TESTING, 210),
+ CREDITS_PRINT(BRAILLE_CODE_CHECK_2, 211),
+ CREDITS_MON(PIKACHU),
+ CREDITS_MAP(ROUTE21_NORTH, 16),
+ CREDITS_PRINT(BRAILLE_CODE_CHECK_3, 221),
+ CREDITS_PRINT(BRAILLE_CODE_CHECK_4, 221),
+ CREDITS_PRINT(SPECIAL_THANKS_4, 221),
+ CREDITS_PRINT(SPECIAL_THANKS_5, 221),
+ CREDITS_PRINT(DUMMY, 52),
+ CREDITS_THEENDGFX(ALLRIGHTSRESERVED, 224),
+ CREDITS_THEENDGFX(THEEND, 240),
+ CREDITS_WAITBUTTON(600)
+};
+
+static const ALIGNED(4) u8 sTextColor_Unused[3] = {0, 1, 2};
+static const ALIGNED(4) u8 sTextColor_Header[3] = {0, 5, 2};
+static const ALIGNED(4) u8 sTextColor_Regular[3] = {0, 1, 2};
+
+static const struct WindowTemplate sCreditsWindowTemplate = {
+ .bg = 0,
+ .tilemapLeft = 0,
+ .tilemapTop = 4,
+ .width = 30,
+ .height = 12,
+ .paletteNum = 15,
+ .baseBlock = 0x008
+};
+
+static const u16 sMalePlayerSpritePal[] = INCBIN_U16("data/credits/unk_8410E10.gbapal");
+static const u32 sMalePlayerSpriteGfx[] = INCBIN_U32("data/credits/unk_8410E30.4bpp.lz");
+static const u16 sFemalePlayerSpritePal[] = INCBIN_U16("data/credits/unk_8411BF8.gbapal");
+static const u32 sFemalePlayerSpriteGfx[] = INCBIN_U32("data/credits/unk_8411C18.4bpp.lz");
+static const u16 sRivalSpritePal[] = INCBIN_U16("data/credits/unk_84129A0.gbapal");
+static const u32 sRivalSpriteGfx[] = INCBIN_U32("data/credits/unk_84129C0.4bpp.lz");
+static const u16 sGroundSpritePal_Grass[] = INCBIN_U16("data/credits/unk_8413318.gbapal");
+static const u32 sGroundSpriteGfx_Grass[] = INCBIN_U32("data/credits/unk_8413338.4bpp.lz");
+static const u16 sGroundSpritePal_Dirt[] = INCBIN_U16("data/credits/unk_8413854.gbapal");
+static const u32 sGroundSpriteGfx_Dirt[] = INCBIN_U32("data/credits/unk_8413874.4bpp.lz");
+static const u16 sGroundSpritePal_City[] = INCBIN_U16("data/credits/unk_8413D98.gbapal");
+static const u32 sGroundSpriteGfx_City[] = INCBIN_U32("data/credits/unk_8413DB8.4bpp.lz");
+
+static const u16 sPlayerRivalSpriteParams[][3] = {
+ { 0, 3, 1 },
+ { 0, 2, 0 },
+ { 0, 3, 0 },
+ { 1, 1, 2 },
+ { 0, 0, 3 }
+};
+
+static const struct OamData sOamData_PlayerOrRival = {
+ .affineMode = ST_OAM_AFFINE_OFF,
+ .mosaic = FALSE,
+ .objMode = ST_OAM_OBJ_NORMAL,
+ .bpp = ST_OAM_4BPP,
+ .shape = ST_OAM_SQUARE,
+ .size = ST_OAM_SIZE_3,
+ .tileNum = 0x000,
+ .priority = 0,
+ .paletteNum = 15
+};
+
+const union AnimCmd sAnimCmds_PlayerOrRival[] = {
+ ANIMCMD_FRAME(0x000, 8),
+ ANIMCMD_FRAME(0x040, 8),
+ ANIMCMD_FRAME(0x080, 8),
+ ANIMCMD_FRAME(0x0C0, 8),
+ ANIMCMD_FRAME(0x100, 8),
+ ANIMCMD_FRAME(0x140, 8),
+ ANIMCMD_JUMP(0)
+};
+
+const union AnimCmd *const sAnimCmdTable_PlayerOrRival[] = {
+ sAnimCmds_PlayerOrRival
+};
+
+static const struct SpriteTemplate sPlayerOrRivalSpriteTemplate = {
+ .oam = &sOamData_PlayerOrRival,
+ .anims = sAnimCmdTable_PlayerOrRival,
+ .affineAnims = gDummySpriteAffineAnimTable,
+ .callback = SpriteCallbackDummy
+};
+
+static const struct OamData sOamData_Ground = {
+ .affineMode = ST_OAM_AFFINE_OFF,
+ .mosaic = FALSE,
+ .objMode = ST_OAM_OBJ_NORMAL,
+ .bpp = ST_OAM_4BPP,
+ .shape = ST_OAM_H_RECTANGLE,
+ .size = ST_OAM_SIZE_3,
+ .tileNum = 0x000,
+ .priority = 0,
+ .paletteNum = 14
+};
+
+const union AnimCmd sAnimCmds_GroundRunning[] = {
+ ANIMCMD_FRAME(0x00, 8),
+ ANIMCMD_FRAME(0x20, 8),
+ ANIMCMD_FRAME(0x40, 8),
+ ANIMCMD_FRAME(0x60, 8),
+ ANIMCMD_FRAME(0x80, 8),
+ ANIMCMD_FRAME(0xA0, 8),
+ ANIMCMD_FRAME(0xC0, 8),
+ ANIMCMD_FRAME(0xE0, 8),
+ ANIMCMD_JUMP(0)
+};
+
+const union AnimCmd sAnimCmds_GroundStatic[] = {
+ ANIMCMD_FRAME(0x00, 8),
+ ANIMCMD_JUMP(0)
+};
+
+const union AnimCmd *const sAnimCmdTable_GroundRunning[] = {
+ sAnimCmds_GroundRunning
+};
+
+const union AnimCmd *const sAnimCmdTable_GroundStatic[] = {
+ sAnimCmds_GroundStatic
+};
+
+static const struct SpriteTemplate sGroundSpriteTemplate_Running = {
+ .oam = &sOamData_Ground,
+ .anims = sAnimCmdTable_GroundRunning,
+ .affineAnims = gDummySpriteAffineAnimTable,
+ .callback = SpriteCallbackDummy
+};
+
+static const struct SpriteTemplate sGroundSpriteTemplate_Static = {
+ .oam = &sOamData_Ground,
+ .anims = sAnimCmdTable_GroundStatic,
+ .affineAnims = gDummySpriteAffineAnimTable,
+ .callback = SpriteCallbackDummy
+};
+
+static const struct CreditsOverworldCmd sOverworldCmd_Route23[] = {
+ { 0x00fe, MAP_GROUP(ROUTE23), MAP_NUM(ROUTE23) },
+ { 0x000b, 0x006b, 0x0001 },
+ { 0x0000, 0x0001, 0x0500 }, // Scroll down
+ { 0x00fd, 0x00fd, 0x00fd }
+};
+
+static const struct CreditsOverworldCmd sOverworldCmd_ViridianCity[] = {
+ { 0x00fe, MAP_GROUP(VIRIDIAN_CITY), MAP_NUM(VIRIDIAN_CITY) },
+ { 0x001e, 0x0022, 0x0001 },
+ { 0x0000, 0xffff, 0x0500 }, // Scroll up
+ { 0x00fd, 0x00fd, 0x00fd }
+};
+
+static const struct CreditsOverworldCmd sOverworldCmd_PewterCity[] = {
+ { 0x00fe, MAP_GROUP(PEWTER_CITY), MAP_NUM(PEWTER_CITY) },
+ { 0x0014, 0x001a, 0x0001 },
+ { 0x0000, 0xffff, 0x0500 }, // Scroll up
+ { 0x00fd, 0x00fd, 0x00fd }
+};
+
+static const struct CreditsOverworldCmd sOverworldCmd_CeruleanCity[] = {
+ { 0x00fe, MAP_GROUP(CERULEAN_CITY), MAP_NUM(CERULEAN_CITY) },
+ { 0x0008, 0x0006, 0x0001 },
+ { 0x0001, 0x0001, 0x0500 }, // Scroll right and down
+ { 0x00fd, 0x00fd, 0x00fd }
+};
+
+static const struct CreditsOverworldCmd sOverworldCmd_Route25[] = {
+ { 0x00fe, MAP_GROUP(ROUTE25), MAP_NUM(ROUTE25) },
+ { 0x0019, 0x0006, 0x0001 },
+ { 0x0001, 0x0000, 0x0500 }, // Scroll right
+ { 0x00fd, 0x00fd, 0x00fd }
+};
+
+static const struct CreditsOverworldCmd sOverworldCmd_VermilionCity[] = {
+ { 0x00fe, MAP_GROUP(VERMILION_CITY), MAP_NUM(VERMILION_CITY) },
+ { 0x0009, 0x0007, 0x0001 },
+ { 0x0001, 0x0001, 0x0500 }, // Scroll right and down
+ { 0x00fd, 0x00fd, 0x00fd }
+};
+
+static const struct CreditsOverworldCmd sOverworldCmd_Route10[] = {
+ { 0x00fe, MAP_GROUP(ROUTE10), MAP_NUM(ROUTE10) },
+ { 0x000b, 0x0044, 0x0001 },
+ { 0x0000, 0x0001, 0x0500 }, // Scroll down
+ { 0x00fd, 0x00fd, 0x00fd }
+};
+
+static const struct CreditsOverworldCmd sOverworldCmd_CeladonCity[] = {
+ { 0x00fe, MAP_GROUP(CELADON_CITY), MAP_NUM(CELADON_CITY) },
+ { 0x0030, 0x0010, 0x0001 },
+ { 0xffff, 0x0000, 0x0500 }, // Scroll left
+ { 0x00fd, 0x00fd, 0x00fd }
+};
+
+static const struct CreditsOverworldCmd sOverworldCmd_SaffronCity[] = {
+ { 0x00fe, MAP_GROUP(SAFFRON_CITY_DUPLICATE), MAP_NUM(SAFFRON_CITY_DUPLICATE) },
+ { 0x0027, 0x0005, 0x0001 },
+ { 0x0000, 0x0001, 0x0500 }, // Scroll down
+ { 0x00fd, 0x00fd, 0x00fd }
+};
+
+static const struct CreditsOverworldCmd sOverworldCmd_Route17[] = {
+ { 0x00fe, MAP_GROUP(ROUTE17), MAP_NUM(ROUTE17) },
+ { 0x0007, 0x002b, 0x0001 },
+ { 0x0000, 0x0001, 0x0500 }, // Scroll down
+ { 0x00fd, 0x00fd, 0x00fd }
+};
+
+static const struct CreditsOverworldCmd sOverworldCmd_FuchsiaCity[] = {
+ { 0x00fe, MAP_GROUP(FUCHSIA_CITY), MAP_NUM(FUCHSIA_CITY) },
+ { 0x001c, 0x0005, 0x0001 },
+ { 0x0000, 0x0001, 0x0500 }, // Scroll down
+ { 0x00fd, 0x00fd, 0x00fd }
+};
+
+static const struct CreditsOverworldCmd sOverworldCmd_CinnabarIsland[] = {
+ { 0x00fe, MAP_GROUP(CINNABAR_ISLAND), MAP_NUM(CINNABAR_ISLAND) },
+ { 0x000d, 0x0011, 0x0001 },
+ { 0x0000, 0xffff, 0x0500 }, // Scroll up
+ { 0x00fd, 0x00fd, 0x00fd }
+};
+
+static const struct CreditsOverworldCmd sOverworldCmd_Route21[] = {
+ { 0x00fe, MAP_GROUP(ROUTE21_NORTH), MAP_NUM(ROUTE21_NORTH) },
+ { 0x0008, 0x0014, 0x0001 },
+ { 0x0000, 0xffff, 0x0500 }, // Scroll up
+ { 0x00fd, 0x00fd, 0x00fd },
+};
+
+static const struct CreditsOverworldCmd *const sOverworldMapScenes[] = {
+ sOverworldCmd_Route23,
+ sOverworldCmd_ViridianCity,
+ sOverworldCmd_PewterCity,
+ sOverworldCmd_CeruleanCity,
+ sOverworldCmd_Route25,
+ sOverworldCmd_VermilionCity,
+ sOverworldCmd_Route10,
+ sOverworldCmd_CeladonCity,
+ sOverworldCmd_SaffronCity,
+ sOverworldCmd_Route17,
+ sOverworldCmd_FuchsiaCity,
+ sOverworldCmd_CinnabarIsland,
+ sOverworldCmd_Route21
+};
+
+static const struct CreditsTextHeader sCreditsTexts[] = {
+ { gCreditsString_Director, gCreditsString_Junichi_Masuda, FALSE },
+ { gCreditsString_Art_Director_Battle_Director, gCreditsString_Ken_Sugimori_Shigeki_Morimoto, FALSE },
+ { gCreditsString_Program_Leader_Planning_Leader_Graphic_Design_Leader, gCreditsString_Tetsuya_Watanabe_Koji_Nishino_Takao_Unno, FALSE },
+ { gCreditsString_Programmers, gCreditsString_Hiroyuki_Nakamura_Masao_Taya_Satoshi_Nohara_Miyuki_Iwasawa_Daisuke_Goto, FALSE },
+ { gCreditsString_System_Programmers, gCreditsString_Tetsuya_Watanabe_Akito_Mori_Hisashi_Sogabe_Sousuke_Tamada, TRUE },
+ { gCreditsString_Graphic_Designers, gCreditsString_Takao_Unno_Asuka_Iwashita_Kanako_Eo_Hiroki_Fuchino, TRUE },
+ { gCreditsString_Graphic_Designers_2, gCreditsString_Ken_Sugimori_Hironobu_Yoshida, TRUE },
+ { gCreditsString_Music_Composition, gCreditsString_Go_Ichinose_Junichi_Masuda, TRUE },
+ { gCreditsString_Sound_Effects, gCreditsString_Go_Ichinose, FALSE },
+ { gCreditsString_Game_Designers, gCreditsString_Junichi_Masuda_Koji_Nishino_Tetsuji_Ohta, FALSE },
+ { gCreditsString_Game_Designers_2, gCreditsString_Hitomi_Sato_Shigeru_Ohmori_Tadashi_Takahashi, FALSE },
+ { gCreditsString_Game_Scenario, gCreditsString_Hitomi_Sato_Satoshi_Tajiri, TRUE },
+ { gCreditsString_Script_Designer_Map_Designer, gCreditsString_Satoshi_Nohara_Shigeru_Ohmori, FALSE },
+ { gCreditsString_Parametric_Designers, gCreditsString_Koji_Nishino_Tetsuji_Ohta_Shigeki_Morimoto, FALSE },
+ { gCreditsString_POKeDEX_Text, gCreditsString_Kenji_Matsushima, FALSE },
+ { gCreditsString_POKeMON_Designers, gCreditsString_Ken_Sugimori_Motofumi_Fujiwara_Shigeki_Morimoto_Hironobu_Yoshida, TRUE },
+ { gCreditsString_POKeMON_Designers_2, gCreditsString_Satoshi_Ohta_Asuka_Iwashita_Takao_Unno_Kanako_Eo_Aimi_Tomita, FALSE },
+ { gCreditsString_POKeMON_Designers_3, gCreditsString_Atsuko_Nishida_Muneo_Saito_Rena_Yoshikawa_Jun_Okutani, TRUE },
+ { gCreditsString_Supporting_Programmers, gCreditsString_Teruyuki_Yoshioka_Takao_Nakano_Satoshi_Mitsuhara_Daisuke_Hoshino, FALSE },
+ { gCreditsString_NCL_Product_Testing, gCreditsString_NCL_Super_Mario_Club, FALSE },
+ { gCreditsString_Special_Thanks, gCreditsString_Hiro_Nakamura_Hiroyuki_Uesugi_Teruki_Murakawa_Kazuya_Suyama, FALSE },
+ { gCreditsString_Special_Thanks_2, gCreditsString_Kenji_Tominaga_Kenjiro_Ito_Tomotaka_Komura_Michiko_Takizawa, FALSE },
+ { gCreditsString_Special_Thanks_3, gCreditsString_Makiko_Takada_Mikiko_Ohashi_Shusaku_Egami_Takanao_Kondo_Rui_Kawaguchi, FALSE },
+ { gCreditsString_Braille_Code_Check, gCreditsString_Japan_Braille_Library, FALSE },
+ { gCreditsString_Information_Supervisors, gCreditsString_Hiroki_Enomoto_Kazuyuki_Terada_Yuri_Sakurai_Yumi_Funasaka_Naoko_Yanase, FALSE },
+ { gCreditsString_Coordinators, gCreditsString_Azusa_Tajima_Akira_Kinashi_Kazuki_Yoshihara_Retsuji_Nomoto, FALSE },
+ { gCreditsString_Task_Managers, gCreditsString_Hitoshi_Yamagami_Gakuji_Nomoto, TRUE },
+ { gCreditsString_Producers, gCreditsString_Hiroyuki_Jinnai_Takehiro_Izushi_Hiroaki_Tsuru, FALSE },
+ { gCreditsString_Executive_Director, gCreditsString_Satoshi_Tajiri, FALSE },
+ { gCreditsString_Executive_Producer, gCreditsString_Satoru_Iwata, FALSE },
+ { gCreditsString_Executive_Producer_2, gCreditsString_Tsunekaz_Ishihara, FALSE },
+ { gCreditsString_English_Version_Coordinators, gCreditsString_Hiro_Nakamura_Seth_McMahill, FALSE },
+ { gCreditsString_Translator_Text_Editor, gCreditsString_Nob_Ogasawara_Teresa_Lillygren, FALSE },
+ { gCreditsString_Programmers_2, gCreditsString_Teruki_Murakawa_Souichi_Yamamoto_Yuichiro_Ito_Akira_Kinashi, FALSE },
+ { gCreditsString_Environment_Tool_Programmers, gCreditsString_Teruki_Murakawa_Souichi_Yamamoto_Kimiko_Nakamichi, TRUE },
+ { gCreditsString_NOA_Product_Testing, gCreditsString_Thomas_Hertzog_Kathy_Huguenard_Mika_Kurosawa, TRUE },
+ { gCreditsString_Braille_Code_Check_2, gCreditsString_National_Federation_of_the_Blind_Patricia_A_Maurer_Japan_Braille_Library_European_Blind_Union, TRUE },
+ { gCreditsString_Braille_Code_Check_3, gCreditsString_National_Information_Library_Service_Margaret_Campion, TRUE },
+ { gCreditsString_Special_Thanks_4, gCreditsString_Takehiro_Izushi_Motoyasu_Tojima_Hitoshi_Yamagami_Hiroyuki_Uesugi, FALSE },
+ { gCreditsString_Special_Thanks_5, gCreditsString_Nicola_Pratt_Barlow_Shellie_Dow_Anthony_Howitt_Naoko_Saeki_Kyoko_Onishi, FALSE },
+ { gCreditsString_Braille_Code_Check_4, gCreditsString_The_Royal_New_Zealand_Foundation_of_the_Blind_Greg_Moran, FALSE },
+ { gCreditsString_Graphic_Designer, gCreditsString_Akira_Kinashi, FALSE },
+ { gString_Dummy, gString_Dummy, FALSE }
+};
+
+void Special_Credits(void)
+{
+ sCreditsMgr = AllocZeroed(sizeof(*sCreditsMgr));
+ ResetTasks();
+ sCreditsMgr->taskId = 0xFF;
+ sCreditsMgr->unk_1D = 0;
+ ResetSpriteData();
+ SetMainCallback2(CB2_Credits);
+}
+
+static void CB2_Credits(void)
+{
+ switch (RollCredits())
+ {
+ case 0:
+ RunTasks();
+ AnimateSprites();
+ BuildOamBuffer();
+ UpdatePaletteFade();
+ break;
+ case 1:
+ if (sCreditsMgr->unk_1D & 1)
+ {
+ Overworld_CreditsMainCB();
+ }
+ else
+ {
+ RunTasks();
+ AnimateSprites();
+ BuildOamBuffer();
+ UpdatePaletteFade();
+ }
+ sCreditsMgr->unk_1D++;
+ break;
+ case 2:
+ FlagClear(0x4000);
+ gDisableMapMusicChangeOnMapLoad = MUSIC_DISABLE_OFF;
+ Free(sCreditsMgr);
+ SoftReset(RESET_ALL);
+ // noreturn
+ }
+}
+
+static void SwitchWin1OffWin0On(void)
+{
+ ClearGpuRegBits(REG_OFFSET_DISPCNT, DISPCNT_WIN1_ON);
+ SetGpuRegBits(REG_OFFSET_DISPCNT, DISPCNT_WIN0_ON);
+ SetGpuReg(REG_OFFSET_WININ, 0x1F3F);
+ SetGpuReg(REG_OFFSET_WINOUT, 0x000E);
+}
+
+static void InitBgDarkenEffect(void)
+{
+ SetGpuReg(REG_OFFSET_BLDCNT, BLDCNT_TGT1_BG1 | BLDCNT_TGT1_BG2 | BLDCNT_TGT1_BG3 | BLDCNT_EFFECT_DARKEN);
+ SetGpuReg(REG_OFFSET_BLDALPHA, BLDALPHA_BLEND(16, 4));
+ SetGpuReg(REG_OFFSET_BLDY, 10);
+}
+
+static void CreateCreditsWindow(void)
+{
+ sCreditsMgr->windowId = AddWindow(&sCreditsWindowTemplate);
+ FillWindowPixelBuffer(sCreditsMgr->windowId, PIXEL_FILL(0));
+ PutWindowTilemap(sCreditsMgr->windowId);
+ CopyWindowToVram(sCreditsMgr->windowId, 3);
+ sCreditsMgr->windowIsActive = TRUE;
+}
+
+static void DestroyCreditsWindow(void)
+{
+ if (sCreditsMgr->windowIsActive)
+ {
+ RemoveWindow(sCreditsMgr->windowId);
+ CleanupOverworldWindowsAndTilemaps();
+ sCreditsMgr->windowIsActive = FALSE;
+ }
+}
+
+static bool32 DoOverworldMapScrollScene(UNUSED u8 unused)
+{
+ switch (sCreditsMgr->subseqno)
+ {
+ case 0:
+ FlagSet(0x4000);
+ gDisableMapMusicChangeOnMapLoad = MUSIC_DISABLE_KEEP;
+ sCreditsMgr->ovwldseqno = 0;
+ sCreditsMgr->subseqno++;
+ // fallthrough
+ case 1:
+ if (!Overworld_DoScrollSceneForCredits(&sCreditsMgr->ovwldseqno, sOverworldMapScenes[sCreditsMgr->whichMon], 0))
+ return FALSE;
+ CreateCreditsWindow();
+ SetGpuReg(REG_OFFSET_WIN0H, 0xF0);
+ SetGpuReg(REG_OFFSET_WIN0V, 0x247C);
+ SwitchWin1OffWin0On();
+ InitBgDarkenEffect();
+ Menu_LoadStdPalAt(0xF0);
+ gPlttBufferUnfaded[0xFF] = RGB_BLACK;
+ gPlttBufferFaded[0xFF] = RGB_BLACK;
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+#ifdef NONMATCHING
+static s32 RollCredits(void)
+{
+ u16 win0v[8];
+
+ switch (sCreditsMgr->mainseqno)
+ {
+ case CREDITSSCENE_INIT_WIN0:
+ SwitchWin1OffWin0On();
+ SetGpuReg(REG_OFFSET_WIN0H, 0xF0);
+ SetGpuReg(REG_OFFSET_WIN0V, 0x4F51);
+ sCreditsMgr->mainseqno = CREDITSSCENE_SETUP_DARKEN_EFFECT;
+ return 0;
+ case CREDITSSCENE_SETUP_DARKEN_EFFECT:
+ InitBgDarkenEffect();
+ CreateCreditsWindow();
+ Menu_LoadStdPalAt(0xF0);
+ gPlttBufferUnfaded[0xFF] = RGB_BLACK;
+ gPlttBufferFaded[0xFF] = RGB_BLACK;
+ sCreditsMgr->mainseqno = CREDITSSCENE_OPEN_WIN0;
+ return 0;
+ case CREDITSSCENE_OPEN_WIN0:
+ win0v[0] = GetGpuReg(REG_OFFSET_WIN0V) >> 8;
+ win0v[1] = GetGpuReg(REG_OFFSET_WIN0V) & 0xFF;
+ if (win0v[0] == 0x24)
+ {
+ sCreditsMgr->timer = 0;
+ sCreditsMgr->mainseqno = CREDITSSCENE_LOAD_PLAYER_SPRITE_AT_INDIGO;
+ }
+ else
+ {
+ win0v[0]--;
+ win0v[1]++;
+ SetGpuReg(REG_OFFSET_WIN0V, win0v[1] + (win0v[0] << 8));
+ }
+ return 0;
+ case CREDITSSCENE_LOAD_PLAYER_SPRITE_AT_INDIGO:
+ if (sCreditsMgr->timer == 0)
+ {
+ LoadPlayerOrRivalSprite(0);
+ sCreditsMgr->timer = 100;
+ sCreditsMgr->mainseqno = CREDITSSCENE_PRINT_TITLE_STAFF;
+ }
+ else
+ {
+ sCreditsMgr->timer--;
+ }
+ return 0;
+ case CREDITSSCENE_PRINT_TITLE_STAFF:
+ if (sCreditsMgr->timer == 0)
+ {
+ sCreditsMgr->timer = 360;
+ AddTextPrinterParameterized4(sCreditsMgr->windowId, 1, 0x08, 0x29, 1, 2, sTextColor_Header, 0, TITLE_TEXT);
+ sCreditsMgr->mainseqno = CREDITSSCENE_WAIT_TITLE_STAFF;
+ }
+ else
+ {
+ sCreditsMgr->timer--;
+ }
+ return 0;
+ case CREDITSSCENE_WAIT_TITLE_STAFF:
+ if (sCreditsMgr->timer != 0)
+ {
+ sCreditsMgr->timer--;
+ }
+ else
+ {
+ DestroyCreditsWindow();
+ sCreditsMgr->mainseqno = CREDITSSCENE_EXEC_CMD;
+ sCreditsMgr->timer = 0;
+ sCreditsMgr->scrcmdidx = 0;
+ }
+ return 0;
+ case CREDITSSCENE_EXEC_CMD:
+ if (sCreditsMgr->timer != 0)
+ {
+ sCreditsMgr->timer--;
+ return sCreditsMgr->canSpeedThrough;
+ }
+ else
+ {
+ switch (sCreditsScript[sCreditsMgr->scrcmdidx].cmd)
+ {
+ case CREDITSSCRCMD_PRINT:
+ BeginNormalPaletteFade(0x00008000, 0, 0, 16, RGB_BLACK);
+ sCreditsMgr->mainseqno = CREDITSSCENE_PRINT_ADDPRINTER1;
+ FillWindowPixelBuffer(sCreditsMgr->windowId, PIXEL_FILL(0));
+ return sCreditsMgr->canSpeedThrough;
+ case CREDITSSCRCMD_MAPNEXT:
+ sCreditsMgr->mainseqno = CREDITSSCENE_MAPNEXT_DESTROYWINDOW;
+ sCreditsMgr->whichMon = sCreditsScript[sCreditsMgr->scrcmdidx].param;
+ FieldWeather_StartFadingOutCreditsMap(1, 0, 0x3FFFFFFF);
+ break;
+ case CREDITSSCRCMD_MAP:
+ sCreditsMgr->mainseqno = CREDITSSCENE_MAP_LOADMAP_CREATESPRITES;
+ sCreditsMgr->whichMon = sCreditsScript[sCreditsMgr->scrcmdidx].param;
+ break;
+ case CREDITSSCRCMD_MON:
+ sCreditsMgr->mainseqno = CREDITSSCENE_MON_DESTROY_ASSETS;
+ sCreditsMgr->whichMon = sCreditsScript[sCreditsMgr->scrcmdidx].param;
+ fade_screen(1, 0);
+ break;
+ case CREDITSSCRCMD_THEENDGFX:
+ sCreditsMgr->mainseqno = CREDITSSCENE_THEEND_DESTROY_ASSETS;
+ sCreditsMgr->whichMon = sCreditsScript[sCreditsMgr->scrcmdidx].param;
+ BeginNormalPaletteFade(0xFFFFFFFF, 4, 0, 16, RGB_BLACK);
+ break;
+ case CREDITSSCRCMD_WAITBUTTON:
+ sCreditsMgr->mainseqno = CREDITSSCENE_WAITBUTTON;
+ break;
+ }
+ sCreditsMgr->timer = sCreditsScript[sCreditsMgr->scrcmdidx].duration;
+ sCreditsMgr->scrcmdidx++;
+ return 0;
+ }
+ case CREDITSSCENE_PRINT_ADDPRINTER1:
+ if (!gPaletteFade.active)
+ {
+ win0v[0] = sCreditsTexts[sCreditsScript[sCreditsMgr->scrcmdidx].param].unk_8; // unused
+ AddTextPrinterParameterized4(sCreditsMgr->windowId, 1, 2, 6, 0, 0, sTextColor_Header, -1, sCreditsTexts[sCreditsScript[sCreditsMgr->scrcmdidx].param].unk_0);
+ sCreditsMgr->mainseqno = CREDITSSCENE_PRINT_ADDPRINTER2;
+ }
+ return sCreditsMgr->canSpeedThrough;
+ case CREDITSSCENE_PRINT_ADDPRINTER2:
+ win0v[0] = sCreditsTexts[sCreditsScript[sCreditsMgr->scrcmdidx].param].unk_8;
+ AddTextPrinterParameterized4(sCreditsMgr->windowId, 2, 8, 6, 0, 0, sTextColor_Header, -1, sCreditsTexts[sCreditsScript[sCreditsMgr->scrcmdidx].param].unk_4);
+ sCreditsMgr->mainseqno = CREDITSSCENE_PRINT_DELAY;
+ return sCreditsMgr->canSpeedThrough;
+ case CREDITSSCENE_PRINT_DELAY:
+ CopyWindowToVram(sCreditsMgr->windowId, 2);
+ sCreditsMgr->timer = sCreditsScript[sCreditsMgr->scrcmdidx].duration;
+ sCreditsMgr->scrcmdidx++;
+ BeginNormalPaletteFade(0x00008000, 0, 16, 0, RGB_BLACK);
+ sCreditsMgr->mainseqno = CREDITSSCENE_EXEC_CMD;
+ return sCreditsMgr->canSpeedThrough;
+ case CREDITSSCENE_MAPNEXT_DESTROYWINDOW:
+ if (!gPaletteFade.active)
+ {
+ DestroyCreditsWindow();
+ sCreditsMgr->subseqno = 0;
+ sCreditsMgr->mainseqno = CREDITSSCENE_MAPNEXT_LOADMAP;
+ }
+ return 0;
+ case CREDITSSCENE_MAPNEXT_LOADMAP:
+ if (DoOverworldMapScrollScene(sCreditsMgr->whichMon))
+ {
+ sCreditsMgr->canSpeedThrough = 1;
+ sCreditsMgr->mainseqno = CREDITSSCENE_EXEC_CMD;
+ }
+ return 0;
+ case CREDITSSCENE_MAP_LOADMAP_CREATESPRITES:
+ if (!gPaletteFade.active)
+ {
+ DestroyCreditsWindow();
+ sCreditsMgr->subseqno = 0;
+ while (!DoOverworldMapScrollScene(sCreditsMgr->whichMon))
+ {}
+ switch (sCreditsMgr->whichMon)
+ {
+ case 3:
+ default:
+ win0v[0] = 1;
+ break;
+ case 6:
+ win0v[0] = 2;
+ break;
+ case 9:
+ win0v[0] = 3;
+ break;
+ case 12:
+ win0v[0] = 4;
+ break;
+ }
+ LoadPlayerOrRivalSprite(win0v[0]);
+ sCreditsMgr->canSpeedThrough = 1;
+ sCreditsMgr->mainseqno = CREDITSSCENE_EXEC_CMD;
+ }
+ return 0;
+ case CREDITSSCENE_MON_DESTROY_ASSETS:
+ if (!gPaletteFade.active)
+ {
+ DestroyPlayerOrRivalSprite();
+ DestroyCreditsWindow();
+ sCreditsMgr->subseqno = 0;
+ sCreditsMgr->canSpeedThrough = 0;
+ sCreditsMgr->mainseqno = CREDITSSCENE_MON_SHOW;
+ }
+ return 0;
+ case CREDITSSCENE_MON_SHOW:
+ if (DoCreditsMonScene())
+ {
+ sCreditsMgr->mainseqno = CREDITSSCENE_EXEC_CMD;
+ }
+ return 0;
+ case CREDITSSCENE_THEEND_DESTROY_ASSETS:
+ if (!gPaletteFade.active)
+ {
+ DestroyCreditsWindow();
+ sCreditsMgr->subseqno = 0;
+ sCreditsMgr->canSpeedThrough = 0;
+ sCreditsMgr->mainseqno = CREDITSSCENE_THEEND_SHOW;
+ }
+ return 0;
+ case CREDITSSCENE_THEEND_SHOW:
+ if (DoCopyrightOrTheEndGfxScene())
+ {
+ sCreditsMgr->mainseqno = CREDITSSCENE_EXEC_CMD;
+ }
+ return 0;
+ case CREDITSSCENE_WAITBUTTON:
+ if (JOY_NEW(A_BUTTON))
+ {
+ BeginNormalPaletteFade(0xFFFFFFFF, 0, 0, 16, RGB_WHITE);
+ sCreditsMgr->mainseqno = CREDITSSCENE_TERMINATE;
+ }
+ else if (sCreditsMgr->timer == 0)
+ {
+ sCreditsMgr->mainseqno = CREDITSSCENE_TERMINATE;
+ BeginNormalPaletteFade(0xFFFFFFFF, 0, 0, 16, RGB_WHITE);
+ }
+ else
+ sCreditsMgr->timer--;
+ return 0;
+ case CREDITSSCENE_TERMINATE:
+ if (!gPaletteFade.active)
+ DestroyCreditsWindow();
+ break;
+ }
+ return 2;
+}
+#else
+NAKED
+static s32 RollCredits(void)
+{
+ asm_unified("\tpush {r4-r7,lr}\n"
+ "\tsub sp, 0x24\n"
+ "\tldr r1, _080F3BEC @ =sCreditsMgr\n"
+ "\tldr r0, [r1]\n"
+ "\tldrb r0, [r0]\n"
+ "\tadds r7, r1, 0\n"
+ "\tcmp r0, 0x12\n"
+ "\tbls _080F3BE2\n"
+ "\tb _080F4180_default_return2\n"
+ "_080F3BE2:\n"
+ "\tlsls r0, 2\n"
+ "\tldr r1, _080F3BF0 @ =_080F3BF4\n"
+ "\tadds r0, r1\n"
+ "\tldr r0, [r0]\n"
+ "\tmov pc, r0\n"
+ "\t.align 2, 0\n"
+ "_080F3BEC: .4byte sCreditsMgr\n"
+ "_080F3BF0: .4byte _080F3BF4\n"
+ "\t.align 2, 0\n"
+ "_080F3BF4:\n"
+ "\t.4byte _080F3C40_case00\n"
+ "\t.4byte _080F3C64_case01\n"
+ "\t.4byte _080F3C98_case02\n"
+ "\t.4byte _080F3CEE_case03\n"
+ "\t.4byte _080F3D0A_case04\n"
+ "\t.4byte _080F3D48_case05\n"
+ "\t.4byte _080F3D6A_case06\n"
+ "\t.4byte _080F3EB4_case07\n"
+ "\t.4byte _080F3F24_case08\n"
+ "\t.4byte _080F3F84_case09\n"
+ "\t.4byte _080F3FC4_case0A\n"
+ "\t.4byte _080F3FF0_case0B\n"
+ "\t.4byte _080F400A_case0C\n"
+ "\t.4byte _080F4084_case0D\n"
+ "\t.4byte _080F40B8_case0E\n"
+ "\t.4byte _080F40D0_case0F\n"
+ "\t.4byte _080F4100_case10\n"
+ "\t.4byte _080F4118_case11\n"
+ "\t.4byte _080F4170_case12\n"
+ "_080F3C40_case00:\n"
+ "\tbl SwitchWin1OffWin0On\n"
+ "\tmovs r0, 0x40\n"
+ "\tmovs r1, 0xF0\n"
+ "\tbl SetGpuReg\n"
+ "\tldr r1, _080F3C5C @ =0x00004f51\n"
+ "\tmovs r0, 0x44\n"
+ "\tbl SetGpuReg\n"
+ "\tldr r0, _080F3C60 @ =sCreditsMgr\n"
+ "\tldr r1, [r0]\n"
+ "\tmovs r0, 0x1\n"
+ "\tb _080F413C_setfield0_return0\n"
+ "\t.align 2, 0\n"
+ "_080F3C5C: .4byte 0x00004f51\n"
+ "_080F3C60: .4byte sCreditsMgr\n"
+ "_080F3C64_case01:\n"
+ "\tbl InitBgDarkenEffect\n"
+ "\tbl CreateCreditsWindow\n"
+ "\tmovs r0, 0xF0\n"
+ "\tbl Menu_LoadStdPalAt\n"
+ "\tldr r0, _080F3C8C @ =gPlttBufferUnfaded\n"
+ "\tmovs r2, 0xFF\n"
+ "\tlsls r2, 1\n"
+ "\tadds r0, r2\n"
+ "\tmovs r1, 0\n"
+ "\tstrh r1, [r0]\n"
+ "\tldr r0, _080F3C90 @ =gPlttBufferFaded\n"
+ "\tadds r0, r2\n"
+ "\tstrh r1, [r0]\n"
+ "\tldr r0, _080F3C94 @ =sCreditsMgr\n"
+ "\tldr r1, [r0]\n"
+ "\tmovs r0, 0x2\n"
+ "\tb _080F413C_setfield0_return0\n"
+ "\t.align 2, 0\n"
+ "_080F3C8C: .4byte gPlttBufferUnfaded\n"
+ "_080F3C90: .4byte gPlttBufferFaded\n"
+ "_080F3C94: .4byte sCreditsMgr\n"
+ "_080F3C98_case02:\n"
+ "\tmovs r0, 0x44\n"
+ "\tbl GetGpuReg\n"
+ "\tadd r1, sp, 0x14\n"
+ "\tlsls r0, 16\n"
+ "\tlsrs r0, 24\n"
+ "\tstrh r0, [r1]\n"
+ "\tmovs r0, 0x44\n"
+ "\tbl GetGpuReg\n"
+ "\tadd r2, sp, 0x14\n"
+ "\tmovs r1, 0xFF\n"
+ "\tands r1, r0\n"
+ "\tstrh r1, [r2, 0x2]\n"
+ "\tadds r0, r2, 0\n"
+ "\tldrh r0, [r0]\n"
+ "\tcmp r0, 0x24\n"
+ "\tbne _080F3CCC\n"
+ "\tldr r0, _080F3CC8 @ =sCreditsMgr\n"
+ "\tldr r1, [r0]\n"
+ "\tmovs r0, 0\n"
+ "\tstrh r0, [r1, 0x4]\n"
+ "\tmovs r0, 0x3\n"
+ "\tb _080F413C_setfield0_return0\n"
+ "\t.align 2, 0\n"
+ "_080F3CC8: .4byte sCreditsMgr\n"
+ "_080F3CCC:\n"
+ "\tadd r1, sp, 0x14\n"
+ "\tsubs r0, 0x1\n"
+ "\tstrh r0, [r1]\n"
+ "\tadds r2, r1, 0\n"
+ "\tadds r0, r1, 0\n"
+ "\tldrh r1, [r0, 0x2]\n"
+ "\tadds r1, 0x1\n"
+ "\tstrh r1, [r2, 0x2]\n"
+ "\tldrh r0, [r0]\n"
+ "\tlsls r0, 8\n"
+ "\tadds r1, r0\n"
+ "\tlsls r1, 16\n"
+ "\tlsrs r1, 16\n"
+ "\tmovs r0, 0x44\n"
+ "\tbl SetGpuReg\n"
+ "\tb _080F3D06_return0\n"
+ "_080F3CEE_case03:\n"
+ "\tldr r1, [r7]\n"
+ "\tldrh r0, [r1, 0x4]\n"
+ "\tcmp r0, 0\n"
+ "\tbne _080F3D52_decfield4_return0\n"
+ "\tmovs r0, 0\n"
+ "\tbl LoadPlayerOrRivalSprite\n"
+ "\tldr r0, [r7]\n"
+ "\tmovs r1, 0x64\n"
+ "\tstrh r1, [r0, 0x4]\n"
+ "\tmovs r1, 0x4\n"
+ "\tstrb r1, [r0]\n"
+ "_080F3D06_return0:\n"
+ "\tmovs r0, 0\n"
+ "\tb _080F4182_return\n"
+ "_080F3D0A_case04:\n"
+ "\tldr r1, [r7]\n"
+ "\tldrh r0, [r1, 0x4]\n"
+ "\tadds r2, r0, 0\n"
+ "\tcmp r2, 0\n"
+ "\tbne _080F3D52_decfield4_return0\n"
+ "\tmovs r0, 0xB4\n"
+ "\tlsls r0, 1\n"
+ "\tstrh r0, [r1, 0x4]\n"
+ "\tldrb r0, [r1, 0xA]\n"
+ "\tmovs r1, 0x1\n"
+ "\tstr r1, [sp]\n"
+ "\tmovs r1, 0x2\n"
+ "\tstr r1, [sp, 0x4]\n"
+ "\tldr r1, _080F3D40 @ =sTextColor_Header\n"
+ "\tstr r1, [sp, 0x8]\n"
+ "\tstr r2, [sp, 0xC]\n"
+ "\tldr r1, _080F3D44 @ =TITLE_TEXT\n"
+ "\tstr r1, [sp, 0x10]\n"
+ "\tmovs r1, 0x1\n"
+ "\tmovs r2, 0x8\n"
+ "\tmovs r3, 0x29\n"
+ "\tbl AddTextPrinterParameterized4\n"
+ "\tldr r1, [r7]\n"
+ "\tmovs r0, 0x5\n"
+ "\tb _080F413C_setfield0_return0\n"
+ "\t.align 2, 0\n"
+ "_080F3D40: .4byte sTextColor_Header\n"
+ "_080F3D44: .4byte TITLE_TEXT\n"
+ "_080F3D48_case05:\n"
+ "\tldr r1, [r7]\n"
+ "\tldrh r0, [r1, 0x4]\n"
+ "\tadds r4, r0, 0\n"
+ "\tcmp r4, 0\n"
+ "\tbeq _080F3D58\n"
+ "_080F3D52_decfield4_return0:\n"
+ "\tsubs r0, 0x1\n"
+ "\tstrh r0, [r1, 0x4]\n"
+ "\tb _080F3D06_return0\n"
+ "_080F3D58:\n"
+ "\tbl DestroyCreditsWindow\n"
+ "\tldr r0, [r7]\n"
+ "\tmovs r1, 0x6\n"
+ "\tstrb r1, [r0]\n"
+ "\tldr r0, [r7]\n"
+ "\tstrh r4, [r0, 0x4]\n"
+ "\tstrh r4, [r0, 0x6]\n"
+ "\tb _080F3D06_return0\n"
+ "_080F3D6A_case06:\n"
+ "\tldr r2, [r7]\n"
+ "\tldrh r0, [r2, 0x4]\n"
+ "\tcmp r0, 0\n"
+ "\tbeq _080F3D7A\n"
+ "\tsubs r0, 0x1\n"
+ "\tstrh r0, [r2, 0x4]\n"
+ "\tldrb r0, [r2, 0x8]\n"
+ "\tb _080F4182_return\n"
+ "_080F3D7A:\n"
+ "\tldr r1, _080F3D94 @ =sCreditsScript\n"
+ "\tldrh r0, [r2, 0x6]\n"
+ "\tlsls r0, 2\n"
+ "\tadds r0, r1\n"
+ "\tldrb r0, [r0]\n"
+ "\tcmp r0, 0x5\n"
+ "\tbls _080F3D8A\n"
+ "\tb _080F3E94\n"
+ "_080F3D8A:\n"
+ "\tlsls r0, 2\n"
+ "\tldr r1, _080F3D98 @ =_080F3D9C\n"
+ "\tadds r0, r1\n"
+ "\tldr r0, [r0]\n"
+ "\tmov pc, r0\n"
+ "\t.align 2, 0\n"
+ "_080F3D94: .4byte sCreditsScript\n"
+ "_080F3D98: .4byte _080F3D9C\n"
+ "\t.align 2, 0\n"
+ "_080F3D9C:\n"
+ "\t.4byte _080F3DB4\n"
+ "\t.4byte _080F3DE0\n"
+ "\t.4byte _080F3E10\n"
+ "\t.4byte _080F3E30\n"
+ "\t.4byte _080F3E58\n"
+ "\t.4byte _080F3E8C\n"
+ "_080F3DB4:\n"
+ "\tmovs r0, 0x80\n"
+ "\tlsls r0, 8\n"
+ "\tmovs r1, 0\n"
+ "\tstr r1, [sp]\n"
+ "\tmovs r2, 0\n"
+ "\tmovs r3, 0x10\n"
+ "\tbl BeginNormalPaletteFade\n"
+ "\tldr r4, _080F3DDC @ =sCreditsMgr\n"
+ "\tldr r1, [r4]\n"
+ "\tmovs r0, 0x7\n"
+ "\tstrb r0, [r1]\n"
+ "\tldr r0, [r4]\n"
+ "\tldrb r0, [r0, 0xA]\n"
+ "\tmovs r1, 0\n"
+ "\tbl FillWindowPixelBuffer\n"
+ "\tldr r0, [r4]\n"
+ "\tldrb r0, [r0, 0x8]\n"
+ "\tb _080F4182_return\n"
+ "\t.align 2, 0\n"
+ "_080F3DDC: .4byte sCreditsMgr\n"
+ "_080F3DE0:\n"
+ "\tldr r2, _080F3E04 @ =sCreditsMgr\n"
+ "\tldr r1, [r2]\n"
+ "\tmovs r0, 0xA\n"
+ "\tstrb r0, [r1]\n"
+ "\tldr r2, [r2]\n"
+ "\tldr r1, _080F3E08 @ =sCreditsScript\n"
+ "\tldrh r0, [r2, 0x6]\n"
+ "\tlsls r0, 2\n"
+ "\tadds r0, r1\n"
+ "\tldrb r0, [r0, 0x1]\n"
+ "\tstrb r0, [r2, 0x9]\n"
+ "\tldr r2, _080F3E0C @ =0x3fffffff\n"
+ "\tmovs r0, 0x1\n"
+ "\tmovs r1, 0\n"
+ "\tbl FieldWeather_StartFadingOutCreditsMap\n"
+ "\tb _080F3E94\n"
+ "\t.align 2, 0\n"
+ "_080F3E04: .4byte sCreditsMgr\n"
+ "_080F3E08: .4byte sCreditsScript\n"
+ "_080F3E0C: .4byte 0x3fffffff\n"
+ "_080F3E10:\n"
+ "\tldr r2, _080F3E28 @ =sCreditsMgr\n"
+ "\tldr r1, [r2]\n"
+ "\tmovs r0, 0xC\n"
+ "\tstrb r0, [r1]\n"
+ "\tldr r2, [r2]\n"
+ "\tldr r1, _080F3E2C @ =sCreditsScript\n"
+ "\tldrh r0, [r2, 0x6]\n"
+ "\tlsls r0, 2\n"
+ "\tadds r0, r1\n"
+ "\tldrb r0, [r0, 0x1]\n"
+ "\tstrb r0, [r2, 0x9]\n"
+ "\tb _080F3E94\n"
+ "\t.align 2, 0\n"
+ "_080F3E28: .4byte sCreditsMgr\n"
+ "_080F3E2C: .4byte sCreditsScript\n"
+ "_080F3E30:\n"
+ "\tldr r2, _080F3E50 @ =sCreditsMgr\n"
+ "\tldr r1, [r2]\n"
+ "\tmovs r0, 0xD\n"
+ "\tstrb r0, [r1]\n"
+ "\tldr r2, [r2]\n"
+ "\tldr r1, _080F3E54 @ =sCreditsScript\n"
+ "\tldrh r0, [r2, 0x6]\n"
+ "\tlsls r0, 2\n"
+ "\tadds r0, r1\n"
+ "\tldrb r0, [r0, 0x1]\n"
+ "\tstrb r0, [r2, 0x9]\n"
+ "\tmovs r0, 0x1\n"
+ "\tmovs r1, 0\n"
+ "\tbl fade_screen\n"
+ "\tb _080F3E94\n"
+ "\t.align 2, 0\n"
+ "_080F3E50: .4byte sCreditsMgr\n"
+ "_080F3E54: .4byte sCreditsScript\n"
+ "_080F3E58:\n"
+ "\tldr r2, _080F3E84 @ =sCreditsMgr\n"
+ "\tldr r1, [r2]\n"
+ "\tmovs r3, 0\n"
+ "\tmovs r0, 0xF\n"
+ "\tstrb r0, [r1]\n"
+ "\tldr r2, [r2]\n"
+ "\tldr r1, _080F3E88 @ =sCreditsScript\n"
+ "\tldrh r0, [r2, 0x6]\n"
+ "\tlsls r0, 2\n"
+ "\tadds r0, r1\n"
+ "\tldrb r0, [r0, 0x1]\n"
+ "\tstrb r0, [r2, 0x9]\n"
+ "\tmovs r0, 0x1\n"
+ "\tnegs r0, r0\n"
+ "\tstr r3, [sp]\n"
+ "\tmovs r1, 0x4\n"
+ "\tmovs r2, 0\n"
+ "\tmovs r3, 0x10\n"
+ "\tbl BeginNormalPaletteFade\n"
+ "\tb _080F3E94\n"
+ "\t.align 2, 0\n"
+ "_080F3E84: .4byte sCreditsMgr\n"
+ "_080F3E88: .4byte sCreditsScript\n"
+ "_080F3E8C:\n"
+ "\tldr r0, _080F3EAC @ =sCreditsMgr\n"
+ "\tldr r1, [r0]\n"
+ "\tmovs r0, 0x11\n"
+ "\tstrb r0, [r1]\n"
+ "_080F3E94:\n"
+ "\tldr r0, _080F3EAC @ =sCreditsMgr\n"
+ "\tldr r1, [r0]\n"
+ "\tldr r2, _080F3EB0 @ =sCreditsScript\n"
+ "\tldrh r0, [r1, 0x6]\n"
+ "\tlsls r0, 2\n"
+ "\tadds r0, r2\n"
+ "\tldrh r0, [r0, 0x2]\n"
+ "\tstrh r0, [r1, 0x4]\n"
+ "\tldrh r0, [r1, 0x6]\n"
+ "\tadds r0, 0x1\n"
+ "\tstrh r0, [r1, 0x6]\n"
+ "\tb _080F3D06_return0\n"
+ "\t.align 2, 0\n"
+ "_080F3EAC: .4byte sCreditsMgr\n"
+ "_080F3EB0: .4byte sCreditsScript\n"
+ "_080F3EB4_case07:\n"
+ "\tldr r0, _080F3F14 @ =gPaletteFade\n"
+ "\tldrb r1, [r0, 0x7]\n"
+ "\tmovs r0, 0x80\n"
+ "\tands r0, r1\n"
+ "\tlsls r0, 24\n"
+ "\tlsrs r6, r0, 24\n"
+ "\tcmp r6, 0\n"
+ "\tbne _080F3FBA_returnfield8\n"
+ "\tadd r3, sp, 0x14\n"
+ "\tldr r5, _080F3F18 @ =sCreditsTexts\n"
+ "\tldr r4, _080F3F1C @ =sCreditsScript\n"
+ "\tldr r2, [r7]\n"
+ "\tldrh r0, [r2, 0x6]\n"
+ "\tlsls r0, 2\n"
+ "\tadds r0, r4\n"
+ "\tldrb r1, [r0, 0x1]\n"
+ "\tlsls r0, r1, 1\n"
+ "\tadds r0, r1\n"
+ "\tlsls r0, 2\n"
+ "\tadds r0, r5\n"
+ "\tldrb r0, [r0, 0x8]\n"
+ "\tstrh r0, [r3]\n"
+ "\tldrb r0, [r2, 0xA]\n"
+ "\tstr r6, [sp]\n"
+ "\tstr r6, [sp, 0x4]\n"
+ "\tldr r1, _080F3F20 @ =sTextColor_Header\n"
+ "\tstr r1, [sp, 0x8]\n"
+ "\tmovs r1, 0x1\n"
+ "\tnegs r1, r1\n"
+ "\tstr r1, [sp, 0xC]\n"
+ "\tldrh r1, [r2, 0x6]\n"
+ "\tlsls r1, 2\n"
+ "\tadds r1, r4\n"
+ "\tldrb r2, [r1, 0x1]\n"
+ "\tlsls r1, r2, 1\n"
+ "\tadds r1, r2\n"
+ "\tlsls r1, 2\n"
+ "\tadds r1, r5\n"
+ "\tldr r1, [r1]\n"
+ "\tstr r1, [sp, 0x10]\n"
+ "\tmovs r1, 0x1\n"
+ "\tmovs r2, 0x2\n"
+ "\tmovs r3, 0x6\n"
+ "\tbl AddTextPrinterParameterized4\n"
+ "\tldr r1, [r7]\n"
+ "\tmovs r0, 0x8\n"
+ "\tb _080F3FB8_setfield0_returnfield8\n"
+ "\t.align 2, 0\n"
+ "_080F3F14: .4byte gPaletteFade\n"
+ "_080F3F18: .4byte sCreditsTexts\n"
+ "_080F3F1C: .4byte sCreditsScript\n"
+ "_080F3F20: .4byte sTextColor_Header\n"
+ "_080F3F24_case08:\n"
+ "\tadd r4, sp, 0x14\n"
+ "\tldr r3, _080F3F78 @ =sCreditsTexts\n"
+ "\tldr r5, _080F3F7C @ =sCreditsScript\n"
+ "\tldr r2, [r7]\n"
+ "\tldrh r0, [r2, 0x6]\n"
+ "\tlsls r0, 2\n"
+ "\tadds r0, r5\n"
+ "\tldrb r1, [r0, 0x1]\n"
+ "\tlsls r0, r1, 1\n"
+ "\tadds r0, r1\n"
+ "\tlsls r0, 2\n"
+ "\tadds r0, r3\n"
+ "\tldrb r0, [r0, 0x8]\n"
+ "\tmovs r1, 0\n"
+ "\tstrh r0, [r4]\n"
+ "\tldrb r0, [r2, 0xA]\n"
+ "\tstr r1, [sp]\n"
+ "\tstr r1, [sp, 0x4]\n"
+ "\tldr r1, _080F3F80 @ =sTextColor_Regular\n"
+ "\tstr r1, [sp, 0x8]\n"
+ "\tmovs r1, 0x1\n"
+ "\tnegs r1, r1\n"
+ "\tstr r1, [sp, 0xC]\n"
+ "\tldrh r1, [r2, 0x6]\n"
+ "\tlsls r1, 2\n"
+ "\tadds r1, r5\n"
+ "\tldrb r2, [r1, 0x1]\n"
+ "\tlsls r1, r2, 1\n"
+ "\tadds r1, r2\n"
+ "\tlsls r1, 2\n"
+ "\tadds r3, 0x4\n"
+ "\tadds r1, r3\n"
+ "\tldr r1, [r1]\n"
+ "\tstr r1, [sp, 0x10]\n"
+ "\tmovs r1, 0x2\n"
+ "\tmovs r2, 0x8\n"
+ "\tmovs r3, 0x6\n"
+ "\tbl AddTextPrinterParameterized4\n"
+ "\tldr r1, [r7]\n"
+ "\tmovs r0, 0x9\n"
+ "\tb _080F3FB8_setfield0_returnfield8\n"
+ "\t.align 2, 0\n"
+ "_080F3F78: .4byte sCreditsTexts\n"
+ "_080F3F7C: .4byte sCreditsScript\n"
+ "_080F3F80: .4byte sTextColor_Regular\n"
+ "_080F3F84_case09:\n"
+ "\tldr r0, [r7]\n"
+ "\tldrb r0, [r0, 0xA]\n"
+ "\tmovs r1, 0x2\n"
+ "\tbl CopyWindowToVram\n"
+ "\tldr r1, [r7]\n"
+ "\tldr r2, _080F3FC0 @ =sCreditsScript\n"
+ "\tldrh r0, [r1, 0x6]\n"
+ "\tlsls r0, 2\n"
+ "\tadds r0, r2\n"
+ "\tldrh r0, [r0, 0x2]\n"
+ "\tmovs r2, 0\n"
+ "\tstrh r0, [r1, 0x4]\n"
+ "\tldrh r0, [r1, 0x6]\n"
+ "\tadds r0, 0x1\n"
+ "\tstrh r0, [r1, 0x6]\n"
+ "\tmovs r0, 0x80\n"
+ "\tlsls r0, 8\n"
+ "\tstr r2, [sp]\n"
+ "\tmovs r1, 0\n"
+ "\tmovs r2, 0x10\n"
+ "\tmovs r3, 0\n"
+ "\tbl BeginNormalPaletteFade\n"
+ "\tldr r1, [r7]\n"
+ "\tmovs r0, 0x6\n"
+ "_080F3FB8_setfield0_returnfield8:\n"
+ "\tstrb r0, [r1]\n"
+ "_080F3FBA_returnfield8:\n"
+ "\tldr r0, [r7]\n"
+ "\tldrb r0, [r0, 0x8]\n"
+ "\tb _080F4182_return\n"
+ "\t.align 2, 0\n"
+ "_080F3FC0: .4byte sCreditsScript\n"
+ "_080F3FC4_case0A:\n"
+ "\tldr r0, _080F3FE8 @ =gPaletteFade\n"
+ "\tldrb r1, [r0, 0x7]\n"
+ "\tmovs r0, 0x80\n"
+ "\tands r0, r1\n"
+ "\tlsls r0, 24\n"
+ "\tlsrs r4, r0, 24\n"
+ "\tcmp r4, 0\n"
+ "\tbeq _080F3FD6\n"
+ "\tb _080F3D06_return0\n"
+ "_080F3FD6:\n"
+ "\tbl DestroyCreditsWindow\n"
+ "\tldr r1, _080F3FEC @ =sCreditsMgr\n"
+ "\tldr r0, [r1]\n"
+ "\tstrb r4, [r0, 0x1]\n"
+ "\tldr r1, [r1]\n"
+ "\tmovs r0, 0xB\n"
+ "\tb _080F413C_setfield0_return0\n"
+ "\t.align 2, 0\n"
+ "_080F3FE8: .4byte gPaletteFade\n"
+ "_080F3FEC: .4byte sCreditsMgr\n"
+ "_080F3FF0_case0B:\n"
+ "\tldr r0, [r7]\n"
+ "\tldrb r0, [r0, 0x9]\n"
+ "\tbl DoOverworldMapScrollScene\n"
+ "\tcmp r0, 0\n"
+ "\tbne _080F3FFE\n"
+ "\tb _080F3D06_return0\n"
+ "_080F3FFE:\n"
+ "\tldr r1, [r7]\n"
+ "\tmovs r0, 0x1\n"
+ "\tstrb r0, [r1, 0x8]\n"
+ "\tldr r1, [r7]\n"
+ "\tmovs r0, 0x6\n"
+ "\tb _080F413C_setfield0_return0\n"
+ "_080F400A_case0C:\n"
+ "\tldr r0, _080F4050 @ =gPaletteFade\n"
+ "\tldrb r1, [r0, 0x7]\n"
+ "\tmovs r0, 0x80\n"
+ "\tands r0, r1\n"
+ "\tlsls r0, 24\n"
+ "\tlsrs r4, r0, 24\n"
+ "\tcmp r4, 0\n"
+ "\tbeq _080F401C\n"
+ "\tb _080F3D06_return0\n"
+ "_080F401C:\n"
+ "\tbl DestroyCreditsWindow\n"
+ "\tldr r1, _080F4054 @ =sCreditsMgr\n"
+ "\tldr r0, [r1]\n"
+ "\tstrb r4, [r0, 0x1]\n"
+ "\tadds r4, r1, 0\n"
+ "_080F4028:\n"
+ "\tldr r0, [r4]\n"
+ "\tldrb r0, [r0, 0x9]\n"
+ "\tbl DoOverworldMapScrollScene\n"
+ "\tcmp r0, 0\n"
+ "\tbeq _080F4028\n"
+ "\tldr r0, _080F4054 @ =sCreditsMgr\n"
+ "\tldr r0, [r0]\n"
+ "\tldrb r0, [r0, 0x9]\n"
+ "\tcmp r0, 0x6\n"
+ "\tbeq _080F4058\n"
+ "\tcmp r0, 0x6\n"
+ "\tble _080F404A\n"
+ "\tcmp r0, 0x9\n"
+ "\tbeq _080F405E\n"
+ "\tcmp r0, 0xC\n"
+ "\tbeq _080F4064\n"
+ "_080F404A:\n"
+ "\tadd r1, sp, 0x14\n"
+ "\tmovs r0, 0x1\n"
+ "\tb _080F4068\n"
+ "\t.align 2, 0\n"
+ "_080F4050: .4byte gPaletteFade\n"
+ "_080F4054: .4byte sCreditsMgr\n"
+ "_080F4058:\n"
+ "\tadd r1, sp, 0x14\n"
+ "\tmovs r0, 0x2\n"
+ "\tb _080F4068\n"
+ "_080F405E:\n"
+ "\tadd r1, sp, 0x14\n"
+ "\tmovs r0, 0x3\n"
+ "\tb _080F4068\n"
+ "_080F4064:\n"
+ "\tadd r1, sp, 0x14\n"
+ "\tmovs r0, 0x4\n"
+ "_080F4068:\n"
+ "\tstrh r0, [r1]\n"
+ "\tadd r0, sp, 0x14\n"
+ "\tldrb r0, [r0]\n"
+ "\tbl LoadPlayerOrRivalSprite\n"
+ "\tldr r2, _080F4080 @ =sCreditsMgr\n"
+ "\tldr r1, [r2]\n"
+ "\tmovs r0, 0x1\n"
+ "\tstrb r0, [r1, 0x8]\n"
+ "\tldr r1, [r2]\n"
+ "\tmovs r0, 0x6\n"
+ "\tb _080F413C_setfield0_return0\n"
+ "\t.align 2, 0\n"
+ "_080F4080: .4byte sCreditsMgr\n"
+ "_080F4084_case0D:\n"
+ "\tldr r0, _080F40B0 @ =gPaletteFade\n"
+ "\tldrb r1, [r0, 0x7]\n"
+ "\tmovs r0, 0x80\n"
+ "\tands r0, r1\n"
+ "\tlsls r0, 24\n"
+ "\tlsrs r4, r0, 24\n"
+ "\tcmp r4, 0\n"
+ "\tbeq _080F4096\n"
+ "\tb _080F3D06_return0\n"
+ "_080F4096:\n"
+ "\tbl DestroyPlayerOrRivalSprite\n"
+ "\tbl DestroyCreditsWindow\n"
+ "\tldr r1, _080F40B4 @ =sCreditsMgr\n"
+ "\tldr r0, [r1]\n"
+ "\tstrb r4, [r0, 0x1]\n"
+ "\tldr r0, [r1]\n"
+ "\tstrb r4, [r0, 0x8]\n"
+ "\tldr r1, [r1]\n"
+ "\tmovs r0, 0xE\n"
+ "\tb _080F413C_setfield0_return0\n"
+ "\t.align 2, 0\n"
+ "_080F40B0: .4byte gPaletteFade\n"
+ "_080F40B4: .4byte sCreditsMgr\n"
+ "_080F40B8_case0E:\n"
+ "\tbl DoCreditsMonScene\n"
+ "\tcmp r0, 0\n"
+ "\tbne _080F40C2\n"
+ "\tb _080F3D06_return0\n"
+ "_080F40C2:\n"
+ "\tldr r0, _080F40CC @ =sCreditsMgr\n"
+ "\tldr r1, [r0]\n"
+ "\tmovs r0, 0x6\n"
+ "\tb _080F413C_setfield0_return0\n"
+ "\t.align 2, 0\n"
+ "_080F40CC: .4byte sCreditsMgr\n"
+ "_080F40D0_case0F:\n"
+ "\tldr r0, _080F40F8 @ =gPaletteFade\n"
+ "\tldrb r1, [r0, 0x7]\n"
+ "\tmovs r0, 0x80\n"
+ "\tands r0, r1\n"
+ "\tlsls r0, 24\n"
+ "\tlsrs r4, r0, 24\n"
+ "\tcmp r4, 0\n"
+ "\tbeq _080F40E2\n"
+ "\tb _080F3D06_return0\n"
+ "_080F40E2:\n"
+ "\tbl DestroyCreditsWindow\n"
+ "\tldr r1, _080F40FC @ =sCreditsMgr\n"
+ "\tldr r0, [r1]\n"
+ "\tstrb r4, [r0, 0x1]\n"
+ "\tldr r0, [r1]\n"
+ "\tstrb r4, [r0, 0x8]\n"
+ "\tldr r1, [r1]\n"
+ "\tmovs r0, 0x10\n"
+ "\tb _080F413C_setfield0_return0\n"
+ "\t.align 2, 0\n"
+ "_080F40F8: .4byte gPaletteFade\n"
+ "_080F40FC: .4byte sCreditsMgr\n"
+ "_080F4100_case10:\n"
+ "\tbl DoCopyrightOrTheEndGfxScene\n"
+ "\tcmp r0, 0\n"
+ "\tbne _080F410A\n"
+ "\tb _080F3D06_return0\n"
+ "_080F410A:\n"
+ "\tldr r0, _080F4114 @ =sCreditsMgr\n"
+ "\tldr r1, [r0]\n"
+ "\tmovs r0, 0x6\n"
+ "\tb _080F413C_setfield0_return0\n"
+ "\t.align 2, 0\n"
+ "_080F4114: .4byte sCreditsMgr\n"
+ "_080F4118_case11:\n"
+ "\tldr r0, _080F4140 @ =gMain\n"
+ "\tldrh r1, [r0, 0x2E]\n"
+ "\tmovs r0, 0x1\n"
+ "\tands r0, r1\n"
+ "\tcmp r0, 0\n"
+ "\tbeq _080F414C\n"
+ "\tmovs r0, 0x1\n"
+ "\tnegs r0, r0\n"
+ "\tldr r1, _080F4144 @ =0x00007fff\n"
+ "\tstr r1, [sp]\n"
+ "\tmovs r1, 0\n"
+ "\tmovs r2, 0\n"
+ "\tmovs r3, 0x10\n"
+ "\tbl BeginNormalPaletteFade\n"
+ "\tldr r0, _080F4148 @ =sCreditsMgr\n"
+ "\tldr r1, [r0]\n"
+ "\tmovs r0, 0x12\n"
+ "_080F413C_setfield0_return0:\n"
+ "\tstrb r0, [r1]\n"
+ "\tb _080F3D06_return0\n"
+ "\t.align 2, 0\n"
+ "_080F4140: .4byte gMain\n"
+ "_080F4144: .4byte 0x00007fff\n"
+ "_080F4148: .4byte sCreditsMgr\n"
+ "_080F414C:\n"
+ "\tldr r1, [r7]\n"
+ "\tldrh r0, [r1, 0x4]\n"
+ "\tcmp r0, 0\n"
+ "\tbeq _080F4156\n"
+ "\tb _080F3D52_decfield4_return0\n"
+ "_080F4156:\n"
+ "\tmovs r0, 0x12\n"
+ "\tstrb r0, [r1]\n"
+ "\tsubs r0, 0x13\n"
+ "\tldr r1, _080F416C @ =0x00007fff\n"
+ "\tstr r1, [sp]\n"
+ "\tmovs r1, 0\n"
+ "\tmovs r2, 0\n"
+ "\tmovs r3, 0x10\n"
+ "\tbl BeginNormalPaletteFade\n"
+ "\tb _080F3D06_return0\n"
+ "\t.align 2, 0\n"
+ "_080F416C: .4byte 0x00007fff\n"
+ "_080F4170_case12:\n"
+ "\tldr r0, _080F418C @ =gPaletteFade\n"
+ "\tldrb r1, [r0, 0x7]\n"
+ "\tmovs r0, 0x80\n"
+ "\tands r0, r1\n"
+ "\tcmp r0, 0\n"
+ "\tbne _080F4180_default_return2\n"
+ "\tbl DestroyCreditsWindow\n"
+ "_080F4180_default_return2:\n"
+ "\tmovs r0, 0x2\n"
+ "_080F4182_return:\n"
+ "\tadd sp, 0x24\n"
+ "\tpop {r4-r7}\n"
+ "\tpop {r1}\n"
+ "\tbx r1\n"
+ "\t.align 2, 0\n"
+ "_080F418C: .4byte gPaletteFade");
+}
+#endif //NONMATCHING
+
+static void VBlankCB(void)
+{
+ LoadOam();
+ ProcessSpriteCopyRequests();
+ TransferPlttBuffer();
+}
+
+static void LoadCreditsMonPic(u8 whichMon)
+{
+ switch (whichMon)
+ {
+ case CREDITSMON_CHARIZARD:
+ InitWindows(sWindowTemplates_Charizard);
+ FillWindowPixelBuffer(0, PIXEL_FILL(0));
+ LoadMonPicInWindow(SPECIES_CHARIZARD, 8, 0, TRUE, 10, 0);
+ CopyToWindowPixelBuffer(1, (const void *)sWindow1Map_Charizard, 0, 0);
+ CopyToWindowPixelBuffer(2, (const void *)sWindow2Map_Charizard, 0, 0);
+ break;
+ case CREDITSMON_VENUSAUR:
+ InitWindows(sWindowTemplates_Venusaur);
+ FillWindowPixelBuffer(0, PIXEL_FILL(0));
+ LoadMonPicInWindow(SPECIES_VENUSAUR, 8, 0, TRUE, 10, 0);
+ CopyToWindowPixelBuffer(1, (const void *)sWindow1Map_Venusaur, 0, 0);
+ CopyToWindowPixelBuffer(2, (const void *)sWindow2Map_Venusaur, 0, 0);
+ break;
+ case CREDITSMON_BLASTOISE:
+ InitWindows(sWindowTemplates_Blastoise);
+ FillWindowPixelBuffer(0, PIXEL_FILL(0));
+ LoadMonPicInWindow(SPECIES_BLASTOISE, 8, 0, TRUE, 10, 0);
+ CopyToWindowPixelBuffer(1, (const void *)sWindow1Map_Blastoise, 0, 0);
+ CopyToWindowPixelBuffer(2, (const void *)sWindow2Map_Blastoise, 0, 0);
+ break;
+ case CREDITSMON_PIKACHU:
+ InitWindows(sWindowTemplates_Pikachu);
+ FillWindowPixelBuffer(0, PIXEL_FILL(0));
+ LoadMonPicInWindow(SPECIES_PIKACHU, 8, 0, TRUE, 10, 0);
+ CopyToWindowPixelBuffer(1, (const void *)sWindow1Map_Pikachu, 0, 0);
+ CopyToWindowPixelBuffer(2, (const void *)sWindow2Map_Pikachu, 0, 0);
+ break;
+ }
+ CopyWindowToVram(0, 2);
+ CopyWindowToVram(1, 2);
+ CopyWindowToVram(2, 2);
+}
+
+static u16 GetCreditsMonSpecies(u8 whichMon)
+{
+ switch (whichMon)
+ {
+ case CREDITSMON_CHARIZARD:
+ return SPECIES_CHARIZARD;
+ case CREDITSMON_VENUSAUR:
+ return SPECIES_VENUSAUR;
+ case CREDITSMON_BLASTOISE:
+ return SPECIES_BLASTOISE;
+ case CREDITSMON_PIKACHU:
+ return SPECIES_PIKACHU;
+ default:
+ return SPECIES_NONE;
+ }
+}
+
+static bool32 DoCreditsMonScene(void)
+{
+ switch (sCreditsMgr->subseqno)
+ {
+ case 0:
+ SetVBlankCallback(NULL);
+ SetHBlankCallback(NULL);
+ ClearGpuRegBits(REG_OFFSET_DISPCNT, DISPCNT_WIN0_ON | DISPCNT_WIN1_ON);
+ SetGpuReg(REG_OFFSET_WININ, 0);
+ SetGpuReg(REG_OFFSET_WINOUT, 0);
+ SetGpuReg(REG_OFFSET_BLDCNT, 0);
+ SetGpuReg(REG_OFFSET_BLDALPHA, 0);
+ SetGpuReg(REG_OFFSET_BLDY, 0);
+ ResetPaletteFade();
+ ResetSpriteData();
+ ResetTasks();
+ ResetBgsAndClearDma3BusyFlags(1);
+ InitBgsFromTemplates(1, sBgTemplates_MonSceneOrTheEnd, NELEMS(sBgTemplates_MonSceneOrTheEnd));
+ SetBgTilemapBuffer(0, Alloc(BG_SCREEN_SIZE));
+ ChangeBgX(0, 0, 0);
+ ChangeBgY(0, 0, 0);
+ ChangeBgX(1, 0, 0);
+ ChangeBgY(1, 0, 0);
+ sCreditsMgr->creditsMonTimer = 0;
+ sCreditsMgr->unk_0E = 0;
+ SetBgAffine(2, 0x8000, 0x8000, 0x78, 0x50, sCreditsMgr->creditsMonTimer, sCreditsMgr->creditsMonTimer, 0);
+ DecompressAndLoadBgGfxUsingHeap(1, gCreditsPokeballBgGfxTiles, 0x2000, 0, 0);
+ DecompressAndLoadBgGfxUsingHeap(2, sAffineCircleGfx, 0x2000, 0, 0);
+ DecompressAndLoadBgGfxUsingHeap(1, gCreditsPokeballBgGfxMap, 0x500, 0, 1);
+ DecompressAndLoadBgGfxUsingHeap(2, sAffineCircleMap, 0x400, 0, 1);
+ LoadPalette(gCreditsMonBackdropPals[sCreditsMgr->whichMon], 0, 0x20);
+ LoadPalette(sPalette_OneBlackThenAllWhite, 0xF0, 0x20);
+ LoadCreditsMonPic(sCreditsMgr->whichMon);
+ SetVBlankCallback(VBlankCB);
+ EnableInterrupts(INTR_FLAG_VBLANK);
+ sCreditsMgr->subseqno++;
+ break;
+ case 1:
+ FillBgTilemapBufferRect(0, 0, 0, 0, 32, 32, PIXEL_FILL(1));
+ PutWindowTilemap(0);
+ CopyBgTilemapBufferToVram(2);
+ CopyBgTilemapBufferToVram(1);
+ CopyBgTilemapBufferToVram(0);
+ sCreditsMgr->subseqno++;
+ break;
+ case 2:
+ ShowBg(2);
+ ShowBg(0);
+ BeginNormalPaletteFade(0xFFFFFFFF, 0, 16, 0, RGB_BLACK);
+ sCreditsMgr->creditsMonTimer = 40;
+ sCreditsMgr->subseqno++;
+ break;
+ case 3:
+ if (sCreditsMgr->creditsMonTimer != 0)
+ sCreditsMgr->creditsMonTimer--;
+ else
+ sCreditsMgr->subseqno++;
+ break;
+ case 4:
+ if (!gPaletteFade.active)
+ {
+ sCreditsMgr->creditsMonTimer = 8;
+ sCreditsMgr->unk_0E = 1;
+ sCreditsMgr->subseqno++;
+ }
+ break;
+ case 5:
+ if (sCreditsMgr->creditsMonTimer != 0)
+ sCreditsMgr->creditsMonTimer--;
+ else
+ {
+ if (sCreditsMgr->unk_0E < 3)
+ {
+ PutWindowTilemap(sCreditsMgr->unk_0E);
+ CopyBgTilemapBufferToVram(0);
+ sCreditsMgr->creditsMonTimer = 4;
+ sCreditsMgr->unk_0E++;
+ }
+ else
+ sCreditsMgr->subseqno++;
+ }
+ break;
+ case 6:
+ if (sCreditsMgr->creditsMonTimer < 256)
+ {
+ sCreditsMgr->creditsMonTimer += 16;
+ SetBgAffine(2, 0x8000, 0x8000, 0x78, 0x50, sCreditsMgr->creditsMonTimer, sCreditsMgr->creditsMonTimer, 0);
+ }
+ else
+ {
+ SetBgAffine(2, 0x8000, 0x8000, 0x78, 0x50, 0x100, 0x100, 0);
+ sCreditsMgr->creditsMonTimer = 32;
+ sCreditsMgr->subseqno++;
+ }
+ break;
+ case 7:
+ if (sCreditsMgr->creditsMonTimer != 0)
+ sCreditsMgr->creditsMonTimer--;
+ else
+ {
+ HideBg(2);
+ ShowBg(1);
+ PlayCry2(GetCreditsMonSpecies(sCreditsMgr->whichMon), 0, 125, 10);
+ sCreditsMgr->creditsMonTimer = 128;
+ sCreditsMgr->subseqno++;
+ }
+ break;
+ case 8:
+ if (sCreditsMgr->creditsMonTimer != 0)
+ sCreditsMgr->creditsMonTimer--;
+ else
+ {
+ BeginNormalPaletteFade(0xFFFFFFFF, 0, 0, 16, RGB_BLACK);
+ sCreditsMgr->subseqno++;
+ }
+ break;
+ case 9:
+ if (!gPaletteFade.active)
+ {
+ FreeAllWindowBuffers();
+ Free(GetBgTilemapBuffer(0));
+ sCreditsMgr->subseqno = 0;
+ return TRUE;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+static bool32 DoCopyrightOrTheEndGfxScene(void)
+{
+ switch (sCreditsMgr->subseqno)
+ {
+ case 0:
+ SetVBlankCallback(NULL);
+ SetHBlankCallback(NULL);
+ ClearGpuRegBits(REG_OFFSET_DISPCNT, DISPCNT_WIN0_ON | DISPCNT_WIN1_ON);
+ SetGpuReg(REG_OFFSET_WININ, 0);
+ SetGpuReg(REG_OFFSET_WINOUT, 0);
+ SetGpuReg(REG_OFFSET_BLDCNT, 0);
+ SetGpuReg(REG_OFFSET_BLDALPHA, 0);
+ SetGpuReg(REG_OFFSET_BLDY, 0);
+ ResetPaletteFade();
+ ResetSpriteData();
+ ResetTasks();
+ ResetBgsAndClearDma3BusyFlags(1);
+ InitBgsFromTemplates(0, sBgTemplates_MonSceneOrTheEnd, 1);
+ ChangeBgX(0, 0, 0);
+ ChangeBgY(0, 0, 0);
+ DecompressAndLoadBgGfxUsingHeap(0, sCopyrightOrTheEndGfxHeaders[sCreditsMgr->whichMon].tiles, 0x2000, 0, 0);
+ DecompressAndLoadBgGfxUsingHeap(0, sCopyrightOrTheEndGfxHeaders[sCreditsMgr->whichMon].map, 0x800, 0, 1);
+ LoadPalette(sCopyrightOrTheEndGfxHeaders[sCreditsMgr->whichMon].palette, 0x00, 0x200);
+ SetVBlankCallback(VBlankCB);
+ EnableInterrupts(INTR_FLAG_VBLANK);
+ sCreditsMgr->subseqno++;
+ break;
+ case 1:
+ CopyBgTilemapBufferToVram(0);
+ sCreditsMgr->subseqno++;
+ break;
+ case 2:
+ ShowBg(0);
+ if (sCreditsMgr->whichMon != 0)
+ BeginNormalPaletteFade(0xFFFFFFFF, 0, 0, 0, RGB_BLACK);
+ else
+ BeginNormalPaletteFade(0xFFFFFFFF, 0, 16, 0, RGB_BLACK);
+ sCreditsMgr->subseqno++;
+ break;
+ case 3:
+ if (!gPaletteFade.active)
+ {
+ sCreditsMgr->subseqno = 0;
+ return TRUE;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+static void Task_MovePlayerAndGroundSprites(u8 taskId)
+{
+ struct CreditsTaskData * data = (void *)gTasks[taskId].data;
+ switch (data->spriteMoveCmd)
+ {
+ case 0:
+ break;
+ case 1:
+ if (gSprites[data->playerSpriteId].pos1.x != 0xD0)
+ {
+ gSprites[data->playerSpriteId].pos1.x--;
+ gSprites[data->groundSpriteId].pos1.x--;
+ }
+ else
+ {
+ data->spriteMoveCmd = 0;
+ }
+ break;
+ case 2:
+ if (sCreditsMgr->unk_1D & 1)
+ {
+ if (gSprites[data->playerSpriteId].pos1.y != 0x50)
+ {
+ gSprites[data->playerSpriteId].pos1.y--;
+ gSprites[data->groundSpriteId].pos1.y--;
+ }
+ else
+ {
+ data->spriteMoveCmd = 0;
+ }
+ }
+ break;
+ case 3:
+ if (sCreditsMgr->mainseqno == 15)
+ {
+ gSprites[data->playerSpriteId].pos1.x--;
+ gSprites[data->groundSpriteId].pos1.x--;
+ }
+ break;
+ }
+}
+
+static void DestroyPlayerOrRivalSprite(void)
+{
+ if (sCreditsMgr->taskId != 0xFF)
+ {
+ struct CreditsTaskData * data = (void *)gTasks[sCreditsMgr->taskId].data;
+ FreeSpriteTilesByTag(data->playerTilesTag);
+ DestroySprite(&gSprites[data->playerSpriteId]);
+ FreeSpriteTilesByTag(data->groundTilesTag);
+ DestroySprite(&gSprites[data->groundSpriteId]);
+ DestroyTask(sCreditsMgr->taskId);
+ sCreditsMgr->taskId = 0xFF;
+ }
+}
+
+static void LoadPlayerOrRivalSprite(u8 whichScene)
+{
+ u8 taskId;
+ struct CreditsTaskData * data;
+ s32 x, y;
+ struct SpriteTemplate sprTemplate;
+ struct CompressedSpriteSheet sprSheet;
+
+ if (sCreditsMgr->taskId == 0xFF)
+ {
+ taskId = CreateTask(Task_MovePlayerAndGroundSprites, 0);
+ data = (void *)gTasks[taskId].data;
+ sCreditsMgr->taskId = taskId;
+ switch (sPlayerRivalSpriteParams[whichScene][2])
+ {
+ default:
+ case 0:
+ x = 0xd0;
+ y = 0x50;
+ break;
+ case 1:
+ x = 0x110;
+ y = 0x50;
+ break;
+ case 2:
+ x = 0xd0;
+ y = 0xa0;
+ break;
+ }
+ data->spriteMoveCmd = sPlayerRivalSpriteParams[whichScene][2];
+ data->playerTilesTag = 0x2000;
+ data->field_04 = 0xFFFF;
+ switch (sPlayerRivalSpriteParams[whichScene][0])
+ {
+ case 0:
+ // Player
+ if (gSaveBlock2Ptr->playerGender == MALE)
+ {
+ sprSheet.data = sMalePlayerSpriteGfx;
+ sprSheet.size = 0x3000;
+ sprSheet.tag = data->playerTilesTag;
+ LoadCompressedSpriteSheet(&sprSheet);
+ LoadPalette(sMalePlayerSpritePal, 0x1F0, 0x20);
+ }
+ else
+ {
+ sprSheet.data = sFemalePlayerSpriteGfx;
+ sprSheet.size = 0x3000;
+ sprSheet.tag = data->playerTilesTag;
+ LoadCompressedSpriteSheet(&sprSheet);
+ LoadPalette(sFemalePlayerSpritePal, 0x1F0, 0x20);
+ }
+ break;
+ case 1:
+ // Rival
+ sprSheet.data = sRivalSpriteGfx;
+ sprSheet.size = 0x3000;
+ sprSheet.tag = data->playerTilesTag;
+ LoadCompressedSpriteSheet(&sprSheet);
+ LoadPalette(sRivalSpritePal, 0x1F0, 0x20);
+ break;
+ }
+ sprTemplate = sPlayerOrRivalSpriteTemplate;
+ sprTemplate.tileTag = data->playerTilesTag;
+ data->playerSpriteId = CreateSprite(&sprTemplate, x, y, 0);
+ gSprites[data->playerSpriteId].oam.paletteNum = 0xF;
+ gSprites[data->playerSpriteId].subpriority = 0;
+
+ data->groundTilesTag = 0x2001;
+ data->field_0A = 0xFFFF;
+ switch (sPlayerRivalSpriteParams[whichScene][1])
+ {
+ case 0:
+ sprSheet.data = sGroundSpriteGfx_Grass;
+ sprSheet.size = 0x3000;
+ sprSheet.tag = data->groundTilesTag;
+ LoadCompressedSpriteSheet(&sprSheet);
+ LoadPalette(sGroundSpritePal_Grass, 0x1E0, 0x20);
+ sprTemplate = sGroundSpriteTemplate_Running;
+ break;
+ case 1:
+ sprSheet.data = sGroundSpriteGfx_Grass;
+ sprSheet.size = 0x3000;
+ sprSheet.tag = data->groundTilesTag;
+ LoadCompressedSpriteSheet(&sprSheet);
+ LoadPalette(sGroundSpritePal_Grass, 0x1E0, 0x20);
+ sprTemplate = sGroundSpriteTemplate_Static;
+ break;
+ case 2:
+ sprSheet.data = sGroundSpriteGfx_Dirt;
+ sprSheet.size = 0x3000;
+ sprSheet.tag = data->groundTilesTag;
+ LoadCompressedSpriteSheet(&sprSheet);
+ LoadPalette(sGroundSpritePal_Dirt, 0x1E0, 0x20);
+ sprTemplate = sGroundSpriteTemplate_Running;
+ break;
+ case 3:
+ sprSheet.data = sGroundSpriteGfx_City;
+ sprSheet.size = 0x3000;
+ sprSheet.tag = data->groundTilesTag;
+ LoadCompressedSpriteSheet(&sprSheet);
+ LoadPalette(sGroundSpritePal_City, 0x1E0, 0x20);
+ sprTemplate = sGroundSpriteTemplate_Running;
+ break;
+ }
+ sprTemplate.tileTag = data->groundTilesTag;
+ data->groundSpriteId = CreateSprite(&sprTemplate, x, y + 0x26, 0);
+ gSprites[data->groundSpriteId].oam.paletteNum = 0xE;
+ gSprites[data->groundSpriteId].subpriority = 1;
+ }
+}
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/daycare.c b/src/daycare.c
index abc3b23ca..c082b4298 100644
--- a/src/daycare.c
+++ b/src/daycare.c
@@ -63,7 +63,7 @@ struct EggHatchData
u8 unused_9;
u8 unused_A;
u16 species;
- struct TextColor textColor;
+ u8 textColor[3];
};
extern const u8 gText_MaleSymbol4[];
@@ -168,215 +168,215 @@ static const u8 sEggHatchTiles[] = INCBIN_U8("graphics/misc/egg_hatch.4bpp");
static const u8 sEggShardTiles[] = INCBIN_U8("graphics/misc/egg_shard.4bpp");
static const struct OamData sOamData_EggHatch =
- {
- .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 = 1,
- .paletteNum = 0,
- .affineParam = 0,
- };
+{
+ .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 = 1,
+ .paletteNum = 0,
+ .affineParam = 0,
+};
static const union AnimCmd sSpriteAnim_EggHatch0[] =
- {
- ANIMCMD_FRAME(0, 5),
- ANIMCMD_END
- };
+{
+ ANIMCMD_FRAME(0, 5),
+ ANIMCMD_END
+};
static const union AnimCmd sSpriteAnim_EggHatch1[] =
- {
- ANIMCMD_FRAME(16, 5),
- ANIMCMD_END
- };
+{
+ ANIMCMD_FRAME(16, 5),
+ ANIMCMD_END
+};
static const union AnimCmd sSpriteAnim_EggHatch2[] =
- {
- ANIMCMD_FRAME(32, 5),
- ANIMCMD_END
- };
+{
+ ANIMCMD_FRAME(32, 5),
+ ANIMCMD_END
+};
static const union AnimCmd sSpriteAnim_EggHatch3[] =
- {
- ANIMCMD_FRAME(48, 5),
- ANIMCMD_END
- };
+{
+ ANIMCMD_FRAME(48, 5),
+ ANIMCMD_END
+};
static const union AnimCmd *const sSpriteAnimTable_EggHatch[] =
- {
- sSpriteAnim_EggHatch0,
- sSpriteAnim_EggHatch1,
- sSpriteAnim_EggHatch2,
- sSpriteAnim_EggHatch3,
- };
+{
+ sSpriteAnim_EggHatch0,
+ sSpriteAnim_EggHatch1,
+ sSpriteAnim_EggHatch2,
+ sSpriteAnim_EggHatch3,
+};
static const struct SpriteSheet sEggHatch_Sheet =
- {
- .data = sEggHatchTiles,
- .size = 2048,
- .tag = 12345,
- };
+{
+ .data = sEggHatchTiles,
+ .size = 2048,
+ .tag = 12345,
+};
static const struct SpriteSheet sEggShards_Sheet =
- {
- .data = sEggShardTiles,
- .size = 128,
- .tag = 23456,
- };
+{
+ .data = sEggShardTiles,
+ .size = 128,
+ .tag = 23456,
+};
static const struct SpritePalette sEgg_SpritePalette =
- {
- .data = sEggPalette,
- .tag = 54321
- };
+{
+ .data = sEggPalette,
+ .tag = 54321
+};
static const struct SpriteTemplate sSpriteTemplate_EggHatch =
- {
- .tileTag = 12345,
- .paletteTag = 54321,
- .oam = &sOamData_EggHatch,
- .anims = sSpriteAnimTable_EggHatch,
- .images = NULL,
- .affineAnims = gDummySpriteAffineAnimTable,
- .callback = SpriteCallbackDummy
- };
+{
+ .tileTag = 12345,
+ .paletteTag = 54321,
+ .oam = &sOamData_EggHatch,
+ .anims = sSpriteAnimTable_EggHatch,
+ .images = NULL,
+ .affineAnims = gDummySpriteAffineAnimTable,
+ .callback = SpriteCallbackDummy
+};
static const struct OamData sOamData_EggShard =
- {
- .y = 0,
- .affineMode = 0,
- .objMode = 0,
- .mosaic = 0,
- .bpp = 0,
- .shape = SPRITE_SHAPE(8x8),
- .x = 0,
- .matrixNum = 0,
- .size = SPRITE_SIZE(8x8),
- .tileNum = 0,
- .priority = 2,
- .paletteNum = 0,
- .affineParam = 0,
- };
+{
+ .y = 0,
+ .affineMode = 0,
+ .objMode = 0,
+ .mosaic = 0,
+ .bpp = 0,
+ .shape = SPRITE_SHAPE(8x8),
+ .x = 0,
+ .matrixNum = 0,
+ .size = SPRITE_SIZE(8x8),
+ .tileNum = 0,
+ .priority = 2,
+ .paletteNum = 0,
+ .affineParam = 0,
+};
static const union AnimCmd sSpriteAnim_EggShard0[] =
- {
- ANIMCMD_FRAME(0, 5),
- ANIMCMD_END
- };
+{
+ ANIMCMD_FRAME(0, 5),
+ ANIMCMD_END
+};
static const union AnimCmd sSpriteAnim_EggShard1[] =
- {
- ANIMCMD_FRAME(1, 5),
- ANIMCMD_END
- };
+{
+ ANIMCMD_FRAME(1, 5),
+ ANIMCMD_END
+};
static const union AnimCmd sSpriteAnim_EggShard2[] =
- {
- ANIMCMD_FRAME(2, 5),
- ANIMCMD_END
- };
+{
+ ANIMCMD_FRAME(2, 5),
+ ANIMCMD_END
+};
static const union AnimCmd sSpriteAnim_EggShard3[] =
- {
- ANIMCMD_FRAME(3, 5),
- ANIMCMD_END
- };
+{
+ ANIMCMD_FRAME(3, 5),
+ ANIMCMD_END
+};
static const union AnimCmd *const sSpriteAnimTable_EggShard[] =
- {
- sSpriteAnim_EggShard0,
- sSpriteAnim_EggShard1,
- sSpriteAnim_EggShard2,
- sSpriteAnim_EggShard3,
- };
+{
+ sSpriteAnim_EggShard0,
+ sSpriteAnim_EggShard1,
+ sSpriteAnim_EggShard2,
+ sSpriteAnim_EggShard3,
+};
static const struct SpriteTemplate sSpriteTemplate_EggShard =
- {
- .tileTag = 23456,
- .paletteTag = 54321,
- .oam = &sOamData_EggShard,
- .anims = sSpriteAnimTable_EggShard,
- .images = NULL,
- .affineAnims = gDummySpriteAffineAnimTable,
- .callback = SpriteCB_EggShard
- };
+{
+ .tileTag = 23456,
+ .paletteTag = 54321,
+ .oam = &sOamData_EggShard,
+ .anims = sSpriteAnimTable_EggShard,
+ .images = NULL,
+ .affineAnims = gDummySpriteAffineAnimTable,
+ .callback = SpriteCB_EggShard
+};
static const struct BgTemplate sBgTemplates_EggHatch[2] =
+{
{
- {
- .bg = 0,
- .charBaseIndex = 2,
- .mapBaseIndex = 24,
- .screenSize = 3,
- .paletteMode = 0,
- .priority = 0,
- .baseTile = 0
- },
-
- {
- .bg = 1,
- .charBaseIndex = 0,
- .mapBaseIndex = 8,
- .screenSize = 1,
- .paletteMode = 0,
- .priority = 2,
- .baseTile = 0
- },
- };
+ .bg = 0,
+ .charBaseIndex = 2,
+ .mapBaseIndex = 24,
+ .screenSize = 3,
+ .paletteMode = 0,
+ .priority = 0,
+ .baseTile = 0
+ },
+
+ {
+ .bg = 1,
+ .charBaseIndex = 0,
+ .mapBaseIndex = 8,
+ .screenSize = 1,
+ .paletteMode = 0,
+ .priority = 2,
+ .baseTile = 0
+ },
+};
static const struct WindowTemplate sWinTemplates_EggHatch[2] =
- {
- {
- .bg = 0,
- .tilemapLeft = 2,
- .tilemapTop = 15,
- .width = 26,
- .height = 4,
- .paletteNum = 0,
- .baseBlock = 64
- },
- DUMMY_WIN_TEMPLATE
- };
-
-static const struct WindowTemplate sYesNoWinTemplate =
+{
{
.bg = 0,
- .tilemapLeft = 21,
- .tilemapTop = 9,
- .width = 6,
+ .tilemapLeft = 2,
+ .tilemapTop = 15,
+ .width = 26,
.height = 4,
- .paletteNum = 15,
- .baseBlock = 424
- };
+ .paletteNum = 0,
+ .baseBlock = 64
+ },
+ DUMMY_WIN_TEMPLATE
+};
+
+static const struct WindowTemplate sYesNoWinTemplate =
+{
+ .bg = 0,
+ .tilemapLeft = 21,
+ .tilemapTop = 9,
+ .width = 6,
+ .height = 4,
+ .paletteNum = 15,
+ .baseBlock = 424
+};
static const s16 sEggShardVelocities[][2] =
- {
- {Q_8_8(-1.5), Q_8_8(-3.75)},
- {Q_8_8(-5), Q_8_8(-3)},
- {Q_8_8(3.5), Q_8_8(-3)},
- {Q_8_8(-4), Q_8_8(-3.75)},
- {Q_8_8(2), Q_8_8(-1.5)},
- {Q_8_8(-0.5), Q_8_8(-6.75)},
- {Q_8_8(5), Q_8_8(-2.25)},
- {Q_8_8(-1.5), Q_8_8(-3.75)},
- {Q_8_8(4.5), Q_8_8(-1.5)},
- {Q_8_8(-1), Q_8_8(-6.75)},
- {Q_8_8(4), Q_8_8(-2.25)},
- {Q_8_8(-3.5), Q_8_8(-3.75)},
- {Q_8_8(1), Q_8_8(-1.5)},
- {Q_8_8(-3.515625), Q_8_8(-6.75)},
- {Q_8_8(4.5), Q_8_8(-2.25)},
- {Q_8_8(-0.5), Q_8_8(-7.5)},
- {Q_8_8(1), Q_8_8(-4.5)},
- {Q_8_8(-2.5), Q_8_8(-2.25)},
- {Q_8_8(2.5), Q_8_8(-7.5)},
- };
+{
+ {Q_8_8(-1.5), Q_8_8(-3.75)},
+ {Q_8_8(-5), Q_8_8(-3)},
+ {Q_8_8(3.5), Q_8_8(-3)},
+ {Q_8_8(-4), Q_8_8(-3.75)},
+ {Q_8_8(2), Q_8_8(-1.5)},
+ {Q_8_8(-0.5), Q_8_8(-6.75)},
+ {Q_8_8(5), Q_8_8(-2.25)},
+ {Q_8_8(-1.5), Q_8_8(-3.75)},
+ {Q_8_8(4.5), Q_8_8(-1.5)},
+ {Q_8_8(-1), Q_8_8(-6.75)},
+ {Q_8_8(4), Q_8_8(-2.25)},
+ {Q_8_8(-3.5), Q_8_8(-3.75)},
+ {Q_8_8(1), Q_8_8(-1.5)},
+ {Q_8_8(-3.515625), Q_8_8(-6.75)},
+ {Q_8_8(4.5), Q_8_8(-2.25)},
+ {Q_8_8(-0.5), Q_8_8(-7.5)},
+ {Q_8_8(1), Q_8_8(-4.5)},
+ {Q_8_8(-2.5), Q_8_8(-2.25)},
+ {Q_8_8(2.5), Q_8_8(-7.5)},
+};
// code
@@ -2202,8 +2202,8 @@ static void CreateEggShardSprite(u8 x, u8 y, s16 data1, s16 data2, s16 data3, u8
static void EggHatchPrintMessage(u8 windowId, u8* string, u8 x, u8 y, u8 speed)
{
FillWindowPixelBuffer(windowId, 0xFF);
- sEggHatchData->textColor.fgColor = 0;
- sEggHatchData->textColor.bgColor = 5;
- sEggHatchData->textColor.shadowColor = 6;
- AddTextPrinterParameterized4(windowId, 3, x, y, 1, 1, &sEggHatchData->textColor, speed, string);
+ sEggHatchData->textColor[0] = 0;
+ sEggHatchData->textColor[1] = 5;
+ sEggHatchData->textColor[2] = 6;
+ AddTextPrinterParameterized4(windowId, 3, x, y, 1, 1, sEggHatchData->textColor, speed, string);
}
diff --git a/src/diploma.c b/src/diploma.c
index 7fa140430..afb22400b 100644
--- a/src/diploma.c
+++ b/src/diploma.c
@@ -52,7 +52,7 @@ static const u8 gUnknown_84159B3[] = _("{HIGHLIGHT TRANSPARENT}     
static const u8 gUnknown_84159ED[] = _("{COLOR RED}{HIGHLIGHT TRANSPARENT}ゲームフリーク");
static const u8 gUnknown_84159FB[] = _("{COLOR RED}{HIGHLIGHT TRANSPARENT}");
-static const ALIGNED(4) struct TextColor gUnknown_8415A04 = {0, 2, 3};
+static const ALIGNED(4) u8 gUnknown_8415A04[3] = {0, 2, 3};
static const struct BgTemplate gUnknown_8415A08[] = {
{
@@ -276,10 +276,10 @@ static void DiplomaPrintText(void)
FillWindowPixelBuffer(0, 0);
DynamicPlaceholderTextUtil_ExpandPlaceholders(arr, gUnknown_841B60E);
width = GetStringWidth(2, arr, -1);
- AddTextPrinterParameterized3(0, 2, 0x78 - (width / 2), 4, &gUnknown_8415A04, -1, arr);
+ AddTextPrinterParameterized3(0, 2, 0x78 - (width / 2), 4, gUnknown_8415A04, -1, arr);
DynamicPlaceholderTextUtil_ExpandPlaceholders(arr, gUnknown_841B619);
width = GetStringWidth(2, arr, -1);
- AddTextPrinterParameterized3(0, 0x2, 0x78 - (width / 2), 0x1E, &gUnknown_8415A04, -1, arr);
- AddTextPrinterParameterized3(0, 0x2, 0x78, 0x69, &gUnknown_8415A04, 0, gUnknown_841B684);
+ AddTextPrinterParameterized3(0, 0x2, 0x78 - (width / 2), 0x1E, gUnknown_8415A04, -1, arr);
+ AddTextPrinterParameterized3(0, 0x2, 0x78, 0x69, gUnknown_8415A04, 0, gUnknown_841B684);
PutWindowTilemap(0);
}
diff --git a/src/ereader_helpers.c b/src/ereader_helpers.c
index 2a9b9ea71..cce6cefca 100644
--- a/src/ereader_helpers.c
+++ b/src/ereader_helpers.c
@@ -51,7 +51,7 @@ int EReader_Send(size_t r6, const void * r5)
{
GetKeyInput();
if (TEST_BUTTON(sJoyNew, B_BUTTON))
- gUnknown_3003F84 = 2;
+ gShouldAdvanceLinkState = 2;
sSendRecvStatus = EReaderHandleTransfer(1, r6, r5, NULL);
if ((sSendRecvStatus & 0x13) == 0x10)
@@ -71,7 +71,7 @@ int EReader_Send(size_t r6, const void * r5)
}
else
{
- gUnknown_3003F84 = 0;
+ gShouldAdvanceLinkState = 0;
VBlankIntrWait();
}
}
@@ -90,7 +90,7 @@ int EReader_Recv(void * r5)
{
GetKeyInput();
if (TEST_BUTTON(sJoyNew, B_BUTTON))
- gUnknown_3003F84 = 2;
+ gShouldAdvanceLinkState = 2;
sSendRecvStatus = EReaderHandleTransfer(0, 0, NULL, r5);
if ((sSendRecvStatus & 0x13) == 0x10)
@@ -110,7 +110,7 @@ int EReader_Recv(void * r5)
}
else
{
- gUnknown_3003F84 = 0;
+ gShouldAdvanceLinkState = 0;
VBlankIntrWait();
}
}
@@ -150,7 +150,7 @@ static void OpenSerial32(void)
REG_RCNT = 0;
REG_SIOCNT = SIO_INTR_ENABLE | SIO_32BIT_MODE;
REG_SIOCNT |= SIO_MULTI_SD;
- gUnknown_3003F84 = 0;
+ gShouldAdvanceLinkState = 0;
sCounter1 = 0;
sCounter2 = 0;
}
@@ -167,7 +167,7 @@ u16 EReaderHandleTransfer(u8 mode, size_t size, const void * data, void * recvBu
case 1:
if (DetermineSendRecvState(mode))
EnableSio();
- if (gUnknown_3003F84 == 2)
+ if (gShouldAdvanceLinkState == 2)
{
sSendRecvMgr.field_04 = 2;
sSendRecvMgr.state = 6;
@@ -179,7 +179,7 @@ u16 EReaderHandleTransfer(u8 mode, size_t size, const void * data, void * recvBu
sSendRecvMgr.state = 3;
// fallthrough
case 3:
- if (gUnknown_3003F84 == 2)
+ if (gShouldAdvanceLinkState == 2)
{
sSendRecvMgr.field_04 = 2;
sSendRecvMgr.state = 6;
diff --git a/src/fame_checker.c b/src/fame_checker.c
index aa1684981..31bd11b55 100644
--- a/src/fame_checker.c
+++ b/src/fame_checker.c
@@ -152,9 +152,9 @@ static const u16 sOakSpritePalette[] = INCBIN_U16("data/fame_checker/pal_845f580
static const u16 gUnknown_845F5A0[] = INCBIN_U16("data/fame_checker/pal_845f5a0.gbapal"); // unused?
static const u16 sSilhouettePalette[] = INCBIN_U16("data/fame_checker/pal_845f5c0.gbapal");
-static const struct TextColor sTextColor_White = {TEXT_COLOR_TRANSPARENT, TEXT_COLOR_WHITE, TEXT_COLOR_DARK_GREY};
-static const struct TextColor sTextColor_DkGrey = {TEXT_COLOR_TRANSPARENT, TEXT_COLOR_DARK_GREY, 0x03};
-static const struct TextColor sTextColor_Green = {TEXT_COLOR_TRANSPARENT, 0x06, 0x07};
+static const u8 sTextColor_White[3] = {0, 1, 2};
+static const u8 sTextColor_DkGrey[3] = {0, 2, 3};
+static const u8 sTextColor_Green[3] = {0, 6, 7};
static const u16 sTrainerIdxs[] = {
FC_NONTRAINER_START + 0, // OAK
@@ -900,7 +900,7 @@ static void PrintUIHelp(u8 state)
}
width = GetStringWidth(0, src, 0);
FillWindowPixelRect(FCWINDOWID_UIHELP, 0x00, 0, 0, 0xc0, 0x10);
- AddTextPrinterParameterized4(FCWINDOWID_UIHELP, 0, 188 - width, 0, 0, 2, &sTextColor_White, -1, src);
+ AddTextPrinterParameterized4(FCWINDOWID_UIHELP, 0, 188 - width, 0, 0, 2, sTextColor_White, -1, src);
FC_PutWindowTilemapAndCopyWindowToVramMode3(FCWINDOWID_UIHELP);
}
@@ -1213,10 +1213,10 @@ static void UpdateIconDescriptionBox(u8 whichText)
gIconDescriptionBoxIsOpen = 1;
FillWindowPixelRect(FCWINDOWID_ICONDESC, 0x00, 0, 0, 0x58, 0x20);
width = (0x54 - GetStringWidth(0, sFlavorTextOriginLocationTexts[idx], 0)) / 2;
- AddTextPrinterParameterized4(FCWINDOWID_ICONDESC, 0, width, 0, 0, 2, &sTextColor_DkGrey, -1, sFlavorTextOriginLocationTexts[idx]);
+ AddTextPrinterParameterized4(FCWINDOWID_ICONDESC, 0, width, 0, 0, 2, sTextColor_DkGrey, -1, sFlavorTextOriginLocationTexts[idx]);
StringExpandPlaceholders(gStringVar1, sFlavorTextOriginObjectNameTexts[idx]);
width = (0x54 - GetStringWidth(0, gStringVar1, 0)) / 2;
- AddTextPrinterParameterized4(FCWINDOWID_ICONDESC, 0, width, 10, 0, 2, &sTextColor_DkGrey, -1, gStringVar1);
+ AddTextPrinterParameterized4(FCWINDOWID_ICONDESC, 0, width, 10, 0, 2, sTextColor_DkGrey, -1, gStringVar1);
FC_PutWindowTilemapAndCopyWindowToVramMode3(FCWINDOWID_ICONDESC);
}
@@ -1341,14 +1341,14 @@ static void FC_DoMoveCursor(s32 itemIndex, bool8 onInit)
u16 who;
ListMenuGetScrollAndRow(sFameCheckerData->listMenuTaskId, &listY, &cursorY);
who = listY + cursorY;
- AddTextPrinterParameterized4(FCWINDOWID_LIST, 2, 8, 14 * cursorY + 4, 0, 0, &sTextColor_Green, 0, sListMenuItems[itemIndex].label);
+ AddTextPrinterParameterized4(FCWINDOWID_LIST, 2, 8, 14 * cursorY + 4, 0, 0, sTextColor_Green, 0, sListMenuItems[itemIndex].label);
if (!onInit)
{
if (listY < sFameCheckerData->listMenuTopIdx2)
sFameCheckerData->listMenuDrawnSelIdx++;
else if (listY > sFameCheckerData->listMenuTopIdx2 && who != sFameCheckerData->numUnlockedPersons - 1)
sFameCheckerData->listMenuDrawnSelIdx--;
- AddTextPrinterParameterized4(FCWINDOWID_LIST, 2, 8, 14 * sFameCheckerData->listMenuDrawnSelIdx + 4, 0, 0, &sTextColor_DkGrey, 0, sListMenuItems[sFameCheckerData->listMenuCurIdx].label);
+ AddTextPrinterParameterized4(FCWINDOWID_LIST, 2, 8, 14 * sFameCheckerData->listMenuDrawnSelIdx + 4, 0, 0, sTextColor_DkGrey, 0, sListMenuItems[sFameCheckerData->listMenuCurIdx].label);
}
sFameCheckerData->listMenuCurIdx = itemIndex;
@@ -1546,7 +1546,7 @@ static void PlaceListMenuCursor(bool8 isActive)
{
u16 cursorY = ListMenuGetYCoordForPrintingArrowCursor(sFameCheckerData->listMenuTaskId);
if (isActive == TRUE)
- AddTextPrinterParameterized4(FCWINDOWID_LIST, 2, 0, cursorY, 0, 0, &sTextColor_DkGrey, 0, gFameCheckerText_ListMenuCursor);
+ AddTextPrinterParameterized4(FCWINDOWID_LIST, 2, 0, cursorY, 0, 0, sTextColor_DkGrey, 0, gFameCheckerText_ListMenuCursor);
else
- AddTextPrinterParameterized4(FCWINDOWID_LIST, 2, 0, cursorY, 0, 0, &sTextColor_White, 0, gFameCheckerText_ListMenuCursor);
+ AddTextPrinterParameterized4(FCWINDOWID_LIST, 2, 0, cursorY, 0, 0, sTextColor_White, 0, gFameCheckerText_ListMenuCursor);
}
diff --git a/src/field_fadetransition.c b/src/field_fadetransition.c
new file mode 100644
index 000000000..2ed3b80b9
--- /dev/null
+++ b/src/field_fadetransition.c
@@ -0,0 +1,965 @@
+#include "global.h"
+#include "palette.h"
+#include "field_fadetransition.h"
+#include "overworld.h"
+#include "fldeff.h"
+#include "field_weather.h"
+#include "map_preview_screen.h"
+#include "field_player_avatar.h"
+#include "task.h"
+#include "script.h"
+#include "cable_club.h"
+#include "fieldmap.h"
+#include "metatile_behavior.h"
+#include "quest_log.h"
+#include "link.h"
+#include "map_obj_80688E4.h"
+#include "sound.h"
+#include "field_door.h"
+#include "field_effect.h"
+#include "field_screen_effect.h"
+#include "field_map_obj.h"
+#include "field_map_obj_helpers.h"
+#include "field_specials.h"
+#include "map_obj_lock.h"
+#include "start_menu.h"
+#include "constants/songs.h"
+
+static void sub_807DF4C(u8 a0);
+static void sub_807DFBC(u8 taskId);
+static void task_map_chg_seq_0807E20C(u8 taskId);
+static void task_map_chg_seq_0807E2CC(u8 taskId);
+static void sub_807E31C(u8 taskId);
+static void sub_807E718(u8 taskId);
+static void sub_807E784(u8 taskId);
+static void sub_807E80C(u8 taskId);
+static void sub_807E980(u8 taskId);
+static void sub_807EB64(u16, s16*, s16*);
+static void sub_807EBBC(u8 a0, s16 *a1, s16 *a2);
+static void sub_807EAC4(s16, s16, s16*, s16*, s16*);
+static void sub_807EC34(u8 taskId);
+static void sub_807ECBC(s16 *, s16 *, s16 *, s16 *, s16 *);
+static bool8 sub_807EDA0(s16 *, s16 *, s16 *, s16 *, s16 *);
+
+void palette_bg_faded_fill_white(void)
+{
+ CpuFastFill16(RGB_WHITE, gPlttBufferFaded, 0x400);
+}
+
+void palette_bg_faded_fill_black(void)
+{
+ CpuFastFill16(RGB_BLACK, gPlttBufferFaded, 0x400);
+}
+
+void pal_fill_for_maplights(void)
+{
+ switch (sub_80C9DCC(get_map_light_from_warp0(), GetCurrentMapType()))
+ {
+ case 0:
+ palette_bg_faded_fill_black();
+ fade_screen(0, 0);
+ palette_bg_faded_fill_black();
+ break;
+ case 1:
+ palette_bg_faded_fill_white();
+ fade_screen(2, 0);
+ palette_bg_faded_fill_white();
+ break;
+ }
+}
+
+static void sub_807DBAC(void)
+{
+ switch (sub_80C9DCC(get_map_light_from_warp0(), GetCurrentMapType()))
+ {
+ case 0:
+ palette_bg_faded_fill_black();
+ fade_screen(0, 3);
+ palette_bg_faded_fill_black();
+ break;
+ case 1:
+ palette_bg_faded_fill_white();
+ fade_screen(2, 3);
+ palette_bg_faded_fill_white();
+ break;
+ }
+}
+
+void sub_807DC00(void)
+{
+ palette_bg_faded_fill_black();
+ fade_screen(0, 0);
+ palette_bg_faded_fill_black();
+}
+
+void sub_807DC18(void)
+{
+ const struct MapHeader *header = warp1_get_mapheader();
+ if (header->regionMapSectionId != gMapHeader.regionMapSectionId && sub_80F8110(header->regionMapSectionId, FALSE))
+ fade_screen(1, 0);
+ else
+ {
+ switch (sub_80C9D7C(GetCurrentMapType(), header->mapType))
+ {
+ case 0:
+ fade_screen(1, 0);
+ break;
+ case 1:
+ fade_screen(3, 0);
+ break;
+ }
+ }
+}
+
+static void sub_807DC70(void)
+{
+ switch (sub_80C9D7C(GetCurrentMapType(), warp1_get_mapheader()->mapType))
+ {
+ case 0:
+ fade_screen(1, 3);
+ break;
+ case 1:
+ fade_screen(3, 3);
+ break;
+ }
+}
+
+static void sub_807DCB0(bool8 arg)
+{
+ sub_805CB04(!arg);
+}
+
+static void task0A_nop_for_a_while(u8 taskId)
+{
+ if (sub_807E418() == TRUE)
+ DestroyTask(taskId);
+}
+
+void sub_807DCE4(void)
+{
+ ScriptContext2_Enable();
+ Overworld_PlaySpecialMapMusic();
+ sub_807DC00();
+ CreateTask(task0A_nop_for_a_while, 10);
+}
+
+static void task0A_asap_script_env_2_enable_and_set_ctx_running(u8 taskId)
+{
+ if (sub_807E418() == TRUE)
+ {
+ DestroyTask(taskId);
+ EnableBothScriptContexts();
+ }
+}
+
+void FieldCallback_ReturnToEventScript2(void)
+{
+ ScriptContext2_Enable();
+ Overworld_PlaySpecialMapMusic();
+ sub_807DC00();
+ CreateTask(task0A_asap_script_env_2_enable_and_set_ctx_running, 10);
+}
+
+void sub_807DD44(void)
+{
+ ScriptContext2_Enable();
+ sub_807DC00();
+ CreateTask(task0A_asap_script_env_2_enable_and_set_ctx_running, 10);
+}
+
+static void task_mpl_807DD60(u8 taskId)
+{
+ struct Task *task = &gTasks[taskId];
+ switch (task->data[0])
+ {
+ case 0:
+ task->data[1] = sub_8081150();
+ task->data[0]++;
+ break;
+ case 1:
+ if (gTasks[task->data[1]].isActive != TRUE)
+ {
+ pal_fill_for_maplights();
+ task->data[0]++;
+ }
+ break;
+ case 2:
+ if (sub_807E418() == TRUE)
+ {
+ ScriptContext2_Disable();
+ DestroyTask(taskId);
+ }
+ break;
+ }
+}
+
+void sub_807DDD0(void)
+{
+ ScriptContext2_Enable();
+ Overworld_PlaySpecialMapMusic();
+ palette_bg_faded_fill_black();
+ CreateTask(task_mpl_807DD60, 10);
+}
+
+static void sub_807DDF0(u8 taskId)
+{
+ struct Task *task = &gTasks[taskId];
+ switch (task->data[0])
+ {
+ case 0:
+ sub_800AB9C();
+ task->data[0]++;
+ break;
+ case 1:
+ if (IsLinkTaskFinished())
+ {
+ pal_fill_for_maplights();
+ task->data[0]++;
+ }
+ break;
+ case 2:
+ if (sub_807E418() == TRUE)
+ {
+ sub_8009FE8();
+ ScriptContext2_Disable();
+ DestroyTask(taskId);
+ }
+ break;
+ }
+}
+
+void sub_807DE58(void)
+{
+ ScriptContext2_Enable();
+ Overworld_PlaySpecialMapMusic();
+ palette_bg_faded_fill_black();
+ CreateTask(sub_807DDF0, 10);
+}
+
+static void sub_807DE78(bool8 a0)
+{
+ s16 x, y;
+ u32 behavior;
+ TaskFunc func;
+
+ PlayerGetDestCoords(&x, &y);
+ behavior = MapGridGetMetatileBehaviorAt(x, y);
+ if (MetatileBehavior_IsWarpDoor_2(behavior) == TRUE)
+ {
+ func = sub_807DFBC;
+ switch (sub_80C9DCC(get_map_light_from_warp0(), GetCurrentMapType()))
+ {
+ case 0:
+ palette_bg_faded_fill_black();
+ break;
+ case 1:
+ palette_bg_faded_fill_white();
+ break;
+ }
+ }
+ else
+ {
+ sub_807DF4C(a0);
+ if (MetatileBehavior_IsCaveDoor(behavior) == TRUE)
+ func = task_map_chg_seq_0807E20C;
+ else if (MetatileBehavior_IsUnknownWarp6C_to_6F(behavior) == TRUE)
+ {
+ u8 tmp = gUnknown_2031DE0;
+ func = task_map_chg_seq_0807E2CC;
+ if (!tmp)
+ func = sub_807EC34;
+ }
+ else
+ func = task_map_chg_seq_0807E2CC;
+ }
+ gUnknown_2031DE0 = FALSE;
+ CreateTask(func, 10);
+}
+
+static void sub_807DF4C(bool8 a0)
+{
+ if (!a0)
+ pal_fill_for_maplights();
+ else
+ sub_807DC00();
+}
+
+void sub_807DF64(void)
+{
+ Overworld_PlaySpecialMapMusic();
+ sub_8111CF0();
+ sub_807DE78(FALSE);
+ ScriptContext2_Enable();
+}
+
+void sub_807DF7C(void)
+{
+ Overworld_PlaySpecialMapMusic();
+ sub_8111CF0();
+ sub_807DE78(TRUE);
+ ScriptContext2_Enable();
+}
+
+static void sub_807DF94(void)
+{
+ Overworld_PlaySpecialMapMusic();
+ pal_fill_for_maplights();
+ sub_8111CF0();
+ PlaySE(SE_RU_GASHIN);
+ CreateTask(sub_807E31C, 10);
+ ScriptContext2_Enable();
+}
+
+static void sub_807DFBC(u8 taskId)
+{
+ struct Task * task = &gTasks[taskId];
+ s16 *x = &task->data[2];
+ s16 *y = &task->data[3];
+
+ if (task->data[0] == 0)
+ task->data[0] = 5;
+
+ switch (task->data[0])
+ {
+ case 0: // Never reached
+ sub_807DCB0(0);
+ player_bitmagic();
+ PlayerGetDestCoords(x, y);
+ FieldSetDoorOpened(*x, *y);
+ task->data[0] = 1;
+ break;
+ case 5:
+ sub_807DCB0(0);
+ player_bitmagic();
+ sub_807F114();
+ sub_807DBAC();
+ task->data[0] = 6;
+ break;
+ case 6:
+ task->data[15]++;
+ if (task->data[15] == 25)
+ {
+ PlayerGetDestCoords(x, y);
+ PlaySE(GetDoorSoundEffect(*x, *y));
+ FieldAnimateDoorOpen(*x, *y);
+ task->data[0] = 7;
+ }
+ break;
+ case 7:
+ if (!FieldIsDoorAnimationRunning())
+ {
+ PlayerGetDestCoords(&task->data[12], &task->data[13]);
+ sub_807DCB0(TRUE);
+ FieldObjectSetHeldMovement(&gMapObjects[GetFieldObjectIdByLocalIdAndMap(0xFF, 0, 0)], 16);
+ task->data[0] = 8;
+ }
+ break;
+ case 8:
+ task->data[14]++;
+ if (task->data[14] == 14)
+ {
+ FieldAnimateDoorClose(task->data[12], task->data[13]);
+ task->data[0] = 9;
+ }
+ break;
+ case 9:
+ if (sub_807E418() && walkrun_is_standing_still() && !FieldIsDoorAnimationRunning() && !FuncIsActiveTask(sub_807F204))
+ {
+ FieldObjectClearHeldMovementIfFinished(&gMapObjects[GetFieldObjectIdByLocalIdAndMap(0xFF, 0, 0)]);
+ task->data[0] = 4;
+ }
+ break;
+ // Legacy RS
+ case 1:
+ if (sub_807E418())
+ {
+ sub_807DCB0(TRUE);
+ FieldObjectSetHeldMovement(&gMapObjects[GetFieldObjectIdByLocalIdAndMap(0xFF, 0, 0)], 16);
+ task->data[0] = 2;
+ }
+ break;
+ case 2:
+ if (walkrun_is_standing_still())
+ {
+ task->data[1] = FieldAnimateDoorClose(*x, *y);
+ FieldObjectClearHeldMovementIfFinished(&gMapObjects[GetFieldObjectIdByLocalIdAndMap(0xFF, 0, 0)]);
+ task->data[0] = 3;
+ }
+ break;
+ case 3:
+ if (task->data[1] < 0 || gTasks[task->data[1]].isActive != TRUE)
+ task->data[0] = 4;
+ break;
+ case 4:
+ UnfreezeMapObjects();
+ ScriptContext2_Disable();
+ DestroyTask(taskId);
+ break;
+ }
+}
+
+static void task_map_chg_seq_0807E20C(u8 taskId)
+{
+ struct Task * task = &gTasks[taskId];
+ s16 *x = &task->data[2];
+ s16 *y = &task->data[3];
+
+ switch (task->data[0])
+ {
+ case 0:
+ sub_807DCB0(0);
+ player_bitmagic();
+ PlayerGetDestCoords(x, y);
+ task->data[0] = 1;
+ break;
+ case 1:
+ if (sub_807E418())
+ {
+ sub_807DCB0(TRUE);
+ FieldObjectSetHeldMovement(&gMapObjects[GetFieldObjectIdByLocalIdAndMap(0xFF, 0, 0)], sub_8063F84(GetPlayerFacingDirection()));
+ task->data[0] = 2;
+ }
+ break;
+ case 2:
+ if (walkrun_is_standing_still())
+ {
+ task->data[0] = 3;
+ }
+ break;
+ case 3:
+ UnfreezeMapObjects();
+ ScriptContext2_Disable();
+ DestroyTask(taskId);
+ break;
+ }
+}
+
+static void task_map_chg_seq_0807E2CC(u8 taskId)
+{
+ switch (gTasks[taskId].data[0])
+ {
+ case 0:
+ player_bitmagic();
+ ScriptContext2_Enable();
+ gTasks[taskId].data[0]++;
+ break;
+ case 1:
+ if (sub_807E418())
+ {
+ UnfreezeMapObjects();
+ ScriptContext2_Disable();
+ DestroyTask(taskId);
+ }
+ break;
+ }
+}
+
+static void sub_807E31C(u8 taskId)
+{
+ switch (gTasks[taskId].data[0])
+ {
+ case 0:
+ player_bitmagic();
+ ScriptContext2_Enable();
+ sub_805DC04();
+ gTasks[taskId].data[0]++;
+ break;
+ case 1:
+ if (sub_807E418() && sub_805DC24() != TRUE)
+ {
+ UnfreezeMapObjects();
+ ScriptContext2_Disable();
+ DestroyTask(taskId);
+ }
+ break;
+ }
+}
+
+static void sub_807E378(u8 taskId)
+{
+ if (sub_807E418() == TRUE)
+ {
+ DestroyTask(taskId);
+ CreateTask(sub_806F1F0, 80);
+ }
+}
+
+void sub_807E3A0(void)
+{
+ sub_807DC00();
+ CreateTask(sub_807E378, 80);
+ ScriptContext2_Enable();
+}
+
+bool32 sub_807E3BC(void)
+{
+ sub_806F1D4();
+ return FALSE;
+}
+
+static void task_mpl_807E3C8(u8 taskId)
+{
+ if (sub_807E418() == TRUE)
+ {
+ ScriptContext2_Disable();
+ DestroyTask(taskId);
+ sub_80696C0();
+ }
+}
+
+void sub_807E3EC(void)
+{
+ ScriptContext2_Enable();
+ Overworld_PlaySpecialMapMusic();
+ sub_807DC00();
+ CreateTask(task_mpl_807E3C8, 10);
+}
+
+static bool32 sub_807E40C(void)
+{
+ return gPaletteFade.active;
+}
+
+bool32 sub_807E418(void)
+{
+ if (sub_807AA70() == TRUE && sub_80F83B0())
+ return TRUE;
+ else
+ return FALSE;
+}
+
+void DoWarp(void)
+{
+ ScriptContext2_Enable();
+ sub_8055F88();
+ sub_807DC18();
+ PlayRainStoppingSoundEffect();
+ PlaySE(SE_KAIDAN);
+ gFieldCallback = sub_807DF64;
+ CreateTask(sub_807E718, 10);
+}
+
+void DoDiveWarp(void)
+{
+ ScriptContext2_Enable();
+ sub_8055F88();
+ sub_807DC18();
+ PlayRainStoppingSoundEffect();
+ gFieldCallback = sub_807DF64;
+ CreateTask(sub_807E718, 10);
+}
+
+void sub_807E4A0(u16 a, u16 b)
+{
+ u8 taskId = CreateTask(sub_807E980, 10);
+ gTasks[taskId].data[1] = a;
+ gTasks[taskId].data[15] = b;
+ sub_807E980(taskId);
+}
+
+void DoDoorWarp(void)
+{
+ ScriptContext2_Enable();
+ gFieldCallback = sub_807DF64;
+ CreateTask(sub_807E80C, 10);
+}
+
+void sub_807E500(void)
+{
+ ScriptContext2_Enable();
+ CreateTask(sub_807E718, 10);
+ gFieldCallback = sub_807DF94;
+}
+
+void sub_807E524(void)
+{
+ ScriptContext2_Enable();
+ gFieldCallback = sub_807DF64;
+ CreateTask(sub_807E784, 10);
+}
+
+void DoFallWarp(void)
+{
+ DoDiveWarp();
+ gFieldCallback = sub_8084454;
+}
+
+void sub_807E560(u8 a0)
+{
+ ScriptContext2_Enable();
+ sub_8084784(a0, 10);
+}
+
+void sub_807E57C(void)
+{
+ ScriptContext2_Enable();
+ sub_8084F2C(10);
+}
+
+void sub_807E58C(void)
+{
+ ScriptContext2_Enable();
+ sub_80853CC(10);
+}
+
+void sub_807E59C(void)
+{
+ ScriptContext2_Enable();
+ sub_8055F88();
+ CreateTask(sub_807E784, 10);
+ gFieldCallback = sub_807DF94;
+}
+
+void sub_807E5C4(void)
+{
+ ScriptContext2_Enable();
+ sub_807DC18();
+ CreateTask(sub_807E718, 10);
+ gFieldCallback = nullsub_60;
+}
+
+static void sub_807E5EC(u8 taskId)
+{
+ struct Task * task = &gTasks[taskId];
+ switch (task->data[0])
+ {
+ case 0:
+ ScriptContext2_Enable();
+ task->data[0]++;
+ break;
+ case 1:
+ if (!sub_807E40C() && sub_8055FC4())
+ task->data[0]++;
+ break;
+ case 2:
+ WarpIntoMap();
+ SetMainCallback2(sub_8056788);
+ DestroyTask(taskId);
+ break;
+ }
+}
+
+void sub_807E654(void)
+{
+ ScriptContext2_Enable();
+ sub_8055F88();
+ sub_807DC18();
+ PlaySE(SE_KAIDAN);
+ CreateTask(sub_807E5EC, 10);
+}
+
+static void sub_807E678(u8 taskId)
+{
+ s16 * data = gTasks[taskId].data;
+ switch (data[0])
+ {
+ case 0:
+ ClearLinkCallback_2();
+ fade_screen(1, 0);
+ sub_8055F88();
+ PlaySE(SE_KAIDAN);
+ data[0]++;
+ break;
+ case 1:
+ if (!sub_807E40C() && sub_8055FC4())
+ {
+ sub_800AAC0();
+ data[0]++;
+ }
+ break;
+ case 2:
+ if (gReceivedRemoteLinkPlayers == 0)
+ {
+ WarpIntoMap();
+ SetMainCallback2(CB2_LoadMap);
+ DestroyTask(taskId);
+ }
+ break;
+ }
+}
+
+void sub_807E704(void)
+{
+ CreateTask(sub_807E678, 10);
+}
+
+static void sub_807E718(u8 taskId)
+{
+ struct Task *task = &gTasks[taskId];
+ switch (task->data[0])
+ {
+ case 0:
+ player_bitmagic();
+ ScriptContext2_Enable();
+ task->data[0]++;
+ break;
+ case 1:
+ if (!sub_807E40C() && sub_8055FC4())
+ task->data[0]++;
+ break;
+ case 2:
+ WarpIntoMap();
+ SetMainCallback2(CB2_LoadMap);
+ DestroyTask(taskId);
+ break;
+ }
+}
+
+static void sub_807E784(u8 taskId)
+{
+ struct Task *task = &gTasks[taskId];
+ switch (task->data[0])
+ {
+ case 0:
+ player_bitmagic();
+ ScriptContext2_Enable();
+ PlaySE(SE_FU_ZUZUZU);
+ sub_805DAB0();
+ task->data[0]++;
+ break;
+ case 1:
+ if (!sub_805DAD0())
+ {
+ sub_807DC18();
+ task->data[0]++;
+ }
+ break;
+ case 2:
+ if (!sub_807E40C() && sub_8055FC4())
+ task->data[0]++;
+ break;
+ case 3:
+ WarpIntoMap();
+ SetMainCallback2(CB2_LoadMap);
+ DestroyTask(taskId);
+ break;
+ }
+}
+
+static void sub_807E80C(u8 taskId)
+{
+ struct Task *task = &gTasks[taskId];
+ s16 * xp = &task->data[2];
+ s16 * yp = &task->data[3];
+ switch (task->data[0])
+ {
+ case 0:
+ player_bitmagic();
+ PlayerGetDestCoords(xp, yp);
+ PlaySE(GetDoorSoundEffect(*xp, *yp - 1));
+ task->data[1] = FieldAnimateDoorOpen(*xp, *yp - 1);
+ task->data[0] = 1;
+ break;
+ case 1:
+ if (task->data[1] < 0 || gTasks[task->data[1]].isActive != TRUE)
+ {
+ FieldObjectClearAnimIfSpecialAnimActive(&gMapObjects[GetFieldObjectIdByLocalIdAndMap(0xFF, 0, 0)]);
+ FieldObjectSetHeldMovement(&gMapObjects[GetFieldObjectIdByLocalIdAndMap(0xFF, 0, 0)], 17);
+ task->data[0] = 2;
+ }
+ break;
+ case 2:
+ if (walkrun_is_standing_still())
+ {
+ task->data[1] = FieldAnimateDoorClose(*xp, *yp - 1);
+ FieldObjectClearHeldMovementIfFinished(&gMapObjects[GetFieldObjectIdByLocalIdAndMap(0xFF, 0, 0)]);
+ sub_807DCB0(FALSE);
+ task->data[0] = 3;
+ }
+ break;
+ case 3:
+ if (task->data[1] < 0 || gTasks[task->data[1]].isActive != TRUE)
+ {
+ task->data[0] = 4;
+ }
+ break;
+ case 4:
+ sub_8055F88();
+ sub_807DC18();
+ PlayRainStoppingSoundEffect();
+ task->data[0] = 0;
+ task->func = sub_807E718;
+ break;
+ case 5:
+ sub_8055F88();
+ PlayRainStoppingSoundEffect();
+ task->data[0] = 0;
+ task->func = sub_807E718;
+ break;
+ }
+}
+
+static void sub_807E980(u8 taskId)
+{
+ s16 * data = gTasks[taskId].data;
+ struct MapObject *playerObj = &gMapObjects[gPlayerAvatar.mapObjectId];
+ struct Sprite *playerSpr = &gSprites[gPlayerAvatar.spriteId];
+ switch (data[0])
+ {
+ case 0:
+ ScriptContext2_Enable();
+ player_bitmagic();
+ CameraObjectReset2();
+ data[0]++;
+ break;
+ case 1:
+ if (!FieldObjectIsMovementOverridden(playerObj) || FieldObjectClearHeldMovementIfFinished(playerObj))
+ {
+ if (data[15] != 0)
+ data[15]--;
+ else
+ {
+ sub_8055F88();
+ PlayRainStoppingSoundEffect();
+ playerSpr->oam.priority = 1;
+ sub_807EB64(data[1], &data[2], &data[3]);
+ PlaySE(SE_KAIDAN);
+ data[0]++;
+ }
+ }
+ break;
+ case 2:
+ sub_807EAC4(data[2], data[3], &data[4], &data[5], &data[6]);
+ data[15]++;
+ if (data[15] >= 12)
+ {
+ sub_807DC18();
+ data[0]++;
+ }
+ break;
+ case 3:
+ sub_807EAC4(data[2], data[3], &data[4], &data[5], &data[6]);
+ if (!sub_807E40C() && sub_8055FC4())
+ data[0]++;
+ break;
+ default:
+ gFieldCallback = sub_807DF64;
+ WarpIntoMap();
+ SetMainCallback2(CB2_LoadMap);
+ DestroyTask(taskId);
+ break;
+ }
+}
+
+static void sub_807EAC4(s16 a0, s16 a1, s16 *a2, s16 *a3, s16 *a4)
+{
+ struct Sprite *playerSpr = &gSprites[gPlayerAvatar.spriteId];
+ struct MapObject *playerObj = &gMapObjects[gPlayerAvatar.mapObjectId];
+ if (a1 > 0 || *a4 > 6)
+ *a3 += a1;
+ *a2 += a0;
+ (*a4)++;
+ playerSpr->pos2.x = *a2 >> 5;
+ playerSpr->pos2.y = *a3 >> 5;
+ if (playerObj->mapobj_bit_7)
+ {
+ FieldObjectForceSetSpecialAnim(playerObj, GetStepInPlaceDelay16AnimId(GetPlayerFacingDirection()));
+ }
+}
+
+static void sub_807EB64(u16 a0, s16 *a1, s16 *a2)
+{
+ FieldObjectForceSetSpecialAnim(&gMapObjects[gPlayerAvatar.mapObjectId], GetStepInPlaceDelay16AnimId(GetPlayerFacingDirection()));
+ sub_807EBBC(a0, a1, a2);
+}
+
+static void sub_807EBBC(u8 a0, s16 *a1, s16 *a2)
+{
+ if (MetatileBehavior_IsUnknownWarp6C(a0))
+ {
+ *a1 = 16;
+ *a2 = -10;
+ }
+ else if (MetatileBehavior_IsUnknownWarp6D(a0))
+ {
+ *a1 = -17;
+ *a2 = -10;
+ }
+ else if (MetatileBehavior_IsUnknownWarp6E(a0))
+ {
+ *a1 = 17;
+ *a2 = 3;
+ }
+ else if (MetatileBehavior_IsUnknownWarp6F(a0))
+ {
+ *a1 = -17;
+ *a2 = 3;
+ }
+ else
+ {
+ *a1 = 0;
+ *a2 = 0;
+ }
+}
+
+static void sub_807EC34(u8 taskId)
+{
+ s16 * data = gTasks[taskId].data;
+ switch (data[0])
+ {
+ default:
+ if (sub_807E418() == TRUE)
+ {
+ CameraObjectReset1();
+ ScriptContext2_Disable();
+ DestroyTask(taskId);
+ }
+ break;
+ case 0:
+ Overworld_PlaySpecialMapMusic();
+ pal_fill_for_maplights();
+ ScriptContext2_Enable();
+ sub_807ECBC(&data[1], &data[2], &data[3], &data[4], &data[5]);
+ data[0]++;
+ break;
+ case 1:
+ if (!sub_807EDA0(&data[1], &data[2], &data[3], &data[4], &data[5]))
+ data[0]++;
+ break;
+ }
+}
+
+static void sub_807ECBC(s16 *a0, s16 *a1, s16 *a2, s16 *a3, s16 *a4)
+{
+ s16 x, y;
+ u8 behavior;
+ s32 r1;
+ struct Sprite *sprite;
+ PlayerGetDestCoords(&x, &y);
+ behavior = MapGridGetMetatileBehaviorAt(x, y);
+ if (MetatileBehavior_IsUnknownWarp6E(behavior) || MetatileBehavior_IsUnknownWarp6C(behavior))
+ r1 = 3;
+ else
+ r1 = 4;
+ FieldObjectForceSetSpecialAnim(&gMapObjects[gPlayerAvatar.mapObjectId], sub_8064270(r1));
+ sub_807EBBC(behavior, a0, a1);
+ *a2 = *a0 * 16;
+ *a3 = *a1 * 16;
+ *a4 = 16;
+ sprite = &gSprites[gPlayerAvatar.spriteId];
+ sprite->pos2.x = *a2 >> 5;
+ sprite->pos2.y = *a3 >> 5;
+ *a0 *= -1;
+ *a1 *= -1;
+}
+
+static bool8 sub_807EDA0(s16 *a0, s16 *a1, s16 *a2, s16 *a3, s16 *a4)
+{
+ struct Sprite *sprite;
+ sprite = &gSprites[gPlayerAvatar.spriteId];
+ if (*a4 != 0)
+ {
+ *a2 += *a0;
+ *a3 += *a1;
+ sprite->pos2.x = *a2 >> 5;
+ sprite->pos2.y = *a3 >> 5;
+ (*a4)--;
+ return TRUE;
+ }
+ else
+ {
+ sprite->pos2.x = 0;
+ sprite->pos2.y = 0;
+ return FALSE;
+ }
+}
diff --git a/src/fieldmap.c b/src/fieldmap.c
index 2683eaf62..000b8105b 100644
--- a/src/fieldmap.c
+++ b/src/fieldmap.c
@@ -833,7 +833,7 @@ s32 sub_80596FC(struct MapConnection *connection, s32 x, s32 y)
return FALSE;
}
-struct MapConnection *sub_805973C(s16 x, s16 y)
+struct MapConnection *GetMapConnectionAtPos(s16 x, s16 y)
{
s32 count;
struct MapConnection *connection;
diff --git a/src/help_system.c b/src/help_system.c
index 1fd43fffa..2d96ae9d6 100644
--- a/src/help_system.c
+++ b/src/help_system.c
@@ -25,7 +25,7 @@ struct HelpSystemVideoState
/*0x0c*/ u16 savedBg0Hofs;
/*0x0e*/ u16 savedBg0Vofs;
/*0x10*/ u16 savedBldCnt;
- /*0x12*/ struct TextColor savedTextColor;
+ /*0x12*/ u8 savedTextColor[3];
/*0x15*/ u8 state;
};
@@ -171,9 +171,9 @@ void SaveMapTiles(void)
void SaveMapTextColors(void)
{
SaveTextColors(
- &sVideoState.savedTextColor.fgColor,
- &sVideoState.savedTextColor.bgColor,
- &sVideoState.savedTextColor.shadowColor
+ &sVideoState.savedTextColor[0],
+ &sVideoState.savedTextColor[1],
+ &sVideoState.savedTextColor[2]
);
}
@@ -200,9 +200,9 @@ void RestoreMapTiles(void)
void RestoreMapTextColors(void)
{
RestoreTextColors(
- &sVideoState.savedTextColor.fgColor,
- &sVideoState.savedTextColor.bgColor,
- &sVideoState.savedTextColor.shadowColor
+ &sVideoState.savedTextColor[0],
+ &sVideoState.savedTextColor[1],
+ &sVideoState.savedTextColor[2]
);
}
diff --git a/src/intro.c b/src/intro.c
index bb7242f2f..c15f40dd3 100644
--- a/src/intro.c
+++ b/src/intro.c
@@ -561,7 +561,7 @@ static bool8 sub_80EC62C(void)
}
break;
case 142:
- sub_800B388();
+ ResetSerial();
SetMainCallback2(sub_80EC5B8);
break;
}
diff --git a/src/item.c b/src/item.c
index 3f33f464d..42dfd334e 100644
--- a/src/item.c
+++ b/src/item.c
@@ -326,7 +326,7 @@ void ClearPCItemSlots(void)
}
}
-void ClearItemSlotsInAllBagPockets(void)
+void ClearBag(void)
{
u16 i;
diff --git a/src/item_pc.c b/src/item_pc.c
index 8f1fd1cbf..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"
@@ -125,7 +125,7 @@ static const struct MenuAction sItemPcSubmenuOptions[] = {
{gFameCheckerText_Cancel, {.void_u8 = Task_ItemPcCancel}}
};
-static const struct TextColor gUnknown_8453F8C[] = {
+static const u8 gUnknown_8453F8C[][3] = {
{0, 1, 2},
{0, 2, 3},
{0, 3, 2},
@@ -1111,7 +1111,7 @@ static void unused_ItemPc_AddTextPrinterParameterized(u8 windowId, const u8 * st
static void ItemPc_AddTextPrinterParameterized(u8 windowId, u8 fontId, const u8 * str, u8 x, u8 y, u8 letterSpacing, u8 lineSpacing, u8 speed, u8 colorIdx)
{
- AddTextPrinterParameterized4(windowId, fontId, x, y, letterSpacing, lineSpacing, &gUnknown_8453F8C[colorIdx], speed, str);
+ AddTextPrinterParameterized4(windowId, fontId, x, y, letterSpacing, lineSpacing, gUnknown_8453F8C[colorIdx], speed, str);
}
static void ItemPc_SetBorderStyleOnWindow(u8 windowId)
diff --git a/src/item_use.c b/src/item_use.c
index 3653a1699..c472960bd 100644
--- a/src/item_use.c
+++ b/src/item_use.c
@@ -274,7 +274,7 @@ void ItemUseOnFieldCB_Rod(u8 taskId)
void ItemUseOutOfBattle_Itemfinder(u8 taskId)
{
IncrementGameStat(GAME_STAT_USED_ITEMFINDER);
- sItemUseOnFieldCB = sub_813EC8C;
+ sItemUseOnFieldCB = ItemUseOnFieldCB_Itemfinder;
sub_80A103C(taskId);
}
@@ -435,7 +435,7 @@ void FieldUseFunc_BerryPouch(u8 taskId)
void InitBerryPouchFromBag(void)
{
- InitBerryPouch(0, ReturnToBagFromKeyItem, 0);
+ InitBerryPouch(BERRYPOUCH_FROMFIELD, ReturnToBagFromKeyItem, 0);
}
void Task_InitBerryPouchFromField(u8 taskId)
@@ -444,7 +444,7 @@ void Task_InitBerryPouchFromField(u8 taskId)
{
CleanupOverworldWindowsAndTilemaps();
sub_80A1184();
- InitBerryPouch(0, CB2_ReturnToField, 1);
+ InitBerryPouch(BERRYPOUCH_FROMFIELD, CB2_ReturnToField, 1);
DestroyTask(taskId);
}
}
@@ -457,7 +457,7 @@ void BattleUseFunc_BerryPouch(u8 taskId)
void InitBerryPouchFromBattle(void)
{
- InitBerryPouch(4, sub_8107ECC, 0);
+ InitBerryPouch(BERRYPOUCH_FROMBATTLE, sub_8107ECC, 0);
}
void FieldUseFunc_TeachyTv(u8 taskId)
@@ -852,7 +852,7 @@ void FieldUseFunc_OakStopsYou(u8 taskId)
if (GetPocketByItemId(gSpecialVar_ItemId) == POCKET_BERRY_POUCH)
{
StringExpandPlaceholders(gStringVar4, gUnknown_8416425);
- DisplayItemMessageInBerryPouch(taskId, 4, gStringVar4, sub_813E2B8);
+ DisplayItemMessageInBerryPouch(taskId, 4, gStringVar4, Task_BerryPouch_DestroyDialogueWindowAndRefreshListMenu);
}
else
sub_80A1110(taskId, gTasks[taskId].data[3]);
diff --git a/src/itemfinder.c b/src/itemfinder.c
new file mode 100644
index 000000000..4e723ac03
--- /dev/null
+++ b/src/itemfinder.c
@@ -0,0 +1,659 @@
+#include "global.h"
+#include "task.h"
+#include "new_menu_helpers.h"
+#include "strings.h"
+#include "event_scripts.h"
+#include "map_obj_lock.h"
+#include "script.h"
+#include "sound.h"
+#include "event_data.h"
+#include "field_player_avatar.h"
+#include "field_specials.h"
+#include "fieldmap.h"
+#include "itemfinder.h"
+#include "constants/songs.h"
+
+static void Task_NoResponse_CleanUp(u8 taskId);
+static void Task_ItemfinderResponseSoundsAndAnims(u8 taskId);
+static void Task_ItemfinderUnderfootSoundsAndAnims(u8 taskId);
+static bool8 HiddenItemIsWithinRangeOfPlayer(struct MapEvents * events, u8 taskId);
+static void SetUnderfootHiddenItem(u8 taskId, struct HiddenItemStruct hiddenItem);
+static void SetNormalHiddenItem(u8 taskId);
+static void FindHiddenItemsInConnectedMaps(u8 taskId);
+static void RegisterHiddenItemRelativeCoordsIfCloser(u8 taskId, s16 dx, s16 dy);
+static u8 GetPlayerDirectionTowardsHiddenItem(s16 itemX, s16 itemY);
+static void Task_ItemfinderResponsePrintMessage(u8 taskId);
+static void Task_ItemfinderResponseCleanUp(u8 taskId);
+static void Task_ItemfinderUnderfootPrintMessage(u8 taskId);
+static void Task_ItemfinderUnderfootDigUpItem(u8 taskId);
+static void DestroyArrowAndStarTiles(void);
+static void LoadArrowAndStarTiles(void);
+static void CreateArrowSprite(u8 animNum, u8 direction);
+static void SpriteCallback_Arrow(struct Sprite * sprite);
+static void SpriteCallback_DestroyArrow(struct Sprite * sprite);
+static u8 CreateStarSprite(void);
+static void SpriteCallback_Star(struct Sprite * sprite);
+static void SpriteCallback_DestroyStar(struct Sprite * sprite);
+
+#define ARROW_TILE_TAG 2000
+
+static const u16 sArrowAndStarSpriteTiles[] = INCBIN_U16("data/itemfinder/spr_tiles.4bpp");
+
+static const union AnimCmd sArrowAnim0[] = {
+ ANIMCMD_FRAME( 0, 10),
+ ANIMCMD_END
+};
+
+static const union AnimCmd sArrowAnim1[] = {
+ ANIMCMD_FRAME( 4, 10),
+ ANIMCMD_END
+};
+
+static const union AnimCmd sArrowAnim2[] = {
+ ANIMCMD_FRAME( 8, 10),
+ ANIMCMD_END
+};
+
+static const union AnimCmd sArrowAnim3[] = {
+ ANIMCMD_FRAME(12, 10),
+ ANIMCMD_END
+};
+
+static const union AnimCmd sStarAnim[] = {
+ ANIMCMD_FRAME(16, 10),
+ ANIMCMD_END
+};
+
+static const union AnimCmd *const sArrowAndStarSpriteAnimTable[] = {
+ sArrowAnim0,
+ sArrowAnim1,
+ sArrowAnim2,
+ sArrowAnim3,
+ sStarAnim
+};
+
+static const struct OamData sArrowAndStarSpriteOamData = {
+ .affineMode = ST_OAM_AFFINE_NORMAL,
+ .shape = ST_OAM_SQUARE,
+ .size = ST_OAM_SIZE_1
+};
+
+static const union AffineAnimCmd sAffineAnim_Left[] = {
+ AFFINEANIMCMD_FRAME(0, 0, 0x00, 1),
+ AFFINEANIMCMD_END
+};
+
+static const union AffineAnimCmd sAffineAnim_Down[] = {
+ AFFINEANIMCMD_FRAME(0, 0, 0x40, 1),
+ AFFINEANIMCMD_END
+};
+
+static const union AffineAnimCmd sAffineAnim_Right[] = {
+ AFFINEANIMCMD_FRAME(0, 0, 0x80, 1),
+ AFFINEANIMCMD_END
+};
+
+static const union AffineAnimCmd sAffineAnim_Up[] = {
+ AFFINEANIMCMD_FRAME(0, 0, 0xc0, 1),
+ AFFINEANIMCMD_END
+};
+
+static const union AffineAnimCmd *const sArrowAndStarSpriteAffineAnimTable[] = {
+ sAffineAnim_Left,
+ sAffineAnim_Down,
+ sAffineAnim_Right,
+ sAffineAnim_Up
+};
+
+static const struct SpriteTemplate gUnknown_84647E4 = {
+ .tileTag = ARROW_TILE_TAG,
+ .paletteTag = 0xFFFF,
+ .oam = &sArrowAndStarSpriteOamData,
+ .anims = sArrowAndStarSpriteAnimTable,
+ .affineAnims = sArrowAndStarSpriteAffineAnimTable,
+ .callback = SpriteCallback_Arrow
+};
+
+static const struct SpriteSheet sArrowAndStarSpriteSheet = {
+ .data = sArrowAndStarSpriteTiles,
+ .size = sizeof(sArrowAndStarSpriteTiles),
+ .tag = ARROW_TILE_TAG
+};
+
+#define tItemX data[0]
+#define tItemY data[1]
+#define tHiddenItemFound data[2]
+#define tDingTimer data[3]
+#define tNumDingsRemaining data[4]
+#define tDingNum data[5]
+#define tUnderfoot data[6]
+#define tStartSpriteId data[7]
+
+void ItemUseOnFieldCB_Itemfinder(u8 taskId)
+{
+ u8 i;
+ for (i = 0; i < 16; i++)
+ gTasks[taskId].data[i] = 0;
+ if (HiddenItemIsWithinRangeOfPlayer(gMapHeader.events, taskId) == TRUE)
+ {
+ LoadArrowAndStarTiles();
+ if (gTasks[taskId].tUnderfoot == TRUE)
+ gTasks[taskId].func = Task_ItemfinderUnderfootSoundsAndAnims;
+ else
+ gTasks[taskId].func = Task_ItemfinderResponseSoundsAndAnims;
+ }
+ else
+ {
+ DisplayItemMessageOnField(taskId, 2, gText_NopeTheresNoResponse, Task_NoResponse_CleanUp);
+ }
+}
+
+static void Task_NoResponse_CleanUp(u8 taskId)
+{
+ ClearDialogWindowAndFrame(0, TRUE);
+ sub_80696C0();
+ ScriptContext2_Disable();
+ DestroyTask(taskId);
+}
+
+static void Task_ItemfinderResponseSoundsAndAnims(u8 taskId)
+{
+ s16 *data = gTasks[taskId].data;
+ u8 direction;
+ if (tDingTimer % 25 == 0)
+ {
+ direction = GetPlayerDirectionTowardsHiddenItem(tItemX, tItemY);
+ if (tNumDingsRemaining == 0)
+ {
+ gTasks[taskId].func = Task_ItemfinderResponsePrintMessage;
+ return;
+ }
+ else
+ {
+ PlaySE(SE_TOY_F);
+ CreateArrowSprite(tDingNum, direction);
+ tDingNum++;
+ tNumDingsRemaining--;
+ }
+ }
+ tDingTimer++;
+}
+
+static void Task_ItemfinderUnderfootSoundsAndAnims(u8 taskId)
+{
+ s16 *data = gTasks[taskId].data;
+ if (tDingTimer % 25 == 0)
+ {
+ if (tNumDingsRemaining == 0)
+ {
+ gTasks[taskId].func = Task_ItemfinderUnderfootPrintMessage;
+ return;
+ }
+ else
+ {
+ PlaySE(SE_TOY_F);
+ tStartSpriteId = CreateStarSprite();
+ tDingNum++;
+ tNumDingsRemaining--;
+ }
+ }
+ tDingTimer++;
+}
+
+static bool8 HiddenItemIsWithinRangeOfPlayer(struct MapEvents * events, u8 taskId)
+{
+ s16 x, y, i, dx, dy;
+ PlayerGetDestCoords(&x, &y);
+ gTasks[taskId].tHiddenItemFound = FALSE;
+ for (i = 0; i < events->bgEventCount; i++)
+ {
+ if (events->bgEvents[i].kind == 7 && !FlagGet(GetHiddenItemAttr(events->bgEvents[i].bgUnion.hiddenItem, HIDDEN_ITEM_FLAG)))
+ {
+ dx = events->bgEvents[i].x + 7 - x;
+ dy = events->bgEvents[i].y + 7 - y;
+ if (GetHiddenItemAttr(events->bgEvents[i].bgUnion.hiddenItem, HIDDEN_ITEM_UNDERFOOT) == TRUE)
+ {
+ if (dx == 0 && dy == 0)
+ {
+ SetUnderfootHiddenItem(taskId, events->bgEvents[i].bgUnion.hiddenItem);
+ return TRUE;
+ }
+ }
+ else if (
+ dx >= -7
+ && dx <= 7
+ && dy >= -5
+ && dy <= 5
+ )
+ {
+ RegisterHiddenItemRelativeCoordsIfCloser(taskId, dx, dy);
+ }
+ }
+ }
+ FindHiddenItemsInConnectedMaps(taskId);
+ if (gTasks[taskId].tHiddenItemFound == TRUE)
+ {
+ SetNormalHiddenItem(taskId);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void SetUnderfootHiddenItem(u8 taskId, struct HiddenItemStruct hiddenItem)
+{
+ s16 *data = gTasks[taskId].data;
+ gSpecialVar_0x8004 = GetHiddenItemAttr(hiddenItem, HIDDEN_ITEM_FLAG);
+ gSpecialVar_0x8005 = GetHiddenItemAttr(hiddenItem, HIDDEN_ITEM_ID);
+ gSpecialVar_0x8006 = 1;
+ TV_PrintIntToStringVar(0, gSpecialVar_0x8005);
+ tHiddenItemFound = TRUE;
+ tItemX = 0;
+ tItemY = 0;
+ tNumDingsRemaining = 3;
+ tUnderfoot = TRUE;
+}
+
+static void SetNormalHiddenItem(u8 taskId)
+{
+ s16 *data = gTasks[taskId].data;
+ s16 absY = tItemY;
+ s16 absX = tItemX;
+
+ // The strength of the response increases inversely with distance to the item.
+ if (tItemX == 0 && tItemY == 0)
+ tNumDingsRemaining = 4;
+ else
+ {
+ if (tItemX < 0)
+ absX = tItemX * -1;
+ if (tItemY < 0)
+ absY = tItemY * -1;
+ if (absX > absY)
+ {
+ if (absX > 3)
+ tNumDingsRemaining = 2;
+ else
+ tNumDingsRemaining = 4;
+ }
+ else
+ {
+ if (absY > 3)
+ tNumDingsRemaining = 2;
+ else
+ tNumDingsRemaining = 4;
+ }
+ }
+}
+
+static bool8 HiddenItemAtPos(struct MapEvents * events, s16 x, s16 y)
+{
+ u8 bgEventCount = events->bgEventCount;
+ struct BgEvent * bgEvents = events->bgEvents;
+ u16 eventFlag;
+ int i;
+
+ for (i = 0; i < bgEventCount; i++)
+ {
+ if (
+ bgEvents[i].kind == 7
+ && x == bgEvents[i].x
+ && y == bgEvents[i].y
+ )
+ {
+ eventFlag = GetHiddenItemAttr(bgEvents[i].bgUnion.hiddenItem, HIDDEN_ITEM_FLAG);
+ if (GetHiddenItemAttr(bgEvents[i].bgUnion.hiddenItem, HIDDEN_ITEM_UNDERFOOT) != TRUE && !FlagGet(eventFlag))
+ return TRUE;
+ else
+ return FALSE;
+ }
+ }
+ return FALSE;
+}
+
+static bool8 HiddenItemInConnectedMapAtPos(struct MapConnection * connection, s32 x, s32 y)
+{
+ const struct MapHeader * mapHeader;
+ u16 localX, localY;
+ u32 localOffset;
+ s32 localLength;
+
+ mapHeader = mapconnection_get_mapheader(connection);
+
+ switch (connection->direction)
+ {
+ // same weird temp variable behavior seen in HiddenItemAtPos
+ case 2:
+ localOffset = connection->offset + 7;
+ localX = x - localOffset;
+ localLength = mapHeader->mapData->height - 7;
+ localY = localLength + y; // additions are reversed for some reason
+ break;
+ case 1:
+ localOffset = connection->offset + 7;
+ localX = x - localOffset;
+ localLength = gMapHeader.mapData->height + 7;
+ localY = y - localLength;
+ break;
+ case 3:
+ localLength = mapHeader->mapData->width - 7;
+ localX = localLength + x; // additions are reversed for some reason
+ localOffset = connection->offset + 7;
+ localY = y - localOffset;
+ break;
+ case 4:
+ localLength = gMapHeader.mapData->width + 7;
+ localX = x - localLength;
+ localOffset = connection->offset + 7;
+ localY = y - localOffset;
+ break;
+ default:
+ return FALSE;
+ }
+ return HiddenItemAtPos(mapHeader->events, localX, localY);
+}
+
+static void FindHiddenItemsInConnectedMaps(u8 taskId)
+{
+ s16 x, y;
+ s16 curX, curY;
+ s16 width = gMapHeader.mapData->width + 7;
+ s16 height = gMapHeader.mapData->height + 7;
+
+ s16 var1 = 7;
+ s16 var2 = 7;
+
+ PlayerGetDestCoords(&x, &y);
+
+ for (curX = x - 7; curX <= x + 7; curX++)
+ {
+ for (curY = y - 5; curY <= y + 5; curY++)
+ {
+ if (var1 > curX
+ || curX >= width
+ || var2 > curY
+ || curY >= height)
+ {
+ struct MapConnection * conn = GetMapConnectionAtPos(curX, curY);
+ if (conn != NULL && HiddenItemInConnectedMapAtPos(conn, curX, curY) == TRUE)
+ RegisterHiddenItemRelativeCoordsIfCloser(taskId, curX - x, curY - y);
+ }
+ }
+ }
+}
+
+static void RegisterHiddenItemRelativeCoordsIfCloser(u8 taskId, s16 dx, s16 dy)
+{
+ s16 *data = gTasks[taskId].data;
+ s16 dx2, dy2, dx3, dy3;
+
+ if (tHiddenItemFound == FALSE)
+ {
+ tItemX = dx;
+ tItemY = dy;
+ tHiddenItemFound = TRUE;
+ }
+ else
+ {
+ // tItemX and tItemY contain the player's coordinates.
+ // dx and dy contain the item's coordinates.
+ if (tItemX < 0)
+ dx2 = tItemX * -1; // item is to the left
+ else
+ dx2 = tItemX; // item is to the right
+
+ if (tItemY < 0)
+ dy2 = tItemY * -1; // item is to the north
+ else
+ dy2 = tItemY; // item is to the south
+
+ if (dx < 0)
+ dx3 = dx * -1;
+ else
+ dx3 = dx;
+
+ if (dy < 0)
+ dy3 = dy * -1;
+ else
+ dy3 = dy;
+
+ if (dx2 + dy2 > dx3 + dy3)
+ {
+ tItemX = dx;
+ tItemY = dy;
+ }
+ else
+ {
+ if (dx2 + dy2 == dx3 + dy3 && (dy2 > dy3 || (dy2 == dy3 && tItemY < dy)))
+ {
+ tItemX = dx;
+ tItemY = dy;
+ }
+ }
+ }
+}
+
+static u8 GetPlayerDirectionTowardsHiddenItem(s16 itemX, s16 itemY)
+{
+ s16 abX, abY;
+
+ if (itemX == 0 && itemY == 0)
+ return DIR_NONE; // player is standing on the item.
+
+ // get absolute X distance.
+ if (itemX < 0)
+ abX = itemX * -1;
+ else
+ abX = itemX;
+
+ // get absolute Y distance.
+ if (itemY < 0)
+ abY = itemY * -1;
+ else
+ abY = itemY;
+
+ if (abX > abY)
+ {
+ if (itemX < 0)
+ return DIR_EAST;
+ else
+ return DIR_NORTH;
+ }
+ else
+ {
+ if (abX < abY)
+ {
+ if (itemY < 0)
+ return DIR_SOUTH;
+ else
+ return DIR_WEST;
+ }
+ if (abX == abY)
+ {
+ if (itemY < 0)
+ return DIR_SOUTH;
+ else
+ return DIR_WEST;
+ }
+ return DIR_NONE; // should never get here. return something so it doesnt crash.
+ }
+}
+
+static void Task_ItemfinderResponsePrintMessage(u8 taskId)
+{
+ DisplayItemMessageOnField(taskId, 2, gText_ItemfinderResponding, Task_ItemfinderResponseCleanUp);
+}
+
+static void Task_ItemfinderResponseCleanUp(u8 taskId)
+{
+ DestroyArrowAndStarTiles();
+ ClearDialogWindowAndFrame(0, TRUE);
+ sub_80696C0();
+ ScriptContext2_Disable();
+ DestroyTask(taskId);
+}
+
+static void Task_ItemfinderUnderfootPrintMessage(u8 taskId)
+{
+ DisplayItemMessageOnField(taskId, 2, gText_ItemfinderShakingWildly, Task_ItemfinderUnderfootDigUpItem);
+}
+
+static void Task_ItemfinderUnderfootDigUpItem(u8 taskId)
+{
+ DestroyArrowAndStarTiles();
+ DestroyTask(taskId);
+ ScriptContext1_SetupScript(EventScript_ItemfinderDigUpUnderfootItem);
+ ScriptContext2_Enable();
+}
+
+#undef tStartSpriteId
+#undef tUnderfoot
+#undef tDingNum
+#undef tNumDingsRemaining
+#undef tDingTimer
+#undef tHiddenItemFound
+#undef tItemY
+#undef tItemX
+
+#define spData0 data[0]
+#define spDeltaX data[1]
+#define spDeltaY data[2]
+#define spCurX data[3]
+#define spCurY data[4]
+#define spCenterX data[5]
+#define spCenterY data[6]
+#define spAnimNum data[7]
+
+static void LoadArrowAndStarTiles(void)
+{
+ LoadSpriteSheet(&sArrowAndStarSpriteSheet);
+}
+
+static void DestroyArrowAndStarTiles(void)
+{
+ FreeSpriteTilesByTag(ARROW_TILE_TAG);
+}
+
+static void CreateArrowSprite(u8 animNum, u8 direction)
+{
+ u8 spriteId = CreateSprite(&gUnknown_84647E4, 120, 76, 0);
+ gSprites[spriteId].oam.paletteNum = 0;
+ StartSpriteAnim(&gSprites[spriteId], animNum);
+ gSprites[spriteId].spAnimNum = animNum;
+ gSprites[spriteId].spData0 = 0;
+ gSprites[spriteId].spCurX = 0;
+ gSprites[spriteId].spCurY = 0;
+ gSprites[spriteId].spCenterX = 120;
+ gSprites[spriteId].spCenterY = 76;
+ switch (direction)
+ {
+ case DIR_NONE:
+ switch (GetPlayerFacingDirection())
+ {
+ case DIR_WEST:
+ gSprites[spriteId].spDeltaX = -100;
+ gSprites[spriteId].spDeltaY = 0;
+ StartSpriteAffineAnim(&gSprites[spriteId], 0);
+ break;
+ case DIR_NORTH:
+ gSprites[spriteId].spDeltaX = 0;
+ gSprites[spriteId].spDeltaY = -100;
+ StartSpriteAffineAnim(&gSprites[spriteId], 3);
+ break;
+ case DIR_EAST:
+ gSprites[spriteId].spDeltaX = 100;
+ gSprites[spriteId].spDeltaY = 0;
+ StartSpriteAffineAnim(&gSprites[spriteId], 2);
+ break;
+ case DIR_SOUTH:
+ gSprites[spriteId].spDeltaX = 0;
+ gSprites[spriteId].spDeltaY = 100;
+ StartSpriteAffineAnim(&gSprites[spriteId], 1);
+ break;
+ }
+ break;
+ case DIR_SOUTH:
+ gSprites[spriteId].spDeltaX = 0;
+ gSprites[spriteId].spDeltaY = -100;
+ StartSpriteAffineAnim(&gSprites[spriteId], 3);
+ break;
+ case DIR_NORTH:
+ gSprites[spriteId].spDeltaX = 100;
+ gSprites[spriteId].spDeltaY = 0;
+ StartSpriteAffineAnim(&gSprites[spriteId], 2);
+ break;
+ case DIR_WEST:
+ gSprites[spriteId].spDeltaX = 0;
+ gSprites[spriteId].spDeltaY = 100;
+ StartSpriteAffineAnim(&gSprites[spriteId], 1);
+ break;
+ case DIR_EAST:
+ gSprites[spriteId].spDeltaX = -100;
+ gSprites[spriteId].spDeltaY = 0;
+ break;
+ }
+}
+
+static void SpriteCallback_Arrow(struct Sprite * sprite)
+{
+ s16 x, y;
+ sprite->spCurX += sprite->spDeltaX;
+ sprite->spCurY += sprite->spDeltaY;
+ sprite->pos1.x = sprite->spCenterX + (sprite->spCurX >> 8);
+ sprite->pos1.y = sprite->spCenterY + (sprite->spCurY >> 8);
+ if (sprite->pos1.x <= 104
+ || sprite->pos1.x > 132
+ || sprite->pos1.y <= 60
+ || sprite->pos1.y > 88)
+ sprite->callback = SpriteCallback_DestroyArrow;
+}
+
+static void SpriteCallback_DestroyArrow(struct Sprite * sprite)
+{
+ FreeSpriteOamMatrix(sprite);
+ DestroySprite(sprite);
+}
+
+static u8 CreateStarSprite(void)
+{
+ u8 spriteId = CreateSprite(&gUnknown_84647E4, 120, 76, 0);
+ gSprites[spriteId].oam.paletteNum = 0;
+ gSprites[spriteId].callback = SpriteCallback_Star;
+ StartSpriteAnim(&gSprites[spriteId], 4);
+ gSprites[spriteId].spAnimNum = 0;
+ gSprites[spriteId].spData0 = 0;
+ gSprites[spriteId].spCurX = 0;
+ gSprites[spriteId].spCurY = 0;
+ gSprites[spriteId].spCenterX = 120;
+ gSprites[spriteId].spCenterY = 76;
+ gSprites[spriteId].spDeltaX = 0;
+ gSprites[spriteId].spDeltaY = -100;
+ return spriteId;
+}
+
+static void SpriteCallback_Star(struct Sprite * sprite)
+{
+ s16 x, y;
+ sprite->spCurX += sprite->spDeltaX;
+ sprite->spCurY += sprite->spDeltaY;
+ sprite->pos1.x = sprite->spCenterX + (sprite->spCurX >> 8);
+ sprite->pos1.y = sprite->spCenterY + (sprite->spCurY >> 8);
+ if (sprite->pos1.x <= 104
+ || sprite->pos1.x > 132
+ || sprite->pos1.y <= 60
+ || sprite->pos1.y > 88)
+ sprite->callback = SpriteCallback_DestroyStar;
+}
+
+static void SpriteCallback_DestroyStar(struct Sprite * sprite)
+{
+ DestroySprite(sprite);
+}
+
+#undef spAnimNum
+#undef spCenterY
+#undef spCenterX
+#undef spCurY
+#undef spCurX
+#undef spDeltaY
+#undef spDeltaX
+#undef spData0
diff --git a/src/link.c b/src/link.c
new file mode 100644
index 000000000..9d67c984e
--- /dev/null
+++ b/src/link.c
@@ -0,0 +1,2259 @@
+#include "global.h"
+#include "palette.h"
+#include "bg.h"
+#include "m4a.h"
+#include "scanline_effect.h"
+#include "bg_regs.h"
+#include "gpu_regs.h"
+#include "decompress.h"
+#include "malloc.h"
+#include "save.h"
+#include "battle.h"
+#include "quest_log.h"
+#include "link_rfu.h"
+#include "librfu.h"
+#include "random.h"
+#include "task.h"
+#include "event_data.h"
+#include "string_util.h"
+#include "item_menu.h"
+#include "trade.h"
+#include "text.h"
+#include "sound.h"
+#include "menu.h"
+#include "overworld.h"
+#include "new_menu_helpers.h"
+#include "link.h"
+#include "window.h"
+#include "graphics.h"
+#include "strings.h"
+#include "help_system.h"
+#include "reset_save_heap.h"
+#include "constants/battle.h"
+#include "constants/songs.h"
+
+extern u16 gHeldKeyCodeToSend;
+
+struct BlockTransfer
+{
+ u16 pos;
+ u16 size;
+ const u8 *src;
+ bool8 active;
+ u8 multiplayerId;
+};
+
+struct LinkTestBGInfo
+{
+ u32 screenBaseBlock;
+ u32 paletteNum;
+ u32 dummy_8;
+ u32 dummy_C;
+};
+
+#define SIO_MULTI_CNT ((struct SioMultiCnt *)REG_ADDR_SIOCNT)
+
+static struct BlockTransfer sBlockSend;
+ALIGNED(8) static struct BlockTransfer sBlockRecv[MAX_LINK_PLAYERS];
+static u32 sBlockSendDelayCounter;
+static u32 gUnknown_3000E4C;
+static u8 gUnknown_3000E50;
+static u32 sPlayerDataExchangeStatus;
+static u32 gUnknown_3000E58;
+static u8 sLinkTestLastBlockSendPos;
+ALIGNED(8) static u8 sLinkTestLastBlockRecvPos[MAX_LINK_PLAYERS];
+// File break?
+static u8 sNumVBlanksWithoutSerialIntr;
+static bool8 sSendBufferEmpty;
+static u16 sSendNonzeroCheck;
+static u16 sRecvNonzeroCheck;
+static u8 sChecksumAvailable;
+static u8 sHandshakePlayerCount;
+
+u16 gLinkPartnersHeldKeys[6];
+u32 gLinkDebugSeed;
+struct LinkPlayerBlock gLocalLinkPlayerBlock;
+bool8 gLinkErrorOccurred;
+u32 gLinkDebugFlags;
+u32 gFiller_3003EB4;
+bool8 gRemoteLinkPlayersNotReceived[MAX_LINK_PLAYERS];
+u8 gBlockReceivedStatus[MAX_LINK_PLAYERS];
+u32 gFiller_3003EC0;
+u16 gLinkHeldKeys;
+u16 gRecvCmds[MAX_RFU_PLAYERS][CMD_LENGTH];
+u32 gLinkStatus;
+bool8 gUnknown_3003F24;
+bool8 gUnknown_3003F28;
+bool8 gUnknown_3003F2C[MAX_LINK_PLAYERS];
+bool8 gUnknown_3003F30[MAX_LINK_PLAYERS];
+u16 gUnknown_3003F34;
+u8 gSuppressLinkErrorMessage;
+bool8 gWirelessCommType;
+bool8 gSavedLinkPlayerCount;
+u16 gSendCmd[CMD_LENGTH];
+u8 gSavedMultiplayerId;
+bool8 gReceivedRemoteLinkPlayers;
+struct LinkTestBGInfo gLinkTestBGInfo;
+void (*gLinkCallback)(void);
+u8 gShouldAdvanceLinkState;
+u16 gLinkTestBlockChecksums[MAX_LINK_PLAYERS];
+u8 gBlockRequestType;
+u32 gFiller_3003F94; // file
+u32 gFiller_3003F98; // boundary
+u32 gFiller_3003F9C; // here?
+u8 gLastSendQueueCount;
+struct Link gLink;
+u8 gLastRecvQueueCount;
+u16 gLinkSavedIme;
+
+EWRAM_DATA bool8 gLinkTestDebugValuesEnabled = FALSE;
+EWRAM_DATA bool8 gUnknown_2022111 = FALSE;
+EWRAM_DATA u32 gUnknown_2022114 = 0;
+EWRAM_DATA u16 gBlockRecvBuffer[MAX_RFU_PLAYERS][BLOCK_BUFFER_SIZE / 2] = {};
+EWRAM_DATA u8 gBlockSendBuffer[BLOCK_BUFFER_SIZE] = {};
+EWRAM_DATA bool8 gLinkOpen = FALSE;
+EWRAM_DATA u16 gLinkType = 0;
+EWRAM_DATA u16 gLinkTimeOutCounter = 0;
+EWRAM_DATA struct LinkPlayer gLocalLinkPlayer = {};
+EWRAM_DATA struct LinkPlayer gLinkPlayers[MAX_RFU_PLAYERS] = {};
+EWRAM_DATA struct LinkPlayer gSavedLinkPlayers[MAX_RFU_PLAYERS] = {};
+EWRAM_DATA struct {
+ u32 status;
+ u8 lastRecvQueueCount;
+ u8 lastSendQueueCount;
+ u8 unk_06;
+} sLinkErrorBuffer = {};
+EWRAM_DATA u16 gUnknown_202285C = 0;
+EWRAM_DATA void *gUnknown_2022860 = NULL;
+
+static void InitLocalLinkPlayer(void);
+static void sub_800978C(void);
+static void CB2_LinkTest(void);
+static void ProcessRecvCmds(u8 id);
+static void sub_800A040(void);
+static void ResetBlockSend(void);
+static bool32 InitBlockSend(const void *src, size_t size);
+static void LinkCB_BlockSendBegin(void);
+static void LinkCB_BlockSend(void);
+static void LinkCB_BlockSendEnd(void);
+static void sub_800A3CC(void);
+static void SetBlockReceivedFlag(u8 id);
+static u16 LinkTestCalcBlockChecksum(const u16 *src, u16 size);
+static void LinkTest_prnthex(u32 pos, u8 a0, u8 a1, u8 a2);
+static void LinkCB_RequestPlayerDataExchange(void);
+static void Task_PrintTestData(u8 taskId);
+static void sub_800AB0C(void);
+static void sub_800AB38(void);
+static void sub_800ABD4(void);
+static void sub_800AC00(void);
+static void CheckErrorStatus(void);
+static void CB2_PrintErrorMessage(void);
+static void sub_800B210(void);
+static void DisableSerial(void);
+static void EnableSerial(void);
+static bool8 IsSioMultiMaster(void);
+static void CheckMasterOrSlave(void);
+static void InitTimer(void);
+static void EnqueueSendCmd(u16 *sendCmd);
+static void DequeueRecvCmds(u16 (*recvCmds)[CMD_LENGTH]);
+static void StartTransfer(void);
+static bool8 DoHandshake(void);
+static void DoRecv(void);
+static void DoSend(void);
+static void StopTimer(void);
+static void SendRecvDone(void);
+
+ALIGNED(4) static const u16 sWirelessLinkDisplayPal[] = INCBIN_U16("graphics/interface/wireless_link_display.gbapal");
+static const u16 sWirelessLinkDisplay4bpp[] = INCBIN_U16("graphics/interface/wireless_link_display.4bpp.lz");
+static const u16 sWirelessLinkDisplayBin[] = INCBIN_U16("graphics/interface/wireless_link_display.bin.lz");
+static const u16 sLinkTestFontPal[] = INCBIN_U16("graphics/interface/link_test_font.gbapal");
+static const u16 sLinkTestFontGfx[] = INCBIN_U16("graphics/interface/link_test_font.4bpp");
+
+static const struct BlockRequest sBlockRequests[] = {
+ {gBlockSendBuffer, 200},
+ {gBlockSendBuffer, 200},
+ {gBlockSendBuffer, 100},
+ {gBlockSendBuffer, 220},
+ {gBlockSendBuffer, 40}
+};
+static const char sASCIIGameFreakInc[] = "GameFreak inc.";
+static const char sASCIITestPrint[] = "TEST PRINT\n"
+ "P0\n"
+ "P1\n"
+ "P2\n"
+ "P3";
+
+static const struct BgTemplate sLinkErrorBgTemplates[] = {
+ {
+ .bg = 0,
+ .charBaseIndex = 2,
+ .mapBaseIndex = 31,
+ .priority = 0
+ }, {
+ .bg = 1,
+ .charBaseIndex = 0,
+ .mapBaseIndex = 8,
+ .priority = 1
+ }
+};
+
+static const struct WindowTemplate sLinkErrorWindowTemplates[] = {
+ {
+ .bg = 0,
+ .tilemapLeft = 0,
+ .tilemapTop = 0,
+ .width = 30,
+ .height = 5,
+ .paletteNum = 15,
+ .baseBlock = 0x002
+ }, {
+ .bg = 0,
+ .tilemapLeft = 0,
+ .tilemapTop = 6,
+ .width = 30,
+ .height = 7,
+ .paletteNum = 15,
+ .baseBlock = 0x098
+ }, {
+ .bg = 0,
+ .tilemapLeft = 0,
+ .tilemapTop = 13,
+ .width = 30,
+ .height = 7,
+ .paletteNum = 15,
+ .baseBlock = 0x16A
+ }, DUMMY_WIN_TEMPLATE
+};
+
+static const u8 sLinkErrorTextColor[] = { 0x00, 0x01, 0x02 };
+
+bool8 IsWirelessAdapterConnected(void)
+{
+ if (gUnknown_203ADFA == 2 || gUnknown_203ADFA == 3)
+ return FALSE;
+
+ sub_800B1F4();
+ sub_80F86F4();
+ sub_80FB128(1);
+ if (sub_80FD3A4() == 0x8001)
+ {
+ rfu_REQ_stopMode();
+ rfu_waitREQComplete();
+ return TRUE;
+ }
+ sub_800B210();
+ CloseLink();
+ RestoreSerialTimer3IntrHandlers();
+ return FALSE;
+}
+
+void Task_DestroySelf(u8 taskId)
+{
+ DestroyTask(taskId);
+}
+
+void InitLinkTestBG(u8 paletteNum, u8 bgNum, u8 screenBaseBlock, u8 charBaseBlock, u16 a4)
+{
+ LoadPalette(sLinkTestFontPal, paletteNum * 16, 0x20);
+ DmaCopy16(3, sLinkTestFontGfx, (u16 *)BG_CHAR_ADDR(charBaseBlock) + (16 * a4), sizeof sLinkTestFontGfx);
+ gLinkTestBGInfo.screenBaseBlock = screenBaseBlock;
+ gLinkTestBGInfo.paletteNum = paletteNum;
+ gLinkTestBGInfo.dummy_8 = a4;
+ switch (bgNum)
+ {
+ case 1:
+ SetGpuReg(REG_OFFSET_BG1CNT, BGCNT_SCREENBASE(screenBaseBlock) | BGCNT_PRIORITY(1) | BGCNT_CHARBASE(charBaseBlock));
+ break;
+ case 2:
+ SetGpuReg(REG_OFFSET_BG2CNT, BGCNT_SCREENBASE(screenBaseBlock) | BGCNT_PRIORITY(1) | BGCNT_CHARBASE(charBaseBlock));
+ break;
+ case 3:
+ SetGpuReg(REG_OFFSET_BG3CNT, BGCNT_SCREENBASE(screenBaseBlock) | BGCNT_PRIORITY(1) | BGCNT_CHARBASE(charBaseBlock));
+ break;
+ }
+ SetGpuReg(REG_OFFSET_BG0HOFS + bgNum * 4, 0);
+ SetGpuReg(REG_OFFSET_BG0VOFS + bgNum * 4, 0);
+}
+
+void sub_80095BC(u8 paletteNum, u8 bgNum, u8 screenBaseBlock, u8 charBaseBlock)
+{
+ LoadPalette(sLinkTestFontPal, paletteNum * 16, 0x20);
+ DmaCopy16(3, sLinkTestFontGfx, (u16 *)BG_CHAR_ADDR(charBaseBlock), sizeof sLinkTestFontGfx);
+ gLinkTestBGInfo.screenBaseBlock = screenBaseBlock;
+ gLinkTestBGInfo.paletteNum = paletteNum;
+ gLinkTestBGInfo.dummy_8 = 0;
+ SetGpuReg(gBGControlRegOffsets[bgNum], BGCNT_SCREENBASE(screenBaseBlock) | BGCNT_CHARBASE(charBaseBlock));
+}
+
+void LinkTestScreen(void)
+{
+ int i;
+
+ ResetSpriteData();
+ FreeAllSpritePalettes();
+ ResetTasks();
+ SetVBlankCallback(sub_800978C);
+ ResetBlockSend();
+ gLinkType = 0x1111;
+ OpenLink();
+ SeedRng(gMain.vblankCounter2);
+ for (i = 0; i < MAX_LINK_PLAYERS; i++)
+ {
+ gSaveBlock2Ptr->playerTrainerId[i] = Random() % 256;
+ }
+ InitLinkTestBG(0, 2, 4, 0, 0);
+ SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_MODE_0 | DISPCNT_OBJ_1D_MAP | DISPCNT_BG0_ON | DISPCNT_BG2_ON | DISPCNT_OBJ_ON);
+ CreateTask(Task_DestroySelf, 0);
+ RunTasks();
+ AnimateSprites();
+ BuildOamBuffer();
+ UpdatePaletteFade();
+ gUnknown_3000E58 = 0;
+ InitLocalLinkPlayer();
+ CreateTask(Task_PrintTestData, 0);
+ SetMainCallback2(CB2_LinkTest);
+}
+
+void SetLocalLinkPlayerId(u8 playerId)
+{
+ gLocalLinkPlayer.id = playerId;
+}
+
+static void InitLocalLinkPlayer(void)
+{
+ gLocalLinkPlayer.trainerId = gSaveBlock2Ptr->playerTrainerId[0] | (gSaveBlock2Ptr->playerTrainerId[1] << 8) | (gSaveBlock2Ptr->playerTrainerId[2] << 16) | (gSaveBlock2Ptr->playerTrainerId[3] << 24);
+ StringCopy(gLocalLinkPlayer.name, gSaveBlock2Ptr->playerName);
+ gLocalLinkPlayer.gender = gSaveBlock2Ptr->playerGender;
+ gLocalLinkPlayer.linkType = gLinkType;
+ gLocalLinkPlayer.language = gGameLanguage;
+ gLocalLinkPlayer.version = gGameVersion + 0x4000;
+ gLocalLinkPlayer.lp_field_2 = 0x8000;
+ gLocalLinkPlayer.name[8] = IsNationalPokedexEnabled();
+ if (FlagGet(FLAG_0x844))
+ {
+ gLocalLinkPlayer.name[8] |= 0x10;
+ }
+}
+
+static void sub_800978C(void)
+{
+ LoadOam();
+ ProcessSpriteCopyRequests();
+ TransferPlttBuffer();
+}
+
+void InitLink(void)
+{
+ int i;
+
+ for (i = 0; i < 8; i++)
+ {
+ gSendCmd[i] = 0xEFFF;
+ }
+ gLinkOpen = TRUE;
+ EnableSerial();
+}
+
+void Task_TriggerHandshake(u8 taskId)
+{
+ if (++gTasks[taskId].data[0] == 5)
+ {
+ gShouldAdvanceLinkState = 1;
+ DestroyTask(taskId);
+ }
+}
+
+void OpenLink(void)
+{
+ int i;
+
+ if (!gWirelessCommType)
+ {
+ ResetSerial();
+ InitLink();
+ gLinkCallback = LinkCB_RequestPlayerDataExchange;
+ gLinkVSyncDisabled = FALSE;
+ gLinkErrorOccurred = FALSE;
+ gSuppressLinkErrorMessage = FALSE;
+ ResetBlockReceivedFlags();
+ ResetBlockSend();
+ gUnknown_3000E4C = 0;
+ gUnknown_3003F28 = FALSE;
+ gUnknown_3003F24 = FALSE;
+ gUnknown_3003F34 = 0;
+ CreateTask(Task_TriggerHandshake, 2);
+ }
+ else
+ {
+ sub_80F86F4();
+ }
+ gReceivedRemoteLinkPlayers = 0;
+ for (i = 0; i < MAX_LINK_PLAYERS; i++)
+ {
+ gRemoteLinkPlayersNotReceived[i] = TRUE;
+ gUnknown_3003F30[i] = FALSE;
+ gUnknown_3003F2C[i] = FALSE;
+ }
+}
+
+void CloseLink(void)
+{
+ gReceivedRemoteLinkPlayers = FALSE;
+ if (gWirelessCommType)
+ {
+ sub_80F8DC0();
+ }
+ gLinkOpen = FALSE;
+ DisableSerial();
+}
+
+void TestBlockTransfer(u8 nothing, u8 is, u8 used)
+{
+ u8 i;
+ u8 status;
+
+ if (sLinkTestLastBlockSendPos != sBlockSend.pos)
+ {
+ LinkTest_prnthex(sBlockSend.pos, 2, 3, 2);
+ sLinkTestLastBlockSendPos = sBlockSend.pos;
+ }
+ for (i = 0; i < MAX_LINK_PLAYERS; i++)
+ {
+ if (sLinkTestLastBlockRecvPos[i] != sBlockRecv[i].pos)
+ {
+ LinkTest_prnthex(sBlockRecv[i].pos, 2, i + 4, 2);
+ sLinkTestLastBlockRecvPos[i] = sBlockRecv[i].pos;
+ }
+ }
+ status = GetBlockReceivedStatus();
+ if (status == 0xF) // 0b1111
+ {
+ for (i = 0; i < MAX_LINK_PLAYERS; i++)
+ {
+ if ((status >> i) & 1)
+ {
+ gLinkTestBlockChecksums[i] = LinkTestCalcBlockChecksum(gBlockRecvBuffer[i], sBlockRecv[i].size);
+ ResetBlockReceivedFlag(i);
+ if (gLinkTestBlockChecksums[i] != 0x0342)
+ {
+ gLinkTestDebugValuesEnabled = FALSE;
+ gUnknown_2022111 = FALSE;
+ }
+ }
+ }
+ }
+}
+
+void LinkTestProcessKeyInput(void)
+{
+ if (JOY_NEW(A_BUTTON))
+ {
+ gShouldAdvanceLinkState = 1;
+ }
+ if (JOY_HELD(B_BUTTON))
+ {
+ InitBlockSend(gHeap + 0x4000, 0x2004);
+ }
+ if (JOY_NEW(L_BUTTON))
+ {
+ BeginNormalPaletteFade(0xFFFFFFFF, 0, 16, 0, RGB(2, 0, 0));
+ }
+ if (JOY_NEW(START_BUTTON))
+ {
+ SetSuppressLinkErrorMessage(TRUE);
+ }
+ if (JOY_NEW(R_BUTTON))
+ {
+ TrySavingData(1);
+ }
+ if (JOY_NEW(SELECT_BUTTON))
+ {
+ sub_800AAC0();
+ }
+ if (gLinkTestDebugValuesEnabled)
+ {
+ SetLinkDebugValues(gMain.vblankCounter2, gLinkCallback ? gLinkVSyncDisabled : gLinkVSyncDisabled | 0x10);
+ }
+}
+
+static void CB2_LinkTest(void)
+{
+ LinkTestProcessKeyInput();
+ TestBlockTransfer(1, 1, 0);
+ RunTasks();
+ AnimateSprites();
+ BuildOamBuffer();
+ UpdatePaletteFade();
+}
+
+u16 LinkMain2(const u16 *heldKeys)
+{
+ u8 i;
+
+ if (!gLinkOpen)
+ {
+ return 0;
+ }
+ for (i = 0; i < 8; i++)
+ {
+ gSendCmd[i] = 0;
+ }
+ gLinkHeldKeys = *heldKeys;
+ if (gLinkStatus & LINK_STAT_CONN_ESTABLISHED)
+ {
+ ProcessRecvCmds(SIO_MULTI_CNT->id);
+ if (gLinkCallback != NULL)
+ {
+ gLinkCallback();
+ }
+ CheckErrorStatus();
+ }
+ return gLinkStatus;
+}
+
+void HandleReceiveRemoteLinkPlayer(u8 who)
+{
+ int i;
+ int count;
+
+ count = 0;
+ gRemoteLinkPlayersNotReceived[who] = FALSE;
+ for (i = 0; i < GetLinkPlayerCount_2(); i++)
+ {
+ count += gRemoteLinkPlayersNotReceived[i];
+ }
+ if (count == 0 && gReceivedRemoteLinkPlayers == 0)
+ {
+ gReceivedRemoteLinkPlayers = 1;
+ }
+}
+
+void ProcessRecvCmds(u8 unused)
+{
+ u16 i;
+
+ for (i = 0; i < MAX_LINK_PLAYERS; i++)
+ {
+ gLinkPartnersHeldKeys[i] = 0;
+ if (gRecvCmds[i][0] == 0)
+ {
+ continue;
+ }
+ switch (gRecvCmds[i][0])
+ {
+ case LINKCMD_SEND_LINK_TYPE:
+ {
+ struct LinkPlayerBlock * block;
+
+ InitLocalLinkPlayer();
+ block = &gLocalLinkPlayerBlock;
+ block->linkPlayer = gLocalLinkPlayer;
+ memcpy(block->magic1, sASCIIGameFreakInc, sizeof(block->magic1) - 1);
+ memcpy(block->magic2, sASCIIGameFreakInc, sizeof(block->magic2) - 1);
+ InitBlockSend(block, sizeof(*block));
+ break;
+ }
+ case LINKCMD_SEND_HELD_KEYS:
+ gLinkPartnersHeldKeys[i] = gRecvCmds[i][1];
+ break;
+ case LINKCMD_0x5555:
+ gUnknown_3003F28 = TRUE;
+ break;
+ case LINKCMD_0x5566:
+ gUnknown_3003F28 = TRUE;
+ break;
+ case LINKCMD_INIT_BLOCK:
+ {
+ struct BlockTransfer * blockRecv;
+
+ blockRecv = &sBlockRecv[i];
+ blockRecv->pos = 0;
+ blockRecv->size = gRecvCmds[i][1];
+ blockRecv->multiplayerId = gRecvCmds[i][2];
+ break;
+ }
+ case LINKCMD_CONT_BLOCK:
+ {
+ if (sBlockRecv[i].size > BLOCK_BUFFER_SIZE)
+ {
+ u16 *buffer;
+ u16 j;
+
+ buffer = (u16 *)gDecompressionBuffer;
+ for (j = 0; j < CMD_LENGTH - 1; j++)
+ {
+ buffer[(sBlockRecv[i].pos / 2) + j] = gRecvCmds[i][j + 1];
+ }
+ }
+ else
+ {
+ u16 j;
+
+ for (j = 0; j < CMD_LENGTH - 1; j++)
+ {
+ gBlockRecvBuffer[i][(sBlockRecv[i].pos / 2) + j] = gRecvCmds[i][j + 1];
+ }
+ }
+
+ sBlockRecv[i].pos += (CMD_LENGTH - 1) * 2;
+
+ if (sBlockRecv[i].pos >= sBlockRecv[i].size)
+ {
+ if (gRemoteLinkPlayersNotReceived[i] == TRUE)
+ {
+ struct LinkPlayerBlock * block;
+ struct LinkPlayer * linkPlayer;
+
+ block = (struct LinkPlayerBlock *)&gBlockRecvBuffer[i];
+ linkPlayer = &gLinkPlayers[i];
+ *linkPlayer = block->linkPlayer;
+ if ((linkPlayer->version & 0xFF) == VERSION_RUBY || (linkPlayer->version & 0xFF) == VERSION_SAPPHIRE)
+ {
+ linkPlayer->name[10] = 0;
+ linkPlayer->name[9] = 0;
+ linkPlayer->name[8] = 0;
+ }
+ sub_800B284(linkPlayer);
+ if (strcmp(block->magic1, sASCIIGameFreakInc) != 0
+ || strcmp(block->magic2, sASCIIGameFreakInc) != 0)
+ {
+ SetMainCallback2(CB2_LinkError);
+ }
+ else
+ {
+ HandleReceiveRemoteLinkPlayer(i);
+ }
+ }
+ else
+ {
+ SetBlockReceivedFlag(i);
+ }
+ }
+ }
+ break;
+ case LINKCMD_0x5FFF:
+ gUnknown_3003F30[i] = TRUE;
+ break;
+ case LINKCMD_0x2FFE:
+ gUnknown_3003F2C[i] = TRUE;
+ break;
+ case LINKCMD_0xAAAA:
+ sub_800A3CC();
+ break;
+ case LINKCMD_0xCCCC:
+ SendBlock(0, sBlockRequests[gRecvCmds[i][1]].address, sBlockRequests[gRecvCmds[i][1]].size);
+ break;
+ case LINKCMD_SEND_HELD_KEYS_2:
+ gLinkPartnersHeldKeys[i] = gRecvCmds[i][1];
+ break;
+ }
+ }
+}
+
+void BuildSendCmd(u16 command)
+{
+ switch (command)
+ {
+ case LINKCMD_SEND_LINK_TYPE:
+ gSendCmd[0] = LINKCMD_SEND_LINK_TYPE;
+ gSendCmd[1] = gLinkType;
+ break;
+ case LINKCMD_0x2FFE:
+ gSendCmd[0] = LINKCMD_0x2FFE;
+ break;
+ case LINKCMD_SEND_HELD_KEYS:
+ gSendCmd[0] = LINKCMD_SEND_HELD_KEYS;
+ gSendCmd[1] = gMain.heldKeys;
+ break;
+ case LINKCMD_0x5555:
+ gSendCmd[0] = LINKCMD_0x5555;
+ break;
+ case LINKCMD_0x6666:
+ gSendCmd[0] = LINKCMD_0x6666;
+ gSendCmd[1] = 0;
+ break;
+ case LINKCMD_0x7777:
+ {
+ u8 i;
+
+ gSendCmd[0] = LINKCMD_0x7777;
+ for (i = 0; i < 5; i++)
+ {
+ gSendCmd[i + 1] = 0xEE;
+ }
+ break;
+ }
+ case LINKCMD_INIT_BLOCK:
+ gSendCmd[0] = LINKCMD_INIT_BLOCK;
+ gSendCmd[1] = sBlockSend.size;
+ gSendCmd[2] = sBlockSend.multiplayerId + 0x80;
+ break;
+ case LINKCMD_0xAAAA:
+ gSendCmd[0] = LINKCMD_0xAAAA;
+ break;
+ case LINKCMD_0xAAAB:
+ gSendCmd[0] = LINKCMD_0xAAAB;
+ gSendCmd[1] = gSpecialVar_ItemId;
+ break;
+ case LINKCMD_0xCCCC:
+ gSendCmd[0] = LINKCMD_0xCCCC;
+ gSendCmd[1] = gBlockRequestType;
+ break;
+ case LINKCMD_0x5FFF:
+ gSendCmd[0] = LINKCMD_0x5FFF;
+ gSendCmd[1] = gUnknown_3003F34;
+ break;
+ case LINKCMD_0x5566:
+ gSendCmd[0] = LINKCMD_0x5566;
+ break;
+ case LINKCMD_SEND_HELD_KEYS_2:
+ if (gHeldKeyCodeToSend == 0 || gLinkTransferringData)
+ {
+ break;
+ }
+ gSendCmd[0] = LINKCMD_SEND_HELD_KEYS_2;
+ gSendCmd[1] = gHeldKeyCodeToSend;
+ break;
+ }
+}
+
+void sub_8009FE8(void)
+{
+ if (gWirelessCommType)
+ {
+ sub_80F9828();
+ }
+ gLinkCallback = sub_800A040;
+}
+
+bool32 IsSendingKeysToLink(void)
+{
+ if (gWirelessCommType)
+ {
+ return IsSendingKeysToRfu();
+ }
+ if (gLinkCallback == sub_800A040)
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void sub_800A040(void)
+{
+ if (gReceivedRemoteLinkPlayers == TRUE)
+ {
+ BuildSendCmd(LINKCMD_SEND_HELD_KEYS_2);
+ }
+}
+
+void ClearLinkCallback(void)
+{
+ gLinkCallback = NULL;
+}
+
+void ClearLinkCallback_2(void)
+{
+ if (gWirelessCommType)
+ {
+ Rfu_set_zero();
+ }
+ else
+ {
+ gLinkCallback = NULL;
+ }
+}
+
+u8 GetLinkPlayerCount(void)
+{
+ if (gWirelessCommType)
+ {
+ return GetRfuPlayerCount();
+ }
+ return EXTRACT_PLAYER_COUNT(gLinkStatus);
+}
+
+void OpenLinkTimed(void)
+{
+ sPlayerDataExchangeStatus = 0;
+ gLinkTimeOutCounter = 0;
+ OpenLink();
+}
+
+u8 GetLinkPlayerDataExchangeStatusTimed(int lower, int upper)
+{
+ int i;
+ int count;
+ u32 index;
+ u8 cmpVal;
+ u32 linkType1;
+ u32 linkType2;
+
+ count = 0;
+ if (gReceivedRemoteLinkPlayers == TRUE)
+ {
+ cmpVal = GetLinkPlayerCount_2();
+ if (lower > cmpVal || cmpVal > upper)
+ {
+ sPlayerDataExchangeStatus = EXCHANGE_STAT_6;
+ return 6;
+ }
+ else
+ {
+ if (GetLinkPlayerCount() == 0)
+ {
+ gLinkErrorOccurred = TRUE;
+ CloseLink();
+ }
+ for (i = 0, index = 0; i < GetLinkPlayerCount(); index++, i++)
+ {
+ if (gLinkPlayers[index].linkType == gLinkPlayers[0].linkType)
+ {
+ count++;
+ }
+ }
+ if (count == GetLinkPlayerCount())
+ {
+ if (gLinkPlayers[0].linkType == 0x1133)
+ {
+ switch (sub_804FB34())
+ {
+ case 0:
+ sPlayerDataExchangeStatus = EXCHANGE_COMPLETE;
+ break;
+ case 1:
+ sPlayerDataExchangeStatus = EXCHANGE_STAT_4;
+ break;
+ case 2:
+ sPlayerDataExchangeStatus = EXCHANGE_STAT_5;
+ break;
+ }
+ }
+ else
+ {
+ sPlayerDataExchangeStatus = EXCHANGE_COMPLETE;
+ }
+ }
+ else
+ {
+ sPlayerDataExchangeStatus = EXCHANGE_IN_PROGRESS;
+ }
+ }
+ }
+ else if (++gLinkTimeOutCounter > 600)
+ {
+ sPlayerDataExchangeStatus = EXCHANGE_TIMED_OUT;
+ }
+ return sPlayerDataExchangeStatus;
+}
+
+bool8 IsLinkPlayerDataExchangeComplete(void)
+{
+ u8 i;
+ u8 count;
+ bool8 retval;
+
+ count = 0;
+ for (i = 0; i < GetLinkPlayerCount(); i++)
+ {
+ if (gLinkPlayers[i].linkType == gLinkPlayers[0].linkType)
+ {
+ count++;
+ }
+ }
+ if (count == GetLinkPlayerCount())
+ {
+ retval = TRUE;
+ sPlayerDataExchangeStatus = EXCHANGE_COMPLETE;
+ }
+ else
+ {
+ retval = FALSE;
+ sPlayerDataExchangeStatus = EXCHANGE_IN_PROGRESS;
+ }
+ return retval;
+}
+
+u32 GetLinkPlayerTrainerId(u8 who)
+{
+ return gLinkPlayers[who].trainerId;
+}
+
+void ResetLinkPlayers(void)
+{
+ int i;
+
+ for (i = 0; i <= MAX_LINK_PLAYERS; i++)
+ {
+ gLinkPlayers[i] = (struct LinkPlayer){};
+ }
+}
+
+static void ResetBlockSend(void)
+{
+ sBlockSend.active = FALSE;
+ sBlockSend.pos = 0;
+ sBlockSend.size = 0;
+ sBlockSend.src = NULL;
+}
+
+static bool32 InitBlockSend(const void *src, size_t size)
+{
+ if (sBlockSend.active)
+ {
+ return FALSE;
+ }
+ sBlockSend.multiplayerId = GetMultiplayerId();
+ sBlockSend.active = TRUE;
+ sBlockSend.size = size;
+ sBlockSend.pos = 0;
+ if (size > 0x100)
+ {
+ sBlockSend.src = src;
+ }
+ else
+ {
+ if (src != gBlockSendBuffer)
+ {
+ memcpy(gBlockSendBuffer, src, size);
+ }
+ sBlockSend.src = gBlockSendBuffer;
+ }
+ BuildSendCmd(LINKCMD_INIT_BLOCK);
+ gLinkCallback = LinkCB_BlockSendBegin;
+ sBlockSendDelayCounter = 0;
+ return TRUE;
+}
+
+static void LinkCB_BlockSendBegin(void)
+{
+ if (++sBlockSendDelayCounter > 2)
+ {
+ gLinkCallback = LinkCB_BlockSend;
+ }
+}
+
+static void LinkCB_BlockSend(void)
+{
+ int i;
+ const u8 *src;
+
+ src = sBlockSend.src;
+ gSendCmd[0] = LINKCMD_CONT_BLOCK;
+ for (i = 0; i < 7; i++)
+ {
+ gSendCmd[i + 1] = (src[sBlockSend.pos + i * 2 + 1] << 8) | src[sBlockSend.pos + i * 2];
+ }
+ sBlockSend.pos += 14;
+ if (sBlockSend.size <= sBlockSend.pos)
+ {
+ sBlockSend.active = FALSE;
+ gLinkCallback = LinkCB_BlockSendEnd;
+ }
+}
+
+static void LinkCB_BlockSendEnd(void)
+{
+ gLinkCallback = NULL;
+}
+void sub_800A3AC(void)
+{
+ GetMultiplayerId();
+ BuildSendCmd(LINKCMD_SEND_HELD_KEYS);
+ gUnknown_2022114++;
+}
+
+static void sub_800A3CC(void)
+{
+ gUnknown_2022114 = 0;
+ gLinkCallback = sub_800A3AC;
+}
+
+
+u32 sub_800A3E8(void)
+{
+ return gUnknown_2022114;
+}
+
+void sub_800A3F4(void)
+{
+ BuildSendCmd(LINKCMD_0xAAAA);
+}
+
+u8 GetMultiplayerId(void)
+{
+ if (gWirelessCommType == TRUE)
+ {
+ return rfu_get_multiplayer_id();
+ }
+ return SIO_MULTI_CNT->id;
+}
+
+u8 bitmask_all_link_players_but_self(void)
+{
+ u8 mpId;
+
+ mpId = GetMultiplayerId();
+ return ((1 << MAX_LINK_PLAYERS) - 1) ^ (1 << mpId);
+}
+
+bool8 SendBlock(u8 unused, const void *src, u16 size)
+{
+ if (gWirelessCommType == TRUE)
+ {
+ return Rfu_InitBlockSend(src, size);
+ }
+ return InitBlockSend(src, size);
+}
+
+bool8 sub_800A474(u8 a0)
+{
+ if (gWirelessCommType == TRUE)
+ {
+ return sub_80FA0F8(a0);
+ }
+ if (gLinkCallback == NULL)
+ {
+ gBlockRequestType = a0;
+ BuildSendCmd(LINKCMD_0xCCCC);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+bool8 IsLinkTaskFinished(void)
+{
+ if (gWirelessCommType == TRUE)
+ {
+ return IsRfuTaskFinished();
+ }
+ return gLinkCallback == NULL;
+}
+
+u8 GetBlockReceivedStatus(void)
+{
+ if (gWirelessCommType == TRUE)
+ {
+ return Rfu_GetBlockReceivedStatus();
+ }
+ return (gBlockReceivedStatus[3] << 3) | (gBlockReceivedStatus[2] << 2) | (gBlockReceivedStatus[1] << 1) | (gBlockReceivedStatus[0] << 0);
+}
+
+void SetBlockReceivedFlag(u8 who)
+{
+ if (gWirelessCommType == TRUE)
+ {
+ Rfu_SetBlockReceivedFlag(who);
+ }
+ else
+ {
+ gBlockReceivedStatus[who] = TRUE;
+ }
+}
+
+void ResetBlockReceivedFlags(void)
+{
+ int i;
+
+ if (gWirelessCommType == TRUE)
+ {
+ for (i = 0; i < MAX_RFU_PLAYERS; i++)
+ {
+ Rfu_ResetBlockReceivedFlag(i);
+ }
+ }
+ else
+ {
+ for (i = 0; i < MAX_LINK_PLAYERS; i++)
+ {
+ gBlockReceivedStatus[i] = FALSE;
+ }
+ }
+}
+
+void ResetBlockReceivedFlag(u8 who)
+{
+ if (gWirelessCommType == TRUE)
+ {
+ Rfu_ResetBlockReceivedFlag(who);
+ }
+ else if (gBlockReceivedStatus[who])
+ {
+ gBlockReceivedStatus[who] = FALSE;
+ }
+}
+
+void CheckShouldAdvanceLinkState(void)
+{
+ if ((gLinkStatus & LINK_STAT_MASTER) && EXTRACT_PLAYER_COUNT(gLinkStatus) > 1)
+ {
+ gShouldAdvanceLinkState = 1;
+ }
+}
+
+static u16 LinkTestCalcBlockChecksum(const u16 *src, u16 size)
+{
+ u16 chksum;
+ u16 i;
+
+ chksum = 0;
+ for (i = 0; i < size / 2; i++)
+ {
+ chksum += src[i];
+ }
+ return chksum;
+}
+
+void LinkTest_prnthexchar(char a0, u8 a1, u8 a2)
+{
+ u16 *vAddr;
+
+ vAddr = (u16 *)BG_SCREEN_ADDR(gLinkTestBGInfo.screenBaseBlock);
+ vAddr[a2 * 32 + a1] = (gLinkTestBGInfo.paletteNum << 12) | (a0 + 1 + gLinkTestBGInfo.dummy_8);
+}
+
+void LinkTest_prntchar(char a0, u8 a1, u8 a2)
+{
+ u16 *vAddr;
+
+ vAddr = (u16 *)BG_SCREEN_ADDR(gLinkTestBGInfo.screenBaseBlock);
+ vAddr[a2 * 32 + a1] = (gLinkTestBGInfo.paletteNum << 12) | (a0 + gLinkTestBGInfo.dummy_8);
+}
+
+static void LinkTest_prnthex(u32 pos, u8 a0, u8 a1, u8 a2)
+{
+ char sp[32 / 2];
+ int i;
+
+ for (i = 0; i < a2; i++)
+ {
+ sp[i] = pos & 0xf;
+ pos >>= 4;
+ }
+ for (i = a2 - 1; i >= 0; i--)
+ {
+ LinkTest_prnthexchar(sp[i], a0, a1);
+ a0++;
+ }
+}
+
+void LinkTest_prntstr(const char *a0, u8 a1, u8 a2)
+{
+ int r6;
+ int i;
+ int r5;
+
+ r5 = 0;
+ r6 = 0;
+ for (i = 0; a0[i] != 0; a0++)
+ {
+ if (a0[i] == *"\n")
+ {
+ r5++;
+ r6 = 0;
+ }
+ else
+ {
+ LinkTest_prntchar(a0[i], a1 + r6, a2 + r5);
+ r6++;
+ }
+ }
+}
+
+static void LinkCB_RequestPlayerDataExchange(void)
+{
+ if (gLinkStatus & LINK_STAT_MASTER)
+ {
+ BuildSendCmd(LINKCMD_SEND_LINK_TYPE);
+ }
+ gLinkCallback = NULL;
+}
+
+static void Task_PrintTestData(u8 taskId)
+{
+ char sp[32];
+ int i;
+
+ strcpy(sp, sASCIITestPrint);
+ LinkTest_prntstr(sp, 5, 2);
+ LinkTest_prnthex(gShouldAdvanceLinkState, 2, 1, 2);
+ LinkTest_prnthex(gLinkStatus, 15, 1, 8);
+ LinkTest_prnthex(gLink.state, 2, 10, 2);
+ LinkTest_prnthex(EXTRACT_PLAYER_COUNT(gLinkStatus), 15, 10, 2);
+ LinkTest_prnthex(GetMultiplayerId(), 15, 12, 2);
+ LinkTest_prnthex(gLastSendQueueCount, 25, 1, 2);
+ LinkTest_prnthex(gLastRecvQueueCount, 25, 2, 2);
+ LinkTest_prnthex(GetBlockReceivedStatus(), 15, 5, 2);
+ LinkTest_prnthex(gLinkDebugSeed, 2, 12, 8);
+ LinkTest_prnthex(gLinkDebugFlags, 2, 13, 8);
+ LinkTest_prnthex(GetSioMultiSI(), 25, 5, 1);
+ LinkTest_prnthex(IsSioMultiMaster(), 25, 6, 1);
+ LinkTest_prnthex(IsLinkConnectionEstablished(), 25, 7, 1);
+ LinkTest_prnthex(HasLinkErrorOccurred(), 25, 8, 1);
+ for (i = 0; i < MAX_LINK_PLAYERS; i++)
+ {
+ LinkTest_prnthex(gLinkTestBlockChecksums[i], 10, 4 + i, 4);
+ }
+}
+
+void SetLinkDebugValues(u32 seed, u32 flags)
+{
+ gLinkDebugSeed = seed;
+ gLinkDebugFlags = flags;
+}
+
+u8 sub_800A8A4(void)
+{
+ int i;
+ u8 flags;
+
+ flags = 0;
+ for (i = 0; i < gSavedLinkPlayerCount; i++)
+ {
+ flags |= (1 << i);
+ }
+ return flags;
+}
+
+u8 sub_800A8D4(void)
+{
+ int i;
+ u8 flags;
+
+ flags = 0;
+ for (i = 0; i < GetLinkPlayerCount(); i++)
+ {
+ flags |= (1 << i);
+ }
+ return flags;
+}
+
+void sub_800A900(u8 a0)
+{
+ int i;
+
+ gSavedLinkPlayerCount = a0;
+ gSavedMultiplayerId = GetMultiplayerId();
+ for (i = 0; i < MAX_RFU_PLAYERS; i++)
+ {
+ gSavedLinkPlayers[i] = gLinkPlayers[i];
+ }
+}
+
+// The number of players when trading began. This is frequently compared against the
+// current number of connected players to check if anyone dropped out.
+u8 GetSavedPlayerCount(void)
+{
+ return gSavedLinkPlayerCount;
+}
+
+u8 GetSavedMultiplayerId(void)
+{
+ return gSavedMultiplayerId;
+}
+
+bool8 sub_800A95C(void)
+{
+ int i;
+ unsigned count;
+
+ count = 0;
+ for (i = 0; i < gSavedLinkPlayerCount; i++)
+ {
+ if (gLinkPlayers[i].trainerId == gSavedLinkPlayers[i].trainerId)
+ {
+ count++;
+ }
+ }
+ if (count == gSavedLinkPlayerCount)
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void sub_800A9A4(void)
+{
+ u8 i;
+
+ for (i = 0; i < gSavedLinkPlayerCount; i++)
+ {
+ if (gSavedLinkPlayers[i].trainerId != gLinkPlayers[i].trainerId || StringCompare(gSavedLinkPlayers[i].name, gLinkPlayers[i].name) != 0)
+ {
+ gLinkErrorOccurred = TRUE;
+ CloseLink();
+ SetMainCallback2(CB2_LinkError);
+ }
+ }
+}
+
+void sub_800AA24(void)
+{
+ gSavedLinkPlayerCount = 0;
+ gSavedMultiplayerId = 0;
+}
+
+u8 GetLinkPlayerCount_2(void)
+{
+ return EXTRACT_PLAYER_COUNT(gLinkStatus);
+}
+
+bool8 IsLinkMaster(void)
+{
+ if (gWirelessCommType)
+ {
+ return Rfu_IsMaster();
+ }
+ return EXTRACT_MASTER(gLinkStatus);
+}
+
+u8 sub_800AA74(void)
+{
+ return gUnknown_3000E50;
+}
+
+void sub_800AA80(u16 a0)
+{
+ if (gWirelessCommType == TRUE)
+ {
+ task_add_05_task_del_08FA224_when_no_RfuFunc();
+ }
+ else
+ {
+ if (gLinkCallback == NULL)
+ {
+ gLinkCallback = sub_800AB0C;
+ gUnknown_3003F24 = FALSE;
+ gUnknown_3003F34 = a0;
+ }
+ }
+}
+
+void sub_800AAC0(void)
+{
+ if (gWirelessCommType == TRUE)
+ {
+ task_add_05_task_del_08FA224_when_no_RfuFunc();
+ }
+ else
+ {
+ if (gLinkCallback != NULL)
+ {
+ gUnknown_202285C++;
+ }
+ else
+ {
+ gLinkCallback = sub_800AB0C;
+ gUnknown_3003F24 = FALSE;
+ gUnknown_3003F34 = 0;
+ }
+ }
+}
+
+static void sub_800AB0C(void)
+{
+ if (gLastRecvQueueCount == 0)
+ {
+ BuildSendCmd(LINKCMD_0x5FFF);
+ gLinkCallback = sub_800AB38;
+ }
+}
+
+static void sub_800AB38(void)
+{
+ int i;
+ unsigned count;
+ u8 linkPlayerCount;
+
+ linkPlayerCount = GetLinkPlayerCount();
+ count = 0;
+ for (i = 0; i < linkPlayerCount; i++)
+ {
+ if (gUnknown_3003F30[i])
+ {
+ count++;
+ }
+ }
+ if (count == linkPlayerCount)
+ {
+ gBattleTypeFlags &= ~(BATTLE_TYPE_20 | 0xFFFF0000);
+ gLinkVSyncDisabled = TRUE;
+ CloseLink();
+ gLinkCallback = NULL;
+ gUnknown_3003F24 = TRUE;
+ }
+}
+
+void sub_800AB9C(void)
+{
+ if (gWirelessCommType == TRUE)
+ {
+ sub_80FA42C();
+ }
+ else
+ {
+ if (gLinkCallback == NULL)
+ {
+ gLinkCallback = sub_800ABD4;
+ }
+ gUnknown_3003F24 = FALSE;
+ }
+}
+
+static void sub_800ABD4(void)
+{
+ if (gLastRecvQueueCount == 0)
+ {
+ BuildSendCmd(LINKCMD_0x2FFE);
+ gLinkCallback = sub_800AC00;
+ }
+}
+
+static void sub_800AC00(void)
+{
+ u8 i;
+ u8 linkPlayerCount;
+
+ linkPlayerCount = GetLinkPlayerCount();
+ for (i = 0; i < linkPlayerCount; i++)
+ {
+ if (!gUnknown_3003F2C[i])
+ {
+ break;
+ }
+ }
+ if (i == linkPlayerCount)
+ {
+ for (i = 0; i < MAX_LINK_PLAYERS; i++)
+ {
+ gUnknown_3003F2C[i] = FALSE;
+ }
+ gLinkCallback = NULL;
+ }
+}
+
+static void CheckErrorStatus(void)
+{
+ if (gLinkOpen && EXTRACT_LINK_ERRORS(gLinkStatus))
+ {
+ if (!gSuppressLinkErrorMessage)
+ {
+ sLinkErrorBuffer.status = gLinkStatus;
+ sLinkErrorBuffer.lastRecvQueueCount = gLastRecvQueueCount;
+ sLinkErrorBuffer.lastSendQueueCount = gLastSendQueueCount;
+ SetMainCallback2(CB2_LinkError);
+ }
+ gLinkErrorOccurred = TRUE;
+ CloseLink();
+ }
+}
+
+void sub_800ACBC(u32 status, u8 lastSendQueueCount, u8 lastRecvQueueCount, u8 unk_06)
+{
+ sLinkErrorBuffer.status = status;
+ sLinkErrorBuffer.lastSendQueueCount = lastSendQueueCount;
+ sLinkErrorBuffer.lastRecvQueueCount = lastRecvQueueCount;
+ sLinkErrorBuffer.unk_06 = unk_06;
+}
+
+void CB2_LinkError(void)
+{
+ u8 *tilemapBuffer;
+
+ SetGpuReg(REG_OFFSET_DISPCNT, 0);
+ m4aMPlayStop(&gMPlayInfo_SE1);
+ m4aMPlayStop(&gMPlayInfo_SE2);
+ m4aMPlayStop(&gMPlayInfo_SE3);
+ InitHeap(gHeap, HEAP_SIZE);
+ ResetSpriteData();
+ FreeAllSpritePalettes();
+ ResetPaletteFadeControl();
+ FillPalette(0, 0, 2);
+ ResetTasks();
+ ScanlineEffect_Stop();
+ if (gWirelessCommType)
+ {
+ if (!sLinkErrorBuffer.unk_06)
+ {
+ gWirelessCommType = 3;
+ }
+ sub_80F85F8();
+ }
+ SetVBlankCallback(sub_800978C);
+ ResetBgsAndClearDma3BusyFlags(0);
+ InitBgsFromTemplates(0, sLinkErrorBgTemplates, 2);
+ gUnknown_2022860 = tilemapBuffer = malloc(0x800);
+ SetBgTilemapBuffer(1, tilemapBuffer);
+ if (InitWindows(sLinkErrorWindowTemplates))
+ {
+ DeactivateAllTextPrinters();
+ ResetTempTileDataBuffers();
+ SetGpuReg(REG_OFFSET_BLDALPHA, 0);
+ SetGpuReg(REG_OFFSET_BG0HOFS, 0);
+ SetGpuReg(REG_OFFSET_BG0VOFS, 0);
+ SetGpuReg(REG_OFFSET_BG1HOFS, 0);
+ SetGpuReg(REG_OFFSET_BG1VOFS, 0);
+ ClearGpuRegBits(REG_OFFSET_DISPCNT, DISPCNT_WIN0_ON | DISPCNT_WIN1_ON | DISPCNT_OBJWIN_ON);
+ LoadPalette(gTMCaseMainWindowPalette, 0xf0, 0x20);
+ gSoftResetDisabled = FALSE;
+ CreateTask(Task_DestroySelf, 0);
+ StopMapMusic();
+ gMain.callback1 = NULL;
+ RunTasks();
+ AnimateSprites();
+ BuildOamBuffer();
+ UpdatePaletteFade();
+ SetMainCallback2(CB2_PrintErrorMessage);
+ }
+}
+
+void sub_800AE1C(void)
+{
+ DecompressAndLoadBgGfxUsingHeap(1, sWirelessLinkDisplay4bpp, FALSE, 0, 0);
+ CopyToBgTilemapBuffer(1, sWirelessLinkDisplayBin, 0, 0);
+ CopyBgTilemapBufferToVram(1);
+ LoadPalette(sWirelessLinkDisplayPal, 0, 0x20);
+ FillWindowPixelBuffer(0, PIXEL_FILL(0));
+ FillWindowPixelBuffer(2, PIXEL_FILL(0));
+ AddTextPrinterParameterized3(0, 3, 2, 5, sLinkErrorTextColor, 0, gText_CommErrorEllipsis);
+ AddTextPrinterParameterized3(2, 3, 2, 2, sLinkErrorTextColor, 0, gText_MoveCloserToLinkPartner);
+ PutWindowTilemap(0);
+ PutWindowTilemap(2);
+ CopyWindowToVram(0, 0);
+ CopyWindowToVram(2, 3);
+ ShowBg(0);
+ ShowBg(1);
+}
+
+void sub_800AED0(void)
+{
+ FillWindowPixelBuffer(1, PIXEL_FILL(0));
+ FillWindowPixelBuffer(2, PIXEL_FILL(0));
+ AddTextPrinterParameterized3(1, 3, 2, 0, sLinkErrorTextColor, 0, gText_CommErrorCheckConnections);
+ PutWindowTilemap(1);
+ PutWindowTilemap(2);
+ CopyWindowToVram(1, 0);
+ CopyWindowToVram(2, 3);
+ ShowBg(0);
+}
+
+static void CB2_PrintErrorMessage(void)
+{
+ switch (gMain.state)
+ {
+ case 00:
+ if (sLinkErrorBuffer.unk_06)
+ {
+ sub_800AE1C();
+ }
+ else
+ {
+ sub_800AED0();
+ }
+ break;
+ case 30:
+ PlaySE(SE_BOO);
+ break;
+ case 60:
+ PlaySE(SE_BOO);
+ break;
+ case 90:
+ PlaySE(SE_BOO);
+ break;
+ case 130:
+ if (gWirelessCommType == 2)
+ {
+ AddTextPrinterParameterized3(0, 3, 2, 20, sLinkErrorTextColor, 0, gText_ABtnTitleScreen);
+ }
+ else if (gWirelessCommType == 1)
+ {
+ AddTextPrinterParameterized3(0, 3, 2, 20, sLinkErrorTextColor, 0, gText_ABtnRegistrationCounter);
+ }
+ break;
+ }
+ if (gMain.state == 160)
+ {
+ if (gWirelessCommType == 1)
+ {
+ if (JOY_NEW(A_BUTTON))
+ {
+ sub_812B484();
+ PlaySE(SE_PIN);
+ gWirelessCommType = 0;
+ sLinkErrorBuffer.unk_06 = 0;
+ sub_8079B7C();
+ }
+ }
+ else if (gWirelessCommType == 2)
+ {
+ if (JOY_NEW(A_BUTTON))
+ {
+ sub_812B484();
+ rfu_REQ_stopMode();
+ rfu_waitREQComplete();
+ DoSoftReset();
+ }
+ }
+ }
+ if (gMain.state != 160)
+ {
+ gMain.state++;
+ }
+}
+
+bool8 GetSioMultiSI(void)
+{
+ return (REG_SIOCNT & 0x04) != 0;
+}
+
+static bool8 IsSioMultiMaster(void)
+{
+ return (REG_SIOCNT & 0x8) && !(REG_SIOCNT & 0x04);
+}
+
+bool8 IsLinkConnectionEstablished(void)
+{
+ return EXTRACT_CONN_ESTABLISHED(gLinkStatus);
+}
+
+void SetSuppressLinkErrorMessage(bool8 flag)
+{
+ gSuppressLinkErrorMessage = flag;
+}
+
+bool8 HasLinkErrorOccurred(void)
+{
+ return gLinkErrorOccurred;
+}
+
+void sub_800B0B4(void)
+{
+ struct LinkPlayerBlock * block;
+
+ InitLocalLinkPlayer();
+ block = &gLocalLinkPlayerBlock;
+ block->linkPlayer = gLocalLinkPlayer;
+ memcpy(block->magic1, sASCIIGameFreakInc, sizeof(block->magic1) - 1);
+ memcpy(block->magic2, sASCIIGameFreakInc, sizeof(block->magic2) - 1);
+ memcpy(gBlockSendBuffer, block, sizeof(*block));
+}
+
+void sub_800B110(u32 who)
+{
+ u8 who_ = who;
+ struct LinkPlayerBlock * block;
+ struct LinkPlayer * player;
+
+ block = (struct LinkPlayerBlock *)gBlockRecvBuffer[who_];
+ player = &gLinkPlayers[who_];
+ *player = block->linkPlayer;
+ sub_800B284(player);
+ if (strcmp(block->magic1, sASCIIGameFreakInc) != 0 || strcmp(block->magic2, sASCIIGameFreakInc) != 0)
+ {
+ SetMainCallback2(CB2_LinkError);
+ }
+}
+
+bool8 HandleLinkConnection(void)
+{
+ bool32 r4;
+ bool32 r5;
+
+ if (gWirelessCommType == 0)
+ {
+ gLinkStatus = LinkMain1(&gShouldAdvanceLinkState, gSendCmd, gRecvCmds);
+ LinkMain2(&gMain.heldKeys);
+ if ((gLinkStatus & LINK_STAT_RECEIVED_NOTHING) && sub_8058318() == TRUE)
+ {
+ return TRUE;
+ }
+ }
+ else
+ {
+ r4 = sub_80FAE94();
+ r5 = sub_80FAEF0();
+ if (sub_8058318() == TRUE)
+ {
+ if (r4 == TRUE || IsRfuRecvQueueEmpty() || r5)
+ {
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+void sub_800B1F4(void)
+{
+ if (gReceivedRemoteLinkPlayers == 0)
+ {
+ gWirelessCommType = 1;
+ }
+}
+
+static void sub_800B210(void)
+{
+ if (gReceivedRemoteLinkPlayers == 0)
+ {
+ gWirelessCommType = 0;
+ }
+}
+
+void sub_800B22C(void)
+{
+ if (gReceivedRemoteLinkPlayers == 0)
+ {
+ gWirelessCommType = 0;
+ }
+}
+
+u32 GetLinkRecvQueueLength(void)
+{
+ if (gWirelessCommType != 0)
+ {
+ return GetRfuRecvQueueLength();
+ }
+ return gLink.recvQueue.count;
+}
+
+bool32 sub_800B270(void)
+{
+ if (GetLinkRecvQueueLength() > 2)
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void sub_800B284(struct LinkPlayer * player)
+{
+ player->name[10] = player->name[8];
+ ConvertInternationalString(player->name, player->language);
+}
+
+// File break?
+
+static void DisableSerial(void)
+{
+ DisableInterrupts(INTR_FLAG_TIMER3 | INTR_FLAG_SERIAL);
+ REG_SIOCNT = SIO_MULTI_MODE;
+ REG_TM3CNT_H = 0;
+ REG_IF = INTR_FLAG_TIMER3 | INTR_FLAG_SERIAL;
+ REG_SIOMLT_SEND = 0;
+ REG_SIOMLT_RECV = 0;
+ CpuFill32(0, &gLink, sizeof(gLink));
+}
+
+static void EnableSerial(void)
+{
+ DisableInterrupts(INTR_FLAG_TIMER3 | INTR_FLAG_SERIAL);
+ REG_RCNT = 0;
+ REG_SIOCNT = SIO_MULTI_MODE;
+ REG_SIOCNT |= SIO_115200_BPS | SIO_INTR_ENABLE;
+ EnableInterrupts(INTR_FLAG_SERIAL);
+ REG_SIOMLT_SEND = 0;
+ CpuFill32(0, &gLink, sizeof(gLink));
+ sNumVBlanksWithoutSerialIntr = 0;
+ sSendNonzeroCheck = 0;
+ sRecvNonzeroCheck = 0;
+ sChecksumAvailable = 0;
+ sHandshakePlayerCount = 0;
+ gLastSendQueueCount = 0;
+ gLastRecvQueueCount = 0;
+}
+
+void ResetSerial(void)
+{
+ EnableSerial();
+ DisableSerial();
+}
+
+u32 LinkMain1(u8 *shouldAdvanceLinkState, u16 *sendCmd, u16 (*recvCmds)[CMD_LENGTH])
+{
+ u32 retVal;
+ u32 retVal2;
+
+ switch (gLink.state)
+ {
+ case LINK_STATE_START0:
+ DisableSerial();
+ gLink.state = LINK_STATE_START1;
+ break;
+ case LINK_STATE_START1:
+ if (*shouldAdvanceLinkState == 1)
+ {
+ EnableSerial();
+ gLink.state = LINK_STATE_HANDSHAKE;
+ }
+ break;
+ case LINK_STATE_HANDSHAKE:
+ switch (*shouldAdvanceLinkState)
+ {
+ default:
+ CheckMasterOrSlave();
+ break;
+ case 1:
+ if (gLink.isMaster == LINK_MASTER && gLink.playerCount > 1)
+ {
+ gLink.handshakeAsMaster = TRUE;
+ }
+ break;
+ case 2:
+ gLink.state = LINK_STATE_START0;
+ REG_SIOMLT_SEND = 0;
+ break;
+ }
+ break;
+ case LINK_STATE_INIT_TIMER:
+ InitTimer();
+ gLink.state = LINK_STATE_CONN_ESTABLISHED;
+ // fallthrough
+ case LINK_STATE_CONN_ESTABLISHED:
+ EnqueueSendCmd(sendCmd);
+ DequeueRecvCmds(recvCmds);
+ break;
+ }
+ *shouldAdvanceLinkState = 0;
+ retVal = gLink.localId;
+ retVal |= (gLink.playerCount << LINK_STAT_PLAYER_COUNT_SHIFT);
+ if (gLink.isMaster == LINK_MASTER)
+ {
+ retVal |= LINK_STAT_MASTER;
+ }
+ {
+ u32 receivedNothing = gLink.receivedNothing << LINK_STAT_RECEIVED_NOTHING_SHIFT;
+ u32 link_field_F = gLink.link_field_F << LINK_STAT_UNK_FLAG_9_SHIFT;
+ u32 hardwareError = gLink.hardwareError << LINK_STAT_ERROR_HARDWARE_SHIFT;
+ u32 badChecksum = gLink.badChecksum << LINK_STAT_ERROR_CHECKSUM_SHIFT;
+ u32 queueFull = gLink.queueFull << LINK_STAT_ERROR_QUEUE_FULL_SHIFT;
+ u32 val;
+
+ if (gLink.state == LINK_STATE_CONN_ESTABLISHED)
+ {
+ val = LINK_STAT_CONN_ESTABLISHED;
+ val |= receivedNothing;
+ val |= retVal;
+ val |= link_field_F;
+ val |= hardwareError;
+ val |= badChecksum;
+ val |= queueFull;
+ }
+ else
+ {
+ val = retVal;
+ val |= receivedNothing;
+ val |= link_field_F;
+ val |= hardwareError;
+ val |= badChecksum;
+ val |= queueFull;
+ }
+
+ retVal = val;
+ }
+
+ if (gLink.lag == LAG_MASTER)
+ {
+ retVal |= LINK_STAT_ERROR_LAG_MASTER;
+ }
+
+ if (gLink.localId >= MAX_LINK_PLAYERS)
+ {
+ retVal |= LINK_STAT_ERROR_INVALID_ID;
+ }
+
+ retVal2 = retVal;
+ if (gLink.lag == LAG_SLAVE)
+ {
+ retVal2 |= LINK_STAT_ERROR_LAG_SLAVE;
+ }
+
+ return retVal2;
+}
+
+static void CheckMasterOrSlave(void)
+{
+ u32 terminals;
+
+ terminals = *(vu32 *)REG_ADDR_SIOCNT & (SIO_MULTI_SD | SIO_MULTI_SI);
+ if (terminals == SIO_MULTI_SD && gLink.localId == 0)
+ {
+ gLink.isMaster = LINK_MASTER;
+ }
+ else
+ {
+ gLink.isMaster = LINK_SLAVE;
+ }
+}
+
+static void InitTimer(void)
+{
+ if (gLink.isMaster)
+ {
+ REG_TM3CNT_L = -197;
+ REG_TM3CNT_H = TIMER_64CLK | TIMER_INTR_ENABLE;
+ EnableInterrupts(INTR_FLAG_TIMER3);
+ }
+}
+
+static void EnqueueSendCmd(u16 *sendCmd)
+{
+ u8 i;
+ u8 offset;
+
+ gLinkSavedIme = REG_IME;
+ REG_IME = 0;
+ if (gLink.sendQueue.count < QUEUE_CAPACITY)
+ {
+ offset = gLink.sendQueue.pos + gLink.sendQueue.count;
+ if (offset >= QUEUE_CAPACITY)
+ {
+ offset -= QUEUE_CAPACITY;
+ }
+ for (i = 0; i < CMD_LENGTH; i++)
+ {
+ sSendNonzeroCheck |= *sendCmd;
+ gLink.sendQueue.data[i][offset] = *sendCmd;
+ *sendCmd = 0;
+ sendCmd++;
+ }
+ }
+ else
+ {
+ gLink.queueFull = QUEUE_FULL_SEND;
+ }
+ if (sSendNonzeroCheck)
+ {
+ gLink.sendQueue.count++;
+ sSendNonzeroCheck = 0;
+ }
+ REG_IME = gLinkSavedIme;
+ gLastSendQueueCount = gLink.sendQueue.count;
+}
+
+static void DequeueRecvCmds(u16 (*recvCmds)[CMD_LENGTH])
+{
+ u8 i;
+ u8 j;
+
+ gLinkSavedIme = REG_IME;
+ REG_IME = 0;
+ if (gLink.recvQueue.count == 0)
+ {
+ for (i = 0; i < gLink.playerCount; i++)
+ {
+ for (j = 0; j < CMD_LENGTH; j++)
+ {
+ recvCmds[i][j] = 0;
+ }
+ }
+
+ gLink.receivedNothing = TRUE;
+ }
+ else
+ {
+ for (i = 0; i < gLink.playerCount; i++)
+ {
+ for (j = 0; j < CMD_LENGTH; j++)
+ {
+ recvCmds[i][j] = gLink.recvQueue.data[i][j][gLink.recvQueue.pos];
+ }
+ }
+ gLink.recvQueue.count--;
+ gLink.recvQueue.pos++;
+ if (gLink.recvQueue.pos >= QUEUE_CAPACITY)
+ {
+ gLink.recvQueue.pos = 0;
+ }
+ gLink.receivedNothing = FALSE;
+ }
+ REG_IME = gLinkSavedIme;
+}
+
+void LinkVSync(void)
+{
+ if (gLink.isMaster)
+ {
+ switch (gLink.state)
+ {
+ case LINK_STATE_CONN_ESTABLISHED:
+ if (gLink.serialIntrCounter < 9)
+ {
+ if (gLink.hardwareError != TRUE)
+ {
+ gLink.lag = LAG_MASTER;
+ }
+ else
+ {
+ StartTransfer();
+ }
+ }
+ else if (gLink.lag != LAG_MASTER)
+ {
+ gLink.serialIntrCounter = 0;
+ StartTransfer();
+ }
+ break;
+ case LINK_STATE_HANDSHAKE:
+ StartTransfer();
+ break;
+ }
+ }
+ else if (gLink.state == LINK_STATE_CONN_ESTABLISHED || gLink.state == LINK_STATE_HANDSHAKE)
+ {
+ if (++sNumVBlanksWithoutSerialIntr > 10)
+ {
+ if (gLink.state == LINK_STATE_CONN_ESTABLISHED)
+ {
+ gLink.lag = LAG_SLAVE;
+ }
+ if (gLink.state == LINK_STATE_HANDSHAKE)
+ {
+ gLink.playerCount = 0;
+ gLink.link_field_F = FALSE;
+ }
+ }
+ }
+}
+
+void Timer3Intr(void)
+{
+ StopTimer();
+ StartTransfer();
+}
+
+void SerialCB(void)
+{
+ gLink.localId = SIO_MULTI_CNT->id;
+ switch (gLink.state)
+ {
+ case LINK_STATE_CONN_ESTABLISHED:
+ gLink.hardwareError = SIO_MULTI_CNT->error;
+ DoRecv();
+ DoSend();
+ SendRecvDone();
+ break;
+ case LINK_STATE_HANDSHAKE:
+ if (DoHandshake())
+ {
+ if (gLink.isMaster)
+ {
+ gLink.state = LINK_STATE_INIT_TIMER;
+ gLink.serialIntrCounter = 8;
+ }
+ else
+ {
+ gLink.state = LINK_STATE_CONN_ESTABLISHED;
+ }
+ }
+ break;
+ }
+ gLink.serialIntrCounter++;
+ sNumVBlanksWithoutSerialIntr = 0;
+ if (gLink.serialIntrCounter == 8)
+ {
+ gLastRecvQueueCount = gLink.recvQueue.count;
+ }
+}
+
+static void StartTransfer(void)
+{
+ REG_SIOCNT |= SIO_START;
+}
+
+static bool8 DoHandshake(void)
+{
+ u8 i;
+ u8 playerCount;
+ u16 minRecv;
+
+ playerCount = 0;
+ minRecv = 0xFFFF;
+ if (gLink.handshakeAsMaster == TRUE)
+ {
+ REG_SIOMLT_SEND = MASTER_HANDSHAKE;
+ }
+ else
+ {
+ REG_SIOMLT_SEND = SLAVE_HANDSHAKE;
+ }
+ *(u64 *)gLink.tempRecvBuffer = REG_SIOMLT_RECV;
+ REG_SIOMLT_RECV = 0;
+ gLink.handshakeAsMaster = FALSE;
+ for (i = 0; i < 4; i++)
+ {
+ if ((gLink.tempRecvBuffer[i] & ~0x3) == SLAVE_HANDSHAKE || gLink.tempRecvBuffer[i] == MASTER_HANDSHAKE)
+ {
+ playerCount++;
+ if (minRecv > gLink.tempRecvBuffer[i] && gLink.tempRecvBuffer[i] != 0)
+ {
+ minRecv = gLink.tempRecvBuffer[i];
+ }
+ }
+ else
+ {
+ if (gLink.tempRecvBuffer[i] != 0xFFFF)
+ {
+ playerCount = 0;
+ }
+ break;
+ }
+ }
+ gLink.playerCount = playerCount;
+ if (gLink.playerCount > 1 && gLink.playerCount == sHandshakePlayerCount && gLink.tempRecvBuffer[0] == MASTER_HANDSHAKE)
+ {
+ return TRUE;
+ }
+ if (gLink.playerCount > 1)
+ {
+ gLink.link_field_F = (minRecv & 3) + 1;
+ }
+ else
+ {
+ gLink.link_field_F = 0;
+ }
+ sHandshakePlayerCount = gLink.playerCount;
+ return FALSE;
+}
+
+static void DoRecv(void)
+{
+ u16 recv[4];
+ u8 i;
+ u8 index;
+
+ *(u64 *)recv = REG_SIOMLT_RECV;
+ if (gLink.sendCmdIndex == 0)
+ {
+ for (i = 0; i < gLink.playerCount; i++)
+ {
+ if (gLink.checksum != recv[i] && sChecksumAvailable)
+ {
+ gLink.badChecksum = TRUE;
+ }
+ }
+ gLink.checksum = 0;
+ sChecksumAvailable = TRUE;
+ }
+ else
+ {
+ index = gLink.recvQueue.pos + gLink.recvQueue.count;
+ if (index >= QUEUE_CAPACITY)
+ {
+ index -= QUEUE_CAPACITY;
+ }
+ if (gLink.recvQueue.count < QUEUE_CAPACITY)
+ {
+ for (i = 0; i < gLink.playerCount; i++)
+ {
+ gLink.checksum += recv[i];
+ sRecvNonzeroCheck |= recv[i];
+ gLink.recvQueue.data[i][gLink.recvCmdIndex][index] = recv[i];
+ }
+ }
+ else
+ {
+ gLink.queueFull = QUEUE_FULL_RECV;
+ }
+ gLink.recvCmdIndex++;
+ if (gLink.recvCmdIndex == CMD_LENGTH && sRecvNonzeroCheck)
+ {
+ gLink.recvQueue.count++;
+ sRecvNonzeroCheck = 0;
+ }
+ }
+}
+
+static void DoSend(void)
+{
+ if (gLink.sendCmdIndex == CMD_LENGTH)
+ {
+ REG_SIOMLT_SEND = gLink.checksum;
+ if (!sSendBufferEmpty)
+ {
+ gLink.sendQueue.count--;
+ gLink.sendQueue.pos++;
+ if (gLink.sendQueue.pos >= QUEUE_CAPACITY)
+ {
+ gLink.sendQueue.pos = 0;
+ }
+ }
+ else
+ {
+ sSendBufferEmpty = FALSE;
+ }
+ }
+ else
+ {
+ if (!sSendBufferEmpty && gLink.sendQueue.count == 0)
+ {
+ sSendBufferEmpty = TRUE;
+ }
+ if (sSendBufferEmpty)
+ {
+ REG_SIOMLT_SEND = 0;
+ }
+ else
+ {
+ REG_SIOMLT_SEND = gLink.sendQueue.data[gLink.sendCmdIndex][gLink.sendQueue.pos];
+ }
+ gLink.sendCmdIndex++;
+ }
+}
+
+static void StopTimer(void)
+{
+ if (gLink.isMaster)
+ {
+ REG_TM3CNT_H &= ~TIMER_ENABLE;
+ REG_TM3CNT_L = -197;
+ }
+}
+
+static void SendRecvDone(void)
+{
+ if (gLink.recvCmdIndex == CMD_LENGTH)
+ {
+ gLink.sendCmdIndex = 0;
+ gLink.recvCmdIndex = 0;
+ }
+ else if (gLink.isMaster)
+ {
+ REG_TM3CNT_H |= TIMER_ENABLE;
+ }
+}
+
+void ResetSendBuffer(void)
+{
+ u8 i;
+ u8 j;
+
+ gLink.sendQueue.count = 0;
+ gLink.sendQueue.pos = 0;
+ for (i = 0; i < CMD_LENGTH; i++)
+ {
+ for (j = 0; j < QUEUE_CAPACITY; j++)
+ {
+ gLink.sendQueue.data[i][j] = 0xEFFF;
+ }
+ }
+}
+
+void ResetRecvBuffer(void)
+{
+ u8 i;
+ u8 j;
+ u8 k;
+
+ gLink.recvQueue.count = 0;
+ gLink.recvQueue.pos = 0;
+ for (i = 0; i < MAX_LINK_PLAYERS; i++)
+ {
+ for (j = 0; j < CMD_LENGTH; j++)
+ {
+ for (k = 0; k < QUEUE_CAPACITY; k++)
+ {
+ gLink.recvQueue.data[i][j][k] = 0xEFFF;
+ }
+ }
+ }
+}
diff --git a/src/list_menu.c b/src/list_menu.c
index 5f2bd02b3..d25eb3faf 100644
--- a/src/list_menu.c
+++ b/src/list_menu.c
@@ -368,21 +368,21 @@ static u8 ListMenuInitInternal(struct ListMenuTemplate *listMenuTemplate, u16 sc
static void ListMenuPrint(struct ListMenu *list, const u8 *str, u8 x, u8 y)
{
- struct TextColor colors;
+ u8 colors[3];
if (gListMenuOverride.enabled)
{
- colors.fgColor = gListMenuOverride.fillValue;
- colors.bgColor = gListMenuOverride.cursorPal;
- colors.shadowColor = gListMenuOverride.cursorShadowPal;
- AddTextPrinterParameterized4(list->template.windowId, gListMenuOverride.fontId, x, y, gListMenuOverride.lettersSpacing, 0, &colors, TEXT_SPEED_FF, str);
+ colors[0] = gListMenuOverride.fillValue;
+ colors[1] = gListMenuOverride.cursorPal;
+ colors[2] = gListMenuOverride.cursorShadowPal;
+ AddTextPrinterParameterized4(list->template.windowId, gListMenuOverride.fontId, x, y, gListMenuOverride.lettersSpacing, 0, colors, TEXT_SPEED_FF, str);
gListMenuOverride.enabled = FALSE;
}
else
{
- colors.fgColor = list->template.fillValue;
- colors.bgColor = list->template.cursorPal;
- colors.shadowColor = list->template.cursorShadowPal;
- AddTextPrinterParameterized4(list->template.windowId, list->template.fontId, x, y, list->template.lettersSpacing, 0, &colors, TEXT_SPEED_FF, str);
+ colors[0] = list->template.fillValue;
+ colors[1] = list->template.cursorPal;
+ colors[2] = list->template.cursorShadowPal;
+ AddTextPrinterParameterized4(list->template.windowId, list->template.fontId, x, y, list->template.lettersSpacing, 0, colors, TEXT_SPEED_FF, str);
}
}
diff --git a/src/mailbox_pc.c b/src/mailbox_pc.c
new file mode 100644
index 000000000..0ebffc67e
--- /dev/null
+++ b/src/mailbox_pc.c
@@ -0,0 +1,143 @@
+#include "global.h"
+#include "malloc.h"
+#include "window.h"
+#include "menu.h"
+#include "string_util.h"
+#include "new_menu_helpers.h"
+#include "list_menu.h"
+#include "player_pc.h"
+#include "strings.h"
+#include "menu_indicators.h"
+#include "sound.h"
+#include "constants/songs.h"
+
+static EWRAM_DATA u8 sWindowIds[3] = {};
+static EWRAM_DATA struct ListMenuItem * sListMenuItems = NULL;
+
+static void MoveCursorFunc(s32 itemIndex, bool8 onInit, struct ListMenu * list);
+
+static const struct WindowTemplate sWindowTemplates[] = {
+ {
+ .bg = 0,
+ .tilemapLeft = 1,
+ .tilemapTop = 1,
+ .width = 10,
+ .height = 2,
+ .paletteNum = 15,
+ .baseBlock = 0x008
+ }, {
+ .bg = 0,
+ .tilemapLeft = 19,
+ .tilemapTop = 1,
+ .width = 10,
+ .height = 18,
+ .paletteNum = 15,
+ .baseBlock = 0x01c
+ }, {
+ .bg = 0,
+ .tilemapLeft = 1,
+ .tilemapTop = 1,
+ .width = 15,
+ .height = 8,
+ .paletteNum = 15,
+ .baseBlock = 0x01c
+ }
+};
+
+static const u8 sTextColor[3] = {1, 2, 3};
+static const u8 sString_Dummy[] = _("");
+
+bool8 MailboxPC_InitBuffers(u8 num)
+{
+ u8 i;
+
+ sListMenuItems = Alloc(sizeof(struct ListMenuItem) * (num + 1));
+ if (sListMenuItems == NULL)
+ return FALSE;
+ for (i = 0; i < NELEMS(sWindowIds); i++)
+ sWindowIds[i] = 0xFF;
+ return TRUE;
+}
+
+u8 MailboxPC_GetAddWindow(u8 winIdx)
+{
+ if (sWindowIds[winIdx] == 0xFF)
+ {
+ sWindowIds[winIdx] = AddWindow(&sWindowTemplates[winIdx]);
+ SetStdWindowBorderStyle(sWindowIds[winIdx], 0);
+ }
+ return sWindowIds[winIdx];
+}
+
+void MailboxPC_RemoveWindow(u8 winIdx)
+{
+ ClearStdWindowAndFrameToTransparent(sWindowIds[winIdx], FALSE);
+ ClearWindowTilemap(sWindowIds[winIdx]);
+ RemoveWindow(sWindowIds[winIdx]);
+ sWindowIds[winIdx] = 0xFF;
+}
+
+u8 MailboxPC_GetWindowId(u8 winIdx)
+{
+ return sWindowIds[winIdx];
+}
+
+static void ItemPrintFunc(u8 windowId, s32 itemId, u8 y)
+{
+ u8 strbuf[30];
+ if (itemId != -2)
+ {
+ StringCopy(strbuf, gSaveBlock1Ptr->mail[itemId + PARTY_SIZE].playerName);
+ if (StringLength(strbuf) <= 5)
+ ConvertInternationalString(strbuf, LANGUAGE_JAPANESE);
+ AddTextPrinterParameterized4(windowId, 2, 8, y, 0, 0, sTextColor, -1, strbuf);
+ }
+}
+
+u8 MailboxPC_InitListMenu(struct PlayerPC_Unk_203AAC4 * playerPcStruct)
+{
+ u16 i;
+ for (i = 0; i < playerPcStruct->unk_5; i++)
+ {
+ sListMenuItems[i].label = sString_Dummy;
+ sListMenuItems[i].index = i;
+ }
+ sListMenuItems[i].label = gFameCheckerText_Cancel;
+ sListMenuItems[i].index = -2;
+
+ gMultiuseListMenuTemplate.items = sListMenuItems;
+ gMultiuseListMenuTemplate.totalItems = playerPcStruct->unk_5 + 1;
+ gMultiuseListMenuTemplate.windowId = sWindowIds[1];
+ gMultiuseListMenuTemplate.header_X = 0;
+ gMultiuseListMenuTemplate.item_X = GetMenuCursorDimensionByFont(2, 0);
+ gMultiuseListMenuTemplate.cursor_X = 0;
+ gMultiuseListMenuTemplate.lettersSpacing = 0;
+ gMultiuseListMenuTemplate.itemVerticalPadding = 2;
+ gMultiuseListMenuTemplate.maxShowed = 8;
+ gMultiuseListMenuTemplate.fontId = 2;
+ gMultiuseListMenuTemplate.upText_Y = 10;
+ gMultiuseListMenuTemplate.cursorPal = 2;
+ gMultiuseListMenuTemplate.fillValue = 1;
+ gMultiuseListMenuTemplate.cursorShadowPal = 3;
+ gMultiuseListMenuTemplate.moveCursorFunc = MoveCursorFunc;
+ gMultiuseListMenuTemplate.itemPrintFunc = ItemPrintFunc;
+ gMultiuseListMenuTemplate.cursorKind = 0;
+ gMultiuseListMenuTemplate.scrollMultiple = 0;
+ return ListMenuInit(&gMultiuseListMenuTemplate, playerPcStruct->scrollOffset, playerPcStruct->selectedRow);
+}
+
+static void MoveCursorFunc(s32 itemIndex, bool8 onInit, struct ListMenu * list)
+{
+ if (onInit != TRUE)
+ PlaySE(SE_SELECT);
+}
+
+void MailboxPC_AddScrollIndicatorArrows(struct PlayerPC_Unk_203AAC4 * playerPcStruct)
+{
+ playerPcStruct->unk_A = AddScrollIndicatorArrowPairParameterized(2, 0xC2, 0xC, 0x94, playerPcStruct->unk_5 - playerPcStruct->unk_4 + 1, 110, 110, &playerPcStruct->scrollOffset);
+}
+
+void MailboxPC_DestroyListMenuBuffer(void)
+{
+ Free(sListMenuItems);
+}
diff --git a/src/map_preview_screen.c b/src/map_preview_screen.c
index b913d5fe8..7d9fa935e 100644
--- a/src/map_preview_screen.c
+++ b/src/map_preview_screen.c
@@ -447,21 +447,21 @@ u16 sub_80F8318(u8 mapsec)
{
u16 windowId;
u32 xctr;
- struct TextColor color[0];
+ u8 color[0];
windowId = AddWindow(&sMapNameWindow);
FillWindowPixelBuffer(windowId, PIXEL_FILL(1));
PutWindowTilemap(windowId);
- color->fgColor = 1; // Access violation
- color->bgColor = 4; // Access violation
- color->shadowColor = 3; // Access violation
+ color[0] = 1; // Access violation
+ color[1] = 4; // Access violation
+ color[2] = 3; // Access violation
GetMapName(gStringVar4, mapsec, 0);
xctr = 104 - GetStringWidth(2, gStringVar4, 0);
AddTextPrinterParameterized4(windowId, 2, xctr / 2, 2, 0, 0, color/* Access violation */, -1, gStringVar4);
return windowId;
}
-bool8 sub_80F83B0(void)
+bool32 sub_80F83B0(void)
{
if (FuncIsActiveTask(sub_80F83D0) == TRUE)
{
diff --git a/src/menu.c b/src/menu.c
index bef442d84..b8a68145c 100644
--- a/src/menu.c
+++ b/src/menu.c
@@ -41,12 +41,7 @@ static void WindowFunc_DrawStdFrameWithCustomTileAndPalette(u8 bg, u8 tilemapLef
static void WindowFunc_ClearStdWindowAndFrameToTransparent(u8 bg, u8 tilemapLeft, u8 tilemapTop, u8 width, u8 height, u8 paletteNum);
static u8 MultichoiceGrid_MoveCursor(s8 deltaX, s8 deltaY);
-static const struct TextColor gUnknown_8456618 =
-{
- .fgColor = 15,
- .bgColor = 1,
- .shadowColor = 2,
-};
+static const u8 gUnknown_8456618[3] = {15, 1, 2};
void DrawDialogFrameWithCustomTileAndPalette(u8 windowId, bool8 copyToVram, u16 tileNum, u8 paletteNum)
{
@@ -205,7 +200,7 @@ void TopBarWindowPrintString(const u8 *string, u8 unused, bool8 copyToVram)
PutWindowTilemap(sTopBarWindowId);
FillWindowPixelBuffer(sTopBarWindowId, PIXEL_FILL(15));
width = GetStringWidth(0, string, 0);
- AddTextPrinterParameterized3(sTopBarWindowId, 0, -20 - width, 1, &gUnknown_8456618, 0, string);
+ AddTextPrinterParameterized3(sTopBarWindowId, 0, -20 - width, 1, gUnknown_8456618, 0, string);
if (copyToVram)
CopyWindowToVram(sTopBarWindowId, 3);
}
@@ -213,22 +208,22 @@ void TopBarWindowPrintString(const u8 *string, u8 unused, bool8 copyToVram)
void TopBarWindowPrintTwoStrings(const u8 *string, const u8 *string2, bool8 fgColorChooser, u8 unused, bool8 copyToVram)
{
- struct TextColor color;
+ u8 color[3];
s32 fgColor, width;
if ( sTopBarWindowId != 0xFF )
{
if (fgColorChooser)
{
- color.fgColor = 0;
- color.bgColor = 1;
- color.shadowColor = 2;
+ color[0] = 0;
+ color[1] = 1;
+ color[2] = 2;
}
else
{
- color.fgColor = 15;
- color.bgColor = 1;
- color.shadowColor = 2;
+ color[0] = 15;
+ color[1] = 1;
+ color[2] = 2;
}
PutWindowTilemap(sTopBarWindowId);
@@ -236,9 +231,9 @@ void TopBarWindowPrintTwoStrings(const u8 *string, const u8 *string2, bool8 fgCo
if (string2)
{
width = GetStringWidth(0, string2, 0);
- AddTextPrinterParameterized3(sTopBarWindowId, 0, -20 - width, 1, &color, 0, string2);
+ AddTextPrinterParameterized3(sTopBarWindowId, 0, -20 - width, 1, color, 0, string2);
}
- AddTextPrinterParameterized4(sTopBarWindowId, 1, 4, 1, 0, 0, &color, 0, string);
+ AddTextPrinterParameterized4(sTopBarWindowId, 1, 4, 1, 0, 0, color, 0, string);
if (copyToVram)
CopyWindowToVram(sTopBarWindowId, 3);
}
diff --git a/src/menu2.c b/src/menu2.c
index 6a3668cfa..c6d5ab094 100644
--- a/src/menu2.c
+++ b/src/menu2.c
@@ -1,5 +1,6 @@
#include "global.h"
#include "text.h"
+#include "blit.h"
#include "gpu_regs.h"
#include "task.h"
#include "wild_encounter.h"
@@ -424,7 +425,7 @@ static const u8 gUnknown_845FD54[][5] = {
[SPECIES_OLD_UNOWN_QMARK - 1] = {0x20, 0x23, 0x08, 0x20, 0x2d}
};
-void AddTextPrinterParameterized3(u8 windowId, u8 fontId, u8 x, u8 y, const struct TextColor * color, s8 speed, const u8 * str)
+void AddTextPrinterParameterized3(u8 windowId, u8 fontId, u8 x, u8 y, const u8 * color, s8 speed, const u8 * str)
{
struct TextPrinterTemplate printer;
@@ -438,13 +439,13 @@ void AddTextPrinterParameterized3(u8 windowId, u8 fontId, u8 x, u8 y, const stru
printer.letterSpacing = GetFontAttribute(fontId, 2);
printer.lineSpacing = GetFontAttribute(fontId, 3);
printer.unk = 0;
- printer.fgColor = color->bgColor;
- printer.bgColor = color->fgColor;
- printer.shadowColor = color->shadowColor;
+ printer.fgColor = color[1];
+ printer.bgColor = color[0];
+ printer.shadowColor = color[2];
AddTextPrinter(&printer, speed, NULL);
}
-void AddTextPrinterParameterized4(u8 windowId, u8 fontId, u8 x, u8 y, u8 letterSpacing, u8 lineSpacing, const struct TextColor *color, s8 speed, const u8 *str)
+void AddTextPrinterParameterized4(u8 windowId, u8 fontId, u8 x, u8 y, u8 letterSpacing, u8 lineSpacing, const u8 *color, s8 speed, const u8 *str)
{
struct TextPrinterTemplate printer;
@@ -458,9 +459,9 @@ void AddTextPrinterParameterized4(u8 windowId, u8 fontId, u8 x, u8 y, u8 letterS
printer.letterSpacing = letterSpacing;
printer.lineSpacing = lineSpacing;
printer.unk = 0;
- printer.fgColor = color->bgColor;
- printer.bgColor = color->fgColor;
- printer.shadowColor = color->shadowColor;
+ printer.fgColor = color[1];
+ printer.bgColor = color[0];
+ printer.shadowColor = color[2];
AddTextPrinter(&printer, speed, NULL);
}
@@ -502,261 +503,75 @@ void sub_812E6DC(u8 windowId, const u8 * src, u16 x, u16 y)
}
}
-// Yeah, no, I'm not bothering with this
-NAKED
-static void sub_812E768(void * a0, void * a1, u16 a2, u16 a3, u16 a4, u16 a5, u16 a6, u16 a7)
+static void sub_812E768(const struct Bitmap *src, struct Bitmap *dst, u16 srcX, u16 srcY, u16 dstX, u16 dstY, u16 width, u16 height)
{
- asm_unified("\tpush {r4-r7,lr}\n"
- "\tmov r7, r10\n"
- "\tmov r6, r9\n"
- "\tmov r5, r8\n"
- "\tpush {r5-r7}\n"
- "\tsub sp, 0x28\n"
- "\tstr r0, [sp]\n"
- "\tstr r1, [sp, 0x4]\n"
- "\tldr r0, [sp, 0x48]\n"
- "\tldr r4, [sp, 0x4C]\n"
- "\tldr r1, [sp, 0x50]\n"
- "\tldr r5, [sp, 0x54]\n"
- "\tlsls r2, 16\n"
- "\tlsrs r2, 16\n"
- "\tstr r2, [sp, 0x8]\n"
- "\tlsls r3, 16\n"
- "\tlsrs r3, 16\n"
- "\tlsls r0, 16\n"
- "\tlsrs r0, 16\n"
- "\tstr r0, [sp, 0xC]\n"
- "\tlsls r4, 16\n"
- "\tlsrs r4, 16\n"
- "\tlsls r1, 16\n"
- "\tlsrs r1, 16\n"
- "\tlsls r5, 16\n"
- "\tlsrs r5, 16\n"
- "\tldr r2, [sp, 0x4]\n"
- "\tldrh r0, [r2, 0x4]\n"
- "\tldr r2, [sp, 0xC]\n"
- "\tsubs r0, r2\n"
- "\tldr r2, [sp, 0x8]\n"
- "\tadds r2, r1, r2\n"
- "\tstr r2, [sp, 0x10]\n"
- "\tcmp r0, r1\n"
- "\tbge _0812E7B4\n"
- "\tldr r1, [sp, 0x8]\n"
- "\tadds r0, r1\n"
- "\tstr r0, [sp, 0x10]\n"
- "_0812E7B4:\n"
- "\tldr r2, [sp, 0x4]\n"
- "\tldrh r1, [r2, 0x6]\n"
- "\tsubs r0, r1, r4\n"
- "\tcmp r0, r5\n"
- "\tbge _0812E7C6\n"
- "\tadds r0, r3, r1\n"
- "\tsubs r0, r4\n"
- "\tstr r0, [sp, 0x14]\n"
- "\tb _0812E7CA\n"
- "_0812E7C6:\n"
- "\tadds r5, r3, r5\n"
- "\tstr r5, [sp, 0x14]\n"
- "_0812E7CA:\n"
- "\tldr r0, [sp]\n"
- "\tldrh r1, [r0, 0x4]\n"
- "\tmovs r2, 0x7\n"
- "\tadds r0, r1, 0\n"
- "\tands r0, r2\n"
- "\tadds r1, r0\n"
- "\tasrs r1, 3\n"
- "\tstr r1, [sp, 0x18]\n"
- "\tldr r0, [sp, 0x4]\n"
- "\tldrh r1, [r0, 0x4]\n"
- "\tadds r0, r1, 0\n"
- "\tands r0, r2\n"
- "\tadds r1, r0\n"
- "\tasrs r1, 3\n"
- "\tstr r1, [sp, 0x1C]\n"
- "\tmov r12, r3\n"
- "\tmov r8, r4\n"
- "\tldr r1, [sp, 0x14]\n"
- "\tcmp r12, r1\n"
- "\tblt _0812E7F4\n"
- "\tb _0812E932\n"
- "_0812E7F4:\n"
- "\tldr r5, [sp, 0x8]\n"
- "\tldr r6, [sp, 0xC]\n"
- "\tmov r2, r12\n"
- "\tadds r2, 0x1\n"
- "\tstr r2, [sp, 0x20]\n"
- "\tmov r0, r8\n"
- "\tadds r0, 0x1\n"
- "\tstr r0, [sp, 0x24]\n"
- "\tldr r1, [sp, 0x10]\n"
- "\tcmp r5, r1\n"
- "\tblt _0812E80C\n"
- "\tb _0812E922\n"
- "_0812E80C:\n"
- "\tmovs r7, 0x1\n"
- "\tmovs r2, 0xF0\n"
- "\tmov r10, r2\n"
- "\tmovs r0, 0xF\n"
- "\tmov r9, r0\n"
- "_0812E816:\n"
- "\tasrs r0, r5, 1\n"
- "\tmovs r1, 0x3\n"
- "\tands r0, r1\n"
- "\tldr r2, [sp]\n"
- "\tldr r1, [r2]\n"
- "\tadds r1, r0\n"
- "\tasrs r0, r5, 3\n"
- "\tlsls r0, 5\n"
- "\tadds r1, r0\n"
- "\tmov r2, r12\n"
- "\tasrs r0, r2, 3\n"
- "\tldr r2, [sp, 0x18]\n"
- "\tmuls r0, r2\n"
- "\tlsls r0, 5\n"
- "\tadds r1, r0\n"
- "\tmov r2, r12\n"
- "\tlsls r0, r2, 29\n"
- "\tlsrs r0, 27\n"
- "\tadds r3, r1, r0\n"
- "\tasrs r0, r6, 1\n"
- "\tmovs r1, 0x3\n"
- "\tands r0, r1\n"
- "\tldr r2, [sp, 0x4]\n"
- "\tldr r1, [r2]\n"
- "\tadds r1, r0\n"
- "\tasrs r0, r6, 3\n"
- "\tlsls r0, 5\n"
- "\tadds r1, r0\n"
- "\tmov r2, r8\n"
- "\tasrs r0, r2, 3\n"
- "\tldr r2, [sp, 0x1C]\n"
- "\tmuls r0, r2\n"
- "\tlsls r0, 5\n"
- "\tadds r1, r0\n"
- "\tmov r2, r8\n"
- "\tlsls r0, r2, 29\n"
- "\tlsrs r0, 27\n"
- "\tadds r4, r1, r0\n"
- "\tadds r0, r4, 0\n"
- "\tands r0, r7\n"
- "\tcmp r0, 0\n"
- "\tbeq _0812E8C2\n"
- "\tsubs r4, 0x1\n"
- "\tadds r0, r6, 0\n"
- "\tands r0, r7\n"
- "\tcmp r0, 0\n"
- "\tbeq _0812E89A\n"
- "\tldrh r0, [r4]\n"
- "\tldr r2, _0812E88C @ =0x00000fff\n"
- "\tands r2, r0\n"
- "\tadds r0, r5, 0\n"
- "\tands r0, r7\n"
- "\tcmp r0, 0\n"
- "\tbeq _0812E890\n"
- "\tldrb r1, [r3]\n"
- "\tmov r0, r10\n"
- "\tands r0, r1\n"
- "\tlsls r0, 8\n"
- "\tb _0812E912\n"
- "\t.align 2, 0\n"
- "_0812E88C: .4byte 0x00000fff\n"
- "_0812E890:\n"
- "\tldrb r1, [r3]\n"
- "\tmov r0, r9\n"
- "\tands r0, r1\n"
- "\tlsls r0, 12\n"
- "\tb _0812E912\n"
- "_0812E89A:\n"
- "\tldrh r0, [r4]\n"
- "\tldr r2, _0812E8B4 @ =0x0000f0ff\n"
- "\tands r2, r0\n"
- "\tadds r0, r5, 0\n"
- "\tands r0, r7\n"
- "\tcmp r0, 0\n"
- "\tbeq _0812E8B8\n"
- "\tldrb r1, [r3]\n"
- "\tmov r0, r10\n"
- "\tands r0, r1\n"
- "\tlsls r0, 4\n"
- "\tb _0812E912\n"
- "\t.align 2, 0\n"
- "_0812E8B4: .4byte 0x0000f0ff\n"
- "_0812E8B8:\n"
- "\tldrb r1, [r3]\n"
- "\tmov r0, r9\n"
- "\tands r0, r1\n"
- "\tlsls r0, 8\n"
- "\tb _0812E912\n"
- "_0812E8C2:\n"
- "\tadds r0, r6, 0\n"
- "\tands r0, r7\n"
- "\tcmp r0, 0\n"
- "\tbeq _0812E8EE\n"
- "\tldrh r0, [r4]\n"
- "\tldr r2, _0812E8E0 @ =0x0000ff0f\n"
- "\tands r2, r0\n"
- "\tadds r0, r5, 0\n"
- "\tands r0, r7\n"
- "\tcmp r0, 0\n"
- "\tbeq _0812E8E4\n"
- "\tldrb r1, [r3]\n"
- "\tmov r0, r10\n"
- "\tb _0812E910\n"
- "\t.align 2, 0\n"
- "_0812E8E0: .4byte 0x0000ff0f\n"
- "_0812E8E4:\n"
- "\tldrb r1, [r3]\n"
- "\tmov r0, r9\n"
- "\tands r0, r1\n"
- "\tlsls r0, 4\n"
- "\tb _0812E912\n"
- "_0812E8EE:\n"
- "\tldrh r0, [r4]\n"
- "\tldr r2, _0812E908 @ =0x0000fff0\n"
- "\tands r2, r0\n"
- "\tadds r0, r5, 0\n"
- "\tands r0, r7\n"
- "\tcmp r0, 0\n"
- "\tbeq _0812E90C\n"
- "\tldrb r1, [r3]\n"
- "\tmov r0, r10\n"
- "\tands r0, r1\n"
- "\tlsrs r0, 4\n"
- "\tb _0812E912\n"
- "\t.align 2, 0\n"
- "_0812E908: .4byte 0x0000fff0\n"
- "_0812E90C:\n"
- "\tldrb r1, [r3]\n"
- "\tmov r0, r9\n"
- "_0812E910:\n"
- "\tands r0, r1\n"
- "_0812E912:\n"
- "\torrs r2, r0\n"
- "\tstrh r2, [r4]\n"
- "\tadds r5, 0x1\n"
- "\tadds r6, 0x1\n"
- "\tldr r0, [sp, 0x10]\n"
- "\tcmp r5, r0\n"
- "\tbge _0812E922\n"
- "\tb _0812E816\n"
- "_0812E922:\n"
- "\tldr r1, [sp, 0x20]\n"
- "\tmov r12, r1\n"
- "\tldr r2, [sp, 0x24]\n"
- "\tmov r8, r2\n"
- "\tldr r0, [sp, 0x14]\n"
- "\tcmp r12, r0\n"
- "\tbge _0812E932\n"
- "\tb _0812E7F4\n"
- "_0812E932:\n"
- "\tadd sp, 0x28\n"
- "\tpop {r3-r5}\n"
- "\tmov r8, r3\n"
- "\tmov r9, r4\n"
- "\tmov r10, r5\n"
- "\tpop {r4-r7}\n"
- "\tpop {r0}\n"
- "\tbx r0");
+ s32 loopSrcY, loopDstY, loopSrcX, loopDstX, xEnd, yEnd, multiplierSrcY, multiplierDstY;
+ u16 toOrr;
+ const u8 *pixelsSrc;
+ u16 *pixelsDst;
+
+ if (dst->width - dstX < width)
+ xEnd = dst->width - dstX + srcX;
+ else
+ xEnd = width + srcX;
+
+ if (dst->height - dstY < height)
+ yEnd = srcY + dst->height - dstY;
+ else
+ yEnd = srcY + height;
+ multiplierSrcY = (src->width + (src->width & 7)) >> 3;
+ multiplierDstY = (dst->width + (dst->width & 7)) >> 3;
+ for (loopSrcY = srcY, loopDstY = dstY; loopSrcY < yEnd; loopSrcY++, loopDstY++)
+ {
+ for (loopSrcX = srcX, loopDstX = dstX; loopSrcX < xEnd; loopSrcX++, loopDstX++)
+ {
+ #ifndef NONMATCHING
+ asm("":::"r4");
+ #endif
+ pixelsSrc = src->pixels + ((loopSrcX >> 1) & 3) + ((loopSrcX >> 3) << 5) + (((loopSrcY >> 3) * multiplierSrcY) << 5) + ((u32)(loopSrcY << 0x1d) >> 0x1B);
+ pixelsDst = (u16 *)(dst->pixels + ((loopDstX >> 1) & 3) + ((loopDstX >> 3) << 5) + ((( loopDstY >> 3) * multiplierDstY) << 5) + ((u32)( loopDstY << 0x1d) >> 0x1B));
+
+ if ((uintptr_t)pixelsDst & 0x1)
+ {
+ pixelsDst = (void *)pixelsDst - 1;
+ if (loopDstX & 0x1)
+ {
+ toOrr = *pixelsDst & 0x0fff;
+ if (loopSrcX & 0x1)
+ *pixelsDst = toOrr | ((*pixelsSrc & 0xf0) << 8);
+ else
+ *pixelsDst = toOrr | ((*pixelsSrc & 0x0f) << 12);
+ }
+ else
+ {
+ toOrr = *pixelsDst & 0xf0ff;
+ if (loopSrcX & 0x1)
+ *pixelsDst = toOrr | ((*pixelsSrc & 0xf0) << 4);
+ else
+ *pixelsDst = toOrr | ((*pixelsSrc & 0x0f) << 8);
+ }
+ }
+ else
+ {
+ if (loopDstX & 1)
+ {
+ toOrr = *pixelsDst & 0xff0f;
+ if (loopSrcX & 1)
+ *pixelsDst = toOrr | ((*pixelsSrc & 0xf0) << 0);
+ else
+ *pixelsDst = toOrr | ((*pixelsSrc & 0x0f) << 4);
+ }
+ else
+ {
+ toOrr = *pixelsDst & 0xfff0;
+ if (loopSrcX & 1)
+ *pixelsDst = toOrr | ((*pixelsSrc & 0xf0) >> 4);
+ else
+ *pixelsDst = toOrr | ((*pixelsSrc & 0x0f) >> 0);
+ }
+ }
+ }
+ }
}
#define tEvA data[0]
diff --git a/src/menu_helpers.c b/src/menu_helpers.c
index fdcb9a56e..06e7bb08b 100644
--- a/src/menu_helpers.c
+++ b/src/menu_helpers.c
@@ -95,7 +95,7 @@ u8 sub_80BF66C(void)
return 0;
}
-bool8 sub_80BF6A8(u16 itemId)
+bool8 itemid_link_can_give_berry(u16 itemId)
{
if (itemId != ITEM_ENIGMA_BERRY)
return TRUE;
diff --git a/src/mevent.c b/src/mevent.c
index fc0c1f2d4..af2006e14 100644
--- a/src/mevent.c
+++ b/src/mevent.c
@@ -126,7 +126,7 @@ u8 sub_8143674(struct MEvent_Str_1 *mgr)
resp = 2;
if (mgr->status & 4)
resp = 3;
- gUnknown_3003F84 = 0;
+ gShouldAdvanceLinkState = 0;
return resp;
}
@@ -134,7 +134,7 @@ static void ResetTTDataBuffer(void)
{
memset(gDecompressionBuffer, 0, 0x2000);
gLinkType = 0x5502;
- sub_8009804();
+ OpenLink();
SetSuppressLinkErrorMessage(TRUE);
}
@@ -143,7 +143,7 @@ bool32 sub_81436EC(void)
vu16 imeBak = REG_IME;
u16 data[4];
REG_IME = 0;
- *(u64 *)data = gSioMlt_Recv;
+ *(u64 *)data = *(u64 *)gLink.tempRecvBuffer;
REG_IME = imeBak;
if ( data[0] == 0xB9A0
&& data[1] == 0xCCD0
@@ -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;
}
@@ -588,7 +588,7 @@ void DestroyWonderCard(void)
ClearRamScript();
sub_806E2D0();
sub_806E370();
- sub_80E7524(gSaveBlock2Ptr->unk_4A0);
+ sub_80E7524(gSaveBlock2Ptr->unk_B0.field_3F0);
}
bool32 sub_8143F68(const struct MEventBuffer_32E0_Sub * data)
@@ -769,7 +769,7 @@ void sub_81442CC(struct MEventStruct_Unk1442CC * data)
data->unk_14 = 0;
for (i = 0; i < 4; i++)
data->unk_16[i] = gSaveBlock1Ptr->unk_3120.unk_338[i];
- CopyUnalignedWord(data->unk_4C, gSaveBlock2Ptr->playerTrainerId);
+ CopyTrainerId(data->unk_4C, gSaveBlock2Ptr->playerTrainerId);
StringCopy(data->unk_45, gSaveBlock2Ptr->playerName);
for (i = 0; i < 6; i++)
data->unk_50[i] = gSaveBlock1Ptr->unk2CA0[i];
diff --git a/src/mevent_8145654.c b/src/mevent_8145654.c
index c67cd1358..85c1dfccb 100644
--- a/src/mevent_8145654.c
+++ b/src/mevent_8145654.c
@@ -61,7 +61,7 @@ void sub_81461D8(void);
extern const struct OamData gOamData_83AC9F8;
-const struct TextColor gUnknown_8467068[] = {
+const u8 gUnknown_8467068[][3] = {
{0, 2, 3},
{0, 1, 2}
};
@@ -348,28 +348,28 @@ void sub_8145D18(u8 whichWindow)
case 0:
{
s32 x;
- AddTextPrinterParameterized3(windowId, 3, 0, 1, &gUnknown_8467068[gUnknown_203F3C8->unk_0170->textPal1], 0, gUnknown_203F3C8->unk_018B);
+ AddTextPrinterParameterized3(windowId, 3, 0, 1, gUnknown_8467068[gUnknown_203F3C8->unk_0170->textPal1], 0, gUnknown_203F3C8->unk_018B);
x = 160 - GetStringWidth(3, gUnknown_203F3C8->unk_01B4, GetFontAttribute(3, 2));
if (x < 0)
x = 0;
- AddTextPrinterParameterized3(windowId, 3, x, 17, &gUnknown_8467068[gUnknown_203F3C8->unk_0170->textPal1], 0, gUnknown_203F3C8->unk_01B4);
+ AddTextPrinterParameterized3(windowId, 3, x, 17, gUnknown_8467068[gUnknown_203F3C8->unk_0170->textPal1], 0, gUnknown_203F3C8->unk_01B4);
if (gUnknown_203F3C8->unk_0000.unk_04 != 0)
{
- AddTextPrinterParameterized3(windowId, 2, 166, 17, &gUnknown_8467068[gUnknown_203F3C8->unk_0170->textPal1], 0, gUnknown_203F3C8->unk_01DD);
+ AddTextPrinterParameterized3(windowId, 2, 166, 17, gUnknown_8467068[gUnknown_203F3C8->unk_0170->textPal1], 0, gUnknown_203F3C8->unk_01DD);
}
break;
}
case 1:
for (; sp0C < 4; sp0C++)
{
- AddTextPrinterParameterized3(windowId, 3, 0, 16 * sp0C + 2, &gUnknown_8467068[gUnknown_203F3C8->unk_0170->textPal2], 0, gUnknown_203F3C8->unk_01E4[sp0C]);
+ AddTextPrinterParameterized3(windowId, 3, 0, 16 * sp0C + 2, gUnknown_8467068[gUnknown_203F3C8->unk_0170->textPal2], 0, gUnknown_203F3C8->unk_01E4[sp0C]);
}
break;
case 2:
- AddTextPrinterParameterized3(windowId, 3, 0, gUnknown_8467070[gUnknown_203F3C8->unk_0000.unk_08_0], &gUnknown_8467068[gUnknown_203F3C8->unk_0170->textPal3], 0, gUnknown_203F3C8->unk_0288);
+ AddTextPrinterParameterized3(windowId, 3, 0, gUnknown_8467070[gUnknown_203F3C8->unk_0000.unk_08_0], gUnknown_8467068[gUnknown_203F3C8->unk_0170->textPal3], 0, gUnknown_203F3C8->unk_0288);
if (gUnknown_203F3C8->unk_0000.unk_08_0 != 2)
{
- AddTextPrinterParameterized3(windowId, 3, 0, 16 + gUnknown_8467070[gUnknown_203F3C8->unk_0000.unk_08_0], &gUnknown_8467068[gUnknown_203F3C8->unk_0170->textPal3], 0, gUnknown_203F3C8->unk_02B1);
+ AddTextPrinterParameterized3(windowId, 3, 0, 16 + gUnknown_8467070[gUnknown_203F3C8->unk_0000.unk_08_0], gUnknown_8467068[gUnknown_203F3C8->unk_0170->textPal3], 0, gUnknown_203F3C8->unk_02B1);
}
else
{
@@ -378,11 +378,11 @@ void sub_8145D18(u8 whichWindow)
s32 spacing = GetFontAttribute(3, 2);
for (; sp0C < gUnknown_203F3C8->unk_0175; sp0C++)
{
- AddTextPrinterParameterized3(windowId, 3, x, y, &gUnknown_8467068[gUnknown_203F3C8->unk_0170->textPal3], 0, gUnknown_203F3C8->unk_02DC[sp0C].unk_01);
+ AddTextPrinterParameterized3(windowId, 3, x, y, gUnknown_8467068[gUnknown_203F3C8->unk_0170->textPal3], 0, gUnknown_203F3C8->unk_02DC[sp0C].unk_01);
if (gUnknown_203F3C8->unk_02DC[sp0C].unk_42[0] != EOS)
{
x += GetStringWidth(3, gUnknown_203F3C8->unk_02DC[sp0C].unk_01, spacing);
- AddTextPrinterParameterized3(windowId, 2, x, y, &gUnknown_8467068[gUnknown_203F3C8->unk_0170->textPal3], 0, gUnknown_203F3C8->unk_02DC[sp0C].unk_42);
+ AddTextPrinterParameterized3(windowId, 2, x, y, gUnknown_8467068[gUnknown_203F3C8->unk_0170->textPal3], 0, gUnknown_203F3C8->unk_02DC[sp0C].unk_42);
x += GetStringWidth(3, gUnknown_203F3C8->unk_02DC[sp0C].unk_42, spacing) + gUnknown_203F3C8->unk_02DC[sp0C].unk_00;
}
}
@@ -471,7 +471,7 @@ void sub_8146980(void);
void sub_8146A30(void);
void sub_8146B58(void);
-const struct TextColor gUnknown_8468038[] = {
+const u8 gUnknown_8468038[][3] = {
{0, 2, 3},
{0, 1, 2}
};
@@ -758,10 +758,10 @@ void sub_8146A30(void)
x = (0xe0 - GetStringWidth(3, gUnknown_203F3CC->unk_01CE, GetFontAttribute(3, 2))) / 2;
if (x < 0)
x = 0;
- AddTextPrinterParameterized3(gUnknown_203F3CC->unk_01C8[0], 3, x, 6, &gUnknown_8468038[gUnknown_203F3CC->unk_01BC->textPal1], 0, gUnknown_203F3CC->unk_01CE);
+ AddTextPrinterParameterized3(gUnknown_203F3CC->unk_01C8[0], 3, x, 6, gUnknown_8468038[gUnknown_203F3CC->unk_01BC->textPal1], 0, gUnknown_203F3CC->unk_01CE);
for (; i < 10; ++i)
{
- AddTextPrinterParameterized3(gUnknown_203F3CC->unk_01C8[1], 3, 0, 16 * i + 2, &gUnknown_8468038[gUnknown_203F3CC->unk_01BC->textPal2], 0, gUnknown_203F3CC->unk_01F7[i]);
+ AddTextPrinterParameterized3(gUnknown_203F3CC->unk_01C8[1], 3, 0, 16 * i + 2, gUnknown_8468038[gUnknown_203F3CC->unk_01BC->textPal2], 0, gUnknown_203F3CC->unk_01F7[i]);
}
CopyWindowToVram(gUnknown_203F3CC->unk_01C8[0], 3);
CopyWindowToVram(gUnknown_203F3CC->unk_01C8[1], 3);
diff --git a/src/mevent_server.c b/src/mevent_server.c
index 1c2dc4ced..4e2b7280d 100644
--- a/src/mevent_server.c
+++ b/src/mevent_server.c
@@ -226,7 +226,7 @@ static u32 ish_mainseq_4(struct mevent_client * svr)
sub_8069EA4(svr->recvBuffer, 1000);
break;
case 18:
- memcpy(gSaveBlock2Ptr->unk_4A0, svr->recvBuffer, 0xbc);
+ memcpy(gSaveBlock2Ptr->unk_B0.field_3F0, svr->recvBuffer, 0xbc);
ValidateEReaderTrainer();
break;
case 21:
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/mystery_event_script.c b/src/mystery_event_script.c
index 70566ca58..89df1ee34 100644
--- a/src/mystery_event_script.c
+++ b/src/mystery_event_script.c
@@ -281,7 +281,7 @@ bool8 MEScrCmd_givepokemon(struct ScriptContext *ctx)
bool8 MEScrCmd_addtrainer(struct ScriptContext *ctx)
{
u32 data = ScriptReadWord(ctx) - ctx->data[1] + ctx->data[0];
- memcpy(gSaveBlock2Ptr->unk_4A0, (void *)data, 0xBC);
+ memcpy(gSaveBlock2Ptr->unk_B0.field_3F0, (void *)data, 0xBC);
ValidateEReaderTrainer();
StringExpandPlaceholders(gStringVar4, gText_MysteryGiftNewTrainer);
ctx->data[2] = 2;
diff --git a/src/mystery_gift_menu.c b/src/mystery_gift_menu.c
index 9ff96eb23..dbc685602 100644
--- a/src/mystery_gift_menu.c
+++ b/src/mystery_gift_menu.c
@@ -409,9 +409,9 @@ const u8 *const Unref_08366ED8[] = {
gText_ReturnToTitle
};
-ALIGNED(4) const struct TextColor sMG_Ereader_TextColor_1 = { 0, 1, 2 };
-ALIGNED(4) const struct TextColor sMG_Ereader_TextColor_1_Copy = { 0, 1, 2 };
-ALIGNED(4) const struct TextColor sMG_Ereader_TextColor_2 = { 1, 2, 3 };
+ALIGNED(4) const u8 sMG_Ereader_TextColor_1[3] = { 0, 1, 2 };
+ALIGNED(4) const u8 sMG_Ereader_TextColor_1_Copy[3] = { 0, 1, 2 };
+ALIGNED(4) const u8 sMG_Ereader_TextColor_2[3] = { 1, 2, 3 };
const u8 gUnknown_8466EF3[] = _("テスト");
const u8 gUnknown_8466EF7[] = _("むげんのチケット");
@@ -538,14 +538,14 @@ void PrintMysteryGiftOrEReaderTopMenu(bool8 mg_or_ereader, bool32 usePickOkCance
if (!mg_or_ereader)
{
src = usePickOkCancel == TRUE ? gText_PickOKExit : gText_PickOKCancel;
- AddTextPrinterParameterized4(0, 2, 2, 2, 0, 0, &sMG_Ereader_TextColor_1, 0, gText_MysteryGift);
+ AddTextPrinterParameterized4(0, 2, 2, 2, 0, 0, sMG_Ereader_TextColor_1, 0, gText_MysteryGift);
width = 222 - GetStringWidth(0, src, 0);
- AddTextPrinterParameterized4(0, 0, width, 2, 0, 0, &sMG_Ereader_TextColor_1, 0, src);
+ AddTextPrinterParameterized4(0, 0, width, 2, 0, 0, sMG_Ereader_TextColor_1, 0, src);
}
else
{
- AddTextPrinterParameterized4(0, 2, 2, 2, 0, 0, &sMG_Ereader_TextColor_1, 0, gJPText_MysteryGift);
- AddTextPrinterParameterized4(0, 0, 0x78, 2, 0, 0, &sMG_Ereader_TextColor_1, 0, gJPText_DecideStop);
+ AddTextPrinterParameterized4(0, 2, 2, 2, 0, 0, sMG_Ereader_TextColor_1, 0, gJPText_MysteryGift);
+ AddTextPrinterParameterized4(0, 0, 0x78, 2, 0, 0, sMG_Ereader_TextColor_1, 0, gJPText_DecideStop);
}
CopyWindowToVram(0, 2);
PutWindowTilemap(0);
@@ -596,7 +596,7 @@ void AddTextPrinterToWindow1(const u8 *str)
{
StringExpandPlaceholders(gStringVar4, str);
FillWindowPixelBuffer(1, 0x11);
- AddTextPrinterParameterized4(1, 2, 0, 2, 0, 2, &sMG_Ereader_TextColor_2, 0, gStringVar4);
+ AddTextPrinterParameterized4(1, 2, 0, 2, 0, 2, sMG_Ereader_TextColor_2, 0, gStringVar4);
DrawTextBorderOuter(1, 0x001, 0xF);
PutWindowTilemap(1);
CopyWindowToVram(1, 3);
@@ -737,7 +737,7 @@ s8 mevent_message_print_and_prompt_yes_no(u8 * textState, u16 * windowId, bool8
*windowId = AddWindow(&sWindowTemplate_PromptYesOrNo_Width20);
}
FillWindowPixelBuffer(*windowId, 0x11);
- AddTextPrinterParameterized4(*windowId, 2, 0, 2, 0, 2, &sMG_Ereader_TextColor_2, 0, gStringVar4);
+ AddTextPrinterParameterized4(*windowId, 2, 0, 2, 0, 2, sMG_Ereader_TextColor_2, 0, gStringVar4);
DrawTextBorderOuter(*windowId, 0x001, 0x0F);
CopyWindowToVram(*windowId, 2);
PutWindowTilemap(*windowId);
@@ -798,7 +798,7 @@ s32 HandleMysteryGiftListMenu(u8 * textState, u16 * windowId, bool32 cannotToss,
}
*windowId = AddWindow(&sMysteryGiftMenuWindowTemplate);
FillWindowPixelBuffer(*windowId, 0x11);
- AddTextPrinterParameterized4(*windowId, 2, 0, 2, 0, 2, &sMG_Ereader_TextColor_2, 0, gStringVar4);
+ AddTextPrinterParameterized4(*windowId, 2, 0, 2, 0, 2, sMG_Ereader_TextColor_2, 0, gStringVar4);
DrawTextBorderOuter(*windowId, 0x001, 0x0F);
CopyWindowToVram(*windowId, 2);
PutWindowTilemap(*windowId);
@@ -1436,7 +1436,7 @@ void task00_mystery_gift(u8 taskId)
}
break;
case 13:
- if (IsNoOneConnected())
+ if (IsRfuTaskFinished())
{
DestroyWirelessStatusIndicatorSprite();
data->state = 14;
@@ -1712,7 +1712,7 @@ void task00_mystery_gift(u8 taskId)
data->state = 34;
break;
case 34:
- if (IsNoOneConnected())
+ if (IsRfuTaskFinished())
{
DestroyWirelessStatusIndicatorSprite();
data->state = 35;
diff --git a/src/new_game.c b/src/new_game.c
new file mode 100644
index 000000000..58deac9b9
--- /dev/null
+++ b/src/new_game.c
@@ -0,0 +1,161 @@
+#include "global.h"
+#include "new_game.h"
+#include "random.h"
+#include "main.h"
+#include "overworld.h"
+#include "constants/maps.h"
+#include "load_save.h"
+#include "item_menu.h"
+#include "tm_case.h"
+#include "berry_pouch.h"
+#include "quest_log.h"
+#include "wild_encounter.h"
+#include "event_data.h"
+#include "string_util.h"
+#include "mail_data.h"
+#include "play_time.h"
+#include "money.h"
+#include "battle_records.h"
+#include "pokemon_size_record.h"
+#include "pokemon_storage_system.h"
+#include "roamer.h"
+#include "item.h"
+#include "player_pc.h"
+#include "berry.h"
+#include "easy_chat.h"
+#include "union_room_chat.h"
+#include "mevent.h"
+#include "trainer_tower.h"
+#include "script.h"
+#include "berry_powder.h"
+#include "pokemon_jump.h"
+#include "event_scripts.h"
+
+// this file's functions
+static void ResetMiniGamesResults(void);
+
+// EWRAM vars
+EWRAM_DATA bool8 gDifferentSaveFile = FALSE;
+
+void SetTrainerId(u32 trainerId, u8 *dst)
+{
+ dst[0] = trainerId;
+ dst[1] = trainerId >> 8;
+ dst[2] = trainerId >> 16;
+ dst[3] = trainerId >> 24;
+}
+
+void CopyTrainerId(u8 *dst, u8 *src)
+{
+ s32 i;
+ for (i = 0; i < 4; i++)
+ dst[i] = src[i];
+}
+
+static void InitPlayerTrainerId(void)
+{
+ u32 trainerId = (Random() << 0x10) | GetGeneratedTrainerIdLower();
+ SetTrainerId(trainerId, gSaveBlock2Ptr->playerTrainerId);
+}
+
+static void SetDefaultOptions(void)
+{
+ gSaveBlock2Ptr->optionsTextSpeed = OPTIONS_TEXT_SPEED_MID;
+ gSaveBlock2Ptr->optionsWindowFrameType = 0;
+ gSaveBlock2Ptr->optionsSound = OPTIONS_SOUND_MONO;
+ gSaveBlock2Ptr->optionsBattleStyle = OPTIONS_BATTLE_STYLE_SHIFT;
+ gSaveBlock2Ptr->optionsBattleSceneOff = FALSE;
+ gSaveBlock2Ptr->regionMapZoom = FALSE;
+ gSaveBlock2Ptr->optionsButtonMode = OPTIONS_BUTTON_MODE_NORMAL;
+}
+
+static void ClearPokedexFlags(void)
+{
+ memset(&gSaveBlock2Ptr->pokedex.owned, 0, sizeof(gSaveBlock2Ptr->pokedex.owned));
+ memset(&gSaveBlock2Ptr->pokedex.seen, 0, sizeof(gSaveBlock2Ptr->pokedex.seen));
+}
+
+static void sub_80549D4(void)
+{
+ CpuFill32(0, &gSaveBlock2Ptr->unk_B0, sizeof(gSaveBlock2Ptr->unk_B0));
+}
+
+static void WarpToPlayersRoom(void)
+{
+ SetWarpDestination(MAP_GROUP(PALLET_TOWN_PLAYERS_HOUSE_2F), MAP_NUM(PALLET_TOWN_PLAYERS_HOUSE_2F), -1, 6, 6);
+ WarpIntoMap();
+}
+
+void Sav2_ClearSetDefault(void)
+{
+ ClearSav2();
+ SetDefaultOptions();
+}
+
+void ResetMenuAndMonGlobals(void)
+{
+ gDifferentSaveFile = FALSE;
+ ZeroPlayerPartyMons();
+ ZeroEnemyPartyMons();
+ sub_81089BC();
+ ResetTMCaseCursorPos();
+ BerryPouch_CursorResetToTop();
+ sub_811089C();
+ sub_8083214(Random());
+ sub_806E6FC();
+}
+
+void NewGameInitData(void)
+{
+ u8 rivalName[PLAYER_NAME_LENGTH];
+
+ StringCopy(rivalName, gSaveBlock1Ptr->rivalName);
+ gDifferentSaveFile = TRUE;
+ gSaveBlock2Ptr->encryptionKey = 0;
+ ZeroPlayerPartyMons();
+ ZeroEnemyPartyMons();
+ sub_80549D4();
+ ClearSav1();
+ ClearMailData();
+ gSaveBlock2Ptr->specialSaveWarpFlags = 0;
+ gSaveBlock2Ptr->field_A8 = 0;
+ gSaveBlock2Ptr->field_AC = 1;
+ gSaveBlock2Ptr->field_AD = 0;
+ InitPlayerTrainerId();
+ PlayTimeCounter_Reset();
+ ClearPokedexFlags();
+ InitEventData();
+ ResetFameChecker();
+ SetMoney(&gSaveBlock1Ptr->money, 3000);
+ ResetGameStats();
+ ClearPlayerLinkBattleRecords();
+ sub_80A0904();
+ sub_80A0958();
+ sub_806E190();
+ gPlayerPartyCount = 0;
+ ZeroPlayerPartyMons();
+ ResetPokemonStorageSystem();
+ ClearRoamerData();
+ gSaveBlock1Ptr->registeredItem = 0;
+ ClearBag();
+ NewGameInitPCItems();
+ sub_809C794();
+ InitEasyChatPhrases();
+ sub_8113044();
+ copy_strings_to_sav1();
+ ResetMiniGamesResults();
+ sub_8143D24();
+ sub_815D838();
+ WarpToPlayersRoom();
+ ScriptContext2_RunNewScript(EventScript_ResetAllMapFlags);
+ StringCopy(gSaveBlock1Ptr->rivalName, rivalName);
+ ResetTrainerTowerResults();
+}
+
+static void ResetMiniGamesResults(void)
+{
+ CpuFill16(0, &gSaveBlock2Ptr->berryCrush, sizeof(struct BerryCrush));
+ SetBerryPowder(&gSaveBlock2Ptr->berryCrush.berryPowderAmount, 0);
+ ResetPokeJumpResults();
+ CpuFill16(0, &gSaveBlock2Ptr->berryPick, sizeof(struct BerryPickingResults));
+}
diff --git a/src/oak_speech.c b/src/oak_speech.c
index c00335136..04b66f528 100644
--- a/src/oak_speech.c
+++ b/src/oak_speech.c
@@ -37,7 +37,7 @@ struct OakSpeechResources
u16 unk_0010;
u16 unk_0012;
u16 unk_0014[4];
- struct TextColor textColor;
+ u8 textColor[3];
u8 textSpeed;
u8 filler_0020[0x1800];
u8 bg2TilemapBuffer[0x400];
@@ -273,11 +273,11 @@ static const struct WindowTemplate sNewGameAdventureIntroWindowTemplates[] = {
}, DUMMY_WIN_TEMPLATE
};
-ALIGNED(4) const struct TextColor sTextColor_HelpSystem = {
+const u8 sTextColor_HelpSystem[4] = {
0x00, 0x01, 0x02
};
-ALIGNED(4) const struct TextColor sTextColor_OakSpeech = {
+const u8 sTextColor_OakSpeech[4] = {
0x00, 0x02, 0x03
};
@@ -576,7 +576,7 @@ static void CreateHelpDocsPage1(void)
sOakSpeechResources->unk_0014[0] = AddWindow(sHelpDocsWindowTemplatePtrs[sOakSpeechResources->unk_0012]);
PutWindowTilemap(sOakSpeechResources->unk_0014[0]);
FillWindowPixelBuffer(sOakSpeechResources->unk_0014[0], 0x00);
- AddTextPrinterParameterized4(sOakSpeechResources->unk_0014[0], 2, 2, 0, 1, 1, &sTextColor_HelpSystem, 0, gNewGame_HelpDocs1);
+ AddTextPrinterParameterized4(sOakSpeechResources->unk_0014[0], 2, 2, 0, 1, 1, sTextColor_HelpSystem, 0, gNewGame_HelpDocs1);
CopyWindowToVram(sOakSpeechResources->unk_0014[0], 3);
FillBgTilemapBufferRect_Palette0(1, 0x3000, 1, 3, 5, 16);
CopyBgTilemapBufferToVram(1);
@@ -598,7 +598,7 @@ static void Task_OakSpeech4(u8 taskId)
sOakSpeechResources->unk_0014[i] = AddWindow(&sHelpDocsWindowTemplatePtrs[sOakSpeechResources->unk_0012][i]);
PutWindowTilemap(sOakSpeechResources->unk_0014[i]);
FillWindowPixelBuffer(sOakSpeechResources->unk_0014[i], 0x00);
- AddTextPrinterParameterized4(sOakSpeechResources->unk_0014[i], 2, 6, 0, 1, 1, &sTextColor_HelpSystem, 0, sHelpDocsPtrs[i + r7 * 3]);
+ AddTextPrinterParameterized4(sOakSpeechResources->unk_0014[i], 2, 6, 0, 1, 1, sTextColor_HelpSystem, 0, sHelpDocsPtrs[i + r7 * 3]);
CopyWindowToVram(sOakSpeechResources->unk_0014[i], 3);
}
@@ -730,7 +730,7 @@ static void Task_OakSpeech6(u8 taskId)
sOakSpeechResources->unk_0012 = 0;
gMain.state = 0;
data[15] = 16;
- AddTextPrinterParameterized4(data[14], 2, 3, 5, 1, 0, &sTextColor_OakSpeech, 0, sNewGameAdventureIntroTextPointers[0]);
+ AddTextPrinterParameterized4(data[14], 2, 3, 5, 1, 0, sTextColor_OakSpeech, 0, sNewGameAdventureIntroTextPointers[0]);
data[5] = CreateTextCursorSpriteForOakSpeech(0, 0xe2, 0x91, 0, 0);
gSprites[data[5]].oam.objMode = ST_OAM_OBJ_BLEND;
gSprites[data[5]].oam.priority = 0;
@@ -790,7 +790,7 @@ static void Task_OakSpeech7(u8 taskId)
if (data[15] <= 0)
{
FillWindowPixelBuffer(data[14], 0x00);
- AddTextPrinterParameterized4(data[14], 2, 3, 5, 1, 0, &sTextColor_OakSpeech, 0, sNewGameAdventureIntroTextPointers[sOakSpeechResources->unk_0012]);
+ AddTextPrinterParameterized4(data[14], 2, 3, 5, 1, 0, sTextColor_OakSpeech, 0, sNewGameAdventureIntroTextPointers[sOakSpeechResources->unk_0012]);
if (sOakSpeechResources->unk_0012 == 0)
{
ClearTopBarWindow();
@@ -1056,14 +1056,14 @@ static void Task_OakSpeech19(u8 taskId)
PutWindowTilemap(gTasks[taskId].data[13]);
DrawStdFrameWithCustomTileAndPalette(gTasks[taskId].data[13], 1, GetStdWindowBaseTileNum(), 14);
FillWindowPixelBuffer(gTasks[taskId].data[13], 0x11);
- sOakSpeechResources->textColor.fgColor = 1;
- sOakSpeechResources->textColor.bgColor = 2;
- sOakSpeechResources->textColor.shadowColor = 3;
- AddTextPrinterParameterized3(gTasks[taskId].data[13], 2, 8, 1, &sOakSpeechResources->textColor, 0, gText_Boy);
- sOakSpeechResources->textColor.fgColor = 1;
- sOakSpeechResources->textColor.bgColor = 2;
- sOakSpeechResources->textColor.shadowColor = 3;
- AddTextPrinterParameterized3(gTasks[taskId].data[13], 2, 8, 17, &sOakSpeechResources->textColor, 0, gText_Girl);
+ sOakSpeechResources->textColor[0] = 1;
+ sOakSpeechResources->textColor[1] = 2;
+ sOakSpeechResources->textColor[2] = 3;
+ AddTextPrinterParameterized3(gTasks[taskId].data[13], 2, 8, 1, sOakSpeechResources->textColor, 0, gText_Boy);
+ sOakSpeechResources->textColor[0] = 1;
+ sOakSpeechResources->textColor[1] = 2;
+ sOakSpeechResources->textColor[2] = 3;
+ AddTextPrinterParameterized3(gTasks[taskId].data[13], 2, 8, 17, sOakSpeechResources->textColor, 0, gText_Girl);
Menu_InitCursor(gTasks[taskId].data[13], 2, 0, 1, GetFontAttribute(2, 1) + 2, 2, 0);
CopyWindowToVram(gTasks[taskId].data[13], 3);
gTasks[taskId].func = Task_OakSpeech20;
diff --git a/src/palette.c b/src/palette.c
new file mode 100644
index 000000000..6609c9baa
--- /dev/null
+++ b/src/palette.c
@@ -0,0 +1,989 @@
+#include "global.h"
+#include "palette.h"
+#include "util.h"
+#include "decompress.h"
+#include "gpu_regs.h"
+#include "task.h"
+
+enum
+{
+ NORMAL_FADE,
+ FAST_FADE,
+ HARDWARE_FADE,
+};
+
+#define NUM_PALETTE_STRUCTS 16
+
+// unused palette struct
+struct PaletteStructTemplate
+{
+ u16 uid;
+ u16 *src;
+ u16 pst_field_8_0:1;
+ u16 pst_field_8_1:9;
+ u16 size:5;
+ u16 pst_field_9_7:1;
+ u8 pst_field_A;
+ u8 srcCount:5;
+ u8 pst_field_B_5:3;
+ u8 pst_field_C;
+};
+
+struct PaletteStruct
+{
+ const struct PaletteStructTemplate *base;
+ u32 ps_field_4_0:1;
+ u16 ps_field_4_1:1;
+ u32 baseDestOffset:9;
+ u16 destOffset:10;
+ u16 srcIndex:7;
+ u8 ps_field_8;
+ u8 ps_field_9;
+};
+
+static void sub_8070790(struct PaletteStruct *, u32 *);
+static void sub_80708F4(struct PaletteStruct *, u32 *);
+static void sub_80709B4(struct PaletteStruct *);
+static u8 GetPaletteNumByUid(u16);
+static u8 UpdateNormalPaletteFade(void);
+static void BeginFastPaletteFadeInternal(u8);
+static u8 UpdateFastPaletteFade(void);
+static u8 UpdateHardwarePaletteFade(void);
+static void UpdateBlendRegisters(void);
+static bool8 IsSoftwarePaletteFadeFinishing(void);
+static void sub_80718B8(u8 taskId);
+
+ALIGNED(4) EWRAM_DATA u16 gPlttBufferUnfaded[PLTT_BUFFER_SIZE] = {0};
+ALIGNED(4) EWRAM_DATA u16 gPlttBufferFaded[PLTT_BUFFER_SIZE] = {0};
+static EWRAM_DATA struct PaletteStruct sPaletteStructs[NUM_PALETTE_STRUCTS] = {0};
+EWRAM_DATA struct PaletteFadeControl gPaletteFade = {0};
+static EWRAM_DATA u32 sPlttBufferTransferPending = 0;
+EWRAM_DATA u8 gPaletteDecompressionBuffer[PLTT_DECOMP_BUFFER_SIZE] = {0};
+
+static const struct PaletteStructTemplate gDummyPaletteStructTemplate =
+{
+ .uid = 0xFFFF,
+ .pst_field_B_5 = 1
+};
+
+static const u8 sRoundedDownGrayscaleMap[] =
+{
+ 0, 0, 0, 0, 0,
+ 5, 5, 5, 5, 5,
+ 11, 11, 11, 11, 11,
+ 16, 16, 16, 16, 16,
+ 21, 21, 21, 21, 21,
+ 27, 27, 27, 27, 27,
+ 31, 31
+};
+
+void LoadCompressedPalette(const u32 *src, u16 offset, u16 size)
+{
+ LZDecompressWram(src, gPaletteDecompressionBuffer);
+ CpuCopy16(gPaletteDecompressionBuffer, gPlttBufferUnfaded + offset, size);
+ CpuCopy16(gPaletteDecompressionBuffer, gPlttBufferFaded + offset, size);
+}
+
+void LoadPalette(const void *src, u16 offset, u16 size)
+{
+ CpuCopy16(src, gPlttBufferUnfaded + offset, size);
+ CpuCopy16(src, gPlttBufferFaded + offset, size);
+}
+
+void FillPalette(u16 value, u16 offset, u16 size)
+{
+ CpuFill16(value, gPlttBufferUnfaded + offset, size);
+ CpuFill16(value, gPlttBufferFaded + offset, size);
+}
+
+void TransferPlttBuffer(void)
+{
+ if (!gPaletteFade.bufferTransferDisabled)
+ {
+ void *src = gPlttBufferFaded;
+ void *dest = (void *)PLTT;
+ DmaCopy16(3, src, dest, PLTT_SIZE);
+ sPlttBufferTransferPending = 0;
+ if (gPaletteFade.mode == HARDWARE_FADE && gPaletteFade.active)
+ UpdateBlendRegisters();
+ }
+}
+
+u8 UpdatePaletteFade(void)
+{
+ u8 result;
+ u8 dummy = 0;
+
+ if (sPlttBufferTransferPending)
+ return PALETTE_FADE_STATUS_LOADING;
+ if (gPaletteFade.mode == NORMAL_FADE)
+ result = UpdateNormalPaletteFade();
+ else if (gPaletteFade.mode == FAST_FADE)
+ result = UpdateFastPaletteFade();
+ else
+ result = UpdateHardwarePaletteFade();
+ sPlttBufferTransferPending = gPaletteFade.multipurpose1 | dummy;
+ return result;
+}
+
+void ResetPaletteFade(void)
+{
+ u8 i;
+
+ for (i = 0; i < 16; ++i)
+ ResetPaletteStruct(i);
+ ResetPaletteFadeControl();
+}
+
+void ReadPlttIntoBuffers(void)
+{
+ u16 i;
+ u16 *pltt = (u16 *)PLTT;
+
+ for (i = 0; i < PLTT_SIZE / 2; ++i)
+ {
+ gPlttBufferUnfaded[i] = pltt[i];
+ gPlttBufferFaded[i] = pltt[i];
+ }
+}
+
+bool8 BeginNormalPaletteFade(u32 selectedPalettes, s8 delay, u8 startY, u8 targetY, u16 blendColor)
+{
+ u8 temp;
+ u16 color = blendColor;
+
+ if (gPaletteFade.active)
+ {
+ return FALSE;
+ }
+ else
+ {
+ gPaletteFade.deltaY = 2;
+ if (delay < 0)
+ {
+ gPaletteFade.deltaY += (delay * -1);
+ delay = 0;
+ }
+ gPaletteFade_selectedPalettes = selectedPalettes;
+ gPaletteFade.delayCounter = delay;
+ gPaletteFade_delay = delay;
+ gPaletteFade.y = startY;
+ gPaletteFade.targetY = targetY;
+ gPaletteFade.blendColor = color;
+ gPaletteFade.active = TRUE;
+ gPaletteFade.mode = NORMAL_FADE;
+ if (startY < targetY)
+ gPaletteFade.yDec = FALSE;
+ else
+ gPaletteFade.yDec = TRUE;
+ UpdatePaletteFade();
+ temp = gPaletteFade.bufferTransferDisabled;
+ gPaletteFade.bufferTransferDisabled = FALSE;
+ CpuCopy32(gPlttBufferFaded, (void *)PLTT, PLTT_SIZE);
+ sPlttBufferTransferPending = 0;
+ if (gPaletteFade.mode == HARDWARE_FADE && gPaletteFade.active)
+ UpdateBlendRegisters();
+ gPaletteFade.bufferTransferDisabled = temp;
+ return TRUE;
+ }
+}
+
+// not used
+static bool8 sub_80706D0(u32 a1, u8 a2, u8 a3, u8 a4, u16 a5)
+{
+ ReadPlttIntoBuffers();
+ return BeginNormalPaletteFade(a1, a2, a3, a4, a5);
+}
+
+// not used
+static void sub_8070718(u8 a1, u32 *a2)
+{
+ u8 i;
+
+ for (i = 0; i < NUM_PALETTE_STRUCTS; ++i)
+ {
+ struct PaletteStruct *palstruct = &sPaletteStructs[i];
+
+ if (palstruct->ps_field_4_0)
+ {
+ if (palstruct->base->pst_field_8_0 == a1)
+ {
+ u8 val1 = palstruct->srcIndex;
+ u8 val2 = palstruct->base->srcCount;
+
+ if (val1 == val2)
+ {
+ sub_80709B4(palstruct);
+ if (!palstruct->ps_field_4_0)
+ continue;
+ }
+ if (palstruct->ps_field_8 == 0)
+ sub_8070790(palstruct, a2);
+ else
+ --palstruct->ps_field_8;
+ sub_80708F4(palstruct, a2);
+ }
+ }
+ }
+}
+
+// not used
+static void sub_8070790(struct PaletteStruct *a1, u32 *a2)
+{
+ s32 srcIndex;
+ s32 srcCount;
+ u8 i = 0;
+ u16 srcOffset = a1->srcIndex * a1->base->size;
+
+ if (!a1->base->pst_field_8_0)
+ {
+ while (i < a1->base->size)
+ {
+ gPlttBufferUnfaded[a1->destOffset] = a1->base->src[srcOffset];
+ gPlttBufferFaded[a1->destOffset] = a1->base->src[srcOffset];
+ ++i;
+ ++a1->destOffset;
+ ++srcOffset;
+ }
+ }
+ else
+ {
+ while (i < a1->base->size)
+ {
+ gPlttBufferFaded[a1->destOffset] = a1->base->src[srcOffset];
+ ++i;
+ ++a1->destOffset;
+ ++srcOffset;
+ }
+ }
+ a1->destOffset = a1->baseDestOffset;
+ a1->ps_field_8 = a1->base->pst_field_A;
+ ++a1->srcIndex;
+ srcIndex = a1->srcIndex;
+ srcCount = a1->base->srcCount;
+ if (srcIndex >= srcCount)
+ {
+ if (a1->ps_field_9)
+ --a1->ps_field_9;
+ a1->srcIndex = 0;
+ }
+ *a2 |= 1 << (a1->baseDestOffset >> 4);
+}
+
+// not used
+static void sub_80708F4(struct PaletteStruct *a1, u32 *a2)
+{
+ if (gPaletteFade.active && ((1 << (a1->baseDestOffset >> 4)) & gPaletteFade_selectedPalettes))
+ {
+ if (!a1->base->pst_field_8_0)
+ {
+ if (gPaletteFade.delayCounter != gPaletteFade_delay)
+ BlendPalette(a1->baseDestOffset,
+ a1->base->size,
+ gPaletteFade.y,
+ gPaletteFade.blendColor);
+ }
+ else
+ {
+ if (!gPaletteFade.delayCounter)
+ {
+ if (a1->ps_field_8 != a1->base->pst_field_A)
+ {
+ u32 srcOffset = a1->srcIndex * a1->base->size;
+ u8 i;
+
+ for (i = 0; i < a1->base->size; ++i)
+ gPlttBufferFaded[a1->baseDestOffset + i] = a1->base->src[srcOffset + i];
+ }
+ }
+ }
+ }
+}
+
+// not used
+static void sub_80709B4(struct PaletteStruct *a1)
+{
+ if (!a1->ps_field_9)
+ {
+ s32 val = a1->base->pst_field_B_5;
+
+ if (!val)
+ {
+ a1->srcIndex = 0;
+ a1->ps_field_8 = a1->base->pst_field_A;
+ a1->ps_field_9 = a1->base->pst_field_C;
+ a1->destOffset = a1->baseDestOffset;
+ }
+ else
+ {
+ if (val < 0)
+ return;
+ if (val > 2)
+ return;
+ ResetPaletteStructByUid(a1->base->uid);
+ }
+ }
+ else
+ {
+ --a1->ps_field_9;
+ }
+}
+
+void ResetPaletteStructByUid(u16 a1)
+{
+ u8 paletteNum = GetPaletteNumByUid(a1);
+ if (paletteNum != 16)
+ ResetPaletteStruct(paletteNum);
+}
+
+void ResetPaletteStruct(u8 paletteNum)
+{
+ sPaletteStructs[paletteNum].base = &gDummyPaletteStructTemplate;
+ sPaletteStructs[paletteNum].ps_field_4_0 = 0;
+ sPaletteStructs[paletteNum].baseDestOffset = 0;
+ sPaletteStructs[paletteNum].destOffset = 0;
+ sPaletteStructs[paletteNum].srcIndex = 0;
+ sPaletteStructs[paletteNum].ps_field_4_1 = 0;
+ sPaletteStructs[paletteNum].ps_field_8 = 0;
+ sPaletteStructs[paletteNum].ps_field_9 = 0;
+}
+
+void ResetPaletteFadeControl(void)
+{
+ gPaletteFade.multipurpose1 = 0;
+ gPaletteFade.multipurpose2 = 0;
+ gPaletteFade.delayCounter = 0;
+ gPaletteFade.y = 0;
+ gPaletteFade.targetY = 0;
+ gPaletteFade.blendColor = 0;
+ gPaletteFade.active = FALSE;
+ gPaletteFade.multipurpose2 = 0; // assign same value twice
+ gPaletteFade.yDec = FALSE;
+ gPaletteFade.bufferTransferDisabled = FALSE;
+ gPaletteFade.shouldResetBlendRegisters = FALSE;
+ gPaletteFade.hardwareFadeFinishing = FALSE;
+ gPaletteFade.softwareFadeFinishing = FALSE;
+ gPaletteFade.softwareFadeFinishingCounter = 0;
+ gPaletteFade.objPaletteToggle = 0;
+ gPaletteFade.deltaY = 2;
+}
+
+// not used
+static void sub_8070AFC(u16 uid)
+{
+ u8 paletteNum = GetPaletteNumByUid(uid);
+ if (paletteNum != 16)
+ sPaletteStructs[paletteNum].ps_field_4_1 = 1;
+}
+
+// not used
+static void sub_8070B28(u16 uid)
+{
+ u8 paletteNum = GetPaletteNumByUid(uid);
+ if (paletteNum != 16)
+ sPaletteStructs[paletteNum].ps_field_4_1 = 0;
+}
+
+// not used
+static u8 GetPaletteNumByUid(u16 uid)
+{
+ u8 i;
+
+ for (i = 0; i < NUM_PALETTE_STRUCTS; ++i)
+ if (sPaletteStructs[i].base->uid == uid)
+ return i;
+ return 16;
+}
+
+static u8 UpdateNormalPaletteFade(void)
+{
+ u16 paletteOffset;
+ u16 selectedPalettes;
+
+ if (!gPaletteFade.active)
+ return PALETTE_FADE_STATUS_DONE;
+ if (IsSoftwarePaletteFadeFinishing())
+ {
+ return gPaletteFade.active ? PALETTE_FADE_STATUS_ACTIVE : PALETTE_FADE_STATUS_DONE;
+ }
+ else
+ {
+ if (!gPaletteFade.objPaletteToggle)
+ {
+ if (gPaletteFade.delayCounter < gPaletteFade_delay)
+ {
+ ++gPaletteFade.delayCounter;
+ return 2;
+ }
+ gPaletteFade.delayCounter = 0;
+ }
+ paletteOffset = 0;
+ if (!gPaletteFade.objPaletteToggle)
+ {
+ selectedPalettes = gPaletteFade_selectedPalettes;
+ }
+ else
+ {
+ selectedPalettes = gPaletteFade_selectedPalettes >> 16;
+ paletteOffset = 256;
+ }
+ while (selectedPalettes)
+ {
+ if (selectedPalettes & 1)
+ BlendPalette(paletteOffset,
+ 16,
+ gPaletteFade.y,
+ gPaletteFade.blendColor);
+ selectedPalettes >>= 1;
+ paletteOffset += 16;
+ }
+ gPaletteFade.objPaletteToggle ^= 1;
+ if (!gPaletteFade.objPaletteToggle)
+ {
+ if (gPaletteFade.y == gPaletteFade.targetY)
+ {
+ gPaletteFade_selectedPalettes = 0;
+ gPaletteFade.softwareFadeFinishing = TRUE;
+ }
+ else
+ {
+ s8 val;
+
+ if (!gPaletteFade.yDec)
+ {
+ val = gPaletteFade.y;
+ val += gPaletteFade.deltaY;
+ if (val > gPaletteFade.targetY)
+ val = gPaletteFade.targetY;
+ gPaletteFade.y = val;
+ }
+ else
+ {
+ val = gPaletteFade.y;
+ val -= gPaletteFade.deltaY;
+ if (val < gPaletteFade.targetY)
+ val = gPaletteFade.targetY;
+ gPaletteFade.y = val;
+ }
+ }
+ }
+ // gPaletteFade.active cannot change since the last time it was checked. So this
+ // is equivalent to `return PALETTE_FADE_STATUS_ACTIVE;`
+ return gPaletteFade.active ? PALETTE_FADE_STATUS_ACTIVE : PALETTE_FADE_STATUS_DONE;
+ }
+}
+
+void InvertPlttBuffer(u32 selectedPalettes)
+{
+ u16 paletteOffset = 0;
+
+ while (selectedPalettes)
+ {
+ if (selectedPalettes & 1)
+ {
+ u8 i;
+
+ for (i = 0; i < 16; ++i)
+ gPlttBufferFaded[paletteOffset + i] = ~gPlttBufferFaded[paletteOffset + i];
+ }
+ selectedPalettes >>= 1;
+ paletteOffset += 16;
+ }
+}
+
+void TintPlttBuffer(u32 selectedPalettes, s8 r, s8 g, s8 b)
+{
+ u16 paletteOffset = 0;
+
+ while (selectedPalettes)
+ {
+ if (selectedPalettes & 1)
+ {
+ u8 i;
+
+ for (i = 0; i < 16; ++i)
+ {
+ struct PlttData *data = (struct PlttData *)&gPlttBufferFaded[paletteOffset + i];
+
+ data->r += r;
+ data->g += g;
+ data->b += b;
+ }
+ }
+ selectedPalettes >>= 1;
+ paletteOffset += 16;
+ }
+}
+
+void UnfadePlttBuffer(u32 selectedPalettes)
+{
+ u16 paletteOffset = 0;
+
+ while (selectedPalettes)
+ {
+ if (selectedPalettes & 1)
+ {
+ u8 i;
+
+ for (i = 0; i < 16; ++i)
+ gPlttBufferFaded[paletteOffset + i] = gPlttBufferUnfaded[paletteOffset + i];
+ }
+ selectedPalettes >>= 1;
+ paletteOffset += 16;
+ }
+}
+
+void BeginFastPaletteFade(u8 submode)
+{
+ gPaletteFade.deltaY = 2;
+ BeginFastPaletteFadeInternal(submode);
+}
+
+static void BeginFastPaletteFadeInternal(u8 submode)
+{
+ gPaletteFade.y = 31;
+ gPaletteFade_submode = submode & 0x3F;
+ gPaletteFade.active = TRUE;
+ gPaletteFade.mode = FAST_FADE;
+ if (submode == FAST_FADE_IN_FROM_BLACK)
+ CpuFill16(RGB_BLACK, gPlttBufferFaded, PLTT_SIZE);
+ if (submode == FAST_FADE_IN_FROM_WHITE)
+ CpuFill16(RGB_WHITE, gPlttBufferFaded, PLTT_SIZE);
+ UpdatePaletteFade();
+}
+
+static u8 UpdateFastPaletteFade(void)
+{
+ u16 i;
+ u16 paletteOffsetStart, paletteOffsetEnd;
+ s8 r0, g0, b0, r, g, b;
+
+ if (!gPaletteFade.active)
+ return PALETTE_FADE_STATUS_DONE;
+ if (IsSoftwarePaletteFadeFinishing())
+ return gPaletteFade.active ? PALETTE_FADE_STATUS_ACTIVE : PALETTE_FADE_STATUS_DONE;
+ if (gPaletteFade.objPaletteToggle)
+ {
+ paletteOffsetStart = 256;
+ paletteOffsetEnd = 512;
+ }
+ else
+ {
+ paletteOffsetStart = 0;
+ paletteOffsetEnd = 256;
+ }
+ switch (gPaletteFade_submode)
+ {
+ case FAST_FADE_IN_FROM_WHITE:
+ for (i = paletteOffsetStart; i < paletteOffsetEnd; ++i)
+ {
+ struct PlttData *unfaded;
+ struct PlttData *faded;
+
+ unfaded = (struct PlttData *)&gPlttBufferUnfaded[i];
+ r0 = unfaded->r;
+ g0 = unfaded->g;
+ b0 = unfaded->b;
+ faded = (struct PlttData *)&gPlttBufferFaded[i];
+ r = faded->r - 2;
+ g = faded->g - 2;
+ b = faded->b - 2;
+ if (r < r0)
+ r = r0;
+ if (g < g0)
+ g = g0;
+ if (b < b0)
+ b = b0;
+ gPlttBufferFaded[i] = r | (g << 5) | (b << 10);
+ }
+ break;
+ case FAST_FADE_OUT_TO_WHITE:
+ for (i = paletteOffsetStart; i < paletteOffsetEnd; ++i)
+ {
+ struct PlttData *data = (struct PlttData *)&gPlttBufferFaded[i];
+
+ r = data->r + 2;
+ g = data->g + 2;
+ b = data->b + 2;
+ if (r > 31)
+ r = 31;
+ if (g > 31)
+ g = 31;
+ if (b > 31)
+ b = 31;
+ gPlttBufferFaded[i] = r | (g << 5) | (b << 10);
+ }
+ break;
+ case FAST_FADE_IN_FROM_BLACK:
+ for (i = paletteOffsetStart; i < paletteOffsetEnd; ++i)
+ {
+ struct PlttData *unfaded;
+ struct PlttData *faded;
+
+ unfaded = (struct PlttData *)&gPlttBufferUnfaded[i];
+ r0 = unfaded->r;
+ g0 = unfaded->g;
+ b0 = unfaded->b;
+ faded = (struct PlttData *)&gPlttBufferFaded[i];
+ r = faded->r + 2;
+ g = faded->g + 2;
+ b = faded->b + 2;
+ if (r > r0)
+ r = r0;
+ if (g > g0)
+ g = g0;
+ if (b > b0)
+ b = b0;
+ gPlttBufferFaded[i] = r | (g << 5) | (b << 10);
+ }
+ break;
+ case FAST_FADE_OUT_TO_BLACK:
+ for (i = paletteOffsetStart; i < paletteOffsetEnd; ++i)
+ {
+ struct PlttData *data = (struct PlttData *)&gPlttBufferFaded[i];
+
+ r = data->r - 2;
+ g = data->g - 2;
+ b = data->b - 2;
+ if (r < 0)
+ r = 0;
+ if (g < 0)
+ g = 0;
+ if (b < 0)
+ b = 0;
+ gPlttBufferFaded[i] = r | (g << 5) | (b << 10);
+ }
+ }
+ gPaletteFade.objPaletteToggle ^= 1;
+ if (gPaletteFade.objPaletteToggle)
+ // gPaletteFade.active cannot change since the last time it was checked. So this
+ // is equivalent to `return PALETTE_FADE_STATUS_ACTIVE;`
+ return gPaletteFade.active ? PALETTE_FADE_STATUS_ACTIVE : PALETTE_FADE_STATUS_DONE;
+ if (gPaletteFade.y - gPaletteFade.deltaY < 0)
+ gPaletteFade.y = 0;
+ else
+ gPaletteFade.y -= gPaletteFade.deltaY;
+ if (gPaletteFade.y == 0)
+ {
+ switch (gPaletteFade_submode)
+ {
+ case FAST_FADE_IN_FROM_WHITE:
+ case FAST_FADE_IN_FROM_BLACK:
+ CpuCopy32(gPlttBufferUnfaded, gPlttBufferFaded, PLTT_SIZE);
+ break;
+ case FAST_FADE_OUT_TO_WHITE:
+ CpuFill32(0xFFFFFFFF, gPlttBufferFaded, PLTT_SIZE);
+ break;
+ case FAST_FADE_OUT_TO_BLACK:
+ CpuFill32(0x00000000, gPlttBufferFaded, PLTT_SIZE);
+ break;
+ }
+ gPaletteFade.mode = NORMAL_FADE;
+ gPaletteFade.softwareFadeFinishing = TRUE;
+ }
+ // gPaletteFade.active cannot change since the last time it was checked. So this
+ // is equivalent to `return PALETTE_FADE_STATUS_ACTIVE;`
+ return gPaletteFade.active ? PALETTE_FADE_STATUS_ACTIVE : PALETTE_FADE_STATUS_DONE;
+}
+
+void BeginHardwarePaletteFade(u8 blendCnt, u8 delay, u8 y, u8 targetY, u8 shouldResetBlendRegisters)
+{
+ gPaletteFade_blendCnt = blendCnt;
+ gPaletteFade.delayCounter = delay;
+ gPaletteFade_delay = delay;
+ gPaletteFade.y = y;
+ gPaletteFade.targetY = targetY;
+ gPaletteFade.active = TRUE;
+ gPaletteFade.mode = HARDWARE_FADE;
+ gPaletteFade.shouldResetBlendRegisters = shouldResetBlendRegisters & 1;
+ gPaletteFade.hardwareFadeFinishing = FALSE;
+ if (y < targetY)
+ gPaletteFade.yDec = FALSE;
+ else
+ gPaletteFade.yDec = TRUE;
+}
+
+static u8 UpdateHardwarePaletteFade(void)
+{
+ if (!gPaletteFade.active)
+ return PALETTE_FADE_STATUS_DONE;
+ if (gPaletteFade.delayCounter < gPaletteFade_delay)
+ {
+ ++gPaletteFade.delayCounter;
+ return PALETTE_FADE_STATUS_DELAY;
+ }
+ gPaletteFade.delayCounter = 0;
+ if (!gPaletteFade.yDec)
+ {
+ ++gPaletteFade.y;
+ if (gPaletteFade.y > gPaletteFade.targetY)
+ {
+ ++gPaletteFade.hardwareFadeFinishing;
+ --gPaletteFade.y;
+ }
+ }
+ else
+ {
+ if (gPaletteFade.y-- - 1 < gPaletteFade.targetY)
+ {
+ ++gPaletteFade.hardwareFadeFinishing;
+ ++gPaletteFade.y;
+ }
+ }
+
+ if (gPaletteFade.hardwareFadeFinishing)
+ {
+ if (gPaletteFade.shouldResetBlendRegisters)
+ {
+ gPaletteFade_blendCnt = 0;
+ gPaletteFade.y = 0;
+ }
+ gPaletteFade.shouldResetBlendRegisters = FALSE;
+ }
+ // gPaletteFade.active cannot change since the last time it was checked. So this
+ // is equivalent to `return PALETTE_FADE_STATUS_ACTIVE;`
+ return gPaletteFade.active ? PALETTE_FADE_STATUS_ACTIVE : PALETTE_FADE_STATUS_DONE;
+}
+
+static void UpdateBlendRegisters(void)
+{
+ SetGpuReg(REG_OFFSET_BLDCNT, (u16)gPaletteFade_blendCnt);
+ SetGpuReg(REG_OFFSET_BLDY, gPaletteFade.y);
+ if (gPaletteFade.hardwareFadeFinishing)
+ {
+ gPaletteFade.hardwareFadeFinishing = FALSE;
+ gPaletteFade.mode = 0;
+ gPaletteFade_blendCnt = 0;
+ gPaletteFade.y = 0;
+ gPaletteFade.active = FALSE;
+ }
+}
+
+static bool8 IsSoftwarePaletteFadeFinishing(void)
+{
+ if (gPaletteFade.softwareFadeFinishing)
+ {
+ if (gPaletteFade.softwareFadeFinishingCounter == 4)
+ {
+ gPaletteFade.active = FALSE;
+ gPaletteFade.softwareFadeFinishing = FALSE;
+ gPaletteFade.softwareFadeFinishingCounter = 0;
+ }
+ else
+ {
+ ++gPaletteFade.softwareFadeFinishingCounter;
+ }
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+void BlendPalettes(u32 selectedPalettes, u8 coeff, u16 color)
+{
+ u16 paletteOffset;
+
+ for (paletteOffset = 0; selectedPalettes; paletteOffset += 16)
+ {
+ if (selectedPalettes & 1)
+ BlendPalette(paletteOffset, 16, coeff, color);
+ selectedPalettes >>= 1;
+ }
+}
+
+void BlendPalettesUnfaded(u32 selectedPalettes, u8 coeff, u16 color)
+{
+ // This copy is done via DMA in both RUBY and EMERALD
+ CpuFastCopy(gPlttBufferUnfaded, gPlttBufferFaded, 0x400);
+ BlendPalettes(selectedPalettes, coeff, color);
+}
+
+void TintPalette_GrayScale(u16 *palette, u16 count)
+{
+ s32 r, g, b, i;
+ u32 gray;
+
+ for (i = 0; i < count; ++i)
+ {
+ r = (*palette >> 0) & 0x1F;
+ g = (*palette >> 5) & 0x1F;
+ b = (*palette >> 10) & 0x1F;
+ gray = (r * Q_8_8(0.3) + g * Q_8_8(0.59) + b * Q_8_8(0.1133)) >> 8;
+ *palette++ = (gray << 10) | (gray << 5) | (gray << 0);
+ }
+}
+
+void TintPalette_GrayScale2(u16 *palette, u16 count)
+{
+ s32 r, g, b, i;
+ u32 gray;
+
+ for (i = 0; i < count; ++i)
+ {
+ r = (*palette >> 0) & 0x1F;
+ g = (*palette >> 5) & 0x1F;
+ b = (*palette >> 10) & 0x1F;
+ gray = (r * Q_8_8(0.3) + g * Q_8_8(0.59) + b * Q_8_8(0.1133)) >> 8;
+
+ if (gray > 0x1F)
+ gray = 0x1F;
+ gray = sRoundedDownGrayscaleMap[gray];
+ *palette++ = (gray << 10) | (gray << 5) | (gray << 0);
+ }
+}
+
+void TintPalette_SepiaTone(u16 *palette, u16 count)
+{
+ s32 r, g, b, i;
+ u32 gray;
+
+ for (i = 0; i < count; ++i)
+ {
+ r = (*palette >> 0) & 0x1F;
+ g = (*palette >> 5) & 0x1F;
+ b = (*palette >> 10) & 0x1F;
+ gray = (r * Q_8_8(0.3) + g * Q_8_8(0.59) + b * Q_8_8(0.1133)) >> 8;
+ r = (u16)((Q_8_8(1.2) * gray)) >> 8;
+ g = (u16)((Q_8_8(1.0) * gray)) >> 8;
+ b = (u16)((Q_8_8(0.94) * gray)) >> 8;
+ if (r > 31)
+ r = 31;
+ *palette++ = (b << 10) | (g << 5) | (r << 0);
+ }
+}
+
+void TintPalette_CustomTone(u16 *palette, u16 count, u16 rTone, u16 gTone, u16 bTone)
+{
+ s32 r, g, b, i;
+ u32 gray;
+
+ for (i = 0; i < count; ++i)
+ {
+ r = (*palette >> 0) & 0x1F;
+ g = (*palette >> 5) & 0x1F;
+ b = (*palette >> 10) & 0x1F;
+ gray = (r * Q_8_8(0.3) + g * Q_8_8(0.59) + b * Q_8_8(0.1133)) >> 8;
+ r = (u16)((rTone * gray)) >> 8;
+ g = (u16)((gTone * gray)) >> 8;
+ b = (u16)((bTone * gray)) >> 8;
+ if (r > 31)
+ r = 31;
+ if (g > 31)
+ g = 31;
+ if (b > 31)
+ b = 31;
+ *palette++ = (b << 10) | (g << 5) | (r << 0);
+ }
+}
+
+void sub_80716F8(const u16 *src, u16 *dst, u16 count, u8 a4)
+{
+ s32 r, g, b, i;
+ u32 gray;
+
+ if (!a4)
+ {
+ for (i = 0; i < count; ++i)
+ *dst++ = *src++;
+ }
+ else
+ {
+ for (i = 0; i < count; ++src, ++dst, ++i)
+ {
+ r = (*src >> 0) & 0x1F;
+ g = (*src >> 5) & 0x1F;
+ b = (*src >> 10) & 0x1F;
+ gray = (r * Q_8_8(0.3) + g * Q_8_8(0.59) + b * Q_8_8(0.1133)) >> 8;
+ r += (a4 * (gray - r) >> 4);
+ g += (a4 * (gray - g) >> 4);
+ b += (a4 * (gray - b) >> 4);
+ *dst = (b << 10) | (g << 5) | (r << 0);
+ }
+ }
+}
+
+void sub_80717A8(u32 a1, s8 a2, u8 a3, u8 a4, u16 a5, u8 a6, u8 a7)
+{
+ u8 taskId;
+
+ taskId = CreateTask(sub_80718B8, a6);
+ gTasks[taskId].data[0] = a3;
+ gTasks[taskId].data[1] = a4;
+ if (a2 >= 0)
+ {
+ gTasks[taskId].data[3] = a2;
+ gTasks[taskId].data[2] = 1;
+ }
+ else
+ {
+ gTasks[taskId].data[3] = 0;
+ gTasks[taskId].data[2] = -a2 + 1;
+ }
+ if (a4 < a3)
+ gTasks[taskId].data[2] *= -1;
+ SetWordTaskArg(taskId, 5, a1);
+ gTasks[taskId].data[7] = a5;
+ gTasks[taskId].data[8] = a7;
+ gTasks[taskId].func(taskId);
+}
+
+bool32 sub_807185C(u8 var)
+{
+ s32 i;
+
+ for (i = 0; i < NUM_TASKS; ++i)
+ if (gTasks[i].isActive == TRUE
+ && gTasks[i].func == sub_80718B8
+ && gTasks[i].data[8] == var)
+ return TRUE;
+ return FALSE;
+}
+
+void sub_8071898(void)
+{
+ u8 taskId;
+
+ while (TRUE)
+ {
+ taskId = FindTaskIdByFunc(sub_80718B8);
+ if (taskId == TASK_NONE)
+ break;
+ DestroyTask(taskId);
+ }
+}
+
+static void sub_80718B8(u8 taskId)
+{
+ u32 wordVar;
+ s16 *data;
+ s16 temp;
+
+ data = gTasks[taskId].data;
+ wordVar = GetWordTaskArg(taskId, 5);
+ if (++data[4] > data[3])
+ {
+ data[4] = 0;
+ BlendPalettes(wordVar, data[0], data[7]);
+ temp = data[1];
+ if (data[0] == temp)
+ {
+ DestroyTask(taskId);
+ }
+ else
+ {
+ data[0] += data[2];
+ if (data[2] >= 0)
+ {
+ if (data[0] < temp)
+ return;
+ }
+ else if (data[0] > temp)
+ {
+ return;
+ }
+ data[0] = temp;
+ }
+ }
+}
diff --git a/src/pokedex_area_markers.c b/src/pokedex_area_markers.c
new file mode 100644
index 000000000..d3c36a83e
--- /dev/null
+++ b/src/pokedex_area_markers.c
@@ -0,0 +1,238 @@
+#include "global.h"
+#include "malloc.h"
+#include "bg.h"
+#include "decompress.h"
+#include "gpu_regs.h"
+#include "palette.h"
+#include "task.h"
+#include "wild_pokemon_area.h"
+#include "pokedex_area_markers.h"
+
+static const u16 sMarkerPal[] = INCBIN_U16("data/pokedex_area_markers/marker.gbapal");
+static const u32 sMarkerTiles[] = INCBIN_U32("data/pokedex_area_markers/marker.4bpp.lz");
+
+static const struct Subsprite sSubsprite0 = {
+ .size = ST_OAM_SIZE_0,
+ .shape = ST_OAM_SQUARE,
+ .priority = 1,
+ .tileOffset = 0
+};
+
+static const struct Subsprite sSubsprite1 = {
+ .size = ST_OAM_SIZE_0,
+ .shape = ST_OAM_H_RECTANGLE,
+ .priority = 1,
+ .tileOffset = 1
+};
+
+static const struct Subsprite sSubsprite2 = {
+ .size = ST_OAM_SIZE_0,
+ .shape = ST_OAM_V_RECTANGLE,
+ .priority = 1,
+ .tileOffset = 3
+};
+
+static const struct Subsprite sSubsprite3 = {
+ .size = ST_OAM_SIZE_2,
+ .shape = ST_OAM_H_RECTANGLE,
+ .priority = 1,
+ .tileOffset = 5
+};
+
+static const struct Subsprite sSubsprite4 = {
+ .size = ST_OAM_SIZE_2,
+ .shape = ST_OAM_V_RECTANGLE,
+ .priority = 1,
+ .tileOffset = 13
+};
+
+static const struct Subsprite sSubsprite5 = {
+ .size = ST_OAM_SIZE_2,
+ .shape = ST_OAM_H_RECTANGLE,
+ .priority = 1,
+ .tileOffset = 21
+};
+
+static const struct Subsprite sSubsprite6 = {
+ .size = ST_OAM_SIZE_2,
+ .shape = ST_OAM_V_RECTANGLE,
+ .priority = 1,
+ .tileOffset = 29
+};
+
+
+static const struct Subsprite *const sSubsprites[] = {
+ &sSubsprite0,
+ &sSubsprite1,
+ &sSubsprite2,
+ &sSubsprite3,
+ &sSubsprite4,
+ &sSubsprite5,
+ &sSubsprite6
+};
+
+static const s8 sSubspriteLookupTable[][4] = {
+ { 0, 0x00, 0x00 },
+ { 0, 0x36, 0x2c },
+ { 0, 0x36, 0x1c },
+ { 0, 0x36, 0x0c },
+ { 0, 0x5c, 0x0c },
+ { 0, 0x6e, 0x18 },
+ { 0, 0x5c, 0x24 },
+ { 0, 0x4c, 0x18 },
+ { 0, 0x4e, 0x34 },
+ { 0, 0x36, 0x3e },
+ { 0, 0x2a, 0x02 },
+ { 0, 0x5c, 0x18 },
+ { 2, 0x36, 0x20 },
+ { 2, 0x36, 0x10 },
+ { 1, 0x3d, 0x0c },
+ { 1, 0x4d, 0x0c },
+ { 0, 0x5c, 0x12 },
+ { 0, 0x5c, 0x1e },
+ { 0, 0x54, 0x18 },
+ { 1, 0x62, 0x18 },
+ { 1, 0x62, 0x0c },
+ { 2, 0x6e, 0x0c },
+ { 1, 0x62, 0x24 },
+ { 4, 0x6a, 0x19 },
+ { 1, 0x64, 0x2e },
+ { 2, 0x5e, 0x2d },
+ { 1, 0x55, 0x34 },
+ { 0, 0x44, 0x18 },
+ { 4, 0x3e, 0x1a },
+ { 1, 0x40, 0x34 },
+ { 0, 0x4e, 0x3c },
+ { 3, 0x37, 0x3a },
+ { 2, 0x36, 0x32 },
+ { 1, 0x28, 0x1c },
+ { 4, 0x26, 0x04 },
+ { 0, 0x5c, 0x04 },
+ { 3, 0x5a, 0xfe },
+ { 0, 0x33, 0x14 },
+ { 1, 0x3d, 0x12 },
+ { 0, 0x48, 0x08 },
+ { 0, 0x57, 0x08 },
+ { 0, 0x70, 0x0e },
+ { 0, 0x71, 0x14 },
+ { 0, 0x71, 0x19 },
+ { 1, 0x4e, 0x2c },
+ { 0, 0x41, 0x3c },
+ { 0, 0x34, 0x3e },
+ { 0, 0x2d, 0x07 },
+ { 0, 0x0a, 0x0a },
+ { 0, 0x0c, 0x23 },
+ { 0, 0x0e, 0x34 },
+ { 0, 0x0c, 0x54 },
+ { 0, 0x2d, 0x51 },
+ { 0, 0x4c, 0x54 },
+ { 0, 0x68, 0x52 },
+ { 2, 0x0e, 0x02 },
+ { 0, 0x0a, 0x0f },
+ { 0, 0x0c, 0x1d },
+ { 1, 0x02, 0x34 },
+ { 1, 0x0c, 0x38 },
+ { 1, 0x2c, 0x4a },
+ { 1, 0x24, 0x4e },
+ { 2, 0x30, 0x50 },
+ { 2, 0x34, 0x56 },
+ { 0, 0x48, 0x4a },
+ { 1, 0x48, 0x4e },
+ { 2, 0x51, 0x50 },
+ { 0, 0x4c, 0x5c },
+ { 0, 0x68, 0x4b },
+ { 0, 0x68, 0x56 },
+ { 2, 0x6c, 0x53 },
+ { 3, 0x60, 0x5a },
+ { 0, 0x0e, 0x01 },
+ { 0, 0x05, 0x34 },
+ { 0, 0x0d, 0x50 },
+ { 0, 0x36, 0x4a },
+ { 0, 0x45, 0x49 },
+ { 0, 0x4c, 0x4d },
+ { 0, 0x49, 0x5f },
+ { 3, 0x60, 0x5a }
+};
+
+static void Task_ShowAreaMarkers(u8 taskId)
+{
+ struct PAM_TaskData * data = (void *)gTasks[taskId].data;
+ gSprites[data->spr_id].invisible = FALSE;
+}
+
+u8 sub_8134230(u16 species, u16 tilesTag, u8 palIdx, u8 y)
+{
+ struct SpriteTemplate spriteTemplate;
+ struct CompressedSpriteSheet spriteSheet;
+ u8 taskId;
+ struct PAM_TaskData * data;
+ struct Subsprite * subsprites;
+
+ spriteSheet.data = sMarkerTiles;
+ spriteSheet.size = 0x4A0;
+ spriteSheet.tag = tilesTag;
+ LoadCompressedSpriteSheet(&spriteSheet);
+ LoadPalette(sMarkerPal, 0x100 + 16 * palIdx, 0x20);
+ taskId = CreateTask(Task_ShowAreaMarkers, 0);
+ data = (void *)gTasks[taskId].data;
+ data->unk_0C = 0;
+ data->tilesTag = tilesTag;
+ data->unk_10 = 0xFFFF;
+ subsprites = Alloc(120 * sizeof(struct Subsprite));
+ data->buffer = subsprites;
+ data->subsprites.subsprites = subsprites;
+ data->subsprites.subspriteCount = BuildPokedexAreaSubspriteBuffer(species, subsprites);
+ SetGpuRegBits(REG_OFFSET_DISPCNT, DISPCNT_OBJWIN_ON);
+ SetGpuReg(REG_OFFSET_BLDCNT, BLDCNT_TGT1_BG1 | BLDCNT_EFFECT_BLEND | BLDCNT_TGT2_BG0 | BLDCNT_TGT2_BG1 | BLDCNT_TGT2_BG2 | BLDCNT_TGT2_BG3 | BLDCNT_TGT2_BD);
+ SetGpuReg(REG_OFFSET_BLDALPHA, BLDALPHA_BLEND(12, 8));
+ SetGpuReg(REG_OFFSET_BLDY, 0);
+ SetGpuReg(REG_OFFSET_WININ, 0x1F1F);
+ SetGpuReg(REG_OFFSET_WINOUT, 0x2F3D);
+ spriteTemplate = gDummySpriteTemplate;
+ spriteTemplate.tileTag = tilesTag;
+ data->spr_id = CreateSprite(&spriteTemplate, 104, y + 32, 0);
+ SetSubspriteTables(&gSprites[data->spr_id], &data->subsprites);
+ gSprites[data->spr_id].oam.objMode = ST_OAM_OBJ_WINDOW;
+ gSprites[data->spr_id].oam.paletteNum = palIdx;
+ gSprites[data->spr_id].subspriteTableNum = 0;
+ gSprites[data->spr_id].invisible = TRUE;
+ HideBg(1);
+ SetBgAttribute(1, BG_ATTR_CHARBASEINDEX, 0);
+ FillBgTilemapBufferRect_Palette0(1, 0x00F, 0, 0, 30, 20);
+ CopyBgTilemapBufferToVram(1);
+ ShowBg(1);
+ return taskId;
+}
+
+void sub_81343F4(u8 taskId)
+{
+ struct PAM_TaskData * data = (void *)gTasks[taskId].data;
+ FreeSpriteTilesByTag(data->tilesTag);
+ DestroySprite(&gSprites[data->spr_id]);
+ Free(data->buffer);
+ SetGpuReg(REG_OFFSET_BLDCNT, 0);
+ SetGpuReg(REG_OFFSET_BLDALPHA, 0);
+ SetGpuReg(REG_OFFSET_BLDY, 0);
+ SetGpuReg(REG_OFFSET_WININ, 0x1F1F);
+ SetGpuReg(REG_OFFSET_WINOUT, 0x1F1F);
+ ClearGpuRegBits(REG_OFFSET_DISPCNT, DISPCNT_OBJWIN_ON);
+ HideBg(1);
+ SetBgAttribute(1, BG_ATTR_CHARBASEINDEX, 2);
+ FillBgTilemapBufferRect_Palette0(1, 0x000, 0, 0, 30, 20);
+ CopyBgTilemapBufferToVram(1);
+ ShowBg(1);
+ DestroyTask(taskId);
+}
+
+void SetAreaSubsprite(s32 i, s32 whichArea, struct Subsprite * subsprites)
+{
+ subsprites[i] = *sSubsprites[sSubspriteLookupTable[whichArea][0]];
+ subsprites[i].x = sSubspriteLookupTable[whichArea][1];
+ subsprites[i].y = sSubspriteLookupTable[whichArea][2];
+}
+
+u8 sub_81344E0(u8 taskId)
+{
+ struct PAM_TaskData * data = (void *)gTasks[taskId].data;
+ return data->subsprites.subspriteCount;
+}
diff --git a/src/pokemon.c b/src/pokemon.c
index 6f1df249f..4685dcbcd 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);
@@ -3884,7 +3883,7 @@ bool8 ExecuteTableBasedItemEffect(struct Pokemon *mon, u16 item, u8 partyIndex,
return PokemonUseItemEffects(mon, item, partyIndex, moveIndex, 0);
}
-bool8 PokemonUseItemEffects(struct Pokemon *pkmn, u16 item, u8 partyIndex, u8 moveIndex, u8 e)
+bool8 PokemonUseItemEffects(struct Pokemon *mon, u16 item, u8 partyIndex, u8 moveIndex, u8 e)
{
u32 data;
s32 friendship;
@@ -3900,7 +3899,7 @@ bool8 PokemonUseItemEffects(struct Pokemon *pkmn, u16 item, u8 partyIndex, u8 mo
u8 r10;
u32 r4;
- heldItem = GetMonData(pkmn, MON_DATA_HELD_ITEM, NULL);
+ heldItem = GetMonData(mon, MON_DATA_HELD_ITEM, NULL);
if (heldItem == ITEM_ENIGMA_BERRY)
{
if (gMain.inBattle)
@@ -4024,27 +4023,27 @@ bool8 PokemonUseItemEffects(struct Pokemon *pkmn, u16 item, u8 partyIndex, u8 mo
retVal = FALSE;
}
if ((itemEffect[cmdIndex] & 0x40) // raise level
- && GetMonData(pkmn, MON_DATA_LEVEL, NULL) != 100)
+ && GetMonData(mon, MON_DATA_LEVEL, NULL) != 100)
{
- data = gExperienceTables[gBaseStats[GetMonData(pkmn, MON_DATA_SPECIES, NULL)].growthRate][GetMonData(pkmn, MON_DATA_LEVEL, NULL) + 1];
- SetMonData(pkmn, MON_DATA_EXP, &data);
- CalculateMonStats(pkmn);
+ data = gExperienceTables[gBaseStats[GetMonData(mon, MON_DATA_SPECIES, NULL)].growthRate][GetMonData(mon, MON_DATA_LEVEL, NULL) + 1];
+ SetMonData(mon, MON_DATA_EXP, &data);
+ CalculateMonStats(mon);
retVal = FALSE;
}
if ((itemEffect[cmdIndex] & 0x20)
- && HealStatusConditions(pkmn, partyIndex, 7, sp34) == 0)
+ && HealStatusConditions(mon, partyIndex, 7, sp34) == 0)
{
if (sp34 != 4)
gBattleMons[sp34].status2 &= ~STATUS2_NIGHTMARE;
retVal = FALSE;
}
- if ((itemEffect[cmdIndex] & 0x10) && HealStatusConditions(pkmn, partyIndex, 0xF88, sp34) == 0)
+ if ((itemEffect[cmdIndex] & 0x10) && HealStatusConditions(mon, partyIndex, 0xF88, sp34) == 0)
retVal = FALSE;
- if ((itemEffect[cmdIndex] & 8) && HealStatusConditions(pkmn, partyIndex, 16, sp34) == 0)
+ if ((itemEffect[cmdIndex] & 8) && HealStatusConditions(mon, partyIndex, 16, sp34) == 0)
retVal = FALSE;
- if ((itemEffect[cmdIndex] & 4) && HealStatusConditions(pkmn, partyIndex, 32, sp34) == 0)
+ if ((itemEffect[cmdIndex] & 4) && HealStatusConditions(mon, partyIndex, 32, sp34) == 0)
retVal = FALSE;
- if ((itemEffect[cmdIndex] & 2) && HealStatusConditions(pkmn, partyIndex, 64, sp34) == 0)
+ if ((itemEffect[cmdIndex] & 2) && HealStatusConditions(mon, partyIndex, 64, sp34) == 0)
retVal = FALSE;
if ((itemEffect[cmdIndex] & 1) // heal confusion
&& gMain.inBattle && sp34 != 4 && (gBattleMons[sp34].status2 & STATUS2_CONFUSION))
@@ -4059,16 +4058,16 @@ bool8 PokemonUseItemEffects(struct Pokemon *pkmn, u16 item, u8 partyIndex, u8 mo
if (r10 & 0x20)
{
r10 &= ~0x20;
- data = (GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL) & gPPUpGetMask[moveIndex]) >> (moveIndex * 2);
- sp28 = CalculatePPWithBonus(GetMonData(pkmn, MON_DATA_MOVE1 + moveIndex, NULL), GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL), moveIndex);
+ data = (GetMonData(mon, MON_DATA_PP_BONUSES, NULL) & gPPUpGetMask[moveIndex]) >> (moveIndex * 2);
+ sp28 = CalculatePPWithBonus(GetMonData(mon, MON_DATA_MOVE1 + moveIndex, NULL), GetMonData(mon, MON_DATA_PP_BONUSES, NULL), moveIndex);
if (data < 3 && sp28 > 4)
{
- data = GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL) + gPPUpAddMask[moveIndex];
- SetMonData(pkmn, MON_DATA_PP_BONUSES, &data);
+ data = GetMonData(mon, MON_DATA_PP_BONUSES, NULL) + gPPUpAddMask[moveIndex];
+ SetMonData(mon, MON_DATA_PP_BONUSES, &data);
- data = CalculatePPWithBonus(GetMonData(pkmn, MON_DATA_MOVE1 + moveIndex, NULL), data, moveIndex) - sp28;
- data = GetMonData(pkmn, MON_DATA_PP1 + moveIndex, NULL) + data;
- SetMonData(pkmn, MON_DATA_PP1 + moveIndex, &data);
+ data = CalculatePPWithBonus(GetMonData(mon, MON_DATA_MOVE1 + moveIndex, NULL), data, moveIndex) - sp28;
+ data = GetMonData(mon, MON_DATA_PP1 + moveIndex, NULL) + data;
+ SetMonData(mon, MON_DATA_PP1 + moveIndex, &data);
retVal = FALSE;
}
}
@@ -4084,10 +4083,10 @@ bool8 PokemonUseItemEffects(struct Pokemon *pkmn, u16 item, u8 partyIndex, u8 mo
{
case 0:
case 1:
- evCount = GetMonEVCount(pkmn);
+ evCount = GetMonEVCount(mon);
if (evCount >= 510)
return TRUE;
- data = GetMonData(pkmn, sGetMonDataEVConstants[sp28], NULL);
+ data = GetMonData(mon, sGetMonDataEVConstants[sp28], NULL);
if (data < 100)
{
if (data + itemEffect[sp24] > 100)
@@ -4097,8 +4096,8 @@ bool8 PokemonUseItemEffects(struct Pokemon *pkmn, u16 item, u8 partyIndex, u8 mo
if (evCount + r4 > 510)
r4 += 510 - (evCount + r4);
data += r4;
- SetMonData(pkmn, sGetMonDataEVConstants[sp28], &data);
- CalculateMonStats(pkmn);
+ SetMonData(mon, sGetMonDataEVConstants[sp28], &data);
+ CalculateMonStats(mon);
sp24++;
retVal = FALSE;
}
@@ -4107,7 +4106,7 @@ bool8 PokemonUseItemEffects(struct Pokemon *pkmn, u16 item, u8 partyIndex, u8 mo
// revive?
if (r10 & 0x10)
{
- if (GetMonData(pkmn, MON_DATA_HP, NULL) != 0)
+ if (GetMonData(mon, MON_DATA_HP, NULL) != 0)
{
sp24++;
break;
@@ -4118,20 +4117,20 @@ 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++;
}
}
}
else
{
- if (GetMonData(pkmn, MON_DATA_HP, NULL) == 0)
+ if (GetMonData(mon, MON_DATA_HP, NULL) == 0)
{
sp24++;
break;
@@ -4141,10 +4140,10 @@ bool8 PokemonUseItemEffects(struct Pokemon *pkmn, u16 item, u8 partyIndex, u8 mo
switch (data)
{
case 0xFF:
- data = GetMonData(pkmn, MON_DATA_MAX_HP, NULL) - GetMonData(pkmn, MON_DATA_HP, NULL);
+ data = GetMonData(mon, MON_DATA_MAX_HP, NULL) - GetMonData(mon, MON_DATA_HP, NULL);
break;
case 0xFE:
- data = GetMonData(pkmn, MON_DATA_MAX_HP, NULL) / 2;
+ data = GetMonData(mon, MON_DATA_MAX_HP, NULL) / 2;
if (data == 0)
data = 1;
break;
@@ -4152,26 +4151,26 @@ bool8 PokemonUseItemEffects(struct Pokemon *pkmn, u16 item, u8 partyIndex, u8 mo
data = gBattleScripting.field_23;
break;
}
- if (GetMonData(pkmn, MON_DATA_MAX_HP, NULL) != GetMonData(pkmn, MON_DATA_HP, NULL))
+ if (GetMonData(mon, MON_DATA_MAX_HP, NULL) != GetMonData(mon, MON_DATA_HP, NULL))
{
if (e == 0)
{
- data = GetMonData(pkmn, MON_DATA_HP, NULL) + data;
- if (data > GetMonData(pkmn, MON_DATA_MAX_HP, NULL))
- data = GetMonData(pkmn, MON_DATA_MAX_HP, NULL);
- SetMonData(pkmn, MON_DATA_HP, &data);
+ data = GetMonData(mon, MON_DATA_HP, NULL) + data;
+ if (data > GetMonData(mon, MON_DATA_MAX_HP, NULL))
+ data = GetMonData(mon, MON_DATA_MAX_HP, NULL);
+ SetMonData(mon, MON_DATA_HP, &data);
if (gMain.inBattle && sp34 != 4)
{
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;
}
}
@@ -4191,21 +4190,21 @@ bool8 PokemonUseItemEffects(struct Pokemon *pkmn, u16 item, u8 partyIndex, u8 mo
{
u16 r4;
- data = GetMonData(pkmn, MON_DATA_PP1 + r5, NULL);
- r4 = GetMonData(pkmn, MON_DATA_MOVE1 + r5, NULL);
- if (data != CalculatePPWithBonus(r4, GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL), r5))
+ data = GetMonData(mon, MON_DATA_PP1 + r5, NULL);
+ r4 = GetMonData(mon, MON_DATA_MOVE1 + r5, NULL);
+ if (data != CalculatePPWithBonus(r4, GetMonData(mon, MON_DATA_PP_BONUSES, NULL), r5))
{
data += itemEffect[sp24];
- r4 = GetMonData(pkmn, MON_DATA_MOVE1 + r5, NULL);
- if (data > CalculatePPWithBonus(r4, GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL), r5))
+ r4 = GetMonData(mon, MON_DATA_MOVE1 + r5, NULL);
+ if (data > CalculatePPWithBonus(r4, GetMonData(mon, MON_DATA_PP_BONUSES, NULL), r5))
{
- r4 = GetMonData(pkmn, MON_DATA_MOVE1 + r5, NULL);
- data = CalculatePPWithBonus(r4, GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL), r5);
+ r4 = GetMonData(mon, MON_DATA_MOVE1 + r5, NULL);
+ data = CalculatePPWithBonus(r4, GetMonData(mon, MON_DATA_PP_BONUSES, NULL), r5);
}
- SetMonData(pkmn, MON_DATA_PP1 + r5, &data);
+ SetMonData(mon, 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;
}
@@ -4216,21 +4215,21 @@ bool8 PokemonUseItemEffects(struct Pokemon *pkmn, u16 item, u8 partyIndex, u8 mo
{
u16 r4;
- data = GetMonData(pkmn, MON_DATA_PP1 + moveIndex, NULL);
- r4 = GetMonData(pkmn, MON_DATA_MOVE1 + moveIndex, NULL);
- if (data != CalculatePPWithBonus(r4, GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL), moveIndex))
+ data = GetMonData(mon, MON_DATA_PP1 + moveIndex, NULL);
+ r4 = GetMonData(mon, MON_DATA_MOVE1 + moveIndex, NULL);
+ if (data != CalculatePPWithBonus(r4, GetMonData(mon, MON_DATA_PP_BONUSES, NULL), moveIndex))
{
data += itemEffect[sp24++];
- r4 = GetMonData(pkmn, MON_DATA_MOVE1 + moveIndex, NULL);
- if (data > CalculatePPWithBonus(r4, GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL), moveIndex))
+ r4 = GetMonData(mon, MON_DATA_MOVE1 + moveIndex, NULL);
+ if (data > CalculatePPWithBonus(r4, GetMonData(mon, MON_DATA_PP_BONUSES, NULL), moveIndex))
{
- r4 = GetMonData(pkmn, MON_DATA_MOVE1 + moveIndex, NULL);
- data = CalculatePPWithBonus(r4, GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL), moveIndex);
+ r4 = GetMonData(mon, MON_DATA_MOVE1 + moveIndex, NULL);
+ data = CalculatePPWithBonus(r4, GetMonData(mon, MON_DATA_PP_BONUSES, NULL), moveIndex);
}
- SetMonData(pkmn, MON_DATA_PP1 + moveIndex, &data);
+ SetMonData(mon, 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;
}
@@ -4238,11 +4237,11 @@ bool8 PokemonUseItemEffects(struct Pokemon *pkmn, u16 item, u8 partyIndex, u8 mo
break;
case 7:
{
- u16 targetSpecies = GetEvolutionTargetSpecies(pkmn, 2, item);
+ u16 targetSpecies = GetEvolutionTargetSpecies(mon, 2, item);
if (targetSpecies != SPECIES_NONE)
{
- BeginEvolutionScene(pkmn, targetSpecies, 0, partyIndex);
+ BeginEvolutionScene(mon, targetSpecies, 0, partyIndex);
return FALSE;
}
}
@@ -4268,10 +4267,10 @@ bool8 PokemonUseItemEffects(struct Pokemon *pkmn, u16 item, u8 partyIndex, u8 mo
case 1:
case 2:
case 3:
- evCount = GetMonEVCount(pkmn);
+ evCount = GetMonEVCount(mon);
if (evCount >= 510)
return TRUE;
- data = GetMonData(pkmn, sGetMonDataEVConstants[sp28 + 2], NULL);
+ data = GetMonData(mon, sGetMonDataEVConstants[sp28 + 2], NULL);
if (data < 100)
{
if (data + itemEffect[sp24] > 100)
@@ -4281,98 +4280,98 @@ bool8 PokemonUseItemEffects(struct Pokemon *pkmn, u16 item, u8 partyIndex, u8 mo
if (evCount + r4 > 510)
r4 += 510 - (evCount + r4);
data += r4;
- SetMonData(pkmn, sGetMonDataEVConstants[sp28 + 2], &data);
- CalculateMonStats(pkmn);
+ SetMonData(mon, sGetMonDataEVConstants[sp28 + 2], &data);
+ CalculateMonStats(mon);
retVal = FALSE;
sp24++;
}
break;
case 4:
- data = (GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL) & gPPUpGetMask[moveIndex]) >> (moveIndex * 2);
+ data = (GetMonData(mon, MON_DATA_PP_BONUSES, NULL) & gPPUpGetMask[moveIndex]) >> (moveIndex * 2);
if (data < 3)
{
- r4 = CalculatePPWithBonus(GetMonData(pkmn, MON_DATA_MOVE1 + moveIndex, NULL), GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL), moveIndex);
- data = GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL);
+ r4 = CalculatePPWithBonus(GetMonData(mon, MON_DATA_MOVE1 + moveIndex, NULL), GetMonData(mon, MON_DATA_PP_BONUSES, NULL), moveIndex);
+ data = GetMonData(mon, MON_DATA_PP_BONUSES, NULL);
data &= gPPUpSetMask[moveIndex];
data += gPPUpAddMask[moveIndex] * 3;
- SetMonData(pkmn, MON_DATA_PP_BONUSES, &data);
- data = CalculatePPWithBonus(GetMonData(pkmn, MON_DATA_MOVE1 + moveIndex, NULL), data, moveIndex) - r4;
- data = GetMonData(pkmn, MON_DATA_PP1 + moveIndex, NULL) + data;
- SetMonData(pkmn, MON_DATA_PP1 + moveIndex, &data);
+ SetMonData(mon, MON_DATA_PP_BONUSES, &data);
+ data = CalculatePPWithBonus(GetMonData(mon, MON_DATA_MOVE1 + moveIndex, NULL), data, moveIndex) - r4;
+ data = GetMonData(mon, MON_DATA_PP1 + moveIndex, NULL) + data;
+ SetMonData(mon, MON_DATA_PP1 + moveIndex, &data);
retVal = FALSE;
}
break;
case 5:
- if (GetMonData(pkmn, MON_DATA_FRIENDSHIP, NULL) < 100 && retVal == 0 && sp2C == 0)
+ if (GetMonData(mon, MON_DATA_FRIENDSHIP, NULL) < 100 && retVal == 0 && sp2C == 0)
{
sp2C = itemEffect[sp24];
- friendship = GetMonData(pkmn, MON_DATA_FRIENDSHIP, NULL);
+ friendship = GetMonData(mon, MON_DATA_FRIENDSHIP, NULL);
if (sp2C > 0 && holdEffect == HOLD_EFFECT_HAPPINESS_UP)
friendship += 150 * sp2C / 100;
else
friendship += sp2C;
if (sp2C > 0)
{
- if (GetMonData(pkmn, MON_DATA_POKEBALL, NULL) == 11)
+ if (GetMonData(mon, MON_DATA_POKEBALL, NULL) == 11)
friendship++;
- if (GetMonData(pkmn, MON_DATA_MET_LOCATION, NULL) == GetCurrentRegionMapSectionId())
+ if (GetMonData(mon, MON_DATA_MET_LOCATION, NULL) == GetCurrentRegionMapSectionId())
friendship++;
}
if (friendship < 0)
friendship = 0;
if (friendship > 255)
friendship = 255;
- SetMonData(pkmn, MON_DATA_FRIENDSHIP, &friendship);
+ SetMonData(mon, MON_DATA_FRIENDSHIP, &friendship);
}
sp24++;
break;
case 6:
- if (GetMonData(pkmn, MON_DATA_FRIENDSHIP, NULL) >= 100 && GetMonData(pkmn, MON_DATA_FRIENDSHIP, NULL) < 200
+ if (GetMonData(mon, MON_DATA_FRIENDSHIP, NULL) >= 100 && GetMonData(mon, MON_DATA_FRIENDSHIP, NULL) < 200
&& retVal == 0 && sp2C == 0)
{
sp2C = itemEffect[sp24];
- friendship = GetMonData(pkmn, MON_DATA_FRIENDSHIP, NULL);
+ friendship = GetMonData(mon, MON_DATA_FRIENDSHIP, NULL);
if (sp2C > 0 && holdEffect == HOLD_EFFECT_HAPPINESS_UP)
friendship += 150 * sp2C / 100;
else
friendship += sp2C;
if (sp2C > 0)
{
- if (GetMonData(pkmn, MON_DATA_POKEBALL, NULL) == 11)
+ if (GetMonData(mon, MON_DATA_POKEBALL, NULL) == 11)
friendship++;
- if (GetMonData(pkmn, MON_DATA_MET_LOCATION, NULL) == GetCurrentRegionMapSectionId())
+ if (GetMonData(mon, MON_DATA_MET_LOCATION, NULL) == GetCurrentRegionMapSectionId())
friendship++;
}
if (friendship < 0)
friendship = 0;
if (friendship > 255)
friendship = 255;
- SetMonData(pkmn, MON_DATA_FRIENDSHIP, &friendship);
+ SetMonData(mon, MON_DATA_FRIENDSHIP, &friendship);
}
sp24++;
break;
case 7:
- if (GetMonData(pkmn, MON_DATA_FRIENDSHIP, NULL) >= 200 && retVal == 0 && sp2C == 0)
+ if (GetMonData(mon, MON_DATA_FRIENDSHIP, NULL) >= 200 && retVal == 0 && sp2C == 0)
{
sp2C = itemEffect[sp24];
- friendship = GetMonData(pkmn, MON_DATA_FRIENDSHIP, NULL);
+ friendship = GetMonData(mon, MON_DATA_FRIENDSHIP, NULL);
if (sp2C > 0 && holdEffect == HOLD_EFFECT_HAPPINESS_UP)
friendship += 150 * sp2C / 100;
else
friendship += sp2C;
if (sp2C > 0)
{
- if (GetMonData(pkmn, MON_DATA_POKEBALL, NULL) == 11)
+ if (GetMonData(mon, MON_DATA_POKEBALL, NULL) == 11)
friendship++;
- if (GetMonData(pkmn, MON_DATA_MET_LOCATION, NULL) == GetCurrentRegionMapSectionId())
+ if (GetMonData(mon, MON_DATA_MET_LOCATION, NULL) == GetCurrentRegionMapSectionId())
friendship++;
}
if (friendship < 0)
friendship = 0;
if (friendship > 255)
friendship = 255;
- SetMonData(pkmn, MON_DATA_FRIENDSHIP, &friendship);
+ SetMonData(mon, MON_DATA_FRIENDSHIP, &friendship);
}
sp24++;
break;
@@ -4405,73 +4404,59 @@ static bool8 HealStatusConditions(struct Pokemon *mon, u32 unused, u32 healMask,
}
}
-#ifdef NONMATCHING
-/*
- * This is nonmatching due to the compiler's insistence on avoiding the u8 cast
- * when loading gMain.inBattle. If it weren't for this absent cast, differing
- * the function would be a lot easier.
- */
-bool8 PokemonUseItemEffects2(struct Pokemon *pkmn, u16 item, u8 partyIndex, u8 moveIndex, u8 e)
-{ // BEGIN
+bool8 PokemonUseItemEffects2(struct Pokemon *mon, u16 item, u8 partyIndex, u8 moveIndex)
+{
u32 data;
+ s32 tmp;
s32 cmdIndex;
bool8 retVal = TRUE;
const u8 *itemEffect;
- u8 sp24 = 6;
- u32 sp28;
- s8 sp2C = 0;
- u8 sp34 = 4;
+ u8 r10 = 6;
+ u32 i;
+ s32 sp18 = 0;
+ u8 holdEffect;
+ u8 battlerId = 4;
u16 heldItem;
- u8 r10;
- s32 r4;
+ u8 curEffect;
+ u32 curMoveId;
- heldItem = GetMonData(pkmn, MON_DATA_HELD_ITEM, NULL);
+ heldItem = GetMonData(mon, MON_DATA_HELD_ITEM, NULL);
+ // you have to write as such, because otherwise gMain.inBattle will lose its u8 cast
if (heldItem == ITEM_ENIGMA_BERRY)
{
if (gMain.inBattle)
- /*holdEffect = */gEnigmaBerries[gBattlerInMenuId].holdEffect;
+ holdEffect = gEnigmaBerries[gBattlerInMenuId].holdEffect;
else
- /*holdEffect = */gSaveBlock1Ptr->enigmaBerry.holdEffect;
+ holdEffect = gSaveBlock1Ptr->enigmaBerry.holdEffect;
}
else
{
- /*holdEffect = */ItemId_GetHoldEffect(heldItem);
+ holdEffect = ItemId_GetHoldEffect(heldItem);
}
-
gPotentialItemEffectBattler = gBattlerInMenuId;
-
- // grr. the original asm also u8 masks after loading the bitmask, despite
- // the fact that is a useless operation. what's going on here? Something
- // dumb I bet like dead code.
if (gMain.inBattle)
{
gActiveBattler = gBattlerInMenuId;
- if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER)
- cmdIndex = 0;
- else
- cmdIndex = 1;
- while (cmdIndex < gBattlersCount)
+ for (cmdIndex = GetBattlerSide(gActiveBattler) != B_SIDE_PLAYER;
+ cmdIndex < gBattlersCount;
+ cmdIndex += 2)
{
- if (gBattlerPartyIndexes[cmdIndex] == partyIndex)
+ if (gBattlerPartyIndexes[cmdIndex] == partyIndex)
{
- sp34 = cmdIndex;
+ battlerId = cmdIndex;
break;
}
- cmdIndex += 2;
}
}
else
{
gActiveBattler = 0;
- sp34 = 4;
+ battlerId = 4;
}
-
- // _08042504
if (!IS_POKEMON_ITEM(item))
return TRUE;
if (gItemEffectTable[item - 13] == NULL && item != ITEM_ENIGMA_BERRY)
return TRUE;
-
if (item == ITEM_ENIGMA_BERRY)
{
if (gMain.inBattle)
@@ -4483,1464 +4468,209 @@ bool8 PokemonUseItemEffects2(struct Pokemon *pkmn, u16 item, u8 partyIndex, u8 m
{
itemEffect = gItemEffectTable[item - 13];
}
-
for (cmdIndex = 0; cmdIndex < 6; cmdIndex++)
{
switch (cmdIndex)
{
// status healing effects
case 0:
- if ((itemEffect[cmdIndex] & 0x80)
- && gMain.inBattle && sp34 != 4 && (gBattleMons[sp34].status2 & STATUS2_INFATUATION))
- {
- //gBattleMons[sp34].status2 &= ~STATUS2_INFATUATION;
+ if (itemEffect[cmdIndex] & 0x80
+ && gMain.inBattle
+ && battlerId != 4
+ && gBattleMons[battlerId].status2 & STATUS2_INFATUATION)
retVal = FALSE;
- }
- if ((itemEffect[cmdIndex] & 0x30)
+ if (itemEffect[cmdIndex] & 0x30
&& !(gBattleMons[gActiveBattler].status2 & STATUS2_FOCUS_ENERGY))
- {
- //gBattleMons[gActiveBattler].status2 |= STATUS2_FOCUS_ENERGY;
retVal = FALSE;
- }
if ((itemEffect[cmdIndex] & 0xF)
&& gBattleMons[gActiveBattler].statStages[STAT_STAGE_ATK] < 12)
- {
- //gBattleMons[gActiveBattler].statStages[STAT_STAGE_ATK] += itemEffect[cmdIndex] & 0xF;
- //if (gBattleMons[gActiveBattler].statStages[STAT_STAGE_ATK] > 12)
- // gBattleMons[gActiveBattler].statStages[STAT_STAGE_ATK] = 12;
retVal = FALSE;
- }
break;
// in-battle stat boosting effects?
case 1:
if ((itemEffect[cmdIndex] & 0xF0)
&& gBattleMons[gActiveBattler].statStages[STAT_STAGE_DEF] < 12)
- {
- //gBattleMons[gActiveBattler].statStages[STAT_STAGE_DEF] += (itemEffect[cmdIndex] & 0xF0) >> 4;
- //if (gBattleMons[gActiveBattler].statStages[STAT_STAGE_DEF] > 12)
- // gBattleMons[gActiveBattler].statStages[STAT_STAGE_DEF] = 12;
retVal = FALSE;
- }
if ((itemEffect[cmdIndex] & 0xF)
&& gBattleMons[gActiveBattler].statStages[STAT_STAGE_SPEED] < 12)
- {
- //gBattleMons[gActiveBattler].statStages[STAT_STAGE_SPEED] += itemEffect[cmdIndex] & 0xF;
- //if (gBattleMons[gActiveBattler].statStages[STAT_STAGE_SPEED] > 12)
- // gBattleMons[gActiveBattler].statStages[STAT_STAGE_SPEED] = 12;
retVal = FALSE;
- }
break;
// more stat boosting effects?
case 2:
if ((itemEffect[cmdIndex] & 0xF0)
&& gBattleMons[gActiveBattler].statStages[STAT_STAGE_ACC] < 12)
- {
- //gBattleMons[gActiveBattler].statStages[STAT_STAGE_ACC] += (itemEffect[cmdIndex] & 0xF0) >> 4;
- //if (gBattleMons[gActiveBattler].statStages[STAT_STAGE_ACC] > 12)
- // gBattleMons[gActiveBattler].statStages[STAT_STAGE_ACC] = 12;
retVal = FALSE;
- }
if ((itemEffect[cmdIndex] & 0xF)
&& gBattleMons[gActiveBattler].statStages[STAT_STAGE_SPATK] < 12)
- {
- //gBattleMons[gActiveBattler].statStages[STAT_STAGE_SPATK] += itemEffect[cmdIndex] & 0xF;
- //if (gBattleMons[gActiveBattler].statStages[STAT_STAGE_SPATK] > 12)
- // gBattleMons[gActiveBattler].statStages[STAT_STAGE_SPATK] = 12;
retVal = FALSE;
- }
break;
case 3:
if ((itemEffect[cmdIndex] & 0x80)
&& gSideTimers[GetBattlerSide(gActiveBattler)].mistTimer == 0)
- {
- //gSideTimers[GetBattlerSide(gActiveBattler)].mistTimer = 5;
retVal = FALSE;
- }
if ((itemEffect[cmdIndex] & 0x40) // raise level
- && GetMonData(pkmn, MON_DATA_LEVEL, NULL) != 100)
- {
- //data = gExperienceTables[gBaseStats[GetMonData(pkmn, MON_DATA_SPECIES, NULL)].growthRate][GetMonData(pkmn, MON_DATA_LEVEL, NULL) + 1];
- //SetMonData(pkmn, MON_DATA_EXP, &data);
- //CalculateMonStats(pkmn);
+ && GetMonData(mon, MON_DATA_LEVEL, NULL) != 100)
retVal = FALSE;
- }
if ((itemEffect[cmdIndex] & 0x20)
- && sub_8042BE8(pkmn, partyIndex, 7, sp34) == 0)
- {
- //if (sp34 != 4)
- // gBattleMons[sp34].status2 &= ~STATUS2_NIGHTMARE;
+ && sub_8042BE8(mon, partyIndex, 7, battlerId))
retVal = FALSE;
- }
- if ((itemEffect[cmdIndex] & 0x10) && sub_8042BE8(pkmn, partyIndex, 0xF88, sp34) == 0)
+ if ((itemEffect[cmdIndex] & 0x10) && sub_8042BE8(mon, partyIndex, 0xF88, battlerId))
retVal = FALSE;
- if ((itemEffect[cmdIndex] & 8) && sub_8042BE8(pkmn, partyIndex, 16, sp34) == 0)
+ if ((itemEffect[cmdIndex] & 8) && sub_8042BE8(mon, partyIndex, 16, battlerId))
retVal = FALSE;
- if ((itemEffect[cmdIndex] & 4) && sub_8042BE8(pkmn, partyIndex, 32, sp34) == 0)
+ if ((itemEffect[cmdIndex] & 4) && sub_8042BE8(mon, partyIndex, 32, battlerId))
retVal = FALSE;
- if ((itemEffect[cmdIndex] & 2) && sub_8042BE8(pkmn, partyIndex, 64, sp34) == 0)
+ if ((itemEffect[cmdIndex] & 2) && sub_8042BE8(mon, partyIndex, 64, battlerId))
retVal = FALSE;
- if ((itemEffect[cmdIndex] & 1) // heal confusion
- && gMain.inBattle && sp34 != 4 && (gBattleMons[sp34].status2 & STATUS2_CONFUSION))
- {
- //gBattleMons[sp34].status2 &= ~STATUS2_CONFUSION;
+ if (itemEffect[cmdIndex] & 1 // heal confusion
+ && gMain.inBattle && battlerId != 4 && (gBattleMons[battlerId].status2 & STATUS2_CONFUSION))
retVal = FALSE;
- }
break;
// EV, HP, and PP raising effects
case 4:
- r10 = itemEffect[cmdIndex];
- if (r10 & 0x20)
+ curEffect = itemEffect[cmdIndex];
+ if (curEffect & 0x20)
{
- r10 &= ~0x20;
- data = (GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL) & gPPUpGetMask[moveIndex]) >> (moveIndex * 2);
- sp28 = CalculatePPWithBonus(GetMonData(pkmn, MON_DATA_MOVE1 + moveIndex, NULL), GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL), moveIndex);
- if (data < 3 && sp28 > 4)
- {
- //data = GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL) + gPPUpAddMask[moveIndex];
- //SetMonData(pkmn, MON_DATA_PP_BONUSES, &data);
- //
- //data = CalculatePPWithBonus(GetMonData(pkmn, MON_DATA_MOVE1 + moveIndex, NULL), data, moveIndex) - sp28;
- //data = GetMonData(pkmn, MON_DATA_PP1 + moveIndex, NULL) + data;
- //SetMonData(pkmn, MON_DATA_PP1 + moveIndex, &data);
+ curEffect &= ~0x20;
+ data = (GetMonData(mon, MON_DATA_PP_BONUSES, NULL) & gPPUpGetMask[moveIndex]) >> (moveIndex * 2);
+ i = CalculatePPWithBonus(GetMonData(mon, MON_DATA_MOVE1 + moveIndex, NULL), GetMonData(mon, MON_DATA_PP_BONUSES, NULL), moveIndex);
+ if (data < 3 && i > 4)
retVal = FALSE;
- }
}
- sp28 = 0;
- while (r10 != 0) // _080428C0
+ i = 0;
+ while (curEffect) // _080428C0
{
- if (r10 & 1)
+ if (curEffect & 1)
{
- u16 evCount;
- u16 targetSpecies;
- s32 r5;
-
- switch (sp28)
+ switch (i)
{
case 0:
case 1:
- evCount = GetMonEVCount(pkmn);
- if (evCount >= 510)
+ if (GetMonEVCount(mon) >= 510)
return TRUE;
- data = GetMonData(pkmn, sGetMonDataEVConstants[sp28], NULL);
+ data = GetMonData(mon, sGetMonDataEVConstants[i], NULL);
if (data < 100)
{
- //if (data + itemEffect[sp24] > 100)
- // r4 = 100 - (data + itemEffect[sp24]) + itemEffect[sp24];
- //else
- // r4 = itemEffect[sp24];
- //if (evCount + r4 > 510)
- // r4 += 510 - (evCount + r4);
- //data += r4;
- //SetMonData(pkmn, sGetMonDataEVConstants[sp28], &data);
- //CalculateMonStats(pkmn);
- sp24++;
+ r10++;
retVal = FALSE;
}
break;
case 2:
// revive?
- if (r10 & 0x10)
+ if (curEffect & 0x10)
{
- if (GetMonData(pkmn, MON_DATA_HP, NULL) != 0)
+ if (GetMonData(mon, MON_DATA_HP, NULL) != 0)
{
- sp24++;
+ r10++;
break;
}
- /*
- if (gMain.inBattle)
- {
- if (sp34 != 4)
- {
- gAbsentBattlerFlags &= ~gBitTable[sp34];
- CopyPlayerPartyMonToBattleData(sp34, pokemon_order_func(gBattlerPartyIndexes[sp34]));
- if (GetBattlerSide(gActiveBattler) == 0 && gBattleResults.unk4 < 255)
- gBattleResults.unk4++;
- }
- else
- {
- gAbsentBattlerFlags &= ~gBitTable[gActiveBattler ^ 2];
- if (GetBattlerSide(gActiveBattler) == 0 && gBattleResults.unk4 < 255)
- gBattleResults.unk4++;
- }
- }
- */
}
else
{
- if (GetMonData(pkmn, MON_DATA_HP, NULL) == 0)
+ if (GetMonData(mon, MON_DATA_HP, NULL) == 0)
{
- sp24++;
+ r10++;
break;
}
}
- /*
- data = itemEffect[sp24++];
- switch (data)
- {
- case 0xFF:
- data = GetMonData(pkmn, MON_DATA_MAX_HP, NULL) - GetMonData(pkmn, MON_DATA_HP, NULL);
- break;
- case 0xFE:
- data = GetMonData(pkmn, MON_DATA_MAX_HP, NULL) / 2;
- if (data == 0)
- data = 1;
- break;
- case 0xFD:
- data = gBattleScripting.field_23;
- break;
- }
- */
- if (GetMonData(pkmn, MON_DATA_MAX_HP, NULL) != GetMonData(pkmn, MON_DATA_HP, NULL))
- {
- /*
- if (e == 0)
- {
- data = GetMonData(pkmn, MON_DATA_HP, NULL) + data;
- if (data > GetMonData(pkmn, MON_DATA_MAX_HP, NULL))
- data = GetMonData(pkmn, MON_DATA_MAX_HP, NULL);
- SetMonData(pkmn, MON_DATA_HP, &data);
- if (gMain.inBattle && sp34 != 4)
- {
- gBattleMons[sp34].hp = data;
- if (!(r10 & 0x10) && GetBattlerSide(gActiveBattler) == 0)
- {
- if (gBattleResults.unk3 < 255)
- gBattleResults.unk3++;
- // I have to re-use this variable to match.
- r5 = gActiveBattler;
- gActiveBattler = sp34;
- BtlController_EmitGetMonData(0, 0, 0);
- MarkBufferBankForExecution(gActiveBattler);
- gActiveBattler = r5;
- }
- }
- }
- else
- {
- gBattleMoveDamage = -data;
- }
- */
+ if (GetMonData(mon, MON_DATA_MAX_HP, NULL) != GetMonData(mon, MON_DATA_HP, NULL))
retVal = FALSE;
- }
- sp24++;
- r10 &= 0xEF;
+ r10++;
+ curEffect &= 0xEF;
break;
case 3:
- if (!(r10 & 2))
+ if (!(curEffect & 2))
{
- for (r5 = 0; r5 < 4; r5++)
+ for (tmp = 0; tmp < MAX_MON_MOVES; tmp++)
{
- data = GetMonData(pkmn, MON_DATA_PP1 + r5, NULL);
- r4 = GetMonData(pkmn, MON_DATA_MOVE1 + r5, NULL);
- if (data != CalculatePPWithBonus(r4, GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL), r5))
- {
- /*
- data += itemEffect[sp24];
- r4 = GetMonData(pkmn, MON_DATA_MOVE1 + r5, NULL);
- if (data > CalculatePPWithBonus(r4, GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL), r5))
- {
- r4 = GetMonData(pkmn, MON_DATA_MOVE1 + r5, NULL);
- data = CalculatePPWithBonus(r4, GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL), r5);
- }
- SetMonData(pkmn, MON_DATA_PP1 + r5, &data);
- if (gMain.inBattle
- && sp34 != 4 && !(gBattleMons[sp34].status2 & 0x200000)
- && !(gDisableStructs[sp34].unk18_b & gBitTable[r5]))
- gBattleMons[sp34].pp[r5] = data;
- */
+ data = GetMonData(mon, MON_DATA_PP1 + tmp, NULL);
+ if (data != CalculatePPWithBonus(GetMonData(mon, MON_DATA_MOVE1 + tmp, NULL), GetMonData(mon, MON_DATA_PP_BONUSES, NULL), tmp))
retVal = FALSE;
- }
}
+ r10++;
}
else // _080429FA
{
- data = GetMonData(pkmn, MON_DATA_PP1 + moveIndex, NULL);
- r4 = GetMonData(pkmn, MON_DATA_MOVE1 + moveIndex, NULL);
- if (data != CalculatePPWithBonus(r4, GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL), moveIndex))
+ data = GetMonData(mon, MON_DATA_PP1 + moveIndex, NULL);
+ curMoveId = GetMonData(mon, MON_DATA_MOVE1 + moveIndex, NULL);
+ if (data != CalculatePPWithBonus(curMoveId, GetMonData(mon, MON_DATA_PP_BONUSES, NULL), moveIndex))
{
- /*
- data += itemEffect[sp24++];
- r4 = GetMonData(pkmn, MON_DATA_MOVE1 + moveIndex, NULL);
- if (data > CalculatePPWithBonus(r4, GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL), moveIndex))
- {
- r4 = GetMonData(pkmn, MON_DATA_MOVE1 + moveIndex, NULL);
- data = CalculatePPWithBonus(r4, GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL), moveIndex);
- }
- SetMonData(pkmn, MON_DATA_PP1 + moveIndex, &data);
- if (gMain.inBattle
- && sp34 != 4 && !(gBattleMons[sp34].status2 & 0x200000)
- && !(gDisableStructs[sp34].unk18_b & gBitTable[moveIndex]))
- gBattleMons[sp34].pp[moveIndex] = data;
- */
- sp24++;
+ r10++;
retVal = FALSE;
}
}
break;
case 7:
- {
- targetSpecies = GetEvolutionTargetSpecies(pkmn, 2, item);
-
- if (targetSpecies != SPECIES_NONE)
- {
- //BeginEvolutionScene(pkmn, targetSpecies, 0, partyIndex);
- return FALSE;
- }
- }
+ if (GetEvolutionTargetSpecies(mon, 2, item) != SPECIES_NONE)
+ return FALSE;
break;
}
}
- sp28++;
- r10 >>= 1;
+ i++;
+ curEffect >>= 1;
}
break;
case 5:
- r10 = itemEffect[cmdIndex];
- sp28 = 0;
- while (r10 != 0)
+ curEffect = itemEffect[cmdIndex];
+ i = 0;
+ while (curEffect)
{
- if (r10 & 1)
+ if (curEffect & 1)
{
- u16 evCount;
-
- switch (sp28)
+ switch (i)
{
case 0:
case 1:
case 2:
case 3:
- evCount = GetMonEVCount(pkmn);
- if (evCount >= 510)
+ if (GetMonEVCount(mon) >= 510)
return TRUE;
- data = GetMonData(pkmn, sGetMonDataEVConstants[sp28 + 2], NULL);
+ data = GetMonData(mon, sGetMonDataEVConstants[i + 2], NULL);
if (data < 100)
{
- /*
- if (data + itemEffect[sp24] > 100)
- r4 = 100 - (data + itemEffect[sp24]) + itemEffect[sp24];
- else
- r4 = itemEffect[sp24];
- if (evCount + r4 > 510)
- r4 += 510 - (evCount + r4);
- data += r4;
- SetMonData(pkmn, sGetMonDataEVConstants[sp28 + 2], &data);
- CalculateMonStats(pkmn);
- */
retVal = FALSE;
- sp24++;
+ r10++;
}
break;
case 4:
- data = (GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL) & gPPUpGetMask[moveIndex]) >> (moveIndex * 2);
- r4 = CalculatePPWithBonus(GetMonData(pkmn, MON_DATA_MOVE1 + moveIndex, NULL), GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL), moveIndex);
- if (data < 3)
- {
- if (r4 <= 4)
- break;
- /*
-
- data = GetMonData(pkmn, MON_DATA_PP_BONUSES, NULL);
- data &= gPPUpSetMask[moveIndex];
- data += gPPUpAddMask[moveIndex] * 3;
-
- SetMonData(pkmn, MON_DATA_PP_BONUSES, &data);
- data = CalculatePPWithBonus(GetMonData(pkmn, MON_DATA_MOVE1 + moveIndex, NULL), data, moveIndex) - r4;
- data = GetMonData(pkmn, MON_DATA_PP1 + moveIndex, NULL) + data;
- SetMonData(pkmn, MON_DATA_PP1 + moveIndex, &data);
- */
+ data = (GetMonData(mon, MON_DATA_PP_BONUSES, NULL) & gPPUpGetMask[moveIndex]) >> (moveIndex * 2);
+ tmp = CalculatePPWithBonus(GetMonData(mon, MON_DATA_MOVE1 + moveIndex, NULL), GetMonData(mon, MON_DATA_PP_BONUSES, NULL), moveIndex);
+ if (data < 3 && tmp > 4)
retVal = FALSE;
- }
break;
case 5:
- if (GetMonData(pkmn, MON_DATA_FRIENDSHIP, NULL) < 100 && retVal == 0 && sp2C == 0)
- {
- sp2C = itemEffect[sp24];
- /*
- friendship = GetMonData(pkmn, MON_DATA_FRIENDSHIP, NULL);
- if (sp2C > 0 && holdEffect == HOLD_EFFECT_HAPPINESS_UP)
- friendship += 150 * sp2C / 100;
- else
- friendship += sp2C;
- if (sp2C > 0)
- {
- if (GetMonData(pkmn, MON_DATA_POKEBALL, NULL) == 11)
- friendship++;
- if (GetMonData(pkmn, MON_DATA_MET_LOCATION, NULL) == GetCurrentRegionMapSectionId())
- friendship++;
- }
- if (friendship < 0)
- friendship = 0;
- if (friendship > 255)
- friendship = 255;
- SetMonData(pkmn, MON_DATA_FRIENDSHIP, &friendship);
- */
- }
- sp24++;
+ if (GetMonData(mon, MON_DATA_FRIENDSHIP, NULL) < 100
+ && retVal == FALSE
+ && sp18 == 0)
+ sp18 = itemEffect[r10];
+ r10++;
break;
case 6:
- if (GetMonData(pkmn, MON_DATA_FRIENDSHIP, NULL) >= 100 && GetMonData(pkmn, MON_DATA_FRIENDSHIP, NULL) < 200
- && retVal == 0 && sp2C == 0)
- {
- sp2C = itemEffect[sp24];
- /*
- friendship = GetMonData(pkmn, MON_DATA_FRIENDSHIP, NULL);
- if (sp2C > 0 && holdEffect == HOLD_EFFECT_HAPPINESS_UP)
- friendship += 150 * sp2C / 100;
- else
- friendship += sp2C;
- if (sp2C > 0)
- {
- if (GetMonData(pkmn, MON_DATA_POKEBALL, NULL) == 11)
- friendship++;
- if (GetMonData(pkmn, MON_DATA_MET_LOCATION, NULL) == GetCurrentRegionMapSectionId())
- friendship++;
- }
- if (friendship < 0)
- friendship = 0;
- if (friendship > 255)
- friendship = 255;
- SetMonData(pkmn, MON_DATA_FRIENDSHIP, &friendship);
- */
- }
- sp24++;
+ if (GetMonData(mon, MON_DATA_FRIENDSHIP, NULL) >= 100
+ && GetMonData(mon, MON_DATA_FRIENDSHIP, NULL) < 200
+ && retVal == FALSE
+ && sp18 == 0)
+ sp18 = itemEffect[r10];
+ r10++;
break;
- case 7:
- if (GetMonData(pkmn, MON_DATA_FRIENDSHIP, NULL) >= 200 && retVal == 0 && sp2C == 0)
- {
- sp2C = itemEffect[sp24];
- /*
- friendship = GetMonData(pkmn, MON_DATA_FRIENDSHIP, NULL);
- if (sp2C > 0 && holdEffect == HOLD_EFFECT_HAPPINESS_UP)
- friendship += 150 * sp2C / 100;
- else
- friendship += sp2C;
- if (sp2C > 0)
- {
- if (GetMonData(pkmn, MON_DATA_POKEBALL, NULL) == 11)
- friendship++;
- if (GetMonData(pkmn, MON_DATA_MET_LOCATION, NULL) == GetCurrentRegionMapSectionId())
- friendship++;
- }
- if (friendship < 0)
- friendship = 0;
- if (friendship > 255)
- friendship = 255;
- SetMonData(pkmn, MON_DATA_FRIENDSHIP, &friendship);
- */
- }
- sp24++;
+ case 7:\
+
+ if (GetMonData(mon, MON_DATA_FRIENDSHIP, NULL) >= 200
+ && retVal == FALSE
+ && sp18 == 0)
+ sp18 = itemEffect[r10];
+ r10++;
break;
}
}
- sp28++;
- r10 >>= 1;
+ i++;
+ curEffect >>= 1;
}
break;
}
}
return retVal;
}
-#else
-__attribute__((naked))
-bool8 PokemonUseItemEffects2(struct Pokemon *pkmn, u16 item, u8 partyIndex, u8 moveIndex, u8 e)
-{
- asm(".syntax unified\n\
- push {r4-r7,lr}\n\
- mov r7, r10\n\
- mov r6, r9\n\
- mov r5, r8\n\
- push {r5-r7}\n\
- sub sp, 0x20\n\
- mov r8, r0\n\
- lsls r1, 16\n\
- lsrs r1, 16\n\
- str r1, [sp]\n\
- lsls r2, 24\n\
- lsrs r2, 24\n\
- str r2, [sp, 0x4]\n\
- lsls r3, 24\n\
- lsrs r3, 24\n\
- str r3, [sp, 0x8]\n\
- movs r0, 0x1\n\
- str r0, [sp, 0x10]\n\
- movs r1, 0x6\n\
- mov r10, r1\n\
- movs r2, 0\n\
- str r2, [sp, 0x18]\n\
- movs r0, 0x4\n\
- str r0, [sp, 0x1C]\n\
- mov r0, r8\n\
- movs r1, 0xC\n\
- bl GetMonData\n\
- lsls r0, 16\n\
- lsrs r0, 16\n\
- cmp r0, 0xAF\n\
- beq _08042458\n\
- bl ItemId_GetHoldEffect\n\
-_08042458:\n\
- ldr r1, _080424B0 @ =gPotentialItemEffectBattler\n\
- ldr r0, _080424B4 @ =gBattlerInMenuId\n\
- ldrb r2, [r0]\n\
- strb r2, [r1]\n\
- ldr r0, _080424B8 @ =gMain\n\
- ldr r1, _080424BC @ =0x00000439\n\
- adds r0, r1\n\
- ldrb r1, [r0]\n\
- movs r0, 0x2\n\
- ands r0, r1\n\
- lsls r0, 24\n\
- lsrs r1, r0, 24\n\
- cmp r1, 0\n\
- beq _080424F8\n\
- ldr r0, _080424C0 @ =gActiveBattler\n\
- strb r2, [r0]\n\
- ldrb r0, [r0]\n\
- bl GetBattlerSide\n\
- lsls r0, 24\n\
- lsrs r0, 24\n\
- negs r1, r0\n\
- orrs r1, r0\n\
- lsrs r1, 31\n\
- str r1, [sp, 0xC]\n\
- ldr r0, _080424C4 @ =gBattlersCount\n\
- ldr r4, [sp]\n\
- subs r4, 0xD\n\
- ldrb r0, [r0]\n\
- cmp r1, r0\n\
- bge _08042504\n\
- ldr r2, _080424C8 @ =gBattlerPartyIndexes\n\
- lsls r0, r1, 1\n\
- adds r0, r2\n\
- ldrh r3, [r0]\n\
- ldr r1, [sp, 0x4]\n\
- lsls r0, r1, 16\n\
- lsrs r1, r0, 16\n\
- adds r5, r0, 0\n\
- cmp r3, r1\n\
- bne _080424CC\n\
- ldr r2, [sp, 0xC]\n\
- str r2, [sp, 0x1C]\n\
- b _08042504\n\
- .align 2, 0\n\
-_080424B0: .4byte gPotentialItemEffectBattler\n\
-_080424B4: .4byte gBattlerInMenuId\n\
-_080424B8: .4byte gMain\n\
-_080424BC: .4byte 0x00000439\n\
-_080424C0: .4byte gActiveBattler\n\
-_080424C4: .4byte gBattlersCount\n\
-_080424C8: .4byte gBattlerPartyIndexes\n\
-_080424CC:\n\
- ldr r0, [sp, 0xC]\n\
- adds r0, 0x2\n\
- str r0, [sp, 0xC]\n\
- ldr r0, _080424F4 @ =gBattlersCount\n\
- ldr r1, [sp, 0xC]\n\
- ldrb r0, [r0]\n\
- cmp r1, r0\n\
- bge _08042504\n\
- lsls r0, r1, 1\n\
- adds r0, r2\n\
- ldrh r1, [r0]\n\
- lsrs r0, r5, 16\n\
- cmp r1, r0\n\
- bne _080424CC\n\
- ldr r2, [sp, 0xC]\n\
- lsls r0, r2, 24\n\
- lsrs r0, 24\n\
- str r0, [sp, 0x1C]\n\
- b _08042504\n\
- .align 2, 0\n\
-_080424F4: .4byte gBattlersCount\n\
-_080424F8:\n\
- ldr r0, _08042520 @ =gActiveBattler\n\
- strb r1, [r0]\n\
- movs r0, 0x4\n\
- str r0, [sp, 0x1C]\n\
- ldr r4, [sp]\n\
- subs r4, 0xD\n\
-_08042504:\n\
- lsls r0, r4, 16\n\
- lsrs r0, 16\n\
- cmp r0, 0xA5\n\
- bhi _08042578\n\
- ldr r1, _08042524 @ =gItemEffectTable\n\
- lsls r0, r4, 2\n\
- adds r0, r1\n\
- ldr r0, [r0]\n\
- cmp r0, 0\n\
- bne _08042528\n\
- ldr r1, [sp]\n\
- cmp r1, 0xAF\n\
- beq _0804252E\n\
- b _08042578\n\
- .align 2, 0\n\
-_08042520: .4byte gActiveBattler\n\
-_08042524: .4byte gItemEffectTable\n\
-_08042528:\n\
- ldr r2, [sp]\n\
- cmp r2, 0xAF\n\
- bne _0804257C\n\
-_0804252E:\n\
- ldr r0, _08042550 @ =gMain\n\
- ldr r1, _08042554 @ =0x00000439\n\
- adds r0, r1\n\
- ldrb r1, [r0]\n\
- movs r0, 0x2\n\
- ands r0, r1\n\
- cmp r0, 0\n\
- beq _08042560\n\
- ldr r0, _08042558 @ =gActiveBattler\n\
- ldrb r1, [r0]\n\
- lsls r0, r1, 3\n\
- subs r0, r1\n\
- lsls r0, 2\n\
- ldr r1, _0804255C @ =gEnigmaBerries+0x8\n\
- adds r0, r1\n\
- b _0804257C\n\
- .align 2, 0\n\
-_08042550: .4byte gMain\n\
-_08042554: .4byte 0x00000439\n\
-_08042558: .4byte gActiveBattler\n\
-_0804255C: .4byte gEnigmaBerries+0x8\n\
-_08042560:\n\
- ldr r0, _0804256C @ =gSaveBlock1Ptr\n\
- ldr r0, [r0]\n\
- ldr r2, _08042570 @ =0x00003108\n\
- adds r2, r0, r2\n\
- str r2, [sp, 0x14]\n\
- b _0804257E\n\
- .align 2, 0\n\
-_0804256C: .4byte gSaveBlock1Ptr\n\
-_08042570: .4byte 0x00003108\n\
-_08042574:\n\
- movs r0, 0\n\
- b _08042BD8\n\
-_08042578:\n\
- movs r0, 0x1\n\
- b _08042BD8\n\
-_0804257C:\n\
- str r0, [sp, 0x14]\n\
-_0804257E:\n\
- movs r0, 0\n\
- str r0, [sp, 0xC]\n\
-_08042582:\n\
- ldr r1, [sp, 0xC]\n\
- cmp r1, 0x5\n\
- bls _0804258A\n\
- b _08042BCA\n\
-_0804258A:\n\
- lsls r0, r1, 2\n\
- ldr r1, _08042594 @ =_08042598\n\
- adds r0, r1\n\
- ldr r0, [r0]\n\
- mov pc, r0\n\
- .align 2, 0\n\
-_08042594: .4byte _08042598\n\
- .align 2, 0\n\
-_08042598:\n\
- .4byte _080425B0\n\
- .4byte _0804264C\n\
- .4byte _080426A8\n\
- .4byte _08042708\n\
- .4byte _08042850\n\
- .4byte _08042A6A\n\
-_080425B0:\n\
- ldr r0, [sp, 0x14]\n\
- ldr r1, [sp, 0xC]\n\
- adds r2, r0, r1\n\
- ldrb r1, [r2]\n\
- movs r0, 0x80\n\
- ands r0, r1\n\
- adds r5, r2, 0\n\
- cmp r0, 0\n\
- beq _080425F4\n\
- ldr r0, _0804263C @ =gMain\n\
- ldr r2, _08042640 @ =0x00000439\n\
- adds r0, r2\n\
- ldrb r1, [r0]\n\
- movs r0, 0x2\n\
- ands r0, r1\n\
- cmp r0, 0\n\
- beq _080425F4\n\
- ldr r0, [sp, 0x1C]\n\
- cmp r0, 0x4\n\
- beq _080425F4\n\
- ldr r1, _08042644 @ =gBattleMons\n\
- movs r0, 0x58\n\
- ldr r2, [sp, 0x1C]\n\
- muls r0, r2\n\
- adds r1, 0x50\n\
- adds r0, r1\n\
- ldr r0, [r0]\n\
- movs r1, 0xF0\n\
- lsls r1, 12\n\
- ands r0, r1\n\
- cmp r0, 0\n\
- beq _080425F4\n\
- movs r0, 0\n\
- str r0, [sp, 0x10]\n\
-_080425F4:\n\
- ldrb r1, [r5]\n\
- movs r0, 0x30\n\
- ands r0, r1\n\
- cmp r0, 0\n\
- beq _0804261C\n\
- ldr r1, _08042644 @ =gBattleMons\n\
- ldr r0, _08042648 @ =gActiveBattler\n\
- ldrb r2, [r0]\n\
- movs r0, 0x58\n\
- muls r0, r2\n\
- adds r1, 0x50\n\
- adds r0, r1\n\
- ldr r0, [r0]\n\
- movs r1, 0x80\n\
- lsls r1, 13\n\
- ands r0, r1\n\
- cmp r0, 0\n\
- bne _0804261C\n\
- movs r1, 0\n\
- str r1, [sp, 0x10]\n\
-_0804261C:\n\
- ldrb r1, [r5]\n\
- movs r0, 0xF\n\
- ands r0, r1\n\
- cmp r0, 0\n\
- bne _08042628\n\
- b _08042BCA\n\
-_08042628:\n\
- ldr r2, _08042644 @ =gBattleMons\n\
- ldr r0, _08042648 @ =gActiveBattler\n\
- ldrb r1, [r0]\n\
- movs r0, 0x58\n\
- muls r0, r1\n\
- adds r0, r2\n\
- ldrb r0, [r0, 0x19]\n\
- lsls r0, 24\n\
- asrs r0, 24\n\
- b _080426F2\n\
- .align 2, 0\n\
-_0804263C: .4byte gMain\n\
-_08042640: .4byte 0x00000439\n\
-_08042644: .4byte gBattleMons\n\
-_08042648: .4byte gActiveBattler\n\
-_0804264C:\n\
- ldr r0, [sp, 0x14]\n\
- ldr r1, [sp, 0xC]\n\
- adds r2, r0, r1\n\
- ldrb r1, [r2]\n\
- movs r0, 0xF0\n\
- ands r0, r1\n\
- adds r5, r2, 0\n\
- cmp r0, 0\n\
- beq _08042678\n\
- ldr r2, _080426A0 @ =gBattleMons\n\
- ldr r0, _080426A4 @ =gActiveBattler\n\
- ldrb r1, [r0]\n\
- movs r0, 0x58\n\
- muls r0, r1\n\
- adds r0, r2\n\
- ldrb r0, [r0, 0x1A]\n\
- lsls r0, 24\n\
- asrs r0, 24\n\
- cmp r0, 0xB\n\
- bgt _08042678\n\
- movs r2, 0\n\
- str r2, [sp, 0x10]\n\
-_08042678:\n\
- ldrb r1, [r5]\n\
- movs r0, 0xF\n\
- ands r0, r1\n\
- cmp r0, 0\n\
- bne _08042684\n\
- b _08042BCA\n\
-_08042684:\n\
- ldr r2, _080426A0 @ =gBattleMons\n\
- ldr r0, _080426A4 @ =gActiveBattler\n\
- ldrb r1, [r0]\n\
- movs r0, 0x58\n\
- muls r0, r1\n\
- adds r0, r2\n\
- ldrb r0, [r0, 0x1B]\n\
- lsls r0, 24\n\
- asrs r0, 24\n\
- cmp r0, 0xB\n\
- ble _0804269C\n\
- b _08042BCA\n\
-_0804269C:\n\
- b _08042832\n\
- .align 2, 0\n\
-_080426A0: .4byte gBattleMons\n\
-_080426A4: .4byte gActiveBattler\n\
-_080426A8:\n\
- ldr r1, [sp, 0x14]\n\
- ldr r0, [sp, 0xC]\n\
- adds r2, r1, r0\n\
- ldrb r1, [r2]\n\
- movs r0, 0xF0\n\
- ands r0, r1\n\
- adds r5, r2, 0\n\
- cmp r0, 0\n\
- beq _080426D4\n\
- ldr r2, _08042700 @ =gBattleMons\n\
- ldr r0, _08042704 @ =gActiveBattler\n\
- ldrb r1, [r0]\n\
- movs r0, 0x58\n\
- muls r0, r1\n\
- adds r0, r2\n\
- ldrb r0, [r0, 0x1E]\n\
- lsls r0, 24\n\
- asrs r0, 24\n\
- cmp r0, 0xB\n\
- bgt _080426D4\n\
- movs r1, 0\n\
- str r1, [sp, 0x10]\n\
-_080426D4:\n\
- ldrb r1, [r5]\n\
- movs r0, 0xF\n\
- ands r0, r1\n\
- cmp r0, 0\n\
- bne _080426E0\n\
- b _08042BCA\n\
-_080426E0:\n\
- ldr r2, _08042700 @ =gBattleMons\n\
- ldr r0, _08042704 @ =gActiveBattler\n\
- ldrb r1, [r0]\n\
- movs r0, 0x58\n\
- muls r0, r1\n\
- adds r0, r2\n\
- ldrb r0, [r0, 0x1C]\n\
- lsls r0, 24\n\
- asrs r0, 24\n\
-_080426F2:\n\
- cmp r0, 0xB\n\
- ble _080426F8\n\
- b _08042BCA\n\
-_080426F8:\n\
- movs r2, 0\n\
- str r2, [sp, 0x10]\n\
- b _08042BCA\n\
- .align 2, 0\n\
-_08042700: .4byte gBattleMons\n\
-_08042704: .4byte gActiveBattler\n\
-_08042708:\n\
- ldr r0, [sp, 0x14]\n\
- ldr r1, [sp, 0xC]\n\
- adds r2, r0, r1\n\
- ldrb r1, [r2]\n\
- movs r0, 0x80\n\
- ands r0, r1\n\
- adds r5, r2, 0\n\
- cmp r0, 0\n\
- beq _0804273A\n\
- ldr r4, _08042838 @ =gSideTimers\n\
- ldr r0, _0804283C @ =gActiveBattler\n\
- ldrb r0, [r0]\n\
- bl GetBattlerSide\n\
- lsls r0, 24\n\
- lsrs r0, 24\n\
- lsls r1, r0, 1\n\
- adds r1, r0\n\
- lsls r1, 2\n\
- adds r1, r4\n\
- ldrb r0, [r1, 0x4]\n\
- cmp r0, 0\n\
- bne _0804273A\n\
- movs r2, 0\n\
- str r2, [sp, 0x10]\n\
-_0804273A:\n\
- ldrb r1, [r5]\n\
- movs r0, 0x40\n\
- ands r0, r1\n\
- cmp r0, 0\n\
- beq _08042756\n\
- mov r0, r8\n\
- movs r1, 0x38\n\
- movs r2, 0\n\
- bl GetMonData\n\
- cmp r0, 0x64\n\
- beq _08042756\n\
- movs r0, 0\n\
- str r0, [sp, 0x10]\n\
-_08042756:\n\
- ldrb r1, [r5]\n\
- movs r0, 0x20\n\
- ands r0, r1\n\
- cmp r0, 0\n\
- beq _08042776\n\
- mov r0, r8\n\
- ldr r1, [sp, 0x4]\n\
- movs r2, 0x7\n\
- ldr r3, [sp, 0x1C]\n\
- bl sub_8042BE8\n\
- lsls r0, 24\n\
- cmp r0, 0\n\
- beq _08042776\n\
- movs r1, 0\n\
- str r1, [sp, 0x10]\n\
-_08042776:\n\
- ldrb r1, [r5]\n\
- movs r0, 0x10\n\
- ands r0, r1\n\
- cmp r0, 0\n\
- beq _08042796\n\
- ldr r2, _08042840 @ =0x00000f88\n\
- mov r0, r8\n\
- ldr r1, [sp, 0x4]\n\
- ldr r3, [sp, 0x1C]\n\
- bl sub_8042BE8\n\
- lsls r0, 24\n\
- cmp r0, 0\n\
- beq _08042796\n\
- movs r2, 0\n\
- str r2, [sp, 0x10]\n\
-_08042796:\n\
- ldrb r1, [r5]\n\
- movs r0, 0x8\n\
- ands r0, r1\n\
- cmp r0, 0\n\
- beq _080427B6\n\
- mov r0, r8\n\
- ldr r1, [sp, 0x4]\n\
- movs r2, 0x10\n\
- ldr r3, [sp, 0x1C]\n\
- bl sub_8042BE8\n\
- lsls r0, 24\n\
- cmp r0, 0\n\
- beq _080427B6\n\
- movs r0, 0\n\
- str r0, [sp, 0x10]\n\
-_080427B6:\n\
- ldrb r1, [r5]\n\
- movs r0, 0x4\n\
- ands r0, r1\n\
- cmp r0, 0\n\
- beq _080427D6\n\
- mov r0, r8\n\
- ldr r1, [sp, 0x4]\n\
- movs r2, 0x20\n\
- ldr r3, [sp, 0x1C]\n\
- bl sub_8042BE8\n\
- lsls r0, 24\n\
- cmp r0, 0\n\
- beq _080427D6\n\
- movs r1, 0\n\
- str r1, [sp, 0x10]\n\
-_080427D6:\n\
- ldrb r1, [r5]\n\
- movs r0, 0x2\n\
- ands r0, r1\n\
- cmp r0, 0\n\
- beq _080427F6\n\
- mov r0, r8\n\
- ldr r1, [sp, 0x4]\n\
- movs r2, 0x40\n\
- ldr r3, [sp, 0x1C]\n\
- bl sub_8042BE8\n\
- lsls r0, 24\n\
- cmp r0, 0\n\
- beq _080427F6\n\
- movs r2, 0\n\
- str r2, [sp, 0x10]\n\
-_080427F6:\n\
- ldrb r1, [r5]\n\
- movs r0, 0x1\n\
- ands r0, r1\n\
- cmp r0, 0\n\
- bne _08042802\n\
- b _08042BCA\n\
-_08042802:\n\
- ldr r0, _08042844 @ =gMain\n\
- ldr r1, _08042848 @ =0x00000439\n\
- adds r0, r1\n\
- ldrb r1, [r0]\n\
- movs r0, 0x2\n\
- ands r0, r1\n\
- cmp r0, 0\n\
- bne _08042814\n\
- b _08042BCA\n\
-_08042814:\n\
- ldr r2, [sp, 0x1C]\n\
- cmp r2, 0x4\n\
- bne _0804281C\n\
- b _08042BCA\n\
-_0804281C:\n\
- ldr r1, _0804284C @ =gBattleMons\n\
- movs r0, 0x58\n\
- muls r0, r2\n\
- adds r1, 0x50\n\
- adds r0, r1\n\
- ldr r0, [r0]\n\
- movs r1, 0x7\n\
- ands r0, r1\n\
- cmp r0, 0\n\
- bne _08042832\n\
- b _08042BCA\n\
-_08042832:\n\
- movs r0, 0\n\
- str r0, [sp, 0x10]\n\
- b _08042BCA\n\
- .align 2, 0\n\
-_08042838: .4byte gSideTimers\n\
-_0804283C: .4byte gActiveBattler\n\
-_08042840: .4byte 0x00000f88\n\
-_08042844: .4byte gMain\n\
-_08042848: .4byte 0x00000439\n\
-_0804284C: .4byte gBattleMons\n\
-_08042850:\n\
- ldr r1, [sp, 0x14]\n\
- ldr r2, [sp, 0xC]\n\
- adds r0, r1, r2\n\
- ldrb r7, [r0]\n\
- movs r0, 0x20\n\
- ands r0, r7\n\
- cmp r0, 0\n\
- beq _080428B6\n\
- movs r0, 0xDF\n\
- ands r7, r0\n\
- mov r0, r8\n\
- movs r1, 0x15\n\
- movs r2, 0\n\
- bl GetMonData\n\
- adds r5, r0, 0\n\
- ldr r0, _080428DC @ =gPPUpGetMask\n\
- ldr r1, [sp, 0x8]\n\
- adds r0, r1, r0\n\
- ldrb r0, [r0]\n\
- ands r5, r0\n\
- lsls r0, r1, 1\n\
- lsrs r5, r0\n\
- adds r1, 0xD\n\
- mov r0, r8\n\
- movs r2, 0\n\
- bl GetMonData\n\
- adds r4, r0, 0\n\
- lsls r4, 16\n\
- lsrs r4, 16\n\
- mov r0, r8\n\
- movs r1, 0x15\n\
- movs r2, 0\n\
- bl GetMonData\n\
- adds r1, r0, 0\n\
- lsls r1, 24\n\
- lsrs r1, 24\n\
- adds r0, r4, 0\n\
- ldr r2, [sp, 0x8]\n\
- bl CalculatePPWithBonus\n\
- lsls r0, 24\n\
- lsrs r0, 24\n\
- cmp r5, 0x2\n\
- bhi _080428B6\n\
- cmp r0, 0x4\n\
- bls _080428B6\n\
- movs r2, 0\n\
- str r2, [sp, 0x10]\n\
-_080428B6:\n\
- movs r0, 0\n\
- mov r9, r0\n\
- cmp r7, 0\n\
- bne _080428C0\n\
- b _08042BCA\n\
-_080428C0:\n\
- movs r0, 0x1\n\
- ands r0, r7\n\
- cmp r0, 0\n\
- bne _080428CA\n\
- b _08042A5C\n\
-_080428CA:\n\
- mov r1, r9\n\
- cmp r1, 0x7\n\
- bls _080428D2\n\
- b _08042A5C\n\
-_080428D2:\n\
- lsls r0, r1, 2\n\
- ldr r1, _080428E0 @ =_080428E4\n\
- adds r0, r1\n\
- ldr r0, [r0]\n\
- mov pc, r0\n\
- .align 2, 0\n\
-_080428DC: .4byte gPPUpGetMask\n\
-_080428E0: .4byte _080428E4\n\
- .align 2, 0\n\
-_080428E4:\n\
- .4byte _08042904\n\
- .4byte _08042904\n\
- .4byte _08042934\n\
- .4byte _08042996\n\
- .4byte _08042A5C\n\
- .4byte _08042A5C\n\
- .4byte _08042A5C\n\
- .4byte _08042A4A\n\
-_08042904:\n\
- mov r0, r8\n\
- bl GetMonEVCount\n\
- lsls r0, 16\n\
- ldr r1, _0804292C @ =0x01fd0000\n\
- cmp r0, r1\n\
- bls _08042914\n\
- b _08042578\n\
-_08042914:\n\
- ldr r0, _08042930 @ =sGetMonDataEVConstants\n\
- add r0, r9\n\
- ldrb r1, [r0]\n\
- mov r0, r8\n\
- movs r2, 0\n\
- bl GetMonData\n\
- adds r5, r0, 0\n\
- cmp r5, 0x63\n\
- bls _0804292A\n\
- b _08042A5C\n\
-_0804292A:\n\
- b _08042A3A\n\
- .align 2, 0\n\
-_0804292C: .4byte 0x01fd0000\n\
-_08042930: .4byte sGetMonDataEVConstants\n\
-_08042934:\n\
- movs r0, 0x10\n\
- ands r0, r7\n\
- cmp r0, 0\n\
- beq _08042952\n\
- mov r0, r8\n\
- movs r1, 0x39\n\
- movs r2, 0\n\
- bl GetMonData\n\
- cmp r0, 0\n\
- beq _08042968\n\
- mov r0, r10\n\
- adds r0, 0x1\n\
- lsls r0, 24\n\
- b _080429F4\n\
-_08042952:\n\
- mov r0, r8\n\
- movs r1, 0x39\n\
- movs r2, 0\n\
- bl GetMonData\n\
- cmp r0, 0\n\
- bne _08042968\n\
- mov r0, r10\n\
- adds r0, 0x1\n\
- lsls r0, 24\n\
- b _080429F4\n\
-_08042968:\n\
- mov r0, r8\n\
- movs r1, 0x3A\n\
- movs r2, 0\n\
- bl GetMonData\n\
- adds r4, r0, 0\n\
- mov r0, r8\n\
- movs r1, 0x39\n\
- movs r2, 0\n\
- bl GetMonData\n\
- cmp r4, r0\n\
- beq _08042986\n\
- movs r0, 0\n\
- str r0, [sp, 0x10]\n\
-_08042986:\n\
- mov r0, r10\n\
- adds r0, 0x1\n\
- lsls r0, 24\n\
- lsrs r0, 24\n\
- mov r10, r0\n\
- movs r0, 0xEF\n\
- ands r7, r0\n\
- b _08042A5C\n\
-_08042996:\n\
- movs r0, 0x2\n\
- ands r0, r7\n\
- cmp r0, 0\n\
- bne _080429FA\n\
- movs r6, 0\n\
- movs r1, 0x1\n\
- add r10, r1\n\
-_080429A4:\n\
- adds r1, r6, 0\n\
- adds r1, 0x11\n\
- mov r0, r8\n\
- movs r2, 0\n\
- bl GetMonData\n\
- adds r5, r0, 0\n\
- adds r1, r6, 0\n\
- adds r1, 0xD\n\
- mov r0, r8\n\
- movs r2, 0\n\
- bl GetMonData\n\
- adds r4, r0, 0\n\
- lsls r4, 16\n\
- lsrs r4, 16\n\
- mov r0, r8\n\
- movs r1, 0x15\n\
- movs r2, 0\n\
- bl GetMonData\n\
- adds r1, r0, 0\n\
- lsls r1, 24\n\
- lsrs r1, 24\n\
- lsls r2, r6, 24\n\
- lsrs r2, 24\n\
- adds r0, r4, 0\n\
- bl CalculatePPWithBonus\n\
- lsls r0, 24\n\
- lsrs r0, 24\n\
- cmp r5, r0\n\
- beq _080429EA\n\
- movs r2, 0\n\
- str r2, [sp, 0x10]\n\
-_080429EA:\n\
- adds r6, 0x1\n\
- cmp r6, 0x3\n\
- ble _080429A4\n\
- mov r1, r10\n\
- lsls r0, r1, 24\n\
-_080429F4:\n\
- lsrs r0, 24\n\
- mov r10, r0\n\
- b _08042A5C\n\
-_080429FA:\n\
- ldr r1, [sp, 0x8]\n\
- adds r1, 0x11\n\
- mov r0, r8\n\
- movs r2, 0\n\
- bl GetMonData\n\
- adds r5, r0, 0\n\
- ldr r1, [sp, 0x8]\n\
- adds r1, 0xD\n\
- mov r0, r8\n\
- movs r2, 0\n\
- bl GetMonData\n\
- adds r4, r0, 0\n\
- lsls r4, 16\n\
- lsrs r4, 16\n\
- mov r0, r8\n\
- movs r1, 0x15\n\
- movs r2, 0\n\
- bl GetMonData\n\
- adds r1, r0, 0\n\
- lsls r1, 24\n\
- lsrs r1, 24\n\
- adds r0, r4, 0\n\
- ldr r2, [sp, 0x8]\n\
- bl CalculatePPWithBonus\n\
- lsls r0, 24\n\
- lsrs r0, 24\n\
- cmp r5, r0\n\
- beq _08042A5C\n\
-_08042A3A:\n\
- mov r0, r10\n\
- adds r0, 0x1\n\
- lsls r0, 24\n\
- lsrs r0, 24\n\
- mov r10, r0\n\
- movs r2, 0\n\
- str r2, [sp, 0x10]\n\
- b _08042A5C\n\
-_08042A4A:\n\
- mov r0, r8\n\
- movs r1, 0x2\n\
- ldr r2, [sp]\n\
- bl GetEvolutionTargetSpecies\n\
- lsls r0, 16\n\
- cmp r0, 0\n\
- beq _08042A5C\n\
- b _08042574\n\
-_08042A5C:\n\
- movs r0, 0x1\n\
- add r9, r0\n\
- lsrs r7, 1\n\
- cmp r7, 0\n\
- beq _08042A68\n\
- b _080428C0\n\
-_08042A68:\n\
- b _08042BCA\n\
-_08042A6A:\n\
- ldr r1, [sp, 0x14]\n\
- ldr r2, [sp, 0xC]\n\
- adds r0, r1, r2\n\
- ldrb r7, [r0]\n\
- movs r0, 0\n\
- mov r9, r0\n\
- cmp r7, 0\n\
- bne _08042A7C\n\
- b _08042BCA\n\
-_08042A7C:\n\
- movs r0, 0x1\n\
- ands r0, r7\n\
- cmp r0, 0\n\
- bne _08042A86\n\
- b _08042BBE\n\
-_08042A86:\n\
- mov r1, r9\n\
- cmp r1, 0x7\n\
- bls _08042A8E\n\
- b _08042BBE\n\
-_08042A8E:\n\
- lsls r0, r1, 2\n\
- ldr r1, _08042A98 @ =_08042A9C\n\
- adds r0, r1\n\
- ldr r0, [r0]\n\
- mov pc, r0\n\
- .align 2, 0\n\
-_08042A98: .4byte _08042A9C\n\
- .align 2, 0\n\
-_08042A9C:\n\
- .4byte _08042ABC\n\
- .4byte _08042ABC\n\
- .4byte _08042ABC\n\
- .4byte _08042ABC\n\
- .4byte _08042AF4\n\
- .4byte _08042B4C\n\
- .4byte _08042B68\n\
- .4byte _08042B92\n\
-_08042ABC:\n\
- mov r0, r8\n\
- bl GetMonEVCount\n\
- lsls r0, 16\n\
- ldr r1, _08042AEC @ =0x01fd0000\n\
- cmp r0, r1\n\
- bls _08042ACC\n\
- b _08042578\n\
-_08042ACC:\n\
- ldr r0, _08042AF0 @ =sGetMonDataEVConstants\n\
- mov r1, r9\n\
- adds r1, 0x2\n\
- adds r1, r0\n\
- ldrb r1, [r1]\n\
- mov r0, r8\n\
- movs r2, 0\n\
- bl GetMonData\n\
- adds r5, r0, 0\n\
- cmp r5, 0x63\n\
- bhi _08042BBE\n\
- movs r2, 0\n\
- str r2, [sp, 0x10]\n\
- b _08042BB4\n\
- .align 2, 0\n\
-_08042AEC: .4byte 0x01fd0000\n\
-_08042AF0: .4byte sGetMonDataEVConstants\n\
-_08042AF4:\n\
- mov r0, r8\n\
- movs r1, 0x15\n\
- movs r2, 0\n\
- bl GetMonData\n\
- adds r5, r0, 0\n\
- ldr r0, _08042B48 @ =gPPUpGetMask\n\
- ldr r1, [sp, 0x8]\n\
- adds r0, r1, r0\n\
- ldrb r0, [r0]\n\
- ands r5, r0\n\
- lsls r0, r1, 1\n\
- lsrs r5, r0\n\
- adds r1, 0xD\n\
- mov r0, r8\n\
- movs r2, 0\n\
- bl GetMonData\n\
- adds r4, r0, 0\n\
- lsls r4, 16\n\
- lsrs r4, 16\n\
- mov r0, r8\n\
- movs r1, 0x15\n\
- movs r2, 0\n\
- bl GetMonData\n\
- adds r1, r0, 0\n\
- lsls r1, 24\n\
- lsrs r1, 24\n\
- adds r0, r4, 0\n\
- ldr r2, [sp, 0x8]\n\
- bl CalculatePPWithBonus\n\
- lsls r0, 24\n\
- lsrs r6, r0, 24\n\
- cmp r5, 0x2\n\
- bhi _08042BBE\n\
- cmp r6, 0x4\n\
- ble _08042BBE\n\
- movs r2, 0\n\
- str r2, [sp, 0x10]\n\
- b _08042BBE\n\
- .align 2, 0\n\
-_08042B48: .4byte gPPUpGetMask\n\
-_08042B4C:\n\
- mov r0, r8\n\
- movs r1, 0x20\n\
- movs r2, 0\n\
- bl GetMonData\n\
- cmp r0, 0x63\n\
- bhi _08042BB4\n\
- ldr r0, [sp, 0x10]\n\
- cmp r0, 0\n\
- bne _08042BB4\n\
- ldr r1, [sp, 0x18]\n\
- cmp r1, 0\n\
- bne _08042BB4\n\
- b _08042BAC\n\
-_08042B68:\n\
- mov r0, r8\n\
- movs r1, 0x20\n\
- movs r2, 0\n\
- bl GetMonData\n\
- cmp r0, 0x63\n\
- bls _08042BB4\n\
- mov r0, r8\n\
- movs r1, 0x20\n\
- movs r2, 0\n\
- bl GetMonData\n\
- cmp r0, 0xC7\n\
- bhi _08042BB4\n\
- ldr r2, [sp, 0x10]\n\
- cmp r2, 0\n\
- bne _08042BB4\n\
- ldr r0, [sp, 0x18]\n\
- cmp r0, 0\n\
- bne _08042BB4\n\
- b _08042BAC\n\
-_08042B92:\n\
- mov r0, r8\n\
- movs r1, 0x20\n\
- movs r2, 0\n\
- bl GetMonData\n\
- cmp r0, 0xC7\n\
- bls _08042BB4\n\
- ldr r1, [sp, 0x10]\n\
- cmp r1, 0\n\
- bne _08042BB4\n\
- ldr r2, [sp, 0x18]\n\
- cmp r2, 0\n\
- bne _08042BB4\n\
-_08042BAC:\n\
- ldr r0, [sp, 0x14]\n\
- add r0, r10\n\
- ldrb r0, [r0]\n\
- str r0, [sp, 0x18]\n\
-_08042BB4:\n\
- mov r0, r10\n\
- adds r0, 0x1\n\
- lsls r0, 24\n\
- lsrs r0, 24\n\
- mov r10, r0\n\
-_08042BBE:\n\
- movs r0, 0x1\n\
- add r9, r0\n\
- lsrs r7, 1\n\
- cmp r7, 0\n\
- beq _08042BCA\n\
- b _08042A7C\n\
-_08042BCA:\n\
- ldr r1, [sp, 0xC]\n\
- adds r1, 0x1\n\
- str r1, [sp, 0xC]\n\
- cmp r1, 0x5\n\
- bgt _08042BD6\n\
- b _08042582\n\
-_08042BD6:\n\
- ldr r0, [sp, 0x10]\n\
-_08042BD8:\n\
- add sp, 0x20\n\
- pop {r3-r5}\n\
- mov r8, r3\n\
- mov r9, r4\n\
- mov r10, r5\n\
- pop {r4-r7}\n\
- pop {r1}\n\
- bx r1\n\
- .syntax divided\n");
-}
-#endif
static bool8 sub_8042BE8(struct Pokemon *mon, u32 unused, u32 healMask, u8 battleId)
{
@@ -6106,7 +4836,7 @@ const u8 *Battle_PrintStatBoosterEffectMessage(u16 itemId)
}
else
{
- sBattler_AI = gBattlerInMenuId;
+ gBattlerAttacker = gBattlerInMenuId;
BattleStringExpandPlaceholdersToDisplayedString(BattleText_GetPumped);
}
}
@@ -6114,7 +4844,7 @@ const u8 *Battle_PrintStatBoosterEffectMessage(u16 itemId)
if (itemEffect[3] & 0x80)
{
- sBattler_AI = gBattlerInMenuId;
+ gBattlerAttacker = gBattlerInMenuId;
BattleStringExpandPlaceholdersToDisplayedString(BattleText_MistShroud);
}
@@ -6231,7 +4961,7 @@ u16 GetEvolutionTargetSpecies(struct Pokemon *mon, u8 type, u16 evolutionItem)
if (gEvolutionTable[species][i].param == heldItem)
{
targetSpecies = gEvolutionTable[species][i].targetSpecies;
- if (sub_806E25C() || targetSpecies <= 151)
+ if (IsNationalPokedexEnabled() || targetSpecies <= 151)
{
heldItem = 0;
SetMonData(mon, MON_DATA_HELD_ITEM, &heldItem);
@@ -6466,9 +5196,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 +5213,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++)
@@ -6899,7 +5629,7 @@ u16 sub_8043F90(u16 species)
{
species = SpeciesToNationalPokedexNum(species);
- if (!sub_806E25C() && species > 151)
+ if (!IsNationalPokedexEnabled() && species > 151)
return 0xFFFF;
return species;
}
@@ -6962,10 +5692,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 +5805,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 +5864,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/prof_pc.c b/src/prof_pc.c
index 35566e714..aeabc8644 100644
--- a/src/prof_pc.c
+++ b/src/prof_pc.c
@@ -33,7 +33,7 @@ u16 Special_GetPokedexCount(void)
gSpecialVar_0x8005 = GetNationalPokedexCount(0);
gSpecialVar_0x8006 = GetNationalPokedexCount(1);
}
- return sub_806E25C();
+ return IsNationalPokedexEnabled();
}
const u8 * sub_80CA424(u16 count)
diff --git a/src/quest_log.c b/src/quest_log.c
index 0d1cb7984..065b42cef 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"
@@ -410,7 +410,7 @@ const struct WindowTemplate gUnknown_845661C[3] = {
{ 0, 0, 14, 30, 6, 15, 0x14c }
};
-const struct TextColor gUnknown_8456634 = {15, 1, 12};
+const u8 gUnknown_8456634[3] = {15, 1, 12};
const u16 gUnknown_8456638[] = INCBIN_U16("data/graphics/unknown_8456638.bin");
@@ -871,7 +871,7 @@ void sub_8110F90(u8 unused)
gSaveBlock1Ptr->location.mapNum = 19;
gSaveBlock1Ptr->location.warpId = -1;
gUnknown_203ADF8 = 0;
- gUnknown_2031DD8 = 1;
+ gDisableMapMusicChangeOnMapLoad = 1;
sub_8082740(1);
sub_8111368();
}
@@ -923,7 +923,7 @@ void sub_8111070(u8 a0)
StringAppend(gStringVar4, gStringVar1);
}
- AddTextPrinterParameterized4(gUnknown_203ADFE[0], 2, 2, 2, 1, 2, &gUnknown_8456634, 0, gStringVar4);
+ AddTextPrinterParameterized4(gUnknown_203ADFE[0], 2, 2, 2, 1, 2, gUnknown_8456634, 0, gStringVar4);
PutWindowTilemap(gUnknown_203ADFE[0]);
PutWindowTilemap(gUnknown_203ADFE[1]);
CopyWindowToVram(gUnknown_203ADFE[0], 2);
@@ -986,7 +986,7 @@ void sub_8111368(void)
{
gUnknown_203ADFA = 2;
sub_806E6FC();
- ClearItemSlotsInAllBagPockets();
+ ClearBag();
ClearPCItemSlots();
if (sub_8110AC8() == 1)
{
@@ -1418,7 +1418,7 @@ void sub_8111D10(void)
PutWindowTilemap(gUnknown_203ADFE[2]);
sub_8111D90(gUnknown_203ADFE[2]);
- AddTextPrinterParameterized4(gUnknown_203ADFE[2], 2, 2, gUnknown_8456698[count], 1, 0, &gUnknown_8456634, 0, gStringVar4);
+ AddTextPrinterParameterized4(gUnknown_203ADFE[2], 2, 2, gUnknown_8456698[count], 1, 0, gUnknown_8456634, 0, gStringVar4);
ScheduleBgCopyTilemapToVram(0);
}
@@ -1567,7 +1567,7 @@ void sub_81120AC(u8 taskId)
switch (data[0])
{
case 0:
- gUnknown_2031DD8 = 0;
+ gDisableMapMusicChangeOnMapLoad = 0;
Overworld_PlaySpecialMapMusic();
sub_811229C();
FillWindowPixelRect(gUnknown_203ADFE[0], 0xF, 0, 0, gUnknown_845661C[0].width * 8, gUnknown_845661C[0].height * 8);
@@ -2463,13 +2463,13 @@ void sub_8112FD0(void)
sub_8112F18(gUnknown_203B020);
}
-const struct TextColor gUnknown_8456930 = {
+const u8 gUnknown_8456930[3] = {
0, 10, 2
};
void sub_8112FE4(const u8 * a0)
{
- AddTextPrinterParameterized4(gUnknown_203B020, 0x02, 2, 5, 1, 1, &gUnknown_8456930, -1, a0);
+ AddTextPrinterParameterized4(gUnknown_203B020, 0x02, 2, 5, 1, 1, gUnknown_8456930, -1, a0);
}
void sub_8113018(const u8 * text, u8 mode)
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..85059691f 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"
@@ -733,7 +733,7 @@ bool8 ScrCmd_warp(struct ScriptContext *ctx)
u16 x = VarGet(ScriptReadHalfword(ctx));
u16 y = VarGet(ScriptReadHalfword(ctx));
- Overworld_SetWarpDestination(mapGroup, mapNum, warpId, x, y);
+ SetWarpDestination(mapGroup, mapNum, warpId, x, y);
DoWarp();
ResetInitialPlayerAvatarState();
return TRUE;
@@ -747,7 +747,7 @@ bool8 ScrCmd_warpsilent(struct ScriptContext *ctx)
u16 x = VarGet(ScriptReadHalfword(ctx));
u16 y = VarGet(ScriptReadHalfword(ctx));
- Overworld_SetWarpDestination(mapGroup, mapNum, warpId, x, y);
+ SetWarpDestination(mapGroup, mapNum, warpId, x, y);
DoDiveWarp();
ResetInitialPlayerAvatarState();
return TRUE;
@@ -761,7 +761,7 @@ bool8 ScrCmd_warpdoor(struct ScriptContext *ctx)
u16 x = VarGet(ScriptReadHalfword(ctx));
u16 y = VarGet(ScriptReadHalfword(ctx));
- Overworld_SetWarpDestination(mapGroup, mapNum, warpId, x, y);
+ SetWarpDestination(mapGroup, mapNum, warpId, x, y);
DoDoorWarp();
ResetInitialPlayerAvatarState();
return TRUE;
@@ -778,7 +778,7 @@ bool8 ScrCmd_warphole(struct ScriptContext *ctx)
if (mapGroup == 0xFF && mapNum == 0xFF)
SetWarpDestinationToFixedHoleWarp(x - 7, y - 7);
else
- Overworld_SetWarpDestination(mapGroup, mapNum, -1, x - 7, y - 7);
+ SetWarpDestination(mapGroup, mapNum, -1, x - 7, y - 7);
DoFallWarp();
ResetInitialPlayerAvatarState();
return TRUE;
@@ -792,7 +792,7 @@ bool8 ScrCmd_warpteleport(struct ScriptContext *ctx)
u16 x = VarGet(ScriptReadHalfword(ctx));
u16 y = VarGet(ScriptReadHalfword(ctx));
- Overworld_SetWarpDestination(mapGroup, mapNum, warpId, x, y);
+ SetWarpDestination(mapGroup, mapNum, warpId, x, y);
sub_807E59C();
ResetInitialPlayerAvatarState();
return TRUE;
@@ -806,7 +806,7 @@ bool8 ScrCmd_warpteleport2(struct ScriptContext *ctx)
u16 x = VarGet(ScriptReadHalfword(ctx));
u16 y = VarGet(ScriptReadHalfword(ctx));
- Overworld_SetWarpDestination(mapGroup, mapNum, warpId, x, y);
+ SetWarpDestination(mapGroup, mapNum, warpId, x, y);
sub_805DAE4(GetPlayerFacingDirection());
sub_807E500();
ResetInitialPlayerAvatarState();
@@ -821,7 +821,7 @@ bool8 ScrCmd_setwarp(struct ScriptContext *ctx)
u16 x = VarGet(ScriptReadHalfword(ctx));
u16 y = VarGet(ScriptReadHalfword(ctx));
- Overworld_SetWarpDestination(mapGroup, mapNum, warpId, x, y);
+ SetWarpDestination(mapGroup, mapNum, warpId, x, y);
return FALSE;
}
diff --git a/src/seagallop.c b/src/seagallop.c
index e3333ef32..5374f7bac 100644
--- a/src/seagallop.c
+++ b/src/seagallop.c
@@ -319,7 +319,7 @@ static void Task_SeaGallop_3(void)
gSpecialVar_0x8006 = 0;
warpInfo = sSeaGallopSpawnTable[gSpecialVar_0x8006];
- Overworld_SetWarpDestination(warpInfo[0], warpInfo[1], -1, warpInfo[2], warpInfo[3]);
+ SetWarpDestination(warpInfo[0], warpInfo[1], -1, warpInfo[2], warpInfo[3]);
PlayRainStoppingSoundEffect();
PlaySE(SE_KAIDAN);
gFieldCallback = sub_807DF64;
diff --git a/src/slot_machine.c b/src/slot_machine.c
index 740234d95..28292902f 100644
--- a/src/slot_machine.c
+++ b/src/slot_machine.c
@@ -2074,7 +2074,7 @@ static bool32 sub_8141180(u8 a0)
static bool8 sub_8141198(u8 * state, struct SlotMachineSetupTaskData * ptr)
{
u16 pal;
- struct TextColor textColor;
+ u8 textColor[3];
u32 x;
switch (*state)
@@ -2125,10 +2125,10 @@ static bool8 sub_8141198(u8 * state, struct SlotMachineSetupTaskData * ptr)
PutWindowTilemap(1);
x = 0xEC - GetStringWidth(0, gString_SlotMachineControls, 0);
- textColor.fgColor = 15;
- textColor.bgColor = 1;
- textColor.shadowColor = 2;
- AddTextPrinterParameterized3(1, 0, x, 0, &textColor, 0, gString_SlotMachineControls);
+ textColor[0] = 15;
+ textColor[1] = 1;
+ textColor[2] = 2;
+ AddTextPrinterParameterized3(1, 0, x, 0, textColor, 0, gString_SlotMachineControls);
CopyBgTilemapBufferToVram(0);
SetGpuRegBits(REG_OFFSET_DISPCNT, DISPCNT_MODE_0 | 0x20 | DISPCNT_OBJ_1D_MAP | DISPCNT_OBJ_ON);
diff --git a/src/sound.c b/src/sound.c
index dc4b9f604..41b9f5b7d 100644
--- a/src/sound.c
+++ b/src/sound.c
@@ -15,7 +15,7 @@ struct Fanfare
};
// TODO: what are these
-extern u8 gUnknown_2031DD8;
+extern u8 gDisableMapMusicChangeOnMapLoad;
extern u8 gUnknown_203ADFA;
extern u8 gUnknown_203F174;
@@ -571,7 +571,7 @@ void PlayBGM(u16 songNum)
void PlaySE(u16 songNum)
{
- if(gUnknown_2031DD8 == 0 && gUnknown_203ADFA != 2)
+ if(gDisableMapMusicChangeOnMapLoad == 0 && gUnknown_203ADFA != 2)
m4aSongNumStart(songNum);
}
diff --git a/src/string_util.c b/src/string_util.c
index e1129a5a5..d5b5202df 100644
--- a/src/string_util.c
+++ b/src/string_util.c
@@ -2,6 +2,10 @@
#include "string_util.h"
#include "text.h"
+EWRAM_DATA u8 gStringVar1[32] = {};
+EWRAM_DATA u8 gStringVar2[20] = {};
+EWRAM_DATA u8 gStringVar3[20] = {};
+EWRAM_DATA u8 gStringVar4[1000] = {};
EWRAM_DATA u8 gUnknownStringVar[16] = {0};
static const u8 sDigits[] = __("0123456789ABCDEF");
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/text.c b/src/text.c
index d36a448ad..5b88e9f75 100644
--- a/src/text.c
+++ b/src/text.c
@@ -6,19 +6,18 @@
#include "text.h"
#include "sprite.h"
#include "blit.h"
+#include "sound.h"
+#include "m4a.h"
+#include "quest_log.h"
+#include "window.h"
+#include "graphics.h"
+#include "dynamic_placeholder_text_util.h"
extern u8 gGlyphInfo[0x90];
-extern u8 gUnknown_203ADFA;
-extern u16 gTMCaseMainWindowPalette[];
extern const struct OamData gOamData_83AC9D0;
-extern void FillWindowPixelRect(u8 windowId, u8 fillValue, u16 x, u16 y, u16 width, u16 height);
-extern void BlitBitmapRectToWindow(u8 windowId, const u8 *pixels, u16 srcX, u16 srcY, u16 srcWidth, int srcHeight, u16 destX, u16 destY, u16 rectWidth, u16 rectHeight);
-extern u8 GetKeypadIconWidth(u8 keypadIconId);
-extern void CopyWindowToVram(u8 windowId, u8 mode);
-extern s32 GetGlyphWidthFont6(u16 glyphId, bool32 isJapanese);
-extern void PlaySE(u16 songNum);
-extern u8* DynamicPlaceholderTextUtil_GetPlaceholderPtr(u8 a1);
+static void DecompressGlyphFont3(u16 glyphId, bool32 isJapanese);
+static void DecompressGlyphFont4(u16 glyphId, bool32 isJapanese);
TextFlags gTextFlags;
@@ -378,107 +377,107 @@ const u16 gFont9JapaneseGlyphs[] = INCBIN_U16("data/graphics/fonts/font9_jap.fwj
u16 Font0Func(struct TextPrinter *textPrinter)
{
- struct TextPrinterSubStruct *subStruct = &textPrinter->sub_union.sub;
+ struct TextPrinterSubStruct *subStruct = &textPrinter->subUnion.sub;
- if (subStruct->field_1_top == 0)
+ if (subStruct->hasGlyphIdBeenSet == 0)
{
- textPrinter->sub_union.sub.font_type = 0;
- subStruct->field_1_top = 1;
+ textPrinter->subUnion.sub.glyphId = 0;
+ subStruct->hasGlyphIdBeenSet = 1;
}
return RenderText(textPrinter);
}
u16 Font1Func(struct TextPrinter *textPrinter)
{
- struct TextPrinterSubStruct *subStruct = &textPrinter->sub_union.sub;
+ struct TextPrinterSubStruct *subStruct = &textPrinter->subUnion.sub;
- if (subStruct->field_1_top == 0)
+ if (subStruct->hasGlyphIdBeenSet == 0)
{
- textPrinter->sub_union.sub.font_type = 1;
- subStruct->field_1_top = 1;
+ textPrinter->subUnion.sub.glyphId = 1;
+ subStruct->hasGlyphIdBeenSet = 1;
}
return RenderText(textPrinter);
}
u16 Font2Func(struct TextPrinter *textPrinter)
{
- struct TextPrinterSubStruct *subStruct = &textPrinter->sub_union.sub;
+ struct TextPrinterSubStruct *subStruct = &textPrinter->subUnion.sub;
- if (subStruct->field_1_top == 0)
+ if (subStruct->hasGlyphIdBeenSet == 0)
{
- textPrinter->sub_union.sub.font_type = 2;
- subStruct->field_1_top = 1;
+ textPrinter->subUnion.sub.glyphId = 2;
+ subStruct->hasGlyphIdBeenSet = 1;
}
return RenderText(textPrinter);
}
u16 Font3Func(struct TextPrinter *textPrinter)
{
- struct TextPrinterSubStruct *subStruct = &textPrinter->sub_union.sub;
+ struct TextPrinterSubStruct *subStruct = &textPrinter->subUnion.sub;
- if (subStruct->field_1_top == 0)
+ if (subStruct->hasGlyphIdBeenSet == 0)
{
- textPrinter->sub_union.sub.font_type = 3;
- subStruct->field_1_top = 1;
+ textPrinter->subUnion.sub.glyphId = 3;
+ subStruct->hasGlyphIdBeenSet = 1;
}
return RenderText(textPrinter);
}
u16 Font4Func(struct TextPrinter *textPrinter)
{
- struct TextPrinterSubStruct *subStruct = &textPrinter->sub_union.sub;
+ struct TextPrinterSubStruct *subStruct = &textPrinter->subUnion.sub;
- if (subStruct->field_1_top == 0)
+ if (subStruct->hasGlyphIdBeenSet == 0)
{
- textPrinter->sub_union.sub.font_type = 4;
- subStruct->field_1_top = 1;
+ textPrinter->subUnion.sub.glyphId = 4;
+ subStruct->hasGlyphIdBeenSet = 1;
}
return RenderText(textPrinter);
}
u16 Font5Func(struct TextPrinter *textPrinter)
{
- struct TextPrinterSubStruct *subStruct = &textPrinter->sub_union.sub;
+ struct TextPrinterSubStruct *subStruct = &textPrinter->subUnion.sub;
- if (subStruct->field_1_top == 0)
+ if (subStruct->hasGlyphIdBeenSet == 0)
{
- textPrinter->sub_union.sub.font_type = 5;
- subStruct->field_1_top = 1;
+ textPrinter->subUnion.sub.glyphId = 5;
+ subStruct->hasGlyphIdBeenSet = 1;
}
return RenderText(textPrinter);
}
void TextPrinterInitDownArrowCounters(struct TextPrinter *textPrinter)
{
- struct TextPrinterSubStruct *subStruct = &textPrinter->sub_union.sub;
+ struct TextPrinterSubStruct *subStruct = &textPrinter->subUnion.sub;
if (gTextFlags.autoScroll == 1)
- subStruct->frames_visible_counter = 0;
+ subStruct->autoScrollDelay = 0;
else
{
- subStruct->field_1_upmid = 0;
- subStruct->field_1 = 0;
+ subStruct->downArrowYPosIdx = 0;
+ subStruct->downArrowDelay = 0;
}
}
void TextPrinterDrawDownArrow(struct TextPrinter *textPrinter)
{
- struct TextPrinterSubStruct *subStruct = &textPrinter->sub_union.sub;
+ struct TextPrinterSubStruct *subStruct = &textPrinter->subUnion.sub;
const u8 *arrowTiles;
if (gTextFlags.autoScroll == 0)
{
- if (subStruct->field_1 != 0)
+ if (subStruct->downArrowDelay != 0)
{
- subStruct->field_1 = ((*(u32*)&textPrinter->sub_union.sub) << 19 >> 27) - 1; // convoluted way of getting field_1, necessary to match
+ subStruct->downArrowDelay = ((*(u32*)&textPrinter->subUnion.sub) << 19 >> 27) - 1; // convoluted way of getting field_1, necessary to match
}
else
{
FillWindowPixelRect(
- textPrinter->subPrinter.windowId,
- textPrinter->subPrinter.bgColor << 4 | textPrinter->subPrinter.bgColor,
- textPrinter->subPrinter.currentX,
- textPrinter->subPrinter.currentY,
+ textPrinter->printerTemplate.windowId,
+ textPrinter->printerTemplate.bgColor << 4 | textPrinter->printerTemplate.bgColor,
+ textPrinter->printerTemplate.currentX,
+ textPrinter->printerTemplate.currentY,
10,
12);
@@ -494,20 +493,20 @@ void TextPrinterDrawDownArrow(struct TextPrinter *textPrinter)
}
BlitBitmapRectToWindow(
- textPrinter->subPrinter.windowId,
+ textPrinter->printerTemplate.windowId,
arrowTiles,
- gDownArrowYCoords[*(u32*)subStruct << 17 >> 30], // subStruct->field_1_upmid but again, stupidly retrieved
+ gDownArrowYCoords[*(u32*)subStruct << 17 >> 30], // subStruct->downArrowYPosIdx but again, stupidly retrieved
0,
0x80,
0x10,
- textPrinter->subPrinter.currentX,
- textPrinter->subPrinter.currentY,
+ textPrinter->printerTemplate.currentX,
+ textPrinter->printerTemplate.currentY,
10,
12);
- CopyWindowToVram(textPrinter->subPrinter.windowId, 0x2);
+ CopyWindowToVram(textPrinter->printerTemplate.windowId, 0x2);
- subStruct->field_1 = 0x8;
- subStruct->field_1_upmid = (*(u32*)subStruct << 17 >> 30) + 1;
+ subStruct->downArrowDelay = 0x8;
+ subStruct->downArrowYPosIdx = (*(u32*)subStruct << 17 >> 30) + 1;
}
}
}
@@ -515,27 +514,27 @@ void TextPrinterDrawDownArrow(struct TextPrinter *textPrinter)
void TextPrinterClearDownArrow(struct TextPrinter *textPrinter)
{
FillWindowPixelRect(
- textPrinter->subPrinter.windowId,
- textPrinter->subPrinter.bgColor << 4 | textPrinter->subPrinter.bgColor,
- textPrinter->subPrinter.currentX,
- textPrinter->subPrinter.currentY,
+ textPrinter->printerTemplate.windowId,
+ textPrinter->printerTemplate.bgColor << 4 | textPrinter->printerTemplate.bgColor,
+ textPrinter->printerTemplate.currentX,
+ textPrinter->printerTemplate.currentY,
10,
12);
- CopyWindowToVram(textPrinter->subPrinter.windowId, 0x2);
+ CopyWindowToVram(textPrinter->printerTemplate.windowId, 0x2);
}
bool8 TextPrinterWaitAutoMode(struct TextPrinter *textPrinter)
{
- struct TextPrinterSubStruct *subStruct = &textPrinter->sub_union.sub;
+ struct TextPrinterSubStruct *subStruct = &textPrinter->subUnion.sub;
u8 delay = (gUnknown_203ADFA == 2) ? 50 : 120;
- if (subStruct->frames_visible_counter == delay)
+ if (subStruct->autoScrollDelay == delay)
{
return TRUE;
}
else
{
- subStruct->frames_visible_counter++;
+ subStruct->autoScrollDelay++;
return FALSE;
}
}
@@ -550,7 +549,7 @@ bool16 TextPrinterWaitWithDownArrow(struct TextPrinter *textPrinter)
else
{
TextPrinterDrawDownArrow(textPrinter);
- if (gMain.newKeys & (A_BUTTON | B_BUTTON))
+ if (JOY_NEW(A_BUTTON | B_BUTTON))
{
result = TRUE;
PlaySE(5);
@@ -568,7 +567,7 @@ bool16 TextPrinterWait(struct TextPrinter *textPrinter)
}
else
{
- if (gMain.newKeys & (A_BUTTON | B_BUTTON))
+ if (JOY_NEW(A_BUTTON | B_BUTTON))
{
result = TRUE;
PlaySE(5);
@@ -619,772 +618,291 @@ void DrawDownArrow(u8 windowId, u16 x, u16 y, u8 bgColor, bool8 drawArrow, u8 *c
}
}
-__attribute__((naked))
u16 RenderText(struct TextPrinter *textPrinter)
{
- asm(".syntax unified\n\
- push {r4-r6,lr}\n\
- adds r6, r0, 0\n\
- adds r4, r6, 0\n\
- adds r4, 0x14\n\
- ldrb r0, [r6, 0x1C]\n\
- cmp r0, 0x6\n\
- bls _080057A0\n\
- b _08005D68\n\
-_080057A0:\n\
- lsls r0, 2\n\
- ldr r1, _080057AC @ =_080057B0\n\
- adds r0, r1\n\
- ldr r0, [r0]\n\
- mov pc, r0\n\
- .align 2, 0\n\
-_080057AC: .4byte _080057B0\n\
- .align 2, 0\n\
-_080057B0:\n\
- .4byte _080057CC\n\
- .4byte _08005C58\n\
- .4byte _08005C6C\n\
- .4byte _08005C98\n\
- .4byte _08005CD0\n\
- .4byte _08005D44\n\
- .4byte _08005D56\n\
-_080057CC:\n\
- ldr r2, _08005820 @ =gMain\n\
- ldrh r1, [r2, 0x2C]\n\
- movs r0, 0x3\n\
- ands r0, r1\n\
- cmp r0, 0\n\
- beq _080057E6\n\
- ldrb r1, [r4]\n\
- movs r0, 0x10\n\
- ands r0, r1\n\
- cmp r0, 0\n\
- beq _080057E6\n\
- movs r0, 0\n\
- strb r0, [r6, 0x1E]\n\
-_080057E6:\n\
- ldrb r1, [r6, 0x1E]\n\
- cmp r1, 0\n\
- beq _08005828\n\
- ldrb r0, [r6, 0x1D]\n\
- cmp r0, 0\n\
- beq _08005828\n\
- subs r0, r1, 0x1\n\
- strb r0, [r6, 0x1E]\n\
- ldr r0, _08005824 @ =gTextFlags\n\
- ldrb r1, [r0]\n\
- movs r0, 0x1\n\
- ands r0, r1\n\
- cmp r0, 0\n\
- bne _08005804\n\
- b _08005B30\n\
-_08005804:\n\
- ldrh r1, [r2, 0x2E]\n\
- movs r0, 0x3\n\
- ands r0, r1\n\
- cmp r0, 0\n\
- bne _08005810\n\
- b _08005B30\n\
-_08005810:\n\
- ldrb r0, [r4]\n\
- movs r1, 0x10\n\
- orrs r0, r1\n\
- strb r0, [r4]\n\
- movs r0, 0\n\
- strb r0, [r6, 0x1E]\n\
- b _08005B30\n\
- .align 2, 0\n\
-_08005820: .4byte gMain\n\
-_08005824: .4byte gTextFlags\n\
-_08005828:\n\
- ldr r2, _08005838 @ =gTextFlags\n\
- ldrb r1, [r2]\n\
- movs r0, 0x4\n\
- ands r0, r1\n\
- cmp r0, 0\n\
- beq _0800583C\n\
- movs r0, 0x1\n\
- b _0800583E\n\
- .align 2, 0\n\
-_08005838: .4byte gTextFlags\n\
-_0800583C:\n\
- ldrb r0, [r6, 0x1D]\n\
-_0800583E:\n\
- strb r0, [r6, 0x1E]\n\
- ldr r0, [r6]\n\
- ldrb r3, [r0]\n\
- adds r0, 0x1\n\
- str r0, [r6]\n\
- adds r0, r3, 0\n\
- subs r0, 0xF8\n\
- cmp r0, 0x7\n\
- bls _08005852\n\
- b _08005B6C\n\
-_08005852:\n\
- lsls r0, 2\n\
- ldr r1, _0800585C @ =_08005860\n\
- adds r0, r1\n\
- ldr r0, [r0]\n\
- mov pc, r0\n\
- .align 2, 0\n\
-_0800585C: .4byte _08005860\n\
- .align 2, 0\n\
-_08005860:\n\
- .4byte _08005B46\n\
- .4byte _08005B34\n\
- .4byte _08005B26\n\
- .4byte _08005B22\n\
- .4byte _080058AC\n\
- .4byte _080058A8\n\
- .4byte _08005880\n\
- .4byte _08005D68\n\
-_08005880:\n\
- ldrb r0, [r6, 0x6]\n\
- strb r0, [r6, 0x8]\n\
- ldrb r1, [r6, 0x5]\n\
- ldr r0, _080058A4 @ =gFonts\n\
- ldr r2, [r0]\n\
- lsls r0, r1, 1\n\
- adds r0, r1\n\
- lsls r0, 2\n\
- adds r0, r2\n\
- ldrb r1, [r6, 0xB]\n\
- ldrb r0, [r0, 0x5]\n\
- adds r1, r0\n\
- ldrb r0, [r6, 0x9]\n\
- adds r0, r1\n\
- strb r0, [r6, 0x9]\n\
-_0800589E:\n\
- movs r0, 0x2\n\
- b _08005D6A\n\
- .align 2, 0\n\
-_080058A4: .4byte gFonts\n\
-_080058A8:\n\
- ldr r0, [r6]\n\
- b _08005B0A\n\
-_080058AC:\n\
- ldr r0, [r6]\n\
- ldrb r3, [r0]\n\
- adds r0, 0x1\n\
- str r0, [r6]\n\
- subs r0, r3, 0x1\n\
- cmp r0, 0x17\n\
- bls _080058BC\n\
- b _08005B6C\n\
-_080058BC:\n\
- lsls r0, 2\n\
- ldr r1, _080058C8 @ =_080058CC\n\
- adds r0, r1\n\
- ldr r0, [r0]\n\
- mov pc, r0\n\
- .align 2, 0\n\
-_080058C8: .4byte _080058CC\n\
- .align 2, 0\n\
-_080058CC:\n\
- .4byte _0800592C\n\
- .4byte _0800594E\n\
- .4byte _08005972\n\
- .4byte _0800598C\n\
- .4byte _080059D8\n\
- .4byte _080059DC\n\
- .4byte _0800589E\n\
- .4byte _080059F4\n\
- .4byte _08005A04\n\
- .4byte _08005A1A\n\
- .4byte _08005A1E\n\
- .4byte _08005A66\n\
- .4byte _08005A70\n\
- .4byte _08005A78\n\
- .4byte _08005A84\n\
- .4byte _08005A4C\n\
- .4byte _08005AB0\n\
- .4byte _08005ACC\n\
- .4byte _08005ADC\n\
- .4byte _08005B00\n\
- .4byte _08005B10\n\
- .4byte _08005B18\n\
- .4byte _08005A96\n\
- .4byte _08005AA4\n\
-_0800592C:\n\
- ldr r2, [r6]\n\
- ldrb r1, [r2]\n\
- lsls r1, 4\n\
- ldrb r3, [r6, 0xC]\n\
- movs r0, 0xF\n\
- ands r0, r3\n\
- orrs r0, r1\n\
- strb r0, [r6, 0xC]\n\
- adds r2, 0x1\n\
- str r2, [r6]\n\
- lsls r0, 24\n\
- lsrs r0, 28\n\
- ldrb r2, [r6, 0xD]\n\
- lsls r1, r2, 28\n\
- lsrs r1, 28\n\
- lsrs r2, 4\n\
- b _080059D2\n\
-_0800594E:\n\
- ldr r1, [r6]\n\
- ldrb r2, [r1]\n\
- movs r0, 0xF\n\
- ands r0, r2\n\
- ldrb r3, [r6, 0xD]\n\
- movs r2, 0x10\n\
- negs r2, r2\n\
- ands r2, r3\n\
- orrs r2, r0\n\
- strb r2, [r6, 0xD]\n\
- adds r1, 0x1\n\
- str r1, [r6]\n\
- ldrb r0, [r6, 0xC]\n\
- lsrs r0, 4\n\
- lsls r1, r2, 28\n\
- lsrs r1, 28\n\
- lsrs r2, 4\n\
- b _080059D2\n\
-_08005972:\n\
- ldr r1, [r6]\n\
- ldrb r0, [r1]\n\
- lsls r0, 4\n\
- ldrb r3, [r6, 0xD]\n\
- movs r2, 0xF\n\
- ands r2, r3\n\
- orrs r2, r0\n\
- strb r2, [r6, 0xD]\n\
- adds r1, 0x1\n\
- str r1, [r6]\n\
- ldrb r0, [r6, 0xC]\n\
- lsrs r0, 4\n\
- b _080059CA\n\
-_0800598C:\n\
- ldr r3, [r6]\n\
- ldrb r1, [r3]\n\
- lsls r1, 4\n\
- ldrb r4, [r6, 0xC]\n\
- movs r2, 0xF\n\
- adds r0, r2, 0\n\
- ands r0, r4\n\
- orrs r0, r1\n\
- strb r0, [r6, 0xC]\n\
- adds r5, r3, 0x1\n\
- str r5, [r6]\n\
- ldrb r3, [r3, 0x1]\n\
- adds r1, r2, 0\n\
- ands r1, r3\n\
- ldrb r4, [r6, 0xD]\n\
- movs r3, 0x10\n\
- negs r3, r3\n\
- ands r3, r4\n\
- orrs r3, r1\n\
- strb r3, [r6, 0xD]\n\
- adds r4, r5, 0x1\n\
- str r4, [r6]\n\
- ldrb r1, [r5, 0x1]\n\
- lsls r1, 4\n\
- ands r2, r3\n\
- orrs r2, r1\n\
- strb r2, [r6, 0xD]\n\
- adds r4, 0x1\n\
- str r4, [r6]\n\
- lsls r0, 24\n\
- lsrs r0, 28\n\
-_080059CA:\n\
- lsls r1, r2, 28\n\
- lsrs r1, 28\n\
- lsls r2, 24\n\
- lsrs r2, 28\n\
-_080059D2:\n\
- bl GenerateFontHalfRowLookupTable\n\
- b _0800589E\n\
-_080059D8:\n\
- ldr r0, [r6]\n\
- b _08005B0A\n\
-_080059DC:\n\
- ldr r0, [r6]\n\
- ldrb r0, [r0]\n\
- movs r1, 0xF\n\
- ands r1, r0\n\
- ldrb r2, [r4]\n\
- movs r0, 0x10\n\
- negs r0, r0\n\
- ands r0, r2\n\
- orrs r0, r1\n\
- strb r0, [r4]\n\
- ldr r0, [r6]\n\
- b _08005B0A\n\
-_080059F4:\n\
- ldr r0, [r6]\n\
- ldrb r1, [r0]\n\
- strb r1, [r6, 0x1E]\n\
- adds r0, 0x1\n\
- str r0, [r6]\n\
- movs r0, 0x6\n\
- strb r0, [r6, 0x1C]\n\
- b _0800589E\n\
-_08005A04:\n\
- movs r0, 0x1\n\
- strb r0, [r6, 0x1C]\n\
- ldrb r1, [r2]\n\
- movs r0, 0x4\n\
- ands r0, r1\n\
- cmp r0, 0\n\
- bne _08005A14\n\
- b _08005B30\n\
-_08005A14:\n\
- movs r0, 0\n\
- strb r0, [r4, 0x2]\n\
- b _08005B30\n\
-_08005A1A:\n\
- movs r0, 0x5\n\
- b _08005D52\n\
-_08005A1E:\n\
- ldr r0, [r6]\n\
- ldrb r3, [r0]\n\
- adds r1, r0, 0x1\n\
- str r1, [r6]\n\
- ldrb r0, [r0, 0x1]\n\
- lsls r0, 8\n\
- orrs r3, r0\n\
- adds r1, 0x1\n\
- str r1, [r6]\n\
- ldr r0, _08005A48 @ =gUnknown_203ADFA\n\
- ldrb r0, [r0]\n\
- subs r0, 0x2\n\
- lsls r0, 24\n\
- lsrs r0, 24\n\
- cmp r0, 0x1\n\
- bhi _08005A40\n\
- b _0800589E\n\
-_08005A40:\n\
- adds r0, r3, 0\n\
- bl PlayBGM\n\
- b _0800589E\n\
- .align 2, 0\n\
-_08005A48: .4byte gUnknown_203ADFA\n\
-_08005A4C:\n\
- ldr r0, [r6]\n\
- ldrb r3, [r0]\n\
- adds r1, r0, 0x1\n\
- str r1, [r6]\n\
- ldrb r0, [r0, 0x1]\n\
- lsls r0, 8\n\
- orrs r3, r0\n\
- adds r1, 0x1\n\
- str r1, [r6]\n\
- adds r0, r3, 0\n\
- bl PlaySE\n\
- b _0800589E\n\
-_08005A66:\n\
- ldr r1, [r6]\n\
- adds r0, r1, 0x1\n\
- str r0, [r6]\n\
- ldrb r3, [r1, 0x1]\n\
- b _08005B6C\n\
-_08005A70:\n\
- ldr r1, [r6]\n\
- ldrb r0, [r1]\n\
- ldrb r2, [r6, 0x6]\n\
- b _08005AD2\n\
-_08005A78:\n\
- ldr r1, [r6]\n\
- ldrb r0, [r1]\n\
- ldrb r3, [r6, 0x7]\n\
- adds r0, r3\n\
- strb r0, [r6, 0x9]\n\
- b _08005AD6\n\
-_08005A84:\n\
- ldrb r0, [r6, 0x4]\n\
- ldrb r2, [r6, 0xD]\n\
- lsls r2, 28\n\
- lsrs r1, r2, 4\n\
- orrs r1, r2\n\
- lsrs r1, 24\n\
- bl FillWindowPixelBuffer\n\
- b _0800589E\n\
-_08005A96:\n\
- ldr r0, _08005AA0 @ =gMPlayInfo_BGM\n\
- bl m4aMPlayStop\n\
- b _0800589E\n\
- .align 2, 0\n\
-_08005AA0: .4byte gMPlayInfo_BGM\n\
-_08005AA4:\n\
- ldr r0, _08005AAC @ =gMPlayInfo_BGM\n\
- bl m4aMPlayContinue\n\
- b _0800589E\n\
- .align 2, 0\n\
-_08005AAC: .4byte gMPlayInfo_BGM\n\
-_08005AB0:\n\
- ldr r0, [r6]\n\
- ldrb r4, [r0]\n\
- adds r0, 0x1\n\
- str r0, [r6]\n\
- cmp r4, 0\n\
- bgt _08005ABE\n\
- b _0800589E\n\
-_08005ABE:\n\
- adds r0, r6, 0\n\
- adds r1, r4, 0\n\
- bl ClearTextSpan\n\
- ldrb r0, [r6, 0x8]\n\
- adds r0, r4\n\
- b _08005C4E\n\
-_08005ACC:\n\
- ldr r1, [r6]\n\
- ldrb r0, [r6, 0x6]\n\
- ldrb r2, [r1]\n\
-_08005AD2:\n\
- adds r0, r2\n\
- strb r0, [r6, 0x8]\n\
-_08005AD6:\n\
- adds r1, 0x1\n\
- str r1, [r6]\n\
- b _0800589E\n\
-_08005ADC:\n\
- ldr r0, [r6]\n\
- ldrb r2, [r0]\n\
- ldrb r1, [r6, 0x6]\n\
- adds r2, r1\n\
- adds r0, 0x1\n\
- str r0, [r6]\n\
- ldrb r0, [r6, 0x8]\n\
- subs r4, r2, r0\n\
- cmp r4, 0\n\
- bgt _08005AF2\n\
- b _0800589E\n\
-_08005AF2:\n\
- adds r0, r6, 0\n\
- adds r1, r4, 0\n\
- bl ClearTextSpan\n\
- ldrb r0, [r6, 0x8]\n\
- adds r0, r4\n\
- b _08005C4E\n\
-_08005B00:\n\
- ldr r0, [r6]\n\
- ldrb r2, [r0]\n\
- adds r1, r6, 0\n\
- adds r1, 0x20\n\
- strb r2, [r1]\n\
-_08005B0A:\n\
- adds r0, 0x1\n\
- str r0, [r6]\n\
- b _0800589E\n\
-_08005B10:\n\
- adds r1, r6, 0\n\
- adds r1, 0x21\n\
- movs r0, 0x1\n\
- b _08005B1E\n\
-_08005B18:\n\
- adds r1, r6, 0\n\
- adds r1, 0x21\n\
- movs r0, 0\n\
-_08005B1E:\n\
- strb r0, [r1]\n\
- b _0800589E\n\
-_08005B22:\n\
- movs r0, 0x2\n\
- b _08005B28\n\
-_08005B26:\n\
- movs r0, 0x3\n\
-_08005B28:\n\
- strb r0, [r6, 0x1C]\n\
- adds r0, r6, 0\n\
- bl TextPrinterInitDownArrowCounters\n\
-_08005B30:\n\
- movs r0, 0x3\n\
- b _08005D6A\n\
-_08005B34:\n\
- ldr r0, [r6]\n\
- ldrb r3, [r0]\n\
- movs r2, 0x80\n\
- lsls r2, 1\n\
- adds r1, r2, 0\n\
- orrs r3, r1\n\
- adds r0, 0x1\n\
- str r0, [r6]\n\
- b _08005B6C\n\
-_08005B46:\n\
- ldr r0, [r6]\n\
- ldrb r3, [r0]\n\
- adds r0, 0x1\n\
- str r0, [r6]\n\
- ldrb r0, [r6, 0x4]\n\
- adds r1, r3, 0\n\
- ldrb r2, [r6, 0x8]\n\
- ldrb r3, [r6, 0x9]\n\
- bl DrawKeypadIcon\n\
- ldr r1, _08005B68 @ =gGlyphInfo\n\
- adds r1, 0x80\n\
- strb r0, [r1]\n\
- ldrb r3, [r6, 0xA]\n\
- adds r0, r3\n\
- b _08005C4A\n\
- .align 2, 0\n\
-_08005B68: .4byte gGlyphInfo\n\
-_08005B6C:\n\
- ldr r0, [r4]\n\
- lsls r0, 28\n\
- lsrs r0, 28\n\
- cmp r0, 0x5\n\
- bhi _08005BEE\n\
- lsls r0, 2\n\
- ldr r1, _08005B80 @ =_08005B84\n\
- adds r0, r1\n\
- ldr r0, [r0]\n\
- mov pc, r0\n\
- .align 2, 0\n\
-_08005B80: .4byte _08005B84\n\
- .align 2, 0\n\
-_08005B84:\n\
- .4byte _08005B9C\n\
- .4byte _08005BAA\n\
- .4byte _08005BB8\n\
- .4byte _08005BC6\n\
- .4byte _08005BD4\n\
- .4byte _08005BE2\n\
-_08005B9C:\n\
- adds r0, r6, 0\n\
- adds r0, 0x21\n\
- ldrb r1, [r0]\n\
- adds r0, r3, 0\n\
- bl DecompressGlyphFont0\n\
- b _08005BEE\n\
-_08005BAA:\n\
- adds r0, r6, 0\n\
- adds r0, 0x21\n\
- ldrb r1, [r0]\n\
- adds r0, r3, 0\n\
- bl DecompressGlyphFont1\n\
- b _08005BEE\n\
-_08005BB8:\n\
- adds r0, r6, 0\n\
- adds r0, 0x21\n\
- ldrb r1, [r0]\n\
- adds r0, r3, 0\n\
- bl DecompressGlyphFont2\n\
- b _08005BEE\n\
-_08005BC6:\n\
- adds r0, r6, 0\n\
- adds r0, 0x21\n\
- ldrb r1, [r0]\n\
- adds r0, r3, 0\n\
- bl DecompressGlyphFont3\n\
- b _08005BEE\n\
-_08005BD4:\n\
- adds r0, r6, 0\n\
- adds r0, 0x21\n\
- ldrb r1, [r0]\n\
- adds r0, r3, 0\n\
- bl DecompressGlyphFont4\n\
- b _08005BEE\n\
-_08005BE2:\n\
- adds r0, r6, 0\n\
- adds r0, 0x21\n\
- ldrb r1, [r0]\n\
- adds r0, r3, 0\n\
- bl DecompressGlyphFont5\n\
-_08005BEE:\n\
- adds r0, r6, 0\n\
- bl CopyGlyphToWindow\n\
- adds r2, r6, 0\n\
- adds r2, 0x20\n\
- ldrb r0, [r2]\n\
- cmp r0, 0\n\
- beq _08005C28\n\
- ldr r1, _08005C24 @ =gGlyphInfo\n\
- adds r1, 0x80\n\
- ldrb r0, [r1]\n\
- ldrb r3, [r6, 0x8]\n\
- adds r0, r3\n\
- strb r0, [r6, 0x8]\n\
- ldrb r2, [r2]\n\
- ldrb r0, [r1]\n\
- subs r4, r2, r0\n\
- cmp r4, 0\n\
- ble _08005C50\n\
- adds r0, r6, 0\n\
- adds r1, r4, 0\n\
- bl ClearTextSpan\n\
- ldrb r0, [r6, 0x8]\n\
- adds r0, r4\n\
- b _08005C4E\n\
- .align 2, 0\n\
-_08005C24: .4byte gGlyphInfo\n\
-_08005C28:\n\
- adds r0, r6, 0\n\
- adds r0, 0x21\n\
- ldrb r0, [r0]\n\
- cmp r0, 0\n\
- beq _08005C44\n\
- ldr r0, _08005C40 @ =gGlyphInfo\n\
- adds r0, 0x80\n\
- ldrb r1, [r6, 0xA]\n\
- ldrb r0, [r0]\n\
- adds r1, r0\n\
- ldrb r0, [r6, 0x8]\n\
- b _08005C4C\n\
- .align 2, 0\n\
-_08005C40: .4byte gGlyphInfo\n\
-_08005C44:\n\
- ldr r0, _08005C54 @ =gGlyphInfo\n\
- adds r0, 0x80\n\
- ldrb r0, [r0]\n\
-_08005C4A:\n\
- ldrb r1, [r6, 0x8]\n\
-_08005C4C:\n\
- adds r0, r1\n\
-_08005C4E:\n\
- strb r0, [r6, 0x8]\n\
-_08005C50:\n\
- movs r0, 0\n\
- b _08005D6A\n\
- .align 2, 0\n\
-_08005C54: .4byte gGlyphInfo\n\
-_08005C58:\n\
- adds r0, r6, 0\n\
- bl TextPrinterWait\n\
- lsls r0, 16\n\
- cmp r0, 0\n\
- bne _08005C66\n\
- b _08005B30\n\
-_08005C66:\n\
- movs r0, 0\n\
- strb r0, [r6, 0x1C]\n\
- b _08005B30\n\
-_08005C6C:\n\
- adds r0, r6, 0\n\
- bl TextPrinterWaitWithDownArrow\n\
- lsls r0, 16\n\
- cmp r0, 0\n\
- bne _08005C7A\n\
- b _08005B30\n\
-_08005C7A:\n\
- ldrb r0, [r6, 0x4]\n\
- ldrb r2, [r6, 0xD]\n\
- lsls r2, 28\n\
- lsrs r1, r2, 4\n\
- orrs r1, r2\n\
- lsrs r1, 24\n\
- bl FillWindowPixelBuffer\n\
- ldrb r0, [r6, 0x6]\n\
- movs r1, 0\n\
- strb r0, [r6, 0x8]\n\
- ldrb r0, [r6, 0x7]\n\
- strb r0, [r6, 0x9]\n\
- strb r1, [r6, 0x1C]\n\
- b _08005B30\n\
-_08005C98:\n\
- adds r0, r6, 0\n\
- bl TextPrinterWaitWithDownArrow\n\
- lsls r0, 16\n\
- cmp r0, 0\n\
- bne _08005CA6\n\
- b _08005B30\n\
-_08005CA6:\n\
- adds r0, r6, 0\n\
- bl TextPrinterClearDownArrow\n\
- ldrb r1, [r6, 0x5]\n\
- ldr r0, _08005CCC @ =gFonts\n\
- ldr r2, [r0]\n\
- lsls r0, r1, 1\n\
- adds r0, r1\n\
- lsls r0, 2\n\
- adds r0, r2\n\
- ldrb r1, [r6, 0xB]\n\
- ldrb r0, [r0, 0x5]\n\
- adds r1, r0\n\
- strb r1, [r6, 0x1F]\n\
- ldrb r0, [r6, 0x6]\n\
- strb r0, [r6, 0x8]\n\
- movs r0, 0x4\n\
- strb r0, [r6, 0x1C]\n\
- b _08005B30\n\
- .align 2, 0\n\
-_08005CCC: .4byte gFonts\n\
-_08005CD0:\n\
- ldrb r2, [r6, 0x1F]\n\
- cmp r2, 0\n\
- beq _08005D40\n\
- ldr r4, _08005D04 @ =gWindowVerticalScrollSpeeds\n\
- ldr r5, _08005D08 @ =gSaveBlock2Ptr\n\
- ldr r0, [r5]\n\
- ldrb r0, [r0, 0x14]\n\
- lsls r1, r0, 29\n\
- lsrs r0, r1, 29\n\
- adds r0, r4\n\
- ldrb r0, [r0]\n\
- cmp r2, r0\n\
- bcs _08005D0C\n\
- ldrb r0, [r6, 0x4]\n\
- ldrb r1, [r6, 0xD]\n\
- lsls r1, 28\n\
- lsrs r3, r1, 4\n\
- orrs r3, r1\n\
- lsrs r3, 24\n\
- movs r1, 0\n\
- bl ScrollWindow\n\
- movs r0, 0\n\
- strb r0, [r6, 0x1F]\n\
- b _08005D36\n\
- .align 2, 0\n\
-_08005D04: .4byte gWindowVerticalScrollSpeeds\n\
-_08005D08: .4byte gSaveBlock2Ptr\n\
-_08005D0C:\n\
- ldrb r0, [r6, 0x4]\n\
- lsrs r1, 29\n\
- adds r1, r4\n\
- ldrb r2, [r1]\n\
- ldrb r1, [r6, 0xD]\n\
- lsls r1, 28\n\
- lsrs r3, r1, 4\n\
- orrs r3, r1\n\
- lsrs r3, 24\n\
- movs r1, 0\n\
- bl ScrollWindow\n\
- ldr r0, [r5]\n\
- ldrb r0, [r0, 0x14]\n\
- lsls r0, 29\n\
- lsrs r0, 29\n\
- adds r0, r4\n\
- ldrb r1, [r6, 0x1F]\n\
- ldrb r0, [r0]\n\
- subs r1, r0\n\
- strb r1, [r6, 0x1F]\n\
-_08005D36:\n\
- ldrb r0, [r6, 0x4]\n\
- movs r1, 0x2\n\
- bl CopyWindowToVram\n\
- b _08005B30\n\
-_08005D40:\n\
- strb r2, [r6, 0x1C]\n\
- b _08005B30\n\
-_08005D44:\n\
- bl IsSEPlaying\n\
- lsls r0, 24\n\
- lsrs r0, 24\n\
- cmp r0, 0\n\
- beq _08005D52\n\
- b _08005B30\n\
-_08005D52:\n\
- strb r0, [r6, 0x1C]\n\
- b _08005B30\n\
-_08005D56:\n\
- ldrb r0, [r6, 0x1E]\n\
- adds r1, r0, 0\n\
- cmp r1, 0\n\
- beq _08005D64\n\
- subs r0, 0x1\n\
- strb r0, [r6, 0x1E]\n\
- b _08005B30\n\
-_08005D64:\n\
- strb r1, [r6, 0x1C]\n\
- b _08005B30\n\
-_08005D68:\n\
- movs r0, 0x1\n\
-_08005D6A:\n\
- pop {r4-r6}\n\
- pop {r1}\n\
- bx r1\n\
- .syntax divided");
+ struct TextPrinterSubStruct *subStruct = &textPrinter->subUnion.sub;
+ u16 currChar;
+ s32 width;
+ s32 widthHelper;
+
+ switch (textPrinter->state)
+ {
+ case 0:
+ if (JOY_HELD(A_BUTTON | B_BUTTON) && subStruct->hasPrintBeenSpedUp)
+ textPrinter->delayCounter = 0;
+
+ if (textPrinter->delayCounter && textPrinter->textSpeed)
+ {
+ textPrinter->delayCounter--;
+ if (gTextFlags.canABSpeedUpPrint && JOY_NEW(A_BUTTON | B_BUTTON))
+ {
+ subStruct->hasPrintBeenSpedUp = TRUE;
+ textPrinter->delayCounter = 0;
+ }
+ return 3;
+ }
+
+ if (gTextFlags.autoScroll)
+ textPrinter->delayCounter = 1;
+ else
+ textPrinter->delayCounter = textPrinter->textSpeed;
+
+ currChar = *textPrinter->printerTemplate.currentChar;
+ textPrinter->printerTemplate.currentChar++;
+
+ switch (currChar)
+ {
+ case CHAR_NEWLINE:
+ textPrinter->printerTemplate.currentX = textPrinter->printerTemplate.x;
+ textPrinter->printerTemplate.currentY += gFonts[textPrinter->printerTemplate.fontId].maxLetterHeight + textPrinter->printerTemplate.lineSpacing;
+ return 2;
+ case PLACEHOLDER_BEGIN:
+ textPrinter->printerTemplate.currentChar++;
+ return 2;
+ case EXT_CTRL_CODE_BEGIN:
+ currChar = *textPrinter->printerTemplate.currentChar;
+ textPrinter->printerTemplate.currentChar++;
+ switch (currChar)
+ {
+ case 1:
+ textPrinter->printerTemplate.fgColor = *textPrinter->printerTemplate.currentChar;
+ textPrinter->printerTemplate.currentChar++;
+ GenerateFontHalfRowLookupTable(textPrinter->printerTemplate.fgColor, textPrinter->printerTemplate.bgColor, textPrinter->printerTemplate.shadowColor);
+ return 2;
+ case 2:
+ textPrinter->printerTemplate.bgColor = *textPrinter->printerTemplate.currentChar;
+ textPrinter->printerTemplate.currentChar++;
+ GenerateFontHalfRowLookupTable(textPrinter->printerTemplate.fgColor, textPrinter->printerTemplate.bgColor, textPrinter->printerTemplate.shadowColor);
+ return 2;
+ case 3:
+ textPrinter->printerTemplate.shadowColor = *textPrinter->printerTemplate.currentChar;
+ textPrinter->printerTemplate.currentChar++;
+ GenerateFontHalfRowLookupTable(textPrinter->printerTemplate.fgColor, textPrinter->printerTemplate.bgColor, textPrinter->printerTemplate.shadowColor);
+ return 2;
+ case 4:
+ textPrinter->printerTemplate.fgColor = *textPrinter->printerTemplate.currentChar;
+ textPrinter->printerTemplate.currentChar++;
+ textPrinter->printerTemplate.bgColor = *textPrinter->printerTemplate.currentChar;
+ textPrinter->printerTemplate.currentChar++;
+ textPrinter->printerTemplate.shadowColor = *textPrinter->printerTemplate.currentChar;
+ textPrinter->printerTemplate.currentChar++;
+ GenerateFontHalfRowLookupTable(textPrinter->printerTemplate.fgColor, textPrinter->printerTemplate.bgColor, textPrinter->printerTemplate.shadowColor);
+ return 2;
+ case 5:
+ textPrinter->printerTemplate.currentChar++;
+ return 2;
+ case 6:
+ subStruct->glyphId = *textPrinter->printerTemplate.currentChar;
+ textPrinter->printerTemplate.currentChar++;
+ return 2;
+ case EXT_CTRL_CODE_UNKNOWN_7:
+ return 2;
+ case 8:
+ textPrinter->delayCounter = *textPrinter->printerTemplate.currentChar;
+ textPrinter->printerTemplate.currentChar++;
+ textPrinter->state = 6;
+ return 2;
+ case 9:
+ textPrinter->state = 1;
+ if (gTextFlags.autoScroll)
+ subStruct->autoScrollDelay = 0;
+ return 3;
+ case 10:
+ textPrinter->state = 5;
+ return 3;
+ case 11:
+ currChar = *textPrinter->printerTemplate.currentChar;
+ textPrinter->printerTemplate.currentChar++;
+ currChar |= *textPrinter->printerTemplate.currentChar << 8;
+ textPrinter->printerTemplate.currentChar++;
+ if ((u8)(gUnknown_203ADFA - 2u) > 1)
+ PlayBGM(currChar);
+ return 2;
+ case 16:
+ currChar = *textPrinter->printerTemplate.currentChar;
+ textPrinter->printerTemplate.currentChar++;
+ currChar |= (*textPrinter->printerTemplate.currentChar << 8);
+ textPrinter->printerTemplate.currentChar++;
+ PlaySE(currChar);
+ return 2;
+ case 12:
+ textPrinter->printerTemplate.currentChar++;
+ currChar = *textPrinter->printerTemplate.currentChar;
+ break;
+ case 13:
+ textPrinter->printerTemplate.currentX = textPrinter->printerTemplate.x + *textPrinter->printerTemplate.currentChar;
+ textPrinter->printerTemplate.currentChar++;
+ return 2;
+ case 14:
+ textPrinter->printerTemplate.currentY = textPrinter->printerTemplate.y + *textPrinter->printerTemplate.currentChar;
+ textPrinter->printerTemplate.currentChar++;
+ return 2;
+ case 15:
+ FillWindowPixelBuffer(textPrinter->printerTemplate.windowId, PIXEL_FILL(textPrinter->printerTemplate.bgColor));
+ return 2;
+ case 23:
+ m4aMPlayStop(&gMPlayInfo_BGM);
+ return 2;
+ case 24:
+ m4aMPlayContinue(&gMPlayInfo_BGM);
+ return 2;
+ case EXT_CTRL_CODE_CLEAR:
+ width = *textPrinter->printerTemplate.currentChar;
+ textPrinter->printerTemplate.currentChar++;
+ if (width > 0)
+ {
+ ClearTextSpan(textPrinter, width);
+ textPrinter->printerTemplate.currentX += width;
+ return 0;
+ }
+ return 2;
+ case 18:
+ textPrinter->printerTemplate.currentX = *textPrinter->printerTemplate.currentChar + textPrinter->printerTemplate.x;
+ textPrinter->printerTemplate.currentChar++;
+ return 2;
+ case EXT_CTRL_CODE_CLEAR_TO:
+ {
+ widthHelper = *textPrinter->printerTemplate.currentChar;
+ widthHelper += textPrinter->printerTemplate.x;
+ textPrinter->printerTemplate.currentChar++;
+ width = widthHelper - textPrinter->printerTemplate.currentX;
+ if (width > 0)
+ {
+ ClearTextSpan(textPrinter, width);
+ textPrinter->printerTemplate.currentX += width;
+ return 0;
+ }
+ }
+ return 2;
+ case EXT_CTRL_CODE_MIN_LETTER_SPACING:
+ textPrinter->minLetterSpacing = *textPrinter->printerTemplate.currentChar++;
+ return 2;
+ case EXT_CTRL_CODE_JPN:
+ textPrinter->japanese = 1;
+ return 2;
+ case EXT_CTRL_CODE_ENG:
+ textPrinter->japanese = 0;
+ return 2;
+ }
+ break;
+ case CHAR_PROMPT_CLEAR:
+ textPrinter->state = 2;
+ TextPrinterInitDownArrowCounters(textPrinter);
+ return 3;
+ case CHAR_PROMPT_SCROLL:
+ textPrinter->state = 3;
+ TextPrinterInitDownArrowCounters(textPrinter);
+ return 3;
+ case CHAR_EXTRA_EMOJI:
+ currChar = *textPrinter->printerTemplate.currentChar | 0x100;
+ textPrinter->printerTemplate.currentChar++;
+ break;
+ case CHAR_KEYPAD_ICON:
+ currChar = *textPrinter->printerTemplate.currentChar++;
+ gGlyphInfo[0x80] = DrawKeypadIcon(textPrinter->printerTemplate.windowId, currChar, textPrinter->printerTemplate.currentX, textPrinter->printerTemplate.currentY);
+ textPrinter->printerTemplate.currentX += gGlyphInfo[0x80] + textPrinter->printerTemplate.letterSpacing;
+ return 0;
+ case EOS:
+ return 1;
+ }
+
+ switch (subStruct->glyphId)
+ {
+ case 0:
+ DecompressGlyphFont0(currChar, textPrinter->japanese);
+ break;
+ case 1:
+ DecompressGlyphFont1(currChar, textPrinter->japanese);
+ break;
+ case 2:
+ DecompressGlyphFont2(currChar, textPrinter->japanese);
+ break;
+ case 3:
+ DecompressGlyphFont3(currChar, textPrinter->japanese);
+ break;
+ case 4:
+ DecompressGlyphFont4(currChar, textPrinter->japanese);
+ break;
+ case 5:
+ DecompressGlyphFont5(currChar, textPrinter->japanese);
+ }
+
+ CopyGlyphToWindow(textPrinter);
+
+ if (textPrinter->minLetterSpacing)
+ {
+ textPrinter->printerTemplate.currentX += gGlyphInfo[0x80];
+ width = textPrinter->minLetterSpacing - gGlyphInfo[0x80];
+ if (width > 0)
+ {
+ ClearTextSpan(textPrinter, width);
+ textPrinter->printerTemplate.currentX += width;
+ }
+ }
+ else
+ {
+ if (textPrinter->japanese)
+ textPrinter->printerTemplate.currentX += (gGlyphInfo[0x80] + textPrinter->printerTemplate.letterSpacing);
+ else
+ textPrinter->printerTemplate.currentX += gGlyphInfo[0x80];
+ }
+ return 0;
+ case 1:
+ if (TextPrinterWait(textPrinter))
+ textPrinter->state = 0;
+ return 3;
+ case 2:
+ if (TextPrinterWaitWithDownArrow(textPrinter))
+ {
+ FillWindowPixelBuffer(textPrinter->printerTemplate.windowId, PIXEL_FILL(textPrinter->printerTemplate.bgColor));
+ textPrinter->printerTemplate.currentX = textPrinter->printerTemplate.x;
+ textPrinter->printerTemplate.currentY = textPrinter->printerTemplate.y;
+ textPrinter->state = 0;
+ }
+ return 3;
+ case 3:
+ if (TextPrinterWaitWithDownArrow(textPrinter))
+ {
+ TextPrinterClearDownArrow(textPrinter);
+ textPrinter->scrollDistance = gFonts[textPrinter->printerTemplate.fontId].maxLetterHeight + textPrinter->printerTemplate.lineSpacing;
+ textPrinter->printerTemplate.currentX = textPrinter->printerTemplate.x;
+ textPrinter->state = 4;
+ }
+ return 3;
+ case 4:
+ if (textPrinter->scrollDistance)
+ {
+
+ if (textPrinter->scrollDistance < gWindowVerticalScrollSpeeds[gSaveBlock2Ptr->optionsTextSpeed])
+ {
+ ScrollWindow(textPrinter->printerTemplate.windowId, 0, textPrinter->scrollDistance, PIXEL_FILL(textPrinter->printerTemplate.bgColor));
+ textPrinter->scrollDistance = 0;
+ }
+ else
+ {
+ ScrollWindow(textPrinter->printerTemplate.windowId, 0, gWindowVerticalScrollSpeeds[gSaveBlock2Ptr->optionsTextSpeed], PIXEL_FILL(textPrinter->printerTemplate.bgColor));
+ textPrinter->scrollDistance -= gWindowVerticalScrollSpeeds[gSaveBlock2Ptr->optionsTextSpeed];
+ }
+ CopyWindowToVram(textPrinter->printerTemplate.windowId, 2);
+ }
+ else
+ {
+ textPrinter->state = 0;
+ }
+ return 3;
+ case 5:
+ if (!IsSEPlaying())
+ textPrinter->state = 0;
+ return 3;
+ case 6:
+ if (textPrinter->delayCounter != 0)
+ textPrinter->delayCounter--;
+ else
+ textPrinter->state = 0;
+ return 3;
+ }
+
+ return 1;
}
s32 GetStringWidthFixedWidthFont(const u8 *str, u8 fontId, u8 letterSpacing)
@@ -1484,7 +1002,7 @@ s32 (*GetFontWidthFunc(u8 glyphId))(u16 _glyphId, bool32 _isJapanese)
for (i = 0; i < 7; ++i)
{
- if (glyphId == gGlyphWidthFuncs[i].font_id)
+ if (glyphId == gGlyphWidthFuncs[i].fontId)
return *gGlyphWidthFuncs[i].func;
}
@@ -1498,7 +1016,7 @@ s32 GetStringWidth(u8 fontId, const u8 *str, s16 letterSpacing)
s32 (*func)(u16 glyphId, bool32 isJapanese);
int localLetterSpacing;
register u32 lineWidth asm("r5");
- u8 *bufferPointer;
+ const u8 *bufferPointer;
int glyphWidth;
u32 width;
@@ -1516,7 +1034,7 @@ s32 GetStringWidth(u8 fontId, const u8 *str, s16 letterSpacing)
width = 0;
lineWidth = 0;
- bufferPointer = 0;
+ bufferPointer = NULL;
while (*str != 0xFF)
{
@@ -1559,7 +1077,7 @@ s32 GetStringWidth(u8 fontId, const u8 *str, s16 letterSpacing)
}
lineWidth += glyphWidth;
}
- bufferPointer = 0;
+ bufferPointer = NULL;
break;
case 0xFC:
switch (*++str)
@@ -1813,7 +1331,7 @@ u8 DrawKeypadIcon(u8 windowId, u8 keypadIconId, u16 x, u16 y)
{
BlitBitmapRectToWindow(
windowId,
- gKeypadIconTiles + (gKeypadIcons[keypadIconId].tile_offset * 0x20),
+ gKeypadIconTiles + (gKeypadIcons[keypadIconId].tileOffset * 0x20),
0,
0,
0x80,
@@ -1827,7 +1345,7 @@ u8 DrawKeypadIcon(u8 windowId, u8 keypadIconId, u16 x, u16 y)
u8 GetKeypadIconTileOffset(u8 keypadIconId)
{
- return gKeypadIcons[keypadIconId].tile_offset;
+ return gKeypadIcons[keypadIconId].tileOffset;
}
u8 GetKeypadIconWidth(u8 keypadIconId)
@@ -1976,7 +1494,7 @@ s32 GetGlyphWidthFont2(u16 glyphId, bool32 isJapanese)
}
}
-void DecompressGlyphFont3(u16 glyphId, bool32 isJapanese)
+static void DecompressGlyphFont3(u16 glyphId, bool32 isJapanese)
{
const u16* glyphs;
int i;
@@ -2019,7 +1537,7 @@ s32 GetGlyphWidthFont3(u16 glyphId, bool32 isJapanese)
return gFont2LatinGlyphWidths[glyphId];
}
-void DecompressGlyphFont4(u16 glyphId, bool32 isJapanese)
+static void DecompressGlyphFont4(u16 glyphId, bool32 isJapanese)
{
const u16* glyphs;
int i;
diff --git a/src/text_printer.c b/src/text_printer.c
index 6c124b448..28377b55a 100644
--- a/src/text_printer.c
+++ b/src/text_printer.c
@@ -45,27 +45,27 @@ void DeactivateAllTextPrinters (void)
{
int printer;
for (printer = 0; printer < NUM_TEXT_PRINTERS; ++printer)
- sTextPrinters[printer].sub_union.sub.active = 0;
+ sTextPrinters[printer].active = 0;
}
u16 AddTextPrinterParameterized(u8 windowId, u8 fontId, const u8 *str, u8 x, u8 y, u8 speed, void (*callback)(struct TextPrinterTemplate *, u16))
{
- struct TextPrinterTemplate subPrinter;
-
- subPrinter.currentChar = str;
- subPrinter.windowId = windowId;
- subPrinter.fontId = fontId;
- subPrinter.x = x;
- subPrinter.y = y;
- subPrinter.currentX = x;
- subPrinter.currentY = y;
- subPrinter.letterSpacing = gFonts[fontId].letterSpacing;
- subPrinter.lineSpacing = gFonts[fontId].lineSpacing;
- subPrinter.unk = gFonts[fontId].unk;
- subPrinter.fgColor = gFonts[fontId].fgColor;
- subPrinter.bgColor = gFonts[fontId].bgColor;
- subPrinter.shadowColor = gFonts[fontId].shadowColor;
- return AddTextPrinter(&subPrinter, speed, callback);
+ struct TextPrinterTemplate printerTemplate;
+
+ printerTemplate.currentChar = str;
+ printerTemplate.windowId = windowId;
+ printerTemplate.fontId = fontId;
+ printerTemplate.x = x;
+ printerTemplate.y = y;
+ printerTemplate.currentX = x;
+ printerTemplate.currentY = y;
+ printerTemplate.letterSpacing = gFonts[fontId].letterSpacing;
+ printerTemplate.lineSpacing = gFonts[fontId].lineSpacing;
+ printerTemplate.unk = gFonts[fontId].unk;
+ printerTemplate.fgColor = gFonts[fontId].fgColor;
+ printerTemplate.bgColor = gFonts[fontId].bgColor;
+ printerTemplate.shadowColor = gFonts[fontId].shadowColor;
+ return AddTextPrinter(&printerTemplate, speed, callback);
}
bool16 AddTextPrinter(struct TextPrinterTemplate *textSubPrinter, u8 speed, void (*callback)(struct TextPrinterTemplate *, u16))
@@ -76,18 +76,18 @@ bool16 AddTextPrinter(struct TextPrinterTemplate *textSubPrinter, u8 speed, void
if (!gFonts)
return FALSE;
- sTempTextPrinter.sub_union.sub.active = 1;
+ sTempTextPrinter.active = 1;
sTempTextPrinter.state = 0;
- sTempTextPrinter.text_speed = speed;
+ sTempTextPrinter.textSpeed = speed;
sTempTextPrinter.delayCounter = 0;
sTempTextPrinter.scrollDistance = 0;
for (i = 0; i < 7; ++i)
{
- sTempTextPrinter.sub_union.sub_fields[i] = 0;
+ sTempTextPrinter.subUnion.fields[i] = 0;
}
- sTempTextPrinter.subPrinter = *textSubPrinter;
+ sTempTextPrinter.printerTemplate = *textSubPrinter;
sTempTextPrinter.callback = callback;
sTempTextPrinter.minLetterSpacing = 0;
sTempTextPrinter.japanese = 0;
@@ -95,12 +95,12 @@ bool16 AddTextPrinter(struct TextPrinterTemplate *textSubPrinter, u8 speed, void
GenerateFontHalfRowLookupTable(textSubPrinter->fgColor, textSubPrinter->bgColor, textSubPrinter->shadowColor);
if (speed != TEXT_SPEED_FF && speed != 0x0)
{
- --sTempTextPrinter.text_speed;
+ --sTempTextPrinter.textSpeed;
sTextPrinters[textSubPrinter->windowId] = sTempTextPrinter;
}
else
{
- sTempTextPrinter.text_speed = 0;
+ sTempTextPrinter.textSpeed = 0;
for (j = 0; j < 0x400; ++j)
{
if ((u32)RenderFont(&sTempTextPrinter) == 1)
@@ -108,8 +108,8 @@ bool16 AddTextPrinter(struct TextPrinterTemplate *textSubPrinter, u8 speed, void
}
if (speed != TEXT_SPEED_FF)
- CopyWindowToVram(sTempTextPrinter.subPrinter.windowId, 2);
- sTextPrinters[textSubPrinter->windowId].sub_union.sub.active = 0;
+ CopyWindowToVram(sTempTextPrinter.printerTemplate.windowId, 2);
+ sTextPrinters[textSubPrinter->windowId].active = 0;
}
return TRUE;
}
@@ -121,18 +121,18 @@ void RunTextPrinters(void)
for (i = 0; i < 0x20; ++i)
{
- if (sTextPrinters[i].sub_union.sub.active != 0)
+ if (sTextPrinters[i].active != 0)
{
temp = RenderFont(&sTextPrinters[i]);
switch (temp) {
case 0:
- CopyWindowToVram(sTextPrinters[i].subPrinter.windowId, 2);
+ CopyWindowToVram(sTextPrinters[i].printerTemplate.windowId, 2);
case 3:
if (sTextPrinters[i].callback != 0)
- sTextPrinters[i].callback(&sTextPrinters[i].subPrinter, temp);
+ sTextPrinters[i].callback(&sTextPrinters[i].printerTemplate, temp);
break;
case 1:
- sTextPrinters[i].sub_union.sub.active = 0;
+ sTextPrinters[i].active = 0;
break;
}
}
@@ -141,7 +141,7 @@ void RunTextPrinters(void)
bool16 IsTextPrinterActive(u8 id)
{
- return sTextPrinters[id].sub_union.sub.active;
+ return sTextPrinters[id].active;
}
u32 RenderFont(struct TextPrinter *textPrinter)
@@ -149,7 +149,7 @@ u32 RenderFont(struct TextPrinter *textPrinter)
u32 ret;
while (TRUE)
{
- ret = gFonts[textPrinter->subPrinter.fontId].fontFunction(textPrinter);
+ ret = gFonts[textPrinter->printerTemplate.fontId].fontFunction(textPrinter);
if (ret != 2)
return ret;
}
diff --git a/src/tm_case.c b/src/tm_case.c
index 79e10bcfc..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"
@@ -185,7 +185,7 @@ static const u8 sText_SingleSpace[] = _(" ");
static ALIGNED(4) const u16 sPal3Override[] = {RGB(8, 8, 8), RGB(30, 16, 6)};
-static const struct TextColor sTextColors[] = {
+static const u8 sTextColors[][3] = {
{0, 1, 2},
{0, 2, 3},
{0, 3, 6},
@@ -1317,7 +1317,7 @@ static void InitWindowTemplatesAndPals(void)
static void AddTextPrinterParameterized_ColorByIndex(u8 windowId, u8 fontId, const u8 * str, u8 x, u8 y, u8 letterSpacing, u8 lineSpacing, u8 speed, u8 colorIdx)
{
- AddTextPrinterParameterized4(windowId, fontId, x, y, letterSpacing, lineSpacing, &sTextColors[colorIdx], speed, str);
+ AddTextPrinterParameterized4(windowId, fontId, x, y, letterSpacing, lineSpacing, sTextColors[colorIdx], speed, str);
}
static void TMCase_SetWindowBorder1(u8 windowId)
@@ -1339,7 +1339,7 @@ static void TMCase_PrintMessageWithFollowupTask(u8 taskId, u8 windowId, const u8
static void PrintStringTMCaseOnWindow3(void)
{
u32 distance = 72 - GetStringWidth(1, gText_TMCase, 0);
- AddTextPrinterParameterized3(3, 1, distance / 2, 1, &sTextColors[0], 0, gText_TMCase);
+ AddTextPrinterParameterized3(3, 1, distance / 2, 1, sTextColors[0], 0, gText_TMCase);
}
static void DrawMoveInfoUIMarkers(void)
diff --git a/src/trainer_pokemon_sprites.c b/src/trainer_pokemon_sprites.c
index 25a8d4515..2ef5e02f0 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);
}
@@ -278,7 +278,7 @@ u16 FreeAndDestroyMonPicSprite(u16 spriteId)
return FreeAndDestroyPicSpriteInternal(spriteId);
}
-u16 sub_810C228(u16 species, u32 otId, u32 personality, bool8 isFrontPic, u8 paletteSlot, u8 windowId)
+u16 LoadMonPicInWindow(u16 species, u32 otId, u32 personality, bool8 isFrontPic, u8 paletteSlot, u8 windowId)
{
return sub_810C0C0(species, otId, personality, isFrontPic, 0, 0, paletteSlot, windowId, FALSE);
}
diff --git a/src/trainer_tower.c b/src/trainer_tower.c
index d80be781d..17f6b9e09 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"
@@ -423,7 +423,7 @@ const struct WindowTemplate gUnknown_847A218[] = {
const u32 gUnknown_847A228 = 0x70; // unused
-const struct TextColor gUnknown_847A22C = {0, 2, 3};
+const u8 gUnknown_847A22C[3] = {0, 2, 3};
void (*const gUnknown_847A230[])(void) = {
sub_815DD44,
@@ -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;
@@ -1433,14 +1433,14 @@ void PrintTrainerTowerRecords(void)
sub_815DC8C();
FillWindowPixelRect(0, 0, 0, 0, 0xd8, 0x90);
sub_815EC0C();
- AddTextPrinterParameterized3(0, 2, 0x4a, 0, &gUnknown_847A22C, 0, gUnknown_83FE982);
+ AddTextPrinterParameterized3(0, 2, 0x4a, 0, gUnknown_847A22C, 0, gUnknown_83FE982);
for (i = 0; i < 4; i++)
{
PRINT_TOWER_TIME(sub_815EDDC(&gSaveBlock1Ptr->unkArray[i].unk4));
StringExpandPlaceholders(gStringVar4, gUnknown_83FE998);
- AddTextPrinterParameterized3(windowId, 2, 0x18, 0x24 + 0x14 * i, &gUnknown_847A22C, 0, gUnknown_83FE9C4[i]);
- AddTextPrinterParameterized3(windowId, 2, 0x60, 0x24 + 0x14 * i, &gUnknown_847A22C, 0, gStringVar4);
+ AddTextPrinterParameterized3(windowId, 2, 0x18, 0x24 + 0x14 * i, gUnknown_847A22C, 0, gUnknown_83FE9C4[i]);
+ AddTextPrinterParameterized3(windowId, 2, 0x60, 0x24 + 0x14 * i, gUnknown_847A22C, 0, gStringVar4);
}
PutWindowTilemap(windowId);
@@ -1458,7 +1458,7 @@ void sub_815EDF4(u32 * counter, u32 value)
*counter = value ^ gSaveBlock2Ptr->encryptionKey;
}
-void sub_815EE0C(void)
+void ResetTrainerTowerResults(void)
{
s32 i;
diff --git a/src/wild_pokemon_area.c b/src/wild_pokemon_area.c
new file mode 100644
index 000000000..5ea9d1610
--- /dev/null
+++ b/src/wild_pokemon_area.c
@@ -0,0 +1,308 @@
+#include "global.h"
+#include "field_specials.h"
+#include "event_data.h"
+#include "wild_encounter.h"
+#include "roamer.h"
+#include "overworld.h"
+#include "pokedex_area_markers.h"
+#include "constants/vars.h"
+#include "constants/region_map.h"
+#include "constants/species.h"
+
+struct SeviiDexArea
+{
+ const u16 (*lut)[2];
+ s32 count;
+};
+
+struct RoamerPair
+{
+ u16 roamer;
+ u16 starter;
+};
+
+static s32 GetRoamerIndex(u16 species);
+static s32 CountRoamerNests(u16 species, struct Subsprite * subsprites);
+static bool32 PokemonInAnyEncounterTableInMap(const struct WildPokemonHeader * data, s32 species);
+static bool32 PokemonInEncounterTable(const struct WildPokemonInfo * pokemon, s32 species, s32 count);
+static u16 GetMapSecIdFromWildMonHeader(const struct WildPokemonHeader * header);
+static bool32 TryGetMapSecPokedexAreaEntry(u16 mapSecId, const u16 (*lut)[2], s32 count, s32 * lutIdx_p, u16 * tableIdx_p);
+
+static const u16 sDexAreas_Kanto[][2] = {
+ { MAPSEC_PALLET_TOWN, 1 },
+ { MAPSEC_VIRIDIAN_CITY, 2 },
+ { MAPSEC_PEWTER_CITY, 3 },
+ { MAPSEC_CERULEAN_CITY, 4 },
+ { MAPSEC_LAVENDER_TOWN, 5 },
+ { MAPSEC_VERMILION_CITY, 6 },
+ { MAPSEC_CELADON_CITY, 7 },
+ { MAPSEC_FUCHSIA_CITY, 8 },
+ { MAPSEC_CINNABAR_ISLAND, 9 },
+ { MAPSEC_INDIGO_PLATEAU, 10 },
+ { MAPSEC_SAFFRON_CITY, 11 },
+ { MAPSEC_ROUTE_4_FLYDUP, 15 },
+ { MAPSEC_ROUTE_10_FLYDUP, 21 },
+ { MAPSEC_ROUTE_1, 12 },
+ { MAPSEC_ROUTE_2, 13 },
+ { MAPSEC_ROUTE_3, 14 },
+ { MAPSEC_ROUTE_4, 15 },
+ { MAPSEC_ROUTE_5, 16 },
+ { MAPSEC_ROUTE_6, 17 },
+ { MAPSEC_ROUTE_7, 18 },
+ { MAPSEC_ROUTE_8, 19 },
+ { MAPSEC_ROUTE_9, 20 },
+ { MAPSEC_ROUTE_10, 21 },
+ { MAPSEC_ROUTE_11, 22 },
+ { MAPSEC_ROUTE_12, 23 },
+ { MAPSEC_ROUTE_13, 24 },
+ { MAPSEC_ROUTE_14, 25 },
+ { MAPSEC_ROUTE_15, 26 },
+ { MAPSEC_ROUTE_16, 27 },
+ { MAPSEC_ROUTE_17, 28 },
+ { MAPSEC_ROUTE_18, 29 },
+ { MAPSEC_ROUTE_19, 30 },
+ { MAPSEC_ROUTE_20, 31 },
+ { MAPSEC_ROUTE_21, 32 },
+ { MAPSEC_ROUTE_22, 33 },
+ { MAPSEC_ROUTE_23, 34 },
+ { MAPSEC_ROUTE_24, 35 },
+ { MAPSEC_ROUTE_25, 36 },
+ { MAPSEC_VIRIDIAN_FOREST, 37 },
+ { MAPSEC_MT_MOON, 39 },
+ { MAPSEC_S_S_ANNE, 6 },
+ { MAPSEC_UNDERGROUND_PATH, 11 },
+ { MAPSEC_UNDERGROUND_PATH_2, 11 },
+ { MAPSEC_DIGLETTS_CAVE, 38 },
+ { MAPSEC_KANTO_VICTORY_ROAD, 47 },
+ { MAPSEC_ROCKET_HIDEOUT, 7 },
+ { MAPSEC_SILPH_CO, 11 },
+ { MAPSEC_POKEMON_MANSION, 46 },
+ { MAPSEC_KANTO_SAFARI_ZONE, 44 },
+ { MAPSEC_POKEMON_LEAGUE, 47 },
+ { MAPSEC_ROCK_TUNNEL, 41 },
+ { MAPSEC_SEAFOAM_ISLANDS, 45 },
+ { MAPSEC_POKEMON_TOWER, 43 },
+ { MAPSEC_CERULEAN_CAVE, 40 },
+ { MAPSEC_POWER_PLANT, 42 }
+};
+
+static const u16 sDexAreas_Sevii1[][2] = {
+ { MAPSEC_KINDLE_ROAD, 55 },
+ { MAPSEC_TREASURE_BEACH, 56 },
+ { MAPSEC_ONE_ISLAND, 48 },
+ { MAPSEC_MT_EMBER, 72 }
+};
+
+static const u16 sDexAreas_Sevii2[][2] = {
+ { MAPSEC_CAPE_BRINK, 57 },
+ { MAPSEC_TWO_ISLAND, 49 }
+};
+
+static const u16 sDexAreas_Sevii3[][2] = {
+ { MAPSEC_BOND_BRIDGE, 58 },
+ { MAPSEC_THREE_ISLE_PORT, 59 },
+ { MAPSEC_THREE_ISLAND, 50 },
+ { MAPSEC_BERRY_FOREST, 73 },
+ { MAPSEC_THREE_ISLE_PATH, 59 }
+};
+
+static const u16 sDexAreas_Sevii4[][2] = {
+ { MAPSEC_FOUR_ISLAND, 51 },
+ { MAPSEC_ICEFALL_CAVE, 74 }
+};
+
+static const u16 sDexAreas_Sevii5[][2] = {
+ { MAPSEC_RESORT_GORGEOUS, 60 },
+ { MAPSEC_WATER_LABYRINTH, 61 },
+ { MAPSEC_FIVE_ISLE_MEADOW, 62 },
+ { MAPSEC_MEMORIAL_PILLAR, 63 },
+ { MAPSEC_FIVE_ISLAND, 52 },
+ { MAPSEC_ROCKET_WAREHOUSE, 62 },
+ { MAPSEC_LOST_CAVE, 75 }
+};
+
+static const u16 sDexAreas_Sevii6[][2] = {
+ { MAPSEC_OUTCAST_ISLAND, 64 },
+ { MAPSEC_GREEN_PATH, 65 },
+ { MAPSEC_WATER_PATH, 66 },
+ { MAPSEC_RUIN_VALLEY, 67 },
+ { MAPSEC_DOTTED_HOLE, 78 },
+ { MAPSEC_PATTERN_BUSH, 77 },
+ { MAPSEC_ALTERING_CAVE, 76 }
+};
+
+static const u16 sDexAreas_Sevii7[][2] = {
+ { MAPSEC_TRAINER_TOWER, 68 },
+ { MAPSEC_CANYON_ENTRANCE, 69 },
+ { MAPSEC_SEVAULT_CANYON, 70 },
+ { MAPSEC_TANOBY_RUINS, 71 },
+ { MAPSEC_MONEAN_CHAMBER, 79 },
+ { MAPSEC_LIPTOO_CHAMBER, 79 },
+ { MAPSEC_WEEPTH_CHAMBER, 79 },
+ { MAPSEC_DILFORD_CHAMBER, 79 },
+ { MAPSEC_SCUFIB_CHAMBER, 79 },
+ { MAPSEC_RIXY_CHAMBER, 79 },
+ { MAPSEC_VIAPOIS_CHAMBER, 79 }
+};
+
+static const struct SeviiDexArea sSeviiDexAreas[] = {
+ { sDexAreas_Sevii1, 4 },
+ { sDexAreas_Sevii2, 2 },
+ { sDexAreas_Sevii3, 5 },
+ { sDexAreas_Sevii4, 2 },
+ { sDexAreas_Sevii5, 7 },
+ { sDexAreas_Sevii6, 7 },
+ { sDexAreas_Sevii7, 11 }
+};
+
+static const struct RoamerPair sRoamerPairs[] = {
+ { SPECIES_ENTEI, SPECIES_BULBASAUR },
+ { SPECIES_SUICUNE, SPECIES_CHARMANDER },
+ { SPECIES_RAIKOU, SPECIES_SQUIRTLE }
+};
+
+s32 BuildPokedexAreaSubspriteBuffer(u16 species, struct Subsprite * subsprites)
+{
+ s32 areaCount;
+ s32 j;
+ s32 mapSecId;
+ u16 dexAreaSubspriteIdx;
+ s32 dexAreaEntryLUTidx;
+ s32 seviiAreas;
+ s32 alteringCaveCount;
+ s32 alteringCaveNum;
+ s32 i;
+
+ if (GetRoamerIndex(species) >= SPECIES_NONE)
+ {
+ return CountRoamerNests(species, subsprites);
+ }
+
+ seviiAreas = GetUnlockedSeviiAreas();
+ alteringCaveCount = 0;
+ alteringCaveNum = VarGet(VAR_0x4024);
+ if (alteringCaveNum > 8)
+ alteringCaveNum = 0;
+ for (i = 0, areaCount = 0; gWildMonHeaders[i].mapGroup != 0xFF; i++)
+ {
+ mapSecId = GetMapSecIdFromWildMonHeader(&gWildMonHeaders[i]);
+ if (mapSecId == MAPSEC_ALTERING_CAVE)
+ {
+ alteringCaveCount++;
+ if (alteringCaveNum != alteringCaveCount - 1)
+ continue;
+ }
+ if (PokemonInAnyEncounterTableInMap(&gWildMonHeaders[i], species))
+ {
+ dexAreaEntryLUTidx = 0;
+ while (TryGetMapSecPokedexAreaEntry(mapSecId, sDexAreas_Kanto, 55, &dexAreaEntryLUTidx, &dexAreaSubspriteIdx))
+ {
+ if (dexAreaSubspriteIdx != 0)
+ {
+ SetAreaSubsprite(areaCount++, dexAreaSubspriteIdx, subsprites);
+ }
+ }
+ for (j = 0; j < NELEMS(sSeviiDexAreas); j++)
+ {
+ if ((seviiAreas >> j) & 1)
+ {
+ dexAreaEntryLUTidx = 0;
+ while (TryGetMapSecPokedexAreaEntry(mapSecId, sSeviiDexAreas[j].lut, sSeviiDexAreas[j].count, &dexAreaEntryLUTidx, &dexAreaSubspriteIdx))
+ {
+ if (dexAreaSubspriteIdx != 0)
+ {
+ SetAreaSubsprite(areaCount++, dexAreaSubspriteIdx, subsprites);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return areaCount;
+}
+
+static s32 GetRoamerIndex(u16 species)
+{
+ s32 i;
+ for (i = 0; i < NELEMS(sRoamerPairs); i++)
+ {
+ if (sRoamerPairs[i].roamer == species)
+ return i;
+ }
+
+ return -1;
+}
+
+static s32 CountRoamerNests(u16 species, struct Subsprite * subsprites)
+{
+ u16 roamerLocation;
+ s32 roamerIdx;
+ u16 dexAreaSubspriteIdx;
+ s32 dexAreaEntryLUTidx;
+
+ roamerIdx = GetRoamerIndex(species);
+ if (roamerIdx < 0)
+ return 0;
+ if (sRoamerPairs[roamerIdx].starter != ScrSpecial_GetStarter())
+ return 0;
+ roamerLocation = GetRoamerLocationMapSectionId();
+ dexAreaEntryLUTidx = 0;
+ if (TryGetMapSecPokedexAreaEntry(roamerLocation, sDexAreas_Kanto, 55, &dexAreaEntryLUTidx, &dexAreaSubspriteIdx))
+ {
+ if (dexAreaSubspriteIdx != 0)
+ {
+ SetAreaSubsprite(0, dexAreaSubspriteIdx, subsprites);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static bool32 PokemonInAnyEncounterTableInMap(const struct WildPokemonHeader * data, s32 species)
+{
+ if (PokemonInEncounterTable(data->landMonsInfo, species, 12))
+ return TRUE;
+ if (PokemonInEncounterTable(data->waterMonsInfo, species, 5))
+ return TRUE;
+ if (PokemonInEncounterTable(data->fishingMonsInfo, species, 12))
+ return TRUE;
+ if (PokemonInEncounterTable(data->rockSmashMonsInfo, species, 5))
+ return TRUE;
+
+ return FALSE;
+}
+
+static bool32 PokemonInEncounterTable(const struct WildPokemonInfo * info, s32 species, s32 count)
+{
+ s32 i;
+ if (info != NULL)
+ {
+ for (i = 0; i < count; i++)
+ {
+ if (info->wildPokemon[i].species == species)
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static u16 GetMapSecIdFromWildMonHeader(const struct WildPokemonHeader * header)
+{
+ return get_mapheader_by_bank_and_number(header->mapGroup, header->mapNum)->regionMapSectionId;
+}
+
+static bool32 TryGetMapSecPokedexAreaEntry(u16 mapSecId, const u16 (*lut)[2], s32 count, s32 * lutIdx_p, u16 * tableIdx_p)
+{
+ s32 i;
+ for (i = *lutIdx_p; i < count; i++)
+ {
+ if (lut[i][0] == mapSecId)
+ {
+ *tableIdx_p = lut[i][1];
+ *lutIdx_p = i + 1;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
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);
diff --git a/src/wireless_communication_status_screen.c b/src/wireless_communication_status_screen.c
index cc80d37b6..1c355c3ba 100644
--- a/src/wireless_communication_status_screen.c
+++ b/src/wireless_communication_status_screen.c
@@ -329,37 +329,37 @@ void sub_814F46C(u8 taskId)
void sub_814F65C(u8 windowId, u8 fontId, const u8 * str, u8 x, u8 y, u8 palIdx)
{
- struct TextColor textColor;
+ u8 textColor[3];
switch (palIdx)
{
case 0:
- textColor.fgColor = 0;
- textColor.bgColor = 2;
- textColor.shadowColor = 3;
+ textColor[0] = 0;
+ textColor[1] = 2;
+ textColor[2] = 3;
break;
case 1:
- textColor.fgColor = 0;
- textColor.bgColor = 1;
- textColor.shadowColor = 3;
+ textColor[0] = 0;
+ textColor[1] = 1;
+ textColor[2] = 3;
break;
case 2:
- textColor.fgColor = 0;
- textColor.bgColor = 4;
- textColor.shadowColor = 5;
+ textColor[0] = 0;
+ textColor[1] = 4;
+ textColor[2] = 5;
break;
case 3:
- textColor.fgColor = 0;
- textColor.bgColor = 7;
- textColor.shadowColor = 6;
+ textColor[0] = 0;
+ textColor[1] = 7;
+ textColor[2] = 6;
break;
case 4:
- textColor.fgColor = 0;
- textColor.bgColor = 1;
- textColor.shadowColor = 2;
+ textColor[0] = 0;
+ textColor[1] = 1;
+ textColor[2] = 2;
break;
// default: UB
}
- AddTextPrinterParameterized4(windowId, fontId,x, y, fontId == 0 ? 0 : 1, 0, &textColor, -1, str);
+ AddTextPrinterParameterized4(windowId, fontId,x, y, fontId == 0 ? 0 : 1, 0, textColor, -1, str);
}
u32 sub_814F714(struct UnkStruct_x20 * unk20, u32 * arg1)