Zelda64Recomp/patches/better_double_sot.c
2024-06-18 19:45:16 +02:00

374 lines
13 KiB
C

#include "patches.h"
void dsot_load_day_number_texture(PlayState* play, s32 day);
void dsot_actor_fixes(PlayState* play);
bool dsot_used = false;
u8 choiceHour;
void dsot_init_hour_selection(PlayState* play) {
choiceHour = (u16)(TIME_TO_HOURS_F(CURRENT_TIME - 1)) + 1;
if (choiceHour >= 24) {
choiceHour = 0;
}
if (choiceHour == 6) {
if (gSaveContext.save.day == 3) {
choiceHour = 5;
} else {
dsot_load_day_number_texture(play, gSaveContext.save.day + 1);
}
}
}
#define TIMER_INIT 10
#define TIMER_STEP_RESET 0
void dsot_handle_hour_selection(PlayState* play) {
static s8 sAnalogStickTimer = TIMER_INIT;
static s8 sPrevStickX = 0;
static bool sAnalogStickHeld = false;
Input* input = CONTROLLER1(&play->state);
if ((input->rel.stick_x >= 30) && !sAnalogStickHeld) {
u8 prevHour = choiceHour;
sAnalogStickHeld = true;
choiceHour++;
if (choiceHour >= 24) {
choiceHour = 0;
}
if ((CURRENT_DAY == 3) && (prevHour <= 5) && (choiceHour > 5)) {
choiceHour = 5;
} else if ((prevHour <= 6) && (choiceHour > 6)) {
choiceHour = 6;
} else {
Audio_PlaySfx(NA_SE_SY_CURSOR);
}
if ((choiceHour == 6) && (choiceHour > prevHour)) {
dsot_load_day_number_texture(play, gSaveContext.save.day + 1);
}
} else if ((input->rel.stick_x <= -30) && !sAnalogStickHeld) {
u8 prevHour = choiceHour;
sAnalogStickHeld = true;
choiceHour--;
if (choiceHour > 24) {
choiceHour = 23;
}
if (((CLOCK_TIME(choiceHour, 0) <= CURRENT_TIME) && (CLOCK_TIME(prevHour, 0) > CURRENT_TIME)
|| ((choiceHour > prevHour) && (CURRENT_TIME >= CLOCK_TIME(23, 0)))
|| ((choiceHour > prevHour) && (CURRENT_TIME < CLOCK_TIME(1, 0))))) {
choiceHour = prevHour;
}
else {
Audio_PlaySfx(NA_SE_SY_CURSOR);
}
if ((prevHour == 6) && (choiceHour < prevHour)) {
dsot_load_day_number_texture(play, gSaveContext.save.day);
}
} else if (ABS_ALT(input->rel.stick_x) < 30) {
sAnalogStickHeld = false;
sAnalogStickTimer = TIMER_INIT;
}
if (ABS_ALT(input->rel.stick_x) >= 30) {
if (!DECR(sAnalogStickTimer)) {
sAnalogStickHeld = false;
sAnalogStickTimer = TIMER_STEP_RESET;
}
}
if (sPrevStickX * input->rel.stick_x < 0) {
sAnalogStickHeld = false;
sAnalogStickTimer = TIMER_INIT;
}
sPrevStickX = input->rel.stick_x;
}
void dsot_cancel_hour_selection(PlayState* play) {
if (choiceHour == 6) {
dsot_load_day_number_texture(play, gSaveContext.save.day);
}
}
void dsot_advance_hour(PlayState* play) {
gSaveContext.save.time = CLOCK_TIME(choiceHour, 0);
gSaveContext.skyboxTime = CURRENT_TIME;
dsot_used = true;
// Instantly enable/disable rain on day 2.
if ((CURRENT_DAY == 2) && (Environment_GetStormState(play) != STORM_STATE_OFF)) {
if ((CURRENT_TIME >= CLOCK_TIME(8, 0)) && (CURRENT_TIME < CLOCK_TIME(18, 00))) {
gWeatherMode = WEATHER_MODE_RAIN;
play->envCtx.lightningState = LIGHTNING_ON;
play->envCtx.precipitation[PRECIP_RAIN_MAX] = 60;
play->envCtx.precipitation[PRECIP_RAIN_CUR] = 60;
Environment_PlayStormNatureAmbience(play);
} else {
gWeatherMode = WEATHER_MODE_CLEAR;
play->envCtx.lightningState = LIGHTNING_OFF;
play->envCtx.precipitation[PRECIP_RAIN_MAX] = 0;
play->envCtx.precipitation[PRECIP_RAIN_CUR] = 0;
Environment_StopStormNatureAmbience(play);
}
}
// Play music/ambience.
if ((CURRENT_TIME >= CLOCK_TIME(18, 0)) || (CURRENT_TIME <= CLOCK_TIME(6,0))) {
SEQCMD_STOP_SEQUENCE(SEQ_PLAYER_BGM_MAIN, 0);
} else if (CURRENT_TIME > CLOCK_TIME(17, 10)) {
SEQCMD_STOP_SEQUENCE(SEQ_PLAYER_BGM_MAIN, 240);
}
play->envCtx.timeSeqState = TIMESEQ_NIGHT_BEGIN_SFX;
if ((CURRENT_TIME >= CLOCK_TIME(18, 0)) || (CURRENT_TIME <= CLOCK_TIME(6, 0))) {
Audio_PlayAmbience(play->sequenceCtx.ambienceId);
Audio_SetAmbienceChannelIO(AMBIENCE_CHANNEL_CRITTER_0, 1, 1);
play->envCtx.timeSeqState = TIMESEQ_NIGHT_DELAY;
}
if ((CURRENT_TIME >= CLOCK_TIME(19, 0)) || (CURRENT_TIME <= CLOCK_TIME(6, 0))) {
Audio_SetAmbienceChannelIO(AMBIENCE_CHANNEL_CRITTER_0, 1, 0);
Audio_SetAmbienceChannelIO(AMBIENCE_CHANNEL_CRITTER_1 << 4 | AMBIENCE_CHANNEL_CRITTER_3, 1, 1);
play->envCtx.timeSeqState = TIMESEQ_DAY_BEGIN_SFX;
}
if ((CURRENT_TIME >= CLOCK_TIME(5, 0)) && (CURRENT_TIME <= CLOCK_TIME(6, 0))) {
Audio_SetAmbienceChannelIO(AMBIENCE_CHANNEL_CRITTER_1 << 4 | AMBIENCE_CHANNEL_CRITTER_3, 1, 0);
Audio_SetAmbienceChannelIO(AMBIENCE_CHANNEL_CRITTER_4 << 4 | AMBIENCE_CHANNEL_CRITTER_5, 1, 1);
play->envCtx.timeSeqState = TIMESEQ_DAY_DELAY;
}
dsot_actor_fixes(play);
}
// Loads day number texture from week_static for the three-day clock
void dsot_load_day_number_texture(PlayState* play, s32 day) {
s16 i = day - 1;
if ((i < 0) || (i >= 3)) {
i = 0;
}
DmaMgr_SendRequest0((void*)(play->interfaceCtx.doActionSegment + 0x780),
SEGMENT_ROM_START_OFFSET(week_static, i * 0x510), 0x510);
}
#include "overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope.h"
void Interface_DrawClock(PlayState* play);
// Wrapper for Interface_DrawClock to display selected hour
void dsot_draw_clock(PlayState* play) {
MessageContext *msgCtx = &play->msgCtx;
u8 prev_msgMode = msgCtx->msgMode;
msgCtx->msgMode = MSGMODE_NONE;
PlayState* state = (PlayState*)&play->state;
s32 prev_frameAdvanceCtx = state->frameAdvCtx.enabled;
state->frameAdvCtx.enabled = 0;
u16 prev_pauseCtx_state = play->pauseCtx.state;
play->pauseCtx.state = PAUSE_STATE_OFF;
u16 prev_pauseCtx_debugEditor = play->pauseCtx.state;
play->pauseCtx.debugEditor = DEBUG_EDITOR_NONE;
bool flag_modified = false;
if (!(play->actorCtx.flags & ACTORCTX_FLAG_TELESCOPE_ON)) {
play->actorCtx.flags ^= ACTORCTX_FLAG_TELESCOPE_ON;
flag_modified = true;
}
u16 prev_time = gSaveContext.save.time;
gSaveContext.save.time = CLOCK_TIME(choiceHour, 0);
Interface_DrawClock(play);
gSaveContext.save.time = prev_time;
if (flag_modified) {
play->actorCtx.flags ^= ACTORCTX_FLAG_TELESCOPE_ON;
}
play->pauseCtx.debugEditor == prev_pauseCtx_debugEditor;
play->pauseCtx.state = prev_pauseCtx_state;
state->frameAdvCtx.enabled = prev_frameAdvanceCtx;
msgCtx->msgMode = prev_msgMode;
}
#include "overlays/actors/ovl_Obj_Tokeidai/z_obj_tokeidai.h"
#include "overlays/actors/ovl_Obj_Tokei_Step/z_obj_tokei_step.h"
void dsot_ObjTokeidai_fix(ObjTokeidai* this, PlayState* play);
void dsot_ObjTokeiStep_fix(ObjTokeiStep* this, PlayState* play);
// Calls actor specific fixes
void dsot_actor_fixes(PlayState* play) {
s32 category;
Actor* actor;
Player* player = GET_PLAYER(play);
u32* categoryFreezeMaskP;
ActorListEntry* entry;
ActorContext* actorCtx = &play->actorCtx;
for (category = 0, entry = actorCtx->actorLists; category < ACTORCAT_MAX;
entry++, categoryFreezeMaskP++, category++) {
actor = entry->first;
for (actor = entry->first; actor != NULL; actor = actor->next) {
switch(actor->id) {
case ACTOR_OBJ_TOKEIDAI:
dsot_ObjTokeidai_fix(actor, play);
break;
case ACTOR_OBJ_TOKEI_STEP:
dsot_ObjTokeiStep_fix(actor, play);
break;
}
}
}
}
// z_obj_tokeidai
#define GET_CURRENT_CLOCK_HOUR(this) ((s32)TIME_TO_HOURS_F((this)->clockTime))
#define GET_CLOCK_FACE_ROTATION(currentClockHour) (TRUNCF_BINANG(currentClockHour * (0x10000 / 24.0f)))
#define GET_MINUTE_RING_OR_EXTERIOR_GEAR_ROTATION(currentClockMinute) \
(TRUNCF_BINANG(currentClockMinute * (0x10000 * 12.0f / 360)))
s32 ObjTokeidai_GetTargetSunMoonPanelRotation(void);
void ObjTokeidai_SetupClockOrExteriorGear(ObjTokeidai* this);
void ObjTokeidai_Init(Actor* thisx, PlayState* play);
void ObjTokeidai_Draw(Actor* thisx, PlayState* play);
void ObjTokeidai_Clock_Init(ObjTokeidai* this);
void ObjTokeidai_SetupTowerOpening(ObjTokeidai* this);
void ObjTokeidai_DoNothing(ObjTokeidai* this, PlayState* play);
void ObjTokeidai_RotateOnMinuteChange(ObjTokeidai* this, s32 playSfx);
void ObjTokeidai_RotateOnHourChange(ObjTokeidai* this, PlayState* play);
void ObjTokeidai_ExteriorGear_Draw(Actor* thisx, PlayState* play);
void ObjTokeidai_Clock_Draw(Actor* thisx, PlayState* play);
void ObjTokeidai_Counterweight_Idle(ObjTokeidai* this, PlayState* play);
void ObjTokeidai_ExteriorGear_Idle(ObjTokeidai* this, PlayState* play);
void ObjTokeidai_TowerClock_Idle(ObjTokeidai* this, PlayState* play);
s32 dsot_ObjTokeidai_get_target_sun_moon_panel_rotation(void) {
if (CURRENT_TIME >= CLOCK_TIME(18, 0) || CURRENT_TIME < CLOCK_TIME(6, 0)) {
return 0x8000;
}
return 0;
}
void dsot_ObjTokeidai_update_clock(ObjTokeidai* this, u16 currentHour, u16 currentMinute) {
// @recomp Manual relocation, TODO remove when the recompiler handles this automatically.
this->clockTime = CURRENT_TIME;
// Instantly switch to desired hour
u16 clockHour = currentHour;
if (currentMinute == 0) {
clockHour--;
if (clockHour > 23) {
clockHour = 23;
}
}
this->clockFaceRotation = GET_CLOCK_FACE_ROTATION(clockHour);
this->clockHour = clockHour;
this->clockFaceAngularVelocity = 0;
this->clockFaceRotationTimer = 0;
// Instantly switch to desired minute
u16 clockMinute = currentMinute - 1;
if (clockMinute > 59) {
clockMinute = 59;
}
this->minuteRingOrExteriorGearRotation = GET_MINUTE_RING_OR_EXTERIOR_GEAR_ROTATION(clockMinute);
this->clockMinute = clockMinute;
this->minuteRingOrExteriorGearAngularVelocity = 0x5A;
this->minuteRingOrExteriorGearRotationTimer = 0;
// Switch the medalion rotation immediately.
if (((currentHour != 6) && (currentHour != 18)) || (currentMinute != 0)) {
this->sunMoonPanelRotation = dsot_ObjTokeidai_get_target_sun_moon_panel_rotation();
this->sunMoonPanelAngularVelocity = 0;
}
}
#define PAST_MIDNIGHT ((CURRENT_DAY == 3) && (CURRENT_TIME < CLOCK_TIME(6, 0)) && (CURRENT_TIME > CLOCK_TIME(0, 0)))
void dsot_ObjTokeidai_fix(ObjTokeidai* this, PlayState* play) {
s32 type = OBJ_TOKEIDAI_TYPE(&this->actor);
u16 currentHour = TIME_TO_HOURS_F(CURRENT_TIME);
u16 currentMinute = TIME_TO_MINUTES_F(CURRENT_TIME);
switch(type) {
case OBJ_TOKEIDAI_TYPE_COUNTERWEIGHT_TERMINA_FIELD:
case OBJ_TOKEIDAI_TYPE_COUNTERWEIGHT_CLOCK_TOWN:
if (PAST_MIDNIGHT && (this->actionFunc == actor_relocate(this, ObjTokeidai_Counterweight_Idle))) {
this->actor.shape.rot.y = 0;
ObjTokeidai_Init(this, play);
}
break;
case OBJ_TOKEIDAI_TYPE_EXTERIOR_GEAR_TERMINA_FIELD:
case OBJ_TOKEIDAI_TYPE_EXTERIOR_GEAR_CLOCK_TOWN:
if (PAST_MIDNIGHT && (this->actionFunc == actor_relocate(this, ObjTokeidai_ExteriorGear_Idle))) {
dsot_ObjTokeidai_update_clock(this, 0, 0);
// @recomp Manual relocation, TODO remove when the recompiler handles this automatically.
this->actor.draw = actor_relocate(this, ObjTokeidai_ExteriorGear_Draw);
ObjTokeidai_Init(this, play);
} else {
dsot_ObjTokeidai_update_clock(this, currentHour, currentMinute);
}
break;
case OBJ_TOKEIDAI_TYPE_TOWER_CLOCK_TERMINA_FIELD:
case OBJ_TOKEIDAI_TYPE_TOWER_CLOCK_CLOCK_TOWN:
if (PAST_MIDNIGHT && (this->actionFunc == actor_relocate(this, ObjTokeidai_TowerClock_Idle))) {
dsot_ObjTokeidai_update_clock(this, 0, 0);
// @recomp Manual relocation, TODO remove when the recompiler handles this automatically.
this->actor.draw = actor_relocate(this, ObjTokeidai_Clock_Draw);
ObjTokeidai_Init(this, play);
} else {
dsot_ObjTokeidai_update_clock(this, currentHour, currentMinute);
}
break;
case OBJ_TOKEIDAI_TYPE_STAIRCASE_TO_ROOFTOP:
if (PAST_MIDNIGHT) {
// @recomp Manual relocation, TODO remove when the recompiler handles this automatically.
this->actor.draw = actor_relocate(this, ObjTokeidai_Draw);
}
break;
case OBJ_TOKEIDAI_TYPE_WALL_CLOCK:
case OBJ_TOKEIDAI_TYPE_SMALL_WALL_CLOCK:
dsot_ObjTokeidai_update_clock(this, currentHour, currentMinute);
break;
}
}
// z_obj_tokei_step
void ObjTokeiStep_InitStepsOpen(ObjTokeiStep* this);
void ObjTokeiStep_SetupDoNothingOpen(ObjTokeiStep* this);
void ObjTokeiStep_DrawOpen(Actor* thisx, PlayState* play);
void dsot_ObjTokeiStep_fix(ObjTokeiStep* this, PlayState* play) {
if (PAST_MIDNIGHT) {
// @recomp Manual relocation, TODO remove when the recompiler handles this automatically.
this->dyna.actor.draw = actor_relocate(this, ObjTokeiStep_DrawOpen);
ObjTokeiStep_InitStepsOpen(this);
ObjTokeiStep_SetupDoNothingOpen(this);
DynaPoly_DisableCollision(play, &play->colCtx.dyna, this->dyna.bgId);
}
}