#include "global.h" #include "buffers.h" #include "z64debug_display.h" #include "z64quake.h" #include "z64rumble.h" #include "z64shrink_window.h" #include "z64view.h" #include "overlays/gamestates/ovl_daytelop/z_daytelop.h" #include "overlays/gamestates/ovl_opening/z_opening.h" #include "overlays/gamestates/ovl_file_choose/z_file_choose.h" #include "overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope.h" s32 gDbgCamEnabled = false; u8 D_801D0D54 = false; s16 sTransitionFillTimer; Input D_801F6C18; TransitionTile sTransitionTile; s32 gTransitionTileState; VisMono sVisMono; Color_RGBA8_u32 gVisMonoColor; Struct_80140E80 D_801F6D38; Struct_80140E80* D_801F6D4C; BombersNotebook sBombersNotebook; u8 sBombersNotebookOpen; u8 sMotionBlurStatus; typedef enum { /* 0 */ MOTION_BLUR_OFF, /* 1 */ MOTION_BLUR_SETUP, /* 2 */ MOTION_BLUR_PROCESS } MotionBlurStatus; void Play_DrawMotionBlur(PlayState* this) { GraphicsContext* gfxCtx = this->state.gfxCtx; s32 alpha; Gfx* gfx; Gfx* gfxHead; if (R_MOTION_BLUR_PRIORITY_ENABLED) { alpha = R_MOTION_BLUR_PRIORITY_ALPHA; if (sMotionBlurStatus == MOTION_BLUR_OFF) { sMotionBlurStatus = MOTION_BLUR_SETUP; } } else if (R_MOTION_BLUR_ENABLED) { alpha = R_MOTION_BLUR_ALPHA; if (sMotionBlurStatus == MOTION_BLUR_OFF) { sMotionBlurStatus = MOTION_BLUR_SETUP; } } else { alpha = 0; sMotionBlurStatus = MOTION_BLUR_OFF; } if (sMotionBlurStatus != MOTION_BLUR_OFF) { OPEN_DISPS(gfxCtx); gfxHead = POLY_OPA_DISP; gfx = Graph_GfxPlusOne(gfxHead); gSPDisplayList(OVERLAY_DISP++, gfx); this->pauseBgPreRender.fbuf = gfxCtx->curFrameBuffer; this->pauseBgPreRender.fbufSave = this->unk_18E64; if (sMotionBlurStatus == MOTION_BLUR_PROCESS) { func_80170AE0(&this->pauseBgPreRender, &gfx, alpha); } else { sMotionBlurStatus = MOTION_BLUR_PROCESS; } func_801705B4(&this->pauseBgPreRender, &gfx); gSPEndDisplayList(gfx++); Graph_BranchDlist(gfxHead, gfx); POLY_OPA_DISP = gfx; CLOSE_DISPS(gfxCtx); } } void Play_InitMotionBlur(void) { R_MOTION_BLUR_ENABLED = false; R_MOTION_BLUR_PRIORITY_ENABLED = false; sMotionBlurStatus = MOTION_BLUR_OFF; } void Play_DestroyMotionBlur(void) { R_MOTION_BLUR_ENABLED = false; R_MOTION_BLUR_PRIORITY_ENABLED = false; sMotionBlurStatus = MOTION_BLUR_OFF; } void Play_SetMotionBlurAlpha(u32 alpha) { R_MOTION_BLUR_ALPHA = alpha; } void Play_EnableMotionBlur(u32 alpha) { R_MOTION_BLUR_ALPHA = alpha; R_MOTION_BLUR_ENABLED = true; } void Play_DisableMotionBlur(void) { R_MOTION_BLUR_ENABLED = false; } // How much each color component contributes to the intensity image. // These coefficients are close to what the YUV color space defines Y (luminance) as: // https://en.wikipedia.org/wiki/YUV#Conversion_to/from_RGB #define PLAY_INTENSITY_RED 2 #define PLAY_INTENSITY_GREEN 4 #define PLAY_INTENSITY_BLUE 1 #define PLAY_INTENSITY_NORM (0x1F * PLAY_INTENSITY_RED + 0x1F * PLAY_INTENSITY_GREEN + 0x1F * PLAY_INTENSITY_BLUE) #define PLAY_INTENSITY_MIX(r, g, b, m) \ ((((r)*PLAY_INTENSITY_RED + (g)*PLAY_INTENSITY_GREEN + (b)*PLAY_INTENSITY_BLUE) * (m)) / PLAY_INTENSITY_NORM) /** * Converts an RGBA16 buffer to an Intensity Image * * @param[out] destI destination buffer * @param[in] srcRgba16 source buffer * @param[in] rgba16Width width of a full row for the RGBA16 * @param[in] pixelLeft X coordinate of the top-left RGBA16 pixel to start with * @param[in] pixelTop Y coordinate of the top-left RGBA16 pixel to start with * @param[in] pixelRight X coordinate of the bottom-right RGBA16 pixel to end with * @param[in] pixelBottom Y coordinate of the bottom-right RGBA16 pixel to end with * @param[in] bitDepth bit depth for the intensity image */ void Play_ConvertRgba16ToIntensityImage(void* destI, u16* srcRgba16, s32 rgba16Width, s32 pixelLeft, s32 pixelTop, s32 pixelRight, s32 pixelBottom, s32 bitDepth) { s32 i; s32 j; u32 pixel; u32 r; u32 g; u32 b; switch (bitDepth) { case 4: { u8* destI4 = destI; u32 upper; u32 lower; for (i = pixelTop; i <= pixelBottom; i++) { for (j = pixelLeft; j <= pixelRight; j += 2) { pixel = srcRgba16[i * rgba16Width + j]; r = RGBA16_GET_R(pixel); g = RGBA16_GET_G(pixel); b = RGBA16_GET_B(pixel); upper = PLAY_INTENSITY_MIX(r, g, b, 15); pixel = srcRgba16[i * rgba16Width + j + 1]; r = RGBA16_GET_R(pixel); g = RGBA16_GET_G(pixel); b = RGBA16_GET_B(pixel); lower = PLAY_INTENSITY_MIX(r, g, b, 15); *(destI4++) = (upper << 4) | lower; } } break; } case 5: { u8* destI5 = destI; for (i = pixelTop; i <= pixelBottom; i++) { for (j = pixelLeft; j <= pixelRight; j++) { pixel = srcRgba16[i * rgba16Width + j]; r = RGBA16_GET_R(pixel); g = RGBA16_GET_G(pixel); b = RGBA16_GET_B(pixel); pixel = 0; *(destI5++) = PLAY_INTENSITY_MIX(r, g, b, 255) & 0xF8; } } break; } case 8: { u8* destI8 = destI; for (i = pixelTop; i <= pixelBottom; i++) { for (j = pixelLeft; j <= pixelRight; j++) { pixel = srcRgba16[i * rgba16Width + j]; r = RGBA16_GET_R(pixel); g = RGBA16_GET_G(pixel); b = RGBA16_GET_B(pixel); *(destI8++) = PLAY_INTENSITY_MIX(r, g, b, 255); } } break; } case 16: { u16* destI16 = destI; for (i = pixelTop; i <= pixelBottom; i++) { for (j = pixelLeft; j <= pixelRight; j++) { *(destI16++) = srcRgba16[i * rgba16Width + j]; } } break; } } } void Play_SetMotionBlurPriorityAlpha(u32 alpha) { R_MOTION_BLUR_PRIORITY_ALPHA = alpha; } void Play_EnableMotionBlurPriority(u32 alpha) { R_MOTION_BLUR_PRIORITY_ALPHA = alpha; R_MOTION_BLUR_PRIORITY_ENABLED = true; } void Play_DisableMotionBlurPriority(void) { R_MOTION_BLUR_PRIORITY_ENABLED = false; } // Will take the photograph, but doesn't compress and save it void Play_TriggerPictoPhoto(void) { R_PICTO_PHOTO_STATE = PICTO_PHOTO_STATE_SETUP; } void Play_TakePictoPhoto(PreRender* prerender) { PreRender_ApplyFilters(prerender); Play_ConvertRgba16ToIntensityImage(gPictoPhotoI8, prerender->fbufSave, SCREEN_WIDTH, PICTO_PHOTO_TOPLEFT_X, PICTO_PHOTO_TOPLEFT_Y, (PICTO_PHOTO_TOPLEFT_X + PICTO_PHOTO_WIDTH) - 1, (PICTO_PHOTO_TOPLEFT_Y + PICTO_PHOTO_HEIGHT) - 1, 8); } s32 Play_ChooseDynamicTransition(PlayState* this, s32 transitionType) { s32 nextTransitionType = transitionType; if (transitionType == TRANS_TYPE_FADE_DYNAMIC) { if (!gSaveContext.save.isNight) { nextTransitionType = TRANS_TYPE_FADE_WHITE; } else { nextTransitionType = TRANS_TYPE_FADE_BLACK; } } if (nextTransitionType != transitionType) { this->transitionType = nextTransitionType; } return nextTransitionType; } void Play_SetupTransition(PlayState* this, s32 transitionType) { TransitionContext* transitionCtx = &this->transitionCtx; s32 fbdemoType; bzero(transitionCtx, sizeof(TransitionContext)); fbdemoType = -1; if (transitionType & TRANS_TYPE_WIPE3) { fbdemoType = FBDEMO_WIPE3; } else if ((transitionType & 0x78) == TRANS_TYPE_WIPE4) { // Checks not only type, but also a max value of 39 fbdemoType = FBDEMO_WIPE4; } else if (!(transitionType & (TRANS_TYPE_WIPE3 | TRANS_TYPE_WIPE4))) { switch (transitionType) { case TRANS_TYPE_TRIFORCE: fbdemoType = FBDEMO_TRIFORCE; break; case TRANS_TYPE_WIPE: case TRANS_TYPE_WIPE_FAST: fbdemoType = FBDEMO_WIPE1; 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: fbdemoType = FBDEMO_FADE; break; case TRANS_TYPE_FILL_WHITE_FAST: case TRANS_TYPE_FILL_WHITE: this->transitionMode = TRANS_MODE_FILL_WHITE_INIT; break; case TRANS_TYPE_INSTANT: this->transitionMode = TRANS_MODE_INSTANT; break; case TRANS_TYPE_FILL_BROWN: this->transitionMode = TRANS_MODE_FILL_BROWN_INIT; break; case TRANS_TYPE_SANDSTORM_PERSIST: this->transitionMode = TRANS_MODE_SANDSTORM_INIT; break; case TRANS_TYPE_SANDSTORM_END: this->transitionMode = TRANS_MODE_SANDSTORM_END_INIT; break; case TRANS_TYPE_CS_BLACK_FILL: this->transitionMode = TRANS_MODE_CS_BLACK_FILL_INIT; break; case TRANS_TYPE_CIRCLE: fbdemoType = FBDEMO_CIRCLE; break; case TRANS_TYPE_WIPE5: fbdemoType = FBDEMO_WIPE5; break; default: fbdemoType = -1; __assert("../z_play.c", 1420); } } else { fbdemoType = -1; __assert("../z_play.c", 1423); } transitionCtx->transitionType = transitionType; transitionCtx->fbdemoType = fbdemoType; if (fbdemoType != -1) { Transition_Init(transitionCtx); } } void Play_ClearTransition(PlayState* this) { if (this->transitionCtx.fbdemoType != -1) { Transition_Destroy(&this->transitionCtx); } this->transitionCtx.transitionType = -1; } Gfx* Play_SetFog(PlayState* this, Gfx* gfx) { s32 fogFar = this->lightCtx.zFar * (5.0f / 64.0f); return Gfx_SetFogWithSync(gfx, this->lightCtx.fogColor.r, this->lightCtx.fogColor.g, this->lightCtx.fogColor.b, 0, this->lightCtx.fogNear, ((fogFar <= 1000) ? 1000 : fogFar)); } void Play_Destroy(GameState* thisx) { PlayState* this = (PlayState*)thisx; GraphicsContext* gfxCtx = this->state.gfxCtx; if (sBombersNotebookOpen) { MsgEvent_SendNullTask(); func_80178750(); gfxCtx->curFrameBuffer = SysCfb_GetFbPtr(gfxCtx->framebufferIndex % 2); gfxCtx->zbuffer = SysCfb_GetZBuffer(); gfxCtx->viMode = D_801FBB88; gfxCtx->viConfigFeatures = gViConfigFeatures; gfxCtx->xScale = gViConfigXScale; gfxCtx->yScale = gViConfigYScale; gfxCtx->updateViMode = true; sBombersNotebookOpen = false; } BombersNotebook_Destroy(&sBombersNotebook); this->state.gfxCtx->callback = NULL; this->state.gfxCtx->callbackArg = 0; Play_DestroyMotionBlur(); if (R_PAUSE_BG_PRERENDER_STATE != PAUSE_BG_PRERENDER_OFF) { PreRender_ApplyFiltersSlowlyDestroy(&this->pauseBgPreRender); R_PAUSE_BG_PRERENDER_STATE = PAUSE_BG_PRERENDER_OFF; } R_PICTO_PHOTO_STATE = PICTO_PHOTO_STATE_OFF; PreRender_Destroy(&this->pauseBgPreRender); this->unk_18E58 = NULL; this->pictoPhotoI8 = NULL; this->unk_18E60 = NULL; this->unk_18E64 = NULL; this->unk_18E68 = NULL; Effect_DestroyAll(this); EffectSS_Clear(this); CollisionCheck_DestroyContext(this, &this->colChkCtx); if (gTransitionTileState == TRANS_TILE_READY) { TransitionTile_Destroy(&sTransitionTile); gTransitionTileState = TRANS_TILE_OFF; } if ((this->transitionMode == TRANS_MODE_INSTANCE_RUNNING) || D_801D0D54) { this->transitionCtx.destroy(&this->transitionCtx.instanceData); Play_ClearTransition(this); this->transitionMode = TRANS_MODE_OFF; } ShrinkWindow_Destroy(); TransitionFade_Destroy(&this->unk_18E48); VisMono_Destroy(&sVisMono); func_80140EA0(D_801F6D4C); D_801F6D4C = NULL; if (CHECK_WEEKEVENTREG(WEEKEVENTREG_92_80)) { Actor_CleanupContext(&this->actorCtx, this); } CLEAR_WEEKEVENTREG(WEEKEVENTREG_92_80); Interface_Destroy(this); KaleidoScopeCall_Destroy(this); KaleidoManager_Destroy(); ZeldaArena_Cleanup(); } #define PLAY_COMPRESS_BITS 5 #define PLAY_DECOMPRESS_BITS 8 void Play_CompressI8ToI5(void* srcI8, void* destI5, size_t size) { u32 i; u8* src = srcI8; s8* dest = destI5; s32 bitsLeft = PLAY_DECOMPRESS_BITS; // Bits left in the current dest pixel left to compress into u32 destPixel = 0; s32 shift; u32 srcPixel; for (i = 0; i < size; i++) { srcPixel = *src++; srcPixel = (srcPixel * 0x1F + 0x80) / 0xFF; shift = bitsLeft - PLAY_COMPRESS_BITS; if (shift > 0) { destPixel |= srcPixel << shift; } else { destPixel |= srcPixel >> -shift; *dest++ = destPixel; shift += PLAY_DECOMPRESS_BITS; destPixel = srcPixel << shift; } bitsLeft = shift; } if (bitsLeft < PLAY_DECOMPRESS_BITS) { *dest = destPixel; } } void Play_DecompressI5ToI8(void* srcI5, void* destI8, size_t size) { u32 i; u8* src = srcI5; s8* dest = destI8; s32 bitsLeft = PLAY_DECOMPRESS_BITS; // Bits left in the current src pixel left to decompress u32 destPixel; s32 shift; u32 srcPixel = *src++; for (i = 0; i < size; i++) { shift = bitsLeft - PLAY_COMPRESS_BITS; if (shift > 0) { destPixel = 0; destPixel |= srcPixel >> shift; } else { destPixel = 0; destPixel |= srcPixel << -shift; srcPixel = *src++; shift += PLAY_DECOMPRESS_BITS; destPixel |= srcPixel >> shift; } destPixel = (destPixel & 0x1F) * 0xFF / 0x1F; *dest++ = destPixel; bitsLeft = shift; } } f32 Play_GetWaterSurface(PlayState* this, Vec3f* pos, s32* lightIndex) { Player* player = GET_PLAYER(this); f32 waterSurfaceY = player->actor.world.pos.y; WaterBox* waterBox; s32 bgId; if (!WaterBox_GetSurfaceImpl(this, &this->colCtx, pos->x, pos->z, &waterSurfaceY, &waterBox, &bgId)) { return BGCHECK_Y_MIN; } if (waterSurfaceY < pos->y) { return BGCHECK_Y_MIN; } *lightIndex = WaterBox_GetLightSettingIndex(&this->colCtx, waterBox); return waterSurfaceY; } void Play_UpdateWaterCamera(PlayState* this, Camera* camera) { static s16 sQuakeIndex = -1; static s16 sIsCameraUnderwater = false; s32 pad; s32 lightIndex; Player* player = GET_PLAYER(this); sIsCameraUnderwater = camera->stateFlags & CAM_STATE_UNDERWATER; if (Play_GetWaterSurface(this, &camera->eye, &lightIndex) != BGCHECK_Y_MIN) { if (!sIsCameraUnderwater) { Camera_SetFlags(camera, CAM_STATE_UNDERWATER); sQuakeIndex = -1; Distortion_SetType(DISTORTION_TYPE_UNDERWATER_ENTRY); Distortion_SetCountdown(80); } func_801A3EC0(0x20); func_800F6834(this, lightIndex); if ((sQuakeIndex == -1) || (Quake_GetCountdown(sQuakeIndex) == 10)) { s16 quakeIndex = Quake_Add(camera, QUAKE_TYPE_5); sQuakeIndex = quakeIndex; if (quakeIndex != 0) { Quake_SetSpeed(sQuakeIndex, 550); Quake_SetQuakeValues(sQuakeIndex, 1, 1, 180, 0); Quake_SetCountdown(sQuakeIndex, 1000); } } if (player->stateFlags3 & PLAYER_STATE3_8000) { Distortion_SetType(DISTORTION_TYPE_ZORA_SWIMMING); Distortion_ClearType(DISTORTION_TYPE_NON_ZORA_SWIMMING); } else { Distortion_SetType(DISTORTION_TYPE_NON_ZORA_SWIMMING); Distortion_ClearType(DISTORTION_TYPE_ZORA_SWIMMING); } } else { if (sIsCameraUnderwater) { Camera_ClearFlags(camera, CAM_STATE_UNDERWATER); } Distortion_ClearType(DISTORTION_TYPE_NON_ZORA_SWIMMING); Distortion_ClearType(DISTORTION_TYPE_UNDERWATER_ENTRY); Distortion_ClearType(DISTORTION_TYPE_ZORA_SWIMMING); if (sQuakeIndex != 0) { Quake_Remove(sQuakeIndex); } func_800F694C(this); func_801A3EC0(0); } } void Play_UpdateTransition(PlayState* this) { s32 pad; if (this->transitionMode == TRANS_MODE_OFF) { return; } switch (this->transitionMode) { case TRANS_MODE_SETUP: if (this->transitionTrigger != TRANS_TRIGGER_END) { s16 sceneLayer = 0; Interface_SetHudVisibility(HUD_VISIBILITY_NONE); if (gSaveContext.nextCutsceneIndex >= 0xFFF0) { sceneLayer = (gSaveContext.nextCutsceneIndex & 0xF) + 1; } if ((!(Entrance_GetTransitionFlags(this->nextEntrance + sceneLayer) & 0x8000) || ((this->nextEntrance == ENTRANCE(PATH_TO_MOUNTAIN_VILLAGE, 1)) && !CHECK_WEEKEVENTREG(WEEKEVENTREG_33_80)) || ((this->nextEntrance == ENTRANCE(ROAD_TO_SOUTHERN_SWAMP, 1)) && !CHECK_WEEKEVENTREG(WEEKEVENTREG_20_02)) || ((this->nextEntrance == ENTRANCE(TERMINA_FIELD, 2)) && !CHECK_WEEKEVENTREG(WEEKEVENTREG_55_80)) || ((this->nextEntrance == ENTRANCE(ROAD_TO_IKANA, 1)) && !CHECK_WEEKEVENTREG(WEEKEVENTREG_52_20))) && (!func_800FE590(this) || (Entrance_GetSceneId(this->nextEntrance + sceneLayer) < 0) || (Audio_GetActiveSequence(SEQ_PLAYER_BGM_MAIN) != NA_BGM_FINAL_HOURS))) { func_801A4058(20); gSaveContext.seqId = (u8)NA_BGM_DISABLED; gSaveContext.ambienceId = AMBIENCE_ID_DISABLED; } if (func_800FD768()) { func_801A4058(20); gSaveContext.seqId = (u8)NA_BGM_DISABLED; gSaveContext.ambienceId = AMBIENCE_ID_DISABLED; } if (func_800FE590(this) && (Entrance_GetSceneId(this->nextEntrance + sceneLayer) >= 0) && (Audio_GetActiveSequence(SEQ_PLAYER_BGM_MAIN) == NA_BGM_FINAL_HOURS)) { func_801A41C8(20); } } if (!D_801D0D54) { Play_SetupTransition(this, Play_ChooseDynamicTransition(this, this->transitionType)); } if (this->transitionMode >= TRANS_MODE_FILL_WHITE_INIT) { // non-instance modes break out of this switch break; } // fallthrough case TRANS_MODE_INSTANCE_INIT: { s32 transWipeSpeed; s32 transFadeDuration; u32 color; this->transitionCtx.init(&this->transitionCtx.instanceData); if (this->transitionCtx.transitionType & (TRANS_TYPE_WIPE3 | TRANS_TYPE_WIPE4)) { this->transitionCtx.setType(&this->transitionCtx.instanceData, this->transitionCtx.transitionType | TRANS_TYPE_SET_PARAMS); } if ((this->transitionCtx.transitionType == TRANS_TYPE_WIPE_FAST) || (this->transitionCtx.transitionType == TRANS_TYPE_FILL_WHITE_FAST)) { //! @bug TRANS_TYPE_FILL_WHITE_FAST will never reach this code. //! It is a non-instance type transition which doesn't run this case. transWipeSpeed = 28; } else { transWipeSpeed = 14; } gSaveContext.transWipeSpeed = transWipeSpeed; switch (this->transitionCtx.transitionType) { case TRANS_TYPE_FADE_BLACK_FAST: case TRANS_TYPE_FADE_WHITE_FAST: transFadeDuration = 20; break; case TRANS_TYPE_FADE_BLACK_SLOW: case TRANS_TYPE_FADE_WHITE_SLOW: transFadeDuration = 150; break; case TRANS_TYPE_FADE_WHITE_INSTANT: transFadeDuration = 2; break; default: transFadeDuration = 60; break; } gSaveContext.transFadeDuration = transFadeDuration; switch (this->transitionCtx.transitionType) { case TRANS_TYPE_FADE_WHITE: case TRANS_TYPE_FADE_WHITE_FAST: case TRANS_TYPE_FADE_WHITE_SLOW: case TRANS_TYPE_FADE_WHITE_CS_DELAYED: case TRANS_TYPE_FADE_WHITE_INSTANT: color = RGBA8(160, 160, 160, 255); break; case TRANS_TYPE_FADE_GREEN: color = RGBA8(140, 140, 100, 255); break; case TRANS_TYPE_FADE_BLUE: color = RGBA8(70, 100, 110, 255); break; default: color = RGBA8(0, 0, 0, 255); break; } this->transitionCtx.setColor(&this->transitionCtx.instanceData, color); if (this->transitionCtx.setEnvColor != NULL) { this->transitionCtx.setEnvColor(&this->transitionCtx.instanceData, color); } this->transitionCtx.setType(&this->transitionCtx.instanceData, (this->transitionTrigger == TRANS_TRIGGER_END) ? TRANS_INSTANCE_TYPE_FILL_OUT : TRANS_INSTANCE_TYPE_FILL_IN); this->transitionCtx.start(&this->transitionCtx.instanceData); if (this->transitionCtx.transitionType == TRANS_TYPE_FADE_WHITE_CS_DELAYED) { this->transitionMode = TRANS_MODE_INSTANCE_WAIT; } else { this->transitionMode = TRANS_MODE_INSTANCE_RUNNING; } break; } case TRANS_MODE_INSTANCE_RUNNING: if (this->transitionCtx.isDone(&this->transitionCtx.instanceData)) { if (this->transitionTrigger != TRANS_TRIGGER_END) { if (this->transitionCtx.transitionType == TRANS_TYPE_CIRCLE) { D_801D0D54 = false; } if (gSaveContext.gameMode == 4) { STOP_GAMESTATE(&this->state); SET_NEXT_GAMESTATE(&this->state, TitleSetup_Init, sizeof(TitleSetupState)); } else if (gSaveContext.gameMode != 2) { STOP_GAMESTATE(&this->state); SET_NEXT_GAMESTATE(&this->state, Play_Init, sizeof(PlayState)); gSaveContext.save.entrance = this->nextEntrance; if (gSaveContext.minigameStatus == MINIGAME_STATUS_ACTIVE) { gSaveContext.minigameStatus = MINIGAME_STATUS_END; } } else { // 2 STOP_GAMESTATE(&this->state); SET_NEXT_GAMESTATE(&this->state, FileSelect_Init, sizeof(FileSelectState)); } } else { if (this->transitionCtx.transitionType == TRANS_TYPE_CIRCLE) { D_801D0D54 = true; } else { this->transitionCtx.destroy(&this->transitionCtx.instanceData); Play_ClearTransition(this); } this->transitionMode = TRANS_MODE_OFF; if (gTransitionTileState == TRANS_TILE_READY) { TransitionTile_Destroy(&sTransitionTile); gTransitionTileState = TRANS_TILE_OFF; Game_SetFramerateDivisor(&this->state, 3); } } this->transitionTrigger = TRANS_TRIGGER_OFF; } else { this->transitionCtx.update(&this->transitionCtx.instanceData, this->state.framerateDivisor); } break; } // update non-instance transitions switch (this->transitionMode) { case TRANS_MODE_FILL_WHITE_INIT: sTransitionFillTimer = 0; this->envCtx.fillScreen = true; this->envCtx.screenFillColor[0] = 160; this->envCtx.screenFillColor[1] = 160; this->envCtx.screenFillColor[2] = 160; if (this->transitionTrigger != TRANS_TRIGGER_END) { this->envCtx.screenFillColor[3] = 0; this->transitionMode = TRANS_MODE_FILL_IN; } else { this->envCtx.screenFillColor[3] = 255; this->transitionMode = TRANS_MODE_FILL_OUT; } break; case TRANS_MODE_FILL_IN: this->envCtx.screenFillColor[3] = (sTransitionFillTimer / 20.0f) * 255.0f; if (sTransitionFillTimer >= 20) { STOP_GAMESTATE(&this->state); SET_NEXT_GAMESTATE(&this->state, Play_Init, sizeof(PlayState)); gSaveContext.save.entrance = this->nextEntrance; this->transitionTrigger = TRANS_TRIGGER_OFF; this->transitionMode = TRANS_MODE_OFF; } else { sTransitionFillTimer++; } break; case TRANS_MODE_FILL_OUT: this->envCtx.screenFillColor[3] = (1.0f - (sTransitionFillTimer / 20.0f)) * 255.0f; if (sTransitionFillTimer >= 20) { gTransitionTileState = TRANS_TILE_OFF; Game_SetFramerateDivisor(&this->state, 3); this->transitionTrigger = TRANS_TRIGGER_OFF; this->transitionMode = TRANS_MODE_OFF; this->envCtx.fillScreen = false; } else { sTransitionFillTimer++; } break; case TRANS_MODE_FILL_BROWN_INIT: sTransitionFillTimer = 0; this->envCtx.fillScreen = true; this->envCtx.screenFillColor[0] = 170; this->envCtx.screenFillColor[1] = 160; this->envCtx.screenFillColor[2] = 150; if (this->transitionTrigger != TRANS_TRIGGER_END) { this->envCtx.screenFillColor[3] = 0; this->transitionMode = TRANS_MODE_FILL_IN; } else { this->envCtx.screenFillColor[3] = 255; this->transitionMode = TRANS_MODE_FILL_OUT; } break; case TRANS_MODE_INSTANT: if (this->transitionTrigger != TRANS_TRIGGER_END) { STOP_GAMESTATE(&this->state); SET_NEXT_GAMESTATE(&this->state, Play_Init, sizeof(PlayState)); gSaveContext.save.entrance = this->nextEntrance; this->transitionTrigger = TRANS_TRIGGER_OFF; this->transitionMode = TRANS_MODE_OFF; } else { gTransitionTileState = TRANS_TILE_OFF; Game_SetFramerateDivisor(&this->state, 3); this->transitionTrigger = TRANS_TRIGGER_OFF; this->transitionMode = TRANS_MODE_OFF; } break; case TRANS_MODE_INSTANCE_WAIT: if (gSaveContext.cutsceneTransitionControl != 0) { this->transitionMode = TRANS_MODE_INSTANCE_RUNNING; } break; case TRANS_MODE_SANDSTORM_INIT: if (this->transitionTrigger != TRANS_TRIGGER_END) { this->envCtx.sandstormState = 1; this->transitionMode = TRANS_MODE_SANDSTORM; } else { this->envCtx.sandstormState = 2; this->envCtx.sandstormPrimA = 255; this->envCtx.sandstormEnvA = 255; this->transitionMode = TRANS_MODE_SANDSTORM; } break; case TRANS_MODE_SANDSTORM: func_8019F128(NA_SE_EV_SAND_STORM - SFX_FLAG); if (this->transitionTrigger == TRANS_TRIGGER_END) { if (this->envCtx.sandstormPrimA < 110) { gTransitionTileState = TRANS_TILE_OFF; Game_SetFramerateDivisor(&this->state, 3); this->transitionTrigger = TRANS_TRIGGER_OFF; this->transitionMode = TRANS_MODE_OFF; } } else { if (this->envCtx.sandstormEnvA == 255) { STOP_GAMESTATE(&this->state); SET_NEXT_GAMESTATE(&this->state, Play_Init, sizeof(PlayState)); gSaveContext.save.entrance = this->nextEntrance; this->transitionTrigger = TRANS_TRIGGER_OFF; this->transitionMode = TRANS_MODE_OFF; } } break; case TRANS_MODE_SANDSTORM_END_INIT: if (this->transitionTrigger == TRANS_TRIGGER_END) { this->envCtx.sandstormState = 4; this->envCtx.sandstormPrimA = 255; this->envCtx.sandstormEnvA = 255; // "It's here!!!!!!!!!" (void)"来た!!!!!!!!!!!!!!!!!!!!!"; this->transitionMode = TRANS_MODE_SANDSTORM_END; } else { this->transitionMode = TRANS_MODE_SANDSTORM_INIT; } break; case TRANS_MODE_SANDSTORM_END: func_8019F128(NA_SE_EV_SAND_STORM - SFX_FLAG); if (this->transitionTrigger == TRANS_TRIGGER_END) { if (this->envCtx.sandstormPrimA <= 0) { gTransitionTileState = TRANS_TILE_OFF; Game_SetFramerateDivisor(&this->state, 3); this->transitionTrigger = TRANS_TRIGGER_OFF; this->transitionMode = TRANS_MODE_OFF; } } break; case TRANS_MODE_CS_BLACK_FILL_INIT: sTransitionFillTimer = 0; this->envCtx.fillScreen = true; this->envCtx.screenFillColor[0] = 0; this->envCtx.screenFillColor[1] = 0; this->envCtx.screenFillColor[2] = 0; this->envCtx.screenFillColor[3] = 255; this->transitionMode = TRANS_MODE_CS_BLACK_FILL; break; case TRANS_MODE_CS_BLACK_FILL: if (gSaveContext.cutsceneTransitionControl != 0) { this->envCtx.screenFillColor[3] = gSaveContext.cutsceneTransitionControl; if (gSaveContext.cutsceneTransitionControl <= 100) { gTransitionTileState = TRANS_TILE_OFF; Game_SetFramerateDivisor(&this->state, 3); this->transitionTrigger = TRANS_TRIGGER_OFF; this->transitionMode = TRANS_MODE_OFF; } } break; } } const char D_801DFA34[][4] = { "all", "a", "a", "b", "b", "c", "c", "d", "d", "e", "e", "f", "fa", "fa", "fb", "fb", "fc", "fc", "fd", "fd", "fe", "fe", "fg", "fg", "fh", "fh", "fi", "fi", "fj", "fj", "fk", "fk", "f", "g", "g", "h", "h", "i", "i", "all", "all", "a", "b", "c", "d", "e", "f", "g", "h", "i", "f", "fa", "fb", "fc", "fd", "fe", "ff", "fg", "fh", "fi", "fj", "fk", }; #ifdef NON_MATCHING // Stack issues void Play_UpdateMain(PlayState* this) { Input* input = this->state.input; u8 freezeFlashTimer; s32 sp5C = false; gSegments[4] = VIRTUAL_TO_PHYSICAL(this->objectCtx.status[this->objectCtx.mainKeepIndex].segment); gSegments[5] = VIRTUAL_TO_PHYSICAL(this->objectCtx.status[this->objectCtx.subKeepIndex].segment); gSegments[2] = VIRTUAL_TO_PHYSICAL(this->sceneSegment); if (R_PICTO_PHOTO_STATE == PICTO_PHOTO_STATE_PROCESS) { R_PICTO_PHOTO_STATE = PICTO_PHOTO_STATE_READY; MsgEvent_SendNullTask(); Play_TakePictoPhoto(&this->pauseBgPreRender); R_PICTO_PHOTO_STATE = PICTO_PHOTO_STATE_OFF; } Actor_SetMovementScale(this->state.framerateDivisor); if (FrameAdvance_Update(&this->frameAdvCtx, &input[1])) { if ((this->transitionMode == TRANS_MODE_OFF) && (this->transitionTrigger != TRANS_TRIGGER_OFF)) { this->transitionMode = TRANS_MODE_SETUP; } if (gTransitionTileState != TRANS_TILE_OFF) { switch (gTransitionTileState) { case TRANS_TILE_PROCESS: if (TransitionTile_Init(&sTransitionTile, 10, 7) == NULL) { gTransitionTileState = TRANS_TILE_OFF; } else { sTransitionTile.zBuffer = gZBufferPtr; gTransitionTileState = TRANS_TILE_READY; Game_SetFramerateDivisor(&this->state, 1); } break; case TRANS_TILE_READY: TransitionTile_Update(&sTransitionTile); break; default: break; } } Play_UpdateTransition(this); if (gTransitionTileState != TRANS_TILE_READY) { if ((gSaveContext.gameMode == 0) && (((this->msgCtx.msgMode == 0)) || ((this->msgCtx.currentTextId == 0xFF) && (this->msgCtx.msgMode == 0x42) && (this->msgCtx.unk12020 == 0x41)) || ((this->msgCtx.currentTextId >= 0x100) && (this->msgCtx.currentTextId <= 0x200))) && (this->gameOverCtx.state == GAMEOVER_INACTIVE)) { KaleidoSetup_Update(this); } sp5C = (this->pauseCtx.state != 0) || (this->pauseCtx.debugEditor != DEBUG_EDITOR_NONE); AnimationContext_Reset(&this->animationCtx); Object_UpdateBank(&this->objectCtx); if (!sp5C && (IREG(72) == 0)) { this->gameplayFrames++; Rumble_SetUpdateEnabled(true); if ((this->actorCtx.freezeFlashTimer != 0) && (this->actorCtx.freezeFlashTimer-- < 5)) { freezeFlashTimer = this->actorCtx.freezeFlashTimer; if ((freezeFlashTimer > 0) && ((freezeFlashTimer % 2) != 0)) { this->envCtx.fillScreen = true; this->envCtx.screenFillColor[0] = this->envCtx.screenFillColor[1] = this->envCtx.screenFillColor[2] = 150; this->envCtx.screenFillColor[3] = 80; } else { this->envCtx.fillScreen = false; } } else { Room_HandleLoadCallbacks(this, &this->roomCtx); CollisionCheck_AT(this, &this->colChkCtx); CollisionCheck_OC(this, &this->colChkCtx); CollisionCheck_Damage(this, &this->colChkCtx); CollisionCheck_ClearContext(this, &this->colChkCtx); if (!this->haltAllActors) { Actor_UpdateAll(this, &this->actorCtx); } Cutscene_Update1(this, &this->csCtx); Cutscene_Update2(this, &this->csCtx); Effect_UpdateAll(this); EffectSS_UpdateAllParticles(this); EffFootmark_Update(this); } } else { Rumble_SetUpdateEnabled(false); } Room_Noop(this, &this->roomCtx.curRoom, &input[1], 0); Room_Noop(this, &this->roomCtx.prevRoom, &input[1], 1); Skybox_Update(&this->skyboxCtx); if ((this->pauseCtx.state != 0) || (this->pauseCtx.debugEditor != DEBUG_EDITOR_NONE)) { KaleidoScopeCall_Update(this); } else if (this->gameOverCtx.state != GAMEOVER_INACTIVE) { GameOver_Update(this); } Message_Update(this); Interface_Update(this); AnimationContext_Update(this, &this->animationCtx); SoundSource_UpdateAll(this); ShrinkWindow_Update(this->state.framerateDivisor); TransitionFade_Update(&this->unk_18E48, this->state.framerateDivisor); } } if (!sp5C || gDbgCamEnabled) { s32 sp54; // camId Vec3s sp48; // InputDir this->nextCamera = this->activeCamId; for (sp54 = 0; sp54 < 4; sp54++) { if ((sp54 != this->nextCamera) && (this->cameraPtrs[sp54] != NULL)) { Camera_Update(&sp48, this->cameraPtrs[sp54]); } } Camera_Update(&sp48, this->cameraPtrs[this->nextCamera]); } if (!sp5C) { Play_UpdateWaterCamera(this, this->cameraPtrs[this->nextCamera]); Distortion_Update(); } Environment_Update(this, &this->envCtx, &this->lightCtx, &this->pauseCtx, &this->msgCtx, &this->gameOverCtx, this->state.gfxCtx); if (this->sramCtx.status != 0) { if (gSaveContext.save.isOwlSave) { func_80147198(&this->sramCtx); } else { func_80147068(&this->sramCtx); } } } #else void Play_UpdateMain(PlayState* this); #pragma GLOBAL_ASM("asm/non_matchings/code/z_play/Play_UpdateMain.s") #endif void Play_Update(PlayState* this) { if (!sBombersNotebookOpen) { if (this->pauseCtx.bombersNotebookOpen) { sBombersNotebookOpen = true; sBombersNotebook.unk_00 = 0; } } else if (CHECK_BTN_ALL(CONTROLLER1(&this->state)->press.button, BTN_L) || CHECK_BTN_ALL(CONTROLLER1(&this->state)->press.button, BTN_B) || CHECK_BTN_ALL(CONTROLLER1(&this->state)->press.button, BTN_START) || (gIrqMgrResetStatus != 0)) { sBombersNotebookOpen = false; this->pauseCtx.bombersNotebookOpen = false; sBombersNotebook.unk_00 = 0; this->msgCtx.msgLength = 0; this->msgCtx.msgMode = 0; this->msgCtx.currentTextId = 0; this->msgCtx.stateTimer = 0; play_sound(NA_SE_SY_CANCEL); } if (sBombersNotebookOpen) { BombersNotebook_Update(this, &sBombersNotebook, this->state.input); Message_Update(this); } else { Play_UpdateMain(this); } } void Play_PostWorldDraw(PlayState* this) { if ((this->pauseCtx.state != 0) || (this->pauseCtx.debugEditor != DEBUG_EDITOR_NONE)) { KaleidoScopeCall_Draw(this); } if (gSaveContext.gameMode == 0) { Interface_Draw(this); } if (((this->pauseCtx.state == 0) && (this->pauseCtx.debugEditor == DEBUG_EDITOR_NONE)) || (this->msgCtx.currentTextId != 0xFF)) { Message_Draw(this); } if (this->gameOverCtx.state != GAMEOVER_INACTIVE) { GameOver_FadeLights(this); } // Shrink the whole screen display (at the end of First and Second Day by default) if (gSaveContext.screenScaleFlag) { Gfx* nextOpa; Gfx* opa; GraphicsContext* gfxCtx = this->state.gfxCtx; D_801F6D4C->scale = gSaveContext.screenScale / 1000.0f; OPEN_DISPS(gfxCtx); opa = POLY_OPA_DISP; nextOpa = Graph_GfxPlusOne(opa); gSPDisplayList(OVERLAY_DISP++, nextOpa); func_80141778(D_801F6D4C, &nextOpa, this->unk_18E60, gfxCtx); gSPEndDisplayList(nextOpa++); Graph_BranchDlist(opa, nextOpa); POLY_OPA_DISP = nextOpa; CLOSE_DISPS(gfxCtx); } } #ifdef NON_MATCHING // Stack issues and 1 small issue around Play_PostWorldDraw void Play_DrawMain(PlayState* this) { GraphicsContext* gfxCtx = this->state.gfxCtx; Lights* sp268; Vec3f sp25C; u8 sp25B = false; if (R_PAUSE_BG_PRERENDER_STATE >= PAUSE_BG_PRERENDER_UNK4) { PreRender_ApplyFiltersSlowlyDestroy(&this->pauseBgPreRender); R_PAUSE_BG_PRERENDER_STATE = PAUSE_BG_PRERENDER_OFF; } if ((R_PAUSE_BG_PRERENDER_STATE <= PAUSE_BG_PRERENDER_SETUP) && (gTransitionTileState <= TRANS_TILE_SETUP)) { if (this->skyboxCtx.skyboxShouldDraw || (this->roomCtx.curRoom.roomShape->base.type == ROOM_SHAPE_TYPE_IMAGE)) { func_8012CF0C(gfxCtx, false, true, 0, 0, 0); } else { func_8012CF0C(gfxCtx, true, true, this->lightCtx.fogColor.r, this->lightCtx.fogColor.g, this->lightCtx.fogColor.b); } } else { func_8012CF0C(gfxCtx, false, false, 0, 0, 0); } OPEN_DISPS(gfxCtx); gSegments[4] = VIRTUAL_TO_PHYSICAL(this->objectCtx.status[this->objectCtx.mainKeepIndex].segment); gSegments[5] = VIRTUAL_TO_PHYSICAL(this->objectCtx.status[this->objectCtx.subKeepIndex].segment); gSegments[2] = VIRTUAL_TO_PHYSICAL(this->sceneSegment); gSPSegment(POLY_OPA_DISP++, 0x04, this->objectCtx.status[this->objectCtx.mainKeepIndex].segment); gSPSegment(POLY_XLU_DISP++, 0x04, this->objectCtx.status[this->objectCtx.mainKeepIndex].segment); gSPSegment(OVERLAY_DISP++, 0x04, this->objectCtx.status[this->objectCtx.mainKeepIndex].segment); gSPSegment(POLY_OPA_DISP++, 0x05, this->objectCtx.status[this->objectCtx.subKeepIndex].segment); gSPSegment(POLY_XLU_DISP++, 0x05, this->objectCtx.status[this->objectCtx.subKeepIndex].segment); gSPSegment(OVERLAY_DISP++, 0x05, this->objectCtx.status[this->objectCtx.subKeepIndex].segment); gSPSegment(POLY_OPA_DISP++, 0x02, this->sceneSegment); gSPSegment(POLY_XLU_DISP++, 0x02, this->sceneSegment); gSPSegment(OVERLAY_DISP++, 0x02, this->sceneSegment); if (1) { f32 var_fv0; // zFar ShrinkWindow_Draw(gfxCtx); POLY_OPA_DISP = Play_SetFog(this, POLY_OPA_DISP); POLY_XLU_DISP = Play_SetFog(this, POLY_XLU_DISP); // zFar var_fv0 = this->lightCtx.zFar; if (var_fv0 > 12800.0f) { var_fv0 = 12800.0f; } View_SetPerspective(&this->view, this->view.fovy, this->view.zNear, var_fv0); View_Apply(&this->view, 0xF); // The billboard matrix temporarily stores the viewing matrix Matrix_MtxToMtxF(&this->view.viewing, &this->billboardMtxF); Matrix_MtxToMtxF(&this->view.projection, &this->viewProjectionMtxF); this->projectionMtxFDiagonal.x = this->viewProjectionMtxF.xx; this->projectionMtxFDiagonal.y = this->viewProjectionMtxF.yy; this->projectionMtxFDiagonal.z = -this->viewProjectionMtxF.zz; SkinMatrix_MtxFMtxFMult(&this->viewProjectionMtxF, &this->billboardMtxF, &this->viewProjectionMtxF); this->billboardMtxF.mf[3][2] = this->billboardMtxF.mf[3][1] = this->billboardMtxF.mf[3][0] = this->billboardMtxF.mf[2][3] = this->billboardMtxF.mf[1][3] = this->billboardMtxF.mf[0][3] = 0.0f; Matrix_Transpose(&this->billboardMtxF); this->billboardMtx = GRAPH_ALLOC(this->state.gfxCtx, 2 * sizeof(Mtx)); Matrix_MtxFToMtx(&this->billboardMtxF, this->billboardMtx); Matrix_RotateYF(BINANG_TO_RAD((s16)(Camera_GetCamDirYaw(GET_ACTIVE_CAM(this)) + 0x8000)), MTXMODE_NEW); Matrix_ToMtx(this->billboardMtx + 1); gSPSegment(POLY_OPA_DISP++, 0x01, this->billboardMtx); gSPSegment(POLY_XLU_DISP++, 0x01, this->billboardMtx); gSPSegment(OVERLAY_DISP++, 0x01, this->billboardMtx); if (1) { Gfx* sp218; Gfx* sp214 = POLY_OPA_DISP; sp218 = Graph_GfxPlusOne(sp214); gSPDisplayList(OVERLAY_DISP++, sp218); if (((this->transitionMode == TRANS_MODE_INSTANCE_RUNNING) || (this->transitionMode == TRANS_TYPE_INSTANT)) || D_801D0D54) { View spA8; View_Init(&spA8, gfxCtx); spA8.flags = 0xA; SET_FULLSCREEN_VIEWPORT(&spA8); View_ApplyTo(&spA8, &sp218); this->transitionCtx.draw(&this->transitionCtx.instanceData, &sp218); } TransitionFade_Draw(&this->unk_18E48, &sp218); if (gVisMonoColor.a != 0) { sVisMono.primColor.rgba = gVisMonoColor.rgba; VisMono_Draw(&sVisMono, &sp218); } gSPEndDisplayList(sp218++); Graph_BranchDlist(sp214, sp218); POLY_OPA_DISP = sp218; } if (gTransitionTileState == TRANS_TILE_READY) { Gfx* sp90 = POLY_OPA_DISP; TransitionTile_Draw(&sTransitionTile, &sp90); POLY_OPA_DISP = sp90; sp25B = true; goto PostWorldDraw; } PreRender_SetValues(&this->pauseBgPreRender, D_801FBBCC, D_801FBBCE, gfxCtx->curFrameBuffer, gfxCtx->zbuffer); if (R_PAUSE_BG_PRERENDER_STATE == PAUSE_BG_PRERENDER_PROCESS) { MsgEvent_SendNullTask(); if (!gSaveContext.screenScaleFlag) { PreRender_ApplyFiltersSlowlyInit(&this->pauseBgPreRender); } R_PAUSE_BG_PRERENDER_STATE = PAUSE_BG_PRERENDER_READY; SREG(33) |= 1; } else { if (R_PAUSE_BG_PRERENDER_STATE == PAUSE_BG_PRERENDER_READY) { Gfx* sp8C = POLY_OPA_DISP; if (this->pauseBgPreRender.unk_4D == 2) { func_80170B28(&this->pauseBgPreRender, &sp8C); } else { func_80170798(&this->pauseBgPreRender, &sp8C); } gSPDisplayList(sp8C++, D_0E000000.syncSegments); POLY_OPA_DISP = sp8C; sp25B = true; goto PostWorldDraw; } if (this->unk_18844 == 0) { if (1) { if ((this->skyboxId != SKYBOX_NONE) && !this->envCtx.skyboxDisabled) { if ((this->skyboxId == SKYBOX_NORMAL_SKY) || (this->skyboxId == SKYBOX_3)) { Environment_UpdateSkybox(this->skyboxId, &this->envCtx, &this->skyboxCtx); Skybox_Draw(&this->skyboxCtx, gfxCtx, this->skyboxId, this->envCtx.unk_13, this->view.eye.x, this->view.eye.y, this->view.eye.z); } else if (!this->skyboxCtx.skyboxShouldDraw) { Skybox_Draw(&this->skyboxCtx, gfxCtx, this->skyboxId, 0, this->view.eye.x, this->view.eye.y, this->view.eye.z); } } func_800FE390(this); } sp268 = LightContext_NewLights(&this->lightCtx, gfxCtx); if (this->roomCtx.curRoom.enablePosLights || (MREG(93) != 0)) { sp268->enablePosLights = true; } Lights_BindAll(sp268, this->lightCtx.listHead, NULL, this); Lights_Draw(sp268, gfxCtx); if (1) { u32 roomDrawFlags = ((1) ? 1 : 0) | (((void)0, 1) ? 2 : 0); // FAKE: Scene_Draw(this); if (this->roomCtx.unk78) { Room_Draw(this, &this->roomCtx.curRoom, roomDrawFlags & 3); Room_Draw(this, &this->roomCtx.prevRoom, roomDrawFlags & 3); } } if (this->skyboxCtx.skyboxShouldDraw) { Vec3f sp78; if (1) {} Camera_GetQuakeOffset(&sp78, GET_ACTIVE_CAM(this)); Skybox_Draw(&this->skyboxCtx, gfxCtx, this->skyboxId, 0, this->view.eye.x + sp78.x, this->view.eye.y + sp78.y, this->view.eye.z + sp78.z); } // envCtx.precipitation[PRECIP_RAIN_CUR] if (this->envCtx.unk_F2[1] != 0) { Environment_DrawRain(this, &this->view, gfxCtx); } } if (1) { Environment_FillScreen(gfxCtx, 0, 0, 0, this->bgCoverAlpha, 1); } if (1) { Actor_DrawAll(this, &this->actorCtx); } if (1) { if (!this->envCtx.sunMoonDisabled) { sp25C.x = this->view.eye.x + this->envCtx.sunPos.x; sp25C.y = this->view.eye.y + this->envCtx.sunPos.y; sp25C.z = this->view.eye.z + this->envCtx.sunPos.z; Environment_DrawSunLensFlare(this, &this->envCtx, &this->view, gfxCtx, sp25C); } Environment_DrawCustomLensFlare(this); } if (1) { if (R_PLAY_FILL_SCREEN_ON) { Environment_FillScreen(gfxCtx, R_PLAY_FILL_SCREEN_R, R_PLAY_FILL_SCREEN_G, R_PLAY_FILL_SCREEN_B, R_PLAY_FILL_SCREEN_ALPHA, 3); } switch (this->envCtx.fillScreen) { case 1: Environment_FillScreen(gfxCtx, this->envCtx.screenFillColor[0], this->envCtx.screenFillColor[1], this->envCtx.screenFillColor[2], this->envCtx.screenFillColor[3], 3); break; default: break; } } if (1) { if (this->envCtx.sandstormState != 0) { Environment_DrawSandstorm(this, this->envCtx.sandstormState); } } if (this->worldCoverAlpha != 0) { Environment_FillScreen(gfxCtx, 0, 0, 0, this->worldCoverAlpha, 3); } if (1) { DebugDisplay_DrawObjects(this); } Play_DrawMotionBlur(this); if (((R_PAUSE_BG_PRERENDER_STATE == PAUSE_BG_PRERENDER_SETUP) || (gTransitionTileState == TRANS_TILE_SETUP)) || (R_PICTO_PHOTO_STATE == PICTO_PHOTO_STATE_SETUP)) { Gfx* sp74; Gfx* sp70 = POLY_OPA_DISP; sp74 = Graph_GfxPlusOne(sp70); gSPDisplayList(OVERLAY_DISP++, sp74); this->pauseBgPreRender.fbuf = gfxCtx->curFrameBuffer; if (R_PAUSE_BG_PRERENDER_STATE == PAUSE_BG_PRERENDER_SETUP) { R_PAUSE_BG_PRERENDER_STATE = PAUSE_BG_PRERENDER_PROCESS; this->pauseBgPreRender.fbufSave = gfxCtx->zbuffer; this->pauseBgPreRender.cvgSave = this->unk_18E58; } else if (R_PICTO_PHOTO_STATE == PICTO_PHOTO_STATE_SETUP) { R_PICTO_PHOTO_STATE = PICTO_PHOTO_STATE_PROCESS; this->pauseBgPreRender.fbufSave = gfxCtx->zbuffer; this->pauseBgPreRender.cvgSave = this->unk_18E58; } else { gTransitionTileState = TRANS_TILE_PROCESS; this->pauseBgPreRender.fbufSave = gfxCtx->zbuffer; this->pauseBgPreRender.cvgSave = NULL; } func_801705B4(&this->pauseBgPreRender, &sp74); if (this->pauseBgPreRender.cvgSave != NULL) { func_80170730(&this->pauseBgPreRender, &sp74); } gSPEndDisplayList(sp74++); Graph_BranchDlist(sp70, sp74); POLY_OPA_DISP = sp74; this->unk_18B49 = 2; SREG(33) |= 1; } else { PostWorldDraw: if (1) { Play_PostWorldDraw(this); } } } } if ((this->view.unk164 != 0) && !gDbgCamEnabled) { Vec3s sp4C; Camera_Update(&sp4C, GET_ACTIVE_CAM(this)); View_UpdateViewingMatrix(&this->view); this->view.unk164 = 0; if ((this->skyboxId != SKYBOX_NONE) && !this->envCtx.skyboxDisabled) { Skybox_UpdateMatrix(&this->skyboxCtx, this->view.eye.x, this->view.eye.y, this->view.eye.z); } } if (!sp25B) { func_800FE3E0(this); } CLOSE_DISPS(this->state.gfxCtx); } #else void Play_DrawMain(PlayState* this); #pragma GLOBAL_ASM("asm/non_matchings/code/z_play/Play_DrawMain.s") #endif void Play_Draw(PlayState* this) { GraphicsContext* gfxCtx = this->state.gfxCtx; { GraphicsContext* gfxCtx2 = this->state.gfxCtx; if (sBombersNotebookOpen) { if (D_801FBBD4 != 1) { MsgEvent_SendNullTask(); func_80178818(); gfxCtx2->curFrameBuffer = SysCfb_GetFbPtr(gfxCtx2->framebufferIndex % 2); gfxCtx2->zbuffer = SysCfb_GetZBuffer(); gfxCtx2->viMode = D_801FBB88; gfxCtx2->viConfigFeatures = gViConfigFeatures; gfxCtx2->xScale = gViConfigXScale; gfxCtx2->yScale = gViConfigYScale; gfxCtx2->updateViMode = true; } } else { if (D_801FBBD4 != 0) { MsgEvent_SendNullTask(); func_80178750(); gfxCtx2->curFrameBuffer = SysCfb_GetFbPtr(gfxCtx2->framebufferIndex % 2); gfxCtx2->zbuffer = SysCfb_GetZBuffer(); gfxCtx2->viMode = D_801FBB88; gfxCtx2->viConfigFeatures = gViConfigFeatures; gfxCtx2->xScale = gViConfigXScale; gfxCtx2->yScale = gViConfigYScale; gfxCtx2->updateViMode = true; } } } if (sBombersNotebookOpen && ((SREG(2) != 2) || (gZBufferPtr == NULL))) { BombersNotebook_Draw(&sBombersNotebook, gfxCtx); Message_Draw(this); } else { Play_DrawMain(this); } } void Play_Main(GameState* thisx) { static Input* prevInput = NULL; PlayState* this = (PlayState*)thisx; prevInput = CONTROLLER1(&this->state); DebugDisplay_Init(); { GraphicsContext* gfxCtx = this->state.gfxCtx; if (1) { this->state.gfxCtx = NULL; } Play_Update(this); this->state.gfxCtx = gfxCtx; } { Input input = *CONTROLLER1(&this->state); if (1) { *CONTROLLER1(&this->state) = D_801F6C18; } Play_Draw(this); *CONTROLLER1(&this->state) = input; } ActorCutscene_Update(); ActorCutscene_ClearWaiting(); } s32 Play_InCsMode(PlayState* this) { return (this->csCtx.state != 0) || Player_InCsMode(this); } f32 Play_GetFloorSurfaceImpl(PlayState* this, MtxF* mtx, CollisionPoly** poly, s32* bgId, Vec3f* pos) { f32 floorHeight = BgCheck_EntityRaycastFloor3(&this->colCtx, poly, bgId, pos); if (floorHeight > BGCHECK_Y_MIN) { func_800C0094(*poly, pos->x, floorHeight, pos->z, mtx); } else { mtx->xy = 0.0f; mtx->zx = 0.0f; mtx->yx = 0.0f; mtx->xx = 0.0f; mtx->wz = 0.0f; mtx->xz = 0.0f; mtx->wy = 0.0f; mtx->wx = 0.0f; mtx->zz = 0.0f; mtx->yz = 0.0f; mtx->zy = 0.0f; mtx->yy = 1.0f; mtx->xw = pos->x; mtx->yw = pos->y; mtx->zw = pos->z; mtx->ww = 1.0f; } return floorHeight; } void Play_GetFloorSurface(PlayState* this, MtxF* mtx, Vec3f* pos) { CollisionPoly* poly; s32 bgId; Play_GetFloorSurfaceImpl(this, mtx, &poly, &bgId, pos); } void* Play_LoadFile(PlayState* this, RomFile* entry) { size_t size = entry->vromEnd - entry->vromStart; void* allocp = THA_AllocTailAlign16(&this->state.heap, size); DmaMgr_SendRequest0(allocp, entry->vromStart, size); return allocp; } void Play_InitEnvironment(PlayState* this, s16 skyboxId) { Skybox_Init(&this->state, &this->skyboxCtx, skyboxId); Environment_Init(this, &this->envCtx, 0); } void Play_InitScene(PlayState* this, s32 spawn) { this->curSpawn = spawn; this->linkActorEntry = NULL; this->actorCsCamList = NULL; this->setupEntranceList = NULL; this->setupExitList = NULL; this->naviQuestHints = NULL; this->setupPathList = NULL; this->sceneMaterialAnims = NULL; this->roomCtx.unk74 = NULL; this->numSetupActors = 0; Object_InitBank(&this->state, &this->objectCtx); LightContext_Init(this, &this->lightCtx); Door_InitContext(&this->state, &this->doorCtx); Room_Init(this, &this->roomCtx); gSaveContext.worldMapArea = 0; Scene_ExecuteCommands(this, this->sceneSegment); Play_InitEnvironment(this, this->skyboxId); } void Play_SpawnScene(PlayState* this, s32 sceneId, s32 spawn) { s32 pad; SceneTableEntry* scene = &gSceneTable[sceneId]; scene->unk_D = 0; this->loadedScene = scene; this->sceneId = sceneId; this->sceneConfig = scene->drawConfig; this->sceneSegment = Play_LoadFile(this, &scene->segment); scene->unk_D = 0; gSegments[2] = VIRTUAL_TO_PHYSICAL(this->sceneSegment); Play_InitScene(this, spawn); Room_AllocateAndLoad(this, &this->roomCtx); } void Play_GetScreenPos(PlayState* this, Vec3f* worldPos, Vec3f* screenPos) { f32 invW; // screenPos temporarily stores the projectedPos Actor_GetProjectedPos(this, worldPos, screenPos, &invW); screenPos->x = (SCREEN_WIDTH / 2) + (screenPos->x * invW * (SCREEN_WIDTH / 2)); screenPos->y = (SCREEN_HEIGHT / 2) - (screenPos->y * invW * (SCREEN_HEIGHT / 2)); } s16 Play_CreateSubCamera(PlayState* this) { s16 subCamId; for (subCamId = CAM_ID_SUB_FIRST; subCamId < NUM_CAMS; subCamId++) { if (this->cameraPtrs[subCamId] == NULL) { break; } } // if no subCameras available if (subCamId == NUM_CAMS) { return CAM_ID_NONE; } this->cameraPtrs[subCamId] = &this->subCameras[subCamId - CAM_ID_SUB_FIRST]; Camera_Init(this->cameraPtrs[subCamId], &this->view, &this->colCtx, this); this->cameraPtrs[subCamId]->camId = subCamId; return subCamId; } s16 Play_GetActiveCamId(PlayState* this) { return this->activeCamId; } s32 Play_ChangeCameraStatus(PlayState* this, s16 camId, s16 status) { s16 camIdx = (camId == CAM_ID_NONE) ? this->activeCamId : camId; if (status == CAM_STATUS_ACTIVE) { this->activeCamId = camIdx; } return Camera_ChangeStatus(this->cameraPtrs[camIdx], status); } void Play_ClearCamera(PlayState* this, s16 camId) { s16 camIdx = (camId == CAM_ID_NONE) ? this->activeCamId : camId; if (this->cameraPtrs[camIdx] != NULL) { Camera_ChangeStatus(this->cameraPtrs[camIdx], CAM_STATUS_INACTIVE); this->cameraPtrs[camIdx] = NULL; } } void Play_ClearAllSubCameras(PlayState* this) { s16 subCamId; for (subCamId = CAM_ID_SUB_FIRST; subCamId < NUM_CAMS; subCamId++) { if (this->cameraPtrs[subCamId] != NULL) { Play_ClearCamera(this, subCamId); } } this->activeCamId = CAM_ID_MAIN; } Camera* Play_GetCamera(PlayState* this, s16 camId) { s16 camIdx = (camId == CAM_ID_NONE) ? this->activeCamId : camId; return this->cameraPtrs[camIdx]; } /** * @return bit-packed success if each of the params were applied */ s32 Play_SetCameraAtEye(PlayState* this, s16 camId, Vec3f* at, Vec3f* eye) { s32 successfullySet = 0; s16 camIdx = (camId == CAM_ID_NONE) ? this->activeCamId : camId; Camera* camera = this->cameraPtrs[camIdx]; successfullySet |= Camera_SetViewParam(camera, CAM_VIEW_AT, at); successfullySet <<= 1; successfullySet |= Camera_SetViewParam(camera, CAM_VIEW_EYE, eye); camera->dist = Math3D_Distance(at, eye); if (camera->focalActor != NULL) { camera->atActorOffset.x = at->x - camera->focalActor->world.pos.x; camera->atActorOffset.y = at->y - camera->focalActor->world.pos.y; camera->atActorOffset.z = at->z - camera->focalActor->world.pos.z; } else { camera->atActorOffset.x = camera->atActorOffset.y = camera->atActorOffset.z = 0.0f; } camera->atLerpStepScale = 0.01f; return successfullySet; } /** * @return bit-packed success if each of the params were applied */ s32 Play_SetCameraAtEyeUp(PlayState* this, s16 camId, Vec3f* at, Vec3f* eye, Vec3f* up) { s32 successfullySet = 0; s16 camIdx = (camId == CAM_ID_NONE) ? this->activeCamId : camId; Camera* camera = this->cameraPtrs[camIdx]; successfullySet |= Camera_SetViewParam(camera, CAM_VIEW_AT, at); successfullySet <<= 1; successfullySet |= Camera_SetViewParam(camera, CAM_VIEW_EYE, eye); successfullySet <<= 1; successfullySet |= Camera_SetViewParam(camera, CAM_VIEW_UP, up); camera->dist = Math3D_Distance(at, eye); if (camera->focalActor != NULL) { camera->atActorOffset.x = at->x - camera->focalActor->world.pos.x; camera->atActorOffset.y = at->y - camera->focalActor->world.pos.y; camera->atActorOffset.z = at->z - camera->focalActor->world.pos.z; } else { camera->atActorOffset.x = camera->atActorOffset.y = camera->atActorOffset.z = 0.0f; } camera->atLerpStepScale = 0.01f; return successfullySet; } /** * @return true if the fov was successfully set */ s32 Play_SetCameraFov(PlayState* this, s16 camId, f32 fov) { s32 successfullySet = Camera_SetViewParam(this->cameraPtrs[camId], CAM_VIEW_FOV, &fov) & 1; if (1) {} return successfullySet; } s32 Play_SetCameraRoll(PlayState* this, s16 camId, s16 roll) { s16 camIdx = (camId == CAM_ID_NONE) ? this->activeCamId : camId; Camera* camera = this->cameraPtrs[camIdx]; camera->roll = roll; return 1; } void Play_CopyCamera(PlayState* this, s16 destCamId, s16 srcCamId) { s16 srcCamId2 = (srcCamId == CAM_ID_NONE) ? this->activeCamId : srcCamId; s16 destCamId1 = (destCamId == CAM_ID_NONE) ? this->activeCamId : destCamId; Camera_Copy(this->cameraPtrs[destCamId1], this->cameraPtrs[srcCamId2]); } // Same as Play_ChangeCameraSetting but also calls Camera_InitPlayerSettings s32 func_80169A50(PlayState* this, s16 camId, Player* player, s16 setting) { Camera* camera; s16 camIdx = (camId == CAM_ID_NONE) ? this->activeCamId : camId; camera = this->cameraPtrs[camIdx]; Camera_InitPlayerSettings(camera, player); return Camera_ChangeSetting(camera, setting); } s32 Play_ChangeCameraSetting(PlayState* this, s16 camId, s16 setting) { return Camera_ChangeSetting(Play_GetCamera(this, camId), setting); } // Related to bosses and fishing void func_80169AFC(PlayState* this, s16 camId, s16 timer) { s16 camIdx = (camId == CAM_ID_NONE) ? this->activeCamId : camId; s16 i; Play_ClearCamera(this, camIdx); for (i = CAM_ID_SUB_FIRST; i < NUM_CAMS; i++) { if (this->cameraPtrs[i] != NULL) { Play_ClearCamera(this, i); } } if (timer <= 0) { Play_ChangeCameraStatus(this, CAM_ID_MAIN, CAM_STATUS_ACTIVE); this->cameraPtrs[CAM_ID_MAIN]->childCamId = this->cameraPtrs[CAM_ID_MAIN]->doorTimer2 = 0; } } s16 Play_GetCameraUID(PlayState* this, s16 camId) { Camera* camera = this->cameraPtrs[camId]; if (camera != NULL) { return camera->uid; } else { return -1; } } // Unused in both MM and OoT, purpose is very unclear s16 func_80169BF8(PlayState* this, s16 camId, s16 uid) { Camera* camera = this->cameraPtrs[camId]; if (camera != NULL) { return 0; } else if (camera->uid != uid) { return 0; } else if (camera->status != CAM_STATUS_ACTIVE) { return 2; } else { return 1; } } u16 Play_GetActorCsCamSetting(PlayState* this, s32 csCamDataIndex) { ActorCsCamInfo* actorCsCamList = &this->actorCsCamList[csCamDataIndex]; return actorCsCamList->setting; } Vec3s* Play_GetActorCsCamFuncData(PlayState* this, s32 csCamDataIndex) { ActorCsCamInfo* actorCsCamList = &this->actorCsCamList[csCamDataIndex]; return Lib_SegmentedToVirtual(actorCsCamList->actorCsCamFuncData); } /** * Converts the number of a scene to its "original" equivalent, the default version of the area which the player first * enters. */ s16 Play_GetOriginalSceneId(s16 sceneId) { // Inverted Stone Tower Temple -> Stone Tower Temple if (sceneId == SCENE_INISIE_R) { return SCENE_INISIE_N; } // Purified Southern Swamp -> Poisoned Sothern Swamp if (sceneId == SCENE_20SICHITAI2) { return SCENE_20SICHITAI; } // Spring Mountain Village -> Winter Mountain Village if (sceneId == SCENE_10YUKIYAMANOMURA2) { return SCENE_10YUKIYAMANOMURA; } // Spring Goron Village -> Winter Goron Village if (sceneId == SCENE_11GORONNOSATO2) { return SCENE_11GORONNOSATO; } // Spring Path to Goron Village -> Winter Path to Goron Village if (sceneId == SCENE_17SETUGEN2) { return SCENE_17SETUGEN; } // Inverted Stone Tower -> Stone Tower if (sceneId == SCENE_F41) { return SCENE_F40; } return sceneId; } /** * Copies the flags set in ActorContext over to the current scene's CycleSceneFlags, usually using the original scene * number. Exception for Inverted Stone Tower Temple, which uses its own. */ void Play_SaveCycleSceneFlags(GameState* thisx) { PlayState* this = (PlayState*)thisx; CycleSceneFlags* cycleSceneFlags; cycleSceneFlags = &gSaveContext.cycleSceneFlags[Play_GetOriginalSceneId(this->sceneId)]; cycleSceneFlags->chest = this->actorCtx.sceneFlags.chest; cycleSceneFlags->switch0 = this->actorCtx.sceneFlags.switches[0]; cycleSceneFlags->switch1 = this->actorCtx.sceneFlags.switches[1]; if (this->sceneId == SCENE_INISIE_R) { // Inverted Stone Tower Temple cycleSceneFlags = &gSaveContext.cycleSceneFlags[this->sceneId]; } cycleSceneFlags->collectible = this->actorCtx.sceneFlags.collectible[0]; cycleSceneFlags->clearedRoom = this->actorCtx.sceneFlags.clearedRoom; } void Play_SetRespawnData(GameState* thisx, s32 respawnMode, u16 entrance, s32 roomIndex, s32 playerParams, Vec3f* pos, s16 yaw) { PlayState* this = (PlayState*)thisx; gSaveContext.respawn[respawnMode].entrance = Entrance_Create(entrance >> 9, 0, entrance & 0xF); gSaveContext.respawn[respawnMode].roomIndex = roomIndex; gSaveContext.respawn[respawnMode].pos = *pos; gSaveContext.respawn[respawnMode].yaw = yaw; gSaveContext.respawn[respawnMode].playerParams = playerParams; gSaveContext.respawn[respawnMode].tempSwitchFlags = this->actorCtx.sceneFlags.switches[2]; gSaveContext.respawn[respawnMode].unk_18 = this->actorCtx.sceneFlags.collectible[1]; gSaveContext.respawn[respawnMode].tempCollectFlags = this->actorCtx.sceneFlags.collectible[2]; } void Play_SetupRespawnPoint(GameState* thisx, s32 respawnMode, s32 playerParams) { PlayState* this = (PlayState*)thisx; Player* player = GET_PLAYER(this); if (this->sceneId != SCENE_KAKUSIANA) { // Grottos Play_SetRespawnData(&this->state, respawnMode, ((void)0, gSaveContext.save.entrance), this->roomCtx.curRoom.num, playerParams, &player->actor.world.pos, player->actor.shape.rot.y); } } // Override respawn data in Sakon's Hideout void func_80169ECC(PlayState* this) { if (this->sceneId == SCENE_SECOM) { this->nextEntrance = ENTRANCE(IKANA_CANYON, 6); gSaveContext.respawnFlag = -7; } } // Gameplay_TriggerVoidOut ? // Used by Player, Ikana_Rotaryroom, Bji01, Kakasi, LiftNuts, Test4, Warptag, WarpUzu, Roomtimer void func_80169EFC(GameState* thisx) { PlayState* this = (PlayState*)thisx; gSaveContext.respawn[RESPAWN_MODE_DOWN].tempSwitchFlags = this->actorCtx.sceneFlags.switches[2]; gSaveContext.respawn[RESPAWN_MODE_DOWN].unk_18 = this->actorCtx.sceneFlags.collectible[1]; gSaveContext.respawn[RESPAWN_MODE_DOWN].tempCollectFlags = this->actorCtx.sceneFlags.collectible[2]; this->nextEntrance = gSaveContext.respawn[RESPAWN_MODE_DOWN].entrance; gSaveContext.respawnFlag = 1; func_80169ECC(this); this->transitionTrigger = TRANS_TRIGGER_START; this->transitionType = TRANS_TYPE_FADE_BLACK; } // Gameplay_LoadToLastEntrance ? // Used by game_over and Test7 void func_80169F78(GameState* thisx) { PlayState* this = (PlayState*)thisx; this->nextEntrance = gSaveContext.respawn[RESPAWN_MODE_TOP].entrance; gSaveContext.respawnFlag = -1; func_80169ECC(this); this->transitionTrigger = TRANS_TRIGGER_START; this->transitionType = TRANS_TYPE_FADE_BLACK; } // Gameplay_TriggerRespawn ? // Used for void by Wallmaster, Deku Shrine doors. Also used by Player, Kaleido, DoorWarp1 void func_80169FDC(GameState* thisx) { func_80169F78(thisx); } s32 Play_CamIsNotFixed(GameState* thisx) { PlayState* this = (PlayState*)thisx; return this->roomCtx.curRoom.roomShape->base.type != ROOM_SHAPE_TYPE_IMAGE; } s32 FrameAdvance_IsEnabled(GameState* thisx) { PlayState* this = (PlayState*)thisx; return this->frameAdvCtx.enabled != 0; } // Unused, unchanged from OoT, which uses it only in one Camera function. /** * @brief Tests if \p actor is a door and the sides are different rooms. * * @param[in] thisx GameState, promoted to play inside. * @param[in] actor Actor to test. * @param[out] yaw Facing angle of the actor, or reverse if in the back room. * @return true if \p actor is a door and the sides are in different rooms, false otherwise */ s32 func_8016A02C(GameState* thisx, Actor* actor, s16* yaw) { PlayState* this = (PlayState*)thisx; TransitionActorEntry* transitionActor; s8 frontRoom; if (actor->category != ACTORCAT_DOOR) { return false; } transitionActor = &this->doorCtx.transitionActorList[(u16)actor->params >> 10]; frontRoom = transitionActor->sides[0].room; if (frontRoom == transitionActor->sides[1].room) { return false; } if (frontRoom == actor->room) { *yaw = actor->shape.rot.y; } else { *yaw = actor->shape.rot.y + 0x8000; } return true; } // Unused /** * @brief Tests if \p pos is underwater. * * @param[in] this PlayState * @param[in] pos position to test * @return true if inside a waterbox and not above a void. */ s32 Play_IsUnderwater(PlayState* this, Vec3f* pos) { WaterBox* waterBox; CollisionPoly* poly; Vec3f waterSurfacePos; s32 bgId; waterSurfacePos = *pos; if ((WaterBox_GetSurface1(this, &this->colCtx, waterSurfacePos.x, waterSurfacePos.z, &waterSurfacePos.y, &waterBox) == true) && (pos->y < waterSurfacePos.y) && (BgCheck_EntityRaycastFloor3(&this->colCtx, &poly, &bgId, &waterSurfacePos) != BGCHECK_Y_MIN)) { return true; } else { return false; } } s32 Play_IsDebugCamEnabled(void) { return gDbgCamEnabled; } // A mapping from playerActorCsIds to sGlobalCamDataSettings indices. s16 D_801D0D64[] = { -3, -2, -4, -5, -7, -11, -8, -9, -6, -16 }; // Used by Player /** * Extract the common actor cutscene ids used by Player from the scene and set the actor cutscene ids in * this->playerActorCsIds. If a playerActorCsId is not present in the scene, then that particular id is set * to -1. Otherwise, if there is an ActorCutscene where csCamSceneDataId matches the appropriate element of D_801D0D64, * set the corresponding playerActorCsId (and possibly change its priority for the zeroth one) */ void Play_AssignPlayerActorCsIdsFromScene(GameState* thisx, s32 startActorCsId) { PlayState* this = (PlayState*)thisx; s32 i; s16* curPlayerActorCsId = this->playerActorCsIds; s16* phi_s1 = D_801D0D64; for (i = 0; i < ARRAY_COUNT(this->playerActorCsIds); i++, curPlayerActorCsId++, phi_s1++) { ActorCutscene* actorCutscene; s32 curActorCsId; *curPlayerActorCsId = -1; for (curActorCsId = startActorCsId; curActorCsId != -1; curActorCsId = actorCutscene->additionalCutscene) { actorCutscene = ActorCutscene_GetCutscene(curActorCsId); if (actorCutscene->csCamSceneDataId == *phi_s1) { if ((actorCutscene->csCamSceneDataId == -3) && (actorCutscene->priority == 700)) { // override ocarina cs priority actorCutscene->priority = 550; } *curPlayerActorCsId = curActorCsId; break; } } } } // Set values to fill screen void Play_FillScreen(GameState* thisx, s16 fillScreenOn, u8 red, u8 green, u8 blue, u8 alpha) { R_PLAY_FILL_SCREEN_ON = fillScreenOn; R_PLAY_FILL_SCREEN_R = red; R_PLAY_FILL_SCREEN_G = green; R_PLAY_FILL_SCREEN_B = blue; R_PLAY_FILL_SCREEN_ALPHA = alpha; } void Play_Init(GameState* thisx) { PlayState* this = (PlayState*)thisx; GraphicsContext* gfxCtx = this->state.gfxCtx; s32 pad; uintptr_t zAlloc; s32 zAllocSize; Player* player; s32 i; s32 spawn; u8 sceneLayer; s32 scene; if ((gSaveContext.respawnFlag == -4) || (gSaveContext.respawnFlag == -0x63)) { if (CHECK_EVENTINF(EVENTINF_27)) { CLEAR_EVENTINF(EVENTINF_27); STOP_GAMESTATE(&this->state); SET_NEXT_GAMESTATE(&this->state, DayTelop_Init, sizeof(DayTelopState)); return; } gSaveContext.unk_3CA7 = 1; if (gSaveContext.respawnFlag == -0x63) { gSaveContext.respawnFlag = 2; } } else { gSaveContext.unk_3CA7 = 0; } if (gSaveContext.save.entrance == -1) { gSaveContext.save.entrance = 0; STOP_GAMESTATE(&this->state); SET_NEXT_GAMESTATE(&this->state, TitleSetup_Init, sizeof(TitleSetupState)); return; } if ((gSaveContext.nextCutsceneIndex == 0xFFEF) || (gSaveContext.nextCutsceneIndex == 0xFFF0)) { scene = ((void)0, gSaveContext.save.entrance) >> 9; spawn = (((void)0, gSaveContext.save.entrance) >> 4) & 0x1F; if (CHECK_WEEKEVENTREG(WEEKEVENTREG_33_80)) { if (scene == ENTR_SCENE_MOUNTAIN_VILLAGE_WINTER) { scene = ENTR_SCENE_MOUNTAIN_VILLAGE_SPRING; } else if (scene == ENTR_SCENE_GORON_VILLAGE_WINTER) { scene = ENTR_SCENE_GORON_VILLAGE_SPRING; } else if (scene == ENTR_SCENE_PATH_TO_GORON_VILLAGE_WINTER) { scene = ENTR_SCENE_PATH_TO_GORON_VILLAGE_SPRING; } else if ((scene == ENTR_SCENE_SNOWHEAD) || (scene == ENTR_SCENE_PATH_TO_SNOWHEAD) || (scene == ENTR_SCENE_PATH_TO_MOUNTAIN_VILLAGE) || (scene == ENTR_SCENE_GORON_SHRINE) || (scene == ENTR_SCENE_GORON_RACETRACK)) { gSaveContext.nextCutsceneIndex = 0xFFF0; } } if (CHECK_WEEKEVENTREG(WEEKEVENTREG_20_02)) { if (scene == ENTR_SCENE_SOUTHERN_SWAMP_POISONED) { scene = ENTR_SCENE_SOUTHERN_SWAMP_CLEARED; } else if (scene == ENTR_SCENE_WOODFALL) { gSaveContext.nextCutsceneIndex = 0xFFF1; } } if (CHECK_WEEKEVENTREG(WEEKEVENTREG_52_20) && (scene == ENTR_SCENE_IKANA_CANYON)) { gSaveContext.nextCutsceneIndex = 0xFFF2; } if (CHECK_WEEKEVENTREG(WEEKEVENTREG_55_80) && ((scene == ENTR_SCENE_GREAT_BAY_COAST) || (scene == ENTR_SCENE_ZORA_CAPE))) { gSaveContext.nextCutsceneIndex = 0xFFF0; } // "First cycle" Termina Field if (INV_CONTENT(ITEM_OCARINA) != ITEM_OCARINA) { if ((scene == ENTR_SCENE_TERMINA_FIELD) && (((void)0, gSaveContext.save.entrance) != ENTRANCE(TERMINA_FIELD, 10))) { gSaveContext.nextCutsceneIndex = 0xFFF4; } } //! FAKE: gSaveContext.save.entrance = Entrance_Create(((void)0, scene), spawn, ((void)0, gSaveContext.save.entrance) & 0xF); } GameState_Realloc(&this->state, 0); KaleidoManager_Init(this); ShrinkWindow_Init(); View_Init(&this->view, gfxCtx); func_801A3EC0(0); Quake_Init(); Distortion_Init(this); for (i = 0; i < ARRAY_COUNT(this->cameraPtrs); i++) { this->cameraPtrs[i] = NULL; } Camera_Init(&this->mainCamera, &this->view, &this->colCtx, this); Camera_ChangeStatus(&this->mainCamera, CAM_STATUS_ACTIVE); for (i = 0; i < ARRAY_COUNT(this->subCameras); i++) { Camera_Init(&this->subCameras[i], &this->view, &this->colCtx, this); Camera_ChangeStatus(&this->subCameras[i], CAM_STATUS_INACTIVE); } this->cameraPtrs[CAM_ID_MAIN] = &this->mainCamera; this->cameraPtrs[CAM_ID_MAIN]->uid = CAM_ID_MAIN; this->activeCamId = CAM_ID_MAIN; func_800DFF18(&this->mainCamera, 0x7F); Sram_Alloc(&this->state, &this->sramCtx); Regs_InitData(this); Message_Init(this); GameOver_Init(this); SoundSource_InitAll(this); EffFootmark_Init(this); Effect_Init(this); EffectSS_Init(this, 100); CollisionCheck_InitContext(this, &this->colChkCtx); AnimationContext_Reset(&this->animationCtx); Cutscene_Init(this, &this->csCtx); if (gSaveContext.nextCutsceneIndex != 0xFFEF) { gSaveContext.save.cutscene = gSaveContext.nextCutsceneIndex; gSaveContext.nextCutsceneIndex = 0xFFEF; } if (gSaveContext.save.cutscene == 0xFFFD) { gSaveContext.save.cutscene = 0; } if (gSaveContext.nextDayTime != 0xFFFF) { gSaveContext.save.time = gSaveContext.nextDayTime; gSaveContext.skyboxTime = gSaveContext.nextDayTime; } if ((gSaveContext.save.time >= CLOCK_TIME(18, 0)) || (gSaveContext.save.time < CLOCK_TIME(6, 30))) { gSaveContext.save.isNight = true; } else { gSaveContext.save.isNight = false; } func_800EDDB0(this); if (((gSaveContext.gameMode != 0) && (gSaveContext.gameMode != 1)) || (gSaveContext.save.cutscene >= 0xFFF0)) { gSaveContext.unk_3DC0 = 0; Magic_Reset(this); gSaveContext.sceneLayer = (gSaveContext.save.cutscene & 0xF) + 1; // Set saved cutscene to 0 so it doesn't immediately play, but instead let the `CutsceneManager` handle it. gSaveContext.save.cutscene = 0; } else { gSaveContext.sceneLayer = 0; } sceneLayer = gSaveContext.sceneLayer; Play_SpawnScene( this, Entrance_GetSceneIdAbsolute(((void)0, gSaveContext.save.entrance) + ((void)0, gSaveContext.sceneLayer)), Entrance_GetSpawnNum(((void)0, gSaveContext.save.entrance) + ((void)0, gSaveContext.sceneLayer))); KaleidoScopeCall_Init(this); Interface_Init(this); if (gSaveContext.nextDayTime != 0xFFFF) { if (gSaveContext.nextDayTime == 0x8000) { gSaveContext.save.day++; gSaveContext.save.daysElapsed++; gSaveContext.dogIsLost = true; gSaveContext.nextDayTime = -2; } else { gSaveContext.nextDayTime = -3; } } Play_InitMotionBlur(); R_PAUSE_BG_PRERENDER_STATE = PAUSE_BG_PRERENDER_OFF; R_PICTO_PHOTO_STATE = PICTO_PHOTO_STATE_OFF; PreRender_Init(&this->pauseBgPreRender); PreRender_SetValuesSave(&this->pauseBgPreRender, D_801FBBCC, D_801FBBCE, NULL, NULL, NULL); PreRender_SetValues(&this->pauseBgPreRender, D_801FBBCC, D_801FBBCE, NULL, NULL); this->unk_18E64 = D_801FBB90; this->pictoPhotoI8 = gPictoPhotoI8; this->unk_18E68 = D_80784600; this->unk_18E58 = D_80784600; this->unk_18E60 = D_80784600; gTransitionTileState = TRANS_TILE_OFF; this->transitionMode = TRANS_MODE_OFF; D_801D0D54 = false; FrameAdvance_Init(&this->frameAdvCtx); Rand_Seed(osGetTime()); Matrix_Init(&this->state); this->state.main = Play_Main; this->state.destroy = Play_Destroy; this->transitionTrigger = TRANS_TRIGGER_END; this->worldCoverAlpha = 0; this->bgCoverAlpha = 0; this->haltAllActors = false; this->unk_18844 = false; if (gSaveContext.gameMode != 1) { if (gSaveContext.nextTransitionType == TRANS_NEXT_TYPE_DEFAULT) { this->transitionType = (Entrance_GetTransitionFlags(((void)0, gSaveContext.save.entrance) + sceneLayer) >> 7) & 0x7F; } else { this->transitionType = gSaveContext.nextTransitionType; gSaveContext.nextTransitionType = TRANS_NEXT_TYPE_DEFAULT; } } else { this->transitionType = TRANS_TYPE_FADE_BLACK; } TransitionFade_Init(&this->unk_18E48); TransitionFade_SetType(&this->unk_18E48, 3); TransitionFade_SetColor(&this->unk_18E48, RGBA8(160, 160, 160, 255)); TransitionFade_Start(&this->unk_18E48); VisMono_Init(&sVisMono); gVisMonoColor.a = 0; D_801F6D4C = &D_801F6D38; func_80140E80(D_801F6D4C); D_801F6D4C->lodProportion = 0.0f; D_801F6D4C->mode = 1; D_801F6D4C->primColor.r = 0; D_801F6D4C->primColor.g = 0; D_801F6D4C->primColor.b = 0; D_801F6D4C->primColor.a = 0; D_801F6D4C->envColor.r = 0; D_801F6D4C->envColor.g = 0; D_801F6D4C->envColor.b = 0; D_801F6D4C->envColor.a = 0; EnvFlags_UnsetAll(this); THA_GetRemaining(&this->state.heap); zAllocSize = THA_GetRemaining(&this->state.heap); zAlloc = (uintptr_t)THA_AllocTailAlign16(&this->state.heap, zAllocSize); ZeldaArena_Init(((zAlloc + 8) & ~0xF), (zAllocSize - ((zAlloc + 8) & ~0xF)) + zAlloc); //! @bug: Incorrect ALIGN16s Actor_InitContext(this, &this->actorCtx, this->linkActorEntry); while (!Room_HandleLoadCallbacks(this, &this->roomCtx)) {} if ((CURRENT_DAY != 0) && ((this->roomCtx.curRoom.behaviorType1 == ROOM_BEHAVIOR_TYPE1_1) || (this->roomCtx.curRoom.behaviorType1 == ROOM_BEHAVIOR_TYPE1_5))) { Actor_Spawn(&this->actorCtx, this, ACTOR_EN_TEST4, 0.0f, 0.0f, 0.0f, 0, 0, 0, 0); } player = GET_PLAYER(this); Camera_InitPlayerSettings(&this->mainCamera, player); gDbgCamEnabled = false; if ((player->actor.params & 0xFF) != 0xFF) { Camera_ChangeDataIdx(&this->mainCamera, player->actor.params & 0xFF); } func_800F15D8(&this->mainCamera); Interface_SetSceneRestrictions(this); func_800FB758(this); gSaveContext.seqId = this->sequenceCtx.seqId; gSaveContext.ambienceId = this->sequenceCtx.ambienceId; AnimationContext_Update(this, &this->animationCtx); func_800EDBE0(this); gSaveContext.respawnFlag = 0; sBombersNotebookOpen = false; BombersNotebook_Init(&sBombersNotebook); } //! TODO: fake symbol, remove when BombersNotebook_Update is matching u16 D_801D0D78[] = { 0, 0, 0, 0 };