Compare commits

...

26 Commits

Author SHA1 Message Date
SuperDude88 3eeb9a19a1 Fix for Mobile Testing 2026-07-07 19:04:14 -04:00
SuperDude88 060f0eb419 Use OSGetSystemTime Extension
- Fixes desyncing issues with save file time and a couple other odd instances, particularly on mobile platforms that suspend apps
2026-07-07 18:52:16 -04:00
SuperDude88 e491d254f8 Don't Use OSCalendarTime for Speedrun Timing
- Shouldn't rely on OSTicksToCalendarTime since it will be changed to handle time zone conversion, and that doesn't make sense on elapsed time
2026-07-07 18:12:15 -04:00
TakaRikka 84c5729c4e Merge pull request #2175 from TwilitRealm/mirror-mode-fixes
Mirror Mode Map Fixes
2026-07-07 04:00:07 -07:00
Luke Street e0ab36b7d9 Stable game ABI: Use PARTIAL_DEBUG
To make sure that mods can work across debug and release build, we can utilize PARTIAL_DEBUG to enable only DEBUG changes that affect struct and vtable layout
2026-07-07 01:14:29 -06:00
Luke Street d40a30ee13 Config: renaming & add change subscriptions 2026-07-07 00:50:11 -06:00
SuperDude88 074b43a089 Merge pull request #2173 from Lypopp/Invincible-enemies-activated-with-speedrun-mode-activated
Fixed invincible enemies cheat not deactivating on game startup when speedrun mode is activated
2026-07-06 19:48:53 -04:00
SuperDude88 8e8dc305e6 Update Pane Mirroring
- Update mirroring of Link + the dungeon map icon properly when the setting changes
2026-07-05 23:33:15 -04:00
SuperDude88 41389f4a8e Remove Extra Mirror
Accidentally left this in from testing
2026-07-05 22:58:45 -04:00
SuperDude88 8145dec2a1 Remove Outdated MIrror Mode TODO
This was done!
2026-07-05 20:19:42 -04:00
SuperDude88 4089a80a17 Mirror Mode Map Fixes
Will probably need to resolve conflicts with Wii code once decomp has related PR merged + we sync things

