diff --git a/CMakeLists.txt b/CMakeLists.txt index a90608a0a6..d12b95b68e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -446,6 +446,12 @@ target_link_libraries(dusk PRIVATE aurora::main ${GAME_LIBS} ${JSYSTEM_LINK_LIBR target_precompile_headers(dusk PRIVATE "$<$:${CMAKE_SOURCE_DIR}/include/dusk_pch.hpp>") if (TARGET crashpad_handler) add_dependencies(dusk crashpad_handler) + add_custom_command(TARGET dusk POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + "$" + "$" + COMMENT "Copying crashpad handler" + ) endif () if (ANDROID) diff --git a/extern/aurora b/extern/aurora index 449e7bf9b9..211bfb00a8 160000 --- a/extern/aurora +++ b/extern/aurora @@ -1 +1 @@ -Subproject commit 449e7bf9b934a61b7c5736d8b87310a1f92829f0 +Subproject commit 211bfb00a85b551c436aed2359cb6233c05b4cee diff --git a/files.cmake b/files.cmake index bb55ad7e0c..07abed9a0f 100644 --- a/files.cmake +++ b/files.cmake @@ -1452,6 +1452,7 @@ set(DUSK_FILES src/dusk/imgui/ImGuiMenuTools.hpp src/dusk/imgui/ImGuiMenuRandomizer.cpp src/dusk/imgui/ImGuiMenuRandomizer.hpp + src/dusk/imgui/ImGuiActorSpawner.cpp src/dusk/imgui/ImGuiProcessOverlay.cpp src/dusk/imgui/ImGuiCameraOverlay.cpp src/dusk/imgui/ImGuiHeapOverlay.cpp @@ -1496,6 +1497,8 @@ set(DUSK_FILES src/dusk/ui/prelaunch.hpp src/dusk/ui/preset.cpp src/dusk/ui/preset.hpp + src/dusk/ui/reporting.cpp + src/dusk/ui/reporting.hpp src/dusk/ui/select_button.cpp src/dusk/ui/select_button.hpp src/dusk/ui/settings.cpp diff --git a/include/dusk/crash_reporting.h b/include/dusk/crash_reporting.h index dfc5bde817..c42079a6f6 100644 --- a/include/dusk/crash_reporting.h +++ b/include/dusk/crash_reporting.h @@ -1,8 +1,17 @@ #pragma once -namespace dusk { +namespace dusk::crash_reporting { -void InitializeCrashReporting(); -void ShutdownCrashReporting(); +enum class Consent { + Unavailable, + Unknown, + Given, + Revoked, +}; -} // namespace dusk +void initialize(); +void shutdown(); +Consent get_consent(); +void set_consent(bool enabled); + +} // namespace dusk::crash_reporting diff --git a/include/dusk/settings.h b/include/dusk/settings.h index aa93b53c96..be1a9d9637 100644 --- a/include/dusk/settings.h +++ b/include/dusk/settings.h @@ -144,6 +144,8 @@ struct UserSettings { ConfigVar freeCamera; ConfigVar invertCameraXAxis; ConfigVar invertCameraYAxis; + ConfigVar invertFirstPersonXAxis; + ConfigVar invertFirstPersonYAxis; ConfigVar freeCameraSensitivity; ConfigVar debugFlyCam; ConfigVar debugFlyCamLockEvents; @@ -187,7 +189,6 @@ struct UserSettings { ConfigVar skipPreLaunchUI; ConfigVar showPipelineCompilation; ConfigVar wasPresetChosen; - ConfigVar enableCrashReporting; ConfigVar checkForUpdates; ConfigVar cardFileType; ConfigVar enableAdvancedSettings; diff --git a/src/d/actor/d_a_alink_link.inc b/src/d/actor/d_a_alink_link.inc index b278d866fc..abbda0c5e2 100644 --- a/src/d/actor/d_a_alink_link.inc +++ b/src/d/actor/d_a_alink_link.inc @@ -119,8 +119,8 @@ BOOL daAlink_c::setBodyAngleToCamera() { var_f31 /= dComIfGp_getCameraZoomScale(field_0x317c); } - shape_angle.y = shape_angle.y + (var_f31 * cM_ssin(mStickAngle)); - sp8 = mBodyAngle.x + (var_f31 * cM_scos(mStickAngle)); + shape_angle.y = shape_angle.y + (var_f31 * cM_ssin(mStickAngle) IF_DUSK(* (dusk::getSettings().game.invertFirstPersonXAxis ? -1.0f : 1.0f))); + sp8 = mBodyAngle.x + (var_f31 * cM_scos(mStickAngle) IF_DUSK(* (dusk::getSettings().game.invertFirstPersonYAxis ? -1.0f : 1.0f))); if (checkNotItemSinkLimit() && sp8 > 0 && sp8 > mBodyAngle.x) { sp8 = mBodyAngle.x; @@ -144,8 +144,8 @@ BOOL daAlink_c::setBodyAngleToCamera() { f32 gy_pitch = 0.f; dusk::gyro::getAimDeltas(gy_yaw, gy_pitch); - shape_angle.y = shape_angle.y + cM_rad2s(gy_yaw * gyro_scale); - sp8 = sp8 + cM_rad2s(gy_pitch * gyro_scale); + shape_angle.y = shape_angle.y + cM_rad2s(gy_yaw * gyro_scale * (dusk::getSettings().game.invertFirstPersonXAxis ? -1.0f : 1.0f)); + sp8 = sp8 + cM_rad2s(gy_pitch * gyro_scale * (dusk::getSettings().game.invertFirstPersonYAxis ? -1.0f : 1.0f)); if (checkNotItemSinkLimit() && sp8 > 0 && sp8 > mBodyAngle.x) { sp8 = mBodyAngle.x; diff --git a/src/d/d_camera.cpp b/src/d/d_camera.cpp index 0adf6d4bd6..266504d98c 100644 --- a/src/d/d_camera.cpp +++ b/src/d/d_camera.cpp @@ -7648,7 +7648,10 @@ bool dCamera_c::freeCamera() { cXyz camMovement = {mPadInfo.mCStick.mLastPosX, mPadInfo.mCStick.mLastPosY, 0.0f}; f32 magnitude = sqrt(mPadInfo.mCStick.mLastPosX * mPadInfo.mCStick.mLastPosX + mPadInfo.mCStick.mLastPosY * mPadInfo.mCStick.mLastPosY); - if (mPadInfo.mCStick.mLastPosX != 0 || mPadInfo.mCStick.mLastPosY != 0) { + // If we aren't in manual cam mode, don't trigger it if the player tries to hit C-up + // for first person + if (mPadInfo.mCStick.mLastPosX != 0 || mPadInfo.mCStick.mLastPosY < 0 || + (mCamParam.mManualMode == 1 && mPadInfo.mCStick.mLastPosY != 0)) { mCamParam.mManualMode = 1; camMovement = camMovement.normalize(); camMovement.y *= dusk::getSettings().game.invertCameraYAxis ? 1.0f : -1.0f; diff --git a/src/dusk/crash_reporting.cpp b/src/dusk/crash_reporting.cpp index 657e5ef5d2..5c75388361 100644 --- a/src/dusk/crash_reporting.cpp +++ b/src/dusk/crash_reporting.cpp @@ -4,7 +4,6 @@ #include "dusk/dusk.h" #include "dusk/logging.h" #include "dusk/main.h" -#include "dusk/settings.h" #include "version.h" #include @@ -13,114 +12,83 @@ #include #include -#include "SDL3/SDL_filesystem.h" - #if DUSK_ENABLE_SENTRY_NATIVE #include #endif -namespace dusk { +namespace dusk::crash_reporting { namespace { #if DUSK_ENABLE_SENTRY_NATIVE bool g_sentryInitialized = false; -bool IsTruthy(std::string_view value) { - return value == "1" || value == "true" || value == "TRUE" || value == "yes" - || value == "YES" || value == "on" || value == "ON"; +bool truthy(std::string_view value) { + return value == "1" || value == "true" || value == "TRUE" || value == "yes" || value == "YES" || + value == "on" || value == "ON"; } -std::string GetEnvOrEmpty(const char* name) { +std::string env_or_empty(const char* name) { if (const char* value = std::getenv(name)) { return value; } return {}; } -bool GetEffectiveEnabled() { - const std::string env = GetEnvOrEmpty("DUSK_SENTRY_ENABLED"); - if (!env.empty()) { - return IsTruthy(env); - } - return getSettings().backend.enableCrashReporting; +bool disabled_by_env() { + const std::string env = env_or_empty("DUSK_SENTRY_ENABLED"); + return !env.empty() && !truthy(env); } -std::string GetEffectiveDsn() { - const std::string env = GetEnvOrEmpty("DUSK_SENTRY_DSN"); +std::string effective_dsn() { + const std::string env = env_or_empty("DUSK_SENTRY_DSN"); if (!env.empty()) { return env; } return DUSK_SENTRY_DSN; } -bool GetEffectiveDebug() { - const std::string env = GetEnvOrEmpty("DUSK_SENTRY_DEBUG"); +bool effective_debug() { + const std::string env = env_or_empty("DUSK_SENTRY_DEBUG"); if (!env.empty()) { - return IsTruthy(env); + return truthy(env); } return false; } -std::string GetReleaseName() { +std::string release_name() { return std::string(AppName) + "@" DUSK_WC_DESCRIBE; } -std::filesystem::path GetSentryDatabasePath() { +std::filesystem::path sentry_database_path() { return dusk::ConfigPath / "sentry"; } -std::filesystem::path GetLogAttachmentPath() { +std::filesystem::path log_attachment_path() { if (const char* logPath = GetLogFilePath()) { return logPath; } return {}; } -std::filesystem::path GetCrashpadHandlerPath() { - const char* basePath = SDL_GetBasePath(); - if (!basePath) { - return {}; - } - - const std::filesystem::path handlerDir(basePath); -#if _WIN32 - return handlerDir / "crashpad_handler.exe"; -#else - return handlerDir / "crashpad_handler"; -#endif -} - -void ConfigurePathOptions(sentry_options_t* options) { - const auto databasePath = GetSentryDatabasePath(); +void configure_path_options(sentry_options_t* options) { + const auto databasePath = sentry_database_path(); std::error_code ec; std::filesystem::create_directories(databasePath, ec); if (ec) { - DuskLog.warn("Unable to create Sentry database path '{}': {}", - databasePath.string(), ec.message()); + DuskLog.warn( + "Unable to create Sentry database path '{}': {}", databasePath.string(), ec.message()); } #if _WIN32 const std::wstring databasePathWide = databasePath.wstring(); sentry_options_set_database_pathw(options, databasePathWide.c_str()); - - const auto handlerPath = GetCrashpadHandlerPath(); - if (!handlerPath.empty()) { - const std::wstring handlerPathWide = handlerPath.wstring(); - sentry_options_set_handler_pathw(options, handlerPathWide.c_str()); - } #else const std::string databasePathUtf8 = databasePath.string(); sentry_options_set_database_path(options, databasePathUtf8.c_str()); - - const auto handlerPath = GetCrashpadHandlerPath(); - if (!handlerPath.empty()) { - const std::string handlerPathUtf8 = handlerPath.string(); - sentry_options_set_handler_path(options, handlerPathUtf8.c_str()); - } #endif - const auto logPath = GetLogAttachmentPath(); + const auto logPath = log_attachment_path(); if (!logPath.empty()) { #if _WIN32 sentry_options_add_attachmentw(options, logPath.wstring().c_str()); @@ -133,32 +101,29 @@ void ConfigurePathOptions(sentry_options_t* options) { } // namespace -void InitializeCrashReporting() { +void initialize() { #if DUSK_ENABLE_SENTRY_NATIVE - if (g_sentryInitialized) { + if (g_sentryInitialized || disabled_by_env()) { return; } - if (!GetEffectiveEnabled()) { - return; - } - - const std::string dsn = GetEffectiveDsn(); + const std::string dsn = effective_dsn(); if (dsn.empty()) { DuskLog.warn("Crash reporting is enabled but no Sentry DSN is configured"); return; } - const std::string release = GetReleaseName(); + const std::string release = release_name(); sentry_options_t* options = sentry_options_new(); sentry_options_set_dsn(options, dsn.c_str()); sentry_options_set_release(options, release.c_str()); sentry_options_set_environment(options, DUSK_SENTRY_ENVIRONMENT); - sentry_options_set_debug(options, GetEffectiveDebug() ? 1 : 0); + sentry_options_set_debug(options, effective_debug() ? 1 : 0); + sentry_options_set_require_user_consent(options, 1); sentry_options_set_cache_keep(options, 1); sentry_options_set_max_breadcrumbs(options, 100); - ConfigurePathOptions(options); + configure_path_options(options); if (sentry_init(options) != 0) { DuskLog.warn("Failed to initialize Sentry crash reporting"); @@ -173,7 +138,7 @@ void InitializeCrashReporting() { #endif } -void ShutdownCrashReporting() { +void shutdown() { #if DUSK_ENABLE_SENTRY_NATIVE if (!g_sentryInitialized) { return; @@ -184,4 +149,40 @@ void ShutdownCrashReporting() { #endif } -} // namespace dusk +Consent get_consent() { +#if DUSK_ENABLE_SENTRY_NATIVE + if (!g_sentryInitialized) { + return Consent::Unavailable; + } + + switch (sentry_user_consent_get()) { + case SENTRY_USER_CONSENT_GIVEN: + return Consent::Given; + case SENTRY_USER_CONSENT_REVOKED: + return Consent::Revoked; + case SENTRY_USER_CONSENT_UNKNOWN: + default: + return Consent::Unknown; + } +#else + return Consent::Unavailable; +#endif +} + +void set_consent(bool enabled) { +#if DUSK_ENABLE_SENTRY_NATIVE + if (!g_sentryInitialized) { + return; + } + + if (enabled) { + sentry_user_consent_give(); + } else { + sentry_user_consent_revoke(); + } +#else + (void)enabled; +#endif +} + +} // namespace dusk::crash_reporting diff --git a/src/dusk/imgui/ImGuiActorSpawner.cpp b/src/dusk/imgui/ImGuiActorSpawner.cpp new file mode 100644 index 0000000000..f3aefd4c41 --- /dev/null +++ b/src/dusk/imgui/ImGuiActorSpawner.cpp @@ -0,0 +1,142 @@ +#include "imgui.h" + +#include "ImGuiMenuTools.hpp" +#include "d/actor/d_a_alink.h" +#include "d/d_com_inf_game.h" +#include "f_op/f_op_actor_mng.h" +#include "SSystem/SComponent/c_sxyz.h" +#include "SSystem/SComponent/c_xyz.h" + +namespace dusk { +namespace { + +struct ActorSpawnerState { + int actorId = 0; + int params = -1; + int argument = -1; + int angleX = 0; + int angleY = 0; + int angleZ = 0; + float scaleX = 1.0f; + float scaleY = 1.0f; + float scaleZ = 1.0f; + bool usePlayerRoom = true; + int manualRoom = 0; + int spawnCount = 1; + bool hasResult = false; + unsigned int lastResult = 0; + int lastAttempted = 0; +}; + +ActorSpawnerState s_state; + +} // namespace + +void ImGuiMenuTools::ShowActorSpawner() { + if (!m_showActorSpawner) { + return; + } + + if (!ImGui::Begin("Actor Spawner", &m_showActorSpawner)) { + ImGui::End(); + return; + } + + daAlink_c* player = (daAlink_c*)dComIfGp_getPlayer(0); + + ImGui::SeparatorText("Actor"); + ImGui::InputInt("Actor ID", &s_state.actorId); + ImGui::InputInt("Params (hex)", &s_state.params, 0, 0, ImGuiInputTextFlags_CharsHexadecimal); + ImGui::InputInt("Argument", &s_state.argument); + s_state.argument = (s_state.argument < -128) ? -128 : (s_state.argument > 127) ? 127 : s_state.argument; + + ImGui::SeparatorText("Angle"); + ImGui::InputInt("Angle X", &s_state.angleX); + ImGui::InputInt("Angle Y", &s_state.angleY); + ImGui::InputInt("Angle Z", &s_state.angleZ); + + ImGui::SeparatorText("Scale"); + ImGui::InputFloat("Scale X", &s_state.scaleX, 0.1f, 1.0f); + ImGui::InputFloat("Scale Y", &s_state.scaleY, 0.1f, 1.0f); + ImGui::InputFloat("Scale Z", &s_state.scaleZ, 0.1f, 1.0f); + + ImGui::SeparatorText("Spawn"); + ImGui::InputInt("Count", &s_state.spawnCount); + if (s_state.spawnCount < 1) { + s_state.spawnCount = 1; + } + + ImGui::SeparatorText("Position"); + ImGui::Checkbox("Use player room", &s_state.usePlayerRoom); + if (!s_state.usePlayerRoom) { + ImGui::InputInt("Room No", &s_state.manualRoom); + } + + if (player != nullptr) { + ImGui::Text("Spawn pos: %.2f, %.2f, %.2f", + player->current.pos.x, player->current.pos.y, player->current.pos.z); + } else { + ImGui::TextDisabled("Player not available"); + } + + ImGui::Separator(); + + bool canSpawn = player != nullptr; + if (!canSpawn) { + ImGui::BeginDisabled(); + } + + if (ImGui::Button("Spawn", ImVec2(-1, 0))) { + cXyz pos = player->current.pos; + csXyz angle; + angle.set((s16)s_state.angleX, (s16)s_state.angleY, (s16)s_state.angleZ); + cXyz scale(s_state.scaleX, s_state.scaleY, s_state.scaleZ); + int roomNo = s_state.usePlayerRoom ? player->current.roomNo : s_state.manualRoom; + + layer_class* savedLayer = fpcLy_CurrentLayer(); + base_process_class* playScene = fpcM_SearchByName(fpcNm_PLAY_SCENE_e); + if (playScene != nullptr) { + fpcLy_SetCurrentLayer(&((process_node_class*)playScene)->layer); + } + + s_state.lastResult = 0; + s_state.lastAttempted = s_state.spawnCount; + for (int i = 0; i < s_state.spawnCount; ++i) { + unsigned int result = fopAcM_create( + (s16)s_state.actorId, + (u32)s_state.params, + &pos, + roomNo, + &angle, + &scale, + (s8)s_state.argument + ); + if (result != 0) { + s_state.lastResult = result; + } + } + s_state.hasResult = true; + + fpcLy_SetCurrentLayer(savedLayer); + } + + if (!canSpawn) { + ImGui::EndDisabled(); + } + + if (s_state.hasResult) { + if (s_state.lastResult != 0) { + if (s_state.lastAttempted == 1) { + ImGui::Text("Spawned: proc ID %u", s_state.lastResult); + } else { + ImGui::Text("Spawned %d (last proc ID %u)", s_state.lastAttempted, s_state.lastResult); + } + } else { + ImGui::TextColored(ImVec4(1, 0.4f, 0.4f, 1), "Spawn failed (returned 0)"); + } + } + + ImGui::End(); +} + +} // namespace dusk diff --git a/src/dusk/imgui/ImGuiConsole.cpp b/src/dusk/imgui/ImGuiConsole.cpp index 6aa61505cf..75aec33c33 100644 --- a/src/dusk/imgui/ImGuiConsole.cpp +++ b/src/dusk/imgui/ImGuiConsole.cpp @@ -376,6 +376,7 @@ namespace dusk { m_menuTools.ShowAudioDebug(); m_menuTools.ShowSaveEditor(); m_menuTools.ShowStateShare(); + m_menuTools.ShowActorSpawner(); } m_menuRandomizer.windowRandoStats(); m_menuRandomizer.windowRandoGeneration(); diff --git a/src/dusk/imgui/ImGuiMenuTools.cpp b/src/dusk/imgui/ImGuiMenuTools.cpp index 306298c28d..1cb0ec474c 100644 --- a/src/dusk/imgui/ImGuiMenuTools.cpp +++ b/src/dusk/imgui/ImGuiMenuTools.cpp @@ -107,6 +107,7 @@ namespace dusk { ImGui::MenuItem("Audio Debug", hotkeys::SHOW_AUDIO_DEBUG, &m_showAudioDebug); ImGui::MenuItem("Bloom", nullptr, &m_showBloomWindow); ImGui::MenuItem("Stub Log", nullptr, &m_showStubLog); + ImGui::MenuItem("Actor Spawner", nullptr, &m_showActorSpawner); if (!dusk::IsGameLaunched) { ImGui::EndDisabled(); diff --git a/src/dusk/imgui/ImGuiMenuTools.hpp b/src/dusk/imgui/ImGuiMenuTools.hpp index 6b925b7bf3..1c17ddbbe9 100644 --- a/src/dusk/imgui/ImGuiMenuTools.hpp +++ b/src/dusk/imgui/ImGuiMenuTools.hpp @@ -28,6 +28,7 @@ namespace dusk { void ShowSaveEditor(); void ShowStateShare(); void ShowInputViewer(); + void ShowActorSpawner(); private: bool m_showDebugOverlay = false; @@ -70,6 +71,7 @@ namespace dusk { bool m_showInputViewer = false; bool m_showInputViewerGyro = false; + bool m_showActorSpawner = false; int m_inputOverlayCorner = 3; std::string m_controllerName; }; diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index 0ae68f45ba..1ce679c6c3 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -81,6 +81,8 @@ UserSettings g_userSettings = { .freeCamera {"game.freeCamera", false}, .invertCameraXAxis {"game.invertCameraXAxis", false}, .invertCameraYAxis {"game.invertCameraYAxis", false}, + .invertFirstPersonXAxis {"game.invertFirstPersonXAxis", false}, + .invertFirstPersonYAxis {"game.invertFirstPersonYAxis", false}, .freeCameraSensitivity {"game.freeCameraSensitivity", 1.0f}, .debugFlyCam {"game.debugFlyCam", false}, .debugFlyCamLockEvents {"game.debugFlyCamLockEvents", true}, @@ -122,7 +124,6 @@ UserSettings g_userSettings = { .skipPreLaunchUI {"backend.skipPreLaunchUI", false}, .showPipelineCompilation {"backend.showPipelineCompilation", false}, .wasPresetChosen {"backend.wasPresetChosen", false}, - .enableCrashReporting {"backend.enableCrashReporting", true}, .checkForUpdates {"backend.checkForUpdates", true}, .cardFileType {"backend.cardFileType", static_cast(CARD_GCIFOLDER)}, .enableAdvancedSettings {"backend.enableAdvancedSettings", false}, @@ -172,6 +173,8 @@ void registerSettings() { Register(g_userSettings.game.enableMirrorMode); Register(g_userSettings.game.invertCameraXAxis); Register(g_userSettings.game.invertCameraYAxis); + Register(g_userSettings.game.invertFirstPersonXAxis); + Register(g_userSettings.game.invertFirstPersonYAxis); Register(g_userSettings.game.freeCameraSensitivity); Register(g_userSettings.game.minimalHUD); Register(g_userSettings.game.pauseOnFocusLost); @@ -231,7 +234,6 @@ void registerSettings() { Register(g_userSettings.backend.skipPreLaunchUI); Register(g_userSettings.backend.showPipelineCompilation); Register(g_userSettings.backend.wasPresetChosen); - Register(g_userSettings.backend.enableCrashReporting); Register(g_userSettings.backend.checkForUpdates); Register(g_userSettings.backend.cardFileType); Register(g_userSettings.backend.enableAdvancedSettings); diff --git a/src/dusk/ui/controller_config.cpp b/src/dusk/ui/controller_config.cpp index 52c6f6c702..88baa7b29c 100644 --- a/src/dusk/ui/controller_config.cpp +++ b/src/dusk/ui/controller_config.cpp @@ -208,7 +208,15 @@ bool input_neutral(int port) { bool keyboard_escape_pressed() { int keyCount = 0; const bool* keys = SDL_GetKeyboardState(&keyCount); - return keys != nullptr && SDL_SCANCODE_ESCAPE < keyCount && keys[SDL_SCANCODE_ESCAPE]; + if (keys == nullptr || SDL_SCANCODE_ESCAPE >= keyCount || !keys[SDL_SCANCODE_ESCAPE]) { + return false; + } + for (int i = 0; i < keyCount; ++i) { + if (i != SDL_SCANCODE_ESCAPE && keys[i]) { + return false; + } + } + return true; } Rml::String keyboard_key_name(s32 scancode) { diff --git a/src/dusk/ui/preset.cpp b/src/dusk/ui/preset.cpp index 1d559972f5..9d8e4e957e 100644 --- a/src/dusk/ui/preset.cpp +++ b/src/dusk/ui/preset.cpp @@ -33,6 +33,7 @@ void applyPresetDusk() { s.game.fastTears.setValue(true); s.game.biggerWallets.setValue(true); s.game.invertCameraXAxis.setValue(true); + s.game.invertFirstPersonYAxis.setValue(true); s.game.no2ndFishForCat.setValue(true); s.game.enableAchievementToasts.setValue(true); s.game.enableControllerToasts.setValue(true); diff --git a/src/dusk/ui/reporting.cpp b/src/dusk/ui/reporting.cpp new file mode 100644 index 0000000000..e32a9cbc20 --- /dev/null +++ b/src/dusk/ui/reporting.cpp @@ -0,0 +1,113 @@ +#if DUSK_ENABLE_SENTRY_NATIVE + +#include "reporting.hpp" + +#include "button.hpp" +#include "dusk/crash_reporting.h" +#include "ui.hpp" + +#include + +namespace dusk::ui { + +CrashReportWindow::CrashReportWindow() : WindowSmall("modal", "modal-dialog") { + mDialog->SetClass("modal-dialog", true); + + auto* header = append(mDialog, "div"); + header->SetClass("modal-header", true); + + auto* title = append(header, "div"); + title->SetClass("modal-title", true); + title->SetInnerRML("Send Crash Reports"); + + auto* headIcon = append(header, "icon"); + headIcon->SetClass("question-mark", true); + + auto* intro = append(mDialog, "div"); + intro->SetClass("modal-body", true); + intro->SetInnerRML( + "Dusk can automatically send crash reports to the developers. Crash reports contain the " + "following:" + "
• Operating system version
• CPU architecture
• GPU model & driver version" + "
• File paths (may include account username)
• Stack trace

" + "This can be changed in the Settings menu at any time."); + + auto* grid = append(mDialog, "div"); + grid->SetClass("preset-grid", true); + + struct OptionInfo { + const char* name; + const char* desc; + void (*apply)(); + }; + + static constexpr OptionInfo kOptions[] = { + {"Enable", + "Send crash reports to Dusk developers. Reports will include the information described " + "above.", + [] { crash_reporting::set_consent(true); }}, + {"Disable", + "Do not send crash reports. This may make it more difficult to resolve issues you " + "encounter.", + [] { crash_reporting::set_consent(false); }}, + }; + + for (const auto& option : kOptions) { + auto* col = append(grid, "div"); + col->SetClass("preset-col", true); + + auto btn = std::make_unique