pokeemerald/src/record_mixing.c

1409 lines
48 KiB
C
Raw Normal View History

2017-10-03 04:02:22 +02:00
#include "global.h"
#include "malloc.h"
#include "random.h"
#include "constants/items.h"
2017-11-07 05:03:11 +01:00
#include "text.h"
2017-11-07 05:20:11 +01:00
#include "item.h"
2017-11-04 01:59:29 +01:00
#include "task.h"
2017-11-07 05:33:39 +01:00
#include "save.h"
#include "load_save.h"
2017-11-04 23:48:13 +01:00
#include "pokemon.h"
2017-11-04 01:59:29 +01:00
#include "cable_club.h"
2017-11-04 02:56:18 +01:00
#include "link.h"
2018-05-20 10:51:09 +02:00
#include "link_rfu.h"
2017-11-04 02:56:18 +01:00
#include "tv.h"
#include "battle_tower.h"
2017-11-04 15:15:58 +01:00
#include "window.h"
2017-11-04 02:56:18 +01:00
#include "mystery_event_script.h"
2017-11-04 03:07:36 +01:00
#include "secret_base.h"
#include "mauville_old_man.h"
2017-11-04 15:15:58 +01:00
#include "sound.h"
#include "constants/songs.h"
#include "menu.h"
2017-11-04 15:15:58 +01:00
#include "overworld.h"
2018-12-08 22:05:11 +01:00
#include "field_screen_effect.h"
2018-12-20 04:19:54 +01:00
#include "fldeff_misc.h"
2017-11-04 15:15:58 +01:00
#include "script.h"
#include "event_data.h"
2017-11-05 00:03:41 +01:00
#include "lilycove_lady.h"
2017-11-04 15:15:58 +01:00
#include "strings.h"
2017-11-04 15:55:39 +01:00
#include "string_util.h"
2017-11-04 01:59:29 +01:00
#include "record_mixing.h"
2018-05-19 14:30:41 +02:00
#include "new_game.h"
2018-05-25 21:00:41 +02:00
#include "daycare.h"
#include "international_string_util.h"
2018-10-28 21:11:53 +01:00
#include "constants/battle_frontier.h"
2019-03-02 09:18:08 +01:00
#include "dewford_trend.h"
2017-10-03 04:02:22 +02:00
2021-10-23 16:55:46 +02:00
// Number of bytes of the record transferred at a time
#define BUFFER_CHUNK_SIZE 200
#define NUM_SWAP_COMBOS 3
2018-05-26 00:25:36 +02:00
2021-10-23 16:55:46 +02:00
// Used by several tasks in this file
#define tState data[0]
2017-10-03 04:02:22 +02:00
2018-10-28 21:11:53 +01:00
struct RecordMixingHallRecords
2018-05-26 00:25:36 +02:00
{
2021-10-23 16:55:46 +02:00
struct RankingHall1P hallRecords1P[HALL_FACILITIES_COUNT][FRONTIER_LVL_MODE_COUNT][HALL_RECORDS_COUNT * 2];
struct RankingHall2P hallRecords2P[FRONTIER_LVL_MODE_COUNT][HALL_RECORDS_COUNT * 2];
2018-05-26 00:25:36 +02:00
};
2021-10-23 16:55:46 +02:00
struct PlayerRecordRS
2018-05-22 21:54:57 +02:00
{
2019-04-05 23:11:24 +02:00
struct SecretBase secretBases[SECRET_BASES_COUNT];
2018-05-26 00:25:36 +02:00
TVShow tvShows[TV_SHOWS_COUNT];
PokeNews pokeNews[POKE_NEWS_COUNT];
2017-11-07 05:20:11 +01:00
OldMan oldMan;
2021-03-31 21:53:01 +02:00
struct DewfordTrend dewfordTrends[SAVED_TRENDS_COUNT];
2021-04-25 23:22:45 +02:00
struct RecordMixingDaycareMail daycareMail;
2017-11-07 05:20:11 +01:00
struct RSBattleTowerRecord battleTowerRecord;
2018-10-22 19:22:57 +02:00
u16 giftItem;
2021-10-23 16:55:46 +02:00
u16 padding[50];
2017-11-07 05:20:11 +01:00
};
2021-10-23 16:55:46 +02:00
struct PlayerRecordEmerald
2018-05-22 21:54:57 +02:00
{
2019-04-05 23:11:24 +02:00
/* 0x0000 */ struct SecretBase secretBases[SECRET_BASES_COUNT];
2018-05-26 00:25:36 +02:00
/* 0x0c80 */ TVShow tvShows[TV_SHOWS_COUNT];
/* 0x1004 */ PokeNews pokeNews[POKE_NEWS_COUNT];
2017-11-04 14:39:41 +01:00
/* 0x1044 */ OldMan oldMan;
2021-03-31 21:53:01 +02:00
/* 0x1084 */ struct DewfordTrend dewfordTrends[SAVED_TRENDS_COUNT];
2021-04-25 23:22:45 +02:00
/* 0x10ac */ struct RecordMixingDaycareMail daycareMail;
2018-05-26 00:25:36 +02:00
/* 0x1124 */ struct EmeraldBattleTowerRecord battleTowerRecord;
2018-10-22 19:22:57 +02:00
/* 0x1210 */ u16 giftItem;
2017-11-04 14:39:41 +01:00
/* 0x1214 */ LilycoveLady lilycoveLady;
2019-11-21 01:00:08 +01:00
/* 0x1254 */ struct Apprentice apprentices[2];
2018-10-28 21:11:53 +01:00
/* 0x12dc */ struct PlayerHallRecords hallRecords;
2021-10-23 16:55:46 +02:00
/* 0x1434 */ u8 padding[16];
2018-05-26 00:25:36 +02:00
}; // 0x1444
2021-10-23 16:55:46 +02:00
union PlayerRecord
2018-05-26 00:25:36 +02:00
{
2021-10-23 16:55:46 +02:00
struct PlayerRecordRS ruby;
struct PlayerRecordEmerald emerald;
2018-05-26 00:25:36 +02:00
};
2017-11-04 02:56:18 +01:00
2021-10-23 16:55:46 +02:00
static bool8 sReadyToReceive;
static struct SecretBase *sSecretBasesSave;
static TVShow *sTvShowsSave;
static PokeNews *sPokeNewsSave;
static OldMan *sOldManSave;
2021-03-31 21:53:01 +02:00
static struct DewfordTrend *sDewfordTrendsSave;
2021-10-23 16:55:46 +02:00
static struct RecordMixingDaycareMail *sRecordMixMailSave;
static void *sBattleTowerSave;
static LilycoveLady *sLilycoveLadySave;
static void *sApprenticesSave;
static void *sBattleTowerSave_Duplicate;
static u32 sRecordStructSize;
2021-10-23 16:55:46 +02:00
static u8 sDaycareMailRandSum;
2021-10-24 21:49:45 +02:00
static struct PlayerHallRecords *sPartnerHallRecords[HALL_RECORDS_COUNT];
2018-05-26 00:25:36 +02:00
2021-10-23 16:55:46 +02:00
static EWRAM_DATA struct RecordMixingDaycareMail sRecordMixMail = {0};
static EWRAM_DATA union PlayerRecord *sReceivedRecords = NULL;
static EWRAM_DATA union PlayerRecord *sSentRecord = NULL;
static void Task_RecordMixing_Main(u8);
static void Task_MixingRecordsRecv(u8);
static void Task_SendPacket(u8);
static void Task_CopyReceiveBuffer(u8);
static void Task_SendPacket_SwitchToReceive(u8);
static void *LoadPtrFromTaskData(const u16 *);
static void StorePtrInTaskData(void *, u16 *);
2018-05-26 00:25:36 +02:00
static u8 GetMultiplayerId_(void);
static void *GetPlayerRecvBuffer(u8);
static void ReceiveOldManData(OldMan *, size_t, u8);
2021-10-23 16:55:46 +02:00
static void ReceiveBattleTowerData(void *, size_t, u8);
2018-05-26 00:25:36 +02:00
static void ReceiveLilycoveLadyData(LilycoveLady *, size_t, u8);
2021-10-23 16:55:46 +02:00
static void CalculateDaycareMailRandSum(const u8 *);
2021-04-25 23:22:45 +02:00
static void ReceiveDaycareMailData(struct RecordMixingDaycareMail *, size_t, u8, TVShow *);
2021-10-23 16:55:46 +02:00
static void ReceiveGiftItem(u16 *, u8 );
static void Task_DoRecordMixing(u8);
static void GetSavedApprentices(struct Apprentice *, struct Apprentice *);
static void ReceiveApprenticeData(struct Apprentice *, size_t, u32);
static void ReceiveRankingHallRecords(struct PlayerHallRecords *, size_t, u32);
static void GetRecordMixingDaycareMail(struct RecordMixingDaycareMail *);
static void SanitizeDaycareMailForRuby(struct RecordMixingDaycareMail *);
static void SanitizeEmeraldBattleTowerRecord(struct EmeraldBattleTowerRecord *);
static void SanitizeRubyBattleTowerRecord(struct RSBattleTowerRecord *);
static const u8 sPlayerIdxOrders_2Player[] = {1, 0};
static const u8 sPlayerIdxOrders_3Player[][3] =
2018-05-25 21:00:41 +02:00
{
{1, 2, 0},
{2, 0, 1},
};
2021-10-23 16:55:46 +02:00
static const u8 sPlayerIdxOrders_4Player[][4] =
2018-05-25 21:00:41 +02:00
{
{1, 0, 3, 2},
{3, 0, 1, 2},
{2, 0, 3, 1},
{1, 3, 0, 2},
{2, 3, 0, 1},
{3, 2, 0, 1},
{1, 2, 3, 0},
{2, 3, 1, 0},
{3, 2, 1, 0},
};
2017-11-04 17:03:50 +01:00
2021-10-23 16:55:46 +02:00
// When 3 players can swap mail 2 players are randomly selected and the 3rd is left out
static const u8 sDaycareMailSwapIds_3Player[NUM_SWAP_COMBOS][2] =
2018-05-25 21:00:41 +02:00
{
{0, 1},
{1, 2},
{2, 0},
};
2021-10-23 16:55:46 +02:00
static const u8 sDaycareMailSwapIds_4Player[NUM_SWAP_COMBOS][4] =
2018-05-25 21:00:41 +02:00
{
2021-10-23 16:55:46 +02:00
{0, 1, 2, 3}, // 0 swaps with 1, 2 swaps with 3
{0, 2, 1, 3},
{0, 3, 2, 1},
2018-05-25 21:00:41 +02:00
};
2017-11-07 05:03:11 +01:00
2019-02-18 07:03:44 +01:00
void RecordMixingPlayerSpotTriggered(void)
2017-11-04 01:59:29 +01:00
{
2020-06-04 00:00:53 +02:00
CreateTask_EnterCableClubSeat(Task_RecordMixing_Main);
2017-11-04 01:59:29 +01:00
}
2018-05-26 00:25:36 +02:00
// these variables were const in R/S, but had to become changeable because of saveblocks changing RAM position
static void SetSrcLookupPointers(void)
2017-11-04 01:59:29 +01:00
{
2018-05-26 00:25:36 +02:00
sSecretBasesSave = gSaveBlock1Ptr->secretBases;
sTvShowsSave = gSaveBlock1Ptr->tvShows;
sPokeNewsSave = gSaveBlock1Ptr->pokeNews;
sOldManSave = &gSaveBlock1Ptr->oldMan;
2021-03-31 21:53:01 +02:00
sDewfordTrendsSave = gSaveBlock1Ptr->dewfordTrends;
2021-10-23 16:55:46 +02:00
sRecordMixMailSave = &sRecordMixMail;
2018-10-21 20:13:12 +02:00
sBattleTowerSave = &gSaveBlock2Ptr->frontier.towerPlayer;
2018-05-26 00:25:36 +02:00
sLilycoveLadySave = &gSaveBlock1Ptr->lilycoveLady;
2018-10-28 21:11:53 +01:00
sApprenticesSave = gSaveBlock2Ptr->apprentices;
2018-10-21 20:13:12 +02:00
sBattleTowerSave_Duplicate = &gSaveBlock2Ptr->frontier.towerPlayer;
2017-11-04 01:59:29 +01:00
}
2017-11-04 02:56:18 +01:00
2021-10-23 16:55:46 +02:00
static void PrepareUnknownExchangePacket(struct PlayerRecordRS *dest)
2017-11-04 02:56:18 +01:00
{
2018-05-26 00:25:36 +02:00
memcpy(dest->secretBases, sSecretBasesSave, sizeof(dest->secretBases));
memcpy(dest->tvShows, sTvShowsSave, sizeof(dest->tvShows));
2021-04-25 23:22:45 +02:00
SanitizeTVShowLocationsForRuby(dest->tvShows);
2018-05-26 00:25:36 +02:00
memcpy(dest->pokeNews, sPokeNewsSave, sizeof(dest->pokeNews));
memcpy(&dest->oldMan, sOldManSave, sizeof(dest->oldMan));
2021-03-31 21:53:01 +02:00
memcpy(dest->dewfordTrends, sDewfordTrendsSave, sizeof(dest->dewfordTrends));
2021-04-25 23:22:45 +02:00
GetRecordMixingDaycareMail(&dest->daycareMail);
EmeraldBattleTowerRecordToRuby(sBattleTowerSave, &dest->battleTowerRecord);
2018-05-26 00:25:36 +02:00
2017-11-04 02:56:18 +01:00
if (GetMultiplayerId() == 0)
2018-10-22 19:22:57 +02:00
dest->giftItem = GetRecordMixingGift();
2017-11-04 02:56:18 +01:00
}
2017-11-04 03:07:36 +01:00
2021-10-23 16:55:46 +02:00
static void PrepareExchangePacketForRubySapphire(struct PlayerRecordRS *dest)
2017-11-04 03:07:36 +01:00
{
2018-05-26 00:25:36 +02:00
memcpy(dest->secretBases, sSecretBasesSave, sizeof(dest->secretBases));
2019-04-05 23:11:24 +02:00
ClearJapaneseSecretBases(dest->secretBases);
2018-05-26 00:25:36 +02:00
memcpy(dest->tvShows, sTvShowsSave, sizeof(dest->tvShows));
2021-04-25 23:22:45 +02:00
SanitizeTVShowsForRuby(dest->tvShows);
2018-05-26 00:25:36 +02:00
memcpy(dest->pokeNews, sPokeNewsSave, sizeof(dest->pokeNews));
memcpy(&dest->oldMan, sOldManSave, sizeof(dest->oldMan));
2021-04-25 23:22:45 +02:00
SanitizeMauvilleOldManForRuby(&dest->oldMan);
2021-03-31 21:53:01 +02:00
memcpy(dest->dewfordTrends, sDewfordTrendsSave, sizeof(dest->dewfordTrends));
2021-04-25 23:22:45 +02:00
GetRecordMixingDaycareMail(&dest->daycareMail);
SanitizeDaycareMailForRuby(&dest->daycareMail);
EmeraldBattleTowerRecordToRuby(sBattleTowerSave, &dest->battleTowerRecord);
2018-05-26 00:25:36 +02:00
SanitizeRubyBattleTowerRecord(&dest->battleTowerRecord);
2017-11-04 03:07:36 +01:00
if (GetMultiplayerId() == 0)
2018-10-22 19:22:57 +02:00
dest->giftItem = GetRecordMixingGift();
2017-11-04 03:07:36 +01:00
}
2017-11-04 03:26:12 +01:00
2018-05-26 00:25:36 +02:00
static void PrepareExchangePacket(void)
2017-11-04 03:26:12 +01:00
{
2019-04-05 23:11:24 +02:00
SetPlayerSecretBaseParty();
2021-04-25 23:22:45 +02:00
DeactivateAllNormalTVShows();
2018-05-26 00:25:36 +02:00
SetSrcLookupPointers();
2017-11-04 17:20:40 +01:00
if (Link_AnyPartnersPlayingRubyOrSapphire())
2017-11-04 03:26:12 +01:00
{
if (LinkDummy_Return2() == 0)
2018-05-26 00:25:36 +02:00
PrepareUnknownExchangePacket(&sSentRecord->ruby);
2017-11-04 03:26:12 +01:00
else
2018-05-26 00:25:36 +02:00
PrepareExchangePacketForRubySapphire(&sSentRecord->ruby);
2017-11-04 03:26:12 +01:00
}
else
{
2018-05-26 00:25:36 +02:00
memcpy(sSentRecord->emerald.secretBases, sSecretBasesSave, sizeof(sSentRecord->emerald.secretBases));
memcpy(sSentRecord->emerald.tvShows, sTvShowsSave, sizeof(sSentRecord->emerald.tvShows));
memcpy(sSentRecord->emerald.pokeNews, sPokeNewsSave, sizeof(sSentRecord->emerald.pokeNews));
memcpy(&sSentRecord->emerald.oldMan, sOldManSave, sizeof(sSentRecord->emerald.oldMan));
memcpy(&sSentRecord->emerald.lilycoveLady, sLilycoveLadySave, sizeof(sSentRecord->emerald.lilycoveLady));
2021-03-31 21:53:01 +02:00
memcpy(sSentRecord->emerald.dewfordTrends, sDewfordTrendsSave, sizeof(sSentRecord->emerald.dewfordTrends));
2021-04-25 23:22:45 +02:00
GetRecordMixingDaycareMail(&sSentRecord->emerald.daycareMail);
2018-05-26 00:25:36 +02:00
memcpy(&sSentRecord->emerald.battleTowerRecord, sBattleTowerSave, sizeof(sSentRecord->emerald.battleTowerRecord));
SanitizeEmeraldBattleTowerRecord(&sSentRecord->emerald.battleTowerRecord);
2017-11-04 03:26:12 +01:00
if (GetMultiplayerId() == 0)
2018-10-22 19:22:57 +02:00
sSentRecord->emerald.giftItem = GetRecordMixingGift();
2018-05-26 00:25:36 +02:00
2019-11-21 01:00:08 +01:00
GetSavedApprentices(sSentRecord->emerald.apprentices, sApprenticesSave);
2018-10-28 21:11:53 +01:00
GetPlayerHallRecords(&sSentRecord->emerald.hallRecords);
2017-11-04 03:26:12 +01:00
}
}
2017-11-04 14:39:41 +01:00
2021-10-23 16:55:46 +02:00
static void ReceiveExchangePacket(u32 multiplayerId)
2017-11-04 14:39:41 +01:00
{
2017-11-04 17:20:40 +01:00
if (Link_AnyPartnersPlayingRubyOrSapphire())
2017-11-04 14:39:41 +01:00
{
// Ruby/Sapphire
2021-10-23 16:55:46 +02:00
CalculateDaycareMailRandSum((void *)sReceivedRecords->ruby.tvShows);
ReceiveSecretBasesData(sReceivedRecords->ruby.secretBases, sizeof(sReceivedRecords->ruby), multiplayerId);
ReceiveDaycareMailData(&sReceivedRecords->ruby.daycareMail, sizeof(sReceivedRecords->ruby), multiplayerId, sReceivedRecords->ruby.tvShows);
ReceiveBattleTowerData(&sReceivedRecords->ruby.battleTowerRecord, sizeof(sReceivedRecords->ruby), multiplayerId);
ReceiveTvShowsData(sReceivedRecords->ruby.tvShows, sizeof(sReceivedRecords->ruby), multiplayerId);
ReceivePokeNewsData(sReceivedRecords->ruby.pokeNews, sizeof(sReceivedRecords->ruby), multiplayerId);
ReceiveOldManData(&sReceivedRecords->ruby.oldMan, sizeof(sReceivedRecords->ruby), multiplayerId);
ReceiveDewfordTrendData(sReceivedRecords->ruby.dewfordTrends, sizeof(sReceivedRecords->ruby), multiplayerId);
ReceiveGiftItem(&sReceivedRecords->ruby.giftItem, multiplayerId);
2017-11-04 14:39:41 +01:00
}
else
{
// Emerald
2021-10-23 16:55:46 +02:00
CalculateDaycareMailRandSum((void *)sReceivedRecords->emerald.tvShows);
ReceiveSecretBasesData(sReceivedRecords->emerald.secretBases, sizeof(sReceivedRecords->emerald), multiplayerId);
ReceiveTvShowsData(sReceivedRecords->emerald.tvShows, sizeof(sReceivedRecords->emerald), multiplayerId);
ReceivePokeNewsData(sReceivedRecords->emerald.pokeNews, sizeof(sReceivedRecords->emerald), multiplayerId);
ReceiveOldManData(&sReceivedRecords->emerald.oldMan, sizeof(sReceivedRecords->emerald), multiplayerId);
ReceiveDewfordTrendData(sReceivedRecords->emerald.dewfordTrends, sizeof(sReceivedRecords->emerald), multiplayerId);
ReceiveDaycareMailData(&sReceivedRecords->emerald.daycareMail, sizeof(sReceivedRecords->emerald), multiplayerId, sReceivedRecords->emerald.tvShows);
ReceiveBattleTowerData(&sReceivedRecords->emerald.battleTowerRecord, sizeof(sReceivedRecords->emerald), multiplayerId);
ReceiveGiftItem(&sReceivedRecords->emerald.giftItem, multiplayerId);
ReceiveLilycoveLadyData(&sReceivedRecords->emerald.lilycoveLady, sizeof(sReceivedRecords->emerald), multiplayerId);
2021-10-24 21:49:45 +02:00
ReceiveApprenticeData(sReceivedRecords->emerald.apprentices, sizeof(sReceivedRecords->emerald), (u8)multiplayerId);
ReceiveRankingHallRecords(&sReceivedRecords->emerald.hallRecords, sizeof(sReceivedRecords->emerald), (u8)multiplayerId);
2017-11-04 14:39:41 +01:00
}
}
2017-11-04 15:15:58 +01:00
2018-05-26 00:25:36 +02:00
static void PrintTextOnRecordMixing(const u8 *src)
2017-11-04 15:15:58 +01:00
{
DrawDialogueFrame(0, 0);
2021-10-30 22:47:37 +02:00
AddTextPrinterParameterized(0, FONT_NORMAL, src, 0, 1, 0, NULL);
2021-11-03 20:29:18 +01:00
CopyWindowToVram(0, COPYWIN_FULL);
2017-11-04 15:15:58 +01:00
}
2018-05-26 00:25:36 +02:00
#define tCounter data[0]
static void Task_RecordMixing_SoundEffect(u8 taskId)
2017-11-04 15:15:58 +01:00
{
2018-05-26 00:25:36 +02:00
if (++gTasks[taskId].tCounter == 50)
2017-11-04 15:15:58 +01:00
{
2020-08-21 00:02:00 +02:00
PlaySE(SE_M_ATTRACT);
2018-05-26 00:25:36 +02:00
gTasks[taskId].tCounter = 0;
2017-11-04 15:15:58 +01:00
}
}
2018-05-26 00:25:36 +02:00
#undef tCounter
2021-10-23 16:55:46 +02:00
#define tTimer data[8]
#define tLinkTaskId data[10]
#define tSoundTaskId data[15]
2018-05-26 00:25:36 +02:00
2021-10-23 16:55:46 +02:00
// Note: gSpecialVar_0x8005 here contains the player's spot id.
2018-05-26 00:25:36 +02:00
static void Task_RecordMixing_Main(u8 taskId)
2017-11-04 15:15:58 +01:00
{
2018-05-26 00:25:36 +02:00
s16 *data = gTasks[taskId].data;
2017-11-04 15:15:58 +01:00
2018-05-26 00:25:36 +02:00
switch (tState)
2017-11-04 15:15:58 +01:00
{
2018-05-26 00:25:36 +02:00
case 0: // init
2021-10-23 16:55:46 +02:00
sSentRecord = malloc(sizeof(*sSentRecord));
sReceivedRecords = malloc(sizeof(*sReceivedRecords) * MAX_LINK_PLAYERS);
2019-03-01 07:18:58 +01:00
SetLocalLinkPlayerId(gSpecialVar_0x8005);
2018-05-26 00:25:36 +02:00
VarSet(VAR_TEMP_0, 1);
2021-10-23 16:55:46 +02:00
sReadyToReceive = FALSE;
2018-05-26 00:25:36 +02:00
PrepareExchangePacket();
2020-06-29 17:41:09 +02:00
CreateRecordMixingLights();
2018-05-26 00:25:36 +02:00
tState = 1;
2021-10-23 16:55:46 +02:00
tLinkTaskId = CreateTask(Task_MixingRecordsRecv, 80);
tSoundTaskId = CreateTask(Task_RecordMixing_SoundEffect, 81);
2018-05-26 00:25:36 +02:00
break;
case 1: // wait for Task_MixingRecordsRecv
2021-10-23 16:55:46 +02:00
if (!gTasks[tLinkTaskId].isActive)
2018-05-26 00:25:36 +02:00
{
tState = 2;
FlagSet(FLAG_SYS_MIX_RECORD);
2020-06-29 17:41:09 +02:00
DestroyRecordMixingLights();
2021-10-23 16:55:46 +02:00
DestroyTask(tSoundTaskId);
2018-05-26 00:25:36 +02:00
}
break;
case 2:
2021-10-23 16:55:46 +02:00
tLinkTaskId = CreateTask(Task_DoRecordMixing, 10);
2018-05-26 00:25:36 +02:00
tState = 3;
2020-08-21 00:02:00 +02:00
PlaySE(SE_M_BATON_PASS);
2018-05-26 00:25:36 +02:00
break;
case 3: // wait for Task_DoRecordMixing
2021-10-23 16:55:46 +02:00
if (!gTasks[tLinkTaskId].isActive)
2018-05-26 00:25:36 +02:00
{
tState = 4;
if (gWirelessCommType == 0)
2021-10-23 16:55:46 +02:00
tLinkTaskId = CreateTask_ReestablishCableClubLink();
2018-05-26 00:25:36 +02:00
PrintTextOnRecordMixing(gText_RecordMixingComplete);
2021-10-23 16:55:46 +02:00
tTimer = 0;
2018-05-26 00:25:36 +02:00
}
break;
case 4: // wait 60 frames
2021-10-23 16:55:46 +02:00
if (++tTimer > 60)
2018-05-26 00:25:36 +02:00
tState = 5;
break;
2021-10-23 16:55:46 +02:00
case 5: // Wait for the task created by CreateTask_ReestablishCableClubLink
if (!gTasks[tLinkTaskId].isActive)
2018-05-26 00:25:36 +02:00
{
free(sReceivedRecords);
free(sSentRecord);
2021-04-06 22:55:33 +02:00
SetLinkWaitingForScript();
2018-05-26 00:25:36 +02:00
if (gWirelessCommType != 0)
2019-12-17 09:24:44 +01:00
CreateTask(Task_ReturnToFieldRecordMixing, 10);
ClearDialogWindowAndFrame(0, 1);
2018-05-26 00:25:36 +02:00
DestroyTask(taskId);
EnableBothScriptContexts();
}
break;
2017-11-04 15:15:58 +01:00
}
}
2017-11-04 15:55:39 +01:00
2021-10-23 16:55:46 +02:00
#undef tTimer
#undef tLinkTaskId
#undef tSoundTaskId
// Task data for Task_MixingRecordsRecv and subsequent tasks
#define tSentRecord data[2] // Used to store a ptr, so data[2] and data[3]
#define tNumChunksSent data[4]
#define tMultiplayerId data[5]
#define tCopyTaskId data[10]
// Task data for Task_CopyReceiveBuffer
#define tParentTaskId data[0]
#define tNumChunksRecv(i) data[1 + (i)] // Number of chunks of the record received per player
#define tRecvRecords data[5] // Used to store a ptr, so data[5] and data[6]
2018-05-26 00:25:36 +02:00
static void Task_MixingRecordsRecv(u8 taskId)
2017-11-04 15:55:39 +01:00
{
2018-05-26 00:25:36 +02:00
struct Task *task = &gTasks[taskId];
2017-11-04 15:55:39 +01:00
2021-10-23 16:55:46 +02:00
switch (task->tState)
2017-11-04 15:55:39 +01:00
{
2018-05-26 00:25:36 +02:00
case 0:
PrintTextOnRecordMixing(gText_MixingRecords);
task->data[8] = 0x708;
2021-10-23 16:55:46 +02:00
task->tState = 400;
2018-05-26 00:25:36 +02:00
ClearLinkCallback_2();
break;
case 100: // wait 20 frames
if (++task->data[12] > 20)
{
task->data[12] = 0;
2021-10-23 16:55:46 +02:00
task->tState = 101;
2018-05-26 00:25:36 +02:00
}
break;
case 101:
{
u8 players = GetLinkPlayerCount_2();
if (IsLinkMaster() == TRUE)
2017-11-04 15:55:39 +01:00
{
if (players == GetSavedPlayerCount())
2017-11-04 15:55:39 +01:00
{
PlaySE(SE_PIN);
2021-10-23 16:55:46 +02:00
task->tState = 201;
2017-11-04 15:55:39 +01:00
task->data[12] = 0;
}
}
else
{
PlaySE(SE_BOO);
2021-10-23 16:55:46 +02:00
task->tState = 301;
2017-11-04 15:55:39 +01:00
}
2018-05-26 00:25:36 +02:00
}
break;
case 201:
// We're the link master. Delay for 30 frames per connected player.
if (GetSavedPlayerCount() == GetLinkPlayerCount_2() && ++task->data[12] > (GetLinkPlayerCount_2() * 30))
2018-05-26 00:25:36 +02:00
{
CheckShouldAdvanceLinkState();
2021-10-23 16:55:46 +02:00
task->tState = 1;
2018-05-26 00:25:36 +02:00
}
break;
case 301:
if (GetSavedPlayerCount() == GetLinkPlayerCount_2())
2021-10-23 16:55:46 +02:00
task->tState = 1;
2018-05-26 00:25:36 +02:00
break;
case 400: // wait 20 frames
if (++task->data[12] > 20)
{
2021-10-23 16:55:46 +02:00
task->tState = 1;
2018-05-26 00:25:36 +02:00
task->data[12] = 0;
}
break;
case 1: // wait for handshake
if (gReceivedRemoteLinkPlayers != 0)
{
ConvertIntToDecimalStringN(gStringVar1, GetMultiplayerId_(), STR_CONV_MODE_LEADING_ZEROS, 2);
2021-10-23 16:55:46 +02:00
task->tState = 5;
2018-05-26 00:25:36 +02:00
}
break;
case 2:
{
u8 subTaskId;
task->data[6] = GetLinkPlayerCount_2();
2021-10-23 16:55:46 +02:00
task->tState = 0;
task->tMultiplayerId = GetMultiplayerId_();
2018-05-26 00:25:36 +02:00
task->func = Task_SendPacket;
2017-11-04 17:20:40 +01:00
if (Link_AnyPartnersPlayingRubyOrSapphire())
2017-11-04 15:55:39 +01:00
{
2021-10-23 16:55:46 +02:00
StorePtrInTaskData(sSentRecord, &task->tSentRecord);
2018-05-26 00:25:36 +02:00
subTaskId = CreateTask(Task_CopyReceiveBuffer, 80);
2021-10-23 16:55:46 +02:00
task->tCopyTaskId = subTaskId;
gTasks[subTaskId].tParentTaskId = taskId;
StorePtrInTaskData(sReceivedRecords, &gTasks[subTaskId].tRecvRecords);
sRecordStructSize = sizeof(struct PlayerRecordRS);
2017-11-04 15:55:39 +01:00
}
else
{
2021-10-23 16:55:46 +02:00
StorePtrInTaskData(sSentRecord, &task->tSentRecord);
2018-05-26 00:25:36 +02:00
subTaskId = CreateTask(Task_CopyReceiveBuffer, 80);
2021-10-23 16:55:46 +02:00
task->tCopyTaskId = subTaskId;
gTasks[subTaskId].tParentTaskId = taskId;
StorePtrInTaskData(sReceivedRecords, &gTasks[subTaskId].tRecvRecords);
sRecordStructSize = sizeof(struct PlayerRecordEmerald);
2017-11-04 15:55:39 +01:00
}
2018-05-26 00:25:36 +02:00
}
break;
case 5: // wait 60 frames
if (++task->data[10] > 60)
{
task->data[10] = 0;
2021-10-23 16:55:46 +02:00
task->tState = 2;
2018-05-26 00:25:36 +02:00
}
break;
2017-11-04 15:55:39 +01:00
}
}
2017-11-04 16:16:07 +01:00
2018-05-26 00:25:36 +02:00
static void Task_SendPacket(u8 taskId)
2017-11-04 16:16:07 +01:00
{
2018-05-26 00:25:36 +02:00
struct Task *task = &gTasks[taskId];
2021-10-23 16:55:46 +02:00
switch (task->tState)
2017-11-04 16:16:07 +01:00
{
2021-10-23 16:55:46 +02:00
case 0: // Copy record data chunk to send buffer
2018-05-26 00:25:36 +02:00
{
2021-10-23 16:55:46 +02:00
void *recordData = LoadPtrFromTaskData(&task->tSentRecord) + task->tNumChunksSent * BUFFER_CHUNK_SIZE;
2018-05-26 00:25:36 +02:00
memcpy(gBlockSendBuffer, recordData, BUFFER_CHUNK_SIZE);
2021-10-23 16:55:46 +02:00
task->tState++;
2018-05-26 00:25:36 +02:00
}
break;
case 1:
if (GetMultiplayerId() == 0)
SendBlockRequest(BLOCK_REQ_SIZE_200);
2021-10-23 16:55:46 +02:00
task->tState++;
2018-05-26 00:25:36 +02:00
break;
case 2:
break;
case 3:
2021-10-23 16:55:46 +02:00
// If sent final chunk of record, move on to next state.
// Otherwise return to first state and send next chunk.
task->tNumChunksSent++;
if (task->tNumChunksSent == sRecordStructSize / BUFFER_CHUNK_SIZE + 1)
task->tState++;
2018-05-26 00:25:36 +02:00
else
2021-10-23 16:55:46 +02:00
task->tState = 0;
2018-05-26 00:25:36 +02:00
break;
case 4:
2021-10-23 16:55:46 +02:00
if (!gTasks[task->tCopyTaskId].isActive)
2018-05-26 00:25:36 +02:00
task->func = Task_SendPacket_SwitchToReceive;
break;
2017-11-04 16:16:07 +01:00
}
}
2017-11-04 16:39:23 +01:00
2018-05-26 00:25:36 +02:00
static void Task_CopyReceiveBuffer(u8 taskId)
2017-11-04 16:39:23 +01:00
{
2018-05-26 00:25:36 +02:00
struct Task *task = &gTasks[taskId];
u8 status = GetBlockReceivedStatus();
u8 handledPlayers = 0;
if (status == GetLinkPlayerCountAsBitFlags())
2017-11-04 16:39:23 +01:00
{
2018-05-26 00:25:36 +02:00
u8 i;
for (i = 0; i < GetLinkPlayerCount(); i++)
2017-11-04 16:39:23 +01:00
{
2018-05-26 00:25:36 +02:00
if ((status >> i) & 1)
2017-11-04 16:39:23 +01:00
{
2021-10-23 16:55:46 +02:00
void *dest = LoadPtrFromTaskData(&task->tRecvRecords) + task->tNumChunksRecv(i) * BUFFER_CHUNK_SIZE + sRecordStructSize * i;
void *src = GetPlayerRecvBuffer(i);
if ((task->tNumChunksRecv(i) + 1) * BUFFER_CHUNK_SIZE > sRecordStructSize)
memcpy(dest, src, sRecordStructSize - task->tNumChunksRecv(i) * BUFFER_CHUNK_SIZE);
2017-11-04 16:39:23 +01:00
else
2018-05-26 00:25:36 +02:00
memcpy(dest, src, BUFFER_CHUNK_SIZE);
2017-11-04 16:39:23 +01:00
ResetBlockReceivedFlag(i);
2021-10-23 16:55:46 +02:00
task->tNumChunksRecv(i)++;
if (task->tNumChunksRecv(i) == sRecordStructSize / BUFFER_CHUNK_SIZE + 1)
2018-05-26 00:25:36 +02:00
handledPlayers++;
2017-11-04 16:39:23 +01:00
}
}
2021-10-23 16:55:46 +02:00
gTasks[task->tParentTaskId].tState++;
2017-11-04 16:39:23 +01:00
}
2018-05-26 00:25:36 +02:00
if (handledPlayers == GetLinkPlayerCount())
2017-11-04 16:39:23 +01:00
DestroyTask(taskId);
}
2017-11-04 16:49:28 +01:00
2021-10-23 16:55:46 +02:00
static void Task_WaitReceivePacket(u8 taskId)
2017-11-04 16:49:28 +01:00
{
2018-05-26 00:25:36 +02:00
struct Task *task = &gTasks[taskId];
2017-11-04 16:49:28 +01:00
2021-10-23 16:55:46 +02:00
// Wait for Task_CopyReceiveBuffer to finish
if (!gTasks[task->tCopyTaskId].isActive)
2017-11-04 16:49:28 +01:00
DestroyTask(taskId);
}
2018-05-26 00:25:36 +02:00
static void Task_ReceivePacket(u8 taskId)
2017-11-04 16:49:28 +01:00
{
2018-05-26 00:25:36 +02:00
struct Task *task = &gTasks[taskId];
2017-11-04 16:49:28 +01:00
2021-10-23 16:55:46 +02:00
task->func = Task_WaitReceivePacket;
if (sReadyToReceive == TRUE)
ReceiveExchangePacket(task->tMultiplayerId);
2017-11-04 16:49:28 +01:00
}
2018-05-26 00:25:36 +02:00
static void Task_SendPacket_SwitchToReceive(u8 taskId)
2017-11-04 16:49:28 +01:00
{
2018-05-26 00:25:36 +02:00
gTasks[taskId].func = Task_ReceivePacket;
2021-10-23 16:55:46 +02:00
sReadyToReceive = TRUE;
2017-11-04 16:49:28 +01:00
}
2018-05-26 00:25:36 +02:00
static void *LoadPtrFromTaskData(const u16 *asShort)
2017-11-04 16:49:28 +01:00
{
return (void *)(asShort[0] | (asShort[1] << 16));
}
2018-05-26 00:25:36 +02:00
static void StorePtrInTaskData(void *records, u16 *asShort)
2017-11-04 16:49:28 +01:00
{
2017-11-04 23:48:13 +01:00
asShort[0] = (u32)records;
asShort[1] = ((u32)records >> 16);
2017-11-04 16:49:28 +01:00
}
2018-05-26 00:25:36 +02:00
static u8 GetMultiplayerId_(void)
2017-11-04 16:49:28 +01:00
{
return GetMultiplayerId();
}
2018-05-26 00:25:36 +02:00
static void *GetPlayerRecvBuffer(u8 id)
2017-11-04 16:49:28 +01:00
{
return gBlockRecvBuffer[id];
}
2017-11-04 17:03:50 +01:00
2018-05-26 00:25:36 +02:00
static void ShufflePlayerIndices(u32 *data)
2017-11-04 17:03:50 +01:00
{
u32 i;
u32 linkTrainerId;
2018-05-26 00:25:36 +02:00
u32 players = GetLinkPlayerCount();
2017-11-04 17:03:50 +01:00
2018-05-26 00:25:36 +02:00
switch (players)
2017-11-04 17:03:50 +01:00
{
2018-05-26 00:25:36 +02:00
case 2:
2021-10-23 16:55:46 +02:00
for (i = 0; i < ARRAY_COUNT(sPlayerIdxOrders_2Player); i++)
data[i] = sPlayerIdxOrders_2Player[i];
2018-05-26 00:25:36 +02:00
break;
case 3:
2021-10-23 16:55:46 +02:00
linkTrainerId = GetLinkPlayerTrainerId(0) % ARRAY_COUNT(sPlayerIdxOrders_3Player);
for (i = 0; i < ARRAY_COUNT(sPlayerIdxOrders_3Player[0]); i++)
data[i] = sPlayerIdxOrders_3Player[linkTrainerId][i];
2018-05-26 00:25:36 +02:00
break;
case 4:
2021-10-23 16:55:46 +02:00
linkTrainerId = GetLinkPlayerTrainerId(0) % ARRAY_COUNT(sPlayerIdxOrders_4Player);
for (i = 0; i < ARRAY_COUNT(sPlayerIdxOrders_4Player[0]); i++)
data[i] = sPlayerIdxOrders_4Player[linkTrainerId][i];
2018-05-26 00:25:36 +02:00
break;
2017-11-04 17:03:50 +01:00
}
}
2017-11-04 17:20:40 +01:00
2021-10-23 16:55:46 +02:00
static void ReceiveOldManData(OldMan *records, size_t recordSize, u8 multiplayerId)
2017-11-04 17:20:40 +01:00
{
u8 version;
u16 language;
2021-10-23 16:55:46 +02:00
OldMan *oldMan;
2019-10-20 22:11:07 +02:00
u32 mixIndices[MAX_LINK_PLAYERS];
2017-11-04 17:20:40 +01:00
2018-05-26 00:25:36 +02:00
ShufflePlayerIndices(mixIndices);
2021-10-23 16:55:46 +02:00
oldMan = (void *)records + recordSize * mixIndices[multiplayerId];
version = gLinkPlayers[mixIndices[multiplayerId]].version;
language = gLinkPlayers[mixIndices[multiplayerId]].language;
2018-05-26 00:25:36 +02:00
2017-11-04 17:20:40 +01:00
if (Link_AnyPartnersPlayingRubyOrSapphire())
2021-10-23 16:55:46 +02:00
SanitizeReceivedRubyOldMan(oldMan, version, language);
2017-11-04 17:20:40 +01:00
else
2021-10-23 16:55:46 +02:00
SanitizeReceivedEmeraldOldMan(oldMan, version, language);
2018-05-26 00:25:36 +02:00
2021-10-23 16:55:46 +02:00
memcpy(sOldManSave, (void *)records + recordSize * mixIndices[multiplayerId], sizeof(OldMan));
2018-05-19 12:32:44 +02:00
ResetMauvilleOldManFlag();
2017-11-04 17:20:40 +01:00
}
2017-11-04 23:48:13 +01:00
2021-10-23 16:55:46 +02:00
static void ReceiveBattleTowerData(void *records, size_t recordSize, u8 multiplayerId)
2017-11-04 23:48:13 +01:00
{
2021-10-23 16:55:46 +02:00
struct EmeraldBattleTowerRecord *battleTowerRecord;
struct BattleTowerPokemon *btPokemon;
2019-11-21 01:00:08 +01:00
u32 mixIndices[MAX_LINK_PLAYERS];
2017-11-04 23:48:13 +01:00
s32 i;
2018-05-26 00:25:36 +02:00
ShufflePlayerIndices(mixIndices);
2017-11-04 23:48:13 +01:00
if (Link_AnyPartnersPlayingRubyOrSapphire())
{
2021-10-23 16:55:46 +02:00
if (RubyBattleTowerRecordToEmerald((void *)records + recordSize * mixIndices[multiplayerId], (void *)records + recordSize * multiplayerId) == TRUE)
2017-11-04 23:48:13 +01:00
{
2021-10-23 16:55:46 +02:00
battleTowerRecord = (void *)records + recordSize * multiplayerId;
battleTowerRecord->language = gLinkPlayers[mixIndices[multiplayerId]].language;
CalcEmeraldBattleTowerChecksum(battleTowerRecord);
2017-11-04 23:48:13 +01:00
}
}
else
{
2021-10-23 16:55:46 +02:00
memcpy((void *)records + recordSize * multiplayerId, (void *)records + recordSize * mixIndices[multiplayerId], sizeof(struct EmeraldBattleTowerRecord));
battleTowerRecord = (void *)records + recordSize * multiplayerId;
2021-04-25 23:22:45 +02:00
for (i = 0; i < MAX_FRONTIER_PARTY_SIZE; i++)
2017-11-04 23:48:13 +01:00
{
2021-10-23 16:55:46 +02:00
btPokemon = &battleTowerRecord->party[i];
2017-11-04 23:48:13 +01:00
if (btPokemon->species != SPECIES_NONE && IsStringJapanese(btPokemon->nickname))
ConvertInternationalString(btPokemon->nickname, LANGUAGE_JAPANESE);
}
2021-10-23 16:55:46 +02:00
CalcEmeraldBattleTowerChecksum(battleTowerRecord);
2017-11-05 00:03:41 +01:00
}
2021-10-23 16:55:46 +02:00
PutNewBattleTowerRecord((void *)records + recordSize * multiplayerId);
2017-11-05 00:03:41 +01:00
}
2021-10-23 16:55:46 +02:00
static void ReceiveLilycoveLadyData(LilycoveLady *records, size_t recordSize, u8 multiplayerId)
2017-11-05 00:03:41 +01:00
{
2021-10-23 16:55:46 +02:00
LilycoveLady *lilycoveLady;
2019-11-21 01:00:08 +01:00
u32 mixIndices[MAX_LINK_PLAYERS];
2017-11-05 00:03:41 +01:00
2018-05-26 00:25:36 +02:00
ShufflePlayerIndices(mixIndices);
2021-10-23 16:55:46 +02:00
memcpy((void *)records + recordSize * multiplayerId, sLilycoveLadySave, sizeof(LilycoveLady));
2018-05-26 00:25:36 +02:00
2017-11-05 00:03:41 +01:00
if (GetLilycoveLadyId() == 0)
{
2021-10-23 16:55:46 +02:00
lilycoveLady = malloc(sizeof(*lilycoveLady));
if (lilycoveLady == NULL)
2017-11-05 00:03:41 +01:00
return;
2018-05-26 00:25:36 +02:00
2021-10-23 16:55:46 +02:00
memcpy(lilycoveLady, sLilycoveLadySave, sizeof(LilycoveLady));
2017-11-05 00:03:41 +01:00
}
else
{
2021-10-23 16:55:46 +02:00
lilycoveLady = NULL;
2017-11-05 00:03:41 +01:00
}
2018-05-26 00:25:36 +02:00
2021-10-23 16:55:46 +02:00
memcpy(sLilycoveLadySave, (void *)records + recordSize * mixIndices[multiplayerId], sizeof(LilycoveLady));
2019-08-06 02:37:09 +02:00
ResetLilycoveLadyForRecordMix();
2021-10-23 16:55:46 +02:00
if (lilycoveLady != NULL)
2017-11-05 00:03:41 +01:00
{
2021-10-23 16:55:46 +02:00
QuizLadyClearQuestionForRecordMix(lilycoveLady);
free(lilycoveLady);
2017-11-04 23:48:13 +01:00
}
}
2017-11-07 05:03:11 +01:00
2021-10-23 16:55:46 +02:00
static u8 GetDaycareMailItemId(struct DaycareMail *mail)
2017-11-07 05:03:11 +01:00
{
2021-10-23 16:55:46 +02:00
return mail->message.itemId;
2017-11-07 05:03:11 +01:00
}
2021-10-23 16:55:46 +02:00
// Indexes for a 2 element array used to store the multiplayer id and daycare
// slot that correspond to a daycare Pokémon that can hold an item.
enum {
MULTIPLAYER_ID,
DAYCARE_SLOT,
};
static void SwapDaycareMail(struct RecordMixingDaycareMail *records, size_t recordSize, u8 (*idxs)[2], u8 playerSlot1, u8 playerSlot2)
2017-11-07 05:03:11 +01:00
{
2021-10-23 16:55:46 +02:00
struct DaycareMail temp;
struct RecordMixingDaycareMail *mixMail1, *mixMail2;
// 1st player's daycare mail --> temp
mixMail1 = (void *)records + recordSize * idxs[playerSlot1][MULTIPLAYER_ID];
memcpy(&temp, &mixMail1->mail[idxs[playerSlot1][DAYCARE_SLOT]], sizeof(struct DaycareMail));
// 2nd player's daycare mail --> 1st player's daycare mail
mixMail2 = (void *)records + recordSize * idxs[playerSlot2][MULTIPLAYER_ID];
memcpy(&mixMail1->mail[idxs[playerSlot1][DAYCARE_SLOT]], &mixMail2->mail[idxs[playerSlot2][DAYCARE_SLOT]], sizeof(struct DaycareMail));
// temp --> 2nd player's daycare mail
memcpy(&mixMail2->mail[idxs[playerSlot2][DAYCARE_SLOT]], &temp, sizeof(struct DaycareMail));
2017-11-07 05:03:11 +01:00
}
2021-10-23 16:55:46 +02:00
// This sum is used to determine which players will swap daycare mail if there are more than 2 players who can.
// The TV show data is used to calculate this sum.
static void CalculateDaycareMailRandSum(const u8 *src)
2017-11-07 05:03:11 +01:00
{
u8 sum;
2018-05-26 00:25:36 +02:00
s32 i;
2017-11-07 05:03:11 +01:00
sum = 0;
2021-04-25 23:22:45 +02:00
for (i = 0; i < 256; i++)
2017-11-07 05:03:11 +01:00
sum += src[i];
2018-05-26 00:25:36 +02:00
2021-10-23 16:55:46 +02:00
sDaycareMailRandSum = sum;
2017-11-07 05:03:11 +01:00
}
2021-10-23 16:55:46 +02:00
static u8 GetDaycareMailRandSum(void)
2017-11-07 05:03:11 +01:00
{
2021-10-23 16:55:46 +02:00
return sDaycareMailRandSum;
2017-11-07 05:03:11 +01:00
}
2021-10-23 16:55:46 +02:00
static void ReceiveDaycareMailData(struct RecordMixingDaycareMail *records, size_t recordSize, u8 multiplayerId, TVShow *shows)
2017-11-07 05:03:11 +01:00
{
2018-12-28 23:40:25 +01:00
u16 i, j;
2017-11-07 05:03:11 +01:00
u8 linkPlayerCount;
2018-12-28 23:40:25 +01:00
u8 tableId;
2021-10-23 16:55:46 +02:00
struct RecordMixingDaycareMail *mixMail;
u8 playerSlot1, playerSlot2;
2018-12-28 23:40:25 +01:00
void *ptr;
2021-10-23 16:55:46 +02:00
u8 unusedArr1[MAX_LINK_PLAYERS];
u8 unusedArr2[MAX_LINK_PLAYERS];
struct RecordMixingDaycareMail *unusedMixMail[MAX_LINK_PLAYERS];
bool8 canHoldItem[MAX_LINK_PLAYERS][DAYCARE_MON_COUNT];
u8 idxs[MAX_LINK_PLAYERS][2];
u8 numDaycareCanHold;
2017-11-07 05:03:11 +01:00
u16 oldSeed;
2018-12-28 23:40:25 +01:00
bool32 anyRS;
2017-11-07 05:03:11 +01:00
2021-10-23 16:55:46 +02:00
// Seed RNG to the first player's trainer id so that
// every player has the same random swap occur
// (see the other use of Random2 in this function)
2017-11-07 05:03:11 +01:00
oldSeed = Random2();
SeedRng2(gLinkPlayers[0].trainerId);
linkPlayerCount = GetLinkPlayerCount();
2021-10-23 16:55:46 +02:00
for (i = 0; i < MAX_LINK_PLAYERS; i++)
2017-11-07 05:03:11 +01:00
{
2021-10-23 16:55:46 +02:00
unusedArr1[i] = 0xFF;
unusedArr2[i] = 0;
canHoldItem[i][0] = FALSE;
canHoldItem[i][1] = FALSE;
2017-11-07 05:03:11 +01:00
}
2018-12-28 23:40:25 +01:00
2021-10-23 16:55:46 +02:00
// Handle language differences if RS / Japanese players are present
2017-11-07 05:03:11 +01:00
anyRS = Link_AnyPartnersPlayingRubyOrSapphire();
2018-12-28 23:40:25 +01:00
for (i = 0; i < GetLinkPlayerCount(); i++)
2017-11-07 05:03:11 +01:00
{
2018-12-28 23:40:25 +01:00
u32 language, version;
2021-10-23 16:55:46 +02:00
mixMail = (void *)records + i * recordSize;
2018-12-28 23:40:25 +01:00
language = gLinkPlayers[i].language;
version = gLinkPlayers[i].version & 0xFF;
2021-10-23 16:55:46 +02:00
for (j = 0; j < mixMail->numDaycareMons; j++)
2017-11-07 05:03:11 +01:00
{
2018-12-28 23:40:25 +01:00
u16 otNameLanguage, nicknameLanguage;
2021-10-23 16:55:46 +02:00
struct DaycareMail *daycareMail = &mixMail->mail[j];
2018-12-28 23:40:25 +01:00
2021-10-23 16:55:46 +02:00
if (daycareMail->message.itemId == ITEM_NONE)
2018-12-28 23:40:25 +01:00
continue;
if (anyRS)
2017-11-07 05:03:11 +01:00
{
2021-10-23 16:55:46 +02:00
// Handle OT name language
if (StringLength(daycareMail->otName) <= 5)
2018-12-28 23:40:25 +01:00
{
otNameLanguage = LANGUAGE_JAPANESE;
}
else
{
2021-10-23 16:55:46 +02:00
StripExtCtrlCodes(daycareMail->otName);
2018-12-28 23:40:25 +01:00
otNameLanguage = language;
}
2021-10-23 16:55:46 +02:00
// Handle nickname langugae
if (daycareMail->monName[0] == EXT_CTRL_CODE_BEGIN && daycareMail->monName[1] == EXT_CTRL_CODE_JPN)
2017-11-07 05:03:11 +01:00
{
2021-10-23 16:55:46 +02:00
StripExtCtrlCodes(daycareMail->monName);
2018-12-28 23:40:25 +01:00
nicknameLanguage = LANGUAGE_JAPANESE;
2017-11-07 05:03:11 +01:00
}
2018-12-28 23:40:25 +01:00
else
2017-11-07 05:03:11 +01:00
{
2018-12-28 23:40:25 +01:00
nicknameLanguage = language;
}
2021-10-23 16:55:46 +02:00
// Set languages
2018-12-28 23:40:25 +01:00
if (version == VERSION_RUBY || version == VERSION_SAPPHIRE)
{
2021-10-23 16:55:46 +02:00
daycareMail->gameLanguage = otNameLanguage;
daycareMail->monLanguage = nicknameLanguage;
2017-11-07 05:03:11 +01:00
}
}
2018-12-28 23:40:25 +01:00
else if (language == LANGUAGE_JAPANESE)
{
2021-10-23 16:55:46 +02:00
if (IsStringJapanese(daycareMail->otName))
daycareMail->gameLanguage = LANGUAGE_JAPANESE;
2018-12-28 23:40:25 +01:00
else
2021-10-23 16:55:46 +02:00
daycareMail->gameLanguage = GAME_LANGUAGE;
2018-12-28 23:40:25 +01:00
2021-10-23 16:55:46 +02:00
if (IsStringJapanese(daycareMail->monName))
daycareMail->monLanguage = LANGUAGE_JAPANESE;
2018-12-28 23:40:25 +01:00
else
2021-10-23 16:55:46 +02:00
daycareMail->monLanguage = GAME_LANGUAGE;
2018-12-28 23:40:25 +01:00
}
2017-11-07 05:03:11 +01:00
}
}
2018-12-28 23:40:25 +01:00
2021-10-23 16:55:46 +02:00
// For each player, get which of their daycare Pokémon can hold items
// (can't hold items if already holding one, or if daycare slot is empty).
// Note that when deposited in the daycare, Pokémon have their mail taken
// from them and returned upon withdrawal, which means daycare Pokémon that
// have associated mail do not have a held item.
// Because not holding an item is the only determination for a swap, this also
// means that a "swap" can occur even if neither Pokémon has associated mail.
numDaycareCanHold = 0;
2018-12-28 23:40:25 +01:00
for (i = 0; i < linkPlayerCount; i++)
2017-11-07 05:03:11 +01:00
{
2021-10-23 16:55:46 +02:00
mixMail = (void *)records + i * recordSize;
if (mixMail->numDaycareMons == 0)
2018-12-28 23:40:25 +01:00
continue;
2021-10-23 16:55:46 +02:00
for (j = 0; j < mixMail->numDaycareMons; j++)
2017-11-07 05:03:11 +01:00
{
2021-10-23 16:55:46 +02:00
if (!mixMail->cantHoldItem[j])
canHoldItem[i][j] = TRUE;
2017-11-07 05:03:11 +01:00
}
}
2018-12-28 23:40:25 +01:00
2021-10-23 16:55:46 +02:00
// Fill the idxs array with data about which players
// and which daycare slots should swap mail.
2018-12-28 23:40:25 +01:00
j = 0;
for (i = 0; i < linkPlayerCount; i++)
2017-11-07 05:03:11 +01:00
{
2021-10-23 16:55:46 +02:00
mixMail = (void *)records + i * recordSize;
// Count number of players that have at least
// one daycare Pokémon with no held item
if (canHoldItem[i][0] == TRUE || canHoldItem[i][1] == TRUE)
numDaycareCanHold++;
if (canHoldItem[i][0] == TRUE && canHoldItem[i][1] == FALSE)
2017-11-07 05:03:11 +01:00
{
2021-10-23 16:55:46 +02:00
// Only daycare slot 0 can hold an item for this player, record it
idxs[j][MULTIPLAYER_ID] = i;
idxs[j][DAYCARE_SLOT] = 0;
2018-12-28 23:40:25 +01:00
j++;
2017-11-07 05:03:11 +01:00
}
2021-10-23 16:55:46 +02:00
else if (canHoldItem[i][0] == FALSE && canHoldItem[i][1] == TRUE)
2017-11-07 05:03:11 +01:00
{
2021-10-23 16:55:46 +02:00
// Only daycare slot 1 can hold an item for this player, record it
idxs[j][MULTIPLAYER_ID] = i;
idxs[j][DAYCARE_SLOT] = 1;
2018-12-28 23:40:25 +01:00
j++;
2017-11-07 05:03:11 +01:00
}
2021-10-23 16:55:46 +02:00
else if (canHoldItem[i][0] == TRUE && canHoldItem[i][1] == TRUE)
2017-11-07 05:03:11 +01:00
{
2021-10-23 16:55:46 +02:00
// Both daycare slots can hold an item, choose which one to use.
// If either one is the only one to have associated mail, use that one.
// If both do or don't have associated mail, choose one randomly.
u32 itemId1, itemId2;
idxs[j][MULTIPLAYER_ID] = i;
itemId1 = GetDaycareMailItemId(&mixMail->mail[0]);
itemId2 = GetDaycareMailItemId(&mixMail->mail[1]);
if ((!itemId1 && !itemId2) || (itemId1 && itemId2))
idxs[j][DAYCARE_SLOT] = Random2() % 2;
else if (itemId1 && !itemId2)
idxs[j][DAYCARE_SLOT] = 0;
else if (!itemId1 && itemId2)
idxs[j][DAYCARE_SLOT] = 1;
2018-12-28 23:40:25 +01:00
j++;
2017-11-07 05:03:11 +01:00
}
}
2018-12-28 23:40:25 +01:00
2021-10-23 16:55:46 +02:00
// Copy the player's record mix mail 4 times to an array that's never read.
for (i = 0; i < MAX_LINK_PLAYERS; i++)
2017-11-07 05:03:11 +01:00
{
2021-10-23 16:55:46 +02:00
mixMail = &records[multiplayerId * recordSize];
unusedMixMail[i] = mixMail;
2017-11-07 05:03:11 +01:00
}
2018-12-28 23:40:25 +01:00
2021-10-23 16:55:46 +02:00
// Choose a random table id to determine who will
// swap if there are more than 2 candidate players.
tableId = GetDaycareMailRandSum() % NUM_SWAP_COMBOS;
switch (numDaycareCanHold)
2017-11-07 05:03:11 +01:00
{
2018-12-28 23:40:25 +01:00
case 2:
2021-10-23 16:55:46 +02:00
// 2 players can swap, just perform swap.
SwapDaycareMail(records, recordSize, idxs, 0, 1);
2018-12-28 23:40:25 +01:00
break;
case 3:
2021-10-23 16:55:46 +02:00
// 3 players can swap, select 2 and leave the 3rd out
playerSlot1 = sDaycareMailSwapIds_3Player[tableId][0];
playerSlot2 = sDaycareMailSwapIds_3Player[tableId][1];
SwapDaycareMail(records, recordSize, idxs, playerSlot1, playerSlot2);
2018-12-28 23:40:25 +01:00
break;
case 4:
2021-10-23 16:55:46 +02:00
// 4 players can swap, select which 2 pairings will swap
ptr = idxs;
// Swap pair 1
playerSlot1 = sDaycareMailSwapIds_4Player[tableId][0];
playerSlot2 = sDaycareMailSwapIds_4Player[tableId][1];
SwapDaycareMail(records, recordSize, ptr, playerSlot1, playerSlot2);
// Swap pair 2
playerSlot1 = sDaycareMailSwapIds_4Player[tableId][2];
playerSlot2 = sDaycareMailSwapIds_4Player[tableId][3];
SwapDaycareMail(records, recordSize, ptr, playerSlot1, playerSlot2);
2018-12-28 23:40:25 +01:00
break;
2017-11-07 05:03:11 +01:00
}
2018-12-28 23:40:25 +01:00
2021-10-23 16:55:46 +02:00
// Save player's record mixed mail to the daycare (in case it has changed)
mixMail = (void *)records + multiplayerId * recordSize;
memcpy(&gSaveBlock1Ptr->daycare.mons[0].mail, &mixMail->mail[0], sizeof(struct DaycareMail));
memcpy(&gSaveBlock1Ptr->daycare.mons[1].mail, &mixMail->mail[1], sizeof(struct DaycareMail));
2017-11-07 05:03:11 +01:00
SeedRng(oldSeed);
}
2018-12-28 23:40:25 +01:00
2017-11-07 05:20:11 +01:00
2021-10-23 16:55:46 +02:00
static void ReceiveGiftItem(u16 *item, u8 multiplayerId)
2017-11-07 05:20:11 +01:00
{
2021-10-23 16:55:46 +02:00
if (multiplayerId != 0 && *item != ITEM_NONE && GetPocketByItemId(*item) == POCKET_KEY_ITEMS)
2017-11-07 05:20:11 +01:00
{
if (!CheckBagHasItem(*item, 1) && !CheckPCHasItem(*item, 1) && AddBagItem(*item, 1))
{
2018-05-20 12:21:39 +02:00
VarSet(VAR_TEMP_1, *item);
2017-11-07 05:20:11 +01:00
StringCopy(gStringVar1, gLinkPlayers[0].name);
if (*item == ITEM_EON_TICKET)
2020-03-03 07:36:20 +01:00
FlagSet(FLAG_ENABLE_SHIP_SOUTHERN_ISLAND);
2017-11-07 05:20:11 +01:00
}
else
{
2018-05-20 12:21:39 +02:00
VarSet(VAR_TEMP_1, ITEM_NONE);
2017-11-07 05:20:11 +01:00
}
}
}
2017-11-07 05:33:39 +01:00
static void Task_DoRecordMixing(u8 taskId)
2017-11-07 05:33:39 +01:00
{
2018-05-26 00:25:36 +02:00
struct Task *task = &gTasks[taskId];
2017-11-07 05:33:39 +01:00
2021-10-23 16:55:46 +02:00
switch (task->tState)
2017-11-07 05:33:39 +01:00
{
2018-05-26 00:25:36 +02:00
case 0:
2021-10-23 16:55:46 +02:00
task->tState++;
2018-05-26 00:25:36 +02:00
break;
case 1:
if (Link_AnyPartnersPlayingRubyOrSapphire())
2021-10-23 16:55:46 +02:00
task->tState++;
2018-05-26 00:25:36 +02:00
else
2021-10-23 16:55:46 +02:00
task->tState = 6;
2018-05-26 00:25:36 +02:00
break;
case 2:
2021-10-23 16:55:46 +02:00
// Mixing Ruby/Sapphire records.
2018-12-27 23:30:47 +01:00
SetContinueGameWarpStatusToDynamicWarp();
2021-10-29 04:54:41 +02:00
WriteSaveBlock2();
2021-10-23 16:55:46 +02:00
task->tState++;
2018-05-26 00:25:36 +02:00
break;
case 3:
2021-10-29 04:54:41 +02:00
if (WriteSaveBlock1Sector())
2018-05-26 00:25:36 +02:00
{
2018-12-27 23:30:47 +01:00
ClearContinueGameWarpStatus2();
2021-10-23 16:55:46 +02:00
task->tState = 4;
2018-05-26 00:25:36 +02:00
task->data[1] = 0;
}
break;
case 4: // Wait 10 frames
2018-05-26 00:25:36 +02:00
if (++task->data[1] > 10)
{
2020-08-13 09:09:47 +02:00
SetCloseLinkCallback();
2021-10-23 16:55:46 +02:00
task->tState++;
2018-05-26 00:25:36 +02:00
}
break;
case 5:
2021-10-23 16:55:46 +02:00
// Finish mixing Ruby/Sapphire records
if (gReceivedRemoteLinkPlayers == FALSE)
2018-05-26 00:25:36 +02:00
DestroyTask(taskId);
break;
2019-02-18 07:03:44 +01:00
// Mixing Emerald records.
2018-05-26 00:25:36 +02:00
case 6:
if (!Rfu_SetLinkRecovery(FALSE))
2018-05-26 00:25:36 +02:00
{
2021-10-29 04:54:41 +02:00
CreateTask(Task_LinkFullSave, 5);
2021-10-23 16:55:46 +02:00
task->tState++;
2018-05-26 00:25:36 +02:00
}
break;
2021-10-29 04:54:41 +02:00
case 7: // wait for Task_LinkFullSave to finish.
if (!FuncIsActiveTask(Task_LinkFullSave))
2018-05-26 00:25:36 +02:00
{
if (gWirelessCommType)
2017-11-07 05:33:39 +01:00
{
Rfu_SetLinkRecovery(TRUE);
2021-10-23 16:55:46 +02:00
task->tState = 8;
2017-11-07 05:33:39 +01:00
}
else
{
2021-10-23 16:55:46 +02:00
task->tState = 4;
2017-11-07 05:33:39 +01:00
}
2018-05-26 00:25:36 +02:00
}
break;
case 8:
2020-08-13 09:09:47 +02:00
SetLinkStandbyCallback();
2021-10-23 16:55:46 +02:00
task->tState++;
2018-05-26 00:25:36 +02:00
break;
case 9:
2018-12-31 09:22:21 +01:00
if (IsLinkTaskFinished())
2018-05-26 00:25:36 +02:00
DestroyTask(taskId);
break;
2017-11-07 05:33:39 +01:00
}
}
2018-05-19 14:30:41 +02:00
2019-11-21 01:00:08 +01:00
static void GetSavedApprentices(struct Apprentice *dst, struct Apprentice *src)
2018-05-19 14:30:41 +02:00
{
s32 i, id;
2019-11-21 01:00:08 +01:00
s32 apprenticeSaveId, oldPlayerApprenticeSaveId;
s32 numOldPlayerApprentices, numMixApprentices;
2018-05-19 14:30:41 +02:00
dst[0].playerName[0] = EOS;
dst[1].playerName[0] = EOS;
2018-05-22 21:54:57 +02:00
dst[0] = src[0];
2018-05-19 14:30:41 +02:00
2019-11-21 01:00:08 +01:00
oldPlayerApprenticeSaveId = 0;
numOldPlayerApprentices = 0;
apprenticeSaveId = 0;
numMixApprentices = 0;
2018-05-19 14:30:41 +02:00
for (i = 0; i < 2; i++)
{
2021-10-23 16:55:46 +02:00
id = (i + gSaveBlock2Ptr->playerApprentice.saveId) % (APPRENTICE_COUNT - 1) + 1;
if (src[id].playerName[0] != EOS)
2018-05-19 14:30:41 +02:00
{
2018-11-01 21:31:10 +01:00
if (GetTrainerId(src[id].playerId) != GetTrainerId(gSaveBlock2Ptr->playerTrainerId))
2018-05-19 14:30:41 +02:00
{
2019-11-21 01:00:08 +01:00
numMixApprentices++;
apprenticeSaveId = id;
2018-05-19 14:30:41 +02:00
}
2018-11-01 21:31:10 +01:00
if (GetTrainerId(src[id].playerId) == GetTrainerId(gSaveBlock2Ptr->playerTrainerId))
2018-05-19 14:30:41 +02:00
{
2019-11-21 01:00:08 +01:00
numOldPlayerApprentices++;
oldPlayerApprenticeSaveId = id;
2018-05-19 14:30:41 +02:00
}
}
}
2019-11-21 01:00:08 +01:00
// Prefer passing on other mixed Apprentices rather than old player's Apprentices
if (numMixApprentices == 0 && numOldPlayerApprentices != 0)
2018-05-19 14:30:41 +02:00
{
2019-11-21 01:00:08 +01:00
numMixApprentices = numOldPlayerApprentices;
apprenticeSaveId = oldPlayerApprenticeSaveId;
2018-05-19 14:30:41 +02:00
}
2019-11-21 01:00:08 +01:00
switch (numMixApprentices)
2018-05-19 14:30:41 +02:00
{
case 1:
2019-11-21 01:00:08 +01:00
dst[1] = src[apprenticeSaveId];
2018-05-19 14:30:41 +02:00
break;
case 2:
if (Random2() > 0x3333)
2019-11-21 01:00:08 +01:00
dst[1] = src[gSaveBlock2Ptr->playerApprentice.saveId + 1];
2018-05-19 14:30:41 +02:00
else
dst[1] = src[((gSaveBlock2Ptr->playerApprentice.saveId + 1) % (APPRENTICE_COUNT - 1) + 1)];
2018-05-19 14:30:41 +02:00
break;
}
2018-05-22 21:54:57 +02:00
}
2018-10-28 21:11:53 +01:00
void GetPlayerHallRecords(struct PlayerHallRecords *dst)
2018-05-22 21:54:57 +02:00
{
s32 i, j;
2018-10-28 21:11:53 +01:00
for (i = 0; i < HALL_FACILITIES_COUNT; i++)
2018-05-22 21:54:57 +02:00
{
2021-10-23 16:55:46 +02:00
for (j = 0; j < FRONTIER_LVL_MODE_COUNT; j++)
2018-05-22 21:54:57 +02:00
{
2018-11-01 21:31:10 +01:00
CopyTrainerId(dst->onePlayer[i][j].id, gSaveBlock2Ptr->playerTrainerId);
2018-10-28 21:11:53 +01:00
dst->onePlayer[i][j].language = GAME_LANGUAGE;
StringCopy(dst->onePlayer[i][j].name, gSaveBlock2Ptr->playerName);
2018-05-22 21:54:57 +02:00
}
}
2021-10-23 16:55:46 +02:00
for (j = 0; j < FRONTIER_LVL_MODE_COUNT; j++)
2018-05-22 21:54:57 +02:00
{
2018-10-28 21:11:53 +01:00
dst->twoPlayers[j].language = GAME_LANGUAGE;
2018-11-01 21:31:10 +01:00
CopyTrainerId(dst->twoPlayers[j].id1, gSaveBlock2Ptr->playerTrainerId);
2019-11-24 22:58:40 +01:00
CopyTrainerId(dst->twoPlayers[j].id2, gSaveBlock2Ptr->frontier.opponentTrainerIds[j]);
2018-10-28 21:11:53 +01:00
StringCopy(dst->twoPlayers[j].name1, gSaveBlock2Ptr->playerName);
2019-11-24 22:58:40 +01:00
StringCopy(dst->twoPlayers[j].name2, gSaveBlock2Ptr->frontier.opponentNames[j]);
2018-05-22 21:54:57 +02:00
}
2021-10-23 16:55:46 +02:00
for (i = 0; i < FRONTIER_LVL_MODE_COUNT; i++)
2018-05-22 21:54:57 +02:00
{
2021-10-23 16:55:46 +02:00
dst->onePlayer[RANKING_HALL_TOWER_SINGLES][i].winStreak = gSaveBlock2Ptr->frontier.towerRecordWinStreaks[FRONTIER_MODE_SINGLES][i];
dst->onePlayer[RANKING_HALL_TOWER_DOUBLES][i].winStreak = gSaveBlock2Ptr->frontier.towerRecordWinStreaks[FRONTIER_MODE_DOUBLES][i];
dst->onePlayer[RANKING_HALL_TOWER_MULTIS][i].winStreak = gSaveBlock2Ptr->frontier.towerRecordWinStreaks[FRONTIER_MODE_MULTIS][i];
dst->onePlayer[RANKING_HALL_DOME][i].winStreak = gSaveBlock2Ptr->frontier.domeRecordWinStreaks[FRONTIER_MODE_SINGLES][i];
dst->onePlayer[RANKING_HALL_PALACE][i].winStreak = gSaveBlock2Ptr->frontier.palaceRecordWinStreaks[FRONTIER_MODE_SINGLES][i];
dst->onePlayer[RANKING_HALL_ARENA][i].winStreak = gSaveBlock2Ptr->frontier.arenaRecordStreaks[i];
dst->onePlayer[RANKING_HALL_FACTORY][i].winStreak = gSaveBlock2Ptr->frontier.factoryRecordWinStreaks[FRONTIER_MODE_SINGLES][i];
dst->onePlayer[RANKING_HALL_PIKE][i].winStreak = gSaveBlock2Ptr->frontier.pikeRecordStreaks[i];
dst->onePlayer[RANKING_HALL_PYRAMID][i].winStreak = gSaveBlock2Ptr->frontier.pyramidRecordStreaks[i];
2018-10-28 21:11:53 +01:00
dst->twoPlayers[i].winStreak = gSaveBlock2Ptr->frontier.towerRecordWinStreaks[FRONTIER_MODE_LINK_MULTIS][i];
2018-05-22 21:54:57 +02:00
}
}
2019-11-21 01:00:08 +01:00
static bool32 IsApprenticeAlreadySaved(struct Apprentice *mixApprentice, struct Apprentice *apprentices)
2018-05-22 21:54:57 +02:00
{
s32 i;
2019-11-15 00:56:18 +01:00
for (i = 0; i < APPRENTICE_COUNT; i++)
2018-05-22 21:54:57 +02:00
{
2019-11-15 00:56:18 +01:00
if (GetTrainerId(mixApprentice->playerId) == GetTrainerId(apprentices[i].playerId)
&& mixApprentice->number == apprentices[i].number)
2018-05-22 21:54:57 +02:00
return TRUE;
}
return FALSE;
}
2018-05-25 21:00:41 +02:00
2021-10-23 16:55:46 +02:00
static void ReceiveApprenticeData(struct Apprentice *records, size_t recordSize, u32 multiplayerId)
2018-05-25 21:00:41 +02:00
{
2019-11-21 01:00:08 +01:00
s32 i, numApprentices, apprenticeId;
2021-10-23 16:55:46 +02:00
struct Apprentice *mixApprentice;
2019-11-21 01:00:08 +01:00
u32 mixIndices[MAX_LINK_PLAYERS];
u32 apprenticeSaveId;
2018-05-25 21:00:41 +02:00
2018-05-26 00:25:36 +02:00
ShufflePlayerIndices(mixIndices);
2021-10-23 16:55:46 +02:00
mixApprentice = (void*)records + (recordSize * mixIndices[multiplayerId]);
2019-11-21 01:00:08 +01:00
numApprentices = 0;
apprenticeId = 0;
2018-05-25 21:00:41 +02:00
for (i = 0; i < 2; i++)
{
2021-10-23 16:55:46 +02:00
if (mixApprentice[i].playerName[0] != EOS && !IsApprenticeAlreadySaved(&mixApprentice[i], &gSaveBlock2Ptr->apprentices[0]))
2018-05-25 21:00:41 +02:00
{
2019-11-21 01:00:08 +01:00
numApprentices++;
apprenticeId = i;
2018-05-25 21:00:41 +02:00
}
}
2019-11-21 01:00:08 +01:00
switch (numApprentices)
2018-05-25 21:00:41 +02:00
{
case 1:
2019-11-21 01:00:08 +01:00
apprenticeSaveId = gSaveBlock2Ptr->playerApprentice.saveId + 1;
2021-10-23 16:55:46 +02:00
gSaveBlock2Ptr->apprentices[apprenticeSaveId] = mixApprentice[apprenticeId];
gSaveBlock2Ptr->playerApprentice.saveId = (gSaveBlock2Ptr->playerApprentice.saveId + 1) % (APPRENTICE_COUNT - 1);
2018-05-25 21:00:41 +02:00
break;
case 2:
for (i = 0; i < 2; i++)
{
apprenticeSaveId = ((i ^ 1) + gSaveBlock2Ptr->playerApprentice.saveId) % (APPRENTICE_COUNT - 1) + 1;
2021-10-23 16:55:46 +02:00
gSaveBlock2Ptr->apprentices[apprenticeSaveId] = mixApprentice[i];
2018-05-25 21:00:41 +02:00
}
gSaveBlock2Ptr->playerApprentice.saveId = (gSaveBlock2Ptr->playerApprentice.saveId + 2) % (APPRENTICE_COUNT - 1);
2018-05-25 21:00:41 +02:00
break;
}
}
2021-10-24 21:49:45 +02:00
static void GetNewHallRecords(struct RecordMixingHallRecords *dst, void *records, size_t recordSize, u32 multiplayerId, s32 linkPlayerCount)
2018-05-25 21:00:41 +02:00
{
2018-11-03 18:21:33 +01:00
s32 i, j, k, l;
2021-10-24 21:49:45 +02:00
s32 repeatTrainers;
2018-11-03 18:21:33 +01:00
2021-10-24 21:49:45 +02:00
// Load sPartnerHallRecords with link partners' hall records
2018-11-03 18:21:33 +01:00
k = 0;
2021-10-24 21:49:45 +02:00
for (i = 0; i < linkPlayerCount; i++)
2018-11-03 18:21:33 +01:00
{
2021-10-24 21:49:45 +02:00
if (i != multiplayerId)
sPartnerHallRecords[k++] = records;
if (k == HALL_RECORDS_COUNT)
2018-11-03 18:21:33 +01:00
break;
2021-10-24 21:49:45 +02:00
records += recordSize;
2018-11-03 18:21:33 +01:00
}
2021-10-24 21:49:45 +02:00
// Get improved 1P hall records
2018-11-03 18:21:33 +01:00
for (i = 0; i < HALL_FACILITIES_COUNT; i++)
{
2021-10-23 16:55:46 +02:00
for (j = 0; j < FRONTIER_LVL_MODE_COUNT; j++)
2018-11-03 18:21:33 +01:00
{
2021-10-24 21:49:45 +02:00
// First get the existing saved records
2021-10-23 16:55:46 +02:00
for (k = 0; k < HALL_RECORDS_COUNT; k++)
2018-11-03 18:21:33 +01:00
dst->hallRecords1P[i][j][k] = gSaveBlock2Ptr->hallRecords1P[i][j][k];
2021-10-24 21:49:45 +02:00
// Then read the new mixed records
2018-11-03 18:21:33 +01:00
for (k = 0; k < linkPlayerCount - 1; k++)
{
2021-10-24 21:49:45 +02:00
repeatTrainers = 0;
2021-10-23 16:55:46 +02:00
for (l = 0; l < HALL_RECORDS_COUNT; l++)
2018-11-03 18:21:33 +01:00
{
2021-10-24 21:49:45 +02:00
// If the new trainer is already in the existing saved records, only
// use the new one if the win streak is better
if (GetTrainerId(dst->hallRecords1P[i][j][l].id) == GetTrainerId(sPartnerHallRecords[k]->onePlayer[i][j].id))
2018-11-03 18:21:33 +01:00
{
2021-10-24 21:49:45 +02:00
repeatTrainers++;
if (dst->hallRecords1P[i][j][l].winStreak < sPartnerHallRecords[k]->onePlayer[i][j].winStreak)
dst->hallRecords1P[i][j][l] = sPartnerHallRecords[k]->onePlayer[i][j];
2018-11-03 18:21:33 +01:00
}
}
2021-10-24 21:49:45 +02:00
// If all of the mixed records are new trainers, just save them
if (repeatTrainers == 0)
dst->hallRecords1P[i][j][k + HALL_RECORDS_COUNT] = sPartnerHallRecords[k]->onePlayer[i][j];
2018-11-03 18:21:33 +01:00
}
}
}
2021-10-24 21:49:45 +02:00
// Get improved 2P hall records
2021-10-23 16:55:46 +02:00
for (j = 0; j < FRONTIER_LVL_MODE_COUNT; j++)
2018-11-03 18:21:33 +01:00
{
2021-10-24 21:49:45 +02:00
// First get the existing saved records
2021-10-23 16:55:46 +02:00
for (k = 0; k < HALL_RECORDS_COUNT; k++)
2018-11-03 18:21:33 +01:00
dst->hallRecords2P[j][k] = gSaveBlock2Ptr->hallRecords2P[j][k];
2021-10-24 21:49:45 +02:00
// Then read the new mixed records
2018-11-03 18:21:33 +01:00
for (k = 0; k < linkPlayerCount - 1; k++)
{
2021-10-24 21:49:45 +02:00
repeatTrainers = 0;
2021-10-23 16:55:46 +02:00
for (l = 0; l < HALL_RECORDS_COUNT; l++)
2018-11-03 18:21:33 +01:00
{
2021-10-24 21:49:45 +02:00
// If the new trainer pair is already in the existing saved records, only
// use the new pair if the win streak is better
if (GetTrainerId(dst->hallRecords2P[j][l].id1) == GetTrainerId(sPartnerHallRecords[k]->twoPlayers[j].id1)
&& GetTrainerId(dst->hallRecords2P[j][l].id2) == GetTrainerId(sPartnerHallRecords[k]->twoPlayers[j].id2))
2018-11-03 18:21:33 +01:00
{
2021-10-24 21:49:45 +02:00
repeatTrainers++;
if (dst->hallRecords2P[j][l].winStreak < sPartnerHallRecords[k]->twoPlayers[j].winStreak)
dst->hallRecords2P[j][l] = sPartnerHallRecords[k]->twoPlayers[j];
2018-11-03 18:21:33 +01:00
}
}
2021-10-24 21:49:45 +02:00
// If all of the mixed records are new trainer pairs, just save them
if (repeatTrainers == 0)
dst->hallRecords2P[j][k + HALL_RECORDS_COUNT] = sPartnerHallRecords[k]->twoPlayers[j];
2018-11-03 18:21:33 +01:00
}
}
2018-05-25 21:00:41 +02:00
}
2021-10-24 21:49:45 +02:00
static void FillWinStreakRecords1P(struct RankingHall1P *playerRecords, struct RankingHall1P *mixRecords)
2018-05-25 21:00:41 +02:00
{
s32 i, j;
2021-10-24 21:49:45 +02:00
// Fill the player's 1P records with the highest win streaks from the mixed records
2021-10-23 16:55:46 +02:00
for (i = 0; i < HALL_RECORDS_COUNT; i++)
2018-05-25 21:00:41 +02:00
{
2021-10-24 21:49:45 +02:00
// Get the highest remaining win streak in the mixed hall records
2018-10-28 21:11:53 +01:00
s32 highestWinStreak = 0;
s32 highestId = -1;
2021-10-24 21:49:45 +02:00
for (j = 0; j < HALL_RECORDS_COUNT * 2; j++)
2018-05-25 21:00:41 +02:00
{
2021-10-24 21:49:45 +02:00
if (mixRecords[j].winStreak > highestWinStreak)
2018-05-25 21:00:41 +02:00
{
2018-10-28 21:11:53 +01:00
highestId = j;
2021-10-24 21:49:45 +02:00
highestWinStreak = mixRecords[j].winStreak;
2018-05-25 21:00:41 +02:00
}
}
2021-10-24 21:49:45 +02:00
// Save the win streak to the player's records, then clear it from the mixed records
2018-10-28 21:11:53 +01:00
if (highestId >= 0)
2018-05-25 21:00:41 +02:00
{
2021-10-24 21:49:45 +02:00
playerRecords[i] = mixRecords[highestId];
mixRecords[highestId].winStreak = 0;
2018-05-25 21:00:41 +02:00
}
}
}
2021-10-24 21:49:45 +02:00
static void FillWinStreakRecords2P(struct RankingHall2P *playerRecords, struct RankingHall2P *mixRecords)
2018-05-25 21:00:41 +02:00
{
s32 i, j;
2021-10-24 21:49:45 +02:00
// Fill the player's 2P records with the highest win streaks from the mixed records
for (i = 0; i < HALL_RECORDS_COUNT; i++)
2018-05-25 21:00:41 +02:00
{
2021-10-24 21:49:45 +02:00
// Get the highest remaining win streak in the mixed hall records
2018-10-28 21:11:53 +01:00
s32 highestWinStreak = 0;
s32 highestId = -1;
2021-10-24 21:49:45 +02:00
for (j = 0; j < HALL_RECORDS_COUNT * 2; j++)
2018-05-25 21:00:41 +02:00
{
2021-10-24 21:49:45 +02:00
if (mixRecords[j].winStreak > highestWinStreak)
2018-05-25 21:00:41 +02:00
{
2018-10-28 21:11:53 +01:00
highestId = j;
2021-10-24 21:49:45 +02:00
highestWinStreak = mixRecords[j].winStreak;
2018-05-25 21:00:41 +02:00
}
}
2021-10-24 21:49:45 +02:00
// Save the win streak to the player's records, then clear it from the mixed records
2018-10-28 21:11:53 +01:00
if (highestId >= 0)
2018-05-25 21:00:41 +02:00
{
2021-10-24 21:49:45 +02:00
playerRecords[i] = mixRecords[highestId];
mixRecords[highestId].winStreak = 0;
2018-05-25 21:00:41 +02:00
}
}
}
2021-10-24 21:49:45 +02:00
static void SaveHighestWinStreakRecords(struct RecordMixingHallRecords *mixHallRecords)
2018-05-25 21:00:41 +02:00
{
2018-10-28 21:11:53 +01:00
s32 i, j;
for (i = 0; i < HALL_FACILITIES_COUNT; i++)
{
2021-10-23 16:55:46 +02:00
for (j = 0; j < FRONTIER_LVL_MODE_COUNT; j++)
2021-10-24 21:49:45 +02:00
FillWinStreakRecords1P(gSaveBlock2Ptr->hallRecords1P[i][j], mixHallRecords->hallRecords1P[i][j]);
2018-10-28 21:11:53 +01:00
}
2021-10-23 16:55:46 +02:00
for (j = 0; j < FRONTIER_LVL_MODE_COUNT; j++)
2021-10-24 21:49:45 +02:00
FillWinStreakRecords2P(gSaveBlock2Ptr->hallRecords2P[j], mixHallRecords->hallRecords2P[j]);
2018-05-25 21:00:41 +02:00
}
2021-10-24 21:49:45 +02:00
static void ReceiveRankingHallRecords(struct PlayerHallRecords *records, size_t recordSize, u32 multiplayerId)
2018-05-25 21:00:41 +02:00
{
u8 linkPlayerCount = GetLinkPlayerCount();
2021-10-23 16:55:46 +02:00
struct RecordMixingHallRecords *mixHallRecords = AllocZeroed(sizeof(*mixHallRecords));
2018-05-25 21:00:41 +02:00
2021-10-24 21:49:45 +02:00
GetNewHallRecords(mixHallRecords, records, recordSize, multiplayerId, linkPlayerCount);
SaveHighestWinStreakRecords(mixHallRecords);
2018-05-25 21:00:41 +02:00
2021-10-23 16:55:46 +02:00
Free(mixHallRecords);
2018-05-25 21:00:41 +02:00
}
2021-04-25 23:22:45 +02:00
static void GetRecordMixingDaycareMail(struct RecordMixingDaycareMail *dst)
2018-05-25 21:00:41 +02:00
{
2021-10-23 16:55:46 +02:00
sRecordMixMail.mail[0] = gSaveBlock1Ptr->daycare.mons[0].mail;
sRecordMixMail.mail[1] = gSaveBlock1Ptr->daycare.mons[1].mail;
InitDaycareMailRecordMixing(&gSaveBlock1Ptr->daycare, &sRecordMixMail);
*dst = *sRecordMixMailSave;
2018-05-25 21:00:41 +02:00
}
2021-04-25 23:22:45 +02:00
static void SanitizeDaycareMailForRuby(struct RecordMixingDaycareMail *src)
2018-05-25 21:00:41 +02:00
{
s32 i;
for (i = 0; i < src->numDaycareMons; i++)
{
2021-04-25 23:22:45 +02:00
struct DaycareMail *mail = &src->mail[i];
2021-10-23 16:55:46 +02:00
if (mail->message.itemId != ITEM_NONE)
2018-05-25 21:00:41 +02:00
{
if (mail->gameLanguage != LANGUAGE_JAPANESE)
2021-10-23 16:55:46 +02:00
PadNameString(mail->otName, EXT_CTRL_CODE_BEGIN);
2018-05-25 21:00:41 +02:00
ConvertInternationalString(mail->monName, mail->monLanguage);
}
}
}
2018-05-26 00:25:36 +02:00
static void SanitizeRubyBattleTowerRecord(struct RSBattleTowerRecord *src)
2018-05-25 21:00:41 +02:00
{
}
2018-05-26 00:25:36 +02:00
static void SanitizeEmeraldBattleTowerRecord(struct EmeraldBattleTowerRecord *dst)
2018-05-25 21:00:41 +02:00
{
s32 i;
2021-04-25 23:22:45 +02:00
for (i = 0; i < MAX_FRONTIER_PARTY_SIZE; i++)
2018-05-25 21:00:41 +02:00
{
struct BattleTowerPokemon *towerMon = &dst->party[i];
2021-04-25 23:22:45 +02:00
if (towerMon->species != SPECIES_NONE)
2018-05-25 21:00:41 +02:00
StripExtCtrlCodes(towerMon->nickname);
}
2018-05-26 00:25:36 +02:00
CalcEmeraldBattleTowerChecksum(dst);
2018-05-25 21:00:41 +02:00
}