#include #include "2s2h/BenGui/HudEditor.h" #include "2s2h/GameInteractor/GameInteractor.h" #include "2s2h/Enhancements/Enhancements.h" #include "2s2h/Enhancements/FrameInterpolation/FrameInterpolation.h" #include "2s2h_assets.h" extern "C" { #include "variables.h" #include "overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope.h" #include "interface/parameter_static/parameter_static.h" #include "misc/title_static/title_static.h" Gfx* Gfx_DrawTexRectIA8(Gfx* gfx, TexturePtr texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, s16 rectTop, s16 rectWidth, s16 rectHeight, u16 dsdx, u16 dtdy); Gfx* Gfx_DrawTexRectIA8_DropShadow(Gfx* gfx, TexturePtr texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, s16 rectTop, s16 rectWidth, s16 rectHeight, u16 dsdx, u16 dtdy, s16 r, s16 g, s16 b, s16 a); Gfx* Gfx_DrawTexRectIA8_DropShadowOffset(Gfx* gfx, TexturePtr texture, s16 textureWidth, s16 textureHeight, s16 rectLeft, s16 rectTop, s16 rectWidth, s16 rectHeight, u16 dsdx, u16 dtdy, s16 r, s16 g, s16 b, s16 a, s32 masks, s32 rects); } #define CVAR_NAME "gEnhancements.Graphics.ClockType" #define CVAR CVarGetInteger(CVAR_NAME, CLOCK_TYPE_ORIGINAL) #define SECONDS_IN_THREE_DAYS (3 * 24 * 60 * 60) #define SECONDS_IN_SIX_HOURS (6 * 60 * 60) #define CLOCK_SECTION_HALFWIDTH 24 #define CLOCK_SECTION_WIDTH 48 static TexturePtr sDigitTextures[] = { (TexturePtr)gFinalHoursClockDigit0Tex, (TexturePtr)gFinalHoursClockDigit1Tex, (TexturePtr)gFinalHoursClockDigit2Tex, (TexturePtr)gFinalHoursClockDigit3Tex, (TexturePtr)gFinalHoursClockDigit4Tex, (TexturePtr)gFinalHoursClockDigit5Tex, (TexturePtr)gFinalHoursClockDigit6Tex, (TexturePtr)gFinalHoursClockDigit7Tex, (TexturePtr)gFinalHoursClockDigit8Tex, (TexturePtr)gFinalHoursClockDigit9Tex, (TexturePtr)gFinalHoursClockColonTex, }; static TexturePtr sFinalHoursDigitTextures[] = { (TexturePtr)gThreeDayClock3DSFinalHoursDigit0Tex, (TexturePtr)gThreeDayClock3DSFinalHoursDigit1Tex, (TexturePtr)gThreeDayClock3DSFinalHoursDigit2Tex, (TexturePtr)gThreeDayClock3DSFinalHoursDigit3Tex, (TexturePtr)gThreeDayClock3DSFinalHoursDigit4Tex, (TexturePtr)gThreeDayClock3DSFinalHoursDigit5Tex, (TexturePtr)gThreeDayClock3DSFinalHoursDigit6Tex, (TexturePtr)gThreeDayClock3DSFinalHoursDigit7Tex, (TexturePtr)gThreeDayClock3DSFinalHoursDigit8Tex, (TexturePtr)gThreeDayClock3DSFinalHoursDigit9Tex, (TexturePtr)gThreeDayClock3DSFinalHoursColonTex, }; s16 finalHoursClockSlots[8]; void Draw3DSClock() { HudEditor_SetActiveElement(HUD_EDITOR_ELEMENT_CLOCK); if (HudEditor_IsActiveElementHidden()) { return; } static s32 sPreviousTimeCheck = -1; if ((R_TIME_SPEED != 0) && ((gPlayState->msgCtx.msgMode == MSGMODE_NONE) || ((gPlayState->actorCtx.flags & ACTORCTX_FLAG_TELESCOPE_ON) && !Play_InCsMode(gPlayState)) || (gPlayState->msgCtx.msgMode == MSGMODE_NONE) || ((gPlayState->msgCtx.currentTextId >= 0x100) && (gPlayState->msgCtx.currentTextId <= 0x200)) || (gSaveContext.gameMode == GAMEMODE_END_CREDITS)) && !FrameAdvance_IsEnabled(gPlayState) && !Environment_IsTimeStopped() && (gSaveContext.save.day <= 3)) { if ((gPlayState->pauseCtx.state == PAUSE_STATE_OFF) && (gPlayState->pauseCtx.debugEditor == DEBUG_EDITOR_NONE)) { static s16 sThreeDayClockAlpha = 255; static s32 sFinalHoursIntro = 0; sThreeDayClockAlpha = gPlayState->interfaceCtx.bAlpha; s16 posX = 160; s16 posY = 210; OPEN_DISPS(gPlayState->state.gfxCtx); Gfx_SetupDL42_Overlay(gPlayState->state.gfxCtx); gDPSetRenderMode(OVERLAY_DISP++, G_RM_XLU_SURF, G_RM_XLU_SURF2); gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); gDPSetAlphaCompare(OVERLAY_DISP++, G_AC_THRESHOLD); // Draw background of 3DS clock // TODO: Replace this with matrix/vertex handling to avoid gaps when scaled OVERLAY_DISP = Gfx_DrawTexRectIA8_DropShadow( OVERLAY_DISP, (TexturePtr)gThreeDayClock3DSEdgeTex, CLOCK_SECTION_HALFWIDTH * 3, 12, posX - (CLOCK_SECTION_HALFWIDTH * 4), posY, CLOCK_SECTION_HALFWIDTH * 3, 12, 1 << 10, 1 << 10, 255, 255, 255, sThreeDayClockAlpha); HudEditor_SetActiveElement(HUD_EDITOR_ELEMENT_CLOCK); OVERLAY_DISP = Gfx_DrawTexRectIA8_DropShadow(OVERLAY_DISP, (TexturePtr)gThreeDayClock3DSMiddleTex, CLOCK_SECTION_WIDTH, 12, posX - CLOCK_SECTION_HALFWIDTH, posY, CLOCK_SECTION_WIDTH, 12, 1 << 10, 1 << 10, 255, 255, 255, sThreeDayClockAlpha); HudEditor_SetActiveElement(HUD_EDITOR_ELEMENT_CLOCK); OVERLAY_DISP = Gfx_DrawTexRectIA8_DropShadowOffset( OVERLAY_DISP, (TexturePtr)gThreeDayClock3DSEdgeTex, CLOCK_SECTION_HALFWIDTH * 3, 12, posX + CLOCK_SECTION_HALFWIDTH, posY, CLOCK_SECTION_HALFWIDTH * 3, 12, 1 << 10, 1 << 10, 255, 255, 255, sThreeDayClockAlpha, 3, (CLOCK_SECTION_HALFWIDTH * 3) << 5); // Coloured day markers, which highlights the current day with its own colour u16 fillalpha = 64; if (gSaveContext.save.day <= 1) { fillalpha = 255; } gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 0, 128, 255, (fillalpha * sThreeDayClockAlpha) / 255); HudEditor_SetActiveElement(HUD_EDITOR_ELEMENT_CLOCK); OVERLAY_DISP = Gfx_DrawTexRectIA8(OVERLAY_DISP, (TexturePtr)gThreeDayClock3DSFillTex, CLOCK_SECTION_WIDTH, 12, posX - CLOCK_SECTION_HALFWIDTH - CLOCK_SECTION_WIDTH, posY, CLOCK_SECTION_WIDTH, 12, 1 << 10, 1 << 10); fillalpha = 64; if (gSaveContext.save.day == 2) { fillalpha = 255; } gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 192, 0, (fillalpha * sThreeDayClockAlpha) / 255); HudEditor_SetActiveElement(HUD_EDITOR_ELEMENT_CLOCK); OVERLAY_DISP = Gfx_DrawTexRectIA8(OVERLAY_DISP, (TexturePtr)gThreeDayClock3DSFillTex, CLOCK_SECTION_WIDTH, 12, posX - CLOCK_SECTION_HALFWIDTH, posY, CLOCK_SECTION_WIDTH, 12, 1 << 10, 1 << 10); fillalpha = 64; if (gSaveContext.save.day >= 3) { fillalpha = 255; } gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 64, 0, (fillalpha * sThreeDayClockAlpha) / 255); HudEditor_SetActiveElement(HUD_EDITOR_ELEMENT_CLOCK); OVERLAY_DISP = Gfx_DrawTexRectIA8(OVERLAY_DISP, (TexturePtr)gThreeDayClock3DSFillTex, CLOCK_SECTION_WIDTH, 12, posX + CLOCK_SECTION_HALFWIDTH, posY, CLOCK_SECTION_WIDTH, 12, 1 << 10, 1 << 10); // This next part is for the little arrow that shows what the current time is s32 timeUntilCrash = (s32)TIME_TO_SECONDS_F(TIME_UNTIL_MOON_CRASH); // This is a safety measure to delay skipping the arrow marker back a day on the frame before the // day changes by just checking if time went backwards if (sPreviousTimeCheck != -1 && sPreviousTimeCheck > SECONDS_IN_THREE_DAYS - timeUntilCrash) { s32 trueTimeUntilCrash = timeUntilCrash; timeUntilCrash = SECONDS_IN_THREE_DAYS - sPreviousTimeCheck; sPreviousTimeCheck = SECONDS_IN_THREE_DAYS - trueTimeUntilCrash; } else { sPreviousTimeCheck = SECONDS_IN_THREE_DAYS - timeUntilCrash; } // Compute the position of the arrow, and then draw it s32 timeoffset = std::max( std::min(3 * CLOCK_SECTION_WIDTH - (timeUntilCrash * 3 * CLOCK_SECTION_WIDTH) / SECONDS_IN_THREE_DAYS, 3 * CLOCK_SECTION_WIDTH), 0); u16 counterX = posX - (CLOCK_SECTION_HALFWIDTH * 3) + timeoffset; u16 counterY = posY - 4; gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 200, 0, 0, sThreeDayClockAlpha); HudEditor_SetActiveElement(HUD_EDITOR_ELEMENT_CLOCK); OVERLAY_DISP = Gfx_DrawTexRectIA8(OVERLAY_DISP, (TexturePtr)gThreeDayClock3DSArrowTex, 8, 8, counterX - 4, posY + 4, 8, 8, 1 << 10, 1 << 10); u16 curMinutes = (s32)TIME_TO_MINUTES_F(CURRENT_TIME) % 60; u16 curHours = (s32)TIME_TO_MINUTES_F(CURRENT_TIME) / 60; // Final hours timer if ((CURRENT_DAY >= 4) || ((CURRENT_DAY == 3) && (CURRENT_TIME >= (CLOCK_TIME(0, 0) + 5)) && (CURRENT_TIME < CLOCK_TIME(6, 0)))) { // This all just gets the time to display in each number slot s32 timeInSeconds = timeUntilCrash % 60; s32 timeInMinutes = (timeUntilCrash / 60) % 60; s32 timeInHours = (timeUntilCrash / 60) / 60; finalHoursClockSlots[0] = std::min(timeInHours / 10, 9); finalHoursClockSlots[1] = timeInHours % 10; finalHoursClockSlots[2] = 10; finalHoursClockSlots[3] = timeInMinutes / 10; finalHoursClockSlots[4] = timeInMinutes % 10; finalHoursClockSlots[5] = 10; finalHoursClockSlots[6] = timeInSeconds / 10; finalHoursClockSlots[7] = timeInSeconds % 10; u16 finalTimerSpacing = 8; u16 finalTimerPos = posX - finalTimerSpacing * 4 - finalTimerSpacing / 2; s16 i; s32 percentToCrash = std::min(std::max((timeUntilCrash * 255) / SECONDS_IN_SIX_HOURS, 0), 255); // This is a lerp between yellow and red based on the remaining time u16 finalHoursR1 = 255; u16 finalHoursR2 = 255; u16 finalHoursG1 = 0; u16 finalHoursG2 = 255; u16 finalHoursB1 = 0; u16 finalHoursB2 = 0; u16 finalHoursR = (((255 - percentToCrash) * finalHoursR1) + (percentToCrash * finalHoursR2)) / 255; u16 finalHoursG = (((255 - percentToCrash) * finalHoursG1) + (percentToCrash * finalHoursG2)) / 255; u16 finalHoursB = (((255 - percentToCrash) * finalHoursB1) + (percentToCrash * finalHoursB2)) / 255; // Controls the distance and speed modifier of the final hours clock intro animation // finalHoursOffset controls the distance offset for the intro animation // finalHoursModifier controls the speed of the intro animation - larger is slower s32 finalHoursOffset = 10; s32 finalHoursModifier = 2; if (sFinalHoursIntro < finalHoursOffset * finalHoursModifier) { sFinalHoursIntro++; } gDPSetPrimColor(OVERLAY_DISP++, 0, 0, finalHoursR, finalHoursG, finalHoursB, (sThreeDayClockAlpha * sFinalHoursIntro) / (finalHoursOffset * finalHoursModifier)); // This just loops through and draws all the numbers for (i = 0; i < 8; i++) { HudEditor_SetActiveElement(HUD_EDITOR_ELEMENT_CLOCK); OVERLAY_DISP = Gfx_DrawTexRectIA8( OVERLAY_DISP, sFinalHoursDigitTextures[finalHoursClockSlots[i]], 16, 16, finalTimerPos, posY - 14 - finalHoursOffset + (sFinalHoursIntro / finalHoursModifier), 16, 16, 1 << 10, 1 << 10); finalTimerPos += finalTimerSpacing; } HudEditor_SetActiveElement(HUD_EDITOR_ELEMENT_CLOCK); OVERLAY_DISP = Gfx_DrawTexRectIA8( OVERLAY_DISP, (TexturePtr)gThreeDayClock3DSFinalHoursMoonTex, 16, 16, posX - 8, posY - 28 - finalHoursOffset + (sFinalHoursIntro / finalHoursModifier), 16, 16, 1 << 10, 1 << 10); } else { sFinalHoursIntro = 0; // Draw the current time in a small box // TODO: Add scale slider to allow this box to be scaled independently of the main UI element // Draw the background box if (gSaveContext.save.timeSpeedOffset == -2) { u16 pulseTime = ((s32)TIME_TO_SECONDS_F(CURRENT_TIME) % 120); u16 pulse; if (pulseTime < 60) { pulse = (pulseTime * 255) / 60; } else { pulse = 255 - ((pulseTime - 60) * 255) / 60; } gDPSetPrimColor(OVERLAY_DISP++, 0, 0, pulse, pulse, 0, sThreeDayClockAlpha / 2); } else { gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 0, 0, 0, sThreeDayClockAlpha / 2); } HudEditor_SetActiveElement(HUD_EDITOR_ELEMENT_CLOCK); OVERLAY_DISP = Gfx_DrawTexRectIA8(OVERLAY_DISP, (TexturePtr)gThreeDayClock3DSTimeBackdropTex, CLOCK_SECTION_WIDTH, 16, counterX - CLOCK_SECTION_HALFWIDTH, counterY - 4, CLOCK_SECTION_WIDTH, 16, 1 << 10, 1 << 10); counterX -= 8; // Draw the sun/moon next to the current time TexturePtr daynightmarker; if (curHours < 6 || curHours >= 18) { daynightmarker = (TexturePtr)gThreeDayClockMoonHourTex; gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 200, 200, 0, sThreeDayClockAlpha); } else { daynightmarker = (TexturePtr)gThreeDayClockSunHourTex; gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 200, 64, 0, sThreeDayClockAlpha); } HudEditor_SetActiveElement(HUD_EDITOR_ELEMENT_CLOCK); OVERLAY_DISP = Gfx_DrawTexRectI8(OVERLAY_DISP, daynightmarker, 24, 24, counterX - 11, counterY - 2, 12, 12, 1 << 11, 1 << 11); if (!CVarGetInteger("gEnhancements.Graphics.24HoursClock", 0)) { curHours %= 12; if (curHours == 0) { curHours = 12; } } u16 curTensHours = curHours / 10; curHours %= 10; u16 curTensMinutes = curMinutes / 10; curMinutes %= 10; gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, sThreeDayClockAlpha); // Inverted time if (gSaveContext.save.timeSpeedOffset == -2) { HudEditor_SetActiveElement(HUD_EDITOR_ELEMENT_CLOCK); OVERLAY_DISP = Gfx_DrawTexRectIA8(OVERLAY_DISP, (TexturePtr)gThreeDayClock3DSSlowTimeTex, 16, 16, counterX + 8 + 20, counterY - 4, 16, 16, 1 << 10, 1 << 10); gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 64, 192, 255, sThreeDayClockAlpha); } // Digital time u16 timerSpacing = 6; if (curTensHours > 0 || CVarGetInteger("gEnhancements.Graphics.24HoursClock", 0)) { timerSpacing = 4; HudEditor_SetActiveElement(HUD_EDITOR_ELEMENT_CLOCK); OVERLAY_DISP = Gfx_DrawTexRectI8(OVERLAY_DISP, sDigitTextures[curTensHours], 8, 8, counterX, counterY, 8, 8, 1 << 10, 1 << 10); counterX += timerSpacing; } HudEditor_SetActiveElement(HUD_EDITOR_ELEMENT_CLOCK); OVERLAY_DISP = Gfx_DrawTexRectI8(OVERLAY_DISP, sDigitTextures[curHours], 8, 8, counterX, counterY, 8, 8, 1 << 10, 1 << 10); counterX += timerSpacing; HudEditor_SetActiveElement(HUD_EDITOR_ELEMENT_CLOCK); OVERLAY_DISP = Gfx_DrawTexRectI8(OVERLAY_DISP, sDigitTextures[10], 8, 8, counterX, counterY, 8, 8, 1 << 10, 1 << 10); counterX += timerSpacing; HudEditor_SetActiveElement(HUD_EDITOR_ELEMENT_CLOCK); OVERLAY_DISP = Gfx_DrawTexRectI8(OVERLAY_DISP, sDigitTextures[curTensMinutes], 8, 8, counterX, counterY, 8, 8, 1 << 10, 1 << 10); counterX += timerSpacing; HudEditor_SetActiveElement(HUD_EDITOR_ELEMENT_CLOCK); OVERLAY_DISP = Gfx_DrawTexRectI8(OVERLAY_DISP, sDigitTextures[curMinutes], 8, 8, counterX, counterY, 8, 8, 1 << 10, 1 << 10); } gDPPipeSync(OVERLAY_DISP++); CLOSE_DISPS(gPlayState->state.gfxCtx); } else { sPreviousTimeCheck = SECONDS_IN_THREE_DAYS - (s32)TIME_TO_SECONDS_F(TIME_UNTIL_MOON_CRASH); } } else { sPreviousTimeCheck = SECONDS_IN_THREE_DAYS - (s32)TIME_TO_SECONDS_F(TIME_UNTIL_MOON_CRASH); } hudEditorActiveElement = HUD_EDITOR_ELEMENT_NONE; } void Register3DSClock() { COND_VB_SHOULD(VB_PREVENT_CLOCK_DISPLAY, CVAR == CLOCK_TYPE_3DS, { *should = true; Draw3DSClock(); }); } static RegisterShipInitFunc initFunc(Register3DSClock, { CVAR_NAME });