summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/battle_ai_script_commands.c141
-rw-r--r--src/battle_anim_mon_movement.c366
-rw-r--r--src/battle_controller_opponent.c1788
-rw-r--r--src/battle_controller_safari.c677
-rw-r--r--src/battle_controllers.c1172
-rw-r--r--src/battle_gfx_sfx_util.c1055
-rw-r--r--src/battle_util.c3199
-rw-r--r--src/battle_util2.c102
-rw-r--r--src/mevent.c8
-rw-r--r--src/mevent_server_helpers.c6
-rw-r--r--src/pokemon.c73
-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/trainer_pokemon_sprites.c4
-rw-r--r--src/trainer_tower.c8
16 files changed, 8683 insertions, 248 deletions
diff --git a/src/battle_ai_script_commands.c b/src/battle_ai_script_commands.c
index 1650f1056..1be10d228 100644
--- a/src/battle_ai_script_commands.c
+++ b/src/battle_ai_script_commands.c
@@ -34,7 +34,6 @@ enum
extern const u8 *gAIScriptPtr;
extern u8 *BattleAIs[];
-extern u16 gLastUsedMove[];
static void BattleAICmd_if_random_less_than(void);
static void BattleAICmd_if_random_greater_than(void);
@@ -249,10 +248,10 @@ static const u16 sDiscouragedPowerfulMoveEffects[] =
};
// TODO: move these
-extern u8 sBattler_AI;
+extern u8 gBattlerAttacker;
extern const u32 gBitTable[]; // util.h
extern u32 gStatuses3[]; // battle_2.h
-extern u16 gSideAffecting[2];
+extern u16 gSideStatuses[2];
extern const struct BattleMove gBattleMoves[];
extern u16 gDynamicBasePower;
extern u8 gMoveResultFlags;
@@ -318,7 +317,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 +330,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.
@@ -425,13 +424,13 @@ void BattleAI_DoAIProcessing(void)
break;
case AIState_SettingUp:
gAIScriptPtr = BattleAIs[AI_THINKING_STRUCT->aiLogicId]; // set the AI ptr.
- if (gBattleMons[sBattler_AI].pp[AI_THINKING_STRUCT->movesetIndex] == 0)
+ 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;
@@ -466,7 +465,7 @@ 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;
}
}
@@ -480,13 +479,13 @@ void sub_80C71A8(u8 a)
BATTLE_HISTORY->usedMoves[a / 2][i] = 0;
}
-void sub_80C71D0(u8 a, u8 b)
+void RecordAbilityBattle(u8 a, u8 b)
{
if (GetBattlerSide(a) == 0)
BATTLE_HISTORY->abilities[GetBattlerPosition(a) & 1] = b;
}
-void sub_80C7208(u8 a, u8 b)
+void RecordItemEffectBattle(u8 a, u8 b)
{
if (GetBattlerSide(a) == 0)
BATTLE_HISTORY->itemEffects[GetBattlerPosition(a) & 1] = b;
@@ -544,7 +543,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 +558,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 +573,7 @@ static void BattleAICmd_if_hp_equal(void)
u16 index;
if (gAIScriptPtr[1] == USER)
- index = sBattler_AI;
+ index = gBattlerAttacker;
else
index = gBattlerTarget;
@@ -589,7 +588,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 +604,7 @@ static void BattleAICmd_if_status(void)
u32 arg;
if (gAIScriptPtr[1] == USER)
- index = sBattler_AI;
+ index = gBattlerAttacker;
else
index = gBattlerTarget;
@@ -623,7 +622,7 @@ static void BattleAICmd_if_not_status(void)
u32 arg;
if (gAIScriptPtr[1] == USER)
- index = sBattler_AI;
+ index = gBattlerAttacker;
else
index = gBattlerTarget;
@@ -641,7 +640,7 @@ static void BattleAICmd_if_status2(void)
u32 arg;
if (gAIScriptPtr[1] == USER)
- index = sBattler_AI;
+ index = gBattlerAttacker;
else
index = gBattlerTarget;
@@ -659,7 +658,7 @@ static void BattleAICmd_if_not_status2(void)
u32 arg;
if (gAIScriptPtr[1] == USER)
- index = sBattler_AI;
+ index = gBattlerAttacker;
else
index = gBattlerTarget;
@@ -677,7 +676,7 @@ static void BattleAICmd_if_status3(void)
u32 arg;
if (gAIScriptPtr[1] == USER)
- index = sBattler_AI;
+ index = gBattlerAttacker;
else
index = gBattlerTarget;
@@ -695,7 +694,7 @@ static void BattleAICmd_if_not_status3(void)
u32 arg;
if (gAIScriptPtr[1] == USER)
- index = sBattler_AI;
+ index = gBattlerAttacker;
else
index = gBattlerTarget;
@@ -713,14 +712,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 +731,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 +906,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 +922,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 +943,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 +991,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 +1034,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 +1059,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 +1067,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 +1091,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 +1144,7 @@ static void BattleAICmd_get_ability(void)
u8 index;
if (gAIScriptPtr[1] == USER)
- index = sBattler_AI;
+ index = gBattlerAttacker;
else
index = gBattlerTarget;
@@ -1218,11 +1217,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 +1256,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 +1372,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 +1401,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 +1416,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 +1431,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 +1446,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 +1470,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 +1499,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 +1523,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 +1557,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 +1590,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 +1602,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 +1619,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,7 +1643,7 @@ static void BattleAICmd_if_last_move_did_damage(void)
u8 index;
if (gAIScriptPtr[1] == USER)
- index = sBattler_AI;
+ index = gBattlerAttacker;
else
index = gBattlerTarget;
@@ -1739,7 +1738,7 @@ static void BattleAICmd_get_hold_effect(void)
u16 side;
if (gAIScriptPtr[1] == USER)
- index = sBattler_AI;
+ index = gBattlerAttacker;
else
index = gBattlerTarget;
@@ -1759,7 +1758,7 @@ static void BattleAICmd_get_gender(void)
u8 index;
if (gAIScriptPtr[1] == USER)
- index = sBattler_AI;
+ index = gBattlerAttacker;
else
index = gBattlerTarget;
@@ -1773,7 +1772,7 @@ static void BattleAICmd_is_first_turn(void)
u8 index;
if (gAIScriptPtr[1] == USER)
- index = sBattler_AI;
+ index = gBattlerAttacker;
else
index = gBattlerTarget;
@@ -1787,7 +1786,7 @@ static void BattleAICmd_get_stockpile_count(void)
u8 index;
if (gAIScriptPtr[1] == USER)
- index = sBattler_AI;
+ index = gBattlerAttacker;
else
index = gBattlerTarget;
@@ -1808,7 +1807,7 @@ static void BattleAICmd_get_used_held_item(void)
u8 battlerId;
if (gAIScriptPtr[1] == AI_USER)
- battlerId = sBattler_AI;
+ battlerId = gBattlerAttacker;
else
battlerId = gBattlerTarget;
@@ -1848,7 +1847,7 @@ static void BattleAICmd_get_protect_count(void)
u8 index;
if (gAIScriptPtr[1] == USER)
- index = sBattler_AI;
+ index = gBattlerAttacker;
else
index = gBattlerTarget;
@@ -1903,7 +1902,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 +1910,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 +1918,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 +1930,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,7 +1938,7 @@ 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;
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_opponent.c b/src/battle_controller_opponent.c
new file mode 100644
index 000000000..25c6ffcb9
--- /dev/null
+++ b/src/battle_controller_opponent.c
@@ -0,0 +1,1788 @@
+#include "global.h"
+#include "bg.h"
+#include "data2.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] < ARRAY_COUNT(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_GetAIActionToUse();
+
+ 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 < 6; ++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_safari.c b/src/battle_controller_safari.c
new file mode 100644
index 000000000..2246f17bb
--- /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 "data2.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..509a00824
--- /dev/null
+++ b/src/battle_controllers.c
@@ -0,0 +1,1172 @@
+#include "global.h"
+#include "battle.h"
+#include "battle_2.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] = gUnknown_2023D72; // 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..3789b04c2
--- /dev/null
+++ b/src/battle_gfx_sfx_util.c
@@ -0,0 +1,1055 @@
+#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 "data2.h"
+#include "util.h"
+#include "party_menu.h"
+#include "battle.h"
+#include "battle_2.h"
+#include "battle_controllers.h"
+#include "battle_ai_script_commands.h"
+#include "battle_anim.h"
+#include "battle_interface.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(gUnknown_8239FD4[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_util.c b/src/battle_util.c
new file mode 100644
index 000000000..cf91b122f
--- /dev/null
+++ b/src/battle_util.c
@@ -0,0 +1,3199 @@
+#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 = 0;
+ gDisableStructs[gActiveBattler].disableTimer = 0;
+ }
+ else if (--gDisableStructs[gActiveBattler].disableTimer == 0) // disable ends
+ {
+ gDisableStructs[gActiveBattler].disabledMove = 0;
+ 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 = 0;
+ gDisableStructs[gActiveBattler].encoreTimer = 0;
+ }
+ else if (--gDisableStructs[gActiveBattler].encoreTimer == 0
+ || gBattleMons[gActiveBattler].pp[gDisableStructs[gActiveBattler].encoredMovePos] == 0)
+ {
+ gDisableStructs[gActiveBattler].encoredMove = 0;
+ 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 != 0)
+ {
+ 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 = GetBankMultiplayerId(battler);
+ if (GetBattlerSide(battler) == B_SIDE_PLAYER)
+ party = gPlayerParty;
+ else
+ party = gEnemyParty;
+ flankId = GetLinkTrainerFlankId(playerId);
+ for (i = flankId * 3; i < flankId * 3 + 3; ++i)
+ {
+ if (GetMonData(&party[i], MON_DATA_HP) != 0
+ && GetMonData(&party[i], MON_DATA_SPECIES2) != SPECIES_NONE
+ && GetMonData(&party[i], MON_DATA_SPECIES2) != SPECIES_EGG)
+ break;
+ }
+ return (i == flankId * 3 + 3);
+ }
+ else
+ {
+ if (GetBattlerSide(battler) == B_SIDE_OPPONENT)
+ {
+ playerId = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
+ flankId = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT);
+ party = gEnemyParty;
+ }
+ else
+ {
+ playerId = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
+ flankId = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT);
+ party = gPlayerParty;
+ }
+ if (partyIdBattlerOn1 == PARTY_SIZE)
+ partyIdBattlerOn1 = gBattlerPartyIndexes[playerId];
+ if (partyIdBattlerOn2 == PARTY_SIZE)
+ partyIdBattlerOn2 = gBattlerPartyIndexes[flankId];
+ for (i = 0; i < PARTY_SIZE; ++i)
+ {
+ if (GetMonData(&party[i], MON_DATA_HP) != 0
+ && GetMonData(&party[i], MON_DATA_SPECIES2) != SPECIES_NONE
+ && GetMonData(&party[i], MON_DATA_SPECIES2) != SPECIES_EGG
+ && i != partyIdBattlerOn1
+ && i != partyIdBattlerOn2
+ && i != *(gBattleStruct->monToSwitchIntoId + playerId)
+ && i != flankId[gBattleStruct->monToSwitchIntoId])
+ break;
+ }
+ return (i == PARTY_SIZE);
+ }
+ }
+}
+
+enum
+{
+ CASTFORM_NO_CHANGE,
+ CASTFORM_TO_NORMAL,
+ CASTFORM_TO_FIRE,
+ CASTFORM_TO_WATER,
+ CASTFORM_TO_ICE,
+};
+
+u8 CastformDataTypeChange(u8 battler)
+{
+ u8 formChange = 0;
+ if (gBattleMons[battler].species != SPECIES_CASTFORM || gBattleMons[battler].ability != ABILITY_FORECAST || gBattleMons[battler].hp == 0)
+ return CASTFORM_NO_CHANGE;
+ if (!WEATHER_HAS_EFFECT && !IS_BATTLER_OF_TYPE(battler, TYPE_NORMAL))
+ {
+ SET_BATTLER_TYPE(battler, TYPE_NORMAL);
+ return CASTFORM_TO_NORMAL;
+ }
+ if (!WEATHER_HAS_EFFECT)
+ return CASTFORM_NO_CHANGE;
+ if (!(gBattleWeather & (WEATHER_RAIN_ANY | WEATHER_SUN_ANY | WEATHER_HAIL_ANY)) && !IS_BATTLER_OF_TYPE(battler, TYPE_NORMAL))
+ {
+ SET_BATTLER_TYPE(battler, TYPE_NORMAL);
+ formChange = CASTFORM_TO_NORMAL;
+ }
+ if (gBattleWeather & WEATHER_SUN_ANY && !IS_BATTLER_OF_TYPE(battler, TYPE_FIRE))
+ {
+ SET_BATTLER_TYPE(battler, TYPE_FIRE);
+ formChange = CASTFORM_TO_FIRE;
+ }
+ if (gBattleWeather & WEATHER_RAIN_ANY && !IS_BATTLER_OF_TYPE(battler, TYPE_WATER))
+ {
+ SET_BATTLER_TYPE(battler, TYPE_WATER);
+ formChange = CASTFORM_TO_WATER;
+ }
+ if (gBattleWeather & WEATHER_HAIL_ANY && !IS_BATTLER_OF_TYPE(battler, TYPE_ICE))
+ {
+ SET_BATTLER_TYPE(battler, TYPE_ICE);
+ formChange = CASTFORM_TO_ICE;
+ }
+ return formChange;
+}
+
+u8 AbilityBattleEffects(u8 caseID, u8 battler, u8 ability, u8 special, u16 moveArg)
+{
+ u8 effect = 0;
+ struct Pokemon *pokeAtk;
+ struct Pokemon *pokeDef;
+ u16 speciesAtk;
+ u16 speciesDef;
+ u32 pidAtk;
+ u32 pidDef;
+
+ if (gBattlerAttacker >= gBattlersCount)
+ gBattlerAttacker = battler;
+ if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER)
+ pokeAtk = &gPlayerParty[gBattlerPartyIndexes[gBattlerAttacker]];
+ else
+ pokeAtk = &gEnemyParty[gBattlerPartyIndexes[gBattlerAttacker]];
+ if (gBattlerTarget >= gBattlersCount)
+ gBattlerTarget = battler;
+ if (GetBattlerSide(gBattlerTarget) == B_SIDE_PLAYER)
+ pokeDef = &gPlayerParty[gBattlerPartyIndexes[gBattlerTarget]];
+ else
+ pokeDef = &gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]];
+ speciesAtk = GetMonData(pokeAtk, MON_DATA_SPECIES);
+ pidAtk = GetMonData(pokeAtk, MON_DATA_PERSONALITY);
+ speciesDef = GetMonData(pokeDef, MON_DATA_SPECIES);
+ pidDef = GetMonData(pokeDef, MON_DATA_PERSONALITY);
+ if (!(gBattleTypeFlags & BATTLE_TYPE_SAFARI)) // Why isn't that check done at the beginning?
+ {
+ u8 moveType;
+ s32 i;
+ u8 side;
+ u8 target1;
+
+ if (special)
+ gLastUsedAbility = special;
+ else
+ gLastUsedAbility = gBattleMons[battler].ability;
+ if (!moveArg)
+ moveArg = gCurrentMove;
+ GET_MOVE_TYPE(moveArg, moveType);
+ if ((gBattleTypeFlags & (BATTLE_TYPE_GHOST | BATTLE_TYPE_LEGENDARY)) == BATTLE_TYPE_GHOST
+ && (gLastUsedAbility == ABILITY_INTIMIDATE || gLastUsedAbility == ABILITY_TRACE))
+ return effect;
+ switch (caseID)
+ {
+ case ABILITYEFFECT_ON_SWITCHIN: // 0
+ if (gBattlerAttacker >= gBattlersCount)
+ gBattlerAttacker = battler;
+ switch (gLastUsedAbility)
+ {
+ case ABILITYEFFECT_SWITCH_IN_WEATHER:
+ switch (GetCurrentWeather())
+ {
+ case WEATHER_RAIN:
+ case WEATHER_RAIN_THUNDERSTORM:
+ case WEATHER_DOWNPOUR:
+ if (!(gBattleWeather & WEATHER_RAIN_ANY))
+ {
+ gBattleWeather = (WEATHER_RAIN_TEMPORARY | WEATHER_RAIN_PERMANENT);
+ gBattleScripting.animArg1 = B_ANIM_RAIN_CONTINUES;
+ gBattleScripting.battler = battler;
+ ++effect;
+ }
+ break;
+ case WEATHER_SANDSTORM:
+ if (!(gBattleWeather & WEATHER_SANDSTORM_ANY))
+ {
+ gBattleWeather = (WEATHER_SANDSTORM_PERMANENT | WEATHER_SANDSTORM_TEMPORARY);
+ gBattleScripting.animArg1 = B_ANIM_SANDSTORM_CONTINUES;
+ gBattleScripting.battler = battler;
+ ++effect;
+ }
+ break;
+ case WEATHER_DROUGHT:
+ if (!(gBattleWeather & WEATHER_SUN_ANY))
+ {
+ gBattleWeather = (WEATHER_SUN_PERMANENT | WEATHER_SUN_TEMPORARY);
+ gBattleScripting.animArg1 = B_ANIM_SUN_CONTINUES;
+ gBattleScripting.battler = battler;
+ ++effect;
+ }
+ break;
+ }
+ if (effect)
+ {
+ gBattleCommunication[MULTISTRING_CHOOSER] = GetCurrentWeather();
+ BattleScriptPushCursorAndCallback(BattleScript_OverworldWeatherStarts);
+ }
+ break;
+ case ABILITY_DRIZZLE:
+ if (!(gBattleWeather & WEATHER_RAIN_PERMANENT))
+ {
+ gBattleWeather = (WEATHER_RAIN_PERMANENT | WEATHER_RAIN_TEMPORARY);
+ BattleScriptPushCursorAndCallback(BattleScript_DrizzleActivates);
+ gBattleScripting.battler = battler;
+ ++effect;
+ }
+ break;
+ case ABILITY_SAND_STREAM:
+ if (!(gBattleWeather & WEATHER_SANDSTORM_PERMANENT))
+ {
+ gBattleWeather = (WEATHER_SANDSTORM_PERMANENT | WEATHER_SANDSTORM_TEMPORARY);
+ BattleScriptPushCursorAndCallback(BattleScript_SandstreamActivates);
+ gBattleScripting.battler = battler;
+ ++effect;
+ }
+ break;
+ case ABILITY_DROUGHT:
+ if (!(gBattleWeather & WEATHER_SUN_PERMANENT))
+ {
+ gBattleWeather = (WEATHER_SUN_PERMANENT | WEATHER_SUN_TEMPORARY);
+ BattleScriptPushCursorAndCallback(BattleScript_DroughtActivates);
+ gBattleScripting.battler = battler;
+ ++effect;
+ }
+ break;
+ case ABILITY_INTIMIDATE:
+ if (!(gSpecialStatuses[battler].intimidatedMon))
+ {
+ gStatuses3[battler] |= STATUS3_INTIMIDATE_POKES;
+ gSpecialStatuses[battler].intimidatedMon = 1;
+ }
+ break;
+ case ABILITY_FORECAST:
+ effect = CastformDataTypeChange(battler);
+ if (effect != 0)
+ {
+ BattleScriptPushCursorAndCallback(BattleScript_CastformChange);
+ gBattleScripting.battler = battler;
+ *(&gBattleStruct->formToChangeInto) = effect - 1;
+ }
+ break;
+ case ABILITY_TRACE:
+ if (!(gSpecialStatuses[battler].traced))
+ {
+ gStatuses3[battler] |= STATUS3_TRACE;
+ gSpecialStatuses[battler].traced = 1;
+ }
+ break;
+ case ABILITY_CLOUD_NINE:
+ case ABILITY_AIR_LOCK:
+ {
+ for (target1 = 0; target1 < gBattlersCount; ++target1)
+ {
+ effect = CastformDataTypeChange(target1);
+ if (effect != 0)
+ {
+ BattleScriptPushCursorAndCallback(BattleScript_CastformChange);
+ gBattleScripting.battler = target1;
+ *(&gBattleStruct->formToChangeInto) = effect - 1;
+ break;
+ }
+ }
+ }
+ break;
+ }
+ break;
+ case ABILITYEFFECT_ENDTURN: // 1
+ if (gBattleMons[battler].hp != 0)
+ {
+ gBattlerAttacker = battler;
+ switch (gLastUsedAbility)
+ {
+ case ABILITY_RAIN_DISH:
+ if (WEATHER_HAS_EFFECT && (gBattleWeather & WEATHER_RAIN_ANY)
+ && gBattleMons[battler].maxHP > gBattleMons[battler].hp)
+ {
+ gLastUsedAbility = ABILITY_RAIN_DISH; // why
+ BattleScriptPushCursorAndCallback(BattleScript_RainDishActivates);
+ gBattleMoveDamage = gBattleMons[battler].maxHP / 16;
+ if (gBattleMoveDamage == 0)
+ gBattleMoveDamage = 1;
+ gBattleMoveDamage *= -1;
+ ++effect;
+ }
+ break;
+ case ABILITY_SHED_SKIN:
+ if ((gBattleMons[battler].status1 & STATUS1_ANY) && (Random() % 3) == 0)
+ {
+ if (gBattleMons[battler].status1 & (STATUS1_POISON | STATUS1_TOXIC_POISON))
+ StringCopy(gBattleTextBuff1, gStatusConditionString_PoisonJpn);
+ if (gBattleMons[battler].status1 & STATUS1_SLEEP)
+ StringCopy(gBattleTextBuff1, gStatusConditionString_SleepJpn);
+ if (gBattleMons[battler].status1 & STATUS1_PARALYSIS)
+ StringCopy(gBattleTextBuff1, gStatusConditionString_ParalysisJpn);
+ if (gBattleMons[battler].status1 & STATUS1_BURN)
+ StringCopy(gBattleTextBuff1, gStatusConditionString_BurnJpn);
+ if (gBattleMons[battler].status1 & STATUS1_FREEZE)
+ StringCopy(gBattleTextBuff1, gStatusConditionString_IceJpn);
+ gBattleMons[battler].status1 = 0;
+ gBattleMons[battler].status2 &= ~(STATUS2_NIGHTMARE); // fix nightmare glitch
+ gBattleScripting.battler = gActiveBattler = battler;
+ BattleScriptPushCursorAndCallback(BattleScript_ShedSkinActivates);
+ BtlController_EmitSetMonData(0, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[battler].status1);
+ MarkBattlerForControllerExec(gActiveBattler);
+ ++effect;
+ }
+ break;
+ case ABILITY_SPEED_BOOST:
+ if (gBattleMons[battler].statStages[STAT_SPEED] < 0xC && gDisableStructs[battler].isFirstTurn != 2)
+ {
+ ++gBattleMons[battler].statStages[STAT_SPEED];
+ gBattleScripting.animArg1 = 0x11;
+ gBattleScripting.animArg2 = 0;
+ BattleScriptPushCursorAndCallback(BattleScript_SpeedBoostActivates);
+ gBattleScripting.battler = battler;
+ ++effect;
+ }
+ break;
+ case ABILITY_TRUANT:
+ gDisableStructs[gBattlerAttacker].truantCounter ^= 1;
+ break;
+ }
+ }
+ break;
+ case ABILITYEFFECT_MOVES_BLOCK: // 2
+ if (gLastUsedAbility == ABILITY_SOUNDPROOF)
+ {
+ for (i = 0; sSoundMovesTable[i] != 0xFFFF; ++i)
+ if (sSoundMovesTable[i] == moveArg)
+ break;
+ if (sSoundMovesTable[i] != 0xFFFF)
+ {
+ if (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS)
+ gHitMarker |= HITMARKER_NO_PPDEDUCT;
+ gBattlescriptCurrInstr = BattleScript_SoundproofProtected;
+ effect = 1;
+ }
+ }
+ break;
+ case ABILITYEFFECT_ABSORBING: // 3
+ if (moveArg)
+ {
+ switch (gLastUsedAbility)
+ {
+ case ABILITY_VOLT_ABSORB:
+ if (moveType == TYPE_ELECTRIC && gBattleMoves[moveArg].power != 0)
+ {
+ if (gProtectStructs[gBattlerAttacker].notFirstStrike)
+ gBattlescriptCurrInstr = BattleScript_MoveHPDrain;
+ else
+ gBattlescriptCurrInstr = BattleScript_MoveHPDrain_PPLoss;
+ effect = 1;
+ }
+ break;
+ case ABILITY_WATER_ABSORB:
+ if (moveType == TYPE_WATER && gBattleMoves[moveArg].power != 0)
+ {
+ if (gProtectStructs[gBattlerAttacker].notFirstStrike)
+ gBattlescriptCurrInstr = BattleScript_MoveHPDrain;
+ else
+ gBattlescriptCurrInstr = BattleScript_MoveHPDrain_PPLoss;
+ effect = 1;
+ }
+ break;
+ case ABILITY_FLASH_FIRE:
+ if (moveType == TYPE_FIRE && !(gBattleMons[battler].status1 & STATUS1_FREEZE))
+ {
+ if (!(gBattleResources->flags->flags[battler] & RESOURCE_FLAG_FLASH_FIRE))
+ {
+ gBattleCommunication[MULTISTRING_CHOOSER] = 0;
+ if (gProtectStructs[gBattlerAttacker].notFirstStrike)
+ gBattlescriptCurrInstr = BattleScript_FlashFireBoost;
+ else
+ gBattlescriptCurrInstr = BattleScript_FlashFireBoost_PPLoss;
+ gBattleResources->flags->flags[battler] |= RESOURCE_FLAG_FLASH_FIRE;
+ effect = 2;
+ }
+ else
+ {
+ gBattleCommunication[MULTISTRING_CHOOSER] = 1;
+ if (gProtectStructs[gBattlerAttacker].notFirstStrike)
+ gBattlescriptCurrInstr = BattleScript_FlashFireBoost;
+ else
+ gBattlescriptCurrInstr = BattleScript_FlashFireBoost_PPLoss;
+ effect = 2;
+ }
+ }
+ break;
+ }
+ if (effect == 1)
+ {
+ if (gBattleMons[battler].maxHP == gBattleMons[battler].hp)
+ {
+ if ((gProtectStructs[gBattlerAttacker].notFirstStrike))
+ gBattlescriptCurrInstr = BattleScript_MonMadeMoveUseless;
+ else
+ gBattlescriptCurrInstr = BattleScript_MonMadeMoveUseless_PPLoss;
+ }
+ else
+ {
+ gBattleMoveDamage = gBattleMons[battler].maxHP / 4;
+ if (gBattleMoveDamage == 0)
+ gBattleMoveDamage = 1;
+ gBattleMoveDamage *= -1;
+ }
+ }
+ }
+ break;
+ case ABILITYEFFECT_MOVE_END: // Think contact abilities.
+ switch (gLastUsedAbility)
+ {
+ case ABILITY_COLOR_CHANGE:
+ if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
+ && moveArg != MOVE_STRUGGLE
+ && gBattleMoves[moveArg].power != 0
+ && TARGET_TURN_DAMAGED
+ && !IS_BATTLER_OF_TYPE(battler, moveType)
+ && gBattleMons[battler].hp != 0)
+ {
+ SET_BATTLER_TYPE(battler, moveType);
+ PREPARE_TYPE_BUFFER(gBattleTextBuff1, moveType);
+ BattleScriptPushCursor();
+ gBattlescriptCurrInstr = BattleScript_ColorChangeActivates;
+ ++effect;
+ }
+ break;
+ case ABILITY_ROUGH_SKIN:
+ if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
+ && gBattleMons[gBattlerAttacker].hp != 0
+ && !gProtectStructs[gBattlerAttacker].confusionSelfDmg
+ && TARGET_TURN_DAMAGED
+ && (gBattleMoves[moveArg].flags & FLAG_MAKES_CONTACT))
+ {
+ gBattleMoveDamage = gBattleMons[gBattlerAttacker].maxHP / 16;
+ if (gBattleMoveDamage == 0)
+ gBattleMoveDamage = 1;
+ BattleScriptPushCursor();
+ gBattlescriptCurrInstr = BattleScript_RoughSkinActivates;
+ ++effect;
+ }
+ break;
+ case ABILITY_EFFECT_SPORE:
+ if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
+ && gBattleMons[gBattlerAttacker].hp != 0
+ && !gProtectStructs[gBattlerAttacker].confusionSelfDmg
+ && TARGET_TURN_DAMAGED
+ && (gBattleMoves[moveArg].flags & FLAG_MAKES_CONTACT)
+ && (Random() % 10) == 0)
+ {
+ do
+ gBattleCommunication[MOVE_EFFECT_BYTE] = Random() & 3;
+ while (gBattleCommunication[MOVE_EFFECT_BYTE] == 0);
+
+ if (gBattleCommunication[MOVE_EFFECT_BYTE] == MOVE_EFFECT_BURN)
+ gBattleCommunication[MOVE_EFFECT_BYTE] += 2; // 5 MOVE_EFFECT_PARALYSIS
+ gBattleCommunication[MOVE_EFFECT_BYTE] += MOVE_EFFECT_AFFECTS_USER;
+ BattleScriptPushCursor();
+ gBattlescriptCurrInstr = BattleScript_ApplySecondaryEffect;
+ gHitMarker |= HITMARKER_IGNORE_SAFEGUARD;
+ ++effect;
+ }
+ break;
+ case ABILITY_POISON_POINT:
+ if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
+ && gBattleMons[gBattlerAttacker].hp != 0
+ && !gProtectStructs[gBattlerAttacker].confusionSelfDmg
+ && TARGET_TURN_DAMAGED
+ && (gBattleMoves[moveArg].flags & FLAG_MAKES_CONTACT)
+ && (Random() % 3) == 0)
+ {
+ gBattleCommunication[MOVE_EFFECT_BYTE] = MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_POISON;
+ BattleScriptPushCursor();
+ gBattlescriptCurrInstr = BattleScript_ApplySecondaryEffect;
+ gHitMarker |= HITMARKER_IGNORE_SAFEGUARD;
+ ++effect;
+ }
+ break;
+ case ABILITY_STATIC:
+ if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
+ && gBattleMons[gBattlerAttacker].hp != 0
+ && !gProtectStructs[gBattlerAttacker].confusionSelfDmg
+ && TARGET_TURN_DAMAGED
+ && (gBattleMoves[moveArg].flags & FLAG_MAKES_CONTACT)
+ && (Random() % 3) == 0)
+ {
+ gBattleCommunication[MOVE_EFFECT_BYTE] = MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_PARALYSIS;
+ BattleScriptPushCursor();
+ gBattlescriptCurrInstr = BattleScript_ApplySecondaryEffect;
+ gHitMarker |= HITMARKER_IGNORE_SAFEGUARD;
+ ++effect;
+ }
+ break;
+ case ABILITY_FLAME_BODY:
+ if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
+ && gBattleMons[gBattlerAttacker].hp != 0
+ && !gProtectStructs[gBattlerAttacker].confusionSelfDmg
+ && (gBattleMoves[moveArg].flags & FLAG_MAKES_CONTACT)
+ && TARGET_TURN_DAMAGED
+ && (Random() % 3) == 0)
+ {
+ gBattleCommunication[MOVE_EFFECT_BYTE] = MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_BURN;
+ BattleScriptPushCursor();
+ gBattlescriptCurrInstr = BattleScript_ApplySecondaryEffect;
+ gHitMarker |= HITMARKER_IGNORE_SAFEGUARD;
+ ++effect;
+ }
+ break;
+ case ABILITY_CUTE_CHARM:
+ if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
+ && gBattleMons[gBattlerAttacker].hp != 0
+ && !gProtectStructs[gBattlerAttacker].confusionSelfDmg
+ && (gBattleMoves[moveArg].flags & FLAG_MAKES_CONTACT)
+ && TARGET_TURN_DAMAGED
+ && gBattleMons[gBattlerTarget].hp != 0
+ && (Random() % 3) == 0
+ && gBattleMons[gBattlerAttacker].ability != ABILITY_OBLIVIOUS
+ && GetGenderFromSpeciesAndPersonality(speciesAtk, pidAtk) != GetGenderFromSpeciesAndPersonality(speciesDef, pidDef)
+ && !(gBattleMons[gBattlerAttacker].status2 & STATUS2_INFATUATION)
+ && GetGenderFromSpeciesAndPersonality(speciesAtk, pidAtk) != MON_GENDERLESS
+ && GetGenderFromSpeciesAndPersonality(speciesDef, pidDef) != MON_GENDERLESS)
+ {
+ gBattleMons[gBattlerAttacker].status2 |= STATUS2_INFATUATED_WITH(gBattlerTarget);
+ BattleScriptPushCursor();
+ gBattlescriptCurrInstr = BattleScript_CuteCharmActivates;
+ ++effect;
+ }
+ break;
+ }
+ break;
+ case ABILITYEFFECT_IMMUNITY: // 5
+ for (battler = 0; battler < gBattlersCount; ++battler)
+ {
+ switch (gBattleMons[battler].ability)
+ {
+ case ABILITY_IMMUNITY:
+ if (gBattleMons[battler].status1 & (STATUS1_POISON | STATUS1_TOXIC_POISON | STATUS1_TOXIC_COUNTER))
+ {
+ StringCopy(gBattleTextBuff1, gStatusConditionString_PoisonJpn);
+ effect = 1;
+ }
+ break;
+ case ABILITY_OWN_TEMPO:
+ if (gBattleMons[battler].status2 & STATUS2_CONFUSION)
+ {
+ StringCopy(gBattleTextBuff1, gStatusConditionString_ConfusionJpn);
+ effect = 2;
+ }
+ break;
+ case ABILITY_LIMBER:
+ if (gBattleMons[battler].status1 & STATUS1_PARALYSIS)
+ {
+ StringCopy(gBattleTextBuff1, gStatusConditionString_ParalysisJpn);
+ effect = 1;
+ }
+ break;
+ case ABILITY_INSOMNIA:
+ case ABILITY_VITAL_SPIRIT:
+ if (gBattleMons[battler].status1 & STATUS1_SLEEP)
+ {
+ gBattleMons[battler].status2 &= ~(STATUS2_NIGHTMARE);
+ StringCopy(gBattleTextBuff1, gStatusConditionString_SleepJpn);
+ effect = 1;
+ }
+ break;
+ case ABILITY_WATER_VEIL:
+ if (gBattleMons[battler].status1 & STATUS1_BURN)
+ {
+ StringCopy(gBattleTextBuff1, gStatusConditionString_BurnJpn);
+ effect = 1;
+ }
+ break;
+ case ABILITY_MAGMA_ARMOR:
+ if (gBattleMons[battler].status1 & STATUS1_FREEZE)
+ {
+ StringCopy(gBattleTextBuff1, gStatusConditionString_IceJpn);
+ effect = 1;
+ }
+ break;
+ case ABILITY_OBLIVIOUS:
+ if (gBattleMons[battler].status2 & STATUS2_INFATUATION)
+ {
+ StringCopy(gBattleTextBuff1, gStatusConditionString_LoveJpn);
+ effect = 3;
+ }
+ break;
+ }
+ if (effect)
+ {
+ switch (effect)
+ {
+ case 1: // status cleared
+ gBattleMons[battler].status1 = 0;
+ break;
+ case 2: // get rid of confusion
+ gBattleMons[battler].status2 &= ~(STATUS2_CONFUSION);
+ break;
+ case 3: // get rid of infatuation
+ gBattleMons[battler].status2 &= ~(STATUS2_INFATUATION);
+ break;
+ }
+
+ BattleScriptPushCursor();
+ gBattlescriptCurrInstr = BattleScript_AbilityCuredStatus;
+ gBattleScripting.battler = battler;
+ gActiveBattler = battler;
+ BtlController_EmitSetMonData(0, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[gActiveBattler].status1);
+ MarkBattlerForControllerExec(gActiveBattler);
+ return effect;
+ }
+ }
+ break;
+ case ABILITYEFFECT_FORECAST: // 6
+ for (battler = 0; battler < gBattlersCount; ++battler)
+ {
+ if (gBattleMons[battler].ability == ABILITY_FORECAST)
+ {
+ effect = CastformDataTypeChange(battler);
+ if (effect)
+ {
+ BattleScriptPushCursorAndCallback(BattleScript_CastformChange);
+ gBattleScripting.battler = battler;
+ *(&gBattleStruct->formToChangeInto) = effect - 1;
+ return effect;
+ }
+ }
+ }
+ break;
+ case ABILITYEFFECT_SYNCHRONIZE: // 7
+ if (gLastUsedAbility == ABILITY_SYNCHRONIZE && (gHitMarker & HITMARKER_SYNCHRONISE_EFFECT))
+ {
+ gHitMarker &= ~(HITMARKER_SYNCHRONISE_EFFECT);
+ gBattleStruct->synchronizeMoveEffect &= ~(MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_CERTAIN);
+ if (gBattleStruct->synchronizeMoveEffect == MOVE_EFFECT_TOXIC)
+ gBattleStruct->synchronizeMoveEffect = MOVE_EFFECT_POISON;
+ gBattleCommunication[MOVE_EFFECT_BYTE] = gBattleStruct->synchronizeMoveEffect + MOVE_EFFECT_AFFECTS_USER;
+ gBattleScripting.battler = gBattlerTarget;
+ BattleScriptPushCursor();
+ gBattlescriptCurrInstr = BattleScript_SynchronizeActivates;
+ gHitMarker |= HITMARKER_IGNORE_SAFEGUARD;
+ ++effect;
+ }
+ break;
+ case ABILITYEFFECT_ATK_SYNCHRONIZE: // 8
+ if (gLastUsedAbility == ABILITY_SYNCHRONIZE && (gHitMarker & HITMARKER_SYNCHRONISE_EFFECT))
+ {
+ gHitMarker &= ~(HITMARKER_SYNCHRONISE_EFFECT);
+ gBattleStruct->synchronizeMoveEffect &= ~(MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_CERTAIN);
+ if (gBattleStruct->synchronizeMoveEffect == MOVE_EFFECT_TOXIC)
+ gBattleStruct->synchronizeMoveEffect = MOVE_EFFECT_POISON;
+ gBattleCommunication[MOVE_EFFECT_BYTE] = gBattleStruct->synchronizeMoveEffect;
+ gBattleScripting.battler = gBattlerAttacker;
+ BattleScriptPushCursor();
+ gBattlescriptCurrInstr = BattleScript_SynchronizeActivates;
+ gHitMarker |= HITMARKER_IGNORE_SAFEGUARD;
+ ++effect;
+ }
+ break;
+ case ABILITYEFFECT_INTIMIDATE1: // 9
+ for (i = 0; i < gBattlersCount; ++i)
+ {
+ if (gBattleMons[i].ability == ABILITY_INTIMIDATE && gStatuses3[i] & STATUS3_INTIMIDATE_POKES)
+ {
+ gLastUsedAbility = ABILITY_INTIMIDATE;
+ gStatuses3[i] &= ~(STATUS3_INTIMIDATE_POKES);
+ BattleScriptPushCursorAndCallback(BattleScript_IntimidateActivatesEnd3);
+ gBattleStruct->intimidateBattler = i;
+ ++effect;
+ break;
+ }
+ }
+ break;
+ case ABILITYEFFECT_TRACE: // 11
+ for (i = 0; i < gBattlersCount; ++i)
+ {
+ if (gBattleMons[i].ability == ABILITY_TRACE && (gStatuses3[i] & STATUS3_TRACE))
+ {
+ u8 target2;
+
+ side = (GetBattlerPosition(i) ^ BIT_SIDE) & BIT_SIDE; // side of the opposing pokemon
+ target1 = GetBattlerAtPosition(side);
+ target2 = GetBattlerAtPosition(side + BIT_FLANK);
+ if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
+ {
+ if (gBattleMons[target1].ability != 0 && gBattleMons[target1].hp != 0
+ && gBattleMons[target2].ability != 0 && gBattleMons[target2].hp != 0)
+ {
+ gActiveBattler = GetBattlerAtPosition(((Random() & 1) * 2) | side);
+ gBattleMons[i].ability = gBattleMons[gActiveBattler].ability;
+ gLastUsedAbility = gBattleMons[gActiveBattler].ability;
+ ++effect;
+ }
+ else if (gBattleMons[target1].ability != 0 && gBattleMons[target1].hp != 0)
+ {
+ gActiveBattler = target1;
+ gBattleMons[i].ability = gBattleMons[gActiveBattler].ability;
+ gLastUsedAbility = gBattleMons[gActiveBattler].ability;
+ ++effect;
+ }
+ else if (gBattleMons[target2].ability != 0 && gBattleMons[target2].hp != 0)
+ {
+ gActiveBattler = target2;
+ gBattleMons[i].ability = gBattleMons[gActiveBattler].ability;
+ gLastUsedAbility = gBattleMons[gActiveBattler].ability;
+ ++effect;
+ }
+ }
+ else
+ {
+ gActiveBattler = target1;
+ if (gBattleMons[target1].ability && gBattleMons[target1].hp)
+ {
+ gBattleMons[i].ability = gBattleMons[target1].ability;
+ gLastUsedAbility = gBattleMons[target1].ability;
+ ++effect;
+ }
+ }
+ if (effect)
+ {
+ BattleScriptPushCursorAndCallback(BattleScript_TraceActivates);
+ gStatuses3[i] &= ~(STATUS3_TRACE);
+ gBattleScripting.battler = i;
+ PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, gActiveBattler, gBattlerPartyIndexes[gActiveBattler])
+ PREPARE_ABILITY_BUFFER(gBattleTextBuff2, gLastUsedAbility)
+ break;
+ }
+ }
+ }
+ break;
+ case ABILITYEFFECT_INTIMIDATE2: // 10
+ for (i = 0; i < gBattlersCount; ++i)
+ {
+ if (gBattleMons[i].ability == ABILITY_INTIMIDATE && (gStatuses3[i] & STATUS3_INTIMIDATE_POKES))
+ {
+ gLastUsedAbility = ABILITY_INTIMIDATE;
+ gStatuses3[i] &= ~(STATUS3_INTIMIDATE_POKES);
+ BattleScriptPushCursor();
+ gBattlescriptCurrInstr = BattleScript_IntimidateActivates;
+ gBattleStruct->intimidateBattler = i;
+ ++effect;
+ break;
+ }
+ }
+ break;
+ case ABILITYEFFECT_CHECK_OTHER_SIDE: // 12
+ side = GetBattlerSide(battler);
+ for (i = 0; i < gBattlersCount; ++i)
+ {
+ if (GetBattlerSide(i) != side && gBattleMons[i].ability == ability)
+ {
+ gLastUsedAbility = ability;
+ effect = i + 1;
+ }
+ }
+ break;
+ case ABILITYEFFECT_CHECK_BATTLER_SIDE: // 13
+ side = GetBattlerSide(battler);
+ for (i = 0; i < gBattlersCount; ++i)
+ {
+ if (GetBattlerSide(i) == side && gBattleMons[i].ability == ability)
+ {
+ gLastUsedAbility = ability;
+ effect = i + 1;
+ }
+ }
+ break;
+ case ABILITYEFFECT_FIELD_SPORT: // 14
+ switch (gLastUsedAbility)
+ {
+ case 0xFD:
+ for (i = 0; i < gBattlersCount; ++i)
+ if (gStatuses3[i] & STATUS3_MUDSPORT)
+ effect = i + 1;
+ break;
+ case 0xFE:
+ for (i = 0; i < gBattlersCount; ++i)
+ if (gStatuses3[i] & STATUS3_WATERSPORT)
+ effect = i + 1;
+ break;
+ default:
+ for (i = 0; i < gBattlersCount; ++i)
+ {
+ if (gBattleMons[i].ability == ability)
+ {
+ gLastUsedAbility = ability;
+ effect = i + 1;
+ }
+ }
+ break;
+ }
+ break;
+ case ABILITYEFFECT_CHECK_ON_FIELD: // 19
+ for (i = 0; i < gBattlersCount; ++i)
+ {
+ if (gBattleMons[i].ability == ability && gBattleMons[i].hp != 0)
+ {
+ gLastUsedAbility = ability;
+ effect = i + 1;
+ }
+ }
+ break;
+ case ABILITYEFFECT_CHECK_FIELD_EXCEPT_BATTLER: // 15
+ side = GetBattlerSide(battler);
+ for (i = 0; i < gBattlersCount; ++i)
+ {
+ if (GetBattlerSide(i) != side && gBattleMons[i].ability == ability)
+ {
+ gLastUsedAbility = ability;
+ effect = i + 1;
+ break;
+ }
+ }
+ if (!effect)
+ {
+ for (i = 0; i < gBattlersCount; ++i)
+ {
+ if (gBattleMons[i].ability == ability && GetBattlerSide(i) == side && i != battler)
+ {
+ gLastUsedAbility = ability;
+ effect = i + 1;
+ }
+ }
+ }
+ break;
+ case ABILITYEFFECT_COUNT_OTHER_SIDE: // 16
+ side = GetBattlerSide(battler);
+ for (i = 0; i < gBattlersCount; ++i)
+ {
+ if (GetBattlerSide(i) != side && gBattleMons[i].ability == ability)
+ {
+ gLastUsedAbility = ability;
+ ++effect;
+ }
+ }
+ break;
+ case ABILITYEFFECT_COUNT_BATTLER_SIDE: // 17
+ side = GetBattlerSide(battler);
+ for (i = 0; i < gBattlersCount; ++i)
+ {
+ if (GetBattlerSide(i) == side && gBattleMons[i].ability == ability)
+ {
+ gLastUsedAbility = ability;
+ ++effect;
+ }
+ }
+ break;
+ case ABILITYEFFECT_COUNT_ON_FIELD: // 18
+ for (i = 0; i < gBattlersCount; ++i)
+ {
+ if (gBattleMons[i].ability == ability && i != battler)
+ {
+ gLastUsedAbility = ability;
+ ++effect;
+ }
+ }
+ break;
+ }
+ if (effect && caseID < ABILITYEFFECT_CHECK_OTHER_SIDE && gLastUsedAbility != 0xFF)
+ RecordAbilityBattle(battler, gLastUsedAbility);
+ }
+ return effect;
+}
+
+void BattleScriptExecute(const u8 *BS_ptr)
+{
+ gBattlescriptCurrInstr = BS_ptr;
+ gBattleResources->battleCallbackStack->function[gBattleResources->battleCallbackStack->size++] = gBattleMainFunc;
+ gBattleMainFunc = RunBattleScriptCommands_PopCallbacksStack;
+ gCurrentActionFuncId = 0;
+}
+
+void BattleScriptPushCursorAndCallback(const u8 *BS_ptr)
+{
+ BattleScriptPushCursor();
+ gBattlescriptCurrInstr = BS_ptr;
+ gBattleResources->battleCallbackStack->function[gBattleResources->battleCallbackStack->size++] = gBattleMainFunc;
+ gBattleMainFunc = RunBattleScriptCommands;
+}
+
+enum
+{
+ ITEM_NO_EFFECT,
+ ITEM_STATUS_CHANGE,
+ ITEM_EFFECT_OTHER,
+ ITEM_PP_CHANGE,
+ ITEM_HP_CHANGE,
+ ITEM_STATS_CHANGE,
+};
+
+u8 ItemBattleEffects(u8 caseID, u8 battlerId, bool8 moveTurn)
+{
+ int i = 0;
+ u8 effect = ITEM_NO_EFFECT;
+ u8 changedPP = 0;
+ u8 battlerHoldEffect, atkHoldEffect, defHoldEffect;
+ u8 battlerHoldEffectParam, atkHoldEffectParam, defHoldEffectParam;
+ u16 atkItem, defItem;
+
+ gLastUsedItem = gBattleMons[battlerId].item;
+ if (gLastUsedItem == ITEM_ENIGMA_BERRY)
+ {
+ battlerHoldEffect = gEnigmaBerries[battlerId].holdEffect;
+ battlerHoldEffectParam = gEnigmaBerries[battlerId].holdEffectParam;
+ }
+ else
+ {
+ battlerHoldEffect = ItemId_GetHoldEffect(gLastUsedItem);
+ battlerHoldEffectParam = ItemId_GetHoldEffectParam(gLastUsedItem);
+ }
+
+ atkItem = gBattleMons[gBattlerAttacker].item;
+ if (atkItem == ITEM_ENIGMA_BERRY)
+ {
+ atkHoldEffect = gEnigmaBerries[gBattlerAttacker].holdEffect;
+ atkHoldEffectParam = gEnigmaBerries[gBattlerAttacker].holdEffectParam;
+ }
+ else
+ {
+ atkHoldEffect = ItemId_GetHoldEffect(atkItem);
+ atkHoldEffectParam = ItemId_GetHoldEffectParam(atkItem);
+ }
+
+ // def variables are unused
+ defItem = gBattleMons[gBattlerTarget].item;
+ if (defItem == ITEM_ENIGMA_BERRY)
+ {
+ defHoldEffect = gEnigmaBerries[gBattlerTarget].holdEffect;
+ defHoldEffectParam = gEnigmaBerries[gBattlerTarget].holdEffectParam;
+ }
+ else
+ {
+ defHoldEffect = ItemId_GetHoldEffect(defItem);
+ defHoldEffectParam = ItemId_GetHoldEffectParam(defItem);
+ }
+ switch (caseID)
+ {
+ case ITEMEFFECT_ON_SWITCH_IN:
+ switch (battlerHoldEffect)
+ {
+ case HOLD_EFFECT_DOUBLE_PRIZE:
+ gBattleStruct->moneyMultiplier = 2;
+ break;
+ case HOLD_EFFECT_RESTORE_STATS:
+ for (i = 0; i < NUM_BATTLE_STATS; ++i)
+ {
+ if (gBattleMons[battlerId].statStages[i] < 6)
+ {
+ gBattleMons[battlerId].statStages[i] = 6;
+ effect = ITEM_STATS_CHANGE;
+ }
+ }
+ if (effect)
+ {
+ gBattleScripting.battler = battlerId;
+ gPotentialItemEffectBattler = battlerId;
+ gActiveBattler = gBattlerAttacker = battlerId;
+ BattleScriptExecute(BattleScript_WhiteHerbEnd2);
+ }
+ break;
+ }
+ break;
+ case 1:
+ if (gBattleMons[battlerId].hp)
+ {
+ switch (battlerHoldEffect)
+ {
+ case HOLD_EFFECT_RESTORE_HP:
+ if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / 2 && !moveTurn)
+ {
+ gBattleMoveDamage = battlerHoldEffectParam;
+ if (gBattleMons[battlerId].hp + battlerHoldEffectParam > gBattleMons[battlerId].maxHP)
+ gBattleMoveDamage = gBattleMons[battlerId].maxHP - gBattleMons[battlerId].hp;
+ gBattleMoveDamage *= -1;
+ BattleScriptExecute(BattleScript_ItemHealHP_RemoveItem);
+ effect = 4;
+ }
+ break;
+ case HOLD_EFFECT_RESTORE_PP:
+ if (!moveTurn)
+ {
+ struct Pokemon *mon;
+ u8 ppBonuses;
+ u16 move;
+
+ if (GetBattlerSide(battlerId) == B_SIDE_PLAYER)
+ mon = &gPlayerParty[gBattlerPartyIndexes[battlerId]];
+ else
+ mon = &gEnemyParty[gBattlerPartyIndexes[battlerId]];
+ for (i = 0; i < MAX_MON_MOVES; ++i)
+ {
+ move = GetMonData(mon, MON_DATA_MOVE1 + i);
+ changedPP = GetMonData(mon, MON_DATA_PP1 + i);
+ ppBonuses = GetMonData(mon, MON_DATA_PP_BONUSES);
+ if (move && changedPP == 0)
+ break;
+ }
+ if (i != MAX_MON_MOVES)
+ {
+ u8 maxPP = CalculatePPWithBonus(move, ppBonuses, i);
+ if (changedPP + battlerHoldEffectParam > maxPP)
+ changedPP = maxPP;
+ else
+ changedPP = changedPP + battlerHoldEffectParam;
+ PREPARE_MOVE_BUFFER(gBattleTextBuff1, move);
+ BattleScriptExecute(BattleScript_BerryPPHealEnd2);
+ BtlController_EmitSetMonData(0, i + REQUEST_PPMOVE1_BATTLE, 0, 1, &changedPP);
+ MarkBattlerForControllerExec(gActiveBattler);
+ effect = ITEM_PP_CHANGE;
+ }
+ }
+ break;
+ case HOLD_EFFECT_RESTORE_STATS:
+ for (i = 0; i < NUM_BATTLE_STATS; ++i)
+ {
+ if (gBattleMons[battlerId].statStages[i] < 6)
+ {
+ gBattleMons[battlerId].statStages[i] = 6;
+ effect = ITEM_STATS_CHANGE;
+ }
+ }
+ if (effect)
+ {
+ gBattleScripting.battler = battlerId;
+ gPotentialItemEffectBattler = battlerId;
+ gActiveBattler = gBattlerAttacker = battlerId;
+ BattleScriptExecute(BattleScript_WhiteHerbEnd2);
+ }
+ break;
+ case HOLD_EFFECT_LEFTOVERS:
+ if (gBattleMons[battlerId].hp < gBattleMons[battlerId].maxHP && !moveTurn)
+ {
+ gBattleMoveDamage = gBattleMons[battlerId].maxHP / 16;
+ if (gBattleMoveDamage == 0)
+ gBattleMoveDamage = 1;
+ if (gBattleMons[battlerId].hp + gBattleMoveDamage > gBattleMons[battlerId].maxHP)
+ gBattleMoveDamage = gBattleMons[battlerId].maxHP - gBattleMons[battlerId].hp;
+ gBattleMoveDamage *= -1;
+ BattleScriptExecute(BattleScript_ItemHealHP_End2);
+ effect = ITEM_HP_CHANGE;
+ RecordItemEffectBattle(battlerId, battlerHoldEffect);
+ }
+ break;
+ case HOLD_EFFECT_CONFUSE_SPICY:
+ if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / 2 && !moveTurn)
+ {
+ PREPARE_FLAVOR_BUFFER(gBattleTextBuff1, FLAVOR_SPICY);
+ gBattleMoveDamage = gBattleMons[battlerId].maxHP / battlerHoldEffectParam;
+ if (gBattleMoveDamage == 0)
+ gBattleMoveDamage = 1;
+ if (gBattleMons[battlerId].hp + gBattleMoveDamage > gBattleMons[battlerId].maxHP)
+ gBattleMoveDamage = gBattleMons[battlerId].maxHP - gBattleMons[battlerId].hp;
+ gBattleMoveDamage *= -1;
+ if (GetFlavorRelationByPersonality(gBattleMons[battlerId].personality, FLAVOR_SPICY) < 0)
+ BattleScriptExecute(BattleScript_BerryConfuseHealEnd2);
+ else
+ BattleScriptExecute(BattleScript_ItemHealHP_RemoveItem);
+ effect = ITEM_HP_CHANGE;
+ }
+ break;
+ case HOLD_EFFECT_CONFUSE_DRY:
+ if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / 2 && !moveTurn)
+ {
+ PREPARE_FLAVOR_BUFFER(gBattleTextBuff1, FLAVOR_DRY);
+ gBattleMoveDamage = gBattleMons[battlerId].maxHP / battlerHoldEffectParam;
+ if (gBattleMoveDamage == 0)
+ gBattleMoveDamage = 1;
+ if (gBattleMons[battlerId].hp + gBattleMoveDamage > gBattleMons[battlerId].maxHP)
+ gBattleMoveDamage = gBattleMons[battlerId].maxHP - gBattleMons[battlerId].hp;
+ gBattleMoveDamage *= -1;
+ if (GetFlavorRelationByPersonality(gBattleMons[battlerId].personality, FLAVOR_DRY) < 0)
+ BattleScriptExecute(BattleScript_BerryConfuseHealEnd2);
+ else
+ BattleScriptExecute(BattleScript_ItemHealHP_RemoveItem);
+ effect = ITEM_HP_CHANGE;
+ }
+ break;
+ case HOLD_EFFECT_CONFUSE_SWEET:
+ if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / 2 && !moveTurn)
+ {
+ PREPARE_FLAVOR_BUFFER(gBattleTextBuff1, FLAVOR_SWEET);
+ gBattleMoveDamage = gBattleMons[battlerId].maxHP / battlerHoldEffectParam;
+ if (gBattleMoveDamage == 0)
+ gBattleMoveDamage = 1;
+ if (gBattleMons[battlerId].hp + gBattleMoveDamage > gBattleMons[battlerId].maxHP)
+ gBattleMoveDamage = gBattleMons[battlerId].maxHP - gBattleMons[battlerId].hp;
+ gBattleMoveDamage *= -1;
+ if (GetFlavorRelationByPersonality(gBattleMons[battlerId].personality, FLAVOR_SWEET) < 0)
+ BattleScriptExecute(BattleScript_BerryConfuseHealEnd2);
+ else
+ BattleScriptExecute(BattleScript_ItemHealHP_RemoveItem);
+ effect = ITEM_HP_CHANGE;
+ }
+ break;
+ case HOLD_EFFECT_CONFUSE_BITTER:
+ if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / 2 && !moveTurn)
+ {
+ PREPARE_FLAVOR_BUFFER(gBattleTextBuff1, FLAVOR_BITTER);
+ gBattleMoveDamage = gBattleMons[battlerId].maxHP / battlerHoldEffectParam;
+ if (gBattleMoveDamage == 0)
+ gBattleMoveDamage = 1;
+ if (gBattleMons[battlerId].hp + gBattleMoveDamage > gBattleMons[battlerId].maxHP)
+ gBattleMoveDamage = gBattleMons[battlerId].maxHP - gBattleMons[battlerId].hp;
+ gBattleMoveDamage *= -1;
+ if (GetFlavorRelationByPersonality(gBattleMons[battlerId].personality, FLAVOR_BITTER) < 0)
+ BattleScriptExecute(BattleScript_BerryConfuseHealEnd2);
+ else
+ BattleScriptExecute(BattleScript_ItemHealHP_RemoveItem);
+ effect = ITEM_HP_CHANGE;
+ }
+ break;
+ case HOLD_EFFECT_CONFUSE_SOUR:
+ if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / 2 && !moveTurn)
+ {
+ PREPARE_FLAVOR_BUFFER(gBattleTextBuff1, FLAVOR_SOUR);
+ gBattleMoveDamage = gBattleMons[battlerId].maxHP / battlerHoldEffectParam;
+ if (gBattleMoveDamage == 0)
+ gBattleMoveDamage = 1;
+ if (gBattleMons[battlerId].hp + gBattleMoveDamage > gBattleMons[battlerId].maxHP)
+ gBattleMoveDamage = gBattleMons[battlerId].maxHP - gBattleMons[battlerId].hp;
+ gBattleMoveDamage *= -1;
+ if (GetFlavorRelationByPersonality(gBattleMons[battlerId].personality, FLAVOR_SOUR) < 0)
+ BattleScriptExecute(BattleScript_BerryConfuseHealEnd2);
+ else
+ BattleScriptExecute(BattleScript_ItemHealHP_RemoveItem);
+ effect = ITEM_HP_CHANGE;
+ }
+ break;
+ case HOLD_EFFECT_ATTACK_UP:
+ if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / battlerHoldEffectParam && !moveTurn && gBattleMons[battlerId].statStages[STAT_ATK] < 0xC)
+ {
+ PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_ATK);
+ PREPARE_STRING_BUFFER(gBattleTextBuff2, STRINGID_STATROSE);
+ gEffectBattler = battlerId;
+ SET_STATCHANGER(STAT_ATK, 1, FALSE);
+ gBattleScripting.animArg1 = 0xE + STAT_ATK;
+ gBattleScripting.animArg2 = 0;
+ BattleScriptExecute(BattleScript_BerryStatRaiseEnd2);
+ effect = ITEM_STATS_CHANGE;
+ }
+ break;
+ case HOLD_EFFECT_DEFENSE_UP:
+ if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / battlerHoldEffectParam && !moveTurn && gBattleMons[battlerId].statStages[STAT_DEF] < 0xC)
+ {
+ PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_DEF);
+ gEffectBattler = battlerId;
+ SET_STATCHANGER(STAT_DEF, 1, FALSE);
+ gBattleScripting.animArg1 = 0xE + STAT_DEF;
+ gBattleScripting.animArg2 = 0;
+ BattleScriptExecute(BattleScript_BerryStatRaiseEnd2);
+ effect = ITEM_STATS_CHANGE;
+ }
+ break;
+ case HOLD_EFFECT_SPEED_UP:
+ if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / battlerHoldEffectParam && !moveTurn && gBattleMons[battlerId].statStages[STAT_SPEED] < 0xC)
+ {
+ PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_SPEED);
+ gEffectBattler = battlerId;
+ SET_STATCHANGER(STAT_SPEED, 1, FALSE);
+ gBattleScripting.animArg1 = 0xE + STAT_SPEED;
+ gBattleScripting.animArg2 = 0;
+ BattleScriptExecute(BattleScript_BerryStatRaiseEnd2);
+ effect = ITEM_STATS_CHANGE;
+ }
+ break;
+ case HOLD_EFFECT_SP_ATTACK_UP:
+ if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / battlerHoldEffectParam && !moveTurn && gBattleMons[battlerId].statStages[STAT_SPATK] < 0xC)
+ {
+ PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_SPATK);
+ gEffectBattler = battlerId;
+ SET_STATCHANGER(STAT_SPATK, 1, FALSE);
+ gBattleScripting.animArg1 = 0xE + STAT_SPATK;
+ gBattleScripting.animArg2 = 0;
+ BattleScriptExecute(BattleScript_BerryStatRaiseEnd2);
+ effect = ITEM_STATS_CHANGE;
+ }
+ break;
+ case HOLD_EFFECT_SP_DEFENSE_UP:
+ if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / battlerHoldEffectParam && !moveTurn && gBattleMons[battlerId].statStages[STAT_SPDEF] < 0xC)
+ {
+ PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_SPDEF);
+ gEffectBattler = battlerId;
+ SET_STATCHANGER(STAT_SPDEF, 1, FALSE);
+ gBattleScripting.animArg1 = 0xE + STAT_SPDEF;
+ gBattleScripting.animArg2 = 0;
+ BattleScriptExecute(BattleScript_BerryStatRaiseEnd2);
+ effect = ITEM_STATS_CHANGE;
+ }
+ break;
+ case HOLD_EFFECT_CRITICAL_UP:
+ if (gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / battlerHoldEffectParam && !moveTurn && !(gBattleMons[battlerId].status2 & STATUS2_FOCUS_ENERGY))
+ {
+ gBattleMons[battlerId].status2 |= STATUS2_FOCUS_ENERGY;
+ BattleScriptExecute(BattleScript_BerryFocusEnergyEnd2);
+ effect = ITEM_EFFECT_OTHER;
+ }
+ break;
+ case HOLD_EFFECT_RANDOM_STAT_UP:
+ if (!moveTurn && gBattleMons[battlerId].hp <= gBattleMons[battlerId].maxHP / battlerHoldEffectParam)
+ {
+ for (i = 0; i < 5 && gBattleMons[battlerId].statStages[STAT_ATK + i] >= 0xC; ++i);
+ if (i != 5)
+ {
+ do
+ i = Random() % 5;
+ while (gBattleMons[battlerId].statStages[STAT_ATK + i] == 0xC);
+ PREPARE_STAT_BUFFER(gBattleTextBuff1, i + 1);
+ gBattleTextBuff2[0] = B_BUFF_PLACEHOLDER_BEGIN;
+ gBattleTextBuff2[1] = B_BUFF_STRING;
+ gBattleTextBuff2[2] = STRINGID_STATSHARPLY;
+ gBattleTextBuff2[3] = STRINGID_STATSHARPLY >> 8;
+ gBattleTextBuff2[4] = B_BUFF_STRING;
+ gBattleTextBuff2[5] = STRINGID_STATROSE;
+ gBattleTextBuff2[6] = STRINGID_STATROSE >> 8;
+ gBattleTextBuff2[7] = EOS;
+ gEffectBattler = battlerId;
+ SET_STATCHANGER(i + 1, 2, FALSE);
+ gBattleScripting.animArg1 = 0x21 + i + 6;
+ gBattleScripting.animArg2 = 0;
+ BattleScriptExecute(BattleScript_BerryStatRaiseEnd2);
+ effect = ITEM_STATS_CHANGE;
+ }
+ }
+ break;
+ case HOLD_EFFECT_CURE_PAR:
+ if (gBattleMons[battlerId].status1 & STATUS1_PARALYSIS)
+ {
+ gBattleMons[battlerId].status1 &= ~(STATUS1_PARALYSIS);
+ BattleScriptExecute(BattleScript_BerryCurePrlzEnd2);
+ effect = ITEM_STATUS_CHANGE;
+ }
+ break;
+ case HOLD_EFFECT_CURE_PSN:
+ if (gBattleMons[battlerId].status1 & STATUS1_PSN_ANY)
+ {
+ gBattleMons[battlerId].status1 &= ~(STATUS1_PSN_ANY | STATUS1_TOXIC_COUNTER);
+ BattleScriptExecute(BattleScript_BerryCurePsnEnd2);
+ effect = ITEM_STATUS_CHANGE;
+ }
+ break;
+ case HOLD_EFFECT_CURE_BRN:
+ if (gBattleMons[battlerId].status1 & STATUS1_BURN)
+ {
+ gBattleMons[battlerId].status1 &= ~(STATUS1_BURN);
+ BattleScriptExecute(BattleScript_BerryCureBrnEnd2);
+ effect = ITEM_STATUS_CHANGE;
+ }
+ break;
+ case HOLD_EFFECT_CURE_FRZ:
+ if (gBattleMons[battlerId].status1 & STATUS1_FREEZE)
+ {
+ gBattleMons[battlerId].status1 &= ~(STATUS1_FREEZE);
+ BattleScriptExecute(BattleScript_BerryCureFrzEnd2);
+ effect = ITEM_STATUS_CHANGE;
+ }
+ break;
+ case HOLD_EFFECT_CURE_SLP:
+ if (gBattleMons[battlerId].status1 & STATUS1_SLEEP)
+ {
+ gBattleMons[battlerId].status1 &= ~(STATUS1_SLEEP);
+ gBattleMons[battlerId].status2 &= ~(STATUS2_NIGHTMARE);
+ BattleScriptExecute(BattleScript_BerryCureSlpEnd2);
+ effect = ITEM_STATUS_CHANGE;
+ }
+ break;
+ case HOLD_EFFECT_CURE_CONFUSION:
+ if (gBattleMons[battlerId].status2 & STATUS2_CONFUSION)
+ {
+ gBattleMons[battlerId].status2 &= ~(STATUS2_CONFUSION);
+ BattleScriptExecute(BattleScript_BerryCureConfusionEnd2);
+ effect = ITEM_EFFECT_OTHER;
+ }
+ break;
+ case HOLD_EFFECT_CURE_STATUS:
+ if (gBattleMons[battlerId].status1 & STATUS1_ANY || gBattleMons[battlerId].status2 & STATUS2_CONFUSION)
+ {
+ i = 0;
+ if (gBattleMons[battlerId].status1 & STATUS1_PSN_ANY)
+ {
+ StringCopy(gBattleTextBuff1, gStatusConditionString_PoisonJpn);
+ ++i;
+ }
+ if (gBattleMons[battlerId].status1 & STATUS1_SLEEP)
+ {
+ gBattleMons[battlerId].status2 &= ~(STATUS2_NIGHTMARE);
+ StringCopy(gBattleTextBuff1, gStatusConditionString_SleepJpn);
+ ++i;
+ }
+ if (gBattleMons[battlerId].status1 & STATUS1_PARALYSIS)
+ {
+ StringCopy(gBattleTextBuff1, gStatusConditionString_ParalysisJpn);
+ ++i;
+ }
+ if (gBattleMons[battlerId].status1 & STATUS1_BURN)
+ {
+ StringCopy(gBattleTextBuff1, gStatusConditionString_BurnJpn);
+ ++i;
+ }
+ if (gBattleMons[battlerId].status1 & STATUS1_FREEZE)
+ {
+ StringCopy(gBattleTextBuff1, gStatusConditionString_IceJpn);
+ ++i;
+ }
+ if (gBattleMons[battlerId].status2 & STATUS2_CONFUSION)
+ {
+ StringCopy(gBattleTextBuff1, gStatusConditionString_ConfusionJpn);
+ ++i;
+ }
+ if (!(i > 1))
+ gBattleCommunication[MULTISTRING_CHOOSER] = 0;
+ else
+ gBattleCommunication[MULTISTRING_CHOOSER] = 1;
+ gBattleMons[battlerId].status1 = 0;
+ gBattleMons[battlerId].status2 &= ~(STATUS2_CONFUSION);
+ BattleScriptExecute(BattleScript_BerryCureChosenStatusEnd2);
+ effect = ITEM_STATUS_CHANGE;
+ }
+ break;
+ case HOLD_EFFECT_CURE_ATTRACT:
+ if (gBattleMons[battlerId].status2 & STATUS2_INFATUATION)
+ {
+ gBattleMons[battlerId].status2 &= ~(STATUS2_INFATUATION);
+ StringCopy(gBattleTextBuff1, gStatusConditionString_LoveJpn);
+ BattleScriptExecute(BattleScript_BerryCureChosenStatusEnd2);
+ gBattleCommunication[MULTISTRING_CHOOSER] = 0;
+ effect = ITEM_EFFECT_OTHER;
+ }
+ break;
+ }
+ if (effect)
+ {
+ gBattleScripting.battler = battlerId;
+ gPotentialItemEffectBattler = battlerId;
+ gActiveBattler = gBattlerAttacker = battlerId;
+ switch (effect)
+ {
+ case ITEM_STATUS_CHANGE:
+ BtlController_EmitSetMonData(0, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[battlerId].status1);
+ MarkBattlerForControllerExec(gActiveBattler);
+ break;
+ case ITEM_PP_CHANGE:
+ if (!(gBattleMons[battlerId].status2 & STATUS2_TRANSFORMED) && !(gDisableStructs[battlerId].mimickedMoves & gBitTable[i]))
+ gBattleMons[battlerId].pp[i] = changedPP;
+ break;
+ }
+ }
+ }
+ break;
+ case 2:
+ break;
+ case ITEMEFFECT_MOVE_END:
+ for (battlerId = 0; battlerId < gBattlersCount; ++battlerId)
+ {
+ gLastUsedItem = gBattleMons[battlerId].item;
+ if (gBattleMons[battlerId].item == ITEM_ENIGMA_BERRY)
+ {
+ battlerHoldEffect = gEnigmaBerries[battlerId].holdEffect;
+ battlerHoldEffectParam = gEnigmaBerries[battlerId].holdEffectParam;
+ }
+ else
+ {
+ battlerHoldEffect = ItemId_GetHoldEffect(gLastUsedItem);
+ battlerHoldEffectParam = ItemId_GetHoldEffectParam(gLastUsedItem);
+ }
+ switch (battlerHoldEffect)
+ {
+ case HOLD_EFFECT_CURE_PAR:
+ if (gBattleMons[battlerId].status1 & STATUS1_PARALYSIS)
+ {
+ gBattleMons[battlerId].status1 &= ~(STATUS1_PARALYSIS);
+ BattleScriptPushCursor();
+ gBattlescriptCurrInstr = BattleScript_BerryCureParRet;
+ effect = ITEM_STATUS_CHANGE;
+ }
+ break;
+ case HOLD_EFFECT_CURE_PSN:
+ if (gBattleMons[battlerId].status1 & STATUS1_PSN_ANY)
+ {
+ gBattleMons[battlerId].status1 &= ~(STATUS1_PSN_ANY | STATUS1_TOXIC_COUNTER);
+ BattleScriptPushCursor();
+ gBattlescriptCurrInstr = BattleScript_BerryCurePsnRet;
+ effect = ITEM_STATUS_CHANGE;
+ }
+ break;
+ case HOLD_EFFECT_CURE_BRN:
+ if (gBattleMons[battlerId].status1 & STATUS1_BURN)
+ {
+ gBattleMons[battlerId].status1 &= ~(STATUS1_BURN);
+ BattleScriptPushCursor();
+ gBattlescriptCurrInstr = BattleScript_BerryCureBrnRet;
+ effect = ITEM_STATUS_CHANGE;
+ }
+ break;
+ case HOLD_EFFECT_CURE_FRZ:
+ if (gBattleMons[battlerId].status1 & STATUS1_FREEZE)
+ {
+ gBattleMons[battlerId].status1 &= ~(STATUS1_FREEZE);
+ BattleScriptPushCursor();
+ gBattlescriptCurrInstr = BattleScript_BerryCureFrzRet;
+ effect = ITEM_STATUS_CHANGE;
+ }
+ break;
+ case HOLD_EFFECT_CURE_SLP:
+ if (gBattleMons[battlerId].status1 & STATUS1_SLEEP)
+ {
+ gBattleMons[battlerId].status1 &= ~(STATUS1_SLEEP);
+ gBattleMons[battlerId].status2 &= ~(STATUS2_NIGHTMARE);
+ BattleScriptPushCursor();
+ gBattlescriptCurrInstr = BattleScript_BerryCureSlpRet;
+ effect = ITEM_STATUS_CHANGE;
+ }
+ break;
+ case HOLD_EFFECT_CURE_CONFUSION:
+ if (gBattleMons[battlerId].status2 & STATUS2_CONFUSION)
+ {
+ gBattleMons[battlerId].status2 &= ~(STATUS2_CONFUSION);
+ BattleScriptPushCursor();
+ gBattlescriptCurrInstr = BattleScript_BerryCureConfusionRet;
+ effect = ITEM_EFFECT_OTHER;
+ }
+ break;
+ case HOLD_EFFECT_CURE_ATTRACT:
+ if (gBattleMons[battlerId].status2 & STATUS2_INFATUATION)
+ {
+ gBattleMons[battlerId].status2 &= ~(STATUS2_INFATUATION);
+ StringCopy(gBattleTextBuff1, gStatusConditionString_LoveJpn);
+ BattleScriptPushCursor();
+ gBattleCommunication[MULTISTRING_CHOOSER] = 0;
+ gBattlescriptCurrInstr = BattleScript_BerryCureChosenStatusRet;
+ effect = ITEM_EFFECT_OTHER;
+ }
+ break;
+ case HOLD_EFFECT_CURE_STATUS:
+ if (gBattleMons[battlerId].status1 & STATUS1_ANY || gBattleMons[battlerId].status2 & STATUS2_CONFUSION)
+ {
+ if (gBattleMons[battlerId].status1 & STATUS1_PSN_ANY)
+ {
+ StringCopy(gBattleTextBuff1, gStatusConditionString_PoisonJpn);
+ }
+ if (gBattleMons[battlerId].status1 & STATUS1_SLEEP)
+ {
+ gBattleMons[battlerId].status2 &= ~(STATUS2_NIGHTMARE);
+ StringCopy(gBattleTextBuff1, gStatusConditionString_SleepJpn);
+ }
+ if (gBattleMons[battlerId].status1 & STATUS1_PARALYSIS)
+ {
+ StringCopy(gBattleTextBuff1, gStatusConditionString_ParalysisJpn);
+ }
+ if (gBattleMons[battlerId].status1 & STATUS1_BURN)
+ {
+ StringCopy(gBattleTextBuff1, gStatusConditionString_BurnJpn);
+ }
+ if (gBattleMons[battlerId].status1 & STATUS1_FREEZE)
+ {
+ StringCopy(gBattleTextBuff1, gStatusConditionString_IceJpn);
+ }
+ if (gBattleMons[battlerId].status2 & STATUS2_CONFUSION)
+ {
+ StringCopy(gBattleTextBuff1, gStatusConditionString_ConfusionJpn);
+ }
+ gBattleMons[battlerId].status1 = 0;
+ gBattleMons[battlerId].status2 &= ~(STATUS2_CONFUSION);
+ BattleScriptPushCursor();
+ gBattleCommunication[MULTISTRING_CHOOSER] = 0;
+ gBattlescriptCurrInstr = BattleScript_BerryCureChosenStatusRet;
+ effect = ITEM_STATUS_CHANGE;
+ }
+ break;
+ case HOLD_EFFECT_RESTORE_STATS:
+ for (i = 0; i < NUM_BATTLE_STATS; ++i)
+ {
+ if (gBattleMons[battlerId].statStages[i] < 6)
+ {
+ gBattleMons[battlerId].statStages[i] = 6;
+ effect = ITEM_STATS_CHANGE;
+ }
+ }
+ if (effect)
+ {
+ gBattleScripting.battler = battlerId;
+ gPotentialItemEffectBattler = battlerId;
+ BattleScriptPushCursor();
+ gBattlescriptCurrInstr = BattleScript_WhiteHerbRet;
+ return effect;
+ }
+ break;
+ }
+ if (effect)
+ {
+ gBattleScripting.battler = battlerId;
+ gPotentialItemEffectBattler = battlerId;
+ gActiveBattler = battlerId;
+ BtlController_EmitSetMonData(0, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[gActiveBattler].status1);
+ MarkBattlerForControllerExec(gActiveBattler);
+ break;
+ }
+ }
+ break;
+ case ITEMEFFECT_KINGSROCK_SHELLBELL:
+ if (gBattleMoveDamage)
+ {
+ switch (atkHoldEffect)
+ {
+ case HOLD_EFFECT_FLINCH:
+ if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
+ && TARGET_TURN_DAMAGED
+ && (Random() % 100) < battlerHoldEffectParam
+ && gBattleMoves[gCurrentMove].flags & FLAG_KINGSROCK_AFFECTED
+ && gBattleMons[gBattlerTarget].hp)
+ {
+ gBattleCommunication[MOVE_EFFECT_BYTE] = MOVE_EFFECT_FLINCH;
+ BattleScriptPushCursor();
+ SetMoveEffect(0, 0);
+ BattleScriptPop();
+ }
+ break;
+ case HOLD_EFFECT_SHELL_BELL:
+ if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
+ && gSpecialStatuses[gBattlerTarget].dmg != 0
+ && gSpecialStatuses[gBattlerTarget].dmg != 0xFFFF
+ && gBattlerAttacker != gBattlerTarget
+ && gBattleMons[gBattlerAttacker].hp != gBattleMons[gBattlerAttacker].maxHP
+ && gBattleMons[gBattlerAttacker].hp != 0)
+ {
+ gLastUsedItem = atkItem;
+ gPotentialItemEffectBattler = gBattlerAttacker;
+ gBattleScripting.battler = gBattlerAttacker;
+ gBattleMoveDamage = (gSpecialStatuses[gBattlerTarget].dmg / atkHoldEffectParam) * -1;
+ if (gBattleMoveDamage == 0)
+ gBattleMoveDamage = -1;
+ gSpecialStatuses[gBattlerTarget].dmg = 0;
+ BattleScriptPushCursor();
+ gBattlescriptCurrInstr = BattleScript_ItemHealHP_Ret;
+ ++effect;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ return effect;
+}
+
+void ClearFuryCutterDestinyBondGrudge(u8 battlerId)
+{
+ gDisableStructs[battlerId].furyCutterCounter = 0;
+ gBattleMons[battlerId].status2 &= ~(STATUS2_DESTINY_BOND);
+ gStatuses3[battlerId] &= ~(STATUS3_GRUDGE);
+}
+
+void HandleAction_RunBattleScript(void) // identical to RunBattleScriptCommands
+{
+ if (!gBattleControllerExecFlags)
+ gBattleScriptingCommandsTable[*gBattlescriptCurrInstr]();
+}
+
+u8 GetMoveTarget(u16 move, u8 setTarget)
+{
+ u8 targetBattler = 0;
+ u8 moveTarget;
+ u8 side;
+
+ if (setTarget)
+ moveTarget = setTarget - 1;
+ else
+ moveTarget = gBattleMoves[move].target;
+ switch (moveTarget)
+ {
+ case MOVE_TARGET_SELECTED:
+ side = GetBattlerSide(gBattlerAttacker) ^ BIT_SIDE;
+ if (gSideTimers[side].followmeTimer && gBattleMons[gSideTimers[side].followmeTarget].hp)
+ targetBattler = gSideTimers[side].followmeTarget;
+ else
+ {
+ side = GetBattlerSide(gBattlerAttacker);
+ do
+ {
+ targetBattler = Random() % gBattlersCount;
+ } while (targetBattler == gBattlerAttacker || side == GetBattlerSide(targetBattler) || gAbsentBattlerFlags & gBitTable[targetBattler]);
+ if (gBattleMoves[move].type == TYPE_ELECTRIC
+ && AbilityBattleEffects(ABILITYEFFECT_COUNT_OTHER_SIDE, gBattlerAttacker, ABILITY_LIGHTNING_ROD, 0, 0)
+ && gBattleMons[targetBattler].ability != ABILITY_LIGHTNING_ROD)
+ {
+ targetBattler ^= BIT_FLANK;
+ RecordAbilityBattle(targetBattler, gBattleMons[targetBattler].ability);
+ gSpecialStatuses[targetBattler].lightningRodRedirected = 1;
+ }
+ }
+ break;
+ case MOVE_TARGET_DEPENDS:
+ case MOVE_TARGET_BOTH:
+ case MOVE_TARGET_FOES_AND_ALLY:
+ case MOVE_TARGET_OPPONENTS_FIELD:
+ targetBattler = GetBattlerAtPosition((GetBattlerPosition(gBattlerAttacker) & BIT_SIDE) ^ BIT_SIDE);
+ if (gAbsentBattlerFlags & gBitTable[targetBattler])
+ targetBattler ^= BIT_FLANK;
+ break;
+ case MOVE_TARGET_RANDOM:
+ side = GetBattlerSide(gBattlerAttacker) ^ BIT_SIDE;
+ if (gSideTimers[side].followmeTimer && gBattleMons[gSideTimers[side].followmeTarget].hp)
+ targetBattler = gSideTimers[side].followmeTarget;
+ else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && moveTarget & MOVE_TARGET_RANDOM)
+ {
+ if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER)
+ {
+ if (Random() & 1)
+ targetBattler = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
+ else
+ targetBattler = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT);
+ }
+ else
+ {
+ if (Random() & 1)
+ targetBattler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
+ else
+ targetBattler = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT);
+ }
+ if (gAbsentBattlerFlags & gBitTable[targetBattler])
+ targetBattler ^= BIT_FLANK;
+ }
+ else
+ targetBattler = GetBattlerAtPosition((GetBattlerPosition(gBattlerAttacker) & BIT_SIDE) ^ BIT_SIDE);
+ break;
+ case MOVE_TARGET_USER_OR_SELECTED:
+ case MOVE_TARGET_USER:
+ targetBattler = gBattlerAttacker;
+ break;
+ }
+ *(gBattleStruct->moveTarget + gBattlerAttacker) = targetBattler;
+ return targetBattler;
+}
+
+static bool32 HasObedientBitSet(u8 battlerId)
+{
+ if (GetBattlerSide(battlerId) == B_SIDE_OPPONENT
+ || (GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerId]], MON_DATA_SPECIES, NULL) != SPECIES_DEOXYS
+ && GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerId]], MON_DATA_SPECIES, NULL) != SPECIES_MEW))
+ return TRUE;
+ return GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerId]], MON_DATA_OBEDIENCE, NULL);
+}
+
+u8 IsMonDisobedient(void)
+{
+ s32 rnd;
+ s32 calc;
+ u8 obedienceLevel = 0;
+
+ if ((gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_POKEDUDE)) || GetBattlerSide(gBattlerAttacker) == B_SIDE_OPPONENT)
+ return 0;
+ if (HasObedientBitSet(gBattlerAttacker)) // only if species is Mew or Deoxys
+ {
+ if (!IsOtherTrainer(gBattleMons[gBattlerAttacker].otId, gBattleMons[gBattlerAttacker].otName) || FlagGet(FLAG_0x827))
+ return 0;
+ obedienceLevel = 10;
+ if (FlagGet(FLAG_0x821))
+ obedienceLevel = 30;
+ if (FlagGet(FLAG_0x823))
+ obedienceLevel = 50;
+ if (FlagGet(FLAG_0x825))
+ obedienceLevel = 70;
+ }
+ if (gBattleMons[gBattlerAttacker].level <= obedienceLevel)
+ return 0;
+ rnd = (Random() & 255);
+ calc = (gBattleMons[gBattlerAttacker].level + obedienceLevel) * rnd >> 8;
+ if (calc < obedienceLevel)
+ return 0;
+ // is not obedient
+ if (gCurrentMove == MOVE_RAGE)
+ gBattleMons[gBattlerAttacker].status2 &= ~(STATUS2_RAGE);
+ if (gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP && (gCurrentMove == MOVE_SNORE || gCurrentMove == MOVE_SLEEP_TALK))
+ {
+ gBattlescriptCurrInstr = BattleScript_IgnoresWhileAsleep;
+ return 1;
+ }
+ rnd = (Random() & 255);
+ calc = (gBattleMons[gBattlerAttacker].level + obedienceLevel) * rnd >> 8;
+ if (calc < obedienceLevel && gCurrentMove != MOVE_FOCUS_PUNCH) // Additional check for focus punch in FR
+ {
+ calc = CheckMoveLimitations(gBattlerAttacker, gBitTable[gCurrMovePos], 0xFF);
+ if (calc == 0xF) // all moves cannot be used
+ {
+ gBattleCommunication[MULTISTRING_CHOOSER] = Random() & 3;
+ gBattlescriptCurrInstr = BattleScript_MoveUsedLoafingAround;
+ return 1;
+ }
+ else // use a random move
+ {
+ do
+ gCurrMovePos = gChosenMovePos = Random() & 3;
+ while (gBitTable[gCurrMovePos] & calc);
+ gCalledMove = gBattleMons[gBattlerAttacker].moves[gCurrMovePos];
+ gBattlescriptCurrInstr = BattleScript_IgnoresAndUsesRandomMove;
+ gBattlerTarget = GetMoveTarget(gCalledMove, 0);
+ gHitMarker |= HITMARKER_x200000;
+ return 2;
+ }
+ }
+ else
+ {
+ obedienceLevel = gBattleMons[gBattlerAttacker].level - obedienceLevel;
+ calc = (Random() & 255);
+ if (calc < obedienceLevel && !(gBattleMons[gBattlerAttacker].status1 & STATUS1_ANY) && gBattleMons[gBattlerAttacker].ability != ABILITY_VITAL_SPIRIT && gBattleMons[gBattlerAttacker].ability != ABILITY_INSOMNIA)
+ {
+ // try putting asleep
+ int i;
+
+ for (i = 0; i < gBattlersCount; ++i)
+ if (gBattleMons[i].status2 & STATUS2_UPROAR)
+ break;
+ if (i == gBattlersCount)
+ {
+ gBattlescriptCurrInstr = BattleScript_IgnoresAndFallsAsleep;
+ return 1;
+ }
+ }
+ calc -= obedienceLevel;
+ if (calc < obedienceLevel)
+ {
+ gBattleMoveDamage = CalculateBaseDamage(&gBattleMons[gBattlerAttacker], &gBattleMons[gBattlerAttacker], MOVE_POUND, 0, 40, 0, gBattlerAttacker, gBattlerAttacker);
+ gBattlerTarget = gBattlerAttacker;
+ gBattlescriptCurrInstr = BattleScript_IgnoresAndHitsItself;
+ gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
+ return 2;
+ }
+ else
+ {
+ gBattleCommunication[MULTISTRING_CHOOSER] = Random() & 3;
+ gBattlescriptCurrInstr = BattleScript_MoveUsedLoafingAround;
+ return 1;
+ }
+ }
+}
diff --git a/src/battle_util2.c b/src/battle_util2.c
new file mode 100644
index 000000000..6a3f3525b
--- /dev/null
+++ b/src/battle_util2.c
@@ -0,0 +1,102 @@
+#include "global.h"
+#include "bg.h"
+#include "battle.h"
+#include "pokemon.h"
+#include "malloc.h"
+#include "trainer_tower.h"
+#include "battle_util2.h"
+
+void AllocateBattleResources(void)
+{
+ if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_TOWER)
+ InitTrainerTowerBattleStruct();
+ if (gBattleTypeFlags & BATTLE_TYPE_POKEDUDE)
+ {
+ void **ptr = gUnknown_3005EE0;
+ s32 i = 3;
+
+ do
+ *ptr++ = AllocZeroed(8);
+ while (--i >= 0);
+ }
+ gBattleStruct = AllocZeroed(sizeof(*gBattleStruct));
+ gBattleResources = AllocZeroed(sizeof(*gBattleResources));
+ gBattleResources->secretBase = AllocZeroed(sizeof(*gBattleResources->secretBase));
+ gBattleResources->flags = AllocZeroed(sizeof(*gBattleResources->flags));
+ gBattleResources->battleScriptsStack = AllocZeroed(sizeof(*gBattleResources->battleScriptsStack));
+ gBattleResources->battleCallbackStack = AllocZeroed(sizeof(*gBattleResources->battleCallbackStack));
+ gBattleResources->beforeLvlUp = AllocZeroed(sizeof(*gBattleResources->beforeLvlUp));
+ gBattleResources->ai = AllocZeroed(sizeof(*gBattleResources->ai));
+ gBattleResources->battleHistory = AllocZeroed(sizeof(*gBattleResources->battleHistory));
+ gBattleResources->AI_ScriptsStack = AllocZeroed(sizeof(*gBattleResources->AI_ScriptsStack));
+ gLinkBattleSendBuffer = AllocZeroed(BATTLE_BUFFER_LINK_SIZE);
+ gLinkBattleRecvBuffer = AllocZeroed(BATTLE_BUFFER_LINK_SIZE);
+ gUnknown_2022BB8 = AllocZeroed(0x2000);
+ gUnknown_2022BBC = AllocZeroed(0x1000);
+ SetBgTilemapBuffer(1, gUnknown_2022BBC);
+ SetBgTilemapBuffer(2, gUnknown_2022BBC);
+}
+
+void FreeBattleResources(void)
+{
+ if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_TOWER)
+ FreeTrainerTowerBattleStruct();
+ if (gBattleTypeFlags & BATTLE_TYPE_POKEDUDE)
+ {
+ void **ptr = gUnknown_3005EE0;
+ void *nullPtr = NULL;
+ s32 i = 3;
+
+ do
+ {
+ Free(*ptr);
+ *ptr++ = nullPtr;
+ }
+ while (--i >= 0);
+ }
+ if (gBattleResources != NULL)
+ {
+ FREE_AND_SET_NULL(gBattleStruct);
+ FREE_AND_SET_NULL(gBattleResources->secretBase);
+ FREE_AND_SET_NULL(gBattleResources->flags);
+ FREE_AND_SET_NULL(gBattleResources->battleScriptsStack);
+ FREE_AND_SET_NULL(gBattleResources->battleCallbackStack);
+ FREE_AND_SET_NULL(gBattleResources->beforeLvlUp);
+ FREE_AND_SET_NULL(gBattleResources->ai);
+ FREE_AND_SET_NULL(gBattleResources->battleHistory);
+ FREE_AND_SET_NULL(gBattleResources->AI_ScriptsStack);
+ FREE_AND_SET_NULL(gBattleResources);
+ FREE_AND_SET_NULL(gLinkBattleSendBuffer);
+ FREE_AND_SET_NULL(gLinkBattleRecvBuffer);
+ FREE_AND_SET_NULL(gUnknown_2022BB8);
+ FREE_AND_SET_NULL(gUnknown_2022BBC);
+ }
+}
+
+void AdjustFriendshipOnBattleFaint(u8 battlerId)
+{
+ u8 opposingBattlerId, opposingBattlerId2;
+
+ if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
+ {
+ opposingBattlerId = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
+ opposingBattlerId2 = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT);
+ if (gBattleMons[opposingBattlerId2].level > gBattleMons[opposingBattlerId].level)
+ opposingBattlerId = opposingBattlerId2;
+ }
+ else
+ {
+ opposingBattlerId = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
+ }
+ if (gBattleMons[opposingBattlerId].level > gBattleMons[battlerId].level)
+ {
+ if (gBattleMons[opposingBattlerId].level - gBattleMons[battlerId].level > 29)
+ AdjustFriendship(&gPlayerParty[gBattlerPartyIndexes[battlerId]], 9);
+ else
+ AdjustFriendship(&gPlayerParty[gBattlerPartyIndexes[battlerId]], 7);
+ }
+ else
+ {
+ AdjustFriendship(&gPlayerParty[gBattlerPartyIndexes[battlerId]], 7);
+ }
+}
diff --git a/src/mevent.c b/src/mevent.c
index fc0c1f2d4..d0d7f59aa 100644
--- a/src/mevent.c
+++ b/src/mevent.c
@@ -134,7 +134,7 @@ static void ResetTTDataBuffer(void)
{
memset(gDecompressionBuffer, 0, 0x2000);
gLinkType = 0x5502;
- sub_8009804();
+ OpenLink();
SetSuppressLinkErrorMessage(TRUE);
}
@@ -156,7 +156,7 @@ bool32 sub_81436EC(void)
static bool32 IsEReaderConnectionSane(void)
{
- if (sub_800AA48() && GetLinkPlayerCount_2() == 2)
+ if (IsLinkMaster() && GetLinkPlayerCount_2() == 2)
return TRUE;
return FALSE;
}
@@ -171,7 +171,7 @@ u32 sub_8143770(u8 * r4, u16 * r5)
switch (*r4)
{
case 0:
- if (sub_800AA48() && GetLinkPlayerCount_2() > 1)
+ if (IsLinkMaster() && GetLinkPlayerCount_2() > 1)
{
*r4 = 1;
;
@@ -193,7 +193,7 @@ u32 sub_8143770(u8 * r4, u16 * r5)
if (GetLinkPlayerCount_2() == 2)
{
PlaySE(SE_TOY_G);
- sub_800A5BC();
+ CheckShouldAdvanceLinkState();
*r5 = 0;
*r4 = 3;
}
diff --git a/src/mevent_server_helpers.c b/src/mevent_server_helpers.c
index 35efa68c8..6df5207f0 100644
--- a/src/mevent_server_helpers.c
+++ b/src/mevent_server_helpers.c
@@ -155,7 +155,7 @@ static bool32 mevent_send_func(struct mevent_srv_sub * svr)
switch (svr->seqno)
{
case 0:
- if (sub_800A4BC())
+ if (IsLinkTaskFinished())
{
header.ident = svr->sendIdent;
header.size = svr->sendSize;
@@ -167,7 +167,7 @@ static bool32 mevent_send_func(struct mevent_srv_sub * svr)
}
break;
case 1:
- if (sub_800A4BC())
+ if (IsLinkTaskFinished())
{
if (mevent_has_received(svr->sendPlayerNo))
{
@@ -189,7 +189,7 @@ static bool32 mevent_send_func(struct mevent_srv_sub * svr)
}
break;
case 2:
- if (sub_800A4BC())
+ if (IsLinkTaskFinished())
{
if (CalcCRC16WithTable(svr->sendBfr, svr->sendSize) != svr->sendCRC)
sub_80FA190();
diff --git a/src/pokemon.c b/src/pokemon.c
index 6f1df249f..5f26881ab 100644
--- a/src/pokemon.c
+++ b/src/pokemon.c
@@ -19,7 +19,6 @@
#include "evolution_scene.h"
#include "battle_message.h"
#include "battle_util.h"
-#include "battle_ai_script_commands.h"
#include "link.h"
#include "m4a.h"
#include "sound.h"
@@ -1792,7 +1791,7 @@ void CreateBoxMon(struct BoxPokemon *boxMon, u16 species, u8 level, u8 fixedIV,
if (gBaseStats[species].abilities[1])
{
value = personality & 1;
- SetBoxMonData(boxMon, MON_DATA_ALT_ABILITY, &value);
+ SetBoxMonData(boxMon, MON_DATA_ABILITY_NUM, &value);
}
GiveBoxMonInitialMoveset(boxMon);
@@ -1941,8 +1940,8 @@ void CreateBattleTowerMon(struct Pokemon *mon, struct BattleTowerPokemon *src)
SetMonData(mon, MON_DATA_SPEED_EV, &src->speedEV);
SetMonData(mon, MON_DATA_SPATK_EV, &src->spAttackEV);
SetMonData(mon, MON_DATA_SPDEF_EV, &src->spDefenseEV);
- value = src->altAbility;
- SetMonData(mon, MON_DATA_ALT_ABILITY, &value);
+ value = src->abilityNum;
+ SetMonData(mon, MON_DATA_ABILITY_NUM, &value);
value = src->hpIV;
SetMonData(mon, MON_DATA_HP_IV, &value);
value = src->attackIV;
@@ -1998,7 +1997,7 @@ void sub_803E23C(struct Pokemon *mon, struct BattleTowerPokemon *dest)
dest->speedIV = GetMonData(mon, MON_DATA_SPEED_IV, NULL);
dest->spAttackIV = GetMonData(mon, MON_DATA_SPATK_IV, NULL);
dest->spDefenseIV = GetMonData(mon, MON_DATA_SPDEF_IV, NULL);
- dest->altAbility = GetMonData(mon, MON_DATA_ALT_ABILITY, NULL);
+ dest->abilityNum = GetMonData(mon, MON_DATA_ABILITY_NUM, NULL);
dest->personality = GetMonData(mon, MON_DATA_PERSONALITY, NULL);
GetMonData(mon, MON_DATA_NICKNAME, dest->nickname);
}
@@ -2553,7 +2552,7 @@ s32 CalculateBaseDamage(struct BattlePokemon *attacker, struct BattlePokemon *de
damage /= 2;
// sunny
- if (gBattleWeather & WEATHER_SUNNY_ANY)
+ if (gBattleWeather & WEATHER_SUN_ANY)
{
switch (type)
{
@@ -2568,7 +2567,7 @@ s32 CalculateBaseDamage(struct BattlePokemon *attacker, struct BattlePokemon *de
}
// flash fire triggered
- if ((gBattleResources->flags->flags[battlerIdAtk] & UNKNOWN_FLAG_FLASH_FIRE) && type == TYPE_FIRE)
+ if ((gBattleResources->flags->flags[battlerIdAtk] & RESOURCE_FLAG_FLASH_FIRE) && type == TYPE_FIRE)
damage = (15 * damage) / 10;
}
@@ -2592,7 +2591,7 @@ u8 CountAliveMonsInBattle(u8 caseId)
case BATTLE_ALIVE_ATK_SIDE:
for (i = 0; i < 4; i++)
{
- if (GetBattlerSide(i) == GetBattlerSide(sBattler_AI) && !(gAbsentBattlerFlags & gBitTable[i]))
+ if (GetBattlerSide(i) == GetBattlerSide(gBattlerAttacker) && !(gAbsentBattlerFlags & gBitTable[i]))
retVal++;
}
break;
@@ -3100,8 +3099,8 @@ u32 GetBoxMonData(struct BoxPokemon *boxMon, s32 field, u8 *data)
case MON_DATA_IS_EGG:
retVal = substruct3->isEgg;
break;
- case MON_DATA_ALT_ABILITY:
- retVal = substruct3->altAbility;
+ case MON_DATA_ABILITY_NUM:
+ retVal = substruct3->abilityNum;
break;
case MON_DATA_COOL_RIBBON:
retVal = substruct3->coolRibbon;
@@ -3503,8 +3502,8 @@ void SetBoxMonData(struct BoxPokemon *boxMon, s32 field, const void *dataArg)
else
boxMon->isEgg = 0;
break;
- case MON_DATA_ALT_ABILITY:
- SET8(substruct3->altAbility);
+ case MON_DATA_ABILITY_NUM:
+ SET8(substruct3->abilityNum);
break;
case MON_DATA_COOL_RIBBON:
SET8(substruct3->coolRibbon);
@@ -3699,9 +3698,9 @@ u8 GetMonsStateToDoubles(void)
return (aliveCount > 1) ? PLAYER_HAS_TWO_USABLE_MONS : PLAYER_HAS_ONE_USABLE_MON;
}
-u8 GetAbilityBySpecies(u16 species, bool8 altAbility)
+u8 GetAbilityBySpecies(u16 species, bool8 abilityNum)
{
- if (altAbility)
+ if (abilityNum)
gLastUsedAbility = gBaseStats[species].abilities[1];
else
gLastUsedAbility = gBaseStats[species].abilities[0];
@@ -3712,8 +3711,8 @@ u8 GetAbilityBySpecies(u16 species, bool8 altAbility)
u8 GetMonAbility(struct Pokemon *mon)
{
u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL);
- u8 altAbility = GetMonData(mon, MON_DATA_ALT_ABILITY, NULL);
- return GetAbilityBySpecies(species, altAbility);
+ u8 abilityNum = GetMonData(mon, MON_DATA_ABILITY_NUM, NULL);
+ return GetAbilityBySpecies(species, abilityNum);
}
static void CreateSecretBaseEnemyParty(struct SecretBaseRecord *secretBaseRecord)
@@ -3859,11 +3858,11 @@ static void CopyPlayerPartyMonToBattleData(u8 battlerId, u8 partyIndex)
gBattleMons[battlerId].spAttack = GetMonData(&gPlayerParty[partyIndex], MON_DATA_SPATK, NULL);
gBattleMons[battlerId].spDefense = GetMonData(&gPlayerParty[partyIndex], MON_DATA_SPDEF, NULL);
gBattleMons[battlerId].isEgg = GetMonData(&gPlayerParty[partyIndex], MON_DATA_IS_EGG, NULL);
- gBattleMons[battlerId].altAbility = GetMonData(&gPlayerParty[partyIndex], MON_DATA_ALT_ABILITY, NULL);
+ gBattleMons[battlerId].abilityNum = GetMonData(&gPlayerParty[partyIndex], MON_DATA_ABILITY_NUM, NULL);
gBattleMons[battlerId].otId = GetMonData(&gPlayerParty[partyIndex], MON_DATA_OT_ID, NULL);
gBattleMons[battlerId].type1 = gBaseStats[gBattleMons[battlerId].species].type1;
gBattleMons[battlerId].type2 = gBaseStats[gBattleMons[battlerId].species].type2;
- gBattleMons[battlerId].ability = GetAbilityBySpecies(gBattleMons[battlerId].species, gBattleMons[battlerId].altAbility);
+ gBattleMons[battlerId].ability = GetAbilityBySpecies(gBattleMons[battlerId].species, gBattleMons[battlerId].abilityNum);
GetMonData(&gPlayerParty[partyIndex], MON_DATA_NICKNAME, nickname);
StringCopy10(gBattleMons[battlerId].nickname, nickname);
GetMonData(&gPlayerParty[partyIndex], MON_DATA_OT_NAME, gBattleMons[battlerId].otName);
@@ -4118,14 +4117,14 @@ bool8 PokemonUseItemEffects(struct Pokemon *pkmn, u16 item, u8 partyIndex, u8 mo
{
gAbsentBattlerFlags &= ~gBitTable[sp34];
CopyPlayerPartyMonToBattleData(sp34, pokemon_order_func(gBattlerPartyIndexes[sp34]));
- if (GetBattlerSide(gActiveBattler) == 0 && gBattleResults.unk4 < 255)
- gBattleResults.unk4++;
+ if (GetBattlerSide(gActiveBattler) == 0 && gBattleResults.numRevivesUsed < 255)
+ gBattleResults.numRevivesUsed++;
}
else
{
gAbsentBattlerFlags &= ~gBitTable[gActiveBattler ^ 2];
- if (GetBattlerSide(gActiveBattler) == 0 && gBattleResults.unk4 < 255)
- gBattleResults.unk4++;
+ if (GetBattlerSide(gActiveBattler) == 0 && gBattleResults.numRevivesUsed < 255)
+ gBattleResults.numRevivesUsed++;
}
}
}
@@ -4165,13 +4164,13 @@ bool8 PokemonUseItemEffects(struct Pokemon *pkmn, u16 item, u8 partyIndex, u8 mo
gBattleMons[sp34].hp = data;
if (!(r10 & 0x10) && GetBattlerSide(gActiveBattler) == 0)
{
- if (gBattleResults.unk3 < 255)
- gBattleResults.unk3++;
+ if (gBattleResults.numHealingItemsUsed < 255)
+ gBattleResults.numHealingItemsUsed++;
// I have to re-use this variable to match.
r5 = gActiveBattler;
gActiveBattler = sp34;
BtlController_EmitGetMonData(0, 0, 0);
- MarkBufferBankForExecution(gActiveBattler);
+ MarkBattlerForControllerExec(gActiveBattler);
gActiveBattler = r5;
}
}
@@ -4205,7 +4204,7 @@ bool8 PokemonUseItemEffects(struct Pokemon *pkmn, u16 item, u8 partyIndex, u8 mo
SetMonData(pkmn, MON_DATA_PP1 + r5, &data);
if (gMain.inBattle
&& sp34 != 4 && !(gBattleMons[sp34].status2 & 0x200000)
- && !(gDisableStructs[sp34].unk18_b & gBitTable[r5]))
+ && !(gDisableStructs[sp34].mimickedMoves & gBitTable[r5]))
gBattleMons[sp34].pp[r5] = data;
retVal = FALSE;
}
@@ -4230,7 +4229,7 @@ bool8 PokemonUseItemEffects(struct Pokemon *pkmn, u16 item, u8 partyIndex, u8 mo
SetMonData(pkmn, MON_DATA_PP1 + moveIndex, &data);
if (gMain.inBattle
&& sp34 != 4 && !(gBattleMons[sp34].status2 & 0x200000)
- && !(gDisableStructs[sp34].unk18_b & gBitTable[moveIndex]))
+ && !(gDisableStructs[sp34].mimickedMoves & gBitTable[moveIndex]))
gBattleMons[sp34].pp[moveIndex] = data;
retVal = FALSE;
}
@@ -4710,7 +4709,7 @@ bool8 PokemonUseItemEffects2(struct Pokemon *pkmn, u16 item, u8 partyIndex, u8 m
r5 = gActiveBattler;
gActiveBattler = sp34;
BtlController_EmitGetMonData(0, 0, 0);
- MarkBufferBankForExecution(gActiveBattler);
+ MarkBattlerForControllerExec(gActiveBattler);
gActiveBattler = r5;
}
}
@@ -6106,7 +6105,7 @@ const u8 *Battle_PrintStatBoosterEffectMessage(u16 itemId)
}
else
{
- sBattler_AI = gBattlerInMenuId;
+ gBattlerAttacker = gBattlerInMenuId;
BattleStringExpandPlaceholdersToDisplayedString(BattleText_GetPumped);
}
}
@@ -6114,7 +6113,7 @@ const u8 *Battle_PrintStatBoosterEffectMessage(u16 itemId)
if (itemEffect[3] & 0x80)
{
- sBattler_AI = gBattlerInMenuId;
+ gBattlerAttacker = gBattlerInMenuId;
BattleStringExpandPlaceholdersToDisplayedString(BattleText_MistShroud);
}
@@ -6962,10 +6961,10 @@ const u32 *GetMonFrontSpritePal(struct Pokemon *mon)
u16 species = GetMonData(mon, MON_DATA_SPECIES2, 0);
u32 otId = GetMonData(mon, MON_DATA_OT_ID, 0);
u32 personality = GetMonData(mon, MON_DATA_PERSONALITY, 0);
- return GetFrontSpritePalFromSpeciesAndPersonality(species, otId, personality);
+ return GetMonSpritePalFromSpeciesAndPersonality(species, otId, personality);
}
-const u32 *GetFrontSpritePalFromSpeciesAndPersonality(u16 species, u32 otId, u32 personality)
+const u32 *GetMonSpritePalFromSpeciesAndPersonality(u16 species, u32 otId, u32 personality)
{
u32 shinyValue;
@@ -7075,15 +7074,9 @@ void BoxMonRestorePP(struct BoxPokemon *boxMon)
}
}
-// SetMonPreventsSwitchingString
-void sub_8044348(void)
+void SetMonPreventsSwitchingString(void)
{
-#ifdef NONMATCHING
- gLastUsedAbility = gBattleStruct -> abilityPreventingSwitchout; // fixed from the original
-#else
- gLastUsedAbility = ((u8 *) gBattleStruct)[0xac]; // huh? why is this wrong?
-#endif
-
+ gLastUsedAbility = gBattleStruct -> abilityPreventingSwitchout;
gBattleTextBuff1[0] = B_BUFF_PLACEHOLDER_BEGIN;
gBattleTextBuff1[1] = B_BUFF_MON_NICK_WITH_PREFIX;
gBattleTextBuff1[2] = gBattleStruct->battlerPreventingSwitchout;
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..c75e8c583
--- /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 "data2.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/trainer_pokemon_sprites.c b/src/trainer_pokemon_sprites.c
index 25a8d4515..5f49173dc 100644
--- a/src/trainer_pokemon_sprites.c
+++ b/src/trainer_pokemon_sprites.c
@@ -104,7 +104,7 @@ void LoadPicPaletteByTagOrSlot(u16 species, u32 otId, u32 personality, u8 palett
if (paletteTag == 0xFFFF)
{
sCreatingSpriteTemplate.paletteTag = 0xFFFF;
- LoadCompressedPalette(GetFrontSpritePalFromSpeciesAndPersonality(species, otId, personality), 0x100 + paletteSlot * 0x10, 0x20);
+ LoadCompressedPalette(GetMonSpritePalFromSpeciesAndPersonality(species, otId, personality), 0x100 + paletteSlot * 0x10, 0x20);
}
else
{
@@ -130,7 +130,7 @@ void LoadPicPaletteByTagOrSlot(u16 species, u32 otId, u32 personality, u8 palett
void LoadPicPaletteBySlot(u16 species, u32 otId, u32 personality, u8 paletteSlot, bool8 isTrainer)
{
if (!isTrainer)
- LoadCompressedPalette(GetFrontSpritePalFromSpeciesAndPersonality(species, otId, personality), paletteSlot * 0x10, 0x20);
+ LoadCompressedPalette(GetMonSpritePalFromSpeciesAndPersonality(species, otId, personality), paletteSlot * 0x10, 0x20);
else
LoadCompressedPalette(gTrainerFrontPicPaletteTable[species].data, paletteSlot * 0x10, 0x20);
}
diff --git a/src/trainer_tower.c b/src/trainer_tower.c
index d80be781d..a1eedf2ac 100644
--- a/src/trainer_tower.c
+++ b/src/trainer_tower.c
@@ -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;