mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-05-24 15:00:55 -04:00
Merge remote-tracking branch 'origin/main' into ci-2
This commit is contained in:
+1
-1
@@ -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")
|
||||
|
||||
Vendored
+1
-1
Submodule extern/aurora updated: 4dd23c74d8...aa83f6d915
+4
-1
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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(¢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() {
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
namespace dusk {
|
||||
void DrawBloomWindow(bool& open);
|
||||
void ApplyBloomOverride();
|
||||
} // namespace dusk
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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")) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ namespace dusk {
|
||||
void drawInventoryTab();
|
||||
void drawCollectionTab();
|
||||
void drawFlagsTab();
|
||||
void drawMinigameTab();
|
||||
void drawConfigTab();
|
||||
|
||||
private:
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user