pokefirered/src/battle_main.c
2020-01-15 16:00:45 -05:00

4412 lines
164 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "global.h"
#include "battle.h"
#include "battle_anim.h"
#include "battle_ai_script_commands.h"
#include "battle_controllers.h"
#include "battle_interface.h"
#include "battle_main.h"
#include "battle_message.h"
#include "battle_scripts.h"
#include "battle_setup.h"
#include "battle_tower.h"
#include "battle_string_ids.h"
#include "berry.h"
#include "bg.h"
#include "data.h"
#include "decompress.h"
#include "dma3.h"
#include "event_data.h"
#include "evolution_scene.h"
#include "graphics.h"
#include "gpu_regs.h"
#include "help_system.h"
#include "item.h"
#include "link.h"
#include "link_rfu.h"
#include "load_save.h"
#include "main.h"
#include "malloc.h"
#include "m4a.h"
#include "palette.h"
#include "party_menu.h"
#include "pokeball.h"
#include "pokedex.h"
#include "pokemon.h"
#include "quest_log.h"
#include "random.h"
#include "roamer.h"
#include "safari_zone.h"
#include "scanline_effect.h"
#include "sound.h"
#include "sprite.h"
#include "string_util.h"
#include "strings.h"
#include "task.h"
#include "text.h"
#include "trig.h"
#include "vs_seeker.h"
#include "util.h"
#include "window.h"
#include "cable_club.h"
#include "constants/abilities.h"
#include "constants/battle_move_effects.h"
#include "constants/hold_effects.h"
#include "constants/items.h"
#include "constants/moves.h"
#include "constants/pokemon.h"
#include "constants/songs.h"
#include "constants/species.h"
#include "constants/trainers.h"
#include "constants/trainer_classes.h"
static void sub_80111EC(struct Sprite *sprite);
static void HandleAction_UseMove(void);
static void HandleAction_Switch(void);
static void HandleAction_UseItem(void);
static void HandleAction_Run(void);
static void HandleAction_WatchesCarefully(void);
static void HandleAction_SafariZoneBallThrow(void);
static void HandleAction_ThrowBait(void);
static void HandleAction_ThrowRock(void);
static void HandleAction_SafariZoneRun(void);
static void HandleAction_OldManBallThrow(void);
static void HandleAction_TryFinish(void);
static void HandleAction_NothingIsFainted(void);
static void HandleAction_ActionFinished(void);
static void HandleEndTurn_ContinueBattle(void);
static void HandleEndTurn_BattleWon(void);
static void HandleEndTurn_BattleLost(void);
static void HandleEndTurn_RanFromBattle(void);
static void HandleEndTurn_MonFled(void);
static void HandleEndTurn_FinishBattle(void);
static void CB2_InitBattleInternal(void);
static void CB2_PreInitMultiBattle(void);
static void CB2_HandleStartMultiBattle(void);
static u8 CreateNPCTrainerParty(struct Pokemon *party, u16 trainerNum);
static void CB2_HandleStartBattle(void);
static void TryCorrectShedinjaLanguage(struct Pokemon *mon);
static void BattleMainCB1(void);
static void CB2_QuitPokeDudeBattle(void);
static void sub_80111FC(struct Sprite *sprite);
static void sub_8011B94(void);
static void sub_8011BB0(void);
static void SpriteCB_MoveWildMonToRight(struct Sprite *sprite);
static void SpriteCB_WildMonShowHealthbox(struct Sprite *sprite);
static void sub_8011E3C(struct Sprite *sprite);
static void SpriteCB_AnimFaintOpponent(struct Sprite *sprite);
static void sub_8012060(struct Sprite *sprite);
static void oac_poke_ally_(struct Sprite *sprite);
static void SpriteCallbackDummy3(struct Sprite *sprite);
static void SpriteCB_BounceEffect(struct Sprite *sprite);
static void sub_8012398(struct Sprite *sprite);
static void BattleStartClearSetData(void);
static void BattleIntroGetMonsData(void);
static void TurnValuesCleanUp(bool8 var0);
static void SpecialStatusesClear(void);
static void BattleIntroPrepareBackgroundSlide(void);
static void BattleIntroDrawTrainersOrMonsSprites(void);
static void BattleIntroDrawPartySummaryScreens(void);
static void BattleIntroPrintTrainerWantsToBattle(void);
static void BattleIntroPrintWildMonAttacked(void);
static void BattleIntroPrintOpponentSendsOut(void);
static void BattleIntroPrintPlayerSendsOut(void);
static void BattleIntroRecordMonsToDex(void);
static void BattleIntroOpponentSendsOutMonAnimation(void);
static void BattleIntroPlayerSendsOutMonAnimation(void);
static void TryDoEventsBeforeFirstTurn(void);
static void HandleTurnActionSelectionState(void);
static void RunTurnActionsFunctions(void);
static void SetActionsAndBattlersTurnOrder(void);
static void CheckFocusPunch_ClearVarsBeforeTurnStarts(void);
static void HandleEndTurn_FinishBattle(void);
static void FreeResetData_ReturnToOvOrDoEvolutions(void);
static void ReturnFromBattleToOverworld(void);
static void TryEvolvePokemon(void);
static void WaitForEvoSceneToFinish(void);
EWRAM_DATA u16 gBattle_BG0_X = 0;
EWRAM_DATA u16 gBattle_BG0_Y = 0;
EWRAM_DATA u16 gBattle_BG1_X = 0;
EWRAM_DATA u16 gBattle_BG1_Y = 0;
EWRAM_DATA u16 gBattle_BG2_X = 0;
EWRAM_DATA u16 gBattle_BG2_Y = 0;
EWRAM_DATA u16 gBattle_BG3_X = 0;
EWRAM_DATA u16 gBattle_BG3_Y = 0;
EWRAM_DATA u16 gBattle_WIN0H = 0;
EWRAM_DATA u16 gBattle_WIN0V = 0;
EWRAM_DATA u16 gBattle_WIN1H = 0;
EWRAM_DATA u16 gBattle_WIN1V = 0;
EWRAM_DATA u8 gDisplayedStringBattle[300] = {0};
EWRAM_DATA u8 gBattleTextBuff1[TEXT_BUFF_ARRAY_COUNT] = {0};
EWRAM_DATA u8 gBattleTextBuff2[TEXT_BUFF_ARRAY_COUNT] = {0};
EWRAM_DATA u8 gBattleTextBuff3[TEXT_BUFF_ARRAY_COUNT] = {0};
static EWRAM_DATA u32 gUnknown_2022AE8[25] = {0};
EWRAM_DATA u32 gBattleTypeFlags = 0;
EWRAM_DATA u8 gBattleTerrain = 0;
EWRAM_DATA u32 gUnknown_2022B54 = 0;
EWRAM_DATA struct UnknownPokemonStruct4 gMultiPartnerParty[3] = {0};
EWRAM_DATA u8 *gUnknown_2022BB8 = NULL;
EWRAM_DATA u8 *gUnknown_2022BBC = NULL;
EWRAM_DATA u16 *gUnknown_2022BC0 = NULL;
EWRAM_DATA u8 gBattleBufferA[MAX_BATTLERS_COUNT][0x200] = {0};
EWRAM_DATA u8 gBattleBufferB[MAX_BATTLERS_COUNT][0x200] = {0};
EWRAM_DATA u8 gActiveBattler = 0;
EWRAM_DATA u32 gBattleControllerExecFlags = 0;
EWRAM_DATA u8 gBattlersCount = 0;
EWRAM_DATA u16 gBattlerPartyIndexes[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA u8 gBattlerPositions[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA u8 gActionsByTurnOrder[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA u8 gBattlerByTurnOrder[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA u8 gCurrentTurnActionNumber = 0;
EWRAM_DATA u8 gCurrentActionFuncId = 0;
EWRAM_DATA struct BattlePokemon gBattleMons[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA u8 gBattlerSpriteIds[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA u8 gCurrMovePos = 0;
EWRAM_DATA u8 gChosenMovePos = 0;
EWRAM_DATA u16 gCurrentMove = 0;
EWRAM_DATA u16 gChosenMove = 0;
EWRAM_DATA u16 gCalledMove = 0;
EWRAM_DATA s32 gBattleMoveDamage = 0;
EWRAM_DATA s32 gHpDealt = 0;
EWRAM_DATA s32 gTakenDmg[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA u16 gLastUsedItem = 0;
EWRAM_DATA u8 gLastUsedAbility = 0;
EWRAM_DATA u8 gBattlerAttacker = 0;
EWRAM_DATA u8 gBattlerTarget = 0;
EWRAM_DATA u8 gBattlerFainted = 0;
EWRAM_DATA u8 gEffectBattler = 0;
EWRAM_DATA u8 gPotentialItemEffectBattler = 0;
EWRAM_DATA u8 gAbsentBattlerFlags = 0;
EWRAM_DATA u8 gCritMultiplier = 0;
EWRAM_DATA u8 gMultiHitCounter = 0;
EWRAM_DATA const u8 *gBattlescriptCurrInstr = NULL;
EWRAM_DATA u32 gUnusedBattleMainVar = 0;
EWRAM_DATA u8 gChosenActionByBattler[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA const u8 *gSelectionBattleScripts[MAX_BATTLERS_COUNT] = {NULL};
EWRAM_DATA u16 gLastPrintedMoves[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA u16 gLastMoves[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA u16 gLastLandedMoves[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA u16 gLastHitByType[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA u16 gLastResultingMoves[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA u16 gLockedMoves[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA u8 gLastHitBy[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA u16 gChosenMoveByBattler[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA u8 gMoveResultFlags = 0;
EWRAM_DATA u32 gHitMarker = 0;
static EWRAM_DATA u8 gUnknown_2023DD4[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA u8 gTakenDmgByBattler[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA u8 gUnknown_2023DDC = 0;
EWRAM_DATA u16 gSideStatuses[2] = {0};
EWRAM_DATA struct SideTimer gSideTimers[2] = {0};
EWRAM_DATA u32 gStatuses3[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA struct DisableStruct gDisableStructs[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA u16 gPauseCounterBattle = 0;
EWRAM_DATA u16 gPaydayMoney = 0;
EWRAM_DATA u16 gRandomTurnNumber = 0;
EWRAM_DATA u8 gBattleCommunication[BATTLE_COMMUNICATION_ENTRIES_COUNT] = {0};
EWRAM_DATA u8 gBattleOutcome = 0;
EWRAM_DATA struct ProtectStruct gProtectStructs[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA struct SpecialStatus gSpecialStatuses[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA u16 gBattleWeather = 0;
EWRAM_DATA struct WishFutureKnock gWishFutureKnock = {0};
EWRAM_DATA u16 gIntroSlideFlags = 0;
EWRAM_DATA u8 gSentPokesToOpponent[2] = {0};
EWRAM_DATA u16 gDynamicBasePower = 0;
EWRAM_DATA u16 gExpShareExp = 0;
EWRAM_DATA struct BattleEnigmaBerry gEnigmaBerries[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA struct BattleScripting gBattleScripting = {0};
EWRAM_DATA struct BattleStruct *gBattleStruct = NULL;
EWRAM_DATA u8 *gLinkBattleSendBuffer = NULL;
EWRAM_DATA u8 *gLinkBattleRecvBuffer = NULL;
EWRAM_DATA struct BattleResources *gBattleResources = NULL;
EWRAM_DATA u8 gActionSelectionCursor[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA u8 gMoveSelectionCursor[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA u8 gBattlerStatusSummaryTaskId[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA u8 gBattlerInMenuId = 0;
EWRAM_DATA bool8 gDoingBattleAnim = FALSE;
EWRAM_DATA u32 gTransformedPersonalities[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA struct BattleSpriteData *gBattleSpritesDataPtr = NULL;
EWRAM_DATA struct MonSpritesGfx *gMonSpritesGfxPtr = NULL;
EWRAM_DATA u16 gBattleMovePower = 0;
EWRAM_DATA u16 gMoveToLearn = 0;
EWRAM_DATA u8 gBattleMonForms[MAX_BATTLERS_COUNT] = {0};
void (*gPreBattleCallback1)(void);
void (*gBattleMainFunc)(void);
struct BattleResults gBattleResults;
u8 gLeveledUpInBattle;
void (*gBattlerControllerFuncs[MAX_BATTLERS_COUNT])(void);
u8 gHealthboxSpriteIds[MAX_BATTLERS_COUNT];
u8 gMultiUsePlayerCursor;
u8 gNumberOfMovesToChoose;
u8 gUnknown_3004FFC[MAX_BATTLERS_COUNT];
static const struct ScanlineEffectParams sIntroScanlineParams16Bit =
{
&REG_BG3HOFS, SCANLINE_EFFECT_DMACNT_16BIT, 1
};
const struct SpriteTemplate gUnknown_824EFF0 =
{
.tileTag = 0,
.paletteTag = 0,
.oam = &gDummyOamData,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = sub_80111EC,
};
static const u8 sText_ShedinjaJpnName[] = _("ヌケニン"); // Nukenin
const struct OamData gOamData_824F010 =
{
.y = 0,
.affineMode = ST_OAM_AFFINE_NORMAL,
.objMode = ST_OAM_OBJ_NORMAL,
.bpp = ST_OAM_4BPP,
.shape = SPRITE_SHAPE(64x64),
.x = 0,
.size = SPRITE_SIZE(64x64),
.tileNum = 0,
.priority = 2,
.paletteNum = 0,
.affineParam = 0,
};
const struct OamData gOamData_824F018 =
{
.y = 0,
.affineMode = ST_OAM_AFFINE_NORMAL,
.objMode = ST_OAM_OBJ_NORMAL,
.bpp = ST_OAM_4BPP,
.shape = SPRITE_SHAPE(64x64),
.x = 0,
.size = SPRITE_SIZE(64x64),
.tileNum = 0,
.priority = 2,
.paletteNum = 2,
.affineParam = 0,
};
// not used
static const union AnimCmd gUnknown_824F020[] =
{
ANIMCMD_FRAME(0, 5),
ANIMCMD_JUMP(0),
};
// not used
static const union AnimCmd *const gUnknown_824F028[] =
{
gUnknown_824F020,
};
// not used
static const union AffineAnimCmd gUnknown_824F02C[] =
{
AFFINEANIMCMD_FRAME(-0x10, 0x0, 0, 4),
AFFINEANIMCMD_FRAME(0x0, 0x0, 0, 0x3C),
AFFINEANIMCMD_JUMP(1),
};
// not used
static const union AffineAnimCmd *const gUnknown_824F044[] =
{
gUnknown_824F02C,
};
static const s8 gUnknown_824F048[] = { -32, -16, -16, -32, -32, 0, 0, 0 };
// format: attacking type, defending type, damage multiplier
// the multiplier is a (decimal) fixed-point number:
// 20 is ×2.0 TYPE_MUL_SUPER_EFFECTIVE
// 10 is ×1.0 TYPE_MUL_NORMAL
// 05 is ×0.5 TYPE_MUL_NOT_EFFECTIVE
// 00 is ×0.0 TYPE_MUL_NO_EFFECT
const u8 gTypeEffectiveness[336] =
{
TYPE_NORMAL, TYPE_ROCK, TYPE_MUL_NOT_EFFECTIVE,
TYPE_NORMAL, TYPE_STEEL, TYPE_MUL_NOT_EFFECTIVE,
TYPE_FIRE, TYPE_FIRE, TYPE_MUL_NOT_EFFECTIVE,
TYPE_FIRE, TYPE_WATER, TYPE_MUL_NOT_EFFECTIVE,
TYPE_FIRE, TYPE_GRASS, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_FIRE, TYPE_ICE, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_FIRE, TYPE_BUG, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_FIRE, TYPE_ROCK, TYPE_MUL_NOT_EFFECTIVE,
TYPE_FIRE, TYPE_DRAGON, TYPE_MUL_NOT_EFFECTIVE,
TYPE_FIRE, TYPE_STEEL, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_WATER, TYPE_FIRE, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_WATER, TYPE_WATER, TYPE_MUL_NOT_EFFECTIVE,
TYPE_WATER, TYPE_GRASS, TYPE_MUL_NOT_EFFECTIVE,
TYPE_WATER, TYPE_GROUND, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_WATER, TYPE_ROCK, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_WATER, TYPE_DRAGON, TYPE_MUL_NOT_EFFECTIVE,
TYPE_ELECTRIC, TYPE_WATER, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_ELECTRIC, TYPE_ELECTRIC, TYPE_MUL_NOT_EFFECTIVE,
TYPE_ELECTRIC, TYPE_GRASS, TYPE_MUL_NOT_EFFECTIVE,
TYPE_ELECTRIC, TYPE_GROUND, TYPE_MUL_NO_EFFECT,
TYPE_ELECTRIC, TYPE_FLYING, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_ELECTRIC, TYPE_DRAGON, TYPE_MUL_NOT_EFFECTIVE,
TYPE_GRASS, TYPE_FIRE, TYPE_MUL_NOT_EFFECTIVE,
TYPE_GRASS, TYPE_WATER, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_GRASS, TYPE_GRASS, TYPE_MUL_NOT_EFFECTIVE,
TYPE_GRASS, TYPE_POISON, TYPE_MUL_NOT_EFFECTIVE,
TYPE_GRASS, TYPE_GROUND, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_GRASS, TYPE_FLYING, TYPE_MUL_NOT_EFFECTIVE,
TYPE_GRASS, TYPE_BUG, TYPE_MUL_NOT_EFFECTIVE,
TYPE_GRASS, TYPE_ROCK, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_GRASS, TYPE_DRAGON, TYPE_MUL_NOT_EFFECTIVE,
TYPE_GRASS, TYPE_STEEL, TYPE_MUL_NOT_EFFECTIVE,
TYPE_ICE, TYPE_WATER, TYPE_MUL_NOT_EFFECTIVE,
TYPE_ICE, TYPE_GRASS, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_ICE, TYPE_ICE, TYPE_MUL_NOT_EFFECTIVE,
TYPE_ICE, TYPE_GROUND, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_ICE, TYPE_FLYING, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_ICE, TYPE_DRAGON, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_ICE, TYPE_STEEL, TYPE_MUL_NOT_EFFECTIVE,
TYPE_ICE, TYPE_FIRE, TYPE_MUL_NOT_EFFECTIVE,
TYPE_FIGHTING, TYPE_NORMAL, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_FIGHTING, TYPE_ICE, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_FIGHTING, TYPE_POISON, TYPE_MUL_NOT_EFFECTIVE,
TYPE_FIGHTING, TYPE_FLYING, TYPE_MUL_NOT_EFFECTIVE,
TYPE_FIGHTING, TYPE_PSYCHIC, TYPE_MUL_NOT_EFFECTIVE,
TYPE_FIGHTING, TYPE_BUG, TYPE_MUL_NOT_EFFECTIVE,
TYPE_FIGHTING, TYPE_ROCK, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_FIGHTING, TYPE_DARK, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_FIGHTING, TYPE_STEEL, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_POISON, TYPE_GRASS, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_POISON, TYPE_POISON, TYPE_MUL_NOT_EFFECTIVE,
TYPE_POISON, TYPE_GROUND, TYPE_MUL_NOT_EFFECTIVE,
TYPE_POISON, TYPE_ROCK, TYPE_MUL_NOT_EFFECTIVE,
TYPE_POISON, TYPE_GHOST, TYPE_MUL_NOT_EFFECTIVE,
TYPE_POISON, TYPE_STEEL, TYPE_MUL_NO_EFFECT,
TYPE_GROUND, TYPE_FIRE, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_GROUND, TYPE_ELECTRIC, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_GROUND, TYPE_GRASS, TYPE_MUL_NOT_EFFECTIVE,
TYPE_GROUND, TYPE_POISON, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_GROUND, TYPE_FLYING, TYPE_MUL_NO_EFFECT,
TYPE_GROUND, TYPE_BUG, TYPE_MUL_NOT_EFFECTIVE,
TYPE_GROUND, TYPE_ROCK, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_GROUND, TYPE_STEEL, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_FLYING, TYPE_ELECTRIC, TYPE_MUL_NOT_EFFECTIVE,
TYPE_FLYING, TYPE_GRASS, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_FLYING, TYPE_FIGHTING, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_FLYING, TYPE_BUG, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_FLYING, TYPE_ROCK, TYPE_MUL_NOT_EFFECTIVE,
TYPE_FLYING, TYPE_STEEL, TYPE_MUL_NOT_EFFECTIVE,
TYPE_PSYCHIC, TYPE_FIGHTING, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_PSYCHIC, TYPE_POISON, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_PSYCHIC, TYPE_PSYCHIC, TYPE_MUL_NOT_EFFECTIVE,
TYPE_PSYCHIC, TYPE_DARK, TYPE_MUL_NO_EFFECT,
TYPE_PSYCHIC, TYPE_STEEL, TYPE_MUL_NOT_EFFECTIVE,
TYPE_BUG, TYPE_FIRE, TYPE_MUL_NOT_EFFECTIVE,
TYPE_BUG, TYPE_GRASS, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_BUG, TYPE_FIGHTING, TYPE_MUL_NOT_EFFECTIVE,
TYPE_BUG, TYPE_POISON, TYPE_MUL_NOT_EFFECTIVE,
TYPE_BUG, TYPE_FLYING, TYPE_MUL_NOT_EFFECTIVE,
TYPE_BUG, TYPE_PSYCHIC, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_BUG, TYPE_GHOST, TYPE_MUL_NOT_EFFECTIVE,
TYPE_BUG, TYPE_DARK, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_BUG, TYPE_STEEL, TYPE_MUL_NOT_EFFECTIVE,
TYPE_ROCK, TYPE_FIRE, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_ROCK, TYPE_ICE, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_ROCK, TYPE_FIGHTING, TYPE_MUL_NOT_EFFECTIVE,
TYPE_ROCK, TYPE_GROUND, TYPE_MUL_NOT_EFFECTIVE,
TYPE_ROCK, TYPE_FLYING, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_ROCK, TYPE_BUG, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_ROCK, TYPE_STEEL, TYPE_MUL_NOT_EFFECTIVE,
TYPE_GHOST, TYPE_NORMAL, TYPE_MUL_NO_EFFECT,
TYPE_GHOST, TYPE_PSYCHIC, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_GHOST, TYPE_DARK, TYPE_MUL_NOT_EFFECTIVE,
TYPE_GHOST, TYPE_STEEL, TYPE_MUL_NOT_EFFECTIVE,
TYPE_GHOST, TYPE_GHOST, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_DRAGON, TYPE_DRAGON, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_DRAGON, TYPE_STEEL, TYPE_MUL_NOT_EFFECTIVE,
TYPE_DARK, TYPE_FIGHTING, TYPE_MUL_NOT_EFFECTIVE,
TYPE_DARK, TYPE_PSYCHIC, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_DARK, TYPE_GHOST, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_DARK, TYPE_DARK, TYPE_MUL_NOT_EFFECTIVE,
TYPE_DARK, TYPE_STEEL, TYPE_MUL_NOT_EFFECTIVE,
TYPE_STEEL, TYPE_FIRE, TYPE_MUL_NOT_EFFECTIVE,
TYPE_STEEL, TYPE_WATER, TYPE_MUL_NOT_EFFECTIVE,
TYPE_STEEL, TYPE_ELECTRIC, TYPE_MUL_NOT_EFFECTIVE,
TYPE_STEEL, TYPE_ICE, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_STEEL, TYPE_ROCK, TYPE_MUL_SUPER_EFFECTIVE,
TYPE_STEEL, TYPE_STEEL, TYPE_MUL_NOT_EFFECTIVE,
TYPE_FORESIGHT, TYPE_FORESIGHT, TYPE_MUL_NO_EFFECT,
TYPE_NORMAL, TYPE_GHOST, TYPE_MUL_NO_EFFECT,
TYPE_FIGHTING, TYPE_GHOST, TYPE_MUL_NO_EFFECT,
TYPE_ENDTABLE, TYPE_ENDTABLE, TYPE_MUL_NO_EFFECT
};
const u8 gTypeNames[][TYPE_NAME_LENGTH + 1] =
{
_("NORMAL"),
_("FIGHT"),
_("FLYING"),
_("POISON"),
_("GROUND"),
_("ROCK"),
_("BUG"),
_("GHOST"),
_("STEEL"),
_("???"),
_("FIRE"),
_("WATER"),
_("GRASS"),
_("ELECTR"),
_("PSYCHC"),
_("ICE"),
_("DRAGON"),
_("DARK"),
};
// This is a factor in how much money you get for beating a trainer.
const struct TrainerMoney gTrainerMoneyTable[] =
{
{ CLASS_LEADER_2, 25 },
{ CLASS_ELITE_FOUR_2, 25 },
{ CLASS_PKMN_PROF, 25 },
{ CLASS_RIVAL, 4 },
{ CLASS_RIVAL_2, 9 },
{ CLASS_CHAMPION_2, 25 },
{ CLASS_YOUNGSTER_2, 4 },
{ CLASS_BUG_CATCHER_2, 3 },
{ CLASS_HIKER_2, 9 },
{ CLASS_BIRD_KEEPER_2, 6 },
{ CLASS_PICNICKER_2, 5 },
{ CLASS_SUPER_NERD, 6 },
{ CLASS_FISHERMAN_2, 9 },
{ CLASS_TEAM_ROCKET, 8 },
{ CLASS_LASS_2, 4 },
{ CLASS_BEAUTY_2, 18 },
{ CLASS_BLACK_BELT_2, 6 },
{ CLASS_CUE_BALL, 6 },
{ CLASS_CHANNELER, 8 },
{ CLASS_ROCKER, 6 },
{ CLASS_GENTLEMAN_2, 18 },
{ CLASS_BURGLAR, 22 },
{ CLASS_SWIMMER_MALE_2, 1 },
{ CLASS_ENGINEER, 12 },
{ CLASS_JUGGLER, 10 },
{ CLASS_SAILOR_2, 8 },
{ CLASS_COOLTRAINER_2, 9 },
{ CLASS_POKEMANIAC_2, 12 },
{ CLASS_TAMER, 10 },
{ CLASS_CAMPER_2, 5 },
{ CLASS_PSYCHIC_2, 5 },
{ CLASS_BIKER, 5 },
{ CLASS_GAMER, 18 },
{ CLASS_SCIENTIST, 12 },
{ CLASS_CRUSH_GIRL, 6 },
{ CLASS_TUBER_3, 1 },
{ CLASS_PKMN_BREEDER_2, 7 },
{ CLASS_PKMN_RANGER_2, 9 },
{ CLASS_AROMA_LADY_2, 7 },
{ CLASS_RUIN_MANIAC_2, 12 },
{ CLASS_LADY_2, 50 },
{ CLASS_PAINTER, 4 },
{ CLASS_TWINS_2, 3 },
{ CLASS_YOUNG_COUPLE_2, 7 },
{ CLASS_SIS_AND_BRO_2, 1 },
{ CLASS_COOL_COUPLE, 6 },
{ CLASS_CRUSH_KIN, 6 },
{ CLASS_SWIMMER_FEMALE_2, 1 },
{ CLASS_PLAYER, 1 },
{ CLASS_LEADER, 25 },
{ CLASS_ELITE_FOUR, 25 },
{ CLASS_LASS, 4 },
{ CLASS_YOUNGSTER, 4 },
{ CLASS_PKMN_TRAINER_3, 15 },
{ CLASS_HIKER, 10 },
{ CLASS_BEAUTY, 20 },
{ CLASS_FISHERMAN, 10 },
{ CLASS_LADY, 50 },
{ CLASS_TRIATHLETE, 10 },
{ CLASS_TEAM_AQUA, 5 },
{ CLASS_TWINS, 3 },
{ CLASS_SWIMMER_FEMALE, 2 },
{ CLASS_BUG_CATCHER, 4 },
{ CLASS_SCHOOL_KID, 5 },
{ CLASS_RICH_BOY, 50 },
{ CLASS_SR_AND_JR, 4 },
{ CLASS_BLACK_BELT, 8 },
{ CLASS_TUBER, 1 },
{ CLASS_HEX_MANIAC, 6 },
{ CLASS_PKMN_BREEDER, 10 },
{ CLASS_TEAM_MAGMA, 5 },
{ CLASS_INTERVIEWER, 12 },
{ CLASS_TUBER_2, 1 },
{ CLASS_YOUNG_COUPLE, 8 },
{ CLASS_GUITARIST, 8 },
{ CLASS_GENTLEMAN, 20 },
{ CLASS_CHAMPION, 50 },
{ CLASS_MAGMA_LEADER, 20 },
{ CLASS_BATTLE_GIRL, 6 },
{ CLASS_SWIMMER_MALE, 2 },
{ CLASS_POKEFAN, 20 },
{ CLASS_EXPERT, 10 },
{ CLASS_DRAGON_TAMER, 12 },
{ CLASS_BIRD_KEEPER, 8 },
{ CLASS_NINJA_BOY, 3 },
{ CLASS_PARASOL_LADY, 10 },
{ CLASS_BUG_MANIAC, 15 },
{ CLASS_SAILOR, 8 },
{ CLASS_COLLECTOR, 15 },
{ CLASS_PKMN_RANGER, 12 },
{ CLASS_MAGMA_ADMIN, 10 },
{ CLASS_AROMA_LADY, 10 },
{ CLASS_RUIN_MANIAC, 15 },
{ CLASS_COOLTRAINER, 12 },
{ CLASS_POKEMANIAC, 15 },
{ CLASS_KINDLER, 8 },
{ CLASS_CAMPER, 4 },
{ CLASS_PICNICKER, 4 },
{ CLASS_PSYCHIC, 6 },
{ CLASS_SIS_AND_BRO, 3 },
{ CLASS_OLD_COUPLE, 10 },
{ CLASS_AQUA_ADMIN, 10 },
{ CLASS_AQUA_LEADER, 20 },
{ CLASS_BOSS, 25 },
{ 0xFF, 5 },
};
#include "data/text/abilities.h"
static void (*const sTurnActionsFuncsTable[])(void) =
{
[B_ACTION_USE_MOVE] = HandleAction_UseMove,
[B_ACTION_USE_ITEM] = HandleAction_UseItem,
[B_ACTION_SWITCH] = HandleAction_Switch,
[B_ACTION_RUN] = HandleAction_Run,
[B_ACTION_SAFARI_WATCH_CAREFULLY] = HandleAction_WatchesCarefully,
[B_ACTION_SAFARI_BALL] = HandleAction_SafariZoneBallThrow,
[B_ACTION_SAFARI_POKEBLOCK] = HandleAction_ThrowBait,
[B_ACTION_SAFARI_GO_NEAR] = HandleAction_ThrowRock,
[B_ACTION_SAFARI_RUN] = HandleAction_SafariZoneRun,
[B_ACTION_OLDMAN_THROW] = HandleAction_OldManBallThrow,
[B_ACTION_EXEC_SCRIPT] = HandleAction_RunBattleScript,
[B_ACTION_TRY_FINISH] = HandleAction_TryFinish,
[B_ACTION_FINISHED] = HandleAction_ActionFinished,
[B_ACTION_NOTHING_FAINTED] = HandleAction_NothingIsFainted,
};
static void (*const sEndTurnFuncsTable[])(void) =
{
[0] = HandleEndTurn_ContinueBattle, //B_OUTCOME_NONE?
[B_OUTCOME_WON] = HandleEndTurn_BattleWon,
[B_OUTCOME_LOST] = HandleEndTurn_BattleLost,
[B_OUTCOME_DREW] = HandleEndTurn_BattleLost,
[B_OUTCOME_RAN] = HandleEndTurn_RanFromBattle,
[B_OUTCOME_PLAYER_TELEPORTED] = HandleEndTurn_FinishBattle,
[B_OUTCOME_MON_FLED] = HandleEndTurn_MonFled,
[B_OUTCOME_CAUGHT] = HandleEndTurn_FinishBattle,
[B_OUTCOME_NO_SAFARI_BALLS] = HandleEndTurn_FinishBattle,
};
const u8 gStatusConditionString_PoisonJpn[8] = _("どく$$$$$");
const u8 gStatusConditionString_SleepJpn[8] = _("ねむり$$$$");
const u8 gStatusConditionString_ParalysisJpn[8] = _("まひ$$$$$");
const u8 gStatusConditionString_BurnJpn[8] = _("やけど$$$$");
const u8 gStatusConditionString_IceJpn[8] = _("こおり$$$$");
const u8 gStatusConditionString_ConfusionJpn[8] = _("こんらん$$$");
const u8 gStatusConditionString_LoveJpn[8] = _("メロメロ$$$");
const u8 *const gStatusConditionStringsTable[7][2] =
{
{ gStatusConditionString_PoisonJpn, gText_Poison },
{ gStatusConditionString_SleepJpn, gText_Sleep },
{ gStatusConditionString_ParalysisJpn, gText_Paralysis },
{ gStatusConditionString_BurnJpn, gText_Burn },
{ gStatusConditionString_IceJpn, gText_Ice },
{ gStatusConditionString_ConfusionJpn, gText_Confusion },
{ gStatusConditionString_LoveJpn, gText_Love }
};
void CB2_InitBattle(void)
{
MoveSaveBlocks_ResetHeap();
AllocateBattleResources();
AllocateBattleSpritesData();
AllocateMonSpritesGfx();
if (gBattleTypeFlags & BATTLE_TYPE_MULTI)
{
HandleLinkBattleSetup();
SetMainCallback2(CB2_PreInitMultiBattle);
gBattleCommunication[MULTIUSE_STATE] = 0;
}
else
{
CB2_InitBattleInternal();
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);
}
}
}
}
static void CB2_InitBattleInternal(void)
{
s32 i;
SetHBlankCallback(NULL);
SetVBlankCallback(NULL);
CpuFill32(0, (void *)VRAM, VRAM_SIZE);
SetGpuReg(REG_OFFSET_MOSAIC, 0);
SetGpuReg(REG_OFFSET_WIN0H, WIN_RANGE(0, 0xF0));
SetGpuReg(REG_OFFSET_WIN0V, WIN_RANGE(0x50, 0x51));
SetGpuReg(REG_OFFSET_WININ, 0);
SetGpuReg(REG_OFFSET_WINOUT, 0);
gBattle_WIN0H = WIN_RANGE(0, 0xF0);
gBattle_WIN0V = WIN_RANGE(0x50, 0x51);
ScanlineEffect_Clear();
for (i = 0; i < 80; ++i)
{
gScanlineEffectRegBuffers[0][i] = 0xF0;
gScanlineEffectRegBuffers[1][i] = 0xF0;
}
for (; i < 160; ++i)
{
gScanlineEffectRegBuffers[0][i] = 0xFF10;
gScanlineEffectRegBuffers[1][i] = 0xFF10;
}
ScanlineEffect_SetParams(sIntroScanlineParams16Bit);
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;
gBattleTerrain = BattleSetup_GetTerrainId();
InitBattleBgsVideo();
LoadBattleTextboxAndBackground();
ResetSpriteData();
ResetTasks();
DrawBattleEntryBackground();
FreeAllSpritePalettes();
gReservedSpritePaletteCount = 4;
SetVBlankCallback(VBlankCB_Battle);
SetUpBattleVars();
if (gBattleTypeFlags & BATTLE_TYPE_MULTI)
SetMainCallback2(CB2_HandleStartMultiBattle);
else
SetMainCallback2(CB2_HandleStartBattle);
if (!(gBattleTypeFlags & BATTLE_TYPE_LINK))
{
CreateNPCTrainerParty(&gEnemyParty[0], gTrainerBattleOpponent_A);
SetWildMonHeldItem();
}
gMain.inBattle = TRUE;
for (i = 0; i < PARTY_SIZE; ++i)
AdjustFriendship(&gPlayerParty[i], 3);
gBattleCommunication[MULTIUSE_STATE] = 0;
}
static void sub_800FFEC(void)
{
u16 r6 = 0;
u16 species = SPECIES_NONE;
u16 hp = 0;
u32 status = 0;
s32 i;
for (i = 0; i < PARTY_SIZE; ++i)
{
species = GetMonData(&gPlayerParty[i], MON_DATA_SPECIES2);
hp = GetMonData(&gPlayerParty[i], MON_DATA_HP);
status = GetMonData(&gPlayerParty[i], MON_DATA_STATUS);
if (species == SPECIES_NONE)
continue;
if (species != SPECIES_EGG && hp != 0 && status == 0)
r6 |= 1 << i * 2;
if (species == SPECIES_NONE)
continue;
if (hp != 0 && (species == SPECIES_EGG || status != 0))
r6 |= 2 << i * 2;
if (species == SPECIES_NONE)
continue;
if (species != SPECIES_EGG && hp == 0)
r6 |= 3 << i * 2;
}
gBattleStruct->field_186 = r6;
*(&gBattleStruct->field_187) = r6 >> 8;
}
static void SetPlayerBerryDataInBattleStruct(void)
{
s32 i;
struct BattleStruct *battleStruct = gBattleStruct;
struct BattleEnigmaBerry *battleBerry = &battleStruct->battleEnigmaBerry;
if (IsEnigmaBerryValid() == TRUE)
{
for (i = 0; i < BERRY_NAME_LENGTH; ++i)
battleBerry->name[i] = gSaveBlock1Ptr->enigmaBerry.berry.name[i];
battleBerry->name[i] = EOS;
for (i = 0; i < BERRY_ITEM_EFFECT_COUNT; ++i)
battleBerry->itemEffect[i] = gSaveBlock1Ptr->enigmaBerry.itemEffect[i];
battleBerry->holdEffect = gSaveBlock1Ptr->enigmaBerry.holdEffect;
battleBerry->holdEffectParam = gSaveBlock1Ptr->enigmaBerry.holdEffectParam;
}
else
{
const struct Berry *berryData = GetBerryInfo(ItemIdToBerryType(ITEM_ENIGMA_BERRY));
for (i = 0; i < BERRY_NAME_LENGTH; ++i)
battleBerry->name[i] = berryData->name[i];
battleBerry->name[i] = EOS;
for (i = 0; i < BERRY_ITEM_EFFECT_COUNT; ++i)
battleBerry->itemEffect[i] = 0;
battleBerry->holdEffect = HOLD_EFFECT_NONE;
battleBerry->holdEffectParam = 0;
}
}
static void SetAllPlayersBerryData(void)
{
s32 i, j;
if (!(gBattleTypeFlags & BATTLE_TYPE_LINK))
{
if (IsEnigmaBerryValid() == TRUE)
{
for (i = 0; i < BERRY_NAME_LENGTH; ++i)
{
gEnigmaBerries[0].name[i] = gSaveBlock1Ptr->enigmaBerry.berry.name[i];
gEnigmaBerries[2].name[i] = gSaveBlock1Ptr->enigmaBerry.berry.name[i];
}
gEnigmaBerries[0].name[i] = EOS;
gEnigmaBerries[2].name[i] = EOS;
for (i = 0; i < BERRY_ITEM_EFFECT_COUNT; ++i)
{
gEnigmaBerries[0].itemEffect[i] = gSaveBlock1Ptr->enigmaBerry.itemEffect[i];
gEnigmaBerries[2].itemEffect[i] = gSaveBlock1Ptr->enigmaBerry.itemEffect[i];
}
gEnigmaBerries[0].holdEffect = gSaveBlock1Ptr->enigmaBerry.holdEffect;
gEnigmaBerries[2].holdEffect = gSaveBlock1Ptr->enigmaBerry.holdEffect;
gEnigmaBerries[0].holdEffectParam = gSaveBlock1Ptr->enigmaBerry.holdEffectParam;
gEnigmaBerries[2].holdEffectParam = gSaveBlock1Ptr->enigmaBerry.holdEffectParam;
}
else
{
const struct Berry *berryData = GetBerryInfo(ItemIdToBerryType(ITEM_ENIGMA_BERRY));
for (i = 0; i < BERRY_NAME_LENGTH; ++i)
{
gEnigmaBerries[0].name[i] = berryData->name[i];
gEnigmaBerries[2].name[i] = berryData->name[i];
}
gEnigmaBerries[0].name[i] = EOS;
gEnigmaBerries[2].name[i] = EOS;
for (i = 0; i < BERRY_ITEM_EFFECT_COUNT; ++i)
{
gEnigmaBerries[0].itemEffect[i] = 0;
gEnigmaBerries[2].itemEffect[i] = 0;
}
gEnigmaBerries[0].holdEffect = HOLD_EFFECT_NONE;
gEnigmaBerries[2].holdEffect = HOLD_EFFECT_NONE;
gEnigmaBerries[0].holdEffectParam = 0;
gEnigmaBerries[2].holdEffectParam = 0;
}
}
else
{
s32 numPlayers;
struct BattleEnigmaBerry *src;
u8 battlerId;
if (gBattleTypeFlags & BATTLE_TYPE_MULTI)
{
for (i = 0; i < 4; ++i)
{
src = (struct BattleEnigmaBerry *)(gBlockRecvBuffer[i] + 2);
battlerId = gLinkPlayers[i].id;
for (j = 0; j < BERRY_NAME_LENGTH; ++j)
gEnigmaBerries[battlerId].name[j] = src->name[j];
gEnigmaBerries[battlerId].name[j] = EOS;
for (j = 0; j < BERRY_ITEM_EFFECT_COUNT; ++j)
gEnigmaBerries[battlerId].itemEffect[j] = src->itemEffect[j];
gEnigmaBerries[battlerId].holdEffect = src->holdEffect;
gEnigmaBerries[battlerId].holdEffectParam = src->holdEffectParam;
}
}
else
{
for (i = 0; i < 2; ++i)
{
src = (struct BattleEnigmaBerry *)(gBlockRecvBuffer[i] + 2);
for (j = 0; j < BERRY_NAME_LENGTH; ++j)
{
gEnigmaBerries[i].name[j] = src->name[j];
gEnigmaBerries[i + 2].name[j] = src->name[j];
}
gEnigmaBerries[i].name[j] = EOS;
gEnigmaBerries[i + 2].name[j] = EOS;
for (j = 0; j < BERRY_ITEM_EFFECT_COUNT; ++j)
{
gEnigmaBerries[i].itemEffect[j] = src->itemEffect[j];
gEnigmaBerries[i + 2].itemEffect[j] = src->itemEffect[j];
}
gEnigmaBerries[i].holdEffect = src->holdEffect;
gEnigmaBerries[i + 2].holdEffect = src->holdEffect;
gEnigmaBerries[i].holdEffectParam = src->holdEffectParam;
gEnigmaBerries[i + 2].holdEffectParam = src->holdEffectParam;
}
}
}
}
static void sub_8010414(u8 arg0, u8 arg1)
{
u8 var = 0;
if (gBlockRecvBuffer[0][0] == 256)
{
if (arg1 == 0)
gBattleTypeFlags |= BATTLE_TYPE_IS_MASTER | BATTLE_TYPE_TRAINER;
else
gBattleTypeFlags |= BATTLE_TYPE_TRAINER;
++var;
}
if (var == 0)
{
s32 i;
for (i = 0; i < arg0; ++i)
if (gBlockRecvBuffer[0][0] != gBlockRecvBuffer[i][0])
break;
if (i == arg0)
{
if (arg1 == 0)
gBattleTypeFlags |= BATTLE_TYPE_IS_MASTER | BATTLE_TYPE_TRAINER;
else
gBattleTypeFlags |= BATTLE_TYPE_TRAINER;
++var;
}
if (var == 0)
{
for (i = 0; i < arg0; ++i)
{
if (gBlockRecvBuffer[i][0] == 0x201)
if (i != arg1 && i < arg1)
break;
if (gBlockRecvBuffer[i][0] > 0x201 && i != arg1)
break;
}
if (i == arg0)
gBattleTypeFlags |= BATTLE_TYPE_IS_MASTER | BATTLE_TYPE_TRAINER;
else
gBattleTypeFlags |= BATTLE_TYPE_TRAINER;
}
}
}
static void CB2_HandleStartBattle(void)
{
u8 playerMultiplayerId;
u8 enemyMultiplayerId;
RunTasks();
AnimateSprites();
BuildOamBuffer();
playerMultiplayerId = GetMultiplayerId();
gBattleStruct->multiplayerId = playerMultiplayerId;
enemyMultiplayerId = playerMultiplayerId ^ BIT_SIDE;
switch (gBattleCommunication[MULTIUSE_STATE])
{
case 0:
if (!IsDma3ManagerBusyWithBgCopy())
{
ShowBg(0);
ShowBg(1);
ShowBg(2);
ShowBg(3);
sub_80357C8();
gBattleCommunication[MULTIUSE_STATE] = 1;
}
if (gWirelessCommType)
LoadWirelessStatusIndicatorSpriteGfx();
break;
case 1:
if (gBattleTypeFlags & BATTLE_TYPE_LINK)
{
if (gReceivedRemoteLinkPlayers != 0)
{
if (IsLinkTaskFinished())
{
*(&gBattleStruct->field_184) = 1;
*(&gBattleStruct->field_185) = 2;
sub_800FFEC();
SetPlayerBerryDataInBattleStruct();
SendBlock(bitmask_all_link_players_but_self(), &gBattleStruct->field_184, 32);
gBattleCommunication[MULTIUSE_STATE] = 2;
}
if (gWirelessCommType != 0)
CreateWirelessStatusIndicatorSprite(0, 0);
}
}
else
{
gBattleTypeFlags |= BATTLE_TYPE_IS_MASTER;
gBattleCommunication[MULTIUSE_STATE] = 15;
SetAllPlayersBerryData();
}
break;
case 2:
if ((GetBlockReceivedStatus() & 3) == 3)
{
u8 taskId;
ResetBlockReceivedFlags();
sub_8010414(2, playerMultiplayerId);
SetAllPlayersBerryData();
taskId = CreateTask(sub_800F6FC, 0);
gTasks[taskId].data[1] = 270;
gTasks[taskId].data[2] = 90;
gTasks[taskId].data[5] = 0;
gTasks[taskId].data[3] = gBattleStruct->field_186 | (gBattleStruct->field_187 << 8);
gTasks[taskId].data[4] = gBlockRecvBuffer[enemyMultiplayerId][1];
SetDeoxysStats();
++gBattleCommunication[MULTIUSE_STATE];
}
break;
case 3:
if (IsLinkTaskFinished())
{
SendBlock(bitmask_all_link_players_but_self(), gPlayerParty, sizeof(struct Pokemon) * 2);
++gBattleCommunication[MULTIUSE_STATE];
}
break;
case 4:
if ((GetBlockReceivedStatus() & 3) == 3)
{
ResetBlockReceivedFlags();
memcpy(gEnemyParty, gBlockRecvBuffer[enemyMultiplayerId], sizeof(struct Pokemon) * 2);
++gBattleCommunication[MULTIUSE_STATE];
}
break;
case 7:
if (IsLinkTaskFinished())
{
SendBlock(bitmask_all_link_players_but_self(), gPlayerParty + 2, sizeof(struct Pokemon) * 2);
++gBattleCommunication[MULTIUSE_STATE];
}
break;
case 8:
if ((GetBlockReceivedStatus() & 3) == 3)
{
ResetBlockReceivedFlags();
memcpy(gEnemyParty + 2, gBlockRecvBuffer[enemyMultiplayerId], sizeof(struct Pokemon) * 2);
++gBattleCommunication[MULTIUSE_STATE];
}
break;
case 11:
if (IsLinkTaskFinished())
{
SendBlock(bitmask_all_link_players_but_self(), gPlayerParty + 4, sizeof(struct Pokemon) * 2);
++gBattleCommunication[MULTIUSE_STATE];
}
break;
case 12:
if ((GetBlockReceivedStatus() & 3) == 3)
{
ResetBlockReceivedFlags();
memcpy(gEnemyParty + 4, gBlockRecvBuffer[enemyMultiplayerId], sizeof(struct Pokemon) * 2);
TryCorrectShedinjaLanguage(&gEnemyParty[0]);
TryCorrectShedinjaLanguage(&gEnemyParty[1]);
TryCorrectShedinjaLanguage(&gEnemyParty[2]);
TryCorrectShedinjaLanguage(&gEnemyParty[3]);
TryCorrectShedinjaLanguage(&gEnemyParty[4]);
TryCorrectShedinjaLanguage(&gEnemyParty[5]);
++gBattleCommunication[MULTIUSE_STATE];
}
break;
case 15:
sub_800D30C();
++gBattleCommunication[MULTIUSE_STATE];
gBattleCommunication[SPRITES_INIT_STATE1] = 0;
gBattleCommunication[SPRITES_INIT_STATE2] = 0;
break;
case 16:
if (BattleInitAllSprites(&gBattleCommunication[SPRITES_INIT_STATE1], &gBattleCommunication[SPRITES_INIT_STATE2]))
{
gPreBattleCallback1 = gMain.callback1;
gMain.callback1 = BattleMainCB1;
SetMainCallback2(BattleMainCB2);
if (gBattleTypeFlags & BATTLE_TYPE_LINK)
{
gBattleTypeFlags |= BATTLE_TYPE_20;
}
}
break;
case 5:
case 9:
case 13:
++gBattleCommunication[MULTIUSE_STATE];
gBattleCommunication[1] = 1;
case 6:
case 10:
case 14:
if (--gBattleCommunication[1] == 0)
++gBattleCommunication[MULTIUSE_STATE];
break;
}
}
static void sub_80108C4(void)
{
s32 i, j;
u8 *nick, *cur;
for (i = 0; i < 3; ++i)
{
gMultiPartnerParty[i].species = GetMonData(&gPlayerParty[i], MON_DATA_SPECIES);
gMultiPartnerParty[i].heldItem = GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM);
nick = gMultiPartnerParty[i].nickname;
GetMonData(&gPlayerParty[i], MON_DATA_NICKNAME, nick);
gMultiPartnerParty[i].level = GetMonData(&gPlayerParty[i], MON_DATA_LEVEL);
gMultiPartnerParty[i].hp = GetMonData(&gPlayerParty[i], MON_DATA_HP);
gMultiPartnerParty[i].maxhp = GetMonData(&gPlayerParty[i], MON_DATA_MAX_HP);
gMultiPartnerParty[i].status = GetMonData(&gPlayerParty[i], MON_DATA_STATUS);
gMultiPartnerParty[i].personality = GetMonData(&gPlayerParty[i], MON_DATA_PERSONALITY);
gMultiPartnerParty[i].gender = GetMonGender(&gPlayerParty[i]);
StripExtCtrlCodes(nick);
if (GetMonData(&gPlayerParty[i], MON_DATA_LANGUAGE) != LANGUAGE_JAPANESE)
{
for (cur = nick, j = 0; cur[j] != EOS; ++j)
;
while (j < 6)
cur[j++] = 0;
cur[j] = EOS;
}
}
memcpy(&gBattleStruct->field_184, gMultiPartnerParty, sizeof(gMultiPartnerParty));
}
static void CB2_PreInitMultiBattle(void)
{
s32 i;
u8 playerMultiplierId;
u8 r4 = 0xF;
u16 *savedBattleTypeFlags;
void (**savedCallback)(void);
playerMultiplierId = GetMultiplayerId();
gBattleStruct->multiplayerId = playerMultiplierId;
savedCallback = &gBattleStruct->savedCallback;
savedBattleTypeFlags = &gBattleStruct->savedBattleTypeFlags;
RunTasks();
AnimateSprites();
BuildOamBuffer();
switch (gBattleCommunication[MULTIUSE_STATE])
{
case 0:
if (gReceivedRemoteLinkPlayers != 0 && IsLinkTaskFinished())
{
sub_80108C4();
SendBlock(bitmask_all_link_players_but_self(), &gBattleStruct->field_184, sizeof(gMultiPartnerParty));
++gBattleCommunication[MULTIUSE_STATE];
}
break;
case 1:
if ((GetBlockReceivedStatus() & r4) == r4)
{
ResetBlockReceivedFlags();
for (i = 0; i < 4; ++i)
{
if (i == playerMultiplierId)
continue;
if ((!(gLinkPlayers[i].id & 1) && !(gLinkPlayers[playerMultiplierId].id & 1))
|| (gLinkPlayers[i].id & 1 && gLinkPlayers[playerMultiplierId].id & 1))
memcpy(gMultiPartnerParty, gBlockRecvBuffer[i], sizeof(gMultiPartnerParty));
}
++gBattleCommunication[MULTIUSE_STATE];
*savedCallback = gMain.savedCallback;
*savedBattleTypeFlags = gBattleTypeFlags;
gMain.savedCallback = CB2_PreInitMultiBattle;
ShowPartyMenuToShowcaseMultiBattleParty();
}
break;
case 2:
if (!gPaletteFade.active)
{
++gBattleCommunication[MULTIUSE_STATE];
if (gWirelessCommType)
sub_800AB9C();
else
sub_800AAC0();
}
break;
case 3:
if (gWirelessCommType)
{
if (IsLinkRfuTaskFinished())
{
gBattleTypeFlags = *savedBattleTypeFlags;
gMain.savedCallback = *savedCallback;
SetMainCallback2(CB2_InitBattleInternal);
}
}
else if (gReceivedRemoteLinkPlayers == 0)
{
gBattleTypeFlags = *savedBattleTypeFlags;
gMain.savedCallback = *savedCallback;
SetMainCallback2(CB2_InitBattleInternal);
}
break;
}
}
static void CB2_HandleStartMultiBattle(void)
{
u8 playerMultiplayerId;
s32 id;
u8 taskId;
playerMultiplayerId = GetMultiplayerId();
gBattleStruct->multiplayerId = playerMultiplayerId;
RunTasks();
AnimateSprites();
BuildOamBuffer();
switch (gBattleCommunication[MULTIUSE_STATE])
{
case 0:
if (!IsDma3ManagerBusyWithBgCopy())
{
ShowBg(0);
ShowBg(1);
ShowBg(2);
ShowBg(3);
sub_80357C8();
gBattleCommunication[MULTIUSE_STATE] = 1;
}
if (gWirelessCommType)
LoadWirelessStatusIndicatorSpriteGfx();
break;
case 1:
if (gReceivedRemoteLinkPlayers != 0)
{
if (IsLinkTaskFinished())
{
*(&gBattleStruct->field_184) = 1;
*(&gBattleStruct->field_185) = 2;
sub_800FFEC();
SetPlayerBerryDataInBattleStruct();
SendBlock(bitmask_all_link_players_but_self(), &gBattleStruct->field_184, 32);
++gBattleCommunication[MULTIUSE_STATE];
}
if (gWirelessCommType)
CreateWirelessStatusIndicatorSprite(0, 0);
}
break;
case 2:
if ((GetBlockReceivedStatus() & 0xF) == 0xF)
{
ResetBlockReceivedFlags();
sub_8010414(4, playerMultiplayerId);
SetAllPlayersBerryData();
SetDeoxysStats();
memcpy(gDecompressionBuffer, gPlayerParty, sizeof(struct Pokemon) * 3);
taskId = CreateTask(sub_800F6FC, 0);
gTasks[taskId].data[1] = 270;
gTasks[taskId].data[2] = 90;
gTasks[taskId].data[5] = 0;
gTasks[taskId].data[3] = 0;
gTasks[taskId].data[4] = 0;
for (id = 0; id < MAX_LINK_PLAYERS; ++id)
{
switch (gLinkPlayers[id].id)
{
case 0:
gTasks[taskId].data[3] |= gBlockRecvBuffer[id][1] & 0x3F;
break;
case 1:
gTasks[taskId].data[4] |= gBlockRecvBuffer[id][1] & 0x3F;
break;
case 2:
gTasks[taskId].data[3] |= (gBlockRecvBuffer[id][1] & 0x3F) << 6;
break;
case 3:
gTasks[taskId].data[4] |= (gBlockRecvBuffer[id][1] & 0x3F) << 6;
break;
}
}
ZeroPlayerPartyMons();
ZeroEnemyPartyMons();
++gBattleCommunication[MULTIUSE_STATE];
}
else
{
break;
}
// fall through
case 3:
if (IsLinkTaskFinished())
{
SendBlock(bitmask_all_link_players_but_self(), gDecompressionBuffer, sizeof(struct Pokemon) * 2);
++gBattleCommunication[MULTIUSE_STATE];
}
break;
case 4:
if ((GetBlockReceivedStatus() & 0xF) == 0xF)
{
ResetBlockReceivedFlags();
for (id = 0; id < MAX_LINK_PLAYERS; ++id)
{
if (id == playerMultiplayerId)
{
switch (gLinkPlayers[id].id)
{
case 0:
case 3:
memcpy(gPlayerParty, gBlockRecvBuffer[id], sizeof(struct Pokemon) * 2);
break;
case 1:
case 2:
memcpy(gPlayerParty + 3, gBlockRecvBuffer[id], sizeof(struct Pokemon) * 2);
break;
}
}
else
{
if ((!(gLinkPlayers[id].id & 1) && !(gLinkPlayers[playerMultiplayerId].id & 1))
|| ((gLinkPlayers[id].id & 1) && (gLinkPlayers[playerMultiplayerId].id & 1)))
{
switch (gLinkPlayers[id].id)
{
case 0:
case 3:
memcpy(gPlayerParty, gBlockRecvBuffer[id], sizeof(struct Pokemon) * 2);
break;
case 1:
case 2:
memcpy(gPlayerParty + 3, gBlockRecvBuffer[id], sizeof(struct Pokemon) * 2);
break;
}
}
else
{
switch (gLinkPlayers[id].id)
{
case 0:
case 3:
memcpy(gEnemyParty, gBlockRecvBuffer[id], sizeof(struct Pokemon) * 2);
break;
case 1:
case 2:
memcpy(gEnemyParty + 3, gBlockRecvBuffer[id], sizeof(struct Pokemon) * 2);
break;
}
}
}
}
++gBattleCommunication[MULTIUSE_STATE];
}
break;
case 7:
if (IsLinkTaskFinished())
{
SendBlock(bitmask_all_link_players_but_self(), gDecompressionBuffer + sizeof(struct Pokemon) * 2, sizeof(struct Pokemon));
++gBattleCommunication[MULTIUSE_STATE];
}
break;
case 8:
if ((GetBlockReceivedStatus() & 0xF) == 0xF)
{
ResetBlockReceivedFlags();
for (id = 0; id < MAX_LINK_PLAYERS; ++id)
{
if (id == playerMultiplayerId)
{
switch (gLinkPlayers[id].id)
{
case 0:
case 3:
memcpy(gPlayerParty + 2, gBlockRecvBuffer[id], sizeof(struct Pokemon));
break;
case 1:
case 2:
memcpy(gPlayerParty + 5, gBlockRecvBuffer[id], sizeof(struct Pokemon));
break;
}
}
else
{
if ((!(gLinkPlayers[id].id & 1) && !(gLinkPlayers[playerMultiplayerId].id & 1))
|| ((gLinkPlayers[id].id & 1) && (gLinkPlayers[playerMultiplayerId].id & 1)))
{
switch (gLinkPlayers[id].id)
{
case 0:
case 3:
memcpy(gPlayerParty + 2, gBlockRecvBuffer[id], sizeof(struct Pokemon));
break;
case 1:
case 2:
memcpy(gPlayerParty + 5, gBlockRecvBuffer[id], sizeof(struct Pokemon));
break;
}
}
else
{
switch (gLinkPlayers[id].id)
{
case 0:
case 3:
memcpy(gEnemyParty + 2, gBlockRecvBuffer[id], sizeof(struct Pokemon));
break;
case 1:
case 2:
memcpy(gEnemyParty + 5, gBlockRecvBuffer[id], sizeof(struct Pokemon));
break;
}
}
}
}
TryCorrectShedinjaLanguage(&gPlayerParty[0]);
TryCorrectShedinjaLanguage(&gPlayerParty[1]);
TryCorrectShedinjaLanguage(&gPlayerParty[2]);
TryCorrectShedinjaLanguage(&gPlayerParty[3]);
TryCorrectShedinjaLanguage(&gPlayerParty[4]);
TryCorrectShedinjaLanguage(&gPlayerParty[5]);
TryCorrectShedinjaLanguage(&gEnemyParty[0]);
TryCorrectShedinjaLanguage(&gEnemyParty[1]);
TryCorrectShedinjaLanguage(&gEnemyParty[2]);
TryCorrectShedinjaLanguage(&gEnemyParty[3]);
TryCorrectShedinjaLanguage(&gEnemyParty[4]);
TryCorrectShedinjaLanguage(&gEnemyParty[5]);
++gBattleCommunication[MULTIUSE_STATE];
}
break;
case 11:
sub_800D30C();
++gBattleCommunication[MULTIUSE_STATE];
gBattleCommunication[SPRITES_INIT_STATE1] = 0;
gBattleCommunication[SPRITES_INIT_STATE2] = 0;
break;
case 12:
if (BattleInitAllSprites(&gBattleCommunication[SPRITES_INIT_STATE1], &gBattleCommunication[SPRITES_INIT_STATE2]))
{
gPreBattleCallback1 = gMain.callback1;
gMain.callback1 = BattleMainCB1;
SetMainCallback2(BattleMainCB2);
if (gBattleTypeFlags & BATTLE_TYPE_LINK)
gBattleTypeFlags |= BATTLE_TYPE_20;
}
break;
case 5:
case 9:
++gBattleCommunication[0];
gBattleCommunication[SPRITES_INIT_STATE1] = 1;
// fall through
case 6:
case 10:
if (--gBattleCommunication[SPRITES_INIT_STATE1] == 0)
++gBattleCommunication[0];
break;
}
}
void BattleMainCB2(void)
{
AnimateSprites();
BuildOamBuffer();
RunTextPrinters();
UpdatePaletteFade();
RunTasks();
if (JOY_HELD(B_BUTTON) && gBattleTypeFlags & BATTLE_TYPE_POKEDUDE)
{
gSpecialVar_Result = gBattleOutcome = B_OUTCOME_DREW;
ResetPaletteFadeControl();
BeginNormalPaletteFade(0xFFFFFFFF, 0, 0, 0x10, RGB_BLACK);
SetMainCallback2(CB2_QuitPokeDudeBattle);
}
}
void FreeRestoreBattleData(void)
{
gMain.callback1 = gPreBattleCallback1;
gScanlineEffect.state = 3;
gMain.inBattle = FALSE;
ZeroEnemyPartyMons();
m4aSongNumStop(SE_HINSI);
FreeMonSpritesGfx();
FreeBattleSpritesData();
FreeBattleResources();
}
static void CB2_QuitPokeDudeBattle(void)
{
UpdatePaletteFade();
if (!gPaletteFade.active)
{
FreeRestoreBattleData();
FreeAllWindowBuffers();
SetMainCallback2(gMain.savedCallback);
}
}
static void sub_80111EC(struct Sprite *sprite)
{
sprite->data[0] = 0;
sprite->callback = sub_80111FC;
}
static void sub_80111FC(struct Sprite *sprite)
{
switch (sprite->data[0])
{
case 0:
gUnknown_2022BC0 = AllocZeroed(0x1000);
++sprite->data[0];
sprite->data[1] = 0;
sprite->data[2] = 0x281;
sprite->data[3] = 0;
sprite->data[4] = 1;
// fall through
case 1:
if (--sprite->data[4] == 0)
{
s32 i, r2, r0;
sprite->data[4] = 2;
r2 = sprite->data[1] + sprite->data[3] * 32;
r0 = sprite->data[2] - sprite->data[3] * 32;
for (i = 0; i <= 29; i += 2)
{
*(&gUnknown_2022BC0[r2] + i) = 0x3D;
*(&gUnknown_2022BC0[r0] + i) = 0x3D;
}
if (++sprite->data[3] == 21)
{
++sprite->data[0];
sprite->data[1] = 32;
}
}
break;
case 2:
if (--sprite->data[1] == 20)
{
if (gUnknown_2022BC0 != NULL)
{
memset(gUnknown_2022BC0, 0, 0x1000);
FREE_AND_SET_NULL(gUnknown_2022BC0);
}
SetMainCallback2(CB2_InitBattle);
}
break;
}
}
static u8 CreateNPCTrainerParty(struct Pokemon *party, u16 trainerNum)
{
u32 nameHash = 0;
u32 personalityValue;
u8 fixedIV;
s32 i, j;
if (trainerNum == TRAINER_SECRET_BASE)
return 0;
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER
&& !(gBattleTypeFlags & (BATTLE_TYPE_BATTLE_TOWER | BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_TRAINER_TOWER)))
{
ZeroEnemyPartyMons();
for (i = 0; i < gTrainers[trainerNum].partySize; ++i)
{
if (gTrainers[trainerNum].doubleBattle == TRUE)
personalityValue = 0x80;
else if (gTrainers[trainerNum].encounterMusic_gender & 0x80)
personalityValue = 0x78;
else
personalityValue = 0x88;
for (j = 0; gTrainers[trainerNum].trainerName[j] != EOS; ++j)
nameHash += gTrainers[trainerNum].trainerName[j];
switch (gTrainers[trainerNum].partyFlags)
{
case 0:
{
const struct TrainerMonNoItemDefaultMoves *partyData = gTrainers[trainerNum].party.NoItemDefaultMoves;
for (j = 0; gSpeciesNames[partyData[i].species][j] != EOS; ++j)
nameHash += gSpeciesNames[partyData[i].species][j];
personalityValue += nameHash << 8;
fixedIV = partyData[i].iv * 31 / 255;
CreateMon(&party[i], partyData[i].species, partyData[i].lvl, fixedIV, TRUE, personalityValue, OT_ID_RANDOM_NO_SHINY, 0);
break;
}
case F_TRAINER_PARTY_CUSTOM_MOVESET:
{
const struct TrainerMonNoItemCustomMoves *partyData = gTrainers[trainerNum].party.NoItemCustomMoves;
for (j = 0; gSpeciesNames[partyData[i].species][j] != EOS; ++j)
nameHash += gSpeciesNames[partyData[i].species][j];
personalityValue += nameHash << 8;
fixedIV = partyData[i].iv * 31 / 255;
CreateMon(&party[i], partyData[i].species, partyData[i].lvl, fixedIV, TRUE, personalityValue, OT_ID_RANDOM_NO_SHINY, 0);
for (j = 0; j < MAX_MON_MOVES; ++j)
{
SetMonData(&party[i], MON_DATA_MOVE1 + j, &partyData[i].moves[j]);
SetMonData(&party[i], MON_DATA_PP1 + j, &gBattleMoves[partyData[i].moves[j]].pp);
}
break;
}
case F_TRAINER_PARTY_HELD_ITEM:
{
const struct TrainerMonItemDefaultMoves *partyData = gTrainers[trainerNum].party.ItemDefaultMoves;
for (j = 0; gSpeciesNames[partyData[i].species][j] != EOS; ++j)
nameHash += gSpeciesNames[partyData[i].species][j];
personalityValue += nameHash << 8;
fixedIV = partyData[i].iv * 31 / 255;
CreateMon(&party[i], partyData[i].species, partyData[i].lvl, fixedIV, TRUE, personalityValue, OT_ID_RANDOM_NO_SHINY, 0);
SetMonData(&party[i], MON_DATA_HELD_ITEM, &partyData[i].heldItem);
break;
}
case F_TRAINER_PARTY_CUSTOM_MOVESET | F_TRAINER_PARTY_HELD_ITEM:
{
const struct TrainerMonItemCustomMoves *partyData = gTrainers[trainerNum].party.ItemCustomMoves;
for (j = 0; gSpeciesNames[partyData[i].species][j] != EOS; ++j)
nameHash += gSpeciesNames[partyData[i].species][j];
personalityValue += nameHash << 8;
fixedIV = partyData[i].iv * 31 / 255;
CreateMon(&party[i], partyData[i].species, partyData[i].lvl, fixedIV, TRUE, personalityValue, OT_ID_RANDOM_NO_SHINY, 0);
SetMonData(&party[i], MON_DATA_HELD_ITEM, &partyData[i].heldItem);
for (j = 0; j < MAX_MON_MOVES; ++j)
{
SetMonData(&party[i], MON_DATA_MOVE1 + j, &partyData[i].moves[j]);
SetMonData(&party[i], MON_DATA_PP1 + j, &gBattleMoves[partyData[i].moves[j]].pp);
}
break;
}
}
}
gBattleTypeFlags |= gTrainers[trainerNum].doubleBattle;
}
return gTrainers[trainerNum].partySize;
}
// not used
static void sub_80116CC(void)
{
if (REG_VCOUNT < 0xA0 && REG_VCOUNT >= 0x6F)
REG_BG0CNT = BGCNT_PRIORITY(0) | BGCNT_CHARBASE(0) | BGCNT_SCREENBASE(24) | BGCNT_16COLOR | BGCNT_TXT256x512;
}
void VBlankCB_Battle(void)
{
// Change gRngSeed every vblank.
Random();
SetGpuReg(REG_OFFSET_BG0HOFS, gBattle_BG0_X);
SetGpuReg(REG_OFFSET_BG0VOFS, gBattle_BG0_Y);
SetGpuReg(REG_OFFSET_BG1HOFS, gBattle_BG1_X);
SetGpuReg(REG_OFFSET_BG1VOFS, gBattle_BG1_Y);
SetGpuReg(REG_OFFSET_BG2HOFS, gBattle_BG2_X);
SetGpuReg(REG_OFFSET_BG2VOFS, gBattle_BG2_Y);
SetGpuReg(REG_OFFSET_BG3HOFS, gBattle_BG3_X);
SetGpuReg(REG_OFFSET_BG3VOFS, gBattle_BG3_Y);
SetGpuReg(REG_OFFSET_WIN0H, gBattle_WIN0H);
SetGpuReg(REG_OFFSET_WIN0V, gBattle_WIN0V);
SetGpuReg(REG_OFFSET_WIN1H, gBattle_WIN1H);
SetGpuReg(REG_OFFSET_WIN1V, gBattle_WIN1V);
LoadOam();
ProcessSpriteCopyRequests();
TransferPlttBuffer();
ScanlineEffect_InitHBlankDmaTransfer();
}
void nullsub_9(struct Sprite *sprite)
{
}
static void sub_80117BC(struct Sprite *sprite)
{
if (sprite->data[0] != 0)
sprite->pos1.x = sprite->data[1] + ((sprite->data[2] & 0xFF00) >> 8);
else
sprite->pos1.x = sprite->data[1] - ((sprite->data[2] & 0xFF00) >> 8);
sprite->data[2] += 0x180;
if (sprite->affineAnimEnded)
{
FreeSpriteTilesByTag(ANIM_SPRITES_START);
FreeSpritePaletteByTag(ANIM_SPRITES_START);
FreeSpriteOamMatrix(sprite);
DestroySprite(sprite);
}
}
void sub_801182C(struct Sprite *sprite)
{
StartSpriteAffineAnim(sprite, 1);
sprite->callback = sub_80117BC;
PlaySE(SE_BT_START);
}
static void sub_801184C(u8 taskId)
{
struct Pokemon *party1 = NULL;
struct Pokemon *party2 = NULL;
u8 multiplayerId = gBattleStruct->multiplayerId;
u32 r7;
s32 i;
if (gBattleTypeFlags & BATTLE_TYPE_MULTI)
{
switch (gLinkPlayers[multiplayerId].id)
{
case 0:
case 2:
party1 = gPlayerParty;
party2 = gEnemyParty;
break;
case 1:
case 3:
party1 = gEnemyParty;
party2 = gPlayerParty;
break;
}
}
else
{
party1 = gPlayerParty;
party2 = gEnemyParty;
}
r7 = 0;
for (i = 0; i < PARTY_SIZE; ++i)
{
u16 species = GetMonData(&party1[i], MON_DATA_SPECIES2);
u16 hp = GetMonData(&party1[i], MON_DATA_HP);
u32 status = GetMonData(&party1[i], MON_DATA_STATUS);
if (species == SPECIES_NONE)
continue;
if (species != SPECIES_EGG && hp != 0 && status == 0)
r7 |= 1 << i * 2;
if (species == SPECIES_NONE)
continue;
if (hp != 0 && (species == SPECIES_EGG || status != 0))
r7 |= 2 << i * 2;
if (species == SPECIES_NONE)
continue;
if (species != SPECIES_EGG && hp == 0)
r7 |= 3 << i * 2;
}
gTasks[taskId].data[3] = r7;
r7 = 0;
for (i = 0; i < PARTY_SIZE; ++i)
{
u16 species = GetMonData(&party2[i], MON_DATA_SPECIES2);
u16 hp = GetMonData(&party2[i], MON_DATA_HP);
u32 status = GetMonData(&party2[i], MON_DATA_STATUS);
if (species == SPECIES_NONE)
continue;
if (species != SPECIES_EGG && hp != 0 && status == 0)
r7 |= 1 << i * 2;
if (species == SPECIES_NONE)
continue;
if (hp != 0 && (species == SPECIES_EGG || status != 0))
r7 |= 2 << i * 2;
if (species == SPECIES_NONE)
continue;
if (species != SPECIES_EGG && hp == 0)
r7 |= 3 << i * 2;
}
gTasks[taskId].data[4] = r7;
}
void sub_8011A1C(void)
{
s32 i;
u8 taskId;
SetHBlankCallback(NULL);
SetVBlankCallback(NULL);
CpuFill32(0, (void *)VRAM, VRAM_SIZE);
SetGpuReg(REG_OFFSET_MOSAIC, 0);
SetGpuReg(REG_OFFSET_WIN0H, WIN_RANGE(0, 0xF0));
SetGpuReg(REG_OFFSET_WIN0V, WIN_RANGE(0x50, 0x51));
SetGpuReg(REG_OFFSET_WININ, 0);
SetGpuReg(REG_OFFSET_WINOUT, 0);
gBattle_WIN0H = WIN_RANGE(0, 0xF0);
gBattle_WIN0V = WIN_RANGE(0x50, 0x51);
ScanlineEffect_Clear();
for (i = 0; i < 80; ++i)
{
gScanlineEffectRegBuffers[0][i] = 0xF0;
gScanlineEffectRegBuffers[1][i] = 0xF0;
}
for (; i < 160; ++i)
{
gScanlineEffectRegBuffers[0][i] = 0xFF10;
gScanlineEffectRegBuffers[1][i] = 0xFF10;
}
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;
InitBattleBgsVideo();
LoadCompressedPalette(gBattleTextboxPalette, 0, 64);
LoadBattleMenuWindowGfx();
ResetSpriteData();
ResetTasks();
DrawBattleEntryBackground();
SetGpuReg(REG_OFFSET_WINOUT, WININ_WIN0_BG0 | WININ_WIN0_BG1 | WININ_WIN0_BG2 | WININ_WIN0_OBJ | WININ_WIN0_CLR);
FreeAllSpritePalettes();
gReservedSpritePaletteCount = 4;
SetVBlankCallback(VBlankCB_Battle);
taskId = CreateTask(sub_800F6FC, 0);
gTasks[taskId].data[1] = 270;
gTasks[taskId].data[2] = 90;
gTasks[taskId].data[5] = 1;
sub_801184C(taskId);
SetMainCallback2(sub_8011B94);
gBattleCommunication[MULTIUSE_STATE] = 0;
}
static void sub_8011B94(void)
{
sub_8011BB0();
AnimateSprites();
BuildOamBuffer();
UpdatePaletteFade();
RunTasks();
}
static void sub_8011BB0(void)
{
s32 i;
switch (gBattleCommunication[MULTIUSE_STATE])
{
case 0:
ShowBg(0);
ShowBg(1);
ShowBg(2);
gBattleCommunication[1] = 0xFF;
++gBattleCommunication[MULTIUSE_STATE];
break;
case 1:
if (--gBattleCommunication[1] == 0)
{
BeginNormalPaletteFade(0xFFFFFFFF, 0, 0, 0x10, RGB_BLACK);
++gBattleCommunication[MULTIUSE_STATE];
}
break;
case 2:
if (!gPaletteFade.active)
{
SetMainCallback2(gMain.savedCallback);
TrySetQuestLogLinkBattleEvent();
FreeMonSpritesGfx();
FreeBattleSpritesData();
FreeBattleResources();
}
break;
}
}
u32 GetBattleBgAttribute(u8 arrayId, u8 caseId)
{
u32 ret = 0;
switch (caseId)
{
case 0:
ret = gBattleBgTemplates[arrayId].bg;
break;
case 1:
ret = gBattleBgTemplates[arrayId].charBaseIndex;
break;
case 2:
ret = gBattleBgTemplates[arrayId].mapBaseIndex;
break;
case 3:
ret = gBattleBgTemplates[arrayId].screenSize;
break;
case 4:
ret = gBattleBgTemplates[arrayId].paletteMode;
break;
case 5:
ret = gBattleBgTemplates[arrayId].priority;
break;
case 6:
ret = gBattleBgTemplates[arrayId].baseTile;
break;
}
return ret;
}
static void TryCorrectShedinjaLanguage(struct Pokemon *mon)
{
u8 nickname[POKEMON_NAME_LENGTH + 1];
u8 language = LANGUAGE_JAPANESE;
if (GetMonData(mon, MON_DATA_SPECIES) == SPECIES_SHEDINJA
&& GetMonData(mon, MON_DATA_LANGUAGE) != language)
{
GetMonData(mon, MON_DATA_NICKNAME, nickname);
if (StringCompareWithoutExtCtrlCodes(nickname, sText_ShedinjaJpnName) == 0)
SetMonData(mon, MON_DATA_LANGUAGE, &language);
}
}
#define sBattler data[0]
#define sSpeciesId data[2]
void SpriteCB_WildMon(struct Sprite *sprite)
{
sprite->callback = SpriteCB_MoveWildMonToRight;
StartSpriteAnimIfDifferent(sprite, 0);
BeginNormalPaletteFade(0x20000, 0, 10, 10, RGB(8, 8, 8));
}
static void SpriteCB_MoveWildMonToRight(struct Sprite *sprite)
{
if ((gIntroSlideFlags & 1) == 0)
{
sprite->pos2.x += 2;
if (sprite->pos2.x == 0)
{
sprite->callback = SpriteCB_WildMonShowHealthbox;
PlayCry1(sprite->data[2], 25);
}
}
}
static void SpriteCB_WildMonShowHealthbox(struct Sprite *sprite)
{
if (sprite->animEnded)
{
sub_804BD94(sprite->sBattler);
SetHealthboxSpriteVisible(gHealthboxSpriteIds[sprite->sBattler]);
sprite->callback = SpriteCallbackDummy2;
StartSpriteAnimIfDifferent(sprite, 0);
BeginNormalPaletteFade(0x20000, 0, 10, 0, RGB(8, 8, 8));
}
}
void SpriteCallbackDummy2(struct Sprite *sprite)
{
}
// not used
static void sub_8011E28(struct Sprite *sprite)
{
sprite->data[3] = 6;
sprite->data[4] = 1;
sprite->callback = sub_8011E3C;
}
// not used
static void sub_8011E3C(struct Sprite *sprite)
{
--sprite->data[4];
if (sprite->data[4] == 0)
{
sprite->data[4] = 8;
sprite->invisible ^= 1;
--sprite->data[3];
if (sprite->data[3] == 0)
{
sprite->invisible = FALSE;
sprite->callback = SpriteCallbackDummy2;
gUnknown_2022AE8[0] = 0;
}
}
}
void SpriteCB_FaintOpponentMon(struct Sprite *sprite)
{
u8 battler = sprite->sBattler;
u16 species;
u8 yOffset;
if (gBattleSpritesDataPtr->battlerData[battler].transformSpecies != 0)
species = gBattleSpritesDataPtr->battlerData[battler].transformSpecies;
else
species = sprite->sSpeciesId;
GetMonData(&gEnemyParty[gBattlerPartyIndexes[battler]], MON_DATA_PERSONALITY); // Unused return value.
if (species == SPECIES_UNOWN)
{
u32 personalityValue = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battler]], MON_DATA_PERSONALITY);
u16 unownForm = ((((personalityValue & 0x3000000) >> 18) | ((personalityValue & 0x30000) >> 12) | ((personalityValue & 0x300) >> 6) | (personalityValue & 3)) % 0x1C);
u16 unownSpecies;
if (unownForm == 0)
unownSpecies = SPECIES_UNOWN; // Use the A Unown form.
else
unownSpecies = NUM_SPECIES + unownForm; // Use one of the other Unown letters.
yOffset = gMonFrontPicCoords[unownSpecies].y_offset;
}
else if (species == SPECIES_CASTFORM)
{
yOffset = gCastformFrontSpriteCoords[gBattleMonForms[battler]].y_offset;
}
else if (species > NUM_SPECIES)
{
yOffset = gMonFrontPicCoords[SPECIES_NONE].y_offset;
}
else
{
yOffset = gMonFrontPicCoords[species].y_offset;
}
sprite->data[3] = 8 - yOffset / 8;
sprite->data[4] = 1;
sprite->callback = SpriteCB_AnimFaintOpponent;
}
static void SpriteCB_AnimFaintOpponent(struct Sprite *sprite)
{
s32 i;
if (--sprite->data[4] == 0)
{
sprite->data[4] = 2;
sprite->pos2.y += 8; // Move the sprite down.
if (--sprite->data[3] < 0)
{
FreeSpriteOamMatrix(sprite);
DestroySprite(sprite);
}
else // Erase bottom part of the sprite to create a smooth illusion of mon falling down.
{
u8 *dst = (u8 *)gMonSpritesGfxPtr->sprites[GetBattlerPosition(sprite->sBattler)] + (gBattleMonForms[sprite->sBattler] << 11) + (sprite->data[3] << 8);
for (i = 0; i < 0x100; ++i)
*(dst++) = 0;
StartSpriteAnim(sprite, gBattleMonForms[sprite->sBattler]);
}
}
}
void sub_8012044(struct Sprite *sprite)
{
sprite->data[3] = 8;
sprite->data[4] = sprite->invisible;
sprite->callback = sub_8012060;
}
static void sub_8012060(struct Sprite *sprite)
{
--sprite->data[3];
if (sprite->data[3] == 0)
{
sprite->invisible ^= 1;
sprite->data[3] = 8;
}
}
void sub_8012098(struct Sprite *sprite)
{
sprite->invisible = sprite->data[4];
sprite->data[4] = FALSE;
sprite->callback = SpriteCallbackDummy2;
}
void sub_80120C4(struct Sprite *sprite)
{
sprite->callback = oac_poke_ally_;
}
static void oac_poke_ally_(struct Sprite *sprite)
{
if (!(gIntroSlideFlags & 1))
{
sprite->pos2.x -= 2;
if (sprite->pos2.x == 0)
{
sprite->callback = SpriteCallbackDummy3;
sprite->data[1] = 0;
}
}
}
void sub_8012100(struct Sprite *sprite)
{
sprite->callback = SpriteCallbackDummy3;
}
static void SpriteCallbackDummy3(struct Sprite *sprite)
{
}
void sub_8012110(struct Sprite *sprite)
{
if (!(gIntroSlideFlags & 1))
{
sprite->pos2.x += sprite->data[1];
sprite->pos2.y += sprite->data[2];
}
}
#define sSinIndex data[0]
#define sDelta data[1]
#define sAmplitude data[2]
#define sBouncerSpriteId data[3]
#define sWhich data[4]
void DoBounceEffect(u8 battler, u8 which, s8 delta, s8 amplitude)
{
u8 invisibleSpriteId;
u8 bouncerSpriteId;
switch (which)
{
case BOUNCE_HEALTHBOX:
default:
if (gBattleSpritesDataPtr->healthBoxesData[battler].healthboxIsBouncing)
return;
break;
case BOUNCE_MON:
if (gBattleSpritesDataPtr->healthBoxesData[battler].battlerIsBouncing)
return;
break;
}
invisibleSpriteId = CreateInvisibleSpriteWithCallback(SpriteCB_BounceEffect);
if (which == BOUNCE_HEALTHBOX)
{
bouncerSpriteId = gHealthboxSpriteIds[battler];
gBattleSpritesDataPtr->healthBoxesData[battler].healthboxBounceSpriteId = invisibleSpriteId;
gBattleSpritesDataPtr->healthBoxesData[battler].healthboxIsBouncing = 1;
gSprites[invisibleSpriteId].sSinIndex = 128; // 0
}
else
{
bouncerSpriteId = gBattlerSpriteIds[battler];
gBattleSpritesDataPtr->healthBoxesData[battler].battlerBounceSpriteId = invisibleSpriteId;
gBattleSpritesDataPtr->healthBoxesData[battler].battlerIsBouncing = 1;
gSprites[invisibleSpriteId].sSinIndex = 192; // -1
}
gSprites[invisibleSpriteId].sDelta = delta;
gSprites[invisibleSpriteId].sAmplitude = amplitude;
gSprites[invisibleSpriteId].sBouncerSpriteId = bouncerSpriteId;
gSprites[invisibleSpriteId].sWhich = which;
gSprites[bouncerSpriteId].pos2.x = 0;
gSprites[bouncerSpriteId].pos2.y = 0;
}
void EndBounceEffect(u8 battler, u8 which)
{
u8 bouncerSpriteId;
if (which == BOUNCE_HEALTHBOX)
{
if (!gBattleSpritesDataPtr->healthBoxesData[battler].healthboxIsBouncing)
return;
bouncerSpriteId = gSprites[gBattleSpritesDataPtr->healthBoxesData[battler].healthboxBounceSpriteId].sBouncerSpriteId;
DestroySprite(&gSprites[gBattleSpritesDataPtr->healthBoxesData[battler].healthboxBounceSpriteId]);
gBattleSpritesDataPtr->healthBoxesData[battler].healthboxIsBouncing = 0;
}
else
{
if (!gBattleSpritesDataPtr->healthBoxesData[battler].battlerIsBouncing)
return;
bouncerSpriteId = gSprites[gBattleSpritesDataPtr->healthBoxesData[battler].battlerBounceSpriteId].sBouncerSpriteId;
DestroySprite(&gSprites[gBattleSpritesDataPtr->healthBoxesData[battler].battlerBounceSpriteId]);
gBattleSpritesDataPtr->healthBoxesData[battler].battlerIsBouncing = 0;
}
gSprites[bouncerSpriteId].pos2.x = 0;
gSprites[bouncerSpriteId].pos2.y = 0;
}
static void SpriteCB_BounceEffect(struct Sprite *sprite)
{
u8 bouncerSpriteId = sprite->sBouncerSpriteId;
s32 index;
if (sprite->sWhich == BOUNCE_HEALTHBOX)
index = sprite->sSinIndex;
else
index = sprite->sSinIndex;
gSprites[bouncerSpriteId].pos2.y = Sin(index, sprite->sAmplitude) + sprite->sAmplitude;
sprite->sSinIndex = (sprite->sSinIndex + sprite->sDelta) & 0xFF;
}
void sub_8012354(struct Sprite *sprite)
{
StartSpriteAnim(sprite, 1);
sprite->callback = sub_8012398;
}
void sub_801236C(struct Sprite *sprite)
{
if (sprite->animDelayCounter == 0)
sprite->centerToCornerVecX = gUnknown_824F048[sprite->animCmdIndex];
}
static void sub_8012398(struct Sprite *sprite)
{
sub_801236C(sprite);
if (sprite->animEnded)
sprite->callback = SpriteCallbackDummy3;
}
void nullsub_12(void)
{
}
void BeginBattleIntro(void)
{
BattleStartClearSetData();
gBattleCommunication[1] = 0;
gBattleMainFunc = BattleIntroGetMonsData;
}
static void BattleMainCB1(void)
{
gBattleMainFunc();
for (gActiveBattler = 0; gActiveBattler < gBattlersCount; ++gActiveBattler)
gBattlerControllerFuncs[gActiveBattler]();
}
static void BattleStartClearSetData(void)
{
s32 i;
u32 j;
u8 *dataPtr;
TurnValuesCleanUp(FALSE);
SpecialStatusesClear();
for (i = 0; i < MAX_BATTLERS_COUNT; ++i)
{
gStatuses3[i] = 0;
dataPtr = (u8 *)&gDisableStructs[i];
for (j = 0; j < sizeof(struct DisableStruct); ++j)
dataPtr[j] = 0;
gDisableStructs[i].isFirstTurn = 2;
gUnknown_2023DD4[i] = 0;
gLastMoves[i] = MOVE_NONE;
gLastLandedMoves[i] = MOVE_NONE;
gLastHitByType[i] = 0;
gLastResultingMoves[i] = MOVE_NONE;
gLastHitBy[i] = 0xFF;
gLockedMoves[i] = MOVE_NONE;
gLastPrintedMoves[i] = MOVE_NONE;
gBattleResources->flags->flags[i] = 0;
}
for (i = 0; i < 2; ++i)
{
gSideStatuses[i] = 0;
dataPtr = (u8 *)&gSideTimers[i];
for (j = 0; j < sizeof(struct SideTimer); ++j)
dataPtr[j] = 0;
}
gBattlerAttacker = 0;
gBattlerTarget = 0;
gBattleWeather = 0;
dataPtr = (u8 *)&gWishFutureKnock;
for (i = 0; i < sizeof(struct WishFutureKnock); ++i)
dataPtr[i] = 0;
gHitMarker = 0;
if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_POKEDUDE)) && gSaveBlock2Ptr->optionsBattleSceneOff)
gHitMarker |= HITMARKER_NO_ANIMATIONS;
gBattleScripting.battleStyle = gSaveBlock2Ptr->optionsBattleStyle;
gMultiHitCounter = 0;
gBattleOutcome = 0;
gBattleControllerExecFlags = 0;
gPaydayMoney = 0;
gBattleResources->battleScriptsStack->size = 0;
gBattleResources->battleCallbackStack->size = 0;
for (i = 0; i < BATTLE_COMMUNICATION_ENTRIES_COUNT; ++i)
gBattleCommunication[i] = 0;
gPauseCounterBattle = 0;
gBattleMoveDamage = 0;
gIntroSlideFlags = 0;
gBattleScripting.animTurn = 0;
gBattleScripting.animTargetsHit = 0;
gLeveledUpInBattle = 0;
gAbsentBattlerFlags = 0;
gBattleStruct->runTries = 0;
gBattleStruct->safariGoNearCounter = 0;
gBattleStruct->safariPkblThrowCounter = 0;
*(&gBattleStruct->safariCatchFactor) = gBaseStats[GetMonData(&gEnemyParty[0], MON_DATA_SPECIES)].catchRate * 100 / 1275;
*(&gBattleStruct->safariEscapeFactor) = gBaseStats[GetMonData(&gEnemyParty[0], MON_DATA_SPECIES)].safariZoneFleeRate * 100 / 1275;
if (gBattleStruct->safariEscapeFactor <= 1)
gBattleStruct->safariEscapeFactor = 2;
gBattleStruct->wildVictorySong = 0;
gBattleStruct->moneyMultiplier = 1;
for (i = 0; i < 8; ++i)
{
*((u8 *)gBattleStruct->lastTakenMove + i) = MOVE_NONE;
*((u8 *)gBattleStruct->usedHeldItems + i) = ITEM_NONE;
*((u8 *)gBattleStruct->choicedMove + i) = MOVE_NONE;
*((u8 *)gBattleStruct->changedItems + i) = ITEM_NONE;
*(i + 0 * 8 + (u8 *)(gBattleStruct->lastTakenMoveFrom) + 0) = 0;
*(i + 1 * 8 + (u8 *)(gBattleStruct->lastTakenMoveFrom) + 0) = 0;
*(i + 2 * 8 + (u8 *)(gBattleStruct->lastTakenMoveFrom) + 0) = 0;
*(i + 3 * 8 + (u8 *)(gBattleStruct->lastTakenMoveFrom) + 0) = 0;
}
*(gBattleStruct->AI_monToSwitchIntoId + 0) = PARTY_SIZE;
*(gBattleStruct->AI_monToSwitchIntoId + 1) = PARTY_SIZE;
*(&gBattleStruct->givenExpMons) = 0;
for (i = 0; i < 11; ++i)
gBattleResults.catchAttempts[i] = 0;
gBattleResults.battleTurnCounter = 0;
gBattleResults.playerFaintCounter = 0;
gBattleResults.opponentFaintCounter = 0;
gBattleResults.playerSwitchesCounter = 0;
gBattleResults.numHealingItemsUsed = 0;
gBattleResults.numRevivesUsed = 0;
gBattleResults.playerMonWasDamaged = FALSE;
gBattleResults.usedMasterBall = FALSE;
gBattleResults.lastOpponentSpecies = SPECIES_NONE;
gBattleResults.lastUsedMovePlayer = MOVE_NONE;
gBattleResults.lastUsedMoveOpponent = MOVE_NONE;
gBattleResults.playerMon1Species = SPECIES_NONE;
gBattleResults.playerMon2Species = SPECIES_NONE;
gBattleResults.caughtMonSpecies = SPECIES_NONE;
for (i = 0; i < POKEMON_NAME_LENGTH; ++i)
{
gBattleResults.playerMon1Name[i] = 0;
gBattleResults.playerMon2Name[i] = 0;
gBattleResults.caughtMonNick[i] = 0;
}
}
void SwitchInClearSetData(void)
{
struct DisableStruct disableStructCopy = gDisableStructs[gActiveBattler];
s32 i;
u8 *ptr;
if (gBattleMoves[gCurrentMove].effect != EFFECT_BATON_PASS)
{
for (i = 0; i < NUM_BATTLE_STATS; ++i)
gBattleMons[gActiveBattler].statStages[i] = 6;
for (i = 0; i < gBattlersCount; ++i)
{
if ((gBattleMons[i].status2 & STATUS2_ESCAPE_PREVENTION) && gDisableStructs[i].battlerPreventingEscape == gActiveBattler)
gBattleMons[i].status2 &= ~STATUS2_ESCAPE_PREVENTION;
if ((gStatuses3[i] & STATUS3_ALWAYS_HITS) && gDisableStructs[i].battlerWithSureHit == gActiveBattler)
{
gStatuses3[i] &= ~STATUS3_ALWAYS_HITS;
gDisableStructs[i].battlerWithSureHit = 0;
}
}
}
if (gBattleMoves[gCurrentMove].effect == EFFECT_BATON_PASS)
{
gBattleMons[gActiveBattler].status2 &= (STATUS2_CONFUSION | STATUS2_FOCUS_ENERGY | STATUS2_SUBSTITUTE | STATUS2_ESCAPE_PREVENTION | STATUS2_CURSED);
gStatuses3[gActiveBattler] &= (STATUS3_LEECHSEED_BATTLER | STATUS3_LEECHSEED | STATUS3_ALWAYS_HITS | STATUS3_PERISH_SONG | STATUS3_ROOTED | STATUS3_MUDSPORT | STATUS3_WATERSPORT);
for (i = 0; i < gBattlersCount; ++i)
{
if (GetBattlerSide(gActiveBattler) != GetBattlerSide(i)
&& (gStatuses3[i] & STATUS3_ALWAYS_HITS) != 0
&& (gDisableStructs[i].battlerWithSureHit == gActiveBattler))
{
gStatuses3[i] &= ~(STATUS3_ALWAYS_HITS);
gStatuses3[i] |= 0x10;
}
}
}
else
{
gBattleMons[gActiveBattler].status2 = 0;
gStatuses3[gActiveBattler] = 0;
}
for (i = 0; i < gBattlersCount; ++i)
{
if (gBattleMons[i].status2 & STATUS2_INFATUATED_WITH(gActiveBattler))
gBattleMons[i].status2 &= ~(STATUS2_INFATUATED_WITH(gActiveBattler));
if ((gBattleMons[i].status2 & STATUS2_WRAPPED) && *(gBattleStruct->wrappedBy + i) == gActiveBattler)
gBattleMons[i].status2 &= ~(STATUS2_WRAPPED);
}
gActionSelectionCursor[gActiveBattler] = 0;
gMoveSelectionCursor[gActiveBattler] = 0;
ptr = (u8 *)&gDisableStructs[gActiveBattler];
for (i = 0; i < sizeof(struct DisableStruct); ++i)
ptr[i] = 0;
if (gBattleMoves[gCurrentMove].effect == EFFECT_BATON_PASS)
{
gDisableStructs[gActiveBattler].substituteHP = disableStructCopy.substituteHP;
gDisableStructs[gActiveBattler].battlerWithSureHit = disableStructCopy.battlerWithSureHit;
gDisableStructs[gActiveBattler].perishSongTimer = disableStructCopy.perishSongTimer;
gDisableStructs[gActiveBattler].perishSongTimerStartValue = disableStructCopy.perishSongTimerStartValue;
gDisableStructs[gActiveBattler].battlerPreventingEscape = disableStructCopy.battlerPreventingEscape;
}
gMoveResultFlags = 0;
gDisableStructs[gActiveBattler].isFirstTurn = 2;
gLastMoves[gActiveBattler] = MOVE_NONE;
gLastLandedMoves[gActiveBattler] = MOVE_NONE;
gLastHitByType[gActiveBattler] = 0;
gLastResultingMoves[gActiveBattler] = MOVE_NONE;
gLastPrintedMoves[gActiveBattler] = MOVE_NONE;
gLastHitBy[gActiveBattler] = 0xFF;
*(gBattleStruct->lastTakenMove + gActiveBattler * 2 + 0) = MOVE_NONE;
*(gBattleStruct->lastTakenMove + gActiveBattler * 2 + 1) = MOVE_NONE;
*(0 * 2 + gActiveBattler * 8 + (u8 *)(gBattleStruct->lastTakenMoveFrom) + 0) = 0;
*(0 * 2 + gActiveBattler * 8 + (u8 *)(gBattleStruct->lastTakenMoveFrom) + 1) = 0;
*(1 * 2 + gActiveBattler * 8 + (u8 *)(gBattleStruct->lastTakenMoveFrom) + 0) = 0;
*(1 * 2 + gActiveBattler * 8 + (u8 *)(gBattleStruct->lastTakenMoveFrom) + 1) = 0;
*(2 * 2 + gActiveBattler * 8 + (u8 *)(gBattleStruct->lastTakenMoveFrom) + 0) = 0;
*(2 * 2 + gActiveBattler * 8 + (u8 *)(gBattleStruct->lastTakenMoveFrom) + 1) = 0;
*(3 * 2 + gActiveBattler * 8 + (u8 *)(gBattleStruct->lastTakenMoveFrom) + 0) = 0;
*(3 * 2 + gActiveBattler * 8 + (u8 *)(gBattleStruct->lastTakenMoveFrom) + 1) = 0;
for (i = 0; i < gBattlersCount; ++i)
{
if (i != gActiveBattler)
{
*(gBattleStruct->lastTakenMove + i * 2 + 0) = MOVE_NONE;
*(gBattleStruct->lastTakenMove + i * 2 + 1) = MOVE_NONE;
}
*(i * 8 + gActiveBattler * 2 + (u8 *)(gBattleStruct->lastTakenMoveFrom) + 0) = 0;
*(i * 8 + gActiveBattler * 2 + (u8 *)(gBattleStruct->lastTakenMoveFrom) + 1) = 0;
}
*((u8 *)(&gBattleStruct->choicedMove[gActiveBattler]) + 0) = MOVE_NONE;
*((u8 *)(&gBattleStruct->choicedMove[gActiveBattler]) + 1) = MOVE_NONE;
gBattleResources->flags->flags[gActiveBattler] = 0;
gCurrentMove = MOVE_NONE;
}
void FaintClearSetData(void)
{
s32 i;
u8 *ptr;
for (i = 0; i < NUM_BATTLE_STATS; ++i)
gBattleMons[gActiveBattler].statStages[i] = 6;
gBattleMons[gActiveBattler].status2 = 0;
gStatuses3[gActiveBattler] = 0;
for (i = 0; i < gBattlersCount; ++i)
{
if ((gBattleMons[i].status2 & STATUS2_ESCAPE_PREVENTION) && gDisableStructs[i].battlerPreventingEscape == gActiveBattler)
gBattleMons[i].status2 &= ~STATUS2_ESCAPE_PREVENTION;
if (gBattleMons[i].status2 & STATUS2_INFATUATED_WITH(gActiveBattler))
gBattleMons[i].status2 &= ~(STATUS2_INFATUATED_WITH(gActiveBattler));
if ((gBattleMons[i].status2 & STATUS2_WRAPPED) && *(gBattleStruct->wrappedBy + i) == gActiveBattler)
gBattleMons[i].status2 &= ~(STATUS2_WRAPPED);
}
gActionSelectionCursor[gActiveBattler] = 0;
gMoveSelectionCursor[gActiveBattler] = 0;
ptr = (u8 *)&gDisableStructs[gActiveBattler];
for (i = 0; i < sizeof(struct DisableStruct); ++i)
ptr[i] = 0;
gProtectStructs[gActiveBattler].protected = FALSE;
gProtectStructs[gActiveBattler].endured = FALSE;
gProtectStructs[gActiveBattler].noValidMoves = FALSE;
gProtectStructs[gActiveBattler].helpingHand = FALSE;
gProtectStructs[gActiveBattler].bounceMove = FALSE;
gProtectStructs[gActiveBattler].stealMove = FALSE;
gProtectStructs[gActiveBattler].flag0Unknown = FALSE;
gProtectStructs[gActiveBattler].prlzImmobility = FALSE;
gProtectStructs[gActiveBattler].confusionSelfDmg = FALSE;
gProtectStructs[gActiveBattler].targetNotAffected = FALSE;
gProtectStructs[gActiveBattler].chargingTurn = FALSE;
gProtectStructs[gActiveBattler].fleeFlag = 0;
gProtectStructs[gActiveBattler].usedImprisonedMove = FALSE;
gProtectStructs[gActiveBattler].loveImmobility = FALSE;
gProtectStructs[gActiveBattler].usedDisabledMove = FALSE;
gProtectStructs[gActiveBattler].usedTauntedMove = FALSE;
gProtectStructs[gActiveBattler].flag2Unknown = FALSE;
gProtectStructs[gActiveBattler].flinchImmobility = FALSE;
gProtectStructs[gActiveBattler].notFirstStrike = FALSE;
gDisableStructs[gActiveBattler].isFirstTurn = 2;
gLastMoves[gActiveBattler] = MOVE_NONE;
gLastLandedMoves[gActiveBattler] = MOVE_NONE;
gLastHitByType[gActiveBattler] = MOVE_NONE;
gLastResultingMoves[gActiveBattler] = MOVE_NONE;
gLastPrintedMoves[gActiveBattler] = MOVE_NONE;
gLastHitBy[gActiveBattler] = 0xFF;
*((u8 *)(&gBattleStruct->choicedMove[gActiveBattler]) + 0) = MOVE_NONE;
*((u8 *)(&gBattleStruct->choicedMove[gActiveBattler]) + 1) = MOVE_NONE;
*(gBattleStruct->lastTakenMove + gActiveBattler * 2 + 0) = MOVE_NONE;
*(gBattleStruct->lastTakenMove + gActiveBattler * 2 + 1) = MOVE_NONE;
*(0 * 2 + gActiveBattler * 8 + (u8 *)(gBattleStruct->lastTakenMoveFrom) + 0) = 0;
*(0 * 2 + gActiveBattler * 8 + (u8 *)(gBattleStruct->lastTakenMoveFrom) + 1) = 0;
*(1 * 2 + gActiveBattler * 8 + (u8 *)(gBattleStruct->lastTakenMoveFrom) + 0) = 0;
*(1 * 2 + gActiveBattler * 8 + (u8 *)(gBattleStruct->lastTakenMoveFrom) + 1) = 0;
*(2 * 2 + gActiveBattler * 8 + (u8 *)(gBattleStruct->lastTakenMoveFrom) + 0) = 0;
*(2 * 2 + gActiveBattler * 8 + (u8 *)(gBattleStruct->lastTakenMoveFrom) + 1) = 0;
*(3 * 2 + gActiveBattler * 8 + (u8 *)(gBattleStruct->lastTakenMoveFrom) + 0) = 0;
*(3 * 2 + gActiveBattler * 8 + (u8 *)(gBattleStruct->lastTakenMoveFrom) + 1) = 0;
for (i = 0; i < gBattlersCount; ++i)
{
if (i != gActiveBattler)
{
*(gBattleStruct->lastTakenMove + i * 2 + 0) = MOVE_NONE;
*(gBattleStruct->lastTakenMove + i * 2 + 1) = MOVE_NONE;
}
*(i * 8 + gActiveBattler * 2 + (u8 *)(gBattleStruct->lastTakenMoveFrom) + 0) = 0;
*(i * 8 + gActiveBattler * 2 + (u8 *)(gBattleStruct->lastTakenMoveFrom) + 1) = 0;
}
gBattleResources->flags->flags[gActiveBattler] = 0;
gBattleMons[gActiveBattler].type1 = gBaseStats[gBattleMons[gActiveBattler].species].type1;
gBattleMons[gActiveBattler].type2 = gBaseStats[gBattleMons[gActiveBattler].species].type2;
}
static void BattleIntroGetMonsData(void)
{
switch (gBattleCommunication[MULTIUSE_STATE])
{
case 0:
gActiveBattler = gBattleCommunication[1];
BtlController_EmitGetMonData(0, REQUEST_ALL_BATTLE, 0);
MarkBattlerForControllerExec(gActiveBattler);
++gBattleCommunication[MULTIUSE_STATE];
break;
case 1:
if (gBattleControllerExecFlags == 0)
{
++gBattleCommunication[1];
if (gBattleCommunication[1] == gBattlersCount)
gBattleMainFunc = BattleIntroPrepareBackgroundSlide;
else
gBattleCommunication[MULTIUSE_STATE] = 0;
}
break;
}
}
static void BattleIntroPrepareBackgroundSlide(void)
{
if (gBattleControllerExecFlags == 0)
{
gActiveBattler = GetBattlerAtPosition(0);
BtlController_EmitIntroSlide(0, gBattleTerrain);
MarkBattlerForControllerExec(gActiveBattler);
gBattleMainFunc = BattleIntroDrawTrainersOrMonsSprites;
gBattleCommunication[MULTIUSE_STATE] = 0;
gBattleCommunication[SPRITES_INIT_STATE1] = 0;
}
}
static void BattleIntroDrawTrainersOrMonsSprites(void)
{
u8 *ptr;
s32 i;
if (!gBattleControllerExecFlags)
{
for (gActiveBattler = 0; gActiveBattler < gBattlersCount; ++gActiveBattler)
{
if ((gBattleTypeFlags & BATTLE_TYPE_SAFARI)
&& GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER)
{
ptr = (u8 *)&gBattleMons[gActiveBattler];
for (i = 0; i < sizeof(struct BattlePokemon); ++i)
ptr[i] = 0;
}
else
{
u16 *hpOnSwitchout;
ptr = (u8 *)&gBattleMons[gActiveBattler];
for (i = 0; i < sizeof(struct BattlePokemon); ++i)
ptr[i] = gBattleBufferB[gActiveBattler][4 + i];
gBattleMons[gActiveBattler].type1 = gBaseStats[gBattleMons[gActiveBattler].species].type1;
gBattleMons[gActiveBattler].type2 = gBaseStats[gBattleMons[gActiveBattler].species].type2;
gBattleMons[gActiveBattler].ability = GetAbilityBySpecies(gBattleMons[gActiveBattler].species, gBattleMons[gActiveBattler].abilityNum);
hpOnSwitchout = &gBattleStruct->hpOnSwitchout[GetBattlerSide(gActiveBattler)];
*hpOnSwitchout = gBattleMons[gActiveBattler].hp;
for (i = 0; i < NUM_BATTLE_STATS; ++i)
gBattleMons[gActiveBattler].statStages[i] = 6;
gBattleMons[gActiveBattler].status2 = 0;
}
if (GetBattlerPosition(gActiveBattler) == B_POSITION_PLAYER_LEFT)
{
BtlController_EmitDrawTrainerPic(0);
MarkBattlerForControllerExec(gActiveBattler);
}
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER)
{
if (GetBattlerPosition(gActiveBattler) == B_POSITION_OPPONENT_LEFT)
{
BtlController_EmitDrawTrainerPic(0);
MarkBattlerForControllerExec(gActiveBattler);
}
if (GetBattlerSide(gActiveBattler) == B_SIDE_OPPONENT
&& !(gBattleTypeFlags & (BATTLE_TYPE_EREADER_TRAINER
| BATTLE_TYPE_POKEDUDE
| BATTLE_TYPE_LINK
| BATTLE_TYPE_GHOST
| BATTLE_TYPE_OLD_MAN_TUTORIAL
| BATTLE_TYPE_LEGENDARY)))
HandleSetPokedexFlag(SpeciesToNationalPokedexNum(gBattleMons[gActiveBattler].species), FLAG_SET_SEEN, gBattleMons[gActiveBattler].personality);
}
else
{
if (GetBattlerSide(gActiveBattler) == B_SIDE_OPPONENT)
{
if (gBattleTypeFlags & (BATTLE_TYPE_GHOST | BATTLE_TYPE_LEGENDARY))
{
if ((gBattleTypeFlags & (BATTLE_TYPE_GHOST | BATTLE_TYPE_LEGENDARY)) != BATTLE_TYPE_GHOST)
HandleSetPokedexFlag(SpeciesToNationalPokedexNum(gBattleMons[gActiveBattler].species), FLAG_SET_SEEN, gBattleMons[gActiveBattler].personality);
}
else if (!(gBattleTypeFlags & (BATTLE_TYPE_EREADER_TRAINER
| BATTLE_TYPE_POKEDUDE
| BATTLE_TYPE_LINK
| BATTLE_TYPE_GHOST
| BATTLE_TYPE_OLD_MAN_TUTORIAL
| BATTLE_TYPE_LEGENDARY)))
{
HandleSetPokedexFlag(SpeciesToNationalPokedexNum(gBattleMons[gActiveBattler].species), FLAG_SET_SEEN, gBattleMons[gActiveBattler].personality);
}
BtlController_EmitLoadMonSprite(0);
MarkBattlerForControllerExec(gActiveBattler);
}
}
if (gBattleTypeFlags & BATTLE_TYPE_MULTI
&& (GetBattlerPosition(gActiveBattler) == B_POSITION_PLAYER_RIGHT || GetBattlerPosition(gActiveBattler) == B_POSITION_OPPONENT_RIGHT))
{
BtlController_EmitDrawTrainerPic(0);
MarkBattlerForControllerExec(gActiveBattler);
}
}
gBattleMainFunc = BattleIntroDrawPartySummaryScreens;
}
}
static void BattleIntroDrawPartySummaryScreens(void)
{
s32 i;
struct HpAndStatus hpStatus[PARTY_SIZE];
if (!gBattleControllerExecFlags)
{
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER)
{
for (i = 0; i < PARTY_SIZE; ++i)
{
if (GetMonData(&gEnemyParty[i], MON_DATA_SPECIES2) == SPECIES_NONE
|| GetMonData(&gEnemyParty[i], MON_DATA_SPECIES2) == SPECIES_EGG)
{
hpStatus[i].hp = 0xFFFF;
hpStatus[i].status = 0;
}
else
{
hpStatus[i].hp = GetMonData(&gEnemyParty[i], MON_DATA_HP);
hpStatus[i].status = GetMonData(&gEnemyParty[i], MON_DATA_STATUS);
}
}
gActiveBattler = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
BtlController_EmitDrawPartyStatusSummary(0, hpStatus, 0x80);
MarkBattlerForControllerExec(gActiveBattler);
for (i = 0; i < PARTY_SIZE; ++i)
{
if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES2) == SPECIES_NONE
|| GetMonData(&gPlayerParty[i], MON_DATA_SPECIES2) == SPECIES_EGG)
{
hpStatus[i].hp = 0xFFFF;
hpStatus[i].status = 0;
}
else
{
hpStatus[i].hp = GetMonData(&gPlayerParty[i], MON_DATA_HP);
hpStatus[i].status = GetMonData(&gPlayerParty[i], MON_DATA_STATUS);
}
}
gActiveBattler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
BtlController_EmitDrawPartyStatusSummary(0, hpStatus, 0x80);
MarkBattlerForControllerExec(gActiveBattler);
gBattleMainFunc = BattleIntroPrintTrainerWantsToBattle;
}
else
{
// The struct gets set here, but nothing is ever done with it since
// wild battles don't show the party summary.
// Still, there's no point in having dead code.
for (i = 0; i < PARTY_SIZE; ++i)
{
if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES2) == SPECIES_NONE
|| GetMonData(&gPlayerParty[i], MON_DATA_SPECIES2) == SPECIES_EGG)
{
hpStatus[i].hp = 0xFFFF;
hpStatus[i].status = 0;
}
else
{
hpStatus[i].hp = GetMonData(&gPlayerParty[i], MON_DATA_HP);
hpStatus[i].status = GetMonData(&gPlayerParty[i], MON_DATA_STATUS);
}
}
gBattleMainFunc = BattleIntroPrintWildMonAttacked;
}
}
}
static void BattleIntroPrintTrainerWantsToBattle(void)
{
if (!gBattleControllerExecFlags)
{
gActiveBattler = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
PrepareStringBattle(STRINGID_INTROMSG, gActiveBattler);
gBattleMainFunc = BattleIntroPrintOpponentSendsOut;
}
}
static void BattleIntroPrintWildMonAttacked(void)
{
if (!gBattleControllerExecFlags)
{
gBattleMainFunc = BattleIntroPrintPlayerSendsOut;
PrepareStringBattle(STRINGID_INTROMSG, 0);
if ((gBattleTypeFlags & (BATTLE_TYPE_LEGENDARY | BATTLE_TYPE_GHOST)) == (BATTLE_TYPE_LEGENDARY | BATTLE_TYPE_GHOST))
{
gBattleScripting.battler = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
BattleScriptExecute(BattleScript_SilphScopeUnveiled);
}
}
}
static void BattleIntroPrintOpponentSendsOut(void)
{
if (!gBattleControllerExecFlags)
{
PrepareStringBattle(STRINGID_INTROSENDOUT, GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT));
gBattleMainFunc = BattleIntroOpponentSendsOutMonAnimation;
}
}
static void BattleIntroOpponentSendsOutMonAnimation(void)
{
if (!gBattleControllerExecFlags)
{
for (gActiveBattler = 0; gActiveBattler < gBattlersCount; ++gActiveBattler)
{
if (GetBattlerPosition(gActiveBattler) == B_POSITION_OPPONENT_LEFT)
{
BtlController_EmitIntroTrainerBallThrow(0);
MarkBattlerForControllerExec(gActiveBattler);
}
if (gBattleTypeFlags & BATTLE_TYPE_MULTI && GetBattlerPosition(gActiveBattler) == B_POSITION_OPPONENT_RIGHT)
{
BtlController_EmitIntroTrainerBallThrow(0);
MarkBattlerForControllerExec(gActiveBattler);
}
}
gBattleMainFunc = BattleIntroRecordMonsToDex;
}
}
static void BattleIntroRecordMonsToDex(void)
{
if (!gBattleControllerExecFlags)
{
for (gActiveBattler = 0; gActiveBattler < gBattlersCount; ++gActiveBattler)
if (GetBattlerSide(gActiveBattler) == B_SIDE_OPPONENT
&& !(gBattleTypeFlags & (BATTLE_TYPE_EREADER_TRAINER
| BATTLE_TYPE_POKEDUDE
| BATTLE_TYPE_LINK
| BATTLE_TYPE_GHOST
| BATTLE_TYPE_OLD_MAN_TUTORIAL
| BATTLE_TYPE_LEGENDARY)))
HandleSetPokedexFlag(SpeciesToNationalPokedexNum(gBattleMons[gActiveBattler].species), FLAG_SET_SEEN, gBattleMons[gActiveBattler].personality);
gBattleMainFunc = BattleIntroPrintPlayerSendsOut;
}
}
// not used
static void sub_80136C4(void)
{
if (!gBattleControllerExecFlags)
gBattleMainFunc = BattleIntroPrintPlayerSendsOut;
}
void BattleIntroPrintPlayerSendsOut(void)
{
if (!gBattleControllerExecFlags)
{
if (!(gBattleTypeFlags & BATTLE_TYPE_SAFARI))
PrepareStringBattle(STRINGID_INTROSENDOUT, GetBattlerAtPosition(B_POSITION_PLAYER_LEFT));
gBattleMainFunc = BattleIntroPlayerSendsOutMonAnimation;
}
}
static void BattleIntroPlayerSendsOutMonAnimation(void)
{
u32 position;
if (!gBattleControllerExecFlags)
{
for (gActiveBattler = 0; gActiveBattler < gBattlersCount; ++gActiveBattler)
{
if (GetBattlerPosition(gActiveBattler) == B_POSITION_PLAYER_LEFT)
{
BtlController_EmitIntroTrainerBallThrow(0);
MarkBattlerForControllerExec(gActiveBattler);
}
if (gBattleTypeFlags & BATTLE_TYPE_MULTI && GetBattlerPosition(gActiveBattler) == B_POSITION_PLAYER_RIGHT)
{
BtlController_EmitIntroTrainerBallThrow(0);
MarkBattlerForControllerExec(gActiveBattler);
}
}
gBattleStruct->switchInAbilitiesCounter = 0;
gBattleStruct->switchInItemsCounter = 0;
gBattleStruct->overworldWeatherDone = FALSE;
gBattleMainFunc = TryDoEventsBeforeFirstTurn;
}
}
// not used
static void sub_80137D0(void)
{
if (!gBattleControllerExecFlags)
{
for (gActiveBattler = 0; gActiveBattler < gBattlersCount; ++gActiveBattler)
{
if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER)
{
BtlController_EmitSwitchInAnim(0, gBattlerPartyIndexes[gActiveBattler], FALSE);
MarkBattlerForControllerExec(gActiveBattler);
}
}
gBattleStruct->switchInAbilitiesCounter = 0;
gBattleStruct->switchInItemsCounter = 0;
gBattleStruct->overworldWeatherDone = FALSE;
gBattleMainFunc = TryDoEventsBeforeFirstTurn;
}
}
static void TryDoEventsBeforeFirstTurn(void)
{
s32 i, j;
u8 effect = 0;
if (!gBattleControllerExecFlags)
{
if (gBattleStruct->switchInAbilitiesCounter == 0)
{
for (i = 0; i < gBattlersCount; ++i)
gBattlerByTurnOrder[i] = i;
for (i = 0; i < gBattlersCount - 1; ++i)
for (j = i + 1; j < gBattlersCount; ++j)
if (GetWhoStrikesFirst(gBattlerByTurnOrder[i], gBattlerByTurnOrder[j], TRUE) != 0)
SwapTurnOrder(i, j);
}
if (!gBattleStruct->overworldWeatherDone
&& AbilityBattleEffects(0, 0, 0, ABILITYEFFECT_SWITCH_IN_WEATHER, 0) != 0)
{
gBattleStruct->overworldWeatherDone = TRUE;
return;
}
// Check all switch in abilities happening from the fastest mon to slowest.
while (gBattleStruct->switchInAbilitiesCounter < gBattlersCount)
{
if (AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, gBattlerByTurnOrder[gBattleStruct->switchInAbilitiesCounter], 0, 0, 0) != 0)
++effect;
++gBattleStruct->switchInAbilitiesCounter;
if (effect)
return;
}
if (AbilityBattleEffects(ABILITYEFFECT_INTIMIDATE1, 0, 0, 0, 0) != 0)
return;
if (AbilityBattleEffects(ABILITYEFFECT_TRACE, 0, 0, 0, 0) != 0)
return;
// Check all switch in items having effect from the fastest mon to slowest.
while (gBattleStruct->switchInItemsCounter < gBattlersCount)
{
if (ItemBattleEffects(ITEMEFFECT_ON_SWITCH_IN, gBattlerByTurnOrder[gBattleStruct->switchInItemsCounter], FALSE))
++effect;
++gBattleStruct->switchInItemsCounter;
if (effect)
return;
}
for (i = 0; i < gBattlersCount; ++i) // pointless, ruby leftover
;
for (i = 0; i < MAX_BATTLERS_COUNT; ++i)
{
*(gBattleStruct->monToSwitchIntoId + i) = PARTY_SIZE;
gChosenActionByBattler[i] = B_ACTION_NONE;
gChosenMoveByBattler[i] = MOVE_NONE;
}
TurnValuesCleanUp(FALSE);
SpecialStatusesClear();
*(&gBattleStruct->field_91) = gAbsentBattlerFlags;
gBattleMainFunc = HandleTurnActionSelectionState;
ResetSentPokesToOpponentValue();
for (i = 0; i < BATTLE_COMMUNICATION_ENTRIES_COUNT; ++i)
gBattleCommunication[i] = 0;
for (i = 0; i < gBattlersCount; ++i)
gBattleMons[i].status2 &= ~(STATUS2_FLINCHED);
*(&gBattleStruct->turnEffectsTracker) = 0;
*(&gBattleStruct->turnEffectsBattlerId) = 0;
*(&gBattleStruct->wishPerishSongState) = 0;
*(&gBattleStruct->wishPerishSongBattlerId) = 0;
gBattleScripting.atk49_state = 0;
gBattleStruct->faintedActionsState = 0;
gBattleStruct->turnCountersTracker = 0;
gMoveResultFlags = 0;
gRandomTurnNumber = Random();
}
}
static void HandleEndTurn_ContinueBattle(void)
{
s32 i;
if (!gBattleControllerExecFlags)
{
gBattleMainFunc = BattleTurnPassed;
for (i = 0; i < BATTLE_COMMUNICATION_ENTRIES_COUNT; ++i)
gBattleCommunication[i] = 0;
for (i = 0; i < gBattlersCount; ++i)
{
gBattleMons[i].status2 &= ~(STATUS2_FLINCHED);
if ((gBattleMons[i].status1 & STATUS1_SLEEP) && (gBattleMons[i].status2 & STATUS2_MULTIPLETURNS))
CancelMultiTurnMoves(i);
}
gBattleStruct->turnEffectsTracker = 0;
gBattleStruct->turnEffectsBattlerId = 0;
gBattleStruct->wishPerishSongState = 0;
gBattleStruct->wishPerishSongBattlerId = 0;
gBattleStruct->turnCountersTracker = 0;
gMoveResultFlags = 0;
}
}
void BattleTurnPassed(void)
{
s32 i;
TurnValuesCleanUp(TRUE);
if (gBattleOutcome == 0)
{
if (DoFieldEndTurnEffects())
return;
if (DoBattlerEndTurnEffects())
return;
}
if (HandleFaintedMonActions())
return;
gBattleStruct->faintedActionsState = 0;
if (HandleWishPerishSongOnTurnEnd())
return;
TurnValuesCleanUp(FALSE);
gHitMarker &= ~(HITMARKER_NO_ATTACKSTRING);
gHitMarker &= ~(HITMARKER_UNABLE_TO_USE_MOVE);
gHitMarker &= ~(HITMARKER_PLAYER_FAINTED);
gHitMarker &= ~(HITMARKER_x100000);
gBattleScripting.animTurn = 0;
gBattleScripting.animTargetsHit = 0;
gBattleScripting.atk49_state = 0;
gBattleMoveDamage = 0;
gMoveResultFlags = 0;
for (i = 0; i < 5; ++i)
gBattleCommunication[i] = 0;
if (gBattleOutcome != 0)
{
gCurrentActionFuncId = B_ACTION_FINISHED;
gBattleMainFunc = RunTurnActionsFunctions;
return;
}
if (gBattleResults.battleTurnCounter < 0xFF)
++gBattleResults.battleTurnCounter;
for (i = 0; i < gBattlersCount; ++i)
{
gChosenActionByBattler[i] = B_ACTION_NONE;
gChosenMoveByBattler[i] = MOVE_NONE;
}
for (i = 0; i < MAX_BATTLERS_COUNT; ++i)
*(gBattleStruct->monToSwitchIntoId + i) = PARTY_SIZE;
*(&gBattleStruct->field_91) = gAbsentBattlerFlags;
gBattleMainFunc = HandleTurnActionSelectionState;
gRandomTurnNumber = Random();
}
u8 IsRunningFromBattleImpossible(void)
{
u8 holdEffect;
u8 side;
s32 i;
if (gBattleMons[gActiveBattler].item == ITEM_ENIGMA_BERRY)
holdEffect = gEnigmaBerries[gActiveBattler].holdEffect;
else
holdEffect = ItemId_GetHoldEffect(gBattleMons[gActiveBattler].item);
gPotentialItemEffectBattler = gActiveBattler;
if (holdEffect == HOLD_EFFECT_CAN_ALWAYS_RUN
|| (gBattleTypeFlags & BATTLE_TYPE_LINK)
|| gBattleMons[gActiveBattler].ability == ABILITY_RUN_AWAY)
return BATTLE_RUN_SUCCESS;
side = GetBattlerSide(gActiveBattler);
for (i = 0; i < gBattlersCount; ++i)
{
if (side != GetBattlerSide(i)
&& gBattleMons[i].ability == ABILITY_SHADOW_TAG)
{
gBattleScripting.battler = i;
gLastUsedAbility = gBattleMons[i].ability;
gBattleCommunication[MULTISTRING_CHOOSER] = 2;
return BATTLE_RUN_FAILURE;
}
if (side != GetBattlerSide(i)
&& gBattleMons[gActiveBattler].ability != ABILITY_LEVITATE
&& !IS_BATTLER_OF_TYPE(gActiveBattler, TYPE_FLYING)
&& gBattleMons[i].ability == ABILITY_ARENA_TRAP)
{
gBattleScripting.battler = i;
gLastUsedAbility = gBattleMons[i].ability;
gBattleCommunication[MULTISTRING_CHOOSER] = 2;
return BATTLE_RUN_FAILURE;
}
}
i = AbilityBattleEffects(ABILITYEFFECT_CHECK_FIELD_EXCEPT_BATTLER, gActiveBattler, ABILITY_MAGNET_PULL, 0, 0);
if (i != 0 && IS_BATTLER_OF_TYPE(gActiveBattler, TYPE_STEEL))
{
gBattleScripting.battler = i - 1;
gLastUsedAbility = gBattleMons[i - 1].ability;
gBattleCommunication[MULTISTRING_CHOOSER] = 2;
return BATTLE_RUN_FAILURE;
}
if ((gBattleMons[gActiveBattler].status2 & (STATUS2_ESCAPE_PREVENTION | STATUS2_WRAPPED))
|| (gStatuses3[gActiveBattler] & STATUS3_ROOTED))
{
gBattleCommunication[MULTISTRING_CHOOSER] = 0;
return BATTLE_RUN_FORBIDDEN;
}
if (gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE)
{
gBattleCommunication[MULTISTRING_CHOOSER] = 1;
return BATTLE_RUN_FORBIDDEN;
}
return BATTLE_RUN_SUCCESS;
}
void sub_8013F6C(u8 battler)
{
s32 i;
u8 r4, r1;
for (i = 0; i < 3; ++i)
gBattlePartyCurrentOrder[i] = *(battler * 3 + i + (u8 *)(gBattleStruct->field_60));
r4 = GetPartyIdFromBattlePartyId(gBattlerPartyIndexes[battler]);
r1 = GetPartyIdFromBattlePartyId(*(gBattleStruct->monToSwitchIntoId + battler));
SwitchPartyMonSlots(r4, r1);
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
{
for (i = 0; i < 3; ++i)
{
*(battler * 3 + i + (u8 *)(gBattleStruct->field_60)) = gBattlePartyCurrentOrder[i];
*(BATTLE_PARTNER(battler) * 3 + i + (u8 *)(gBattleStruct->field_60)) = gBattlePartyCurrentOrder[i];
}
}
else
{
for (i = 0; i < 3; ++i)
*(battler * 3 + i + (u8 *)(gBattleStruct->field_60)) = gBattlePartyCurrentOrder[i];
}
}
enum
{
STATE_BEFORE_ACTION_CHOSEN,
STATE_WAIT_ACTION_CHOSEN,
STATE_WAIT_ACTION_CASE_CHOSEN,
STATE_WAIT_ACTION_CONFIRMED_STANDBY,
STATE_WAIT_ACTION_CONFIRMED,
STATE_SELECTION_SCRIPT,
STATE_WAIT_SET_BEFORE_ACTION,
};
static void HandleTurnActionSelectionState(void)
{
s32 i;
gBattleCommunication[ACTIONS_CONFIRMED_COUNT] = 0;
for (gActiveBattler = 0; gActiveBattler < gBattlersCount; ++gActiveBattler)
{
u8 position = GetBattlerPosition(gActiveBattler);
switch (gBattleCommunication[gActiveBattler])
{
case STATE_BEFORE_ACTION_CHOSEN: // Choose an action.
*(gBattleStruct->monToSwitchIntoId + gActiveBattler) = PARTY_SIZE;
if (gBattleTypeFlags & BATTLE_TYPE_MULTI
|| (position & BIT_FLANK) == B_FLANK_LEFT
|| gBattleStruct->field_91 & gBitTable[GetBattlerAtPosition(BATTLE_PARTNER(position))]
|| gBattleCommunication[GetBattlerAtPosition(BATTLE_PARTNER(position))] == STATE_WAIT_ACTION_CONFIRMED)
{
if (gBattleStruct->field_91 & gBitTable[gActiveBattler])
{
gChosenActionByBattler[gActiveBattler] = B_ACTION_NOTHING_FAINTED;
if (!(gBattleTypeFlags & BATTLE_TYPE_MULTI))
gBattleCommunication[gActiveBattler] = STATE_WAIT_ACTION_CONFIRMED;
else
gBattleCommunication[gActiveBattler] = STATE_WAIT_ACTION_CONFIRMED_STANDBY;
}
else
{
if (gBattleMons[gActiveBattler].status2 & STATUS2_MULTIPLETURNS
|| gBattleMons[gActiveBattler].status2 & STATUS2_RECHARGE)
{
gChosenActionByBattler[gActiveBattler] = B_ACTION_USE_MOVE;
gBattleCommunication[gActiveBattler] = STATE_WAIT_ACTION_CONFIRMED_STANDBY;
}
else
{
BtlController_EmitChooseAction(0, gChosenActionByBattler[0], gBattleBufferB[0][1] | (gBattleBufferB[0][2] << 8));
MarkBattlerForControllerExec(gActiveBattler);
++gBattleCommunication[gActiveBattler];
}
}
}
break;
case STATE_WAIT_ACTION_CHOSEN: // Try to perform an action.
if (!(gBattleControllerExecFlags & ((gBitTable[gActiveBattler]) | (0xF0000000) | (gBitTable[gActiveBattler] << 4) | (gBitTable[gActiveBattler] << 8) | (gBitTable[gActiveBattler] << 0xC))))
{
gChosenActionByBattler[gActiveBattler] = gBattleBufferB[gActiveBattler][1];
switch (gBattleBufferB[gActiveBattler][1])
{
case B_ACTION_USE_MOVE:
if (AreAllMovesUnusable())
{
gBattleCommunication[gActiveBattler] = STATE_SELECTION_SCRIPT;
*(gBattleStruct->selectionScriptFinished + gActiveBattler) = FALSE;
*(gBattleStruct->stateIdAfterSelScript + gActiveBattler) = STATE_WAIT_ACTION_CONFIRMED_STANDBY;
*(gBattleStruct->moveTarget + gActiveBattler) = gBattleBufferB[gActiveBattler][3];
return;
}
else if (gDisableStructs[gActiveBattler].encoredMove != MOVE_NONE)
{
gChosenMoveByBattler[gActiveBattler] = gDisableStructs[gActiveBattler].encoredMove;
*(gBattleStruct->chosenMovePositions + gActiveBattler) = gDisableStructs[gActiveBattler].encoredMovePos;
gBattleCommunication[gActiveBattler] = STATE_WAIT_ACTION_CONFIRMED_STANDBY;
return;
}
else
{
struct ChooseMoveStruct moveInfo;
moveInfo.species = gBattleMons[gActiveBattler].species;
moveInfo.monType1 = gBattleMons[gActiveBattler].type1;
moveInfo.monType2 = gBattleMons[gActiveBattler].type2;
for (i = 0; i < MAX_MON_MOVES; ++i)
{
moveInfo.moves[i] = gBattleMons[gActiveBattler].moves[i];
moveInfo.currentPp[i] = gBattleMons[gActiveBattler].pp[i];
moveInfo.maxPp[i] = CalculatePPWithBonus(gBattleMons[gActiveBattler].moves[i],
gBattleMons[gActiveBattler].ppBonuses,
i);
}
BtlController_EmitChooseMove(0, (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) != 0, FALSE, &moveInfo);
MarkBattlerForControllerExec(gActiveBattler);
}
break;
case B_ACTION_USE_ITEM:
if (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_BATTLE_TOWER | BATTLE_TYPE_EREADER_TRAINER))
{
gSelectionBattleScripts[gActiveBattler] = BattleScript_ActionSelectionItemsCantBeUsed;
gBattleCommunication[gActiveBattler] = STATE_SELECTION_SCRIPT;
*(gBattleStruct->selectionScriptFinished + gActiveBattler) = FALSE;
*(gBattleStruct->stateIdAfterSelScript + gActiveBattler) = STATE_BEFORE_ACTION_CHOSEN;
return;
}
else
{
BtlController_EmitChooseItem(0, gBattleStruct->field_60[gActiveBattler]);
MarkBattlerForControllerExec(gActiveBattler);
}
break;
case B_ACTION_SWITCH:
*(gBattleStruct->field_58 + gActiveBattler) = gBattlerPartyIndexes[gActiveBattler];
if (gBattleMons[gActiveBattler].status2 & (STATUS2_WRAPPED | STATUS2_ESCAPE_PREVENTION) || gStatuses3[gActiveBattler] & STATUS3_ROOTED)
{
BtlController_EmitChoosePokemon(0, PARTY_ACTION_CANT_SWITCH, 6, ABILITY_NONE, gBattleStruct->field_60[gActiveBattler]);
}
else if ((i = ABILITY_ON_OPPOSING_FIELD(gActiveBattler, ABILITY_SHADOW_TAG))
|| ((i = ABILITY_ON_OPPOSING_FIELD(gActiveBattler, ABILITY_ARENA_TRAP))
&& !IS_BATTLER_OF_TYPE(gActiveBattler, TYPE_FLYING)
&& gBattleMons[gActiveBattler].ability != ABILITY_LEVITATE)
|| ((i = AbilityBattleEffects(ABILITYEFFECT_CHECK_FIELD_EXCEPT_BATTLER, gActiveBattler, ABILITY_MAGNET_PULL, 0, 0))
&& IS_BATTLER_OF_TYPE(gActiveBattler, TYPE_STEEL)))
{
BtlController_EmitChoosePokemon(0, ((i - 1) << 4) | PARTY_ACTION_ABILITY_PREVENTS, 6, gLastUsedAbility, gBattleStruct->field_60[gActiveBattler]);
}
else
{
if (gActiveBattler == 2 && gChosenActionByBattler[0] == B_ACTION_SWITCH)
BtlController_EmitChoosePokemon(0, PARTY_ACTION_CHOOSE_MON, *(gBattleStruct->monToSwitchIntoId + 0), ABILITY_NONE, gBattleStruct->field_60[gActiveBattler]);
else if (gActiveBattler == 3 && gChosenActionByBattler[1] == B_ACTION_SWITCH)
BtlController_EmitChoosePokemon(0, PARTY_ACTION_CHOOSE_MON, *(gBattleStruct->monToSwitchIntoId + 1), ABILITY_NONE, gBattleStruct->field_60[gActiveBattler]);
else
BtlController_EmitChoosePokemon(0, PARTY_ACTION_CHOOSE_MON, 6, ABILITY_NONE, gBattleStruct->field_60[gActiveBattler]);
}
MarkBattlerForControllerExec(gActiveBattler);
break;
case B_ACTION_SAFARI_BALL:
if (IsPlayerPartyAndPokemonStorageFull())
{
gSelectionBattleScripts[gActiveBattler] = BattleScript_PrintFullBox;
gBattleCommunication[gActiveBattler] = STATE_SELECTION_SCRIPT;
*(gBattleStruct->selectionScriptFinished + gActiveBattler) = FALSE;
*(gBattleStruct->stateIdAfterSelScript + gActiveBattler) = STATE_BEFORE_ACTION_CHOSEN;
return;
}
break;
case B_ACTION_CANCEL_PARTNER:
gBattleCommunication[gActiveBattler] = STATE_WAIT_SET_BEFORE_ACTION;
gBattleCommunication[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gActiveBattler)))] = STATE_BEFORE_ACTION_CHOSEN;
BtlController_EmitEndBounceEffect(0);
MarkBattlerForControllerExec(gActiveBattler);
return;
}
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER
&& !(gBattleTypeFlags & BATTLE_TYPE_LINK)
&& gBattleBufferB[gActiveBattler][1] == B_ACTION_RUN)
{
BattleScriptExecute(BattleScript_PrintCantRunFromTrainer);
gBattleCommunication[gActiveBattler] = STATE_BEFORE_ACTION_CHOSEN;
}
else if (IsRunningFromBattleImpossible() != BATTLE_RUN_SUCCESS
&& gBattleBufferB[gActiveBattler][1] == B_ACTION_RUN)
{
gSelectionBattleScripts[gActiveBattler] = BattleScript_PrintCantEscapeFromBattle;
gBattleCommunication[gActiveBattler] = STATE_SELECTION_SCRIPT;
*(gBattleStruct->selectionScriptFinished + gActiveBattler) = FALSE;
*(gBattleStruct->stateIdAfterSelScript + gActiveBattler) = STATE_BEFORE_ACTION_CHOSEN;
return;
}
else
{
++gBattleCommunication[gActiveBattler];
}
}
break;
case STATE_WAIT_ACTION_CASE_CHOSEN:
if (!(gBattleControllerExecFlags & ((gBitTable[gActiveBattler]) | (0xF0000000) | (gBitTable[gActiveBattler] << 4) | (gBitTable[gActiveBattler] << 8) | (gBitTable[gActiveBattler] << 0xC))))
{
switch (gChosenActionByBattler[gActiveBattler])
{
case B_ACTION_USE_MOVE:
switch (gBattleBufferB[gActiveBattler][1])
{
case 3 ... 9:
gChosenActionByBattler[gActiveBattler] = gBattleBufferB[gActiveBattler][1];
return;
default:
if ((gBattleBufferB[gActiveBattler][2] | (gBattleBufferB[gActiveBattler][3] << 8)) == 0xFFFF)
{
gBattleCommunication[gActiveBattler] = STATE_BEFORE_ACTION_CHOSEN;
}
else if (TrySetCantSelectMoveBattleScript())
{
gBattleCommunication[gActiveBattler] = STATE_SELECTION_SCRIPT;
*(gBattleStruct->selectionScriptFinished + gActiveBattler) = FALSE;
gBattleBufferB[gActiveBattler][1] = 0;
*(gBattleStruct->stateIdAfterSelScript + gActiveBattler) = STATE_WAIT_ACTION_CHOSEN;
return;
}
else
{
*(gBattleStruct->chosenMovePositions + gActiveBattler) = gBattleBufferB[gActiveBattler][2];
gChosenMoveByBattler[gActiveBattler] = gBattleMons[gActiveBattler].moves[*(gBattleStruct->chosenMovePositions + gActiveBattler)];
*(gBattleStruct->moveTarget + gActiveBattler) = gBattleBufferB[gActiveBattler][3];
++gBattleCommunication[gActiveBattler];
}
break;
}
break;
case B_ACTION_USE_ITEM:
if ((gBattleBufferB[gActiveBattler][1] | (gBattleBufferB[gActiveBattler][2] << 8)) == 0)
{
gBattleCommunication[gActiveBattler] = STATE_BEFORE_ACTION_CHOSEN;
}
else
{
gLastUsedItem = (gBattleBufferB[gActiveBattler][1] | (gBattleBufferB[gActiveBattler][2] << 8));
++gBattleCommunication[gActiveBattler];
}
break;
case B_ACTION_SWITCH:
if (gBattleBufferB[gActiveBattler][1] == PARTY_SIZE)
{
gBattleCommunication[gActiveBattler] = STATE_BEFORE_ACTION_CHOSEN;
}
else
{
*(gBattleStruct->monToSwitchIntoId + gActiveBattler) = gBattleBufferB[gActiveBattler][1];
if (gBattleTypeFlags & BATTLE_TYPE_MULTI)
{
*(gActiveBattler * 3 + (u8 *)(gBattleStruct->field_60) + 0) &= 0xF;
*(gActiveBattler * 3 + (u8 *)(gBattleStruct->field_60) + 0) |= (gBattleBufferB[gActiveBattler][2] & 0xF0);
*(gActiveBattler * 3 + (u8 *)(gBattleStruct->field_60) + 1) = gBattleBufferB[gActiveBattler][3];
*((gActiveBattler ^ BIT_FLANK) * 3 + (u8 *)(gBattleStruct->field_60) + 0) &= (0xF0);
*((gActiveBattler ^ BIT_FLANK) * 3 + (u8 *)(gBattleStruct->field_60) + 0) |= (gBattleBufferB[gActiveBattler][2] & 0xF0) >> 4;
*((gActiveBattler ^ BIT_FLANK) * 3 + (u8 *)(gBattleStruct->field_60) + 2) = gBattleBufferB[gActiveBattler][3];
}
++gBattleCommunication[gActiveBattler];
}
break;
case B_ACTION_RUN:
gHitMarker |= HITMARKER_RUN;
++gBattleCommunication[gActiveBattler];
break;
case B_ACTION_SAFARI_WATCH_CAREFULLY:
++gBattleCommunication[gActiveBattler];
break;
case B_ACTION_SAFARI_BALL:
++gBattleCommunication[gActiveBattler];
break;
case B_ACTION_SAFARI_POKEBLOCK:
case B_ACTION_SAFARI_GO_NEAR:
++gBattleCommunication[gActiveBattler];
break;
case B_ACTION_SAFARI_RUN:
gHitMarker |= HITMARKER_RUN;
++gBattleCommunication[gActiveBattler];
break;
case B_ACTION_OLDMAN_THROW:
++gBattleCommunication[gActiveBattler];
break;
}
}
break;
case STATE_WAIT_ACTION_CONFIRMED_STANDBY:
if (!(gBattleControllerExecFlags & ((gBitTable[gActiveBattler]) | (0xF0000000) | (gBitTable[gActiveBattler] << 4) | (gBitTable[gActiveBattler] << 8) | (gBitTable[gActiveBattler] << 0xC))))
{
if (((gBattleTypeFlags & (BATTLE_TYPE_MULTI | BATTLE_TYPE_DOUBLE)) != BATTLE_TYPE_DOUBLE)
|| (position & BIT_FLANK) != B_FLANK_LEFT
|| (*(&gBattleStruct->field_91) & gBitTable[GetBattlerAtPosition(position ^ BIT_FLANK)]))
BtlController_EmitLinkStandbyMsg(0, 0);
else
BtlController_EmitLinkStandbyMsg(0, 1);
MarkBattlerForControllerExec(gActiveBattler);
++gBattleCommunication[gActiveBattler];
}
break;
case STATE_WAIT_ACTION_CONFIRMED:
if (!(gBattleControllerExecFlags & ((gBitTable[gActiveBattler]) | (0xF0000000) | (gBitTable[gActiveBattler] << 4) | (gBitTable[gActiveBattler] << 8) | (gBitTable[gActiveBattler] << 0xC))))
++gBattleCommunication[ACTIONS_CONFIRMED_COUNT];
break;
case STATE_SELECTION_SCRIPT:
if (*(gBattleStruct->selectionScriptFinished + gActiveBattler))
{
gBattleCommunication[gActiveBattler] = *(gBattleStruct->stateIdAfterSelScript + gActiveBattler);
}
else
{
gBattlerAttacker = gActiveBattler;
gBattlescriptCurrInstr = gSelectionBattleScripts[gActiveBattler];
if (!(gBattleControllerExecFlags & ((gBitTable[gActiveBattler]) | (0xF0000000) | (gBitTable[gActiveBattler] << 4) | (gBitTable[gActiveBattler] << 8) | (gBitTable[gActiveBattler] << 0xC))))
gBattleScriptingCommandsTable[gBattlescriptCurrInstr[0]]();
gSelectionBattleScripts[gActiveBattler] = gBattlescriptCurrInstr;
}
break;
case STATE_WAIT_SET_BEFORE_ACTION:
if (!(gBattleControllerExecFlags & ((gBitTable[gActiveBattler]) | (0xF0000000) | (gBitTable[gActiveBattler] << 4) | (gBitTable[gActiveBattler] << 8) | (gBitTable[gActiveBattler] << 0xC))))
gBattleCommunication[gActiveBattler] = STATE_BEFORE_ACTION_CHOSEN;
break;
}
}
// Check if everyone chose actions.
if (gBattleCommunication[ACTIONS_CONFIRMED_COUNT] == gBattlersCount)
gBattleMainFunc = SetActionsAndBattlersTurnOrder;
}
void SwapTurnOrder(u8 id1, u8 id2)
{
u32 temp;
SWAP(gActionsByTurnOrder[id1], gActionsByTurnOrder[id2], temp);
SWAP(gBattlerByTurnOrder[id1], gBattlerByTurnOrder[id2], temp);
}
u8 GetWhoStrikesFirst(u8 battler1, u8 battler2, bool8 ignoreChosenMoves)
{
u8 strikesFirst = 0;
u8 speedMultiplierBattler1 = 0, speedMultiplierBattler2 = 0;
u32 speedBattler1 = 0, speedBattler2 = 0;
u8 holdEffect = 0;
u8 holdEffectParam = 0;
u16 moveBattler1 = 0, moveBattler2 = 0;
if (WEATHER_HAS_EFFECT)
{
if ((gBattleMons[battler1].ability == ABILITY_SWIFT_SWIM && gBattleWeather & WEATHER_RAIN_ANY)
|| (gBattleMons[battler1].ability == ABILITY_CHLOROPHYLL && gBattleWeather & WEATHER_SUN_ANY))
speedMultiplierBattler1 = 2;
else
speedMultiplierBattler1 = 1;
if ((gBattleMons[battler2].ability == ABILITY_SWIFT_SWIM && gBattleWeather & WEATHER_RAIN_ANY)
|| (gBattleMons[battler2].ability == ABILITY_CHLOROPHYLL && gBattleWeather & WEATHER_SUN_ANY))
speedMultiplierBattler2 = 2;
else
speedMultiplierBattler2 = 1;
}
else
{
speedMultiplierBattler1 = 1;
speedMultiplierBattler2 = 1;
}
speedBattler1 = (gBattleMons[battler1].speed * speedMultiplierBattler1)
* (gStatStageRatios[gBattleMons[battler1].statStages[STAT_SPEED]][0])
/ (gStatStageRatios[gBattleMons[battler1].statStages[STAT_SPEED]][1]);
if (gBattleMons[battler1].item == ITEM_ENIGMA_BERRY)
{
holdEffect = gEnigmaBerries[battler1].holdEffect;
holdEffectParam = gEnigmaBerries[battler1].holdEffectParam;
}
else
{
holdEffect = ItemId_GetHoldEffect(gBattleMons[battler1].item);
holdEffectParam = ItemId_GetHoldEffectParam(gBattleMons[battler1].item);
}
// badge boost
if (!(gBattleTypeFlags & BATTLE_TYPE_LINK)
&& FlagGet(FLAG_BADGE03_GET)
&& GetBattlerSide(battler1) == B_SIDE_PLAYER)
speedBattler1 = (speedBattler1 * 110) / 100;
if (holdEffect == HOLD_EFFECT_MACHO_BRACE)
speedBattler1 /= 2;
if (gBattleMons[battler1].status1 & STATUS1_PARALYSIS)
speedBattler1 /= 4;
if (holdEffect == HOLD_EFFECT_QUICK_CLAW && gRandomTurnNumber < (0xFFFF * holdEffectParam) / 100)
speedBattler1 = UINT_MAX;
// check second battlerId's speed
speedBattler2 = (gBattleMons[battler2].speed * speedMultiplierBattler2)
* (gStatStageRatios[gBattleMons[battler2].statStages[STAT_SPEED]][0])
/ (gStatStageRatios[gBattleMons[battler2].statStages[STAT_SPEED]][1]);
if (gBattleMons[battler2].item == ITEM_ENIGMA_BERRY)
{
holdEffect = gEnigmaBerries[battler2].holdEffect;
holdEffectParam = gEnigmaBerries[battler2].holdEffectParam;
}
else
{
holdEffect = ItemId_GetHoldEffect(gBattleMons[battler2].item);
holdEffectParam = ItemId_GetHoldEffectParam(gBattleMons[battler2].item);
}
// badge boost
if (!(gBattleTypeFlags & BATTLE_TYPE_LINK)
&& FlagGet(FLAG_BADGE03_GET)
&& GetBattlerSide(battler2) == B_SIDE_PLAYER)
speedBattler2 = (speedBattler2 * 110) / 100;
if (holdEffect == HOLD_EFFECT_MACHO_BRACE)
speedBattler2 /= 2;
if (gBattleMons[battler2].status1 & STATUS1_PARALYSIS)
speedBattler2 /= 4;
if (holdEffect == HOLD_EFFECT_QUICK_CLAW && gRandomTurnNumber < (0xFFFF * holdEffectParam) / 100)
speedBattler2 = UINT_MAX;
if (ignoreChosenMoves)
{
moveBattler1 = MOVE_NONE;
moveBattler2 = MOVE_NONE;
}
else
{
if (gChosenActionByBattler[battler1] == B_ACTION_USE_MOVE)
{
if (gProtectStructs[battler1].noValidMoves)
moveBattler1 = MOVE_STRUGGLE;
else
moveBattler1 = gBattleMons[battler1].moves[*(gBattleStruct->chosenMovePositions + battler1)];
}
else
moveBattler1 = MOVE_NONE;
if (gChosenActionByBattler[battler2] == B_ACTION_USE_MOVE)
{
if (gProtectStructs[battler2].noValidMoves)
moveBattler2 = MOVE_STRUGGLE;
else
moveBattler2 = gBattleMons[battler2].moves[*(gBattleStruct->chosenMovePositions + battler2)];
}
else
moveBattler2 = MOVE_NONE;
}
// both move priorities are different than 0
if (gBattleMoves[moveBattler1].priority != 0 || gBattleMoves[moveBattler2].priority != 0)
{
// both priorities are the same
if (gBattleMoves[moveBattler1].priority == gBattleMoves[moveBattler2].priority)
{
if (speedBattler1 == speedBattler2 && Random() & 1)
strikesFirst = 2; // same speeds, same priorities
else if (speedBattler1 < speedBattler2)
strikesFirst = 1; // battler2 has more speed
// else battler1 has more speed
}
else if (gBattleMoves[moveBattler1].priority < gBattleMoves[moveBattler2].priority)
strikesFirst = 1; // battler2's move has greater priority
// else battler1's move has greater priority
}
// both priorities are equal to 0
else
{
if (speedBattler1 == speedBattler2 && Random() & 1)
strikesFirst = 2; // same speeds, same priorities
else if (speedBattler1 < speedBattler2)
strikesFirst = 1; // battler2 has more speed
// else battler1 has more speed
}
return strikesFirst;
}
static void SetActionsAndBattlersTurnOrder(void)
{
s32 turnOrderId = 0;
s32 i, j;
if (gBattleTypeFlags & BATTLE_TYPE_SAFARI)
{
for (gActiveBattler = 0; gActiveBattler < gBattlersCount; ++gActiveBattler)
{
gActionsByTurnOrder[turnOrderId] = gChosenActionByBattler[gActiveBattler];
gBattlerByTurnOrder[turnOrderId] = gActiveBattler;
++turnOrderId;
}
}
else
{
if (gBattleTypeFlags & BATTLE_TYPE_LINK)
{
for (gActiveBattler = 0; gActiveBattler < gBattlersCount; ++gActiveBattler)
{
if (gChosenActionByBattler[gActiveBattler] == B_ACTION_RUN)
{
turnOrderId = 5;
break;
}
}
}
else if (gChosenActionByBattler[0] == B_ACTION_RUN)
{
gActiveBattler = 0;
turnOrderId = 5;
}
if (turnOrderId == 5) // One of battlers wants to run.
{
gActionsByTurnOrder[0] = gChosenActionByBattler[gActiveBattler];
gBattlerByTurnOrder[0] = gActiveBattler;
turnOrderId = 1;
for (i = 0; i < gBattlersCount; ++i)
{
if (i != gActiveBattler)
{
gActionsByTurnOrder[turnOrderId] = gChosenActionByBattler[i];
gBattlerByTurnOrder[turnOrderId] = i;
++turnOrderId;
}
}
gBattleMainFunc = CheckFocusPunch_ClearVarsBeforeTurnStarts;
gBattleStruct->focusPunchBattlerId = 0;
return;
}
else
{
for (gActiveBattler = 0; gActiveBattler < gBattlersCount; ++gActiveBattler)
{
if (gChosenActionByBattler[gActiveBattler] == B_ACTION_USE_ITEM || gChosenActionByBattler[gActiveBattler] == B_ACTION_SWITCH)
{
gActionsByTurnOrder[turnOrderId] = gChosenActionByBattler[gActiveBattler];
gBattlerByTurnOrder[turnOrderId] = gActiveBattler;
++turnOrderId;
}
}
for (gActiveBattler = 0; gActiveBattler < gBattlersCount; ++gActiveBattler)
{
if (gChosenActionByBattler[gActiveBattler] != B_ACTION_USE_ITEM && gChosenActionByBattler[gActiveBattler] != B_ACTION_SWITCH)
{
gActionsByTurnOrder[turnOrderId] = gChosenActionByBattler[gActiveBattler];
gBattlerByTurnOrder[turnOrderId] = gActiveBattler;
++turnOrderId;
}
}
for (i = 0; i < gBattlersCount - 1; ++i)
{
for (j = i + 1; j < gBattlersCount; ++j)
{
u8 battler1 = gBattlerByTurnOrder[i];
u8 battler2 = gBattlerByTurnOrder[j];
if (gActionsByTurnOrder[i] != B_ACTION_USE_ITEM
&& gActionsByTurnOrder[j] != B_ACTION_USE_ITEM
&& gActionsByTurnOrder[i] != B_ACTION_SWITCH
&& gActionsByTurnOrder[j] != B_ACTION_SWITCH)
if (GetWhoStrikesFirst(battler1, battler2, FALSE))
SwapTurnOrder(i, j);
}
}
}
}
gBattleMainFunc = CheckFocusPunch_ClearVarsBeforeTurnStarts;
gBattleStruct->focusPunchBattlerId = 0;
}
static void TurnValuesCleanUp(bool8 var0)
{
s32 i;
u8 *dataPtr;
for (gActiveBattler = 0; gActiveBattler < gBattlersCount; ++gActiveBattler)
{
if (var0)
{
gProtectStructs[gActiveBattler].protected = FALSE;
gProtectStructs[gActiveBattler].endured = FALSE;
}
else
{
dataPtr = (u8 *)(&gProtectStructs[gActiveBattler]);
for (i = 0; i < sizeof(struct ProtectStruct); ++i)
dataPtr[i] = 0;
if (gDisableStructs[gActiveBattler].isFirstTurn)
--gDisableStructs[gActiveBattler].isFirstTurn;
if (gDisableStructs[gActiveBattler].rechargeTimer)
{
--gDisableStructs[gActiveBattler].rechargeTimer;
if (gDisableStructs[gActiveBattler].rechargeTimer == 0)
gBattleMons[gActiveBattler].status2 &= ~(STATUS2_RECHARGE);
}
}
if (gDisableStructs[gActiveBattler].substituteHP == 0)
gBattleMons[gActiveBattler].status2 &= ~(STATUS2_SUBSTITUTE);
}
gSideTimers[0].followmeTimer = 0;
gSideTimers[1].followmeTimer = 0;
}
static void SpecialStatusesClear(void)
{
for (gActiveBattler = 0; gActiveBattler < gBattlersCount; ++gActiveBattler)
{
s32 i;
u8 *dataPtr = (u8 *)(&gSpecialStatuses[gActiveBattler]);
for (i = 0; i < sizeof(struct SpecialStatus); ++i)
dataPtr[i] = 0;
}
}
static void CheckFocusPunch_ClearVarsBeforeTurnStarts(void)
{
if (!(gHitMarker & HITMARKER_RUN))
{
while (gBattleStruct->focusPunchBattlerId < gBattlersCount)
{
gActiveBattler = gBattlerAttacker = gBattleStruct->focusPunchBattlerId;
++gBattleStruct->focusPunchBattlerId;
if (gChosenMoveByBattler[gActiveBattler] == MOVE_FOCUS_PUNCH
&& !(gBattleMons[gActiveBattler].status1 & STATUS1_SLEEP)
&& !(gDisableStructs[gBattlerAttacker].truantCounter)
&& !(gProtectStructs[gActiveBattler].noValidMoves))
{
BattleScriptExecute(BattleScript_FocusPunchSetUp);
return;
}
}
}
TryClearRageStatuses();
gCurrentTurnActionNumber = 0;
{
// something stupid needed to match
u8 zero;
gCurrentActionFuncId = gActionsByTurnOrder[(zero = 0)];
}
gDynamicBasePower = 0;
gBattleStruct->dynamicMoveType = 0;
gBattleMainFunc = RunTurnActionsFunctions;
gBattleCommunication[3] = 0;
gBattleCommunication[4] = 0;
gBattleScripting.multihitMoveEffect = 0;
gBattleResources->battleScriptsStack->size = 0;
}
static void RunTurnActionsFunctions(void)
{
if (gBattleOutcome != 0)
gCurrentActionFuncId = B_ACTION_FINISHED;
*(&gBattleStruct->savedTurnActionNumber) = gCurrentTurnActionNumber;
sTurnActionsFuncsTable[gCurrentActionFuncId]();
if (gCurrentTurnActionNumber >= gBattlersCount) // everyone did their actions, turn finished
{
gHitMarker &= ~(HITMARKER_x100000);
gBattleMainFunc = sEndTurnFuncsTable[gBattleOutcome & 0x7F];
}
else
{
if (gBattleStruct->savedTurnActionNumber != gCurrentTurnActionNumber) // action turn has been done, clear hitmarker bits for another battlerId
{
gHitMarker &= ~(HITMARKER_NO_ATTACKSTRING);
gHitMarker &= ~(HITMARKER_UNABLE_TO_USE_MOVE);
}
}
}
static void HandleEndTurn_BattleWon(void)
{
gCurrentActionFuncId = 0;
if (gBattleTypeFlags & BATTLE_TYPE_LINK)
{
gBattleTextBuff1[0] = gBattleOutcome;
gBattlerAttacker = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
gBattlescriptCurrInstr = BattleScript_LinkBattleWonOrLost;
gBattleOutcome &= ~(B_OUTCOME_LINK_BATTLE_RAN);
}
else if (gBattleTypeFlags & (BATTLE_TYPE_TRAINER_TOWER | BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_BATTLE_TOWER))
{
BattleStopLowHpSound();
PlayBGM(MUS_WIN_TRE);
gBattlescriptCurrInstr = BattleScript_BattleTowerTrainerBattleWon;
}
else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER && !(gBattleTypeFlags & BATTLE_TYPE_LINK))
{
BattleStopLowHpSound();
gBattlescriptCurrInstr = BattleScript_LocalTrainerBattleWon;
switch (gTrainers[gTrainerBattleOpponent_A].trainerClass)
{
case CLASS_LEADER_2:
case CLASS_CHAMPION_2:
PlayBGM(MUS_WIN_GYM);
break;
case CLASS_BOSS:
case CLASS_TEAM_ROCKET:
case CLASS_COOLTRAINER_2:
case CLASS_ELITE_FOUR_2:
case CLASS_GENTLEMAN_2:
default:
PlayBGM(MUS_WIN_TRE);
break;
}
}
else
{
gBattlescriptCurrInstr = BattleScript_PayDayMoneyAndPickUpItems;
}
gBattleMainFunc = HandleEndTurn_FinishBattle;
}
static void HandleEndTurn_BattleLost(void)
{
gCurrentActionFuncId = 0;
if (gBattleTypeFlags & BATTLE_TYPE_LINK)
{
gBattleTextBuff1[0] = gBattleOutcome;
gBattlerAttacker = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
gBattlescriptCurrInstr = BattleScript_LinkBattleWonOrLost;
gBattleOutcome &= ~(B_OUTCOME_LINK_BATTLE_RAN);
}
else
{
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER && ScrSpecial_GetTrainerBattleMode() == 9)
{
if (sub_80803D8() & 1)
gBattleCommunication[MULTISTRING_CHOOSER] = 1;
else
gBattleCommunication[MULTISTRING_CHOOSER] = 2;
gBattlerAttacker = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
}
else
{
gBattleCommunication[MULTISTRING_CHOOSER] = 0;
}
gBattlescriptCurrInstr = BattleScript_LocalBattleLost;
}
gBattleMainFunc = HandleEndTurn_FinishBattle;
}
static void HandleEndTurn_RanFromBattle(void)
{
gCurrentActionFuncId = 0;
switch (gProtectStructs[gBattlerAttacker].fleeFlag)
{
default:
gBattlescriptCurrInstr = BattleScript_GotAwaySafely;
break;
case 1:
gBattlescriptCurrInstr = BattleScript_SmokeBallEscape;
break;
case 2:
gBattlescriptCurrInstr = BattleScript_RanAwayUsingMonAbility;
break;
}
gBattleMainFunc = HandleEndTurn_FinishBattle;
}
static void HandleEndTurn_MonFled(void)
{
gCurrentActionFuncId = 0;
PREPARE_MON_NICK_BUFFER(gBattleTextBuff1, gBattlerAttacker, gBattlerPartyIndexes[gBattlerAttacker]);
gBattlescriptCurrInstr = BattleScript_WildMonFled;
gBattleMainFunc = HandleEndTurn_FinishBattle;
}
static void HandleEndTurn_FinishBattle(void)
{
if (gCurrentActionFuncId == B_ACTION_TRY_FINISH || gCurrentActionFuncId == B_ACTION_FINISHED)
{
if (!(gBattleTypeFlags & (BATTLE_TYPE_TRAINER_TOWER | BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_OLD_MAN_TUTORIAL | BATTLE_TYPE_BATTLE_TOWER | BATTLE_TYPE_SAFARI | BATTLE_TYPE_FIRST_BATTLE | BATTLE_TYPE_LINK)))
{
for (gActiveBattler = 0; gActiveBattler < gBattlersCount; ++gActiveBattler)
{
if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER)
{
if (gBattleResults.playerMon1Species == SPECIES_NONE)
{
gBattleResults.playerMon1Species = gBattleMons[gActiveBattler].species;
StringCopy(gBattleResults.playerMon1Name, gBattleMons[gActiveBattler].nickname);
}
else
{
gBattleResults.playerMon2Species = gBattleMons[gActiveBattler].species;
StringCopy(gBattleResults.playerMon2Name, gBattleMons[gActiveBattler].nickname);
}
}
}
}
TrySetQuestLogBattleEvent();
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER)
sub_810CB90();
BeginFastPaletteFade(3);
FadeOutMapMusic(5);
gBattleMainFunc = FreeResetData_ReturnToOvOrDoEvolutions;
gCB2_AfterEvolution = BattleMainCB2;
}
else if (!gBattleControllerExecFlags)
{
gBattleScriptingCommandsTable[gBattlescriptCurrInstr[0]]();
}
}
static void FreeResetData_ReturnToOvOrDoEvolutions(void)
{
if (!gPaletteFade.active)
{
ResetSpriteData();
if (gLeveledUpInBattle == 0 || gBattleOutcome != B_OUTCOME_WON)
gBattleMainFunc = ReturnFromBattleToOverworld;
else
gBattleMainFunc = TryEvolvePokemon;
FreeAllWindowBuffers();
if (!(gBattleTypeFlags & BATTLE_TYPE_LINK))
{
FreeMonSpritesGfx();
FreeBattleSpritesData();
FreeBattleResources();
}
}
}
static void TryEvolvePokemon(void)
{
s32 i;
while (gLeveledUpInBattle != 0)
{
for (i = 0; i < PARTY_SIZE; ++i)
{
if (gLeveledUpInBattle & gBitTable[i])
{
u16 species;
u8 levelUpBits = gLeveledUpInBattle;
levelUpBits &= ~(gBitTable[i]);
gLeveledUpInBattle = levelUpBits;
species = GetEvolutionTargetSpecies(&gPlayerParty[i], 0, levelUpBits);
if (species != SPECIES_NONE)
{
gBattleMainFunc = WaitForEvoSceneToFinish;
EvolutionScene(&gPlayerParty[i], species, 0x81, i);
return;
}
}
}
}
gBattleMainFunc = ReturnFromBattleToOverworld;
}
static void WaitForEvoSceneToFinish(void)
{
if (gMain.callback2 == BattleMainCB2)
gBattleMainFunc = TryEvolvePokemon;
}
static void ReturnFromBattleToOverworld(void)
{
if (!(gBattleTypeFlags & BATTLE_TYPE_LINK))
{
RandomlyGivePartyPokerus(gPlayerParty);
PartySpreadPokerus(gPlayerParty);
}
if (!(gBattleTypeFlags & BATTLE_TYPE_LINK) || gReceivedRemoteLinkPlayers == 0)
{
gSpecialVar_Result = gBattleOutcome;
gMain.inBattle = FALSE;
gMain.callback1 = gPreBattleCallback1;
if (gBattleTypeFlags & BATTLE_TYPE_ROAMER)
{
UpdateRoamerHPStatus(&gEnemyParty[0]);
if ((gBattleOutcome & B_OUTCOME_WON) || gBattleOutcome == B_OUTCOME_CAUGHT)
SetRoamerInactive();
}
m4aSongNumStop(SE_HINSI);
SetMainCallback2(gMain.savedCallback);
}
}
void RunBattleScriptCommands_PopCallbacksStack(void)
{
if (gCurrentActionFuncId == B_ACTION_TRY_FINISH || gCurrentActionFuncId == B_ACTION_FINISHED)
{
if (gBattleResources->battleCallbackStack->size != 0)
--gBattleResources->battleCallbackStack->size;
gBattleMainFunc = gBattleResources->battleCallbackStack->function[gBattleResources->battleCallbackStack->size];
}
else
{
if (!gBattleControllerExecFlags)
gBattleScriptingCommandsTable[gBattlescriptCurrInstr[0]]();
}
}
void RunBattleScriptCommands(void)
{
if (!gBattleControllerExecFlags)
gBattleScriptingCommandsTable[gBattlescriptCurrInstr[0]]();
}
static void HandleAction_UseMove(void)
{
u8 side;
u8 var = 4;
gBattlerAttacker = gBattlerByTurnOrder[gCurrentTurnActionNumber];
if (*(&gBattleStruct->field_91) & gBitTable[gBattlerAttacker])
{
gCurrentActionFuncId = B_ACTION_FINISHED;
return;
}
gCritMultiplier = 1;
gBattleScripting.dmgMultiplier = 1;
gBattleStruct->atkCancellerTracker = 0;
gMoveResultFlags = 0;
gMultiHitCounter = 0;
gBattleCommunication[6] = 0;
gCurrMovePos = gChosenMovePos = *(gBattleStruct->chosenMovePositions + gBattlerAttacker);
// choose move
if (gProtectStructs[gBattlerAttacker].noValidMoves)
{
gProtectStructs[gBattlerAttacker].noValidMoves = 0;
gCurrentMove = gChosenMove = MOVE_STRUGGLE;
gHitMarker |= HITMARKER_NO_PPDEDUCT;
*(gBattleStruct->moveTarget + gBattlerAttacker) = GetMoveTarget(MOVE_STRUGGLE, 0);
}
else if (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS || gBattleMons[gBattlerAttacker].status2 & STATUS2_RECHARGE)
{
gCurrentMove = gChosenMove = gLockedMoves[gBattlerAttacker];
}
// encore forces you to use the same move
else if (gDisableStructs[gBattlerAttacker].encoredMove != MOVE_NONE
&& gDisableStructs[gBattlerAttacker].encoredMove == gBattleMons[gBattlerAttacker].moves[gDisableStructs[gBattlerAttacker].encoredMovePos])
{
gCurrentMove = gChosenMove = gDisableStructs[gBattlerAttacker].encoredMove;
gCurrMovePos = gChosenMovePos = gDisableStructs[gBattlerAttacker].encoredMovePos;
*(gBattleStruct->moveTarget + gBattlerAttacker) = GetMoveTarget(gCurrentMove, 0);
}
// check if the encored move wasn't overwritten
else if (gDisableStructs[gBattlerAttacker].encoredMove != MOVE_NONE
&& gDisableStructs[gBattlerAttacker].encoredMove != gBattleMons[gBattlerAttacker].moves[gDisableStructs[gBattlerAttacker].encoredMovePos])
{
gCurrMovePos = gChosenMovePos = gDisableStructs[gBattlerAttacker].encoredMovePos;
gCurrentMove = gChosenMove = gBattleMons[gBattlerAttacker].moves[gCurrMovePos];
gDisableStructs[gBattlerAttacker].encoredMove = MOVE_NONE;
gDisableStructs[gBattlerAttacker].encoredMovePos = 0;
gDisableStructs[gBattlerAttacker].encoreTimer = 0;
*(gBattleStruct->moveTarget + gBattlerAttacker) = GetMoveTarget(gCurrentMove, 0);
}
else if (gBattleMons[gBattlerAttacker].moves[gCurrMovePos] != gChosenMoveByBattler[gBattlerAttacker])
{
gCurrentMove = gChosenMove = gBattleMons[gBattlerAttacker].moves[gCurrMovePos];
*(gBattleStruct->moveTarget + gBattlerAttacker) = GetMoveTarget(gCurrentMove, 0);
}
else
{
gCurrentMove = gChosenMove = gBattleMons[gBattlerAttacker].moves[gCurrMovePos];
}
if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER)
gBattleResults.lastUsedMovePlayer = gCurrentMove;
else
gBattleResults.lastUsedMoveOpponent = gCurrentMove;
// choose target
side = GetBattlerSide(gBattlerAttacker) ^ BIT_SIDE;
if (gSideTimers[side].followmeTimer != 0
&& gBattleMoves[gCurrentMove].target == MOVE_TARGET_SELECTED
&& GetBattlerSide(gBattlerAttacker) != GetBattlerSide(gSideTimers[side].followmeTarget)
&& gBattleMons[gSideTimers[side].followmeTarget].hp != 0)
{
gBattlerTarget = gSideTimers[side].followmeTarget;
}
else if ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
&& gSideTimers[side].followmeTimer == 0
&& (gBattleMoves[gCurrentMove].power != 0
|| gBattleMoves[gCurrentMove].target != MOVE_TARGET_USER)
&& gBattleMons[*(gBattleStruct->moveTarget + gBattlerAttacker)].ability != ABILITY_LIGHTNING_ROD
&& gBattleMoves[gCurrentMove].type == TYPE_ELECTRIC)
{
side = GetBattlerSide(gBattlerAttacker);
for (gActiveBattler = 0; gActiveBattler < gBattlersCount; ++gActiveBattler)
if (side != GetBattlerSide(gActiveBattler)
&& *(gBattleStruct->moveTarget + gBattlerAttacker) != gActiveBattler
&& gBattleMons[gActiveBattler].ability == ABILITY_LIGHTNING_ROD
&& GetBattlerTurnOrderNum(gActiveBattler) < var)
var = GetBattlerTurnOrderNum(gActiveBattler);
if (var == 4)
{
if (gBattleMoves[gChosenMove].target & MOVE_TARGET_RANDOM)
{
if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER)
{
if (Random() & 1)
gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
else
gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT);
}
else
{
if (Random() & 1)
gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
else
gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT);
}
}
else
{
gBattlerTarget = *(gBattleStruct->moveTarget + gBattlerAttacker);
}
if (gAbsentBattlerFlags & gBitTable[gBattlerTarget])
{
if (GetBattlerSide(gBattlerAttacker) != GetBattlerSide(gBattlerTarget))
{
gBattlerTarget = GetBattlerAtPosition(GetBattlerPosition(gBattlerTarget) ^ BIT_FLANK);
}
else
{
gBattlerTarget = GetBattlerAtPosition(GetBattlerPosition(gBattlerAttacker) ^ BIT_SIDE);
if (gAbsentBattlerFlags & gBitTable[gBattlerTarget])
gBattlerTarget = GetBattlerAtPosition(GetBattlerPosition(gBattlerTarget) ^ BIT_FLANK);
}
}
}
else
{
gActiveBattler = gBattlerByTurnOrder[var];
RecordAbilityBattle(gActiveBattler, gBattleMons[gActiveBattler].ability);
gSpecialStatuses[gActiveBattler].lightningRodRedirected = 1;
gBattlerTarget = gActiveBattler;
}
}
else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE
&& gBattleMoves[gChosenMove].target & MOVE_TARGET_RANDOM)
{
if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER)
{
if (Random() & 1)
gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
else
gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT);
}
else
{
if (Random() & 1)
gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
else
gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT);
}
if (gAbsentBattlerFlags & gBitTable[gBattlerTarget]
&& GetBattlerSide(gBattlerAttacker) != GetBattlerSide(gBattlerTarget))
gBattlerTarget = GetBattlerAtPosition(GetBattlerPosition(gBattlerTarget) ^ BIT_FLANK);
}
else
{
gBattlerTarget = *(gBattleStruct->moveTarget + gBattlerAttacker);
if (gAbsentBattlerFlags & gBitTable[gBattlerTarget])
{
if (GetBattlerSide(gBattlerAttacker) != GetBattlerSide(gBattlerTarget))
{
gBattlerTarget = GetBattlerAtPosition(GetBattlerPosition(gBattlerTarget) ^ BIT_FLANK);
}
else
{
gBattlerTarget = GetBattlerAtPosition(GetBattlerPosition(gBattlerAttacker) ^ BIT_SIDE);
if (gAbsentBattlerFlags & gBitTable[gBattlerTarget])
gBattlerTarget = GetBattlerAtPosition(GetBattlerPosition(gBattlerTarget) ^ BIT_FLANK);
}
}
}
gBattlescriptCurrInstr = gBattleScriptsForMoveEffects[gBattleMoves[gCurrentMove].effect];
gCurrentActionFuncId = B_ACTION_EXEC_SCRIPT;
}
static void HandleAction_Switch(void)
{
gBattlerAttacker = gBattlerByTurnOrder[gCurrentTurnActionNumber];
gBattle_BG0_X = 0;
gBattle_BG0_Y = 0;
gActionSelectionCursor[gBattlerAttacker] = 0;
gMoveSelectionCursor[gBattlerAttacker] = 0;
PREPARE_MON_NICK_BUFFER(gBattleTextBuff1, gBattlerAttacker, *(gBattleStruct->field_58 + gBattlerAttacker));
gBattleScripting.battler = gBattlerAttacker;
gBattlescriptCurrInstr = BattleScript_ActionSwitch;
gCurrentActionFuncId = B_ACTION_EXEC_SCRIPT;
if (gBattleResults.playerSwitchesCounter < 255)
++gBattleResults.playerSwitchesCounter;
}
static void HandleAction_UseItem(void)
{
gBattlerAttacker = gBattlerTarget = gBattlerByTurnOrder[gCurrentTurnActionNumber];
gBattle_BG0_X = 0;
gBattle_BG0_Y = 0;
ClearFuryCutterDestinyBondGrudge(gBattlerAttacker);
gLastUsedItem = gBattleBufferB[gBattlerAttacker][1] | (gBattleBufferB[gBattlerAttacker][2] << 8);
if (gLastUsedItem <= ITEM_PREMIER_BALL) // is ball
{
gBattlescriptCurrInstr = gBattlescriptsForBallThrow[gLastUsedItem];
}
else if (gLastUsedItem == ITEM_POKE_DOLL || gLastUsedItem == ITEM_FLUFFY_TAIL)
{
gBattlescriptCurrInstr = gBattlescriptsForRunningByItem[0];
}
else if (gLastUsedItem == ITEM_POKE_FLUTE)
{
gBattlescriptCurrInstr = gBattlescriptsForRunningByItem[1];
}
else if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER)
{
gBattlescriptCurrInstr = gBattlescriptsForUsingItem[0];
}
else
{
gBattleScripting.battler = gBattlerAttacker;
switch (*(gBattleStruct->AI_itemType + (gBattlerAttacker >> 1)))
{
case AI_ITEM_FULL_RESTORE:
case AI_ITEM_HEAL_HP:
break;
case AI_ITEM_CURE_CONDITION:
gBattleCommunication[MULTISTRING_CHOOSER] = 0;
if (*(gBattleStruct->AI_itemFlags + gBattlerAttacker / 2) & 1)
{
if (*(gBattleStruct->AI_itemFlags + gBattlerAttacker / 2) & 0x3E)
gBattleCommunication[MULTISTRING_CHOOSER] = 5;
}
else
{
while (!(*(gBattleStruct->AI_itemFlags + gBattlerAttacker / 2) & 1))
{
*(gBattleStruct->AI_itemFlags + gBattlerAttacker / 2) >>= 1;
++gBattleCommunication[MULTISTRING_CHOOSER];
}
}
break;
case AI_ITEM_X_STAT:
gBattleCommunication[MULTISTRING_CHOOSER] = 4;
if (*(gBattleStruct->AI_itemFlags + (gBattlerAttacker >> 1)) & 0x80)
{
gBattleCommunication[MULTISTRING_CHOOSER] = 5;
}
else
{
PREPARE_STAT_BUFFER(gBattleTextBuff1, STAT_ATK);
PREPARE_STRING_BUFFER(gBattleTextBuff2, CHAR_X);
while (!((*(gBattleStruct->AI_itemFlags + (gBattlerAttacker >> 1))) & 1))
{
*(gBattleStruct->AI_itemFlags + gBattlerAttacker / 2) >>= 1;
++gBattleTextBuff1[2];
}
gBattleScripting.animArg1 = gBattleTextBuff1[2] + 14;
gBattleScripting.animArg2 = 0;
}
break;
case AI_ITEM_GUARD_SPECS:
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
gBattleCommunication[MULTISTRING_CHOOSER] = 2;
else
gBattleCommunication[MULTISTRING_CHOOSER] = 0;
break;
}
gBattlescriptCurrInstr = gBattlescriptsForUsingItem[*(gBattleStruct->AI_itemType + gBattlerAttacker / 2)];
}
gCurrentActionFuncId = B_ACTION_EXEC_SCRIPT;
}
bool8 TryRunFromBattle(u8 battler)
{
bool8 effect = FALSE;
u8 holdEffect;
u8 pyramidMultiplier;
u8 speedVar;
if (gBattleMons[battler].item == ITEM_ENIGMA_BERRY)
holdEffect = gEnigmaBerries[battler].holdEffect;
else
holdEffect = ItemId_GetHoldEffect(gBattleMons[battler].item);
gPotentialItemEffectBattler = battler;
if (holdEffect == HOLD_EFFECT_CAN_ALWAYS_RUN)
{
gLastUsedItem = gBattleMons[battler].item;
gProtectStructs[battler].fleeFlag = 1;
++effect;
}
else if (gBattleMons[battler].ability == ABILITY_RUN_AWAY)
{
gLastUsedAbility = ABILITY_RUN_AWAY;
gProtectStructs[battler].fleeFlag = 2;
++effect;
}
else if ((gBattleTypeFlags & (BATTLE_TYPE_LEGENDARY | BATTLE_TYPE_GHOST)) == BATTLE_TYPE_GHOST)
{
if (GetBattlerSide(battler) == B_SIDE_PLAYER)
++effect;
}
else
{
if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE))
{
if (gBattleMons[battler].speed < gBattleMons[BATTLE_OPPOSITE(battler)].speed)
{
speedVar = (gBattleMons[battler].speed * 128) / (gBattleMons[BATTLE_OPPOSITE(battler)].speed) + (gBattleStruct->runTries * 30);
if (speedVar > (Random() & 0xFF))
++effect;
}
else // same speed or faster
{
++effect;
}
}
++gBattleStruct->runTries;
}
if (effect)
{
gCurrentTurnActionNumber = gBattlersCount;
gBattleOutcome = B_OUTCOME_RAN;
}
return effect;
}
static void HandleAction_Run(void)
{
gBattlerAttacker = gBattlerByTurnOrder[gCurrentTurnActionNumber];
if (gBattleTypeFlags & BATTLE_TYPE_LINK)
{
gCurrentTurnActionNumber = gBattlersCount;
for (gActiveBattler = 0; gActiveBattler < gBattlersCount; ++gActiveBattler)
{
if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER)
{
if (gChosenActionByBattler[gActiveBattler] == B_ACTION_RUN)
gBattleOutcome |= B_OUTCOME_LOST;
}
else
{
if (gChosenActionByBattler[gActiveBattler] == B_ACTION_RUN)
gBattleOutcome |= B_OUTCOME_WON;
}
}
gBattleOutcome |= B_OUTCOME_LINK_BATTLE_RAN;
}
else
{
if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER)
{
if (!TryRunFromBattle(gBattlerAttacker)) // failed to run away
{
ClearFuryCutterDestinyBondGrudge(gBattlerAttacker);
gBattleCommunication[MULTISTRING_CHOOSER] = 3;
gBattlescriptCurrInstr = BattleScript_PrintFailedToRunString;
gCurrentActionFuncId = B_ACTION_EXEC_SCRIPT;
}
}
else
{
if (gBattleMons[gBattlerAttacker].status2 & (STATUS2_WRAPPED | STATUS2_ESCAPE_PREVENTION))
{
gBattleCommunication[MULTISTRING_CHOOSER] = 4;
gBattlescriptCurrInstr = BattleScript_PrintFailedToRunString;
gCurrentActionFuncId = B_ACTION_EXEC_SCRIPT;
}
else
{
gCurrentTurnActionNumber = gBattlersCount;
gBattleOutcome = B_OUTCOME_MON_FLED;
}
}
}
}
static void HandleAction_WatchesCarefully(void)
{
gBattlerAttacker = gBattlerByTurnOrder[gCurrentTurnActionNumber];
gBattle_BG0_X = 0;
gBattle_BG0_Y = 0;
if (gBattleStruct->safariGoNearCounter != 0)
{
--gBattleStruct->safariGoNearCounter;
if (gBattleStruct->safariGoNearCounter == 0)
{
*(&gBattleStruct->safariCatchFactor) = gBaseStats[GetMonData(gEnemyParty, MON_DATA_SPECIES)].catchRate * 100 / 1275;
gBattleCommunication[MULTISTRING_CHOOSER] = 0;
}
else
{
gBattleCommunication[MULTISTRING_CHOOSER] = 1;
}
}
else
{
if (gBattleStruct->safariPkblThrowCounter != 0)
{
--gBattleStruct->safariPkblThrowCounter;
if (gBattleStruct->safariPkblThrowCounter == 0)
gBattleCommunication[MULTISTRING_CHOOSER] = 0;
else
gBattleCommunication[5] = 2;
}
else
{
gBattleCommunication[MULTISTRING_CHOOSER] = 0;
}
}
gBattlescriptCurrInstr = gBattlescriptsForSafariActions[0];
gCurrentActionFuncId = B_ACTION_EXEC_SCRIPT;
}
static void HandleAction_SafariZoneBallThrow(void)
{
gBattlerAttacker = gBattlerByTurnOrder[gCurrentTurnActionNumber];
gBattle_BG0_X = 0;
gBattle_BG0_Y = 0;
--gNumSafariBalls;
gLastUsedItem = ITEM_SAFARI_BALL;
gBattlescriptCurrInstr = gBattlescriptsForBallThrow[ITEM_SAFARI_BALL];
gCurrentActionFuncId = B_ACTION_EXEC_SCRIPT;
}
static void HandleAction_ThrowBait(void)
{
gBattlerAttacker = gBattlerByTurnOrder[gCurrentTurnActionNumber];
gBattle_BG0_X = 0;
gBattle_BG0_Y = 0;
gBattleStruct->safariPkblThrowCounter += Random() % 5 + 2;
if (gBattleStruct->safariPkblThrowCounter > 6)
gBattleStruct->safariPkblThrowCounter = 6;
gBattleStruct->safariGoNearCounter = 0;
gBattleStruct->safariCatchFactor >>= 1;
if (gBattleStruct->safariCatchFactor <= 2)
gBattleStruct->safariCatchFactor = 3;
gBattlescriptCurrInstr = gBattlescriptsForSafariActions[2];
gCurrentActionFuncId = B_ACTION_EXEC_SCRIPT;
}
static void HandleAction_ThrowRock(void)
{
gBattlerAttacker = gBattlerByTurnOrder[gCurrentTurnActionNumber];
gBattle_BG0_X = 0;
gBattle_BG0_Y = 0;
gBattleStruct->safariGoNearCounter += Random() % 5 + 2;
if (gBattleStruct->safariGoNearCounter > 6)
gBattleStruct->safariGoNearCounter = 6;
gBattleStruct->safariPkblThrowCounter = 0;
gBattleStruct->safariCatchFactor <<= 1;
if (gBattleStruct->safariCatchFactor > 20)
gBattleStruct->safariCatchFactor = 20;
gBattlescriptCurrInstr = gBattlescriptsForSafariActions[1];
gCurrentActionFuncId = B_ACTION_EXEC_SCRIPT;
}
static void HandleAction_SafariZoneRun(void)
{
gBattlerAttacker = gBattlerByTurnOrder[gCurrentTurnActionNumber];
PlaySE(SE_NIGERU);
gCurrentTurnActionNumber = gBattlersCount;
gBattleOutcome = B_OUTCOME_RAN;
}
static void HandleAction_OldManBallThrow(void)
{
gBattlerAttacker = gBattlerByTurnOrder[gCurrentTurnActionNumber];
gBattle_BG0_X = 0;
gBattle_BG0_Y = 0;
PREPARE_MON_NICK_BUFFER(gBattleTextBuff1, gBattlerAttacker, gBattlerPartyIndexes[gBattlerAttacker])
gBattlescriptCurrInstr = gBattlescriptsForSafariActions[3];
gCurrentActionFuncId = B_ACTION_EXEC_SCRIPT;
gActionsByTurnOrder[1] = B_ACTION_FINISHED;
}
static void HandleAction_TryFinish(void)
{
if (!HandleFaintedMonActions())
{
gBattleStruct->faintedActionsState = 0;
gCurrentActionFuncId = B_ACTION_FINISHED;
}
}
static void HandleAction_NothingIsFainted(void)
{
++gCurrentTurnActionNumber;
gCurrentActionFuncId = gActionsByTurnOrder[gCurrentTurnActionNumber];
gHitMarker &= ~(HITMARKER_DESTINYBOND | HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_ATTACKSTRING_PRINTED
| HITMARKER_NO_PPDEDUCT | HITMARKER_IGNORE_SAFEGUARD | HITMARKER_IGNORE_ON_AIR
| HITMARKER_IGNORE_UNDERGROUND | HITMARKER_IGNORE_UNDERWATER | HITMARKER_x100000
| HITMARKER_OBEYS | HITMARKER_x10 | HITMARKER_SYNCHRONISE_EFFECT
| HITMARKER_CHARGING | HITMARKER_x4000000);
}
static void HandleAction_ActionFinished(void)
{
++gCurrentTurnActionNumber;
gCurrentActionFuncId = gActionsByTurnOrder[gCurrentTurnActionNumber];
SpecialStatusesClear();
gHitMarker &= ~(HITMARKER_DESTINYBOND | HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_ATTACKSTRING_PRINTED
| HITMARKER_NO_PPDEDUCT | HITMARKER_IGNORE_SAFEGUARD | HITMARKER_IGNORE_ON_AIR
| HITMARKER_IGNORE_UNDERGROUND | HITMARKER_IGNORE_UNDERWATER | HITMARKER_x100000
| HITMARKER_OBEYS | HITMARKER_x10 | HITMARKER_SYNCHRONISE_EFFECT
| HITMARKER_CHARGING | HITMARKER_x4000000);
gCurrentMove = MOVE_NONE;
gBattleMoveDamage = 0;
gMoveResultFlags = 0;
gBattleScripting.animTurn = 0;
gBattleScripting.animTargetsHit = 0;
gLastLandedMoves[gBattlerAttacker] = 0;
gLastHitByType[gBattlerAttacker] = 0;
gBattleStruct->dynamicMoveType = 0;
gDynamicBasePower = 0;
gBattleScripting.atk49_state = 0;
gBattleCommunication[MOVE_EFFECT_BYTE] = 0;
gBattleCommunication[ACTIONS_CONFIRMED_COUNT] = 0;
gBattleScripting.multihitMoveEffect = 0;
gBattleResources->battleScriptsStack->size = 0;
}