pokeemerald/src/trainer_hill.c

1108 lines
33 KiB
C
Raw Normal View History

2019-01-13 12:12:27 +01:00
#include "global.h"
#include "malloc.h"
2019-01-13 12:12:27 +01:00
#include "battle.h"
#include "battle_tower.h"
#include "battle_setup.h"
#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-01-13 12:12:27 +01:00
#include "constants/event_object_movement_constants.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 12:12:27 +01:00
#include "constants/maps.h"
2019-01-19 12:57:18 +01:00
#include "constants/species.h"
2019-01-13 20:50:08 +01:00
#include "constants/trainers.h"
2019-01-21 17:55:27 +01:00
#include "constants/easy_chat.h"
#include "constants/trainer_hill.h"
2019-01-13 20:50:08 +01:00
2019-01-13 13:15:23 +01:00
#define HILL_TAG_NORMAL 0
#define HILL_TAG_VARIETY 1
#define HILL_TAG_UNIQUE 2
#define HILL_TAG_EXPERT 3
2019-01-13 12:12:27 +01:00
#define HILL_MAX_TIME 215999 // 60 * 60 * 60 - 1
2019-01-13 13:15:23 +01:00
// EWRAM
struct TrHillStruct2
{
u8 floorId;
struct TrHillTag tag;
2019-11-13 22:10:05 +01:00
struct TrHillFloor floors[NUM_TRAINER_HILL_FLOORS];
};
2019-01-13 13:15:23 +01:00
static EWRAM_DATA struct TrHillStruct2 *sHillData = NULL;
static EWRAM_DATA struct TrHillRoomTrainers *sRoomTrainers = 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.
static void TrainerHillStartChallenge(void);
2019-11-13 22:10:05 +01:00
static void GetOwnerState(void);
static void GiveChallengePrize(void);
static void CheckFinalTime(void);
static void TrainerHillResumeTimer(void);
static void TrainerHillSetPlayerLost(void);
static void TrainerHillGetChallengeStatus(void);
2019-11-13 22:10:05 +01:00
static void BufferChallengeTime(void);
static void GetAllFloorsUsed(void);
static void ClearVarResult(void);
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);
static void TrainerHillSetTag(void);
2019-01-13 20:50:08 +01:00
static void SetUpDataStruct(void);
static void FreeDataStruct(void);
static void nullsub_2(void);
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},
{TRAINER_CLASS_PKMN_TRAINER_3, TRAINER_ENCOUNTER_MUSIC_MALE},
{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};
static const u16 sPrizeListGigaSwap[] = {ITEM_TM19_GIGA_DRAIN, ITEM_ETHER, ITEM_MAX_POTION, ITEM_REVIVE, ITEM_FLUFFY_TAIL, ITEM_GREAT_BALL};
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,
sPrizeListGigaSwap,
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
2019-11-13 22:10:05 +01:00
static const u16 sUnknown_0862A5D4[] = INCBIN_U16("graphics/pokenav/862A5D4.gbapal");
static const u8 sRecordWinColors[] = {TEXT_COLOR_TRANSPARENT, TEXT_COLOR_DARK_GREY, TEXT_COLOR_LIGHT_GREY};
2019-01-13 13:15:23 +01:00
static const struct TrHillTag *const sDataPerTag[] =
{
2019-01-19 12:57:18 +01:00
&sDataTagNormal,
&sDataTagVariety,
&sDataTagUnique,
&sDataTagExpert,
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-13 22:10:05 +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,
[TRAINER_HILL_FUNC_CLEAR_RESULT] = ClearVarResult,
[TRAINER_HILL_FUNC_IN_CHALLENGE] = IsTrainerHillChallengeActive,
[TRAINER_HILL_FUNC_POST_BATTLE_TEXT] = ShowTrainerHillPostBattleText,
[TRAINER_HILL_FUNC_SET_ALL_TRAINER_FLAGS] = SetAllTrainerFlags,
[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,
[TRAINER_HILL_FUNC_SET_TAG] = TrainerHillSetTag,
2019-01-13 13:15:23 +01:00
};
static const u8 *const sTagMatchStrings[] =
{
gText_NormalTagMatch,
gText_VarietyTagMatch,
gText_UniqueTagMatch,
gText_ExpertTagMatch,
};
2019-11-13 22:10:05 +01:00
static const struct EventObjectTemplate sTrainerEventObjectTemplate =
2019-01-13 13:15:23 +01:00
{
.graphicsId = EVENT_OBJ_GFX_RIVAL_BRENDAN_NORMAL,
.elevation = 3,
.movementType = MOVEMENT_TYPE_LOOK_AROUND,
.movementRangeX = 1,
.movementRangeY = 1,
.trainerType = 1,
};
2019-11-13 22:10:05 +01:00
static const u32 sNextFloorMapNum[NUM_TRAINER_HILL_FLOORS] =
{
MAP_NUM(TRAINER_HILL_2F),
MAP_NUM(TRAINER_HILL_3F),
MAP_NUM(TRAINER_HILL_4F),
MAP_NUM(TRAINER_HILL_ROOF)
};
static const u8 sTrainerPartySlots[][PARTY_SIZE / 2] =
{
{0, 1, 2},
{3, 4, 5}
};
2019-01-13 13:15:23 +01:00
2019-01-13 20:50:08 +01:00
// code
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:10:05 +01:00
gSaveBlock2Ptr->frontier.savedGame = FALSE;
gSaveBlock2Ptr->frontier.neverRead = 0;
gSaveBlock1Ptr->trainerHill.bestTime = 0;
2019-01-13 12:12:27 +01:00
for (i = 0; i < 4; 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;
2019-01-13 13:15:23 +01:00
return gFacilityClassToTrainerClass[sRoomTrainers->facilityClass[id]];
2019-01-13 12:12:27 +01:00
}
void GetTrainerHillTrainerName(u8 *dst, u16 trainerId)
{
s32 i;
u8 id = trainerId - 1;
for (i = 0; i < HILL_TRAINER_NAME_LENGTH; i++)
2019-01-13 13:15:23 +01:00
dst[i] = sRoomTrainers->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;
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();
sRoomTrainers = AllocZeroed(sizeof(*sRoomTrainers));
2019-01-13 12:12:27 +01:00
for (i = 0; i < 2; i++)
{
for (j = 0; j < HILL_TRAINER_NAME_LENGTH; j++)
{
sRoomTrainers->name[i][j] = sHillData->floors[sHillData->floorId].trainers[i].name[j];
2019-01-13 12:12:27 +01:00
}
sRoomTrainers->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
{
2019-01-13 13:15:23 +01:00
if (sRoomTrainers != NULL)
FREE_AND_SET_NULL(sRoomTrainers);
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
{
2019-01-13 13:15:23 +01:00
sHillData = AllocZeroed(sizeof(*sHillData));
2019-01-31 22:51:20 +01:00
sHillData->floorId = gMapHeader.mapLayoutId - LAYOUT_TRAINER_HILL_1F;
CpuCopy32(sDataPerTag[gSaveBlock1Ptr->trainerHill.tag], &sHillData->tag, sizeof(sHillData->tag) + 4 * sizeof(struct TrHillFloor));
2019-01-13 12:12:27 +01:00
nullsub_2();
}
}
2019-01-13 20:50:08 +01:00
static void FreeDataStruct(void)
2019-01-13 12:12:27 +01:00
{
2019-01-13 13:15:23 +01:00
if (sHillData != NULL)
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:
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:
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:
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:
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
}
static void TrainerHillStartChallenge(void)
2019-01-13 12:12:27 +01:00
{
nullsub_2();
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-13 22:10:05 +01:00
gSaveBlock1Ptr->trainerHill.neverRead = 0;
2019-03-01 07:49:11 +01:00
SetTrainerHillVBlankCounter(&gSaveBlock1Ptr->trainerHill.timer);
gSaveBlock1Ptr->trainerHill.timer = 0;
2019-11-13 22:10:05 +01:00
gSaveBlock1Ptr->trainerHill.spokeToOwner = 0;
gSaveBlock1Ptr->trainerHill.checkedFinalTime = 0;
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
2019-11-13 22:10:05 +01:00
if (sHillData->tag.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;
gSaveBlock2Ptr->frontier.neverRead = 0;
2019-01-13 12:12:27 +01:00
gSpecialVar_Result = 0;
}
else
{
gSpecialVar_Result = 1;
}
}
2019-11-13 22:10:05 +01:00
// Either the bestTime and Timer field names are incorrect or something weird is going on
// Because when gSpecialVar_Result == 0 after this call the player is complimented on their fast time
// And when gSpecialVar_Result == 1 theyre told that they werent very quick
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;
}
else if (GetTimerValue(&gSaveBlock1Ptr->trainerHill.bestTime) > gSaveBlock1Ptr->trainerHill.timer)
2019-01-13 12:12:27 +01:00
{
SetTimerValue(&gSaveBlock1Ptr->trainerHill.bestTime, gSaveBlock1Ptr->trainerHill.timer);
gSaveBlock1Ptr->trainerHillTimes[gSaveBlock1Ptr->trainerHill.tag] = 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
}
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
{
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
}
}
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
}
static void TrainerHillGetChallengeStatus(void)
2019-01-13 12:12:27 +01:00
{
if (gSaveBlock1Ptr->trainerHill.hasLost)
2019-01-13 12:12:27 +01:00
{
// The player lost their last match.
2019-11-13 22:10:05 +01:00
gSaveBlock1Ptr->trainerHill.hasLost = FALSE;
gSpecialVar_Result = TRAINER_HILL_PLAYER_STATUS_LOST;
2019-01-13 12:12:27 +01:00
}
else if (gSaveBlock1Ptr->trainerHill.maybeECardScanDuringChallenge)
2019-01-13 12:12:27 +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
{
// 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;
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
static void GetAllFloorsUsed(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
if (sHillData->tag.numFloors != NUM_TRAINER_HILL_FLOORS)
2019-01-13 12:12:27 +01:00
{
ConvertIntToDecimalStringN(gStringVar1, sHillData->tag.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
static void ClearVarResult(void)
2019-01-13 12:12:27 +01:00
{
2019-01-13 13:15:23 +01:00
SetUpDataStruct();
2019-01-13 12:12:27 +01:00
gSpecialVar_Result = 0;
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
{
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
}
void nullsub_129(void)
{
}
2019-01-13 20:50:08 +01:00
static void nullsub_2(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();
FillWindowPixelBuffer(0, PIXEL_FILL(0));
2019-01-13 12:12:27 +01:00
x = GetStringCenterAlignXOffset(1, gText_TimeBoard, 0xD0);
2019-01-13 13:15:23 +01:00
AddTextPrinterParameterized3(0, 1, x, 2, sRecordWinColors, TEXT_SPEED_FF, gText_TimeBoard);
2019-01-13 12:12:27 +01:00
y = 18;
for (i = 0; i < 4; i++)
{
2019-01-13 13:15:23 +01:00
AddTextPrinterParameterized3(0, 1, 0, y, sRecordWinColors, TEXT_SPEED_FF, sTagMatchStrings[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);
x = GetStringRightAlignXOffset(1, gStringVar4, 0xD0);
2019-01-13 13:15:23 +01:00
AddTextPrinterParameterized3(0, 1, x, y, sRecordWinColors, TEXT_SPEED_FF, gStringVar4);
2019-01-13 12:12:27 +01:00
y += 17;
}
PutWindowTilemap(0);
CopyWindowToVram(0, 3);
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-13 22:10:05 +01:00
void LoadTrainerHillEventObjectTemplates(void)
2019-01-13 12:12:27 +01:00
{
2019-01-13 13:15:23 +01:00
u8 i, floorId;
2019-01-13 12:12:27 +01:00
struct EventObjectTemplate *eventTemplates = gSaveBlock1Ptr->eventObjectTemplates;
2019-11-13 22:10:05 +01:00
if (!LoadTrainerHillFloorEventObjectScripts())
2019-01-13 12:12:27 +01:00
return;
2019-01-13 13:15:23 +01:00
SetUpDataStruct();
2019-01-13 12:12:27 +01:00
for (i = 0; i < 2; i++)
2019-02-07 18:37:28 +01:00
gSaveBlock2Ptr->frontier.trainerIds[i] = 0xFFFF;
2019-01-13 12:12:27 +01:00
CpuFill32(0, gSaveBlock1Ptr->eventObjectTemplates, sizeof(gSaveBlock1Ptr->eventObjectTemplates));
2019-01-13 13:15:23 +01:00
floorId = GetFloorId();
2019-01-13 12:12:27 +01:00
for (i = 0; i < 2; i++)
{
u8 bits;
2019-11-13 22:10:05 +01:00
eventTemplates[i] = sTrainerEventObjectTemplate;
2019-01-13 12:12:27 +01:00
eventTemplates[i].localId = i + 1;
eventTemplates[i].graphicsId = FacilityClassToGraphicsId(sHillData->floors[floorId].trainers[i].facilityClass);
eventTemplates[i].x = sHillData->floors[floorId].display.coords[i] & 0xF;
eventTemplates[i].y = ((sHillData->floors[floorId].display.coords[i] >> 4) & 0xF) + 5;
2019-01-13 12:12:27 +01:00
bits = i << 2;
eventTemplates[i].movementType = ((sHillData->floors[floorId].display.direction >> bits) & 0xF) + MOVEMENT_TYPE_FACE_UP;
eventTemplates[i].trainerRange_berryTreeId = (sHillData->floors[floorId].display.range >> 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-13 22:10:05 +01:00
bool32 LoadTrainerHillFloorEventObjectScripts(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;
}
// Functionally equivalent.
#ifdef NONMATCHING
2019-01-13 20:50:08 +01:00
static u32 sub_81D5F58(u8 floorId, u32 bit, u32 arg2, u32 arg3)
2019-01-13 12:12:27 +01:00
{
u32 var0, var1, var2, var3;
2019-11-13 22:10:05 +01:00
var0 = (sHillData->floors[floorId].display.unk3A0[arg2] >> (15 - bit)) & 1;
var1 = sHillData->floors[floorId].display.data[arg2 * arg3 + bit];
2019-01-13 12:12:27 +01:00
var3 = 0x200;
var2 = 0x3000;
return ((var0 << 10) | var2) | (var1 | var3);
}
#else
NAKED
2019-01-13 20:50:08 +01:00
static u32 sub_81D5F58(u8 floorId, u32 bit, u32 arg2, u32 arg3)
2019-01-13 12:12:27 +01:00
{
asm_unified("\n\
push {r4,r5,lr}\n\
lsls r0, 24\n\
lsrs r0, 24\n\
2019-01-13 13:15:23 +01:00
ldr r4, =sHillData\n\
2019-01-13 12:12:27 +01:00
ldr r4, [r4]\n\
mov r12, r4\n\
lsls r4, r2, 1\n\
lsls r5, r0, 4\n\
subs r5, r0\n\
lsls r5, 3\n\
subs r5, r0\n\
lsls r5, 3\n\
adds r4, r5\n\
movs r0, 0xE8\n\
lsls r0, 2\n\
add r0, r12\n\
adds r0, r4\n\
ldrh r0, [r0]\n\
movs r4, 0xF\n\
subs r4, r1\n\
asrs r0, r4\n\
movs r4, 0x1\n\
ands r0, r4\n\
muls r2, r3\n\
adds r2, r1\n\
adds r2, r5\n\
movs r1, 0xA8\n\
lsls r1, 2\n\
add r1, r12\n\
adds r1, r2\n\
ldrb r1, [r1]\n\
movs r2, 0x80\n\
lsls r2, 2\n\
adds r3, r2, 0\n\
movs r2, 0xC0\n\
lsls r2, 6\n\
lsls r0, 10\n\
orrs r0, r2\n\
orrs r1, r3\n\
orrs r0, r1\n\
pop {r4,r5}\n\
pop {r1}\n\
bx r1\n\
.pool");
}
#endif // NONMATCHING
2019-11-13 22:10:05 +01:00
void GenerateTrainerHillFloorLayout(u16 *mapArg)
2019-01-13 12:12:27 +01:00
{
s32 i, j;
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;
gBackupMapLayout.width = 31;
gBackupMapLayout.height = 35;
dst = mapArg + 224;
for (i = 0; i < 5; i++)
{
for (j = 0; j < 16; j++)
dst[j] = src[j];
dst += 31;
src += 16;
}
for (i = 0; i < 16; i++)
{
for (j = 0; j < 16; j++)
2019-01-13 13:15:23 +01:00
dst[j] = sub_81D5F58(mapId, j, i, 0x10);
2019-01-13 12:12:27 +01:00
dst += 31;
}
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
// This would only occur in an E-Reader challenge, which is exclusive to JP Emerald
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-01-13 20:50:08 +01:00
bool8 GetHillTrainerFlag(u8 eventObjectId)
2019-01-13 12:12:27 +01:00
{
2019-01-13 13:15:23 +01:00
u32 floorId = GetFloorId() * 2;
u8 bitId = gEventObjects[eventObjectId].localId - 1 + floorId;
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;
2019-01-13 13:15:23 +01:00
u8 floorId = GetFloorId() * 2;
2019-01-13 12:12:27 +01:00
for (i = 0; i < 2; i++)
{
2019-02-07 18:37:28 +01:00
if (gSaveBlock2Ptr->frontier.trainerIds[i] == gTrainerBattleOpponent_A)
2019-01-13 12:12:27 +01:00
{
2019-11-13 22:10:05 +01:00
gSaveBlock2Ptr->frontier.trainerFlags |= gBitTable[floorId + i];
2019-01-13 12:12:27 +01:00
break;
}
}
if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS)
{
for (i = 0; i < 2; i++)
{
2019-02-07 18:37:28 +01:00
if (gSaveBlock2Ptr->frontier.trainerIds[i] == gTrainerBattleOpponent_B)
2019-01-13 12:12:27 +01:00
{
2019-11-13 22:10:05 +01:00
gSaveBlock2Ptr->frontier.trainerFlags |= gBitTable[floorId + i];
2019-01-13 12:12:27 +01:00
break;
}
}
}
}
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);
2019-01-13 12:12:27 +01:00
sub_80982B8();
}
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
2019-01-14 20:57:44 +01:00
if (trainerId == 0 || trainerId > 2)
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();
2019-11-13 22:10:05 +01:00
for (i = firstMonId, partySlot = 0; i < firstMonId + 3; 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];
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);
CreateNPCTrainerHillParty(gTrainerBattleOpponent_B, 3);
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;
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();
2019-11-13 22:10:05 +01:00
floors = sHillData->tag.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
}
2019-11-13 22:10:05 +01:00
// Palette never loaded, sub_81D6534 always FALSE
2019-01-13 12:12:27 +01:00
void sub_81D64C0(void)
{
if (sub_81D6534() == TRUE)
2019-11-13 22:10:05 +01:00
LoadPalette(sUnknown_0862A5D4, 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
2019-01-13 12:12:27 +01:00
bool32 sub_81D6534(void)
{
2019-11-13 22:10:05 +01:00
if (!InTrainerHillChallenge() || GetCurrentTrainerHillMapId() == TRAINER_HILL_ENTRANCE)
2019-01-13 12:12:27 +01:00
return FALSE;
2019-11-13 22:10:05 +01:00
ClearVarResult();
2019-01-13 12:12:27 +01:00
if (gSpecialVar_Result == 0)
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
{
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
}
static void TrainerHillSetTag(void)
2019-01-13 12:12:27 +01:00
{
2019-01-13 13:15:23 +01:00
gSaveBlock1Ptr->trainerHill.tag = gSpecialVar_0x8005;
gSaveBlock1Ptr->trainerHill.bestTime = gSaveBlock1Ptr->trainerHillTimes[gSpecialVar_0x8005];
2019-01-13 12:12:27 +01:00
}
2019-11-13 22:10:05 +01:00
static u8 GetPrizeListId(bool8 npcChallenge)
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
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
}
2019-11-13 22:10:05 +01:00
// Not possible to win TMs from E-Reader challenges
if (npcChallenge)
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;
s32 var = 0, prizeListSetId, minutes, id;
2019-01-13 12:12:27 +01:00
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
{
2019-11-13 22:10:05 +01:00
var += sHillData->floors[i].trainerNum1;
var += sHillData->floors[i].trainerNum2;
2019-01-13 12:12:27 +01:00
}
2019-11-13 22:10:05 +01:00
prizeListSetId = var / 256;
prizeListSetId %= 2;
if (FlagGet(FLAG_SYS_GAME_CLEAR) && sHillData->tag.trainerType == HILL_TRAINER_TYPE_NPC)
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
2019-01-13 13:15:23 +01:00
if (gSaveBlock1Ptr->trainerHill.tag == HILL_TAG_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
2019-11-13 22:10:05 +01:00
prizeList = sPrizeListSets[prizeListSetId][i];
minutes = (signed)(gSaveBlock1Ptr->trainerHill.timer) / (60 * 60);
2019-01-13 12:12:27 +01:00
if (minutes < 12)
id = 0;
else if (minutes < 13)
id = 1;
else if (minutes < 14)
id = 2;
else if (minutes < 16)
id = 3;
else if (minutes < 18)
id = 4;
else
id = 5;
2019-11-13 22:10:05 +01:00
return prizeList[id];
2019-01-13 12:12:27 +01:00
}