#include "global.h" #include "vt.h" #include #include "soh/Enhancements/gameconsole.h" #include "soh/frame_interpolation.h" #include "soh/Enhancements/debugconsole.h" #include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/Enhancements/randomizer/randomizer_entrance.h" #include #include #include "soh/Enhancements/enhancementTypes.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include #include #include void* D_8012D1F0 = NULL; //UNK_TYPE D_8012D1F4 = 0; // unused Input* D_8012D1F8 = NULL; TransitionUnk sTrnsnUnk; s32 gTrnsnUnkState; VisMono D_80161498; Color_RGBA8_u32 D_801614B0; FaultClient D_801614B8; s16 D_801614C8; #if 0 u64 D_801614D0[0xA00]; #endif PlayState* gPlayState; s16 firstInit = 0; s16 gEnPartnerId; void OTRPlay_SpawnScene(PlayState* play, s32 sceneNum, s32 spawn); void enableBetaQuest(); void disableBetaQuest(); void func_800BC450(PlayState* play) { Camera_ChangeDataIdx(GET_ACTIVE_CAM(play), play->unk_1242B - 1); } void func_800BC490(PlayState* play, s16 point) { assert(point == 1 || point == 2); play->unk_1242B = point; if ((YREG(15) != 0x10) && (gSaveContext.cutsceneIndex < 0xFFF0)) { Audio_PlaySoundGeneral((point == 1) ? NA_SE_SY_CAMERA_ZOOM_DOWN : NA_SE_SY_CAMERA_ZOOM_UP, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); } func_800BC450(play); } s32 func_800BC56C(PlayState* play, s16 arg1) { return (arg1 == play->unk_1242B); } // original name: "Game_play_shop_pr_vr_switch_set" void func_800BC590(PlayState* play) { osSyncPrintf("Game_play_shop_pr_vr_switch_set()\n"); if (YREG(15) == 0x10) { play->unk_1242B = 2; } } void Gameplay_SetupTransition(PlayState* play, s32 transitionType) { TransitionContext* transitionCtx = &play->transitionCtx; memset(transitionCtx,0, sizeof(TransitionContext)); transitionCtx->transitionType = transitionType; // Circle Transition Types if ((transitionCtx->transitionType >> 5) == 1) { transitionCtx->init = TransitionCircle_Init; transitionCtx->destroy = TransitionCircle_Destroy; transitionCtx->start = TransitionCircle_Start; transitionCtx->isDone = TransitionCircle_IsDone; transitionCtx->draw = TransitionCircle_Draw; transitionCtx->update = TransitionCircle_Update; transitionCtx->setType = TransitionCircle_SetType; transitionCtx->setColor = TransitionCircle_SetColor; transitionCtx->setEnvColor = TransitionCircle_SetEnvColor; } else { switch (transitionCtx->transitionType) { case TRANS_TYPE_TRIFORCE: transitionCtx->init = TransitionTriforce_Init; transitionCtx->destroy = TransitionTriforce_Destroy; transitionCtx->start = TransitionTriforce_Start; transitionCtx->isDone = TransitionTriforce_IsDone; transitionCtx->draw = TransitionTriforce_Draw; transitionCtx->update = TransitionTriforce_Update; transitionCtx->setType = TransitionTriforce_SetType; transitionCtx->setColor = TransitionTriforce_SetColor; transitionCtx->setEnvColor = NULL; break; case TRANS_TYPE_WIPE: case TRANS_TYPE_WIPE_FAST: transitionCtx->init = TransitionWipe_Init; transitionCtx->destroy = TransitionWipe_Destroy; transitionCtx->start = TransitionWipe_Start; transitionCtx->isDone = TransitionWipe_IsDone; transitionCtx->draw = TransitionWipe_Draw; transitionCtx->update = TransitionWipe_Update; transitionCtx->setType = TransitionWipe_SetType; transitionCtx->setColor = TransitionWipe_SetColor; transitionCtx->setEnvColor = NULL; break; case TRANS_TYPE_FADE_BLACK: case TRANS_TYPE_FADE_WHITE: case TRANS_TYPE_FADE_BLACK_FAST: case TRANS_TYPE_FADE_WHITE_FAST: case TRANS_TYPE_FADE_BLACK_SLOW: case TRANS_TYPE_FADE_WHITE_SLOW: case TRANS_TYPE_FADE_WHITE_CS_DELAYED: case TRANS_TYPE_FADE_WHITE_INSTANT: case TRANS_TYPE_FADE_GREEN: case TRANS_TYPE_FADE_BLUE: transitionCtx->init = TransitionFade_Init; transitionCtx->destroy = TransitionFade_Destroy; transitionCtx->start = TransitionFade_Start; transitionCtx->isDone = TransitionFade_IsDone; transitionCtx->draw = TransitionFade_Draw; transitionCtx->update = TransitionFade_Update; transitionCtx->setType = TransitionFade_SetType; transitionCtx->setColor = TransitionFade_SetColor; transitionCtx->setEnvColor = NULL; break; case TRANS_TYPE_FILL_WHITE2: case TRANS_TYPE_FILL_WHITE: play->transitionMode = TRANS_MODE_FILL_WHITE_INIT; break; case TRANS_TYPE_INSTANT: play->transitionMode = TRANS_MODE_INSTANT; break; case TRANS_TYPE_FILL_BROWN: play->transitionMode = TRANS_MODE_FILL_BROWN_INIT; break; case TRANS_TYPE_SANDSTORM_PERSIST: play->transitionMode = TRANS_MODE_SANDSTORM_INIT; break; case TRANS_TYPE_SANDSTORM_END: play->transitionMode = TRANS_MODE_SANDSTORM_END_INIT; break; case TRANS_TYPE_CS_BLACK_FILL: play->transitionMode = TRANS_MODE_CS_BLACK_FILL_INIT; break; default: Fault_AddHungupAndCrash(__FILE__, __LINE__); break; } } } void func_800BC88C(PlayState* play) { play->transitionCtx.transitionType = -1; } Gfx* Play_SetFog(PlayState* play, Gfx* gfx) { return Gfx_SetFog2(gfx, play->lightCtx.fogColor[0], play->lightCtx.fogColor[1], play->lightCtx.fogColor[2], 0, play->lightCtx.fogNear, 1000); } void Play_Destroy(GameState* thisx) { PlayState* play = (PlayState*)thisx; Player* player = GET_PLAYER(play); GameInteractor_ExecuteOnPlayDestroy(); // Only initialize the frame counter when exiting the title screen if (gSaveContext.fileNum == 0xFF) { play->gameplayFrames = 0; } // In ER, remove link from epona when entering somewhere that doesn't support epona if (IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_OVERWORLD_ENTRANCES)) { Entrance_HandleEponaState(); } play->state.gfxCtx->callback = NULL; play->state.gfxCtx->callbackParam = 0; SREG(91) = 0; R_PAUSE_MENU_MODE = 0; PreRender_Destroy(&play->pauseBgPreRender); Effect_DeleteAll(play); EffectSs_ClearAll(play); CollisionCheck_DestroyContext(play, &play->colChkCtx); if (gTrnsnUnkState == 3) { TransitionUnk_Destroy(&sTrnsnUnk); gTrnsnUnkState = 0; } if (play->transitionMode == TRANS_MODE_INSTANCE_RUNNING) { play->transitionCtx.destroy(&play->transitionCtx.data); func_800BC88C(play); play->transitionMode = TRANS_MODE_OFF; } ShrinkWindow_Destroy(); TransitionFade_Destroy(&play->transitionFade); VisMono_Destroy(&D_80161498); if (gSaveContext.linkAge != play->linkAgeOnLoad) { Inventory_SwapAgeEquipment(); Player_SetEquipmentData(play, player); } func_80031C3C(&play->actorCtx, play); func_80110990(play); KaleidoScopeCall_Destroy(play); KaleidoManager_Destroy(); ZeldaArena_Cleanup(); Fault_RemoveClient(&D_801614B8); disableBetaQuest(); gPlayState = NULL; } void GivePlayerRandoRewardSongOfTime(PlayState* play, RandomizerCheck check) { Player* player = GET_PLAYER(play); if (gSaveContext.entranceIndex == ENTR_HYRULE_FIELD_16 && player != NULL && !Player_InBlockingCsMode(play, player) && !Flags_GetTreasure(play, 0x1F) && gSaveContext.nextTransitionType == TRANS_NEXT_TYPE_DEFAULT && !gSaveContext.pendingIceTrapCount) { GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(check, RG_SONG_OF_TIME); GiveItemEntryWithoutActor(play, getItemEntry); player->pendingFlag.flagID = 0x1F; player->pendingFlag.flagType = FLAG_SCENE_TREASURE; } } void GivePlayerRandoRewardNocturne(PlayState* play, RandomizerCheck check) { Player* player = GET_PLAYER(play); if ((gSaveContext.entranceIndex == ENTR_KAKARIKO_VILLAGE_0 || gSaveContext.entranceIndex == ENTR_KAKARIKO_VILLAGE_1 || gSaveContext.entranceIndex == ENTR_KAKARIKO_VILLAGE_2) && LINK_IS_ADULT && CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST) && CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE) && CHECK_QUEST_ITEM(QUEST_MEDALLION_WATER) && player != NULL && !Player_InBlockingCsMode(play, player) && !Flags_GetEventChkInf(EVENTCHKINF_BONGO_BONGO_ESCAPED_FROM_WELL)) { GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(check, RG_NOCTURNE_OF_SHADOW); GiveItemEntryWithoutActor(play, getItemEntry); player->pendingFlag.flagID = 0xAA; player->pendingFlag.flagType = FLAG_EVENT_CHECK_INF; } } void GivePlayerRandoRewardRequiem(PlayState* play, RandomizerCheck check) { Player* player = GET_PLAYER(play); if ((gSaveContext.gameMode == 0) && (gSaveContext.respawnFlag <= 0) && (gSaveContext.cutsceneIndex < 0xFFF0)) { if ((gSaveContext.entranceIndex == ENTR_DESERT_COLOSSUS_1) && !Flags_GetEventChkInf(EVENTCHKINF_LEARNED_REQUIEM_OF_SPIRIT) && player != NULL && !Player_InBlockingCsMode(play, player)) { GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(check, RG_SONG_OF_TIME); GiveItemEntryWithoutActor(play, getItemEntry); player->pendingFlag.flagID = 0xAC; player->pendingFlag.flagType = FLAG_EVENT_CHECK_INF; } } } void GivePlayerRandoRewardMasterSword(PlayState* play, RandomizerCheck check) { Player* player = GET_PLAYER(play); if (gSaveContext.entranceIndex == ENTR_TEMPLE_OF_TIME_2 && LINK_IS_ADULT && player != NULL && !Player_InBlockingCsMode(play, player) && Randomizer_GetSettingValue(RSK_SHUFFLE_MASTER_SWORD) && !Flags_GetRandomizerInf(RAND_INF_TOT_MASTER_SWORD)) { GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(check, RG_MASTER_SWORD); GiveItemEntryWithoutActor(play, getItemEntry); player->pendingFlag.flagID = RAND_INF_TOT_MASTER_SWORD; player->pendingFlag.flagType = FLAG_RANDOMIZER_INF; } } u8 CheckStoneCount() { u8 stoneCount = 0; if (CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD)) { stoneCount++; } if (CHECK_QUEST_ITEM(QUEST_GORON_RUBY)) { stoneCount++; } if (CHECK_QUEST_ITEM(QUEST_ZORA_SAPPHIRE)) { stoneCount++; } return stoneCount; } u8 CheckMedallionCount() { u8 medallionCount = 0; if (CHECK_QUEST_ITEM(QUEST_MEDALLION_FOREST)) { medallionCount++; } if (CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE)) { medallionCount++; } if (CHECK_QUEST_ITEM(QUEST_MEDALLION_WATER)) { medallionCount++; } if (CHECK_QUEST_ITEM(QUEST_MEDALLION_SHADOW)) { medallionCount++; } if (CHECK_QUEST_ITEM(QUEST_MEDALLION_SPIRIT)) { medallionCount++; } if (CHECK_QUEST_ITEM(QUEST_MEDALLION_LIGHT)) { medallionCount++; } return medallionCount; } u8 CheckDungeonCount() { u8 dungeonCount = 0; if (Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_DEKU_TREE)) { dungeonCount++; } if (Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_DODONGOS_CAVERN)) { dungeonCount++; } if (Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_JABU_JABUS_BELLY)) { dungeonCount++; } if (Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_FOREST_TEMPLE)) { dungeonCount++; } if (Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_FIRE_TEMPLE)) { dungeonCount++; } if (Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_WATER_TEMPLE)) { dungeonCount++; } if (Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_SPIRIT_TEMPLE)) { dungeonCount++; } if (Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_SHADOW_TEMPLE)) { dungeonCount++; } return dungeonCount; } u8 CheckBridgeRewardCount() { u8 bridgeRewardCount = 0; switch (Randomizer_GetSettingValue(RSK_BRIDGE_OPTIONS)) { case RO_BRIDGE_WILDCARD_REWARD: if (Flags_GetRandomizerInf(RAND_INF_GREG_FOUND)) { bridgeRewardCount += 1; } break; case RO_BRIDGE_GREG_REWARD: if (Flags_GetRandomizerInf(RAND_INF_GREG_FOUND)) { bridgeRewardCount += 1; } break; } return bridgeRewardCount; } u8 CheckLACSRewardCount() { u8 lacsRewardCount = 0; switch (Randomizer_GetSettingValue(RSK_LACS_OPTIONS)) { case RO_LACS_WILDCARD_REWARD: if (Flags_GetRandomizerInf(RAND_INF_GREG_FOUND)) { lacsRewardCount += 1; } break; case RO_LACS_GREG_REWARD: if (Flags_GetRandomizerInf(RAND_INF_GREG_FOUND)) { lacsRewardCount += 1; } break; } return lacsRewardCount; } void GivePlayerRandoRewardZeldaLightArrowsGift(PlayState* play, RandomizerCheck check) { Player* player = GET_PLAYER(play); u8 meetsRequirements = 0; switch (Randomizer_GetSettingValue(RSK_GANONS_BOSS_KEY)) { case RO_GANON_BOSS_KEY_LACS_STONES: if ((CheckStoneCount() + CheckLACSRewardCount()) >= Randomizer_GetSettingValue(RSK_LACS_STONE_COUNT)) { meetsRequirements = true; } break; case RO_GANON_BOSS_KEY_LACS_MEDALLIONS: if ((CheckMedallionCount() + CheckLACSRewardCount()) >= Randomizer_GetSettingValue(RSK_LACS_MEDALLION_COUNT)) { meetsRequirements = true; } break; case RO_GANON_BOSS_KEY_LACS_REWARDS: if ((CheckMedallionCount() + CheckStoneCount() + CheckLACSRewardCount()) >= Randomizer_GetSettingValue(RSK_LACS_REWARD_COUNT)) { meetsRequirements = true; } break; case RO_GANON_BOSS_KEY_LACS_DUNGEONS: if ((CheckDungeonCount() + CheckLACSRewardCount()) >= Randomizer_GetSettingValue(RSK_LACS_DUNGEON_COUNT)) { meetsRequirements = true; } break; case RO_GANON_BOSS_KEY_LACS_TOKENS: if (gSaveContext.inventory.gsTokens >= Randomizer_GetSettingValue(RSK_LACS_TOKEN_COUNT)) { meetsRequirements = true; } break; default: if (CHECK_QUEST_ITEM(QUEST_MEDALLION_SPIRIT) && CHECK_QUEST_ITEM(QUEST_MEDALLION_SHADOW)) { meetsRequirements = true; } break; } if (meetsRequirements && LINK_IS_ADULT && (gEntranceTable[((void)0, gSaveContext.entranceIndex)].scene == SCENE_TEMPLE_OF_TIME) && !Flags_GetTreasure(play, 0x1E) && player != NULL && !Player_InBlockingCsMode(play, player) && play->transitionTrigger == TRANS_TRIGGER_OFF) { GetItemEntry getItem = Randomizer_GetItemFromKnownCheck(check, GI_ARROW_LIGHT); if (GiveItemEntryWithoutActor(play, getItem)) { player->pendingFlag.flagID = 0x1E; player->pendingFlag.flagType = FLAG_SCENE_TREASURE; } } } void GivePlayerRandoRewardSariaGift(PlayState* play, RandomizerCheck check) { Player* player = GET_PLAYER(play); if (gSaveContext.entranceIndex == ENTR_LOST_WOODS_9) { GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(check, RG_ZELDAS_LULLABY); if (!Flags_GetEventChkInf(EVENTCHKINF_SPOKE_TO_SARIA_ON_BRIDGE) && player != NULL && !Player_InBlockingCsMode(play, player)) { GiveItemEntryWithoutActor(play, getItemEntry); player->pendingFlag.flagType = FLAG_EVENT_CHECK_INF; player->pendingFlag.flagID = 0xC1; } } } void Play_Init(GameState* thisx) { PlayState* play = (PlayState*)thisx; GraphicsContext* gfxCtx = play->state.gfxCtx; enableBetaQuest(); gPlayState = play; //play->state.gfxCtx = NULL; uintptr_t zAlloc; uintptr_t zAllocAligned; size_t zAllocSize; Player* player; s32 playerStartCamId; s32 i; u8 tempSetupIndex; s32 pad[2]; // Skip Child Stealth when option is enabled, Zelda's Letter isn't obtained and Impa's reward hasn't been received // eventChkInf[4] & 1 = Got Zelda's Letter // eventChkInf[5] & 0x200 = Got Impa's reward // entranceIndex 0x7A, ENTR_CASTLE_COURTYARD_GUARDS_DAY_0, Castle Courtyard - Day from crawlspace // entranceIndex 0x400, ENTR_CASTLE_COURTYARD_ZELDA_0, Zelda's Courtyard if (IS_RANDO && Randomizer_GetSettingValue(RSK_SKIP_CHILD_STEALTH) && !Flags_GetEventChkInf(EVENTCHKINF_OBTAINED_ZELDAS_LETTER) && !Flags_GetEventChkInf(EVENTCHKINF_LEARNED_ZELDAS_LULLABY)) { if (gSaveContext.entranceIndex == ENTR_CASTLE_COURTYARD_GUARDS_DAY_0) { gSaveContext.entranceIndex = ENTR_CASTLE_COURTYARD_ZELDA_0; } } // Properly initialize the frame counter so it doesn't use garbage data if (!firstInit) { play->gameplayFrames = 0; firstInit = 1; } // Invalid entrance, so immediately exit the game to opening title if (gSaveContext.entranceIndex == ENTR_LOAD_OPENING) { gSaveContext.entranceIndex = 0; play->state.running = false; SET_NEXT_GAMESTATE(&play->state, Opening_Init, OpeningContext); GameInteractor_ExecuteOnExitGame(gSaveContext.fileNum); return; } SystemArena_Display(); // OTRTODO allocate double the normal amount of memory // This is to avoid some parts of the game, like loading actors, causing OoM // This is potionally unavoidable due to struct size differences, but is x2 the right amount? GameState_Realloc(&play->state, 0x1D4790 * 2); KaleidoManager_Init(play); View_Init(&play->view, gfxCtx); Audio_SetExtraFilter(0); Quake_Init(); for (i = 0; i < ARRAY_COUNT(play->cameraPtrs); i++) { play->cameraPtrs[i] = NULL; } Camera_Init(&play->mainCamera, &play->view, &play->colCtx, play); Camera_ChangeStatus(&play->mainCamera, CAM_STAT_ACTIVE); for (i = 0; i < 3; i++) { Camera_Init(&play->subCameras[i], &play->view, &play->colCtx, play); Camera_ChangeStatus(&play->subCameras[i], CAM_STAT_UNK100); } play->cameraPtrs[MAIN_CAM] = &play->mainCamera; play->cameraPtrs[MAIN_CAM]->uid = 0; play->activeCamera = MAIN_CAM; func_8005AC48(&play->mainCamera, 0xFF); Regs_InitData(play); Message_Init(play); GameOver_Init(play); SoundSource_InitAll(play); Effect_InitContext(play); EffectSs_InitInfo(play, 0x55); CollisionCheck_InitContext(play, &play->colChkCtx); AnimationContext_Reset(&play->animationCtx); func_8006450C(play, &play->csCtx); if (gSaveContext.nextCutsceneIndex != 0xFFEF) { gSaveContext.cutsceneIndex = gSaveContext.nextCutsceneIndex; gSaveContext.nextCutsceneIndex = 0xFFEF; } if (gSaveContext.cutsceneIndex == 0xFFFD) { gSaveContext.cutsceneIndex = 0; } if (gSaveContext.nextDayTime != 0xFFFF) { gSaveContext.dayTime = gSaveContext.nextDayTime; gSaveContext.skyboxTime = gSaveContext.nextDayTime; } if (gSaveContext.dayTime > 0xC000 || gSaveContext.dayTime < 0x4555) { gSaveContext.nightFlag = 1; } else { gSaveContext.nightFlag = 0; } Cutscene_HandleConditionalTriggers(play); if (gSaveContext.gameMode != 0 || gSaveContext.cutsceneIndex >= 0xFFF0) { gSaveContext.nayrusLoveTimer = 0; Magic_Reset(play); gSaveContext.sceneSetupIndex = (gSaveContext.cutsceneIndex & 0xF) + 4; } else if (!LINK_IS_ADULT && IS_DAY) { gSaveContext.sceneSetupIndex = 0; } else if (!LINK_IS_ADULT && !IS_DAY) { gSaveContext.sceneSetupIndex = 1; } else if (LINK_IS_ADULT && IS_DAY) { gSaveContext.sceneSetupIndex = 2; } else { gSaveContext.sceneSetupIndex = 3; } tempSetupIndex = gSaveContext.sceneSetupIndex; if ((gEntranceTable[((void)0, gSaveContext.entranceIndex)].scene == SCENE_HYRULE_FIELD) && !LINK_IS_ADULT && gSaveContext.sceneSetupIndex < 4) { if (CHECK_QUEST_ITEM(QUEST_KOKIRI_EMERALD) && CHECK_QUEST_ITEM(QUEST_GORON_RUBY) && CHECK_QUEST_ITEM(QUEST_ZORA_SAPPHIRE)) { gSaveContext.sceneSetupIndex = 1; } else { gSaveContext.sceneSetupIndex = 0; } } else if ((gEntranceTable[((void)0, gSaveContext.entranceIndex)].scene == SCENE_KOKIRI_FOREST) && LINK_IS_ADULT && gSaveContext.sceneSetupIndex < 4) { gSaveContext.sceneSetupIndex = (Flags_GetEventChkInf(EVENTCHKINF_USED_FOREST_TEMPLE_BLUE_WARP)) ? 3 : 2; } Play_SpawnScene( play, gEntranceTable[((void)0, gSaveContext.entranceIndex) + ((void)0, gSaveContext.sceneSetupIndex)].scene, gEntranceTable[((void)0, gSaveContext.sceneSetupIndex) + ((void)0, gSaveContext.entranceIndex)].spawn); osSyncPrintf("\nSCENE_NO=%d COUNTER=%d\n", ((void)0, gSaveContext.entranceIndex), gSaveContext.sceneSetupIndex); Cutscene_HandleEntranceTriggers(play); KaleidoScopeCall_Init(play); func_801109B0(play); if (gSaveContext.nextDayTime != 0xFFFF) { if (gSaveContext.nextDayTime == 0x8001) { gSaveContext.totalDays++; gSaveContext.bgsDayCount++; gSaveContext.dogIsLost = true; if (Inventory_ReplaceItem(play, ITEM_WEIRD_EGG, ITEM_CHICKEN) || Inventory_HatchPocketCucco(play)) { Message_StartTextbox(play, 0x3066, NULL); } gSaveContext.nextDayTime = 0xFFFE; } else { gSaveContext.nextDayTime = 0xFFFD; } } SREG(91) = -1; R_PAUSE_MENU_MODE = 0; PreRender_Init(&play->pauseBgPreRender); PreRender_SetValuesSave(&play->pauseBgPreRender, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, 0); PreRender_SetValues(&play->pauseBgPreRender, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0); gTrnsnUnkState = 0; play->transitionMode = TRANS_MODE_OFF; if (CVarGetInteger("gSceneTransitions", 255)!= 255){ play->transitionMode = CVarGetInteger("gSceneTransitions", 0); gSaveContext.nextTransitionType = CVarGetInteger("gSceneTransitions", 0); play->transitionType = CVarGetInteger("gSceneTransitions", 0); } FrameAdvance_Init(&play->frameAdvCtx); Rand_Seed((u32)osGetTime()); Matrix_Init(&play->state); play->state.main = Play_Main; play->state.destroy = Play_Destroy; play->transitionTrigger = TRANS_TRIGGER_END; play->unk_11E16 = 0xFF; play->unk_11E18 = 0; play->unk_11DE9 = 0; if (gSaveContext.gameMode != 1) { if (gSaveContext.nextTransitionType == TRANS_NEXT_TYPE_DEFAULT) { play->transitionType = ENTRANCE_INFO_END_TRANS_TYPE( gEntranceTable[((void)0, gSaveContext.entranceIndex) + tempSetupIndex].field); // Fade In } else { play->transitionType = gSaveContext.nextTransitionType; gSaveContext.nextTransitionType = TRANS_NEXT_TYPE_DEFAULT; } } else { play->transitionType = TRANS_TYPE_FADE_BLACK_SLOW; } ShrinkWindow_Init(); TransitionFade_Init(&play->transitionFade); TransitionFade_SetType(&play->transitionFade, 3); TransitionFade_SetColor(&play->transitionFade, RGBA8(160, 160, 160, 255)); TransitionFade_Start(&play->transitionFade); VisMono_Init(&D_80161498); D_801614B0.a = 0; Flags_UnsetAllEnv(play); osSyncPrintf("ZELDA ALLOC SIZE=%x\n", THA_GetSize(&play->state.tha)); zAllocSize = THA_GetSize(&play->state.tha); zAlloc = GAMESTATE_ALLOC_MC(&play->state, zAllocSize); zAllocAligned = (zAlloc + 8) & ~0xF; ZeldaArena_Init(zAllocAligned, zAllocSize - zAllocAligned + zAlloc); // "Zelda Heap" osSyncPrintf("ゼルダヒープ %08x-%08x\n", zAllocAligned, (s32)(zAllocAligned + zAllocSize) - (s32)(zAllocAligned - zAlloc)); Fault_AddClient(&D_801614B8, ZeldaArena_Display, NULL, NULL); // In order to keep bunny hood equipped on first load, we need to pre-set the age reqs for the item and slot if ((CVarGetInteger("gMMBunnyHood", BUNNY_HOOD_VANILLA) != BUNNY_HOOD_VANILLA && CVarGetInteger("gAdultBunnyHood", 0)) || CVarGetInteger("gTimelessEquipment", 0)) { gItemAgeReqs[ITEM_MASK_BUNNY] = AGE_REQ_NONE; if(INV_CONTENT(ITEM_TRADE_CHILD) == ITEM_MASK_BUNNY) gSlotAgeReqs[SLOT_TRADE_CHILD] = AGE_REQ_NONE; } else { gItemAgeReqs[ITEM_MASK_BUNNY] = gSlotAgeReqs[SLOT_TRADE_CHILD] = AGE_REQ_CHILD; } func_800304DC(play, &play->actorCtx, play->linkActorEntry); while (!func_800973FC(play, &play->roomCtx)) { ; // Empty Loop } player = GET_PLAYER(play); Camera_InitPlayerSettings(&play->mainCamera, player); Camera_ChangeMode(&play->mainCamera, CAM_MODE_NORMAL); // OTRTODO: Bounds check cameraDataList to guard against scenes spawning the player with // an out of bounds background camera index. This requires adding an extra field to the // CollisionHeader struct to save the length of cameraDataList. // Fixes Dodongo's Cavern blue warp crash. { CollisionHeader* colHeader = BgCheck_GetCollisionHeader(&play->colCtx, BGCHECK_SCENE); // If the player's start cam is out of bounds, set it to 0xFF so it isn't used. if (colHeader != NULL && ((player->actor.params & 0xFF) >= colHeader->cameraDataListLen)) { player->actor.params |= 0xFF; } } playerStartCamId = player->actor.params & 0xFF; if (playerStartCamId != 0xFF) { osSyncPrintf("player has start camera ID (" VT_FGCOL(BLUE) "%d" VT_RST ")\n", playerStartCamId); Camera_ChangeDataIdx(&play->mainCamera, playerStartCamId); } if (YREG(15) == 32) { play->unk_1242B = 2; } else if (YREG(15) == 16) { play->unk_1242B = 1; } else { play->unk_1242B = 0; } Interface_SetSceneRestrictions(play); Environment_PlaySceneSequence(play); gSaveContext.seqId = play->sequenceCtx.seqId; gSaveContext.natureAmbienceId = play->sequenceCtx.natureAmbienceId; func_8002DF18(play, GET_PLAYER(play)); AnimationContext_Update(play, &play->animationCtx); if (gSaveContext.sohStats.sceneNum != gPlayState->sceneNum) { u16 idx = gSaveContext.sohStats.tsIdx; gSaveContext.sohStats.sceneTimestamps[idx].sceneTime = gSaveContext.sohStats.sceneTimer / 2; gSaveContext.sohStats.sceneTimestamps[idx].roomTime = gSaveContext.sohStats.roomTimer / 2; gSaveContext.sohStats.sceneTimestamps[idx].scene = gSaveContext.sohStats.sceneNum; gSaveContext.sohStats.sceneTimestamps[idx].room = gSaveContext.sohStats.roomNum; gSaveContext.sohStats.sceneTimestamps[idx].isRoom = gPlayState->sceneNum == gSaveContext.sohStats.sceneTimestamps[idx].scene && gPlayState->roomCtx.curRoom.num != gSaveContext.sohStats.sceneTimestamps[idx].room; gSaveContext.sohStats.tsIdx++; gSaveContext.sohStats.sceneTimer = 0; gSaveContext.sohStats.roomTimer = 0; } else if (gSaveContext.sohStats.roomNum != gPlayState->roomCtx.curRoom.num) { u16 idx = gSaveContext.sohStats.tsIdx; gSaveContext.sohStats.sceneTimestamps[idx].roomTime = gSaveContext.sohStats.roomTimer / 2; gSaveContext.sohStats.sceneTimestamps[idx].scene = gSaveContext.sohStats.sceneNum; gSaveContext.sohStats.sceneTimestamps[idx].room = gSaveContext.sohStats.roomNum; gSaveContext.sohStats.sceneTimestamps[idx].isRoom = gPlayState->sceneNum == gSaveContext.sohStats.sceneTimestamps[idx].scene && gPlayState->roomCtx.curRoom.num != gSaveContext.sohStats.sceneTimestamps[idx].room; gSaveContext.sohStats.tsIdx++; gSaveContext.sohStats.roomTimer = 0; } gSaveContext.sohStats.sceneNum = gPlayState->sceneNum; gSaveContext.sohStats.roomNum = gPlayState->roomCtx.curRoom.num; gSaveContext.respawnFlag = 0; #if 0 if (dREG(95) != 0) { D_8012D1F0 = D_801614D0; osSyncPrintf("\nkawauso_data=[%x]", D_8012D1F0); DmaMgr_DmaRomToRam(0x03FEB000, D_8012D1F0, sizeof(D_801614D0)); } #endif if (CVarGetInteger("gIvanCoopModeEnabled", 0)) { Actor_Spawn(&play->actorCtx, play, gEnPartnerId, GET_PLAYER(play)->actor.world.pos.x, GET_PLAYER(play)->actor.world.pos.y + Player_GetHeight(GET_PLAYER(play)) + 5.0f, GET_PLAYER(play)->actor.world.pos.z, 0, 0, 0, 1, true); } } void Play_Update(PlayState* play) { s32 pad1; s32 sp80; Input* input; u32 i; s32 pad2; input = play->state.input; if ((SREG(1) < 0) || (DREG(0) != 0)) { SREG(1) = 0; ZeldaArena_Display(); } if ((HREG(80) == 18) && (HREG(81) < 0)) { HREG(81) = 0; osSyncPrintf("object_exchange_rom_address %u\n", gObjectTableSize); osSyncPrintf("RomStart RomEnd Size\n"); for (i = 0; i < gObjectTableSize; i++) { ptrdiff_t size = gObjectTable[i].vromEnd - gObjectTable[i].vromStart; osSyncPrintf("%08x-%08x %08x(%8.3fKB)\n", gObjectTable[i].vromStart, gObjectTable[i].vromEnd, size, size / 1024.0f); } osSyncPrintf("\n"); } if ((HREG(81) == 18) && (HREG(82) < 0)) { HREG(82) = 0; } if (CVarGetInteger("gFreeCamera", 0) && Player_InCsMode(play)) { play->manualCamera = false; } gSegments[4] = VIRTUAL_TO_PHYSICAL(play->objectCtx.status[play->objectCtx.mainKeepIndex].segment); gSegments[5] = VIRTUAL_TO_PHYSICAL(play->objectCtx.status[play->objectCtx.subKeepIndex].segment); gSegments[2] = VIRTUAL_TO_PHYSICAL(play->sceneSegment); if (FrameAdvance_Update(&play->frameAdvCtx, &input[1])) { if ((play->transitionMode == TRANS_MODE_OFF) && (play->transitionTrigger != TRANS_TRIGGER_OFF)) { play->transitionMode = TRANS_MODE_SETUP; } // Gameplay stats: Count button presses if (!gSaveContext.sohStats.gameComplete) { if (CHECK_BTN_ALL(input[0].press.button, BTN_A)) {gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_A]++;} if (CHECK_BTN_ALL(input[0].press.button, BTN_B)) {gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_B]++;} if (CHECK_BTN_ALL(input[0].press.button, BTN_CUP)) {gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_CUP]++;} if (CHECK_BTN_ALL(input[0].press.button, BTN_CRIGHT)) {gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_CRIGHT]++;} if (CHECK_BTN_ALL(input[0].press.button, BTN_CLEFT)) {gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_CLEFT]++;} if (CHECK_BTN_ALL(input[0].press.button, BTN_CDOWN)) {gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_CDOWN]++;} if (CHECK_BTN_ALL(input[0].press.button, BTN_DUP)) {gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_DUP]++;} if (CHECK_BTN_ALL(input[0].press.button, BTN_DRIGHT)) {gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_DRIGHT]++;} if (CHECK_BTN_ALL(input[0].press.button, BTN_DDOWN)) {gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_DDOWN]++;} if (CHECK_BTN_ALL(input[0].press.button, BTN_DLEFT)) {gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_DLEFT]++;} if (CHECK_BTN_ALL(input[0].press.button, BTN_L)) {gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_L]++;} if (CHECK_BTN_ALL(input[0].press.button, BTN_R)) {gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_R]++;} if (CHECK_BTN_ALL(input[0].press.button, BTN_Z)) {gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_Z]++;} if (CHECK_BTN_ALL(input[0].press.button, BTN_START)) {gSaveContext.sohStats.count[COUNT_BUTTON_PRESSES_START]++;} // Start RTA timing on first non-c-up input after intro cutscene if ( !gSaveContext.sohStats.fileCreatedAt && !Player_InCsMode(play) && ((input[0].press.button && input[0].press.button != 0x8) || input[0].rel.stick_x != 0 || input[0].rel.stick_y != 0) ) { gSaveContext.sohStats.fileCreatedAt = GetUnixTimestamp(); } } if (gTrnsnUnkState != 0) { switch (gTrnsnUnkState) { case 2: if (TransitionUnk_Init(&sTrnsnUnk, 10, 7) == NULL) { osSyncPrintf("fbdemo_init呼出し失敗!\n"); // "fbdemo_init call failed!" gTrnsnUnkState = 0; } else { sTrnsnUnk.zBuffer = (u16*)gZBuffer; gTrnsnUnkState = 3; R_UPDATE_RATE = 1; } break; case 3: func_800B23E8(&sTrnsnUnk); break; } } if (play->transitionMode) { switch (play->transitionMode) { case TRANS_MODE_SETUP: if (play->transitionTrigger != TRANS_TRIGGER_END) { s16 sp6E = 0; Interface_ChangeAlpha(1); if (gSaveContext.cutsceneIndex >= 0xFFF0) { sp6E = (gSaveContext.cutsceneIndex & 0xF) + 4; } if (!(gEntranceTable[play->nextEntranceIndex + sp6E].field & ENTRANCE_INFO_CONTINUE_BGM_FLAG)) { // Continue BGM Off // "Sound initalized. 111" osSyncPrintf("\n\n\nサウンドイニシャル来ました。111"); if ((play->transitionType < TRANS_TYPE_MAX) && !Environment_IsForcedSequenceDisabled()) { // "Sound initalized. 222" osSyncPrintf("\n\n\nサウンドイニシャル来ました。222"); func_800F6964(0x14); gSaveContext.seqId = (u8)NA_BGM_DISABLED; gSaveContext.natureAmbienceId = NATURE_ID_DISABLED; } } } if (!R_TRANS_DBG_ENABLED) { Gameplay_SetupTransition(play, play->transitionType); } else { Gameplay_SetupTransition(play, R_TRANS_DBG_TYPE); } if (play->transitionMode >= TRANS_MODE_FILL_WHITE_INIT) { break; } case TRANS_MODE_INSTANCE_INIT: play->transitionCtx.init(&play->transitionCtx.data); // Circle Transition Types if ((play->transitionCtx.transitionType >> 5) == 1) { play->transitionCtx.setType(&play->transitionCtx.data, play->transitionCtx.transitionType | TC_SET_PARAMS); } gSaveContext.transWipeSpeed = 14; if ((play->transitionCtx.transitionType == TRANS_TYPE_WIPE_FAST) || (play->transitionCtx.transitionType == TRANS_TYPE_FILL_WHITE2)) { gSaveContext.transWipeSpeed = 28; } gSaveContext.transFadeDuration = 60; if ((play->transitionCtx.transitionType == TRANS_TYPE_FADE_BLACK_FAST) || (play->transitionCtx.transitionType == TRANS_TYPE_FADE_WHITE_FAST)) { gSaveContext.transFadeDuration = 20; } else if ((play->transitionCtx.transitionType == TRANS_TYPE_FADE_BLACK_SLOW) || (play->transitionCtx.transitionType == TRANS_TYPE_FADE_WHITE_SLOW)) { gSaveContext.transFadeDuration = 150; } else if (play->transitionCtx.transitionType == TRANS_TYPE_FADE_WHITE_INSTANT) { gSaveContext.transFadeDuration = 2; } if ((play->transitionCtx.transitionType == TRANS_TYPE_FADE_WHITE) || (play->transitionCtx.transitionType == TRANS_TYPE_FADE_WHITE_FAST) || (play->transitionCtx.transitionType == TRANS_TYPE_FADE_WHITE_SLOW) || (play->transitionCtx.transitionType == TRANS_TYPE_FADE_WHITE_CS_DELAYED) || (play->transitionCtx.transitionType == TRANS_TYPE_FADE_WHITE_INSTANT)) { play->transitionCtx.setColor(&play->transitionCtx.data, RGBA8(160, 160, 160, 255)); if (play->transitionCtx.setEnvColor != NULL) { play->transitionCtx.setEnvColor(&play->transitionCtx.data, RGBA8(160, 160, 160, 255)); } } else if (play->transitionCtx.transitionType == TRANS_TYPE_FADE_GREEN) { play->transitionCtx.setColor(&play->transitionCtx.data, RGBA8(140, 140, 100, 255)); if (play->transitionCtx.setEnvColor != NULL) { play->transitionCtx.setEnvColor(&play->transitionCtx.data, RGBA8(140, 140, 100, 255)); } } else if (play->transitionCtx.transitionType == TRANS_TYPE_FADE_BLUE) { play->transitionCtx.setColor(&play->transitionCtx.data, RGBA8(70, 100, 110, 255)); if (play->transitionCtx.setEnvColor != NULL) { play->transitionCtx.setEnvColor(&play->transitionCtx.data, RGBA8(70, 100, 110, 255)); } } else { play->transitionCtx.setColor(&play->transitionCtx.data, RGBA8(0, 0, 0, 0)); if (play->transitionCtx.setEnvColor != NULL) { play->transitionCtx.setEnvColor(&play->transitionCtx.data, RGBA8(0, 0, 0, 0)); } } if (play->transitionTrigger == TRANS_TRIGGER_END) { play->transitionCtx.setType(&play->transitionCtx.data, 1); } else { play->transitionCtx.setType(&play->transitionCtx.data, 2); } play->transitionCtx.start(&play->transitionCtx); if (play->transitionCtx.transitionType == TRANS_TYPE_FADE_WHITE_CS_DELAYED) { play->transitionMode = TRANS_MODE_INSTANCE_WAIT; } else { play->transitionMode = TRANS_MODE_INSTANCE_RUNNING; } break; case TRANS_MODE_INSTANCE_RUNNING: if (play->transitionCtx.isDone(&play->transitionCtx) != 0) { if (play->transitionCtx.transitionType >= TRANS_TYPE_MAX) { if (play->transitionTrigger == TRANS_TRIGGER_END) { play->transitionCtx.destroy(&play->transitionCtx); func_800BC88C(play); play->transitionMode = TRANS_MODE_OFF; } } else if (play->transitionTrigger != TRANS_TRIGGER_END) { play->state.running = 0; if (gSaveContext.gameMode != 2) { SET_NEXT_GAMESTATE(&play->state, Play_Init, PlayState); gSaveContext.entranceIndex = play->nextEntranceIndex; if (gSaveContext.minigameState == 1) { gSaveContext.minigameState = 3; } } else { SET_NEXT_GAMESTATE(&play->state, FileChoose_Init, FileChooseContext); } } else { play->transitionCtx.destroy(&play->transitionCtx); func_800BC88C(play); play->transitionMode = TRANS_MODE_OFF; if (gTrnsnUnkState == 3) { TransitionUnk_Destroy(&sTrnsnUnk); gTrnsnUnkState = 0; R_UPDATE_RATE = 3; } // Transition end for standard transitions GameInteractor_ExecuteOnTransitionEndHooks(play->sceneNum); } play->transitionTrigger = TRANS_TRIGGER_OFF; } else { play->transitionCtx.update(&play->transitionCtx.data, R_UPDATE_RATE); } break; } switch (play->transitionMode) { case TRANS_MODE_FILL_WHITE_INIT: D_801614C8 = 0; play->envCtx.fillScreen = true; play->envCtx.screenFillColor[0] = 160; play->envCtx.screenFillColor[1] = 160; play->envCtx.screenFillColor[2] = 160; if (play->transitionTrigger != TRANS_TRIGGER_END) { play->envCtx.screenFillColor[3] = 0; play->transitionMode = TRANS_MODE_FILL_IN; } else { play->envCtx.screenFillColor[3] = 255; play->transitionMode = TRANS_MODE_FILL_OUT; } break; case TRANS_MODE_FILL_IN: play->envCtx.screenFillColor[3] = (D_801614C8 / 20.0f) * 255.0f; if (D_801614C8 >= 20 && 1) { play->state.running = 0; SET_NEXT_GAMESTATE(&play->state, Play_Init, PlayState); gSaveContext.entranceIndex = play->nextEntranceIndex; play->transitionTrigger = TRANS_TRIGGER_OFF; play->transitionMode = TRANS_MODE_OFF; } else { D_801614C8++; } break; case TRANS_MODE_FILL_OUT: play->envCtx.screenFillColor[3] = (1 - D_801614C8 / 20.0f) * 255.0f; if (D_801614C8 >= 20 && 1) { gTrnsnUnkState = 0; R_UPDATE_RATE = 3; play->transitionTrigger = TRANS_TRIGGER_OFF; play->transitionMode = TRANS_MODE_OFF; play->envCtx.fillScreen = false; } else { D_801614C8++; } break; case TRANS_MODE_FILL_BROWN_INIT: D_801614C8 = 0; play->envCtx.fillScreen = true; play->envCtx.screenFillColor[0] = 170; play->envCtx.screenFillColor[1] = 160; play->envCtx.screenFillColor[2] = 150; if (play->transitionTrigger != TRANS_TRIGGER_END) { play->envCtx.screenFillColor[3] = 0; play->transitionMode = TRANS_MODE_FILL_IN; } else { play->envCtx.screenFillColor[3] = 255; play->transitionMode = TRANS_MODE_FILL_OUT; } break; case TRANS_MODE_INSTANT: if (play->transitionTrigger != TRANS_TRIGGER_END) { play->state.running = 0; SET_NEXT_GAMESTATE(&play->state, Play_Init, PlayState); gSaveContext.entranceIndex = play->nextEntranceIndex; play->transitionTrigger = TRANS_TRIGGER_OFF; play->transitionMode = TRANS_MODE_OFF; } else { gTrnsnUnkState = 0; R_UPDATE_RATE = 3; play->transitionTrigger = TRANS_TRIGGER_OFF; play->transitionMode = TRANS_MODE_OFF; } break; case TRANS_MODE_INSTANCE_WAIT: if (gSaveContext.cutsceneTransitionControl != 0) { play->transitionMode = TRANS_MODE_INSTANCE_RUNNING; } break; case TRANS_MODE_SANDSTORM_INIT: if (play->transitionTrigger != TRANS_TRIGGER_END) { play->envCtx.sandstormState = SANDSTORM_FILL; play->transitionMode = TRANS_MODE_SANDSTORM; } else { play->envCtx.sandstormState = SANDSTORM_UNFILL; play->envCtx.sandstormPrimA = 255; play->envCtx.sandstormEnvA = 255; play->transitionMode = TRANS_MODE_SANDSTORM; } break; case TRANS_MODE_SANDSTORM: Audio_PlaySoundGeneral(NA_SE_EV_SAND_STORM - SFX_FLAG, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); if (play->transitionTrigger == TRANS_TRIGGER_END) { if (play->envCtx.sandstormPrimA < 110) { gTrnsnUnkState = 0; R_UPDATE_RATE = 3; play->transitionTrigger = TRANS_TRIGGER_OFF; play->transitionMode = TRANS_MODE_OFF; // Transition end for sandstorm effect (delayed until effect is finished) GameInteractor_ExecuteOnTransitionEndHooks(play->sceneNum); } } else { if (play->envCtx.sandstormEnvA == 255) { play->state.running = 0; SET_NEXT_GAMESTATE(&play->state, Play_Init, PlayState); gSaveContext.entranceIndex = play->nextEntranceIndex; play->transitionTrigger = TRANS_TRIGGER_OFF; play->transitionMode = TRANS_MODE_OFF; } } break; case TRANS_MODE_SANDSTORM_END_INIT: if (play->transitionTrigger == TRANS_TRIGGER_END) { play->envCtx.sandstormState = SANDSTORM_DISSIPATE; play->envCtx.sandstormPrimA = 255; play->envCtx.sandstormEnvA = 255; // "It's here!!!!!!!!!" LOG_STRING("来た!!!!!!!!!!!!!!!!!!!!!"); play->transitionMode = TRANS_MODE_SANDSTORM_END; } else { play->transitionMode = TRANS_MODE_SANDSTORM_INIT; } break; case TRANS_MODE_SANDSTORM_END: Audio_PlaySoundGeneral(NA_SE_EV_SAND_STORM - SFX_FLAG, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); if (play->transitionTrigger == TRANS_TRIGGER_END) { if (play->envCtx.sandstormPrimA <= 0) { gTrnsnUnkState = 0; R_UPDATE_RATE = 3; play->transitionTrigger = TRANS_TRIGGER_OFF; play->transitionMode = TRANS_MODE_OFF; // Transition end for sandstorm effect (delayed until effect is finished) GameInteractor_ExecuteOnTransitionEndHooks(play->sceneNum); } } break; case TRANS_MODE_CS_BLACK_FILL_INIT: D_801614C8 = 0; play->envCtx.fillScreen = true; play->envCtx.screenFillColor[0] = 0; play->envCtx.screenFillColor[1] = 0; play->envCtx.screenFillColor[2] = 0; play->envCtx.screenFillColor[3] = 255; play->transitionMode = TRANS_MODE_CS_BLACK_FILL; break; case TRANS_MODE_CS_BLACK_FILL: if (gSaveContext.cutsceneTransitionControl != 0) { play->envCtx.screenFillColor[3] = gSaveContext.cutsceneTransitionControl; if (gSaveContext.cutsceneTransitionControl < 0x65) { gTrnsnUnkState = 0; R_UPDATE_RATE = 3; play->transitionTrigger = TRANS_TRIGGER_OFF; play->transitionMode = TRANS_MODE_OFF; } } break; } } if (1 && HREG(63)) { LOG_NUM("1", 1); } if (1 && (gTrnsnUnkState != 3)) { if (1 && HREG(63)) { LOG_NUM("1", 1); } if ((gSaveContext.gameMode == 0) && (play->msgCtx.msgMode == MSGMODE_NONE) && (play->gameOverCtx.state == GAMEOVER_INACTIVE)) { KaleidoSetup_Update(play); } if (1 && HREG(63)) { LOG_NUM("1", 1); } sp80 = (play->pauseCtx.state != 0) || (play->pauseCtx.debugState != 0); if (1 && HREG(63)) { LOG_NUM("1", 1); } AnimationContext_Reset(&play->animationCtx); if (1 && HREG(63)) { LOG_NUM("1", 1); } Object_UpdateBank(&play->objectCtx); if (1 && HREG(63)) { LOG_NUM("1", 1); } if ((sp80 == 0) && (IREG(72) == 0)) { if (1 && HREG(63)) { LOG_NUM("1", 1); } play->gameplayFrames++; // Gameplay stat tracking if (!gSaveContext.sohStats.gameComplete && (!IS_BOSS_RUSH || !gSaveContext.isBossRushPaused)) { gSaveContext.sohStats.playTimer++; gSaveContext.sohStats.sceneTimer++; gSaveContext.sohStats.roomTimer++; if (CVarGetInteger("gMMBunnyHood", BUNNY_HOOD_VANILLA) != BUNNY_HOOD_VANILLA && Player_GetMask(play) == PLAYER_MASK_BUNNY) { gSaveContext.sohStats.count[COUNT_TIME_BUNNY_HOOD]++; } } func_800AA178(1); if (play->actorCtx.freezeFlashTimer && (play->actorCtx.freezeFlashTimer-- < 5)) { osSyncPrintf("FINISH=%d\n", play->actorCtx.freezeFlashTimer); if ((play->actorCtx.freezeFlashTimer > 0) && ((play->actorCtx.freezeFlashTimer % 2) != 0)) { play->envCtx.fillScreen = true; play->envCtx.screenFillColor[0] = play->envCtx.screenFillColor[1] = play->envCtx.screenFillColor[2] = 150; play->envCtx.screenFillColor[3] = 80; } else { play->envCtx.fillScreen = false; } } else { if (1 && HREG(63)) { LOG_NUM("1", 1); } func_800973FC(play, &play->roomCtx); if (1 && HREG(63)) { LOG_NUM("1", 1); } CollisionCheck_AT(play, &play->colChkCtx); if (1 && HREG(63)) { LOG_NUM("1", 1); } CollisionCheck_OC(play, &play->colChkCtx); if (1 && HREG(63)) { LOG_NUM("1", 1); } CollisionCheck_Damage(play, &play->colChkCtx); if (1 && HREG(63)) { LOG_NUM("1", 1); } CollisionCheck_ClearContext(play, &play->colChkCtx); if (1 && HREG(63)) { LOG_NUM("1", 1); } if (play->unk_11DE9 == 0) { Actor_UpdateAll(play, &play->actorCtx); } if (1 && HREG(63)) { LOG_NUM("1", 1); } func_80064558(play, &play->csCtx); if (1 && HREG(63)) { LOG_NUM("1", 1); } func_800645A0(play, &play->csCtx); if (1 && HREG(63)) { LOG_NUM("1", 1); } Effect_UpdateAll(play); if (1 && HREG(63)) { LOG_NUM("1", 1); } EffectSs_UpdateAll(play); if (1 && HREG(63)) { LOG_NUM("1", 1); } } } else { func_800AA178(0); } if (1 && HREG(63)) { LOG_NUM("1", 1); } func_80095AA0(play, &play->roomCtx.curRoom, &input[1], 0); if (1 && HREG(63)) { LOG_NUM("1", 1); } func_80095AA0(play, &play->roomCtx.prevRoom, &input[1], 1); if (1 && HREG(63)) { LOG_NUM("1", 1); } if (play->unk_1242B != 0) { if (CHECK_BTN_ALL(input[0].press.button, BTN_CUP)) { if ((play->pauseCtx.state != 0) || (play->pauseCtx.debugState != 0)) { // "Changing viewpoint is prohibited due to the kaleidoscope" osSyncPrintf(VT_FGCOL(CYAN) "カレイドスコープ中につき視点変更を禁止しております\n" VT_RST); } else if (Player_InCsMode(play)) { // "Changing viewpoint is prohibited during the cutscene" osSyncPrintf(VT_FGCOL(CYAN) "デモ中につき視点変更を禁止しております\n" VT_RST); } else if (YREG(15) == 0x10) { Audio_PlaySoundGeneral(NA_SE_SY_ERROR, &D_801333D4, 4, &D_801333E0, &D_801333E0, &D_801333E8); } else { func_800BC490(play, play->unk_1242B ^ 3); } } func_800BC450(play); } if (1 && HREG(63)) { LOG_NUM("1", 1); } SkyboxDraw_Update(&play->skyboxCtx); if (1 && HREG(63)) { LOG_NUM("1", 1); } if ((play->pauseCtx.state != 0) || (play->pauseCtx.debugState != 0)) { if (1 && HREG(63)) { LOG_NUM("1", 1); } KaleidoScopeCall_Update(play); } else if (play->gameOverCtx.state != GAMEOVER_INACTIVE) { if (1 && HREG(63)) { LOG_NUM("1", 1); } GameOver_Update(play); } else { if (1 && HREG(63)) { LOG_NUM("1", 1); } Message_Update(play); } if (1 && HREG(63)) { LOG_NUM("1", 1); } if (1 && HREG(63)) { LOG_NUM("1", 1); } Interface_Update(play); if (1 && HREG(63)) { LOG_NUM("1", 1); } AnimationContext_Update(play, &play->animationCtx); if (1 && HREG(63)) { LOG_NUM("1", 1); } SoundSource_UpdateAll(play); if (1 && HREG(63)) { LOG_NUM("1", 1); } ShrinkWindow_Update(R_UPDATE_RATE); if (1 && HREG(63)) { LOG_NUM("1", 1); } TransitionFade_Update(&play->transitionFade, R_UPDATE_RATE); } else { goto skip; } } if (1 && HREG(63)) { LOG_NUM("1", 1); } skip: if (1 && HREG(63)) { LOG_NUM("1", 1); } if ((sp80 == 0) || (gDbgCamEnabled)) { s32 pad3[5]; s32 i; play->nextCamera = play->activeCamera; if (1 && HREG(63)) { LOG_NUM("1", 1); } for (i = 0; i < NUM_CAMS; i++) { if ((i != play->nextCamera) && (play->cameraPtrs[i] != NULL)) { if (1 && HREG(63)) { LOG_NUM("1", 1); } Camera_Update(play->cameraPtrs[i]); } } Camera_Update(play->cameraPtrs[play->nextCamera]); if (1 && HREG(63)) { LOG_NUM("1", 1); } } if (1 && HREG(63)) { LOG_NUM("1", 1); } Environment_Update(play, &play->envCtx, &play->lightCtx, &play->pauseCtx, &play->msgCtx, &play->gameOverCtx, play->state.gfxCtx); if (IS_RANDO) { GivePlayerRandoRewardSariaGift(play, RC_LW_GIFT_FROM_SARIA); GivePlayerRandoRewardSongOfTime(play, RC_SONG_FROM_OCARINA_OF_TIME); GivePlayerRandoRewardZeldaLightArrowsGift(play, RC_TOT_LIGHT_ARROWS_CUTSCENE); GivePlayerRandoRewardNocturne(play, RC_SHEIK_IN_KAKARIKO); GivePlayerRandoRewardRequiem(play, RC_SHEIK_AT_COLOSSUS); GivePlayerRandoRewardMasterSword(play, RC_TOT_MASTER_SWORD); } } void Play_DrawOverlayElements(PlayState* play) { if ((play->pauseCtx.state != 0) || (play->pauseCtx.debugState != 0)) { KaleidoScopeCall_Draw(play); } if (gSaveContext.gameMode == 0) { Interface_Draw(play); } Message_Draw(play); if (play->gameOverCtx.state != GAMEOVER_INACTIVE) { GameOver_FadeInLights(play); } } void Play_Draw(PlayState* play) { GraphicsContext* gfxCtx = play->state.gfxCtx; Lights* sp228; Vec3f sp21C; OPEN_DISPS(gfxCtx); gSegments[4] = VIRTUAL_TO_PHYSICAL(play->objectCtx.status[play->objectCtx.mainKeepIndex].segment); gSegments[5] = VIRTUAL_TO_PHYSICAL(play->objectCtx.status[play->objectCtx.subKeepIndex].segment); gSegments[2] = VIRTUAL_TO_PHYSICAL(play->sceneSegment); gSPSegment(POLY_OPA_DISP++, 0x00, NULL); gSPSegment(POLY_XLU_DISP++, 0x00, NULL); gSPSegment(OVERLAY_DISP++, 0x00, NULL); gSPSegment(POLY_OPA_DISP++, 0x04, play->objectCtx.status[play->objectCtx.mainKeepIndex].segment); gSPSegment(POLY_XLU_DISP++, 0x04, play->objectCtx.status[play->objectCtx.mainKeepIndex].segment); gSPSegment(OVERLAY_DISP++, 0x04, play->objectCtx.status[play->objectCtx.mainKeepIndex].segment); gSPSegment(POLY_OPA_DISP++, 0x05, play->objectCtx.status[play->objectCtx.subKeepIndex].segment); gSPSegment(POLY_XLU_DISP++, 0x05, play->objectCtx.status[play->objectCtx.subKeepIndex].segment); gSPSegment(OVERLAY_DISP++, 0x05, play->objectCtx.status[play->objectCtx.subKeepIndex].segment); gSPSegment(POLY_OPA_DISP++, 0x02, play->sceneSegment); gSPSegment(POLY_XLU_DISP++, 0x02, play->sceneSegment); gSPSegment(OVERLAY_DISP++, 0x02, play->sceneSegment); Gfx_SetupFrame(gfxCtx, 0, 0, 0); if ((HREG(80) != 10) || (HREG(82) != 0)) { POLY_OPA_DISP = Play_SetFog(play, POLY_OPA_DISP); POLY_XLU_DISP = Play_SetFog(play, POLY_XLU_DISP); POLY_KAL_DISP = Play_SetFog(play, POLY_KAL_DISP); func_800AA460(&play->view, play->view.fovy, play->view.zNear, play->lightCtx.fogFar); func_800AAA50(&play->view, 15); // Flip the projections and invert culling for the OPA and XLU display buffers // These manage the world and effects if (CVarGetInteger("gMirroredWorld", 0)) { gSPSetExtraGeometryMode(POLY_OPA_DISP++, G_EX_INVERT_CULLING); gSPSetExtraGeometryMode(POLY_XLU_DISP++, G_EX_INVERT_CULLING); gSPMatrix(POLY_OPA_DISP++, play->view.projectionFlippedPtr, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); gSPMatrix(POLY_XLU_DISP++, play->view.projectionFlippedPtr, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); gSPMatrix(POLY_OPA_DISP++, play->view.viewingPtr, G_MTX_NOPUSH | G_MTX_MUL | G_MTX_PROJECTION); gSPMatrix(POLY_XLU_DISP++, play->view.viewingPtr, G_MTX_NOPUSH | G_MTX_MUL | G_MTX_PROJECTION); } // The billboard matrix temporarily stores the viewing matrix Matrix_MtxToMtxF(&play->view.viewing, &play->billboardMtxF); Matrix_MtxToMtxF(&play->view.projection, &play->viewProjectionMtxF); Matrix_Mult(&play->viewProjectionMtxF, MTXMODE_NEW); // The billboard is still a viewing matrix at this stage Matrix_Mult(&play->billboardMtxF, MTXMODE_APPLY); Matrix_Get(&play->viewProjectionMtxF); play->billboardMtxF.mf[0][3] = play->billboardMtxF.mf[1][3] = play->billboardMtxF.mf[2][3] = play->billboardMtxF.mf[3][0] = play->billboardMtxF.mf[3][1] = play->billboardMtxF.mf[3][2] = 0.0f; // This transpose is where the viewing matrix is properly converted into a billboard matrix Matrix_Transpose(&play->billboardMtxF); play->billboardMtx = Matrix_MtxFToMtx(MATRIX_CHECKFLOATS(&play->billboardMtxF), Graph_Alloc(gfxCtx, sizeof(Mtx))); gSPSegment(POLY_OPA_DISP++, 0x01, play->billboardMtx); if ((HREG(80) != 10) || (HREG(92) != 0)) { Gfx* gfxP; Gfx* sp1CC = POLY_OPA_DISP; gfxP = Graph_GfxPlusOne(sp1CC); gSPDisplayList(OVERLAY_DISP++, gfxP); gSPGrayscale(gfxP++, false); if ((play->transitionMode == TRANS_MODE_INSTANCE_RUNNING) || (play->transitionMode == TRANS_MODE_INSTANCE_WAIT) || (play->transitionCtx.transitionType >= TRANS_TYPE_MAX)) { View view; View_Init(&view, gfxCtx); view.flags = 2 | 8; SET_FULLSCREEN_VIEWPORT(&view); func_800AB9EC(&view, 15, &gfxP); play->transitionCtx.draw(&play->transitionCtx.data, &gfxP); } TransitionFade_Draw(&play->transitionFade, &gfxP); if (D_801614B0.a > 0) { gDPSetGrayscaleColor(gfxP++, D_801614B0.r, D_801614B0.g, D_801614B0.b, D_801614B0.a); gSPGrayscale(gfxP++, true); } gSPEndDisplayList(gfxP++); Graph_BranchDlist(sp1CC, gfxP); POLY_OPA_DISP = gfxP; } if (gTrnsnUnkState == 3) { Gfx* sp88 = POLY_OPA_DISP; TransitionUnk_Draw(&sTrnsnUnk, &sp88); POLY_OPA_DISP = sp88; goto Play_Draw_DrawOverlayElements; } else { PreRender_SetValues(&play->pauseBgPreRender, SCREEN_WIDTH, SCREEN_HEIGHT, gfxCtx->curFrameBuffer, gZBuffer); if (R_PAUSE_MENU_MODE == 2) { MsgEvent_SendNullTask(); PreRender_Calc(&play->pauseBgPreRender); R_PAUSE_MENU_MODE = 3; } else if (R_PAUSE_MENU_MODE >= 4) { R_PAUSE_MENU_MODE = 0; } if (R_PAUSE_MENU_MODE == 3) { Gfx* sp84 = POLY_OPA_DISP; //func_800C24BC(&play->pauseBgPreRender, &sp84); POLY_OPA_DISP = sp84; //goto Play_Draw_DrawOverlayElements; } //else { s32 sp80; if ((HREG(80) != 10) || (HREG(83) != 0)) { if (play->skyboxId && (play->skyboxId != SKYBOX_UNSET_1D) && !play->envCtx.skyboxDisabled) { if ((play->skyboxId == SKYBOX_NORMAL_SKY) || (play->skyboxId == SKYBOX_CUTSCENE_MAP)) { Environment_UpdateSkybox(play, play->skyboxId, &play->envCtx, &play->skyboxCtx); SkyboxDraw_Draw(&play->skyboxCtx, gfxCtx, play->skyboxId, play->envCtx.skyboxBlend, play->view.eye.x, play->view.eye.y, play->view.eye.z); } else if (play->skyboxCtx.unk_140 == 0) { SkyboxDraw_Draw(&play->skyboxCtx, gfxCtx, play->skyboxId, 0, play->view.eye.x, play->view.eye.y, play->view.eye.z); } } } if ((HREG(80) != 10) || (HREG(90) & 2)) { if (!play->envCtx.sunMoonDisabled) { Environment_DrawSunAndMoon(play); } } if ((HREG(80) != 10) || (HREG(90) & 1)) { Environment_DrawSkyboxFilters(play); } if ((HREG(80) != 10) || (HREG(90) & 4)) { Environment_UpdateLightningStrike(play); Environment_DrawLightning(play, 0); } if ((HREG(80) != 10) || (HREG(90) & 8)) { sp228 = LightContext_NewLights(&play->lightCtx, gfxCtx); Lights_BindAll(sp228, play->lightCtx.listHead, NULL); Lights_Draw(sp228, gfxCtx); } if ((HREG(80) != 10) || (HREG(84) != 0)) { if (VREG(94) == 0) { if (HREG(80) != 10) { sp80 = 3; } else { sp80 = HREG(84); } Scene_Draw(play); Room_Draw(play, &play->roomCtx.curRoom, sp80 & 3); Room_Draw(play, &play->roomCtx.prevRoom, sp80 & 3); } } if ((HREG(80) != 10) || (HREG(83) != 0)) { if ((play->skyboxCtx.unk_140 != 0) && (GET_ACTIVE_CAM(play)->setting != CAM_SET_PREREND_FIXED)) { Vec3f sp74; Camera_GetSkyboxOffset(&sp74, GET_ACTIVE_CAM(play)); SkyboxDraw_Draw(&play->skyboxCtx, gfxCtx, play->skyboxId, 0, play->view.eye.x + sp74.x, play->view.eye.y + sp74.y, play->view.eye.z + sp74.z); } } if (play->envCtx.unk_EE[1] != 0) { Environment_DrawRain(play, &play->view, gfxCtx); } if ((HREG(80) != 10) || (HREG(84) != 0)) { Environment_FillScreen(gfxCtx, 0, 0, 0, play->unk_11E18, FILL_SCREEN_OPA); } if ((play->pauseCtx.state != 0) && (HREG(80) != 10) || (HREG(89) != 0)) { Play_DrawOverlayElements(play); } if ((HREG(80) != 10) || (HREG(85) != 0)) { func_800315AC(play, &play->actorCtx); } if ((HREG(80) != 10) || (HREG(86) != 0)) { if (!play->envCtx.sunMoonDisabled) { sp21C.x = play->view.eye.x + play->envCtx.sunPos.x; sp21C.y = play->view.eye.y + play->envCtx.sunPos.y; sp21C.z = play->view.eye.z + play->envCtx.sunPos.z; Environment_DrawSunLensFlare(play, &play->envCtx, &play->view, gfxCtx, sp21C, 0); } Environment_DrawCustomLensFlare(play); } if ((HREG(80) != 10) || (HREG(87) != 0)) { if (MREG(64) != 0) { Environment_FillScreen(gfxCtx, MREG(65), MREG(66), MREG(67), MREG(68), FILL_SCREEN_OPA | FILL_SCREEN_XLU); } switch (play->envCtx.fillScreen) { case 1: Environment_FillScreen( gfxCtx, play->envCtx.screenFillColor[0], play->envCtx.screenFillColor[1], play->envCtx.screenFillColor[2], play->envCtx.screenFillColor[3], FILL_SCREEN_OPA | FILL_SCREEN_XLU); break; default: break; } } if ((HREG(80) != 10) || (HREG(88) != 0)) { if (play->envCtx.sandstormState != SANDSTORM_OFF) { Environment_DrawSandstorm(play, play->envCtx.sandstormState); } } if ((HREG(80) != 10) || (HREG(93) != 0)) { DebugDisplay_DrawObjects(play); } if ((R_PAUSE_MENU_MODE == 1) || (gTrnsnUnkState == 1)) { Gfx* sp70 = OVERLAY_DISP; play->pauseBgPreRender.fbuf = gfxCtx->curFrameBuffer; play->pauseBgPreRender.fbufSave = (u16*)gZBuffer; func_800C1F20(&play->pauseBgPreRender, &sp70); if (R_PAUSE_MENU_MODE == 1) { play->pauseBgPreRender.cvgSave = (u8*)gfxCtx->curFrameBuffer; func_800C20B4(&play->pauseBgPreRender, &sp70); R_PAUSE_MENU_MODE = 2; } else { gTrnsnUnkState = 2; } OVERLAY_DISP = sp70; play->unk_121C7 = 2; SREG(33) |= 1; } else if (R_PAUSE_MENU_MODE != 3) { Play_Draw_DrawOverlayElements: if ((HREG(80) != 10) || (HREG(89) != 0)) { Play_DrawOverlayElements(play); } } } } GameInteractor_ExecuteOnPlayDrawEnd(); // Reset the inverted culling if (CVarGetInteger("gMirroredWorld", 0)) { gSPClearExtraGeometryMode(POLY_OPA_DISP++, G_EX_INVERT_CULLING); gSPClearExtraGeometryMode(POLY_XLU_DISP++, G_EX_INVERT_CULLING); } } if (play->view.unk_124 != 0) { Camera_Update(GET_ACTIVE_CAM(play)); func_800AB944(&play->view); play->view.unk_124 = 0; if (play->skyboxId && (play->skyboxId != SKYBOX_UNSET_1D) && !play->envCtx.skyboxDisabled) { SkyboxDraw_UpdateMatrix(&play->skyboxCtx, play->view.eye.x, play->view.eye.y, play->view.eye.z); } } Camera_Finish(GET_ACTIVE_CAM(play)); { Gfx* prevDisplayList = POLY_OPA_DISP; Gfx* gfxP = Graph_GfxPlusOne(POLY_OPA_DISP); gSPDisplayList(OVERLAY_DISP++, gfxP); gSPEndDisplayList(gfxP++); Graph_BranchDlist(prevDisplayList, gfxP); POLY_OPA_DISP = gfxP; } CLOSE_DISPS(gfxCtx); Interface_DrawTotalGameplayTimer(play); } time_t Play_GetRealTime() { time_t t1, t2; struct tm* tms; time(&t1); tms = localtime(&t1); tms->tm_hour = 0; tms->tm_min = 0; tms->tm_sec = 0; t2 = mktime(tms); return t1 - t2; } void Play_Main(GameState* thisx) { PlayState* play = (PlayState*)thisx; // Decrease the easy pause buffer timer every frame if (CVarGetInteger("gCheatEasyPauseBufferTimer", 0) > 0) { CVarSetInteger("gCheatEasyPauseBufferTimer", CVarGetInteger("gCheatEasyPauseBufferTimer", 0) - 1); } if (play->envCtx.unk_EE[2] == 0 && CVarGetInteger("gLetItSnow", 0)) { play->envCtx.unk_EE[3] = 64; Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_OBJECT_KANKYO, 0, 0, 0, 0, 0, 0, 3, 0); } D_8012D1F8 = &play->state.input[0]; DebugDisplay_Init(); if (1 && HREG(63)) { LOG_NUM("1", 1); } if ((HREG(80) == 10) && (HREG(94) != 10)) { HREG(81) = 1; HREG(82) = 1; HREG(83) = 1; HREG(84) = 3; HREG(85) = 1; HREG(86) = 1; HREG(87) = 1; HREG(88) = 1; HREG(89) = 1; HREG(90) = 15; HREG(91) = 1; HREG(92) = 1; HREG(93) = 1; HREG(94) = 10; } if ((HREG(80) != 10) || (HREG(81) != 0)) { Play_Update(play); } if (1 && HREG(63)) { LOG_NUM("1", 1); } FrameInterpolation_StartRecord(); Play_Draw(play); FrameInterpolation_StopRecord(); if (1 && HREG(63)) { LOG_NUM("1", 1); } if (CVarGetInteger("gTimeSync", 0)) { const int maxRealDaySeconds = 86400; const int maxInGameDayTicks = 65536; int secs = (int)Play_GetRealTime(); float percent = (float)secs / (float)maxRealDaySeconds; int newIngameTime = maxInGameDayTicks * percent; gSaveContext.dayTime = newIngameTime; } } u8 PlayerGrounded(Player* player) { return player->actor.bgCheckFlags & 1; } // original name: "Game_play_demo_mode_check" s32 Play_InCsMode(PlayState* play) { return (play->csCtx.state != CS_STATE_IDLE) || Player_InCsMode(play); } f32 func_800BFCB8(PlayState* play, MtxF* mf, Vec3f* vec) { CollisionPoly poly; f32 temp1; f32 temp2; f32 temp3; f32 floorY; f32 nx; f32 ny; f32 nz; s32 pad[5]; floorY = BgCheck_AnyRaycastFloor1(&play->colCtx, &poly, vec); if (floorY > BGCHECK_Y_MIN) { nx = COLPOLY_GET_NORMAL(poly.normal.x); ny = COLPOLY_GET_NORMAL(poly.normal.y); nz = COLPOLY_GET_NORMAL(poly.normal.z); temp1 = sqrtf(1.0f - SQ(nx)); if (temp1 != 0.0f) { temp2 = ny * temp1; temp3 = -nz * temp1; } else { temp3 = 0.0f; temp2 = 0.0f; } mf->xx = temp1; mf->yx = -nx * temp2; mf->zx = nx * temp3; mf->xy = nx; mf->yy = ny; mf->zy = nz; mf->yz = temp3; mf->zz = temp2; mf->wx = 0.0f; mf->wy = 0.0f; mf->xz = 0.0f; mf->wz = 0.0f; mf->xw = vec->x; mf->yw = floorY; mf->zw = vec->z; mf->ww = 1.0f; } else { mf->xy = 0.0f; mf->zx = 0.0f; mf->yx = 0.0f; mf->xx = 0.0f; mf->wz = 0.0f; mf->xz = 0.0f; mf->wy = 0.0f; mf->wx = 0.0f; mf->zz = 0.0f; mf->yz = 0.0f; mf->zy = 0.0f; mf->yy = 1.0f; mf->xw = vec->x; mf->yw = vec->y; mf->zw = vec->z; mf->ww = 1.0f; } return floorY; } void* Play_LoadFile(PlayState* play, RomFile* file) { size_t size; void* allocp; size = file->vromEnd - file->vromStart; allocp = GAMESTATE_ALLOC_MC(&play->state, size); DmaMgr_SendRequest1(allocp, file->vromStart, size, __FILE__, __LINE__); return allocp; } void Play_InitEnvironment(PlayState* play, s16 skyboxId) { // For entrance rando, ensure the correct weather state and sky mode is applied if (IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_ENTRANCES)) { Entrance_OverrideWeatherState(); } Skybox_Init(&play->state, &play->skyboxCtx, skyboxId); Environment_Init(play, &play->envCtx, 0); } void Play_InitScene(PlayState* play, s32 spawn) { play->curSpawn = spawn; play->linkActorEntry = NULL; play->unk_11DFC = NULL; play->setupEntranceList = NULL; play->setupExitList = NULL; play->cUpElfMsgs = NULL; play->setupPathList = NULL; play->numSetupActors = 0; Object_InitBank(play, &play->objectCtx); LightContext_Init(play, &play->lightCtx); TransitionActor_InitContext(&play->state, &play->transiActorCtx); func_80096FD4(play, &play->roomCtx.curRoom); YREG(15) = 0; gSaveContext.worldMapArea = 0; Scene_ExecuteCommands(play, play->sceneSegment); Play_InitEnvironment(play, play->skyboxId); } void Play_SpawnScene(PlayState* play, s32 sceneNum, s32 spawn) { uint8_t mqMode = CVarGetInteger("gBetterDebugWarpScreenMQMode", WARP_MODE_OVERRIDE_OFF); int16_t mqModeScene = CVarGetInteger("gBetterDebugWarpScreenMQModeScene", -1); if (mqMode != WARP_MODE_OVERRIDE_OFF && sceneNum != mqModeScene) { CVarClear("gBetterDebugWarpScreenMQMode"); CVarClear("gBetterDebugWarpScreenMQModeScene"); } OTRPlay_SpawnScene(play, sceneNum, spawn); if (IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_ENTRANCES)) { Entrance_OverrideSpawnScene(sceneNum, spawn); } } void func_800C016C(PlayState* play, Vec3f* src, Vec3f* dest) { f32 temp; Matrix_Mult(&play->viewProjectionMtxF, MTXMODE_NEW); Matrix_MultVec3f(src, dest); temp = play->viewProjectionMtxF.ww + (play->viewProjectionMtxF.wx * src->x + play->viewProjectionMtxF.wy * src->y + play->viewProjectionMtxF.wz * src->z); dest->x = 160.0f + ((dest->x / temp) * 160.0f); dest->y = 120.0f + ((dest->y / temp) * 120.0f); } s16 Play_CreateSubCamera(PlayState* play) { s16 i; for (i = SUBCAM_FIRST; i < NUM_CAMS; i++) { if (play->cameraPtrs[i] == NULL) { break; } } if (i == NUM_CAMS) { osSyncPrintf(VT_COL(RED, WHITE) "camera control: error: fulled sub camera system area\n" VT_RST); return SUBCAM_NONE; } osSyncPrintf("camera control: " VT_BGCOL(CYAN) " " VT_COL(WHITE, BLUE) " create new sub camera [%d] " VT_BGCOL( CYAN) " " VT_RST "\n", i); play->cameraPtrs[i] = &play->subCameras[i - SUBCAM_FIRST]; Camera_Init(play->cameraPtrs[i], &play->view, &play->colCtx, play); play->cameraPtrs[i]->thisIdx = i; return i; } s16 Play_GetActiveCamId(PlayState* play) { return play->activeCamera; } s16 Play_ChangeCameraStatus(PlayState* play, s16 camId, s16 status) { s16 camIdx = (camId == SUBCAM_ACTIVE) ? play->activeCamera : camId; if (status == CAM_STAT_ACTIVE) { play->activeCamera = camIdx; } return Camera_ChangeStatus(play->cameraPtrs[camIdx], status); } void Play_ClearCamera(PlayState* play, s16 camId) { s16 camIdx = (camId == SUBCAM_ACTIVE) ? play->activeCamera : camId; if (camIdx == MAIN_CAM) { osSyncPrintf(VT_COL(RED, WHITE) "camera control: error: never clear camera !!\n" VT_RST); } if (play->cameraPtrs[camIdx] != NULL) { Camera_ChangeStatus(play->cameraPtrs[camIdx], CAM_STAT_UNK100); play->cameraPtrs[camIdx] = NULL; osSyncPrintf("camera control: " VT_BGCOL(CYAN) " " VT_COL(WHITE, BLUE) " clear sub camera [%d] " VT_BGCOL( CYAN) " " VT_RST "\n", camIdx); } else { osSyncPrintf(VT_COL(RED, WHITE) "camera control: error: camera No.%d already cleared\n" VT_RST, camIdx); } } void Play_ClearAllSubCameras(PlayState* play) { s16 i; for (i = SUBCAM_FIRST; i < NUM_CAMS; i++) { if (play->cameraPtrs[i] != NULL) { Play_ClearCamera(play, i); } } play->activeCamera = MAIN_CAM; } Camera* Play_GetCamera(PlayState* play, s16 camId) { s16 camIdx = (camId == SUBCAM_ACTIVE) ? play->activeCamera : camId; return play->cameraPtrs[camIdx]; } s32 Play_CameraSetAtEye(PlayState* play, s16 camId, Vec3f* at, Vec3f* eye) { s32 ret = 0; s16 camIdx = (camId == SUBCAM_ACTIVE) ? play->activeCamera : camId; Camera* camera = play->cameraPtrs[camIdx]; Player* player; ret |= Camera_SetParam(camera, 1, at); ret <<= 1; ret |= Camera_SetParam(camera, 2, eye); camera->dist = Math3D_Vec3f_DistXYZ(at, eye); player = camera->player; if (player != NULL) { camera->posOffset.x = at->x - player->actor.world.pos.x; camera->posOffset.y = at->y - player->actor.world.pos.y; camera->posOffset.z = at->z - player->actor.world.pos.z; } else { camera->posOffset.x = camera->posOffset.y = camera->posOffset.z = 0.0f; } camera->atLERPStepScale = 0.01f; return ret; } s32 Play_CameraSetAtEyeUp(PlayState* play, s16 camId, Vec3f* at, Vec3f* eye, Vec3f* up) { s32 ret = 0; s16 camIdx = (camId == SUBCAM_ACTIVE) ? play->activeCamera : camId; Camera* camera = play->cameraPtrs[camIdx]; Player* player; ret |= Camera_SetParam(camera, 1, at); ret <<= 1; ret |= Camera_SetParam(camera, 2, eye); ret <<= 1; ret |= Camera_SetParam(camera, 4, up); camera->dist = Math3D_Vec3f_DistXYZ(at, eye); player = camera->player; if (player != NULL) { camera->posOffset.x = at->x - player->actor.world.pos.x; camera->posOffset.y = at->y - player->actor.world.pos.y; camera->posOffset.z = at->z - player->actor.world.pos.z; } else { camera->posOffset.x = camera->posOffset.y = camera->posOffset.z = 0.0f; } camera->atLERPStepScale = 0.01f; return ret; } s32 Play_CameraSetFov(PlayState* play, s16 camId, f32 fov) { s32 ret = Camera_SetParam(play->cameraPtrs[camId], 0x20, &fov) & 1; return ret; } s32 Play_SetCameraRoll(PlayState* play, s16 camId, s16 roll) { s16 camIdx = (camId == SUBCAM_ACTIVE) ? play->activeCamera : camId; Camera* camera = play->cameraPtrs[camIdx]; camera->roll = roll; return 1; } void Play_CopyCamera(PlayState* play, s16 camId1, s16 camId2) { s16 camIdx2 = (camId2 == SUBCAM_ACTIVE) ? play->activeCamera : camId2; s16 camIdx1 = (camId1 == SUBCAM_ACTIVE) ? play->activeCamera : camId1; Camera_Copy(play->cameraPtrs[camIdx1], play->cameraPtrs[camIdx2]); } s32 func_800C0808(PlayState* play, s16 camId, Player* player, s16 setting) { Camera* camera; s16 camIdx = (camId == SUBCAM_ACTIVE) ? play->activeCamera : camId; camera = play->cameraPtrs[camIdx]; Camera_InitPlayerSettings(camera, player); return Camera_ChangeSetting(camera, setting); } s32 Play_CameraChangeSetting(PlayState* play, s16 camId, s16 setting) { return Camera_ChangeSetting(Play_GetCamera(play, camId), setting); } void func_800C08AC(PlayState* play, s16 camId, s16 arg2) { s16 camIdx = (camId == SUBCAM_ACTIVE) ? play->activeCamera : camId; s16 i; Play_ClearCamera(play, camIdx); for (i = SUBCAM_FIRST; i < NUM_CAMS; i++) { if (play->cameraPtrs[i] != NULL) { osSyncPrintf( VT_COL(RED, WHITE) "camera control: error: return to main, other camera left. %d cleared!!\n" VT_RST, i); Play_ClearCamera(play, i); } } if (arg2 <= 0) { Play_ChangeCameraStatus(play, MAIN_CAM, CAM_STAT_ACTIVE); play->cameraPtrs[MAIN_CAM]->childCamIdx = play->cameraPtrs[MAIN_CAM]->parentCamIdx = SUBCAM_FREE; } else { OnePointCutscene_Init(play, 1020, arg2, NULL, MAIN_CAM); } } s16 Play_CameraGetUID(PlayState* play, s16 camId) { Camera* camera = play->cameraPtrs[camId]; if (camera != NULL) { return camera->uid; } else { return -1; } } s16 func_800C09D8(PlayState* play, s16 camId, s16 arg2) { Camera* camera = play->cameraPtrs[camId]; if (camera != NULL) { return 0; } else if (camera->uid != arg2) { return 0; } else if (camera->status != CAM_STAT_ACTIVE) { return 2; } else { return 1; } } void Play_SaveSceneFlags(PlayState* play) { SavedSceneFlags* savedSceneFlags = &gSaveContext.sceneFlags[play->sceneNum]; savedSceneFlags->chest = play->actorCtx.flags.chest; savedSceneFlags->swch = play->actorCtx.flags.swch; savedSceneFlags->clear = play->actorCtx.flags.clear; savedSceneFlags->collect = play->actorCtx.flags.collect; } void Play_SetRespawnData(PlayState* play, s32 respawnMode, s16 entranceIndex, s32 roomIndex, s32 playerParams, Vec3f* pos, s16 yaw) { RespawnData* respawnData = &gSaveContext.respawn[respawnMode]; respawnData->entranceIndex = entranceIndex; respawnData->roomIndex = roomIndex; respawnData->pos = *pos; respawnData->yaw = yaw; respawnData->playerParams = playerParams; respawnData->tempSwchFlags = play->actorCtx.flags.tempSwch; respawnData->tempCollectFlags = play->actorCtx.flags.tempCollect; } void Play_SetupRespawnPoint(PlayState* play, s32 respawnMode, s32 playerParams) { Player* player = GET_PLAYER(play); s32 entranceIndex; s8 roomIndex; if ((play->sceneNum != SCENE_FAIRYS_FOUNTAIN) && (play->sceneNum != SCENE_GROTTOS)) { roomIndex = play->roomCtx.curRoom.num; entranceIndex = gSaveContext.entranceIndex; Play_SetRespawnData(play, respawnMode, entranceIndex, roomIndex, playerParams, &player->actor.world.pos, player->actor.shape.rot.y); } } void Play_TriggerVoidOut(PlayState* play) { gSaveContext.respawn[RESPAWN_MODE_DOWN].tempSwchFlags = play->actorCtx.flags.tempSwch; gSaveContext.respawn[RESPAWN_MODE_DOWN].tempCollectFlags = play->actorCtx.flags.tempCollect; gSaveContext.respawnFlag = 1; play->transitionTrigger = TRANS_TRIGGER_START; play->nextEntranceIndex = gSaveContext.respawn[RESPAWN_MODE_DOWN].entranceIndex; play->transitionType = TRANS_TYPE_FADE_BLACK; } void Play_LoadToLastEntrance(PlayState* play) { gSaveContext.respawnFlag = -1; play->transitionTrigger = TRANS_TRIGGER_START; if ((play->sceneNum == SCENE_GANONS_TOWER_COLLAPSE_INTERIOR) || (play->sceneNum == SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR) || (play->sceneNum == SCENE_INSIDE_GANONS_CASTLE_COLLAPSE) || (play->sceneNum == SCENE_GANON_BOSS)) { play->nextEntranceIndex = ENTR_GANONS_TOWER_COLLAPSE_EXTERIOR_0; Item_Give(play, ITEM_SWORD_MASTER); } else if ((gSaveContext.entranceIndex == ENTR_HYRULE_FIELD_11) || (gSaveContext.entranceIndex == ENTR_HYRULE_FIELD_12) || (gSaveContext.entranceIndex == ENTR_HYRULE_FIELD_13) || (gSaveContext.entranceIndex == ENTR_HYRULE_FIELD_15)) { play->nextEntranceIndex = ENTR_HYRULE_FIELD_6; } else { play->nextEntranceIndex = gSaveContext.entranceIndex; } play->transitionType = TRANS_TYPE_FADE_BLACK; } void Play_TriggerRespawn(PlayState* play) { Play_SetupRespawnPoint(play, RESPAWN_MODE_DOWN, 0xDFF); Play_LoadToLastEntrance(play); } s32 func_800C0CB8(PlayState* play) { return (play->roomCtx.curRoom.meshHeader->base.type != 1) && (YREG(15) != 0x20) && (YREG(15) != 0x30) && (YREG(15) != 0x40) && (play->sceneNum != SCENE_CASTLE_COURTYARD_GUARDS_DAY); } s32 FrameAdvance_IsEnabled(PlayState* play) { return !!play->frameAdvCtx.enabled; } s32 func_800C0D34(PlayState* play, Actor* actor, s16* yaw) { TransitionActorEntry* transitionActor; s32 frontRoom; if (actor->category != ACTORCAT_DOOR) { return 0; } transitionActor = &play->transiActorCtx.list[(u16)actor->params >> 10]; frontRoom = transitionActor->sides[0].room; if (frontRoom == transitionActor->sides[1].room) { return 0; } if (frontRoom == actor->room) { *yaw = actor->shape.rot.y; } else { *yaw = actor->shape.rot.y + 0x8000; } return 1; } s32 func_800C0DB4(PlayState* play, Vec3f* pos) { WaterBox* waterBox; CollisionPoly* poly; Vec3f waterSurfacePos; s32 bgId; waterSurfacePos = *pos; if (WaterBox_GetSurface1(play, &play->colCtx, waterSurfacePos.x, waterSurfacePos.z, &waterSurfacePos.y, &waterBox) == true && pos->y < waterSurfacePos.y && BgCheck_EntityRaycastFloor3(&play->colCtx, &poly, &bgId, &waterSurfacePos) != BGCHECK_Y_MIN) { return true; } else { return false; } } void Play_PerformSave(PlayState* play) { if (play != NULL && gSaveContext.fileNum != 0xFF) { Play_SaveSceneFlags(play); gSaveContext.savedSceneNum = play->sceneNum; // Track values from temp B uint8_t prevB = gSaveContext.equips.buttonItems[0]; uint8_t prevStatus = gSaveContext.buttonStatus[0]; // Replicate the B button restore from minigames/epona that kaleido does if (gSaveContext.equips.buttonItems[0] == ITEM_SLINGSHOT || gSaveContext.equips.buttonItems[0] == ITEM_BOW || gSaveContext.equips.buttonItems[0] == ITEM_BOMBCHU || gSaveContext.equips.buttonItems[0] == ITEM_FISHING_POLE || (gSaveContext.equips.buttonItems[0] == ITEM_NONE && !Flags_GetInfTable(INFTABLE_SWORDLESS))) { gSaveContext.equips.buttonItems[0] = gSaveContext.buttonStatus[0]; Interface_RandoRestoreSwordless(); } Save_SaveFile(); // Restore temp B values back gSaveContext.equips.buttonItems[0] = prevB; gSaveContext.buttonStatus[0] = prevStatus; uint8_t triforceHuntCompleted = IS_RANDO && gSaveContext.triforcePiecesCollected == Randomizer_GetSettingValue(RSK_TRIFORCE_HUNT_PIECES_REQUIRED) && Randomizer_GetSettingValue(RSK_TRIFORCE_HUNT); if (CVarGetInteger("gAutosave", AUTOSAVE_OFF) != AUTOSAVE_OFF || triforceHuntCompleted) { Overlay_DisplayText(3.0f, "Game Saved"); } } }