diff --git a/files.cmake b/files.cmake index cb2200d59e..64cb21d225 100644 --- a/files.cmake +++ b/files.cmake @@ -1429,6 +1429,7 @@ set(DUSK_FILES src/dusk/globals.cpp src/dusk/gyro.cpp src/dusk/gamepad_color.cpp + src/dusk/autosave.cpp src/dusk/io.cpp src/dusk/layout.cpp src/dusk/logging.cpp diff --git a/include/dusk/autosave.h b/include/dusk/autosave.h new file mode 100644 index 0000000000..248924fcb8 --- /dev/null +++ b/include/dusk/autosave.h @@ -0,0 +1,17 @@ +#pragma once + +#ifndef AUTOSAVE_H +#define AUTOSAVE_H + +#include +#include + +void noAutoSave(); +void triggerAutoSave(); +void updateAutoSave(); +void enterAutoSave(); +void autoSaving(); +void waitingForWrite(); +void endAutoSave(); + +#endif \ No newline at end of file diff --git a/include/dusk/settings.h b/include/dusk/settings.h index e6fb0bd32b..e11647e8e4 100644 --- a/include/dusk/settings.h +++ b/include/dusk/settings.h @@ -81,6 +81,7 @@ struct UserSettings { ConfigVar instantSaves; ConfigVar instantText; ConfigVar sunsSong; + ConfigVar autoSave; // Preferences ConfigVar enableMirrorMode; diff --git a/src/d/actor/d_a_door_shutter.cpp b/src/d/actor/d_a_door_shutter.cpp index a481762132..ebd1c9a185 100644 --- a/src/d/actor/d_a_door_shutter.cpp +++ b/src/d/actor/d_a_door_shutter.cpp @@ -17,6 +17,11 @@ #include #include +#if TARGET_PC +#include +#include +#endif + char* daDoor20_c::getStopBmdName() { switch (door_param2_c::getKind(this)) { case 3: @@ -196,6 +201,7 @@ void daDoor20_c::setEventPrm() { } else { roomNo = FRoomNo; } + if (dComIfGp_roomControl_checkStatusFlag(roomNo, 1)) { if (door_param2_c::getKind(this) == 9) { if (daPy_py_c::checkNowWolf()) { @@ -564,6 +570,11 @@ int daDoor20_c::openEnd(int param_1) { openEnd_1(); break; } + + #if TARGET_PC + triggerAutoSave(); + #endif + return 1; } diff --git a/src/d/d_s_play.cpp b/src/d/d_s_play.cpp index 38308d0668..f0e2330a15 100644 --- a/src/d/d_s_play.cpp +++ b/src/d/d_s_play.cpp @@ -41,6 +41,7 @@ #if TARGET_PC #include "dusk/memory.h" +#include #endif #if DEBUG @@ -700,6 +701,10 @@ static u8 lbl_8074CAE4; static u32 l_sceneChangeStartTick; #endif +#if TARGET_PC +static BOOL autoSaved; +#endif + static int dScnPly_Execute(dScnPly_c* i_this) { #if DEBUG fapGm_HIO_c::startCpuTimer(); @@ -742,6 +747,15 @@ static int dScnPly_Execute(dScnPly_c* i_this) { } } + #if TARGET_PC + if (!dComIfGp_event_runCheck() && !fopOvlpM_IsPeek() && !dComIfG_resetToOpening(i_this) && + !dComIfGp_isEnableNextStage() && autoSaved == FALSE) + { + triggerAutoSave(); + autoSaved = TRUE; + } + #endif + dKy_itudemo_se(); #if DEBUG @@ -1593,6 +1607,11 @@ static int dScnPly_Create(scene_class* i_this) { dScnPly_c* a_this = (dScnPly_c*)i_this; int phase_state = dComLbG_PhaseHandler(&a_this->field_0x1c4, l_method, a_this); + + #if TARGET_PC + autoSaved = FALSE; + #endif + return phase_state; } diff --git a/src/d/d_save.cpp b/src/d/d_save.cpp index be95d1f190..8e4af75322 100644 --- a/src/d/d_save.cpp +++ b/src/d/d_save.cpp @@ -27,7 +27,11 @@ #include "lingcod/lingcod.h" #endif +#if TARGET_PC #include "dusk/settings.h" +#include +#include +#endif static u8 dSv_item_rename(u8 i_itemNo) { switch (i_itemNo) { @@ -345,6 +349,10 @@ void dSv_player_item_c::setItem(int i_slotNo, u8 i_itemNo) { dComIfGp_setSelectItem(i); } } + + #if TARGET_PC + triggerAutoSave(); + #endif } u8 dSv_player_item_c::getItem(int i_slotNo, bool i_checkCombo) const { diff --git a/src/dusk/autosave.cpp b/src/dusk/autosave.cpp new file mode 100644 index 0000000000..f9b76f4c67 --- /dev/null +++ b/src/dusk/autosave.cpp @@ -0,0 +1,88 @@ +#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) + { + mAutoSaveProc = 1; + } +} + +void updateAutoSave() { + (AutoSaveFuncsProc[mAutoSaveProc])(); +} + +void writeAutoSave() { + int stageNo = dStage_stagInfo_GetSaveTbl(dComIfGp_getStageStagInfo()); + + dComIfGs_putSave(stageNo); + 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.AddToast("Saving...", 2.0f); + mAutoSaveProc = 0; +} \ No newline at end of file diff --git a/src/dusk/imgui/ImGuiConsole.cpp b/src/dusk/imgui/ImGuiConsole.cpp index 7db2b863c2..321cb2e579 100644 --- a/src/dusk/imgui/ImGuiConsole.cpp +++ b/src/dusk/imgui/ImGuiConsole.cpp @@ -365,7 +365,7 @@ namespace dusk { } if (dusk::IsGameLaunched && !m_isLaunchInitialized) { - m_toasts.emplace_back(ImGui::GetIO().MouseSource == ImGuiMouseSource_TouchScreen ? + AddToast(ImGui::GetIO().MouseSource == ImGuiMouseSource_TouchScreen ? "Tap to toggle menu"s : "Press F1 to toggle menu"s, 4.f); diff --git a/src/dusk/imgui/ImGuiFirstRunPreset.cpp b/src/dusk/imgui/ImGuiFirstRunPreset.cpp index b728b8cd24..6b561cd877 100644 --- a/src/dusk/imgui/ImGuiFirstRunPreset.cpp +++ b/src/dusk/imgui/ImGuiFirstRunPreset.cpp @@ -44,6 +44,7 @@ static void ApplyPresetDusk() { s.game.enableFrameInterpolation.setValue(true); s.game.sunsSong.setValue(true); s.game.bloomMode.setValue(BloomMode::Dusk); + s.game.autoSave.setValue(true); } // ========================================================================= diff --git a/src/dusk/imgui/ImGuiMenuGame.cpp b/src/dusk/imgui/ImGuiMenuGame.cpp index 7a82288a86..4e046ccf51 100644 --- a/src/dusk/imgui/ImGuiMenuGame.cpp +++ b/src/dusk/imgui/ImGuiMenuGame.cpp @@ -121,6 +121,17 @@ namespace dusk { 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."); diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index 6198e789d1..49df869dd4 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -32,7 +32,7 @@ UserSettings g_userSettings = { .disableRupeeCutscenes {"game.disableRupeeCutscenes", false}, .noSwordRecoil {"game.noSwordRecoil", false}, .damageMultiplier {"game.damageMultiplier", 1}, - .noHeartDrops{"game.noHeartDrops", false}, + .noHeartDrops {"game.noHeartDrops", false}, .instantDeath {"game.instantDeath", false}, .fastClimbing {"game.fastClimbing", false}, .noMissClimbing {"game.noMissClimbing", false}, @@ -41,6 +41,7 @@ UserSettings g_userSettings = { .instantSaves {"game.instantSaves", false}, .instantText {"game.instantText", false}, .sunsSong {"game.sunsSong", false}, + .autoSave {"game.autoSave", false}, // Preferences .enableMirrorMode {"game.enableMirrorMode", false}, @@ -53,7 +54,7 @@ UserSettings g_userSettings = { .bloomMode {"game.bloomMode", BloomMode::Classic}, .bloomMultiplier {"game.bloomMultiplier", 1.0f}, .disableWaterRefraction {"game.disableWaterRefraction", false}, - .enableFrameInterpolation = {"game.enableFrameInterpolation", false}, + .enableFrameInterpolation {"game.enableFrameInterpolation", false}, .internalResolutionScale {"game.internalResolutionScale", 0}, .shadowResolutionMultiplier {"game.shadowResolutionMultiplier", 1}, .enableDepthOfField {"game.enableDepthOfField", true}, @@ -154,6 +155,7 @@ void registerSettings() { Register(g_userSettings.game.instantSaves); Register(g_userSettings.game.instantText); Register(g_userSettings.game.sunsSong); + Register(g_userSettings.game.autoSave); Register(g_userSettings.game.enableMirrorMode); Register(g_userSettings.game.invertCameraXAxis); Register(g_userSettings.game.invertCameraYAxis); diff --git a/src/f_ap/f_ap_game.cpp b/src/f_ap/f_ap_game.cpp index beec5847ef..12d724bf69 100644 --- a/src/f_ap/f_ap_game.cpp +++ b/src/f_ap/f_ap_game.cpp @@ -23,8 +23,12 @@ #include "f_op/f_op_scene_mng.h" #include "m_Do/m_Do_graphic.h" #include "m_Do/m_Do_main.h" + +#if TARGET_PC #include "tracy/Tracy.hpp" #include +#include +#endif fapGm_HIO_c::fapGm_HIO_c() { mUsingHostIO = true; @@ -737,7 +741,8 @@ static void fapGm_AfterRecord() { static void duskExecute() { handleGamepadColor(); - + updateAutoSave(); + if (mDoCPd_c::getHoldR(PAD_1) && mDoCPd_c::getTrigX(PAD_1)) { if (const auto link = g_dComIfG_gameInfo.play.getPlayer(0)) { dynamic_cast(link)->handleWolfHowl(); @@ -815,6 +820,7 @@ void fapGm_Execute() { #else fpcM_ManagementFunc(NULL, fapGm_After); #endif + cCt_Counter(0); #ifdef TARGET_PC dusk::speedrun::onGameFrame();