Merge remote-tracking branch 'origin/main' into ci-2

This commit is contained in:
Luke Street
2026-04-16 16:34:55 -06:00
37 changed files with 1082 additions and 441 deletions
+1 -1
View File
@@ -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")
+1 -1
+4 -1
View File
@@ -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
@@ -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
@@ -1376,6 +1378,7 @@ set(DUSK_FILES
src/dusk/imgui/ImGuiSaveEditor.cpp
src/dusk/imgui/ImGuiStateShare.hpp
src/dusk/imgui/ImGuiStateShare.cpp
src/dusk/iso_validate.cpp
src/dusk/offset_ptr.cpp
src/dusk/OSContext.cpp
src/dusk/OSThread.cpp
+11 -4
View File
@@ -6,7 +6,6 @@
#include <stddef.h>
#include <stdint.h>
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
+17
View File
@@ -0,0 +1,17 @@
#ifndef DUSK_GYRO_H
#define DUSK_GYRO_H
namespace dusk::gyro {
void read(float dt);
void getAimDeltas(float& out_yaw, float& out_pitch);
bool queryGyroAimItemContext();
void rollgoalTick(bool play_active, s16 camera_yaw);
void rollgoalTableOffset(s16& out_ax, s16& out_az);
extern bool s_sensor_keep_alive;
bool get_sensor_keep_alive();
void set_sensor_keep_alive(bool value);
} // namespace dusk::gyro
#endif
-10
View File
@@ -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
+9 -4
View File
@@ -81,10 +81,14 @@ struct UserSettings {
// Input
ConfigVar<bool> enableGyroAim;
ConfigVar<float> gyroAimSensitivityX;
ConfigVar<float> gyroAimSensitivityY;
ConfigVar<bool> gyroAimInvertPitch;
ConfigVar<bool> gyroAimInvertYaw;
ConfigVar<bool> enableGyroRollgoal;
ConfigVar<float> gyroSensitivityX;
ConfigVar<float> gyroSensitivityY;
ConfigVar<float> gyroSensitivityRollgoal;
ConfigVar<float> gyroSmoothing;
ConfigVar<float> gyroDeadband;
ConfigVar<bool> gyroInvertPitch;
ConfigVar<bool> gyroInvertYaw;
// Cheats
ConfigVar<bool> enableFastIronBoots;
@@ -106,6 +110,7 @@ struct UserSettings {
ConfigVar<bool> showPipelineCompilation;
ConfigVar<bool> wasPresetChosen;
ConfigVar<bool> enableCrashReporting;
ConfigVar<bool> duskMenuOpen;
} backend;
};
+2 -12
View File
@@ -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,17 +142,7 @@ BOOL daAlink_c::setBodyAngleToCamera() {
f32 gy_yaw = 0.f;
f32 gy_pitch = 0.f;
dusk::gyro_aim::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);
+9
View File
@@ -215,7 +215,16 @@ int daDsh_c::create() {
mType = getType();
#ifdef TARGET_PC
const char* l_resName[] = {l_arcName[mType], ""};
#else
// !@bug By making this static, it is only initialized the first time it runs
// If gate types that use other arcs are loaded later (without reloading the code)
// this array never gets updated and will load the incorrect arc
// On GC/Wii, REL loading causes this to reset/reinitialize so the bug is avoided
// but TPHD is all statically linked so daDsh_c::CreateHeap fails to get model data and the gate unloads
static const char* l_resName[] = {l_arcName[mType], ""};
#endif
int phase = mResLoader.load(l_resName, NULL);
if (phase == cPhs_COMPLEATE_e) {
+40
View File
@@ -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<s16>(reg_f30 * (-6000.0f + JREG_F(7))) + rg_add_x;
s16 tgt_z = static_cast<s16>(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;
}
+12
View File
@@ -363,6 +363,18 @@ int daMyna_c::destroy() {
mpMorf->stopZelAnime();
}
#ifdef TARGET_PC
// !@bug d_a_myna.rel unload used to zero these file-statics; with static linking they dangle across scenes.
daMyna_LightActor = NULL;
daMyna_evtTagActor0 = NULL;
daMyna_evtTagActor1 = NULL;
daMyna_actor_count = 0;
for (int i = 0; i < 10; i++) {
daMyna_targetActor[i] = NULL;
daMyna_subActor[i] = NULL;
}
#endif
#if DEBUG
l_HOSTIO.removeHIO();
#endif
-1
View File
@@ -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<const Mtx*>(process->view.viewMtx), process->view.viewMtx);
#endif
#if WIDESCREEN_SUPPORT
+5
View File
@@ -33,6 +33,7 @@
#include <cstdlib>
#include <cstring>
#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};
-50
View File
@@ -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;
+6
View File
@@ -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);
+152 -67
View File
@@ -1,12 +1,7 @@
#include "f_op/f_op_camera_mng.h"
#include "dusk/frame_interpolation.h"
#include <algorithm>
#include <cmath>
#include <cstdint>
#include <memory>
#include <unordered_map>
#include <vector>
#include "f_op/f_op_camera_mng.h"
namespace {
enum class Op : uint8_t {
@@ -80,6 +75,36 @@ std::vector<Path*> g_current_path;
std::unordered_map<const Mtx*, MatrixValue> 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<f32>(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(&center, 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(&center, 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, &center, &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() {
+123
View File
@@ -0,0 +1,123 @@
#include "dusk/gyro.h"
#include "d/actor/d_a_alink.h"
namespace dusk::gyro {
namespace {
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_pitch_rad = 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 = s_pitch_rad = s_roll_rad = 0.0f;
s_rollgoal_ax = s_rollgoal_az = 0;
}
float apply_deadband(float v, float deadband_rad_s) {
if (v > -deadband_rad_s && v < deadband_rad_s) {
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<bool>(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;
}
const float smooth_alpha = kGyroEmaAlphaMax + dusk::getSettings().game.gyroSmoothing * (kGyroEmaAlphaMin - kGyroEmaAlphaMax);
const float deadband = dusk::getSettings().game.gyroDeadband;
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 = -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 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) {
if (!play_active) {
reset_filter_state();
return;
}
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, -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_ax, s16& out_az) {
out_ax = static_cast<s16>(s_rollgoal_ax);
out_az = static_cast<s16>(s_rollgoal_az);
}
} // namespace dusk::gyro
-87
View File
@@ -1,87 +0,0 @@
#include "dusk/gyro_aim.h"
#include <SDL3/SDL.h>
#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<bool>(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<bool>(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
+126
View File
@@ -0,0 +1,126 @@
#include <algorithm>
#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<u8>(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<int>(colorValue[0] * 255.0f + 0.5f));
color.g = ClampToByte(static_cast<int>(colorValue[1] * 255.0f + 0.5f));
color.b = ClampToByte(static_cast<int>(colorValue[2] * 255.0f + 0.5f));
color.a = ClampToByte(static_cast<int>(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<u8>(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<unsigned>(bloom->getPoint()));
ImGui::Text("Blur Size: %u", static_cast<unsigned>(bloom->getBlureSize()));
ImGui::Text("Blur Ratio: %u", static_cast<unsigned>(bloom->getBlureRatio()));
ImGui::Text("Blend RGBA: %u, %u, %u, %u", static_cast<unsigned>(blendColor.r),
static_cast<unsigned>(blendColor.g), static_cast<unsigned>(blendColor.b),
static_cast<unsigned>(blendColor.a));
ImGui::Text("Mono RGBA: %u, %u, %u, %u", static_cast<unsigned>(monoColor.r),
static_cast<unsigned>(monoColor.g), static_cast<unsigned>(monoColor.b),
static_cast<unsigned>(monoColor.a));
ImGui::End();
}
void ImGuiMenuTools::ShowBloomWindow() {
DrawBloomWindow(m_showBloomWindow);
}
} // namespace dusk
+6
View File
@@ -0,0 +1,6 @@
#pragma once
namespace dusk {
void DrawBloomWindow(bool& open);
void ApplyBloomOverride();
} // namespace dusk
+27 -10
View File
@@ -40,9 +40,15 @@ namespace dusk {
void ImGuiTextCenter(std::string_view text) {
ImGui::NewLine();
float fontSize = ImGui::CalcTextSize(text.data(), text.data() + text.size()).x;
float fontSize = ImGui::CalcTextSize(
text.data(),
text.data() + text.size(),
false,
ImGui::GetWindowSize().x).x;
ImGui::SameLine(ImGui::GetWindowSize().x / 2 - fontSize + fontSize / 2);
ImGui::PushTextWrapPos(ImGui::GetWindowSize().x);
ImGuiStringViewText(text);
ImGui::PopTextWrapPos();
}
bool ImGuiButtonCenter(std::string_view text) {
@@ -228,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;
}
@@ -265,6 +280,7 @@ namespace dusk {
m_menuTools.ShowHeapOverlay();
m_menuTools.ShowStubLog();
m_menuTools.ShowMapLoader();
m_menuTools.ShowBloomWindow();
m_menuTools.ShowPlayerInfo();
m_menuTools.ShowAudioDebug();
m_menuTools.ShowSaveEditor();
@@ -273,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 {
+24 -22
View File
@@ -5,7 +5,7 @@
#include "ImGuiConsole.hpp"
#include "ImGuiMenuGame.hpp"
#include <SDL3/SDL.h>
#include <dolphin/pad.h>
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);
}
}
+100 -74
View File
@@ -9,107 +9,117 @@ namespace dusk {
void ImGuiMenuEnhancements::draw() {
if (ImGui::BeginMenu("Enhancements")) {
if (ImGui::BeginMenu("Quality of Life")) {
config::ImGuiCheckbox("Quick Transform (R+Y)", getSettings().game.enableQuickTransform);
if (ImGui::BeginMenu("Gameplay")) {
ImGui::SeparatorText("Preferences");
config::ImGuiCheckbox("Mirror Mode", getSettings().game.enableMirrorMode);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Allows you to quickly transform between forms\n"
"without having to talk to Midna.");
ImGui::SetTooltip("Mirrors the world horizontally, matching the Wii version of the game.");
}
config::ImGuiCheckbox("Sun's Song (R+X)", getSettings().game.sunsSong);
config::ImGuiCheckbox("Disable Main HUD", getSettings().game.disableMainHUD);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Allows Wolf Link to howl and change the time of day.");
ImGui::SetTooltip("Disables the main HUD of the game.\n"
"Useful for recording or a more immersive experience!");
}
ImGui::SeparatorText("Difficulty");
config::ImGuiSliderInt("Damage Multiplier", getSettings().game.damageMultiplier, 1, 8, "x%d");
config::ImGuiCheckbox("Instant Death", getSettings().game.instantDeath);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Any hit will instantly kill you.");
}
config::ImGuiCheckbox("No Heart Drops", getSettings().game.noHeartDrops);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Hearts will never drop from enemies,\n"
"pots and various other places.");
}
ImGui::SeparatorText("Quality of Life");
config::ImGuiCheckbox("Bigger Wallets", getSettings().game.biggerWallets);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Wallet sizes are like in the HD version. (500, 1000, 2000)");
}
config::ImGuiCheckbox("No Rupee Returns", getSettings().game.noReturnRupees);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Always collect Rupees even if your Wallet is too full.");
}
config::ImGuiCheckbox("Disable Rupee Cutscenes", getSettings().game.disableRupeeCutscenes);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Rupees won't play cutscenes after you've collected them the first time.");
}
config::ImGuiCheckbox("No Sword Recoil", getSettings().game.noSwordRecoil);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Link won't recoil when his sword hits walls.");
}
config::ImGuiCheckbox("Faster Climbing", getSettings().game.fastClimbing);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Quicker climbing on ladders and vines like the HD version.");
}
config::ImGuiCheckbox("Faster Tears of Light", getSettings().game.fastTears);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Tears of Light dropped by Shadow Insects pop out faster like the HD version.");
}
config::ImGuiCheckbox("Instant Saves", getSettings().game.instantSaves);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Skip the delay when writing to the Memory Card.");
}
config::ImGuiCheckbox("No Climbing Miss Animation", getSettings().game.noMissClimbing);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Prevents Link from playing a struggle animation\n"
"when grabbing ledges or climbing on vines.");
}
config::ImGuiCheckbox("Faster Tears of Light", getSettings().game.fastTears);
config::ImGuiCheckbox("No Rupee Returns", getSettings().game.noReturnRupees);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Tears of Light dropped by Shadow Insects pop out faster like the HD version.");
ImGui::SetTooltip("Always collect Rupees even if your Wallet is too full.");
}
config::ImGuiCheckbox("Hide TV Settings Screen", getSettings().game.hideTvSettingsScreen);
config::ImGuiCheckbox("No Sword Recoil", getSettings().game.noSwordRecoil);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Hides the TV calibration screen shown when loading a save.");
ImGui::SetTooltip("Link won't recoil when his sword hits walls.");
}
config::ImGuiCheckbox("Skip TV Settings Screen", getSettings().game.hideTvSettingsScreen);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Skip the TV calibration screen shown when loading a save.");
}
config::ImGuiCheckbox("Skip Warning Screen", getSettings().game.skipWarningScreen);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Skips the warning screen shown when loading the game.");
ImGui::SetTooltip("Skip the warning screen shown when starting the game.");
}
config::ImGuiCheckbox("Instant Saves", getSettings().game.instantSaves);
config::ImGuiCheckbox("Sun's Song (R+X)", getSettings().game.sunsSong);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Skip the delay when writing to the Memory Card.");
ImGui::SetTooltip("Allows Wolf Link to howl and change the time of day.");
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Preferences")) {
config::ImGuiCheckbox("Mirror Mode", getSettings().game.enableMirrorMode);
config::ImGuiCheckbox("Quick Transform (R+Y)", getSettings().game.enableQuickTransform);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Mirrors the world, matching the Wii version of the game.");
}
config::ImGuiCheckbox("Invert Camera X Axis", getSettings().game.invertCameraXAxis);
config::ImGuiCheckbox("Disable Main HUD", getSettings().game.disableMainHUD);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Disables the main HUD of the game.\n"
"Useful for recording or a more immersive experience!");
ImGui::SetTooltip("Transform instantly by pressing R and Y simultaneously.");
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Graphics")) {
config::ImGuiSliderInt("Shadow Resolution", getSettings().game.shadowResolutionMultiplier, 1, 8, "x%d");
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Improves the shadow resolution, making them higher quality.");
}
config::ImGuiCheckbox("Unlock Framerate", getSettings().game.enableFrameInterpolation);
const bool frameInterpolationHovered = ImGui::IsItemHovered();
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.72f, 0.2f, 1.0f));
ImGui::TextUnformatted("[EXPERIMENTAL]");
ImGui::PopStyleColor();
if (frameInterpolationHovered || ImGui::IsItemHovered()) {
ImGui::SetTooltip("Uses inter-frame interpolation to enable higher frame rates.\nVisual artifacts, animation glitches, or instability may occur.");
}
config::ImGuiSliderInt("Shadow Resolution", getSettings().game.shadowResolutionMultiplier, 1, 8, "x%d");
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Improves the shadow resolution, making them higher quality.");
}
ImGui::EndMenu();
}
@@ -128,20 +138,62 @@ namespace dusk {
}
if (ImGui::BeginMenu("Input")) {
config::ImGuiCheckbox("Invert Camera X Axis", getSettings().game.invertCameraXAxis);
ImGui::SeparatorText("Gyro");
config::ImGuiCheckbox("Gyro Aim", getSettings().game.enableGyroAim);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Enables the gyroscope on supported controllers while aiming the\n"
"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 || 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.gyroSensitivityRollgoal, 0.25f, 4.0f, "%.2f");
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Additional multiplier for scaling how strongly\n"
"the gyroscope affects the Rollgoal table.");
}
}
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");
config::ImGuiCheckbox("Turbo Key", getSettings().game.enableTurboKeybind);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Hold TAB to increase game speed by up to 4x.");
}
ImGui::EndMenu();
}
ImGui::Separator();
if (ImGui::BeginMenu("Cheats")) {
config::ImGuiCheckbox("Fast Iron Boots", getSettings().game.enableFastIronBoots);
@@ -163,23 +215,6 @@ namespace dusk {
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Difficulty")) {
config::ImGuiSliderInt("Damage Multiplier", getSettings().game.damageMultiplier, 1, 8, "x%d");
config::ImGuiCheckbox("No Heart Drops", getSettings().game.noHeartDrops);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Hearts will never drop from enemies,\n"
"pots and various other places.");
}
config::ImGuiCheckbox("Instant Death", getSettings().game.instantDeath);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Any hit will instantly kill you.");
}
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Technical")) {
config::ImGuiCheckbox("Restore Wii 1.0 Glitches", getSettings().game.restoreWiiGlitches);
if (ImGui::IsItemHovered()) {
@@ -190,15 +225,6 @@ namespace dusk {
ImGui::EndMenu();
}
if (ImGui::BeginMenu("Tools")) {
config::ImGuiCheckbox("Turbo Key", getSettings().game.enableTurboKeybind);
if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Hold TAB to increase game speed by up to 4x.");
}
ImGui::EndMenu();
}
ImGui::EndMenu();
}
}
+14 -7
View File
@@ -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<float>(deadZones->leftTriggerActivationZone * 100.f) / 32767.f;
if (ImGui::DragFloat("##LThreshold", &tmp, 0.5f, 0.f, 100.f, "%.3f%%")) {
deadZones->leftTriggerActivationZone = static_cast<u16>((tmp / 100.f) * 32767);
PADSerializeMappings();
}
}
}
@@ -519,6 +519,7 @@ namespace dusk {
float tmp = static_cast<float>(deadZones->rightTriggerActivationZone * 100.f) / 32767.f;
if (ImGui::DragFloat("##RThreshold", &tmp, 0.5f, 0.f, 100.f, "%.3f%%")) {
deadZones->rightTriggerActivationZone = static_cast<u16>((tmp / 100.f) * 32767);
PADSerializeMappings();
}
}
}
@@ -581,6 +582,7 @@ namespace dusk {
float tmp = static_cast<float>(deadZones->stickDeadZone * 100.f) / 32767.f;
if (ImGui::DragFloat("##mainDeadZone", &tmp, 0.5f, 0.f, 100.f, "%.3f%%")) {
deadZones->stickDeadZone = static_cast<u16>((tmp / 100.f) * 32767);
PADSerializeMappings();
}
}
}
@@ -643,6 +645,7 @@ namespace dusk {
float tmp = static_cast<float>(deadZones->substickDeadZone * 100.f) / 32767.f;
if (ImGui::DragFloat("##subDeadZone", &tmp, 0.5f, 0.f, 100.f, "%.3f%%")) {
deadZones->substickDeadZone = static_cast<u16>((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();
+1
View File
@@ -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;
};
+1 -1
View File
@@ -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) {
+3
View File
@@ -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;
+38 -2
View File
@@ -7,6 +7,7 @@
#include "ImGuiConsole.hpp"
#include "dusk/main.h"
#include "dusk/settings.h"
#include "../iso_validate.hpp"
#include <SDL3/SDL_dialog.h>
#include <SDL3/SDL_error.h>
@@ -26,15 +27,42 @@ static constexpr std::array<SDL_DialogFileFilter, 2> skGameDiscFileFilters{{
{"All Files", "*"},
}};
static std::string ShowIsoInvalidError(const iso::ValidationError code) {
using namespace std::literals::string_literals;
switch (code) {
case iso::ValidationError::IOError:
return "Unknown IO error occurred"s;
case iso::ValidationError::InvalidImage:
return "Unable to interpret selected file as a disc image"s;
case iso::ValidationError::WrongGame:
return "Selected disc image is for a different game"s;
case iso::ValidationError::WrongVersion:
return "Selected disc image is for an unsupported version of the game. Only North American GameCube (NTSC/GZ2E01) is supported at this time."s;
case iso::ValidationError::ExecutableMismatch:
return "Selected disc image contains modified executable files."s;
default:
return "Unknown error"s;
}
}
void fileDialogCallback(void* userdata, const char* const* filelist, [[maybe_unused]] int filter) {
auto* self = static_cast<ImGuiPreLaunchWindow*>(userdata);
self->m_errorString.clear();
if (filelist != nullptr) {
if (filelist[0] == nullptr) {
// Cancelled
self->m_selectedIsoPath.clear();
} else {
self->m_selectedIsoPath = filelist[0];
getSettings().backend.isoPath.setValue(self->m_selectedIsoPath);
const auto path = filelist[0];
const auto ret = iso::validate(path);
if (ret != iso::ValidationError::Success) {
self->m_selectedIsoPath.clear();
self->m_errorString = std::move(ShowIsoInvalidError(ret));
return;
}
self->m_selectedIsoPath = path;
getSettings().backend.isoPath.setValue(path);
config::Save();
}
} else {
@@ -111,6 +139,10 @@ void ImGuiPreLaunchWindow::drawMainMenu() {
ImGui::PushFont(ImGuiEngine::fontLarge);
if (!isSelectedPathValid()) {
if (!m_errorString.empty()) {
ImGuiTextCenter(m_errorString);
}
if (ImGuiButtonCenter("Select disc image...")) {
SDL_ShowOpenFileDialog(&fileDialogCallback, this, aurora::window::get_sdl_window(),
skGameDiscFileFilters.data(), int(skGameDiscFileFilters.size()),
@@ -148,6 +180,10 @@ void ImGuiPreLaunchWindow::drawOptions() {
if (ImGui::BeginChild("OptionsChild", ImVec2(childWidth, endCursorY - cursorY),
ImGuiChildFlags_None, ImGuiWindowFlags_NoBackground))
{
if (!m_errorString.empty()) {
ImGuiTextCenter(m_errorString);
}
ImGui::InputText("Game ISO Path", &m_selectedIsoPath, ImGuiInputTextFlags_ReadOnly);
ImGui::SameLine();
if (ImGui::Button("Set")) {
+159 -51
View File
@@ -445,7 +445,7 @@ namespace dusk {
}
if (ImGui::BeginTabItem("Minigame")) {
//DrawFlagsTab();
drawMinigameTab();
ImGui::EndTabItem();
}
@@ -815,48 +815,88 @@ 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: ", slot);
ImGui::SameLine();
if (ImGui::BeginCombo(fmt::format("##ItemComboBox{}", slot).c_str(), itemMap.find(item.mItems[slot])->second.m_name.c_str())) {
if (ImGui::Selectable("None")) {
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();
}
dSv_player_item_record_c& itemRecord = dComIfGs_getSaveData()->getPlayer().getItemRecord();
dSv_player_item_max_c& itemMax = dComIfGs_getSaveData()->getPlayer().getItemMax();
ImGuiBeginGroupPanel("Item Max Capacities", { 200, 100 });
ImGui::InputScalar("Arrows Max", ImGuiDataType_U8, &itemMax.mItemMax[0]);
ImGui::InputScalar("Normal Bombs Max", ImGuiDataType_U8, &itemMax.mItemMax[1]);
ImGui::InputScalar("Water Bombs Max", ImGuiDataType_U8, &itemMax.mItemMax[2]);
ImGui::InputScalar("Bomblings Max", ImGuiDataType_U8, &itemMax.mItemMax[3]);
ImGuiEndGroupPanel();
ImGuiBeginGroupPanel("Item Amounts", { 200, 100 });
ImGui::InputScalar("Arrows Amount", ImGuiDataType_U8, &itemRecord.mArrowNum);
ImGui::InputScalar("Slingshot Amount", ImGuiDataType_U8, &itemRecord.mPachinkoNum);
ImGui::InputScalar("Bomb Bag 1 Amount", ImGuiDataType_U8, &itemRecord.mBombNum[0]);
ImGui::InputScalar("Bomb Bag 2 Amount", ImGuiDataType_U8, &itemRecord.mBombNum[1]);
ImGui::InputScalar("Bomb Bag 3 Amount", ImGuiDataType_U8, &itemRecord.mBombNum[2]);
ImGui::InputScalar("Bottle 1 Amount", ImGuiDataType_U8, &itemRecord.mBottleNum[0]);
ImGui::InputScalar("Bottle 2 Amount", ImGuiDataType_U8, &itemRecord.mBottleNum[1]);
ImGui::InputScalar("Bottle 3 Amount", ImGuiDataType_U8, &itemRecord.mBottleNum[2]);
ImGui::InputScalar("Bottle 4 Amount", ImGuiDataType_U8, &itemRecord.mBottleNum[3]);
ImGuiEndGroupPanel();
}
static inline void setItemFirstBit(u8 itemNo, bool owned) {
@@ -902,27 +942,42 @@ namespace dusk {
void ImGuiSaveEditor::drawCollectionTab() {
if (ImGui::TreeNode("Equipment")) {
if (ImGui::TreeNode("Swords")) {
static u8 sword_list[] = {
dItemNo_SWORD_e,
dItemNo_MASTER_SWORD_e,
dItemNo_WOOD_STICK_e,
dItemNo_LIGHT_SWORD_e,
};
for (int i = 0; i < 4; i++) {
bool got = dComIfGs_isCollectSword((u8)i) != 0;
bool got = dComIfGs_isItemFirstBit(sword_list[i]) != 0;
if (ImGui::Checkbox(fmt::format("{0}##sword_{1}", sSwordNames[i], i).c_str(), &got)) {
if (got) dComIfGs_setCollectSword((u8)i);
else dComIfGs_offCollectSword((u8)i);
if (got) dComIfGs_onItemFirstBit(sword_list[i]);
else dComIfGs_offItemFirstBit(sword_list[i]);
}
}
ImGui::TreePop();
}
if (ImGui::TreeNode("Shields")) {
static u8 shield_list[] = {
dItemNo_SHIELD_e,
dItemNo_WOOD_SHIELD_e,
dItemNo_HYLIA_SHIELD_e,
};
for (int i = 0; i < 3; i++) {
bool got = dComIfGs_isCollectShield((u8)i) != 0;
bool got = dComIfGs_isItemFirstBit(shield_list[i]) != 0;
if (ImGui::Checkbox(fmt::format("{0}##shield_{1}", sShieldNames[i], i).c_str(), &got)) {
if (got) dComIfGs_setCollectShield((u8)i);
else dComIfGs_offCollectShield((u8)i);
if (got) dComIfGs_onItemFirstBit(shield_list[i]);
else dComIfGs_offItemFirstBit(shield_list[i]);
}
}
ImGui::TreePop();
}
// TODO: the game checks if you're in ranch clothes, and if so won't display any other tunics in the menu
// find a way to deal with this later
if (ImGui::TreeNode("Tunics")) {
bool ordonClothes = dComIfGs_isItemFirstBit(dItemNo_WEAR_CASUAL_e) != 0;
if (ImGui::Checkbox("Ordon Clothes##tunic_ordon", &ordonClothes)) {
@@ -1228,6 +1283,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++) {
@@ -1235,7 +1300,7 @@ namespace dusk {
}
ImGuiEndGroupPanel();
ImVec2 cursor = ImGui::GetCursorPos();
ImVec2 post_tbox_cursor = ImGui::GetCursorPos();
ImGui::SameLine();
@@ -1245,13 +1310,38 @@ namespace dusk {
}
ImGuiEndGroupPanel();
ImGui::SetCursorPos(cursor);
ImVec2 post_switch_cursor = ImGui::GetCursorPos();
ImGui::SetCursorPos(post_tbox_cursor);
ImGuiBeginGroupPanel("Item", { 100, 100 });
for (int j = 0; j < 1; j++) {
drawFlagList(fmt::format("##_item{}", j).c_str(), membit.mItem[j]);
}
ImGuiEndGroupPanel();
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() {
@@ -1259,15 +1349,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();
@@ -1280,7 +1373,7 @@ namespace dusk {
"Faron",
"Eldin",
"Lanayru",
"Unknown",
"Reserved",
"Hyrule Field",
"Sacred Grove",
"Snowpeak",
@@ -1328,7 +1421,7 @@ namespace dusk {
dSv_event_c& event = dComIfGs_getSaveData()->mEvent;
for (int e = 0; e < 255; e++) {
ImGui::Text("%03d ", e);
ImGui::SameLine();
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)) {
@@ -1345,10 +1438,25 @@ namespace dusk {
}
}
void ImGuiSaveEditor::drawMinigameTab() {
dSv_MiniGame_c& minigame = dComIfGs_getSaveData()->getMiniGame();
InputScalarBE("STAR Game Time (Milliseconds)", ImGuiDataType_U32, &minigame.mHookGameTime);
InputScalarBE("Snowboard Race Time (Milliseconds)", ImGuiDataType_U32, &minigame.mRaceGameTime);
InputScalarBE("Fruit-Pop-Flight Score", ImGuiDataType_U32, &minigame.mBalloonScore);
}
void ImGuiSaveEditor::drawConfigTab() {
dSv_player_config_c& config = dComIfGs_getSaveData()->getPlayer().getConfig();
ImGui::Checkbox("Enable Vibration", (bool*)&config.mVibration);
if (ImGui::BeginCombo("Target Type", "Hold")) {
static const char* kTargetTypeNames[] = {"Hold", "Switch"};
if (ImGui::BeginCombo("Target Type", kTargetTypeNames[config.mAttentionType])) {
if (ImGui::Selectable("Hold")) {
config.mAttentionType = 0;
}
if (ImGui::Selectable("Switch")) {
config.mAttentionType = 1;
}
ImGui::EndCombo();
}
@@ -1366,4 +1474,4 @@ namespace dusk {
ImGui::EndCombo();
}
}
}
}
+1
View File
@@ -17,6 +17,7 @@ namespace dusk {
void drawInventoryTab();
void drawCollectionTab();
void drawFlagsTab();
void drawMinigameTab();
void drawConfigTab();
private:
+126
View File
@@ -0,0 +1,126 @@
#include "iso_validate.hpp"
#include <nod.h>
#include <span>
#include "SDL3/SDL_iostream.h"
namespace dusk::iso {
constexpr const char* TP_GAME_IDS[] = {
"GZ2E01", // GCN USA
"GZ2P01", // GCN PAL
"GZ2J01", // GCN JPN
"RZDE01", // Wii USA
"RZDP01", // Wii PAL
"RZDJ01", // Wii JPN
"RZDK01", // Wii KOR
};
constexpr const char* SUPPORTED_TP_GAME_IDS[] = {
"GZ2E01", // GCN USA
};
template <size_t N>
constexpr bool matches(const char (&id)[6], const char* const (&valid)[N]) {
for (auto elem : valid) {
if (strncmp(id, elem, 6) == 0) {
return true;
}
}
return false;
}
struct NodHandleWrapper {
NodHandle* handle;
NodHandleWrapper() : handle(nullptr) {
}
~NodHandleWrapper() {
if (handle != nullptr) {
nod_free(handle);
handle = nullptr;
}
}
};
static ValidationError convertNodError(NodResult result) {
switch (result) {
case NOD_RESULT_ERR_IO:
return ValidationError::IOError;
case NOD_RESULT_ERR_FORMAT:
return ValidationError::InvalidImage;
default:
return ValidationError::Unknown;
}
}
s64 StreamReadAt(void* user_data, u64 offset, void* out, size_t len) {
if (len == 0) {
return 0;
}
auto io = static_cast<SDL_IOStream*>(user_data);
auto ret = SDL_SeekIO(io, static_cast<s64>(offset), SDL_IO_SEEK_SET);
if (ret < 0) {
return -1;
}
auto read = SDL_ReadIO(io, out, len);
if (read == 0) {
if (SDL_GetIOStatus(io) == SDL_IO_STATUS_EOF) {
return 0;
}
return -1;
}
return static_cast<s64>(read);
}
s64 StreamLength(void* user_data) {
auto io = static_cast<SDL_IOStream*>(user_data);
return SDL_GetIOSize(io);
}
void StreamClose(void* user_data) {
auto io = static_cast<SDL_IOStream*>(user_data);
SDL_CloseIO(io);
}
ValidationError validate(const char* path) {
NodHandleWrapper disc;
const auto sdlStream = SDL_IOFromFile(path, "rb");
const NodDiscStream nod_stream {
.user_data = sdlStream,
.read_at = StreamReadAt,
.stream_len = StreamLength,
.close = StreamClose,
};
auto result = nod_disc_open_stream(&nod_stream, nullptr, &disc.handle);
if (disc.handle == nullptr || result != NOD_RESULT_OK) {
return convertNodError(result);
}
NodDiscHeader header{};
result = nod_disc_header(disc.handle, &header);
if (result != NOD_RESULT_OK) {
return convertNodError(result);
}
if (!matches(header.game_id, TP_GAME_IDS)) {
return ValidationError::WrongGame;
}
if (!matches(header.game_id, SUPPORTED_TP_GAME_IDS)) {
return ValidationError::WrongVersion;
}
return ValidationError::Success;
}
} // namespace dusk::iso
+18
View File
@@ -0,0 +1,18 @@
#ifndef DUSK_ISO_VALIDATE_HPP
#define DUSK_ISO_VALIDATE_HPP
namespace dusk::iso {
enum class ValidationError : u8 {
Success = 0,
IOError,
InvalidImage,
WrongGame,
WrongVersion,
ExecutableMismatch,
Unknown
};
ValidationError validate(const char* path);
}
#endif // DUSK_ISO_VALIDATE_HPP
+19 -9
View File
@@ -55,10 +55,14 @@ UserSettings g_userSettings = {
// Input
.enableGyroAim {"game.enableGyroAim", false},
.gyroAimSensitivityX {"game.gyroAimSensitivityX", 1.0f},
.gyroAimSensitivityY {"game.gyroAimSensitivityY", 1.0f},
.gyroAimInvertPitch {"game.gyroAimInvertPitch", false},
.gyroAimInvertYaw {"game.gyroAimInvertYaw", false},
.enableGyroRollgoal {"game.enableGyroRollgoal", 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},
@@ -79,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}
}
};
@@ -134,10 +139,14 @@ void registerSettings() {
Register(g_userSettings.game.fastSpinner);
Register(g_userSettings.game.enableFrameInterpolation);
Register(g_userSettings.game.enableGyroAim);
Register(g_userSettings.game.gyroAimSensitivityX);
Register(g_userSettings.game.gyroAimSensitivityY);
Register(g_userSettings.game.gyroAimInvertPitch);
Register(g_userSettings.game.gyroAimInvertYaw);
Register(g_userSettings.game.enableGyroRollgoal);
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);
@@ -145,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
-1
View File
@@ -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();
}
+2 -2
View File
@@ -2383,8 +2383,8 @@ void mDoExt_3DlineMat0_c::draw() {
int var_r26 = (field_0x14 << 1) & 0xFFFF;
for (int i = 0; i < field_0x10; i++) {
GXSETARRAY(GX_VA_POS, field_0x18->field_0x8[field_0x16], sizeof(cXyz) * var_r26, sizeof(cXyz), true);
GXSETARRAY(GX_VA_NRM, field_0x18->field_0x10[field_0x16], 3 * var_r26, 3, true);
GXSETARRAY(GX_VA_POS, var_r28->field_0x8[field_0x16], sizeof(cXyz) * var_r26, sizeof(cXyz), true);
GXSETARRAY(GX_VA_NRM, var_r28->field_0x10[field_0x16], 3 * var_r26, 3, true);
GXBegin(GX_TRIANGLESTRIP, GX_VTXFMT0, var_r26);
for (u16 j = 0; j < (u16)var_r26; j++) {
+18 -16
View File
@@ -1344,6 +1344,11 @@ void mDoGph_gInf_c::bloom_c::draw2() {
static_cast<u16>(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];
@@ -1390,7 +1395,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 +1437,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++) {
@@ -1488,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);
@@ -1503,7 +1508,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);
@@ -2229,13 +2233,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
+7 -8
View File
@@ -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,23 +245,22 @@ 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<float>(kSimStepSeconds), dusk::gyro_aim::queryGyroAimItemContext());
}
dusk::gyro::read(kSimStepSeconds);
fapGm_Execute();
mDoAud_Execute();
accumulator -= kSimStepSeconds;
}
dusk::frame_interp::interpolate(static_cast<float>(accumulator / kSimStepSeconds));
cAPIGph_Painter();
{
dusk::frame_interp::PresentationCameraScope presentation_camera;
cAPIGph_Painter();
}
} else {
accumulator = 0.0;
// Game Inputs
mDoCPd_c::read();
if (dusk::getSettings().game.enableGyroAim) {
dusk::gyro_aim::read(static_cast<float>(frame_seconds), dusk::gyro_aim::queryGyroAimItemContext());
}
dusk::gyro::read(frame_seconds);
// EXECUTE GAME LOGIC & RENDER
// This calls mDoGph_Painter -> JFWDisplay -> GX Functions