mirror of
https://github.com/TwilitRealm/dusklight
synced 2026-08-17 04:31:37 -04:00
Merge remote-tracking branch 'origin/main' into feature/autosave
# Conflicts: # src/dusk/imgui/ImGuiMenuEnhancements.cpp # src/dusk/settings.cpp # src/f_ap/f_ap_game.cpp
This commit is contained in:
@@ -117,8 +117,8 @@ static Z2WolfHowlLine sNewSong3[9] = {
|
||||
|
||||
#if TARGET_PC
|
||||
static Z2WolfHowlLine sHowlTimeSong[6] = {
|
||||
{HOWL_LINE_MID, 20}, {HOWL_LINE_LOW, 20}, {HOWL_LINE_HIGH, 40},
|
||||
{HOWL_LINE_MID, 20}, {HOWL_LINE_LOW, 20}, {HOWL_LINE_HIGH, 40},
|
||||
{HOWL_LINE_MID, 15}, {HOWL_LINE_LOW, 15}, {HOWL_LINE_HIGH, 30},
|
||||
{HOWL_LINE_MID, 15}, {HOWL_LINE_LOW, 15}, {HOWL_LINE_HIGH, 30},
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -368,9 +368,9 @@ void Z2WolfHowlMgr::setCorrectData(s8 curveID, Z2WolfHowlData* data) {
|
||||
break;
|
||||
#if TARGET_PC
|
||||
case Z2WOLFHOWL_TIMESONG:
|
||||
cPitchUp = 1.259906f;
|
||||
cPitchCenter = 0.94387f;
|
||||
cPitchDown = 0.840885f;
|
||||
cPitchUp = 1.3348f;
|
||||
cPitchCenter = 0.8909f;
|
||||
cPitchDown = 0.7937f;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
|
||||
@@ -41,7 +41,7 @@ void daAlink_c::handleWolfHowl() {
|
||||
return;
|
||||
}
|
||||
|
||||
bool canTransform = false;
|
||||
bool canHowl = false;
|
||||
|
||||
if (mLinkAcch.ChkGroundHit() && !checkModeFlg(MODE_PLAYER_FLY) && !checkMagneBootsOn()) {
|
||||
if (!checkForestOldCentury()) {
|
||||
@@ -52,12 +52,17 @@ void daAlink_c::handleWolfHowl() {
|
||||
(checkEventRun() || getMidnaActor()->checkMetamorphoseEnable()) &&
|
||||
(checkModeFlg(4) || dComIfGp_checkPlayerStatus0(0, 0x10))))
|
||||
{
|
||||
canTransform = true;
|
||||
canHowl = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!canHowl) {
|
||||
Z2GetAudioMgr()->seStart(Z2SE_SYS_ERROR, NULL, 0, 0, 1.0f, 1.0f, -1.0f, -1.0f, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
getWolfHowlMgrP()->setCorrectCurve(9);
|
||||
procWolfHowlDemoInit();
|
||||
}
|
||||
@@ -154,6 +159,7 @@ bool daAlink_c::checkGyroAimContext() {
|
||||
case PROC_BOW_SUBJECT:
|
||||
case PROC_BOOMERANG_SUBJECT:
|
||||
case PROC_COPY_ROD_SUBJECT:
|
||||
case PROC_HAWK_SUBJECT:
|
||||
case PROC_HOOKSHOT_SUBJECT:
|
||||
case PROC_SWIM_HOOKSHOT_SUBJECT:
|
||||
case PROC_HORSE_BOW_SUBJECT:
|
||||
|
||||
@@ -149,6 +149,23 @@ void daAlink_c::changeWolf() {
|
||||
|
||||
mpLinkModel = initModel(static_cast<J3DModelData*>(dComIfG_getObjectRes(l_wArcName, 14)), 0x20200);
|
||||
|
||||
#ifdef TARGET_PC
|
||||
// Update Wolf Link's eye maxLOD to prevent the eyes from disappearing
|
||||
{
|
||||
J3DTexture* tex = mpLinkModel->getModelData()->getTexture();
|
||||
JUTNameTab* nametable = mpLinkModel->getModelData()->getTextureName();
|
||||
if (tex != nullptr && nametable != nullptr) {
|
||||
for (u16 i = 0; i < tex->getNum(); i++) {
|
||||
const char* tex_name = nametable->getName(i);
|
||||
if (tex_name != NULL && strcmp(tex_name, "wl_eyeball") == 0) {
|
||||
ResTIMG* timg = tex->getResTIMG(i);
|
||||
timg->maxLOD = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
J3DModelData* chainModelData = static_cast<J3DModelData*>(dComIfG_getObjectRes(l_wArcName, 15));
|
||||
for (u16 i = 0; i < 4; i++) {
|
||||
mpWlChainModels[i] = initModel(chainModelData, 0);
|
||||
@@ -162,6 +179,23 @@ void daAlink_c::changeWolf() {
|
||||
mpWlMidnaHairModel =
|
||||
initModelEnv(static_cast<J3DModelData*>(dComIfG_getObjectRes(l_wArcName, 11)), 0x1000000);
|
||||
|
||||
#ifdef TARGET_PC
|
||||
// Update Midna's eye maxLOD to prevent the eyes from disappearing
|
||||
{
|
||||
J3DTexture* tex = mpWlMidnaModel->getModelData()->getTexture();
|
||||
JUTNameTab* nametable = mpWlMidnaModel->getModelData()->getTextureName();
|
||||
if (tex != nullptr && nametable != nullptr) {
|
||||
for (u16 i = 0; i < tex->getNum(); i++) {
|
||||
const char* tex_name = nametable->getName(i);
|
||||
if (tex_name != NULL && strcmp(tex_name, "midona_eyeball") == 0) {
|
||||
ResTIMG* timg = tex->getResTIMG(i);
|
||||
timg->maxLOD = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
mpDMidnaBrk = static_cast<J3DAnmTevRegKey*>(dComIfG_getObjectRes(l_wArcName, 18));
|
||||
mpDMidnaBrk->searchUpdateMaterialID(mpWlMidnaModel->getModelData());
|
||||
mpWlMidnaModel->getModelData()->entryTevRegAnimator(mpDMidnaBrk);
|
||||
@@ -342,6 +376,26 @@ void daAlink_c::changeLink(int param_0) {
|
||||
initModel(static_cast<J3DModelData*>(dComIfG_getObjectRes(mArcName, "zl_face.bmd")), 0x20200);
|
||||
}
|
||||
|
||||
#ifdef TARGET_PC
|
||||
// Update Adult Link's eye maxLOD to prevent the eyes from disappearing
|
||||
{
|
||||
J3DTexture* tex = mpLinkFaceModel->getModelData()->getTexture();
|
||||
JUTNameTab* nametable = mpLinkFaceModel->getModelData()->getTextureName();
|
||||
if (tex != nullptr && nametable != nullptr) {
|
||||
for (u16 i = 0; i < tex->getNum(); i++) {
|
||||
const char* tex_name = nametable->getName(i);
|
||||
if (tex_name != nullptr &&
|
||||
(strcmp(tex_name, "al_eyeball") == 0 || strcmp(tex_name, "highlight02") == 0 ||
|
||||
strcmp(tex_name, "eye_kage01") == 0))
|
||||
{
|
||||
ResTIMG* timg = tex->getResTIMG(i);
|
||||
timg->maxLOD = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
modelData = static_cast<J3DModelData*>(dComIfG_getObjectRes(mArcName, "al_bootsH.bmd"));
|
||||
u16 i;
|
||||
for (i = 0; i < 2; i++) {
|
||||
|
||||
@@ -2059,7 +2059,15 @@ static void demo_camera(b_bq_class* i_this) {
|
||||
for (int i = 0; i < 5; i++) {
|
||||
static u16 g_e_i[] = {0x83EB, 0x83EC, 0x83ED, 0x83EE, 0x83EF};
|
||||
|
||||
dComIfGp_particle_set(g_e_i[i], &pos, NULL, NULL);
|
||||
#if TARGET_PC
|
||||
if (i == 0) {
|
||||
static const cXyz effWideScale = {mDoGph_gInf_c::getAspect(), 1.0f, 1.0f};
|
||||
dComIfGp_particle_set(g_e_i[i], &pos, NULL, &effWideScale);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
dComIfGp_particle_set(g_e_i[i], &pos, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
i_this->mSound.startCreatureSound(Z2SE_EN_BOSS_CONVERGE, 0, 0);
|
||||
|
||||
@@ -2725,7 +2725,16 @@ static void demo_camera(b_ob_class* i_this) {
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
static u16 ex_eff[] = {dPa_RM(ID_ZI_S_OI_CONVERGE_FILTER), dPa_RM(ID_ZI_S_OI_CONVERGE_FILTEROUT), dPa_RM(ID_ZI_S_OI_CONVERGE_HIDE), dPa_RM(ID_ZI_S_OI_CONVERGE_POLYGON_A), dPa_RM(ID_ZI_S_OI_CONVERGE_POLYGON_B)};
|
||||
dComIfGp_particle_set(ex_eff[i], &room_pos, NULL, &sc);
|
||||
|
||||
#if TARGET_PC
|
||||
if (i == 0) {
|
||||
static const cXyz effWideScale = {mDoGph_gInf_c::getAspect() * 10.0f, 10.0f, 10.0f};
|
||||
dComIfGp_particle_set(ex_eff[i], &room_pos, NULL, &effWideScale);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
dComIfGp_particle_set(ex_eff[i], &room_pos, NULL, &sc);
|
||||
}
|
||||
}
|
||||
|
||||
i_this->mDemoCamEye.set(-4820.0f, -18600.0f, -510.0f);
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#if TARGET_PC
|
||||
#include <f_ap/f_ap_game.h>
|
||||
#include <dusk/autosave.h>
|
||||
#endif
|
||||
|
||||
char* daDoor20_c::getStopBmdName() {
|
||||
|
||||
@@ -1677,7 +1677,16 @@ static void demo_camera(e_fm_class* i_this) {
|
||||
cXyz spBC(0.0f, 0.0f, 0.0f);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
static u16 g_e_i[] = {0x847B, 0x847C, 0x847D, 0x847E};
|
||||
dComIfGp_particle_set(g_e_i[i], &spBC, NULL, NULL);
|
||||
|
||||
#if TARGET_PC
|
||||
if (i == 0) {
|
||||
static const cXyz effWideScale = {mDoGph_gInf_c::getAspect(), 1.0f, 1.0f};
|
||||
dComIfGp_particle_set(g_e_i[i], &spBC, NULL, &effWideScale);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
dComIfGp_particle_set(g_e_i[i], &spBC, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
i_this->mDemoCamFovy = 55.0f + NREG_F(10);
|
||||
|
||||
@@ -761,6 +761,11 @@ static void koro2_game(fshop_class* i_this) {
|
||||
sp5C.x = mDoCPd_c::getStickX3D(PAD_1);
|
||||
sp5C.y = 0.0f;
|
||||
sp5C.z = mDoCPd_c::getStickY(PAD_1);
|
||||
#if TARGET_PC
|
||||
if (dusk::getSettings().game.enableMirrorMode) {
|
||||
sp5C.x = -sp5C.x;
|
||||
}
|
||||
#endif
|
||||
MtxPosition(&sp5C, &sp68);
|
||||
|
||||
f32 reg_f31 = sp68.x;
|
||||
@@ -782,20 +787,15 @@ static void koro2_game(fshop_class* i_this) {
|
||||
reg_f30 = 0.0f;
|
||||
}
|
||||
|
||||
s16 gyro_ax = 0;
|
||||
s16 gyro_az = 0;
|
||||
#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);
|
||||
dusk::gyro::rollgoalTableOffset(gyro_ax, gyro_az);
|
||||
}
|
||||
#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
|
||||
cLib_addCalcAngleS2(&i_this->field_0x4020.x, reg_f30 * (-6000.0f + JREG_F(7)) + gyro_ax, 4, 0x200);
|
||||
cLib_addCalcAngleS2(&i_this->field_0x4020.z, reg_f31 * (-6000.0f + JREG_F(8)) + gyro_az, 4, 0x200);
|
||||
}
|
||||
#if TARGET_PC
|
||||
if (i_this->field_0x4010 != 2) {
|
||||
|
||||
@@ -30,6 +30,10 @@ static char* l_arcName = "Mirror";
|
||||
static char* l_arcName2 = "MR-Table";
|
||||
|
||||
dMirror_packet_c::dMirror_packet_c() {
|
||||
#ifdef TARGET_PC
|
||||
GXInitTexObj(&mTexObj, nullptr, 0, 0, static_cast<GXTexFmt>(-1), GX_MAX_TEXWRAPMODE,
|
||||
GX_MAX_TEXWRAPMODE, GX_FALSE);
|
||||
#endif
|
||||
reset();
|
||||
}
|
||||
|
||||
|
||||
@@ -2855,7 +2855,7 @@ void* daMP_Reader(void*) {
|
||||
#endif
|
||||
}
|
||||
|
||||
static u8 daMP_ReadThreadStack[0x2000];
|
||||
static u8 daMP_ReadThreadStack[DUSK_IF_ELSE(8, 0x2000)];
|
||||
|
||||
#if TARGET_PC
|
||||
static BOOL VideoThreadCancelled;
|
||||
@@ -2880,7 +2880,7 @@ static BOOL daMP_CreateReadThread(s32 param_0) {
|
||||
|
||||
static OSThread daMP_VideoDecodeThread;
|
||||
|
||||
static u8 daMP_VideoDecodeThreadStack[0x64000];
|
||||
static u8 daMP_VideoDecodeThreadStack[DUSK_IF_ELSE(8, 0x64000)];
|
||||
|
||||
static OSMessageQueue daMP_FreeTextureSetQueue;
|
||||
|
||||
@@ -3132,7 +3132,7 @@ static BOOL AudioThreadCancelled;
|
||||
|
||||
static OSThread daMP_AudioDecodeThread;
|
||||
|
||||
static u8 daMP_AudioDecodeThreadStack[0x64000];
|
||||
static u8 daMP_AudioDecodeThreadStack[DUSK_IF_ELSE(8, 0x64000)];
|
||||
|
||||
static OSMessageQueue daMP_FreeAudioBufferQueue;
|
||||
|
||||
@@ -4580,3 +4580,12 @@ actor_process_profile_definition g_profile_MOVIE_PLAYER = {
|
||||
};
|
||||
|
||||
AUDIO_INSTANCES;
|
||||
|
||||
#if TARGET_PC
|
||||
void dusk::MoviePlayerShutdown() {
|
||||
// We need to cleanly shut down the threads to avoid crashes on shutdown.
|
||||
if (daMP_c::m_myObj) {
|
||||
daMP_c::m_myObj->daMP_c_Finish();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -16,6 +16,10 @@
|
||||
#include "f_op/f_op_camera_mng.h"
|
||||
#include "m_Do/m_Do_mtx.h"
|
||||
|
||||
#if TARGET_PC
|
||||
#include "dusk/frame_interpolation.h"
|
||||
#endif
|
||||
|
||||
static f32 Reflect(cXyz* i_vec, cBgS_PolyInfo const& i_polyinfo, f32 i_scale) {
|
||||
cM3dGPla plane;
|
||||
|
||||
@@ -31,6 +35,27 @@ static f32 Reflect(cXyz* i_vec, cBgS_PolyInfo const& i_polyinfo, f32 i_scale) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
#if TARGET_PC
|
||||
static void d_a_obj_item_interp_callback(bool isSimFrame, void* pUserWork) {
|
||||
daItem_c* item = static_cast<daItem_c*>(pUserWork);
|
||||
if (item == NULL || item->mpModel == NULL || !item->chkDraw()) {
|
||||
return;
|
||||
}
|
||||
item->setTevStr();
|
||||
if (item->mpBrkAnm != NULL) {
|
||||
s8 tevFrm = item->getTevFrm();
|
||||
if (tevFrm != -1) {
|
||||
item->mpBrkAnm->entry(item->mpModel->getModelData(), tevFrm);
|
||||
} else {
|
||||
item->mpBrkAnm->entry(item->mpModel->getModelData());
|
||||
}
|
||||
}
|
||||
if (item->chkFlag(4)) {
|
||||
fopAcM_setEffectMtx(item, item->mpModel->getModelData());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
const daItemBase_data& daItemBase_c::getData() {
|
||||
return m_data;
|
||||
}
|
||||
@@ -353,6 +378,10 @@ int daItem_c::_daItem_draw() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if TARGET_PC
|
||||
dusk::frame_interp::add_interpolation_callback(&d_a_obj_item_interp_callback, this);
|
||||
#endif
|
||||
|
||||
if (chkDraw()) {
|
||||
return DrawBase();
|
||||
}
|
||||
@@ -390,6 +419,9 @@ void daItem_c::procMainNormal() {
|
||||
cLib_chaseF(&scale.z, mItemScale.z, step_z);
|
||||
}
|
||||
|
||||
#if TARGET_PC
|
||||
if (!dusk::getSettings().game.enableIndefiniteItemDrops) {
|
||||
#endif
|
||||
if (mWaitTimer == 0) {
|
||||
if (mDisappearTimer == 0) {
|
||||
deleteItem();
|
||||
@@ -399,6 +431,9 @@ void daItem_c::procMainNormal() {
|
||||
changeDraw();
|
||||
}
|
||||
}
|
||||
#if TARGET_PC
|
||||
}
|
||||
#endif
|
||||
|
||||
mCcCyl.SetC(current.pos);
|
||||
dComIfG_Ccsp()->Set(&mCcCyl);
|
||||
@@ -1058,9 +1093,16 @@ int daItem_c::CountTimer() {
|
||||
if (checkCountTimer()) {
|
||||
if (mWaitTimer > 0) {
|
||||
mWaitTimer--;
|
||||
} else if (mDisappearTimer > 0) {
|
||||
}
|
||||
#if TARGET_PC
|
||||
else if (!dusk::getSettings().game.enableIndefiniteItemDrops && mDisappearTimer > 0) {
|
||||
mDisappearTimer--;
|
||||
}
|
||||
#else
|
||||
else if (mDisappearTimer > 0) {
|
||||
mDisappearTimer--;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
cLib_calcTimer<u8>(&mBoomWindTgTimer);
|
||||
|
||||
@@ -699,8 +699,8 @@ void dFlower_packet_c::draw() {
|
||||
if (!cLib_checkBit<u8>(sp44->m_state, 4) && !cLib_checkBit<u8>(sp44->m_state, 0x40)) {
|
||||
#ifdef TARGET_PC
|
||||
Mtx flower_mtx;
|
||||
if (dusk::frame_interp::lookup_replacement(reinterpret_cast<const void*>(&sp44->m_modelMtx), flower_mtx)) {
|
||||
|
||||
if (dusk::frame_interp::lookup_replacement(&sp44->m_modelMtx, flower_mtx)) {
|
||||
cMtx_concat(j3dSys.getViewMtx(), flower_mtx, flower_mtx);
|
||||
GXLoadPosMtxImm(flower_mtx, 0);
|
||||
} else
|
||||
#endif
|
||||
@@ -854,21 +854,18 @@ void dFlower_packet_c::draw() {
|
||||
if (!cLib_checkBit<u8>(sp34->m_state, 4) && cLib_checkBit<u8>(sp34->m_state, 0x40)) {
|
||||
#ifdef TARGET_PC
|
||||
Mtx flower_mtx;
|
||||
if (dusk::frame_interp::lookup_replacement(reinterpret_cast<const void*>(&sp34->m_modelMtx), flower_mtx)) {
|
||||
if (dusk::frame_interp::lookup_replacement(&sp34->m_modelMtx, flower_mtx)) {
|
||||
cMtx_concat(j3dSys.getViewMtx(), flower_mtx, flower_mtx);
|
||||
GXLoadPosMtxImm(flower_mtx, 0);
|
||||
} else {
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
GXLoadPosMtxImm(sp34->m_modelMtx, 0);
|
||||
#ifdef TARGET_PC
|
||||
}
|
||||
#endif
|
||||
GXLoadNrmMtxImm(j3dSys.getViewMtx(), 0);
|
||||
|
||||
#if TARGET_PC
|
||||
GXLoadTexObj(&mTexObj_l_J_Ohana01_64128_0419TEX, GX_TEXMAP0);
|
||||
#endif
|
||||
|
||||
if (!cLib_checkBit<u8>(sp34->m_state, 8)) {
|
||||
if (!cLib_checkBit<u8>(sp34->m_state, 0x10)) {
|
||||
GXCallDisplayList(mp_Jhana01DL, m_Jhana01DL_size);
|
||||
@@ -995,7 +992,7 @@ void dFlower_packet_c::update() {
|
||||
mDoMtx_stack_c::scaleM(temp_f31, temp_f31, temp_f31);
|
||||
cMtx_concat(j3dSys.getViewMtx(), temp_r28, data_p->m_modelMtx);
|
||||
#ifdef TARGET_PC
|
||||
dusk::frame_interp::record_final_mtx(mDoMtx_stack_c::get(), data_p->m_modelMtx);
|
||||
dusk::frame_interp::record_final_mtx(temp_r28, data_p->m_modelMtx);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,11 +70,7 @@ dFs_HIO_c::dFs_HIO_c() {
|
||||
select_icon_appear_frames = 5;
|
||||
appear_display_wait_frames = 15;
|
||||
field_0x000d = 15;
|
||||
#if TARGET_PC
|
||||
card_wait_frames = 0;
|
||||
#else
|
||||
card_wait_frames = 90;
|
||||
#endif
|
||||
test_frame_counts[0] = 1.11f;
|
||||
test_frame_counts[1] = 1.11f;
|
||||
test_frame_counts[2] = 1.11f;
|
||||
@@ -2367,7 +2363,7 @@ void dFile_select_c::CommandExec() {
|
||||
break;
|
||||
}
|
||||
|
||||
mWaitTimer = g_fsHIO.card_wait_frames;
|
||||
mWaitTimer = IF_DUSK(dusk::getSettings().game.instantSaves ? 0 :) g_fsHIO.card_wait_frames;
|
||||
}
|
||||
|
||||
void dFile_select_c::DataEraseWait() {
|
||||
@@ -4759,7 +4755,7 @@ void dFile_select_c::MemCardFormatYesSel2Disp() {
|
||||
bool isErrorTxtChange = errorTxtChangeAnm();
|
||||
bool isYnMenuMove = yesnoMenuMoveAnm();
|
||||
if (isErrorTxtChange == true && isYnMenuMove == true) {
|
||||
mWaitTimer = g_fsHIO.card_wait_frames;
|
||||
mWaitTimer = IF_DUSK(dusk::getSettings().game.instantSaves ? 0 :) g_fsHIO.card_wait_frames;
|
||||
mDoMemCd_Format();
|
||||
mCardCheckProc = MEMCARDCHECKPROC_FORMAT;
|
||||
}
|
||||
@@ -4830,7 +4826,7 @@ void dFile_select_c::MemCardMakeGameFileSelDisp() {
|
||||
|
||||
if (isErrorTxtChange == true && isYnMenuMove == true && isKetteiTxtDisp == true) {
|
||||
if (field_0x0268 != 0) {
|
||||
mWaitTimer = g_fsHIO.card_wait_frames;
|
||||
mWaitTimer = IF_DUSK(dusk::getSettings().game.instantSaves ? 0 :) g_fsHIO.card_wait_frames;
|
||||
setInitSaveData();
|
||||
dataSave();
|
||||
mCardCheckProc = MEMCARDCHECKPROC_MAKE_GAMEFILE;
|
||||
|
||||
@@ -237,6 +237,13 @@ void dDrawPath_c::rendering(dDrawPath_c::line_class const* p_line) {
|
||||
if (isDrawType(p_line->field_0x0)) {
|
||||
int width = getLineWidth(p_line->field_0x1);
|
||||
|
||||
#if TARGET_PC
|
||||
f32 height = JUTVideo::getManager()->getRenderHeight() / 448.0f;
|
||||
if (height > 1.0f) {
|
||||
width /= 2;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (width > 0 && p_line->mDataNum >= 2) {
|
||||
GXSetLineWidth(width, GX_TO_ZERO);
|
||||
GXSetTevColor(GX_TEVREG0, *getLineColor(p_line->field_0x0 & 0x3F, p_line->field_0x1));
|
||||
|
||||
@@ -2399,6 +2399,13 @@ void dMenu_Collect3D_c::_move(u8 param_0, u8 param_1) {
|
||||
posZ = 550.0f;
|
||||
}
|
||||
toItem3Dpos(linkPos.x, posY, posZ, &itemPos);
|
||||
|
||||
#if TARGET_PC
|
||||
if (dusk::getSettings().game.enableLinkDollRotation) {
|
||||
const f32 angle = mDoCPd_c::getSubStickX3D(PAD_1) * 2048;
|
||||
ANGLE_ADD(mLinkAngle, angle);
|
||||
} else
|
||||
#endif
|
||||
if (param_0 == 0 && param_1 == 0) {
|
||||
f32 temp = 450.0f;
|
||||
ANGLE_ADD(mLinkAngle, temp);
|
||||
|
||||
+42
-1
@@ -21,6 +21,7 @@
|
||||
#include "d/d_msg_string.h"
|
||||
#include "d/d_meter_haihai.h"
|
||||
#include "d/d_menu_window.h"
|
||||
#include "dusk/settings.h"
|
||||
#include "f_op/f_op_msg_mng.h"
|
||||
#include "m_Do/m_Do_graphic.h"
|
||||
#include <cstring>
|
||||
@@ -945,9 +946,15 @@ void dMenu_DmapBg_c::draw() {
|
||||
mpMeterHaihai->drawHaihai(field_0xdda,
|
||||
x1 + (local_224.x + local_218.x) / 2,
|
||||
y1 + (local_224.y + local_218.y) / 2,
|
||||
-35.0f + (local_224.x - local_218.x),
|
||||
-35.0f + (local_224.x - local_218.x),
|
||||
-35.0f + (local_224.y - local_218.y));
|
||||
#if TARGET_PC
|
||||
if (!dusk::getSettings().game.enableFrameInterpolation) {
|
||||
field_0xdda = 0;
|
||||
}
|
||||
#else
|
||||
field_0xdda = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
dMenu_Dmap_c::myclass->drawFloorScreenTop(mFloorScreen, field_0xd94, field_0xd98, grafContext);
|
||||
@@ -984,7 +991,36 @@ void dMenu_DmapBg_c::update() {
|
||||
JUT_ASSERT(2323, mpBackTexture != NULL);
|
||||
|
||||
void* spec = mpArchive->getResource("spec/spec.dat");
|
||||
#if TARGET_PC
|
||||
struct dmap_spec {
|
||||
/* 0x00 */ BE(f32) field_0x0;
|
||||
/* 0x04 */ BE(f32) field_0x4;
|
||||
/* 0x08 */ BE(f32) field_0x8;
|
||||
/* 0x0C */ u8 field_0xc;
|
||||
/* 0x0D */ u8 field_0xd;
|
||||
/* 0x0E */ u8 field_0xe;
|
||||
/* 0x0F */ u8 field_0xf;
|
||||
/* 0x10 */ u8 field_0x10;
|
||||
/* 0x11 */ u8 field_0x11;
|
||||
/* 0x12 */ u8 field_0x12;
|
||||
/* 0x13 */ u8 field_0x13;
|
||||
};
|
||||
dmap_spec* dspec = (dmap_spec*)spec;
|
||||
|
||||
field_0xd80 = dspec->field_0x0;
|
||||
field_0xd84 = dspec->field_0x4;
|
||||
field_0xd88 = dspec->field_0x8;
|
||||
field_0xd8c = dspec->field_0xc;
|
||||
field_0xd8d = dspec->field_0xd;
|
||||
field_0xd8e = dspec->field_0xe;
|
||||
field_0xd8f = dspec->field_0xf;
|
||||
field_0xd90 = dspec->field_0x10;
|
||||
field_0xd91 = dspec->field_0x11;
|
||||
field_0xd92 = dspec->field_0x12;
|
||||
field_0xd93 = dspec->field_0x13;
|
||||
#else
|
||||
memcpy(&field_0xd80, spec, 20);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2545,6 +2581,11 @@ void dMenu_Dmap_c::zoomIn_proc() {
|
||||
}
|
||||
|
||||
void dMenu_Dmap_c::zoomOut_init_proc() {
|
||||
#if TARGET_PC
|
||||
if (dusk::getSettings().game.enableFrameInterpolation) {
|
||||
mpDrawBg->resetScrollArrowMask();
|
||||
}
|
||||
#endif
|
||||
Z2GetAudioMgr()->seStart(Z2SE_SY_MAP_ZOOMOUT, NULL, 0, 0, 1.0f, 1.0f, -1.0f, -1.0f, 0);
|
||||
mMapCtrl->initZoomOut(10);
|
||||
mpDrawBg->iconScaleAnmInit(1.0f, 0.0f, 10);
|
||||
|
||||
+12
-7
@@ -1769,14 +1769,19 @@ void dMenu_Fmap2DBack_c::calcBlink() {
|
||||
t * (g_fmapHIO.mMapBlink[i + 1].mUnselectedRegion.mBlinkSpeed -
|
||||
g_fmapHIO.mMapBlink[i].mUnselectedRegion.mBlinkSpeed);
|
||||
|
||||
field_0x1218++;
|
||||
if (field_0x1218 >= selected_blink_speed) {
|
||||
field_0x1218 = 0;
|
||||
}
|
||||
#if TARGET_PC
|
||||
if (dusk::frame_interp::get_ui_tick_pending())
|
||||
#endif
|
||||
{
|
||||
field_0x1218++;
|
||||
if (field_0x1218 >= selected_blink_speed) {
|
||||
field_0x1218 = 0;
|
||||
}
|
||||
|
||||
field_0x121a++;
|
||||
if (field_0x121a >= unselected_blink_speed) {
|
||||
field_0x121a = 0;
|
||||
field_0x121a++;
|
||||
if (field_0x121a >= unselected_blink_speed) {
|
||||
field_0x121a = 0;
|
||||
}
|
||||
}
|
||||
|
||||
f32 t_selected = 0.0f;
|
||||
|
||||
@@ -185,6 +185,17 @@ dMenu_Ring_c::dMenu_Ring_c(JKRExpHeap* i_heap, STControl* i_stick, CSTControl* i
|
||||
field_0x682 = 0xc000;
|
||||
break;
|
||||
}
|
||||
#if TARGET_PC
|
||||
mCursorInterpPrevX = 0.0f;
|
||||
mCursorInterpPrevY = 0.0f;
|
||||
mCursorInterpCurrX = 0.0f;
|
||||
mCursorInterpCurrY = 0.0f;
|
||||
mCursorInterpPrevAngle = 0;
|
||||
mCursorInterpCurrAngle = 0;
|
||||
mCursorInterpPrevAngular = false;
|
||||
mCursorInterpCurrAngular = false;
|
||||
mCursorInterpInit = false;
|
||||
#endif
|
||||
for (int i = 0; i < 4; i++) {
|
||||
field_0x674[i] = 0;
|
||||
#if TARGET_PC
|
||||
@@ -631,7 +642,71 @@ void dMenu_Ring_c::_draw() {
|
||||
} else {
|
||||
drawSelectItem();
|
||||
drawItem2();
|
||||
#if TARGET_PC
|
||||
f32 simX = 0.0f;
|
||||
f32 simY = 0.0f;
|
||||
bool restoreSimPos = false;
|
||||
if (dusk::frame_interp::is_enabled() && mAlphaRate >= 1.0f) {
|
||||
simX = mpDrawCursor->getPositionX();
|
||||
simY = mpDrawCursor->getPositionY();
|
||||
|
||||
const bool isAngular = (mStatus == STATUS_MOVE) && !mDirectSelectActive;
|
||||
|
||||
if (dusk::frame_interp::get_ui_tick_pending()) {
|
||||
mCursorInterpPrevX = mCursorInterpCurrX;
|
||||
mCursorInterpPrevY = mCursorInterpCurrY;
|
||||
mCursorInterpPrevAngle = mCursorInterpCurrAngle;
|
||||
mCursorInterpPrevAngular = mCursorInterpCurrAngular;
|
||||
|
||||
mCursorInterpCurrX = simX;
|
||||
mCursorInterpCurrY = simY;
|
||||
mCursorInterpCurrAngle = field_0x66e;
|
||||
mCursorInterpCurrAngular = isAngular;
|
||||
|
||||
// reset prev = curr for first render pass or
|
||||
// when angle modes prev/curr differ
|
||||
// to prevent arrival jitter
|
||||
if (!mCursorInterpInit ||
|
||||
mCursorInterpPrevAngular != mCursorInterpCurrAngular) {
|
||||
mCursorInterpPrevX = mCursorInterpCurrX;
|
||||
mCursorInterpPrevY = mCursorInterpCurrY;
|
||||
mCursorInterpPrevAngle = mCursorInterpCurrAngle;
|
||||
mCursorInterpPrevAngular = mCursorInterpCurrAngular;
|
||||
mCursorInterpInit = true;
|
||||
}
|
||||
}
|
||||
if (mCursorInterpInit) {
|
||||
const f32 step = dusk::frame_interp::get_interpolation_step();
|
||||
if (mCursorInterpPrevAngular && mCursorInterpCurrAngular) {
|
||||
const s16 delta = mCursorInterpCurrAngle - mCursorInterpPrevAngle;
|
||||
const s16 lerpedAngle = mCursorInterpPrevAngle + (s16)(delta * step);
|
||||
|
||||
// yoinked from stick_move_proc()
|
||||
const f32 x = g_ringHIO.mItemRingPosX + FB_WIDTH_BASE / 2 +
|
||||
mRingRadiusH * cM_ssin(lerpedAngle);
|
||||
const f32 y = g_ringHIO.mItemRingPosY + FB_HEIGHT_BASE / 2 +
|
||||
mRingRadiusV * cM_scos(lerpedAngle);
|
||||
mpDrawCursor->setPos(x, y);
|
||||
} else {
|
||||
mpDrawCursor->setPos(
|
||||
mCursorInterpPrevX + (mCursorInterpCurrX - mCursorInterpPrevX) * step,
|
||||
mCursorInterpPrevY + (mCursorInterpCurrY - mCursorInterpPrevY) * step
|
||||
);
|
||||
}
|
||||
restoreSimPos = true;
|
||||
}
|
||||
} else {
|
||||
mCursorInterpInit = false;
|
||||
}
|
||||
#endif
|
||||
mpDrawCursor->draw();
|
||||
#if TARGET_PC
|
||||
// prevents offsetting at destination on the next frame
|
||||
// since stick_wait_proc doesn't call setPos and we clobbered mPositionX/Y
|
||||
if (restoreSimPos) {
|
||||
mpDrawCursor->setPos(simX, simY);
|
||||
}
|
||||
#endif
|
||||
mpItemExplain->trans(mCenterPosX, mCenterPosY);
|
||||
mpItemExplain->draw((J2DOrthoGraph*)grafPort);
|
||||
drawFlag0();
|
||||
|
||||
+37
-21
@@ -18,6 +18,7 @@
|
||||
#include "m_Do/m_Do_controller_pad.h"
|
||||
#include "m_Do/m_Do_graphic.h"
|
||||
#include "d/d_msg_scrn_explain.h"
|
||||
#include "dusk/frame_interpolation.h"
|
||||
#include "dusk/settings.h"
|
||||
#include "JSystem/J2DGraph/J2DAnmLoader.h"
|
||||
#include "f_op/f_op_msg_mng.h"
|
||||
@@ -715,7 +716,9 @@ void dMenu_save_c::_move() {
|
||||
}
|
||||
|
||||
(this->*MenuSaveProc[mMenuProc])();
|
||||
#if !TARGET_PC
|
||||
saveSelAnm();
|
||||
#endif
|
||||
|
||||
if (mWarning != NULL) {
|
||||
mWarning->_move();
|
||||
@@ -732,36 +735,46 @@ void dMenu_save_c::saveSelAnm() {
|
||||
}
|
||||
|
||||
void dMenu_save_c::selFileWakuAnm() {
|
||||
mFileWakuAnmFrame += 2;
|
||||
if (mFileWakuAnmFrame >= mpFileWakuAnm->getFrameMax()) {
|
||||
mFileWakuAnmFrame -= mpFileWakuAnm->getFrameMax();
|
||||
#if TARGET_PC
|
||||
if (dusk::frame_interp::get_ui_tick_pending())
|
||||
#endif
|
||||
{
|
||||
mFileWakuAnmFrame += 2;
|
||||
if (mFileWakuAnmFrame >= mpFileWakuAnm->getFrameMax()) {
|
||||
mFileWakuAnmFrame -= mpFileWakuAnm->getFrameMax();
|
||||
}
|
||||
|
||||
mFileWakuRotAnmFrame += 2;
|
||||
if (mFileWakuRotAnmFrame >= mpFileWakuRotAnm->getFrameMax()) {
|
||||
mFileWakuRotAnmFrame -= mpFileWakuRotAnm->getFrameMax();
|
||||
}
|
||||
}
|
||||
mpFileWakuAnm->setFrame(mFileWakuAnmFrame);
|
||||
|
||||
mFileWakuRotAnmFrame += 2;
|
||||
if (mFileWakuRotAnmFrame >= mpFileWakuRotAnm->getFrameMax()) {
|
||||
mFileWakuRotAnmFrame -= mpFileWakuRotAnm->getFrameMax();
|
||||
}
|
||||
mpFileWakuRotAnm->setFrame(mFileWakuRotAnmFrame);
|
||||
}
|
||||
|
||||
void dMenu_save_c::bookIconAnm() {
|
||||
field_0x154 += 2;
|
||||
if (field_0x154 >= field_0x150->getFrameMax()) {
|
||||
field_0x154 -= field_0x150->getFrameMax();
|
||||
#if TARGET_PC
|
||||
if (dusk::frame_interp::get_ui_tick_pending())
|
||||
#endif
|
||||
{
|
||||
field_0x154 += 2;
|
||||
if (field_0x154 >= field_0x150->getFrameMax()) {
|
||||
field_0x154 -= field_0x150->getFrameMax();
|
||||
}
|
||||
|
||||
field_0x15c += 2;
|
||||
if (field_0x15c >= field_0x158->getFrameMax()) {
|
||||
field_0x15c -= field_0x158->getFrameMax();
|
||||
}
|
||||
|
||||
field_0x164 += 2;
|
||||
if (field_0x164 >= field_0x160->getFrameMax()) {
|
||||
field_0x164 -= field_0x160->getFrameMax();
|
||||
}
|
||||
}
|
||||
field_0x150->setFrame(field_0x154);
|
||||
|
||||
field_0x15c += 2;
|
||||
if (field_0x15c >= field_0x158->getFrameMax()) {
|
||||
field_0x15c -= field_0x158->getFrameMax();
|
||||
}
|
||||
field_0x158->setFrame(field_0x15c);
|
||||
|
||||
field_0x164 += 2;
|
||||
if (field_0x164 >= field_0x160->getFrameMax()) {
|
||||
field_0x164 -= field_0x160->getFrameMax();
|
||||
}
|
||||
field_0x160->setFrame(field_0x164);
|
||||
}
|
||||
|
||||
@@ -2809,6 +2822,9 @@ void dMenu_save_c::menuSaveWide() {
|
||||
|
||||
void dMenu_save_c::_draw2() {
|
||||
if (field_0x21a1 == 0) {
|
||||
#if TARGET_PC
|
||||
saveSelAnm();
|
||||
#endif
|
||||
if (mpScrnExplain != NULL) {
|
||||
dComIfGd_set2DOpa(&mMenuSaveExplain);
|
||||
}
|
||||
|
||||
@@ -1987,6 +1987,13 @@ bool jmessage_tSequenceProcessor::do_isReady() {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if TARGET_PC
|
||||
if (dusk::getSettings().game.instantText && mDoCPd_c::getHoldB(0)) {
|
||||
field_0xb2 = 1;
|
||||
pReference->setSendTimer(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (dComIfGp_checkMesgBgm()) {
|
||||
bool isItemMusicPlaying = true;
|
||||
if (mDoAud_checkPlayingSubBgmFlag() != Z2BGM_ITEM_GET &&
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
|
||||
#if TARGET_PC
|
||||
#include "dusk/memory.h"
|
||||
#include <dusk/autosave.h>
|
||||
#endif
|
||||
|
||||
#if DEBUG
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#if TARGET_PC
|
||||
#include "dusk/settings.h"
|
||||
#include <f_ap/f_ap_game.h>
|
||||
#include <dusk/autosave.h>
|
||||
#endif
|
||||
|
||||
static u8 dSv_item_rename(u8 i_itemNo) {
|
||||
|
||||
+132
-10
@@ -5,14 +5,15 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <span>
|
||||
|
||||
#include "Adpcm.hpp"
|
||||
#include "freeverb/revmodel.hpp"
|
||||
#include "JSystem/JAudio2/JASDriverIF.h"
|
||||
#include "dusk/audio/DuskAudioSystem.h"
|
||||
#include "dusk/endian.h"
|
||||
#include "dusk/logging.h"
|
||||
#include "global.h"
|
||||
#include "tracy/Tracy.hpp"
|
||||
|
||||
@@ -95,6 +96,13 @@ static void RenderChannel(
|
||||
ChannelAuxData& channelAux,
|
||||
OutputSubframe& subframe);
|
||||
|
||||
static void RenderOutputChannel(
|
||||
const JASDsp::TChannel& sourceChannel,
|
||||
ChannelAuxData& aux,
|
||||
OutputChannel outputChannel,
|
||||
const std::span<f32> inputSamples,
|
||||
OutputSubframe& fullOutputSubframe);
|
||||
|
||||
/**
|
||||
* Converts a pitch value on a DSP channel to a sample rate.
|
||||
*/
|
||||
@@ -117,6 +125,8 @@ static void ResetChannel(JASDsp::TChannel& channel, ChannelAuxData& aux) {
|
||||
aux.resamplePos = 0.0;
|
||||
aux.resamplePrev = 0;
|
||||
|
||||
aux.oscPhase = 0;
|
||||
|
||||
aux.prev_lp_out = 0.0f;
|
||||
aux.prev_lp_in = 0.0f;
|
||||
|
||||
@@ -141,6 +151,119 @@ static void MixSubframe(DspSubframe& dst, const DspSubframe& src) {
|
||||
}
|
||||
}
|
||||
|
||||
enum class OscType : u16 {
|
||||
SQUARE_WAVE_PW_50 = 0,
|
||||
SAW_WAVE = 1,
|
||||
SQUARE_WAVE_PW_25 = 3,
|
||||
TRIANGLE_WAVE = 4,
|
||||
// idk what 5 and 6 are
|
||||
SINE_WAVE = 7,
|
||||
// idk what 8 and 9 are
|
||||
SINE_WAVE_VAR_STEP = 10,
|
||||
EVOLVING_HARMONIC = 11,
|
||||
EVOLVING_RAMP = 12,
|
||||
};
|
||||
|
||||
static s16 gEvolvingHarmonic[64];
|
||||
|
||||
static void GenerateEvolvingHarmonic() {
|
||||
static bool initialized = false;
|
||||
if (!initialized) {
|
||||
gEvolvingHarmonic[62] = 8191;
|
||||
gEvolvingHarmonic[63] = 16383;
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
u32 prev2 = (u32)gEvolvingHarmonic[62];
|
||||
u32 prev1 = (u32)gEvolvingHarmonic[63];
|
||||
|
||||
for (int i = 0; i < 64; i += 2) {
|
||||
u32 cur = (u32)gEvolvingHarmonic[i];
|
||||
gEvolvingHarmonic[i] = (s16)((s32)(prev2 * prev1 - (cur << 16)) >> 16);
|
||||
prev2 = prev1;
|
||||
prev1 = cur;
|
||||
|
||||
cur = (u32)gEvolvingHarmonic[i + 1];
|
||||
gEvolvingHarmonic[i + 1] = (s16)((s32)(2u * (prev2 * prev1 + (cur << 16))) >> 16);
|
||||
prev2 = prev1;
|
||||
prev1 = cur;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void RenderOscChannel(
|
||||
JASDsp::TChannel& channel,
|
||||
ChannelAuxData& channelAux,
|
||||
OutputSubframe& subframe) {
|
||||
if (channel.mResetFlag)
|
||||
ResetChannel(channel, channelAux);
|
||||
|
||||
const u32 pitch = channel.mPitch;
|
||||
DspSubframe buf = {};
|
||||
const auto oscType = static_cast<OscType>(channel.mBytesPerBlock);
|
||||
|
||||
switch (oscType) {
|
||||
case OscType::SQUARE_WAVE_PW_50: {
|
||||
std::generate(buf.begin(), buf.end(), [&] {
|
||||
f32 s = channelAux.oscPhase < 0x8000u ? 0.5f : -0.5f;
|
||||
channelAux.oscPhase += pitch >> 1;
|
||||
return s;
|
||||
});
|
||||
break;
|
||||
}
|
||||
case OscType::SQUARE_WAVE_PW_25: {
|
||||
std::generate(buf.begin(), buf.end(), [&] {
|
||||
f32 s = channelAux.oscPhase < 0x4000u ? 0.5f : -0.5f;
|
||||
channelAux.oscPhase += pitch >> 1;
|
||||
return s;
|
||||
});
|
||||
break;
|
||||
}
|
||||
case OscType::SAW_WAVE:
|
||||
case OscType::EVOLVING_RAMP: {
|
||||
std::generate(buf.begin(), buf.end(), [&] {
|
||||
f32 s = (f32)(s16)channelAux.oscPhase / 32768.0f;
|
||||
channelAux.oscPhase += pitch >> 1;
|
||||
return s;
|
||||
});
|
||||
break;
|
||||
}
|
||||
case OscType::SINE_WAVE:
|
||||
case OscType::SINE_WAVE_VAR_STEP: {
|
||||
std::generate(buf.begin(), buf.end(), [&] {
|
||||
f32 s = sinf((f32)channelAux.oscPhase * (2.0f * M_PI / 65536.0f)) * 0.5f;
|
||||
channelAux.oscPhase += pitch >> 1;
|
||||
return s;
|
||||
});
|
||||
break;
|
||||
}
|
||||
case OscType::TRIANGLE_WAVE: {
|
||||
std::generate(buf.begin(), buf.end(), [&] {
|
||||
f32 s = 0.5f - fabsf((f32)(s16)channelAux.oscPhase / 32768.0f);
|
||||
channelAux.oscPhase += pitch >> 1;
|
||||
return s;
|
||||
});
|
||||
break;
|
||||
}
|
||||
case OscType::EVOLVING_HARMONIC: {
|
||||
std::generate(buf.begin(), buf.end(), [&] {
|
||||
f32 s = gEvolvingHarmonic[channelAux.oscPhase >> 10] / 32768.0f;
|
||||
channelAux.oscPhase += pitch >> 1;
|
||||
return s;
|
||||
});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
DuskLog.error("RenderOscChannel: unimplemented oscillator type {}", channel.mBytesPerBlock);
|
||||
break;
|
||||
}
|
||||
|
||||
auto samples = std::span(buf).subspan(0, DSP_SUBFRAME_SIZE);
|
||||
RenderOutputChannel(channel, channelAux, OutputChannel::LEFT, samples, subframe);
|
||||
RenderOutputChannel(channel, channelAux, OutputChannel::RIGHT, samples, subframe);
|
||||
}
|
||||
|
||||
|
||||
void dusk::audio::DspRender(OutputSubframe& subframe) {
|
||||
ZoneScoped;
|
||||
if (DumpAudio != sDumpWasActive) {
|
||||
@@ -152,6 +275,8 @@ void dusk::audio::DspRender(OutputSubframe& subframe) {
|
||||
}
|
||||
}
|
||||
|
||||
GenerateEvolvingHarmonic();
|
||||
|
||||
std::span channels(JASDsp::CH_BUF, DSP_CHANNELS);
|
||||
|
||||
DspSubframe reverbInputL = {};
|
||||
@@ -174,17 +299,14 @@ void dusk::audio::DspRender(OutputSubframe& subframe) {
|
||||
channel.mIsFinished = true;
|
||||
continue;
|
||||
}
|
||||
else if (channel.mWaveAramAddress == 0) {
|
||||
// I think these are oscillator channels? Not backed by audio.
|
||||
// No idea how to implement these yet, so skip them.
|
||||
channel.mIsFinished = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
ValidateChannel(channel);
|
||||
|
||||
OutputSubframe channelSubframe = {};
|
||||
RenderChannel(channel, channelAux, channelSubframe);
|
||||
if (channel.mWaveAramAddress == 0) {
|
||||
RenderOscChannel(channel, channelAux, channelSubframe);
|
||||
} else {
|
||||
ValidateChannel(channel);
|
||||
RenderChannel(channel, channelAux, channelSubframe);
|
||||
}
|
||||
|
||||
if (EnableReverb) {
|
||||
// scale the input to the reverb rather than using wet/dry on the output.
|
||||
|
||||
@@ -53,6 +53,9 @@ namespace dusk::audio {
|
||||
// last consumed sample from decodeBuf
|
||||
s16 resamplePrev;
|
||||
|
||||
// phase of oscillator channels
|
||||
u16 oscPhase;
|
||||
|
||||
// low pass previous state
|
||||
f32 prev_lp_out; // out[n-1]
|
||||
f32 prev_lp_in; // in[n-1]
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
#include "dusk/autosave.h"
|
||||
#include "imgui/ImGuiConsole.hpp"
|
||||
|
||||
u8 mSaveBuffer[QUEST_LOG_SIZE * 3];
|
||||
u8 mAutoSaveProc = 0;
|
||||
int autoSaveWriteState = 0;
|
||||
|
||||
typedef void (*AutoSaveFuncs)();
|
||||
static AutoSaveFuncs AutoSaveFuncsProc[] = {
|
||||
noAutoSave, enterAutoSave, autoSaving, waitingForWrite, endAutoSave,
|
||||
};
|
||||
|
||||
void noAutoSave() {}
|
||||
|
||||
void triggerAutoSave() {
|
||||
if (dusk::getSettings().game.autoSave && mAutoSaveProc == 0 &&
|
||||
strcmp(dComIfGp_getStartStageName(), "F_SP102") != 0 && dComIfGp_getStartStageLayer() != 0)
|
||||
{
|
||||
mAutoSaveProc = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void updateAutoSave() {
|
||||
(AutoSaveFuncsProc[mAutoSaveProc])();
|
||||
}
|
||||
|
||||
void writeAutoSave() {
|
||||
dComIfGs_setMemoryToCard(mSaveBuffer, dComIfGs_getDataNum());
|
||||
mDoMemCdRWm_SetCheckSumGameData(mSaveBuffer, dComIfGs_getDataNum());
|
||||
|
||||
u8* save = mSaveBuffer;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
mDoMemCdRWm_TestCheckSumGameData(save);
|
||||
save += QUEST_LOG_SIZE;
|
||||
}
|
||||
|
||||
g_mDoMemCd_control.save(mSaveBuffer, sizeof(mSaveBuffer), 0);
|
||||
}
|
||||
|
||||
void autoSaving() {
|
||||
int cardState = g_mDoMemCd_control.LoadSync(mSaveBuffer, sizeof(mSaveBuffer), 0);
|
||||
if (cardState != 0) {
|
||||
if (cardState == 2) {
|
||||
mAutoSaveProc = 1;
|
||||
} else if (cardState == 1) {
|
||||
writeAutoSave();
|
||||
mAutoSaveProc = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void enterAutoSave() {
|
||||
u32 cardStatus = g_mDoMemCd_control.getStatus(0);
|
||||
|
||||
if (cardStatus != 14) {
|
||||
switch (cardStatus) {
|
||||
case 2:
|
||||
g_mDoMemCd_control.load();
|
||||
mAutoSaveProc = 2;
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
break;
|
||||
default:
|
||||
mAutoSaveProc = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void waitingForWrite() {
|
||||
autoSaveWriteState = g_mDoMemCd_control.SaveSync();
|
||||
|
||||
if (autoSaveWriteState == 2) {
|
||||
mAutoSaveProc = 0;
|
||||
} else if (autoSaveWriteState == 1) {
|
||||
mAutoSaveProc = 4;
|
||||
}
|
||||
}
|
||||
|
||||
void endAutoSave() {
|
||||
dusk::g_imguiConsole.ShowToast("Saving...", 2.0f);
|
||||
mAutoSaveProc = 0;
|
||||
}
|
||||
+2
-2
@@ -10,7 +10,7 @@
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
#include "dusk/dusk.h"
|
||||
#include "dusk/main.h"
|
||||
|
||||
using namespace dusk::config;
|
||||
|
||||
@@ -24,7 +24,7 @@ static absl::flat_hash_map<std::string_view, ConfigVarBase*> RegisteredConfigVar
|
||||
static bool RegistrationDone = false;
|
||||
|
||||
static std::string GetConfigJsonPath() {
|
||||
return fmt::format("{}{}", configPath, ConfigFileName);
|
||||
return (dusk::ConfigPath / ConfigFileName).string();
|
||||
}
|
||||
|
||||
ConfigVarBase::ConfigVarBase(const char* name, const ConfigImplBase* impl) : name(name), registered(false), layer(ConfigVarLayer::Default), impl(impl) {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "dusk/app_info.hpp"
|
||||
#include "dusk/dusk.h"
|
||||
#include "dusk/logging.h"
|
||||
#include "dusk/main.h"
|
||||
#include "dusk/settings.h"
|
||||
#include "version.h"
|
||||
|
||||
@@ -66,7 +67,7 @@ std::string GetReleaseName() {
|
||||
}
|
||||
|
||||
std::filesystem::path GetSentryDatabasePath() {
|
||||
return std::filesystem::path(configPath) / "sentry";
|
||||
return dusk::ConfigPath / "sentry";
|
||||
}
|
||||
|
||||
std::filesystem::path GetLogAttachmentPath() {
|
||||
|
||||
+21
-5
@@ -3,6 +3,27 @@
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
||||
void __dcbz(void* addr, int offset) {
|
||||
// Gekko cache lines are 32 bytes.
|
||||
// dcbz usually requires addr to be 32-byte aligned.
|
||||
memset((char*)addr + offset, 0, 32);
|
||||
}
|
||||
|
||||
int __cntlzw(unsigned int val) {
|
||||
if (val == 0) return 32; // PowerPC returns 32 if the input is 0
|
||||
#ifdef _MSC_VER
|
||||
unsigned long idx;
|
||||
_BitScanReverse(&idx, val);
|
||||
return 31 - (int)idx;
|
||||
#else
|
||||
return __builtin_clz(val);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef _MSC_VER
|
||||
int stricmp(const char* str1, const char* str2) {
|
||||
char a_var;
|
||||
@@ -48,11 +69,6 @@ int strnicmp(const char* str1, const char* str2, int n) {
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void *_memcpy(void* dest, void const* src, int n) {
|
||||
return memcpy(dest, src, n);
|
||||
}
|
||||
|
||||
void DCZeroRange(void* addr, uint32_t nBytes) {
|
||||
#if defined(_MSC_VER) || TARGET_ANDROID
|
||||
memset(addr, 0, nBytes);
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
// C++ Mangled version of extras.c
|
||||
#include <cstring>
|
||||
#include <cstdint>
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
||||
void *__memcpy(void* dest, void const* src, int n) {
|
||||
return memcpy(dest, src, n);
|
||||
}
|
||||
|
||||
void __dcbz(void* addr, int offset) {
|
||||
// Gekko cache lines are 32 bytes.
|
||||
// dcbz usually requires addr to be 32-byte aligned.
|
||||
memset((char*)addr + offset, 0, 32);
|
||||
}
|
||||
|
||||
int __cntlzw(unsigned int val) {
|
||||
if (val == 0) return 32; // PowerPC returns 32 if the input is 0
|
||||
#ifdef _MSC_VER
|
||||
unsigned long idx;
|
||||
_BitScanReverse(&idx, val);
|
||||
return 31 - (int)idx;
|
||||
#else
|
||||
return __builtin_clz(val);
|
||||
#endif
|
||||
}
|
||||
@@ -127,14 +127,20 @@ void ensure_initialized() {
|
||||
s_initialized = true;
|
||||
}
|
||||
|
||||
void begin_sim_tick() {
|
||||
ensure_initialized();
|
||||
if (!g_enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
s_interpolationCallBackWork.clear();
|
||||
s_cam_prev = std::move(s_cam_curr);
|
||||
}
|
||||
|
||||
void begin_frame(bool enabled, bool is_sim_frame, float step) {
|
||||
g_enabled = enabled;
|
||||
g_is_sim_frame = is_sim_frame;
|
||||
g_step = std::clamp(step, 0.0f, 1.0f);
|
||||
if (is_sim_frame) {
|
||||
s_interpolationCallBackWork.clear();
|
||||
s_cam_prev = std::move(s_cam_curr);
|
||||
}
|
||||
}
|
||||
|
||||
bool is_enabled() {
|
||||
@@ -408,10 +414,8 @@ void begin_presentation_camera() {
|
||||
}
|
||||
|
||||
mDoLib_clipper::setup(view->fovy, view->aspect, view->near_, far_);
|
||||
|
||||
#if WIDESCREEN_SUPPORT
|
||||
mDoGph_gInf_c::offWideZoom();
|
||||
#endif
|
||||
|
||||
// FRAME INTERP NOTE: Removed the call to offWideZoom that was here, it causes problems with presentation during cutscenes.
|
||||
|
||||
s_presentation_depth = 1;
|
||||
|
||||
|
||||
+44
-23
@@ -5,62 +5,84 @@
|
||||
#include <cmath>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace dusk {
|
||||
namespace game_clock {
|
||||
namespace dusk::game_clock {
|
||||
|
||||
using clock = std::chrono::steady_clock;
|
||||
|
||||
bool s_initialized = false;
|
||||
clock::time_point s_previous_sample{};
|
||||
float s_sim_accumulator = 0.0f;
|
||||
clock::time_point s_current_snapshot_time{};
|
||||
|
||||
std::unordered_map<uintptr_t, clock::time_point> s_interval_last_sample;
|
||||
|
||||
constexpr clock::duration kSimPeriodDuration =
|
||||
std::chrono::duration_cast<clock::duration>(std::chrono::duration<float>(sim_pace()));
|
||||
constexpr clock::duration kAbnormalGapResetThreshold = std::chrono::milliseconds(250);
|
||||
constexpr int kMaxSimTicksPerFrame = 2;
|
||||
|
||||
void ensure_initialized() {
|
||||
if (s_initialized) {
|
||||
return;
|
||||
}
|
||||
s_previous_sample = clock::now();
|
||||
s_sim_accumulator = sim_pace();
|
||||
s_current_snapshot_time = s_previous_sample;
|
||||
s_initialized = true;
|
||||
}
|
||||
|
||||
void reset_accumulator() {
|
||||
ensure_initialized();
|
||||
s_sim_accumulator = fmodf(s_sim_accumulator, sim_pace());
|
||||
}
|
||||
|
||||
void reset_frame_timer() {
|
||||
s_previous_sample = clock::now();
|
||||
s_sim_accumulator = 0.0f;
|
||||
s_current_snapshot_time = s_previous_sample - kSimPeriodDuration;
|
||||
}
|
||||
|
||||
MainLoopPacer advance_main_loop() {
|
||||
ensure_initialized();
|
||||
|
||||
const clock::time_point now = clock::now();
|
||||
const float presentation_dt = std::chrono::duration<float>(now - s_previous_sample).count();
|
||||
const clock::duration frame_gap = now - s_previous_sample;
|
||||
const float presentation_dt = std::chrono::duration<float>(frame_gap).count();
|
||||
s_previous_sample = now;
|
||||
|
||||
s_sim_accumulator += presentation_dt;
|
||||
|
||||
MainLoopPacer out{};
|
||||
out.presentation_dt_seconds = presentation_dt;
|
||||
|
||||
const bool should_interpolate = dusk::getSettings().game.enableFrameInterpolation && !dusk::getTransientSettings().skipFrameRateLimit;
|
||||
const bool should_interpolate = dusk::getSettings().game.enableFrameInterpolation &&
|
||||
!dusk::getTransientSettings().skipFrameRateLimit;
|
||||
out.is_interpolating = should_interpolate;
|
||||
out.sim_pace = sim_pace();
|
||||
|
||||
if (!should_interpolate) {
|
||||
s_sim_accumulator = 0.0f;
|
||||
out.do_sim_tick = true;
|
||||
out.interpolation_step = 0.0f;
|
||||
return out;
|
||||
} else {
|
||||
out.do_sim_tick = s_sim_accumulator >= sim_pace();
|
||||
out.interpolation_step = out.do_sim_tick ? 0.0f : s_sim_accumulator / sim_pace();
|
||||
s_current_snapshot_time = now;
|
||||
out.sim_ticks_to_run = 1;
|
||||
return out;
|
||||
}
|
||||
|
||||
if (frame_gap > kAbnormalGapResetThreshold) {
|
||||
s_current_snapshot_time = now - kSimPeriodDuration;
|
||||
out.sim_ticks_to_run = 0;
|
||||
return out;
|
||||
}
|
||||
|
||||
int sim_ticks_to_run = 0;
|
||||
clock::time_point projected_snapshot_time = s_current_snapshot_time;
|
||||
const clock::time_point render_time = now - kSimPeriodDuration;
|
||||
while (sim_ticks_to_run < kMaxSimTicksPerFrame && projected_snapshot_time < render_time) {
|
||||
projected_snapshot_time += kSimPeriodDuration;
|
||||
sim_ticks_to_run++;
|
||||
}
|
||||
out.sim_ticks_to_run = sim_ticks_to_run;
|
||||
return out;
|
||||
}
|
||||
|
||||
void commit_sim_tick() {
|
||||
ensure_initialized();
|
||||
s_current_snapshot_time += kSimPeriodDuration;
|
||||
}
|
||||
|
||||
float sample_interpolation_step() {
|
||||
ensure_initialized();
|
||||
const float step =
|
||||
std::chrono::duration<float>(clock::now() - s_current_snapshot_time).count() / sim_pace();
|
||||
return std::clamp(step, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
float consume_interval(const void* consumer) {
|
||||
@@ -78,5 +100,4 @@ float consume_interval(const void* consumer) {
|
||||
return dt;
|
||||
}
|
||||
|
||||
} // namespace game_clock
|
||||
} // namespace dusk
|
||||
} // namespace dusk::game_clock
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
#include <cmath>
|
||||
#include <SSystem/SComponent/c_xyz.h>
|
||||
#include <d/d_com_inf_game.h>
|
||||
#include <d/actor/d_a_player.h>
|
||||
#include <d/actor/d_a_alink.h>
|
||||
#include <dusk/gamepad_color.h>
|
||||
|
||||
cXyz currentGamepadColor = {0, 0, 0};
|
||||
cXyz finalGamepadColor = {0, 0, 0};
|
||||
cXyz additionalGamepadColor = {0, 0, 0};
|
||||
|
||||
float lerpSpeed = 0.0f;
|
||||
|
||||
const cXyz duskColor = {50, 50, -50};
|
||||
const cXyz noColor = {0, 0, 0};
|
||||
|
||||
cXyz LerpColor(cXyz a, cXyz b, float t) {
|
||||
return {std::lerp(a.x, b.x, t), std::lerp(a.y, b.y, t), std::lerp(a.z, b.z, t)};
|
||||
}
|
||||
|
||||
void FadeLED(cXyz newColor, float speed) {
|
||||
finalGamepadColor = newColor;
|
||||
lerpSpeed = speed / 30.0f;
|
||||
}
|
||||
|
||||
void SetLED(cXyz newColor) {
|
||||
currentGamepadColor = newColor;
|
||||
finalGamepadColor = newColor;
|
||||
}
|
||||
|
||||
void SetGamepadAdditionalColor(cXyz addColor) {
|
||||
additionalGamepadColor.x = addColor.x;
|
||||
additionalGamepadColor.y = addColor.y;
|
||||
additionalGamepadColor.z = addColor.z;
|
||||
}
|
||||
|
||||
void handleGamepadColor() {
|
||||
bool setColor = false;
|
||||
|
||||
fopAc_ac_c* zhint = dComIfGp_att_getZHint();
|
||||
if (zhint != NULL) {
|
||||
FadeLED({50, 50, 175}, 2.0f);
|
||||
setColor = true;
|
||||
}
|
||||
|
||||
daPy_py_c* player = daPy_getPlayerActorClass();
|
||||
daAlink_c* link = daAlink_getAlinkActorClass();
|
||||
|
||||
if (link != nullptr && !setColor) {
|
||||
if (link->checkWolf()) {
|
||||
FadeLED({115, 115, 75}, 5.0f);
|
||||
setColor = true;
|
||||
} else {
|
||||
switch (dComIfGs_getSelectEquipClothes()) {
|
||||
case dItemNo_WEAR_KOKIRI_e:
|
||||
FadeLED({0, 100, 0}, 5.0f);
|
||||
setColor = true;
|
||||
break;
|
||||
case dItemNo_WEAR_ZORA_e:
|
||||
FadeLED({0, 0, 100}, 5.0f);
|
||||
setColor = true;
|
||||
break;
|
||||
case dItemNo_ARMOR_e:
|
||||
if (link->checkMagicArmorHeavy()) {
|
||||
FadeLED({5, 100, 100}, 5.0f);
|
||||
} else {
|
||||
FadeLED({100, 0, 5}, 5.0f);
|
||||
}
|
||||
setColor = true;
|
||||
break;
|
||||
case dItemNo_WEAR_CASUAL_e:
|
||||
FadeLED({235, 230, 115}, 5.0f);
|
||||
setColor = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dKy_darkworld_check()) {
|
||||
SetGamepadAdditionalColor(duskColor);
|
||||
} else {
|
||||
SetGamepadAdditionalColor(noColor);
|
||||
}
|
||||
|
||||
f32 finalRed = finalGamepadColor.x + additionalGamepadColor.x;
|
||||
f32 finalGreen = finalGamepadColor.y + additionalGamepadColor.y;
|
||||
f32 finalBlue = finalGamepadColor.z + additionalGamepadColor.z;
|
||||
|
||||
if (finalRed > 255)
|
||||
finalRed = 255;
|
||||
if (finalRed < 0)
|
||||
finalRed = 0;
|
||||
|
||||
if (finalGreen > 255)
|
||||
finalGreen = 255;
|
||||
if (finalGreen < 0)
|
||||
finalGreen = 0;
|
||||
|
||||
if (finalBlue > 255)
|
||||
finalBlue = 255;
|
||||
if (finalBlue < 0)
|
||||
finalBlue = 0;
|
||||
|
||||
currentGamepadColor = LerpColor(currentGamepadColor, cXyz{finalRed, finalGreen, finalBlue}, lerpSpeed);
|
||||
PADSetColor(PAD_CHAN0, (u8)currentGamepadColor.x, (u8)currentGamepadColor.y, (u8)currentGamepadColor.z);
|
||||
}
|
||||
+85
-5
@@ -1,16 +1,29 @@
|
||||
#include "dusk/gyro.h"
|
||||
#include "d/actor/d_a_alink.h"
|
||||
#include <cmath>
|
||||
|
||||
namespace dusk::gyro {
|
||||
namespace {
|
||||
constexpr s32 kRollgoalTableMaxOffset = 12000;
|
||||
constexpr s32 kRollgoalTableMaxOffset = 6500;
|
||||
constexpr float kGyroEmaAlphaMin = 0.05f;
|
||||
constexpr float kGyroEmaAlphaMax = 1.0f;
|
||||
// Smooth gravity separately so the yaw/roll blend doesn't twitch with raw accel noise.
|
||||
constexpr float kGravityEmaAlpha = 0.1f;
|
||||
constexpr float kMinGravityProjection = 0.2f;
|
||||
// Let roll contribute more strongly as the pad approaches an upright posture.
|
||||
constexpr float kRollAimBoostMax = 2.0f;
|
||||
|
||||
bool s_sensor_enabled = false;
|
||||
bool s_accel_enabled = false;
|
||||
bool s_was_aiming = false;
|
||||
bool s_have_gravity_baseline = false;
|
||||
float s_smooth_gx = 0.0f;
|
||||
float s_smooth_gy = 0.0f;
|
||||
float s_smooth_gz = 0.0f;
|
||||
float s_gravity_y = 0.0f;
|
||||
float s_gravity_z = 0.0f;
|
||||
float s_baseline_gravity_y = 0.0f;
|
||||
float s_baseline_gravity_z = 0.0f;
|
||||
float s_yaw_rad = 0.0f;
|
||||
float s_pitch_rad = 0.0f;
|
||||
float s_roll_rad = 0.0f;
|
||||
@@ -19,6 +32,10 @@ s32 s_rollgoal_az = 0;
|
||||
|
||||
void reset_filter_state() {
|
||||
s_smooth_gx = s_smooth_gy = s_smooth_gz = 0.0f;
|
||||
s_gravity_y = s_gravity_z = 0.0f;
|
||||
s_baseline_gravity_y = s_baseline_gravity_z = 0.0f;
|
||||
s_was_aiming = false;
|
||||
s_have_gravity_baseline = false;
|
||||
s_yaw_rad = s_pitch_rad = s_roll_rad = 0.0f;
|
||||
s_rollgoal_ax = s_rollgoal_az = 0;
|
||||
}
|
||||
@@ -49,15 +66,30 @@ bool queryGyroAimContext() {
|
||||
}
|
||||
|
||||
void read(float dt) {
|
||||
if (!s_sensor_keep_alive && !queryGyroAimContext()) {
|
||||
const bool aim_active = queryGyroAimContext();
|
||||
const bool aim_just_started = aim_active && !s_was_aiming;
|
||||
const bool aim_just_ended = !aim_active && s_was_aiming;
|
||||
s_was_aiming = aim_active;
|
||||
|
||||
if (!s_sensor_keep_alive && !aim_active) {
|
||||
if (s_sensor_enabled) {
|
||||
PADSetSensorEnabled(PAD_CHAN0, PAD_SENSOR_GYRO, FALSE);
|
||||
s_sensor_enabled = false;
|
||||
}
|
||||
if (s_accel_enabled) {
|
||||
PADSetSensorEnabled(PAD_CHAN0, PAD_SENSOR_ACCEL, FALSE);
|
||||
s_accel_enabled = false;
|
||||
}
|
||||
reset_filter_state();
|
||||
return;
|
||||
}
|
||||
|
||||
if (aim_just_started || aim_just_ended) {
|
||||
s_gravity_y = s_gravity_z = 0.0f;
|
||||
s_baseline_gravity_y = s_baseline_gravity_z = 0.0f;
|
||||
s_have_gravity_baseline = false;
|
||||
}
|
||||
|
||||
if (!s_sensor_enabled) {
|
||||
if (!PADHasSensor(PAD_CHAN0, PAD_SENSOR_GYRO)) {
|
||||
return;
|
||||
@@ -68,6 +100,13 @@ void read(float dt) {
|
||||
s_sensor_enabled = true;
|
||||
}
|
||||
|
||||
if (!s_accel_enabled && PADHasSensor(PAD_CHAN0, PAD_SENSOR_ACCEL) &&
|
||||
PADSetSensorEnabled(PAD_CHAN0, PAD_SENSOR_ACCEL, TRUE))
|
||||
{
|
||||
// We only need accel for the gravity-aware yaw/roll mix.
|
||||
s_accel_enabled = true;
|
||||
}
|
||||
|
||||
f32 gyro[3];
|
||||
if (!PADGetSensorData(PAD_CHAN0, PAD_SENSOR_GYRO, gyro, 3)) {
|
||||
return;
|
||||
@@ -80,9 +119,50 @@ void read(float dt) {
|
||||
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
|
||||
const float pitch_rate = apply_deadband(s_smooth_gx, deadband);
|
||||
const float yaw_rate = apply_deadband(s_smooth_gy, deadband);
|
||||
const float roll_rate = apply_deadband(s_smooth_gz, deadband);
|
||||
|
||||
s_pitch_rad = -pitch_rate * dt * dusk::getSettings().game.gyroSensitivityX;
|
||||
s_roll_rad = roll_rate * dt * dusk::getSettings().game.gyroSensitivityX; // GYRO NOTE: Exposing Z sensitivity seems unusual, so I'm just using X
|
||||
|
||||
float horizontal_rate = yaw_rate;
|
||||
if (aim_active && s_accel_enabled) {
|
||||
f32 accel[3];
|
||||
if (PADGetSensorData(PAD_CHAN0, PAD_SENSOR_ACCEL, accel, 3)) {
|
||||
if (!s_have_gravity_baseline) {
|
||||
s_gravity_y = accel[1];
|
||||
s_gravity_z = accel[2];
|
||||
} else {
|
||||
s_gravity_y += kGravityEmaAlpha * (accel[1] - s_gravity_y);
|
||||
s_gravity_z += kGravityEmaAlpha * (accel[2] - s_gravity_z);
|
||||
}
|
||||
|
||||
// Compare the current gravity projection against the gravity vector from
|
||||
// aim start so the user's resting hold angle becomes the neutral baseline.
|
||||
const float gravity_yz_len = std::sqrt((s_gravity_y * s_gravity_y) + (s_gravity_z * s_gravity_z));
|
||||
if (gravity_yz_len >= kMinGravityProjection) {
|
||||
const float current_gravity_y = s_gravity_y / gravity_yz_len;
|
||||
const float current_gravity_z = s_gravity_z / gravity_yz_len;
|
||||
|
||||
if (!s_have_gravity_baseline) {
|
||||
s_baseline_gravity_y = current_gravity_y;
|
||||
s_baseline_gravity_z = current_gravity_z;
|
||||
s_have_gravity_baseline = true;
|
||||
}
|
||||
|
||||
const float yaw_weight =
|
||||
(s_baseline_gravity_y * current_gravity_y) + (s_baseline_gravity_z * current_gravity_z);
|
||||
const float roll_weight =
|
||||
(s_baseline_gravity_y * current_gravity_z) - (s_baseline_gravity_z * current_gravity_y);
|
||||
const float roll_mix = std::fabs(roll_weight);
|
||||
const float roll_boost = 1.0f + (roll_mix * (kRollAimBoostMax - 1.0f));
|
||||
horizontal_rate = (yaw_rate * yaw_weight) + (roll_rate * roll_weight * roll_boost);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s_yaw_rad = horizontal_rate * dt * dusk::getSettings().game.gyroSensitivityY;
|
||||
|
||||
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;
|
||||
|
||||
@@ -305,6 +305,10 @@ namespace dusk {
|
||||
ImGuiMenuGame::ToggleFullscreen();
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_Escape) && getSettings().video.enableFullscreen) {
|
||||
ImGuiMenuGame::ToggleFullscreen();
|
||||
}
|
||||
|
||||
if (!dusk::IsGameLaunched) {
|
||||
m_preLaunchWindow.draw();
|
||||
}
|
||||
@@ -319,9 +323,11 @@ namespace dusk {
|
||||
}
|
||||
}
|
||||
|
||||
// The menu bar renders with ImGuiCol_WindowBg behind it. We just want ImGuiCol_MenuBarBg,
|
||||
// so make the window bg fully transparent temporarily
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.0f, 0.0f, 0.0f, 0.0f));
|
||||
if (showMenu && ImGui::BeginMainMenuBar()) {
|
||||
m_menuGame.draw();
|
||||
m_menuEnhancements.draw();
|
||||
m_menuTools.draw();
|
||||
|
||||
const auto fpsLabel =
|
||||
@@ -336,6 +342,7 @@ namespace dusk {
|
||||
|
||||
ImGui::EndMainMenuBar();
|
||||
}
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
if (!getSettings().backend.wasPresetChosen) {
|
||||
m_firstRunPreset.draw();
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <SDL3/SDL_touch.h>
|
||||
|
||||
#include "ImGuiFirstRunPreset.hpp"
|
||||
#include "ImGuiMenuEnhancements.hpp"
|
||||
#include "ImGuiMenuGame.hpp"
|
||||
#include "ImGuiMenuTools.hpp"
|
||||
#include "ImGuiPreLaunchWindow.hpp"
|
||||
@@ -54,7 +53,6 @@ private:
|
||||
|
||||
ImGuiFirstRunPreset m_firstRunPreset;
|
||||
ImGuiMenuGame m_menuGame;
|
||||
ImGuiMenuEnhancements m_menuEnhancements;
|
||||
ImGuiPreLaunchWindow m_preLaunchWindow;
|
||||
|
||||
// Keep always last
|
||||
|
||||
@@ -126,7 +126,7 @@ void ImGuiEngine_Initialize(float scale) {
|
||||
auto* colors = style.Colors;
|
||||
colors[ImGuiCol_Text] = ImVec4(0.95f, 0.96f, 0.98f, 1.00f);
|
||||
colors[ImGuiCol_TextDisabled] = ImVec4(0.36f, 0.42f, 0.47f, 1.00f);
|
||||
colors[ImGuiCol_WindowBg] = ImVec4(0.11f, 0.15f, 0.17f, 1.00f);
|
||||
colors[ImGuiCol_WindowBg] = ImVec4(0.11f, 0.15f, 0.17f, 0.98f);
|
||||
colors[ImGuiCol_ChildBg] = ImVec4(0.15f, 0.18f, 0.22f, 1.00f);
|
||||
colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.08f, 0.08f, 0.94f);
|
||||
colors[ImGuiCol_Border] = ImVec4(0.08f, 0.10f, 0.12f, 1.00f);
|
||||
@@ -137,7 +137,7 @@ void ImGuiEngine_Initialize(float scale) {
|
||||
colors[ImGuiCol_TitleBg] = ImVec4(0.09f, 0.12f, 0.14f, 0.65f);
|
||||
colors[ImGuiCol_TitleBgActive] = ImVec4(0.08f, 0.10f, 0.12f, 1.00f);
|
||||
colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.00f, 0.00f, 0.00f, 0.51f);
|
||||
colors[ImGuiCol_MenuBarBg] = ImVec4(0.15f, 0.18f, 0.22f, 1.00f);
|
||||
colors[ImGuiCol_MenuBarBg] = ImVec4(0.15f, 0.18f, 0.22f, 0.80f);
|
||||
colors[ImGuiCol_ScrollbarBg] = ImVec4(0.02f, 0.02f, 0.02f, 0.39f);
|
||||
colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.20f, 0.25f, 0.29f, 1.00f);
|
||||
colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.18f, 0.22f, 0.25f, 1.00f);
|
||||
|
||||
@@ -1,260 +0,0 @@
|
||||
#include "imgui.h"
|
||||
|
||||
#include "ImGuiMenuEnhancements.hpp"
|
||||
#include "ImGuiConfig.hpp"
|
||||
#include "dusk/settings.h"
|
||||
|
||||
namespace dusk {
|
||||
ImGuiMenuEnhancements::ImGuiMenuEnhancements() {}
|
||||
|
||||
void ImGuiMenuEnhancements::draw() {
|
||||
if (ImGui::BeginMenu("Enhancements")) {
|
||||
if (ImGui::BeginMenu("Gameplay")) {
|
||||
ImGui::SeparatorText("Preferences");
|
||||
|
||||
config::ImGuiCheckbox("Mirror Mode", getSettings().game.enableMirrorMode);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Mirrors the world horizontally, matching the Wii version of the game.");
|
||||
}
|
||||
|
||||
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::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("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("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("Autosave", getSettings().game.autoSave);
|
||||
const bool autoSaveHovered = ImGui::IsItemHovered();
|
||||
ImGui::SameLine();
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.72f, 0.2f, 1.0f));
|
||||
ImGui::TextUnformatted("[EXPERIMENTAL]");
|
||||
ImGui::PopStyleColor();
|
||||
if (autoSaveHovered || ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip(
|
||||
"Automatically saves the game when going to new areas,\n"
|
||||
"getting an item, or opening a dungeon door.");
|
||||
}
|
||||
|
||||
config::ImGuiCheckbox("Hold B for Instant Text", getSettings().game.instantText);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Make text scroll immediately by holding B.");
|
||||
}
|
||||
|
||||
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("No Rupee Returns", getSettings().game.noReturnRupees);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Always collect Rupees even if your Wallet is too full.");
|
||||
}
|
||||
|
||||
config::ImGuiCheckbox("No Sword Recoil", getSettings().game.noSwordRecoil);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
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("Skip the warning screen shown when starting the game.");
|
||||
}
|
||||
|
||||
config::ImGuiCheckbox("Sun's Song (R+X)", getSettings().game.sunsSong);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Allows Wolf Link to howl and change the time of day.");
|
||||
}
|
||||
|
||||
config::ImGuiCheckbox("Quick Transform (R+Y)", getSettings().game.enableQuickTransform);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
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.");
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Audio")) {
|
||||
config::ImGuiCheckbox("No Low HP Sound", getSettings().game.noLowHpSound);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Disable the beeping sound when having low health.");
|
||||
}
|
||||
|
||||
config::ImGuiCheckbox("Non-Stop Midna's Lament", getSettings().game.midnasLamentNonStop);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Prevents enemy music while Midna's Lament is playing.");
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
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\n"
|
||||
"while in look mode (C-Up) and while aiming the\n"
|
||||
"Slingshot, Gale Boomerang, Hero's Bow, Clawshot(s),\n"
|
||||
"Ball and Chain, and Dominion Rod.");
|
||||
}
|
||||
|
||||
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("Infinite Hearts", getSettings().game.infiniteHearts);
|
||||
config::ImGuiCheckbox("Infinite Arrows", getSettings().game.infiniteArrows);
|
||||
config::ImGuiCheckbox("Infinite Bombs", getSettings().game.infiniteBombs);
|
||||
config::ImGuiCheckbox("Infinite Oil", getSettings().game.infiniteOil);
|
||||
config::ImGuiCheckbox("Infinite Oxygen", getSettings().game.infiniteOxygen);
|
||||
config::ImGuiCheckbox("Infinite Rupees", getSettings().game.infiniteRupees);
|
||||
config::ImGuiCheckbox("Moon Jump (R+A)", getSettings().game.moonJump);
|
||||
config::ImGuiCheckbox("Super Clawshot", getSettings().game.superClawshot);
|
||||
config::ImGuiCheckbox("Always Greatspin", getSettings().game.alwaysGreatspin);
|
||||
|
||||
config::ImGuiCheckbox("Fast Iron Boots", getSettings().game.enableFastIronBoots);
|
||||
|
||||
config::ImGuiCheckbox("Can Transform Anywhere", getSettings().game.canTransformAnywhere);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Allows you to transform even if NPCs are looking.");
|
||||
}
|
||||
|
||||
config::ImGuiCheckbox("Fast Spinner", getSettings().game.fastSpinner);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Speeds up Spinner movement when holding R.");
|
||||
}
|
||||
|
||||
config::ImGuiCheckbox("Free Magic Armor", getSettings().game.freeMagicArmor);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Makes the magic armor work without rupees.");
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Technical")) {
|
||||
config::ImGuiCheckbox("Restore Wii 1.0 Glitches", getSettings().game.restoreWiiGlitches);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Restores patched glitches from Wii USA 1.0,\n"
|
||||
"the first released version.");
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
#ifndef DUSK_IMGUI_MENUENHANCEMENTS_HPP
|
||||
#define DUSK_IMGUI_MENUENHANCEMENTS_HPP
|
||||
|
||||
#include <aurora/aurora.h>
|
||||
#include <pad.h>
|
||||
#include <string>
|
||||
|
||||
#include "imgui.h"
|
||||
|
||||
namespace dusk {
|
||||
class ImGuiMenuEnhancements {
|
||||
public:
|
||||
ImGuiMenuEnhancements();
|
||||
void draw();
|
||||
};
|
||||
}
|
||||
|
||||
#endif // DUSK_IMGUI_MENUENHANCEMENTS_HPP
|
||||
+418
-171
@@ -5,45 +5,18 @@
|
||||
#include "ImGuiConsole.hpp"
|
||||
#include "ImGuiMenuGame.hpp"
|
||||
#include "ImGuiConfig.hpp"
|
||||
#include <imgui_internal.h>
|
||||
|
||||
#include "JSystem/JUtility/JUTGamePad.h"
|
||||
#include "dusk/audio/DuskAudioSystem.h"
|
||||
#include "dusk/audio/DuskDsp.hpp"
|
||||
#include "dusk/dusk.h"
|
||||
#include "dusk/main.h"
|
||||
#include "dusk/hotkeys.h"
|
||||
#include "dusk/settings.h"
|
||||
#include "m_Do/m_Do_controller_pad.h"
|
||||
#include "m_Do/m_Do_graphic.h"
|
||||
|
||||
#include <aurora/gfx.h>
|
||||
#include <aurora/lib/logging.hpp>
|
||||
#include <SDL3/SDL_gamepad.h>
|
||||
#include <SDL3/SDL_misc.h>
|
||||
|
||||
#include "dusk/main.h"
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <TargetConditionals.h>
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) || (defined(__APPLE__) && !TARGET_OS_IOS && !TARGET_OS_MACCATALYST) || (defined(__linux__) && !defined(__ANDROID__))
|
||||
#define DUSK_CAN_OPEN_DATA_FOLDER 1
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
static void OpenDataFolder() {
|
||||
const std::string path = fs::absolute(fs::path(aurora::g_config.configPath)).generic_string();
|
||||
#if defined(_WIN32)
|
||||
const std::string url = std::string("file:///") + path;
|
||||
#else
|
||||
const std::string url = std::string("file://") + path;
|
||||
#endif
|
||||
(void)SDL_OpenURL(url.c_str());
|
||||
}
|
||||
#else
|
||||
#define DUSK_CAN_OPEN_DATA_FOLDER 0
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
constexpr int kInternalResolutionScaleMax = 12;
|
||||
@@ -63,149 +36,13 @@ namespace dusk {
|
||||
ImGuiMenuGame::ImGuiMenuGame() {}
|
||||
|
||||
void ImGuiMenuGame::draw() {
|
||||
if (ImGui::BeginMenu("Game")) {
|
||||
if (ImGui::BeginMenu("Graphics")) {
|
||||
if (!IsMobile) {
|
||||
if (ImGui::MenuItem("Toggle Fullscreen", hotkeys::TOGGLE_FULLSCREEN)) {
|
||||
ToggleFullscreen();
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("Default Window Size")) {
|
||||
getSettings().video.enableFullscreen.setValue(false);
|
||||
VISetWindowFullscreen(false);
|
||||
VISetWindowSize(FB_WIDTH * 2, FB_HEIGHT * 2);
|
||||
VICenterWindow();
|
||||
}
|
||||
}
|
||||
|
||||
bool vsync = getSettings().video.enableVsync;
|
||||
if (ImGui::Checkbox("Enable Vsync", &vsync)) {
|
||||
getSettings().video.enableVsync.setValue(vsync);
|
||||
aurora_enable_vsync(vsync);
|
||||
config::Save();
|
||||
}
|
||||
|
||||
bool lockAspect = getSettings().video.lockAspectRatio;
|
||||
if (ImGui::Checkbox("Force 4:3 Aspect Ratio", &lockAspect)) {
|
||||
getSettings().video.lockAspectRatio.setValue(lockAspect);
|
||||
|
||||
if (lockAspect) {
|
||||
AuroraSetViewportPolicy(AURORA_VIEWPORT_FIT);
|
||||
} else {
|
||||
AuroraSetViewportPolicy(AURORA_VIEWPORT_STRETCH);
|
||||
}
|
||||
|
||||
config::Save();
|
||||
}
|
||||
|
||||
u32 internalResolutionWidth = 0;
|
||||
u32 internalResolutionHeight = 0;
|
||||
AuroraGetRenderSize(&internalResolutionWidth, &internalResolutionHeight);
|
||||
ImGui::TextDisabled("Current internal resolution: %ux%u", internalResolutionWidth,
|
||||
internalResolutionHeight);
|
||||
|
||||
int scale = std::clamp(getSettings().game.internalResolutionScale.getValue(), 0,
|
||||
kInternalResolutionScaleMax);
|
||||
if (ImGui::SliderInt("Internal Resolution", &scale, 0, kInternalResolutionScaleMax,
|
||||
scale == 0 ? "Auto" : "%dx"))
|
||||
{
|
||||
getSettings().game.internalResolutionScale.setValue(scale);
|
||||
VISetFrameBufferScale(static_cast<float>(scale));
|
||||
config::Save();
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Auto renders at the native window resolution.\n"
|
||||
"Higher values scale the game's internal framebuffer.");
|
||||
}
|
||||
|
||||
constexpr const char* bloomModeNames[] = {"Off", "Classic", "Dusk"};
|
||||
int bloomMode = static_cast<int>(getSettings().game.bloomMode.getValue());
|
||||
if (ImGui::BeginCombo("Bloom", bloomModeNames[bloomMode])) {
|
||||
for (int i = 0; i < IM_ARRAYSIZE(bloomModeNames); i++) {
|
||||
const bool selected = bloomMode == i;
|
||||
if (ImGui::Selectable(bloomModeNames[i], selected)) {
|
||||
getSettings().game.bloomMode.setValue(static_cast<BloomMode>(i));
|
||||
config::Save();
|
||||
}
|
||||
if (selected) {
|
||||
ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
bool bloomOff = bloomMode == static_cast<int>(BloomMode::Off);
|
||||
if (bloomOff) ImGui::BeginDisabled();
|
||||
float mult = getSettings().game.bloomMultiplier.getValue();
|
||||
if (ImGui::SliderFloat("Bloom Brightness", &mult, 0.0f, 1.0f, "%.2f")) {
|
||||
getSettings().game.bloomMultiplier.setValue(mult);
|
||||
config::Save();
|
||||
}
|
||||
if (bloomOff) ImGui::EndDisabled();
|
||||
|
||||
ImGui::Checkbox("Enable LOD Bias", &aurora::gx::enableLodBias);
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Audio")) {
|
||||
ImGui::Text("Master Volume");
|
||||
if (config::ImGuiSliderInt("##masterVolume", getSettings().audio.masterVolume, 0, 100)) {
|
||||
dusk::audio::SetMasterVolume(getSettings().audio.masterVolume / 100.0f);
|
||||
}
|
||||
|
||||
if (config::ImGuiCheckbox("Enable Reverb", getSettings().audio.enableReverb)) {
|
||||
dusk::audio::SetEnableReverb(getSettings().audio.enableReverb);
|
||||
}
|
||||
/*
|
||||
// TODO: Implement additional settings
|
||||
ImGui::Text("Main Music Volume");
|
||||
ImGui::SliderFloat("##mainMusicVolume", &getSettings().audio.mainMusicVolume, 0, 100);
|
||||
|
||||
ImGui::Text("Sub Music Volume");
|
||||
ImGui::SliderFloat("##subMusicVolume", &getSettings().audio.subMusicVolume, 0, 100);
|
||||
|
||||
ImGui::Text("Sound Effects Volume");
|
||||
ImGui::SliderFloat("##soundEffectsVolume", &getSettings().audio.soundEffectsVolume, 0, 100);
|
||||
|
||||
ImGui::Text("Fanfare Volume");
|
||||
ImGui::SliderFloat("##fanfareVolume", &getSettings().audio.fanfareVolume, 0, 100);
|
||||
|
||||
Z2AudioMgr* audioMgr = Z2AudioMgr::getInterface();
|
||||
if (audioMgr != nullptr) {
|
||||
}
|
||||
*/
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Controller")) {
|
||||
ImGui::MenuItem("Configure Controller", nullptr, &m_showControllerConfig);
|
||||
ImGui::Checkbox("Show Input Viewer", &m_showInputViewer);
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Interface")) {
|
||||
config::ImGuiCheckbox("Skip Pre-Launch UI", getSettings().backend.skipPreLaunchUI);
|
||||
config::ImGuiCheckbox("Show Pipeline Compilation", getSettings().backend.showPipelineCompilation);
|
||||
#if DUSK_ENABLE_SENTRY_NATIVE
|
||||
config::ImGuiCheckbox("Enable Crash Reporting", getSettings().backend.enableCrashReporting);
|
||||
#endif
|
||||
if (!IsMobile) {
|
||||
config::ImGuiCheckbox("Pause on Focus Lost", getSettings().game.pauseOnFocusLost);
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
#if DUSK_CAN_OPEN_DATA_FOLDER
|
||||
if (ImGui::MenuItem("Open Data Folder")) {
|
||||
OpenDataFolder();
|
||||
}
|
||||
#endif
|
||||
if (ImGui::BeginMenu("Settings")) {
|
||||
drawAudioMenu();
|
||||
drawCheatsMenu();
|
||||
drawGameplayMenu();
|
||||
drawGraphicsMenu();
|
||||
drawInputMenu();
|
||||
drawInterfaceMenu();
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
@@ -221,6 +58,416 @@ namespace dusk {
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiMenuGame::drawGraphicsMenu() {
|
||||
if (ImGui::BeginMenu("Graphics")) {
|
||||
ImGui::SeparatorText("Display");
|
||||
|
||||
if (!IsMobile) {
|
||||
if (ImGui::MenuItem("Toggle Fullscreen", hotkeys::TOGGLE_FULLSCREEN)) {
|
||||
ToggleFullscreen();
|
||||
}
|
||||
|
||||
if (ImGui::Button("Restore Default Window Size")) {
|
||||
getSettings().video.enableFullscreen.setValue(false);
|
||||
VISetWindowFullscreen(false);
|
||||
VISetWindowSize(FB_WIDTH * 2, FB_HEIGHT * 2);
|
||||
VICenterWindow();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
bool vsync = getSettings().video.enableVsync;
|
||||
if (ImGui::Checkbox("Enable VSync", &vsync)) {
|
||||
getSettings().video.enableVsync.setValue(vsync);
|
||||
aurora_enable_vsync(vsync);
|
||||
config::Save();
|
||||
}
|
||||
|
||||
bool lockAspect = getSettings().video.lockAspectRatio;
|
||||
if (ImGui::Checkbox("Force 4:3 Aspect Ratio", &lockAspect)) {
|
||||
getSettings().video.lockAspectRatio.setValue(lockAspect);
|
||||
|
||||
if (lockAspect) {
|
||||
AuroraSetViewportPolicy(AURORA_VIEWPORT_FIT);
|
||||
} else {
|
||||
AuroraSetViewportPolicy(AURORA_VIEWPORT_STRETCH);
|
||||
}
|
||||
|
||||
config::Save();
|
||||
}
|
||||
|
||||
ImGui::SeparatorText("Resolution");
|
||||
|
||||
u32 internalResolutionWidth = 0;
|
||||
u32 internalResolutionHeight = 0;
|
||||
AuroraGetRenderSize(&internalResolutionWidth, &internalResolutionHeight);
|
||||
ImGui::TextDisabled("Current internal resolution: %ux%u", internalResolutionWidth,
|
||||
internalResolutionHeight);
|
||||
|
||||
int scale = std::clamp(getSettings().game.internalResolutionScale.getValue(), 0,
|
||||
kInternalResolutionScaleMax);
|
||||
if (ImGui::SliderInt("Internal Resolution", &scale, 0, kInternalResolutionScaleMax,
|
||||
scale == 0 ? "Auto" : "%dx"))
|
||||
{
|
||||
getSettings().game.internalResolutionScale.setValue(scale);
|
||||
VISetFrameBufferScale(static_cast<float>(scale));
|
||||
config::Save();
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Auto renders at the native window resolution.\n"
|
||||
"Higher values scale the game's internal framebuffer.");
|
||||
}
|
||||
|
||||
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::SeparatorText("Post-Processing");
|
||||
|
||||
constexpr const char* bloomModeNames[] = {"Off", "Classic", "Dusk"};
|
||||
int bloomMode = static_cast<int>(getSettings().game.bloomMode.getValue());
|
||||
if (ImGui::BeginCombo("Bloom", bloomModeNames[bloomMode])) {
|
||||
for (int i = 0; i < IM_ARRAYSIZE(bloomModeNames); i++) {
|
||||
const bool selected = bloomMode == i;
|
||||
if (ImGui::Selectable(bloomModeNames[i], selected)) {
|
||||
getSettings().game.bloomMode.setValue(static_cast<BloomMode>(i));
|
||||
config::Save();
|
||||
}
|
||||
if (selected) {
|
||||
ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
bool bloomOff = bloomMode == static_cast<int>(BloomMode::Off);
|
||||
if (bloomOff) ImGui::BeginDisabled();
|
||||
float mult = getSettings().game.bloomMultiplier.getValue();
|
||||
if (ImGui::SliderFloat("Bloom Brightness", &mult, 0.0f, 1.0f, "%.2f")) {
|
||||
getSettings().game.bloomMultiplier.setValue(mult);
|
||||
config::Save();
|
||||
}
|
||||
if (bloomOff) ImGui::EndDisabled();
|
||||
|
||||
ImGui::SeparatorText("Rendering");
|
||||
|
||||
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.");
|
||||
}
|
||||
|
||||
ImGui::Checkbox("Enable LOD Bias", &aurora::gx::enableLodBias);
|
||||
|
||||
config::ImGuiCheckbox("Enable Depth of Field", getSettings().game.enableDepthOfField);
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiMenuGame::drawGameplayMenu() {
|
||||
if (ImGui::BeginMenu("Gameplay")) {
|
||||
ImGui::SeparatorText("General");
|
||||
|
||||
config::ImGuiCheckbox("Mirror Mode", getSettings().game.enableMirrorMode);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Mirrors the world horizontally, matching the Wii version of the game.");
|
||||
}
|
||||
|
||||
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!");
|
||||
}
|
||||
|
||||
config::ImGuiCheckbox("Restore Wii 1.0 Glitches", getSettings().game.restoreWiiGlitches);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Restores patched glitches from Wii USA 1.0,\n"
|
||||
"the first released version.");
|
||||
}
|
||||
|
||||
config::ImGuiCheckbox("Enable Rotating Link Doll", getSettings().game.enableLinkDollRotation);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Enables rotating Link in the collection menu with the C-Stick");
|
||||
}
|
||||
|
||||
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("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("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("Autosave", getSettings().game.autoSave);
|
||||
const bool autoSaveHovered = ImGui::IsItemHovered();
|
||||
ImGui::SameLine();
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.72f, 0.2f, 1.0f));
|
||||
ImGui::TextUnformatted("[EXPERIMENTAL]");
|
||||
ImGui::PopStyleColor();
|
||||
if (autoSaveHovered || ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Autosaves the game when going to a new area,\n"
|
||||
"opening a dungeon door, or getting a new item.");
|
||||
}
|
||||
|
||||
config::ImGuiCheckbox("Instant Saves", getSettings().game.instantSaves);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Skip the delay when writing to the Memory Card.");
|
||||
}
|
||||
|
||||
config::ImGuiCheckbox("Hold B for Instant Text", getSettings().game.instantText);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Make text scroll immediately by holding B.");
|
||||
}
|
||||
|
||||
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("No Rupee Returns", getSettings().game.noReturnRupees);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Always collect Rupees even if your Wallet is too full.");
|
||||
}
|
||||
|
||||
config::ImGuiCheckbox("No Sword Recoil", getSettings().game.noSwordRecoil);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
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("Skip the warning screen shown when starting the game.");
|
||||
}
|
||||
|
||||
config::ImGuiCheckbox("Sun's Song (R+X)", getSettings().game.sunsSong);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Allows Wolf Link to howl and change the time of day.");
|
||||
}
|
||||
|
||||
config::ImGuiCheckbox("Quick Transform (R+Y)", getSettings().game.enableQuickTransform);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Transform instantly by pressing R and Y simultaneously.");
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiMenuGame::drawCheatsMenu() {
|
||||
if (ImGui::BeginMenu("Cheats")) {
|
||||
ImGui::SeparatorText("Resources");
|
||||
config::ImGuiCheckbox("Infinite Hearts", getSettings().game.infiniteHearts);
|
||||
config::ImGuiCheckbox("Infinite Arrows", getSettings().game.infiniteArrows);
|
||||
config::ImGuiCheckbox("Infinite Bombs", getSettings().game.infiniteBombs);
|
||||
config::ImGuiCheckbox("Infinite Oil", getSettings().game.infiniteOil);
|
||||
config::ImGuiCheckbox("Infinite Oxygen", getSettings().game.infiniteOxygen);
|
||||
config::ImGuiCheckbox("Infinite Rupees", getSettings().game.infiniteRupees);
|
||||
config::ImGuiCheckbox("Items Don't Despawn", getSettings().game.enableIndefiniteItemDrops);
|
||||
ImGui::SetItemTooltip("Items Don't Despawn Unless You Load A Different Room In Which Case They Do But Even Under Some Circumstances They Don't, It Is Quite Rare Though");
|
||||
|
||||
ImGui::SeparatorText("Abilities");
|
||||
config::ImGuiCheckbox("Moon Jump (R+A)", getSettings().game.moonJump);
|
||||
config::ImGuiCheckbox("Super Clawshot", getSettings().game.superClawshot);
|
||||
config::ImGuiCheckbox("Always Greatspin", getSettings().game.alwaysGreatspin);
|
||||
config::ImGuiCheckbox("Fast Iron Boots", getSettings().game.enableFastIronBoots);
|
||||
|
||||
config::ImGuiCheckbox("Can Transform Anywhere", getSettings().game.canTransformAnywhere);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Allows you to transform even if NPCs are looking.");
|
||||
}
|
||||
|
||||
config::ImGuiCheckbox("Fast Spinner", getSettings().game.fastSpinner);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Speeds up Spinner movement when holding R.");
|
||||
}
|
||||
|
||||
config::ImGuiCheckbox("Free Magic Armor", getSettings().game.freeMagicArmor);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Makes the magic armor work without rupees.");
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiMenuGame::drawAudioMenu() {
|
||||
if (ImGui::BeginMenu("Audio")) {
|
||||
|
||||
ImGui::SeparatorText("Volume");
|
||||
|
||||
ImGui::Text("Master Volume");
|
||||
if (config::ImGuiSliderInt("##masterVolume", getSettings().audio.masterVolume, 0, 100)) {
|
||||
dusk::audio::SetMasterVolume(getSettings().audio.masterVolume / 100.0f);
|
||||
}
|
||||
|
||||
/*
|
||||
// TODO: Implement additional settings
|
||||
ImGui::Text("Main Music Volume");
|
||||
ImGui::SliderFloat("##mainMusicVolume", &getSettings().audio.mainMusicVolume, 0, 100);
|
||||
|
||||
ImGui::Text("Sub Music Volume");
|
||||
ImGui::SliderFloat("##subMusicVolume", &getSettings().audio.subMusicVolume, 0, 100);
|
||||
|
||||
ImGui::Text("Sound Effects Volume");
|
||||
ImGui::SliderFloat("##soundEffectsVolume", &getSettings().audio.soundEffectsVolume, 0, 100);
|
||||
|
||||
ImGui::Text("Fanfare Volume");
|
||||
ImGui::SliderFloat("##fanfareVolume", &getSettings().audio.fanfareVolume, 0, 100);
|
||||
|
||||
Z2AudioMgr* audioMgr = Z2AudioMgr::getInterface();
|
||||
if (audioMgr != nullptr) {
|
||||
}
|
||||
*/
|
||||
|
||||
ImGui::SeparatorText("Effects");
|
||||
|
||||
if (config::ImGuiCheckbox("Enable Reverb", getSettings().audio.enableReverb)) {
|
||||
dusk::audio::SetEnableReverb(getSettings().audio.enableReverb);
|
||||
}
|
||||
|
||||
|
||||
ImGui::SeparatorText("Tweaks");
|
||||
|
||||
config::ImGuiCheckbox("No Low HP Sound", getSettings().game.noLowHpSound);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Disable the beeping sound when having low health.");
|
||||
}
|
||||
|
||||
config::ImGuiCheckbox("Non-Stop Midna's Lament", getSettings().game.midnasLamentNonStop);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Prevents enemy music while Midna's Lament is playing.");
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiMenuGame::drawInputMenu() {
|
||||
if (ImGui::BeginMenu("Input")) {
|
||||
ImGui::SeparatorText("Controller");
|
||||
|
||||
if (ImGui::Button("Configure Controller")){
|
||||
m_showControllerConfig = !m_showControllerConfig;
|
||||
}
|
||||
|
||||
ImGui::SeparatorText("Camera");
|
||||
|
||||
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\n"
|
||||
"while in look mode (C-Up) and while aiming the\n"
|
||||
"Slingshot, Gale Boomerang, Hero's Bow, Clawshot(s),\n"
|
||||
"Ball and Chain, and Dominion Rod.");
|
||||
}
|
||||
|
||||
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::Checkbox("Show Input Viewer", &m_showInputViewer);
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiMenuGame::drawInterfaceMenu() {
|
||||
if (ImGui::BeginMenu("Interface")) {
|
||||
config::ImGuiCheckbox("Skip Pre-Launch UI", getSettings().backend.skipPreLaunchUI);
|
||||
config::ImGuiCheckbox("Show Pipeline Compilation", getSettings().backend.showPipelineCompilation);
|
||||
#if DUSK_ENABLE_SENTRY_NATIVE
|
||||
config::ImGuiCheckbox("Enable Crash Reporting", getSettings().backend.enableCrashReporting);
|
||||
#endif
|
||||
if (!IsMobile) {
|
||||
config::ImGuiCheckbox("Pause on Focus Lost", getSettings().game.pauseOnFocusLost);
|
||||
}
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
}
|
||||
|
||||
static void drawVirtualStick(const char* id, const ImVec2& stick) {
|
||||
float scale = ImGuiScale();
|
||||
ImGui::SetCursorPos(ImVec2(ImGui::GetCursorPos().x + 45 * scale, ImGui::GetCursorPos().y + 10));
|
||||
|
||||
@@ -19,6 +19,13 @@ namespace dusk {
|
||||
static void ToggleFullscreen();
|
||||
|
||||
private:
|
||||
void drawAudioMenu();
|
||||
void drawInputMenu();
|
||||
void drawGraphicsMenu();
|
||||
void drawGameplayMenu();
|
||||
void drawCheatsMenu();
|
||||
void drawInterfaceMenu();
|
||||
|
||||
struct {
|
||||
int m_selectedPort = 0;
|
||||
bool m_isReading = false;
|
||||
|
||||
@@ -16,10 +16,58 @@
|
||||
#include "dusk/main.h"
|
||||
#include "m_Do/m_Do_main.h"
|
||||
|
||||
#include <aurora/lib/internal.hpp>
|
||||
#include <SDL3/SDL_misc.h>
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <TargetConditionals.h>
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) || (defined(__APPLE__) && !TARGET_OS_IOS && !TARGET_OS_MACCATALYST) || (defined(__linux__) && !defined(__ANDROID__))
|
||||
#define DUSK_CAN_OPEN_DATA_FOLDER 1
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
static void OpenDataFolder() {
|
||||
const std::string path = fs::absolute(dusk::ConfigPath).generic_string();
|
||||
#if defined(_WIN32)
|
||||
const std::string url = std::string("file:///") + path;
|
||||
#else
|
||||
const std::string url = std::string("file://") + path;
|
||||
#endif
|
||||
(void)SDL_OpenURL(url.c_str());
|
||||
}
|
||||
#else
|
||||
#define DUSK_CAN_OPEN_DATA_FOLDER 0
|
||||
#endif
|
||||
|
||||
namespace dusk {
|
||||
ImGuiMenuTools::ImGuiMenuTools() {}
|
||||
|
||||
void ImGuiMenuTools::draw() {
|
||||
if (ImGui::BeginMenu("Tools")) {
|
||||
if (!dusk::IsGameLaunched) {
|
||||
ImGui::BeginDisabled();
|
||||
}
|
||||
|
||||
ImGui::MenuItem("Save Editor", hotkeys::SHOW_SAVE_EDITOR, &m_showSaveEditor);
|
||||
ImGui::MenuItem("Map Loader", hotkeys::SHOW_MAP_LOADER, &m_showMapLoader);
|
||||
ImGui::MenuItem("State Share", hotkeys::SHOW_STATE_SHARE, &m_showStateShare);
|
||||
|
||||
if (!dusk::IsGameLaunched) {
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
||||
#if DUSK_CAN_OPEN_DATA_FOLDER
|
||||
ImGui::Separator();
|
||||
if (ImGui::MenuItem("Open Data Folder")) {
|
||||
OpenDataFolder();
|
||||
}
|
||||
#endif
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
if (ImGui::BeginMenu("Debug")) {
|
||||
bool developmentMode = mDoMain::developmentMode == 1;
|
||||
if (ImGui::Checkbox("Development Mode", &developmentMode)) {
|
||||
@@ -59,9 +107,6 @@ namespace dusk {
|
||||
ImGui::MenuItem("Debug Overlay", hotkeys::SHOW_DEBUG_OVERLAY, &m_showDebugOverlay);
|
||||
ImGui::MenuItem("Heap Viewer", hotkeys::SHOW_HEAP_VIEWER, &m_showHeapOverlay);
|
||||
ImGui::MenuItem("Player Info", hotkeys::SHOW_PLAYER_INFO, &m_showPlayerInfo);
|
||||
ImGui::MenuItem("Save Editor", hotkeys::SHOW_SAVE_EDITOR, &m_showSaveEditor);
|
||||
ImGui::MenuItem("Map Loader", hotkeys::SHOW_MAP_LOADER, &m_showMapLoader);
|
||||
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);
|
||||
@@ -96,7 +141,9 @@ namespace dusk {
|
||||
ImGui::SetNextWindowBgAlpha(0.65f);
|
||||
if (ImGui::Begin("Debug Overlay", nullptr, windowFlags)) {
|
||||
ImGuiStringViewText(fmt::format(FMT_STRING("FPS: {:.2f}\n"), io.Framerate));
|
||||
ImGuiStringViewText(fmt::format(FMT_STRING("Frame usage: {:.1f}%\n"), frameUsagePct));
|
||||
if (frameUsagePct > 0.f) {
|
||||
ImGuiStringViewText(fmt::format(FMT_STRING("Frame usage: {:.1f}%\n"), frameUsagePct));
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
@@ -163,7 +210,7 @@ namespace dusk {
|
||||
ImGui::Text("Link");
|
||||
ImGuiStringViewText(
|
||||
player != nullptr
|
||||
? fmt::format("Position: {: .2f}, {: .2f}, {: .2f}\n", player->current.pos.x, player->current.pos.y, player->current.pos.z)
|
||||
? fmt::format("Position: {: .4f}, {: .4f}, {: .4f}\n", player->current.pos.x, player->current.pos.y, player->current.pos.z)
|
||||
: "Position: ?, ?, ?\n"
|
||||
);
|
||||
|
||||
@@ -175,7 +222,7 @@ namespace dusk {
|
||||
|
||||
ImGuiStringViewText(
|
||||
player != nullptr
|
||||
? fmt::format("Speed: {0}\n", player->speedF)
|
||||
? fmt::format("Speed: {: .4f}\n", player->speedF)
|
||||
: "Speed: ?\n"
|
||||
);
|
||||
|
||||
@@ -183,7 +230,7 @@ namespace dusk {
|
||||
ImGui::Text("Epona");
|
||||
ImGuiStringViewText(
|
||||
horse != nullptr
|
||||
? fmt::format("Position: {: .2f}, {: .2f}, {: .2f}\n", horse->current.pos.x, horse->current.pos.y, horse->current.pos.z)
|
||||
? fmt::format("Position: {: .4f}, {: .4f}, {: .4f}\n", horse->current.pos.x, horse->current.pos.y, horse->current.pos.z)
|
||||
: "Position: ?, ?, ?\n"
|
||||
);
|
||||
|
||||
@@ -195,7 +242,7 @@ namespace dusk {
|
||||
|
||||
ImGuiStringViewText(
|
||||
horse != nullptr
|
||||
? fmt::format("Speed: {0}\n", horse->speedF)
|
||||
? fmt::format("Speed: {: .4f}\n", horse->speedF)
|
||||
: "Speed: ?\n"
|
||||
);
|
||||
|
||||
|
||||
@@ -46,6 +46,17 @@ static std::string ShowIsoInvalidError(const iso::ValidationError code) {
|
||||
}
|
||||
}
|
||||
|
||||
static std::string_view card_type_name(CARDFileType type) {
|
||||
switch (type) {
|
||||
case CARD_GCIFOLDER:
|
||||
return "GCI Folder"sv;
|
||||
case CARD_RAWIMAGE:
|
||||
return "Card Image"sv;
|
||||
default:
|
||||
return ""sv;
|
||||
}
|
||||
}
|
||||
|
||||
void fileDialogCallback(void* userdata, const char* path, const char* error) {
|
||||
auto* self = static_cast<ImGuiPreLaunchWindow*>(userdata);
|
||||
if (error != nullptr) {
|
||||
@@ -216,6 +227,23 @@ void ImGuiPreLaunchWindow::drawOptions() {
|
||||
if (configuredBackendId != m_initialGraphicsBackend) {
|
||||
ImGui::TextDisabled("Restart Required");
|
||||
}
|
||||
auto curFileType = (CARDFileType)getSettings().backend.cardFileType.getValue();
|
||||
|
||||
if (ImGui::BeginCombo("Save File Type", card_type_name(curFileType).data())) {
|
||||
|
||||
if (ImGui::Selectable("GCI Folder", curFileType == CARD_GCIFOLDER)) {
|
||||
getSettings().backend.cardFileType.setValue(CARD_GCIFOLDER);
|
||||
config::Save();
|
||||
}
|
||||
|
||||
if (ImGui::Selectable("Card Image", curFileType == CARD_RAWIMAGE)) {
|
||||
getSettings().backend.cardFileType.setValue(CARD_RAWIMAGE);
|
||||
config::Save();
|
||||
}
|
||||
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "d/d_item_data.h"
|
||||
#include "d/d_meter2_info.h"
|
||||
#include "d/d_save.h"
|
||||
#include "d/actor/d_a_player.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
@@ -579,20 +580,21 @@ namespace dusk {
|
||||
|
||||
|
||||
if (ImGui::BeginCombo("Clothes", itemMap.find(statusA.mSelectEquip[0])->second.m_name.c_str())) {
|
||||
if (ImGui::Selectable("None")) {
|
||||
statusA.mSelectEquip[0] = dItemNo_NONE_e;
|
||||
}
|
||||
if (ImGui::Selectable("Ordon Clothes")) {
|
||||
statusA.mSelectEquip[0] = dItemNo_WEAR_CASUAL_e;
|
||||
dMeter2Info_setCloth(dItemNo_WEAR_CASUAL_e, false);
|
||||
daPy_getPlayerActorClass()->setClothesChange(0);
|
||||
}
|
||||
if (ImGui::Selectable("Hero's Clothes")) {
|
||||
statusA.mSelectEquip[0] = dItemNo_WEAR_KOKIRI_e;
|
||||
dMeter2Info_setCloth(dItemNo_WEAR_KOKIRI_e, false);
|
||||
daPy_getPlayerActorClass()->setClothesChange(0);
|
||||
}
|
||||
if (ImGui::Selectable("Zora Armor")) {
|
||||
statusA.mSelectEquip[0] = dItemNo_WEAR_ZORA_e;
|
||||
dMeter2Info_setCloth(dItemNo_WEAR_ZORA_e, false);
|
||||
daPy_getPlayerActorClass()->setClothesChange(0);
|
||||
}
|
||||
if (ImGui::Selectable("Magic Armor")) {
|
||||
statusA.mSelectEquip[0] = dItemNo_ARMOR_e;
|
||||
dMeter2Info_setCloth(dItemNo_ARMOR_e, false);
|
||||
daPy_getPlayerActorClass()->setClothesChange(0);
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
@@ -1489,11 +1491,11 @@ namespace dusk {
|
||||
}
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text(e.flagName.c_str());
|
||||
ImGuiStringViewText(e.flagName);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text(e.location.c_str());
|
||||
ImGuiStringViewText(e.location);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text(e.description.c_str());
|
||||
ImGuiStringViewText(e.description);
|
||||
}
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
@@ -5,14 +5,24 @@
|
||||
#include "imgui.h"
|
||||
#include "fmt/format.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "nlohmann/json.hpp"
|
||||
|
||||
#include "d/d_com_inf_game.h"
|
||||
#include "dusk/main.h"
|
||||
#include "dusk/io.hpp"
|
||||
#include "dusk/logging.h"
|
||||
#include "dusk/settings.h"
|
||||
#include "f_op/f_op_overlap_mng.h"
|
||||
#include "../file_select.hpp"
|
||||
#include "aurora/lib/window.hpp"
|
||||
|
||||
#include <unordered_set>
|
||||
#include <zstd.h>
|
||||
|
||||
namespace dusk {
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct StateSharePacket {
|
||||
char stageName[8];
|
||||
@@ -23,9 +33,65 @@ struct StateSharePacket {
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
static constexpr size_t PACKET_TOTAL = sizeof(StateSharePacket) + sizeof(dSv_info_c);
|
||||
static constexpr size_t PACKET_TOTAL = sizeof(StateSharePacket) + sizeof(dSv_info_c);
|
||||
static constexpr size_t PACKET_SAVE_ONLY = sizeof(StateSharePacket) + sizeof(dSv_save_c);
|
||||
static constexpr auto STATES_FILENAME = "states.json";
|
||||
|
||||
void ImGuiStateShare::copyState() {
|
||||
static bool ValidateEncodedState(const std::string&);
|
||||
|
||||
void ImGuiStateShare::onMergeFileSelected(void* userdata, const char* path, const char* /*error*/) {
|
||||
auto* self = static_cast<ImGuiStateShare*>(userdata);
|
||||
if (path != nullptr) {
|
||||
self->m_pendingMergePath = path;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static std::string GetStatesFilePath() {
|
||||
return (dusk::ConfigPath / STATES_FILENAME).string();
|
||||
}
|
||||
|
||||
void ImGuiStateShare::loadStatesFile() {
|
||||
m_loaded = true;
|
||||
const std::filesystem::path filePath = dusk::ConfigPath / STATES_FILENAME;
|
||||
if (!std::filesystem::exists(filePath)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const std::string pathStr = filePath.string();
|
||||
auto data = io::FileStream::ReadAllBytes(pathStr.c_str());
|
||||
auto j = json::parse(data);
|
||||
if (!j.is_array()) {
|
||||
return;
|
||||
}
|
||||
for (const auto& entry : j) {
|
||||
if (!entry.contains("name") || !entry.contains("data")) {
|
||||
continue;
|
||||
}
|
||||
SavedStateEntry s;
|
||||
s.name = entry["name"].get<std::string>();
|
||||
s.encoded = entry["data"].get<std::string>();
|
||||
m_states.push_back(std::move(s));
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
m_statusMsg = fmt::format("Failed to load states: {}", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiStateShare::saveStatesFile() {
|
||||
json j = json::array();
|
||||
for (const auto& s : m_states) {
|
||||
j.push_back(json{{"name", s.name}, {"data", s.encoded}});
|
||||
}
|
||||
try {
|
||||
io::FileStream::WriteAllText(GetStatesFilePath().c_str(), j.dump(2));
|
||||
} catch (const std::exception& e) {
|
||||
m_statusMsg = fmt::format("Failed to save states: {}", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
std::string ImGuiStateShare::encodeCurrentState() {
|
||||
StateSharePacket pkt = {};
|
||||
strncpy(pkt.stageName, dComIfGp_getStartStageName(), 7);
|
||||
pkt.roomNo = dComIfGp_getStartStageRoomNo();
|
||||
@@ -40,26 +106,25 @@ void ImGuiStateShare::copyState() {
|
||||
std::string compressed(bound, '\0');
|
||||
compressed.resize(ZSTD_compress(compressed.data(), bound, raw.data(), raw.size(), 1));
|
||||
|
||||
std::string encoded = absl::Base64Escape(compressed);
|
||||
ImGui::SetClipboardText(encoded.c_str());
|
||||
m_statusMsg = "Copied to clipboard.";
|
||||
return absl::Base64Escape(compressed);
|
||||
}
|
||||
|
||||
bool ImGuiStateShare::pasteState() {
|
||||
const char* clip = ImGui::GetClipboardText();
|
||||
if (!clip || clip[0] == '\0') {
|
||||
m_statusMsg = "Clipboard is empty.";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ImGuiStateShare::applyEncodedState(const std::string& encoded, const std::string& name) {
|
||||
std::string decoded;
|
||||
if (!absl::Base64Unescape(clip, &decoded)) {
|
||||
if (!absl::Base64Unescape(encoded, &decoded)) {
|
||||
m_statusMsg = "Invalid base64.";
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned long long dSize = ZSTD_getFrameContentSize(decoded.data(), decoded.size());
|
||||
if (dSize == ZSTD_CONTENTSIZE_ERROR || dSize == ZSTD_CONTENTSIZE_UNKNOWN || dSize < PACKET_TOTAL) {
|
||||
if (dSize == ZSTD_CONTENTSIZE_ERROR || dSize == ZSTD_CONTENTSIZE_UNKNOWN) {
|
||||
m_statusMsg = "Not a valid state string.";
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool isFull = (dSize == PACKET_TOTAL);
|
||||
const bool isPartial = (dSize == PACKET_SAVE_ONLY);
|
||||
if (!isFull && !isPartial) {
|
||||
m_statusMsg = "Not a valid state string.";
|
||||
return false;
|
||||
}
|
||||
@@ -75,45 +140,272 @@ bool ImGuiStateShare::pasteState() {
|
||||
memcpy(&pkt, raw.data(), sizeof(pkt));
|
||||
pkt.stageName[7] = '\0';
|
||||
|
||||
memcpy(&g_dComIfG_gameInfo.info, raw.data() + sizeof(pkt), sizeof(dSv_info_c));
|
||||
if (isFull) {
|
||||
memcpy(&g_dComIfG_gameInfo.info, raw.data() + sizeof(pkt), sizeof(dSv_info_c));
|
||||
m_pendingInfo = g_dComIfG_gameInfo.info;
|
||||
m_pendingSavedata.reset();
|
||||
} else {
|
||||
memcpy(&g_dComIfG_gameInfo.info.mSavedata, raw.data() + sizeof(pkt), sizeof(dSv_save_c));
|
||||
m_pendingSavedata = g_dComIfG_gameInfo.info.mSavedata;
|
||||
m_pendingInfo.reset();
|
||||
}
|
||||
|
||||
s16 spawnPoint = pkt.startPoint == -4 ? -1 : pkt.startPoint;
|
||||
|
||||
if (spawnPoint == -1) {
|
||||
dComIfGs_setRestartRoomParam(pkt.roomNo & 0x3F);
|
||||
}
|
||||
|
||||
dComIfGp_setNextStage(pkt.stageName, spawnPoint, pkt.roomNo, pkt.layer);
|
||||
m_pendingInfo = g_dComIfG_gameInfo.info;
|
||||
dusk::getTransientSettings().stateShareLoadActive = true;
|
||||
m_stateSharePeekSeen = false;
|
||||
dComIfGp_setNextStage(pkt.stageName, spawnPoint, pkt.roomNo, pkt.layer, 0.0f, 0, 1, 0, 0, 1, 3);
|
||||
|
||||
m_statusMsg = fmt::format("Warping to {} room {} layer {}.", pkt.stageName, (int)pkt.roomNo, (int)pkt.layer);
|
||||
if (name.empty()) {
|
||||
m_statusMsg = fmt::format("{} room {} layer {}.", pkt.stageName, (int)pkt.roomNo, (int)pkt.layer);
|
||||
} else {
|
||||
m_statusMsg = fmt::format("{}: {} room {} layer {}.", name, pkt.stageName, (int)pkt.roomNo, (int)pkt.layer);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImGuiStateShare::tickPendingApply() {
|
||||
if (!m_pendingInfo.has_value() || dComIfGp_isEnableNextStage())
|
||||
if (!m_pendingInfo.has_value() && !m_pendingSavedata.has_value()) {
|
||||
return;
|
||||
g_dComIfG_gameInfo.info = *m_pendingInfo;
|
||||
m_pendingInfo.reset();
|
||||
}
|
||||
if (dComIfGp_isEnableNextStage()) {
|
||||
return;
|
||||
}
|
||||
if (m_pendingInfo.has_value()) {
|
||||
g_dComIfG_gameInfo.info = *m_pendingInfo;
|
||||
m_pendingInfo.reset();
|
||||
} else {
|
||||
g_dComIfG_gameInfo.info.mSavedata = *m_pendingSavedata;
|
||||
m_pendingSavedata.reset();
|
||||
}
|
||||
dComIfGp_offOxygenShowFlag();
|
||||
dComIfGp_setMaxOxygen(600);
|
||||
dComIfGp_setOxygen(600);
|
||||
}
|
||||
|
||||
static bool ValidateEncodedState(const std::string& encoded) {
|
||||
std::string decoded;
|
||||
if (!absl::Base64Unescape(encoded, &decoded)) {
|
||||
return false;
|
||||
}
|
||||
unsigned long long dSize = ZSTD_getFrameContentSize(decoded.data(), decoded.size());
|
||||
return dSize == PACKET_TOTAL || dSize == PACKET_SAVE_ONLY;
|
||||
}
|
||||
|
||||
void ImGuiStateShare::mergeFromFile(const std::string& path) {
|
||||
try {
|
||||
auto data = io::FileStream::ReadAllBytes(path.c_str());
|
||||
auto j = json::parse(data);
|
||||
if (!j.is_array()) {
|
||||
m_statusMsg = "File does not contain a JSON array.";
|
||||
return;
|
||||
}
|
||||
|
||||
std::unordered_set<std::string> existingNames;
|
||||
for (const auto& s : m_states) {
|
||||
existingNames.insert(s.name);
|
||||
}
|
||||
|
||||
int added = 0;
|
||||
int skipped = 0;
|
||||
for (const auto& entry : j) {
|
||||
if (!entry.contains("name") || !entry.contains("data")) {
|
||||
++skipped;
|
||||
continue;
|
||||
}
|
||||
const std::string name = entry["name"].get<std::string>();
|
||||
const std::string encoded = entry["data"].get<std::string>();
|
||||
if (!ValidateEncodedState(encoded)) {
|
||||
++skipped;
|
||||
continue;
|
||||
}
|
||||
if (existingNames.count(name)) {
|
||||
++skipped;
|
||||
continue;
|
||||
}
|
||||
SavedStateEntry s;
|
||||
s.name = name;
|
||||
s.encoded = encoded;
|
||||
existingNames.insert(s.name);
|
||||
m_states.push_back(std::move(s));
|
||||
++added;
|
||||
}
|
||||
|
||||
if (added > 0) {
|
||||
saveStatesFile();
|
||||
}
|
||||
m_statusMsg = fmt::format("Merged: {} added, {} skipped.", added, skipped);
|
||||
} catch (const std::exception& e) {
|
||||
m_statusMsg = fmt::format("Failed to load file: {}", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
void ImGuiStateShare::draw(bool& open) {
|
||||
if (dusk::IsGameLaunched)
|
||||
if (dusk::IsGameLaunched) {
|
||||
tickPendingApply();
|
||||
if (dusk::getTransientSettings().stateShareLoadActive) {
|
||||
if (fopOvlpM_IsPeek()) {
|
||||
m_stateSharePeekSeen = true;
|
||||
} else if (m_stateSharePeekSeen) {
|
||||
dusk::getTransientSettings().stateShareLoadActive = false;
|
||||
m_stateSharePeekSeen = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!open)
|
||||
if (!m_loaded) {
|
||||
loadStatesFile();
|
||||
}
|
||||
|
||||
if (!m_pendingMergePath.empty()) {
|
||||
mergeFromFile(m_pendingMergePath);
|
||||
m_pendingMergePath.clear();
|
||||
}
|
||||
|
||||
if (!open) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ImGui::Begin("State Share", &open, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav)) {
|
||||
ImGui::SetNextWindowSizeConstraints(ImVec2(400, 0), ImVec2(FLT_MAX, FLT_MAX));
|
||||
if (!ImGui::Begin("State Manager", &open, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav)) {
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!dusk::IsGameLaunched) ImGui::BeginDisabled();
|
||||
if (ImGui::Button("Copy State")) copyState();
|
||||
const bool gameRunning = dusk::IsGameLaunched;
|
||||
const bool loadInProgress = dusk::getTransientSettings().stateShareLoadActive;
|
||||
|
||||
const float rowH = ImGui::GetTextLineHeightWithSpacing();
|
||||
const float listH = rowH * 8 + ImGui::GetStyle().FramePadding.y * 2;
|
||||
ImGui::BeginChild("##states", ImVec2(0, listH), true);
|
||||
|
||||
if (m_states.empty()) {
|
||||
ImGui::TextDisabled("No saved states. Save or import one below.");
|
||||
}
|
||||
|
||||
int toDelete = -1;
|
||||
for (int i = 0; i < (int)m_states.size(); ++i) {
|
||||
ImGui::PushID(i);
|
||||
|
||||
if (m_renamingIndex == i) {
|
||||
ImGui::SetNextItemWidth(150);
|
||||
bool done = ImGui::InputText("##rename", m_renameBuffer, sizeof(m_renameBuffer),
|
||||
ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll);
|
||||
if (done) {
|
||||
if (m_renameBuffer[0] != '\0') {
|
||||
m_states[i].name = m_renameBuffer;
|
||||
}
|
||||
m_renamingIndex = -1;
|
||||
saveStatesFile();
|
||||
} else if (ImGui::IsItemDeactivated()) {
|
||||
m_renamingIndex = -1;
|
||||
}
|
||||
} else {
|
||||
ImGui::Selectable(m_states[i].name.c_str(), false, ImGuiSelectableFlags_None, ImVec2(150, 0));
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Double-click to rename");
|
||||
if (ImGui::IsMouseDoubleClicked(0)) {
|
||||
m_renamingIndex = i;
|
||||
strncpy(m_renameBuffer, m_states[i].name.c_str(), sizeof(m_renameBuffer) - 1);
|
||||
m_renameBuffer[sizeof(m_renameBuffer) - 1] = '\0';
|
||||
ImGui::SetKeyboardFocusHere(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
if (!gameRunning || loadInProgress) { ImGui::BeginDisabled(); }
|
||||
if (ImGui::Button("Load")) {
|
||||
applyEncodedState(m_states[i].encoded, m_states[i].name);
|
||||
}
|
||||
if (!gameRunning || loadInProgress) { ImGui::EndDisabled(); }
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Copy")) {
|
||||
ImGui::SetClipboardText(m_states[i].encoded.c_str());
|
||||
m_statusMsg = fmt::format("'{}' copied to clipboard.", m_states[i].name);
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Del")) {
|
||||
toDelete = i;
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
if (toDelete >= 0) {
|
||||
if (m_renamingIndex == toDelete) { m_renamingIndex = -1; }
|
||||
m_states.erase(m_states.begin() + toDelete);
|
||||
saveStatesFile();
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
|
||||
// Toolbar
|
||||
if (!gameRunning) { ImGui::BeginDisabled(); }
|
||||
if (ImGui::Button("Save")) {
|
||||
SavedStateEntry entry;
|
||||
entry.name = fmt::format("State {}", m_states.size() + 1);
|
||||
entry.encoded = encodeCurrentState();
|
||||
m_states.push_back(std::move(entry));
|
||||
saveStatesFile();
|
||||
m_statusMsg = fmt::format("Saved as '{}'.", m_states.back().name);
|
||||
}
|
||||
if (!gameRunning) { ImGui::EndDisabled(); }
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Import State")) pasteState();
|
||||
if (!dusk::IsGameLaunched) ImGui::EndDisabled();
|
||||
if (ImGui::Button("Import Clipboard")) {
|
||||
const char* clip = ImGui::GetClipboardText();
|
||||
if (!clip || clip[0] == '\0') {
|
||||
m_statusMsg = "Clipboard is empty.";
|
||||
} else {
|
||||
std::string clipStr = clip;
|
||||
if (!ValidateEncodedState(clipStr)) {
|
||||
m_statusMsg = "Clipboard does not contain a valid state.";
|
||||
} else {
|
||||
SavedStateEntry entry;
|
||||
entry.name = fmt::format("Imported {}", m_states.size() + 1);
|
||||
entry.encoded = std::move(clipStr);
|
||||
m_states.push_back(std::move(entry));
|
||||
saveStatesFile();
|
||||
m_statusMsg = fmt::format("Imported as '{}'.", m_states.back().name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Load Pack")) {
|
||||
static constexpr SDL_DialogFileFilter filter = {"State pack", "json"};
|
||||
ShowFileSelect(&onMergeFileSelected, this, aurora::window::get_sdl_window(), &filter, 1, nullptr, false);
|
||||
}
|
||||
|
||||
if (!m_states.empty()) {
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Clear All")) {
|
||||
ImGui::OpenPopup("##clearall");
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopup("##clearall")) {
|
||||
ImGui::Text("Delete all saved states?");
|
||||
ImGui::Spacing();
|
||||
if (ImGui::Button("Yes, clear all")) {
|
||||
m_states.clear();
|
||||
m_renamingIndex = -1;
|
||||
saveStatesFile();
|
||||
m_statusMsg = "All states cleared.";
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Cancel")) {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_statusMsg.empty()) {
|
||||
ImGui::Spacing();
|
||||
@@ -125,8 +417,9 @@ void ImGuiStateShare::draw(bool& open) {
|
||||
}
|
||||
|
||||
void ImGuiMenuTools::ShowStateShare() {
|
||||
if (!ImGuiConsole::CheckMenuViewToggle(ImGuiKey_F8, m_showStateShare))
|
||||
if (!ImGuiConsole::CheckMenuViewToggle(ImGuiKey_F8, m_showStateShare)) {
|
||||
return;
|
||||
}
|
||||
m_stateShare.draw(m_showStateShare);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,21 +4,39 @@
|
||||
#include "d/d_save.h"
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace dusk {
|
||||
class ImGuiStateShare {
|
||||
public:
|
||||
void draw(bool& open);
|
||||
|
||||
private:
|
||||
void copyState();
|
||||
bool pasteState();
|
||||
void tickPendingApply();
|
||||
struct SavedStateEntry {
|
||||
std::string name;
|
||||
std::string encoded;
|
||||
};
|
||||
|
||||
class ImGuiStateShare {
|
||||
public:
|
||||
void draw(bool& open);
|
||||
|
||||
private:
|
||||
std::string encodeCurrentState();
|
||||
bool applyEncodedState(const std::string& encoded, const std::string& name = {});
|
||||
void tickPendingApply();
|
||||
void loadStatesFile();
|
||||
void saveStatesFile();
|
||||
void mergeFromFile(const std::string& path);
|
||||
static void onMergeFileSelected(void* userdata, const char* path, const char* error);
|
||||
|
||||
std::vector<SavedStateEntry> m_states;
|
||||
std::string m_statusMsg;
|
||||
std::optional<dSv_info_c> m_pendingInfo;
|
||||
std::optional<dSv_save_c> m_pendingSavedata;
|
||||
int m_renamingIndex = -1;
|
||||
char m_renameBuffer[128] = {};
|
||||
bool m_loaded = false;
|
||||
bool m_stateSharePeekSeen = false;
|
||||
std::string m_pendingMergePath;
|
||||
};
|
||||
|
||||
std::string m_statusMsg;
|
||||
std::optional<dSv_info_c> m_pendingInfo;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -168,14 +168,14 @@ void aurora_log_callback(AuroraLogLevel level, const char* module, const char* m
|
||||
|
||||
aurora::Module DuskLog("dusk");
|
||||
|
||||
void dusk::InitializeFileLogging(const char* configDir, AuroraLogLevel logLevel) {
|
||||
void dusk::InitializeFileLogging(const std::filesystem::path& configDir, AuroraLogLevel logLevel) {
|
||||
std::lock_guard lock(g_logMutex);
|
||||
if (g_logFile != nullptr || configDir == nullptr) {
|
||||
if (g_logFile != nullptr || configDir.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::error_code ec;
|
||||
const std::filesystem::path logsDir = std::filesystem::path(configDir) / "logs";
|
||||
const std::filesystem::path logsDir = configDir / "logs";
|
||||
std::filesystem::create_directories(logsDir, ec);
|
||||
if (ec) {
|
||||
std::fprintf(stderr, "[WARNING | dusk] Failed to create log directory '%s': %s\n",
|
||||
|
||||
+17
-9
@@ -44,6 +44,7 @@ UserSettings g_userSettings = {
|
||||
.invertCameraXAxis {"game.invertCameraXAxis", false},
|
||||
.disableMainHUD {"game.disableMainHUD", false},
|
||||
.pauseOnFocusLost {"game.pauseOnFocusLost", false},
|
||||
.enableLinkDollRotation = {"game.enableLinkDollRotation", false },
|
||||
|
||||
// Graphics
|
||||
.bloomMode {"game.bloomMode", BloomMode::Classic},
|
||||
@@ -52,6 +53,7 @@ UserSettings g_userSettings = {
|
||||
.enableFrameInterpolation {"game.enableFrameInterpolation", false},
|
||||
.internalResolutionScale {"game.internalResolutionScale", 0},
|
||||
.shadowResolutionMultiplier {"game.shadowResolutionMultiplier", 1},
|
||||
.enableDepthOfField {"game.enableDepthOfField", true},
|
||||
|
||||
// Audio
|
||||
.noLowHpSound {"game.noLowHpSound", false},
|
||||
@@ -70,14 +72,15 @@ UserSettings g_userSettings = {
|
||||
|
||||
// Cheats
|
||||
.infiniteHearts {"game.infiniteHearts", false},
|
||||
.infiniteArrows {"game.infiniteArrows", false},
|
||||
.infiniteBombs {"game.infiniteBombs", false},
|
||||
.infiniteOil {"game.infiniteOil", false},
|
||||
.infiniteOxygen {"game.infiniteOxygen", false},
|
||||
.infiniteRupees {"game.infiniteRupees", false},
|
||||
.moonJump {"game.moonJump", false},
|
||||
.superClawshot {"game.superClawshot", false},
|
||||
.alwaysGreatspin {"game.alwaysGreatspin", false},
|
||||
.infiniteArrows{"game.infiniteArrows", false},
|
||||
.infiniteBombs{"game.infiniteBombs", false},
|
||||
.infiniteOil{"game.infiniteOil", false},
|
||||
.infiniteOxygen{"game.infiniteOxygen", false},
|
||||
.infiniteRupees{"game.infiniteRupees", false},
|
||||
.enableIndefiniteItemDrops {"game.enableIndefiniteItemDrops", false},
|
||||
.moonJump{"game.moonJump", false},
|
||||
.superClawshot{"game.superClawshot", false},
|
||||
.alwaysGreatspin{"game.alwaysGreatspin", false},
|
||||
.enableFastIronBoots {"game.enableFastIronBoots", false},
|
||||
.canTransformAnywhere {"game.canTransformAnywhere", false},
|
||||
.fastSpinner {"game.fastSpinner", false},
|
||||
@@ -97,7 +100,8 @@ UserSettings g_userSettings = {
|
||||
.showPipelineCompilation {"backend.showPipelineCompilation", false},
|
||||
.wasPresetChosen {"backend.wasPresetChosen", false},
|
||||
.enableCrashReporting {"backend.enableCrashReporting", true},
|
||||
.duskMenuOpen {"backend.duskMenuOpen", false}
|
||||
.duskMenuOpen {"backend.duskMenuOpen", false},
|
||||
.cardFileType {"backend.cardFileType", static_cast<int>(CARD_GCIFOLDER)}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -145,10 +149,12 @@ void registerSettings() {
|
||||
Register(g_userSettings.game.disableWaterRefraction);
|
||||
Register(g_userSettings.game.internalResolutionScale);
|
||||
Register(g_userSettings.game.shadowResolutionMultiplier);
|
||||
Register(g_userSettings.game.enableDepthOfField);
|
||||
Register(g_userSettings.game.enableFastIronBoots);
|
||||
Register(g_userSettings.game.canTransformAnywhere);
|
||||
Register(g_userSettings.game.freeMagicArmor);
|
||||
Register(g_userSettings.game.restoreWiiGlitches);
|
||||
Register(g_userSettings.game.enableLinkDollRotation);
|
||||
Register(g_userSettings.game.noMissClimbing);
|
||||
Register(g_userSettings.game.noLowHpSound);
|
||||
Register(g_userSettings.game.midnasLamentNonStop);
|
||||
@@ -160,6 +166,7 @@ void registerSettings() {
|
||||
Register(g_userSettings.game.infiniteOil);
|
||||
Register(g_userSettings.game.infiniteOxygen);
|
||||
Register(g_userSettings.game.infiniteRupees);
|
||||
Register(g_userSettings.game.enableIndefiniteItemDrops);
|
||||
Register(g_userSettings.game.moonJump);
|
||||
Register(g_userSettings.game.superClawshot);
|
||||
Register(g_userSettings.game.alwaysGreatspin);
|
||||
@@ -181,6 +188,7 @@ void registerSettings() {
|
||||
Register(g_userSettings.backend.wasPresetChosen);
|
||||
Register(g_userSettings.backend.enableCrashReporting);
|
||||
Register(g_userSettings.backend.duskMenuOpen);
|
||||
Register(g_userSettings.backend.cardFileType);
|
||||
}
|
||||
|
||||
// Transient settings
|
||||
|
||||
+3
-89
@@ -25,9 +25,8 @@
|
||||
|
||||
#if TARGET_PC
|
||||
#include "tracy/Tracy.hpp"
|
||||
#include <dusk/imgui/ImGuiConsole.hpp>
|
||||
#include <m_Do/m_Do_MemCardRWmng.h>
|
||||
#include <m_Do/m_Do_MemCard.h>
|
||||
#include <dusk/gamepad_color.h>
|
||||
#include <dusk/autosave.h>
|
||||
#endif
|
||||
|
||||
fapGm_HIO_c::fapGm_HIO_c() {
|
||||
@@ -739,93 +738,8 @@ static void fapGm_AfterRecord() {
|
||||
fapGm_After();
|
||||
}
|
||||
|
||||
u8 mSaveBuffer[QUEST_LOG_SIZE * 3];
|
||||
u8 mAutoSaveProc = 0;
|
||||
int autoSaveWriteState = 0;
|
||||
|
||||
typedef void (*AutoSaveFuncs)();
|
||||
static AutoSaveFuncs AutoSaveFuncsProc[] = {
|
||||
noAutoSave,
|
||||
enterAutoSave,
|
||||
autoSaving,
|
||||
waitingForWrite,
|
||||
endAutoSave,
|
||||
};
|
||||
|
||||
void noAutoSave() {}
|
||||
|
||||
void triggerAutoSave() {
|
||||
if (dusk::getSettings().game.autoSave && mAutoSaveProc == 0 && strcmp(dComIfGp_getStartStageName(), "F_SP102") != 0 && dComIfGp_getStartStageLayer() != 0)
|
||||
{
|
||||
mAutoSaveProc = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void updateAutoSave() {
|
||||
(AutoSaveFuncsProc[mAutoSaveProc])();
|
||||
}
|
||||
|
||||
void writeAutoSave() {
|
||||
dComIfGs_setMemoryToCard(mSaveBuffer, dComIfGs_getDataNum());
|
||||
mDoMemCdRWm_SetCheckSumGameData(mSaveBuffer, dComIfGs_getDataNum());
|
||||
|
||||
u8* save = mSaveBuffer;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
mDoMemCdRWm_TestCheckSumGameData(save);
|
||||
save += QUEST_LOG_SIZE;
|
||||
}
|
||||
|
||||
g_mDoMemCd_control.save(mSaveBuffer, sizeof(mSaveBuffer), 0);
|
||||
}
|
||||
|
||||
void autoSaving() {
|
||||
int cardState = g_mDoMemCd_control.LoadSync(mSaveBuffer, sizeof(mSaveBuffer), 0);
|
||||
if (cardState != 0) {
|
||||
if (cardState == 2) {
|
||||
mAutoSaveProc = 1;
|
||||
} else if (cardState == 1) {
|
||||
writeAutoSave();
|
||||
mAutoSaveProc = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void enterAutoSave() {
|
||||
u32 cardStatus = g_mDoMemCd_control.getStatus(0);
|
||||
|
||||
if (cardStatus != 14) {
|
||||
switch (cardStatus) {
|
||||
case 2:
|
||||
g_mDoMemCd_control.load();
|
||||
mAutoSaveProc = 2;
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
break;
|
||||
default:
|
||||
mAutoSaveProc = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void waitingForWrite() {
|
||||
autoSaveWriteState = g_mDoMemCd_control.SaveSync();
|
||||
|
||||
if (autoSaveWriteState == 2) {
|
||||
mAutoSaveProc = 0;
|
||||
} else if (autoSaveWriteState == 1) {
|
||||
mAutoSaveProc = 4;
|
||||
}
|
||||
}
|
||||
|
||||
void endAutoSave() {
|
||||
dusk::g_imguiConsole.ShowToast("Saving...", 2.0f);
|
||||
mAutoSaveProc = 0;
|
||||
}
|
||||
|
||||
static void duskExecute() {
|
||||
handleGamepadColor();
|
||||
updateAutoSave();
|
||||
|
||||
if (mDoCPd_c::getHoldR(PAD_1) && mDoCPd_c::getTrigX(PAD_1)) {
|
||||
|
||||
@@ -77,6 +77,8 @@ static OSThread MemCardThread;
|
||||
|
||||
void mDoMemCd_Ctrl_c::ThdInit() {
|
||||
#if !PLATFORM_SHIELD
|
||||
CARDSetLoadType((CARDFileType)dusk::getSettings().backend.cardFileType.getValue());
|
||||
|
||||
CARDInit(DUSK_GAME_NAME, DUSK_GAME_VERSION);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -351,8 +351,13 @@ void mDoExt_modelUpdateDL(J3DModel* i_model) {
|
||||
|
||||
void mDoExt_modelEntryDL(J3DModel* i_model) {
|
||||
#if TARGET_PC
|
||||
if (!dusk::frame_interp::is_sim_frame())
|
||||
if (!dusk::frame_interp::is_sim_frame()) {
|
||||
// FRAME INTERP NOTE: This fixes issue #355 where some lights would flicker.
|
||||
// This is likely better solved by updating J3DMaterial::needsInterpCallBack,
|
||||
// but it's unclear what exactly needs to be added.
|
||||
i_model->diff();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
modelMtxErrorCheck(i_model);
|
||||
|
||||
@@ -1029,15 +1029,8 @@ static void drawDepth2(view_class* param_0, view_port_class* param_1, int param_
|
||||
GX_FALSE, 0);
|
||||
}
|
||||
|
||||
#if TARGET_PC
|
||||
// use full size for pc for higher quality background elements
|
||||
u16 halfWidth = width;
|
||||
u16 halfHeight = height;
|
||||
#else
|
||||
u16 halfWidth = width >> 1;
|
||||
u16 halfHeight = height >> 1;
|
||||
#endif
|
||||
|
||||
GXRenderModeObj* sp24 = JUTGetVideoManager()->getRenderMode();
|
||||
GXSetCopyFilter(GX_FALSE, NULL, GX_TRUE, sp24->vfilter);
|
||||
GXSetTexCopySrc(x_orig, y_orig_pos, width, height);
|
||||
@@ -1155,6 +1148,9 @@ static void drawDepth2(view_class* param_0, view_port_class* param_1, int param_
|
||||
GXSetProjection(ortho, GX_ORTHOGRAPHIC);
|
||||
GXSetCurrentMtx(0);
|
||||
|
||||
#ifdef TARGET_PC
|
||||
if (dusk::getSettings().game.enableDepthOfField)
|
||||
#endif
|
||||
if (l_tevColor0.a > -255 && sp8 == 1) {
|
||||
GXBegin(GX_QUADS, GX_VTXFMT0, 4);
|
||||
GXPosition3s16(x_orig, y_orig_pos, -5);
|
||||
|
||||
+55
-48
@@ -66,13 +66,15 @@
|
||||
|
||||
#include "SDL3/SDL_filesystem.h"
|
||||
#include "cxxopts.hpp"
|
||||
#include "d/actor/d_a_movie_player.h"
|
||||
#include "dusk/audio/DuskAudioSystem.h"
|
||||
#include "dusk/config.hpp"
|
||||
#include "dusk/settings.h"
|
||||
#include "dusk/imgui/ImGuiConsole.hpp"
|
||||
#include "dusk/settings.h"
|
||||
#include "dusk/discord_presence.hpp"
|
||||
#include "tracy/Tracy.hpp"
|
||||
#include "f_pc/f_pc_draw.h"
|
||||
#include "tracy/Tracy.hpp"
|
||||
|
||||
// --- GLOBALS ---
|
||||
s8 mDoMain::developmentMode = -1;
|
||||
@@ -96,6 +98,7 @@ bool dusk::IsRunning = true;
|
||||
bool dusk::IsShuttingDown = false;
|
||||
bool dusk::IsGameLaunched = false;
|
||||
bool dusk::IsFocusPaused = false;
|
||||
std::filesystem::path dusk::ConfigPath;
|
||||
#endif
|
||||
|
||||
s32 LOAD_COPYDATE(void*) {
|
||||
@@ -128,7 +131,6 @@ s32 LOAD_COPYDATE(void*) {
|
||||
AuroraInfo auroraInfo;
|
||||
AuroraStats dusk::lastFrameAuroraStats;
|
||||
float dusk::frameUsagePct = 0.0f;
|
||||
const char* configPath;
|
||||
|
||||
bool launchUILoop() {
|
||||
while (dusk::IsRunning && !dusk::IsGameLaunched) {
|
||||
@@ -240,8 +242,6 @@ void main01(void) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const dusk::game_clock::MainLoopPacer pacing = dusk::game_clock::advance_main_loop();
|
||||
|
||||
VIWaitForRetrace();
|
||||
|
||||
dusk::lastFrameAuroraStats = *aurora_get_stats();
|
||||
@@ -252,28 +252,33 @@ void main01(void) {
|
||||
|
||||
mDoGph_gInf_c::updateRenderSize();
|
||||
|
||||
dusk::frame_interp::begin_frame(pacing.is_interpolating, pacing.do_sim_tick, pacing.interpolation_step);
|
||||
const auto pacing = dusk::game_clock::advance_main_loop();
|
||||
if (pacing.is_interpolating) {
|
||||
if (pacing.do_sim_tick) {
|
||||
if (pacing.sim_ticks_to_run > 0) {
|
||||
dusk::frame_interp::begin_frame(true, true, 0.0f);
|
||||
dusk::frame_interp::set_ui_tick_pending(true);
|
||||
mDoCPd_c::read();
|
||||
DuskDebugPad();
|
||||
dusk::gyro::read(pacing.sim_pace);
|
||||
fapGm_Execute();
|
||||
mDoAud_Execute();
|
||||
dusk::game_clock::reset_accumulator();
|
||||
for (int sim_tick = 0; sim_tick < pacing.sim_ticks_to_run; ++sim_tick) {
|
||||
dusk::frame_interp::begin_sim_tick();
|
||||
mDoCPd_c::read();
|
||||
DuskDebugPad();
|
||||
dusk::gyro::read(pacing.sim_pace);
|
||||
fapGm_Execute();
|
||||
mDoAud_Execute();
|
||||
dusk::game_clock::commit_sim_tick();
|
||||
}
|
||||
}
|
||||
|
||||
dusk::frame_interp::begin_frame(true, false,
|
||||
dusk::game_clock::sample_interpolation_step());
|
||||
dusk::frame_interp::interpolate();
|
||||
dusk::frame_interp::begin_presentation_camera();
|
||||
if (!pacing.do_sim_tick) {
|
||||
// run draw functions for anything specially marked to handle interp on non-sim
|
||||
// ticks
|
||||
fpcM_DrawIterater((fpcM_DrawIteraterFunc)fpcM_Draw);
|
||||
}
|
||||
// run draw functions for anything specially marked to handle interp
|
||||
fpcM_DrawIterater((fpcM_DrawIteraterFunc)fpcM_Draw);
|
||||
cAPIGph_Painter();
|
||||
dusk::frame_interp::end_presentation_camera();
|
||||
dusk::frame_interp::set_ui_tick_pending(false);
|
||||
} else {
|
||||
dusk::frame_interp::begin_frame(false, true, 0.0f);
|
||||
dusk::frame_interp::set_ui_tick_pending(true);
|
||||
|
||||
// Game Inputs
|
||||
@@ -377,7 +382,7 @@ static void ApplyCVarOverrides(const cxxopts::OptionValue& option) {
|
||||
}
|
||||
}
|
||||
|
||||
static const char* CalculateConfigPath() {
|
||||
static std::filesystem::path CalculateConfigPath() {
|
||||
const auto result = SDL_GetPrefPath(dusk::OrgName, dusk::AppName);
|
||||
if (!result) {
|
||||
DuskLog.fatal("Unable to get PrefPath: {}", SDL_GetError());
|
||||
@@ -386,13 +391,12 @@ static const char* CalculateConfigPath() {
|
||||
return result;
|
||||
}
|
||||
|
||||
static void EnsureInitialPipelineCache(const char* configDir) {
|
||||
if (configDir == nullptr) {
|
||||
static void EnsureInitialPipelineCache(const std::filesystem::path& configDir) {
|
||||
if (configDir.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::filesystem::path configPathFs(configDir);
|
||||
const std::filesystem::path pipelineCachePath = configPathFs / "pipeline_cache.db";
|
||||
const std::filesystem::path pipelineCachePath = configDir / "pipeline_cache.db";
|
||||
if (std::filesystem::exists(pipelineCachePath)) {
|
||||
return;
|
||||
}
|
||||
@@ -411,10 +415,10 @@ static void EnsureInitialPipelineCache(const char* configDir) {
|
||||
}
|
||||
|
||||
std::error_code ec;
|
||||
std::filesystem::create_directories(configPathFs, ec);
|
||||
std::filesystem::create_directories(configDir, ec);
|
||||
if (ec) {
|
||||
DuskLog.warn("Failed to create config directory '{}' for pipeline cache: {}",
|
||||
configPathFs.string(), ec.message());
|
||||
configDir.string(), ec.message());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -505,37 +509,38 @@ int game_main(int argc, char* argv[]) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
configPath = CalculateConfigPath();
|
||||
dusk::ConfigPath = CalculateConfigPath();
|
||||
const auto startupLogLevel = static_cast<AuroraLogLevel>(parsed_arg_options["log-level"].as<uint8_t>());
|
||||
dusk::InitializeFileLogging(configPath, startupLogLevel);
|
||||
dusk::InitializeFileLogging(dusk::ConfigPath, startupLogLevel);
|
||||
|
||||
dusk::config::LoadFromUserPreferences();
|
||||
ApplyCVarOverrides(parsed_arg_options["cvar"]);
|
||||
dusk::InitializeCrashReporting();
|
||||
EnsureInitialPipelineCache(configPath);
|
||||
|
||||
AuroraConfig config{};
|
||||
config.appName = dusk::AppName;
|
||||
config.configPath = configPath;
|
||||
config.vsync = dusk::getSettings().video.enableVsync;
|
||||
config.startFullscreen = dusk::getSettings().video.enableFullscreen;
|
||||
config.windowPosX = -1;
|
||||
config.windowPosY = -1;
|
||||
config.windowWidth = defaultWindowWidth * 2;
|
||||
config.windowHeight = defaultWindowHeight * 2;
|
||||
config.desiredBackend = ResolveDesiredBackend(parsed_arg_options);
|
||||
config.logCallback = &aurora_log_callback;
|
||||
config.logLevel = startupLogLevel;
|
||||
config.mem1Size = 256 * 1024 * 1024;
|
||||
config.mem2Size = 24 * 1024 * 1024;
|
||||
config.allowJoystickBackgroundEvents = true;
|
||||
config.imGuiInitCallback = &aurora_imgui_init_callback;
|
||||
config.allowTextureReplacements = true;
|
||||
config.allowTextureDumps = false;
|
||||
|
||||
EnsureInitialPipelineCache(dusk::ConfigPath);
|
||||
PADSetDefaultMapping(&defaultPadMapping);
|
||||
|
||||
auroraInfo = aurora_initialize(argc, argv, &config);
|
||||
{
|
||||
const auto configPathString = dusk::ConfigPath.string();
|
||||
AuroraConfig config{};
|
||||
config.appName = dusk::AppName;
|
||||
config.configPath = configPathString.c_str();
|
||||
config.vsync = dusk::getSettings().video.enableVsync;
|
||||
config.startFullscreen = dusk::getSettings().video.enableFullscreen;
|
||||
config.windowPosX = -1;
|
||||
config.windowPosY = -1;
|
||||
config.windowWidth = defaultWindowWidth * 2;
|
||||
config.windowHeight = defaultWindowHeight * 2;
|
||||
config.desiredBackend = ResolveDesiredBackend(parsed_arg_options);
|
||||
config.logCallback = &aurora_log_callback;
|
||||
config.logLevel = startupLogLevel;
|
||||
config.mem1Size = 256 * 1024 * 1024;
|
||||
config.mem2Size = 24 * 1024 * 1024;
|
||||
config.allowJoystickBackgroundEvents = true;
|
||||
config.imGuiInitCallback = &aurora_imgui_init_callback;
|
||||
config.allowTextureReplacements = true;
|
||||
config.allowTextureDumps = false;
|
||||
auroraInfo = aurora_initialize(argc, argv, &config);
|
||||
}
|
||||
|
||||
#ifdef DUSK_DISCORD_RPC
|
||||
dusk::discord::Initialize();
|
||||
@@ -614,6 +619,8 @@ int game_main(int argc, char* argv[]) {
|
||||
|
||||
main01();
|
||||
|
||||
dusk::MoviePlayerShutdown();
|
||||
|
||||
dusk::ShutdownCrashReporting();
|
||||
dusk::ShutdownFileLogging();
|
||||
fflush(stdout);
|
||||
|
||||
Reference in New Issue
Block a user