Change to setPlusZoomCenterX is not present on Wii (but fixes controls), probably from its lack of CStick
2026-07-05 20:19:33 -04:00
hector.bonhoure@gmail.com b772b6d952 Fixed invincible enemies cheat not deactivating on game startup when speedrun mode is activated 2026-07-05 16:05:55 +02:00
Irastris 42910ab2fd Restore simultaneous gyro and joystick input for first person aiming (#2164) 2026-07-02 00:13:22 -06:00
Olivia!! f54892b2c2 do not disable regular touch input if menu touch is disabled (#2152) 2026-07-02 00:03:53 -06:00
Luke Street f32e069c4b Improve mouse hiding logic (#2163)
* Improve mouse hiding logic

* Restore ImGui logic
2026-07-02 02:03:18 -04:00
Luke Street 09f087656a Guard against null dMsgObject_getMsgObjectClass() in a few places 2026-06-30 15:19:30 -06:00
Irastris fe15366912 Make Ganondorf cape tearing deterministic when using texture replacements (#2145)
* Make Ganondorf cape tearing deterministic when using texture replacements

* GXIsTexObjReplaced -> has_replacement

* Only check for a replacement once during creation

* Update Aurora
2026-06-28 22:44:26 -06:00
jdflyer cfadf7607a Various Mirror Mode Fixes (#2149)
* Various Mirror Mode Fixes

* Avoid mutating mArrowPos2DX

---------

Co-authored-by: Luke Street <luke@street.dev>
2026-06-28 22:42:52 -06:00
Luke Street f81d25b425 Add "Touch Targeting" option 2026-06-27 21:20:14 -06:00
qubitnano ebf6f31719 flake.nix: patchelf libvulkan (#2124)
Since aurora 7ccdae18077caa67acb0f61a525aa5a423cc3b2c, dusklight can't
find libvulkan.so on nix
2026-06-27 21:15:57 -06:00
Luke Street 590d209f76 Update README.md 2026-06-27 21:09:04 -06:00
Luke Street ed21cd4fd0 Update README.md 2026-06-27 20:35:17 -06:00
Luke Street 277538bb81 New pipeline progress UI 2026-06-24 22:03:58 -07:00
Luke Street 5418b1831d Update aurora & flake.nix versions 2026-06-17 23:12:49 -06:00
Luke Street 2d4e69466b Refine menu_pointer click events
Only short clicks/taps count & they must
not move between targets
2026-06-17 22:48:44 -06:00
Luke Street 427dcfab82 Show active Graphics Backend in Settings; not configured 2026-06-17 18:09:00 -06:00
81 changed files with 1596 additions and 594 deletions
+5 -4
View File
@@ -363,7 +363,8 @@ set(DUSK_COPYRIGHT "Copyright (C) Twilit Realm contributors")
source_group("dolzel" FILES ${DOLZEL_FILES} ${Z2AUDIOLIB_FILES} ${REL_FILES}) source_group("dolzel" FILES ${DOLZEL_FILES} ${Z2AUDIOLIB_FILES} ${REL_FILES})
source_group("dusklight" FILES ${DUSK_FILES} ${DUSK_HTTP_BACKEND_FILES}) source_group("dusklight" FILES ${DUSK_FILES} ${DUSK_HTTP_BACKEND_FILES})
set(GAME_COMPILE_DEFS TARGET_PC WIDESCREEN_SUPPORT=1 AVOID_UB=1 VERSION=0 MTX_USE_PS=1) # PARTIAL_DEBUG makes debug and release share one struct/vtable ABI so a mod binary loads into either
set(GAME_COMPILE_DEFS TARGET_PC WIDESCREEN_SUPPORT=1 AVOID_UB=1 VERSION=0 MTX_USE_PS=1 PARTIAL_DEBUG=1)
set(GAME_INCLUDE_DIRS set(GAME_INCLUDE_DIRS
include include
@@ -474,7 +475,7 @@ set(GAME_DEBUG_FILES
set_source_files_properties( set_source_files_properties(
${GAME_DEBUG_FILES} ${GAME_DEBUG_FILES}
PROPERTIES PROPERTIES
COMPILE_DEFINITIONS "$<$<CONFIG:Debug>:DEBUG=1>;$<$<CONFIG:Debug>:PARTIAL_DEBUG=1>" COMPILE_DEFINITIONS "$<$<CONFIG:Debug>:DEBUG=1>;PARTIAL_DEBUG=1"
) )
# game_base is for all other game code files # game_base is for all other game code files
@@ -488,14 +489,14 @@ set(GAME_BASE_FILES
set_source_files_properties( set_source_files_properties(
${GAME_BASE_FILES} ${GAME_BASE_FILES}
PROPERTIES PROPERTIES
COMPILE_DEFINITIONS "NDEBUG=1;NDEBUG_DEFINED=1;DEBUG_DEFINED=0;$<$<CONFIG:Debug>:PARTIAL_DEBUG=1>" COMPILE_DEFINITIONS "NDEBUG=1;NDEBUG_DEFINED=1;DEBUG_DEFINED=0;PARTIAL_DEBUG=1"
) )
foreach(jsystem_lib IN LISTS JSYSTEM_LIBRARIES) foreach(jsystem_lib IN LISTS JSYSTEM_LIBRARIES)
target_compile_definitions(${jsystem_lib} PRIVATE target_compile_definitions(${jsystem_lib} PRIVATE
${GAME_COMPILE_DEFS} ${GAME_COMPILE_DEFS}
$<$<CONFIG:Debug>:DEBUG=1> $<$<CONFIG:Debug>:DEBUG=1>
$<$<CONFIG:Debug>:PARTIAL_DEBUG=1> PARTIAL_DEBUG=1
) )
target_include_directories(${jsystem_lib} PRIVATE ${GAME_INCLUDE_DIRS}) target_include_directories(${jsystem_lib} PRIVATE ${GAME_INCLUDE_DIRS})
target_link_libraries(${jsystem_lib} PRIVATE ${GAME_LIBS}) target_link_libraries(${jsystem_lib} PRIVATE ${GAME_LIBS})
+4 -18
View File
@@ -20,31 +20,17 @@ It aims to be as accurate as possible to the original while also providing new o
> Dusklight does *not* provide any copyrighted assets. You must provide your own copy of the original game. > Dusklight does *not* provide any copyrighted assets. You must provide your own copy of the original game.
> [!IMPORTANT] > [!IMPORTANT]
> At a minimum, Dusklight requires a GPU with support for either D3D12, Vulkan, or Metal. Your experience with specific hardware, operating systems, and drivers may vary. In particular, older Intel iGPUs have a high likelihood of incompatibility. We are also aware of a number of issues on devices with Adreno GPUs and are working to resolve them. > At a minimum, Dusklight requires a GPU with support for D3D12, Vulkan 1.1+, or Metal. For older devices, best-effort support is provided for D3D11 and OpenGL ES (Android), but will not achieve full accuracy or performance. Your experience with specific hardware, operating systems, and drivers may vary.
### 1. Dump your game ### 1. Dump your game
You must dump your own copy of the game, please see [this article](https://wiki.dolphin-emu.org/index.php?title=Ripping_Games) for instructions. After dumping, you can use a program like [Dolphin](https://dolphin-emu.org/) or [nodtool](https://github.com/encounter/nod/releases) to convert the `.iso` to a `.rvz` to save space. You must dump your own copy of the game. Please see [this article](https://wiki.dolphin-emu.org/index.php?title=Ripping_Games) for instructions. After dumping, you can use a program like [Dolphin](https://dolphin-emu.org/) or [nodtool](https://github.com/encounter/nod/releases) to convert the `.iso` to `.rvz` to save space.
Currently, only the GameCube USA and EUR releases are supported. Support for other versions of the game is planned in the future. Currently, only the GameCube USA and EUR releases are supported. Support for other versions of the game is planned in the future.
### 2. Download [Dusklight](https://github.com/TwilitRealm/dusklight/releases) ### 2. Install Dusklight
### 3. Setup the game Visit the [official installation guide](https://twilitrealm.dev/install/) for full instructions.
**Windows / macOS / Linux**
- Extract the .zip file
- Launch Dusklight
- Press **Select Disc Image** and provide the path to your supported game dump
- Press **Play**!
**iOS**
- Follow the [iOS setup guide](docs/ios-install-altstore.md)
**Android**
- Install the Dusklight APK
- Launch Dusklight
- Press **Select Disc Image** and provide the path to your supported game dump
- Press **Play**!
# Building # Building
+1 -1
+20 -14
View File
@@ -16,37 +16,37 @@
]; ];
forAllSystems = lib.genAttrs supportedSystems; forAllSystems = lib.genAttrs supportedSystems;
dawnVersion = "v20260423.175430"; dawnVersion = "v20260618.032059";
nodVersion = "v2.0.0-alpha.8"; nodVersion = "v2.0.0-alpha.10";
versionSuffix = "nix-" + (self.shortRev or self.dirtyShortRev or "dirty"); versionSuffix = "nix-" + (self.shortRev or self.dirtyShortRev or "dirty");
dawnInfo = { dawnInfo = {
"x86_64-linux" = { "x86_64-linux" = {
triple = "linux-x86_64"; triple = "linux-x86_64";
hash = "sha256-HXfKTLHtMPwupnFnaflCARtXVPuS/0PoCePXidjE5xs="; hash = "sha256-GFSd573b+VQx/VmFdNQgWDd0V9ayQlcw0Zuopke12ak=";
}; };
"aarch64-linux" = { "aarch64-linux" = {
triple = "linux-aarch64"; triple = "linux-aarch64";
hash = "sha256-34yyFpfqBZUwoFXQ41F0AwAU78FaNihOSY0oriwn6B0="; hash = "sha256-ZaoP7BAjBMnfAv2/AMRi3FNH2ZtyqASCSFyU/oB2Mzg=";
}; };
"aarch64-darwin" = { "aarch64-darwin" = {
triple = "darwin-arm64"; triple = "darwin-arm64";
hash = "sha256-eQnzrBp6gjiBek1VYQ9A5W13ClYWrDDKjIqv/7eNTR4="; hash = "sha256-HT+qtlLaSHyoXPrUcXgcTGa877X5YfzbxRD4bJb7i1Y=";
}; };
"x86_64-darwin" = { "x86_64-darwin" = {
triple = "darwin-x86_64"; triple = "darwin-x86_64";
hash = "sha256-QGWiGdxiI9kci3NPXH6QFFirxn16851zB/w3jqhIBJ4="; hash = "sha256-cUNaCbA7rlKSukDVKGaVEVw0Zt1+mSbaHbmUCMvMVWc=";
}; };
}; };
nodPrebuiltInfo = { nodPrebuiltInfo = {
"x86_64-linux" = { "x86_64-linux" = {
triple = "linux-x86_64"; triple = "linux-x86_64";
hash = "sha256-mUqvLsbsqaZ+HAjMmHYPYO+MgtanGRTw7Gzn5uXR5rE="; hash = "sha256-FVQWECVA2gWdc+n5OQ/Tvwn8z0qdgjSd1WlFt5HKOec=";
}; };
"aarch64-darwin" = { "aarch64-darwin" = {
triple = "macos-arm64"; triple = "macos-arm64";
hash = "sha256-UPy1ywCcv0K6VJOU3uUelJuUdBh3UNaPRlyP5LOBeDw="; hash = "sha256-8ZEejxksVgShNKUVRCBYaLOp9x/qOC9pAeVrElQUGUk=";
}; };
}; };
@@ -75,7 +75,7 @@
''; '';
dawn = pkgs.fetchzip { dawn = pkgs.fetchzip {
url = "https://github.com/encounter/dawn-build/releases/download/${dawnVersion}/dawn-${dawnInfo.${system}.triple}.tar.gz"; url = "https://github.com/encounter/dawn/releases/download/${dawnVersion}/dawn-${dawnInfo.${system}.triple}.tar.gz";
hash = dawnInfo.${system}.hash; hash = dawnInfo.${system}.hash;
stripRoot = false; stripRoot = false;
}; };
@@ -94,7 +94,7 @@
owner = "encounter"; owner = "encounter";
repo = "nod"; repo = "nod";
rev = nodVersion; rev = nodVersion;
hash = "sha256-+zrtVzjo0+X/6uMcNUn1+FaSR+jOhrcQSDNBFjw0NDs="; hash = "sha256-r8qDlOVxv5iKiFjJQrcBuL9HVoOM3yEjRVnQIMqaICs=";
}; };
patches = [ ./fix-cmake-paths.patch ]; patches = [ ./fix-cmake-paths.patch ];
cargoDeps = pkgs.rustPlatform.importCargoLock { cargoDeps = pkgs.rustPlatform.importCargoLock {
@@ -141,12 +141,12 @@
XXHASH = pkgs.xxhash.src; XXHASH = pkgs.xxhash.src;
ZSTD = pkgs.zstd.src; ZSTD = pkgs.zstd.src;
FMT = pkgs.fetchzip { FMT = pkgs.fetchzip {
url = "https://github.com/fmtlib/fmt/archive/refs/tags/11.1.4.tar.gz"; url = "https://github.com/fmtlib/fmt/archive/refs/tags/12.1.0.tar.gz";
hash = "sha256-sUbxlYi/Aupaox3JjWFqXIjcaQa0LFjclQAOleT+FRA="; hash = "sha256-ZmI1Dv0ZabPlxa02OpERI47jp7zFfjpeWCy1WyuPYZ0=";
}; };
TRACY = pkgs.fetchzip { TRACY = pkgs.fetchzip {
url = "https://github.com/wolfpld/tracy/archive/a64b9a20294d59421a2f57aeca3c6383d8c48169.tar.gz"; url = "https://github.com/wolfpld/tracy/archive/6789e7d6f9a65ec98926b602097a33a9676d2606.tar.gz";
hash = "sha256-hbNGOsGeyGSvCJ2No8RkwOib1lX2on3vNZSzyVkZdXw="; hash = "sha256-Xxyd7G/mnXEPpN+ehmwl0AkAhS3CwObpJNDgcqbdUJg=";
}; };
IMGUI = pkgs.fetchFromGitHub { IMGUI = pkgs.fetchFromGitHub {
owner = "ocornut"; owner = "ocornut";
@@ -269,6 +269,12 @@
runHook postInstall runHook postInstall
''; '';
postFixup = lib.optionalString (!isDarwin) ''
patchelf \
--add-needed "${pkgs.vulkan-loader}/lib/libvulkan.so" \
$out/bin/dusklight
'';
dontStrip = true; dontStrip = true;
meta = { meta = {
@@ -20,7 +20,7 @@ public:
/* 0x14 */ cM3dGAab mM3dGAab; /* 0x14 */ cM3dGAab mM3dGAab;
/* 0x30 */ cBgS_ShdwDraw_Callback mCallbackFun; /* 0x30 */ cBgS_ShdwDraw_Callback mCallbackFun;
#if DEBUG #if PARTIAL_DEBUG || DEBUG
/* 0x34 */ int field_0x34; /* 0x34 */ int field_0x34;
#endif #endif
}; };
+2
View File
@@ -48,6 +48,8 @@ public:
/* 0x1370 */ Z2FxLineMgr mFxLineMgr; /* 0x1370 */ Z2FxLineMgr mFxLineMgr;
#if DEBUG #if DEBUG
/* 0x13BC */ Z2DebugSys mDebugSys; /* 0x13BC */ Z2DebugSys mDebugSys;
#elif PARTIAL_DEBUG
alignas(Z2DebugSys) u8 mDebugSys[sizeof(Z2DebugSys)];
#endif #endif
}; // Size: 0x138C }; // Size: 0x138C
+1 -1
View File
@@ -194,7 +194,7 @@ public:
JAISoundHandle* getMainBgmHandle() { return &mMainBgmHandle; } JAISoundHandle* getMainBgmHandle() { return &mMainBgmHandle; }
JAISoundHandle* getSubBgmHandle() { return &mSubBgmHandle; } JAISoundHandle* getSubBgmHandle() { return &mSubBgmHandle; }
#if DEBUG #if PARTIAL_DEBUG || DEBUG
f32 field_0x00_debug; f32 field_0x00_debug;
u8 field_0x04_debug; u8 field_0x04_debug;
#endif #endif
+2 -2
View File
@@ -100,13 +100,13 @@ public:
bool isForceBattle() { return forceBattle_; } bool isForceBattle() { return forceBattle_; }
JSUList<Z2CreatureEnemy>* getEnemyList() { return &field_0x0; } JSUList<Z2CreatureEnemy>* getEnemyList() { return &field_0x0; }
#if DEBUG #if PARTIAL_DEBUG || DEBUG
JSUList<Z2SoundObjBase>* getAllList() { return &allList_; } JSUList<Z2SoundObjBase>* getAllList() { return &allList_; }
#endif #endif
private: private:
/* 0x00 */ JSUList<Z2CreatureEnemy> field_0x0; /* 0x00 */ JSUList<Z2CreatureEnemy> field_0x0;
#if DEBUG #if PARTIAL_DEBUG || DEBUG
/* 0x0C */ JSUList<Z2SoundObjBase> allList_; /* 0x0C */ JSUList<Z2SoundObjBase> allList_;
#endif #endif
/* 0x0C */ Z2EnemyArea enemyArea_; /* 0x0C */ Z2EnemyArea enemyArea_;
+1 -1
View File
@@ -7,7 +7,7 @@
struct Z2SoundStarter; struct Z2SoundStarter;
class Z2SoundObjBase : public Z2SoundHandles class Z2SoundObjBase : public Z2SoundHandles
#if DEBUG #if PARTIAL_DEBUG || DEBUG
, public JSULink<Z2SoundObjBase> , public JSULink<Z2SoundObjBase>
#endif #endif
{ {
+7 -2
View File
@@ -88,9 +88,14 @@ public:
/* 0x396A */ u8 field_0x396A[0x399E - 0x396A]; /* 0x396A */ u8 field_0x396A[0x399E - 0x396A];
/* 0x399E */ s16 field_0x399e; /* 0x399E */ s16 field_0x399e;
/* 0x39A0 */ u8 field_0x39A0[0x39A4 - 0x39A0]; /* 0x39A0 */ u8 field_0x39A0[0x39A4 - 0x39A0];
#if TARGET_PC
/* 0x39A4 */ cM_rnd_c mMantRng;
#endif
}; };
#if TARGET_PC
STATIC_ASSERT(sizeof(mant_class) == 0x39ac);
#else
STATIC_ASSERT(sizeof(mant_class) == 0x39a4); STATIC_ASSERT(sizeof(mant_class) == 0x39a4);
#endif
#endif /* D_A_MANT_H */ #endif /* D_A_MANT_H */
+2 -2
View File
@@ -45,7 +45,7 @@ private:
class dAttParam_c : public JORReflexible { class dAttParam_c : public JORReflexible {
public: public:
#if DEBUG #if PARTIAL_DEBUG || DEBUG
/* 0x04 */ s8 mHIOChildNo; /* 0x04 */ s8 mHIOChildNo;
#endif #endif
@@ -66,7 +66,7 @@ public:
/* 0x35 */ u8 mAttnCursorDisappearFrames; /* 0x35 */ u8 mAttnCursorDisappearFrames;
/* 0x38 */ f32 field_0x38; /* 0x38 */ f32 field_0x38;
/* 0x3C */ f32 field_0x3c; /* 0x3C */ f32 field_0x3c;
#if DEBUG #if PARTIAL_DEBUG || DEBUG
/* 0x44 */ s32 mDebugDispPosX; /* 0x44 */ s32 mDebugDispPosX;
/* 0x48 */ s32 mDebugDispPosY; /* 0x48 */ s32 mDebugDispPosY;
#endif #endif
+2 -2
View File
@@ -201,7 +201,7 @@ private:
/* 0x02C */ u32 m_flags; /* 0x02C */ u32 m_flags;
/* 0x030 */ cXyz* pm_pos; /* 0x030 */ cXyz* pm_pos;
/* 0x034 */ cXyz* pm_old_pos; /* 0x034 */ cXyz* pm_old_pos;
#if DEBUG #if PARTIAL_DEBUG || DEBUG
/* 0x038 */ cXyz unk_0x38; /* 0x038 */ cXyz unk_0x38;
#endif #endif
/* 0x038 */ cXyz* pm_speed; /* 0x038 */ cXyz* pm_speed;
@@ -229,7 +229,7 @@ private:
/* 0x0CC */ f32 field_0xcc; /* 0x0CC */ f32 field_0xcc;
/* 0x0D0 */ f32 m_wtr_chk_offset; /* 0x0D0 */ f32 m_wtr_chk_offset;
/* 0x0D4 */ cBgS_PolyInfo* pm_out_poly_info; /* 0x0D4 */ cBgS_PolyInfo* pm_out_poly_info;
#if DEBUG #if PARTIAL_DEBUG || DEBUG
/* 0x0E4 */ cXyz unk_0xe4; /* 0x0E4 */ cXyz unk_0xe4;
#endif #endif
/* 0x0D8 */ f32 field_0xd8; /* 0x0D8 */ f32 field_0xd8;
+1 -1
View File
@@ -79,7 +79,7 @@ public:
// /* 0x0000 */ cCcS mCCcS; // /* 0x0000 */ cCcS mCCcS;
/* 0x284C */ dCcMassS_Mng mMass_Mng; /* 0x284C */ dCcMassS_Mng mMass_Mng;
#if DEBUG #if PARTIAL_DEBUG || DEBUG
/* 0x2AD0 */ u8 m_is_mass_all_timer; /* 0x2AD0 */ u8 m_is_mass_all_timer;
#endif #endif
}; // Size = 0x2AC4 }; // Size = 0x2AC4
+1 -1
View File
@@ -1037,7 +1037,7 @@ public:
/* 0x1DE09 */ u8 field_0x1de09; /* 0x1DE09 */ u8 field_0x1de09;
/* 0x1DE0A */ u8 field_0x1de0a; /* 0x1DE0A */ u8 field_0x1de0a;
/* 0x1DE0B */ u8 mIsDebugMode; /* 0x1DE0B */ u8 mIsDebugMode;
#if DEBUG #if PARTIAL_DEBUG || DEBUG
/* 0x1DE0C */ OSStopwatch mStopwatch; /* 0x1DE0C */ OSStopwatch mStopwatch;
#endif #endif
+1 -1
View File
@@ -41,7 +41,7 @@ public:
BASE_ROOM5, BASE_ROOM5,
BASE_DEMO, BASE_DEMO,
#if DEBUG #if PARTIAL_DEBUG || DEBUG
BASE_DEBUG, BASE_DEBUG,
#endif #endif
+1 -1
View File
@@ -259,7 +259,7 @@ public:
/* 0x09B8 */ DUNGEON_LIGHT dungeonlight[8]; /* 0x09B8 */ DUNGEON_LIGHT dungeonlight[8];
/* 0x0C18 */ BOSS_LIGHT field_0x0c18[8]; /* 0x0C18 */ BOSS_LIGHT field_0x0c18[8];
/* 0x0D58 */ BOSS_LIGHT field_0x0d58[6]; /* 0x0D58 */ BOSS_LIGHT field_0x0d58[6];
#if DEBUG #if PARTIAL_DEBUG || DEBUG
/* 0x0E48 */ NAVYCHAN navy; /* 0x0E48 */ NAVYCHAN navy;
/* 0x0E58 */ u8 field_0xe58[0xE68 - 0xE58]; // part of NAVYCHAN? /* 0x0E58 */ u8 field_0xe58[0xE68 - 0xE58]; // part of NAVYCHAN?
#endif #endif
+1
View File
@@ -218,6 +218,7 @@ private:
bool mCursorInterpPrevAngular; bool mCursorInterpPrevAngular;
bool mCursorInterpCurrAngular; bool mCursorInterpCurrAngular;
bool mCursorInterpInit; bool mCursorInterpInit;
bool mPointerTouchPressHoveredCurrent;
#endif #endif
}; };
+10
View File
@@ -360,7 +360,12 @@ inline void dMsgObject_demoMessageGroup() {
} }
inline bool dMsgObject_isTalkNowCheck() { inline bool dMsgObject_isTalkNowCheck() {
#if TARGET_PC
dMsgObject_c* msgObject = dMsgObject_getMsgObjectClass();
return msgObject != NULL && msgObject->getStatus() != 1;
#else
return dMsgObject_getMsgObjectClass()->getStatus() == 1 ? false : true; return dMsgObject_getMsgObjectClass()->getStatus() == 1 ? false : true;
#endif
} }
inline bool dMsgObject_isKillMessageFlag() { inline bool dMsgObject_isKillMessageFlag() {
@@ -497,7 +502,12 @@ inline void dMsgObject_onMsgSend() {
} }
inline bool dMsgObject_isFukidashiCheck() { inline bool dMsgObject_isFukidashiCheck() {
#if TARGET_PC
dMsgObject_c* msgObject = dMsgObject_getMsgObjectClass();
return msgObject != NULL && msgObject->getScrnDrawPtr() != NULL;
#else
return dMsgObject_getMsgObjectClass()->getScrnDrawPtr() == NULL ? false : true; return dMsgObject_getMsgObjectClass()->getScrnDrawPtr() == NULL ? false : true;
#endif
} }
inline void* dMsgObject_getTalkHeap() { inline void* dMsgObject_getTalkHeap() {
+2 -2
View File
@@ -521,13 +521,13 @@ private:
/* 0x019 */ u8 field_0x19; /* 0x019 */ u8 field_0x19;
/* 0x01A */ u8 field_0x1a; /* 0x01A */ u8 field_0x1a;
/* 0x01B */ u8 field_0x1b; /* 0x01B */ u8 field_0x1b;
#if DEBUG #if PARTIAL_DEBUG || DEBUG
/* 0x01C */ dPa_simpleEcallBack field_0x1c[48]; /* 0x01C */ dPa_simpleEcallBack field_0x1c[48];
#else #else
/* 0x01C */ dPa_simpleEcallBack field_0x1c[25]; /* 0x01C */ dPa_simpleEcallBack field_0x1c[25];
#endif #endif
/* 0x210 */ level_c field_0x210; /* 0x210 */ level_c field_0x210;
#if DEBUG #if PARTIAL_DEBUG || DEBUG
u8 mSceneCount; u8 mSceneCount;
#endif #endif
}; };
+1 -1
View File
@@ -59,7 +59,7 @@ private:
/* 0x18 */ JKRHeap* heap; /* 0x18 */ JKRHeap* heap;
/* 0x1C */ JKRSolidHeap* mDataHeap; /* 0x1C */ JKRSolidHeap* mDataHeap;
/* 0x20 */ void** mRes; /* 0x20 */ void** mRes;
#if DEBUG #if PARTIAL_DEBUG || DEBUG
/* 0x24 */ int mSize; /* 0x24 */ int mSize;
#endif #endif
}; // Size: 0x24 }; // Size: 0x24
+4 -1
View File
@@ -1008,7 +1008,7 @@ public:
static const int ZONE_MAX = 0x20; static const int ZONE_MAX = 0x20;
#if DEBUG #if PARTIAL_DEBUG || DEBUG
/* 0x000 */ u8 unk_0x0; /* 0x000 */ u8 unk_0x0;
/* 0x001 */ char unk_0x1; /* 0x001 */ char unk_0x1;
/* 0x000 */ u8 unk_0x2[0x48 - 0x2]; /* 0x000 */ u8 unk_0x2[0x48 - 0x2];
@@ -1029,6 +1029,9 @@ public:
/* 0xF30 */ s64 mSaveTotalTime; /* 0xF30 */ s64 mSaveTotalTime;
#if DEBUG #if DEBUG
/* 0xF80 */ flagFile_c mFlagFile; /* 0xF80 */ flagFile_c mFlagFile;
#elif PARTIAL_DEBUG
// flagFile_c's ctor/virtuals are only defined under #if DEBUG (d_save.cpp)
alignas(flagFile_c) u8 mFlagFile[sizeof(flagFile_c)];
#endif #endif
}; // Size: 0xF38 }; // Size: 0xF38
+4 -4
View File
@@ -538,7 +538,7 @@ public:
/* vt[86] */ virtual stage_tgsc_class* getDrTg(void) const = 0; /* vt[86] */ virtual stage_tgsc_class* getDrTg(void) const = 0;
/* vt[87] */ virtual void setDoor(stage_tgsc_class*) = 0; /* vt[87] */ virtual void setDoor(stage_tgsc_class*) = 0;
/* vt[88] */ virtual stage_tgsc_class* getDoor(void) const = 0; /* vt[88] */ virtual stage_tgsc_class* getDoor(void) const = 0;
#if DEBUG #if PARTIAL_DEBUG || DEBUG
virtual void setUnit(void*) = 0; virtual void setUnit(void*) = 0;
virtual void* getUnit() = 0; virtual void* getUnit() = 0;
#endif #endif
@@ -796,7 +796,7 @@ public:
virtual stage_tgsc_class* getDrTg(void) const { return mDrTg; } virtual stage_tgsc_class* getDrTg(void) const { return mDrTg; }
virtual void setDoor(stage_tgsc_class* i_Door) { mDoor = i_Door; } virtual void setDoor(stage_tgsc_class* i_Door) { mDoor = i_Door; }
virtual stage_tgsc_class* getDoor(void) const { return mDoor; } virtual stage_tgsc_class* getDoor(void) const { return mDoor; }
#if DEBUG #if PARTIAL_DEBUG || DEBUG
virtual void setUnit(void* i_Unit) { mUnit = i_Unit; } virtual void setUnit(void* i_Unit) { mUnit = i_Unit; }
virtual void* getUnit() { return mUnit; } virtual void* getUnit() { return mUnit; }
#endif #endif
@@ -845,7 +845,7 @@ public:
/* 0x54 */ stage_tgsc_class* mDrTg; /* 0x54 */ stage_tgsc_class* mDrTg;
/* 0x58 */ stage_tgsc_class* mDoor; /* 0x58 */ stage_tgsc_class* mDoor;
/* 0x5C */ dStage_FloorInfo_c* mFloorInfo; /* 0x5C */ dStage_FloorInfo_c* mFloorInfo;
#if DEBUG #if PARTIAL_DEBUG || DEBUG
/* 0x60 */ void* mUnit; /* 0x60 */ void* mUnit;
#endif #endif
/* 0x60 */ u16 mPlayerNum; /* 0x60 */ u16 mPlayerNum;
@@ -990,7 +990,7 @@ public:
/* vt[86] */ virtual stage_tgsc_class* getDrTg(void) const { return mDrTg; } /* vt[86] */ virtual stage_tgsc_class* getDrTg(void) const { return mDrTg; }
/* vt[87] */ virtual void setDoor(stage_tgsc_class* i_Door) { mDoor = i_Door; } /* vt[87] */ virtual void setDoor(stage_tgsc_class* i_Door) { mDoor = i_Door; }
/* vt[88] */ virtual stage_tgsc_class* getDoor(void) const { return mDoor; } /* vt[88] */ virtual stage_tgsc_class* getDoor(void) const { return mDoor; }
#if DEBUG #if PARTIAL_DEBUG || DEBUG
virtual void setUnit(void* i_Unit) { virtual void setUnit(void* i_Unit) {
UNUSED(i_Unit); UNUSED(i_Unit);
OSReport("stage non unit list data !!\n"); OSReport("stage non unit list data !!\n");
+3
View File
@@ -97,6 +97,9 @@ public:
private: private:
#if DEBUG #if DEBUG
/* 0x00 */ dVibTest_c mVibTest; /* 0x00 */ dVibTest_c mVibTest;
#elif PARTIAL_DEBUG
// dVibTest_c's ctor/virtuals are only defined under #if DEBUG (d_vibration.cpp)
alignas(dVibTest_c) u8 mVibTest[sizeof(dVibTest_c)];
#endif #endif
class { class {
+70 -7
View File
@@ -1,9 +1,11 @@
#ifndef DUSK_CONFIG_HPP #ifndef DUSK_CONFIG_HPP
#define DUSK_CONFIG_HPP #define DUSK_CONFIG_HPP
#include <concepts>
#include <functional> #include <functional>
#include <nlohmann/json.hpp>
#include <stdexcept> #include <stdexcept>
#include "nlohmann/json.hpp"
#include "config_var.hpp" #include "config_var.hpp"
namespace dusk::config { namespace dusk::config {
@@ -40,7 +42,7 @@ public:
[[nodiscard]] virtual nlohmann::json dumpToJson(const ConfigVarBase& cVar) const = 0; [[nodiscard]] virtual nlohmann::json dumpToJson(const ConfigVarBase& cVar) const = 0;
}; };
template<ConfigValue T> template <ConfigValue T>
class ConfigImpl : public ConfigImplBase { class ConfigImpl : public ConfigImplBase {
// Just downcasting the references... // Just downcasting the references...
void loadFromJson(ConfigVarBase& cVar, const nlohmann::json& jsonValue) const final { void loadFromJson(ConfigVarBase& cVar, const nlohmann::json& jsonValue) const final {
@@ -90,20 +92,28 @@ public:
void Register(ConfigVarBase& configVar); void Register(ConfigVarBase& configVar);
/** /**
* \brief Indicate that all registrations have happened and everything should lock in. * \brief Unregister a CVar, detaching it from the config system.
*
* If the CVar carries a user-set value (Value or Speedrun layer), it is stashed as an
* unregistered key: Save() keeps writing it, and a later Register() of the same name restores
* it through the normal back-fill path. The CVar may be destroyed after this returns.
*/ */
void FinishRegistration(); void unregister(ConfigVarBase& configVar);
/** /**
* \brief Load config from the standard user preferences location. * \brief Load config from the standard user preferences location.
*/ */
void LoadFromUserPreferences(); void load_from_user_preferences();
void LoadFromFileName(const char* path); void load_from_file_name(const char* path);
void load_arg_override(std::string_view name, std::string_view value);
void shutdown();
/** /**
* \brief Save the config to file. * \brief Save the config to file.
*/ */
void Save(); void save();
/** /**
* \brief Get a registered CVar by name. * \brief Get a registered CVar by name.
@@ -124,6 +134,59 @@ void ClearAllActionBindings(int port);
*/ */
void EnumerateRegistered(std::function<void(ConfigVarBase&)> callback); void EnumerateRegistered(std::function<void(ConfigVarBase&)> callback);
/**
* \brief Type-erased change callback. previousValue points at the value before the mutation
* (a `const T*` for a `ConfigVar<T>`) and is valid only for the duration of the call.
*/
using ChangeCallback = std::function<void(ConfigVarBase& cVar, const void* previousValue)>;
/**
* \brief Token identifying a change subscription. 0 is never a valid token.
*/
using Subscription = u64;
/**
* \brief Subscribe to changes of the named CVar (registered or not yet registered).
*
* Fired synchronously on the mutating thread (in practice the game thread) whenever the CVar's
* effective value changes at runtime: setValue, override/speedrun setters and clears. Values
* applied by config load or launch arguments do *not* notify: loads happen during startup
* before the subsystems callbacks push values into are initialized, and each subsystem reads
* its initial value itself at its own init. Callbacks may mutate other CVars; a nested
* mutation of the same CVar applies but does not re-notify.
*/
Subscription subscribe(std::string_view name, ChangeCallback callback);
/**
* \brief Typed convenience overload: the callback receives the current and previous values.
*/
template <ConfigValue T, typename Callback>
requires std::invocable<Callback, const T&, const T&> Subscription subscribe(
ConfigVar<T>& cVar, Callback&& callback) {
return subscribe(cVar.getName(),
[&cVar, cb = std::forward<Callback>(callback)](ConfigVarBase&, const void* previousValue) {
cb(cVar.getValue(), *static_cast<const T*>(previousValue));
});
}
void unsubscribe(Subscription token);
/**
* \brief Register a CVar and attach a change callback in one step.
*
* Useful for pushing settings into external systems (e.g. aurora) from one place instead of
* every UI setter. The callback fires only for runtime changes (see subscribe); the value
* loaded from config or launch arguments does not fire it the external system reads its
* initial value itself at its own initialization.
*/
template <ConfigValue T, typename Callback>
requires std::invocable<Callback, const T&, const T&> Subscription Register(
ConfigVar<T>& cVar, Callback&& onChange) {
auto subscription = subscribe(cVar, std::forward<Callback>(onChange));
Register(static_cast<ConfigVarBase&>(cVar));
return subscription;
}
template <ConfigValue T> template <ConfigValue T>
const ConfigImplBase* GetConfigImpl() { const ConfigImplBase* GetConfigImpl() {
static ConfigImpl<T> config; static ConfigImpl<T> config;
+82 -6
View File
@@ -2,10 +2,12 @@
#define DUSK_CONFIG_VAR_HPP #define DUSK_CONFIG_VAR_HPP
#include "dolphin/types.h" #include "dolphin/types.h"
#include <type_traits> #include <concepts>
#include <cstdlib> #include <cstdlib>
#include <limits> #include <limits>
#include <optional>
#include <string> #include <string>
#include <type_traits>
/** /**
* The configuration system. * The configuration system.
@@ -69,7 +71,7 @@ protected:
/** /**
* The name of this CVar, used in the configuration file. * The name of this CVar, used in the configuration file.
*/ */
const char* name; std::string name;
/** /**
* Whether this CVar has been registered with the global managing logic. * Whether this CVar has been registered with the global managing logic.
@@ -87,8 +89,8 @@ protected:
*/ */
const ConfigImplBase* impl; const ConfigImplBase* impl;
ConfigVarBase(const char* name, const ConfigImplBase* impl); ConfigVarBase(const ConfigVarBase&) = delete;
virtual ~ConfigVarBase() = default; ConfigVarBase(std::string name, const ConfigImplBase* impl);
/** /**
* Check that the CVar is registered, aborting if this is not the case. * Check that the CVar is registered, aborting if this is not the case.
@@ -98,7 +100,22 @@ protected:
abort(); abort();
} }
/**
* Whether any change subscriber (see config::subscribe) is attached to this CVar's name.
*/
[[nodiscard]] bool has_subscribers() const;
/**
* Notify change subscribers (see config::subscribe) that the effective value of this CVar
* changed. Called by mutators after the change has been applied; previousValue points at
* the old value (a `const T*` for a `ConfigVar<T>`), valid only for the duration of the
* call.
*/
void notify_changed(const void* previousValue);
public: public:
virtual ~ConfigVarBase();
/** /**
* Get the name of this CVar, used in the configuration file. * Get the name of this CVar, used in the configuration file.
*/ */
@@ -121,6 +138,7 @@ public:
* This is necessary to make it legal to access. * This is necessary to make it legal to access.
*/ */
void markRegistered(); void markRegistered();
void unmarkRegistered();
/** /**
* Clear a speedrun-mode override if one is active on this CVar. * Clear a speedrun-mode override if one is active on this CVar.
@@ -155,6 +173,7 @@ template <typename T>
concept ConfigValue = concept ConfigValue =
!std::is_const_v<T> !std::is_const_v<T>
&& !std::is_volatile_v<T> && !std::is_volatile_v<T>
&& std::equality_comparable<T>
&& (std::is_same_v<T, bool> && (std::is_same_v<T, bool>
|| ConfigValueInteger<T> || ConfigValueInteger<T>
|| std::is_same_v<T, f32> || std::is_same_v<T, f32>
@@ -166,6 +185,9 @@ concept ConfigValue =
template <ConfigValue T> template <ConfigValue T>
const ConfigImplBase* GetConfigImpl(); const ConfigImplBase* GetConfigImpl();
template <ConfigValue T>
class ConfigImpl;
template <typename T> template <typename T>
struct ConfigEnumRange { struct ConfigEnumRange {
static constexpr auto min = std::numeric_limits<std::underlying_type_t<T>>::min(); static constexpr auto min = std::numeric_limits<std::underlying_type_t<T>>::min();
@@ -192,10 +214,12 @@ public:
* @param arg Arguments to forward to construct the default value. * @param arg Arguments to forward to construct the default value.
*/ */
template <typename... Args> template <typename... Args>
ConfigVar(const char* name, Args&&... arg) ConfigVar(std::string name, Args&&... arg)
: ConfigVarBase(name, GetConfigImpl<T>()), defaultValue(std::forward<Args>(arg)...), : ConfigVarBase(std::move(name), GetConfigImpl<T>()), defaultValue(std::forward<Args>(arg)...),
value(), overrideValue() {} value(), overrideValue() {}
ConfigVar(ConfigVar const&) = delete;
/** /**
* \brief Get the current value of the CVar. * \brief Get the current value of the CVar.
* *
@@ -234,6 +258,7 @@ public:
*/ */
void setValue(T newValue, bool replaceOverride = true) { void setValue(T newValue, bool replaceOverride = true) {
checkRegistered(); checkRegistered();
const auto previous = previous_for_notify();
value = std::move(newValue); value = std::move(newValue);
if (replaceOverride) { if (replaceOverride) {
@@ -242,6 +267,7 @@ public:
} else if (layer != ConfigVarLayer::Override) { } else if (layer != ConfigVarLayer::Override) {
layer = ConfigVarLayer::Value; layer = ConfigVarLayer::Value;
} }
notify_if_changed(previous);
} }
operator const T&() { operator const T&() {
@@ -258,8 +284,10 @@ public:
*/ */
void setOverrideValue(T newValue) { void setOverrideValue(T newValue) {
checkRegistered(); checkRegistered();
const auto previous = previous_for_notify();
overrideValue = std::move(newValue); overrideValue = std::move(newValue);
layer = ConfigVarLayer::Override; layer = ConfigVarLayer::Override;
notify_if_changed(previous);
} }
/** /**
@@ -273,25 +301,31 @@ public:
void setSpeedrunValue(T newValue) { void setSpeedrunValue(T newValue) {
checkRegistered(); checkRegistered();
if (layer != ConfigVarLayer::Override) { if (layer != ConfigVarLayer::Override) {
const auto previous = previous_for_notify();
priorLayer = layer; priorLayer = layer;
overrideValue = std::move(newValue); overrideValue = std::move(newValue);
layer = ConfigVarLayer::Speedrun; layer = ConfigVarLayer::Speedrun;
notify_if_changed(previous);
} }
} }
void clearOverride() { void clearOverride() {
checkRegistered(); checkRegistered();
if (layer == ConfigVarLayer::Override) { if (layer == ConfigVarLayer::Override) {
const auto previous = previous_for_notify();
overrideValue = {}; overrideValue = {};
layer = ConfigVarLayer::Value; layer = ConfigVarLayer::Value;
notify_if_changed(previous);
} }
} }
void clearSpeedrunOverride() override { void clearSpeedrunOverride() override {
checkRegistered(); checkRegistered();
if (layer == ConfigVarLayer::Speedrun) { if (layer == ConfigVarLayer::Speedrun) {
const auto previous = previous_for_notify();
overrideValue = {}; overrideValue = {};
layer = priorLayer; layer = priorLayer;
notify_if_changed(previous);
} }
} }
@@ -305,6 +339,48 @@ public:
const ConfigVarLayer effectiveLayer = (layer == ConfigVarLayer::Speedrun) ? priorLayer : layer; const ConfigVarLayer effectiveLayer = (layer == ConfigVarLayer::Speedrun) ? priorLayer : layer;
return effectiveLayer == ConfigVarLayer::Default ? defaultValue : value; return effectiveLayer == ConfigVarLayer::Default ? defaultValue : value;
} }
private:
// The config loader applies values through the silent load_* methods below.
friend class ConfigImpl<T>;
/**
* Copy of the effective value before a mutation, taken only when someone is subscribed.
*/
[[nodiscard]] std::optional<T> previous_for_notify() const {
return has_subscribers() ? std::optional<T>{getValue()} : std::nullopt;
}
/**
* Notify subscribers if the effective value actually changed across a mutation.
*/
void notify_if_changed(const std::optional<T>& previous) {
if (previous.has_value() && !(getValue() == *previous)) {
notify_changed(&*previous);
}
}
/**
* setValue(newValue, false) without notifying change subscribers. Used when loading config:
* loads happen during startup before the subsystems change callbacks push values into are
* initialized, and each subsystem applies the loaded value itself at its own init.
*/
void load_value(T newValue) {
checkRegistered();
value = std::move(newValue);
if (layer != ConfigVarLayer::Override) {
layer = ConfigVarLayer::Value;
}
}
/**
* setOverrideValue without notifying change subscribers (see load_value).
*/
void load_override_value(T newValue) {
checkRegistered();
overrideValue = std::move(newValue);
layer = ConfigVarLayer::Override;
}
}; };
using ActionBindConfigVar = ConfigVar<int>; using ActionBindConfigVar = ConfigVar<int>;
+7 -2
View File
@@ -6,6 +6,9 @@ class CPaneMgr;
namespace dusk::menu_pointer { namespace dusk::menu_pointer {
using TargetId = u16;
constexpr TargetId InvalidTarget = 0xffff;
enum class Context { enum class Context {
None, None,
FileSelect, FileSelect,
@@ -43,12 +46,14 @@ bool active() noexcept;
bool enabled() noexcept; bool enabled() noexcept;
bool mouse_capture_active() noexcept; bool mouse_capture_active() noexcept;
const State& state() noexcept; const State& state() noexcept;
void set_hover_target(TargetId target) noexcept;
bool consume_click() noexcept; bool consume_click() noexcept;
bool peek_click() noexcept;
void set_dialog_choice(u8 choice, bool clicked) noexcept; void set_dialog_choice(u8 choice, bool clicked) noexcept;
bool get_dialog_choice(u8& choice) noexcept; bool get_dialog_choice(u8& choice) noexcept;
bool consume_dialog_click(u8& choice) noexcept; bool consume_dialog_click(u8& choice) noexcept;
void defer_activation(Context context, u8 target) noexcept; void defer_activation(Context context, TargetId target) noexcept;
bool consume_deferred_activation(Context context, u8 target) noexcept; bool consume_deferred_activation(Context context, TargetId target) noexcept;
void clear_deferred_activation(Context context) noexcept; void clear_deferred_activation(Context context) noexcept;
u32 suppressed_pad_buttons(u32 port) noexcept; u32 suppressed_pad_buttons(u32 port) noexcept;
void finish_pad_suppression_read(u32 port) noexcept; void finish_pad_suppression_read(u32 port) noexcept;
+4 -4
View File
@@ -4,9 +4,9 @@
namespace dusk::mouse { namespace dusk::mouse {
void read(); void read();
void getAimDeltas(float& out_yaw, float& out_pitch); void get_aim_deltas(float& out_yaw, float& out_pitch);
void getCameraDeltas(float& out_yaw, float& out_pitch); void get_camera_deltas(float& out_yaw, float& out_pitch);
void handle_event(const SDL_Event& event) noexcept; void handle_event(const SDL_Event& event) noexcept;
void onFocusLost(); void on_focus_lost();
void onFocusGained(); void on_focus_gained();
} // namespace dusk::mouse } // namespace dusk::mouse
+15 -2
View File
@@ -7,7 +7,8 @@
namespace dusk { namespace dusk {
using namespace config; using config::ConfigVar;
using config::ActionBindConfigVar;
enum class BloomMode : int { enum class BloomMode : int {
Off = 0, Off = 0,
@@ -46,6 +47,12 @@ enum class FrameInterpMode : u8 {
Unlimited = 2, Unlimited = 2,
}; };
enum class TouchTargeting : u8 {
Hybrid = 0,
Hold = 1,
Switch = 2,
};
enum class MenuScaling : u8 { enum class MenuScaling : u8 {
GameCube = 0, GameCube = 0,
Wii = 1, Wii = 1,
@@ -97,6 +104,12 @@ struct ConfigEnumRange<FrameInterpMode> {
static constexpr auto max = FrameInterpMode::Unlimited; static constexpr auto max = FrameInterpMode::Unlimited;
}; };
template <>
struct ConfigEnumRange<TouchTargeting> {
static constexpr auto min = TouchTargeting::Hybrid;
static constexpr auto max = TouchTargeting::Switch;
};
template <> template <>
struct ConfigEnumRange<MenuScaling> { struct ConfigEnumRange<MenuScaling> {
static constexpr auto min = MenuScaling::GameCube; static constexpr auto min = MenuScaling::GameCube;
@@ -216,6 +229,7 @@ struct UserSettings {
ConfigVar<bool> invertMouseY; ConfigVar<bool> invertMouseY;
ConfigVar<bool> freeCamera; ConfigVar<bool> freeCamera;
ConfigVar<bool> enableTouchControls; ConfigVar<bool> enableTouchControls;
ConfigVar<TouchTargeting> touchTargeting;
ConfigVar<bool> enableMenuPointer; ConfigVar<bool> enableMenuPointer;
ConfigVar<ui::ControlLayout> touchControlsLayout; ConfigVar<ui::ControlLayout> touchControlsLayout;
ConfigVar<bool> invertCameraXAxis; ConfigVar<bool> invertCameraXAxis;
@@ -275,7 +289,6 @@ struct UserSettings {
ConfigVar<DiscVerificationState> isoVerification; ConfigVar<DiscVerificationState> isoVerification;
ConfigVar<std::string> graphicsBackend; ConfigVar<std::string> graphicsBackend;
ConfigVar<bool> skipPreLaunchUI; ConfigVar<bool> skipPreLaunchUI;
ConfigVar<bool> showPipelineCompilation;
ConfigVar<bool> wasPresetChosen; ConfigVar<bool> wasPresetChosen;
ConfigVar<bool> checkForUpdates; ConfigVar<bool> checkForUpdates;
ConfigVar<int> cardFileType; ConfigVar<int> cardFileType;
+1 -1
View File
@@ -36,7 +36,7 @@ typedef struct node_create_request {
/* 0x58 */ s16 name; /* 0x58 */ s16 name;
/* 0x5C */ void* data; /* 0x5C */ void* data;
/* 0x60 */ s16 unk_0x60; /* 0x60 */ s16 unk_0x60;
#if DEBUG #if PARTIAL_DEBUG || DEBUG
/* 0x64 */ int unk_0x64; /* 0x64 */ int unk_0x64;
/* 0x68 */ int unk_0x68; /* 0x68 */ int unk_0x68;
#endif #endif
+2 -2
View File
@@ -11,13 +11,13 @@ class mDoAud_zelAudio_c : public Z2AudioMgr {
public: public:
void reset(); void reset();
mDoAud_zelAudio_c() { mDoAud_zelAudio_c() {
#if DEBUG #if PARTIAL_DEBUG || DEBUG
setMode(2); setMode(2);
#endif #endif
} }
~mDoAud_zelAudio_c() {} ~mDoAud_zelAudio_c() {}
#if DEBUG #if PARTIAL_DEBUG || DEBUG
u8 getMode() { return field_0x13bd; } u8 getMode() { return field_0x13bd; }
void setMode(u8 mode) { field_0x13bd = mode; } void setMode(u8 mode) { field_0x13bd = mode; }
+5
View File
@@ -35,6 +35,11 @@ public:
/* 0x4 */ s8 mNo; /* 0x4 */ s8 mNo;
/* 0x5 */ u8 mCount; /* 0x5 */ u8 mCount;
#else #else
#if PARTIAL_DEBUG
// Initialized here since the DEBUG ctor doesn't run.
/* 0x4 */ s8 mNo = -1;
/* 0x5 */ u8 mCount = 0;
#endif
virtual ~mDoHIO_entry_c() {} virtual ~mDoHIO_entry_c() {}
#endif #endif
}; };
@@ -12,12 +12,23 @@ struct JORNodeEvent;
class JORMContext; class JORMContext;
class JORServer; class JORServer;
// NOTE (stable game ABI): these classes stay non-polymorphic outside DEBUG
// on purpose. Making them polymorphic under PARTIAL_DEBUG would give every one of the ~250
// derived HIO classes a vptr and turn their plain `void genMessage(JORMContext*);`
// declarations into implicit virtual overrides whose definitions are #if DEBUG-gated; every
// instantiated one then fails to link (missing vtable). Closure types shared with DEBUG TUs
// either declare their own unconditional virtuals (vptr in all TUs anyway) or add a
// PARTIAL_DEBUG-only virtual dtor for vptr parity (see dAttParam_c).
class JOREventListener { class JOREventListener {
public: public:
#if DEBUG #if DEBUG
JOREventListener() {} JOREventListener() {}
#if TARGET_PC
virtual void listenPropertyEvent(const JORPropertyEvent*) {}
#else
virtual void listenPropertyEvent(const JORPropertyEvent*) = 0; virtual void listenPropertyEvent(const JORPropertyEvent*) = 0;
#endif #endif
#endif
}; };
class JORReflexible : public JOREventListener { class JORReflexible : public JOREventListener {
@@ -30,7 +41,11 @@ public:
virtual void listenPropertyEvent(const JORPropertyEvent*); virtual void listenPropertyEvent(const JORPropertyEvent*);
virtual void listen(u32, const JOREvent*); virtual void listen(u32, const JOREvent*);
virtual void genObjectInfo(const JORGenEvent*); virtual void genObjectInfo(const JORGenEvent*);
#if TARGET_PC
virtual void genMessage(JORMContext*) {}
#else
virtual void genMessage(JORMContext*) = 0; virtual void genMessage(JORMContext*) = 0;
#endif
virtual void listenNodeEvent(const JORNodeEvent*); virtual void listenNodeEvent(const JORNodeEvent*);
#endif #endif
}; };
@@ -314,7 +314,7 @@ public:
> >
{ {
TIterator_data_(const TFunctionValue_list_parameter& rParent, const f32* value) { TIterator_data_(const TFunctionValue_list_parameter& rParent, const f32* value) {
#if DEBUG #if PARTIAL_DEBUG || DEBUG
pOwn_ = &rParent; pOwn_ = &rParent;
#endif #endif
pf_ = value; pf_ = value;
@@ -372,7 +372,7 @@ public:
return (r1.pf_ - r2.pf_) / suData_size; return (r1.pf_ - r2.pf_) / suData_size;
} }
#if DEBUG #if PARTIAL_DEBUG || DEBUG
/* 0x00 */ const TFunctionValue_list_parameter* pOwn_; /* 0x00 */ const TFunctionValue_list_parameter* pOwn_;
#endif #endif
/* 0x00 */ const f32* pf_; /* 0x00 */ const f32* pf_;
@@ -425,7 +425,7 @@ public:
> >
{ {
TIterator_data_(const TFunctionValue_hermite& rParent, const f32* value) { TIterator_data_(const TFunctionValue_hermite& rParent, const f32* value) {
#if DEBUG #if PARTIAL_DEBUG || DEBUG
pOwn_ = &rParent; pOwn_ = &rParent;
#endif #endif
pf_ = value; pf_ = value;
@@ -491,7 +491,7 @@ public:
return (r1.pf_ - r2.pf_) / r1.uSize_; return (r1.pf_ - r2.pf_) / r1.uSize_;
} }
#if DEBUG #if PARTIAL_DEBUG || DEBUG
/* 0x00 */ const TFunctionValue_hermite* pOwn_; /* 0x00 */ const TFunctionValue_hermite* pOwn_;
/* 0x04 */ const f32* pf_; /* 0x04 */ const f32* pf_;
/* 0x08 */ u32 uSize_; /* 0x08 */ u32 uSize_;
+52 -2
View File
@@ -21,6 +21,7 @@ body {
} }
fps, fps,
pipeline-progress,
toast { toast {
position: absolute; position: absolute;
border: 1dp #92875B; border: 1dp #92875B;
@@ -98,7 +99,7 @@ toast message row.muted {
opacity: 0.5; opacity: 0.5;
} }
toast progress { progress {
height: 4dp; height: 4dp;
position: absolute; position: absolute;
left: 0; left: 0;
@@ -106,10 +107,50 @@ toast progress {
width: 100%; width: 100%;
} }
toast progress fill { progress fill {
background-color: rgba(194, 164, 45, 80%); background-color: rgba(194, 164, 45, 80%);
} }
pipeline-progress {
left: 12dp;
bottom: 12dp;
display: flex;
flex-flow: column;
z-index: 100;
min-width: 260dp;
max-width: 90%;
padding: 10dp 16dp 12dp;
border-radius: 7dp;
overflow: hidden;
filter: opacity(0);
transition: filter 0.2s linear-in-out;
pointer-events: none;
}
pipeline-progress[open] {
filter: opacity(1);
}
pipeline-status {
display: flex;
align-items: center;
gap: 8dp;
font-size: 18dp;
font-weight: normal;
white-space: nowrap;
}
icon.pipeline-spinner {
width: 1.2em;
height: 1.2em;
line-height: 1.2em;
font-size: 1.2em;
color: #C2A42D;
text-align: center;
transform-origin: center;
animation: 1s linear infinite pipeline-spinner-spin;
}
toast.achievement { toast.achievement {
border: 1dp #C2A42D; border: 1dp #C2A42D;
} }
@@ -310,6 +351,15 @@ logo img.outer {
} }
} }
@keyframes pipeline-spinner-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@media (max-height: 640dp) { @media (max-height: 640dp) {
toast { toast {
top: 20dp; top: 20dp;
+1 -1
View File
@@ -22,7 +22,7 @@
#include "os_report.h" #include "os_report.h"
Z2SoundObjBase::Z2SoundObjBase() Z2SoundObjBase::Z2SoundObjBase()
#if DEBUG #if PARTIAL_DEBUG || DEBUG
: JSULink<Z2SoundObjBase>(this) : JSULink<Z2SoundObjBase>(this)
#endif #endif
{ {
+5 -12
View File
@@ -122,18 +122,11 @@ BOOL daAlink_c::setBodyAngleToCamera() {
var_f31 /= dComIfGp_getCameraZoomScale(field_0x317c); var_f31 /= dComIfGp_getCameraZoomScale(field_0x317c);
} }
#if TARGET_PC shape_angle.y = shape_angle.y + (var_f31 * cM_ssin(mStickAngle) IF_DUSK(* (dusk::getSettings().game.invertFirstPersonXAxis ? -1.0f : 1.0f)));
if (dusk::getSettings().game.enableMouseAim && checkAimInputContext()) { sp8 = mBodyAngle.x + (var_f31 * cM_scos(mStickAngle) IF_DUSK(* (dusk::getSettings().game.invertFirstPersonYAxis ? -1.0f : 1.0f)));
sp8 = mBodyAngle.x;
} else
#endif
{
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) { if (checkNotItemSinkLimit() && sp8 > 0 && sp8 > mBodyAngle.x) {
sp8 = mBodyAngle.x; sp8 = mBodyAngle.x;
}
} }
} else { } else {
sp8 = mBodyAngle.x; sp8 = mBodyAngle.x;
@@ -156,7 +149,7 @@ BOOL daAlink_c::setBodyAngleToCamera() {
f32 final_yaw = 0.f; f32 final_yaw = 0.f;
f32 final_pitch = 0.f; f32 final_pitch = 0.f;
if (dusk::getSettings().game.enableMouseAim) { if (dusk::getSettings().game.enableMouseAim) {
dusk::mouse::getAimDeltas(final_yaw, final_pitch); dusk::mouse::get_aim_deltas(final_yaw, final_pitch);
} }
if (dusk::getSettings().game.enableGyroAim) { if (dusk::getSettings().game.enableGyroAim) {
f32 gyro_yaw = 0.f; f32 gyro_yaw = 0.f;
+22 -3
View File
@@ -11,6 +11,7 @@
#include "d/d_com_inf_game.h" #include "d/d_com_inf_game.h"
#if TARGET_PC #if TARGET_PC
#include <aurora/texture.hpp>
#include "dusk/dvd_asset.hpp" #include "dusk/dvd_asset.hpp"
#include "dusk/frame_interpolation.h" #include "dusk/frame_interpolation.h"
@@ -40,6 +41,8 @@ static f32* l_texCoord_get() { alignas(32) static f32 buf[338]; static bool _
//#define l_pos (l_pos_get()) //#define l_pos (l_pos_get())
#define l_normal (l_normal_get()) #define l_normal (l_normal_get())
#define l_texCoord (l_texCoord_get()) #define l_texCoord (l_texCoord_get())
static bool l_Egnd_mantTEX_hasReplacement = false;
#else #else
#include "assets/l_Egnd_mantTEX.h" #include "assets/l_Egnd_mantTEX.h"
@@ -223,6 +226,7 @@ void daMant_packet_c::draw() {
GXInitTexObjCI( GXInitTexObjCI(
&undersideTexObj, l_Egnd_mantTEX_U, 0x80, 0x80, GX_TF_C8, GX_CLAMP, GX_CLAMP, 0, 0); &undersideTexObj, l_Egnd_mantTEX_U, 0x80, 0x80, GX_TF_C8, GX_CLAMP, GX_CLAMP, 0, 0);
GXInitTexObjLOD(&undersideTexObj, GX_LINEAR, GX_LINEAR, 0.0, 0.0, 0.0, 0, 0, GX_ANISO_1); GXInitTexObjLOD(&undersideTexObj, GX_LINEAR, GX_LINEAR, 0.0, 0.0, 0.0, 0, 0, GX_ANISO_1);
l_Egnd_mantTEX_hasReplacement = aurora::texture::has_replacement(&mainTexObj, &tlutObj);
textureObjsInitialized = true; textureObjsInitialized = true;
} }
#else #else
@@ -636,7 +640,11 @@ static int daMant_Execute(mant_class* i_this) {
iVar8 = 0; iVar8 = 0;
if (i_this->field_0x3967 != 0) { if (i_this->field_0x3967 != 0) {
#if TARGET_PC
mant_cut_type = l_Egnd_mantTEX_hasReplacement ? 1 : i_this->field_0x3967;
#else
mant_cut_type = i_this->field_0x3967; mant_cut_type = i_this->field_0x3967;
#endif
if (i_this->field_0x3968 < 15) { if (i_this->field_0x3968 < 15) {
i_this->field_0x3968++; i_this->field_0x3968++;
@@ -648,9 +656,18 @@ static int daMant_Execute(mant_class* i_this) {
iVar8 = 20; iVar8 = 20;
} }
unaff_r29 = cM_rndF(65536.0f); #if TARGET_PC
var_f31 = cM_rndFX(32.0f); if (l_Egnd_mantTEX_hasReplacement) {
var_f30 = cM_rndFX(32.0f); unaff_r29 = i_this->mMantRng.getF(65536.0f);
var_f31 = i_this->mMantRng.getFX(32.0f);
var_f30 = i_this->mMantRng.getFX(32.0f);
} else
#endif
{
unaff_r29 = cM_rndF(65536.0f);
var_f31 = cM_rndFX(32.0f);
var_f30 = cM_rndFX(32.0f);
}
} }
i_this->field_0x3967 = 0; i_this->field_0x3967 = 0;
@@ -760,6 +777,8 @@ static int daMant_Create(fopAc_ac_c* i_this) {
if(textureObjsInitialized) { if(textureObjsInitialized) {
GXInitTlutObjData(&tlutObj, l_Egnd_mantPAL); // make sure the cached textures are updated GXInitTlutObjData(&tlutObj, l_Egnd_mantPAL); // make sure the cached textures are updated
} }
m_this->mMantRng.init(66, 16983, 855);
#endif #endif
lbl_277_bss_0 = 0; lbl_277_bss_0 = 0;
+5 -1
View File
@@ -7602,6 +7602,10 @@ bool dCamera_c::executeDebugFlyCam() {
sFlyCamLastMousePos = mouseValid ? io.MousePos : ImVec2{-1.0f, -1.0f}; sFlyCamLastMousePos = mouseValid ? io.MousePos : ImVec2{-1.0f, -1.0f};
} }
if (dusk::getSettings().game.enableMirrorMode) {
stickX *= -1.0f;
}
f32 verticalDisp = 0.0f; f32 verticalDisp = 0.0f;
if (trigR >= FLYCAM_TRIGGER_DEADZONE) { if (trigR >= FLYCAM_TRIGGER_DEADZONE) {
verticalDisp += trigR; verticalDisp += trigR;
@@ -7712,7 +7716,7 @@ bool dCamera_c::freeCamera() {
f32 yaw_rad = 0.0f; f32 yaw_rad = 0.0f;
f32 pitch_rad = 0.0f; f32 pitch_rad = 0.0f;
dusk::mouse::getCameraDeltas(yaw_rad, pitch_rad); dusk::mouse::get_camera_deltas(yaw_rad, pitch_rad);
if (dusk::getSettings().game.enableMouseCamera && (yaw_rad != 0.0f || pitch_rad != 0.0f) && if (dusk::getSettings().game.enableMouseCamera && (yaw_rad != 0.0f || pitch_rad != 0.0f) &&
!dComIfGp_checkCameraAttentionStatus(dComIfGp_getPlayerCameraID(0), 0x8)) !dComIfGp_checkCameraAttentionStatus(dComIfGp_getPlayerCameraID(0), 0x8))
{ {
+32 -21
View File
@@ -777,6 +777,7 @@ bool dFile_select_c::pointerDataSelect() {
if (!dusk::menu_pointer::hit_pane(mSelFilePanes[i], 8.0f)) { if (!dusk::menu_pointer::hit_pane(mSelFilePanes[i], 8.0f)) {
continue; continue;
} }
dusk::menu_pointer::set_hover_target(pointer_target(s_pointerDataSelectTarget, i));
const bool clicked = dusk::menu_pointer::consume_click(); const bool clicked = dusk::menu_pointer::consume_click();
if (mSelectNum != i) { if (mSelectNum != i) {
mDoAud_seStart(Z2SE_FILE_SELECT_CURSOR, NULL, 0, 0); mDoAud_seStart(Z2SE_FILE_SELECT_CURSOR, NULL, 0, 0);
@@ -805,6 +806,7 @@ bool dFile_select_c::pointerMenuSelect() {
if (!dusk::menu_pointer::hit_pane(m3mSelPane[i], 8.0f)) { if (!dusk::menu_pointer::hit_pane(m3mSelPane[i], 8.0f)) {
continue; continue;
} }
dusk::menu_pointer::set_hover_target(pointer_target(s_pointerMenuSelectTarget, i));
const bool clicked = dusk::menu_pointer::consume_click(); const bool clicked = dusk::menu_pointer::consume_click();
if (!mIsDataNew[mSelectNum] && mSelectMenuNum != i) { if (!mIsDataNew[mSelectNum] && mSelectMenuNum != i) {
mDoAud_seStart(Z2SE_SY_MENU_CURSOR_COMMON, NULL, 0, 0); mDoAud_seStart(Z2SE_SY_MENU_CURSOR_COMMON, NULL, 0, 0);
@@ -833,6 +835,7 @@ bool dFile_select_c::pointerCopyDataToSelect() {
if (!dusk::menu_pointer::hit_pane(mCpSelPane[i], 8.0f)) { if (!dusk::menu_pointer::hit_pane(mCpSelPane[i], 8.0f)) {
continue; continue;
} }
dusk::menu_pointer::set_hover_target(pointer_target(s_pointerCopySelectTarget, i));
const bool clicked = dusk::menu_pointer::consume_click(); const bool clicked = dusk::menu_pointer::consume_click();
if (field_0x026b != i) { if (field_0x026b != i) {
mDoAud_seStart(Z2SE_FILE_SELECT_CURSOR, NULL, 0, 0); mDoAud_seStart(Z2SE_FILE_SELECT_CURSOR, NULL, 0, 0);
@@ -861,6 +864,7 @@ bool dFile_select_c::pointerYesNoSelect(bool errorSelect) {
if (!dusk::menu_pointer::hit_pane(mYnSelPane[i], 8.0f)) { if (!dusk::menu_pointer::hit_pane(mYnSelPane[i], 8.0f)) {
continue; continue;
} }
dusk::menu_pointer::set_hover_target(pointer_target(s_pointerYesNoSelectTarget, i));
const bool clicked = const bool clicked =
(!errorSelect || field_0x0268 == i) && dusk::menu_pointer::consume_click(); (!errorSelect || field_0x0268 == i) && dusk::menu_pointer::consume_click();
if (field_0x0268 != i) { if (field_0x0268 != i) {
@@ -1103,12 +1107,13 @@ void dFile_select_c::dataSelectAnmSet() {
void dFile_select_c::dataSelectMoveAnime() { void dFile_select_c::dataSelectMoveAnime() {
#if TARGET_PC #if TARGET_PC
dusk::menu_pointer::begin_context(dusk::menu_pointer::Context::FileSelect); dusk::menu_pointer::begin_context(dusk::menu_pointer::Context::FileSelect);
if (mSelectNum != 0xFF && dusk::menu_pointer::hit_pane(mSelFilePanes[mSelectNum], 8.0f) && if (mSelectNum != 0xFF && dusk::menu_pointer::hit_pane(mSelFilePanes[mSelectNum], 8.0f)) {
dusk::menu_pointer::consume_click()) dusk::menu_pointer::set_hover_target(pointer_target(s_pointerDataSelectTarget, mSelectNum));
{ if (dusk::menu_pointer::consume_click()) {
dusk::menu_pointer::defer_activation( dusk::menu_pointer::defer_activation(
dusk::menu_pointer::Context::FileSelect, dusk::menu_pointer::Context::FileSelect,
pointer_target(s_pointerDataSelectTarget, mSelectNum)); pointer_target(s_pointerDataSelectTarget, mSelectNum));
}
} }
#endif #endif
bool iVar7 = true; bool iVar7 = true;
@@ -1494,12 +1499,14 @@ void dFile_select_c::menuSelectMoveAnm() {
#if TARGET_PC #if TARGET_PC
dusk::menu_pointer::begin_context(dusk::menu_pointer::Context::FileSelect); dusk::menu_pointer::begin_context(dusk::menu_pointer::Context::FileSelect);
if (mSelectMenuNum != 0xFF && if (mSelectMenuNum != 0xFF &&
dusk::menu_pointer::hit_pane(m3mSelPane[mSelectMenuNum], 8.0f) && dusk::menu_pointer::hit_pane(m3mSelPane[mSelectMenuNum], 8.0f))
dusk::menu_pointer::consume_click())
{ {
dusk::menu_pointer::defer_activation( dusk::menu_pointer::set_hover_target(pointer_target(s_pointerMenuSelectTarget, mSelectMenuNum));
dusk::menu_pointer::Context::FileSelect, if (dusk::menu_pointer::consume_click()) {
pointer_target(s_pointerMenuSelectTarget, mSelectMenuNum)); dusk::menu_pointer::defer_activation(
dusk::menu_pointer::Context::FileSelect,
pointer_target(s_pointerMenuSelectTarget, mSelectMenuNum));
}
} }
#endif #endif
bool tmp1 = true; bool tmp1 = true;
@@ -1997,12 +2004,14 @@ void dFile_select_c::copyDataToSelectMoveAnm() {
#if TARGET_PC #if TARGET_PC
dusk::menu_pointer::begin_context(dusk::menu_pointer::Context::FileSelect); dusk::menu_pointer::begin_context(dusk::menu_pointer::Context::FileSelect);
if (field_0x026b != 0xFF && if (field_0x026b != 0xFF &&
dusk::menu_pointer::hit_pane(mCpSelPane[field_0x026b], 8.0f) && dusk::menu_pointer::hit_pane(mCpSelPane[field_0x026b], 8.0f))
dusk::menu_pointer::consume_click())
{ {
dusk::menu_pointer::defer_activation( dusk::menu_pointer::set_hover_target(pointer_target(s_pointerCopySelectTarget, field_0x026b));
dusk::menu_pointer::Context::FileSelect, if (dusk::menu_pointer::consume_click()) {
pointer_target(s_pointerCopySelectTarget, field_0x026b)); dusk::menu_pointer::defer_activation(
dusk::menu_pointer::Context::FileSelect,
pointer_target(s_pointerCopySelectTarget, field_0x026b));
}
} }
#endif #endif
bool iVar7 = true; bool iVar7 = true;
@@ -2522,12 +2531,14 @@ void dFile_select_c::yesNoCursorMoveAnm() {
#if TARGET_PC #if TARGET_PC
dusk::menu_pointer::begin_context(dusk::menu_pointer::Context::FileSelect); dusk::menu_pointer::begin_context(dusk::menu_pointer::Context::FileSelect);
if (field_0x0268 != 0xFF && if (field_0x0268 != 0xFF &&
dusk::menu_pointer::hit_pane(mYnSelPane[field_0x0268], 8.0f) && dusk::menu_pointer::hit_pane(mYnSelPane[field_0x0268], 8.0f))
dusk::menu_pointer::consume_click())
{ {
dusk::menu_pointer::defer_activation( dusk::menu_pointer::set_hover_target(pointer_target(s_pointerYesNoSelectTarget, field_0x0268));
dusk::menu_pointer::Context::FileSelect, if (dusk::menu_pointer::consume_click()) {
pointer_target(s_pointerYesNoSelectTarget, field_0x0268)); dusk::menu_pointer::defer_activation(
dusk::menu_pointer::Context::FileSelect,
pointer_target(s_pointerYesNoSelectTarget, field_0x0268));
}
} }
#endif #endif
bool isYnSelMove = yesnoSelectMoveAnm(); bool isYnSelMove = yesnoSelectMoveAnm();
+1 -1
View File
@@ -118,7 +118,7 @@ static int dKyeff_Create(kankyo_class* i_this) {
if (strcmp(dComIfGp_getStartStageName(), "Name") == 0) { if (strcmp(dComIfGp_getStartStageName(), "Name") == 0) {
camera_process_class* camera = dComIfGp_getCamera(0); camera_process_class* camera = dComIfGp_getCamera(0);
OSTime time = OSGetTime(); OSTime time = DUSK_IF_ELSE(OSGetSystemTime(), OSGetTime());
OSTicksToCalendarTime(time, &calendar); OSTicksToCalendarTime(time, &calendar);
g_env_light.global_wind_influence.vec.x = 1.0f; g_env_light.global_wind_influence.vec.x = 1.0f;
+6 -1
View File
@@ -1640,7 +1640,7 @@ void dMap_c::_move(f32 i_centerX, f32 i_centerZ, int i_roomNo, f32 param_3) {
mCenterX += IF_DUSK(dusk::getSettings().game.enableMirrorMode ? -mPackX :) mPackX; mCenterX += IF_DUSK(dusk::getSettings().game.enableMirrorMode ? -mPackX :) mPackX;
mCenterZ -= mPackZ; mCenterZ -= mPackZ;
mCenterX += field_0x64; mCenterX += IF_DUSK(dusk::getSettings().game.enableMirrorMode ? -field_0x64 : ) field_0x64;
mCenterZ += mPackPlusZ; mCenterZ += mPackPlusZ;
} }
@@ -1649,6 +1649,8 @@ void dMap_c::_move(f32 i_centerX, f32 i_centerZ, int i_roomNo, f32 param_3) {
{ {
#if DEBUG #if DEBUG
field_0x64 = 33830.0f; field_0x64 = 33830.0f;
#elif TARGET_PC
field_0x64 = dusk::getSettings().game.enableMirrorMode ? 33830.0f : 0.0f;
#else #else
field_0x64 = 0.0f; field_0x64 = 0.0f;
#endif #endif
@@ -1657,6 +1659,9 @@ void dMap_c::_move(f32 i_centerX, f32 i_centerZ, int i_roomNo, f32 param_3) {
f32 temp = (field_0x58 * (f32)(field_0x74 + 4)) * 0.5f; f32 temp = (field_0x58 * (f32)(field_0x74 + 4)) * 0.5f;
#if DEBUG #if DEBUG
mRightEdgePlus = -(((dMpath_c::getMinZ() - (-127103.67f)) - temp) / field_0x58); mRightEdgePlus = -(((dMpath_c::getMinZ() - (-127103.67f)) - temp) / field_0x58);
#elif TARGET_PC
mRightEdgePlus = dusk::getSettings().game.enableMirrorMode ? -(((dMpath_c::getMinX() - (-127103.67f)) - temp) / field_0x58) : 0.0f;
#else #else
mRightEdgePlus = 0.0f; mRightEdgePlus = 0.0f;
#endif #endif
+1
View File
@@ -1960,6 +1960,7 @@ bool dMenu_Collect2D_c::pointerWait() {
if (getItemTag(x, y, true) == 0 || !dusk::menu_pointer::hit_pane(mpSelPm[x][y], 8.0f)) { if (getItemTag(x, y, true) == 0 || !dusk::menu_pointer::hit_pane(mpSelPm[x][y], 8.0f)) {
continue; continue;
} }
dusk::menu_pointer::set_hover_target(static_cast<dusk::menu_pointer::TargetId>(x + y * 7));
if (mCursorX != x || mCursorY != y) { if (mCursorX != x || mCursorY != y) {
mDoAud_seStart(Z2SE_SY_MENU_CURSOR_COMMON, NULL, 0, 0); mDoAud_seStart(Z2SE_SY_MENU_CURSOR_COMMON, NULL, 0, 0);
mCursorX = x; mCursorX = x;
+24 -1
View File
@@ -914,6 +914,20 @@ void dMenu_DmapBg_c::dMapBgWide() {
void dMenu_DmapBg_c::draw() { void dMenu_DmapBg_c::draw() {
#if TARGET_PC #if TARGET_PC
dMapBgWide(); dMapBgWide();
static bool prevMirror = false; // default state of panes is not mirrored
if(prevMirror != dusk::getSettings().game.enableMirrorMode) {
if(dusk::getSettings().game.enableMirrorMode) {
static_cast<J2DPicture*>(mFloorScreen->search(MULTI_CHAR('rink')))->setMirror(J2DMirror_X);
static_cast<J2DPicture*>(mBaseScreen->search(MULTI_CHAR('map000')))->setMirror(J2DMirror_X);
}
else {
static_cast<J2DPicture*>(mFloorScreen->search(MULTI_CHAR('rink')))->setMirror(MIRROR0);
static_cast<J2DPicture*>(mBaseScreen->search(MULTI_CHAR('map000')))->setMirror(MIRROR0);
}
prevMirror = dusk::getSettings().game.enableMirrorMode;
}
#endif #endif
u32 scissor_left; u32 scissor_left;
@@ -960,6 +974,15 @@ void dMenu_DmapBg_c::draw() {
mpBackTexture->setAlpha(dVar17 * (field_0xdbc * field_0xd9c)); mpBackTexture->setAlpha(dVar17 * (field_0xdbc * field_0xd9c));
f32 local_28c = mpBackTexture->getBounds().i.x; f32 local_28c = mpBackTexture->getBounds().i.x;
#if TARGET_PC
if(dusk::getSettings().game.enableMirrorMode) {
CPaneMgr mgr;
Vec local_94 = mgr.getGlobalVtxCenter(mMapPane, true, 0);
local_28c = (local_94.x * 2.0f) - (local_28c + 0.5f * mpBackTexture->getWidth()) - 0.5f * mpBackTexture->getWidth();
}
#endif
mpBackTexture->setBlackWhite(color_black, color_white); mpBackTexture->setBlackWhite(color_black, color_white);
mpBackTexture->draw(local_28c, field_0xd94 + mpBackTexture->getBounds().i.y, mpBackTexture->getWidth(), mpBackTexture->draw(local_28c, field_0xd94 + mpBackTexture->getBounds().i.y, mpBackTexture->getWidth(),
mpBackTexture->getHeight(), mpBackTexture->getHeight(),
@@ -1981,7 +2004,7 @@ void dMenu_Dmap_c::mapControl() {
f32 temp_f28 = (var_f29 / 100.0f) * var_f31; f32 temp_f28 = (var_f29 / 100.0f) * var_f31;
f32 sp18 = temp_f28 * cM_ssin(stick_angle); f32 sp18 = temp_f28 * cM_ssin(stick_angle);
f32 sp14 = temp_f28 * cM_scos(stick_angle); f32 sp14 = temp_f28 * cM_scos(stick_angle);
mMapCtrl->setPlusZoomCenterX(sp18); mMapCtrl->setPlusZoomCenterX(IF_DUSK(dusk::getSettings().game.enableMirrorMode ? -sp18 :) sp18);
mMapCtrl->setPlusZoomCenterZ(sp14); mMapCtrl->setPlusZoomCenterZ(sp14);
} }
+1 -20
View File
@@ -359,14 +359,7 @@ f32 dMenu_StageMapCtrl_c::getPixelStageSizeZ() const {
f32 dMenu_StageMapCtrl_c::getPixelCenterX() const { f32 dMenu_StageMapCtrl_c::getPixelCenterX() const {
f32 var_f31 = dMpath_c::getCenterX(); f32 var_f31 = dMpath_c::getCenterX();
#if TARGET_PC
if (dusk::getSettings().game.enableMirrorMode) {
return (1.0f / field_0xbc) * (field_0x9c + var_f31);
}
else return (1.0f / field_0xbc) * (field_0x9c - var_f31);
#else
return (1.0f / field_0xbc) * (field_0x9c - var_f31); return (1.0f / field_0xbc) * (field_0x9c - var_f31);
#endif
} }
f32 dMenu_StageMapCtrl_c::getPixelCenterZ() const { f32 dMenu_StageMapCtrl_c::getPixelCenterZ() const {
@@ -425,18 +418,7 @@ inline static f32 rightModeCnvPos(f32 param_0) {
void dMenu_StageMapCtrl_c::cnvPosTo2Dpos(f32 param_0, f32 param_1, f32* param_2, void dMenu_StageMapCtrl_c::cnvPosTo2Dpos(f32 param_0, f32 param_1, f32* param_2,
f32* param_3) const { f32* param_3) const {
if (param_2 != NULL) { if (param_2 != NULL) {
#if TARGET_PC
if (dusk::getSettings().game.enableMirrorMode) {
*param_2 =
(0.5f * field_0x94) + rightModeCnvPos((1.0f / field_0xbc) * (field_0x9c + param_0));
} else {
*param_2 =
(0.5f * field_0x94) + rightModeCnvPos((1.0f / field_0xbc) * (param_0 - field_0x9c));
}
#else
*param_2 = (0.5f * field_0x94) + rightModeCnvPos((1.0f / field_0xbc) * (param_0 - field_0x9c)); *param_2 = (0.5f * field_0x94) + rightModeCnvPos((1.0f / field_0xbc) * (param_0 - field_0x9c));
#endif
} }
if (param_3 != NULL) { if (param_3 != NULL) {
@@ -933,8 +915,7 @@ void dMenu_StageMapCtrl_c::move() {
void dMenu_DmapMapCtrl_c::draw() { void dMenu_DmapMapCtrl_c::draw() {
if (field_0xef != 0) { if (field_0xef != 0) {
setPos(field_0xeb, field_0xec, setPos(field_0xeb, field_0xec, field_0x9c,
IF_DUSK(dusk::getSettings().game.enableMirrorMode ? -field_0x9c :) field_0x9c,
field_0xa0, field_0xbc, true, field_0xd8); field_0xa0, field_0xbc, true, field_0xd8);
} }
} }
+10 -15
View File
@@ -931,17 +931,8 @@ void dMenu_Fmap_c::region_map_proc() {
mpDraw2DBack->regionMapMove(mpStick); mpDraw2DBack->regionMapMove(mpStick);
int stage_no, room_no; int stage_no, room_no;
#if TARGET_PC
f32 arrow_pos_x = mpDraw2DBack->getArrowPos2DX();
if (dusk::getSettings().game.enableMirrorMode) {
arrow_pos_x = mpDraw2DBack->getMirrorPosX(arrow_pos_x, 0.0f);
}
f32 pos_x = arrow_pos_x - mDoGph_gInf_c::getMinXF() - mDoGph_gInf_c::getWidthF() * 0.5f;
#else
f32 pos_x = mpDraw2DBack->getArrowPos2DX() - mDoGph_gInf_c::getMinXF() f32 pos_x = mpDraw2DBack->getArrowPos2DX() - mDoGph_gInf_c::getMinXF()
- mDoGph_gInf_c::getWidthF() * 0.5f; - mDoGph_gInf_c::getWidthF() * 0.5f;
#endif
f32 pos_y = mpDraw2DBack->getArrowPos2DY() - mDoGph_gInf_c::getHeightF() * 0.5f; f32 pos_y = mpDraw2DBack->getArrowPos2DY() - mDoGph_gInf_c::getHeightF() * 0.5f;
mpMenuFmapMap->getPointStagePathInnerNo(getNowFmapRegionData(), pos_x, pos_y, mpMenuFmapMap->getPointStagePathInnerNo(getNowFmapRegionData(), pos_x, pos_y,
@@ -2486,12 +2477,6 @@ void dMenu_Fmap_c::portalWarpMapMove(STControl* i_stick) {
f32 arrow_y = mpDraw2DBack->getArrowPos2DY(); f32 arrow_y = mpDraw2DBack->getArrowPos2DY();
u8 uVar6 = 0xff; u8 uVar6 = 0xff;
#if TARGET_PC
if (dusk::getSettings().game.enableMirrorMode) {
arrow_x = mpDraw2DBack->getMirrorPosX(arrow_x, 0.0f);
}
#endif
for (int i = 0; i < portal_dat->mCount; i++) { for (int i = 0; i < portal_dat->mCount; i++) {
if (portals[i].mRegionNo == mpDraw2DBack->getRegionCursor() + 1 if (portals[i].mRegionNo == mpDraw2DBack->getRegionCursor() + 1
@@ -2561,6 +2546,11 @@ void dMenu_Fmap_c::drawIcon(f32 param_0, bool param_1) {
if (mProcess == PROC_PORTAL_DEMO1) { if (mProcess == PROC_PORTAL_DEMO1) {
is_portal_demo1 = 1; is_portal_demo1 = 1;
} }
#if TARGET_PC
if(dusk::getSettings().game.enableMirrorMode) {
angle = 0x10000 - angle;
}
#endif
mpDraw2DBack->setIcon2DPos(0x11, stage_name, pos.x, pos.z, cM_sht2d(angle), mpDraw2DBack->setIcon2DPos(0x11, stage_name, pos.x, pos.z, cM_sht2d(angle),
is_portal_demo1, param_1); is_portal_demo1, param_1);
@@ -2649,6 +2639,11 @@ void dMenu_Fmap_c::drawPlayEnterIcon() {
angle = dComIfGs_getPlayerFieldLastStayAngleY(); angle = dComIfGs_getPlayerFieldLastStayAngleY();
SAFE_STRCPY(stage_name, dComIfGs_getPlayerFieldLastStayName()); SAFE_STRCPY(stage_name, dComIfGs_getPlayerFieldLastStayName());
} }
#if TARGET_PC
if(dusk::getSettings().game.enableMirrorMode) {
angle = 0x10000 - angle;
}
#endif
mpDraw2DBack->setIcon2DPos(0x15, stage_name, pos.x, pos.z, cM_sht2d(angle), 0, false); mpDraw2DBack->setIcon2DPos(0x15, stage_name, pos.x, pos.z, cM_sht2d(angle), 0, false);
} }
} }
+72 -18
View File
@@ -426,7 +426,15 @@ void dMenu_Fmap2DBack_c::draw() {
} }
mpPointParent->setAlphaRate(mArrowAlpha * mSpotTextureFadeAlpha); mpPointParent->setAlphaRate(mArrowAlpha * mSpotTextureFadeAlpha);
mpPointParent->translate(mArrowPos2DX + mTransX, mArrowPos2DY + mTransZ);
f32 drawX = mArrowPos2DX + mTransX;
#ifdef TARGET_PC
if (dusk::getSettings().game.enableMirrorMode) {
drawX = getMirrorPosX(drawX, 0.0f);
}
#endif
mpPointParent->translate(drawX, mArrowPos2DY + mTransZ);
mpPointScreen->draw(0.0f, 0.0f, grafPort); mpPointScreen->draw(0.0f, 0.0f, grafPort);
} }
@@ -1018,6 +1026,11 @@ void dMenu_Fmap2DBack_c::allmap_move2(STControl* param_0) {
f32 delta_y = speed * cM_ssin(angle); f32 delta_y = speed * cM_ssin(angle);
f32 delta_x = speed * cM_scos(angle); f32 delta_x = speed * cM_scos(angle);
#ifdef TARGET_PC
if (dusk::getSettings().game.enableMirrorMode) {
delta_y = -delta_y;
}
#endif
control_xpos = control_xpos + delta_y; control_xpos = control_xpos + delta_y;
control_ypos = control_ypos + delta_x; control_ypos = control_ypos + delta_x;
} }
@@ -1046,11 +1059,6 @@ void dMenu_Fmap2DBack_c::allmap_move2(STControl* param_0) {
calcAllMapPos2D((mArrowPos3DX + control_xpos) - mStageTransX, calcAllMapPos2D((mArrowPos3DX + control_xpos) - mStageTransX,
(mArrowPos3DZ + control_ypos) - mStageTransZ, &sp14, &sp10); (mArrowPos3DZ + control_ypos) - mStageTransZ, &sp14, &sp10);
#if TARGET_PC
if (dusk::getSettings().game.enableMirrorMode) {
sp14 = getMirrorPosX(sp14, 0.0f);
}
#endif
mSelectRegion = 0xff; mSelectRegion = 0xff;
for (int i = 7; i >= 0; i--) { for (int i = 7; i >= 0; i--) {
@@ -1465,6 +1473,12 @@ void dMenu_Fmap2DBack_c::worldGridDraw() {
f32 dVar8 = -mStageTransZ; f32 dVar8 = -mStageTransZ;
calcAllMapPos2D(dVar9, dVar8, &local_74, &local_78); calcAllMapPos2D(dVar9, dVar8, &local_74, &local_78);
#if TARGET_PC
if(dusk::getSettings().game.enableMirrorMode) {
local_74 = getMirrorPosX(local_74, 0.0f);
}
#endif
J2DDrawLine(local_74, mDoGph_gInf_c::getMinYF(), local_74, J2DDrawLine(local_74, mDoGph_gInf_c::getMinYF(), local_74,
mDoGph_gInf_c::getMinYF() + mDoGph_gInf_c::getHeightF(), mDoGph_gInf_c::getMinYF() + mDoGph_gInf_c::getHeightF(),
JUtility::TColor(255, 255, 255, 255), 6); JUtility::TColor(255, 255, 255, 255), 6);
@@ -1473,6 +1487,11 @@ void dMenu_Fmap2DBack_c::worldGridDraw() {
while (true) { while (true) {
calcAllMapPos2D(xPos, dVar8, &local_74, &local_78); calcAllMapPos2D(xPos, dVar8, &local_74, &local_78);
if (local_74 >= getMapScissorAreaLX()) { if (local_74 >= getMapScissorAreaLX()) {
#if TARGET_PC
if(dusk::getSettings().game.enableMirrorMode) {
local_74 = getMirrorPosX(local_74, 0.0f);
}
#endif
J2DDrawLine(local_74, mDoGph_gInf_c::getMinYF(), local_74, J2DDrawLine(local_74, mDoGph_gInf_c::getMinYF(), local_74,
mDoGph_gInf_c::getMinYF() + mDoGph_gInf_c::getHeightF(), mDoGph_gInf_c::getMinYF() + mDoGph_gInf_c::getHeightF(),
JUtility::TColor(255, 255, 255, 255), 6); JUtility::TColor(255, 255, 255, 255), 6);
@@ -1534,6 +1553,12 @@ void dMenu_Fmap2DBack_c::regionGridDraw() {
f32 dVar8 = mRegionOriginZ[mRegionCursor] - mStageTransZ; f32 dVar8 = mRegionOriginZ[mRegionCursor] - mStageTransZ;
calcAllMapPos2D(dVar9, dVar8, &local_74, &local_78); calcAllMapPos2D(dVar9, dVar8, &local_74, &local_78);
#if TARGET_PC
if(dusk::getSettings().game.enableMirrorMode) {
local_74 = getMirrorPosX(local_74, 0.0f);
}
#endif
J2DDrawLine(local_74, mDoGph_gInf_c::getMinYF(), local_74, J2DDrawLine(local_74, mDoGph_gInf_c::getMinYF(), local_74,
mDoGph_gInf_c::getMinYF() + mDoGph_gInf_c::getHeightF(), mDoGph_gInf_c::getMinYF() + mDoGph_gInf_c::getHeightF(),
JUtility::TColor(180, 0, 0, 255), 6); JUtility::TColor(180, 0, 0, 255), 6);
@@ -1542,6 +1567,12 @@ void dMenu_Fmap2DBack_c::regionGridDraw() {
while (true) { while (true) {
calcAllMapPos2D(xPos, dVar8, &local_74, &local_78); calcAllMapPos2D(xPos, dVar8, &local_74, &local_78);
if (local_74 >= getMapScissorAreaLX()) { if (local_74 >= getMapScissorAreaLX()) {
#if TARGET_PC
if(dusk::getSettings().game.enableMirrorMode) {
local_74 = getMirrorPosX(local_74, 0.0f);
}
#endif
J2DDrawLine(local_74, mDoGph_gInf_c::getMinYF(), local_74, J2DDrawLine(local_74, mDoGph_gInf_c::getMinYF(), local_74,
mDoGph_gInf_c::getMinYF() + mDoGph_gInf_c::getHeightF(), mDoGph_gInf_c::getMinYF() + mDoGph_gInf_c::getHeightF(),
JUtility::TColor(180, 0, 0, 255), 6); JUtility::TColor(180, 0, 0, 255), 6);
@@ -1604,6 +1635,12 @@ void dMenu_Fmap2DBack_c::worldOriginDraw() {
f32 local_44, local_48; f32 local_44, local_48;
calcAllMapPos2D(-mStageTransX, -mStageTransZ, &local_44, &local_48); calcAllMapPos2D(-mStageTransX, -mStageTransZ, &local_44, &local_48);
#if TARGET_PC
if(dusk::getSettings().game.enableMirrorMode) {
local_44 = getMirrorPosX(local_44, 0.0f);
}
#endif
J2DDrawLine(mDoGph_gInf_c::getMinXF(), local_48 - local_44 + mDoGph_gInf_c::getMinXF(), J2DDrawLine(mDoGph_gInf_c::getMinXF(), local_48 - local_44 + mDoGph_gInf_c::getMinXF(),
mDoGph_gInf_c::getMinXF() + mDoGph_gInf_c::getWidthF(), mDoGph_gInf_c::getMinXF() + mDoGph_gInf_c::getWidthF(),
local_48 - local_44 + (mDoGph_gInf_c::getMinXF() + mDoGph_gInf_c::getWidthF()), local_48 - local_44 + (mDoGph_gInf_c::getMinXF() + mDoGph_gInf_c::getWidthF()),
@@ -1638,6 +1675,13 @@ void dMenu_Fmap2DBack_c::scrollAreaDraw() {
calcAllMapPos2D(x_min - mStageTransX, z_min - mStageTransZ, &local_4c, &local_50); calcAllMapPos2D(x_min - mStageTransX, z_min - mStageTransZ, &local_4c, &local_50);
calcAllMapPos2D(x_max - mStageTransX, z_max - mStageTransZ, &local_54, &local_58); calcAllMapPos2D(x_max - mStageTransX, z_max - mStageTransZ, &local_54, &local_58);
#if TARGET_PC
if(dusk::getSettings().game.enableMirrorMode) {
local_4c = getMirrorPosX(local_4c, 0.0f);
local_54 = getMirrorPosX(local_54, 0.0f);
}
#endif
J2DDrawLine(local_4c, local_50, local_4c, local_58, J2DDrawLine(local_4c, local_50, local_4c, local_58,
JUtility::TColor(255, 255, 255, 255), 6); JUtility::TColor(255, 255, 255, 255), 6);
J2DDrawLine(local_54, local_50, local_54, local_58, J2DDrawLine(local_54, local_50, local_54, local_58,
@@ -1658,6 +1702,11 @@ void dMenu_Fmap2DBack_c::regionOriginDraw() {
f32 center_x, center_y; f32 center_x, center_y;
calcAllMapPos2D(mRegionOriginX[i] - mStageTransX, mRegionOriginZ[i] - mStageTransZ, calcAllMapPos2D(mRegionOriginX[i] - mStageTransX, mRegionOriginZ[i] - mStageTransZ,
&center_x, &center_y); &center_x, &center_y);
#if TARGET_PC
if(dusk::getSettings().game.enableMirrorMode) {
center_x = getMirrorPosX(center_x - 3.0f, 3.0f);
}
#endif
J2DFillBox(center_x - 3.0f, center_y - 3.0f, 6.0f, 6.0f, JUtility::TColor(255, 0, 0, 255)); J2DFillBox(center_x - 3.0f, center_y - 3.0f, 6.0f, 6.0f, JUtility::TColor(255, 0, 0, 255));
} }
} }
@@ -1675,6 +1724,11 @@ void dMenu_Fmap2DBack_c::stageOriginDraw() {
f32 v1 = mRegionOriginX[mRegionCursor] + stage_data[i].mOffsetX - mStageTransX; f32 v1 = mRegionOriginX[mRegionCursor] + stage_data[i].mOffsetX - mStageTransX;
f32 v2 = mRegionOriginZ[mRegionCursor] + stage_data[i].mOffsetZ - mStageTransZ; f32 v2 = mRegionOriginZ[mRegionCursor] + stage_data[i].mOffsetZ - mStageTransZ;
calcAllMapPos2D(v1, v2, &center_x, &center_y); calcAllMapPos2D(v1, v2, &center_x, &center_y);
#if TARGET_PC
if(dusk::getSettings().game.enableMirrorMode) {
center_x = getMirrorPosX(center_x - 3.0f, 3.0f);
}
#endif
J2DFillBox(center_x - 3.0f, center_y - 3.0f, 6.0f, 6.0f, J2DFillBox(center_x - 3.0f, center_y - 3.0f, 6.0f, 6.0f,
JUtility::TColor(0, 0, 255, 255)); JUtility::TColor(0, 0, 255, 255));
} }
@@ -1921,7 +1975,7 @@ void dMenu_Fmap2DBack_c::regionMapMove(STControl* i_stick) {
f32 speed = base_speed / 100.0f * local_78; f32 speed = base_speed / 100.0f * local_78;
f32 speed_y = speed * cM_ssin(angle); f32 speed_y = speed * cM_ssin(angle);
f32 speed_x = speed * cM_scos(angle); f32 speed_x = speed * cM_scos(angle);
control_xpos += speed_y; control_xpos += IF_DUSK(dusk::getSettings().game.enableMirrorMode ? -speed_y :) speed_y;
control_ypos += speed_x; control_ypos += speed_x;
} }
} }
@@ -1946,11 +2000,6 @@ void dMenu_Fmap2DBack_c::regionMapMove(STControl* i_stick) {
calcAllMapPos2D(mArrowPos3DX + control_xpos - mStageTransX, calcAllMapPos2D(mArrowPos3DX + control_xpos - mStageTransX,
mArrowPos3DZ + control_ypos - mStageTransZ, &pos_x, &pos_y); mArrowPos3DZ + control_ypos - mStageTransZ, &pos_x, &pos_y);
#if TARGET_PC
if (dusk::getSettings().game.enableMirrorMode) {
pos_x = getMirrorPosX(pos_x, 0.0f);
}
#endif
mSelectRegion = 0xff; mSelectRegion = 0xff;
int region = mRegionCursor; int region = mRegionCursor;
@@ -1982,11 +2031,6 @@ void dMenu_Fmap2DBack_c::stageMapMove(STControl* i_stick, u8 param_1, bool param
if (stick_value >= slow_bound && param_2 && field_0x1238 != 2) { if (stick_value >= slow_bound && param_2 && field_0x1238 != 2) {
bVar6 = true; bVar6 = true;
s16 angle = i_stick->getAngleStick(); s16 angle = i_stick->getAngleStick();
#if TARGET_PC
if (dusk::getSettings().game.enableMirrorMode) {
angle = -angle;
}
#endif
f32 local_68 = mTexMaxX - mTexMinX; f32 local_68 = mTexMaxX - mTexMinX;
f32 spot_zoom = getSpotMapZoomRate(); f32 spot_zoom = getSpotMapZoomRate();
f32 region_zoom = getRegionMapZoomRate(mRegionCursor); f32 region_zoom = getRegionMapZoomRate(mRegionCursor);
@@ -2001,7 +2045,7 @@ void dMenu_Fmap2DBack_c::stageMapMove(STControl* i_stick, u8 param_1, bool param
f32 speed = base_speed / 100.0f * local_78; f32 speed = base_speed / 100.0f * local_78;
f32 speed_x = speed * cM_ssin(angle); f32 speed_x = speed * cM_ssin(angle);
f32 speed_z = speed * cM_scos(angle); f32 speed_z = speed * cM_scos(angle);
mStageTransX += speed_x; mStageTransX += IF_DUSK(dusk::getSettings().game.enableMirrorMode ? -speed_x :) speed_x;
mStageTransZ += speed_z; mStageTransZ += speed_z;
} else if (!param_2) { } else if (!param_2) {
return; return;
@@ -2095,6 +2139,11 @@ void dMenu_Fmap2DBack_c::drawDebugStageArea() {
if (stage_no >= 0) { if (stage_no >= 0) {
f32 v = i + mDoGph_gInf_c::getMinXF(); f32 v = i + mDoGph_gInf_c::getMinXF();
f32 v2 = j; f32 v2 = j;
#if TARGET_PC
if(dusk::getSettings().game.enableMirrorMode) {
v = getMirrorPosX(v - 3.0f, 3.0f);
}
#endif
J2DFillBox(v - 3.0f, v2 - 3.0f, 6.0f, 6.0f, colors[stage_no % 6]); J2DFillBox(v - 3.0f, v2 - 3.0f, 6.0f, 6.0f, colors[stage_no % 6]);
} }
} }
@@ -2130,6 +2179,11 @@ void dMenu_Fmap2DBack_c::drawDebugRegionArea() {
mRegionMapSizeX[region] * mZoom, mRegionMapSizeY[region] * mZoom, mRegionMapSizeX[region] * mZoom, mRegionMapSizeY[region] * mZoom,
mpAreaTex[region]->getTexture(0)->getTexInfo()); mpAreaTex[region]->getTexture(0)->getTexInfo());
if (u) { if (u) {
#if TARGET_PC
if(dusk::getSettings().game.enableMirrorMode) {
pos_x = getMirrorPosX(pos_x - 3.0f, 3.0f);
}
#endif
J2DFillBox(pos_x - 3.0f, pos_y - 3.0f, 6.0f, 6.0f, colors[region]); J2DFillBox(pos_x - 3.0f, pos_y - 3.0f, 6.0f, 6.0f, colors[region]);
break; break;
} }
+1
View File
@@ -320,6 +320,7 @@ bool dMenu_Insect_c::pointerWait() {
if (!isGetInsect(x, y) || !dusk::menu_pointer::hit_pane(mpINSParent[index], 8.0f)) { if (!isGetInsect(x, y) || !dusk::menu_pointer::hit_pane(mpINSParent[index], 8.0f)) {
continue; continue;
} }
dusk::menu_pointer::set_hover_target(index);
if (field_0xf4 != x || field_0xf5 != y) { if (field_0xf4 != x || field_0xf5 != y) {
field_0xf4 = x; field_0xf4 = x;
+1
View File
@@ -482,6 +482,7 @@ bool dMenu_Letter_c::pointerWait() {
if (!dusk::menu_pointer::hit_pane(mpLetterParent[i], 8.0f)) { if (!dusk::menu_pointer::hit_pane(mpLetterParent[i], 8.0f)) {
continue; continue;
} }
dusk::menu_pointer::set_hover_target(i);
if (mIndex != i) { if (mIndex != i) {
mIndex = i; mIndex = i;
+6 -14
View File
@@ -394,21 +394,8 @@ void dMenuMapCommon_c::drawIcon(f32 i_posX, f32 i_posY, f32 param_3, f32 param_4
icon_size_y *= _c7c; icon_size_y *= _c7c;
} }
#if TARGET_PC
f32 rotation = mIconInfo[info_idx].rotation;
if (dusk::getSettings().game.enableMirrorMode &&
(mIconInfo[info_idx].icon_no == ICON_LINK_e ||
mIconInfo[info_idx].icon_no == ICON_LINK_ENTER_e))
{
rotation = -rotation;
}
mPictures[mIconInfo[info_idx].icon_no]->rotate(icon_size_x / 2, icon_size_y / 2, ROTATE_Z,
rotation);
#else
mPictures[mIconInfo[info_idx].icon_no]->rotate(icon_size_x / 2, icon_size_y / 2, ROTATE_Z, mPictures[mIconInfo[info_idx].icon_no]->rotate(icon_size_x / 2, icon_size_y / 2, ROTATE_Z,
mIconInfo[info_idx].rotation); mIconInfo[info_idx].rotation);
#endif
if (mIconInfo[info_idx].icon_no == ICON_LIGHT_DROP_e) { if (mIconInfo[info_idx].icon_no == ICON_LIGHT_DROP_e) {
mPictures[mIconInfo[info_idx].icon_no]->setAlpha((180.0f * _c80) / 255.0f); mPictures[mIconInfo[info_idx].icon_no]->setAlpha((180.0f * _c80) / 255.0f);
@@ -423,10 +410,15 @@ void dMenuMapCommon_c::drawIcon(f32 i_posX, f32 i_posY, f32 param_3, f32 param_4
} }
f32 pos_x = i_posX + (icon_pos_x - (icon_size_x / 2)); f32 pos_x = i_posX + (icon_pos_x - (icon_size_x / 2));
bool r4 = false;
#if TARGET_PC #if TARGET_PC
if (dusk::getSettings().game.enableMirrorMode) { if (dusk::getSettings().game.enableMirrorMode) {
pos_x = getMirrorCenterPosX(i_posX + (icon_pos_x - (icon_size_x / 2)), icon_size_x / 2); pos_x = getMirrorCenterPosX(i_posX + (icon_pos_x - (icon_size_x / 2)), icon_size_x / 2);
} }
if(mIconInfo[info_idx].icon_no == ICON_GOLD_WOLF_e) {
r4 = true;
}
#endif #endif
mPictures[mIconInfo[info_idx].icon_no]->draw(pos_x, (i_posY + (icon_pos_y - icon_size_y / 2)), mPictures[mIconInfo[info_idx].icon_no]->draw(pos_x, (i_posY + (icon_pos_y - icon_size_y / 2)),
@@ -435,7 +427,7 @@ void dMenuMapCommon_c::drawIcon(f32 i_posX, f32 i_posY, f32 param_3, f32 param_4
if (mIconInfo[info_idx].icon_no == ICON_LIGHT_DROP_e) { if (mIconInfo[info_idx].icon_no == ICON_LIGHT_DROP_e) {
mLightDropPic->draw((pos_x + (icon_size_x / 2)) - (var_f29 / 2), mLightDropPic->draw((pos_x + (icon_size_x / 2)) - (var_f29 / 2),
((icon_size_y / 2) + (i_posY + (icon_pos_y - icon_size_y / 2))) - (var_f28 / 2), ((icon_size_y / 2) + (i_posY + (icon_pos_y - icon_size_y / 2))) - (var_f28 / 2),
var_f29, var_f28, false, false, false); var_f29, var_f28, r4, false, false);
} }
} }
} }
+45 -5
View File
@@ -83,6 +83,12 @@ enum SelectType {
SelectType8, SelectType8,
}; };
#if TARGET_PC
static dusk::menu_pointer::TargetId option_yes_no_target(u8 index) noexcept {
return static_cast<dusk::menu_pointer::TargetId>(0x100 + index);
}
#endif
dMenu_Option_c::dMenu_Option_c(JKRArchive* i_archive, STControl* i_stick) { dMenu_Option_c::dMenu_Option_c(JKRArchive* i_archive, STControl* i_stick) {
mUseFlag = 0; mUseFlag = 0;
mBarScale[0] = g_drawHIO.mOptionScreen.mBarScale[0]; mBarScale[0] = g_drawHIO.mOptionScreen.mBarScale[0];
@@ -1098,18 +1104,28 @@ void dMenu_Option_c::confirm_move_move() {
if (!dusk::menu_pointer::hit_pane(mpYesNoSelBase_c[i], 8.0f)) { if (!dusk::menu_pointer::hit_pane(mpYesNoSelBase_c[i], 8.0f)) {
continue; continue;
} }
dusk::menu_pointer::set_hover_target(option_yes_no_target(i));
const bool clicked = dusk::menu_pointer::consume_click();
if (field_0x3f9 != i) { if (field_0x3f9 != i) {
Z2GetAudioMgr()->seStart(Z2SE_SY_MENU_CURSOR_COMMON, NULL, 0, 0, 1.0f, 1.0f, -1.0f, Z2GetAudioMgr()->seStart(Z2SE_SY_MENU_CURSOR_COMMON, NULL, 0, 0, 1.0f, 1.0f, -1.0f,
-1.0f, 0); -1.0f, 0);
field_0x3fa = field_0x3f9; field_0x3fa = field_0x3f9;
field_0x3f9 = i; field_0x3f9 = i;
if (clicked) {
yesNoSelectStart();
field_0x3ef = SelectType7;
dMeter2Info_set2DVibrationM();
mpWarning->_move();
setAnimation();
return;
}
yesnoSelectAnmSet(); yesnoSelectAnmSet();
field_0x3ef = SelectType6; field_0x3ef = SelectType6;
mpWarning->_move(); mpWarning->_move();
setAnimation(); setAnimation();
return; return;
} }
if (dusk::menu_pointer::consume_click()) { if (clicked) {
yesNoSelectStart(); yesNoSelectStart();
field_0x3ef = SelectType7; field_0x3ef = SelectType7;
dMeter2Info_set2DVibrationM(); dMeter2Info_set2DVibrationM();
@@ -1156,11 +1172,36 @@ void dMenu_Option_c::confirm_select_init() {
} }
void dMenu_Option_c::confirm_select_move() { void dMenu_Option_c::confirm_select_move() {
#if TARGET_PC
dusk::menu_pointer::begin_context(dusk::menu_pointer::Context::Options);
if (field_0x3f9 != 0xff &&
dusk::menu_pointer::hit_pane(mpYesNoSelBase_c[field_0x3f9], 8.0f))
{
const dusk::menu_pointer::TargetId target = option_yes_no_target(field_0x3f9);
dusk::menu_pointer::set_hover_target(target);
if (dusk::menu_pointer::consume_click()) {
dusk::menu_pointer::defer_activation(dusk::menu_pointer::Context::Options, target);
}
}
#endif
u8 selectMoveAnm = yesnoSelectMoveAnm(); u8 selectMoveAnm = yesnoSelectMoveAnm();
u8 wakuAlphaAnm = yesnoWakuAlpahAnm(field_0x3fa); u8 wakuAlphaAnm = yesnoWakuAlpahAnm(field_0x3fa);
if (selectMoveAnm == 1 && wakuAlphaAnm == 1) { if (selectMoveAnm == 1 && wakuAlphaAnm == 1) {
yesnoCursorShow(); yesnoCursorShow();
#if TARGET_PC
if (field_0x3f9 != 0xff &&
dusk::menu_pointer::consume_deferred_activation(
dusk::menu_pointer::Context::Options, option_yes_no_target(field_0x3f9)))
{
yesNoSelectStart();
field_0x3ef = SelectType7;
dMeter2Info_set2DVibrationM();
mpWarning->_move();
setAnimation();
return;
}
#endif
field_0x3ef = SelectType5; field_0x3ef = SelectType5;
} }
mpWarning->_move(); mpWarning->_move();
@@ -2196,16 +2237,14 @@ bool dMenu_Option_c::isRumbleSupported() {
#if TARGET_PC #if TARGET_PC
bool dMenu_Option_c::pointerConfirmSelect() { bool dMenu_Option_c::pointerConfirmSelect() {
dusk::menu_pointer::begin_context(dusk::menu_pointer::Context::Options); dusk::menu_pointer::begin_context(dusk::menu_pointer::Context::Options);
if (!dusk::menu_pointer::state().clicked) {
return false;
}
for (u8 i = 0; i < SelectType3; ++i) { for (u8 i = 0; i < SelectType3; ++i) {
if (dusk::menu_pointer::hit_pane(mpMenuPane[i], 8.0f)) { if (dusk::menu_pointer::hit_pane(mpMenuPane[i], 8.0f)) {
dusk::menu_pointer::set_hover_target(i);
return false; return false;
} }
} }
dusk::menu_pointer::set_hover_target(0x200);
if (!dusk::menu_pointer::consume_click()) { if (!dusk::menu_pointer::consume_click()) {
return false; return false;
} }
@@ -2226,6 +2265,7 @@ bool dMenu_Option_c::dpdMenuMove() {
if (!dusk::menu_pointer::hit_pane(mpMenuPane[i], 8.0f)) { if (!dusk::menu_pointer::hit_pane(mpMenuPane[i], 8.0f)) {
continue; continue;
} }
dusk::menu_pointer::set_hover_target(i);
if (getSelectType() != i) { if (getSelectType() != i) {
field_0x3ef = i; field_0x3ef = i;
setCursorPos(i); setCursorPos(i);
+23 -1
View File
@@ -198,6 +198,7 @@ dMenu_Ring_c::dMenu_Ring_c(JKRExpHeap* i_heap, STControl* i_stick, CSTControl* i
mCursorInterpPrevAngular = false; mCursorInterpPrevAngular = false;
mCursorInterpCurrAngular = false; mCursorInterpCurrAngular = false;
mCursorInterpInit = false; mCursorInterpInit = false;
mPointerTouchPressHoveredCurrent = false;
#endif #endif
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
field_0x674[i] = 0; field_0x674[i] = 0;
@@ -1561,6 +1562,10 @@ bool dMenu_Ring_c::pointerMove() {
if (hoveredSlot < 0) { if (hoveredSlot < 0) {
return false; return false;
} }
if (pointer.pressed) {
mPointerTouchPressHoveredCurrent = pointer.touch && hoveredSlot == mCurrentSlot;
}
dusk::menu_pointer::set_hover_target(static_cast<dusk::menu_pointer::TargetId>(hoveredSlot));
if (mCurrentSlot != hoveredSlot) { if (mCurrentSlot != hoveredSlot) {
mDirectSelectCursorPos.x = mItemSlotPosX[mCurrentSlot]; mDirectSelectCursorPos.x = mItemSlotPosX[mCurrentSlot];
@@ -1573,10 +1578,27 @@ bool dMenu_Ring_c::pointerMove() {
return true; return true;
} }
if (dusk::menu_pointer::consume_click()) { const bool clickOpensExplain = !pointer.touch || mPointerTouchPressHoveredCurrent;
if (clickOpensExplain && dusk::menu_pointer::consume_click()) {
const u8 item = dComIfGs_getItem(mItemSlots[mCurrentSlot], false);
if (!dMeter2Info_isTouchKeyCheck(0xe) && openExplain(item)) {
dMeter2Info_setItemExplainWindowStatus(1);
field_0x6c4 = mCurrentSlot;
setStatus(STATUS_EXPLAIN);
dMeter2Info_set2DVibration();
setDoStatus(0);
} else {
Z2GetAudioMgr()->seStart(Z2SE_SYS_ERROR, NULL, 0, 0, 1.0f, 1.0f, -1.0f,
-1.0f, 0);
}
mPointerTouchPressHoveredCurrent = false;
return true; return true;
} }
if (pointer.released) {
mPointerTouchPressHoveredCurrent = false;
}
return false; return false;
} }
#endif #endif
+16 -10
View File
@@ -1820,6 +1820,7 @@ bool dMenu_save_c::pointerSaveSelect() {
if (!dusk::menu_pointer::hit_pane(mpSelData[i], 8.0f)) { if (!dusk::menu_pointer::hit_pane(mpSelData[i], 8.0f)) {
continue; continue;
} }
dusk::menu_pointer::set_hover_target(pointer_target(s_pointerSaveSelectTarget, i));
const bool clicked = dusk::menu_pointer::consume_click(); const bool clicked = dusk::menu_pointer::consume_click();
if (mSelectedFile != i) { if (mSelectedFile != i) {
mDoAud_seStart(Z2SE_FILE_SELECT_CURSOR, NULL, 0, 0); mDoAud_seStart(Z2SE_FILE_SELECT_CURSOR, NULL, 0, 0);
@@ -1848,6 +1849,7 @@ bool dMenu_save_c::pointerYesNoSelect(bool errorSelect, u8 errParam, u8 soundPar
if (!dusk::menu_pointer::hit_pane(mpNoYes[i], 8.0f)) { if (!dusk::menu_pointer::hit_pane(mpNoYes[i], 8.0f)) {
continue; continue;
} }
dusk::menu_pointer::set_hover_target(pointer_target(s_pointerYesNoSelectTarget, i));
const bool clicked = const bool clicked =
(!errorSelect || mYesNoCursor == i) && dusk::menu_pointer::consume_click(); (!errorSelect || mYesNoCursor == i) && dusk::menu_pointer::consume_click();
if (mYesNoCursor != i) { if (mYesNoCursor != i) {
@@ -1952,12 +1954,14 @@ void dMenu_save_c::saveSelectMoveAnime() {
#if TARGET_PC #if TARGET_PC
dusk::menu_pointer::begin_context(dusk::menu_pointer::Context::Save); dusk::menu_pointer::begin_context(dusk::menu_pointer::Context::Save);
if (mSelectedFile != 0xFF && if (mSelectedFile != 0xFF &&
dusk::menu_pointer::hit_pane(mpSelData[mSelectedFile], 8.0f) && dusk::menu_pointer::hit_pane(mpSelData[mSelectedFile], 8.0f))
dusk::menu_pointer::consume_click())
{ {
dusk::menu_pointer::defer_activation( dusk::menu_pointer::set_hover_target(pointer_target(s_pointerSaveSelectTarget, mSelectedFile));
dusk::menu_pointer::Context::Save, if (dusk::menu_pointer::consume_click()) {
pointer_target(s_pointerSaveSelectTarget, mSelectedFile)); dusk::menu_pointer::defer_activation(
dusk::menu_pointer::Context::Save,
pointer_target(s_pointerSaveSelectTarget, mSelectedFile));
}
} }
#endif #endif
bool bookWakuAnmComplete = true; bool bookWakuAnmComplete = true;
@@ -2130,12 +2134,14 @@ void dMenu_save_c::yesNoCursorMoveAnm() {
#if TARGET_PC #if TARGET_PC
dusk::menu_pointer::begin_context(dusk::menu_pointer::Context::Save); dusk::menu_pointer::begin_context(dusk::menu_pointer::Context::Save);
if (mYesNoCursor != 0xFF && if (mYesNoCursor != 0xFF &&
dusk::menu_pointer::hit_pane(mpNoYes[mYesNoCursor], 8.0f) && dusk::menu_pointer::hit_pane(mpNoYes[mYesNoCursor], 8.0f))
dusk::menu_pointer::consume_click())
{ {
dusk::menu_pointer::defer_activation( dusk::menu_pointer::set_hover_target(pointer_target(s_pointerYesNoSelectTarget, mYesNoCursor));
dusk::menu_pointer::Context::Save, if (dusk::menu_pointer::consume_click()) {
pointer_target(s_pointerYesNoSelectTarget, mYesNoCursor)); dusk::menu_pointer::defer_activation(
dusk::menu_pointer::Context::Save,
pointer_target(s_pointerYesNoSelectTarget, mYesNoCursor));
}
} }
#endif #endif
bool selAnmComplete = yesnoSelectMoveAnm(0); bool selAnmComplete = yesnoSelectMoveAnm(0);
+1
View File
@@ -316,6 +316,7 @@ bool dMenu_Skill_c::pointerWait() {
if (!dusk::menu_pointer::hit_pane(mpLetterParent[i], 8.0f)) { if (!dusk::menu_pointer::hit_pane(mpLetterParent[i], 8.0f)) {
continue; continue;
} }
dusk::menu_pointer::set_hover_target(i);
if (mIndex != i) { if (mIndex != i) {
mIndex = i; mIndex = i;
+11
View File
@@ -437,7 +437,12 @@ void dMeter2_c::checkStatus() {
field_0x128 = daPy_py_c::checkNowWolf(); field_0x128 = daPy_py_c::checkNowWolf();
#if TARGET_PC
dMsgObject_c* msgObject = dMsgObject_getMsgObjectClass();
if (!dComIfGp_2dShowCheck() || (msgObject != NULL && msgObject->isPlaceMessage())) {
#else
if (!dComIfGp_2dShowCheck() || dMsgObject_getMsgObjectClass()->isPlaceMessage()) { if (!dComIfGp_2dShowCheck() || dMsgObject_getMsgObjectClass()->isPlaceMessage()) {
#endif
mStatus |= 0x4000; mStatus |= 0x4000;
} else if (dComIfGp_checkPlayerStatus1(0, 1) && dComIfGp_getAStatus() == 0x12) { } else if (dComIfGp_checkPlayerStatus1(0, 1) && dComIfGp_getAStatus() == 0x12) {
mStatus |= 0x200000; mStatus |= 0x200000;
@@ -2870,8 +2875,14 @@ void dMeter2_c::alphaAnimeButton() {
u8 var_31; u8 var_31;
var_31 = 0; var_31 = 0;
#if TARGET_PC
dMsgObject_c* msgObject = dMsgObject_getMsgObjectClass();
if ((mStatus & 0x4000) ||
((mStatus & 0x100) && (msgObject != NULL && msgObject->isAutoMessageFlag())) ||
#else
if ((mStatus & 0x4000) || if ((mStatus & 0x4000) ||
((mStatus & 0x100) && dMsgObject_getMsgObjectClass()->isAutoMessageFlag()) || ((mStatus & 0x100) && dMsgObject_getMsgObjectClass()->isAutoMessageFlag()) ||
#endif
((mStatus & 0x40000000) && !(mStatus & 0x100)) || (mStatus & 0x80000000) || (mStatus & 8) || ((mStatus & 0x40000000) && !(mStatus & 0x100)) || (mStatus & 0x80000000) || (mStatus & 8) ||
(mStatus & 0x10) || (mStatus & 0x20) || (mStatus & 0x04000000) || (mStatus & 0x10000000)) (mStatus & 0x10) || (mStatus & 0x20) || (mStatus & 0x04000000) || (mStatus & 0x10000000))
{ {
-11
View File
@@ -428,17 +428,6 @@ static void dummyStrings() {
dMsgObject_HIO_c g_MsgObject_HIO_c; dMsgObject_HIO_c g_MsgObject_HIO_c;
int dMsgObject_c::_execute() { int dMsgObject_c::_execute() {
// TODO: enabling wii message overrides fixes direction text, but gives wrong item control text
/*#if TARGET_PC
if (dusk::getSettings().game.enableMirrorMode) {
// enable wii message index override
g_MsgObject_HIO_c.mMessageDisplay = 1;
} else if (!dusk::getSettings().game.enableMirrorMode && g_MsgObject_HIO_c.mMessageDisplay == 1) {
g_MsgObject_HIO_c.mMessageDisplay = 0;
}
#endif*/
field_0x4c7 = 0; field_0x4c7 = 0;
if (mpTalkHeap != NULL) { if (mpTalkHeap != NULL) {
field_0x148 = mDoExt_setCurrentHeap(mpTalkHeap); field_0x148 = mDoExt_setCurrentHeap(mpTalkHeap);
+2 -1
View File
@@ -560,7 +560,8 @@ bool dMsgScrn3Select_c::pointerMove() {
mDPDPoint = choice; mDPDPoint = choice;
field_0x110 = paneIndex; field_0x110 = paneIndex;
dusk::menu_pointer::set_dialog_choice(choice, dusk::menu_pointer::state().clicked); dusk::menu_pointer::set_hover_target(choice);
dusk::menu_pointer::set_dialog_choice(choice, dusk::menu_pointer::peek_click());
return true; return true;
} }
+1 -1
View File
@@ -1823,7 +1823,7 @@ int dSv_info_c::memory_to_card(char* card_ptr, int dataNum) {
savedata->getPlayer().getPlayerInfo().setTotalTime(play_time); savedata->getPlayer().getPlayerInfo().setTotalTime(play_time);
} }
savedata->getPlayer().getPlayerStatusB().setDateIpl(OSGetTime()); savedata->getPlayer().getPlayerStatusB().setDateIpl(DUSK_IF_ELSE(OSGetSystemTime(), OSGetTime()));
memcpy(card_ptr, savedata, sizeof(dSv_save_c)); memcpy(card_ptr, savedata, sizeof(dSv_save_c));
card_ptr += 0x958; card_ptr += 0x958;
+280 -150
View File
@@ -7,6 +7,7 @@
#include "dusk/io.hpp" #include "dusk/io.hpp"
#include "dusk/settings.h" #include "dusk/settings.h"
#include <algorithm>
#include <cmath> #include <cmath>
#include <filesystem> #include <filesystem>
#include <limits> #include <limits>
@@ -15,77 +16,90 @@
#include <string_view> #include <string_view>
#include <system_error> #include <system_error>
#include <utility> #include <utility>
#include <vector>
#include "dusk/action_bindings.h" #include "dusk/action_bindings.h"
#include "dusk/logging.h"
#include "dusk/main.h" #include "dusk/main.h"
using namespace dusk::config; namespace dusk::config {
namespace {
constexpr auto ConfigFileName = "config.json"; constexpr auto ConfigFileName = "config.json";
using json = nlohmann::json; using json = nlohmann::json;
aurora::Module DuskConfigLog("dusk::config"); aurora::Module DuskConfigLog("dusk::config");
static absl::flat_hash_map<std::string_view, ConfigVarBase*> RegisteredConfigVars; absl::flat_hash_map<std::string, ConfigVarBase*> RegisteredConfigVars;
static bool RegistrationDone = false; absl::flat_hash_map<std::string, nlohmann::json> UnregisteredConfigVars;
absl::flat_hash_map<std::string, std::string> UnregisteredConfigVarOverrides;
static std::optional<dusk::ui::ControlAnchor> parse_control_anchor(std::string_view value) { struct ChangeSubscription {
Subscription token;
ChangeCallback callback;
};
absl::flat_hash_map<std::string, std::vector<ChangeSubscription> > s_changeSubscriptions;
absl::flat_hash_map<Subscription, std::string> s_changeTokenNames;
Subscription s_nextChangeToken = 1;
// Names currently being notified; guards against a callback re-notifying its own CVar.
std::vector<std::string> s_activeChangeNotifications;
std::optional<ui::ControlAnchor> parse_control_anchor(std::string_view value) {
if (value == "none") { if (value == "none") {
return dusk::ui::ControlAnchor::None; return ui::ControlAnchor::None;
} }
if (value == "top") { if (value == "top") {
return dusk::ui::ControlAnchor::Top; return ui::ControlAnchor::Top;
} }
if (value == "left") { if (value == "left") {
return dusk::ui::ControlAnchor::Left; return ui::ControlAnchor::Left;
} }
if (value == "bottom") { if (value == "bottom") {
return dusk::ui::ControlAnchor::Bottom; return ui::ControlAnchor::Bottom;
} }
if (value == "right") { if (value == "right") {
return dusk::ui::ControlAnchor::Right; return ui::ControlAnchor::Right;
} }
if (value == "topLeft") { if (value == "topLeft") {
return dusk::ui::ControlAnchor::TopLeft; return ui::ControlAnchor::TopLeft;
} }
if (value == "topRight") { if (value == "topRight") {
return dusk::ui::ControlAnchor::TopRight; return ui::ControlAnchor::TopRight;
} }
if (value == "bottomLeft") { if (value == "bottomLeft") {
return dusk::ui::ControlAnchor::BottomLeft; return ui::ControlAnchor::BottomLeft;
} }
if (value == "bottomRight") { if (value == "bottomRight") {
return dusk::ui::ControlAnchor::BottomRight; return ui::ControlAnchor::BottomRight;
} }
return std::nullopt; return std::nullopt;
} }
static const char* control_anchor_value(dusk::ui::ControlAnchor anchor) { const char* control_anchor_value(ui::ControlAnchor anchor) {
switch (anchor) { switch (anchor) {
case dusk::ui::ControlAnchor::None: case ui::ControlAnchor::None:
return "none"; return "none";
case dusk::ui::ControlAnchor::Top: case ui::ControlAnchor::Top:
return "top"; return "top";
case dusk::ui::ControlAnchor::Left: case ui::ControlAnchor::Left:
return "left"; return "left";
case dusk::ui::ControlAnchor::Bottom: case ui::ControlAnchor::Bottom:
return "bottom"; return "bottom";
case dusk::ui::ControlAnchor::Right: case ui::ControlAnchor::Right:
return "right"; return "right";
case dusk::ui::ControlAnchor::TopLeft: case ui::ControlAnchor::TopLeft:
return "topLeft"; return "topLeft";
case dusk::ui::ControlAnchor::TopRight: case ui::ControlAnchor::TopRight:
return "topRight"; return "topRight";
case dusk::ui::ControlAnchor::BottomLeft: case ui::ControlAnchor::BottomLeft:
return "bottomLeft"; return "bottomLeft";
case dusk::ui::ControlAnchor::BottomRight: case ui::ControlAnchor::BottomRight:
return "bottomRight"; return "bottomRight";
} }
return "none"; return "none";
} }
static std::optional<float> json_finite_float(const json& object, const char* key) { std::optional<float> json_finite_float(const json& object, const char* key) {
const auto iter = object.find(key); const auto iter = object.find(key);
if (iter == object.end() || !iter->is_number()) { if (iter == object.end() || !iter->is_number()) {
return std::nullopt; return std::nullopt;
@@ -99,7 +113,7 @@ static std::optional<float> json_finite_float(const json& object, const char* ke
return value; return value;
} }
static std::optional<dusk::ui::ControlProps> parse_control_props(const json& value) { std::optional<ui::ControlProps> parse_control_props(const json& value) {
if (!value.is_object()) { if (!value.is_object()) {
return std::nullopt; return std::nullopt;
} }
@@ -118,7 +132,7 @@ static std::optional<dusk::ui::ControlProps> parse_control_props(const json& val
if (!anchor || *w <= 0.0f || *h <= 0.0f || *scale <= 0.0f) { if (!anchor || *w <= 0.0f || *h <= 0.0f || *scale <= 0.0f) {
return std::nullopt; return std::nullopt;
} }
return dusk::ui::ControlProps{ return ui::ControlProps{
.x = *x, .x = *x,
.y = *y, .y = *y,
.w = *w, .w = *w,
@@ -128,17 +142,17 @@ static std::optional<dusk::ui::ControlProps> parse_control_props(const json& val
}; };
} }
static std::filesystem::path GetConfigJsonPath() { std::filesystem::path GetConfigJsonPath() {
return dusk::ConfigPath / ConfigFileName; return ConfigPath / ConfigFileName;
} }
static std::filesystem::path GetTempConfigJsonPath(const std::filesystem::path& configJsonPath) { std::filesystem::path GetTempConfigJsonPath(const std::filesystem::path& configJsonPath) {
auto tempPath = configJsonPath; auto tempPath = configJsonPath;
tempPath.replace_filename(fmt::format(".{}.tmp", configJsonPath.filename().string())); tempPath.replace_filename(fmt::format(".{}.tmp", configJsonPath.filename().string()));
return tempPath; return tempPath;
} }
static void ReplaceFile(const std::filesystem::path& source, const std::filesystem::path& target) { void ReplaceFile(const std::filesystem::path& source, const std::filesystem::path& target) {
std::error_code ec; std::error_code ec;
std::filesystem::rename(source, target, ec); std::filesystem::rename(source, target, ec);
if (ec) { if (ec) {
@@ -148,19 +162,8 @@ static void ReplaceFile(const std::filesystem::path& source, const std::filesyst
} }
} }
ConfigVarBase::ConfigVarBase(const char* name, const ConfigImplBase* impl)
: name(name), registered(false), layer(ConfigVarLayer::Default), impl(impl) {}
const char* ConfigVarBase::getName() const noexcept {
return name;
}
const ConfigImplBase* ConfigVarBase::getImpl() const noexcept {
return impl;
}
template <typename T> template <typename T>
static T sanitizeEnumValue(const ConfigVar<T>& cVar, T value) { T sanitizeEnumValue(const ConfigVar<T>& cVar, T value) {
if constexpr (std::is_enum_v<T>) { if constexpr (std::is_enum_v<T>) {
using Underlying = std::underlying_type_t<T>; using Underlying = std::underlying_type_t<T>;
const Underlying raw = static_cast<Underlying>(value); const Underlying raw = static_cast<Underlying>(value);
@@ -174,6 +177,83 @@ static T sanitizeEnumValue(const ConfigVar<T>& cVar, T value) {
return value; return value;
} }
template <ConfigValue T>
requires std::is_integral_v<T>&& std::is_signed_v<T> T parse_arg_value(
const ConfigVar<T>&, const std::string_view stringValue) {
const std::string str(stringValue);
const auto result = std::stoll(str);
if (result >= std::numeric_limits<T>::min() && result <= std::numeric_limits<T>::max()) {
return static_cast<T>(result);
}
throw std::out_of_range("Value is too large");
}
template <ConfigValue T>
requires std::is_integral_v<T>&& std::is_unsigned_v<T> T parse_arg_value(
const ConfigVar<T>&, const std::string_view stringValue) {
const std::string str(stringValue);
const auto result = std::stoull(str);
if (result <= std::numeric_limits<T>::max()) {
return static_cast<T>(result);
}
throw std::out_of_range("Value is too large");
}
f32 parse_arg_value(const ConfigVar<f32>&, const std::string_view stringValue) {
const std::string str(stringValue);
return std::stof(str);
}
f64 parse_arg_value(const ConfigVar<f64>&, const std::string_view stringValue) {
const std::string str(stringValue);
return std::stod(str);
}
std::string parse_arg_value(const ConfigVar<std::string>&, const std::string_view stringValue) {
return std::string(stringValue);
}
template <ConfigValue T>
requires std::is_enum_v<T> T parse_arg_value(
const ConfigVar<T>& cVar, const std::string_view stringValue) {
using Underlying = std::underlying_type_t<T>;
const std::string str(stringValue);
if constexpr (std::is_signed_v<Underlying>) {
const auto result = std::stoll(str);
if (result >= std::numeric_limits<Underlying>::min() &&
result <= std::numeric_limits<Underlying>::max())
{
return sanitizeEnumValue(cVar, static_cast<T>(result));
}
throw std::out_of_range("Value is too large");
} else {
const auto result = std::stoull(str);
if (result <= std::numeric_limits<Underlying>::max()) {
return sanitizeEnumValue(cVar, static_cast<T>(result));
}
throw std::out_of_range("Value is too large");
}
}
} // namespace
ConfigVarBase::ConfigVarBase(std::string name, const ConfigImplBase* impl)
: name(std::move(name)), registered(false), layer(ConfigVarLayer::Default), impl(impl) {}
const char* ConfigVarBase::getName() const noexcept {
return name.c_str();
}
const ConfigImplBase* ConfigVarBase::getImpl() const noexcept {
return impl;
}
ConfigVarBase::~ConfigVarBase() {
if (registered) {
DuskLog.fatal("CVar '{}' was destroyed while still registered!", name);
}
}
template <ConfigValue T> template <ConfigValue T>
void ConfigImpl<T>::loadFromJson(ConfigVar<T>& cVar, const json& jsonValue) { void ConfigImpl<T>::loadFromJson(ConfigVar<T>& cVar, const json& jsonValue) {
if constexpr (std::is_enum_v<T>) { if constexpr (std::is_enum_v<T>) {
@@ -187,12 +267,12 @@ void ConfigImpl<T>::loadFromJson(ConfigVar<T>& cVar, const json& jsonValue) {
const Underlying raw = b ? static_cast<Underlying>(1) : static_cast<Underlying>(0); const Underlying raw = b ? static_cast<Underlying>(1) : static_cast<Underlying>(0);
cVar.setValue(sanitizeEnumValue(cVar, static_cast<T>(raw)), false); cVar.load_value(sanitizeEnumValue(cVar, static_cast<T>(raw)));
return; return;
} }
} }
cVar.setValue(sanitizeEnumValue(cVar, jsonValue.get<T>()), false); cVar.load_value(sanitizeEnumValue(cVar, jsonValue.get<T>()));
} }
template <ConfigValue T> template <ConfigValue T>
@@ -200,74 +280,9 @@ nlohmann::json ConfigImpl<T>::dumpToJson(const ConfigVar<T>& cVar) {
return cVar.getValueForSave(); return cVar.getValueForSave();
} }
template <ConfigValue T>
requires std::is_integral_v<T>&& std::is_signed_v<T> static void loadFromArgImpl(
ConfigVar<T>& cVar, const std::string_view stringValue) {
const std::string str(stringValue);
const auto result = std::stoll(str);
if (result >= std::numeric_limits<T>::min() && result <= std::numeric_limits<T>::max()) {
cVar.setOverrideValue(result);
} else {
throw std::out_of_range("Value is too large");
}
}
template <ConfigValue T>
requires std::is_integral_v<T>&& std::is_unsigned_v<T> static void loadFromArgImpl(
ConfigVar<T>& cVar, const std::string_view stringValue) {
const std::string str(stringValue);
const auto result = std::stoull(str);
if (result <= std::numeric_limits<T>::max()) {
cVar.setOverrideValue(result);
} else {
throw std::out_of_range("Value is too large");
}
}
static void loadFromArgImpl(ConfigVar<f32>& cVar, const std::string_view stringValue) {
const std::string str(stringValue);
const auto result = std::stof(str);
cVar.setOverrideValue(result);
}
static void loadFromArgImpl(ConfigVar<f64>& cVar, const std::string_view stringValue) {
const std::string str(stringValue);
const auto result = std::stod(str);
cVar.setOverrideValue(result);
}
static void loadFromArgImpl(ConfigVar<std::string>& cVar, const std::string_view stringValue) {
cVar.setOverrideValue(std::string(stringValue));
}
template <ConfigValue T>
requires std::is_enum_v<T> static void loadFromArgImpl(
ConfigVar<T>& cVar, const std::string_view stringValue) {
using Underlying = std::underlying_type_t<T>;
const std::string str(stringValue);
if constexpr (std::is_signed_v<Underlying>) {
const auto result = std::stoll(str);
if (result >= std::numeric_limits<Underlying>::min() &&
result <= std::numeric_limits<Underlying>::max())
{
cVar.setOverrideValue(sanitizeEnumValue(cVar, static_cast<T>(result)));
} else {
throw std::out_of_range("Value is too large");
}
} else {
const auto result = std::stoull(str);
if (result <= std::numeric_limits<Underlying>::max()) {
cVar.setOverrideValue(sanitizeEnumValue(cVar, static_cast<T>(result)));
} else {
throw std::out_of_range("Value is too large");
}
}
}
template <ConfigValue T> template <ConfigValue T>
void ConfigImpl<T>::loadFromArg(ConfigVar<T>& cVar, const std::string_view stringValue) { void ConfigImpl<T>::loadFromArg(ConfigVar<T>& cVar, const std::string_view stringValue) {
loadFromArgImpl(cVar, stringValue); cVar.load_override_value(parse_arg_value(cVar, stringValue));
} }
template <> template <>
@@ -275,18 +290,16 @@ void ConfigImpl<bool>::loadFromArg(ConfigVar<bool>& cVar, const std::string_view
if (stringValue == "1" || stringValue == "TRUE" || stringValue == "true" || if (stringValue == "1" || stringValue == "TRUE" || stringValue == "true" ||
stringValue == "True") stringValue == "True")
{ {
cVar.setOverrideValue(true); cVar.load_override_value(true);
} else if (stringValue == "0" || stringValue == "FALSE" || stringValue == "false" || } else if (stringValue == "0" || stringValue == "FALSE" || stringValue == "false" ||
stringValue == "False") stringValue == "False")
{ {
cVar.setOverrideValue(false); cVar.load_override_value(false);
} else { } else {
throw InvalidConfigError("Value cannot be parsed as boolean"); throw InvalidConfigError("Value cannot be parsed as boolean");
} }
} }
// My IDE is convinced this namespace is necessary. It shouldn't be AFAICT?
namespace dusk::config {
template class ConfigImpl<bool>; template class ConfigImpl<bool>;
template class ConfigImpl<s8>; template class ConfigImpl<s8>;
template class ConfigImpl<u8>; template class ConfigImpl<u8>;
@@ -299,10 +312,10 @@ template class ConfigImpl<u64>;
template class ConfigImpl<f32>; template class ConfigImpl<f32>;
template class ConfigImpl<f64>; template class ConfigImpl<f64>;
template class ConfigImpl<std::string>; template class ConfigImpl<std::string>;
template class ConfigImpl<dusk::BloomMode>; template class ConfigImpl<BloomMode>;
template class ConfigImpl<dusk::DepthOfFieldMode>; template class ConfigImpl<DepthOfFieldMode>;
template class ConfigImpl<dusk::DiscVerificationState>; template class ConfigImpl<DiscVerificationState>;
template class ConfigImpl<dusk::GameLanguage>; template class ConfigImpl<GameLanguage>;
template <> template <>
void ConfigImpl<FrameInterpMode>::loadFromJson( void ConfigImpl<FrameInterpMode>::loadFromJson(
@@ -312,11 +325,11 @@ void ConfigImpl<FrameInterpMode>::loadFromJson(
const FrameInterpMode mode = b ? FrameInterpMode::Unlimited : FrameInterpMode::Off; const FrameInterpMode mode = b ? FrameInterpMode::Unlimited : FrameInterpMode::Off;
cVar.setValue(sanitizeEnumValue(cVar, mode), false); cVar.load_value(sanitizeEnumValue(cVar, mode));
return; return;
} }
cVar.setValue(sanitizeEnumValue(cVar, jsonValue.get<FrameInterpMode>()), false); cVar.load_value(sanitizeEnumValue(cVar, jsonValue.get<FrameInterpMode>()));
} }
template <> template <>
@@ -347,7 +360,7 @@ void ConfigImpl<ui::ControlLayout>::loadFromJson(
} }
} }
cVar.setValue(std::move(layout), false); cVar.load_value(std::move(layout));
} }
template <> template <>
@@ -377,25 +390,59 @@ nlohmann::json ConfigImpl<ui::ControlLayout>::dumpToJson(const ConfigVar<ui::Con
}; };
} }
template class ConfigImpl<dusk::FrameInterpMode>; template class ConfigImpl<FrameInterpMode>;
template class ConfigImpl<dusk::MenuScaling>; template class ConfigImpl<TouchTargeting>;
template class ConfigImpl<dusk::Resampler>; template class ConfigImpl<MenuScaling>;
template class ConfigImpl<dusk::MagicArmorMode>; template class ConfigImpl<Resampler>;
template class ConfigImpl<dusk::ui::ControlLayout>; template class ConfigImpl<MagicArmorMode>;
} // namespace dusk::config template class ConfigImpl<ui::ControlLayout>;
void dusk::config::Register(ConfigVarBase& configVar) {
const auto& name = configVar.getName();
if (RegistrationDone) {
DuskConfigLog.fatal("Tried to register CVar {} after registrations closed!", name);
}
void Register(ConfigVarBase& configVar) {
const std::string_view name = configVar.getName();
if (RegisteredConfigVars.contains(name)) { if (RegisteredConfigVars.contains(name)) {
DuskConfigLog.fatal("Tried to register CVar {} twice!", name); DuskConfigLog.fatal("Tried to register CVar {} twice!", name);
} }
RegisteredConfigVars[name] = &configVar; RegisteredConfigVars[name] = &configVar;
configVar.markRegistered(); configVar.markRegistered();
const auto unregPair = UnregisteredConfigVars.find(name);
if (unregPair != UnregisteredConfigVars.end()) {
const auto value = std::move(unregPair->second);
UnregisteredConfigVars.erase(name);
try {
configVar.getImpl()->loadFromJson(configVar, value);
} catch (std::exception& e) {
DuskConfigLog.error("Failed to load key '{}' from config value: {}", name, e.what());
}
}
const auto overridePair = UnregisteredConfigVarOverrides.find(name);
if (overridePair != UnregisteredConfigVarOverrides.end()) {
try {
configVar.getImpl()->loadFromArg(configVar, overridePair->second);
} catch (std::exception& e) {
DuskConfigLog.error("Failed to load key '{}' from override arg: {}", name, e.what());
}
}
}
void unregister(ConfigVarBase& configVar) {
const std::string_view name = configVar.getName();
const auto it = RegisteredConfigVars.find(name);
if (it == RegisteredConfigVars.end() || it->second != &configVar) {
DuskConfigLog.fatal("Tried to unregister CVar '{}' that is not registered!", name);
}
const auto layer = configVar.getLayer();
if (layer == ConfigVarLayer::Value || layer == ConfigVarLayer::Speedrun) {
UnregisteredConfigVars.insert_or_assign(
std::string{name}, configVar.getImpl()->dumpToJson(configVar));
}
RegisteredConfigVars.erase(it);
configVar.unmarkRegistered();
} }
void ConfigVarBase::markRegistered() { void ConfigVarBase::markRegistered() {
@@ -405,21 +452,24 @@ void ConfigVarBase::markRegistered() {
registered = true; registered = true;
} }
void dusk::config::FinishRegistration() { void ConfigVarBase::unmarkRegistered() {
RegistrationDone = true; if (!registered)
abort();
registered = false;
} }
void dusk::config::LoadFromUserPreferences() { void load_from_user_preferences() {
const auto configJsonPath = GetConfigJsonPath(); const auto configJsonPath = GetConfigJsonPath();
if (configJsonPath.empty()) { if (configJsonPath.empty()) {
return; return;
} }
const auto configPathString = io::fs_path_to_string(configJsonPath); const auto configPathString = io::fs_path_to_string(configJsonPath);
LoadFromFileName(configPathString.c_str()); load_from_file_name(configPathString.c_str());
} }
static void LoadFromPath(const char* path) { static void LoadFromPath(const char* path) {
auto data = dusk::io::FileStream::ReadAllBytes(path); auto data = io::FileStream::ReadAllBytes(path);
json j = json::parse(data); json j = json::parse(data);
if (!j.is_object()) { if (!j.is_object()) {
@@ -427,11 +477,16 @@ static void LoadFromPath(const char* path) {
return; return;
} }
UnregisteredConfigVars.clear();
for (const auto& el : j.items()) { for (const auto& el : j.items()) {
const auto& key = el.key(); const auto& key = el.key();
auto configVar = RegisteredConfigVars.find(key); auto configVar = RegisteredConfigVars.find(key);
if (configVar == RegisteredConfigVars.end()) { if (configVar == RegisteredConfigVars.end()) {
DuskConfigLog.error("Unknown key '{}' found in config!", key); DuskConfigLog.debug("Unknown key '{}' found in config! If this gets registered later, "
"that's acceptable!",
key);
UnregisteredConfigVars.emplace(key, el.value());
continue; continue;
} }
@@ -443,11 +498,7 @@ static void LoadFromPath(const char* path) {
} }
} }
void dusk::config::LoadFromFileName(const char* path) { void load_from_file_name(const char* path) {
if (!RegistrationDone) {
DuskConfigLog.fatal("Registration not finished yet!");
}
DuskConfigLog.info("Loading config from '{}'", path); DuskConfigLog.info("Loading config from '{}'", path);
try { try {
@@ -465,7 +516,21 @@ void dusk::config::LoadFromFileName(const char* path) {
} }
} }
void dusk::config::Save() { void load_arg_override(std::string_view name, std::string_view value) {
const auto cVar = GetConfigVar(name);
if (!cVar) {
UnregisteredConfigVarOverrides.emplace(name, value);
return;
}
try {
cVar->getImpl()->loadFromArg(*cVar, value);
} catch (const std::exception& e) {
DuskLog.fatal("Unable to parse: '{}': {}", value, e.what());
}
}
void save() {
const auto configJsonPath = GetConfigJsonPath(); const auto configJsonPath = GetConfigJsonPath();
if (configJsonPath.empty()) { if (configJsonPath.empty()) {
return; return;
@@ -483,6 +548,10 @@ void dusk::config::Save() {
} }
} }
for (const auto& pair : UnregisteredConfigVars) {
j[pair.first] = pair.second;
}
try { try {
const auto tempConfigJsonPath = GetTempConfigJsonPath(configJsonPath); const auto tempConfigJsonPath = GetTempConfigJsonPath(configJsonPath);
io::FileStream::WriteAllText(tempConfigJsonPath, j.dump(4)); io::FileStream::WriteAllText(tempConfigJsonPath, j.dump(4));
@@ -492,14 +561,14 @@ void dusk::config::Save() {
} }
} }
void dusk::config::ClearAllActionBindings(int port) { void ClearAllActionBindings(int port) {
for (auto& actionBinding : getActionBinds() | std::views::values) { for (auto& actionBinding : getActionBinds() | std::views::values) {
actionBinding.configVars->at(port).setValue(PAD_NATIVE_BUTTON_INVALID); actionBinding.configVars->at(port).setValue(PAD_NATIVE_BUTTON_INVALID);
} }
Save(); save();
} }
ConfigVarBase* dusk::config::GetConfigVar(std::string_view name) { ConfigVarBase* GetConfigVar(std::string_view name) {
const auto configVar = RegisteredConfigVars.find(name); const auto configVar = RegisteredConfigVars.find(name);
if (configVar != RegisteredConfigVars.end()) { if (configVar != RegisteredConfigVars.end()) {
return configVar->second; return configVar->second;
@@ -508,8 +577,69 @@ ConfigVarBase* dusk::config::GetConfigVar(std::string_view name) {
return nullptr; return nullptr;
} }
void dusk::config::EnumerateRegistered(std::function<void(ConfigVarBase&)> callback) { void EnumerateRegistered(std::function<void(ConfigVarBase&)> callback) {
for (auto& pair : RegisteredConfigVars) { for (auto& pair : RegisteredConfigVars) {
callback(*pair.second); callback(*pair.second);
} }
} }
Subscription subscribe(std::string_view name, ChangeCallback callback) {
const auto token = s_nextChangeToken++;
s_changeSubscriptions[std::string{name}].push_back({token, std::move(callback)});
s_changeTokenNames.emplace(token, std::string{name});
return token;
}
void unsubscribe(Subscription token) {
const auto nameIt = s_changeTokenNames.find(token);
if (nameIt == s_changeTokenNames.end()) {
DuskConfigLog.fatal("Tried to unsubscribe unknown change token {}!", token);
}
const auto subsIt = s_changeSubscriptions.find(nameIt->second);
auto& subscriptions = subsIt->second;
std::erase_if(
subscriptions, [token](const ChangeSubscription& sub) { return sub.token == token; });
if (subscriptions.empty()) {
s_changeSubscriptions.erase(subsIt);
}
s_changeTokenNames.erase(nameIt);
}
bool ConfigVarBase::has_subscribers() const {
return s_changeSubscriptions.contains(name);
}
void ConfigVarBase::notify_changed(const void* previousValue) {
const auto subsIt = s_changeSubscriptions.find(name);
if (subsIt == s_changeSubscriptions.end()) {
return;
}
if (std::ranges::find(s_activeChangeNotifications, name) != s_activeChangeNotifications.end()) {
DuskConfigLog.error("Recursive change notification for CVar '{}' suppressed", name);
return;
}
s_activeChangeNotifications.push_back(name);
// Copied so callbacks can subscribe/unsubscribe safely.
const auto subscriptions = subsIt->second;
for (const auto& sub : subscriptions) {
sub.callback(*this, previousValue);
}
s_activeChangeNotifications.pop_back();
}
void shutdown() {
for (auto& pair : RegisteredConfigVars) {
pair.second->unmarkRegistered();
}
RegisteredConfigVars.clear();
UnregisteredConfigVars.clear();
UnregisteredConfigVarOverrides.clear();
s_changeSubscriptions.clear();
s_changeTokenNames.clear();
s_activeChangeNotifications.clear();
}
} // namespace dusk::config
+4 -4
View File
@@ -9,7 +9,7 @@ namespace dusk::config {
bool copy = var.getValue(); bool copy = var.getValue();
if (ImGui::Checkbox(title, &copy)) { if (ImGui::Checkbox(title, &copy)) {
var.setValue(copy); var.setValue(copy);
Save(); save();
return true; return true;
} }
@@ -20,7 +20,7 @@ namespace dusk::config {
float val = var; float val = var;
if (ImGui::SliderFloat(label, &val, v_min, v_max, format, flags)) { if (ImGui::SliderFloat(label, &val, v_min, v_max, format, flags)) {
var.setValue(val); var.setValue(val);
Save(); save();
return true; return true;
} }
@@ -31,7 +31,7 @@ namespace dusk::config {
int val = var; int val = var;
if (ImGui::SliderInt(label, &val, v_min, v_max, format, flags)) { if (ImGui::SliderInt(label, &val, v_min, v_max, format, flags)) {
var.setValue(val); var.setValue(val);
Save(); save();
return true; return true;
} }
@@ -42,7 +42,7 @@ namespace dusk::config {
bool copy = p_selected.getValue(); bool copy = p_selected.getValue();
if (ImGui::MenuItem(label, shortcut, &copy, enabled)) { if (ImGui::MenuItem(label, shortcut, &copy, enabled)) {
p_selected.setValue(copy); p_selected.setValue(copy);
Save(); save();
return true; return true;
} }
+2 -30
View File
@@ -254,7 +254,7 @@ namespace dusk {
if (ImGui::IsKeyPressed(ImGuiKey_F11)) { if (ImGui::IsKeyPressed(ImGuiKey_F11)) {
getSettings().video.enableFullscreen.setValue(!getSettings().video.enableFullscreen); getSettings().video.enableFullscreen.setValue(!getSettings().video.enableFullscreen);
VISetWindowFullscreen(getSettings().video.enableFullscreen); VISetWindowFullscreen(getSettings().video.enableFullscreen);
config::Save(); config::save();
} }
if (getSettings().game.enableResetKeybind && ImGui::GetIO().KeyCtrl && if (getSettings().game.enableResetKeybind && ImGui::GetIO().KeyCtrl &&
@@ -336,7 +336,7 @@ namespace dusk {
if constexpr (SupportsProcessRestart) { if constexpr (SupportsProcessRestart) {
if (ImGui::Button("Retry (Auto backend)")) { if (ImGui::Button("Retry (Auto backend)")) {
getSettings().backend.graphicsBackend.setValue("auto"); getSettings().backend.graphicsBackend.setValue("auto");
config::Save(); config::save();
RestartRequested = true; RestartRequested = true;
IsRunning = false; IsRunning = false;
} }
@@ -375,7 +375,6 @@ namespace dusk {
void ImGuiConsole::PostDraw() { void ImGuiConsole::PostDraw() {
m_menuTools.afterDraw(); m_menuTools.afterDraw();
ShowPipelineProgress();
} }
void ImGuiConsole::UpdateDragScroll() { void ImGuiConsole::UpdateDragScroll() {
@@ -524,31 +523,4 @@ namespace dusk {
return false; return false;
} }
void ImGuiConsole::ShowPipelineProgress() {
const auto* stats = aurora_get_stats();
const u32 queuedPipelines = stats->queuedPipelines;
if (queuedPipelines == 0 || !getSettings().backend.showPipelineCompilation) {
return;
}
const u32 createdPipelines = stats->createdPipelines;
const u32 totalPipelines = queuedPipelines + createdPipelines;
const auto* viewport = ImGui::GetMainViewport();
const auto padding = viewport->WorkPos.y + 10.f;
const auto halfWidth = viewport->GetWorkCenter().x;
ImGui::SetNextWindowPos(ImVec2{halfWidth, padding}, ImGuiCond_Always, ImVec2{0.5f, 0.f});
ImGui::SetNextWindowSize(ImVec2{halfWidth, 0.f}, ImGuiCond_Always);
ImGui::SetNextWindowBgAlpha(0.65f);
ImGui::Begin("Pipelines", nullptr,
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing);
const auto percent = static_cast<float>(createdPipelines) / static_cast<float>(totalPipelines);
const auto progressStr = fmt::format("Processing pipelines: {} / {}", createdPipelines, totalPipelines);
const auto textSize = ImGui::CalcTextSize(progressStr.data(), progressStr.data() + progressStr.size());
ImGui::NewLine();
ImGui::SameLine(ImGui::GetWindowWidth() / 2.f - textSize.x + textSize.x / 2.f);
ImGuiStringViewText(progressStr);
ImGui::ProgressBar(percent);
ImGui::End();
}
} }
-1
View File
@@ -35,7 +35,6 @@ private:
// Keep always last // Keep always last
ImGuiMenuTools m_menuTools; ImGuiMenuTools m_menuTools;
void ShowPipelineProgress();
void UpdateDragScroll(); void UpdateDragScroll();
}; };
+1 -1
View File
@@ -73,7 +73,7 @@ namespace dusk {
bool disableWaterRefraction = getSettings().game.disableWaterRefraction; bool disableWaterRefraction = getSettings().game.disableWaterRefraction;
if (ImGui::Checkbox("Disable Water Refraction", &disableWaterRefraction)) { if (ImGui::Checkbox("Disable Water Refraction", &disableWaterRefraction)) {
getSettings().game.disableWaterRefraction.setValue(disableWaterRefraction); getSettings().game.disableWaterRefraction.setValue(disableWaterRefraction);
config::Save(); config::save();
} }
ImGui::Checkbox("Enable LOD Bias", &aurora::gx::enableLodBias); ImGui::Checkbox("Enable LOD Bias", &aurora::gx::enableLodBias);
ImGui::EndMenu(); ImGui::EndMenu();
+143 -15
View File
@@ -1,16 +1,34 @@
#include "dusk/menu_pointer.h" #include "dusk/menu_pointer.h"
#include "m_Do/m_Do_graphic.h"
#include "d/d_pane_class.h" #include "d/d_pane_class.h"
#include "dusk/settings.h" #include "dusk/settings.h"
#include "m_Do/m_Do_graphic.h"
#include <aurora/rmlui.hpp> #include <aurora/rmlui.hpp>
#include <dolphin/pad.h> #include <dolphin/pad.h>
#include <algorithm> #include <algorithm>
#include <chrono>
namespace dusk::menu_pointer { namespace dusk::menu_pointer {
namespace { namespace {
using Clock = std::chrono::steady_clock;
constexpr auto kTapMaxDuration = std::chrono::milliseconds(300);
constexpr f32 kTapMoveThresholdDp = 12.0f;
struct Gesture {
bool active = false;
bool movedTooFar = false;
bool crossedTarget = false;
bool pressTargetValid = false;
Context pressContext = Context::None;
TargetId pressTarget = InvalidTarget;
f32 startX = 0.0f;
f32 startY = 0.0f;
Clock::time_point startedAt{};
};
State s_state; State s_state;
bool s_clickConsumed = false; bool s_clickConsumed = false;
Context s_lastContext = Context::None; Context s_lastContext = Context::None;
@@ -27,7 +45,14 @@ s32 s_mouseButton = -1;
u32 s_suppressedPadHoldMask = 0; u32 s_suppressedPadHoldMask = 0;
u32 s_suppressedPadNextReadMask = 0; u32 s_suppressedPadNextReadMask = 0;
Context s_deferredActivationContext = Context::None; Context s_deferredActivationContext = Context::None;
u8 s_deferredActivationTarget = 0xFF; TargetId s_deferredActivationTarget = InvalidTarget;
Gesture s_gesture;
bool s_hoverTargetValid = false;
TargetId s_hoverTarget = InvalidTarget;
bool s_clickPending = false;
Context s_clickContext = Context::None;
TargetId s_clickTarget = InvalidTarget;
bool s_clickTargetValid = false;
s32 scancode_from_rml_button(s32 button) noexcept { s32 scancode_from_rml_button(s32 button) noexcept {
switch (button) { switch (button) {
@@ -104,6 +129,37 @@ void suppress_pad_for_mouse_button(s32 button, bool held) noexcept {
} }
} }
f32 tap_move_threshold() noexcept {
auto* context = aurora::rmlui::get_context();
if (context == nullptr) {
return kTapMoveThresholdDp;
}
return kTapMoveThresholdDp * std::max(context->GetDensityIndependentPixelRatio(), 1.0f);
}
void update_gesture_movement(f32 x, f32 y) noexcept {
if (!s_gesture.active || s_gesture.movedTooFar) {
return;
}
const f32 dx = x - s_gesture.startX;
const f32 dy = y - s_gesture.startY;
const f32 threshold = tap_move_threshold();
if (dx * dx + dy * dy > threshold * threshold) {
s_gesture.movedTooFar = true;
}
}
void clear_click_state() noexcept {
s_clickConsumed = false;
s_clickPending = false;
s_clickContext = Context::None;
s_clickTarget = InvalidTarget;
s_clickTargetValid = false;
s_state.clicked = false;
}
void set_position_from_rml(f32 x, f32 y) noexcept { void set_position_from_rml(f32 x, f32 y) noexcept {
auto* context = aurora::rmlui::get_context(); auto* context = aurora::rmlui::get_context();
if (context == nullptr) { if (context == nullptr) {
@@ -121,7 +177,7 @@ void set_position_from_rml(f32 x, f32 y) noexcept {
void clear_input_state() noexcept { void clear_input_state() noexcept {
s_state = {}; s_state = {};
s_clickConsumed = false; clear_click_state();
s_lastDialogChoice = 0xFF; s_lastDialogChoice = 0xFF;
s_currentDialogChoice = 0xFF; s_currentDialogChoice = 0xFF;
s_lastDialogChoiceValid = false; s_lastDialogChoiceValid = false;
@@ -134,7 +190,10 @@ void clear_input_state() noexcept {
s_suppressedPadHoldMask = 0; s_suppressedPadHoldMask = 0;
s_suppressedPadNextReadMask = 0; s_suppressedPadNextReadMask = 0;
s_deferredActivationContext = Context::None; s_deferredActivationContext = Context::None;
s_deferredActivationTarget = 0xFF; s_deferredActivationTarget = InvalidTarget;
s_gesture = {};
s_hoverTargetValid = false;
s_hoverTarget = InvalidTarget;
} }
} // namespace } // namespace
@@ -144,8 +203,6 @@ bool handle_fallthrough_pointer(f32 x, f32 y, Phase phase, bool touch, s32 mouse
return false; return false;
} }
s_clickConsumed = false;
if (!touch) { if (!touch) {
if (phase == Phase::Press) { if (phase == Phase::Press) {
if (!mouse_button_is_menu_confirm(mouseButton)) { if (!mouse_button_is_menu_confirm(mouseButton)) {
@@ -174,21 +231,41 @@ bool handle_fallthrough_pointer(f32 x, f32 y, Phase phase, bool touch, s32 mouse
} }
if (phase != Phase::Cancel) { if (phase != Phase::Cancel) {
update_gesture_movement(x, y);
set_position_from_rml(x, y); set_position_from_rml(x, y);
} }
s_state.touch = touch; s_state.touch = touch;
switch (phase) { switch (phase) {
case Phase::Press: case Phase::Press:
clear_click_state();
s_gesture = {
.active = true,
.startX = x,
.startY = y,
.startedAt = Clock::now(),
};
s_state.down = true; s_state.down = true;
s_state.pressed = true; s_state.pressed = true;
break; break;
case Phase::Release: case Phase::Release: {
const bool shortEnough =
s_gesture.active && Clock::now() - s_gesture.startedAt <= kTapMaxDuration;
const bool stillEnough = s_gesture.active && !s_gesture.movedTooFar;
const bool targetClean = s_gesture.active && !s_gesture.crossedTarget;
s_clickContext = s_gesture.pressContext;
s_clickTarget = s_gesture.pressTarget;
s_clickTargetValid = s_gesture.pressTargetValid;
s_clickPending = shortEnough && stillEnough && targetClean;
s_state.down = false; s_state.down = false;
s_state.released = true; s_state.released = true;
s_state.clicked = true; s_state.clicked = s_clickPending;
s_gesture = {};
break; break;
}
case Phase::Cancel: case Phase::Cancel:
clear_click_state();
s_gesture = {};
s_state.down = false; s_state.down = false;
break; break;
case Phase::Move: case Phase::Move:
@@ -211,6 +288,12 @@ void begin_game_frame() noexcept {
} }
void end_game_frame() noexcept { void end_game_frame() noexcept {
if (s_gesture.active && s_gesture.pressTargetValid &&
s_currentContext == s_gesture.pressContext && !s_hoverTargetValid)
{
s_gesture.crossedTarget = true;
}
s_lastContext = s_currentContext; s_lastContext = s_currentContext;
s_lastDialogChoice = s_currentDialogChoice; s_lastDialogChoice = s_currentDialogChoice;
s_lastDialogChoiceValid = s_currentDialogChoiceValid; s_lastDialogChoiceValid = s_currentDialogChoiceValid;
@@ -222,10 +305,16 @@ void end_game_frame() noexcept {
s_state.valid = false; s_state.valid = false;
} }
s_clickConsumed = false; s_clickConsumed = false;
s_clickPending = false;
s_clickContext = Context::None;
s_clickTarget = InvalidTarget;
s_clickTargetValid = false;
s_hoverTargetValid = false;
s_hoverTarget = InvalidTarget;
} }
void begin_context(Context context) noexcept { void begin_context(Context context) noexcept {
if (context == Context::None) { if (context == Context::None || !enabled()) {
return; return;
} }
@@ -237,7 +326,11 @@ void begin_context(Context context) noexcept {
s_suppressedPadHoldMask = 0; s_suppressedPadHoldMask = 0;
s_suppressedPadNextReadMask = 0; s_suppressedPadNextReadMask = 0;
s_deferredActivationContext = Context::None; s_deferredActivationContext = Context::None;
s_deferredActivationTarget = 0xFF; s_deferredActivationTarget = InvalidTarget;
s_gesture = {};
s_hoverTargetValid = false;
s_hoverTarget = InvalidTarget;
clear_click_state();
} }
s_currentContext = context; s_currentContext = context;
@@ -259,15 +352,50 @@ const State& state() noexcept {
return s_state; return s_state;
} }
void set_hover_target(TargetId target) noexcept {
s_hoverTargetValid = true;
s_hoverTarget = target;
if (s_gesture.active && !s_gesture.pressTargetValid && s_state.down) {
s_gesture.pressContext = s_currentContext;
s_gesture.pressTarget = target;
s_gesture.pressTargetValid = true;
}
if (s_gesture.active && s_gesture.pressTargetValid &&
(s_currentContext != s_gesture.pressContext || target != s_gesture.pressTarget))
{
s_gesture.crossedTarget = true;
}
}
bool click_matches_hover_target() noexcept {
if (!s_clickPending || !s_hoverTargetValid) {
return false;
}
if (!s_clickTargetValid) {
return true;
}
return s_currentContext == s_clickContext && s_hoverTarget == s_clickTarget;
}
bool consume_click() noexcept { bool consume_click() noexcept {
if (!s_state.clicked || s_clickConsumed) { if (s_clickConsumed || !click_matches_hover_target()) {
return false; return false;
} }
s_clickConsumed = true; s_clickConsumed = true;
s_clickPending = false;
s_state.clicked = false;
return true; return true;
} }
bool peek_click() noexcept {
return !s_clickConsumed && click_matches_hover_target();
}
void set_dialog_choice(u8 choice, bool clicked) noexcept { void set_dialog_choice(u8 choice, bool clicked) noexcept {
s_currentDialogChoice = choice; s_currentDialogChoice = choice;
s_currentDialogChoiceValid = true; s_currentDialogChoiceValid = true;
@@ -300,18 +428,18 @@ bool consume_dialog_click(u8& choice) noexcept {
return false; return false;
} }
void defer_activation(Context context, u8 target) noexcept { void defer_activation(Context context, TargetId target) noexcept {
s_deferredActivationContext = context; s_deferredActivationContext = context;
s_deferredActivationTarget = target; s_deferredActivationTarget = target;
} }
bool consume_deferred_activation(Context context, u8 target) noexcept { bool consume_deferred_activation(Context context, TargetId target) noexcept {
if (s_deferredActivationContext != context || s_deferredActivationTarget != target) { if (s_deferredActivationContext != context || s_deferredActivationTarget != target) {
return false; return false;
} }
s_deferredActivationContext = Context::None; s_deferredActivationContext = Context::None;
s_deferredActivationTarget = 0xFF; s_deferredActivationTarget = InvalidTarget;
return true; return true;
} }
@@ -321,7 +449,7 @@ void clear_deferred_activation(Context context) noexcept {
} }
s_deferredActivationContext = Context::None; s_deferredActivationContext = Context::None;
s_deferredActivationTarget = 0xFF; s_deferredActivationTarget = InvalidTarget;
} }
u32 suppressed_pad_buttons(u32 port) noexcept { u32 suppressed_pad_buttons(u32 port) noexcept {
+74 -52
View File
@@ -1,54 +1,69 @@
#include "dusk/mouse.h" #include "dusk/mouse.h"
#include "d/actor/d_a_alink.h"
#include "d/d_com_inf_game.h"
#include "dusk/menu_pointer.h" #include "dusk/menu_pointer.h"
#include "dusk/settings.h" #include "dusk/settings.h"
#include "dusk/ui/ui.hpp" #include "dusk/ui/ui.hpp"
#include "d/actor/d_a_alink.h"
#include "d/d_com_inf_game.h"
#include <aurora/lib/window.hpp>
#include <imgui.h>
#include <SDL3/SDL_mouse.h> #include <SDL3/SDL_mouse.h>
#include <SDL3/SDL_video.h> #include <SDL3/SDL_video.h>
#include <aurora/lib/window.hpp>
#include <imgui.h>
#include <chrono>
namespace dusk::mouse { namespace dusk::mouse {
namespace { namespace {
constexpr float kMousePixelToRad = 0.0025f; using Clock = std::chrono::steady_clock;
constexpr int kIdleHideFrames = 99; // Approx. 3 seconds with 33ms ticks
float s_aim_yaw_rad = 0.0f; constexpr float kMousePixelToRad = 0.0025f;
float s_aim_pitch_rad = 0.0f; constexpr auto kCursorIdleDuration = std::chrono::seconds(1);
float s_camera_yaw_rad = 0.0f;
float s_aim_yaw_rad = 0.0f;
float s_aim_pitch_rad = 0.0f;
float s_camera_yaw_rad = 0.0f;
float s_camera_pitch_rad = 0.0f; float s_camera_pitch_rad = 0.0f;
int s_idle_frames = 0; Clock::time_point s_last_cursor_motion = Clock::now();
void reset_deltas() { void reset_deltas() {
s_aim_yaw_rad = s_aim_pitch_rad = 0.0f; s_aim_yaw_rad = s_aim_pitch_rad = 0.0f;
s_camera_yaw_rad = s_camera_pitch_rad = 0.0f; s_camera_yaw_rad = s_camera_pitch_rad = 0.0f;
} }
bool queryMouseAimContext() { bool query_mouse_aim_context() {
return getSettings().game.enableMouseAim.getValue() && dCamera_c::isAimActive(); return getSettings().game.enableMouseAim.getValue() && dCamera_c::isAimActive();
} }
bool wantMouseCapture() { bool want_mouse_capture() {
return getSettings().game.enableMouseCamera.getValue() || queryMouseAimContext(); return getSettings().game.enableMouseCamera.getValue() || query_mouse_aim_context();
} }
bool isWindowFocused(SDL_Window* window) { bool mouse_input_enabled() {
const auto& game = getSettings().game;
return game.enableMouseAim.getValue() || game.enableMouseCamera.getValue();
}
bool is_window_focused(SDL_Window* window) {
if (window == nullptr) { if (window == nullptr) {
return false; return false;
} }
return (SDL_GetWindowFlags(window) & SDL_WINDOW_INPUT_FOCUS) != 0; return (SDL_GetWindowFlags(window) & SDL_WINDOW_INPUT_FOCUS) != 0;
} }
bool shouldCaptureMouse(SDL_Window* window) { bool imgui_windows_visible() {
if (window == nullptr || ui::any_document_visible() || menu_pointer::active()) { return ImGui::GetIO().MetricsRenderWindows > 0;
return false;
}
return wantMouseCapture() && isWindowFocused(window);
} }
bool syncCaptureState(SDL_Window* window, bool should_capture) { bool should_capture_mouse(SDL_Window* window) {
if (window == nullptr || ui::any_document_visible() || imgui_windows_visible() ||
menu_pointer::active())
{
return false;
}
return want_mouse_capture() && is_window_focused(window);
}
bool sync_capture_state(SDL_Window* window, bool should_capture) {
if (window == nullptr) { if (window == nullptr) {
reset_deltas(); reset_deltas();
return false; return false;
@@ -78,7 +93,7 @@ bool syncCaptureState(SDL_Window* window, bool should_capture) {
return is_captured; return is_captured;
} }
void accumulateDeltas(float mx_rel, float my_rel, bool camera_active, bool aim_active) { void accumulate_deltas(float mx_rel, float my_rel, bool camera_active, bool aim_active) {
const auto& game = getSettings().game; const auto& game = getSettings().game;
const bool mirror_mode = game.enableMirrorMode.getValue(); const bool mirror_mode = game.enableMirrorMode.getValue();
const bool invert_y = game.invertMouseY.getValue(); const bool invert_y = game.invertMouseY.getValue();
@@ -114,57 +129,62 @@ void set_cursor_visible(bool visible) {
} }
} }
void update_cursor_visibility(SDL_Window* window, bool captured) { bool cursor_idle() {
if (window == nullptr || !isWindowFocused(window)) { return Clock::now() - s_last_cursor_motion >= kCursorIdleDuration;
return; }
}
bool should_show_cursor(bool captured) {
if (captured) { if (captured) {
s_idle_frames = 0; return false;
set_cursor_visible(false); }
if (ui::any_document_visible()) {
return true;
}
if (imgui_windows_visible()) {
return true;
}
if (menu_pointer::enabled() && menu_pointer::active()) {
return true;
}
if (mouse_input_enabled()) {
return false;
}
return !cursor_idle();
}
void update_cursor_visibility(SDL_Window* window, bool captured) {
if (window == nullptr || !is_window_focused(window)) {
return; return;
} }
const ImGuiIO& io = ImGui::GetIO(); set_cursor_visible(should_show_cursor(captured));
if (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f) {
s_idle_frames = 0;
set_cursor_visible(true);
return;
}
if (s_idle_frames < kIdleHideFrames) {
++s_idle_frames;
set_cursor_visible(true);
} else {
set_cursor_visible(false);
}
} }
} // namespace } // namespace
void read() { void read() {
SDL_Window* window = aurora::window::get_sdl_window(); SDL_Window* window = aurora::window::get_sdl_window();
const bool capture_active = syncCaptureState(window, shouldCaptureMouse(window)); const bool capture_active = sync_capture_state(window, should_capture_mouse(window));
update_cursor_visibility(window, capture_active); update_cursor_visibility(window, capture_active);
if (!capture_active) { if (!capture_active) {
return; return;
} }
const bool aim_active = capture_active && queryMouseAimContext(); const bool aim_active = capture_active && query_mouse_aim_context();
const bool camera_active = capture_active && getSettings().game.enableMouseCamera; const bool camera_active = capture_active && getSettings().game.enableMouseCamera;
float mx_rel = 0.0f; float mx_rel = 0.0f;
float my_rel = 0.0f; float my_rel = 0.0f;
SDL_GetRelativeMouseState(&mx_rel, &my_rel); SDL_GetRelativeMouseState(&mx_rel, &my_rel);
accumulateDeltas(mx_rel, my_rel, camera_active, aim_active); accumulate_deltas(mx_rel, my_rel, camera_active, aim_active);
} }
void getAimDeltas(float& out_yaw, float& out_pitch) { void get_aim_deltas(float& out_yaw, float& out_pitch) {
out_yaw = s_aim_yaw_rad; out_yaw = s_aim_yaw_rad;
out_pitch = s_aim_pitch_rad; out_pitch = s_aim_pitch_rad;
} }
void getCameraDeltas(float& out_yaw, float& out_pitch) { void get_camera_deltas(float& out_yaw, float& out_pitch) {
out_yaw = 0.0f; out_yaw = 0.0f;
out_pitch = 0.0f; out_pitch = 0.0f;
@@ -178,26 +198,28 @@ void getCameraDeltas(float& out_yaw, float& out_pitch) {
void handle_event(const SDL_Event& event) noexcept { void handle_event(const SDL_Event& event) noexcept {
switch (event.type) { switch (event.type) {
case SDL_EVENT_MOUSE_MOTION:
s_last_cursor_motion = Clock::now();
break;
case SDL_EVENT_WINDOW_FOCUS_LOST: case SDL_EVENT_WINDOW_FOCUS_LOST:
onFocusLost(); on_focus_lost();
break; break;
case SDL_EVENT_WINDOW_FOCUS_GAINED: case SDL_EVENT_WINDOW_FOCUS_GAINED:
onFocusGained(); on_focus_gained();
break; break;
} }
} }
void onFocusLost() { void on_focus_lost() {
SDL_Window* window = aurora::window::get_sdl_window(); SDL_Window* window = aurora::window::get_sdl_window();
if (window != nullptr) { if (window != nullptr) {
syncCaptureState(window, false); sync_capture_state(window, false);
} }
s_idle_frames = 0;
set_cursor_visible(true); set_cursor_visible(true);
} }
void onFocusGained() { void on_focus_gained() {
SDL_Window* window = aurora::window::get_sdl_window(); SDL_Window* window = aurora::window::get_sdl_window();
syncCaptureState(window, shouldCaptureMouse(window)); sync_capture_state(window, should_capture_mouse(window));
} }
} // namespace dusk::mouse } // namespace dusk::mouse
+5 -3
View File
@@ -1,5 +1,6 @@
#include "dusk/settings.h" #include "dusk/settings.h"
#include "dusk/config.hpp" #include "dusk/config.hpp"
#include <aurora/aurora.h>
namespace dusk { namespace dusk {
@@ -96,6 +97,7 @@ UserSettings g_userSettings = {
.invertMouseY {"game.invertMouseY", false}, .invertMouseY {"game.invertMouseY", false},
.freeCamera {"game.freeCamera", false}, .freeCamera {"game.freeCamera", false},
.enableTouchControls {"game.enableTouchControls", false}, .enableTouchControls {"game.enableTouchControls", false},
.touchTargeting {"game.touchTargeting", TouchTargeting::Hybrid},
.enableMenuPointer {"game.enableMenuPointer", true}, .enableMenuPointer {"game.enableMenuPointer", true},
.touchControlsLayout {"game.touchControlsLayout", ui::ControlLayout{}}, .touchControlsLayout {"game.touchControlsLayout", ui::ControlLayout{}},
.invertCameraXAxis {"game.invertCameraXAxis", false}, .invertCameraXAxis {"game.invertCameraXAxis", false},
@@ -160,7 +162,6 @@ UserSettings g_userSettings = {
.isoVerification {"backend.isoVerification", DiscVerificationState::Unknown}, .isoVerification {"backend.isoVerification", DiscVerificationState::Unknown},
.graphicsBackend {"backend.graphicsBackend", "auto"}, .graphicsBackend {"backend.graphicsBackend", "auto"},
.skipPreLaunchUI {"backend.skipPreLaunchUI", false}, .skipPreLaunchUI {"backend.skipPreLaunchUI", false},
.showPipelineCompilation {"backend.showPipelineCompilation", false},
.wasPresetChosen {"backend.wasPresetChosen", false}, .wasPresetChosen {"backend.wasPresetChosen", false},
.checkForUpdates {"backend.checkForUpdates", true}, .checkForUpdates {"backend.checkForUpdates", true},
.cardFileType {"backend.cardFileType", static_cast<int>(CARD_GCIFOLDER)}, .cardFileType {"backend.cardFileType", static_cast<int>(CARD_GCIFOLDER)},
@@ -267,7 +268,8 @@ void registerSettings() {
Register(g_userSettings.game.touchCameraYSensitivity); Register(g_userSettings.game.touchCameraYSensitivity);
Register(g_userSettings.game.minimalHUD); Register(g_userSettings.game.minimalHUD);
Register(g_userSettings.game.hudScale); Register(g_userSettings.game.hudScale);
Register(g_userSettings.game.pauseOnFocusLost); Register(g_userSettings.game.pauseOnFocusLost,
[](const bool& value, const bool&) { aurora_set_pause_on_focus_lost(value); });
Register(g_userSettings.game.enableDiscordPresence); Register(g_userSettings.game.enableDiscordPresence);
Register(g_userSettings.game.bloomMode); Register(g_userSettings.game.bloomMode);
Register(g_userSettings.game.bloomMultiplier); Register(g_userSettings.game.bloomMultiplier);
@@ -330,6 +332,7 @@ void registerSettings() {
Register(g_userSettings.game.invertMouseY); Register(g_userSettings.game.invertMouseY);
Register(g_userSettings.game.freeCamera); Register(g_userSettings.game.freeCamera);
Register(g_userSettings.game.enableTouchControls); Register(g_userSettings.game.enableTouchControls);
Register(g_userSettings.game.touchTargeting);
Register(g_userSettings.game.enableMenuPointer); Register(g_userSettings.game.enableMenuPointer);
Register(g_userSettings.game.touchControlsLayout); Register(g_userSettings.game.touchControlsLayout);
Register(g_userSettings.game.debugFlyCam); Register(g_userSettings.game.debugFlyCam);
@@ -345,7 +348,6 @@ void registerSettings() {
Register(g_userSettings.backend.isoVerification); Register(g_userSettings.backend.isoVerification);
Register(g_userSettings.backend.graphicsBackend); Register(g_userSettings.backend.graphicsBackend);
Register(g_userSettings.backend.skipPreLaunchUI); Register(g_userSettings.backend.skipPreLaunchUI);
Register(g_userSettings.backend.showPipelineCompilation);
Register(g_userSettings.backend.wasPresetChosen); Register(g_userSettings.backend.wasPresetChosen);
Register(g_userSettings.backend.checkForUpdates); Register(g_userSettings.backend.checkForUpdates);
Register(g_userSettings.backend.cardFileType); Register(g_userSettings.backend.cardFileType);
+1 -1
View File
@@ -34,9 +34,9 @@ void resetForSpeedrunMode() {
getSettings().game.fastRoll.setSpeedrunValue(false); getSettings().game.fastRoll.setSpeedrunValue(false);
getSettings().game.fastSpinner.setSpeedrunValue(false); getSettings().game.fastSpinner.setSpeedrunValue(false);
getSettings().game.armorRupeeDrain.setSpeedrunValue(MagicArmorMode::NORMAL); getSettings().game.armorRupeeDrain.setSpeedrunValue(MagicArmorMode::NORMAL);
getSettings().game.invincibleEnemies.setSpeedrunValue(false);
getSettings().game.pauseOnFocusLost.setSpeedrunValue(false); getSettings().game.pauseOnFocusLost.setSpeedrunValue(false);
aurora_set_pause_on_focus_lost(false);
getSettings().backend.enableAdvancedSettings.setSpeedrunValue(false); getSettings().backend.enableAdvancedSettings.setSpeedrunValue(false);
getSettings().game.recordingMode.setSpeedrunValue(false); getSettings().game.recordingMode.setSpeedrunValue(false);
+3
View File
@@ -7,6 +7,9 @@ float s_pitch_dp = 0.0f;
} // namespace } // namespace
void add_delta(float yaw_dp, float pitch_dp) noexcept { void add_delta(float yaw_dp, float pitch_dp) noexcept {
if (getSettings().game.enableMirrorMode) {
yaw_dp *= -1.0;
}
s_yaw_dp += yaw_dp; s_yaw_dp += yaw_dp;
s_pitch_dp += pitch_dp; s_pitch_dp += pitch_dp;
} }
+5 -5
View File
@@ -278,7 +278,7 @@ ControllerConfigWindow::ControllerConfigWindow(bool prelaunch) {
void ControllerConfigWindow::hide(bool close) { void ControllerConfigWindow::hide(bool close) {
stop_rumble_test(); stop_rumble_test();
cancel_pending_binding(); cancel_pending_binding();
config::Save(); config::save();
Window::hide(close); Window::hide(close);
} }
@@ -403,7 +403,7 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) {
PADClearPort(port); PADClearPort(port);
PADSetKeyboardActive(static_cast<u32>(port), FALSE); PADSetKeyboardActive(static_cast<u32>(port), FALSE);
PADSerializeMappings(); PADSerializeMappings();
ClearAllActionBindings(port); config::ClearAllActionBindings(port);
refresh_controller_page(); refresh_controller_page();
}); });
@@ -417,7 +417,7 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) {
PADClearPort(port); PADClearPort(port);
PADSetKeyboardActive(static_cast<u32>(port), TRUE); PADSetKeyboardActive(static_cast<u32>(port), TRUE);
PADSerializeMappings(); PADSerializeMappings();
ClearAllActionBindings(port); config::ClearAllActionBindings(port);
}); });
const u32 controllerCount = PADCount(); const u32 controllerCount = PADCount();
@@ -439,7 +439,7 @@ void ControllerConfigWindow::render_page(Pane& pane, int port, Page page) {
PADSetKeyboardActive(static_cast<u32>(port), FALSE); PADSetKeyboardActive(static_cast<u32>(port), FALSE);
PADSetPortForIndex(i, port); PADSetPortForIndex(i, port);
PADSerializeMappings(); PADSerializeMappings();
ClearAllActionBindings(port); config::ClearAllActionBindings(port);
}); });
} }
break; break;
@@ -1125,7 +1125,7 @@ void ControllerConfigWindow::poll_pending_binding() {
return; return;
} }
mPendingActionBinding->setValue(button); mPendingActionBinding->setValue(button);
config::Save(); config::save();
finish_pending_binding(completedPort); finish_pending_binding(completedPort);
} }
return; return;
+4
View File
@@ -51,6 +51,8 @@ struct ControlProps {
float h = 0.0f; float h = 0.0f;
float scale = 1.0f; float scale = 1.0f;
ControlAnchor anchor = ControlAnchor::None; ControlAnchor anchor = ControlAnchor::None;
bool operator==(const ControlProps&) const = default;
}; };
struct ControlRect { struct ControlRect {
@@ -76,6 +78,8 @@ struct ControlLayout {
int version = Version; int version = Version;
std::map<std::string, ControlProps, std::less<> > controls; std::map<std::string, ControlProps, std::less<> > controls;
bool operator==(const ControlLayout&) const = default;
}; };
constexpr std::array<std::string_view, 9> kControlLayoutIds = { constexpr std::array<std::string_view, 9> kControlLayoutIds = {
+1 -1
View File
@@ -300,7 +300,7 @@ void GraphicsTuner::show() {
} }
void GraphicsTuner::hide(bool close) { void GraphicsTuner::hide(bool close) {
config::Save(); config::save();
mRoot->RemoveAttribute("open"); mRoot->RemoveAttribute("open");
if (close) { if (close) {
mPendingClose = true; mPendingClose = true;
+80 -13
View File
@@ -1,9 +1,9 @@
#include "overlay.hpp" #include "overlay.hpp"
#include "aurora/lib/logging.hpp" #include "aurora/lib/logging.hpp"
#include "controller_config.hpp"
#include "dusk/achievements.h" #include "dusk/achievements.h"
#include "dusk/action_bindings.h" #include "dusk/action_bindings.h"
#include "controller_config.hpp"
#include "dusk/livesplit.h" #include "dusk/livesplit.h"
#include "dusk/settings.h" #include "dusk/settings.h"
#include "dusk/speedrun.h" #include "dusk/speedrun.h"
@@ -33,6 +33,13 @@ const Rml::String kDocumentSource = R"RML(
</head> </head>
<body> <body>
<fps id="fps" /> <fps id="fps" />
<pipeline-progress id="pipeline-progress">
<pipeline-status>
<icon class="pipeline-spinner">&#xe9d0;</icon>
<span id="pipeline-progress-label" />
</pipeline-status>
<progress id="pipeline-progress-bar" />
</pipeline-progress>
<speedrun-timer id="speedrun-timer"> <speedrun-timer id="speedrun-timer">
<speedrun-rta id="speedrun-rta" /> <speedrun-rta id="speedrun-rta" />
<speedrun-igt id="speedrun-igt" /> <speedrun-igt id="speedrun-igt" />
@@ -48,6 +55,7 @@ constexpr std::array<std::pair<const char*, const char*>, 3> kAutoSaveLayers{{
}}; }};
constexpr auto kMenuNotificationDuration = std::chrono::milliseconds(2500); constexpr auto kMenuNotificationDuration = std::chrono::milliseconds(2500);
constexpr auto kPipelineProgressOpenDelay = std::chrono::milliseconds(250);
constexpr std::array<const char*, 4> kFpsCorners = {"tl", "tr", "bl", "br"}; constexpr std::array<const char*, 4> kFpsCorners = {"tl", "tr", "bl", "br"};
@@ -160,8 +168,8 @@ Rml::Element* create_menu_notification(Rml::Element* parent) {
Rml::String padButton{}; Rml::String padButton{};
SDL_Gamepad* gamepad = gamepad_for_port(PAD_CHAN0); SDL_Gamepad* gamepad = gamepad_for_port(PAD_CHAN0);
if (isActionBound(ActionBinds::OPEN_DUSKLIGHT_MENU, PAD_CHAN0) && gamepad != nullptr) { if (isActionBound(ActionBinds::OPEN_DUSKLIGHT_MENU, PAD_CHAN0) && gamepad != nullptr) {
padButton = native_button_name(gamepad, padButton = native_button_name(
getActionBindButton(ActionBinds::OPEN_DUSKLIGHT_MENU, PAD_CHAN0)); gamepad, getActionBindButton(ActionBinds::OPEN_DUSKLIGHT_MENU, PAD_CHAN0));
} else { } else {
padButton = back_button_name(); padButton = back_button_name();
} }
@@ -189,14 +197,26 @@ void remove_element(Rml::Element*& elem) noexcept {
} // namespace } // namespace
static std::string FormatTime(OSTime ticks) { static std::string FormatElapsedTime(OSTime ticksElapsed) {
OSCalendarTime t; using namespace std::chrono;
OSTicksToCalendarTime(ticks, &t);
return fmt::format("{0:02}:{1:02}:{2:02}.{3:03}", t.hour, t.min, t.sec, t.msec); milliseconds ms{OSTicksToMilliseconds(ticksElapsed)};
const hours hr = duration_cast<hours>(ms);
ms -= hr;
const minutes min = duration_cast<minutes>(ms);
ms -= min;
const seconds sec = duration_cast<seconds>(ms);
ms -= sec;
return fmt::format("{0:02}:{1:02}:{2:02}.{3:03}", hr.count(), min.count(), sec.count(), ms.count());
} }
Overlay::Overlay() : Document(kDocumentSource, true) { Overlay::Overlay() : Document(kDocumentSource, true) {
mFpsCounter = mDocument->GetElementById("fps"); mFpsCounter = mDocument->GetElementById("fps");
mPipelineProgress = mDocument->GetElementById("pipeline-progress");
mPipelineProgressLabel = mDocument->GetElementById("pipeline-progress-label");
mPipelineProgressBar = mDocument->GetElementById("pipeline-progress-bar");
mSpeedrunTimer = mDocument->GetElementById("speedrun-timer"); mSpeedrunTimer = mDocument->GetElementById("speedrun-timer");
mSpeedrunRta = mDocument->GetElementById("speedrun-rta"); mSpeedrunRta = mDocument->GetElementById("speedrun-rta");
mSpeedrunIgt = mDocument->GetElementById("speedrun-igt"); mSpeedrunIgt = mDocument->GetElementById("speedrun-igt");
@@ -258,6 +278,8 @@ void Overlay::update() {
} }
} }
update_pipeline_progress();
#if !(defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS && !TARGET_OS_MACCATALYST)) #if !(defined(__ANDROID__) || (defined(__APPLE__) && TARGET_OS_IOS && !TARGET_OS_MACCATALYST))
if (getSettings().game.speedrunMode && getSettings().game.liveSplitEnabled) { if (getSettings().game.speedrunMode && getSettings().game.liveSplitEnabled) {
dusk::speedrun::updateLiveSplit(); dusk::speedrun::updateLiveSplit();
@@ -304,12 +326,13 @@ void Overlay::update() {
if (getSettings().game.showSpeedrunRTATimer) { if (getSettings().game.showSpeedrunRTATimer) {
mSpeedrunRta->SetAttribute("open", ""); mSpeedrunRta->SetAttribute("open", "");
mSpeedrunRta->SetInnerRML(escape(fmt::format("RTA {}", FormatTime(elapsedTime)))); mSpeedrunRta->SetInnerRML(escape(fmt::format("RTA {}", FormatElapsedTime(elapsedTime))));
} else { } else {
mSpeedrunRta->RemoveAttribute("open"); mSpeedrunRta->RemoveAttribute("open");
} }
mSpeedrunIgt->SetInnerRML(escape(fmt::format("IGT {}", FormatTime(m_speedrunInfo.m_igtTimer)))); mSpeedrunIgt->SetInnerRML(
escape(fmt::format("IGT {}", FormatElapsedTime(m_speedrunInfo.m_igtTimer))));
} else { } else {
mSpeedrunTimer->RemoveAttribute("open"); mSpeedrunTimer->RemoveAttribute("open");
} }
@@ -373,10 +396,8 @@ void Overlay::update() {
std::chrono::duration<float>(clock::now() - mCurrentToastStartTime).count(); std::chrono::duration<float>(clock::now() - mCurrentToastStartTime).count();
const float ratio = duration > 0.0f ? std::clamp(elapsed / duration, 0.0f, 1.0f) : 1.0f; const float ratio = duration > 0.0f ? std::clamp(elapsed / duration, 0.0f, 1.0f) : 1.0f;
const auto remaining = 1.f - ratio; const auto remaining = 1.f - ratio;
Rml::ElementList list; if (auto* progress = mCurrentToast->QuerySelector("progress")) {
mDocument->GetElementsByTagName(list, "progress"); progress->SetAttribute("value", remaining);
for (auto* elem : list) {
elem->SetAttribute("value", remaining);
} }
if (remaining == 0.f) { if (remaining == 0.f) {
if (mCurrentToast->IsPseudoClassSet("done") || if (mCurrentToast->IsPseudoClassSet("done") ||
@@ -395,6 +416,52 @@ void Overlay::update() {
} }
} }
void Overlay::update_pipeline_progress() {
if (mPipelineProgress == nullptr || mPipelineProgressLabel == nullptr ||
mPipelineProgressBar == nullptr)
{
return;
}
const auto* stats = aurora_get_stats();
const uint32_t queuedPipelines = stats != nullptr ? stats->queuedPipelines : 0;
if (queuedPipelines == 0) {
mPipelineProgress->RemoveAttribute("open");
mPipelineProgressActive = false;
mPipelineBatchCreatedBase = 0;
mLastQueuedPipelines = 0;
return;
}
const uint32_t createdPipelines = stats->createdPipelines;
if (!mPipelineProgressActive || createdPipelines < mPipelineBatchCreatedBase) {
mPipelineProgressActive = true;
mPipelineBatchCreatedBase = createdPipelines;
mPipelineProgressStartTime = clock::now();
mLastQueuedPipelines = 0;
}
const uint32_t builtPipelines = createdPipelines - mPipelineBatchCreatedBase;
const uint32_t totalPipelines = queuedPipelines + builtPipelines;
const float progress = totalPipelines > 0 ? static_cast<float>(builtPipelines) /
static_cast<float>(totalPipelines) :
0.0f;
if (queuedPipelines != mLastQueuedPipelines) {
mLastQueuedPipelines = queuedPipelines;
const auto noun = queuedPipelines == 1 ? "pipeline" : "pipelines";
mPipelineProgressLabel->SetInnerRML(
escape(fmt::format("Building {} {}", queuedPipelines, noun)));
}
mPipelineProgressBar->SetAttribute("value", progress);
if (clock::now() >= mPipelineProgressStartTime + kPipelineProgressOpenDelay) {
mPipelineProgress->SetAttribute("open", "");
} else {
mPipelineProgress->RemoveAttribute("open");
}
}
bool Overlay::handle_nav_command(Rml::Event& event, NavCommand cmd) { bool Overlay::handle_nav_command(Rml::Event& event, NavCommand cmd) {
Log.warn("Overlay received nav command: {}", magic_enum::enum_name(cmd)); Log.warn("Overlay received nav command: {}", magic_enum::enum_name(cmd));
return false; return false;
+10
View File
@@ -16,7 +16,13 @@ public:
protected: protected:
bool handle_nav_command(Rml::Event& event, NavCommand cmd) override; bool handle_nav_command(Rml::Event& event, NavCommand cmd) override;
private:
void update_pipeline_progress();
Rml::Element* mFpsCounter = nullptr; Rml::Element* mFpsCounter = nullptr;
Rml::Element* mPipelineProgress = nullptr;
Rml::Element* mPipelineProgressLabel = nullptr;
Rml::Element* mPipelineProgressBar = nullptr;
Rml::Element* mCurrentToast = nullptr; Rml::Element* mCurrentToast = nullptr;
Rml::Element* mControllerWarning = nullptr; Rml::Element* mControllerWarning = nullptr;
Rml::Element* mMenuNotification = nullptr; Rml::Element* mMenuNotification = nullptr;
@@ -25,7 +31,11 @@ protected:
Rml::Element* mSpeedrunIgt = nullptr; Rml::Element* mSpeedrunIgt = nullptr;
clock::time_point mCurrentToastStartTime; clock::time_point mCurrentToastStartTime;
clock::time_point mMenuNotificationStartTime; clock::time_point mMenuNotificationStartTime;
clock::time_point mPipelineProgressStartTime;
Uint64 mFpsLastUpdate = 0; Uint64 mFpsLastUpdate = 0;
uint32_t mPipelineBatchCreatedBase = 0;
uint32_t mLastQueuedPipelines = 0;
bool mPipelineProgressActive = false;
}; };
} // namespace dusk::ui } // namespace dusk::ui
+1 -1
View File
@@ -309,7 +309,7 @@ void persist_disc_choice(const std::string& path, iso::ValidationError validatio
getSettings().backend.isoPath.setValue(path); getSettings().backend.isoPath.setValue(path);
getSettings().backend.isoVerification.setValue(verification); getSettings().backend.isoVerification.setValue(verification);
config::Save(); config::save();
if (previousPath != path || previousVerification != verification) { if (previousPath != path || previousVerification != verification) {
iso::log_verification_state(path, verification); iso::log_verification_state(path, verification);
+1 -1
View File
@@ -106,7 +106,7 @@ PresetWindow::PresetWindow() : WindowSmall("modal", "modal-dialog") {
if (cmd == NavCommand::Confirm) { if (cmd == NavCommand::Confirm) {
apply(); apply();
getSettings().backend.wasPresetChosen.setValue(true); getSettings().backend.wasPresetChosen.setValue(true);
config::Save(); config::save();
hide(true); hide(true);
return true; return true;
} }
+86 -37
View File
@@ -77,6 +77,18 @@ constexpr std::array kInterpolationModes = {
"Unlimited", "Unlimited",
}; };
constexpr std::array kTouchTargetingLabels = {
"Hybrid",
"Hold",
"Switch",
};
constexpr std::array kTouchTargetingDescriptions = {
"Tap once to lock on when a target is found. Double-tap when none is found to hold L.",
"L stays held only while your finger is on the button.",
"Tap L to keep it held. Tap again to release it.",
};
constexpr std::array kGyroInputModeLabels = { constexpr std::array kGyroInputModeLabels = {
"Sensor", "Sensor",
"Mouse", "Mouse",
@@ -236,7 +248,6 @@ void reset_for_speedrun_mode() {
getSettings().game.invincibleEnemies.setSpeedrunValue(false); getSettings().game.invincibleEnemies.setSpeedrunValue(false);
getSettings().game.pauseOnFocusLost.setSpeedrunValue(false); getSettings().game.pauseOnFocusLost.setSpeedrunValue(false);
aurora_set_pause_on_focus_lost(false);
getSettings().backend.enableAdvancedSettings.setSpeedrunValue(false); getSettings().backend.enableAdvancedSettings.setSpeedrunValue(false);
getSettings().game.recordingMode.setSpeedrunValue(false); getSettings().game.recordingMode.setSpeedrunValue(false);
@@ -251,7 +262,6 @@ void clear_speedrun_overrides() {
void restore_from_speedrun_mode() { void restore_from_speedrun_mode() {
clear_speedrun_overrides(); clear_speedrun_overrides();
aurora_set_pause_on_focus_lost(getSettings().game.pauseOnFocusLost.getValue());
} }
std::filesystem::path normalized_display_path(const std::filesystem::path& path) { std::filesystem::path normalized_display_path(const std::filesystem::path& path) {
@@ -407,6 +417,14 @@ bool gyro_enabled() {
return getSettings().game.enableGyroAim || getSettings().game.enableGyroRollgoal; return getSettings().game.enableGyroAim || getSettings().game.enableGyroRollgoal;
} }
Rml::String touch_targeting_label(TouchTargeting targeting) {
const auto index = static_cast<std::size_t>(targeting);
if (index >= kTouchTargetingLabels.size()) {
return "Unknown";
}
return kTouchTargetingLabels[index];
}
struct ConfigBoolProps { struct ConfigBoolProps {
Rml::String key; Rml::String key;
Rml::String icon; Rml::String icon;
@@ -427,7 +445,7 @@ SelectButton& config_bool_select(
return; return;
} }
var.setValue(value); var.setValue(value);
config::Save(); config::save();
if (callback) { if (callback) {
callback(value); callback(value);
} }
@@ -448,7 +466,7 @@ void add_speedrun_disabled_option(Pane& leftPane, Pane& rightPane, ConfigVar<boo
config_bool_select(leftPane, rightPane, var, { config_bool_select(leftPane, rightPane, var, {
.key = key, .key = key,
.helpText = helpText, .helpText = helpText,
.isDisabled = [] { return getSettings().game.speedrunMode; }, .isDisabled = [] { return getSettings().game.speedrunMode.getValue(); },
}); });
} }
@@ -461,7 +479,7 @@ SelectButton& config_percent_select(Pane& leftPane, Pane& rightPane, ConfigVar<f
.setValue = .setValue =
[&var, min, max](int value) { [&var, min, max](int value) {
var.setValue(std::clamp(value, min, max) / 100.0f); var.setValue(std::clamp(value, min, max) / 100.0f);
config::Save(); config::save();
}, },
.isDisabled = std::move(isDisabled), .isDisabled = std::move(isDisabled),
.isModified = [&var] { return var.getValue() != var.getDefaultValue(); }, .isModified = [&var] { return var.getValue() != var.getDefaultValue(); },
@@ -483,12 +501,12 @@ SelectButton& config_int_select(Pane& leftPane, Pane& rightPane, ConfigVar<int>&
std::string suffix = "") { std::string suffix = "") {
auto& button = leftPane.add_child<NumberButton>(NumberButton::Props{ auto& button = leftPane.add_child<NumberButton>(NumberButton::Props{
.key = std::move(key), .key = std::move(key),
.getValue = [&var] { return var; }, .getValue = [&var] { return var.getValue(); },
.setValue = .setValue =
[&var, min, max, callback = std::move(onChange)](int value) { [&var, min, max, callback = std::move(onChange)](int value) {
const int clampedValue = std::clamp(value, min, max); const int clampedValue = std::clamp(value, min, max);
var.setValue(clampedValue); var.setValue(clampedValue);
config::Save(); config::save();
if (callback) { if (callback) {
callback(clampedValue); callback(clampedValue);
} }
@@ -660,7 +678,7 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
.on_pressed([i] { .on_pressed([i] {
mDoAud_seStartMenu(kSoundItemChange); mDoAud_seStartMenu(kSoundItemChange);
getSettings().game.language.setValue(static_cast<GameLanguage>(i)); getSettings().game.language.setValue(static_cast<GameLanguage>(i));
config::Save(); config::save();
}); });
} }
pane.add_rml("<br/>Changes require a restart."); pane.add_rml("<br/>Changes require a restart.");
@@ -668,7 +686,7 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
leftPane.register_control( leftPane.register_control(
leftPane.add_select_button({ leftPane.add_select_button({
.key = "Graphics Backend", .key = "Graphics Backend",
.getValue = [] { return Rml::String{backend_name(configured_backend())}; }, .getValue = [] { return Rml::String{backend_name(aurora_get_backend())}; },
.isModified = .isModified =
[] { [] {
return getSettings().backend.graphicsBackend.getValue() != return getSettings().backend.graphicsBackend.getValue() !=
@@ -687,7 +705,7 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
mDoAud_seStartMenu(kSoundItemChange); mDoAud_seStartMenu(kSoundItemChange);
getSettings().backend.graphicsBackend.setValue( getSettings().backend.graphicsBackend.setValue(
std::string{backend_id(backend)}); std::string{backend_id(backend)});
config::Save(); config::save();
}); });
} }
pane.add_rml("<br/>Changes require a restart."); pane.add_rml("<br/>Changes require a restart.");
@@ -718,7 +736,7 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
.on_pressed([i] { .on_pressed([i] {
mDoAud_seStartMenu(kSoundItemChange); mDoAud_seStartMenu(kSoundItemChange);
getSettings().backend.cardFileType.setValue(i); getSettings().backend.cardFileType.setValue(i);
config::Save(); config::save();
}); });
} }
}); });
@@ -735,7 +753,7 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
mDoAud_seStartMenu(kSoundItemChange); mDoAud_seStartMenu(kSoundItemChange);
getSettings().video.enableFullscreen.setValue(!getSettings().video.enableFullscreen); getSettings().video.enableFullscreen.setValue(!getSettings().video.enableFullscreen);
VISetWindowFullscreen(getSettings().video.enableFullscreen); VISetWindowFullscreen(getSettings().video.enableFullscreen);
config::Save(); config::save();
}), }),
rightPane, [](Pane& pane) { pane.clear(); }); rightPane, [](Pane& pane) { pane.clear(); });
leftPane.register_control(leftPane.add_button("Restore Default Window Size").on_pressed([] { leftPane.register_control(leftPane.add_button("Restore Default Window Size").on_pressed([] {
@@ -766,7 +784,6 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
{ {
.key = "Pause on Focus Lost", .key = "Pause on Focus Lost",
.helpText = "Pause the game when window focus is lost.", .helpText = "Pause the game when window focus is lost.",
.onChange = [](bool value) { aurora_set_pause_on_focus_lost(value); },
.isDisabled = [] { return IsMobile || getSettings().game.speedrunMode; }, .isDisabled = [] { return IsMobile || getSettings().game.speedrunMode; },
}); });
leftPane.register_control( leftPane.register_control(
@@ -798,7 +815,7 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
.on_pressed([] { .on_pressed([] {
mDoAud_seStartMenu(kSoundItemChange); mDoAud_seStartMenu(kSoundItemChange);
getSettings().video.enableFpsOverlay.setValue(false); getSettings().video.enableFpsOverlay.setValue(false);
config::Save(); config::save();
}); });
for (int i = 0; i < static_cast<int>(kFpsOverlayCornerNames.size()); ++i) { for (int i = 0; i < static_cast<int>(kFpsOverlayCornerNames.size()); ++i) {
pane.add_button( pane.add_button(
@@ -814,7 +831,7 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
mDoAud_seStartMenu(kSoundItemChange); mDoAud_seStartMenu(kSoundItemChange);
getSettings().video.enableFpsOverlay.setValue(true); getSettings().video.enableFpsOverlay.setValue(true);
getSettings().video.fpsOverlayCorner.setValue(i); getSettings().video.fpsOverlayCorner.setValue(i);
config::Save(); config::save();
}); });
} }
pane.add_rml( pane.add_rml(
@@ -830,7 +847,7 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
const auto windowSize = aurora::window::get_window_size(); const auto windowSize = aurora::window::get_window_size();
dusk::getSettings().video.lastWindowWidth.setValue(windowSize.width); dusk::getSettings().video.lastWindowWidth.setValue(windowSize.width);
dusk::getSettings().video.lastWindowHeight.setValue(windowSize.height); dusk::getSettings().video.lastWindowHeight.setValue(windowSize.height);
dusk::config::Save(); dusk::config::save();
} }
}, },
.isDisabled = [] { return IsMobile; }, .isDisabled = [] { return IsMobile; },
@@ -936,7 +953,7 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
mDoAud_seStartMenu(kSoundItemChange); mDoAud_seStartMenu(kSoundItemChange);
getSettings().game.enableFrameInterpolation.setValue(static_cast<FrameInterpMode>(i)); getSettings().game.enableFrameInterpolation.setValue(static_cast<FrameInterpMode>(i));
android::update_surface_frame_rate(); android::update_surface_frame_rate();
config::Save(); config::save();
}); });
} }
pane.add_rml(kUnlockFramerateHelpText); pane.add_rml(kUnlockFramerateHelpText);
@@ -1003,6 +1020,45 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
pane.clear(); pane.clear();
pane.add_text("Open the touch controls layout editor."); pane.add_text("Open the touch controls layout editor.");
}); });
leftPane.register_control(leftPane.add_select_button({
.key = "Touch Targeting",
.getValue =
[] {
return touch_targeting_label(
getSettings().game.touchTargeting.getValue());
},
.isDisabled =
[] { return !getSettings().game.enableTouchControls; },
.isModified =
[] {
const auto& targeting =
getSettings().game.touchTargeting;
return targeting.getValue() !=
targeting.getDefaultValue();
},
}),
rightPane, [](Pane& pane) {
pane.clear();
for (int i = 0; i < static_cast<int>(kTouchTargetingLabels.size()); ++i) {
pane.add_button({
.text = kTouchTargetingLabels[i],
.isSelected =
[i] {
return getSettings().game.touchTargeting.getValue() ==
static_cast<TouchTargeting>(i);
},
})
.on_pressed([i] {
mDoAud_seStartMenu(kSoundItemChange);
getSettings().game.touchTargeting.setValue(
static_cast<TouchTargeting>(i));
config::save();
});
}
pane.add_rml(fmt::format("<br/>Hybrid: {}<br/>Hold: {}<br/>Switch: {}",
kTouchTargetingDescriptions[0], kTouchTargetingDescriptions[1],
kTouchTargetingDescriptions[2]));
});
config_percent_select(leftPane, rightPane, getSettings().game.touchCameraXSensitivity, config_percent_select(leftPane, rightPane, getSettings().game.touchCameraXSensitivity,
"Touch Camera X Sensitivity", "Touch Camera X Sensitivity",
"Adjusts touch camera horizontal sensitivity.<br/><br/>Applies to touch input only.", "Adjusts touch camera horizontal sensitivity.<br/><br/>Applies to touch input only.",
@@ -1092,7 +1148,7 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
leftPane.add_section("Tools"); leftPane.add_section("Tools");
addOption("Turbo Key", getSettings().game.enableTurboKeybind, addOption("Turbo Key", getSettings().game.enableTurboKeybind,
"Hold Tab to increase game speed by up to 4x.", "Hold Tab to increase game speed by up to 4x.",
[] { return getSettings().game.speedrunMode; }); [] { return getSettings().game.speedrunMode.getValue(); });
addOption("Reset Key (" + Rml::String{hotkeys::DO_RESET} + ")", addOption("Reset Key (" + Rml::String{hotkeys::DO_RESET} + ")",
getSettings().game.enableResetKeybind, getSettings().game.enableResetKeybind,
"Press " + Rml::String{hotkeys::DO_RESET} + " to reset the game."); "Press " + Rml::String{hotkeys::DO_RESET} + " to reset the game.");
@@ -1111,7 +1167,7 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
.setValue = .setValue =
[](int value) { [](int value) {
getSettings().audio.masterVolume.setValue(value); getSettings().audio.masterVolume.setValue(value);
config::Save(); config::save();
audio::SetMasterVolume(audio::MasterVolumeToLinear(value / 100.0f)); audio::SetMasterVolume(audio::MasterVolumeToLinear(value / 100.0f));
}, },
.isModified = .isModified =
@@ -1203,9 +1259,9 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
.setValue = .setValue =
[](int value) { [](int value) {
getSettings().game.damageMultiplier.setValue(value); getSettings().game.damageMultiplier.setValue(value);
config::Save(); config::save();
}, },
.isDisabled = [] { return getSettings().game.speedrunMode; }, .isDisabled = [] { return getSettings().game.speedrunMode.getValue(); },
.isModified = .isModified =
[] { [] {
return getSettings().game.damageMultiplier.getValue() != return getSettings().game.damageMultiplier.getValue() !=
@@ -1349,7 +1405,7 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
[] { [] {
return kMagicArmorModes[static_cast<u8>(getSettings().game.armorRupeeDrain.getValue())]; return kMagicArmorModes[static_cast<u8>(getSettings().game.armorRupeeDrain.getValue())];
}, },
.isDisabled = [] { return getSettings().game.speedrunMode; }, .isDisabled = [] { return getSettings().game.speedrunMode.getValue(); },
.isModified = .isModified =
[] { [] {
return getSettings().game.armorRupeeDrain.getValue() != return getSettings().game.armorRupeeDrain.getValue() !=
@@ -1368,7 +1424,7 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
.on_pressed([i] { .on_pressed([i] {
mDoAud_seStartMenu(kSoundItemChange); mDoAud_seStartMenu(kSoundItemChange);
getSettings().game.armorRupeeDrain.setValue(static_cast<MagicArmorMode>(i)); getSettings().game.armorRupeeDrain.setValue(static_cast<MagicArmorMode>(i));
config::Save(); config::save();
}); });
} }
pane.add_rml( pane.add_rml(
@@ -1421,13 +1477,13 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
mDoAud_seStartMenu(kSoundItemChange); mDoAud_seStartMenu(kSoundItemChange);
getSettings().game.enableAchievementToasts.setValue(true); getSettings().game.enableAchievementToasts.setValue(true);
getSettings().game.enableControllerToasts.setValue(true); getSettings().game.enableControllerToasts.setValue(true);
config::Save(); config::save();
}); });
pane.add_button("Select None").on_pressed([] { pane.add_button("Select None").on_pressed([] {
mDoAud_seStartMenu(kSoundItemChange); mDoAud_seStartMenu(kSoundItemChange);
getSettings().game.enableAchievementToasts.setValue(false); getSettings().game.enableAchievementToasts.setValue(false);
getSettings().game.enableControllerToasts.setValue(false); getSettings().game.enableControllerToasts.setValue(false);
config::Save(); config::save();
}); });
pane.add_section("Types"); pane.add_section("Types");
@@ -1443,7 +1499,7 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
mDoAud_seStartMenu(kSoundItemChange); mDoAud_seStartMenu(kSoundItemChange);
auto& v = getSettings().game.enableAchievementToasts; auto& v = getSettings().game.enableAchievementToasts;
v.setValue(!v.getValue()); v.setValue(!v.getValue());
config::Save(); config::save();
}); });
pane.add_button( pane.add_button(
{ {
@@ -1455,7 +1511,7 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
mDoAud_seStartMenu(kSoundItemChange); mDoAud_seStartMenu(kSoundItemChange);
auto& v = getSettings().game.enableControllerToasts; auto& v = getSettings().game.enableControllerToasts;
v.setValue(!v.getValue()); v.setValue(!v.getValue());
config::Save(); config::save();
}); });
pane.add_rml("<br/>Choose which notifications can be displayed."); pane.add_rml("<br/>Choose which notifications can be displayed.");
}); });
@@ -1485,11 +1541,6 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
.helpText = "When starting Dusklight, skip the main menu and boot straight into the " .helpText = "When starting Dusklight, skip the main menu and boot straight into the "
"game if a disc image is available.", "game if a disc image is available.",
}); });
config_bool_select(leftPane, rightPane, getSettings().backend.showPipelineCompilation,
{
.key = "Show Pipeline Compilation",
.helpText = "Show an overlay when shaders are being compiled for your hardware.",
});
config_bool_select(leftPane, rightPane, getSettings().backend.checkForUpdates, config_bool_select(leftPane, rightPane, getSettings().backend.checkForUpdates,
{ {
.key = "Check for Updates", .key = "Check for Updates",
@@ -1526,7 +1577,7 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
} }
} }
}, },
.isDisabled = [] { return getSettings().game.speedrunMode; }, .isDisabled = [] { return getSettings().game.speedrunMode.getValue(); },
}); });
config_bool_select(leftPane, rightPane, getSettings().game.showInputViewer, config_bool_select(leftPane, rightPane, getSettings().game.showInputViewer,
{ {
@@ -1563,15 +1614,13 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
[i] { [i] {
return getSettings().game.menuScalingMode.getValue() == return getSettings().game.menuScalingMode.getValue() ==
static_cast<MenuScaling>(i); static_cast<MenuScaling>(i);
;
}, },
}) })
.on_pressed([i] { .on_pressed([i] {
mDoAud_seStartMenu(kSoundItemChange); mDoAud_seStartMenu(kSoundItemChange);
getSettings().game.menuScalingMode.setValue( getSettings().game.menuScalingMode.setValue(
static_cast<MenuScaling>(i)); static_cast<MenuScaling>(i));
; config::save();
config::Save();
}); });
} }
pane.add_rml("<br/>Changes how the Collection and File Select menus scale to your " pane.add_rml("<br/>Changes how the Collection and File Select menus scale to your "
@@ -1597,7 +1646,7 @@ void SettingsWindow::update() {
} }
void SettingsWindow::hide(bool close) { void SettingsWindow::hide(bool close) {
config::Save(); config::save();
Window::hide(close); Window::hide(close);
} }
+65 -26
View File
@@ -485,43 +485,71 @@ void TouchControls::set_control_pressed(Control control, bool pressed) {
mLastLTapTime = {}; mLastLTapTime = {};
break; break;
} }
if (pressed && (mLLatched || mManualLLatched)) { switch (getSettings().game.touchTargeting.getValue()) {
case TouchTargeting::Hold:
mLPressed = pressed;
mLLatched = false; mLLatched = false;
mManualLLatched = false; mManualLLatched = false;
mLPressed = false; mLReleasePending = false;
mLReleasePending = true;
mLPressStartTime = {}; mLPressStartTime = {};
mLastLTapTime = {}; mLastLTapTime = {};
set_control_visual(control, false); break;
} else if (pressed) { case TouchTargeting::Switch:
const auto now = clock::now(); if (pressed) {
if (!player_attention_locked() && mLastLTapTime != clock::time_point{} && const bool wasLatched = mLPressed || mLLatched || mManualLLatched;
now - mLastLTapTime <= kLDoubleTapWindow) mLPressed = false;
{ mLLatched = false;
mManualLLatched = true; mManualLLatched = !wasLatched;
mLReleasePending = true;
} else {
mLPressed = false;
mLLatched = false;
mLReleasePending = false;
}
mLPressStartTime = {};
mLastLTapTime = {};
break;
case TouchTargeting::Hybrid:
default:
if (pressed && (mLLatched || mManualLLatched)) {
mLLatched = false;
mManualLLatched = false;
mLPressed = false; mLPressed = false;
mLReleasePending = true; mLReleasePending = true;
mLPressStartTime = {}; mLPressStartTime = {};
mLastLTapTime = {}; mLastLTapTime = {};
set_control_visual(control, false);
} else if (pressed) {
const auto now = clock::now();
if (!player_attention_locked() && mLastLTapTime != clock::time_point{} &&
now - mLastLTapTime <= kLDoubleTapWindow)
{
mManualLLatched = true;
mLPressed = false;
mLReleasePending = true;
mLPressStartTime = {};
mLastLTapTime = {};
} else if (!mLReleasePending) {
mLPressed = true;
mLPressStartTime = now;
}
} else if (!mLReleasePending) { } else if (!mLReleasePending) {
mLPressed = true; mLPressed = false;
mLPressStartTime = now;
} }
} else if (!mLReleasePending) { if (!pressed) {
mLPressed = false; const auto now = clock::now();
} if (!mLReleasePending) {
if (!pressed) { const bool wasQuickTap = mLPressStartTime != clock::time_point{} &&
const auto now = clock::now(); now - mLPressStartTime <= kLDoubleTapWindow;
if (!mLReleasePending) { mLastLTapTime = wasQuickTap ? now : clock::time_point{};
const bool wasQuickTap = mLPressStartTime != clock::time_point{} && }
now - mLPressStartTime <= kLDoubleTapWindow; mLPressStartTime = {};
mLastLTapTime = wasQuickTap ? now : clock::time_point{}; mLReleasePending = false;
} }
mLPressStartTime = {}; if (!pressed && !player_attention_locked()) {
mLReleasePending = false; mLLatched = false;
} }
if (!pressed && !player_attention_locked()) { break;
mLLatched = false;
} }
break; break;
case Control::R: case Control::R:
@@ -635,6 +663,17 @@ void TouchControls::apply_control_transform(Control control) noexcept {
} }
void TouchControls::sync_l_lock_state() noexcept { void TouchControls::sync_l_lock_state() noexcept {
const auto targeting = getSettings().game.touchTargeting.getValue();
if (targeting == TouchTargeting::Hold) {
mLLatched = false;
mManualLLatched = false;
return;
}
if (targeting == TouchTargeting::Switch) {
mLLatched = false;
return;
}
if (player_attention_locked()) { if (player_attention_locked()) {
if (mLPressed) { if (mLPressed) {
mLLatched = true; mLLatched = true;
+1 -1
View File
@@ -585,7 +585,7 @@ bool TouchControlsEditor::handle_nav_command(Rml::Event& event, NavCommand cmd)
void TouchControlsEditor::save_layout() { void TouchControlsEditor::save_layout() {
mWorkingLayout.version = ControlLayout::Version; mWorkingLayout.version = ControlLayout::Version;
getSettings().game.touchControlsLayout.setValue(mWorkingLayout); getSettings().game.touchControlsLayout.setValue(mWorkingLayout);
config::Save(); config::save();
mDoAud_seStartMenu(kSoundItemChange); mDoAud_seStartMenu(kSoundItemChange);
pop(); pop();
} }
+1 -1
View File
@@ -320,7 +320,7 @@ static void mDoMemCdRWm_BuildHeader(mDoMemCdRWm_HeaderData* header) {
#endif #endif
OSCalendarTime time; OSCalendarTime time;
OSTicksToCalendarTime(OSGetTime(), &time); OSTicksToCalendarTime(DUSK_IF_ELSE(OSGetSystemTime(), OSGetTime()), &time);
#if TARGET_PC #if TARGET_PC
if (dusk::version::isRegionPal()) { if (dusk::version::isRegionPal()) {
+1 -1
View File
@@ -527,7 +527,7 @@ void myExceptionCallback(u16, OSContext*, u32, u32) {
u32 btnHold; u32 btnHold;
u32 btnTrig; u32 btnTrig;
mDoMain::sHungUpTime = OSGetTime(); mDoMain::sHungUpTime = DUSK_IF_ELSE(OSGetSystemTime(), OSGetTime());
OSReportEnable(); OSReportEnable();
cAPICPad_recalibrate(); cAPICPad_recalibrate();
// "Vibration stopping & resetting to default\n" // "Vibration stopping & resetting to default\n"
+15 -21
View File
@@ -249,12 +249,12 @@ void main01(void) {
goto eventsDone; goto eventsDone;
case AURORA_PAUSED: case AURORA_PAUSED:
dusk::audio::SetPaused(true); dusk::audio::SetPaused(true);
dusk::mouse::onFocusLost(); dusk::mouse::on_focus_lost();
break; break;
case AURORA_UNPAUSED: case AURORA_UNPAUSED:
dusk::audio::SetPaused(false); dusk::audio::SetPaused(false);
dusk::game_clock::reset_frame_timer(); dusk::game_clock::reset_frame_timer();
dusk::mouse::onFocusGained(); dusk::mouse::on_focus_gained();
break; break;
case AURORA_SDL_EVENT: case AURORA_SDL_EVENT:
dusk::mouse::handle_event(event->sdl); dusk::mouse::handle_event(event->sdl);
@@ -265,7 +265,7 @@ void main01(void) {
if (dusk::getSettings().video.rememberWindowSize && !dusk::getSettings().video.enableFullscreen) { if (dusk::getSettings().video.rememberWindowSize && !dusk::getSettings().video.enableFullscreen) {
dusk::getSettings().video.lastWindowWidth.setValue(event->windowSize.width); dusk::getSettings().video.lastWindowWidth.setValue(event->windowSize.width);
dusk::getSettings().video.lastWindowHeight.setValue(event->windowSize.height); dusk::getSettings().video.lastWindowHeight.setValue(event->windowSize.height);
dusk::config::Save(); dusk::config::save();
} }
break; break;
case AURORA_DISPLAY_SCALE_CHANGED: case AURORA_DISPLAY_SCALE_CHANGED:
@@ -430,16 +430,7 @@ static void ApplyCVarOverrides(const cxxopts::OptionValue& option) {
const auto name = std::string_view(cvarArg).substr(0, sep); const auto name = std::string_view(cvarArg).substr(0, sep);
const auto value = std::string_view(cvarArg).substr(sep + 1); const auto value = std::string_view(cvarArg).substr(sep + 1);
const auto cVar = dusk::config::GetConfigVar(name); dusk::config::load_arg_override(name, value);
if (!cVar) {
DuskLog.fatal("Unknown --cvar name: '{}'", name);
}
try {
cVar->getImpl()->loadFromArg(*cVar, value);
} catch (const std::exception& e) {
DuskLog.fatal("Unable to parse: '{}': {}", value, e.what());
}
} }
} }
@@ -510,7 +501,6 @@ int game_main(int argc, char* argv[]) {
mainCalled = true; mainCalled = true;
dusk::registerSettings(); dusk::registerSettings();
dusk::config::FinishRegistration();
cxxopts::ParseResult parsed_arg_options; cxxopts::ParseResult parsed_arg_options;
@@ -551,10 +541,7 @@ int game_main(int argc, char* argv[]) {
log_build_info(); log_build_info();
dusk::config::LoadFromUserPreferences(); dusk::config::load_from_user_preferences();
if (dusk::getSettings().game.speedrunMode) {
dusk::resetForSpeedrunMode();
}
ApplyCVarOverrides(parsed_arg_options["cvar"]); ApplyCVarOverrides(parsed_arg_options["cvar"]);
dusk::android::update_surface_frame_rate(); dusk::android::update_surface_frame_rate();
dusk::crash_reporting::initialize(); dusk::crash_reporting::initialize();
@@ -618,6 +605,12 @@ int game_main(int argc, char* argv[]) {
auroraInfo = aurora_initialize(argc, argv, &config); auroraInfo = aurora_initialize(argc, argv, &config);
} }
// Apply after aurora_initialize: speedrun mode mutates cvars whose change callbacks push
// values into aurora.
if (dusk::getSettings().game.speedrunMode) {
dusk::resetForSpeedrunMode();
}
#ifdef DUSK_DISCORD #ifdef DUSK_DISCORD
if (dusk::getSettings().game.enableDiscordPresence) { if (dusk::getSettings().game.enableDiscordPresence) {
dusk::discord::initialize(); dusk::discord::initialize();
@@ -700,7 +693,7 @@ int game_main(int argc, char* argv[]) {
dusk::getSettings().backend.isoPath.setValue(dvd_path); dusk::getSettings().backend.isoPath.setValue(dvd_path);
dusk::getSettings().backend.isoVerification.setValue( dusk::getSettings().backend.isoVerification.setValue(
dusk::DiscVerificationState::Unknown); dusk::DiscVerificationState::Unknown);
dusk::config::Save(); dusk::config::save();
dusk::IsGameLaunched = true; dusk::IsGameLaunched = true;
} }
} else { } else {
@@ -723,7 +716,7 @@ int game_main(int argc, char* argv[]) {
saveConfigBeforePrelaunch = true; saveConfigBeforePrelaunch = true;
} }
if (saveConfigBeforePrelaunch) { if (saveConfigBeforePrelaunch) {
dusk::config::Save(); dusk::config::save();
} }
if (!dusk::getSettings().backend.skipPreLaunchUI) { if (!dusk::getSettings().backend.skipPreLaunchUI) {
@@ -777,7 +770,7 @@ int game_main(int argc, char* argv[]) {
OSInit(); OSInit();
mDoMain::sPowerOnTime = OSGetTime(); mDoMain::sPowerOnTime = DUSK_IF_ELSE(OSGetSystemTime(), OSGetTime());
// Reset Data // Reset Data
static mDoRstData sResetData = {0}; static mDoRstData sResetData = {0};
@@ -814,6 +807,7 @@ int game_main(int argc, char* argv[]) {
#endif #endif
dusk::ui::shutdown(); dusk::ui::shutdown();
dusk::texture_replacements::shutdown(); dusk::texture_replacements::shutdown();
dusk::config::shutdown();
aurora_shutdown(); aurora_shutdown();
return 0; return 0;
+189
View File
@@ -0,0 +1,189 @@
"""
Loads d_a_mant.rel from CWD, applies tears using seed (66, 16983, 855) and cut type 1, writes PNGs to tear_textures/
Requires pillow and xxhash.
"""
import math
import struct
import xxhash
from pathlib import Path
from PIL import Image
def yaz0_decompress(data):
expand_size = struct.unpack_from(">I", data, 4)[0]
out = bytearray(expand_size)
src_pos = 0x10
dst_pos = 0
chunk_bits_left = 0
chunk_bits = 0
while dst_pos < expand_size:
if chunk_bits_left == 0:
chunk_bits = data[src_pos]
src_pos += 1
chunk_bits_left = 8
if chunk_bits & 0x80:
out[dst_pos] = data[src_pos]
src_pos += 1
dst_pos += 1
else:
b0 = data[src_pos]
b1 = data[src_pos + 1]
src_pos += 2
dist = ((b0 & 0x0F) << 8) | b1
count = b0 >> 4
if count == 0:
count = data[src_pos] + 0x12
src_pos += 1
else:
count += 2
copy_pos = dst_pos - dist - 1
for _ in range(count):
out[dst_pos] = out[copy_pos]
dst_pos += 1
copy_pos += 1
chunk_bits <<= 1
chunk_bits_left -= 1
return bytes(out)
SINCOS_TABLE = tuple(
(
math.sin((i * math.tau) / (1 << 13)),
math.cos((i * math.tau) / (1 << 13)),
)
for i in range(1 << 13)
)
def rnd(rng):
rng[0] = (rng[0] * 171) % 30269
rng[1] = (rng[1] * 172) % 30307
rng[2] = (rng[2] * 170) % 30323
value = rng[0] / 30269.0 + rng[1] / 30307.0 + rng[2] / 30323.0
return abs(value % 1.0)
def rnd_f(rng, max_value):
return rnd(rng) * max_value
def rnd_fx(rng, max_value):
return max_value * (rnd(rng) - 0.5) * 2.0
def linear_index_to_swizzled(linear_index):
within_tile_x = linear_index & 0x7
tile_row_offset = (linear_index & 0x78) * 4
tile_column_offset = (linear_index >> 4) & 0x18
macro_row_offset = linear_index & 0x3E00
return within_tile_x + tile_row_offset + tile_column_offset + macro_row_offset
SWIZZLED_TO_XY = [(0, 0)] * 0x4000
for linear_index in range(0x4000):
x = linear_index & 0x7F
y = linear_index >> 7
swizzled_index = linear_index_to_swizzled(linear_index)
SWIZZLED_TO_XY[swizzled_index] = (x, y)
NEIGHBOR_OFFSETS = (0, 1, 0x80, 0x81, 2, 0x82, 0x102, 0x101, 0x100)
def write_c8_texture(c8_data, stage, output_dir, palette, palette_data):
min_index = min(c8_data)
max_index = max(c8_data)
tlut_offset = 2 * min_index
tlut_size = 2 * (max_index + 1 - min_index)
tlut_end = tlut_offset + tlut_size
path = output_dir / (
f"[{stage:02d}] tex1_{0x80}x{0x80}_"
f"{xxhash.xxh64(c8_data, seed=0).intdigest():016x}_"
f"{xxhash.xxh64(palette_data[tlut_offset:tlut_end], seed=0).intdigest():016x}_"
f"{0x9}.png"
)
rgba = Image.new("RGBA", (0x80, 0x80))
pixels = rgba.load()
for swizzled_index, palette_index in enumerate(c8_data):
x, y = SWIZZLED_TO_XY[swizzled_index]
pixels[x, y] = palette[palette_index]
rgba.save(path)
return path
def write_stage(tex, tex_u, pal, stage, output_dir, palette):
return [
write_c8_texture(bytes(tex), stage, output_dir, palette, pal),
write_c8_texture(bytes(tex_u), stage, output_dir, palette, pal),
]
def export_mant_tears():
rel = yaz0_decompress(Path("d_a_mant.rel").read_bytes())
tex = bytearray(rel[0x1C00 : 0x1C00 + 0x4000])
tex_u = bytearray([6] * 0x4000)
pal = bytes(rel[0x9C00 : 0x9C00 + 0x60])
rng = [int(66), int(16983), int(855)]
Path("tear_textures").mkdir(parents=True, exist_ok=True)
written = []
rgba_palette = []
for offset in range(0, len(pal), 2):
color16 = struct.unpack_from(">H", pal, offset)[0]
if color16 & 0x8000:
r = (color16 >> 7) & 0xF8
r |= r >> 5
g = (color16 >> 2) & 0xF8
g |= g >> 5
b = (color16 << 3) & 0xF8
b |= b >> 5
a = 255
else:
r = (color16 >> 4) & 0xF0
r |= r >> 4
g = color16 & 0xF0
g |= g >> 4
b = (color16 << 4) & 0xF0
b |= b >> 4
a = (color16 >> 7) & 0xE0
a |= (a >> 3) | (a >> 6)
rgba_palette.append((r, g, b, a))
while len(rgba_palette) < 256:
rgba_palette.append((0, 0, 0, 0))
written.extend(write_stage(tex, tex_u, pal, 0, Path("tear_textures"), rgba_palette))
cut_step = 0
for _ in range(15):
cut_step += 1
angle = int(rnd_f(rng, 65536.0)) & 0xFFFF
if angle >= 0x8000:
angle -= 0x10000
x = rnd_fx(rng, 32.0)
y = rnd_fx(rng, 32.0)
sincos_index = (angle & 0xFFFF) >> (16 - 13)
sin_v, cos_v = SINCOS_TABLE[sincos_index]
for i, texel_count in enumerate(
tuple(
1 if i <= 3 or i >= 26 else (9 if 12 <= i <= 18 else 4)
for i in range(30)
)
):
x += sin_v
y -= cos_v
packed = int(x + 64.0) | (int(y + 64.0) << 7)
for j in range(texel_count):
u_var1 = packed + NEIGHBOR_OFFSETS[j]
if 0 <= u_var1 < 0x4000:
i_var5 = linear_index_to_swizzled(u_var1)
tex[i_var5] = 0
tex_u[i_var5] = 0
written.extend(write_stage(tex, tex_u, pal, cut_step, Path("tear_textures"), rgba_palette))
return written
if __name__ == "__main__":
export_mant_tears()