diff options
author | DizzyEggg <jajkodizzy@wp.pl> | 2017-11-18 21:45:52 +0100 |
---|---|---|
committer | DizzyEggg <jajkodizzy@wp.pl> | 2017-11-18 21:45:52 +0100 |
commit | 3de416662b5a83d96cbe010462971ed2c246d70d (patch) | |
tree | 7070ce944c7ef2d46cde333faec6a9930d1399c2 /src | |
parent | f8b40754c70679bb3ec4c0611613a0658f679225 (diff) | |
parent | 5dc8beb034e70b4e1eba61cca4009d5e06356605 (diff) |
wild encounter, fix merge conflicts
Diffstat (limited to 'src')
-rw-r--r-- | src/battle_2.c | 5 | ||||
-rw-r--r-- | src/battle_controller_recorded_opponent.c | 2 | ||||
-rw-r--r-- | src/battle_controller_recorded_player.c | 2 | ||||
-rw-r--r-- | src/battle_controllers.c | 2 | ||||
-rw-r--r-- | src/battle_message.c | 4 | ||||
-rwxr-xr-x | src/braille_puzzles.c | 6 | ||||
-rw-r--r-- | src/daycare.c | 1318 | ||||
-rw-r--r-- | src/decoration.c | 42 | ||||
-rw-r--r-- | src/egg_hatch.c | 18 | ||||
-rwxr-xr-x | src/pokemon_summary_screen.c | 17 | ||||
-rw-r--r-- | src/recorded_battle.c | 1676 | ||||
-rw-r--r-- | src/save.c | 3 | ||||
-rw-r--r-- | src/secret_base.c | 8 |
13 files changed, 3048 insertions, 55 deletions
diff --git a/src/battle_2.c b/src/battle_2.c index 798fabf4b..2be6f9d77 100644 --- a/src/battle_2.c +++ b/src/battle_2.c @@ -142,7 +142,6 @@ extern u8 gLastUsedAbility; extern u8 gUnknown_0203CF00[]; extern const u8* gBattlescriptPtrsForSelection[BATTLE_BANKS_COUNT]; extern const u8* gBattlescriptCurrInstr; -extern u32 gBattlePalaceMoveSelectionRngValue; extern u8 gActionsByTurnOrder[BATTLE_BANKS_COUNT]; extern u8 gCurrentTurnActionNumber; extern u16 gDynamicBasePower; @@ -2803,7 +2802,7 @@ static void BattleStartClearSetData(void) if (!(gBattleTypeFlags & BATTLE_TYPE_LINK) && gSaveBlock2Ptr->optionsBattleSceneOff == TRUE) gHitMarker |= HITMARKER_NO_ANIMATIONS; } - else if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_x2000000)) && sub_8185FB8()) + else if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_x2000000)) && GetBattleStyleInRecordedBattle()) gHitMarker |= HITMARKER_NO_ANIMATIONS; gBattleScripting.battleStyle = gSaveBlock2Ptr->optionsBattleStyle; @@ -3920,7 +3919,7 @@ static void HandleTurnActionSelectionState(void) switch (gBattleCommunication[gActiveBank]) { case STATE_TURN_START_RECORD: // recorded battle related on start of every turn - sub_8185FD0(); + RecordedBattle_CopyBankMoves(); gBattleCommunication[gActiveBank] = STATE_BEFORE_ACTION_CHOSEN; break; case STATE_BEFORE_ACTION_CHOSEN: // choose an action diff --git a/src/battle_controller_recorded_opponent.c b/src/battle_controller_recorded_opponent.c index 506ab46c7..b55d644ac 100644 --- a/src/battle_controller_recorded_opponent.c +++ b/src/battle_controller_recorded_opponent.c @@ -1261,7 +1261,7 @@ static void RecordedOpponentHandleDrawTrainerPic(void) } else { - trainerPicId = PlayerGenderToFrontTrainerPicId(sub_8185F40()); + trainerPicId = PlayerGenderToFrontTrainerPicId(GetActiveBankLinkPlayerGender()); } } else diff --git a/src/battle_controller_recorded_player.c b/src/battle_controller_recorded_player.c index 03a65961b..78e7497a5 100644 --- a/src/battle_controller_recorded_player.c +++ b/src/battle_controller_recorded_player.c @@ -1229,7 +1229,7 @@ static void RecordedPlayerHandleDrawTrainerPic(void) if (gBattleTypeFlags & BATTLE_TYPE_x2000000) { if (gBattleTypeFlags & BATTLE_TYPE_MULTI) - trainerPicId = sub_8185F40(); + trainerPicId = GetActiveBankLinkPlayerGender(); else trainerPicId = gLinkPlayers[gUnknown_0203C7B4].gender; } diff --git a/src/battle_controllers.c b/src/battle_controllers.c index 5c874438e..858540a9f 100644 --- a/src/battle_controllers.c +++ b/src/battle_controllers.c @@ -113,7 +113,7 @@ void sub_8032768(void) sub_8184DA4(2); if (!(gBattleTypeFlags & BATTLE_TYPE_RECORDED)) - sub_8185EB8(); + RecordedBattle_SaveParties(); if (gBattleTypeFlags & BATTLE_TYPE_LINK) SetControllersVariablesInLinkBattle(); diff --git a/src/battle_message.c b/src/battle_message.c index 4bd6ca5b7..9c37cf646 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -58,7 +58,7 @@ extern const u8* GetTrainer1LoseText(void); // battle_setup extern const u8* GetTrainer2LoseText(void); // battle_setup extern void GetFrontierTrainerName(u8 *dst, u16 trainerId); extern s32 GetStringCenterAlignXOffsetWithLetterSpacing(u8 fontId, const u8 *str, s32 totalWidth, s16 letterSpacing); -extern u8 sub_8185FC4(void); +extern u8 GetTextSpeedInRecordedBattle(void); extern u8 sav2_get_text_speed(void); // this file's functions @@ -2264,7 +2264,7 @@ void BattleHandleAddTextPrinter(const u8 *text, u8 arg1) if (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_x2000000)) speed = 1; else if (gBattleTypeFlags & BATTLE_TYPE_RECORDED) - speed = sRecordedBattleTextSpeeds[sub_8185FC4()]; + speed = sRecordedBattleTextSpeeds[GetTextSpeedInRecordedBattle()]; else speed = sav2_get_text_speed(); diff --git a/src/braille_puzzles.c b/src/braille_puzzles.c index c8055470a..85b2bc509 100755 --- a/src/braille_puzzles.c +++ b/src/braille_puzzles.c @@ -13,7 +13,7 @@ extern void DrawWholeMapView(); // field_camera extern void SetCameraPanningCallback(void ( *callback)()); // field_camera extern void InstallCameraPanAheadCallback(void); extern void SetCameraPanning(s16 x, s16 y); -extern u8 brm_get_pokemon_selection(void); +extern u8 GetCursorSelectionMonId(void); extern void FieldEffectActiveListRemove(u8 id); // field_effect extern u8 oei_task_add(void); @@ -220,7 +220,7 @@ bool8 ShouldDoBrailleStrengthEffect(void) void sub_8179834(void) { - gFieldEffectArguments[0] = brm_get_pokemon_selection(); + gFieldEffectArguments[0] = GetCursorSelectionMonId(); FieldEffectStart(FLDEFF_USE_FLY_ANCIENT_TOMB); } @@ -256,7 +256,7 @@ bool8 ShouldDoBrailleFlyEffect(void) void sub_8179918(void) { - gFieldEffectArguments[0] = brm_get_pokemon_selection(); + gFieldEffectArguments[0] = GetCursorSelectionMonId(); FieldEffectStart(FLDEFF_USE_FLY_ANCIENT_TOMB); } diff --git a/src/daycare.c b/src/daycare.c new file mode 100644 index 000000000..8dd45863a --- /dev/null +++ b/src/daycare.c @@ -0,0 +1,1318 @@ +#include "global.h" +#include "pokemon.h" +#include "daycare.h" +#include "string_util.h" +#include "species.h" +#include "items.h" +#include "mail.h" +#include "pokemon_storage_system.h" +#include "event_data.h" +#include "rng.h" +#include "main.h" +#include "moves.h" +#include "egg_hatch.h" +#include "text.h" +#include "menu.h" +#include "international_string_util.h" +#include "script.h" +#include "task.h" +#include "window.h" +#include "list_menu.h" + +#define EGG_MOVES_ARRAY_COUNT 10 +#define EGG_LVL_UP_MOVES_ARRAY_COUNT 50 + +extern u16 gMoveToLearn; + +// text +extern const u8 gText_MaleSymbol4[]; +extern const u8 gText_FemaleSymbol4[]; +extern const u8 gText_GenderlessSymbol[]; +extern const u8 gText_NewLine2[]; +extern const u8 gText_Exit4[]; +extern const u8 gText_Lv[]; +extern const u8 gExpandedPlaceholder_Empty[]; +extern const u8 gText_Exit[]; +extern const u8 gDaycareText_GetAlongVeryWell[]; +extern const u8 gDaycareText_GetAlong[]; +extern const u8 gDaycareText_DontLikeOther[]; +extern const u8 gDaycareText_PlayOther[]; + +extern u8 GetCursorSelectionMonId(void); +extern u16 ItemIdToBattleMoveId(u16); +extern s32 ListMenuHandleInput(u8); +extern void sub_81AE6C8(u8, u16*, u16*); +extern void sub_819746C(u8, bool8); +extern void sub_81973FC(u8, bool8); +extern void sub_81B9328(void); +extern void sub_81AF078(u32, bool8, struct ListMenu *); +extern void c2_exit_to_overworld_2_switch(void); + +// this file's functions +static void ClearDaycareMonMisc(struct DaycareMiscMon *misc); +static void SetInitialEggData(struct Pokemon *mon, u16 species, struct DayCare *daycare); +static u8 GetDaycareCompatibilityScore(struct DayCare *daycare); +static void DaycarePrintMonInfo(u8 windowId, s32 daycareSlotId, u8 y); + +// RAM buffers used to assist with BuildEggMoveset() +EWRAM_DATA static u16 sHatchedEggLevelUpMoves[EGG_LVL_UP_MOVES_ARRAY_COUNT] = {0}; +EWRAM_DATA static u16 sHatchedEggFatherMoves[4] = {0}; +EWRAM_DATA static u16 sHatchedEggFinalMoves[4] = {0}; +EWRAM_DATA static u16 sHatchedEggEggMoves[EGG_MOVES_ARRAY_COUNT] = {0}; +EWRAM_DATA static u16 sHatchedEggMotherMoves[4] = {0}; + +#include "data/pokemon/egg_moves.h" + +static const struct WindowTemplate sDaycareLevelMenuWindowTemplate = {0, 0xF, 1, 0xE, 6, 0xF, 8}; + +static const struct ListMenuItem sLevelMenuItems[] = +{ + {gExpandedPlaceholder_Empty, 0}, + {gExpandedPlaceholder_Empty, 1}, + {gText_Exit, 5} +}; + +static const struct ListMenuTemplate sDaycareListMenuLevelTemplate = +{ + .items = sLevelMenuItems, + .unk_04 = sub_81AF078, + .unk_08 = DaycarePrintMonInfo, + .totalItems = 3, + .maxShowed = 3, + .unk_10 = 0, + .unk_11 = 0, + .unk_12 = 8, + .cursor_Y = 0, + .upText_Y = 1, + .cursorColor = 2, + .fillColor = 1, + .cursorShadowColor = 3, + .unk_16_0 = TRUE, + .spaceBetweenItems = 0, + .unk_16_7 = FALSE, + .unk_17_0 = 1 +}; + +static const u8 *const sCompatibilityMessages[] = +{ + gDaycareText_GetAlongVeryWell, + gDaycareText_GetAlong, + gDaycareText_DontLikeOther, + gDaycareText_PlayOther +}; + +static const u8 sJapaneseEggNickname[] = _("タマゴ"); // "tamago" ("egg" in Japanese) + +u8 *GetMonNick(struct Pokemon *mon, u8 *dest) +{ + u8 nickname[POKEMON_NAME_LENGTH * 2]; + + GetMonData(mon, MON_DATA_NICKNAME, nickname); + return StringCopy10(dest, nickname); +} + +u8 *GetBoxMonNick(struct BoxPokemon *mon, u8 *dest) +{ + u8 nickname[POKEMON_NAME_LENGTH * 2]; + + GetBoxMonData(mon, MON_DATA_NICKNAME, nickname); + return StringCopy10(dest, nickname); +} + +u8 CountPokemonInDaycare(struct DayCare *daycare) +{ + u8 i, count; + count = 0; + + for (i = 0; i < DAYCARE_MON_COUNT; i++) + { + if (GetBoxMonData(&daycare->mons[i].mon, MON_DATA_SPECIES) != 0) + count++; + } + + return count; +} + +void InitDaycareMailRecordMixing(struct DayCare *daycare, struct RecordMixingDayCareMail *daycareMail) +{ + u8 i; + u8 numDaycareMons = 0; + + for (i = 0; i < DAYCARE_MON_COUNT; i++) + { + if (GetBoxMonData(&daycare->mons[i].mon, MON_DATA_SPECIES) != SPECIES_NONE) + { + numDaycareMons++; + if (GetBoxMonData(&daycare->mons[i].mon, MON_DATA_HELD_ITEM) == ITEM_NONE) + { + daycareMail->holdsItem[i] = FALSE; + } + else + { + daycareMail->holdsItem[i] = TRUE; + } + } + else + { + daycareMail->holdsItem[i] = TRUE; + } + } + + daycareMail->numDaycareMons = numDaycareMons; +} + +static s8 Daycare_FindEmptySpot(struct DayCare *daycare) +{ + u8 i; + + for (i = 0; i < DAYCARE_MON_COUNT; i++) + { + if (GetBoxMonData(&daycare->mons[i].mon, MON_DATA_SPECIES) == 0) + return i; + } + + return -1; +} + +static void StorePokemonInDaycare(struct Pokemon *mon, struct DaycareMon *daycareMon) +{ + if (MonHasMail(mon)) + { + u8 mailId; + + StringCopy(daycareMon->misc.OT_name, gSaveBlock2Ptr->playerName); + GetMonNick(mon, daycareMon->misc.monName); + StripExtCtrlCodes(daycareMon->misc.monName); + daycareMon->misc.gameLanguage = LANGUAGE_ENGLISH; + daycareMon->misc.monLanguage = GetMonData(mon, MON_DATA_LANGUAGE); + mailId = GetMonData(mon, MON_DATA_MAIL); + daycareMon->misc.mail = gSaveBlock1Ptr->mail[mailId]; + TakeMailFromMon(mon); + } + + daycareMon->mon = mon->box; + BoxMonRestorePP(&daycareMon->mon); + daycareMon->steps = 0; + ZeroMonData(mon); + CompactPartySlots(); + CalculatePlayerPartyCount(); +} + +static void StorePokemonInEmptyDaycareSlot(struct Pokemon *mon, struct DayCare *daycare) +{ + s8 slotId = Daycare_FindEmptySpot(daycare); + StorePokemonInDaycare(mon, &daycare->mons[slotId]); +} + +void StoreSelectedPokemonInDaycare(void) +{ + u8 monId = GetCursorSelectionMonId(); + StorePokemonInEmptyDaycareSlot(&gPlayerParty[monId], &gSaveBlock1Ptr->daycare); +} + +// Shifts the second daycare pokemon slot into the first slot. +static void ShiftDaycareSlots(struct DayCare *daycare) +{ + // This condition is only satisfied when the player takes out the first pokemon from the daycare. + if (GetBoxMonData(&daycare->mons[1].mon, MON_DATA_SPECIES) != 0 + && GetBoxMonData(&daycare->mons[0].mon, MON_DATA_SPECIES) == 0) + { + daycare->mons[0].mon = daycare->mons[1].mon; + ZeroBoxMonData(&daycare->mons[1].mon); + + daycare->mons[0].misc = daycare->mons[1].misc; + daycare->mons[0].steps = daycare->mons[1].steps; + daycare->mons[1].steps = 0; + ClearDaycareMonMisc(&daycare->mons[1].misc); + } +} + +static void ApplyDaycareExperience(struct Pokemon *mon) +{ + s32 i; + bool8 firstMove; + u16 learnedMove; + + for (i = 0; i < MAX_MON_LEVEL; i++) + { + // Add the mon's gained daycare experience level by level until it can't level up anymore. + if (TryIncrementMonLevel(mon)) + { + // Teach the mon new moves it learned while in the daycare. + firstMove = TRUE; + while ((learnedMove = MonTryLearningNewMove(mon, firstMove)) != 0) + { + firstMove = FALSE; + if (learnedMove == 0xFFFF) + { + // Mon already knows 4 moves. + DeleteFirstMoveAndGiveMoveToMon(mon, gMoveToLearn); + } + } + } + else + { + break; + } + } + + // Re-calculate the mons stats at its new level. + CalculateMonStats(mon); +} + +static u16 TakeSelectedPokemonFromDaycare(struct DaycareMon *daycareMon) +{ + u16 species; + u32 experience; + struct Pokemon pokemon; + + GetBoxMonNick(&daycareMon->mon, gStringVar1); + species = GetBoxMonData(&daycareMon->mon, MON_DATA_SPECIES); + BoxMonToMon(&daycareMon->mon, &pokemon); + + if (GetMonData(&pokemon, MON_DATA_LEVEL) != MAX_MON_LEVEL) + { + experience = GetMonData(&pokemon, MON_DATA_EXP) + daycareMon->steps; + SetMonData(&pokemon, MON_DATA_EXP, &experience); + ApplyDaycareExperience(&pokemon); + } + + gPlayerParty[PARTY_SIZE - 1] = pokemon; + if (daycareMon->misc.mail.itemId) + { + GiveMailToMon2(&gPlayerParty[PARTY_SIZE - 1], &daycareMon->misc.mail); + ClearDaycareMonMisc(&daycareMon->misc); + } + + ZeroBoxMonData(&daycareMon->mon); + daycareMon->steps = 0; + CompactPartySlots(); + CalculatePlayerPartyCount(); + return species; +} + +static u16 TakeSelectedPokemonMonFromDaycareShiftSlots(struct DayCare *daycare, u8 slotId) +{ + u16 species = TakeSelectedPokemonFromDaycare(&daycare->mons[slotId]); + ShiftDaycareSlots(daycare); + return species; +} + +u16 TakePokemonFromDaycare(void) +{ + return TakeSelectedPokemonMonFromDaycareShiftSlots(&gSaveBlock1Ptr->daycare, gSpecialVar_0x8004); +} + +static u8 GetLevelAfterDaycareSteps(struct BoxPokemon *mon, u32 steps) +{ + struct BoxPokemon tempMon = *mon; + + u32 experience = GetBoxMonData(mon, MON_DATA_EXP) + steps; + SetBoxMonData(&tempMon, MON_DATA_EXP, &experience); + return GetLevelFromBoxMonExp(&tempMon); +} + +static u8 GetNumLevelsGainedFromSteps(struct DaycareMon *daycareMon) +{ + u8 levelBefore; + u8 levelAfter; + + levelBefore = GetLevelFromBoxMonExp(&daycareMon->mon); + levelAfter = GetLevelAfterDaycareSteps(&daycareMon->mon, daycareMon->steps); + return levelAfter - levelBefore; +} + +static u8 GetNumLevelsGainedForDaycareMon(struct DaycareMon *daycareMon) +{ + u8 numLevelsGained = GetNumLevelsGainedFromSteps(daycareMon); + ConvertIntToDecimalStringN(gStringVar2, numLevelsGained, STR_CONV_MODE_LEFT_ALIGN, 2); + GetBoxMonNick(&daycareMon->mon, gStringVar1); + return numLevelsGained; +} + +static u32 GetDaycareCostForSelectedMon(struct DaycareMon *daycareMon) +{ + u32 cost; + + u8 numLevelsGained = GetNumLevelsGainedFromSteps(daycareMon); + GetBoxMonNick(&daycareMon->mon, gStringVar1); + cost = 100 + 100 * numLevelsGained; + ConvertIntToDecimalStringN(gStringVar2, cost, STR_CONV_MODE_LEFT_ALIGN, 5); + return cost; +} + +static u16 GetDaycareCostForMon(struct DayCare *daycare, u8 slotId) +{ + return GetDaycareCostForSelectedMon(&daycare->mons[slotId]); +} + +void GetDaycareCost(void) +{ + gSpecialVar_0x8005 = GetDaycareCostForMon(&gSaveBlock1Ptr->daycare, gSpecialVar_0x8004); +} + +static void Debug_AddDaycareSteps(u16 numSteps) +{ + gSaveBlock1Ptr->daycare.mons[0].steps += numSteps; + gSaveBlock1Ptr->daycare.mons[1].steps += numSteps; +} + +u8 GetNumLevelsGainedFromDaycare(void) +{ + if (GetBoxMonData(&gSaveBlock1Ptr->daycare.mons[gSpecialVar_0x8004], MON_DATA_SPECIES) != 0) + return GetNumLevelsGainedForDaycareMon(&gSaveBlock1Ptr->daycare.mons[gSpecialVar_0x8004]); + + return 0; +} + +static void ClearDaycareMonMisc(struct DaycareMiscMon *misc) +{ + s32 i; + + for (i = 0; i < OT_NAME_LENGTH + 1; i++) + misc->OT_name[i] = 0; + for (i = 0; i < POKEMON_NAME_LENGTH + 1; i++) + misc->monName[i] = 0; + + ClearMailStruct(&misc->mail); +} + +static void ClearDaycareMon(struct DaycareMon *daycareMon) +{ + ZeroBoxMonData(&daycareMon->mon); + daycareMon->steps = 0; + ClearDaycareMonMisc(&daycareMon->misc); +} + +static void ClearAllDaycareData(struct DayCare *daycare) +{ + u8 i; + + for (i = 0; i < DAYCARE_MON_COUNT; i++) + ClearDaycareMon(&daycare->mons[i]); + + daycare->offspringPersonality = 0; + daycare->stepCounter = 0; +} + +// Determines what the species of an Egg would be based on the given species. +// It determines this by working backwards through the evolution chain of the +// given species. +static u16 GetEggSpecies(u16 species) +{ + int i, j, k; + bool8 found; + + // Working backwards up to 5 times seems arbitrary, since the maximum number + // of times would only be 3 for 3-stage evolutions. + for (i = 0; i < EVOS_PER_MON; i++) + { + found = FALSE; + for (j = 1; j < NUM_SPECIES; j++) + { + for (k = 0; k < EVOS_PER_MON; k++) + { + if (gEvolutionTable[j].evolutions[k].targetSpecies == species) + { + species = j; + found = TRUE; + break; + } + } + + if (found) + break; + } + + if (j == NUM_SPECIES) + break; + } + + return species; +} + +static s32 GetSlotToInheritNature(struct DayCare *daycare) +{ + u32 species[DAYCARE_MON_COUNT]; + s32 i; + s32 dittoCount; + s32 slot = -1; + + // search for female gender + for (i = 0; i < DAYCARE_MON_COUNT; i++) + { + if (GetBoxMonGender(&daycare->mons[i].mon) == MON_FEMALE) + slot = i; + } + + // search for ditto + for (dittoCount = 0, i = 0; i < DAYCARE_MON_COUNT; i++) + { + species[i] = GetBoxMonData(&daycare->mons[i].mon, MON_DATA_SPECIES); + if (species[i] == SPECIES_DITTO) + dittoCount++, slot = i; + } + + // coin flip on ...two Dittos + if (dittoCount == 2) + { + if (Random() >= USHRT_MAX / 2) + slot = 0; + else + slot = 1; + } + + // nature inheritance only if holds everstone + if (GetBoxMonData(&daycare->mons[slot].mon, MON_DATA_HELD_ITEM) != ITEM_EVERSTONE + || Random() >= USHRT_MAX / 2) + { + return -1; + } + + return slot; +} + +static void _TriggerPendingDaycareEgg(struct DayCare *daycare) +{ + s32 natureSlot; + s32 natureTries = 0; + + SeedRng2(gMain.vblankCounter2); + natureSlot = GetSlotToInheritNature(daycare); + + if (natureSlot < 0) + { + daycare->offspringPersonality = (Random2() << 0x10) | ((Random() % 0xfffe) + 1); + } + else + { + u8 wantedNature = GetNatureFromPersonality(GetBoxMonData(&daycare->mons[natureSlot].mon, MON_DATA_PERSONALITY, NULL)); + u32 personality; + + do + { + personality = (Random2() << 0x10) | (Random()); + if (wantedNature == GetNatureFromPersonality(personality) && personality != 0) + break; // we found a personality with the same nature + + natureTries++; + } while (natureTries <= 2400); + + daycare->offspringPersonality = personality; + } + + FlagSet(FLAG_PENDING_DAYCARE_EGG); +} + +static void _TriggerPendingDaycareMaleEgg(struct DayCare *daycare) +{ + daycare->offspringPersonality = (Random()) | (0x8000); + FlagSet(FLAG_PENDING_DAYCARE_EGG); +} + +void TriggerPendingDaycareEgg(void) +{ + _TriggerPendingDaycareEgg(&gSaveBlock1Ptr->daycare); +} + +static void TriggerPendingDaycareMaleEgg(void) +{ + _TriggerPendingDaycareMaleEgg(&gSaveBlock1Ptr->daycare); +} + +// Removes the selected index from the given IV list and shifts the remaining +// elements to the left. +static void RemoveIVIndexFromList(u8 *ivs, u8 selectedIv) +{ + s32 i, j; + u8 temp[NUM_STATS]; + + ivs[selectedIv] = 0xff; + for (i = 0; i < NUM_STATS; i++) + { + temp[i] = ivs[i]; + } + + j = 0; + for (i = 0; i < NUM_STATS; i++) + { + if (temp[i] != 0xff) + ivs[j++] = temp[i]; + } +} + +static void InheritIVs(struct Pokemon *egg, struct DayCare *daycare) +{ + u8 i; + u8 selectedIvs[3]; + u8 availableIVs[NUM_STATS]; + u8 whichParent[ARRAY_COUNT(selectedIvs)]; + u8 iv; + + // Initialize a list of IV indices. + for (i = 0; i < NUM_STATS; i++) + { + availableIVs[i] = i; + } + + // Select the 3 IVs that will be inherited. + for (i = 0; i < ARRAY_COUNT(selectedIvs); i++) + { + // Randomly pick an IV from the available list. + selectedIvs[i] = availableIVs[Random() % (NUM_STATS - i)]; + + // Remove the selected IV index from the available IV indices. + RemoveIVIndexFromList(availableIVs, i); + } + + // Determine which parent each of the selected IVs should inherit from. + for (i = 0; i < ARRAY_COUNT(selectedIvs); i++) + { + whichParent[i] = Random() % 2; + } + + // Set each of inherited IVs on the egg mon. + for (i = 0; i < ARRAY_COUNT(selectedIvs); i++) + { + switch (selectedIvs[i]) + { + case 0: + iv = GetBoxMonData(&daycare->mons[whichParent[i]].mon, MON_DATA_HP_IV); + SetMonData(egg, MON_DATA_HP_IV, &iv); + break; + case 1: + iv = GetBoxMonData(&daycare->mons[whichParent[i]].mon, MON_DATA_ATK_IV); + SetMonData(egg, MON_DATA_ATK_IV, &iv); + break; + case 2: + iv = GetBoxMonData(&daycare->mons[whichParent[i]].mon, MON_DATA_DEF_IV); + SetMonData(egg, MON_DATA_DEF_IV, &iv); + break; + case 3: + iv = GetBoxMonData(&daycare->mons[whichParent[i]].mon, MON_DATA_SPEED_IV); + SetMonData(egg, MON_DATA_SPEED_IV, &iv); + break; + case 4: + iv = GetBoxMonData(&daycare->mons[whichParent[i]].mon, MON_DATA_SPATK_IV); + SetMonData(egg, MON_DATA_SPATK_IV, &iv); + break; + case 5: + iv = GetBoxMonData(&daycare->mons[whichParent[i]].mon, MON_DATA_SPDEF_IV); + SetMonData(egg, MON_DATA_SPDEF_IV, &iv); + break; + } + } +} + +// Counts the number of egg moves a pokemon learns and stores the moves in +// the given array. +static u8 GetEggMoves(struct Pokemon *pokemon, u16 *eggMoves) +{ + u16 eggMoveIdx; + u16 numEggMoves; + u16 species; + u16 i; + + numEggMoves = 0; + eggMoveIdx = 0; + species = GetMonData(pokemon, MON_DATA_SPECIES); + for (i = 0; i < ARRAY_COUNT(gEggMoves) - 1; i++) + { + if (gEggMoves[i] == species + EGG_MOVES_SPECIES_OFFSET) + { + eggMoveIdx = i + 1; + break; + } + } + + for (i = 0; i < EGG_MOVES_ARRAY_COUNT; i++) + { + if (gEggMoves[eggMoveIdx + i] > EGG_MOVES_SPECIES_OFFSET) + { + // TODO: the curly braces around this if statement are required for a matching build. + break; + } + + eggMoves[i] = gEggMoves[eggMoveIdx + i]; + numEggMoves++; + } + + return numEggMoves; +} + +static void BuildEggMoveset(struct Pokemon *egg, struct BoxPokemon *father, struct BoxPokemon *mother) +{ + u16 numSharedParentMoves; + u32 numLevelUpMoves; + u16 numEggMoves; + u16 i, j; + + numSharedParentMoves = 0; + for (i = 0; i < 4; i++) + { + sHatchedEggMotherMoves[i] = 0; + sHatchedEggFatherMoves[i] = 0; + sHatchedEggFinalMoves[i] = 0; + } + for (i = 0; i < EGG_MOVES_ARRAY_COUNT; i++) + sHatchedEggEggMoves[i] = 0; + for (i = 0; i < EGG_LVL_UP_MOVES_ARRAY_COUNT; i++) + sHatchedEggLevelUpMoves[i] = 0; + + numLevelUpMoves = GetLevelUpMovesBySpecies(GetMonData(egg, MON_DATA_SPECIES), sHatchedEggLevelUpMoves); + for (i = 0; i < 4; i++) + { + sHatchedEggFatherMoves[i] = GetBoxMonData(father, MON_DATA_MOVE1 + i); + sHatchedEggMotherMoves[i] = GetBoxMonData(mother, MON_DATA_MOVE1 + i); + } + + numEggMoves = GetEggMoves(egg, sHatchedEggEggMoves); + + for (i = 0; i < 4; i++) + { + if (sHatchedEggFatherMoves[i] != MOVE_NONE) + { + for (j = 0; j < numEggMoves; j++) + { + if (sHatchedEggFatherMoves[i] == sHatchedEggEggMoves[j]) + { + if (GiveMoveToMon(egg, sHatchedEggFatherMoves[i]) == 0xffff) + DeleteFirstMoveAndGiveMoveToMon(egg, sHatchedEggFatherMoves[i]); + break; + } + } + } + else + { + break; + } + } + for (i = 0; i < 4; i++) + { + if (sHatchedEggFatherMoves[i] != MOVE_NONE) + { + for (j = 0; j < NUM_TECHNICAL_MACHINES + NUM_HIDDEN_MACHINES; j++) + { + if (sHatchedEggFatherMoves[i] == ItemIdToBattleMoveId(ITEM_TM01 + j) && CanMonLearnTMHM(egg, j)) + { + if (GiveMoveToMon(egg, sHatchedEggFatherMoves[i]) == 0xffff) + DeleteFirstMoveAndGiveMoveToMon(egg, sHatchedEggFatherMoves[i]); + } + } + } + } + for (i = 0; i < 4; i++) + { + if (sHatchedEggFatherMoves[i] == MOVE_NONE) + break; + for (j = 0; j < 4; j++) + { + if (sHatchedEggFatherMoves[i] == sHatchedEggMotherMoves[j] && sHatchedEggFatherMoves[i] != MOVE_NONE) + sHatchedEggFinalMoves[numSharedParentMoves++] = sHatchedEggFatherMoves[i]; + } + } + + for (i = 0; i < 4; i++) + { + if (sHatchedEggFinalMoves[i] == MOVE_NONE) + break; + for (j = 0; j < numLevelUpMoves; j++) + { + if (sHatchedEggLevelUpMoves[j] != MOVE_NONE && sHatchedEggFinalMoves[i] == sHatchedEggLevelUpMoves[j]) + { + if (GiveMoveToMon(egg, sHatchedEggFinalMoves[i]) == 0xffff) + DeleteFirstMoveAndGiveMoveToMon(egg, sHatchedEggFinalMoves[i]); + break; + } + } + } +} + +static void RemoveEggFromDayCare(struct DayCare *daycare) +{ + daycare->offspringPersonality = 0; + daycare->stepCounter = 0; +} + +void RejectEggFromDayCare(void) +{ + RemoveEggFromDayCare(&gSaveBlock1Ptr->daycare); +} + +static void AlterEggSpeciesWithIncenseItem(u16 *species, struct DayCare *daycare) +{ + u16 motherItem, fatherItem; + if (*species == SPECIES_WYNAUT || *species == SPECIES_AZURILL) + { + motherItem = GetBoxMonData(&daycare->mons[0].mon, MON_DATA_HELD_ITEM); + fatherItem = GetBoxMonData(&daycare->mons[1].mon, MON_DATA_HELD_ITEM); + if (*species == SPECIES_WYNAUT && motherItem != ITEM_LAX_INCENSE && fatherItem != ITEM_LAX_INCENSE) + { + *species = SPECIES_WOBBUFFET; + } + + if (*species == SPECIES_AZURILL && motherItem != ITEM_SEA_INCENSE && fatherItem != ITEM_SEA_INCENSE) + { + *species = SPECIES_MARILL; + } + } +} + +static void GiveVoltTackleIfLightBall(struct Pokemon *mon, struct DayCare *daycare) +{ + u32 motherItem = GetBoxMonData(&daycare->mons[0].mon, MON_DATA_HELD_ITEM); + u32 fatherItem = GetBoxMonData(&daycare->mons[1].mon, MON_DATA_HELD_ITEM); + + if (motherItem == ITEM_LIGHT_BALL || fatherItem == ITEM_LIGHT_BALL) + { + if (GiveMoveToMon(mon, MOVE_VOLT_TACKLE) == 0xFFFF) + DeleteFirstMoveAndGiveMoveToMon(mon, MOVE_VOLT_TACKLE); + } +} + +static u16 DetermineEggSpeciesAndParentSlots(struct DayCare *daycare, u8 *parentSlots) +{ + u16 i; + u16 species[2]; + u16 eggSpecies; + + // Determine which of the daycare mons is the mother and father of the egg. + // The 0th index of the parentSlots array is considered the mother slot, and the + // 1st index is the father slot. + for (i = 0; i < 2; i++) + { + species[i] = GetBoxMonData(&daycare->mons[i].mon, MON_DATA_SPECIES); + if (species[i] == SPECIES_DITTO) + { + parentSlots[0] = i ^ 1; + parentSlots[1] = i; + } + else if (GetBoxMonGender(&daycare->mons[i].mon) == MON_FEMALE) + { + parentSlots[0] = i; + parentSlots[1] = i ^ 1; + } + } + + eggSpecies = GetEggSpecies(species[parentSlots[0]]); + if (eggSpecies == SPECIES_NIDORAN_F && daycare->offspringPersonality & 0x8000) + { + eggSpecies = SPECIES_NIDORAN_M; + } + if (eggSpecies == SPECIES_ILLUMISE && daycare->offspringPersonality & 0x8000) + { + eggSpecies = SPECIES_VOLBEAT; + } + + // Make Ditto the "mother" slot if the other daycare mon is male. + if (species[parentSlots[1]] == SPECIES_DITTO && GetBoxMonGender(&daycare->mons[parentSlots[0]].mon) != MON_FEMALE) + { + u8 temp = parentSlots[1]; + parentSlots[1] = parentSlots[0]; + parentSlots[0] = temp; + } + + return eggSpecies; +} + +static void _GiveEggFromDaycare(struct DayCare *daycare) // give_egg +{ + struct Pokemon egg; + u16 species; + u8 parentSlots[2]; // 0th index is "mother" daycare slot, 1st is "father" + bool8 isEgg; + + species = DetermineEggSpeciesAndParentSlots(daycare, parentSlots); + AlterEggSpeciesWithIncenseItem(&species, daycare); + SetInitialEggData(&egg, species, daycare); + InheritIVs(&egg, daycare); + BuildEggMoveset(&egg, &daycare->mons[parentSlots[1]].mon, &daycare->mons[parentSlots[0]].mon); + + if (species == SPECIES_PICHU) + GiveVoltTackleIfLightBall(&egg, daycare); + + isEgg = TRUE; + SetMonData(&egg, MON_DATA_IS_EGG, &isEgg); + gPlayerParty[PARTY_SIZE - 1] = egg; + CompactPartySlots(); + CalculatePlayerPartyCount(); + RemoveEggFromDayCare(daycare); +} + +void CreateEgg(struct Pokemon *mon, u16 species, bool8 setHotSpringsLocation) +{ + u8 metLevel; + u16 ball; + u8 language; + u8 metLocation; + u8 isEgg; + + CreateMon(mon, species, EGG_HATCH_LEVEL, 0x20, FALSE, 0, FALSE, 0); + metLevel = 0; + ball = ITEM_POKE_BALL; + language = LANGUAGE_JAPANESE; + SetMonData(mon, MON_DATA_POKEBALL, &ball); + SetMonData(mon, MON_DATA_NICKNAME, sJapaneseEggNickname); + SetMonData(mon, MON_DATA_FRIENDSHIP, &gBaseStats[species].eggCycles); + SetMonData(mon, MON_DATA_MET_LEVEL, &metLevel); + SetMonData(mon, MON_DATA_LANGUAGE, &language); + if (setHotSpringsLocation) + { + metLocation = 253; // hot springs; see PokemonSummaryScreen_PrintEggTrainerMemo + SetMonData(mon, MON_DATA_MET_LOCATION, &metLocation); + } + + isEgg = TRUE; + SetMonData(mon, MON_DATA_IS_EGG, &isEgg); +} + +static void SetInitialEggData(struct Pokemon *mon, u16 species, struct DayCare *daycare) +{ + u32 personality; + u16 ball; + u8 metLevel; + u8 language; + + personality = daycare->offspringPersonality; + CreateMon(mon, species, EGG_HATCH_LEVEL, 0x20, TRUE, personality, FALSE, 0); + metLevel = 0; + ball = ITEM_POKE_BALL; + language = LANGUAGE_JAPANESE; + SetMonData(mon, MON_DATA_POKEBALL, &ball); + SetMonData(mon, MON_DATA_NICKNAME, sJapaneseEggNickname); + SetMonData(mon, MON_DATA_FRIENDSHIP, &gBaseStats[species].eggCycles); + SetMonData(mon, MON_DATA_MET_LEVEL, &metLevel); + SetMonData(mon, MON_DATA_LANGUAGE, &language); +} + +void GiveEggFromDaycare(void) +{ + _GiveEggFromDaycare(&gSaveBlock1Ptr->daycare); +} + +static bool8 _DoEggActions_CheckHatch(struct DayCare *daycare) +{ + u32 i, validEggs = 0; + + for (i = 0; i < DAYCARE_MON_COUNT; i++) + { + if (GetBoxMonData(&daycare->mons[i].mon, MON_DATA_SANITY_BIT2)) + daycare->mons[i].steps++, validEggs++; + } + + // try to trigger poke sex + if (daycare->offspringPersonality == 0 && validEggs == 2 && (daycare->mons[1].steps & 0xFF) == 0xFF) + { + u8 loveScore = GetDaycareCompatibilityScore(daycare); + if (loveScore > (Random() * 100u) / USHRT_MAX) + TriggerPendingDaycareEgg(); + } + + if (++daycare->stepCounter == 255) // hatch an egg + { + u32 steps; + u8 toSub = GetEggStepsToSubtract(); + + for (i = 0; i < gPlayerPartyCount; i++) + { + if (!GetMonData(&gPlayerParty[i], MON_DATA_IS_EGG)) + continue; + if (GetMonData(&gPlayerParty[i], MON_DATA_SANITY_BIT1)) + continue; + + steps = GetMonData(&gPlayerParty[i], MON_DATA_FRIENDSHIP); + if (steps != 0) // subtract needed steps + { + if (steps >= toSub) + steps -= toSub; + else + steps -= 1; + + SetMonData(&gPlayerParty[i], MON_DATA_FRIENDSHIP, &steps); + } + else // hatch the egg + { + gSpecialVar_0x8004 = i; + return TRUE; + } + } + } + + return FALSE; // no hatching +} + +bool8 DoEggActions_CheckHatch(void) +{ + return _DoEggActions_CheckHatch(&gSaveBlock1Ptr->daycare); +} + +static bool8 IsEggPending(struct DayCare *daycare) +{ + return (daycare->offspringPersonality != 0); +} + +// gStringVar1 = first mon's nickname +// gStringVar2 = second mon's nickname +// gStringVar3 = first mon trainer's name +static void _GetDaycareMonNicknames(struct DayCare *daycare) +{ + u8 text[12]; + if (GetBoxMonData(&daycare->mons[0].mon, MON_DATA_SPECIES) != 0) + { + GetBoxMonNick(&daycare->mons[0].mon, gStringVar1); + GetBoxMonData(&daycare->mons[0].mon, MON_DATA_OT_NAME, text); + StringCopy(gStringVar3, text); + } + + if (GetBoxMonData(&daycare->mons[1].mon, MON_DATA_SPECIES) != 0) + { + GetBoxMonNick(&daycare->mons[1].mon, gStringVar2); + } +} + +u16 GetSelectedMonNickAndSpecies(void) +{ + GetBoxMonNick(&gPlayerParty[GetCursorSelectionMonId()].box, gStringVar1); + return GetBoxMonData(&gPlayerParty[GetCursorSelectionMonId()].box, MON_DATA_SPECIES); +} + +void GetDaycareMonNicknames(void) +{ + _GetDaycareMonNicknames(&gSaveBlock1Ptr->daycare); +} + +u8 GetDaycareState(void) +{ + // The daycare can be in 4 possible states: + // 0: default state--no deposited mons, no egg + // 1: there is an egg waiting for the player to pick it up + // 2: there is a single pokemon in the daycare + // 3: there are two pokemon in the daycare, no egg + + u8 numMons; + if (IsEggPending(&gSaveBlock1Ptr->daycare)) + { + // There is an Egg waiting for the player. + return 1; + } + + numMons = CountPokemonInDaycare(&gSaveBlock1Ptr->daycare); + if (numMons != 0) + { + return numMons + 1; + } + + return 0; +} + +static u8 GetDaycarePokemonCount(void) +{ + u8 ret = CountPokemonInDaycare(&gSaveBlock1Ptr->daycare); + if (ret) + return ret; + + return 0; +} + +static bool8 EggGroupsOverlap(u16 *eggGroups1, u16 *eggGroups2) +{ + // Determine if the two given egg group lists contain any of the + // same egg groups. + s32 i, j; + + for (i = 0; i < 2; i++) + { + for (j = 0; j < 2; j++) + { + if (eggGroups1[i] == eggGroups2[j]) + return TRUE; + } + } + + return FALSE; +} + +static u8 GetDaycareCompatibilityScore(struct DayCare *daycare) +{ + u32 i; + u16 eggGroups[2][2]; + u16 species[2]; + u32 trainerIds[2]; + u32 genders[2]; + + for (i = 0; i < 2; i++) + { + u32 personality; + + species[i] = GetBoxMonData(&daycare->mons[i].mon, MON_DATA_SPECIES); + trainerIds[i] = GetBoxMonData(&daycare->mons[i].mon, MON_DATA_OT_ID); + personality = GetBoxMonData(&daycare->mons[i].mon, MON_DATA_PERSONALITY); + genders[i] = GetGenderFromSpeciesAndPersonality(species[i], personality); + eggGroups[i][0] = gBaseStats[species[i]].eggGroup1; + eggGroups[i][1] = gBaseStats[species[i]].eggGroup2; + } + + // check unbreedable egg group + if (eggGroups[0][0] == EGG_GROUP_UNDISCOVERED || eggGroups[1][0] == EGG_GROUP_UNDISCOVERED) + return 0; + // two Ditto can't breed + if (eggGroups[0][0] == EGG_GROUP_DITTO && eggGroups[1][0] == EGG_GROUP_DITTO) + return 0; + + // now that we checked, one ditto can breed with any other mon + if (eggGroups[0][0] == EGG_GROUP_DITTO || eggGroups[1][0] == EGG_GROUP_DITTO) + { + if (trainerIds[0] == trainerIds[1]) // same trainer + return 20; + + return 50; // different trainers, more chance of poke sex + } + else + { + if (genders[0] == genders[1]) // no homo + return 0; + if (genders[0] == MON_GENDERLESS || genders[1] == MON_GENDERLESS) + return 0; + if (!EggGroupsOverlap(eggGroups[0], eggGroups[1])) // not compatible with each other + return 0; + + if (species[0] == species[1]) // same species + { + if (trainerIds[0] == trainerIds[1]) // same species and trainer + return 50; + + return 70; // different trainers, same species + } + else + { + if (trainerIds[0] != trainerIds[1]) // different trainers, different species + return 50; + + return 20; // different species, same trainer + } + } +} + +static u8 GetDaycareCompatibilityScoreFromSave(void) +{ + return GetDaycareCompatibilityScore(&gSaveBlock1Ptr->daycare); +} + +void SetDaycareCompatibilityString(void) +{ + u8 whichString; + u8 relationshipScore; + + relationshipScore = GetDaycareCompatibilityScoreFromSave(); + whichString = 0; + if (relationshipScore == 0) + whichString = 3; + if (relationshipScore == 20) + whichString = 2; + if (relationshipScore == 50) + whichString = 1; + if (relationshipScore == 70) + whichString = 0; + + StringCopy(gStringVar4, sCompatibilityMessages[whichString]); +} + +bool8 NameHasGenderSymbol(const u8 *name, u8 genderRatio) +{ + u8 i; + u8 symbolsCount[2]; // male, female + symbolsCount[0] = symbolsCount[1] = 0; + + for (i = 0; name[i] != EOS; i++) + { + if (name[i] == CHAR_MALE) + symbolsCount[0]++; + if (name[i] == CHAR_FEMALE) + symbolsCount[1]++; + } + + if (genderRatio == MON_MALE && symbolsCount[0] != 0 && symbolsCount[1] == 0) + return TRUE; + if (genderRatio == MON_FEMALE && symbolsCount[1] != 0 && symbolsCount[0] == 0) + return TRUE; + + return FALSE; +} + +static u8 *AppendGenderSymbol(u8 *name, u8 gender) +{ + if (gender == MON_MALE) + { + if (!NameHasGenderSymbol(name, MON_MALE)) + return StringAppend(name, gText_MaleSymbol4); + } + else if (gender == MON_FEMALE) + { + if (!NameHasGenderSymbol(name, MON_FEMALE)) + return StringAppend(name, gText_FemaleSymbol4); + } + + return StringAppend(name, gText_GenderlessSymbol); +} + +static u8 *AppendMonGenderSymbol(u8 *name, struct BoxPokemon *boxMon) +{ + return AppendGenderSymbol(name, GetBoxMonGender(boxMon)); +} + +static void GetDaycareLevelMenuText(struct DayCare *daycare, u8 *dest) +{ + u8 monNames[2][20]; + u8 i; + + *dest = EOS; + for (i = 0; i < 2; i++) + { + GetBoxMonNick(&daycare->mons[i].mon, monNames[i]); + AppendMonGenderSymbol(monNames[i], &daycare->mons[i].mon); + } + + StringCopy(dest, monNames[0]); + StringAppend(dest, gText_NewLine2); + StringAppend(dest, monNames[1]); + StringAppend(dest, gText_NewLine2); + StringAppend(dest, gText_Exit4); +} + +static void GetDaycareLevelMenuLevelText(struct DayCare *daycare, u8 *dest) +{ + u8 i; + u8 level; + u8 text[20]; + + *dest = EOS; + for (i = 0; i < 2; i++) + { + StringAppend(dest, gText_Lv); + level = GetLevelAfterDaycareSteps(&daycare->mons[i].mon, daycare->mons[i].steps); + ConvertIntToDecimalStringN(text, level, STR_CONV_MODE_LEFT_ALIGN, 3); + StringAppend(dest, text); + StringAppend(dest, gText_NewLine2); + } +} + +static void DaycareAddTextPrinter(u8 windowId, const u8 *text, u32 x, u32 y) +{ + struct TextSubPrinter printer; + + printer.current_text_offset = text; + printer.windowId = windowId; + printer.fontId = 1; + printer.x = x; + printer.y = y; + printer.currentX = x; + printer.currentY = y; + printer.fontColor_l = 0; + gTextFlags.flag_1 = 0; + printer.letterSpacing = 0; + printer.lineSpacing = 1; + printer.fontColor_h = 2; + printer.bgColor = 1; + printer.shadowColor = 3; + + AddTextPrinter(&printer, 0xFF, NULL); +} + +static void DaycarePrintMonNick(struct DayCare *daycare, u8 windowId, u32 daycareSlotId, u32 y) +{ + u8 nick[POKEMON_NAME_LENGTH * 2]; + + GetBoxMonNick(&daycare->mons[daycareSlotId].mon, nick); + AppendMonGenderSymbol(nick, &daycare->mons[daycareSlotId].mon); + DaycareAddTextPrinter(windowId, nick, 8, y); +} + +static void DaycarePrintMonLvl(struct DayCare *daycare, u8 windowId, u32 daycareSlotId, u32 y) +{ + u8 level; + u32 x; + u8 lvlText[12]; + u8 intText[8]; + + StringCopy(lvlText, gText_Lv); + level = GetLevelAfterDaycareSteps(&daycare->mons[daycareSlotId].mon, daycare->mons[daycareSlotId].steps); + ConvertIntToDecimalStringN(intText, level, STR_CONV_MODE_LEFT_ALIGN, 3); + StringAppend(lvlText, intText); + x = GetStringRightAlignXOffset(1, lvlText, 112); + DaycareAddTextPrinter(windowId, lvlText, x, y); +} + +static void DaycarePrintMonInfo(u8 windowId, s32 daycareSlotId, u8 y) +{ + if (daycareSlotId < (unsigned) DAYCARE_MON_COUNT) + { + DaycarePrintMonNick(&gSaveBlock1Ptr->daycare, windowId, daycareSlotId, y); + DaycarePrintMonLvl(&gSaveBlock1Ptr->daycare, windowId, daycareSlotId, y); + } +} + +#define tMenuListTaskId data[0] +#define tWindowId data[1] + +static void Task_HandleDaycareLevelMenuInput(u8 taskId) +{ + u32 var = ListMenuHandleInput(gTasks[taskId].tMenuListTaskId); + + if (gMain.newKeys & A_BUTTON) + { + switch (var) + { + case 0: + case 1: + gSpecialVar_Result = var; + break; + case 5: + gSpecialVar_Result = 2; + break; + } + sub_81AE6C8(gTasks[taskId].tMenuListTaskId, NULL, NULL); + sub_819746C(gTasks[taskId].tWindowId, TRUE); + RemoveWindow(gTasks[taskId].tWindowId); + DestroyTask(taskId); + EnableBothScriptContexts(); + } + else if (gMain.newKeys & B_BUTTON) + { + gSpecialVar_Result = 2; + sub_81AE6C8(gTasks[taskId].tMenuListTaskId, NULL, NULL); + sub_819746C(gTasks[taskId].tWindowId, TRUE); + RemoveWindow(gTasks[taskId].tWindowId); + DestroyTask(taskId); + EnableBothScriptContexts(); + } +} + +void ShowDaycareLevelMenu(void) +{ + struct ListMenuTemplate menuTemplate; + u8 windowId; + u8 listMenuTaskId; + u8 daycareMenuTaskId; + + windowId = AddWindow(&sDaycareLevelMenuWindowTemplate); + sub_81973FC(windowId, FALSE); + + menuTemplate = sDaycareListMenuLevelTemplate; + menuTemplate.unk_10 = windowId; + listMenuTaskId = ListMenuInit(&menuTemplate, 0, 0); + + CopyWindowToVram(windowId, 3); + + daycareMenuTaskId = CreateTask(Task_HandleDaycareLevelMenuInput, 3); + gTasks[daycareMenuTaskId].tMenuListTaskId = listMenuTaskId; + gTasks[daycareMenuTaskId].tWindowId = windowId; +} + +#undef tMenuListTaskId +#undef tWindowId + +void ChooseSendDaycareMon(void) +{ + sub_81B9328(); + gMain.savedCallback = c2_exit_to_overworld_2_switch; +} diff --git a/src/decoration.c b/src/decoration.c index f622662d3..adcdc583b 100644 --- a/src/decoration.c +++ b/src/decoration.c @@ -1,5 +1,3 @@ - -// Includes #include "global.h" #include "decompress.h" #include "malloc.h" @@ -68,27 +66,27 @@ struct DecorRearrangementDataBuffer { // Static RAM declarations EWRAM_DATA u8 *gCurDecorInventoryItems = NULL; -EWRAM_DATA u8 sSecretBasePCMenuCursorPos = 0; -EWRAM_DATA u8 sCurDecorCatCount = 0; -EWRAM_DATA u8 sSecretBaseItemsIndicesBuffer[16] = {}; -EWRAM_DATA u8 sPlayerRoomItemsIndicesBuffer[12] = {}; -EWRAM_DATA u16 sSecretBasePCSelectDecorLineNo = 0; -EWRAM_DATA u16 sSecretBasePCSelectDecorPageNo = 0; +EWRAM_DATA static u8 sSecretBasePCMenuCursorPos = 0; +EWRAM_DATA static u8 sCurDecorCatCount = 0; +EWRAM_DATA static u8 sSecretBaseItemsIndicesBuffer[16] = {}; +EWRAM_DATA static u8 sPlayerRoomItemsIndicesBuffer[12] = {}; +EWRAM_DATA static u16 sSecretBasePCSelectDecorLineNo = 0; +EWRAM_DATA static u16 sSecretBasePCSelectDecorPageNo = 0; EWRAM_DATA u8 gCurDecorationIndex = 0; -EWRAM_DATA u8 sCurDecorationCategory = DECORCAT_DESK; -EWRAM_DATA u32 filler_0203a174[2] = {}; +EWRAM_DATA static u8 sCurDecorationCategory = DECORCAT_DESK; +EWRAM_DATA static u32 filler_0203a174[2] = {}; EWRAM_DATA struct DecorPCPointers gUnknown_0203A17C = {}; -EWRAM_DATA u8 sDecorMenuWindowIndices[4] = {}; +EWRAM_DATA static u8 sDecorMenuWindowIndices[4] = {}; EWRAM_DATA struct DecorPCBuffer *sDecorPCBuffer = NULL; EWRAM_DATA struct PlaceDecorationGraphicsDataBuffer sPlaceDecorationGraphicsDataBuffer = {}; -EWRAM_DATA u16 sCurDecorMapX = 0; -EWRAM_DATA u16 sCurDecorMapY = 0; -EWRAM_DATA u8 sDecor_CameraSpriteObjectIdx1 = 0; -EWRAM_DATA u8 sDecor_CameraSpriteObjectIdx2 = 0; -EWRAM_DATA u8 sDecorationLastDirectionMoved = 0; -EWRAM_DATA struct OamData sDecorSelectorOam = {}; -EWRAM_DATA struct DecorRearrangementDataBuffer sDecorRearrangementDataBuffer[16] = {}; -EWRAM_DATA u8 sCurDecorSelectedInRearrangement = 0; +EWRAM_DATA static u16 sCurDecorMapX = 0; +EWRAM_DATA static u16 sCurDecorMapY = 0; +EWRAM_DATA static u8 sDecor_CameraSpriteObjectIdx1 = 0; +EWRAM_DATA static u8 sDecor_CameraSpriteObjectIdx2 = 0; +EWRAM_DATA static u8 sDecorationLastDirectionMoved = 0; +EWRAM_DATA static struct OamData sDecorSelectorOam = {}; +EWRAM_DATA static struct DecorRearrangementDataBuffer sDecorRearrangementDataBuffer[16] = {}; +EWRAM_DATA static u8 sCurDecorSelectedInRearrangement = 0; // Static ROM declarations @@ -800,9 +798,9 @@ void sub_8127330(u8 taskId) sDecorPCBuffer->items[i].unk_04 = -2; gUnknown_03006310 = gUnknown_085A6BD0; gUnknown_03006310.unk_10 = sDecorMenuWindowIndices[1]; - gUnknown_03006310.unk_0c = sDecorPCBuffer->unk_520; - gUnknown_03006310.unk_00 = sDecorPCBuffer->items; - gUnknown_03006310.unk_0e = sDecorPCBuffer->unk_521; + gUnknown_03006310.totalItems = sDecorPCBuffer->unk_520; + gUnknown_03006310.items = sDecorPCBuffer->items; + gUnknown_03006310.maxShowed = sDecorPCBuffer->unk_521; } void sub_8127454(u8 *dest, u16 decorId) diff --git a/src/egg_hatch.c b/src/egg_hatch.c index 13c088883..a3f7c2ab0 100644 --- a/src/egg_hatch.c +++ b/src/egg_hatch.c @@ -1,5 +1,6 @@ #include "global.h" #include "pokemon.h" +#include "egg_hatch.h" #include "pokedex.h" #include "items.h" #include "script.h" @@ -23,6 +24,7 @@ #include "m4a.h" #include "window.h" #include "abilities.h" +#include "daycare.h" #include "battle.h" // to get rid of later struct EggHatchData @@ -327,7 +329,7 @@ static void CreatedHatchedMon(struct Pokemon *egg, struct Pokemon *temp) pokerus = GetMonData(egg, MON_DATA_POKERUS); obedience = GetMonData(egg, MON_DATA_OBEDIENCE); - CreateMon(temp, species, 5, 32, TRUE, personality, 0, 0); + CreateMon(temp, species, EGG_HATCH_LEVEL, 32, TRUE, personality, 0, 0); for (i = 0; i < 4; i++) { @@ -393,19 +395,19 @@ void ScriptHatchMon(void) AddHatchedMonToParty(gSpecialVar_0x8004); } -static bool8 sub_807158C(struct DaycareData* daycare, u8 daycareId) +static bool8 sub_807158C(struct DayCare *daycare, u8 daycareId) { u8 nick[0x20]; - struct DaycareMon* daycareMon = &daycare->mons[daycareId]; + struct DaycareMon *daycareMon = &daycare->mons[daycareId]; GetBoxMonNick(&daycareMon->mon, nick); - if (daycareMon->mail.itemId != 0 - && (StringCompareWithoutExtCtrlCodes(nick, daycareMon->monName) != 0 - || StringCompareWithoutExtCtrlCodes(gSaveBlock2Ptr->playerName, daycareMon->OT_name) != 0)) + if (daycareMon->misc.mail.itemId != 0 + && (StringCompareWithoutExtCtrlCodes(nick, daycareMon->misc.monName) != 0 + || StringCompareWithoutExtCtrlCodes(gSaveBlock2Ptr->playerName, daycareMon->misc.OT_name) != 0)) { StringCopy(gStringVar1, nick); - TVShowConvertInternationalString(gStringVar2, daycareMon->OT_name, daycareMon->language_maybe); - TVShowConvertInternationalString(gStringVar3, daycareMon->monName, daycareMon->unknown); + TVShowConvertInternationalString(gStringVar2, daycareMon->misc.OT_name, daycareMon->misc.gameLanguage); + TVShowConvertInternationalString(gStringVar3, daycareMon->misc.monName, daycareMon->misc.monLanguage); return TRUE; } return FALSE; diff --git a/src/pokemon_summary_screen.c b/src/pokemon_summary_screen.c index 838e91d90..d09cbc407 100755 --- a/src/pokemon_summary_screen.c +++ b/src/pokemon_summary_screen.c @@ -97,7 +97,6 @@ extern u8 gUnknown_08D97D0C; extern void reset_temp_tile_data_buffers(); extern void decompress_and_copy_tile_data_to_vram(u8 a, void* tiledata, u8 b, u8 c, u8 d); extern u8 free_temp_tile_data_buffers_if_possible(); -extern void sub_8069004(struct BoxPokemon* a, void* b); extern void sub_81C1E20(u8 taskId); extern u8 *GetMonNickname(struct Pokemon *mon, u8 *dest); extern u16 SpeciesToPokedexNum(u16 species); @@ -667,7 +666,7 @@ void sub_81C0098(struct Pokemon *mon) else { struct BoxPokemon *boxMon = gUnknown_0203CF1C->unk0->boxMon; - sub_8069004(&boxMon[gUnknown_0203CF1C->unk40BE], mon); + BoxMonToMon(&boxMon[gUnknown_0203CF1C->unk40BE], mon); } } @@ -1810,7 +1809,7 @@ void sub_81C171C(u8 taskId) void sub_81C174C(u8 taskId) { s16* data = gTasks[taskId].data; - + if (sub_81221EC() != 1) { if (gPaletteFade.active != 1) @@ -1857,7 +1856,7 @@ void sub_81C174C(u8 taskId) gUnknown_0203CF21 = 4; gSpecialVar_0x8005 = 4; sub_81C044C(taskId); - } + } } } } @@ -1907,7 +1906,7 @@ void sub_81C1940(u8 taskId) { if (gUnknown_0203CF1C->unk40C0 != 2) { - + ClearWindowTilemap(19); if (!gSprites[gUnknown_0203CF1C->unk40D5].invisible) ClearWindowTilemap(13); @@ -2224,7 +2223,7 @@ void sub_81C1E20(u8 taskId) { if (gUnknown_0203CF1C->unk40C0 == 2) PutWindowTilemap(14); - + } else { @@ -2277,7 +2276,7 @@ void sub_81C1F80(u8 taskId) PutWindowTilemap(15); sub_81C240C(data[2]); } - else + else { if (!gSprites[gUnknown_0203CF1C->unk40D5].invisible) { @@ -3645,7 +3644,7 @@ void sub_81C3D54(u8 taskId) { s16 *data = gTasks[taskId].data; s16 dataa = data[0] - 1; - + switch (dataa) { case 0: @@ -3676,4 +3675,4 @@ void sub_81C3D54(u8 taskId) return; } data[0]++; -}
\ No newline at end of file +} diff --git a/src/recorded_battle.c b/src/recorded_battle.c new file mode 100644 index 000000000..e843ff7cb --- /dev/null +++ b/src/recorded_battle.c @@ -0,0 +1,1676 @@ +#include "global.h" +#include "battle.h" +#include "recorded_battle.h" +#include "main.h" +#include "pokemon.h" +#include "rng.h" +#include "event_data.h" +#include "link.h" +#include "string_util.h" +#include "palette.h" +#include "save.h" +#include "malloc.h" +#include "util.h" +#include "task.h" +#include "text.h" + +#define BANK_RECORD_SIZE 664 +#define ILLEGAL_BATTLE_TYPES ((BATTLE_TYPE_LINK | BATTLE_TYPE_SAFARI | BATTLE_TYPE_FIRST_BATTLE \ + | BATTLE_TYPE_WALLY_TUTORIAL | BATTLE_TYPE_ROAMER | BATTLE_TYPE_EREADER_TRAINER \ + | BATTLE_TYPE_KYOGRE_GROUDON | BATTLE_TYPE_LEGENDARY | BATTLE_TYPE_REGI \ + | BATTLE_TYPE_RECORDED | BATTLE_TYPE_x4000000 | BATTLE_TYPE_SECRET_BASE \ + | BATTLE_TYPE_GROUDON | BATTLE_TYPE_KYORGE | BATTLE_TYPE_RAYQUAZA)) + +extern u32 gBattleTypeFlags; +extern u16 gTrainerBattleOpponent_A; +extern u16 gTrainerBattleOpponent_B; +extern u16 gPartnerTrainerId; +extern u8 gActiveBank; +extern u8 gNoOfAllBanks; +extern u16 gBattlePartyID[BATTLE_BANKS_COUNT]; +extern struct BattlePokemon gBattleMons[BATTLE_BANKS_COUNT]; +extern u16 gChosenMovesByBanks[BATTLE_BANKS_COUNT]; +extern u8 gUnknown_03001278; +extern u8 gUnknown_03001279; + +struct PlayerInfo +{ + u32 trainerId; + u8 name[PLAYER_NAME_LENGTH]; + u8 gender; + u16 bank; + u16 language; +}; + +struct MovePp +{ + u16 moves[4]; + u8 pp[4]; +}; + +struct RecordedBattleSave +{ + struct Pokemon playerParty[PARTY_SIZE]; + struct Pokemon opponentParty[PARTY_SIZE]; + u8 playersName[BATTLE_BANKS_COUNT][PLAYER_NAME_LENGTH]; + u8 playersGender[BATTLE_BANKS_COUNT]; + u32 playersTrainerId[BATTLE_BANKS_COUNT]; + u8 playersLanguage[BATTLE_BANKS_COUNT]; + u32 rngSeed; + u32 battleFlags; + u8 playersBank[BATTLE_BANKS_COUNT]; + u16 opponentA; + u16 opponentB; + u16 partnerId; + u16 field_4FA; + u8 field_4FC; + u8 field_4FD; + u8 field_4FE; + u8 battleStyle : 1; + u8 textSpeed : 3; + u32 AI_scripts; + u8 field_504[8]; + u8 field_50C; + u8 field_50D; + u16 field_50E[6]; + u8 field_51A; + u8 field_51B; + u8 battleRecord[BATTLE_BANKS_COUNT][BANK_RECORD_SIZE]; + u32 checksum; +}; + +EWRAM_DATA u32 gRecordedBattleRngSeed = 0; +EWRAM_DATA u32 gBattlePalaceMoveSelectionRngValue = 0; +EWRAM_DATA static u8 sBattleRecords[BATTLE_BANKS_COUNT][BANK_RECORD_SIZE] = {0}; +EWRAM_DATA static u16 sRecordedBytesNo[BATTLE_BANKS_COUNT] = {0}; +EWRAM_DATA static u16 sUnknown_0203C79C[4] = {0}; +EWRAM_DATA static u16 sUnknown_0203C7A4[4] = {0}; +EWRAM_DATA static u8 sUnknown_0203C7AC = 0; +EWRAM_DATA static u8 sUnknown_0203C7AD = 0; +EWRAM_DATA static u8 sUnknown_0203C7AE = 0; +EWRAM_DATA static u8 sUnknown_0203C7AF = 0; +EWRAM_DATA static MainCallback sCallback2_AfterRecordedBattle = NULL; +EWRAM_DATA u8 gUnknown_0203C7B4 = 0; +EWRAM_DATA static u8 sUnknown_0203C7B5 = 0; +EWRAM_DATA static u8 sRecordedBattle_BattleStyle = 0; +EWRAM_DATA static u8 sRecordedBattle_TextSpeed = 0; +EWRAM_DATA static u32 sRecordedBattle_BattleFlags = 0; +EWRAM_DATA static u32 sRecordedBattle_AI_Scripts = 0; +EWRAM_DATA static struct Pokemon sSavedPlayerParty[PARTY_SIZE] = {0}; +EWRAM_DATA static struct Pokemon sSavedOpponentParty[PARTY_SIZE] = {0}; +EWRAM_DATA static u16 sRecordedBattle_PlayerMonMoves[2][4] = {0}; +EWRAM_DATA static struct PlayerInfo sRecordedBattle_Players[BATTLE_BANKS_COUNT] = {0}; +EWRAM_DATA static u8 sUnknown_0203CCD0 = 0; +EWRAM_DATA static u8 sUnknown_0203CCD1[8] = {0}; +EWRAM_DATA static u8 sUnknown_0203CCD9 = 0; +EWRAM_DATA static u8 sUnknown_0203CCDA = 0; +EWRAM_DATA static u16 sUnknown_0203CCDC[6] = {0}; +EWRAM_DATA static u8 sUnknown_0203CCE8 = 0; + +extern u32 sub_81A513C(void); +extern void PlayMapChosenOrBattleBGM(bool8); + +// this file's functions +static u8 sub_8185278(u8 *arg0, u8 *arg1, u8 *arg2); +static bool32 AllocTryCopyRecordedBattleSaveData(struct RecordedBattleSave *dst); +static void RecordedBattle_RestoreSavedParties(void); +static void CB2_RecordedBattle(void); + +void sub_8184DA4(u8 arg0) +{ + s32 i, j; + + sUnknown_0203C7AC = arg0; + sUnknown_0203CCD0 = 0; + + for (i = 0; i < BATTLE_BANKS_COUNT; i++) + { + sRecordedBytesNo[i] = 0; + sUnknown_0203C79C[i] = 0; + sUnknown_0203C7A4[i] = 0; + + if (arg0 == 1) + { + for (j = 0; j < BANK_RECORD_SIZE; j++) + { + sBattleRecords[i][j] |= 0xFF; + } + sRecordedBattle_BattleFlags = gBattleTypeFlags; + sRecordedBattle_AI_Scripts = gBattleResources->ai->aiFlags; + } + } +} + +void sub_8184E58(void) +{ + s32 i, j; + + if (sUnknown_0203C7AC == 1) + { + gRecordedBattleRngSeed = gRngValue; + sUnknown_0203C7AE = VarGet(VAR_FRONTIER_FACILITY); + sUnknown_0203C7AF = sub_81A513C(); + } + else if (sUnknown_0203C7AC == 2) + { + gRngValue = gRecordedBattleRngSeed; + } + + if (gBattleTypeFlags & BATTLE_TYPE_LINK) + { + u8 linkPlayersCount; + u8 text[30]; + + gUnknown_0203C7B4 = GetMultiplayerId(); + linkPlayersCount = GetLinkPlayerCount(); + + for (i = 0; i < BATTLE_BANKS_COUNT; i++) + { + sRecordedBattle_Players[i].trainerId = gLinkPlayers[i].trainerId; + sRecordedBattle_Players[i].gender = gLinkPlayers[i].gender; + sRecordedBattle_Players[i].bank = gLinkPlayers[i].lp_field_18; + sRecordedBattle_Players[i].language = gLinkPlayers[i].language; + + if (i < linkPlayersCount) + { + StringCopy(text, gLinkPlayers[i].name); + StripExtCtrlCodes(text); + StringCopy(sRecordedBattle_Players[i].name, text); + } + else + { + for (j = 0; j < PLAYER_NAME_LENGTH; j++) + sRecordedBattle_Players[i].name[j] = gLinkPlayers[i].name[j]; + } + } + } + else + { + sRecordedBattle_Players[0].trainerId = (gSaveBlock2Ptr->playerTrainerId[0]) + | (gSaveBlock2Ptr->playerTrainerId[1] << 8) + | (gSaveBlock2Ptr->playerTrainerId[2] << 16) + | (gSaveBlock2Ptr->playerTrainerId[3] << 24); + + sRecordedBattle_Players[0].gender = gSaveBlock2Ptr->playerGender; + sRecordedBattle_Players[0].bank = 0; + sRecordedBattle_Players[0].language = gGameLanguage; + + for (i = 0; i < PLAYER_NAME_LENGTH; i++) + sRecordedBattle_Players[0].name[i] = gSaveBlock2Ptr->playerName[i]; + } +} + +void RecordedBattle_SetBankAction(u8 bank, u8 action) +{ + if (sRecordedBytesNo[bank] < BANK_RECORD_SIZE && sUnknown_0203C7AC != 2) + { + sBattleRecords[bank][sRecordedBytesNo[bank]++] = action; + } +} + +void RecordedBattle_ClearBankAction(u8 bank, u8 bytesToClear) +{ + s32 i; + + for (i = 0; i < bytesToClear; i++) + { + sRecordedBytesNo[bank]--; + sBattleRecords[bank][sRecordedBytesNo[bank]] |= 0xFF; + if (sRecordedBytesNo[bank] == 0) + break; + } +} + +u8 RecordedBattle_ReadBankAction(u8 bank) +{ + // trying to read past array or invalid action byte, battle is over + if (sRecordedBytesNo[bank] >= BANK_RECORD_SIZE || sBattleRecords[bank][sRecordedBytesNo[bank]] == 0xFF) + { + gSpecialVar_Result = gBattleOutcome = BATTLE_PLAYER_TELEPORTED; // hah + ResetPaletteFadeControl(); + BeginNormalPaletteFade(-1, 0, 0, 0x10, 0); + SetMainCallback2(CB2_QuitRecordedBattle); + return -1; + } + else + { + return sBattleRecords[bank][sRecordedBytesNo[bank]++]; + } +} + +u8 sub_81850D0(void) +{ + return sUnknown_0203C7AC; +} + +u8 sub_81850DC(u8 *arg0) +{ + u8 i, j; + u8 ret = 0; + + for (i = 0; i < BATTLE_BANKS_COUNT; i++) + { + if (sRecordedBytesNo[i] != sUnknown_0203C79C[i]) + { + arg0[ret++] = i; + arg0[ret++] = sRecordedBytesNo[i] - sUnknown_0203C79C[i]; + + for (j = 0; j < sRecordedBytesNo[i] - sUnknown_0203C79C[i]; j++) + { + arg0[ret++] = sBattleRecords[i][sUnknown_0203C79C[i] + j]; + } + + sUnknown_0203C79C[i] = sRecordedBytesNo[i]; + } + } + + return ret; +} + +void sub_81851A8(u8 *arg0) +{ + s32 i; + u8 var1 = 2; + u8 var2; + + if (!(gBattleTypeFlags & BATTLE_TYPE_LINK)) + return; + + for (i = 0; i < GetLinkPlayerCount(); i++) + { + if ((gLinkPlayers[i].version & 0xFF) != VERSION_EMERALD) + return; + } + + if (!(gBattleTypeFlags & BATTLE_TYPE_WILD)) + { + for (var2 = *arg0; var2 != 0;) + { + u8 unkVar = sub_8185278(arg0, &var1, &var2); + u8 unkVar2 = sub_8185278(arg0, &var1, &var2); + + for (i = 0; i < unkVar2; i++) + { + sBattleRecords[unkVar][sUnknown_0203C7A4[unkVar]++] = sub_8185278(arg0, &var1, &var2); + } + } + } +} + +static u8 sub_8185278(u8 *arg0, u8 *arg1, u8 *arg2) +{ + (*arg2)--; + return arg0[(*arg1)++]; +} + +bool32 CanCopyRecordedBattleSaveData(void) +{ + struct RecordedBattleSave *dst = AllocZeroed(sizeof(struct RecordedBattleSave)); + bool32 ret = AllocTryCopyRecordedBattleSaveData(dst); + Free(dst); + return ret; +} + +static bool32 IsRecordedBattleSaveValid(struct RecordedBattleSave *save) +{ + if (save->battleFlags == 0) + return FALSE; + if (save->battleFlags & ILLEGAL_BATTLE_TYPES) + return FALSE; + if (CalcByteArraySum((void*)(save), sizeof(*save) - 4) != save->checksum) + return FALSE; + + return TRUE; +} + +static bool32 sub_81852F0(struct RecordedBattleSave *battleSave, struct RecordedBattleSave *saveSection) +{ + memset(saveSection, 0, sizeof(struct SaveSection)); + memcpy(saveSection, battleSave, sizeof(*battleSave)); + + saveSection->checksum = CalcByteArraySum((void*)(saveSection), sizeof(*saveSection) - 4); + + if (sub_8153634(31, (void*)(saveSection)) != 1) + return FALSE; + + return TRUE; +} + +#ifdef NONMATCHING +u32 MoveRecordedBattleToSaveData(void) +{ + s32 i, j; + u8 var = 0; + struct RecordedBattleSave *battleSave = AllocZeroed(sizeof(struct RecordedBattleSave)); + struct SaveSection *savSection = AllocZeroed(sizeof(struct SaveSection)); + + for (i = 0; i < PARTY_SIZE; i++) + { + battleSave->playerParty[i] = sSavedPlayerParty[i]; + battleSave->opponentParty[i] = sSavedOpponentParty[i]; + } + + for (i = 0; i < BATTLE_BANKS_COUNT; i++) + { + for (j = 0; j < PLAYER_NAME_LENGTH; j++) + { + battleSave->playersName[i][j] = sRecordedBattle_Players[i].name[j]; + } + battleSave->playersGender[i] = sRecordedBattle_Players[i].gender; + battleSave->playersLanguage[i] = sRecordedBattle_Players[i].language; + battleSave->playersBank[i] = sRecordedBattle_Players[i].bank; + battleSave->playersTrainerId[i] = sRecordedBattle_Players[i].trainerId; + } + + battleSave->rngSeed = gRecordedBattleRngSeed; + + if (sRecordedBattle_BattleFlags & BATTLE_TYPE_LINK) + { + battleSave->battleFlags = (sRecordedBattle_BattleFlags & ~(BATTLE_TYPE_LINK | BATTLE_TYPE_20)); + battleSave->battleFlags |= BATTLE_TYPE_x2000000; + + if (sRecordedBattle_BattleFlags & BATTLE_TYPE_WILD) + { + battleSave->battleFlags |= BATTLE_TYPE_x80000000; + } + else if (sRecordedBattle_BattleFlags & BATTLE_TYPE_MULTI) + { + switch (sRecordedBattle_Players[0].bank) + { + case 0: + case 2: + if (!(sRecordedBattle_Players[gUnknown_0203C7B4].bank & 1)) + battleSave->battleFlags |= BATTLE_TYPE_x80000000; + break; + case 1: + case 3: + if ((sRecordedBattle_Players[gUnknown_0203C7B4].bank & 1)) + battleSave->battleFlags |= BATTLE_TYPE_x80000000; + break; + } + } + } + else + { + battleSave->battleFlags = sRecordedBattle_BattleFlags; + } + + battleSave->opponentA = gTrainerBattleOpponent_A; + battleSave->opponentB = gTrainerBattleOpponent_B; + battleSave->partnerId = gPartnerTrainerId; + battleSave->field_4FA = gUnknown_0203C7B4; + battleSave->field_4FC = gSaveBlock2Ptr->field_CA9_b; + battleSave->field_4FD = sUnknown_0203C7AE; + battleSave->field_4FE = sUnknown_0203C7AF; + battleSave->battleStyle = gSaveBlock2Ptr->optionsBattleStyle; + battleSave->textSpeed = gSaveBlock2Ptr->optionsTextSpeed; + battleSave->AI_scripts = sRecordedBattle_AI_Scripts; + + /* Can't match it without proper knowledge of the Saveblock 2. + if (gTrainerBattleOpponent_A >= 300 && gTrainerBattleOpponent_A <= 399) + { + for (i = 0; i < 8; i++) + { + battleSave->field_504[i] = gSaveBlock2Ptr->field_738[gTrainerBattleOpponent_A - 300].field_4[i]; + } + battleSave->field_50C = gSaveBlock2Ptr->field_738[gTrainerBattleOpponent_A - 300].field_1; + + if (sUnknown_0203CCE8 == 1) + { + for (i = 0; i < 6; i++) + { + battleSave->field_50E[i] = gSaveBlock2Ptr->field_738[gTrainerBattleOpponent_A - 300].field_28[i]; + } + } + else + { + for (i = 0; i < 6; i++) + { + battleSave->field_50E[i] = gSaveBlock2Ptr->field_738[gTrainerBattleOpponent_A - 300].field_1C[i]; + } + } + battleSave->field_51A = gSaveBlock2Ptr->field_738[gTrainerBattleOpponent_A - 300].field_E4; + } + else if (gTrainerBattleOpponent_B >= 300 && gTrainerBattleOpponent_B <= 399) + { + for (i = 0; i < 8; i++) + { + battleSave->field_504[i] = gSaveBlock2Ptr->field_738[gTrainerBattleOpponent_B - 300].field_4[i]; + } + battleSave->field_50C = gSaveBlock2Ptr->field_738[gTrainerBattleOpponent_B - 300].field_1; + + if (sUnknown_0203CCE8 == 1) + { + for (i = 0; i < 6; i++) + { + battleSave->field_50E[i] = gSaveBlock2Ptr->field_738[gTrainerBattleOpponent_B - 300].field_28[i]; + } + } + else + { + for (i = 0; i < 6; i++) + { + battleSave->field_50E[i] = gSaveBlock2Ptr->field_738[gTrainerBattleOpponent_B - 300].field_1C[i]; + } + } + battleSave->field_51A = gSaveBlock2Ptr->field_738[gTrainerBattleOpponent_B - 300].field_E4; + } + else if (gPartnerTrainerId >= 300 && gPartnerTrainerId <= 399) + { + for (i = 0; i < 8; i++) + { + battleSave->field_504[i] = gSaveBlock2Ptr->field_738[gPartnerTrainerId - 300].field_4[i]; + } + battleSave->field_50C = gSaveBlock2Ptr->field_738[gPartnerTrainerId - 300].field_1; + + if (sUnknown_0203CCE8 == 1) + { + for (i = 0; i < 6; i++) + { + battleSave->field_50E[i] = gSaveBlock2Ptr->field_738[gPartnerTrainerId - 300].field_28[i]; + } + } + else + { + for (i = 0; i < 6; i++) + { + battleSave->field_50E[i] = gSaveBlock2Ptr->field_738[gPartnerTrainerId - 300].field_1C[i]; + } + } + battleSave->field_51A = gSaveBlock2Ptr->field_738[gPartnerTrainerId - 300].field_E4; + } + + */ +} + +#else +__attribute__((naked)) +u32 MoveRecordedBattleToSaveData(void) +{ + asm(".syntax unified\n\ + push {r4-r7,lr}\n\ + mov r7, r10\n\ + mov r6, r9\n\ + mov r5, r8\n\ + push {r5-r7}\n\ + sub sp, 0xC\n\ + movs r0, 0\n\ + str r0, [sp, 0x4]\n\ + movs r0, 0xF8\n\ + lsls r0, 4\n\ + bl AllocZeroed\n\ + adds r7, r0, 0\n\ + movs r0, 0x80\n\ + lsls r0, 5\n\ + bl AllocZeroed\n\ + str r0, [sp]\n\ + movs r6, 0\n\ +_0818535E:\n\ + movs r0, 0x64\n\ + adds r4, r6, 0\n\ + muls r4, r0\n\ + adds r5, r7, r4\n\ + ldr r1, =sSavedPlayerParty\n\ + adds r1, r4, r1\n\ + adds r0, r5, 0\n\ + movs r2, 0x64\n\ + bl memcpy\n\ + movs r1, 0x96\n\ + lsls r1, 2\n\ + adds r5, r1\n\ + ldr r0, =sSavedOpponentParty\n\ + adds r4, r0\n\ + adds r0, r5, 0\n\ + adds r1, r4, 0\n\ + movs r2, 0x64\n\ + bl memcpy\n\ + adds r6, 0x1\n\ + cmp r6, 0x5\n\ + ble _0818535E\n\ + movs r6, 0\n\ + ldr r2, =gSaveBlock2Ptr\n\ + mov r9, r2\n\ + movs r3, 0x9A\n\ + lsls r3, 3\n\ + adds r3, r7, r3\n\ + str r3, [sp, 0x8]\n\ + ldr r5, =sRecordedBattle_Players\n\ + mov r8, r6\n\ + mov r12, r6\n\ + movs r4, 0x96\n\ + lsls r4, 3\n\ + adds r4, r7\n\ + mov r10, r4\n\ + ldr r0, =0x000004e4\n\ + adds r4, r7, r0\n\ +_081853AC:\n\ + lsls r1, r6, 3\n\ + ldr r0, =sRecordedBattle_Players\n\ + adds r0, 0x4\n\ + mov r3, r8\n\ + adds r2, r3, r0\n\ + add r1, r10\n\ + movs r3, 0x7\n\ +_081853BA:\n\ + ldrb r0, [r2]\n\ + strb r0, [r1]\n\ + adds r2, 0x1\n\ + adds r1, 0x1\n\ + subs r3, 0x1\n\ + cmp r3, 0\n\ + bge _081853BA\n\ + ldr r0, [sp, 0x8]\n\ + adds r1, r0, r6\n\ + ldrb r0, [r5, 0xC]\n\ + strb r0, [r1]\n\ + ldrh r0, [r5, 0x10]\n\ + strb r0, [r4]\n\ + ldrh r0, [r5, 0xE]\n\ + strb r0, [r4, 0xC]\n\ + ldr r1, =0x000004d4\n\ + adds r0, r7, r1\n\ + add r0, r12\n\ + ldr r1, [r5]\n\ + str r1, [r0]\n\ + adds r5, 0x14\n\ + movs r2, 0x14\n\ + add r8, r2\n\ + movs r3, 0x4\n\ + add r12, r3\n\ + adds r4, 0x1\n\ + adds r6, 0x1\n\ + cmp r6, 0x3\n\ + ble _081853AC\n\ + movs r4, 0x9D\n\ + lsls r4, 3\n\ + adds r1, r7, r4\n\ + ldr r5, =gRecordedBattleRngSeed\n\ + ldr r0, [r5]\n\ + str r0, [r1]\n\ + ldr r0, =sRecordedBattle_BattleFlags\n\ + ldr r2, [r0]\n\ + movs r0, 0x2\n\ + ands r0, r2\n\ + cmp r0, 0\n\ + beq _081854DC\n\ + ldr r1, =0x000004ec\n\ + adds r3, r7, r1\n\ + movs r1, 0x23\n\ + negs r1, r1\n\ + ands r1, r2\n\ + movs r0, 0x80\n\ + lsls r0, 18\n\ + orrs r1, r0\n\ + str r1, [r3]\n\ + movs r0, 0x4\n\ + ands r0, r2\n\ + cmp r0, 0\n\ + beq _08185454\n\ + movs r0, 0x80\n\ + lsls r0, 24\n\ + orrs r1, r0\n\ + str r1, [r3]\n\ + b _081854E2\n\ + .pool\n\ +_08185454:\n\ + movs r0, 0x40\n\ + ands r2, r0\n\ + cmp r2, 0\n\ + beq _081854E2\n\ + ldr r2, =sRecordedBattle_Players\n\ + ldrh r0, [r2, 0xE]\n\ + cmp r0, 0x1\n\ + beq _081854A8\n\ + cmp r0, 0x1\n\ + bgt _08185474\n\ + cmp r0, 0\n\ + beq _0818547E\n\ + b _081854E2\n\ + .pool\n\ +_08185474:\n\ + cmp r0, 0x2\n\ + beq _0818547E\n\ + cmp r0, 0x3\n\ + beq _081854A8\n\ + b _081854E2\n\ +_0818547E:\n\ + ldr r3, =gUnknown_0203C7B4\n\ + ldrb r1, [r3]\n\ + lsls r0, r1, 2\n\ + adds r0, r1\n\ + lsls r0, 2\n\ + ldr r4, =sRecordedBattle_Players\n\ + adds r0, r4\n\ + ldrh r1, [r0, 0xE]\n\ + movs r0, 0x1\n\ + ands r0, r1\n\ + cmp r0, 0\n\ + bne _081854E2\n\ + ldr r5, =0x000004ec\n\ + adds r0, r7, r5\n\ + b _081854C4\n\ + .pool\n\ +_081854A8:\n\ + ldr r0, =gUnknown_0203C7B4\n\ + ldrb r1, [r0]\n\ + lsls r0, r1, 2\n\ + adds r0, r1\n\ + lsls r0, 2\n\ + ldr r1, =sRecordedBattle_Players\n\ + adds r0, r1\n\ + ldrh r1, [r0, 0xE]\n\ + movs r0, 0x1\n\ + ands r0, r1\n\ + cmp r0, 0\n\ + beq _081854E2\n\ + ldr r2, =0x000004ec\n\ + adds r0, r7, r2\n\ +_081854C4:\n\ + ldr r1, [r0]\n\ + movs r2, 0x80\n\ + lsls r2, 24\n\ + orrs r1, r2\n\ + str r1, [r0]\n\ + b _081854E2\n\ + .pool\n\ +_081854DC:\n\ + ldr r3, =0x000004ec\n\ + adds r0, r7, r3\n\ + str r2, [r0]\n\ +_081854E2:\n\ + ldr r4, =gTrainerBattleOpponent_A\n\ + ldrh r1, [r4]\n\ + ldr r5, =0x000004f4\n\ + adds r0, r7, r5\n\ + strh r1, [r0]\n\ + ldr r0, =gTrainerBattleOpponent_B\n\ + ldrh r1, [r0]\n\ + ldr r2, =0x000004f6\n\ + adds r0, r7, r2\n\ + strh r1, [r0]\n\ + ldr r3, =gPartnerTrainerId\n\ + ldrh r1, [r3]\n\ + movs r4, 0x9F\n\ + lsls r4, 3\n\ + adds r0, r7, r4\n\ + strh r1, [r0]\n\ + ldr r5, =gUnknown_0203C7B4\n\ + ldrb r1, [r5]\n\ + adds r2, 0x4\n\ + adds r0, r7, r2\n\ + strh r1, [r0]\n\ + mov r3, r9\n\ + ldr r0, [r3]\n\ + ldr r4, =0x00000ca9\n\ + adds r0, r4\n\ + ldrb r0, [r0]\n\ + lsls r0, 30\n\ + lsrs r0, 30\n\ + ldr r5, =0x000004fc\n\ + adds r1, r7, r5\n\ + strb r0, [r1]\n\ + ldr r0, =sUnknown_0203C7AE\n\ + ldrb r1, [r0]\n\ + adds r2, 0x3\n\ + adds r0, r7, r2\n\ + strb r1, [r0]\n\ + ldr r3, =sUnknown_0203C7AF\n\ + ldrb r1, [r3]\n\ + ldr r4, =0x000004fe\n\ + adds r0, r7, r4\n\ + strb r1, [r0]\n\ + mov r5, r9\n\ + ldr r0, [r5]\n\ + ldrb r1, [r0, 0x15]\n\ + lsls r1, 29\n\ + ldr r0, =0x000004ff\n\ + adds r3, r7, r0\n\ + lsrs r1, 31\n\ + ldrb r2, [r3]\n\ + movs r0, 0x2\n\ + negs r0, r0\n\ + ands r0, r2\n\ + orrs r0, r1\n\ + strb r0, [r3]\n\ + ldr r1, [r5]\n\ + ldrb r1, [r1, 0x14]\n\ + lsls r1, 29\n\ + lsrs r1, 28\n\ + movs r2, 0xF\n\ + negs r2, r2\n\ + ands r0, r2\n\ + orrs r0, r1\n\ + strb r0, [r3]\n\ + movs r2, 0xA0\n\ + lsls r2, 3\n\ + adds r1, r7, r2\n\ + ldr r3, =sRecordedBattle_AI_Scripts\n\ + ldr r0, [r3]\n\ + str r0, [r1]\n\ + ldr r4, =0xfffffed4\n\ + adds r1, r4, 0\n\ + ldr r5, =gTrainerBattleOpponent_A\n\ + ldrh r5, [r5]\n\ + adds r0, r1, r5\n\ + lsls r0, 16\n\ + lsrs r0, 16\n\ + cmp r0, 0x63\n\ + bls _08185580\n\ + b _081856C4\n\ +_08185580:\n\ + movs r6, 0\n\ + ldr r0, =0x00000504\n\ + adds r3, r7, r0\n\ + mov r10, r9\n\ + ldr r1, =gTrainerBattleOpponent_A\n\ + mov r12, r1\n\ + adds r2, r4, 0\n\ + mov r8, r2\n\ + ldr r4, =0x0000073c\n\ +_08185592:\n\ + adds r2, r3, r6\n\ + mov r5, r10\n\ + ldr r1, [r5]\n\ + mov r5, r12\n\ + ldrh r0, [r5]\n\ + add r0, r8\n\ + movs r5, 0xEC\n\ + muls r0, r5\n\ + adds r0, r6, r0\n\ + adds r1, r4\n\ + adds r1, r0\n\ + ldrb r0, [r1]\n\ + strb r0, [r2]\n\ + adds r6, 0x1\n\ + cmp r6, 0x7\n\ + ble _08185592\n\ + mov r0, r9\n\ + ldr r2, [r0]\n\ + ldr r1, =gTrainerBattleOpponent_A\n\ + ldrh r0, [r1]\n\ + ldr r3, =0xfffffed4\n\ + adds r0, r3\n\ + movs r1, 0xEC\n\ + muls r0, r1\n\ + adds r2, r0\n\ + ldr r4, =0x00000739\n\ + adds r2, r4\n\ + ldrb r1, [r2]\n\ + ldr r5, =0x0000050c\n\ + adds r0, r7, r5\n\ + strb r1, [r0]\n\ + ldr r1, =sUnknown_0203CCE8\n\ + ldrb r0, [r1]\n\ + cmp r0, 0x1\n\ + bne _08185664\n\ + movs r6, 0\n\ + ldr r2, =0x0000050e\n\ + adds r4, r7, r2\n\ + mov r10, r9\n\ + ldr r5, =gTrainerBattleOpponent_A\n\ + mov r8, r5\n\ + adds r5, r3, 0\n\ +_081855E6:\n\ + lsls r3, r6, 1\n\ + mov r0, r10\n\ + ldr r2, [r0]\n\ + mov r1, r8\n\ + ldrh r0, [r1]\n\ + adds r0, r5\n\ + movs r1, 0xEC\n\ + muls r0, r1\n\ + adds r3, r0\n\ + movs r0, 0xEC\n\ + lsls r0, 3\n\ + adds r2, r0\n\ + adds r2, r3\n\ + ldrh r0, [r2]\n\ + strh r0, [r4]\n\ + adds r4, 0x2\n\ + adds r6, 0x1\n\ + cmp r6, 0x5\n\ + ble _081855E6\n\ + b _08185696\n\ + .pool\n\ +_08185664:\n\ + movs r6, 0\n\ + ldr r1, =0x0000050e\n\ + adds r4, r7, r1\n\ + mov r10, r9\n\ + ldr r2, =gTrainerBattleOpponent_A\n\ + mov r8, r2\n\ + adds r5, r3, 0\n\ +_08185672:\n\ + lsls r3, r6, 1\n\ + mov r0, r10\n\ + ldr r2, [r0]\n\ + mov r1, r8\n\ + ldrh r0, [r1]\n\ + adds r0, r5\n\ + movs r1, 0xEC\n\ + muls r0, r1\n\ + adds r3, r0\n\ + ldr r0, =0x00000754\n\ + adds r2, r0\n\ + adds r2, r3\n\ + ldrh r0, [r2]\n\ + strh r0, [r4]\n\ + adds r4, 0x2\n\ + adds r6, 0x1\n\ + cmp r6, 0x5\n\ + ble _08185672\n\ +_08185696:\n\ + mov r1, r9\n\ + ldr r2, [r1]\n\ + ldr r3, =gTrainerBattleOpponent_A\n\ + ldrh r0, [r3]\n\ + ldr r4, =0xfffffed4\n\ + adds r0, r4\n\ + movs r1, 0xEC\n\ + muls r0, r1\n\ + adds r2, r0\n\ + ldr r5, =0x0000081c\n\ + adds r2, r5\n\ + ldrb r1, [r2]\n\ + b _08185856\n\ + .pool\n\ +_081856C4:\n\ + ldr r3, =gTrainerBattleOpponent_B\n\ + ldrh r3, [r3]\n\ + adds r0, r1, r3\n\ + lsls r0, 16\n\ + lsrs r0, 16\n\ + cmp r0, 0x63\n\ + bls _081856D4\n\ + b _081857E4\n\ +_081856D4:\n\ + movs r6, 0\n\ + ldr r4, =0x00000504\n\ + adds r3, r7, r4\n\ + mov r10, r9\n\ + ldr r5, =gTrainerBattleOpponent_B\n\ + mov r12, r5\n\ + ldr r0, =0xfffffed4\n\ + mov r8, r0\n\ + ldr r4, =0x0000073c\n\ +_081856E6:\n\ + adds r2, r3, r6\n\ + mov r5, r10\n\ + ldr r1, [r5]\n\ + mov r5, r12\n\ + ldrh r0, [r5]\n\ + add r0, r8\n\ + movs r5, 0xEC\n\ + muls r0, r5\n\ + adds r0, r6, r0\n\ + adds r1, r4\n\ + adds r1, r0\n\ + ldrb r0, [r1]\n\ + strb r0, [r2]\n\ + adds r6, 0x1\n\ + cmp r6, 0x7\n\ + ble _081856E6\n\ + mov r0, r9\n\ + ldr r2, [r0]\n\ + ldr r1, =gTrainerBattleOpponent_B\n\ + ldrh r0, [r1]\n\ + ldr r3, =0xfffffed4\n\ + adds r0, r3\n\ + movs r1, 0xEC\n\ + muls r0, r1\n\ + adds r2, r0\n\ + ldr r4, =0x00000739\n\ + adds r2, r4\n\ + ldrb r1, [r2]\n\ + ldr r5, =0x0000050c\n\ + adds r0, r7, r5\n\ + strb r1, [r0]\n\ + ldr r1, =sUnknown_0203CCE8\n\ + ldrb r0, [r1]\n\ + cmp r0, 0x1\n\ + bne _08185784\n\ + movs r6, 0\n\ + ldr r2, =0x0000050e\n\ + adds r4, r7, r2\n\ + mov r10, r9\n\ + ldr r5, =gTrainerBattleOpponent_B\n\ + mov r8, r5\n\ + adds r5, r3, 0\n\ +_0818573A:\n\ + lsls r3, r6, 1\n\ + mov r0, r10\n\ + ldr r2, [r0]\n\ + mov r1, r8\n\ + ldrh r0, [r1]\n\ + adds r0, r5\n\ + movs r1, 0xEC\n\ + muls r0, r1\n\ + adds r3, r0\n\ + movs r0, 0xEC\n\ + lsls r0, 3\n\ + adds r2, r0\n\ + adds r2, r3\n\ + ldrh r0, [r2]\n\ + strh r0, [r4]\n\ + adds r4, 0x2\n\ + adds r6, 0x1\n\ + cmp r6, 0x5\n\ + ble _0818573A\n\ + b _081857B6\n\ + .pool\n\ +_08185784:\n\ + movs r6, 0\n\ + ldr r1, =0x0000050e\n\ + adds r4, r7, r1\n\ + mov r10, r9\n\ + ldr r2, =gTrainerBattleOpponent_B\n\ + mov r8, r2\n\ + adds r5, r3, 0\n\ +_08185792:\n\ + lsls r3, r6, 1\n\ + mov r0, r10\n\ + ldr r2, [r0]\n\ + mov r1, r8\n\ + ldrh r0, [r1]\n\ + adds r0, r5\n\ + movs r1, 0xEC\n\ + muls r0, r1\n\ + adds r3, r0\n\ + ldr r0, =0x00000754\n\ + adds r2, r0\n\ + adds r2, r3\n\ + ldrh r0, [r2]\n\ + strh r0, [r4]\n\ + adds r4, 0x2\n\ + adds r6, 0x1\n\ + cmp r6, 0x5\n\ + ble _08185792\n\ +_081857B6:\n\ + mov r1, r9\n\ + ldr r2, [r1]\n\ + ldr r3, =gTrainerBattleOpponent_B\n\ + ldrh r0, [r3]\n\ + ldr r4, =0xfffffed4\n\ + adds r0, r4\n\ + movs r1, 0xEC\n\ + muls r0, r1\n\ + adds r2, r0\n\ + ldr r5, =0x0000081c\n\ + adds r2, r5\n\ + ldrb r1, [r2]\n\ + b _08185856\n\ + .pool\n\ +_081857E4:\n\ + ldr r3, =gPartnerTrainerId\n\ + ldrh r3, [r3]\n\ + adds r0, r1, r3\n\ + lsls r0, 16\n\ + lsrs r0, 16\n\ + cmp r0, 0x63\n\ + bhi _0818585C\n\ + movs r6, 0\n\ + ldr r4, =0x00000504\n\ + adds r3, r7, r4\n\ + mov r10, r9\n\ + ldr r5, =gPartnerTrainerId\n\ + mov r12, r5\n\ + ldr r0, =0xfffffed4\n\ + mov r8, r0\n\ + ldr r4, =0x0000073c\n\ +_08185804:\n\ + adds r2, r3, r6\n\ + mov r5, r10\n\ + ldr r1, [r5]\n\ + mov r5, r12\n\ + ldrh r0, [r5]\n\ + add r0, r8\n\ + movs r5, 0xEC\n\ + muls r0, r5\n\ + adds r0, r6, r0\n\ + adds r1, r4\n\ + adds r1, r0\n\ + ldrb r0, [r1]\n\ + strb r0, [r2]\n\ + adds r6, 0x1\n\ + cmp r6, 0x7\n\ + ble _08185804\n\ + mov r0, r9\n\ + ldr r1, [r0]\n\ + ldr r2, =gPartnerTrainerId\n\ + ldrh r0, [r2]\n\ + ldr r3, =0xfffffed4\n\ + adds r0, r3\n\ + movs r2, 0xEC\n\ + muls r0, r2\n\ + adds r1, r0\n\ + ldr r4, =0x00000739\n\ + adds r1, r4\n\ + ldrb r1, [r1]\n\ + ldr r5, =0x0000050c\n\ + adds r0, r7, r5\n\ + strb r1, [r0]\n\ + mov r0, r9\n\ + ldr r1, [r0]\n\ + ldr r4, =gPartnerTrainerId\n\ + ldrh r0, [r4]\n\ + adds r0, r3\n\ + muls r0, r2\n\ + adds r1, r0\n\ + ldr r5, =0x0000081c\n\ + adds r1, r5\n\ + ldrb r1, [r1]\n\ +_08185856:\n\ + ldr r2, =0x0000051a\n\ + adds r0, r7, r2\n\ + strb r1, [r0]\n\ +_0818585C:\n\ + ldr r3, =gTrainerBattleOpponent_A\n\ + ldrh r0, [r3]\n\ + ldr r1, =0x0000018f\n\ + cmp r0, r1\n\ + bls _08185900\n\ + mov r4, r9\n\ + ldr r2, [r4]\n\ + adds r1, r0, 0\n\ + ldr r3, =0xfffffe70\n\ + adds r1, r3\n\ + lsls r0, r1, 4\n\ + adds r0, r1\n\ + lsls r0, 2\n\ + adds r2, r0\n\ + adds r2, 0xDC\n\ + ldrb r0, [r2]\n\ + lsls r0, 27\n\ + lsrs r0, 27\n\ + ldr r5, =0x0000050d\n\ + adds r1, r7, r5\n\ + strb r0, [r1]\n\ + movs r6, 0\n\ + ldr r0, =0x0000050e\n\ + adds r4, r7, r0\n\ + mov r10, r9\n\ + ldr r1, =gTrainerBattleOpponent_A\n\ + mov r8, r1\n\ + adds r5, r3, 0\n\ +_08185894:\n\ + lsls r3, r6, 1\n\ + mov r0, r10\n\ + ldr r2, [r0]\n\ + mov r0, r8\n\ + ldrh r1, [r0]\n\ + adds r1, r5\n\ + lsls r0, r1, 4\n\ + adds r0, r1\n\ + lsls r0, 2\n\ + adds r3, r0\n\ + movs r1, 0x82\n\ + lsls r1, 1\n\ + adds r2, r1\n\ + adds r2, r3\n\ + ldrh r0, [r2]\n\ + strh r0, [r4]\n\ + adds r4, 0x2\n\ + adds r6, 0x1\n\ + cmp r6, 0x5\n\ + ble _08185894\n\ + mov r3, r9\n\ + ldr r2, [r3]\n\ + ldr r4, =gTrainerBattleOpponent_A\n\ + ldrh r1, [r4]\n\ + ldr r5, =0xfffffe70\n\ + adds r1, r5\n\ + b _081859AC\n\ + .pool\n\ +_08185900:\n\ + ldr r3, =gTrainerBattleOpponent_B\n\ + ldrh r0, [r3]\n\ + cmp r0, r1\n\ + bls _0818597C\n\ + mov r4, r9\n\ + ldr r2, [r4]\n\ + adds r1, r0, 0\n\ + ldr r3, =0xfffffe70\n\ + adds r1, r3\n\ + lsls r0, r1, 4\n\ + adds r0, r1\n\ + lsls r0, 2\n\ + adds r2, r0\n\ + adds r2, 0xDC\n\ + ldrb r0, [r2]\n\ + lsls r0, 27\n\ + lsrs r0, 27\n\ + ldr r5, =0x0000050d\n\ + adds r1, r7, r5\n\ + strb r0, [r1]\n\ + movs r6, 0\n\ + ldr r0, =0x0000050e\n\ + adds r4, r7, r0\n\ + mov r10, r9\n\ + ldr r1, =gTrainerBattleOpponent_B\n\ + mov r8, r1\n\ + adds r5, r3, 0\n\ +_08185936:\n\ + lsls r3, r6, 1\n\ + mov r0, r10\n\ + ldr r2, [r0]\n\ + mov r0, r8\n\ + ldrh r1, [r0]\n\ + adds r1, r5\n\ + lsls r0, r1, 4\n\ + adds r0, r1\n\ + lsls r0, 2\n\ + adds r3, r0\n\ + movs r1, 0x82\n\ + lsls r1, 1\n\ + adds r2, r1\n\ + adds r2, r3\n\ + ldrh r0, [r2]\n\ + strh r0, [r4]\n\ + adds r4, 0x2\n\ + adds r6, 0x1\n\ + cmp r6, 0x5\n\ + ble _08185936\n\ + mov r3, r9\n\ + ldr r2, [r3]\n\ + ldr r4, =gTrainerBattleOpponent_B\n\ + ldrh r1, [r4]\n\ + ldr r5, =0xfffffe70\n\ + adds r1, r5\n\ + b _081859AC\n\ + .pool\n\ +_0818597C:\n\ + ldr r3, =gPartnerTrainerId\n\ + ldrh r0, [r3]\n\ + cmp r0, r1\n\ + bls _081859C0\n\ + mov r4, r9\n\ + ldr r2, [r4]\n\ + adds r1, r0, 0\n\ + ldr r3, =0xfffffe70\n\ + adds r1, r3\n\ + lsls r0, r1, 4\n\ + adds r0, r1\n\ + lsls r0, 2\n\ + adds r2, r0\n\ + adds r2, 0xDC\n\ + ldrb r0, [r2]\n\ + lsls r0, 27\n\ + lsrs r0, 27\n\ + ldr r5, =0x0000050d\n\ + adds r1, r7, r5\n\ + strb r0, [r1]\n\ + ldr r2, [r4]\n\ + ldr r0, =gPartnerTrainerId\n\ + ldrh r1, [r0]\n\ + adds r1, r3\n\ +_081859AC:\n\ + lsls r0, r1, 4\n\ + adds r0, r1\n\ + lsls r0, 2\n\ + adds r2, r0\n\ + ldr r1, =0x0000011b\n\ + adds r0, r2, r1\n\ + ldrb r1, [r0]\n\ + ldr r2, =0x0000051b\n\ + adds r0, r7, r2\n\ + strb r1, [r0]\n\ +_081859C0:\n\ + movs r6, 0\n\ + ldr r3, =0x00000297\n\ + mov r10, r3\n\ + ldr r4, =sBattleRecords\n\ + mov r9, r4\n\ + movs r5, 0xA6\n\ + lsls r5, 2\n\ + mov r8, r5\n\ + ldr r0, =0x0000051c\n\ + adds r5, r7, r0\n\ +_081859D4:\n\ + adds r4, r6, 0x1\n\ + mov r0, r8\n\ + muls r0, r6\n\ + mov r1, r9\n\ + adds r2, r0, r1\n\ + adds r1, r0, r5\n\ + mov r3, r10\n\ + adds r3, 0x1\n\ +_081859E4:\n\ + ldrb r0, [r2]\n\ + strb r0, [r1]\n\ + adds r2, 0x1\n\ + adds r1, 0x1\n\ + subs r3, 0x1\n\ + cmp r3, 0\n\ + bne _081859E4\n\ + adds r6, r4, 0\n\ + cmp r6, 0x3\n\ + ble _081859D4\n\ +_081859F8:\n\ + adds r0, r7, 0\n\ + ldr r1, [sp]\n\ + bl sub_81852F0\n\ + adds r4, r0, 0\n\ + cmp r4, 0x1\n\ + beq _08185A14\n\ + ldr r0, [sp, 0x4]\n\ + adds r0, 0x1\n\ + lsls r0, 24\n\ + lsrs r0, 24\n\ + str r0, [sp, 0x4]\n\ + cmp r0, 0x2\n\ + bls _081859F8\n\ +_08185A14:\n\ + adds r0, r7, 0\n\ + bl Free\n\ + ldr r0, [sp]\n\ + bl Free\n\ + adds r0, r4, 0\n\ + add sp, 0xC\n\ + pop {r3-r5}\n\ + mov r8, r3\n\ + mov r9, r4\n\ + mov r10, r5\n\ + pop {r4-r7}\n\ + pop {r1}\n\ + bx r1\n\ + .pool\n\ + .syntax divided"); +} +#endif // NONMATCHING + +static bool32 TryCopyRecordedBattleSaveData(struct RecordedBattleSave *dst, struct SaveSection *saveBuffer) +{ + if (TryCopySpecialSaveSection(SECTION_ID_RECORDED_BATTLE, (void*)(saveBuffer)) != 1) + return FALSE; + + memcpy(dst, saveBuffer, sizeof(struct RecordedBattleSave)); + + if (!IsRecordedBattleSaveValid(dst)) + return FALSE; + + return TRUE; +} + +static bool32 AllocTryCopyRecordedBattleSaveData(struct RecordedBattleSave *dst) +{ + struct SaveSection *savBuffer = AllocZeroed(sizeof(struct SaveSection)); + bool32 ret = TryCopyRecordedBattleSaveData(dst, savBuffer); + Free(savBuffer); + + return ret; +} + +static void CB2_RecordedBattleEnd(void) +{ + gSaveBlock2Ptr->frontierChosenLvl = sUnknown_0203C7AD; + gBattleOutcome = 0; + gBattleTypeFlags = 0; + gTrainerBattleOpponent_A = 0; + gTrainerBattleOpponent_B = 0; + gPartnerTrainerId = 0; + + RecordedBattle_RestoreSavedParties(); + SetMainCallback2(sCallback2_AfterRecordedBattle); +} + +#define tFramesToWait data[0] + +static void Task_StartAfterCountdown(u8 taskId) +{ + if (--gTasks[taskId].tFramesToWait == 0) + { + gMain.savedCallback = CB2_RecordedBattleEnd; + SetMainCallback2(CB2_InitBattle); + DestroyTask(taskId); + } +} + +static void SetRecordedBattleVarsFromSave(struct RecordedBattleSave *src) +{ + bool8 var; + s32 i, j; + + ZeroPlayerPartyMons(); + ZeroEnemyPartyMons(); + + for (i = 0; i < PARTY_SIZE; i++) + { + gPlayerParty[i] = src->playerParty[i]; + gEnemyParty[i] = src->opponentParty[i]; + } + + for (i = 0; i < BATTLE_BANKS_COUNT; i++) + { + for (var = FALSE, j = 0; j < PLAYER_NAME_LENGTH; j++) + { + gLinkPlayers[i].name[j] = src->playersName[i][j]; + if (src->playersName[i][j] == EOS) + var = TRUE; + } + gLinkPlayers[i].gender = src->playersGender[i]; + gLinkPlayers[i].language = src->playersLanguage[i]; + gLinkPlayers[i].lp_field_18 = src->playersBank[i]; + gLinkPlayers[i].trainerId = src->playersTrainerId[i]; + + if (var) + ConvertInternationalString(gLinkPlayers[i].name, gLinkPlayers[i].language); + } + + gRecordedBattleRngSeed = src->rngSeed; + gBattleTypeFlags = src->battleFlags | BATTLE_TYPE_RECORDED; + gTrainerBattleOpponent_A = src->opponentA; + gTrainerBattleOpponent_B = src->opponentB; + gPartnerTrainerId = src->partnerId; + gUnknown_0203C7B4 = src->field_4FA; + sUnknown_0203C7AD = gSaveBlock2Ptr->frontierChosenLvl; + sUnknown_0203C7AE = src->field_4FD; + sUnknown_0203C7AF = src->field_4FE; + sRecordedBattle_BattleStyle = src->battleStyle; + sRecordedBattle_TextSpeed = src->textSpeed; + sRecordedBattle_AI_Scripts = src->AI_scripts; + + for (i = 0; i < 8; i++) + { + sUnknown_0203CCD1[i] = src->field_504[i]; + } + + sUnknown_0203CCD9 = src->field_50C; + sUnknown_0203CCDA = src->field_50D; + gUnknown_03001278 = src->field_51A; + gUnknown_03001279 = src->field_51B; + + for (i = 0; i < 6; i++) + { + sUnknown_0203CCDC[i] = src->field_50E[i]; + } + + gSaveBlock2Ptr->frontierChosenLvl = src->field_4FC; + + for (i = 0; i < BATTLE_BANKS_COUNT; i++) + { + for (j = 0; j < BANK_RECORD_SIZE; j++) + { + sBattleRecords[i][j] = src->battleRecord[i][j]; + } + } +} + +void PlayRecordedBattle(void (*CB2_After)(void)) +{ + struct RecordedBattleSave *battleSave = AllocZeroed(sizeof(struct RecordedBattleSave)); + if (AllocTryCopyRecordedBattleSaveData(battleSave) == TRUE) + { + u8 taskId; + + RecordedBattle_SaveParties(); + SetRecordedBattleVarsFromSave(battleSave); + + taskId = CreateTask(Task_StartAfterCountdown, 1); + gTasks[taskId].tFramesToWait = 128; + + sCallback2_AfterRecordedBattle = CB2_After; + PlayMapChosenOrBattleBGM(FALSE); + SetMainCallback2(CB2_RecordedBattle); + } + Free(battleSave); +} + +#undef tFramesToWait + +static void CB2_RecordedBattle(void) +{ + AnimateSprites(); + BuildOamBuffer(); + RunTasks(); +} + +u8 sub_8185EA0(void) +{ + return sUnknown_0203C7AE; +} + +u8 sub_8185EAC(void) +{ + return sUnknown_0203C7AF; +} + +void RecordedBattle_SaveParties(void) +{ + s32 i; + + for (i = 0; i < PARTY_SIZE; i++) + { + sSavedPlayerParty[i] = gPlayerParty[i]; + sSavedOpponentParty[i] = gEnemyParty[i]; + } +} + +static void RecordedBattle_RestoreSavedParties(void) +{ + s32 i; + + for (i = 0; i < PARTY_SIZE; i++) + { + gPlayerParty[i] = sSavedPlayerParty[i]; + gEnemyParty[i] = sSavedOpponentParty[i]; + } +} + +u8 GetActiveBankLinkPlayerGender(void) +{ + s32 i; + + for (i = 0; i < MAX_LINK_PLAYERS; i++) + { + if (gLinkPlayers[i].lp_field_18 == gActiveBank) + break; + } + + if (i != MAX_LINK_PLAYERS) + return gLinkPlayers[i].gender; + + return 0; +} + +void sub_8185F84(void) +{ + sUnknown_0203C7B5 = 0; +} + +void sub_8185F90(u16 arg0) +{ + sUnknown_0203C7B5 |= (arg0 & 0x8000) >> 0xF; +} + +u8 sub_8185FAC(void) +{ + return sUnknown_0203C7B5; +} + +u8 GetBattleStyleInRecordedBattle(void) +{ + return sRecordedBattle_BattleStyle; +} + +u8 GetTextSpeedInRecordedBattle(void) +{ + return sRecordedBattle_TextSpeed; +} + +void RecordedBattle_CopyBankMoves(void) +{ + s32 i; + + if (GetBankSide(gActiveBank) == SIDE_OPPONENT) + return; + if (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_x2000000)) + return; + if (sUnknown_0203C7AC == 2) + return; + + for (i = 0; i < 4; i++) + { + sRecordedBattle_PlayerMonMoves[gActiveBank / 2][i] = gBattleMons[gActiveBank].moves[i]; + } +} + +#define ACTION_MOVE_CHANGE 6 + +void sub_818603C(u8 arg0) +{ + s32 bank, j, k; + + if (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_x2000000)) + return; + + for (bank = 0; bank < gNoOfAllBanks; bank++) + { + if (GetBankSide(bank) != SIDE_OPPONENT) // player's side only + { + if (arg0 == 1) + { + for (j = 0; j < 4; j++) + { + if (gBattleMons[bank].moves[j] != sRecordedBattle_PlayerMonMoves[bank / 2][j]) + break; + } + if (j != 4) // player's mon's move has been changed + { + RecordedBattle_SetBankAction(bank, ACTION_MOVE_CHANGE); + for (j = 0; j < 4; j++) + { + for (k = 0; k < 4; k++) + { + if (gBattleMons[bank].moves[j] == sRecordedBattle_PlayerMonMoves[bank / 2][k]) + { + RecordedBattle_SetBankAction(bank, k); + break; + } + } + } + } + } + else + { + if (sBattleRecords[bank][sRecordedBytesNo[bank]] == ACTION_MOVE_CHANGE) + { + u8 ppBonuses[4]; + u8 array1[4]; + u8 array2[4]; + struct MovePp movePp; + u8 array3[8]; + u8 var; + + RecordedBattle_ReadBankAction(bank); + for (j = 0; j < 4; j++) + { + ppBonuses[j] = ((gBattleMons[bank].ppBonuses & ((3 << (j << 1)))) >> (j << 1)); + } + for (j = 0; j < 4; j++) + { + array1[j] = RecordedBattle_ReadBankAction(bank); + movePp.moves[j] = gBattleMons[bank].moves[array1[j]]; + movePp.pp[j] = gBattleMons[bank].pp[array1[j]]; + array3[j] = ppBonuses[array1[j]]; + array2[j] = (gDisableStructs[bank].unk18_b & gBitTable[j]) >> j; + } + for (j = 0; j < 4; j++) + { + gBattleMons[bank].moves[j] = movePp.moves[j]; + gBattleMons[bank].pp[j] = movePp.pp[j]; + } + gBattleMons[bank].ppBonuses = 0; + gDisableStructs[bank].unk18_b = 0; + for (j = 0; j < 4; j++) + { + gBattleMons[bank].ppBonuses |= (array3[j]) << (j << 1); + gDisableStructs[bank].unk18_b |= (array2[j]) << (j); + } + + if (!(gBattleMons[bank].status2 & STATUS2_TRANSFORMED)) + { + for (j = 0; j < 4; j++) + { + ppBonuses[j] = ((GetMonData(&gPlayerParty[gBattlePartyID[bank]], MON_DATA_PP_BONUSES, NULL) & ((3 << (j << 1)))) >> (j << 1)); + } + for (j = 0; j < 4; j++) + { + movePp.moves[j] = GetMonData(&gPlayerParty[gBattlePartyID[bank]], MON_DATA_MOVE1 + array1[j], NULL); + movePp.pp[j] = GetMonData(&gPlayerParty[gBattlePartyID[bank]], MON_DATA_PP1 + array1[j], NULL); + array3[j] = ppBonuses[array1[j]]; + } + for (j = 0; j < 4; j++) + { + SetMonData(&gPlayerParty[gBattlePartyID[bank]], MON_DATA_MOVE1 + j, &movePp.moves[j]); + SetMonData(&gPlayerParty[gBattlePartyID[bank]], MON_DATA_PP1 + j, &movePp.pp[j]); + } + var = 0; + for (j = 0; j < 4; j++) + { + var |= (array3[j]) << (j << 1); + } + SetMonData(&gPlayerParty[gBattlePartyID[bank]], MON_DATA_PP_BONUSES, &var); + } + + gChosenMovesByBanks[bank] = gBattleMons[bank].moves[*(gBattleStruct->chosenMovePositions + bank)]; + } + } + } + } +} + +u32 GetAiScriptsInRecordedBattle(void) +{ + return sRecordedBattle_AI_Scripts; +} + +void sub_8186444(void) +{ + sUnknown_0203CCD0 = 1; +} + +bool8 sub_8186450(void) +{ + return (sUnknown_0203CCD0 == 0); +} + +void sub_8186468(u8 *dst) +{ + s32 i; + + for (i = 0; i < 8; i++) + dst[i] = sUnknown_0203CCD1[i]; + + dst[7] = EOS; + ConvertInternationalString(dst, gUnknown_03001278); +} + +u8 sub_818649C(void) +{ + return sUnknown_0203CCD9; +} + +u8 sub_81864A8(void) +{ + return sUnknown_0203CCDA; +} + +u8 sub_81864B4(void) +{ + return gUnknown_03001278; +} + +u8 sub_81864C0(void) +{ + return gUnknown_03001279; +} + +void sub_81864CC(void) +{ + sUnknown_0203CCE8 = gBattleOutcome; +} + +u16 *sub_81864E0(void) +{ + return sUnknown_0203CCDC; +} diff --git a/src/save.c b/src/save.c index 2c1b26ca9..528c67145 100644 --- a/src/save.c +++ b/src/save.c @@ -797,7 +797,7 @@ u16 sub_815355C(void) return 0; } -u32 sub_81535DC(u8 sector, u8* dst) +u32 TryCopySpecialSaveSection(u8 sector, u8* dst) { s32 i; s32 size; @@ -826,6 +826,7 @@ u32 sub_8153634(u8 sector, u8* src) if (sector != 30 && sector != 31) return 0xFF; + savDataBuffer = &gSaveDataBuffer; *(u32*)(savDataBuffer) = 0xB39D; diff --git a/src/secret_base.c b/src/secret_base.c index 89e276e80..de2b7221a 100644 --- a/src/secret_base.c +++ b/src/secret_base.c @@ -1099,9 +1099,9 @@ void game_continue(u8 taskId) } gUnknown_03006310 = gUnknown_0858D07C; gUnknown_03006310.unk_10 = data[6]; - gUnknown_03006310.unk_0c = data[0]; - gUnknown_03006310.unk_00 = gUnknown_0203A020->items; - gUnknown_03006310.unk_0e = data[3]; + gUnknown_03006310.totalItems = data[0]; + gUnknown_03006310.items = gUnknown_0203A020->items; + gUnknown_03006310.maxShowed = data[3]; } void sub_80E9DEC(u32 a0, bool8 flag, struct ListMenu *menu) @@ -1866,7 +1866,7 @@ void sub_80EAF80(void *records, size_t recordSize, u8 linkIdx) { struct SecretBaseRecordMixer mixers[3]; u16 i; - + if (FlagGet(0x60)) { switch (GetLinkPlayerCount()) |