data/claims/mm.yamlThe Legend of Zelda: Majora's Mask (the zeldaret decompilation, zeldaret/mm)
In the N64 US 1.0 code the clock is a 16-bit count in which a day is 0x10000 units, so a game minute is about 45.5 units (computed; the macro CLOCK_TIME_MINUTE truncates it to 45). Each play update, Environment_UpdateTime adds the time speed R_TIME_SPEED and then, if that speed isn't 0, the save's timeSpeedOffset. It adds only when time isn't stopped, the game isn't paused or over, no textbox is open (or its message id is 0xF7, 0x20D2, 0x140C, or 0x100 through 0x200, or the game mode is the end credits), the debug frame advance (FrameAdvance_IsEnabled) is off, no transition is under way (transitionMode off, or a game mode other than normal) and none has been triggered, and no cutscene runs. From 18:00 until 6:00 it sets the save's isNight flag. mm/clock
About: The Legend of Zelda: Majora's Mask Time of day
Value
{
"unitsPerDay": 65536,
"unitsPerMinute": 45.5,
"nightFrom": "18:00",
"nightUntil": "6:00"
}include/macros.h#L23-L26#define CLOCK_TIME(hr, min) (s32)(((hr) * 60 + (min)) * 0x10000 / (24 * 60))
#define CLOCK_TIME_MINUTE (CLOCK_TIME(0, 1))
#define CLOCK_TIME_HOUR (CLOCK_TIME(1, 0))
#define DAY_LENGTH (CLOCK_TIME(24, 0))mm: a decompilation of The Legend of Zelda: Majora's Mask (zeldaret/mm) · include/z64save.h#L334-L338/* 0x0C */ u16 time; // "zelda_time"
…
/* 0x10 */ s32 isNight; // "asahiru_fg"
/* 0x14 */ s32 timeSpeedOffset; // "change_zelda_time"
/* 0x18 */ s32 day; // "totalday"mm: a decompilation of The Legend of Zelda: Majora's Mask (zeldaret/mm) · src/code/z_kankyo.c#L1203-L1214if (!sEnvIsTimeStopped && (pauseCtx->state == PAUSE_STATE_OFF) && (gameOverCtx->state == GAMEOVER_INACTIVE)) {
if ((msgCtx->msgMode == MSGMODE_NONE) || (msgCtx->currentTextId == 0xF7) || (msgCtx->currentTextId == 0x20D2) ||
(msgCtx->currentTextId == 0x140C) ||
((msgCtx->currentTextId >= 0x100) && (msgCtx->currentTextId <= 0x200)) ||
(gSaveContext.gameMode == GAMEMODE_END_CREDITS)) {
if (!FrameAdvance_IsEnabled(play) &&
((play->transitionMode == TRANS_MODE_OFF) || (gSaveContext.gameMode != GAMEMODE_NORMAL))) {
if (play->transitionTrigger == TRANS_TRIGGER_OFF) {
if ((CutsceneManager_GetCurrentCsId() == CS_ID_NONE) && !Play_InCsMode(play)) {
gSaveContext.save.time = CURRENT_TIME + (u16)R_TIME_SPEED;
if (R_TIME_SPEED != 0) {
gSaveContext.save.time = CURRENT_TIME + (u16)((void)0, gSaveContext.save.timeSpeedOffset);mm: a decompilation of The Legend of Zelda: Majora's Mask (zeldaret/mm) · src/code/z_kankyo.c#L1227-L1230time = CURRENT_TIME;
if ((time >= CLOCK_TIME(18, 0)) || (time < CLOCK_TIME(6, 0))) {
gSaveContext.save.isNight = true;mm: a decompilation of The Legend of Zelda: Majora's Mask (zeldaret/mm) · src/code/z_kankyo.c#L1774-L1779if (pauseCtx->state == PAUSE_STATE_OFF) {
…
Environment_UpdateTime(play, envCtx, pauseCtx, msgCtx, gameOverCtx);mm: a decompilation of The Legend of Zelda: Majora's Mask (zeldaret/mm) · src/code/z_play.c#L1051Environment_Update(this, &this->envCtx, &this->lightCtx, &this->pauseCtx, &this->msgCtx, &this->gameOverCtx,Checked by claude, 2026-10-22 — read Environment_UpdateTime (z_kankyo.c L1199-L1235), its call in Environment_Update (L1767), and that call in Play_UpdateMain (z_play.c L920); neither file has a version #if; the u16 time wraps each day, and the day counter is a separate fieldFrameAdvance_IsEnabled is a debug feature. What the message ids that let time run are (the 0x100-0x200 range and three others) is in text data the atlas hasn't read. The day counter changes in the three-day events actor at dawn (see mm/moon-crash).
Cited by: The Legend of Zelda: Majora's Mask
In the N64 US 1.0 code each scene's header sets its time speed, and a header value of 0xFF gives 0, a scene where time stands still. While Link's ocarina slot is empty, a nonzero scene speed becomes 5, under a decompilers' comment that reads "Increase time speed during first cycle"; the scene's speed becomes R_TIME_SPEED only while Sun's Song is inactive. The Inverted Song of Time is offered only where the scene's restriction flags allow it (otherwise text 0x1B95 and nothing) and the speed isn't 0; accepting it sets the offset to −2, or back to 0 if it was already set, and the update adds the offset only while the speed isn't 0. The game updates once every 3 video frames, set by GameState_SetFramerateDivisor(3) into a field whose comment reads "how many VIs should elapse before next swap". Assuming NTSC's 59.94 video frames a second (the atlas's assumption, not in the code), that is about 20 updates a second, and at speed 5 a game minute takes about 0.46 s, a day about 656 s, and the three days about 33 minutes (computed). The usual speed of a scene is in its header, ROM data the decompilation doesn't hold. mm/time-speed
About: The Legend of Zelda: Majora's Mask Time of day
Value
{
"firstCycleSpeed": 5,
"invertedOffset": -2,
"videoFramesPerUpdate": 3,
"assumedVideoHz": 59.94,
"firstCycleDaySeconds": 656,
"firstCycleThreeDaysMinutes": 32.8
}src/code/z_scene.c#L393-L406if (cmd->timeSettings.timeSpeed != 0xFF) {
play->envCtx.sceneTimeSpeed = cmd->timeSettings.timeSpeed;
} else {
play->envCtx.sceneTimeSpeed = 0;
}
// Increase time speed during first cycle
if ((gSaveContext.save.saveInfo.inventory.items[SLOT_OCARINA] == ITEM_NONE) && (play->envCtx.sceneTimeSpeed != 0)) {
play->envCtx.sceneTimeSpeed = 5;
}
if (gSaveContext.sunsSongState == SUNSSONG_INACTIVE) {
…mm: a decompilation of The Legend of Zelda: Majora's Mask (zeldaret/mm) · src/code/z_message.c#L5890-L5906} else if (sLastPlayedSong == OCARINA_SONG_INVERTED_TIME) {
if (interfaceCtx->restrictions.invSongOfTime == 0) {
if (R_TIME_SPEED != 0) {
…
play->msgCtx.ocarinaMode = OCARINA_MODE_PROCESS_INVERTED_TIME;
} else {
Message_StartTextbox(play, 0x1B8B, NULL);
play->msgCtx.ocarinaMode = OCARINA_MODE_END;
}
} else {
sLastPlayedSong = 0xFF;
Message_StartTextbox(play, 0x1B95, NULL);
…mm: a decompilation of The Legend of Zelda: Majora's Mask (zeldaret/mm) · src/code/z_kankyo.c#L1212-L1214gSaveContext.save.time = CURRENT_TIME + (u16)R_TIME_SPEED;
if (R_TIME_SPEED != 0) {
gSaveContext.save.time = CURRENT_TIME + (u16)((void)0, gSaveContext.save.timeSpeedOffset);mm: a decompilation of The Legend of Zelda: Majora's Mask (zeldaret/mm) · src/code/z_message.c#L5683-L5694} else if ((msgCtx->textboxEndType == TEXTBOX_ENDTYPE_TWO_CHOICE) &&
(play->msgCtx.ocarinaMode == OCARINA_MODE_PROCESS_INVERTED_TIME)) {
if (Message_ShouldAdvance(play)) {
if (msgCtx->choiceIndex == 0) {
Audio_PlaySfx_MessageDecide();
if (gSaveContext.save.timeSpeedOffset == 0) {
play->msgCtx.ocarinaMode = OCARINA_MODE_APPLY_INV_SOT_SLOW;
gSaveContext.save.timeSpeedOffset = -2;
} else {
play->msgCtx.ocarinaMode = OCARINA_MODE_APPLY_INV_SOT_FAST;
gSaveContext.save.timeSpeedOffset = 0;
}mm: a decompilation of The Legend of Zelda: Majora's Mask (zeldaret/mm) · src/code/game.c#L40-L43void GameState_SetFramerateDivisor(GameState* gameState, s32 divisor) {
R_UPDATE_RATE = divisor;
gameState->framerateDivisor = divisor;
GameState_UpdateFramerateDivisors(divisor);mm: a decompilation of The Legend of Zelda: Majora's Mask (zeldaret/mm) · src/code/game.c#L243GameState_SetFramerateDivisor(gameState, 3);mm: a decompilation of The Legend of Zelda: Majora's Mask (zeldaret/mm) · src/code/graph.c#L236cfb->updateRate = gameState->framerateDivisor;mm: a decompilation of The Legend of Zelda: Majora's Mask (zeldaret/mm) · include/scheduler.h#L25/* 0x11 */ s8 updateRate; // how many VIs should elapse before next swapChecked by claude, 2026-10-22 — read Scene_CommandTimeSettings (z_scene.c L387-L420), the ocarina-song dispatch in Message_Update (z_message.c L5881-L5925) and its two-choice handler (L5683-L5700), and GameState_Init (game.c L243); the divisor is set to 3 again as play transitions end (z_play.c L736) and to 2 only in pause and map paths (z_kaleido_setup.c L141, z_message.c L5801); no version #if in any cited range. Computed: 0x10000 / 5 = 13,107 updates a day, / 20 = 655 s; three days 32.8 minutesSun's Song sets R_TIME_SPEED to 400 until the next dawn or dusk, and only where the scene speed isn't 0 (z_parameter.c L7217-L7242). With the offset at −2 an update adds the scene speed minus 2. NTSC's video rate of about 59.94 Hz is the atlas's assumption; the code counts video frames, not seconds.
Cited by: The Legend of Zelda: Majora's Mask
In the N64 US 1.0 code, answering yes to the Song of Time's prompt runs Sram_SaveEndOfCycle, whose comment says it is "Used by Song of Time (when clicking "Yes") and (indirectly) by the "Dawn of the New Day" cutscene". The function sets the day counter to 0 and the clock one unit before 6:00, clears the Inverted Song's offset, and adds one to a reset count capped at 999, but on the song's path the message code saves the day, the time, and the cutscene index before the call and puts them back right after it; where the cycle returns to the first dawn is untraced. It sets the rupees Link carries to 0 and every ammo count to 0 except the pictograph box's, and turns any bottle holding an item from ITEM_POTION_RED through ITEM_OBABA_DRINK back into an empty bottle. It sets a sword at or below the Razor Sword back to the Kokiri Sword (or the Gilded Sword if a stolen-item slot holds it), removes dungeon keys, boss keys, stray fairies, and the pictograph, and clears the event-info flags. Scene flags (switches, chests, collectibles) and week-event flags survive only where a per-scene or per-flag persistence table marks them. Among the rest, it resets five minigame high scores and the three Deku Playground scores, but not the bank balance, which is kept in the high-score slot HS_BANK_RUPEES. mm/song-of-time-reset
About: The Legend of Zelda: Majora's Mask Time of day
Codemm: a decompilation of The Legend of Zelda: Majora's Mask (zeldaret/mm) ·src/code/z_message.c#L5881-L5884if (sLastPlayedSong == OCARINA_SONG_TIME) {
if (interfaceCtx->restrictions.songOfTime == 0) {
Message_StartTextbox(play, 0x1B8A, NULL);
play->msgCtx.ocarinaMode = OCARINA_MODE_PROCESS_SOT;mm: a decompilation of The Legend of Zelda: Majora's Mask (zeldaret/mm) · src/code/z_message.c#L5668-L5673} else if ((msgCtx->textboxEndType == TEXTBOX_ENDTYPE_TWO_CHOICE) &&
(play->msgCtx.ocarinaMode == OCARINA_MODE_PROCESS_SOT)) {
if (Message_ShouldAdvance(play)) {
if (msgCtx->choiceIndex == 0) {
Audio_PlaySfx_MessageDecide();
msgCtx->msgMode = MSGMODE_NEW_CYCLE_0;mm: a decompilation of The Legend of Zelda: Majora's Mask (zeldaret/mm) · src/code/z_message.c#L6022-L6034case MSGMODE_NEW_CYCLE_0:
play->state.unk_A3 = 1;
sp44 = gSaveContext.save.cutsceneIndex;
sp3E = CURRENT_TIME;
sp40 = gSaveContext.save.day;
Sram_SaveEndOfCycle(play);
gSaveContext.timerStates[TIMER_ID_MOON_CRASH] = TIMER_STATE_OFF;
func_8014546C(&play->sramCtx);
gSaveContext.save.day = sp40;
gSaveContext.save.time = sp3E;
…mm: a decompilation of The Legend of Zelda: Majora's Mask (zeldaret/mm) · src/code/z_sram_NES.c#L427-L444* Used by Song of Time (when clicking "Yes") and (indirectly) by the "Dawn of the New Day" cutscene
*/
void Sram_SaveEndOfCycle(PlayState* play) {
…
gSaveContext.save.timeSpeedOffset = 0;
gSaveContext.save.eventDayCount = 0;
gSaveContext.save.day = 0;
gSaveContext.save.time = CLOCK_TIME(6, 0) - 1;
gSaveContext.save.saveInfo.playerData.threeDayResetCount++;
if (gSaveContext.save.saveInfo.playerData.threeDayResetCount > 999) {
gSaveContext.save.saveInfo.playerData.threeDayResetCount = 999;mm: a decompilation of The Legend of Zelda: Majora's Mask (zeldaret/mm) · src/code/z_sram_NES.c#L450-L464play->actorCtx.sceneFlags.chest &= sPersistentCycleSceneFlags[sceneId].chest;
…
for (i = 0; i < SCENE_MAX; i++) {
gSaveContext.cycleSceneFlags[i].switch0 =
((void)0, gSaveContext.cycleSceneFlags[i].switch0) & sPersistentCycleSceneFlags[i].switch0;mm: a decompilation of The Legend of Zelda: Majora's Mask (zeldaret/mm) · src/code/z_sram_NES.c#L486-L499for (i = 0; i < ARRAY_COUNT(sPersistentCycleWeekEventRegs); i++) {
u16 isPersistentBits = sPersistentCycleWeekEventRegs[i];
…
if (!(isPersistentBits & 3)) {
gSaveContext.save.saveInfo.weekEventReg[i] =
((void)0, gSaveContext.save.saveInfo.weekEventReg[i]) & (0xFF ^ sBitFlags8[j]);
…
for (i = 0; i < ARRAY_COUNT(gSaveContext.eventInf); i++) {
gSaveContext.eventInf[i] = 0;mm: a decompilation of The Legend of Zelda: Majora's Mask (zeldaret/mm) · src/code/z_sram_NES.c#L536-L556for (i = 0; i < ITEM_NUM_SLOTS; i++) {
if (gAmmoItems[i] != ITEM_NONE) {
if ((gSaveContext.save.saveInfo.inventory.items[i] != ITEM_NONE) && (i != SLOT_PICTOGRAPH_BOX)) {
item = gSaveContext.save.saveInfo.inventory.items[i];
AMMO(item) = 0;
…
for (i = SLOT_BOTTLE_1; i <= SLOT_BOTTLE_6; i++) {
// Check for all bottled items
if (gSaveContext.save.saveInfo.inventory.items[i] >= ITEM_POTION_RED) {
if (gSaveContext.save.saveInfo.inventory.items[i] <= ITEM_OBABA_DRINK) {
…
gSaveContext.save.saveInfo.inventory.items[i] = ITEM_BOTTLE;mm: a decompilation of The Legend of Zelda: Majora's Mask (zeldaret/mm) · src/code/z_sram_NES.c#L561-L573REMOVE_QUEST_ITEM(QUEST_PICTOGRAPH);
…
if (GET_CUR_EQUIP_VALUE(EQUIP_TYPE_SWORD) <= EQUIP_VALUE_SWORD_RAZOR) {
SET_EQUIP_VALUE(EQUIP_TYPE_SWORD, EQUIP_VALUE_SWORD_KOKIRI);
if (CUR_FORM == 0) {
if ((STOLEN_ITEM_1 >= ITEM_SWORD_GILDED) || (STOLEN_ITEM_2 >= ITEM_SWORD_GILDED)) {
CUR_FORM_EQUIP(EQUIP_SLOT_B) = ITEM_SWORD_GILDED;
SET_EQUIP_VALUE(EQUIP_TYPE_SWORD, EQUIP_VALUE_SWORD_GILDED);mm: a decompilation of The Legend of Zelda: Majora's Mask (zeldaret/mm) · src/code/z_sram_NES.c#L637-L645Sram_ClearHighscores();
for (i = 0; i < 8; i++) {
gSaveContext.save.saveInfo.inventory.dungeonItems[i] &= (u8)~1; // remove boss key
DUNGEON_KEY_COUNT(i) = 0;
gSaveContext.save.saveInfo.inventory.strayFairies[i] = 0;
}
gSaveContext.save.saveInfo.playerData.rupees = 0;mm: a decompilation of The Legend of Zelda: Majora's Mask (zeldaret/mm) · src/code/z_sram_NES.c#L404-L413void Sram_ClearHighscores(void) {
HS_SET_BOAT_ARCHERY_HIGH_SCORE(19);
HS_SET_HIGH_SCORE_3_LOWER(10);
HS_SET_HORSE_BACK_BALLOON_TIME(SECONDS_TO_TIMER(60));
HS_SET_TOWN_SHOOTING_GALLERY_HIGH_SCORE(39);
HS_SET_SWAMP_SHOOTING_GALLERY_HIGH_SCORE(10);
gSaveContext.save.saveInfo.dekuPlaygroundHighScores[0] = SECONDS_TO_TIMER(75);
gSaveContext.save.saveInfo.dekuPlaygroundHighScores[1] = SECONDS_TO_TIMER(75);
gSaveContext.save.saveInfo.dekuPlaygroundHighScores[2] = SECONDS_TO_TIMER(76);mm: a decompilation of The Legend of Zelda: Majora's Mask (zeldaret/mm) · include/z64save.h#L613-L614#define HS_GET_BANK_RUPEES() (HIGH_SCORE(HS_BANK_RUPEES) & 0xFFFF)
#define HS_SET_BANK_RUPEES(rupees) (HIGH_SCORE(HS_BANK_RUPEES) = ((HIGH_SCORE(HS_BANK_RUPEES) & 0xFFFF0000) | (rupees)))mm: a decompilation of The Legend of Zelda: Majora's Mask (zeldaret/mm) · spec/spec#L694-L696#if MM_VERSION >= N64_US
include "$(BUILD_DIR)/src/code/z_sram_NES.o"
#endifChecked by claude, 2026-10-22 — read Sram_SaveEndOfCycle whole (z_sram_NES.c L430-L653) and Sram_ClearHighscores (L404-L414); the five HS_SET macros write the slots HS_BOAT_ARCHERY (twice, upper and lower halves), HS_HORSE_BACK_BALLOON, and HS_SHOOTING_GALLERY (both halves) (z64save.h L616-L630), never HS_BANK_RUPEES (slot 0); the Deku Playground's three high scores are reset as well (L411-L413); the US build links z_sram_NES.c, which has no version #ifBefore it zeroes them, the function sets event-info flags recording which of rupees, bombs, nuts, sticks, and arrows were lost (L502-L534). It also returns bottles and the Great Fairy's Sword from the stolen-item slots (L587-L612), calls Inventory_DeleteItem on three trade-item slots and takes trade items off the C buttons (L614-L623), clears skulltula counts, the alien record, the powder keg timer, and horse data (L625-L652), and deletes the Fierce Deity's Mask if a week-event flag is set (L482-L484). Health is raised to at least 0x30 (L563-L565). Between the call and the restore, the message code calls func_8014546C (L6030), and it then starts the flash write (L6036-L6040); whether the flash copy takes the reset day and time is untraced.
Cited by: The Legend of Zelda: Majora's Mask, Time of day
In the N64 US 1.0 code, accepting the Song of Double Time's prompt sets the clock to 6:00 if the save's isNight flag is set and to 18:00 if not, and turns off the moon's countdown timer; this code doesn't change the day counter. The three-day events actor (En_Test4) then sees the clock cross 6:00 and, in its no-cutscene branch, adds a day; that the jump to exactly 6:00 trips its crossing test is the atlas's reading. The song isn't offered on the third night (day 3 with isNight set) or where a scene's restriction flags forbid it. mm/double-time
About: The Legend of Zelda: Majora's Mask Time of day
Value
{
"fromNight": "6:00",
"fromDay": "18:00"
}src/overlays/actors/ovl_En_Test4/z_en_test4.c#L419-L429curTimeUntilTransition = CURRENT_TIME - transitionTime;
prevTimeUntilTransition = this->prevTime - transitionTime;
…
if ((curTimeUntilTransition * prevTimeUntilTransition) <= 0) {mm: a decompilation of The Legend of Zelda: Majora's Mask (zeldaret/mm) · src/overlays/actors/ovl_En_Test4/z_en_test4.c#L115-L118} else if ((sCsIdList[this->daytimeIndex] <= CS_ID_NONE) || (play->actorCtx.flags & ACTORCTX_FLAG_TELESCOPE_ON)) {
// Previously night, turning day, without a cutscene
Sram_IncrementDay();
gSaveContext.save.time = CLOCK_TIME(6, 0);mm: a decompilation of The Legend of Zelda: Majora's Mask (zeldaret/mm) · src/code/z_message.c#L5908-L5919} else if (sLastPlayedSong == OCARINA_SONG_DOUBLE_TIME) {
if (interfaceCtx->restrictions.songOfDoubleTime == 0) {
if ((CURRENT_DAY != 3) || (gSaveContext.save.isNight == 0)) {
if (gSaveContext.save.isNight) {
Message_StartTextbox(play, sDoubleTimeSkipToDayTextIds[CURRENT_DAY - 1], NULL);
} else {
Message_StartTextbox(play, sDoubleTimeSkipToNightTextIds[CURRENT_DAY - 1], NULL);
}
play->msgCtx.ocarinaMode = OCARINA_MODE_PROCESS_DOUBLE_TIME;
} else {
Message_StartTextbox(play, 0x1B94, NULL);
play->msgCtx.ocarinaMode = OCARINA_MODE_END;mm: a decompilation of The Legend of Zelda: Majora's Mask (zeldaret/mm) · src/code/z_message.c#L5702-L5713} else if ((msgCtx->textboxEndType == TEXTBOX_ENDTYPE_TWO_CHOICE) &&
(play->msgCtx.ocarinaMode == OCARINA_MODE_PROCESS_DOUBLE_TIME)) {
if (Message_ShouldAdvance(play)) {
if (msgCtx->choiceIndex == 0) {
Audio_PlaySfx_MessageDecide();
if (gSaveContext.save.isNight != 0) {
gSaveContext.save.time = CLOCK_TIME(6, 0);
} else {
gSaveContext.save.time = CLOCK_TIME(18, 0);
}
play->msgCtx.ocarinaMode = OCARINA_MODE_APPLY_DOUBLE_SOT;
gSaveContext.timerStates[TIMER_ID_MOON_CRASH] = TIMER_STATE_OFF;mm: a decompilation of The Legend of Zelda: Majora's Mask (zeldaret/mm) · src/code/z_message.c#L5305-L5313s16 sDoubleTimeSkipToNightTextIds[] = {
0x1B91, // Night of 1st day
0x1B90, // Night of 2nd day
0x1B8F, // Night of final day
};
s16 sDoubleTimeSkipToDayTextIds[] = {
0x1B92, // Dawn of 2nd day
0x1B8E, // Dawn of final day
};Checked by claude, 2026-10-22 — read the song dispatch (z_message.c L5881-L5925) and the two-choice handler (L5702-L5719); neither range is inside one of z_message.c's version blocks (L18-L85, L3122-L4162, L6142-L6155 hold them); isNight is set from the clock each update (z_kankyo.c L1229-L1230)The prompt's text ids are picked by day, and the decompilers' comments name what each says. The day counter moves when the three-day events actor sees the clock cross 6:00 (see mm/moon-crash); that the jump to 6:00 trips it is the atlas's reading of the check at z_en_test4.c L429, which passes when the clock lands exactly on the transition time.
Cited by: The Legend of Zelda: Majora's Mask
In the N64 US 1.0 code, the actor En_Test4, which its file's comment describes as "Three-Day Events: day-night transitions", compares each update the clock with the next transition time (6:00 by night, 18:00 by day) by multiplying the 16-bit differences of the current and previous times from it and treating a product of 0 or less as a crossing (the atlas's reading of the test), unless a transition, a cutscene, setup actors, a room load, or the debug camera is under way. When the clock crosses 6:00 with the day counter at 3, it calls Interface_StartMoonCrash, which sets the day to 4 and the clock to 6:00 plus 10 units and starts a white fade to a Termina Field entrance. On days 1 and 2 the same crossing adds a day when no transition cutscene is set or the telescope is on; the other branch (z_en_test4.c L446-L470), which sets a flag the decomp names EVENTINF_TRIGGER_DAYTELOP for the Dawn of a New Day screen, is untraced. Play_Init spawns this actor in dungeon and boss rooms on any day but 0; elsewhere it comes from scene data the atlas hasn't read. mm/moon-crash
About: The Legend of Zelda: Majora's Mask Time of day
Value
{
"crashDay": 3,
"crashTime": "6:00",
"dayAfter": 4
}src/overlays/actors/ovl_En_Test4/z_en_test4.c#L4 * Description: Three-Day Events: day-night transitions, shrink screen before next day,mm: a decompilation of The Legend of Zelda: Majora's Mask (zeldaret/mm) · src/overlays/actors/ovl_En_Test4/z_en_test4.c#L404-L445void EnTest4_HandleEvents(EnTest4* this, PlayState* play) {
static u16 sDayNightTransitionTimes[THREEDAY_DAYTIME_MAX] = {
CLOCK_TIME(6, 0), // THREEDAY_DAYTIME_NIGHT
CLOCK_TIME(18, 0), // THREEDAY_DAYTIME_DAY
};
…
if ((play->transitionMode == TRANS_MODE_OFF) && !Play_InCsMode(play) && (play->numSetupActors <= 0) &&
(play->roomCtx.status == 0) && !Play_IsDebugCamEnabled()) {
u16 transitionTime = sDayNightTransitionTimes[this->daytimeIndex];
…
curTimeUntilTransition = CURRENT_TIME - transitionTime;
prevTimeUntilTransition = this->prevTime - transitionTime;
…mm: a decompilation of The Legend of Zelda: Majora's Mask (zeldaret/mm) · src/overlays/actors/ovl_En_Test4/z_en_test4.c#L446-L470} else if (((sCsIdList[this->daytimeIndex] <= CS_ID_NONE) ||
(play->actorCtx.flags & ACTORCTX_FLAG_TELESCOPE_ON)) &&
(CURRENT_DAY != 3)) {
// Turn day without cutscene
EnTest4_HandleDayNightSwap(this, play);
} else {
…
SET_EVENTINF(EVENTINF_TRIGGER_DAYTELOP);mm: a decompilation of The Legend of Zelda: Majora's Mask (zeldaret/mm) · src/overlays/actors/ovl_En_Test4/z_en_test4.c#L115-L118} else if ((sCsIdList[this->daytimeIndex] <= CS_ID_NONE) || (play->actorCtx.flags & ACTORCTX_FLAG_TELESCOPE_ON)) {
// Previously night, turning day, without a cutscene
Sram_IncrementDay();
gSaveContext.save.time = CLOCK_TIME(6, 0);mm: a decompilation of The Legend of Zelda: Majora's Mask (zeldaret/mm) · src/code/z_sram_NES.c#L655-L658void Sram_IncrementDay(void) {
if (CURRENT_DAY <= 3) {
gSaveContext.save.day++;
gSaveContext.save.eventDayCount++;mm: a decompilation of The Legend of Zelda: Majora's Mask (zeldaret/mm) · src/code/z_parameter.c#L5612-L5623void Interface_StartMoonCrash(PlayState* play) {
…
gSaveContext.save.day = 4;
gSaveContext.save.eventDayCount = 4;
gSaveContext.save.time = CLOCK_TIME(6, 0) + 10;
play->nextEntrance = ENTRANCE(TERMINA_FIELD, 12);
gSaveContext.nextCutsceneIndex = 0;
play->transitionTrigger = TRANS_TRIGGER_START;
play->transitionType = TRANS_TYPE_FADE_WHITE;mm: a decompilation of The Legend of Zelda: Majora's Mask (zeldaret/mm) · src/code/z_play.c#L2316-L2318if ((CURRENT_DAY != 0) &&
((this->roomCtx.curRoom.type == ROOM_TYPE_DUNGEON) || (this->roomCtx.curRoom.type == ROOM_TYPE_BOSS))) {
Actor_Spawn(&this->actorCtx, this, ACTOR_EN_TEST4, 0.0f, 0.0f, 0.0f, 0, 0, 0, 0);Checked by claude, 2026-10-22 — read EnTest4_HandleEvents (z_en_test4.c L404-L500), EnTest4_HandleDayNightSwap (L105-L160), Interface_StartMoonCrash (z_parameter.c L5612-L5624), and Play_Init's spawn (z_play.c L2316-L2319); none of these files has a version #if except an AVOID_UB block in z_parameter.c (L4529-L4531) outside the rangeOn days 1 and 2 the dawn either swaps day and night in place (no transition cutscene, or the telescope on) or starts the Dawn of a New Day screen (L446-L470), which isn't traced here. What happens at Termina Field entrance 12 on day 4 is cutscene data. The products are of 16-bit differences, so a wrap past midnight reads as a sign change only near the transition time, the atlas's reading.
Cited by: The Legend of Zelda: Majora's Mask, Time of day