mirror of https://github.com/zeldaret/mm.git
En_Mt_tag OK and documented (Goron Race Manager) (#659)
* EnMttag_Init OK * EnMttag_Destroy OK * EnMttag_Update OK * func_809CF9A0 OK * Update struct * func_809CF444 OK * func_809CFA00 OK * func_809CF394 OK * func_809CF8EC OK * func_809CFA54 OK * func_809CF350 OK * func_809CF848 OK * func_809CF950 OK * func_809CFBC4 OK * func_809CFD98 OK * func_809CFE28 OK * func_809CFF94 OK * func_809CFC38 OK * Get super close on func_809CF4EC * Import data * Get a start on func_809CF67C * func_809CF4EC OK, thanks engineer! * func_809CF67C OK, thanks engineer! * Use the generated reloc * Name message functions and raceGorons array * Name some more stuff * Name more functions * Name most functions * Take a stab at trying to figure out what the data represents * Much better naming and understanding * Name more * Strip out debugging junk * Better name * Name the rest of the variables * Describe bug * Document the h*ck out of things * Simplify PlayerCheatStatus a bit * Use sCheckpointPositions as per engineer's advice * Do some really stupid naming nonsense to explain the side effect of a function * Fix Racing/RaceGoron inconsistency * Decimal weekEventReg accesses * Rename to EnMttag_UpdateCheckPoints * Capitalization consistency + update functions.txt * Respond to review * Respond to review * Respond to Elliptic's review * idk if Elliptic meant the function name or the variable so let's just do both lol * Fix function name changed by z_demo merge
This commit is contained in:
parent
3431b542ff
commit
cbfa42c070
3
spec
3
spec
|
|
@ -2481,8 +2481,7 @@ beginseg
|
||||||
name "ovl_En_Mt_tag"
|
name "ovl_En_Mt_tag"
|
||||||
compress
|
compress
|
||||||
include "build/src/overlays/actors/ovl_En_Mt_tag/z_en_mt_tag.o"
|
include "build/src/overlays/actors/ovl_En_Mt_tag/z_en_mt_tag.o"
|
||||||
include "build/data/ovl_En_Mt_tag/ovl_En_Mt_tag.data.o"
|
include "build/src/overlays/actors/ovl_En_Mt_tag/ovl_En_Mt_tag_reloc.o"
|
||||||
include "build/data/ovl_En_Mt_tag/ovl_En_Mt_tag.reloc.o"
|
|
||||||
endseg
|
endseg
|
||||||
|
|
||||||
beginseg
|
beginseg
|
||||||
|
|
|
||||||
|
|
@ -14,15 +14,20 @@ void EnMttag_Init(Actor* thisx, GlobalContext* globalCtx);
|
||||||
void EnMttag_Destroy(Actor* thisx, GlobalContext* globalCtx);
|
void EnMttag_Destroy(Actor* thisx, GlobalContext* globalCtx);
|
||||||
void EnMttag_Update(Actor* thisx, GlobalContext* globalCtx);
|
void EnMttag_Update(Actor* thisx, GlobalContext* globalCtx);
|
||||||
|
|
||||||
void func_809CF9A0(EnMttag* this, GlobalContext* globalCtx);
|
void EnMttag_ShowIntroCutscene(EnMttag* this, GlobalContext* globalCtx);
|
||||||
void func_809CFA00(EnMttag* this, GlobalContext* globalCtx);
|
void EnMttag_WaitForIntroCutsceneToEnd(EnMttag* this, GlobalContext* globalCtx);
|
||||||
void func_809CFA54(EnMttag* this, GlobalContext* globalCtx);
|
void EnMttag_RaceStart(EnMttag* this, GlobalContext* globalCtx);
|
||||||
void func_809CFC38(EnMttag* this, GlobalContext* globalCtx);
|
void EnMttag_Race(EnMttag* this, GlobalContext* globalCtx);
|
||||||
void func_809CFD98(EnMttag* this, GlobalContext* globalCtx);
|
void EnMttag_RaceFinish(EnMttag* this, GlobalContext* globalCtx);
|
||||||
void func_809CFE28(EnMttag* this, GlobalContext* globalCtx);
|
void EnMttag_PotentiallyRestartRace(EnMttag* this, GlobalContext* globalCtx);
|
||||||
void func_809CFF94(EnMttag* this, GlobalContext* globalCtx);
|
void EnMttag_HandleCantWinChoice(EnMttag* this, GlobalContext* globalCtx);
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GORON_RACE_CHEAT_NO_CHEATING,
|
||||||
|
GORON_RACE_CHEAT_FALSE_START,
|
||||||
|
GORON_RACE_CHEAT_TRYING_TO_REACH_GOAL_FROM_BEHIND,
|
||||||
|
} PlayerCheatStatus;
|
||||||
|
|
||||||
#if 0
|
|
||||||
const ActorInit En_Mt_tag_InitVars = {
|
const ActorInit En_Mt_tag_InitVars = {
|
||||||
ACTOR_EN_MT_TAG,
|
ACTOR_EN_MT_TAG,
|
||||||
ACTORCAT_BG,
|
ACTORCAT_BG,
|
||||||
|
|
@ -35,42 +40,474 @@ const ActorInit En_Mt_tag_InitVars = {
|
||||||
(ActorFunc)NULL,
|
(ActorFunc)NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
static s32 sStartingCheckpointPerSceneExitIndex[] = {
|
||||||
|
0, 0, 0, 0, 1, 9, 12, 16, 19, 22, 26, 29, 30, 32, 34, 36, 39, 42, 45,
|
||||||
|
};
|
||||||
|
|
||||||
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Mt_tag/func_809CF350.s")
|
// The Y-positions here are never used by any part of this actor.
|
||||||
|
static Vec3f sCheckpointPositions[] = {
|
||||||
|
{ -105.0f, 1000.0f, -240.0f }, { -1751.0f, 1000.0f, -240.0f }, { -3138.0f, 1000.0f, -74.0f },
|
||||||
|
{ -4617.0f, 1000.0f, 277.0f }, { -5060.0f, 1000.0f, 388.0f }, { -5412.0f, 1000.0f, 573.0f },
|
||||||
|
{ -5523.0f, 1000.0f, 1035.0f }, { -5393.0f, 1000.0f, 1405.0f }, { -5060.0f, 1000.0f, 1553.0f },
|
||||||
|
{ -3933.0f, 1000.0f, 1479.0f }, { -3212.0f, 1000.0f, 1461.0f }, { -2805.0f, 1000.0f, 1645.0f },
|
||||||
|
{ -2638.0f, 1000.0f, 2071.0f }, { -2823.0f, 1000.0f, 2422.0f }, { -3212.0f, 1000.0f, 2607.0f },
|
||||||
|
{ -3785.0f, 1000.0f, 2977.0f }, { -4321.0f, 1000.0f, 3501.0f }, { -4654.0f, 1000.0f, 4185.0f },
|
||||||
|
{ -4802.0f, 1000.0f, 4779.0f }, { -4672.0f, 1000.0f, 5426.0f }, { -4339.0f, 1000.0f, 6037.0f },
|
||||||
|
{ -3748.0f, 1000.0f, 6314.0f }, { -2749.0f, 1000.0f, 6478.0f }, { -2453.0f, 1000.0f, 6922.0f },
|
||||||
|
{ -2269.0f, 1000.0f, 7754.0f }, { -2453.0f, 1000.0f, 8309.0f }, { -3008.0f, 1000.0f, 8438.0f },
|
||||||
|
{ -3304.0f, 1000.0f, 8179.0f }, { -3600.0f, 1000.0f, 7606.0f }, { -3600.0f, 1000.0f, 6885.0f },
|
||||||
|
{ -3618.0f, 1000.0f, 4392.0f }, { -3600.0f, 1000.0f, 3855.0f }, { -3396.0f, 1000.0f, 3189.0f },
|
||||||
|
{ -3396.0f, 1000.0f, 2283.0f }, { -3600.0f, 1000.0f, 818.0f }, { -3803.0f, 1000.0f, -88.0f },
|
||||||
|
{ -4543.0f, 1000.0f, -2457.0f }, { -4543.0f, 1000.0f, -2938.0f }, { -4543.0f, 1000.0f, -3530.0f },
|
||||||
|
{ -4284.0f, 1000.0f, -4333.0f }, { -3581.0f, 1000.0f, -4795.0f }, { -2805.0f, 1000.0f, -4850.0f },
|
||||||
|
{ -1825.0f, 1000.0f, -4703.0f }, { -1326.0f, 1000.0f, -4166.0f }, { -1122.0f, 1000.0f, -3186.0f },
|
||||||
|
{ -1085.0f, 1000.0f, -2059.0f }, { -1067.0f, 1000.0f, -912.0f },
|
||||||
|
};
|
||||||
|
|
||||||
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Mt_tag/func_809CF394.s")
|
/**
|
||||||
|
* Returns true if the specified position is in the finish line.
|
||||||
|
* The range extends a little bit beyond the finish line's in-game visual.
|
||||||
|
*/
|
||||||
|
s32 EnMttag_IsInFinishLine(Vec3f* pos) {
|
||||||
|
return Math3D_XZBoundCheck(-1261.0f, -901.0f, -1600.0f, -1520.0f, pos->x, pos->z);
|
||||||
|
}
|
||||||
|
|
||||||
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Mt_tag/func_809CF444.s")
|
/**
|
||||||
|
* Returns a value in PlayerCheatStatus that indicates if the player is cheating
|
||||||
|
* and, if so, what kind of cheating the player is performing.
|
||||||
|
*/
|
||||||
|
s32 EnMttag_CheckPlayerCheatStatus(Vec3f* pos) {
|
||||||
|
if (!(gSaveContext.eventInf[1] & 1)) {
|
||||||
|
if (Math3D_XZBoundCheck(-466.0f, -386.0f, -687.0f, 193.0f, pos->x, pos->z)) {
|
||||||
|
// The race hasn't started yet, but the player is beyond the starting line.
|
||||||
|
return GORON_RACE_CHEAT_FALSE_START;
|
||||||
|
}
|
||||||
|
} else if (Math3D_XZBoundCheck(-1127.0f, -1007.0f, -867.0f, -787.0f, pos->x, pos->z)) {
|
||||||
|
// The goal is actually quite close to the start, just behind a large wall.
|
||||||
|
// This checks if the player is in an area "behind" the goal that is not accessible
|
||||||
|
// in normal play; it can only be reached by climbing the wall somehow. Perhaps they
|
||||||
|
// were worried that players would find a way to climb the wall with a glitch, or
|
||||||
|
// perhaps they just wanted to punish people using cheat codes.
|
||||||
|
return GORON_RACE_CHEAT_TRYING_TO_REACH_GOAL_FROM_BEHIND;
|
||||||
|
}
|
||||||
|
return GORON_RACE_CHEAT_NO_CHEATING;
|
||||||
|
}
|
||||||
|
|
||||||
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Mt_tag/func_809CF4EC.s")
|
/**
|
||||||
|
* This function tries to find all four Race Gorons present in the racetrack.
|
||||||
|
* If it finds them, it stores a pointer to each one in the actor's struct.
|
||||||
|
* Returns true if all four Race Gorons are found.
|
||||||
|
*/
|
||||||
|
s32 EnMttag_AreFourRaceGoronsPresent(EnMttag* this, GlobalContext* globalCtx) {
|
||||||
|
Actor* actor = NULL;
|
||||||
|
s32 i = 0;
|
||||||
|
s32 areGoronsPresent;
|
||||||
|
|
||||||
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Mt_tag/func_809CF67C.s")
|
do {
|
||||||
|
actor = SubS_FindActor(globalCtx, actor, ACTORCAT_NPC, ACTOR_EN_RG);
|
||||||
|
if (actor != NULL) {
|
||||||
|
this->raceGorons[i] = (EnRg*)actor;
|
||||||
|
i++;
|
||||||
|
} else if ((actor == NULL) || (actor->next == NULL)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Mt_tag/func_809CF848.s")
|
actor = actor->next;
|
||||||
|
} while (i < ARRAY_COUNT(this->raceGorons));
|
||||||
|
|
||||||
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Mt_tag/func_809CF8EC.s")
|
if (i < ARRAY_COUNT(this->raceGorons)) {
|
||||||
|
areGoronsPresent = false;
|
||||||
|
} else {
|
||||||
|
areGoronsPresent = true;
|
||||||
|
}
|
||||||
|
|
||||||
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Mt_tag/func_809CF950.s")
|
return areGoronsPresent;
|
||||||
|
}
|
||||||
|
|
||||||
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Mt_tag/func_809CF9A0.s")
|
/**
|
||||||
|
* Returns the checkpoint number for the supplied actor.
|
||||||
|
* At the start of the race, all race entrants are at checkpoint 1, and their
|
||||||
|
* checkpoint number gradually increases as they move forward through the racetrack.
|
||||||
|
* The player can have a checkpoint number of -1 if they move far enough backwards
|
||||||
|
* from the starting line.
|
||||||
|
*/
|
||||||
|
s32 EnMttag_GetCurrentCheckpoint(Actor* actor, GlobalContext* globalCtx, s32* upcomingCheckpoint,
|
||||||
|
f32* outPerpendicularPointX, f32* outPerpendicularPointZ) {
|
||||||
|
s32 curentCheckpoint = -1;
|
||||||
|
s32 hasSetCurrentCheckpointOnce = false;
|
||||||
|
f32 minLineLengthSq = 0.0f;
|
||||||
|
s32 sceneExitIndex;
|
||||||
|
f32 perpendicularPointX;
|
||||||
|
f32 perpendicularPointZ;
|
||||||
|
f32 lineLenSq;
|
||||||
|
s32 checkpointIterator;
|
||||||
|
|
||||||
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Mt_tag/func_809CFA00.s")
|
// The Goron Racetrack is configured such that the sceneExitIndex for any given floor polygon
|
||||||
|
// gradually increases as you move forward through the racetrack.
|
||||||
|
sceneExitIndex = SurfaceType_GetSceneExitIndex(&globalCtx->colCtx, actor->floorPoly, actor->floorBgId);
|
||||||
|
if ((sceneExitIndex < 4) || (sceneExitIndex >= 19)) {
|
||||||
|
//! @bug - upcomingCheckpoint is not initialized here
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Mt_tag/func_809CFA54.s")
|
checkpointIterator = sStartingCheckpointPerSceneExitIndex[sceneExitIndex];
|
||||||
|
|
||||||
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Mt_tag/func_809CFBC4.s")
|
// Iterates through all possible checkpoints that are associated with this sceneExitIndex.
|
||||||
|
do {
|
||||||
|
if ((Math3D_PointDistToLine2D(
|
||||||
|
actor->world.pos.x, actor->world.pos.z, (&sCheckpointPositions[checkpointIterator])[-1].x,
|
||||||
|
(&sCheckpointPositions[checkpointIterator])[-1].z, (&sCheckpointPositions[checkpointIterator])[1].x,
|
||||||
|
(&sCheckpointPositions[checkpointIterator])[1].z, &perpendicularPointX, &perpendicularPointZ,
|
||||||
|
&lineLenSq)) &&
|
||||||
|
(!hasSetCurrentCheckpointOnce || ((curentCheckpoint + 1) == checkpointIterator) ||
|
||||||
|
(lineLenSq < minLineLengthSq))) {
|
||||||
|
minLineLengthSq = lineLenSq;
|
||||||
|
curentCheckpoint = checkpointIterator;
|
||||||
|
*outPerpendicularPointX = perpendicularPointX;
|
||||||
|
*outPerpendicularPointZ = perpendicularPointZ;
|
||||||
|
hasSetCurrentCheckpointOnce = true;
|
||||||
|
}
|
||||||
|
checkpointIterator++;
|
||||||
|
} while (checkpointIterator < sStartingCheckpointPerSceneExitIndex[sceneExitIndex + 1]);
|
||||||
|
|
||||||
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Mt_tag/func_809CFC38.s")
|
*upcomingCheckpoint = curentCheckpoint + 1;
|
||||||
|
return curentCheckpoint;
|
||||||
|
}
|
||||||
|
|
||||||
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Mt_tag/func_809CFD98.s")
|
/**
|
||||||
|
* Returns true if the player is almost certainly going to lose the race.
|
||||||
|
* Specifically, it checks if the player's current checkpoint is 24 or more
|
||||||
|
* checkpoints behind the leading racer. This value was probably chosen because
|
||||||
|
* falling off the wooden bridge in the middle of the track can set the player
|
||||||
|
* back up to 23 checkpoints.
|
||||||
|
*
|
||||||
|
* This function also has the side effect of updating the number of checkpoints
|
||||||
|
* ahead of the player each Race Goron is.
|
||||||
|
*/
|
||||||
|
s32 EnMttag_UpdateCheckpoints(EnMttag* this, GlobalContext* globalCtx) {
|
||||||
|
Player* player = GET_PLAYER(globalCtx);
|
||||||
|
EnRg* rg;
|
||||||
|
s32 currentCheckpoints[5];
|
||||||
|
s32 upcomingCheckpoints[5];
|
||||||
|
f32 perpendicularPointsX[5];
|
||||||
|
f32 perpendicularPointsZ[5];
|
||||||
|
s32 highestCurrentCheckpoint;
|
||||||
|
s32 i;
|
||||||
|
s32 playerIsLikelyToLose = false;
|
||||||
|
|
||||||
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Mt_tag/func_809CFE28.s")
|
highestCurrentCheckpoint = -1;
|
||||||
|
currentCheckpoints[0] = EnMttag_GetCurrentCheckpoint(&player->actor, globalCtx, &upcomingCheckpoints[0],
|
||||||
|
&perpendicularPointsX[0], &perpendicularPointsZ[0]);
|
||||||
|
for (i = 1; i < ARRAY_COUNT(this->raceGorons) + 1; i++) {
|
||||||
|
currentCheckpoints[i] =
|
||||||
|
EnMttag_GetCurrentCheckpoint(&this->raceGorons[i - 1]->actor, globalCtx, &upcomingCheckpoints[i],
|
||||||
|
&perpendicularPointsX[i], &perpendicularPointsZ[i]);
|
||||||
|
if (highestCurrentCheckpoint < currentCheckpoints[i]) {
|
||||||
|
highestCurrentCheckpoint = currentCheckpoints[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Mt_tag/func_809CFF94.s")
|
for (i = 1; i < ARRAY_COUNT(this->raceGorons) + 1; i++) {
|
||||||
|
rg = this->raceGorons[i - 1];
|
||||||
|
|
||||||
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Mt_tag/EnMttag_Init.s")
|
// Because of the bug described in EnMttag_GetCurrentCheckpoint, these values may not be initialized.
|
||||||
|
//! @bug When initialized, this check is pointless because upcomingCheckpoint is always 0 or higher.
|
||||||
|
if ((upcomingCheckpoints[i] != -1) && (upcomingCheckpoints[0] != -1)) {
|
||||||
|
rg->numCheckpointsAheadOfPlayer = (upcomingCheckpoints[i] - upcomingCheckpoints[0]);
|
||||||
|
} else {
|
||||||
|
rg->numCheckpointsAheadOfPlayer = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Mt_tag/EnMttag_Destroy.s")
|
if ((currentCheckpoints[0] > 0) && (currentCheckpoints[0] < highestCurrentCheckpoint) &&
|
||||||
|
(player->actor.bgCheckFlags & 1) && ((highestCurrentCheckpoint - currentCheckpoints[0]) >= 24)) {
|
||||||
|
playerIsLikelyToLose = true;
|
||||||
|
}
|
||||||
|
|
||||||
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Mt_tag/EnMttag_Update.s")
|
return playerIsLikelyToLose;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exits the race and returns the player back to "normal" gameplay.
|
||||||
|
* Whether the player won or lost the race is determined by arg1 and nextTransition.
|
||||||
|
*/
|
||||||
|
s32 EnMttag_ExitRace(GlobalContext* globalCtx, s32 arg1, s32 nextTransition) {
|
||||||
|
CUR_FORM_EQUIP(EQUIP_SLOT_B) = ITEM_SWORD_KOKIRI;
|
||||||
|
globalCtx->nextEntranceIndex = 0xD020;
|
||||||
|
if ((gSaveContext.weekEventReg[33] & 0x80)) {
|
||||||
|
// Spring
|
||||||
|
gSaveContext.nextCutsceneIndex = 0xFFF0;
|
||||||
|
} else {
|
||||||
|
// Winter
|
||||||
|
gSaveContext.nextCutsceneIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
globalCtx->sceneLoadFlag = 0x14;
|
||||||
|
globalCtx->unk_1887F = arg1;
|
||||||
|
gSaveContext.nextTransition = nextTransition;
|
||||||
|
func_801477B4(globalCtx);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the text which says that the player has made a false start.
|
||||||
|
*/
|
||||||
|
void EnMttag_ShowFalseStartMessage(EnMttag* this, GlobalContext* globalCtx) {
|
||||||
|
gSaveContext.unk_3DD0[4] = 0;
|
||||||
|
Message_StartTextbox(globalCtx, 0xE95, NULL); // An entrant made a false start
|
||||||
|
func_800B7298(globalCtx, &this->actor, 7);
|
||||||
|
Audio_QueueSeqCmd(0x101400FF);
|
||||||
|
this->actionFunc = EnMttag_PotentiallyRestartRace;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Displays the text from the Goron Elder's child which tells the player that
|
||||||
|
* they probably can't win the race.
|
||||||
|
*/
|
||||||
|
void EnMttag_ShowCantWinMessage(EnMttag* this, GlobalContext* globalCtx) {
|
||||||
|
Message_StartTextbox(globalCtx, 0xE97, NULL); // You can't win now...
|
||||||
|
func_800B7298(globalCtx, &this->actor, 7);
|
||||||
|
this->actionFunc = EnMttag_HandleCantWinChoice;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows the cutscene that pans over the race track and shows all five race entrants.
|
||||||
|
*/
|
||||||
|
void EnMttag_ShowIntroCutscene(EnMttag* this, GlobalContext* globalCtx) {
|
||||||
|
if (ActorCutscene_GetCanPlayNext(this->actor.cutscene)) {
|
||||||
|
ActorCutscene_StartAndSetUnkLinkFields(this->actor.cutscene, &this->actor);
|
||||||
|
this->actionFunc = EnMttag_WaitForIntroCutsceneToEnd;
|
||||||
|
} else {
|
||||||
|
ActorCutscene_SetIntentToPlay(this->actor.cutscene);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When the intro cutscene concludes, this sets the weekEventReg to prevent it
|
||||||
|
* from showing again and starts the race.
|
||||||
|
*/
|
||||||
|
void EnMttag_WaitForIntroCutsceneToEnd(EnMttag* this, GlobalContext* globalCtx) {
|
||||||
|
if (ActorCutscene_GetCurrentIndex() != this->actor.cutscene) {
|
||||||
|
gSaveContext.weekEventReg[12] |= 2;
|
||||||
|
this->actionFunc = EnMttag_RaceStart;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the race from when the Gorons are first lined up at the
|
||||||
|
* starting block to when the countdown finishes.
|
||||||
|
*/
|
||||||
|
void EnMttag_RaceStart(EnMttag* this, GlobalContext* globalCtx) {
|
||||||
|
Player* player = GET_PLAYER(globalCtx);
|
||||||
|
s32 playerCheatStatus;
|
||||||
|
|
||||||
|
if (this->raceInitialized == true) {
|
||||||
|
playerCheatStatus = EnMttag_CheckPlayerCheatStatus(&player->actor.world.pos);
|
||||||
|
if (playerCheatStatus != GORON_RACE_CHEAT_NO_CHEATING) {
|
||||||
|
if (playerCheatStatus == GORON_RACE_CHEAT_FALSE_START) {
|
||||||
|
this->shouldRestartRace = true;
|
||||||
|
} else {
|
||||||
|
this->shouldRestartRace = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
EnMttag_ShowFalseStartMessage(this, globalCtx);
|
||||||
|
gSaveContext.eventInf[1] |= 8;
|
||||||
|
} else {
|
||||||
|
if (DECR(this->timer) == 60) {
|
||||||
|
func_8010E9F0(4, 0);
|
||||||
|
globalCtx->interfaceCtx.unk_280 = 1;
|
||||||
|
Audio_QueueSeqCmd(NA_BGM_GORON_RACE | 0x8000);
|
||||||
|
globalCtx->envCtx.unk_E4 = 0xFE;
|
||||||
|
player->stateFlags1 &= ~0x20;
|
||||||
|
} else if ((this->timer < 60) && (globalCtx->interfaceCtx.unk_280 == 8)) {
|
||||||
|
this->timer = 0;
|
||||||
|
gSaveContext.eventInf[1] |= 1;
|
||||||
|
this->actionFunc = EnMttag_Race;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (EnMttag_AreFourRaceGoronsPresent(this, globalCtx)) {
|
||||||
|
this->raceInitialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if any Race Goron is over the finish line.
|
||||||
|
*/
|
||||||
|
s32 EnMttag_IsAnyRaceGoronOverFinishLine(EnMttag* this) {
|
||||||
|
s32 isAnyRaceGoronOverFinishLine = false;
|
||||||
|
s32 i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_COUNT(this->raceGorons); i++) {
|
||||||
|
if ((EnMttag_IsInFinishLine(&this->raceGorons[i]->actor.world.pos)) &&
|
||||||
|
(this->raceGorons[i]->actor.update != NULL)) {
|
||||||
|
isAnyRaceGoronOverFinishLine = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isAnyRaceGoronOverFinishLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the race from when the countdown finishes to when
|
||||||
|
* any race entrant crosses the finish line.
|
||||||
|
*/
|
||||||
|
void EnMttag_Race(EnMttag* this, GlobalContext* globalCtx) {
|
||||||
|
Player* player = GET_PLAYER(globalCtx);
|
||||||
|
Vec3f* playerPos = &player->actor.world.pos;
|
||||||
|
s32 playerCheatStatus;
|
||||||
|
|
||||||
|
if (EnMttag_IsInFinishLine(playerPos)) {
|
||||||
|
gSaveContext.unk_3DD0[4] = 6;
|
||||||
|
play_sound(NA_SE_SY_START_SHOT);
|
||||||
|
Audio_QueueSeqCmd(NA_BGM_GORON_GOAL | 0x8000);
|
||||||
|
this->timer = 55;
|
||||||
|
gSaveContext.eventInf[1] |= 2;
|
||||||
|
this->actionFunc = EnMttag_RaceFinish;
|
||||||
|
} else if (EnMttag_IsAnyRaceGoronOverFinishLine(this)) {
|
||||||
|
gSaveContext.unk_3DD0[4] = 6;
|
||||||
|
play_sound(NA_SE_SY_START_SHOT);
|
||||||
|
Audio_QueueSeqCmd(NA_BGM_GORON_GOAL | 0x8000);
|
||||||
|
this->timer = 55;
|
||||||
|
gSaveContext.eventInf[1] |= 4;
|
||||||
|
this->actionFunc = EnMttag_RaceFinish;
|
||||||
|
} else {
|
||||||
|
playerCheatStatus = EnMttag_CheckPlayerCheatStatus(playerPos);
|
||||||
|
if (playerCheatStatus != GORON_RACE_CHEAT_NO_CHEATING) {
|
||||||
|
if (playerCheatStatus == GORON_RACE_CHEAT_FALSE_START) {
|
||||||
|
this->shouldRestartRace = true;
|
||||||
|
} else {
|
||||||
|
this->shouldRestartRace = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
EnMttag_ShowFalseStartMessage(this, globalCtx);
|
||||||
|
gSaveContext.eventInf[1] |= 8;
|
||||||
|
} else if ((EnMttag_UpdateCheckpoints(this, globalCtx)) && (this->timer == 0)) {
|
||||||
|
EnMttag_ShowCantWinMessage(this, globalCtx);
|
||||||
|
gSaveContext.eventInf[1] |= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the race after any race entrant crosses the finish line.
|
||||||
|
* This function simply waits for a bit before exiting the race.
|
||||||
|
*/
|
||||||
|
void EnMttag_RaceFinish(EnMttag* this, GlobalContext* globalCtx) {
|
||||||
|
if (DECR(this->timer) == 0) {
|
||||||
|
if ((gSaveContext.eventInf[1] & 2)) {
|
||||||
|
// Player won
|
||||||
|
EnMttag_ExitRace(globalCtx, 3, 3);
|
||||||
|
} else {
|
||||||
|
// A non-player Goron won
|
||||||
|
EnMttag_ExitRace(globalCtx, 2, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
Actor_MarkForDeath(&this->actor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restarts the race if this->shouldRestartRace is true. Otherwise, it exits the race.
|
||||||
|
* In practice, the only time this exits the race is if the player tries to cheat by
|
||||||
|
* reaching the goal from behind.
|
||||||
|
*/
|
||||||
|
void EnMttag_PotentiallyRestartRace(EnMttag* this, GlobalContext* globalCtx) {
|
||||||
|
u8 talkState = Message_GetState(&globalCtx->msgCtx);
|
||||||
|
|
||||||
|
if (((talkState == 5 && func_80147624(globalCtx)) || talkState == 2)) {
|
||||||
|
if (this->shouldRestartRace) {
|
||||||
|
globalCtx->nextEntranceIndex = 0xD010;
|
||||||
|
|
||||||
|
if (gSaveContext.weekEventReg[33] & 0x80) {
|
||||||
|
// Spring
|
||||||
|
gSaveContext.nextCutsceneIndex = 0xFFF0;
|
||||||
|
} else {
|
||||||
|
// Winter
|
||||||
|
gSaveContext.nextCutsceneIndex = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
globalCtx->sceneLoadFlag = 0x14;
|
||||||
|
globalCtx->unk_1887F = 2;
|
||||||
|
gSaveContext.nextTransition = 2;
|
||||||
|
func_801477B4(globalCtx);
|
||||||
|
func_800B7298(globalCtx, &this->actor, 7);
|
||||||
|
Parameter_AddMagic(globalCtx, ((void)0, gSaveContext.unk_3F30) + (gSaveContext.doubleMagic * 48) + 48);
|
||||||
|
|
||||||
|
gSaveContext.eventInf[1] &= (u8)~1;
|
||||||
|
gSaveContext.eventInf[1] &= (u8)~2;
|
||||||
|
gSaveContext.eventInf[1] &= (u8)~4;
|
||||||
|
gSaveContext.eventInf[1] &= (u8)~8;
|
||||||
|
gSaveContext.eventInf[2] = ((gSaveContext.eventInf[2] & 0xF) + 1) | (gSaveContext.eventInf[2] & 0xF0);
|
||||||
|
} else {
|
||||||
|
EnMttag_ExitRace(globalCtx, 2, 2);
|
||||||
|
}
|
||||||
|
Actor_MarkForDeath(&this->actor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function either exits the race or resumes it based on how the player
|
||||||
|
* responded to the Goron Elder's son's question.
|
||||||
|
*/
|
||||||
|
void EnMttag_HandleCantWinChoice(EnMttag* this, GlobalContext* globalCtx) {
|
||||||
|
if ((Message_GetState(&globalCtx->msgCtx) == 4) && (func_80147624(globalCtx))) {
|
||||||
|
if (globalCtx->msgCtx.choiceIndex != 0) {
|
||||||
|
// Exit the race
|
||||||
|
func_8019F230();
|
||||||
|
gSaveContext.unk_3DD0[4] = 0;
|
||||||
|
EnMttag_ExitRace(globalCtx, 2, 2);
|
||||||
|
gSaveContext.eventInf[1] &= (u8)~8;
|
||||||
|
gSaveContext.eventInf[1] |= 4;
|
||||||
|
Actor_MarkForDeath(&this->actor);
|
||||||
|
} else {
|
||||||
|
// Keep racing
|
||||||
|
func_8019F208();
|
||||||
|
func_801477B4(globalCtx);
|
||||||
|
func_800B7298(globalCtx, &this->actor, 6);
|
||||||
|
gSaveContext.eventInf[1] &= (u8)~8;
|
||||||
|
this->timer = 100;
|
||||||
|
this->actionFunc = EnMttag_Race;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnMttag_Init(Actor* thisx, GlobalContext* globalCtx) {
|
||||||
|
Player* player;
|
||||||
|
EnMttag* this = THIS;
|
||||||
|
|
||||||
|
if (gSaveContext.entranceIndex == 0xD010) {
|
||||||
|
player = GET_PLAYER(globalCtx);
|
||||||
|
player->stateFlags1 |= 0x20;
|
||||||
|
this->raceInitialized = false;
|
||||||
|
this->timer = 100;
|
||||||
|
|
||||||
|
gSaveContext.eventInf[1] &= (u8)~1;
|
||||||
|
gSaveContext.eventInf[1] &= (u8)~2;
|
||||||
|
gSaveContext.eventInf[1] &= (u8)~4;
|
||||||
|
gSaveContext.eventInf[1] &= (u8)~8;
|
||||||
|
|
||||||
|
if (!(gSaveContext.weekEventReg[12] & 2)) {
|
||||||
|
this->actionFunc = EnMttag_ShowIntroCutscene;
|
||||||
|
} else {
|
||||||
|
s32 requiredScopeTemp;
|
||||||
|
|
||||||
|
this->actionFunc = EnMttag_RaceStart;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Actor_MarkForDeath(&this->actor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnMttag_Destroy(Actor* thisx, GlobalContext* globalCtx) {
|
||||||
|
EnMttag* this = THIS;
|
||||||
|
if (gSaveContext.unk_3DD0[4] != 6) {
|
||||||
|
gSaveContext.unk_3DD0[4] = 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnMttag_Update(Actor* thisx, GlobalContext* globalCtx) {
|
||||||
|
EnMttag* this = THIS;
|
||||||
|
this->actionFunc(this, globalCtx);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,20 @@
|
||||||
#define Z_EN_MT_TAG_H
|
#define Z_EN_MT_TAG_H
|
||||||
|
|
||||||
#include "global.h"
|
#include "global.h"
|
||||||
|
#include "overlays/actors/ovl_En_Rg/z_en_rg.h"
|
||||||
|
|
||||||
struct EnMttag;
|
struct EnMttag;
|
||||||
|
|
||||||
typedef void (*EnMttagActionFunc)(struct EnMttag*, GlobalContext*);
|
typedef void (*EnMttagActionFunc)(struct EnMttag*, GlobalContext*);
|
||||||
|
|
||||||
typedef struct EnMttag {
|
typedef struct EnMttag {
|
||||||
/* 0x0000 */ Actor actor;
|
/* 0x000 */ Actor actor;
|
||||||
/* 0x0144 */ EnMttagActionFunc actionFunc;
|
/* 0x144 */ EnMttagActionFunc actionFunc;
|
||||||
/* 0x0148 */ char unk_148[0x20];
|
/* 0x148 */ EnRg* raceGorons[4];
|
||||||
|
/* 0x158 */ u16 raceInitialized;
|
||||||
|
/* 0x15A */ s16 timer;
|
||||||
|
/* 0x15C */ UNK_TYPE1 unk_15C[0x8];
|
||||||
|
/* 0x164 */ s32 shouldRestartRace;
|
||||||
} EnMttag; // size = 0x168
|
} EnMttag; // size = 0x168
|
||||||
|
|
||||||
extern const ActorInit En_Mt_tag_InitVars;
|
extern const ActorInit En_Mt_tag_InitVars;
|
||||||
|
|
|
||||||
|
|
@ -477,11 +477,11 @@ s32 func_80BF47AC(EnRg* this, GlobalContext* globalCtx) {
|
||||||
|
|
||||||
if ((this->unk_310 & 0x400) || (this->unk_310 & 0x1000)) {
|
if ((this->unk_310 & 0x400) || (this->unk_310 & 0x1000)) {
|
||||||
phi_f0 = 0.0f;
|
phi_f0 = 0.0f;
|
||||||
} else if (this->unk_348 >= 2) {
|
} else if (this->numCheckpointsAheadOfPlayer >= 2) {
|
||||||
phi_f0 = phi_f2 * 0.5f;
|
phi_f0 = phi_f2 * 0.5f;
|
||||||
} else if (this->unk_348 == 1) {
|
} else if (this->numCheckpointsAheadOfPlayer == 1) {
|
||||||
phi_f0 = phi_f2 * 0.75f;
|
phi_f0 = phi_f2 * 0.75f;
|
||||||
} else if (this->unk_348 == 0) {
|
} else if (this->numCheckpointsAheadOfPlayer == 0) {
|
||||||
s16 temp_v0_3 = this->actor.yawTowardsPlayer - this->actor.world.rot.y;
|
s16 temp_v0_3 = this->actor.yawTowardsPlayer - this->actor.world.rot.y;
|
||||||
|
|
||||||
if ((ABS_ALT(temp_v0_3) > 0x4000) || (this->unk_326 > 0)) {
|
if ((ABS_ALT(temp_v0_3) > 0x4000) || (this->unk_326 > 0)) {
|
||||||
|
|
@ -489,7 +489,7 @@ s32 func_80BF47AC(EnRg* this, GlobalContext* globalCtx) {
|
||||||
} else {
|
} else {
|
||||||
phi_f0 = phi_f2 * 0.94f;
|
phi_f0 = phi_f2 * 0.94f;
|
||||||
}
|
}
|
||||||
} else if (this->unk_348 == -1) {
|
} else if (this->numCheckpointsAheadOfPlayer == -1) {
|
||||||
phi_f0 = phi_f2 * 1.6f;
|
phi_f0 = phi_f2 * 1.6f;
|
||||||
} else {
|
} else {
|
||||||
phi_f0 = 2.0f * phi_f2;
|
phi_f0 = 2.0f * phi_f2;
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ typedef struct EnRg {
|
||||||
/* 0x033C */ s32 unk_33C;
|
/* 0x033C */ s32 unk_33C;
|
||||||
/* 0x0340 */ s32 unk_340;
|
/* 0x0340 */ s32 unk_340;
|
||||||
/* 0x0344 */ s32 unk_344;
|
/* 0x0344 */ s32 unk_344;
|
||||||
/* 0x0348 */ s32 unk_348;
|
/* 0x0348 */ s32 numCheckpointsAheadOfPlayer;
|
||||||
/* 0x034C */ EnRgStruct unk_34C[32];
|
/* 0x034C */ EnRgStruct unk_34C[32];
|
||||||
} EnRg; // size = 0xACC
|
} EnRg; // size = 0xACC
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9198,22 +9198,22 @@
|
||||||
0x809CEEAC:("func_809CEEAC",),
|
0x809CEEAC:("func_809CEEAC",),
|
||||||
0x809CEF0C:("BgSpdweb_Update",),
|
0x809CEF0C:("BgSpdweb_Update",),
|
||||||
0x809CEF30:("BgSpdweb_Draw",),
|
0x809CEF30:("BgSpdweb_Draw",),
|
||||||
0x809CF350:("func_809CF350",),
|
0x809CF350:("EnMttag_IsInFinishLine",),
|
||||||
0x809CF394:("func_809CF394",),
|
0x809CF394:("EnMttag_CheckPlayerCheatStatus",),
|
||||||
0x809CF444:("func_809CF444",),
|
0x809CF444:("EnMttag_AreFourRaceGoronsPresent",),
|
||||||
0x809CF4EC:("func_809CF4EC",),
|
0x809CF4EC:("EnMttag_GetCurrentCheckpoint",),
|
||||||
0x809CF67C:("func_809CF67C",),
|
0x809CF67C:("EnMttag_UpdateCheckpoints",),
|
||||||
0x809CF848:("func_809CF848",),
|
0x809CF848:("EnMttag_ExitRace",),
|
||||||
0x809CF8EC:("func_809CF8EC",),
|
0x809CF8EC:("EnMttag_ShowFalseStartMessage",),
|
||||||
0x809CF950:("func_809CF950",),
|
0x809CF950:("EnMttag_ShowCantWinMessage",),
|
||||||
0x809CF9A0:("func_809CF9A0",),
|
0x809CF9A0:("EnMttag_ShowIntroCutscene",),
|
||||||
0x809CFA00:("func_809CFA00",),
|
0x809CFA00:("EnMttag_WaitForIntroCutsceneToEnd",),
|
||||||
0x809CFA54:("func_809CFA54",),
|
0x809CFA54:("EnMttag_RaceStart",),
|
||||||
0x809CFBC4:("func_809CFBC4",),
|
0x809CFBC4:("EnMttag_IsAnyRaceGoronOverFinishLine",),
|
||||||
0x809CFC38:("func_809CFC38",),
|
0x809CFC38:("EnMttag_Race",),
|
||||||
0x809CFD98:("func_809CFD98",),
|
0x809CFD98:("EnMttag_RaceFinish",),
|
||||||
0x809CFE28:("func_809CFE28",),
|
0x809CFE28:("EnMttag_PotentiallyRestartRace",),
|
||||||
0x809CFF94:("func_809CFF94",),
|
0x809CFF94:("EnMttag_HandleCantWinChoice",),
|
||||||
0x809D0090:("EnMttag_Init",),
|
0x809D0090:("EnMttag_Init",),
|
||||||
0x809D0138:("EnMttag_Destroy",),
|
0x809D0138:("EnMttag_Destroy",),
|
||||||
0x809D0168:("EnMttag_Update",),
|
0x809D0168:("EnMttag_Update",),
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue