2019-01-13 12:12:27 +01:00
|
|
|
#include "global.h"
|
2019-09-09 03:07:54 +02:00
|
|
|
#include "malloc.h"
|
2019-01-13 12:12:27 +01:00
|
|
|
#include "battle.h"
|
|
|
|
#include "battle_tower.h"
|
|
|
|
#include "battle_setup.h"
|
2019-03-22 20:16:26 +01:00
|
|
|
#include "ereader_helpers.h"
|
2019-01-13 12:12:27 +01:00
|
|
|
#include "event_data.h"
|
2019-01-13 20:50:08 +01:00
|
|
|
#include "event_scripts.h"
|
2019-01-13 12:12:27 +01:00
|
|
|
#include "fieldmap.h"
|
|
|
|
#include "field_message_box.h"
|
|
|
|
#include "international_string_util.h"
|
|
|
|
#include "item.h"
|
|
|
|
#include "main.h"
|
|
|
|
#include "menu.h"
|
|
|
|
#include "overworld.h"
|
|
|
|
#include "palette.h"
|
|
|
|
#include "pokemon.h"
|
|
|
|
#include "script.h"
|
|
|
|
#include "string_util.h"
|
2019-01-13 13:15:23 +01:00
|
|
|
#include "strings.h"
|
2019-01-13 12:12:27 +01:00
|
|
|
#include "text.h"
|
2019-01-13 13:15:23 +01:00
|
|
|
#include "trainer_hill.h"
|
2019-01-13 12:12:27 +01:00
|
|
|
#include "window.h"
|
|
|
|
#include "util.h"
|
2019-11-13 22:10:05 +01:00
|
|
|
#include "constants/battle_ai.h"
|
2019-11-21 20:03:35 +01:00
|
|
|
#include "constants/event_object_movement.h"
|
2019-01-13 13:15:23 +01:00
|
|
|
#include "constants/event_objects.h"
|
2019-01-13 20:50:08 +01:00
|
|
|
#include "constants/items.h"
|
2019-01-31 22:51:20 +01:00
|
|
|
#include "constants/layouts.h"
|
2019-01-19 12:57:18 +01:00
|
|
|
#include "constants/moves.h"
|
2019-01-13 20:50:08 +01:00
|
|
|
#include "constants/trainers.h"
|
2019-02-28 04:16:01 +01:00
|
|
|
#include "constants/trainer_hill.h"
|
2020-04-21 21:53:48 +02:00
|
|
|
#include "constants/trainer_types.h"
|
2019-01-13 20:50:08 +01:00
|
|
|
|
2019-01-13 12:12:27 +01:00
|
|
|
#define HILL_MAX_TIME 215999 // 60 * 60 * 60 - 1
|
|
|
|
|
2022-03-04 17:02:19 +01:00
|
|
|
struct FloorTrainers
|
2019-09-04 21:42:53 +02:00
|
|
|
{
|
2022-07-26 04:59:13 +02:00
|
|
|
u8 name[HILL_TRAINERS_PER_FLOOR][TRAINER_NAME_LENGTH + 1];
|
2022-03-04 17:02:19 +01:00
|
|
|
u8 facilityClass[HILL_TRAINERS_PER_FLOOR];
|
2019-09-04 21:42:53 +02:00
|
|
|
};
|
|
|
|
|
2022-03-04 17:02:19 +01:00
|
|
|
static EWRAM_DATA struct {
|
|
|
|
u8 floorId;
|
|
|
|
struct TrainerHillChallenge challenge;
|
|
|
|
struct TrainerHillFloor floors[NUM_TRAINER_HILL_FLOORS];
|
|
|
|
} *sHillData = NULL;
|
|
|
|
|
|
|
|
static EWRAM_DATA struct FloorTrainers *sFloorTrainers = NULL;
|
2019-03-01 07:49:11 +01:00
|
|
|
EWRAM_DATA u32 *gTrainerHillVBlankCounter = NULL;
|
2019-01-13 12:12:27 +01:00
|
|
|
|
|
|
|
// This file's functions.
|
2019-02-28 03:28:34 +01:00
|
|
|
static void TrainerHillStartChallenge(void);
|
2019-11-13 22:10:05 +01:00
|
|
|
static void GetOwnerState(void);
|
|
|
|
static void GiveChallengePrize(void);
|
|
|
|
static void CheckFinalTime(void);
|
2019-02-28 03:28:34 +01:00
|
|
|
static void TrainerHillResumeTimer(void);
|
|
|
|
static void TrainerHillSetPlayerLost(void);
|
2019-02-28 04:16:01 +01:00
|
|
|
static void TrainerHillGetChallengeStatus(void);
|
2019-11-13 22:10:05 +01:00
|
|
|
static void BufferChallengeTime(void);
|
|
|
|
static void GetAllFloorsUsed(void);
|
2020-06-24 22:27:00 +02:00
|
|
|
static void GetInEReaderMode(void);
|
2019-11-13 22:10:05 +01:00
|
|
|
static void IsTrainerHillChallengeActive(void);
|
|
|
|
static void ShowTrainerHillPostBattleText(void);
|
|
|
|
static void SetAllTrainerFlags(void);
|
|
|
|
static void GetGameSaved(void);
|
|
|
|
static void SetGameSaved(void);
|
|
|
|
static void ClearGameSaved(void);
|
|
|
|
static void GetChallengeWon(void);
|
2022-03-04 17:02:19 +01:00
|
|
|
static void TrainerHillSetMode(void);
|
2019-01-13 20:50:08 +01:00
|
|
|
static void SetUpDataStruct(void);
|
|
|
|
static void FreeDataStruct(void);
|
2021-03-21 04:47:08 +01:00
|
|
|
static void TrainerHillDummy(void);
|
2019-01-13 20:50:08 +01:00
|
|
|
static void SetTimerValue(u32 *dst, u32 val);
|
|
|
|
static u32 GetTimerValue(u32 *src);
|
2019-11-13 22:10:05 +01:00
|
|
|
static void SetTrainerHillMonLevel(struct Pokemon *mon, u8 level);
|
|
|
|
static u16 GetPrizeItemId(void);
|
2019-01-13 12:12:27 +01:00
|
|
|
|
|
|
|
// const data
|
2019-01-19 12:57:18 +01:00
|
|
|
#include "data/battle_frontier/trainer_hill.h"
|
2019-01-13 13:15:23 +01:00
|
|
|
|
2019-01-13 12:12:27 +01:00
|
|
|
struct
|
|
|
|
{
|
|
|
|
u8 trainerClass;
|
|
|
|
u8 musicId;
|
2019-11-13 22:10:05 +01:00
|
|
|
} static const sTrainerClassesAndMusic[] =
|
2019-01-13 20:50:08 +01:00
|
|
|
{
|
|
|
|
{TRAINER_CLASS_TEAM_AQUA, TRAINER_ENCOUNTER_MUSIC_AQUA},
|
|
|
|
{TRAINER_CLASS_AQUA_ADMIN, TRAINER_ENCOUNTER_MUSIC_AQUA},
|
|
|
|
{TRAINER_CLASS_AQUA_LEADER, TRAINER_ENCOUNTER_MUSIC_AQUA},
|
|
|
|
{TRAINER_CLASS_AROMA_LADY, TRAINER_ENCOUNTER_MUSIC_FEMALE},
|
|
|
|
{TRAINER_CLASS_BATTLE_GIRL, TRAINER_ENCOUNTER_MUSIC_INTENSE},
|
|
|
|
{TRAINER_CLASS_SWIMMER_F, TRAINER_ENCOUNTER_MUSIC_FEMALE},
|
|
|
|
{TRAINER_CLASS_POKEFAN, TRAINER_ENCOUNTER_MUSIC_TWINS},
|
|
|
|
{TRAINER_CLASS_DRAGON_TAMER, TRAINER_ENCOUNTER_MUSIC_INTENSE},
|
|
|
|
{TRAINER_CLASS_COOLTRAINER, TRAINER_ENCOUNTER_MUSIC_COOL},
|
|
|
|
{TRAINER_CLASS_GUITARIST, TRAINER_ENCOUNTER_MUSIC_INTENSE},
|
|
|
|
{TRAINER_CLASS_SAILOR, TRAINER_ENCOUNTER_MUSIC_MALE},
|
|
|
|
{TRAINER_CLASS_TWINS, TRAINER_ENCOUNTER_MUSIC_TWINS},
|
|
|
|
{TRAINER_CLASS_INTERVIEWER, TRAINER_ENCOUNTER_MUSIC_INTERVIEWER},
|
|
|
|
{TRAINER_CLASS_RUIN_MANIAC, TRAINER_ENCOUNTER_MUSIC_HIKER},
|
|
|
|
{TRAINER_CLASS_GENTLEMAN, TRAINER_ENCOUNTER_MUSIC_RICH},
|
|
|
|
{TRAINER_CLASS_SWIMMER_M, TRAINER_ENCOUNTER_MUSIC_FEMALE},
|
|
|
|
{TRAINER_CLASS_POKEMANIAC, TRAINER_ENCOUNTER_MUSIC_SUSPICIOUS},
|
|
|
|
{TRAINER_CLASS_BLACK_BELT, TRAINER_ENCOUNTER_MUSIC_INTENSE},
|
|
|
|
{TRAINER_CLASS_OLD_COUPLE, TRAINER_ENCOUNTER_MUSIC_INTENSE},
|
|
|
|
{TRAINER_CLASS_BUG_MANIAC, TRAINER_ENCOUNTER_MUSIC_SUSPICIOUS},
|
|
|
|
{TRAINER_CLASS_CAMPER, TRAINER_ENCOUNTER_MUSIC_MALE},
|
|
|
|
{TRAINER_CLASS_KINDLER, TRAINER_ENCOUNTER_MUSIC_HIKER},
|
|
|
|
{TRAINER_CLASS_TEAM_MAGMA, TRAINER_ENCOUNTER_MUSIC_MAGMA},
|
|
|
|
{TRAINER_CLASS_MAGMA_ADMIN, TRAINER_ENCOUNTER_MUSIC_MAGMA},
|
|
|
|
{TRAINER_CLASS_MAGMA_LEADER, TRAINER_ENCOUNTER_MUSIC_MAGMA},
|
|
|
|
{TRAINER_CLASS_LASS, TRAINER_ENCOUNTER_MUSIC_FEMALE},
|
|
|
|
{TRAINER_CLASS_BUG_CATCHER, TRAINER_ENCOUNTER_MUSIC_MALE},
|
|
|
|
{TRAINER_CLASS_NINJA_BOY, TRAINER_ENCOUNTER_MUSIC_SUSPICIOUS},
|
|
|
|
{TRAINER_CLASS_RICH_BOY, TRAINER_ENCOUNTER_MUSIC_RICH},
|
|
|
|
{TRAINER_CLASS_HEX_MANIAC, TRAINER_ENCOUNTER_MUSIC_SUSPICIOUS},
|
|
|
|
{TRAINER_CLASS_BEAUTY, TRAINER_ENCOUNTER_MUSIC_FEMALE},
|
|
|
|
{TRAINER_CLASS_LADY, TRAINER_ENCOUNTER_MUSIC_FEMALE},
|
|
|
|
{TRAINER_CLASS_PARASOL_LADY, TRAINER_ENCOUNTER_MUSIC_FEMALE},
|
|
|
|
{TRAINER_CLASS_PICNICKER, TRAINER_ENCOUNTER_MUSIC_GIRL},
|
|
|
|
{TRAINER_CLASS_PKMN_BREEDER, TRAINER_ENCOUNTER_MUSIC_FEMALE},
|
|
|
|
{TRAINER_CLASS_COLLECTOR, TRAINER_ENCOUNTER_MUSIC_SUSPICIOUS},
|
|
|
|
{TRAINER_CLASS_PKMN_RANGER, TRAINER_ENCOUNTER_MUSIC_COOL},
|
2021-10-03 05:48:12 +02:00
|
|
|
{TRAINER_CLASS_RIVAL, TRAINER_ENCOUNTER_MUSIC_MALE},
|
2019-01-13 20:50:08 +01:00
|
|
|
{TRAINER_CLASS_YOUNG_COUPLE, TRAINER_ENCOUNTER_MUSIC_GIRL},
|
|
|
|
{TRAINER_CLASS_PSYCHIC, TRAINER_ENCOUNTER_MUSIC_INTENSE},
|
|
|
|
{TRAINER_CLASS_SR_AND_JR, TRAINER_ENCOUNTER_MUSIC_TWINS},
|
|
|
|
{TRAINER_CLASS_ELITE_FOUR, TRAINER_ENCOUNTER_MUSIC_FEMALE},
|
|
|
|
{TRAINER_CLASS_YOUNGSTER, TRAINER_ENCOUNTER_MUSIC_MALE},
|
|
|
|
{TRAINER_CLASS_EXPERT, TRAINER_ENCOUNTER_MUSIC_INTENSE},
|
|
|
|
{TRAINER_CLASS_TRIATHLETE, TRAINER_ENCOUNTER_MUSIC_MALE},
|
|
|
|
{TRAINER_CLASS_BIRD_KEEPER, TRAINER_ENCOUNTER_MUSIC_COOL},
|
|
|
|
{TRAINER_CLASS_FISHERMAN, TRAINER_ENCOUNTER_MUSIC_HIKER},
|
|
|
|
{TRAINER_CLASS_CHAMPION, TRAINER_ENCOUNTER_MUSIC_MALE},
|
|
|
|
{TRAINER_CLASS_TUBER_M, TRAINER_ENCOUNTER_MUSIC_MALE},
|
|
|
|
{TRAINER_CLASS_TUBER_F, TRAINER_ENCOUNTER_MUSIC_GIRL},
|
|
|
|
{TRAINER_CLASS_SIS_AND_BRO, TRAINER_ENCOUNTER_MUSIC_SWIMMER},
|
|
|
|
{TRAINER_CLASS_HIKER, TRAINER_ENCOUNTER_MUSIC_HIKER},
|
|
|
|
{TRAINER_CLASS_LEADER, TRAINER_ENCOUNTER_MUSIC_FEMALE},
|
|
|
|
{TRAINER_CLASS_SCHOOL_KID, TRAINER_ENCOUNTER_MUSIC_MALE},
|
|
|
|
};
|
|
|
|
|
2019-11-13 22:10:05 +01:00
|
|
|
static const u16 sPrizeListRareCandy1[] = {ITEM_RARE_CANDY, ITEM_ETHER, ITEM_MAX_POTION, ITEM_REVIVE, ITEM_FLUFFY_TAIL, ITEM_GREAT_BALL};
|
|
|
|
static const u16 sPrizeListLuxuryBall1[] = {ITEM_LUXURY_BALL, ITEM_ETHER, ITEM_MAX_POTION, ITEM_REVIVE, ITEM_FLUFFY_TAIL, ITEM_GREAT_BALL};
|
|
|
|
static const u16 sPrizeListMaxRevive1[] = {ITEM_MAX_REVIVE, ITEM_ETHER, ITEM_MAX_POTION, ITEM_REVIVE, ITEM_FLUFFY_TAIL, ITEM_GREAT_BALL};
|
|
|
|
static const u16 sPrizeListMaxEther1[] = {ITEM_MAX_ETHER, ITEM_ETHER, ITEM_MAX_POTION, ITEM_REVIVE, ITEM_FLUFFY_TAIL, ITEM_GREAT_BALL};
|
|
|
|
static const u16 sPrizeListElixir1[] = {ITEM_ELIXIR, ITEM_ETHER, ITEM_MAX_POTION, ITEM_REVIVE, ITEM_FLUFFY_TAIL, ITEM_GREAT_BALL};
|
|
|
|
static const u16 sPrizeListRoar[] = {ITEM_TM05_ROAR, ITEM_ETHER, ITEM_MAX_POTION, ITEM_REVIVE, ITEM_FLUFFY_TAIL, ITEM_GREAT_BALL};
|
|
|
|
static const u16 sPrizeListSludgeBomb[] = {ITEM_TM36_SLUDGE_BOMB, ITEM_ETHER, ITEM_MAX_POTION, ITEM_REVIVE, ITEM_FLUFFY_TAIL, ITEM_GREAT_BALL};
|
|
|
|
static const u16 sPrizeListToxic[] = {ITEM_TM06_TOXIC, ITEM_ETHER, ITEM_MAX_POTION, ITEM_REVIVE, ITEM_FLUFFY_TAIL, ITEM_GREAT_BALL};
|
|
|
|
static const u16 sPrizeListSunnyDay[] = {ITEM_TM11_SUNNY_DAY, ITEM_ETHER, ITEM_MAX_POTION, ITEM_REVIVE, ITEM_FLUFFY_TAIL, ITEM_GREAT_BALL};
|
|
|
|
static const u16 sPrizeListEarthQuake[] = {ITEM_TM26_EARTHQUAKE, ITEM_ETHER, ITEM_MAX_POTION, ITEM_REVIVE, ITEM_FLUFFY_TAIL, ITEM_GREAT_BALL};
|
|
|
|
|
|
|
|
static const u16 sPrizeListRareCandy2[] = {ITEM_RARE_CANDY, ITEM_ETHER, ITEM_MAX_POTION, ITEM_REVIVE, ITEM_FLUFFY_TAIL, ITEM_GREAT_BALL};
|
|
|
|
static const u16 sPrizeListLuxuryBall2[] = {ITEM_LUXURY_BALL, ITEM_ETHER, ITEM_MAX_POTION, ITEM_REVIVE, ITEM_FLUFFY_TAIL, ITEM_GREAT_BALL};
|
|
|
|
static const u16 sPrizeListMaxRevive2[] = {ITEM_MAX_REVIVE, ITEM_ETHER, ITEM_MAX_POTION, ITEM_REVIVE, ITEM_FLUFFY_TAIL, ITEM_GREAT_BALL};
|
|
|
|
static const u16 sPrizeListMaxEther2[] = {ITEM_MAX_ETHER, ITEM_ETHER, ITEM_MAX_POTION, ITEM_REVIVE, ITEM_FLUFFY_TAIL, ITEM_GREAT_BALL};
|
|
|
|
static const u16 sPrizeListElixir2[] = {ITEM_ELIXIR, ITEM_ETHER, ITEM_MAX_POTION, ITEM_REVIVE, ITEM_FLUFFY_TAIL, ITEM_GREAT_BALL};
|
|
|
|
static const u16 sPrizeListBrickBreak[] = {ITEM_TM31_BRICK_BREAK, ITEM_ETHER, ITEM_MAX_POTION, ITEM_REVIVE, ITEM_FLUFFY_TAIL, ITEM_GREAT_BALL};
|
|
|
|
static const u16 sPrizeListTorment[] = {ITEM_TM41_TORMENT, ITEM_ETHER, ITEM_MAX_POTION, ITEM_REVIVE, ITEM_FLUFFY_TAIL, ITEM_GREAT_BALL};
|
|
|
|
static const u16 sPrizeListSkillSwap[] = {ITEM_TM48_SKILL_SWAP, ITEM_ETHER, ITEM_MAX_POTION, ITEM_REVIVE, ITEM_FLUFFY_TAIL, ITEM_GREAT_BALL};
|
2020-05-19 22:13:25 +02:00
|
|
|
static const u16 sPrizeListGigaDrain[] = {ITEM_TM19_GIGA_DRAIN, ITEM_ETHER, ITEM_MAX_POTION, ITEM_REVIVE, ITEM_FLUFFY_TAIL, ITEM_GREAT_BALL};
|
2019-11-13 22:10:05 +01:00
|
|
|
static const u16 sPrizeListAttract[] = {ITEM_TM45_ATTRACT, ITEM_ETHER, ITEM_MAX_POTION, ITEM_REVIVE, ITEM_FLUFFY_TAIL, ITEM_GREAT_BALL};
|
|
|
|
|
|
|
|
static const u16 *const sPrizeLists1[NUM_TRAINER_HILL_PRIZE_LISTS] =
|
|
|
|
{
|
|
|
|
sPrizeListRareCandy1,
|
|
|
|
sPrizeListLuxuryBall1,
|
|
|
|
sPrizeListMaxRevive1,
|
|
|
|
sPrizeListMaxEther1,
|
|
|
|
sPrizeListElixir1,
|
|
|
|
sPrizeListRoar,
|
|
|
|
sPrizeListSludgeBomb,
|
|
|
|
sPrizeListToxic,
|
|
|
|
sPrizeListSunnyDay,
|
|
|
|
sPrizeListEarthQuake
|
2019-01-13 20:50:08 +01:00
|
|
|
};
|
2019-01-13 12:12:27 +01:00
|
|
|
|
2019-11-13 22:10:05 +01:00
|
|
|
static const u16 *const sPrizeLists2[NUM_TRAINER_HILL_PRIZE_LISTS] =
|
|
|
|
{
|
|
|
|
sPrizeListRareCandy2,
|
|
|
|
sPrizeListLuxuryBall2,
|
|
|
|
sPrizeListMaxRevive2,
|
|
|
|
sPrizeListMaxEther2,
|
|
|
|
sPrizeListElixir2,
|
|
|
|
sPrizeListBrickBreak,
|
|
|
|
sPrizeListTorment,
|
|
|
|
sPrizeListSkillSwap,
|
2020-05-19 22:13:25 +02:00
|
|
|
sPrizeListGigaDrain,
|
2019-11-13 22:10:05 +01:00
|
|
|
sPrizeListAttract
|
2019-01-13 20:50:08 +01:00
|
|
|
};
|
|
|
|
|
2019-11-13 22:10:05 +01:00
|
|
|
static const u16 *const *const sPrizeListSets[] =
|
2019-01-13 20:50:08 +01:00
|
|
|
{
|
2019-11-13 22:10:05 +01:00
|
|
|
sPrizeLists1,
|
|
|
|
sPrizeLists2
|
2019-01-13 20:50:08 +01:00
|
|
|
};
|
2019-01-13 12:12:27 +01:00
|
|
|
|
2022-01-14 18:29:30 +01:00
|
|
|
static const u16 sEReader_Pal[] = INCBIN_U16("graphics/trainer_hill/ereader.gbapal");
|
2021-04-10 04:39:34 +02:00
|
|
|
static const u8 sRecordWinColors[] = {TEXT_COLOR_TRANSPARENT, TEXT_COLOR_DARK_GRAY, TEXT_COLOR_LIGHT_GRAY};
|
2019-01-13 13:15:23 +01:00
|
|
|
|
2022-03-04 17:02:19 +01:00
|
|
|
static const struct TrainerHillChallenge *const sChallengeData[NUM_TRAINER_HILL_MODES] =
|
2019-01-13 13:15:23 +01:00
|
|
|
{
|
2022-03-04 17:02:19 +01:00
|
|
|
[HILL_MODE_NORMAL] = &sChallenge_Normal,
|
|
|
|
[HILL_MODE_VARIETY] = &sChallenge_Variety,
|
|
|
|
[HILL_MODE_UNIQUE] = &sChallenge_Unique,
|
|
|
|
[HILL_MODE_EXPERT] = &sChallenge_Expert,
|
2019-01-13 13:15:23 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
// Unused.
|
|
|
|
static const u8 *const sFloorStrings[] =
|
|
|
|
{
|
|
|
|
gText_TrainerHill1F,
|
|
|
|
gText_TrainerHill2F,
|
|
|
|
gText_TrainerHill3F,
|
|
|
|
gText_TrainerHill4F,
|
|
|
|
};
|
|
|
|
|
|
|
|
static void (* const sHillFunctions[])(void) =
|
|
|
|
{
|
2019-11-23 22:08:50 +01:00
|
|
|
[TRAINER_HILL_FUNC_START] = TrainerHillStartChallenge,
|
|
|
|
[TRAINER_HILL_FUNC_GET_OWNER_STATE] = GetOwnerState,
|
|
|
|
[TRAINER_HILL_FUNC_GIVE_PRIZE] = GiveChallengePrize,
|
|
|
|
[TRAINER_HILL_FUNC_CHECK_FINAL_TIME] = CheckFinalTime,
|
|
|
|
[TRAINER_HILL_FUNC_RESUME_TIMER] = TrainerHillResumeTimer,
|
|
|
|
[TRAINER_HILL_FUNC_SET_LOST] = TrainerHillSetPlayerLost,
|
|
|
|
[TRAINER_HILL_FUNC_GET_CHALLENGE_STATUS] = TrainerHillGetChallengeStatus,
|
|
|
|
[TRAINER_HILL_FUNC_GET_CHALLENGE_TIME] = BufferChallengeTime,
|
|
|
|
[TRAINER_HILL_FUNC_GET_ALL_FLOORS_USED] = GetAllFloorsUsed,
|
2020-06-24 22:27:00 +02:00
|
|
|
[TRAINER_HILL_FUNC_GET_IN_EREADER_MODE] = GetInEReaderMode,
|
2019-11-23 22:08:50 +01:00
|
|
|
[TRAINER_HILL_FUNC_IN_CHALLENGE] = IsTrainerHillChallengeActive,
|
|
|
|
[TRAINER_HILL_FUNC_POST_BATTLE_TEXT] = ShowTrainerHillPostBattleText,
|
2019-11-13 22:10:05 +01:00
|
|
|
[TRAINER_HILL_FUNC_SET_ALL_TRAINER_FLAGS] = SetAllTrainerFlags,
|
2019-11-23 22:08:50 +01:00
|
|
|
[TRAINER_HILL_FUNC_GET_GAME_SAVED] = GetGameSaved,
|
|
|
|
[TRAINER_HILL_FUNC_SET_GAME_SAVED] = SetGameSaved,
|
|
|
|
[TRAINER_HILL_FUNC_CLEAR_GAME_SAVED] = ClearGameSaved,
|
|
|
|
[TRAINER_HILL_FUNC_GET_WON] = GetChallengeWon,
|
2022-03-04 17:02:19 +01:00
|
|
|
[TRAINER_HILL_FUNC_SET_MODE] = TrainerHillSetMode,
|
2019-01-13 13:15:23 +01:00
|
|
|
};
|
|
|
|
|
2022-03-04 17:02:19 +01:00
|
|
|
static const u8 *const sModeStrings[NUM_TRAINER_HILL_MODES] =
|
2019-01-13 13:15:23 +01:00
|
|
|
{
|
2022-03-04 17:02:19 +01:00
|
|
|
[HILL_MODE_NORMAL] = gText_NormalTagMatch,
|
|
|
|
[HILL_MODE_VARIETY] = gText_VarietyTagMatch,
|
|
|
|
[HILL_MODE_UNIQUE] = gText_UniqueTagMatch,
|
|
|
|
[HILL_MODE_EXPERT] = gText_ExpertTagMatch,
|
2019-01-13 13:15:23 +01:00
|
|
|
};
|
|
|
|
|
2019-11-21 04:55:44 +01:00
|
|
|
static const struct ObjectEventTemplate sTrainerObjectEventTemplate =
|
2019-01-13 13:15:23 +01:00
|
|
|
{
|
2019-11-21 05:12:51 +01:00
|
|
|
.graphicsId = OBJ_EVENT_GFX_RIVAL_BRENDAN_NORMAL,
|
2019-01-13 13:15:23 +01:00
|
|
|
.elevation = 3,
|
|
|
|
.movementType = MOVEMENT_TYPE_LOOK_AROUND,
|
|
|
|
.movementRangeX = 1,
|
|
|
|
.movementRangeY = 1,
|
2020-04-21 21:53:48 +02:00
|
|
|
.trainerType = TRAINER_TYPE_NORMAL,
|
2019-01-13 13:15:23 +01:00
|
|
|
};
|
|
|
|
|
2021-08-25 00:59:32 +02:00
|
|
|
static const u32 sNextFloorMapNum[NUM_TRAINER_HILL_FLOORS] =
|
2019-11-13 22:10:05 +01:00
|
|
|
{
|
2022-03-04 17:02:19 +01:00
|
|
|
[TRAINER_HILL_1F - 1] = MAP_NUM(TRAINER_HILL_2F),
|
|
|
|
[TRAINER_HILL_2F - 1] = MAP_NUM(TRAINER_HILL_3F),
|
|
|
|
[TRAINER_HILL_3F - 1] = MAP_NUM(TRAINER_HILL_4F),
|
|
|
|
[TRAINER_HILL_4F - 1] = MAP_NUM(TRAINER_HILL_ROOF)
|
2019-11-13 22:10:05 +01:00
|
|
|
};
|
2022-03-04 17:02:19 +01:00
|
|
|
static const u8 sTrainerPartySlots[HILL_TRAINERS_PER_FLOOR][PARTY_SIZE / 2] =
|
2019-11-13 22:10:05 +01:00
|
|
|
{
|
2021-08-25 00:59:32 +02:00
|
|
|
{0, 1, 2},
|
2019-11-13 22:10:05 +01:00
|
|
|
{3, 4, 5}
|
|
|
|
};
|
2019-01-13 13:15:23 +01:00
|
|
|
|
|
|
|
void CallTrainerHillFunction(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2019-01-13 13:15:23 +01:00
|
|
|
SetUpDataStruct();
|
|
|
|
sHillFunctions[gSpecialVar_0x8004]();
|
|
|
|
FreeDataStruct();
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
2019-01-13 20:50:08 +01:00
|
|
|
void ResetTrainerHillResults(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
|
|
|
s32 i;
|
|
|
|
|
2019-11-13 22:31:01 +01:00
|
|
|
gSaveBlock2Ptr->frontier.savedGame = 0;
|
2019-11-16 19:45:21 +01:00
|
|
|
gSaveBlock2Ptr->frontier.unk_EF9 = 0;
|
2019-02-28 04:16:01 +01:00
|
|
|
gSaveBlock1Ptr->trainerHill.bestTime = 0;
|
2022-03-04 17:02:19 +01:00
|
|
|
for (i = 0; i < NUM_TRAINER_HILL_MODES; i++)
|
2019-01-13 13:15:23 +01:00
|
|
|
SetTimerValue(&gSaveBlock1Ptr->trainerHillTimes[i], HILL_MAX_TIME);
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
2019-01-13 20:50:08 +01:00
|
|
|
static u8 GetFloorId(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2019-01-31 22:51:20 +01:00
|
|
|
return gMapHeader.mapLayoutId - LAYOUT_TRAINER_HILL_1F;
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
u8 GetTrainerHillOpponentClass(u16 trainerId)
|
|
|
|
{
|
|
|
|
u8 id = trainerId - 1;
|
|
|
|
|
2022-03-04 17:02:19 +01:00
|
|
|
return gFacilityClassToTrainerClass[sFloorTrainers->facilityClass[id]];
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void GetTrainerHillTrainerName(u8 *dst, u16 trainerId)
|
|
|
|
{
|
|
|
|
s32 i;
|
|
|
|
u8 id = trainerId - 1;
|
|
|
|
|
2022-07-26 04:59:13 +02:00
|
|
|
for (i = 0; i < TRAINER_NAME_LENGTH + 1; i++)
|
2022-03-04 17:02:19 +01:00
|
|
|
dst[i] = sFloorTrainers->name[id][i];
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
u8 GetTrainerHillTrainerFrontSpriteId(u16 trainerId)
|
|
|
|
{
|
|
|
|
u8 id, facilityClass;
|
|
|
|
|
2019-01-13 13:15:23 +01:00
|
|
|
SetUpDataStruct();
|
2019-01-13 12:12:27 +01:00
|
|
|
id = trainerId - 1;
|
2019-09-04 21:42:53 +02:00
|
|
|
facilityClass = sHillData->floors[sHillData->floorId].trainers[id].facilityClass;
|
2019-01-13 13:15:23 +01:00
|
|
|
FreeDataStruct();
|
2019-01-13 12:12:27 +01:00
|
|
|
|
|
|
|
return gFacilityClassToPicIndex[facilityClass];
|
|
|
|
}
|
|
|
|
|
2019-01-13 20:50:08 +01:00
|
|
|
void InitTrainerHillBattleStruct(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
|
|
|
s32 i, j;
|
|
|
|
|
2019-01-13 13:15:23 +01:00
|
|
|
SetUpDataStruct();
|
2022-03-04 17:02:19 +01:00
|
|
|
sFloorTrainers = AllocZeroed(sizeof(*sFloorTrainers));
|
2019-01-13 12:12:27 +01:00
|
|
|
|
2022-03-04 17:02:19 +01:00
|
|
|
for (i = 0; i < HILL_TRAINERS_PER_FLOOR; i++)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2022-07-26 04:59:13 +02:00
|
|
|
for (j = 0; j < TRAINER_NAME_LENGTH + 1; j++)
|
2022-03-04 17:02:19 +01:00
|
|
|
sFloorTrainers->name[i][j] = sHillData->floors[sHillData->floorId].trainers[i].name[j];
|
|
|
|
|
|
|
|
sFloorTrainers->facilityClass[i] = sHillData->floors[sHillData->floorId].trainers[i].facilityClass;
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
2019-03-01 07:49:11 +01:00
|
|
|
SetTrainerHillVBlankCounter(&gSaveBlock1Ptr->trainerHill.timer);
|
2019-01-13 13:15:23 +01:00
|
|
|
FreeDataStruct();
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
2019-01-13 20:50:08 +01:00
|
|
|
void FreeTrainerHillBattleStruct(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2022-03-04 17:02:19 +01:00
|
|
|
TRY_FREE_AND_SET_NULL(sFloorTrainers);
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
2019-01-13 20:50:08 +01:00
|
|
|
static void SetUpDataStruct(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2019-01-13 13:15:23 +01:00
|
|
|
if (sHillData == NULL)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
Undo PokeCodec's PRs
This commit undoes most of PokeCodec's PRs after the debate in chat. Some
harmless or completely superseded PRs have been left alone, as there is not
much benefit in attempting to undo them.
Reverts #1104, #1108, #1115, #1118, #1119, #1124, #1126, #1127, #1132, #1136,
#1137, #1139, #1140, #1144, #1148, #1149, #1150, #1153, #1155, #1177, #1179,
#1180, #1181, #1182 and #1183.
2020-09-13 09:22:50 +02:00
|
|
|
sHillData = AllocZeroed(sizeof(*sHillData));
|
2019-01-31 22:51:20 +01:00
|
|
|
sHillData->floorId = gMapHeader.mapLayoutId - LAYOUT_TRAINER_HILL_1F;
|
2022-03-04 17:02:19 +01:00
|
|
|
|
|
|
|
// This copy depends on the floor data for each challenge being directly after the
|
|
|
|
// challenge header data, and for the field 'floors' in sHillData to come directly
|
|
|
|
// after the field 'challenge'.
|
|
|
|
// e.g. for HILL_MODE_NORMAL, it will copy sChallenge_Normal to sHillData->challenge and
|
|
|
|
// it will copy sFloors_Normal to sHillData->floors
|
|
|
|
CpuCopy32(sChallengeData[gSaveBlock1Ptr->trainerHill.mode], &sHillData->challenge, sizeof(sHillData->challenge) + sizeof(sHillData->floors));
|
2021-03-21 04:47:08 +01:00
|
|
|
TrainerHillDummy();
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-13 20:50:08 +01:00
|
|
|
static void FreeDataStruct(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2022-03-04 17:02:19 +01:00
|
|
|
TRY_FREE_AND_SET_NULL(sHillData);
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void CopyTrainerHillTrainerText(u8 which, u16 trainerId)
|
|
|
|
{
|
2019-01-13 13:15:23 +01:00
|
|
|
u8 id, floorId;
|
2019-01-13 12:12:27 +01:00
|
|
|
|
2019-01-13 13:15:23 +01:00
|
|
|
SetUpDataStruct();
|
|
|
|
floorId = GetFloorId();
|
2019-01-13 12:12:27 +01:00
|
|
|
id = trainerId - 1;
|
|
|
|
|
|
|
|
switch (which)
|
|
|
|
{
|
2019-11-13 22:10:05 +01:00
|
|
|
case TRAINER_HILL_TEXT_INTRO:
|
2019-09-04 21:42:53 +02:00
|
|
|
FrontierSpeechToString(sHillData->floors[floorId].trainers[id].speechBefore);
|
2019-01-13 12:12:27 +01:00
|
|
|
break;
|
2019-11-13 22:10:05 +01:00
|
|
|
case TRAINER_HILL_TEXT_PLAYER_LOST:
|
2019-09-04 21:42:53 +02:00
|
|
|
FrontierSpeechToString(sHillData->floors[floorId].trainers[id].speechWin);
|
2019-01-13 12:12:27 +01:00
|
|
|
break;
|
2019-11-13 22:10:05 +01:00
|
|
|
case TRAINER_HILL_TEXT_PLAYER_WON:
|
2019-09-04 21:42:53 +02:00
|
|
|
FrontierSpeechToString(sHillData->floors[floorId].trainers[id].speechLose);
|
2019-01-13 12:12:27 +01:00
|
|
|
break;
|
2019-11-13 22:10:05 +01:00
|
|
|
case TRAINER_HILL_TEXT_AFTER:
|
2019-09-04 21:42:53 +02:00
|
|
|
FrontierSpeechToString(sHillData->floors[floorId].trainers[id].speechAfter);
|
2019-01-13 12:12:27 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-01-13 13:15:23 +01:00
|
|
|
FreeDataStruct();
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
2019-02-28 03:28:34 +01:00
|
|
|
static void TrainerHillStartChallenge(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2021-03-21 04:47:08 +01:00
|
|
|
TrainerHillDummy();
|
2019-04-04 17:55:18 +02:00
|
|
|
if (!ReadTrainerHillAndValidate())
|
2019-01-13 12:12:27 +01:00
|
|
|
gSaveBlock1Ptr->trainerHill.field_3D6E_0f = 1;
|
|
|
|
else
|
|
|
|
gSaveBlock1Ptr->trainerHill.field_3D6E_0f = 0;
|
|
|
|
|
2019-11-16 19:45:21 +01:00
|
|
|
gSaveBlock1Ptr->trainerHill.unk_3D6C = 0;
|
2019-03-01 07:49:11 +01:00
|
|
|
SetTrainerHillVBlankCounter(&gSaveBlock1Ptr->trainerHill.timer);
|
2019-02-28 03:28:34 +01:00
|
|
|
gSaveBlock1Ptr->trainerHill.timer = 0;
|
2019-11-13 22:10:05 +01:00
|
|
|
gSaveBlock1Ptr->trainerHill.spokeToOwner = 0;
|
|
|
|
gSaveBlock1Ptr->trainerHill.checkedFinalTime = 0;
|
2019-02-28 04:16:01 +01:00
|
|
|
gSaveBlock1Ptr->trainerHill.maybeECardScanDuringChallenge = 0;
|
2019-11-13 22:10:05 +01:00
|
|
|
gSaveBlock2Ptr->frontier.trainerFlags = 0;
|
2019-01-13 12:12:27 +01:00
|
|
|
gBattleOutcome = 0;
|
2019-11-13 22:10:05 +01:00
|
|
|
gSaveBlock1Ptr->trainerHill.receivedPrize = 0;
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
2019-11-13 22:10:05 +01:00
|
|
|
static void GetOwnerState(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2019-03-01 07:49:11 +01:00
|
|
|
ClearTrainerHillVBlankCounter();
|
2019-01-13 12:12:27 +01:00
|
|
|
gSpecialVar_Result = 0;
|
2019-11-13 22:10:05 +01:00
|
|
|
if (gSaveBlock1Ptr->trainerHill.spokeToOwner)
|
2019-01-13 12:12:27 +01:00
|
|
|
gSpecialVar_Result++;
|
2019-11-13 22:10:05 +01:00
|
|
|
if (gSaveBlock1Ptr->trainerHill.receivedPrize && gSaveBlock1Ptr->trainerHill.checkedFinalTime)
|
2019-01-13 12:12:27 +01:00
|
|
|
gSpecialVar_Result++;
|
|
|
|
|
2019-11-13 22:10:05 +01:00
|
|
|
gSaveBlock1Ptr->trainerHill.spokeToOwner = TRUE;
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
2019-11-13 22:10:05 +01:00
|
|
|
static void GiveChallengePrize(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2019-11-13 22:10:05 +01:00
|
|
|
u16 itemId = GetPrizeItemId();
|
2019-01-13 12:12:27 +01:00
|
|
|
|
2022-03-04 17:02:19 +01:00
|
|
|
if (sHillData->challenge.numFloors != NUM_TRAINER_HILL_FLOORS || gSaveBlock1Ptr->trainerHill.receivedPrize)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
|
|
|
gSpecialVar_Result = 2;
|
|
|
|
}
|
|
|
|
else if (AddBagItem(itemId, 1) == TRUE)
|
|
|
|
{
|
|
|
|
CopyItemName(itemId, gStringVar2);
|
2019-11-13 22:10:05 +01:00
|
|
|
gSaveBlock1Ptr->trainerHill.receivedPrize = TRUE;
|
2019-11-16 19:45:21 +01:00
|
|
|
gSaveBlock2Ptr->frontier.unk_EF9 = 0;
|
2019-01-13 12:12:27 +01:00
|
|
|
gSpecialVar_Result = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gSpecialVar_Result = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-16 19:54:50 +01:00
|
|
|
// If bestTime > timer, the challenge was completed faster and its a new record
|
|
|
|
// Otherwise the owner says it was a slow time and to complete it faster next time
|
2019-11-13 22:10:05 +01:00
|
|
|
static void CheckFinalTime(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2019-11-13 22:10:05 +01:00
|
|
|
if (gSaveBlock1Ptr->trainerHill.checkedFinalTime)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
|
|
|
gSpecialVar_Result = 2;
|
|
|
|
}
|
2019-02-28 04:16:01 +01:00
|
|
|
else if (GetTimerValue(&gSaveBlock1Ptr->trainerHill.bestTime) > gSaveBlock1Ptr->trainerHill.timer)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2019-02-28 04:16:01 +01:00
|
|
|
SetTimerValue(&gSaveBlock1Ptr->trainerHill.bestTime, gSaveBlock1Ptr->trainerHill.timer);
|
2022-03-04 17:02:19 +01:00
|
|
|
gSaveBlock1Ptr->trainerHillTimes[gSaveBlock1Ptr->trainerHill.mode] = gSaveBlock1Ptr->trainerHill.bestTime;
|
2019-01-13 12:12:27 +01:00
|
|
|
gSpecialVar_Result = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gSpecialVar_Result = 1;
|
|
|
|
}
|
|
|
|
|
2019-11-13 22:10:05 +01:00
|
|
|
gSaveBlock1Ptr->trainerHill.checkedFinalTime = TRUE;
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
2019-02-28 03:28:34 +01:00
|
|
|
static void TrainerHillResumeTimer(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2019-11-13 22:10:05 +01:00
|
|
|
if (!gSaveBlock1Ptr->trainerHill.spokeToOwner)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2019-02-28 03:28:34 +01:00
|
|
|
if (gSaveBlock1Ptr->trainerHill.timer >= HILL_MAX_TIME)
|
|
|
|
gSaveBlock1Ptr->trainerHill.timer = HILL_MAX_TIME;
|
2019-01-13 12:12:27 +01:00
|
|
|
else
|
2019-03-01 07:49:11 +01:00
|
|
|
SetTrainerHillVBlankCounter(&gSaveBlock1Ptr->trainerHill.timer);
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-28 03:28:34 +01:00
|
|
|
static void TrainerHillSetPlayerLost(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2019-11-13 22:10:05 +01:00
|
|
|
gSaveBlock1Ptr->trainerHill.hasLost = TRUE;
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
2019-02-28 04:16:01 +01:00
|
|
|
static void TrainerHillGetChallengeStatus(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2019-02-28 03:28:34 +01:00
|
|
|
if (gSaveBlock1Ptr->trainerHill.hasLost)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2019-02-28 04:16:01 +01:00
|
|
|
// The player lost their last match.
|
2019-11-13 22:10:05 +01:00
|
|
|
gSaveBlock1Ptr->trainerHill.hasLost = FALSE;
|
2019-02-28 04:16:01 +01:00
|
|
|
gSpecialVar_Result = TRAINER_HILL_PLAYER_STATUS_LOST;
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
2019-02-28 04:16:01 +01:00
|
|
|
else if (gSaveBlock1Ptr->trainerHill.maybeECardScanDuringChallenge)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2019-02-28 04:16:01 +01:00
|
|
|
// Unreachable code. Something relating to eCards?
|
|
|
|
gSaveBlock1Ptr->trainerHill.maybeECardScanDuringChallenge = 0;
|
|
|
|
gSpecialVar_Result = TRAINER_HILL_PLAYER_STATUS_ECARD_SCANNED;
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-02-28 04:16:01 +01:00
|
|
|
// Continue playing.
|
|
|
|
gSpecialVar_Result = TRAINER_HILL_PLAYER_STATUS_NORMAL;
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-13 22:10:05 +01:00
|
|
|
static void BufferChallengeTime(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
|
|
|
s32 total, minutes, secondsWhole, secondsFraction;
|
|
|
|
|
2019-02-28 03:28:34 +01:00
|
|
|
total = gSaveBlock1Ptr->trainerHill.timer;
|
2019-01-13 12:12:27 +01:00
|
|
|
if (total >= HILL_MAX_TIME)
|
|
|
|
total = HILL_MAX_TIME;
|
|
|
|
|
|
|
|
minutes = total / (60 * 60);
|
|
|
|
total %= (60 * 60);
|
|
|
|
secondsWhole = total / 60;
|
|
|
|
total %= 60;
|
|
|
|
secondsFraction = (total * 168) / 100;
|
|
|
|
|
|
|
|
ConvertIntToDecimalStringN(gStringVar1, minutes, STR_CONV_MODE_RIGHT_ALIGN, 2);
|
|
|
|
ConvertIntToDecimalStringN(gStringVar2, secondsWhole, STR_CONV_MODE_RIGHT_ALIGN, 2);
|
|
|
|
ConvertIntToDecimalStringN(gStringVar3, secondsFraction, STR_CONV_MODE_LEADING_ZEROS, 2);
|
|
|
|
}
|
|
|
|
|
2019-11-13 22:10:05 +01:00
|
|
|
// Returns TRUE if all 4 floors are used
|
|
|
|
// Returns FALSE otherwise, and buffers the number of floors used
|
2019-11-13 22:31:01 +01:00
|
|
|
// The only time fewer than all 4 floors are used is for the JP-exclusive E-Reader and Default modes
|
2019-11-13 22:10:05 +01:00
|
|
|
static void GetAllFloorsUsed(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2019-01-13 13:15:23 +01:00
|
|
|
SetUpDataStruct();
|
2022-03-04 17:02:19 +01:00
|
|
|
if (sHillData->challenge.numFloors != NUM_TRAINER_HILL_FLOORS)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2022-03-04 17:02:19 +01:00
|
|
|
ConvertIntToDecimalStringN(gStringVar1, sHillData->challenge.numFloors, STR_CONV_MODE_LEFT_ALIGN, 1);
|
2019-11-13 22:10:05 +01:00
|
|
|
gSpecialVar_Result = FALSE;
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2019-11-13 22:10:05 +01:00
|
|
|
gSpecialVar_Result = TRUE;
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
2019-01-13 13:15:23 +01:00
|
|
|
FreeDataStruct();
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
2019-11-13 22:10:05 +01:00
|
|
|
// May have been dummied. Every time this is called a conditional for var result occurs afterwards
|
2020-06-24 22:27:00 +02:00
|
|
|
// Relation to E-Reader is an assumption, most dummied Trainer Hill code seems to be JP E-Reader mode related
|
|
|
|
static void GetInEReaderMode(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2019-01-13 13:15:23 +01:00
|
|
|
SetUpDataStruct();
|
2020-06-24 22:27:00 +02:00
|
|
|
gSpecialVar_Result = FALSE;
|
2019-01-13 13:15:23 +01:00
|
|
|
FreeDataStruct();
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
2019-10-07 02:04:30 +02:00
|
|
|
bool8 InTrainerHillChallenge(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2019-02-28 04:16:01 +01:00
|
|
|
if (VarGet(VAR_TRAINER_HILL_IS_ACTIVE) == 0)
|
2019-01-13 12:12:27 +01:00
|
|
|
return FALSE;
|
2019-11-13 22:10:05 +01:00
|
|
|
else if (gSaveBlock1Ptr->trainerHill.spokeToOwner)
|
2019-01-13 12:12:27 +01:00
|
|
|
return FALSE;
|
|
|
|
else if (GetCurrentTrainerHillMapId() != 0)
|
|
|
|
return TRUE;
|
|
|
|
else
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2019-11-13 22:10:05 +01:00
|
|
|
static void IsTrainerHillChallengeActive(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2019-10-07 02:04:30 +02:00
|
|
|
if (!InTrainerHillChallenge())
|
2019-11-13 22:10:05 +01:00
|
|
|
gSpecialVar_Result = FALSE;
|
2019-01-13 12:12:27 +01:00
|
|
|
else
|
2019-11-13 22:10:05 +01:00
|
|
|
gSpecialVar_Result = TRUE;
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
2021-03-21 04:47:08 +01:00
|
|
|
static void TrainerHillDummy_Unused(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-03-21 04:47:08 +01:00
|
|
|
static void TrainerHillDummy(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void PrintOnTrainerHillRecordsWindow(void)
|
|
|
|
{
|
|
|
|
s32 i, x, y;
|
|
|
|
u32 total, minutes, secondsWhole, secondsFraction;
|
|
|
|
|
2019-01-13 13:15:23 +01:00
|
|
|
SetUpDataStruct();
|
2019-03-02 23:25:39 +01:00
|
|
|
FillWindowPixelBuffer(0, PIXEL_FILL(0));
|
2021-10-30 22:47:37 +02:00
|
|
|
x = GetStringCenterAlignXOffset(FONT_NORMAL, gText_TimeBoard, 0xD0);
|
2021-11-03 23:29:18 +01:00
|
|
|
AddTextPrinterParameterized3(0, FONT_NORMAL, x, 2, sRecordWinColors, TEXT_SKIP_DRAW, gText_TimeBoard);
|
2019-01-13 12:12:27 +01:00
|
|
|
|
|
|
|
y = 18;
|
2022-03-04 17:02:19 +01:00
|
|
|
for (i = 0; i < NUM_TRAINER_HILL_MODES; i++)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2022-03-04 17:02:19 +01:00
|
|
|
AddTextPrinterParameterized3(0, FONT_NORMAL, 0, y, sRecordWinColors, TEXT_SKIP_DRAW, sModeStrings[i]);
|
2019-01-13 12:12:27 +01:00
|
|
|
y += 15;
|
2019-01-13 13:15:23 +01:00
|
|
|
total = GetTimerValue(&gSaveBlock1Ptr->trainerHillTimes[i]);
|
2019-01-13 12:12:27 +01:00
|
|
|
minutes = total / (60 * 60);
|
|
|
|
total %= (60 * 60);
|
|
|
|
ConvertIntToDecimalStringN(gStringVar1, minutes, STR_CONV_MODE_RIGHT_ALIGN, 2);
|
|
|
|
secondsWhole = total / 60;
|
|
|
|
total %= 60;
|
|
|
|
ConvertIntToDecimalStringN(gStringVar2, secondsWhole, STR_CONV_MODE_RIGHT_ALIGN, 2);
|
|
|
|
secondsFraction = (total * 168) / 100;
|
|
|
|
ConvertIntToDecimalStringN(gStringVar3, secondsFraction, STR_CONV_MODE_LEADING_ZEROS, 2);
|
|
|
|
StringExpandPlaceholders(StringCopy(gStringVar4, gText_TimeCleared), gText_XMinYDotZSec);
|
2021-10-30 22:47:37 +02:00
|
|
|
x = GetStringRightAlignXOffset(FONT_NORMAL, gStringVar4, 0xD0);
|
2021-11-03 23:29:18 +01:00
|
|
|
AddTextPrinterParameterized3(0, FONT_NORMAL, x, y, sRecordWinColors, TEXT_SKIP_DRAW, gStringVar4);
|
2019-01-13 12:12:27 +01:00
|
|
|
y += 17;
|
|
|
|
}
|
|
|
|
|
|
|
|
PutWindowTilemap(0);
|
2021-11-03 20:29:18 +01:00
|
|
|
CopyWindowToVram(0, COPYWIN_FULL);
|
2019-01-13 13:15:23 +01:00
|
|
|
FreeDataStruct();
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
2019-01-13 13:15:23 +01:00
|
|
|
// Leftover from Fire Red / Leaf Green as in these games,
|
|
|
|
// the timer had to be xored by the encryption key in Sav2.
|
2019-01-13 20:50:08 +01:00
|
|
|
static u32 GetTimerValue(u32 *src)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
|
|
|
return *src;
|
|
|
|
}
|
|
|
|
|
2019-01-13 20:50:08 +01:00
|
|
|
static void SetTimerValue(u32 *dst, u32 val)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
|
|
|
*dst = val;
|
|
|
|
}
|
|
|
|
|
2019-11-21 04:55:44 +01:00
|
|
|
void LoadTrainerHillObjectEventTemplates(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2019-01-13 13:15:23 +01:00
|
|
|
u8 i, floorId;
|
2019-11-21 04:55:44 +01:00
|
|
|
struct ObjectEventTemplate *eventTemplates = gSaveBlock1Ptr->objectEventTemplates;
|
2019-01-13 12:12:27 +01:00
|
|
|
|
2019-11-21 04:55:44 +01:00
|
|
|
if (!LoadTrainerHillFloorObjectEventScripts())
|
2019-01-13 12:12:27 +01:00
|
|
|
return;
|
|
|
|
|
2019-01-13 13:15:23 +01:00
|
|
|
SetUpDataStruct();
|
2022-03-04 17:02:19 +01:00
|
|
|
for (i = 0; i < HILL_TRAINERS_PER_FLOOR; i++)
|
2019-02-07 18:37:28 +01:00
|
|
|
gSaveBlock2Ptr->frontier.trainerIds[i] = 0xFFFF;
|
2019-11-21 04:55:44 +01:00
|
|
|
CpuFill32(0, gSaveBlock1Ptr->objectEventTemplates, sizeof(gSaveBlock1Ptr->objectEventTemplates));
|
2019-01-13 12:12:27 +01:00
|
|
|
|
2019-01-13 13:15:23 +01:00
|
|
|
floorId = GetFloorId();
|
2022-03-04 17:02:19 +01:00
|
|
|
for (i = 0; i < HILL_TRAINERS_PER_FLOOR; i++)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
|
|
|
u8 bits;
|
|
|
|
|
2019-11-21 04:55:44 +01:00
|
|
|
eventTemplates[i] = sTrainerObjectEventTemplate;
|
2019-01-13 12:12:27 +01:00
|
|
|
eventTemplates[i].localId = i + 1;
|
2019-09-04 21:42:53 +02:00
|
|
|
eventTemplates[i].graphicsId = FacilityClassToGraphicsId(sHillData->floors[floorId].trainers[i].facilityClass);
|
2022-03-04 17:02:19 +01:00
|
|
|
eventTemplates[i].x = sHillData->floors[floorId].map.trainerCoords[i] & 0xF;
|
|
|
|
eventTemplates[i].y = ((sHillData->floors[floorId].map.trainerCoords[i] >> 4) & 0xF) + 5;
|
2019-01-13 12:12:27 +01:00
|
|
|
bits = i << 2;
|
2022-03-04 17:02:19 +01:00
|
|
|
eventTemplates[i].movementType = ((sHillData->floors[floorId].map.trainerDirections >> bits) & 0xF) + MOVEMENT_TYPE_FACE_UP;
|
|
|
|
eventTemplates[i].trainerRange_berryTreeId = (sHillData->floors[floorId].map.trainerRanges >> bits) & 0xF;
|
2019-11-13 22:10:05 +01:00
|
|
|
eventTemplates[i].script = TrainerHill_EventScript_TrainerBattle;
|
2019-02-07 18:37:28 +01:00
|
|
|
gSaveBlock2Ptr->frontier.trainerIds[i] = i + 1;
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
2019-01-13 13:15:23 +01:00
|
|
|
FreeDataStruct();
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
2019-11-21 04:55:44 +01:00
|
|
|
bool32 LoadTrainerHillFloorObjectEventScripts(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2019-01-13 13:15:23 +01:00
|
|
|
SetUpDataStruct();
|
2019-11-13 22:10:05 +01:00
|
|
|
// Something may have been dummied here
|
2019-01-13 13:15:23 +01:00
|
|
|
FreeDataStruct();
|
2019-01-13 12:12:27 +01:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2022-03-04 17:02:19 +01:00
|
|
|
static u16 GetMetatileForFloor(u8 floorId, u32 x, u32 y, u32 floorWidth) // floorWidth is always 16
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2020-09-20 07:50:54 +02:00
|
|
|
bool8 impassable;
|
|
|
|
u16 metatile;
|
|
|
|
u16 elevation;
|
2019-01-13 12:12:27 +01:00
|
|
|
|
2022-03-04 17:02:19 +01:00
|
|
|
impassable = (sHillData->floors[floorId].map.collisionData[y] >> (15 - x) & 1);
|
|
|
|
metatile = sHillData->floors[floorId].map.metatileData[floorWidth * y + x] + NUM_METATILES_IN_PRIMARY;
|
2022-01-19 16:15:32 +01:00
|
|
|
elevation = 3 << MAPGRID_ELEVATION_SHIFT;
|
2019-01-13 12:12:27 +01:00
|
|
|
|
2022-01-19 16:15:32 +01:00
|
|
|
return ((impassable << MAPGRID_COLLISION_SHIFT) & MAPGRID_COLLISION_MASK) | elevation | (metatile & MAPGRID_METATILE_ID_MASK);
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
2019-11-13 22:10:05 +01:00
|
|
|
void GenerateTrainerHillFloorLayout(u16 *mapArg)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2022-03-04 17:02:19 +01:00
|
|
|
s32 y, x;
|
2019-01-13 12:12:27 +01:00
|
|
|
u16 *src, *dst;
|
2019-01-13 13:15:23 +01:00
|
|
|
u8 mapId = GetCurrentTrainerHillMapId();
|
2019-01-13 12:12:27 +01:00
|
|
|
|
2019-11-13 22:10:05 +01:00
|
|
|
if (mapId == TRAINER_HILL_ENTRANCE)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
|
|
|
InitMapFromSavedGame();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-01-13 13:15:23 +01:00
|
|
|
SetUpDataStruct();
|
2019-11-13 22:10:05 +01:00
|
|
|
if (mapId == TRAINER_HILL_ROOF)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
|
|
|
InitMapFromSavedGame();
|
2019-01-13 13:15:23 +01:00
|
|
|
FreeDataStruct();
|
2019-01-13 12:12:27 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-01-13 13:15:23 +01:00
|
|
|
mapId = GetFloorId();
|
2019-01-13 12:12:27 +01:00
|
|
|
src = gMapHeader.mapLayout->map;
|
|
|
|
gBackupMapLayout.map = mapArg;
|
2022-03-04 17:02:19 +01:00
|
|
|
// Dimensions include border area loaded beyond map
|
|
|
|
gBackupMapLayout.width = HILL_FLOOR_WIDTH + 15;
|
|
|
|
gBackupMapLayout.height = HILL_FLOOR_HEIGHT + 14;
|
2019-01-13 12:12:27 +01:00
|
|
|
dst = mapArg + 224;
|
2021-04-29 22:08:43 +02:00
|
|
|
|
|
|
|
// First 5 rows of the map (Entrance / Exit) are always the same
|
2022-03-04 17:02:19 +01:00
|
|
|
for (y = 0; y < HILL_FLOOR_HEIGHT_MARGIN; y++)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2022-03-04 17:02:19 +01:00
|
|
|
for (x = 0; x < HILL_FLOOR_WIDTH; x++)
|
|
|
|
dst[x] = src[x];
|
2019-01-13 12:12:27 +01:00
|
|
|
dst += 31;
|
|
|
|
src += 16;
|
|
|
|
}
|
|
|
|
|
2021-04-29 22:08:43 +02:00
|
|
|
// Load the 16x16 floor-specific layout
|
2022-03-04 17:02:19 +01:00
|
|
|
for (y = 0; y < HILL_FLOOR_HEIGHT_MAIN; y++)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2022-03-04 17:02:19 +01:00
|
|
|
for (x = 0; x < HILL_FLOOR_WIDTH; x++)
|
|
|
|
dst[x] = GetMetatileForFloor(mapId, x, y, HILL_FLOOR_WIDTH);
|
2019-01-13 12:12:27 +01:00
|
|
|
dst += 31;
|
|
|
|
}
|
|
|
|
|
2019-02-22 08:23:46 +01:00
|
|
|
RunOnLoadMapScript();
|
2019-01-13 13:15:23 +01:00
|
|
|
FreeDataStruct();
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool32 InTrainerHill(void)
|
|
|
|
{
|
|
|
|
bool32 ret;
|
|
|
|
|
2019-01-31 22:51:20 +01:00
|
|
|
if (gMapHeader.mapLayoutId == LAYOUT_TRAINER_HILL_1F
|
2019-11-13 22:10:05 +01:00
|
|
|
|| gMapHeader.mapLayoutId == LAYOUT_TRAINER_HILL_2F
|
|
|
|
|| gMapHeader.mapLayoutId == LAYOUT_TRAINER_HILL_3F
|
|
|
|
|| gMapHeader.mapLayoutId == LAYOUT_TRAINER_HILL_4F)
|
2019-01-13 12:12:27 +01:00
|
|
|
ret = TRUE;
|
|
|
|
else
|
|
|
|
ret = FALSE;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
u8 GetCurrentTrainerHillMapId(void)
|
|
|
|
{
|
2019-11-13 22:10:05 +01:00
|
|
|
u8 mapId;
|
2019-01-13 12:12:27 +01:00
|
|
|
|
2019-01-31 22:51:20 +01:00
|
|
|
if (gMapHeader.mapLayoutId == LAYOUT_TRAINER_HILL_1F)
|
2019-11-13 22:10:05 +01:00
|
|
|
mapId = TRAINER_HILL_1F;
|
2019-01-31 22:51:20 +01:00
|
|
|
else if (gMapHeader.mapLayoutId == LAYOUT_TRAINER_HILL_2F)
|
2019-11-13 22:10:05 +01:00
|
|
|
mapId = TRAINER_HILL_2F;
|
2019-01-31 22:51:20 +01:00
|
|
|
else if (gMapHeader.mapLayoutId == LAYOUT_TRAINER_HILL_3F)
|
2019-11-13 22:10:05 +01:00
|
|
|
mapId = TRAINER_HILL_3F;
|
2019-01-31 22:51:20 +01:00
|
|
|
else if (gMapHeader.mapLayoutId == LAYOUT_TRAINER_HILL_4F)
|
2019-11-13 22:10:05 +01:00
|
|
|
mapId = TRAINER_HILL_4F;
|
2019-01-31 22:51:20 +01:00
|
|
|
else if (gMapHeader.mapLayoutId == LAYOUT_TRAINER_HILL_ROOF)
|
2019-11-13 22:10:05 +01:00
|
|
|
mapId = TRAINER_HILL_ROOF;
|
2019-01-31 22:51:20 +01:00
|
|
|
else if (gMapHeader.mapLayoutId == LAYOUT_TRAINER_HILL_ENTRANCE)
|
2019-11-13 22:10:05 +01:00
|
|
|
mapId = TRAINER_HILL_ENTRANCE;
|
2019-01-13 12:12:27 +01:00
|
|
|
else
|
2019-11-13 22:10:05 +01:00
|
|
|
mapId = 0;
|
2019-01-13 12:12:27 +01:00
|
|
|
|
2019-11-13 22:10:05 +01:00
|
|
|
return mapId;
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
2019-11-13 22:10:05 +01:00
|
|
|
// Unused
|
|
|
|
static bool32 OnTrainerHillRoof(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2019-11-13 22:10:05 +01:00
|
|
|
bool32 onRoof;
|
2019-01-13 12:12:27 +01:00
|
|
|
|
2019-01-31 22:51:20 +01:00
|
|
|
if (gMapHeader.mapLayoutId == LAYOUT_TRAINER_HILL_ROOF)
|
2019-11-13 22:10:05 +01:00
|
|
|
onRoof = TRUE;
|
2019-01-13 12:12:27 +01:00
|
|
|
else
|
2019-11-13 22:10:05 +01:00
|
|
|
onRoof = FALSE;
|
2019-01-13 12:12:27 +01:00
|
|
|
|
2019-11-13 22:10:05 +01:00
|
|
|
return onRoof;
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
2019-11-13 22:10:05 +01:00
|
|
|
const struct WarpEvent* SetWarpDestinationTrainerHill4F(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
|
|
|
const struct MapHeader *header = Overworld_GetMapHeaderByGroupAndId(MAP_GROUP(TRAINER_HILL_4F), MAP_NUM(TRAINER_HILL_4F));
|
|
|
|
|
|
|
|
return &header->events->warps[1];
|
|
|
|
}
|
|
|
|
|
2019-11-13 22:10:05 +01:00
|
|
|
// For warping from the roof in challenges where the 4F is not the final challenge floor
|
2019-11-13 22:31:01 +01:00
|
|
|
// This would only occur in the JP-exclusive Default and E-Reader challenges
|
2019-11-13 22:10:05 +01:00
|
|
|
const struct WarpEvent* SetWarpDestinationTrainerHillFinalFloor(u8 warpEventId)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2019-11-13 22:10:05 +01:00
|
|
|
u8 numFloors;
|
2019-01-13 12:12:27 +01:00
|
|
|
const struct MapHeader *header;
|
|
|
|
|
|
|
|
if (warpEventId == 1)
|
|
|
|
return &gMapHeader.events->warps[1];
|
|
|
|
|
2019-11-13 22:10:05 +01:00
|
|
|
numFloors = GetNumFloorsInTrainerHillChallenge();
|
|
|
|
if (numFloors == 0 || numFloors > NUM_TRAINER_HILL_FLOORS)
|
|
|
|
numFloors = NUM_TRAINER_HILL_FLOORS;
|
2019-01-13 12:12:27 +01:00
|
|
|
|
2019-11-13 22:10:05 +01:00
|
|
|
header = Overworld_GetMapHeaderByGroupAndId(MAP_GROUP(TRAINER_HILL_4F), sNextFloorMapNum[numFloors - 1]);
|
2019-01-13 12:12:27 +01:00
|
|
|
return &header->events->warps[0];
|
|
|
|
}
|
|
|
|
|
2019-01-13 20:50:08 +01:00
|
|
|
u16 LocalIdToHillTrainerId(u8 localId)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2019-02-07 18:37:28 +01:00
|
|
|
return gSaveBlock2Ptr->frontier.trainerIds[localId - 1];
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
2019-11-21 04:55:44 +01:00
|
|
|
bool8 GetHillTrainerFlag(u8 objectEventId)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2022-03-04 17:02:19 +01:00
|
|
|
u32 trainerIndexStart = GetFloorId() * HILL_TRAINERS_PER_FLOOR;
|
|
|
|
u8 bitId = gObjectEvents[objectEventId].localId - 1 + trainerIndexStart;
|
2019-01-13 12:12:27 +01:00
|
|
|
|
2019-11-13 22:10:05 +01:00
|
|
|
return gSaveBlock2Ptr->frontier.trainerFlags & gBitTable[bitId];
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
2019-01-13 20:50:08 +01:00
|
|
|
void SetHillTrainerFlag(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
|
|
|
u8 i;
|
2022-03-04 17:02:19 +01:00
|
|
|
u8 trainerIndexStart = GetFloorId() * HILL_TRAINERS_PER_FLOOR;
|
2019-01-13 12:12:27 +01:00
|
|
|
|
2022-03-04 17:02:19 +01:00
|
|
|
for (i = 0; i < HILL_TRAINERS_PER_FLOOR; i++)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2019-02-07 18:37:28 +01:00
|
|
|
if (gSaveBlock2Ptr->frontier.trainerIds[i] == gTrainerBattleOpponent_A)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2022-03-04 17:02:19 +01:00
|
|
|
gSaveBlock2Ptr->frontier.trainerFlags |= gBitTable[trainerIndexStart + i];
|
2019-01-13 12:12:27 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS)
|
|
|
|
{
|
2022-03-04 17:02:19 +01:00
|
|
|
for (i = 0; i < HILL_TRAINERS_PER_FLOOR; i++)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2019-02-07 18:37:28 +01:00
|
|
|
if (gSaveBlock2Ptr->frontier.trainerIds[i] == gTrainerBattleOpponent_B)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2022-03-04 17:02:19 +01:00
|
|
|
gSaveBlock2Ptr->frontier.trainerFlags |= gBitTable[trainerIndexStart + i];
|
2019-01-13 12:12:27 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-04 21:35:35 +02:00
|
|
|
const u8 *GetTrainerHillTrainerScript(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2019-11-13 22:10:05 +01:00
|
|
|
return TrainerHill_EventScript_TrainerBattle;
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
2019-11-13 22:10:05 +01:00
|
|
|
static void ShowTrainerHillPostBattleText(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2019-11-13 22:10:05 +01:00
|
|
|
CopyTrainerHillTrainerText(TRAINER_HILL_TEXT_AFTER, gSpecialVar_LastTalked);
|
2020-06-04 00:25:16 +02:00
|
|
|
ShowFieldMessageFromBuffer();
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
2019-11-13 22:10:05 +01:00
|
|
|
static void CreateNPCTrainerHillParty(u16 trainerId, u8 firstMonId)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
|
|
|
u8 trId, level;
|
2019-11-13 22:10:05 +01:00
|
|
|
s32 i, floorId, partySlot;
|
2019-01-13 12:12:27 +01:00
|
|
|
|
2022-03-04 17:02:19 +01:00
|
|
|
if (trainerId == 0 || trainerId > HILL_TRAINERS_PER_FLOOR)
|
2019-01-13 12:12:27 +01:00
|
|
|
return;
|
|
|
|
|
|
|
|
trId = trainerId - 1;
|
2019-01-13 13:15:23 +01:00
|
|
|
SetUpDataStruct();
|
2019-01-13 12:12:27 +01:00
|
|
|
level = GetHighestLevelInPlayerParty();
|
2019-01-13 13:15:23 +01:00
|
|
|
floorId = GetFloorId();
|
2022-03-04 17:02:19 +01:00
|
|
|
for (i = firstMonId, partySlot = 0; i < firstMonId + PARTY_SIZE / 2; i++, partySlot++)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2019-11-13 22:10:05 +01:00
|
|
|
u8 id = sTrainerPartySlots[trId][partySlot];
|
2019-01-13 12:12:27 +01:00
|
|
|
struct Pokemon *mon = &gEnemyParty[i];
|
|
|
|
|
2019-09-04 21:42:53 +02:00
|
|
|
CreateBattleTowerMon(mon, &sHillData->floors[floorId].trainers[trId].mons[id]);
|
2019-11-13 22:10:05 +01:00
|
|
|
SetTrainerHillMonLevel(mon, level);
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
2019-01-13 13:15:23 +01:00
|
|
|
FreeDataStruct();
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
2019-01-13 20:50:08 +01:00
|
|
|
void FillHillTrainerParty(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
|
|
|
ZeroEnemyPartyMons();
|
2019-11-13 22:10:05 +01:00
|
|
|
CreateNPCTrainerHillParty(gTrainerBattleOpponent_A, 0);
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
2019-01-13 20:50:08 +01:00
|
|
|
void FillHillTrainersParties(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
|
|
|
ZeroEnemyPartyMons();
|
2019-11-13 22:10:05 +01:00
|
|
|
CreateNPCTrainerHillParty(gTrainerBattleOpponent_A, 0);
|
2022-03-04 17:02:19 +01:00
|
|
|
CreateNPCTrainerHillParty(gTrainerBattleOpponent_B, PARTY_SIZE / 2);
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// This function is unused, but my best guess is
|
|
|
|
// it was supposed to return AI scripts for trainer
|
|
|
|
// hill trainers.
|
2019-11-13 22:10:05 +01:00
|
|
|
u32 GetTrainerHillAIFlags(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2019-11-13 22:10:05 +01:00
|
|
|
return (AI_SCRIPT_CHECK_BAD_MOVE | AI_SCRIPT_TRY_TO_FAINT | AI_SCRIPT_CHECK_VIABILITY);
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
2019-01-13 20:50:08 +01:00
|
|
|
u8 GetTrainerEncounterMusicIdInTrainerHill(u16 trainerId)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
|
|
|
s32 i;
|
|
|
|
u8 trId, facilityClass;
|
|
|
|
|
2019-01-13 13:15:23 +01:00
|
|
|
SetUpDataStruct();
|
2019-01-13 12:12:27 +01:00
|
|
|
trId = trainerId - 1;
|
2019-09-04 21:42:53 +02:00
|
|
|
facilityClass = sHillData->floors[sHillData->floorId].trainers[trId].facilityClass;
|
2019-01-13 13:15:23 +01:00
|
|
|
FreeDataStruct();
|
2019-01-13 12:12:27 +01:00
|
|
|
|
2019-11-13 22:10:05 +01:00
|
|
|
for (i = 0; i < ARRAY_COUNT(sTrainerClassesAndMusic); i++)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2019-11-13 22:10:05 +01:00
|
|
|
if (sTrainerClassesAndMusic[i].trainerClass == gFacilityClassToTrainerClass[facilityClass])
|
|
|
|
return sTrainerClassesAndMusic[i].musicId;
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-11-13 22:10:05 +01:00
|
|
|
static void SetTrainerHillMonLevel(struct Pokemon *mon, u8 level)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
|
|
|
u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL);
|
|
|
|
u32 exp = gExperienceTables[gBaseStats[species].growthRate][level];
|
|
|
|
|
|
|
|
SetMonData(mon, MON_DATA_EXP, &exp);
|
|
|
|
SetMonData(mon, MON_DATA_LEVEL, &level);
|
|
|
|
CalculateMonStats(mon);
|
|
|
|
}
|
|
|
|
|
2019-11-13 22:10:05 +01:00
|
|
|
u8 GetNumFloorsInTrainerHillChallenge(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2019-11-13 22:10:05 +01:00
|
|
|
u8 floors;
|
2019-01-13 12:12:27 +01:00
|
|
|
|
2019-01-13 13:15:23 +01:00
|
|
|
SetUpDataStruct();
|
2022-03-04 17:02:19 +01:00
|
|
|
floors = sHillData->challenge.numFloors;
|
2019-01-13 13:15:23 +01:00
|
|
|
FreeDataStruct();
|
2019-01-13 12:12:27 +01:00
|
|
|
|
2019-11-13 22:10:05 +01:00
|
|
|
return floors;
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
2019-11-13 22:10:05 +01:00
|
|
|
static void SetAllTrainerFlags(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2019-11-13 22:10:05 +01:00
|
|
|
gSaveBlock2Ptr->frontier.trainerFlags = 0xFF;
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
2020-06-24 22:27:00 +02:00
|
|
|
// Palette never loaded, OnTrainerHillEReaderChallengeFloor always FALSE
|
|
|
|
void TryLoadTrainerHillEReaderPalette(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2020-06-24 22:27:00 +02:00
|
|
|
if (OnTrainerHillEReaderChallengeFloor() == TRUE)
|
|
|
|
LoadPalette(sEReader_Pal, 0x70, 0x20);
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
2019-11-13 22:10:05 +01:00
|
|
|
static void GetGameSaved(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2019-11-13 22:10:05 +01:00
|
|
|
gSpecialVar_Result = gSaveBlock2Ptr->frontier.savedGame;
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
2019-11-13 22:10:05 +01:00
|
|
|
static void SetGameSaved(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2019-11-13 22:10:05 +01:00
|
|
|
gSaveBlock2Ptr->frontier.savedGame = TRUE;
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
2019-11-13 22:10:05 +01:00
|
|
|
static void ClearGameSaved(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2019-11-13 22:10:05 +01:00
|
|
|
gSaveBlock2Ptr->frontier.savedGame = FALSE;
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
2019-11-13 22:10:05 +01:00
|
|
|
// Always FALSE
|
2020-06-24 22:27:00 +02:00
|
|
|
bool32 OnTrainerHillEReaderChallengeFloor(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2019-11-13 22:10:05 +01:00
|
|
|
if (!InTrainerHillChallenge() || GetCurrentTrainerHillMapId() == TRAINER_HILL_ENTRANCE)
|
2019-01-13 12:12:27 +01:00
|
|
|
return FALSE;
|
|
|
|
|
2020-06-24 22:27:00 +02:00
|
|
|
GetInEReaderMode();
|
|
|
|
if (gSpecialVar_Result == FALSE)
|
2019-01-13 12:12:27 +01:00
|
|
|
return FALSE;
|
|
|
|
else
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2019-11-13 22:10:05 +01:00
|
|
|
static void GetChallengeWon(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2019-02-28 03:28:34 +01:00
|
|
|
if (gSaveBlock1Ptr->trainerHill.hasLost)
|
2019-11-13 22:10:05 +01:00
|
|
|
gSpecialVar_Result = FALSE;
|
2019-01-13 12:12:27 +01:00
|
|
|
else
|
2019-11-13 22:10:05 +01:00
|
|
|
gSpecialVar_Result = TRUE;
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
2022-03-04 17:02:19 +01:00
|
|
|
static void TrainerHillSetMode(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2022-03-04 17:02:19 +01:00
|
|
|
gSaveBlock1Ptr->trainerHill.mode = gSpecialVar_0x8005;
|
2019-02-28 04:16:01 +01:00
|
|
|
gSaveBlock1Ptr->trainerHill.bestTime = gSaveBlock1Ptr->trainerHillTimes[gSpecialVar_0x8005];
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
2022-04-01 07:21:00 +02:00
|
|
|
// Determines which prize list to use from the set of prize lists.
|
|
|
|
static u8 GetPrizeListId(bool8 allowTMs)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2019-11-13 22:10:05 +01:00
|
|
|
u8 prizeListId, i, modBy;
|
2019-01-13 12:12:27 +01:00
|
|
|
|
2022-04-01 07:21:00 +02:00
|
|
|
// The initial selection depends on the trainer numbers for the completed challenge.
|
|
|
|
// These don't change with the available challenge modes, so Normal/Unique will always
|
|
|
|
// have a prizeListId of 8, and Variety/Expert will have a prizeListId of 24.
|
2019-11-13 22:10:05 +01:00
|
|
|
prizeListId = 0;
|
|
|
|
for (i = 0; i < NUM_TRAINER_HILL_FLOORS; i++)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2019-11-13 22:10:05 +01:00
|
|
|
prizeListId ^= sHillData->floors[i].trainerNum1 & 0x1F;
|
|
|
|
prizeListId ^= sHillData->floors[i].trainerNum2 & 0x1F;
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
2022-04-01 07:21:00 +02:00
|
|
|
// In practice, the conditional below is always true.
|
|
|
|
// The 2nd half of the lists in both sets of lists all have a TM as the "grand prize", while the 1st half do not,
|
|
|
|
// so taking the mod of the (total / 2) ensures that a prize list without a TM will be used.
|
|
|
|
if (allowTMs)
|
2019-11-13 22:10:05 +01:00
|
|
|
modBy = NUM_TRAINER_HILL_PRIZE_LISTS;
|
2019-01-13 12:12:27 +01:00
|
|
|
else
|
2019-11-13 22:10:05 +01:00
|
|
|
modBy = NUM_TRAINER_HILL_PRIZE_LISTS / 2;
|
2019-01-13 12:12:27 +01:00
|
|
|
|
2019-11-13 22:10:05 +01:00
|
|
|
prizeListId %= modBy;
|
|
|
|
return prizeListId;
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
|
|
|
|
2019-11-13 22:10:05 +01:00
|
|
|
static u16 GetPrizeItemId(void)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
|
|
|
u8 i;
|
2019-11-13 22:10:05 +01:00
|
|
|
const u16 *prizeList;
|
2022-04-01 07:21:00 +02:00
|
|
|
s32 trainerNumSum = 0, prizeListSetId, minutes, id;
|
2019-01-13 12:12:27 +01:00
|
|
|
|
2022-04-01 07:21:00 +02:00
|
|
|
// First determine which set of prize lists to use. The sets of lists only differ in
|
|
|
|
// what TMs they can offer as the "grand prize" for a time under 12 minutes.
|
|
|
|
// Which set of lists gets used is based on the sum of all the trainer numbers for that
|
|
|
|
// challenge. These don't change with the available challenge modes, so Normal will always
|
|
|
|
// have a prizeListSetId of 0, and Unique/Variety/Expert will have a prizeListSetId of 1.
|
2019-11-13 22:10:05 +01:00
|
|
|
for (i = 0; i < NUM_TRAINER_HILL_FLOORS; i++)
|
2019-01-13 12:12:27 +01:00
|
|
|
{
|
2022-04-01 07:21:00 +02:00
|
|
|
trainerNumSum += sHillData->floors[i].trainerNum1;
|
|
|
|
trainerNumSum += sHillData->floors[i].trainerNum2;
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|
2022-04-01 07:21:00 +02:00
|
|
|
prizeListSetId = trainerNumSum / 256;
|
|
|
|
prizeListSetId %= (int)ARRAY_COUNT(sPrizeListSets);
|
2019-01-13 12:12:27 +01:00
|
|
|
|
2022-04-01 07:21:00 +02:00
|
|
|
// Now get which prize list to use from the set. See GetPrizeListId for details.
|
|
|
|
// The below conditional will always be true, because a Trainer Hill challenge can't be entered
|
|
|
|
// until the player has entered the Hall of Fame (FLAG_SYS_GAME_CLEAR is set) and because all
|
|
|
|
// of the available challenge modes have the full 8 trainers (NUM_TRAINER_HILL_TRAINERS).
|
2022-03-04 17:02:19 +01:00
|
|
|
if (FlagGet(FLAG_SYS_GAME_CLEAR) && sHillData->challenge.numTrainers == NUM_TRAINER_HILL_TRAINERS)
|
2019-11-13 22:10:05 +01:00
|
|
|
i = GetPrizeListId(TRUE);
|
2019-01-13 12:12:27 +01:00
|
|
|
else
|
2019-11-13 22:10:05 +01:00
|
|
|
i = GetPrizeListId(FALSE);
|
2019-01-13 12:12:27 +01:00
|
|
|
|
2022-04-01 07:21:00 +02:00
|
|
|
// 1 is added to Expert mode's prize list selection because otherwise it has the same prizes as Variety
|
2022-03-04 17:02:19 +01:00
|
|
|
if (gSaveBlock1Ptr->trainerHill.mode == HILL_MODE_EXPERT)
|
2019-11-13 22:10:05 +01:00
|
|
|
i = (i + 1) % NUM_TRAINER_HILL_PRIZE_LISTS;
|
2019-01-13 12:12:27 +01:00
|
|
|
|
2022-04-01 07:21:00 +02:00
|
|
|
// After the above (non-random) calculations, the following are the possible prize list selections:
|
|
|
|
// sPrizeListSets[0][8] (Normal)
|
|
|
|
// sPrizeListSets[1][4] (Variety)
|
|
|
|
// sPrizeListSets[1][8] (Unique)
|
|
|
|
// sPrizeListSets[1][5] (Expert)
|
2019-11-13 22:10:05 +01:00
|
|
|
prizeList = sPrizeListSets[prizeListSetId][i];
|
2022-04-01 07:21:00 +02:00
|
|
|
|
|
|
|
// Which prize is given from the list depends on the time scored.
|
|
|
|
// The prize for any time after 12 minutes is the same in every list.
|
|
|
|
// The prizes for a time under 12 minutes are:
|
|
|
|
// - ITEM_TM11_SUNNY_DAY (Normal)
|
|
|
|
// - ITEM_ELIXIR (Variety)
|
|
|
|
// - ITEM_TM19_GIGA_DRAIN (Unique)
|
|
|
|
// - ITEM_TM31_BRICK_BREAK (Expert)
|
|
|
|
// As an additional note, if players were allowed to enter a Trainer Hill challenge before
|
|
|
|
// entering the Hall of Fame, there would be 1 additional prize possibility (ITEM_MAX_ETHER)
|
|
|
|
// as Normal / Unique modes would use sPrizeListSets[0][3] / sPrizeListSets[1][3] respectively.
|
2019-02-28 03:28:34 +01:00
|
|
|
minutes = (signed)(gSaveBlock1Ptr->trainerHill.timer) / (60 * 60);
|
2019-01-13 12:12:27 +01:00
|
|
|
if (minutes < 12)
|
2022-04-01 07:21:00 +02:00
|
|
|
id = 0; // Depends on list
|
2019-01-13 12:12:27 +01:00
|
|
|
else if (minutes < 13)
|
2022-04-01 07:21:00 +02:00
|
|
|
id = 1; // ITEM_ETHER
|
2019-01-13 12:12:27 +01:00
|
|
|
else if (minutes < 14)
|
2022-04-01 07:21:00 +02:00
|
|
|
id = 2; // ITEM_MAX_POTION
|
2019-01-13 12:12:27 +01:00
|
|
|
else if (minutes < 16)
|
2022-04-01 07:21:00 +02:00
|
|
|
id = 3; // ITEM_REVIVE
|
2019-01-13 12:12:27 +01:00
|
|
|
else if (minutes < 18)
|
2022-04-01 07:21:00 +02:00
|
|
|
id = 4; // ITEM_FLUFFY_TAIL
|
2019-01-13 12:12:27 +01:00
|
|
|
else
|
2022-04-01 07:21:00 +02:00
|
|
|
id = 5; // ITEM_GREAT_BALL
|
2019-01-13 12:12:27 +01:00
|
|
|
|
2019-11-13 22:10:05 +01:00
|
|
|
return prizeList[id];
|
2019-01-13 12:12:27 +01:00
|
|
|
}
|