Compare commits

..

18 Commits

Author SHA1 Message Date
SuperDude88 a01c01b582 Move restore_from_speedrun_mode
I think it makes more sense to have this in the same place the overrides are set
2026-07-06 20:29:39 -04:00
SuperDude88 552b3ad353 Deduplicate Speedrun Overrides
Avoids issues where only one is updated + makes sense organizationally
2026-07-06 19:59:43 -04: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
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
39 changed files with 936 additions and 300 deletions
+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 = {
+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 */
+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() {
+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
+13 -1
View File
@@ -46,6 +46,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 +103,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 +228,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 +288,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
View File
@@ -37,5 +37,6 @@ struct SpeedrunInfo {
extern SpeedrunInfo m_speedrunInfo; extern SpeedrunInfo m_speedrunInfo;
void resetForSpeedrunMode(); void resetForSpeedrunMode();
void restoreFromSpeedrunMode();
} // namespace dusk } // namespace dusk
+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 -8
View File
@@ -122,19 +122,12 @@ BOOL daAlink_c::setBodyAngleToCamera() {
var_f31 /= dComIfGp_getCameraZoomScale(field_0x317c); var_f31 /= dComIfGp_getCameraZoomScale(field_0x317c);
} }
#if TARGET_PC
if (dusk::getSettings().game.enableMouseAim && checkAimInputContext()) {
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))); 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))); 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;
+19
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,10 +656,19 @@ static int daMant_Execute(mant_class* i_this) {
iVar8 = 20; iVar8 = 20;
} }
#if TARGET_PC
if (l_Egnd_mantTEX_hasReplacement) {
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); unaff_r29 = cM_rndF(65536.0f);
var_f31 = cM_rndFX(32.0f); var_f31 = cM_rndFX(32.0f);
var_f30 = 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))
{ {
+20 -9
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,13 +1107,14 @@ 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;
bool iVar6 = true; bool iVar6 = true;
@@ -1494,13 +1499,15 @@ 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::set_hover_target(pointer_target(s_pointerMenuSelectTarget, mSelectMenuNum));
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_pointerMenuSelectTarget, mSelectMenuNum)); pointer_target(s_pointerMenuSelectTarget, mSelectMenuNum));
} }
}
#endif #endif
bool tmp1 = true; bool tmp1 = true;
@@ -1997,13 +2004,15 @@ 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::set_hover_target(pointer_target(s_pointerCopySelectTarget, field_0x026b));
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_pointerCopySelectTarget, field_0x026b)); pointer_target(s_pointerCopySelectTarget, field_0x026b));
} }
}
#endif #endif
bool iVar7 = true; bool iVar7 = true;
bool iVar6 = true; bool iVar6 = true;
@@ -2522,13 +2531,15 @@ 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::set_hover_target(pointer_target(s_pointerYesNoSelectTarget, field_0x0268));
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_pointerYesNoSelectTarget, field_0x0268)); pointer_target(s_pointerYesNoSelectTarget, field_0x0268));
} }
}
#endif #endif
bool isYnSelMove = yesnoSelectMoveAnm(); bool isYnSelMove = yesnoSelectMoveAnm();
bool isYnWakuAlpha = yesnoWakuAlpahAnm(field_0x0269); bool isYnWakuAlpha = yesnoWakuAlpahAnm(field_0x0269);
+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;
+19 -11
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);
} }
@@ -1005,6 +1013,11 @@ void dMenu_Fmap2DBack_c::allmap_move2(STControl* param_0) {
f32 stickValue = param_0->getValueStick(); f32 stickValue = param_0->getValueStick();
if (stickValue >= spC) { if (stickValue >= spC) {
s16 angle = param_0->getAngleStick(); s16 angle = param_0->getAngleStick();
#ifdef TARGET_PC
if (dusk::getSettings().game.enableMirrorMode) {
angle = -angle;
}
#endif
f32 local_68 = (mTexMaxX - mTexMinX); f32 local_68 = (mTexMaxX - mTexMinX);
f32 zoomRate = local_68 / getAllMapZoomRate(); f32 zoomRate = local_68 / getAllMapZoomRate();
f32 sp24; f32 sp24;
@@ -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--) {
@@ -1907,6 +1915,11 @@ void dMenu_Fmap2DBack_c::regionMapMove(STControl* i_stick) {
f32 stick_value = i_stick->getValueStick(); f32 stick_value = i_stick->getValueStick();
if (stick_value >= slow_bound) { if (stick_value >= slow_bound) {
s16 angle = i_stick->getAngleStick(); s16 angle = i_stick->getAngleStick();
#ifdef 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);
@@ -1946,11 +1959,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;
+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;
+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
+10 -4
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,13 +1954,15 @@ 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::set_hover_target(pointer_target(s_pointerSaveSelectTarget, mSelectedFile));
if (dusk::menu_pointer::consume_click()) {
dusk::menu_pointer::defer_activation( dusk::menu_pointer::defer_activation(
dusk::menu_pointer::Context::Save, dusk::menu_pointer::Context::Save,
pointer_target(s_pointerSaveSelectTarget, mSelectedFile)); pointer_target(s_pointerSaveSelectTarget, mSelectedFile));
} }
}
#endif #endif
bool bookWakuAnmComplete = true; bool bookWakuAnmComplete = true;
bool selWakuAnmComplete = true; bool selWakuAnmComplete = true;
@@ -2130,13 +2134,15 @@ 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::set_hover_target(pointer_target(s_pointerYesNoSelectTarget, mYesNoCursor));
if (dusk::menu_pointer::consume_click()) {
dusk::menu_pointer::defer_activation( dusk::menu_pointer::defer_activation(
dusk::menu_pointer::Context::Save, dusk::menu_pointer::Context::Save,
pointer_target(s_pointerYesNoSelectTarget, mYesNoCursor)); pointer_target(s_pointerYesNoSelectTarget, mYesNoCursor));
} }
}
#endif #endif
bool selAnmComplete = yesnoSelectMoveAnm(0); bool selAnmComplete = yesnoSelectMoveAnm(0);
bool wakuAnmComplete = yesnoWakuAlpahAnm(mYesNoPrevCursor); bool wakuAnmComplete = yesnoWakuAlpahAnm(mYesNoPrevCursor);
+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))
{ {
+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
View File
@@ -378,6 +378,7 @@ nlohmann::json ConfigImpl<ui::ControlLayout>::dumpToJson(const ConfigVar<ui::Con
} }
template class ConfigImpl<dusk::FrameInterpMode>; template class ConfigImpl<dusk::FrameInterpMode>;
template class ConfigImpl<dusk::TouchTargeting>;
template class ConfigImpl<dusk::MenuScaling>; template class ConfigImpl<dusk::MenuScaling>;
template class ConfigImpl<dusk::Resampler>; template class ConfigImpl<dusk::Resampler>;
template class ConfigImpl<dusk::MagicArmorMode>; template class ConfigImpl<dusk::MagicArmorMode>;
-28
View File
@@ -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();
}; };
+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 {
+70 -48
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 {
using Clock = std::chrono::steady_clock;
constexpr float kMousePixelToRad = 0.0025f; constexpr float kMousePixelToRad = 0.0025f;
constexpr int kIdleHideFrames = 99; // Approx. 3 seconds with 33ms ticks constexpr auto kCursorIdleDuration = std::chrono::seconds(1);
float s_aim_yaw_rad = 0.0f; float s_aim_yaw_rad = 0.0f;
float s_aim_pitch_rad = 0.0f; float s_aim_pitch_rad = 0.0f;
float s_camera_yaw_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
+2 -2
View File
@@ -96,6 +96,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 +161,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)},
@@ -330,6 +330,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 +346,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);
+13
View File
@@ -1,5 +1,6 @@
#include "dusk/speedrun.h" #include "dusk/speedrun.h"
#include "dusk/settings.h" #include "dusk/settings.h"
#include "dusk/config.hpp"
#include "m_Do/m_Do_main.h" #include "m_Do/m_Do_main.h"
#include <aurora/aurora.h> #include <aurora/aurora.h>
@@ -34,6 +35,7 @@ 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); aurora_set_pause_on_focus_lost(false);
@@ -43,4 +45,15 @@ void resetForSpeedrunMode() {
getSettings().game.debugFlyCam.setSpeedrunValue(false); getSettings().game.debugFlyCam.setSpeedrunValue(false);
} }
static void clear_speedrun_overrides() {
config::EnumerateRegistered([](config::ConfigVarBase& cvar) {
cvar.clearSpeedrunOverride();
});
}
void restoreFromSpeedrunMode() {
clear_speedrun_overrides();
aurora_set_pause_on_focus_lost(getSettings().game.pauseOnFocusLost.getValue());
}
} // namespace dusk } // namespace dusk
+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;
} }
+66 -8
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();
} }
@@ -197,6 +205,9 @@ static std::string FormatTime(OSTime ticks) {
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 +269,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();
@@ -309,7 +322,8 @@ void Overlay::update() {
mSpeedrunRta->RemoveAttribute("open"); mSpeedrunRta->RemoveAttribute("open");
} }
mSpeedrunIgt->SetInnerRML(escape(fmt::format("IGT {}", FormatTime(m_speedrunInfo.m_igtTimer)))); mSpeedrunIgt->SetInnerRML(
escape(fmt::format("IGT {}", FormatTime(m_speedrunInfo.m_igtTimer))));
} else { } else {
mSpeedrunTimer->RemoveAttribute("open"); mSpeedrunTimer->RemoveAttribute("open");
} }
@@ -373,10 +387,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 +407,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
+63 -56
View File
@@ -15,6 +15,7 @@
#include "dusk/io.hpp" #include "dusk/io.hpp"
#include "dusk/livesplit.h" #include "dusk/livesplit.h"
#include "dusk/discord_presence.hpp" #include "dusk/discord_presence.hpp"
#include "dusk/speedrun.h"
#include "graphics_tuner.hpp" #include "graphics_tuner.hpp"
#include "m_Do/m_Do_main.h" #include "m_Do/m_Do_main.h"
#include "menu_bar.hpp" #include "menu_bar.hpp"
@@ -77,6 +78,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",
@@ -206,54 +219,6 @@ AuroraBackend configured_backend() {
return configuredBackend; return configuredBackend;
} }
void reset_for_speedrun_mode() {
mDoMain::developmentMode = -1;
getSettings().game.enableTurboKeybind.setSpeedrunValue(false);
getSettings().game.damageMultiplier.setSpeedrunValue(1);
getSettings().game.instantDeath.setSpeedrunValue(false);
getSettings().game.noHeartDrops.setSpeedrunValue(false);
getSettings().game.autoSave.setSpeedrunValue(false);
getSettings().game.sunsSong.setSpeedrunValue(false);
getSettings().game.infiniteHearts.setSpeedrunValue(false);
getSettings().game.infiniteArrows.setSpeedrunValue(false);
getSettings().game.infiniteSeeds.setSpeedrunValue(false);
getSettings().game.infiniteBombs.setSpeedrunValue(false);
getSettings().game.infiniteOil.setSpeedrunValue(false);
getSettings().game.infiniteOxygen.setSpeedrunValue(false);
getSettings().game.infiniteRupees.setSpeedrunValue(false);
getSettings().game.enableIndefiniteItemDrops.setSpeedrunValue(false);
getSettings().game.moonJump.setSpeedrunValue(false);
getSettings().game.superClawshot.setSpeedrunValue(false);
getSettings().game.alwaysGreatspin.setSpeedrunValue(false);
getSettings().game.enableFastIronBoots.setSpeedrunValue(false);
getSettings().game.canTransformAnywhere.setSpeedrunValue(false);
getSettings().game.fastRoll.setSpeedrunValue(false);
getSettings().game.fastSpinner.setSpeedrunValue(false);
getSettings().game.armorRupeeDrain.setSpeedrunValue(MagicArmorMode::NORMAL);
getSettings().game.invincibleEnemies.setSpeedrunValue(false);
getSettings().game.pauseOnFocusLost.setSpeedrunValue(false);
aurora_set_pause_on_focus_lost(false);
getSettings().backend.enableAdvancedSettings.setSpeedrunValue(false);
getSettings().game.recordingMode.setSpeedrunValue(false);
getSettings().game.debugFlyCam.setSpeedrunValue(false);
}
void clear_speedrun_overrides() {
config::EnumerateRegistered([](config::ConfigVarBase& cvar) {
cvar.clearSpeedrunOverride();
});
}
void restore_from_speedrun_mode() {
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) {
std::error_code ec; std::error_code ec;
auto normalized = std::filesystem::weakly_canonical(path, ec); auto normalized = std::filesystem::weakly_canonical(path, ec);
@@ -407,6 +372,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;
@@ -668,7 +641,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() !=
@@ -1003,6 +976,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.",
@@ -1266,9 +1278,9 @@ SettingsWindow::SettingsWindow(bool prelaunch) : mPrelaunch(prelaunch) {
.onChange = .onChange =
[](bool enabled) { [](bool enabled) {
if (enabled) { if (enabled) {
reset_for_speedrun_mode(); resetForSpeedrunMode();
} else { } else {
restore_from_speedrun_mode(); restoreFromSpeedrunMode();
if (getSettings().game.liveSplitEnabled) { if (getSettings().game.liveSplitEnabled) {
speedrun::disconnectLiveSplit(); speedrun::disconnectLiveSplit();
} }
@@ -1485,11 +1497,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",
+39
View File
@@ -485,6 +485,32 @@ void TouchControls::set_control_pressed(Control control, bool pressed) {
mLastLTapTime = {}; mLastLTapTime = {};
break; break;
} }
switch (getSettings().game.touchTargeting.getValue()) {
case TouchTargeting::Hold:
mLPressed = pressed;
mLLatched = false;
mManualLLatched = false;
mLReleasePending = false;
mLPressStartTime = {};
mLastLTapTime = {};
break;
case TouchTargeting::Switch:
if (pressed) {
const bool wasLatched = mLPressed || mLLatched || mManualLLatched;
mLPressed = false;
mLLatched = false;
mManualLLatched = !wasLatched;
mLReleasePending = true;
} else {
mLPressed = false;
mLLatched = false;
mLReleasePending = false;
}
mLPressStartTime = {};
mLastLTapTime = {};
break;
case TouchTargeting::Hybrid:
default:
if (pressed && (mLLatched || mManualLLatched)) { if (pressed && (mLLatched || mManualLLatched)) {
mLLatched = false; mLLatched = false;
mManualLLatched = false; mManualLLatched = false;
@@ -524,6 +550,8 @@ void TouchControls::set_control_pressed(Control control, bool pressed) {
mLLatched = false; mLLatched = false;
} }
break; break;
}
break;
case Control::R: case Control::R:
mRTriggerHeld = pressed; mRTriggerHeld = pressed;
break; break;
@@ -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;
+2 -2
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);
+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()