pokeemerald/src/battle_main.c

5751 lines
214 KiB
C
Raw Normal View History

2017-10-01 01:12:42 +02:00
#include "global.h"
#include "battle.h"
#include "battle_anim.h"
2020-12-20 14:47:20 -07:00
#include "battle_ai_main.h"
2020-12-15 21:57:33 -07:00
#include "battle_ai_util.h"
2018-11-13 22:28:46 +00:00
#include "battle_arena.h"
#include "battle_controllers.h"
2018-02-09 01:09:02 +01:00
#include "battle_interface.h"
2018-12-07 14:47:20 -05:00
#include "battle_main.h"
2018-11-13 22:28:46 +00:00
#include "battle_message.h"
2018-11-18 17:52:22 +01:00
#include "battle_pyramid.h"
2018-11-13 22:28:46 +00:00
#include "battle_scripts.h"
#include "battle_setup.h"
#include "battle_tower.h"
#include "battle_util.h"
2017-10-01 01:12:42 +02:00
#include "berry.h"
2018-11-13 22:28:46 +00:00
#include "bg.h"
2019-04-04 23:53:06 +02:00
#include "data.h"
Ported TheXaman's latest changes to the Debug Menu (#2815) * Added option for generating incrementing pokemon in pc boxes # Conflicts: # src/debug.c * added submenu arrows, increased menu high to full screen # Conflicts: # src/debug.c * combined flags and vars into one submenu # Conflicts: # src/debug.c * added new window to flags/vars showing the current state and added submenu indicator # Conflicts: # src/debug.c * added alligned arrows for debug submenus # Conflicts: # src/debug.c * used {CLEAR_TO X} instead of manual spaces # Conflicts: # src/debug.c * renamed gDebugText to proper sDebugText # Conflicts: # src/debug.c * added Fill submenu, added fill function for PC items and all bag pockets @LOuroboros # Conflicts: # src/debug.c * put cheat start into utility # Conflicts: # src/debug.c * put fill submenu into main menu # Conflicts: # src/debug.c * tiny fix * renaming and reordering # Conflicts: # src/debug.c * Added reset pokedex flags for @AsparagusEduardo * made flag toggle list dynamic # Conflicts: # src/debug.c * initial battle debug menu WIP # Conflicts: # src/debug.c # src/wild_encounter.c * fix visual bug * added battle start # Conflicts: # include/debug.h # src/battle_ai_script_commands.c * Added faster way to add initial movesets to mon * Added waiting music for the slow box filling * Simplified the call to scripts * Simplified debug scripts * Disabled Battle Test for now * Fixed personality on fast PC fill being always 0 * Removed BATTLE_ENGINE instances + added AI_FLAG_COUNT * Added missing return TRUE * Sets nickname * Changed how GetSpeciesName to how it's used upstream --------- Co-authored-by: TheXaman <48356183+TheXaman@users.noreply.github.com>
2023-07-18 03:17:03 -04:00
#include "debug.h"
2018-11-13 22:28:46 +00:00
#include "decompress.h"
2018-11-14 00:01:50 +00:00
#include "dma3.h"
2018-11-13 22:28:46 +00:00
#include "event_data.h"
#include "evolution_scene.h"
2018-12-18 20:15:59 -06:00
#include "graphics.h"
2018-11-13 22:28:46 +00:00
#include "gpu_regs.h"
#include "international_string_util.h"
2017-10-02 23:32:39 +02:00
#include "item.h"
2017-10-01 01:12:42 +02:00
#include "link.h"
2017-11-10 22:08:17 -05:00
#include "link_rfu.h"
2018-11-13 22:28:46 +00:00
#include "load_save.h"
#include "main.h"
#include "malloc.h"
2017-10-01 18:54:01 +02:00
#include "m4a.h"
2018-11-13 22:28:46 +00:00
#include "palette.h"
#include "party_menu.h"
#include "pokeball.h"
#include "pokedex.h"
#include "pokemon.h"
#include "random.h"
2018-11-13 22:28:46 +00:00
#include "recorded_battle.h"
#include "roamer.h"
#include "safari_zone.h"
#include "scanline_effect.h"
2017-10-01 18:54:01 +02:00
#include "sound.h"
#include "sprite.h"
2018-11-13 22:28:46 +00:00
#include "string_util.h"
2018-12-24 00:02:29 +01:00
#include "strings.h"
2018-11-13 22:28:46 +00:00
#include "task.h"
#include "test_runner.h"
2018-11-13 22:28:46 +00:00
#include "text.h"
2017-10-02 23:32:39 +02:00
#include "trig.h"
2018-11-13 22:28:46 +00:00
#include "tv.h"
#include "util.h"
2019-04-16 09:38:49 +02:00
#include "wild_encounter.h"
2018-11-13 22:28:46 +00:00
#include "window.h"
2017-12-05 11:55:48 -06:00
#include "constants/abilities.h"
2018-11-13 22:28:46 +00:00
#include "constants/battle_move_effects.h"
#include "constants/battle_string_ids.h"
#include "constants/hold_effects.h"
#include "constants/items.h"
2017-12-11 12:27:51 -06:00
#include "constants/moves.h"
2019-10-17 19:22:03 -04:00
#include "constants/party_menu.h"
2018-06-20 23:07:51 +02:00
#include "constants/rgb.h"
2018-11-13 22:28:46 +00:00
#include "constants/songs.h"
2018-11-14 00:01:50 +00:00
#include "constants/trainers.h"
2019-03-01 22:32:50 -05:00
#include "cable_club.h"
2017-10-01 01:12:42 +02:00
extern struct Evolution gEvolutionTable[][EVOS_PER_MON];
2018-06-17 16:48:58 +02:00
extern const struct BgTemplate gBattleBgTemplates[];
2018-06-28 21:06:32 +02:00
extern const struct WindowTemplate *const gBattleWindowTemplates[];
2017-10-02 23:32:39 +02:00
2017-10-01 01:12:42 +02:00
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);
2017-10-01 18:54:01 +02:00
static void TryCorrectShedinjaLanguage(struct Pokemon *mon);
static u8 CreateNPCTrainerParty(struct Pokemon *party, u16 trainerNum, bool8 firstTrainer);
2017-10-06 19:09:37 +02:00
static void BattleMainCB1(void);
2021-01-22 20:03:21 -05:00
static void CB2_EndLinkBattle(void);
static void EndLinkBattleInSteps(void);
2021-10-04 10:21:03 -04:00
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);
2021-10-04 10:21:03 -04:00
static void SpriteCB_Flicker(struct Sprite *sprite);
2018-06-20 23:07:51 +02:00
static void SpriteCB_AnimFaintOpponent(struct Sprite *sprite);
static void SpriteCB_BlinkVisible(struct Sprite *sprite);
2021-10-04 10:21:03 -04:00
static void SpriteCB_Idle(struct Sprite *sprite);
2021-01-22 23:22:37 -05:00
static void SpriteCB_BattleSpriteSlideLeft(struct Sprite *sprite);
2017-10-06 19:09:37 +02:00
static void TurnValuesCleanUp(bool8 var0);
2018-06-20 23:07:51 +02:00
static void SpriteCB_BounceEffect(struct Sprite *sprite);
2017-10-02 23:32:39 +02:00
static void BattleStartClearSetData(void);
static void DoBattleIntro(void);
2017-10-02 23:32:39 +02:00
static void TryDoEventsBeforeFirstTurn(void);
2017-10-06 19:09:37 +02:00
static void HandleTurnActionSelectionState(void);
2017-10-06 00:12:01 +02:00
static void RunTurnActionsFunctions(void);
2018-02-08 11:17:41 +01:00
static void SetActionsAndBattlersTurnOrder(void);
2023-08-29 16:20:16 +02:00
static void UpdateBattlerPartyOrdersOnSwitch(u32 battler);
static bool8 AllAtActionConfirmed(void);
2021-11-19 22:03:10 +01:00
static void TryChangeTurnOrder(void);
static void CheckChosenMoveForEffectsBeforeTurnStarts(void);
2018-09-16 18:55:32 +02:00
static void CheckMegaEvolutionBeforeTurn(void);
static void CheckQuickClaw_CustapBerryActivation(void);
2017-10-06 00:12:01 +02:00
static void FreeResetData_ReturnToOvOrDoEvolutions(void);
static void ReturnFromBattleToOverworld(void);
static void TryEvolvePokemon(void);
static void WaitForEvoSceneToFinish(void);
2017-10-06 19:09:37 +02:00
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);
2022-07-29 11:15:33 -04:00
static void SpriteCB_UnusedBattleInit(struct Sprite *sprite);
2021-10-04 10:21:03 -04:00
static void SpriteCB_UnusedBattleInit_Main(struct Sprite *sprite);
2022-05-05 11:41:27 -07:00
static void TrySpecialEvolution(void);
static u32 Crc32B (const u8 *data, u32 size);
static u32 GeneratePartyHash(const struct Trainer *trainer, u32 i);
2018-11-19 17:36:39 +01:00
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;
2018-12-01 11:12:31 +01:00
EWRAM_DATA u8 gDisplayedStringBattle[400] = {0};
2018-06-30 15:35:54 +02:00
EWRAM_DATA u8 gBattleTextBuff1[TEXT_BUFF_ARRAY_COUNT] = {0};
EWRAM_DATA u8 gBattleTextBuff2[TEXT_BUFF_ARRAY_COUNT] = {0};
2020-12-01 16:36:44 -05:00
EWRAM_DATA u8 gBattleTextBuff3[30] = {0}; //expanded for stupidly long z move names
2021-10-04 10:21:03 -04:00
// 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};
2018-02-07 22:53:40 +01:00
EWRAM_DATA u32 gBattleTypeFlags = 0;
EWRAM_DATA u8 gBattleTerrain = 0;
EWRAM_DATA u32 gUnusedFirstBattleVar1 = 0; // Never read
2021-10-04 10:21:03 -04:00
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;
2018-02-07 22:53:40 +01:00
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};
2018-06-28 21:06:32 +02:00
EWRAM_DATA u8 gBattlerByTurnOrder[MAX_BATTLERS_COUNT] = {0};
2018-02-07 22:53:40 +01:00
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;
2018-02-08 11:17:41 +01:00
EWRAM_DATA u8 gChosenMovePos = 0;
2018-02-07 22:53:40 +01:00
EWRAM_DATA u16 gCurrentMove = 0;
EWRAM_DATA u16 gChosenMove = 0;
2018-09-22 18:41:00 +02:00
EWRAM_DATA u16 gCalledMove = 0;
2018-02-07 22:53:40 +01:00
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;
2018-02-07 22:53:40 +01:00
EWRAM_DATA u8 gBattlerAttacker = 0;
EWRAM_DATA u8 gBattlerTarget = 0;
2018-02-08 11:17:41 +01:00
EWRAM_DATA u8 gBattlerFainted = 0;
EWRAM_DATA u8 gEffectBattler = 0;
2018-02-08 12:13:29 +01:00
EWRAM_DATA u8 gPotentialItemEffectBattler = 0;
2018-02-07 22:53:40 +01:00
EWRAM_DATA u8 gAbsentBattlerFlags = 0;
2018-07-14 22:56:03 +02:00
EWRAM_DATA u8 gIsCriticalHit = FALSE;
2018-02-07 22:53:40 +01:00
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};
2018-09-22 18:37:03 +02:00
EWRAM_DATA u16 gLastUsedMove = 0;
2018-02-07 22:53:40 +01:00
EWRAM_DATA u8 gLastHitBy[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA u16 gChosenMoveByBattler[MAX_BATTLERS_COUNT] = {0};
2018-07-21 15:11:13 +02:00
EWRAM_DATA u16 gMoveResultFlags = 0;
2018-02-07 22:53:40 +01:00
EWRAM_DATA u32 gHitMarker = 0;
EWRAM_DATA u8 gTakenDmgByBattler[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA u8 gUnusedFirstBattleVar2 = 0; // Never read
EWRAM_DATA u32 gSideStatuses[NUM_BATTLE_SIDES] = {0};
2022-10-20 08:41:18 -03:00
EWRAM_DATA struct SideTimer gSideTimers[NUM_BATTLE_SIDES] = {0};
2018-02-07 22:53:40 +01:00
EWRAM_DATA u32 gStatuses3[MAX_BATTLERS_COUNT] = {0};
2021-10-10 01:13:23 -03:00
EWRAM_DATA u32 gStatuses4[MAX_BATTLERS_COUNT] = {0};
2018-02-07 22:53:40 +01:00
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};
2018-02-08 11:17:41 +01:00
EWRAM_DATA u16 gIntroSlideFlags = 0;
2018-02-07 22:53:40 +01:00
EWRAM_DATA u8 gSentPokesToOpponent[2] = {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};
2018-02-08 11:17:41 +01:00
EWRAM_DATA u8 gBattlerStatusSummaryTaskId[MAX_BATTLERS_COUNT] = {0};
2018-02-07 22:53:40 +01:00
EWRAM_DATA u8 gBattlerInMenuId = 0;
EWRAM_DATA bool8 gDoingBattleAnim = FALSE;
EWRAM_DATA u32 gTransformedPersonalities[MAX_BATTLERS_COUNT] = {0};
EWRAM_DATA u32 gTransformedOtIds[MAX_BATTLERS_COUNT] = {0};
2018-02-07 22:53:40 +01:00
EWRAM_DATA u8 gPlayerDpadHoldFrames = 0;
EWRAM_DATA struct BattleSpriteData *gBattleSpritesDataPtr = NULL;
EWRAM_DATA struct MonSpritesGfx *gMonSpritesGfxPtr = NULL;
2021-01-22 21:35:16 -05:00
EWRAM_DATA struct BattleHealthboxInfo *gBattleControllerOpponentHealthboxData = NULL; // Never read
EWRAM_DATA struct BattleHealthboxInfo *gBattleControllerOpponentFlankHealthboxData = NULL; // Never read
2018-02-07 22:53:40 +01:00
EWRAM_DATA u16 gBattleMovePower = 0;
EWRAM_DATA u16 gMoveToLearn = 0;
2018-07-14 13:17:10 +02:00
EWRAM_DATA u32 gFieldStatuses = 0;
EWRAM_DATA struct FieldTimer gFieldTimers = {0};
2018-09-29 13:36:33 +02:00
EWRAM_DATA u8 gBattlerAbility = 0;
2018-11-24 01:02:02 +01:00
EWRAM_DATA u16 gPartnerSpriteId = 0;
2020-11-19 10:35:37 -07:00
EWRAM_DATA struct TotemBoost gTotemBoosts[MAX_BATTLERS_COUNT] = {0};
2020-12-02 23:09:35 -03:00
EWRAM_DATA bool8 gHasFetchedBall = FALSE;
2020-12-05 03:03:55 -03:00
EWRAM_DATA u8 gLastUsedBall = 0;
EWRAM_DATA u16 gLastThrownBall = 0;
EWRAM_DATA u16 gBallToDisplay = 0;
EWRAM_DATA bool8 gLastUsedBallMenuPresent = FALSE;
2022-05-05 11:41:27 -07:00
EWRAM_DATA u8 gPartyCriticalHits[PARTY_SIZE] = {0};
EWRAM_DATA static u8 sTriedEvolving = 0;
2018-02-07 22:53:40 +01:00
2018-02-07 23:21:51 +01:00
void (*gPreBattleCallback1)(void);
void (*gBattleMainFunc)(void);
struct BattleResults gBattleResults;
u8 gLeveledUpInBattle;
u8 gHealthboxSpriteIds[MAX_BATTLERS_COUNT];
u8 gMultiUsePlayerCursor;
u8 gNumberOfMovesToChoose;
2018-12-23 14:52:47 +01:00
static const struct ScanlineEffectParams sIntroScanlineParams16Bit =
{
2021-11-10 17:01:21 -05:00
&REG_BG3HOFS, SCANLINE_EFFECT_DMACNT_16BIT, 1
2018-12-23 14:52:47 +01:00
};
// unused
static const struct ScanlineEffectParams sIntroScanlineParams32Bit =
{
2021-11-10 17:01:21 -05:00
&REG_BG3HOFS, SCANLINE_EFFECT_DMACNT_32BIT, 1
2018-12-23 14:52:47 +01:00
};
2021-10-04 10:21:03 -04:00
const struct SpriteTemplate gUnusedBattleInitSprite =
2018-12-23 14:52:47 +01:00
{
.tileTag = 0,
.paletteTag = 0,
.oam = &gDummyOamData,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
2021-10-04 10:21:03 -04:00
.callback = SpriteCB_UnusedBattleInit,
2018-12-23 14:52:47 +01:00
};
static const u8 sText_ShedinjaJpnName[] = _("ヌケニン"); // Nukenin
2021-01-22 23:22:37 -05:00
const struct OamData gOamData_BattleSpriteOpponentSide =
2018-12-23 14:52:47 +01:00
{
.y = 0,
2019-04-01 18:31:10 -04:00
.affineMode = ST_OAM_AFFINE_NORMAL,
.objMode = ST_OAM_OBJ_NORMAL,
.bpp = ST_OAM_4BPP,
.shape = SPRITE_SHAPE(64x64),
2018-12-23 14:52:47 +01:00
.x = 0,
.size = SPRITE_SIZE(64x64),
2018-12-23 14:52:47 +01:00
.tileNum = 0,
.priority = 2,
.paletteNum = 0,
2019-04-01 18:31:10 -04:00
.affineParam = 0,
2018-12-23 14:52:47 +01:00
};
2021-01-22 23:22:37 -05:00
const struct OamData gOamData_BattleSpritePlayerSide =
2018-12-23 14:52:47 +01:00
{
.y = 0,
2019-04-01 18:31:10 -04:00
.affineMode = ST_OAM_AFFINE_NORMAL,
.objMode = ST_OAM_OBJ_NORMAL,
.bpp = ST_OAM_4BPP,
.shape = SPRITE_SHAPE(64x64),
2018-12-23 14:52:47 +01:00
.x = 0,
.size = SPRITE_SIZE(64x64),
2018-12-23 14:52:47 +01:00
.tileNum = 0,
.priority = 2,
.paletteNum = 2,
2019-04-01 18:31:10 -04:00
.affineParam = 0,
2018-12-23 14:52:47 +01:00
};
2018-12-23 13:52:53 +01:00
2021-11-08 13:18:58 -05:00
static const s8 sCenterToCornerVecXs[8] ={-32, -16, -16, -32, -32};
2018-02-26 14:29:17 +01:00
2019-10-17 19:22:03 -04:00
const u8 gTypeNames[NUMBER_OF_MON_TYPES][TYPE_NAME_LENGTH + 1] =
2018-02-26 14:29:17 +01:00
{
[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"),
2018-02-26 14:29:17 +01:00
};
// 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},
2018-12-02 22:35:11 -05:00
{TRAINER_CLASS_TUBER_F, 1},
{TRAINER_CLASS_TUBER_M, 1},
2018-02-26 14:29:17 +01:00
{TRAINER_CLASS_SIS_AND_BRO, 3},
2018-12-02 22:35:11 -05:00
{TRAINER_CLASS_COOLTRAINER, 12},
2018-02-26 14:29:17 +01:00
{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},
2018-02-26 14:29:17 +01:00
{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},
2021-10-04 10:21:03 -04:00
{0xFF, 5}, // Any trainer class not listed above uses this
2018-02-26 14:29:17 +01:00
};
#if B_TRAINER_CLASS_POKE_BALLS >= GEN_7
static const u16 sTrainerBallTable[TRAINER_CLASS_COUNT] =
{
#if B_TRAINER_CLASS_POKE_BALLS == GEN_7
[TRAINER_CLASS_PKMN_BREEDER] = ITEM_FRIEND_BALL,
#elif B_TRAINER_CLASS_POKE_BALLS == GEN_8
[TRAINER_CLASS_PKMN_BREEDER] = ITEM_HEAL_BALL,
#endif
[TRAINER_CLASS_COOLTRAINER] = ITEM_ULTRA_BALL,
[TRAINER_CLASS_COLLECTOR] = ITEM_PREMIER_BALL,
[TRAINER_CLASS_SWIMMER_M] = ITEM_DIVE_BALL,
[TRAINER_CLASS_BLACK_BELT] = ITEM_ULTRA_BALL,
[TRAINER_CLASS_AQUA_LEADER] = ITEM_MASTER_BALL,
[TRAINER_CLASS_GENTLEMAN] = ITEM_LUXURY_BALL,
[TRAINER_CLASS_ELITE_FOUR] = ITEM_ULTRA_BALL,
#if B_TRAINER_CLASS_POKE_BALLS == GEN_7
[TRAINER_CLASS_FISHERMAN] = ITEM_LURE_BALL,
#elif B_TRAINER_CLASS_POKE_BALLS == GEN_8
[TRAINER_CLASS_FISHERMAN] = ITEM_DIVE_BALL,
#endif
[TRAINER_CLASS_SWIMMER_F] = ITEM_DIVE_BALL,
[TRAINER_CLASS_COOLTRAINER_2] = ITEM_ULTRA_BALL,
[TRAINER_CLASS_MAGMA_LEADER] = ITEM_MASTER_BALL,
};
#endif
2018-02-26 14:29:17 +01:00
#include "data/text/abilities.h"
2017-10-06 19:09:37 +02:00
static void (* const sTurnActionsFuncsTable[])(void) =
{
2021-10-04 10:21:03 -04:00
[B_ACTION_USE_MOVE] = HandleAction_UseMove,
[B_ACTION_USE_ITEM] = HandleAction_UseItem,
[B_ACTION_SWITCH] = HandleAction_Switch,
[B_ACTION_RUN] = HandleAction_Run,
2018-12-08 14:10:30 +08:00
[B_ACTION_SAFARI_WATCH_CAREFULLY] = HandleAction_WatchesCarefully,
2021-10-04 10:21:03 -04:00
[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,
2017-10-06 19:09:37 +02:00
};
static void (* const sEndTurnFuncsTable[])(void) =
{
2021-10-04 10:21:03 -04:00
[0] = HandleEndTurn_ContinueBattle,
[B_OUTCOME_WON] = HandleEndTurn_BattleWon,
[B_OUTCOME_LOST] = HandleEndTurn_BattleLost,
[B_OUTCOME_DREW] = HandleEndTurn_BattleLost,
[B_OUTCOME_RAN] = HandleEndTurn_RanFromBattle,
2018-12-08 14:10:30 +08:00
[B_OUTCOME_PLAYER_TELEPORTED] = HandleEndTurn_FinishBattle,
2021-10-04 10:21:03 -04:00
[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,
2017-10-06 19:09:37 +02:00
};
2021-10-04 10:21:03 -04:00
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[] = _("メロメロ$$$");
2017-10-06 19:09:37 +02:00
const u8 *const gStatusConditionStringsTable[][2] =
2017-10-06 19:09:37 +02:00
{
{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}
};
2017-10-01 01:12:42 +02:00
void CB2_InitBattle(void)
{
if (!gTestRunnerEnabled)
MoveSaveBlocks_ResetHeap();
2017-11-12 16:39:21 +01:00
AllocateBattleResources();
2017-10-01 01:12:42 +02:00
AllocateBattleSpritesData();
AllocateMonSpritesGfx();
2021-03-12 16:55:58 -05:00
RecordedBattle_ClearFrontierPassFlag();
2017-10-01 01:12:42 +02:00
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;
2017-10-01 01:12:42 +02:00
SetHBlankCallback(NULL);
SetVBlankCallback(NULL);
2022-07-29 10:52:35 -04:00
CpuFill32(0, (void *)(VRAM), VRAM_SIZE);
2017-10-01 01:12:42 +02:00
SetGpuReg(REG_OFFSET_MOSAIC, 0);
2021-04-15 02:04:01 -04:00
SetGpuReg(REG_OFFSET_WIN0H, DISPLAY_WIDTH);
SetGpuReg(REG_OFFSET_WIN0V, WIN_RANGE(DISPLAY_HEIGHT / 2, DISPLAY_HEIGHT / 2 + 1));
2017-10-01 01:12:42 +02:00
SetGpuReg(REG_OFFSET_WININ, 0);
SetGpuReg(REG_OFFSET_WINOUT, 0);
2021-04-15 02:04:01 -04:00
gBattle_WIN0H = DISPLAY_WIDTH;
2017-10-01 01:12:42 +02:00
2018-11-24 01:02:02 +01:00
if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && gPartnerTrainerId != TRAINER_STEVEN_PARTNER && gPartnerTrainerId < TRAINER_CUSTOM_PARTNER)
2017-10-01 01:12:42 +02:00
{
2021-04-15 02:04:01 -04:00
gBattle_WIN0V = DISPLAY_HEIGHT - 1;
gBattle_WIN1H = DISPLAY_WIDTH;
2017-10-01 01:12:42 +02:00
gBattle_WIN1V = 32;
}
else
{
2021-04-15 02:04:01 -04:00
gBattle_WIN0V = WIN_RANGE(DISPLAY_HEIGHT / 2, DISPLAY_HEIGHT / 2 + 1);
2018-01-29 17:47:12 +01:00
ScanlineEffect_Clear();
2017-10-01 01:12:42 +02:00
for (i = 0; i < DISPLAY_HEIGHT / 2; i++)
2017-10-01 01:12:42 +02:00
{
2018-01-29 17:47:12 +01:00
gScanlineEffectRegBuffers[0][i] = 0xF0;
gScanlineEffectRegBuffers[1][i] = 0xF0;
2017-10-01 01:12:42 +02:00
}
2019-01-05 19:25:46 +01:00
for (; i < DISPLAY_HEIGHT; i++)
2017-10-01 01:12:42 +02:00
{
2018-01-29 17:47:12 +01:00
gScanlineEffectRegBuffers[0][i] = 0xFF10;
gScanlineEffectRegBuffers[1][i] = 0xFF10;
2017-10-01 01:12:42 +02:00
}
2018-12-23 14:52:47 +01:00
ScanlineEffect_SetParams(sIntroScanlineParams16Bit);
2017-10-01 01:12:42 +02:00
}
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;
#if DEBUG_OVERWORLD_MENU == TRUE
Ported TheXaman's latest changes to the Debug Menu (#2815) * Added option for generating incrementing pokemon in pc boxes # Conflicts: # src/debug.c * added submenu arrows, increased menu high to full screen # Conflicts: # src/debug.c * combined flags and vars into one submenu # Conflicts: # src/debug.c * added new window to flags/vars showing the current state and added submenu indicator # Conflicts: # src/debug.c * added alligned arrows for debug submenus # Conflicts: # src/debug.c * used {CLEAR_TO X} instead of manual spaces # Conflicts: # src/debug.c * renamed gDebugText to proper sDebugText # Conflicts: # src/debug.c * added Fill submenu, added fill function for PC items and all bag pockets @LOuroboros # Conflicts: # src/debug.c * put cheat start into utility # Conflicts: # src/debug.c * put fill submenu into main menu # Conflicts: # src/debug.c * tiny fix * renaming and reordering # Conflicts: # src/debug.c * Added reset pokedex flags for @AsparagusEduardo * made flag toggle list dynamic # Conflicts: # src/debug.c * initial battle debug menu WIP # Conflicts: # src/debug.c # src/wild_encounter.c * fix visual bug * added battle start # Conflicts: # include/debug.h # src/battle_ai_script_commands.c * Added faster way to add initial movesets to mon * Added waiting music for the slow box filling * Simplified the call to scripts * Simplified debug scripts * Disabled Battle Test for now * Fixed personality on fast PC fill being always 0 * Removed BATTLE_ENGINE instances + added AI_FLAG_COUNT * Added missing return TRUE * Sets nickname * Changed how GetSpeciesName to how it's used upstream --------- Co-authored-by: TheXaman <48356183+TheXaman@users.noreply.github.com>
2023-07-18 03:17:03 -04:00
if (!gIsDebugBattle)
#endif
{
gBattleTerrain = BattleSetup_GetTerrainId();
}
2017-10-01 01:12:42 +02:00
if (gBattleTypeFlags & BATTLE_TYPE_RECORDED)
2017-12-16 01:08:55 +01:00
gBattleTerrain = BATTLE_TERRAIN_BUILDING;
2017-10-01 01:12:42 +02:00
2019-11-06 18:18:11 -06:00
InitBattleBgsVideo();
2017-10-01 01:12:42 +02:00
LoadBattleTextboxAndBackground();
ResetSpriteData();
ResetTasks();
2018-06-17 16:48:58 +02:00
DrawBattleEntryBackground();
2017-10-01 01:12:42 +02:00
FreeAllSpritePalettes();
gReservedSpritePaletteCount = MAX_BATTLERS_COUNT;
2017-10-01 01:12:42 +02:00
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 DEBUG_OVERWORLD_MENU == TRUE
Ported TheXaman's latest changes to the Debug Menu (#2815) * Added option for generating incrementing pokemon in pc boxes # Conflicts: # src/debug.c * added submenu arrows, increased menu high to full screen # Conflicts: # src/debug.c * combined flags and vars into one submenu # Conflicts: # src/debug.c * added new window to flags/vars showing the current state and added submenu indicator # Conflicts: # src/debug.c * added alligned arrows for debug submenus # Conflicts: # src/debug.c * used {CLEAR_TO X} instead of manual spaces # Conflicts: # src/debug.c * renamed gDebugText to proper sDebugText # Conflicts: # src/debug.c * added Fill submenu, added fill function for PC items and all bag pockets @LOuroboros # Conflicts: # src/debug.c * put cheat start into utility # Conflicts: # src/debug.c * put fill submenu into main menu # Conflicts: # src/debug.c * tiny fix * renaming and reordering # Conflicts: # src/debug.c * Added reset pokedex flags for @AsparagusEduardo * made flag toggle list dynamic # Conflicts: # src/debug.c * initial battle debug menu WIP # Conflicts: # src/debug.c # src/wild_encounter.c * fix visual bug * added battle start # Conflicts: # include/debug.h # src/battle_ai_script_commands.c * Added faster way to add initial movesets to mon * Added waiting music for the slow box filling * Simplified the call to scripts * Simplified debug scripts * Disabled Battle Test for now * Fixed personality on fast PC fill being always 0 * Removed BATTLE_ENGINE instances + added AI_FLAG_COUNT * Added missing return TRUE * Sets nickname * Changed how GetSpeciesName to how it's used upstream --------- Co-authored-by: TheXaman <48356183+TheXaman@users.noreply.github.com>
2023-07-18 03:17:03 -04:00
if (!gIsDebugBattle)
#endif
Ported TheXaman's latest changes to the Debug Menu (#2815) * Added option for generating incrementing pokemon in pc boxes # Conflicts: # src/debug.c * added submenu arrows, increased menu high to full screen # Conflicts: # src/debug.c * combined flags and vars into one submenu # Conflicts: # src/debug.c * added new window to flags/vars showing the current state and added submenu indicator # Conflicts: # src/debug.c * added alligned arrows for debug submenus # Conflicts: # src/debug.c * used {CLEAR_TO X} instead of manual spaces # Conflicts: # src/debug.c * renamed gDebugText to proper sDebugText # Conflicts: # src/debug.c * added Fill submenu, added fill function for PC items and all bag pockets @LOuroboros # Conflicts: # src/debug.c * put cheat start into utility # Conflicts: # src/debug.c * put fill submenu into main menu # Conflicts: # src/debug.c * tiny fix * renaming and reordering # Conflicts: # src/debug.c * Added reset pokedex flags for @AsparagusEduardo * made flag toggle list dynamic # Conflicts: # src/debug.c * initial battle debug menu WIP # Conflicts: # src/debug.c # src/wild_encounter.c * fix visual bug * added battle start # Conflicts: # include/debug.h # src/battle_ai_script_commands.c * Added faster way to add initial movesets to mon * Added waiting music for the slow box filling * Simplified the call to scripts * Simplified debug scripts * Disabled Battle Test for now * Fixed personality on fast PC fill being always 0 * Removed BATTLE_ENGINE instances + added AI_FLAG_COUNT * Added missing return TRUE * Sets nickname * Changed how GetSpeciesName to how it's used upstream --------- Co-authored-by: TheXaman <48356183+TheXaman@users.noreply.github.com>
2023-07-18 03:17:03 -04:00
{
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)
Ported TheXaman's latest changes to the Debug Menu (#2815) * Added option for generating incrementing pokemon in pc boxes # Conflicts: # src/debug.c * added submenu arrows, increased menu high to full screen # Conflicts: # src/debug.c * combined flags and vars into one submenu # Conflicts: # src/debug.c * added new window to flags/vars showing the current state and added submenu indicator # Conflicts: # src/debug.c * added alligned arrows for debug submenus # Conflicts: # src/debug.c * used {CLEAR_TO X} instead of manual spaces # Conflicts: # src/debug.c * renamed gDebugText to proper sDebugText # Conflicts: # src/debug.c * added Fill submenu, added fill function for PC items and all bag pockets @LOuroboros # Conflicts: # src/debug.c * put cheat start into utility # Conflicts: # src/debug.c * put fill submenu into main menu # Conflicts: # src/debug.c * tiny fix * renaming and reordering # Conflicts: # src/debug.c * Added reset pokedex flags for @AsparagusEduardo * made flag toggle list dynamic # Conflicts: # src/debug.c * initial battle debug menu WIP # Conflicts: # src/debug.c # src/wild_encounter.c * fix visual bug * added battle start # Conflicts: # include/debug.h # src/battle_ai_script_commands.c * Added faster way to add initial movesets to mon * Added waiting music for the slow box filling * Simplified the call to scripts * Simplified debug scripts * Disabled Battle Test for now * Fixed personality on fast PC fill being always 0 * Removed BATTLE_ENGINE instances + added AI_FLAG_COUNT * Added missing return TRUE * Sets nickname * Changed how GetSpeciesName to how it's used upstream --------- Co-authored-by: TheXaman <48356183+TheXaman@users.noreply.github.com>
2023-07-18 03:17:03 -04:00
CreateNPCTrainerParty(&gEnemyParty[PARTY_SIZE / 2], gTrainerBattleOpponent_B, FALSE);
SetWildMonHeldItem();
CalculateEnemyPartyCount();
Ported TheXaman's latest changes to the Debug Menu (#2815) * Added option for generating incrementing pokemon in pc boxes # Conflicts: # src/debug.c * added submenu arrows, increased menu high to full screen # Conflicts: # src/debug.c * combined flags and vars into one submenu # Conflicts: # src/debug.c * added new window to flags/vars showing the current state and added submenu indicator # Conflicts: # src/debug.c * added alligned arrows for debug submenus # Conflicts: # src/debug.c * used {CLEAR_TO X} instead of manual spaces # Conflicts: # src/debug.c * renamed gDebugText to proper sDebugText # Conflicts: # src/debug.c * added Fill submenu, added fill function for PC items and all bag pockets @LOuroboros # Conflicts: # src/debug.c * put cheat start into utility # Conflicts: # src/debug.c * put fill submenu into main menu # Conflicts: # src/debug.c * tiny fix * renaming and reordering # Conflicts: # src/debug.c * Added reset pokedex flags for @AsparagusEduardo * made flag toggle list dynamic # Conflicts: # src/debug.c * initial battle debug menu WIP # Conflicts: # src/debug.c # src/wild_encounter.c * fix visual bug * added battle start # Conflicts: # include/debug.h # src/battle_ai_script_commands.c * Added faster way to add initial movesets to mon * Added waiting music for the slow box filling * Simplified the call to scripts * Simplified debug scripts * Disabled Battle Test for now * Fixed personality on fast PC fill being always 0 * Removed BATTLE_ENGINE instances + added AI_FLAG_COUNT * Added missing return TRUE * Sets nickname * Changed how GetSpeciesName to how it's used upstream --------- Co-authored-by: TheXaman <48356183+TheXaman@users.noreply.github.com>
2023-07-18 03:17:03 -04:00
}
}
2017-10-01 01:12:42 +02:00
gMain.inBattle = TRUE;
2019-12-14 03:58:20 -05:00
gSaveBlock2Ptr->frontier.disableRecordBattle = FALSE;
2017-10-01 01:12:42 +02:00
2018-06-20 23:07:51 +02:00
for (i = 0; i < PARTY_SIZE; i++)
2020-02-02 13:28:54 -05:00
AdjustFriendship(&gPlayerParty[i], FRIENDSHIP_EVENT_LEAGUE_BATTLE);
2017-10-01 01:12:42 +02:00
// Apply party-wide start-of-battle form changes
for (i = 0; i < PARTY_SIZE; i++)
{
// Player's side
TryFormChange(i, B_SIDE_PLAYER, FORM_CHANGE_BEGIN_BATTLE);
// Opponent's side
TryFormChange(i, B_SIDE_OPPONENT, FORM_CHANGE_BEGIN_BATTLE);
}
2017-10-01 01:12:42 +02:00
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_SPECIES_OR_EGG); \
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; \
2021-01-22 23:22:37 -05:00
}
// For Vs Screen at link battle start
static void BufferPartyVsScreenHealth_AtStart(void)
2017-10-01 01:12:42 +02:00
{
2021-01-22 23:22:37 -05:00
u16 flags = 0;
2017-10-01 01:12:42 +02:00
s32 i;
2021-01-22 23:22:37 -05:00
BUFFER_PARTY_VS_SCREEN_STATUS(gPlayerParty, flags, i);
2021-03-15 15:22:41 -04:00
gBattleStruct->multiBuffer.linkBattlerHeader.vsScreenHealthFlagsLo = flags;
*(&gBattleStruct->multiBuffer.linkBattlerHeader.vsScreenHealthFlagsHi) = flags >> 8;
gBattleStruct->multiBuffer.linkBattlerHeader.vsScreenHealthFlagsHi |= FlagGet(FLAG_SYS_FRONTIER_PASS) << 7;
2017-10-01 01:12:42 +02:00
}
static void SetPlayerBerryDataInBattleStruct(void)
{
s32 i;
struct BattleStruct *battleStruct = gBattleStruct;
2021-03-15 15:22:41 -04:00
struct BattleEnigmaBerry *battleBerry = &battleStruct->multiBuffer.linkBattlerHeader.battleEnigmaBerry;
2017-10-01 01:12:42 +02:00
if (IsEnigmaBerryValid() == TRUE)
{
2018-09-01 22:03:21 +02:00
for (i = 0; i < BERRY_NAME_LENGTH; i++)
2017-10-01 01:12:42 +02:00
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));
2017-10-01 01:12:42 +02:00
2018-09-01 22:03:21 +02:00
for (i = 0; i < BERRY_NAME_LENGTH; i++)
2017-10-01 01:12:42 +02:00
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)
{
2022-08-15 18:27:37 -04:00
s32 i, j;
2017-10-01 01:12:42 +02:00
if (!(gBattleTypeFlags & BATTLE_TYPE_LINK))
{
if (IsEnigmaBerryValid() == TRUE)
{
2018-09-01 22:03:21 +02:00
for (i = 0; i < BERRY_NAME_LENGTH; i++)
2017-10-01 01:12:42 +02:00
{
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));
2017-10-01 01:12:42 +02:00
2018-09-01 22:03:21 +02:00
for (i = 0; i < BERRY_NAME_LENGTH; i++)
2017-10-01 01:12:42 +02:00
{
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;
2017-10-01 01:12:42 +02:00
gEnigmaBerries[0].holdEffectParam = 0;
gEnigmaBerries[2].holdEffectParam = 0;
}
}
else
{
s32 numPlayers;
struct BattleEnigmaBerry *src;
2023-09-14 11:05:00 +02:00
u32 battler;
2017-10-01 01:12:42 +02:00
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);
2023-08-30 10:18:31 +02:00
battler = gLinkPlayers[i].id;
2017-10-01 01:12:42 +02:00
2018-09-01 22:03:21 +02:00
for (j = 0; j < BERRY_NAME_LENGTH; j++)
2023-08-30 10:18:31 +02:00
gEnigmaBerries[battler].name[j] = src->name[j];
gEnigmaBerries[battler].name[j] = EOS;
2017-10-01 01:12:42 +02:00
for (j = 0; j < BERRY_ITEM_EFFECT_COUNT; j++)
2023-08-30 10:18:31 +02:00
gEnigmaBerries[battler].itemEffect[j] = src->itemEffect[j];
2017-10-01 01:12:42 +02:00
2023-08-30 10:18:31 +02:00
gEnigmaBerries[battler].holdEffect = src->holdEffect;
gEnigmaBerries[battler].holdEffectParam = src->holdEffectParam;
2017-10-01 01:12:42 +02:00
}
}
else
{
for (i = 0; i < 2; i++)
{
src = (struct BattleEnigmaBerry *)(gBlockRecvBuffer[i] + 2);
2018-09-01 22:03:21 +02:00
for (j = 0; j < BERRY_NAME_LENGTH; j++)
2017-10-01 01:12:42 +02:00
{
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;
}
}
}
}
2021-03-12 16:55:58 -05:00
// This was inlined in Ruby/Sapphire
static void FindLinkBattleMaster(u8 numPlayers, u8 multiPlayerId)
2017-10-01 01:12:42 +02:00
{
2021-03-12 16:55:58 -05:00
u8 found = 0;
2017-10-01 01:12:42 +02:00
2021-03-12 16:55:58 -05:00
// If player 1 is playing the minimum version, player 1 is master.
if (gBlockRecvBuffer[0][0] == 0x100)
2017-10-01 01:12:42 +02:00
{
2021-03-12 16:55:58 -05:00
if (multiPlayerId == 0)
gBattleTypeFlags |= BATTLE_TYPE_IS_MASTER | BATTLE_TYPE_TRAINER;
2017-10-01 01:12:42 +02:00
else
gBattleTypeFlags |= BATTLE_TYPE_TRAINER;
2021-03-12 16:55:58 -05:00
found++;
2017-10-01 01:12:42 +02:00
}
2021-03-12 16:55:58 -05:00
if (found == 0)
2017-10-01 01:12:42 +02:00
{
2021-03-12 16:55:58 -05:00
// If multiple different versions are being used, player 1 is master.
2017-10-01 01:12:42 +02:00
s32 i;
2021-03-12 16:55:58 -05:00
for (i = 0; i < numPlayers; i++)
2017-10-01 01:12:42 +02:00
{
if (gBlockRecvBuffer[0][0] != gBlockRecvBuffer[i][0])
break;
}
2021-03-12 16:55:58 -05:00
if (i == numPlayers)
2017-10-01 01:12:42 +02:00
{
2021-03-12 16:55:58 -05:00
if (multiPlayerId == 0)
gBattleTypeFlags |= BATTLE_TYPE_IS_MASTER | BATTLE_TYPE_TRAINER;
2017-10-01 01:12:42 +02:00
else
gBattleTypeFlags |= BATTLE_TYPE_TRAINER;
2021-03-12 16:55:58 -05:00
found++;
2017-10-01 01:12:42 +02:00
}
2021-03-12 16:55:58 -05:00
if (found == 0)
2017-10-01 01:12:42 +02:00
{
2021-03-12 16:55:58 -05:00
// Lowest index player with the highest game version is master.
for (i = 0; i < numPlayers; i++)
2017-10-01 01:12:42 +02:00
{
2021-03-12 16:55:58 -05:00
if (gBlockRecvBuffer[i][0] == 0x300 && i != multiPlayerId)
2017-10-01 01:12:42 +02:00
{
2021-03-12 16:55:58 -05:00
if (i < multiPlayerId)
2017-10-01 01:12:42 +02:00
break;
}
2021-03-12 16:55:58 -05:00
if (gBlockRecvBuffer[i][0] > 0x300 && i != multiPlayerId)
2017-10-01 01:12:42 +02:00
break;
}
2021-03-12 16:55:58 -05:00
if (i == numPlayers)
gBattleTypeFlags |= BATTLE_TYPE_IS_MASTER | BATTLE_TYPE_TRAINER;
2017-10-01 01:12:42 +02:00
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);
2021-09-24 14:30:15 -04:00
FillAroundBattleWindows();
2017-10-01 01:12:42 +02:00
gBattleCommunication[MULTIUSE_STATE] = 1;
}
2017-11-12 23:58:05 -05:00
if (gWirelessCommType)
2019-04-04 17:05:46 -04:00
LoadWirelessStatusIndicatorSpriteGfx();
2017-10-01 01:12:42 +02:00
break;
case 1:
if (gBattleTypeFlags & BATTLE_TYPE_LINK)
{
if (gReceivedRemoteLinkPlayers)
2017-10-01 01:12:42 +02:00
{
2018-12-31 02:22:21 -06:00
if (IsLinkTaskFinished())
2017-10-01 01:12:42 +02:00
{
2021-03-12 16:55:58 -05:00
// 0x300
2021-03-15 15:22:41 -04:00
*(&gBattleStruct->multiBuffer.linkBattlerHeader.versionSignatureLo) = 0;
*(&gBattleStruct->multiBuffer.linkBattlerHeader.versionSignatureHi) = 3;
2021-01-22 23:22:37 -05:00
BufferPartyVsScreenHealth_AtStart();
2017-10-01 01:12:42 +02:00
SetPlayerBerryDataInBattleStruct();
2020-06-03 15:28:29 -04:00
if (gTrainerBattleOpponent_A == TRAINER_UNION_ROOM)
2017-10-01 01:12:42 +02:00
{
2018-07-22 12:49:49 +02:00
gLinkPlayers[0].id = 0;
gLinkPlayers[1].id = 1;
2017-10-01 01:12:42 +02:00
}
SendBlock(BitmaskAllOtherLinkPlayers(), &gBattleStruct->multiBuffer.linkBattlerHeader, sizeof(gBattleStruct->multiBuffer.linkBattlerHeader));
2017-10-01 01:12:42 +02:00
gBattleCommunication[MULTIUSE_STATE] = 2;
}
2017-11-12 23:58:05 -05:00
if (gWirelessCommType)
2017-12-03 22:01:06 -05:00
CreateWirelessStatusIndicatorSprite(0, 0);
2017-10-01 01:12:42 +02:00
}
}
else
{
if (!(gBattleTypeFlags & BATTLE_TYPE_RECORDED))
gBattleTypeFlags |= BATTLE_TYPE_IS_MASTER;
2017-10-01 01:12:42 +02:00
gBattleCommunication[MULTIUSE_STATE] = 15;
SetAllPlayersBerryData();
}
break;
case 2:
if ((GetBlockReceivedStatus() & 3) == 3)
{
u8 taskId;
ResetBlockReceivedFlags();
2021-03-12 16:55:58 -05:00
FindLinkBattleMaster(2, playerMultiplayerId);
2017-10-01 01:12:42 +02:00
SetAllPlayersBerryData();
2019-11-06 18:18:11 -06:00
taskId = CreateTask(InitLinkBattleVsScreen, 0);
2017-10-01 01:12:42 +02:00
gTasks[taskId].data[1] = 0x10E;
gTasks[taskId].data[2] = 0x5A;
gTasks[taskId].data[5] = 0;
2021-03-15 15:22:41 -04:00
gTasks[taskId].data[3] = gBattleStruct->multiBuffer.linkBattlerHeader.vsScreenHealthFlagsLo | (gBattleStruct->multiBuffer.linkBattlerHeader.vsScreenHealthFlagsHi << 8);
2017-10-01 01:12:42 +02:00
gTasks[taskId].data[4] = gBlockRecvBuffer[enemyMultiplayerId][1];
2021-03-12 16:55:58 -05:00
RecordedBattle_SetFrontierPassFlagFromHword(gBlockRecvBuffer[playerMultiplayerId][1]);
RecordedBattle_SetFrontierPassFlagFromHword(gBlockRecvBuffer[enemyMultiplayerId][1]);
2017-10-01 01:12:42 +02:00
gBattleCommunication[MULTIUSE_STATE]++;
}
break;
case 3:
2021-10-04 10:21:03 -04:00
// Link battle, send/receive party Pokémon 2 at a time
2018-12-31 02:22:21 -06:00
if (IsLinkTaskFinished())
2017-10-01 01:12:42 +02:00
{
2021-10-04 10:21:03 -04:00
// Send Pokémon 1-2
SendBlock(BitmaskAllOtherLinkPlayers(), gPlayerParty, sizeof(struct Pokemon) * 2);
2017-10-01 01:12:42 +02:00
gBattleCommunication[MULTIUSE_STATE]++;
}
break;
case 4:
if ((GetBlockReceivedStatus() & 3) == 3)
{
2021-10-04 10:21:03 -04:00
// Recv Pokémon 1-2
2017-10-01 01:12:42 +02:00
ResetBlockReceivedFlags();
memcpy(gEnemyParty, gBlockRecvBuffer[enemyMultiplayerId], sizeof(struct Pokemon) * 2);
gBattleCommunication[MULTIUSE_STATE]++;
}
break;
case 7:
2018-12-31 02:22:21 -06:00
if (IsLinkTaskFinished())
2017-10-01 01:12:42 +02:00
{
2021-10-04 10:21:03 -04:00
// Send Pokémon 3-4
SendBlock(BitmaskAllOtherLinkPlayers(), &gPlayerParty[2], sizeof(struct Pokemon) * 2);
2017-10-01 01:12:42 +02:00
gBattleCommunication[MULTIUSE_STATE]++;
}
break;
case 8:
if ((GetBlockReceivedStatus() & 3) == 3)
{
2021-10-04 10:21:03 -04:00
// Recv Pokémon 3-4
2017-10-01 01:12:42 +02:00
ResetBlockReceivedFlags();
2021-10-04 10:21:03 -04:00
memcpy(&gEnemyParty[2], gBlockRecvBuffer[enemyMultiplayerId], sizeof(struct Pokemon) * 2);
2017-10-01 01:12:42 +02:00
gBattleCommunication[MULTIUSE_STATE]++;
}
break;
case 11:
2018-12-31 02:22:21 -06:00
if (IsLinkTaskFinished())
2017-10-01 01:12:42 +02:00
{
2021-10-04 10:21:03 -04:00
// Send Pokémon 5-6
SendBlock(BitmaskAllOtherLinkPlayers(), &gPlayerParty[4], sizeof(struct Pokemon) * 2);
2017-10-01 01:12:42 +02:00
gBattleCommunication[MULTIUSE_STATE]++;
}
break;
case 12:
if ((GetBlockReceivedStatus() & 3) == 3)
{
2021-10-04 10:21:03 -04:00
// Recv Pokémon 5-6
2017-10-01 01:12:42 +02:00
ResetBlockReceivedFlags();
2021-10-04 10:21:03 -04:00
memcpy(&gEnemyParty[4], gBlockRecvBuffer[enemyMultiplayerId], sizeof(struct Pokemon) * 2);
2017-10-01 18:54:01 +02:00
TryCorrectShedinjaLanguage(&gEnemyParty[0]);
TryCorrectShedinjaLanguage(&gEnemyParty[1]);
TryCorrectShedinjaLanguage(&gEnemyParty[2]);
TryCorrectShedinjaLanguage(&gEnemyParty[3]);
TryCorrectShedinjaLanguage(&gEnemyParty[4]);
TryCorrectShedinjaLanguage(&gEnemyParty[5]);
2017-10-01 01:12:42 +02:00
gBattleCommunication[MULTIUSE_STATE]++;
}
break;
case 15:
InitBattleControllers();
2021-10-04 10:21:03 -04:00
RecordedBattle_SetTrainerInfo();
2017-10-01 01:12:42 +02:00
gBattleCommunication[SPRITES_INIT_STATE1] = 0;
gBattleCommunication[SPRITES_INIT_STATE2] = 0;
if (gBattleTypeFlags & BATTLE_TYPE_LINK)
{
2021-10-04 10:21:03 -04:00
// Check if both players are using Emerald
// to determine if the recorded battle rng
// seed needs to be sent
2017-10-01 01:12:42 +02:00
s32 i;
Decompile TV (#80) * ClearTVShowData * special_0x44 * DoTVShow (nonmatching because align) * DoTVShowBravoTrainerPokemonProfile * Update field names * DoTVShowBravoTrainerBattleTower * Renaming of struct fields * sub_80EBFF4 and UpdateTVScreensOnMap * SetTVMetatilesOnMap * Power buttons for the TV screens on the map * special_0x45 * sub_80EC18C * special_0x4a * ResetGabbyAndTy * GabbyAndTyBeforeInterview * GabbyAndTyAfterInterview * Through IsTVShowInSearchOfTrainersAiring * GabbyAndTyGetLastQuote * GabbyAndTyGetLastBattleTrivia * GabbyAndTySetScriptVarsToFieldObjectLocalIds * InterviewAfter; use TVShow as a precursor for making the individual show structs anonymous * Make TV structs anonymous within the union * Move the TV union to its own subheader * Move TV show enums to the global.tv.h subheader * Funcion renaming * Apply static attributes where able * PutPokemonTodayCaughtOnAir * sub_80EC8A4 * PutPokemonTodayFailedOnTheAir * sub_80EC9E8, sub_80ECA10 * sub_80ECA38 * sub_80ECB00 * Put3CheersForPokeblocksOnTheAir * PutFanClubSpecialOnTheAir * ContestLiveUpdates_BeforeInterview * Other before-interview Contest Live Updates functions * ContestLiveUpdates_BeforeInterview_5 * InterviewAfter_BravoTrainerPokemonProfile * BravoTrainerPokemonProfile_BeforeInterview1 * BravoTrainerPokemonProfile_BeforeInterview2 * Disassemble TV data * Decompile TV data * InterviewAfter_BravoTrainerBattleTowerProfile * SaveRecordedItemPurchasesForTVShow * PutNameRaterShowOnTheAir * StartMassOutbreak * PutLilycoveContestLadyShowOnTheAir * InterviewAfter_FanClubLetter * Rip TV strings * InterviewAfter_RecentHappenings * InterviewAfter_PkmnFanClubOpinions * sub_80ED718 * EndMassOutbreak * sub_80ED888 * sub_80ED8B4 * UpdateMassOutbreakTimeLeft * sub_80ED950 * PutFishingAdviceShowOnTheAir * through sub_80EDA80 * ewram and common syms are now fetched from the object files * BSS symbols are taken from the tv.o file * through sub_80EDC60 * sub_80EDCE8 * sub_80EDD78 * through sub_80EDE84 * nomatching sub_80EDE98 * sub_80EDFB4 * sub_80EE104 * sub_80EE104 * sub_80EE184 * sub_80EE2CC * sub_80EE35C * sub_80EE44C * sub_80EE4DC * sub_80EE5A4 * sub_80EE69C * sub_80EE72C * sub_80EE7C0 * sub_80EE818 * sub_80EE8C8 * sub_80EEA70 * sub_80EEB98 * sub_80EEBF4 * through sub_80EED60 * Functions relating to Pokemon News * sub_80EEF6C * GetPriceReduction * IsPriceDiscounted * sub_80EF120 * through sub_80EF370 * sub_80EF40C * HasMixableShowAlreadyBeenSpawnedWithPlayerID * TV_SortPurchasesByQuantity * FindActiveBroadcastByShowType_SetScriptResult * InterviewBefore * through sub_80EF88C * through sub_80EF93C * through sub_80EFA24 * through TV_BernoulliTrial * sub_80EFB58 * sub_80EFBA4 * sub_80EFBDC * through sub_80EFD98 * ChangePokemonNickname * ChangeBoxPokemonNickname * sub_80EFF9C * through player_id_to_dword * CheckForBigMovieOrEmergencyNewsOnTV * GetMomOrDadStringForTVMessage * sub_80F01E8 * sub_80F0358 * sub_80F049C * TV record mixing functions * sub_80F06D0 * sub_80F0708 nonmatching * through sub_80F0B24 * sub_80F0B64 * through sub_80F0C04 * sub_80F0C7C * sub_80F0D60 * sub_80F0E58 * sub_80F0E84 * through sub_80F0F24 * sub_80F0F64 * sub_80F1208 * sub_80F1254 * sub_80F1290 * sub_80F12A4 * sub_80F14F8 * DoTVShowTodaysSmartShopper * DoTVShowTheNameRaterShow * DoTVShowPokemonTodaySuccessfulCapture * DoTVShowPokemonTodayFailedCapture * DoTVShowPokemonFanClubLetter * DoTVShowRecentHappenings * DoTVShowPokemonFanClubOpinions * DoTVShowPokemonNewsMassOutbreak * DoTVShowPokemonContestLiveUpdates * DoTVShowPokemonBattleUpdate * DoTVShow3CheersForPokeblocks * DoTVShowInSearchOfTrainers * Label GabbyAndTyData fields; remove ddump comments from data/text/tv.inc * DoTVShowPokemonAngler * DoTVShowTheWorldOfMasters; update RAM symbols and field names * Decorate static functions * DoTVShowTodaysRivalTrainer; region map enums * TVDewfordTrendWatcherNetworkTextGroup * DoTVShowHoennTreasureInvestigators * DoTVShowFindThatGamer * DoTVShowBreakingNewsTV * DoTVShowSecretBaseVisit * DoTVShowPokemonLotterWinnerFlashReport * DoTVShowThePokemonBattleSeminar * DoTVShowTrainerFanClubSpecial, DoTVShowTrainerFanClub * DoTVShowSpotTheCuties * DoTVShowPokemonNewsBattleFrontier * DoTVShowWhatsNo1InHoennToday * Helpers for DoTVShowSecretBaseSecrets * DoTVShowSecretBaseSecrets * DoTVShowSafariFanClub * Finish decompilation of tv.s * Some renaming * Rename text group pointers * revoke statis; pokenews enums * Labels are number one * Label all TV struct fields * Make data/text/tv.inc more readable * Split data/text/tv.inc * Rename pokenews text pointers * Frontier Symbol constants; indicate static rodata objects with 's' prefix * Fix leading spaces/tabs F*** CLion sometimes * Fix inconsequential warning
2017-10-13 11:09:36 -04:00
for (i = 0; i < 2 && (gLinkPlayers[i].version & 0xFF) == VERSION_EMERALD; i++);
2017-10-01 01:12:42 +02:00
if (i == 2)
gBattleCommunication[MULTIUSE_STATE] = 16;
else
gBattleCommunication[MULTIUSE_STATE] = 18;
}
else
{
gBattleCommunication[MULTIUSE_STATE] = 18;
}
break;
case 16:
2021-10-04 10:21:03 -04:00
// Both players are using Emerald, send rng seed for recorded battle
2018-12-31 02:22:21 -06:00
if (IsLinkTaskFinished())
2017-10-01 01:12:42 +02:00
{
SendBlock(BitmaskAllOtherLinkPlayers(), &gRecordedBattleRngSeed, sizeof(gRecordedBattleRngSeed));
2017-10-01 01:12:42 +02:00
gBattleCommunication[MULTIUSE_STATE]++;
}
break;
case 17:
2021-10-04 10:21:03 -04:00
// Receive rng seed for recorded battle (only read it if partner is the link master)
2017-10-01 01:12:42 +02:00
if ((GetBlockReceivedStatus() & 3) == 3)
{
ResetBlockReceivedFlags();
if (!(gBattleTypeFlags & BATTLE_TYPE_IS_MASTER))
2017-10-01 01:12:42 +02:00
memcpy(&gRecordedBattleRngSeed, gBlockRecvBuffer[enemyMultiplayerId], sizeof(gRecordedBattleRngSeed));
gBattleCommunication[MULTIUSE_STATE]++;
}
break;
case 18:
2021-10-04 10:21:03 -04:00
// Finish, start battle
2017-10-01 01:12:42 +02:00
if (BattleInitAllSprites(&gBattleCommunication[SPRITES_INIT_STATE1], &gBattleCommunication[SPRITES_INIT_STATE2]))
{
gPreBattleCallback1 = gMain.callback1;
gMain.callback1 = BattleMainCB1;
SetMainCallback2(BattleMainCB2);
if (gBattleTypeFlags & BATTLE_TYPE_LINK)
2021-01-13 15:17:32 -05:00
gBattleTypeFlags |= BATTLE_TYPE_LINK_IN_BATTLE;
2017-10-01 01:12:42 +02:00
}
break;
2021-10-04 10:21:03 -04:00
// Introduce short delays between sending party Pokémon for link
2017-10-01 01:12:42 +02:00
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;
2021-10-04 10:21:03 -04:00
u8 partnerMultiplayerId;
2017-10-01 01:12:42 +02:00
RunTasks();
AnimateSprites();
BuildOamBuffer();
playerMultiplayerId = GetMultiplayerId();
gBattleScripting.multiplayerId = playerMultiplayerId;
2021-10-04 10:21:03 -04:00
partnerMultiplayerId = playerMultiplayerId ^ BIT_SIDE;
2017-10-01 01:12:42 +02:00
switch (gBattleCommunication[MULTIUSE_STATE])
{
case 0:
if (!IsDma3ManagerBusyWithBgCopy())
{
ShowBg(0);
ShowBg(1);
ShowBg(2);
ShowBg(3);
2021-09-24 14:30:15 -04:00
FillAroundBattleWindows();
2017-10-01 01:12:42 +02:00
gBattleCommunication[MULTIUSE_STATE] = 1;
}
2017-11-12 23:58:05 -05:00
if (gWirelessCommType)
2019-04-04 17:05:46 -04:00
LoadWirelessStatusIndicatorSpriteGfx();
2017-10-01 01:12:42 +02:00
// fall through
case 1:
if (gBattleTypeFlags & BATTLE_TYPE_LINK)
{
if (gReceivedRemoteLinkPlayers)
2017-10-01 01:12:42 +02:00
{
u8 language;
2018-07-22 12:49:49 +02:00
gLinkPlayers[0].id = 0;
gLinkPlayers[1].id = 2;
gLinkPlayers[2].id = 1;
gLinkPlayers[3].id = 3;
2017-10-01 01:12:42 +02:00
GetFrontierTrainerName(gLinkPlayers[2].name, gTrainerBattleOpponent_A);
GetFrontierTrainerName(gLinkPlayers[3].name, gTrainerBattleOpponent_B);
GetBattleTowerTrainerLanguage(&language, gTrainerBattleOpponent_A);
2017-10-01 01:12:42 +02:00
gLinkPlayers[2].language = language;
GetBattleTowerTrainerLanguage(&language, gTrainerBattleOpponent_B);
2017-10-01 01:12:42 +02:00
gLinkPlayers[3].language = language;
2018-12-31 02:22:21 -06:00
if (IsLinkTaskFinished())
2017-10-01 01:12:42 +02:00
{
2021-03-12 16:55:58 -05:00
// 0x300
2021-03-15 15:22:41 -04:00
*(&gBattleStruct->multiBuffer.linkBattlerHeader.versionSignatureLo) = 0;
*(&gBattleStruct->multiBuffer.linkBattlerHeader.versionSignatureHi) = 3;
2021-01-22 23:22:37 -05:00
BufferPartyVsScreenHealth_AtStart();
2017-10-01 01:12:42 +02:00
SetPlayerBerryDataInBattleStruct();
SendBlock(BitmaskAllOtherLinkPlayers(), &gBattleStruct->multiBuffer.linkBattlerHeader, sizeof(gBattleStruct->multiBuffer.linkBattlerHeader));
2017-10-01 01:12:42 +02:00
gBattleCommunication[MULTIUSE_STATE] = 2;
}
2017-11-12 23:58:05 -05:00
if (gWirelessCommType)
2017-12-03 22:01:06 -05:00
CreateWirelessStatusIndicatorSprite(0, 0);
2017-10-01 01:12:42 +02:00
}
}
else
{
if (!(gBattleTypeFlags & BATTLE_TYPE_RECORDED))
gBattleTypeFlags |= BATTLE_TYPE_IS_MASTER;
2017-10-01 01:12:42 +02:00
gBattleCommunication[MULTIUSE_STATE] = 13;
SetAllPlayersBerryData();
}
break;
case 2:
if ((GetBlockReceivedStatus() & 3) == 3)
{
u8 taskId;
ResetBlockReceivedFlags();
2021-03-12 16:55:58 -05:00
FindLinkBattleMaster(2, playerMultiplayerId);
2017-10-01 01:12:42 +02:00
SetAllPlayersBerryData();
2019-11-06 18:18:11 -06:00
taskId = CreateTask(InitLinkBattleVsScreen, 0);
2017-10-01 01:12:42 +02:00
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:
2021-10-04 10:21:03 -04:00
// Link battle, send/receive party Pokémon in groups
2018-12-31 02:22:21 -06:00
if (IsLinkTaskFinished())
2017-10-01 01:12:42 +02:00
{
2021-10-04 10:21:03 -04:00
// Send Pokémon 1-2
SendBlock(BitmaskAllOtherLinkPlayers(), gPlayerParty, sizeof(struct Pokemon) * 2);
2017-10-01 01:12:42 +02:00
gBattleCommunication[MULTIUSE_STATE]++;
}
break;
case 4:
if ((GetBlockReceivedStatus() & 3) == 3)
{
2021-10-04 10:21:03 -04:00
// Recv partner's Pokémon 1-2, and copy partner's and own Pokémon into party positions
2017-10-01 01:12:42 +02:00
ResetBlockReceivedFlags();
2018-07-22 12:49:49 +02:00
if (gLinkPlayers[playerMultiplayerId].id != 0)
2017-10-01 01:12:42 +02:00
{
2021-10-04 10:21:03 -04:00
memcpy(gPlayerParty, gBlockRecvBuffer[partnerMultiplayerId], sizeof(struct Pokemon) * 2);
memcpy(&gPlayerParty[MULTI_PARTY_SIZE], gBlockRecvBuffer[playerMultiplayerId], sizeof(struct Pokemon) * 2);
2017-10-01 01:12:42 +02:00
}
else
{
memcpy(gPlayerParty, gBlockRecvBuffer[playerMultiplayerId], sizeof(struct Pokemon) * 2);
2021-10-04 10:21:03 -04:00
memcpy(&gPlayerParty[MULTI_PARTY_SIZE], gBlockRecvBuffer[partnerMultiplayerId], sizeof(struct Pokemon) * 2);
2017-10-01 01:12:42 +02:00
}
gBattleCommunication[MULTIUSE_STATE]++;
}
break;
case 5:
2018-12-31 02:22:21 -06:00
if (IsLinkTaskFinished())
2017-10-01 01:12:42 +02:00
{
2021-10-04 10:21:03 -04:00
// Send Pokémon 3
SendBlock(BitmaskAllOtherLinkPlayers(), &gPlayerParty[2], sizeof(struct Pokemon));
2017-10-01 01:12:42 +02:00
gBattleCommunication[MULTIUSE_STATE]++;
}
break;
case 6:
if ((GetBlockReceivedStatus() & 3) == 3)
{
2021-10-04 10:21:03 -04:00
// Recv partner's Pokémon 3, and copy partner's and own Pokémon into party positions
2017-10-01 01:12:42 +02:00
ResetBlockReceivedFlags();
2018-07-22 12:49:49 +02:00
if (gLinkPlayers[playerMultiplayerId].id != 0)
2017-10-01 01:12:42 +02:00
{
2021-10-04 10:21:03 -04:00
memcpy(&gPlayerParty[2], gBlockRecvBuffer[partnerMultiplayerId], sizeof(struct Pokemon));
memcpy(&gPlayerParty[2 + MULTI_PARTY_SIZE], gBlockRecvBuffer[playerMultiplayerId], sizeof(struct Pokemon));
2017-10-01 01:12:42 +02:00
}
else
{
2021-10-04 10:21:03 -04:00
memcpy(&gPlayerParty[2], gBlockRecvBuffer[playerMultiplayerId], sizeof(struct Pokemon));
memcpy(&gPlayerParty[2 + MULTI_PARTY_SIZE], gBlockRecvBuffer[partnerMultiplayerId], sizeof(struct Pokemon));
2017-10-01 01:12:42 +02:00
}
gBattleCommunication[MULTIUSE_STATE]++;
}
break;
case 7:
2018-12-31 02:22:21 -06:00
if (IsLinkTaskFinished())
2017-10-01 01:12:42 +02:00
{
2021-10-04 10:21:03 -04:00
// Send enemy Pokémon 1-2 to partner
SendBlock(BitmaskAllOtherLinkPlayers(), gEnemyParty, sizeof(struct Pokemon) * 2);
2017-10-01 01:12:42 +02:00
gBattleCommunication[MULTIUSE_STATE]++;
}
break;
case 8:
if ((GetBlockReceivedStatus() & 3) == 3)
{
2021-10-04 10:21:03 -04:00
// Recv enemy Pokémon 1-2 (if not master)
2017-10-01 01:12:42 +02:00
ResetBlockReceivedFlags();
if (GetMultiplayerId() != 0)
memcpy(gEnemyParty, gBlockRecvBuffer[0], sizeof(struct Pokemon) * 2);
gBattleCommunication[MULTIUSE_STATE]++;
}
break;
case 9:
2018-12-31 02:22:21 -06:00
if (IsLinkTaskFinished())
2017-10-01 01:12:42 +02:00
{
2021-10-04 10:21:03 -04:00
// Send enemy Pokémon 3-4 to partner
SendBlock(BitmaskAllOtherLinkPlayers(), &gEnemyParty[2], sizeof(struct Pokemon) * 2);
2017-10-01 01:12:42 +02:00
gBattleCommunication[MULTIUSE_STATE]++;
}
break;
case 10:
if ((GetBlockReceivedStatus() & 3) == 3)
{
2021-10-04 10:21:03 -04:00
// Recv enemy Pokémon 3-4 (if not master)
2017-10-01 01:12:42 +02:00
ResetBlockReceivedFlags();
if (GetMultiplayerId() != 0)
2021-10-04 10:21:03 -04:00
memcpy(&gEnemyParty[2], gBlockRecvBuffer[0], sizeof(struct Pokemon) * 2);
2017-10-01 01:12:42 +02:00
gBattleCommunication[MULTIUSE_STATE]++;
}
break;
case 11:
2018-12-31 02:22:21 -06:00
if (IsLinkTaskFinished())
2017-10-01 01:12:42 +02:00
{
2021-10-04 10:21:03 -04:00
// Send enemy Pokémon 5-6 to partner
SendBlock(BitmaskAllOtherLinkPlayers(), &gEnemyParty[4], sizeof(struct Pokemon) * 2);
2017-10-01 01:12:42 +02:00
gBattleCommunication[MULTIUSE_STATE]++;
}
break;
case 12:
if ((GetBlockReceivedStatus() & 3) == 3)
{
2021-10-04 10:21:03 -04:00
// Recv enemy Pokémon 5-6 (if not master)
2017-10-01 01:12:42 +02:00
ResetBlockReceivedFlags();
if (GetMultiplayerId() != 0)
2021-10-04 10:21:03 -04:00
memcpy(&gEnemyParty[4], gBlockRecvBuffer[0], sizeof(struct Pokemon) * 2);
2017-10-01 18:54:01 +02:00
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]);
2017-10-01 01:12:42 +02:00
gBattleCommunication[MULTIUSE_STATE]++;
}
break;
case 13:
InitBattleControllers();
2021-10-04 10:21:03 -04:00
RecordedBattle_SetTrainerInfo();
2017-10-01 01:12:42 +02:00
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:
2021-10-04 10:21:03 -04:00
// Send rng seed for recorded battle
2018-12-31 02:22:21 -06:00
if (IsLinkTaskFinished())
2017-10-01 01:12:42 +02:00
{
SendBlock(BitmaskAllOtherLinkPlayers(), &gRecordedBattleRngSeed, sizeof(gRecordedBattleRngSeed));
2017-10-01 01:12:42 +02:00
gBattleCommunication[MULTIUSE_STATE]++;
}
break;
case 15:
2021-10-04 10:21:03 -04:00
// Receive rng seed for recorded battle (only read it if partner is the link master)
2017-10-01 01:12:42 +02:00
if ((GetBlockReceivedStatus() & 3) == 3)
{
ResetBlockReceivedFlags();
if (!(gBattleTypeFlags & BATTLE_TYPE_IS_MASTER))
2021-10-04 10:21:03 -04:00
memcpy(&gRecordedBattleRngSeed, gBlockRecvBuffer[partnerMultiplayerId], sizeof(gRecordedBattleRngSeed));
2017-10-01 01:12:42 +02:00
gBattleCommunication[MULTIUSE_STATE]++;
}
break;
case 16:
2021-10-04 10:21:03 -04:00
// Finish, start battle
2017-10-01 01:12:42 +02:00
if (BattleInitAllSprites(&gBattleCommunication[SPRITES_INIT_STATE1], &gBattleCommunication[SPRITES_INIT_STATE2]))
{
2020-12-24 16:18:47 -05:00
TrySetLinkBattleTowerEnemyPartyLevel();
2017-10-01 01:12:42 +02:00
gPreBattleCallback1 = gMain.callback1;
gMain.callback1 = BattleMainCB1;
SetMainCallback2(BattleMainCB2);
if (gBattleTypeFlags & BATTLE_TYPE_LINK)
2021-01-13 15:17:32 -05:00
gBattleTypeFlags |= BATTLE_TYPE_LINK_IN_BATTLE;
2017-10-01 01:12:42 +02:00
}
break;
}
}
2021-10-04 10:21:03 -04:00
static void SetMultiPartnerMenuParty(u8 offset)
2017-10-01 01:12:42 +02:00
{
s32 i;
2021-10-04 10:21:03 -04:00
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]);
2019-10-17 19:22:03 -04:00
StripExtCtrlCodes(gMultiPartnerParty[i].nickname);
2021-10-04 10:21:03 -04:00
if (GetMonData(&gPlayerParty[offset + i], MON_DATA_LANGUAGE) != LANGUAGE_JAPANESE)
2019-10-17 19:22:03 -04:00
PadNameString(gMultiPartnerParty[i].nickname, CHAR_SPACE);
2017-10-01 01:12:42 +02:00
}
2019-10-17 19:22:03 -04:00
memcpy(sMultiPartnerPartyBuffer, gMultiPartnerParty, sizeof(gMultiPartnerParty));
2017-10-01 01:12:42 +02:00
}
static void CB2_PreInitMultiBattle(void)
{
s32 i;
u8 playerMultiplierId;
2021-10-04 10:21:03 -04:00
s32 numPlayers = MAX_BATTLERS_COUNT;
u8 blockMask = 0xF;
2018-06-20 23:07:51 +02:00
u32 *savedBattleTypeFlags;
2017-10-01 01:12:42 +02:00
void (**savedCallback)(void);
if (gBattleTypeFlags & BATTLE_TYPE_BATTLE_TOWER)
{
numPlayers = 2;
2021-10-04 10:21:03 -04:00
blockMask = 3;
2017-10-01 01:12:42 +02:00
}
playerMultiplierId = GetMultiplayerId();
gBattleScripting.multiplayerId = playerMultiplierId;
savedCallback = &gBattleStruct->savedCallback;
savedBattleTypeFlags = &gBattleStruct->savedBattleTypeFlags;
RunTasks();
AnimateSprites();
BuildOamBuffer();
switch (gBattleCommunication[MULTIUSE_STATE])
{
case 0:
if (gReceivedRemoteLinkPlayers && IsLinkTaskFinished())
2017-10-01 01:12:42 +02:00
{
2021-10-04 10:21:03 -04:00
sMultiPartnerPartyBuffer = Alloc(sizeof(gMultiPartnerParty));
SetMultiPartnerMenuParty(0);
SendBlock(BitmaskAllOtherLinkPlayers(), sMultiPartnerPartyBuffer, sizeof(gMultiPartnerParty));
2017-10-01 01:12:42 +02:00
gBattleCommunication[MULTIUSE_STATE]++;
}
break;
case 1:
2021-10-04 10:21:03 -04:00
if ((GetBlockReceivedStatus() & blockMask) == blockMask)
2017-10-01 01:12:42 +02:00
{
ResetBlockReceivedFlags();
for (i = 0; i < numPlayers; i++)
{
if (i == playerMultiplierId)
continue;
2019-10-17 19:22:03 -04:00
if (numPlayers == MAX_LINK_PLAYERS)
2017-10-01 01:12:42 +02:00
{
2018-07-22 12:49:49 +02:00
if ((!(gLinkPlayers[i].id & 1) && !(gLinkPlayers[playerMultiplierId].id & 1))
|| (gLinkPlayers[i].id & 1 && gLinkPlayers[playerMultiplierId].id & 1))
2017-10-01 01:12:42 +02:00
{
2021-10-04 10:21:03 -04:00
memcpy(gMultiPartnerParty, gBlockRecvBuffer[i], sizeof(gMultiPartnerParty));
2017-10-01 01:12:42 +02:00
}
}
else
{
2021-10-04 10:21:03 -04:00
memcpy(gMultiPartnerParty, gBlockRecvBuffer[i], sizeof(gMultiPartnerParty));
2017-10-01 01:12:42 +02:00
}
}
gBattleCommunication[MULTIUSE_STATE]++;
*savedCallback = gMain.savedCallback;
*savedBattleTypeFlags = gBattleTypeFlags;
gMain.savedCallback = CB2_PreInitMultiBattle;
2019-10-17 19:22:03 -04:00
ShowPartyMenuToShowcaseMultiBattleParty();
2017-10-01 01:12:42 +02:00
}
break;
case 2:
2018-12-31 02:22:21 -06:00
if (IsLinkTaskFinished() && !gPaletteFade.active)
2017-10-01 01:12:42 +02:00
{
gBattleCommunication[MULTIUSE_STATE]++;
2017-11-12 23:58:05 -05:00
if (gWirelessCommType)
2020-08-13 03:09:47 -04:00
SetLinkStandbyCallback();
2017-10-01 01:12:42 +02:00
else
2020-08-13 03:09:47 -04:00
SetCloseLinkCallback();
2017-10-01 01:12:42 +02:00
}
break;
case 3:
2017-11-12 23:58:05 -05:00
if (gWirelessCommType)
2017-10-01 01:12:42 +02:00
{
2019-10-09 05:56:44 -04:00
if (IsLinkRfuTaskFinished())
2017-10-01 01:12:42 +02:00
{
gBattleTypeFlags = *savedBattleTypeFlags;
gMain.savedCallback = *savedCallback;
SetMainCallback2(CB2_InitBattleInternal);
FREE_AND_SET_NULL(sMultiPartnerPartyBuffer);
2017-10-01 01:12:42 +02:00
}
}
else if (gReceivedRemoteLinkPlayers == 0)
{
gBattleTypeFlags = *savedBattleTypeFlags;
gMain.savedCallback = *savedCallback;
SetMainCallback2(CB2_InitBattleInternal);
FREE_AND_SET_NULL(sMultiPartnerPartyBuffer);
2017-10-01 01:12:42 +02:00
}
break;
}
}
static void CB2_PreInitIngamePlayerPartnerBattle(void)
{
2018-06-20 23:07:51 +02:00
u32 *savedBattleTypeFlags;
2017-10-01 01:12:42 +02:00
void (**savedCallback)(void);
savedCallback = &gBattleStruct->savedCallback;
savedBattleTypeFlags = &gBattleStruct->savedBattleTypeFlags;
RunTasks();
AnimateSprites();
BuildOamBuffer();
switch (gBattleCommunication[MULTIUSE_STATE])
{
case 0:
2021-10-04 10:21:03 -04:00
sMultiPartnerPartyBuffer = Alloc(sizeof(gMultiPartnerParty));
SetMultiPartnerMenuParty(MULTI_PARTY_SIZE);
2017-10-01 01:12:42 +02:00
gBattleCommunication[MULTIUSE_STATE]++;
*savedCallback = gMain.savedCallback;
*savedBattleTypeFlags = gBattleTypeFlags;
gMain.savedCallback = CB2_PreInitIngamePlayerPartnerBattle;
2019-10-17 19:22:03 -04:00
ShowPartyMenuToShowcaseMultiBattleParty();
2017-10-01 01:12:42 +02:00
break;
case 1:
if (!gPaletteFade.active)
{
gBattleCommunication[MULTIUSE_STATE] = 2;
gBattleTypeFlags = *savedBattleTypeFlags;
gMain.savedCallback = *savedCallback;
SetMainCallback2(CB2_InitBattleInternal);
FREE_AND_SET_NULL(sMultiPartnerPartyBuffer);
2017-10-01 01:12:42 +02:00
}
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);
2021-09-24 14:30:15 -04:00
FillAroundBattleWindows();
2017-10-01 01:12:42 +02:00
gBattleCommunication[MULTIUSE_STATE] = 1;
}
2017-11-12 23:58:05 -05:00
if (gWirelessCommType)
2019-04-04 17:05:46 -04:00
LoadWirelessStatusIndicatorSpriteGfx();
2017-10-01 01:12:42 +02:00
break;
case 1:
if (gBattleTypeFlags & BATTLE_TYPE_LINK)
{
if (gReceivedRemoteLinkPlayers)
2017-10-01 01:12:42 +02:00
{
2018-12-31 02:22:21 -06:00
if (IsLinkTaskFinished())
2017-10-01 01:12:42 +02:00
{
2021-03-12 16:55:58 -05:00
// 0x300
2021-03-15 15:22:41 -04:00
*(&gBattleStruct->multiBuffer.linkBattlerHeader.versionSignatureLo) = 0;
*(&gBattleStruct->multiBuffer.linkBattlerHeader.versionSignatureHi) = 3;
2021-01-22 23:22:37 -05:00
BufferPartyVsScreenHealth_AtStart();
2017-10-01 01:12:42 +02:00
SetPlayerBerryDataInBattleStruct();
SendBlock(BitmaskAllOtherLinkPlayers(), &gBattleStruct->multiBuffer.linkBattlerHeader, sizeof(gBattleStruct->multiBuffer.linkBattlerHeader));
2017-10-01 01:12:42 +02:00
gBattleCommunication[MULTIUSE_STATE]++;
}
2017-11-12 23:58:05 -05:00
if (gWirelessCommType)
2017-12-03 22:01:06 -05:00
CreateWirelessStatusIndicatorSprite(0, 0);
2017-10-01 01:12:42 +02:00
}
}
else
{
if (!(gBattleTypeFlags & BATTLE_TYPE_RECORDED))
gBattleTypeFlags |= BATTLE_TYPE_IS_MASTER;
2017-10-01 01:12:42 +02:00
gBattleCommunication[MULTIUSE_STATE] = 7;
SetAllPlayersBerryData();
}
break;
case 2:
if ((GetBlockReceivedStatus() & 0xF) == 0xF)
{
ResetBlockReceivedFlags();
2021-03-12 16:55:58 -05:00
FindLinkBattleMaster(4, playerMultiplayerId);
2017-10-01 01:12:42 +02:00
SetAllPlayersBerryData();
2019-11-06 18:18:11 -06:00
var = CreateTask(InitLinkBattleVsScreen, 0);
2017-10-01 01:12:42 +02:00
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++)
{
2021-03-12 16:55:58 -05:00
RecordedBattle_SetFrontierPassFlagFromHword(gBlockRecvBuffer[id][1]);
2018-07-22 12:49:49 +02:00
switch (gLinkPlayers[id].id)
2017-10-01 01:12:42 +02:00
{
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:
2018-12-31 02:22:21 -06:00
if (IsLinkTaskFinished())
2017-10-01 01:12:42 +02:00
{
SendBlock(BitmaskAllOtherLinkPlayers(), gPlayerParty, sizeof(struct Pokemon) * 2);
2017-10-01 01:12:42 +02:00
gBattleCommunication[MULTIUSE_STATE]++;
}
break;
case 4:
if ((GetBlockReceivedStatus() & 0xF) == 0xF)
{
ResetBlockReceivedFlags();
for (id = 0; id < MAX_LINK_PLAYERS; id++)
{
if (id == playerMultiplayerId)
{
2018-07-22 12:49:49 +02:00
switch (gLinkPlayers[id].id)
2017-10-01 01:12:42 +02:00
{
case 0:
case 3:
memcpy(gPlayerParty, gBlockRecvBuffer[id], sizeof(struct Pokemon) * 2);
break;
case 1:
case 2:
2019-10-31 21:33:01 -04:00
memcpy(gPlayerParty + MULTI_PARTY_SIZE, gBlockRecvBuffer[id], sizeof(struct Pokemon) * 2);
2017-10-01 01:12:42 +02:00
break;
}
}
else
{
2018-07-22 12:49:49 +02:00
if ((!(gLinkPlayers[id].id & 1) && !(gLinkPlayers[playerMultiplayerId].id & 1))
|| ((gLinkPlayers[id].id & 1) && (gLinkPlayers[playerMultiplayerId].id & 1)))
2017-10-01 01:12:42 +02:00
{
2018-07-22 12:49:49 +02:00
switch (gLinkPlayers[id].id)
2017-10-01 01:12:42 +02:00
{
case 0:
case 3:
memcpy(gPlayerParty, gBlockRecvBuffer[id], sizeof(struct Pokemon) * 2);
break;
case 1:
case 2:
2019-10-31 21:33:01 -04:00
memcpy(gPlayerParty + MULTI_PARTY_SIZE, gBlockRecvBuffer[id], sizeof(struct Pokemon) * 2);
2017-10-01 01:12:42 +02:00
break;
}
}
else
{
2018-07-22 12:49:49 +02:00
switch (gLinkPlayers[id].id)
2017-10-01 01:12:42 +02:00
{
case 0:
case 3:
memcpy(gEnemyParty, gBlockRecvBuffer[id], sizeof(struct Pokemon) * 2);
break;
case 1:
case 2:
2019-10-31 21:33:01 -04:00
memcpy(gEnemyParty + MULTI_PARTY_SIZE, gBlockRecvBuffer[id], sizeof(struct Pokemon) * 2);
2017-10-01 01:12:42 +02:00
break;
}
}
}
}
gBattleCommunication[MULTIUSE_STATE]++;
}
break;
case 5:
2018-12-31 02:22:21 -06:00
if (IsLinkTaskFinished())
2017-10-01 01:12:42 +02:00
{
SendBlock(BitmaskAllOtherLinkPlayers(), gPlayerParty + 2, sizeof(struct Pokemon));
2017-10-01 01:12:42 +02:00
gBattleCommunication[MULTIUSE_STATE]++;
}
break;
case 6:
if ((GetBlockReceivedStatus() & 0xF) == 0xF)
{
ResetBlockReceivedFlags();
for (id = 0; id < MAX_LINK_PLAYERS; id++)
{
if (id == playerMultiplayerId)
{
2018-07-22 12:49:49 +02:00
switch (gLinkPlayers[id].id)
2017-10-01 01:12:42 +02:00
{
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
{
2018-07-22 12:49:49 +02:00
if ((!(gLinkPlayers[id].id & 1) && !(gLinkPlayers[playerMultiplayerId].id & 1))
|| ((gLinkPlayers[id].id & 1) && (gLinkPlayers[playerMultiplayerId].id & 1)))
2017-10-01 01:12:42 +02:00
{
2018-07-22 12:49:49 +02:00
switch (gLinkPlayers[id].id)
2017-10-01 01:12:42 +02:00
{
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
{
2018-07-22 12:49:49 +02:00
switch (gLinkPlayers[id].id)
2017-10-01 01:12:42 +02:00
{
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;
}
}
}
}
2017-10-01 18:54:01 +02:00
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]);
2017-10-01 01:12:42 +02:00
gBattleCommunication[MULTIUSE_STATE]++;
}
break;
case 7:
InitBattleControllers();
2021-10-04 10:21:03 -04:00
RecordedBattle_SetTrainerInfo();
2017-10-01 01:12:42 +02:00
gBattleCommunication[SPRITES_INIT_STATE1] = 0;
gBattleCommunication[SPRITES_INIT_STATE2] = 0;
if (gBattleTypeFlags & BATTLE_TYPE_LINK)
{
2019-09-08 12:21:24 -04:00
for (id = 0; id < MAX_LINK_PLAYERS && (gLinkPlayers[id].version & 0xFF) == VERSION_EMERALD; id++);
2017-10-01 01:12:42 +02:00
2019-09-08 12:21:24 -04:00
if (id == MAX_LINK_PLAYERS)
2017-10-01 01:12:42 +02:00
gBattleCommunication[MULTIUSE_STATE] = 8;
else
gBattleCommunication[MULTIUSE_STATE] = 10;
}
else
{
gBattleCommunication[MULTIUSE_STATE] = 10;
}
break;
case 8:
2018-12-31 02:22:21 -06:00
if (IsLinkTaskFinished())
2017-10-01 01:12:42 +02:00
{
u32 *ptr = gBattleStruct->multiBuffer.battleVideo;
2017-10-01 01:12:42 +02:00
ptr[0] = gBattleTypeFlags;
ptr[1] = gRecordedBattleRngSeed; // UB: overwrites berry data
SendBlock(BitmaskAllOtherLinkPlayers(), ptr, sizeof(gBattleStruct->multiBuffer.battleVideo));
2017-10-01 01:12:42 +02:00
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)
{
2018-07-01 11:15:42 +02:00
gTrainerBattleOpponent_A = TRAINER_LINK_OPPONENT;
2021-01-13 15:17:32 -05:00
gBattleTypeFlags |= BATTLE_TYPE_LINK_IN_BATTLE;
2017-10-01 01:12:42 +02:00
}
}
break;
}
}
2017-10-01 18:54:01 +02:00
void BattleMainCB2(void)
{
AnimateSprites();
BuildOamBuffer();
RunTextPrinters();
UpdatePaletteFade();
RunTasks();
2021-10-04 10:21:03 -04:00
if (JOY_HELD(B_BUTTON) && gBattleTypeFlags & BATTLE_TYPE_RECORDED && RecordedBattle_CanStopPlayback())
2017-10-01 18:54:01 +02:00
{
2021-10-04 10:21:03 -04:00
// Player pressed B during recorded battle playback, end battle
2018-01-16 15:12:38 -06:00
gSpecialVar_Result = gBattleOutcome = B_OUTCOME_PLAYER_TELEPORTED;
2017-10-01 18:54:01 +02:00
ResetPaletteFadeControl();
2021-10-04 10:21:03 -04:00
BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, RGB_BLACK);
2017-10-01 18:54:01 +02:00
SetMainCallback2(CB2_QuitRecordedBattle);
}
}
static void FreeRestoreBattleData(void)
{
gMain.callback1 = gPreBattleCallback1;
2018-01-29 17:47:12 +01:00
gScanlineEffect.state = 3;
2021-10-04 10:21:03 -04:00
gMain.inBattle = FALSE;
2017-10-01 18:54:01 +02:00
ZeroEnemyPartyMons();
2020-08-20 18:02:00 -04:00
m4aSongNumStop(SE_LOW_HEALTH);
2017-10-01 18:54:01 +02:00
FreeMonSpritesGfx();
FreeBattleSpritesData();
FreeBattleResources();
}
void CB2_QuitRecordedBattle(void)
{
UpdatePaletteFade();
if (!gPaletteFade.active)
{
m4aMPlayStop(&gMPlayInfo_SE1);
m4aMPlayStop(&gMPlayInfo_SE2);
if (gTestRunnerEnabled)
TestRunner_Battle_AfterLastTurn();
2017-10-01 18:54:01 +02:00
FreeRestoreBattleData();
FreeAllWindowBuffers();
SetMainCallback2(gMain.savedCallback);
}
}
2021-10-04 10:21:03 -04:00
#define sState data[0]
#define sDelay data[4]
2022-07-29 11:15:33 -04:00
static void SpriteCB_UnusedBattleInit(struct Sprite *sprite)
2017-10-01 18:54:01 +02:00
{
2021-10-04 10:21:03 -04:00
sprite->sState = 0;
sprite->callback = SpriteCB_UnusedBattleInit_Main;
2017-10-01 18:54:01 +02:00
}
2021-10-04 10:21:03 -04:00
static void SpriteCB_UnusedBattleInit_Main(struct Sprite *sprite)
2017-10-01 18:54:01 +02:00
{
u16 *arr = (u16 *)gDecompressionBuffer;
2017-10-01 18:54:01 +02:00
2021-10-04 10:21:03 -04:00
switch (sprite->sState)
2017-10-01 18:54:01 +02:00
{
case 0:
2021-10-04 10:21:03 -04:00
sprite->sState++;
2017-12-02 21:44:50 +01:00
sprite->data[1] = 0;
sprite->data[2] = 0x281;
sprite->data[3] = 0;
2021-10-04 10:21:03 -04:00
sprite->sDelay = 1;
2017-10-01 18:54:01 +02:00
// fall through
case 1:
2021-10-04 10:21:03 -04:00
sprite->sDelay--;
if (sprite->sDelay == 0)
2017-10-01 18:54:01 +02:00
{
s32 i;
s32 r2;
s32 r0;
2021-10-04 10:21:03 -04:00
sprite->sDelay = 2;
2017-12-02 21:44:50 +01:00
r2 = sprite->data[1] + sprite->data[3] * 32;
r0 = sprite->data[2] - sprite->data[3] * 32;
2017-10-01 18:54:01 +02:00
for (i = 0; i < 29; i += 2)
{
arr[r2 + i] = 0x3D;
arr[r0 + i] = 0x3D;
}
2017-12-02 21:44:50 +01:00
sprite->data[3]++;
if (sprite->data[3] == 21)
2017-10-01 18:54:01 +02:00
{
2021-10-04 10:21:03 -04:00
sprite->sState++;
2017-12-02 21:44:50 +01:00
sprite->data[1] = 32;
2017-10-01 18:54:01 +02:00
}
}
break;
case 2:
2017-12-02 21:44:50 +01:00
sprite->data[1]--;
if (sprite->data[1] == 20)
2017-10-01 18:54:01 +02:00
SetMainCallback2(CB2_InitBattle);
break;
}
}
static u32 Crc32B (const u8 *data, u32 size)
{
s32 i, j;
u32 byte, crc, mask;
i = 0;
crc = 0xFFFFFFFF;
for (i = 0; i < size; ++i)
{
byte = data[i];
crc = crc ^ byte;
for (j = 7; j >= 0; --j)
{
mask = -(crc & 1);
crc = (crc >> 1) ^ (0xEDB88320 & mask);
}
}
return ~crc;
}
static u32 GeneratePartyHash(const struct Trainer *trainer, u32 i)
{
2023-07-05 18:23:18 -04:00
const u8 *buffer = (const u8 *) &trainer->party[i];
u32 n = sizeof(*trainer->party);
return Crc32B(buffer, n);
}
void ModifyPersonalityForNature(u32 *personality, u32 newNature)
{
u32 nature = GetNatureFromPersonality(*personality);
s32 diff = abs(nature - newNature);
s32 sign = (nature > newNature) ? 1 : -1;
if (diff > NUM_NATURES / 2)
{
diff = NUM_NATURES - diff;
sign *= -1;
}
*personality -= (diff * sign);
}
u32 GeneratePersonalityForGender(u32 gender, u32 species)
{
const struct SpeciesInfo *speciesInfo = &gSpeciesInfo[species];
if (gender == MON_MALE)
return ((255 - speciesInfo->genderRatio) / 2) + speciesInfo->genderRatio;
else
return speciesInfo->genderRatio / 2;
}
void CustomTrainerPartyAssignMoves(struct Pokemon *mon, const struct TrainerMon *partyEntry)
2023-04-07 10:53:44 +02:00
{
bool32 noMoveSet = TRUE;
u32 j;
for (j = 0; j < MAX_MON_MOVES; ++j)
{
if (partyEntry->moves[j] != MOVE_NONE)
noMoveSet = FALSE;
}
if (noMoveSet)
{
// TODO: Figure out a default strategy when moves are not set, to generate a good moveset
return;
}
for (j = 0; j < MAX_MON_MOVES; ++j)
{
SetMonData(mon, MON_DATA_MOVE1 + j, &partyEntry->moves[j]);
SetMonData(mon, MON_DATA_PP1 + j, &gBattleMoves[partyEntry->moves[j]].pp);
}
}
u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer *trainer, bool32 firstTrainer, u32 battleTypeFlags)
2017-10-01 18:54:01 +02:00
{
u32 personalityValue;
u8 fixedIV;
s32 i, j;
u8 monsCount;
if (battleTypeFlags & BATTLE_TYPE_TRAINER && !(battleTypeFlags & (BATTLE_TYPE_FRONTIER
2017-10-01 18:54:01 +02:00
| BATTLE_TYPE_EREADER_TRAINER
2018-09-20 22:00:00 +02:00
| BATTLE_TYPE_TRAINER_HILL)))
2017-10-01 18:54:01 +02:00
{
if (firstTrainer == TRUE)
ZeroEnemyPartyMons();
if (battleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS)
2017-10-01 18:54:01 +02:00
{
if (trainer->partySize > PARTY_SIZE / 2)
2021-10-04 10:21:03 -04:00
monsCount = PARTY_SIZE / 2;
2017-10-01 18:54:01 +02:00
else
monsCount = trainer->partySize;
2017-10-01 18:54:01 +02:00
}
else
{
monsCount = trainer->partySize;
2017-10-01 18:54:01 +02:00
}
for (i = 0; i < monsCount; i++)
{
s32 ball = -1;
u32 personalityHash = GeneratePartyHash(trainer, i);
2023-07-05 18:29:28 -04:00
const struct TrainerMon *partyData = trainer->party;
u32 otIdType = OT_ID_RANDOM_NO_SHINY;
u32 fixedOtId = 0;
if (trainer->doubleBattle == TRUE)
2017-10-01 18:54:01 +02:00
personalityValue = 0x80;
else if (trainer->encounterMusic_gender & F_TRAINER_FEMALE)
2021-06-25 11:50:09 -04:00
personalityValue = 0x78; // Use personality more likely to result in a female Pokémon
2017-10-01 18:54:01 +02:00
else
2021-06-25 11:50:09 -04:00
personalityValue = 0x88; // Use personality more likely to result in a male Pokémon
2017-10-01 18:54:01 +02:00
personalityValue += personalityHash << 8;
if (partyData[i].gender == TRAINER_MON_MALE)
personalityValue = (personalityValue & 0xFFFFFF00) | GeneratePersonalityForGender(MON_MALE, partyData[i].species);
else if (partyData[i].gender == TRAINER_MON_FEMALE)
personalityValue = (personalityValue & 0xFFFFFF00) | GeneratePersonalityForGender(MON_FEMALE, partyData[i].species);
if (partyData[i].nature != 0)
ModifyPersonalityForNature(&personalityValue, partyData[i].nature - 1);
if (partyData[i].isShiny)
2017-10-01 18:54:01 +02:00
{
otIdType = OT_ID_PRESET;
fixedOtId = HIHALF(personalityValue) ^ LOHALF(personalityValue);
2017-10-01 18:54:01 +02:00
}
CreateMon(&party[i], partyData[i].species, partyData[i].lvl, 0, TRUE, personalityValue, otIdType, fixedOtId);
SetMonData(&party[i], MON_DATA_HELD_ITEM, &partyData[i].heldItem);
2017-10-01 18:54:01 +02:00
CustomTrainerPartyAssignMoves(&party[i], &partyData[i]);
SetMonData(&party[i], MON_DATA_IVS, &(partyData[i].iv));
if (partyData[i].ev != NULL)
2017-10-01 18:54:01 +02:00
{
SetMonData(&party[i], MON_DATA_HP_EV, &(partyData[i].ev[0]));
SetMonData(&party[i], MON_DATA_ATK_EV, &(partyData[i].ev[1]));
SetMonData(&party[i], MON_DATA_DEF_EV, &(partyData[i].ev[2]));
SetMonData(&party[i], MON_DATA_SPATK_EV, &(partyData[i].ev[3]));
SetMonData(&party[i], MON_DATA_SPDEF_EV, &(partyData[i].ev[4]));
SetMonData(&party[i], MON_DATA_SPEED_EV, &(partyData[i].ev[5]));
2017-10-01 18:54:01 +02:00
}
if (partyData[i].ability != ABILITY_NONE)
2017-10-01 18:54:01 +02:00
{
const struct SpeciesInfo *speciesInfo = &gSpeciesInfo[partyData[i].species];
u32 maxAbilities = ARRAY_COUNT(speciesInfo->abilities);
for (j = 0; j < maxAbilities; ++j)
2017-10-01 18:54:01 +02:00
{
if (speciesInfo->abilities[j] == partyData[i].ability)
break;
2017-10-01 18:54:01 +02:00
}
if (j < maxAbilities)
SetMonData(&party[i], MON_DATA_ABILITY_NUM, &j);
2017-10-01 18:54:01 +02:00
}
SetMonData(&party[i], MON_DATA_FRIENDSHIP, &(partyData[i].friendship));
if (partyData[i].ball != ITEM_NONE)
{
ball = partyData[i].ball;
SetMonData(&party[i], MON_DATA_POKEBALL, &ball);
}
if (partyData[i].nickname != NULL)
{
SetMonData(&party[i], MON_DATA_NICKNAME, partyData[i].nickname);
2017-10-01 18:54:01 +02:00
}
CalculateMonStats(&party[i]);
#if B_TRAINER_CLASS_POKE_BALLS >= GEN_7
if (ball == -1)
{
ball = (sTrainerBallTable[trainer->trainerClass]) ? sTrainerBallTable[trainer->trainerClass] : ITEM_POKE_BALL;
SetMonData(&party[i], MON_DATA_POKEBALL, &ball);
}
#endif
2017-10-01 18:54:01 +02:00
}
}
return trainer->partySize;
}
static u8 CreateNPCTrainerParty(struct Pokemon *party, u16 trainerNum, bool8 firstTrainer)
{
u8 retVal;
if (trainerNum == TRAINER_SECRET_BASE)
return 0;
retVal = CreateNPCTrainerPartyFromTrainer(party, &gTrainers[trainerNum], firstTrainer, gBattleTypeFlags);
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER && !(gBattleTypeFlags & (BATTLE_TYPE_FRONTIER
| BATTLE_TYPE_EREADER_TRAINER
| BATTLE_TYPE_TRAINER_HILL)))
{
2017-10-01 18:54:01 +02:00
gBattleTypeFlags |= gTrainers[trainerNum].doubleBattle;
}
return retVal;
2017-10-01 18:54:01 +02:00
}
2023-08-09 09:15:42 +02:00
void CreateTrainerPartyForPlayer(void)
{
ZeroPlayerPartyMons();
2023-08-09 09:34:41 +02:00
gPartnerTrainerId = gSpecialVar_0x8004;
CreateNPCTrainerPartyFromTrainer(gPlayerParty, &gTrainers[gSpecialVar_0x8004], TRUE, BATTLE_TYPE_TRAINER);
2023-08-09 09:15:42 +02:00
}
2017-10-01 18:54:01 +02:00
void VBlankCB_Battle(void)
{
2018-06-20 23:07:51 +02:00
// Change gRngSeed every vblank unless the battle could be recorded.
2017-10-01 18:54:01 +02:00
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();
2018-01-29 17:47:12 +01:00
ScanlineEffect_InitHBlankDmaTransfer();
2017-10-01 18:54:01 +02:00
}
2021-01-22 23:22:37 -05:00
void SpriteCB_VsLetterDummy(struct Sprite *sprite)
2017-10-01 18:54:01 +02:00
{
}
2021-01-22 23:22:37 -05:00
static void SpriteCB_VsLetter(struct Sprite *sprite)
2017-10-01 18:54:01 +02:00
{
2017-12-02 21:44:50 +01:00
if (sprite->data[0] != 0)
2021-07-07 09:11:52 -04:00
sprite->x = sprite->data[1] + ((sprite->data[2] & 0xFF00) >> 8);
2017-10-01 18:54:01 +02:00
else
2021-07-07 09:11:52 -04:00
sprite->x = sprite->data[1] - ((sprite->data[2] & 0xFF00) >> 8);
2017-10-01 18:54:01 +02:00
2017-12-02 21:44:50 +01:00
sprite->data[2] += 0x180;
2017-10-01 18:54:01 +02:00
if (sprite->affineAnimEnded)
{
2021-01-22 20:03:21 -05:00
FreeSpriteTilesByTag(ANIM_SPRITES_START);
FreeSpritePaletteByTag(ANIM_SPRITES_START);
2017-10-01 18:54:01 +02:00
FreeSpriteOamMatrix(sprite);
DestroySprite(sprite);
}
}
2021-01-22 23:22:37 -05:00
void SpriteCB_VsLetterInit(struct Sprite *sprite)
2017-10-01 18:54:01 +02:00
{
StartSpriteAffineAnim(sprite, 1);
2021-01-22 23:22:37 -05:00
sprite->callback = SpriteCB_VsLetter;
2020-08-20 18:02:00 -04:00
PlaySE(SE_MUGSHOT);
2017-10-01 18:54:01 +02:00
}
2021-01-22 23:22:37 -05:00
static void BufferPartyVsScreenHealth_AtEnd(u8 taskId)
2017-10-01 18:54:01 +02:00
{
2021-01-22 20:03:21 -05:00
struct Pokemon *party1 = NULL;
struct Pokemon *party2 = NULL;
u8 multiplayerId = gBattleScripting.multiplayerId;
2021-01-22 23:22:37 -05:00
u32 flags;
2017-10-01 18:54:01 +02:00
s32 i;
if (gBattleTypeFlags & BATTLE_TYPE_MULTI)
{
2021-01-22 20:03:21 -05:00
switch (gLinkPlayers[multiplayerId].id)
2017-10-01 18:54:01 +02:00
{
case 0:
case 2:
2021-01-22 20:03:21 -05:00
party1 = gPlayerParty;
party2 = gEnemyParty;
2017-10-01 18:54:01 +02:00
break;
case 1:
case 3:
2021-01-22 20:03:21 -05:00
party1 = gEnemyParty;
party2 = gPlayerParty;
2017-10-01 18:54:01 +02:00
break;
}
}
else
{
2021-01-22 20:03:21 -05:00
party1 = gPlayerParty;
party2 = gEnemyParty;
2017-10-01 18:54:01 +02:00
}
2021-01-22 23:22:37 -05:00
flags = 0;
BUFFER_PARTY_VS_SCREEN_STATUS(party1, flags, i);
gTasks[taskId].data[3] = flags;
2017-10-01 18:54:01 +02:00
2021-01-22 23:22:37 -05:00
flags = 0;
BUFFER_PARTY_VS_SCREEN_STATUS(party2, flags, i);
gTasks[taskId].data[4] = flags;
2017-10-01 18:54:01 +02:00
}
2021-01-22 20:03:21 -05:00
void CB2_InitEndLinkBattle(void)
2017-10-01 18:54:01 +02:00
{
s32 i;
u8 taskId;
SetHBlankCallback(NULL);
SetVBlankCallback(NULL);
2021-09-24 14:30:15 -04:00
gBattleTypeFlags &= ~BATTLE_TYPE_LINK_IN_BATTLE;
2017-10-01 18:54:01 +02:00
if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER)
{
SetMainCallback2(gMain.savedCallback);
FreeBattleResources();
FreeBattleSpritesData();
FreeMonSpritesGfx();
}
else
{
2022-07-29 10:52:35 -04:00
CpuFill32(0, (void *)(VRAM), VRAM_SIZE);
2017-10-01 18:54:01 +02:00
SetGpuReg(REG_OFFSET_MOSAIC, 0);
2021-01-22 20:03:21 -05:00
SetGpuReg(REG_OFFSET_WIN0H, DISPLAY_WIDTH);
SetGpuReg(REG_OFFSET_WIN0V, WIN_RANGE(DISPLAY_HEIGHT / 2, DISPLAY_HEIGHT / 2 + 1));
2017-10-01 18:54:01 +02:00
SetGpuReg(REG_OFFSET_WININ, 0);
SetGpuReg(REG_OFFSET_WINOUT, 0);
2021-01-22 20:03:21 -05:00
gBattle_WIN0H = DISPLAY_WIDTH;
gBattle_WIN0V = WIN_RANGE(DISPLAY_HEIGHT / 2, DISPLAY_HEIGHT / 2 + 1);
2018-01-29 17:47:12 +01:00
ScanlineEffect_Clear();
2017-10-01 18:54:01 +02:00
2019-01-05 19:25:46 +01:00
i = 0;
while (i < 80)
2017-10-01 18:54:01 +02:00
{
2018-01-29 17:47:12 +01:00
gScanlineEffectRegBuffers[0][i] = 0xF0;
gScanlineEffectRegBuffers[1][i] = 0xF0;
2019-01-05 19:25:46 +01:00
i++;
2017-10-01 18:54:01 +02:00
}
2019-01-05 19:25:46 +01:00
while (i < 160)
2017-10-01 18:54:01 +02:00
{
2018-01-29 17:47:12 +01:00
gScanlineEffectRegBuffers[0][i] = 0xFF10;
gScanlineEffectRegBuffers[1][i] = 0xFF10;
2019-01-05 19:25:46 +01:00
i++;
2017-10-01 18:54:01 +02:00
}
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;
2019-11-06 18:18:11 -06:00
InitBattleBgsVideo();
2022-08-19 16:32:00 +01:00
LoadCompressedPalette(gBattleTextboxPalette, BG_PLTT_ID(0), 2 * PLTT_SIZE_4BPP);
2018-06-17 16:48:58 +02:00
LoadBattleMenuWindowGfx();
2017-10-01 18:54:01 +02:00
ResetSpriteData();
ResetTasks();
2018-06-17 16:48:58 +02:00
DrawBattleEntryBackground();
2021-01-22 20:03:21 -05:00
SetGpuReg(REG_OFFSET_WINOUT, WINOUT_WIN01_BG0 | WINOUT_WIN01_BG1 | WINOUT_WIN01_BG2 | WINOUT_WIN01_OBJ | WINOUT_WIN01_CLR);
2017-10-01 18:54:01 +02:00
FreeAllSpritePalettes();
gReservedSpritePaletteCount = MAX_BATTLERS_COUNT;
2017-10-01 18:54:01 +02:00
SetVBlankCallback(VBlankCB_Battle);
2021-01-22 20:03:21 -05:00
// Show end Vs screen with battle results
2019-11-06 18:18:11 -06:00
taskId = CreateTask(InitLinkBattleVsScreen, 0);
2017-10-01 18:54:01 +02:00
gTasks[taskId].data[1] = 0x10E;
gTasks[taskId].data[2] = 0x5A;
gTasks[taskId].data[5] = 1;
2021-01-22 23:22:37 -05:00
BufferPartyVsScreenHealth_AtEnd(taskId);
2021-01-22 20:03:21 -05:00
SetMainCallback2(CB2_EndLinkBattle);
2017-10-01 18:54:01 +02:00
gBattleCommunication[MULTIUSE_STATE] = 0;
}
}
2021-01-22 20:03:21 -05:00
static void CB2_EndLinkBattle(void)
2017-10-01 18:54:01 +02:00
{
2021-01-22 20:03:21 -05:00
EndLinkBattleInSteps();
2017-10-01 18:54:01 +02:00
AnimateSprites();
BuildOamBuffer();
RunTextPrinters();
UpdatePaletteFade();
RunTasks();
}
2021-01-22 20:03:21 -05:00
static void EndLinkBattleInSteps(void)
2017-10-01 18:54:01 +02:00
{
s32 i;
switch (gBattleCommunication[MULTIUSE_STATE])
{
case 0:
ShowBg(0);
ShowBg(1);
ShowBg(2);
gBattleCommunication[1] = 0xFF;
2017-10-01 18:54:01 +02:00
gBattleCommunication[MULTIUSE_STATE]++;
break;
case 1:
if (--gBattleCommunication[1] == 0)
{
2021-10-04 10:21:03 -04:00
BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, RGB_BLACK);
2017-10-01 18:54:01 +02:00
gBattleCommunication[MULTIUSE_STATE]++;
}
break;
case 2:
if (!gPaletteFade.active)
{
2023-09-14 11:05:00 +02:00
u32 battlerCount;
2017-10-01 18:54:01 +02:00
2021-03-15 15:22:41 -04:00
gMain.anyLinkBattlerHasFrontierPass = RecordedBattle_GetFrontierPassFlag();
2017-10-01 18:54:01 +02:00
if (gBattleTypeFlags & BATTLE_TYPE_MULTI)
2021-10-04 10:21:03 -04:00
battlerCount = 4;
2017-10-01 18:54:01 +02:00
else
2021-10-04 10:21:03 -04:00
battlerCount = 2;
2017-10-01 18:54:01 +02:00
2021-10-04 10:21:03 -04:00
for (i = 0; i < battlerCount && (gLinkPlayers[i].version & 0xFF) == VERSION_EMERALD; i++);
2017-10-01 18:54:01 +02:00
2021-10-04 10:21:03 -04:00
if (!gSaveBlock2Ptr->frontier.disableRecordBattle && i == battlerCount)
2017-10-01 18:54:01 +02:00
{
2017-11-08 15:20:10 -06:00
if (FlagGet(FLAG_SYS_FRONTIER_PASS))
2017-10-01 18:54:01 +02:00
{
2021-10-04 10:21:03 -04:00
// Ask player if they want to record the battle
2017-10-01 18:54:01 +02:00
FreeAllWindowBuffers();
2021-10-04 10:21:03 -04:00
SetMainCallback2(CB2_InitAskRecordBattle);
2017-10-01 18:54:01 +02:00
}
2021-03-15 15:22:41 -04:00
else if (!gMain.anyLinkBattlerHasFrontierPass)
2017-10-01 18:54:01 +02:00
{
2021-10-04 10:21:03 -04:00
// No players can record this battle, end
2017-10-01 18:54:01 +02:00
SetMainCallback2(gMain.savedCallback);
FreeBattleResources();
FreeBattleSpritesData();
FreeMonSpritesGfx();
}
else if (gReceivedRemoteLinkPlayers == 0)
{
2021-10-04 10:21:03 -04:00
// Player can't record battle but
// another player can, reconnect with them
2021-03-04 17:48:40 -05:00
CreateTask(Task_ReconnectWithLinkPlayers, 5);
2017-10-01 18:54:01 +02:00
gBattleCommunication[MULTIUSE_STATE]++;
}
else
{
gBattleCommunication[MULTIUSE_STATE]++;
}
}
else
{
SetMainCallback2(gMain.savedCallback);
FreeBattleResources();
FreeBattleSpritesData();
FreeMonSpritesGfx();
}
}
break;
case 3:
2022-07-29 10:52:35 -04:00
CpuFill32(0, (void *)VRAM, VRAM_SIZE);
2017-10-01 18:54:01 +02:00
for (i = 0; i < 2; i++)
LoadChosenBattleElement(i);
2021-10-04 10:21:03 -04:00
BeginNormalPaletteFade(PALETTES_ALL, 0, 16, 0, RGB_BLACK);
2017-10-01 18:54:01 +02:00
gBattleCommunication[MULTIUSE_STATE]++;
break;
case 4:
if (!gPaletteFade.active)
gBattleCommunication[MULTIUSE_STATE]++;
break;
case 5:
2021-03-04 17:48:40 -05:00
if (!FuncIsActiveTask(Task_ReconnectWithLinkPlayers))
2017-10-01 18:54:01 +02:00
gBattleCommunication[MULTIUSE_STATE]++;
break;
case 6:
2018-12-31 02:22:21 -06:00
if (IsLinkTaskFinished() == TRUE)
2017-10-01 18:54:01 +02:00
{
2020-08-13 03:09:47 -04:00
SetLinkStandbyCallback();
2021-10-08 16:50:52 -04:00
BattlePutTextOnWindow(gText_LinkStandby3, B_WIN_MSG);
2017-10-01 18:54:01 +02:00
gBattleCommunication[MULTIUSE_STATE]++;
}
break;
case 7:
2021-10-08 16:50:52 -04:00
if (!IsTextPrinterActive(B_WIN_MSG))
2017-10-01 18:54:01 +02:00
{
2018-12-31 02:22:21 -06:00
if (IsLinkTaskFinished() == TRUE)
2017-10-01 18:54:01 +02:00
gBattleCommunication[MULTIUSE_STATE]++;
}
break;
case 8:
2017-11-12 23:58:05 -05:00
if (!gWirelessCommType)
2020-08-13 03:09:47 -04:00
SetCloseLinkCallback();
2017-10-01 18:54:01 +02:00
gBattleCommunication[MULTIUSE_STATE]++;
break;
case 9:
2021-03-15 15:22:41 -04:00
if (!gMain.anyLinkBattlerHasFrontierPass || gWirelessCommType || gReceivedRemoteLinkPlayers != 1)
2017-10-01 18:54:01 +02:00
{
2021-10-04 10:21:03 -04:00
gMain.anyLinkBattlerHasFrontierPass = FALSE;
2017-10-01 18:54:01 +02:00
SetMainCallback2(gMain.savedCallback);
FreeBattleResources();
FreeBattleSpritesData();
FreeMonSpritesGfx();
}
break;
}
}
2021-01-22 23:22:37 -05:00
u32 GetBattleBgTemplateData(u8 arrayId, u8 caseId)
2017-10-01 18:54:01 +02:00
{
u32 ret = 0;
switch (caseId)
{
case 0:
2018-06-17 16:48:58 +02:00
ret = gBattleBgTemplates[arrayId].bg;
2017-10-01 18:54:01 +02:00
break;
case 1:
2018-06-17 16:48:58 +02:00
ret = gBattleBgTemplates[arrayId].charBaseIndex;
2017-10-01 18:54:01 +02:00
break;
case 2:
2018-06-17 16:48:58 +02:00
ret = gBattleBgTemplates[arrayId].mapBaseIndex;
2017-10-01 18:54:01 +02:00
break;
case 3:
2018-06-17 16:48:58 +02:00
ret = gBattleBgTemplates[arrayId].screenSize;
2017-10-01 18:54:01 +02:00
break;
case 4:
2018-06-17 16:48:58 +02:00
ret = gBattleBgTemplates[arrayId].paletteMode;
2017-10-01 18:54:01 +02:00
break;
2021-01-22 23:22:37 -05:00
case 5: // Only this case is used
2018-06-17 16:48:58 +02:00
ret = gBattleBgTemplates[arrayId].priority;
2017-10-01 18:54:01 +02:00
break;
case 6:
2018-06-17 16:48:58 +02:00
ret = gBattleBgTemplates[arrayId].baseTile;
2017-10-01 18:54:01 +02:00
break;
}
return ret;
}
2021-10-04 10:21:03 -04:00
static void CB2_InitAskRecordBattle(void)
2017-10-01 18:54:01 +02:00
{
s32 i;
SetHBlankCallback(NULL);
SetVBlankCallback(NULL);
2022-07-29 10:52:35 -04:00
CpuFill32(0, (void *)(VRAM), VRAM_SIZE);
2017-10-01 18:54:01 +02:00
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;
2019-11-06 18:18:11 -06:00
InitBattleBgsVideo();
2017-10-01 18:54:01 +02:00
SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_OBJ_ON | DISPCNT_OBJ_1D_MAP);
2018-06-17 16:48:58 +02:00
LoadBattleMenuWindowGfx();
2017-10-01 18:54:01 +02:00
for (i = 0; i < 2; i++)
LoadChosenBattleElement(i);
ResetSpriteData();
ResetTasks();
FreeAllSpritePalettes();
gReservedSpritePaletteCount = MAX_BATTLERS_COUNT;
2017-10-01 18:54:01 +02:00
SetVBlankCallback(VBlankCB_Battle);
2021-10-04 10:21:03 -04:00
SetMainCallback2(CB2_AskRecordBattle);
2021-02-24 11:01:02 -05:00
BeginNormalPaletteFade(PALETTES_ALL, 0, 0x10, 0, RGB_BLACK);
2017-10-01 18:54:01 +02:00
gBattleCommunication[MULTIUSE_STATE] = 0;
}
2021-10-04 10:21:03 -04:00
static void CB2_AskRecordBattle(void)
2017-10-01 18:54:01 +02:00
{
2021-10-04 10:21:03 -04:00
AskRecordBattle();
2017-10-01 18:54:01 +02:00
AnimateSprites();
BuildOamBuffer();
RunTextPrinters();
UpdatePaletteFade();
RunTasks();
}
2021-10-04 10:21:03 -04:00
// 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)
2017-10-01 18:54:01 +02:00
{
switch (gBattleCommunication[MULTIUSE_STATE])
{
2021-10-04 10:21:03 -04:00
case STATE_INIT:
2017-10-01 18:54:01 +02:00
ShowBg(0);
ShowBg(1);
ShowBg(2);
gBattleCommunication[MULTIUSE_STATE]++;
break;
2021-10-04 10:21:03 -04:00
case STATE_LINK:
2021-03-15 15:22:41 -04:00
if (gMain.anyLinkBattlerHasFrontierPass && gReceivedRemoteLinkPlayers == 0)
2021-03-04 17:48:40 -05:00
CreateTask(Task_ReconnectWithLinkPlayers, 5);
2017-10-01 18:54:01 +02:00
gBattleCommunication[MULTIUSE_STATE]++;
break;
2021-10-04 10:21:03 -04:00
case STATE_WAIT_LINK:
2021-03-04 17:48:40 -05:00
if (!FuncIsActiveTask(Task_ReconnectWithLinkPlayers))
2017-10-01 18:54:01 +02:00
gBattleCommunication[MULTIUSE_STATE]++;
break;
2021-10-04 10:21:03 -04:00
case STATE_ASK_RECORD:
2017-10-01 18:54:01 +02:00
if (!gPaletteFade.active)
{
2021-10-04 10:21:03 -04:00
// "Would you like to record your battle on your FRONTIER PASS?"
2021-10-08 16:50:52 -04:00
BattlePutTextOnWindow(gText_RecordBattleToPass, B_WIN_MSG);
2017-10-01 18:54:01 +02:00
gBattleCommunication[MULTIUSE_STATE]++;
}
break;
2021-10-04 10:21:03 -04:00
case STATE_PRINT_YES_NO:
2021-10-08 16:50:52 -04:00
if (!IsTextPrinterActive(B_WIN_MSG))
2017-10-01 18:54:01 +02:00
{
HandleBattleWindow(YESNOBOX_X_Y, 0);
2021-10-08 16:50:52 -04:00
BattlePutTextOnWindow(gText_BattleYesNoChoice, B_WIN_YESNO);
2017-10-01 18:54:01 +02:00
gBattleCommunication[CURSOR_POSITION] = 1;
2017-10-29 16:15:23 +01:00
BattleCreateYesNoCursorAt(1);
2017-10-01 18:54:01 +02:00
gBattleCommunication[MULTIUSE_STATE]++;
}
break;
2021-10-04 10:21:03 -04:00
case STATE_HANDLE_YES_NO:
2020-09-04 21:11:55 -04:00
if (JOY_NEW(DPAD_UP))
2017-10-01 18:54:01 +02:00
{
if (gBattleCommunication[CURSOR_POSITION] != 0)
{
2021-10-04 10:21:03 -04:00
// Moved cursor onto Yes
2017-10-01 18:54:01 +02:00
PlaySE(SE_SELECT);
2017-10-29 16:15:23 +01:00
BattleDestroyYesNoCursorAt(gBattleCommunication[CURSOR_POSITION]);
2017-10-01 18:54:01 +02:00
gBattleCommunication[CURSOR_POSITION] = 0;
2017-10-29 16:15:23 +01:00
BattleCreateYesNoCursorAt(0);
2017-10-01 18:54:01 +02:00
}
}
2020-09-04 21:11:55 -04:00
else if (JOY_NEW(DPAD_DOWN))
2017-10-01 18:54:01 +02:00
{
if (gBattleCommunication[CURSOR_POSITION] == 0)
{
2021-10-04 10:21:03 -04:00
// Moved cursor onto No
2017-10-01 18:54:01 +02:00
PlaySE(SE_SELECT);
2017-10-29 16:15:23 +01:00
BattleDestroyYesNoCursorAt(gBattleCommunication[CURSOR_POSITION]);
2017-10-01 18:54:01 +02:00
gBattleCommunication[CURSOR_POSITION] = 1;
2017-10-29 16:15:23 +01:00
BattleCreateYesNoCursorAt(1);
2017-10-01 18:54:01 +02:00
}
}
2020-09-04 21:11:55 -04:00
else if (JOY_NEW(A_BUTTON))
2017-10-01 18:54:01 +02:00
{
PlaySE(SE_SELECT);
if (gBattleCommunication[CURSOR_POSITION] == 0)
{
2021-10-04 10:21:03 -04:00
// Selected Yes
HandleBattleWindow(YESNOBOX_X_Y, WINDOW_CLEAR);
2017-10-01 18:54:01 +02:00
gBattleCommunication[1] = MoveRecordedBattleToSaveData();
2021-10-04 10:21:03 -04:00
gBattleCommunication[MULTIUSE_STATE] = STATE_RECORD_YES;
2017-10-01 18:54:01 +02:00
}
else
{
2021-10-04 10:21:03 -04:00
// Selected No
2017-10-01 18:54:01 +02:00
gBattleCommunication[MULTIUSE_STATE]++;
}
}
2020-09-04 21:11:55 -04:00
else if (JOY_NEW(B_BUTTON))
2017-10-01 18:54:01 +02:00
{
PlaySE(SE_SELECT);
gBattleCommunication[MULTIUSE_STATE]++;
}
break;
2021-10-04 10:21:03 -04:00
case STATE_RECORD_NO:
2018-12-31 02:22:21 -06:00
if (IsLinkTaskFinished() == TRUE)
2017-10-01 18:54:01 +02:00
{
HandleBattleWindow(YESNOBOX_X_Y, WINDOW_CLEAR);
2021-03-15 15:22:41 -04:00
if (gMain.anyLinkBattlerHasFrontierPass)
2017-10-01 18:54:01 +02:00
{
2021-10-04 10:21:03 -04:00
// Other battlers may be recording, wait for them
2020-08-13 03:09:47 -04:00
SetLinkStandbyCallback();
2021-10-08 16:50:52 -04:00
BattlePutTextOnWindow(gText_LinkStandby3, B_WIN_MSG);
2017-10-01 18:54:01 +02:00
}
2021-10-04 10:21:03 -04:00
gBattleCommunication[MULTIUSE_STATE]++; // STATE_END_RECORD_NO
2017-10-01 18:54:01 +02:00
}
break;
2021-10-04 10:21:03 -04:00
case STATE_WAIT_END:
2017-10-01 18:54:01 +02:00
if (--gBattleCommunication[1] == 0)
{
2021-03-15 15:22:41 -04:00
if (gMain.anyLinkBattlerHasFrontierPass && !gWirelessCommType)
2020-08-13 03:09:47 -04:00
SetCloseLinkCallback();
2017-10-01 18:54:01 +02:00
gBattleCommunication[MULTIUSE_STATE]++;
}
break;
2021-10-04 10:21:03 -04:00
case STATE_END:
2021-03-15 15:22:41 -04:00
if (!gMain.anyLinkBattlerHasFrontierPass || gWirelessCommType || gReceivedRemoteLinkPlayers != 1)
2017-10-01 18:54:01 +02:00
{
2021-10-04 10:21:03 -04:00
gMain.anyLinkBattlerHasFrontierPass = FALSE;
2017-10-01 18:54:01 +02:00
if (!gPaletteFade.active)
{
SetMainCallback2(gMain.savedCallback);
FreeBattleResources();
FreeBattleSpritesData();
FreeMonSpritesGfx();
}
}
break;
2021-10-04 10:21:03 -04:00
case STATE_RECORD_YES:
2017-10-01 18:54:01 +02:00
if (gBattleCommunication[1] == 1)
{
PlaySE(SE_SAVE);
BattleStringExpandPlaceholdersToDisplayedString(gText_BattleRecordedOnPass);
2021-10-08 16:50:52 -04:00
BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_MSG);
2021-10-04 10:21:03 -04:00
gBattleCommunication[1] = 128; // Delay
2017-10-01 18:54:01 +02:00
gBattleCommunication[MULTIUSE_STATE]++;
}
else
{
2019-12-14 03:58:20 -05:00
BattleStringExpandPlaceholdersToDisplayedString(BattleFrontier_BattleTowerBattleRoom_Text_RecordCouldntBeSaved);
2021-10-08 16:50:52 -04:00
BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_MSG);
2021-10-04 10:21:03 -04:00
gBattleCommunication[1] = 128; // Delay
2017-10-01 18:54:01 +02:00
gBattleCommunication[MULTIUSE_STATE]++;
}
break;
2021-10-04 10:21:03 -04:00
case STATE_RECORD_WAIT:
2021-10-08 16:50:52 -04:00
if (IsLinkTaskFinished() == TRUE && !IsTextPrinterActive(B_WIN_MSG) && --gBattleCommunication[1] == 0)
2017-10-01 18:54:01 +02:00
{
2021-03-15 15:22:41 -04:00
if (gMain.anyLinkBattlerHasFrontierPass)
2017-10-01 18:54:01 +02:00
{
2020-08-13 03:09:47 -04:00
SetLinkStandbyCallback();
2021-10-08 16:50:52 -04:00
BattlePutTextOnWindow(gText_LinkStandby3, B_WIN_MSG);
2017-10-01 18:54:01 +02:00
}
gBattleCommunication[MULTIUSE_STATE]++;
}
break;
2021-10-04 10:21:03 -04:00
case STATE_END_RECORD_YES:
case STATE_END_RECORD_NO:
2021-10-08 16:50:52 -04:00
if (!IsTextPrinterActive(B_WIN_MSG))
2017-10-01 18:54:01 +02:00
{
2021-03-15 15:22:41 -04:00
if (gMain.anyLinkBattlerHasFrontierPass)
2017-10-01 18:54:01 +02:00
{
2018-12-31 02:22:21 -06:00
if (IsLinkTaskFinished() == TRUE)
2017-10-01 18:54:01 +02:00
{
2021-10-04 10:21:03 -04:00
BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, RGB_BLACK);
gBattleCommunication[1] = 32; // Delay
gBattleCommunication[MULTIUSE_STATE] = STATE_WAIT_END;
2017-10-01 18:54:01 +02:00
}
}
else
{
2021-10-04 10:21:03 -04:00
BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, RGB_BLACK);
gBattleCommunication[1] = 32; // Delay
gBattleCommunication[MULTIUSE_STATE] = STATE_WAIT_END;
2017-10-01 18:54:01 +02:00
}
}
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);
2018-12-23 14:52:47 +01:00
if (StringCompareWithoutExtCtrlCodes(nickname, sText_ShedinjaJpnName) == 0)
2017-10-01 18:54:01 +02:00
SetMonData(mon, MON_DATA_LANGUAGE, &language);
}
}
2021-10-04 10:21:03 -04:00
u32 GetBattleWindowTemplatePixelWidth(u32 windowsType, u32 tableId)
2017-10-01 18:54:01 +02:00
{
2021-10-04 10:21:03 -04:00
return gBattleWindowTemplates[windowsType][tableId].width * 8;
2017-10-01 18:54:01 +02:00
}
2018-02-06 16:09:39 -06:00
#define sBattler data[0]
#define sSpeciesId data[2]
2017-10-01 18:54:01 +02:00
void SpriteCB_WildMon(struct Sprite *sprite)
2017-10-01 18:54:01 +02:00
{
sprite->callback = SpriteCB_MoveWildMonToRight;
2017-10-01 18:54:01 +02:00
StartSpriteAnimIfDifferent(sprite, 0);
2018-10-16 22:19:53 +02:00
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));
2017-10-01 18:54:01 +02:00
}
static void SpriteCB_MoveWildMonToRight(struct Sprite *sprite)
2017-10-01 18:54:01 +02:00
{
2018-02-08 11:17:41 +01:00
if ((gIntroSlideFlags & 1) == 0)
2017-10-01 18:54:01 +02:00
{
2021-07-07 09:11:52 -04:00
sprite->x2 += 2;
if (sprite->x2 == 0)
2017-10-01 18:54:01 +02:00
{
sprite->callback = SpriteCB_WildMonShowHealthbox;
2017-10-01 18:54:01 +02:00
}
}
}
static void SpriteCB_WildMonShowHealthbox(struct Sprite *sprite)
2017-10-01 18:54:01 +02:00
{
if (sprite->animEnded)
{
2021-01-22 20:03:21 -05:00
StartHealthboxSlideIn(sprite->sBattler);
2018-02-06 16:09:39 -06:00
SetHealthboxSpriteVisible(gHealthboxSpriteIds[sprite->sBattler]);
sprite->callback = SpriteCB_WildMonAnimate;
2017-10-01 18:54:01 +02:00
StartSpriteAnimIfDifferent(sprite, 0);
2018-10-16 22:19:53 +02:00
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));
2017-10-01 18:54:01 +02:00
}
}
static void SpriteCB_WildMonAnimate(struct Sprite *sprite)
2017-10-01 18:54:01 +02:00
{
if (!gPaletteFade.active)
{
2018-02-06 16:09:39 -06:00
BattleAnimateFrontSprite(sprite, sprite->sSpeciesId, FALSE, 1);
2017-10-01 18:54:01 +02:00
}
}
void SpriteCallbackDummy_2(struct Sprite *sprite)
{
}
2021-10-04 10:21:03 -04:00
#define sNumFlickers data[3]
#define sDelay data[4]
static void SpriteCB_Flicker(struct Sprite *sprite)
2017-10-01 18:54:01 +02:00
{
2021-10-04 10:21:03 -04:00
sprite->sDelay--;
if (sprite->sDelay == 0)
2017-10-01 18:54:01 +02:00
{
2021-10-04 10:21:03 -04:00
sprite->sDelay = 8;
2017-10-01 18:54:01 +02:00
sprite->invisible ^= 1;
2021-10-04 10:21:03 -04:00
sprite->sNumFlickers--;
if (sprite->sNumFlickers == 0)
2017-10-01 18:54:01 +02:00
{
sprite->invisible = FALSE;
sprite->callback = SpriteCallbackDummy_2;
// sFlickerArray[0] = 0;
2017-10-01 18:54:01 +02:00
}
}
}
2021-10-04 10:21:03 -04:00
#undef sNumFlickers
#undef sDelay
2017-10-01 18:54:01 +02:00
extern const struct MonCoords gMonFrontPicCoords[];
2018-06-20 23:07:51 +02:00
void SpriteCB_FaintOpponentMon(struct Sprite *sprite)
2017-10-01 18:54:01 +02:00
{
2018-02-06 16:09:39 -06:00
u8 battler = sprite->sBattler;
2019-04-07 12:40:18 +02:00
u32 personality = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battler]], MON_DATA_PERSONALITY);
2017-10-01 18:54:01 +02:00
u16 species;
u8 yOffset;
2018-02-06 16:09:39 -06:00
if (gBattleSpritesDataPtr->battlerData[battler].transformSpecies != 0)
species = gBattleSpritesDataPtr->battlerData[battler].transformSpecies;
2017-10-01 18:54:01 +02:00
else
2018-02-06 16:09:39 -06:00
species = sprite->sSpeciesId;
2017-10-01 18:54:01 +02:00
if (species == SPECIES_UNOWN)
{
2019-04-07 12:40:18 +02:00
species = GetUnownSpeciesId(personality);
yOffset = gMonFrontPicCoords[species].y_offset;
2017-10-01 18:54:01 +02:00
}
else if (species > NUM_SPECIES)
{
yOffset = gMonFrontPicCoords[SPECIES_NONE].y_offset;
}
else
{
yOffset = gMonFrontPicCoords[species].y_offset;
}
2017-12-02 21:44:50 +01:00
sprite->data[3] = 8 - yOffset / 8;
sprite->data[4] = 1;
2018-06-20 23:07:51 +02:00
sprite->callback = SpriteCB_AnimFaintOpponent;
2017-10-01 18:54:01 +02:00
}
2018-06-20 23:07:51 +02:00
static void SpriteCB_AnimFaintOpponent(struct Sprite *sprite)
2017-10-01 18:54:01 +02:00
{
s32 i;
2018-06-20 23:07:51 +02:00
if (--sprite->data[4] == 0)
2017-10-01 18:54:01 +02:00
{
2017-12-02 21:44:50 +01:00
sprite->data[4] = 2;
2021-07-07 09:11:52 -04:00
sprite->y2 += 8; // Move the sprite down.
2018-06-20 23:07:51 +02:00
if (--sprite->data[3] < 0)
2017-10-01 18:54:01 +02:00
{
FreeSpriteOamMatrix(sprite);
DestroySprite(sprite);
}
2018-06-20 23:07:51 +02:00
else // Erase bottom part of the sprite to create a smooth illusion of mon falling down.
2017-10-01 18:54:01 +02:00
{
u8 *dst = gMonSpritesGfxPtr->sprites.byte[GetBattlerPosition(sprite->sBattler)] + (sprite->data[3] << 8);
2017-10-01 18:54:01 +02:00
for (i = 0; i < 0x100; i++)
*(dst++) = 0;
StartSpriteAnim(sprite, 0);
2017-10-01 18:54:01 +02:00
}
}
}
2020-07-14 11:13:03 +02:00
// Used when selecting a move, which can hit multiple targets, in double battles.
void SpriteCB_ShowAsMoveTarget(struct Sprite *sprite)
2017-10-01 18:54:01 +02:00
{
2017-12-02 21:44:50 +01:00
sprite->data[3] = 8;
sprite->data[4] = sprite->invisible;
sprite->callback = SpriteCB_BlinkVisible;
2017-10-01 18:54:01 +02:00
}
static void SpriteCB_BlinkVisible(struct Sprite *sprite)
2017-10-01 18:54:01 +02:00
{
2020-07-14 11:13:03 +02:00
if (--sprite->data[3] == 0)
2017-10-01 18:54:01 +02:00
{
sprite->invisible ^= 1;
2017-12-02 21:44:50 +01:00
sprite->data[3] = 8;
2017-10-01 18:54:01 +02:00
}
}
void SpriteCB_HideAsMoveTarget(struct Sprite *sprite)
2017-10-01 18:54:01 +02:00
{
2017-12-02 21:44:50 +01:00
sprite->invisible = sprite->data[4];
sprite->data[4] = FALSE;
2017-10-01 18:54:01 +02:00
sprite->callback = SpriteCallbackDummy_2;
}
2021-10-04 10:21:03 -04:00
void SpriteCB_OpponentMonFromBall(struct Sprite *sprite)
2017-10-02 23:32:39 +02:00
{
if (sprite->affineAnimEnded)
{
2021-01-13 15:17:32 -05:00
if (!(gHitMarker & HITMARKER_NO_ANIMATIONS) || gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK))
2017-10-02 23:32:39 +02:00
{
2018-02-06 16:09:39 -06:00
if (HasTwoFramesAnimation(sprite->sSpeciesId))
2017-10-02 23:32:39 +02:00
StartSpriteAnim(sprite, 1);
}
2018-02-06 16:09:39 -06:00
BattleAnimateFrontSprite(sprite, sprite->sSpeciesId, TRUE, 1);
2017-10-02 23:32:39 +02:00
}
}
2021-01-22 23:22:37 -05:00
// This callback is frequently overwritten by SpriteCB_TrainerSlideIn
void SpriteCB_BattleSpriteStartSlideLeft(struct Sprite *sprite)
2017-10-02 23:32:39 +02:00
{
2021-01-22 23:22:37 -05:00
sprite->callback = SpriteCB_BattleSpriteSlideLeft;
2017-10-02 23:32:39 +02:00
}
2021-01-22 23:22:37 -05:00
static void SpriteCB_BattleSpriteSlideLeft(struct Sprite *sprite)
2017-10-02 23:32:39 +02:00
{
if (!(gIntroSlideFlags & 1))
2017-10-02 23:32:39 +02:00
{
2021-07-07 09:11:52 -04:00
sprite->x2 -= 2;
if (sprite->x2 == 0)
2017-10-02 23:32:39 +02:00
{
2021-10-04 10:21:03 -04:00
sprite->callback = SpriteCB_Idle;
2017-12-02 21:44:50 +01:00
sprite->data[1] = 0;
2017-10-02 23:32:39 +02:00
}
}
}
2021-01-22 23:22:37 -05:00
// Unused
2021-10-04 10:21:03 -04:00
static void SetIdleSpriteCallback(struct Sprite *sprite)
2017-10-02 23:32:39 +02:00
{
2021-10-04 10:21:03 -04:00
sprite->callback = SpriteCB_Idle;
2017-10-02 23:32:39 +02:00
}
2021-10-04 10:21:03 -04:00
static void SpriteCB_Idle(struct Sprite *sprite)
2017-10-02 23:32:39 +02:00
{
}
2021-01-22 20:03:21 -05:00
#define sSpeedX data[1]
#define sSpeedY data[2]
void SpriteCB_FaintSlideAnim(struct Sprite *sprite)
2017-10-02 23:32:39 +02:00
{
2018-02-08 11:17:41 +01:00
if (!(gIntroSlideFlags & 1))
2017-10-02 23:32:39 +02:00
{
2021-07-07 09:11:52 -04:00
sprite->x2 += sprite->sSpeedX;
sprite->y2 += sprite->sSpeedY;
2017-10-02 23:32:39 +02:00
}
}
2021-01-22 20:03:21 -05:00
#undef sSpeedX
#undef sSpeedY
2019-05-02 23:10:01 +02:00
#define sSinIndex data[3]
#define sDelta data[4]
#define sAmplitude data[5]
#define sBouncerSpriteId data[6]
#define sWhich data[7]
2018-06-20 23:07:51 +02:00
void DoBounceEffect(u8 battler, u8 which, s8 delta, s8 amplitude)
2017-10-02 23:32:39 +02:00
{
2018-06-20 23:07:51 +02:00
u8 invisibleSpriteId;
u8 bouncerSpriteId;
2017-10-02 23:32:39 +02:00
2018-06-20 23:07:51 +02:00
switch (which)
2017-10-02 23:32:39 +02:00
{
2018-06-20 23:07:51 +02:00
case BOUNCE_HEALTHBOX:
default:
if (gBattleSpritesDataPtr->healthBoxesData[battler].healthboxIsBouncing)
2017-10-02 23:32:39 +02:00
return;
2018-06-20 23:07:51 +02:00
break;
case BOUNCE_MON:
if (gBattleSpritesDataPtr->healthBoxesData[battler].battlerIsBouncing)
2017-10-02 23:32:39 +02:00
return;
2018-06-20 23:07:51 +02:00
break;
2017-10-02 23:32:39 +02:00
}
2018-06-20 23:07:51 +02:00
invisibleSpriteId = CreateInvisibleSpriteWithCallback(SpriteCB_BounceEffect);
if (which == BOUNCE_HEALTHBOX)
2017-10-02 23:32:39 +02:00
{
2018-06-20 23:07:51 +02:00
bouncerSpriteId = gHealthboxSpriteIds[battler];
gBattleSpritesDataPtr->healthBoxesData[battler].healthboxBounceSpriteId = invisibleSpriteId;
gBattleSpritesDataPtr->healthBoxesData[battler].healthboxIsBouncing = 1;
gSprites[invisibleSpriteId].sSinIndex = 128; // 0
2017-10-02 23:32:39 +02:00
}
else
{
2018-06-20 23:07:51 +02:00
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;
2019-05-02 23:10:01 +02:00
gSprites[invisibleSpriteId].sBattler = battler;
2021-07-07 09:11:52 -04:00
gSprites[bouncerSpriteId].x2 = 0;
gSprites[bouncerSpriteId].y2 = 0;
2017-10-02 23:32:39 +02:00
}
2018-06-20 23:07:51 +02:00
void EndBounceEffect(u8 battler, u8 which)
2017-10-02 23:32:39 +02:00
{
2018-06-20 23:07:51 +02:00
u8 bouncerSpriteId;
2017-10-02 23:32:39 +02:00
2018-06-20 23:07:51 +02:00
if (which == BOUNCE_HEALTHBOX)
2017-10-02 23:32:39 +02:00
{
2018-06-20 23:07:51 +02:00
if (!gBattleSpritesDataPtr->healthBoxesData[battler].healthboxIsBouncing)
2017-10-02 23:32:39 +02:00
return;
2018-06-20 23:07:51 +02:00
bouncerSpriteId = gSprites[gBattleSpritesDataPtr->healthBoxesData[battler].healthboxBounceSpriteId].sBouncerSpriteId;
DestroySprite(&gSprites[gBattleSpritesDataPtr->healthBoxesData[battler].healthboxBounceSpriteId]);
gBattleSpritesDataPtr->healthBoxesData[battler].healthboxIsBouncing = 0;
2017-10-02 23:32:39 +02:00
}
else
{
2018-06-20 23:07:51 +02:00
if (!gBattleSpritesDataPtr->healthBoxesData[battler].battlerIsBouncing)
2017-10-02 23:32:39 +02:00
return;
2018-06-20 23:07:51 +02:00
bouncerSpriteId = gSprites[gBattleSpritesDataPtr->healthBoxesData[battler].battlerBounceSpriteId].sBouncerSpriteId;
DestroySprite(&gSprites[gBattleSpritesDataPtr->healthBoxesData[battler].battlerBounceSpriteId]);
gBattleSpritesDataPtr->healthBoxesData[battler].battlerIsBouncing = 0;
2017-10-02 23:32:39 +02:00
}
2018-06-20 23:07:51 +02:00
2021-07-07 09:11:52 -04:00
gSprites[bouncerSpriteId].x2 = 0;
gSprites[bouncerSpriteId].y2 = 0;
2017-10-02 23:32:39 +02:00
}
2018-06-20 23:07:51 +02:00
static void SpriteCB_BounceEffect(struct Sprite *sprite)
2017-10-02 23:32:39 +02:00
{
2018-06-20 23:07:51 +02:00
u8 bouncerSpriteId = sprite->sBouncerSpriteId;
2019-05-02 23:10:01 +02:00
s32 index = sprite->sSinIndex;
s32 y = Sin(index, sprite->sAmplitude) + sprite->sAmplitude;
2017-10-02 23:32:39 +02:00
gSprites[bouncerSpriteId].y2 = y;
2018-06-20 23:07:51 +02:00
sprite->sSinIndex = (sprite->sSinIndex + sprite->sDelta) & 0xFF;
2017-10-02 23:32:39 +02:00
}
2018-06-20 23:07:51 +02:00
#undef sSinIndex
#undef sDelta
#undef sAmplitude
#undef sBouncerSpriteId
#undef sWhich
2021-10-04 10:21:03 -04:00
void SpriteCB_PlayerMonFromBall(struct Sprite *sprite)
2017-10-02 23:32:39 +02:00
{
if (sprite->affineAnimEnded)
2018-02-06 16:09:39 -06:00
BattleAnimateBackSprite(sprite, sprite->sSpeciesId);
2017-10-02 23:32:39 +02:00
}
2021-10-04 10:21:03 -04:00
static void SpriteCB_TrainerThrowObject_Main(struct Sprite *sprite)
2017-10-02 23:32:39 +02:00
{
2021-11-08 13:18:58 -05:00
AnimSetCenterToCornerVecX(sprite);
2017-10-02 23:32:39 +02:00
if (sprite->animEnded)
2021-10-04 10:21:03 -04:00
sprite->callback = SpriteCB_Idle;
2017-10-02 23:32:39 +02:00
}
// Sprite callback for a trainer back pic to throw an object
2021-10-04 10:21:03 -04:00
// (Wally throwing a ball, throwing Pokéblocks/balls in the Safari Zone)
2020-08-30 15:11:44 -04:00
void SpriteCB_TrainerThrowObject(struct Sprite *sprite)
2017-10-02 23:32:39 +02:00
{
StartSpriteAnim(sprite, 1);
2021-10-04 10:21:03 -04:00
sprite->callback = SpriteCB_TrainerThrowObject_Main;
2017-10-02 23:32:39 +02:00
}
2021-11-08 13:18:58 -05:00
void AnimSetCenterToCornerVecX(struct Sprite *sprite)
2017-10-02 23:32:39 +02:00
{
if (sprite->animDelayCounter == 0)
2021-11-08 13:18:58 -05:00
sprite->centerToCornerVecX = sCenterToCornerVecXs[sprite->animCmdIndex];
2017-10-02 23:32:39 +02:00
}
void BeginBattleIntroDummy(void)
2017-10-02 23:32:39 +02:00
{
}
void BeginBattleIntro(void)
{
BattleStartClearSetData();
gBattleCommunication[1] = 0;
gBattleStruct->introState = 0;
gBattleMainFunc = DoBattleIntro;
2017-10-02 23:32:39 +02:00
}
2017-10-06 19:09:37 +02:00
static void BattleMainCB1(void)
2017-10-02 23:32:39 +02:00
{
u32 battler;
2017-10-02 23:32:39 +02:00
gBattleMainFunc();
for (battler = 0; battler < gBattlersCount; battler++)
gBattlerControllerFuncs[battler](battler);
2017-10-02 23:32:39 +02:00
}
static void BattleStartClearSetData(void)
{
s32 i;
2017-10-06 00:12:01 +02:00
TurnValuesCleanUp(FALSE);
2017-10-02 23:32:39 +02:00
SpecialStatusesClear();
2019-01-27 13:52:02 +01:00
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));
2018-02-05 19:46:59 -06:00
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
2017-10-02 23:32:39 +02:00
{
gStatuses3[i] = 0;
2021-10-18 21:55:16 -03:00
gStatuses4[i] = 0;
gDisableStructs[i].isFirstTurn = 2;
gLastMoves[i] = MOVE_NONE;
gLastLandedMoves[i] = MOVE_NONE;
2017-11-26 14:17:02 +01:00
gLastHitByType[i] = 0;
gLastResultingMoves[i] = MOVE_NONE;
gLastHitBy[i] = 0xFF;
gLockedMoves[i] = MOVE_NONE;
gLastPrintedMoves[i] = MOVE_NONE;
2017-10-02 23:32:39 +02:00
gBattleResources->flags->flags[i] = 0;
2017-11-26 17:15:28 +01:00
gPalaceSelectionBattleScripts[i] = 0;
gBattleStruct->lastTakenMove[i] = MOVE_NONE;
gBattleStruct->choicedMove[i] = MOVE_NONE;
2019-01-27 13:52:02 +01:00
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;
2019-01-27 13:52:02 +01:00
gBattleStruct->AI_monToSwitchIntoId[i] = PARTY_SIZE;
gBattleStruct->skyDropTargets[i] = 0xFF;
2022-09-24 12:12:31 -04:00
gBattleStruct->overwrittenAbilities[i] = ABILITY_NONE;
2017-10-02 23:32:39 +02:00
}
2018-09-22 18:37:03 +02:00
gLastUsedMove = 0;
2018-08-11 12:16:00 +02:00
gFieldStatuses = 0;
2017-10-02 23:32:39 +02:00
2020-12-02 23:09:35 -03:00
gHasFetchedBall = FALSE;
gLastUsedBall = 0;
2018-02-06 16:09:39 -06:00
gBattlerAttacker = 0;
gBattlerTarget = 0;
gEffectBattler = 0;
gBattleScripting.battler = 0;
gBattlerAbility = 0;
2017-10-02 23:32:39 +02:00
gBattleWeather = 0;
gHitMarker = 0;
if (!(gBattleTypeFlags & BATTLE_TYPE_RECORDED))
{
if (!(gBattleTypeFlags & BATTLE_TYPE_LINK) && gSaveBlock2Ptr->optionsBattleSceneOff == TRUE)
gHitMarker |= HITMARKER_NO_ANIMATIONS;
}
2021-01-13 15:17:32 -05:00
else if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK)) && GetBattleSceneInRecordedBattle())
2019-01-27 13:52:02 +01:00
{
2017-10-02 23:32:39 +02:00
gHitMarker |= HITMARKER_NO_ANIMATIONS;
2019-01-27 13:52:02 +01:00
}
2017-10-02 23:32:39 +02:00
gBattleScripting.battleStyle = gSaveBlock2Ptr->optionsBattleStyle;
2018-08-03 18:01:14 +02:00
gBattleScripting.expOnCatch = (B_EXP_CATCH >= GEN_6);
gBattleScripting.monCaught = FALSE;
2017-10-02 23:32:39 +02:00
gMultiHitCounter = 0;
gBattleScripting.savedDmg = 0;
2017-10-02 23:32:39 +02:00
gBattleOutcome = 0;
2018-02-06 13:48:02 -06:00
gBattleControllerExecFlags = 0;
2017-10-02 23:32:39 +02:00
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;
2018-02-08 11:17:41 +01:00
gIntroSlideFlags = 0;
2017-10-02 23:32:39 +02:00
gBattleScripting.animTurn = 0;
gBattleScripting.animTargetsHit = 0;
gLeveledUpInBattle = 0;
2018-02-05 19:46:59 -06:00
gAbsentBattlerFlags = 0;
2017-10-06 00:12:01 +02:00
gBattleStruct->runTries = 0;
2018-07-01 11:15:42 +02:00
gBattleStruct->safariGoNearCounter = 0;
gBattleStruct->safariPkblThrowCounter = 0;
gBattleStruct->safariCatchFactor = gSpeciesInfo[GetMonData(&gEnemyParty[0], MON_DATA_SPECIES)].catchRate * 100 / 1275;
2018-07-01 11:15:42 +02:00
gBattleStruct->safariEscapeFactor = 3;
2017-10-02 23:32:39 +02:00
gBattleStruct->wildVictorySong = 0;
gBattleStruct->moneyMultiplier = 1;
2018-12-07 23:50:56 +01:00
gBattleStruct->givenExpMons = 0;
2020-07-16 20:12:12 -04:00
gBattleStruct->palaceFlags = 0;
2017-10-02 23:32:39 +02:00
gRandomTurnNumber = Random();
2018-06-28 21:06:32 +02:00
gBattleResults.shinyWildMon = IsMonShiny(&gEnemyParty[0]);
2017-10-02 23:32:39 +02:00
2018-11-11 18:33:16 +01:00
gBattleStruct->arenaLostPlayerMons = 0;
gBattleStruct->arenaLostOpponentMons = 0;
2018-09-16 21:08:49 +02:00
gBattleStruct->mega.triggerSpriteId = 0xFF;
2023-08-09 22:12:26 -05:00
gBattleStruct->burst.triggerSpriteId = 0xFF;
2022-08-24 21:56:24 +02:00
for (i = 0; i < ARRAY_COUNT(gSideTimers); i++)
{
gSideTimers[i].stickyWebBattlerId = 0xFF;
}
gBattleStruct->appearedInBattle = 0;
gBattleStruct->beatUpSlot = 0;
2022-08-24 21:56:24 +02:00
for (i = 0; i < PARTY_SIZE; i++)
2021-10-28 17:52:52 -04:00
{
gBattleStruct->usedHeldItems[i][B_SIDE_PLAYER] = 0;
gBattleStruct->usedHeldItems[i][B_SIDE_OPPONENT] = 0;
gBattleStruct->itemLost[i].originalItem = GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM);
2022-05-05 11:41:27 -07:00
gPartyCriticalHits[i] = 0;
gBattleStruct->allowedToChangeFormInWeather[i][B_SIDE_PLAYER] = FALSE;
gBattleStruct->allowedToChangeFormInWeather[i][B_SIDE_OPPONENT] = FALSE;
2021-10-28 17:52:52 -04:00
}
gBattleStruct->swapDamageCategory = FALSE; // Photon Geyser, Shell Side Arm, Light That Burns the Sky
gSelectedMonPartyId = PARTY_SIZE; // Revival Blessing
2017-10-02 23:32:39 +02:00
}
2023-08-29 16:20:16 +02:00
void SwitchInClearSetData(u32 battler)
2017-10-02 23:32:39 +02:00
{
s32 i;
2023-08-29 16:20:16 +02:00
struct DisableStruct disableStructCopy = gDisableStructs[battler];
2017-10-02 23:32:39 +02:00
2023-08-29 16:20:16 +02:00
ClearIllusionMon(battler);
2017-10-02 23:32:39 +02:00
if (gBattleMoves[gCurrentMove].effect != EFFECT_BATON_PASS)
{
2018-11-18 20:00:36 +01:00
for (i = 0; i < NUM_BATTLE_STATS; i++)
2023-08-29 16:20:16 +02:00
gBattleMons[battler].statStages[i] = DEFAULT_STAT_STAGE;
2018-02-05 19:46:59 -06:00
for (i = 0; i < gBattlersCount; i++)
2017-10-02 23:32:39 +02:00
{
2023-08-29 16:20:16 +02:00
if ((gBattleMons[i].status2 & STATUS2_ESCAPE_PREVENTION) && gDisableStructs[i].battlerPreventingEscape == battler)
2017-10-02 23:32:39 +02:00
gBattleMons[i].status2 &= ~STATUS2_ESCAPE_PREVENTION;
2023-08-29 16:20:16 +02:00
if ((gStatuses3[i] & STATUS3_ALWAYS_HITS) && gDisableStructs[i].battlerWithSureHit == battler)
2017-10-02 23:32:39 +02:00
{
gStatuses3[i] &= ~STATUS3_ALWAYS_HITS;
2018-02-06 16:09:39 -06:00
gDisableStructs[i].battlerWithSureHit = 0;
2017-10-02 23:32:39 +02:00
}
}
}
if (gBattleMoves[gCurrentMove].effect == EFFECT_BATON_PASS)
{
2023-08-29 16:20:16 +02:00
gBattleMons[battler].status2 &= (STATUS2_CONFUSION | STATUS2_FOCUS_ENERGY | STATUS2_SUBSTITUTE | STATUS2_ESCAPE_PREVENTION | STATUS2_CURSED);
gStatuses3[battler] &= (STATUS3_LEECHSEED_BATTLER | STATUS3_LEECHSEED | STATUS3_ALWAYS_HITS | STATUS3_PERISH_SONG | STATUS3_ROOTED
2021-10-19 09:38:27 -04:00
| STATUS3_GASTRO_ACID | STATUS3_EMBARGO | STATUS3_TELEKINESIS | STATUS3_MAGNET_RISE | STATUS3_HEAL_BLOCK
| STATUS3_AQUA_RING | STATUS3_POWER_TRICK);
2023-08-29 16:20:16 +02:00
gStatuses4[battler] &= (STATUS4_MUD_SPORT | STATUS4_WATER_SPORT | STATUS4_INFINITE_CONFUSION);
2018-02-05 19:46:59 -06:00
for (i = 0; i < gBattlersCount; i++)
2017-10-02 23:32:39 +02:00
{
2023-08-29 16:20:16 +02:00
if (GetBattlerSide(battler) != GetBattlerSide(i)
2017-10-02 23:32:39 +02:00
&& (gStatuses3[i] & STATUS3_ALWAYS_HITS) != 0
2023-08-29 16:20:16 +02:00
&& (gDisableStructs[i].battlerWithSureHit == battler))
2017-10-02 23:32:39 +02:00
{
2021-09-24 14:30:15 -04:00
gStatuses3[i] &= ~STATUS3_ALWAYS_HITS;
2020-07-16 20:12:12 -04:00
gStatuses3[i] |= STATUS3_ALWAYS_HITS_TURN(2);
2017-10-02 23:32:39 +02:00
}
}
2023-08-29 16:20:16 +02:00
if (gStatuses3[battler] & STATUS3_POWER_TRICK)
SWAP(gBattleMons[battler].attack, gBattleMons[battler].defense, i);
2017-10-02 23:32:39 +02:00
}
else
{
2023-08-29 16:20:16 +02:00
gBattleMons[battler].status2 = 0;
gStatuses3[battler] = 0;
gStatuses4[battler] = 0;
2017-10-02 23:32:39 +02:00
}
2018-02-05 19:46:59 -06:00
for (i = 0; i < gBattlersCount; i++)
2017-10-02 23:32:39 +02:00
{
2023-08-29 16:20:16 +02:00
if (gBattleMons[i].status2 & STATUS2_INFATUATED_WITH(battler))
gBattleMons[i].status2 &= ~STATUS2_INFATUATED_WITH(battler);
if ((gBattleMons[i].status2 & STATUS2_WRAPPED) && *(gBattleStruct->wrappedBy + i) == battler)
2021-09-24 14:30:15 -04:00
gBattleMons[i].status2 &= ~STATUS2_WRAPPED;
2017-10-02 23:32:39 +02:00
}
2023-08-29 16:20:16 +02:00
gActionSelectionCursor[battler] = 0;
gMoveSelectionCursor[battler] = 0;
2017-10-02 23:32:39 +02:00
2023-08-29 16:20:16 +02:00
memset(&gDisableStructs[battler], 0, sizeof(struct DisableStruct));
2017-10-02 23:32:39 +02:00
if (gBattleMoves[gCurrentMove].effect == EFFECT_BATON_PASS)
{
2023-08-29 16:20:16 +02:00
gDisableStructs[battler].substituteHP = disableStructCopy.substituteHP;
gDisableStructs[battler].battlerWithSureHit = disableStructCopy.battlerWithSureHit;
gDisableStructs[battler].perishSongTimer = disableStructCopy.perishSongTimer;
gDisableStructs[battler].battlerPreventingEscape = disableStructCopy.battlerPreventingEscape;
2017-10-02 23:32:39 +02:00
}
2018-01-16 15:12:38 -06:00
gMoveResultFlags = 0;
2023-08-29 16:20:16 +02:00
gDisableStructs[battler].isFirstTurn = 2;
gDisableStructs[battler].truantSwitchInHack = disableStructCopy.truantSwitchInHack;
gLastMoves[battler] = MOVE_NONE;
gLastLandedMoves[battler] = MOVE_NONE;
gLastHitByType[battler] = 0;
gLastResultingMoves[battler] = MOVE_NONE;
gLastPrintedMoves[battler] = MOVE_NONE;
gLastHitBy[battler] = 0xFF;
gBattleStruct->lastTakenMove[battler] = 0;
gBattleStruct->sameMoveTurns[battler] = 0;
gBattleStruct->lastTakenMoveFrom[battler][0] = 0;
gBattleStruct->lastTakenMoveFrom[battler][1] = 0;
gBattleStruct->lastTakenMoveFrom[battler][2] = 0;
gBattleStruct->lastTakenMoveFrom[battler][3] = 0;
gBattleStruct->lastMoveFailed &= ~(gBitTable[battler]);
gBattleStruct->palaceFlags &= ~(gBitTable[battler]);
2022-08-24 21:56:24 +02:00
for (i = 0; i < ARRAY_COUNT(gSideTimers); i++)
{
// Switched into sticky web user slot, so reset stored battler ID
2023-08-29 16:20:16 +02:00
if (gSideTimers[i].stickyWebBattlerId == battler)
gSideTimers[i].stickyWebBattlerId = 0xFF;
}
2017-10-02 23:32:39 +02:00
2018-02-05 19:46:59 -06:00
for (i = 0; i < gBattlersCount; i++)
2017-10-02 23:32:39 +02:00
{
2023-08-29 16:20:16 +02:00
if (i != battler && GetBattlerSide(i) != GetBattlerSide(battler))
gBattleStruct->lastTakenMove[i] = MOVE_NONE;
2017-10-02 23:32:39 +02:00
2023-08-29 16:20:16 +02:00
gBattleStruct->lastTakenMoveFrom[i][battler] = 0;
2019-01-27 13:52:02 +01:00
}
2017-10-02 23:32:39 +02:00
2023-08-29 16:20:16 +02:00
gBattleStruct->choicedMove[battler] = MOVE_NONE;
gBattleResources->flags->flags[battler] = 0;
gCurrentMove = MOVE_NONE;
2018-12-07 23:50:56 +01:00
gBattleStruct->arenaTurnCounter = 0xFF;
2022-08-24 21:56:24 +02:00
2021-09-02 15:33:42 -04:00
// Reset damage to prevent things like red card activating if the switched-in mon is holding it
2023-08-29 16:20:16 +02:00
gSpecialStatuses[battler].physicalDmg = 0;
gSpecialStatuses[battler].specialDmg = 0;
2017-10-02 23:32:39 +02:00
2023-08-29 16:20:16 +02:00
gBattleStruct->overwrittenAbilities[battler] = ABILITY_NONE;
2017-10-02 23:32:39 +02:00
// Clear selected party ID so Revival Blessing doesn't get confused.
gSelectedMonPartyId = PARTY_SIZE;
// Allow for illegal abilities within tests.
if (gTestRunnerEnabled)
{
2023-08-29 16:20:16 +02:00
u32 side = GetBattlerSide(battler);
u32 partyIndex = gBattlerPartyIndexes[battler];
if (TestRunner_Battle_GetForcedAbility(side, partyIndex))
gBattleMons[i].ability = gBattleStruct->overwrittenAbilities[i] = TestRunner_Battle_GetForcedAbility(side, partyIndex);
}
2023-08-29 16:20:16 +02:00
Ai_UpdateSwitchInData(battler);
2017-10-02 23:32:39 +02:00
}
2023-08-29 16:20:16 +02:00
void FaintClearSetData(u32 battler)
2017-10-02 23:32:39 +02:00
{
s32 i;
2018-11-18 20:00:36 +01:00
for (i = 0; i < NUM_BATTLE_STATS; i++)
2023-08-29 16:20:16 +02:00
gBattleMons[battler].statStages[i] = DEFAULT_STAT_STAGE;
2017-10-02 23:32:39 +02:00
2023-08-29 16:20:16 +02:00
gBattleMons[battler].status2 = 0;
gStatuses3[battler] = 0;
gStatuses4[battler] = 0;
2017-10-02 23:32:39 +02:00
2018-02-05 19:46:59 -06:00
for (i = 0; i < gBattlersCount; i++)
2017-10-02 23:32:39 +02:00
{
2023-08-29 16:20:16 +02:00
if ((gBattleMons[i].status2 & STATUS2_ESCAPE_PREVENTION) && gDisableStructs[i].battlerPreventingEscape == battler)
2017-10-02 23:32:39 +02:00
gBattleMons[i].status2 &= ~STATUS2_ESCAPE_PREVENTION;
2023-08-29 16:20:16 +02:00
if (gBattleMons[i].status2 & STATUS2_INFATUATED_WITH(battler))
gBattleMons[i].status2 &= ~STATUS2_INFATUATED_WITH(battler);
if ((gBattleMons[i].status2 & STATUS2_WRAPPED) && *(gBattleStruct->wrappedBy + i) == battler)
2021-09-24 14:30:15 -04:00
gBattleMons[i].status2 &= ~STATUS2_WRAPPED;
2017-10-02 23:32:39 +02:00
}
2023-08-29 16:20:16 +02:00
gActionSelectionCursor[battler] = 0;
gMoveSelectionCursor[battler] = 0;
memset(&gDisableStructs[battler], 0, sizeof(struct DisableStruct));
gProtectStructs[battler].protected = FALSE;
gProtectStructs[battler].spikyShielded = FALSE;
gProtectStructs[battler].kingsShielded = FALSE;
gProtectStructs[battler].banefulBunkered = FALSE;
gProtectStructs[battler].quash = FALSE;
gProtectStructs[battler].obstructed = FALSE;
gProtectStructs[battler].silkTrapped = FALSE;
gProtectStructs[battler].endured = FALSE;
gProtectStructs[battler].noValidMoves = FALSE;
gProtectStructs[battler].helpingHand = FALSE;
gProtectStructs[battler].bounceMove = FALSE;
gProtectStructs[battler].stealMove = FALSE;
gProtectStructs[battler].prlzImmobility = FALSE;
gProtectStructs[battler].confusionSelfDmg = FALSE;
gProtectStructs[battler].targetAffected = FALSE;
gProtectStructs[battler].chargingTurn = FALSE;
gProtectStructs[battler].fleeType = 0;
gProtectStructs[battler].usedImprisonedMove = FALSE;
gProtectStructs[battler].loveImmobility = FALSE;
gProtectStructs[battler].usedDisabledMove = FALSE;
gProtectStructs[battler].usedTauntedMove = FALSE;
gProtectStructs[battler].flag2Unknown = FALSE;
gProtectStructs[battler].flinchImmobility = FALSE;
gProtectStructs[battler].notFirstStrike = FALSE;
gProtectStructs[battler].usedHealBlockedMove = FALSE;
gProtectStructs[battler].usesBouncedMove = FALSE;
gProtectStructs[battler].usedGravityPreventedMove = FALSE;
gProtectStructs[battler].usedThroatChopPreventedMove = FALSE;
gProtectStructs[battler].statRaised = FALSE;
gProtectStructs[battler].statFell = FALSE;
gProtectStructs[battler].pranksterElevated = FALSE;
gDisableStructs[battler].isFirstTurn = 2;
gLastMoves[battler] = MOVE_NONE;
gLastLandedMoves[battler] = MOVE_NONE;
gLastHitByType[battler] = 0;
gLastResultingMoves[battler] = MOVE_NONE;
gLastPrintedMoves[battler] = MOVE_NONE;
gLastHitBy[battler] = 0xFF;
gBattleStruct->choicedMove[battler] = MOVE_NONE;
gBattleStruct->sameMoveTurns[battler] = 0;
gBattleStruct->lastTakenMove[battler] = MOVE_NONE;
gBattleStruct->lastTakenMoveFrom[battler][0] = 0;
gBattleStruct->lastTakenMoveFrom[battler][1] = 0;
gBattleStruct->lastTakenMoveFrom[battler][2] = 0;
gBattleStruct->lastTakenMoveFrom[battler][3] = 0;
gBattleStruct->palaceFlags &= ~(gBitTable[battler]);
2022-08-24 21:56:24 +02:00
for (i = 0; i < ARRAY_COUNT(gSideTimers); i++)
{
// User of sticky web fainted, so reset the stored battler ID
2023-08-29 16:20:16 +02:00
if (gSideTimers[i].stickyWebBattlerId == battler)
gSideTimers[i].stickyWebBattlerId = 0xFF;
}
2018-02-05 19:46:59 -06:00
for (i = 0; i < gBattlersCount; i++)
{
2023-08-29 16:20:16 +02:00
if (i != battler && GetBattlerSide(i) != GetBattlerSide(battler))
gBattleStruct->lastTakenMove[i] = MOVE_NONE;
2019-01-27 13:52:02 +01:00
2023-08-29 16:20:16 +02:00
gBattleStruct->lastTakenMoveFrom[i][battler] = 0;
2017-10-02 23:32:39 +02:00
}
2023-08-29 16:20:16 +02:00
gBattleResources->flags->flags[battler] = 0;
2017-10-02 23:32:39 +02:00
2023-08-29 16:20:16 +02:00
gBattleMons[battler].type1 = gSpeciesInfo[gBattleMons[battler].species].types[0];
gBattleMons[battler].type2 = gSpeciesInfo[gBattleMons[battler].species].types[1];
gBattleMons[battler].type3 = TYPE_MYSTERY;
2017-10-02 23:32:39 +02:00
2023-08-29 16:20:16 +02:00
Ai_UpdateFaintData(battler);
TryBattleFormChange(battler, FORM_CHANGE_FAINT);
2023-08-29 16:20:16 +02:00
gBattleStruct->overwrittenAbilities[battler] = ABILITY_NONE;
2022-10-27 20:44:43 +02:00
// If the fainted mon was involved in a Sky Drop
2023-08-29 16:20:16 +02:00
if (gBattleStruct->skyDropTargets[battler] != 0xFF)
{
// Get battler id of the other Pokemon involved in this Sky Drop
2023-08-29 16:20:16 +02:00
u8 otherSkyDropper = gBattleStruct->skyDropTargets[battler];
// Clear Sky Drop data
2023-08-29 16:20:16 +02:00
gBattleStruct->skyDropTargets[battler] = 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;
}
}
}
}
2022-10-27 20:44:43 +02:00
// Clear Z-Move data
gBattleStruct->zmove.active = FALSE;
2023-08-29 16:20:16 +02:00
gBattleStruct->zmove.toBeUsed[battler] = MOVE_NONE;
gBattleStruct->zmove.effect = EFFECT_HIT;
2017-10-02 23:32:39 +02:00
}
static void DoBattleIntro(void)
2017-10-02 23:32:39 +02:00
{
s32 i;
u32 battler;
u8 *state = &gBattleStruct->introState;
switch (*state)
2017-10-02 23:32:39 +02:00
{
case 0: // Get Data of all battlers.
battler = gBattleCommunication[1];
BtlController_EmitGetMonData(battler, BUFFER_A, REQUEST_ALL_BATTLE, 0);
MarkBattlerForControllerExec(battler);
(*state)++;
2017-10-02 23:32:39 +02:00
break;
case 1: // Loop through all battlers.
if (!gBattleControllerExecFlags)
2017-10-02 23:32:39 +02:00
{
if (++gBattleCommunication[1] == gBattlersCount)
(*state)++;
2017-10-02 23:32:39 +02:00
else
*state = 0;
2017-10-02 23:32:39 +02:00
}
break;
case 2: // Start graphical intro slide.
if (!gBattleControllerExecFlags)
2017-10-02 23:32:39 +02:00
{
battler = GetBattlerAtPosition(0);
BtlController_EmitIntroSlide(battler, BUFFER_A, gBattleTerrain);
MarkBattlerForControllerExec(battler);
gBattleCommunication[0] = 0;
gBattleCommunication[1] = 0;
(*state)++;
2017-10-02 23:32:39 +02:00
}
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 (battler = 0; battler < gBattlersCount; battler++)
2017-10-02 23:32:39 +02:00
{
if ((gBattleTypeFlags & BATTLE_TYPE_SAFARI) && GetBattlerSide(battler) == B_SIDE_PLAYER)
2017-10-02 23:32:39 +02:00
{
memset(&gBattleMons[battler], 0, sizeof(struct BattlePokemon));
2017-10-02 23:32:39 +02:00
}
else
2017-10-02 23:32:39 +02:00
{
memcpy(&gBattleMons[battler], &gBattleResources->bufferB[battler][4], sizeof(struct BattlePokemon));
gBattleMons[battler].type1 = gSpeciesInfo[gBattleMons[battler].species].types[0];
gBattleMons[battler].type2 = gSpeciesInfo[gBattleMons[battler].species].types[1];
gBattleMons[battler].type3 = TYPE_MYSTERY;
gBattleMons[battler].ability = GetAbilityBySpecies(gBattleMons[battler].species, gBattleMons[battler].abilityNum);
gBattleStruct->hpOnSwitchout[GetBattlerSide(battler)] = gBattleMons[battler].hp;
gBattleMons[battler].status2 = 0;
for (i = 0; i < NUM_BATTLE_STATS; i++)
gBattleMons[battler].statStages[i] = DEFAULT_STAT_STAGE;
2017-10-02 23:32:39 +02:00
}
// Draw sprite.
switch (GetBattlerPosition(battler))
2017-10-02 23:32:39 +02:00
{
case B_POSITION_PLAYER_LEFT: // player sprite
BtlController_EmitDrawTrainerPic(battler, BUFFER_A);
MarkBattlerForControllerExec(battler);
break;
case B_POSITION_OPPONENT_LEFT:
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) // opponent 1 sprite
2017-10-02 23:32:39 +02:00
{
BtlController_EmitDrawTrainerPic(battler, BUFFER_A);
MarkBattlerForControllerExec(battler);
2017-10-02 23:32:39 +02:00
}
else // wild mon 1
{
BtlController_EmitLoadMonSprite(battler, BUFFER_A);
MarkBattlerForControllerExec(battler);
gBattleResults.lastOpponentSpecies = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battler]], MON_DATA_SPECIES, NULL);
}
break;
case B_POSITION_PLAYER_RIGHT:
if (gBattleTypeFlags & (BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER)) // partner sprite
{
BtlController_EmitDrawTrainerPic(battler, BUFFER_A);
MarkBattlerForControllerExec(battler);
}
break;
case B_POSITION_OPPONENT_RIGHT:
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER)
{
2019-01-27 20:54:34 +01:00
if (gBattleTypeFlags & (BATTLE_TYPE_MULTI | BATTLE_TYPE_TWO_OPPONENTS) && !BATTLE_TWO_VS_ONE_OPPONENT) // opponent 2 if exists
{
BtlController_EmitDrawTrainerPic(battler, BUFFER_A);
MarkBattlerForControllerExec(battler);
}
}
else if (IsBattlerAlive(battler)) // wild mon 2 if alive
{
BtlController_EmitLoadMonSprite(battler, BUFFER_A);
MarkBattlerForControllerExec(battler);
gBattleResults.lastOpponentSpecies = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battler]], MON_DATA_SPECIES, NULL);
}
break;
2017-10-02 23:32:39 +02:00
}
if (gBattleTypeFlags & BATTLE_TYPE_ARENA)
BattleArena_InitPoints();
2017-10-02 23:32:39 +02:00
}
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER)
2017-10-02 23:32:39 +02:00
{
(*state)++;
2017-10-02 23:32:39 +02:00
}
else // Skip party summary since it is a wild battle.
2017-10-02 23:32:39 +02:00
{
#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
2017-10-02 23:32:39 +02:00
}
break;
case 5: // draw party summary in trainer battles
if (!gBattleControllerExecFlags)
2017-10-02 23:32:39 +02:00
{
struct HpAndStatus hpStatus[PARTY_SIZE];
for (i = 0; i < PARTY_SIZE; i++)
2017-10-02 23:32:39 +02:00
{
if (GetMonData(&gEnemyParty[i], MON_DATA_SPECIES_OR_EGG) == SPECIES_NONE
|| GetMonData(&gEnemyParty[i], MON_DATA_SPECIES_OR_EGG) == 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);
}
2017-10-02 23:32:39 +02:00
}
battler = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
BtlController_EmitDrawPartyStatusSummary(battler, BUFFER_A, hpStatus, PARTY_SUMM_SKIP_DRAW_DELAY);
MarkBattlerForControllerExec(battler);
for (i = 0; i < PARTY_SIZE; i++)
2017-10-02 23:32:39 +02:00
{
if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES_OR_EGG) == SPECIES_NONE
|| GetMonData(&gPlayerParty[i], MON_DATA_SPECIES_OR_EGG) == 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);
}
2017-10-02 23:32:39 +02:00
}
battler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
BtlController_EmitDrawPartyStatusSummary(battler, BUFFER_A, hpStatus, PARTY_SUMM_SKIP_DRAW_DELAY);
MarkBattlerForControllerExec(battler);
2017-10-02 23:32:39 +02:00
(*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)))
2017-10-02 23:32:39 +02:00
{
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)))
2017-10-02 23:32:39 +02:00
{
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER)
2017-10-02 23:32:39 +02:00
{
(*state)++;
2017-10-02 23:32:39 +02:00
}
else
{
#if B_FAST_INTRO == TRUE
*state = 15; // Wait for text to be printed.
#else
*state = 14; // Wait for text and sprite.
#endif
2017-10-02 23:32:39 +02:00
}
}
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));
2017-10-02 23:32:39 +02:00
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))
battler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
2017-10-02 23:32:39 +02:00
else
battler = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
2017-10-02 23:32:39 +02:00
BtlController_EmitIntroTrainerBallThrow(battler, BUFFER_A);
MarkBattlerForControllerExec(battler);
(*state)++;
break;
case 12: // nothing
(*state)++;
case 13: // second opponent's mon send out
2019-01-27 20:54:34 +01:00
if (gBattleTypeFlags & (BATTLE_TYPE_MULTI | BATTLE_TYPE_TWO_OPPONENTS) && !BATTLE_TWO_VS_ONE_OPPONENT)
2017-10-02 23:32:39 +02:00
{
if (gBattleTypeFlags & BATTLE_TYPE_RECORDED_LINK && !(gBattleTypeFlags & BATTLE_TYPE_RECORDED_IS_MASTER))
battler = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT);
else
battler = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT);
BtlController_EmitIntroTrainerBallThrow(battler, BUFFER_A);
MarkBattlerForControllerExec(battler);
2017-10-02 23:32:39 +02:00
}
#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.
2017-10-02 23:32:39 +02:00
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))
battler = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
else
battler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
2017-10-02 23:32:39 +02:00
// 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[battler ^ BIT_SIDE]].callback == SpriteCallbackDummy)
2017-10-02 23:32:39 +02:00
{
return;
}
#endif
2017-10-02 23:32:39 +02:00
PrepareStringBattle(STRINGID_INTROSENDOUT, battler);
2017-10-02 23:32:39 +02:00
}
(*state)++;
break;
case 17: // wait for player send out message
if (!(gBattleTypeFlags & BATTLE_TYPE_LINK && gBattleControllerExecFlags))
2017-10-02 23:32:39 +02:00
{
if (gBattleTypeFlags & BATTLE_TYPE_RECORDED_LINK && !(gBattleTypeFlags & BATTLE_TYPE_RECORDED_IS_MASTER))
battler = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
2017-10-02 23:32:39 +02:00
else
battler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
2017-10-02 23:32:39 +02:00
if (!IsBattlerMarkedForControllerExec(battler))
(*state)++;
2017-10-02 23:32:39 +02:00
}
break;
case 18: // player 1 send out
if (gBattleTypeFlags & BATTLE_TYPE_RECORDED_LINK && !(gBattleTypeFlags & BATTLE_TYPE_RECORDED_IS_MASTER))
battler = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
2017-10-02 23:32:39 +02:00
else
battler = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
2017-10-02 23:32:39 +02:00
BtlController_EmitIntroTrainerBallThrow(battler, BUFFER_A);
MarkBattlerForControllerExec(battler);
(*state)++;
break;
case 19: // player 2 send out
if (gBattleTypeFlags & (BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER))
2017-10-02 23:32:39 +02:00
{
if (gBattleTypeFlags & BATTLE_TYPE_RECORDED_LINK && !(gBattleTypeFlags & BATTLE_TYPE_RECORDED_IS_MASTER))
battler = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT);
else
battler = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT);
BtlController_EmitIntroTrainerBallThrow(battler, BUFFER_A);
MarkBattlerForControllerExec(battler);
2017-10-02 23:32:39 +02:00
}
(*state)++;
break;
case 20: // set dex and battle vars
if (!gBattleControllerExecFlags)
2017-10-02 23:32:39 +02:00
{
for (battler = 0; battler < gBattlersCount; battler++)
2017-10-02 23:32:39 +02:00
{
if (GetBattlerSide(battler) == 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[battler].species), FLAG_SET_SEEN, gBattleMons[battler].personality);
}
2017-10-02 23:32:39 +02:00
}
gBattleStruct->switchInAbilitiesCounter = 0;
gBattleStruct->switchInItemsCounter = 0;
gBattleStruct->overworldWeatherDone = FALSE;
2023-09-13 17:28:26 +02:00
SetAiLogicDataForTurn(AI_DATA); // get assumed abilities, hold effects, etc of all battlers
2022-08-23 01:07:25 +02:00
Ai_InitPartyStruct(); // Save mons party counts, and first 2/4 mons on the battlefield.
gBattleMainFunc = TryDoEventsBeforeFirstTurn;
}
break;
2017-10-02 23:32:39 +02:00
}
}
static void TryDoEventsBeforeFirstTurn(void)
{
2020-09-08 14:26:37 -04:00
s32 i, j;
2017-10-02 23:32:39 +02:00
2018-02-06 13:48:02 -06:00
if (gBattleControllerExecFlags)
2017-10-02 23:32:39 +02:00
return;
2021-06-08 16:40:47 +02:00
// Set invalid mons as absent(for example when starting a double battle with only one pokemon).
2021-10-01 05:25:25 -03:00
if (!(gBattleTypeFlags & BATTLE_TYPE_SAFARI))
2021-06-08 16:40:47 +02:00
{
2021-10-01 05:25:25 -03:00
for (i = 0; i < gBattlersCount; i++)
{
struct Pokemon *party = GetBattlerParty(i);
struct Pokemon *mon = &party[gBattlerPartyIndexes[i]];
if (gBattleMons[i].hp == 0 || gBattleMons[i].species == SPECIES_NONE || GetMonData(mon, MON_DATA_IS_EGG))
2021-10-01 05:25:25 -03:00
gAbsentBattlerFlags |= gBitTable[i];
}
2021-06-08 16:40:47 +02:00
}
// Allow for illegal abilities within tests.
if (gTestRunnerEnabled && gBattleStruct->switchInAbilitiesCounter == 0)
{
for (i = 0; i < gBattlersCount; ++i)
{
u32 side = GetBattlerSide(i);
u32 partyIndex = gBattlerPartyIndexes[i];
if (TestRunner_Battle_GetForcedAbility(side, partyIndex))
gBattleMons[i].ability = gBattleStruct->overwrittenAbilities[i] = TestRunner_Battle_GetForcedAbility(side, partyIndex);
}
}
2017-10-02 23:32:39 +02:00
if (gBattleStruct->switchInAbilitiesCounter == 0)
{
2018-02-05 19:46:59 -06:00
for (i = 0; i < gBattlersCount; i++)
2018-06-28 21:06:32 +02:00
gBattlerByTurnOrder[i] = i;
2018-02-05 19:46:59 -06:00
for (i = 0; i < gBattlersCount - 1; i++)
2017-10-02 23:32:39 +02:00
{
2018-02-05 19:46:59 -06:00
for (j = i + 1; j < gBattlersCount; j++)
2017-10-02 23:32:39 +02:00
{
if (GetWhichBattlerFaster(gBattlerByTurnOrder[i], gBattlerByTurnOrder[j], TRUE) != 0)
2017-10-04 19:25:14 +02:00
SwapTurnOrder(i, j);
2017-10-02 23:32:39 +02:00
}
}
}
if (!gBattleStruct->overworldWeatherDone
&& AbilityBattleEffects(ABILITYEFFECT_SWITCH_IN_WEATHER, 0, 0, ABILITYEFFECT_SWITCH_IN_WEATHER, 0) != 0)
2017-10-02 23:32:39 +02:00
{
gBattleStruct->overworldWeatherDone = TRUE;
return;
}
if (!gBattleStruct->terrainDone && AbilityBattleEffects(ABILITYEFFECT_SWITCH_IN_TERRAIN, 0, 0, ABILITYEFFECT_SWITCH_IN_TERRAIN, 0) != 0)
2020-12-13 19:42:48 -07:00
{
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
2021-10-29 22:32:19 -04:00
// Check neutralizing gas
if (AbilityBattleEffects(ABILITYEFFECT_NEUTRALIZINGGAS, 0, 0, 0, 0) != 0)
return;
2018-06-28 21:06:32 +02:00
// Check all switch in abilities happening from the fastest mon to slowest.
2018-02-05 19:46:59 -06:00
while (gBattleStruct->switchInAbilitiesCounter < gBattlersCount)
2017-10-02 23:32:39 +02:00
{
2019-08-05 11:24:49 +02:00
gBattlerAttacker = gBattlerByTurnOrder[gBattleStruct->switchInAbilitiesCounter++];
if (TryPrimalReversion(gBattlerAttacker))
return;
2019-08-05 11:24:49 +02:00
if (AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, gBattlerAttacker, 0, 0, 0) != 0)
2017-10-02 23:32:39 +02:00
return;
}
if (AbilityBattleEffects(ABILITYEFFECT_TRACE1, 0, 0, 0, 0) != 0)
2017-10-02 23:32:39 +02:00
return;
2018-06-28 21:06:32 +02:00
// Check all switch in items having effect from the fastest mon to slowest.
2018-02-05 19:46:59 -06:00
while (gBattleStruct->switchInItemsCounter < gBattlersCount)
2017-10-02 23:32:39 +02:00
{
2019-08-05 11:24:49 +02:00
if (ItemBattleEffects(ITEMEFFECT_ON_SWITCH_IN, gBattlerByTurnOrder[gBattleStruct->switchInItemsCounter++], FALSE))
2017-10-02 23:32:39 +02:00
return;
}
2018-02-05 19:46:59 -06:00
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
2017-10-02 23:32:39 +02:00
{
2018-06-28 21:06:32 +02:00
*(gBattleStruct->monToSwitchIntoId + i) = PARTY_SIZE;
2018-02-06 16:09:39 -06:00
gChosenActionByBattler[i] = B_ACTION_NONE;
gChosenMoveByBattler[i] = MOVE_NONE;
// Record party slots of player's mons that appeared in battle
2023-03-28 10:38:48 -04:00
if (!BattlerHasAi(i))
gBattleStruct->appearedInBattle |= gBitTable[gBattlerPartyIndexes[i]];
2017-10-02 23:32:39 +02:00
}
2017-10-06 00:12:01 +02:00
TurnValuesCleanUp(FALSE);
2017-10-02 23:32:39 +02:00
SpecialStatusesClear();
*(&gBattleStruct->absentBattlerFlags) = gAbsentBattlerFlags;
2021-10-08 16:50:52 -04:00
BattlePutTextOnWindow(gText_EmptyString3, B_WIN_MSG);
2017-10-04 19:25:14 +02:00
gBattleMainFunc = HandleTurnActionSelectionState;
2017-10-02 23:32:39 +02:00
ResetSentPokesToOpponentValue();
for (i = 0; i < BATTLE_COMMUNICATION_ENTRIES_COUNT; i++)
gBattleCommunication[i] = 0;
2018-02-05 19:46:59 -06:00
for (i = 0; i < gBattlersCount; i++)
2021-09-24 14:30:15 -04:00
gBattleMons[i].status2 &= ~STATUS2_FLINCHED;
2017-10-02 23:32:39 +02:00
*(&gBattleStruct->turnEffectsTracker) = 0;
2018-02-07 22:53:40 +01:00
*(&gBattleStruct->turnEffectsBattlerId) = 0;
2017-12-02 14:08:55 +01:00
*(&gBattleStruct->wishPerishSongState) = 0;
2018-02-07 22:53:40 +01:00
*(&gBattleStruct->wishPerishSongBattlerId) = 0;
2019-08-23 08:46:21 -04:00
gBattleScripting.moveendState = 0;
2017-12-02 14:08:55 +01:00
gBattleStruct->faintedActionsState = 0;
2018-02-07 22:53:40 +01:00
gBattleStruct->turnCountersTracker = 0;
2018-01-16 15:12:38 -06:00
gMoveResultFlags = 0;
2017-10-02 23:32:39 +02:00
gRandomTurnNumber = Random();
2022-08-24 21:56:24 +02:00
2023-09-13 17:28:26 +02:00
SetAiLogicDataForTurn(AI_DATA); // get assumed abilities, hold effects, etc of all battlers
2017-10-02 23:32:39 +02:00
if (gBattleTypeFlags & BATTLE_TYPE_ARENA)
{
StopCryAndClearCrySongs();
2018-08-25 21:04:12 +02:00
BattleScriptExecute(BattleScript_ArenaTurnBeginning);
2017-10-02 23:32:39 +02:00
}
if ((i = ShouldDoTrainerSlide(GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT), TRAINER_SLIDE_BEFORE_FIRST_TURN)))
BattleScriptExecute(i == 1 ? BattleScript_TrainerASlideMsgEnd2 : BattleScript_TrainerBSlideMsgEnd2);
2017-10-02 23:32:39 +02:00
}
2017-10-06 19:09:37 +02:00
static void HandleEndTurn_ContinueBattle(void)
2017-10-02 23:32:39 +02:00
{
s32 i;
2018-02-06 13:48:02 -06:00
if (gBattleControllerExecFlags == 0)
2017-10-02 23:32:39 +02:00
{
gBattleMainFunc = BattleTurnPassed;
for (i = 0; i < BATTLE_COMMUNICATION_ENTRIES_COUNT; i++)
gBattleCommunication[i] = 0;
2018-02-05 19:46:59 -06:00
for (i = 0; i < gBattlersCount; i++)
2017-10-02 23:32:39 +02:00
{
2021-09-24 14:30:15 -04:00
gBattleMons[i].status2 &= ~STATUS2_FLINCHED;
2018-01-16 15:12:38 -06:00
if ((gBattleMons[i].status1 & STATUS1_SLEEP) && (gBattleMons[i].status2 & STATUS2_MULTIPLETURNS))
2017-10-02 23:32:39 +02:00
CancelMultiTurnMoves(i);
}
gBattleStruct->turnEffectsTracker = 0;
2018-02-07 22:53:40 +01:00
gBattleStruct->turnEffectsBattlerId = 0;
2017-12-02 14:08:55 +01:00
gBattleStruct->wishPerishSongState = 0;
2018-02-07 22:53:40 +01:00
gBattleStruct->wishPerishSongBattlerId = 0;
gBattleStruct->turnCountersTracker = 0;
2018-01-16 15:12:38 -06:00
gMoveResultFlags = 0;
2017-10-02 23:32:39 +02:00
}
}
void BattleTurnPassed(void)
{
s32 i;
2017-10-06 00:12:01 +02:00
TurnValuesCleanUp(TRUE);
2017-10-02 23:32:39 +02:00
if (gBattleOutcome == 0)
{
2018-08-03 00:13:44 +02:00
if (DoFieldEndTurnEffects())
2017-10-02 23:32:39 +02:00
return;
2018-08-03 00:13:44 +02:00
if (DoBattlerEndTurnEffects())
2017-10-02 23:32:39 +02:00
return;
}
2018-06-28 21:06:32 +02:00
if (HandleFaintedMonActions())
2017-10-02 23:32:39 +02:00
return;
2017-12-02 14:08:55 +01:00
gBattleStruct->faintedActionsState = 0;
2018-06-28 21:06:32 +02:00
if (HandleWishPerishSongOnTurnEnd())
2017-10-02 23:32:39 +02:00
return;
2017-10-06 00:12:01 +02:00
TurnValuesCleanUp(FALSE);
2021-09-24 14:30:15 -04:00
gHitMarker &= ~HITMARKER_NO_ATTACKSTRING;
gHitMarker &= ~HITMARKER_UNABLE_TO_USE_MOVE;
gHitMarker &= ~HITMARKER_PLAYER_FAINTED;
gHitMarker &= ~HITMARKER_PASSIVE_DAMAGE;
2017-10-02 23:32:39 +02:00
gBattleScripting.animTurn = 0;
gBattleScripting.animTargetsHit = 0;
2019-08-23 08:46:21 -04:00
gBattleScripting.moveendState = 0;
2017-10-02 23:32:39 +02:00
gBattleMoveDamage = 0;
2018-01-16 15:12:38 -06:00
gMoveResultFlags = 0;
2017-10-02 23:32:39 +02:00
for (i = 0; i < 5; i++)
gBattleCommunication[i] = 0;
if (gBattleOutcome != 0)
{
2019-09-03 15:08:25 -04:00
gCurrentActionFuncId = B_ACTION_FINISHED;
2017-10-06 00:12:01 +02:00
gBattleMainFunc = RunTurnActionsFunctions;
2017-10-02 23:32:39 +02:00
return;
}
if (gBattleResults.battleTurnCounter < 0xFF)
2017-10-02 23:32:39 +02:00
{
gBattleResults.battleTurnCounter++;
2018-12-07 23:50:56 +01:00
gBattleStruct->arenaTurnCounter++;
2017-10-02 23:32:39 +02:00
}
2018-02-05 19:46:59 -06:00
for (i = 0; i < gBattlersCount; i++)
2017-10-02 23:32:39 +02:00
{
2018-02-06 16:09:39 -06:00
gChosenActionByBattler[i] = B_ACTION_NONE;
gChosenMoveByBattler[i] = MOVE_NONE;
2017-10-02 23:32:39 +02:00
}
2018-08-03 00:13:44 +02:00
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
2018-06-28 21:06:32 +02:00
*(gBattleStruct->monToSwitchIntoId + i) = PARTY_SIZE;
2017-10-02 23:32:39 +02:00
*(&gBattleStruct->absentBattlerFlags) = gAbsentBattlerFlags;
2021-10-08 16:50:52 -04:00
BattlePutTextOnWindow(gText_EmptyString3, B_WIN_MSG);
2023-09-13 17:28:26 +02:00
SetAiLogicDataForTurn(AI_DATA); // get assumed abilities, hold effects, etc of all battlers
2017-10-04 19:25:14 +02:00
gBattleMainFunc = HandleTurnActionSelectionState;
2017-10-02 23:32:39 +02:00
gRandomTurnNumber = Random();
if (gBattleTypeFlags & BATTLE_TYPE_PALACE)
2020-07-16 20:12:12 -04:00
BattleScriptExecute(BattleScript_PalacePrintFlavorText);
2018-12-07 23:50:56 +01:00
else if (gBattleTypeFlags & BATTLE_TYPE_ARENA && gBattleStruct->arenaTurnCounter == 0)
2018-08-25 21:04:12 +02:00
BattleScriptExecute(BattleScript_ArenaTurnBeginning);
else if ((i = ShouldDoTrainerSlide(GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT), TRAINER_SLIDE_LAST_LOW_HP)))
BattleScriptExecute(i == 1 ? BattleScript_TrainerASlideMsgEnd2 : BattleScript_TrainerBSlideMsgEnd2);
2023-02-27 07:45:01 -03:00
else if ((i = ShouldDoTrainerSlide(GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT), TRAINER_SLIDE_LAST_HALF_HP)))
BattleScriptExecute(i == 1 ? BattleScript_TrainerASlideMsgEnd2 : BattleScript_TrainerBSlideMsgEnd2);
2023-02-27 07:45:01 -03:00
else if ((i = ShouldDoTrainerSlide(GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT), TRAINER_SLIDE_FIRST_CRITICAL_HIT)))
BattleScriptExecute(i == 1 ? BattleScript_TrainerASlideMsgEnd2 : BattleScript_TrainerBSlideMsgEnd2);
2023-02-27 07:45:01 -03:00
else if ((i = ShouldDoTrainerSlide(GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT), TRAINER_SLIDE_FIRST_SUPER_EFFECTIVE_HIT)))
BattleScriptExecute(i == 1 ? BattleScript_TrainerASlideMsgEnd2 : BattleScript_TrainerBSlideMsgEnd2);
2023-02-27 07:45:01 -03:00
else if ((i = ShouldDoTrainerSlide(GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT), TRAINER_SLIDE_FIRST_STAB_MOVE)))
BattleScriptExecute(i == 1 ? BattleScript_TrainerASlideMsgEnd2 : BattleScript_TrainerBSlideMsgEnd2);
2023-02-27 07:45:01 -03:00
else if ((i = ShouldDoTrainerSlide(GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT), TRAINER_SLIDE_PLAYER_MON_UNAFFECTED)))
BattleScriptExecute(i == 1 ? BattleScript_TrainerASlideMsgEnd2 : BattleScript_TrainerBSlideMsgEnd2);
2017-10-02 23:32:39 +02:00
}
2023-08-29 16:20:16 +02:00
u8 IsRunningFromBattleImpossible(u32 battler)
2017-10-02 23:32:39 +02:00
{
u32 holdEffect, i;
2017-10-02 23:32:39 +02:00
2023-08-29 16:20:16 +02:00
if (gBattleMons[battler].item == ITEM_ENIGMA_BERRY_E_READER)
holdEffect = gEnigmaBerries[battler].holdEffect;
2017-10-02 23:32:39 +02:00
else
2023-08-29 16:20:16 +02:00
holdEffect = ItemId_GetHoldEffect(gBattleMons[battler].item);
2017-10-02 23:32:39 +02:00
2023-08-29 16:20:16 +02:00
gPotentialItemEffectBattler = battler;
2017-10-02 23:32:39 +02:00
if (gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE) // Cannot ever run from saving Birch's battle.
2017-10-02 23:32:39 +02:00
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_DONT_LEAVE_BIRCH;
return BATTLE_RUN_FORBIDDEN;
2017-10-02 23:32:39 +02:00
}
2023-08-29 16:20:16 +02:00
if (GetBattlerPosition(battler) == 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;
}
2017-10-02 23:32:39 +02:00
if (holdEffect == HOLD_EFFECT_CAN_ALWAYS_RUN)
return BATTLE_RUN_SUCCESS;
#if B_GHOSTS_ESCAPE >= GEN_6
2023-08-29 16:20:16 +02:00
if (IS_BATTLER_OF_TYPE(battler, TYPE_GHOST))
return BATTLE_RUN_SUCCESS;
#endif
2017-10-02 23:32:39 +02:00
if (gBattleTypeFlags & BATTLE_TYPE_LINK)
return BATTLE_RUN_SUCCESS;
2023-08-29 16:20:16 +02:00
if (GetBattlerAbility(battler) == ABILITY_RUN_AWAY)
return BATTLE_RUN_SUCCESS;
2017-10-02 23:32:39 +02:00
2023-08-29 16:20:16 +02:00
if ((i = IsAbilityPreventingEscape(battler)))
2017-10-02 23:32:39 +02:00
{
2018-02-05 19:46:59 -06:00
gBattleScripting.battler = i - 1;
2017-10-02 23:32:39 +02:00
gLastUsedAbility = gBattleMons[i - 1].ability;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PREVENTS_ESCAPE;
return BATTLE_RUN_FAILURE;
2017-10-02 23:32:39 +02:00
}
2023-08-29 16:20:16 +02:00
if (!CanBattlerEscape(battler))
2017-10-02 23:32:39 +02:00
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_CANT_ESCAPE;
return BATTLE_RUN_FORBIDDEN;
2017-10-02 23:32:39 +02:00
}
return BATTLE_RUN_SUCCESS;
2017-10-02 23:32:39 +02:00
}
2023-09-14 11:05:00 +02:00
void SwitchPartyOrder(u32 battler)
{
s32 i;
2023-09-14 11:05:00 +02:00
u32 partyId1, partyId2;
2019-10-17 19:22:03 -04:00
for (i = 0; i < (int)ARRAY_COUNT(gBattlePartyCurrentOrder); i++)
gBattlePartyCurrentOrder[i] = *(battler * 3 + i + (u8 *)(gBattleStruct->battlerPartyOrders));
2019-10-25 21:55:01 -04:00
partyId1 = GetPartyIdFromBattlePartyId(gBattlerPartyIndexes[battler]);
partyId2 = GetPartyIdFromBattlePartyId(*(gBattleStruct->monToSwitchIntoId + battler));
SwitchPartyMonSlots(partyId1, partyId2);
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
{
2019-10-17 19:22:03 -04:00
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
{
2019-10-17 19:22:03 -04:00
for (i = 0; i < (int)ARRAY_COUNT(gBattlePartyCurrentOrder); i++)
{
*(battler * 3 + i + (u8 *)(gBattleStruct->battlerPartyOrders)) = gBattlePartyCurrentOrder[i];
}
}
}
2017-10-06 17:06:45 +02:00
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
};
2017-10-04 19:25:14 +02:00
2017-10-06 19:09:37 +02:00
static void HandleTurnActionSelectionState(void)
{
s32 i, battler;
2017-10-04 19:25:14 +02:00
gBattleCommunication[ACTIONS_CONFIRMED_COUNT] = 0;
for (battler = 0; battler < gBattlersCount; battler++)
{
2023-09-14 00:07:41 +02:00
u32 position = GetBattlerPosition(battler);
switch (gBattleCommunication[battler])
{
2018-07-07 19:57:09 +02:00
case STATE_TURN_START_RECORD: // Recorded battle related action on start of every turn.
RecordedBattle_CopyBattlerMoves(battler);
gBattleCommunication[battler] = STATE_BEFORE_ACTION_CHOSEN;
2022-08-24 21:56:24 +02:00
// Do AI score computations here so we can use them in AI_TrySwitchOrUseItem
2022-10-27 20:44:43 +02:00
if ((gBattleTypeFlags & BATTLE_TYPE_HAS_AI || IsWildMonSmart())
&& (BattlerHasAi(battler) && !(gBattleTypeFlags & BATTLE_TYPE_PALACE)))
2023-03-28 10:58:44 -04:00
{
gBattleStruct->aiMoveOrAction[battler] = ComputeBattleAiScores(battler);
}
2023-09-14 00:07:41 +02:00
// fallthrough
2018-07-07 19:57:09 +02:00
case STATE_BEFORE_ACTION_CHOSEN: // Choose an action.
*(gBattleStruct->monToSwitchIntoId + battler) = PARTY_SIZE;
if (gBattleTypeFlags & BATTLE_TYPE_MULTI
2018-06-28 21:06:32 +02:00
|| (position & BIT_FLANK) == B_FLANK_LEFT
|| gBattleStruct->absentBattlerFlags & gBitTable[GetBattlerAtPosition(BATTLE_PARTNER(position))]
2018-06-28 21:06:32 +02:00
|| gBattleCommunication[GetBattlerAtPosition(BATTLE_PARTNER(position))] == STATE_WAIT_ACTION_CONFIRMED)
{
if (gBattleStruct->absentBattlerFlags & gBitTable[battler])
{
gChosenActionByBattler[battler] = B_ACTION_NOTHING_FAINTED;
if (!(gBattleTypeFlags & BATTLE_TYPE_MULTI))
gBattleCommunication[battler] = STATE_WAIT_ACTION_CONFIRMED;
else
gBattleCommunication[battler] = STATE_WAIT_ACTION_CONFIRMED_STANDBY;
}
else
{
if (gBattleMons[battler].status2 & STATUS2_MULTIPLETURNS
|| gBattleMons[battler].status2 & STATUS2_RECHARGE)
{
gChosenActionByBattler[battler] = B_ACTION_USE_MOVE;
gBattleCommunication[battler] = STATE_WAIT_ACTION_CONFIRMED_STANDBY;
}
2018-10-16 22:19:53 +02:00
else if (WILD_DOUBLE_BATTLE
&& position == B_POSITION_PLAYER_RIGHT
&& (gBattleStruct->throwingPokeBall || gChosenActionByBattler[GetBattlerAtPosition(B_POSITION_PLAYER_LEFT)] == B_ACTION_RUN)
&& gChosenActionByBattler[GetBattlerAtPosition(B_POSITION_PLAYER_LEFT)] != B_ACTION_NOTHING_FAINTED)
{
gBattleStruct->throwingPokeBall = FALSE;
gChosenActionByBattler[battler] = B_ACTION_NOTHING_FAINTED; // Not fainted, but it cannot move, because of the throwing ball.
gBattleCommunication[battler] = STATE_WAIT_ACTION_CONFIRMED_STANDBY;
}
else
{
gBattleStruct->itemPartyIndex[battler] = PARTY_SIZE;
BtlController_EmitChooseAction(battler, BUFFER_A, gChosenActionByBattler[0], gBattleResources->bufferB[0][1] | (gBattleResources->bufferB[0][2] << 8));
MarkBattlerForControllerExec(battler);
gBattleCommunication[battler]++;
}
}
}
break;
2018-07-07 19:57:09 +02:00
case STATE_WAIT_ACTION_CHOSEN: // Try to perform an action.
if (!(gBattleControllerExecFlags & ((gBitTable[battler]) | (0xF << 28) | (gBitTable[battler] << 4) | (gBitTable[battler] << 8) | (gBitTable[battler] << 12))))
{
RecordedBattle_SetBattlerAction(battler, gBattleResources->bufferB[battler][1]);
gChosenActionByBattler[battler] = gBattleResources->bufferB[battler][1];
switch (gBattleResources->bufferB[battler][1])
{
2018-02-06 16:09:39 -06:00
case B_ACTION_USE_MOVE:
if (AreAllMovesUnusable(battler))
{
gBattleCommunication[battler] = STATE_SELECTION_SCRIPT;
*(gBattleStruct->selectionScriptFinished + battler) = FALSE;
*(gBattleStruct->stateIdAfterSelScript + battler) = STATE_WAIT_ACTION_CONFIRMED_STANDBY;
*(gBattleStruct->moveTarget + battler) = gBattleResources->bufferB[battler][3];
return;
}
else if (gDisableStructs[battler].encoredMove != 0)
{
gChosenMoveByBattler[battler] = gDisableStructs[battler].encoredMove;
*(gBattleStruct->chosenMovePositions + battler) = gDisableStructs[battler].encoredMovePos;
gBattleCommunication[battler] = STATE_WAIT_ACTION_CONFIRMED_STANDBY;
return;
}
else
{
struct ChooseMoveStruct moveInfo;
2020-12-01 13:43:15 -05:00
moveInfo.zmove = gBattleStruct->zmove;
moveInfo.mega = gBattleStruct->mega;
moveInfo.species = gBattleMons[battler].species;
moveInfo.monType1 = gBattleMons[battler].type1;
moveInfo.monType2 = gBattleMons[battler].type2;
moveInfo.monType3 = gBattleMons[battler].type3;
2019-09-08 12:21:24 -04:00
for (i = 0; i < MAX_MON_MOVES; i++)
{
moveInfo.moves[i] = gBattleMons[battler].moves[i];
moveInfo.currentPp[i] = gBattleMons[battler].pp[i];
2017-10-08 14:54:51 +02:00
moveInfo.maxPp[i] = CalculatePPWithBonus(
gBattleMons[battler].moves[i],
gBattleMons[battler].ppBonuses,
i);
}
BtlController_EmitChooseMove(battler, BUFFER_A, (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) != 0, FALSE, &moveInfo);
MarkBattlerForControllerExec(battler);
}
break;
2018-02-06 16:09:39 -06:00
case B_ACTION_USE_ITEM:
if (FlagGet(B_FLAG_NO_BAG_USE))
{
RecordedBattle_ClearBattlerAction(battler, 1);
gSelectionBattleScripts[battler] = BattleScript_ActionSelectionItemsCantBeUsed;
gBattleCommunication[battler] = STATE_SELECTION_SCRIPT;
*(gBattleStruct->selectionScriptFinished + battler) = FALSE;
*(gBattleStruct->stateIdAfterSelScript + battler) = STATE_BEFORE_ACTION_CHOSEN;
return;
}
if (((gBattleTypeFlags & (BATTLE_TYPE_LINK
| BATTLE_TYPE_FRONTIER_NO_PYRAMID
| BATTLE_TYPE_EREADER_TRAINER
2021-01-13 15:17:32 -05:00
| BATTLE_TYPE_RECORDED_LINK))
&& !gTestRunnerEnabled)
// Or if currently held by Sky Drop
|| gStatuses3[battler] & STATUS3_SKY_DROPPED)
{
RecordedBattle_ClearBattlerAction(battler, 1);
gSelectionBattleScripts[battler] = BattleScript_ActionSelectionItemsCantBeUsed;
gBattleCommunication[battler] = STATE_SELECTION_SCRIPT;
*(gBattleStruct->selectionScriptFinished + battler) = FALSE;
*(gBattleStruct->stateIdAfterSelScript + battler) = STATE_BEFORE_ACTION_CHOSEN;
return;
}
else
{
BtlController_EmitChooseItem(battler, BUFFER_A, gBattleStruct->battlerPartyOrders[battler]);
MarkBattlerForControllerExec(battler);
}
break;
2018-02-06 16:09:39 -06:00
case B_ACTION_SWITCH:
*(gBattleStruct->battlerPartyIndexes + battler) = gBattlerPartyIndexes[battler];
2020-04-28 11:14:20 +02:00
if (gBattleTypeFlags & BATTLE_TYPE_ARENA
|| !CanBattlerEscape(battler))
{
BtlController_EmitChoosePokemon(battler, BUFFER_A, PARTY_ACTION_CANT_SWITCH, PARTY_SIZE, ABILITY_NONE, gBattleStruct->battlerPartyOrders[battler]);
}
else if (ItemId_GetHoldEffect(gBattleMons[battler].item) != HOLD_EFFECT_SHED_SHELL
&& (i = IsAbilityPreventingEscape(battler))) // must be last to keep i value integrity
{
BtlController_EmitChoosePokemon(battler, BUFFER_A, ((i - 1) << 4) | PARTY_ACTION_ABILITY_PREVENTS, PARTY_SIZE, gBattleMons[i - 1].ability, gBattleStruct->battlerPartyOrders[battler]);
}
else
{
if (battler == 2 && gChosenActionByBattler[0] == B_ACTION_SWITCH)
BtlController_EmitChoosePokemon(battler, BUFFER_A, PARTY_ACTION_CHOOSE_MON, *(gBattleStruct->monToSwitchIntoId + 0), ABILITY_NONE, gBattleStruct->battlerPartyOrders[battler]);
else if (battler == 3 && gChosenActionByBattler[1] == B_ACTION_SWITCH)
BtlController_EmitChoosePokemon(battler, BUFFER_A, PARTY_ACTION_CHOOSE_MON, *(gBattleStruct->monToSwitchIntoId + 1), ABILITY_NONE, gBattleStruct->battlerPartyOrders[battler]);
else
BtlController_EmitChoosePokemon(battler, BUFFER_A, PARTY_ACTION_CHOOSE_MON, PARTY_SIZE, ABILITY_NONE, gBattleStruct->battlerPartyOrders[battler]);
}
MarkBattlerForControllerExec(battler);
break;
2018-02-06 16:09:39 -06:00
case B_ACTION_SAFARI_BALL:
if (IsPlayerPartyAndPokemonStorageFull())
{
gSelectionBattleScripts[battler] = BattleScript_PrintFullBox;
gBattleCommunication[battler] = STATE_SELECTION_SCRIPT;
*(gBattleStruct->selectionScriptFinished + battler) = FALSE;
*(gBattleStruct->stateIdAfterSelScript + battler) = STATE_BEFORE_ACTION_CHOSEN;
return;
}
break;
2018-02-06 16:09:39 -06:00
case B_ACTION_SAFARI_POKEBLOCK:
BtlController_EmitChooseItem(battler, BUFFER_A, gBattleStruct->battlerPartyOrders[battler]);
MarkBattlerForControllerExec(battler);
break;
2018-02-06 16:09:39 -06:00
case B_ACTION_CANCEL_PARTNER:
gBattleCommunication[battler] = STATE_WAIT_SET_BEFORE_ACTION;
gBattleCommunication[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battler)))] = STATE_BEFORE_ACTION_CHOSEN;
RecordedBattle_ClearBattlerAction(battler, 1);
if (gBattleMons[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battler)))].status2 & STATUS2_MULTIPLETURNS
|| gBattleMons[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battler)))].status2 & STATUS2_RECHARGE)
{
BtlController_EmitEndBounceEffect(battler, BUFFER_A);
MarkBattlerForControllerExec(battler);
return;
}
else if (gChosenActionByBattler[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battler)))] == B_ACTION_SWITCH)
{
RecordedBattle_ClearBattlerAction(GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battler))), 2);
}
else if (gChosenActionByBattler[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battler)))] == B_ACTION_RUN)
{
RecordedBattle_ClearBattlerAction(GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battler))), 1);
}
else if (gChosenActionByBattler[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battler)))] == B_ACTION_USE_MOVE
&& (gProtectStructs[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battler)))].noValidMoves
|| gDisableStructs[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battler)))].encoredMove))
{
RecordedBattle_ClearBattlerAction(GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battler))), 1);
}
else if (gBattleTypeFlags & BATTLE_TYPE_PALACE
&& gChosenActionByBattler[GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battler)))] == B_ACTION_USE_MOVE)
{
gRngValue = gBattlePalaceMoveSelectionRngValue;
RecordedBattle_ClearBattlerAction(GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battler))), 1);
}
else
{
RecordedBattle_ClearBattlerAction(GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battler))), 3);
}
gBattleStruct->mega.toEvolve &= ~(gBitTable[BATTLE_PARTNER(GetBattlerPosition(battler))]);
2023-08-30 13:23:55 +02:00
gBattleStruct->burst.toBurst &= ~(gBitTable[BATTLE_PARTNER(GetBattlerPosition(battler))]);
gBattleStruct->zmove.toBeUsed[BATTLE_PARTNER(GetBattlerPosition(battler))] = MOVE_NONE;
BtlController_EmitEndBounceEffect(battler, BUFFER_A);
MarkBattlerForControllerExec(battler);
return;
2018-07-13 23:00:56 +02:00
case B_ACTION_DEBUG:
BtlController_EmitDebugMenu(battler, BUFFER_A);
MarkBattlerForControllerExec(battler);
2018-07-13 23:00:56 +02:00
break;
}
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER
2018-09-20 22:00:00 +02:00
&& gBattleTypeFlags & (BATTLE_TYPE_FRONTIER | BATTLE_TYPE_TRAINER_HILL)
&& gBattleResources->bufferB[battler][1] == B_ACTION_RUN)
{
gSelectionBattleScripts[battler] = BattleScript_AskIfWantsToForfeitMatch;
gBattleCommunication[battler] = STATE_SELECTION_SCRIPT_MAY_RUN;
*(gBattleStruct->selectionScriptFinished + battler) = FALSE;
*(gBattleStruct->stateIdAfterSelScript + battler) = STATE_BEFORE_ACTION_CHOSEN;
return;
}
else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER
2021-01-13 15:17:32 -05:00
&& !(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK))
&& gBattleResources->bufferB[battler][1] == B_ACTION_RUN)
{
2017-11-26 17:26:11 +01:00
BattleScriptExecute(BattleScript_PrintCantRunFromTrainer);
gBattleCommunication[battler] = STATE_BEFORE_ACTION_CHOSEN;
}
else if (IsRunningFromBattleImpossible(battler) != BATTLE_RUN_SUCCESS
&& gBattleResources->bufferB[battler][1] == B_ACTION_RUN)
{
gSelectionBattleScripts[battler] = BattleScript_PrintCantEscapeFromBattle;
gBattleCommunication[battler] = STATE_SELECTION_SCRIPT;
*(gBattleStruct->selectionScriptFinished + battler) = FALSE;
*(gBattleStruct->stateIdAfterSelScript + battler) = STATE_BEFORE_ACTION_CHOSEN;
return;
}
else
{
gBattleCommunication[battler]++;
}
}
break;
2017-10-04 19:25:14 +02:00
case STATE_WAIT_ACTION_CASE_CHOSEN:
if (!(gBattleControllerExecFlags & ((gBitTable[battler]) | (0xF << 28) | (gBitTable[battler] << 4) | (gBitTable[battler] << 8) | (gBitTable[battler] << 12))))
{
switch (gChosenActionByBattler[battler])
{
2018-02-06 16:09:39 -06:00
case B_ACTION_USE_MOVE:
switch (gBattleResources->bufferB[battler][1])
{
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
gChosenActionByBattler[battler] = gBattleResources->bufferB[battler][1];
return;
case 15:
gChosenActionByBattler[battler] = B_ACTION_SWITCH;
UpdateBattlerPartyOrdersOnSwitch(battler);
return;
default:
RecordedBattle_CheckMovesetChanges(B_RECORD_MODE_PLAYBACK);
if ((gBattleResources->bufferB[battler][2] | (gBattleResources->bufferB[battler][3] << 8)) == 0xFFFF)
{
gBattleCommunication[battler] = STATE_BEFORE_ACTION_CHOSEN;
RecordedBattle_ClearBattlerAction(battler, 1);
}
else if (TrySetCantSelectMoveBattleScript(battler))
{
RecordedBattle_ClearBattlerAction(battler, 1);
gBattleCommunication[battler] = STATE_SELECTION_SCRIPT;
*(gBattleStruct->selectionScriptFinished + battler) = FALSE;
gBattleResources->bufferB[battler][1] = B_ACTION_USE_MOVE;
*(gBattleStruct->stateIdAfterSelScript + battler) = STATE_WAIT_ACTION_CHOSEN;
return;
}
else
{
if (!(gBattleTypeFlags & BATTLE_TYPE_PALACE))
{
RecordedBattle_SetBattlerAction(battler, gBattleResources->bufferB[battler][2]);
RecordedBattle_SetBattlerAction(battler, gBattleResources->bufferB[battler][3]);
}
2023-08-30 13:23:55 +02:00
gBattleStruct->chosenMovePositions[battler] = gBattleResources->bufferB[battler][2] & ~(RET_MEGA_EVOLUTION | RET_ULTRA_BURST);
gChosenMoveByBattler[battler] = gBattleMons[battler].moves[gBattleStruct->chosenMovePositions[battler]];
gBattleStruct->moveTarget[battler] = gBattleResources->bufferB[battler][3];
if (gBattleResources->bufferB[battler][2] & RET_MEGA_EVOLUTION)
gBattleStruct->mega.toEvolve |= gBitTable[battler];
2023-08-30 13:23:55 +02:00
else if (gBattleResources->bufferB[battler][2] & RET_ULTRA_BURST)
gBattleStruct->burst.toBurst |= gBitTable[battler];
gBattleCommunication[battler]++;
}
break;
}
break;
2018-02-06 16:09:39 -06:00
case B_ACTION_USE_ITEM:
if ((gBattleResources->bufferB[battler][1] | (gBattleResources->bufferB[battler][2] << 8)) == 0)
{
gBattleCommunication[battler] = STATE_BEFORE_ACTION_CHOSEN;
}
else
{
gLastUsedItem = (gBattleResources->bufferB[battler][1] | (gBattleResources->bufferB[battler][2] << 8));
if (ItemId_GetPocket(gLastUsedItem) == POCKET_POKE_BALLS)
gBattleStruct->throwingPokeBall = TRUE;
gBattleCommunication[battler]++;
}
break;
2018-02-06 16:09:39 -06:00
case B_ACTION_SWITCH:
if (gBattleResources->bufferB[battler][1] == PARTY_SIZE)
{
gBattleCommunication[battler] = STATE_BEFORE_ACTION_CHOSEN;
RecordedBattle_ClearBattlerAction(battler, 1);
}
else
{
UpdateBattlerPartyOrdersOnSwitch(battler);
gBattleCommunication[battler]++;
}
break;
2018-02-06 16:09:39 -06:00
case B_ACTION_RUN:
2017-10-06 17:06:45 +02:00
gHitMarker |= HITMARKER_RUN;
gBattleCommunication[battler]++;
break;
2018-02-06 16:09:39 -06:00
case B_ACTION_SAFARI_WATCH_CAREFULLY:
gBattleCommunication[battler]++;
break;
2018-02-06 16:09:39 -06:00
case B_ACTION_SAFARI_BALL:
gBattleCommunication[battler]++;
break;
2021-06-25 13:37:59 -06:00
case B_ACTION_THROW_BALL:
2021-08-31 12:51:03 -04:00
gBattleStruct->throwingPokeBall = TRUE;
gBattleCommunication[battler]++;
2021-06-25 13:37:59 -06:00
break;
2018-02-06 16:09:39 -06:00
case B_ACTION_SAFARI_POKEBLOCK:
if ((gBattleResources->bufferB[battler][1] | (gBattleResources->bufferB[battler][2] << 8)) != 0)
gBattleCommunication[battler]++;
else
gBattleCommunication[battler] = STATE_BEFORE_ACTION_CHOSEN;
break;
2018-02-06 16:09:39 -06:00
case B_ACTION_SAFARI_GO_NEAR:
gBattleCommunication[battler]++;
break;
2018-02-06 16:09:39 -06:00
case B_ACTION_SAFARI_RUN:
2017-10-06 17:06:45 +02:00
gHitMarker |= HITMARKER_RUN;
gBattleCommunication[battler]++;
break;
2018-06-20 23:07:51 +02:00
case B_ACTION_WALLY_THROW:
gBattleCommunication[battler]++;
break;
2018-07-13 23:00:56 +02:00
case B_ACTION_DEBUG:
gBattleCommunication[battler] = STATE_BEFORE_ACTION_CHOSEN;
2018-07-13 23:00:56 +02:00
break;
}
}
break;
2017-10-04 19:25:14 +02:00
case STATE_WAIT_ACTION_CONFIRMED_STANDBY:
if (!(gBattleControllerExecFlags & ((gBitTable[battler])
2021-03-30 17:38:09 -04:00
| (0xF << 28)
| (gBitTable[battler] << 4)
| (gBitTable[battler] << 8)
| (gBitTable[battler] << 12))))
{
if (AllAtActionConfirmed())
i = TRUE;
else
i = FALSE;
if (((gBattleTypeFlags & BATTLE_TYPE_MULTI) || !(gBattleTypeFlags & BATTLE_TYPE_DOUBLE))
|| (position & BIT_FLANK) != B_FLANK_LEFT
2022-08-27 01:26:13 -04:00
|| (*(&gBattleStruct->absentBattlerFlags) & gBitTable[GetBattlerAtPosition(BATTLE_PARTNER(position))]))
{
BtlController_EmitLinkStandbyMsg(battler, BUFFER_A, LINK_STANDBY_MSG_STOP_BOUNCE, i);
}
else
{
BtlController_EmitLinkStandbyMsg(battler, BUFFER_A, LINK_STANDBY_STOP_BOUNCE_ONLY, i);
}
MarkBattlerForControllerExec(battler);
gBattleCommunication[battler]++;
}
break;
2017-10-04 19:25:14 +02:00
case STATE_WAIT_ACTION_CONFIRMED:
if (!(gBattleControllerExecFlags & ((gBitTable[battler]) | (0xF << 28) | (gBitTable[battler] << 4) | (gBitTable[battler] << 8) | (gBitTable[battler] << 12))))
{
2017-10-04 19:25:14 +02:00
gBattleCommunication[ACTIONS_CONFIRMED_COUNT]++;
}
break;
2017-10-04 19:25:14 +02:00
case STATE_SELECTION_SCRIPT:
if (*(gBattleStruct->selectionScriptFinished + battler))
{
gBattleCommunication[battler] = *(gBattleStruct->stateIdAfterSelScript + battler);
}
else
{
gBattlerAttacker = battler;
gBattlescriptCurrInstr = gSelectionBattleScripts[battler];
if (!(gBattleControllerExecFlags & ((gBitTable[battler]) | (0xF << 28) | (gBitTable[battler] << 4) | (gBitTable[battler] << 8) | (gBitTable[battler] << 12))))
{
gBattleScriptingCommandsTable[gBattlescriptCurrInstr[0]]();
}
gSelectionBattleScripts[battler] = gBattlescriptCurrInstr;
}
break;
2017-10-04 19:25:14 +02:00
case STATE_WAIT_SET_BEFORE_ACTION:
if (!(gBattleControllerExecFlags & ((gBitTable[battler]) | (0xF << 28) | (gBitTable[battler] << 4) | (gBitTable[battler] << 8) | (gBitTable[battler] << 12))))
{
gBattleCommunication[battler] = STATE_BEFORE_ACTION_CHOSEN;
}
break;
2017-10-04 19:25:14 +02:00
case STATE_SELECTION_SCRIPT_MAY_RUN:
if (*(gBattleStruct->selectionScriptFinished + battler))
{
if (gBattleResources->bufferB[battler][1] == B_ACTION_NOTHING_FAINTED)
{
2017-10-06 17:06:45 +02:00
gHitMarker |= HITMARKER_RUN;
gChosenActionByBattler[battler] = B_ACTION_RUN;
gBattleCommunication[battler] = STATE_WAIT_ACTION_CONFIRMED_STANDBY;
}
else
{
RecordedBattle_ClearBattlerAction(battler, 1);
gBattleCommunication[battler] = *(gBattleStruct->stateIdAfterSelScript + battler);
}
}
else
{
gBattlerAttacker = battler;
gBattlescriptCurrInstr = gSelectionBattleScripts[battler];
if (!(gBattleControllerExecFlags & ((gBitTable[battler]) | (0xF << 28) | (gBitTable[battler] << 4) | (gBitTable[battler] << 8) | (gBitTable[battler] << 12))))
{
gBattleScriptingCommandsTable[gBattlescriptCurrInstr[0]]();
}
gSelectionBattleScripts[battler] = gBattlescriptCurrInstr;
}
break;
}
}
// Check if everyone chose actions.
2018-02-05 19:46:59 -06:00
if (gBattleCommunication[ACTIONS_CONFIRMED_COUNT] == gBattlersCount)
{
RecordedBattle_CheckMovesetChanges(B_RECORD_MODE_RECORDING);
if (WILD_DOUBLE_BATTLE
&& gBattleStruct->throwingPokeBall
&& gChosenActionByBattler[GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT)] != B_ACTION_NOTHING_FAINTED)
{
2021-08-31 12:51:03 -04:00
// 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)
// if throwing a ball in a wild battle with an in-game partner, skip partner's turn when throwing a ball
if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER)
gChosenActionByBattler[GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT)] = B_ACTION_NOTHING_FAINTED;
else
gChosenActionByBattler[GetBattlerAtPosition(B_POSITION_PLAYER_LEFT)] = B_ACTION_NOTHING_FAINTED;
2021-08-31 12:51:03 -04:00
}
2018-02-08 11:17:41 +01:00
gBattleMainFunc = SetActionsAndBattlersTurnOrder;
if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER)
{
2018-02-05 19:46:59 -06:00
for (i = 0; i < gBattlersCount; i++)
{
2018-02-06 16:09:39 -06:00
if (gChosenActionByBattler[i] == B_ACTION_SWITCH)
2019-10-25 21:55:01 -04:00
SwitchPartyOrderInGameMulti(i, *(gBattleStruct->monToSwitchIntoId + i));
}
}
}
}
2017-10-04 19:25:14 +02:00
static bool8 AllAtActionConfirmed(void)
2017-10-04 19:25:14 +02:00
{
s32 i, count;
2017-10-04 19:25:14 +02:00
for (count = 0, i = 0; i < gBattlersCount; i++)
2017-10-04 19:25:14 +02:00
{
if (gBattleCommunication[i] == STATE_WAIT_ACTION_CONFIRMED)
count++;
2017-10-04 19:25:14 +02:00
}
if (count + 1 == gBattlersCount)
2017-10-04 19:25:14 +02:00
return TRUE;
else
return FALSE;
}
2023-08-29 16:20:16 +02:00
static void UpdateBattlerPartyOrdersOnSwitch(u32 battler)
2017-10-04 19:25:14 +02:00
{
2023-08-29 16:20:16 +02:00
gBattleStruct->monToSwitchIntoId[battler] = gBattleResources->bufferB[battler][1];
RecordedBattle_SetBattlerAction(battler, gBattleResources->bufferB[battler][1]);
2017-10-04 19:25:14 +02:00
if (gBattleTypeFlags & BATTLE_TYPE_LINK && gBattleTypeFlags & BATTLE_TYPE_MULTI)
{
2023-08-29 16:20:16 +02:00
*(battler * 3 + (u8 *)(gBattleStruct->battlerPartyOrders) + 0) &= 0xF;
*(battler * 3 + (u8 *)(gBattleStruct->battlerPartyOrders) + 0) |= (gBattleResources->bufferB[battler][2] & 0xF0);
*(battler * 3 + (u8 *)(gBattleStruct->battlerPartyOrders) + 1) = gBattleResources->bufferB[battler][3];
2017-10-04 19:25:14 +02:00
2023-08-29 16:20:16 +02:00
*((BATTLE_PARTNER(battler)) * 3 + (u8 *)(gBattleStruct->battlerPartyOrders) + 0) &= (0xF0);
*((BATTLE_PARTNER(battler)) * 3 + (u8 *)(gBattleStruct->battlerPartyOrders) + 0) |= (gBattleResources->bufferB[battler][2] & 0xF0) >> 4;
*((BATTLE_PARTNER(battler)) * 3 + (u8 *)(gBattleStruct->battlerPartyOrders) + 2) = gBattleResources->bufferB[battler][3];
2017-10-04 19:25:14 +02:00
}
}
void SwapTurnOrder(u8 id1, u8 id2)
{
u32 temp;
SWAP(gActionsByTurnOrder[id1], gActionsByTurnOrder[id2], temp);
SWAP(gBattlerByTurnOrder[id1], gBattlerByTurnOrder[id2], temp);
2017-10-04 19:25:14 +02:00
}
// For AI, so it doesn't 'cheat' by knowing player's ability
u32 GetBattlerTotalSpeedStatArgs(u32 battler, u32 ability, u32 holdEffect)
2017-10-04 19:25:14 +02:00
{
2023-08-30 10:18:31 +02:00
u32 speed = gBattleMons[battler].speed;
u32 highestStat = GetHighestStatId(battler);
2017-10-04 19:25:14 +02:00
2018-07-14 22:56:03 +02:00
// weather abilities
2017-10-04 19:25:14 +02:00
if (WEATHER_HAS_EFFECT)
{
if (ability == ABILITY_SWIFT_SWIM && holdEffect != HOLD_EFFECT_UTILITY_UMBRELLA && gBattleWeather & B_WEATHER_RAIN)
2018-07-14 22:56:03 +02:00
speed *= 2;
else if (ability == ABILITY_CHLOROPHYLL && holdEffect != HOLD_EFFECT_UTILITY_UMBRELLA && gBattleWeather & B_WEATHER_SUN)
2018-07-14 22:56:03 +02:00
speed *= 2;
else if (ability == ABILITY_SAND_RUSH && gBattleWeather & B_WEATHER_SANDSTORM)
2018-07-14 22:56:03 +02:00
speed *= 2;
2023-04-29 12:30:39 +02:00
else if (ability == ABILITY_SLUSH_RUSH && (gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW)))
2018-07-14 22:56:03 +02:00
speed *= 2;
2017-10-04 19:25:14 +02:00
}
2018-07-14 22:56:03 +02:00
// other abilities
2023-08-30 10:18:31 +02:00
if (ability == ABILITY_QUICK_FEET && gBattleMons[battler].status1 & STATUS1_ANY)
2018-07-14 22:56:03 +02:00
speed = (speed * 150) / 100;
else if (ability == ABILITY_SURGE_SURFER && gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN)
speed *= 2;
2023-08-30 10:18:31 +02:00
else if (ability == ABILITY_SLOW_START && gDisableStructs[battler].slowStartTimer != 0)
2020-04-18 12:52:15 +02:00
speed /= 2;
else if (ability == ABILITY_PROTOSYNTHESIS && gBattleWeather & B_WEATHER_SUN && highestStat == STAT_SPEED)
speed = (speed * 150) / 100;
else if (ability == ABILITY_QUARK_DRIVE && gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN && highestStat == STAT_SPEED)
speed = (speed * 150) / 100;
2017-10-04 19:25:14 +02:00
2018-07-14 22:56:03 +02:00
// stat stages
2023-08-30 10:18:31 +02:00
speed *= gStatStageRatios[gBattleMons[battler].statStages[STAT_SPEED]][0];
speed /= gStatStageRatios[gBattleMons[battler].statStages[STAT_SPEED]][1];
2017-10-04 19:25:14 +02:00
2018-07-14 22:56:03 +02:00
// player's badge boost
if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK | BATTLE_TYPE_FRONTIER))
2023-08-30 10:18:31 +02:00
&& ShouldGetStatBadgeBoost(FLAG_BADGE03_GET, battler)
&& GetBattlerSide(battler) == B_SIDE_PLAYER)
2017-10-04 19:25:14 +02:00
{
2018-07-14 22:56:03 +02:00
speed = (speed * 110) / 100;
2017-10-04 19:25:14 +02:00
}
2018-07-14 22:56:03 +02:00
// item effects
if (holdEffect == HOLD_EFFECT_MACHO_BRACE || holdEffect == HOLD_EFFECT_POWER_ITEM)
2018-07-14 22:56:03 +02:00
speed /= 2;
else if (holdEffect == HOLD_EFFECT_IRON_BALL)
speed /= 2;
else if (holdEffect == HOLD_EFFECT_CHOICE_SCARF)
speed = (speed * 150) / 100;
2023-08-30 10:18:31 +02:00
else if (holdEffect == HOLD_EFFECT_QUICK_POWDER && gBattleMons[battler].species == SPECIES_DITTO && !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED))
2018-07-14 22:56:03 +02:00
speed *= 2;
2017-10-04 19:25:14 +02:00
2018-07-14 22:56:03 +02:00
// various effects
if (gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_TAILWIND)
2018-07-14 22:56:03 +02:00
speed *= 2;
2023-08-30 10:18:31 +02:00
if (gBattleResources->flags->flags[battler] & RESOURCE_FLAG_UNBURDEN)
2018-07-14 22:56:03 +02:00
speed *= 2;
2017-10-04 19:25:14 +02:00
2018-07-14 22:56:03 +02:00
// paralysis drop
2023-08-30 10:18:31 +02:00
if (gBattleMons[battler].status1 & STATUS1_PARALYSIS && ability != ABILITY_QUICK_FEET)
#if B_PARALYSIS_SPEED >= GEN_7
speed /= 2;
#else
speed /= 4;
#endif
2017-10-04 19:25:14 +02:00
2018-07-14 22:56:03 +02:00
return speed;
}
2017-10-04 19:25:14 +02:00
u32 GetBattlerTotalSpeedStat(u32 battler)
{
u32 ability = GetBattlerAbility(battler);
u32 holdEffect = GetBattlerHoldEffect(battler, TRUE);
return GetBattlerTotalSpeedStatArgs(battler, ability, holdEffect);
}
2023-08-30 10:18:31 +02:00
s8 GetChosenMovePriority(u32 battler)
2018-07-28 00:25:02 +02:00
{
u16 move;
2017-10-04 19:25:14 +02:00
2023-08-30 10:18:31 +02:00
gProtectStructs[battler].pranksterElevated = 0;
if (gProtectStructs[battler].noValidMoves)
2018-07-28 00:25:02 +02:00
move = MOVE_STRUGGLE;
else
2023-08-30 10:18:31 +02:00
move = gBattleMons[battler].moves[*(gBattleStruct->chosenMovePositions + battler)];
2017-10-04 19:25:14 +02:00
2023-08-30 10:18:31 +02:00
return GetMovePriority(battler, move);
}
2017-10-04 19:25:14 +02:00
2023-08-30 10:18:31 +02:00
s8 GetMovePriority(u32 battler, u16 move)
{
s8 priority;
2023-08-30 10:18:31 +02:00
u16 ability = GetBattlerAbility(battler);
2017-10-04 19:25:14 +02:00
2018-12-22 15:10:24 +01:00
priority = gBattleMoves[move].priority;
if (ability == ABILITY_GALE_WINGS
#if B_GALE_WINGS >= GEN_7
2023-08-30 10:18:31 +02:00
&& BATTLER_MAX_HP(battler)
#endif
&& gBattleMoves[move].type == TYPE_FLYING)
2017-10-04 19:25:14 +02:00
{
2018-12-22 15:10:24 +01:00
priority++;
2017-10-04 19:25:14 +02:00
}
else if (ability == ABILITY_PRANKSTER && IS_MOVE_STATUS(move))
2018-07-28 00:25:02 +02:00
{
2023-08-30 10:18:31 +02:00
gProtectStructs[battler].pranksterElevated = 1;
2018-12-22 15:10:24 +01:00
priority++;
2018-07-28 00:25:02 +02:00
}
2023-08-30 10:18:31 +02:00
else if (gBattleMoves[move].effect == EFFECT_GRASSY_GLIDE && gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && IsBattlerGrounded(battler))
2017-10-04 19:25:14 +02:00
{
2018-12-22 15:10:24 +01:00
priority++;
2017-10-04 19:25:14 +02:00
}
else if (ability == ABILITY_TRIAGE)
2017-10-04 19:25:14 +02:00
{
2018-11-30 21:42:30 +01:00
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:
case EFFECT_JUNGLE_HEALING:
2018-12-22 15:10:24 +01:00
priority += 3;
2018-11-30 21:42:30 +01:00
break;
}
2017-10-04 19:25:14 +02:00
}
2023-08-30 10:18:31 +02:00
if (gProtectStructs[battler].quash)
2022-08-29 10:26:55 +02:00
priority = -8;
2017-10-04 19:25:14 +02:00
2018-12-22 15:10:24 +01:00
return priority;
2018-07-28 00:25:02 +02:00
}
2017-10-04 19:25:14 +02:00
// Function for AI with variables provided as arguments to speed the computation time
u32 GetWhichBattlerFasterArgs(u32 battler1, u32 battler2, bool32 ignoreChosenMoves, u32 ability1, u32 ability2,
u32 holdEffectBattler1, u32 holdEffectBattler2, u32 speedBattler1, u32 speedBattler2, s32 priority1, s32 priority2)
2018-07-14 22:56:03 +02:00
{
u32 strikesFirst = 0;
2017-10-04 19:25:14 +02:00
2021-10-12 21:49:18 -04:00
// Battler 1
// Quick Draw
if (!ignoreChosenMoves && ability1 == ABILITY_QUICK_DRAW && !IS_MOVE_STATUS(gChosenMoveByBattler[battler1]) && RandomPercentage(RNG_QUICK_DRAW, 30))
2021-10-12 21:49:18 -04:00
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;
2017-10-04 19:25:14 +02:00
2021-10-12 21:49:18 -04:00
// Battler 2
// Quick Draw
if (!ignoreChosenMoves && ability2 == ABILITY_QUICK_DRAW && !IS_MOVE_STATUS(gChosenMoveByBattler[battler2]) && RandomPercentage(RNG_QUICK_DRAW, 30))
2021-10-12 21:49:18 -04:00
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;
2017-10-04 19:25:14 +02:00
2018-07-28 00:25:02 +02:00
if (priority1 == priority2)
2017-10-04 19:25:14 +02:00
{
// QUICK CLAW / CUSTAP - always first
2018-07-22 19:02:41 +02:00
// LAGGING TAIL - always last
// STALL - always last
2022-08-24 21:56:24 +02:00
2021-10-12 21:49:18 -04:00
if (gProtectStructs[battler1].quickDraw && !gProtectStructs[battler2].quickDraw)
2018-07-22 19:02:41 +02:00
strikesFirst = 0;
2021-10-12 21:49:18 -04:00
else if (!gProtectStructs[battler1].quickDraw && gProtectStructs[battler2].quickDraw)
strikesFirst = 1;
else if (gProtectStructs[battler1].usedCustapBerry && !gProtectStructs[battler2].usedCustapBerry)
2018-07-22 19:02:41 +02:00
strikesFirst = 0;
else if (gProtectStructs[battler2].usedCustapBerry && !gProtectStructs[battler1].usedCustapBerry)
2018-07-22 19:02:41 +02:00
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)
2018-07-22 19:02:41 +02:00
strikesFirst = 1;
else if (ability2 == ABILITY_STALL && ability1 != ABILITY_STALL)
2018-07-22 19:02:41 +02:00
strikesFirst = 0;
2022-12-06 06:18:18 -03:00
else if (ability1 == ABILITY_MYCELIUM_MIGHT && ability2 != ABILITY_MYCELIUM_MIGHT && IS_MOVE_STATUS(gCurrentMove))
strikesFirst = 1;
else if (ability2 == ABILITY_MYCELIUM_MIGHT && ability1 != ABILITY_MYCELIUM_MIGHT && IS_MOVE_STATUS(gCurrentMove))
strikesFirst = 0;
2017-10-04 19:25:14 +02:00
else
{
2018-02-08 11:17:41 +01:00
if (speedBattler1 == speedBattler2 && Random() & 1)
2018-07-22 19:02:41 +02:00
{
2017-10-04 19:25:14 +02:00
strikesFirst = 2; // same speeds, same priorities
2018-07-22 19:02:41 +02:00
}
2018-02-08 11:17:41 +01:00
else if (speedBattler1 < speedBattler2)
2018-07-22 19:02:41 +02:00
{
// 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;
}
2017-10-04 19:25:14 +02:00
}
}
2018-07-28 00:25:02 +02:00
else if (priority1 < priority2)
2018-07-22 19:02:41 +02:00
{
strikesFirst = 1; // battler2's move has greater priority
}
2017-10-04 19:25:14 +02:00
else
{
2018-07-22 19:02:41 +02:00
strikesFirst = 0; // battler1's move has greater priority
2017-10-04 19:25:14 +02:00
}
return strikesFirst;
}
u32 GetWhichBattlerFaster(u32 battler1, u32 battler2, bool32 ignoreChosenMoves)
{
s32 priority1 = 0, priority2 = 0;
u32 ability1 = GetBattlerAbility(battler1);
u32 speedBattler1 = GetBattlerTotalSpeedStat(battler1);
u32 holdEffectBattler1 = GetBattlerHoldEffect(battler1, TRUE);
u32 speedBattler2 = GetBattlerTotalSpeedStat(battler2);
u32 holdEffectBattler2 = GetBattlerHoldEffect(battler2, TRUE);
u32 ability2 = GetBattlerAbility(battler2);
if (!ignoreChosenMoves)
{
if (gChosenActionByBattler[battler1] == B_ACTION_USE_MOVE)
priority1 = GetChosenMovePriority(battler1);
if (gChosenActionByBattler[battler2] == B_ACTION_USE_MOVE)
priority2 = GetChosenMovePriority(battler2);
}
return GetWhichBattlerFasterArgs(battler1, battler2, ignoreChosenMoves, ability1, ability2,
holdEffectBattler1, holdEffectBattler2, speedBattler1, speedBattler2, priority1, priority2);
}
2018-02-08 11:17:41 +01:00
static void SetActionsAndBattlersTurnOrder(void)
2017-10-04 19:25:14 +02:00
{
2018-06-28 21:06:32 +02:00
s32 turnOrderId = 0;
2023-08-30 10:18:31 +02:00
s32 i, j, battler;
2017-10-04 19:25:14 +02:00
if (gBattleTypeFlags & BATTLE_TYPE_SAFARI)
{
2023-08-30 10:18:31 +02:00
for (battler = 0; battler < gBattlersCount; battler++)
2017-10-04 19:25:14 +02:00
{
2023-08-30 10:18:31 +02:00
gActionsByTurnOrder[turnOrderId] = gChosenActionByBattler[battler];
gBattlerByTurnOrder[turnOrderId] = battler;
2018-06-28 21:06:32 +02:00
turnOrderId++;
2017-10-04 19:25:14 +02:00
}
}
else
{
if (gBattleTypeFlags & BATTLE_TYPE_LINK)
{
2023-08-30 10:18:31 +02:00
for (battler = 0; battler < gBattlersCount; battler++)
2017-10-04 19:25:14 +02:00
{
2023-08-30 10:18:31 +02:00
if (gChosenActionByBattler[battler] == B_ACTION_RUN)
2017-10-04 19:25:14 +02:00
{
2018-06-28 21:06:32 +02:00
turnOrderId = 5;
2017-10-04 19:25:14 +02:00
break;
}
}
}
else
{
2018-02-06 16:09:39 -06:00
if (gChosenActionByBattler[0] == B_ACTION_RUN)
2017-10-04 19:25:14 +02:00
{
2023-08-30 10:18:31 +02:00
battler = 0;
2018-06-28 21:06:32 +02:00
turnOrderId = 5;
2017-10-04 19:25:14 +02:00
}
2018-02-06 16:09:39 -06:00
if (gChosenActionByBattler[2] == B_ACTION_RUN)
2017-10-04 19:25:14 +02:00
{
2023-08-30 10:18:31 +02:00
battler = 2;
2018-06-28 21:06:32 +02:00
turnOrderId = 5;
2017-10-04 19:25:14 +02:00
}
}
2018-06-28 21:06:32 +02:00
if (turnOrderId == 5) // One of battlers wants to run.
2017-10-04 19:25:14 +02:00
{
2023-08-30 10:18:31 +02:00
gActionsByTurnOrder[0] = gChosenActionByBattler[battler];
gBattlerByTurnOrder[0] = battler;
2018-06-28 21:06:32 +02:00
turnOrderId = 1;
2018-02-05 19:46:59 -06:00
for (i = 0; i < gBattlersCount; i++)
2017-10-04 19:25:14 +02:00
{
2023-08-30 10:18:31 +02:00
if (i != battler)
2017-10-04 19:25:14 +02:00
{
2018-06-28 21:06:32 +02:00
gActionsByTurnOrder[turnOrderId] = gChosenActionByBattler[i];
gBattlerByTurnOrder[turnOrderId] = i;
turnOrderId++;
2017-10-04 19:25:14 +02:00
}
}
}
else
2017-10-04 19:25:14 +02:00
{
2023-08-30 10:18:31 +02:00
for (battler = 0; battler < gBattlersCount; battler++)
2017-10-04 19:25:14 +02:00
{
2023-08-30 10:18:31 +02:00
if (gChosenActionByBattler[battler] == B_ACTION_USE_ITEM
|| gChosenActionByBattler[battler] == B_ACTION_SWITCH
|| gChosenActionByBattler[battler] == B_ACTION_THROW_BALL)
{
2023-08-30 10:18:31 +02:00
gActionsByTurnOrder[turnOrderId] = gChosenActionByBattler[battler];
gBattlerByTurnOrder[turnOrderId] = battler;
turnOrderId++;
}
2017-10-04 19:25:14 +02:00
}
2023-08-30 10:18:31 +02:00
for (battler = 0; battler < gBattlersCount; battler++)
2017-10-04 19:25:14 +02:00
{
2023-08-30 10:18:31 +02:00
if (gChosenActionByBattler[battler] != B_ACTION_USE_ITEM
&& gChosenActionByBattler[battler] != B_ACTION_SWITCH
&& gChosenActionByBattler[battler] != B_ACTION_THROW_BALL)
{
2023-08-30 10:18:31 +02:00
gActionsByTurnOrder[turnOrderId] = gChosenActionByBattler[battler];
gBattlerByTurnOrder[turnOrderId] = battler;
turnOrderId++;
}
2017-10-04 19:25:14 +02:00
}
for (i = 0; i < gBattlersCount - 1; i++)
2017-10-04 19:25:14 +02:00
{
for (j = i + 1; j < gBattlersCount; j++)
2017-10-04 19:25:14 +02:00
{
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 (GetWhichBattlerFaster(battler1, battler2, FALSE))
SwapTurnOrder(i, j);
}
2017-10-04 19:25:14 +02:00
}
}
}
}
gBattleMainFunc = CheckQuickClaw_CustapBerryActivation;
gBattleStruct->quickClawBattlerId = 0;
2017-10-06 00:12:01 +02:00
}
2017-10-06 19:09:37 +02:00
static void TurnValuesCleanUp(bool8 var0)
2017-10-06 00:12:01 +02:00
{
s32 i;
2023-08-29 16:20:16 +02:00
for (i = 0; i < gBattlersCount; i++)
2017-10-06 00:12:01 +02:00
{
if (var0)
{
2023-08-29 16:20:16 +02:00
gProtectStructs[i].protected = FALSE;
gProtectStructs[i].spikyShielded = FALSE;
gProtectStructs[i].kingsShielded = FALSE;
gProtectStructs[i].banefulBunkered = FALSE;
gProtectStructs[i].quash = FALSE;
gProtectStructs[i].usedCustapBerry = FALSE;
2017-10-06 00:12:01 +02:00
}
else
{
2023-08-29 16:20:16 +02:00
memset(&gProtectStructs[i], 0, sizeof(struct ProtectStruct));
2017-10-06 00:12:01 +02:00
2023-08-29 16:20:16 +02:00
if (gDisableStructs[i].isFirstTurn)
gDisableStructs[i].isFirstTurn--;
2017-10-06 00:12:01 +02:00
2023-08-29 16:20:16 +02:00
if (gDisableStructs[i].rechargeTimer)
2017-10-06 00:12:01 +02:00
{
2023-08-29 16:20:16 +02:00
gDisableStructs[i].rechargeTimer--;
if (gDisableStructs[i].rechargeTimer == 0)
gBattleMons[i].status2 &= ~STATUS2_RECHARGE;
2017-10-06 00:12:01 +02:00
}
}
2023-08-29 16:20:16 +02:00
if (gDisableStructs[i].substituteHP == 0)
gBattleMons[i].status2 &= ~STATUS2_SUBSTITUTE;
2023-08-29 16:20:16 +02:00
gSpecialStatuses[i].parentalBondState = PARENTAL_BOND_OFF;
2017-10-06 00:12:01 +02:00
}
2022-12-15 17:39:27 -03:00
gSideStatuses[B_SIDE_PLAYER] &= ~(SIDE_STATUS_QUICK_GUARD | SIDE_STATUS_WIDE_GUARD | SIDE_STATUS_CRAFTY_SHIELD | SIDE_STATUS_MAT_BLOCK);
gSideStatuses[B_SIDE_OPPONENT] &= ~(SIDE_STATUS_QUICK_GUARD | SIDE_STATUS_WIDE_GUARD | SIDE_STATUS_CRAFTY_SHIELD | SIDE_STATUS_MAT_BLOCK);
gSideTimers[B_SIDE_PLAYER].followmeTimer = 0;
gSideTimers[B_SIDE_OPPONENT].followmeTimer = 0;
2017-10-06 00:12:01 +02:00
}
void SpecialStatusesClear(void)
2017-10-06 00:12:01 +02:00
{
2019-01-27 13:52:02 +01:00
memset(&gSpecialStatuses, 0, sizeof(gSpecialStatuses));
2017-10-06 00:12:01 +02:00
}
static void PopulateArrayWithBattlers(u8 *battlers)
2018-09-16 18:55:32 +02:00
{
u32 i;
for (i = 0; i < gBattlersCount; i++)
battlers[i] = i;
}
static bool32 TryDoMegaEvosBeforeMoves(void)
{
2023-08-09 22:12:26 -05:00
if (!(gHitMarker & HITMARKER_RUN) && (gBattleStruct->mega.toEvolve || gBattleStruct->burst.toBurst))
2018-09-16 18:55:32 +02:00
{
2023-08-30 13:23:55 +02:00
u32 i, battler;
struct Pokemon *party;
struct Pokemon *mon;
u8 megaOrder[MAX_BATTLERS_COUNT];
PopulateArrayWithBattlers(megaOrder);
SortBattlersBySpeed(megaOrder, FALSE);
for (i = 0; i < gBattlersCount; i++)
2018-09-16 18:55:32 +02:00
{
if (gBattleStruct->mega.toEvolve & gBitTable[megaOrder[i]]
&& !(gProtectStructs[megaOrder[i]].noValidMoves))
2018-09-16 18:55:32 +02:00
{
gBattlerAttacker = megaOrder[i];
gBattleStruct->mega.toEvolve &= ~(gBitTable[gBattlerAttacker]);
gLastUsedItem = gBattleMons[gBattlerAttacker].item;
party = GetBattlerParty(gBattlerAttacker);
mon = &party[gBattlerPartyIndexes[gBattlerAttacker]];
if (GetBattleFormChangeTargetSpecies(gBattlerAttacker, FORM_CHANGE_BATTLE_MEGA_EVOLUTION_MOVE) != SPECIES_NONE)
2020-11-07 09:53:34 -03:00
BattleScriptExecute(BattleScript_WishMegaEvolution);
else
BattleScriptExecute(BattleScript_MegaEvolution);
return TRUE;
2018-09-16 18:55:32 +02:00
}
2023-08-09 22:12:26 -05:00
if (gBattleStruct->burst.toBurst & gBitTable[megaOrder[i]]
&& !(gProtectStructs[megaOrder[i]].noValidMoves))
{
2023-08-30 13:23:55 +02:00
battler = gBattlerAttacker = megaOrder[i];
gBattleStruct->burst.toBurst &= ~(gBitTable[battler]);
gLastUsedItem = gBattleMons[battler].item;
party = GetBattlerParty(battler);
mon = &party[gBattlerPartyIndexes[battler]];
2023-08-09 22:12:26 -05:00
BattleScriptExecute(BattleScript_UltraBurst);
return TRUE;
}
2018-09-16 18:55:32 +02:00
}
2017-10-06 00:12:01 +02:00
}
2018-09-16 18:55:32 +02:00
#if B_MEGA_EVO_TURN_ORDER >= GEN_7
TryChangeTurnOrder(); // This will just do nothing if no mon has mega evolved.
#endif
return FALSE;
2021-11-19 22:03:10 +01:00
}
static bool32 TryDoMoveEffectsBeforeMoves(void)
2017-10-06 00:12:01 +02:00
{
2017-10-06 17:06:45 +02:00
if (!(gHitMarker & HITMARKER_RUN))
2017-10-06 00:12:01 +02:00
{
u32 i;
struct Pokemon *mon;
u8 battlers[MAX_BATTLERS_COUNT];
PopulateArrayWithBattlers(battlers);
SortBattlersBySpeed(battlers, FALSE);
for (i = 0; i < gBattlersCount; i++)
2017-10-06 00:12:01 +02:00
{
if (!(gBattleStruct->focusPunchBattlers & gBitTable[battlers[i]])
&& !(gBattleMons[battlers[i]].status1 & STATUS1_SLEEP)
&& !(gDisableStructs[battlers[i]].truantCounter)
&& !(gProtectStructs[battlers[i]].noValidMoves))
2017-10-06 00:12:01 +02:00
{
gBattleStruct->focusPunchBattlers |= gBitTable[battlers[i]];
2023-08-30 10:18:31 +02:00
gBattlerAttacker = battlers[i];
switch (gChosenMoveByBattler[gBattlerAttacker])
2021-06-15 19:57:21 +02:00
{
case MOVE_FOCUS_PUNCH:
BattleScriptExecute(BattleScript_FocusPunchSetUp);
return TRUE;
2021-06-15 19:57:21 +02:00
case MOVE_BEAK_BLAST:
BattleScriptExecute(BattleScript_BeakBlastSetUp);
return TRUE;
case MOVE_SHELL_TRAP:
BattleScriptExecute(BattleScript_ShellTrapSetUp);
return TRUE;
2021-06-15 19:57:21 +02:00
}
2017-10-06 00:12:01 +02:00
}
}
}
return FALSE;
}
// In gen7, priority and speed are recalculated during the turn in which a pokemon mega evolves
static void TryChangeTurnOrder(void)
{
u32 i, j;
for (i = 0; i < gBattlersCount - 1; i++)
{
for (j = i + 1; j < gBattlersCount; j++)
{
u32 battler1 = gBattlerByTurnOrder[i];
u32 battler2 = gBattlerByTurnOrder[j];
if (gActionsByTurnOrder[i] == B_ACTION_USE_MOVE
&& gActionsByTurnOrder[j] == B_ACTION_USE_MOVE)
{
if (GetWhichBattlerFaster(battler1, battler2, FALSE))
SwapTurnOrder(i, j);
}
}
}
}
static void CheckQuickClaw_CustapBerryActivation(void)
{
2023-08-30 10:18:31 +02:00
u32 i, battler;
if (!(gHitMarker & HITMARKER_RUN))
{
while (gBattleStruct->quickClawBattlerId < gBattlersCount)
{
2023-08-30 10:18:31 +02:00
battler = gBattlerAttacker = gBattleStruct->quickClawBattlerId;
gBattleStruct->quickClawBattlerId++;
2023-08-30 10:18:31 +02:00
if (gChosenActionByBattler[battler] == B_ACTION_USE_MOVE
&& gChosenMoveByBattler[battler] != MOVE_FOCUS_PUNCH // quick claw message doesn't need to activate here
&& (gProtectStructs[battler].usedCustapBerry || gProtectStructs[battler].quickDraw)
&& !(gBattleMons[battler].status1 & STATUS1_SLEEP)
&& !(gDisableStructs[gBattlerAttacker].truantCounter)
2023-08-30 10:18:31 +02:00
&& !(gProtectStructs[battler].noValidMoves))
{
2023-08-30 10:18:31 +02:00
if (gProtectStructs[battler].usedCustapBerry)
{
2023-08-30 10:18:31 +02:00
gLastUsedItem = gBattleMons[battler].item;
2021-10-12 21:49:18 -04:00
PREPARE_ITEM_BUFFER(gBattleTextBuff1, gLastUsedItem);
2023-08-30 10:18:31 +02:00
if (GetBattlerHoldEffect(battler, FALSE) == HOLD_EFFECT_CUSTAP_BERRY)
2021-10-12 21:49:18 -04:00
{
// don't record berry since its gone now
BattleScriptExecute(BattleScript_CustapBerryActivation);
}
else
{
2023-08-30 10:18:31 +02:00
RecordItemEffectBattle(battler, GetBattlerHoldEffect(battler, FALSE));
2021-10-12 21:49:18 -04:00
BattleScriptExecute(BattleScript_QuickClawActivation);
}
}
2023-08-30 10:18:31 +02:00
else if (gProtectStructs[battler].quickDraw)
{
2023-08-30 10:18:31 +02:00
gBattlerAbility = battler;
gProtectStructs[battler].quickDraw = FALSE;
gLastUsedAbility = gBattleMons[battler].ability;
2021-10-12 21:49:18 -04:00
PREPARE_ABILITY_BUFFER(gBattleTextBuff1, gLastUsedAbility);
2023-08-30 10:18:31 +02:00
RecordAbilityBattle(battler, gLastUsedAbility);
2021-10-12 21:49:18 -04:00
BattleScriptExecute(BattleScript_QuickDrawActivation);
}
return;
}
}
}
2021-06-08 16:40:47 +02:00
// setup stuff before turns/actions
2018-08-11 12:16:00 +02:00
TryClearRageAndFuryCutter();
2017-10-06 00:12:01 +02:00
gCurrentTurnActionNumber = 0;
gCurrentActionFuncId = gActionsByTurnOrder[0];
2017-10-06 00:12:01 +02:00
gBattleStruct->dynamicMoveType = 0;
gBattleStruct->effectsBeforeUsingMoveDone = FALSE;
gBattleStruct->focusPunchBattlers = 0;
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
2020-04-29 11:53:03 +02:00
{
gBattleStruct->ateBoost[i] = FALSE;
2020-04-29 11:53:03 +02:00
gSpecialStatuses[i].gemBoost = FALSE;
}
2017-10-06 00:12:01 +02:00
gBattleMainFunc = RunTurnActionsFunctions;
gBattleCommunication[3] = 0;
gBattleCommunication[4] = 0;
gBattleScripting.multihitMoveEffect = 0;
2017-10-06 00:12:01 +02:00
gBattleResources->battleScriptsStack->size = 0;
}
static void RunTurnActionsFunctions(void)
{
if (gBattleOutcome != 0)
2019-09-03 15:08:25 -04:00
gCurrentActionFuncId = B_ACTION_FINISHED;
2017-10-06 00:12:01 +02:00
// Mega Evolve / Focus Punch-like moves after switching, items, running, but before using a move.
if (gCurrentActionFuncId == B_ACTION_USE_MOVE && !gBattleStruct->effectsBeforeUsingMoveDone)
{
if (TryDoMegaEvosBeforeMoves())
return;
else if (TryDoMoveEffectsBeforeMoves())
return;
gBattleStruct->effectsBeforeUsingMoveDone = TRUE;
}
*(&gBattleStruct->savedTurnActionNumber) = gCurrentTurnActionNumber;
2017-10-06 19:09:37 +02:00
sTurnActionsFuncsTable[gCurrentActionFuncId]();
2017-10-06 00:12:01 +02:00
2018-02-05 19:46:59 -06:00
if (gCurrentTurnActionNumber >= gBattlersCount) // everyone did their actions, turn finished
2017-10-06 00:12:01 +02:00
{
2021-09-24 14:30:15 -04:00
gHitMarker &= ~HITMARKER_PASSIVE_DAMAGE;
2017-10-06 19:09:37 +02:00
gBattleMainFunc = sEndTurnFuncsTable[gBattleOutcome & 0x7F];
2017-10-06 00:12:01 +02:00
}
else
2017-10-06 00:12:01 +02:00
{
2023-08-30 10:18:31 +02:00
if (gBattleStruct->savedTurnActionNumber != gCurrentTurnActionNumber) // action turn has been done, clear hitmarker bits for another battler
{
2021-09-24 14:30:15 -04:00
gHitMarker &= ~HITMARKER_NO_ATTACKSTRING;
gHitMarker &= ~HITMARKER_UNABLE_TO_USE_MOVE;
}
2017-10-06 00:12:01 +02:00
}
}
2017-10-06 19:09:37 +02:00
static void HandleEndTurn_BattleWon(void)
2017-10-06 00:12:01 +02:00
{
gCurrentActionFuncId = 0;
2021-01-13 15:17:32 -05:00
if (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK))
2017-10-06 00:12:01 +02:00
{
2017-11-10 18:12:18 -06:00
gSpecialVar_Result = gBattleOutcome;
2017-10-06 00:12:01 +02:00
gBattleTextBuff1[0] = gBattleOutcome;
2018-02-06 16:09:39 -06:00
gBattlerAttacker = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
2017-10-06 00:12:01 +02:00
gBattlescriptCurrInstr = BattleScript_LinkBattleWonOrLost;
2021-09-24 14:30:15 -04:00
gBattleOutcome &= ~B_OUTCOME_LINK_BATTLE_RAN;
2017-10-06 00:12:01 +02:00
}
else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER
2018-09-20 22:00:00 +02:00
&& gBattleTypeFlags & (BATTLE_TYPE_FRONTIER | BATTLE_TYPE_TRAINER_HILL | BATTLE_TYPE_EREADER_TRAINER))
2017-10-06 00:12:01 +02:00
{
2017-11-04 16:11:13 +01:00
BattleStopLowHpSound();
2017-10-06 00:12:01 +02:00
gBattlescriptCurrInstr = BattleScript_FrontierTrainerBattleWon;
if (gTrainerBattleOpponent_A == TRAINER_FRONTIER_BRAIN)
2020-08-20 18:02:00 -04:00
PlayBGM(MUS_VICTORY_GYM_LEADER);
2017-10-06 00:12:01 +02:00
else
2020-08-20 18:02:00 -04:00
PlayBGM(MUS_VICTORY_TRAINER);
2017-10-06 00:12:01 +02:00
}
else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER && !(gBattleTypeFlags & BATTLE_TYPE_LINK))
{
2017-11-04 16:11:13 +01:00
BattleStopLowHpSound();
2017-10-06 00:12:01 +02:00
gBattlescriptCurrInstr = BattleScript_LocalTrainerBattleWon;
switch (gTrainers[gTrainerBattleOpponent_A].trainerClass)
{
2017-12-17 21:19:08 +01:00
case TRAINER_CLASS_ELITE_FOUR:
case TRAINER_CLASS_CHAMPION:
2020-08-20 18:02:00 -04:00
PlayBGM(MUS_VICTORY_LEAGUE);
2017-10-06 00:12:01 +02:00
break;
2017-12-17 21:19:08 +01:00
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:
2020-08-20 18:02:00 -04:00
PlayBGM(MUS_VICTORY_AQUA_MAGMA);
2017-10-06 00:12:01 +02:00
break;
2017-12-17 21:19:08 +01:00
case TRAINER_CLASS_LEADER:
2020-08-20 18:02:00 -04:00
PlayBGM(MUS_VICTORY_GYM_LEADER);
2017-10-06 00:12:01 +02:00
break;
default:
2020-08-20 18:02:00 -04:00
PlayBGM(MUS_VICTORY_TRAINER);
2017-10-06 00:12:01 +02:00
break;
}
}
else
{
gBattlescriptCurrInstr = BattleScript_PayDayMoneyAndPickUpItems;
}
gBattleMainFunc = HandleEndTurn_FinishBattle;
}
2017-10-06 19:09:37 +02:00
static void HandleEndTurn_BattleLost(void)
2017-10-06 00:12:01 +02:00
{
gCurrentActionFuncId = 0;
2021-01-13 15:17:32 -05:00
if (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK))
2017-10-06 00:12:01 +02:00
{
if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER)
{
2018-01-16 15:12:38 -06:00
if (gBattleOutcome & B_OUTCOME_LINK_BATTLE_RAN)
2017-10-06 00:12:01 +02:00
{
2017-12-03 00:47:21 +01:00
gBattlescriptCurrInstr = BattleScript_PrintPlayerForfeitedLinkBattle;
2021-09-24 14:30:15 -04:00
gBattleOutcome &= ~B_OUTCOME_LINK_BATTLE_RAN;
2019-12-14 03:58:20 -05:00
gSaveBlock2Ptr->frontier.disableRecordBattle = TRUE;
2017-10-06 00:12:01 +02:00
}
else
{
gBattlescriptCurrInstr = BattleScript_FrontierLinkBattleLost;
2021-09-24 14:30:15 -04:00
gBattleOutcome &= ~B_OUTCOME_LINK_BATTLE_RAN;
2017-10-06 00:12:01 +02:00
}
}
else
{
gBattleTextBuff1[0] = gBattleOutcome;
2018-02-06 16:09:39 -06:00
gBattlerAttacker = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
2017-10-06 00:12:01 +02:00
gBattlescriptCurrInstr = BattleScript_LinkBattleWonOrLost;
2021-09-24 14:30:15 -04:00
gBattleOutcome &= ~B_OUTCOME_LINK_BATTLE_RAN;
2017-10-06 00:12:01 +02:00
}
}
else
{
gBattlescriptCurrInstr = BattleScript_LocalBattleLost;
}
gBattleMainFunc = HandleEndTurn_FinishBattle;
}
2017-10-06 19:09:37 +02:00
static void HandleEndTurn_RanFromBattle(void)
2017-10-06 00:12:01 +02:00
{
gCurrentActionFuncId = 0;
if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER && gBattleTypeFlags & BATTLE_TYPE_TRAINER)
{
2017-12-03 00:47:21 +01:00
gBattlescriptCurrInstr = BattleScript_PrintPlayerForfeited;
2018-01-16 15:12:38 -06:00
gBattleOutcome = B_OUTCOME_FORFEITED;
2019-12-14 03:58:20 -05:00
gSaveBlock2Ptr->frontier.disableRecordBattle = TRUE;
2017-10-06 00:12:01 +02:00
}
2018-09-20 22:00:00 +02:00
else if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_HILL)
2017-10-06 00:12:01 +02:00
{
2017-12-03 00:47:21 +01:00
gBattlescriptCurrInstr = BattleScript_PrintPlayerForfeited;
2018-01-16 15:12:38 -06:00
gBattleOutcome = B_OUTCOME_FORFEITED;
2017-10-06 00:12:01 +02:00
}
else
{
2021-10-04 10:21:03 -04:00
switch (gProtectStructs[gBattlerAttacker].fleeType)
2017-10-06 00:12:01 +02:00
{
default:
gBattlescriptCurrInstr = BattleScript_GotAwaySafely;
break;
2021-10-04 10:21:03 -04:00
case FLEE_ITEM:
2017-10-06 00:12:01 +02:00
gBattlescriptCurrInstr = BattleScript_SmokeBallEscape;
break;
2021-10-04 10:21:03 -04:00
case FLEE_ABILITY:
2017-10-06 00:12:01 +02:00
gBattlescriptCurrInstr = BattleScript_RanAwayUsingMonAbility;
break;
}
}
gBattleMainFunc = HandleEndTurn_FinishBattle;
}
2017-10-06 19:09:37 +02:00
static void HandleEndTurn_MonFled(void)
2017-10-06 00:12:01 +02:00
{
gCurrentActionFuncId = 0;
2018-02-06 16:09:39 -06:00
PREPARE_MON_NICK_BUFFER(gBattleTextBuff1, gBattlerAttacker, gBattlerPartyIndexes[gBattlerAttacker]);
2017-10-06 00:12:01 +02:00
gBattlescriptCurrInstr = BattleScript_WildMonFled;
gBattleMainFunc = HandleEndTurn_FinishBattle;
}
2017-10-06 19:09:37 +02:00
static void HandleEndTurn_FinishBattle(void)
2017-10-06 00:12:01 +02:00
{
2023-08-29 16:20:16 +02:00
u32 i, battler;
2018-09-16 21:08:49 +02:00
2019-09-03 15:08:25 -04:00
if (gCurrentActionFuncId == B_ACTION_TRY_FINISH || gCurrentActionFuncId == B_ACTION_FINISHED)
2017-10-06 00:12:01 +02:00
{
if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK
2021-01-13 15:17:32 -05:00
| BATTLE_TYPE_RECORDED_LINK
2017-10-06 00:12:01 +02:00
| BATTLE_TYPE_FIRST_BATTLE
| BATTLE_TYPE_SAFARI
| BATTLE_TYPE_EREADER_TRAINER
| BATTLE_TYPE_WALLY_TUTORIAL
| BATTLE_TYPE_FRONTIER)))
{
2023-08-29 16:20:16 +02:00
for (battler = 0; battler < gBattlersCount; battler++)
2017-10-06 00:12:01 +02:00
{
2023-08-29 16:20:16 +02:00
if (GetBattlerSide(battler) == B_SIDE_PLAYER)
2017-10-06 00:12:01 +02:00
{
if (gBattleResults.playerMon1Species == SPECIES_NONE)
{
2023-08-29 16:20:16 +02:00
gBattleResults.playerMon1Species = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battler]], MON_DATA_SPECIES, NULL);
GetMonData(&gPlayerParty[gBattlerPartyIndexes[battler]], MON_DATA_NICKNAME, gBattleResults.playerMon1Name);
2017-10-06 00:12:01 +02:00
}
else
{
2023-08-29 16:20:16 +02:00
gBattleResults.playerMon2Species = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battler]], MON_DATA_SPECIES, NULL);
GetMonData(&gPlayerParty[gBattlerPartyIndexes[battler]], MON_DATA_NICKNAME, gBattleResults.playerMon2Name);
2017-10-06 00:12:01 +02:00
}
}
}
2021-04-25 17:22:45 -04:00
TryPutPokemonTodayOnAir();
2017-10-06 00:12:01 +02:00
}
if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK
2021-01-13 15:17:32 -05:00
| BATTLE_TYPE_RECORDED_LINK
2017-10-06 00:12:01 +02:00
| BATTLE_TYPE_TRAINER
| BATTLE_TYPE_FIRST_BATTLE
| BATTLE_TYPE_SAFARI
| BATTLE_TYPE_FRONTIER
| BATTLE_TYPE_EREADER_TRAINER
| BATTLE_TYPE_WALLY_TUTORIAL))
2018-06-28 21:06:32 +02:00
&& gBattleResults.shinyWildMon)
2017-10-06 00:12:01 +02:00
{
2021-04-25 17:22:45 -04:00
TryPutBreakingNewsOnAir();
2017-10-06 00:12:01 +02:00
}
2021-10-04 10:21:03 -04:00
RecordedBattle_SetPlaybackFinished();
if (gTestRunnerEnabled)
TestRunner_Battle_AfterLastTurn();
2017-10-06 00:12:01 +02:00
BeginFastPaletteFade(3);
FadeOutMapMusic(5);
#if B_TRAINERS_KNOCK_OFF_ITEMS == TRUE || B_RESTORE_HELD_BATTLE_ITEMS == TRUE
TryRestoreHeldItems();
#endif
2018-09-16 21:08:49 +02:00
for (i = 0; i < PARTY_SIZE; i++)
{
bool8 changedForm = FALSE;
// Appeared in battle and didn't faint
if ((gBattleStruct->appearedInBattle & gBitTable[i]) && GetMonData(&gPlayerParty[i], MON_DATA_HP, NULL) != 0)
changedForm = TryFormChange(i, B_SIDE_PLAYER, FORM_CHANGE_END_BATTLE_TERRAIN);
if (!changedForm)
changedForm = TryFormChange(i, B_SIDE_PLAYER, FORM_CHANGE_END_BATTLE);
// Clear original species field
gBattleStruct->changedSpecies[B_SIDE_PLAYER][i] = SPECIES_NONE;
gBattleStruct->changedSpecies[B_SIDE_OPPONENT][i] = SPECIES_NONE;
#if B_RECALCULATE_STATS >= GEN_5
// Recalculate the stats of every party member before the end
if (!changedForm)
CalculateMonStats(&gPlayerParty[i]);
#endif
}
2022-10-21 02:08:37 -03:00
// Clear battle mon species to avoid a bug on the next battle that causes
// healthboxes loading incorrectly due to it trying to create a Mega Indicator
// if the previous battler would've had it.
2022-10-21 02:08:37 -03:00
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
{
2022-10-21 02:08:37 -03:00
gBattleMons[i].species = SPECIES_NONE;
}
2017-10-06 00:12:01 +02:00
gBattleMainFunc = FreeResetData_ReturnToOvOrDoEvolutions;
2017-10-29 16:15:23 +01:00
gCB2_AfterEvolution = BattleMainCB2;
2017-10-06 00:12:01 +02:00
}
else
{
2018-02-06 13:48:02 -06:00
if (gBattleControllerExecFlags == 0)
2017-10-06 00:12:01 +02:00
gBattleScriptingCommandsTable[gBattlescriptCurrInstr[0]]();
}
}
static void FreeResetData_ReturnToOvOrDoEvolutions(void)
{
if (!gPaletteFade.active)
{
2019-04-16 09:38:49 +02:00
gIsFishingEncounter = FALSE;
2020-10-17 02:33:15 -03:00
gIsSurfingEncounter = FALSE;
2017-10-06 00:12:01 +02:00
ResetSpriteData();
2022-05-05 11:41:27 -07:00
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
)
2017-10-06 00:12:01 +02:00
{
2022-05-05 11:41:27 -07:00
gBattleMainFunc = TrySpecialEvolution;
2017-10-06 00:12:01 +02:00
}
else
{
gBattleMainFunc = ReturnFromBattleToOverworld;
return;
}
}
FreeAllWindowBuffers();
if (!(gBattleTypeFlags & BATTLE_TYPE_LINK))
{
FreeMonSpritesGfx();
FreeBattleResources();
FreeBattleSpritesData();
}
}
2022-05-05 11:41:27 -07:00
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);
2022-05-05 11:41:27 -07:00
if (species != SPECIES_NONE && !(sTriedEvolving & gBitTable[i]))
{
sTriedEvolving |= gBitTable[i];
FreeAllWindowBuffers();
gBattleMainFunc = WaitForEvoSceneToFinish;
EvolutionScene(&gPlayerParty[i], species, TRUE, i);
return;
}
}
sTriedEvolving = 0;
gBattleMainFunc = TryEvolvePokemon;
}
2017-10-06 00:12:01 +02:00
static void TryEvolvePokemon(void)
{
s32 i;
while (gLeveledUpInBattle != 0)
{
2018-02-08 11:17:41 +01:00
for (i = 0; i < PARTY_SIZE; i++)
2017-10-06 00:12:01 +02:00
{
if (gLeveledUpInBattle & gBitTable[i])
{
u16 species;
u8 levelUpBits = gLeveledUpInBattle;
levelUpBits &= ~(gBitTable[i]);
gLeveledUpInBattle = levelUpBits;
species = GetEvolutionTargetSpecies(&gPlayerParty[i], EVO_MODE_NORMAL, levelUpBits, NULL);
2017-10-06 00:12:01 +02:00
if (species != SPECIES_NONE)
{
FreeAllWindowBuffers();
gBattleMainFunc = WaitForEvoSceneToFinish;
EvolutionScene(&gPlayerParty[i], species, TRUE, i);
return;
}
}
}
}
gBattleMainFunc = ReturnFromBattleToOverworld;
}
static void WaitForEvoSceneToFinish(void)
{
if (gMain.callback2 == BattleMainCB2)
2022-05-05 11:41:27 -07:00
gBattleMainFunc = TrySpecialEvolution;
2017-10-06 00:12:01 +02:00
}
static void ReturnFromBattleToOverworld(void)
{
if (!(gBattleTypeFlags & BATTLE_TYPE_LINK))
{
RandomlyGivePartyPokerus(gPlayerParty);
PartySpreadPokerus(gPlayerParty);
}
if (gBattleTypeFlags & BATTLE_TYPE_LINK && gReceivedRemoteLinkPlayers)
2017-10-06 00:12:01 +02:00
return;
2017-11-10 18:12:18 -06:00
gSpecialVar_Result = gBattleOutcome;
2021-10-04 10:21:03 -04:00
gMain.inBattle = FALSE;
2017-10-06 00:12:01 +02:00
gMain.callback1 = gPreBattleCallback1;
if (gBattleTypeFlags & BATTLE_TYPE_ROAMER)
{
UpdateRoamerHPStatus(&gEnemyParty[0]);
#ifndef BUGFIX
2018-02-08 00:35:13 +01:00
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.
2017-10-06 00:12:01 +02:00
SetRoamerInactive();
}
2020-08-20 18:02:00 -04:00
m4aSongNumStop(SE_LOW_HEALTH);
2017-10-06 00:12:01 +02:00
SetMainCallback2(gMain.savedCallback);
}
void RunBattleScriptCommands_PopCallbacksStack(void)
{
2019-09-03 15:08:25 -04:00
if (gCurrentActionFuncId == B_ACTION_TRY_FINISH || gCurrentActionFuncId == B_ACTION_FINISHED)
2017-10-06 00:12:01 +02:00
{
2018-02-06 16:09:39 -06:00
if (gBattleResources->battleCallbackStack->size != 0)
gBattleResources->battleCallbackStack->size--;
gBattleMainFunc = gBattleResources->battleCallbackStack->function[gBattleResources->battleCallbackStack->size];
2017-10-06 00:12:01 +02:00
}
else
{
2018-02-06 13:48:02 -06:00
if (gBattleControllerExecFlags == 0)
2017-10-06 00:12:01 +02:00
gBattleScriptingCommandsTable[gBattlescriptCurrInstr[0]]();
}
}
void RunBattleScriptCommands(void)
{
2018-02-06 13:48:02 -06:00
if (gBattleControllerExecFlags == 0)
2017-10-06 00:12:01 +02:00
gBattleScriptingCommandsTable[gBattlescriptCurrInstr[0]]();
}
2023-09-14 11:05:00 +02:00
void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk)
{
u32 moveType, ateType, attackerAbility;
2021-05-24 09:52:45 -06:00
u16 holdEffect = GetBattlerHoldEffect(battlerAtk, TRUE);
if (move == MOVE_STRUGGLE)
return;
2017-10-06 00:12:01 +02:00
2020-04-29 11:53:03 +02:00
gBattleStruct->dynamicMoveType = 0;
gBattleStruct->ateBoost[battlerAtk] = 0;
gSpecialStatuses[battlerAtk].gemBoost = FALSE;
2020-04-29 11:53:03 +02:00
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;
2023-04-29 12:30:39 +02:00
else if (gBattleWeather & (B_WEATHER_HAIL |B_WEATHER_SNOW))
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)
2018-09-22 17:27:51 +02:00
{
2021-05-24 09:52:45 -06:00
if (holdEffect == gBattleMoves[move].argument)
gBattleStruct->dynamicMoveType = ItemId_GetSecondaryId(gBattleMons[battlerAtk].item) | F_DYNAMIC_TYPE_2;
2018-09-22 17:27:51 +02:00
}
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;
2018-11-17 12:10:24 +01:00
else if (gBattleMons[battlerAtk].type3 != TYPE_MYSTERY)
gBattleStruct->dynamicMoveType = gBattleMons[battlerAtk].type3 | F_DYNAMIC_TYPE_2;
}
2019-05-20 12:03:00 +02:00
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;
}
2021-10-04 22:45:37 -03:00
else if (gBattleMoves[move].effect == EFFECT_TERRAIN_PULSE)
{
2021-11-06 11:07:40 -03:00
if (IsBattlerTerrainAffected(battlerAtk, STATUS_FIELD_TERRAIN_ANY))
2021-10-04 22:45:37 -03:00
{
if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN)
gBattleStruct->dynamicMoveType = TYPE_ELECTRIC | F_DYNAMIC_TYPE_2;
2021-10-04 22:45:37 -03:00
else if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN)
gBattleStruct->dynamicMoveType = TYPE_GRASS | F_DYNAMIC_TYPE_2;
2021-10-04 22:45:37 -03:00
else if (gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN)
gBattleStruct->dynamicMoveType = TYPE_FAIRY | F_DYNAMIC_TYPE_2;
2021-10-04 22:45:37 -03:00
else if (gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN)
gBattleStruct->dynamicMoveType = TYPE_PSYCHIC | F_DYNAMIC_TYPE_2;
2021-11-07 11:25:08 -03:00
else //failsafe
gBattleStruct->dynamicMoveType = TYPE_NORMAL | F_DYNAMIC_TYPE_2;
2021-10-04 22:45:37 -03:00
}
}
attackerAbility = GetBattlerAbility(battlerAtk);
GET_MOVE_TYPE(move, moveType);
if ((gFieldStatuses & STATUS_FIELD_ION_DELUGE && moveType == TYPE_NORMAL)
2021-10-10 01:13:23 -03:00
|| 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
2019-05-20 12:03:00 +02:00
&& 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;
}
Convert move flags and bans into GCC bitfields (#2952) * Slicing moves to new bitfield * Wind moves to new bitfield * Two-strike moves to new bitfield * Forgot to add flagTwoStrikes to battle_moves.h * Removed "flag" from field names * FLAG_HIT_IN_SUBSTITUTE and FLAG_THAW_USER * Airborne moves * FLAG_POWDER, FLAG_TARGET_ABILITY_IGNORED and FLAG_DANCE * FLAG_BALLISTIC and FLAG_PROTECTION_MOVE * Fixed missing uses of MOVE_UNAVAILABLE in battle_ai_util.c * FLAG_SOUND * FLAG_DMG_UNDERGROUND and FLAG_DMG_UNDERWATER * FLAG_DMG_MINIMIZE * Cleanup * FLAG_STAT_STAGES_IGNORED * Updated Pollen Puff's ballistic flag * FLAG_STRONG_JAW_BOOST and FLAG_MEGA_LAUNCHER_BOOST * thaw * FLAG_THREE_STRIKES * FLAG_IRON_FIST_BOOST * FLAG_RECKLESS_BOOST * FLAG_HIGH_CRIT * Removed empty flags * Moves that fail when called by Me First + added missing Shell Trap * Moves that fail when Gravity is active * Better names for banned fields * Moves that fail when called by Instruct * Cleanup * Contact Moves + Fixed Wandering Spirit skipping contact checks * Inverted FLAG_PROTECT_AFFECTED so that there's a flag for moves that SKIP protect. * Simplified B_MOVE_FLAGS configs * FORBIDDEN_METRONOME * Renamed hitsPastSubstitute to ignoresSubstitute * FORBIDDEN_PARENTAL_BOND * Struggle uncallable by Metronome * FORBIDDEN_MIMIC * FLAG_KINGS_ROCK_AFFECTED * Made a single config for move flags * Macro for checking move flags * FLAG_MAGIC_COAT_AFFECTED * Fixed HasMagicCoatAffectedMove * FLAG_SNATCH_AFFECTED * Removed unused EFFECT_FLINCH_MINIMIZE_HIT * Fixed Stench/King's Rock interaction * Removed sMovesNotAffectedByStench in favor of checking move effects * Removed EFFECT_TWISTER, which was a repeat of EFFECT_FLINCH_HIT * Changed Gen2 configs to less than Gen 3 * FORBIDDEN_SLEEP_TALK * Cleanup * Inverted FLAG_MIRROR_MOVE_AFFECTED * FLAG_SHEER_FORCE_BOOST * Ordered * FORBIDDEN_ASSIST and FORBIDDEN_COPYCAT * Removed TestMoveFlags and TestMoveFlagsInMoveset + flags field * Fixed Triple Arrows test
2023-07-03 04:01:59 -04:00
else if (gBattleMoves[move].soundMove && attackerAbility == ABILITY_LIQUID_VOICE)
{
gBattleStruct->dynamicMoveType = TYPE_WATER | F_DYNAMIC_TYPE_2;
}
2021-10-17 12:27:17 -03:00
else if (gStatuses4[battlerAtk] & STATUS4_PLASMA_FISTS && moveType == TYPE_NORMAL)
{
gBattleStruct->dynamicMoveType = TYPE_ELECTRIC | F_DYNAMIC_TYPE_2;
2021-10-17 12:27:17 -03:00
}
else if (move == MOVE_AURA_WHEEL && gBattleMons[battlerAtk].species == SPECIES_MORPEKO_HANGRY)
{
gBattleStruct->dynamicMoveType = TYPE_DARK | F_DYNAMIC_TYPE_2;
}
2019-05-07 15:26:58 +02:00
// Check if a gem should activate.
GET_MOVE_TYPE(move, moveType);
2021-05-24 09:52:45 -06:00
if (holdEffect == HOLD_EFFECT_GEMS
2019-05-07 15:26:58 +02:00
&& moveType == ItemId_GetSecondaryId(gBattleMons[battlerAtk].item))
{
gSpecialStatuses[battlerAtk].gemParam = GetBattlerHoldEffectParam(battlerAtk);
gSpecialStatuses[battlerAtk].gemBoost = TRUE;
2019-05-07 15:26:58 +02:00
}
}
2020-11-19 10:35:37 -07:00
// special to set a field's totem boost(s)
// inputs:
2023-08-30 10:18:31 +02:00
// var8000: battler
2020-11-19 10:35:37 -07:00
// var8001 - var8007: stat changes
void SetTotemBoost(void)
{
2023-09-14 11:05:00 +02:00
u32 battler = gSpecialVar_0x8000;
u32 i;
2020-11-19 10:35:37 -07:00
for (i = 0; i < (NUM_BATTLE_STATS - 1); i++)
{
if (*(&gSpecialVar_0x8001 + i))
{
2023-08-30 10:18:31 +02:00
gTotemBoosts[battler].stats |= (1 << i);
gTotemBoosts[battler].statChanges[i] = *(&gSpecialVar_0x8001 + i);
gTotemBoosts[battler].stats |= 0x80; // used as a flag for the "totem flared to life" script
2020-11-19 10:35:37 -07:00
}
}
}
bool32 IsWildMonSmart(void)
{
#if B_SMART_WILD_AI_FLAG != 0
return (FlagGet(B_SMART_WILD_AI_FLAG));
#else
return FALSE;
#endif
}