From 433f058aeae4db50b0d7a915065e44b91e431544 Mon Sep 17 00:00:00 2001 From: Irastris Date: Wed, 15 Apr 2026 20:11:18 -0400 Subject: [PATCH 01/15] Frame interp: Revised camera system FOV is now smooth, and the individual hacks for stars and Epona's reins are removed --- include/dusk/frame_interpolation.h | 15 +- src/d/d_camera.cpp | 1 - src/d/d_kankyo_rain.cpp | 50 ------- src/dusk/frame_interpolation.cpp | 219 ++++++++++++++++++++--------- src/f_ap/f_ap_game.cpp | 1 - src/m_Do/m_Do_graphic.cpp | 4 +- src/m_Do/m_Do_main.cpp | 5 +- 7 files changed, 168 insertions(+), 127 deletions(-) diff --git a/include/dusk/frame_interpolation.h b/include/dusk/frame_interpolation.h index 3aadfe8520..383d16d288 100644 --- a/include/dusk/frame_interpolation.h +++ b/include/dusk/frame_interpolation.h @@ -6,7 +6,6 @@ #include #include -struct cXyz; class camera_process_class; #ifdef __cplusplus @@ -15,7 +14,6 @@ namespace frame_interp { void ensure_initialized(); -void begin_record_camera(); void begin_record(); void end_record(); void interpolate(float step); @@ -39,8 +37,17 @@ void record_final_mtx_raw_tagged(const Mtx* dest, const Mtx src, uint64_t stable bool lookup_replacement(const void* source, Mtx out); bool lookup_concat_replacement(const void* lhs, const void* rhs, Mtx out); -void camera_eye_from_view_mtx(MtxP view_mtx, cXyz* o_eye); -bool build_star_view(Mtx o_view, Mtx o_cam_billboard_base, cXyz* o_anchor_eye, float* o_fovy); +void begin_presentation_camera(); +void end_presentation_camera(); + +struct PresentationCameraScope { + PresentationCameraScope() { begin_presentation_camera(); } + ~PresentationCameraScope() { end_presentation_camera(); } + PresentationCameraScope(const PresentationCameraScope&) = delete; + PresentationCameraScope& operator=(const PresentationCameraScope&) = delete; + PresentationCameraScope(PresentationCameraScope&&) = delete; + PresentationCameraScope& operator=(PresentationCameraScope&&) = delete; +}; uint64_t alloc_simple_shadow_pair_base(); } // namespace frame_interp diff --git a/src/d/d_camera.cpp b/src/d/d_camera.cpp index 7b95a0d8ea..b93d569003 100644 --- a/src/d/d_camera.cpp +++ b/src/d/d_camera.cpp @@ -11079,7 +11079,6 @@ static int camera_draw(camera_process_class* i_this) { &process->view.lookat.up, process->view.bank); #ifdef TARGET_PC dusk::frame_interp::record_camera(process, camera_id); - dusk::frame_interp::record_final_mtx_raw(reinterpret_cast(process->view.viewMtx), process->view.viewMtx); #endif #if WIDESCREEN_SUPPORT diff --git a/src/d/d_kankyo_rain.cpp b/src/d/d_kankyo_rain.cpp index 953768a525..31ba59e33e 100644 --- a/src/d/d_kankyo_rain.cpp +++ b/src/d/d_kankyo_rain.cpp @@ -4110,54 +4110,20 @@ void dKyr_drawStar(Mtx drawMtx, u8** tex) { color_reg0.b = 0xFF; color_reg0.a = 0xFF; -#if TARGET_PC - Mtx star_gx_view; - cXyz anchor_eye; - f32 star_fovy = 45.0f; - MtxP gx_load_view = drawMtx; - bool star_use_present_view = false; - - if (dusk::getSettings().game.enableFrameInterpolation) { - star_use_present_view = dusk::frame_interp::build_star_view(star_gx_view, camMtx, &anchor_eye, &star_fovy); - } - - if (star_use_present_view) { - gx_load_view = star_gx_view; - } else { - if (dComIfGd_getView() != NULL) { - MTXInverse(dComIfGd_getView()->viewMtxNoTrans, camMtx); - anchor_eye = camera->view.lookat.eye; - star_fovy = dComIfGd_getView()->fovy; - } else { - return; - } - } -#else if (dComIfGd_getView() != NULL) { MTXInverse(dComIfGd_getView()->viewMtxNoTrans, camMtx); } else { return; } -#endif if (strcmp(dComIfGp_getStartStageName(), "F_SP200") == 0 && dComIfG_play_c::getLayerNo(0) == 0) { moon_pos = envlight->moon_pos; } else { -#if TARGET_PC - moon_pos = anchor_eye + envlight->moon_pos; -#else moon_pos = camera->view.lookat.eye + envlight->moon_pos; -#endif if (sp38) { -#if TARGET_PC - moon_pos.x = 3900.0f + anchor_eye.x; - moon_pos.y = 8052.0f + anchor_eye.y; - moon_pos.z = -9072.0f + anchor_eye.z; -#else moon_pos.x = 3900.0f + camera->view.lookat.eye.x; moon_pos.y = 8052.0f + camera->view.lookat.eye.y; moon_pos.z = -9072.0f + camera->view.lookat.eye.z; -#endif } } @@ -4193,11 +4159,7 @@ void dKyr_drawStar(Mtx drawMtx, u8** tex) { MTXRotRad(rotMtx, 'Z', DEG_TO_RAD(rot)); MTXConcat(camMtx, rotMtx, camMtx); -#if TARGET_PC - GXLoadPosMtxImm(gx_load_view, GX_PNMTX0); -#else GXLoadPosMtxImm(drawMtx, GX_PNMTX0); -#endif GXSetCurrentMtx(GX_PNMTX0); rot += 0.65f; @@ -4205,23 +4167,12 @@ void dKyr_drawStar(Mtx drawMtx, u8** tex) { rot = 0.0f; } -#if TARGET_PC - spBC = anchor_eye; -#else spBC.x = camera->view.lookat.eye.x; spBC.y = camera->view.lookat.eye.y; spBC.z = camera->view.lookat.eye.z; -#endif f32 sp34 = -1.0f; int sp30 = 0; -#if TARGET_PC - f32 var_f30 = star_fovy / 45.0f; - if (var_f30 >= 1.0f) { - var_f30 = 1.0f; - } - var_f30 = 1.0f - var_f30; -#else f32 var_f30 = 0.0f; if (dComIfGd_getView() != NULL) { @@ -4231,7 +4182,6 @@ void dKyr_drawStar(Mtx drawMtx, u8** tex) { } var_f30 = 1.0f - var_f30; } -#endif f32 temp_f27 = 0.28f * (1.0f - var_f30); sp98.x = 0.0f; diff --git a/src/dusk/frame_interpolation.cpp b/src/dusk/frame_interpolation.cpp index 0fa7d036e0..e3de48bcfc 100644 --- a/src/dusk/frame_interpolation.cpp +++ b/src/dusk/frame_interpolation.cpp @@ -1,12 +1,7 @@ -#include "f_op/f_op_camera_mng.h" #include "dusk/frame_interpolation.h" -#include -#include -#include #include -#include -#include +#include "f_op/f_op_camera_mng.h" namespace { enum class Op : uint8_t { @@ -80,6 +75,36 @@ std::vector g_current_path; std::unordered_map g_replacements; +struct CameraSnapshot { + cXyz eye{}; + cXyz center{}; + cXyz up{}; + s16 bank{}; + f32 fovy{}; + f32 aspect{}; + f32 near_{}; + f32 far_{}; + bool valid{}; +}; + +CameraSnapshot s_cam_prev{}; +CameraSnapshot s_cam_curr{}; + +view_class s_presentation_view_backup{}; +int s_presentation_depth = 0; + +void copy_view_to_snap(CameraSnapshot* dst, const view_class& v) { + dst->eye = v.lookat.eye; + dst->center = v.lookat.center; + dst->up = v.lookat.up; + dst->bank = v.bank; + dst->fovy = v.fovy; + dst->aspect = v.aspect; + dst->near_ = v.near_; + dst->far_ = v.far_; + dst->valid = true; +} + inline void copy_matrix(const Mtx src, Mtx dst) { MTXCopy(src, dst); } @@ -104,6 +129,12 @@ inline void lerp_xyz(cXyz* out, const cXyz& lhs, const cXyz& rhs, float step) { out->z = lhs.z * old_weight + rhs.z * step; } +static s16 lerp_bank(s16 a, s16 b, f32 t) { + const f32 ra = S2RAD(a); + const f32 d = remainderf(S2RAD(b) - ra, 2.0f * static_cast(M_PI)); + return cAngle::Radian_to_SAngle(ra + d * t); +} + inline bool matrix_differs(const Mtx lhs, const Mtx rhs, float epsilon = 0.0001f) { for (size_t row = 0; row < 3; ++row) { for (size_t col = 0; col < 4; ++col) { @@ -287,6 +318,7 @@ void ensure_initialized() { void begin_record() { ensure_initialized(); + if (!g_enabled) { g_interpolating = false; g_sync_presentation = false; @@ -294,6 +326,8 @@ void begin_record() { g_current_recording = {}; g_current_path.clear(); clear_replacements(); + s_cam_prev.valid = false; + s_cam_curr.valid = false; return; } @@ -305,6 +339,15 @@ void begin_record() { g_recording = true; g_interpolating = false; clear_replacements(); + + ::camera_process_class* cam = dComIfGp_getCamera(0); + if (cam == nullptr) { + s_cam_prev.valid = false; + s_cam_curr.valid = false; + return; + } else { + copy_view_to_snap(&s_cam_prev, cam->view); + } } void end_record() { @@ -454,84 +497,126 @@ bool lookup_concat_replacement(const void* lhs, const void* rhs, Mtx out) { return true; } -// TODO: Is there already a built-in function for this? -void camera_eye_from_view_mtx(MtxP view_mtx, cXyz* o_eye) { - o_eye->x = -(view_mtx[0][0] * view_mtx[0][3] + view_mtx[1][0] * view_mtx[1][3] + view_mtx[2][0] * view_mtx[2][3]); - o_eye->y = -(view_mtx[0][1] * view_mtx[0][3] + view_mtx[1][1] * view_mtx[1][3] + view_mtx[2][1] * view_mtx[2][3]); - o_eye->z = -(view_mtx[0][2] * view_mtx[0][3] + view_mtx[1][2] * view_mtx[1][3] + view_mtx[2][2] * view_mtx[2][3]); -} - -namespace { -struct CamSnap { - cXyz eye{}; - cXyz center{}; - cXyz up{}; - s16 bank{}; - f32 fovy{}; - bool valid{}; -}; - -CamSnap s_star_prev{}; -CamSnap s_star_curr{}; - -static void copy_view_to_snap(CamSnap* dst, const view_class& v) { - dst->eye = v.lookat.eye; - dst->center = v.lookat.center; - dst->up = v.lookat.up; - dst->bank = v.bank; - dst->fovy = v.fovy; - dst->valid = true; -} - -static void billboard_base_from_view(MtxP view_mtx, MtxP o_cam_billboard_base) { - Mtx rot; - MTXCopy(view_mtx, rot); - rot[0][3] = rot[1][3] = rot[2][3] = 0.0f; - MTXInverse(rot, o_cam_billboard_base); -} -} // namespace - -void begin_record_camera() { - ::camera_process_class* cam = dComIfGp_getCamera(0); - if (cam == nullptr) { - return; - } - copy_view_to_snap(&s_star_prev, cam->view); -} - void record_camera(::camera_process_class* cam, int camera_id) { - if (!getSettings().game.enableFrameInterpolation || camera_id != 0 || cam == nullptr) { + if (!g_enabled || camera_id != 0 || cam == nullptr) { return; } - copy_view_to_snap(&s_star_curr, cam->view); + copy_view_to_snap(&s_cam_curr, cam->view); } -bool build_star_view(Mtx o_view, Mtx o_cam_billboard_base, cXyz* o_anchor_eye, float* o_fovy) { - if (!getSettings().game.enableFrameInterpolation || !s_star_prev.valid || !s_star_curr.valid) { - return false; +void begin_presentation_camera() { + ensure_initialized(); + if (!g_enabled) { + return; } + if (s_presentation_depth > 0) { + s_presentation_depth++; + return; + } + if (!s_cam_prev.valid || !s_cam_curr.valid) { + return; + } + + view_class* const view = dComIfGd_getView(); + if (view == nullptr) { + return; + } + + std::memcpy(&s_presentation_view_backup, view, sizeof(view_class)); const f32 step = get_interpolation_step(); cXyz eye; cXyz center; cXyz up; - lerp_xyz(&eye, s_star_prev.eye, s_star_curr.eye, step); - lerp_xyz(¢er, s_star_prev.center, s_star_curr.center, step); - lerp_xyz(&up, s_star_prev.up, s_star_curr.up, step); + lerp_xyz(&eye, s_cam_prev.eye, s_cam_curr.eye, step); + lerp_xyz(¢er, s_cam_prev.center, s_cam_curr.center, step); + lerp_xyz(&up, s_cam_prev.up, s_cam_curr.up, step); if (!up.normalizeRS()) { - up = s_star_curr.up; + up = s_cam_curr.up; up.normalizeRS(); } - const f32 bank_rad = S2RAD(s_star_prev.bank) * (1.0f - step) + S2RAD(s_star_curr.bank) * step; - const s16 bank = cAngle::Radian_to_SAngle(bank_rad); + view->lookat.eye = eye; + view->lookat.center = center; + view->lookat.up = up; + view->bank = lerp_bank(s_cam_prev.bank, s_cam_curr.bank, step); + view->fovy = s_cam_prev.fovy + (s_cam_curr.fovy - s_cam_prev.fovy) * step; + view->aspect = s_cam_prev.aspect + (s_cam_curr.aspect - s_cam_prev.aspect) * step; + view->near_ = s_cam_prev.near_ + (s_cam_curr.near_ - s_cam_prev.near_) * step; + view->far_ = s_cam_prev.far_ + (s_cam_curr.far_ - s_cam_prev.far_) * step; - mDoMtx_lookAt(o_view, &eye, ¢er, &up, bank); - billboard_base_from_view(o_view, o_cam_billboard_base); + // FRAME INTERP TODO: Largely copied from d_camera's camera_draw function from this point, got any better ideas? + C_MTXPerspective(view->projMtx, view->fovy, view->aspect, view->near_, view->far_); + mDoMtx_lookAt(view->viewMtx, &view->lookat.eye, &view->lookat.center, &view->lookat.up, view->bank); +#if WIDESCREEN_SUPPORT + mDoGph_gInf_c::setWideZoomProjection(view->projMtx); +#endif + j3dSys.setViewMtx(view->viewMtx); + cMtx_inverse(view->viewMtx, view->invViewMtx); - *o_anchor_eye = eye; - *o_fovy = s_star_prev.fovy + (s_star_curr.fovy - s_star_prev.fovy) * step; - return true; + bool camera_attention_status = dComIfGp_getCameraAttentionStatus(0) & 0x80; + Z2GetAudience()->setAudioCamera(view->viewMtx, view->lookat.eye, view->lookat.center, view->fovy, view->aspect, camera_attention_status, 0, false); + + dBgS_GndChk gndchk; + gndchk.OnWaterGrp(); + gndchk.SetPos(&view->lookat.eye); + f32 cross = dComIfG_Bgsp().GroundCross(&gndchk); + if (cross != -G_CM3D_F_INF) { + if (dComIfG_Bgsp().ChkGrpInf(gndchk, 0x100)) { + mDoAud_getCameraMapInfo(6); + } else { + mDoAud_getCameraMapInfo(dComIfG_Bgsp().GetMtrlSndId(gndchk)); + } + mDoAud_setCameraGroupInfo(dComIfG_Bgsp().GetGrpSoundId(gndchk)); + Vec spDC; + spDC.x = view->lookat.eye.x; + spDC.y = cross; + spDC.z = view->lookat.eye.z; + Z2AudioMgr::getInterface()->setCameraPolygonPos(&spDC); + } else { + Z2AudioMgr::getInterface()->setCameraPolygonPos(nullptr); + } + + MTXCopy(view->viewMtx, view->viewMtxNoTrans); + view->viewMtxNoTrans[0][3] = 0.0f; + view->viewMtxNoTrans[1][3] = 0.0f; + view->viewMtxNoTrans[2][3] = 0.0f; + cMtx_concatProjView(view->projMtx, view->viewMtx, view->projViewMtx); + + f32 far_; + f32 var_f30; + if (dComIfGp_getCameraAttentionStatus(0) & 8) { + far_ = view->far_; + } else { +#if DEBUG + if (g_envHIO.mOther.mAdjustCullFar != 0) { + var_f30 = g_envHIO.mOther.mCullFarValue; + } else +#endif + { + var_f30 = dStage_stagInfo_GetCullPoint(dComIfGp_getStageStagInfo()); + } + far_ = var_f30; + } + + mDoLib_clipper::setup(view->fovy, view->aspect, view->near_, far_); + + s_presentation_depth = 1; +} + +void end_presentation_camera() { + if (s_presentation_depth == 0) { + return; + } + s_presentation_depth--; + if (s_presentation_depth > 0) { + return; + } + + view_class* const view = dComIfGd_getView(); + if (view != nullptr) { + std::memcpy(view, &s_presentation_view_backup, sizeof(view_class)); + } } uint64_t alloc_simple_shadow_pair_base() { diff --git a/src/f_ap/f_ap_game.cpp b/src/f_ap/f_ap_game.cpp index 84d2ab8e97..93a9148957 100644 --- a/src/f_ap/f_ap_game.cpp +++ b/src/f_ap/f_ap_game.cpp @@ -725,7 +725,6 @@ void fapGm_After() { #ifdef TARGET_PC static void fapGm_Before() { - dusk::frame_interp::begin_record_camera(); dusk::frame_interp::begin_record(); } diff --git a/src/m_Do/m_Do_graphic.cpp b/src/m_Do/m_Do_graphic.cpp index 92c8707782..5440d63da8 100644 --- a/src/m_Do/m_Do_graphic.cpp +++ b/src/m_Do/m_Do_graphic.cpp @@ -2229,13 +2229,11 @@ int mDoGph_Painter() { #if TARGET_PC if (dusk::getSettings().game.enableFrameInterpolation) { - cXyz pres_eye; - dusk::frame_interp::camera_eye_from_view_mtx(j3dSys.getViewMtx(), &pres_eye); // FRAME INTERP NOTE: Currently only recalculating points for Epona's reins. Need a more global solution. if (daHorse_c* horse = dComIfGp_getHorseActor()) { horse->lerpControlPoints(dusk::frame_interp::get_interpolation_step()); } - g_dComIfG_gameInfo.drawlist.refresh3DlineMats(pres_eye); + g_dComIfG_gameInfo.drawlist.refresh3DlineMats(camera_p->view.lookat.eye); } #endif diff --git a/src/m_Do/m_Do_main.cpp b/src/m_Do/m_Do_main.cpp index 3568e044e1..7e34770f11 100644 --- a/src/m_Do/m_Do_main.cpp +++ b/src/m_Do/m_Do_main.cpp @@ -253,7 +253,10 @@ void main01(void) { accumulator -= kSimStepSeconds; } dusk::frame_interp::interpolate(static_cast(accumulator / kSimStepSeconds)); - cAPIGph_Painter(); + { + dusk::frame_interp::PresentationCameraScope presentation_camera; + cAPIGph_Painter(); + } } else { accumulator = 0.0; From 269505a69fe972e489ac9c582911dce1b626f9ca Mon Sep 17 00:00:00 2001 From: Luke Street Date: Wed, 15 Apr 2026 22:36:28 -0600 Subject: [PATCH 02/15] Fix bloom viewport hack --- src/m_Do/m_Do_graphic.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/m_Do/m_Do_graphic.cpp b/src/m_Do/m_Do_graphic.cpp index 5440d63da8..fe3556c4d1 100644 --- a/src/m_Do/m_Do_graphic.cpp +++ b/src/m_Do/m_Do_graphic.cpp @@ -1390,7 +1390,7 @@ void mDoGph_gInf_c::bloom_c::draw2() { if (enabled) { GXCreateFrameBuffer(width * 0.75f, height * 0.5f); - GXSetViewport(0.0f, 0.0f, width, height, 0.1f, 1.0f); // use oversized viewport to make the math easier + GXSetViewport(0.0f, 0.0f, width, height, 0.0f, 1.0f); // use oversized viewport to make the math easier GXSetNumTevStages(3); GXSetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR_NULL); @@ -1432,16 +1432,16 @@ void mDoGph_gInf_c::bloom_c::draw2() { // Setup blur filter TEV. GXSetNumTexGens(8); - u32 texMtxID = GX_TEXMTX0; - int angle = 0; - for (int texCoord = (int)GX_TEXCOORD0; texCoord < (int)GX_MAX_TEXCOORD; texCoord++) { - GXSetTexCoordGen((GXTexCoordID)texCoord, GX_TG_MTX2x4, GX_TG_TEX0, texMtxID); - mDoMtx_stack_c::transS((blurScale * cM_scos(angle)) * getInvScale(), - blurScale * cM_ssin(angle), 0.0f); - GXLoadTexMtxImm(mDoMtx_stack_c::get(), texMtxID, GX_MTX2x4); - texMtxID += 3; - angle += 0x2000; - } + u32 texMtxID = GX_TEXMTX0; + int angle = 0; + for (int texCoord = (int)GX_TEXCOORD0; texCoord < (int)GX_MAX_TEXCOORD; texCoord++) { + GXSetTexCoordGen((GXTexCoordID)texCoord, GX_TG_MTX2x4, GX_TG_TEX0, texMtxID); + mDoMtx_stack_c::transS((blurScale * cM_scos(angle)) * getInvScale(), + blurScale * cM_ssin(angle), 0.0f); + GXLoadTexMtxImm(mDoMtx_stack_c::get(), texMtxID, GX_MTX2x4); + texMtxID += 3; + angle += 0x2000; + } GXSetNumTevStages(8); for (int stage = 0; stage < 8; stage++) { From 98b0c8296e8bf72937c07c20fbd211bb45b64636 Mon Sep 17 00:00:00 2001 From: Luke Street Date: Wed, 15 Apr 2026 23:01:37 -0600 Subject: [PATCH 03/15] And this too --- extern/aurora | 2 +- src/m_Do/m_Do_graphic.cpp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/extern/aurora b/extern/aurora index 4d7ff2ac11..aa83f6d915 160000 --- a/extern/aurora +++ b/extern/aurora @@ -1 +1 @@ -Subproject commit 4d7ff2ac115409192ab17e45771c739ea004cef1 +Subproject commit aa83f6d91545c304b8f62a2965c7dcd1ec8b511b diff --git a/src/m_Do/m_Do_graphic.cpp b/src/m_Do/m_Do_graphic.cpp index fe3556c4d1..d087ff61e9 100644 --- a/src/m_Do/m_Do_graphic.cpp +++ b/src/m_Do/m_Do_graphic.cpp @@ -1503,7 +1503,6 @@ void mDoGph_gInf_c::bloom_c::draw2() { GXLoadTexObj(texFinal, GX_TEXMAP0); GXRestoreFrameBuffer(); - GXSetViewport(0.0f, 0.0f, width, height, 0.0f, 1.0f); // Now blend our bloom into the real FB. GXSetTevColor(GX_TEVREG0, mBlendColor); From 5c61c5ede8664fa067e99460f55275ebb104860a Mon Sep 17 00:00:00 2001 From: Luke Street Date: Thu, 16 Apr 2026 00:34:17 -0600 Subject: [PATCH 04/15] Add Bloom debug window --- files.cmake | 2 + src/d/d_kankyo.cpp | 5 ++ src/dusk/imgui/ImGuiBloomWindow.cpp | 126 ++++++++++++++++++++++++++++ src/dusk/imgui/ImGuiBloomWindow.hpp | 6 ++ src/dusk/imgui/ImGuiConsole.cpp | 1 + src/dusk/imgui/ImGuiMenuTools.cpp | 2 +- src/dusk/imgui/ImGuiMenuTools.hpp | 3 + 7 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 src/dusk/imgui/ImGuiBloomWindow.cpp create mode 100644 src/dusk/imgui/ImGuiBloomWindow.hpp diff --git a/files.cmake b/files.cmake index d1e0436625..8f62b9346c 100644 --- a/files.cmake +++ b/files.cmake @@ -1358,6 +1358,8 @@ set(DUSK_FILES src/dusk/imgui/ImGuiEngine.hpp src/dusk/imgui/ImGuiMenuGame.cpp src/dusk/imgui/ImGuiMenuGame.hpp + src/dusk/imgui/ImGuiBloomWindow.cpp + src/dusk/imgui/ImGuiBloomWindow.hpp src/dusk/imgui/ImGuiMenuTools.cpp src/dusk/imgui/ImGuiMenuTools.hpp src/dusk/imgui/ImGuiMenuEnhancements.cpp diff --git a/src/d/d_kankyo.cpp b/src/d/d_kankyo.cpp index 60e4d619a4..9bb96efc82 100644 --- a/src/d/d_kankyo.cpp +++ b/src/d/d_kankyo.cpp @@ -33,6 +33,7 @@ #include #include #if TARGET_PC +#include "dusk/imgui/ImGuiBloomWindow.hpp" #include "dusk/settings.h" #endif @@ -2578,6 +2579,10 @@ void dScnKy_env_light_c::setLight() { mDoGph_gInf_c::getBloom()->setMode(mode); } +#if TARGET_PC + dusk::ApplyBloomOverride(); +#endif + f32 var_f30; if (dKy_Outdoor_check() == true) { static f32 now_shadow_alpha[] = {0.25f, 0.35f, 0.6f, 0.6f, 0.25f, 0.35f}; diff --git a/src/dusk/imgui/ImGuiBloomWindow.cpp b/src/dusk/imgui/ImGuiBloomWindow.cpp new file mode 100644 index 0000000000..1741f82cd8 --- /dev/null +++ b/src/dusk/imgui/ImGuiBloomWindow.cpp @@ -0,0 +1,126 @@ +#include + +#include "imgui.h" + +#include "ImGuiBloomWindow.hpp" +#include "ImGuiMenuTools.hpp" +#include "m_Do/m_Do_graphic.h" + +namespace dusk { +namespace { +struct BloomOverride { + bool enabled = false; + bool bloomEnabled = true; + int mode = 0; + int point = 128; + int blureSize = 64; + int blureRatio = 128; + GXColor blendColor = {255, 255, 255, 255}; + GXColor monoColor = {0, 0, 0, 0}; +}; + +BloomOverride s_bloomOverride; + +void SyncFromCurrentBloom() { + mDoGph_gInf_c::bloom_c* bloom = mDoGph_gInf_c::getBloom(); + s_bloomOverride.bloomEnabled = bloom->getEnable() != 0; + s_bloomOverride.mode = bloom->mMode; + s_bloomOverride.point = bloom->getPoint(); + s_bloomOverride.blureSize = bloom->getBlureSize(); + s_bloomOverride.blureRatio = bloom->getBlureRatio(); + s_bloomOverride.blendColor = *bloom->getBlendColor(); + s_bloomOverride.monoColor = *bloom->getMonoColor(); +} + +u8 ClampToByte(int value) { + return static_cast(std::clamp(value, 0, 255)); +} + +void DrawColorEdit(const char* label, GXColor& color) { + float colorValue[4] = { + color.r / 255.0f, + color.g / 255.0f, + color.b / 255.0f, + color.a / 255.0f, + }; + if (ImGui::ColorEdit4(label, colorValue, ImGuiColorEditFlags_Uint8)) { + color.r = ClampToByte(static_cast(colorValue[0] * 255.0f + 0.5f)); + color.g = ClampToByte(static_cast(colorValue[1] * 255.0f + 0.5f)); + color.b = ClampToByte(static_cast(colorValue[2] * 255.0f + 0.5f)); + color.a = ClampToByte(static_cast(colorValue[3] * 255.0f + 0.5f)); + } +} +} // namespace + +void ApplyBloomOverride() { + if (!s_bloomOverride.enabled) { + return; + } + + mDoGph_gInf_c::bloom_c* bloom = mDoGph_gInf_c::getBloom(); + bloom->setEnable(s_bloomOverride.bloomEnabled ? 1 : 0); + bloom->setMode(static_cast(std::clamp(s_bloomOverride.mode, 0, 1))); + bloom->setPoint(ClampToByte(s_bloomOverride.point)); + bloom->setBlureSize(ClampToByte(s_bloomOverride.blureSize)); + bloom->setBlureRatio(ClampToByte(s_bloomOverride.blureRatio)); + bloom->setBlendColor(s_bloomOverride.blendColor); + bloom->setMonoColor(s_bloomOverride.monoColor); +} + +void DrawBloomWindow(bool& open) { + if (!open) { + return; + } + + if (!ImGui::Begin("Bloom", &open)) { + ImGui::End(); + return; + } + + bool copyCurrent = false; + if (ImGui::Checkbox("Override", &s_bloomOverride.enabled) && s_bloomOverride.enabled) { + copyCurrent = true; + } + + ImGui::SameLine(); + if (ImGui::Button("Sync with current")) { + copyCurrent = true; + } + + if (copyCurrent) { + SyncFromCurrentBloom(); + } + + ImGui::SeparatorText("Values"); + ImGui::Checkbox("Enabled", &s_bloomOverride.bloomEnabled); + ImGui::SliderInt("Mode", &s_bloomOverride.mode, 0, 1); + ImGui::SliderInt("Threshold", &s_bloomOverride.point, 0, 255); + ImGui::SliderInt("Blur Size", &s_bloomOverride.blureSize, 0, 255); + ImGui::SliderInt("Blur Ratio", &s_bloomOverride.blureRatio, 0, 255); + DrawColorEdit("Blend Color", s_bloomOverride.blendColor); + DrawColorEdit("Mono Color", s_bloomOverride.monoColor); + + ApplyBloomOverride(); + + ImGui::SeparatorText("Current"); + mDoGph_gInf_c::bloom_c* bloom = mDoGph_gInf_c::getBloom(); + GXColor blendColor = *bloom->getBlendColor(); + GXColor monoColor = *bloom->getMonoColor(); + ImGui::Text("Enabled: %d", bloom->getEnable()); + ImGui::Text("Mode: %d", bloom->mMode); + ImGui::Text("Threshold: %u", static_cast(bloom->getPoint())); + ImGui::Text("Blur Size: %u", static_cast(bloom->getBlureSize())); + ImGui::Text("Blur Ratio: %u", static_cast(bloom->getBlureRatio())); + ImGui::Text("Blend RGBA: %u, %u, %u, %u", static_cast(blendColor.r), + static_cast(blendColor.g), static_cast(blendColor.b), + static_cast(blendColor.a)); + ImGui::Text("Mono RGBA: %u, %u, %u, %u", static_cast(monoColor.r), + static_cast(monoColor.g), static_cast(monoColor.b), + static_cast(monoColor.a)); + ImGui::End(); +} + +void ImGuiMenuTools::ShowBloomWindow() { + DrawBloomWindow(m_showBloomWindow); +} +} // namespace dusk diff --git a/src/dusk/imgui/ImGuiBloomWindow.hpp b/src/dusk/imgui/ImGuiBloomWindow.hpp new file mode 100644 index 0000000000..58e644e96d --- /dev/null +++ b/src/dusk/imgui/ImGuiBloomWindow.hpp @@ -0,0 +1,6 @@ +#pragma once + +namespace dusk { +void DrawBloomWindow(bool& open); +void ApplyBloomOverride(); +} // namespace dusk diff --git a/src/dusk/imgui/ImGuiConsole.cpp b/src/dusk/imgui/ImGuiConsole.cpp index 0cf3906d75..f71dc8c278 100644 --- a/src/dusk/imgui/ImGuiConsole.cpp +++ b/src/dusk/imgui/ImGuiConsole.cpp @@ -271,6 +271,7 @@ namespace dusk { m_menuTools.ShowHeapOverlay(); m_menuTools.ShowStubLog(); m_menuTools.ShowMapLoader(); + m_menuTools.ShowBloomWindow(); m_menuTools.ShowPlayerInfo(); m_menuTools.ShowAudioDebug(); m_menuTools.ShowSaveEditor(); diff --git a/src/dusk/imgui/ImGuiMenuTools.cpp b/src/dusk/imgui/ImGuiMenuTools.cpp index 7b317c978b..9bb7670e88 100644 --- a/src/dusk/imgui/ImGuiMenuTools.cpp +++ b/src/dusk/imgui/ImGuiMenuTools.cpp @@ -54,7 +54,7 @@ namespace dusk { ImGui::MenuItem("State Share", hotkeys::SHOW_STATE_SHARE, &m_showStateShare); ImGui::MenuItem("Debug Camera", hotkeys::SHOW_DEBUG_CAMERA, &m_showCameraOverlay); ImGui::MenuItem("Audio Debug", hotkeys::SHOW_AUDIO_DEBUG, &m_showAudioDebug); - + ImGui::MenuItem("Bloom", nullptr, &m_showBloomWindow); ImGui::MenuItem("Stub Log", nullptr, &m_showStubLog); if (!dusk::IsGameLaunched) { diff --git a/src/dusk/imgui/ImGuiMenuTools.hpp b/src/dusk/imgui/ImGuiMenuTools.hpp index 05bdd9364b..e48aab2742 100644 --- a/src/dusk/imgui/ImGuiMenuTools.hpp +++ b/src/dusk/imgui/ImGuiMenuTools.hpp @@ -21,6 +21,7 @@ namespace dusk { void ShowHeapOverlay(); void ShowStubLog(); void ShowMapLoader(); + void ShowBloomWindow(); void ShowPlayerInfo(); void ShowAudioDebug(); void ShowSaveEditor(); @@ -41,6 +42,8 @@ namespace dusk { bool m_showMapLoader = false; + bool m_showBloomWindow = false; + bool m_showAudioDebug = false; struct { int mapIdx = -1; From ff92ba3abce0a68ac540e3f4b3125b806fa06836 Mon Sep 17 00:00:00 2001 From: "Jasper St. Pierre" Date: Thu, 16 Apr 2026 00:22:40 -0700 Subject: [PATCH 05/15] bloom viewport fix --- src/m_Do/m_Do_graphic.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/m_Do/m_Do_graphic.cpp b/src/m_Do/m_Do_graphic.cpp index d087ff61e9..edab073c3b 100644 --- a/src/m_Do/m_Do_graphic.cpp +++ b/src/m_Do/m_Do_graphic.cpp @@ -1344,6 +1344,11 @@ void mDoGph_gInf_c::bloom_c::draw2() { static_cast(prev.h / 2), }; } + for (int i = 0; i < ARRAY_SIZE(divRects); i++) { + auto & rect = divRects[i]; + if (rect.w == 0) rect.w = 1; + if (rect.h == 0) rect.h = 1; + } auto divCopySrc = [&](int divNo) { auto const& rect = divRects[divNo]; From 06e6a97ee704cecdeb7d907f3c1939eff0c370fd Mon Sep 17 00:00:00 2001 From: "Jasper St. Pierre" Date: Thu, 16 Apr 2026 00:43:13 -0700 Subject: [PATCH 06/15] e&c --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 25d5d386f8..1dbaefd7d3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -297,7 +297,7 @@ endif () # Edit & Continue if (MSVC) - if (CMAKE_MSVC_DEBUG_INFORMATION_FORMAT STREQUAL "" AND CMAKE_BUILD_TYPE STREQUAL Debug) + if ("${CMAKE_MSVC_DEBUG_INFORMATION_FORMAT}" STREQUAL "" AND CMAKE_BUILD_TYPE STREQUAL "Debug") set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "EditAndContinue") endif () if (CMAKE_MSVC_DEBUG_INFORMATION_FORMAT STREQUAL "EditAndContinue") From 6bc1d3d84741a4aeda36c59d05493aca06c18011 Mon Sep 17 00:00:00 2001 From: "Jasper St. Pierre" Date: Thu, 16 Apr 2026 01:05:03 -0700 Subject: [PATCH 07/15] bloom blend tweakk --- src/m_Do/m_Do_graphic.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/m_Do/m_Do_graphic.cpp b/src/m_Do/m_Do_graphic.cpp index edab073c3b..d774a7de7d 100644 --- a/src/m_Do/m_Do_graphic.cpp +++ b/src/m_Do/m_Do_graphic.cpp @@ -1493,7 +1493,7 @@ void mDoGph_gInf_c::bloom_c::draw2() { GXSetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY); GXSetBlendMode(GX_BM_BLEND, GX_BL_SRCALPHA, GX_BL_ONE, GX_LO_OR); for (int i = divNum; i > divStart; i--) { - float alpha = 255.0f * powf(0.25f * dusk::getSettings().game.bloomMultiplier.getValue(), 1.0f / (divNum - i + 1)); + float alpha = 255.0f * powf(0.25f * dusk::getSettings().game.bloomMultiplier.getValue(), 1.0f / (i - divStart + 1)); GXSetTevColorS10(GX_TEVREG0, {0, 0, 0, s16(alpha)}); divCopySrc(i); From a2882711ee7e0c5df0b64c920067e6e694ed8985 Mon Sep 17 00:00:00 2001 From: TakaRikka Date: Thu, 16 Apr 2026 01:44:44 -0700 Subject: [PATCH 08/15] add dungeon item flags to save editor --- src/dusk/imgui/ImGuiSaveEditor.cpp | 60 +++++++++++++++++++++++------- 1 file changed, 47 insertions(+), 13 deletions(-) diff --git a/src/dusk/imgui/ImGuiSaveEditor.cpp b/src/dusk/imgui/ImGuiSaveEditor.cpp index 1618998249..03d948be36 100644 --- a/src/dusk/imgui/ImGuiSaveEditor.cpp +++ b/src/dusk/imgui/ImGuiSaveEditor.cpp @@ -1263,6 +1263,16 @@ namespace dusk { } } + static inline void genDungeonItemCheckbox(dSv_memBit_c& membit, const char* label, int flag) { + bool tempFlag = membit.isDungeonItem(flag); + if (ImGui::Checkbox(label, &tempFlag)) { + if (tempFlag) + membit.onDungeonItem(flag); + else + membit.offDungeonItem(flag); + } + } + void genMembitFlags(const char* id, dSv_memBit_c& membit) { ImGuiBeginGroupPanel("Chest", { 100, 100 }); for (int j = 0; j < 2; j++) { @@ -1270,7 +1280,7 @@ namespace dusk { } ImGuiEndGroupPanel(); - ImVec2 start_cursor = ImGui::GetCursorPos(); + ImVec2 post_tbox_cursor = ImGui::GetCursorPos(); ImGui::SameLine(); @@ -1280,9 +1290,9 @@ namespace dusk { } ImGuiEndGroupPanel(); - ImVec2 cursor = ImGui::GetCursorPos(); + ImVec2 post_switch_cursor = ImGui::GetCursorPos(); - ImGui::SetCursorPos(start_cursor); + ImGui::SetCursorPos(post_tbox_cursor); ImGuiBeginGroupPanel("Item", { 100, 100 }); for (int j = 0; j < 1; j++) { @@ -1290,7 +1300,28 @@ namespace dusk { } ImGuiEndGroupPanel(); - ImGui::SetCursorPos({ start_cursor.x, cursor.y }); + ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 10.0f); + + genDungeonItemCheckbox(membit, "Got Map", dSv_memBit_c::MAP); + ImGui::SameLine(230.0f); + genDungeonItemCheckbox(membit, "Got Compass", dSv_memBit_c::COMPASS); + + genDungeonItemCheckbox(membit, "Got Boss Key", dSv_memBit_c::BOSS_KEY); + ImGui::SameLine(230.0f); + genDungeonItemCheckbox(membit, "Saw Boss Demo", dSv_memBit_c::STAGE_BOSS_DEMO); + + genDungeonItemCheckbox(membit, "Got Heart Container", dSv_memBit_c::STAGE_LIFE); + ImGui::SameLine(230.0f); + genDungeonItemCheckbox(membit, "Defeated Boss", dSv_memBit_c::STAGE_BOSS_ENEMY); + + genDungeonItemCheckbox(membit, "Defeated Miniboss", dSv_memBit_c::STAGE_BOSS_ENEMY_2); + ImGui::SameLine(230.0f); + genDungeonItemCheckbox(membit, "Got Ooccoo", dSv_memBit_c::OOCCOO_NOTE); + + int keyTemp = membit.getKeyNum(); + if (ImGui::SliderInt("Keys", &keyTemp, 0, 5)) { + membit.setKeyNum(keyTemp); + } } void ImGuiSaveEditor::drawFlagsTab() { @@ -1298,15 +1329,18 @@ namespace dusk { dSv_memBit_c& membit = g_dComIfG_gameInfo.info.mMemory.mBit; genMembitFlags("##TempSceneFlags", membit); - int stageNo = dStage_stagInfo_GetSaveTbl(dComIfGp_getStageStagInfo()); - if (ImGui::Button("Save##SaveTempFlags")) { - dComIfGs_putSave(stageNo); - } + stage_stag_info_class* pstag = dComIfGp_getStageStagInfo(); + if (pstag != nullptr) { + int stageNo = dStage_stagInfo_GetSaveTbl(pstag); + if (ImGui::Button("Save##SaveTempFlags")) { + dComIfGs_putSave(stageNo); + } - ImGui::SameLine(); + ImGui::SameLine(); - if (ImGui::Button("Load##LoadSaveFlags")) { - dComIfGs_getSave(stageNo); + if (ImGui::Button("Load##LoadSaveFlags")) { + dComIfGs_getSave(stageNo); + } } ImGui::TreePop(); @@ -1319,7 +1353,7 @@ namespace dusk { "Faron", "Eldin", "Lanayru", - "Unknown", + "Reserved", "Hyrule Field", "Sacred Grove", "Snowpeak", @@ -1420,4 +1454,4 @@ namespace dusk { ImGui::EndCombo(); } } -} \ No newline at end of file +} From 3db0281088d7e54f42e3e6cc0649768bf3cd878d Mon Sep 17 00:00:00 2001 From: Irastris Date: Thu, 16 Apr 2026 00:28:13 -0400 Subject: [PATCH 09/15] Gyro: ROLLGOAL! --- files.cmake | 2 +- include/dusk/gyro.h | 17 +++ include/dusk/gyro_aim.h | 10 -- include/dusk/settings.h | 2 + src/d/actor/d_a_alink_link.inc | 4 +- src/d/actor/d_a_mg_fshop.cpp | 40 +++++++ src/dusk/gyro.cpp | 123 ++++++++++++++++++++++ src/dusk/gyro_aim.cpp | 87 --------------- src/dusk/imgui/ImGuiControllerOverlay.cpp | 46 ++++---- src/dusk/imgui/ImGuiMenuEnhancements.cpp | 27 ++++- src/dusk/imgui/ImGuiMenuGame.hpp | 1 + src/dusk/settings.cpp | 4 + src/m_Do/m_Do_main.cpp | 10 +- 13 files changed, 240 insertions(+), 133 deletions(-) create mode 100644 include/dusk/gyro.h delete mode 100644 include/dusk/gyro_aim.h create mode 100644 src/dusk/gyro.cpp delete mode 100644 src/dusk/gyro_aim.cpp diff --git a/files.cmake b/files.cmake index 8f62b9346c..e91415b3be 100644 --- a/files.cmake +++ b/files.cmake @@ -1344,7 +1344,7 @@ set(DUSK_FILES src/dusk/extras.cpp src/dusk/frame_interpolation.cpp src/dusk/globals.cpp - src/dusk/gyro_aim.cpp + src/dusk/gyro.cpp src/dusk/io.cpp src/dusk/layout.cpp src/dusk/logging.cpp diff --git a/include/dusk/gyro.h b/include/dusk/gyro.h new file mode 100644 index 0000000000..5636d34b05 --- /dev/null +++ b/include/dusk/gyro.h @@ -0,0 +1,17 @@ +#ifndef DUSK_GYRO_H +#define DUSK_GYRO_H + +namespace dusk::gyro { +void read(float dt); +void consumeAimDeltas(float& out_yaw_rad, float& out_pitch_rad); +bool queryGyroAimItemContext(); + +void rollgoalTick(bool play_active, s16 camera_yaw); +void rollgoalTableOffset(s16& out_add_x, s16& out_add_z); + +extern bool s_sensor_keep_alive; +bool get_sensor_keep_alive(); +void set_sensor_keep_alive(bool value); +} // namespace dusk::gyro + +#endif diff --git a/include/dusk/gyro_aim.h b/include/dusk/gyro_aim.h deleted file mode 100644 index aafc926811..0000000000 --- a/include/dusk/gyro_aim.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef DUSK_GYRO_AIM_H -#define DUSK_GYRO_AIM_H - -namespace dusk::gyro_aim { -void read(float dt, bool context_active); -void consumeAimDeltas(float& out_yaw_rad, float& out_pitch_rad); -bool queryGyroAimItemContext(); -} // namespace dusk::gyro_aim - -#endif diff --git a/include/dusk/settings.h b/include/dusk/settings.h index 67dce3c067..7bfc65a312 100644 --- a/include/dusk/settings.h +++ b/include/dusk/settings.h @@ -81,8 +81,10 @@ struct UserSettings { // Input ConfigVar enableGyroAim; + ConfigVar enableGyroRollgoal; ConfigVar gyroAimSensitivityX; ConfigVar gyroAimSensitivityY; + ConfigVar gyroRollgoalSensitivity; ConfigVar gyroAimInvertPitch; ConfigVar gyroAimInvertYaw; diff --git a/src/d/actor/d_a_alink_link.inc b/src/d/actor/d_a_alink_link.inc index 4f8f5fa7b6..6bf9ae23dc 100644 --- a/src/d/actor/d_a_alink_link.inc +++ b/src/d/actor/d_a_alink_link.inc @@ -11,7 +11,7 @@ #include "d/actor/d_a_tag_mhint.h" #if TARGET_PC -#include "dusk/gyro_aim.h" +#include "dusk/gyro.h" #endif bool daAlink_c::checkNoSubjectModeCamera() { @@ -142,7 +142,7 @@ BOOL daAlink_c::setBodyAngleToCamera() { f32 gy_yaw = 0.f; f32 gy_pitch = 0.f; - dusk::gyro_aim::consumeAimDeltas(gy_yaw, gy_pitch); + dusk::gyro::consumeAimDeltas(gy_yaw, gy_pitch); if (dusk::getSettings().game.gyroAimInvertPitch) { gy_pitch = -gy_pitch; diff --git a/src/d/actor/d_a_mg_fshop.cpp b/src/d/actor/d_a_mg_fshop.cpp index cb9cc12dea..1c93648eac 100644 --- a/src/d/actor/d_a_mg_fshop.cpp +++ b/src/d/actor/d_a_mg_fshop.cpp @@ -14,6 +14,10 @@ #include "d/d_s_play.h" #include "Z2AudioLib/Z2Instances.h" +#if TARGET_PC +#include "dusk/gyro.h" +#endif + enum koro2_parts { KORO2_PART_BOX = 1, KORO2_PART_CURVE_A_U_L, @@ -723,6 +727,14 @@ static void koro2_game(fshop_class* i_this) { cLib_addCalcAngleS2(&i_this->field_0x4020.x, 0, 2, 0x200); cLib_addCalcAngleS2(&i_this->field_0x4020.z, 0, 2, 0x200); case 2: +#if TARGET_PC + if (dusk::getSettings().game.enableGyroRollgoal) { + if (!dusk::gyro::get_sensor_keep_alive()) { + dusk::gyro::set_sensor_keep_alive(true); + } + } +#endif + actor->scale.x = 10.0f; if (i_this->field_0x4010 == 2) { static f32 old_stick_x = 0.0f; @@ -739,6 +751,11 @@ static void koro2_game(fshop_class* i_this) { old_stick_x = mDoCPd_c::getSubStickX(PAD_1); cLib_addCalcAngleS2(&i_this->field_0x4060, i_this->field_0x4062, 4, 0x1000); +#if TARGET_PC + if (dusk::getSettings().game.enableGyroRollgoal) { + dusk::gyro::rollgoalTick(true, i_this->field_0x4060); + } +#endif cMtx_YrotS(*calc_mtx, -i_this->field_0x4060); sp5C.x = mDoCPd_c::getStickX3D(PAD_1); @@ -765,9 +782,26 @@ static void koro2_game(fshop_class* i_this) { reg_f30 = 0.0f; } +#if TARGET_PC + if (dusk::getSettings().game.enableGyroRollgoal) { + s16 rg_add_x; + s16 rg_add_z; + dusk::gyro::rollgoalTableOffset(rg_add_x, rg_add_z); + s16 tgt_x = static_cast(reg_f30 * (-6000.0f + JREG_F(7))) + rg_add_x; + s16 tgt_z = static_cast(reg_f31 * (-6000.0f + JREG_F(8))) + rg_add_z; + cLib_addCalcAngleS2(&i_this->field_0x4020.x, tgt_x, 4, 0x200); + cLib_addCalcAngleS2(&i_this->field_0x4020.z, tgt_z, 4, 0x200); + } +#else cLib_addCalcAngleS2(&i_this->field_0x4020.x, reg_f30 * (-6000.0f + JREG_F(7)), 4, 0x200); cLib_addCalcAngleS2(&i_this->field_0x4020.z, reg_f31 * (-6000.0f + JREG_F(8)), 4, 0x200); +#endif } +#if TARGET_PC + if (i_this->field_0x4010 != 2) { + dusk::gyro::rollgoalTick(false, i_this->field_0x4060); + } +#endif break; } @@ -1145,6 +1179,12 @@ static int daFshop_Delete(fshop_class* i_this) { } } +#if TARGET_PC + if (dusk::getSettings().game.enableGyroRollgoal) { + dusk::gyro::set_sensor_keep_alive(false); + } +#endif + return 1; } diff --git a/src/dusk/gyro.cpp b/src/dusk/gyro.cpp new file mode 100644 index 0000000000..a2b026713a --- /dev/null +++ b/src/dusk/gyro.cpp @@ -0,0 +1,123 @@ +#include "dusk/gyro.h" +#include "d/actor/d_a_alink.h" + +namespace dusk::gyro { +namespace { +// TODO: Make deadband and smoothing configurable +constexpr float kDeadbandRadS = 0.04f; +constexpr float kSmoothAlpha = 0.35f; +constexpr s32 kRollgoalTableMaxOffset = 12000; + +bool s_sensor_enabled = false; +float s_smooth_gx = 0.0f; +float s_smooth_gy = 0.0f; +float s_smooth_gz = 0.0f; +float s_yaw_rad = 0.0f; +float s_yaw_rad_pending = 0.0f; +float s_pitch_rad = 0.0f; +float s_pitch_rad_pending = 0.0f; +float s_roll_rad = 0.0f; +s32 s_rollgoal_ax = 0; +s32 s_rollgoal_az = 0; + +void reset_filter_state() { + s_smooth_gx = s_smooth_gy = s_smooth_gz = 0.0f; + s_yaw_rad_pending = s_pitch_rad_pending = s_roll_rad = 0.0f; + s_rollgoal_ax = s_rollgoal_az = 0; +} + +float apply_deadband(float v) { + if (v > -kDeadbandRadS && v < kDeadbandRadS) { + return 0.0f; + } + return v; +} +} // namespace + +bool s_sensor_keep_alive = false; + +bool get_sensor_keep_alive() { return s_sensor_keep_alive; } +void set_sensor_keep_alive(bool value) { s_sensor_keep_alive = value; } + +bool queryGyroAimItemContext() { + if (!static_cast(dusk::getSettings().game.enableGyroAim)) { + return false; + } + + daAlink_c* link = daAlink_getAlinkActorClass(); + if (link == nullptr) { + return false; + } + + return link->checkGyroAimItemContext() && dComIfGp_checkCameraAttentionStatus(link->field_0x317c, 0x10); +} + +void read(float dt) { + if (!s_sensor_keep_alive && !(dusk::getSettings().game.enableGyroAim && queryGyroAimItemContext())) { + if (s_sensor_enabled) { + PADSetSensorEnabled(PAD_CHAN0, PAD_SENSOR_GYRO, FALSE); + s_sensor_enabled = false; + } + reset_filter_state(); + return; + } + + if (!s_sensor_enabled) { + if (!PADHasSensor(PAD_CHAN0, PAD_SENSOR_GYRO)) { + return; + } + if (!PADSetSensorEnabled(PAD_CHAN0, PAD_SENSOR_GYRO, TRUE)) { + return; + } + s_sensor_enabled = true; + } + + f32 gyro[3]; + if (!PADGetSensorData(PAD_CHAN0, PAD_SENSOR_GYRO, gyro, 3)) { + return; + } + + s_smooth_gx += kSmoothAlpha * (gyro[0] - s_smooth_gx); + s_smooth_gy += kSmoothAlpha * (gyro[1] - s_smooth_gy); + s_smooth_gz += kSmoothAlpha * (gyro[2] - s_smooth_gz); + + s_pitch_rad = apply_deadband(s_smooth_gx) * dt * dusk::getSettings().game.gyroAimSensitivityX; + s_yaw_rad = apply_deadband(s_smooth_gy) * dt * dusk::getSettings().game.gyroAimSensitivityY; + s_roll_rad = apply_deadband(s_smooth_gz) * dt * dusk::getSettings().game.gyroAimSensitivityX; // GYRO NOTE: Exposing Z sensitivity seems unusual, so I'm just using X + + s_pitch_rad_pending += s_pitch_rad; + s_yaw_rad_pending += s_yaw_rad; +} + +void consumeAimDeltas(float& out_yaw_rad, float& out_pitch_rad) { + out_yaw_rad = s_yaw_rad_pending; + out_pitch_rad = s_pitch_rad_pending; + s_yaw_rad_pending = s_pitch_rad_pending = 0.0f; +} + +void rollgoalTick(bool play_active, s16 camera_yaw) { + if (!play_active) { + reset_filter_state(); + return; + } + + const float pitch_rad = s_pitch_rad * dusk::getSettings().game.gyroRollgoalSensitivity; + const float roll_rad = s_roll_rad * dusk::getSettings().game.gyroRollgoalSensitivity; + + s_rollgoal_az += cM_rad2s(roll_rad); + cXyz in(roll_rad, 0.0f, pitch_rad); + cXyz out; + cMtx_YrotS(*calc_mtx, static_cast(-camera_yaw)); + MtxPosition(&in, &out); + + s_rollgoal_ax += cM_rad2s(out.z); + s_rollgoal_az += cM_rad2s(out.x); + s_rollgoal_ax = std::clamp(s_rollgoal_ax, -kRollgoalTableMaxOffset, kRollgoalTableMaxOffset); + s_rollgoal_az = std::clamp(s_rollgoal_az, -kRollgoalTableMaxOffset, kRollgoalTableMaxOffset); +} + +void rollgoalTableOffset(s16& out_add_x, s16& out_add_z) { + out_add_x = static_cast(s_rollgoal_ax); + out_add_z = static_cast(s_rollgoal_az); +} +} // namespace dusk::gyro diff --git a/src/dusk/gyro_aim.cpp b/src/dusk/gyro_aim.cpp deleted file mode 100644 index 48edd640b3..0000000000 --- a/src/dusk/gyro_aim.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include "dusk/gyro_aim.h" - -#include -#include "d/actor/d_a_alink.h" - -namespace dusk::gyro_aim { -namespace { -// TODO: Make deadband and smoothing configurable -constexpr float kDeadbandRadS = 0.04f; -constexpr float kSmoothAlpha = 0.35f; -bool s_sensor_enabled = false; -float s_smooth_gx = 0.0f; -float s_smooth_gy = 0.0f; -float s_pending_yaw_rad = 0.0f; -float s_pending_pitch_rad = 0.0f; - -void reset_filter_state() { - s_smooth_gx = s_smooth_gy = 0.0f; - s_pending_yaw_rad = s_pending_pitch_rad = 0.0f; -} - -float apply_deadband(float v) { - if (v > -kDeadbandRadS && v < kDeadbandRadS) { - return 0.0f; - } - return v; -} -} // namespace - -void read(float dt, bool context_active) { - if (!context_active || !static_cast(dusk::getSettings().game.enableGyroAim)) { - SDL_Gamepad* pad = SDL_GetGamepadFromPlayerIndex(0); - if (pad != nullptr && s_sensor_enabled) { - SDL_SetGamepadSensorEnabled(pad, SDL_SENSOR_GYRO, false); - s_sensor_enabled = false; - } - reset_filter_state(); - return; - } - - SDL_Gamepad* pad = SDL_GetGamepadFromPlayerIndex(0); - if (pad == nullptr || !SDL_GamepadHasSensor(pad, SDL_SENSOR_GYRO)) { - return; - } - - if (!s_sensor_enabled) { - if (!SDL_SetGamepadSensorEnabled(pad, SDL_SENSOR_GYRO, true)) { - return; - } - s_sensor_enabled = true; - reset_filter_state(); - } - - float gyro[3]; - if (!SDL_GetGamepadSensorData(pad, SDL_SENSOR_GYRO, gyro, 3)) { - return; - } - - s_smooth_gx += kSmoothAlpha * (gyro[0] - s_smooth_gx); - s_smooth_gy += kSmoothAlpha * (gyro[1] - s_smooth_gy); - float yaw_rate = apply_deadband(s_smooth_gy); - float pitch_rate = apply_deadband(s_smooth_gx); - - s_pending_yaw_rad += yaw_rate * dt * dusk::getSettings().game.gyroAimSensitivityX; - s_pending_pitch_rad += pitch_rate * dt * dusk::getSettings().game.gyroAimSensitivityY; -} - -void consumeAimDeltas(float& out_yaw_rad, float& out_pitch_rad) { - out_yaw_rad = s_pending_yaw_rad; - out_pitch_rad = s_pending_pitch_rad; - s_pending_yaw_rad = 0.0f; - s_pending_pitch_rad = 0.0f; -} - -bool queryGyroAimItemContext() { - if (!static_cast(dusk::getSettings().game.enableGyroAim)) { - return false; - } - - daAlink_c* link = daAlink_getAlinkActorClass(); - if (link == nullptr) { - return false; - } - - return link->checkGyroAimItemContext() && dComIfGp_checkCameraAttentionStatus(link->field_0x317c, 0x10); -} -} // namespace dusk::gyro_aim diff --git a/src/dusk/imgui/ImGuiControllerOverlay.cpp b/src/dusk/imgui/ImGuiControllerOverlay.cpp index 8c24063375..dd0a15806c 100644 --- a/src/dusk/imgui/ImGuiControllerOverlay.cpp +++ b/src/dusk/imgui/ImGuiControllerOverlay.cpp @@ -5,7 +5,7 @@ #include "ImGuiConsole.hpp" #include "ImGuiMenuGame.hpp" -#include +#include namespace dusk { void ImGuiMenuGame::windowInputViewer() { @@ -260,31 +260,33 @@ namespace dusk { size.y = 130 * scale; ImGui::Dummy(size); - SDL_Gamepad* pad = SDL_GetGamepadFromPlayerIndex(0); - if (pad != nullptr && SDL_GamepadHasSensor(pad, SDL_SENSOR_GYRO)) { + if (PADHasSensor(PAD_1, PAD_SENSOR_GYRO) == TRUE) { ImGui::Separator(); - ImGui::TextUnformatted("Gyro"); + ImGui::Checkbox("Gyro Values", &m_showInputViewerGyro); + if (m_showInputViewerGyro) { + ImGui::TextUnformatted("Gyro"); - constexpr float kBarScale = 4.0f; - auto bar = [kBarScale](const char* label, float v) { - const float a = std::fabs(v); - const float t = std::min(1.f, a / kBarScale); - char overlay[32]; - snprintf(overlay, sizeof(overlay), "%s %+.3f", label, v); - ImGui::ProgressBar(t, ImVec2(-1, 0), overlay); - }; + constexpr float kBarScale = 4.0f; + auto bar = [kBarScale](const char* label, float v) { + const float a = std::fabs(v); + const float t = std::min(1.f, a / kBarScale); + char overlay[32]; + snprintf(overlay, sizeof(overlay), "%s %+.3f", label, v); + ImGui::ProgressBar(t, ImVec2(-1, 0), overlay); + }; - if (SDL_SetGamepadSensorEnabled(pad, SDL_SENSOR_GYRO, true)) { - float gyro[3]; - if (SDL_GetGamepadSensorData(pad, SDL_SENSOR_GYRO, gyro, 3)) { - bar("X", gyro[0]); - bar("Y", gyro[1]); - bar("Z", gyro[2]); + if (PADSetSensorEnabled(PAD_1, PAD_SENSOR_GYRO, TRUE) == TRUE) { + f32 gyro[3]; + if (PADGetSensorData(PAD_1, PAD_SENSOR_GYRO, gyro, 3) == TRUE) { + bar("X", gyro[0]); + bar("Y", gyro[1]); + bar("Z", gyro[2]); + } + } else { + bar("X", 0.f); + bar("Y", 0.f); + bar("Z", 0.f); } - } else { - bar("X", 0.f); - bar("Y", 0.f); - bar("Z", 0.f); } } diff --git a/src/dusk/imgui/ImGuiMenuEnhancements.cpp b/src/dusk/imgui/ImGuiMenuEnhancements.cpp index b7d75fea2f..516b924680 100644 --- a/src/dusk/imgui/ImGuiMenuEnhancements.cpp +++ b/src/dusk/imgui/ImGuiMenuEnhancements.cpp @@ -148,10 +148,29 @@ namespace dusk { "Slingshot, Gale Boomerang, Hero's Bow, Clawshot(s), Ball and Chain, and Dominion Rod."); } - config::ImGuiSliderFloat("Gyro Pitch Sensitivity", getSettings().game.gyroAimSensitivityY, 0.25f, 4.0f, "%.2f"); - config::ImGuiSliderFloat("Gyro Yaw Sensitivity", getSettings().game.gyroAimSensitivityX, 0.25f, 4.0f, "%.2f"); - config::ImGuiCheckbox("Invert Gyro Pitch", getSettings().game.gyroAimInvertPitch); - config::ImGuiCheckbox("Invert Gyro Yaw", getSettings().game.gyroAimInvertYaw); + config::ImGuiCheckbox("Gyro Rollgoal", getSettings().game.enableGyroRollgoal); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Enables the gyroscope on supported controllers to\n" + "tilt the Rollgoal table in Hena's Cabin."); + } + + if (getSettings().game.enableGyroAim) { + config::ImGuiSliderFloat("Gyro Pitch Sensitivity", getSettings().game.gyroAimSensitivityY, 0.25f, 4.0f, "%.2f"); + config::ImGuiSliderFloat("Gyro Yaw Sensitivity", getSettings().game.gyroAimSensitivityX, 0.25f, 4.0f, "%.2f"); + } + + if (getSettings().game.enableGyroRollgoal) { + config::ImGuiSliderFloat("Rollgoal Sensitivity", getSettings().game.gyroRollgoalSensitivity, 0.25f, 4.0f, "%.2f"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Additional multiplier for scaling how strongly\n" + "the gyroscope affects the Rollgoal table."); + } + } + + if (getSettings().game.enableGyroAim) { + config::ImGuiCheckbox("Invert Gyro Pitch", getSettings().game.gyroAimInvertPitch); + config::ImGuiCheckbox("Invert Gyro Yaw", getSettings().game.gyroAimInvertYaw); + } ImGui::SeparatorText("Tools"); diff --git a/src/dusk/imgui/ImGuiMenuGame.hpp b/src/dusk/imgui/ImGuiMenuGame.hpp index 77e5952d1a..4d51cbc865 100644 --- a/src/dusk/imgui/ImGuiMenuGame.hpp +++ b/src/dusk/imgui/ImGuiMenuGame.hpp @@ -30,6 +30,7 @@ namespace dusk { bool m_showControllerConfig = false; bool m_showInputViewer = false; + bool m_showInputViewerGyro = false; int m_inputOverlayCorner = 3; std::string m_controllerName; }; diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index 99ff183e79..fd6569ed2c 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -55,8 +55,10 @@ UserSettings g_userSettings = { // Input .enableGyroAim {"game.enableGyroAim", false}, + .enableGyroRollgoal {"game.enableGyroRollgoal", false}, .gyroAimSensitivityX {"game.gyroAimSensitivityX", 1.0f}, .gyroAimSensitivityY {"game.gyroAimSensitivityY", 1.0f}, + .gyroRollgoalSensitivity {"game.gyroRollgoalSensitivity", 1.0f}, .gyroAimInvertPitch {"game.gyroAimInvertPitch", false}, .gyroAimInvertYaw {"game.gyroAimInvertYaw", false}, @@ -134,8 +136,10 @@ void registerSettings() { Register(g_userSettings.game.fastSpinner); Register(g_userSettings.game.enableFrameInterpolation); Register(g_userSettings.game.enableGyroAim); + Register(g_userSettings.game.enableGyroRollgoal); Register(g_userSettings.game.gyroAimSensitivityX); Register(g_userSettings.game.gyroAimSensitivityY); + Register(g_userSettings.game.gyroRollgoalSensitivity); Register(g_userSettings.game.gyroAimInvertPitch); Register(g_userSettings.game.gyroAimInvertYaw); diff --git a/src/m_Do/m_Do_main.cpp b/src/m_Do/m_Do_main.cpp index 7e34770f11..715f9d3c84 100644 --- a/src/m_Do/m_Do_main.cpp +++ b/src/m_Do/m_Do_main.cpp @@ -51,7 +51,7 @@ #include "dusk/crash_reporting.h" #include "dusk/dusk.h" #include "dusk/frame_interpolation.h" -#include "dusk/gyro_aim.h" +#include "dusk/gyro.h" #include "dusk/imgui/ImGuiEngine.hpp" #include "dusk/logging.h" #include "dusk/main.h" @@ -245,9 +245,7 @@ void main01(void) { dusk::frame_interp::notify_presentation_frame(); while (accumulator >= kSimStepSeconds) { mDoCPd_c::read(); - if (dusk::getSettings().game.enableGyroAim) { - dusk::gyro_aim::read(static_cast(kSimStepSeconds), dusk::gyro_aim::queryGyroAimItemContext()); - } + dusk::gyro::read(kSimStepSeconds); fapGm_Execute(); mDoAud_Execute(); accumulator -= kSimStepSeconds; @@ -262,9 +260,7 @@ void main01(void) { // Game Inputs mDoCPd_c::read(); - if (dusk::getSettings().game.enableGyroAim) { - dusk::gyro_aim::read(static_cast(frame_seconds), dusk::gyro_aim::queryGyroAimItemContext()); - } + dusk::gyro::read(frame_seconds); // EXECUTE GAME LOGIC & RENDER // This calls mDoGph_Painter -> JFWDisplay -> GX Functions From dd84c3f46db22e1e05120ffc3e74e794f413ccd6 Mon Sep 17 00:00:00 2001 From: TakaRikka Date: Thu, 16 Apr 2026 03:33:16 -0700 Subject: [PATCH 10/15] add get item flags to save editor --- src/dusk/imgui/ImGuiSaveEditor.cpp | 84 ++++++++++++++++++------------ 1 file changed, 52 insertions(+), 32 deletions(-) diff --git a/src/dusk/imgui/ImGuiSaveEditor.cpp b/src/dusk/imgui/ImGuiSaveEditor.cpp index 03d948be36..2c679308a2 100644 --- a/src/dusk/imgui/ImGuiSaveEditor.cpp +++ b/src/dusk/imgui/ImGuiSaveEditor.cpp @@ -815,46 +815,66 @@ namespace dusk { void ImGuiSaveEditor::drawInventoryTab() { dSv_player_item_c& item = dComIfGs_getSaveData()->getPlayer().getItem(); - if (ImGui::Button("Default All##inv_default_all")) { - for (int slot = 0; slot < 24; slot++) { - dComIfGs_setItem(slot, getSlotDefault(slot)); + if (ImGui::TreeNode("Item Wheel")) { + if (ImGui::Button("Default All##inv_default_all")) { + for (int slot = 0; slot < 24; slot++) { + dComIfGs_setItem(slot, getSlotDefault(slot)); + } } - } - ImGui::SameLine(); - if (ImGui::Button("Clear All##inv_clear_all")) { - for (int slot = 0; slot < 24; slot++) { - dComIfGs_setItem(slot, dItemNo_NONE_e); - } - } - - ImGuiBeginGroupPanel("Items", { 200, 100 }); - for (int slot = 0; slot < 24; slot++) { - ImGui::Text("Slot %02d (%s): ", slot, itemMap.find(getSlotDefault(slot))->second.m_name.c_str()); - ImGui::SameLine(240.0f); - if (ImGui::BeginCombo(fmt::format("##ItemComboBox{}", slot).c_str(), itemMap.find(item.mItems[slot])->second.m_name.c_str())) { - if (ImGui::Selectable("None")) { + ImGui::SameLine(); + if (ImGui::Button("Clear All##inv_clear_all")) { + for (int slot = 0; slot < 24; slot++) { dComIfGs_setItem(slot, dItemNo_NONE_e); } + } - for (int i = 0; i < 254; i++) { - if (itemMap.find(i)->second.m_type != ITEMTYPE_EQUIP_e) continue; - - if (ImGui::Selectable(fmt::format("{0}##item_{1}{2}", itemMap.find(i)->second.m_name, slot, i).c_str())) { - dComIfGs_setItem(slot, itemMap.find(i)->first); + ImGuiBeginGroupPanel("Items", { 200, 100 }); + for (int slot = 0; slot < 24; slot++) { + ImGui::Text("Slot %02d (%s): ", slot, itemMap.find(getSlotDefault(slot))->second.m_name.c_str()); + ImGui::SameLine(240.0f); + if (ImGui::BeginCombo(fmt::format("##ItemComboBox{}", slot).c_str(), itemMap.find(item.mItems[slot])->second.m_name.c_str())) { + if (ImGui::Selectable("None")) { + dComIfGs_setItem(slot, dItemNo_NONE_e); } + + for (int i = 0; i < 254; i++) { + if (itemMap.find(i)->second.m_type != ITEMTYPE_EQUIP_e) continue; + + if (ImGui::Selectable(fmt::format("{0}##item_{1}{2}", itemMap.find(i)->second.m_name, slot, i).c_str())) { + dComIfGs_setItem(slot, itemMap.find(i)->first); + } + } + ImGui::EndCombo(); + } + ImGui::SameLine(); + if (ImGui::SmallButton(fmt::format("Default##slot_d_{}", slot).c_str())) { + dComIfGs_setItem(slot, getSlotDefault(slot)); + } + ImGui::SameLine(); + if (ImGui::SmallButton(fmt::format("Clear##slot_c_{}", slot).c_str())) { + dComIfGs_setItem(slot, dItemNo_NONE_e); } - ImGui::EndCombo(); - } - ImGui::SameLine(); - if (ImGui::SmallButton(fmt::format("Default##slot_d_{}", slot).c_str())) { - dComIfGs_setItem(slot, getSlotDefault(slot)); - } - ImGui::SameLine(); - if (ImGui::SmallButton(fmt::format("Clear##slot_c_{}", slot).c_str())) { - dComIfGs_setItem(slot, dItemNo_NONE_e); } + ImGuiEndGroupPanel(); + + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Get Item Flags")) { + for (int i = 0; i < 254; i++) { + if (itemMap.find(i)->second.m_name == "Reserved") continue; + + bool flag = dComIfGs_isItemFirstBit(i); + if (ImGui::Checkbox(fmt::format("{0}##item_{1}", itemMap.find(i)->second.m_name, i).c_str(), &flag)) { + if (flag) + dComIfGs_onItemFirstBit(i); + else + dComIfGs_offItemFirstBit(i); + } + } + + ImGui::TreePop(); } - ImGuiEndGroupPanel(); dSv_player_item_record_c& itemRecord = dComIfGs_getSaveData()->getPlayer().getItemRecord(); dSv_player_item_max_c& itemMax = dComIfGs_getSaveData()->getPlayer().getItemMax(); From 88bdea1fd347ad94b7d55ec4cc2446f2308b648b Mon Sep 17 00:00:00 2001 From: TakaRikka Date: Thu, 16 Apr 2026 04:46:06 -0700 Subject: [PATCH 11/15] mirror minimap --- src/d/d_map_path.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/d/d_map_path.cpp b/src/d/d_map_path.cpp index f7ce0f1a51..a31ccdedfd 100644 --- a/src/d/d_map_path.cpp +++ b/src/d/d_map_path.cpp @@ -456,6 +456,12 @@ void dRenderingFDAmap_c::preRenderingMap() { GXSetClipMode(GX_CLIP_ENABLE); setTevSettingNonTextureDirectColor(); f32 right = field_0x8 * 0.5f; +#if TARGET_PC + if (dusk::getSettings().game.enableMirrorMode) { + right = field_0x8 * -0.5f; + } +#endif + f32 top = field_0xc * 0.5f; Mtx44 matrix; C_MTXOrtho(matrix, top, -top, -right, right, 0.0f, 10000.0f); From c7d9a8733ff79bbef162ffe56b61b9962a7be593 Mon Sep 17 00:00:00 2001 From: Irastris Date: Thu, 16 Apr 2026 15:31:58 -0400 Subject: [PATCH 12/15] Gyro: Revisions & Rollgoal Mirror Mode --- include/dusk/gyro.h | 4 +- include/dusk/settings.h | 12 +++--- src/d/actor/d_a_alink_link.inc | 12 +----- src/dusk/gyro.cpp | 54 ++++++++++++------------ src/dusk/imgui/ImGuiMenuEnhancements.cpp | 36 ++++++++++------ src/dusk/settings.cpp | 24 ++++++----- 6 files changed, 74 insertions(+), 68 deletions(-) diff --git a/include/dusk/gyro.h b/include/dusk/gyro.h index 5636d34b05..4ee1c61737 100644 --- a/include/dusk/gyro.h +++ b/include/dusk/gyro.h @@ -3,11 +3,11 @@ namespace dusk::gyro { void read(float dt); -void consumeAimDeltas(float& out_yaw_rad, float& out_pitch_rad); +void getAimDeltas(float& out_yaw, float& out_pitch); bool queryGyroAimItemContext(); void rollgoalTick(bool play_active, s16 camera_yaw); -void rollgoalTableOffset(s16& out_add_x, s16& out_add_z); +void rollgoalTableOffset(s16& out_ax, s16& out_az); extern bool s_sensor_keep_alive; bool get_sensor_keep_alive(); diff --git a/include/dusk/settings.h b/include/dusk/settings.h index 7bfc65a312..ea3d090e6b 100644 --- a/include/dusk/settings.h +++ b/include/dusk/settings.h @@ -82,11 +82,13 @@ struct UserSettings { // Input ConfigVar enableGyroAim; ConfigVar enableGyroRollgoal; - ConfigVar gyroAimSensitivityX; - ConfigVar gyroAimSensitivityY; - ConfigVar gyroRollgoalSensitivity; - ConfigVar gyroAimInvertPitch; - ConfigVar gyroAimInvertYaw; + ConfigVar gyroSensitivityX; + ConfigVar gyroSensitivityY; + ConfigVar gyroSensitivityRollgoal; + ConfigVar gyroSmoothing; + ConfigVar gyroDeadband; + ConfigVar gyroInvertPitch; + ConfigVar gyroInvertYaw; // Cheats ConfigVar enableFastIronBoots; diff --git a/src/d/actor/d_a_alink_link.inc b/src/d/actor/d_a_alink_link.inc index 6bf9ae23dc..6de02a7598 100644 --- a/src/d/actor/d_a_alink_link.inc +++ b/src/d/actor/d_a_alink_link.inc @@ -142,17 +142,7 @@ BOOL daAlink_c::setBodyAngleToCamera() { f32 gy_yaw = 0.f; f32 gy_pitch = 0.f; - dusk::gyro::consumeAimDeltas(gy_yaw, gy_pitch); - - if (dusk::getSettings().game.gyroAimInvertPitch) { - gy_pitch = -gy_pitch; - } - if (dusk::getSettings().game.gyroAimInvertYaw) { - gy_yaw = -gy_yaw; - } - if (dusk::getSettings().game.enableMirrorMode) { - gy_yaw = -gy_yaw; - } + dusk::gyro::getAimDeltas(gy_yaw, gy_pitch); shape_angle.y = shape_angle.y + cM_rad2s(gy_yaw * gyro_scale); sp8 = sp8 + cM_rad2s(gy_pitch * gyro_scale); diff --git a/src/dusk/gyro.cpp b/src/dusk/gyro.cpp index a2b026713a..0905bd59fd 100644 --- a/src/dusk/gyro.cpp +++ b/src/dusk/gyro.cpp @@ -3,31 +3,28 @@ namespace dusk::gyro { namespace { -// TODO: Make deadband and smoothing configurable -constexpr float kDeadbandRadS = 0.04f; -constexpr float kSmoothAlpha = 0.35f; constexpr s32 kRollgoalTableMaxOffset = 12000; +constexpr float kGyroEmaAlphaMin = 0.05f; +constexpr float kGyroEmaAlphaMax = 1.0f; bool s_sensor_enabled = false; float s_smooth_gx = 0.0f; float s_smooth_gy = 0.0f; float s_smooth_gz = 0.0f; float s_yaw_rad = 0.0f; -float s_yaw_rad_pending = 0.0f; float s_pitch_rad = 0.0f; -float s_pitch_rad_pending = 0.0f; float s_roll_rad = 0.0f; s32 s_rollgoal_ax = 0; s32 s_rollgoal_az = 0; void reset_filter_state() { s_smooth_gx = s_smooth_gy = s_smooth_gz = 0.0f; - s_yaw_rad_pending = s_pitch_rad_pending = s_roll_rad = 0.0f; + s_yaw_rad = s_pitch_rad = s_roll_rad = 0.0f; s_rollgoal_ax = s_rollgoal_az = 0; } -float apply_deadband(float v) { - if (v > -kDeadbandRadS && v < kDeadbandRadS) { +float apply_deadband(float v, float deadband_rad_s) { + if (v > -deadband_rad_s && v < deadband_rad_s) { return 0.0f; } return v; @@ -35,7 +32,6 @@ float apply_deadband(float v) { } // namespace bool s_sensor_keep_alive = false; - bool get_sensor_keep_alive() { return s_sensor_keep_alive; } void set_sensor_keep_alive(bool value) { s_sensor_keep_alive = value; } @@ -77,22 +73,25 @@ void read(float dt) { return; } - s_smooth_gx += kSmoothAlpha * (gyro[0] - s_smooth_gx); - s_smooth_gy += kSmoothAlpha * (gyro[1] - s_smooth_gy); - s_smooth_gz += kSmoothAlpha * (gyro[2] - s_smooth_gz); + const float smooth_alpha = kGyroEmaAlphaMax + dusk::getSettings().game.gyroSmoothing * (kGyroEmaAlphaMin - kGyroEmaAlphaMax); + const float deadband = dusk::getSettings().game.gyroDeadband; - s_pitch_rad = apply_deadband(s_smooth_gx) * dt * dusk::getSettings().game.gyroAimSensitivityX; - s_yaw_rad = apply_deadband(s_smooth_gy) * dt * dusk::getSettings().game.gyroAimSensitivityY; - s_roll_rad = apply_deadband(s_smooth_gz) * dt * dusk::getSettings().game.gyroAimSensitivityX; // GYRO NOTE: Exposing Z sensitivity seems unusual, so I'm just using X + s_smooth_gx += smooth_alpha * (gyro[0] - s_smooth_gx); + s_smooth_gy += smooth_alpha * (gyro[1] - s_smooth_gy); + s_smooth_gz += smooth_alpha * (gyro[2] - s_smooth_gz); - s_pitch_rad_pending += s_pitch_rad; - s_yaw_rad_pending += s_yaw_rad; + s_pitch_rad = -apply_deadband(s_smooth_gx, deadband) * dt * dusk::getSettings().game.gyroSensitivityX; + s_yaw_rad = apply_deadband(s_smooth_gy, deadband) * dt * dusk::getSettings().game.gyroSensitivityY; + s_roll_rad = apply_deadband(s_smooth_gz, deadband) * dt * dusk::getSettings().game.gyroSensitivityX; // GYRO NOTE: Exposing Z sensitivity seems unusual, so I'm just using X + + s_pitch_rad = dusk::getSettings().game.gyroInvertPitch ? -s_pitch_rad : s_pitch_rad; + s_yaw_rad = dusk::getSettings().game.gyroInvertYaw ? -s_yaw_rad : s_yaw_rad; + s_yaw_rad = dusk::getSettings().game.enableMirrorMode ? -s_yaw_rad : s_yaw_rad; } -void consumeAimDeltas(float& out_yaw_rad, float& out_pitch_rad) { - out_yaw_rad = s_yaw_rad_pending; - out_pitch_rad = s_pitch_rad_pending; - s_yaw_rad_pending = s_pitch_rad_pending = 0.0f; +void getAimDeltas(float& out_yaw, float& out_pitch) { + out_yaw = s_yaw_rad; + out_pitch = s_pitch_rad; } void rollgoalTick(bool play_active, s16 camera_yaw) { @@ -101,13 +100,14 @@ void rollgoalTick(bool play_active, s16 camera_yaw) { return; } - const float pitch_rad = s_pitch_rad * dusk::getSettings().game.gyroRollgoalSensitivity; - const float roll_rad = s_roll_rad * dusk::getSettings().game.gyroRollgoalSensitivity; + float pitch_rad = -s_pitch_rad * dusk::getSettings().game.gyroSensitivityRollgoal; + float roll_rad = s_roll_rad * dusk::getSettings().game.gyroSensitivityRollgoal; + roll_rad = dusk::getSettings().game.enableMirrorMode ? -roll_rad : roll_rad; s_rollgoal_az += cM_rad2s(roll_rad); cXyz in(roll_rad, 0.0f, pitch_rad); cXyz out; - cMtx_YrotS(*calc_mtx, static_cast(-camera_yaw)); + cMtx_YrotS(*calc_mtx, -camera_yaw); MtxPosition(&in, &out); s_rollgoal_ax += cM_rad2s(out.z); @@ -116,8 +116,8 @@ void rollgoalTick(bool play_active, s16 camera_yaw) { s_rollgoal_az = std::clamp(s_rollgoal_az, -kRollgoalTableMaxOffset, kRollgoalTableMaxOffset); } -void rollgoalTableOffset(s16& out_add_x, s16& out_add_z) { - out_add_x = static_cast(s_rollgoal_ax); - out_add_z = static_cast(s_rollgoal_az); +void rollgoalTableOffset(s16& out_ax, s16& out_az) { + out_ax = static_cast(s_rollgoal_ax); + out_az = static_cast(s_rollgoal_az); } } // namespace dusk::gyro diff --git a/src/dusk/imgui/ImGuiMenuEnhancements.cpp b/src/dusk/imgui/ImGuiMenuEnhancements.cpp index 516b924680..d47b4e8d4b 100644 --- a/src/dusk/imgui/ImGuiMenuEnhancements.cpp +++ b/src/dusk/imgui/ImGuiMenuEnhancements.cpp @@ -154,22 +154,32 @@ namespace dusk { "tilt the Rollgoal table in Hena's Cabin."); } - if (getSettings().game.enableGyroAim) { - config::ImGuiSliderFloat("Gyro Pitch Sensitivity", getSettings().game.gyroAimSensitivityY, 0.25f, 4.0f, "%.2f"); - config::ImGuiSliderFloat("Gyro Yaw Sensitivity", getSettings().game.gyroAimSensitivityX, 0.25f, 4.0f, "%.2f"); - } + if (getSettings().game.enableGyroAim || getSettings().game.enableGyroRollgoal) { + config::ImGuiSliderFloat("Gyro Pitch Sensitivity", getSettings().game.gyroSensitivityY, 0.25f, 4.0f, "%.2f"); + config::ImGuiSliderFloat("Gyro Yaw Sensitivity", getSettings().game.gyroSensitivityX, 0.25f, 4.0f, "%.2f"); - if (getSettings().game.enableGyroRollgoal) { - config::ImGuiSliderFloat("Rollgoal Sensitivity", getSettings().game.gyroRollgoalSensitivity, 0.25f, 4.0f, "%.2f"); - if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Additional multiplier for scaling how strongly\n" - "the gyroscope affects the Rollgoal table."); + if (getSettings().game.enableGyroRollgoal) { + config::ImGuiSliderFloat("Rollgoal Sensitivity", getSettings().game.gyroSensitivityRollgoal, 0.25f, 4.0f, "%.2f"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Additional multiplier for scaling how strongly\n" + "the gyroscope affects the Rollgoal table."); + } } - } - if (getSettings().game.enableGyroAim) { - config::ImGuiCheckbox("Invert Gyro Pitch", getSettings().game.gyroAimInvertPitch); - config::ImGuiCheckbox("Invert Gyro Yaw", getSettings().game.gyroAimInvertYaw); + config::ImGuiSliderFloat("Gyro Deadband", getSettings().game.gyroDeadband, 0.0f, 0.5f, "%.3f"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Angular rates below this magnitude are treated as zero,\n" + "reducing drift and jitter when the controller is still."); + } + + config::ImGuiSliderFloat("Gyro Smoothing", getSettings().game.gyroSmoothing, 0.0f, 1.0f, "%.2f"); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Low values track raw gyro input more closely,\n" + "while higher values smooth out input over time."); + } + + config::ImGuiCheckbox("Invert Gyro Pitch", getSettings().game.gyroInvertPitch); + config::ImGuiCheckbox("Invert Gyro Yaw", getSettings().game.gyroInvertYaw); } ImGui::SeparatorText("Tools"); diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index fd6569ed2c..6718297f9a 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -56,11 +56,13 @@ UserSettings g_userSettings = { // Input .enableGyroAim {"game.enableGyroAim", false}, .enableGyroRollgoal {"game.enableGyroRollgoal", false}, - .gyroAimSensitivityX {"game.gyroAimSensitivityX", 1.0f}, - .gyroAimSensitivityY {"game.gyroAimSensitivityY", 1.0f}, - .gyroRollgoalSensitivity {"game.gyroRollgoalSensitivity", 1.0f}, - .gyroAimInvertPitch {"game.gyroAimInvertPitch", false}, - .gyroAimInvertYaw {"game.gyroAimInvertYaw", false}, + .gyroSensitivityX {"game.gyroSensitivityX", 1.0f}, + .gyroSensitivityY {"game.gyroSensitivityY", 1.0f}, + .gyroSensitivityRollgoal {"game.gyroSensitivityRollgoal", 1.0f}, + .gyroSmoothing {"game.gyroSmoothing", 0.65f}, + .gyroDeadband {"game.gyroDeadband", 0.04f}, + .gyroInvertPitch {"game.gyroInvertPitch", false}, + .gyroInvertYaw {"game.gyroInvertYaw", false}, // Cheats .enableFastIronBoots {"game.enableFastIronBoots", false}, @@ -137,11 +139,13 @@ void registerSettings() { Register(g_userSettings.game.enableFrameInterpolation); Register(g_userSettings.game.enableGyroAim); Register(g_userSettings.game.enableGyroRollgoal); - Register(g_userSettings.game.gyroAimSensitivityX); - Register(g_userSettings.game.gyroAimSensitivityY); - Register(g_userSettings.game.gyroRollgoalSensitivity); - Register(g_userSettings.game.gyroAimInvertPitch); - Register(g_userSettings.game.gyroAimInvertYaw); + Register(g_userSettings.game.gyroSensitivityX); + Register(g_userSettings.game.gyroSensitivityY); + Register(g_userSettings.game.gyroSensitivityRollgoal); + Register(g_userSettings.game.gyroDeadband); + Register(g_userSettings.game.gyroSmoothing); + Register(g_userSettings.game.gyroInvertPitch); + Register(g_userSettings.game.gyroInvertYaw); Register(g_userSettings.backend.isoPath); Register(g_userSettings.backend.graphicsBackend); From 2cc11c74b74f224fe49a58c42b313b49eadd5d91 Mon Sep 17 00:00:00 2001 From: MelonSpeedruns Date: Thu, 16 Apr 2026 15:58:39 -0400 Subject: [PATCH 13/15] Lots of Imgui Adjustments (#405) - Store if the F1 menu is open for the next boot - Auto-save controller changes without having the press the Save button - Changed FPS X position so it doesn't clip to the right of the window - Changed F1 Toast to be 2.5 secs so it doesn't go over the title screen Co-authored-by: MelonSpeedruns --- include/dusk/settings.h | 1 + src/dusk/imgui/ImGuiConsole.cpp | 28 +++++++++++++++++++--------- src/dusk/imgui/ImGuiMenuGame.cpp | 21 ++++++++++++++------- src/dusk/settings.cpp | 4 +++- 4 files changed, 37 insertions(+), 17 deletions(-) diff --git a/include/dusk/settings.h b/include/dusk/settings.h index ea3d090e6b..2676c461cd 100644 --- a/include/dusk/settings.h +++ b/include/dusk/settings.h @@ -110,6 +110,7 @@ struct UserSettings { ConfigVar showPipelineCompilation; ConfigVar wasPresetChosen; ConfigVar enableCrashReporting; + ConfigVar duskMenuOpen; } backend; }; diff --git a/src/dusk/imgui/ImGuiConsole.cpp b/src/dusk/imgui/ImGuiConsole.cpp index f71dc8c278..eccf9b3c20 100644 --- a/src/dusk/imgui/ImGuiConsole.cpp +++ b/src/dusk/imgui/ImGuiConsole.cpp @@ -234,31 +234,40 @@ namespace dusk { ImGuiMenuGame::ToggleFullscreen(); } - bool showMenu = !dusk::IsGameLaunched || !CheckMenuViewToggle(ImGuiKey_F1, m_isHidden); + if (!dusk::IsGameLaunched) { + m_preLaunchWindow.draw(); + } + + m_isHidden = getSettings().backend.duskMenuOpen; + CheckMenuViewToggle(ImGuiKey_F1, m_isHidden); - if (showMenu && ImGui::BeginMainMenuBar()) { + if (dusk::IsGameLaunched) { + if (getSettings().backend.duskMenuOpen != m_isHidden) { + m_isHidden = !getSettings().backend.duskMenuOpen; + getSettings().backend.duskMenuOpen.setValue(m_isHidden); + config::Save(); + } + } + + if ((!dusk::IsGameLaunched || getSettings().backend.duskMenuOpen) && ImGui::BeginMainMenuBar()) { m_menuGame.draw(); m_menuEnhancements.draw(); m_menuTools.draw(); - ImGui::SetCursorPosX(ImGui::GetWindowWidth() - 80.0f * ImGuiScale()); + ImGui::SetCursorPosX(ImGui::GetWindowWidth() - 100.0f * ImGuiScale()); ImGuiIO& io = ImGui::GetIO(); ImGuiStringViewText(fmt::format(FMT_STRING("FPS: {:.2f}\n"), io.Framerate)); ImGui::EndMainMenuBar(); } - if (!dusk::IsGameLaunched) { - m_preLaunchWindow.draw(); - } - if (!getSettings().backend.wasPresetChosen) { m_firstRunPreset.draw(); return; } if (dusk::IsGameLaunched && !m_isLaunchInitialized) { - m_toasts.emplace_back("Press F1 to toggle menu"s, 5.f); + m_toasts.emplace_back("Press F1 to toggle menu"s, 2.5f); m_isLaunchInitialized = true; } @@ -280,7 +289,8 @@ namespace dusk { DuskDebugPad(); // temporary, remove later // Only show cursor when menu or any windows are open - if (showMenu || ImGui::GetIO().MetricsRenderWindows > 0) { + if ((!dusk::IsGameLaunched || getSettings().backend.duskMenuOpen) || ImGui::GetIO().MetricsRenderWindows > 0) + { ImGui::GetIO().ConfigFlags &= ~ImGuiConfigFlags_NoMouseCursorChange; // Imgui will re-show cursor. } else { diff --git a/src/dusk/imgui/ImGuiMenuGame.cpp b/src/dusk/imgui/ImGuiMenuGame.cpp index 48c64f8f0e..c40e362db8 100644 --- a/src/dusk/imgui/ImGuiMenuGame.cpp +++ b/src/dusk/imgui/ImGuiMenuGame.cpp @@ -304,6 +304,7 @@ namespace dusk { m_controllerConfig.m_pendingButtonMapping = nullptr; m_controllerConfig.m_pendingPort = -1; PADBlockInput(false); + PADSerializeMappings(); } } @@ -316,6 +317,7 @@ namespace dusk { m_controllerConfig.m_pendingAxisMapping = nullptr; m_controllerConfig.m_pendingPort = -1; PADBlockInput(false); + PADSerializeMappings(); } else { auto nativeButton = PADGetNativeButtonPressed(m_controllerConfig.m_pendingPort); if (nativeButton != -1) { @@ -324,6 +326,7 @@ namespace dusk { m_controllerConfig.m_pendingAxisMapping = nullptr; m_controllerConfig.m_pendingPort = -1; PADBlockInput(false); + PADSerializeMappings(); } } } @@ -400,11 +403,6 @@ namespace dusk { // if "None" selected PADClearPort(m_controllerConfig.m_selectedPort); } - } - - // save mappings button - ImGui::SameLine(); - if (ImGui::Button("Save")) { PADSerializeMappings(); } @@ -412,6 +410,7 @@ namespace dusk { ImGui::SameLine(); if (ImGui::Button("Default")) { PADRestoreDefaultMapping(m_controllerConfig.m_selectedPort); + PADSerializeMappings(); } // buttons panel @@ -508,6 +507,7 @@ namespace dusk { float tmp = static_cast(deadZones->leftTriggerActivationZone * 100.f) / 32767.f; if (ImGui::DragFloat("##LThreshold", &tmp, 0.5f, 0.f, 100.f, "%.3f%%")) { deadZones->leftTriggerActivationZone = static_cast((tmp / 100.f) * 32767); + PADSerializeMappings(); } } } @@ -519,6 +519,7 @@ namespace dusk { float tmp = static_cast(deadZones->rightTriggerActivationZone * 100.f) / 32767.f; if (ImGui::DragFloat("##RThreshold", &tmp, 0.5f, 0.f, 100.f, "%.3f%%")) { deadZones->rightTriggerActivationZone = static_cast((tmp / 100.f) * 32767); + PADSerializeMappings(); } } } @@ -581,6 +582,7 @@ namespace dusk { float tmp = static_cast(deadZones->stickDeadZone * 100.f) / 32767.f; if (ImGui::DragFloat("##mainDeadZone", &tmp, 0.5f, 0.f, 100.f, "%.3f%%")) { deadZones->stickDeadZone = static_cast((tmp / 100.f) * 32767); + PADSerializeMappings(); } } } @@ -643,6 +645,7 @@ namespace dusk { float tmp = static_cast(deadZones->substickDeadZone * 100.f) / 32767.f; if (ImGui::DragFloat("##subDeadZone", &tmp, 0.5f, 0.f, 100.f, "%.3f%%")) { deadZones->substickDeadZone = static_cast((tmp / 100.f) * 32767); + PADSerializeMappings(); } } } @@ -654,8 +657,12 @@ namespace dusk { ImGuiBeginGroupPanel("Options", ImVec2(150 * scale, 20 * scale)); if (deadZones != nullptr) { - ImGui::Checkbox("Enable Dead Zones", &deadZones->useDeadzones); - ImGui::Checkbox("Emulate Triggers", &deadZones->emulateTriggers); + if (ImGui::Checkbox("Enable Dead Zones", &deadZones->useDeadzones)) { + PADSerializeMappings(); + } + if (ImGui::Checkbox("Emulate Triggers", &deadZones->emulateTriggers)) { + PADSerializeMappings(); + } } ImGuiEndGroupPanel(); diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index 6718297f9a..e4cace9ff0 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -83,7 +83,8 @@ UserSettings g_userSettings = { .skipPreLaunchUI {"backend.skipPreLaunchUI", false}, .showPipelineCompilation {"backend.showPipelineCompilation", false}, .wasPresetChosen {"backend.wasPresetChosen", false}, - .enableCrashReporting {"backend.enableCrashReporting", true} + .enableCrashReporting {"backend.enableCrashReporting", true}, + .duskMenuOpen {"backend.duskMenuOpen", false} } }; @@ -153,6 +154,7 @@ void registerSettings() { Register(g_userSettings.backend.showPipelineCompilation); Register(g_userSettings.backend.wasPresetChosen); Register(g_userSettings.backend.enableCrashReporting); + Register(g_userSettings.backend.duskMenuOpen); } // Transient settings From 72a73ba661b11fa8439ecc7d2a8268ff2a657487 Mon Sep 17 00:00:00 2001 From: roeming Date: Thu, 16 Apr 2026 19:11:20 -0400 Subject: [PATCH 14/15] Add UI for better manipulation of event flags --- src/dusk/imgui/ImGuiEventFlags.hpp | 842 +++++++++++++++++++++++++++++ src/dusk/imgui/ImGuiSaveEditor.cpp | 135 ++++- 2 files changed, 965 insertions(+), 12 deletions(-) create mode 100644 src/dusk/imgui/ImGuiEventFlags.hpp diff --git a/src/dusk/imgui/ImGuiEventFlags.hpp b/src/dusk/imgui/ImGuiEventFlags.hpp new file mode 100644 index 0000000000..694336da03 --- /dev/null +++ b/src/dusk/imgui/ImGuiEventFlags.hpp @@ -0,0 +1,842 @@ +#ifndef DUSK_IMGUI_EVENTFLAGS_HPP +#define DUSK_IMGUI_EVENTFLAGS_HPP + +#include +#include + +struct duskImguiEventFlagEntry { + uint8_t byteIndex; + uint8_t bitIndex; + uint16_t flagID; + std::string flagName; + std::string description; + std::string location; +}; + +inline duskImguiEventFlagEntry duskImguiEventFlags[] = { + { 0x00, 0x80, 0x0080, "TEST_001", "Kakariko bridge portal warp hint", "Faron Woods" }, + { 0x00, 0x40, 0x0040, "TEST_002", "Big magma stone portal warp hint", "Death Mountain" }, + { 0x00, 0x20, 0x0020, "TEST_003", "Handed over tomato puree", "LV5 Dungeon" }, + { 0x00, 0x10, 0x0010, "TEST_004", "Handed over secret ingredient", "LV5 Dungeon" }, + { 0x00, 0x08, 0x0008, "F_0001", "Spoke to Renado after Colin returns", "Kakariko Village" }, + { 0x00, 0x04, 0x0004, "F_0002", "Lost wrestling match to elder goron for first time", "Death Mountain (room)" }, + { 0x00, 0x02, 0x0002, "F_0003", "Handed over tomato puree and left room", "LV5 Dungeon" }, + { 0x00, 0x01, 0x0001, "F_0004", "Handed over secret ingredient and left room", "LV5 Dungeon" }, + { 0x01, 0x80, 0x0180, "F_0005", "Gathered 14 Tears of Light in area 4", "Misc." }, + { 0x01, 0x40, 0x0140, "F_0006", "First conversation with Yeto in kitchen", "LV5 Dungeon" }, + { 0x01, 0x20, 0x0120, "F_0007", "Spoke to Yeta while holding cheese", "LV5 Dungeon" }, + { 0x01, 0x10, 0x0110, "F_0008", "First conversation with Fado at the farm on 1st day", "Ordon Village" }, + { 0x01, 0x08, 0x0108, "F_0009", "Approach secret entrance with Colin", "Ordon Woods" }, + { 0x01, 0x04, 0x0104, "F_0010", "First convo with Colin blocking path (forced)", "Ordon Village" }, + { 0x01, 0x02, 0x0102, "F_0011", "Fence jumping complete", "Ranch" }, + { 0x01, 0x01, 0x0101, "F_0012", "Get metal sword!", "Ordon Village" }, + { 0x02, 0x80, 0x0280, "F_0013", "2nd day - First time rampaging goat escapes", "Ordon Village" }, + { 0x02, 0x40, 0x0240, "F_0014", "sword tutorial ends", "Ordon Village" }, + { 0x02, 0x20, 0x0220, "F_0015", "Slingshot tutorial ends", "Ordon Village" }, + { 0x02, 0x10, 0x0210, "F_0016", "On 3rd day, start following Colin who is blocking path", "Ordon Village" }, + { 0x02, 0x08, 0x0208, "F_0017", "Spoke to beth right after sword tutorial", "Ordon Village" }, + { 0x02, 0x04, 0x0204, "F_0018", "Asked by Fado to jump fence", "Ranch" }, + { 0x02, 0x02, 0x0202, "F_0019", "Spoke with Ilia (Colin is there too) at the spring", "Ordon Woods" }, + { 0x02, 0x01, 0x0201, "F_0020", "First convo with Sera while shop is closed", "Ordon Village" }, + { 0x03, 0x80, 0x0380, "D_0001", "Stopped by squirrel in front of house at night", "Ordon Village" }, + { 0x03, 0x40, 0x0340, "F_0021", "2nd day: spoke with Pergie", "Ordon Village" }, + { 0x03, 0x20, 0x0320, "F_0022", "Start fence-jump on 1st day", "Ranch" }, + { 0x03, 0x10, 0x0310, "F_0023", "Called by Jaggle from below hill", "Ordon Village" }, + { 0x03, 0x08, 0x0308, "F_0024", "Spoke with Talo/Malo/Beth (before obtaining slingshot)", "Ordon Village" }, + { 0x03, 0x04, 0x0304, "F_0025", "Pass Uli's pick-up tutorial", "Ordon Village" }, + { 0x03, 0x02, 0x0302, "F_0026", "gave wooden sword to talo on 3rd day", "Ordon Village" }, + { 0x03, 0x01, 0x0301, "F_0027", "Uli tutorial ends (same whether pass or fail)", "Ordon Village" }, + { 0x04, 0x80, 0x0480, "F_0028", "spoke to yeta while holding pumpkin", "LV5 Dungeon" }, + { 0x04, 0x40, 0x0440, "F_0029", "2nd day - refused sword tutorial", "Ordon Village" }, + { 0x04, 0x20, 0x0420, "F_0030", "before sword tutorial - first conversation with Beth", "Ordon Village" }, + { 0x04, 0x10, 0x0410, "F_0031", "2nd day - Spoke to Uli bfore finding basket", "Ordon Village" }, + { 0x04, 0x08, 0x0408, "F_0032", "3rd day - First convo with fado (before forced goat chase)", "Ranch" }, + { 0x04, 0x04, 0x0404, "F_0033", "First day - spoke with Uli", "Ordon Village" }, + { 0x04, 0x02, 0x0402, "M_006", "3rd day - finished chasing goats, speak to Fado in free state", "Ranch" }, + { 0x04, 0x01, 0x0401, "M_007", "first conversation with Shad in basement (about the words of opening)", "Kakariko Village" }, + { 0x05, 0x80, 0x0580, "M_008", "cutscene - attacked by monsters at Ordon spring", "Kawagoe Cutscene" }, + { 0x05, 0x40, 0x0540, "M_009", "[cutscene: 6B] Prison escape - Midna rides on back", "Kawagoe Cutscene" }, + { 0x05, 0x20, 0x0520, "M_010", "[cutscene: 6A] Midna appears in the prison", "Kawagoe Cutscene" }, + { 0x05, 0x10, 0x0510, "M_011", "Midna removes wolf's chains in prison", "Inside Hyrule Castle" }, + { 0x05, 0x08, 0x0508, "M_012", "[cutscene: 7] Meet Princess Zelda at castle", "Kawagoe Cutscene" }, + { 0x05, 0x04, 0x0504, "M_013", "First heard about Twilight gate from Midna", "Misc." }, + { 0x05, 0x02, 0x0502, "M_014", "[cutscene: 8] First warped from castle by Midna", "Kawagoe Cutscene" }, + { 0x05, 0x01, 0x0501, "M_015", "Can use Midna's B charge attack", "Faron Woods" }, + { 0x06, 0x80, 0x0680, "M_016", "[cutscene: 9] Ordon village spirit appears", "Kawagoe Cutscene" }, + { 0x06, 0x40, 0x0640, "M_017", "[cutscene: 10] Dark Hyrule Forest - Midna again", "Kawagoe Cutscene" }, + { 0x06, 0x20, 0x0620, "M_018", "Brought Kakariko bridge back to original location", "main event" }, + { 0x06, 0x10, 0x0610, "M_019", "[cutscene: 11] forest spirit revived - Hero's birth", "Kawagoe Cutscene" }, + { 0x06, 0x08, 0x0608, "M_020", "[cutscene: ] Colin kidnapped : ON once watched", "Kawagoe Cutscene" }, + { 0x06, 0x04, 0x0604, "M_021", "First portal warp", "main event" }, + { 0x06, 0x02, 0x0602, "M_022", "LV1 dungeon clear (Midna creates warp hole)", "LV1 Dungeon" }, + { 0x06, 0x01, 0x0601, "M_023", "Epona rescued flag", "main event" }, + { 0x07, 0x80, 0x0780, "M_024", "[cutscene: 16] take back Colin", "Kawagoe Cutscene" }, + { 0x07, 0x40, 0x0740, "M_025", "First wresting match against fat (elder) goron", "Death Mountain (room)" }, + { 0x07, 0x20, 0x0720, "M_026", "Reunion with Bo (Watched cutscene before wrestle match)", "Ordon Village" }, + { 0x07, 0x10, 0x0710, "M_027", "[cutscene: 13] kids in the church (beast eyes)", "Kawagoe Cutscene" }, + { 0x07, 0x08, 0x0708, "M_028", "[cutscene: 14] restore mountain spirit - Reuinion with Colin et al.", "Kawagoe Cutscene" }, + { 0x07, 0x04, 0x0704, "M_029", "Win wrestle match against Gor Coron", "Death Mountain (room)" }, + { 0x07, 0x02, 0x0702, "M_030", "First conversation with Gor Coron", "Death Mountain (room)" }, + { 0x07, 0x01, 0x0701, "M_031", "LV2 dungeon clear", "LV2 Dungeon" }, + { 0x08, 0x80, 0x0880, "M_032", "Melted Zora river ice with magma rock", "main event" }, + { 0x08, 0x40, 0x0840, "M_033", "Start carriage guarding game", "main event" }, + { 0x08, 0x20, 0x0820, "M_034", "[cutscene: 19] Reunion with Ilia LV3", "Kawagoe Cutscene" }, + { 0x08, 0x10, 0x0810, "M_035", "[cutscene: 35] after carriage guarding event", "Kawagoe Cutscene" }, + { 0x08, 0x08, 0x0808, "M_036", "Begin carriage guarding (after joust revenge)", "main event" }, + { 0x08, 0x04, 0x0804, "M_037", "Got Zora armor from Zora queen", "Kakariko Village" }, + { 0x08, 0x02, 0x0802, "M_038", "[bow and arrow game] listen to hawkeye hint", "Kakariko Village" }, + { 0x08, 0x01, 0x0801, "M_039", "[Bow and arrow game] First time talking to Talo", "Kakariko Village" }, + { 0x09, 0x80, 0x0980, "M_040", "[bow and arrow game] Spoke to Talo after completing", "Kakariko Village" }, + { 0x09, 0x40, 0x0940, "M_041", "[Bow and Arrow game] First attempt", "Kakariko Village" }, + { 0x09, 0x20, 0x0920, "M_042", "[Bow and Arrow game] clear", "Kakariko Village" }, + { 0x09, 0x10, 0x0910, "M_043", "[Bow and Arrow game] Complete using Hawkeye", "Kakariko Village" }, + { 0x09, 0x08, 0x0908, "M_044", "[Barnes Bomb Shop] Bought premium pack", "Kakariko Village" }, + { 0x09, 0x04, 0x0904, "M_045", "LV3 Dungeon clear", "LV3 Dungeon" }, + { 0x09, 0x02, 0x0902, "M_046", "[Iza river descent] Get advice about boulder blocking river", "Zora's River" }, + { 0x09, 0x01, 0x0901, "M_047", "Iza Twilight - Talked after defeating shadow bugs", "Zora's River" }, + { 0x0A, 0x80, 0x0A80, "M_048", "Ran away while clearing rubble at hut", "Zora's River" }, + { 0x0A, 0x40, 0x0A40, "M_049", "Threw first rolling goron at death mountain", "Death Mountain" }, + { 0x0A, 0x20, 0x0A20, "M_050", "Joust bridge disappears", "main event" }, + { 0x0A, 0x10, 0x0A10, "M_051", "Shadow Kargorok (?) (Large) event complete (Horse grass appears in various places)", "main event" }, + { 0x0A, 0x08, 0x0A08, "M_052", "Horseback battle clear", "main event" }, + { 0x0A, 0x04, 0x0A04, "M_053", "Horseback battle cutscene", "main event" }, + { 0x0A, 0x02, 0x0A02, "M_054", "Joust / one-on-one battle cutscene", "main event" }, + { 0x0A, 0x01, 0x0A01, "M_055", "Did damage at least once during joust/one-on-one battle", "main event" }, + { 0x0B, 0x80, 0x0B80, "M_056", "Ignored Iza's concerns", "Ring field" }, + { 0x0B, 0x40, 0x0B40, "M_057", "View boar cutscene after defeating King Bulblin", "Desert" }, + { 0x0B, 0x20, 0x0B20, "M_058", "First time meeting Yeta (forced converation)", "LV5 Dungeon" }, + { 0x0B, 0x10, 0x0B10, "M_059", "received map from Yeta", "LV5 Dungeon" }, + { 0x0B, 0x08, 0x0B08, "M_060", "[Iza river descent] ", "Zora's River" }, + { 0x0B, 0x04, 0x0B04, "M_061", "[Iza river descent] Got explanation for clearing rubble 1 time", "Zora's River" }, + { 0x0B, 0x02, 0x0B02, "M_062", "[Iza] Says thanks after night stalker battle", "Zora's River" }, + { 0x0B, 0x01, 0x0B01, "M_063", "[Iza river descent] Finish job (First time descending river)", "Zora's River" }, + { 0x0C, 0x80, 0x0C80, "M_064", "Water returns to Hylia Lake (spoke with Queen Rutela)", "Zora's Domain" }, + { 0x0C, 0x40, 0x0C40, "M_065", "Spoke with Faron spirit after clearing LV1 dungeon", "Faron Woods" }, + //{ 0x0C, 0x20, 0x0C20, "M_066", "N/A", "N/A" }, + { 0x0C, 0x10, 0x0C10, "M_067", "Midna riding / not riding (ON == riding)", "main event" }, + { 0x0C, 0x08, 0x0C08, "M_068", "when OFF, wolf carries sword and shield on back", "main event" }, + { 0x0C, 0x04, 0x0C04, "M_069", "First conversation with child goron shop clerk", "Kakariko Village" }, + { 0x0C, 0x02, 0x0C02, "M_070", "[cutscene: 18] Lanayru spirit restored", "Kawagoe Cutscene" }, + { 0x0C, 0x01, 0x0C01, "M_071", "[cutscene: 20] Zant appears (during Midna's desperate hour)", "Kawagoe Cutscene" }, + { 0x0D, 0x80, 0x0D80, "M_072", "Get wooden shield", "Ordon Village" }, + { 0x0D, 0x40, 0x0D40, "M_073", "Spoke with Renado after guarding carriage [0030]", "Kakariko Village" }, + { 0x0D, 0x20, 0x0D20, "M_074", "Spoke with Renado after guarding carriage [0031]", "Kakariko Village" }, + { 0x0D, 0x10, 0x0D10, "M_075", "Forced conversation with Yeta after getting bedroom key", "LV5 Dungeon" }, + { 0x0D, 0x08, 0x0D08, "M_076", "First conversation with Castle Town Malo Mart shop clerk", "Misc." }, + { 0x0D, 0x04, 0x0D04, "M_077", "Get shadow crystal (can now transform)", "main event" }, + { 0x0D, 0x02, 0x0D02, "M_078", "Spoke with frog A", "Ordon Village" }, + { 0x0D, 0x01, 0x0D01, "M_079", "Ordon village night: Heard Z Jump dialogie in Pergie's house", "Ordon Village" }, + { 0x0E, 0x80, 0x0E80, "M_080", "Spoke with brown cucco", "Ordon Village" }, + { 0x0E, 0x40, 0x0E40, "M_081", "First conversation with Goron that shoots you up (shared with everyone)", "Death Mountain" }, + { 0x0E, 0x20, 0x0E20, "M_082", "Spoke with spring Goron A", "Death Mountain" }, + { 0x0E, 0x10, 0x0E10, "M_083", "Heard Fyer's talk after water restored", "Lake Hylia" }, + { 0x0E, 0x08, 0x0E08, "M_084", "Complete sequence of shopping at Malo Mart first time", "Kakariko Village" }, + { 0x0E, 0x04, 0x0E04, "M_085", "Midna dialogue right before Boss Bug's Tear of Light appears", "Twilight Ring field" }, + { 0x0E, 0x02, 0x0E02, "M_086", "Show Boss Bug's Tear of Light on the map", "Twilight Ring field" }, + { 0x0E, 0x01, 0x0E01, "M_087", "Ilia memory event start", "Ring field" }, + { 0x0F, 0x80, 0x0F80, "M_088", "Get Renado's Letter", "Kakariko Village" }, + { 0x0F, 0x40, 0x0F40, "M_089", "First time entering doctor's office (forced conversation)", "Castle Town" }, + { 0x0F, 0x20, 0x0F20, "M_090", "Spoke to town doctor before showing receipt", "Castle Town" }, + { 0x0F, 0x10, 0x0F10, "M_091", "Buy out fundraiser amount (Malo becomes nice)", "Kakariko Village" }, + { 0x0F, 0x08, 0x0F08, "M_092", "Warped Eldin Bridge", "Ring field" }, + { 0x0F, 0x04, 0x0F04, "M_093", "First conversation with Fyer after desert's debut", "Lake Hylia" }, + { 0x0F, 0x02, 0x0F02, "M_094", "First time visiting Rizu's hut after completing river job (forced conversation)", "Zora's River" }, + { 0x0F, 0x01, 0x0F01, "M_095", "First time meeting Coro (obtain lantern)", "Faron Woods" }, + { 0x10, 0x80, 0x1080, "M_096", "3rd day: spoke with Pergie", "Ordon Village" }, + { 0x10, 0x40, 0x1040, "F_0034", "first conversation wtih Rusl", "Ordon Village" }, + { 0x10, 0x20, 0x1020, "F_0035", "F0003: Spoke to Colin while he is stopping hors (doesn't have fishing rod)", "Ordon Village" }, + { 0x10, 0x10, 0x1010, "M_001", "Opening cutscene", "Kawagoe Cutscene" }, + { 0x10, 0x08, 0x1008, "M_003", "F0003: Spoke to Colin while he is stopping hors (has fishing rod)", "Ordon Village" }, + { 0x10, 0x04, 0x1004, "F_0036", "Spoke to Jaggle using L-focus before climbing vines?", "Ordon Village" }, + { 0x10, 0x02, 0x1002, "F_0037", "Jaggle - Spoke on the hill?", "Ordon Village" }, + { 0x10, 0x01, 0x1001, "F_0038", "Opening (2nd day) cat returns home", "Ordon Village" }, + { 0x11, 0x80, 0x1180, "F_0039", "Warned by Hanch after climbing vines on 2nd day", "Ordon Village" }, + { 0x11, 0x40, 0x1140, "F_0040", "2nd day: Spoke to Jaggle after blowing on whistle", "Ordon Village" }, + { 0x11, 0x20, 0x1120, "F_0041", "Opening 2nd day - After this is turned ON Hanch is attacked by bees", "Ordon Village" }, + { 0x11, 0x10, 0x1110, "F_0042", "Spoke with Yeta right after arriving at bedroom", "LV5 Dungeon" }, + { 0x11, 0x08, 0x1108, "F_0043", "First visit after fundrasing funds drop to 200", "Kakariko Village" }, + { 0x11, 0x04, 0x1104, "F_0044", "Accepted sword tutorial first time", "Ordon Village" }, + { 0x11, 0x02, 0x1102, "F_0045", "Opening 3rd day - spoke with Uli", "Ordon Village" }, + { 0x11, 0x01, 0x1101, "F_0046", "Spoke with Sera after saving(lt;) failing(gt;) cat", "Ordon Village" }, + { 0x12, 0x80, 0x1280, "F_0047", "First visit after Ordon Village shop opens", "Ordon Village" }, + { 0x12, 0x40, 0x1240, "F_0048", "Uli's pick-up tutorial ", "Ordon Village" }, + { 0x12, 0x20, 0x1220, "F_0049", "Uli's pick up tutorial ", "Ordon Village" }, + { 0x12, 0x10, 0x1210, "F_0050", "Saw cutscene for getting iron boots", "Ordon Village" }, + { 0x12, 0x08, 0x1208, "F_0051", "Spoke to Sera in the shop after saving cat", "Ordon Village" }, + { 0x12, 0x04, 0x1204, "F_0052", "Had 2nd conversaton with Sera before saving cat", "Ordon Village" }, + { 0x12, 0x02, 0x1202, "F_0053", "Saw night stalker appearance cutscene", "Faron Woods" }, + { 0x12, 0x01, 0x1201, "F_0054", "Lost wrestling match with elder goron while wearing iron boots", "Death Mountain" }, + { 0x13, 0x80, 0x1380, "F_0055", "Received Vessel of Light from Faron spirit", "Faron Woods" }, + { 0x13, 0x40, 0x1340, "F_0056", "Lost to elder goron 2+ times", "Death Mountain" }, + { 0x13, 0x20, 0x1320, "F_0057", "[cutscene: 17] Part with the children", "Kawagoe Cutscene" }, + { 0x13, 0x10, 0x1310, "F_0058", "Listened to voices on other side of door in Telma's shop", "Castle Town" }, + { 0x13, 0x08, 0x1308, "F_0059", "Conversation after getting spirit and tears of light (darkness cleared) <- probably unused ...", "+ Scary flag" }, + { 0x13, 0x04, 0x1304, "F_0060", "First conversation with Hozu in the World of Light", "Zora's River" }, + { 0x13, 0x02, 0x1302, "F_0061", "Heard spring goron and shopkeeper rumers after winning wrestling match against elder goron", "Death Mountain" }, + { 0x13, 0x01, 0x1301, "F_0062", "Abandoned taking Fyer's cannon after paying", "Lake Hylia" }, + { 0x14, 0x80, 0x1480, "F_0063", "Used Fyer's cannon for first time", "Lake Hylia" }, + { 0x14, 0x40, 0x1440, "F_0064", "first convo with yeta after obtaining tomato puree", "LV5 Dungeon" }, + { 0x14, 0x20, 0x1420, "F_0065", "Yeta adds last symbol onto map", "LV5 Dungeon" }, + { 0x14, 0x10, 0x1410, "F_0066", "First saw Goron cutscene on mountain path", "Death Mountain" }, + { 0x14, 0x08, 0x1408, "F_0067", "Recieved milk jar (1/2) from Sera", "Ordon Village" }, + //{ 0x14, 0x04, 0x1404, "F_0068", "N/A", "N/A" }, + { 0x14, 0x02, 0x1402, "F_0069", "F0048: Thanked by Colen for clearing path", "Ordon Village" }, + { 0x14, 0x01, 0x1401, "F_0070", "Colin went deep into the woods", "Ordon Woods" }, + { 0x15, 0x80, 0x1580, "M_002", "[cutscene: 2] Met with Ilia (brings horse to spring)", "Kawagoe Cutscene" }, + { 0x15, 0x40, 0x1540, "F_0071", "Cannot warp to Lanayru", "Twilight Ring field" }, + { 0x15, 0x20, 0x1520, "F_0072", "Knocked down large beehive with hawk", "Ordon Village" }, + { 0x15, 0x10, 0x1510, "F_0073", "Attacked after charging at large beehive", "Ordon Village" }, + { 0x15, 0x08, 0x1508, "F_0074", "Hanch attacked by bees", "Ordon Village" }, + { 0x15, 0x04, 0x1504, "F_0075", "Angered Jaggle by destroying pumpkin", "Ordon Village" }, + { 0x15, 0x02, 0x1502, "F_0076", "Spoke to Hanch in lake", "Ordon Village" }, + { 0x15, 0x01, 0x1501, "F_0077", "First converstaion with Agetha inside", "Castle Town" }, + { 0x16, 0x80, 0x1680, "F_0078", "Hanch returned to land after jumping into lake", "Ordon Village" }, + { 0x16, 0x40, 0x1640, "F_0079", "2nd Day - successful knocked down rampaging mountain goat", "Ordon Village" }, + { 0x16, 0x20, 0x1620, "F_0080", "Completed all of mountain goat rampage event", "Ordon Village" }, + { 0x16, 0x10, 0x1610, "F_0081", "20 mountain goats rampaged", "Ordon Village" }, + { 0x16, 0x08, 0x1608, "F_0082", "Completed coversation with Bo after 20th mountain goat's rampage", "Ordon Village" }, + { 0x16, 0x04, 0x1604, "F_0083", "Deliver letter from Agetha", "Letter" }, + { 0x16, 0x02, 0x1602, "F_0084", "Opening days 2&3: knocked down a beehive with slingshot", "Ordon Village" }, + { 0x16, 0x01, 0x1601, "F_0085", "Rusl appears at woods entrance", "Ordon Village" }, + { 0x17, 0x80, 0x1780, "F_0086", "Spoke with Hanch after knocking down beehive with hawk", "Ordon Village" }, + { 0x17, 0x40, 0x1740, "F_0087", "Left search area after first conversation with Pergie", "Ordon Village" }, + { 0x17, 0x20, 0x1720, "F_0088", "Spoke to Beth after quitting sword tutorial", "Ordon Village" }, + { 0x17, 0x10, 0x1710, "F_0089", "Talked to village chief for first time", "Ordon Village" }, + { 0x17, 0x08, 0x1708, "F_0090", "F:1126 - South - Spoke with Agetha's stalker (before talking with Agetha inside)", "Castle Town" }, + { 0x17, 0x04, 0x1704, "F_0091", "F:1126 - South - Spoke with Agetha's stalker (after talking with Agetha inside)", "Ordon Village" }, + { 0x17, 0x02, 0x1702, "F_0092", "F:1127 - South - Spoke with the Hyrule soldier guide", "Castle Town" }, + { 0x17, 0x01, 0x1701, "F_0093", "F:1128 - South - Spoke with female clerk at vegetable stand", "Castle Town" }, + { 0x18, 0x80, 0x1880, "F_0094", "Talo went after the monkey", "Ordon Village" }, + { 0x18, 0x40, 0x1840, "F_0095", "Spoke to Fado before mountain goat rampage", "Ranch" }, + { 0x18, 0x20, 0x1820, "F_0096", "Have spoken to Bo with 1 health", "Ordon Village" }, + { 0x18, 0x10, 0x1810, "F_0097", "First conversation with dog eavesdrop hint", "Ordon Village" }, + { 0x18, 0x08, 0x1808, "F_0202", "Rusl / Wolf fails to get sword", "Ordon Village" }, + { 0x18, 0x04, 0x1804, "F_0203", "First tried to steal from unnmaned shop (Havent checked donation box)", "Shop" }, + { 0x18, 0x02, 0x1802, "F_0204", "Talked to Midna from across the bars in the sewer", "Inside Hyrule Castle" }, + { 0x18, 0x01, 0x1801, "F_0205", "Heard Rusl and Uli talking in Ordon village at night", "Ordon Village" }, + { 0x19, 0x80, 0x1980, "F_0206", "Barrier of darkness tag: tried to enter without getting sword and shield", "Ordon Woods" }, + { 0x19, 0x40, 0x1940, "F_0207", "Viewed Hanch, the Hawker cutscene", "Ordon Village" }, + { 0x19, 0x20, 0x1920, "F_0208", "Hanch leapt after being startled by wolf", "Ordon Village" }, + { 0x19, 0x10, 0x1910, "F_0209", "Escape after failing to eavesdrop to Bo and Jaggle", "Ordon Village" }, + { 0x19, 0x08, 0x1908, "F_0210", "F0004: Try to get on horse when Colin is stopping horse", "Ordon Village" }, + { 0x19, 0x04, 0x1904, "F_0211", "Successfully eavesdrop on Bo and Jaggle", "Ordon Village" }, + { 0x19, 0x02, 0x1902, "F_0212", "Conversation wtih Colin in front of Ordon springs gate", "Ordon Woods" }, + { 0x19, 0x01, 0x1901, "F_0213", "Called by spirit after NS fight at Kakariko entrance", "Kakariko Village" }, + { 0x1A, 0x80, 0x1A80, "F_0214", " First heard Midna's hint about Z", "Inside Hyrule Castle" }, + { 0x1A, 0x40, 0x1A40, "F_0215", "Spoke with cat on roof", "Ordon Village" }, + { 0x1A, 0x20, 0x1A20, "F_0216", "Spoke with Ordon village cucco B (white)", "Ordon Village" }, + { 0x1A, 0x10, 0x1A10, "F_0217", "First conversation after speaking with Coro again after clearing up twilight", "Faron Woods" }, + { 0x1A, 0x08, 0x1A08, "F_0218", "Bought jar of oil from Coro", "Faron Woods" }, + { 0x1A, 0x04, 0x1A04, "F_0219", "Heard Agetha say \"but you have some\"", "Castle Town" }, + { 0x1A, 0x02, 0x1A02, "F_0220", "First time - Talked to one of the Hyrule soldiers underneath castle in Twilight", "Inside Hyrule Castle" }, + { 0x1A, 0x01, 0x1A01, "F_0221", "Received vessel of light from spirit", "Kakariko Village" }, + { 0x1B, 0x80, 0x1B80, "F_0222", "Finished Coro ignite event", "Faron Woods" }, + { 0x1B, 0x40, 0x1B40, "F_0223", "Listened to Coro's dialogue before defeating shadow bugs", "Farron Woods inside woodcutter's shop" }, + { 0x1B, 0x20, 0x1B20, "F_0224", "Flag for lantern guide monkey cutscene", "Faron Woods" }, + { 0x1B, 0x10, 0x1B10, "F_0225", "Lanter guide monkey doesn't come out a second time", "Faron Woods" }, + { 0x1B, 0x08, 0x1B08, "F_0226", "Get lantern back from monkey", "Faron Woods" }, + { 0x1B, 0x04, 0x1B04, "F_0227", "Try to leave mist woods after lantern taken", "Faron Woods" }, + { 0x1B, 0x02, 0x1B02, "F_0228", "Listened to goron's complaint at the entrance to twilight mountain path", "Death Mountain" }, + { 0x1B, 0x01, 0x1B01, "F_0229", "Try to leave the mist woods area without lantern (after returning monkey)", "Faron Woods" }, + { 0x1C, 0x80, 0x1C80, "F_0230", "Twilight - listened to goron B's complaints", "Death Mountain" }, + { 0x1C, 0x40, 0x1C40, "F_0231", "Did first wrestle match with Bo", "Ordon Village" }, + { 0x1C, 0x20, 0x1C20, "F_0232", "get iron boots from Bo", "Ordon Village" }, + { 0x1C, 0x10, 0x1C10, "F_0233", "Win practice battle with Bo (before getting boots)", "Ordon Village" }, + { 0x1C, 0x08, 0x1C08, "F_234", "Attacked by Trill", "Faron Woods" }, + { 0x1C, 0x04, 0x1C04, "F_0235", "Giant game clear", "Sacred Grove" }, + { 0x1C, 0x02, 0x1C02, "F_0236", "Make Midna angry after doing wrong destination for Kakariko bridge warp", "Misc." }, + { 0x1C, 0x01, 0x1C01, "F_0237", "Entered Malo Mart (chatted with Malo)", "Kakariko Village" }, + { 0x1D, 0x80, 0x1D80, "F_0238", "Destroyed all puppets (monkey girl event before sacred grove)", "Faron Woods" }, + { 0x1D, 0x40, 0x1D40, "T_0239", "Spoke with Fyer (start dark carge)", "Lake Hylia" }, + { 0x1D, 0x20, 0x1D20, "F_0240", "Speak with Barnes while heas making bombs", "Kakariko Village" }, + { 0x1D, 0x10, 0x1D10, "F_0241", "First conversation with Barnes after bomb shop open", "Kakariko Village" }, + { 0x1D, 0x08, 0x1D08, "F_0242", "Spoke with the dazed chief goron after Fyrus battle", "LV2 Dungeon" }, + { 0x1D, 0x04, 0x1D04, "F_0243", "Speak with Luda wiping Colin's sweat", "Kakariko Village" }, + { 0x1D, 0x02, 0x1D02, "F_0244", "Speak to Talo while COlin is wiping sweat", "Kakariko Village" }, + { 0x1D, 0x01, 0x1D01, "MAP_VISIBLE", "Area map show/hide", "2D Map" }, + { 0x1E, 0x80, 0x1E80, "F_0246", "Malo Mart fundraiser (and carying spring water) start", "Kakariko Village" }, + { 0x1E, 0x40, 0x1E40, "F_0247", "First conversation with Malo (shopping complete) after helping Malo Mart fundraiser", "Kakariko Village" }, + { 0x1E, 0x20, 0x1E20, "F_0248", "Speak with Coro after clearing Forest Temple", "Faron Woods" }, + { 0x1E, 0x10, 0x1E10, "F_0249", "Try to leave after paying for Flight by Fowl (first time only)", "Lake Hylia" }, + { 0x1E, 0x08, 0x1E08, "F_0250", "[cutscene: 21] reunion with Zelda / Midna revived (Ganon wall appears)", "Kawagoe Cutscene" }, + { 0x1E, 0x04, 0x1E04, "F_0251", "Speak again with Sera before finding kids (forced conversation)", "Ordon Village" }, + { 0x1E, 0x02, 0x1E02, "F_0252", "Spoke to Sera while kids are missing (dealing with shop)", "Ordon Village" }, + { 0x1E, 0x01, 0x1E01, "F_0253", "Stuck to magnet lift at least once", "For E3 2006" }, + { 0x1F, 0x80, 0x1F80, "F_0254", "Hit boss's weak spot at least once", "For E3 2006" }, + { 0x1F, 0x40, 0x1F40, "F_0255", "Boss exhausted (grabbing chains) only on during state (normally off)", "For E3 2006" }, + { 0x1F, 0x20, 0x1F20, "F_0256", "Knocked down boss at leased once", "For E3 2006" }, + { 0x1F, 0x10, 0x1F10, "F_0257", "Only ON when boss is in hollow state (normally off, changes in real time)", "For E3 2006" }, + { 0x1F, 0x08, 0x1F08, "F_0258", "Heard hint right above heavy switch", "For E3 2006" }, + { 0x1F, 0x04, 0x1F04, "F_0259", "Heard first forced dialogue from Midna", "For E3 2006" }, + { 0x1F, 0x02, 0x1F02, "F_0260", "First time speaking to Resistance Rusl", "Castle Town" }, + { 0x1F, 0x01, 0x1F01, "F_0261", "First conversation with Resistance Auru (made fun of)", "Castle Town" }, + { 0x20, 0x80, 0x2080, "F_0262", "First conversation with Resistance Shad (made fun of)", "Castle Town" }, + { 0x20, 0x40, 0x2040, "F_0263", "First conversation with Resistance Ashei (3) (made fun of)", "Castle Town" }, + { 0x20, 0x20, 0x2020, "F_0264", "get master sword", "Kawagoe Cutscene" }, + { 0x20, 0x10, 0x2010, "F_0265", "LV4 dungeon clear", "LV4 Dungeon," }, + { 0x20, 0x08, 0x2008, "F_0266", "LV5 dungeon clear", "LV5 Dungeon" }, + { 0x20, 0x04, 0x2004, "F_0267", "LV6 dungeon clear", "LV6 Dungeon" }, + { 0x20, 0x02, 0x2002, "F_0268", "LV7 dungeon clear", "LV7 dungeon" }, + { 0x20, 0x01, 0x2001, "F_0269", "First conversation with Telma after getting master sword", "Castle Town" }, + { 0x21, 0x80, 0x2180, "F_0270", "Gave Renado's letter to Telma", "Castle Town" }, + { 0x21, 0x40, 0x2140, "F_0271", "Talked with Telma again after the other conversation after getting master sword", "Castle Town" }, + { 0x21, 0x20, 0x2120, "F_0272", "Saw Auru's location on map", "Castle Town" }, + { 0x21, 0x10, 0x2110, "F_0273", "Saw Ashei's location on map", "Castle Town" }, + { 0x21, 0x08, 0x2108, "F_0274", "Saw Rusl's location on map", "Castle Town" }, + { 0x21, 0x04, 0x2104, "F_0275", "Saw Shad's location on map", "Castle Town" }, + { 0x21, 0x02, 0x2102, "F_0276", "Heard conversation with Louise about stolen wood carving", "Castle Town" }, + { 0x21, 0x01, 0x2101, "F_0277", "Hear conversation between Telma and Ilia in Telma's shop (Twilight)", "Castle Town" }, + { 0x22, 0x80, 0x2280, "F_0278", "Received pendant from Impaz", "Hidden Village" }, + { 0x22, 0x40, 0x2240, "F_0279", "Saw cutscene about scent of kids from wooden sword", "Ring field" }, + { 0x22, 0x20, 0x2220, "F_0280", "Saw cutscene about Ilia's scent from pouch", "Ring field" }, + { 0x22, 0x10, 0x2210, "F_0281", "Malo Mart opens in Castle Town", "Shop" }, + { 0x22, 0x08, 0x2208, "F_282", "First conversation with Yeto at peak after LV5 dungeon clear", "Snowpeak mountain" }, + { 0x22, 0x04, 0x2204, "F_283", "Get wood carving", "Ring field" }, + { 0x22, 0x02, 0x2202, "F_284", "Already have a score recorded for Plumm's game", "Lake Hylia" }, + { 0x22, 0x01, 0x2201, "F_285", "First conversation with Plumm as wolf", "Lake Hylia" }, + { 0x23, 0x80, 0x2380, "F_286", "Cleared Plumm's attraction (get heart piece)", "Lake Hylia" }, + { 0x23, 0x40, 0x2340, "F_287", "Handed wood carving to Ilia", "Kakariko Village" }, + { 0x23, 0x20, 0x2320, "F_288", "[cutscene: ] Ilia gets her memories back", "Kawagoe Cutscene" }, + { 0x23, 0x10, 0x2310, "F_289", "Heard conversation about entering double clawshot game LV1", "Castle Town" }, + { 0x23, 0x08, 0x2308, "F_290", "Double clawshot game LV1 cleared", "Castle Town" }, + { 0x23, 0x04, 0x2304, "M_097", "Can use magic", "main event" }, + { 0x23, 0x02, 0x2302, "F_0292", "Star Game 2 first experience", "Castle Town" }, + { 0x23, 0x01, 0x2301, "F_0293", "Star Game 2 cleared", "Castle Town" }, + { 0x24, 0x80, 0x2480, "F_0294", "Shaman - Prayers reached the heavens (heart piece obtained complete)", "Castle Town" }, + { 0x24, 0x40, 0x2440, "F_0295", "Watched meeting Louise event when sneaking into Telma's shop (wolf)", "Castle Town" }, + { 0x24, 0x20, 0x2420, "F_0296", "Spoke with Louise after kicked being kicked out of Telma's shop", "Castle Town" }, + { 0x24, 0x10, 0x2410, "F_0297", "First spoke with Chudley store clerk", "Castle Town" }, + { 0x24, 0x08, 0x2408, "F_0298", "Spoke with Auru after completing LV4 dungeon", "Castle Town" }, + { 0x24, 0x04, 0x2404, "F_0299", "Spoke with Shad at Telma's shop after getting master sword", "Castle Town" }, + { 0x24, 0x02, 0x2402, "F_0300", "Heard about Sky People from Shad at Telma's shop", "Castle Town" }, + { 0x24, 0x01, 0x2401, "F_0301", "Spoke with Shad after LV7 dungeon clear", "Castle Town" }, + { 0x25, 0x80, 0x2580, "F_0302", "Saw cutscene of Shad casting spells underneat Kakariko Village", "Kakariko Village" }, + { 0x25, 0x40, 0x2540, "F_0303", "Saw Shad's spell 2", "Kakariko Village" }, + { 0x25, 0x20, 0x2520, "F_0304", "Spoke with Auru at Hylia Lake", "Lake Hylia" }, + { 0x25, 0x10, 0x2510, "F_0305", "Heard about Fyer from Auru (desert cannon ON)", "Lake Hylia" }, + { 0x25, 0x08, 0x2508, "F_0306", "Used Fyer's cannon to go to desert", "Desert" }, + { 0x25, 0x04, 0x2504, "F_0307", "Spoke with Yeto at top of mountain as wolf", "Snowpeak mountain" }, + { 0x25, 0x02, 0x2502, "F_0308", "Watched first meeting event with Yeto at top of mountain (human)", "Snowpeak mountain" }, + { 0x25, 0x01, 0x2501, "F_0309", "First conversation with Agetha inside (gives golden bug capture quest)", "Castle Town" }, + { 0x26, 0x80, 0x2680, "F_0310", "Hand Auru's note to Fyer", "Lake Hylia" }, + { 0x26, 0x40, 0x2640, "F_0311", "LV8 Dungeon use 1", "LV8 Dungeon" }, + { 0x26, 0x20, 0x2620, "F_0312", "LV8 Dungeon use 2", "LV8 Dungeon" }, + { 0x26, 0x10, 0x2610, "F_0313", "LV8 Dungeon use 3", "LV8 Dungeon" }, + { 0x26, 0x08, 0x2608, "F_0314", "LV8 Dungeon use 4", "LV8 Dungeon" }, + { 0x26, 0x04, 0x2604, "F_0315", "LV8 Dungeon use 5", "LV8 Dungeon" }, + { 0x26, 0x02, 0x2602, "F_0316", "LV8 Dungeon use 6", "LV8 Dungeon" }, + { 0x26, 0x01, 0x2601, "F_0317", "LV8 Dungeon use 7", "LV8 Dungeon" }, + { 0x27, 0x80, 0x2780, "F_0318", "LV8 Dungeon use 8", "LV8 Dungeon" }, + { 0x27, 0x40, 0x2740, "F_0319", "LV8 Dungeon use 9", "LV8 Dungeon" }, + { 0x27, 0x20, 0x2720, "F_0320", "LV8 Dungeon use 10", "LV8 Dungeon" }, + { 0x27, 0x10, 0x2710, "F_0321", "Showed reciept to town doctor", "Castle Town" }, + { 0x27, 0x08, 0x2708, "F_0322", "Flow 0010 Spoke with Zora in Zora armor", "Zora's Domain" }, + { 0x27, 0x04, 0x2704, "F_0323", "Flow 0020 Spoke with Zora", "Zora's Domain" }, + { 0x27, 0x02, 0x2702, "F_0324", "Flow 0023 Speak with Zora before blowing up magma rock", "Zora's Domain" }, + { 0x27, 0x01, 0x2701, "F_0325", "Flow 0023 Speak with Zora after blowing up magma rock", "Zora's Domain" }, + { 0x28, 0x80, 0x2880, "F_0326", "LV8 mini-boss defeated flag", "LV8 Dungeon" }, + { 0x28, 0x40, 0x2840, "F_0327", "First time warped by Ooccoo Sr.", "Misc." }, + { 0x28, 0x20, 0x2820, "F_0328", "Talk again with Jaggle after finding kids", "Ordon Village" }, + { 0x28, 0x10, 0x2810, "F_0329", "Jaggle brings up shield", "Ordon Village" }, + { 0x28, 0x08, 0x2808, "F_0330", "Meet again with Uli for the first time (first forced conversation)", "Ordon Village" }, + { 0x28, 0x04, 0x2804, "F_0331", "Meet again and talk with Uli after finding kids (1st time)", "Ordon Village" }, + { 0x28, 0x02, 0x2802, "F_0332", "Meet again and talk with Uli after finding kids (2nd time)", "Ordon Village" }, + { 0x28, 0x01, 0x2801, "F_0333", "Uli brings up the sword", "Ordon Village" }, + { 0x29, 0x80, 0x2980, "F_0334", "Spoke with Ashei at Telma's shop after getting master sword", "Castle Town" }, + { 0x29, 0x40, 0x2940, "F_0335", "Obtained scribble from Ashei at mountain pass", "Snowpeak mountain" }, + { 0x29, 0x20, 0x2920, "F_0336", "Blew up molten rock bomb ", "Zora's Domain" }, + { 0x29, 0x10, 0x2910, "F_0337", "Listened to old ladies A/B conversation (wolf / Ralis hint FLOW104)", "Castle Town" }, + { 0x29, 0x08, 0x2908, "F_0338", "Obtained 1 secret techinques - Shield attack", "Secret techniques" }, + { 0x29, 0x04, 0x2904, "F_0339", "Obtained 2 secret techinques", "Secret techniques" }, + { 0x29, 0x02, 0x2902, "F_0340", "Obtained 3 secret techinques", "Secret techniques" }, + { 0x29, 0x01, 0x2901, "F_0341", "Obtained 4 secret techinques", "Secret techniques" }, + { 0x2A, 0x80, 0x2A80, "F_0342", "Obtained 5 secret techinques", "Secret techniques" }, + { 0x2A, 0x40, 0x2A40, "F_0343", "Obtained 6 secret techinques", "Secret techniques" }, + { 0x2A, 0x20, 0x2A20, "F_0344", "Obtained 7 secret techinques", "Secret techniques" }, + { 0x2A, 0x10, 0x2A10, "F_0345", "Opening 2nd day - lit Coro's pot", "Faron Woods" }, + { 0x2A, 0x08, 0x2A08, "F_0346", "Turned down Auru's request to go to the desert", "Lake Hylia" }, + { 0x2A, 0x04, 0x2A04, "F_0347", "Spoke with Ashei after LV5 dungeon clear ", "Castle Town" }, + { 0x2A, 0x02, 0x2A02, "F_0348", "Spoke with Zora soldier in front of LV3 dungeon (before bombing entrance)", "Lake Hylia" }, + { 0x2A, 0x01, 0x2A01, "F_0349", "Spoke with Zora soldier in front of LV3 dungeon (after bombing entrance)", "Lake Hylia" }, + { 0x2B, 0x80, 0x2B80, "F_0350", "Showed wood carving doll to Renado", "Kakariko Village" }, + { 0x2B, 0x40, 0x2B40, "F_0351", "First meeting with Agether outside (haven't met inside yet) : gives bug even quest", "Ring field" }, + { 0x2B, 0x20, 0x2B20, "F_0352", "Second conversation with Agetha (outside)", "Ring field" }, + { 0x2B, 0x10, 0x2B10, "F_0353", "Conversation with lady complaining about Chudley's shop", "Castle Town" }, + { 0x2B, 0x08, 0x2B08, "F_0354", "[cutscene] Mirror complete", "Kawagoe Cutscene" }, + { 0x2B, 0x04, 0x2B04, "F_0355", "Showed wood carving to village doctor", "Castle Town" }, + { 0x2B, 0x02, 0x2B02, "F_0356", "Spoke with village doctor after Ilia's memories restored", "Castle Town" }, + { 0x2B, 0x01, 0x2B01, "F_0357", "Spoke with Telma's guardian goron after she came back", "Death Mountain" }, + { 0x2C, 0x80, 0x2C80, "F_0358", "Spoke with Telma after Ilia's memories restored", "Castle Town" }, + { 0x2C, 0x40, 0x2C40, "F_0359", "Spoke with Ilia while she's watching over Ralis", "Kakariko Village" }, + { 0x2C, 0x20, 0x2C20, "F_0360", "Conversation with fortune teller as wolf", "Castle Town" }, + { 0x2C, 0x10, 0x2C10, "F_0361", "Spun the spinning pillars", "LV4 Dungeon," }, + { 0x2C, 0x08, 0x2C08, "F_0362", "Properly spoke with Resistance Rusl again (2nd time)", "Castle Town" }, + { 0x2C, 0x04, 0x2C04, "F_0363", "Stole sword from Rusl in Ordon Village at night (wolf)", "Ordon Village" }, + { 0x2C, 0x02, 0x2C02, "F_0364", "Listened to monkey girl's laments (Twilight)", "Faron Woods" }, + { 0x2C, 0x01, 0x2C01, "F_0365", "Spoke with Gor Liggs after LV dungeon clear", "Death Mountain (room)" }, + { 0x2D, 0x80, 0x2D80, "F_0366", "Spoke with Gor Liggs after Ilia's memories restored", "Death Mountain (room)" }, + { 0x2D, 0x40, 0x2D40, "F_0367", "Spoke with goron blocking mountain path after LV2 dungeon clear", "Death Mountain" }, + { 0x2D, 0x20, 0x2D20, "F_0368", "First conversation wtih goron in front of hotel", "Kakariko Village" }, + { 0x2D, 0x10, 0x2D10, "F_0369", "First conversation with goron in front of watch tower", "Kakariko Village" }, + { 0x2D, 0x08, 0x2D08, "F_0370", "First converstaion with Karakiko springs goron (adult)", "Kakariko Village" }, + { 0x2D, 0x04, 0x2D04, "F_0371", "First conversation wtih goron in front of bomb shop (while only selling bombs)", "Kakariko Village" }, + { 0x2D, 0x02, 0x2D02, "F_0372", "Tried to by milk before saving cat", "Ordon Village" }, + { 0x2D, 0x01, 0x2D01, "F_0373", "Coversation with Darbus after Ilia's memories restored", "Death Mountain" }, + { 0x2E, 0x80, 0x2E80, "F_0374", "Conversation with Darbus in wrestling room after LV2 dungeon clear", "Death Mountain" }, + { 0x2E, 0x40, 0x2E40, "F_0375", "First conversation with fundraising goron at Malo Mart", "Kakariko Village" }, + { 0x2E, 0x20, 0x2E20, "F_0376", "Gathered funds for bridge repair! (set by program after raising funds)", "Kakariko Village" }, + { 0x2E, 0x10, 0x2E10, "F_0377", "Goron spring water shop open!", "Castle Town" }, + { 0x2E, 0x08, 0x2E08, "F_0378", "Darbus destroyed hidden village boulder", "Ring field" }, + { 0x2E, 0x04, 0x2E04, "F_0379", "Completed golden bugs", "N/A" }, + { 0x2E, 0x02, 0x2E02, "F_0380", "Complete first meeting with Agetha (Recieved bug collection quest)", "N/A" }, + { 0x2E, 0x01, 0x2E01, "F_0381", "First conversation with parent goron after spring water shop opens", "Castle Town" }, + { 0x2F, 0x80, 0x2F80, "F_0382", "Listened to laments of fallen goron (while bridge broken)", "Castle Town" }, + { 0x2F, 0x40, 0x2F40, "F_0383", "Spoke with child goron after spring water shop opens", "Castle Town" }, + { 0x2F, 0x20, 0x2F20, "F_0384", "Spring water shop - Spoke with child goron while shop closed", "Castle Town" }, + { 0x2F, 0x10, 0x2F10, "F_0385", "First conversation with Barnes after stocking water bombs", "Kakariko Village" }, + { 0x2F, 0x08, 0x2F08, "F_0386", "Conversation with Barnes after he starts selling Bomblings and landmines", "Kakariko Village" }, + { 0x2F, 0x04, 0x2F04, "F_0387", "Learned scent of medicine", "Castle Town" }, + { 0x2F, 0x02, 0x2F02, "F_0388", "Graveyard - Spoke with Ralis after changing scene (before obtaining earrings)", "Kakariko Village" }, + { 0x2F, 0x01, 0x2F01, "F_0389", "First conversation with goron digging hole in south", "Ring field" }, + { 0x30, 0x80, 0x3080, "F_0390", "First conversation after saving Jovani", "Castle Town" }, + { 0x30, 0x40, 0x3040, "F_0391", "Gave spring water to goron south of castle town", "Ring field" }, + { 0x30, 0x20, 0x3020, "F_0392", "spoke with goron digging hole after opening caslt town south road", "Ring field" }, + { 0x30, 0x10, 0x3010, "F_0393", "First conversation at Poe shop (generic Poe appearance)", "Castle Town" }, + { 0x30, 0x08, 0x3008, "F_0394", "Received first key from chibi elder (204)", "LV2 Dungeon" }, + { 0x30, 0x04, 0x3004, "F_0395", "First conversation with Gor Liggs in Kakariko Village", "Kakariko Village" }, + { 0x30, 0x02, 0x3002, "F_0396", "Failed to carry hot spring water (speak with elder to reset)", "Kakariko Village" }, + { 0x30, 0x01, 0x3001, "F_0397", "Gor Liggs conversation sequence B", "Kakariko Village" }, + { 0x31, 0x80, 0x3180, "F_0398", "Gor Liggs conversation sequence C", "Kakariko Village" }, + { 0x31, 0x40, 0x3140, "F_0399", "Conversation with Gor Liggs - hot spring game available", "Kakariko Village" }, + { 0x31, 0x20, 0x3120, "F_0400", "Warped sky cannon to Lake Hylia", "Kakariko Village" }, + { 0x31, 0x10, 0x3110, "F_0401", "Beetle (M)", "Golden bugs" }, + { 0x31, 0x08, 0x3108, "F_0402", "Beetle (F)", "Golden bugs" }, + { 0x31, 0x04, 0x3104, "F_0403", "Butterfly (M)", "Golden bugs" }, + { 0x31, 0x02, 0x3102, "F_0404", "Butterfly (F)", "Golden bugs" }, + { 0x31, 0x01, 0x3101, "F_0405", "Stag beetle (M)", "Golden bugs" }, + { 0x32, 0x80, 0x3280, "F_0406", "Stag beetle (F)", "Golden bugs" }, + { 0x32, 0x40, 0x3240, "F_0407", "Grasshopper (M)", "Golden bugs" }, + { 0x32, 0x20, 0x3220, "F_0408", "Grasshopper (F)", "Golden bugs" }, + { 0x32, 0x10, 0x3210, "F_0409", "Phasmid (M)", "Golden bugs" }, + { 0x32, 0x08, 0x3208, "F_0410", "Phasmid (F)", "Golden bugs" }, + { 0x32, 0x04, 0x3204, "F_0411", "Pill bug (M)", "Golden bugs" }, + { 0x32, 0x02, 0x3202, "F_0412", "Pill bug (F)", "Golden bugs" }, + { 0x32, 0x01, 0x3201, "F_0413", "Mantis (M)", "Golden bugs" }, + { 0x33, 0x80, 0x3380, "F_0414", "Mantis (F)", "Golden bugs" }, + { 0x33, 0x40, 0x3340, "F_0415", "Ladybug (M)", "Golden bugs" }, + { 0x33, 0x20, 0x3320, "F_0416", "Ladybug (F)", "Golden bugs" }, + { 0x33, 0x10, 0x3310, "F_0417", "Snail (M)", "Golden bugs" }, + { 0x33, 0x08, 0x3308, "F_0418", "Snail (F)", "Golden bugs" }, + { 0x33, 0x04, 0x3304, "F_0419", "Dragonfly (M)", "Golden bugs" }, + { 0x33, 0x02, 0x3302, "F_0420", "Dragonfly (F)", "Golden bugs" }, + { 0x33, 0x01, 0x3301, "F_0421", "Ant (M)", "Golden bugs" }, + { 0x34, 0x80, 0x3480, "F_0422", "Ant (F)", "Golden bugs" }, + { 0x34, 0x40, 0x3440, "F_0423", "Dayfly (M)", "Golden bugs" }, + { 0x34, 0x20, 0x3420, "F_0424", "Dayfly (F)", "Golden bugs" }, + { 0x34, 0x10, 0x3410, "F_0425", "[Captured in bottle] Beetle (M)", "Golden bugs" }, + { 0x34, 0x08, 0x3408, "F_0426", "[Captured in bottle] Beetle (F)", "Golden bugs" }, + { 0x34, 0x04, 0x3404, "F_0427", "[Captured in bottle] Butterfly (M)", "Golden bugs" }, + { 0x34, 0x02, 0x3402, "F_0428", "[Captured in bottle] Butterfly (F)", "Golden bugs" }, + { 0x34, 0x01, 0x3401, "F_0429", "[Captured in bottle] Stag beetle (M)", "Golden bugs" }, + { 0x35, 0x80, 0x3580, "F_0430", "[Captured in bottle] Stag beetle (F)", "Golden bugs" }, + { 0x35, 0x40, 0x3540, "F_0431", "[Captured in bottle] Grasshopper (M)", "Golden bugs" }, + { 0x35, 0x20, 0x3520, "F_0432", "[Captured in bottle] Grasshopper (F)", "Golden bugs" }, + { 0x35, 0x10, 0x3510, "F_0433", "[Captured in bottle] Phasmid (M)", "Golden bugs" }, + { 0x35, 0x08, 0x3508, "F_0434", "[Captured in bottle] Phasmid (F)", "Golden bugs" }, + { 0x35, 0x04, 0x3504, "F_0435", "[Captured in bottle] Pill bug (M)", "Golden bugs" }, + { 0x35, 0x02, 0x3502, "F_0436", "[Captured in bottle] Pill bug (F)", "Golden bugs" }, + { 0x35, 0x01, 0x3501, "F_0437", "[Captured in bottle] Mantis (M)", "Golden bugs" }, + { 0x36, 0x80, 0x3680, "F_0438", "[Captured in bottle] Mantis (F)", "Golden bugs" }, + { 0x36, 0x40, 0x3640, "F_0439", "[Captured in bottle] Ladybug (M)", "Golden bugs" }, + { 0x36, 0x20, 0x3620, "F_0440", "[Captured in bottle] Ladybug (F)", "Golden bugs" }, + { 0x36, 0x10, 0x3610, "F_0441", "[Captured in bottle] Snail (M)", "Golden bugs" }, + { 0x36, 0x08, 0x3608, "F_0442", "[Captured in bottle] Snail (F)", "Golden bugs" }, + { 0x36, 0x04, 0x3604, "F_0443", "[Captured in bottle] Dragonfly (M)", "Golden bugs" }, + { 0x36, 0x02, 0x3602, "F_0444", "[Captured in bottle] Dragonfly (F)", "Golden bugs" }, + { 0x36, 0x01, 0x3601, "F_0445", "[Captured in bottle] Ant (M)", "Golden bugs" }, + { 0x37, 0x80, 0x3780, "F_0446", "[Captured in bottle] Ant (F)", "Golden bugs" }, + { 0x37, 0x40, 0x3740, "F_0447", "[Captured in bottle] Dayfly (M)", "Golden bugs" }, + { 0x37, 0x20, 0x3720, "F_0448", "[Captured in bottle] Dayfly (F)", "Golden bugs" }, + { 0x37, 0x10, 0x3710, "F_0449", "Talk with Gor Liggs again - first time after talking iwth Hot spring water parent goron (before fundraising)", "Kakariko Village" }, + { 0x37, 0x08, 0x3708, "F_0450", "Double Clawshot shop final stage first conversation", "Castle Town" }, + { 0x37, 0x04, 0x3704, "F_0453", "Postman first appears", "Ring field" }, + { 0x37, 0x02, 0x3702, "F_0454", "Recieved 3rd key from fundraising elder (206)", "LV2 Dungeon" }, + { 0x37, 0x01, 0x3701, "F_0455", "Recieved 3rd key from fundraising elder (205)", "LV2 Dungeon" }, + { 0x38, 0x80, 0x3880, "F_0456", "First time meeting with Jovani", "Castle Town" }, + { 0x38, 0x40, 0x3840, "F_0457", "Revived cat", "Castle Town" }, + { 0x38, 0x20, 0x3820, "F_0458", "Coversation with Jovani after collecting 60 ghosts", "Castle Town" }, + { 0x38, 0x10, 0x3810, "F_0459", "Coversation with Gengle after collecting 40 ghosts", "Castle Town" }, + { 0x38, 0x08, 0x3808, "F_0460", "Coversation with Gengle after collecting 50 ghosts", "Castle Town" }, + { 0x38, 0x04, 0x3804, "F_0461", "First time entered fishing house", "Fishing" }, + { 0x38, 0x02, 0x3802, "F_0462", "Reserved for fishing", "Fishing" }, + { 0x38, 0x01, 0x3801, "F_0463", "Reserved for fishing", "Fishing" }, + { 0x39, 0x80, 0x3980, "F_0464", "Reserved for fishing", "Fishing" }, + { 0x39, 0x40, 0x3940, "F_0465", "Reserved for fishing", "Fishing" }, + { 0x39, 0x20, 0x3920, "F_0466", "Reserved for fishing", "Fishing" }, + //{ 0x39, 0x10, 0x3910, "F_0467", "N/A", "Fishing" }, + { 0x39, 0x08, 0x3908, "F_0468", "Reserved for fishing", "Fishing" }, + { 0x39, 0x04, 0x3904, "F_0469", "Reserved for fishing", "Fishing" }, + { 0x39, 0x02, 0x3902, "F_0470", "Reserved for fishing", "Fishing" }, + { 0x39, 0x01, 0x3901, "F_0471", "Finished 2 wrestling matches against Bo", "Ordon Village" }, + { 0x3A, 0x80, 0x3A80, "F_0472", "Distant howling complete (for secret technique 2)", "Secret techniques" }, + { 0x3A, 0x40, 0x3A40, "F_0473", "Distant howling complete (for secret technique 3)", "Secret techniques" }, + { 0x3A, 0x20, 0x3A20, "F_0474", "Distant howling complete (for secret technique 4)", "Secret techniques" }, + { 0x3A, 0x10, 0x3A10, "F_0475", "Distant howling complete (for secret technique 5)", "Secret techniques" }, + { 0x3A, 0x08, 0x3A08, "F_0476", "Distant howling complete (for secret technique 6)", "Secret techniques" }, + { 0x3A, 0x04, 0x3A04, "F_0477", "Distant howling complete (for secret technique 7)", "Secret techniques" }, + { 0x3A, 0x02, 0x3A02, "F_0478", "Spoke to Ralis who returned to Zora's domain", "Zora's Domain" }, + { 0x3A, 0x01, 0x3A01, "F_0479", "First conversation with Ralis in front of grave", "Kakariko Village" }, + { 0x3B, 0x80, 0x3B80, "F_0480", "Received Coral Earrings from Ralis", "Kakariko Village" }, + { 0x3B, 0x40, 0x3B40, "F_0481", "Beat Yeta at snowboard first time", "Snowpeak mountain" }, + { 0x3B, 0x20, 0x3B20, "F_0482", "After beating Yeto, first conversation with Yeta at peack (challanged to a match)", "N/A" }, + { 0x3B, 0x10, 0x3B10, "F_0483", "First beat Yeta at snowboard (heart piece)", "Snowpeak mountain" }, + { 0x3B, 0x08, 0x3B08, "F_0484", "Completed sky canon repairs!", "Lake Hylia" }, + { 0x3B, 0x04, 0x3B04, "F_0485", "Heard Fyer talk about repairs 1 time", "Lake Hylia" }, + { 0x3B, 0x02, 0x3B02, "F_0486", "Spoke with Luda while Ralis is visiting graveyard", "Kakariko Village" }, + { 0x3B, 0x01, 0x3B01, "F_0487", "Spoke with Luda after Ralis returns home", "Kakariko Village" }, + { 0x3C, 0x80, 0x3C80, "F_0488", "Conversation with goron in front of bomb shop - underwater bomb appears", "Kakariko Village" }, + { 0x3C, 0x40, 0x3C40, "F_0489", "Conversation with goron in front of bomb shop - all bomb types appear", "Kakariko Village" }, + { 0x3C, 0x20, 0x3C20, "F_0490", "Spoke with cucco (thinks Link will eat him)", "Ordon Village" }, + { 0x3C, 0x10, 0x3C10, "F_0491", "Spoke with mini-boss (magnet goron) after LV2 dungeon clear", "LV2 Dungeon" }, + { 0x3C, 0x08, 0x3C08, "F_0492", "Gold wolf disappearance 2", "Secret techniques" }, + { 0x3C, 0x04, 0x3C04, "F_0493", "Gold wolf disappearance 3", "Secret techniques" }, + { 0x3C, 0x02, 0x3C02, "F_0494", "Gold wolf disappearance 4", "Secret techniques" }, + { 0x3C, 0x01, 0x3C01, "F_0495", "Gold wolf disappearance 5", "Secret techniques" }, + { 0x3D, 0x80, 0x3D80, "F_0496", "Gold wolf disappearance 6", "Secret techniques" }, + { 0x3D, 0x40, 0x3D40, "F_0497", "Gold wolf disappearance 7", "Secret techniques" }, + { 0x3D, 0x20, 0x3D20, "F_0498", "Spoke with Beth after Lalis is revived", "Kakariko Village" }, + { 0x3D, 0x10, 0x3D10, "F_0499", "Saved magma goron", "Zora's Domain" }, + { 0x3D, 0x08, 0x3D08, "F_0500", "Met Uli before finding kids (look at sleeping Rusl)", "Ordon Village" }, + { 0x3D, 0x04, 0x3D04, "F_0501", "Cave of Ordeals - B10 first arrival", "sub-dungeon" }, + { 0x3D, 0x02, 0x3D02, "F_0502", "Cave of Ordeals - B20 first arrival", "sub-dungeon" }, + { 0x3D, 0x01, 0x3D01, "F_0503", "Cave of Ordeals - B30 first arrival", "sub-dungeon" }, + { 0x3E, 0x80, 0x3E80, "F_0504", "Cave of Ordeals - B40 first arrival", "sub-dungeon" }, + { 0x3E, 0x40, 0x3E40, "F_0505", "Cave of Ordeals - B50 first arrival (clear)", "sub-dungeon" }, + { 0x3E, 0x20, 0x3E20, "F_0506", "Only met Ooccoo Sr. - not yet son (shared LV1-LV5)", "N/A" }, + { 0x3E, 0x10, 0x3E10, "F_0507", "Also met the son in dungeon with first Ooccoo Sr. meeting - turns 506 OFF (shared LV1-LV5)", "N/A" }, + { 0x3E, 0x08, 0x3E08, "F_0508", "Meb Ooccoo Sr. B - doesnt turn OFF (shared LV1-LV5)", "Ooccoo Sr. stuff" }, + { 0x3E, 0x04, 0x3E04, "F_0509", "Met Ooccoo Sr. second time", "Ooccoo Sr. stuff" }, + { 0x3E, 0x02, 0x3E02, "F_0510", "Watched LV7 dungion start cutscene", "LV7 dungeon" }, + { 0x3E, 0x01, 0x3E01, "F_0513", "Spoke with Colin when neither Ilia nor Ralis are doing well", "Kakariko Village" }, + { 0x3F, 0x80, 0x3F80, "F_0514", "Spoke with Colin after Ralis is revived but before Ilia is revived", "Kakariko Village" }, + { 0x3F, 0x40, 0x3F40, "F_0515", "Spoke with Colin after Ilia and Ralis are revived", "Kakariko Village" }, + { 0x3F, 0x20, 0x3F20, "F_0516", "Told Uli directly about having found kids", "Ordon Village" }, + { 0x3F, 0x10, 0x3F10, "F_0517", "Had normal conversation 1 with Uli after finding kids (before Colin kidnapped)", "Ordon Village" }, + { 0x3F, 0x08, 0x3F08, "F_0518", "Colin kidnapped ~ Heard about giving letter to Colin from sleeping Uli", "Ordon Village" }, + { 0x3F, 0x04, 0x3F04, "F_0519", "Spoke with Juggle after finding children", "Ordon Village" }, + { 0x3F, 0x02, 0x3F02, "F_0520", "Spoke with Sera after finding children", "Ordon Village" }, + { 0x3F, 0x01, 0x3F01, "F_0521", "Spoke with Sera across the counter after finding children (her script is shortened after this)", "Ordon Village" }, + { 0x40, 0x80, 0x4080, "F_0522", "Heard about Sacred Grove from saved monkey girl", "Faron Woods" }, + { 0x40, 0x40, 0x4040, "F_0523", "Spoke with Hanch before finding children", "Ordon Village" }, + { 0x40, 0x20, 0x4020, "F_0524", "Spoke with Hanch after finding children", "Ordon Village" }, + { 0x40, 0x10, 0x4010, "F_0525", "First conversation with underwater Zora bomb seller", "Lake Hylia" }, + { 0x40, 0x08, 0x4008, "F_0526", "[Cutscene: 23] Midna reveals her true form", "Kawagoe Cutscene" }, + { 0x40, 0x04, 0x4004, "F_0527", "Refused Resistance Rusl's request", "Faron Woods" }, + { 0x40, 0x02, 0x4002, "F_0528", "Resistance Rusl summoned golden cucco", "Faron Woods" }, + { 0x40, 0x01, 0x4001, "F_0529", "Spoke with Rusl after clearing LV6 dungion", "Castle Town" }, + { 0x41, 0x80, 0x4180, "F_0530", "Spoke with Pergie after finding children", "Ordon Village" }, + { 0x41, 0x40, 0x4140, "F_0531", "Spoke with Pergie while children are kidnapped", "Ordon Village" }, + { 0x41, 0x20, 0x4120, "F_0532", "Pergie butts in about a shield", "Ordon Village" }, + { 0x41, 0x10, 0x4110, "F_0533", "Spoke with Fado after finding children", "Ordon Village" }, + { 0x41, 0x08, 0x4108, "F_0534", "Spoke with Fado before finding children", "Ordon Village" }, + { 0x41, 0x04, 0x4104, "F_0535", "Spoke with goron in water after recieving reward (bomb bag)", "Zora's Domain" }, + { 0x41, 0x02, 0x4102, "F_0536", "Spoke with goron in water before recieving reward", "Zora's Domain" }, + { 0x41, 0x01, 0x4101, "F_0537", "Had Coro and wolf conversation", "Faron Woods" }, + { 0x42, 0x80, 0x4280, "F_0538", "Spoke with sky person Oocoo Sr. after clearing LV7 dungeon", "LV7 dungeon" }, + { 0x42, 0x40, 0x4240, "F_0539", "Receieved heart piece from Fado for mountain goat chase", "Ordon Village" }, + { 0x42, 0x20, 0x4220, "F_0540", "Heard hint from Midna right after first portal warp", "Faron Woods" }, + //{ 0x42, 0x10, 0x4210, "F_0541", "N/A", "N/A" }, + { 0x42, 0x08, 0x4208, "F_0542", "[Cutscene] Ganon wall disappears (Midna goes crazy)", "Kawagoe Cutscene" }, + { 0x42, 0x04, 0x4204, "F_0543", "Met Resistance Rusl again in the woods", "Faron Woods" }, + { 0x42, 0x02, 0x4202, "F_0544", "Watched cutscene of monkey girl running away after being attacked by puppet", "Faron Woods" }, + { 0x42, 0x01, 0x4201, "F_0545", "Watched cutscene of Ooccoo Sr. parting (after LV6)", "Sacred Grove" }, + { 0x43, 0x80, 0x4380, "F_0546", "East - Spoke with soldier in front of east gate about light (while bridge is broken)", "Castle Town" }, + { 0x43, 0x40, 0x4340, "F_0547", "conversation with Jovani after collecting 40 ghosts", "Castle Town" }, + { 0x43, 0x20, 0x4320, "F_0548", "Opening 3rd day - spoke with Jaggle", "Ordon Village" }, + { 0x43, 0x10, 0x4310, "F_0549", "Heard old lady A, B, soldier set (talking about water) (when there's no water)", "Castle Town" }, + { 0x43, 0x08, 0x4308, "F_0550", "Gain ability to use sense", "main event" }, + { 0x43, 0x04, 0x4304, "F_0551", "LV8 Dungeon control use 1", "LV8 Dungeon" }, + { 0x43, 0x02, 0x4302, "F_0552", "LV8 Dungeon control use 2", "LV8 Dungeon" }, + { 0x43, 0x01, 0x4301, "F_0553", "LV8 Dungeon control use 3", "LV8 Dungeon" }, + { 0x44, 0x80, 0x4480, "F_0554", "LV8 Dungeon control use 4", "LV8 Dungeon" }, + { 0x44, 0x40, 0x4440, "F_0555", "LV8 Dungeon control use 5", "LV8 Dungeon" }, + { 0x44, 0x20, 0x4420, "F_0556", "LV8 Dungeon control use 6", "LV8 Dungeon" }, + { 0x44, 0x10, 0x4410, "F_0557", "LV8 Dungeon control use 7", "LV8 Dungeon" }, + { 0x44, 0x08, 0x4408, "F_0558", "LV8 Dungeon control use 8", "LV8 Dungeon" }, + { 0x44, 0x04, 0x4404, "F_0559", "LV8 Dungeon control use 9", "LV8 Dungeon" }, + { 0x44, 0x02, 0x4402, "F_0560", "LV8 Dungeon control use 10", "LV8 Dungeon" }, + { 0x44, 0x01, 0x4401, "F_0561", "LV8 Dungeon control use 11", "LV8 Dungeon" }, + { 0x45, 0x80, 0x4580, "F_0562", "LV8 Dungeon control use 12", "LV8 Dungeon" }, + { 0x45, 0x40, 0x4540, "F_0563", "Brought back Gengle (after getting 50 souls, dealt with by program)", "Misc." }, + { 0x45, 0x20, 0x4520, "F_0564", "Heard story from solder at Telma's shop (during Twilight)", "Castle Town" }, + { 0x45, 0x10, 0x4510, "F_0565", "2nd Day Complete", "N/A" }, + { 0x45, 0x08, 0x4508, "F_0566", "Letter from Ooccoo Sr. came in OK (no son)", "Ooccoo Sr. stuff" }, + { 0x45, 0x04, 0x4504, "F_0567", "Letter from Ooccoo Sr. came in OK (yes son)", "Ooccoo Sr. stuff" }, + { 0x45, 0x02, 0x4502, "F_0568", "Broke Iza's pots (first offence)", "Zora's River" }, + { 0x45, 0x01, 0x4501, "F_0569", "Compensation demands from Iza (resets if paid)", "Zora's River" }, + { 0x46, 0x80, 0x4680, "F_0570", "Cleared LV8 dungeon", "LV8 Dungeon" }, + { 0x46, 0x40, 0x4640, "F_0571", "Refuse Talo's request (for wooden sword) on 3rd day (first time)", "Ordon Village" }, + { 0x46, 0x20, 0x4620, "F_0572", "Refuse Talo's request (for wooden sword) on 3rd day (again)", "Ordon Village" }, + { 0x46, 0x10, 0x4610, "F_0573", "1st Day - Fado moves to the farm (disappears from in front of Link's house)", "Ordon Village" }, + { 0x46, 0x08, 0x4608, "F_0574", "1st Day - Start mountain goat chasing in evening", "Ranch" }, + { 0x46, 0x04, 0x4604, "F_0575", "1st Day - End mountain goat chasing in evening", "Ranch" }, + { 0x46, 0x02, 0x4602, "F_0576", "2nd Day - Spoke with Fado", "Ordon Village" }, + { 0x46, 0x01, 0x4601, "F_0577", "2nd Day - Retrieved basket from monkey (hit hawk)", "Ordon Village" }, + { 0x47, 0x80, 0x4780, "F_0578", "Spoke to Beth on 3rd day", "Ordon Village" }, + { 0x47, 0x40, 0x4740, "F_0579", "Opening (2nd day) First time talking with Hanch before being attacked by bees", "Ordon Village" }, + { 0x47, 0x20, 0x4720, "F_0580", "1st Day - Blew on Epona's reed whistle at Ordon Spring", "Ordon Village" }, + { 0x47, 0x10, 0x4710, "F_0581", "2nd Day - First time calling the hawk using the grass whistle", "Ordon Village" }, + { 0x47, 0x08, 0x4708, "F_0582", "1st Day - spoke to Ilia before blowing the reed whistle", "Ordon Woods" }, + { 0x47, 0x04, 0x4704, "F_0583", "1st day - Spoke to Ilia after blowing the reed whistle", "Ordon Woods" }, + { 0x47, 0x02, 0x4702, "F_0584", "1st Day - spoke to Rusl", "Ordon Village" }, + { 0x47, 0x01, 0x4701, "F_0585", "1st day - Spoke to Bo", "Ordon Village" }, + { 0x48, 0x80, 0x4880, "F_0586", "Spoke to Bo after defeating mountain goat", "Ordon Village" }, + { 0x48, 0x40, 0x4840, "F_0587", "successfully defeated rampaging mountain goat", "Ordon Village" }, + { 0x48, 0x20, 0x4820, "F_0588", "First time letting rampaging mountain goat escape", "Ordon Village" }, + { 0x48, 0x10, 0x4810, "F_0589", "Sera - spoke on first day", "Ordon Village" }, + { 0x48, 0x08, 0x4808, "F_0590", "2nd Day - Spoke to Malo during Talo's disappearance (after getting lantern)", "Ordon Village" }, + { 0x48, 0x04, 0x4804, "F_0591", "2nd Day - Spoke to Malo right after Talo disappears", "Ordon Woods" }, + { 0x48, 0x02, 0x4802, "F_0592", "2nd Day - Spoke with Beth during Talo's disappearance (after getting lantern)", "Ordon Village" }, + { 0x48, 0x01, 0x4801, "F_0593", "2nd Day - Conversation with Beth right after Talo's disappearance", "Ordon Woods" }, + { 0x49, 0x80, 0x4980, "F_0594", "2nd Day - Colin (conversation after receiving lantern from Coro)", "Ordon Village" }, + { 0x49, 0x40, 0x4940, "F_0595", "2nd Day - Conversation with Colin after sword tutorial and after horse rejection", "Ordon Village" }, + { 0x49, 0x20, 0x4920, "F_0596", "2nd Day - Conversation with Colin after sword tutorial, before horse rejection", "Ordon Village" }, + { 0x49, 0x10, 0x4910, "F_0597", "2nd Day - First time speaking with Bo (after successfully defeating mountain goat)", "Ordon Village" }, + { 0x49, 0x08, 0x4908, "F_0598", "2nd Day - First conversation with Bo", "Ordon Village" }, + { 0x49, 0x04, 0x4904, "F_0599", "2nd Day - tried to enter Bo's house", "Ordon Village" }, + { 0x49, 0x02, 0x4902, "F_0600", "Purchase slingshot", "Ordon Village" }, + { 0x49, 0x01, 0x4901, "F_0601", "Spoke to imprisoned Talo", "Faron Woods" }, + { 0x4A, 0x40, 0x4A40, "F_0700", "First day ends", "N/A" }, + { 0x4A, 0x20, 0x4A20, "F_0701", "Talo discovers monkey in Link's house garden", "N/A" }, + { 0x4A, 0x10, 0x4A10, "F_0702", "Confirmed kidnapped Talo", "Ordon Village" }, + { 0x4A, 0x08, 0x4A08, "F_0606", "Giants switched places", "Sacred Grove" }, + { 0x4A, 0x04, 0x4A04, "F_0607", "Quit Slingshot tutorial", "Ordon Village" }, + { 0x4A, 0x02, 0x4A02, "F_0608", "Began Slingshot tutorial", "Ordon Village" }, + { 0x4A, 0x01, 0x4A01, "F_0609", "Slingshot Tutorial - First time hitting scarcrow torso", "Ordon Village" }, + { 0x4B, 0x80, 0x4B80, "F_0610", "Slingshot Tutorial - Hit it without using focus", "Ordon Village" }, + { 0x4B, 0x40, 0x4B40, "F_0611", "Slingshot Tutorial - Hit it using focusing", "Ordon Village" }, + { 0x4B, 0x20, 0x4B20, "F_0612", "Pachinco Tutorial - Spoke before hitting target", "Ordon Village" }, + { 0x4B, 0x10, 0x4B10, "F_0613", "Slingshot Tutorial - Spoke before getting 2 scarecrow heards", "Ordon Village" }, + { 0x4B, 0x08, 0x4B08, "F_0614", "2nd Day - Heard forced conversation immediately after the slignshot tutorial", "Ordon Village" }, + { 0x4B, 0x04, 0x4B04, "F_0615", "Recieved vessel of light from Lanayru spirit", "Lake Hylia" }, + { 0x4B, 0x02, 0x4B02, "F_0616", "3rd Day - Spoke to Talo/Malo after handing over wooden sword (1st time)", "Ordon Village" }, + { 0x4B, 0x01, 0x4B01, "F_0617", "3rd Day - Spoke to Talo/Malo after handing over wooden sword (2nd time)", "Ordon Village" }, + { 0x4C, 0x80, 0x4C80, "F_0618", "Scooped bee larva into bottle on opening 2nd Day ", "Ordon Village" }, + { 0x4C, 0x40, 0x4C40, "F_0619", "Spoke to Zora soldier (near cannon) in Lake Hylia", "Lake Hylia" }, + { 0x4C, 0x20, 0x4C20, "F_0620", "First caught a Reek Fish", "Zora's Domain" }, + { 0x4C, 0x10, 0x4C10, "F_0621", "Spoke to Hanch on 3rd day (knocked down beehive on 2nd day)", "Ordon Village" }, + { 0x4C, 0x08, 0x4C08, "F_0622", "Opening 2nd day: Spoke to Hanch before being attacked by bees", "Ordon Village" }, + { 0x4C, 0x04, 0x4C04, "F_0623", "Opening 3rd day: Spoke to Hanch before being attacked by bees", "Ordon Village" }, + { 0x4C, 0x02, 0x4C02, "F_0624", "3rd day: Warned by Hanch when climbing vines", "Ordon Village" }, + { 0x4C, 0x01, 0x4C01, "F_0625", "Saved Talo and a monkey", "Faron Woods" }, + { 0x4D, 0x80, 0x4D80, "F_0626", "Received a heart piece from Jovani", "Castle Town" }, + { 0x4D, 0x40, 0x4D40, "F_0627", "Spoke with Jovani at the bar", "Castle Town" }, + { 0x4D, 0x20, 0x4D20, "F_0628", "Received 200 Rupees from Gengle", "Castle Town" }, + { 0x4D, 0x10, 0x4D10, "F_0629", "First conversation iwth Gengle after speaking with Jovani at the bar", "Castle Town" }, + { 0x4D, 0x08, 0x4D08, "F_0630", "(Cutscene 4 - ?) Right after Link is captured (wolf)", "Kawagoe Cutscene" }, + { 0x4A, 0x80, 0x4A80, "F_0631", "Heard when zooming in on fish tank (1)", "Fishing Pond (inside)" }, + { 0x4D, 0x04, 0x4D04, "F_0632", "Heard first time zooming in on fish tank (2)", "Fishing Pond (inside)" }, + { 0x4D, 0x02, 0x4D02, "F_0633", "Heard second time zooming in on fish tank (2)", "Fishing Pond (inside)" }, + { 0x4D, 0x01, 0x4D01, "F_0634", "Heard zooming in on lure (no frog)", "Fishing Pond (inside)" }, + { 0x4E, 0x80, 0x4E80, "F_0635", "Heard zooming in on lure (yes frog)", "Fishing Pond (inside)" }, + { 0x4E, 0x40, 0x4E40, "F_0636", "Heard first time zooming in on frog lure", "Fishing Pond (inside)" }, + { 0x4E, 0x20, 0x4E20, "F_0637", "Cleard all of roll goal game (get frog lure)", "Fishing Pond (inside)" }, + { 0x4E, 0x10, 0x4E10, "F_0638", "Heard first time zooming in on canoe", "Fishing Pond (inside)" }, + { 0x4E, 0x08, 0x4E08, "F_0639", "Heard first time zooming in on hat", "Fishing Pond (inside)" }, + { 0x4E, 0x04, 0x4E04, "F_0640", "Heard first time zooming in on pot", "Fishing Pond (inside)" }, + { 0x4E, 0x02, 0x4E02, "F_0641", "Heard First time zooming in on rug", "Fishing Pond (inside)" }, + { 0x4E, 0x01, 0x4E01, "F_0642", "Heard first time zooming in on book", "Fishing Pond (inside)" }, + { 0x4F, 0x80, 0x4F80, "F_0643", "Heard first time zooming in on old man's photo", "Fishing Pond (inside)" }, + { 0x4F, 0x40, 0x4F40, "F_0644", "Heard first time zooming in on Coro's photo", "Fishing Pond (inside)" }, + { 0x4F, 0x20, 0x4F20, "F_0645", "Heard First time zoomin in on Iza's photo", "Fishing Pond (inside)" }, + { 0x4F, 0x10, 0x4F10, "F_0646", "Heard first time zooming in on Hena's photo", "Fishing Pond (inside)" }, + { 0x4F, 0x08, 0x4F08, "F_0647", "Heard second time zooming in on Hena's photo", "Fishing Pond (inside)" }, + { 0x4F, 0x04, 0x4F04, "F_0648", "Heard Hena's photo 1,2 (additional story)", "Fishing Pond (inside)" }, + { 0x4F, 0x02, 0x4F02, "F_0649", "Heard first time zooming in on Link's picture", "Fishing Pond (inside)" }, + { 0x4F, 0x01, 0x4F01, "F_0650", "Heard second time zooming in on Link's picture", "Fishing Pond (inside)" }, + { 0x50, 0x80, 0x5080, "F_0651", "Caught Greengill with bobber first time", "Fishing" }, + { 0x50, 0x40, 0x5040, "F_0652", "Caught Hylian Bass with bobber first time", "Fishing" }, + { 0x50, 0x20, 0x5020, "F_0653", "Caught Hylian Pike with bobber first time", "Fishing" }, + { 0x50, 0x10, 0x5010, "F_0654", "Caught Hylian Loach using bobber first time", "Fishing" }, + { 0x50, 0x08, 0x5008, "F_0655", "Caught an Ordon Catfish using bobber first time", "Fishing" }, + { 0x50, 0x04, 0x5004, "F_0656", "Caught something (any fish) using lure first time", "Fishing" }, + { 0x50, 0x02, 0x5002, "F_0657", "Caught Hylian Loach in front of Hena first time", "Fishing" }, + { 0x50, 0x01, 0x5001, "F_0658", "Recieved large wallet from Agitha", "Castle Town" }, + { 0x51, 0x80, 0x5180, "F_0659", "Hear information about spirit spring in Lake Hylia from Twilight Soldier or map", "Castle Town" }, + { 0x51, 0x40, 0x5140, "F_0660", "Twilight Lake Hylia Listened to Zora soldier A talk", "Lake Hylia" }, + { 0x51, 0x20, 0x5120, "F_0661", "Twilight Lake Hylia Listened to Zora soldier B&C talk", "Lake Hylia" }, + { 0x51, 0x10, 0x5110, "F_0662", "Omit rules after clearing roll goal game once", "Fishing Pond (inside)" }, + { 0x51, 0x08, 0x5108, "F_0663", "Listened to Purdy's unnecessary words", "Fishing Pond (inside)" }, + { 0x51, 0x04, 0x5104, "F_0664", "After clearing roll goal game / first attempt", "Fishing Pond (inside)" }, + { 0x51, 0x02, 0x5102, "F_0665", "First zoom in Roll goal game", "Fishing Pond (inside)" }, + { 0x51, 0x01, 0x5101, "F_0666", "Showed Yeto sketch to Zora soldier next to Snowpeak Mountain", "Zora's Domain" }, + { 0x52, 0x80, 0x5280, "F_0667", "Showed Yeto sketch to average Zora soldier (generic)", "Zora's Domain" }, + { 0x52, 0x40, 0x5240, "F_0668", "Showed Yeto sketch to Zora civilian (generic)", "Zora's Domain" }, + { 0x52, 0x20, 0x5220, "F_0669", "Pulled on Fyrus chains at least once", "LV2 Dungeon" }, + { 0x52, 0x10, 0x5210, "F_0670", "Hitting knocked-down Fyrus", "LV2 Dungeon" }, + { 0x52, 0x08, 0x5208, "F_0671", "Through magnet goron into lava once", "LV2 Dungeon" }, + { 0x52, 0x04, 0x5204, "F_0672", "First time seeing magnet goron tumbling", "LV2 Dungeon" }, + { 0x52, 0x02, 0x5202, "F_0673", "Heard hint about Fyrus's weakness (F0215)", "LV2 Dungeon" }, + { 0x52, 0x01, 0x5201, "F_0674", "Light - Spoke with cafe table: townsperson A1", "Castle Town" }, + { 0x53, 0x80, 0x5380, "F_0675", "Light - cafe counter: Spoke with townsgirl A1 (shop clerk)", "Castle Town" }, + { 0x53, 0x40, 0x5340, "F_0676", "Light - Spoke with Cafe customer: Townsperson F1 (just the man)", "Castle Town" }, + { 0x53, 0x20, 0x5320, "F_0677", "Light - Spoke with Cafe customer B: Townsperson D1 and Man B1", "Castle Town" }, + { 0x53, 0x10, 0x5310, "F_0678", "Spoke with Group A (Town girl C2, Boy B1, Town girl D1) in front of fountain", "Castle Town" }, + { 0x53, 0x08, 0x5308, "F_0679", "Cheated during Roll goal game", "Fishing Pond (inside)" }, + { 0x53, 0x04, 0x5304, "F_0680", "Heard Midna's hint after cutscene of ghost escaping", "LV4 Dungeon," }, + { 0x53, 0x02, 0x5302, "F_0681", "Heard Midna's hint after defeating first ghost", "LV4 Dungeon," }, + { 0x53, 0x01, 0x5301, "F_0682", "First conversation with Oocca Shopkeeper", "LV7 dungeon" }, + { 0x54, 0x80, 0x5480, "F_0683", "Spoke with Group B (Old lady A1, Boy A1, Town girl B1) in central square", "Castle Town" }, + { 0x54, 0x40, 0x5440, "F_0684", "Look at R00 statue using sense", "LV6 Dungeon" }, + { 0x54, 0x20, 0x5420, "F_0685", "(Cutscene 32) Sage appears, get first Mirror of Twilight shard", "Kawagoe Cutscene" }, + { 0x54, 0x10, 0x5410, "F_0686", "Get fused shadow piece (final mask)", "LV8 Dungeon" }, + { 0x54, 0x08, 0x5408, "F_0687", "Spoke with town girl D1 in front of south road hot spring shop (CLOSED)", "Castle Town" }, + { 0x54, 0x04, 0x5404, "F_0688", "Spoke with town girl D1 in front of south road hot spring shop (OPEN)", "Castle Town" }, + { 0x54, 0x02, 0x5402, "F_0689", "South Road - Spoke with meat shop villager man C1", "Castle Town" }, + { 0x54, 0x01, 0x5401, "F_0690", "South Road - Spoke with woman A1 in front of fortune teller's shop", "Castle Town" }, + { 0x55, 0x80, 0x5580, "F_0691", "Spoke with south alley children before guarding carriage", "Castle Town" }, + { 0x55, 0x40, 0x5540, "F_0692", "Spoke with south alley children after guarding carriage", "Castle Town" }, + { 0x55, 0x20, 0x5520, "F_0693", "First conversation with Plumm while human", "Lake Hylia" }, + { 0x55, 0x10, 0x5510, "F_0694", "Conversation with Jovani after collecting 20 ghosts", "Castle Town" }, + { 0x55, 0x08, 0x5508, "F_0695", "First time frog lure slips out", "Fishing" }, + { 0x55, 0x04, 0x5504, "F_0696", "Spoke with inquiring Hyrule guard before saving Jovani", "Castle Town" }, + { 0x55, 0x02, 0x5502, "F_0697", "Spoke with inquiring Hyrule guard after saving Jovani", "N/A" }, + { 0x55, 0x01, 0x5501, "F_0698", "Spoke with woman B1 in front of south vegetable stand (before guarding carriage)", "Castle Town" }, + { 0x56, 0x80, 0x5680, "F_0699", "Spoke with woman B1 in front of south vegetable stand (after guarding carriage)", "Castle Town" }, + { 0x56, 0x40, 0x5640, "F_0700B", "South Vegetable vendor - Spoke with Villager D2 (before guarding carriage)", "Castle Town" }, + { 0x56, 0x20, 0x5620, "F_0701B", "South Vegetable vendor - Spoke with Villager D2 (after guarding carriage)", "N/A" }, + { 0x56, 0x10, 0x5610, "F_0702B", "South Baker - Spoke with Boy A2", "Castle Town" }, + { 0x56, 0x08, 0x5608, "F_0703", "Caught Reek fish for first time while fishing", "Fishing" }, + { 0x56, 0x04, 0x5604, "F_0704", "Listened to Iza in Twilight before domain is thawed", "Zora's River" }, + { 0x56, 0x02, 0x5602, "F_0705", "First conversation with Kaeru (frog) during Midna's desperate hour", "Castle Town" }, + { 0x56, 0x01, 0x5601, "F_0706", "First conversation wtih Torako (cat) during Midna's desperate hour", "Castle Town" }, + { 0x57, 0x80, 0x5780, "F_0707", "Midna's desperate hour, first conversation with Mii (cat)", "Castle Town" }, + { 0x57, 0x40, 0x5740, "F_0708", "First conversaton with doctor after completing spirit", "Castle Town" }, + { 0x57, 0x20, 0x5720, "F_0709", "Midna's desperate hour, first conversation wtih Stephanie (cat)", "Castle Town" }, + { 0x57, 0x10, 0x5710, "F_0715", "Speak with dog to west", "Castle Town" }, + { 0x57, 0x08, 0x5708, "F_0716", "Speak with dog to the east", "Castle Town" }, + { 0x57, 0x04, 0x5704, "F_0717", "Wist - Speak to the dog owner", "Castle Town" }, + { 0x57, 0x02, 0x5702, "F_0718", "South - Spoke to old lady at the vegetable stand (Chudley)", "Castle Town" }, + { 0x57, 0x01, 0x5701, "F_0719", "South - Spoke to old lady at the vegetable stand (Malo Mart)", "Castle Town" }, + { 0x58, 0x80, 0x5880, "F_0720", "South - Spoke to fruit stand vendor (before guarding carriage)", "Castle Town" }, + { 0x58, 0x40, 0x5840, "F_0721", "South - Spoke to fruit stand vendor (after guarding carriage)", "Castle Town" }, + { 0x58, 0x20, 0x5820, "F_0722", "East - Spoke to man at the T-shaped street", "Castle Town" }, + { 0x58, 0x10, 0x5810, "F_0723", "East - Spoke to man in the alley", "Castle Town" }, + { 0x58, 0x08, 0x5808, "F_0724", "East - Spoke to two ladies gossiping", "Castle Town" }, + { 0x58, 0x04, 0x5804, "F_0725", "Speak to Renado who is announcing Shad's visit", "Kakariko Village" }, + { 0x58, 0x02, 0x5802, "F_0726", "Talk to Iza as wolf", "Zora's River" }, + { 0x58, 0x01, 0x5801, "F_0727", "First conversation with shoe-shine boy", "Castle Town" }, + { 0x59, 0x80, 0x5980, "F_0728", "Refused entry into Chudley's shop because of dirty shoes", "Castle Town" }, + { 0x59, 0x40, 0x5940, "F_0729", "Right after postman's \"Hey!\"", "Ring field" }, + { 0x59, 0x20, 0x5920, "F_0730", "Rode Iza's boat for the first time", "Zora's River" }, + { 0x59, 0x10, 0x5910, "F_0732", "Spoke with Cucco (After completing Ilia's memory event)", "Hidden Village" }, + { 0x59, 0x08, 0x5908, "F_0733", "Recieved Heart piece reward from Iza for going down the river", "Zora's River" }, + { 0x59, 0x04, 0x5904, "F_0734", "Spoke with wild duck", "Fishing" }, + { 0x59, 0x02, 0x5902, "F_0735", "Spoke with domestic duck", "Fishing" }, + { 0x59, 0x01, 0x5901, "F_0736", "Heard Midna's hint / monkey hint", "LV1 Dungeon" }, + { 0x5A, 0x80, 0x5A80, "F_0737", "First conversation with Udo (cat) during Midna's desperate hour", "Castle Town" }, + { 0x5A, 0x40, 0x5A40, "F_0738", "South - Spoke with female customer at vegetable stand", "Castle Town" }, + { 0x5A, 0x20, 0x5A20, "F_0739", "South - Spoke with old customer at fruit stand", "Castle Town" }, + { 0x5A, 0x10, 0x5A10, "F_0740", "Spoke with child Goron (selling lantern oil) (before Ganon wall)", "Castle Town" }, + { 0x5A, 0x08, 0x5A08, "F_0741", "Spoke with child Goron (selling lantern oil) (after Ganon wall)", "Castle Town" }, + { 0x5A, 0x04, 0x5A04, "F_0742", "Spoke with child Goron (selling red potion)", "Castle Town" }, + { 0x5A, 0x02, 0x5A02, "F_0743", "Spoke with adult Goron (30 arrows) (before Ganon wall)", "Castle Town" }, + { 0x5A, 0x01, 0x5A01, "F_0744", "Spoke with adult Goron (30 arrows) (after Ganon wall)", "Castle Town" }, + { 0x5B, 0x80, 0x5B80, "F_0745", "Spoke with adult Goron (Hylia shield)", "Castle Town" }, + { 0x5B, 0x40, 0x5B40, "F_0746", "Spoke with Cucco A", "Kakariko Village" }, + { 0x5B, 0x20, 0x5B20, "F_0747", "Spoke with Cucco B", "Kakariko Village" }, + { 0x5B, 0x10, 0x5B10, "F_0748", "First conversation with hint hawk", "Ring field" }, + { 0x5B, 0x08, 0x5B08, "F_0749", "After clearing cat game (Heart piece is buried, whether it is obtained depends on the player)", "Hidden Village" }, + { 0x5B, 0x04, 0x5B04, "F_0750", "First conversation with Captain Cucco after beginning cat game activities", "Hidden Village" }, + { 0x5B, 0x02, 0x5B02, "F_0751", "Heard detailed explaination from Captain Cucco for the first time", "Hidden Village" }, + { 0x5B, 0x01, 0x5B01, "F_0752", "Beat cat game again", "Hidden Village" }, + { 0x5C, 0x80, 0x5C80, "F_0753", "Scooped Coro's bad soup for the first time", "Faron Woods" }, + { 0x5C, 0x40, 0x5C40, "F_0754", "Heard Midna's hint / Monkey hint 2 times (SAVE72=ON)", "LV1 Dungeon" }, + { 0x5C, 0x20, 0x5C20, "F_0755", "Twilight Heard Iza's line right after shadow bug appears", "Zora's River" }, + { 0x5C, 0x10, 0x5C10, "F_0756", "Join with Ooccoo Sr.", "LV7 dungeon" }, + { 0x5C, 0x08, 0x5C08, "F_0757", "Had Trill and wolf conversation", "Faron Woods" }, + { 0x5C, 0x04, 0x5C04, "F_0758", "Stole from unmanned shop (For use in Trill wolf conversation. Resets after speaking)", "Faron Woods" }, + { 0x5C, 0x02, 0x5C02, "F_0759", "Opening (3rd day) first conversation with Sera", "Ordon Village" }, + { 0x5C, 0x01, 0x5C01, "B_BTN_GUIDE", "B-button guide", "Misc." }, + { 0x5D, 0x80, 0x5D80, "J_BTN_GUIDE", "X-button guide", "Misc." }, + { 0x5D, 0x40, 0x5D40, "F_0768", "Heard forced conversation with Midna after Hylia Lake introduction cutscene", "Lake Hylia" }, + { 0x5D, 0x20, 0x5D20, "F_0769", "Forced conversation with Midna right after first arrival at upper Zora's river", "Zora's River" }, + { 0x5D, 0x10, 0x5D10, "F_0770", "Twilight (frozen) Heard forced conversation after first visit cutscene", "Zora's Domain" }, + { 0x5D, 0x08, 0x5D08, "F_0771", "Unmanned Shop : last payment stolen", "Faron Woods" }, + { 0x5D, 0x04, 0x5D04, "F_0772", "Unmanned Shop : last payment too little", "Faron Woods" }, + { 0x5D, 0x02, 0x5D02, "F_0773", "Unmanned Shop : last payment too much", "Faron Woods" }, + { 0x5D, 0x01, 0x5D01, "F_0774", "Listened to Midna's conversation after seeing frozen Zora", "Zora's Domain" }, + { 0x5E, 0x80, 0x5E80, "F_0775", "Heard forced conversation with Midna after first Fyer's cannon", "Lake Hylia" }, + { 0x5E, 0x40, 0x5E40, "F_0776", "Link first turned to wolf due to fog in LV8 dungeon", "LV8 Dungeon" }, + { 0x5E, 0x20, 0x5E20, "F_0777", "Spoke to Epona", "Misc." }, + { 0x5E, 0x10, 0x5E10, "F_0778", "Heard forced conversation with Midna after story about spirits after clearing LV1 dungeon", "Faron Woods" }, + { 0x5E, 0x08, 0x5E08, "F_0779", "Heard one hint from Midna while bridge is burning", "Lake Hylia" }, + { 0x5E, 0x04, 0x5E04, "F_0780", "Heard Ilia talk about the messenger from the sky after her memories are restored", "Kakariko Village" }, + { 0x5E, 0x02, 0x5E02, "F_0781", "Spoke to Ilia in church after clearing LV6", "Kakariko Village" }, + { 0x5E, 0x01, 0x5E01, "F_0782", "Spoke to Renado after LV3 complete, before LV6 complete", "Kakariko Village" }, + { 0x5F, 0x80, 0x5F80, "F_0783", "Showed dominion rod to Impaz", "Hidden Village" }, + { 0x5F, 0x40, 0x5F40, "F_0784", "Had normal conversation with Shad after he returns to church basement", "Kakariko Village" }, + { 0x5F, 0x20, 0x5F20, "F_0785", "Shad leaves after attempting to warp sky cannon", "Kakariko Village" }, + { 0x5F, 0x10, 0x5F10, "F_0786", "Forced conversation with Shad when he sees the sky cannon", "Kakariko Village" }, + { 0x5F, 0x08, 0x5F08, "F_0787", "Stopped by Midna when trying to warp the sky cannon", "Kakariko Village" }, + { 0x5F, 0x04, 0x5F04, "F_0788", "Talked to Zora going up the waterfall", "Zora's River" }, + { 0x5F, 0x02, 0x5F02, "F_0789", "Grabbed sweet apple 1 time", "Castle Town" }, + { 0x5F, 0x01, 0x5F01, "F_0790", "Sera faces the cat (outside conversation area when 520 is ON)", "Ordon Village" }, + { 0x60, 0x80, 0x6080, "F_0791", "Sky character 1", "Sky character" }, + { 0x60, 0x40, 0x6040, "F_0792", "Sky character 2", "Sky character" }, + { 0x60, 0x20, 0x6020, "F_0793", "Sky character 3", "Sky character" }, + { 0x60, 0x10, 0x6010, "F_0794", "Sky character 4", "Sky character" }, + { 0x60, 0x08, 0x6008, "F_0795", "Sky character 5", "Sky character" }, + { 0x60, 0x04, 0x6004, "F_0796", "Sky character 6", "Sky character" }, + { 0x60, 0x02, 0x6002, "F_0797", "Have selected \"Take Cannon / Repair\" at Fyer's cannon (when there's no other business)", "Lake Hylia" }, + { 0x60, 0x01, 0x6001, "F_0798", "Heard about Zora from Fyer", "Lake Hylia" }, + { 0x61, 0x80, 0x6180, "F_0799", "Listened to the fallen Goron's complaints (after opening bridge)", "Ring field" }, + { 0x61, 0x40, 0x6140, "F_0800", "After returning to Ordon Woods, until Midna comes out of the shadows (If 800 is ON, Midna can't be called)", "Kawagoe Cutscene" }, + { 0x61, 0x20, 0x6120, "F_0801", "First learned Reek Fish scent", "Zora's Domain" }, + { 0x61, 0x10, 0x6110, "F_0802", "Trill attacks when stealing", "Faron Woods" }, + { 0x61, 0x08, 0x6108, "F_0803", "Talked to Old Lady Impaz after going to Sky", "Hidden Village" }, + { 0x61, 0x04, 0x6104, "F_0804", "Heard Midna's forced conversation immediately after plunge into Eldin Twilight", "Twilight Ring field" }, + { 0x61, 0x02, 0x6102, "F_0805", "Bought Hylian shield from Malo Mart", "Kakariko Village" }, + { 0x61, 0x01, 0x6101, "F_0806", "Talked to Hanch on the first day", "Ordon Village" }, + { 0x62, 0x80, 0x6280, "F_0807", "Talked to Colin on the first day", "Ordon Village" }, + { 0x62, 0x40, 0x6240, "F_0808", "Talked to Beth on the first day", "Ordon Village" }, + { 0x62, 0x20, 0x6220, "F_0809", "3 groupie girls (first conversation where they become Link's groupies)", "Castle Town" }, + { 0x62, 0x10, 0x6210, "F_0810", "Learned ghost's scent", "LV4 Dungeon" }, + { 0x62, 0x08, 0x6208, "F_0811", "Attempted Star Game for the first time", "Castle Town" }, + //{ 0x62, 0x04, 0x6204, "F_0812", "N/A", "N/A" }, + //{ 0x62, 0x02, 0x6202, "F_0813", "N/A", "N/A" }, + //{ 0x62, 0x01, 0x6201, "F_0814", "N/A", "N/A" }, + //{ 0x63, 0x80, 0x6380, "F_0815", "N/A", "N/A" }, + //{ 0x63, 0x40, 0x6340, "F_0816", "N/A", "N/A" }, + //{ 0x63, 0x20, 0x6320, "F_0817", "N/A", "N/A" }, + //{ 0x63, 0x10, 0x6310, "F_0818", "N/A", "N/A" }, + //{ 0x63, 0x08, 0x6308, "F_0819", "N/A", "N/A" }, + //{ 0x63, 0x04, 0x6304, "F_0820", "N/A", "N/A" }, + { 0x63, 0x02, 0x6302, "KORO2_ALLCLEAR", "After all stages (8-8) of roll goal game cleared", "Fishing" }, +}; + + +struct MultiBitEventFlag { + uint8_t byteInd; + const char* description; +}; + +inline const MultiBitEventFlag duskImguiU8Events[] = { + { 0xf2, "Ordon Catfish size record in Fishing Hole (in cm)" }, + { 0xf3, "Hylian Pike size record in Fishing Hole (in cm)" }, + { 0xf4, "Hylian Loach size record in Fishing Hole (in cm)" }, + { 0xf5, "Hyrule Bass size record in Fishing Hole (in cm)" }, + { 0xf6, "Number of Rollgoal Levels beaten" }, +}; + +inline const MultiBitEventFlag duskImguiU16Events[] = { + { 0xf7, "Rupees donated to Charlo" }, + { 0xf9, "Rupees donated towards the current Malo Mart donation goal" }, +}; + +inline const MultiBitEventFlag duskImguiSwappedU16Events[] = { + { 0xfb, "Rupees paid to Trill" }, + { 0xfd, "Rupees owed to Trill" }, +}; + +#endif // !DUSK_IMGUI_EVENTFLAGS_HPP \ No newline at end of file diff --git a/src/dusk/imgui/ImGuiSaveEditor.cpp b/src/dusk/imgui/ImGuiSaveEditor.cpp index 2c679308a2..5802c07b47 100644 --- a/src/dusk/imgui/ImGuiSaveEditor.cpp +++ b/src/dusk/imgui/ImGuiSaveEditor.cpp @@ -4,6 +4,7 @@ #include "ImGuiConsole.hpp" #include "ImGuiSaveEditor.hpp" +#include "ImGuiEventFlags.hpp" #include "d/d_com_inf_game.h" #include "d/d_item_data.h" @@ -1419,20 +1420,130 @@ namespace dusk { if (ImGui::TreeNode("Event Flags")) { dSv_event_c& event = dComIfGs_getSaveData()->mEvent; - for (int e = 0; e < 255; e++) { - ImGui::Text("%03d ", e); - ImGui::SameLine(80.0f); - for (int i = 7; i >= 0; i--) { - bool flag = event.mEvent[e] & (1 << i); - if (ImGui::Checkbox(fmt::format("##event{0}{1}", e, i).c_str(), &flag)) { - if (flag) - event.mEvent[e] |= (1 << i); - else - event.mEvent[e] &= ~(1 << i); + + static ImGuiTextFilter filter; + filter.Draw(); // Search bar + + ImVec2 flagTableSize = {700, 400}; + if (ImGui::BeginTable("Events", 4, + ImGuiTableFlags_ScrollY | ImGuiTableFlags_ScrollX | ImGuiTableFlags_Sortable, + flagTableSize)) + { + ImGui::TableSetupScrollFreeze(0, 1); + constexpr int COLUMN_FLAG = 0, COLUMN_NAME = 1, COLUMN_LOC = 2, COLUMN_DESC = 3; + ImGui::TableSetupColumn("Flag"); + ImGui::TableSetupColumn("Name"); + ImGui::TableSetupColumn("Location"); + ImGui::TableSetupColumn("Description"); + ImGui::TableHeadersRow(); + + // if we're sorting by whether the flag is set or not, + // we want to re-sort whenever a flag updates, which means every frame cuz we don't know when it changes. + // otherwise only re-sort when the sort is dirty + if (auto* sort = ImGui::TableGetSortSpecs(); + sort != nullptr && sort->SpecsCount > 0 && + (sort->SpecsDirty || sort->Specs[0].ColumnIndex == COLUMN_FLAG)) + { + auto column = sort->Specs->ColumnIndex; + const auto cmp = [&](const duskImguiEventFlagEntry& l, + const duskImguiEventFlagEntry& r) -> bool { + switch (column) { + case COLUMN_FLAG: + return (bool)event.isEventBit(l.flagID) < + (bool)event.isEventBit(r.flagID); + case COLUMN_NAME: + return l.flagName < r.flagName; + case COLUMN_LOC: + return l.location < r.location; + case COLUMN_DESC: + return l.description < r.description; + } + return false; + }; + + const auto direction = sort->Specs[0].SortDirection; + + if (direction == ImGuiSortDirection_Ascending) { + std::sort(std::begin(duskImguiEventFlags), std::end(duskImguiEventFlags), cmp); + } else { + std::sort(std::rbegin(duskImguiEventFlags), std::rend(duskImguiEventFlags), cmp); } - ImGui::SameLine(); + sort->SpecsDirty = false; } - ImGui::NewLine(); + + for (const auto& e : duskImguiEventFlags) { + if (!filter.PassFilter((e.location + "\n" + e.description + "\n" + e.flagName).c_str())) + { + continue; + } + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + bool flag = event.getEventReg(e.flagID); + if (ImGui::Checkbox(("##" + e.flagName).c_str(), &flag)) { + if (flag) { + event.onEventBit(e.flagID); + } else { + event.offEventBit(e.flagID); + } + } + + ImGui::TableNextColumn(); + ImGui::Text(e.flagName.c_str()); + ImGui::TableNextColumn(); + ImGui::Text(e.location.c_str()); + ImGui::TableNextColumn(); + ImGui::Text(e.description.c_str()); + } + ImGui::EndTable(); + } + + // event values that are stored as u8s in the event flags + for (const auto& e : duskImguiU8Events) { + int v = event.mEvent[e.byteInd]; + if (ImGui::InputInt(e.description, &v)) { + v = std::clamp(v, 0, 0xff); + event.mEvent[e.byteInd] = (u8)v; + } + } + + // event values that are stored as u16s in the event flags + for (const auto& e : duskImguiU16Events) { + int v = (event.mEvent[e.byteInd] << 8) | event.mEvent[e.byteInd + 1]; + if (ImGui::InputInt(e.description, &v)) { + v = std::clamp(v, 0, 0xffff); + event.mEvent[e.byteInd] = (u8)(v >> 8); + event.mEvent[e.byteInd + 1] = (u8)v; + } + } + + // event values that are stored as swapped u16s in the event flags + for (const auto& e : duskImguiSwappedU16Events) { + int v = (event.mEvent[e.byteInd + 1] << 8) | event.mEvent[e.byteInd]; + if (ImGui::InputInt(e.description, &v)) { + v = std::clamp(v, 0, 0xffff); + event.mEvent[e.byteInd + 1] = (u8)(v >> 8); + event.mEvent[e.byteInd] = (u8)v; + } + } + + if (ImGui::TreeNode("Event Matrix")) { + for (int e = 0; e < 255; e++) { + ImGui::Text("%03d ", e); + ImGui::SameLine(80.0f); + for (int i = 7; i >= 0; i--) { + bool flag = event.mEvent[e] & (1 << i); + if (ImGui::Checkbox(fmt::format("##event{0}{1}", e, i).c_str(), &flag)) { + if (flag) + event.mEvent[e] |= (1 << i); + else + event.mEvent[e] &= ~(1 << i); + } + ImGui::SameLine(); + } + ImGui::NewLine(); + } + ImGui::TreePop(); } ImGui::TreePop(); } From c7de341484c3c969689c1a6681f8dd4c794621f1 Mon Sep 17 00:00:00 2001 From: Irastris Date: Thu, 16 Apr 2026 22:29:40 -0400 Subject: [PATCH 15/15] Frame interp: Ganondorf's Cape --- src/d/actor/d_a_mant.cpp | 120 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 1 deletion(-) diff --git a/src/d/actor/d_a_mant.cpp b/src/d/actor/d_a_mant.cpp index bcd7f05986..282502419f 100644 --- a/src/d/actor/d_a_mant.cpp +++ b/src/d/actor/d_a_mant.cpp @@ -12,6 +12,7 @@ #if TARGET_PC #include "dusk/dvd_asset.hpp" +#include "dusk/frame_interpolation.h" static u8* l_Egnd_mantTEX_get() { alignas(32) static u8 buf[0x4000]; static bool _ = (dusk::LoadRelAsset(buf, "/rel/Final/Release/d_a_mant.rel", 0x1C00, 0x4000), true); return buf; } static u8* l_Egnd_mantTEX_U_get() { alignas(32) static u8 buf[0x4000]; static bool _ = (dusk::LoadRelAsset(buf, "/rel/Final/Release/d_a_mant.rel", 0x5C00, 0x4000), true); return buf; } static u8* l_Egnd_mantPAL_get() { alignas(32) static u8 buf[0x60]; static bool _ = (dusk::LoadRelAsset(buf, "/rel/Final/Release/d_a_mant.rel", 0x9C00, 0x60), true); return buf; } @@ -267,6 +268,37 @@ static void* tex_d[2] = { static char lbl_277_bss_0; +#if TARGET_PC +static void mant_build_anchor_frame(const cXyz& anchor_a, const cXyz& anchor_b, Mtx out) { + cXyz axis_x = anchor_b - anchor_a; + if (!axis_x.normalizeRS()) { + axis_x = cXyz::BaseX; + } + + cXyz helper = fabsf(axis_x.y) > 0.95f ? cXyz::BaseZ : cXyz::BaseY; + cXyz axis_z = axis_x.getCrossProduct(helper); + if (!axis_z.normalizeRS()) { + axis_z = cXyz::BaseZ; + } + + cXyz axis_y = axis_z.getCrossProduct(axis_x); + if (!axis_y.normalizeRS()) { + axis_y = cXyz::BaseY; + } + + const cXyz center = anchor_a + ((anchor_b - anchor_a) * 0.5f); + + const cXyz col[3] = { axis_x, axis_y, axis_z }; + const f32 t[3] = { center.x, center.y, center.z }; + for (int r = 0; r < 3; ++r) { + out[r][0] = (&col[0].x)[r]; + out[r][1] = (&col[1].x)[r]; + out[r][2] = (&col[2].x)[r]; + out[r][3] = t[r]; + } +} +#endif + void daMant_packet_c::draw() { #if TARGET_PC void* image = l_Egnd_mantTEX; @@ -290,8 +322,72 @@ void daMant_packet_c::draw() { GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_NRM, GX_CLR_RGB, GX_F32, 0); GXSetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_CLR_RGBA, GX_F32, 0); +#if TARGET_PC + cXyz* draw_pos = &this->mNrm[0][0]; + { + const u8 curr_buffer = this->field_0x74; + const cXyz* curr_pos = &this->mPos[curr_buffer][0]; + const MtxP curr_frame = curr_buffer == 0 ? this->mMtx : this->mMtx2; + + Mtx curr_frame_inverse; + MTXInverse(curr_frame, curr_frame_inverse); + + const u8 prev_buffer = curr_buffer ^ 1; + const cXyz* prev_pos = &this->mPos[prev_buffer][0]; + Mtx prev_frame_inverse; + MTXInverse(prev_buffer == 0 ? this->mMtx : this->mMtx2, prev_frame_inverse); + + Mtx presented_frame; + MTXCopy(curr_frame, presented_frame); + + mant_class* mant_p = reinterpret_cast(reinterpret_cast(this) - offsetof(mant_class, field_0x0570)); + if (mant_p != NULL) { + b_gnd_class* parent = (b_gnd_class*)fopAcM_SearchByID(mant_p->parentActorID); + if (parent != NULL && parent->mpModelMorf != NULL) { + J3DModel* model = parent->mpModelMorf->getModel(); + if (model != NULL) { + MtxP src34 = model->getAnmMtx(34); + MtxP src25 = model->getAnmMtx(25); + Mtx joint_34_scratch; + Mtx joint_25_scratch; + MtxP joint_34 = dusk::frame_interp::lookup_replacement(src34, joint_34_scratch) ? joint_34_scratch : src34; + MtxP joint_25 = dusk::frame_interp::lookup_replacement(src25, joint_25_scratch) ? joint_25_scratch : src25; + + cXyz presented_anchor_a; + cXyz presented_anchor_b; + cXyz local_offset; + + MTXCopy(joint_34, *calc_mtx); + local_offset.set(10.0f, 5.0f, -17.0f); + MtxPosition(&local_offset, &presented_anchor_a); + + MTXCopy(joint_25, *calc_mtx); + local_offset.set(10.0f, 5.0f, 17.0f); + MtxPosition(&local_offset, &presented_anchor_b); + + mant_build_anchor_frame(presented_anchor_a, presented_anchor_b, presented_frame); + } + } + } + + const f32 step = dusk::frame_interp::get_interpolation_step(); + for (int i = 0; i < 169; ++i) { + cXyz curr_local; + MTXMultVec(curr_frame_inverse, &curr_pos[i], &curr_local); + + cXyz prev_local; + MTXMultVec(prev_frame_inverse, &prev_pos[i], &prev_local); + cXyz local = prev_local + ((curr_local - prev_local) * step); + + MTXMultVec(presented_frame, &local, &draw_pos[i]); + } + } + GXSETARRAY(GX_VA_POS, draw_pos, sizeof(mNrm[0]), 12, true); + GXSETARRAY(GX_VA_NRM, &l_normal, sizeof(l_normal), 12, false); +#else GXSETARRAY(GX_VA_POS, this->getPos(), sizeof(mPos[0]), 12, true); GXSETARRAY(GX_VA_NRM, this->getNrm(), sizeof(mNrm[0]), 12, true); +#endif GXSETARRAY(GX_VA_TEX0, &l_texCoord, sizeof(l_texCoord), 8, false); // TODO: set to true when converted to float literals GXSetZCompLoc(0); @@ -329,9 +425,14 @@ void daMant_packet_c::draw() { GXSetCullMode(GX_CULL_BACK); - GXLoadPosMtxImm(this->mMtx, GX_PNMTX0); Mtx MStack_54; +#if TARGET_PC + GXLoadPosMtxImm(j3dSys.getViewMtx(), GX_PNMTX0); + cMtx_inverseTranspose(j3dSys.getViewMtx(), MStack_54); +#else + GXLoadPosMtxImm(this->mMtx, GX_PNMTX0); cMtx_inverseTranspose(this->mMtx, MStack_54); +#endif GXLoadNrmMtxImm(MStack_54, GX_PNMTX0); GXCallDisplayList(l_Egnd_mantDL, 0x3e0); @@ -347,8 +448,13 @@ void daMant_packet_c::draw() { GXSetTevKColor(GX_KCOLOR0, COMPOUND_LITERAL(GXColor){0, 0, 0, 0}); GXSetCullMode(GX_CULL_FRONT); +#if TARGET_PC + GXLoadPosMtxImm(j3dSys.getViewMtx(), GX_PNMTX0); + cMtx_inverseTranspose(j3dSys.getViewMtx(), MStack_54); +#else GXLoadPosMtxImm(this->mMtx2, GX_PNMTX0); cMtx_inverseTranspose(this->mMtx2, MStack_54); +#endif GXLoadNrmMtxImm(MStack_54, GX_PNMTX0); GXCallDisplayList(l_Egnd_mantDL, 0x3e0); @@ -360,11 +466,13 @@ void daMant_packet_c::draw() { static int daMant_Draw(mant_class* i_this) { g_env_light.settingTevStruct(0, &i_this->current.pos, &i_this->tevStr); +#if !TARGET_PC MtxTrans(0.0f, 0.0f, 0.0f, 0.0f); cMtx_concat(j3dSys.getViewMtx(), *calc_mtx, i_this->field_0x0570.getMtx()); cMtx_concat(j3dSys.getViewMtx(), *calc_mtx, i_this->field_0x0570.getMtx2()); +#endif i_this->field_0x0570.setTevStr(&i_this->tevStr); @@ -635,8 +743,13 @@ static void mant_v_calc(mant_class* i_this) { } static void mant_move(mant_class* i_this) { +#if TARGET_PC + u8 uVar1 = i_this->field_0x0570.field_0x74 ^ 1; + cXyz* pcVar5 = &i_this->field_0x0570.mPos[uVar1][0]; +#else u8 uVar1 = i_this->field_0x0570.field_0x74; cXyz* pcVar5 = i_this->field_0x0570.getPos(); +#endif mant_v_calc(i_this); for (int i = 0; i < 13; i++) { for (int j = 0; j < 13; j++) { @@ -644,7 +757,12 @@ static void mant_move(mant_class* i_this) { } } +#if TARGET_PC + mant_build_anchor_frame(i_this->field_0x3928[0], i_this->field_0x3928[1], uVar1 == 0 ? i_this->field_0x0570.mMtx : i_this->field_0x0570.mMtx2); + i_this->field_0x0570.field_0x74 = uVar1; +#else DCStoreRangeNoSync(i_this->field_0x0570.getPos(), 0x7ec); +#endif } static int mant_cut_type;