#include "global.h" #include "battle.h" #include "battle_anim.h" #include "battle_ai_main.h" #include "battle_ai_util.h" #include "battle_arena.h" #include "battle_controllers.h" #include "battle_interface.h" #include "battle_main.h" #include "battle_message.h" #include "battle_pyramid.h" #include "battle_scripts.h" #include "battle_setup.h" #include "battle_tower.h" #include "battle_util.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 "international_string_util.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 "random.h" #include "recorded_battle.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 "tv.h" #include "util.h" #include "wild_encounter.h" #include "window.h" #include "constants/abilities.h" #include "constants/battle_config.h" #include "constants/battle_move_effects.h" #include "constants/battle_string_ids.h" #include "constants/hold_effects.h" #include "constants/items.h" #include "constants/moves.h" #include "constants/party_menu.h" #include "constants/rgb.h" #include "constants/songs.h" #include "constants/trainers.h" #include "cable_club.h" extern struct Evolution gEvolutionTable[][EVOS_PER_MON]; extern const struct BgTemplate gBattleBgTemplates[]; extern const struct WindowTemplate *const gBattleWindowTemplates[]; static void CB2_InitBattleInternal(void); static void CB2_PreInitMultiBattle(void); static void CB2_PreInitIngamePlayerPartnerBattle(void); static void CB2_HandleStartMultiPartnerBattle(void); static void CB2_HandleStartMultiBattle(void); static void CB2_HandleStartBattle(void); static void TryCorrectShedinjaLanguage(struct Pokemon *mon); static u8 CreateNPCTrainerParty(struct Pokemon *party, u16 trainerNum, bool8 firstTrainer); static void BattleMainCB1(void); static void CB2_EndLinkBattle(void); static void EndLinkBattleInSteps(void); static void CB2_InitAskRecordBattle(void); static void CB2_AskRecordBattle(void); static void AskRecordBattle(void); static void SpriteCB_MoveWildMonToRight(struct Sprite *sprite); static void SpriteCB_WildMonShowHealthbox(struct Sprite *sprite); static void SpriteCB_WildMonAnimate(struct Sprite *sprite); static void SpriteCB_Flicker(struct Sprite *sprite); static void SpriteCB_AnimFaintOpponent(struct Sprite *sprite); static void SpriteCB_BlinkVisible(struct Sprite *sprite); static void SpriteCB_Idle(struct Sprite *sprite); static void SpriteCB_BattleSpriteSlideLeft(struct Sprite *sprite); static void TurnValuesCleanUp(bool8 var0); static void SpriteCB_BounceEffect(struct Sprite *sprite); static void BattleStartClearSetData(void); static void DoBattleIntro(void); static void TryDoEventsBeforeFirstTurn(void); static void HandleTurnActionSelectionState(void); static void RunTurnActionsFunctions(void); static void SetActionsAndBattlersTurnOrder(void); static void UpdateBattlerPartyOrdersOnSwitch(void); static bool8 AllAtActionConfirmed(void); static void TryChangeTurnOrder(void); static void CheckFocusPunch_ClearVarsBeforeTurnStarts(void); static void CheckMegaEvolutionBeforeTurn(void); static void CheckQuickClaw_CustapBerryActivation(void); static void FreeResetData_ReturnToOvOrDoEvolutions(void); static void ReturnFromBattleToOverworld(void); static void TryEvolvePokemon(void); static void WaitForEvoSceneToFinish(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 SpriteCB_UnusedBattleInit(struct Sprite *sprite); static void SpriteCB_UnusedBattleInit_Main(struct Sprite *sprite); static void TrySpecialEvolution(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[400] = {0}; EWRAM_DATA u8 gBattleTextBuff1[TEXT_BUFF_ARRAY_COUNT] = {0}; EWRAM_DATA u8 gBattleTextBuff2[TEXT_BUFF_ARRAY_COUNT] = {0}; EWRAM_DATA u8 gBattleTextBuff3[30] = {0}; //expanded for stupidly long z move names // The below array is never intentionally used. However, Juan's // defeat text (SootopolisCity_Gym_1F_Text_JuanDefeat) is too long // for gDisplayedStringBattle and overflows into this array. If it // is removed (and none of the buffers above are increased in size) // it will instead overflow into useful data. EWRAM_DATA static u32 sFlickerArray[25] = {0}; EWRAM_DATA u32 gBattleTypeFlags = 0; EWRAM_DATA u8 gBattleTerrain = 0; EWRAM_DATA u32 gUnusedFirstBattleVar1 = 0; // Never read EWRAM_DATA struct MultiPartnerMenuPokemon gMultiPartnerParty[MULTI_PARTY_SIZE] = {0}; EWRAM_DATA static struct MultiPartnerMenuPokemon* sMultiPartnerPartyBuffer = NULL; EWRAM_DATA u8 *gBattleAnimBgTileBuffer = NULL; EWRAM_DATA u8 *gBattleAnimBgTilemapBuffer = NULL; 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 u16 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 gIsCriticalHit = FALSE; EWRAM_DATA u8 gMultiHitCounter = 0; EWRAM_DATA const u8 *gBattlescriptCurrInstr = NULL; EWRAM_DATA u8 gChosenActionByBattler[MAX_BATTLERS_COUNT] = {0}; EWRAM_DATA const u8 *gSelectionBattleScripts[MAX_BATTLERS_COUNT] = {NULL}; EWRAM_DATA const u8 *gPalaceSelectionBattleScripts[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 u16 gLastUsedMove = 0; EWRAM_DATA u8 gLastHitBy[MAX_BATTLERS_COUNT] = {0}; EWRAM_DATA u16 gChosenMoveByBattler[MAX_BATTLERS_COUNT] = {0}; EWRAM_DATA u16 gMoveResultFlags = 0; EWRAM_DATA u32 gHitMarker = 0; EWRAM_DATA u8 gTakenDmgByBattler[MAX_BATTLERS_COUNT] = {0}; EWRAM_DATA u8 gUnusedFirstBattleVar2 = 0; // Never read EWRAM_DATA u32 gSideStatuses[2] = {0}; EWRAM_DATA struct SideTimer gSideTimers[2] = {0}; EWRAM_DATA u32 gStatuses3[MAX_BATTLERS_COUNT] = {0}; EWRAM_DATA u32 gStatuses4[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 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 u8 gPlayerDpadHoldFrames = 0; EWRAM_DATA struct BattleSpriteData *gBattleSpritesDataPtr = NULL; EWRAM_DATA struct MonSpritesGfx *gMonSpritesGfxPtr = NULL; EWRAM_DATA struct BattleHealthboxInfo *gBattleControllerOpponentHealthboxData = NULL; // Never read EWRAM_DATA struct BattleHealthboxInfo *gBattleControllerOpponentFlankHealthboxData = NULL; // Never read EWRAM_DATA u16 gBattleMovePower = 0; EWRAM_DATA u16 gMoveToLearn = 0; EWRAM_DATA u8 gBattleMonForms[MAX_BATTLERS_COUNT] = {0}; EWRAM_DATA u32 gFieldStatuses = 0; EWRAM_DATA struct FieldTimer gFieldTimers = {0}; EWRAM_DATA u8 gBattlerAbility = 0; EWRAM_DATA u16 gPartnerSpriteId = 0; EWRAM_DATA struct TotemBoost gTotemBoosts[MAX_BATTLERS_COUNT] = {0}; EWRAM_DATA bool8 gHasFetchedBall = FALSE; EWRAM_DATA u8 gLastUsedBall = 0; EWRAM_DATA u16 gLastThrownBall = 0; EWRAM_DATA u8 gPartyCriticalHits[PARTY_SIZE] = {0}; EWRAM_DATA static u8 sTriedEvolving = 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 gBattleControllerData[MAX_BATTLERS_COUNT]; // Used by the battle controllers to store misc sprite/task IDs for each battler static const struct ScanlineEffectParams sIntroScanlineParams16Bit = { ®_BG3HOFS, SCANLINE_EFFECT_DMACNT_16BIT, 1 }; // unused static const struct ScanlineEffectParams sIntroScanlineParams32Bit = { ®_BG3HOFS, SCANLINE_EFFECT_DMACNT_32BIT, 1 }; const struct SpriteTemplate gUnusedBattleInitSprite = { .tileTag = 0, .paletteTag = 0, .oam = &gDummyOamData, .anims = gDummySpriteAnimTable, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCB_UnusedBattleInit, }; static const u8 sText_ShedinjaJpnName[] = _("ヌケニン"); // Nukenin const struct OamData gOamData_BattleSpriteOpponentSide = { .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_BattleSpritePlayerSide = { .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, }; static const s8 sCenterToCornerVecXs[8] ={-32, -16, -16, -32, -32}; const u8 gTypeNames[NUMBER_OF_MON_TYPES][TYPE_NAME_LENGTH + 1] = { [TYPE_NORMAL] = _("Normal"), [TYPE_FIGHTING] = _("Fight"), [TYPE_FLYING] = _("Flying"), [TYPE_POISON] = _("Poison"), [TYPE_GROUND] = _("Ground"), [TYPE_ROCK] = _("Rock"), [TYPE_BUG] = _("Bug"), [TYPE_GHOST] = _("Ghost"), [TYPE_STEEL] = _("Steel"), [TYPE_MYSTERY] = _("???"), [TYPE_FIRE] = _("Fire"), [TYPE_WATER] = _("Water"), [TYPE_GRASS] = _("Grass"), [TYPE_ELECTRIC] = _("Electr"), [TYPE_PSYCHIC] = _("Psychc"), [TYPE_ICE] = _("Ice"), [TYPE_DRAGON] = _("Dragon"), [TYPE_DARK] = _("Dark"), [TYPE_FAIRY] = _("Fairy"), }; // This is a factor in how much money you get for beating a trainer. const struct TrainerMoney gTrainerMoneyTable[] = { {TRAINER_CLASS_TEAM_AQUA, 5}, {TRAINER_CLASS_AQUA_ADMIN, 10}, {TRAINER_CLASS_AQUA_LEADER, 20}, {TRAINER_CLASS_AROMA_LADY, 10}, {TRAINER_CLASS_RUIN_MANIAC, 15}, {TRAINER_CLASS_INTERVIEWER, 12}, {TRAINER_CLASS_TUBER_F, 1}, {TRAINER_CLASS_TUBER_M, 1}, {TRAINER_CLASS_SIS_AND_BRO, 3}, {TRAINER_CLASS_COOLTRAINER, 12}, {TRAINER_CLASS_HEX_MANIAC, 6}, {TRAINER_CLASS_LADY, 50}, {TRAINER_CLASS_BEAUTY, 20}, {TRAINER_CLASS_RICH_BOY, 50}, {TRAINER_CLASS_POKEMANIAC, 15}, {TRAINER_CLASS_SWIMMER_M, 2}, {TRAINER_CLASS_BLACK_BELT, 8}, {TRAINER_CLASS_GUITARIST, 8}, {TRAINER_CLASS_KINDLER, 8}, {TRAINER_CLASS_CAMPER, 4}, {TRAINER_CLASS_OLD_COUPLE, 10}, {TRAINER_CLASS_BUG_MANIAC, 15}, {TRAINER_CLASS_PSYCHIC, 6}, {TRAINER_CLASS_GENTLEMAN, 20}, {TRAINER_CLASS_ELITE_FOUR, 25}, {TRAINER_CLASS_LEADER, 25}, {TRAINER_CLASS_SCHOOL_KID, 5}, {TRAINER_CLASS_SR_AND_JR, 4}, {TRAINER_CLASS_POKEFAN, 20}, {TRAINER_CLASS_EXPERT, 10}, {TRAINER_CLASS_YOUNGSTER, 4}, {TRAINER_CLASS_CHAMPION, 50}, {TRAINER_CLASS_FISHERMAN, 10}, {TRAINER_CLASS_TRIATHLETE, 10}, {TRAINER_CLASS_DRAGON_TAMER, 12}, {TRAINER_CLASS_BIRD_KEEPER, 8}, {TRAINER_CLASS_NINJA_BOY, 3}, {TRAINER_CLASS_BATTLE_GIRL, 6}, {TRAINER_CLASS_PARASOL_LADY, 10}, {TRAINER_CLASS_SWIMMER_F, 2}, {TRAINER_CLASS_PICNICKER, 4}, {TRAINER_CLASS_TWINS, 3}, {TRAINER_CLASS_SAILOR, 8}, {TRAINER_CLASS_COLLECTOR, 15}, {TRAINER_CLASS_RIVAL, 15}, {TRAINER_CLASS_PKMN_BREEDER, 10}, {TRAINER_CLASS_PKMN_RANGER, 12}, {TRAINER_CLASS_TEAM_MAGMA, 5}, {TRAINER_CLASS_MAGMA_ADMIN, 10}, {TRAINER_CLASS_MAGMA_LEADER, 20}, {TRAINER_CLASS_LASS, 4}, {TRAINER_CLASS_BUG_CATCHER, 4}, {TRAINER_CLASS_HIKER, 10}, {TRAINER_CLASS_YOUNG_COUPLE, 8}, {TRAINER_CLASS_WINSTRATE, 10}, {0xFF, 5}, // Any trainer class not listed above uses this }; #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_ThrowPokeblock, [B_ACTION_SAFARI_GO_NEAR] = HandleAction_GoNear, [B_ACTION_SAFARI_RUN] = HandleAction_SafariZoneRun, [B_ACTION_WALLY_THROW] = HandleAction_WallyBallThrow, [B_ACTION_EXEC_SCRIPT] = HandleAction_RunBattleScript, [B_ACTION_TRY_FINISH] = HandleAction_TryFinish, [B_ACTION_FINISHED] = HandleAction_ActionFinished, [B_ACTION_NOTHING_FAINTED] = HandleAction_NothingIsFainted, [B_ACTION_THROW_BALL] = HandleAction_ThrowBall, }; static void (* const sEndTurnFuncsTable[])(void) = { [0] = HandleEndTurn_ContinueBattle, [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, [B_OUTCOME_FORFEITED] = HandleEndTurn_FinishBattle, [B_OUTCOME_MON_TELEPORTED] = HandleEndTurn_FinishBattle, }; const u8 gStatusConditionString_PoisonJpn[] = _("どく$$$$$"); const u8 gStatusConditionString_SleepJpn[] = _("ねむり$$$$"); const u8 gStatusConditionString_ParalysisJpn[] = _("まひ$$$$$"); const u8 gStatusConditionString_BurnJpn[] = _("やけど$$$$"); const u8 gStatusConditionString_IceJpn[] = _("こおり$$$$"); const u8 gStatusConditionString_ConfusionJpn[] = _("こんらん$$$"); const u8 gStatusConditionString_LoveJpn[] = _("メロメロ$$$"); const u8 *const gStatusConditionStringsTable[][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(); RecordedBattle_ClearFrontierPassFlag(); if (gBattleTypeFlags & BATTLE_TYPE_MULTI) { if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) { CB2_InitBattleInternal(); } else if (!(gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER)) { HandleLinkBattleSetup(); SetMainCallback2(CB2_PreInitMultiBattle); } else { SetMainCallback2(CB2_PreInitIngamePlayerPartnerBattle); } gBattleCommunication[MULTIUSE_STATE] = 0; } else { CB2_InitBattleInternal(); } } static void CB2_InitBattleInternal(void) { s32 i; u16 targetSpecies; SetHBlankCallback(NULL); SetVBlankCallback(NULL); CpuFill32(0, (void *)(VRAM), VRAM_SIZE); SetGpuReg(REG_OFFSET_MOSAIC, 0); SetGpuReg(REG_OFFSET_WIN0H, DISPLAY_WIDTH); SetGpuReg(REG_OFFSET_WIN0V, WIN_RANGE(DISPLAY_HEIGHT / 2, DISPLAY_HEIGHT / 2 + 1)); SetGpuReg(REG_OFFSET_WININ, 0); SetGpuReg(REG_OFFSET_WINOUT, 0); gBattle_WIN0H = DISPLAY_WIDTH; if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && gPartnerTrainerId != TRAINER_STEVEN_PARTNER && gPartnerTrainerId < TRAINER_CUSTOM_PARTNER) { gBattle_WIN0V = DISPLAY_HEIGHT - 1; gBattle_WIN1H = DISPLAY_WIDTH; gBattle_WIN1V = 32; } else { gBattle_WIN0V = WIN_RANGE(DISPLAY_HEIGHT / 2, DISPLAY_HEIGHT / 2 + 1); ScanlineEffect_Clear(); i = 0; while (i < 80) { gScanlineEffectRegBuffers[0][i] = 0xF0; gScanlineEffectRegBuffers[1][i] = 0xF0; i++; } while (i < 160) { gScanlineEffectRegBuffers[0][i] = 0xFF10; gScanlineEffectRegBuffers[1][i] = 0xFF10; i++; } 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(); if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) gBattleTerrain = BATTLE_TERRAIN_BUILDING; InitBattleBgsVideo(); LoadBattleTextboxAndBackground(); ResetSpriteData(); ResetTasks(); DrawBattleEntryBackground(); FreeAllSpritePalettes(); gReservedSpritePaletteCount = 4; SetVBlankCallback(VBlankCB_Battle); SetUpBattleVarsAndBirchZigzagoon(); if (gBattleTypeFlags & BATTLE_TYPE_MULTI && gBattleTypeFlags & BATTLE_TYPE_BATTLE_TOWER) SetMainCallback2(CB2_HandleStartMultiPartnerBattle); else if (gBattleTypeFlags & BATTLE_TYPE_MULTI && gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) SetMainCallback2(CB2_HandleStartMultiPartnerBattle); else if (gBattleTypeFlags & BATTLE_TYPE_MULTI) SetMainCallback2(CB2_HandleStartMultiBattle); else SetMainCallback2(CB2_HandleStartBattle); if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED))) { CreateNPCTrainerParty(&gEnemyParty[0], gTrainerBattleOpponent_A, TRUE); if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS && !BATTLE_TWO_VS_ONE_OPPONENT) CreateNPCTrainerParty(&gEnemyParty[PARTY_SIZE / 2], gTrainerBattleOpponent_B, FALSE); SetWildMonHeldItem(); } gMain.inBattle = TRUE; gSaveBlock2Ptr->frontier.disableRecordBattle = FALSE; for (i = 0; i < PARTY_SIZE; i++) AdjustFriendship(&gPlayerParty[i], FRIENDSHIP_EVENT_LEAGUE_BATTLE); // Apply party-wide start-of-battle form changes for (i = 0; i < PARTY_SIZE; i++) { // Player's side targetSpecies = GetFormChangeTargetSpecies(&gPlayerParty[i], FORM_BATTLE_BEGIN, 0); if (targetSpecies != SPECIES_NONE) { SetMonData(&gPlayerParty[i], MON_DATA_SPECIES, &targetSpecies); CalculateMonStats(&gPlayerParty[i]); TryToSetBattleFormChangeMoves(&gPlayerParty[i]); } // Opponent's side targetSpecies = GetFormChangeTargetSpecies(&gEnemyParty[i], FORM_BATTLE_BEGIN, 0); if (targetSpecies != SPECIES_NONE) { SetMonData(&gEnemyParty[i], MON_DATA_SPECIES, &targetSpecies); CalculateMonStats(&gEnemyParty[i]); TryToSetBattleFormChangeMoves(&gEnemyParty[i]); } } gBattleCommunication[MULTIUSE_STATE] = 0; } #define BUFFER_PARTY_VS_SCREEN_STATUS(party, flags, i) \ for ((i) = 0; (i) < PARTY_SIZE; (i)++) \ { \ u16 species = GetMonData(&(party)[(i)], MON_DATA_SPECIES2); \ u16 hp = GetMonData(&(party)[(i)], MON_DATA_HP); \ u32 status = GetMonData(&(party)[(i)], MON_DATA_STATUS); \ \ if (species == SPECIES_NONE) \ continue; \ \ /* Is healthy mon? */ \ if (species != SPECIES_EGG && hp != 0 && status == 0) \ (flags) |= 1 << (i) * 2; \ \ if (species == SPECIES_NONE) /* Redundant */ \ continue; \ \ /* Is Egg or statused? */ \ if (hp != 0 && (species == SPECIES_EGG || status != 0)) \ (flags) |= 2 << (i) * 2; \ \ if (species == SPECIES_NONE) /* Redundant */ \ continue; \ \ /* Is fainted? */ \ if (species != SPECIES_EGG && hp == 0) \ (flags) |= 3 << (i) * 2; \ } // For Vs Screen at link battle start static void BufferPartyVsScreenHealth_AtStart(void) { u16 flags = 0; s32 i; BUFFER_PARTY_VS_SCREEN_STATUS(gPlayerParty, flags, i); gBattleStruct->multiBuffer.linkBattlerHeader.vsScreenHealthFlagsLo = flags; *(&gBattleStruct->multiBuffer.linkBattlerHeader.vsScreenHealthFlagsHi) = flags >> 8; gBattleStruct->multiBuffer.linkBattlerHeader.vsScreenHealthFlagsHi |= FlagGet(FLAG_SYS_FRONTIER_PASS) << 7; } static void SetPlayerBerryDataInBattleStruct(void) { s32 i; struct BattleStruct *battleStruct = gBattleStruct; struct BattleEnigmaBerry *battleBerry = &battleStruct->multiBuffer.linkBattlerHeader.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_E_READER)); 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_E_READER)); 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) { if (gBattleTypeFlags & BATTLE_TYPE_BATTLE_TOWER) numPlayers = 2; else numPlayers = 4; for (i = 0; i < numPlayers; 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; } } } } // This was inlined in Ruby/Sapphire static void FindLinkBattleMaster(u8 numPlayers, u8 multiPlayerId) { u8 found = 0; // If player 1 is playing the minimum version, player 1 is master. if (gBlockRecvBuffer[0][0] == 0x100) { if (multiPlayerId == 0) gBattleTypeFlags |= BATTLE_TYPE_IS_MASTER | BATTLE_TYPE_TRAINER; else gBattleTypeFlags |= BATTLE_TYPE_TRAINER; found++; } if (found == 0) { // If multiple different versions are being used, player 1 is master. s32 i; for (i = 0; i < numPlayers; i++) { if (gBlockRecvBuffer[0][0] != gBlockRecvBuffer[i][0]) break; } if (i == numPlayers) { if (multiPlayerId == 0) gBattleTypeFlags |= BATTLE_TYPE_IS_MASTER | BATTLE_TYPE_TRAINER; else gBattleTypeFlags |= BATTLE_TYPE_TRAINER; found++; } if (found == 0) { // Lowest index player with the highest game version is master. for (i = 0; i < numPlayers; i++) { if (gBlockRecvBuffer[i][0] == 0x300 && i != multiPlayerId) { if (i < multiPlayerId) break; } if (gBlockRecvBuffer[i][0] > 0x300 && i != multiPlayerId) break; } if (i == numPlayers) 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(); gBattleScripting.multiplayerId = playerMultiplayerId; enemyMultiplayerId = playerMultiplayerId ^ BIT_SIDE; switch (gBattleCommunication[MULTIUSE_STATE]) { case 0: if (!IsDma3ManagerBusyWithBgCopy()) { ShowBg(0); ShowBg(1); ShowBg(2); ShowBg(3); FillAroundBattleWindows(); gBattleCommunication[MULTIUSE_STATE] = 1; } if (gWirelessCommType) LoadWirelessStatusIndicatorSpriteGfx(); break; case 1: if (gBattleTypeFlags & BATTLE_TYPE_LINK) { if (gReceivedRemoteLinkPlayers != 0) { if (IsLinkTaskFinished()) { // 0x300 *(&gBattleStruct->multiBuffer.linkBattlerHeader.versionSignatureLo) = 0; *(&gBattleStruct->multiBuffer.linkBattlerHeader.versionSignatureHi) = 3; BufferPartyVsScreenHealth_AtStart(); SetPlayerBerryDataInBattleStruct(); if (gTrainerBattleOpponent_A == TRAINER_UNION_ROOM) { gLinkPlayers[0].id = 0; gLinkPlayers[1].id = 1; } SendBlock(BitmaskAllOtherLinkPlayers(), &gBattleStruct->multiBuffer.linkBattlerHeader, sizeof(gBattleStruct->multiBuffer.linkBattlerHeader)); gBattleCommunication[MULTIUSE_STATE] = 2; } if (gWirelessCommType) CreateWirelessStatusIndicatorSprite(0, 0); } } else { if (!(gBattleTypeFlags & BATTLE_TYPE_RECORDED)) gBattleTypeFlags |= BATTLE_TYPE_IS_MASTER; gBattleCommunication[MULTIUSE_STATE] = 15; SetAllPlayersBerryData(); } break; case 2: if ((GetBlockReceivedStatus() & 3) == 3) { u8 taskId; ResetBlockReceivedFlags(); FindLinkBattleMaster(2, playerMultiplayerId); SetAllPlayersBerryData(); taskId = CreateTask(InitLinkBattleVsScreen, 0); gTasks[taskId].data[1] = 0x10E; gTasks[taskId].data[2] = 0x5A; gTasks[taskId].data[5] = 0; gTasks[taskId].data[3] = gBattleStruct->multiBuffer.linkBattlerHeader.vsScreenHealthFlagsLo | (gBattleStruct->multiBuffer.linkBattlerHeader.vsScreenHealthFlagsHi << 8); gTasks[taskId].data[4] = gBlockRecvBuffer[enemyMultiplayerId][1]; RecordedBattle_SetFrontierPassFlagFromHword(gBlockRecvBuffer[playerMultiplayerId][1]); RecordedBattle_SetFrontierPassFlagFromHword(gBlockRecvBuffer[enemyMultiplayerId][1]); gBattleCommunication[MULTIUSE_STATE]++; } break; case 3: // Link battle, send/receive party Pokémon 2 at a time if (IsLinkTaskFinished()) { // Send Pokémon 1-2 SendBlock(BitmaskAllOtherLinkPlayers(), gPlayerParty, sizeof(struct Pokemon) * 2); gBattleCommunication[MULTIUSE_STATE]++; } break; case 4: if ((GetBlockReceivedStatus() & 3) == 3) { // Recv Pokémon 1-2 ResetBlockReceivedFlags(); memcpy(gEnemyParty, gBlockRecvBuffer[enemyMultiplayerId], sizeof(struct Pokemon) * 2); gBattleCommunication[MULTIUSE_STATE]++; } break; case 7: if (IsLinkTaskFinished()) { // Send Pokémon 3-4 SendBlock(BitmaskAllOtherLinkPlayers(), &gPlayerParty[2], sizeof(struct Pokemon) * 2); gBattleCommunication[MULTIUSE_STATE]++; } break; case 8: if ((GetBlockReceivedStatus() & 3) == 3) { // Recv Pokémon 3-4 ResetBlockReceivedFlags(); memcpy(&gEnemyParty[2], gBlockRecvBuffer[enemyMultiplayerId], sizeof(struct Pokemon) * 2); gBattleCommunication[MULTIUSE_STATE]++; } break; case 11: if (IsLinkTaskFinished()) { // Send Pokémon 5-6 SendBlock(BitmaskAllOtherLinkPlayers(), &gPlayerParty[4], sizeof(struct Pokemon) * 2); gBattleCommunication[MULTIUSE_STATE]++; } break; case 12: if ((GetBlockReceivedStatus() & 3) == 3) { // Recv Pokémon 5-6 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: InitBattleControllers(); RecordedBattle_SetTrainerInfo(); gBattleCommunication[SPRITES_INIT_STATE1] = 0; gBattleCommunication[SPRITES_INIT_STATE2] = 0; if (gBattleTypeFlags & BATTLE_TYPE_LINK) { // Check if both players are using Emerald // to determine if the recorded battle rng // seed needs to be sent s32 i; for (i = 0; i < 2 && (gLinkPlayers[i].version & 0xFF) == VERSION_EMERALD; i++); if (i == 2) gBattleCommunication[MULTIUSE_STATE] = 16; else gBattleCommunication[MULTIUSE_STATE] = 18; } else { gBattleCommunication[MULTIUSE_STATE] = 18; } break; case 16: // Both players are using Emerald, send rng seed for recorded battle if (IsLinkTaskFinished()) { SendBlock(BitmaskAllOtherLinkPlayers(), &gRecordedBattleRngSeed, sizeof(gRecordedBattleRngSeed)); gBattleCommunication[MULTIUSE_STATE]++; } break; case 17: // Receive rng seed for recorded battle (only read it if partner is the link master) if ((GetBlockReceivedStatus() & 3) == 3) { ResetBlockReceivedFlags(); if (!(gBattleTypeFlags & BATTLE_TYPE_IS_MASTER)) memcpy(&gRecordedBattleRngSeed, gBlockRecvBuffer[enemyMultiplayerId], sizeof(gRecordedBattleRngSeed)); gBattleCommunication[MULTIUSE_STATE]++; } break; case 18: // Finish, start battle 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_LINK_IN_BATTLE; } break; // Introduce short delays between sending party Pokémon for link 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 CB2_HandleStartMultiPartnerBattle(void) { u8 playerMultiplayerId; u8 partnerMultiplayerId; RunTasks(); AnimateSprites(); BuildOamBuffer(); playerMultiplayerId = GetMultiplayerId(); gBattleScripting.multiplayerId = playerMultiplayerId; partnerMultiplayerId = playerMultiplayerId ^ BIT_SIDE; switch (gBattleCommunication[MULTIUSE_STATE]) { case 0: if (!IsDma3ManagerBusyWithBgCopy()) { ShowBg(0); ShowBg(1); ShowBg(2); ShowBg(3); FillAroundBattleWindows(); gBattleCommunication[MULTIUSE_STATE] = 1; } if (gWirelessCommType) LoadWirelessStatusIndicatorSpriteGfx(); // fall through case 1: if (gBattleTypeFlags & BATTLE_TYPE_LINK) { if (gReceivedRemoteLinkPlayers != 0) { u8 language; gLinkPlayers[0].id = 0; gLinkPlayers[1].id = 2; gLinkPlayers[2].id = 1; gLinkPlayers[3].id = 3; GetFrontierTrainerName(gLinkPlayers[2].name, gTrainerBattleOpponent_A); GetFrontierTrainerName(gLinkPlayers[3].name, gTrainerBattleOpponent_B); GetBattleTowerTrainerLanguage(&language, gTrainerBattleOpponent_A); gLinkPlayers[2].language = language; GetBattleTowerTrainerLanguage(&language, gTrainerBattleOpponent_B); gLinkPlayers[3].language = language; if (IsLinkTaskFinished()) { // 0x300 *(&gBattleStruct->multiBuffer.linkBattlerHeader.versionSignatureLo) = 0; *(&gBattleStruct->multiBuffer.linkBattlerHeader.versionSignatureHi) = 3; BufferPartyVsScreenHealth_AtStart(); SetPlayerBerryDataInBattleStruct(); SendBlock(BitmaskAllOtherLinkPlayers(), &gBattleStruct->multiBuffer.linkBattlerHeader, sizeof(gBattleStruct->multiBuffer.linkBattlerHeader)); gBattleCommunication[MULTIUSE_STATE] = 2; } if (gWirelessCommType) CreateWirelessStatusIndicatorSprite(0, 0); } } else { if (!(gBattleTypeFlags & BATTLE_TYPE_RECORDED)) gBattleTypeFlags |= BATTLE_TYPE_IS_MASTER; gBattleCommunication[MULTIUSE_STATE] = 13; SetAllPlayersBerryData(); } break; case 2: if ((GetBlockReceivedStatus() & 3) == 3) { u8 taskId; ResetBlockReceivedFlags(); FindLinkBattleMaster(2, playerMultiplayerId); SetAllPlayersBerryData(); taskId = CreateTask(InitLinkBattleVsScreen, 0); gTasks[taskId].data[1] = 0x10E; gTasks[taskId].data[2] = 0x5A; gTasks[taskId].data[5] = 0; gTasks[taskId].data[3] = 0x145; gTasks[taskId].data[4] = 0x145; gBattleCommunication[MULTIUSE_STATE]++; } break; case 3: // Link battle, send/receive party Pokémon in groups if (IsLinkTaskFinished()) { // Send Pokémon 1-2 SendBlock(BitmaskAllOtherLinkPlayers(), gPlayerParty, sizeof(struct Pokemon) * 2); gBattleCommunication[MULTIUSE_STATE]++; } break; case 4: if ((GetBlockReceivedStatus() & 3) == 3) { // Recv partner's Pokémon 1-2, and copy partner's and own Pokémon into party positions ResetBlockReceivedFlags(); if (gLinkPlayers[playerMultiplayerId].id != 0) { memcpy(gPlayerParty, gBlockRecvBuffer[partnerMultiplayerId], sizeof(struct Pokemon) * 2); memcpy(&gPlayerParty[MULTI_PARTY_SIZE], gBlockRecvBuffer[playerMultiplayerId], sizeof(struct Pokemon) * 2); } else { memcpy(gPlayerParty, gBlockRecvBuffer[playerMultiplayerId], sizeof(struct Pokemon) * 2); memcpy(&gPlayerParty[MULTI_PARTY_SIZE], gBlockRecvBuffer[partnerMultiplayerId], sizeof(struct Pokemon) * 2); } gBattleCommunication[MULTIUSE_STATE]++; } break; case 5: if (IsLinkTaskFinished()) { // Send Pokémon 3 SendBlock(BitmaskAllOtherLinkPlayers(), &gPlayerParty[2], sizeof(struct Pokemon)); gBattleCommunication[MULTIUSE_STATE]++; } break; case 6: if ((GetBlockReceivedStatus() & 3) == 3) { // Recv partner's Pokémon 3, and copy partner's and own Pokémon into party positions ResetBlockReceivedFlags(); if (gLinkPlayers[playerMultiplayerId].id != 0) { memcpy(&gPlayerParty[2], gBlockRecvBuffer[partnerMultiplayerId], sizeof(struct Pokemon)); memcpy(&gPlayerParty[2 + MULTI_PARTY_SIZE], gBlockRecvBuffer[playerMultiplayerId], sizeof(struct Pokemon)); } else { memcpy(&gPlayerParty[2], gBlockRecvBuffer[playerMultiplayerId], sizeof(struct Pokemon)); memcpy(&gPlayerParty[2 + MULTI_PARTY_SIZE], gBlockRecvBuffer[partnerMultiplayerId], sizeof(struct Pokemon)); } gBattleCommunication[MULTIUSE_STATE]++; } break; case 7: if (IsLinkTaskFinished()) { // Send enemy Pokémon 1-2 to partner SendBlock(BitmaskAllOtherLinkPlayers(), gEnemyParty, sizeof(struct Pokemon) * 2); gBattleCommunication[MULTIUSE_STATE]++; } break; case 8: if ((GetBlockReceivedStatus() & 3) == 3) { // Recv enemy Pokémon 1-2 (if not master) ResetBlockReceivedFlags(); if (GetMultiplayerId() != 0) memcpy(gEnemyParty, gBlockRecvBuffer[0], sizeof(struct Pokemon) * 2); gBattleCommunication[MULTIUSE_STATE]++; } break; case 9: if (IsLinkTaskFinished()) { // Send enemy Pokémon 3-4 to partner SendBlock(BitmaskAllOtherLinkPlayers(), &gEnemyParty[2], sizeof(struct Pokemon) * 2); gBattleCommunication[MULTIUSE_STATE]++; } break; case 10: if ((GetBlockReceivedStatus() & 3) == 3) { // Recv enemy Pokémon 3-4 (if not master) ResetBlockReceivedFlags(); if (GetMultiplayerId() != 0) memcpy(&gEnemyParty[2], gBlockRecvBuffer[0], sizeof(struct Pokemon) * 2); gBattleCommunication[MULTIUSE_STATE]++; } break; case 11: if (IsLinkTaskFinished()) { // Send enemy Pokémon 5-6 to partner SendBlock(BitmaskAllOtherLinkPlayers(), &gEnemyParty[4], sizeof(struct Pokemon) * 2); gBattleCommunication[MULTIUSE_STATE]++; } break; case 12: if ((GetBlockReceivedStatus() & 3) == 3) { // Recv enemy Pokémon 5-6 (if not master) ResetBlockReceivedFlags(); if (GetMultiplayerId() != 0) memcpy(&gEnemyParty[4], gBlockRecvBuffer[0], sizeof(struct Pokemon) * 2); 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 13: InitBattleControllers(); RecordedBattle_SetTrainerInfo(); gBattleCommunication[SPRITES_INIT_STATE1] = 0; gBattleCommunication[SPRITES_INIT_STATE2] = 0; if (gBattleTypeFlags & BATTLE_TYPE_LINK) gBattleCommunication[MULTIUSE_STATE] = 14; else gBattleCommunication[MULTIUSE_STATE] = 16; break; case 14: // Send rng seed for recorded battle if (IsLinkTaskFinished()) { SendBlock(BitmaskAllOtherLinkPlayers(), &gRecordedBattleRngSeed, sizeof(gRecordedBattleRngSeed)); gBattleCommunication[MULTIUSE_STATE]++; } break; case 15: // Receive rng seed for recorded battle (only read it if partner is the link master) if ((GetBlockReceivedStatus() & 3) == 3) { ResetBlockReceivedFlags(); if (!(gBattleTypeFlags & BATTLE_TYPE_IS_MASTER)) memcpy(&gRecordedBattleRngSeed, gBlockRecvBuffer[partnerMultiplayerId], sizeof(gRecordedBattleRngSeed)); gBattleCommunication[MULTIUSE_STATE]++; } break; case 16: // Finish, start battle if (BattleInitAllSprites(&gBattleCommunication[SPRITES_INIT_STATE1], &gBattleCommunication[SPRITES_INIT_STATE2])) { TrySetLinkBattleTowerEnemyPartyLevel(); gPreBattleCallback1 = gMain.callback1; gMain.callback1 = BattleMainCB1; SetMainCallback2(BattleMainCB2); if (gBattleTypeFlags & BATTLE_TYPE_LINK) gBattleTypeFlags |= BATTLE_TYPE_LINK_IN_BATTLE; } break; } } static void SetMultiPartnerMenuParty(u8 offset) { s32 i; for (i = 0; i < MULTI_PARTY_SIZE; i++) { gMultiPartnerParty[i].species = GetMonData(&gPlayerParty[offset + i], MON_DATA_SPECIES); gMultiPartnerParty[i].heldItem = GetMonData(&gPlayerParty[offset + i], MON_DATA_HELD_ITEM); GetMonData(&gPlayerParty[offset + i], MON_DATA_NICKNAME, gMultiPartnerParty[i].nickname); gMultiPartnerParty[i].level = GetMonData(&gPlayerParty[offset + i], MON_DATA_LEVEL); gMultiPartnerParty[i].hp = GetMonData(&gPlayerParty[offset + i], MON_DATA_HP); gMultiPartnerParty[i].maxhp = GetMonData(&gPlayerParty[offset + i], MON_DATA_MAX_HP); gMultiPartnerParty[i].status = GetMonData(&gPlayerParty[offset + i], MON_DATA_STATUS); gMultiPartnerParty[i].personality = GetMonData(&gPlayerParty[offset + i], MON_DATA_PERSONALITY); gMultiPartnerParty[i].gender = GetMonGender(&gPlayerParty[offset + i]); StripExtCtrlCodes(gMultiPartnerParty[i].nickname); if (GetMonData(&gPlayerParty[offset + i], MON_DATA_LANGUAGE) != LANGUAGE_JAPANESE) PadNameString(gMultiPartnerParty[i].nickname, CHAR_SPACE); } memcpy(sMultiPartnerPartyBuffer, gMultiPartnerParty, sizeof(gMultiPartnerParty)); } static void CB2_PreInitMultiBattle(void) { s32 i; u8 playerMultiplierId; s32 numPlayers = MAX_BATTLERS_COUNT; u8 blockMask = 0xF; u32 *savedBattleTypeFlags; void (**savedCallback)(void); if (gBattleTypeFlags & BATTLE_TYPE_BATTLE_TOWER) { numPlayers = 2; blockMask = 3; } playerMultiplierId = GetMultiplayerId(); gBattleScripting.multiplayerId = playerMultiplierId; savedCallback = &gBattleStruct->savedCallback; savedBattleTypeFlags = &gBattleStruct->savedBattleTypeFlags; RunTasks(); AnimateSprites(); BuildOamBuffer(); switch (gBattleCommunication[MULTIUSE_STATE]) { case 0: if (gReceivedRemoteLinkPlayers != 0 && IsLinkTaskFinished()) { sMultiPartnerPartyBuffer = Alloc(sizeof(gMultiPartnerParty)); SetMultiPartnerMenuParty(0); SendBlock(BitmaskAllOtherLinkPlayers(), sMultiPartnerPartyBuffer, sizeof(gMultiPartnerParty)); gBattleCommunication[MULTIUSE_STATE]++; } break; case 1: if ((GetBlockReceivedStatus() & blockMask) == blockMask) { ResetBlockReceivedFlags(); for (i = 0; i < numPlayers; i++) { if (i == playerMultiplierId) continue; if (numPlayers == MAX_LINK_PLAYERS) { if ((!(gLinkPlayers[i].id & 1) && !(gLinkPlayers[playerMultiplierId].id & 1)) || (gLinkPlayers[i].id & 1 && gLinkPlayers[playerMultiplierId].id & 1)) { memcpy(gMultiPartnerParty, gBlockRecvBuffer[i], sizeof(gMultiPartnerParty)); } } else { memcpy(gMultiPartnerParty, gBlockRecvBuffer[i], sizeof(gMultiPartnerParty)); } } gBattleCommunication[MULTIUSE_STATE]++; *savedCallback = gMain.savedCallback; *savedBattleTypeFlags = gBattleTypeFlags; gMain.savedCallback = CB2_PreInitMultiBattle; ShowPartyMenuToShowcaseMultiBattleParty(); } break; case 2: if (IsLinkTaskFinished() && !gPaletteFade.active) { gBattleCommunication[MULTIUSE_STATE]++; if (gWirelessCommType) SetLinkStandbyCallback(); else SetCloseLinkCallback(); } break; case 3: if (gWirelessCommType) { if (IsLinkRfuTaskFinished()) { gBattleTypeFlags = *savedBattleTypeFlags; gMain.savedCallback = *savedCallback; SetMainCallback2(CB2_InitBattleInternal); FREE_AND_SET_NULL(sMultiPartnerPartyBuffer); } } else if (gReceivedRemoteLinkPlayers == 0) { gBattleTypeFlags = *savedBattleTypeFlags; gMain.savedCallback = *savedCallback; SetMainCallback2(CB2_InitBattleInternal); FREE_AND_SET_NULL(sMultiPartnerPartyBuffer); } break; } } static void CB2_PreInitIngamePlayerPartnerBattle(void) { u32 *savedBattleTypeFlags; void (**savedCallback)(void); savedCallback = &gBattleStruct->savedCallback; savedBattleTypeFlags = &gBattleStruct->savedBattleTypeFlags; RunTasks(); AnimateSprites(); BuildOamBuffer(); switch (gBattleCommunication[MULTIUSE_STATE]) { case 0: sMultiPartnerPartyBuffer = Alloc(sizeof(gMultiPartnerParty)); SetMultiPartnerMenuParty(MULTI_PARTY_SIZE); gBattleCommunication[MULTIUSE_STATE]++; *savedCallback = gMain.savedCallback; *savedBattleTypeFlags = gBattleTypeFlags; gMain.savedCallback = CB2_PreInitIngamePlayerPartnerBattle; ShowPartyMenuToShowcaseMultiBattleParty(); break; case 1: if (!gPaletteFade.active) { gBattleCommunication[MULTIUSE_STATE] = 2; gBattleTypeFlags = *savedBattleTypeFlags; gMain.savedCallback = *savedCallback; SetMainCallback2(CB2_InitBattleInternal); FREE_AND_SET_NULL(sMultiPartnerPartyBuffer); } break; } } static void CB2_HandleStartMultiBattle(void) { u8 playerMultiplayerId; s32 id; u8 var; playerMultiplayerId = GetMultiplayerId(); gBattleScripting.multiplayerId = playerMultiplayerId; RunTasks(); AnimateSprites(); BuildOamBuffer(); switch (gBattleCommunication[MULTIUSE_STATE]) { case 0: if (!IsDma3ManagerBusyWithBgCopy()) { ShowBg(0); ShowBg(1); ShowBg(2); ShowBg(3); FillAroundBattleWindows(); gBattleCommunication[MULTIUSE_STATE] = 1; } if (gWirelessCommType) LoadWirelessStatusIndicatorSpriteGfx(); break; case 1: if (gBattleTypeFlags & BATTLE_TYPE_LINK) { if (gReceivedRemoteLinkPlayers != 0) { if (IsLinkTaskFinished()) { // 0x300 *(&gBattleStruct->multiBuffer.linkBattlerHeader.versionSignatureLo) = 0; *(&gBattleStruct->multiBuffer.linkBattlerHeader.versionSignatureHi) = 3; BufferPartyVsScreenHealth_AtStart(); SetPlayerBerryDataInBattleStruct(); SendBlock(BitmaskAllOtherLinkPlayers(), &gBattleStruct->multiBuffer.linkBattlerHeader, sizeof(gBattleStruct->multiBuffer.linkBattlerHeader)); gBattleCommunication[MULTIUSE_STATE]++; } if (gWirelessCommType) CreateWirelessStatusIndicatorSprite(0, 0); } } else { if (!(gBattleTypeFlags & BATTLE_TYPE_RECORDED)) gBattleTypeFlags |= BATTLE_TYPE_IS_MASTER; gBattleCommunication[MULTIUSE_STATE] = 7; SetAllPlayersBerryData(); } break; case 2: if ((GetBlockReceivedStatus() & 0xF) == 0xF) { ResetBlockReceivedFlags(); FindLinkBattleMaster(4, playerMultiplayerId); SetAllPlayersBerryData(); var = CreateTask(InitLinkBattleVsScreen, 0); gTasks[var].data[1] = 0x10E; gTasks[var].data[2] = 0x5A; gTasks[var].data[5] = 0; gTasks[var].data[3] = 0; gTasks[var].data[4] = 0; for (id = 0; id < MAX_LINK_PLAYERS; id++) { RecordedBattle_SetFrontierPassFlagFromHword(gBlockRecvBuffer[id][1]); switch (gLinkPlayers[id].id) { case 0: gTasks[var].data[3] |= gBlockRecvBuffer[id][1] & 0x3F; break; case 1: gTasks[var].data[4] |= gBlockRecvBuffer[id][1] & 0x3F; break; case 2: gTasks[var].data[3] |= (gBlockRecvBuffer[id][1] & 0x3F) << 6; break; case 3: gTasks[var].data[4] |= (gBlockRecvBuffer[id][1] & 0x3F) << 6; break; } } ZeroEnemyPartyMons(); gBattleCommunication[MULTIUSE_STATE]++; } else break; // fall through case 3: if (IsLinkTaskFinished()) { SendBlock(BitmaskAllOtherLinkPlayers(), gPlayerParty, 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 + MULTI_PARTY_SIZE, 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 + MULTI_PARTY_SIZE, 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 + MULTI_PARTY_SIZE, gBlockRecvBuffer[id], sizeof(struct Pokemon) * 2); break; } } } } gBattleCommunication[MULTIUSE_STATE]++; } break; case 5: if (IsLinkTaskFinished()) { SendBlock(BitmaskAllOtherLinkPlayers(), gPlayerParty + 2, sizeof(struct Pokemon)); gBattleCommunication[MULTIUSE_STATE]++; } break; case 6: 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 7: InitBattleControllers(); RecordedBattle_SetTrainerInfo(); gBattleCommunication[SPRITES_INIT_STATE1] = 0; gBattleCommunication[SPRITES_INIT_STATE2] = 0; if (gBattleTypeFlags & BATTLE_TYPE_LINK) { for (id = 0; id < MAX_LINK_PLAYERS && (gLinkPlayers[id].version & 0xFF) == VERSION_EMERALD; id++); if (id == MAX_LINK_PLAYERS) gBattleCommunication[MULTIUSE_STATE] = 8; else gBattleCommunication[MULTIUSE_STATE] = 10; } else { gBattleCommunication[MULTIUSE_STATE] = 10; } break; case 8: if (IsLinkTaskFinished()) { u32 *ptr = gBattleStruct->multiBuffer.battleVideo; ptr[0] = gBattleTypeFlags; ptr[1] = gRecordedBattleRngSeed; // UB: overwrites berry data SendBlock(BitmaskAllOtherLinkPlayers(), ptr, sizeof(gBattleStruct->multiBuffer.battleVideo)); gBattleCommunication[MULTIUSE_STATE]++; } break; case 9: if ((GetBlockReceivedStatus() & 0xF) == 0xF) { ResetBlockReceivedFlags(); for (var = 0; var < 4; var++) { u32 blockValue = gBlockRecvBuffer[var][0]; if (blockValue & 4) { memcpy(&gRecordedBattleRngSeed, &gBlockRecvBuffer[var][2], sizeof(gRecordedBattleRngSeed)); break; } } gBattleCommunication[MULTIUSE_STATE]++; } break; case 10: if (BattleInitAllSprites(&gBattleCommunication[SPRITES_INIT_STATE1], &gBattleCommunication[SPRITES_INIT_STATE2])) { gPreBattleCallback1 = gMain.callback1; gMain.callback1 = BattleMainCB1; SetMainCallback2(BattleMainCB2); if (gBattleTypeFlags & BATTLE_TYPE_LINK) { gTrainerBattleOpponent_A = TRAINER_LINK_OPPONENT; gBattleTypeFlags |= BATTLE_TYPE_LINK_IN_BATTLE; } } break; } } void BattleMainCB2(void) { AnimateSprites(); BuildOamBuffer(); RunTextPrinters(); UpdatePaletteFade(); RunTasks(); if (JOY_HELD(B_BUTTON) && gBattleTypeFlags & BATTLE_TYPE_RECORDED && RecordedBattle_CanStopPlayback()) { // Player pressed B during recorded battle playback, end battle gSpecialVar_Result = gBattleOutcome = B_OUTCOME_PLAYER_TELEPORTED; ResetPaletteFadeControl(); BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, RGB_BLACK); SetMainCallback2(CB2_QuitRecordedBattle); } } static void FreeRestoreBattleData(void) { gMain.callback1 = gPreBattleCallback1; gScanlineEffect.state = 3; gMain.inBattle = FALSE; ZeroEnemyPartyMons(); m4aSongNumStop(SE_LOW_HEALTH); FreeMonSpritesGfx(); FreeBattleSpritesData(); FreeBattleResources(); } void CB2_QuitRecordedBattle(void) { UpdatePaletteFade(); if (!gPaletteFade.active) { m4aMPlayStop(&gMPlayInfo_SE1); m4aMPlayStop(&gMPlayInfo_SE2); FreeRestoreBattleData(); FreeAllWindowBuffers(); SetMainCallback2(gMain.savedCallback); } } #define sState data[0] #define sDelay data[4] static void SpriteCB_UnusedBattleInit(struct Sprite *sprite) { sprite->sState = 0; sprite->callback = SpriteCB_UnusedBattleInit_Main; } static void SpriteCB_UnusedBattleInit_Main(struct Sprite *sprite) { u16 *arr = (u16 *)gDecompressionBuffer; switch (sprite->sState) { case 0: sprite->sState++; sprite->data[1] = 0; sprite->data[2] = 0x281; sprite->data[3] = 0; sprite->sDelay = 1; // fall through case 1: sprite->sDelay--; if (sprite->sDelay == 0) { s32 i; s32 r2; s32 r0; sprite->sDelay = 2; r2 = sprite->data[1] + sprite->data[3] * 32; r0 = sprite->data[2] - sprite->data[3] * 32; for (i = 0; i < 29; i += 2) { arr[r2 + i] = 0x3D; arr[r0 + i] = 0x3D; } sprite->data[3]++; if (sprite->data[3] == 21) { sprite->sState++; sprite->data[1] = 32; } } break; case 2: sprite->data[1]--; if (sprite->data[1] == 20) SetMainCallback2(CB2_InitBattle); break; } } static u8 CreateNPCTrainerParty(struct Pokemon *party, u16 trainerNum, bool8 firstTrainer) { u32 nameHash = 0; u32 personalityValue; u8 fixedIV; s32 i, j; u8 monsCount; if (trainerNum == TRAINER_SECRET_BASE) return 0; if (gBattleTypeFlags & BATTLE_TYPE_TRAINER && !(gBattleTypeFlags & (BATTLE_TYPE_FRONTIER | BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_TRAINER_HILL))) { if (firstTrainer == TRUE) ZeroEnemyPartyMons(); if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) { if (gTrainers[trainerNum].partySize > PARTY_SIZE / 2) monsCount = PARTY_SIZE / 2; else monsCount = gTrainers[trainerNum].partySize; } else { monsCount = gTrainers[trainerNum].partySize; } for (i = 0; i < monsCount; i++) { if (gTrainers[trainerNum].doubleBattle == TRUE) personalityValue = 0x80; else if (gTrainers[trainerNum].encounterMusic_gender & F_TRAINER_FEMALE) personalityValue = 0x78; // Use personality more likely to result in a female Pokémon else personalityValue = 0x88; // Use personality more likely to result in a male Pokémon 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 * MAX_PER_STAT_IVS / 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 * MAX_PER_STAT_IVS / 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 * MAX_PER_STAT_IVS / 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 * MAX_PER_STAT_IVS / 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; } void VBlankCB_Battle(void) { // Change gRngSeed every vblank unless the battle could be recorded. if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_FRONTIER | BATTLE_TYPE_RECORDED))) 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 SpriteCB_VsLetterDummy(struct Sprite *sprite) { } static void SpriteCB_VsLetter(struct Sprite *sprite) { if (sprite->data[0] != 0) sprite->x = sprite->data[1] + ((sprite->data[2] & 0xFF00) >> 8); else sprite->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 SpriteCB_VsLetterInit(struct Sprite *sprite) { StartSpriteAffineAnim(sprite, 1); sprite->callback = SpriteCB_VsLetter; PlaySE(SE_MUGSHOT); } static void BufferPartyVsScreenHealth_AtEnd(u8 taskId) { struct Pokemon *party1 = NULL; struct Pokemon *party2 = NULL; u8 multiplayerId = gBattleScripting.multiplayerId; u32 flags; 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; } flags = 0; BUFFER_PARTY_VS_SCREEN_STATUS(party1, flags, i); gTasks[taskId].data[3] = flags; flags = 0; BUFFER_PARTY_VS_SCREEN_STATUS(party2, flags, i); gTasks[taskId].data[4] = flags; } void CB2_InitEndLinkBattle(void) { s32 i; u8 taskId; SetHBlankCallback(NULL); SetVBlankCallback(NULL); gBattleTypeFlags &= ~BATTLE_TYPE_LINK_IN_BATTLE; if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER) { SetMainCallback2(gMain.savedCallback); FreeBattleResources(); FreeBattleSpritesData(); FreeMonSpritesGfx(); } else { CpuFill32(0, (void *)(VRAM), VRAM_SIZE); SetGpuReg(REG_OFFSET_MOSAIC, 0); SetGpuReg(REG_OFFSET_WIN0H, DISPLAY_WIDTH); SetGpuReg(REG_OFFSET_WIN0V, WIN_RANGE(DISPLAY_HEIGHT / 2, DISPLAY_HEIGHT / 2 + 1)); SetGpuReg(REG_OFFSET_WININ, 0); SetGpuReg(REG_OFFSET_WINOUT, 0); gBattle_WIN0H = DISPLAY_WIDTH; gBattle_WIN0V = WIN_RANGE(DISPLAY_HEIGHT / 2, DISPLAY_HEIGHT / 2 + 1); ScanlineEffect_Clear(); i = 0; while (i < 80) { gScanlineEffectRegBuffers[0][i] = 0xF0; gScanlineEffectRegBuffers[1][i] = 0xF0; i++; } while (i < 160) { gScanlineEffectRegBuffers[0][i] = 0xFF10; gScanlineEffectRegBuffers[1][i] = 0xFF10; i++; } 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, WINOUT_WIN01_BG0 | WINOUT_WIN01_BG1 | WINOUT_WIN01_BG2 | WINOUT_WIN01_OBJ | WINOUT_WIN01_CLR); FreeAllSpritePalettes(); gReservedSpritePaletteCount = 4; SetVBlankCallback(VBlankCB_Battle); // Show end Vs screen with battle results taskId = CreateTask(InitLinkBattleVsScreen, 0); gTasks[taskId].data[1] = 0x10E; gTasks[taskId].data[2] = 0x5A; gTasks[taskId].data[5] = 1; BufferPartyVsScreenHealth_AtEnd(taskId); SetMainCallback2(CB2_EndLinkBattle); gBattleCommunication[MULTIUSE_STATE] = 0; } } static void CB2_EndLinkBattle(void) { EndLinkBattleInSteps(); AnimateSprites(); BuildOamBuffer(); RunTextPrinters(); UpdatePaletteFade(); RunTasks(); } static void EndLinkBattleInSteps(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(PALETTES_ALL, 0, 0, 16, RGB_BLACK); gBattleCommunication[MULTIUSE_STATE]++; } break; case 2: if (!gPaletteFade.active) { u8 battlerCount; gMain.anyLinkBattlerHasFrontierPass = RecordedBattle_GetFrontierPassFlag(); if (gBattleTypeFlags & BATTLE_TYPE_MULTI) battlerCount = 4; else battlerCount = 2; for (i = 0; i < battlerCount && (gLinkPlayers[i].version & 0xFF) == VERSION_EMERALD; i++); if (!gSaveBlock2Ptr->frontier.disableRecordBattle && i == battlerCount) { if (FlagGet(FLAG_SYS_FRONTIER_PASS)) { // Ask player if they want to record the battle FreeAllWindowBuffers(); SetMainCallback2(CB2_InitAskRecordBattle); } else if (!gMain.anyLinkBattlerHasFrontierPass) { // No players can record this battle, end SetMainCallback2(gMain.savedCallback); FreeBattleResources(); FreeBattleSpritesData(); FreeMonSpritesGfx(); } else if (gReceivedRemoteLinkPlayers == 0) { // Player can't record battle but // another player can, reconnect with them CreateTask(Task_ReconnectWithLinkPlayers, 5); gBattleCommunication[MULTIUSE_STATE]++; } else { gBattleCommunication[MULTIUSE_STATE]++; } } else { SetMainCallback2(gMain.savedCallback); FreeBattleResources(); FreeBattleSpritesData(); FreeMonSpritesGfx(); } } break; case 3: CpuFill32(0, (void *)VRAM, VRAM_SIZE); for (i = 0; i < 2; i++) LoadChosenBattleElement(i); BeginNormalPaletteFade(PALETTES_ALL, 0, 16, 0, RGB_BLACK); gBattleCommunication[MULTIUSE_STATE]++; break; case 4: if (!gPaletteFade.active) gBattleCommunication[MULTIUSE_STATE]++; break; case 5: if (!FuncIsActiveTask(Task_ReconnectWithLinkPlayers)) gBattleCommunication[MULTIUSE_STATE]++; break; case 6: if (IsLinkTaskFinished() == TRUE) { SetLinkStandbyCallback(); BattlePutTextOnWindow(gText_LinkStandby3, B_WIN_MSG); gBattleCommunication[MULTIUSE_STATE]++; } break; case 7: if (!IsTextPrinterActive(B_WIN_MSG)) { if (IsLinkTaskFinished() == TRUE) gBattleCommunication[MULTIUSE_STATE]++; } break; case 8: if (!gWirelessCommType) SetCloseLinkCallback(); gBattleCommunication[MULTIUSE_STATE]++; break; case 9: if (!gMain.anyLinkBattlerHasFrontierPass || gWirelessCommType || gReceivedRemoteLinkPlayers != 1) { gMain.anyLinkBattlerHasFrontierPass = FALSE; SetMainCallback2(gMain.savedCallback); FreeBattleResources(); FreeBattleSpritesData(); FreeMonSpritesGfx(); } break; } } u32 GetBattleBgTemplateData(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: // Only this case is used ret = gBattleBgTemplates[arrayId].priority; break; case 6: ret = gBattleBgTemplates[arrayId].baseTile; break; } return ret; } static void CB2_InitAskRecordBattle(void) { s32 i; SetHBlankCallback(NULL); SetVBlankCallback(NULL); CpuFill32(0, (void *)(VRAM), VRAM_SIZE); 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(); SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_OBJ_ON | DISPCNT_OBJ_1D_MAP); LoadBattleMenuWindowGfx(); for (i = 0; i < 2; i++) LoadChosenBattleElement(i); ResetSpriteData(); ResetTasks(); FreeAllSpritePalettes(); gReservedSpritePaletteCount = 4; SetVBlankCallback(VBlankCB_Battle); SetMainCallback2(CB2_AskRecordBattle); BeginNormalPaletteFade(PALETTES_ALL, 0, 0x10, 0, RGB_BLACK); gBattleCommunication[MULTIUSE_STATE] = 0; } static void CB2_AskRecordBattle(void) { AskRecordBattle(); AnimateSprites(); BuildOamBuffer(); RunTextPrinters(); UpdatePaletteFade(); RunTasks(); } // States for AskRecordBattle #define STATE_INIT 0 #define STATE_LINK 1 #define STATE_WAIT_LINK 2 #define STATE_ASK_RECORD 3 #define STATE_PRINT_YES_NO 4 #define STATE_HANDLE_YES_NO 5 #define STATE_RECORD_NO 6 #define STATE_END_RECORD_NO 7 #define STATE_WAIT_END 8 #define STATE_END 9 #define STATE_RECORD_YES 10 #define STATE_RECORD_WAIT 11 #define STATE_END_RECORD_YES 12 static void AskRecordBattle(void) { switch (gBattleCommunication[MULTIUSE_STATE]) { case STATE_INIT: ShowBg(0); ShowBg(1); ShowBg(2); gBattleCommunication[MULTIUSE_STATE]++; break; case STATE_LINK: if (gMain.anyLinkBattlerHasFrontierPass && gReceivedRemoteLinkPlayers == 0) CreateTask(Task_ReconnectWithLinkPlayers, 5); gBattleCommunication[MULTIUSE_STATE]++; break; case STATE_WAIT_LINK: if (!FuncIsActiveTask(Task_ReconnectWithLinkPlayers)) gBattleCommunication[MULTIUSE_STATE]++; break; case STATE_ASK_RECORD: if (!gPaletteFade.active) { // "Would you like to record your battle on your FRONTIER PASS?" BattlePutTextOnWindow(gText_RecordBattleToPass, B_WIN_MSG); gBattleCommunication[MULTIUSE_STATE]++; } break; case STATE_PRINT_YES_NO: if (!IsTextPrinterActive(B_WIN_MSG)) { HandleBattleWindow(YESNOBOX_X_Y, 0); BattlePutTextOnWindow(gText_BattleYesNoChoice, B_WIN_YESNO); gBattleCommunication[CURSOR_POSITION] = 1; BattleCreateYesNoCursorAt(1); gBattleCommunication[MULTIUSE_STATE]++; } break; case STATE_HANDLE_YES_NO: if (JOY_NEW(DPAD_UP)) { if (gBattleCommunication[CURSOR_POSITION] != 0) { // Moved cursor onto Yes PlaySE(SE_SELECT); BattleDestroyYesNoCursorAt(gBattleCommunication[CURSOR_POSITION]); gBattleCommunication[CURSOR_POSITION] = 0; BattleCreateYesNoCursorAt(0); } } else if (JOY_NEW(DPAD_DOWN)) { if (gBattleCommunication[CURSOR_POSITION] == 0) { // Moved cursor onto No PlaySE(SE_SELECT); BattleDestroyYesNoCursorAt(gBattleCommunication[CURSOR_POSITION]); gBattleCommunication[CURSOR_POSITION] = 1; BattleCreateYesNoCursorAt(1); } } else if (JOY_NEW(A_BUTTON)) { PlaySE(SE_SELECT); if (gBattleCommunication[CURSOR_POSITION] == 0) { // Selected Yes HandleBattleWindow(YESNOBOX_X_Y, WINDOW_CLEAR); gBattleCommunication[1] = MoveRecordedBattleToSaveData(); gBattleCommunication[MULTIUSE_STATE] = STATE_RECORD_YES; } else { // Selected No gBattleCommunication[MULTIUSE_STATE]++; } } else if (JOY_NEW(B_BUTTON)) { PlaySE(SE_SELECT); gBattleCommunication[MULTIUSE_STATE]++; } break; case STATE_RECORD_NO: if (IsLinkTaskFinished() == TRUE) { HandleBattleWindow(YESNOBOX_X_Y, WINDOW_CLEAR); if (gMain.anyLinkBattlerHasFrontierPass) { // Other battlers may be recording, wait for them SetLinkStandbyCallback(); BattlePutTextOnWindow(gText_LinkStandby3, B_WIN_MSG); } gBattleCommunication[MULTIUSE_STATE]++; // STATE_END_RECORD_NO } break; case STATE_WAIT_END: if (--gBattleCommunication[1] == 0) { if (gMain.anyLinkBattlerHasFrontierPass && !gWirelessCommType) SetCloseLinkCallback(); gBattleCommunication[MULTIUSE_STATE]++; } break; case STATE_END: if (!gMain.anyLinkBattlerHasFrontierPass || gWirelessCommType || gReceivedRemoteLinkPlayers != 1) { gMain.anyLinkBattlerHasFrontierPass = FALSE; if (!gPaletteFade.active) { SetMainCallback2(gMain.savedCallback); FreeBattleResources(); FreeBattleSpritesData(); FreeMonSpritesGfx(); } } break; case STATE_RECORD_YES: if (gBattleCommunication[1] == 1) { PlaySE(SE_SAVE); BattleStringExpandPlaceholdersToDisplayedString(gText_BattleRecordedOnPass); BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_MSG); gBattleCommunication[1] = 128; // Delay gBattleCommunication[MULTIUSE_STATE]++; } else { BattleStringExpandPlaceholdersToDisplayedString(BattleFrontier_BattleTowerBattleRoom_Text_RecordCouldntBeSaved); BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_MSG); gBattleCommunication[1] = 128; // Delay gBattleCommunication[MULTIUSE_STATE]++; } break; case STATE_RECORD_WAIT: if (IsLinkTaskFinished() == TRUE && !IsTextPrinterActive(B_WIN_MSG) && --gBattleCommunication[1] == 0) { if (gMain.anyLinkBattlerHasFrontierPass) { SetLinkStandbyCallback(); BattlePutTextOnWindow(gText_LinkStandby3, B_WIN_MSG); } gBattleCommunication[MULTIUSE_STATE]++; } break; case STATE_END_RECORD_YES: case STATE_END_RECORD_NO: if (!IsTextPrinterActive(B_WIN_MSG)) { if (gMain.anyLinkBattlerHasFrontierPass) { if (IsLinkTaskFinished() == TRUE) { BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, RGB_BLACK); gBattleCommunication[1] = 32; // Delay gBattleCommunication[MULTIUSE_STATE] = STATE_WAIT_END; } } else { BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, RGB_BLACK); gBattleCommunication[1] = 32; // Delay gBattleCommunication[MULTIUSE_STATE] = STATE_WAIT_END; } } break; } } 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); } } u32 GetBattleWindowTemplatePixelWidth(u32 windowsType, u32 tableId) { return gBattleWindowTemplates[windowsType][tableId].width * 8; } #define sBattler data[0] #define sSpeciesId data[2] void SpriteCB_WildMon(struct Sprite *sprite) { sprite->callback = SpriteCB_MoveWildMonToRight; StartSpriteAnimIfDifferent(sprite, 0); if (WILD_DOUBLE_BATTLE) BeginNormalPaletteFade((0x10000 << sprite->sBattler) | (0x10000 << BATTLE_PARTNER(sprite->sBattler)), 0, 10, 10, RGB(8, 8, 8)); else BeginNormalPaletteFade((0x10000 << sprite->sBattler), 0, 10, 10, RGB(8, 8, 8)); } static void SpriteCB_MoveWildMonToRight(struct Sprite *sprite) { if ((gIntroSlideFlags & 1) == 0) { sprite->x2 += 2; if (sprite->x2 == 0) { sprite->callback = SpriteCB_WildMonShowHealthbox; } } } static void SpriteCB_WildMonShowHealthbox(struct Sprite *sprite) { if (sprite->animEnded) { StartHealthboxSlideIn(sprite->sBattler); SetHealthboxSpriteVisible(gHealthboxSpriteIds[sprite->sBattler]); sprite->callback = SpriteCB_WildMonAnimate; StartSpriteAnimIfDifferent(sprite, 0); if (WILD_DOUBLE_BATTLE) BeginNormalPaletteFade((0x10000 << sprite->sBattler) | (0x10000 << BATTLE_PARTNER(sprite->sBattler)), 0, 10, 0, RGB(8, 8, 8)); else BeginNormalPaletteFade((0x10000 << sprite->sBattler), 0, 10, 0, RGB(8, 8, 8)); } } static void SpriteCB_WildMonAnimate(struct Sprite *sprite) { if (!gPaletteFade.active) { BattleAnimateFrontSprite(sprite, sprite->sSpeciesId, FALSE, 1); } } void SpriteCallbackDummy_2(struct Sprite *sprite) { } #define sNumFlickers data[3] #define sDelay data[4] static void SpriteCB_Flicker(struct Sprite *sprite) { sprite->sDelay--; if (sprite->sDelay == 0) { sprite->sDelay = 8; sprite->invisible ^= 1; sprite->sNumFlickers--; if (sprite->sNumFlickers == 0) { sprite->invisible = FALSE; sprite->callback = SpriteCallbackDummy_2; // sFlickerArray[0] = 0; } } } #undef sNumFlickers #undef sDelay extern const struct MonCoords gMonFrontPicCoords[]; extern const struct MonCoords gCastformFrontSpriteCoords[]; void SpriteCB_FaintOpponentMon(struct Sprite *sprite) { u8 battler = sprite->sBattler; u32 personality = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battler]], MON_DATA_PERSONALITY); 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) { species = GetUnownSpeciesId(personality); yOffset = gMonFrontPicCoords[species].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->y2 += 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 = gMonSpritesGfxPtr->sprites.byte[GetBattlerPosition(sprite->sBattler)] + (gBattleMonForms[sprite->sBattler] << 11) + (sprite->data[3] << 8); for (i = 0; i < 0x100; i++) *(dst++) = 0; StartSpriteAnim(sprite, gBattleMonForms[sprite->sBattler]); } } } // Used when selecting a move, which can hit multiple targets, in double battles. void SpriteCB_ShowAsMoveTarget(struct Sprite *sprite) { sprite->data[3] = 8; sprite->data[4] = sprite->invisible; sprite->callback = SpriteCB_BlinkVisible; } static void SpriteCB_BlinkVisible(struct Sprite *sprite) { if (--sprite->data[3] == 0) { sprite->invisible ^= 1; sprite->data[3] = 8; } } void SpriteCB_HideAsMoveTarget(struct Sprite *sprite) { sprite->invisible = sprite->data[4]; sprite->data[4] = FALSE; sprite->callback = SpriteCallbackDummy_2; } void SpriteCB_OpponentMonFromBall(struct Sprite *sprite) { if (sprite->affineAnimEnded) { if (!(gHitMarker & HITMARKER_NO_ANIMATIONS) || gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK)) { if (HasTwoFramesAnimation(sprite->sSpeciesId)) StartSpriteAnim(sprite, 1); } BattleAnimateFrontSprite(sprite, sprite->sSpeciesId, TRUE, 1); } } // This callback is frequently overwritten by SpriteCB_TrainerSlideIn void SpriteCB_BattleSpriteStartSlideLeft(struct Sprite *sprite) { sprite->callback = SpriteCB_BattleSpriteSlideLeft; } static void SpriteCB_BattleSpriteSlideLeft(struct Sprite *sprite) { if (!(gIntroSlideFlags & 1)) { sprite->x2 -= 2; if (sprite->x2 == 0) { sprite->callback = SpriteCB_Idle; sprite->data[1] = 0; } } } // Unused static void SetIdleSpriteCallback(struct Sprite *sprite) { sprite->callback = SpriteCB_Idle; } static void SpriteCB_Idle(struct Sprite *sprite) { } #define sSpeedX data[1] #define sSpeedY data[2] void SpriteCB_FaintSlideAnim(struct Sprite *sprite) { if (!(gIntroSlideFlags & 1)) { sprite->x2 += sprite->sSpeedX; sprite->y2 += sprite->sSpeedY; } } #undef sSpeedX #undef sSpeedY #define sSinIndex data[3] #define sDelta data[4] #define sAmplitude data[5] #define sBouncerSpriteId data[6] #define sWhich data[7] 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[invisibleSpriteId].sBattler = battler; gSprites[bouncerSpriteId].x2 = 0; gSprites[bouncerSpriteId].y2 = 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].x2 = 0; gSprites[bouncerSpriteId].y2 = 0; } static void SpriteCB_BounceEffect(struct Sprite *sprite) { u8 bouncerSpriteId = sprite->sBouncerSpriteId; s32 index = sprite->sSinIndex; s32 y = Sin(index, sprite->sAmplitude) + sprite->sAmplitude; gSprites[bouncerSpriteId].y2 = y; sprite->sSinIndex = (sprite->sSinIndex + sprite->sDelta) & 0xFF; bouncerSpriteId = GetMegaIndicatorSpriteId(sprite->sBouncerSpriteId); if (sprite->sWhich == BOUNCE_HEALTHBOX && bouncerSpriteId != 0xFF) gSprites[bouncerSpriteId].y2 = y; } #undef sSinIndex #undef sDelta #undef sAmplitude #undef sBouncerSpriteId #undef sWhich void SpriteCB_PlayerMonFromBall(struct Sprite *sprite) { if (sprite->affineAnimEnded) BattleAnimateBackSprite(sprite, sprite->sSpeciesId); } static void SpriteCB_TrainerThrowObject_Main(struct Sprite *sprite) { AnimSetCenterToCornerVecX(sprite); if (sprite->animEnded) sprite->callback = SpriteCB_Idle; } // Sprite callback for a trainer back pic to throw an object // (Wally throwing a ball, throwing Pokéblocks/balls in the Safari Zone) void SpriteCB_TrainerThrowObject(struct Sprite *sprite) { StartSpriteAnim(sprite, 1); sprite->callback = SpriteCB_TrainerThrowObject_Main; } void AnimSetCenterToCornerVecX(struct Sprite *sprite) { if (sprite->animDelayCounter == 0) sprite->centerToCornerVecX = sCenterToCornerVecXs[sprite->animCmdIndex]; } void BeginBattleIntroDummy(void) { } void BeginBattleIntro(void) { BattleStartClearSetData(); gBattleCommunication[1] = 0; gBattleStruct->introState = 0; gBattleMainFunc = DoBattleIntro; } static void BattleMainCB1(void) { gBattleMainFunc(); for (gActiveBattler = 0; gActiveBattler < gBattlersCount; gActiveBattler++) gBattlerControllerFuncs[gActiveBattler](); } static void BattleStartClearSetData(void) { s32 i; TurnValuesCleanUp(FALSE); SpecialStatusesClear(); memset(&gDisableStructs, 0, sizeof(gDisableStructs)); memset(&gFieldTimers, 0, sizeof(gFieldTimers)); memset(&gSideStatuses, 0, sizeof(gSideStatuses)); memset(&gSideTimers, 0, sizeof(gSideTimers)); memset(&gWishFutureKnock, 0, sizeof(gWishFutureKnock)); memset(&gBattleResults, 0, sizeof(gBattleResults)); for (i = 0; i < MAX_BATTLERS_COUNT; i++) { gStatuses3[i] = 0; gStatuses4[i] = 0; gDisableStructs[i].isFirstTurn = 2; 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; gPalaceSelectionBattleScripts[i] = 0; gBattleStruct->lastTakenMove[i] = MOVE_NONE; gBattleStruct->choicedMove[i] = MOVE_NONE; gBattleStruct->changedItems[i] = 0; gBattleStruct->lastTakenMoveFrom[i][0] = MOVE_NONE; gBattleStruct->lastTakenMoveFrom[i][1] = MOVE_NONE; gBattleStruct->lastTakenMoveFrom[i][2] = MOVE_NONE; gBattleStruct->lastTakenMoveFrom[i][3] = MOVE_NONE; gBattleStruct->AI_monToSwitchIntoId[i] = PARTY_SIZE; gBattleStruct->skyDropTargets[i] = 0xFF; } gLastUsedMove = 0; gFieldStatuses = 0; gHasFetchedBall = FALSE; gLastUsedBall = 0; gBattlerAttacker = 0; gBattlerTarget = 0; gBattleWeather = 0; gHitMarker = 0; if (!(gBattleTypeFlags & BATTLE_TYPE_RECORDED)) { if (!(gBattleTypeFlags & BATTLE_TYPE_LINK) && gSaveBlock2Ptr->optionsBattleSceneOff == TRUE) gHitMarker |= HITMARKER_NO_ANIMATIONS; } else if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK)) && GetBattleSceneInRecordedBattle()) { gHitMarker |= HITMARKER_NO_ANIMATIONS; } gBattleScripting.battleStyle = gSaveBlock2Ptr->optionsBattleStyle; gBattleScripting.expOnCatch = (B_EXP_CATCH >= GEN_6); gBattleScripting.monCaught = FALSE; 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 = 3; gBattleStruct->wildVictorySong = 0; gBattleStruct->moneyMultiplier = 1; gBattleStruct->givenExpMons = 0; gBattleStruct->palaceFlags = 0; gRandomTurnNumber = Random(); gBattleResults.shinyWildMon = IsMonShiny(&gEnemyParty[0]); gBattleStruct->arenaLostPlayerMons = 0; gBattleStruct->arenaLostOpponentMons = 0; gBattleStruct->mega.triggerSpriteId = 0xFF; gBattleStruct->stickyWebUser = 0xFF; gBattleStruct->appearedInBattle = 0; for (i = 0; i < PARTY_SIZE; i++) { gBattleStruct->usedHeldItems[i][0] = 0; gBattleStruct->usedHeldItems[i][1] = 0; gBattleStruct->itemStolen[i].originalItem = GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM); gPartyCriticalHits[i] = 0; } gBattleStruct->swapDamageCategory = FALSE; // Photon Geyser, Shell Side Arm, Light That Burns the Sky } void SwitchInClearSetData(void) { s32 i; struct DisableStruct disableStructCopy = gDisableStructs[gActiveBattler]; ClearIllusionMon(gActiveBattler); if (gBattleMoves[gCurrentMove].effect != EFFECT_BATON_PASS) { for (i = 0; i < NUM_BATTLE_STATS; i++) gBattleMons[gActiveBattler].statStages[i] = DEFAULT_STAT_STAGE; 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_GASTRO_ACID | STATUS3_EMBARGO | STATUS3_TELEKINESIS | STATUS3_MAGNET_RISE | STATUS3_HEAL_BLOCK | STATUS3_AQUA_RING | STATUS3_POWER_TRICK); gStatuses4[gActiveBattler] &= (STATUS4_MUD_SPORT | STATUS4_WATER_SPORT); 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] |= STATUS3_ALWAYS_HITS_TURN(2); } } if (gStatuses3[gActiveBattler] & STATUS3_POWER_TRICK) SWAP(gBattleMons[gActiveBattler].attack, gBattleMons[gActiveBattler].defense, i); } else { gBattleMons[gActiveBattler].status2 = 0; gStatuses3[gActiveBattler] = 0; gStatuses4[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; memset(&gDisableStructs[gActiveBattler], 0, sizeof(struct DisableStruct)); 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; gDisableStructs[gActiveBattler].truantSwitchInHack = disableStructCopy.truantSwitchInHack; 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] = 0; gBattleStruct->sameMoveTurns[gActiveBattler] = 0; gBattleStruct->lastTakenMoveFrom[gActiveBattler][0] = 0; gBattleStruct->lastTakenMoveFrom[gActiveBattler][1] = 0; gBattleStruct->lastTakenMoveFrom[gActiveBattler][2] = 0; gBattleStruct->lastTakenMoveFrom[gActiveBattler][3] = 0; gBattleStruct->lastMoveFailed &= ~(gBitTable[gActiveBattler]); gBattleStruct->palaceFlags &= ~(gBitTable[gActiveBattler]); if (gActiveBattler == gBattleStruct->stickyWebUser) gBattleStruct->stickyWebUser = 0xFF; // Switched into sticky web user slot so reset it for (i = 0; i < gBattlersCount; i++) { if (i != gActiveBattler && GetBattlerSide(i) != GetBattlerSide(gActiveBattler)) gBattleStruct->lastTakenMove[i] = MOVE_NONE; gBattleStruct->lastTakenMoveFrom[i][gActiveBattler] = 0; } gBattleStruct->choicedMove[gActiveBattler] = MOVE_NONE; gBattleResources->flags->flags[gActiveBattler] = 0; gCurrentMove = MOVE_NONE; gBattleStruct->arenaTurnCounter = 0xFF; // Reset damage to prevent things like red card activating if the switched-in mon is holding it gSpecialStatuses[gActiveBattler].physicalDmg = 0; gSpecialStatuses[gActiveBattler].specialDmg = 0; ClearBattlerMoveHistory(gActiveBattler); ClearBattlerAbilityHistory(gActiveBattler); ClearBattlerItemEffectHistory(gActiveBattler); } void FaintClearSetData(void) { s32 i; for (i = 0; i < NUM_BATTLE_STATS; i++) gBattleMons[gActiveBattler].statStages[i] = DEFAULT_STAT_STAGE; gBattleMons[gActiveBattler].status2 = 0; gStatuses3[gActiveBattler] = 0; gStatuses4[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; memset(&gDisableStructs[gActiveBattler], 0, sizeof(struct DisableStruct)); gProtectStructs[gActiveBattler].protected = FALSE; gProtectStructs[gActiveBattler].spikyShielded = FALSE; gProtectStructs[gActiveBattler].kingsShielded = FALSE; gProtectStructs[gActiveBattler].banefulBunkered = FALSE; gProtectStructs[gActiveBattler].quash = FALSE; gProtectStructs[gActiveBattler].obstructed = FALSE; gProtectStructs[gActiveBattler].endured = FALSE; gProtectStructs[gActiveBattler].noValidMoves = FALSE; gProtectStructs[gActiveBattler].helpingHand = FALSE; gProtectStructs[gActiveBattler].bounceMove = FALSE; gProtectStructs[gActiveBattler].stealMove = FALSE; gProtectStructs[gActiveBattler].prlzImmobility = FALSE; gProtectStructs[gActiveBattler].confusionSelfDmg = FALSE; gProtectStructs[gActiveBattler].targetAffected = FALSE; gProtectStructs[gActiveBattler].chargingTurn = FALSE; gProtectStructs[gActiveBattler].fleeType = 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; gProtectStructs[gActiveBattler].usedHealBlockedMove = FALSE; gProtectStructs[gActiveBattler].usesBouncedMove = FALSE; gProtectStructs[gActiveBattler].usedGravityPreventedMove = FALSE; gProtectStructs[gActiveBattler].usedThroatChopPreventedMove = FALSE; gProtectStructs[gActiveBattler].statRaised = FALSE; gProtectStructs[gActiveBattler].statFell = FALSE; gProtectStructs[gActiveBattler].pranksterElevated = FALSE; 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->choicedMove[gActiveBattler] = MOVE_NONE; gBattleStruct->sameMoveTurns[gActiveBattler] = 0; gBattleStruct->lastTakenMove[gActiveBattler] = MOVE_NONE; gBattleStruct->lastTakenMoveFrom[gActiveBattler][0] = 0; gBattleStruct->lastTakenMoveFrom[gActiveBattler][1] = 0; gBattleStruct->lastTakenMoveFrom[gActiveBattler][2] = 0; gBattleStruct->lastTakenMoveFrom[gActiveBattler][3] = 0; gBattleStruct->palaceFlags &= ~(gBitTable[gActiveBattler]); if (gActiveBattler == gBattleStruct->stickyWebUser) gBattleStruct->stickyWebUser = 0xFF; // User of sticky web fainted, so reset the stored battler ID for (i = 0; i < gBattlersCount; i++) { if (i != gActiveBattler && GetBattlerSide(i) != GetBattlerSide(gActiveBattler)) gBattleStruct->lastTakenMove[i] = MOVE_NONE; gBattleStruct->lastTakenMoveFrom[i][gActiveBattler] = 0; } gBattleResources->flags->flags[gActiveBattler] = 0; gBattleMons[gActiveBattler].type1 = gBaseStats[gBattleMons[gActiveBattler].species].type1; gBattleMons[gActiveBattler].type2 = gBaseStats[gBattleMons[gActiveBattler].species].type2; gBattleMons[gActiveBattler].type3 = TYPE_MYSTERY; ClearBattlerMoveHistory(gActiveBattler); ClearBattlerAbilityHistory(gActiveBattler); ClearBattlerItemEffectHistory(gActiveBattler); UndoFormChange(gBattlerPartyIndexes[gActiveBattler], GET_BATTLER_SIDE(gActiveBattler), FALSE); if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER) UndoMegaEvolution(gBattlerPartyIndexes[gActiveBattler]); // If the fainted mon was involved in a Sky Drop if (gBattleStruct->skyDropTargets[gActiveBattler] != 0xFF) { // Get battler id of the other Pokemon involved in this Sky Drop u8 otherSkyDropper = gBattleStruct->skyDropTargets[gActiveBattler]; // Clear Sky Drop data gBattleStruct->skyDropTargets[gActiveBattler] = 0xFF; gBattleStruct->skyDropTargets[otherSkyDropper] = 0xFF; // If the other Pokemon involved in this Sky Drop was the target, not the attacker if (gStatuses3[otherSkyDropper] & STATUS3_SKY_DROPPED) { // Release the target and take them out of the semi-invulnerable state gStatuses3[otherSkyDropper] &= ~(STATUS3_SKY_DROPPED | STATUS3_ON_AIR); // Make the target's sprite visible gSprites[gBattlerSpriteIds[otherSkyDropper]].invisible = FALSE; // If the target was sky dropped in the middle of using Outrage/Petal Dance/Thrash, // confuse them upon release and print "confused via fatigue" message and animation. if (gBattleMons[otherSkyDropper].status2 & STATUS2_LOCK_CONFUSE) { gBattleMons[otherSkyDropper].status2 &= ~(STATUS2_LOCK_CONFUSE); // If the released mon can be confused, do so. // Don't use CanBeConfused here, since it can cause issues in edge cases. if (!(GetBattlerAbility(otherSkyDropper) == ABILITY_OWN_TEMPO || gBattleMons[otherSkyDropper].status2 & STATUS2_CONFUSION || IsBattlerTerrainAffected(otherSkyDropper, STATUS_FIELD_MISTY_TERRAIN))) { gBattleMons[otherSkyDropper].status2 |= STATUS2_CONFUSION_TURN(((Random()) % 4) + 2); gBattlerAttacker = otherSkyDropper; gBattlescriptCurrInstr = BattleScript_ThrashConfuses - 2; } } } } } static void DoBattleIntro(void) { s32 i; u8 *state = &gBattleStruct->introState; switch (*state) { case 0: // Get Data of all battlers. gActiveBattler = gBattleCommunication[1]; BtlController_EmitGetMonData(BUFFER_A, REQUEST_ALL_BATTLE, 0); MarkBattlerForControllerExec(gActiveBattler); (*state)++; break; case 1: // Loop through all battlers. if (!gBattleControllerExecFlags) { if (++gBattleCommunication[1] == gBattlersCount) (*state)++; else *state = 0; } break; case 2: // Start graphical intro slide. if (!gBattleControllerExecFlags) { gActiveBattler = GetBattlerAtPosition(0); BtlController_EmitIntroSlide(BUFFER_A, gBattleTerrain); MarkBattlerForControllerExec(gActiveBattler); gBattleCommunication[0] = 0; gBattleCommunication[1] = 0; (*state)++; } break; case 3: // Wait for intro slide. if (!gBattleControllerExecFlags) (*state)++; break; case 4: // Copy battler data gotten in cases 0 and 1. Draw trainer/mon sprite. for (gActiveBattler = 0; gActiveBattler < gBattlersCount; gActiveBattler++) { if ((gBattleTypeFlags & BATTLE_TYPE_SAFARI) && GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER) { memset(&gBattleMons[gActiveBattler], 0, sizeof(struct BattlePokemon)); } else { memcpy(&gBattleMons[gActiveBattler], &gBattleResources->bufferB[gActiveBattler][4], sizeof(struct BattlePokemon)); gBattleMons[gActiveBattler].type1 = gBaseStats[gBattleMons[gActiveBattler].species].type1; gBattleMons[gActiveBattler].type2 = gBaseStats[gBattleMons[gActiveBattler].species].type2; gBattleMons[gActiveBattler].type3 = TYPE_MYSTERY; gBattleMons[gActiveBattler].ability = GetAbilityBySpecies(gBattleMons[gActiveBattler].species, gBattleMons[gActiveBattler].abilityNum); gBattleStruct->hpOnSwitchout[GetBattlerSide(gActiveBattler)] = gBattleMons[gActiveBattler].hp; gBattleMons[gActiveBattler].status2 = 0; for (i = 0; i < NUM_BATTLE_STATS; i++) gBattleMons[gActiveBattler].statStages[i] = DEFAULT_STAT_STAGE; } // Draw sprite. switch (GetBattlerPosition(gActiveBattler)) { case B_POSITION_PLAYER_LEFT: // player sprite BtlController_EmitDrawTrainerPic(BUFFER_A); MarkBattlerForControllerExec(gActiveBattler); break; case B_POSITION_OPPONENT_LEFT: if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) // opponent 1 sprite { BtlController_EmitDrawTrainerPic(BUFFER_A); MarkBattlerForControllerExec(gActiveBattler); } else // wild mon 1 { BtlController_EmitLoadMonSprite(BUFFER_A); MarkBattlerForControllerExec(gActiveBattler); gBattleResults.lastOpponentSpecies = GetMonData(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_SPECIES, NULL); } break; case B_POSITION_PLAYER_RIGHT: if (gBattleTypeFlags & (BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER)) // partner sprite { BtlController_EmitDrawTrainerPic(BUFFER_A); MarkBattlerForControllerExec(gActiveBattler); } break; case B_POSITION_OPPONENT_RIGHT: if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) { if (gBattleTypeFlags & (BATTLE_TYPE_MULTI | BATTLE_TYPE_TWO_OPPONENTS) && !BATTLE_TWO_VS_ONE_OPPONENT) // opponent 2 if exists { BtlController_EmitDrawTrainerPic(BUFFER_A); MarkBattlerForControllerExec(gActiveBattler); } } else if (IsBattlerAlive(gActiveBattler)) // wild mon 2 if alive { BtlController_EmitLoadMonSprite(BUFFER_A); MarkBattlerForControllerExec(gActiveBattler); gBattleResults.lastOpponentSpecies = GetMonData(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_SPECIES, NULL); } break; } if (gBattleTypeFlags & BATTLE_TYPE_ARENA) BattleArena_InitPoints(); } if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) { (*state)++; } else // Skip party summary since it is a wild battle. { #if B_FAST_INTRO == TRUE *state = 7; // Don't wait for sprite, print message at the same time. #else *state = 6; // Wait for sprite to load. #endif } break; case 5: // draw party summary in trainer battles if (!gBattleControllerExecFlags) { struct HpAndStatus hpStatus[PARTY_SIZE]; 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 = HP_EMPTY_SLOT; 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(BUFFER_A, hpStatus, PARTY_SUMM_SKIP_DRAW_DELAY); 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 = HP_EMPTY_SLOT; 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(BUFFER_A, hpStatus, PARTY_SUMM_SKIP_DRAW_DELAY); MarkBattlerForControllerExec(gActiveBattler); (*state)++; } break; case 6: // wait for previous action to complete if (!gBattleControllerExecFlags) (*state)++; break; case 7: // print battle intro message if (!IsBattlerMarkedForControllerExec(GetBattlerAtPosition(B_POSITION_PLAYER_LEFT))) { PrepareStringBattle(STRINGID_INTROMSG, GetBattlerAtPosition(B_POSITION_PLAYER_LEFT)); (*state)++; } break; case 8: // wait for intro message to be printed if (!IsBattlerMarkedForControllerExec(GetBattlerAtPosition(B_POSITION_PLAYER_LEFT))) { if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) { (*state)++; } else { #if B_FAST_INTRO == TRUE *state = 15; // Wait for text to be printed. #else *state = 14; // Wait for text and sprite. #endif } } break; case 9: // print opponent sends out if (gBattleTypeFlags & BATTLE_TYPE_RECORDED_LINK && !(gBattleTypeFlags & BATTLE_TYPE_RECORDED_IS_MASTER)) PrepareStringBattle(STRINGID_INTROSENDOUT, GetBattlerAtPosition(B_POSITION_PLAYER_LEFT)); else PrepareStringBattle(STRINGID_INTROSENDOUT, GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT)); (*state)++; break; case 10: // wait for opponent sends out text if (!gBattleControllerExecFlags) (*state)++; break; case 11: // first opponent's mon send out animation if (gBattleTypeFlags & BATTLE_TYPE_RECORDED_LINK && !(gBattleTypeFlags & BATTLE_TYPE_RECORDED_IS_MASTER)) gActiveBattler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); else gActiveBattler = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); BtlController_EmitIntroTrainerBallThrow(BUFFER_A); MarkBattlerForControllerExec(gActiveBattler); (*state)++; break; case 12: // nothing (*state)++; case 13: // second opponent's mon send out if (gBattleTypeFlags & (BATTLE_TYPE_MULTI | BATTLE_TYPE_TWO_OPPONENTS) && !BATTLE_TWO_VS_ONE_OPPONENT) { if (gBattleTypeFlags & BATTLE_TYPE_RECORDED_LINK && !(gBattleTypeFlags & BATTLE_TYPE_RECORDED_IS_MASTER)) gActiveBattler = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT); else gActiveBattler = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); BtlController_EmitIntroTrainerBallThrow(BUFFER_A); MarkBattlerForControllerExec(gActiveBattler); } #if B_FAST_INTRO == TRUE if (!(gBattleTypeFlags & (BATTLE_TYPE_RECORDED | BATTLE_TYPE_RECORDED_LINK | BATTLE_TYPE_RECORDED_IS_MASTER | BATTLE_TYPE_LINK))) *state = 15; // Print at the same time as trainer sends out second mon. else #endif (*state)++; break; case 14: // wait for opponent 2 send out if (!gBattleControllerExecFlags) (*state)++; break; case 15: // wait for wild battle message if (!IsBattlerMarkedForControllerExec(GetBattlerAtPosition(B_POSITION_PLAYER_LEFT))) (*state)++; break; case 16: // print player sends out if (!(gBattleTypeFlags & BATTLE_TYPE_SAFARI)) { if (gBattleTypeFlags & BATTLE_TYPE_RECORDED_LINK && !(gBattleTypeFlags & BATTLE_TYPE_RECORDED_IS_MASTER)) gActiveBattler = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); else gActiveBattler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); // A hack that makes fast intro work in trainer battles too. #if B_FAST_INTRO == TRUE if (gBattleTypeFlags & BATTLE_TYPE_TRAINER && !(gBattleTypeFlags & (BATTLE_TYPE_RECORDED | BATTLE_TYPE_RECORDED_LINK | BATTLE_TYPE_RECORDED_IS_MASTER | BATTLE_TYPE_LINK)) && gSprites[gHealthboxSpriteIds[gActiveBattler ^ BIT_SIDE]].callback == SpriteCallbackDummy) { return; } #endif PrepareStringBattle(STRINGID_INTROSENDOUT, gActiveBattler); } (*state)++; break; case 17: // wait for player send out message if (!(gBattleTypeFlags & BATTLE_TYPE_LINK && gBattleControllerExecFlags)) { if (gBattleTypeFlags & BATTLE_TYPE_RECORDED_LINK && !(gBattleTypeFlags & BATTLE_TYPE_RECORDED_IS_MASTER)) gActiveBattler = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); else gActiveBattler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); if (!IsBattlerMarkedForControllerExec(gActiveBattler)) (*state)++; } break; case 18: // player 1 send out if (gBattleTypeFlags & BATTLE_TYPE_RECORDED_LINK && !(gBattleTypeFlags & BATTLE_TYPE_RECORDED_IS_MASTER)) gActiveBattler = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); else gActiveBattler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); BtlController_EmitIntroTrainerBallThrow(BUFFER_A); MarkBattlerForControllerExec(gActiveBattler); (*state)++; break; case 19: // player 2 send out if (gBattleTypeFlags & (BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER)) { if (gBattleTypeFlags & BATTLE_TYPE_RECORDED_LINK && !(gBattleTypeFlags & BATTLE_TYPE_RECORDED_IS_MASTER)) gActiveBattler = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); else gActiveBattler = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT); BtlController_EmitIntroTrainerBallThrow(BUFFER_A); MarkBattlerForControllerExec(gActiveBattler); } (*state)++; break; case 20: // set dex and battle vars if (!gBattleControllerExecFlags) { for (gActiveBattler = 0; gActiveBattler < gBattlersCount; gActiveBattler++) { if (GetBattlerSide(gActiveBattler) == B_SIDE_OPPONENT && !(gBattleTypeFlags & (BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_FRONTIER | BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK | BATTLE_TYPE_TRAINER_HILL))) { HandleSetPokedexFlag(SpeciesToNationalPokedexNum(gBattleMons[gActiveBattler].species), FLAG_SET_SEEN, gBattleMons[gActiveBattler].personality); } } gBattleStruct->switchInAbilitiesCounter = 0; gBattleStruct->switchInItemsCounter = 0; gBattleStruct->overworldWeatherDone = FALSE; gBattleMainFunc = TryDoEventsBeforeFirstTurn; } break; } } static void TryDoEventsBeforeFirstTurn(void) { s32 i, j; if (gBattleControllerExecFlags) return; // Set invalid mons as absent(for example when starting a double battle with only one pokemon). if (!(gBattleTypeFlags & BATTLE_TYPE_SAFARI)) { for (i = 0; i < gBattlersCount; i++) { if (gBattleMons[i].hp == 0 || gBattleMons[i].species == SPECIES_NONE) gAbsentBattlerFlags |= gBitTable[i]; } } 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; } if (!gBattleStruct->terrainDone && AbilityBattleEffects(0, 0, 0, ABILITYEFFECT_SWITCH_IN_TERRAIN, 0) != 0) { gBattleStruct->terrainDone = TRUE; return; } // Totem boosts for (i = 0; i < gBattlersCount; i++) { if (gTotemBoosts[i].stats != 0) { gBattlerAttacker = i; BattleScriptExecute(BattleScript_TotemVar); return; } } memset(gTotemBoosts, 0, sizeof(gTotemBoosts)); // erase all totem boosts just to be safe // Primal Reversion for (i = 0; i < gBattlersCount; i++) { if (GetBattlerHoldEffect(i, TRUE) == HOLD_EFFECT_PRIMAL_ORB) { for (j = 0; j < EVOS_PER_MON; j++) { if (gEvolutionTable[gBattleMons[i].species][j].targetSpecies != SPECIES_NONE && gEvolutionTable[gBattleMons[i].species][j].method == EVO_PRIMAL_REVERSION) { gBattlerAttacker = i; BattleScriptExecute(BattleScript_PrimalReversion); return; } } } } // Check neutralizing gas if (AbilityBattleEffects(ABILITYEFFECT_NEUTRALIZINGGAS, 0, 0, 0, 0) != 0) return; // Check all switch in abilities happening from the fastest mon to slowest. while (gBattleStruct->switchInAbilitiesCounter < gBattlersCount) { gBattlerAttacker = gBattlerByTurnOrder[gBattleStruct->switchInAbilitiesCounter++]; if (AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, gBattlerAttacker, 0, 0, 0) != 0) return; } if (AbilityBattleEffects(ABILITYEFFECT_INTIMIDATE1, 0, 0, 0, 0) != 0) return; if (AbilityBattleEffects(ABILITYEFFECT_TRACE1, 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)) return; } for (i = 0; i < MAX_BATTLERS_COUNT; i++) { *(gBattleStruct->monToSwitchIntoId + i) = PARTY_SIZE; gChosenActionByBattler[i] = B_ACTION_NONE; gChosenMoveByBattler[i] = MOVE_NONE; // Record party slots of player's mons that appeared in battle if (!IsBattlerAIControlled(i)) gBattleStruct->appearedInBattle |= gBitTable[gBattlerPartyIndexes[i]]; } TurnValuesCleanUp(FALSE); SpecialStatusesClear(); *(&gBattleStruct->absentBattlerFlags) = gAbsentBattlerFlags; BattlePutTextOnWindow(gText_EmptyString3, B_WIN_MSG); 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.moveendState = 0; gBattleStruct->faintedActionsState = 0; gBattleStruct->turnCountersTracker = 0; gMoveResultFlags = 0; gRandomTurnNumber = Random(); GetAiLogicData(); // get assumed abilities, hold effects, etc of all battlers if (gBattleTypeFlags & BATTLE_TYPE_ARENA) { StopCryAndClearCrySongs(); BattleScriptExecute(BattleScript_ArenaTurnBeginning); } } static void HandleEndTurn_ContinueBattle(void) { s32 i; if (gBattleControllerExecFlags == 0) { 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_PASSIVE_DAMAGE; gBattleScripting.animTurn = 0; gBattleScripting.animTargetsHit = 0; gBattleScripting.moveendState = 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++; gBattleStruct->arenaTurnCounter++; } 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->absentBattlerFlags) = gAbsentBattlerFlags; BattlePutTextOnWindow(gText_EmptyString3, B_WIN_MSG); GetAiLogicData(); // get assumed abilities, hold effects, etc of all battlers gBattleMainFunc = HandleTurnActionSelectionState; gRandomTurnNumber = Random(); if (gBattleTypeFlags & BATTLE_TYPE_PALACE) BattleScriptExecute(BattleScript_PalacePrintFlavorText); else if (gBattleTypeFlags & BATTLE_TYPE_ARENA && gBattleStruct->arenaTurnCounter == 0) BattleScriptExecute(BattleScript_ArenaTurnBeginning); else if (ShouldDoTrainerSlide(GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT), gTrainerBattleOpponent_A, TRAINER_SLIDE_LAST_LOW_HP)) BattleScriptExecute(BattleScript_TrainerSlideMsgEnd2); } u8 IsRunningFromBattleImpossible(void) { u32 holdEffect, i; if (gBattleMons[gActiveBattler].item == ITEM_ENIGMA_BERRY_E_READER) holdEffect = gEnigmaBerries[gActiveBattler].holdEffect; else holdEffect = ItemId_GetHoldEffect(gBattleMons[gActiveBattler].item); gPotentialItemEffectBattler = gActiveBattler; if (gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE) // Cannot ever run from saving Birch's battle. { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_DONT_LEAVE_BIRCH; return BATTLE_RUN_FORBIDDEN; } if (GetBattlerPosition(gActiveBattler) == B_POSITION_PLAYER_RIGHT && WILD_DOUBLE_BATTLE && IsBattlerAlive(GetBattlerAtPosition(B_POSITION_PLAYER_LEFT))) // The second pokemon cannot run from a double wild battle, unless it's the only alive mon. { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_CANT_ESCAPE; return BATTLE_RUN_FORBIDDEN; } if (holdEffect == HOLD_EFFECT_CAN_ALWAYS_RUN) return BATTLE_RUN_SUCCESS; #if B_GHOSTS_ESCAPE >= GEN_6 if (IS_BATTLER_OF_TYPE(gActiveBattler, TYPE_GHOST)) return BATTLE_RUN_SUCCESS; #endif if (gBattleTypeFlags & BATTLE_TYPE_LINK) return BATTLE_RUN_SUCCESS; if (GetBattlerAbility(gActiveBattler) == ABILITY_RUN_AWAY) return BATTLE_RUN_SUCCESS; if ((i = IsAbilityPreventingEscape(gActiveBattler))) { gBattleScripting.battler = i - 1; gLastUsedAbility = gBattleMons[i - 1].ability; gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PREVENTS_ESCAPE; return BATTLE_RUN_FAILURE; } if (!CanBattlerEscape(gActiveBattler)) { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_CANT_ESCAPE; return BATTLE_RUN_FORBIDDEN; } return BATTLE_RUN_SUCCESS; } void SwitchPartyOrder(u8 battler) { s32 i; u8 partyId1; u8 partyId2; for (i = 0; i < (int)ARRAY_COUNT(gBattlePartyCurrentOrder); i++) gBattlePartyCurrentOrder[i] = *(battler * 3 + i + (u8 *)(gBattleStruct->battlerPartyOrders)); partyId1 = GetPartyIdFromBattlePartyId(gBattlerPartyIndexes[battler]); partyId2 = GetPartyIdFromBattlePartyId(*(gBattleStruct->monToSwitchIntoId + battler)); SwitchPartyMonSlots(partyId1, partyId2); if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) { for (i = 0; i < (int)ARRAY_COUNT(gBattlePartyCurrentOrder); i++) { *(battler * 3 + i + (u8 *)(gBattleStruct->battlerPartyOrders)) = gBattlePartyCurrentOrder[i]; *(BATTLE_PARTNER(battler) * 3 + i + (u8 *)(gBattleStruct->battlerPartyOrders)) = gBattlePartyCurrentOrder[i]; } } else { for (i = 0; i < (int)ARRAY_COUNT(gBattlePartyCurrentOrder); i++) { *(battler * 3 + i + (u8 *)(gBattleStruct->battlerPartyOrders)) = gBattlePartyCurrentOrder[i]; } } } enum { STATE_TURN_START_RECORD, 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, STATE_SELECTION_SCRIPT_MAY_RUN }; 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_TURN_START_RECORD: // Recorded battle related action on start of every turn. RecordedBattle_CopyBattlerMoves(); gBattleCommunication[gActiveBattler] = STATE_BEFORE_ACTION_CHOSEN; // Do AI score computations here so we can use them in AI_TrySwitchOrUseItem if ((gBattleTypeFlags & BATTLE_TYPE_HAS_AI || IsWildMonSmart()) && IsBattlerAIControlled(gActiveBattler)) { gBattleStruct->aiMoveOrAction[gActiveBattler] = ComputeBattleAiScores(gActiveBattler); } break; 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->absentBattlerFlags & gBitTable[GetBattlerAtPosition(BATTLE_PARTNER(position))] || gBattleCommunication[GetBattlerAtPosition(BATTLE_PARTNER(position))] == STATE_WAIT_ACTION_CONFIRMED) { if (gBattleStruct->absentBattlerFlags & 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 if (WILD_DOUBLE_BATTLE && position == B_POSITION_PLAYER_RIGHT && (gBattleStruct->throwingPokeBall || gChosenActionByBattler[GetBattlerAtPosition(B_POSITION_PLAYER_LEFT)] == B_ACTION_RUN)) { gBattleStruct->throwingPokeBall = FALSE; gChosenActionByBattler[gActiveBattler] = B_ACTION_NOTHING_FAINTED; // Not fainted, but it cannot move, because of the throwing ball. gBattleCommunication[gActiveBattler] = STATE_WAIT_ACTION_CONFIRMED_STANDBY; } else { BtlController_EmitChooseAction(BUFFER_A, gChosenActionByBattler[0], gBattleResources->bufferB[0][1] | (gBattleResources->bufferB[0][2] << 8)); MarkBattlerForControllerExec(gActiveBattler); gBattleCommunication[gActiveBattler]++; } } } break; case STATE_WAIT_ACTION_CHOSEN: // Try to perform an action. if (!(gBattleControllerExecFlags & ((gBitTable[gActiveBattler]) | (0xF << 28) | (gBitTable[gActiveBattler] << 4) | (gBitTable[gActiveBattler] << 8) | (gBitTable[gActiveBattler] << 12)))) { RecordedBattle_SetBattlerAction(gActiveBattler, gBattleResources->bufferB[gActiveBattler][1]); gChosenActionByBattler[gActiveBattler] = gBattleResources->bufferB[gActiveBattler][1]; switch (gBattleResources->bufferB[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) = gBattleResources->bufferB[gActiveBattler][3]; return; } else if (gDisableStructs[gActiveBattler].encoredMove != 0) { gChosenMoveByBattler[gActiveBattler] = gDisableStructs[gActiveBattler].encoredMove; *(gBattleStruct->chosenMovePositions + gActiveBattler) = gDisableStructs[gActiveBattler].encoredMovePos; gBattleCommunication[gActiveBattler] = STATE_WAIT_ACTION_CONFIRMED_STANDBY; return; } else { struct ChooseMoveStruct moveInfo; moveInfo.zmove = gBattleStruct->zmove; moveInfo.mega = gBattleStruct->mega; moveInfo.species = gBattleMons[gActiveBattler].species; moveInfo.monType1 = gBattleMons[gActiveBattler].type1; moveInfo.monType2 = gBattleMons[gActiveBattler].type2; moveInfo.monType3 = gBattleMons[gActiveBattler].type3; 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(BUFFER_A, (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) != 0, FALSE, &moveInfo); MarkBattlerForControllerExec(gActiveBattler); } break; case B_ACTION_USE_ITEM: if (FlagGet(B_FLAG_NO_BAG_USE)) { RecordedBattle_ClearBattlerAction(gActiveBattler, 1); gSelectionBattleScripts[gActiveBattler] = BattleScript_ActionSelectionItemsCantBeUsed; gBattleCommunication[gActiveBattler] = STATE_SELECTION_SCRIPT; *(gBattleStruct->selectionScriptFinished + gActiveBattler) = FALSE; *(gBattleStruct->stateIdAfterSelScript + gActiveBattler) = STATE_BEFORE_ACTION_CHOSEN; return; } if ((gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_FRONTIER_NO_PYRAMID | BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_RECORDED_LINK)) // Or if currently held by Sky Drop || gStatuses3[gActiveBattler] & STATUS3_SKY_DROPPED) { RecordedBattle_ClearBattlerAction(gActiveBattler, 1); gSelectionBattleScripts[gActiveBattler] = BattleScript_ActionSelectionItemsCantBeUsed; gBattleCommunication[gActiveBattler] = STATE_SELECTION_SCRIPT; *(gBattleStruct->selectionScriptFinished + gActiveBattler) = FALSE; *(gBattleStruct->stateIdAfterSelScript + gActiveBattler) = STATE_BEFORE_ACTION_CHOSEN; return; } else { BtlController_EmitChooseItem(BUFFER_A, gBattleStruct->battlerPartyOrders[gActiveBattler]); MarkBattlerForControllerExec(gActiveBattler); } break; case B_ACTION_SWITCH: *(gBattleStruct->battlerPartyIndexes + gActiveBattler) = gBattlerPartyIndexes[gActiveBattler]; if (gBattleTypeFlags & BATTLE_TYPE_ARENA || !CanBattlerEscape(gActiveBattler)) { BtlController_EmitChoosePokemon(BUFFER_A, PARTY_ACTION_CANT_SWITCH, PARTY_SIZE, ABILITY_NONE, gBattleStruct->battlerPartyOrders[gActiveBattler]); } else if (ItemId_GetHoldEffect(gBattleMons[gActiveBattler].item) != HOLD_EFFECT_SHED_SHELL && (i = IsAbilityPreventingEscape(gActiveBattler))) // must be last to keep i value integrity { BtlController_EmitChoosePokemon(BUFFER_A, ((i - 1) << 4) | PARTY_ACTION_ABILITY_PREVENTS, PARTY_SIZE, gBattleMons[i - 1].ability, gBattleStruct->battlerPartyOrders[gActiveBattler]); } else { if (gActiveBattler == 2 && gChosenActionByBattler[0] == B_ACTION_SWITCH) BtlController_EmitChoosePokemon(BUFFER_A, PARTY_ACTION_CHOOSE_MON, *(gBattleStruct->monToSwitchIntoId + 0), ABILITY_NONE, gBattleStruct->battlerPartyOrders[gActiveBattler]); else if (gActiveBattler == 3 && gChosenActionByBattler[1] == B_ACTION_SWITCH) BtlController_EmitChoosePokemon(BUFFER_A, PARTY_ACTION_CHOOSE_MON, *(gBattleStruct->monToSwitchIntoId + 1), ABILITY_NONE, gBattleStruct->battlerPartyOrders[gActiveBattler]); else BtlController_EmitChoosePokemon(BUFFER_A, PARTY_ACTION_CHOOSE_MON, PARTY_SIZE, ABILITY_NONE, gBattleStruct->battlerPartyOrders[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_SAFARI_POKEBLOCK: BtlController_EmitChooseItem(BUFFER_A, gBattleStruct->battlerPartyOrders[gActiveBattler]); MarkBattlerForControllerExec(gActiveBattler); break; case B_ACTION_CANCEL_PARTNER: gBattleCommunication[gActiveBattler] = STATE_WAIT_SET_BEFORE_ACTION; gBattleCommunication[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gActiveBattler)))] = STATE_BEFORE_ACTION_CHOSEN; RecordedBattle_ClearBattlerAction(gActiveBattler, 1); if (gBattleMons[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gActiveBattler)))].status2 & STATUS2_MULTIPLETURNS || gBattleMons[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gActiveBattler)))].status2 & STATUS2_RECHARGE) { BtlController_EmitEndBounceEffect(BUFFER_A); MarkBattlerForControllerExec(gActiveBattler); return; } else if (gChosenActionByBattler[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gActiveBattler)))] == B_ACTION_SWITCH) { RecordedBattle_ClearBattlerAction(GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gActiveBattler))), 2); } else if (gChosenActionByBattler[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gActiveBattler)))] == B_ACTION_RUN) { RecordedBattle_ClearBattlerAction(GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gActiveBattler))), 1); } else if (gChosenActionByBattler[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gActiveBattler)))] == B_ACTION_USE_MOVE && (gProtectStructs[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gActiveBattler)))].noValidMoves || gDisableStructs[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gActiveBattler)))].encoredMove)) { RecordedBattle_ClearBattlerAction(GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gActiveBattler))), 1); } else if (gBattleTypeFlags & BATTLE_TYPE_PALACE && gChosenActionByBattler[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gActiveBattler)))] == B_ACTION_USE_MOVE) { gRngValue = gBattlePalaceMoveSelectionRngValue; RecordedBattle_ClearBattlerAction(GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gActiveBattler))), 1); } else { RecordedBattle_ClearBattlerAction(GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(gActiveBattler))), 3); } gBattleStruct->mega.toEvolve &= ~(gBitTable[BATTLE_PARTNER(GetBattlerPosition(gActiveBattler))]); gBattleStruct->zmove.toBeUsed[BATTLE_PARTNER(GetBattlerPosition(gActiveBattler))] = MOVE_NONE; BtlController_EmitEndBounceEffect(BUFFER_A); MarkBattlerForControllerExec(gActiveBattler); return; case B_ACTION_DEBUG: BtlController_EmitDebugMenu(BUFFER_A); MarkBattlerForControllerExec(gActiveBattler); break; } if (gBattleTypeFlags & BATTLE_TYPE_TRAINER && gBattleTypeFlags & (BATTLE_TYPE_FRONTIER | BATTLE_TYPE_TRAINER_HILL) && gBattleResources->bufferB[gActiveBattler][1] == B_ACTION_RUN) { gSelectionBattleScripts[gActiveBattler] = BattleScript_AskIfWantsToForfeitMatch; gBattleCommunication[gActiveBattler] = STATE_SELECTION_SCRIPT_MAY_RUN; *(gBattleStruct->selectionScriptFinished + gActiveBattler) = FALSE; *(gBattleStruct->stateIdAfterSelScript + gActiveBattler) = STATE_BEFORE_ACTION_CHOSEN; return; } else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER && !(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK)) && gBattleResources->bufferB[gActiveBattler][1] == B_ACTION_RUN) { BattleScriptExecute(BattleScript_PrintCantRunFromTrainer); gBattleCommunication[gActiveBattler] = STATE_BEFORE_ACTION_CHOSEN; } else if (IsRunningFromBattleImpossible() != BATTLE_RUN_SUCCESS && gBattleResources->bufferB[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]) | (0xF << 28) | (gBitTable[gActiveBattler] << 4) | (gBitTable[gActiveBattler] << 8) | (gBitTable[gActiveBattler] << 12)))) { switch (gChosenActionByBattler[gActiveBattler]) { case B_ACTION_USE_MOVE: switch (gBattleResources->bufferB[gActiveBattler][1]) { case 3: case 4: case 5: case 6: case 7: case 8: case 9: gChosenActionByBattler[gActiveBattler] = gBattleResources->bufferB[gActiveBattler][1]; return; case 15: gChosenActionByBattler[gActiveBattler] = B_ACTION_SWITCH; UpdateBattlerPartyOrdersOnSwitch(); return; default: RecordedBattle_CheckMovesetChanges(B_RECORD_MODE_PLAYBACK); if ((gBattleResources->bufferB[gActiveBattler][2] | (gBattleResources->bufferB[gActiveBattler][3] << 8)) == 0xFFFF) { gBattleCommunication[gActiveBattler] = STATE_BEFORE_ACTION_CHOSEN; RecordedBattle_ClearBattlerAction(gActiveBattler, 1); } else if (TrySetCantSelectMoveBattleScript()) { RecordedBattle_ClearBattlerAction(gActiveBattler, 1); gBattleCommunication[gActiveBattler] = STATE_SELECTION_SCRIPT; *(gBattleStruct->selectionScriptFinished + gActiveBattler) = FALSE; gBattleResources->bufferB[gActiveBattler][1] = B_ACTION_USE_MOVE; *(gBattleStruct->stateIdAfterSelScript + gActiveBattler) = STATE_WAIT_ACTION_CHOSEN; return; } else { if (!(gBattleTypeFlags & BATTLE_TYPE_PALACE)) { RecordedBattle_SetBattlerAction(gActiveBattler, gBattleResources->bufferB[gActiveBattler][2]); RecordedBattle_SetBattlerAction(gActiveBattler, gBattleResources->bufferB[gActiveBattler][3]); } *(gBattleStruct->chosenMovePositions + gActiveBattler) = gBattleResources->bufferB[gActiveBattler][2] & ~RET_MEGA_EVOLUTION; gChosenMoveByBattler[gActiveBattler] = gBattleMons[gActiveBattler].moves[*(gBattleStruct->chosenMovePositions + gActiveBattler)]; *(gBattleStruct->moveTarget + gActiveBattler) = gBattleResources->bufferB[gActiveBattler][3]; if (gBattleResources->bufferB[gActiveBattler][2] & RET_MEGA_EVOLUTION) gBattleStruct->mega.toEvolve |= gBitTable[gActiveBattler]; gBattleCommunication[gActiveBattler]++; } break; } break; case B_ACTION_USE_ITEM: if ((gBattleResources->bufferB[gActiveBattler][1] | (gBattleResources->bufferB[gActiveBattler][2] << 8)) == 0) { gBattleCommunication[gActiveBattler] = STATE_BEFORE_ACTION_CHOSEN; } else { gLastUsedItem = (gBattleResources->bufferB[gActiveBattler][1] | (gBattleResources->bufferB[gActiveBattler][2] << 8)); if (ItemId_GetPocket(gLastUsedItem) == POCKET_POKE_BALLS) gBattleStruct->throwingPokeBall = TRUE; gBattleCommunication[gActiveBattler]++; } break; case B_ACTION_SWITCH: if (gBattleResources->bufferB[gActiveBattler][1] == PARTY_SIZE) { gBattleCommunication[gActiveBattler] = STATE_BEFORE_ACTION_CHOSEN; RecordedBattle_ClearBattlerAction(gActiveBattler, 1); } else { UpdateBattlerPartyOrdersOnSwitch(); 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_THROW_BALL: gBattleStruct->throwingPokeBall = TRUE; gBattleCommunication[gActiveBattler]++; break; case B_ACTION_SAFARI_POKEBLOCK: if ((gBattleResources->bufferB[gActiveBattler][1] | (gBattleResources->bufferB[gActiveBattler][2] << 8)) != 0) gBattleCommunication[gActiveBattler]++; else gBattleCommunication[gActiveBattler] = STATE_BEFORE_ACTION_CHOSEN; break; case B_ACTION_SAFARI_GO_NEAR: gBattleCommunication[gActiveBattler]++; break; case B_ACTION_SAFARI_RUN: gHitMarker |= HITMARKER_RUN; gBattleCommunication[gActiveBattler]++; break; case B_ACTION_WALLY_THROW: gBattleCommunication[gActiveBattler]++; break; case B_ACTION_DEBUG: gBattleCommunication[gActiveBattler] = STATE_BEFORE_ACTION_CHOSEN; break; } } break; case STATE_WAIT_ACTION_CONFIRMED_STANDBY: if (!(gBattleControllerExecFlags & ((gBitTable[gActiveBattler]) | (0xF << 28) | (gBitTable[gActiveBattler] << 4) | (gBitTable[gActiveBattler] << 8) | (gBitTable[gActiveBattler] << 12)))) { if (AllAtActionConfirmed()) i = TRUE; else i = FALSE; if (((gBattleTypeFlags & BATTLE_TYPE_MULTI) || !(gBattleTypeFlags & BATTLE_TYPE_DOUBLE)) || (position & BIT_FLANK) != B_FLANK_LEFT || (*(&gBattleStruct->absentBattlerFlags) & gBitTable[GetBattlerAtPosition(BATTLE_PARTNER(position))])) { BtlController_EmitLinkStandbyMsg(BUFFER_A, LINK_STANDBY_MSG_STOP_BOUNCE, i); } else { BtlController_EmitLinkStandbyMsg(BUFFER_A, LINK_STANDBY_STOP_BOUNCE_ONLY, i); } MarkBattlerForControllerExec(gActiveBattler); gBattleCommunication[gActiveBattler]++; } break; case STATE_WAIT_ACTION_CONFIRMED: if (!(gBattleControllerExecFlags & ((gBitTable[gActiveBattler]) | (0xF << 28) | (gBitTable[gActiveBattler] << 4) | (gBitTable[gActiveBattler] << 8) | (gBitTable[gActiveBattler] << 12)))) { 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]) | (0xF << 28) | (gBitTable[gActiveBattler] << 4) | (gBitTable[gActiveBattler] << 8) | (gBitTable[gActiveBattler] << 12)))) { gBattleScriptingCommandsTable[gBattlescriptCurrInstr[0]](); } gSelectionBattleScripts[gActiveBattler] = gBattlescriptCurrInstr; } break; case STATE_WAIT_SET_BEFORE_ACTION: if (!(gBattleControllerExecFlags & ((gBitTable[gActiveBattler]) | (0xF << 28) | (gBitTable[gActiveBattler] << 4) | (gBitTable[gActiveBattler] << 8) | (gBitTable[gActiveBattler] << 12)))) { gBattleCommunication[gActiveBattler] = STATE_BEFORE_ACTION_CHOSEN; } break; case STATE_SELECTION_SCRIPT_MAY_RUN: if (*(gBattleStruct->selectionScriptFinished + gActiveBattler)) { if (gBattleResources->bufferB[gActiveBattler][1] == B_ACTION_NOTHING_FAINTED) { gHitMarker |= HITMARKER_RUN; gChosenActionByBattler[gActiveBattler] = B_ACTION_RUN; gBattleCommunication[gActiveBattler] = STATE_WAIT_ACTION_CONFIRMED_STANDBY; } else { RecordedBattle_ClearBattlerAction(gActiveBattler, 1); gBattleCommunication[gActiveBattler] = *(gBattleStruct->stateIdAfterSelScript + gActiveBattler); } } else { gBattlerAttacker = gActiveBattler; gBattlescriptCurrInstr = gSelectionBattleScripts[gActiveBattler]; if (!(gBattleControllerExecFlags & ((gBitTable[gActiveBattler]) | (0xF << 28) | (gBitTable[gActiveBattler] << 4) | (gBitTable[gActiveBattler] << 8) | (gBitTable[gActiveBattler] << 12)))) { gBattleScriptingCommandsTable[gBattlescriptCurrInstr[0]](); } gSelectionBattleScripts[gActiveBattler] = gBattlescriptCurrInstr; } break; } } // Check if everyone chose actions. if (gBattleCommunication[ACTIONS_CONFIRMED_COUNT] == gBattlersCount) { RecordedBattle_CheckMovesetChanges(B_RECORD_MODE_RECORDING); if (WILD_DOUBLE_BATTLE && gBattleStruct->throwingPokeBall) { // if we choose to throw a ball with our second mon, skip the action of the first // (if we have chosen throw ball with first, second's is already skipped) gChosenActionByBattler[B_POSITION_PLAYER_LEFT] = B_ACTION_NOTHING_FAINTED; } gBattleMainFunc = SetActionsAndBattlersTurnOrder; if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER) { for (i = 0; i < gBattlersCount; i++) { if (gChosenActionByBattler[i] == B_ACTION_SWITCH) SwitchPartyOrderInGameMulti(i, *(gBattleStruct->monToSwitchIntoId + i)); } } } } static bool8 AllAtActionConfirmed(void) { s32 i, count; for (count = 0, i = 0; i < gBattlersCount; i++) { if (gBattleCommunication[i] == STATE_WAIT_ACTION_CONFIRMED) count++; } if (count + 1 == gBattlersCount) return TRUE; else return FALSE; } static void UpdateBattlerPartyOrdersOnSwitch(void) { *(gBattleStruct->monToSwitchIntoId + gActiveBattler) = gBattleResources->bufferB[gActiveBattler][1]; RecordedBattle_SetBattlerAction(gActiveBattler, gBattleResources->bufferB[gActiveBattler][1]); if (gBattleTypeFlags & BATTLE_TYPE_LINK && gBattleTypeFlags & BATTLE_TYPE_MULTI) { *(gActiveBattler * 3 + (u8 *)(gBattleStruct->battlerPartyOrders) + 0) &= 0xF; *(gActiveBattler * 3 + (u8 *)(gBattleStruct->battlerPartyOrders) + 0) |= (gBattleResources->bufferB[gActiveBattler][2] & 0xF0); *(gActiveBattler * 3 + (u8 *)(gBattleStruct->battlerPartyOrders) + 1) = gBattleResources->bufferB[gActiveBattler][3]; *((BATTLE_PARTNER(gActiveBattler)) * 3 + (u8 *)(gBattleStruct->battlerPartyOrders) + 0) &= (0xF0); *((BATTLE_PARTNER(gActiveBattler)) * 3 + (u8 *)(gBattleStruct->battlerPartyOrders) + 0) |= (gBattleResources->bufferB[gActiveBattler][2] & 0xF0) >> 4; *((BATTLE_PARTNER(gActiveBattler)) * 3 + (u8 *)(gBattleStruct->battlerPartyOrders) + 2) = gBattleResources->bufferB[gActiveBattler][3]; } } void SwapTurnOrder(u8 id1, u8 id2) { u32 temp; SWAP(gActionsByTurnOrder[id1], gActionsByTurnOrder[id2], temp); SWAP(gBattlerByTurnOrder[id1], gBattlerByTurnOrder[id2], temp); } u32 GetBattlerTotalSpeedStat(u8 battlerId) { u32 speed = gBattleMons[battlerId].speed; u32 ability = GetBattlerAbility(battlerId); u32 holdEffect = GetBattlerHoldEffect(battlerId, TRUE); // weather abilities if (WEATHER_HAS_EFFECT) { if (ability == ABILITY_SWIFT_SWIM && holdEffect != HOLD_EFFECT_UTILITY_UMBRELLA && gBattleWeather & B_WEATHER_RAIN) speed *= 2; else if (ability == ABILITY_CHLOROPHYLL && holdEffect != HOLD_EFFECT_UTILITY_UMBRELLA && gBattleWeather & B_WEATHER_SUN) speed *= 2; else if (ability == ABILITY_SAND_RUSH && gBattleWeather & B_WEATHER_SANDSTORM) speed *= 2; else if (ability == ABILITY_SLUSH_RUSH && gBattleWeather & B_WEATHER_HAIL) speed *= 2; } // other abilities if (ability == ABILITY_QUICK_FEET && gBattleMons[battlerId].status1 & STATUS1_ANY) speed = (speed * 150) / 100; else if (ability == ABILITY_SURGE_SURFER && gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN) speed *= 2; else if (ability == ABILITY_SLOW_START && gDisableStructs[battlerId].slowStartTimer != 0) speed /= 2; // stat stages speed *= gStatStageRatios[gBattleMons[battlerId].statStages[STAT_SPEED]][0]; speed /= gStatStageRatios[gBattleMons[battlerId].statStages[STAT_SPEED]][1]; // player's badge boost if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK | BATTLE_TYPE_FRONTIER)) && ShouldGetStatBadgeBoost(FLAG_BADGE03_GET, battlerId) && GetBattlerSide(battlerId) == B_SIDE_PLAYER) { speed = (speed * 110) / 100; } // item effects if (holdEffect == HOLD_EFFECT_MACHO_BRACE || holdEffect == HOLD_EFFECT_POWER_ITEM) speed /= 2; else if (holdEffect == HOLD_EFFECT_IRON_BALL) speed /= 2; else if (holdEffect == HOLD_EFFECT_CHOICE_SCARF) speed = (speed * 150) / 100; else if (holdEffect == HOLD_EFFECT_QUICK_POWDER && gBattleMons[battlerId].species == SPECIES_DITTO && !(gBattleMons[battlerId].status2 & STATUS2_TRANSFORMED)) speed *= 2; // various effects if (gSideStatuses[GET_BATTLER_SIDE(battlerId)] & SIDE_STATUS_TAILWIND) speed *= 2; if (gBattleResources->flags->flags[battlerId] & RESOURCE_FLAG_UNBURDEN) speed *= 2; // paralysis drop if (gBattleMons[battlerId].status1 & STATUS1_PARALYSIS && ability != ABILITY_QUICK_FEET) #if B_PARALYSIS_SPEED >= GEN_7 speed /= 2; #else speed /= 4; #endif return speed; } s8 GetChosenMovePriority(u32 battlerId) { u16 move; gProtectStructs[battlerId].pranksterElevated = 0; if (gProtectStructs[battlerId].noValidMoves) move = MOVE_STRUGGLE; else move = gBattleMons[battlerId].moves[*(gBattleStruct->chosenMovePositions + battlerId)]; return GetMovePriority(battlerId, move); } s8 GetMovePriority(u32 battlerId, u16 move) { s8 priority; u16 ability = GetBattlerAbility(battlerId); priority = gBattleMoves[move].priority; if (ability == ABILITY_GALE_WINGS #if B_GALE_WINGS >= GEN_7 && BATTLER_MAX_HP(battlerId) #endif && gBattleMoves[move].type == TYPE_FLYING) { priority++; } else if (ability == ABILITY_PRANKSTER && IS_MOVE_STATUS(move)) { gProtectStructs[battlerId].pranksterElevated = 1; priority++; } else if (gBattleMoves[move].effect == EFFECT_GRASSY_GLIDE && gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && IsBattlerGrounded(battlerId)) { priority++; } else if (ability == ABILITY_TRIAGE) { switch (gBattleMoves[move].effect) { case EFFECT_RESTORE_HP: case EFFECT_REST: case EFFECT_MORNING_SUN: case EFFECT_MOONLIGHT: case EFFECT_SYNTHESIS: case EFFECT_HEAL_PULSE: case EFFECT_HEALING_WISH: case EFFECT_SWALLOW: case EFFECT_WISH: case EFFECT_SOFTBOILED: case EFFECT_ABSORB: case EFFECT_ROOST: priority += 3; break; } } if (gProtectStructs[battlerId].quash) priority = -8; return priority; } u8 GetWhoStrikesFirst(u8 battler1, u8 battler2, bool8 ignoreChosenMoves) { u8 strikesFirst = 0; u32 speedBattler1 = 0, speedBattler2 = 0; u32 holdEffectBattler1 = 0, holdEffectBattler2 = 0; s8 priority1 = 0, priority2 = 0; u16 ability1 = GetBattlerAbility(battler1), ability2 = GetBattlerAbility(battler2); // Battler 1 speedBattler1 = GetBattlerTotalSpeedStat(battler1); holdEffectBattler1 = GetBattlerHoldEffect(battler1, TRUE); // Quick Draw if (!ignoreChosenMoves && ability1 == ABILITY_QUICK_DRAW && !IS_MOVE_STATUS(gChosenMoveByBattler[battler1]) && Random() % 100 < 30) gProtectStructs[battler1].quickDraw = TRUE; // Quick Claw and Custap Berry if (!gProtectStructs[battler1].quickDraw && ((holdEffectBattler1 == HOLD_EFFECT_QUICK_CLAW && gRandomTurnNumber < (0xFFFF * GetBattlerHoldEffectParam(battler1)) / 100) || (holdEffectBattler1 == HOLD_EFFECT_CUSTAP_BERRY && HasEnoughHpToEatBerry(battler1, 4, gBattleMons[battler1].item)))) gProtectStructs[battler1].usedCustapBerry = TRUE; // Battler 2 speedBattler2 = GetBattlerTotalSpeedStat(battler2); holdEffectBattler2 = GetBattlerHoldEffect(battler2, TRUE); // Quick Draw if (!ignoreChosenMoves && ability2 == ABILITY_QUICK_DRAW && !IS_MOVE_STATUS(gChosenMoveByBattler[battler2]) && Random() % 100 < 30) gProtectStructs[battler2].quickDraw = TRUE; // Quick Claw and Custap Berry if (!gProtectStructs[battler2].quickDraw && ((holdEffectBattler2 == HOLD_EFFECT_QUICK_CLAW && gRandomTurnNumber < (0xFFFF * GetBattlerHoldEffectParam(battler2)) / 100) || (holdEffectBattler2 == HOLD_EFFECT_CUSTAP_BERRY && HasEnoughHpToEatBerry(battler2, 4, gBattleMons[battler2].item)))) gProtectStructs[battler2].usedCustapBerry = TRUE; if (!ignoreChosenMoves) { if (gChosenActionByBattler[battler1] == B_ACTION_USE_MOVE) priority1 = GetChosenMovePriority(battler1); if (gChosenActionByBattler[battler2] == B_ACTION_USE_MOVE) priority2 = GetChosenMovePriority(battler2); } if (priority1 == priority2) { // QUICK CLAW / CUSTAP - always first // LAGGING TAIL - always last // STALL - always last if (gProtectStructs[battler1].quickDraw && !gProtectStructs[battler2].quickDraw) strikesFirst = 0; else if (!gProtectStructs[battler1].quickDraw && gProtectStructs[battler2].quickDraw) strikesFirst = 1; else if (gProtectStructs[battler1].usedCustapBerry && !gProtectStructs[battler2].usedCustapBerry) strikesFirst = 0; else if (gProtectStructs[battler2].usedCustapBerry && !gProtectStructs[battler1].usedCustapBerry) strikesFirst = 1; else if (holdEffectBattler1 == HOLD_EFFECT_LAGGING_TAIL && holdEffectBattler2 != HOLD_EFFECT_LAGGING_TAIL) strikesFirst = 1; else if (holdEffectBattler2 == HOLD_EFFECT_LAGGING_TAIL && holdEffectBattler1 != HOLD_EFFECT_LAGGING_TAIL) strikesFirst = 0; else if (ability1 == ABILITY_STALL && ability2 != ABILITY_STALL) strikesFirst = 1; else if (ability2 == ABILITY_STALL && ability1 != ABILITY_STALL) strikesFirst = 0; else { if (speedBattler1 == speedBattler2 && Random() & 1) { strikesFirst = 2; // same speeds, same priorities } else if (speedBattler1 < speedBattler2) { // battler2 has more speed if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM) strikesFirst = 0; else strikesFirst = 1; } else { // battler1 has more speed if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM) strikesFirst = 1; else strikesFirst = 0; } } } else if (priority1 < priority2) { strikesFirst = 1; // battler2's move has greater priority } else { strikesFirst = 0; // battler1's move has greater priority } 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 (gChosenActionByBattler[2] == B_ACTION_RUN) { gActiveBattler = 2; 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 = CheckMegaEvolutionBeforeTurn; gBattleStruct->mega.battlerId = 0; return; } else { for (gActiveBattler = 0; gActiveBattler < gBattlersCount; gActiveBattler++) { if (gChosenActionByBattler[gActiveBattler] == B_ACTION_USE_ITEM || gChosenActionByBattler[gActiveBattler] == B_ACTION_SWITCH || gChosenActionByBattler[gActiveBattler] == B_ACTION_THROW_BALL) { 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 && gChosenActionByBattler[gActiveBattler] != B_ACTION_THROW_BALL) { 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 && gActionsByTurnOrder[i] != B_ACTION_THROW_BALL && gActionsByTurnOrder[j] != B_ACTION_THROW_BALL) { if (GetWhoStrikesFirst(battler1, battler2, FALSE)) SwapTurnOrder(i, j); } } } } } gBattleMainFunc = CheckMegaEvolutionBeforeTurn; gBattleStruct->mega.battlerId = 0; } static void TurnValuesCleanUp(bool8 var0) { s32 i; for (gActiveBattler = 0; gActiveBattler < gBattlersCount; gActiveBattler++) { if (var0) { gProtectStructs[gActiveBattler].protected = FALSE; gProtectStructs[gActiveBattler].spikyShielded = FALSE; gProtectStructs[gActiveBattler].kingsShielded = FALSE; gProtectStructs[gActiveBattler].banefulBunkered = FALSE; gProtectStructs[gActiveBattler].quash = FALSE; } else { memset(&gProtectStructs[gActiveBattler], 0, sizeof(struct ProtectStruct)); 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; } gSideStatuses[0] &= ~(SIDE_STATUS_QUICK_GUARD | SIDE_STATUS_WIDE_GUARD | SIDE_STATUS_CRAFTY_SHIELD | SIDE_STATUS_MAT_BLOCK); gSideStatuses[1] &= ~(SIDE_STATUS_QUICK_GUARD | SIDE_STATUS_WIDE_GUARD | SIDE_STATUS_CRAFTY_SHIELD | SIDE_STATUS_MAT_BLOCK); gSideTimers[0].followmeTimer = 0; gSideTimers[1].followmeTimer = 0; } void SpecialStatusesClear(void) { memset(&gSpecialStatuses, 0, sizeof(gSpecialStatuses)); } static void CheckMegaEvolutionBeforeTurn(void) { if (!(gHitMarker & HITMARKER_RUN)) { while (gBattleStruct->mega.battlerId < gBattlersCount) { gActiveBattler = gBattlerAttacker = gBattleStruct->mega.battlerId; gBattleStruct->mega.battlerId++; if (gBattleStruct->mega.toEvolve & gBitTable[gActiveBattler] && !(gProtectStructs[gActiveBattler].noValidMoves)) { gBattleStruct->mega.toEvolve &= ~(gBitTable[gActiveBattler]); gLastUsedItem = gBattleMons[gActiveBattler].item; if (gBattleStruct->mega.isWishMegaEvo == TRUE) BattleScriptExecute(BattleScript_WishMegaEvolution); else BattleScriptExecute(BattleScript_MegaEvolution); return; } } } #if B_MEGA_EVO_TURN_ORDER <= GEN_6 gBattleMainFunc = CheckFocusPunch_ClearVarsBeforeTurnStarts; gBattleStruct->focusPunchBattlerId = 0; #else gBattleMainFunc = TryChangeTurnOrder; // This will just do nothing if no mon has mega evolved #endif } // In gen7, priority and speed are recalculated during the turn in which a pokemon mega evolves static void TryChangeTurnOrder(void) { s32 i, j; 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_MOVE && gActionsByTurnOrder[j] == B_ACTION_USE_MOVE) { if (GetWhoStrikesFirst(battler1, battler2, FALSE)) SwapTurnOrder(i, j); } } } gBattleMainFunc = CheckFocusPunch_ClearVarsBeforeTurnStarts; gBattleStruct->focusPunchBattlerId = 0; } static void CheckFocusPunch_ClearVarsBeforeTurnStarts(void) { u32 i; if (!(gHitMarker & HITMARKER_RUN)) { while (gBattleStruct->focusPunchBattlerId < gBattlersCount) { gActiveBattler = gBattlerAttacker = gBattleStruct->focusPunchBattlerId; gBattleStruct->focusPunchBattlerId++; if (!(gBattleMons[gActiveBattler].status1 & STATUS1_SLEEP) && !(gDisableStructs[gBattlerAttacker].truantCounter) && !(gProtectStructs[gActiveBattler].noValidMoves)) { switch(gChosenMoveByBattler[gActiveBattler]) { case MOVE_FOCUS_PUNCH: BattleScriptExecute(BattleScript_FocusPunchSetUp); return; case MOVE_BEAK_BLAST: BattleScriptExecute(BattleScript_BeakBlastSetUp); return; } } } } gBattleMainFunc = CheckQuickClaw_CustapBerryActivation; gBattleStruct->quickClawBattlerId = 0; } static void CheckQuickClaw_CustapBerryActivation(void) { u32 i; if (!(gHitMarker & HITMARKER_RUN)) { while (gBattleStruct->quickClawBattlerId < gBattlersCount) { gActiveBattler = gBattlerAttacker = gBattleStruct->quickClawBattlerId; gBattleStruct->quickClawBattlerId++; if (gChosenActionByBattler[gActiveBattler] == B_ACTION_USE_MOVE && gChosenMoveByBattler[gActiveBattler] != MOVE_FOCUS_PUNCH // quick claw message doesn't need to activate here && (gProtectStructs[gActiveBattler].usedCustapBerry || gProtectStructs[gActiveBattler].quickDraw) && !(gBattleMons[gActiveBattler].status1 & STATUS1_SLEEP) && !(gDisableStructs[gBattlerAttacker].truantCounter) && !(gProtectStructs[gActiveBattler].noValidMoves)) { if (gProtectStructs[gActiveBattler].usedCustapBerry) { gProtectStructs[gActiveBattler].usedCustapBerry = FALSE; gLastUsedItem = gBattleMons[gActiveBattler].item; PREPARE_ITEM_BUFFER(gBattleTextBuff1, gLastUsedItem); if (GetBattlerHoldEffect(gActiveBattler, FALSE) == HOLD_EFFECT_CUSTAP_BERRY) { // don't record berry since its gone now BattleScriptExecute(BattleScript_CustapBerryActivation); } else { RecordItemEffectBattle(gActiveBattler, GetBattlerHoldEffect(gActiveBattler, FALSE)); BattleScriptExecute(BattleScript_QuickClawActivation); } } else if (gProtectStructs[gActiveBattler].quickDraw) { gBattlerAbility = gActiveBattler; gProtectStructs[gActiveBattler].quickDraw = FALSE; gLastUsedAbility = gBattleMons[gActiveBattler].ability; PREPARE_ABILITY_BUFFER(gBattleTextBuff1, gLastUsedAbility); RecordAbilityBattle(gActiveBattler, gLastUsedAbility); BattleScriptExecute(BattleScript_QuickDrawActivation); } return; } } } // setup stuff before turns/actions TryClearRageAndFuryCutter(); gCurrentTurnActionNumber = 0; gCurrentActionFuncId = gActionsByTurnOrder[0]; gBattleStruct->dynamicMoveType = 0; for (i = 0; i < MAX_BATTLERS_COUNT; i++) { gBattleStruct->ateBoost[i] = FALSE; gSpecialStatuses[i].gemBoost = FALSE; } 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_PASSIVE_DAMAGE; 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 | BATTLE_TYPE_RECORDED_LINK)) { gSpecialVar_Result = gBattleOutcome; gBattleTextBuff1[0] = gBattleOutcome; gBattlerAttacker = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); gBattlescriptCurrInstr = BattleScript_LinkBattleWonOrLost; gBattleOutcome &= ~B_OUTCOME_LINK_BATTLE_RAN; } else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER && gBattleTypeFlags & (BATTLE_TYPE_FRONTIER | BATTLE_TYPE_TRAINER_HILL | BATTLE_TYPE_EREADER_TRAINER)) { BattleStopLowHpSound(); gBattlescriptCurrInstr = BattleScript_FrontierTrainerBattleWon; if (gTrainerBattleOpponent_A == TRAINER_FRONTIER_BRAIN) PlayBGM(MUS_VICTORY_GYM_LEADER); else PlayBGM(MUS_VICTORY_TRAINER); } else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER && !(gBattleTypeFlags & BATTLE_TYPE_LINK)) { BattleStopLowHpSound(); gBattlescriptCurrInstr = BattleScript_LocalTrainerBattleWon; switch (gTrainers[gTrainerBattleOpponent_A].trainerClass) { case TRAINER_CLASS_ELITE_FOUR: case TRAINER_CLASS_CHAMPION: PlayBGM(MUS_VICTORY_LEAGUE); break; case TRAINER_CLASS_TEAM_AQUA: case TRAINER_CLASS_TEAM_MAGMA: case TRAINER_CLASS_AQUA_ADMIN: case TRAINER_CLASS_AQUA_LEADER: case TRAINER_CLASS_MAGMA_ADMIN: case TRAINER_CLASS_MAGMA_LEADER: PlayBGM(MUS_VICTORY_AQUA_MAGMA); break; case TRAINER_CLASS_LEADER: PlayBGM(MUS_VICTORY_GYM_LEADER); break; default: PlayBGM(MUS_VICTORY_TRAINER); break; } } else { gBattlescriptCurrInstr = BattleScript_PayDayMoneyAndPickUpItems; } gBattleMainFunc = HandleEndTurn_FinishBattle; } static void HandleEndTurn_BattleLost(void) { gCurrentActionFuncId = 0; if (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK)) { if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER) { if (gBattleOutcome & B_OUTCOME_LINK_BATTLE_RAN) { gBattlescriptCurrInstr = BattleScript_PrintPlayerForfeitedLinkBattle; gBattleOutcome &= ~B_OUTCOME_LINK_BATTLE_RAN; gSaveBlock2Ptr->frontier.disableRecordBattle = TRUE; } else { gBattlescriptCurrInstr = BattleScript_FrontierLinkBattleLost; gBattleOutcome &= ~B_OUTCOME_LINK_BATTLE_RAN; } } else { gBattleTextBuff1[0] = gBattleOutcome; gBattlerAttacker = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT); gBattlescriptCurrInstr = BattleScript_LinkBattleWonOrLost; gBattleOutcome &= ~B_OUTCOME_LINK_BATTLE_RAN; } } else { gBattlescriptCurrInstr = BattleScript_LocalBattleLost; } gBattleMainFunc = HandleEndTurn_FinishBattle; } static void HandleEndTurn_RanFromBattle(void) { gCurrentActionFuncId = 0; if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER && gBattleTypeFlags & BATTLE_TYPE_TRAINER) { gBattlescriptCurrInstr = BattleScript_PrintPlayerForfeited; gBattleOutcome = B_OUTCOME_FORFEITED; gSaveBlock2Ptr->frontier.disableRecordBattle = TRUE; } else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_HILL) { gBattlescriptCurrInstr = BattleScript_PrintPlayerForfeited; gBattleOutcome = B_OUTCOME_FORFEITED; } else { switch (gProtectStructs[gBattlerAttacker].fleeType) { default: gBattlescriptCurrInstr = BattleScript_GotAwaySafely; break; case FLEE_ITEM: gBattlescriptCurrInstr = BattleScript_SmokeBallEscape; break; case FLEE_ABILITY: 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) { u32 i; if (gCurrentActionFuncId == B_ACTION_TRY_FINISH || gCurrentActionFuncId == B_ACTION_FINISHED) { if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK | BATTLE_TYPE_FIRST_BATTLE | BATTLE_TYPE_SAFARI | BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_WALLY_TUTORIAL | BATTLE_TYPE_FRONTIER))) { for (gActiveBattler = 0; gActiveBattler < gBattlersCount; gActiveBattler++) { if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER) { if (gBattleResults.playerMon1Species == SPECIES_NONE) { gBattleResults.playerMon1Species = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_SPECIES, NULL); GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_NICKNAME, gBattleResults.playerMon1Name); } else { gBattleResults.playerMon2Species = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_SPECIES, NULL); GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_NICKNAME, gBattleResults.playerMon2Name); } } } TryPutPokemonTodayOnAir(); } if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK | BATTLE_TYPE_TRAINER | BATTLE_TYPE_FIRST_BATTLE | BATTLE_TYPE_SAFARI | BATTLE_TYPE_FRONTIER | BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_WALLY_TUTORIAL)) && gBattleResults.shinyWildMon) { TryPutBreakingNewsOnAir(); } RecordedBattle_SetPlaybackFinished(); BeginFastPaletteFade(3); FadeOutMapMusic(5); #if B_TRAINERS_KNOCK_OFF_ITEMS == TRUE if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) TryRestoreStolenItems(); #endif for (i = 0; i < PARTY_SIZE; i++) { UndoMegaEvolution(i); UndoFormChange(i, B_SIDE_PLAYER, FALSE); DoBurmyFormChange(i); } #if B_RECALCULATE_STATS >= GEN_5 // Recalculate the stats of every party member before the end for (i = 0; i < PARTY_SIZE; i++) { if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES2) != SPECIES_NONE && GetMonData(&gPlayerParty[i], MON_DATA_SPECIES2) != SPECIES_EGG) { CalculateMonStats(&gPlayerParty[i]); } } #endif gBattleMainFunc = FreeResetData_ReturnToOvOrDoEvolutions; gCB2_AfterEvolution = BattleMainCB2; } else { if (gBattleControllerExecFlags == 0) gBattleScriptingCommandsTable[gBattlescriptCurrInstr[0]](); } } static void FreeResetData_ReturnToOvOrDoEvolutions(void) { if (!gPaletteFade.active) { gIsFishingEncounter = FALSE; gIsSurfingEncounter = FALSE; ResetSpriteData(); if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK | BATTLE_TYPE_FIRST_BATTLE | BATTLE_TYPE_SAFARI | BATTLE_TYPE_FRONTIER | BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_WALLY_TUTORIAL)) #if B_EVOLUTION_AFTER_WHITEOUT <= GEN_5 && (gBattleOutcome == B_OUTCOME_WON || gBattleOutcome == B_OUTCOME_CAUGHT) #endif ) { gBattleMainFunc = TrySpecialEvolution; } else { gBattleMainFunc = ReturnFromBattleToOverworld; return; } } FreeAllWindowBuffers(); if (!(gBattleTypeFlags & BATTLE_TYPE_LINK)) { FreeMonSpritesGfx(); FreeBattleResources(); FreeBattleSpritesData(); } } static void TrySpecialEvolution(void) // Attempts to perform non-level related battle evolutions (not the script command). { s32 i; for (i = 0; i < PARTY_SIZE; i++) { u16 species = GetEvolutionTargetSpecies(&gPlayerParty[i], EVO_MODE_BATTLE_SPECIAL, i, NULL); if (species != SPECIES_NONE && !(sTriedEvolving & gBitTable[i])) { sTriedEvolving |= gBitTable[i]; FreeAllWindowBuffers(); gBattleMainFunc = WaitForEvoSceneToFinish; EvolutionScene(&gPlayerParty[i], species, TRUE, i); return; } } sTriedEvolving = 0; gBattleMainFunc = TryEvolvePokemon; } 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], EVO_MODE_NORMAL, levelUpBits, NULL); if (species != SPECIES_NONE) { FreeAllWindowBuffers(); gBattleMainFunc = WaitForEvoSceneToFinish; EvolutionScene(&gPlayerParty[i], species, TRUE, i); return; } } } } gBattleMainFunc = ReturnFromBattleToOverworld; } static void WaitForEvoSceneToFinish(void) { if (gMain.callback2 == BattleMainCB2) gBattleMainFunc = TrySpecialEvolution; } static void ReturnFromBattleToOverworld(void) { if (!(gBattleTypeFlags & BATTLE_TYPE_LINK)) { RandomlyGivePartyPokerus(gPlayerParty); PartySpreadPokerus(gPlayerParty); } if (gBattleTypeFlags & BATTLE_TYPE_LINK && gReceivedRemoteLinkPlayers != 0) return; gSpecialVar_Result = gBattleOutcome; gMain.inBattle = FALSE; gMain.callback1 = gPreBattleCallback1; if (gBattleTypeFlags & BATTLE_TYPE_ROAMER) { UpdateRoamerHPStatus(&gEnemyParty[0]); #ifndef BUGFIX if ((gBattleOutcome & B_OUTCOME_WON) || gBattleOutcome == B_OUTCOME_CAUGHT) #else if ((gBattleOutcome == B_OUTCOME_WON) || gBattleOutcome == B_OUTCOME_CAUGHT) // Bug: When Roar is used by roamer, gBattleOutcome is B_OUTCOME_PLAYER_TELEPORTED (5). #endif // & with B_OUTCOME_WON (1) will return TRUE and deactivates the roamer. SetRoamerInactive(); } m4aSongNumStop(SE_LOW_HEALTH); 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 == 0) gBattleScriptingCommandsTable[gBattlescriptCurrInstr[0]](); } } void RunBattleScriptCommands(void) { if (gBattleControllerExecFlags == 0) gBattleScriptingCommandsTable[gBattlescriptCurrInstr[0]](); } void SetTypeBeforeUsingMove(u16 move, u8 battlerAtk) { u32 moveType, ateType, attackerAbility; u16 holdEffect = GetBattlerHoldEffect(battlerAtk, TRUE); if (move == MOVE_STRUGGLE) return; gBattleStruct->dynamicMoveType = 0; gBattleStruct->ateBoost[battlerAtk] = 0; gSpecialStatuses[battlerAtk].gemBoost = FALSE; if (gBattleMoves[move].effect == EFFECT_WEATHER_BALL) { if (WEATHER_HAS_EFFECT) { if (gBattleWeather & B_WEATHER_RAIN && holdEffect != HOLD_EFFECT_UTILITY_UMBRELLA) gBattleStruct->dynamicMoveType = TYPE_WATER | F_DYNAMIC_TYPE_2; else if (gBattleWeather & B_WEATHER_SANDSTORM) gBattleStruct->dynamicMoveType = TYPE_ROCK | F_DYNAMIC_TYPE_2; else if (gBattleWeather & B_WEATHER_SUN && holdEffect != HOLD_EFFECT_UTILITY_UMBRELLA) gBattleStruct->dynamicMoveType = TYPE_FIRE | F_DYNAMIC_TYPE_2; else if (gBattleWeather & B_WEATHER_HAIL) gBattleStruct->dynamicMoveType = TYPE_ICE | F_DYNAMIC_TYPE_2; else gBattleStruct->dynamicMoveType = TYPE_NORMAL | F_DYNAMIC_TYPE_2; } } else if (gBattleMoves[move].effect == EFFECT_HIDDEN_POWER) { u8 typeBits = ((gBattleMons[battlerAtk].hpIV & 1) << 0) | ((gBattleMons[battlerAtk].attackIV & 1) << 1) | ((gBattleMons[battlerAtk].defenseIV & 1) << 2) | ((gBattleMons[battlerAtk].speedIV & 1) << 3) | ((gBattleMons[battlerAtk].spAttackIV & 1) << 4) | ((gBattleMons[battlerAtk].spDefenseIV & 1) << 5); // Subtract 4 instead of 1 below because 3 types are excluded (TYPE_NORMAL and TYPE_MYSTERY and TYPE_FAIRY) // The final + 1 skips past Normal, and the following conditional skips TYPE_MYSTERY gBattleStruct->dynamicMoveType = ((NUMBER_OF_MON_TYPES - 4) * typeBits) / 63 + 1; if (gBattleStruct->dynamicMoveType >= TYPE_MYSTERY) gBattleStruct->dynamicMoveType++; gBattleStruct->dynamicMoveType |= F_DYNAMIC_TYPE_1 | F_DYNAMIC_TYPE_2; } else if (gBattleMoves[move].effect == EFFECT_CHANGE_TYPE_ON_ITEM) { if (holdEffect == gBattleMoves[move].argument) gBattleStruct->dynamicMoveType = ItemId_GetSecondaryId(gBattleMons[battlerAtk].item) | F_DYNAMIC_TYPE_2; } else if (gBattleMoves[move].effect == EFFECT_REVELATION_DANCE) { if (gBattleMons[battlerAtk].type1 != TYPE_MYSTERY) gBattleStruct->dynamicMoveType = gBattleMons[battlerAtk].type1 | F_DYNAMIC_TYPE_2; else if (gBattleMons[battlerAtk].type2 != TYPE_MYSTERY) gBattleStruct->dynamicMoveType = gBattleMons[battlerAtk].type2 | F_DYNAMIC_TYPE_2; else if (gBattleMons[battlerAtk].type3 != TYPE_MYSTERY) gBattleStruct->dynamicMoveType = gBattleMons[battlerAtk].type3 | F_DYNAMIC_TYPE_2; } else if (gBattleMoves[move].effect == EFFECT_NATURAL_GIFT) { if (ItemId_GetPocket(gBattleMons[battlerAtk].item) == POCKET_BERRIES) gBattleStruct->dynamicMoveType = gNaturalGiftTable[ITEM_TO_BERRY(gBattleMons[battlerAtk].item)].type; } else if (gBattleMoves[move].effect == EFFECT_TERRAIN_PULSE) { if (IsBattlerTerrainAffected(battlerAtk, STATUS_FIELD_TERRAIN_ANY)) { if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN) gBattleStruct->dynamicMoveType = TYPE_ELECTRIC | F_DYNAMIC_TYPE_2; else if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN) gBattleStruct->dynamicMoveType = TYPE_GRASS | F_DYNAMIC_TYPE_2; else if (gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN) gBattleStruct->dynamicMoveType = TYPE_FAIRY | F_DYNAMIC_TYPE_2; else if (gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN) gBattleStruct->dynamicMoveType = TYPE_PSYCHIC | F_DYNAMIC_TYPE_2; else //failsafe gBattleStruct->dynamicMoveType = TYPE_NORMAL | F_DYNAMIC_TYPE_2; } } attackerAbility = GetBattlerAbility(battlerAtk); GET_MOVE_TYPE(move, moveType); if ((gFieldStatuses & STATUS_FIELD_ION_DELUGE && moveType == TYPE_NORMAL) || gStatuses4[battlerAtk] & STATUS4_ELECTRIFIED) { gBattleStruct->dynamicMoveType = TYPE_ELECTRIC | F_DYNAMIC_TYPE_2; } else if (gBattleMoves[move].type == TYPE_NORMAL && gBattleMoves[move].effect != EFFECT_HIDDEN_POWER && gBattleMoves[move].effect != EFFECT_WEATHER_BALL && gBattleMoves[move].effect != EFFECT_CHANGE_TYPE_ON_ITEM && gBattleMoves[move].effect != EFFECT_NATURAL_GIFT && ((attackerAbility == ABILITY_PIXILATE && (ateType = TYPE_FAIRY)) || (attackerAbility == ABILITY_REFRIGERATE && (ateType = TYPE_ICE)) || (attackerAbility == ABILITY_AERILATE && (ateType = TYPE_FLYING)) || ((attackerAbility == ABILITY_GALVANIZE) && (ateType = TYPE_ELECTRIC)) ) ) { gBattleStruct->dynamicMoveType = ateType | F_DYNAMIC_TYPE_2; gBattleStruct->ateBoost[battlerAtk] = 1; } else if (gBattleMoves[move].type != TYPE_NORMAL && gBattleMoves[move].effect != EFFECT_HIDDEN_POWER && gBattleMoves[move].effect != EFFECT_WEATHER_BALL && attackerAbility == ABILITY_NORMALIZE) { gBattleStruct->dynamicMoveType = TYPE_NORMAL | F_DYNAMIC_TYPE_2; gBattleStruct->ateBoost[battlerAtk] = 1; } else if (gBattleMoves[move].flags & FLAG_SOUND && attackerAbility == ABILITY_LIQUID_VOICE) { gBattleStruct->dynamicMoveType = TYPE_WATER | F_DYNAMIC_TYPE_2; } else if (gStatuses4[battlerAtk] & STATUS4_PLASMA_FISTS && moveType == TYPE_NORMAL) { gBattleStruct->dynamicMoveType = TYPE_ELECTRIC | F_DYNAMIC_TYPE_2; } else if (move == MOVE_AURA_WHEEL && gBattleMons[battlerAtk].species == SPECIES_MORPEKO_HANGRY) { gBattleStruct->dynamicMoveType = TYPE_DARK | F_DYNAMIC_TYPE_2; } // Check if a gem should activate. GET_MOVE_TYPE(move, moveType); if (holdEffect == HOLD_EFFECT_GEMS && moveType == ItemId_GetSecondaryId(gBattleMons[battlerAtk].item)) { gSpecialStatuses[battlerAtk].gemParam = GetBattlerHoldEffectParam(battlerAtk); gSpecialStatuses[battlerAtk].gemBoost = TRUE; } } // special to set a field's totem boost(s) // inputs: // var8000: battlerId // var8001 - var8007: stat changes void SetTotemBoost(void) { u8 battlerId = gSpecialVar_0x8000; u8 i; for (i = 0; i < (NUM_BATTLE_STATS - 1); i++) { if (*(&gSpecialVar_0x8001 + i)) { gTotemBoosts[battlerId].stats |= (1 << i); gTotemBoosts[battlerId].statChanges[i] = *(&gSpecialVar_0x8001 + i); gTotemBoosts[battlerId].stats |= 0x80; // used as a flag for the "totem flared to life" script } } } bool32 IsWildMonSmart(void) { #if B_SMART_WILD_AI_FLAG != 0 return (FlagGet(B_SMART_WILD_AI_FLAG)); #else return FALSE; #endif }