pokeemerald/src/battle_tower.c

3539 lines
118 KiB
C
Raw Normal View History

#include "global.h"
2018-10-21 00:06:42 +02:00
#include "battle_tower.h"
#include "apprentice.h"
#include "event_data.h"
#include "battle_setup.h"
#include "overworld.h"
#include "random.h"
2018-10-21 00:06:42 +02:00
#include "text.h"
2018-10-21 20:13:12 +02:00
#include "main.h"
2018-10-21 00:06:42 +02:00
#include "international_string_util.h"
#include "battle.h"
2018-10-30 21:45:26 +01:00
#include "frontier_util.h"
2018-12-05 22:21:26 +01:00
#include "strings.h"
2018-10-21 00:06:42 +02:00
#include "recorded_battle.h"
#include "easy_chat.h"
2018-10-21 20:13:12 +02:00
#include "gym_leader_rematch.h"
#include "battle_transition.h"
#include "trainer_see.h"
#include "new_game.h"
#include "string_util.h"
2019-04-04 23:53:06 +02:00
#include "data.h"
2018-10-22 19:22:57 +02:00
#include "link.h"
#include "field_message_box.h"
#include "tv.h"
2018-11-11 16:44:27 +01:00
#include "battle_factory.h"
#include "constants/apprentice.h"
2020-04-09 21:18:53 +02:00
#include "constants/battle_dome.h"
#include "constants/battle_frontier.h"
2019-12-27 11:26:27 +01:00
#include "constants/battle_frontier_mons.h"
2020-01-08 07:07:53 +01:00
#include "constants/battle_tent_mons.h"
#include "constants/battle_tent_trainers.h"
2019-11-17 00:11:27 +01:00
#include "constants/battle_tower.h"
2019-11-24 22:58:40 +01:00
#include "constants/frontier_util.h"
2018-12-05 22:21:26 +01:00
#include "constants/items.h"
2018-10-21 00:06:42 +02:00
#include "constants/trainers.h"
#include "constants/event_objects.h"
#include "constants/moves.h"
2018-12-03 05:22:42 +01:00
#include "constants/easy_chat.h"
2019-11-01 22:50:54 +01:00
extern const u8 MossdeepCity_SpaceCenter_2F_EventScript_MaxieTrainer[];
extern const u8 MossdeepCity_SpaceCenter_2F_EventScript_TabithaTrainer[];
2018-10-26 23:54:41 +02:00
// EWRAM vars.
EWRAM_DATA const struct BattleFrontierTrainer *gFacilityTrainers = NULL;
EWRAM_DATA const struct FacilityMon *gFacilityTrainerMons = NULL;
2018-11-19 01:03:14 +01:00
// IWRAM common
2020-10-07 23:03:46 +02:00
u16 gFrontierTempParty[MAX_FRONTIER_PARTY_SIZE];
2018-11-19 01:03:14 +01:00
// This file's functions.
static void InitTowerChallenge(void);
static void GetTowerData(void);
static void SetTowerData(void);
2019-11-29 04:46:39 +01:00
static void SetNextFacilityOpponent(void);
static void SetTowerBattleWon(void);
2018-10-25 21:27:10 +02:00
static void AwardBattleTowerRibbons(void);
static void SaveTowerChallenge(void);
2019-11-29 04:46:39 +01:00
static void GetOpponentIntroSpeech(void);
2020-12-24 22:18:47 +01:00
static void BattleTowerNop1(void);
static void BattleTowerNop2(void);
static void LoadMultiPartnerCandidatesData(void);
static void ShowPartnerCandidateMessage(void);
static void LoadLinkMultiOpponentsData(void);
2020-12-24 22:18:47 +01:00
static void TowerTryCloseLink(void);
static void SetMultiPartnerGfx(void);
2019-11-29 04:46:39 +01:00
static void SetTowerInterviewData(void);
2018-10-25 21:27:10 +02:00
static void ValidateBattleTowerRecordChecksums(void);
static void SaveCurrentWinStreak(void);
static void ValidateApprenticesChecksums(void);
2019-11-29 04:46:39 +01:00
static void SetNextBattleTentOpponent(void);
2018-10-25 21:27:10 +02:00
static void CopyEReaderTrainerFarewellMessage(void);
static void ClearBattleTowerRecord(struct EmeraldBattleTowerRecord *record);
2018-10-22 19:22:57 +02:00
static void FillTrainerParty(u16 trainerId, u8 firstMonId, u8 monCount);
2018-10-25 21:27:10 +02:00
static void FillTentTrainerParty_(u16 trainerId, u8 firstMonId, u8 monCount);
2018-10-26 23:54:41 +02:00
static void FillFactoryFrontierTrainerParty(u16 trainerId, u8 firstMonId);
static void FillFactoryTentTrainerParty(u16 trainerId, u8 firstMonId);
2018-10-25 21:27:10 +02:00
static u8 GetFrontierTrainerFixedIvs(u16 trainerId);
static void FillPartnerParty(u16 trainerId);
static void SetEReaderTrainerChecksum(struct BattleTowerEReaderTrainer *ereaderTrainer);
static u8 SetTentPtrsGetLevel(void);
// Const rom data.
2018-12-05 22:21:26 +01:00
const u16 gBattleFrontierHeldItems[] =
{
ITEM_NONE,
ITEM_KINGS_ROCK,
ITEM_SITRUS_BERRY,
ITEM_ORAN_BERRY,
ITEM_CHESTO_BERRY,
ITEM_HARD_STONE,
ITEM_FOCUS_BAND,
ITEM_PERSIM_BERRY,
ITEM_MIRACLE_SEED,
ITEM_BERRY_JUICE,
ITEM_MACHO_BRACE,
ITEM_SILVER_POWDER,
ITEM_CHERI_BERRY,
ITEM_BLACK_GLASSES,
ITEM_BLACK_BELT,
ITEM_SOUL_DEW,
ITEM_CHOICE_BAND,
ITEM_MAGNET,
ITEM_SILK_SCARF,
ITEM_WHITE_HERB,
ITEM_DEEP_SEA_SCALE,
ITEM_DEEP_SEA_TOOTH,
ITEM_MYSTIC_WATER,
ITEM_SHARP_BEAK,
ITEM_QUICK_CLAW,
ITEM_LEFTOVERS,
ITEM_RAWST_BERRY,
ITEM_LIGHT_BALL,
ITEM_POISON_BARB,
ITEM_NEVER_MELT_ICE,
ITEM_ASPEAR_BERRY,
ITEM_SPELL_TAG,
ITEM_BRIGHT_POWDER,
ITEM_LEPPA_BERRY,
ITEM_SCOPE_LENS,
ITEM_TWISTED_SPOON,
ITEM_METAL_COAT,
ITEM_MENTAL_HERB,
ITEM_CHARCOAL,
ITEM_PECHA_BERRY,
ITEM_SOFT_SAND,
ITEM_LUM_BERRY,
ITEM_DRAGON_SCALE,
ITEM_DRAGON_FANG,
ITEM_IAPAPA_BERRY,
ITEM_WIKI_BERRY,
ITEM_SEA_INCENSE,
ITEM_SHELL_BELL,
ITEM_SALAC_BERRY,
ITEM_LANSAT_BERRY,
ITEM_APICOT_BERRY,
ITEM_STARF_BERRY,
ITEM_LIECHI_BERRY,
ITEM_STICK,
ITEM_LAX_INCENSE,
ITEM_AGUAV_BERRY,
ITEM_FIGY_BERRY,
ITEM_THICK_CLUB,
ITEM_MAGO_BERRY,
ITEM_METAL_POWDER,
ITEM_PETAYA_BERRY,
ITEM_LUCKY_PUNCH,
ITEM_GANLON_BERRY
};
2018-12-03 05:22:42 +01:00
#include "data/battle_frontier/battle_frontier_trainer_mons.h"
#include "data/battle_frontier/battle_frontier_trainers.h"
#include "data/battle_frontier/battle_frontier_mons.h"
2018-12-03 05:22:42 +01:00
2018-12-10 16:33:51 +01:00
const u8 gTowerMaleFacilityClasses[30] =
{
FACILITY_CLASS_RUIN_MANIAC,
FACILITY_CLASS_TUBER_M,
FACILITY_CLASS_COOLTRAINER_M,
FACILITY_CLASS_RICH_BOY,
FACILITY_CLASS_POKEMANIAC,
FACILITY_CLASS_SWIMMER_M,
FACILITY_CLASS_BLACK_BELT,
FACILITY_CLASS_GUITARIST,
FACILITY_CLASS_KINDLER,
FACILITY_CLASS_CAMPER,
FACILITY_CLASS_BUG_MANIAC,
FACILITY_CLASS_PSYCHIC_M,
FACILITY_CLASS_GENTLEMAN,
FACILITY_CLASS_SCHOOL_KID_M,
FACILITY_CLASS_POKEFAN_M,
FACILITY_CLASS_EXPERT_M,
FACILITY_CLASS_YOUNGSTER,
FACILITY_CLASS_FISHERMAN,
FACILITY_CLASS_CYCLING_TRIATHLETE_M,
FACILITY_CLASS_RUNNING_TRIATHLETE_M,
FACILITY_CLASS_SWIMMING_TRIATHLETE_M,
FACILITY_CLASS_DRAGON_TAMER,
FACILITY_CLASS_BIRD_KEEPER,
FACILITY_CLASS_NINJA_BOY,
FACILITY_CLASS_SAILOR,
FACILITY_CLASS_COLLECTOR,
FACILITY_CLASS_PKMN_BREEDER_M,
FACILITY_CLASS_PKMN_RANGER_M,
FACILITY_CLASS_BUG_CATCHER,
FACILITY_CLASS_HIKER
};
2018-12-03 05:22:42 +01:00
2018-12-10 16:33:51 +01:00
const u8 gTowerFemaleFacilityClasses[20] =
{
FACILITY_CLASS_AROMA_LADY,
FACILITY_CLASS_TUBER_F,
FACILITY_CLASS_COOLTRAINER_F,
FACILITY_CLASS_HEX_MANIAC,
FACILITY_CLASS_LADY,
FACILITY_CLASS_BEAUTY,
FACILITY_CLASS_PSYCHIC_F,
FACILITY_CLASS_SCHOOL_KID_F,
FACILITY_CLASS_POKEFAN_F,
FACILITY_CLASS_EXPERT_F,
FACILITY_CLASS_CYCLING_TRIATHLETE_F,
FACILITY_CLASS_RUNNING_TRIATHLETE_F,
FACILITY_CLASS_SWIMMING_TRIATHLETE_F,
FACILITY_CLASS_BATTLE_GIRL,
FACILITY_CLASS_PARASOL_LADY,
FACILITY_CLASS_SWIMMER_F,
FACILITY_CLASS_PICNICKER,
2019-11-24 22:58:40 +01:00
FACILITY_CLASS_PKMN_BREEDER_F,
FACILITY_CLASS_PKMN_RANGER_F,
FACILITY_CLASS_LASS
};
2018-12-03 05:22:42 +01:00
2018-12-10 16:33:51 +01:00
const u8 gTowerMaleTrainerGfxIds[30] =
{
OBJ_EVENT_GFX_HIKER,
OBJ_EVENT_GFX_TUBER_M,
OBJ_EVENT_GFX_MAN_3,
OBJ_EVENT_GFX_RICH_BOY,
OBJ_EVENT_GFX_MANIAC,
OBJ_EVENT_GFX_RUNNING_TRIATHLETE_M,
OBJ_EVENT_GFX_BLACK_BELT,
OBJ_EVENT_GFX_MAN_5,
OBJ_EVENT_GFX_MAN_5,
OBJ_EVENT_GFX_CAMPER,
OBJ_EVENT_GFX_MANIAC,
OBJ_EVENT_GFX_PSYCHIC_M,
OBJ_EVENT_GFX_GENTLEMAN,
OBJ_EVENT_GFX_SCHOOL_KID_M,
OBJ_EVENT_GFX_POKEFAN_M,
OBJ_EVENT_GFX_EXPERT_M,
OBJ_EVENT_GFX_YOUNGSTER,
OBJ_EVENT_GFX_FISHERMAN,
OBJ_EVENT_GFX_CYCLING_TRIATHLETE_M,
OBJ_EVENT_GFX_RUNNING_TRIATHLETE_M,
OBJ_EVENT_GFX_RUNNING_TRIATHLETE_M,
OBJ_EVENT_GFX_MAN_3,
OBJ_EVENT_GFX_MAN_5,
OBJ_EVENT_GFX_NINJA_BOY,
OBJ_EVENT_GFX_SAILOR,
OBJ_EVENT_GFX_MANIAC,
OBJ_EVENT_GFX_MAN_4,
OBJ_EVENT_GFX_CAMPER,
OBJ_EVENT_GFX_BUG_CATCHER,
OBJ_EVENT_GFX_HIKER
};
2018-12-03 05:22:42 +01:00
2018-12-10 16:33:51 +01:00
const u8 gTowerFemaleTrainerGfxIds[20] =
{
OBJ_EVENT_GFX_WOMAN_2,
OBJ_EVENT_GFX_TUBER_F,
OBJ_EVENT_GFX_WOMAN_5,
OBJ_EVENT_GFX_HEX_MANIAC,
OBJ_EVENT_GFX_WOMAN_2,
OBJ_EVENT_GFX_BEAUTY,
OBJ_EVENT_GFX_LASS,
OBJ_EVENT_GFX_GIRL_3,
OBJ_EVENT_GFX_POKEFAN_F,
OBJ_EVENT_GFX_EXPERT_F,
OBJ_EVENT_GFX_CYCLING_TRIATHLETE_F,
OBJ_EVENT_GFX_RUNNING_TRIATHLETE_F,
OBJ_EVENT_GFX_RUNNING_TRIATHLETE_F,
OBJ_EVENT_GFX_GIRL_3,
OBJ_EVENT_GFX_WOMAN_5,
OBJ_EVENT_GFX_RUNNING_TRIATHLETE_F,
OBJ_EVENT_GFX_PICNICKER,
OBJ_EVENT_GFX_WOMAN_2,
OBJ_EVENT_GFX_PICNICKER,
OBJ_EVENT_GFX_LASS
};
2018-12-03 05:22:42 +01:00
2019-11-24 22:58:40 +01:00
// Excludes the unused RS_FACILITY_CLASS_BOARDER_1 and _2
static const u8 sRubyFacilityClassToEmerald[RS_FACILITY_CLASSES_COUNT - 2][2] =
{
{RS_FACILITY_CLASS_AQUA_LEADER_ARCHIE, FACILITY_CLASS_AQUA_LEADER_ARCHIE},
{RS_FACILITY_CLASS_AQUA_GRUNT_M, FACILITY_CLASS_AQUA_GRUNT_M},
{RS_FACILITY_CLASS_AQUA_GRUNT_F, FACILITY_CLASS_AQUA_GRUNT_F},
{RS_FACILITY_CLASS_AROMA_LADY, FACILITY_CLASS_AROMA_LADY},
{RS_FACILITY_CLASS_RUIN_MANIAC, FACILITY_CLASS_RUIN_MANIAC},
{RS_FACILITY_CLASS_INTERVIEWER, FACILITY_CLASS_INTERVIEWER},
{RS_FACILITY_CLASS_TUBER_F, FACILITY_CLASS_TUBER_F},
{RS_FACILITY_CLASS_TUBER_M, FACILITY_CLASS_TUBER_M},
{RS_FACILITY_CLASS_COOLTRAINER_M, FACILITY_CLASS_COOLTRAINER_M},
{RS_FACILITY_CLASS_COOLTRAINER_F, FACILITY_CLASS_COOLTRAINER_F},
{RS_FACILITY_CLASS_HEX_MANIAC, FACILITY_CLASS_HEX_MANIAC},
{RS_FACILITY_CLASS_LADY, FACILITY_CLASS_LADY},
{RS_FACILITY_CLASS_BEAUTY, FACILITY_CLASS_BEAUTY},
{RS_FACILITY_CLASS_RICH_BOY, FACILITY_CLASS_RICH_BOY},
{RS_FACILITY_CLASS_POKEMANIAC, FACILITY_CLASS_POKEMANIAC},
{RS_FACILITY_CLASS_SWIMMER_M, FACILITY_CLASS_SWIMMER_M},
{RS_FACILITY_CLASS_BLACK_BELT, FACILITY_CLASS_BLACK_BELT},
{RS_FACILITY_CLASS_GUITARIST, FACILITY_CLASS_GUITARIST},
{RS_FACILITY_CLASS_KINDLER, FACILITY_CLASS_KINDLER},
{RS_FACILITY_CLASS_CAMPER, FACILITY_CLASS_CAMPER},
{RS_FACILITY_CLASS_BUG_MANIAC, FACILITY_CLASS_BUG_MANIAC},
{RS_FACILITY_CLASS_PSYCHIC_M, FACILITY_CLASS_PSYCHIC_M},
{RS_FACILITY_CLASS_PSYCHIC_F, FACILITY_CLASS_PSYCHIC_F},
{RS_FACILITY_CLASS_GENTLEMAN, FACILITY_CLASS_GENTLEMAN},
{RS_FACILITY_CLASS_ELITE_FOUR_M, FACILITY_CLASS_ELITE_FOUR_SIDNEY},
{RS_FACILITY_CLASS_ELITE_FOUR_F, FACILITY_CLASS_ELITE_FOUR_PHOEBE},
{RS_FACILITY_CLASS_LEADER_F, FACILITY_CLASS_LEADER_ROXANNE},
{RS_FACILITY_CLASS_LEADER_M, FACILITY_CLASS_LEADER_BRAWLY},
{RS_FACILITY_CLASS_LEADER_MF, FACILITY_CLASS_LEADER_TATE_AND_LIZA},
{RS_FACILITY_CLASS_SCHOOL_KID_M, FACILITY_CLASS_SCHOOL_KID_M},
{RS_FACILITY_CLASS_SCHOOL_KID_F, FACILITY_CLASS_SCHOOL_KID_F},
{RS_FACILITY_CLASS_SR_AND_JR, FACILITY_CLASS_SR_AND_JR},
{RS_FACILITY_CLASS_POKEFAN_M, FACILITY_CLASS_POKEFAN_M},
{RS_FACILITY_CLASS_POKEFAN_F, FACILITY_CLASS_POKEFAN_F},
{RS_FACILITY_CLASS_EXPERT_M, FACILITY_CLASS_EXPERT_M},
{RS_FACILITY_CLASS_EXPERT_F, FACILITY_CLASS_EXPERT_F},
{RS_FACILITY_CLASS_YOUNGSTER, FACILITY_CLASS_YOUNGSTER},
{RS_FACILITY_CLASS_CHAMPION, FACILITY_CLASS_CHAMPION_WALLACE},
{RS_FACILITY_CLASS_FISHERMAN, FACILITY_CLASS_FISHERMAN},
{RS_FACILITY_CLASS_CYCLING_TRIATHLETE_M, FACILITY_CLASS_CYCLING_TRIATHLETE_M},
{RS_FACILITY_CLASS_CYCLING_TRIATHLETE_F, FACILITY_CLASS_CYCLING_TRIATHLETE_F},
{RS_FACILITY_CLASS_RUNNING_TRIATHLETE_M, FACILITY_CLASS_RUNNING_TRIATHLETE_M},
{RS_FACILITY_CLASS_RUNNING_TRIATHLETE_F, FACILITY_CLASS_RUNNING_TRIATHLETE_F},
{RS_FACILITY_CLASS_SWIMMING_TRIATHLETE_M, FACILITY_CLASS_SWIMMING_TRIATHLETE_M},
{RS_FACILITY_CLASS_SWIMMING_TRIATHLETE_F, FACILITY_CLASS_SWIMMING_TRIATHLETE_F},
{RS_FACILITY_CLASS_DRAGON_TAMER, FACILITY_CLASS_DRAGON_TAMER},
{RS_FACILITY_CLASS_BIRD_KEEPER, FACILITY_CLASS_BIRD_KEEPER},
{RS_FACILITY_CLASS_NINJA_BOY, FACILITY_CLASS_NINJA_BOY},
{RS_FACILITY_CLASS_BATTLE_GIRL, FACILITY_CLASS_BATTLE_GIRL},
{RS_FACILITY_CLASS_PARASOL_LADY, FACILITY_CLASS_PARASOL_LADY},
{RS_FACILITY_CLASS_SWIMMER_F, FACILITY_CLASS_SWIMMER_F},
{RS_FACILITY_CLASS_PICNICKER, FACILITY_CLASS_PICNICKER},
{RS_FACILITY_CLASS_TWINS, FACILITY_CLASS_TWINS},
{RS_FACILITY_CLASS_SAILOR, FACILITY_CLASS_SAILOR},
{RS_FACILITY_CLASS_COLLECTOR, FACILITY_CLASS_COLLECTOR},
{RS_FACILITY_CLASS_WALLY, FACILITY_CLASS_WALLY},
{RS_FACILITY_CLASS_BRENDAN_1, FACILITY_CLASS_BRENDAN},
{RS_FACILITY_CLASS_BRENDAN_2, FACILITY_CLASS_BRENDAN_2},
{RS_FACILITY_CLASS_BRENDAN_3, FACILITY_CLASS_BRENDAN_3},
{RS_FACILITY_CLASS_MAY_1, FACILITY_CLASS_MAY},
{RS_FACILITY_CLASS_MAY_2, FACILITY_CLASS_MAY_2},
{RS_FACILITY_CLASS_MAY_3, FACILITY_CLASS_MAY_3},
{RS_FACILITY_CLASS_PKMN_BREEDER_M, FACILITY_CLASS_PKMN_BREEDER_M},
{RS_FACILITY_CLASS_PKMN_BREEDER_F, FACILITY_CLASS_PKMN_BREEDER_F},
{RS_FACILITY_CLASS_PKMN_RANGER_M, FACILITY_CLASS_PKMN_RANGER_M},
{RS_FACILITY_CLASS_PKMN_RANGER_F, FACILITY_CLASS_PKMN_RANGER_F},
{RS_FACILITY_CLASS_MAGMA_LEADER, FACILITY_CLASS_MAGMA_LEADER_MAXIE},
{RS_FACILITY_CLASS_MAGMA_GRUNT_M, FACILITY_CLASS_MAGMA_GRUNT_M},
{RS_FACILITY_CLASS_MAGMA_GRUNT_F, FACILITY_CLASS_MAGMA_GRUNT_F},
{RS_FACILITY_CLASS_LASS, FACILITY_CLASS_LASS},
{RS_FACILITY_CLASS_BUG_CATCHER, FACILITY_CLASS_BUG_CATCHER},
{RS_FACILITY_CLASS_HIKER, FACILITY_CLASS_HIKER},
{RS_FACILITY_CLASS_YOUNG_COUPLE, FACILITY_CLASS_YOUNG_COUPLE},
{RS_FACILITY_CLASS_OLD_COUPLE, FACILITY_CLASS_OLD_COUPLE},
{RS_FACILITY_CLASS_SIS_AND_BRO, FACILITY_CLASS_SIS_AND_BRO},
2018-12-03 05:22:42 +01:00
};
#define PARTNER_TEXTS(name) \
BattleFrontier_BattleTowerMultiPartnerRoom_Text_##name##Intro, \
BattleFrontier_BattleTowerMultiPartnerRoom_Text_##name##Mon1, \
BattleFrontier_BattleTowerMultiPartnerRoom_Text_##name##Mon2Ask, \
BattleFrontier_BattleTowerMultiPartnerRoom_Text_##name##Accept, \
BattleFrontier_BattleTowerMultiPartnerRoom_Text_##name##Reject
static const u8 *const sPartnerApprenticeTexts1[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(Apprentice1)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerApprenticeTexts2[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(Apprentice2)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerApprenticeTexts3[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(Apprentice3)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerApprenticeTexts4[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(Apprentice4)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerApprenticeTexts5[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(Apprentice5)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerApprenticeTexts6[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(Apprentice6)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerApprenticeTexts7[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(Apprentice7)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerApprenticeTexts8[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(Apprentice8)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerApprenticeTexts9[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(Apprentice9)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerApprenticeTexts10[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(Apprentice10)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerApprenticeTexts11[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(Apprentice11)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerApprenticeTexts12[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(Apprentice12)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerApprenticeTexts13[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(Apprentice13)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerApprenticeTexts14[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(Apprentice14)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerApprenticeTexts15[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(Apprentice15)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerApprenticeTexts16[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(Apprentice16)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsLass[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(Lass)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsYoungster[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(Youngster)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsHiker[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(Hiker)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsBeauty[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(Beauty)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsFisherman[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(Fisherman)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsLady[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(Lady)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsCyclingTriathleteF[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(CyclingTriathleteF)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsBugCatcher[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(BugCatcher)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsSchoolKidM[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(SchoolKidM)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsRichBoy[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(RichBoy)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsBlackBelt[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(BlackBelt)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsTuberF[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(TuberF)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsHexManiac[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(HexManiac)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsPkmnBreederM[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(PkmnBreederM)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsRunningTriathleteF[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(RunningTriathleteF)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsRunningTriathleteM[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(RunningTriathleteM)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsBattleGirl[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(BattleGirl)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsCyclingTriathleteM[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(CyclingTriathleteM)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsTuberM[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(TuberM)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsGuitarist[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(Guitarist)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsGentleman[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(Gentleman)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsPokefanM[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(PokefanM)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsExpertM[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(ExpertM)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsExpertF[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(ExpertF)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsDragonTamer[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(DragonTamer)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsBirdKeeper[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(BirdKeeper)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsNinjaBoy[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(NinjaBoy)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsParasolLady[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(ParasolLady)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsBugManiac[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(BugManiac)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsSailor[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(Sailor)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsCollector[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(Collector)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsPkmnRangerM[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(PkmnRangerM)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsPkmnRangerF[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(PkmnRangerF)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsAromaLady[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(AromaLady)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsRuinManiac[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(RuinManiac)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsCoolTrainerM[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(CoolTrainerM)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsCoolTrainerF[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(CoolTrainerF)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsPokemaniac[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(Pokemaniac)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsKindler[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(Kindler)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsCamper[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(Camper)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsPicnicker[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(Picnicker)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsPsychicM[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(PsychicM)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsPsychicF[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(PsychicF)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsSchoolKidF[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(SchoolKidF)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsPkmnBreederF[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(PkmnBreederF)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsPokefanF[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(PokefanF)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsSwimmerF[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(SwimmerF)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsSwimmingTriathleteM[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(SwimmingTriathleteM)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsSwimmingTriathleteF[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(SwimmingTriathleteF)
2018-12-03 05:22:42 +01:00
};
static const u8 *const sPartnerTextsSwimmerM[] =
2018-12-03 05:22:42 +01:00
{
PARTNER_TEXTS(SwimmerM)
2018-12-03 05:22:42 +01:00
};
struct
{
u32 facilityClass;
const u8 *const *strings;
} const sPartnerTrainerTextTables[] =
{
{FACILITY_CLASS_LASS, sPartnerTextsLass},
{FACILITY_CLASS_YOUNGSTER, sPartnerTextsYoungster},
{FACILITY_CLASS_HIKER, sPartnerTextsHiker},
{FACILITY_CLASS_BEAUTY, sPartnerTextsBeauty},
{FACILITY_CLASS_FISHERMAN, sPartnerTextsFisherman},
{FACILITY_CLASS_LADY, sPartnerTextsLady},
{FACILITY_CLASS_CYCLING_TRIATHLETE_F, sPartnerTextsCyclingTriathleteF},
{FACILITY_CLASS_BUG_CATCHER, sPartnerTextsBugCatcher},
{FACILITY_CLASS_SCHOOL_KID_M, sPartnerTextsSchoolKidM},
{FACILITY_CLASS_RICH_BOY, sPartnerTextsRichBoy},
{FACILITY_CLASS_BLACK_BELT, sPartnerTextsBlackBelt},
{FACILITY_CLASS_TUBER_F, sPartnerTextsTuberF},
{FACILITY_CLASS_HEX_MANIAC, sPartnerTextsHexManiac},
{FACILITY_CLASS_PKMN_BREEDER_M, sPartnerTextsPkmnBreederM},
{FACILITY_CLASS_RUNNING_TRIATHLETE_F, sPartnerTextsRunningTriathleteF},
{FACILITY_CLASS_RUNNING_TRIATHLETE_M, sPartnerTextsRunningTriathleteM},
{FACILITY_CLASS_BATTLE_GIRL, sPartnerTextsBattleGirl},
{FACILITY_CLASS_CYCLING_TRIATHLETE_M, sPartnerTextsCyclingTriathleteM},
{FACILITY_CLASS_TUBER_M, sPartnerTextsTuberM},
{FACILITY_CLASS_GUITARIST, sPartnerTextsGuitarist},
{FACILITY_CLASS_GENTLEMAN, sPartnerTextsGentleman},
{FACILITY_CLASS_POKEFAN_M, sPartnerTextsPokefanM},
{FACILITY_CLASS_EXPERT_M, sPartnerTextsExpertM},
{FACILITY_CLASS_EXPERT_F, sPartnerTextsExpertF},
{FACILITY_CLASS_DRAGON_TAMER, sPartnerTextsDragonTamer},
{FACILITY_CLASS_BIRD_KEEPER, sPartnerTextsBirdKeeper},
{FACILITY_CLASS_NINJA_BOY, sPartnerTextsNinjaBoy},
{FACILITY_CLASS_PARASOL_LADY, sPartnerTextsParasolLady},
{FACILITY_CLASS_BUG_MANIAC, sPartnerTextsBugManiac},
{FACILITY_CLASS_SAILOR, sPartnerTextsSailor},
{FACILITY_CLASS_COLLECTOR, sPartnerTextsCollector},
{FACILITY_CLASS_PKMN_RANGER_M, sPartnerTextsPkmnRangerM},
{FACILITY_CLASS_PKMN_RANGER_F, sPartnerTextsPkmnRangerF},
{FACILITY_CLASS_AROMA_LADY, sPartnerTextsAromaLady},
{FACILITY_CLASS_RUIN_MANIAC, sPartnerTextsRuinManiac},
{FACILITY_CLASS_COOLTRAINER_M, sPartnerTextsCoolTrainerM},
{FACILITY_CLASS_COOLTRAINER_F, sPartnerTextsCoolTrainerF},
{FACILITY_CLASS_POKEMANIAC, sPartnerTextsPokemaniac},
{FACILITY_CLASS_KINDLER, sPartnerTextsKindler},
{FACILITY_CLASS_CAMPER, sPartnerTextsCamper},
{FACILITY_CLASS_PICNICKER, sPartnerTextsPicnicker},
{FACILITY_CLASS_PSYCHIC_M, sPartnerTextsPsychicM},
{FACILITY_CLASS_PSYCHIC_F, sPartnerTextsPsychicF},
{FACILITY_CLASS_SCHOOL_KID_F, sPartnerTextsSchoolKidF},
{FACILITY_CLASS_PKMN_BREEDER_F, sPartnerTextsPkmnBreederF},
{FACILITY_CLASS_POKEFAN_F, sPartnerTextsPokefanF},
{FACILITY_CLASS_SWIMMER_F, sPartnerTextsSwimmerF},
{FACILITY_CLASS_SWIMMING_TRIATHLETE_M, sPartnerTextsSwimmingTriathleteM},
{FACILITY_CLASS_SWIMMING_TRIATHLETE_F, sPartnerTextsSwimmingTriathleteF},
{FACILITY_CLASS_SWIMMER_M, sPartnerTextsSwimmerM}
};
static const u8 *const *const sPartnerApprenticeTextTables[NUM_APPRENTICES] =
{
sPartnerApprenticeTexts1,
sPartnerApprenticeTexts2,
sPartnerApprenticeTexts3,
sPartnerApprenticeTexts4,
sPartnerApprenticeTexts5,
sPartnerApprenticeTexts6,
sPartnerApprenticeTexts7,
sPartnerApprenticeTexts8,
sPartnerApprenticeTexts9,
sPartnerApprenticeTexts10,
sPartnerApprenticeTexts11,
sPartnerApprenticeTexts12,
sPartnerApprenticeTexts13,
sPartnerApprenticeTexts14,
sPartnerApprenticeTexts15,
sPartnerApprenticeTexts16
2018-12-03 05:22:42 +01:00
};
struct
{
u16 species;
u8 fixedIV;
u8 level;
u8 nature;
u8 evs[NUM_STATS];
2019-09-08 17:53:48 +02:00
u16 moves[MAX_MON_MOVES];
} const sStevenMons[MULTI_PARTY_SIZE] =
2018-12-03 05:22:42 +01:00
{
{
.species = SPECIES_METANG,
.fixedIV = MAX_PER_STAT_IVS,
2018-12-03 05:22:42 +01:00
.level = 42,
.nature = NATURE_BRAVE,
.evs = {0, 252, 252, 0, 6, 0},
.moves = {MOVE_LIGHT_SCREEN, MOVE_PSYCHIC, MOVE_REFLECT, MOVE_METAL_CLAW}
},
{
.species = SPECIES_SKARMORY,
.fixedIV = MAX_PER_STAT_IVS,
2018-12-03 05:22:42 +01:00
.level = 43,
.nature = NATURE_IMPISH,
.evs = {252, 0, 0, 0, 6, 252},
.moves = {MOVE_TOXIC, MOVE_AERIAL_ACE, MOVE_PROTECT, MOVE_STEEL_WING}
},
{
.species = SPECIES_AGGRON,
.fixedIV = MAX_PER_STAT_IVS,
2018-12-03 05:22:42 +01:00
.level = 44,
.nature = NATURE_ADAMANT,
.evs = {0, 252, 0, 0, 252, 6},
.moves = {MOVE_THUNDER, MOVE_PROTECT, MOVE_SOLAR_BEAM, MOVE_DRAGON_CLAW}
}
};
#include "data/battle_frontier/battle_tent.h"
2019-11-17 00:11:27 +01:00
static void (* const sBattleTowerFuncs[])(void) =
{
[BATTLE_TOWER_FUNC_INIT] = InitTowerChallenge,
[BATTLE_TOWER_FUNC_GET_DATA] = GetTowerData,
[BATTLE_TOWER_FUNC_SET_DATA] = SetTowerData,
[BATTLE_TOWER_FUNC_SET_OPPONENT] = SetNextFacilityOpponent,
[BATTLE_TOWER_FUNC_SET_BATTLE_WON] = SetTowerBattleWon,
[BATTLE_TOWER_FUNC_GIVE_RIBBONS] = AwardBattleTowerRibbons,
[BATTLE_TOWER_FUNC_SAVE] = SaveTowerChallenge,
[BATTLE_TOWER_FUNC_GET_OPPONENT_INTRO] = GetOpponentIntroSpeech,
2020-12-24 22:18:47 +01:00
[BATTLE_TOWER_FUNC_NOP] = BattleTowerNop1,
[BATTLE_TOWER_FUNC_NOP2] = BattleTowerNop2,
[BATTLE_TOWER_FUNC_LOAD_PARTNERS] = LoadMultiPartnerCandidatesData,
[BATTLE_TOWER_FUNC_PARTNER_MSG] = ShowPartnerCandidateMessage,
[BATTLE_TOWER_FUNC_LOAD_LINK_OPPONENTS] = LoadLinkMultiOpponentsData,
2020-12-24 22:18:47 +01:00
[BATTLE_TOWER_FUNC_TRY_CLOSE_LINK] = TowerTryCloseLink,
[BATTLE_TOWER_FUNC_SET_PARTNER_GFX] = SetMultiPartnerGfx,
[BATTLE_TOWER_FUNC_SET_INTERVIEW_DATA] = SetTowerInterviewData,
2018-10-25 21:27:10 +02:00
};
2019-11-24 22:58:40 +01:00
static const u32 sWinStreakFlags[][2] =
2018-10-25 21:27:10 +02:00
{
2019-11-24 22:58:40 +01:00
{STREAK_TOWER_SINGLES_50, STREAK_TOWER_SINGLES_OPEN},
{STREAK_TOWER_DOUBLES_50, STREAK_TOWER_DOUBLES_OPEN},
{STREAK_TOWER_MULTIS_50, STREAK_TOWER_MULTIS_OPEN},
{STREAK_TOWER_LINK_MULTIS_50, STREAK_TOWER_LINK_MULTIS_OPEN},
2018-10-25 21:27:10 +02:00
};
2019-11-24 22:58:40 +01:00
static const u32 sWinStreakMasks[][2] =
2018-10-25 21:27:10 +02:00
{
2019-11-24 22:58:40 +01:00
{~(STREAK_TOWER_SINGLES_50), ~(STREAK_TOWER_SINGLES_OPEN)},
{~(STREAK_TOWER_DOUBLES_50), ~(STREAK_TOWER_DOUBLES_OPEN)},
{~(STREAK_TOWER_MULTIS_50), ~(STREAK_TOWER_MULTIS_OPEN)},
{~(STREAK_TOWER_LINK_MULTIS_50), ~(STREAK_TOWER_LINK_MULTIS_OPEN)},
2018-10-25 21:27:10 +02:00
};
// The challenge number at which an Apprentice can appear, depending on how many of their questions were answered
static const u8 sApprenticeChallengeThreshold[MAX_APPRENTICE_QUESTIONS] =
2018-10-25 21:27:10 +02:00
{
1, 2, 3, 4, 5, 8, 9, 10, 11, 12
2018-10-25 21:27:10 +02:00
};
2019-11-24 22:58:40 +01:00
// Unclear why this was duplicated
static const u8 sBattleTowerPartySizes2[] =
2018-10-25 21:27:10 +02:00
{
2019-11-24 22:58:40 +01:00
[FRONTIER_MODE_SINGLES] = FRONTIER_PARTY_SIZE,
[FRONTIER_MODE_DOUBLES] = FRONTIER_DOUBLES_PARTY_SIZE,
[FRONTIER_MODE_MULTIS] = FRONTIER_MULTI_PARTY_SIZE,
[FRONTIER_MODE_LINK_MULTIS] = FRONTIER_MULTI_PARTY_SIZE,
2018-10-25 21:27:10 +02:00
};
2019-12-21 00:52:29 +01:00
// Trainer ID ranges for possible frontier trainers to encounter on particular challenges
// Trainers are scaled by difficulty, so higher trainer IDs have better teams
static const u16 sFrontierTrainerIdRanges[][2] =
2018-10-25 21:27:10 +02:00
{
2019-12-21 00:52:29 +01:00
{FRONTIER_TRAINER_BRADY, FRONTIER_TRAINER_JILL}, // 0 - 99
{FRONTIER_TRAINER_TREVIN, FRONTIER_TRAINER_CHLOE}, // 80 - 119
{FRONTIER_TRAINER_ERIK, FRONTIER_TRAINER_SOFIA}, // 100 - 139
{FRONTIER_TRAINER_NORTON, FRONTIER_TRAINER_JAZLYN}, // 120 - 159
{FRONTIER_TRAINER_BRADEN, FRONTIER_TRAINER_ALISON}, // 140 - 179
{FRONTIER_TRAINER_ZACHERY, FRONTIER_TRAINER_LAMAR}, // 160 - 199
{FRONTIER_TRAINER_HANK, FRONTIER_TRAINER_TESS}, // 180 - 219
{FRONTIER_TRAINER_JAXON, FRONTIER_TRAINER_GRETEL}, // 200 - 299
2018-10-25 21:27:10 +02:00
};
2019-12-21 00:52:29 +01:00
static const u16 sFrontierTrainerIdRangesHard[][2] =
2018-10-25 21:27:10 +02:00
{
2019-12-21 00:52:29 +01:00
{FRONTIER_TRAINER_ERIK, FRONTIER_TRAINER_CHLOE}, // 100 - 119
{FRONTIER_TRAINER_NORTON, FRONTIER_TRAINER_SOFIA}, // 120 - 139
{FRONTIER_TRAINER_BRADEN, FRONTIER_TRAINER_JAZLYN}, // 140 - 159
{FRONTIER_TRAINER_ZACHERY, FRONTIER_TRAINER_ALISON}, // 160 - 179
{FRONTIER_TRAINER_HANK, FRONTIER_TRAINER_LAMAR}, // 180 - 199
{FRONTIER_TRAINER_JAXON, FRONTIER_TRAINER_TESS}, // 200 - 219
{FRONTIER_TRAINER_LEON, FRONTIER_TRAINER_RAUL}, // 220 - 239
{FRONTIER_TRAINER_JAXON, FRONTIER_TRAINER_GRETEL}, // 200 - 299
};
2021-03-21 04:47:08 +01:00
// Unknown, unused data
static const u16 sUnused[] = { 179, 141, 200, 183 };
2018-10-25 21:27:10 +02:00
2019-11-24 22:58:40 +01:00
static const u8 sBattleTowerPartySizes[FRONTIER_MODE_COUNT] =
2018-10-25 21:27:10 +02:00
{
2019-11-24 22:58:40 +01:00
[FRONTIER_MODE_SINGLES] = FRONTIER_PARTY_SIZE,
[FRONTIER_MODE_DOUBLES] = FRONTIER_DOUBLES_PARTY_SIZE,
[FRONTIER_MODE_MULTIS] = FRONTIER_MULTI_PARTY_SIZE,
[FRONTIER_MODE_LINK_MULTIS] = FRONTIER_MULTI_PARTY_SIZE,
2018-10-25 21:27:10 +02:00
};
2019-11-24 22:58:40 +01:00
static const u16 sRecordTrainerSpeechWon[] =
2018-10-25 21:27:10 +02:00
{
2019-11-24 22:58:40 +01:00
EC_WORD_YAY, EC_WORD_YAY, EC_WORD_EXCL_EXCL, EC_WORD_I_VE, EC_WORD_WON, EC_WORD_EXCL_EXCL
2018-10-25 21:27:10 +02:00
};
2019-11-24 22:58:40 +01:00
static const u16 sRecordTrainerSpeechLost[] =
2018-10-25 21:27:10 +02:00
{
2019-11-24 22:58:40 +01:00
EC_WORD_TOO, EC_WORD_BAD, EC_WORD_ELLIPSIS, EC_WORD_WE, EC_WORD_LOST, EC_WORD_ELLIPSIS
2018-10-25 21:27:10 +02:00
};
// code
2019-11-17 00:11:27 +01:00
void CallBattleTowerFunc(void)
{
2019-11-17 00:11:27 +01:00
sBattleTowerFuncs[gSpecialVar_0x8004]();
}
static void InitTowerChallenge(void)
{
u32 lvlMode = gSaveBlock2Ptr->frontier.lvlMode;
u32 battleMode = VarGet(VAR_FRONTIER_BATTLE_MODE);
2019-11-24 22:58:40 +01:00
gSaveBlock2Ptr->frontier.challengeStatus = CHALLENGE_STATUS_SAVING;
2018-10-21 00:06:42 +02:00
gSaveBlock2Ptr->frontier.curChallengeBattleNum = 0;
2019-11-24 22:58:40 +01:00
gSaveBlock2Ptr->frontier.challengePaused = FALSE;
gSaveBlock2Ptr->frontier.disableRecordBattle = FALSE;
2019-11-24 22:58:40 +01:00
ResetFrontierTrainerIds();
if (!(gSaveBlock2Ptr->frontier.winStreakActiveFlags & sWinStreakFlags[battleMode][lvlMode]))
2018-10-27 17:39:05 +02:00
gSaveBlock2Ptr->frontier.towerWinStreaks[battleMode][lvlMode] = 0;
2018-10-22 19:22:57 +02:00
ValidateBattleTowerRecordChecksums();
2018-12-27 23:30:47 +01:00
SetDynamicWarp(0, gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum, -1);
gTrainerBattleOpponent_A = 0;
}
static void GetTowerData(void)
{
u32 lvlMode = gSaveBlock2Ptr->frontier.lvlMode;
u32 battleMode = VarGet(VAR_FRONTIER_BATTLE_MODE);
switch (gSpecialVar_0x8005)
{
case 0:
break;
2019-11-29 04:46:39 +01:00
case TOWER_DATA_WIN_STREAK:
gSpecialVar_Result = GetCurrentBattleTowerWinStreak(lvlMode, battleMode);
break;
2019-11-29 04:46:39 +01:00
case TOWER_DATA_WIN_STREAK_ACTIVE:
2019-11-24 22:58:40 +01:00
gSpecialVar_Result = ((gSaveBlock2Ptr->frontier.winStreakActiveFlags & sWinStreakFlags[battleMode][lvlMode]) != 0);
break;
2019-11-29 04:46:39 +01:00
case TOWER_DATA_LVL_MODE:
2019-11-24 22:58:40 +01:00
gSaveBlock2Ptr->frontier.towerLvlMode = gSaveBlock2Ptr->frontier.lvlMode;
break;
}
}
static void SetTowerData(void)
{
u32 lvlMode = gSaveBlock2Ptr->frontier.lvlMode;
u32 battleMode = VarGet(VAR_FRONTIER_BATTLE_MODE);
switch (gSpecialVar_0x8005)
{
case 0:
break;
2019-11-29 04:46:39 +01:00
case TOWER_DATA_WIN_STREAK:
2018-10-27 17:39:05 +02:00
gSaveBlock2Ptr->frontier.towerWinStreaks[battleMode][lvlMode] = gSpecialVar_0x8006;
break;
2019-11-29 04:46:39 +01:00
case TOWER_DATA_WIN_STREAK_ACTIVE:
if (gSpecialVar_0x8006)
2019-11-24 22:58:40 +01:00
gSaveBlock2Ptr->frontier.winStreakActiveFlags |= sWinStreakFlags[battleMode][lvlMode];
else
2019-11-24 22:58:40 +01:00
gSaveBlock2Ptr->frontier.winStreakActiveFlags &= sWinStreakMasks[battleMode][lvlMode];
break;
2019-11-29 04:46:39 +01:00
case TOWER_DATA_LVL_MODE:
2019-11-24 22:58:40 +01:00
gSaveBlock2Ptr->frontier.towerLvlMode = gSaveBlock2Ptr->frontier.lvlMode;
break;
}
}
2019-11-29 04:46:39 +01:00
static void SetTowerBattleWon(void)
{
2018-10-26 23:54:41 +02:00
if (gTrainerBattleOpponent_A == TRAINER_EREADER)
2018-10-22 19:22:57 +02:00
ClearEReaderTrainer(&gSaveBlock2Ptr->frontier.ereaderTrainer);
2019-11-29 04:46:39 +01:00
// towerNumWins is never read outside this conditional
2019-11-24 22:58:40 +01:00
if (gSaveBlock2Ptr->frontier.towerNumWins < MAX_STREAK)
gSaveBlock2Ptr->frontier.towerNumWins++;
2018-10-21 00:06:42 +02:00
gSaveBlock2Ptr->frontier.curChallengeBattleNum++;
2018-10-21 20:13:12 +02:00
SaveCurrentWinStreak();
2018-10-21 00:06:42 +02:00
gSpecialVar_Result = gSaveBlock2Ptr->frontier.curChallengeBattleNum;
}
2018-10-25 21:27:10 +02:00
static bool8 ChooseSpecialBattleTowerTrainer(void)
{
s32 i, j, validMons;
s32 trainerIds[9];
s32 idsCount = 0;
s32 winStreak = 0;
u8 lvlMode = gSaveBlock2Ptr->frontier.lvlMode;
u8 battleMode = VarGet(VAR_FRONTIER_BATTLE_MODE);
if (VarGet(VAR_FRONTIER_FACILITY) != FRONTIER_FACILITY_TOWER)
return FALSE;
winStreak = GetCurrentBattleTowerWinStreak(lvlMode, battleMode);
for (i = 0; i < BATTLE_TOWER_RECORD_COUNT; i++)
{
2018-10-21 20:13:12 +02:00
u32 *record = (u32*)(&gSaveBlock2Ptr->frontier.towerRecords[i]);
u32 recordHasData = 0;
u32 checksum = 0;
for (j = 0; j < (sizeof(struct EmeraldBattleTowerRecord) - 4) / 4; j++) // - 4, because of the last field being the checksum itself.
{
recordHasData |= record[j];
checksum += record[j];
}
validMons = 0;
for (j = 0; j < MAX_FRONTIER_PARTY_SIZE; j++)
{
2018-10-21 20:13:12 +02:00
if (gSaveBlock2Ptr->frontier.towerRecords[i].party[j].species != 0
&& gSaveBlock2Ptr->frontier.towerRecords[i].party[j].level <= GetFrontierEnemyMonLevel(lvlMode))
validMons++;
}
2019-11-24 22:58:40 +01:00
if (validMons >= sBattleTowerPartySizes2[battleMode]
2018-10-21 20:13:12 +02:00
&& gSaveBlock2Ptr->frontier.towerRecords[i].winStreak == winStreak
&& gSaveBlock2Ptr->frontier.towerRecords[i].lvlMode == lvlMode
&& recordHasData
2018-10-21 20:13:12 +02:00
&& gSaveBlock2Ptr->frontier.towerRecords[i].checksum == checksum)
{
2018-10-26 23:54:41 +02:00
trainerIds[idsCount] = i + TRAINER_RECORD_MIXING_FRIEND;
idsCount++;
}
}
if (battleMode == FRONTIER_MODE_SINGLES)
{
ValidateApprenticesChecksums();
2019-11-20 23:36:52 +01:00
for (i = 0; i < APPRENTICE_COUNT; i++)
{
if (gSaveBlock2Ptr->apprentices[i].lvlMode != 0
&& sApprenticeChallengeThreshold[gSaveBlock2Ptr->apprentices[i].numQuestions] == winStreak
&& gSaveBlock2Ptr->apprentices[i].lvlMode - 1 == lvlMode)
{
2018-10-26 23:54:41 +02:00
trainerIds[idsCount] = i + TRAINER_RECORD_MIXING_APPRENTICE;
idsCount++;
}
}
}
if (idsCount != 0)
{
gTrainerBattleOpponent_A = trainerIds[Random() % idsCount];
return TRUE;
}
else
{
return FALSE;
}
}
2018-10-21 00:06:42 +02:00
2019-11-29 04:46:39 +01:00
static void SetNextFacilityOpponent(void)
2018-10-21 00:06:42 +02:00
{
u32 lvlMode = gSaveBlock2Ptr->frontier.lvlMode;
if (lvlMode == FRONTIER_LVL_TENT)
{
2019-11-29 04:46:39 +01:00
SetNextBattleTentOpponent();
2018-10-21 00:06:42 +02:00
}
else
{
u16 id;
u32 battleMode = VarGet(VAR_FRONTIER_BATTLE_MODE);
2018-10-27 21:01:35 +02:00
u16 winStreak = GetCurrentFacilityWinStreak();
u32 challengeNum = winStreak / 7;
SetFacilityPtrsGetLevel();
2018-10-21 00:06:42 +02:00
if (battleMode == FRONTIER_MODE_MULTIS || battleMode == FRONTIER_MODE_LINK_MULTIS)
{
id = gSaveBlock2Ptr->frontier.curChallengeBattleNum;
2019-02-07 18:37:28 +01:00
gTrainerBattleOpponent_A = gSaveBlock2Ptr->frontier.trainerIds[id * 2];
gTrainerBattleOpponent_B = gSaveBlock2Ptr->frontier.trainerIds[id * 2 + 1];
2018-10-21 00:06:42 +02:00
SetBattleFacilityTrainerGfxId(gTrainerBattleOpponent_A, 0);
SetBattleFacilityTrainerGfxId(gTrainerBattleOpponent_B, 1);
}
else if (ChooseSpecialBattleTowerTrainer())
{
SetBattleFacilityTrainerGfxId(gTrainerBattleOpponent_A, 0);
2019-02-07 18:37:28 +01:00
gSaveBlock2Ptr->frontier.trainerIds[gSaveBlock2Ptr->frontier.curChallengeBattleNum] = gTrainerBattleOpponent_A;
2018-10-21 00:06:42 +02:00
}
else
{
s32 i;
while (1)
{
2019-12-21 00:52:29 +01:00
id = GetRandomScaledFrontierTrainerId(challengeNum, gSaveBlock2Ptr->frontier.curChallengeBattleNum);
2018-10-21 00:06:42 +02:00
// Ensure trainer wasn't previously fought in this challenge.
for (i = 0; i < gSaveBlock2Ptr->frontier.curChallengeBattleNum; i++)
{
2019-02-07 18:37:28 +01:00
if (gSaveBlock2Ptr->frontier.trainerIds[i] == id)
2018-10-21 00:06:42 +02:00
break;
}
if (i == gSaveBlock2Ptr->frontier.curChallengeBattleNum)
break;
}
gTrainerBattleOpponent_A = id;
SetBattleFacilityTrainerGfxId(gTrainerBattleOpponent_A, 0);
if (gSaveBlock2Ptr->frontier.curChallengeBattleNum + 1 < 7)
2019-02-07 18:37:28 +01:00
gSaveBlock2Ptr->frontier.trainerIds[gSaveBlock2Ptr->frontier.curChallengeBattleNum] = gTrainerBattleOpponent_A;
2018-10-21 00:06:42 +02:00
}
}
}
2019-12-21 00:52:29 +01:00
u16 GetRandomScaledFrontierTrainerId(u8 challengeNum, u8 battleNum)
2018-10-21 00:06:42 +02:00
{
u16 trainerId;
if (challengeNum <= 7)
{
if (battleNum == 6)
{
2019-12-21 00:52:29 +01:00
// The last battle in each challenge has a jump in difficulty, pulls from a table with higher ranges
trainerId = (sFrontierTrainerIdRangesHard[challengeNum][1] - sFrontierTrainerIdRangesHard[challengeNum][0]) + 1;
trainerId = sFrontierTrainerIdRangesHard[challengeNum][0] + (Random() % trainerId);
2018-10-21 00:06:42 +02:00
}
else
{
2019-12-21 00:52:29 +01:00
trainerId = (sFrontierTrainerIdRanges[challengeNum][1] - sFrontierTrainerIdRanges[challengeNum][0]) + 1;
trainerId = sFrontierTrainerIdRanges[challengeNum][0] + (Random() % trainerId);
2018-10-21 00:06:42 +02:00
}
}
else
{
2019-12-21 00:52:29 +01:00
// After challenge 7, trainer IDs always come from the last, hardest range, which is the same for both trainer ID tables
trainerId = (sFrontierTrainerIdRanges[7][1] - sFrontierTrainerIdRanges[7][0]) + 1;
trainerId = sFrontierTrainerIdRanges[7][0] + (Random() % trainerId);
2018-10-21 00:06:42 +02:00
}
return trainerId;
}
2019-12-21 00:52:29 +01:00
// Unused
static void GetRandomScaledFrontierTrainerIdRange(u8 challengeNum, u8 battleNum, u16 *trainerIdPtr, u8 *rangePtr)
2018-10-21 00:06:42 +02:00
{
2019-12-21 00:52:29 +01:00
u16 trainerId, range;
2018-10-21 00:06:42 +02:00
if (challengeNum <= 7)
{
if (battleNum == 6)
{
2019-12-21 00:52:29 +01:00
// The last battle in each challenge has a jump in difficulty, pulls from a table with higher ranges
range = (sFrontierTrainerIdRangesHard[challengeNum][1] - sFrontierTrainerIdRangesHard[challengeNum][0]) + 1;
trainerId = sFrontierTrainerIdRangesHard[challengeNum][0];
2018-10-21 00:06:42 +02:00
}
else
{
2019-12-21 00:52:29 +01:00
range = (sFrontierTrainerIdRanges[challengeNum][1] - sFrontierTrainerIdRanges[challengeNum][0]) + 1;
trainerId = sFrontierTrainerIdRanges[challengeNum][0];
2018-10-21 00:06:42 +02:00
}
}
else
{
2019-12-21 00:52:29 +01:00
// After challenge 7, trainer IDs always come from the last, hardest range, which is the same for both trainer ID tables
range = (sFrontierTrainerIdRanges[7][1] - sFrontierTrainerIdRanges[7][0]) + 1;
trainerId = sFrontierTrainerIdRanges[7][0];
2018-10-21 00:06:42 +02:00
}
*trainerIdPtr = trainerId;
2019-12-21 00:52:29 +01:00
*rangePtr = range;
2018-10-21 00:06:42 +02:00
}
void SetBattleFacilityTrainerGfxId(u16 trainerId, u8 tempVarId)
{
u32 i;
u8 facilityClass;
u8 trainerObjectGfxId;
SetFacilityPtrsGetLevel();
2018-10-26 23:54:41 +02:00
if (trainerId == TRAINER_EREADER)
2018-10-21 00:06:42 +02:00
{
facilityClass = gSaveBlock2Ptr->frontier.ereaderTrainer.facilityClass;
}
else if (trainerId == TRAINER_FRONTIER_BRAIN)
{
2019-11-21 06:14:40 +01:00
SetFrontierBrainObjEventGfx_2();
2018-10-21 00:06:42 +02:00
return;
}
2019-12-21 00:52:29 +01:00
else if (trainerId < FRONTIER_TRAINERS_COUNT)
2018-10-21 00:06:42 +02:00
{
facilityClass = gFacilityTrainers[trainerId].facilityClass;
}
2018-10-26 23:54:41 +02:00
else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE)
2018-10-21 00:06:42 +02:00
{
2018-10-26 23:54:41 +02:00
facilityClass = gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].facilityClass;
2018-10-21 00:06:42 +02:00
}
else
{
2018-10-26 23:54:41 +02:00
facilityClass = gApprentices[gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].id].facilityClass;
2018-10-21 00:06:42 +02:00
}
// Search male classes.
for (i = 0; i < ARRAY_COUNT(gTowerMaleFacilityClasses); i++)
{
if (gTowerMaleFacilityClasses[i] == facilityClass)
break;
}
if (i != ARRAY_COUNT(gTowerMaleFacilityClasses))
{
trainerObjectGfxId = gTowerMaleTrainerGfxIds[i];
switch (tempVarId)
{
case 0:
default:
VarSet(VAR_OBJ_GFX_ID_0, trainerObjectGfxId);
return;
case 1:
VarSet(VAR_OBJ_GFX_ID_1, trainerObjectGfxId);
return;
case 15:
VarSet(VAR_OBJ_GFX_ID_E, trainerObjectGfxId);
return;
}
}
// Search female classes.
for (i = 0; i < ARRAY_COUNT(gTowerFemaleFacilityClasses); i++)
{
if (gTowerFemaleFacilityClasses[i] == facilityClass)
break;
}
if (i != ARRAY_COUNT(gTowerFemaleFacilityClasses))
{
trainerObjectGfxId = gTowerFemaleTrainerGfxIds[i];
switch (tempVarId)
{
case 0:
default:
VarSet(VAR_OBJ_GFX_ID_0, trainerObjectGfxId);
return;
case 1:
VarSet(VAR_OBJ_GFX_ID_1, trainerObjectGfxId);
return;
case 15:
VarSet(VAR_OBJ_GFX_ID_E, trainerObjectGfxId);
return;
}
}
switch (tempVarId)
{
case 0:
default:
VarSet(VAR_OBJ_GFX_ID_0, OBJ_EVENT_GFX_BOY_1);
2018-10-21 00:06:42 +02:00
return;
case 1:
VarSet(VAR_OBJ_GFX_ID_1, OBJ_EVENT_GFX_BOY_1);
2018-10-21 00:06:42 +02:00
return;
case 15:
VarSet(VAR_OBJ_GFX_ID_E, OBJ_EVENT_GFX_BOY_1);
2018-10-21 00:06:42 +02:00
return;
}
}
void SetEReaderTrainerGfxId(void)
{
2018-10-26 23:54:41 +02:00
SetBattleFacilityTrainerGfxId(TRAINER_EREADER, 0);
2018-10-21 00:06:42 +02:00
}
u8 GetBattleFacilityTrainerGfxId(u16 trainerId)
{
u32 i;
u8 facilityClass;
u8 trainerObjectGfxId;
SetFacilityPtrsGetLevel();
2018-10-26 23:54:41 +02:00
if (trainerId == TRAINER_EREADER)
2018-10-21 00:06:42 +02:00
{
facilityClass = gSaveBlock2Ptr->frontier.ereaderTrainer.facilityClass;
}
2019-12-21 00:52:29 +01:00
else if (trainerId < FRONTIER_TRAINERS_COUNT)
2018-10-21 00:06:42 +02:00
{
facilityClass = gFacilityTrainers[trainerId].facilityClass;
}
2018-10-26 23:54:41 +02:00
else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE)
2018-10-21 00:06:42 +02:00
{
2018-10-26 23:54:41 +02:00
facilityClass = gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].facilityClass;
2018-10-21 00:06:42 +02:00
}
else
{
2018-10-26 23:54:41 +02:00
facilityClass = gApprentices[gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].id].facilityClass;
2018-10-21 00:06:42 +02:00
}
// Search male classes.
for (i = 0; i < ARRAY_COUNT(gTowerMaleFacilityClasses); i++)
{
if (gTowerMaleFacilityClasses[i] == facilityClass)
break;
}
if (i != ARRAY_COUNT(gTowerMaleFacilityClasses))
{
trainerObjectGfxId = gTowerMaleTrainerGfxIds[i];
return trainerObjectGfxId;
}
// Search female classes.
for (i = 0; i < ARRAY_COUNT(gTowerFemaleFacilityClasses); i++)
{
if (gTowerFemaleFacilityClasses[i] == facilityClass)
break;
}
if (i != ARRAY_COUNT(gTowerFemaleFacilityClasses))
{
trainerObjectGfxId = gTowerFemaleTrainerGfxIds[i];
return trainerObjectGfxId;
}
else
{
return OBJ_EVENT_GFX_BOY_1;
2018-10-21 00:06:42 +02:00
}
}
2018-10-22 19:22:57 +02:00
void PutNewBattleTowerRecord(struct EmeraldBattleTowerRecord *newRecordEm)
2018-10-21 00:06:42 +02:00
{
u16 slotValues[6];
u16 slotIds[6];
s32 i, j, k;
s32 slotsCount = 0;
2018-10-22 19:22:57 +02:00
struct EmeraldBattleTowerRecord *newRecord = newRecordEm; // Needed to match.
2018-10-21 00:06:42 +02:00
// Find a record slot of the same player and replace it.
for (i = 0; i < BATTLE_TOWER_RECORD_COUNT; i++)
2018-10-21 00:06:42 +02:00
{
k = 0;
2019-09-08 17:53:48 +02:00
for (j = 0; j < TRAINER_ID_LENGTH; j++)
2018-10-21 00:06:42 +02:00
{
2018-10-22 19:22:57 +02:00
if (gSaveBlock2Ptr->frontier.towerRecords[i].trainerId[j] != newRecord->trainerId[j])
2018-10-21 00:06:42 +02:00
break;
}
if (j == 4)
{
for (k = 0; k < PLAYER_NAME_LENGTH; k++)
{
2020-12-13 05:28:01 +01:00
#ifdef BUGFIX
if (gSaveBlock2Ptr->frontier.towerRecords[i].name[k] != newRecord->name[k])
break;
if (newRecord->name[k] == EOS)
#else
2018-10-22 19:22:57 +02:00
if (gSaveBlock2Ptr->frontier.towerRecords[i].name[j] != newRecord->name[j])
2018-10-21 00:06:42 +02:00
break;
2018-10-22 19:22:57 +02:00
if (newRecord->name[j] == EOS)
#endif
2018-10-21 00:06:42 +02:00
{
k = PLAYER_NAME_LENGTH;
break;
}
}
}
if (k == PLAYER_NAME_LENGTH)
break;
}
if (i < BATTLE_TOWER_RECORD_COUNT)
2018-10-21 00:06:42 +02:00
{
2018-10-22 19:22:57 +02:00
gSaveBlock2Ptr->frontier.towerRecords[i] = *newRecord;
2018-10-21 00:06:42 +02:00
return;
}
// Find an empty record slot.
for (i = 0; i < BATTLE_TOWER_RECORD_COUNT; i++)
2018-10-21 00:06:42 +02:00
{
2018-10-21 20:13:12 +02:00
if (gSaveBlock2Ptr->frontier.towerRecords[i].winStreak == 0)
2018-10-21 00:06:42 +02:00
break;
}
if (i < BATTLE_TOWER_RECORD_COUNT)
2018-10-21 00:06:42 +02:00
{
2018-10-22 19:22:57 +02:00
gSaveBlock2Ptr->frontier.towerRecords[i] = *newRecord;
2018-10-21 00:06:42 +02:00
return;
}
// Find possible slots to replace the record.
2018-10-21 20:13:12 +02:00
slotValues[0] = gSaveBlock2Ptr->frontier.towerRecords[0].winStreak;
2018-10-21 00:06:42 +02:00
slotIds[0] = 0;
slotsCount++;
for (i = 1; i < BATTLE_TOWER_RECORD_COUNT; i++)
2018-10-21 00:06:42 +02:00
{
for (j = 0; j < slotsCount; j++)
{
2018-10-21 20:13:12 +02:00
if (gSaveBlock2Ptr->frontier.towerRecords[i].winStreak < slotValues[j])
2018-10-21 00:06:42 +02:00
{
j = 0;
slotsCount = 1;
2018-10-21 20:13:12 +02:00
slotValues[0] = gSaveBlock2Ptr->frontier.towerRecords[i].winStreak;
2018-10-21 00:06:42 +02:00
slotIds[0] = i;
break;
}
2018-10-21 20:13:12 +02:00
else if (gSaveBlock2Ptr->frontier.towerRecords[i].winStreak > slotValues[j])
2018-10-21 00:06:42 +02:00
{
break;
}
}
if (j == slotsCount)
{
2018-10-21 20:13:12 +02:00
slotValues[slotsCount] = gSaveBlock2Ptr->frontier.towerRecords[i].winStreak;
2018-10-21 00:06:42 +02:00
slotIds[slotsCount] = i;
slotsCount++;
}
}
i = Random() % slotsCount;
2018-10-22 19:22:57 +02:00
gSaveBlock2Ptr->frontier.towerRecords[slotIds[i]] = *newRecord;
2018-10-21 00:06:42 +02:00
}
u8 GetFrontierTrainerFrontSpriteId(u16 trainerId)
{
2018-10-25 21:27:10 +02:00
SetFacilityPtrsGetLevel();
2018-10-21 00:06:42 +02:00
2018-10-26 23:54:41 +02:00
if (trainerId == TRAINER_EREADER)
2018-10-21 00:06:42 +02:00
{
return gFacilityClassToPicIndex[gSaveBlock2Ptr->frontier.ereaderTrainer.facilityClass];
}
else if (trainerId == TRAINER_FRONTIER_BRAIN)
{
return GetFrontierBrainTrainerPicIndex();
}
2019-12-21 00:52:29 +01:00
else if (trainerId < FRONTIER_TRAINERS_COUNT)
2018-10-21 00:06:42 +02:00
{
return gFacilityClassToPicIndex[gFacilityTrainers[trainerId].facilityClass];
}
2018-10-26 23:54:41 +02:00
else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE)
2018-10-21 00:06:42 +02:00
{
if (gBattleTypeFlags & BATTLE_TYPE_RECORDED)
2018-11-01 15:06:50 +01:00
return gFacilityClassToPicIndex[GetRecordedBattleRecordMixFriendClass()];
2018-10-21 00:06:42 +02:00
else
2018-10-26 23:54:41 +02:00
return gFacilityClassToPicIndex[gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].facilityClass];
2018-10-21 00:06:42 +02:00
}
else
{
if (gBattleTypeFlags & BATTLE_TYPE_RECORDED)
2018-11-01 15:06:50 +01:00
return gFacilityClassToPicIndex[gApprentices[GetRecordedBattleApprenticeId()].facilityClass];
2018-10-21 00:06:42 +02:00
else
2018-10-26 23:54:41 +02:00
return gFacilityClassToPicIndex[gApprentices[gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].id].facilityClass];
2018-10-21 00:06:42 +02:00
}
}
u8 GetFrontierOpponentClass(u16 trainerId)
{
u8 trainerClass = 0;
SetFacilityPtrsGetLevel();
2018-10-21 00:06:42 +02:00
2018-10-26 23:54:41 +02:00
if (trainerId == TRAINER_EREADER)
2018-10-21 00:06:42 +02:00
{
trainerClass = gFacilityClassToTrainerClass[gSaveBlock2Ptr->frontier.ereaderTrainer.facilityClass];
}
else if (trainerId == TRAINER_FRONTIER_BRAIN)
{
return GetFrontierBrainTrainerClass();
2018-10-21 00:06:42 +02:00
}
else if (trainerId == TRAINER_STEVEN_PARTNER)
{
trainerClass = gTrainers[TRAINER_STEVEN].trainerClass;
}
2019-12-21 00:52:29 +01:00
else if (trainerId < FRONTIER_TRAINERS_COUNT)
2018-10-21 00:06:42 +02:00
{
trainerClass = gFacilityClassToTrainerClass[gFacilityTrainers[trainerId].facilityClass];
}
2018-10-26 23:54:41 +02:00
else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE)
2018-10-21 00:06:42 +02:00
{
if (gBattleTypeFlags & BATTLE_TYPE_RECORDED)
{
2018-11-01 15:06:50 +01:00
trainerClass = gFacilityClassToTrainerClass[GetRecordedBattleRecordMixFriendClass()];
2018-10-21 00:06:42 +02:00
}
else
{
2018-10-26 23:54:41 +02:00
trainerClass = gFacilityClassToTrainerClass[gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].facilityClass];
2018-10-21 00:06:42 +02:00
}
}
else
{
if (gBattleTypeFlags & BATTLE_TYPE_RECORDED)
{
2018-11-01 15:06:50 +01:00
trainerClass = gFacilityClassToTrainerClass[gApprentices[GetRecordedBattleApprenticeId()].facilityClass];
2018-10-21 00:06:42 +02:00
}
else
{
2018-10-26 23:54:41 +02:00
trainerClass = gFacilityClassToTrainerClass[gApprentices[gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].id].facilityClass];
2018-10-21 00:06:42 +02:00
}
}
return trainerClass;
}
2018-10-25 21:27:10 +02:00
static u8 GetFrontierTrainerFacilityClass(u16 trainerId)
2018-10-21 00:06:42 +02:00
{
u8 facilityClass;
SetFacilityPtrsGetLevel();
2018-10-21 00:06:42 +02:00
2018-10-26 23:54:41 +02:00
if (trainerId == TRAINER_EREADER)
2018-10-21 00:06:42 +02:00
{
facilityClass = gSaveBlock2Ptr->frontier.ereaderTrainer.facilityClass;
}
2019-12-21 00:52:29 +01:00
else if (trainerId < FRONTIER_TRAINERS_COUNT)
2018-10-21 00:06:42 +02:00
{
facilityClass = gFacilityTrainers[trainerId].facilityClass;
}
2018-10-26 23:54:41 +02:00
else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE)
2018-10-21 00:06:42 +02:00
{
if (gBattleTypeFlags & BATTLE_TYPE_RECORDED)
2018-11-01 15:06:50 +01:00
facilityClass = GetRecordedBattleRecordMixFriendClass();
2018-10-21 00:06:42 +02:00
else
2018-10-26 23:54:41 +02:00
facilityClass = gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].facilityClass;
2018-10-21 00:06:42 +02:00
}
else
{
if (gBattleTypeFlags & BATTLE_TYPE_RECORDED)
2018-11-01 15:06:50 +01:00
facilityClass = gApprentices[GetRecordedBattleApprenticeId()].facilityClass;
2018-10-21 00:06:42 +02:00
else
2018-10-26 23:54:41 +02:00
facilityClass = gApprentices[gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].id].facilityClass;
2018-10-21 00:06:42 +02:00
}
return facilityClass;
}
void GetFrontierTrainerName(u8 *dst, u16 trainerId)
{
s32 i = 0;
SetFacilityPtrsGetLevel();
2018-10-21 00:06:42 +02:00
2018-10-26 23:54:41 +02:00
if (trainerId == TRAINER_EREADER)
2018-10-21 00:06:42 +02:00
{
for (i = 0; i < PLAYER_NAME_LENGTH; i++)
dst[i] = gSaveBlock2Ptr->frontier.ereaderTrainer.name[i];
}
else if (trainerId == TRAINER_FRONTIER_BRAIN)
{
CopyFrontierBrainTrainerName(dst);
return;
}
else if (trainerId == TRAINER_STEVEN_PARTNER)
{
for (i = 0; i < PLAYER_NAME_LENGTH; i++)
dst[i] = gTrainers[TRAINER_STEVEN].trainerName[i];
}
2019-12-21 00:52:29 +01:00
else if (trainerId < FRONTIER_TRAINERS_COUNT)
2018-10-21 00:06:42 +02:00
{
for (i = 0; i < PLAYER_NAME_LENGTH; i++)
dst[i] = gFacilityTrainers[trainerId].trainerName[i];
}
2018-10-26 23:54:41 +02:00
else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE)
2018-10-21 00:06:42 +02:00
{
if (gBattleTypeFlags & BATTLE_TYPE_RECORDED)
{
2020-12-24 22:18:47 +01:00
GetRecordedBattleRecordMixFriendName(dst);
2018-10-21 00:06:42 +02:00
return;
}
else
{
2018-10-26 23:54:41 +02:00
struct EmeraldBattleTowerRecord *record = &gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND];
2018-10-21 00:06:42 +02:00
TVShowConvertInternationalString(dst, record->name, record->language);
return;
}
}
else
{
u8 id, language;
if (gBattleTypeFlags & BATTLE_TYPE_RECORDED)
{
2018-11-01 15:06:50 +01:00
id = GetRecordedBattleApprenticeId();
language = GetRecordedBattleApprenticeLanguage();
2018-10-21 00:06:42 +02:00
}
else
{
2018-10-26 23:54:41 +02:00
struct Apprentice *apprentice = &gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE];
2018-10-21 00:06:42 +02:00
id = apprentice->id;
language = apprentice->language;
}
TVShowConvertInternationalString(dst, GetApprenticeNameInLanguage(id, language), language);
return;
}
dst[i] = EOS;
}
2018-10-25 21:27:10 +02:00
static bool8 IsFrontierTrainerFemale(u16 trainerId)
2018-10-21 00:06:42 +02:00
{
u32 i;
u8 facilityClass;
SetFacilityPtrsGetLevel();
2018-10-26 23:54:41 +02:00
if (trainerId == TRAINER_EREADER)
2018-10-21 00:06:42 +02:00
{
facilityClass = gSaveBlock2Ptr->frontier.ereaderTrainer.facilityClass;
}
else if (trainerId == TRAINER_FRONTIER_BRAIN)
{
return IsFrontierBrainFemale();
}
2019-12-21 00:52:29 +01:00
else if (trainerId < FRONTIER_TRAINERS_COUNT)
2018-10-21 00:06:42 +02:00
{
facilityClass = gFacilityTrainers[trainerId].facilityClass;
}
2018-10-26 23:54:41 +02:00
else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE)
2018-10-21 00:06:42 +02:00
{
2018-10-26 23:54:41 +02:00
facilityClass = gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].facilityClass;
2018-10-21 00:06:42 +02:00
}
else
{
2018-10-26 23:54:41 +02:00
facilityClass = gApprentices[gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].id].facilityClass;
2018-10-21 00:06:42 +02:00
}
// Search female classes.
for (i = 0; i < ARRAY_COUNT(gTowerFemaleFacilityClasses); i++)
{
if (gTowerFemaleFacilityClasses[i] == facilityClass)
break;
}
if (i != ARRAY_COUNT(gTowerFemaleFacilityClasses))
return TRUE;
else
return FALSE;
}
2018-10-22 19:22:57 +02:00
void FillFrontierTrainerParty(u8 monsCount)
2018-10-21 00:06:42 +02:00
{
ZeroEnemyPartyMons();
2018-10-22 19:22:57 +02:00
FillTrainerParty(gTrainerBattleOpponent_A, 0, monsCount);
2018-10-21 00:06:42 +02:00
}
2018-10-22 19:22:57 +02:00
void FillFrontierTrainersParties(u8 monsCount)
2018-10-21 00:06:42 +02:00
{
ZeroEnemyPartyMons();
2018-10-22 19:22:57 +02:00
FillTrainerParty(gTrainerBattleOpponent_A, 0, monsCount);
FillTrainerParty(gTrainerBattleOpponent_B, 3, monsCount);
2018-10-21 00:06:42 +02:00
}
2018-10-25 21:27:10 +02:00
static void FillTentTrainerParty(u8 monsCount)
2018-10-21 00:06:42 +02:00
{
ZeroEnemyPartyMons();
FillTentTrainerParty_(gTrainerBattleOpponent_A, 0, monsCount);
2018-10-21 00:06:42 +02:00
}
2018-10-22 19:22:57 +02:00
static void FillTrainerParty(u16 trainerId, u8 firstMonId, u8 monCount)
2018-10-21 00:06:42 +02:00
{
s32 i, j;
u16 chosenMonIndices[4];
u8 friendship = MAX_FRIENDSHIP;
u8 level = SetFacilityPtrsGetLevel();
2018-10-21 00:06:42 +02:00
u8 fixedIV = 0;
u8 bfMonCount;
2020-01-05 16:50:32 +01:00
const u16 *monSet = NULL;
2018-10-21 00:06:42 +02:00
u32 otID = 0;
2019-12-21 00:52:29 +01:00
if (trainerId < FRONTIER_TRAINERS_COUNT)
2018-10-21 00:06:42 +02:00
{
// Normal battle frontier trainer.
fixedIV = GetFrontierTrainerFixedIvs(trainerId);
2020-01-05 16:50:32 +01:00
monSet = gFacilityTrainers[gTrainerBattleOpponent_A].monSet;
2018-10-21 00:06:42 +02:00
}
2018-10-26 23:54:41 +02:00
else if (trainerId == TRAINER_EREADER)
2018-10-21 00:06:42 +02:00
{
for (i = firstMonId; i < firstMonId + 3; i++)
2019-01-13 12:12:27 +01:00
CreateBattleTowerMon(&gEnemyParty[i], &gSaveBlock2Ptr->frontier.ereaderTrainer.party[i - firstMonId]);
2018-10-21 00:06:42 +02:00
return;
}
else if (trainerId == TRAINER_FRONTIER_BRAIN)
{
CreateFrontierBrainPokemon();
return;
}
2018-10-26 23:54:41 +02:00
else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE)
2018-10-21 00:06:42 +02:00
{
// Record mixed player.
for (j = 0, i = firstMonId; i < firstMonId + monCount; j++, i++)
{
2018-10-26 23:54:41 +02:00
if (gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].party[j].species != 0
&& gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].party[j].level <= level)
2018-10-21 00:06:42 +02:00
{
CreateBattleTowerMon_HandleLevel(&gEnemyParty[i], &gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].party[j], FALSE);
2018-10-21 00:06:42 +02:00
}
}
return;
}
else
{
// Apprentice.
for (i = firstMonId; i < firstMonId + 3; i++)
2018-10-26 23:54:41 +02:00
CreateApprenticeMon(&gEnemyParty[i], &gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE], i - firstMonId);
2018-10-21 00:06:42 +02:00
return;
}
// Regular battle frontier trainer.
// Attempt to fill the trainer's party with random Pokemon until 3 have been
// successfully chosen. The trainer's party may not have duplicate pokemon species
// or duplicate held items.
2020-01-05 16:50:32 +01:00
for (bfMonCount = 0; monSet[bfMonCount] != 0xFFFF; bfMonCount++)
2018-10-21 00:06:42 +02:00
;
i = 0;
otID = Random32();
while (i != monCount)
{
2020-01-05 16:50:32 +01:00
u16 monId = monSet[Random() % bfMonCount];
if ((level == 50 || level == 20) && monId > FRONTIER_MONS_HIGH_TIER)
2018-10-21 00:06:42 +02:00
continue;
// Ensure this pokemon species isn't a duplicate.
for (j = 0; j < i + firstMonId; j++)
{
2020-01-05 16:50:32 +01:00
if (GetMonData(&gEnemyParty[j], MON_DATA_SPECIES, NULL) == gFacilityTrainerMons[monId].species)
2018-10-21 00:06:42 +02:00
break;
}
if (j != i + firstMonId)
continue;
// Ensure this Pokemon's held item isn't a duplicate.
for (j = 0; j < i + firstMonId; j++)
{
if (GetMonData(&gEnemyParty[j], MON_DATA_HELD_ITEM, NULL) != 0
2020-01-05 16:50:32 +01:00
&& GetMonData(&gEnemyParty[j], MON_DATA_HELD_ITEM, NULL) == gBattleFrontierHeldItems[gFacilityTrainerMons[monId].itemTableId])
2018-10-21 00:06:42 +02:00
break;
}
if (j != i + firstMonId)
continue;
// Ensure this exact pokemon index isn't a duplicate. This check doesn't seem necessary
// because the species and held items were already checked directly above.
for (j = 0; j < i; j++)
{
2020-01-05 16:50:32 +01:00
if (chosenMonIndices[j] == monId)
2018-10-21 00:06:42 +02:00
break;
}
if (j != i)
continue;
2020-01-05 16:50:32 +01:00
chosenMonIndices[i] = monId;
2018-10-21 00:06:42 +02:00
// Place the chosen pokemon into the trainer's party.
2018-11-11 16:44:27 +01:00
CreateMonWithEVSpreadNatureOTID(&gEnemyParty[i + firstMonId],
2020-01-05 16:50:32 +01:00
gFacilityTrainerMons[monId].species,
2018-10-21 00:06:42 +02:00
level,
2020-01-05 16:50:32 +01:00
gFacilityTrainerMons[monId].nature,
2018-10-21 00:06:42 +02:00
fixedIV,
2020-01-05 16:50:32 +01:00
gFacilityTrainerMons[monId].evSpread,
2018-10-21 00:06:42 +02:00
otID);
friendship = MAX_FRIENDSHIP;
2018-10-21 00:06:42 +02:00
// Give the chosen pokemon its specified moves.
for (j = 0; j < MAX_MON_MOVES; j++)
2018-10-21 00:06:42 +02:00
{
2020-01-05 16:50:32 +01:00
SetMonMoveSlot(&gEnemyParty[i + firstMonId], gFacilityTrainerMons[monId].moves[j], j);
if (gFacilityTrainerMons[monId].moves[j] == MOVE_FRUSTRATION)
2018-10-21 00:06:42 +02:00
friendship = 0; // Frustration is more powerful the lower the pokemon's friendship is.
}
SetMonData(&gEnemyParty[i + firstMonId], MON_DATA_FRIENDSHIP, &friendship);
2020-01-05 16:50:32 +01:00
SetMonData(&gEnemyParty[i + firstMonId], MON_DATA_HELD_ITEM, &gBattleFrontierHeldItems[gFacilityTrainerMons[monId].itemTableId]);
2018-10-21 00:06:42 +02:00
// The pokemon was successfully added to the trainer's party, so it's safe to move on to
// the next party slot.
i++;
}
}
// Probably an early draft before the 'CreateApprenticeMon' was written.
2018-10-25 21:27:10 +02:00
static void Unused_CreateApprenticeMons(u16 trainerId, u8 firstMonId)
2018-10-21 00:06:42 +02:00
{
s32 i, j;
u8 friendship = MAX_FRIENDSHIP;
2018-10-21 00:06:42 +02:00
u8 level = 0;
u8 fixedIV = 0;
struct Apprentice *apprentice = &gSaveBlock2Ptr->apprentices[0];
2019-11-20 23:36:52 +01:00
if (apprentice->numQuestions < 5)
2018-10-21 00:06:42 +02:00
fixedIV = 6;
else
fixedIV = 9;
if (gSaveBlock2Ptr->frontier.lvlMode != FRONTIER_LVL_50)
level = 100;
else
level = 50;
for (i = 0; i != 3; i++)
{
2018-10-22 19:22:57 +02:00
CreateMonWithEVSpread(&gEnemyParty[firstMonId + i], apprentice->party[i].species, level, fixedIV, 8);
friendship = MAX_FRIENDSHIP;
for (j = 0; j < MAX_MON_MOVES; j++)
2018-10-21 00:06:42 +02:00
{
2018-10-22 19:22:57 +02:00
if (apprentice->party[i].moves[j] == MOVE_FRUSTRATION)
2018-10-21 00:06:42 +02:00
friendship = 0;
}
SetMonData(&gEnemyParty[firstMonId + i], MON_DATA_FRIENDSHIP, &friendship);
2018-10-22 19:22:57 +02:00
SetMonData(&gEnemyParty[firstMonId + i], MON_DATA_HELD_ITEM, &apprentice->party[i].item);
2018-10-21 00:06:42 +02:00
}
}
2020-01-05 16:50:32 +01:00
u16 GetRandomFrontierMonFromSet(u16 trainerId)
2018-10-21 00:06:42 +02:00
{
u8 level = SetFacilityPtrsGetLevel();
2020-01-05 16:50:32 +01:00
const u16 *monSet = gFacilityTrainers[trainerId].monSet;
u8 numMons = 0;
u32 monId = monSet[numMons];
2018-10-21 00:06:42 +02:00
2020-01-05 16:50:32 +01:00
while (monId != 0xFFFF)
2018-10-21 00:06:42 +02:00
{
2020-01-05 16:50:32 +01:00
numMons++;
monId = monSet[numMons];
if (monId == 0xFFFF)
2018-10-21 00:06:42 +02:00
break;
}
do
{
2020-01-05 16:50:32 +01:00
monId = monSet[Random() % numMons];
} while((level == 50 || level == 20) && monId > FRONTIER_MONS_HIGH_TIER);
2018-10-21 00:06:42 +02:00
2020-01-05 16:50:32 +01:00
return monId;
2018-10-21 00:06:42 +02:00
}
2018-10-26 23:54:41 +02:00
static void FillFactoryTrainerParty(void)
2018-10-21 00:06:42 +02:00
{
ZeroEnemyPartyMons();
if (gSaveBlock2Ptr->frontier.lvlMode != FRONTIER_LVL_TENT)
2018-10-26 23:54:41 +02:00
FillFactoryFrontierTrainerParty(gTrainerBattleOpponent_A, 0);
2018-10-21 00:06:42 +02:00
else
2018-10-26 23:54:41 +02:00
FillFactoryTentTrainerParty(gTrainerBattleOpponent_A, 0);
2018-10-21 00:06:42 +02:00
}
2018-10-26 23:54:41 +02:00
static void FillFactoryFrontierTrainerParty(u16 trainerId, u8 firstMonId)
2018-10-21 00:06:42 +02:00
{
u8 i, j;
u8 friendship;
u8 level;
u8 fixedIV;
u32 otID;
2019-12-21 00:52:29 +01:00
if (trainerId < FRONTIER_TRAINERS_COUNT)
2018-10-21 00:06:42 +02:00
{
u8 lvlMode = gSaveBlock2Ptr->frontier.lvlMode; // Unused variable.
u8 battleMode = VarGet(VAR_FRONTIER_BATTLE_MODE);
2021-10-23 16:55:46 +02:00
u8 challengeNum = gSaveBlock2Ptr->frontier.towerWinStreaks[battleMode][FRONTIER_LVL_50] / 7;
2018-10-21 00:06:42 +02:00
if (gSaveBlock2Ptr->frontier.curChallengeBattleNum < 6)
2018-11-11 16:44:27 +01:00
fixedIV = GetFactoryMonFixedIV(challengeNum, 0);
2018-10-21 00:06:42 +02:00
else
2018-11-11 16:44:27 +01:00
fixedIV = GetFactoryMonFixedIV(challengeNum, 1);
2018-10-21 00:06:42 +02:00
}
2018-10-26 23:54:41 +02:00
else if (trainerId == TRAINER_EREADER)
2018-10-21 00:06:42 +02:00
{
for (i = firstMonId; i < firstMonId + 3; i++)
2019-01-13 12:12:27 +01:00
CreateBattleTowerMon(&gEnemyParty[i], &gSaveBlock2Ptr->frontier.ereaderTrainer.party[i - firstMonId]);
2018-10-21 00:06:42 +02:00
return;
}
else if (trainerId == TRAINER_FRONTIER_BRAIN)
{
2018-11-11 16:44:27 +01:00
FillFactoryBrainParty();
2018-10-21 00:06:42 +02:00
return;
}
else
{
fixedIV = MAX_PER_STAT_IVS;
2018-10-21 00:06:42 +02:00
}
level = SetFacilityPtrsGetLevel();
2018-10-21 00:06:42 +02:00
otID = T1_READ_32(gSaveBlock2Ptr->playerTrainerId);
2019-12-02 14:44:34 +01:00
for (i = 0; i < FRONTIER_PARTY_SIZE; i++)
2018-10-21 00:06:42 +02:00
{
2020-10-07 23:03:46 +02:00
u16 monId = gFrontierTempParty[i];
2018-11-11 16:44:27 +01:00
CreateMonWithEVSpreadNatureOTID(&gEnemyParty[firstMonId + i],
2020-01-05 16:50:32 +01:00
gFacilityTrainerMons[monId].species,
2018-10-21 00:06:42 +02:00
level,
2020-01-05 16:50:32 +01:00
gFacilityTrainerMons[monId].nature,
2018-10-21 00:06:42 +02:00
fixedIV,
2020-01-05 16:50:32 +01:00
gFacilityTrainerMons[monId].evSpread,
2018-10-21 00:06:42 +02:00
otID);
friendship = 0;
for (j = 0; j < MAX_MON_MOVES; j++)
2020-01-05 16:50:32 +01:00
SetMonMoveAvoidReturn(&gEnemyParty[firstMonId + i], gFacilityTrainerMons[monId].moves[j], j);
2018-10-21 00:06:42 +02:00
SetMonData(&gEnemyParty[firstMonId + i], MON_DATA_FRIENDSHIP, &friendship);
2020-01-05 16:50:32 +01:00
SetMonData(&gEnemyParty[firstMonId + i], MON_DATA_HELD_ITEM, &gBattleFrontierHeldItems[gFacilityTrainerMons[monId].itemTableId]);
2018-10-21 00:06:42 +02:00
}
}
2018-10-26 23:54:41 +02:00
static void FillFactoryTentTrainerParty(u16 trainerId, u8 firstMonId)
2018-10-21 00:06:42 +02:00
{
u8 i, j;
u8 friendship;
u8 level = 30;
u8 fixedIV = 0;
u32 otID = T1_READ_32(gSaveBlock2Ptr->playerTrainerId);
2019-12-02 14:44:34 +01:00
for (i = 0; i < FRONTIER_PARTY_SIZE; i++)
2018-10-21 00:06:42 +02:00
{
2020-10-07 23:03:46 +02:00
u16 monId = gFrontierTempParty[i];
2018-11-11 16:44:27 +01:00
CreateMonWithEVSpreadNatureOTID(&gEnemyParty[firstMonId + i],
2020-01-05 16:50:32 +01:00
gFacilityTrainerMons[monId].species,
2018-10-21 00:06:42 +02:00
level,
2020-01-05 16:50:32 +01:00
gFacilityTrainerMons[monId].nature,
2018-10-21 00:06:42 +02:00
fixedIV,
2020-01-05 16:50:32 +01:00
gFacilityTrainerMons[monId].evSpread,
2018-10-21 00:06:42 +02:00
otID);
friendship = 0;
for (j = 0; j < MAX_MON_MOVES; j++)
2018-10-21 00:06:42 +02:00
{
2020-01-05 16:50:32 +01:00
SetMonMoveAvoidReturn(&gEnemyParty[firstMonId + i], gFacilityTrainerMons[monId].moves[j], j);
if (gFacilityTrainerMons[monId].moves[j] == MOVE_FRUSTRATION)
2018-10-21 00:06:42 +02:00
friendship = 0;
}
SetMonData(&gEnemyParty[firstMonId + i], MON_DATA_FRIENDSHIP, &friendship);
2020-01-05 16:50:32 +01:00
SetMonData(&gEnemyParty[firstMonId + i], MON_DATA_HELD_ITEM, &gBattleFrontierHeldItems[gFacilityTrainerMons[monId].itemTableId]);
2018-10-21 00:06:42 +02:00
}
}
2018-10-21 20:13:12 +02:00
void FrontierSpeechToString(const u16 *words)
2018-10-21 00:06:42 +02:00
{
ConvertEasyChatWordsToString(gStringVar4, words, 3, 2);
2021-10-30 22:47:37 +02:00
if (GetStringWidth(FONT_NORMAL, gStringVar4, -1) > 204u)
2018-10-21 00:06:42 +02:00
{
s32 i = 0;
ConvertEasyChatWordsToString(gStringVar4, words, 2, 3);
while (gStringVar4[i++] != CHAR_NEWLINE)
;
while (gStringVar4[i] != CHAR_NEWLINE)
i++;
gStringVar4[i] = CHAR_PROMPT_SCROLL;
}
}
2018-10-21 20:13:12 +02:00
2019-11-29 04:46:39 +01:00
static void GetOpponentIntroSpeech(void)
2018-10-21 20:13:12 +02:00
{
u16 trainerId;
SetFacilityPtrsGetLevel();
2018-10-21 20:13:12 +02:00
if (gSpecialVar_0x8005)
trainerId = gTrainerBattleOpponent_B;
else
trainerId = gTrainerBattleOpponent_A;
2018-10-26 23:54:41 +02:00
if (trainerId == TRAINER_EREADER)
2018-10-21 20:13:12 +02:00
FrontierSpeechToString(gSaveBlock2Ptr->frontier.ereaderTrainer.greeting);
2019-12-21 00:52:29 +01:00
else if (trainerId < FRONTIER_TRAINERS_COUNT)
2018-10-21 20:13:12 +02:00
FrontierSpeechToString(gFacilityTrainers[trainerId].speechBefore);
2018-10-26 23:54:41 +02:00
else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE)
FrontierSpeechToString(gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].greeting);
2018-10-21 20:13:12 +02:00
else
2019-11-20 23:36:52 +01:00
BufferApprenticeChallengeText(trainerId - TRAINER_RECORD_MIXING_APPRENTICE);
2018-10-21 20:13:12 +02:00
}
static void HandleSpecialTrainerBattleEnd(void)
{
s32 i;
2018-11-01 15:06:50 +01:00
RecordedBattle_SaveBattleOutcome();
2018-10-21 20:13:12 +02:00
switch (gBattleScripting.specialTrainerBattleType)
{
case SPECIAL_BATTLE_TOWER:
case SPECIAL_BATTLE_DOME:
case SPECIAL_BATTLE_PALACE:
case SPECIAL_BATTLE_ARENA:
case SPECIAL_BATTLE_FACTORY:
case SPECIAL_BATTLE_PIKE_SINGLE:
case SPECIAL_BATTLE_PIKE_DOUBLE:
case SPECIAL_BATTLE_PYRAMID:
if (gSaveBlock2Ptr->frontier.battlesCount < 0xFFFFFF)
{
gSaveBlock2Ptr->frontier.battlesCount++;
if (gSaveBlock2Ptr->frontier.battlesCount % 20 == 0)
UpdateGymLeaderRematch();
}
else
{
gSaveBlock2Ptr->frontier.battlesCount = 0xFFFFFF;
}
break;
case SPECIAL_BATTLE_SECRET_BASE:
for (i = 0; i < PARTY_SIZE; i++)
{
u16 itemBefore = GetMonData(&gSaveBlock1Ptr->playerParty[i], MON_DATA_HELD_ITEM);
SetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM, &itemBefore);
}
break;
case SPECIAL_BATTLE_EREADER:
2018-10-22 19:22:57 +02:00
CopyEReaderTrainerFarewellMessage();
2018-10-21 20:13:12 +02:00
break;
}
SetMainCallback2(CB2_ReturnToFieldContinueScriptPlayMapMusic);
}
static void Task_StartBattleAfterTransition(u8 taskId)
{
if (IsBattleTransitionDone() == TRUE)
{
gMain.savedCallback = HandleSpecialTrainerBattleEnd;
SetMainCallback2(CB2_InitBattle);
DestroyTask(taskId);
}
}
void DoSpecialTrainerBattle(void)
{
s32 i;
gBattleScripting.specialTrainerBattleType = gSpecialVar_0x8004;
switch (gSpecialVar_0x8004)
{
case SPECIAL_BATTLE_TOWER:
gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_BATTLE_TOWER;
switch (VarGet(VAR_FRONTIER_BATTLE_MODE))
{
case FRONTIER_MODE_SINGLES:
2019-11-24 22:58:40 +01:00
FillFrontierTrainerParty(FRONTIER_PARTY_SIZE);
2018-10-21 20:13:12 +02:00
break;
case FRONTIER_MODE_DOUBLES:
2019-11-24 22:58:40 +01:00
FillFrontierTrainerParty(FRONTIER_DOUBLES_PARTY_SIZE);
2018-10-21 20:13:12 +02:00
gBattleTypeFlags |= BATTLE_TYPE_DOUBLE;
break;
case FRONTIER_MODE_MULTIS:
2019-11-24 22:58:40 +01:00
FillFrontierTrainersParties(FRONTIER_MULTI_PARTY_SIZE);
2019-02-07 18:37:28 +01:00
gPartnerTrainerId = gSaveBlock2Ptr->frontier.trainerIds[17];
FillPartnerParty(gPartnerTrainerId);
2018-10-21 20:13:12 +02:00
gBattleTypeFlags |= BATTLE_TYPE_DOUBLE | BATTLE_TYPE_INGAME_PARTNER | BATTLE_TYPE_MULTI | BATTLE_TYPE_TWO_OPPONENTS;
break;
case FRONTIER_MODE_LINK_MULTIS:
2021-01-13 21:17:32 +01:00
gBattleTypeFlags |= BATTLE_TYPE_DOUBLE | BATTLE_TYPE_LINK | BATTLE_TYPE_MULTI | BATTLE_TYPE_TOWER_LINK_MULTI;
2019-11-24 22:58:40 +01:00
FillFrontierTrainersParties(FRONTIER_MULTI_PARTY_SIZE);
2018-10-21 20:13:12 +02:00
break;
}
CreateTask(Task_StartBattleAfterTransition, 1);
PlayMapChosenOrBattleBGM(0);
2021-10-04 16:21:03 +02:00
BattleTransition_StartOnField(GetSpecialBattleTransition(B_TRANSITION_GROUP_B_TOWER));
2018-10-21 20:13:12 +02:00
break;
case SPECIAL_BATTLE_SECRET_BASE:
for (i = 0; i < PARTY_SIZE; i++)
{
u16 itemBefore = GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM);
SetMonData(&gSaveBlock1Ptr->playerParty[i], MON_DATA_HELD_ITEM, &itemBefore);
}
CreateTask(Task_StartBattleAfterTransition, 1);
PlayMapChosenOrBattleBGM(0);
2021-10-04 16:21:03 +02:00
BattleTransition_StartOnField(GetSpecialBattleTransition(B_TRANSITION_GROUP_SECRET_BASE));
2018-10-21 20:13:12 +02:00
break;
case SPECIAL_BATTLE_EREADER:
ZeroEnemyPartyMons();
for (i = 0; i < 3; i++)
2019-01-13 12:12:27 +01:00
CreateBattleTowerMon(&gEnemyParty[i], &gSaveBlock2Ptr->frontier.ereaderTrainer.party[i]);
2018-10-21 20:13:12 +02:00
gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_EREADER_TRAINER;
gTrainerBattleOpponent_A = 0;
CreateTask(Task_StartBattleAfterTransition, 1);
PlayMapChosenOrBattleBGM(0);
2021-10-04 16:21:03 +02:00
BattleTransition_StartOnField(GetSpecialBattleTransition(B_TRANSITION_GROUP_E_READER));
2018-10-21 20:13:12 +02:00
break;
case SPECIAL_BATTLE_DOME:
gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_DOME;
if (VarGet(VAR_FRONTIER_BATTLE_MODE) == FRONTIER_MODE_DOUBLES)
gBattleTypeFlags |= BATTLE_TYPE_DOUBLE;
if (gTrainerBattleOpponent_A == TRAINER_FRONTIER_BRAIN)
2020-04-09 21:18:53 +02:00
FillFrontierTrainerParty(DOME_BATTLE_PARTY_SIZE);
2018-10-21 20:13:12 +02:00
CreateTask(Task_StartBattleAfterTransition, 1);
2020-12-24 22:18:47 +01:00
CreateTask_PlayMapChosenOrBattleBGM(0);
2021-10-04 16:21:03 +02:00
BattleTransition_StartOnField(GetSpecialBattleTransition(B_TRANSITION_GROUP_B_DOME));
2018-10-21 20:13:12 +02:00
break;
case SPECIAL_BATTLE_PALACE:
gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_PALACE;
if (VarGet(VAR_FRONTIER_BATTLE_MODE) == FRONTIER_MODE_DOUBLES)
gBattleTypeFlags |= BATTLE_TYPE_DOUBLE;
if (gSaveBlock2Ptr->frontier.lvlMode != FRONTIER_LVL_TENT)
2019-11-24 22:58:40 +01:00
FillFrontierTrainerParty(FRONTIER_PARTY_SIZE);
2018-10-21 20:13:12 +02:00
else
2019-11-24 22:58:40 +01:00
FillTentTrainerParty(FRONTIER_PARTY_SIZE);
2018-10-21 20:13:12 +02:00
CreateTask(Task_StartBattleAfterTransition, 1);
PlayMapChosenOrBattleBGM(0);
2021-10-04 16:21:03 +02:00
BattleTransition_StartOnField(GetSpecialBattleTransition(B_TRANSITION_GROUP_B_PALACE));
2018-10-21 20:13:12 +02:00
break;
case SPECIAL_BATTLE_ARENA:
gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_ARENA;
if (gSaveBlock2Ptr->frontier.lvlMode != FRONTIER_LVL_TENT)
2019-11-24 22:58:40 +01:00
FillFrontierTrainerParty(FRONTIER_PARTY_SIZE);
2018-10-21 20:13:12 +02:00
else
2019-11-24 22:58:40 +01:00
FillTentTrainerParty(FRONTIER_PARTY_SIZE);
2018-10-21 20:13:12 +02:00
CreateTask(Task_StartBattleAfterTransition, 1);
PlayMapChosenOrBattleBGM(0);
2021-10-04 16:21:03 +02:00
BattleTransition_StartOnField(GetSpecialBattleTransition(B_TRANSITION_GROUP_B_ARENA));
2018-10-21 20:13:12 +02:00
break;
case SPECIAL_BATTLE_FACTORY:
gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_FACTORY;
if (VarGet(VAR_FRONTIER_BATTLE_MODE) == FRONTIER_MODE_DOUBLES)
gBattleTypeFlags |= BATTLE_TYPE_DOUBLE;
2018-10-26 23:54:41 +02:00
FillFactoryTrainerParty();
2018-10-21 20:13:12 +02:00
CreateTask(Task_StartBattleAfterTransition, 1);
PlayMapChosenOrBattleBGM(0);
2021-10-04 16:21:03 +02:00
BattleTransition_StartOnField(GetSpecialBattleTransition(B_TRANSITION_GROUP_B_FACTORY));
2018-10-21 20:13:12 +02:00
break;
case SPECIAL_BATTLE_PIKE_SINGLE:
gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_BATTLE_TOWER;
2019-11-24 22:58:40 +01:00
FillFrontierTrainerParty(FRONTIER_PARTY_SIZE);
2018-10-21 20:13:12 +02:00
CreateTask(Task_StartBattleAfterTransition, 1);
PlayMapChosenOrBattleBGM(0);
2021-10-04 16:21:03 +02:00
BattleTransition_StartOnField(GetSpecialBattleTransition(B_TRANSITION_GROUP_B_PIKE));
2018-10-21 20:13:12 +02:00
break;
case SPECIAL_BATTLE_PYRAMID:
gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_PYRAMID;
2019-11-24 22:58:40 +01:00
FillFrontierTrainerParty(FRONTIER_PARTY_SIZE);
2018-10-21 20:13:12 +02:00
CreateTask(Task_StartBattleAfterTransition, 1);
PlayMapChosenOrBattleBGM(0);
2021-10-04 16:21:03 +02:00
BattleTransition_StartOnField(GetSpecialBattleTransition(B_TRANSITION_GROUP_B_PYRAMID));
2018-10-21 20:13:12 +02:00
break;
case SPECIAL_BATTLE_PIKE_DOUBLE:
gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_BATTLE_TOWER | BATTLE_TYPE_DOUBLE | BATTLE_TYPE_TWO_OPPONENTS;
2018-10-22 19:22:57 +02:00
FillFrontierTrainersParties(1);
2018-10-21 20:13:12 +02:00
CreateTask(Task_StartBattleAfterTransition, 1);
PlayMapChosenOrBattleBGM(0);
2021-10-04 16:21:03 +02:00
BattleTransition_StartOnField(GetSpecialBattleTransition(B_TRANSITION_GROUP_B_PIKE));
2018-10-21 20:13:12 +02:00
break;
case SPECIAL_BATTLE_STEVEN:
gBattleTypeFlags = BATTLE_TYPE_TRAINER | BATTLE_TYPE_DOUBLE | BATTLE_TYPE_TWO_OPPONENTS | BATTLE_TYPE_MULTI | BATTLE_TYPE_INGAME_PARTNER;
FillPartnerParty(TRAINER_STEVEN_PARTNER);
2018-10-21 20:13:12 +02:00
gApproachingTrainerId = 0;
2019-11-01 22:50:54 +01:00
BattleSetup_ConfigureTrainerBattle(MossdeepCity_SpaceCenter_2F_EventScript_MaxieTrainer + 1);
2018-10-21 20:13:12 +02:00
gApproachingTrainerId = 1;
2019-11-01 22:50:54 +01:00
BattleSetup_ConfigureTrainerBattle(MossdeepCity_SpaceCenter_2F_EventScript_TabithaTrainer + 1);
2018-10-21 20:13:12 +02:00
gPartnerTrainerId = TRAINER_STEVEN_PARTNER;
CreateTask(Task_StartBattleAfterTransition, 1);
PlayMapChosenOrBattleBGM(0);
BattleTransition_StartOnField(B_TRANSITION_MAGMA);
break;
}
}
2018-10-25 21:27:10 +02:00
static void SaveCurrentWinStreak(void)
2018-10-21 20:13:12 +02:00
{
u8 lvlMode = gSaveBlock2Ptr->frontier.lvlMode;
u8 battleMode = VarGet(VAR_FRONTIER_BATTLE_MODE);
u16 winStreak = GetCurrentBattleTowerWinStreak(lvlMode, battleMode);
2018-10-27 17:39:05 +02:00
if (gSaveBlock2Ptr->frontier.towerWinStreaks[battleMode][lvlMode] < winStreak)
gSaveBlock2Ptr->frontier.towerWinStreaks[battleMode][lvlMode] = winStreak;
2018-10-21 20:13:12 +02:00
}
2020-12-24 22:18:47 +01:00
static void SaveBattleTowerRecord(void)
2018-10-21 20:13:12 +02:00
{
s32 i;
u8 lvlMode, battleMode, class;
struct EmeraldBattleTowerRecord *playerRecord = &gSaveBlock2Ptr->frontier.towerPlayer;
2018-10-22 19:22:57 +02:00
ClearBattleTowerRecord(playerRecord);
2018-10-21 20:13:12 +02:00
lvlMode = gSaveBlock2Ptr->frontier.lvlMode;
battleMode = VarGet(VAR_FRONTIER_BATTLE_MODE);
if (gSaveBlock2Ptr->playerGender != MALE)
{
class = gTowerFemaleFacilityClasses[(gSaveBlock2Ptr->playerTrainerId[0] +
gSaveBlock2Ptr->playerTrainerId[1] +
gSaveBlock2Ptr->playerTrainerId[2] +
gSaveBlock2Ptr->playerTrainerId[3]) % ARRAY_COUNT(gTowerFemaleFacilityClasses)];
}
else
{
class = gTowerMaleFacilityClasses[(gSaveBlock2Ptr->playerTrainerId[0] +
gSaveBlock2Ptr->playerTrainerId[1] +
gSaveBlock2Ptr->playerTrainerId[2] +
gSaveBlock2Ptr->playerTrainerId[3]) % ARRAY_COUNT(gTowerMaleFacilityClasses)];
}
playerRecord->lvlMode = lvlMode;
playerRecord->facilityClass = class;
2018-11-01 21:31:10 +01:00
CopyTrainerId(playerRecord->trainerId, gSaveBlock2Ptr->playerTrainerId);
2018-10-21 20:13:12 +02:00
StringCopy7(playerRecord->name, gSaveBlock2Ptr->playerName);
playerRecord->winStreak = GetCurrentBattleTowerWinStreak(lvlMode, battleMode);
2019-12-21 00:52:29 +01:00
for (i = 0; i < EASY_CHAT_BATTLE_WORDS_COUNT; i++)
2018-10-21 20:13:12 +02:00
{
2019-02-27 19:45:31 +01:00
playerRecord->greeting[i] = gSaveBlock1Ptr->easyChatBattleStart[i];
playerRecord->speechWon[i] = gSaveBlock1Ptr->easyChatBattleWon[i];
playerRecord->speechLost[i] = gSaveBlock1Ptr->easyChatBattleLost[i];
2018-10-21 20:13:12 +02:00
}
2019-11-24 22:58:40 +01:00
for (i = 0; i < MAX_FRONTIER_PARTY_SIZE; i++)
2018-10-21 20:13:12 +02:00
{
2018-10-22 19:22:57 +02:00
if (gSaveBlock2Ptr->frontier.selectedPartyMons[i] != 0)
2020-12-24 22:18:47 +01:00
ConvertPokemonToBattleTowerPokemon(&gPlayerParty[gSaveBlock2Ptr->frontier.selectedPartyMons[i] - 1], &playerRecord->party[i]);
2018-10-21 20:13:12 +02:00
}
playerRecord->language = gGameLanguage;
CalcEmeraldBattleTowerChecksum(&gSaveBlock2Ptr->frontier.towerPlayer);
SaveCurrentWinStreak();
}
static void SaveTowerChallenge(void)
2018-10-21 20:13:12 +02:00
{
u16 lvlMode = gSaveBlock2Ptr->frontier.lvlMode;
u16 battleMode = VarGet(VAR_FRONTIER_BATTLE_MODE);
2018-10-27 17:39:05 +02:00
s32 challengeNum = (signed)(gSaveBlock2Ptr->frontier.towerWinStreaks[battleMode][lvlMode] / 7);
2018-10-21 20:13:12 +02:00
if (gSpecialVar_0x8005 == 0 && (challengeNum > 1 || gSaveBlock2Ptr->frontier.curChallengeBattleNum != 0))
2020-12-24 22:18:47 +01:00
SaveBattleTowerRecord();
2018-10-21 20:13:12 +02:00
2019-11-24 22:58:40 +01:00
gSaveBlock2Ptr->frontier.challengeStatus = gSpecialVar_0x8005;
2018-10-21 20:13:12 +02:00
VarSet(VAR_TEMP_0, 0);
2019-11-24 22:58:40 +01:00
gSaveBlock2Ptr->frontier.challengePaused = TRUE;
SaveGameFrontier();
2018-10-21 20:13:12 +02:00
}
2020-12-24 22:18:47 +01:00
static void BattleTowerNop1(void)
2018-10-21 20:13:12 +02:00
{
}
2020-12-24 22:18:47 +01:00
static void BattleTowerNop2(void)
2018-10-21 20:13:12 +02:00
{
}
static void GetApprenticeMultiPartnerParty(u16 trainerId)
2018-10-21 20:13:12 +02:00
{
s32 i, count;
2020-04-09 21:18:53 +02:00
u32 validSpecies[MULTI_PARTY_SIZE];
2018-10-21 20:13:12 +02:00
u16 species1 = GetMonData(&gPlayerParty[0], MON_DATA_SPECIES, NULL);
u16 species2 = GetMonData(&gPlayerParty[1], MON_DATA_SPECIES, NULL);
count = 0;
2020-04-09 21:18:53 +02:00
for (i = 0; i < MULTI_PARTY_SIZE; i++)
2018-10-21 20:13:12 +02:00
{
2018-10-26 23:54:41 +02:00
u16 apprenticeSpecies = gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].party[i].species;
2018-10-21 20:13:12 +02:00
if (apprenticeSpecies != species1 && apprenticeSpecies != species2)
{
validSpecies[count] = i;
count++;
}
}
2020-10-07 23:03:46 +02:00
gFrontierTempParty[0] = validSpecies[Random() % count];
2018-10-21 20:13:12 +02:00
do
{
2020-10-07 23:03:46 +02:00
gFrontierTempParty[1] = validSpecies[Random() % count];
} while (gFrontierTempParty[0] == gFrontierTempParty[1]);
2018-10-21 20:13:12 +02:00
}
static void GetRecordMixFriendMultiPartnerParty(u16 trainerId)
2018-10-21 20:13:12 +02:00
{
s32 i, count;
u32 validSpecies[3];
u32 lvlMode = gSaveBlock2Ptr->frontier.lvlMode;
u16 species1 = GetMonData(&gPlayerParty[0], MON_DATA_SPECIES, NULL);
u16 species2 = GetMonData(&gPlayerParty[1], MON_DATA_SPECIES, NULL);
count = 0;
for (i = 0; i < MAX_FRONTIER_PARTY_SIZE; i++)
2018-10-21 20:13:12 +02:00
{
2018-10-26 23:54:41 +02:00
if (gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].party[i].species != species1
&& gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].party[i].species != species2
&& gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].party[i].level <= GetFrontierEnemyMonLevel(lvlMode)
&& gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].party[i].species != 0)
2018-10-21 20:13:12 +02:00
{
validSpecies[count] = i;
count++;
}
}
2020-10-07 23:03:46 +02:00
gFrontierTempParty[2] = validSpecies[Random() % count];
2018-10-21 20:13:12 +02:00
do
{
2020-10-07 23:03:46 +02:00
gFrontierTempParty[3] = validSpecies[Random() % count];
} while (gFrontierTempParty[2] == gFrontierTempParty[3]);
2018-10-21 20:13:12 +02:00
}
static void LoadMultiPartnerCandidatesData(void)
2018-10-21 20:13:12 +02:00
{
s32 i, j, k;
u32 spArray[5];
s32 r10;
u16 trainerId;
2020-01-05 16:50:32 +01:00
u16 monId;
2018-10-21 20:13:12 +02:00
u32 lvlMode, battleMode;
s32 challengeNum;
u32 species1, species2;
u32 level;
struct ObjectEventTemplate *objEventTemplates;
2018-10-21 20:13:12 +02:00
objEventTemplates = gSaveBlock1Ptr->objectEventTemplates;
2018-10-21 20:13:12 +02:00
lvlMode = gSaveBlock2Ptr->frontier.lvlMode;
battleMode = VarGet(VAR_FRONTIER_BATTLE_MODE);
2018-10-27 17:39:05 +02:00
challengeNum = gSaveBlock2Ptr->frontier.towerWinStreaks[battleMode][lvlMode] / 7;
2018-10-21 20:13:12 +02:00
species1 = GetMonData(&gPlayerParty[0], MON_DATA_SPECIES, NULL);
species2 = GetMonData(&gPlayerParty[1], MON_DATA_SPECIES, NULL);
level = SetFacilityPtrsGetLevel();
2018-10-21 20:13:12 +02:00
2018-10-22 19:22:57 +02:00
j = 0;
do
2018-10-21 20:13:12 +02:00
{
do
{
2019-12-21 00:52:29 +01:00
trainerId = GetRandomScaledFrontierTrainerId(challengeNum, 0);
2018-10-21 20:13:12 +02:00
for (i = 0; i < j; i++)
{
2019-02-07 18:37:28 +01:00
if (gSaveBlock2Ptr->frontier.trainerIds[i] == trainerId)
2018-10-21 20:13:12 +02:00
break;
2019-02-07 18:37:28 +01:00
if (gFacilityTrainers[gSaveBlock2Ptr->frontier.trainerIds[i]].facilityClass == gFacilityTrainers[trainerId].facilityClass)
2018-10-21 20:13:12 +02:00
break;
}
} while (i != j);
2019-02-07 18:37:28 +01:00
gSaveBlock2Ptr->frontier.trainerIds[j] = trainerId;
2018-10-22 19:22:57 +02:00
j++;
} while (j < 6);
2018-10-21 20:13:12 +02:00
r10 = 8;
for (i = 0; i < 6; i++)
{
2019-02-07 18:37:28 +01:00
trainerId = gSaveBlock2Ptr->frontier.trainerIds[i];
objEventTemplates[i + 1].graphicsId = GetBattleFacilityTrainerGfxId(trainerId);
2018-10-21 20:13:12 +02:00
for (j = 0; j < 2; j++)
{
while (1)
{
2020-01-05 16:50:32 +01:00
monId = GetRandomFrontierMonFromSet(trainerId);
if (j % 2 != 0 && gFacilityTrainerMons[gSaveBlock2Ptr->frontier.trainerIds[r10 - 1]].itemTableId == gFacilityTrainerMons[monId].itemTableId)
2018-10-21 20:13:12 +02:00
continue;
for (k = 8; k < r10; k++)
{
2020-01-05 16:50:32 +01:00
if (gFacilityTrainerMons[gSaveBlock2Ptr->frontier.trainerIds[k]].species == gFacilityTrainerMons[monId].species)
2018-10-21 20:13:12 +02:00
break;
2020-01-05 16:50:32 +01:00
if (species1 == gFacilityTrainerMons[monId].species)
2018-10-21 20:13:12 +02:00
break;
2020-01-05 16:50:32 +01:00
if (species2 == gFacilityTrainerMons[monId].species)
2018-10-21 20:13:12 +02:00
break;
}
if (k == r10)
break;
}
2020-01-05 16:50:32 +01:00
gSaveBlock2Ptr->frontier.trainerIds[r10] = monId;
2018-10-21 20:13:12 +02:00
r10++;
}
}
r10 = 0;
ValidateApprenticesChecksums();
2019-11-20 23:36:52 +01:00
for (i = 0; i < APPRENTICE_COUNT; i++)
2018-10-21 20:13:12 +02:00
{
if (gSaveBlock2Ptr->apprentices[i].lvlMode != 0
&& sApprenticeChallengeThreshold[gSaveBlock2Ptr->apprentices[i].numQuestions] / 7 <= challengeNum
2018-10-21 20:13:12 +02:00
&& gSaveBlock2Ptr->apprentices[i].lvlMode - 1 == lvlMode)
{
k = 0;
2020-04-09 21:18:53 +02:00
for (j = 0; j < MULTI_PARTY_SIZE; j++)
2018-10-21 20:13:12 +02:00
{
2018-10-22 19:22:57 +02:00
if (species1 != gSaveBlock2Ptr->apprentices[i].party[j].species
&& species2 != gSaveBlock2Ptr->apprentices[i].party[j].species)
2018-10-21 20:13:12 +02:00
{
k++;
}
}
if (k > 2)
{
2018-10-26 23:54:41 +02:00
spArray[r10] = i + TRAINER_RECORD_MIXING_APPRENTICE;
2018-10-21 20:13:12 +02:00
r10++;
}
}
}
if (r10 != 0)
{
2019-02-07 18:37:28 +01:00
gSaveBlock2Ptr->frontier.trainerIds[6] = spArray[Random() % r10];
objEventTemplates[7].graphicsId = GetBattleFacilityTrainerGfxId(gSaveBlock2Ptr->frontier.trainerIds[6]);
2018-10-21 20:13:12 +02:00
FlagClear(FLAG_HIDE_BATTLE_TOWER_MULTI_BATTLE_PARTNER_ALT_1);
GetApprenticeMultiPartnerParty(gSaveBlock2Ptr->frontier.trainerIds[6]);
2018-10-21 20:13:12 +02:00
}
r10 = 0;
for (i = 0; i < BATTLE_TOWER_RECORD_COUNT; i++)
2018-10-21 20:13:12 +02:00
{
u32 *record = (u32*)(&gSaveBlock2Ptr->frontier.towerRecords[i]);
u32 recordHasData = 0;
u32 checksum = 0;
for (j = 0; j < (sizeof(struct EmeraldBattleTowerRecord) - 4) / 4; j++) // - 4, because of the last field being the checksum itself.
{
recordHasData |= record[j];
checksum += record[j];
}
if (gSaveBlock2Ptr->frontier.towerRecords[i].winStreak / 7 <= challengeNum
&& gSaveBlock2Ptr->frontier.towerRecords[i].lvlMode == lvlMode
&& recordHasData
&& gSaveBlock2Ptr->frontier.towerRecords[i].checksum == checksum)
{
k = 0;
for (j = 0; j < MAX_FRONTIER_PARTY_SIZE; j++)
2018-10-21 20:13:12 +02:00
{
if (species1 != gSaveBlock2Ptr->frontier.towerRecords[i].party[j].species
&& species2 != gSaveBlock2Ptr->frontier.towerRecords[i].party[j].species
&& gSaveBlock2Ptr->frontier.towerRecords[i].party[j].level <= GetFrontierEnemyMonLevel(lvlMode)
&& gSaveBlock2Ptr->frontier.towerRecords[i].party[j].species != 0)
{
k++;
}
}
if (k > 1)
{
2018-10-26 23:54:41 +02:00
spArray[r10] = i + TRAINER_RECORD_MIXING_FRIEND;
2018-10-21 20:13:12 +02:00
r10++;
}
}
}
if (r10 != 0)
{
2019-02-07 18:37:28 +01:00
gSaveBlock2Ptr->frontier.trainerIds[7] = spArray[Random() % r10];
objEventTemplates[8].graphicsId = GetBattleFacilityTrainerGfxId(gSaveBlock2Ptr->frontier.trainerIds[7]);
2018-10-21 20:13:12 +02:00
FlagClear(FLAG_HIDE_BATTLE_TOWER_MULTI_BATTLE_PARTNER_ALT_2);
GetRecordMixFriendMultiPartnerParty(gSaveBlock2Ptr->frontier.trainerIds[7]);
2018-10-22 19:22:57 +02:00
}
}
2020-12-24 22:18:47 +01:00
static void GetPotentialPartnerMoveAndSpecies(u16 trainerId, u16 monId)
2018-10-22 19:22:57 +02:00
{
u16 move = 0;
u16 species = 0;
SetFacilityPtrsGetLevel();
2018-10-22 19:22:57 +02:00
2018-10-26 23:54:41 +02:00
if (trainerId != TRAINER_EREADER)
2018-10-22 19:22:57 +02:00
{
2019-12-21 00:52:29 +01:00
if (trainerId < FRONTIER_TRAINERS_COUNT)
2018-10-22 19:22:57 +02:00
{
2020-01-05 16:50:32 +01:00
move = gFacilityTrainerMons[monId].moves[0];
species = gFacilityTrainerMons[monId].species;
2018-10-22 19:22:57 +02:00
}
2018-10-26 23:54:41 +02:00
else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE)
2018-10-22 19:22:57 +02:00
{
2020-10-07 23:03:46 +02:00
move = gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].party[gFrontierTempParty[gSpecialVar_0x8005 + 1]].moves[0];
species = gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].party[gFrontierTempParty[gSpecialVar_0x8005 + 1]].species;
2018-10-22 19:22:57 +02:00
}
else
{
s32 i;
2020-10-07 23:03:46 +02:00
move = gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].party[gFrontierTempParty[gSpecialVar_0x8005 - 1]].moves[0];
species = gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].party[gFrontierTempParty[gSpecialVar_0x8005 - 1]].species;
2018-10-22 19:22:57 +02:00
for (i = 0; i < PLAYER_NAME_LENGTH; i++)
2018-10-26 23:54:41 +02:00
gStringVar3[i] = gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].playerName[i];
2018-10-22 19:22:57 +02:00
gStringVar3[i] = EOS;
2018-10-26 23:54:41 +02:00
ConvertInternationalString(gStringVar3, gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].language);
2018-10-22 19:22:57 +02:00
}
}
StringCopy(gStringVar1, gMoveNames[move]);
StringCopy(gStringVar2, gSpeciesNames[species]);
}
2020-12-24 22:18:47 +01:00
// For multi battles in the Battle Tower, the player may choose a partner by talking to them
// These partners can be an NPC or a former/record-mixed Apprentice
// When talked to, their response consists of:
// PARTNER_MSGID_INTRO - A greeting
// PARTNER_MSGID_MON1 - Naming one pokemon on their team, and a move it has
// PARTNER_MSGID_MON2_ASK - Naming a second pokemon on their team, a move it has, and asking if they'd like to be their partner
// PARTNER_MSGID_ACCEPT - If the player agrees to be their partner
// PARTNER_MSGID_REJECT - If the player declines to be their partner
static void ShowPartnerCandidateMessage(void)
2018-10-22 19:22:57 +02:00
{
s32 i, j, partnerId;
2020-01-05 16:50:32 +01:00
s32 monId;
s32 level = SetFacilityPtrsGetLevel();
2018-10-27 21:01:35 +02:00
u16 winStreak = GetCurrentFacilityWinStreak();
s32 challengeNum = winStreak / 7;
2018-10-22 19:22:57 +02:00
s32 k = gSpecialVar_LastTalked - 2;
2019-02-07 18:37:28 +01:00
s32 trainerId = gSaveBlock2Ptr->frontier.trainerIds[k];
2018-10-22 19:22:57 +02:00
for (partnerId = 0; partnerId < ARRAY_COUNT(sPartnerTrainerTextTables); partnerId++)
2018-10-22 19:22:57 +02:00
{
if (sPartnerTrainerTextTables[partnerId].facilityClass == GetFrontierTrainerFacilityClass(trainerId))
2018-10-22 19:22:57 +02:00
break;
}
switch (gSpecialVar_0x8005)
{
case PARTNER_MSGID_INTRO:
2018-10-26 23:54:41 +02:00
if (trainerId == TRAINER_EREADER)
2018-10-22 19:22:57 +02:00
return;
2019-12-21 00:52:29 +01:00
if (trainerId < FRONTIER_TRAINERS_COUNT)
2018-10-22 19:22:57 +02:00
{
GetFrontierTrainerName(gStringVar1, trainerId);
}
2018-10-26 23:54:41 +02:00
else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE)
2018-10-22 19:22:57 +02:00
{
GetFrontierTrainerName(gStringVar1, trainerId);
}
else
{
s32 i;
for (i = 0; i < PLAYER_NAME_LENGTH; i++)
2018-10-26 23:54:41 +02:00
gStringVar1[i] = gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].playerName[i];
2018-10-22 19:22:57 +02:00
gStringVar1[i] = EOS;
2018-10-26 23:54:41 +02:00
ConvertInternationalString(gStringVar1, gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].language);
ConvertIntToDecimalStringN(gStringVar2, gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].number, STR_CONV_MODE_LEFT_ALIGN, 3);
2018-10-22 19:22:57 +02:00
GetFrontierTrainerName(gStringVar3, trainerId);
}
break;
case PARTNER_MSGID_MON1:
2020-01-05 16:50:32 +01:00
monId = gSaveBlock2Ptr->frontier.trainerIds[8 + k * 2];
2020-12-24 22:18:47 +01:00
GetPotentialPartnerMoveAndSpecies(trainerId, monId);
2018-10-22 19:22:57 +02:00
break;
case PARTNER_MSGID_MON2_ASK:
2020-01-05 16:50:32 +01:00
monId = gSaveBlock2Ptr->frontier.trainerIds[9 + k * 2];
2020-12-24 22:18:47 +01:00
GetPotentialPartnerMoveAndSpecies(trainerId, monId);
2018-10-22 19:22:57 +02:00
break;
case PARTNER_MSGID_ACCEPT:
2018-10-22 19:22:57 +02:00
gPartnerTrainerId = trainerId;
2019-12-21 00:52:29 +01:00
if (trainerId < FRONTIER_TRAINERS_COUNT)
2018-10-22 19:22:57 +02:00
{
2019-02-07 18:37:28 +01:00
gSaveBlock2Ptr->frontier.trainerIds[18] = gSaveBlock2Ptr->frontier.trainerIds[8 + k * 2];
gSaveBlock2Ptr->frontier.trainerIds[19] = gSaveBlock2Ptr->frontier.trainerIds[9 + k * 2];
2018-10-22 19:22:57 +02:00
}
2018-10-26 23:54:41 +02:00
else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE)
2018-10-22 19:22:57 +02:00
{
2020-10-07 23:03:46 +02:00
gSaveBlock2Ptr->frontier.trainerIds[18] = gFrontierTempParty[2];
gSaveBlock2Ptr->frontier.trainerIds[19] = gFrontierTempParty[3];
2018-10-22 19:22:57 +02:00
}
else
{
2020-10-07 23:03:46 +02:00
gSaveBlock2Ptr->frontier.trainerIds[18] = gFrontierTempParty[0];
gSaveBlock2Ptr->frontier.trainerIds[19] = gFrontierTempParty[1];
2018-10-22 19:22:57 +02:00
}
for (k = 0; k < 14; k++)
{
while (1)
{
2019-12-21 00:52:29 +01:00
i = GetRandomScaledFrontierTrainerId(challengeNum, k / 2);
2018-10-22 19:22:57 +02:00
if (gPartnerTrainerId == i)
continue;
for (j = 0; j < k; j++)
{
2019-02-07 18:37:28 +01:00
if (gSaveBlock2Ptr->frontier.trainerIds[j] == i)
2018-10-22 19:22:57 +02:00
break;
}
if (j == k)
break;
}
2019-02-07 18:37:28 +01:00
gSaveBlock2Ptr->frontier.trainerIds[k] = i;
2018-10-22 19:22:57 +02:00
}
2019-02-07 18:37:28 +01:00
gSaveBlock2Ptr->frontier.trainerIds[17] = trainerId;
2018-10-22 19:22:57 +02:00
break;
case PARTNER_MSGID_REJECT:
2018-10-22 19:22:57 +02:00
break;
}
2018-10-26 23:54:41 +02:00
if (trainerId == TRAINER_EREADER)
2018-10-22 19:22:57 +02:00
return;
// First check is redundant, only needs to make sure it's not an Apprentice
2019-12-21 00:52:29 +01:00
if (trainerId < FRONTIER_TRAINERS_COUNT)
2018-10-22 19:22:57 +02:00
{
ShowFieldMessage(sPartnerTrainerTextTables[partnerId].strings[gSpecialVar_0x8005]);
2018-10-22 19:22:57 +02:00
}
2018-10-26 23:54:41 +02:00
else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE)
2018-10-22 19:22:57 +02:00
{
ShowFieldMessage(sPartnerTrainerTextTables[partnerId].strings[gSpecialVar_0x8005]);
2018-10-22 19:22:57 +02:00
}
// Trainer is a former/record-mixed Apprentice, do Apprentice message
2018-10-22 19:22:57 +02:00
else
{
u8 apprenticeId = gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].id;
ShowFieldMessage(sPartnerApprenticeTextTables[apprenticeId][gSpecialVar_0x8005]);
2018-10-22 19:22:57 +02:00
}
}
static void LoadLinkMultiOpponentsData(void)
2018-10-22 19:22:57 +02:00
{
s32 challengeNum;
s32 i, j;
s32 trainerId = 0;
u32 lvlMode = gSaveBlock2Ptr->frontier.lvlMode;
u32 battleMode = VarGet(VAR_FRONTIER_BATTLE_MODE);
u32 battleNum = gSaveBlock2Ptr->frontier.curChallengeBattleNum;
GetMultiplayerId(); // Yet another pointless function call.
switch (gSpecialVar_Result)
{
case 0:
if (battleMode == FRONTIER_MODE_LINK_MULTIS)
{
2018-10-27 17:39:05 +02:00
challengeNum = gSaveBlock2Ptr->frontier.towerWinStreaks[battleMode][lvlMode] / 7;
2018-12-31 09:22:21 +01:00
if (IsLinkTaskFinished())
2018-10-22 19:22:57 +02:00
{
SendBlock(BitmaskAllOtherLinkPlayers(), &challengeNum, sizeof(challengeNum));
2018-10-22 19:22:57 +02:00
gSpecialVar_Result = 1;
}
}
else
{
gSpecialVar_Result = 6;
}
break;
case 1:
if ((GetBlockReceivedStatus() & 3) == 3)
{
ResetBlockReceivedFlags();
if (gBlockRecvBuffer[0][0] > gBlockRecvBuffer[1][0])
challengeNum = gBlockRecvBuffer[0][0];
else
challengeNum = gBlockRecvBuffer[1][0];
for (i = 0; i < 14; i++)
{
do
{
2019-12-21 00:52:29 +01:00
trainerId = GetRandomScaledFrontierTrainerId(challengeNum, i / 2);
2018-10-22 19:22:57 +02:00
for (j = 0; j < i; j++)
{
2019-02-07 18:37:28 +01:00
if (gSaveBlock2Ptr->frontier.trainerIds[j] == trainerId)
2018-10-22 19:22:57 +02:00
break;
}
} while (i != j);
if (i == j) // This condition is always true, because of the loop above.
2019-02-07 18:37:28 +01:00
gSaveBlock2Ptr->frontier.trainerIds[i] = trainerId;
2018-10-22 19:22:57 +02:00
}
gSpecialVar_Result = 2;
}
break;
case 2:
2018-12-31 09:22:21 +01:00
if (IsLinkTaskFinished())
2018-10-22 19:22:57 +02:00
{
SendBlock(BitmaskAllOtherLinkPlayers(), &gSaveBlock2Ptr->frontier.trainerIds, sizeof(gSaveBlock2Ptr->frontier.trainerIds));
2018-10-22 19:22:57 +02:00
gSpecialVar_Result = 3;
}
break;
case 3:
if ((GetBlockReceivedStatus() & 3) == 3)
{
ResetBlockReceivedFlags();
2019-02-07 18:37:28 +01:00
memcpy(&gSaveBlock2Ptr->frontier.trainerIds, gBlockRecvBuffer, sizeof(gSaveBlock2Ptr->frontier.trainerIds));
gTrainerBattleOpponent_A = gSaveBlock2Ptr->frontier.trainerIds[battleNum * 2];
gTrainerBattleOpponent_B = gSaveBlock2Ptr->frontier.trainerIds[battleNum * 2 + 1];
2018-10-22 19:22:57 +02:00
SetBattleFacilityTrainerGfxId(gTrainerBattleOpponent_A, 0);
SetBattleFacilityTrainerGfxId(gTrainerBattleOpponent_B, 1);
if (gReceivedRemoteLinkPlayers != 0 && gWirelessCommType == 0)
gSpecialVar_Result = 4;
else
gSpecialVar_Result = 6;
}
break;
case 4:
2020-08-13 09:09:47 +02:00
SetCloseLinkCallback();
2018-10-22 19:22:57 +02:00
gSpecialVar_Result = 5;
break;
case 5:
if (gReceivedRemoteLinkPlayers == 0)
{
gSpecialVar_Result = 6;
}
break;
case 6:
return;
}
}
2020-12-24 22:18:47 +01:00
static void TowerTryCloseLink(void)
2018-10-22 19:22:57 +02:00
{
if (gWirelessCommType != 0)
2020-08-13 09:09:47 +02:00
SetCloseLinkCallback();
2018-10-22 19:22:57 +02:00
}
static void SetMultiPartnerGfx(void)
2018-10-22 19:22:57 +02:00
{
// 0xF below means use VAR_OBJ_GFX_ID_E
SetBattleFacilityTrainerGfxId(gSaveBlock2Ptr->frontier.trainerIds[17], 0xF);
2018-10-22 19:22:57 +02:00
}
2019-11-29 04:46:39 +01:00
static void SetTowerInterviewData(void)
2018-10-22 19:22:57 +02:00
{
s32 i;
u8 text[32];
if (VarGet(VAR_FRONTIER_BATTLE_MODE) != FRONTIER_MODE_SINGLES)
return;
GetFrontierTrainerName(text, gTrainerBattleOpponent_A);
StripExtCtrlCodes(text);
2019-11-24 22:58:40 +01:00
StringCopy(gSaveBlock2Ptr->frontier.towerInterview.opponentName, text);
GetBattleTowerTrainerLanguage(&gSaveBlock2Ptr->frontier.towerInterview.opponentLanguage, gTrainerBattleOpponent_A);
gSaveBlock2Ptr->frontier.towerInterview.opponentSpecies = GetMonData(&gEnemyParty[gBattlerPartyIndexes[1]], MON_DATA_SPECIES, NULL);
gSaveBlock2Ptr->frontier.towerInterview.playerSpecies = GetMonData(&gPlayerParty[gBattlerPartyIndexes[0]], MON_DATA_SPECIES, NULL);
2018-10-22 19:22:57 +02:00
for (i = 0; i < POKEMON_NAME_LENGTH + 1; i++)
2019-11-24 22:58:40 +01:00
gSaveBlock2Ptr->frontier.towerInterview.opponentMonNickname[i] = gBattleMons[0].nickname[i];
gSaveBlock2Ptr->frontier.towerBattleOutcome = gBattleOutcome;
2018-10-22 19:22:57 +02:00
}
2018-10-25 21:27:10 +02:00
static void ValidateBattleTowerRecordChecksums(void)
2018-10-22 19:22:57 +02:00
{
s32 i, j;
u32 *record = (u32*)(&gSaveBlock2Ptr->frontier.towerPlayer);
u32 checksum = 0;
for (j = 0; j < (sizeof(struct EmeraldBattleTowerRecord) - 4) / 4; j++) // - 4, because of the last field being the checksum itself.
{
checksum += record[j];
}
if (gSaveBlock2Ptr->frontier.towerPlayer.checksum != checksum)
ClearBattleTowerRecord(&gSaveBlock2Ptr->frontier.towerPlayer);
for (i = 0; i < BATTLE_TOWER_RECORD_COUNT; i++)
2018-10-22 19:22:57 +02:00
{
record = (u32*)(&gSaveBlock2Ptr->frontier.towerRecords[i]);
checksum = 0;
for (j = 0; j < (sizeof(struct EmeraldBattleTowerRecord) - 4) / 4; j++) // - 4, because of the last field being the checksum itself.
{
checksum += record[j];
}
if (gSaveBlock2Ptr->frontier.towerRecords[i].checksum != checksum)
ClearBattleTowerRecord(&gSaveBlock2Ptr->frontier.towerRecords[i]);
}
}
void CalcEmeraldBattleTowerChecksum(struct EmeraldBattleTowerRecord *record)
{
u32 i;
record->checksum = 0;
for (i = 0; i < (sizeof(struct EmeraldBattleTowerRecord) - 4) / 4; i++) // - 4, because of the last field being the checksum itself.
record->checksum += ((u32 *)record)[i];
}
void CalcRubyBattleTowerChecksum(struct RSBattleTowerRecord *record)
{
u32 i;
record->checksum = 0;
for (i = 0; i < (sizeof(struct RSBattleTowerRecord) - 4) / 4; i++) // - 4, because of the last field being the checksum itself.
record->checksum += ((u32 *)record)[i];
}
2018-10-25 21:27:10 +02:00
static void ClearBattleTowerRecord(struct EmeraldBattleTowerRecord *record)
2018-10-22 19:22:57 +02:00
{
u32 i;
for (i = 0; i < sizeof(struct EmeraldBattleTowerRecord) / 4; i++)
((u32 *)record)[i] = 0;
}
u16 GetCurrentBattleTowerWinStreak(u8 lvlMode, u8 battleMode)
{
2018-10-27 17:39:05 +02:00
u16 winStreak = gSaveBlock2Ptr->frontier.towerWinStreaks[battleMode][lvlMode];
2018-10-22 19:22:57 +02:00
if (winStreak > 9999)
return 9999;
else
return winStreak;
}
2018-10-25 21:27:10 +02:00
static u8 GetMonCountForBattleMode(u8 battleMode)
2018-10-22 19:22:57 +02:00
{
2019-11-24 22:58:40 +01:00
u8 partySizes[ARRAY_COUNT(sBattleTowerPartySizes)];
memcpy(partySizes, sBattleTowerPartySizes, sizeof(sBattleTowerPartySizes));
2018-10-22 19:22:57 +02:00
2019-11-24 22:58:40 +01:00
if (battleMode < ARRAY_COUNT(sBattleTowerPartySizes))
return partySizes[battleMode];
2018-10-22 19:22:57 +02:00
else
2019-11-24 22:58:40 +01:00
return FRONTIER_PARTY_SIZE;
2018-10-22 19:22:57 +02:00
}
struct RibbonCounter
{
u8 partyIndex;
u8 count;
};
2018-10-25 21:27:10 +02:00
static void AwardBattleTowerRibbons(void)
2018-10-22 19:22:57 +02:00
{
s32 i;
u32 partyIndex;
#ifdef BUGFIX
struct RibbonCounter ribbons[MAX_FRONTIER_PARTY_SIZE];
#else
2018-10-22 19:22:57 +02:00
struct RibbonCounter ribbons[3]; // BUG: 4 Pokemon can receive ribbons in a double battle mode.
#endif
2018-10-22 19:22:57 +02:00
u8 ribbonType = 0;
u8 lvlMode = gSaveBlock2Ptr->frontier.lvlMode;
u8 battleMode = VarGet(VAR_FRONTIER_BATTLE_MODE);
u8 monCount = GetMonCountForBattleMode(battleMode);
if (lvlMode != FRONTIER_LVL_50)
ribbonType = MON_DATA_VICTORY_RIBBON;
else
ribbonType = MON_DATA_WINNING_RIBBON;
gSpecialVar_Result = FALSE;
if (GetCurrentBattleTowerWinStreak(lvlMode, battleMode) > 55)
{
for (i = 0; i < monCount; i++)
{
partyIndex = gSaveBlock2Ptr->frontier.selectedPartyMons[i] - 1;
ribbons[i].partyIndex = partyIndex;
ribbons[i].count = 0;
if (!GetMonData(&gSaveBlock1Ptr->playerParty[partyIndex], ribbonType))
{
gSpecialVar_Result = TRUE;
SetMonData(&gSaveBlock1Ptr->playerParty[partyIndex], ribbonType, &gSpecialVar_Result);
ribbons[i].count = GetRibbonCount(&gSaveBlock1Ptr->playerParty[partyIndex]);
}
}
}
if (gSpecialVar_Result)
{
IncrementGameStat(GAME_STAT_RECEIVED_RIBBONS);
for (i = 1; i < monCount; i++)
{
if (ribbons[i].count > ribbons[0].count)
{
struct RibbonCounter prevBest = ribbons[0];
ribbons[0] = ribbons[i];
ribbons[i] = prevBest;
}
}
2019-11-11 03:54:00 +01:00
if (ribbons[0].count > NUM_CUTIES_RIBBONS)
2018-10-22 19:22:57 +02:00
{
2019-11-11 03:54:00 +01:00
TryPutSpotTheCutiesOnAir(&gSaveBlock1Ptr->playerParty[ribbons[0].partyIndex], ribbonType);
2018-10-22 19:22:57 +02:00
}
}
}
// This is a leftover debugging function that is used to populate the E-Reader
// trainer with the player's current data.
2018-10-25 21:27:10 +02:00
static void FillEReaderTrainerWithPlayerData(void)
2018-10-22 19:22:57 +02:00
{
struct BattleTowerEReaderTrainer *ereaderTrainer = &gSaveBlock2Ptr->frontier.ereaderTrainer;
s32 i, j;
if (gSaveBlock2Ptr->playerGender != MALE)
{
ereaderTrainer->facilityClass = gTowerFemaleFacilityClasses[(gSaveBlock2Ptr->playerTrainerId[0] + gSaveBlock2Ptr->playerTrainerId[1]
+ gSaveBlock2Ptr->playerTrainerId[2] + gSaveBlock2Ptr->playerTrainerId[3]) % ARRAY_COUNT(gTowerFemaleFacilityClasses)];
}
else
{
ereaderTrainer->facilityClass = gTowerMaleFacilityClasses[(gSaveBlock2Ptr->playerTrainerId[0] + gSaveBlock2Ptr->playerTrainerId[1]
+ gSaveBlock2Ptr->playerTrainerId[2] + gSaveBlock2Ptr->playerTrainerId[3]) % ARRAY_COUNT(gTowerMaleFacilityClasses)];
}
2018-11-01 21:31:10 +01:00
CopyTrainerId(ereaderTrainer->trainerId, gSaveBlock2Ptr->playerTrainerId);
2018-10-22 19:22:57 +02:00
StringCopy7(ereaderTrainer->name, gSaveBlock2Ptr->playerName);
ereaderTrainer->winStreak = 1;
j = 7;
for (i = 0; i < 6; i++)
{
2019-02-27 19:45:31 +01:00
ereaderTrainer->greeting[i] = gSaveBlock1Ptr->easyChatBattleStart[i];
2018-10-22 19:22:57 +02:00
ereaderTrainer->farewellPlayerLost[i] = j;
ereaderTrainer->farewellPlayerWon[i] = j + 6;
j++;
}
for (i = 0; i < 3; i++)
2020-12-24 22:18:47 +01:00
ConvertPokemonToBattleTowerPokemon(&gPlayerParty[i], &ereaderTrainer->party[i]);
2018-10-22 19:22:57 +02:00
SetEReaderTrainerChecksum(ereaderTrainer);
}
u8 GetEreaderTrainerFrontSpriteId(void)
{
return gFacilityClassToPicIndex[gSaveBlock2Ptr->frontier.ereaderTrainer.facilityClass];
}
u8 GetEreaderTrainerClassId(void)
{
return gFacilityClassToTrainerClass[gSaveBlock2Ptr->frontier.ereaderTrainer.facilityClass];
}
2018-10-25 21:27:10 +02:00
void GetEreaderTrainerName(u8 *dst)
2018-10-22 19:22:57 +02:00
{
s32 i;
for (i = 0; i < 5; i++)
2018-10-25 21:27:10 +02:00
dst[i] = gSaveBlock2Ptr->frontier.ereaderTrainer.name[i];
2018-10-22 19:22:57 +02:00
2018-10-25 21:27:10 +02:00
dst[i] = EOS;
2018-10-22 19:22:57 +02:00
}
// Checks if the saved E-Reader trainer is valid.
void ValidateEReaderTrainer(void)
{
u32 i;
u32 checksum;
struct BattleTowerEReaderTrainer *ereaderTrainer;
gSpecialVar_Result = FALSE;
ereaderTrainer = &gSaveBlock2Ptr->frontier.ereaderTrainer;
checksum = 0;
for (i = 0; i < (sizeof(struct BattleTowerEReaderTrainer) - 4) / 4; i++) // - 4, because of the last field being the checksum itself.
checksum |= ((u32 *)ereaderTrainer)[i];
if (checksum == 0)
{
gSpecialVar_Result = TRUE;
return;
}
checksum = 0;
for (i = 0; i < (sizeof(struct BattleTowerEReaderTrainer) - 4) / 4; i++) // - 4, because of the last field being the checksum itself.
checksum += ((u32 *)ereaderTrainer)[i];
if (gSaveBlock2Ptr->frontier.ereaderTrainer.checksum != checksum)
{
ClearEReaderTrainer(&gSaveBlock2Ptr->frontier.ereaderTrainer);
gSpecialVar_Result = TRUE;
}
}
2018-10-25 21:27:10 +02:00
static void SetEReaderTrainerChecksum(struct BattleTowerEReaderTrainer *ereaderTrainer)
2018-10-22 19:22:57 +02:00
{
s32 i;
ereaderTrainer->checksum = 0;
for (i = 0; i < (sizeof(struct BattleTowerEReaderTrainer) - 4) / 4; i++) // - 4, because of the last field being the checksum itself.
ereaderTrainer->checksum += ((u32 *)ereaderTrainer)[i];
}
void ClearEReaderTrainer(struct BattleTowerEReaderTrainer *ereaderTrainer)
{
u32 i;
for (i = 0; i < (sizeof(struct BattleTowerEReaderTrainer)) / 4; i++)
((u32 *)ereaderTrainer)[i] = 0;
}
void CopyEReaderTrainerGreeting(void)
{
FrontierSpeechToString(gSaveBlock2Ptr->frontier.ereaderTrainer.greeting);
}
2018-10-25 21:27:10 +02:00
static void CopyEReaderTrainerFarewellMessage(void)
2018-10-22 19:22:57 +02:00
{
if (gBattleOutcome == B_OUTCOME_DREW)
gStringVar4[0] = EOS;
else if (gBattleOutcome == B_OUTCOME_WON)
FrontierSpeechToString(gSaveBlock2Ptr->frontier.ereaderTrainer.farewellPlayerWon);
else
FrontierSpeechToString(gSaveBlock2Ptr->frontier.ereaderTrainer.farewellPlayerLost);
}
void TryHideBattleTowerReporter(void)
2018-10-22 19:22:57 +02:00
{
2019-11-24 22:58:40 +01:00
if (gSaveBlock2Ptr->frontier.challengeStatus == CHALLENGE_STATUS_SAVING)
HideBattleTowerReporter();
2019-01-02 22:12:43 +01:00
if (FlagGet(FLAG_CANCEL_BATTLE_ROOM_CHALLENGE) == TRUE)
2018-10-22 19:22:57 +02:00
{
HideBattleTowerReporter();
2019-01-02 22:12:43 +01:00
FlagClear(FLAG_CANCEL_BATTLE_ROOM_CHALLENGE);
2018-10-22 19:22:57 +02:00
}
}
#define STEVEN_OTID 61226
2018-10-25 21:27:10 +02:00
static void FillPartnerParty(u16 trainerId)
2018-10-22 19:22:57 +02:00
{
s32 i, j;
u32 ivs, level;
u32 friendship;
2020-01-05 16:50:32 +01:00
u16 monId;
2018-10-22 19:22:57 +02:00
u32 otID;
u8 trainerName[PLAYER_NAME_LENGTH + 1];
SetFacilityPtrsGetLevel();
2018-10-22 19:22:57 +02:00
if (trainerId == TRAINER_STEVEN_PARTNER)
{
2020-04-09 21:18:53 +02:00
for (i = 0; i < MULTI_PARTY_SIZE; i++)
2018-10-22 19:22:57 +02:00
{
do
{
j = Random32();
} while (IsShinyOtIdPersonality(STEVEN_OTID, j) || sStevenMons[i].nature != GetNatureFromPersonality(j));
2020-04-09 21:18:53 +02:00
CreateMon(&gPlayerParty[MULTI_PARTY_SIZE + i],
2018-10-22 19:22:57 +02:00
sStevenMons[i].species,
sStevenMons[i].level,
sStevenMons[i].fixedIV,
TRUE,
2020-12-13 05:28:01 +01:00
#ifdef BUGFIX
j,
#else
i, // BUG: personality was stored in the 'j' variable. As a result, Steven's pokemon do not have the intended natures.
#endif
2019-08-31 05:06:43 +02:00
OT_ID_PRESET, STEVEN_OTID);
2019-10-18 01:22:03 +02:00
for (j = 0; j < PARTY_SIZE; j++)
2020-04-09 21:18:53 +02:00
SetMonData(&gPlayerParty[MULTI_PARTY_SIZE + i], MON_DATA_HP_EV + j, &sStevenMons[i].evs[j]);
for (j = 0; j < MAX_MON_MOVES; j++)
2020-04-09 21:18:53 +02:00
SetMonMoveSlot(&gPlayerParty[MULTI_PARTY_SIZE + i], sStevenMons[i].moves[j], j);
SetMonData(&gPlayerParty[MULTI_PARTY_SIZE + i], MON_DATA_OT_NAME, gTrainers[TRAINER_STEVEN].trainerName);
2018-10-22 19:22:57 +02:00
j = MALE;
2020-04-09 21:18:53 +02:00
SetMonData(&gPlayerParty[MULTI_PARTY_SIZE + i], MON_DATA_OT_GENDER, &j);
CalculateMonStats(&gPlayerParty[MULTI_PARTY_SIZE + i]);
2018-10-22 19:22:57 +02:00
}
}
2018-10-26 23:54:41 +02:00
else if (trainerId == TRAINER_EREADER)
2018-10-22 19:22:57 +02:00
{
// Scrapped, lol.
trainerName[0] = gGameLanguage;
}
2019-12-21 00:52:29 +01:00
else if (trainerId < FRONTIER_TRAINERS_COUNT)
2018-10-22 19:22:57 +02:00
{
level = SetFacilityPtrsGetLevel();
2018-10-22 19:22:57 +02:00
ivs = GetFrontierTrainerFixedIvs(trainerId);
otID = Random32();
2020-04-09 21:18:53 +02:00
for (i = 0; i < FRONTIER_MULTI_PARTY_SIZE; i++)
2018-10-22 19:22:57 +02:00
{
2020-01-05 16:50:32 +01:00
monId = gSaveBlock2Ptr->frontier.trainerIds[i + 18];
2020-04-09 21:18:53 +02:00
CreateMonWithEVSpreadNatureOTID(&gPlayerParty[MULTI_PARTY_SIZE + i],
2020-01-05 16:50:32 +01:00
gFacilityTrainerMons[monId].species,
2018-10-22 19:22:57 +02:00
level,
2020-01-05 16:50:32 +01:00
gFacilityTrainerMons[monId].nature,
2018-10-22 19:22:57 +02:00
ivs,
2020-01-05 16:50:32 +01:00
gFacilityTrainerMons[monId].evSpread,
2018-10-22 19:22:57 +02:00
otID);
friendship = MAX_FRIENDSHIP;
for (j = 0; j < MAX_MON_MOVES; j++)
2018-10-22 19:22:57 +02:00
{
2020-04-09 21:18:53 +02:00
SetMonMoveSlot(&gPlayerParty[MULTI_PARTY_SIZE + i], gFacilityTrainerMons[monId].moves[j], j);
2020-01-05 16:50:32 +01:00
if (gFacilityTrainerMons[monId].moves[j] == MOVE_FRUSTRATION)
2018-10-22 19:22:57 +02:00
friendship = 0;
}
2020-04-09 21:18:53 +02:00
SetMonData(&gPlayerParty[MULTI_PARTY_SIZE + i], MON_DATA_FRIENDSHIP, &friendship);
SetMonData(&gPlayerParty[MULTI_PARTY_SIZE + i], MON_DATA_HELD_ITEM, &gBattleFrontierHeldItems[gFacilityTrainerMons[monId].itemTableId]);
2018-10-22 19:22:57 +02:00
for (j = 0; j < PLAYER_NAME_LENGTH + 1; j++)
trainerName[j] = gFacilityTrainers[trainerId].trainerName[j];
2020-04-09 21:18:53 +02:00
SetMonData(&gPlayerParty[MULTI_PARTY_SIZE + i], MON_DATA_OT_NAME, &trainerName);
2018-10-22 19:22:57 +02:00
j = IsFrontierTrainerFemale(trainerId);
2020-04-09 21:18:53 +02:00
SetMonData(&gPlayerParty[MULTI_PARTY_SIZE + i], MON_DATA_OT_GENDER, &j);
2018-10-22 19:22:57 +02:00
}
}
2018-10-26 23:54:41 +02:00
else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE)
2018-10-22 19:22:57 +02:00
{
2018-10-26 23:54:41 +02:00
trainerId -= TRAINER_RECORD_MIXING_FRIEND;
2020-04-09 21:18:53 +02:00
for (i = 0; i < FRONTIER_MULTI_PARTY_SIZE; i++)
2018-10-22 19:22:57 +02:00
{
struct EmeraldBattleTowerRecord *record = &gSaveBlock2Ptr->frontier.towerRecords[trainerId];
2019-02-07 18:37:28 +01:00
struct BattleTowerPokemon monData = record->party[gSaveBlock2Ptr->frontier.trainerIds[18 + i]];
2018-10-22 19:22:57 +02:00
StringCopy(trainerName, record->name);
if (record->language == LANGUAGE_JAPANESE)
{
if (monData.nickname[0] != EXT_CTRL_CODE_BEGIN || monData.nickname[1] != EXT_CTRL_CODE_JPN)
{
monData.nickname[5] = EOS;
ConvertInternationalString(monData.nickname, LANGUAGE_JAPANESE);
}
}
else
{
if (monData.nickname[0] == EXT_CTRL_CODE_BEGIN && monData.nickname[1] == EXT_CTRL_CODE_JPN)
trainerName[5] = EOS;
}
CreateBattleTowerMon_HandleLevel(&gPlayerParty[MULTI_PARTY_SIZE + i], &monData, TRUE);
2020-04-09 21:18:53 +02:00
SetMonData(&gPlayerParty[MULTI_PARTY_SIZE + i], MON_DATA_OT_NAME, trainerName);
2018-10-26 23:54:41 +02:00
j = IsFrontierTrainerFemale(trainerId + TRAINER_RECORD_MIXING_FRIEND);
2020-04-09 21:18:53 +02:00
SetMonData(&gPlayerParty[MULTI_PARTY_SIZE + i], MON_DATA_OT_GENDER, &j);
2018-10-22 19:22:57 +02:00
}
}
else
{
2018-10-26 23:54:41 +02:00
trainerId -= TRAINER_RECORD_MIXING_APPRENTICE;
2020-04-09 21:18:53 +02:00
for (i = 0; i < FRONTIER_MULTI_PARTY_SIZE; i++)
2018-10-22 19:22:57 +02:00
{
2020-04-09 21:18:53 +02:00
CreateApprenticeMon(&gPlayerParty[MULTI_PARTY_SIZE + i], &gSaveBlock2Ptr->apprentices[trainerId], gSaveBlock2Ptr->frontier.trainerIds[18 + i]);
2018-10-26 23:54:41 +02:00
j = IsFrontierTrainerFemale(trainerId + TRAINER_RECORD_MIXING_APPRENTICE);
2020-04-09 21:18:53 +02:00
SetMonData(&gPlayerParty[MULTI_PARTY_SIZE + i], MON_DATA_OT_GENDER, &j);
2018-10-22 19:22:57 +02:00
}
}
}
bool32 RubyBattleTowerRecordToEmerald(struct RSBattleTowerRecord *src, struct EmeraldBattleTowerRecord *dst)
{
s32 i, validMons = 0;
2019-11-24 22:58:40 +01:00
for (i = 0; i < FRONTIER_PARTY_SIZE; i++)
2018-10-22 19:22:57 +02:00
{
if (src->party[i].species)
validMons++;
}
2019-11-24 22:58:40 +01:00
if (validMons != FRONTIER_PARTY_SIZE)
2018-10-22 19:22:57 +02:00
{
memset(dst, 0, sizeof(*dst));
return FALSE;
}
else
{
dst->lvlMode = src->lvlMode;
dst->winStreak = src->winStreak;
2020-12-13 05:28:01 +01:00
// UB: Reading outside the array. sRubyFacilityClassToEmerald has less than FACILITY_CLASSES_COUNT entries.
#ifdef UBFIX
for (i = 0; i < ARRAY_COUNT(sRubyFacilityClassToEmerald); i++)
#else
2018-12-05 22:21:26 +01:00
for (i = 0; i < FACILITY_CLASSES_COUNT; i++)
2020-12-13 05:28:01 +01:00
#endif
2018-10-22 19:22:57 +02:00
{
if (sRubyFacilityClassToEmerald[i][0] == src->facilityClass)
break;
}
2018-12-05 22:21:26 +01:00
if (i != FACILITY_CLASSES_COUNT)
2018-10-22 19:22:57 +02:00
dst->facilityClass = sRubyFacilityClassToEmerald[i][1];
else
dst->facilityClass = FACILITY_CLASS_YOUNGSTER;
for (i = 0; i < PLAYER_NAME_LENGTH + 1; i++)
dst->name[i] = src->name[i];
2019-09-08 17:53:48 +02:00
for (i = 0; i < TRAINER_ID_LENGTH; i++)
2018-10-22 19:22:57 +02:00
dst->trainerId[i] = src->trainerId[i];
2019-11-24 22:58:40 +01:00
for (i = 0; i < EASY_CHAT_BATTLE_WORDS_COUNT; i++)
2018-10-22 19:22:57 +02:00
dst->greeting[i] = src->greeting[i];
2019-11-24 22:58:40 +01:00
for (i = 0; i < EASY_CHAT_BATTLE_WORDS_COUNT; i++)
dst->speechWon[i] = sRecordTrainerSpeechWon[i];
for (i = 0; i < EASY_CHAT_BATTLE_WORDS_COUNT; i++)
dst->speechLost[i] = sRecordTrainerSpeechLost[i];
for (i = 0; i < FRONTIER_PARTY_SIZE; i++)
2018-10-22 19:22:57 +02:00
dst->party[i] = src->party[i];
2019-11-24 22:58:40 +01:00
CpuFill32(0, &dst->party[FRONTIER_PARTY_SIZE], sizeof(dst->party[FRONTIER_PARTY_SIZE]));
2018-10-22 19:22:57 +02:00
CalcEmeraldBattleTowerChecksum(dst);
return TRUE;
2018-10-21 20:13:12 +02:00
}
}
bool32 EmeraldBattleTowerRecordToRuby(struct EmeraldBattleTowerRecord *src, struct RSBattleTowerRecord *dst)
{
s32 i, validMons = 0;
2019-11-24 22:58:40 +01:00
for (i = 0; i < FRONTIER_PARTY_SIZE; i++)
{
if (src->party[i].species)
validMons++;
}
2019-11-24 22:58:40 +01:00
if (validMons != FRONTIER_PARTY_SIZE)
{
memset(dst, 0, sizeof(*dst));
return FALSE;
}
else
{
dst->lvlMode = src->lvlMode;
dst->winStreak = src->winStreak;
2020-12-13 05:28:01 +01:00
// UB: Reading outside the array. sRubyFacilityClassToEmerald has less than FACILITY_CLASSES_COUNT entries.
#ifdef UBFIX
for (i = 0; i < ARRAY_COUNT(sRubyFacilityClassToEmerald); i++)
#else
2018-12-05 22:21:26 +01:00
for (i = 0; i < FACILITY_CLASSES_COUNT; i++)
2020-12-13 05:28:01 +01:00
#endif
{
if (sRubyFacilityClassToEmerald[i][1] == src->facilityClass)
break;
}
2018-12-05 22:21:26 +01:00
if (i != FACILITY_CLASSES_COUNT)
dst->facilityClass = sRubyFacilityClassToEmerald[i][0];
else
2019-11-24 22:58:40 +01:00
dst->facilityClass = RS_FACILITY_CLASS_YOUNGSTER;
for (i = 0; i < PLAYER_NAME_LENGTH + 1; i++)
dst->name[i] = src->name[i];
2019-09-08 17:53:48 +02:00
for (i = 0; i < TRAINER_ID_LENGTH; i++)
dst->trainerId[i] = src->trainerId[i];
2019-11-24 22:58:40 +01:00
for (i = 0; i < EASY_CHAT_BATTLE_WORDS_COUNT; i++)
dst->greeting[i] = src->greeting[i];
2019-11-24 22:58:40 +01:00
for (i = 0; i < FRONTIER_PARTY_SIZE; i++)
dst->party[i] = src->party[i];
CalcRubyBattleTowerChecksum(dst);
return TRUE;
}
}
void CalcApprenticeChecksum(struct Apprentice *apprentice)
{
s32 i;
apprentice->checksum = 0;
for (i = 0; i < (sizeof(struct Apprentice) - 4) / 4; i++)
apprentice->checksum += ((u32 *)apprentice)[i];
}
2018-10-25 21:27:10 +02:00
static void ClearApprentice(struct Apprentice *apprentice)
{
s32 i;
for (i = 0; i < (sizeof(struct Apprentice)) / 4; i++)
((u32 *)apprentice)[i] = 0;
ResetApprenticeStruct(apprentice);
}
2018-10-25 21:27:10 +02:00
static void ValidateApprenticesChecksums(void)
{
s32 i, j;
2019-11-20 23:36:52 +01:00
for (i = 0; i < APPRENTICE_COUNT; i++)
{
u32 *data = (u32*) &gSaveBlock2Ptr->apprentices[i];
u32 checksum = 0;
for (j = 0; j < (sizeof(struct Apprentice) - 4) / 4; j++)
checksum += data[j];
if (gSaveBlock2Ptr->apprentices[i].checksum != checksum)
ClearApprentice(&gSaveBlock2Ptr->apprentices[i]);
}
}
void GetBattleTowerTrainerLanguage(u8 *dst, u16 trainerId)
{
2018-10-26 23:54:41 +02:00
if (trainerId == TRAINER_EREADER)
{
*dst = gGameLanguage;
}
2019-12-21 00:52:29 +01:00
else if (trainerId < FRONTIER_TRAINERS_COUNT)
{
*dst = gGameLanguage;
}
2018-10-26 23:54:41 +02:00
else if (trainerId < TRAINER_RECORD_MIXING_APPRENTICE)
{
if (gBattleTypeFlags & BATTLE_TYPE_RECORDED)
2018-11-01 15:06:50 +01:00
*dst = GetRecordedBattleRecordMixFriendLanguage();
else
2018-10-26 23:54:41 +02:00
*dst = gSaveBlock2Ptr->frontier.towerRecords[trainerId - TRAINER_RECORD_MIXING_FRIEND].language;
}
else
{
if (gBattleTypeFlags & BATTLE_TYPE_RECORDED)
2018-11-01 15:06:50 +01:00
*dst = GetRecordedBattleApprenticeLanguage();
else
2018-10-26 23:54:41 +02:00
*dst = gSaveBlock2Ptr->apprentices[trainerId - TRAINER_RECORD_MIXING_APPRENTICE].language;
}
}
u8 SetFacilityPtrsGetLevel(void)
{
if (gSaveBlock2Ptr->frontier.lvlMode == FRONTIER_LVL_TENT)
{
return SetTentPtrsGetLevel();
}
else
{
gFacilityTrainers = gBattleFrontierTrainers;
gFacilityTrainerMons = gBattleFrontierMons;
return GetFrontierEnemyMonLevel(gSaveBlock2Ptr->frontier.lvlMode);
}
}
u8 GetFrontierEnemyMonLevel(u8 lvlMode)
{
u8 level;
switch (lvlMode)
{
default:
case FRONTIER_LVL_50:
level = 50;
break;
case FRONTIER_LVL_OPEN:
level = GetHighestLevelInPlayerParty();
if (level < 60)
level = 60;
break;
}
return level;
}
s32 GetHighestLevelInPlayerParty(void)
{
s32 highestLevel = 0;
s32 i;
for (i = 0; i < PARTY_SIZE; i++)
{
if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES, NULL)
&& GetMonData(&gPlayerParty[i], MON_DATA_SPECIES2, NULL) != SPECIES_EGG)
{
s32 level = GetMonData(&gPlayerParty[i], MON_DATA_LEVEL, NULL);
if (level > highestLevel)
highestLevel = level;
}
}
return highestLevel;
}
2019-12-21 00:52:29 +01:00
// Frontier Trainer parties are roughly scaled in difficulty with higher trainer IDs, so scale IVs as well
// Duplicated in Battle Dome as GetDomeTrainerMonIvs
2018-10-25 21:27:10 +02:00
static u8 GetFrontierTrainerFixedIvs(u16 trainerId)
{
2019-12-21 00:52:29 +01:00
u8 fixedIv;
2019-12-21 00:52:29 +01:00
if (trainerId <= FRONTIER_TRAINER_JILL) // 0 - 99
fixedIv = 3;
else if (trainerId <= FRONTIER_TRAINER_CHLOE) // 100 - 119
fixedIv = 6;
else if (trainerId <= FRONTIER_TRAINER_SOFIA) // 120 - 139
fixedIv = 9;
else if (trainerId <= FRONTIER_TRAINER_JAZLYN) // 140 - 159
fixedIv = 12;
else if (trainerId <= FRONTIER_TRAINER_ALISON) // 160 - 179
fixedIv = 15;
else if (trainerId <= FRONTIER_TRAINER_LAMAR) // 180 - 199
fixedIv = 18;
else if (trainerId <= FRONTIER_TRAINER_TESS) // 200 - 219
fixedIv = 21;
else // 220+ (- 299)
fixedIv = MAX_PER_STAT_IVS;
2019-12-21 00:52:29 +01:00
return fixedIv;
}
2020-12-24 22:18:47 +01:00
static u16 GetBattleTentTrainerId(void)
{
u32 facility = VarGet(VAR_FRONTIER_FACILITY);
2020-12-24 22:18:47 +01:00
if (facility == FRONTIER_FACILITY_PALACE) // Verdanturf Tent; uses Palace mechanics
return Random() % NUM_BATTLE_TENT_TRAINERS;
else if (facility == FRONTIER_FACILITY_ARENA) // Fallarbor Tent; uses Arena mechanics
return Random() % NUM_BATTLE_TENT_TRAINERS;
else if (facility == FRONTIER_FACILITY_FACTORY) // Slateport Tent; uses Factory mechanics
return Random() % NUM_BATTLE_TENT_TRAINERS;
else if (facility == FRONTIER_FACILITY_TOWER)
return 0;
else
return 0;
}
2018-10-25 21:27:10 +02:00
static u8 SetTentPtrsGetLevel(void)
{
u8 level = 30;
u32 facility = VarGet(VAR_FRONTIER_FACILITY);
if (facility == FRONTIER_FACILITY_FACTORY)
{
gFacilityTrainers = gSlateportBattleTentTrainers;
gFacilityTrainerMons = gSlateportBattleTentMons;
}
else if (facility == FRONTIER_FACILITY_PALACE)
{
gFacilityTrainers = gVerdanturfBattleTentTrainers;
gFacilityTrainerMons = gVerdanturfBattleTentMons;
}
else if (facility == FRONTIER_FACILITY_ARENA)
{
gFacilityTrainers = gFallarborBattleTentTrainers;
gFacilityTrainerMons = gFallarborBattleTentMons;
}
else
{
gFacilityTrainers = gBattleFrontierTrainers;
gFacilityTrainerMons = gBattleFrontierMons;
}
level = GetHighestLevelInPlayerParty();
if (level < 30)
level = 30;
return level;
}
2019-11-29 04:46:39 +01:00
static void SetNextBattleTentOpponent(void)
{
s32 i;
u16 trainerId;
do
{
2020-12-24 22:18:47 +01:00
trainerId = GetBattleTentTrainerId();
for (i = 0; i < gSaveBlock2Ptr->frontier.curChallengeBattleNum; i++)
{
2019-02-07 18:37:28 +01:00
if (gSaveBlock2Ptr->frontier.trainerIds[i] == trainerId)
break;
}
} while (i != gSaveBlock2Ptr->frontier.curChallengeBattleNum);
gTrainerBattleOpponent_A = trainerId;
SetBattleFacilityTrainerGfxId(gTrainerBattleOpponent_A, 0);
if (gSaveBlock2Ptr->frontier.curChallengeBattleNum + 1 < 3)
2019-02-07 18:37:28 +01:00
gSaveBlock2Ptr->frontier.trainerIds[gSaveBlock2Ptr->frontier.curChallengeBattleNum] = gTrainerBattleOpponent_A;
}
2018-10-25 21:27:10 +02:00
static void FillTentTrainerParty_(u16 trainerId, u8 firstMonId, u8 monCount)
{
s32 i, j;
2020-04-09 21:18:53 +02:00
u16 chosenMonIndices[MAX_FRONTIER_PARTY_SIZE];
u8 friendship;
u8 level = SetTentPtrsGetLevel();
u8 fixedIV = 0;
u8 bfMonCount;
2020-01-05 16:50:32 +01:00
const u16 *monSet = NULL;
u32 otID = 0;
2020-01-05 16:50:32 +01:00
u16 monId;
2020-01-05 16:50:32 +01:00
monSet = gFacilityTrainers[gTrainerBattleOpponent_A].monSet;
bfMonCount = 0;
2020-01-05 16:50:32 +01:00
monId = monSet[bfMonCount];
while (monId != 0xFFFF)
{
bfMonCount++;
2020-01-05 16:50:32 +01:00
monId = monSet[bfMonCount];
if (monId == 0xFFFF)
break;
}
i = 0;
otID = Random32();
while (i != monCount)
{
2020-01-05 16:50:32 +01:00
u16 monId = monSet[Random() % bfMonCount];
// Ensure this pokemon species isn't a duplicate.
for (j = 0; j < i + firstMonId; j++)
{
2020-01-05 16:50:32 +01:00
if (GetMonData(&gEnemyParty[j], MON_DATA_SPECIES, NULL) == gFacilityTrainerMons[monId].species)
break;
}
if (j != i + firstMonId)
continue;
// Ensure this Pokemon's held item isn't a duplicate.
for (j = 0; j < i + firstMonId; j++)
{
if (GetMonData(&gEnemyParty[j], MON_DATA_HELD_ITEM, NULL) != 0
2020-01-05 16:50:32 +01:00
&& GetMonData(&gEnemyParty[j], MON_DATA_HELD_ITEM, NULL) == gBattleFrontierHeldItems[gFacilityTrainerMons[monId].itemTableId])
break;
}
if (j != i + firstMonId)
continue;
// Ensure this exact pokemon index isn't a duplicate. This check doesn't seem necessary
// because the species and held items were already checked directly above.
for (j = 0; j < i; j++)
{
2020-01-05 16:50:32 +01:00
if (chosenMonIndices[j] == monId)
break;
}
if (j != i)
continue;
2020-01-05 16:50:32 +01:00
chosenMonIndices[i] = monId;
// Place the chosen pokemon into the trainer's party.
2018-11-11 16:44:27 +01:00
CreateMonWithEVSpreadNatureOTID(&gEnemyParty[i + firstMonId],
2020-01-05 16:50:32 +01:00
gFacilityTrainerMons[monId].species,
level,
2020-01-05 16:50:32 +01:00
gFacilityTrainerMons[monId].nature,
fixedIV,
2020-01-05 16:50:32 +01:00
gFacilityTrainerMons[monId].evSpread,
otID);
friendship = MAX_FRIENDSHIP;
// Give the chosen pokemon its specified moves.
for (j = 0; j < MAX_MON_MOVES; j++)
{
2020-01-05 16:50:32 +01:00
SetMonMoveSlot(&gEnemyParty[i + firstMonId], gFacilityTrainerMons[monId].moves[j], j);
if (gFacilityTrainerMons[monId].moves[j] == MOVE_FRUSTRATION)
friendship = 0; // Frustration is more powerful the lower the pokemon's friendship is.
}
SetMonData(&gEnemyParty[i + firstMonId], MON_DATA_FRIENDSHIP, &friendship);
2020-01-05 16:50:32 +01:00
SetMonData(&gEnemyParty[i + firstMonId], MON_DATA_HELD_ITEM, &gBattleFrontierHeldItems[gFacilityTrainerMons[monId].itemTableId]);
// The pokemon was successfully added to the trainer's party, so it's safe to move on to
// the next party slot.
i++;
}
}
2018-10-25 21:27:10 +02:00
2019-01-13 12:12:27 +01:00
u8 FacilityClassToGraphicsId(u8 facilityClass)
2018-10-25 21:27:10 +02:00
{
u8 trainerObjectGfxId;
u8 i;
// Search male classes.
for (i = 0; i < ARRAY_COUNT(gTowerMaleFacilityClasses); i++)
{
if (gTowerMaleFacilityClasses[i] == facilityClass)
break;
}
if (i != ARRAY_COUNT(gTowerMaleFacilityClasses))
{
trainerObjectGfxId = gTowerMaleTrainerGfxIds[i];
return trainerObjectGfxId;
}
// Search female classes.
for (i = 0; i < ARRAY_COUNT(gTowerFemaleFacilityClasses); i++)
{
if (gTowerFemaleFacilityClasses[i] == facilityClass)
break;
}
if (i != ARRAY_COUNT(gTowerFemaleFacilityClasses))
{
trainerObjectGfxId = gTowerFemaleTrainerGfxIds[i];
return trainerObjectGfxId;
}
else
{
return OBJ_EVENT_GFX_BOY_1;
2018-10-25 21:27:10 +02:00
}
}
bool32 ValidateBattleTowerRecord(u8 recordId) // unused
{
s32 i;
u32 *record = (u32*)(&gSaveBlock2Ptr->frontier.towerRecords[recordId]);
u32 checksum = 0;
u32 hasData = 0;
for (i = 0; i < (sizeof(struct EmeraldBattleTowerRecord) - 4) / 4; i++) // - 4, because of the last fjeld bejng the checksum jtself.
{
checksum += record[i];
hasData |= record[i];
}
if (checksum == 0 && hasData == 0)
{
return FALSE;
}
else if (gSaveBlock2Ptr->frontier.towerRecords[recordId].checksum != checksum)
{
ClearBattleTowerRecord(&gSaveBlock2Ptr->frontier.towerRecords[recordId]);
return FALSE;
}
else
{
return TRUE;
}
}
2020-12-24 22:18:47 +01:00
void TrySetLinkBattleTowerEnemyPartyLevel(void)
2018-10-25 21:27:10 +02:00
{
2021-01-13 21:17:32 +01:00
if (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK))
2018-10-25 21:27:10 +02:00
{
s32 i;
u8 enemyLevel = SetFacilityPtrsGetLevel();
for (i = 0; i < PARTY_SIZE; i++)
{
u32 species = GetMonData(&gEnemyParty[i], MON_DATA_SPECIES, NULL);
if (species)
{
SetMonData(&gEnemyParty[i], MON_DATA_EXP, &gExperienceTables[gBaseStats[species].growthRate][enemyLevel]);
CalculateMonStats(&gEnemyParty[i]);
}
}
}
}